CIS-77 Home http://www.c-jump.com/CIS77/CIS77syllabus.htm
The purpose of this assignment is to experiment with integer multiplication and division instructions.
You can continue using Microsoft VC++ project created in the earlier MASM labs.
For related topics, integer formats and data copy instructions, see Data Types and Memory Allocation presentation ( slide version ).
Multiplication is more complicated than the addition and subtraction operations for two reasons:
First, multiplication produces double-length results:
multiplication of two 8-bit numbers requires 16 bits to store the result
multiplication of two 16-bit numbers requires 32 bits to store the result
multiplication of two 32-bit numbers requires 64 bits for the result.
Second, multiplication of signed numbers should be treated differently from that of unsigned numbers, because signed result depends on the sign of the operands.
The Intel instruction set lets you multiply and divide 8-, 16-, and 32-bit integers using
MUL unsigned multiply
DIV unsigned divide
and
IMUL signed multiply
IDIV signed divide
instructions.
The MUL (unsigned multiply) instruction multiplies an 8-, 16-, or 32-bit operand by either AL, AX, or EAX.
The instruction formats are:
MUL reg8/mem8 MUL reg16/mem16 MUL reg32/mem32
The single operand is the multiplier.
Note that immediate operand is not allowed:
mul 10 ; *** invalid instruction
The following table shows the default multiplicand and product, depending on the size of the multiplier:
multiplicand × multiplier = product
The MUL instruction sets the Carry and Overflow flags if the upper half of the product is not equal to zero.
The Carry flag CF is normally used for unsigned arithmetic.
For example,
when AX is multiplied by a l6-bit operand, the product is stored in DX:AX.
If DX is not equal to zero, the Carry flag is set.
To multiply
0l00h × 2000h
we could use the following fragment:
.DATA one WORD 2000h two WORD 0100h .CODE mov ax, [one] mul [two] ; CF = 1, producing 0020h:0000h in DX:AX
The IMUL (signed multiply) instruction performs signed integer multiplication.
It has the same syntax and uses the same operands as the MUL instruction.
What is different is that it preserves the sign of the product.
IMUL sets the Carry and Overflow flags if the high-order product is not just a sign extension of the low-order product.
The Overflow flag OF is normally used for interpreting signed arithmetic.
The following fragment computes 8-bit signed multiplication (48 × 4):
mov al, 48 mov bl, 4 imul bl ; AX = 00C0h (decimal +192), OF = 1
Because AH is not a sign extension of AL, the Overflow flag is set to 1.
The following instructions perform 8-bit signed multiplication of (-4 × 4), producing -16 in AX:
mov al, -4 mov bl, 4 imul bl ; AX = FFF0h, OF = 0
AH is a sign extension of AL, meaning that the signed result fits within AL, therefore the Overflow flag is cleared.
The following instructions perform 16-bit signed multiplication (48 × 4), producing +192 in DX:AX:
mov ax, 48 mov bx, 4 imul bx ; DX:AX = 0000h:00C0h , OF = 0
Here, DX is a sign extension of AX, so the Overflow flag is cleared.
32-bit signed multiplication example:
.data signed_val SDWORD ? ; Data type is signed doubleword .code mov eax, +4823424 mov ebx, -423 imul ebx ; EDX:EAX = FFFFFFFFh:86635D80h, OF = 0 move signed_val, eax ; store the result
EDX is a sign extension of EAX, so the Overflow flag is cleared.
The DIV (unsigned divide) instruction performs 8-bit, 16-bit, and 32-bit division on unsigned integers.
The instruction formats are:
DIV reg8/mem8 DIV reg16/mem16 DIV reg32/mem32
The single operand is the divisor:
dividend / divisor = quotient ( + remainder )
Quotient is the result of a division. For example, when dividing (7 / 4),
the quotient is 1,
the remainder after integer division is 3,
7 is called the dividend, and
4 is the divisor.
The following fragment performs 8-bit unsigned division (83h/2), producing a quotient of 41h and a remainder of 1:
mov ax, 83h ; dividend mov bl, 2 ; divisor div bl ; quotient AL = 41h, remainder AH = 01h
The following instructions perform 16-bit unsigned division (8003h/100h), producing a quotient of 80h and a remainder of 3.
Note that DX contains the high part of the dividend, so it must be cleared before the DIV instruction executes:
mov dx, 0 ; clear high part of the dividend mov ax, 8003h ; set low part of the dividend mov cx, 100h ; set divisor div cx ; quotient AX = 0080h, remainder DX = 0003h
The following fragment performs 32-bit unsigned division using a memory operand as the divisor:
.data dividend QWORD 0000000800300020h divisor DWORD 00000100h .code mov edx, DWORD PTR [dividend + 4] ; high doub1eword mov eax, DWORD PTR [dividend] ; low doubleword div divisor ; quotient EAX = 08003000h, remainder EDX = 00000020h
Before discussing signed integer division, we need to look at three instructions that perform integer sign-extension.
The CBW (convert byte to word) instruction extend s the sign bit of AL into the AH register. This preserves the number 's sign:
.DATA byte_val SBYTE -101 .CODE mov al, byte_val ; AL = 9Bh cbw ; AX = FF9Bh
Note that both 9Bh and FF9Bh both equal decimal -101, the only difference is the storage size.
The CWD (convert word to doubleword) instruction extends the sign bit of AX into the DX register:
.DATA word_val SWORD -101 ; FF9Bh .CODE mov ax, word_val ; AX = FF9Bh cwd ; DX:AX = FFFFh:FF9Bh
The CDQ (convert doubleword to quadword) instruction extends the sign bit of EAX into the EDX register:
.data dword_val SDWORD -101 .code mov eax, dword_val ; EAX = FFFFFF9Bh cdq ; EDX:EAX = FFFFFFFh:FFFFFFF9Bh
The IDIV (signed divide) instruction performs signed integer division, using the same operands as the DIV instruction.
For both DIV and IDIV, all of the arithmetic status flags are undefined after the operation.
When doing 8-bit division, you must sign-extend the dividend into AH before using IDIV.
For example, (-48/5):
.DATA byte_val SBYTE -48 .CODE mov al, byte_val ; dividend cbw ; sign-extend AL into AH mov bl, 5 ; divisor idiv bl ; quotient AL = - 9 , remainder AH = -3
Similarly, l6-bit division requires that AX be sign-extended into DX.
For example, (-5000/256):
.DATA word_val SWORD -5000 .CODE mov ax, word_val ; dividend, low part cwd ; sign-extend AX into DX mov bx, 256 ; divisor idiv bx ; quotient AX = -19, remainder DX = -136
Similarly, 32-bit division requires that EAX be sign-extended into EDX. The next example
For example, (-50000/256):
.DATA dword_val SDWORD -50000 .CODE mov eax, dword_val ; dividend, low cdq ; sign-extend EAX into EDX mov ebx, 256 ; divisor idiv ebx ; quotient EAX = -195, remainder EDX = -80
If a division operand produces a quotient that is too large to fit into the destination operand, divide overflow exception results.
This causes a CPU interrupt, and the current program halts.
For example, the following instructions generate a divide overflow because the quotient 100h will not fit into the AL register:
mov ax, 1000h mov bl, 10h div bl ; AL cannot hold 100h, program crashes
One thing to do to reduce the probability of a divide overflow condition is to use a 32-bit divisor:
mov eax, 1000h cdq mov ebx, 10h div ebx ; EAX = 100h
Extended precision addition and subtraction is the adding and subtracting of numbers having an almost unlimited size.
Writing a C program that adds two 128-bit integers is not an easy task!
However, in assembly language, the
ADC (add with carry), and
SBB (subtract with borrow)
instructions are well-suited to this type of problem.
The ADC (add with carry) instruction adds both a source operand and the contents of the Carry flag to a destination operand:
ADC op1, op2 ; op1 += op2, op1 += CF
The instruction formats are the same as for the ADD instruction:
ADC reg, reg ADC mem, reg ADC reg, mem ADC mem, imm ADC reg, imm
The ADC instruction does not distinguish between signed or unsigned operands.
Instead, the processor evaluates the result for both data types and sets
OF flag to indicate a carry out from the signed result.
CF flag to indicate a carry out from the unsigned result.
The sign flag SF indicates the sign of the signed result.
The ADC instruction is usually executed as part of a chained multibyte or multiword addition, in which an ADD or ADC instruction is followed by another ADC instruction.
The following fragment adds two 8-bit integers (FFh + FFh), producing a 16-bit sum in DL:AL, which is 01h:FEh.
mov dl, 0 mov al, 0FFh add al, 0FFh ; AL = FEh, CF = 1 adc dl, 0 ; DL += CF, add "leftover" carry
Similarly, the following instructions add two 32-bit integers (FFFFFFFFh + FFFFFFFFh).
The result is a 64-bit sum in EDX:EAX, 0000000lh:FFFFFFFEh,
mov edx, 0 mov eax, 0FFFFFFFFh add eax, 0FFFFFFFFh adc edx, 0 ; EDX += CF, add "leftover" carry
The following instructions add two 64-bit numbers received in EBX:EAX and EDX:ECX:
The result is returned in EBX:EAX.
Overflow/underflow conditions are indicated by the Carry flag.
add eax, ecx ; add low parts EAX += ECX, set CF adc ebx, edx ; add high parts EBX += EDX, EBX += CF ; The result is in EBX:EAX ; NOTE: check CF or OF for overflow (*)
The 64-bit subtraction is also simple and similar to the 64-bit addition:
sub eax, ecx ; subtract low parts EAX -= ECX, set CF (borrow) sbb ebx, edx ; subtract high parts EBX -= EDX, EBX -= CF ; The result is in EBX:EAX ; NOTE: check CF or OF for overflow (*)
_____________
(*) The Carry flag CF is normally used for unsigned arithmetic.
The Overflow flag OF is normally used for signed arithmetic.
After subtraction, the carry flag CF = 1 indicates a need for a borrow.
The SBB (subtract with borrow) instruction subtracts both a source operand and the value of the Carry flag CF from a destination operand:
SBB op1, op2 ; op1 -= op2, op1 -= CF
The possible operands are the same as for the ADC instruction.
The following fragment of code performs 64-bit subtraction:
mov edx, 1 ; upper half mov eax, 0 ; lower half sub eax, 1 ; subtract 1 from the lower half, set CF. sbb edx, 0 ; subtract carry CF from the upper half.
The example logic:
Sets EDX:EAX to 00000001h:00000000h.
Subtracts 1 from the value in EDX:EAX.
The lower 32 bits are subtracted first, setting the Carry flag CF.
The upper 32 bits are subtracted next, including the Carry flag.
When an immediate value is used in SBB as an operand, it is sign-extended to the length of the destination operand.
The SBB instruction does not distinguish between signed or unsigned operands.
Instead, the processor evaluates the result for both data types and sets the
OF flag to indicate a borrow in the signed result.
CF flag to indicate a borrow in the unsigned result.
The SF flag indicates the sign of the signed result.
The SBB instruction is usually executed as part of a chained multibyte or multiword subtraction, in which a SUB or SBB instruction is followed by another SBB instruction.
Given:
.DATA op1 QWORD 0A2B2A40675981234h ; first 64-bit operand for addition op2 QWORD 08010870001234502h ; second 64-bit operand for addition sum DWORD 3 dup(?) ; 96-bit sum = ????????????????????????h op3 DWORD 3 dup(2) ; 96-bit oper to sub 20000000200000002h ; Result sum = ????????????????????????h
Write an assembly program to compute
(a) the sum of two 64-bit integers. Store the result as 96-bit sum:
sum = (op1 + op1)
(b) Subtract 96-bit op3 integer from the sum. Store the difference back in the sum memory:
sum = (sum - op3)
Every CPU instruction in your program must have a brief comment!
The penalty for not following this rule is 15 pts deduction...
Enter computation results by replacing corresponding question marks in the program comments.
Things to consider:
The largest operand size on 32-bit CPU is a DWORD, therefore, computation must be implemented as a series of chained additions and subtractions of doublewords, starting from low to high.
Recall how a QWORD is stored in memory . Since program is running on a 32-bit Intel-based CPU, all multibyte values are stored in a reversed sequence order. Therefore, to load QWORD into EBX:EAX, we need to discriminate between high and low parts as follows:
mov ebx, DWORD PTR [op1 + 4] ; high-part of QWORD mov eax, DWORD PTR [op1 + 0] ; low-part of QWORD
Use the same approach to manipulate 96-bit values, such as sum and op3. Again, all bytes must be stored in a reversed sequence order.
Optional (25 xtra pts.) Improve the program by having it display both sum and diff in hexadecimal format on the screen.
What to submit:
Submit only your ASM source (not the EXE or project.)