home *** CD-ROM | disk | FTP | other *** search
/ Black Box 4 / BlackBox.cdr / editors / tde150.arj / TDEASM.C < prev    next >
C/C++ Source or Header  |  1992-04-01  |  66KB  |  1,594 lines

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