home *** CD-ROM | disk | FTP | other *** search
/ The Unsorted BBS Collection / thegreatunsorted.tar / thegreatunsorted / programming / asm_programming / CHAP22-2.DOC < prev    next >
Text File  |  1990-07-31  |  38KB  |  827 lines

  1.  
  2.  
  3.  
  4.                                                                            240
  5.  
  6.              The first thing to talk about is the 8086 mnemonics. The four
  7.              instructions for unpacked BCD numbers are:
  8.  
  9.                  AAA       ASCII adjust for addition
  10.                  AAD       ASCII adjust for division
  11.                  AAM       ASCII adjust for multiplication
  12.                  AAS       ASCII adjust for subtraction.
  13.  
  14.  
  15.              Even though all four instructions have ASCII as part of their
  16.              mnemonic, they have NOTHING to do with ASCII numbers. These
  17.              instructions operate on unpacked BCD numbers. They always give
  18.              results which are unpacked BCD numbers. Because of side effects
  19.              of the 8086 microcode, the add and subtract instructions do
  20.              something unusual, but this will be covered a little later.
  21.  
  22.              Just like the packed BCD instructions, these four unpacked
  23.              instructions use the normal arithmetic operations and adjust the
  24.              results to compensate for their being unpacked BCD numbers. Let's
  25.              start with addition. The following program is like the BCD
  26.              program except that we use AAA (ascii adjust for addition)
  27.              instead of DAA (decimal adjust for addition).
  28.  
  29.              ; - - - - - - - - - - START CODE BELOW THIS LINE 
  30.                     mov   ax_byte, 0C4h       ; half registers, hex, hex 
  31.                     mov   bx_byte, 94h        ; half regs, signed, hex
  32.                     mov   dx_byte, 91h        ; half regs, signed, signed
  33.                     lea   ax, ax_byte 
  34.                     call  set_reg_style 
  35.               
  36.                     mov   cx, 0                    ; clear cx for clarity
  37.              outer_loop:                            
  38.                     mov   ax, 0                    ; clear the registers 
  39.                     mov   bx, 0 
  40.                     mov   dx, 0 
  41.                     call  set_count 
  42.                     call  show_regs        
  43.               
  44.                     call  get_hex_byte             ; byte for al 
  45.                     mov   dx, ax                   ; copy for dx 
  46.                     push  ax                       ; save al 
  47.                     call  get_hex_byte             ; byte for bl 
  48.                     mov   bl, al 
  49.                     mov   bh, bl                   ; copy to bh
  50.                     pop   ax                       ; restore al 
  51.                     call  show_regs_and_wait 
  52.                     add   al, bl                   ; normal add  
  53.                     mov   dx, ax                   ; copy to dx 
  54.                     call  show_regs_and_wait 
  55.                     aaa                            ; make adjustment 
  56.                     mov   dx, ax                   ; copy to dx 
  57.                     call  show_regs_and_wait 
  58.                     jmp   outer_loop 
  59.              ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE 
  60.  
  61.              ______________________
  62.  
  63.              The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
  64.  
  65.  
  66.  
  67.  
  68.              Chapter 22 - BCD Numbers                                      241
  69.              ________________________
  70.  
  71.  
  72.              Since this is a mixture of unpacked BCD instructions and normal
  73.              integer instructions, a copy of AX (which is showing hex) is
  74.              moved to DX (which is showing signed) each time AX is changed.
  75.              Also, a copy of BL is put in BH.
  76.  
  77.              For the rest of this chapter, I will refer to the one's digit,
  78.              the ten's digit and the hundred's digit. In the number 346, 3 is
  79.              the hundred's digit, 4 is the ten's digit, and 6 is the one's
  80.              digit. In the number 928, 9 is the hundred's digit, 2 is the
  81.              ten's digit, and 8 is the one's digit.
  82.  
  83.              If two valid unpacked BCD numbers are added, the result will be
  84.              between 0 and 18. This result MUST be in AL. AAA sets CF if this
  85.              result is greater than 9 ( i.e. there was a carry). AAA leaves
  86.              the one's digit in AL and adds the ten's digit to AH. (The ten's
  87.              digit can only be 0 for 0 - 9 or 1 for 10 - 18).
  88.  
  89.              Run this. The standard input should be hex numbers between 00h
  90.              and 09h. When you feel comfortable with this, stop for a minute
  91.              and read on.
  92.  
  93.              We now come to the peculiarity of this instruction. It isn't
  94.              operating on the whole byte, only the lower half byte.
  95.              Technically, if the LOWER HALF BYTE of each of the two numbers
  96.              which are added is a legitimate BCD digit, then at the end of AAA
  97.              the above results will be true and the UPPER HALF BYTE of AL will
  98.              be set to 0. If you put anything in the upper half byte, it will
  99.              be added by ADD, but will be blanked out (zeroed) by AAA.{1}
  100.  
  101.  
  102.              As a side effect of blanking out (zeroing) the upper half byte,
  103.              it is possible to use the ASCII numbers '0' (30h) to '9' (39h) as
  104.              addends. The result (after AAA) will still be a number between
  105.              00h and 09h with the carry flag either set or cleared, and AH
  106.              incremented if appropriate. Don't use it this way. The
  107.              multiplication and division instructions REQUIRE that the numbers
  108.              be between 00h and 09h, so all numbers should be changed into
  109.              unpacked BCD on data entry. Here is a partial list of things you
  110.              can add to get the result 07h:
  111.  
  112.                  03h + 04h           'c' + 'd'           '+' + 't'
  113.                  '3' + '4'           's' + 't'           'c' + '4'
  114.                  'C' + 'D'           '#' + '$'           'S' + '$'
  115.  
  116.              As you can see, the addition instruction is pretty indiscriminate
  117.              about what kind of data you can put in and still get a legitimate
  118.              unpacked number out. It is your job to make sure that you have
  119.              ____________________
  120.  
  121.                 1. They used the same microcode as for the beginning of DAA.
  122.              If the low half byte of AL is greater than 9, or if there was a
  123.              carry out of the low half byte (if AF, the auxillary flag was
  124.              set), then the 8086 adds 6 to AL, which shifts the excess out of
  125.              the low half byte. It then zeros the high half byte, sets the
  126.              carry flag, and increments AH. If you don't understand this, go
  127.              back and give the footnote on DAA a try. 
  128.  
  129.  
  130.  
  131.  
  132.              The PC Assembler Tutor                                        242
  133.              ______________________
  134.  
  135.              legitimate unpacked data upon data entry.
  136.  
  137.              If this is just a side effect of blanking the upper half byte,
  138.              why did they name the instruction "ascii adjust for addition"?
  139.              It's just that impish sense of humor of those Intel engineers. If
  140.              you think of these instructions as having anything to do with
  141.              ASCII numbers, you will only confuse yourself. These are
  142.              instructions on unpacked BCD numbers, period. 
  143.  
  144.              The other thing to notice is that this instruction increments the
  145.              AH register if there is a carry, so whenever you use AAA, it is
  146.              going to trash the AH register. Count on it. If AH contains
  147.              important data, store it somewhere else.
  148.  
  149.  
  150.              SUBTRACTION
  151.  
  152.              When you have gotten used to how this works, look at subtraction.
  153.              The subtraction routine is the same as the addition routine
  154.              except that (1) ADD is replaced by SUB and (2) AAA is replaced by
  155.              AAS (ascii adjust for subtraction). If you generate a borrow, the
  156.              carry flag will be set and AH will be decremented by 1. 
  157.  
  158.              If you have two legitimate unpacked BCD numbers, then after
  159.              subtraction the result will be from +9 to -9. This result must be
  160.              in AL. After AAS (ascii adjust for subtraction), (1) if AL was
  161.              from 0 to +9, it will stay the same and CF will be cleared (CF=0)
  162.              or (2) if AL was -1 to -9, AAF will borrow 1 from AH (considering
  163.              it a ten's digit), and add 10 to AL. This will give a number from
  164.              +1 to +9. It will also set the carry flag (CF=1) to signal a
  165.              borrow. This is what you do with pencil and paper except that you
  166.              always do the borrow BEFORE the subtraction and AAS always does
  167.              the borrow AFTER the subtraction. Once again, this operates on
  168.              the LOW HALF BYTE, so what is in the upper half byte of the
  169.              numbers is irrelevant. It will be zeroed by AAS.
  170.  
  171.              There is all sorts of data which will generate a legitimate
  172.              unpacked result; some of it is actually legitimate input. It too
  173.              trashes the AH register, so beware. Run this program till you see
  174.              what is going on with individual numbers.
  175.  
  176.  
  177.              MULTIPLICATION
  178.  
  179.              There is also an instruction for multiplication. It assumes that
  180.              AL contains the result of the multiplication of two unpacked BCD
  181.              numbers. That is, 0 <= AL <= 81. After AAM (ascii adjust for
  182.              multiplication), AH will contain the 10's digit and AL will
  183.              contain the 1's digit. If AL is 75, then after AAM, AH will be 7
  184.              and AL will be 5. If AL is 48, then after AAM, AH will be 4 and
  185.              AL will be 8. (If AL is 183, an illegal result, then after AAM, 
  186.              AH will be 18 and AL will be 3).
  187.  
  188.              We are going to use a similar program for multiplication, but AX
  189.              and BL will be half byte unsigned; BH and DX will be half byte
  190.              hex. Every time we change AX, we will copy it to DX. Here is the
  191.              program:
  192.  
  193.  
  194.  
  195.  
  196.              Chapter 22 - BCD Numbers                                      243
  197.              ________________________
  198.  
  199.  
  200.              ; - - - - - - - - - - START CODE BELOW THIS LINE 
  201.                     mov   ax_byte, 0A2h       ; half registers, unsigned 
  202.                     mov   bx_byte, 0C2h       ; half regs, hex, unsigned 
  203.                     mov   dx_byte, 0C4h       ; half regs, hex 
  204.                     lea   ax, ax_byte 
  205.                     call  set_reg_style 
  206.  
  207.                     mov   cx, 0               ; clear cx for clarity 
  208.              outer_loop: 
  209.                     mov   ax, 0               ; clear the registers 
  210.                     mov   bx, 0 
  211.                     mov   dx, 0 
  212.                     call  set_count 
  213.                     call  show_regs       
  214.               
  215.                     call  get_hex_byte        ; byte for al 
  216.                     mov   dx, ax              ; copy to dx 
  217.                     push  ax                  ; save al 
  218.                     call  get_hex_byte        ; byte for bl 
  219.                     mov   bl, al 
  220.                     mov   bh, bl              ; copy to bh 
  221.                     pop   ax                  ; restore al 
  222.                     call  show_regs_and_wait 
  223.                     mul   bl                  ; unsigned multiplication 
  224.                     mov   dx, ax              ; copy to dx
  225.                     call  show_regs_and_wait 
  226.                     aam                       ; make adjustment 
  227.                     mov   dx, ax              ; copy to dx 
  228.                     call  show_regs_and_wait 
  229.                     jmp   outer_loop 
  230.               ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE 
  231.  
  232.              All you need to change from the addition program is the register
  233.              style, ADD -> MUL and AAA -> AAM.
  234.  
  235.              Try this out. Notice that after MUL, what we get in DX is
  236.              garbage. This number is a pure unsigned number, not a BCD number.
  237.              Just to underline how important it is to have legitimate unpacked
  238.              BCD data, try a few multiplications where the upper half byte is
  239.              non-zero and see what happens.
  240.  
  241.  
  242.              DIVISION
  243.  
  244.              AAD (ascii adjust for division) is slightly different. What we
  245.              want to do is divide a number from 0 - 99 by a number from 1 to 9
  246.              (0 gives a zero divide interrupt). Therefore, AAD multiplies what
  247.              is in AH by 10 and adds it to what is in AL. It clears AH for the
  248.              coming unsigned division.{2}  If AH contains 7 and AL contains 2,
  249.              then after AAD, AL will be 72 and AH will be 0. If AH is 4 and AL
  250.              is 6, then after AAD, AL will be 46 and AH will be 0. (If AH is
  251.              14 and AL is 7 - an illegal situation - then after AAD, AL will
  252.              ____________________
  253.  
  254.                 2. Remember that for unsigned byte division, we set AH to 0.
  255.              This was back in the first chapter on division.
  256.  
  257.  
  258.  
  259.  
  260.              The PC Assembler Tutor                                        244
  261.              ______________________
  262.  
  263.              be 147 and AH will be 0)
  264.  
  265.              After you have done AAD, you are ready for regular unsigned
  266.              division. After division, the quotient will be in AL and the
  267.              remainder will be in AH. Here's the program:
  268.  
  269.              ; - - - - - - - - - - START CODE BELOW THIS LINE 
  270.                     mov   ax_byte, 0A2h      ; half registers, unsigned 
  271.                     mov   bx_byte, 0C2h      ; half regs, hex, unsigned 
  272.                     mov   dx_byte, 0C4h      ; half regs, hex 
  273.                     lea   ax, ax_byte 
  274.                     call  set_reg_style 
  275.               
  276.                     mov   cx, 0              ; clear cx for clarity 
  277.              outer_loop: 
  278.                     mov   ax, 0               ; clear the registers 
  279.                     mov   bx, 0 
  280.                     mov   dx, 0 
  281.                     call  set_count 
  282.                     call  show_regs       
  283.               
  284.                     call  get_hex             ; word 
  285.                     mov   dx, ax              ; copy to dx 
  286.                     push  ax                  ; save al for later 
  287.                     call  get_hex_byte        ; byte for bl 
  288.                     mov   bl, al 
  289.                     mov   bh, bl              ; copy to bh 
  290.                     pop   ax                  ; get back al 
  291.                     call  show_regs_and_wait 
  292.                     aad                       ; adjust for division 
  293.                     mov   dx, ax              ; copy to dx
  294.                     call  show_regs_and_wait 
  295.                     div   bl                  ; unsigned division 
  296.                     mov   dx, ax              ; copy to dx 
  297.                     call  show_regs_and_wait 
  298.                     jmp   outer_loop 
  299.               
  300.              ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE 
  301.  
  302.              The differences from the multiplication routine are (1) we get a
  303.              TWO byte hex number for the dividend and (2) AAD comes first,
  304.              then DIV. That's all.
  305.  
  306.              We need to enter a TWO byte unpacked BCD number in order to get
  307.              grist for the mill. 87 is 0807h, 93 is 0903h, 42 is 0402h. This
  308.              will give us two unpacked digits so we have something to put in
  309.              AH. 
  310.  
  311.              Run this program and enter legitimate (and if you want,
  312.              illegitimate) data. Notice that even with legitimate data, it is
  313.              possible to get a quotient that is larger than 9. (47 / 3 is 15,
  314.              remainder 2). Don't worry about this. We will avoid this problem
  315.              in the real program.
  316.  
  317.  
  318.              PACKING AND UNPACKING
  319.  
  320.  
  321.  
  322.  
  323.  
  324.              Chapter 22 - BCD Numbers                                      245
  325.              ________________________
  326.  
  327.              Making an i/o routine is such a bother that we will enter data
  328.              with 'get_bcd' and output data with 'print_bcd'. To do this, we
  329.              need to pack and unpack the data. The calls in C would be:
  330.  
  331.                  unpack_bcd (&packed_number, &unpacked_number) ;
  332.                  pack_bcd (&unpacked_number, &packed_number) ;{3}
  333.  
  334.              The thing to be operated on is the first variable and the result
  335.              is the second variable. First, here's the unpacking routine:
  336.  
  337.              ; - - - - - - - - - -  
  338.              unpack_bcd proc near 
  339.               
  340.                     UNPACK_UNPACKED_ADDRESS  EQU    [bp + 6] 
  341.                     UNPACK_BCD_ADDRESS       EQU    [bp + 4] 
  342.               
  343.               
  344.                     push  bp 
  345.                     mov   bp, sp 
  346.                     PUSHREGS  ax, bx, cx, si, di 
  347.               
  348.                     mov   si, UNPACK_BCD_ADDRESS 
  349.                     mov   di, UNPACK_UNPACKED_ADDRESS 
  350.               
  351.                     ; start at top to unpack 
  352.                     add   si, 9               ; bcd sign 
  353.                     add   di, 18              ; unpacked sign 
  354.                     mov   al, [si]            ; move sign to unpacked 
  355.                     mov   [di], al 
  356.                     dec   si                  ; move down to next byte 
  357.                     dec   di 
  358.                      
  359.                     mov   cx, 9               ; 9 bcd data bytes 
  360.                     ; unpack high byte first, then low byte 
  361.              unpack_loop: 
  362.                     push  cx                  ; save counter
  363.                     mov   al, [si]            ; bcd byte to al 
  364.                     mov   bl, al              ; copy to bl 
  365.                     mov   cl, 4 
  366.                     ror   al, cl              ; high half byte to low half 
  367.                     and   al, 0Fh             ; blank upper half byte 
  368.                     mov   [di], al            ; al is high half of bcd byte 
  369.                     dec   di                  ; result pointer
  370.                     and   bl, 0Fh             ; blank upper half byte 
  371.                     mov   [di], bl            ; bl is low half bcd byte 
  372.                     dec   di                  ; result pointer
  373.                     dec   si                  ; source pointer
  374.                     pop   cx                  ; restore counter
  375.                     loop  unpack_loop 
  376.               
  377.                     POPREGS  ax, bx, cx, si, di 
  378.                     pop   bp 
  379.                     ret 
  380.              ____________________
  381.  
  382.                 3. That '&' means that we are passing the addresses, not the
  383.              values.
  384.  
  385.  
  386.  
  387.  
  388.              The PC Assembler Tutor                                        246
  389.              ______________________
  390.  
  391.               
  392.              unpack_bcd  endp 
  393.              ; - - - - - - - - - - - - - - - - 
  394.  
  395.              This is pretty straightforward. First it moves the sign. Then,
  396.              taking a BCD byte, the routine divides it in two parts, rotating
  397.              the high half byte to the proper position, storing it,
  398.              DECREMENTING the pointer and storing the low half byte. The upper
  399.              half byte of each unpacked byte is zeroed. Notice that by
  400.              starting at the top, it is possible to unpack a number in place.
  401.              Diagram what is happening to make sure you believe that.
  402.  
  403.              Here's the packing routine:
  404.  
  405.              ; - - - - - - - - - - - - - - - - 
  406.              pack_bcd     proc near 
  407.               
  408.                     PACK_BCD_ADDRESS       EQU     [bp + 6] 
  409.                     PACK_UNPACKED_ADDRESS  EQU     [bp + 4] 
  410.               
  411.                     push  bp 
  412.                     mov   bp, sp 
  413.                     PUSHREGS  ax, bx, cx, si, di 
  414.               
  415.                     mov   si, PACK_UNPACKED_ADDRESS 
  416.                     mov   di, PACK_BCD_ADDRESS 
  417.               
  418.                     ; start at bottom to pack 
  419.                     mov   cx, 9               ; 9 bytes of bcd data 
  420.              pack_loop: 
  421.                     push  cx                  ; save counter
  422.                     mov   al, [si + 1]        ; high unpacked byte 
  423.                     mov   cl, 4 
  424.                     ror   al, cl              ; move to high half byte 
  425.                     or    al, [si]            ; OR low half with high half 
  426.                     mov   [di], al            ; move to BCD 
  427.                     inc   di                  ; adjust pointers 
  428.                     add   si, 2  
  429.                     pop   cx                  ; restore counter
  430.                     loop  pack_loop 
  431.               
  432.                     ; si and di are now in the right place for the sign 
  433.                     mov   al, [si] 
  434.                     mov   [di], al 
  435.               
  436.                     POPREGS  ax, bx, cx, si, di 
  437.                     pop   bp 
  438.                     ret 
  439.               
  440.              pack_bcd endp 
  441.              ; - - - - - - - - 
  442.  
  443.              This does the reverse process, rotating the high byte to the
  444.              proper place, then ORing the two together to form a packed BCD
  445.              byte. Notice that by starting at the BOTTOM it is possible to
  446.              pack a number in place. Diagram the action to make sure you
  447.              believe this. 
  448.  
  449.  
  450.  
  451.  
  452.              Chapter 22 - BCD Numbers                                      247
  453.              ________________________
  454.  
  455.  
  456.              These two routines allow us to use 19 byte unpacked BCD numbers.
  457.              Here's the multiplication routine for a 19 byte (18 data bytes
  458.              and 1 sign byte) unpacked number by a 1 byte unpacked number:
  459.  
  460.              ; - - - - -
  461.              unpacked_multiply  proc near 
  462.               
  463.                     PUSHREGS  ax, bx, cx, dx, si, di 
  464.                     mov   si, offset multiplicand 
  465.                     mov   di, offset result 
  466.                     mov   dh, 0                    ; clear dh for carry 
  467.                     mov   bl, multiplier_copy 
  468.                     mov   cx, 18                   ; 18 data bytes 
  469.               
  470.              mult_loop: 
  471.                     mov   al, [si]                 ; multiplicand to al 
  472.                     mul   bl                       ; multiply 
  473.                     add   al, dh                   ; add old carry 
  474.                     aam                            ; adjust al 
  475.                     mov   [di], al                 ; partial result 
  476.                     mov   dh, ah                   ; save carry 
  477.                     inc   si                       ; adjust pointers
  478.                     inc   di 
  479.                     loop  mult_loop 
  480.               
  481.                     mov   extra_byte, ah           ; extra byte 
  482.                     POPREGS  ax, bx, cx, dx, si, di 
  483.                     ret 
  484.               
  485.              unpacked_multiply  endp 
  486.              ; - - - - -  
  487.  
  488.              This is a clone of what we had in the chapter on multiple word
  489.              multiplication but is byte multiplication instead of word
  490.              multiplication. Refer back to that chapter to understand the
  491.              mechanics of the process. We store the partial result and save
  492.              the high byte for addition with the next multiplication. At the
  493.              end we save the 19th byte for printout.
  494.  
  495.              We are cheating a little. we have:
  496.  
  497.                     mul   bl                       ; multiply 
  498.                     add   al, dh                   ; add old carry 
  499.                     aam                            ; adjust al 
  500.  
  501.              when we should have:
  502.  
  503.                     mul   bl                       ; multiply 
  504.                     aam                            ; adjust al 
  505.                     add   al, dh                   ; add old carry 
  506.                     aaa                            ; adjust al
  507.  
  508.              The reason this works is that the maximum multiplication is
  509.              9X9=81. The maximum addition is 81+9 = 90. AAM will work
  510.              correctly with any number 99 or less, so we save a step; we do
  511.              one adjustment instead of two. 
  512.  
  513.  
  514.  
  515.  
  516.              The PC Assembler Tutor                                        248
  517.              ______________________
  518.  
  519.  
  520.              Now, let's look at the driver for the program:
  521.  
  522.              ; + + + + + START DATA BELOW THIS LINE 
  523.              multiplier_message  db    "Enter a number from -9 to +9", 0 
  524.              bcd_in              dt    ? 
  525.              bcd_out             dt    ? 
  526.              multiplicand        db    19 dup (?) 
  527.              multiplier          db    ? 
  528.              multiplier_copy     db    ? 
  529.              result              db    19 dup (?) 
  530.              extra_byte          db    ? 
  531.              result_sign         db    ? 
  532.              ; + + + + + END DATA ABOVE THIS LINE 
  533.  
  534.  
  535.              ; + + + + + START CODE BELOW THIS LINE 
  536.              outer_loop: 
  537.                     mov   ax, offset bcd_in 
  538.                     call  get_bcd 
  539.                     call  print_bcd           ; reprint for clarity 
  540.                     lea   cx, multiplicand 
  541.                     push  cx                  ; unpacked address 
  542.                     push  ax                  ; bcd_in address 
  543.                     call  unpack_bcd 
  544.                     add   sp, 4               ; adjust the stack 
  545.                     mov   al, multiplicand + 18      ; sign byte 
  546.                     mov   result_sign, al     ; either 00h or 80h 
  547.               
  548.              enter_multiplier: 
  549.                     lea   ax, multiplier_message 
  550.                     call  print_string 
  551.                     call  get_signed_byte 
  552.                     ; check for valid multiplier 
  553.                     cmp   al, 9               ; > +9 ? 
  554.                     jg    enter_multiplier 
  555.                     cmp   al, -9              ; < -9? 
  556.                     jl    enter_multiplier 
  557.               
  558.                     ; adjust multiplier sign 
  559.                     mov   multiplier, al  
  560.                     mov   multiplier_copy, al 
  561.                     and   al, 80h             ; sign bit set? 
  562.                     jz    do_the_multiplication 
  563.                     ; negative multiplier, so adjust 
  564.                     neg   multiplier_copy     ; make positive 
  565.                     xor   result_sign, 80h    ; reverse sign of result 
  566.               
  567.              do_the_multiplication: 
  568.                     call  unpacked_multiply 
  569.               
  570.                     mov   al, result_sign   ; transfer sign to result 
  571.                     mov   result + 18, al 
  572.               
  573.                     ; pack the result 
  574.                     mov   ax, offset bcd_out  ; bcd number 
  575.                     push  ax 
  576.  
  577.  
  578.  
  579.  
  580.              Chapter 22 - BCD Numbers                                      249
  581.              ________________________
  582.  
  583.                     mov   ax, offset result   ; unpacked number 
  584.                     push  ax 
  585.                     call  pack_bcd 
  586.                     add   sp, 4               ; adjust the stack 
  587.               
  588.                     ; print multiplicand, multiplier, extra byte and result 
  589.                     mov   ax, offset bcd_in 
  590.                     call  print_bcd 
  591.                     mov   al, multiplier 
  592.                     call  print_signed_byte 
  593.                     mov   al, extra_byte 
  594.                     call  print_hex_byte 
  595.                     mov   ax, offset bcd_out 
  596.                     call  print_bcd 
  597.  
  598.                     jmp   outer_loop 
  599.              ; - - - - - END CODE ABOVE THIS LINE
  600.  
  601.              We enter a multiplicand, unpack it, enter a number from -9 to +9
  602.              and save a copy of its absolute value as well as the sign the
  603.              result will be. We adjust the sign of the result after the
  604.              multiplication. No matter what you enter, the sign will be
  605.              correct, and if you put the 19th byte in front of the BCD number
  606.              which you have printed, the absolute value will be correct. We
  607.              are multiplying with a COPY of the multiplier, so we can reprint
  608.              the actual multiplier; it hasn't changed. At the end we pack the
  609.              result and print everything. The order of printout is
  610.              multiplicand, multiplier, extra byte and result.
  611.  
  612.              Notice that we are not passing the parameters for
  613.              unpacked_multiply on the stack. This is pure whim. A rule for
  614.              when to pass on the stack is (1) If the subroutine doesn't know
  615.              where the parameters will be located in memory, you MUST pass
  616.              them on the stack but (2) if the subroutine knows for certain
  617.              where the parameters will be, it can fetch them itself.
  618.  
  619.              DIVISION
  620.  
  621.              Here is the division routine for 19 byte unpacked numbers:
  622.  
  623.              ; - - - - - - - - 
  624.              unpacked_divide  proc near 
  625.               
  626.                     PUSHREGS  ax, bx, cx, si, di 
  627.               
  628.                     mov   bl, divisor_copy 
  629.                     mov   si, offset dividend + 17  ; start at the top 
  630.                     mov   di, offset quotient + 17 
  631.                     mov   cx, 18              ; 18 numeric bytes 
  632.                     mov   ah, 0               ; clear ah for division
  633.               
  634.              div_loop: 
  635.                     mov   al, [si]            ; dividend byte to al 
  636.                     aad                       ; adjust for unpacked number 
  637.                     div   bl                  ; bl is divisor 
  638.                     mov   [di], al            ; move partial quotient 
  639.                     dec   si                  ; decrement pointers 
  640.  
  641.  
  642.  
  643.  
  644.              The PC Assembler Tutor                                        250
  645.              ______________________
  646.  
  647.                     dec   di 
  648.                     loop  div_loop 
  649.               
  650.                     mov   remainder, ah       ; final remainder 
  651.               
  652.                     POPREGS  ax, bx, cx, si, di 
  653.                     ret 
  654.               
  655.              unpacked_divide  endp 
  656.              ; - - - - - 
  657.  
  658.              It is pretty simple; it too is a clone of the multiple word
  659.              division process. Go back to multiple word division if you don't
  660.              understand it. We keep the previous remainder for the next
  661.              division. Here is the driver:
  662.  
  663.              ; + + + + + + + + + + + + + + + START DATA BELOW THIS LINE 
  664.              divisor_message db "Enter a number from -9 to +9 (but not 0).",0
  665.              bcd_in          dt     ? 
  666.              bcd_out         dt     ? 
  667.              quotient_sign   db     ? 
  668.              remainder_sign  db     ? 
  669.              divisor         db     ? 
  670.              divisor_copy    db     ? 
  671.              dividend        db     19 dup (?) 
  672.              quotient        db     19 dup (?) 
  673.              remainder       db     ? 
  674.              ; + + + + + + + + + + + + + + + END DATA ABOVE THIS LINE 
  675.  
  676.              ; + + + + + + + + + + + + + + + START CODE BELOW THIS LINE 
  677.              outer_loop: 
  678.                     mov   ax, offset bcd_in 
  679.                     call  get_bcd 
  680.                     call  print_bcd           ; reprint for clarity 
  681.                     lea   cx, dividend 
  682.                     push  cx                  ; unpacked address 
  683.                     push  ax                  ; bcd_in address 
  684.                     call  unpack_bcd 
  685.                     add   sp, 4               ; adjust the stack 
  686.                     mov   al, dividend + 18   ; sign byte 
  687.                     mov   quotient_sign, al   ; either 00h or 80h 
  688.                     mov   remainder_sign, al  ; ditto 
  689.               
  690.              enter_divisor: 
  691.                     lea   ax, divisor_message 
  692.                     call  print_string 
  693.                     call  get_signed_byte 
  694.                     ; check for valid divisor 
  695.                     cmp   al, 0               ; 0? 
  696.                     je    enter_divisor 
  697.                     cmp   al, 9               ; > +9 ? 
  698.                     jg    enter_divisor 
  699.                     cmp   al, -9              ; < -9? 
  700.                     jl    enter_divisor 
  701.               
  702.                     ; adjust divisor, quotient sign 
  703.                     mov   divisor, al  
  704.  
  705.  
  706.  
  707.  
  708.              Chapter 22 - BCD Numbers                                      251
  709.              ________________________
  710.  
  711.                     mov   divisor_copy, al 
  712.                     and   al, 80h             ; sign bit set? 
  713.                     jz    do_the_division 
  714.                     ; negative divisor, so adjust 
  715.                     neg   divisor_copy        ; make positive 
  716.                     xor   quotient_sign, 80h  ; reverse sign of quotient 
  717.               
  718.              do_the_division: 
  719.                     call  unpacked_divide 
  720.               
  721.                     mov   al, quotient_sign   ; transfer sign to quotient 
  722.                     mov   quotient + 18, al 
  723.                     test  remainder_sign, 0FFh  ; positive or negative?
  724.                     jz    pack_the_quotient
  725.                     neg   remainder             ; make the remainder negative
  726.               
  727.              pack_the_quotient:
  728.                     mov   ax, offset bcd_out  ; bcd number 
  729.                     push  ax 
  730.                     mov   ax, offset quotient ; unpacked number 
  731.                     push  ax 
  732.                     call  pack_bcd 
  733.                     add   sp, 4               ; adjust the stack 
  734.               
  735.                     ; print dividend, divisor, quotient and remainder 
  736.                     mov   ax, offset bcd_in 
  737.                     call  print_bcd 
  738.                     mov   al, divisor 
  739.                     call  print_signed_byte 
  740.                     mov   ax, offset bcd_out 
  741.                     call  print_bcd 
  742.                     mov   al, remainder 
  743.                     call  print_signed_byte 
  744.               
  745.                     jmp   outer_loop 
  746.              ; + + + + + + + + + + + + + + + END CODE ABOVE THIS LINE 
  747.  
  748.              Enter a BCD number, unpack it. Enter a signed number, do range
  749.              checking, and if it is ok, store it (along with a copy, which we
  750.              make sure is positive). Calculate the sign of the quotient and
  751.              remainder. Do the division, then print the dividend, divisor,
  752.              quotient and remainder. (which have been sign adjusted). The
  753.              remainder is a signed byte.
  754.  
  755.              Like unpacked_multiply, unpacked_divide fetches the parameters
  756.              directly from memory rather than from the stack.
  757.  
  758.  
  759.  
  760.  
  761.  
  762.  
  763.  
  764.  
  765.  
  766.  
  767.  
  768.  
  769.  
  770.  
  771.  
  772.              The PC Assembler Tutor                                        252
  773.              ______________________
  774.  
  775.                                     SUMMARY
  776.  
  777.  
  778.  
  779.              PACKED BCD INSTRUCTIONS
  780.  
  781.              DAA (decimal adjust for addition) adjusts AL, assuming that it
  782.              contains the result of a legitimate packed BCD addition. It
  783.              treats AL as two independent half-bytes. If the result of the
  784.              lower half-byte is 10 or over, it subtracts 10 from the lower
  785.              half-byte and adds the carry to the upper half byte. It then
  786.              looks at the upper half byte. If its result is 10 or over, DAA
  787.              subtracts 10 from the upper half byte and sets the carry flag.
  788.              Otherwise the carry flag is cleared.
  789.  
  790.              DAS (decimal adjust for subtraction) adjusts AL, assuming that it
  791.              contains the result of a legitimate packed BCD subtraction.It
  792.              treats AL as two independent half-bytes. If the result of the
  793.              lower half-byte is -1 or less, it adds 10 to the lower half-byte
  794.              and borrows 1 from the upper half byte. It then looks at the
  795.              upper half byte. If its result is -1 or less, DAS adds 10 to the
  796.              upper half byte and sets the carry flag to indicate a borrow.
  797.              Otherwise the carry flag is cleared.
  798.  
  799.  
  800.              UNPACKED BCD INSTRUCTIONS
  801.  
  802.              AAA  (ascii adjust for addition) adjusts AL, assuming that it
  803.              contains the result of a legitimate unpacked BCD addition. If the
  804.              lower half-byte has generated a result 10 or over, it subtracts
  805.              10, carries 1 to AH, and sets the carry flag. If the result is 9
  806.              or less, it clears CF. In either case it zeroes the upper
  807.              half-byte of AL.
  808.  
  809.              AAD (ascii adjust for division) PREPARES AL and AH for division.
  810.              It assumes that AH contains the 10's digit and AL contains the
  811.              1's digit of a two byte unpacked BCD number. It multiplies AH by
  812.              10 and adds it to AL, thus making a single integer between 0 and
  813.              99. It zeroes AH in preparation for division.
  814.  
  815.              AAM (ascii adjust for multiplication) adjusts AL, assuming that
  816.              it contains the result of a legitimate BCD multiplication. It
  817.              divides the result by 10, putting the quotient in AH and the
  818.              remainder in AL.
  819.  
  820.              AAS  (ascii adjust for subtraction) adjusts AL, assuming that it
  821.              contains the result of a legitimate unpacked BCD subtraction. If
  822.              the lower half-byte has generated a result -1 or less, it borrows
  823.              1 from AH, adds 10 to AL, and sets the carry flag. If the result
  824.              is 0 or more, it clears CF. In either case it zeroes the upper
  825.              half-byte of AL
  826.  
  827.