; ; This program can add two very large unsigned integers. ; The program must receive two numbers in base 10 as ; parameters, and it prints the result to STDOUT. ; The biggest number you can possibly end up with has ; 125 digits. This limit is because DOS parameter string ; is stored in a 127-byte string space starting at DS:0081H. ; Written by Zsolt Nagy-Perge in Dec 15, 2018. ; ; ; To compile & run, enter the following commands: ; ; C:\>TASM ADD ; ; C:\>TLINK /t /c /x ADD ; ; C:\>ADD 3333 55555 ; 58888 ; C:\> ; ; ; CODE SEGMENT ASSUME CS:CODE, DS:CODE, ES:CODE, SS:CODE ORG 256 MAIN: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; See if we've got any parameters. PUSH CS POP DS ; DS <- CS PUSH CS POP ES ; ES <- CS MOV SI,00080H ; SI <- points to parameter string length MOV DI,OFFSET ARGS ; DI <- points to ARGS parameter table LODSB CMP AL,3 ; Parameter string must have at least 3 bytes JB No_Number ; Save the first two parameters. ; (SI now points to the first character of the parameter string.) CALL GrabArgument ; Get first number CALL GrabArgument ; Get second number JMP Add_Numbers ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This function finds the next word in the parameter ; string and saves a pointer for future reference. ; Also, it saves the length of the parameter word. ; ; This function populates the ARGS parameter table. ; The ARGS parameter table consists of 4-byte objects. ; The first 2 bytes are the length of a word, followed ; by a 2-byte pointer to the last letter of word. ; If the word contains anything other than digits (0-9), ; then the program ends prematurely. ; ; DS:SI <- pointer to parameter string ; ES:DI <- pointer to ARGS parameter table ; ; This function increments SI and DI. ; GrabArgument: XOR CX,CX CLD NextChar: CMP SI,0100H ; DOS parameters cannot be longer than 128 bytes JAE No_Number LODSB CMP AL,13 JE No_Number ; End of string? CMP AL,32 JBE NextChar ; Skip whitespace ; We continue this way only if we've got something ; other than a space or tab. ; Normally in C language, pointers always point to the ; first character of a string, but in this program, ; parameter pointers will point to the last letter ; of a string, because we start adding numbers ; from right to left. CountLen: INC CX ; Count the length of string LODSB CMP AL,32 JBE End_of_Number CMP AL,'0' JB No_Number CMP AL,'9' JBE CountLen No_Number: MOV DX,OFFSET About JMP Print_and_Exit End_of_Number: XCHG CX,AX STOSW ; Save string length DEC SI MOV AX,SI DEC AX STOSW ; Save ptr to the end of string RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Prepare for addition. ; Add_Numbers: ; Figure out which number is longer. MOV SI,OFFSET ARGS LODSW ; Load length XCHG CX,AX LODSW ; Skip pointer XCHG BP,AX ; BP = number1 ptr LODSW ; Load next word's length CMP CX,AX ; AX > CX ? JA CX_BIGGER XCHG CX,AX CX_BIGGER: ; Now CX holds the length of the longest string. LODSW XCHG SI,AX ; SI = number2 ptr MOV DI,OFFSET Result-1 ; DI = destination ; Now we have the following setup: ; SI points to the last letter of the first number ; BP points to the last letter of the second number ; DI points to the last letter of the result XOR DX,DX ; DL = carry flag STD NextDigit: ; START ADDING DIGITS CALL GetDigit ; Read from number 1 XCHG BX,AX ; digit -> BX XCHG BP,SI ; Read from number 2 CALL GetDigit ; digit -> AX ADD AL,BL ; Add the two digits ADD AL,DL ; Add carry MOV DL,DH ; Reset carry (DL=0) ADD AL,'0' CMP AL,'9' ; Carry? JBE NO_CARRY SUB AL,10 INC DX ; Set carry (DL=1) NO_CARRY: STOSB ; write result LOOP NextDigit ; Note: DH is always 00. CMP DL,DH ; Is carry set? JE DONE MOV AL,'1' STOSB DONE: MOV DX,DI INC DX Print_and_Exit: MOV AH,9 INT 21H ; Print text MOV AX,4C00H INT 21H ; Exit 0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This function reads the next digit of the number ; into register AL. If we reach the end of the number, ; then AL will be 0, and our pointer (SI) will stay ; in place, otherwise SI will jump to the next digit. ; (Keep in mind we're reading from right to left.) ; ; DS:SI <- pointer to base 10 number string ; AL -> raw value ; GetDigit: LODSB ; digit -> AX SUB AL,'0' CMP AL,10 ; is AL < 10 ? JB ValidRange ; Looks like we reached the end of the number. MOV AL,DH ; AL=0 INC SI ValidRange: RET ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ARGS contains a 2-byte pointer to a parameter word, ; followed by a 2-byte string length, followed by the ; pointer to the next parameter, followed by its length. ; Initially, ARGS contains 2 pointers to a blank string ; of zero length. ; About DB 10,13, "This program adds two positive integers of any size and displays the result." DB 10,13, "Usage: ADD ", 10,13 DB 10,13, "Example: ADD 19333045834565223108349833745893453 12982372388169001", 10,13 DB 10,13, "This program requires two numbers as parameters." DB 10,13, "The parameters may only contain digits (0-9).",10,13,10,13 Result DB "$" ; The program will either display the About message, or ; it will display a number. If we display a number, then ; we will overwrite the About message with digits. ARGS DW 0, OFFSET Blank, 0, OFFSET Blank Blank DW 0 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CODE ENDS END MAIN