home *** CD-ROM | disk | FTP | other *** search
-
-
-
- 122
-
-
- CHAPTER 12 - MULTIPLE WORD ARITHMETIC I
-
-
- Let's review the LOOP instruction. We often want to repeat an
- action a specific number of times. In a FOR loop, we write:
-
- FOR I = 1, 10
-
- That means we want to repeat the code that follows ten times. The
- 8086 has an instruction for this, called the loop instruction. It
- looks like this:
-
- mov cx, 10
- label17:
- ...
- (a bunch of code)
- ...
- loop label17
-
- The count MUST be in the CX register. This is the only register
- you can use for this. When the machine sees the loop instruction,
- it decrements the CX register by one, LEAVING ALL FLAGS ALONE,
- and if the result is not zero, it loops back to the label. If the
- result is zero, it falls through the loop. One problem we might
- have with this instruction is if you enter it with CX = 0, it is
- going to loop 65,536 times. Intel provided a second instruction
- to avoid this - JCXZ (jump if CX is zero). You put it right
- before the loop for insurance.
-
- jcxz label19
- label17:
- ...
- (a bunch of code)
- ...
- loop label17
- label19:
-
- Obviously, in our first example this instruction is not necessary
- because we set CX to 10 just before entering the loop.
-
-
-
- If you have seen the list of 8086 instructions, you will have
- noticed lots of strange looking add and subtract instructions.
- Why are they there? In this chapter we will look at ADC and SBB.
- The others will come in later chapters.
-
- How do engineers decide what a reasonable size for a number is?
- When they started making 8 bit machines (the maximum unsigned
- number is 255) did they go out on the streets and take a poll to
- find out if 255 was the maximum number that people used? No,
- they didn't even think about what people needed. It was a
- question of what the technology would allow at that time.
-
- ______________________
-
- The PC Assembler Tutor - Copyright (C) 1989 Chuck Nelson
-
-
-
-
- Chapter 12 - Multiple Word Arithmetic I 123
- _______________________________________
-
- Similarly, at the time the 8086 was engineered, 16 bits was
- pushing the limits of the technology. But 65,535 doesn't really
- cut the mustard. If those are 65,535 pennies, that isn't even
- your yearly food bill, let alone the cost of housing.
-
- The 8086 instructions give us the option of making integers of
- whatever size we want. Because it is done in software, it is
- slower, but if we are doing thousands or tens of thousands of
- additions instead of hundreds of thousands or millions, it won't
- be a great inconvenience.{1}
-
- ASMHELP.OBJ is set up to input 2 word (4 byte) and 4 word (8
- byte) numbers so those are what we are going to use. 4 byte
- numbers are up to +/- 2 billion and 8 byte numbers are up to
- 9X10exp18. Those should be large enough.
-
- The first instruction is ADC, add with carry. When you add by
- hand, you add everything in the right column, then carry to the
- next column left, repeating this over and over. We don't need to
- do it column by column, but it is necessary to do it word by
- word. In the data section we need:
-
- variable1 dq ?
- variable2 dq ?
-
- and the code for a 4 word (8 byte) addition is the following:
-
- lea si, variable1
- lea di, variable2
- mov ax, [di] ; first addition
- add [si], ax
- pushf ; save the flags
- mov cx, 3
- add si, 2 ; go to next higher word
- add di, 2
-
- long_add_loop: ; next three additions
- mov ax, [di]
- popf ; restore the flags
- adc [si], ax
- pushf ; save the new flags
- add di, 2
- add si, 2
- loop long_add_loop
-
- popf ; pop the flags off the stack
-
-
- ADC is the same as ADD, but it looks at the carry flag - if the
- carry flag is 1, it adds 1 to the result; if the carry flag is
- zero, it does nothing to the result. This works for both signed
- and unsigned numbers. If you don't believe it, you should go back
- ____________________
-
- 1 As a benchmark, it took a moderately slow PC 6.5 seconds to
- do 100,000 eight byte additions. The same PC can do over a
- million two byte additions in 6 seconds.
-
-
-
-
- The PC Assembler Tutor 124
- ______________________
-
- to the introductory chapter with the base 10 machine and look at
- long additions.
-
- Notice PUSHF and POPF. These are special instructions called push
- flags and pop flags. Rather than pushing an arithmetic register
- on the stack, pushf pushes the register containing the flags.
- Popf pops them back into the flags register. This is necessary
- because ADC looks at the carry flag and the ADD instructions in
- the loop:
-
- add di,2
- add si,2
-
- might change the carry flag. The last POPF after the loop is to
- get it off the stack (anything we put on the stack we need to
- take off the stack).
-
- The first addition is a normal addition, the last three take the
- carry into account. We are moving the pointers a word at a time.
- Because the 8086 doesn't allow both operands to be in memory, we
- need to move one into a register. After the addition is
- performed, the result is in memory. We can discard what is in the
- register.
-
- Notice that the first half of the code looks almost the same as
- the code inside the loop. If we could only use ADC instead of
- ADD, we could put the first addition inside the loop. It is
- possible to do this. There is another instruction, CLC, which
- clears the carry flag. Recall that if the carry flag is 0, ADC
- does nothing different from ADD. Therefore, we can have:
-
- lea si, variable1
- lea di, variable2
- mov cx, 4 ; 4 additions in loop
- clc ; set cf to zero
- pushf ; save the flags
-
- long_add_loop:
- mov ax, [di] ; word to a register
- popf ; restore the flags
- adc [si], ax ; register + memory
- pushf ; save the flags
- add di, 2
- add si, 2
- loop long_add_loop
-
- popf ; pop the flags off the stack
-
- It's the same code. The number of loops was increased from 3 to
- 4, and the carry flag was cleared to insure that the first
- addition would have nothing extra added. Here is the basic
- program.
-
- ; - - - - - PUT DATA BELOW THIS LINE
- variable1 dq ?
- variable2 dq ?
- ; - - - - - PUT DATA ABOVE THIS LINE
-
-
-
-
- Chapter 12 - Multiple Word Arithmetic I 125
- _______________________________________
-
-
- ; - - - - - PUT CODE BELOW THIS LINE
-
- call show_regs
-
- outer_loop:
- lea ax, variable1 ; get the variables
- call get_signed_8byte
- call print_signed_8byte
- lea ax, variable2
- call get_signed_8byte
- call print_signed_8byte
-
- lea si, variable1 ; set the pointers
- lea di, variable2
- mov cx, 4 ; loop 4 times (4 words)
- clc ; clear the cf
- pushf ; save the flags
-
- long_add_loop:
- mov ax, [di] ; word to a register
- popf ; restore the flags
- adc [si], ax ; register + memory
- pushf ; save the flags
- add di, 2
- add si, 2
- loop long_add_loop
-
- popf ; restore the flags
-
- lea ax, variable1
- call print_signed_8byte
-
- jmp outer_loop
-
- ; - - - - - PUT CODE ABOVE THIS LINE
-
- The calls to get_signed_8byte are followed by print_signed_8byte.
- This is so you can see what you have actually typed in. It will
- be neat and with commas, so it will be much easier to read.
- Everything else is the same as before.
-
- As an aside, let's talk about commas. Though we can get along
- without commas if we have a 4 digit number, There is no reason to
- do without them when using larger numbers. I find printer output
- that has 15 digit floating point numbers without commas not only
- hard to read but also silly. It's not that the computer is
- incapable of putting in commas, it's that the prople who wrote
- the programs couldn't be bothered with doing 2 hours of work to
- save the users lots and lots of aggrivation. Therefore, for all
- large number input (that is, larger than 1 word) you can use
- commas. They don't even have to be in the right place, the
- computer ignores them. All the following are the same number:
-
- 723469746
- 723,4,69746
- 72,3,46974,6
-
-
-
-
- The PC Assembler Tutor 126
- ______________________
-
- 7,2,3,4,6,9,7,4,6
- 723,469,746
-
- The program strips all commas out of the line and then looks at
- the input. All large output has the commas in the right spot. In
- order to enlist you in the crusade to stamp out unreadable input
- and output, the summary at the end of this chapter has the C code
- necessary for stripping commas. This is my contribution to world
- culture.
-
- If you have played with the signed addition program, all we need
- to do to make it unsigned is to change all the get_signed_8byte
- calls to get_unsigned_8byte calls. Change the print calls to
- unsigned also. Run the program.
-
- Now, let's try subtraction. How much code do we have to alter to
- make it a subtraction program? The answer is - one word. Just
- change the ADC (add with carry) to SBB (subtract with borrow).
- SBB learns from the CF flag whether the last subtraction had to
- borrow, and tells the next subtraction via the CF flag whether it
- has had to borrow. Change it, and try it out. (Yes, really do it,
- don't just pretend that you are going to do it).
-
- Why does this program work with exactly 8 bytes? Because we tell
- the loop via CX that it is 4 words long. If we put 2 in CX, the
- loop will think that the number is 2 words (4 bytes) long, and if
- we put 17 in CX, the loop will think that the number is 17 words
- (34 bytes) long. In fact, this little snippet of code can do
- either signed or unsigned addition or subtraction of any number
- of words simply by altering one number and one word of code.
-
- The code as it stands has only one shortfall. Remember from our
- earlier subtraction that we might want to have an INTO (interrupt
- on overflow) instruction after signed addition and subtraction.
- It needs to be after the last addition (or subtraction). All we
- need to do is put it after the POPF just below the loop. At this
- point the flags show the condition right after the last addition
- or subtraction:
-
- loop long_add_loop
-
- popf ; pop the flags off the stack
- into ; interrupt if overflow is set
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Chapter 12 - Multiple Word Arithmetic I 127
- _______________________________________
-
- SUMMARY
-
-
- ADC (add with carry) is used for multiple word arithmetic. It
- adds two numbers along with plus the value in CF, the carry flag
- (1 if the flag is set and 0 if the flag is cleared). CLC (clear
- the carry flag) should be used to clear CF before the first
- addition. After the addition, the flags register should be pushed
- in order to store CF unless it is certain that CF will not be
- effected by the rest of the loop.
-
- popf
- adc [di], ax
- pushf
-
-
-
- SBB (subtract with borrow) is used for multiple word arithmetic.
- It subtracts one number from the other and subtracts the value in
- CF to account for any borrows from the right. CLC (clear the
- carry flag) should be used to clear CF before the first
- subtraction. After the subtraction, the flags register should be
- pushed in order to store CF unless it is certain that CF will not
- be effected by the rest of the loop.
-
- popf
- sbb [di], ax
- pushf
-
- These operations have the typical 5 possibilities:
-
- 1) register, register
- 2) register, memory
- 3) memory, register
- 4) memory, constant
- 5) register, constant
-
-
-
- You may manually control the value in CF with STC and CLC. STC
- (set the carry flag) sets the value to 1, while CLC (clear the
- carry flag) sets the value to 0. These are used for initial
- settings of multiple word operations.
-
-
- In order to store the values in the flags register you use PUSHF
- (push the flags) until you need them again. At that time you can
- get them back with POPF (pop the flags).
-
-
-
-
-
-
-
-
-
-
-
-
-
- The PC Assembler Tutor 128
- ______________________
-
- STRIPPING COMMAS IN C
-
- In order to get rid of commas, you need some discipline in what
- kind of data entry you have. Specifically, you can't enter large
- numbers on the same line as text strings because text strings are
- likely to have commas that should be kept. This is hardly a major
- restriction. You can have as many numbers as you want on the same
- line since in C you must have whitespace between pieces of data.
-
- We will take all commas out of the line. You can retrofit most
- old programs with almost no change.
-
- The only time that you need this capability is if you are getting
- something from the keyboard, and what you probably have in the
- program is:
-
- scanf ( "format string", variables );
-
- The method is (1) import a text string as a single string, (2)
- strip the commas, and (3) use sscanf instead of scanf.
-
- char buffer[80] ;
-
- fgets (buffer, 80, stdin) ;
- strip_commas (buffer);
- sscanf (buffer, "format string", variables) ;
-
- Both "format string" and the variables remain unchanged when you
- switch from scanf to sscanf. You might want to check for EOF with
- fgets.
-
- Heres the program:
-
- strip_commas (buffer)
- char *buffer ;
- {
- char *ptr1, *ptr2 ;
-
- ptr1 = ptr2 = buffer ;
- while (1)
- {
- if ( *ptr2 == ',') /* skip commas */
- {
- ptr2++ ;
- continue ;
- }
- /*move, increment, and check for 0 */
- if (!(*ptr1++ = *ptr2++)) /* this is '=', not '==' */
- break ;
- }
- return ;
- }
-
-