This is an OS-independent module of EuroTool program EuroCalc.
EUROASM CPU=X64
calcmain PROGRAM Format=COFF, Width=64
%DROPMACRO *
INCLUDEHEAD1 argument.htm
INCLUDE1 stdcal64.htm, cpuext64.htm, cpuext.htm, status32.htm
N STRUC ; Structure which describes a number or a function.
.MAN0 D DWORD ; Lowest third of the mantissa.
.MAN1 D DWORD ; Middle third of the mantissa.
.MAN2 D DWORD ; Highest third of the mantissa.
.EXP D DWORD ; Binary exponent.
ENDSTRUC N
O STRUC ; Structure which describes a function or operation.
.Oper D QWORD ; Operator or function in lower case, e.g. '!','+','-','*','/','^','sin','log' etc.
.Handler D QWORD ; Pointer to the handler.
.OperSize D WORD ; Number of characters in .Oper (1,2 or 3).
.OperId D WORD ; Identifier of the operation or function (1,2,3,,,).
.Priority D WORD ; Base priority of the operation, not influenced yet by parenthesis.
.Arity D WORD ; 'U','B','R','0','1' ; Unary/Binary/Reversed/0/1 operand.
ENDSTRUC O
8000_0000_0000_0000_0000_0000h..7FFF_FFFF_FFFF_FFFF_FFFF_FFFFh,
and bits 96..127 represent the 32-bit exponent as a signed binary integer in the range 8000_0003h..7FFF_FFFFh.
Signum of the exponent is in bit 127, signum of mantissa, and thus the signum of the entire number value, is in bit 95.
8000_0000h is special. The structure with this value in exponent field
is not a number but a function identified by the value in bits 0..15. Priority of the function
is in bits 16..31.
8000_0001h and 8000_0002h are special, too.
The structure with this value in exponent field represents
not a number or a function, but a parenthesis.| Value | Operation | Operator | Arity | Priority | Example |
|---|---|---|---|---|---|
| 1 | FnEul | EUL | Function Euler's number with 0 operand | 15 | eul = 2.718_281_828_459_045_235_360_288 |
| 2 | FnPi | PI | Function Ludolf's number with 0 operand | 15 | pi = 3.141_592_653_589_793_238_462_644 |
| 3 | FnPlus | + | Unary operation | 12 | +2 = 2 |
| 4 | FnMinus | - | Unary operation | 12 | -2 = -2 |
| 5 | FnFactorial | ! | Reversed operation | 14 | 3 ! = 6 |
| 6 | FnAdd | + | Binary operation | 2 | 2 + 3 = 5 |
| 7 | FnSubtract | - | Binary operation | 2 | 2 - 3 = -1 |
| 8 | FnMultiply | * | Binary operation | 4 | 2 * 3 = 6 |
| 9 | FnDivide | / | Binary operation | 4 | 3 / 2 = 1.5 |
| 10 | FnPower | ^ | Binary operation | 6 | 2 ^ 3 = 8 |
| 11 | FnSin | SIN | Function sine with 1 operand | 10 | sin (pi / 6) = 0.5 |
| 12 | FnCos | COS | Function cosine with 1 operand | 10 | cos (pi / 6) = 0.5 |
| 13 | FnTan | TAN | Function tangent with 1 operand | 10 | tan (pi / 4) = 1 |
| 14 | FnCot | COT | Function cotangent with 1 operand | 10 | cot (pi / 4) = 1 |
| 15 | FnSec | SEC | Function secant with 1 operand | 10 | sec pi = -1 |
| 16 | FnCsc | CSC | Function cosecant with 1 operand | 10 | csc (pi/2) = 1 |
| 17 | FnExp | EXP | Function exponent with 1 operand | 10 | exp 1 = 2.718_281_828_459_045_235_360_288 |
| 18 | FnLn | LN | Function natural logarithm with 1 operand | 10 | ln eul = 1 |
| 19 | FnLg | LG | Function decimal logarithm with 1 operand | 10 | lg 100 = 2 |
| Number | Alias | Mantissa | Exponent | N |
|---|---|---|---|---|
| 0 | 0 * 2 ^ 0 | 0 | 0 | 0x0000_0000_0000_0000_0000_0000_0000_0000 |
| 12 | 12 * 2 ^ 0 | 12 | 0 | 0x0000_0000_0000_0000_0000_0000_0000_000C |
| 0.25 | 1 * 2 ^(-2) | 1 | -2 | 0xFFFF_FFFE_0000_0000_0000_0000_0000_0001 |
| -64 | -64 * 2 ^ 0 | -64 | 0 | 0x0000_0000_FFFF_FFFF_FFFF_FFFF_FFFF_FFC0 |
| -64 | -1 * 2 ^ 6 | -1 | 6 | 0x0000_0006_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF |
| +∞ | 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF * 2 ^ 0x7FFF_FFFF | 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF | 0x7FFF_FFFF | 0x7FFF_FFFF_7FFF_FFFF_FFFF_FFFF_FFFF_FFFF |
| -∞ | 0x8000_0000_0000_0000_0000_0000 * 2 ^ 0x7FFF_FFFF | 0x8000_0000_0000_0000_0000_0000 | 0x7FFF_FFFF | 0x7FFF_FFFF_8000_0000_0000_0000_0000_0000 |
| +1/∞ | 0x0000_0000_0000_0000_0000_0001 * 2 ^ 0x8000_0001 | 0x0000_0000_0000_0000_0000_0001 | 0x8000_0001 | 0x8000_0001_0000_0000_0000_0000_0000_0001 |
| -1/∞ | 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF * 2 ^ 0x8000_0001 | 0x0000_0000_0000_0000_0000_0001 | 0x8000_0001 | 0x8000_0001_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF |
| Fn #12h | 0x0000_0000_0000_0000_0000_0012 * 2 ^ 0x8000_0000 | 0x0000_0000_0000_0000_0000_0012 | 0x8000_0000 | 0x8000_0000_0000_0000_0000_0000_0000_0012 |
[.rodata] ; Constant data.
EuroCalc::DB "EuroCalc",0
Version:: DB " version %^Date",0
ALIGN OWORD ; Precomputed numeric constants in N format.
Pi DS N, .EXP=-93, .MAN2=0x6487ED51, .MAN1=0x10B4611A, .MAN0=0x626330F4 ; 3.1415926535897932384626433
HalfPi DS N, .EXP=-94, .MAN2=0x6487ED51, .MAN1=0x10B4611A, .MAN0=0x626330F4 ; PI / 2
TwoPi DS N, .EXP=-92, .MAN2=0x6487ED51, .MAN1=0x10B4611A, .MAN0=0x626330F4 ; PI * 2
Eul DS N, .EXP=-93, .MAN2=0x56FC2A2C, .MAN1=0x515DA54D, .MAN0=0x57EE2AC1 ; 2.7182818284590452353602874
OneEulth DS N, .EXP=-96, .MAN2=0x5E2D58D8, .MAN1=0xB3BCDF1A, .MAN0=0xBADEC72C ; 0.3678794411714423215955237
OneLn10th DS N, .EXP=-96, .MAN2=0x6F2DEC54, .MAN1=0x9B9438CA, .MAN0=0x9AADD4F0 ; 0.4342944819032518276511289
OneTenth DS N, .EXP=-96, .MAN2=0x19999999, .MAN1=0x99999999, .MAN0=0x99999999 ; 0.1000000000000000000000000
Half DS N, .EXP=-1, .MAN2=0x00000000, .MAN1=0x00000000, .MAN0=0x00000001 ; 0.5000000000000000000000000
One DS N, .EXP=-94, .MAN2=0x40000000, .MAN1=0x00000000, .MAN0=0x00000000 ; 1.0000000000000000000000000
Two DS N, .EXP=0, .MAN2=0x00000000, .MAN1=0x00000000, .MAN0=0x00000002 ; 2.0000000000000000000000000
Eight DS N, .EXP=0, .MAN2=0x00000000, .MAN1=0x00000000, .MAN0=0x00000008 ; 8.0000000000000000000000000
Ten DS N, .EXP=0, .MAN2=0x00000000, .MAN1=0x00000000, .MAN0=0x0000000A ; 10.0000000000000000000000000
Sixteen DS N, .EXP=0, .MAN2=0x00000000, .MAN1=0x00000000, .MAN0=0x00000010 ; 16.0000000000000000000000000
Dictionary:: ; Table of calculator functions.
DS O, .Handler=FnEul, .Oper='eul', .OperSize=3, .OperId=01, .Priority=15, .Arity='0'
DS O, .Handler=FnPi, .Oper='pi' , .OperSize=2, .OperId=02, .Priority=15, .Arity='0'
DS O, .Handler=FnPlus, .Oper='+' , .OperSize=1, .OperId=03, .Priority=12, .Arity='U'
DS O, .Handler=FnMinus, .Oper='-' , .OperSize=1, .OperId=04, .Priority=12, .Arity='U'
DS O, .Handler=FnFactorial,.Oper='!' , .OperSize=1, .OperId=05, .Priority=14, .Arity='R'
DS O, .Handler=FnAdd, .Oper='+' , .OperSize=1, .OperId=06, .Priority=02, .Arity='B'
DS O, .Handler=FnSubtract, .Oper='-' , .OperSize=1, .OperId=07, .Priority=02, .Arity='B'
DS O, .Handler=FnMultiply, .Oper='*' , .OperSize=1, .OperId=08, .Priority=04, .Arity='B'
DS O, .Handler=FnDivide, .Oper='/' , .OperSize=1, .OperId=09, .Priority=04, .Arity='B'
DS O, .Handler=FnPower, .Oper='^' , .OperSize=1, .OperId=10, .Priority=06, .Arity='B'
DS O, .Handler=FnSin, .Oper='sin', .OperSize=3, .OperId=11, .Priority=10, .Arity='1'
DS O, .Handler=FnCos, .Oper='cos', .OperSize=3, .OperId=12, .Priority=10, .Arity='1'
DS O, .Handler=FnTan, .Oper='tan', .OperSize=3, .OperId=13, .Priority=10, .Arity='1'
DS O, .Handler=FnCot, .Oper='cot', .OperSize=3, .OperId=14, .Priority=10, .Arity='1'
DS O, .Handler=FnSec, .Oper='sec', .OperSize=3, .OperId=15, .Priority=10, .Arity='1'
DS O, .Handler=FnCsc, .Oper='csc', .OperSize=3, .OperId=16, .Priority=10, .Arity='1'
DS O, .Handler=FnExp, .Oper='exp', .OperSize=3, .OperId=17, .Priority=10, .Arity='1'
DS O, .Handler=FnLn, .Oper='ln' , .OperSize=2, .OperId=18, .Priority=10, .Arity='1'
DS O, .Handler=FnLg, .Oper='lg' , .OperSize=2, .OperId=19, .Priority=10, .Arity='1'
DictionaryEnd:
TitleBtns:: DB 0,\ ; Labels on the buttons.
"Clr",0,\ ; Clear.
"Bs",0, \ ; Backspace.
"^",0, \ ; Power.
"/",0, \
"*",0, \
"-",0, \
"LN",0, \
"LG",0, \
"D",0, \
"E",0, \
"F",0, \
"+",0, \
"EXP",0,\
"!",0, \
"A",0, \
"B",0, \
"C",0, \
"EUL",0,\
"SIN",0,\
"SEC",0,\
"7",0, \
"8",0, \
"9",0, \
"PI",0, \
"COS",0,\
"CSC",0,\
"4",0, \
"5",0, \
"6",0, \
"(",0, \
"TAN",0,\
"COT",0,\
"1",0, \
"2",0, \
"3",0, \
")",0, \
"0y",0, \
"0o",0, \
"0x",0, \
"0",0, \
".",0, \
"=",0, \
0
TitleBtnsEnd::
[.bss]
DS N ; Reserve.
Pbuffer:: DS 640 * N ; Array of N-records, including records with parenthesis.
Nbuffer:: DS 640 * N ; Array of N-records, parenthesis removed, Fn0 replaced.
InpBuffer:: DB 640 * B ; Input expression, zero terminated.
OutBuffer:: DB 640 * B ; Output buffer for the result number.
PbufferEnd:: DQ QWORD ; Pointer behind the last valid N-record.
NbufferEnd:: DQ QWORD ; Pointer behind the last valid N-record. Decreased in Calculate.
ArgCount:: DQ QWORD ; Number of arguments.
[.text]
(12h+0x34) * 2.
Parse:: PROC
.AfterBegin: ; Expected: End, LeftParent, Unary, Number, Fn0, Fn1.
LODSB
CMP AL,0
JE .End:
CMP AL,'='
JE .End:
CMP AL,13
JE .End:
CMP AL,' '
JE .AfterBegin:
CMP AL,'('
JE .StoreLeftParent:
CMP AL,'-'
JE .StoreUnary:
CMP AL,'+'
JE .StoreUnary:
CMP AL,'0'
JB .StoreFn:
CMP AL,'9'
JNA .StoreNumber:
JMP .StoreFn:
.AfterUnary: ; Expected: LeftParent, Number, Fn0, Fn1.
LODSB
CMP AL,' '
JE .AfterUnary:
CMP AL,'('
JE .StoreLeftParent:
CMP AL,'0'
JB .AU5:
CMP AL,'9'
JNA .StoreNumber:
.AU5:JMP .StoreFn:
.AfterBinary: ; Expected: LeftParent, Number, Fn0, Fn1.
LODSB
CMP AL,' '
JE .AfterBinary:
CMP AL,'('
JE .StoreLeftParent:
CMP AL,'0'
JB .AB5:
CMP AL,'9'
JBE .StoreNumber:
.AB5:JMP .StoreFn:
.AfterReversed: ; Expected: End, RightParent, Reversed, Binary.
.AfterNumber: ; Expected: End, RightParent, Reversed, Binary.
LODSB
CMP AL,0
JE .End:
CMP AL,13
JE .End:
CMP AL,10
JE .End:
CMP AL,'='
JE .End:
CMP AL,' '
JE .AfterNumber:
CMP AL,')'
JE .StoreRightParent:
CMP AL,'!'
JE .StoreReversed:
JMP .StoreBinary:
.AfterFn1: ; Expected: LeftParent, Unary, Number, Fn0.
LODSB
CMP AL,' '
JE .AfterFn1:
CMP AL,'('
JE .StoreLeftParent:
CMP AL,'-'
JE .StoreUnary:
CMP AL,'+'
JE .StoreUnary:
CMP AL,'0'
JB .Err:
CMP AL,'9'
JA .StoreFn0:
JMP .StoreNumber:
.StoreLeftParent:
XOR EAX,EAX
STOSQ
MOV EAX,0x80000001
SHL RAX,32
STOSQ
JMP .AfterBegin:
.StoreRightParent:
XOR EAX,EAX
STOSQ
MOV EAX,0x80000002
SHL RAX,32
STOSQ
JMP .AfterNumber:
.StoreNumber:
DEC RSI
PUSH RDI
Invoke ParseNumber, RSI
POP RDI
MOV RAX,R8
STOSQ
MOV RAX,R9
STOSQ
JMP .AfterNumber:
.StoreReversed: ; Reversed operator (!) is expected in AL.
LEA RBX,[Dictionary]
LEA RCX,[DictionaryEnd]
SUB RBX,SIZE# O
.SR1:ADD RBX,SIZE# O
CMP RBX,RCX
JAE .Err:
CMPB [RBX+O.Arity],'R'
JNE .SR1:
CMP [RBX+O.Oper],AL
JNE .SR1:
MOVZX EAX,[RBX+O.Priority]
SHL EAX,16
MOV AX,[RBX+O.OperId]
STOSQ
XOR EAX,EAX
STC
RCR RAX,1
STOSQ
JMP .AfterReversed:
.StoreUnary: ; Unary operator Plus or Minus is expected in AL.
LEA RBX,[Dictionary]
SUB RBX,SIZE# O
.SU1:ADD RBX,SIZE# O
LEA RCX,[DictionaryEnd]
CMP RBX,RCX
JAE .Err:
CMPB [RBX+O.Arity],'U'
JNE .SU1:
CMP [RBX+O.Oper],AL
JNE .SU1:
MOVZX EAX,[RBX+O.Priority]
SHL EAX,16
MOV AX,[RBX+O.OperId]
STOSQ
XOR EAX,EAX
STC
RCR RAX,1
STOSQ
JMP .AfterUnary:
.StoreBinary: ; Binary operator is expected in AL.
LEA RBX,[Dictionary]
SUB RBX,SIZE# O
.SB1:ADD RBX,SIZE# O
LEA RCX,[DictionaryEnd]
CMP RBX,RCX
JAE .Err:
CMPB [RBX+O.Arity],'B'
JNE .SB1:
CMP [RBX+O.Oper],AL
JNE .SB1:
MOVZX EAX,[RBX+O.Priority]
SHL EAX,16
MOV AX,[RBX+O.OperId]
STOSQ
XOR EAX,EAX
STC
RCR RAX,1
STOSQ
JMP .AfterBinary:
.StoreFn0:
DEC RSI ; Function name PI or EUL is expected at RSI. Anything else is error.
MOV EAX,0x2020_2020
OR EAX,[RSI]
CMP AX,'pi'
JNE .F0:
ADD RSI,2
MOV RAX,[Pi+0]
STOSQ
MOV RAX,[Pi+8]
STOSQ
JMP .AfterNumber:
.F0: CMP AX,'eu'
JNE .Err:
SHR EAX,16
CMP AL,'l'
JNE .Err:
ADD RSI,3
MOV RAX,[Eul+0]
STOSQ
MOV RAX,[Eul+8]
STOSQ
JMP .AfterNumber:
.StoreFn:
DEC RSI ; Function name is expected at RSI. Anything else is error.
LEA RBX,[Dictionary]
SUB RBX,SIZE# O
.SF1:ADD RBX,SIZE# O
LEA RCX,[DictionaryEnd]
CMP RBX,RCX
JAE .Err:
MOV AL,[RBX+O.Arity]
CMP AL,'1'
JE .SF3:
CMP AL,'0'
JNE .SF1:
.SF3:MOV RAX,0x2020_2020_2020_2020
OR RAX,[RSI] ; Convert to lower case.
MOVZX RCX,[RBX+O.OperSize]
MOV EDX,ECX
LEA ECX,[8*ECX]
NEG ECX
ADD ECX,64
SHL RAX,CL
SHR RAX,CL
CMP RAX,[RBX+O.Oper]
JNE .SF1:
MOVZX EAX,[RBX+O.Priority]
SHL EAX,16
MOV AX,[RBX+O.OperId]
STOSQ
XOR EAX,EAX
STC
RCR RAX,1
STOSQ
ADD RSI,RDX
CMPB [RBX+O.Arity],'0'
JE .AfterNumber:
JMP .AfterFn1:
.Err:STC
.End:RET
ENDPROC Parse
12_34512_345k0n12_3451234_5678_9ABCh0x1234_5678_9ABC123_456q0o123_4560101_0101b0y0101_0101123.45123.45E5 or 123E-3.Parsing algorithmus can distinguish between 123E4h (hexadecimal number 0x123E4) and
123E4 (decimal number 123*104=1230000).
The number format of EuroCalc is compatible with EuroAssembler but is not limited to 63 bits.
ParseNumber:: Procedure NotationPtr
MultiplierHI LocalVar
MultiplierLO LocalVar
HighestPtr LocalVar
HighestProc LocalVar
ClearLocalVar
INCB [%MultiplierLO] ; Default multiplier is 1.
XOR R8,R8
XOR R9,R9
RstSt [Status::],ArgPostfix
MOV RSI,[%NotationPtr]
LODSB
CMP AL,'0'
JNE .NoPrefix:
LODSB
OR AL,0x20 ; Convert to lower case if it were part of prefix 'N','X','O','Y'.
Dispatch AL,'n','x','o','y'
JMP .NoPrefix:
.n: JMP .LoadDecimal:
.x: JMP .LoadHexa:
.o: JMP .LoadOctal:
.y: JMP .LoadBinary:
.NoPrefix: ; Radix may be specified by postfix 'H','Q','B','E' or by decimal postfix 'D','K','M','G','T','P'.
; We will parse and store the highest pointer together with its procedure (.LoadDecimal, .LoadOctal etc).
; First assume decimal format (%HighestProc=.LoadDecimal).
MOV RSI,[%NotationPtr]
.TryDecimal:
LODSB
CMP AL,0
JE .BidDecimal:
CMP AL,'_'
JE .TryDecimal:
CMP AL,'0'
JB .TestDecimal:
CMP AL,'9'
JBE .TryDecimal:
.TestDecimal: ; First non-digit character after decimal digits.
OR AL,0x20 ; Assume it's a decimal postfix, convert it to lower case 'd','k','m','g','t','p' or 'e'.
CMP AL,'e'
JE .NotDecimal:
Dispatch AL,'d','k','m','g','t','p'
DEC RSI
JMP .BidDecimal:
.d: MOV EAX,1
JMP .Exponent10:
.k: MOV EAX,1024
JMP .Exponent10:
.m: MOV EAX,1_048_576
JMP .Exponent10:
.g: MOV EAX,1_073_741_824
JMP .Exponent10:
.t: MOV RAX,1_099_511_627_776
JMP .Exponent10:
.p: MOV RAX,1_125_899_906_842_624
.Exponent10:
MOV [%MultiplierLO],RAX
SetSt [Status::],ArgPostfix
.BidDecimal: ; End of decimal format.
MOV [%HighestPtr],RSI
LEA RAX,[.LoadDecimal]
MOV [%HighestProc],RAX
.NotDecimal:
MOV RSI,[%NotationPtr]
XOR ECX,ECX ; RCX is nonzero when decimal point occured.
.TryFP:
LODSB
CMP AL,0
JE .BidFP:
CMP AL,'_'
JE .TryFP:
CMP AL,'.'
JNE .TP:
DEC RCX
JMP .TryFP:
.TP:CMP AL,'0'
JB .TestFP:
CMP AL,'9'
JBE .TryFP:
.TestFP:
CMP AL,'E' ; It should be 'E' or 'e', otherwise it's not FP when RCX=0.
JE .ContFP:
CMP AL,'e'
JE .ContFP:
TEST RCX
JNZ .BidFP:
.ContFP:
LODSB
CMP AL,'-'
JE .SignFP:
CMP AL,'+'
JNE .NoSignFP:
.SignFP:
LODSB
.NoSignFP:
CMP AL,'0'
JB .BidFP:
CMP AL,'9'
JBE .SignFP:
.BidFP:
DEC RSI
CMP RSI,[%HighestPtr]
JBE .NotFP:
LEA RAX,[.LoadFP]
MOV [%HighestProc],RAX
.NotFP:
MOV RSI,[%NotationPtr]
.TryHexa:
LODSB
CMP AL,'_'
JE .TryHexa:
CMP AL,'0'
JB .TestHexa:
CMP AL,'9'
JBE .TryHexa:
CMP AL,'A'
JB .TestHexa:
CMP AL,'F'
JBE .TryHexa:
CMP AL,'a'
JB .TestHexa:
CMP AL,'f'
JBE .TryHexa:
.TestHexa:
OR AL,0x20 ; Assume hexa postfix 'H', convert it to lower case.
CMP AL,'h'
JNE .NotHexa:
CMP RSI,[%HighestPtr]
JB .NotHexa:
SetSt [Status::],ArgPostfix
LEA RAX,[.LoadHexa]
MOV [%HighestProc],RAX
.NotHexa:
MOV RSI,[%NotationPtr]
.TryOctal:
LODSB
CMP AL,'_'
JE .TryOctal:
CMP AL,'0'
JB .TestOctal:
CMP AL,'7'
JBE .TryOctal:
.TestOctal:
OR AL,0x20 ; Assume octal postfix 'Q', convert it to lower case.
CMP AL,'q'
JNE .NotOctal:
CMP RSI,[%HighestPtr]
JB .NotOctal:
SetSt [Status::],ArgPostfix
LEA RAX,[.LoadOctal]
MOV [%HighestProc],RAX
.NotOctal:
MOV RSI,[%NotationPtr]
.TryBinary:
LODSB
CMP AL,'_'
JE .TryBinary:
CMP AL,'0'
JB .TestBinary:
CMP AL,'1'
JBE .TryBinary:
.TestBinary:
OR AL,0x20 ; Assume binary postfix 'B', convert it to lower case.
CMP AL,'b'
JNE .NotBinary:
SetSt [Status::],ArgPostfix
CMP RSI,[%HighestPtr]
JB .NotBinary:
LEA RAX,[.LoadBinary]
MOV [%HighestProc],RAX
.NotBinary:
MOV RAX,[%HighestProc]
MOV RSI,[%NotationPtr]
JMP RAX
.LoadDecimal:
XOR EAX,EAX
LODSB
CMP AL,0
JE .Multiplier:
CMP AL,'_'
JE .LoadDecimal:
SUB AL,'0'
JB .Multiplier:
CMP AL,9
JA .Multiplier:
PUSH RAX
Invoke FnMultiply,R8,R9,[Ten+0],[Ten+8]
POP R10
XOR R11,R11
Invoke FnAdd,R8,R9,R10,R11
JMP .LoadDecimal:
.Multiplier:
Invoke FnMultiply,R8,R9,[%MultiplierLO],[%MultiplierHI]
JMP .End:
.LoadHexa:
XOR EAX,EAX
LODSB
CMP AL,'_'
JE .LoadHexa:
SUB AL,'0'
JB .End:
CMP AL,9
JBE .Hex:
SUB AL,7
CMP AL,10
JB .End:
CMP AL,15
JB .Hex:
SUB AL,'a'-'A'
CMP AL,10
JB .End:
CMP AL,15
JA .End:
.Hex:
PUSH RAX
Invoke FnMultiply,R8,R9,[Sixteen+0],[Sixteen+8]
POP R10
XOR R11,R11
Invoke FnAdd,R8,R9,R10,R11
JMP .LoadHexa:
.LoadOctal:
XOR EAX,EAX
LODSB
CMP AL,'_'
JE .LoadOctal:
SUB AL,'0'
JB .End:
CMP AL,7
JA .End:
PUSH RAX
Invoke FnMultiply,R8,R9,[Eight+0],[Eight+8]
POP R10
XOR R11,R11
Invoke FnAdd,R8,R9,R10,R11
JMP .LoadOctal:
.LoadBinary:
XOR EAX,EAX
LODSB
CMP AL,'_'
JE .LoadBinary:
SUB AL,'0'
JB .End:
CMP AL,1
JA .End:
PUSH RAX
Invoke FnMultiply,R8,R9,[Two+0],[Two+8]
POP R10
XOR R11,R11
Invoke FnAdd,R8,R9,R10,R11
JMP .LoadBinary:
.LoadFP:
XOR ECX,ECX ; RCX is the decadic exponent.
.FP1:XOR EAX,EAX
LODSB
CMP AL,0
JE .FP5:
CMP AL,'_'
JE .FP1:
CMP AL,'.'
JNE .FP2:
TEST RCX
JS .End: ; Decimal point for the 2nd time - error.
DEC RCX ; RCX is below zero - decimal point occured.
JMP .FP1:
.FP2:SUB AL,'0'
JB .FP4:
CMP AL,9
JA .FP4:
TEST RCX ; Mantissa digit occured. If behind the decimal point, decrement RCX.
JZ .FP3: ; Decimal point didn't occured yet, keep RCX=0.
DEC RCX ; Otherwise decrement RCX for each digit in fraction.
.FP3:PUSH RAX,RCX ; Count the digit.
Invoke FnMultiply,R8,R9,[Ten+0],[Ten+8]
POP RCX,R10
XOR R11,R11
PUSH RCX
Invoke FnAdd,R8,R9,R10,R11
POP RCX
JMP .FP1:
.FP4:ADD AL,'0' ; Rollback. Mantissa digits ended. AL should be 'E', otherwise FP number ended.
TEST RCX ; Test if decimal digit occured.
JZ .FP6:
.FP5:INC RCX
JZ .FP6:
PUSH RAX,RCX ; RCX is now the negative exponent of 10.
Invoke FnMultiply,R8,R9,[OneTenth+0],[OneTenth+8]
POP RCX,RAX
JMP .FP5:
.FP6:; Decimal point is now solved.
OR AL,0x20 ; Expected 'e' or 'E'.
CMP AL,'e'
JNE .End:
LodD RSI ; Signed decadic exponent expected after 'E'.
INC RSI
TEST RAX
JZ .End:
MOV RDX,RAX
SAR RDX,32
JZ .FP8:
INC RDX
JZ .FP8:
DEC RSI ; Make an error, exponent is too big.
.FP8:TEST RAX
JZ .End:
JS .FP9:
PUSH RAX
Invoke FnMultiply,R8,R9,[Ten+0],[Ten+8]
POP RAX
DEC RAX
JMP .FP8:
.FP9:PUSH RAX
Invoke FnMultiply,R8,R9,[OneTenth+0],[OneTenth+8]
POP RAX
INC RAX
JMP .FP8:
.End:JSt [Status::],ArgPostfix,.90:
DEC RSI
.90:EndProcedure ParseNumber
EchoExpression:: PROC
.10:CMP RSI,RDX
JNB .90:
LODSQ
MOV R8,RAX
LODSQ
MOV R9,RAX
SHR RAX,32
CMP EAX,0x8000_0001
JNE .20:
MOV AL,'('
STOSB
JMP .10:
.20:CMP EAX,0x8000_0002
JNE .30:
MOV AL,')'
STOSB
JMP .10:
.30:CMP EAX,0x8000_0000
JE .40:
PUSH RDX,RSI
Invoke DisplayDecimal,R8,R9,RDI
POP RSI,RDX
DEC RDI ; Rollback at the terminating zero.
JMP .10:
.40:LEA RBX,[Dictionary]
MOVZX ECX,R8W
DEC ECX
LEA RCX,[2*RCX+RCX]
SAL ECX,3
MOV RAX,[RBX+RCX+O.Oper]
MOVZX CX,[RBX+RCX+O.OperSize]
.50:STOSB
SHR RAX,8
LOOP .50:
JMP .10:
.90:MOV EAX,0x0A0D3D20
STOSD
XOR EAX,EAX
STOSB
RET
ENDP EchoExpression
Deparent:: PROC
XOR EBX,EBX ; Priority.
JMPS .20:
.10:MOVSQ
MOVSQ
.20:CMP RSI,RDX
JNC .80:
MOV RAX,[RSI+8]
CMP EAX,0
JNE .10:
SHR RAX,32
CMP EAX,0x80000002
JE .50:
CMP EAX,0x80000001
JE .60:
CMP EAX,0x80000000
JNE .10:
ADD [RSI+0],EBX ; Modify the priority of function|operation.
CMPW [RSI+0],1 ; Function EUL?
JNE .40:
MOV RAX,[Eul+0]
STOSQ
MOV RAX,[Eul+8]
STOSQ
.30:ADD RSI,2*8
JMP .20:
.40:CMPW [RSI+0],2 ; Function PI?
JNE .10:
MOV RAX,[Pi+0]
STOSQ
MOV RAX,[Pi+8]
STOSQ
JMP .30:
.50:TEST EBX ; Right parenthesis.
STC
JZ .90:
SUB EBX,0x0010_0000
JMP .30:
.60:ADD EBX,0x0010_0000 ; Left parenthesis.
JMP .30:
.80:TEST EBX
JZ .90:
STC
.90:RET
ENDP Deparent
Calculate:: PROC
.00:XOR ECX,ECX ; Highest priority so far.
LEA RSI,[Nbuffer]
LEA RAX,[RSI+SIZE# N]
CMP RAX,[NbufferEnd]
JAE .80: ; Calculation done, only one N remains on Nbuffer.
SUB RSI,SIZE# N
.10:ADD RSI,SIZE# N
CMP RSI,[NbufferEnd]
JAE .20:
CMP [RSI+N.EXP],0x80000000 ; Is it number or function?
JNE .10: ; Searching for a function only.
MOVZXW EAX,[RSI+N.MAN0+2] ; Priority of this function.
CMP EAX,ECX
JBE .10:
MOV ECX,EAX
MOV RBX,RSI ; RBX is pointer to the best function so far.
JMP .10:
.20:MOVZXW EDX,[RBX+N.MAN0+0] ; RBX is pointer to the function with highest priority. EDX=O.OperId
SAL EDX,3
LEA RDI,[Dictionary+2*RDX+RDX-SIZE#O] ; RDI points to a dictionary line.
LEA RCX,[RBX-SIZE#N] ; RCX points at preceding N on Nbuffer (perhaps the left operand).
MOV R8,[RCX+0]
MOV R9,[RCX+8] ; R9:R8 is the potential preceding operand.
LEA RDX,[RBX+SIZE#N] ; RDX points at subsequent N on Nbuffer (perhaps the right operand).
MOV R10,[RDX+0]
MOV R11,[RDX+8] ; R11:R10 is the potential following operand.
MOV R15,[RDI+O.Handler]
MOVB AL,[RDI+O.Arity] ; Arity in AL decides of handler's arguments.
Dispatch AL,'U','B','R','1','0'
.0: Invoke R15 ; Function without operand (eul, pi).
MOV [RBX+0],R8
MOV [RBX+8],R9
JMP .00:
.1: ; Function with one operand (sin, exp etc).
.U: PUSH RBX
Invoke R15,R10,R11 ; Unary function (plus, minus).
POP RBX
JC .90:
MOV [RBX+0],R8
MOV [RBX+8],R9
LEA RSI,[RBX+2*SIZE# N]
LEA RDI,[RBX+1*SIZE# N]
MOV RCX,[NbufferEnd]
SUB RCX,RBX
SUB ECX,2*SIZE# N
REP MOVSB
MOV [NbufferEnd],RDI
JMP .00:
.B: PUSH RBX
Invoke R15,R8,R9,R10,R11 ; Binary function (addition, multiplication etc).
POP RBX
JC .90:
MOV [RBX-SIZE# N +0],R8
MOV [RBX-SIZE# N +8],R9
LEA RSI,[RBX+2*SIZE# N]
MOV RDI,RBX
MOV RCX,[NbufferEnd]
SUB RCX,RBX
SUB ECX,2*SIZE# N
REP MOVSB
MOV [NbufferEnd],RDI
JMP .00:
.R: PUSH RBX
Invoke R15,R8,R9 ; Reversed function (factorial).
POP RBX
JC .90:
MOV [RBX-SIZE# N +0],R8
MOV [RBX-SIZE# N +8],R9
LEA RSI,[RBX+1*SIZE# N]
MOV RDI,RBX
MOV RCX,[NbufferEnd]
SUB RCX,RBX
SUB ECX,1*SIZE# N
REP MOVSB
MOV [NbufferEnd],RDI
JMP .00:
.80:MOV R8,[RSI+0]
MOV R9,[RSI+8]
.90:RET
ENDP Calculate
FnPi Procedure
MOV R8,[Pi+0]
MOV R9,[Pi+8]
EndProcedure FnPi
FnEul Procedure
MOV R8,[Eul+0]
MOV R9,[Eul+8]
EndProcedure FnEul
FnFactorial:: Procedure NumberLO, NumberHI
PreviousHI LocalVar
PreviousLO LocalVar
FactorialHI LocalVar
FactorialLO LocalVar
XOR EAX,EAX
MOV [%FactorialHI],RAX
MOV [%PreviousHI],RAX
MOV AL,2
MOV [%FactorialLO],RAX
MOV [%PreviousLO],RAX ; %Previous and %Factorial are 2.
Invoke Compare,[%NumberLO],[%NumberHI],[Two+0],[Two+8]
JBE .80:
.50: MOV R10,[%PreviousLO]
MOV R11,[%PreviousHI]
INC R10
MOV [%PreviousLO],R10
MOV R8,[%FactorialLO]
MOV R9,[%FactorialHI]
Invoke FnMultiply,R8,R9,R10,R11
MOV [%FactorialLO],R8
MOV [%FactorialHI],R9
Invoke Compare,[%PreviousLO],[%PreviousHI],[%NumberLO],[%NumberHI]
JB .50:
.80: MOV R8,[%FactorialLO]
MOV R9,[%FactorialHI]
EndProcedure FnFactorial
FnPlus Procedure NumberLO, NumberHI
MOV R8,[%NumberLO]
MOV R9,[%NumberHI]
EndProcedure FnPlus
FnMinus Procedure NumberLO, NumberHI
MOV R8,[%NumberLO]
MOV R9,[%NumberHI]
CALL Negate:
EndProcedure FnMinus
FnAdd:: Procedure Addend1LO, Addend1HI, Addend2LO, Addend2HI
MOV R8,[%Addend1LO]
MOV R9,[%Addend1HI]
MOV R10,[%Addend2LO]
MOV R11,[%Addend2HI]
CALL UniteExponent
.10: MOV RAX,R8
MOV RDX,R9
MOV R12,R8
MOV R13,R9
ADD R8,R10
ADC R9D,R11D
JO .20:
SAR RDX,32
SAL RDX,32
ADD R9,RDX
JMP .90:
.20: ; Rollback: shift-right (decrease) MAN and increase EXP by 1.
MOV R8,R12
MOV R9,R13 ; Restore Addend1.
MOV RDX,R9
SHR RDX,32
INC RDX
SHL RDX,32
SAR R9D,1
RCR R8,1
JNC .30:
.30: MOV R9D,R9D
ADD R9,RDX
MOV RAX,R11
SHR RAX,32
INC RAX
SHL RAX,32
SAR R11D,1
RCR R10,1
JNC .40:
.40: MOV R11D,R11D
ADD R11,RAX
JMP .10:
.90: CLC
EndProcedure FnAdd
FnSubtract Procedure MinuendLO, MinuendHI, SubtrahendLO, SubtrahendHI
MOV R8,[%MinuendLO]
MOV R9,[%MinuendHI]
MOV R10,[%SubtrahendLO]
MOV R11,[%SubtrahendHI]
CALL UniteExponent
.10: MOV RAX,R8
MOV RDX,R9
SUB R8,R10
SBB R9D,R11D
JO .20:
SAR RDX,32
SAL RDX,32
ADD R9,RDX
JMP .90:
.20: ; Rollback; increase both EXP by 1, decrease MAN.
MOV RAX,R9
SAR R9D,1
RCR R8,1
JNC .30:
.30: MOV R9D,R9D
SAR RAX,32
INC RAX
SAL RAX,32
ADD R9,RAX
MOV RAX,R11
SAR R11D,1
RCR R10,1
JNC .40:
.40: MOV R11D,R11D
SAR RAX,32
INC RAX
SAL RAX,32
ADD R11,RAX
JMP .10:
.90: CLC
EndProcedure FnSubtract
FnMultiply Procedure Factor1LO, Factor1HI, Factor2LO, Factor2HI
Signums LocalVar
XOR ECX,ECX ; Negative values of both factors are converted to positive. The signs are kept in BH, BL.
Invoke Absolute, [%Factor2LO],[%Factor2HI] ; Convert negative number to positive R9:R8 and set CF if it was negative.
ADC CL,0
MOV R10,R8
MOV R11,R9
Invoke Absolute, [%Factor1LO],[%Factor1HI]; Convert negative number to positive R9:R8 and set CF if it was negative.
ADC CH,0
MOV [%Signums],RCX
XOR EBX,EBX ; Prepare exponent of the product.
XOR R14,R14 ; Prepare the result of mantissas multiplication to R14:R13:R12.
MOV RAX,R10
MUL R8
MOV R12,RAX
MOV R13,RDX
MOV EAX,R11D
MUL R8
ADD R13,RAX
ADC R14,RDX
ADC R15,0
MOV EAX,R9D
MUL R10
ADD R13,RAX
ADC R14,RDX
MOV EAX,R11D
MOV EDX,R9D
MUL RDX
ADD R14,RAX
JZ .40:
BSR RCX,R14 ; R14 is nonzero.
ADD CL,32+2
MOV EBX,ECX ; We need to shift MAN by ECX bits to the right.
JMP .50:
.40:TEST R13
JZ .70:
BSR RCX,R13 ; R14:R13 is nonzero.
SUB CL,32-2
JNA .70:
MOV EBX,ECX ; We need to shift MAN by ECX bits to the right.
.50:SHR R14,1
RCR R13,1
RCR R12,1
JNC .60:
.60:DEC ECX
JNZ .50:
.70:MOV RAX,R9 ; Manissa of the product is in bits 0..95.
SAR RAX,32
MOV RDX,R11
SAR RDX,32
ADD RAX,RDX ; Add exponents.
ADD RAX,RBX ; Add how many time was mantissa shifted to the right.
SAL RAX,32
MOV R8,R12
MOV R9D,R13D
ADD R9,RAX
MOV RCX,[%Signums]
XOR CL,CH
JZ .90:
CALL Negate
.90:CLC
EndProcedure FnMultiply
FnDivide Procedure DividendLO, DividendHI, DivisorLO, DivisorHI
Signum LocalVar ; Zero if Quotient will be positive.
DividendNxHI LocalVar ; Variables with Nx have exponent=0.
DividendNxLO LocalVar
DivisorNxHI LocalVar
DivisorNxLO LocalVar
QuotientNxHI LocalVar
QuotientNxLO LocalVar
XOR ECX,ECX
Invoke Absolute, [%DividendLO],[%DividendHI] ; Convert negative number to positive R9:R8 and set CF if it was negative.
ADC CL,0
MOV [%Signum],RCX
CALL MaximizeMantissa ; R9D:R8 od the dividend will be increased, EXP decreased.
MOV [%DividendLO],R8
MOV [%DividendHI],R9
MOV R9D,R9D ; Put zero to exponent.
MOV [%DividendNxLO],R8
MOV [%DividendNxHI],R9
MOV EAX,R9D
OR RAX,R8
JZ .80: ; Dividend=0, return 0.
XOR ECX,ECX ; Negative values of both factors are converted to positive.
Invoke Absolute, [%DivisorLO],[%DivisorHI] ; Convert negative number to positive R9:R8 and set CF if it was negative.
ADC CL,0 ; Result Signum is 0 or 1.
XOR [%Signum],RCX
CALL MaximizeMantissa
MOV EAX,R9D
OR RAX,R8
STC
JZ .90: ; Error when division by 0.
MOV [%DivisorLO],R8
MOV [%DivisorHI],R9
MOV RCX,R9
MOV EAX,ECX
SAR RCX,32
.10:CMP RAX,[%DividendNxHI]
JL .20:
SHR EAX,1 ; Mantissa of divident (DividendNx) is below mantissa of divisor (DivisorNx).
RCR R8,1 ; Decrement mantissa of divisor
INC RCX ; and increment its exponent.
JMP .10:
.20:SAL RCX,32
ADD RAX,RCX
MOV [%DivisorLO],R8
MOV [%DivisorHI],RAX
MOV EAX,EAX ; Put zero to exponent.
MOV [%DivisorNxLO],R8
MOV [%DivisorNxHI],RAX
MOV RSI,[One+0]
MOV RDI,[One+8]
MOV [%QuotientNxLO],RSI
MOV [%QuotientNxHI],RDI
MOV EDI,EDI ; EDI:RSI keeps walking 1 which walks from bit 94 to 0.
.30:OR [%QuotientNxLO],RSI
OR [%QuotientNxHI],RDI
Invoke FnMultiply,[%QuotientNxLO],[%QuotientNxHI],[%DivisorNxLO],[%DivisorNxHI]
Invoke Compare,R8,R9,[%DividendNxLO],[%DividendNxHI]
JE .50:
JB .40: ; Quotient is below, let it keep the walking1 and walk on.
XOR [%QuotientNxLO],RSI ; Quotient is above, remove walking1 EDI:RSI from quotient.
XOR [%QuotientNxHI],RDI
.40:SHR RDI,1 ; Shift the walking 1 one position to the right.
RCR RSI,1
JNC .30: ; Repeat 95 times.
.50:MOV R8,[%QuotientNxLO]
MOV R9,[%QuotientNxHI]
MOV RAX,[%DividendHI]
MOV RDX,[%DivisorHI]
SAR RAX,32
SAR RDX,32
SUB RAX,RDX
SAL RAX,32
ADD R9,RAX
TESTB [%Signum],1
JZ .80:
CALL Negate
.80:CLC
.90:EndProcedure FnDivide
FnSin:: Procedure AngleLO, AngleHI
Number LocalVar ; Ordinal member number 1, 3, 5, 7, 9,..
Signum LocalVar ; Nonzero for negative output.
PowerLO LocalVar ; Angle ^ Number.
PowerHI LocalVar
FactHI LocalVar ; Number !
FactLO LocalVar
ResultHI LocalVar ; Sum of members.
ResultLO LocalVar
PrevResultLO LocalVar ; Used for detection if the result does not change.
PrevResultHI LocalVar
MOV R8,[%AngleLO] ; Saturate Angle between 0 and 2*PI.
MOV R9,[%AngleHI]
.10:BT R9,31
JNC .20:
Invoke FnAdd,R8,R9,[TwoPi+0],[TwoPi+8] ; While Angle is negative.
JMP .10:
.20:MOV [%AngleLO],R8
MOV [%AngleHI],R9
Invoke Compare, R8,R9,[TwoPi+0],[TwoPi+8]
JB .30:
Invoke FnSubtract,[%AngleLO],[%AngleHI],[TwoPi+0],[TwoPi+8] ; While Angle is above 2*PI.
JMP .20:
.30:XOR ECX,ECX ; Angle is between 0 and 2*PI.
MOV [%Signum],RCX ; Sinus is positive when Angle 0..PI.
Invoke Compare, [%AngleLO],[%AngleHI],[Pi+0],[Pi+8]
JB .40:
XOR ECX,ECX ; Otherwise Angle is between PI..2*PI.
DEC RCX ; Signum is negative.
MOV [%Signum],RCX ; Result PI..2*PI is negative.
Invoke FnSubtract,[%AngleLO],[%AngleHI],[Pi+0],[Pi+8]
MOV [%AngleLO],R8
MOV [%AngleHI],R9
.40:MOV R8,[%AngleLO]
MOV R9,[%AngleHI] ; Angle is now between 0 and PI.
Invoke Compare, R8,R9,[HalfPi+0],[HalfPi+8]
JB .50:
Invoke FnSubtract,[%AngleLO],[%AngleHI],[HalfPi+0],[HalfPi+8] ; Angle was between PI/2 and PI.
Invoke FnSubtract,[HalfPi+0],[HalfPi+8],R8,R9 ; R9:R8 is now between 0 and PI/2. Angle is reversed.
MOV [%AngleLO],R8
MOV [%AngleHI],R9 ; Angle is between 0 and PI/2.
.50:; Initialize temporary results.
MOV R8,[%AngleLO]
MOV R9,[%AngleHI]
MOV [%ResultLO],R8
MOV [%ResultHI],R9
MOV [%PrevResultLO],R8
MOV [%PrevResultHI],R9
XOR EAX,EAX
MOV [%FactHI],RAX
INC EAX
MOV [%FactLO],RAX
MOV [%PowerLO],R8
MOV [%PowerHI],R9
MOV [%Number],RAX
.55:MOV R8,[%PowerLO] ; Compute %Power= %Power * %Angle * %Angle.
MOV R9,[%PowerHI]
Invoke FnMultiply,R8,R9,[%AngleLO],[%AngleHI]
Invoke FnMultiply,R8,R9,[%AngleLO],[%AngleHI]
MOV [%PowerLO],R8 ; Angle^3, Angle^5, Angle^7 etc
MOV [%PowerHI],R9
MOV RSI,[%Number] ; Compute %Fact = %Number ! = %Fact * (%Number+1) * (%Number+2)
XOR EDI,EDI
INC RSI
Invoke FnMultiply,[%FactLO],[%FactHI],RSI,RDI
INC RSI
MOV [%Number],RSI
Invoke FnMultiply,R8,R9,RSI,RDI
MOV [%FactLO],R8
MOV [%FactHI],R9
Invoke FnDivide,[%PowerLO],[%PowerHI],R8,R9 ; Compute %Power / %Factorial
JC .90:
MOV RCX,[%Number] ; Add or subtract the quotient R9:R8 to %Result.
SHR RCX,2
JC .60:
Invoke FnAdd,[%ResultLO],[%ResultHI],R8,R9
JMPS .70:
.60:Invoke FnSubtract,[%ResultLO],[%ResultHI],R8,R9
.70:MOV [%ResultLO],R8
MOV [%ResultHI],R9
PUSH R8,R9
Invoke Compare,R8,R9,[%PrevResultLO],[%PrevResultHI]
POP R9,R8
MOV [%PrevResultLO],R8
MOV [%PrevResultHI],R9
JNE .55: ; Compute the next member if they differ.
MOV R8,[%ResultLO]
MOV R9,[%ResultHI]
TESTB [%Signum],1
JZ .85:
CALL Negate
.85:CLC
.90:EndProcedure FnSin
FnCos Procedure AngleLO, AngleHI
Invoke FnAdd,[%AngleLO],[%AngleHI],[HalfPi+0],[HalfPi+8]
Invoke FnSin,R8,R9
EndProcedure FnCos
FnTan Procedure AngleLO, AngleHI
Invoke FnCos,[%AngleLO],[%AngleHI]
PUSH R8,R9
Invoke FnSin,[%AngleLO],[%AngleHI]
POP R11,R10
Invoke FnDivide,R8,R9,R10,R11
EndProcedure FnTan
FnCot Procedure AngleLO, AngleHI
Invoke FnSin,[%AngleLO],[%AngleHI]
PUSH R8,R9
Invoke FnCos,[%AngleLO],[%AngleHI]
POP R11,R10
Invoke FnDivide,R8,R9,R10,R11
EndProcedure FnCot
FnSec Procedure AngleLO, AngleHI
Invoke FnCos,[%AngleLO],[%AngleHI]
Invoke FnDivide,[One+0],[One+8],R8,R9
EndProcedure FnSec
FnCsc Procedure AngleLO, AngleHI
Invoke FnSin,[%AngleLO],[%AngleHI]
Invoke FnDivide,[One+0],[One+8],R8,R9
EndProcedure FnCsc
FnExp:: Procedure NumberLO, NumberHI
OrderLO LocalVar
OrderHI LocalVar
PowerLO LocalVar
PowerHI LocalVar
FactLO LocalVar
FactHI LocalVar
ResultHI LocalVar
ResultLO LocalVar
PrevResultLO LocalVar
PrevResultHI LocalVar
XOR EAX,EAX
XOR EDX,EDX
INC EAX
MOV [%ResultLO],RAX ; Initialize Result to 1.
MOV [%ResultHI],RDX
INC EAX
MOV [%FactLO],RAX ; Initialize Fact to 2.
MOV [%FactHI],RDX
MOV [%OrderLO],RAX ; Initialize Order to 2.
MOV [%OrderHI],RDX
MOV RAX,[%NumberLO]
MOV RDX,[%NumberHI]
BT RDX,31
JC .90:
MOV [%PowerLO],RAX ; Initialize Power to 1*Number.
MOV [%PowerHI],RDX
Invoke FnAdd,RAX,RDX,[%ResultLO],[%ResultHI]
MOV [%ResultLO],R8 ; Initialize Result to 1+Number.
MOV [%ResultHI],R9
.30:; Calculate Power = Power * Number
Invoke FnMultiply,[%PowerLO],[%PowerHI],[%NumberLO],[%NumberHI]
MOV [%PowerLO],R8
MOV [%PowerHI],R9 ; Power = Number ^ Order
Invoke FnDivide,R8,R9,[%FactLO],[%FactHI]
JC .90:
Invoke FnAdd,[%ResultLO],[%ResultHI],R8,R9
MOV [%ResultLO],R8
MOV [%ResultHI],R9
PUSH R8,R9
Invoke Compare,R8,R9,[%PrevResultLO],[%PrevResultHI]
POP R9,R8
MOV [%PrevResultLO],R8
MOV [%PrevResultHI],R9
JE .80:
; Increment the next Order. exp x = 1 + x + x^2/2! + x^3/3! + x^4/4! + ...
MOV RAX,[%OrderLO]
INC RAX
JZ .80:
MOV [%OrderLO],RAX
; Calculate Fact = Fact * Order
MOV R8,[%FactLO]
MOV R9,[%FactHI]
Invoke FnMultiply, R8,R9,[%OrderLO],[%OrderHI]
MOV [%FactLO],R8
MOV [%FactHI],R9 ; Fact = Order!
JMP .30:
.80:CLC
.90:EndProcedure FnExp
FnPower:: Procedure BaseLO, BaseHI, ExponentLO, ExponentHI
Invoke FnLn,[%BaseLO],[%BaseHI]
JC .90:
PUSH R8,R9 ; Temporarily save ln Base.
MOV R10,[%ExponentLO]
MOV R11,[%ExponentHI]
BT R11,31
JC .50: ; Jump if exponent was negative.
POP R9,R8
Invoke FnMultiply,R8,R9,R10,R11 ; ln Base * Exponent
Invoke FnExp,R8,R9 ; eul ^ (ln Base * Exponent)
JMP .90:
.50: Invoke Absolute,R10,R11 ; Exponent was negative. Put its absolute value to R9:R8.
POP R11,R10
Invoke FnMultiply,R8,R9,R10,R11 ; -Exponent * ln Base
Invoke FnExp,R8,R9 ; eul ^ (ln Base * -Exponent)
Invoke FnDivide,[One+0],[One+8],R8,R9 ; 1 / (eul ^ (ln Base * -Exponent))
.90: EndProcedure FnPower
FnLn:: Procedure NumberLO, NumberHI
DeltaLO LocalVar
DeltaHI LocalVar
ResultHI LocalVar
ResultLO LocalVar
PrevResultLO LocalVar
PrevResultHI LocalVar
ClearLocalVar
MOV EAX,1
MOV [%DeltaLO],RAX ; Start with delta=1.
MOV R8,[%NumberLO]
MOV R9,[%NumberHI]
BT R9,31
JC .90:
TEST R8
JNZ .20:
TEST R9D
JNZ .20:
STC
JMP .90:
.20 Invoke Compare,[%NumberLO],[%NumberHI],[One+0],[One+8]
JNE .30:
XOR R8,R8
XOR R9,R9
JMP .90:
.30:Invoke Compare,[%NumberLO],[%NumberHI],[Eul+0],[Eul+8]
JB .40:
Invoke FnAdd,[%ResultLO],[%ResultHI],[%DeltaLO],[%DeltaHI]
MOV [%ResultLO],R8
MOV [%ResultHI],R9
Invoke FnMultiply,[%NumberLO],[%NumberHI],[OneEulth+0],[OneEulth+8]
MOV [%NumberLO],R8
MOV [%NumberHI],R9
JMP .30:
.40:Invoke Compare,[%NumberLO],[%NumberHI],[%DeltaLO],[%DeltaHI]
JA .50:
Invoke FnSubtract,[%ResultLO],[%ResultHI],[%DeltaLO],[%DeltaHI]
MOV [%ResultLO],R8
MOV [%ResultHI],R9
Invoke FnMultiply,[%NumberLO],[%NumberHI],[Eul+0],[Eul+8]
MOV [%NumberLO],R8
MOV [%NumberHI],R9
JMP .40:
.50: ; 1 < %Number < Eul
Invoke FnMultiply, [%DeltaLO],[%DeltaHI],[Half+0],[Half+8]
MOV [%DeltaLO],R8
MOV [%DeltaHI],R9
MOV R8,[%NumberLO]
MOV R9,[%NumberHI]
Invoke FnMultiply,R8,R9,R8,R9
MOV [%NumberLO],R8
MOV [%NumberHI],R9
Invoke Compare,R8,R9,[Eul+0],[Eul+8]
JB .50:
Invoke FnMultiply,[%NumberLO],[%NumberHI],[OneEulth+0],[OneEulth+8]
MOV [%NumberLO],R8
MOV [%NumberHI],R9
Invoke FnAdd,[%ResultLO],[%ResultHI],[%DeltaLO],[%DeltaHI]
MOV [%ResultLO],R8
MOV [%ResultHI],R9
PUSH R8,R9
Invoke Compare,R8,R9,[%PrevResultLO],[%PrevResultHI]
POP R9,R8
MOV [%PrevResultLO],R8
MOV [%PrevResultHI],R9
JNE .50:
CLC
.90:EndProcedure FnLn
FnLg:: Procedure NumberLO, NumberHI
MOV R8,[%NumberLO]
MOV R9,[%NumberHI]
BT R9,31
JC .90:
Invoke FnLn,R8,R9
JC .90:
Invoke FnMultiply,R8,R9,[OneLn10th+0],[OneLn10th+8]
.90:EndProcedure FnLg
%DROPMACRO Compare
Compare Procedure Number1LO, Number1HI, Number2LO, Number2HI
MOV R8,[%Number1LO]
MOV R9,[%Number1HI]
MOV R10,[%Number2LO]
MOV R11,[%Number2HI]
CALL UniteExponent
MOV EAX,R9D
MOV EDX,R11D
CMP EAX,EDX
JNE .90:
CMP R8,R10
.90:EndProcedure Compare
-123_456.789_012_345_678_901_234_567_E+2147483647. It should be at least 50 bytes long.
DisplayDecimalProxy:: PROC
LEA RDI,[OutBuffer]
Invoke DisplayDecimal,R8,R9,RDI
RET
ENDP DisplayDecimalProxy::
DisplayDecimal:: Procedure dNumberLO, dNumberHI, OutBuffer
Temporary LocalVar Size=32
FirstSdigit LocalVar ; Pointer to the 1st significant (nonzero) digit within Temporary.
LastSdigit LocalVar ; Pointer to the last significant (nonzero) digit within Temporary.
PowerOf10 LocalVar ; Decadic exponent of the input number. May be displayed with E modifier.
DecPoint LocalVar ; (Virtual) pointer to the decimal point within Temporary. May be outside the array.
ClearLocalVar
Invoke Absolute, [%dNumberLO], [%dNumberHI]
MOV [%dNumberHI],R9
MOV [%dNumberLO],R8
MOV RDI,[%OutBuffer]
MOV AL,'+'
JNB .13: ; Absolute will set CF=1 on negative number.
MOV AL,'-'
STOSB
.13:MOV [%OutBuffer],RDI
Invoke ZeroExponent, [%dNumberLO], [%dNumberHI]
MOV [%PowerOf10],RDI ; Decimal exponent of the number.
LEA RDI,[%Temporary]
CALL ConvertToDecimal ; Fill the Temporary array with max. 29 digits. RDI is left behind the last digit.
ADD RDI,[%PowerOf10]
MOV [%DecPoint],RDI ; Virtual pointer to the decimal point. It may be outside the Temporary array.
XOR RAX,RAX
MOV [%PowerOf10],RAX
; Find the first significant digit - the first nonzero digit from the left in Temporary.
MOV AL,'0'
LEA RDI,[%Temporary]
MOV RCX,29
REPE SCASB
JZ .17:
DEC RDI
.17:MOV [%FirstSdigit],RDI
LEA RAX,[%Temporary+29]
CMP RDI,RAX
JNE .18:
MOV AL,'0'
MOV RDI,[%OutBuffer]
STOSB
JMP .85:
.18:; Find the last significant digit - the first nonzero digit from the right.
LEA RCX,[%Temporary+28]
MOV RDI,RCX
SUB RCX,[%FirstSdigit]
JNA .20:
STD
REPE SCASB
CLD
JZ .20:
INC RDI
.20:MOV [%LastSdigit],RDI
; When there are more than 25 significant digits, round the unprecise three last digits.
SUB RDI,[%FirstSdigit]
CMP EDI,25
JB .33: ; No rounding.
LEA RDX,[%Temporary+26]
MOV EAX,'0000'
CMPB [RDX],'5'
MOV [RDX],EAX
JB .27:
.23:DEC RDX
INCB [RDX]
CMPB [RDX],'9'
JBE .27:
MOVB [RDX],'0'
JMP .23:
.27:; The rounding is done. Find the FirstSdigit and LastSdigit again.
MOV AL,'0'
LEA RDI,[%Temporary]
MOV RCX,29
REPE SCASB
JZ .28:
DEC RDI
.28:MOV [%FirstSdigit],RDI
LEA RCX,[%Temporary+28]
MOV RDI,RCX
SUB RCX,[%FirstSdigit]
JNA .30:
STD
REPE SCASB
CLD
JZ .30:
INC RDI
.30:MOV [%LastSdigit],RDI
.33:; Find the appropriate format of the result.
MOV RDI,[%OutBuffer]
; Natural format has no decimal point and no exponent, e.g. 12_345_678,
; it is used when DecPoint is above LastSdigit and below Temporary+30
MOV RDX,[%DecPoint]
CMP RDX,[%LastSdigit]
JNA .47:
LEA RAX,[%Temporary+30]
CMP RDX,RAX
JNB .47:
MOV RAX,RDX ; Digits between FirstSdigit and DecPoint.
MOV RSI,[%FirstSdigit]
SUB RAX,RSI
XOR EDX,EDX
MOV EBX,3
DIV RBX
MOV EBX,EAX
MOV ECX,EDX ; Remainder.
JRCXZ .36:
REP MOVSB
TEST EBX
JZ .43:
MOV AL,'_'
STOSB
.36:MOV ECX,EBX ; Dividend.
.40:JRCXZ .43:
MOVSB
MOVSB
MOVSB
CMP ECX,1
JE .43:
MOV AL,'_'
STOSB
LOOP .40:
.43:JMP .85:
.47: ; Format with nonzero integer part, decimal point and decimal part, e.g. 1_234.56 is used
; when DecPoint is above FirstSdigit and
; when DecPoint is between Temporary and Temporary+25.
MOV RDX,[%DecPoint]
CMP RDX,[%FirstSdigit]
JNA .63:
LEA RAX,[%Temporary+0]
CMP RDX,RAX
JB .63:
LEA RAX,[%Temporary+25]
CMP RDX,RAX
JA .63:
; Digits between FirstSdigit and DecPoint.
MOV RAX,RDX ; DecPoint.
MOV RSI,[%FirstSdigit]
SUB RAX,RSI
XOR EDX,EDX
MOV EBX,3
DIV RBX
MOV EBX,EAX
MOV ECX,EDX ; Remainder.
JRCXZ .50:
REP MOVSB
.50:MOV ECX,EBX ; Dividend.
JRCXZ .57:
TEST EDX
JZ .53:
MOV AL,'_'
STOSB
.53:JRCXZ .57:
MOVSB
MOVSB
MOVSB
CMP ECX,1
JBE .57:
MOV AL,'_'
STOSB
LOOP .53:
.57:MOV AL,'.'
STOSB
MOV RSI,[%DecPoint]
MOV RDX,[%LastSdigit]
MOV AL,'_'
.60:MOVSB
CMP RSI,RDX
JA .85:
MOVSB
CMP RSI,RDX
JA .85:
MOVSB
CMP RSI,RDX
JA .85:
STOSB
JMP .60:
.63: ; Format with zero integer part, decimal point and decimal part, e.g. 0.012_34 is used
; when DecPoint is below or equal FirstSdigit and
; when DecPoint is between Temporary and Temporary+25.
MOV RDX,[%DecPoint]
CMP RDX,[%FirstSdigit]
JNBE .70:
LEA RAX,[%Temporary+0]
CMP RDX,RAX
JB .70:
LEA RAX,[%Temporary+25]
CMP RDX,RAX
JA .70:
MOV AX,'0.'
STOSW
MOV RSI,[%DecPoint]
MOV RDX,[%LastSdigit]
MOV AL,'_'
.67:MOVSB
CMP RSI,RDX
JA .85:
MOVSB
CMP RSI,RDX
JA .85:
MOVSB
CMP RSI,RDX
JA .85:
STOSB
JMP .67:
.70:; Otherwise a scientific format is used, which has exponent and
; decimal point right behind the first significant digit, e.g. 1.234_56_E+123
MOV RDX,[%DecPoint]
MOV RSI,[%FirstSdigit]
LEA RAX,[RSI+1]
SUB RDX,RAX
SUB [%DecPoint],RDX
ADD [%PowerOf10],RDX
MOVSB
MOV AL,'.'
STOSB
MOV RDX,[%LastSdigit]
CMP RSI,RDX
JNA .73:
MOV AL,'0'
STOSB
JMP .77:
.73:CMP RSI,RDX
JA .77:
MOVSB
CMP RSI,RDX
JA .77:
MOVSB
CMP RSI,RDX
JA .77:
MOVSB
CMP RSI,RDX
JA .77:
MOV AL,'_'
STOSB
JMP .73:
.77:MOV RDX,[%PowerOf10]
TEST RDX
JZ .85:
MOV AX,'_E'
STOSW
JS .80:
MOV AL,'+'
STOSB
.80:MOV RAX,RDX
StoD RDI
.85:XOR EAX,EAX
STOSB
.90:EndProcedure DisplayDecimal
Absolute Procedure aNumberLO, aNumberHI
MOV R9,[%aNumberHI]
MOV R8,[%aNumberLO]
TEST R9D
JNS .90:
MOV RAX,R9
NOT R8
NOT R9D
ADD R8,1
ADC R9D,0
SHR RAX,32
SHL RAX,32
ADD R9,RAX
STC
.90:EndProcedure Absolute
ConvertToDecimal PROC
; 1 5 10 15 20 25 30 35 40
M %FOR 0x0000_0000_204F_CE5E__3E25_0261_1000_0000, \ 10^28
0x0000_0000_033B_2E3C__9FD0_803C_E800_0000, \ 10^27
0x0000_0000_0052_B7D2__DCC8_0CD2_E400_0000, \ 10^26
0x0000_0000_0008_4595__1614_0148_4A00_0000, \ 10^25
0x0000_0000_0000_D3C2__1BCE_CCED_A100_0000, \ 10^24
0x0000_0000_0000_152D__02C7_E14A_F680_0000, \ 10^23
0x0000_0000_0000_021E__19E0_C9BA_B240_0000, \ 10^22
0x0000_0000_0000_0036__35C9_ADC5_DEA0_0000, \ 10^21
0x0000_0000_0000_0005__6BC7_5E2D_6310_0000, \ 10^20
0x0000_0000_0000_0000__8AC7_2304_89E8_0000, \ 10^19
0x0000_0000_0000_0000__0DE0_B6B3_A764_0000, \ 10^18
0x0000_0000_0000_0000__0163_4578_5D8A_0000, \ 10^17
0x0000_0000_0000_0000__0023_86F2_6FC1_0000, \ 10^16
0x0000_0000_0000_0000__0003_8D7E_A4C6_8000, \ 10^15
0x0000_0000_0000_0000__0000_5AF3_107A_4000, \ 10^14
0x0000_0000_0000_0000__0000_0918_4E72_A000, \ 10^13
0x0000_0000_0000_0000__0000_00E8_D4A5_1000, \ 10^12
0x0000_0000_0000_0000__0000_0017_4876_E800, \ 10^11
0x0000_0000_0000_0000__0000_0002_540B_E400, \ 10^10
0x0000_0000_0000_0000__0000_0000_3B9A_CA00, \ 10^09
0x0000_0000_0000_0000__0000_0000_05F5_E100, \ 10^08
0x0000_0000_0000_0000__0000_0000_0098_9680, \ 10^07
0x0000_0000_0000_0000__0000_0000_000F_4240, \ 10^06
0x0000_0000_0000_0000__0000_0000_0001_86A0, \ 10^05
0x0000_0000_0000_0000__0000_0000_0000_2710, \ 10^04
0x0000_0000_0000_0000__0000_0000_0000_03E8, \ 10^03
0x0000_0000_0000_0000__0000_0000_0000_0064, \ 10^02
0x0000_0000_0000_0000__0000_0000_0000_000A ; 10^01
MOV R11,%M[1..21]
MOV R10,0x%M[24..42]
CALL .OneDigit
%ENDFOR M
MOV EAX,R8D
OR AL,'0'
STOSB
JMP .90:
.OneDigit:
MOV AL,'0'
.30: INC AL
SUB R8,R10
SBB R9D,R11D
JNB .30:
DEC AL ; Rollback.
ADD R8,R10
ADC R9D,R11D
STOSB
.90: RET
ENDP ConvertToDecimal
MaximizeMantissa PROC
MOV RDX,R9
SAR RDX,32
MOV EAX,R9D
OR RAX,R8
JZ .90:
MOV R9D,R9D ; Clear EXP.
.10: DEC RDX
SAL R8,1
RCL R9D,1
JNO .10:
INC RDX ; Rollback the overflow.
RCR R9D,1
RCR R8,1
SAL RDX,32
ADD R9,RDX
.90: RET
ENDP MaximizeMantissa
NormalizeMantissa PROC
.10: MOV EAX,R9D
SAR EAX,28
JZ .90:
INC EAX
JZ .90:
MOV RAX,R9
SAR RAX,32
INC RAX
SAL RAX,32
SAR R9D,1
RCR R8,1
JNC .50:
.50: ADD R9,RAX
JMP .10:
.90: RET
ENDP NormalizeMantissa
MinimizeMantissa PROC
MOV EAX,R9D
OR RAX,R8 ; Test if mantissa is 0.
JNZ .30:
XOR R9,R9 ; Clear EXP if so.
JMP .90:
.30: TEST R8,1 ; Test LSb.
JNZ .50:
MOV RAX,R9
SAR RAX,32
INC RAX
SAL RAX,32
SAR R9D,1
RCR R8,1
ADD R9,RAX
JMP .30:
.50: MOV EAX,R9D ; Mantissa will further decrease only when its 4 MS bits are unequal to one another.
SAR EAX,28
JZ .90: ; End when EAX=0.
INC EAX
JZ .90: ; End when EAX=-1.
MOV RAX,R9
SAR RAX,32
INC RAX ; Increment exponent.
SAL RAX,32
SAR R9D,1 ; Decrease mantissa.
RCR R8,1
JNC .60:
.60: ADD R9,RAX
JMP .50:
.90: RET
ENDP MinimizeMantissa
UniteExponent PROC
MOV RCX,R9
SAR RCX,32 ; EXP of R9:R8.
MOV RDX,R11
SAR RDX,32 ; EXP of R11:R10.
MOV RAX,RCX
SUB RAX,RDX
JZ .80: ; Both EXP are identical.
JL .40:
.10: TEST RAX
JZ .80:
DEC RAX ; EXP of R9:R8 is greater. Decrease it by at most RAX, but not to overflow it.
DEC RCX ; Decrease EXP of R9:R8.
SAL R8,1 ; Increase MAN of R9:R8, so we could decrease EXP.
RCL R9D,1
JNO .10:
INC RAX ; Rollback, since overflow occured.
INC RCX
RCR R9D,1
RCR R8,1
.20: TEST RAX ; Cannot further increase MAN of R9:R8, thus we need to decrease MAN of R11:R10 by RAX.
JZ .80:
CMP R10,0
JNE .30:
CMP R11D,0
JNE .30:
ADD RDX,RAX ; MAN of R11:R10 is 0, so there's no need to increase EXP=RDX one by one.
JMP .80:
.30: DEC RAX
INC RDX ; Increase EXP of R11:R10.
SAR R11D,1 ; Decrease MAN of R11:R10, so we could increase EXP.
RCR R10,1
JNC .20:
JMP .20:
.40: NEG RAX
.50: TEST RAX
JZ .80:
DEC RAX ; EXP of R11:R10 is greater. Decrease it by at most RAX, but not to overflow it.
DEC RDX ; Decrease EXP of R11:R10.
SAL R10,1 ; Increase MAN of R11:R10, so we could decrease EXP.
RCL R11D,1
JNO .50:
INC RAX ; Rollback, since overflow occured.
RCR R11D,1
RCR R10,1
.60: TEST RAX ; Cannot further increase MAN of R11:R10, thus decrease MAN of R9:R8 by RAX.
JZ .80:
CMP R8,0
JNE .70:
CMP R9D,0
JNE .70:
ADD RCX,RAX ; MAN of R8:R9 is 0, so there's no need to increase EXP=RCX one by one.
JMP .80:
.70: DEC RAX
INC RCX ; Increase EXP of R8:R9.
SAR R9D,1 ; Decrease MAN of R8:R9, so we could increase EXP.
RCR R8,1
JMP .60:
.80: MOV R9D,R9D ; Clear old EXP in R9.
SAL RCX,32
ADD R9,RCX
MOV R11D,R11D ; Clear old EXP in R11.
SAL RDX,32
ADD R11,RDX
RET
ENDP UniteExponent
ZeroExponent:: Procedure zeNumberLO, zeNumberHI
SUB EDI,EDI ; This will be power of 10 to multiply the output number.
MOV R8,[%zeNumberLO]
MOV R9,[%zeNumberHI]
TEST R8
JNZ .20:
TEST R9D
JNZ .20:
XOR R9,R9
JMP .90:
.20:CALL NormalizeMantissa
MOV RCX,R9
SAR RCX,32
JZ .90: ; Done, EXP=0.
JS .50: ; EXP is negative; multiply N by 10 and decrement RDI.
CMP ECX,4 ; EXP is positive; divide N by 10 and increment RDI,
JB .70: ; unless EXP is a small positive.
INC RDI
Invoke FnMultiply, [%zeNumberLO],[%zeNumberHI],[OneTenth+0],[OneTenth+8]
.40:CALL NormalizeMantissa
MOV [%zeNumberLO],R8
MOV [%zeNumberHI],R9
JMP .20:
.50:DEC RDI ; N.EXP<0, multiply by 10.
Invoke FnMultiply, [%zeNumberLO],[%zeNumberHI],[Ten+0],[Ten+8]
JMP .40:
.70:JRCXZ .90: ; RCX=0..3.
SAL R8,1 ; RCX=1..3. Right-shift mantissa RCX times.
RCL R9D,1
LOOP .70:
.90:EndProcedure ZeroExponent ; EDI is power of 10 to multiply the output number R9:R8.
Negate PROC
MOV RAX,R9
SHR RAX,32
SHL RAX,32
NOT R8
NOT R9D
INC R8
JNZ .90:
INC R9D
.90: ADD R9,RAX
RET
ENDP Negate
ENDPROGRAM calcmain