NAME LAB4C PROG SEGMENT PARA PUBLIC; 'DATA' ASSUME CS:PROG,DS:DATA ;.287 ;************************************************************************* ; Data structure definitions. ; FPNUM STRUC u3 dw ? ; low word of fraction u2 dw ? ; low middle word of fraction u1 dw ? ; high middle word of fraction u0 dw ? ; high word of fraction sexp dw ? ; sign bit and biased exponent FPNUM ENDS FPRCD RECORD sign:1,exp:15 ; Define format of sexp word BIAS EQU 03FFFH ; Bias for 80-bit IEEE FP format. BIAS32 EQU 07FH ; Bias for 32-bit IEEE FP format. BIAS64 EQU 03FFH ; Bias for 64-bit IEEE FP format. ;************************************************************************* ; DISPLAY: This procedure displays the hex data values in NUM3. ; First each is converted to ASCII, then DOS is called to ; print them. ; DISPLAY PROC NEAR ; Save bits 15-0 high byte for display. MOV AL,BYTE PTR NUM3+1 ; Get high byte high nibble. CALL HIGHNIB MOV BYTE PTR STR+16d,AL ; Save high digit. MOV AL,BYTE PTR NUM3+1 ; Get high byte low nibble. CALL LOWNIB MOV BYTE PTR STR+17d,AL ; Save low digit. ; Save bits 15-0 low byte for display. MOV AL,BYTE PTR NUM3 ; Get low byte high nibble. CALL HIGHNIB MOV BYTE PTR STR+18d,AL ; Save high digit. MOV AL,BYTE PTR NUM3 ; Get high byte low nibble. CALL LOWNIB MOV BYTE PTR STR+19d,AL ; Save low digit. ; Save bits 31-16 high byte for display. MOV AL,BYTE PTR NUM3+3 ; Get high byte high nibble. CALL HIGHNIB MOV BYTE PTR STR+12d,AL ; Save high digit. MOV AL,BYTE PTR NUM3+3 ; Get high byte low nibble. CALL LOWNIB MOV BYTE PTR STR+13d,AL ; Save low digit. ; Save bits 31-16 low byte for display. MOV AL,BYTE PTR NUM3+2 ; Get low byte high nibble. CALL HIGHNIB MOV BYTE PTR STR+14d,AL ; Save high digit. MOV AL,BYTE PTR NUM3+2 ; Get high byte low nibble. CALL LOWNIB MOV BYTE PTR STR+15d,AL ; Save low digit. ; Save bits 47-32 high byte for display. MOV AL,BYTE PTR NUM3+5 ; Get high byte high nibble. CALL HIGHNIB MOV BYTE PTR STR+8,AL ; Save high digit. MOV AL,BYTE PTR NUM3+5 ; Get high byte low nibble. CALL LOWNIB MOV BYTE PTR STR+9,AL ; Save low digit. ; Save bits 47-32 low byte for display. MOV AL,BYTE PTR NUM3+4 ; Get low byte high nibble. CALL HIGHNIB MOV BYTE PTR STR+10d,AL ; Save high digit. MOV AL,BYTE PTR NUM3+4 ; Get high byte low nibble. CALL LOWNIB MOV BYTE PTR STR+11d,AL ; Save low digit. ; Save bits 63-48 high byte for display. MOV AL,BYTE PTR NUM3+7 ; Get high byte high nibble. CALL HIGHNIB MOV BYTE PTR STR+4d,AL ; Save high digit. MOV AL,BYTE PTR NUM3+7 ; Get high byte low nibble. CALL LOWNIB MOV BYTE PTR STR+5d,AL ; Save low digit. ; Save bits 63-48 low byte for display. MOV AL,BYTE PTR NUM3+6 ; Get low byte high nibble. CALL HIGHNIB MOV BYTE PTR STR+6d,AL ; Save high digit. MOV AL,BYTE PTR NUM3+6 ; Get high byte low nibble. CALL LOWNIB MOV BYTE PTR STR+7d,AL ; Save low digit. ; Save exponent high byte for display. MOV AL,BYTE PTR NUM3+9 ; Get high byte high nibble. CALL HIGHNIB MOV BYTE PTR STR+0d,AL ; Save high digit. MOV AL,BYTE PTR NUM3+9 ; Get high byte low nibble. CALL LOWNIB MOV BYTE PTR STR+1d,AL ; Save low digit. ; Save exponent low byte for display. MOV AL,BYTE PTR NUM3+8 ; Get low byte high nibble. CALL HIGHNIB MOV BYTE PTR STR+2d,AL ; Save high digit. MOV AL,BYTE PTR NUM3+8 ; Get high byte low nibble. CALL LOWNIB MOV BYTE PTR STR+3d,AL ; Save low digit. ; Now display the strings. MOV DX,OFFSET STR ; Load string location. MOV AH,09H ; DOS String Display function. INT 21H RET DISPLAY ENDP ;************************************************************************* ; HIGHNIB: Isolates the high nibble digit given in AL to the low nibble ; and calls CONVERT to get the ASCII value. The result is ; returned in AL. ; HIGHNIB PROC NEAR SHR AL,1 ; Put high nibble in low nibble, SHR AL,1 ; discarding low nibble. SHR AL,1 SHR AL,1 CALL CONVERT ; Returns ASCII value in AL. RET HIGHNIB ENDP ;************************************************************************* ; LOWNIB: Isolates the low nibble digit given in AL and calls CONVERT to get ; the ASCII value. The result is returned in AL. ; LOWNIB PROC NEAR AND AL,0FH ; Mask out high nibble. CALL CONVERT ; Convert to ASCII. RET LOWNIB ENDP ;************************************************************************* ; CONVERT: Converts the digit given in AL from the range 00H to 0FH ; into the corresponding ASCII character '0' to 'F'. ; CONVERT PROC NEAR CMP AL,0AH ; Is it A or above? JGE ALPHA DIGIT: ADD AL,'0' ; Convert to ASCII digit. JMP DONE ALPHA: SUB AL,0AH ; Normalize to 0. ADD AL,'A' ; Convert to ASCII character. DONE: RET CONVERT ENDP ;************************************************************************* ; Floating-point multiplication routine, using 80-bit IEEE format, ; where bits 0 to 63 represent the fraction, bits 64 to 78 represent ; the exponent, and bit 79 represents the sign. ; ; This implementation is not a complete robust floating point multiplication ; routine. It does not account for overflow, does not maintain 64 bit ; significance (worst case 62 bits), and the low bit is not rounded ; properly (the result bits of lower significance than 64 places are ; completely ignored in setting the lowest significant bit). ; ; The fraction multiplication algorithm goes as follows: ; ; a = [a3] [a2] [a1] [a0] (read [xx] as a word, or 16-bits) ; b = [b3] [b2] [b1] [b0] ; c = a * b ; ; To multiply a and b, we take the following steps: ; [a3] [a2] [a1] [a0] ; * [b3] [b2] [b1] [b0] ; --------------------------------------------- ; [a0 * b0] ; [a1 * b0] ; [a2 * b0] ; -> [a3 * b0] ; [a0 * b1] ; [a1 * b1] ; -> [a2 * b1] ; -> [a3 * b1] ; [a0 * b2] ; -> [a1 * b2] ; -> [a2 * b2] ; -> [a3 * b2] ; -> [a0 * b3] ; -> [a1 * b3] ; -> [a2 * b3] ; -> [a3 * b3] ; --------------------------------------- ; [c7] [c6] [c5] [c4] [c3] [c2] [c1] [c0] ; ; Only the calculations marked '->' are included in this implementation ; since only [c7] to [c4] are significant (i.e. 64 bits). ; ; Each [ci] is the sum of the resulting words in the column above it. ; For example, [c0] = the low word of [a0 * b0]. All carries are ; propagated left as required. ; FPMULT PROC NEAR push ax push bx push cx push dx push si push di push bp ;*************************************************************** ; Initialize. ;*************************************************************** mov EFLAG,0 ; Clear the error flag. lea si,NUM1 ; Use si, di, and bp as lea di,NUM2 ; pointers. lea bp,NUM3 xor bx,bx ; Use BX and CX to accumulate xor cx,cx ; the results. mov CARRY,0 ; Zero out the carry accumulator. ;*************************************************************** ; Check for normalized format. ;*************************************************************** mov ax,[si].u0 ; Check NUM1 and ax,MASK sign cmp ax,0 jne E1 ; NUM1 is not normalized. jmp ERROR E1: mov ax,[di].u0 ; Check NUM2 and ax,MASK sign cmp ax,0 jne E2 ; NUM2 is not normalized. jmp ERROR E2: ;*************************************************************** ; Multiply the fractional parts. ;*************************************************************** ; Calculate the e^3 results. N0: mov ax,[si].u0 ; e^3 * u0 * v3 mov dx,[di].u3 mul dx ; DX:AX = AX * DX add bx,dx ; Use high word of result only. jnc N1 ; If a carry occurred, catch it add cx,1 ; in the low middle word. N1: mov ax,[si].u1 ; e^3 * u1 * v2 mov dx,[di].u2 mul dx ; DX:AX = AX * DX add bx,dx ; Use high word of result only. jnc N2 ; If a carry occurred, catch it add cx,1 ; in the low middle word. N2: mov ax,[si].u2 ; e^3 * u2 * v1 mov dx,[di].u1 mul dx ; DX:AX = AX * DX add bx,dx ; Use high word of result only. jnc N3 ; If a carry occurred, catch it add cx,1 ; in the low middle word. N3: mov ax,[si].u3 ; e^3 * u3 * v0 mov dx,[di].u0 mul dx ; DX:AX = AX * DX add bx,dx ; Use high word of result only. jnc N4 ; If a carry occurred, catch it add cx,1 ; in the low middle word. ; Calculate the e^2 results. N4: mov ax,[si].u0 ; e^2 * u0 * v2 mov dx,[di].u2 mul dx ; DX:AX = AX * DX add bx,ax ; Use low word of result. jnc N4A ; If carry occurred, catch it add cx,1 ; in the low middle word. N4A: add cx,dx ; Use high word of result. jnc N5 ; If carry occurred, catch it add CARRY,1 ; for later addition. N5: mov ax,[si].u1 ; e^2 * u1 * v1 mov dx,[di].u1 mul dx ; DX:AX = AX * DX add bx,ax ; Use low word of result. jnc N5A ; If carry occurred, catch it add cx,1 ; in the low middle word. N5A: add cx,dx ; Use high word of result. jnc N6 ; If carry occurred, catch it add CARRY,1 ; for later addition. N6: mov ax,[si].u2 ; e^2 * u2 * v0 mov dx,[di].u0 mul dx ; DX:AX = AX * DX add bx,ax ; Use low word of result. jnc N6A ; If carry occurred, catch it add cx,1 ; in the low middle word. N6A: add cx,dx ; Use high word of result. jnc N7 ; If carry occurred, catch it add CARRY,1 ; for later addition. ; Can now save the u3 result and use BX for the u1 result. N7: mov ds:[bp].u3,bx ; Save u3 result. xor bx,bx ; BX now used for u1 result. add bx,CARRY ; Add in the carry. mov CARRY,0 ; Zero carry ; Calculate the e^1 results. N8: mov ax,[si].u0 ; e^1 * u0 * v1 mov dx,[di].u1 mul dx ; DX:AX = AX * DX add cx,ax ; Use the low word result. jnc N8A ; If carry occurred, catch it add bx,1 ; in the high middle word result. N8A: add bx,dx ; Use the high word result. jnc N9 ; If carry occurred, catch it add CARRY,1 ; for later addition. N9: mov ax,[si].u1 ; e^1 * u1 * v0 mov dx,[di].u0 mul dx ; DX:AX = AX * DX add cx,ax ; Use the low word result. jnc N9A ; If carry occurred, catch it add bx,1 ; in the high middle word result. N9A: add bx,dx ; Use the high word result. jnc N10 ; If carry occurred, catch it add CARRY,1 ; for later addition. N10: mov ds:[bp].u2,cx ; Save the u2 result. xor cx,cx ; CX now used for u0 result. add cx,CARRY ; Add in the carry. mov CARRY,0 ; Zero carry ; Calculate the e^0 result. N11: mov ax,[si].u0 ; e^0 * u0 * v0 mov dx,[di].u0 mul dx ; DX:AX = AX * DX add bx,ax ; Use the low word result. jnc N11A ; If carry occurred, catch it add cx,1 ; in high word result. N11A: add cx,dx ; Use the high word result. ; **** CARRY IGNORED **** ; Save the u1 and u0 results. mov ds:[bp].u1,bx mov ds:[bp].u0,cx ;*************************************************************** ; Calculate the resulting exponent. ;*************************************************************** mov ax,NUM1.sexp ; Isolate NUM1 exponent and ax,MASK exp mov bx,NUM2.sexp ; Isolate NUM2 exponent and bx,MASK exp sub ax,BIAS ; Remove bias sub bx,BIAS add ax,bx ; Calculate new exponent. mov NUM3.sexp,ax ; Save exponent back. ; Since we treated NUM1 and NUM2 as fractions, which essentially ; divided them by 2 (because the first bit is an integer bit), ; we must multiply the result by 4 (shift left twice). However, ; we also have to normalize the result to get an integer bit of 1. mov si,0 ; SI used as shift counter. mov ax,NUM3.u0 mov bx,NUM3.u1 mov cx,NUM3.u2 mov dx,NUM3.u3 AGAIN: test ax,MASK sign ; test for high bit = 1 jnz FINISH inc si ; Increment shift counter. shl ax,1 ; Start shift (no carry can occur). shl bx,1 ; Shift second word. jnc NC1 ; Check for register to reg. carry or ax,1 ; Set low bit of u0 word. NC1: shl cx,1 ; Shift third word. jnc NC2 ; Check for register to reg. carry or bx,1 ; Set low bit of u1 word. NC2: shl dx,1 ; Shift fourth word. jnc NC3 ; Check for register to reg. carry or cx,1 ; Set low bit of u2 word. NC3: jmp AGAIN ; Do another shift if necessary. ; Save the shifted result. FINISH: mov NUM3.u3,dx mov NUM3.u2,cx mov NUM3.u1,bx mov NUM3.u0,ax ; Adjust the exponent as required. First adjust the exponent ; modifier to correspond with treating the multiplicands as ; fractions (no integer bit) => 2 shifts, and by another 1 shift ; because the high bit is already the integer bit. mov cx,si ; Adjust shift by 2 because these sub cx,1 ; were necessary, -1 due to int. bit. mov ax,NUM3.sexp and ax,NOT MASK sign sub ax,cx ; Reduce exponent by amount shifted. ; Check for exponent over/underflow. cmp ax,BIAS ; Check for overflow. jg OERROR cmp ax,-BIAS ; Check for underflow. jl UERROR cmp ax,BIAS64 ; Check for long real overflow. jg OERROR cmp ax,-BIAS64 ; Check for long real underflow. jl UERROR cmp ax,BIAS32 ; Check for long real overflow. jg OERROR cmp ax,-BIAS32 ; Check for long real underflow. jl UERROR add ax,BIAS ; Re-add bias. mov NUM3.sexp,ax ; Save adjusted exponent. ;*************************************************************** ; Fix up the sign. ;*************************************************************** mov cx,NUM3.sexp ; Get the result. mov ax,NUM1.sexp ; Isolate NUM1 sign. and ax,MASK sign mov bx,NUM2.sexp ; Isolate NUM2 sign. and bx,MASK sign cmp ax,bx ; Compare the signs. jnz DIFF SAME: and cx,NOT MASK sign ; Set positive sign. jmp GONE DIFF: or cx,MASK sign ; Set negative sign. mov NUM3.sexp,cx ; Save the result. GONE: pop bp pop di pop si pop dx pop cx pop bx pop ax RET ;*************************************************************** ; Handle the errors. Note these are just skeleton error routines. ;*************************************************************** ERROR: mov EFLAG,1 jmp GONE OERROR: mov EFLAG,2 jmp GONE UERROR: mov EFLAG,3 jmp GONE FPMULT ENDP ;************************************************************************* ; START OF PROGRAM. ; beg: MOV AX,DATA ; Set up DS register as ASSUMEd. MOV DS,AX ; Do floating point operations. ; Indicate 8087 operations starting. MOV DX,OFFSET START87 ; Load string location. MOV AH,09H ; DOS String Display function. INT 21H ; 8087 multiplications. FINIT ; Initialize the 8087. FLD TBYTE PTR NUM2 ; Load ST with NUM2 MOV AX,COUNTA OUT87: MOV CX,COUNT LOOP87: FLD TBYTE PTR NUM1 ; Load ST with NUM1 (NUM2 -> ST(1)) FMUL ST,ST(1) ; NUM1 * NUM2 -> ST FSTP TBYTE PTR NUM3 ; NUM1 * NUM2 -> NUM3, NUM2 -> ST LOOP LOOP87 DEC AX JNZ OUT87 ; Indicate 8087 operations finished. MOV DX,OFFSET I1STR ; Load string location. MOV AH,09H ; DOS String Display function. INT 21H CALL DISPLAY ; Display results. ; Indicate emulation operations starting. MOV DX,OFFSET STARTEM ; Load string location. MOV AH,09H ; DOS String Display function. INT 21H ; Emulation operations. MOV AX,COUNTA OUTEM: MOV CX,COUNT FPLOOP: CALL FPMULT LOOP FPLOOP DEC AX JNZ OUTEM ; Indicate emulation operations finished. MOV DX,OFFSET I2STR ; Load string location. MOV AH,09H ; DOS String Display function. INT 21H CALL DISPLAY ; Display results. ; Exit to DOS. MOV AH,4CH INT 21H PROG ends ;************************************************************************* ; START OF DATA. ; ; Printable ASCII strings. DATA SEGMENT I1STR DB "The result of 8087 operations is: ",13,10,"$" I2STR DB "The result of emulations operations is : ",13,10,"$" STR DB 20d DUP(?),13,10,"$" DATA ENDS ; 80287 Status and control DATA SEGMENT ENV DW 47 DUP(?) DUMMY4 DW 16 DUP(0FFEEH) ; Just so we can see where ENV ends. DATA ENDS ; FP Emulation storage space DATA SEGMENT NUM1 FPNUM<00000h,00000h,00000h,0C000h,0C001h>; 80-bit IEEE FP format NUM2 FPNUM<00000h,00000h,00000h,0B220h,04006h> NUM3 FPNUM<> CARRY DW ? ; For carry accumulation. COUNT DW 00f00H ; Number of times to repeat. COUNTA DW 000f0H ; Number of times to repeat. EFLAG DB 00H ; Error flag. DATA ENDS ; Printable ASCII strings. DATA SEGMENT START87 DB "Starting 8087 multiplications.",13,10,"$" DONE87 DB "Finished 8087 multiplications.",13,10,"$" STARTEM DB "Starting emulation multiplications",13,10,"$" DONEEM DB "Finished emulation multiplications",13,10,"$" DATA ENDS END BEG