Prints a real number onto the display output.
This is a disassembly of the Pascal Program:
program main;
var A: Real;
begin
A := 0.3141592654E01;
WriteLn(A);
end.
end program;
It was compiled with x87 emulation and x87 code disabled using:
TPC.EXE -GS -GP -GD -$D+ -$E- -$N- MAIN
A := 0.3141592654E01;
Translates to the following:
CODE000F: MOV WORD PTR [A.Low],9282
CODE0015: MOV WORD PTR [A.Mid],DAA2
CODE001B: MOV WORD PTR [A.High],490F
The Real variable A occupies 6 bytes. 490FDAA29282h is the 48-bit representation of 0.3141592654E01 (PI ~ 9 digits after the decimal) (see: Real Type).
CODE0021: MOV DI,Output
CODE0024: PUSH DS
CODE0025: PUSH DI
CODE0026: PUSH [A.High]
CODE002A: PUSH [A.Mid]
CODE002E: PUSH [A.Low]
CODE0032: MOV AX,0011
CODE0035: PUSH AX
CODE0036: MOV AX,FFFF
CODE0039: PUSH AX
These parameters are pased onto the stack for the successive calls to the system library. Notice that the higher bytes of the number (A) are passed first before the lower bytes. AX = 0011h/17 characters are used to represent the number (1 for the sign, 4 for the exponent, and 12 for the digits including the decimal point). Another parameter AX = FFFFh is pushed to the stack.
Index | Contents | Description |
---|---|---|
SP | FFFF | Constant Parameter |
SP+02 | 0011 | Constant Parameter |
SP+04 | 9282 | Low Word of A |
SP+06 | DAA2 | Mid Word of A |
SP+08 | 490F | High Word of A |
SP+0A | ???? | DI Output (Offset) |
SP+0C | ???? | DS Output (Segment) |
Note that the byte order is actually stored reversed (least significant byte LSB first, followed by the most significant byte MSB) in memory.
CODE003A: CALL SYS:0693
CODE003F: CALL SYS:05DD
CODE0044: CALL SYS:0291
These three calls are used to print the Real number. We shall walkthrough each of these calls.
SYS0291: CMP WORD PTR [InOutRes],+00
SYS0296: JNZ 0299
SYS0298: RETF
SYS0299: MOV AX,[InOutRes]
SYS029C: JMP 010F
SYS:0291 checks whether the printing of the output generated any error. If so, it prints a Runtime error then exits on the jump to SYS:010F.
SYS04F7: CMP WORD PTR [InOutRes],+00
SYS04FC: JNZ 053E
Check if the last operation generated any error. Exit immediately if there was an error.
SYS04FE: ES:
SYS04FF: CMP WORD PTR [BX+02],fmOutput
SYS0504: JNZ 053F
Verify that Output was opened for output. Generate an error code if not then exit.
SYS0506: ES:
SYS0507: MOV CX,[BX+04]
SYS050A: ES:
SYS050B: MOV DI,[BX+08]
SYS050E: SUB CX,DI
SYS0510: SUB DX,CX
SYS0512: JNB 0518
Check if buffer has enough room.
SYS0514: ADD CX,DX
SYS0516: XOR DX,DX
Make adjustments.
SYS0518: PUSH ES
Save ES.
SYS0519: ES:
SYS051A: LES SI,[BX+0C]
SYS051D: ADD DI,SI
Get pointer to buffer into ES:DI (adjusted using SI).
SYS051F: MOV AL,20
SYS0521: CLD
SYS0522: REPZ
SYS0523: STOSB
Clear buffer (fill with whitespace (‘ ‘) = 20h/32).
SYS0524: SUB DI,SI
Adjust pointer to reflect the number of characters to copy.
SYS0526: POP ES
Restore ES.
SYS0527: ES:
SYS0528: MOV [BX+08],DI
Set Output’s BufPos.
SYS052B: ES:
SYS052C: CMP DI,[BX+04]
SYS052F: JNZ 053A
SYS0531: PUSH DX
SYS0532: PUSH ES
SYS0533: PUSH BX
SYS0534: CALL 0619
SYS0537: POP BX
SYS0538: POP ES
SYS0539: POP DX
SYS053A: OR DX,DX
SYS053C: JNZ 0506
SYS053E: RET
SYS053F: MOV WORD PTR [InOutRes],0069
SYS0545: RET
Return with error error code 69h/105: File not open for output.
SYS0546: CMP WORD PTR [InOutRes],+00
SYS054B: JNZ 0595
Check if last operation had any errors.
SYS054D: ES:
SYS054E: CMP WORD PTR [BX+02],fmOutput
SYS0553: JNZ 0596
Check if file/device has been is open for writing.
SYS0555: ES:
SYS0556: MOV CX,[BX+04]
SYS0559: ES:
SYS055A: MOV DI,[BX+08]
SYS055D: SUB CX,DI
SYS055F: SUB AX,CX
SYS0561: JNB 0567
SYS0563: ADD CX,AX
SYS0565: XOR AX,AX
SYS0567: PUSH DS
SYS0568: PUSH ES
SYS0569: PUSH BX
SYS056A: MOV DS,DX
SYS056C: ES:
SYS056D: LES BX,[BX+0C]
SYS0570: ADD DI,BX
SYS0572: CLD
SYS0573: REPZ
SYS0574: MOVSB
SYS0575: SUB DI,BX
SYS0577: POP BX
SYS0578: POP ES
SYS0579: POP DS
SYS057A: ES:
SYS057B: MOV [BX+08],DI
SYS057E: ES:
SYS057F: CMP DI,[BX+04]
SYS0582: JNZ 0591
SYS0584: PUSH AX
SYS0585: PUSH DX
SYS0586: PUSH SI
SYS0587: PUSH ES
SYS0588: PUSH BX
SYS0589: CALL 0619
SYS058C: POP BX
SYS058D: POP ES
SYS058E: POP SI
SYS058F: POP DX
SYS0590: POP AX
SYS0591: OR AX,AX
SYS0593: JNZ 0555
SYS0595: RET
SYS0596: MOV WORD PTR [InOutRes],0069
SYS059C: RET
Return with error error code 69h/105: File not open for output.
SYS0619: PUSH ES
SYS061A: PUSH BX
Save registers ES:BX.
SYS061B: ES:
SYS061C: CALL FAR [BX:InOutFunc]
Call Output’s I/O handler.
SYS061F: OR AX,AX
SYS0621: JZ 0626
Check if the last operation generated any errors.
SYS0623: MOV [InOutRes],AX
Record the result of the last operation.
SYS0626: RET
Because it returns with a NEAR RET, it can only be called from within the system library.
SYS0693: PUSH BP
SYS0694: MOV BP,SP
SYS0696: SUB SP,+40
Save BP and use it as index to the stack. Reserve 40h/64 bytes on the stack.
Index | Contents |
---|---|
BP-40 | (SP Points here) |
BP+00 | Old BP |
BP+02 | Return Address (Offset) |
BP+04 | Return Address (Segment) |
BP+06 | Precision (FFFFh) |
BP+08 | Width (0011h) |
BP+0A | Low Word of Real |
BP+0C | Mid Word of Real |
BP+0E | High Word of Real |
BP+10 | Output buffer (Offset) |
BP+12 | Output buffer (Segment) |
Precision and Width are used to set the desired precision. If Precision is a positive number, it will determine the number of digits after the decimal point.
SYS0699: MOV AX,[BP+0A]
SYS069C: MOV BX,[BP+0C]
SYS069F: MOV DX,[BP+0E]
SYS06A2: MOV CX,[BP+06]
Loads Real number passed on stack to DX:BX:AX. Where DX = High word, BX = Mid word, and AX = Low word. Load parameter into CX ([BP+06] = FFFFh/-1).
SYS06A5: OR CX,CX
SYS06A7: JNS 06B7
Check if CX is unsigned or a negative number.
SYS06A9: MOV CX,0006
SYS06AC: SUB CX,[BP+08]
SYS06AF: CMP CX,-02
SYS06B2: JLE 06B7
Check if number of characters representation is enough.
SYS06B4: MOV CX,FFFE
If not enough, then set default of width 2 digits (FFFEh/-2).
SYS06B7: LEA DI,[BP-40]
SYS06BA: PUSH SS
SYS06BB: POP ES
Point ES:DI to reserved 40h/64 bytes on the stack.
SYS06BC: CALL 0BBD
Call start of conversion routine in SYS:0BBD.
SYS06BF: LES BX,[BP+10]
Load output buffer address in [BP+10] into ES:BX.
SYS06C2: MOV DX,[BP+08]
Load required width into DX.
SYS06C5: SUB DX,CX
SYS06C7: JLE 06CE
Check if all digits have been converted.
SYS06C9: PUSH CX
SYS06CA: CALL 04F7
SYS06CD: POP CX
SYS06CE: MOV AX,CX
SYS06D0: LEA SI,[BP-40]
SYS06D3: MOV DX,SS
SYS06D5: CALL 0546
SYS06D8: MOV SP,BP
SYS06DA: POP BP
SYS06DB: RETF 000A
Registers | Description | Sample value |
---|---|---|
DI:SI:CX | Number read from SYS:0E7A | 0000 0000 0081 |
DX:BX:AX | Number to be converted | HHHH MMMM LLLL |
SYS0889: JMP 0988
SYS088C: OR AL,AL
SYS088E: JZ 0889
Check if exponent is 00h and exit if there is none.
SYS0890: OR CL,CL
SYS0892: JZ 0889
Check if CL is also empty.
SYS0894: PUSH BP
SYS0895: MOV BP,DX
SYS0897: XOR DX,DI
SYS0899: AND DX,8000
SYS089D: XCHG DL,AL
SYS089F: ADD DL,CL
SYS08A1: ADC DH,AL
SYS08A3: MOV CL,AL
SYS08A5: OR BP,8000
SYS08A9: OR DI,8000
SYS08AD: PUSH DX
SYS08AE: OR AH,AH
SYS08B0: JNZ 08B6
SYS08B2: OR BX,BX
SYS08B4: JZ 08C3
SYS08B6: OR CH,CH
SYS08B8: JNZ 08E0
SYS08BA: OR SI,SI
SYS08BC: JNZ 08E0
SYS08BE: XCHG CX,AX
SYS08BF: XCHG BX,SI
SYS08C1: XCHG BP,DI
SYS08C3: MOV AX,CX
SYS08C5: MUL BP
SYS08C7: MOV BX,DX
SYS08C9: MOV AX,SI
SYS08CB: MUL BP
SYS08CD: ADD BX,AX
SYS08CF: ADC DX,+00
SYS08D2: MOV CX,DX
SYS08D4: MOV AX,DI
SYS08D6: MUL BP
SYS08D8: ADD AX,CX
SYS08DA: ADC DX,+00
SYS08DD: JMP 095B
SYS08DF: NOP
SYS08E0: PUSH DI
SYS08E1: PUSH SI
SYS08E2: PUSH CX
SYS08E3: PUSH BP
SYS08E4: PUSH BX
SYS08E5: PUSH AX
SYS08E6: MOV BP,SP
SYS08E8: XOR CX,CX
SYS08EA: MOV AL,[BP+01]
SYS08ED: MUL BYTE PTR [BP+07]
SYS08F0: MOV SI,AX
SYS08F2: MOV DI,CX
SYS08F4: MOV BX,CX
SYS08F6: MOV AX,[BP+00]
SYS08F9: MUL WORD PTR [BP+08]
SYS08FC: ADD SI,AX
SYS08FE: ADC DI,DX
SYS0900: ADC BX,CX
SYS0902: MOV AX,[BP+02]
SYS0905: MUL WORD PTR [BP+06]
SYS0908: ADD SI,AX
SYS090A: ADC DI,DX
SYS090C: ADC BX,CX
SYS090E: MOV SI,CX
SYS0910: MOV AX,[BP+00]
SYS0913: MUL WORD PTR [BP+0A]
SYS0916: ADD DI,AX
SYS0918: ADC BX,DX
SYS091A: ADC SI,CX
SYS091C: MOV AX,[BP+02]
SYS091F: MUL WORD PTR [BP+08]
SYS0922: ADD DI,AX
SYS0924: ADC BX,DX
SYS0926: ADC SI,CX
SYS0928: MOV AX,[BP+04]
SYS092B: MUL WORD PTR [BP+06]
SYS092E: ADD DI,AX
SYS0930: ADC BX,DX
SYS0932: ADC SI,CX
SYS0934: MOV DI,CX
SYS0936: MOV AX,[BP+02]
SYS0939: MUL WORD PTR [BP+0A]
SYS093C: ADD BX,AX
SYS093E: ADC SI,DX
SYS0940: ADC DI,CX
SYS0942: MOV AX,[BP+04]
SYS0945: MUL WORD PTR [BP+08]
SYS0948: ADD BX,AX
SYS094A: ADC SI,DX
SYS094C: ADC DI,CX
SYS094E: MOV AX,[BP+04]
SYS0951: MUL WORD PTR [BP+0A]
SYS0954: ADD AX,SI
SYS0956: ADC DX,DI
SYS0958: ADD SP,+0C
SYS095B: XCHG BX,AX
SYS095C: POP CX
SYS095D: POP BP
SYS095E: OR DH,DH
SYS0960: JS 0969
SYS0962: SHL AX,1
SYS0964: RCL BX,1
SYS0966: RCL DX,1
SYS0968: DEC CX
SYS0969: SUB CX,8081
SYS096D: ADD AX,0080
SYS0970: ADC BX,+00
SYS0973: ADC DX,+00
SYS0976: JNB 097B
SYS0978: RCR DX,1
SYS097A: INC CX
SYS097B: TEST CH,40
SYS097E: JNZ 0988
SYS0980: INC CX
SYS0981: MOV AL,CL
SYS0983: XOR DH,CH
SYS0985: SHR CH,1
SYS0987: RET
SYS0988: XOR AX,AX
SYS098A: MOV BX,AX
SYS098C: MOV DX,AX
SYS098E: RET
SYS098F: OR AL,AL
SYS0991: JZ 0988
SYS0993: PUSH BP
SYS0994: MOV BP,DX
SYS0996: XOR DX,DI
SYS0998: OR DI,8000
SYS099C: OR BP,8000
SYS09A0: AND DX,8000
SYS09A4: XCHG AL,DL
SYS09A6: SUB DL,CL
SYS09A8: SBB DH,AL
SYS09AA: PUSH DX
SYS09AB: MOV AL,02
SYS09AD: MOV DX,0001
SYS09B0: CMP BP,DI
SYS09B2: JNZ 09BA
SYS09B4: CMP BX,SI
SYS09B6: JNZ 09BA
SYS09B8: CMP AH,CH
SYS09BA: JB 09C2
SYS09BC: SUB AH,CH
SYS09BE: SBB BX,SI
SYS09C0: SBB BP,DI
SYS09C2: RCL DX,1
SYS09C4: JB 09D7
SYS09C6: SHL AH,1
SYS09C8: RCL BX,1
SYS09CA: RCL BP,1
SYS09CC: JNB 09B0
SYS09CE: SUB AH,CH
SYS09D0: SBB BX,SI
SYS09D2: SBB BP,DI
SYS09D4: CLC
SYS09D5: JMP 09C2
SYS09D7: DEC AL
SYS09D9: JS 09E5
SYS09DB: PUSH DX
SYS09DC: MOV DX,0001
SYS09DF: JNZ 09C6
SYS09E1: MOV DL,40
SYS09E3: JMP 09C6
SYS09E5: MOV AX,DX
SYS09E7: MOV CL,06
SYS09E9: SHL AX,CL
SYS09EB: POP BX
SYS09EC: POP DX
SYS09ED: POP CX
SYS09EE: POP BP
SYS09EF: NOT AX
SYS09F1: NOT BX
SYS09F3: XOR DX,-01
SYS09F6: JS 09FF
SYS09F8: RCL AX,1
SYS09FA: RCL BX,1
SYS09FC: RCL DX,1
SYS09FE: DEC CX
SYS09FF: ADD CX,8080
SYS0A03: JMP 096D
SYS0BBD: PUSH BP
SYS0BBE: MOV BP,SP
SYS0BC0: SUB SP,+14
At this point DX:BX:AX contains the Real number. Save BP then reserve 14h/20 bytes on the stack.
SYS0BC3: PUSH DI
Save DI. At this point ES:DI contains the pointer temporary output buffer.
Stack after SYS:0BC3
Index | Contents |
---|---|
BP-16 | Saved DI (SP points here) |
BP-14 | (Temporary buffer) |
BP-06 | (number of digits converted) |
BP-04 | (MSB) |
BP-02 | (Precision) |
BP | Saved BP |
SYS0BC4: CMP CX,+0B
SYS0BC7: JLE 0BCC
SYS0BC9: MOV CX,000B
SYS0BCC: CMP CX,-0B
SYS0BCF: JGE 0BD4
Check if CX is within the limit: -0Bh <= CX <= +0Bh.
SYS0BD1: MOV CX,FFF5
Otherwise, set Default CX = FFF5h/-11.
SYS0BD4: MOV [BP-02],CX
Store CX into SS:[BP-02].
SYS0BD7: MOV [BP-04],DH
Store the Real number’s MSB into SS:[BP-04].
SYS0BDA: PUSH ES
SYS0BDB: PUSH DI
Preserve ES:DI.
SYS0BDC: LEA DI,[BP-14]
SYS0BDF: PUSH SS
SYS0BE0: POP ES
Load pointer to reserved area (SS:BP-14) into ES:DI.
SYS0BE1: CALL 0CB5
SYS0BE4: POP DI
SYS0BE5: POP ES
Restore ES:DI.
SYS0BE6: MOV [BP-06],CX
SYS0BE9: MOV SI,[BP-02]
SYS0BEC: OR SI,SI
SYS0BEE: JS 0BFC
SYS0BF0: ADD SI,[BP-06]
SYS0BF3: INC SI
SYS0BF4: JNS 0BFE
SYS0BF6: MOV BYTE PTR [BP-14],00
SYS0BFA: JMP 0C2A
Initialize output buffer by marking the first byte with a NULL/00h byte.
SYS0BFC: NEG SI
Negate SI to make it positive.
SYS0BFE: CMP SI,+0C
SYS0C01: JB 0C06
SYS0C03: MOV SI,000B
Limit to 0Ch/11 digits.
SYS0C06: CMP BYTE PTR [BP+SI-14],35
SYS0C0A: MOV BYTE PTR [BP+SI-14],00
SYS0C0E: JB 0C2A
SYS0C10: DEC SI
SYS0C11: JS 0C22
SYS0C13: INC BYTE PTR [BP+SI-14]
SYS0C16: CMP BYTE PTR [BP+SI-14],39
SYS0C1A: JBE 0C2A
SYS0C1C: MOV BYTE PTR [BP+SI-14],00
SYS0C20: JMP 0C10
SYS0C22: MOV WORD PTR [BP-14],0031
Put NULL (00h)-terminated string ‘1’ into start of the buffer.
SYS0C27: INC WORD PTR [BP-06]
Increase number of digits to convert.
SYS0C2A: XOR SI,SI
Reset SI.
SYS0C2C: CLD
SYS0C2D: MOV DX,[BP-02]
SYS0C30: OR DX,DX
SYS0C32: JS 0C69
SYS0C34: TEST BYTE PTR [BP-04],80
SYS0C38: JZ 0C3D
SYS0C3A: MOV AL,2D
SYS0C3C: STOSB
SYS0C3D: MOV CX,[BP-06]
SYS0C40: OR CX,CX
SYS0C42: JNS 0C49
SYS0C44: MOV AL,30
SYS0C46: STOSB
SYS0C47: JMP 0C50
SYS0C49: CALL 0CA9
SYS0C4C: STOSB
Load one byte into AL on call to SYS:0CA9 and store in ES:DI.
SYS0C4D: DEC CX
SYS0C4E: JNS 0C49
SYS0C50: OR DX,DX
SYS0C52: JZ 0CA0
SYS0C54: MOV AL,2E
SYS0C56: STOSB
SYS0C57: INC CX
SYS0C58: JZ 0C60
SYS0C5A: MOV AL,30
SYS0C5C: STOSB
SYS0C5D: DEC DX
SYS0C5E: JNZ 0C57
SYS0C60: DEC DX
SYS0C61: JS 0CA0
SYS0C63: CALL 0CA9
SYS0C66: STOSB
Load one byte into AL on call to SYS:0CA9 and store in ES:DI.
SYS0C67: JMP 0C60
SYS0C69: MOV AL,20
SYS0C6B: TEST BYTE PTR [BP-04],80
SYS0C6F: JZ 0C73
Prepare ‘ ‘ (whitespace) character in AL. Check if number is negative.
SYS0C71: MOV AL,2D
Prepare ‘-’ character if number is negative.
SYS0C73: STOSB
Store character in AL to ES:[DI].
SYS0C74: CALL 0CA9
SYS0C77: STOSB
Load one byte into AL on call to SYS:0CA9 and store in ES:[DI].
SYS0C78: INC DX
Add to count.
SYS0C79: JZ 0C85
Check if there are mo more digits to convert.
SYS0C7B: MOV AL,2E
SYS0C7D: STOSB
Store ‘.’ into the buffer at ES:[DI].
SYS0C7E: CALL 0CA9
SYS0C81: STOSB
Load one byte into AL on call to SYS:0CA9 and store in ES:[DI].
SYS0C82: INC DX
SYS0C83: JNZ 0C7E
Cycle until all of the significand f’s digits are stored in output buffer.
SYS0C85: MOV AL,45
SYS0C87: STOSB
Store ‘E’ into the buffer at ES:[DI] (scientific notation).
SYS0C88: MOV AL,2B
Prepare ‘+’ character if number is exponent is positive.
SYS0C8A: MOV DX,[BP-06]
SYS0C8D: OR DX,DX
SYS0C8F: JNS 0C95
Check if exponent is negative (SF = 1, signed).
SYS0C91: MOV AL,2D
SYS0C93: NEG DX
Prepare ‘-’ character since exponent is negative. Negate DX to make the exponent positive.
SYS0C95: STOSB
Store exponent sign into the buffer at ES:[DI].
SYS0C96: MOV AX,DX
Store exponent in AX.
SYS0C98: MOV DL,0A
SYS0C9A: IDIV DL
Convert exponent in AL to base 10 (DL = 0Ah) by doing a signed division of AL by DL. Quotient in AL, remainder in AH.
SYS0C9C: ADD AX,3030
SYS0C9F: STOSW
Convert exponent digits in AX to ASCII by adding ‘00’ to AX. Store the converted digits into ES:[DI].
The conversion is now complete.
SYS0CA0: MOV CX,DI
SYS0CA2: POP DI
SYS0CA3: SUB CX,DI
Compute actual number of digits converted in CX.
SYS0CA5: MOV SP,BP
SYS0CA7: POP BP
SYS0CA8: RET
Remove temporary reserved space by restoring SP (using BP) then pop off BP before returning.
SYS0CA9: MOV AL,[BP+SI-14]
SYS0CAC: INC SI
Copy byte character into AL then move pointer (SI) forward by one byte.
SYS0CAD: OR AL,AL
SYS0CAF: JNZ 0CB4
Check if empty.
SYS0CB1: MOV AL,30
SYS0CB3: DEC SI
Pad with ‘0’ if AL is NULL then move pointer (SI) back by one byte.
SYS0CB4: RET
Return.
SYS0CB5: OR AL,AL
SYS0CB7: JNZ 0CC6
At this point AX contains the exponent (in AL) and LSB of the number in AH. Check if exponent non-zero.
SYS0CB9: MOV CX,0006
SYS0CBC: MOV AX,3030
SYS0CBF: CLD
SYS0CC0: REPZ
SYS0CC1: STOSW
SYS0CC2: XOR AL,AL
SYS0CC4: STOSB
SYS0CC5: RET
If exponent is zero, store ‘000000000000’ into the buffer then NULL/00h terminate it.
SYS0CC6: AND DH,7F
Clear the sign bit in DH (Bit 7).
SYS0CC9: PUSH AX
Preserve AX as it is frequently modified by the arithmetic operations.
SYS0CCA: SUB AL,80
Remove bias from exponent (80h)
SYS0CCC: MOV AH,4D
SYS0CCE: IMUL AH
Multiply un-biased exponent in AL with 4Dh/77 and store result in AX.
SYS0CD0: ADD AX,0005
SYS0CD3: MOV AL,AH
SYS0CD5: CBW
Add 5 to AX then move high byte to lower byte then convert to word.
SYS0CD6: MOV CX,AX
Copy result into CX.
SYS0CD8: POP AX
Restore AX.
SYS0CD9: CMP CX,-27
SYS0CDC: JNZ 0CDF
Check if CX = FFD9h/-27h/-39.
SYS0CDE: INC CX
If equal, increment by 1.
SYS0CDF: PUSH CX
SYS0CE0: PUSH DI
Save CX, and DI.
SYS0CE1: NEG CX
Negate CX.
SYS0CE3: CALL 0E2A
Check if CX is within range/limits on call to SYS:0E2A.
SYS0CE6: POP DI
SYS0CE7: POP CX
Restore CX, and DI saved in SYS:0CDF and SYS:0CE0.
SYS0CE8: CMP AL,81
SYS0CEA: JNB 0CF0
Check exponent in AL if below the biased value (80h and below).
SYS0CEC: CALL 0EB6
Adjust the number in DX:BX:AX on call to SYS:0EB6.
SYS0CEF: DEC CX
Adjust CX further.
This subroutine extracts digits into the maximum precision (12 digits) and stores them into a temporary buffer.
SYS0CF0: PUSH CX
SYS0CF1: OR DH,80
SYS0CF4: MOV CL,84
SYS0CF6: SUB CL,AL
SYS0CF8: MOV AL,00
SYS0CFA: JZ 0D06
SYS0CFC: SHR DX,1
SYS0CFE: RCR BX,1
SYS0D00: RCR AX,1
SYS0D02: DEC CL
SYS0D04: JNZ 0CFC
SYS0D06: MOV SI,000C
Convert 12 digits (SI = 000C).
SYS0D09: MOV CH,DH
SYS0D0B: MOV CL,04
SYS0D0D: SHR CH,CL
Copy digit to convert to ASCII in DH to CH. Only the upper digit (bits 4-7) are needed.
SYS0D0F: ADD CH,30
SYS0D12: ES:
SYS0D13: MOV [DI],CH
Convert digit in CH to ASCII by adding it ‘0’ (30h/48) and storing in ES:[DI].
SYS0D15: AND DH,0F
Clear upper bits (4-7) in DH.
SYS0D18: PUSH DX
SYS0D19: PUSH BX
SYS0D1A: PUSH AX
Save current number on the stack.
SYS0D1B: SHL AX,1
SYS0D1D: RCL BX,1
SYS0D1F: RCL DX,1
SYS0D21: SHL AX,1
SYS0D23: RCL BX,1
SYS0D25: RCL DX,1
SYS0D27: POP CX
SYS0D28: ADD AX,CX
SYS0D2A: POP CX
SYS0D2B: ADC BX,CX
SYS0D2D: POP CX
SYS0D2E: ADC DX,CX
SYS0D30: SHL AX,1
SYS0D32: RCL BX,1
SYS0D34: RCL DX,1
This entire sequence multiplies the DX:BX:AX by 10 in place. It does this first by shifting DX:BX:AX twice to the left, with bit 7 of AX and BX carrying over to BX and DX through the CF flag. Next the number preserved on the stack is added to DX:BX:AX, through successive pops of CX then adding it to AX, BX, and then DX with overflows (if any) from AX+CX and BX+CX, carrying over to BX, and DX.
SYS0D36: INC DI
SYS0D37: DEC SI
Move the index pointer ES:DI to the next location then decrease the counter in SI.
SYS0D38: JNZ 0D09
Repeat the process on the next digit if there are any left.
SYS0D3A: ES:
SYS0D3B: MOV BYTE PTR [DI],00
NULL (00h) terminate the output buffer at ES:[DI].
SYS0D3E: POP CX
SYS0D3F: RET
Restore CX then return. Because it returns with a NEAR RET, it can only be called from within the system library.
SYS0E2A: CMP CL,DA
SYS0E2D: JL 0E78
Check if CX is less than DAh/-38.
SYS0E2F: CMP CL,26
SYS0E32: JG 0E78
Check if CX is greater than 26h/38.
SYS0E34: PUSH DX
SYS0E35: PUSH BX
SYS0E36: PUSH AX
Save number DX:BX:AX.
SYS0E37: OR CL,CL
SYS0E39: PUSHF
Check if signed, then save results (Flags).
SYS0E3A: JNS 0E3E
Check if signed.
SYS0E3C: NEG CL
If signed, then negate.
SYS0E3E: MOV BL,CL
SYS0E40: AND BL,FC
Copy lower byte into BL then clear Bits 0 and 1.
SYS0E43: MOV BH,BL
SYS0E45: SHR BL,1
Copy lower byte into BH. Then shift right by 1 bit.
SYS0E47: ADD BL,BH
SYS0E49: XOR BH,BH
Add to BL then clear BH.
SYS0E4B: LEA DI,[BX+0E7A]
Set lookup index to [BX+0E7A] (see SYS:0E7A below).
SYS0E4F: CS:
SYS0E50: MOV AX,[DI]
SYS0E52: CS:
SYS0E53: MOV BX,[DI+02]
SYS0E56: CS:
SYS0E57: MOV DX,[DI+04]
Copy numbers to DX:BX:AX.
SYS0E5A: AND CL,03
Clear bits 2-7 in CL.
SYS0E5D: JZ 0E66
Check if CL is cleared.
SYS0E5F: CALL 0EB6
SYS0E62: DEC CL
SYS0E64: JNZ 0E5F
Make adjustments to the DX:BX:AX on a call to SYS:0EB6.
SYS0E66: MOV CX,AX
SYS0E68: MOV SI,BX
SYS0E6A: MOV DI,DX
Copy DX:BX:AX to DI:SI:CX.
SYS0E6C: POPF
Restore Flags (saved in SYS:0E39).
SYS0E6D: POP AX
SYS0E6E: POP BX
SYS0E6F: POP DX
Restore DX:BX:AX.
SYS0E70: JS 0E75
SYS0E72: JMP 088C
SYS0E75: JMP 098F
Check if signed or unsigned then handle appropriately.
SYS0E78: STC
SYS0E79: RET
Return with carry flag set.
SYS0E7A: 81 00 00 00 00 00
SYS0E80: 8E 00 00 00 40 1C
SYS0E86: 9B 00 00 20 BC 3E
SYS0E8C: A8 00 10 A5 D4 68
SYS0E92: B6 04 BF C9 1B 0E
SYS0E98: C3 AC C5 EB 78 2D
SYS0E9E: D0 CD CE 1B C2 53
SYS0EA4: DE F9 78 39 3F 01
SYS0EAA: EB 2B A8 AD C5 1D
SYS0EB0: F8 C9 7B CE 97 40
This subroutine is used to make adjustments to the number so that it is in a proper format.
SYS0EB6: OR AL,AL
SYS0EB8: JZ 0F03
Check if exponent = 00h.
SYS0EBA: PUSH CX
SYS0EBB: PUSH SI
Preserve CX and SI.
SYS0EBC: OR DH,80
Set sign bit.
SYS0EBF: MOV CL,AL
Copy exponent int CL.
SYS0EC1: XOR AL,AL
Clear exponent.
SYS0EC3: PUSH DX
SYS0EC4: PUSH BX
SYS0EC5: PUSH AX
Save number on the stack.
SYS0EC6: SHR DX,1
SYS0EC8: RCR BX,1
SYS0ECA: RCR AX,1
SYS0ECC: SHR DX,1
SYS0ECE: RCR BX,1
SYS0ED0: RCR AX,1
SYS0ED2: POP SI
SYS0ED3: ADD AX,SI
SYS0ED5: POP SI
SYS0ED6: ADC BX,SI
SYS0ED8: POP SI
SYS0ED9: ADC DX,SI
This entire sequence divides DX:BX:AX by 3. It does this first by shifting DX:BX:AX twice to the right, with bit 0’s of DX and BX carrying over to BX and AX through the CF flag. The number preserved on the stack is then added to DX:BX:AX, through successive pops of SI then adding it to AX, BX, and then DX with successive overflows (if any) from AX+SI and BX+SI, carrying over to BX, and DX.
SYS0EDB: JNB 0EE8
Chec if the operation triggered a ‘carry’.
SYS0EDD: RCR DX,1
SYS0EDF: RCR BX,1
SYS0EE1: RCR AX,1
SYS0EE3: ADD CL,01
Shift to the right (including the carry over in CF) then increase the exponent number in CL.
SYS0EE6: JB 0F01
Check if there is still an overflow in CL then exit.
SYS0EE8: ADD AX,0080
SYS0EEB: ADC BX,+00
SYS0EEE: ADC DX,+00
Fix the exponent then carry over any overflows into BX and DX.
SYS0EF1: JNB 0EFA
Check if there are lingering overflows require another adjustment.
SYS0EF3: RCR DX,1
SYS0EF5: ADD CL,01
Make space for the sign bit by shifting DX to the right once and adjusting the exponent in CL.
SYS0EF8: JB 0F01
If the operation still produced a carry, then it’s possible that the number is badly formatted, out of range or inccorect. If so, nothing can be done further now.
SYS0EFA: AND DH,7F
Clear sign bit.
SYS0EFD: MOV AL,CL
SYS0EFF: ADD AL,03
Copy the exponent in CL back to AL then adjust by 3 to account for the divide by 3 operation above.
SYS0F01: POP SI
SYS0F02: POP CX
Restore CX and SI.
SYS0F03: RET
Because it returns with a NEAR RET, it can only be called from within the system library.
Go Back