home *** CD-ROM | disk | FTP | other *** search
/ Shareware 1 2 the Maxx / sw_1.zip / sw_1 / EDITORS / TDE20.ZIP / TDEASM.C < prev    next >
C/C++ Source or Header  |  1992-06-05  |  78KB  |  1,943 lines

  1. /*
  2.  * In the DTE 5.1 editor, the end of file was marked with '\0'.  I have
  3.  * decided to use ^Z to mark the begin and end of files instead of '\0'.
  4.  * That way, null characters are allowed as normal text characters.  ^Z is used
  5.  * to mark the end of strings in buffers instead of '\0'.  The standard C
  6.  * string library functions should not be used when dealing with text buffers.
  7.  *
  8.  * The often used string routines have been rewritten in assembly.  When using
  9.  * 16 bit processors, accessing memory by WORDs on WORD boundaries is twice
  10.  * as fast as accessing memory by BYTEs.  If a memory pointer is even then it
  11.  * is WORD aligned.  If a memory pointer is odd, do the first BYTE and then
  12.  * the rest of the string is WORD aligned on an even boundary.
  13.  *
  14.  * Two routines were written to adjust the string pointers whenever they
  15.  * approach the end of a segment, cpf() and cpb() (check pointer foward and
  16.  * check pointer backward).  With these two routines, the code may be
  17.  * compiled without the huge memory model.  Another assembly routine was
  18.  * written to compare physical memory locations, ptoul().  For example,
  19.  * all of these pointers point to same physical memory address:
  20.  *
  21.  *         59a1:9122 == 58a1:a122 == 62a1:0122  = physical address 404,274
  22.  *
  23.  * An efficient way to compare far pointers is to convert them to either
  24.  * unsigned long or long integers.  Either one will do - their is no such
  25.  * thing a negative physical memory address.  A long int goes from
  26.  * -2 billion to 2 billion, which leaves plenty of room to describe a physical
  27.  * address, using a long, where the max is 1 MEG.  I used unsigned long.  When
  28.  * adding or subtracting from the physical address of a pointer, we should
  29.  * never, ever get a negative physical address.  This is the concept behind the
  30.  * function ptoul(), which is short for pointer to unsigned long.
  31.  *
  32.  * With these functions written in assembly, this editor is fairly fast.
  33.  * I feel the need for speed.
  34.  *
  35.  * New editor name:  tde, the Thomson-Davis Editor.
  36.  * Author:           Frank Davis
  37.  * Date:             June 5, 1991, version 1.0
  38.  * Date:             July 29, 1991, version 1.1
  39.  * Date:             October 5, 1991, version 1.2
  40.  * Date:             January 20, 1992, version 1.3
  41.  * Date:             February 17, 1992, version 1.4
  42.  * Date:             April 1, 1992, version 1.5
  43.  * Date:             June 5, 1992, version 2.0
  44.  *
  45.  * This modification of Douglas Thomson's code is released into the
  46.  * public domain, Frank Davis.  You may distribute it freely.
  47.  */
  48.  
  49. #include "tdestr.h"
  50. #include "common.h"
  51. #include "tdefunc.h"
  52.  
  53.  
  54. /*
  55.  * Name:    cpf - check_pointer_forward
  56.  * Purpose: To adjust a pointer if it is nearing end of a segment (within 16k)
  57.  * Date:    June 5, 1991
  58.  * Passed:  s:  string pointer
  59.  * Notes:   To avoid a bunch of code generated for pointer arithmetic when using
  60.  *          the huge memory model, this routine adjusts a pointer when it
  61.  *          approaches the end of a segment.
  62.  */
  63. text_ptr cpf( text_ptr s )
  64. {
  65.    _asm {
  66.         mov     ax, WORD PTR s  ; get OFFSET of s
  67.         mov     dx, WORD PTR s+2        ; get SEGMENT of s
  68.         cmp     ax, 0xc000      ; are we within 16k of top of segment?
  69.         jb      get_out         ; no, get out
  70.         sub     ax, 0x8000      ; yes, subtract 32k from offset
  71.         add     dx, 0x0800      ; add 0x0800 paragraphs to segment == 32k
  72.         ALIGN   2
  73. get_out:
  74.    }
  75. }
  76.  
  77.  
  78. /*
  79.  * Name:    cpb - check_pointer_backward
  80.  * Purpose: To adjust a pointer if it is nearing beginning of a segment (16k)
  81.  * Date:    June 5, 1991
  82.  * Passed:  s:  string pointer
  83.  * Notes:   To avoid a bunch of code generated for pointer arithmetic when using
  84.  *          the huge memory model, this routine adjusts a pointer when it
  85.  *          approaches the beginning of a segment.  Don't check NULL pointer.
  86.  */
  87. text_ptr cpb( text_ptr s )
  88. {
  89.    _asm {
  90.         mov     ax, WORD PTR s  ; get OFFSET of s
  91.         mov     dx, WORD PTR s+2        ; get SEGMENT of s
  92.         cmp     ax, 0           ; is offset of s == NULL?
  93.         jne     not_null        ; no, check pointer
  94.         cmp     dx, 0           ; is segment of s == NULL?
  95.         je      get_out         ; yes, don't check NULL pointer
  96.         ALIGN   2
  97. not_null:
  98.         cmp     ax, 0x4000      ; are we within 16k of beginning of segment?
  99.         jae     get_out         ; no, get out
  100.         add     ax, 0x8000      ; yes, add 32k to offset
  101.         sub     dx, 0x0800      ; sub 0x0800 paragraphs from segment == 32k
  102.         ALIGN   2
  103. get_out:
  104.    }
  105. }
  106.  
  107.  
  108. /*
  109.  * Name:    ptoul - pointer to unsigned long
  110.  * Purpose: convert a far pointer to unsigned long integer
  111.  * Date:    June 5, 1991
  112.  * Passed:  s:  a far pointer
  113.  * Notes:   combine the offset and segment like so:
  114.  *                offset       0000
  115.  *                segment   + 0000
  116.  *                          =======
  117.  *                            00000
  118.  *          result is returned in dx:ax
  119.  */
  120. unsigned long ptoul( void far *s )
  121. {
  122.    _asm {
  123.         mov     ax, WORD PTR s          ; ax = OFFSET of s
  124.         mov     dx, WORD PTR s+2        ; dx = SEGMENT of s
  125.         mov     bx, dx          ; put copy of segment in bx
  126.         mov     cl, 12          ; cl = decimal 12 - shift hi word 3 hex digits
  127.         shr     dx, cl          ; convert to 'real segment'
  128.         mov     cl, 4           ; cl = 4  - shift hi word 1 hex digit left
  129.         shl     bx, cl          ; shift bx - to add 3 digits of seg to 4 of off
  130.         add     ax, bx          ; add low part of segment to offset
  131.         adc     dx, 0           ; if carry, bump to next 'real' segment
  132.    }
  133. }
  134.  
  135.  
  136. /*
  137.  * Name:    nptos - normalize pointer to segment
  138.  * Purpose: make the offset of a pointer no larger than a paragraph (16 bytes)
  139.  * Date:    June 5, 1991
  140.  * Passed:  s:  string pointer
  141.  * Notes:   move all but the paragraph from the offset of the pointer to the
  142.  *          segment.   The offset will be no larger than 16 bytes. Why? because
  143.  *          we can add up to 0xFFF0 reliably to a pointer in small, compact or
  144.  *          large model and not worry about segment wrap.
  145.  *
  146.  *                offset       abcx
  147.  *                segment     0000
  148.  *                          =======
  149.  *                offset       000x
  150.  *                segment     0abc
  151.  *          result is returned in dx:ax
  152.  */
  153. text_ptr nptos( text_ptr s )
  154. {
  155.    _asm {
  156.         mov     ax, WORD PTR s          ; ax = OFFSET of s
  157.         mov     dx, WORD PTR s+2        ; dx = SEGMENT of s
  158.         mov     bx, ax          ; put copy of offset in bx
  159.         mov     cl, 4           ; cl = 4  - shift lo word 1 hex digit
  160.         shr     bx, cl          ; shift bx - line up on paragraph
  161.         add     dx, bx          ; add hi part of offset to segment
  162.         and     ax, 0x000f      ; mask out three hex digits in offset
  163.    }
  164. }
  165.  
  166.  
  167. /*
  168.  * Name:    addltop - add long to pointer
  169.  * Purpose: add long integer to a pointer
  170.  * Date:    June 5, 1991
  171.  * Passed:  l: long
  172.  *          p: text pointer
  173.  * Returns: pointer + long integer
  174.  * Notes:   A long integer takes two WORDs.  A far pointer takes two WORDs.
  175.  *          A long integer cannot be added directly to a far pointer.
  176.  *              This diagram may help explain better than I can write.
  177.  *
  178.  *          far pointer            0000   offset
  179.  *                                0xxx    segment
  180.  *                                  +
  181.  *          long integer       00000000             throw away those three
  182.  *                                ======            hex digits on long integer,
  183.  *                                 0000   offset    they have no effect
  184.  *           new far pointer      0xxx    segment
  185.  *
  186.  *          msw = Most Significant WORD
  187.  *          lsw = Least Significant WORD
  188.  *
  189.  *          When working with the long integer, we don't need to worry about
  190.  *          the three x's on segment of the pointer.  Add or subtract the lsw
  191.  *          of the long integer to/from the offset.  If there is a carry,
  192.  *          it only affects the left most digit of the msw of the pointer.
  193.  */
  194. text_ptr addltop( long l, text_ptr p )
  195. {
  196.  
  197.    if (l >= 0) {
  198.       _asm {
  199.         mov     ax, WORD PTR p          ; ax = OFFSET of p
  200.         mov     dx, WORD PTR p+2        ; dx = SEGMENT of p
  201.         mov     bx, WORD PTR l+2        ; msw of l in bx
  202.         add     ax, WORD PTR l          ; add offset of p and lsw of l
  203.         adc     bx, 0           ; if carry, pointer in another segment
  204.         mov     cl, 12          ; cl = 12 - shift off 3 hex digits
  205.         shl     bx, cl          ; consider the 1st hex digit of msw of l
  206.         add     dx, bx          ; add segment of p and 1st digit of msw of l
  207.       }
  208.    } else {
  209.       l = -l;      /* convert l to positive and subtract from pointer p */
  210.       _asm {
  211.         mov     ax, WORD PTR p          ; ax = OFFSET of p
  212.         mov     dx, WORD PTR p+2        ; dx = SEGMENT of p
  213.         mov     bx, WORD PTR l+2        ; msw of l in bx
  214.         mov     cl, 12                  ; cl = 12 - shift off 3 hex digits
  215.         sub     ax, WORD PTR l          ; subtract low part of pointer
  216.         adc     bx, 0                   ; if we borrowed then add it back to bx
  217.         shl     bx, cl                  ; only handle 1st hex digit of msw of l
  218.         sub     dx, bx                  ; subtract msw from segment of p
  219.       }
  220.    }
  221. }
  222.  
  223.  
  224. /*
  225.  * Name:    find_CONTROL_Z - assembler version, see commented C at end
  226.  * Purpose: To determine the length of a line up to ^Z
  227.  * Date:    June 5, 1991
  228.  * Passed:  s: the line to be measured
  229.  * Notes:   DOS carried over ^Z to mark the end of files from CP/M.  Since
  230.  *          it is the only character not allowed in regular text files.  ^Z
  231.  *          can be used, instead of '\0', to mark the end of strings.  All
  232.  *          ASCII characters, except ^Z, may be included in a text file.
  233.  *          However, none of the C string library functions should be used
  234.  *          when working with text.  The string library functions can be used
  235.  *          on responses solicited from the user.
  236.  * Returns: the length of the line
  237.  */
  238. unsigned int  find_CONTROL_Z( text_ptr s )
  239. {
  240.    s = cpf( s );
  241.    _asm {
  242.         mov     dx, ds          ; keep ds in dx, MUST save data segment
  243.         push    si              ; put copy of si on stack
  244.  
  245.         xor     cx, cx          ; cx = 0
  246.         mov     si, WORD PTR s  ; put OFFSET of s in si
  247.         mov     ax, WORD PTR s+2        ; get SEGMENT of s
  248.         mov     ds, ax          ; else, segment in ds
  249.         cmp     si, 0           ; is offset of s == NULL?
  250.         jne     not_null        ; no, find length
  251.         cmp     ax, 0           ; is segment of s == NULL?
  252.         je      get_out         ; yes, line length = 0
  253.         ALIGN   2
  254. not_null:
  255.         mov     bl, CONTROL_Z   ; keep Control Z in bl - eos marker
  256.         mov     ax, si          ; pointer is ok, check for word align
  257.         shr     ax, 1           ; if [si] is odd, lsb is 1 - rotate to carry
  258.         jnc     top             ; see if string is WORD aligned
  259.         lodsb                   ; no, get a BYTE - now WORD aligned
  260.         cmp     al, bl          ; is ds:[si] == ^Z?
  261.         je      get_out         ; yes, have length, cx = 0
  262.         inc     cx              ; increment length variable
  263.         ALIGN   2
  264. top:
  265.         lodsw                   ; string is WORD aligned
  266.         cmp     al, bl          ; is lo BYTE == ^Z?
  267.         je      get_out         ; yes, we have length
  268.         inc     cx              ; no, increment counter
  269.         cmp     ah, bl          ; now test higher BYTE, is it ^Z?
  270.         je      get_out         ; yes, we have length
  271.         inc     cx              ; no, increment length
  272.         jmp     SHORT top       ; look at next two characters
  273.         ALIGN   2
  274. get_out:
  275.         mov     ax, cx          ; put length in ax - as defined by Microsoft
  276.         mov     ds, dx          ; get back data segment from dx
  277.         pop     si              ; get back si from stack
  278.    }
  279.  
  280. /*
  281. int len = 0;
  282.  
  283.    while (*s != ^Z) {
  284.       ++len;
  285.       ++s;
  286.    }
  287.    return len;
  288. */
  289. }
  290.  
  291.  
  292. /*
  293.  * Name:    linelen - assembler version, see commented C at end of routine
  294.  * Purpose: To determine the length of a line, up to either a \n or a
  295.  *           ^Z, whichever comes first.
  296.  * Date:    June 5, 1991
  297.  * Passed:  s: the line to be measured
  298.  * Notes:   Demonstrates 'lodsb' and 'lodsw'.  Memory operations are most
  299.  *           efficient when working with WORDs.  See if first BYTE in
  300.  *           string is WORD aligned.  If it is then work with WORDs else
  301.  *           get the first BYTE and rest of string will be WORD aligned.
  302.  *           The 'mov' instruction could have been used, but 'lobsb' and
  303.  *           'lodsw' automatically increment the memory pointer.
  304.  * Returns: the length of the line
  305.  */
  306. unsigned int  linelen( text_ptr s )
  307. {
  308.    s = cpf( s );
  309.    _asm {
  310.         mov     dx, ds          ; keep ds in dx, MUST save data segment
  311.         push    si              ; save si on stack
  312.  
  313.         xor     cx, cx          ; cx = 0
  314.         mov     si, WORD PTR s  ; put OFFSET of s in si
  315.         mov     ax, WORD PTR s+2        ; get SEGMENT of s
  316.         mov     ds, ax          ; else, segment in ds
  317.         cmp     si, 0           ; is offset of s == NULL?
  318.         jne     not_null        ; no, find length
  319.         cmp     ax, 0           ; is segment of s == NULL?
  320.         je      get_out         ; yes, line length = 0
  321.         ALIGN   2
  322. not_null:
  323.         mov     bl, '\n'        ; keep new line character in bl
  324.         mov     bh, CONTROL_Z   ; keep Control Z in bh - DOS eof marker
  325.         mov     ax, si          ; pointer is ok, check for word align
  326.         shr     ax, 1           ; if [si] is odd, lsb is 1 - rotate to carry
  327.         jnc     top             ; see if string is WORD aligned
  328.         lodsb                   ; no, get a BYTE - now WORD aligned
  329.         cmp     al, bl          ; is BYTE == '\n'?
  330.         je      get_out         ; yes, have length, cx = 0
  331.         cmp     al, bh          ; is ds:[si] == ^Z?
  332.         je      get_out         ; yes, have length, cx = 0
  333.         inc     cx              ; increment length variable
  334.         ALIGN   2
  335. top:
  336.         lodsw                   ; string is WORD aligned
  337.         cmp     al, bl          ; test lower BYTE, is it '\n'
  338.         je      get_out         ; yes, we have length
  339.         cmp     al, bh          ; no, test for ^Z
  340.         je      get_out         ; yes, we have length
  341.         inc     cx              ; no, not '\n' or ^Z so increment counter
  342.         cmp     ah, bl          ; now test higher BYTE, is it '\n'
  343.         je      get_out         ; yes, we have length
  344.         cmp     ah, bh          ; is it ^Z
  345.         je      get_out         ; yes, we have length
  346.         inc     cx              ; no, not '\n' or ^Z so increment length
  347.         jmp     SHORT top       ; look at next two characters
  348.         ALIGN   2
  349. get_out:
  350.         mov     ax, cx          ; put length in ax - as defined by Microsoft
  351.         mov     ds, dx          ; get back data segment from dx
  352.         pop     si              ; get back si from stack
  353.    }
  354.  
  355. /*
  356. int len = 0;
  357.  
  358.    while (*s && *s != '\n') {
  359.       ++len;
  360.       ++s;
  361.    }
  362.    return len;
  363. */
  364. }
  365.  
  366.  
  367. /************* prelinelen is not used, but left in for reference **********/
  368. /*
  369.  * Name:    prelinelen
  370.  * Purpose: To determine the length of a line, from the current position
  371.  *           backwards to either a \n or a ^Z, whichever comes first.
  372.  * Date:    June 5, 1991
  373.  * Passed:  s: the line to be measured
  374.  * Returns: the length of the line up to the current position
  375.  * Notes:   It is assumed there will be a "terminating" ^Z before the
  376.  *           start of the first line.
  377.  */
  378. /*
  379. unsigned int prelinelen( text_ptr s )
  380. {
  381.    s = cpb( s );
  382.    _asm {
  383.         push    di              ; put copy of di on stack
  384.  
  385.         xor     ax, ax          ; ax = 0, keep string length in ax
  386.         mov     di, WORD PTR s  ; get offset of string
  387.         mov     dx, WORD PTR s+2        ; get segment of string
  388.         mov     es, dx          ; put segment in es
  389.         cmp     di, 0           ; is offset of string == NULL?
  390.         jne     not_null        ; no, do string stuff
  391.         cmp     dx, 0           ; is, segment of string == NULL?
  392.         je      get_out         ; yes, don't do NULL string
  393. not_null:
  394.         dec     di              ; look at previous character
  395. ALWORD: dec     di              ; get ready to check for WORD align
  396.         mov     bl, '\n'        ; keep '\n' in bl
  397.         mov     bh, CONTROL_Z   ; keep ^Z in bh
  398.         mov     dx, di          ; pointer is ok, check for WORD align
  399.         shr     dx, 1           ; if [di] is odd, lsb is 1 - rotate to carry
  400.         jnc     top             ; string is WORD aligned
  401.         inc     di              ; fix the second decrement - see ALWORD
  402.         mov     dl, BYTE PTR es:[di]    ; get a BYTE - put in DL
  403.         cmp     dl, bl          ; is it '\n'
  404.         je      get_out         ; yes, get out - count = 0
  405.         cmp     dl, bh          ; is it ^Z
  406.         je      get_out         ; yes, get out - count = 0
  407.         inc     ax              ; increment length counter
  408.         dec     di              ; pointer was BYTE aligned, dec pointer
  409.         dec     di              ; pointer is now WORD aligned
  410.         ALIGN   2
  411. top:
  412.         mov     dx, WORD PTR es:[di]    ; load WORD - hi BYTE is next
  413.         cmp     dh, bl          ; is hi BYTE (next char) '\n'?
  414.         je      get_out         ; yes, get out - count already in ax
  415.         cmp     dh, bh          ; is hi BYTE (next char) ^Z?
  416.         je      get_out         ; yes, get out - count already in ax
  417.         inc     ax              ; increment character counter
  418.         cmp     dl, bl          ; now check lo BYTE, is it '\n'?
  419.         je      get_out         ; yes, get out - count is in ax
  420.         cmp     dl, bh          ; is lo BYTE ^Z?
  421.         je      get_out         ; yes, get out - count is in ax
  422.         inc     ax              ; increment character counter
  423.         dec     di              ; decrement pointer
  424.         dec     di              ; align pointer on WORD
  425.         jmp     SHORT top       ; test next 2 characters
  426. get_out:
  427.         pop     di              ; get back di from stack
  428.    }
  429. int len = 0;
  430.  
  431.    while (*--s != CONTROL_Z && *s != '\n')
  432.       ++len;
  433.    return len;
  434. }
  435. */
  436. /************************** prelinelen is not used ************************/
  437.  
  438.  
  439. /*
  440.  * Name:    find_next
  441.  * Purpose: To find the first character in the next line
  442.  * Date:    June 5, 1991
  443.  * Passed:  s: the starting point
  444.  * Returns: the first character in the next line
  445.  * Notes:   This function goes faster if machine works with WORDs.  See if
  446.  *           first BYTE in string is WORD aligned.  If it is not, get first
  447.  *           BYTE in string then the rest of string is WORD aligned.
  448.  *           Code added at end to adjust segment:offset if needed.
  449.  */
  450. text_ptr find_next( text_ptr s )
  451. {
  452.    _asm {
  453.         push    ds              ; save ds on stack
  454.         push    si              ; save si on stack
  455.  
  456.         mov     si, WORD PTR s          ; load OFFSET of s
  457.         mov     ax, WORD PTR s+2        ; load SEGMENT of s
  458.         mov     ds, ax
  459.         cmp     si, 0           ; is offset of string == NULL?
  460.         jne     not_null        ; no, do string stuff
  461.         cmp     ax, 0           ; is segment of string == NULL?
  462.         je      return_null     ; yes, return NULL if string is NULL
  463.         ALIGN   2
  464. not_null:
  465.         mov     bl, '\n'        ; keep '\n' in bl
  466.         mov     bh, CONTROL_Z   ; keep ^Z in bh
  467.         mov     ax, si          ; move offset of si to ax
  468.         shr     ax, 1           ; shift right into carry flag
  469.         jnc     top             ; is string WORD aligned?
  470.         lodsb                   ; no, get a BYTE
  471.         cmp     al, bl          ; is it '\n'?
  472.         je      next_even       ; yes, si already incremented by lodsb
  473.         cmp     al, bh          ; is it ^Z?
  474.         je      return_null     ; yes, return NULL
  475.         ALIGN   2
  476. top:
  477.         lodsw                   ; string is WORD aligned, get two BYTEs
  478.         cmp     al, bl          ; is next BYTE == '\n'?
  479.         je      next_odd        ; yes, since si inc for WORD (lodsw) - dec di
  480.         cmp     al, bh          ; is next BYTE == ^Z?
  481.         je      return_null     ; yes, return NULL
  482.         cmp     ah, bl          ; is next BYTE in AH == '\n'?
  483.         je      next_even       ; yes, si is OK - return pointer to next BYTE
  484.         cmp     ah, bh          ; is next BYTE in AH == ^Z?
  485.         je      return_null     ; yes, return NULL
  486.         jmp     SHORT top       ; look at next WORD
  487.         ALIGN   2
  488. return_null:
  489.         xor     ax, ax          ; clear ax - offset = NULL
  490.         xor     dx, dx          ; clear dx - segment = NULL
  491.         jmp     SHORT get_out   ; return text_ptr in dx:ax - see Microsoft
  492.         ALIGN   2
  493. next_odd:
  494.         dec     si              ; 'lodsw' went one BYTE too far - so dec si
  495. next_even:
  496.         mov     ax, si          ; ds:si now points to next line, load ax
  497.         mov     dx, ds          ; load dx with segment of next BYTE
  498.         cmp     ax, 0xc000      ; are we within 16k of segment?
  499.         jb      get_out         ; no, get out
  500.         sub     ax, 0x8000      ; yes, subtract 32k from offset
  501.         add     dx, 0x0800      ; add 0x0800 paragraphs to segment
  502.         ALIGN   2
  503. get_out:
  504.         pop     si              ; get back si from stack
  505.         pop     ds              ; get back ds from stack
  506.    }
  507. /*
  508.    while (*s && *s != '\n' && *s != CONTROL_Z)
  509.       ++s;
  510.    if (*s)
  511.       return ++s;
  512.    else
  513.       return NULL;
  514. */
  515. }
  516.  
  517.  
  518. /*
  519.  * Name:    find_prev
  520.  * Purpose: To find the start of the previous line
  521.  * Date:    June 5, 1991
  522.  * Passed:  current: the current line
  523.  * Returns: the start if the previous line
  524.  * Notes:   current should be at the start of the current line.
  525.  *          There must be a ^Z preceding the first line.
  526.  *          This function goes faster if machine works with WORDs.  See if
  527.  *           first BYTE in string is WORD aligned.  If it is not, get first
  528.  *           BYTE in string then the rest of string is WORD aligned.
  529.  *           The test for '\n' will pass a lot more than the test for
  530.  *           ^Z.  Set up the WORD align stuff first.
  531.  *           Since we are searching, by WORDs, backwards, the hi BYTE is the
  532.  *           prev BYTE and the al BYTE is two prev BYTEs (make sense?).
  533.  *           Code added at end to adjust segment:offset if needed.
  534.  */
  535. text_ptr find_prev( text_ptr current )
  536. {
  537.    _asm {
  538.         push    di              ; save di on stack
  539.  
  540.         mov     di, WORD PTR current    ; load OFFSET of current
  541. DECR1:  dec     di                      ; decrement it
  542.         mov     ax, WORD PTR current+2  ; load SEGMENT of current
  543.         mov     es, ax
  544.         cmp     di, 0           ; is offset of string == NULL?
  545.         jne     not_null        ; no, do string stuff
  546.         cmp     ax, 0           ; is segment of string == NULL?
  547.         je      return_null     ; yes, return NULL if string NULL
  548.         ALIGN   2
  549. not_null:
  550.         mov     bl, '\n'        ; keep '\n' in bl
  551.         mov     bh, CONTROL_Z   ; keep ^Z in bh
  552.         mov     ax, di          ; put copy of offset in ax
  553.         shr     ax, 1           ; shift right thru carry flag
  554.         jnc     on_boundary     ; if no carry, string is WORD aligned
  555. ;
  556. ; if we were to dec the pointer twice, it would be WORD aligned with the
  557. ; '--current'  BYTE in the AH register.  if ^Z test fails, might as well
  558. ; test the BYTE in the AL register.
  559. ;
  560. DECR2:  dec     di              ; dec offset one more so it is WORD aligned
  561.         mov     ax, WORD PTR es:[di]    ; might as well load WORD
  562.         cmp     ah, bh          ; is prev BYTE ^Z?
  563.         je      return_null     ; yes, return NULL
  564. ;
  565. ; now we are in the for loop - see commented C code at bottom.
  566. ; 'on_boundary' is not part of the for loop so jump past it if needed.
  567. ;
  568.         cmp     al, bl          ; is prev BYTE '\n'?
  569.         je      inc_pointer     ; yes, increment the pointer and return
  570.         cmp     al, bh          ; is it ^Z?
  571.         je      inc_pointer     ; yes, increment the pointer and return
  572.         jmp     SHORT for_loop  ;no, pointer is now WORD aligned - do for loop
  573.         ALIGN   2
  574. ;
  575. ; the string ended on an odd boundary and the DECR1 has now aligned the
  576. ; string on a WORD.  if we load a WORD, the '--current' BYTE would be in the
  577. ; AL register.
  578. ;
  579. on_boundary:
  580.         mov     ax, WORD PTR es:[di]    ; load --current, aligned on WORD
  581.         cmp     al, bh          ; is --current ^Z?
  582.         je      return_null     ; yes, return NULL
  583. ;
  584. ; now we are in the for loop and string is guaranteed WORD aligned.
  585. ; IMPORTANT: there are 2 cases if the test for '\n' or ^Z pass.
  586. ;            1) AH passed, so di must be increment twice for '++current'
  587. ;            2) AL passed, inc di once for '++current'
  588. ;
  589.         ALIGN   2
  590. for_loop:
  591.         dec     di              ; decrement di twice so it will be
  592.         dec     di              ; WORD aligned
  593.         mov     ax, WORD PTR es:[di]    ; string is WORD aligned
  594.         cmp     ah, bl          ; is --current '\n'?
  595.         je      next_even       ; yes, increment di twice to return ++current
  596.         cmp     ah, bh          ; is --current ^Z?
  597.         je      next_even       ; yes, increment di twice to return ++current
  598.         cmp     al, bl          ; look at low part of WORD, is it '\n'?
  599.         je      inc_pointer     ; yes, increment di once to return ++current
  600.         cmp     al, bh          ; is low part of WORD ^Z?
  601.         je      inc_pointer     ; yes, increment di once to return ++current
  602.         jmp     SHORT for_loop  ; get next WORD
  603.         ALIGN   2
  604. return_null:
  605.         xor     ax, ax          ; clear ax - offset = NULL
  606.         xor     dx, dx          ; clear dx - segment = NULL
  607.         jmp     SHORT get_out   ; return text_ptr in dx:ax - see Microsoft
  608.         ALIGN   2
  609. next_even:
  610.         inc     di              ; di is a WORD too far - inc di
  611. inc_pointer:
  612.         inc     di              ; ++current
  613.         mov     ax, di          ; put offset in ax
  614.         mov     dx, es          ; put segment in dx, return dx:ax - Microsoft
  615.         cmp     ax, 0x4000      ; are we within 16k of segment?
  616.         jae     get_out         ; no, get out
  617.         add     ax, 0x8000      ; yes, add 32k to offset
  618.         sub     dx, 0x0800      ; sub 0x0800 paragraphs to segment
  619.         ALIGN   2
  620. get_out:
  621.         pop     di              ; get back di from stack
  622.    }
  623.  
  624. /*
  625.    if (*--current == ^Z)
  626.       return NULL;
  627.    for (;;) {
  628.       if (*--current == '\n' || *current == ^Z)
  629.          return ++current;
  630.    }
  631. */
  632. }
  633.  
  634.  
  635. /*
  636.  * Name:    update_line
  637.  * Purpose: Display the current line in window
  638.  * Date:    June 5, 1991
  639.  * Passed:  window:  pointer to current window
  640.  * Notes:   Show string starting at column zero and if needed blank rest
  641.  *            of line.  Put max_col in cx and count down.  When we run into
  642.  *            '\n', cx contains number of columns to blank out.  Use the
  643.  *            fast 'rep stosw' to clear the end of line.
  644.  *          The C routine was probably fast enough, but let's do some
  645.  *            assembly because it's so fun.
  646.  *          To handle line lengths longer than 255 characters,
  647.  *            block begin and end columns were changed from real
  648.  *            to virtual columns in this display routine.
  649.  */
  650. void update_line( WINDOW *window )
  651. {
  652. text_ptr text;      /* current character of orig begin considered */
  653. char far *screen_ptr;
  654. int off;
  655. int attr;
  656. int line;
  657. int col;
  658. int bcol;
  659. int bc, ec;
  660. int normal, block;
  661. int max_col;
  662. int block_line;
  663. int show_eol;
  664. int len;
  665. int c;
  666. long rline;
  667. file_infos *file;
  668.  
  669.    if (window->rline > window->file_info->length)
  670.       return;
  671.    file = window->file_info;
  672.    max_col = window->end_col + 1 - window->start_col;
  673.    line = window->cline;
  674.    normal = g_display.text_color;
  675.    block = g_display.block_color;
  676.    show_eol = mode.show_eol;
  677.  
  678.    /*
  679.     * set the screen pointer to physical screen memory.
  680.     */
  681.    screen_ptr = g_display.display_address;
  682.          /* 160 = 80 chars + 80 attr  for each line */
  683.    off = line * 160 + window->start_col * 2;
  684.  
  685.    /*
  686.     * figure which line to display.  assume we are displaying window->cursor.
  687.     *   if the current line is in the line buffer, then text will be the
  688.     *   same as buff_line, which means we need to display the line_buff.
  689.     */
  690.    text = cpf( window->cursor );
  691.    if (g_status.copied && ptoul( text ) == ptoul( g_status.buff_line ))
  692.       text = g_status.line_buff;
  693.  
  694.    /*
  695.     * lets look at the base column.  if the line to display is shorter
  696.     *   than the base column, then set text to eol and we can't see the
  697.     *   eol either.
  698.     */
  699.    bc = window->bcol;
  700.    if (bc > 0) {
  701.       if ((col = linelen( text )) < bc) {
  702.          bc = col;
  703.          show_eol = FALSE;
  704.       }
  705.       text += bc;
  706.    }
  707.    bcol = window->bcol;
  708.    rline = window->rline;
  709.    if (file->block_type && rline >= file->block_br && rline <= file->block_er)
  710.       block_line = TRUE;
  711.    else
  712.       block_line = FALSE;
  713.  
  714.    /*
  715.     * do this if 1) a box block is marked, or 2) a stream block begins
  716.     *   and ends on the same line.
  717.     */
  718.    if (block_line == TRUE && (file->block_type == BOX ||
  719.          (file->block_type == STREAM &&
  720.          rline == file->block_br && rline == file->block_er))) {
  721.       len = linelen( text );
  722.  
  723.       /*
  724.        * start with the bc and ec equal to physical block marker.
  725.        */
  726.       bc = file->block_bc;
  727.       ec = file->block_ec;
  728.       if (ec < bcol || bc >= bcol + max_col)
  729.          /*
  730.           * we can't see block if ending column is less than the base col or
  731.           *   the beginning column is greater than max_col.
  732.           */
  733.          ec = bc = max_col + 1;
  734.       else if (ec < bcol + max_col) {
  735.          /*
  736.           * if the ec is less than the max column, make ec relative to
  737.           *   base column then figure the bc.
  738.           */
  739.          ec = ec - bcol;
  740.          if (bc < bcol)
  741.             bc = 0;
  742.          else
  743.             bc = bc - bcol;
  744.       } else if (bc < bcol + max_col) {
  745.          /*
  746.           * if the bc is less than the max column, make bc relative to
  747.           *   base column then figure the ec.
  748.           */
  749.          bc = bc - bcol;
  750.          if (ec > bcol + max_col)
  751.             ec = max_col;
  752.          else
  753.             ec = ec - bcol;
  754.       } else if (bc < bcol  &&  ec >= bcol + max_col) {
  755.          /*
  756.           * if the block is wider than the screen, make bc start at the
  757.           *   logical begin and make ec end at the logical end of the
  758.           *   window.
  759.           */
  760.          bc = 0;
  761.          ec = max_col;
  762.       }
  763.  
  764.  
  765.       _asm {
  766.         push    ds                      ; MUST save ds - push it on stack
  767.         push    si                      ; save si on stack
  768.         push    di                      ; save di on stack
  769.  
  770. ; on the stack so we can pop it when we're thru displaying line.
  771.         mov     ax, WORD PTR show_eol   ; get the show_eol flag
  772.         push    ax                      ; push the flag
  773.  
  774. ;
  775. ; set up local register variables
  776. ;
  777.         mov     ax, WORD PTR bc         ; get beginning column
  778.         mov     bl, al                  ; keep it in bl
  779.         mov     ax, WORD PTR ec         ; get ending column
  780.         mov     bh, al                  ; keep it in bh
  781.         mov     ax, WORD PTR normal     ; get normal attribute
  782.         mov     dl, al                  ; keep it in dl
  783.         mov     ax, WORD PTR block      ; get block attribute
  784.         mov     dh, al                  ; keep it in dh
  785.         mov     ax, WORD PTR max_col    ; get max number columns on screen
  786.         mov     ch, al                  ; keep it in ch
  787.         xor     cl, cl                  ; col = 0, keep col in cl
  788. ;
  789. ; load screen and text pointer
  790. ;
  791.         mov     di, WORD PTR screen_ptr         ; load OFFSET of screen ptr
  792.         add     di, WORD PTR off                ; add offset of line
  793.         mov     ax, WORD PTR screen_ptr+2       ; load SEGMENT of screen ptr
  794.         mov     es, ax
  795.         mov     si, WORD PTR text       ; load OFFSET of text ptr
  796.         mov     ax, WORD PTR text+2     ; load SEGMENT of text ptr
  797.         mov     ds, ax                  ; move segment of text in ds
  798.         cmp     si, 0                   ; is offset of text ptr == NULL?
  799.         jne     not_null                ; no, output string
  800.         cmp     ax, 0                   ; is segment of text ptr == NULL?
  801.         je      block_eol               ; yes, clear end of line
  802. not_null:
  803.         ALIGN   2
  804. top:
  805.         cmp     cl, ch          ; is col == max_col 0?
  806.         je      getout          ; yes, thru with line
  807.         lodsb                   ; get next char in string
  808.         cmp     al, CONTROL_Z   ; is it ^Z?
  809.         je      block_eol       ; yes, must check block past ^Z
  810.         cmp     al, '\n'        ; is it '\n'?
  811.         je      dspl_eol        ; yes, must check block past '\n'
  812.         mov     ah, dl          ; assume normal attribute
  813.         cmp     cl, bl          ; is col < bc? (less than beginning col)
  814.         jl      ch_out1         ; yes, show char and normal attribute
  815.         cmp     cl, bh          ; is col > ec? (greater than ending col)
  816.         jg      ch_out1         ; yes, show char and normal attribute
  817.         mov     ah, dh          ; must be in a block - show block attribute
  818. ch_out1:
  819.         stosw                   ; else show char on screen
  820.         inc     cl              ; ++col
  821.         jmp     SHORT top       ; get another character
  822.         ALIGN   2
  823. dspl_eol:
  824.         pop     ax              ; look at the show_eol flag
  825.         push    ax              ; push it back on stack
  826.         or      ax, ax          ; or the flag - test for 0
  827.         je      block_eol       ; show_eol flag is FALSE, blank line
  828.         mov     al, EOL_CHAR    ; load some eol indicator
  829.         mov     ah, dl          ; assume normal attribute
  830.         cmp     cl, bl          ; is col < bc? (less than beginning col)
  831.         jl      ch_out2         ; yes, show char and normal attribute
  832.         cmp     cl, bh          ; is col > ec? (greater than ending col)
  833.         jg      ch_out2         ; yes, show char and normal attribute
  834.         mov     ah, dh          ; must be in a block - show block attribute
  835.         ALIGN   2
  836. ch_out2:
  837.         stosw                   ; write eol and attribute to screen
  838.         inc     cl              ; ++col
  839.         cmp     cl, ch          ; is col == max_col?
  840.         je      getout          ; yes, we're done
  841.         ALIGN   2
  842. block_eol:
  843.         mov     al, ' '         ; clear rest of line w/ spaces
  844. b1:
  845.         mov     ah, dl          ; assume normal attribute
  846.         cmp     cl, bl          ; is col < bc? (less than beginning col)
  847.         jl      ch_out3         ; yes, show char and normal attribute
  848.         cmp     cl, bh          ; is col > ec? (greater than ending col)
  849.         jg      ch_out3         ; yes, show char and normal attribute
  850.         mov     ah, dh          ; must be in a block - show block attribute
  851.         ALIGN   2
  852. ch_out3:
  853.         stosw                   ; write blank and attribute to screen
  854.         inc     cl              ; ++col
  855.         cmp     cl, ch          ; is col == max_col?
  856.         jl      b1              ; while less output block
  857.         ALIGN   2
  858. getout:
  859.         add     sp, 2           ; "pop" the show_eol flag
  860.         pop     di
  861.         pop     si
  862.         pop     ds
  863.       }
  864. /*
  865.       for (col=0; col < max_col; col++) {
  866.          attr = normal;
  867.          if (col >= bc && col <= ec)
  868.             attr = block;
  869.          if (col < len)
  870.             c = text[col];
  871.          else
  872.             c = ' ';
  873.          update_char( c, col, line, attr );
  874.       }
  875. */
  876.    } else if (block_line == TRUE && file->block_type == STREAM &&
  877.               (rline == file->block_br || rline == file->block_er)) {
  878.       len = linelen( text );
  879.       if (rline == file->block_br)
  880.          bc = file->block_bc;
  881.       else {
  882.          bc = file->block_ec + 1;
  883.          ec = normal;
  884.          normal = block;
  885.          block = ec;
  886.       }
  887.       if (bc < bcol)
  888.          bc = 0;
  889.       else if (bc < bcol + max_col)
  890.          bc = bc - bcol;
  891.       else
  892.          bc = max_col + 1;
  893.  
  894.       _asm {
  895.         push    ds                      ; MUST save ds - push it on stack
  896.         push    si                      ; save si on stack
  897.         push    di                      ; save di on stack
  898.  
  899. ; on the stack so we can pop it when we're thru displaying line.
  900.         mov     ax, WORD PTR show_eol   ; get the show_eol flag
  901.         push    ax                      ; push the flag
  902. ;
  903. ; set up local register variables
  904. ;
  905.         mov     ax, WORD PTR bc         ; get beginning column
  906.         mov     bl, al                  ; keep it in bl
  907.         mov     ax, WORD PTR normal     ; get normal attribute
  908.         mov     dl, al                  ; keep it in dl
  909.         mov     ax, WORD PTR block      ; get block attribute
  910.         mov     dh, al                  ; keep it in dh
  911.         mov     ax, WORD PTR max_col    ; get max number columns on screen
  912.         mov     ch, al                  ; keep it in ch
  913.         xor     cl, cl                  ; col = 0, keep col in cl
  914. ;
  915. ; load screen and text pointer
  916. ;
  917.         mov     di, WORD PTR screen_ptr         ; load OFFSET of screen ptr
  918.         add     di, WORD PTR off                ; add offset of line
  919.         mov     ax, WORD PTR screen_ptr+2       ; load SEGMENT of screen ptr
  920.         mov     es, ax
  921.         mov     si, WORD PTR text       ; load OFFSET of text ptr
  922.         mov     ax, WORD PTR text+2     ; load SEGMENT of text ptr
  923.         mov     ds, ax                  ; move segment of text in ds
  924.         cmp     si, 0                   ; is offset of text ptr == NULL?
  925.         jne     nott_null               ; no, output string
  926.         cmp     ax, 0                   ; is segment of text ptr == NULL?
  927.         je      stream_eol              ; yes, clear end of line
  928. nott_null:
  929.         ALIGN   2
  930. ttop:
  931.         cmp     cl, ch          ; is col == max_col?
  932.         je      ggetout         ; yes, thru with line
  933.         lodsb                   ; get next char in string
  934.         cmp     al, CONTROL_Z   ; is it ^Z?
  935.         je      stream_eol      ; yes, must check block past ^Z
  936.         cmp     al, '\n'        ; is it '\n'?
  937.         je      ddspl_eol       ; yes, must check block past '\n'
  938.         mov     ah, dl          ; assume normal attribute
  939.         cmp     cl, bl          ; is col < bc? (less than beginning col)
  940.         jl      str_out1        ; yes, show char and normal attribute
  941.         mov     ah, dh          ; must be in a block - show block attribute
  942.         ALIGN   2
  943. str_out1:
  944.         stosw                   ; else show char on screen
  945.         inc     cl              ; ++col
  946.         jmp     SHORT ttop      ; get another character
  947.         ALIGN   2
  948.  
  949. ddspl_eol:
  950.         pop     ax              ; look at the show_eol flag
  951.         push    ax              ; push it back on stack
  952.         or      ax, ax          ; or the flag - test for 0
  953.         je      stream_eol      ; show_eol flag is FALSE, blank line
  954.         mov     al, EOL_CHAR    ; load some eol indicator
  955.         mov     ah, dl          ; assume normal attribute
  956.         cmp     cl, bl          ; is col < bc? (less than beginning col)
  957.         jl      str_out2        ; yes, show char and normal attribute
  958.         mov     ah, dh          ; must be in a block - show block attribute
  959.         ALIGN   2
  960. str_out2:
  961.         stosw                   ; write blank and attribute to screen
  962.         inc     cl              ; ++col
  963.         cmp     cl, ch          ; is col == max_col?
  964.         je      ggetout         ; yes, we're done
  965.         ALIGN   2
  966.  
  967. stream_eol:
  968.         mov     al, ' '         ; clear rest of line w/ spaces
  969.         ALIGN   2
  970. c1:
  971.         mov     ah, dl          ; assume normal attribute
  972.         cmp     cl, bl          ; is col < bc? (less than beginning col)
  973.         jl      str_out3        ; yes, show char and normal attribute
  974.         mov     ah, dh          ; must be in a block - show block attribute
  975.         ALIGN   2
  976. str_out3:
  977.         stosw                   ; write blank and attribute to screen
  978.         inc     cl              ; ++col
  979.         cmp     cl, ch          ; is col == max_col?
  980.         jl      c1              ; while less output block
  981.         ALIGN   2
  982. ggetout:
  983.         add     sp, 2           ; "pop" show_eol
  984.         pop     di
  985.         pop     si
  986.         pop     ds
  987.       }
  988. /*
  989.       for (col=0; col < max_col; col++) {
  990.          attr = normal;
  991.          if (col >= bc && col <= ec)
  992.             attr = block;
  993.          if (col < len)
  994.             c = text[col];
  995.          else
  996.             c = ' ';
  997.          update_char( c, col, line, attr );
  998.       }
  999. */
  1000.    } else {
  1001.       if (block_line)
  1002.          attr = block;
  1003.       else
  1004.          attr = normal;
  1005.       _asm {
  1006.         mov     dx, ds          ; MUST save ds - keep it in dx
  1007.         push    di              ; save di on stack
  1008.         push    si              ; save si on stack
  1009.  
  1010. ; on the stack so we can pop it when we're thru displaying line.
  1011.         mov     ax, WORD PTR show_eol   ; get the show_eol flag
  1012.         push    ax                      ; push the flag
  1013.  
  1014.         mov     bx, WORD PTR attr               ; keep attribute in bl
  1015.         mov     bh, '\n'                        ; keep '\n' in bh
  1016.         mov     cx, WORD PTR max_col            ; keep max_col in cx
  1017.         mov     di, WORD PTR screen_ptr         ; load OFFST of screen ptr
  1018.         add     di, WORD PTR off                ; add offset of line
  1019.         mov     ax, WORD PTR screen_ptr+2       ; load SEGMENT of screen ptr
  1020.         mov     es, ax
  1021.         mov     si, WORD PTR text       ; load OFFSET of text ptr
  1022.         mov     ax, WORD PTR text+2     ; load SEGMENT of text ptr
  1023.         mov     ds, ax                  ; move segment of text in ds
  1024.         cmp     si, 0                   ; is offset of pointer == NULL?
  1025.         jne     nnot_null               ; no, output string
  1026.         cmp     ax, 0                   ; is segment of pointer == NULL?
  1027.         je      clreol                  ; yes, then clear rest of line
  1028. nnot_null:
  1029.         mov     ah, bl                  ; get attribute
  1030.         ALIGN   2
  1031. topp:
  1032.         or      cx, cx          ; col == 0 ?
  1033.         je      getoutt         ; yes, thru with line
  1034.         lodsb                   ; get next char in string
  1035.         cmp     al, CONTROL_Z   ; is it ^Z
  1036.         je      clreol          ; yes, clear end of line
  1037.         cmp     al, bh          ; is it '\n'
  1038.         je      normeol          ; yes, clear end of line
  1039.         stosw                   ; else show char on screen
  1040.         dec     cx              ; --col, count down from max_column
  1041.         jmp     SHORT topp      ; get another character
  1042.         ALIGN   2
  1043.  
  1044. normeol:
  1045.         pop     ax              ; look at the show_eol flag
  1046.         push    ax              ; push it back on stack
  1047.         or      ax, ax          ; or the flag - test for 0
  1048.         je      clreol          ; show_eol flag is FALSE, blank line
  1049.         mov     al, EOL_CHAR    ; load some eol indicator
  1050.         mov     ah, bl          ; assume normal attribute
  1051.         stosw                   ; write blank and attribute to screen
  1052.         dec     cl              ; ++col
  1053.         or      cl, cl          ; is col == 0?
  1054.         je      getoutt         ; yes, we're done
  1055.         ALIGN   2
  1056.  
  1057. clreol:
  1058.         mov     ah, bl          ; get attribute
  1059.         mov     al, ' '         ; clear eol with ' '
  1060.         rep     stosw           ; count is in cx - set rest of line to ' '
  1061.         ALIGN   2
  1062. getoutt:
  1063.         add     sp, 2           ; "pop" show_eol
  1064.         pop     si
  1065.         pop     di
  1066.         mov     ds, dx
  1067.       }
  1068.    }
  1069. /*
  1070.    if (orig != NULL) {
  1071.       text = orig;
  1072.       screen_ptr = g_display.display_address + line * 160 + col * 2;
  1073.       for (; *text != '\n' && *text != ^Z && col < max_col; text++, col++) {
  1074.          *screen_ptr++ = *text;
  1075.          *screen_ptr++ = attr;
  1076.       }
  1077.    }
  1078.    if (col < max_col)
  1079.       eol_clear( col, line, attr );
  1080. */
  1081. }
  1082.  
  1083.  
  1084. /*
  1085.  * Name:    c_output
  1086.  * Purpose: Output one character on prompt lines
  1087.  * Date:    June 5, 1991
  1088.  * Passed:  c:     character to output to screen
  1089.  *          col:   col to display character
  1090.  *          line:  line number to display character
  1091.  *          attr:  attribute of character
  1092.  * Returns: none
  1093.  */
  1094. void c_output( int c, int col, int line, int attr )
  1095. {
  1096. void far *screen_ptr;
  1097. int off;
  1098.  
  1099.    screen_ptr = (void far *)g_display.display_address;
  1100.    off = line * 160 + col * 2;
  1101.  
  1102.    _asm {
  1103.         mov     bx, WORD PTR screen_ptr         ; load OFFSET of screen ptr
  1104.         add     bx, WORD PTR off                ; add offset of line:col
  1105.         mov     ax, WORD PTR screen_ptr+2       ; load SEGMENT of screen ptr
  1106.         mov     es, ax
  1107.         mov     cx, WORD PTR attr       ; get attribute
  1108.         mov     ah, cl                  ; put in ah
  1109.         mov     cx, WORD PTR c          ; get character
  1110.         mov     al, cl                  ; put in al
  1111.         mov     WORD PTR es:[bx], ax    ; show char on screen
  1112.    }
  1113.  
  1114. /*
  1115.    screen_ptr = g_display.display_address + line * 160 + col * 2;
  1116.    *screen_ptr++ = c;
  1117.    *screen_ptr = attr;
  1118. */
  1119. }
  1120.  
  1121.  
  1122. /*
  1123.  * Name:    s_output
  1124.  * Purpose: To output a string
  1125.  * Date:    June 5, 1991
  1126.  * Passed:  s:     string to output
  1127.  *          line:  line to display
  1128.  *          col:   column to begin display
  1129.  *          attr:  color to display string
  1130.  * Notes:   This function is used to output most strings not part of file text.
  1131.  *
  1132.  *          All strings in the SMALL memory model are in the default NEAR data
  1133.  *          segment (used in production version of tde).  Prototype the output
  1134.  *          string as far because when compiling for debugging, you must
  1135.  *          compile with the LARGE library since the /Zi code will not
  1136.  *          fit in the SMALL model.
  1137.  */
  1138. void s_output( char far *s, int line, int col, int attr )
  1139. {
  1140. void far *screen_ptr;
  1141. int off;
  1142. int max_col;
  1143.  
  1144.    max_col = g_display.ncols;
  1145.    screen_ptr = (void far *)g_display.display_address;
  1146.    off = line * 160 + col * 2;
  1147.  
  1148.    _asm {
  1149.         push    ds              ; save ds on stack
  1150.         push    di              ; save di on stack
  1151.         push    si              ; save si on stack
  1152.  
  1153.         mov     bx, WORD PTR attr               ; keep attribute in bx
  1154.         mov     cx, WORD PTR col                ; put cols in cx
  1155.         mov     dx, WORD PTR max_col            ; keep max_col in dx
  1156.         mov     di, WORD PTR screen_ptr         ; load OFFSET of screen ptr
  1157.         add     di, WORD PTR off                ; add offset of line:col
  1158.         mov     ax, WORD PTR screen_ptr+2       ; load SEGMENT of screen ptr
  1159.         mov     es, ax
  1160.         mov     si, WORD PTR s  ; load offset of string ptr
  1161.         or      si, si          ; is it == NULL?
  1162.         je      getout          ; yes, no output needed
  1163.         mov     ax, WORD PTR s+2        ; load segment of string ptr
  1164.         or      ax, ax          ; is pointer == NULL?
  1165.         je      getout          ; yes, no output needed
  1166.         mov     ds, ax          ; load segment of text in ds
  1167.         mov     ah, bl          ; put attribute in AH
  1168.         ALIGN   2
  1169. top:
  1170.         cmp     cx, dx          ; col < max_cols?
  1171.         jge     getout          ; no, thru with line
  1172.         lodsb                   ; get next char in string - put in al
  1173.         or      al, al          ; is it '\0'
  1174.         je      getout          ; yes, end of string
  1175.         cmp     al, '\n'        ; is it '\n'?
  1176.         je      getout          ; yes, end of string
  1177.         stosw                   ; else show attr + char on screen (ah + al)
  1178.         inc     cx              ; col++
  1179.         jmp     SHORT top       ; get another character
  1180.         ALIGN   2
  1181. getout:
  1182.         pop     si              ; get back si
  1183.         pop     di              ; get back di
  1184.         pop     ds              ; get back ds
  1185.    }
  1186.  
  1187. /*
  1188.    screen_ptr = g_display.display_address + line * 160 + col * 2;
  1189.    max_col = g_display.ncols;
  1190.    while (*s && col < max) {
  1191.       *screen_ptr++ = *s++;
  1192.       *screen_ptr++ = attr;
  1193.    }
  1194. */
  1195. }
  1196.  
  1197.  
  1198. /*
  1199.  * Name:    eol_clear
  1200.  * Purpose: To clear the line from col to max columns
  1201.  * Date:    June 5, 1991
  1202.  * Passed:  col:   column to begin clear
  1203.  *          line:  line to clear
  1204.  *          attr:  color to clear
  1205.  * Notes:   Basic assembly
  1206.  */
  1207. void eol_clear( int col, int line, int attr )
  1208. {
  1209. int max_col;
  1210. void far *screen_ptr;
  1211. int off;
  1212.  
  1213.    max_col = g_display.ncols;
  1214.    screen_ptr = (void far *)g_display.display_address;
  1215.    off = line * 160 + col * 2;
  1216.  
  1217.    _asm {
  1218.         push    di                              ; save di on stack
  1219.  
  1220.         mov     bx, WORD PTR attr               ; keep attribute in bx
  1221.         mov     dx, WORD PTR col                ; put cols in dx
  1222.         mov     cx, WORD PTR max_col            ; put max_col in cx
  1223.         cmp     dx, cx                          ; max_cols < cols?
  1224.         jge     getout                          ; no, thru with line
  1225.         sub     cx, dx                          ; number of column to clear
  1226.         mov     di, WORD PTR screen_ptr         ; load OFFSET of screen ptr
  1227.         add     di, WORD PTR off                ; add offset of line:col
  1228.         mov     ax, WORD PTR screen_ptr+2       ; load SEGMENT of screen ptr
  1229.         mov     es, ax
  1230.         mov     ah, bl                          ; get attribute in ah
  1231.         mov     al, ' '                         ; store ' ' in al
  1232.         rep     stosw                           ; clear to end of line
  1233. getout:
  1234.         pop     di                              ; get back di from stack
  1235.    }
  1236.  
  1237. /*
  1238.    for (; col < g_display.ncols; col++) {
  1239.       *p++ = ' ';
  1240.       *p++ = attr;
  1241.    }
  1242. */
  1243. }
  1244.  
  1245.  
  1246. /*
  1247.  * Name:    window_eol_clear
  1248.  * Purpose: To clear the line from start_col to end_col
  1249.  * Date:    June 5, 1991
  1250.  * Passed:  col:   column to begin clear
  1251.  *          line:  line to clear
  1252.  *          attr:  color to clear
  1253.  * Notes:   Basic assembly
  1254.  */
  1255. void window_eol_clear( WINDOW *window, int attr )
  1256. {
  1257. int max_col;
  1258. void far *screen_ptr;
  1259. int off;
  1260.  
  1261.    screen_ptr = (void far *)g_display.display_address;
  1262.    off = window->cline * 160 + window->start_col * 2;
  1263.    max_col = window->end_col + 1 - window->start_col;
  1264.  
  1265.    _asm {
  1266.         push    di                              ; save di on stack
  1267.  
  1268.         mov     bx, WORD PTR attr               ; keep attribute in bx
  1269.         mov     cx, WORD PTR max_col            ; put max_col in cx
  1270.         mov     di, WORD PTR screen_ptr         ; load OFFSET of screen ptr
  1271.         add     di, WORD PTR off                ; add offset of line:col
  1272.         mov     ax, WORD PTR screen_ptr+2       ; load SEGMENT of screen ptr
  1273.         mov     es, ax
  1274.         mov     ah, bl                          ; get attribute in ah
  1275.         mov     al, ' '                         ; store ' ' in al
  1276.         rep     stosw                           ; clear to end of line
  1277. getout:
  1278.         pop     di                              ; get back di from stack
  1279.    }
  1280.  
  1281. /*
  1282.    for (; col < g_display.ncols; col++) {
  1283.       *p++ = ' ';
  1284.       *p++ = attr;
  1285.    }
  1286. */
  1287. }
  1288.  
  1289.  
  1290. /*
  1291.  * Name:    upper_asm
  1292.  * Purpose: To convert all lower case characters to upper characters
  1293.  * Date:    June 5, 1991
  1294.  * Passed:  s:    the starting point
  1295.  *          count: number of characters to convert (unsigned)
  1296.  * Returns: none
  1297.  * Notes:   This function goes faster if machine works with WORDs.  See if
  1298.  *           first BYTE in string is WORD aligned.  If it is not, get first
  1299.  *           BYTE in string then the rest of string is WORD aligned.
  1300.  *
  1301.  *          The pointer should have been normalized, using the nptos, as
  1302.  *          this function does not handle segment wrap.
  1303.  *
  1304.  *          ax, ah, al  = characters from string
  1305.  *          bl          = 'a'
  1306.  *          bh          = 'z'
  1307.  *          cx          = number of characters to look at
  1308.  *          dl          = 0x20, xor 0x20 with lower case to get upper case
  1309.  *          ds:si       = far pointer to character string
  1310.  *                        use ds:si so there is no segment override
  1311.  */
  1312. void upper_asm( text_ptr s, unsigned count )
  1313. {
  1314.    _asm {
  1315.         push    ds              ; save ds on stack
  1316.         push    si              ; save si on stack
  1317.  
  1318.         mov     cx, WORD PTR count      ; load count in cx
  1319.         jcxz    get_out                 ; 1st, if count == 0 then do nothing
  1320.         mov     si, WORD PTR s          ; load OFFSET of s
  1321.         mov     ax, WORD PTR s+2        ; load SEGMENT of s
  1322.         mov     ds, ax
  1323.         or      si, si          ; is offset of string == NULL or 0?
  1324.         jne     not_null        ; no, do string stuff
  1325.         or      ax, ax          ; is segment of string == NULL or 0?
  1326.         je      get_out         ; yes, don't upper case a NULL string
  1327.         ALIGN   2
  1328. not_null:
  1329.         mov     bh, 'z'         ; keep 'z' in bh
  1330.         mov     bl, 'a'         ; keep 'a' in bl
  1331.         mov     dl, 0x20        ; keep 0x20 in dl
  1332.         mov     ax, si          ; move offset of si to ax
  1333.         shr     ax, 1           ; shift right into carry flag
  1334.         jnc     top             ; is string WORD aligned?
  1335.         mov     al, BYTE PTR [si]       ; no, get a BYTE
  1336.         cmp     al, bl          ; is al < 'a' (use unsigned test)
  1337.         jb      word_align      ; yes, dec count and align on WORD
  1338.         cmp     al, bh          ; is al > 'z' (use unsigned test)
  1339.         ja      word_align      ; yes, dec count and align on WORD
  1340.         xor     al, dl          ; convert lower case to upper
  1341.         mov     BYTE PTR [si], al       ; store the character or BYTE
  1342.         ALIGN   2
  1343. word_align:
  1344.         inc     si              ; inc the string pointer - now WORD aligned
  1345.         dec     cx              ; decrement the character count
  1346.         jcxz    get_out         ; if count or cx == 0 then we're done, get_out
  1347.         ALIGN   2
  1348. top:
  1349.         mov     ax, WORD PTR [si]       ; string is WORD aligned, get two BYTEs
  1350.         cmp     al, bl          ; is al < 'a'?
  1351.         jb      upper_hi        ; yes, dec count and check the hi byte
  1352.         cmp     al, bh          ; is al > 'z'?
  1353.         ja      upper_hi        ; yes, dec count and check the hi byte
  1354.         xor     al, dl          ; convert lower case to upper
  1355.         ALIGN   2
  1356. upper_hi:
  1357.         dec     cx              ; decrement the count
  1358.         jcxz    clean_up        ; if count or cx == 0 then we're done, clean_up
  1359.         cmp     ah, bl          ; is al < 'a'?
  1360.         jb      save_word       ; yes, dec count and do next word
  1361.         cmp     ah, bh          ; is al > 'z'?
  1362.         ja      save_word       ; yes, dec count and do next word
  1363.         xor     ah, dl          ; convert lower case to upper
  1364.         ALIGN   2
  1365. save_word:
  1366.         mov     WORD PTR [si], ax       ; else, save changes
  1367.         dec     cx              ; decrement the count
  1368.         jcxz    get_out         ; if count or cx == 0 then we're done, get_out
  1369.         inc     si              ; increment the pointer to next word
  1370.         inc     si
  1371.         jmp     SHORT top       ; look at next WORD
  1372.         ALIGN   2
  1373. clean_up:
  1374.         mov     WORD PTR [si], ax       ; else, save changes then we're done
  1375.         ALIGN   2
  1376. get_out:
  1377.         pop     si              ; get back si from stack
  1378.         pop     ds              ; get back ds from stack
  1379.    }
  1380. /*
  1381.    while (count-- > 0) {
  1382.       if (*s >= 'a' && *s <= 'z')
  1383.          *s++ &= 0xdf;
  1384.    }
  1385. */
  1386. }
  1387.  
  1388.  
  1389. /*
  1390.  * Name:    lower_asm
  1391.  * Purpose: To convert all upper case characters to lower characters
  1392.  * Date:    June 5, 1991
  1393.  * Passed:  s:    the starting point
  1394.  *          count: number of characters to convert (unsigned)
  1395.  * Returns: none
  1396.  * Notes:   This function goes faster if machine works with WORDs.  See if
  1397.  *           first BYTE in string is WORD aligned.  If it is not, get first
  1398.  *           BYTE in string then the rest of string is WORD aligned.
  1399.  *
  1400.  *          The pointer should have been normalized, using the nptos, as
  1401.  *          this function does not handle segment wrap.  One could safely
  1402.  *          pass a count of 0xFFF0 if the pointer has been normalized to
  1403.  *          a segment
  1404.  *
  1405.  *          ax, ah, al  = characters from string
  1406.  *          bl          = 'A'
  1407.  *          bh          = 'Z'
  1408.  *          cx          = number of characters to look at, unsigned
  1409.  *          dl          = 0x20, or upper case with 0x20 to get lower case
  1410.  *          ds:si       = far pointer to character string
  1411.  *                        use ds:si so there is no segment override
  1412.  */
  1413. void lower_asm( text_ptr s, unsigned count )
  1414. {
  1415.    _asm {
  1416.         push    ds              ; save ds on stack
  1417.         push    si              ; save si on stack
  1418.  
  1419.         mov     cx, WORD PTR count      ; load count in cx
  1420.         jcxz    get_out                 ; 1st, if count == 0 then do nothing
  1421.         mov     si, WORD PTR s          ; load OFFSET of s
  1422.         mov     ax, WORD PTR s+2        ; load SEGMENT of s
  1423.         mov     ds, ax
  1424.         or      si, si          ; is offset of string == NULL or 0?
  1425.         jne     not_null        ; no, do string stuff
  1426.         or      ax, ax          ; is segment of string == NULL or 0?
  1427.         je      get_out         ; yes, don't upper case a NULL string
  1428.         ALIGN   2
  1429. not_null:
  1430.         mov     bh, 'Z'         ; keep 'z' in bh
  1431.         mov     bl, 'A'         ; keep 'a' in bl
  1432.         mov     dl, 0x20        ; keep 0x20 in dl
  1433.         mov     ax, si          ; move offset of si to ax
  1434.         shr     ax, 1           ; shift right into carry flag
  1435.         jnc     top             ; is string WORD aligned?
  1436.         mov     al, BYTE PTR [si]       ; no, get a BYTE
  1437.         cmp     al, bl          ; is al < 'A' (use unsigned test)
  1438.         jb      word_align      ; yes, dec count and align on WORD
  1439.         cmp     al, bh          ; is al > 'Z' (use unsigned test)
  1440.         ja      word_align      ; yes, dec count and align on WORD
  1441.         or      al, dl          ; convert upper case to lower
  1442.         mov     BYTE PTR [si], al       ; store the character or BYTE
  1443.         ALIGN   2
  1444. word_align:
  1445.         inc     si              ; inc the string pointer - now WORD aligned
  1446.         dec     cx              ; decrement the character count
  1447.         jcxz    get_out         ; if count or cx == 0 then we're done
  1448.         ALIGN   2
  1449. top:
  1450.         mov     ax, WORD PTR [si]       ; string is WORD aligned, get two BYTEs
  1451.         cmp     al, bl          ; is al < 'A'?
  1452.         jb      lower_hi        ; yes, dec count and check the hi byte
  1453.         cmp     al, bh          ; is al > 'Z'?
  1454.         ja      lower_hi        ; yes, dec count and check the hi byte
  1455.         or      al, dl          ; convert upper case to lower
  1456.         ALIGN   2
  1457. lower_hi:
  1458.         dec     cx              ; decrement the character count
  1459.         jcxz    clean_up        ; if count or cx == 0 then we're done, clean_up
  1460.         cmp     ah, bl          ; is al < 'A'?
  1461.         jb      save_word       ; yes, dec count and do next word
  1462.         cmp     ah, bh          ; is al > 'Z'?
  1463.         ja      save_word       ; yes, dec count and do next word
  1464.         or      ah, dl          ; convert upper case to lower
  1465.         ALIGN   2
  1466. save_word:
  1467.         mov     WORD PTR [si], ax       ; else, save changes
  1468.         dec     cx              ; decrement the count
  1469.         jcxz    get_out         ; if count or cx == 0 then we're done, get out
  1470.         inc     si              ; increment the pointer to next word
  1471.         inc     si
  1472.         jmp     SHORT top       ; look at next WORD
  1473.         ALIGN   2
  1474. clean_up:
  1475.         mov     WORD PTR [si], ax       ; else, save changes then we're done
  1476.         ALIGN   2
  1477. get_out:
  1478.         pop     si              ; get back si from stack
  1479.         pop     ds              ; get back ds from stack
  1480.    }
  1481. /*
  1482.    while (count-- > 0) {
  1483.       if (*s >= 'a' && *s <= 'z')
  1484.          *s++ &= 0xdf;
  1485.    }
  1486. */
  1487. }
  1488.  
  1489.  
  1490. /*
  1491.  * Name:    strip_asm
  1492.  * Purpose: To strip bit 7 from characters
  1493.  * Date:    June 5, 1991
  1494.  * Passed:  s:    the starting point, which should be normalized to a segment
  1495.  *          count: number of characters to strip (unsigned)
  1496.  *                 count should not be greater than 0xfff0
  1497.  * Returns: none
  1498.  * Notes:   This function goes faster if machine works with WORDs.  See if
  1499.  *           first BYTE in string is WORD aligned.  If it is not, get first
  1500.  *           BYTE in string then the rest of string is WORD aligned.
  1501.  *
  1502.  *          The pointer should have been normalized, using the nptos, as
  1503.  *          this function does not handle segment wrap.
  1504.  *
  1505.  *          ax, ah, al  = characters from string
  1506.  *          bl          = 01111111 or 0x7f to strip the hi bit from characters
  1507.  *          cx          = number of characters to look at
  1508.  *          ds:si       = far pointer to character string
  1509.  *                        use ds:si so there is no segment override
  1510.  */
  1511. void strip_asm( text_ptr s, unsigned count )
  1512. {
  1513.    _asm {
  1514.         push    ds              ; save ds on stack
  1515.         push    si              ; save si on stack
  1516.  
  1517.         mov     cx, WORD PTR count      ; load count in cx
  1518.         jcxz    get_out                 ; 1st, if count == 0 then do nothing
  1519.         mov     si, WORD PTR s          ; load OFFSET of s
  1520.         mov     ax, WORD PTR s+2        ; load SEGMENT of s
  1521.         mov     ds, ax
  1522.         or      si, si          ; is offset of string == NULL or 0?
  1523.         jne     not_null        ; no, do string stuff
  1524.         or      ax, ax          ; is segment of string == NULL or 0?
  1525.         je      get_out         ; yes, don't upper case a NULL string
  1526.         ALIGN   2
  1527. not_null:
  1528.         mov     bl, 0x7f        ; turn all bits except high bit in bl
  1529.         mov     ax, si          ; move offset of si to ax
  1530.         shr     ax, 1           ; shift right into carry flag
  1531.         jnc     top             ; is string WORD aligned?
  1532.         mov     al, BYTE PTR [si]       ; no, get a BYTE
  1533.         and     al, bl          ; strip the high bit
  1534.         mov     BYTE PTR [si], al       ; store the character or BYTE
  1535. word_align:
  1536.         inc     si              ; inc the string pointer - now WORD aligned
  1537.         dec     cx              ; dec the character count
  1538.         jcxz    get_out         ; if count or cx == 0 then we're done
  1539.         ALIGN   2
  1540. top:
  1541.         mov     ax, WORD PTR [si]       ; string is WORD aligned, get two BYTEs
  1542.         and     al, bl          ; strip the hi bit from the lo byte
  1543.         dec     cx              ; decrement the count
  1544.         jcxz    clean_up        ; if count or cx == 0 then let's clean up
  1545.         and     ah, bl          ; strip the hi bit from the hi byte
  1546.         mov     WORD PTR [si], ax       ; save changes
  1547.         dec     cx              ; decrement the count
  1548.         jcxz    get_out         ; if count or cx == 0 then we're done
  1549.         inc     si              ; increment the pointer to next word
  1550.         inc     si
  1551.         jmp     SHORT top       ; look at next WORD
  1552.         ALIGN   2
  1553. clean_up:
  1554.         mov     WORD PTR [si], ax       ; else, save changes then we're done
  1555.         ALIGN   2
  1556. get_out:
  1557.         pop     si              ; get back si from stack
  1558.         pop     ds              ; get back ds from stack
  1559.    }
  1560. /*
  1561.    while (count-- > 0) {
  1562.       if (*s >= 'a' && *s <= 'z')
  1563.          *s++ &= 0xdf;
  1564.    }
  1565. */
  1566. }
  1567.  
  1568.  
  1569. /*
  1570.  * Name:    get_fattr
  1571.  * Purpose: To get dos file attributes
  1572.  * Date:    December 26, 1991
  1573.  * Passed:  fname: ASCIIZ file name.  Null terminated file name
  1574.  *          fattr: pointer to file attributes
  1575.  * Returns: 0 if successfull, non zero if not
  1576.  * Notes:   Uses the DOS function to get file attributes.  I really didn't
  1577.  *           like the file attribute functions in the C library:  fstat() and
  1578.  *           stat() or access() and chmod().
  1579.  *           FYI, File Attributes:
  1580.  *              0x00 = Normal.  Can be read or written w/o restriction
  1581.  *              0x01 = Read-only.  Cannot be opened for write; a file with
  1582.  *                     the same name cannot be created.
  1583.  *              0x02 = Hidden.  Not found by directory search.
  1584.  *              0x04 = System.  Not found by directory search.
  1585.  *              0x08 = Volumn Label.
  1586.  *              0x10 = Directory.
  1587.  *              0x20 = Archive.  Set whenever the file is changed, or
  1588.  *                     cleared by the Backup command.
  1589.  *           Return codes:
  1590.  *              0 = No error
  1591.  *              1 = AL not 0 or 1
  1592.  *              2 = file is invalid or does not exist
  1593.  *              3 = path is invalid or does not exist
  1594.  *              5 = Access denied
  1595.  */
  1596. int  get_fattr( char far *fname, int *fattr )
  1597. {
  1598. int rc;                 /* return code */
  1599. int attr;
  1600.  
  1601.    _asm {
  1602.         push    ds
  1603.         mov     dx, WORD PTR fname      ; get OFFSET of filename string
  1604.         mov     ax, WORD PTR fname+2    ; get SEGMENT of filename string
  1605.         mov     ds, ax                  ; put SEGMENT in ds
  1606.         mov     ax, 0x4300              ; function:  get file attributes
  1607.         int     0x21                    ; DOS interrupt
  1608.         pop     ds
  1609.  
  1610.         jc      an_error                ; save the error code from get attr
  1611.         xor     ax, ax                  ; if no carry, no error
  1612.         jmp     SHORT get_out           ; lets get out
  1613. an_error:
  1614.         xor     cx, cx                  ; if error, then zero out cx - attrs
  1615. get_out:
  1616.         mov     WORD PTR rc, ax         ; ax contains error number on error
  1617.         mov     WORD PTR attr, cx       ; cx contains file attributes
  1618.    }
  1619.    *fattr = attr;
  1620.    if (ceh.flag == ERROR)
  1621.       rc = ERROR;
  1622.    return( rc );
  1623. }
  1624.  
  1625.  
  1626. /*
  1627.  * Name:    set_fattr
  1628.  * Purpose: To set dos file attributes
  1629.  * Date:    December 26, 1991
  1630.  * Passed:  fname: ASCIIZ file name.  Null terminated file name
  1631.  *          fattr: file attributes
  1632.  * Returns: 0 if successfull, non zero if not
  1633.  * Notes:   Uses the DOS function to get file attributes.
  1634.  *           Return codes:
  1635.  *              0 = No error
  1636.  *              1 = AL not 0 or 1
  1637.  *              2 = file is invalid or does not exist
  1638.  *              3 = path is invalid or does not exist
  1639.  *              5 = Access denied
  1640.  */
  1641. int  set_fattr( char far *fname, int fattr )
  1642. {
  1643. int rc;                 /* return code */
  1644.  
  1645.    _asm {
  1646.         push    ds
  1647.         mov     dx, WORD PTR fname      ; get OFFSET of filename string
  1648.         mov     ax, WORD PTR fname+2    ; get SEGMENT of filename string
  1649.         mov     ds, ax                  ; put SEGMENT in ds
  1650.         mov     cx, WORD PTR fattr      ; cx contains file attributes
  1651.         mov     ax, 0x4301              ; function:  get file attributes
  1652.         int     0x21                    ; DOS interrupt
  1653.         pop     ds
  1654.  
  1655.         jc      get_out                 ; save the error code from get attr
  1656.         xor     ax, ax                  ; if no carry, no error
  1657. get_out:
  1658.         mov     WORD PTR rc, ax         ; ax contains error number on error
  1659.    }
  1660.    if (ceh.flag == ERROR)
  1661.       rc = ERROR;
  1662.    return( rc );
  1663. }
  1664.  
  1665.  
  1666. /*
  1667.  * Name:    get_current_directory
  1668.  * Purpose: get current directory
  1669.  * Date:    February 13, 1992
  1670.  * Passed:  path:  pointer to buffer to store path
  1671.  *          drive: drive to get current directory
  1672.  * Notes:   use simple DOS interrupt
  1673.  */
  1674. int  get_current_directory( char far *path, int drive )
  1675. {
  1676. int rc;
  1677.  
  1678.    _asm {
  1679.         push    si                      ; save register vars if any
  1680.         push    ds                      ; save ds
  1681.  
  1682.         mov     dx, WORD PTR drive      ; dl = drive, 0 = default, 1 = a, etc..
  1683.         mov     si, WORD PTR path       ; get OFFSET of path
  1684.         mov     ax, WORD PTR path+2     ; get SEGMENT of path
  1685.         mov     ds, ax                  ; put it in ds
  1686.         mov     ah, 0x47                ; function 0x47 == get current dir
  1687.         int     0x21                    ; standard DOS interrupt
  1688.         xor     ax, ax                  ; zero out ax, return OK if no error
  1689.         jnc     no_error                ; if carry set, then an error
  1690.         mov     ax, -1                  ; return -1 if error
  1691. no_error:
  1692.         pop     ds                      ; get back ds
  1693.         pop     si                      ; get back si
  1694.         mov     WORD PTR rc, ax         ; save return code
  1695.    }
  1696.    if (ceh.flag == ERROR)
  1697.       rc = ERROR;
  1698.    return( rc );
  1699. }
  1700.  
  1701.  
  1702. /*
  1703.  * Name:    set_current_directory
  1704.  * Purpose: set current directory
  1705.  * Date:    February 13, 1992
  1706.  * Passed:  new_path: directory path which may include drive letter
  1707.  * Notes:   use simple DOS interrupt
  1708.  */
  1709. int  set_current_directory( char far *new_path )
  1710. {
  1711. int rc;
  1712.  
  1713.    _asm {
  1714.         push    ds                      ; save ds
  1715.  
  1716.         mov     dx, WORD PTR new_path   ; get OFFSET of new_path
  1717.         mov     ax, WORD PTR new_path+2 ; get SEGMENT of new_path
  1718.         mov     ds, ax                  ; put it in ds
  1719.         mov     ah, 0x3b                ; function 0x3b == set current dir
  1720.         int     0x21                    ; standard DOS interrupt
  1721.         xor     ax, ax                  ; zero out ax, return OK if no error
  1722.         jnc     no_error                ; if carry set, then an error
  1723.         mov     ax, -1                  ; return -1 if error
  1724. no_error:
  1725.         pop     ds                      ; get back ds
  1726.         mov     WORD PTR rc, ax         ; save return code
  1727.    }
  1728.    if (ceh.flag == ERROR)
  1729.       rc = ERROR;
  1730.    return( rc );
  1731. }
  1732.  
  1733.  
  1734. /*
  1735.  * Name:    hlight_line
  1736.  * Date:    July 21, 1991
  1737.  * Passed:  x:     column to begin hi lite
  1738.  *          y:     line to begin hi lite
  1739.  *          lgth:  number of characters to hi lite
  1740.  *          attr:  attribute color
  1741.  * Notes:   The attribute byte is the hi byte.
  1742.  */
  1743. void hlight_line( int x, int y, int lgth, int attr )
  1744. {
  1745. int off;
  1746. void far *screen_ptr;
  1747.  
  1748.    screen_ptr = (void far *)g_display.display_address;
  1749.    off = y * 160 + 2 * x + 1;  /* add one - so it points to attribute byte */
  1750.    _asm {
  1751.         push    di              ; save di
  1752.  
  1753.         mov     cx, lgth        ; number of characters to change color
  1754.  
  1755.         mov     di, WORD PTR screen_ptr ; get destination - video memory
  1756.         add     di, off                 ; add offset
  1757.         mov     ax, WORD PTR screen_ptr+2
  1758.         mov     es, ax
  1759.         mov     ax, attr        ; attribute
  1760. lite_len:
  1761.         stosb                   ; store a BYTE
  1762.         inc     di              ; skip over character to next attribute
  1763.         loop    lite_len        ; change next attribute
  1764.         pop     di              ; restore di
  1765.    }
  1766. }
  1767.  
  1768.  
  1769. /*
  1770.  * Name:    cls
  1771.  * Purpose: clear screen
  1772.  * Date:    June 5, 1991
  1773.  * Notes:   Call the video BIOS routine to clear the screen.
  1774.  */
  1775. void cls( void )
  1776. {
  1777. int line;
  1778.  
  1779.    line = g_display.nlines+1;
  1780.    _asm {
  1781.         xor     ch, ch                  ; starting row in ch = 0
  1782.         xor     cl, cl                  ; starting column in cl = 0
  1783.         mov     ax, WORD PTR line       ; get ending row
  1784.         mov     dh, al                  ; put it in dh
  1785.         mov     dl, 79                  ; ending column in dl = 79
  1786.         mov     bh, 7                   ; attribute in bh  = 7 (normal)
  1787.         mov     al, 0                   ; get number of lines
  1788.         mov     ah, 6                   ; get function number
  1789.         push    bp                      ; some BIOS versions wipe out bp
  1790.         int     0x10
  1791.         pop     bp
  1792.    }
  1793. }
  1794.  
  1795.  
  1796. /*
  1797.  * Name:    set_cursor_size
  1798.  * Purpose: To set cursor size according to insert mode.
  1799.  * Date:    June 5, 1991
  1800.  * Passed:  csize:  desired cursor size
  1801.  * Notes:   use the global display structures to set the cursor size
  1802.  */
  1803. void set_cursor_size( int csize )
  1804. {
  1805.    _asm {
  1806.         mov     ah, 1                   ; function 1 - set cursor size
  1807.         mov     cx, WORD PTR csize      ; get cursor size ch:cl == top:bot
  1808.         int     VIDEO_INT               ; video interrupt = 10h
  1809.    }
  1810. }
  1811.  
  1812.  
  1813. /*
  1814.  * Name:    findfirst
  1815.  * Purpose: find the first file matching a pattern using DOS interrupt
  1816.  * Date:    January 6, 1992
  1817.  * Passed:  dta:    disk transfer address
  1818.  *          path:   path to search for files
  1819.  *          f_attr: attributes of files to search for
  1820.  * Notes:   return codes for findfirst:
  1821.  *             0  no error
  1822.  *             2  file is invalid or does not exist
  1823.  *             3  path is invalid or does not exist
  1824.  *            18  no matching directory entry was found
  1825.  *            -1  check the critical error flag for critical errors
  1826.  */
  1827. int  findfirst( DTA far *dta, char far *path, int f_attr )
  1828. {
  1829. void far *old_dta;
  1830. void far *new_dta;
  1831. int rc;
  1832.  
  1833.    new_dta = (void far *)dta;
  1834.    _asm {
  1835.  
  1836. ; save the old dta
  1837.         mov     ah, 0x2f                ; DOS get dta
  1838.         int     0x21                    ; DOS interrupt
  1839.         mov     WORD PTR old_dta, bx    ; save OFFSET of old DTA
  1840.         mov     ax, es
  1841.         mov     WORD PTR old_dta+2, ax  ; save SEGMENT of old DTA
  1842.  
  1843. ; set the new dta
  1844.         push    ds                      ; save ds
  1845.         mov     dx, WORD PTR new_dta    ; get OFFSET of new dta
  1846.         mov     ax, WORD PTR new_dta+2  ; get SEGMENT of new dta
  1847.         mov     ds, ax                  ; put it in ds
  1848.         mov     ah, 0x1a                ; DOS set dta
  1849.         int     0x21                    ; DOS interrupt
  1850.         pop     ds                      ; get back ds
  1851.  
  1852. ; find first matching file
  1853.         push    ds                      ; save ds
  1854.         mov     cx, WORD PTR f_attr     ; file attributes to search for
  1855.         mov     dx, WORD PTR path       ; get OFFSET of path
  1856.         mov     ax, WORD PTR path+2     ; get SEGMENT of path
  1857.         mov     ds, ax                  ; put it in ds
  1858.         mov     ah, 0x4e                ; DOS find first file
  1859.         int     0x21                    ; DOS interrupt
  1860.         pop     ds                      ; get back ds
  1861.  
  1862. ; save the return code
  1863.         jc      an_error                ; carry is set if an error occured
  1864.         xor     ax, ax                  ; zero out ax, return OK if no error
  1865. an_error:
  1866.         mov     WORD PTR rc, ax         ; save the return code
  1867.  
  1868. ; get back old dta
  1869.         push    ds                      ; save ds
  1870.         mov     dx, WORD PTR old_dta    ; get OFFSET of old dta
  1871.         mov     ax, WORD PTR old_dta+2  ; get SEGMENT of old dta
  1872.         mov     ds, ax                  ; put it in ds
  1873.         mov     ah, 0x1a                ; DOS set dta
  1874.         int     0x21                    ; DOS interrupt
  1875.         pop     ds                      ; get back ds
  1876.    }
  1877.    if (ceh.flag == ERROR)
  1878.       rc = ERROR;
  1879.    return( rc );
  1880. }
  1881.  
  1882.  
  1883. /*
  1884.  * Name:    findnext
  1885.  * Purpose: find the next file matching a pattern using DOS interrupt
  1886.  * Date:    January 6, 1992
  1887.  * Passed:  dta:  disk transfer address
  1888.  * Notes:   findfirst() MUST be called before calling this function.
  1889.  *          return codes for findnext:
  1890.  *             0  no error
  1891.  *             2  path is invalid or does not exist
  1892.  *            18  no matching directory entry was found
  1893.  *            -1  check the critical error flag for critical errors
  1894.  */
  1895. int  findnext( DTA far *dta )
  1896. {
  1897. void far *old_dta;
  1898. void far *new_dta;
  1899. int rc;
  1900.  
  1901.    new_dta = (void far *)dta;
  1902.    _asm {
  1903.  
  1904. ; save the old dta
  1905.         mov     ah, 0x2f                ; DOS get dta
  1906.         int     0x21                    ; DOS interrupt
  1907.         mov     WORD PTR old_dta, bx    ; save OFFSET of old DTA
  1908.         mov     ax, es
  1909.         mov     WORD PTR old_dta+2, ax  ; save SEGMENT of old DTA
  1910.  
  1911. ; set the new dta
  1912.         push    ds                      ; save ds
  1913.         mov     dx, WORD PTR new_dta    ; get OFFSET of new dta
  1914.         mov     ax, WORD PTR new_dta+2  ; get SEGMENT of new dta
  1915.         mov     ds, ax                  ; put it in ds
  1916.         mov     ah, 0x1a                ; DOS set dta
  1917.         int     0x21                    ; DOS interrupt
  1918.         pop     ds                      ; get back ds
  1919.  
  1920. ; find next matching file
  1921.         mov     ah, 0x4f                ; DOS find first file
  1922.         int     0x21                    ; DOS interrupt
  1923.  
  1924. ; save the return code
  1925.         jc      an_error                ; carry is set if an error occured
  1926.         xor     ax, ax                  ; zero out ax, return OK if no error
  1927. an_error:
  1928.         mov     WORD PTR rc, ax         ; save the return code
  1929.  
  1930. ; get back old dta
  1931.         push    ds                      ; save ds
  1932.         mov     dx, WORD PTR old_dta    ; get OFFSET of old dta
  1933.         mov     ax, WORD PTR old_dta+2  ; get SEGMENT of old dta
  1934.         mov     ds, ax                  ; put it in ds
  1935.         mov     ah, 0x1a                ; DOS set dta
  1936.         int     0x21                    ; DOS interrupt
  1937.         pop     ds                      ; get back ds
  1938.    }
  1939.    if (ceh.flag == ERROR)
  1940.       rc = ERROR;
  1941.    return( rc );
  1942. }
  1943.