home *** CD-ROM | disk | FTP | other *** search
/ World of Shareware - Software Farm 2 / wosw_2.zip / wosw_2 / CPROG / CMDSRC.ZIP / UTL.ASM < prev   
Assembly Source File  |  1990-04-29  |  15KB  |  686 lines

  1. ; UTL.ASM
  2. ; (c) 1989, 1990 Ashok P. Nadkarni
  3. ;
  4. ; General utility functions for CMDEDIT. SMALL model only. Also assume 
  5. ; ES == DS. 
  6. ;
  7.  
  8.     INCLUDE common.inc
  9.     INCLUDE    general.inc
  10.     INCLUDE    ascii.inc
  11.     INCLUDE dos.inc
  12.     INCLUDE buffers.inc
  13.  
  14.     PUBLIC    stre_cmp
  15.     PUBLIC    tolower
  16.     PUBLIC    xlate_lower
  17.     PUBLIC    getargs
  18.     PUBLIC    isalphnum
  19.     PUBLIC    iscntrl
  20.     PUBLIC    isspace
  21.     PUBLIC    isdelim
  22.     PUBLIC    bell
  23.     PUBLIC    push_string
  24.     PUBLIC    push_word
  25.     PUBLIC    skip_nonwhite
  26.     PUBLIC    skip_whitespace
  27.     PUBLIC    skip_nondelim
  28.     PUBLIC    output_newline
  29.     PUBLIC    output_counted_string
  30.     
  31.     EXTRN    silent:BYTE
  32.     EXTRN    lastchar:WORD
  33.     EXTRN    linebuf:BYTE
  34.  
  35.  
  36. CSEG    SEGMENT    PARA PUBLIC 'CODE'
  37.  
  38. DGROUP    GROUP    CSEG
  39.  
  40.     ASSUME    CS:DGROUP,DS:DGROUP,ES:DGROUP,SS:DGROUP
  41.  
  42. ;+
  43. ; FUNCTION : stre_cmp
  44. ;
  45. ;    Does a case-insensitve comparison of two strings of equal length.
  46. ;
  47. ; Parameters:
  48. ;    DS:SI :=    Address of string 1.
  49. ;    ES:DI :=    Address of string 2.
  50. ;    CX    :=    Length.
  51. ;
  52. ; Returns:
  53. ;    If string 1 = string 2, ZF = 1, CF = 0.
  54. ;    If string 1 < string 2, ZF = 0, CF = 1.
  55. ;    If string 1 > string 2, ZF = 0, CF = 0.
  56. ; Registers AX,CX destroyed.
  57. ;-
  58. stre_cmp proc near
  59.     @save    si,di,dx
  60.     dec    si        ;Prime for loop
  61.     dec    di
  62.     xor    ax,ax        ;Clear flags
  63.     jcxz    @stre_cmp_99
  64. @stre_cmp_10:
  65.     cmpsb            ;Point SI,DI to next byte
  66.     mov    al,[si]        ;String 1 byte
  67.     call    near ptr tolower ;al := Uppercase version
  68.     xchg    al,dl        ;Save it.
  69.     mov    al,ES:[di]    ;Ditto for string 2
  70.     call    near ptr tolower ;al := Uppercase version
  71.     cmp    dl,al        ;Compare string 1 with string 2
  72. @stre_cmp_20:
  73.     loope    @stre_cmp_10    ;Keep looping as long as equal
  74. @stre_cmp_99:
  75.     @restore
  76.     ret
  77. stre_cmp endp
  78.  
  79.  
  80. ;+
  81. ; FUNCTION : tolower
  82. ;
  83. ;    Converts the character in AL to lower case if it is a upper case
  84. ;    character, else leaves it unchanged.
  85. ;
  86. ; Parameters:
  87. ;    Al :=    character
  88. ;
  89. ; Returns:
  90. ;    AL :=    lowercase version or unchanged
  91. ;-
  92. tolower    proc near
  93.     cmp    al,'A'
  94.     jb    @tolower_99
  95.     cmp    al,'Z'
  96.     ja    @tolower_99
  97.     add    al,20h
  98. @tolower_99:
  99.     ret
  100. tolower    endp
  101.  
  102.  
  103.  
  104. ;+
  105. ; FUNCTION : xlate_lower
  106. ;
  107. ;    Converts the passed string to lower case.
  108. ;
  109. ; Parameters:
  110. ;    AX :=    length of string
  111. ;    SI :=    address of string
  112. ;
  113. ; Returns:
  114. ;    Nothing.
  115. ;
  116. ; Registers destroyed:
  117. ;    AX,CX
  118. ;-
  119. xlate_lower proc near
  120.     @save    si,di
  121.     mov    di,si
  122.     mov    cx,ax
  123.     jcxz    @xlate_lower_99
  124.  
  125. @xlate_lower_10:
  126.     lodsb
  127.     call    near ptr tolower
  128.     stosb
  129.     loop    @xlate_lower_10
  130.     
  131. @xlate_lower_99:
  132.     @restore
  133.     ret
  134. xlate_lower endp
  135.  
  136.  
  137.  
  138. ;+ FUNCTION : getargs
  139. ;
  140. ;    getargs does one of two functions depending on the value in AX.
  141. ;    If AX = 0, returns count of arguments in the line,
  142. ;    else if AX = n, returns the nth argument.
  143. ;
  144. ;    The argument separators are tab and space. Note that a
  145. ;    carraige return (0Dh) terminates a line even if the byte
  146. ;    count indicates otherwise.  Arguments containing a SPACE or
  147. ;    TAB separator may be specified by enclosing them in a pair of
  148. ;    quotes ("). The quotes do NOT act as argument delimiters. For
  149. ;    example the following line
  150. ;        this"is a single "arg
  151. ;    contains exactly one argument. An unmatched quote causes the
  152. ;    remaining characters in the line to be treated as a single
  153. ;    argument. A quote character can be included as part of an
  154. ;    argument by preceding it with a ESCARG character. An ESCARG preceding
  155. ;    any other character does not have any special meaning.
  156. ;      Note that all other characters including the NUL char (00h)
  157. ;    have no special significance.
  158. ;
  159. ; Parameters:
  160. ;    DS:SI points to the line
  161. ;    AX = argument number n
  162. ;    CX = Length of line
  163. ;    If parameter n != 0,
  164. ;    then BX = address of user buffer where the returned argument is to
  165. ;        be stored. This param need not be present if n is 0.
  166. ;         DX = length of user buffer.
  167. ;
  168. ;
  169. ; Returns:
  170. ;    If parameter n was 0,
  171. ;        return argument count in AX (CF is undefined),
  172. ;    else
  173. ;        Store n'th argument in the buffer pointed to by BX and
  174. ;        return the number of chars in the argument in AX.
  175. ;        The returned argument has quotes and ESCARGes stripped
  176. ;        out where appropriate. If the buffer is too small, CF
  177. ;        is set to 1, else it is 0. In this case the user buffer
  178. ;        contents are undefined.
  179. ;    BX is explicitly unchanged.
  180. ;
  181. ; Registers CX,DX are destroyed.
  182. ;-
  183. getargs    proc    near
  184. ESCARG    EQU    PERCENT
  185.     @save    si,di
  186.     push    bp
  187.     mov    bp,sp
  188.     sub    sp,2
  189. userbuf_len EQU <word ptr [bp-2]>
  190.     mov    di,ax        ;save argument number
  191.  
  192.     xor    ax,ax        ;al will hold char, ah will hold state
  193.     mov    userbuf_len,dx    ;Save size of user buffer
  194.     xor    dx,dx        ;dx counts arguments
  195.                 ;CX = line length
  196.     or    cx,cx        ;Check if CX is 0 (jump too far for jcxz)
  197.     jne    @getargs_2
  198.     jmp    @getargs_99    ;0 length, jump around ajnup
  199. @getargs_2:
  200. ;    At the start of this loop, the following hold :
  201. ;    (1) CX >= 1. CX holds count of remaining characters.
  202. ;    (2) ah holds the current "state" with the following encoding -
  203. ;        When Bit 1 is 0, bit 0=0 indicates we're outside an argument
  204. ;        and bit0=1 indicates we are inside an arg.
  205. ;        When Bit 1 is 1, we are inside a quoted argument. In this
  206. ;        case, bit 0 "remembers" the state we were in before the
  207. ;        quotes so that it can be restored upon reaching the closing
  208. ;        quotes.
  209. ;        Bit 2 remembers if prev char was a ESCARG (=1) or not (=0)
  210. ;        Bit 3 = 1 indicates this argument is to be copied into the
  211. ;              user buffer
  212.  
  213. in_arg        equ    01h
  214. in_quote     equ    02h
  215. saw_ESCARG    equ    04h
  216.     lodsb                ;Get next char
  217.     cmp    al,CR            ;If carraige-return
  218.     je    @getargs_50        ;  then terminate processing.
  219.     call    near ptr isspace    ;Check if space or tab
  220.     jne    @getargs_10        ;No, jump
  221. ;    Process separator
  222.     and    ah,NOT saw_ESCARG ;Remember char is not a ESCARG
  223.     test    ah,in_quote    ;Are we inside quotes ?
  224.     jnz    @getargs_49        ;If so go onto next char
  225.     and    ah,NOT in_arg    ;else reset the inside arg flag
  226.     jmp    short @getargs_49    ;and go onto next char
  227.  
  228.  
  229. @getargs_10:            ;Not a separator
  230.     test    ah,in_arg OR in_quote ;Were we inside an arg or quoted arg ?
  231.     jnz    @getargs_11        ;Yes, then skip the increment
  232.                 ;else entering an arg, so
  233.     inc    dx        ;    increment arg count
  234.     or    di,di        ;    If function is return arg count
  235.     je    @getargs_11        ;    then go on
  236.     cmp    di,dx        ;    else check if this is the arg we want
  237.     jne    @getargs_11        ;    Nope, keep on
  238.                 ;    Yep, this be the one
  239.     mov    di,bx        ;di = destination buf, ES assumed = DS
  240.     xor    dx,dx        ;Zero the character count
  241.     jmp    short @getargs_80    ;Go to the copy loop
  242. @getargs_11:
  243.     cmp    al,QUOTE    ;Is this a quote ?
  244.     jne    @getargs_15        ;No, normal processing
  245.     test    ah,saw_ESCARG    ;Found quote, was prev char a ESCARG ?
  246.     jnz    @getargs_15        ;Yes, normal processing
  247.     xor    ah,in_quote    ;else toggle the quote flag
  248.     jmp    short @getargs_49    ;go onto next char
  249. @getargs_15:            ;Normal processing
  250.     and    ah,NOT saw_ESCARG ;assume char is not a ESCARG
  251.     cmp    al,ESCARG    ;Is this a ESCARG ?
  252.     jnz    @getargs_20        ;No
  253.     or    ah,saw_ESCARG    ;Set ESCARG flag
  254. @getargs_20:
  255.     test    ah,in_quote    ;Are we inside quotes ?
  256.     jnz    @getargs_49        ;If so go onto next char
  257.     or    ah,in_arg    ;else set the inside arg flag
  258.  
  259. @getargs_49:
  260.     loop    @getargs_2        ;Go onto next char if any
  261.  
  262. @getargs_50:            ;Finished with the line
  263.     or    di,di        ;Were we supposed to return an argument ?
  264.     je    @getargs_99        ;No, so go on
  265.     xor    dx,dx        ;Yes, but arg num was > number of args
  266.                 ; so return a 0 count
  267.     jmp    short @getargs_99    ;Skip over copy arg section
  268.  
  269.  
  270.  
  271. ;Copy argument loop begins.
  272.  
  273. @getargs_70:
  274.     lodsb                ;Get next char
  275.     cmp    al,CR            ;If carraige-return
  276.     je    @getargs_99        ;  then terminate processing.
  277.  
  278.     call    near ptr isspace    ;Check if space or tab
  279.     jne    @getargs_80        ;No, jump
  280.  
  281. ;    Process separator
  282.     test    ah,in_quote    ;Are we inside quotes ?
  283.     jz    @getargs_99        ;No, terminate processing
  284.     jmp    short @getargs_85    ;Treat like any other char
  285.  
  286. @getargs_80:            ;Not a separator
  287.     ;At this point CX = num of bytes remaining in the line including the
  288.     ;one in AL.
  289.  
  290.     cmp    al,QUOTE    ;Is this a quote ?
  291.     jne    @getargs_85        ;No, normal processing
  292.     test    ah,saw_ESCARG    ;Found quote, was prev char a ESCARG ?
  293.     jnz    @getargs_84        ;Yes, jump
  294.     xor    ah,in_quote    ;else toggle the quote flag
  295.     jmp    short @getargs_89    ;go onto next char
  296.  
  297. @getargs_84:            ;Found a \" combination
  298.     dec    di        ;Previous \ shouldn't have been written
  299.     dec    dx        ;or counted.
  300.                 ;fall thru for normal processing
  301.  
  302. @getargs_85:            ;Normal processing
  303.     sub    userbuf_len,1    ;Decrement space remaining in buffer. Do
  304. ;                 NOT use DEC here since CF needs to be set
  305.     jb    @getargs_100    ;No more space, exit with CF set
  306.     stosb            ;Store the char
  307.     inc    dx        ;and incr count
  308.  
  309.     and    ah,NOT saw_ESCARG ;assume char is not a ESCARG
  310.     cmp    al,ESCARG    ;Is this a ESCARG ?
  311.     jnz    @getargs_89    ;No
  312.     or    ah,saw_ESCARG    ;Set ESCARG flag
  313.  
  314. @getargs_89:
  315.     loop    @getargs_70    ;Go onto next char if any
  316.  
  317. @getargs_99:
  318.     xchg    ax,dx        ;AX<-arg count or num chars in returned arg
  319.     clc            ;Clear CF for no error
  320. @getargs_100:
  321.     mov    sp,bp
  322.     pop    bp
  323.     @restore
  324.     ret
  325. getargs    endp
  326.  
  327.  
  328.  
  329.  
  330. ;+
  331. ; FUNCTION : isalphnum
  332. ;
  333. ;    Test if the character is alphanumeric.
  334. ;
  335. ; Parameters:
  336. ;    AL    = character
  337. ;
  338. ; Returns:
  339. ;    CF    = 0 if alphanumeric
  340. ;          1 if not
  341. ; Register(s) destroyed:
  342. ;-
  343. isalphnum proc near
  344.     cmp    al,'0'
  345.     jc    @isalphnum_99        ;Not alphanumeric
  346.     cmp    al,'9'+1
  347.     cmc
  348.     jnc    @isalphnum_99        ;Number
  349.     cmp    al,'A'
  350.     jc    @isalphnum_99        ;Not alphanumeric
  351.     cmp    al,'Z'+1
  352.     cmc
  353.     jnc    @isalphnum_99        ;Uppercase letter
  354.     cmp    al,'a'
  355.     jc    @isalphnum_99        ;Not alphanumeric
  356.     cmp    al,'z'+1
  357.     cmc
  358. @isalphnum_99:
  359.     ret
  360. isalphnum endp
  361.  
  362.  
  363.  
  364. ;+
  365. ; FUNCTION : iscntrl
  366. ;
  367. ;    Check if control character and DEL (00h-1Fh and 0FFh).
  368. ;
  369. ; Parameters:
  370. ;    AL    = character to be checked
  371. ;
  372. ; Returns:
  373. ;    CF    = 0 if AL is a control character or DEL
  374. ;          1 not a control char or DEL
  375. ; Register(s) destroyed:
  376. ;-
  377. iscntrl    proc    near
  378.     cmp    al,DEL
  379.     jne    @iscntrl_99
  380.     cmp    al,' '
  381.     cmc
  382. @iscntrl_99:
  383.     ret
  384. iscntrl    endp
  385.  
  386.  
  387.  
  388. ;+
  389. ; FUNCTION : isspace
  390. ;
  391. ;    Check if a character is a SPACE or a TAB
  392. ;
  393. ; Parameters:
  394. ;    AL    = character to check
  395. ;
  396. ; Returns:
  397. ;    ZF    = 1 if AL is a space or a tab
  398. ;          0 otherwise
  399. ; Register(s) destroyed:
  400. ;
  401. ;-
  402. isspace    proc    near
  403.     cmp    al,TAB
  404.     je    @isspace_99
  405.     cmp    al,SPACE
  406. @isspace_99:
  407.     ret
  408. isspace    endp
  409.  
  410.  
  411.  
  412. ;+
  413. ; FUNCTION : isdelim
  414. ;
  415. ;    Check if a character is an MSDOS delimiter.
  416. ;
  417. ; Parameters:
  418. ;    AL    = character to check
  419. ;
  420. ; Returns:
  421. ;    ZF    = 1 if AL is a delimiter
  422. ;          0 otherwise
  423. ; Register(s) destroyed:
  424. ;
  425. ;-
  426. isdelim    proc    near
  427.     call    near ptr isspace    ;Check if space or tab
  428.     je    @isdelim_99        ;Yes, go return
  429.     cmp    al,'/'
  430.     je    @isdelim_99        ;Yes, go return
  431.     cmp    al,'|'
  432.     je    @isdelim_99        ;Yes, go return
  433.     cmp    al,'<'
  434.     je    @isdelim_99        ;Yes, go return
  435.     cmp    al,'>'
  436. @isdelim_99:
  437.     ret
  438. isdelim    endp
  439.  
  440.  
  441.  
  442. ;+
  443. ; FUNCTION : skip_whitespace
  444. ;
  445. ;    Searches for the next non-whitespace character in a given string.
  446. ;
  447. ; Parameters:
  448. ;    SI    -> pointer to string
  449. ;    CX    == num chars in the string
  450. ;
  451. ; Returns:
  452. ;    CF    = 1 if end-of string reached else 0
  453. ;    SI    ->next non-whitespace character or end-of-string
  454. ;    CX    <-num remaining characters including one pointed to by SI
  455. ;
  456. ; Register(s) destroyed:
  457. ;    AX
  458. ;-
  459. skip_whitespace proc near
  460.     jcxz    @skip_whitespace_98        ;Empty string
  461. @skip_whitespace_10:
  462.     lodsb                    ;AL<-next char
  463.     call    near ptr isspace        ;Whitespace character ?
  464.     loope    @skip_whitespace_10        ;Repeat until
  465. ;                         non-whitespace or string ends
  466.     je    @skip_whitespace_98        ;End-of-string
  467. ; Non-whitespace char found
  468.     dec    si                ;SI->non-whitespace char
  469.     inc    cx                ;CX<-remaining number of bytes
  470.     clc                    ;CF<-0 (char found)
  471.     jmp    short @skip_whitespace_99
  472.  
  473. @skip_whitespace_98:
  474. ; End of string reached.
  475.     stc                    ;Set CF
  476.  
  477. @skip_whitespace_99:
  478.     ret
  479. skip_whitespace endp
  480.  
  481.  
  482.  
  483.  
  484. ;+ FUNCTION : skip_nonwhite, skip_nondelim
  485. ;
  486. ;    Searches for the next whitespace character / delimiter in a given
  487. ;    string.
  488. ;
  489. ; Parameters:
  490. ;    SI    -> pointer to string
  491. ;    CX    == num chars in the string
  492. ;
  493. ; Returns:
  494. ;    CF    = 1 if end-of string reached else 0
  495. ;    SI    ->next whitespace character or end-of-string
  496. ;    CX    <-num remaining characters including one pointed to by SI
  497. ;
  498. ; Register(s) destroyed:
  499. ;    AX
  500. ;-
  501. skip_non proc near
  502. skip_nonwhite LABEL near
  503.     push    dx
  504.     mov    dx,offset DGROUP:isspace
  505.     jmp    short @skip_non    
  506.  
  507. skip_nondelim LABEL near
  508.     push    dx
  509.     mov    dx,offset DGROUP:isdelim
  510. @skip_non:
  511.     jcxz    @skip_non_98            ;Empty string
  512. @skip_non_10:
  513.     lodsb                    ;AL<-next char
  514.     call    dx                ;nonwhite / delimiter
  515. ;                         character ? 
  516.     loopne    @skip_non_10            ;Repeat until
  517. ;                         whitespace or string ends
  518.     jne    @skip_non_98            ;End-of-string
  519. ; whitespace char found
  520.     dec    si                ;SI->whitespace char
  521.     inc    cx                ;CX<-remaining number of bytes
  522.     clc                    ;CF<-0 (char found)
  523.     jmp    short @skip_non_99
  524.  
  525. @skip_non_98:
  526. ; End of string reached.
  527.     stc                    ;Set CF
  528.  
  529. @skip_non_99:
  530.     pop    dx
  531.     ret
  532. skip_non endp
  533.  
  534.  
  535.  
  536.  
  537.  
  538.  
  539. ;+
  540. ; FUNCTION : push_word
  541. ;
  542. ;    Looks for the next word (delimited by whitespace) and pushes it
  543. ;    onto the specified string stack.
  544. ;
  545. ; Parameters:
  546. ;    BX    -> strstack descriptor
  547. ;    SI    -> string
  548. ;    CX    == length of string (< 256)
  549. ;
  550. ; Returns:
  551. ;    AX    <- 0 if no errors
  552. ;          -1 if no room in stack
  553. ;          +1 if no word in string
  554. ;    SI    -> char after first word (or end-of-string)
  555. ;    CX    <- num remaining characters
  556. ;
  557. ; Register(s) destroyed:
  558. ;    DX
  559. ;-
  560. push_word proc    near
  561. ; Skip forward to first word
  562.     call    near ptr skip_whitespace    ;Returns
  563. ;                         SI->start of word
  564. ;                         CX<-remaining chars
  565.     jcxz    @push_word_98            ;No words in line
  566.     mov    dx,si                ;DX->start of word
  567.     push    cx                ;Save count
  568.     call    near ptr skip_nonwhite        ;Find end of word
  569. ;                         SI->beyond word
  570. ;                         CX<-remaining chars
  571.     pop    ax
  572.     sub    ax,cx                ;AX<-length of word
  573.     push    cx                ;Save remaining char count
  574.     xor    cx,cx                ;CX<-0 (don't force push)
  575.     call    near ptr strstk_push        ;Store macro name into
  576. ;                         macro stack. Params
  577. ;                         AX,BX,CX,DX
  578. ;                         Returns Cf = 0 or 1
  579.     pop    cx                ;CX<-remaining character
  580. ;    Assume no error
  581.     mov    ax,0    ;DON'T DO xor ax,ax SINCE CF to be preserved
  582.     jnc    @push_word_99            ;Jump if no error
  583.     dec    ax                ;Error AX <- -1
  584.     jmp    short @push_word_99        ;Exit
  585.  
  586. @push_word_98:
  587. ; No words found in line. Set return codes.
  588.     mov    ax,1                ;Code for blank line
  589.  
  590. @push_word_99:
  591.     ret
  592. push_word    endp
  593.  
  594.  
  595.  
  596.  
  597.  
  598. ;+
  599. ; FUNCTION : push_string
  600. ;
  601. ;    Pushed the specified string onto the specified stack.
  602. ;
  603. ; Parameters:
  604. ;    BX    -> strstack descriptor
  605. ;    SI    -> string
  606. ;    CX    == length of string must be < 256
  607. ;
  608. ; Returns:
  609. ;    CF    <- 0 if no errors
  610. ;           1 if no room in stack
  611. ;
  612. ; Register(s) destroyed:
  613. ;    AX,CX,DX
  614. ;-
  615. push_string proc    near
  616.     mov    dx,si                ;DX->start of string
  617.     mov    ax,cx                ;AX<-length of string
  618.     xor    cx,cx                ;CX<-0 (don't force push)
  619.     call    near ptr strstk_push        ;Store macro name into
  620. ;                         macro stack. Params
  621. ;                         AX,BX,CX,DX
  622. ;                         Returns Cf = 0 or 1
  623.     ret
  624. push_string    endp
  625.  
  626.  
  627.  
  628. ;+
  629. ; FUNCTION : bell
  630. ;
  631. ;    Called to ring the bell.
  632. ;
  633. ; Parameters:
  634. ;    None.
  635. ;
  636. ; Returns:
  637. ;    Nothing.
  638. ; Register(s) destroyed:
  639. ;    AX
  640. ;-
  641. bell    proc    near
  642.     cmp    silent,1
  643.     je    @bell_99
  644.     @DispCh    BEL
  645. @bell_99:
  646.     ret
  647. bell    endp
  648.  
  649.  
  650. ;+
  651. ; FUNCTION : output_counted_string
  652. ;
  653. ; Parameters :
  654. ;    CX - Number of bytes to display
  655. ;    DX - address of string
  656. ; Registers destroyed:
  657. ;    AX,BX,CX,DX
  658. output_counted_string proc near
  659.     mov    ah,40h
  660.     mov    bx,1                ;stdout handle
  661.     int    21h                ;Params ax,bx,cx,dx
  662.     ret
  663. output_counted_string endp
  664.  
  665. ;+
  666. ; FUNCTION: output_newline
  667. ;
  668. ; Registers destroyed:
  669. ;    AX,BX,CX,DX
  670. ;-
  671. output_newline proc near
  672.     @DispCh    CR
  673.     @DispCh    LF
  674.     ret
  675. output_newline endp
  676.  
  677.  
  678.  
  679.  
  680.  
  681.  
  682. CSEG    ENDS
  683.  
  684.     END
  685.  
  686.