;******************************************************************************* ;* Module : STAKMATH.SUB ;* Programmer: Tony Papadimitriou <tonyp@acm.org> ;* Purpose : 32-bit (or 16-bit) stack-based basic math routines (RPN style) ;* Language : Motorola/Freescale/NXP 68HC11 Assembly Language (aspisys.com/ASM11) ;* Status : FREEWARE Copyright (c) 2020 by Tony Papadimitriou <tonyp@acm.org> ;* Note(s) : Use: #Include stkmth32.sub ;* : ;* : Externally defined symbol(s): ;* : ;* : By (re)defining the symbol MATHSIZE (to 16) you get 16-bit code, ;* : instead of the default 32-bit code. ;* : ;* : You can include both versions in your program by doing this: ;* : ;* : #Include stkmth32.sub ;* : MATHSIZE set 16 ;redefine for 16-bit use ;* : #Include stkmth32.sub ;* : ;* : To use the 16-bit version of each call, use these symbols: ;* : ;* : StackAdd16, StackSub16, StackMul16, StackDiv16, StackMod16 ;* : StackSwap16, StackNegate16, StackLoad16, StackSave16 ;* : ;* : To use the 32-bit version of each call, use these symbols: ;* : ;* : StackAdd32, StackSub32, StackMul32, StackDiv32, StackMod32 ;* : StackSwap32, StackNegate32, StackLoad32, StackSave32 ;* : ;* : All routines are re-entrant and work on either the top-of-stack ;* : (TOS) number alone or the two top-most TOS numbers. Similar to ;* : how Reverse Polish Notation (RPN) works. First, you stack the ;* : operands and then call the corresponding routine to perform the ;* : operation. ;* : ;* : The result of any operation is always left on top of stack. ;* : For operations that require two operands and produce a single ;* : result, the result replaces the two operands (stack is reduced). ;* : ;* : Chain calculations are possible by pushing only the new operand ;* : between operations. Alternatively, you can push all operands in ;* : advance but it's not recommended as stack space may be limited. ;* : When done evaluating an expression, just pull the final 32-bit ;* : result from the stack. ;* : ;* : The TOS is the first (or only) operand of any operation. ;* : This is important, for example, for subtraction and division. ;* : ;* : If the current order of the stack is not as you want it, use the ;* : Swap (StackSwap32) operation to change it. ;* : ;* : All operands and results are exactly 32-bit wide. Overflows are ;* : simply truncated. The lower 32-bit number is valid, though. ;* : ;* : (All references to 32-bit become 16-bit, when MATHSIZE = 16) ;* : ;* History : 09.12.07 v1.00 Original (Based on 2009.12.05 HC08/9S08 version) ;* : 10.01.22 v1.01 Added Save operation for completeness ;* : 11.04.18 Moved test code at EOF (for #EXIT optimization) ;* : 18.05.16 Changed #Error to #Fatal about unsupported MATHSIZE ;******************************************************************************* #ifmain ;----------------------------------------------------------------------- #ListOff #Uses mcu.inc #ListOn #MapOff #endif ;------------------------------------------------------------------------ MATHSIZE def 32 ;default wordsize is 32-bit #ifz MATHSIZE MATHSIZE set 16 ;assumed wordsize when zero #endif ? macro BitSize #if MATHSIZE = ~1~ ?WORD equ ~1~/8 _STKMTH{MATHSIZE}_ ;;specific version included _STAKMATH_ def * ;;any version included #endif endm @? 16 ;16-bit quantity (on request) @? 32 ;32-bit quantity (default) #ifndef ?WORD #Fatal Unsupported MATHSIZE ({MATHSIZE}-bit) #endif #Message MATHSIZE = {MATHSIZE}-bit version #ifdef SIGNED #Message Signed routines enabled #endif ;******************************************************************************* ; Macros to make operations as simple as with a high-level language ; In operations that require two operands and a result, if only one operand is ; provided, then the operation is done completely on stack (no other variables ; used). For example, @Add32 A,B,SUM adds A to B and places result in SUM, ; while @Add32 A adds the current stack top to A and leaves result on stack. ; You can use @Load* and @Save* to load the initial value, and store the final ; result, respectively. (Replace * with 16, 24, 32, 40, 48, or 64) ;******************************************************************************* #if MATHSIZE = 16 Load16 macro #Number|Variable ;load constant or variable @_DoLoad ~0.{:0-1}~\,~@~ endm Save16 macro Address @_DoSave 16\,~@~ endm Copy16 macro #Constant|Variable,ToAddress mreq 1,2:#Constant|Variable,ToAddress @@Load16 ~1~ @Save16 ~2~ endm Swap16 macro @_DoSwap 16 endm Add16 macro Addend,Adder,Sum @_DoMath ~0~\,16\,~1~\,~2~\,~3~ endm Sub16 macro Minuend,Subtrahend,Difference @_DoMath ~0~\,16\,~1~\,~2~\,~3~ endm Mul16 macro Multiplicand,Multiplier,Product @_DoMath ~0~\,16\,~1~\,~2~\,~3~ endm Div16 macro Dividend,Divisor,Quotient @_DoMath ~0~\,16\,~1~\,~2~\,~3~ endm Mod16 macro Dividend,Divisor,Remainder @_DoMath ~0~\,16\,~1~\,~2~\,~3~ endm Abs16 macro Source[,Destination] @_DoAbs 16\,~1~\,~2~ endm Neg16 macro Source[,Destination] @_DoNeg 16\,~1~\,~2~ endm #endif ;------------------------------------------------------------------------------- #if MATHSIZE = 24 Load24 macro #Number|Variable ;load constant or variable @_DoLoad ~0.{:0-1}~\,~@~ endm Save24 macro Address @_DoSave 24\,~@~ endm Copy24 macro #Constant|Variable,ToAddress mreq 1,2:#Constant|Variable,ToAddress @@Load24 ~1~ @Save24 ~2~ endm Swap24 macro @_DoSwap 24 endm Add24 macro Addend,Adder,Sum @_DoMath ~0~\,24\,~1~\,~2~\,~3~ endm Sub24 macro Minuend,Subtrahend,Difference @_DoMath ~0~\,24\,~1~\,~2~\,~3~ endm Mul24 macro Multiplicand,Multiplier,Product @_DoMath ~0~\,24\,~1~\,~2~\,~3~ endm Div24 macro Dividend,Divisor,Quotient @_DoMath ~0~\,24\,~1~\,~2~\,~3~ endm Mod24 macro Dividend,Divisor,Remainder @_DoMath ~0~\,24\,~1~\,~2~\,~3~ endm Abs24 macro Source[,Destination] @_DoAbs 24\,~1~\,~2~ endm Neg24 macro Source[,Destination] @_DoNeg 24\,~1~\,~2~ endm #endif ;------------------------------------------------------------------------------- #if MATHSIZE = 32 Load32 macro #Number|Variable ;load constant or variable @_DoLoad ~0.{:0-1}~\,~@~ endm Save32 macro Address @_DoSave 32\,~@~ endm Copy32 macro #Constant|Variable,ToAddress mreq 1,2:#Constant|Variable,ToAddress @@Load32 ~1~ @Save32 ~2~ endm Swap32 macro @_DoSwap 32 endm Add32 macro Addend,Adder,Sum @_DoMath ~0~\,32\,~1~\,~2~\,~3~ endm Sub32 macro Minuend,Subtrahend,Difference @_DoMath ~0~\,32\,~1~\,~2~\,~3~ endm Mul32 macro Multiplicand,Multiplier,Product @_DoMath ~0~\,32\,~1~\,~2~\,~3~ endm Div32 macro Dividend,Divisor,Quotient @_DoMath ~0~\,32\,~1~\,~2~\,~3~ endm Mod32 macro Dividend,Divisor,Remainder @_DoMath ~0~\,32\,~1~\,~2~\,~3~ endm Abs32 macro Source[,Destination] @_DoAbs 32\,~1~\,~2~ endm Neg32 macro Source[,Destination] @_DoNeg 32\,~1~\,~2~ endm #endif ;------------------------------------------------------------------------------- #if MATHSIZE = 40 Load40 macro #Number|Variable ;load constant or variable @_DoLoad ~0.{:0-1}~\,~@~ endm Save40 macro Address @_DoSave 40\,~@~ endm Copy40 macro #Constant|Variable,ToAddress mreq 1,2:#Constant|Variable,ToAddress @@Load40 ~1~ @Save40 ~2~ endm Swap40 macro @_DoSwap 40 endm Add40 macro Addend,Adder,Sum @_DoMath ~0~\,40\,~1~\,~2~\,~3~ endm Sub40 macro Minuend,Subtrahend,Difference @_DoMath ~0~\,40\,~1~\,~2~\,~3~ endm Mul40 macro Multiplicand,Multiplier,Product @_DoMath ~0~\,40\,~1~\,~2~\,~3~ endm Div40 macro Dividend,Divisor,Quotient @_DoMath ~0~\,40\,~1~\,~2~\,~3~ endm Mod40 macro Dividend,Divisor,Remainder @_DoMath ~0~\,40\,~1~\,~2~\,~3~ endm Abs40 macro Source[,Destination] @_DoAbs 40\,~1~\,~2~ endm Neg40 macro Source[,Destination] @_DoNeg 40\,~1~\,~2~ endm #endif ;------------------------------------------------------------------------------- #if MATHSIZE = 48 Load48 macro #Number|Variable ;load constant or variable @_DoLoad ~0.{:0-1}~\,~@~ endm Save48 macro Address @_DoSave 48\,~@~ endm Copy48 macro #Constant|Variable,ToAddress mreq 1,2:#Constant|Variable,ToAddress @@Load48 ~1~ @Save48 ~2~ endm Swap48 macro @_DoSwap 48 endm Add48 macro Addend,Adder,Sum @_DoMath ~0~\,48\,~1~\,~2~\,~3~ endm Sub48 macro Minuend,Subtrahend,Difference @_DoMath ~0~\,48\,~1~\,~2~\,~3~ endm Mul48 macro Multiplicand,Multiplier,Product @_DoMath ~0~\,48\,~1~\,~2~\,~3~ endm Div48 macro Dividend,Divisor,Quotient @_DoMath ~0~\,48\,~1~\,~2~\,~3~ endm Mod48 macro Dividend,Divisor,Remainder @_DoMath ~0~\,48\,~1~\,~2~\,~3~ endm Abs48 macro Source[,Destination] @_DoAbs 48\,~1~\,~2~ endm Neg48 macro Source[,Destination] @_DoNeg 48\,~1~\,~2~ endm #endif ;------------------------------------------------------------------------------- #if MATHSIZE = 64 Load64 macro #Number|Variable ;load constant or variable @_DoLoad ~0.{:0-1}~\,~@~ endm Save64 macro Address @_DoSave 64\,~@~ endm Copy64 macro #Constant|Variable,ToAddress mreq 1,2:#Constant|Variable,ToAddress @@Load64 ~1~ @Save64 ~2~ endm Swap64 macro @_DoSwap 64 endm Add64 macro Addend,Adder,Sum @_DoMath ~0~\,64\,~1~\,~2~\,~3~ endm Sub64 macro Minuend,Subtrahend,Difference @_DoMath ~0~\,64\,~1~\,~2~\,~3~ endm Mul64 macro Multiplicand,Multiplier,Product @_DoMath ~0~\,64\,~1~\,~2~\,~3~ endm Div64 macro Dividend,Divisor,Quotient @_DoMath ~0~\,64\,~1~\,~2~\,~3~ endm Mod64 macro Dividend,Divisor,Remainder @_DoMath ~0~\,64\,~1~\,~2~\,~3~ endm Abs64 macro Source[,Destination] @_DoAbs 64\,~1~\,~2~ endm Neg64 macro Source[,Destination] @_DoNeg 64\,~1~\,~2~ endm #endif ;******************************************************************************* ; Common macro(s) for all operations defined above (not to be called directly) ;******************************************************************************* #ifnomdef _signed_ _signed_ macro #ifndef SIGNED #Warning SIGNED \@~mfilename~\@ expected #endif endm #endif ;------------------------------------------------------------------------------- #ifnomdef _StkMthMax_ _StkMthMax_ macro Expression mset # mtrim 1 #temp mdo mset 0,~'=(),+-*\/&|^><'{:mloop}~ #ifparm ~text.1.1~ = # mset 0,~text.2~ ;;remove # from number #endif #ifnb ~text~ #ifnb ~text.1.1~ = . #Warning Pointers (~text~) have unknown size #endif #ifnum ~text~ ;estimate byte size for numeric constant #ifnz ~text~>16 #if 3 > :temp #temp 3 #endif #endif #ifnz ~text~>24 #if 4 > :temp #temp 4 #endif #endif #endif #ifb ~text.1.1~ = : #ifnonum ~text~ #ifdef ~text~ #if {::~text~} > :temp #temp ::~text~ #endif #endif #endif #endif #endif mloop {:1/2} ;;max term/factor estimate #ifz :temp #Warning Undetermined bit-size (using 32-bit) #temp 4 #endif ; #ifb \@~'*'~\@ = \@~1~\@ ;if any multiplication is present ; #if :temp < 4 ; #temp :temp+1 ;give some more bits (normally double, but on average this is OK) ; #endif ; #endif #if :temp > 8 ;for exceptional cases, enforce #temp 8 ;maximum available bit-size #endif @_FindStkMth_ {:temp*8} endm #endif ;------------------------------------------------------------------------------- #ifnomdef Eval Eval macro Expression mset # mtrim 1 @@_StkMthMax_ ~1~ #temp :mexit @@_FindStkMth_ {:temp} #temp :mexit @@Eval{:temp} ~1~ mexit {:temp} endm #endif ;------------------------------------------------------------------------------- #ifnomdef EvalS EvalS macro Expression mset # @@_signed_ @Eval ~1~ endm #endif ;------------------------------------------------------------------------------- #ifnomdef StrMath StrMath macro #ifnb ~1~ #ifdef ~1,~ #ifnz ::~1,~ @_DoStr {::~1,~*8}\,~1~\,~2~ mexit #endif #endif #endif mset # mtrim 1 @@_StkMthMax_ ~1,~ #temp :mexit @_DoStr {:temp}\,~1~\,~2~ endm #endif ;=============================================================================== #ifnomdef _FindStkMth_ _FindStkMth_ macro BitVersion #temp mdo ~1~/8 ;;find the next highest bit version included (but not less than requested) #ifdef _STKMTH{:mloop*8}_ #ifz :temp #temp :mloop*8 ;;found #endif #endif mloop 8 ;;highest possible is 64 (8x8) #ifz :temp mstop Include STKMTH{~1~}.SUB (or higher) #endif mexit :temp endm #endif ;------------------------------------------------------------------------------- #ifnomdef _Eval_ _Eval_ macro [BitSize,]Expression (eg. [ans=](a+b)*(a-b)/2) #if :macronest = 1 mstop Macro not to be called directly (eg. use @Eval32) #endif #ifb ~00~ = ~0~ mdef 1,32 mswap 0,1 ;;bitsize now in ~ text ~ mdel 1 mset # #Message -------------------------------------------------- #Message Expr: ~1~ #Message -------------------------------------------------- @@_FindStkMth_ ~text~ mset 0,{:mexit},~text~ ;;(bitsize to use, actual bitsize) #endif mset # mreq 1:Expression (eg. [ans=](a+b)*(a-b)/2 -- spaces OK) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #ifb ~00~ = ~0~ mtrim 1 ;;remove all spaces mset #'=' ;;split on assignment (if any) #if :nn > 2 mstop Too many assignment operators #endif #if :nn > 1 @@~0~ ~2~ #ifparm ~1.1.1~ = . ;;pointers ... #if ~text,~/8 <> ~text','2~/8 @@ResizeTOS #~text,~/8\,#~text','2~/8 @@lea ~1~ @@_?sei_ ~1~ @@pullv ~1~ ~text','2~/8 ;;are resized and then pulled @_?cli_ ~1~ mexit #endif #endif #ifdef ~1,~ ;;if assignment var defined @Save~text,~ ~1~ ;;save to it mexit #endif #ifparm ~,1~ = ,spx ;;SPX is SP-equivalent in EVAL mset 1,~1,~,sp ;;change to SP-index #endif #ifparm ~,1~ = ,sp ;;else, if SP-indexed ~1,~ equ ::,~text,~/8 ;;leave on stack with this name #Message Saved to TOS (~1,~) mexit #endif merror Unknown variable \@~1~\@ #endif #endif ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #ifstr ~1~ #if :1-2 > 4 mstop String constant (~1~) too long #endif @Load~text,~ #~1~ mexit #endif ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #ifparm ~1.1.1~ = - ;;process leading negative #ifnum ~1~ @Load~text,~ #~1~ ;;numerics use immediate mode mexit #endif #ifdef ~1.2~ #ifz ::~1.2~ @Load~text,~ #~1~ ;;named constants use immediate mode mexit #endif #endif @~0~ #0~1~ ;;anything else, subtract from zero mexit #endif ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; mset #'+-|^' ;;split terms #if :nn > 1 mdo 2 @@~0~ ~{:nn-:mloop+2}.2~ ;;process terms right to left mloop :nn @@~0~ ~1~ ;;the first term without operator mdo 2 #ifparm ~{:mloop}.1.1~ = + #Message Add~text,~ !jsr StackAdd~text,~ ; #spadd -{~text,~/8} #endif #ifparm ~{:mloop}.1.1~ = - #Message Sub~text,~ !jsr StackSub~text,~ ; #spadd -{~text,~/8} #endif #ifparm ~{:mloop}.1.1~ = | #Message Or~text,~ !jsr StackOr~text,~ ; #spadd -{~text,~/8} #endif #ifparm ~{:mloop}.1.1~ = ^ #Message Xor~text,~ !jsr StackXor~text,~ ; #spadd -{~text,~/8} #endif mloop :nn mexit #endif ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; mset #'*\/&><' ;;split factors #if :nn > 1 mdo 2 @@~0~ ~{:nn-:mloop+2}.2~ ;;process factors right to left mloop :nn @@~0~ ~1~ ;;the first factor without operator mdo 2 #ifparm ~{:mloop}.1.1~ = * #Message Mul~text,~ !jsr StackMul~text,~ ; #spadd -{~text,~/8} #endif #ifparm ~{:mloop}.1.1~ = / #Message Div~text,~ !jsr StackDiv~text,~ ; #spadd -{~text,~/8} #endif #ifparm ~{:mloop}.1.1~ = \ #Message Mod~text,~ !jsr StackMod~text,~ ; #spadd -{~text,~/8} #endif #ifparm ~{:mloop}.1.1~ = & #Message And~text,~ !jsr StackAnd~text,~ ; #spadd -{~text,~/8} #endif #ifparm ~{:mloop}.1.1~ = > #Message Shr~text,~ !jsr StackShr~text,~ ; #spadd -{~text,~/8} #endif #ifparm ~{:mloop}.1.1~ = < #Message Shl~text,~ !jsr StackShl~text,~ ; #spadd -{~text,~/8} #endif mloop :nn mexit #endif ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #ifparm ~1.1.4~~1.{:1}~ = NEG() ;;do NEG(ate) function @@~0~ ~1.5.{:1-5}~ #Message Neg~text,~ !jsr StackNegate~text,~ mexit #endif ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #ifparm ~1.1.4~~1.{:1}~ = ABS() ;;do ABS(olute) function @@~0~ ~1.5.{:1-5}~ #Message Abs~text,~ !jsr StackAbs~text,~ mexit #endif ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #ifparm ~1.1.4~~1.{:1}~ = SQR() ;;do SQR() function -- square @@~0~ ~1.5.{:1-5}~ #Message Sqr~text,~(TOS) tsx !jsr StackLoad~text,~ !jsr StackMul~text,~ mexit #endif ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #ifparm ~1.1.1~~1.{:1}~ = () ;;process parenthesized sub-expression @~0~ ~1.2.{:1-2}~ mexit #endif ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #ifnum ~#1~ mset 1,#~#1~ ;;numerics use immediate mode #endif #ifparm ~1.1.2~ = -: mset 1,#~1~ ;;negative internal symbol use immediate #endif #ifparm ~1.1.1~ = : mset 1,#~1~ ;;positive internal symbol use immediate #endif #ifb ~,1~ #ifdef ~#1~ #ifz ::~#1~ mset 1,#~#1~ ;;named constants use immediate #endif #endif #endif #ifparm ~,1~ = ,spx ;;SPX is SP-equivalent in EVAL mset 1,~1,~,sp ;;change to SP-index #endif #ifparm ~1.1.1~ = . ;;pointers ... #if ~text','2~/8 <> ~text,~/8 @@_?sei_ ~1~ @@pushv ~1~ ~text','2~/8 ;;are pushed and then resized @@_?cli_ ~1~ @ResizeTOS #~text','2~/8\,#~text,~/8 mexit #endif #endif @Load~text,~ ~1~ ;;anything else, load as is endm #endif ;------------------------------------------------------------------------------- #ifnomdef _?sei_ _?sei_ macro #ifdef _NOCLI_ mexit #endif #ifndef _MTOS_ mexit #endif mset # #ifdef ~1,~ #if ::~1,~ < 2 mexit #endif #endif sei endm #endif ;------------------------------------------------------------------------------- #ifnomdef _?cli_ _?cli_ macro #ifdef _NOCLI_ mexit #endif #ifndef _MTOS_ mexit #endif mset # #ifdef ~1,~ #if ::~1,~ < 2 mexit #endif #endif cli endm #endif ;------------------------------------------------------------------------------- #ifnomdef _DoLoad _DoLoad macro BitSize[,Variable] ;if no Variable, wherever HX points #if :macronest = 1 #Warning Macro NOT to be called directly #endif mreq 1:BitSize[,Variable] #temp ~1~/8 ;;bytesize now in :temp mdel 1 ;;get rid of bitsize parm mset # ;;unite all parms into one @@_not_x_ ~1~ ;;X-mode not allowed ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #ifnum ~1~ mset 1,#~1~ ;;numerics use immediate mode #endif #ifb ~,1~ #ifdef ~#1~ #ifz ::~#1~ mset 1,#~#1~ ;;named constants use immediate mode #endif #endif #endif #ifnb ~#~ ;;process immediate mode #Message Load{:temp*8} ~1~ mset 1,~#1~ mset 0 ;;use as flag for CLRA usage mdo #ifz ~#1~>{:mloop-1*8}&$FF #ifz :text clra mset 0,clra ;;flag CLRA was used #endif psha #else ldb #~#1~>{:mloop-1*8}&$FF pshb #endif mloop :temp mexit #endif ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #ifnb ~1,~ #ifb ~1.1.1~ = . ;;except for pointers #ifnz ::~1,~ ;;and constants #if ::~1,~ <> :temp ;;different-size variables @@_?sei_ ~1~ @@pushv ~1~ ;;are pushed and then resized @@_?cli_ ~1~ @ResizeTOS #{::~1,~}\,#{:temp} mexit #endif #endif #endif #endif ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #ifndef ~1,~ #Warning Loading forward \@~1,~\@ as var #endif #Message Load{:temp*8} ~1~ @@lea ~1~ ;;default case @@_?sei_ ~1~ !jsr StackLoad{:temp*8} ;;load as is @@_?cli_ ~1~ endm #endif ;------------------------------------------------------------------------------- #ifnomdef _DoSave _DoSave macro BitSize[,Variable] ;if no Variable, wherever HX points #if :macronest = 1 #Warning Macro NOT to be called directly #endif mreq 1:BitSize[,Variable] @@_not_x_ ~@@~ #ifnb ~2,~ #ifb ~2.1.1~ = . ;;except for pointers #ifnz ::~2,~ ;;and constants #if ::~2,~ <> ~1~/8 ;;different-size variables @@ResizeTOS #{~1~/8}\,#{::~2,~} @@_?sei_ ~@@~ @@pullv ~@@~ ;;are resized and then pulled @_?cli_ ~@@~ mexit #endif #endif #endif #endif #Message Save~1~ ~@@~ @@lea ~@@~ ;;default case @@_?sei_ ~@@~ !jsr StackSave~1~ ;;save as is @@_?cli_ ~@@~ endm #endif ;------------------------------------------------------------------------------- #ifnomdef _DoSwap _DoSwap macro BitSize #if :macronest = 1 #Warning Macro NOT to be called directly #endif !jsr StackSwap~1~ endm #endif ;------------------------------------------------------------------------------- #ifnomdef _DoOperation _DoOperation macro Operation[,BitSize] #if :macronest = 1 #Warning Macro NOT to be called directly #endif mdef 2,~1.{:1-1}~ #Message ~1~ !jsr Stack~1~ endm #endif ;------------------------------------------------------------------------------- #ifnomdef _DoMath _DoMath macro Operation,BitSize[,Operand1[,Operand2[,Answer]]] #if :macronest = 1 #Warning Macro NOT to be called directly #endif #ifnoparm ~3~ @_DoOperation ~1~ mexit #endif #ifnoparm ~4~ @@Load~2~ ~3~ #ifnoparm ~1~ = Add~2~ #ifnoparm ~1~ = Mul~2~ ;except for Add and Mul which are commutative, we must swap the stack !jsr StackSwap~2~ ;one parm is Operand2 (eg, Div32 XXX does TOS/XXX) #endif #endif @_DoOperation ~1~ mexit #endif @@Load~2~ ~4~ @@Load~2~ ~3~ @@_DoOperation ~1~ #ifparm ~5~ @Save~2~ ~5~ #endif endm #endif ;------------------------------------------------------------------------------- #ifnomdef _DoAbs _DoAbs macro BitSize[,Source][,Destination] #if :macronest = 1 #Warning Macro NOT to be called directly #endif #Message Abs~1~ ~@@~ @@_FindStkMth_ ~1~ mset 1,{:mexit} #ifparm ~2~ @@Load~1~ ~2~ #endif !jsr StackAbs~1~ #ifparm ~2~~3~ @Save~1~ ~3~ #endif endm #endif ;------------------------------------------------------------------------------- #ifnomdef _DoNeg _DoNeg macro BitSize[,Source][,Destination] #if :macronest = 1 #Warning Macro NOT to be called directly #endif #Message Neg~1~ ~@@~ @@_FindStkMth_ ~1~ mset 1,{:mexit} #ifparm ~2~ @@Load~1~ ~2~ #endif !jsr StackNegate~1~ #ifparm ~2~~3~ @Save~1~ ~3~ #endif endm #endif ;------------------------------------------------------------------------------- #ifnomdef _DoStr _DoStr macro BitSize,[Variable],[ResultString] #if :macronest = 1 #Warning Macro NOT to be called directly #endif @@_FindStkMth_ ~1~ mset 1,{:mexit} #ifparm ~'~,3~'.{:3}~ = x pshx #endif #ifparm ~2~ @@Load~1~ ~2~ #ifparm ~'~,3~'.{:3}~ = x ldhx ~1~/8+1,asp ;reload user HX for next LDHX #endif #endif #ifnb ~2~ #Message Convert \@~2~\@ ({::~2,~*8}-bit) to ASCIZ in \@~3~\@ #endif @@lea ~3~ !jsr Stack~1~ToASCIZ #ifparm ~2~ ais #~1~/8 #endif #ifparm ~'~,3~'.{:3}~ = x pulx #endif endm #endif ;******************************************************************************* ; X-index offsets to parameters (2 [RTS] + 6 [PUSH DXY]) ?A equ 2+6 ;top-of-stack (TOS) number ?B equ ?A+?WORD ;number after TOS #Cycles ;reset the cycle counter ;******************************************************************************* #ROM ;******************************************************************************* ;******************************************************************************* ; Purpose: Add N1 to N2 and place result on top-of-stack. N1 & N2 removed ; Input : StackHi = Number2 ; : StackLo = Number1 ; Output : Stack = Result ; Note(s): Carry Set on overflow ?Add proc push tsx #if MATHSIZE = 32 ldd ?A+2,x addd ?B+2,x std ?B+2,x #endif ldd ?A,x #if MATHSIZE = 16 addd ?B,x #endif #if MATHSIZE = 32 adcb ?B+1,x adca ?B,x #endif std ?B,x jmp ?RemoveAndReturn ?AddCycles equ :cycles ;******************************************************************************* ; Purpose: Subtract N2 from N1 and place result on top-of-stack. N1 & N2 removed ; Input : StackHi = Number2 ; : StackLo = Number1 ; Output : Stack = Result ; Note(s): Carry Set on borrow ?Subtract proc push tsx #if MATHSIZE = 32 ldd ?A+2,x subd ?B+2,x std ?B+2,x #endif ldd ?A,x #if MATHSIZE = 16 subd ?B,x #endif #if MATHSIZE = 32 sbcb ?B+1,x sbca ?B,x #endif std ?B,x jmp ?RemoveAndReturn ?SubCycles equ :cycles ;******************************************************************************* ; Purpose: Multiply N1 with N2 and place result on top-of-stack. N1 & N2 removed ; Input : StackHi = Number2 ; : StackLo = Number1 ; Output : Stack = Result ; Note(s): Overflows lost, Carry state should be ignored ?Multiply proc push tsx clra psha:?WORD ;allocate temp result space tsy #if MATHSIZE = 16 ;row 1 lda ?A+1,x ldb ?B+1,x mul std ,y ;temporary 16-bit result (2nd byte) lda ?A+1,x ldb ?B+0,x mul addb ,y stb ,y ;row 2 lda ?A+0,x ldb ?B+1,x mul addb ,y stb ,y #endif #if MATHSIZE = 32 ;row 1 lda ?A+3,x ldb ?B+3,x mul std 2,y ;temporary 32-bit result (3rd & 4th bytes) lda ?A+3,x ldb ?B+2,x mul addd 1,y std 1,y clra adca #0 sta ,y ;temporary 32-bit result (2nd byte) lda ?A+3,x ldb ?B+1,x mul addd ,y std ,y ;temporary 32-bit result (1st byte) lda ?A+3,x ldb ?B+0,x mul addb ,y stb ,y ;row 2 lda ?A+2,x ldb ?B+3,x mul addd 1,y std 1,y clra adca ,y sta ,y lda ?A+2,x ldb ?B+2,x mul addd ,y std ,y lda ?A+2,x ldb ?B+1,x mul addb ,y stb ,y ;row 3 lda ?A+1,x ldb ?B+3,x mul addd ,y std ,y lda ?A+1,x ldb ?B+2,x mul addb ,y stb ,y ;row 4 lda ?A+0,x ldb ?B+3,x mul addb ,y stb ,y #endif ;copy result to B while removing from stack ldb #?WORD ?MulCycles equ :cycles CopyResult@@ pula tsx sta ?B+?WORD-1,x decb bne CopyResult@@ ?MulCycles set :cycles*?WORD+?MulCycles jmp ?RemoveAndReturn ?MulCycles set ?MulCycles+:cycles ;******************************************************************************* ; Purpose: Divide N1 by N2 and place quotient on top-of-stack. N1 & N2 removed ; Input : StackHi = Divisor (N2) ; : StackLo = Dividend (N1) ; Output : Stack = Quotient ; : Carry Set on error (division by zero) ?Divide proc push clra ;flag for DIV operation psha bra ?Div.Start ?DivCycles equ :cycles ;******************************************************************************* ; Purpose: Divide N1 by N2 and place remainder on top-of-stack. N1 & N2 removed ; Input : StackHi = Divisor (N2) ; : StackLo = Dividend (N1) ; Output : Stack = Remainder ; : Carry Set on error (division by zero) ?Modulo proc push lda #-1 ;flag for MOD operation psha bra ?Div.Start ?ModCycles equ :cycles ;******************************************************************************* ? set 0 ;X index offsets (after TSX) ?Quotient next ?,?WORD ?Remainder next ?,?WORD ?Temp next ?,?WORD ?BitCounter next ? ?Flag next ? ?Divisor equ ?B+? ?Dividend equ ?A+? ?Result equ ?Divisor ;result overwrites divisor ?Div.Error givex #? ;de-allocate temporaries pull sec ;indicate error condition rts ;------------------------------------------------------------------------------- ?Div.Start getx #?-1 ;quotient, remainder, and temp ;(-1 for already pushed Flag) ; remainder := 0 clrd #if MATHSIZE = 32 std ?Remainder+2,x #endif std ?Remainder,x ; quotient := 0 #if MATHSIZE = 32 std ?Quotient+2,x #endif std ?Quotient,x ; first, test for division by zero error lda ?Divisor,x ora ?Divisor+1,x #if MATHSIZE = 32 ora ?Divisor+2,x ora ?Divisor+3,x #endif beq ?Div.Error ; if Dividend = 0, we're done lda ?Dividend,x ora ?Dividend+1,x #if MATHSIZE = 32 ora ?Dividend+2,x ora ?Dividend+3,x #endif jeq ?Div.Exit ; if (divisor = dividend) then quotient := 1; return ; if (divisor > dividend) then remainder := dividend; return ldd ?Divisor,x cmpd ?Dividend,x bne ?Div.NotEqual #if MATHSIZE = 32 ldd ?Divisor+2,x cmpd ?Dividend+2,x bne ?Div.NotEqual #endif inc ?Quotient+?WORD-1,x ;quotient := 1 #if MATHSIZE = 32 jmp ?Div.Exit ;and get out #else bra ?Div.Exit ;and get out #endif ?Div.NotEqual #if MATHSIZE = 32 ldd ?Divisor+2,x subd ?Dividend+2,x #endif ldd ?Divisor,x #if MATHSIZE = 16 subd ?Dividend,x #endif #if MATHSIZE = 32 sbcb ?Dividend+1,x sbca ?Dividend,x #endif bcs ?Div.Continue #if MATHSIZE = 32 ldd ?Dividend+2,x std ?Remainder+2,x #endif ldd ?Dividend,x std ?Remainder,x #if MATHSIZE = 16 bra ?Div.Exit ;and get out #else jmp ?Div.Exit ;and get out #endif ?Div.Continue lda #?WORD*8 ;bitCounter := 32-bit (or 16); sta ?BitCounter,x ; while (remainder < divisor) do ?Div.While @cop ;in case of many iterations #if MATHSIZE = 32 ldd ?Remainder+2,x subd ?Divisor+2,x #endif ldd ?Remainder,x #if MATHSIZE = 16 subd ?Divisor,x #endif #if MATHSIZE = 32 sbcb ?Divisor+1,x sbca ?Divisor,x #endif bcc ?Div.EndWhile ; remainder := (remainder shl 1) or msb(dividend) lda ?Dividend,x lsla #if MATHSIZE = 32 rol ?Remainder+3,x rol ?Remainder+2,x #endif rol ?Remainder+1,x rol ?Remainder,x ; temp := dividend ldd ?Dividend,x std ?Temp,x #if MATHSIZE = 32 ldd ?Dividend+2,x std ?Temp+2,x #endif ; dividend := dividend shl 1 lsl ?Dividend+?WORD-1,x #if MATHSIZE = 32 rol ?Dividend+2,x rol ?Dividend+1,x #endif rol ?Dividend,x ; bitCounter := bitCounter - 1 dec ?BitCounter,x ; end while bra ?Div.While ; dividend := temp ?Div.EndWhile ldd ?Temp,x std ?Dividend,x #if MATHSIZE = 32 ldd ?Temp+2,x std ?Dividend+2,x #endif ; remainder := remainder shr 1 lsr ?Remainder,x ror ?Remainder+1,x #if MATHSIZE = 32 ror ?Remainder+2,x ror ?Remainder+3,x #endif ; bitCounter := bitCounter + 1 inc ?BitCounter,x ; for i := 0 to bitCounter-1 do ?Div.For @cop ;in case of many iterations tst ?BitCounter,x beq ?Div.Exit dec ?BitCounter,x ; remainder := (remainder shl 1) or msb(dividend) lda ?Dividend,x lsla #if MATHSIZE = 32 rol ?Remainder+3,x rol ?Remainder+2,x #endif rol ?Remainder+1,x rol ?Remainder,x ; temp := remainder - divisor #if MATHSIZE = 32 ldd ?Remainder+2,x subd ?Divisor+2,x std ?Temp+2,x #endif ldd ?Remainder,x #if MATHSIZE = 16 subd ?Divisor,x #endif #if MATHSIZE = 32 sbcb ?Divisor+1,x sbca ?Divisor,x #endif std ?Temp,x ; dividend := dividend shl 1 lsl ?Dividend+?WORD-1,x #if MATHSIZE = 32 rol ?Dividend+2,x rol ?Dividend+1,x #endif rol ?Dividend,x ; q := not msb(temp) lda ?Temp,x eora #$80 ;invert msb ($80=Bit7) anda #$80 ;isolate msb ($80=Bit7) ; quotient := (quotient shl 1) or q tab lslb #if MATHSIZE = 32 rol ?Quotient+3,x rol ?Quotient+2,x #endif rol ?Quotient+1,x rol ?Quotient,x ; if q <> 0 then tsta beq ?Div.For ; remainder := temp ldd ?Temp,x std ?Remainder,x #if MATHSIZE = 32 ldd ?Temp+2,x std ?Remainder+2,x #endif ; end if -- end for bra ?Div.For ?Div.Exit tst ?Flag,x bne ?Div.ExitMod ?cycles equ :cycles ;?Div.ExitDiv #if MATHSIZE = 32 ldd ?Quotient+2,x std ?Result+2,x #endif ldd ?Quotient,x std ?Result,x bra ?Div.ExitBoth ?DivCycles set ?DivCycles+?cycles+:cycles ?Div.ExitMod #if MATHSIZE = 32 ldd ?Remainder+2,x std ?Result+2,x #endif ldd ?Remainder,x std ?Result,x ?ModCycles set ?ModCycles+?cycles+:cycles ?Div.ExitBoth givex #? ;de-allocate temporaries clc ;no error(s) ?cycles set :cycles ?DivCycles set ?DivCycles+?cycles ?ModCycles set ?ModCycles+?cycles ;falls thru to ?RemoveAndReturn ;******************************************************************************* ; Common exit removes lower 32-bit stack element and returns to caller ;******************************************************************************* #temp 6 ;base offset = 6 [PUSH DXY] ?RemoveAndReturn ldd :temp,x ;our return address moved up std :temp+?WORD,x ;above 32-bit word to remove pull ins:?WORD ;remove top-of-stack ?WORD rts ?ReturnCycles equ :cycles ;******************************************************************************* ; Purpose: Swaps the stacked order of N1 and N2 ; Input : StackHi = Number2 ; : StackLo = Number1 ; Output : StackHi = Number1 [StackLo & StackHi in reverse order] ; : StackLo = Number2 ; Note(s): Does not alter stack size ?Swap proc push tsx ldd ?A,x pshd ldd ?B,x std ?A,x puld std ?B,x #if MATHSIZE = 32 ldd ?A+2,x pshd ldd ?B+2,x std ?A+2,x puld std ?B+2,x #endif pull rts ?SwapCycles equ :cycles ;******************************************************************************* ; Purpose: Negate the top-of-stack number ; Input : Stack = Number ; Output : Stack = -Number ; Note(s): Does not alter stack size ?A set ?A-4 ;adjust for PSHX (no PUSH) ?Negate proc pshx ;(requires offset -4 adjustment) tsx com ?A,x ;one's complement com ?A+1,x #if MATHSIZE = 32 com ?A+2,x com ?A+3,x inc ?A+3,x ;two's complement bne ?Negate.Exit inc ?A+2,x bne ?Negate.Exit #endif inc ?A+1,x bne ?Negate.Exit inc ?A,x ?Negate.Exit pulx rts ?NegateCycles equ :cycles ;******************************************************************************* ; Purpose: Create a new top-of-stack and load to it the number pointed by X ; Input : X -> [Variable with] Number ; Output : Stack = Number ; Note(s): This operation makes it easier to load a number to the stack. ; : Hint: To make a copy of the TOS, do: ; : tsx ; : jsr StackLoad32 ; : (Stack is expanded) #temp 4 ;2 [PSHY] + 2 [PSHD] ?NewRTS setn :temp,WORD ?TOSNumber setn :temp,?WORD-WORD ;-WORD as ?OldRTS will be gone ?OldRTS setn :temp,WORD ?Load proc des:?WORD ;allocate new memory pshy pshd tsy ldd ?OldRTS,y ;move RTS after new memory std ?NewRTS,y #if MATHSIZE = 32 ldd 2,x std ?TOSNumber+2,y #endif ldd ,x std ?TOSNumber,y puld puly rts ?LoadCycles equ :cycles ;******************************************************************************* ; Purpose: Unload the top-of-stack number into a variable pointed to by X ; Input : Stack = Number ; : X -> Some 32-bit variable ; Output : Variable pointed to by X receives TOS number ; Note(s): This operation makes it easier to unload a number from the stack. ; : Use: ; : ldx #MyVar ; : jsr StackSave32 ; : (Stack is reduced) #temp 4 ;2 [PSHY] + 2 [PSHD] ?OldRTS setn :temp,WORD ?TOSNumber setn :temp,?WORD-WORD ;-WORD as ?OldRTS will be gone ?NewRTS setn :temp,WORD ?Save proc pshy pshd tsy ldd ?TOSNumber,y std ,x #if MATHSIZE = 32 ldd ?TOSNumber+2,y std 2,x #endif ldd ?OldRTS,y ;move RTS before old memory std ?NewRTS,y puld puly ins:?WORD ;de-allocate TOS memory rts ?SaveCycles equ :cycles ;******************************************************************************* ; Assign global names to the various library calls, depending on version used. ; Different names for each version means you can include both at the same time. ;******************************************************************************* ? macro BitSize #if MATHSIZE = ~1~ StackAdd~1~ exp ?Add StackSub~1~ exp ?Subtract StackMul~1~ exp ?Multiply StackDiv~1~ exp ?Divide StackMod~1~ exp ?Modulo StackSwap~1~ exp ?Swap StackNegate~1~ exp ?Negate StackLoad~1~ exp ?Load StackSave~1~ exp ?Save #endif endm @? 32 @? 16 ;******************************************************************************* #Exit ;******************************************************************************* #Message Module code size: {*-?Add} bytes #Message Add : {?AddCycles+?ReturnCycles} cycles #Message Sub : {?SubCycles+?ReturnCycles} cycles #Message Mul : {?MulCycles+?ReturnCycles} cycles #Message Div : {?DivCycles+?ReturnCycles}+ cycles (indeterminate) #Message Mod : {?ModCycles+?ReturnCycles}+ cycles (indeterminate) #Message Swap: {?SwapCycles} cycles #Message Neg : {?NegateCycles} cycles #Message Load: {?LoadCycles} cycles #Message Save: {?SaveCycles} cycles ;******************************************************************************* #MapOn #RAM Result rmb ?WORD #ROM Start proc lds #STACKTOP clrd ;(for simulator) clrx clry ;--- test negation ldx #N1 bsr ?Load bsr ?Negate ;-N1 = $789ABCDF bsr ?Negate ;-(-N1) = $87654321 ;--- test swap ldx #Zero bsr ?Load !jsr ?Swap !jsr ?Swap ins:?WORD ;--- test multiplication ldx #N2 bsr ?Load jsr ?Multiply ;N1*N2 = $[9A0CC]C5F94116 ins:?WORD ; ^^^^^ truncated ;--- test addition ldx #N1 bsr ?Load ldx #N2 bsr ?Load jsr ?Add ;N1+N2 = $87777777 ins:?WORD ;--- test subtraction ldx #N2 !jsr ?Load ldx #N1 !jsr ?Load jsr ?Subtract ;N1-N2 = $87530ECB ins:?WORD ;--- test integer division ldx #N2 jsr ?Load ldx #N1 jsr ?Load jsr ?Divide ;N1/N2 = $00000770 ins:?WORD ;--- test modulo division ldx #N2 jsr ?Load ldx #N1 jsr ?Load jsr ?Modulo ;N1 mod N2 = $00000381 ;--- test save ldx #Result jsr ?Save ;--- done testing bra * ;------------------------------------------------------------------------------- N1 long $87654321 ;example operand one N2 long $00123456 ;example operand two Zero long 0 ;a zero ;******************************************************************************* @vector Vreset,Start ;******************************************************************************* end :s19crc