----TOADADD.DOC---- TOADADD.PAS Turbo Pascal Inline routines for MS-DOS systems. First release permits you to add numeric strings to numeric strings, or integers to numeric strings. Sounds simple, but this gives you a handle on manipulating literally infinitely large numbers. (When the number depicts more than the number of objects in the universe, that's a pretty good definition of infinite, ne?) Code could be rewritten to permit use with Z80 systems, but I'll leave that as an exercise to the student. (Don't you just HATE that?) More to follow ... subtraction, multiplication, division, etc. (Keep your eyes peeled for TOADMATH.PAS.) (Unless some other Netlandian beats me to it.) Assembler ideas obtained from Chapter 11, "Assembler for the IBM PC and PC-XT", by Peter Abel ((C) 1984 Reston Publishing Company, Inc.) for the usual ripoff price. Works just fine on an 80286 PC clone running PC-DOS 3.1 and Turbo 3.0 .. should be fine on any MS-DOS system and any Turbo version. Released to the public domain. (Yep, it's my code.) David Kirschbaum Toad Hall ABN.ISCAMS@USC-ISID.ARPA ----TOADADD.PAS---- program TOADADD.PAS; {David Kirschbaum Toad Hall ABN.ISCAMS@USC-ISID.ARPA 7573 Jennings Lane Fayetteville NC 28303 (919) 868-3471 Released to the public domain. For MS-DOS systems and Turbo Pascal. } {Methods to provide integer addition for huge numbers .. Presently limited by Turbo Pascal's limitation of string variables to 255 bytes. You COULD use an array for this, extending the length of your number string to available memory! String must be in Data Segment, and should be of the format '0000001' or '003270' or whatever. YES, it MUST be zero-filled, and NO commas or decimals! (We're talking multi-character integers here, remember.) } TYPE Str20 = STRING[20]; var NrStr, IntStr : Str20; i,code : INTEGER; PROCEDURE Addit; {adds two integer strings together.} BEGIN Inline( $1E { push DS} /$07 { pop ES ;ES=DS} /$31/$DB { xor BX,BX ;clear msbs} /$89/$D9 { mov CX,BX} /$8A/$0E/>NrStr { mov CL,[>NrStr] ;the shorter number} /$E3/$2B { jcxz Exit ;no length, forget it} /$8A/$1E/>IntStr { mov BL,[>IntStr] ;string length} /$8D/$B7/>IntStr { lea SI,>IntStr[BX] ;first char} /$89/$F7 { mov DI,SI ;source=destination} /$88/$CB { mov BL,CL ;NrStr length again} /$8D/$9F/>NrStr { lea BX,>NrStr[BX] ;point to last char} /$51 { push CX ;save IntStr length} /$56 { push SI ;and offsets} /$F8 { clc} /$FD { std ;direction right to left} /$B4/$00 {B20: mov AH,0 ;clear AH} /$AC { lodsb ;load IntStr byte} /$12/$07 { adc AL,[BX] ;add NrStr val} /$37 { aaa ;adjust for Ascii} /$AA { stosb ;store sum in IntStr} /$4B { dec BX ;back up NrStr} /$E2/$F6 { loop B20} /$88/$25 { mov [DI],AH ;at end, store carry} /$5E { pop SI ;back to the end again} /$89/$F7 { mov DI,SI ;source=destination} /$59 { pop CX ;get back NrStr length} /$41 { inc CX ;anticipate the carry} /$AC {B30: lodsb ;snarf the new val} /$0C/$30 { or AL,$30 ;ascify it} /$AA { stosb} /$E2/$FA { loop B30} {exit:} ); END; {Another method: Lets you add an integer to ANY global string variable. Both the integer to be added, and the address of the string variable are brought in as parameters. } PROCEDURE Add(nr : INTEGER; VAR S : Str20); {could also be: PROCEDURE Add(nr : INTEGER; VAR S); using Turbo's untyped parameter capability. use a string variable S brought in as a parameter, and the integer nr, also brought in as a parameter. } BEGIN Inline( $8B/$86/>NR { mov AX,>nr[BP] ;nr of spins} /$09/$C0 { or AX,AX ;anything there?} /$74/$27 { je X1 ; nope, quit this mess} /$8B/$9E/>S { mov BX,>S[BP] ;snarf the string address.} { ;(LDS or LES would also work,} { ;but we're assuming S is a global} { ;variable .. makes it much simpler.)} /$89/$DF { mov DI,BX ;offset to S} /$43 { inc BX ;bump past length byte} /$31/$C9 { xor CX,CX ;clear msb} /$8A/$0D { mov CL,[DI] ;get our integer string length} /$01/$CF { add DI,CX ;point to the last char in the string} {;} /$BE/$0A/$00 { mov SI,10 ; put divisor in SI} /$31/$D2 {L1:xor DX,DX ; clear dividend high word} /$80/$2D/$30 { sub byte ptr [DI],'0' ;deasciize the char} /$02/$05 { add AL,[DI] ;add our integer msb to the char val} /$F7/$F6 { div SI ; AX = (DX:AX)/SI, DX = remainder} /$80/$C2/$30 { add DL,'0' ; convert DL remainder byte to ascii} /$88/$15 { mov byte ptr [DI],DL ; put back in string as char} /$4F { dec DI ; back step in string} /$39/$DF { cmp DI,BX ;hit start?} /$76/$04 { jbe X1 ; yep, done} /$09/$C0 { or AX,AX ; all done? (AX = 0?)} /$75/$E9 { jne L1 ; if not, do another digit} ); {X1:} END; {of Add} {A third method, assuming you'll only have ONE global string variable you'll ALWAYS use for this adding. (In this demo, that's IntStr.) Saves a little time by not having to pass another parm. } PROCEDURE Add_Int(nr : INTEGER); { use a global string variable (in this case, IntStr), and the integer nr, brought in as a parameter. } BEGIN Inline( $8B/$86/>NR { mov AX,>nr[BP] ;nr of spins} /$09/$C0 { or AX,AX ;anything there?} /$74/$26 { je X2 ; nope, quit this mess} /$BB/>IntStr { mov BX,>IntStr ;snarf the string address.} {;or could be} {; lea BX,>IntStr} {;(LDS or LES would also work, but we're assuming} {; IntStr is a global variable .. makes it much} {; simpler, and we don't chance messing up DS.)} /$89/$DF { mov DI,BX ;offset to IntStr} /$43 { inc BX ;bump past length byte} /$31/$C9 { xor CX,CX ;clear msb} /$8A/$0D { mov CL,[DI] ;get our integer string length} /$01/$CF { add DI,CX ;point to the last char in the string} {;} /$BE/$0A/$00 { mov SI,10 ; put divisor in SI} /$31/$D2 {L2:xor DX,DX ; clear dividend high word} /$80/$2D/$30 { sub byte ptr [DI],'0' ;deasciize the char} /$02/$05 { add AL,[DI] ;add our integer msb to the char val} /$F7/$F6 { div SI ; AX = (DX:AX)/SI, DX = remainder} /$80/$C2/$30 { add DL,'0' ; convert DL remainder byte to ascii} /$88/$15 { mov byte ptr [DI],DL ; put back in string as char} /$4F { dec DI ; back step in string} /$39/$DF { cmp DI,BX ;hit start?} /$76/$04 { jbe X2 ; yep, done} /$09/$C0 { or AX,AX ; all done? (AX = 0?)} /$75/$E9 { jne L2 ; if not, do another digit} ); {X2:} END; {of Add_Int} PROCEDURE Continue; VAR ch : CHAR; BEGIN Write('Press any key to continue: '); Repeat until keypressed; read(kbd,ch); Writeln; END; begin NrStr := '004'; IntStr := '00000000000000000000'; Writeln('First add one number string [',NrStr,']'); Writeln('to a second number string [',IntStr,']'); Write(IntStr, ' + ', NrStr, ' = '); Addit; Writeln(IntStr); Continue; Writeln('Now, add an integer to a number string:'); FOR i := 1 TO 10 DO BEGIN Write(IntStr, ' + ', i:2, ' = '); Add(i,IntStr); Writeln(IntStr); END; Continue; Writeln('Same process, but using the number string as a global:'); FOR i := 1 TO 10 DO BEGIN Write(IntStr, ' + ', i:2, ' = '); Add_Int(i); Writeln(IntStr); END; Continue; Writeln('Flashy, user-friendly interactive demo.'); Writeln('Enter a positive number string (up to 19 chars).'); Writeln('(Enter a null line (CR) to quit): '); Repeat Readln(NrStr); IF length(NrStr) > 19 THEN NrStr[0] := #19; {truncate} IF NrStr <> '' THEN BEGIN Write(IntStr, ' + ', NrStr, ' = '); Addit; Writeln(IntStr); END; Until NrStr = ''; end.