home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / INFO / EDITOR / TDE120.ZIP / TDEASM.C < prev    next >
Encoding:
C/C++ Source or Header  |  1991-10-05  |  40.9 KB  |  1,026 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.  I
  32.  * feel the need for speed.
  33.  *
  34.  * New editor name:  tde, the Thomson-Davis Editor.
  35.  * Author:           Frank Davis
  36.  * Date:             June 5, 1991
  37.  *
  38.  * This modification of Douglas Thomson's code is released into the
  39.  * public domain, Frank Davis.  You may distribute it freely.
  40.  */
  41.  
  42. #include "tdestr.h"
  43. #include "common.h"
  44. #include "tdefunc.h"
  45.  
  46. /*
  47.  * Name:    cpf - check_pointer_forward
  48.  * Purpose: To adjust a pointer if it is nearing end of a segment (within 16k)
  49.  * Date:    June 5, 1991
  50.  * Passed:  s:  string pointer
  51.  * Notes:   To avoid a bunch of code generated for pointer arithmetic when using
  52.  *          the huge memory model, this routine adjusts a pointer when it
  53.  *          approaches the end of a segment.
  54.  */
  55. text_ptr cpf( text_ptr s )
  56. {
  57.    _asm {
  58.         mov     ax, WORD PTR s  ; get offset of s
  59.         mov     dx, WORD PTR s+2        ; get segment of s
  60.         cmp     ax, 0xc000      ; are we within 16k of top of segment?
  61.         jb      get_out         ; no, get out
  62.         sub     ax, 0x8000      ; yes, subtract 32k from offset
  63.         add     dx, 0x0800      ; add 0x0800 paragraphs to segment == 32k
  64. get_out:
  65.    }
  66. }
  67.  
  68. /*
  69.  * Name:    cpb - check_pointer_backward
  70.  * Purpose: To adjust a pointer if it is nearing beginning of a segment (16k)
  71.  * Date:    June 5, 1991
  72.  * Passed:  s:  string pointer
  73.  * Notes:   To avoid a bunch of code generated for pointer arithmetic when using
  74.  *          the huge memory model, this routine adjusts a pointer when it
  75.  *          approaches the beginning of a segment.  Don't check NULL pointer.
  76.  */
  77. text_ptr cpb( text_ptr s )
  78. {
  79.    _asm {
  80.         mov     ax, WORD PTR s  ; get offset of s
  81.         mov     dx, WORD PTR s+2        ; get segment of s
  82.         cmp     ax, 0           ; is offset of s == NULL?
  83.         jne     not_null        ; no, check pointer
  84.         cmp     dx, 0           ; is segment of s == NULL?
  85.         je      get_out         ; yes, don't check NULL pointer
  86. not_null:
  87.         cmp     ax, 0x4000      ; are we within 16k of beginning of segment?
  88.         jae     get_out         ; no, get out
  89.         add     ax, 0x8000      ; yes, add 32k to offset
  90.         sub     dx, 0x0800      ; sub 0x0800 paragraphs from segment == 32k
  91. get_out:
  92.    }
  93. }
  94.  
  95. /*
  96.  * Name:    ptoul - pointer to unsigned long
  97.  * Purpose: convert a far pointer to unsigned long integer
  98.  * Date:    June 5, 1991
  99.  * Passed:  s:  string pointer
  100.  * Notes:   combine the offset and segment like so:
  101.  *                offset       0000
  102.  *                segment   + 0000
  103.  *                          =======
  104.  *                            00000
  105.  *          result is returned in dx:ax
  106.  */
  107. unsigned long ptoul( text_ptr s )
  108. {
  109.    _asm {
  110.         mov     ax, WORD PTR s          ; ax = offset of s
  111.         mov     dx, WORD PTR s+2        ; dx = segment of s
  112.         mov     bx, dx          ; put copy of segment in bx
  113.         mov     cl, 12          ; cl = decimal 12 - shift hi word 3 digits
  114.         shr     dx, cl          ; convert to 'real segment'
  115.         mov     cl, 4           ; cl = 4  - shift hi word 1 digit
  116.         shl     bx, cl          ; shift bx - line up on paragraph
  117.         add     ax, bx          ; add low part of segment to offset
  118.         adc     dx, 0           ; if carry, bump to next 'real' segment
  119.    }
  120. }
  121.  
  122.  
  123. /*
  124.  * Name:    nptop - normalize pointer to paragraph
  125.  * Purpose: make the offset of a pointer no larger than a paragraph (16 bytes)
  126.  * Date:    June 5, 1991
  127.  * Passed:  s:  string pointer
  128.  * Notes:   move all but the paragraph from the offset of the pointer to the
  129.  *          segment.   The offset will be no larger than 16 bytes. Why? because
  130.  *          we can add up to 0xFFF0 reliably to a pointer in small, compact or
  131.  *          large model and not worry about segment wrap.
  132.  *
  133.  *                offset       abcx
  134.  *                segment     0000
  135.  *                          =======
  136.  *                offset       000x
  137.  *                segment     0abc
  138.  *          result is returned in dx:ax
  139.  */
  140. text_ptr nptop( text_ptr s )
  141. {
  142.    _asm {
  143.         mov     ax, WORD PTR s          ; ax = offset of s
  144.         mov     dx, WORD PTR s+2        ; dx = segment of s
  145.         mov     bx, ax          ; put copy of offset in bx
  146.         mov     cl, 4           ; cl = 4  - shift lo word 1 digit
  147.         shr     bx, cl          ; shift bx - line up on paragraph
  148.         add     dx, bx          ; add hi part of offset to segment
  149.         and     ax, 0x000f      ; mask out three digits in offset
  150.    }
  151. }
  152.  
  153. /*
  154.  * Name:    addltop - add long to pointer
  155.  * Purpose: add long integer to a pointer
  156.  * Date:    June 5, 1991
  157.  * Passed:  l: long
  158.  *          p: text pointer
  159.  * Returns: pointer + long integer
  160.  * Notes:   A long integer takes two WORDs.  A far pointer takes two WORDs.
  161.  *          A long integer cannot be added directly to a far pointer.
  162.  *              This diagram may help explain better than I can write.
  163.  *
  164.  *          far pointer            0000   offset
  165.  *                                0xxx    segment
  166.  *                                  +
  167.  *          long integer       00000000             -throw away those three
  168.  *                                ======             digits on long integer,
  169.  *                                 0000   offset     they have no effect
  170.  *           new far pointer      0xxx    segment
  171.  *
  172.  *          msw = Most Significant WORD
  173.  *          lsw = Least Significant WORD
  174.  *
  175.  *          When working with the long integer, we don't need to worry about
  176.  *          the three x's on segment of the pointer.  Add or subtract the lsw
  177.  *          of the long integer to/from the offset.  If there is a carry,
  178.  *          it only affects the left most digit of the msw of the pointer.
  179.  */
  180. text_ptr addltop( long l, text_ptr p )
  181. {
  182.  
  183.    if (l >= 0) {
  184.       _asm {
  185.         mov     ax, WORD PTR p          ; ax = offset of p
  186.         mov     dx, WORD PTR p+2        ; dx = segment of p
  187.         mov     bx, WORD PTR l+2        ; msw of l in bx
  188.         add     ax, WORD PTR l          ; add lsw of p and lsw of l
  189.         adc     bx, 0                   ; if carry, pointer in another segment
  190.         mov     cl, 12                  ; cl = 12 - shift off 3 digits
  191.         shl     bx, cl                  ; only handle 1st part of msw of l
  192.         add     dx, bx                  ; add msw of p and msw of l
  193.       }
  194.    } else {
  195.       l = -l;      /* convert l to positive and subtract from pointer p */
  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.         mov     cl, 12                  ; cl = 12 - shift off 3 digits
  201.         sub     ax, WORD PTR l          ; subtract low part of pointer
  202.         adc     bx, 0                   ; if we borrowed then add it back to bx
  203.         shl     bx, cl                  ; only handle 1st digit of msw of l
  204.         sub     dx, bx                  ; subtract msw from segment of p
  205.       }
  206.    }
  207. }
  208.  
  209.  
  210. /*
  211.  * Name:    find_CONTROL_Z - assembler version, see commented C at end
  212.  * Purpose: To determine the length of a line up to ^Z
  213.  * Date:    June 5, 1991
  214.  * Passed:  s: the line to be measured
  215.  * Notes:   DOS carried over ^Z to mark the end of files from CP/M.  Since
  216.  *          it is the only character not allowed in regular text files.  ^Z
  217.  *          can be used, instead of '\0', to mark the end of strings.  All
  218.  *          ASCII characters, except ^Z, may be included in a text file.
  219.  *          However, none of the C string library functions should be used
  220.  *          when working with text.  The string library functions can be used
  221.  *          on responses solicited from the user.
  222.  * Returns: the length of the line
  223.  */
  224. int  find_CONTROL_Z( text_ptr s )
  225. {
  226.    s = cpf( s );
  227.    _asm {
  228.         mov     dx, ds          ; keep ds in dx, MUST save data segment
  229.         push    si              ; put copy of si on stack
  230.  
  231.         xor     cx, cx          ; cx = 0
  232.         mov     si, WORD PTR s  ; put offset of s in si
  233.         mov     ax, WORD PTR s+2        ; get segment of s
  234.         mov     ds, ax          ; else, segment in ds
  235.         cmp     si, 0           ; is offset of s == NULL?
  236.         jne     not_null        ; no, find length
  237.         cmp     ax, 0           ; is segment of s == NULL?
  238.         je      get_out         ; yes, line length = 0
  239. not_null:
  240.         mov     bl, CONTROL_Z   ; keep Control Z in bl - eos marker
  241.         mov     ax, si          ; pointer is ok, check for word align
  242.         shr     ax, 1           ; if [si] is odd, lsb is 1 - rotate to carry
  243.         jnc     top             ; see if string is WORD aligned
  244.         lodsb                   ; no, get a BYTE - now WORD aligned
  245.         cmp     al, bl          ; is ds:[si] == ^Z?
  246.         je      get_out         ; yes, have length, cx = 0
  247.         inc     cx              ; increment length variable
  248.         ALIGN   2
  249. top:
  250.         lodsw                   ; string is WORD aligned
  251.         cmp     al, bl          ; is lo BYTE == ^Z?
  252.         je      get_out         ; yes, we have length
  253.         inc     cx              ; no, increment counter
  254.         cmp     ah, bl          ; now test higher BYTE, is it ^Z?
  255.         je      get_out         ; yes, we have length
  256.         inc     cx              ; no, increment length
  257.         jmp     SHORT top       ; look at next two characters
  258. get_out:
  259.         mov     ax, cx          ; put length in ax - as defined by Microsoft
  260.         mov     ds, dx          ; get back data segment from dx
  261.         pop     si              ; get back si from stack
  262.    }
  263.  
  264. /*
  265. int len = 0;
  266.  
  267.    while (*s != ^Z) {
  268.       ++len;
  269.       ++s;
  270.    }
  271.    return len;
  272. */
  273. }
  274.  
  275.  
  276. /*
  277.  * Name:    linelen - assembler version, see commented C at end of routine
  278.  * Purpose: To determine the length of a line, up to either a \n or a
  279.  *           ^Z, whichever comes first.
  280.  * Date:    June 5, 1991
  281.  * Passed:  s: the line to be measured
  282.  * Notes:   Demonstrates 'lodsb' and 'lodsw'.  Memory operations are most
  283.  *           efficient when working with WORDs.  See if first BYTE in
  284.  *           string is WORD aligned.  If it is then work with WORDs else
  285.  *           get the first BYTE and rest of string will be WORD aligned.
  286.  *           The 'mov' instruction could have been used, but 'lobsb' and
  287.  *           'lodsw' automatically increment the memory pointer.
  288.  * Returns: the length of the line
  289.  */
  290. int  linelen( text_ptr s )
  291. {
  292.    s = cpf( s );
  293.    _asm {
  294.         mov     dx, ds          ; keep ds in dx, MUST save data segment
  295.         push    si              ; save si on stack
  296.  
  297.         xor     cx, cx          ; cx = 0
  298.         mov     si, WORD PTR s  ; put offset of s in si
  299.         mov     ax, WORD PTR s+2        ; get segment of s
  300.         mov     ds, ax          ; else, segment in ds
  301.         cmp     si, 0           ; is offset of s == NULL?
  302.         jne     not_null        ; no, find length
  303.         cmp     ax, 0           ; is segment of s == NULL?
  304.         je      get_out         ; yes, line length = 0
  305. not_null:
  306.         mov     bl, '\n'        ; keep new line character in bl
  307.         mov     bh, CONTROL_Z   ; keep Control Z in bh - DOS eof marker
  308.         mov     ax, si          ; pointer is ok, check for word align
  309.         shr     ax, 1           ; if [si] is odd, lsb is 1 - rotate to carry
  310.         jnc     top             ; see if string is WORD aligned
  311.         lodsb                   ; no, get a BYTE - now WORD aligned
  312.         cmp     al, bl          ; is BYTE == '\n'?
  313.         je      get_out         ; yes, have length, cx = 0
  314.         cmp     al, bh          ; is ds:[si] == ^Z?
  315.         je      get_out         ; yes, have length, cx = 0
  316.         inc     cx              ; increment length variable
  317.         ALIGN   2
  318. top:
  319.         lodsw                   ; string is WORD aligned
  320.         cmp     al, bl          ; test lower BYTE, is it '\n'
  321.         je      get_out         ; yes, we have length
  322.         cmp     al, bh          ; no, test for ^Z
  323.         je      get_out         ; yes, we have length
  324.         inc     cx              ; no, increment counter
  325.         cmp     ah, bl          ; now test higher BYTE, is it '\n'
  326.         je      get_out         ; yes, we have length
  327.         cmp     ah, bh          ; is it ^Z
  328.         je      get_out         ; yes, we have length
  329.         inc     cx              ; no, increment length
  330.         jmp     SHORT top       ; look at next two characters
  331.         ALIGN   2
  332. get_out:
  333.         mov     ax, cx          ; put length in ax - as defined by Microsoft
  334.         mov     ds, dx          ; get back data segment from dx
  335.         pop     si              ; get back si from stack
  336.    }
  337.  
  338. /*
  339. int len = 0;
  340.  
  341.    while (*s && *s != '\n') {
  342.       ++len;
  343.       ++s;
  344.    }
  345.    return len;
  346. */
  347. }
  348.  
  349.  
  350. /************* prelinelen is not used, but left in for reference **********/
  351. /*
  352.  * Name:    prelinelen
  353.  * Purpose: To determine the length of a line, from the current position
  354.  *           backwards to either a \n or a ^Z, whichever comes first.
  355.  * Date:    June 5, 1991
  356.  * Passed:  s: the line to be measured
  357.  * Returns: the length of the line up to the current position
  358.  * Notes:   It is assumed there will be a "terminating" ^Z before the
  359.  *           start of the first line.
  360.  */
  361. /*
  362. int prelinelen( text_ptr s )
  363. {
  364.    s = cpb( s );
  365.    _asm {
  366.         push    di              ; put copy of di on stack
  367.  
  368.         xor     ax, ax          ; ax = 0, keep string length in ax
  369.         mov     di, WORD PTR s  ; get offset of string
  370.         mov     dx, WORD PTR s+2        ; get segment of string
  371.         mov     es, dx          ; put segment in es
  372.         cmp     di, 0           ; is offset of string == NULL?
  373.         jne     not_null        ; no, do string stuff
  374.         cmp     dx, 0           ; is, segment of string == NULL?
  375.         je      get_out         ; yes, don't do NULL string
  376. not_null:
  377.         dec     di              ; look at previous character
  378. ALWORD: dec     di              ; get ready to check for WORD align
  379.         mov     bl, '\n'        ; keep '\n' in bl
  380.         mov     bh, CONTROL_Z   ; keep ^Z in bh
  381.         mov     dx, di          ; pointer is ok, check for WORD align
  382.         shr     dx, 1           ; if [di] is odd, lsb is 1 - rotate to carry
  383.         jnc     top             ; string is WORD aligned
  384.         inc     di              ; fix the second decrement - see ALWORD
  385.         mov     dl, BYTE PTR es:[di]    ; get a BYTE - put in DL
  386.         cmp     dl, bl          ; is it '\n'
  387.         je      get_out         ; yes, get out - count = 0
  388.         cmp     dl, bh          ; is it ^Z
  389.         je      get_out         ; yes, get out - count = 0
  390.         inc     ax              ; increment length counter
  391.         dec     di              ; pointer was BYTE aligned, dec pointer
  392.         dec     di              ; pointer is now WORD aligned
  393.         ALIGN   2
  394. top:
  395.         mov     dx, WORD PTR es:[di]    ; load WORD - hi BYTE is next
  396.         cmp     dh, bl          ; is hi BYTE (next char) '\n'?
  397.         je      get_out         ; yes, get out - count already in ax
  398.         cmp     dh, bh          ; is hi BYTE (next char) ^Z?
  399.         je      get_out         ; yes, get out - count already in ax
  400.         inc     ax              ; increment character counter
  401.         cmp     dl, bl          ; now check lo BYTE, is it '\n'?
  402.         je      get_out         ; yes, get out - count is in ax
  403.         cmp     dl, bh          ; is lo BYTE ^Z?
  404.         je      get_out         ; yes, get out - count is in ax
  405.         inc     ax              ; increment character counter
  406.         dec     di              ; decrement pointer
  407.         dec     di              ; align pointer on WORD
  408.         jmp     SHORT top       ; test next 2 characters
  409. get_out:
  410.         pop     di              ; get back di from stack
  411.    }
  412. int len = 0;
  413.  
  414.    while (*--s != CONTROL_Z && *s != '\n')
  415.       ++len;
  416.    return len;
  417. }
  418. */
  419. /************************** prelinelen is not used ************************/
  420.  
  421.  
  422. /*
  423.  * Name:    find_next
  424.  * Purpose: To find the first character in the next line after the starting
  425.  *           point.
  426.  * Date:    June 5, 1991
  427.  * Passed:  s: the starting point
  428.  * Returns: the first character in the next line
  429.  * Notes:   This function goes faster if machine works with WORDs.  See if
  430.  *           first BYTE in string is WORD aligned.  If it is not, get first
  431.  *           BYTE in string then the rest of string is WORD aligned.
  432.  *           Code added at end to adjust segment:offset if needed.
  433.  */
  434. text_ptr find_next( text_ptr s )
  435. {
  436.    _asm {
  437.         push    ds              ; save ds on stack
  438.         push    si              ; save si on stack
  439.  
  440.         mov     si, WORD PTR s          ; load offset of s
  441.         mov     ax, WORD PTR s+2        ; load segment of s
  442.         mov     ds, ax
  443.         cmp     si, 0           ; is offset of string == NULL?
  444.         jne     not_null        ; no, do string stuff
  445.         cmp     ax, 0           ; is segment of string == NULL?
  446.         je      return_null     ; yes, return NULL if string is NULL
  447. not_null:
  448.         mov     bl, '\n'        ; keep '\n' in bl
  449.         mov     bh, CONTROL_Z   ; keep ^Z in bh
  450.         mov     ax, si          ; move offset of si to ax
  451.         shr     ax, 1           ; shift right into carry flag
  452.         jnc     top             ; is string WORD aligned?
  453.         lodsb                   ; no, get a BYTE
  454.         cmp     al, bl          ; is it '\n'?
  455.         je      next_even       ; yes, si already incremented by lodsb
  456.         cmp     al, bh          ; is it ^Z?
  457.         je      return_null     ; yes, return NULL
  458.         ALIGN   2
  459. top:
  460.         lodsw                   ; string is WORD aligned, get two BYTEs
  461.         cmp     al, bl          ; is next BYTE == '\n'?
  462.         je      next_odd        ; yes, since si inc for WORD (lodsw) - dec di
  463.         cmp     al, bh          ; is next BYTE == ^Z?
  464.         je      return_null     ; yes, return NULL
  465.         cmp     ah, bl          ; is next BYTE in AH == '\n'?
  466.         je      next_even       ; yes, si is OK - return pointer to next BYTE
  467.         cmp     ah, bh          ; is next BYTE in AH == ^Z?
  468.         je      return_null     ; yes, return NULL
  469.         jmp     SHORT top       ; look at next WORD
  470.         ALIGN   2
  471. return_null:
  472.         xor     ax, ax          ; clear ax - offset = NULL
  473.         xor     dx, dx          ; clear dx - segment = NULL
  474.         jmp     SHORT get_out   ; return text_ptr in dx:ax - see Microsoft
  475.         ALIGN   2
  476. next_odd:
  477.         dec     si              ; 'lodsw' went one BYTE too far - so dec si
  478. next_even:
  479.         mov     ax, si          ; ds:si now points to next line, load ax
  480.         mov     dx, ds          ; load dx with segment of next BYTE
  481.         cmp     ax, 0xc000      ; are we within 16k of segment?
  482.         jb      get_out         ; no, get out
  483.         sub     ax, 0x8000      ; yes, subtract 32k from offset
  484.         add     dx, 0x0800      ; add 0x0800 paragraphs to segment
  485. get_out:
  486.         pop     si              ; get back si from stack
  487.         pop     ds              ; get back ds from stack
  488.    }
  489. /*
  490.    while (*s && *s != '\n' && *s != CONTROL_Z)
  491.       ++s;
  492.    if (*s)
  493.       return ++s;
  494.    else
  495.       return NULL;
  496. */
  497. }
  498.  
  499.  
  500. /*
  501.  * Name:    find_prev
  502.  * Purpose: To find the start of the line before the current line.
  503.  * Date:    June 5, 1991
  504.  * Passed:  current: the current line
  505.  * Returns: the start if the previous line
  506.  * Notes:   current must be at the start of the current line to begin with.
  507.  *          There must be a ^Z preceding the first line.
  508.  *          This function goes faster if machine works with WORDs.  See if
  509.  *           first BYTE in string is WORD aligned.  If it is not, get first
  510.  *           BYTE in string then the rest of string is WORD aligned.
  511.  *           The test for '\n' will pass a lot more than the test for
  512.  *           ^Z.  Set up the WORD align stuff first.
  513.  *           Since we are searching, by WORDs, backwards, the hi BYTE is the
  514.  *           prev BYTE and the al BYTE is two prev BYTEs (make sense?).
  515.  *           Code added at end to adjust segment:offset if needed.
  516.  */
  517. text_ptr find_prev( text_ptr current )
  518. {
  519.    _asm {
  520.         push    di              ; save di on stack
  521.  
  522.         mov     di, WORD PTR current    ; load offset of current
  523. DECR1:  dec     di                      ; decrement it
  524.         mov     ax, WORD PTR current+2  ; load segment of current
  525.         mov     es, ax
  526.         cmp     di, 0           ; is offset of string == NULL?
  527.         jne     not_null        ; no, do string stuff
  528.         cmp     ax, 0           ; is segment of string == NULL?
  529.         je      return_null     ; yes, return NULL if string NULL
  530. not_null:
  531.         mov     bl, '\n'        ; keep '\n' in bl
  532.         mov     bh, CONTROL_Z   ; keep ^Z in bh
  533.         mov     ax, di          ; put copy of offset in ax
  534.         shr     ax, 1           ; shift right thru carry flag
  535.         jnc     on_boundary     ; if no carry, string is WORD aligned
  536. ;
  537. ; if we were to dec the pointer twice, it would be WORD aligned with the
  538. ; '--current'  BYTE in the AH register.  if ^Z test fails, might as well
  539. ; test the BYTE in the AL register.
  540. ;
  541. DECR2:  dec     di              ; dec offset one more so it is WORD aligned
  542.         mov     ax, WORD PTR es:[di]    ; might as well load WORD
  543.         cmp     ah, bh          ; is prev BYTE ^Z?
  544.         je      return_null     ; yes, return NULL
  545. ;
  546. ; now we are in the for loop - see commented C code at bottom.
  547. ; 'on_boundary' is not part of the for loop so jump past it if needed.
  548. ;
  549.         cmp     al, bl          ; is prev BYTE '\n'?
  550.         je      inc_pointer     ; yes, increment the pointer and return
  551.         cmp     al, bh          ; is it ^Z?
  552.         je      inc_pointer     ; yes, increment the pointer and return
  553.         jmp     SHORT for_loop  ;no, pointer is now WORD aligned - do for loop
  554.         ALIGN   2
  555. ;
  556. ; the string ended on an odd boundary and the DECR1 has now aligned the
  557. ; string on a WORD.  if we load a WORD, the '--current' BYTE would be in the
  558. ; AL register.
  559. ;
  560. on_boundary:
  561.         mov     ax, WORD PTR es:[di]    ; load --current, aligned on WORD
  562.         cmp     al, bh          ; is --current ^Z?
  563.         je      return_null     ; yes, return NULL
  564. ;
  565. ; now we are in the for loop and string is guaranteed WORD aligned.
  566. ; IMPORTANT: there are 2 cases if the test for '\n' or ^Z pass.
  567. ;            1) AH passed, so di must be increment twice for '++current'
  568. ;            2) AL passed, inc di once for '++current'
  569. ;
  570.         ALIGN   2
  571. for_loop:
  572.         dec     di              ; decrement di twice so it will be
  573.         dec     di              ; WORD aligned
  574.         mov     ax, WORD PTR es:[di]    ; string is WORD aligned
  575.         cmp     ah, bl          ; is --current '\n'?
  576.         je      next_even       ; yes, increment di twice to return ++current
  577.         cmp     ah, bh          ; is --current ^Z?
  578.         je      next_even       ; yes, increment di twice to return ++current
  579.         cmp     al, bl          ; look at low part of WORD, is it '\n'?
  580.         je      inc_pointer     ; yes, increment di once to return ++current
  581.         cmp     al, bh          ; is low part of WORD ^Z?
  582.         je      inc_pointer     ; yes, increment di once to return ++current
  583.         jmp     SHORT for_loop  ; get next WORD
  584.         ALIGN   2
  585. return_null:
  586.         xor     ax, ax          ; clear ax - offset = NULL
  587.         xor     dx, dx          ; clear dx - segment = NULL
  588.         jmp     SHORT get_out   ; return text_ptr in dx:ax - see Microsoft
  589.         ALIGN   2
  590. next_even:
  591.         inc     di              ; di is a WORD too far - inc di
  592. inc_pointer:
  593.         inc     di              ; ++current
  594.         mov     ax, di          ; put offset in ax
  595.         mov     dx, es          ; put segment in dx, return dx:ax - Microsoft
  596.         cmp     ax, 0x4000      ; are we within 16k of segment?
  597.         jae     get_out         ; no, get out
  598.         add     ax, 0x8000      ; yes, add 32k to offset
  599.         sub     dx, 0x0800      ; sub 0x0800 paragraphs to segment
  600. get_out:
  601.         pop     di              ; get back di from stack
  602.    }
  603.  
  604. /*
  605.    if (*--current == ^Z)
  606.       return NULL;
  607.    for (;;) {
  608.       if (*--current == '\n' || *current == ^Z)
  609.          return ++current;
  610.    }
  611. */
  612. }
  613.  
  614. /*
  615.  * Name:    update_line
  616.  * Purpose: Display the current line in window
  617.  * Date:    June 5, 1991
  618.  * Passed:  window:   information allowing access to the current window
  619.  * Returns: none
  620.  * Notes:   Show string starting at column zero and if needed blank rest
  621.  *           of line.  Put max_col in cx and count down.  When we run into
  622.  *           '\n', cx contains number of columns to blank out.  Use the
  623.  *           fast 'rep stosw' to clear the end of line.
  624.  */
  625. void update_line( windows *window )
  626. {
  627. text_ptr text;      /* current character of orig begin considered */
  628. char far *screen_ptr;
  629. int off;
  630. int attr;
  631. int line;
  632. int col;
  633. int bc, ec;
  634. int normal, block;
  635. int max_col;
  636. int block_line;
  637. int len;
  638. int c;
  639. long rline;
  640. file_infos *file;
  641.  
  642.    file = window->file_info;
  643.    max_col = g_display.ncols;
  644.    line = window->cline;
  645.    normal = g_display.text_color;
  646.    block = g_display.block_color;
  647.          /* 160 = 80 chars + 80 attr  for each line */
  648.    screen_ptr = g_display.display_address;
  649.    off = line * 160;
  650.    text = cpf( window->cursor );
  651.    if (g_status.copied && ptoul( text ) == ptoul( g_status.buff_line ))
  652.       text = g_status.line_buff;
  653.    bc = window->bcol;
  654.    if (bc > 0) {
  655.       if ((col = linelen( text )) < bc)
  656.          bc = col;
  657.       text += bc;
  658.    }
  659.    rline = window->rline;
  660.    if (file->block_type && rline >= file->block_br && rline <= file->block_er)
  661.       block_line = TRUE;
  662.    else
  663.       block_line = FALSE;
  664.    if (block_line == TRUE && file->block_type == BOX) {
  665.       len = linelen( text );
  666.       bc = file->block_bc - window->bcol;
  667.       ec = file->block_ec - window->bcol;
  668.  
  669.       _asm {
  670.         push    ds                      ; MUST save ds - push it on stack
  671.         push    si                      ; save si on stack
  672.         push    di                      ; save di on stack
  673. ;
  674. ; set up local register variables
  675. ;
  676.         mov     ax, WORD PTR bc         ; get beginning column
  677.         mov     bl, al                  ; keep it in bl
  678.         mov     ax, WORD PTR ec         ; get ending column
  679.         mov     bh, al                  ; keep it in bh
  680.         mov     ax, WORD PTR normal     ; get normal attribute
  681.         mov     dl, al                  ; keep it in dl
  682.         mov     ax, WORD PTR block      ; get block attribute
  683.         mov     dh, al                  ; keep it in dh
  684.         mov     ax, WORD PTR max_col    ; get max number columns on screen
  685.         mov     ch, al                  ; keep it in ch
  686.         xor     cl, cl                  ; col = 0, keep col in cl
  687. ;
  688. ; load screen and text pointer
  689. ;
  690.         mov     di, WORD PTR screen_ptr         ; load offset of screen ptr
  691.         add     di, WORD PTR off                ; add offset of line
  692.         mov     ax, WORD PTR screen_ptr+2       ; load segment of screen ptr
  693.         mov     es, ax
  694.         mov     si, WORD PTR text       ; load offset of text ptr
  695.         mov     ax, WORD PTR text+2     ; load segment of text ptr
  696.         mov     ds, ax                  ; move segment of text in ds
  697.         cmp     si, 0                   ; is offset of text ptr == NULL?
  698.         jne     not_null                ; no, output string
  699.         cmp     ax, 0                   ; is segment of text ptr == NULL?
  700.         je      block_eol               ; yes, clear end of line
  701. not_null:
  702.         ALIGN   2
  703. top:
  704.         cmp     cl, ch          ; is col == max_col 0?
  705.         je      getout          ; yes, thru with line
  706.         lodsb                   ; get next char in string
  707.         cmp     al, CONTROL_Z   ; is it ^Z?
  708.         je      block_eol       ; yes, must check block past ^Z
  709.         cmp     al, '\n'        ; is it '\n'?
  710.         je      block_eol       ; yes, must check block past '\n'
  711.         mov     ah, dl          ; assume normal attribute
  712.         cmp     cl, bl          ; is col < bc? (less than beginning col)
  713.         jl      ch_out1         ; yes, show char and normal attribute
  714.         cmp     cl, bh          ; is col > ec? (greater than ending col)
  715.         jg      ch_out1         ; yes, show char and normal attribute
  716.         mov     ah, dh          ; must be in a block - show block attribute
  717. ch_out1:
  718.         stosw                   ; else show char on screen
  719.         inc     cl              ; ++col
  720.         jmp     SHORT top       ; get another character
  721.         ALIGN   2
  722. block_eol:
  723.         mov     al, ' '         ; clear rest of line w/ spaces
  724. b1:
  725.         mov     ah, dl          ; assume normal attribute
  726.         cmp     cl, bl          ; is col < bc? (less than beginning col)
  727.         jl      ch_out2         ; yes, show char and normal attribute
  728.         cmp     cl, bh          ; is col > ec? (greater than ending col)
  729.         jg      ch_out2         ; yes, show char and normal attribute
  730.         mov     ah, dh          ; must be in a block - show block attribute
  731. ch_out2:
  732.         stosw                   ; write blank and attribute to screen
  733.         inc     cl              ; ++col
  734.         cmp     cl, ch          ; is col == max_col?
  735.         jl      b1              ; while less output block
  736. getout:
  737.         pop     di
  738.         pop     si
  739.         pop     ds
  740.       }
  741. /*
  742.       for (col=0; col < max_col; col++) {
  743.          attr = normal;
  744.          if (col >= bc && col <= ec)
  745.             attr = block;
  746.          if (col < len)
  747.             c = text[col];
  748.          else
  749.             c = ' ';
  750.          update_char( c, col, line, attr );
  751.       }
  752. */
  753.    } else {
  754.       if (block_line)
  755.          attr = block;
  756.       else
  757.          attr = normal;
  758.       _asm {
  759.         mov     dx, ds          ; MUST save ds - keep it in dx
  760.         push    di              ; save di on stack
  761.         push    si              ; save si on stack
  762.  
  763.         mov     bx, WORD PTR attr               ; keep attribute in bl
  764.         mov     bh, '\n'                        ; keep '\n' in bh
  765.         mov     cx, WORD PTR max_col            ; keep max_col in cx
  766.         mov     di, WORD PTR screen_ptr         ; load offset of screen ptr
  767.         add     di, WORD PTR off                ; add offset of line
  768.         mov     ax, WORD PTR screen_ptr+2       ; load segment of screen ptr
  769.         mov     es, ax
  770.         mov     si, WORD PTR text       ; load offset of text ptr
  771.         mov     ax, WORD PTR text+2     ; load segment of text ptr
  772.         mov     ds, ax                  ; move segment of text in ds
  773.         cmp     si, 0                   ; is offset of pointer == NULL?
  774.         jne     nnot_null               ; no, output string
  775.         cmp     ax, 0                   ; is segment of pointer == NULL?
  776.         je      clreol                  ; yes, then clear rest of line
  777. nnot_null:
  778.         mov     ah, bl                  ; get attribute
  779.         ALIGN   2
  780. topp:
  781.         or      cx, cx          ; col == 0 ?
  782.         je      getoutt         ; yes, thru with line
  783.         lodsb                   ; get next char in string
  784.         cmp     al, CONTROL_Z   ; is it ^Z
  785.         je      clreol          ; yes, clear end of line
  786.         cmp     al, bh          ; is it '\n'
  787.         je      clreol          ; yes, clear end of line
  788.         stosw                   ; else show char on screen
  789.         dec     cx              ; --col, count down from max_column
  790.         jmp     SHORT topp      ; get another character
  791.         ALIGN   2
  792. clreol:
  793.         mov     ah, bl          ; get attribute
  794.         mov     al, ' '         ; clear eol with ' '
  795.         rep     stosw           ; count is in cx - set rest of line to ' '
  796. getoutt:
  797.         pop     si
  798.         pop     di
  799.         mov     ds, dx
  800.       }
  801.    }
  802. /*
  803.    if (orig != NULL) {
  804.       text = orig;
  805.       screen_ptr = g_display.display_address + line * 160 + col * 2;
  806.       for (; *text != '\n' && *text != ^Z && col < max_col; text++, col++) {
  807.          *screen_ptr++ = *text;
  808.          *screen_ptr++ = attr;
  809.       }
  810.    }
  811.    if (col < max_col)
  812.       eol_clear( col, line, attr );
  813. */
  814. }
  815.  
  816.  
  817. /*
  818.  * Name:    update_char
  819.  * Purpose: display one character in window
  820.  * Date:    June 5, 1991
  821.  * Passed:  window:  information allowing access to the current window
  822.  *          c:  character to output to screen
  823.  *          col:  col to display character
  824.  *          line:  line number to display character
  825.  * Returns: none
  826.  */
  827. void update_char( windows *window, int c, int col, int line )
  828. {
  829. char far *screen_ptr;
  830. int off;
  831. int attr;
  832. long rline;
  833. file_infos *file;
  834.  
  835.    file = window->file_info;
  836.    rline = window->rline;
  837.    attr = g_display.text_color;
  838.    if (file->block_type) {
  839.       if (rline >= file->block_br && rline <= file->block_er) {
  840.          if (file->block_type == LINE)
  841.             attr = g_display.block_color;
  842.          else if (window->rcol>=file->block_bc && window->rcol<=file->block_ec)
  843.             attr = g_display.block_color;
  844.       }
  845.    }
  846.    screen_ptr = g_display.display_address;
  847.    off = line * 160 + col * 2;
  848.  
  849.    _asm {
  850.         mov     dx, di                  ; save di in dx
  851.  
  852.         mov     di, WORD PTR screen_ptr         ; load offset of screen ptr
  853.         add     di, WORD PTR off                ; add offset of line:col
  854.         mov     ax, WORD PTR screen_ptr+2       ; load segment of screen ptr
  855.         mov     es, ax
  856.         mov     bx, WORD PTR attr       ; get attribute
  857.         mov     ah, bl                  ; put in ah
  858.         mov     bx, WORD PTR c          ; get character
  859.         mov     al, bl                  ; put in al
  860.         stosw                           ; show char on screen
  861.  
  862.         mov     di, dx                  ; get back di from dx
  863.    }
  864.  
  865. /*
  866.    screen_ptr = g_display.display_address + line * 160 + col * 2;
  867.    *screen_ptr++ = c;
  868.    *screen_ptr = attr;
  869. */
  870. }
  871.  
  872.  
  873. /*
  874.  * Name:    c_output
  875.  * Purpose: To make as few changes as possible to cause the current line
  876.  *           to be what it should be.
  877.  * Date:    June 5, 1991
  878.  * Passed:  c:     character to output to screen
  879.  *          col:   col to display character
  880.  *          line:  line number to display character
  881.  *          attr:  attribute of character
  882.  * Returns: none
  883.  */
  884. void c_output( int c, int col, int line, int attr )
  885. {
  886. char far *screen_ptr;
  887. int off;
  888.  
  889.    screen_ptr = g_display.display_address;
  890.    off = line * 160 + col * 2;
  891.  
  892.    _asm {
  893.         mov     dx, di                  ; save di in dx
  894.  
  895.         mov     di, WORD PTR screen_ptr         ; load offset of screen ptr
  896.         add     di, WORD PTR off                ; add offset of line:col
  897.         mov     ax, WORD PTR screen_ptr+2       ; load segment of screen ptr
  898.         mov     es, ax
  899.         mov     bx, WORD PTR attr       ; get attribute
  900.         mov     ah, bl                  ; put in ah
  901.         mov     bx, WORD PTR c          ; get character
  902.         mov     al, bl                  ; put in al
  903.         stosw                           ; show char on screen
  904.  
  905.         mov     di, dx                  ; get back di from dx
  906.    }
  907.  
  908. /*
  909.    screen_ptr = g_display.display_address + line * 160 + col * 2;
  910.    *screen_ptr++ = c;
  911.    *screen_ptr = attr;
  912. */
  913. }
  914.  
  915.  
  916. /*
  917.  * Name:    s_output
  918.  * Purpose: To output character string at the cursor position, advancing
  919.  *           the cursor by the length of the string.
  920.  * Date:    June 5, 1991
  921.  * Passed:  s: string to output
  922.  * Notes:   This function is used to output most strings not part of file text.
  923.  */
  924. void s_output( char far *s, int line, int col, int attr )
  925. {
  926. char far *screen_ptr;
  927. int off;
  928. int max_col;
  929.  
  930.    max_col = g_display.ncols;
  931.    screen_ptr = g_display.display_address;
  932.    off = line * 160 + col * 2;
  933.  
  934.    _asm {
  935.         push    ds              ; save ds on stack
  936.         push    di              ; save di on stack
  937.         push    si              ; save si on stack
  938.  
  939.         mov     bx, WORD PTR attr               ; keep attribute in bx
  940.         mov     cx, WORD PTR col                ; put cols in cx
  941.         mov     dx, WORD PTR max_col            ; keep max_col in dx
  942.         mov     di, WORD PTR screen_ptr         ; load offset of screen ptr
  943.         add     di, WORD PTR off                ; add offset of line:col
  944.         mov     ax, WORD PTR screen_ptr+2       ; load segment of screen ptr
  945.         mov     es, ax
  946.         mov     si, WORD PTR s  ; load offset of string ptr
  947.         or      si, si          ; is it == NULL?
  948.         je      getout          ; yes, no output needed
  949.         mov     ax, WORD PTR s+2        ; load segment of string ptr
  950.         or      ax, ax          ; is pointer == NULL?
  951.         je      getout          ; yes, no output needed
  952.         mov     ds, ax          ; load segment of text in ds
  953.         mov     ah, bl          ; put attribute in AH
  954. top:
  955.         cmp     cx, dx          ; col < max_cols?
  956.         jge     getout          ; no, thru with line
  957.         lodsb                   ; get next char in string - put in al
  958.         or      al, al          ; is it '\0'
  959.         je      getout          ; yes, end of string
  960.         cmp     al, '\n'        ; is it '\n'?
  961.         je      getout          ; yes, end of string
  962.         stosw                   ; else show attr + char on screen (ah + al)
  963.         inc     cx              ; col++
  964.         jmp     SHORT top       ; get another character
  965. getout:
  966.         pop     si              ; get back si
  967.         pop     di              ; get back di
  968.         pop     ds              ; get back ds
  969.    }
  970.  
  971. /*
  972.    screen_ptr = g_display.display_address + line * 160 + col * 2;
  973.    max_col = g_display.ncols;
  974.    while (*s && col < max) {
  975.       *screen_ptr++ = *s++;
  976.       *screen_ptr++ = attr;
  977.    }
  978. */
  979. }
  980.  
  981.  
  982. /*
  983.  * Name:    eol_clear
  984.  * Purpose: To clear the current line from the cursor to the end of the
  985.  *           line to normal spaces.
  986.  * Date:    June 5, 1991
  987.  * Notes:   Basic assembly - comments should be enough explanation.
  988.  */
  989. void eol_clear( int col, int line, int attr )
  990. {
  991. int max_col;
  992. char far *screen_ptr;
  993. int off;
  994.  
  995.    max_col = g_display.ncols;
  996.    screen_ptr = g_display.display_address;
  997.    off = line * 160 + col * 2;
  998.  
  999.    _asm {
  1000.         push    di                              ; save di on stack
  1001.  
  1002.         mov     bx, WORD PTR attr               ; keep attribute in bx
  1003.         mov     dx, WORD PTR col                ; put cols in dx
  1004.         mov     cx, WORD PTR max_col            ; put max_col in cx
  1005.         cmp     dx, cx                          ; max_cols < cols?
  1006.         jge     getout                          ; no, thru with line
  1007.         sub     cx, dx                          ; number of column to clear
  1008.         mov     di, WORD PTR screen_ptr         ; load offset of screen ptr
  1009.         add     di, WORD PTR off                ; add offset of line:col
  1010.         mov     ax, WORD PTR screen_ptr+2       ; load segment of screen ptr
  1011.         mov     es, ax
  1012.         mov     ah, bl                          ; get attribute in ah
  1013.         mov     al, ' '                         ; store ' ' in al
  1014.         rep     stosw                           ; clear to end of line
  1015. getout:
  1016.         pop     di                              ; get back di from stack
  1017.    }
  1018.  
  1019. /*
  1020.    for (; col < g_display.ncols; col++) {
  1021.       *p++ = ' ';
  1022.       *p++ = attr;
  1023.    }
  1024. */
  1025. }
  1026.