home *** CD-ROM | disk | FTP | other *** search
/ The Unsorted BBS Collection / thegreatunsorted.tar / thegreatunsorted / programming / asm_programming / CHAP19-2.DOC < prev    next >
Text File  |  1990-08-02  |  34KB  |  833 lines

  1.  
  2.  
  3.  
  4.              Chapter 19 - Strings                                          199
  5.              ____________________
  6.  
  7.              By this time you may have become annoyed by the fact that there
  8.              is no instruction for moving data from one place in memory to
  9.              another. That is, you can't have:
  10.  
  11.                  mov  variable2, variable1
  12.  
  13.              Instead you have to have:
  14.  
  15.                  mov  ax, variable1
  16.                  mov  variable2, ax
  17.  
  18.              There is one instruction, however, where you can move a BLOCK of
  19.              data from one place in memory to another. It is called MOVS. As
  20.              usual with these instructions, there are two forms.
  21.  
  22.                  movsb
  23.  
  24.              moves a byte from DS:SI to ES:DI and increments or decrements SI
  25.              and DI by one, depending on the setting of DF, the direction
  26.              flag. Notice that either both are incremented or both are
  27.              decremented. You can't have one pointer incrementing while the
  28.              other one is decrementing.
  29.  
  30.                  movsw
  31.  
  32.              moves a word from DS:SI to ES:DI and increments or decrements SI
  33.              and DI by two, depending on the setting of DF, the direction
  34.              flag. This requires the same amount of setup as all the other
  35.              routines we have looked at so far, so it is not efficient to use
  36.              it for just a few bytes. For 30 or so, it is very efficient. This
  37.              has the equivalent effect (except for changing DI and SI) as:
  38.  
  39.                  mov  WORD PTR es:[di], ds:[si]   ; or BYTE PTR for bytes
  40.  
  41.              We are going to write some subroutines which copy strings from
  42.              one place in memory to another.{1} But first we need to review
  43.              text strings. 
  44.  
  45.              The text string world is divided into Pascal and C. A Pascal
  46.              string has its length in the first byte and the first character
  47.              in the second byte. Since the length is in one byte, the string
  48.              length may only be 0 to 255. You read the first byte of the
  49.              string to get the length. 
  50.  
  51.              A C string can have any length. The end of a C string is marked
  52.              by a byte with the value 0d. This is not the character '0', it is
  53.              the number 0 (0hex). In order to find the end of a C string, you
  54.              need to check each character to see if it is 0h. 
  55.  
  56.              We'll do the Pascal string first. We are going to pass the
  57.              ____________________
  58.  
  59.                 1. For the technically minded, these routines will be only
  60.              half of a real life subroutine, since they assume that the two
  61.              strings do not overlap. In robust subroutines, these routines
  62.              would be the section for when the destination address is lower
  63.              than the source address.
  64.  
  65.  
  66.  
  67.  
  68.              The PC Assembler Tutor                                        200
  69.              ______________________
  70.  
  71.              addresses of the strings. If you have the Pascal call:
  72.  
  73.                  move_pascal_string (from_string, to_string) ;
  74.  
  75.              The first thing you need to know is that Pascal pushes things on
  76.              the stack from left to right. In other words, Pascal will
  77.              generate the following code:
  78.  
  79.                  lea  ax, from_string
  80.                  push ax
  81.                  lea  ax, to_string
  82.                  push ax
  83.                  call move_pascal_string
  84.  
  85.              We will start by assuming both near data (all data is in DS), and
  86.              near subroutines. After setting up BP, the stack will look like
  87.              this:{2}
  88.  
  89.                       from_string address           bp + 6
  90.                       to_string address             bp + 4
  91.                       old IP                        bp + 2
  92.                bp ->  old BP                        bp + 0
  93.  
  94.              Here's the subroutine. Remember, PUSHREGS and POPREGS are macros:
  95.  
  96.              ; ----------
  97.              move_pascal_string  proc  near
  98.  
  99.                  FROM_PTR  EQU  [bp+6]
  100.                  TO_PTR    EQU  [bp+4]
  101.  
  102.                  push bp                       ; set up bp
  103.                  mov  bp, sp
  104.                  pushf                         ; push the flags
  105.                  PUSHREGS  cx, si, di, es      ; push the registers
  106.                  push ds                       ; move ds to es
  107.                  pop  es
  108.  
  109.                  mov  si, FROM_PTR             ; load pointers
  110.                  mov  di, TO_PTR
  111.                  cld                           ; clear DF (increment)
  112.  
  113.                  sub  cx, cx                   ; zero cx
  114.                  mov  cl, [si]                 ; length to cl
  115.                  inc  cx                       ; increment count by one
  116.  
  117.                  rep  movsb                    ; the actual move
  118.  
  119.                  POPREGS cx, si, di, es
  120.                  popf                          ; pop the flags
  121.                  pop  bp
  122.                  ret (4)                       ; pop pointers and return
  123.  
  124.              ____________________
  125.  
  126.                 2. If you forgot about BP, go back to the chapter on
  127.              subroutines.
  128.  
  129.  
  130.  
  131.  
  132.              Chapter 19 - Strings                                          201
  133.              ____________________
  134.  
  135.              move_pascal_string endp
  136.              ; ----------
  137.  
  138.              The count is increased by one since we need to move not only the
  139.              text, but the count itself. If the length is 0, we still need to
  140.              move one byte - the count byte. The value in DS is moved to ES
  141.              with a PUSH and a POP. You cannot move directly from one segment
  142.              register to another. Also, at the return we POP 4 bytes (2 words)
  143.              to get the pointers off the stack. Remember, in Pascal, it is the
  144.              subroutine's responsibility to get rid of the arguments from a
  145.              subroutine call. 
  146.  
  147.              You will notice that this time we saved the flags register. Why?
  148.              Because we are clearing DF. When we return from the subroutine,
  149.              we want DF to be exactly the same as it was on entry to the
  150.              subroutine. The calling program may have DF set in some special
  151.              way and we don't want to interfere with that. 
  152.  
  153.              There are three flags which I will call 'hard' flags. Once they
  154.              are set they do not change. These are (1) TF, the trap flag, (2)
  155.              IEF, the interrupt enable flag, and (3) DF, the direction flag.
  156.              The 'soft' flags are CF, OF, ZF, etc. If you call a subroutine
  157.              you expect CF, OF, ZF etc. to be unreliable, but you expect these
  158.              three 'hard' flags to remain the same. TF is the domain of a
  159.              debugger, so it is none of your business. IEF is only of interest
  160.              to you if you are writing an interrupt procedure.{3} The third
  161.              one, DF, is your concern. If you use DF in a subroutine, you MUST
  162.              save the flags to ensure that the DF flag has the same value at
  163.              the return that it had on entry.
  164.  
  165.  
  166.              Now for the C subroutine. If we have a C subroutine call:
  167.  
  168.                  move_c_string ( from_string, to_string ) ;
  169.  
  170.              C pushes things on the stack from right to left (the exact
  171.              opposite of Pascal). The C complier will generate the following
  172.              code.
  173.  
  174.                  lea  ax, to_string
  175.                  push ax
  176.                  lea  ax, from_string
  177.                  push ax
  178.                  call move_pascal_string
  179.                  add  sp, 4
  180.  
  181.              After setting up BP, the stack will look like this:
  182.  
  183.                       to_string address             bp + 6
  184.                       from_string address           bp + 4
  185.                       old IP                        bp + 2
  186.                bp ->  old BP                        bp + 0
  187.              ____________________
  188.  
  189.                 3. If you do an interrupt procedure you don't have to worry
  190.              because INT automatically saves the flags while clearing IEF, and
  191.              IRET restores the flags on exiting.
  192.  
  193.  
  194.  
  195.  
  196.              The PC Assembler Tutor                                        202
  197.              ______________________
  198.  
  199.  
  200.              Here's the C subroutine:
  201.  
  202.              ; ----------
  203.              move_c_string  proc  near
  204.  
  205.                  FROM_PTR  EQU  [bp+4]
  206.                  TO_PTR    EQU  [bp+6]
  207.  
  208.                  push bp                       ; set up bp
  209.                  mov  bp, sp
  210.                  pushf                         ; push the flags
  211.                  PUSHREGS  ax, si, di, es      ; push the registers
  212.                  push ds                       ; move ds to es
  213.                  pop  es
  214.  
  215.                  mov  si, FROM_PTR             ; load pointers
  216.                  mov  di, TO_PTR
  217.                  cld                           ; clear DF (increment)
  218.  
  219.              move_loop:
  220.                  lodsb                         ; source to al
  221.                  stosb                         ; al to destination
  222.                  and  al, al                   ; check for 0
  223.                  jnz  move_loop
  224.  
  225.  
  226.                  POPREGS ax, si, di, es
  227.                  popf                          ; pop the flags
  228.                  pop  bp
  229.                  ret
  230.  
  231.              move_c_string endp
  232.              ; ----------
  233.  
  234.  
  235.              We set up the routine the same way, but we cannot use MOVSB. We
  236.              need to check each individual byte to see if it is 0 hex, so we
  237.              move it to AL, move it from AL to the destination, and then check
  238.              AL for 0. Also note that we did not pop the addresses off the
  239.              stack with the return statement, since in C it is the calling
  240.              program's responsibility to do that. If you look at the calling
  241.              code above, you will see:
  242.  
  243.                  add  sp, 4
  244.  
  245.              which gets rid of the two pointers from the stack. Remember, the
  246.              stack grows downward, so you ADD to decrease the size of the
  247.              stack.
  248.  
  249.              Let's do the same Pascal program again, but this time use long
  250.              pointers, that is, give both the segment and offset of the
  251.              string. This means that we will be able to move from any place in
  252.              memory to any place in memory. Here is the calling code.
  253.  
  254.  
  255.                  mov  ax, segment from_string
  256.  
  257.  
  258.  
  259.  
  260.              Chapter 19 - Strings                                          203
  261.              ____________________
  262.  
  263.                  push ax
  264.                  mov  ax, offset from_string
  265.                  push ax
  266.                  mov  ax, segment to_string
  267.                  push ax
  268.                  mov  ax, offest to_string
  269.                  push ax
  270.                  call move_pascal_string
  271.  
  272.              We will still keep it a near subroutine. After setting up BP, the
  273.              stack will look like this:
  274.  
  275.                       from_string segment           bp + 10
  276.                       from_string offset            bp + 8
  277.                       to_string segment             bp + 6
  278.                       to_string offset              bp + 4
  279.                       old IP                        bp + 2
  280.                bp ->  old BP                        bp + 0
  281.  
  282.              Here's the subroutine:
  283.  
  284.              ; ----------
  285.              move_pascal_string  proc  near
  286.  
  287.                  FROM_PTR  EQU  [bp+8]
  288.                  TO_PTR    EQU  [bp+4]
  289.  
  290.                  push bp                       ; set up bp
  291.                  mov  bp, sp
  292.                  pushf                         ; push the flags
  293.                  PUSHREGS  cx, si, di, ds, es  ; push the registers
  294.  
  295.                  lds  si, FROM_PTR             ; load pointers
  296.                  les  di, TO_PTR
  297.                  cld                           ; clear DF (increment)
  298.  
  299.                  sub  cx, cx                   ; zero cx
  300.                  mov  cl, [si]                 ; length to cl
  301.                  inc  cx                       ; increment count by one
  302.  
  303.                  rep  movsb                    ; the actual move
  304.  
  305.                  POPREGS cx, si, di, ds, es
  306.                  popf                          ; pop the flags
  307.                  pop  bp
  308.                  ret (8)                       ; pop pointers and return
  309.  
  310.              move_pascal_string endp
  311.              ; ----------
  312.  
  313.              This takes slightly less code since we load SI and DS at the same
  314.              time (with LDS -load DS) and we load DI and ES at the same time
  315.              (with LES - load ES). Remember, 8086 instructions which move an
  316.              offset:segment pair always have the offset in low memory and the
  317.              segment in high memory; the offset is the first two bytes and the
  318.              segment is the next two bytes. 
  319.  
  320.  
  321.  
  322.  
  323.  
  324.              The PC Assembler Tutor                                        204
  325.              ______________________
  326.  
  327.              We changed the EQU statements, and the return statement is now:
  328.  
  329.                  ret (8)
  330.  
  331.              so we take 8 bytes (4 words) off the stack, but the rest is the
  332.              same. 
  333.  
  334.  
  335.              CMPS
  336.  
  337.              The final instruction in this group is CMPS, and as usual, it
  338.              comes in two varieties.
  339.  
  340.                  cmpsb
  341.  
  342.              compares the byte addressed by DS:SI to the byte addressed by
  343.              ES:DI. It is the same as the CMP instruction. It moves both bytes
  344.              into the 8086, subtracts the DI byte from the SI byte and sets
  345.              the flags. The two bytes in memory remain unchanged. You can look
  346.              at the flags to see which byte is larger, or if they are equal.
  347.              As usual, both SI and DI are incremented or decremented by one,
  348.              depending on the setting of DF, the direction flag.
  349.  
  350.                  cmpsw
  351.  
  352.              compares the word addressed by DS:SI to the word addressed by
  353.              ES:DI. It is the same as the CMP instruction. It moves both words
  354.              into the 8086, subtracts the DI word from the SI word and sets
  355.              the flags. The two words in memory remain unchanged. You can then
  356.              look at the flags to see which word is larger, or if they are
  357.              equal. Both SI and DI are incremented or decremented by two,
  358.              depending on the setting of DF, the direction flag.  This
  359.              instruction has the same effect on the flags as:
  360.  
  361.                  push ax
  362.                  mov  ax, ds:[si]    ; or AL for bytes
  363.                  cmp  ax, es:[di]    ; performs ( DS:[si] - ES:[DI] )
  364.                  pop  ax
  365.  
  366.  
  367.              What use is this instruction? It is possible to use this for word
  368.              find, and we will do that later, but it is a little
  369.              unsophisticated for that. It is great for data verification,
  370.              however.
  371.  
  372.              When you use the DISKCOMP utility in DOS which compares two
  373.              floppy disks, it reads each of the disks sector by sector, and
  374.              then compares them. A sector is 512 bytes. The code for this
  375.              utility looks like this:
  376.  
  377.  
  378.              ; - - - - - DATA - - - - -
  379.              error_message db   "Sectors are not the same", 0
  380.              disk1_buffer  db   512 dup (?)
  381.              disk2_buffer  db   512 dup (?)
  382.  
  383.              ; - - - - - CODE - - - - -
  384.  
  385.  
  386.  
  387.  
  388.              Chapter 19 - Strings                                          205
  389.              ____________________
  390.  
  391.  
  392.              get_next_sector:
  393.  
  394.              ; the code for reading one sector from each disk goes here.
  395.              ; then we have the code to compare the two sets of data.
  396.  
  397.                  mov  si, offset disk1_buffer
  398.                  mov  di, offset disk2_buffer
  399.  
  400.                  mov  cx, 256                  ; 512 / 2 = 256
  401.                  repe cmpsw
  402.                  je   get_next_sector
  403.  
  404.                  lea  ax, error_message   ; we had an unequal comparison
  405.                  call print_string
  406.                  jmp  get_next_sector
  407.  
  408.              ; - - - - - - - - - - 
  409.  
  410.  
  411.              We do a word compare since it takes only half as many steps. If
  412.              there is an unequal comparison at any time, the REPE instruction
  413.              will terminate the loop. We can test for this inequality with JE
  414.              or JNE. In this example we assume that DS and ES have the same
  415.              segment address. 
  416.  
  417.              Any time you need to verify data, this is the instruction to use.
  418.  
  419.              We are going to build a word search program. It is not very
  420.              valuable since 'a' will not match 'A', but it is a good exercise
  421.              to look at CMPS. We will use ch1str.obj, the file we used at the
  422.              beginning of the chapter, as the text file and you can try to
  423.              find individual words in the file. Remember, the file is
  424.              continuous characters (no spaces), and all characters are small.
  425.              If you didn't save the file length, you will have to run that
  426.              program again to find the length of the file. 
  427.  
  428.              Here's the word_search program:
  429.  
  430.              ; + + + + + + + + + + + + + + + START DATA BELOW THIS LINE 
  431.              EXTRN  ch1str:BYTE 
  432.              entry_banner     db  13,10, "Enter a word for a word search", 0 
  433.              no_match_banner  db  "There was no match", 0 
  434.              input_buffer     db   80 dup (?)  
  435.              letter_count     dw   ? 
  436.              ; + + + + + + + + + + + + + + + END DATA ABOVE THIS LINE 
  437.  
  438.              ; + + + + + + + + + + + + + + + START CODE BELOW THIS LINE 
  439.                     mov   ax, seg ch1str      ; load es register 
  440.                     mov   es, ax 
  441.                     cld                       ; clear DF (increment) 
  442.               
  443.              big_loop: 
  444.                     ; get a word for the word search 
  445.                     mov   ax, offset entry_banner 
  446.                     call  print_string 
  447.                     mov   ax, offset input_buffer 
  448.  
  449.  
  450.  
  451.  
  452.              The PC Assembler Tutor                                        206
  453.              ______________________
  454.  
  455.                     call  get_string 
  456.               
  457.                     ; find the end of string 
  458.                     mov   al, 0               ; compare with 0 
  459.                     mov   bx, offset input_buffer 
  460.                     mov   cx, 0               ; letter count 
  461.              letter_count_loop: 
  462.                     cmp   al, [bx]            ; compare to 0 
  463.                     je    end_of_count_loop 
  464.                     inc   cx                  ; increment count 
  465.                     inc   bx                  ; increment pointer 
  466.                     jmp   letter_count_loop 
  467.              end_of_count_loop: 
  468.                     jcxz  big_loop      ; if cx = 0, string is empty so redo
  469.                     mov   letter_count, cx   ; store our count
  470.               
  471.                     ; look for word match 
  472.                     mov   di, offset ch1str 
  473.                     mov   cx, $$$$            ; $$$$ = length of ch1str 
  474.                     sub   cx, letter_count    ; calculate last possible match
  475.               
  476.              word_search_loop: 
  477.                     push  di                  ; start of search 
  478.                     push  cx                  ; count for ch1str
  479.                     mov   si, offset input_buffer 
  480.                     mov   cx, letter_count 
  481.                     repe  cmpsb               ; the actual comparison 
  482.                     je    found_it           ; if equal, we have a match
  483.               
  484.                     ; no match. are we finished? 
  485.                     pop   cx 
  486.                     pop   di 
  487.                     inc   di               ; move to next starting address
  488.                     loop  word_search_loop 
  489.               
  490.                     ; we fell through. finished, but no match 
  491.                     mov   ax, offset no_match_banner 
  492.                     call  print_string 
  493.                     jmp   big_loop 
  494.  
  495.              found_it:
  496.                     pop   cx                  ; clear cx off the stack
  497.                     pop   di                  ; start of the match
  498.                     mov   si, offset input_buffer
  499.                     mov   cx, 25              ; move 25 characters to buffer
  500.              transfer_loop:
  501.                     mov   al, es:[di]
  502.                     mov   [si], al
  503.                     inc   si
  504.                     inc   di
  505.                     loop  transfer_loop
  506.                     mov   BYTE PTR [si], 0    ; end of a C string
  507.  
  508.                     mov   ax, offset input_buffer
  509.                     call  print_string
  510.                     jmp   big_loop
  511.              ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE
  512.  
  513.  
  514.  
  515.  
  516.              Chapter 19 - Strings                                          207
  517.              ____________________
  518.  
  519.               
  520.  
  521.              The code is so long that the whole assembler file has been put on
  522.              disk so you don't have to do all the typing. The pathname is
  523.              \XTRAFILE\COMPARE.ASM. All you need to do is enter the length of
  524.              ch1str in the MOV instruction where the dollar signs are:
  525.  
  526.                     mov   cx, $$$$           ; $$$$ = length of ch1str
  527.               
  528.              Link with 'link  compare+ch1str+\asmhelp'.  You enter a text
  529.              string and the program looks for an exact match in ch1str. Here
  530.              is how the program is structured.
  531.  
  532.              First, the program prompts you to enter a string. The program
  533.              then counts the number of bytes in the string. It must have a
  534.              non-zero length or the program will prompt you again for a
  535.              string. The program then starts at the beginning of the text. It
  536.              saves a copy of the pointer to the start of the comparison so if
  537.              we fail we can start over again at the next character. The actual
  538.              comparison is:
  539.  
  540.                  repe cmpsb
  541.  
  542.              If that makes it through all the letters in the search string,
  543.              REPE will quit because CX = 0, not because we have an unequal
  544.              character. If the comparison failed we pop DI (the text pointer)
  545.              and start at the next character. 
  546.  
  547.              If there is a match, we move 25 characters (starting with the
  548.              matching characters) from the text to the buffer. It is necessary
  549.              to move these because when you call print_string, the string must
  550.              be in the DATASTUFF segment, and ch1str isn't. We haven't used
  551.              MOVSB here because ES and DS are in the wrong place. For 25
  552.              characters there is only a marginal advantage to setting up for
  553.              MOVS. Finally, the 25 characters are printed. If there is no
  554.              match, a message to that effect is printed. 
  555.  
  556.              The text in ch1str is the first draft of chapter 1, but just for
  557.              interest, I have hidden eight C keywords and eight of your
  558.              favorite Middle English words in the text.{4}  See if you can
  559.              find them.
  560.  
  561.  
  562.              SEGMENT OVERRIDES
  563.  
  564.              Here are the string instructions and the override rules for each
  565.              one.
  566.  
  567.                  LODS moves a byte or word from DS:[si] to AL or AX. You may
  568.                  use CS:[si], SS:[si] or ES:[si].
  569.  
  570.                  STOS moves a byte (or a word) from AL (or AX) to ES:[di]. NO
  571.              ____________________
  572.  
  573.                 4. Two hints. You might find four of these Middle English
  574.              words in the name of a boutique. The other four of the Middle
  575.              English words are some of your favorite monosyllabic words.
  576.  
  577.  
  578.  
  579.  
  580.              The PC Assembler Tutor                                        208
  581.              ______________________
  582.  
  583.                  OVERRIDES ARE ALLOWED.
  584.  
  585.                  SCAS compares AL (or AX) to the byte (or word) pointed to by
  586.                  ES:[di]. NO OVERRIDES ARE ALLOWED.
  587.  
  588.                  MOVS moves a byte (or a word) from DS:[si] to ES:[di].  You
  589.                  may use CS:[si], SS:[si] or ES:[si], but you MAY NOT
  590.                  OVERRIDE ES:[di].
  591.  
  592.                  CMPS compares the byte (or a word) from DS:[si] to ES:[di]. 
  593.                  You may use CS:[si], SS:[si] or ES:[si], but you MAY NOT
  594.                  OVERRIDE ES:[di].
  595.  
  596.              Looking at the whole group, you may override DS:[si], but you may
  597.              not override ES:[di]. The form of the override is strict. We will
  598.              take MOVS as an example. Till now, the instructions were written:
  599.  
  600.                  movsb     ; byte move
  601.                  movsw     ; word move
  602.  
  603.              If you want to do an override, the syntax is:
  604.  
  605.                  movs BYTE PTR ES:[di], SS:[si]
  606.                  movs WORD PTR ES:[di], SS:[si]
  607.  
  608.              If you write:
  609.  
  610.                  movsb     ES:[di], SS:[di]
  611.  
  612.              you will get an assembler error. Here are all the legal forms:
  613.  
  614.              LODS
  615.                  lodsb
  616.                  lodsw
  617.                  lods BYTE PTR SS:[si]         ; or CS:[si], DS:[si], ES:[si]
  618.                  lods WORD PTR SS:[si]         ; or CS:[si], DS:[si], ES:[si]
  619.  
  620.              STOS
  621.                  stosb
  622.                  stosw
  623.                  stos BYTE PTR ES:[di]         ; no override allowed
  624.                  stos WORD PTR ES:[di]         ; no override allowed
  625.  
  626.              SCAS
  627.                  scasb
  628.                  scasw
  629.                  scas BYTE PTR ES:[di]         ; no override allowed
  630.                  scas WORD PTR ES:[di]         ; no override allowed
  631.  
  632.              MOVS
  633.                  movsb
  634.                  movsw
  635.                  movs BYTE PTR ES:[di], SS:[si]     ;or CS, DS, ES:[si]
  636.                  movs WORD PTR ES:[di], SS:[si]     ;or CS, DS, ES:[si]
  637.  
  638.              CMPS
  639.                  cmpsb
  640.  
  641.  
  642.  
  643.  
  644.              Chapter 19 - Strings                                          209
  645.              ____________________
  646.  
  647.                  cmpsw
  648.                  cmps BYTE PTR SS:[si], ES:[di]     ;or CS, DS, ES:[si]
  649.                  cmps WORD PTR SS:[si], ES:[di]     ;or CS, DS, ES:[si]
  650.  
  651.  
  652.              Just because you can do overrides with these instructions doesn't
  653.              mean that you should. In fact, there is a problem. If you are
  654.              using the REP instruction with an override:
  655.  
  656.                  rep movs  WORD PTR ES:[di], SS:[si] 
  657.  
  658.              and the 8086 gets a hardware interrupt,{5} the 8086 forgets the
  659.              override. What this means is that one moment you are moving data
  660.              from the SS segment, and the next moment you are moving data from
  661.              the same offset, but in the DS segment. This just won't do. Thus
  662.              the rule is:
  663.  
  664.                  NEVER USE AN OVERRIDE WITH A REP/REPE/REPNE INSTRUCTION
  665.  
  666.              This actually is no hardship. Using the override adds time to the
  667.              instruction. All you need to do is change the segment addresses
  668.              for the duration of the string instruction, and the code will run
  669.              faster. Of course, there is the setup time, but the break even
  670.              point is say, 20 repeats. Here is what you would do if you needed
  671.              an SS segment override:
  672.  
  673.                  push ds        ; save old DS
  674.                  push ss        ; move SS to DS
  675.                  pop  ds        ; the same as an SS:[di] override
  676.  
  677.                  rep  movsb
  678.  
  679.                  pop  ds        ; get old DS back
  680.  
  681.              The other possibility is to use LOOP instead of REP. It is
  682.              slower, but better slower and reliable than faster and
  683.              unreliable. 
  684.  
  685.                  rep  movs BYTE PTR ES:[di], SS:[si]
  686.  
  687.              is the same as:
  688.  
  689.              repeat_loop:
  690.                  movs BYTE PTR ES:[di], SS:[si]
  691.                  loop repeat_loop
  692.  
  693.              There are even three forms of the LOOP instruction: LOOP, LOOPE,
  694.              LOOPNE which are the exact counterparts to REP, REPE, REPNE.
  695.  
  696.  
  697.  
  698.  
  699.              ____________________
  700.  
  701.                 5. Which can be caused by such rare occurances as your
  702.              pressing a key on the keyboard or one of the 18 timer interrupts
  703.              that happen each second.
  704.  
  705.  
  706.  
  707.  
  708.              The PC Assembler Tutor                                        210
  709.              ______________________
  710.  
  711.  
  712.                                         SUMMARY
  713.  
  714.  
  715.  
  716.              LODS (load from string) moves a byte or word from DS:[si] to AL
  717.              or AX, and increments (or decrements) SI depending on the setting
  718.              of DF, the direction flag (by 1 for bytes and by 2 for words).
  719.              You may use CS:[si], SS:[si] or ES:[si]. This performs the same
  720.              action (except for changing SI) as:
  721.  
  722.                  mov  ax, DS:[SI]              ; or AL for bytes
  723.  
  724.              The allowable forms are:
  725.  
  726.                  lodsb
  727.                  lodsw
  728.                  lods BYTE PTR SS:[si]         ; or CS:[si], DS:[si], ES:[si]
  729.                  lods WORD PTR SS:[si]         ; or CS:[si], DS:[si], ES:[si]
  730.  
  731.  
  732.              STOS (store to string) moves a byte (or a word) from AL (or AX)
  733.              to ES:[di], and increments (or decrements) DI depending on the
  734.              setting of DF, the direction flag (by 1 for bytes and by 2 for
  735.              words). NO OVERRIDES ARE ALLOWED. This performs the same action
  736.              (except for changing DI) as:
  737.  
  738.                  mov  ES:[DI], ax              ; or AL for bytes
  739.  
  740.              The allowable forms are:
  741.  
  742.                  stosb
  743.                  stosw
  744.                  stos BYTE PTR ES:[di]         ; no override allowed
  745.                  stos WORD PTR ES:[di]         ; no override allowed
  746.  
  747.  
  748.              SCAS compares AL (or AX) to the byte (or word) pointed to by
  749.              ES:[di], and increments (or decrements) DI depending on the
  750.              setting of DF, the direction flag (by 1 for bytes and by 2 for
  751.              words). NO OVERRIDES ARE ALLOWED. This sets the flags the same
  752.              way as:
  753.  
  754.                  cmp  ax, ES:[DI]              ; or AL for bytes
  755.  
  756.              The allowable forms are:
  757.  
  758.                  scasb
  759.                  scasw
  760.                  scas BYTE PTR ES:[di]         ; no override allowed
  761.                  scas WORD PTR ES:[di]         ; no override allowed
  762.  
  763.  
  764.              MOVS moves a byte (or a word) from DS:[si] to ES:[di], and
  765.              increments (or decrements) SI and DI, depending on the setting of
  766.              DF, the direction flag (by 1 for bytes and by 2 for words).  You
  767.              may use CS:[si], SS:[si] or ES:[si], but you MAY NOT OVERRIDE
  768.  
  769.  
  770.  
  771.  
  772.              Chapter 19 - Strings                                          211
  773.              ____________________
  774.  
  775.              ES:[di]. Though the following is not a legal instruction, it
  776.              signifies the equivalent action to MOVS (not including changing
  777.              DI and SI):
  778.  
  779.                  mov  WORD PTR ES:[DI], DS:[SI]     ; or BYTE PTR for bytes
  780.  
  781.              The allowable forms are:
  782.  
  783.                  movsb
  784.                  movsw
  785.                  movs BYTE PTR ES:[di], SS:[si]     ;or CS, DS, ES:[si]
  786.                  movs WORD PTR ES:[di], SS:[si]     ;or CS, DS, ES:[si]
  787.  
  788.  
  789.              CMPS compares the byte (or a word) at DS:[si] to the one at
  790.              ES:[di], and increments (or decrements) SI and DI, depending on
  791.              the setting of DF, the direction flag (by 1 for bytes and by 2
  792.              for words).  You may use CS:[si], SS:[si] or ES:[si], but you MAY
  793.              NOT OVERRIDE ES:[di]. Although the following is not a legal
  794.              action, it signifies the equivalent action to CMPS (not including
  795.              changing DI and SI):
  796.  
  797.                  cmp  WORD PTR DS:[SI], ES:[DI]     ; or BYTE PTR for bytes
  798.  
  799.              The allowable forms are:
  800.  
  801.                  cmpsb
  802.                  cmpsw
  803.                  cmps BYTE PTR SS:[si], ES:[di]     ;or CS, DS, ES:[si]
  804.                  cmps WORD PTR SS:[si], ES:[di]     ;or CS, DS, ES:[si]
  805.  
  806.  
  807.  
  808.  
  809.              The string instructions may be prefixed by REP/REPE/REPNE which
  810.              will repeat the instructions according to the following
  811.              conditions:
  812.  
  813.  
  814.                  rep       decrement cx ; repeat if cx is not zero
  815.                  repe      decrement cx ; repeat if cx not zero AND zf = 1
  816.                  repz      decrement cx ; repeat if cx not zero AND zf = 1
  817.                  repne     decrement cx ; repeat if cx not zero AND zf = 0
  818.                  repnz     decrement cx ; repeat if cx not zero AND zf = 0
  819.  
  820.              Here, 'e' stands for equal, 'z' is zero and 'n' is not. These
  821.              repeat instructions should NEVER be used with a segment override,
  822.              since the 8086 will forget the override if a hardware interrupt
  823.              occurs in the middle of the REP loop. 
  824.  
  825.  
  826.              'HARD' FLAGS
  827.  
  828.              IEF, TF and DF are 'hard' flags. Once they are set they remain in
  829.              the same setting. If you use DF, the direction flag, in a
  830.              subroutine, you must save the flags upon entry and restore the
  831.              flags on exiting to make sure that DF has not been altered.
  832.  
  833.