thingamajig/calculator.asm

2024 lines
35 KiB
NASM
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

org 0
load r0, fResultPtr+0
load r1, fResultPtr+1
cleq r0, r0, pushWord
cleq r0, r0, dumpFloat
cleq r0, r0, normalize
load r0, fResultPtr+0
load r1, fResultPtr+1
cleq r0, r0, pushWord
cleq r0, r0, dumpFloat
load r0, fResultPtr+0
load r1, fResultPtr+1
cleq r0, r0, pushWord
cleq r0, r0, fPack
cleq r0, r0, fPrint
cleq r0, r0, newline
halt
cleq r0, r0, readline
load r0, linelen+0
load r1, linelen+1
cleq r0, r0, pushWord
cleq r0, r0, stDup
cleq r0, r0, stPrinthex
xor r0, r0
xor r1, r1
cleq r0, r0, pushWord
printloop:
cleq r0, r0, stOver
cleq r0, r0, stOver
cleq r0, r0, stSwap
cleq r0, r0, stGtEq
brneq r0, r1, printend
cleq r0, r0, stDup
load r0, linebufStart+0
load r1, linebufStart+1
cleq r0, r0, pushWord
cleq r0, r0, stAdd
cleq r0, r0, stLoadByte
cleq r0, r0, stEmit
cleq r0, r0, stInc
breq r0, r0, printloop
printend:
cleq r0, r0, newline
halt
dumpFloat:
xor r0, r0
load r1, #12
cleq r0, r0, pushWord
dumpFloatLoop:
cleq r0, r0, peekWord
or r0, r1
xor r2, r2
breq r0, r2, dumpFloatEnd
cleq r0, r0, stDec
cleq r0, r0, stSwap
cleq r0, r0, stDup
cleq r0, r0, stLoadByte
cleq r0, r0, popWord
xor r0, r0
or r0, r1
cleq r0, r0, writehexByte
load r0, #20
store ffff, r0
cleq r0, r0, stInc
cleq r0, r0, stSwap
breq r0, r0, dumpFloatLoop
dumpFloatEnd:
cleq r0, r0, popWord
cleq r0, r0, popWord
breq r0, r0, newline
debug:
store debugr0, r0
store debugr1, r1
store debugr2, r2
store debugr3, r3
debugRegs:
load r0, debugr0
cleq r0, r0, writehexByte
load r0, #20
store ffff, r0
load r0, debugr1
cleq r0, r0, writehexByte
load r0, #20
store ffff, r0
load r0, debugr2
cleq r0, r0, writehexByte
load r0, #20
store ffff, r0
load r0, debugr3
cleq r0, r0, writehexByte
load r0, #20
store ffff, r0
debugTmpWords:
load r0, tmpWordHigh
load r1, tmpWordLow
cleq r0, r0, writeHexWord
load r0, #20
store ffff, r0
load r0, tmpWord2High
load r1, tmpWord2Low
cleq r0, r0, writeHexWord
load r0, #20
store ffff, r0
load r0, tmpWord3High
load r1, tmpWord3Low
cleq r0, r0, writeHexWord
load r0, #7c
store ffff, r0
load r0, SPStart+0
load r1, SPStart+1
store debugPtr+0, r0
store debugPtr+1, r1
debugLoop:
load r0, debugPtr+0
load r1, debugPtr+1
load r2, SP+0
load r3, SP+1
brneq r0, r2, debugDumpword
breq r1, r3, debugEnd
debugDumpword:
load r0, debugPtr+0
load r1, debugPtr+1
store loadByteHigh, r0
store loadByteLow, r1
cleq r0, r0, loadByte
cleq r0, r0, writehexByte
load r0, debugPtr+0
load r1, debugPtr+1
cleq r0, r0, incWord
store debugPtr+0, r0
store debugPtr+1, r1
load r0, debugPtr+0
load r1, debugPtr+1
store loadByteHigh, r0
store loadByteLow, r1
cleq r0, r0, loadByte
cleq r0, r0, writehexByte
load r0, debugPtr+0
load r1, debugPtr+1
cleq r0, r0, incWord
store debugPtr+0, r0
store debugPtr+1, r1
load r0, #20
store ffff, r0
breq r0, r0, debugLoop
debugEnd:
cleq r0, r0, newline
load r0, debugr0
load r1, debugr1
load r2, debugr2
load r3, debugr3
ret
debugPtr: addr debugPtr
debugr0: data 0
debugr1: data 0
debugr2: data 0
debugr3: data 0
; --
readline:
; Current index starts at 0
xor r0, r0
xor r1, r1
cleq r0, r0, pushWord
readlineLoop:
; Read a byte of input
cleq r0, r0, stKey
; Is it enter?
cleq r0, r0, peekWord
load r0, #0d
breq r0, r1, readlineEnter
; Is it backspace?
load r0, #08
breq r0, r1, readlineBackspace
; Is the buffer full?
cleq r0, r0, stOver
load r0, linebufSize+0
load r1, linebufSize+1
cleq r0, r0, pushWord
cleq r0, r0, stGtEq
brneq r0, r1, readlineBufFull
; Calculate address
cleq r0, r0, stOver
load r0, linebufStart+0
load r1, linebufStart+1
cleq r0, r0, pushWord
cleq r0, r0, stAdd
; Store the read byte
cleq r0, r0, stStoreByte
; Increase index
cleq r0, r0, stInc
breq r0, r0, readlineLoop
readlineEnter:
; Remove the CR byte off the stack, as it's not needed
cleq r0, r0, popWord
cleq r0, r0, popWord
store linelen+0, r0
store linelen+1, r1
breq r0, r0, newline
readlineBackspace:
; Remove the BS byte off the stack
cleq r0, r0, popWord
; Are we at the beginning of the line?
cleq r0, r0, stDup
xor r0, r0
xor r1, r1
cleq r0, r0, pushWord
cleq r0, r0, stSwap
cleq r0, r0, stGtEq
brneq r0, r1, readlineLoop
; Decrease the index and erase the echoed character
; TODO: utf-8
cleq r0, r0, stDec
load r3, #20
store ffff, r3
load r3, #08
store ffff, r3
breq r0, r0, readlineLoop
readlineBufFull:
; Drop the input byte and erase the echoed character
; TODO: utf-8
cleq r0, r0, popWord
load r3, #08
store ffff, r3
load r3, #20
store ffff, r3
load r3, #08
store ffff, r3
breq r0, r0, readlineLoop
; ==================================================================
; Floating point
; ==================================================================
; ------------------------------------------------------------------
; Format conversion
; ------------------------------------------------------------------
; When on-stack, a floating point number uses an 8-byte packed BCD format
; 0 1 2 3 4 5 6 7
; seee mmmm mmmm mmmm
; |\_/ \____________/
; | | |
; | | mantissa, first digit being before and rest after the point
; | exponent, with a bias of 500 (i.e. value of 0 means ×10⁻⁵⁰⁰)
; sign, 00 for non-negative, 01 for negative, 02 for overflow
; For operation, the format is instead an 18-byte unpacked BCD format
; 0 1 2 3 4 5 6 7 8 9 a b c d e f 10 11
; 0s 0e 0e 0e 0o 0m 0m 0m 0m 0m 0m 0m 0m 0m 0m 0m 0m 0r
; | \______/ | \_________________________________/ |
; | | | | extra digit for rounding
; | | | mantissa
; | | extra digit for overflow
; | exponent
; sign
; f ptr --
fUnpack:
; Start from the end
xor r0, r0
load r1, #11
cleq r0, r0, pushWord
cleq r0, r0, stAdd
; Zero out rounding digit
xor r0, r0
xor r1, r1
cleq r0, r0, pushWord
cleq r0, r0, stOver
cleq r0, r0, stStoreByte
cleq r0, r0, stDec
; Mantissa
cleq r0, r0, fUnpackWord
cleq r0, r0, fUnpackWord
cleq r0, r0, fUnpackWord
; Zero out overflow digit
xor r0, r0
xor r1, r1
cleq r0, r0, pushWord
cleq r0, r0, stOver
cleq r0, r0, stStoreByte
cleq r0, r0, stDec
; Sign and exponent
cleq r0, r0, fUnpackWord
; Clear off the pointer from the stack
breq r0, r0, popWord
; w ptr -- ptr-4
fUnpackWord:
; Low nybble of low byte
cleq r0, r0, stOver
cleq r0, r0, stLowNybble
cleq r0, r0, stOver
cleq r0, r0, stStoreByte
cleq r0, r0, stDec
; High nybble of low byte
cleq r0, r0, stOver
cleq r0, r0, stShr4
cleq r0, r0, stLowNybble
cleq r0, r0, stOver
cleq r0, r0, stStoreByte
cleq r0, r0, stDec
; Low nybble of high byte
cleq r0, r0, stOver
cleq r0, r0, stShr8
cleq r0, r0, stLowNybble
cleq r0, r0, stOver
cleq r0, r0, stStoreByte
cleq r0, r0, stDec
; High nybble of high byte
cleq r0, r0, stSwap ; Clear off the word from the stack
cleq r0, r0, stShr8
cleq r0, r0, stShr4
cleq r0, r0, stLowNybble
cleq r0, r0, stOver
cleq r0, r0, stStoreByte
breq r0, r0, stDec
; ptr -- f
fPack:
; Sign and exponent
cleq r0, r0, fPackWord
; Skip over the overflow digit
cleq r0, r0, stInc
; Mantissa
cleq r0, r0, fPackWord
cleq r0, r0, fPackWord
cleq r0, r0, fPackWord
; Clean off the pointer from stack
breq r0, r0, popWord
; ptr -- w ptr+4
fPackWord:
; High nybble of high byte
cleq r0, r0, stDup
cleq r0, r0, stLoadByte
cleq r0, r0, stShl8
cleq r0, r0, stShl4
cleq r0, r0, stSwap
cleq r0, r0, stInc
cleq r0, r0, stSwap
; Low nybble of high byte
cleq r0, r0, stOver
cleq r0, r0, stLoadByte
cleq r0, r0, stShl8
cleq r0, r0, stOr
cleq r0, r0, stSwap
cleq r0, r0, stInc
cleq r0, r0, stSwap
; High nybble of low byte
cleq r0, r0, stOver
cleq r0, r0, stLoadByte
cleq r0, r0, stShl4
cleq r0, r0, stOr
cleq r0, r0, stSwap
cleq r0, r0, stInc
cleq r0, r0, stSwap
; Low nybble of low byte
cleq r0, r0, stOver
cleq r0, r0, stLoadByte
cleq r0, r0, stOr
cleq r0, r0, stSwap
breq r0, r0, stInc
; ------------------------------------------------------------------
; Unpacked floating point variables
; ------------------------------------------------------------------
fArg1Ptr: addr fArg1
fArg1:
data 00 ; sign
data 00 ; exponent
data 00
data 00
data 00 ; overflow
data 00 ; mantissa
data 00
data 00
data 00
data 00
data 00
data 00
data 00
data 00
data 00
data 00
data 00
data 00 ; rounding
fResultPtr: addr fResult
fResult:
data 00 ; sign
data 05 ; exponent
data 00
data 00
data 09 ; overflow
data 09 ; mantissa
data 09
data 09
data 09
data 09
data 09
data 09
data 09
data 09
data 09
data 09
data 09
data 04 ; rounding
; ------------------------------------------------------------------
; Arithmetic
; ------------------------------------------------------------------
; x y z -- tens(x+y+z) ones(x+y+z)
bcdDigitAdd3:
cleq r0, r0, stAdd
cleq r0, r0, stDup
xor r0, r0
load r1, #a
cleq r0, r0, pushWord
cleq r0, r0, stGtEq
breq r0, r1, bcdDigitAdd
; Adjust so that carry is in the high 3 bits
; Basically we want 9+1=a₁₆ to become 20₁₆
xor r0, r0
load r1, #16
cleq r0, r0, pushWord
cleq r0, r0, stAdd
; x y -- tens(x+y) ones(x+y)
bcdDigitAdd:
cleq r0, r0, stAdd
cleq r0, r0, stDup
; Only look at the bottom 5 bits, since high 3 bits may already
; contain carry
cleq r0, r0, popWord
load r2, #1f
and r1, r2
cleq r0, r0, pushWord
xor r0, r0
load r1, #a
cleq r0, r0, pushWord
cleq r0, r0, stGtEq
breq r0, r1, bcdDigitAddNoAdjust
; Adjust so that carry is in the high 3 bits
; Basically we want 9+1=a₁₆ to become 20₁₆
xor r0, r0
load r1, #16
cleq r0, r0, pushWord
cleq r0, r0, stAdd
bcdDigitAddNoAdjust:
cleq r0, r0, stDup
cleq r0, r0, stShr4
cleq r0, r0, stShr1
cleq r0, r0, stSwap
breq r0, r0, stLowNybble
; x -- 9-x
bcdDigit9sComplement:
xor r0, r0
load r1, #9
cleq r0, r0, pushWord
cleq r0, r0, stSwap
breq r0, r0, stSub
; ptr --
incExponent:
; Ones
xor r0, r0
load r1, #3
cleq r0, r0, pushWord
cleq r0, r0, stAdd
cleq r0, r0, stDup
cleq r0, r0, stDup
cleq r0, r0, stLoadByte
xor r0, r0
load r1, #1
cleq r0, r0, pushWord
cleq r0, r0, bcdDigitAdd
cleq r0, r0, stRot
cleq r0, r0, stStoreByte
; Tens
cleq r0, r0, stSwap
cleq r0, r0, stDec
cleq r0, r0, stSwap
cleq r0, r0, stOver
cleq r0, r0, stSwap
cleq r0, r0, stOver
cleq r0, r0, stLoadByte
cleq r0, r0, bcdDigitAdd
cleq r0, r0, stRot
cleq r0, r0, stStoreByte
; Hundreds
cleq r0, r0, stSwap
cleq r0, r0, stDec
cleq r0, r0, stSwap
cleq r0, r0, stOver
cleq r0, r0, stLoadByte
cleq r0, r0, bcdDigitAdd
cleq r0, r0, stSwap
cleq r0, r0, popWord
brneq r0, r1, incExponentOverflow
cleq r0, r0, stSwap
breq r0, r0, stStoreByte
incExponentOverflow:
cleq r0, r0, popWord
cleq r0, r0, stDec
xor r0, r0
load r1, #2
cleq r0, r0, pushWord
cleq r0, r0, stSwap
breq r0, r0, stStoreByte
; ptr --
decExponent:
; Ones
xor r0, r0
load r1, #3
cleq r0, r0, pushWord
cleq r0, r0, stAdd
cleq r0, r0, stDup
cleq r0, r0, stDup
cleq r0, r0, stLoadByte
xor r0, r0
load r1, #9
cleq r0, r0, pushWord
cleq r0, r0, bcdDigitAdd
cleq r0, r0, stRot
cleq r0, r0, stStoreByte
; Tens
cleq r0, r0, stSwap
cleq r0, r0, stDec
cleq r0, r0, stSwap
cleq r0, r0, stOver
cleq r0, r0, stSwap
cleq r0, r0, stOver
cleq r0, r0, stLoadByte
xor r0, r0
load r1, #9
cleq r0, r0, pushWord
cleq r0, r0, bcdDigitAdd3
cleq r0, r0, stRot
cleq r0, r0, stStoreByte
; Ones
cleq r0, r0, stSwap
cleq r0, r0, stDec
cleq r0, r0, stSwap
cleq r0, r0, stOver
cleq r0, r0, stLoadByte
xor r0, r0
load r1, #9
cleq r0, r0, pushWord
cleq r0, r0, bcdDigitAdd3
cleq r0, r0, stSwap
cleq r0, r0, popWord
breq r0, r1, error ; Should never underflow
cleq r0, r0, stSwap
breq r0, r0, stStoreByte
; ptr --
shiftMantissaLeft:
; Shift-in a zero to zero-out the rightmost mantissa digit
xor r0, r0
xor r1, r1
cleq r0, r0, pushWord
; Starting at the end of the extended mantissa
cleq r0, r0, stSwap
xor r0, r0
load r1, #11
cleq r0, r0, pushWord
cleq r0, r0, stAdd
; We run the loop 14 times
xor r0, r0
load r1, #e
cleq r0, r0, pushWord
shiftMantissaLeftLoop:
; See if loop counter hit zero and decrement if not
cleq r0, r0, peekWord
or r0, r1
xor r2, r2
breq r0, r2, shiftMantissaLeftEnd
cleq r0, r0, stDec
; Load ("shift-out") the current value at pointer
cleq r0, r0, stRot
cleq r0, r0, stRot
cleq r0, r0, stDup
cleq r0, r0, stLoadByte
; Store ("shift-in") the previous value
cleq r0, r0, stRot
cleq r0, r0, stRot
cleq r0, r0, stSwap
cleq r0, r0, stOver
cleq r0, r0, stStoreByte
; Decrememnt pointer and put stack back in order
cleq r0, r0, stDec
cleq r0, r0, stRot
breq r0, r0, shiftMantissaLeftLoop
shiftMantissaLeftEnd:
cleq r0, r0, popWord
cleq r0, r0, popWord
breq r0, r0, popWord
; ptr --
shiftMantissaRight:
; Shift-in a zero to zero-out the leftmost mantissa digit
xor r0, r0
xor r1, r1
cleq r0, r0, pushWord
; Starting at the beginning of the extended mantissa
cleq r0, r0, stSwap
xor r0, r0
load r1, #4
cleq r0, r0, pushWord
cleq r0, r0, stAdd
; We run the loop 14 times
xor r0, r0
load r1, #e
cleq r0, r0, pushWord
shiftMantissaRightLoop:
; See if loop counter hit zero and decrement if not
cleq r0, r0, peekWord
or r0, r1
xor r2, r2
breq r0, r2, shiftMantissaRightEnd
cleq r0, r0, stDec
; Load ("shift-out") the current value at pointer
cleq r0, r0, stRot
cleq r0, r0, stRot
cleq r0, r0, stDup
cleq r0, r0, stLoadByte
; Store ("shift-in") the previous value
cleq r0, r0, stRot
cleq r0, r0, stRot
cleq r0, r0, stSwap
cleq r0, r0, stOver
cleq r0, r0, stStoreByte
; Increment pointer and put stack back in order
cleq r0, r0, stInc
cleq r0, r0, stRot
breq r0, r0, shiftMantissaRightLoop
shiftMantissaRightEnd:
cleq r0, r0, popWord
cleq r0, r0, popWord
breq r0, r0, popWord
; --
normalize:
; Do we have a digit in overflow?
load r0, fResult+4
xor r1, r1
breq r0, r1, normalizeNoOverflow
load r0, fResultPtr+0
load r1, fResultPtr+1
cleq r0, r0, pushWord
cleq r0, r0, stDup
cleq r0, r0, shiftMantissaRight
cleq r0, r0, incExponent
; Did we overflow the exponent?
load r0, fResult+0
load r1, #2
breq r0, r1, normalizeEnd
normalizeNoOverflow:
; Is extended mantissa all zeroes?
load r0, fResult+4
load r1, fResult+5
or r0, r1
load r1, fResult+6
or r0, r1
load r1, fResult+7
or r0, r1
load r1, fResult+8
or r0, r1
load r1, fResult+9
or r0, r1
load r1, fResult+a
or r0, r1
load r1, fResult+b
or r0, r1
load r1, fResult+c
or r0, r1
load r1, fResult+d
or r0, r1
load r1, fResult+e
or r0, r1
load r1, fResult+f
or r0, r1
load r1, fResult+10
or r0, r1
load r1, fResult+11
or r0, r1
xor r1, r1
brneq r0, r1, normalizeNonZero
; Zero out the entire number
; This causes sign to be 0 (non-negative), exponent -500, and
; extended mantissa 00.000000000000
load r0, fResultPtr+0
load r1, fResultPtr+1
cleq r0, r0, pushWord
xor r0, r0
load r1, #12
cleq r0, r0, pushWord
breq r0, r0, stZeroMem ; returns
normalizeNonZero:
normalizeTooSmallMantissa:
; Do we have a zero as the digit before the point?
load r0, fResult+5
xor r1, r1
brneq r0, r1, normalizeMantissaNormalized
; Can we decrement the exponent?
load r0, fResult+1
load r1, fResult+2
or r0, r1
load r2, fResult+3
or r0, r1
xor r1, r1
breq r0, r1, normalizeMantissaNormalized
load r0, fResultPtr+0
load r1, fResultPtr+1
cleq r0, r0, pushWord
cleq r0, r0, stDup
cleq r0, r0, shiftMantissaLeft
cleq r0, r0, decExponent
breq r0, r0, normalizeTooSmallMantissa
normalizeMantissaNormalized:
; Is the rounding digit at least 5?
load r0, fResult+11
xor r1, r1
breq r0, r1, normalizeRoundDown
load r1, #1
breq r0, r1, normalizeRoundDown
load r1, #2
breq r0, r1, normalizeRoundDown
load r1, #3
breq r0, r1, normalizeRoundDown
load r1, #4
breq r0, r1, normalizeRoundDown
normalizeRoundUp:
; Pointer to last digit of non-extended mantissa
load r0, fResultPtr+0
load r1, fResultPtr+1
cleq r0, r0, pushWord
xor r0, r0
load r1, #10
cleq r0, r0, pushWord
cleq r0, r0, stAdd
; Carry-in is 1 since we are rounding up
xor r0, r0
load r1, #1
cleq r0, r0, pushWord
; Number of times to run the loop
xor r0, r0
load r1, #d ; 12 normal mantissa + 1 overflow
cleq r0, r0, pushWord
normalizeRoundUpLoop:
cleq r0, r0, peekWord
or r0, r1
xor r2, r2
breq r0, r2, normalizeRoundUpEnd
cleq r0, r0, stDec
cleq r0, r0, stRot
cleq r0, r0, stRot
cleq r0, r0, stOver
cleq r0, r0, stLoadByte
cleq r0, r0, bcdDigitAdd
cleq r0, r0, stRot
cleq r0, r0, stSwap
cleq r0, r0, stOver
cleq r0, r0, stStoreByte
cleq r0, r0, stDec
cleq r0, r0, stSwap
cleq r0, r0, stRot
breq r0, r0, normalizeRoundUpLoop
normalizeRoundUpEnd:
cleq r0, r0, popWord
cleq r0, r0, popWord
cleq r0, r0, popWord
; Zero out the rounding digit
xor r0, r0
store fResult+11, r0
; We might have ended up with a digit in the overflow
breq r0, r0, normalize
normalizeRoundDown:
; Zero out the rounding digit
xor r0, r0
store fResult+11, r0
normalizeEnd:
ret
; ------------------------------------------------------------------
; Output
; ------------------------------------------------------------------
fPrint:
load r0, fArg1Ptr+0
load r1, fArg1Ptr+1
cleq r0, r0, pushWord
cleq r0, r0, fUnpack
; Overflow?
load r0, fArg1+0
load r1, #2
breq r0, r1, fPrintOverflow
; Negative?
load r1, #1
brneq r0, r1, fPrintMantissa
load r0, #2d ; -
store ffff, r0
fPrintMantissa:
load r0, fArg1+5
cleq r0, r0, writehexNybble
load r0, #2e ; .
store ffff, r0
load r0, fArg1+6
cleq r0, r0, writehexNybble
load r0, fArg1+7
cleq r0, r0, writehexNybble
load r0, fArg1+8
cleq r0, r0, writehexNybble
load r0, fArg1+9
cleq r0, r0, writehexNybble
load r0, fArg1+a
cleq r0, r0, writehexNybble
load r0, fArg1+b
cleq r0, r0, writehexNybble
load r0, fArg1+c
cleq r0, r0, writehexNybble
load r0, fArg1+d
cleq r0, r0, writehexNybble
load r0, fArg1+e
cleq r0, r0, writehexNybble
load r0, fArg1+f
cleq r0, r0, writehexNybble
load r0, fArg1+10
cleq r0, r0, writehexNybble
fPrintExponent:
load r0, #65 ; e
store ffff, r0
; Adjust the hundreds digit for the bias
xor r0, r0,
load r1, fArg1+1
cleq r0, r0, pushWord
xor r0, r0
load r1, #5
cleq r0, r0, pushWord
cleq r0, r0, bcdDigitAdd
; Is the exponent negative?
cleq r0, r0, stSwap
cleq r0, r0, popWord
breq r0, r1, fPrintNegativeExponent
cleq r0, r0, popWord
xor r0, r0
or r0, r1
cleq r0, r0, writehexNybble
load r0, fArg1+2
cleq r0, r0, writehexNybble
load r0, fArg1+3
breq r0, r0, writehexNybble
fPrintNegativeExponent:
cleq r0, r0, popWord ; Remove result we don't need
load r0, #2d ; -
store ffff, r0
; The exponent is stored with a bias of 500
; unbiased + 500 = biased
; By this point we know unbiased < 0 and have printed the minus
; sign, so we want to print the negative of unbiased
; unbiased + 500 = biased
; unbiased = -500 + biased
; -unbiased = 500 - biased
; We implement subtraction using 9's complement, where each
; digit of the subtrahend is subtracted from 9 before being
; added to the corresponding minuend. This by itself gives a
; result that is one too small, so we add one to it (c.f. binary
; subtraction, where we negate the subtrahend and then add one).
; 500 + 1 + 999 - biased
; = 500 + 1000 - biased (1000 = 0 mod 1000)
; = 500 - biased
; Ones
xor r0, r0
load r1, #1
cleq r0, r0, pushWord
xor r0, r0
load r1, fArg1+3
cleq r0, r0, pushWord
cleq r0, r0, bcdDigit9sComplement
cleq r0, r0, bcdDigitAdd
cleq r0, r0, stSwap
; Tens
xor r0, r0
load r1, fArg1+2
cleq r0, r0, pushWord
cleq r0, r0, bcdDigit9sComplement
cleq r0, r0, bcdDigitAdd
cleq r0, r0, stSwap
; Hundreds
xor r0, r0
load r1, #5
cleq r0, r0, pushWord
xor r0, r0
load r1, fArg1+1
cleq r0, r0, pushWord
cleq r0, r0, bcdDigit9sComplement
cleq r0, r0, bcdDigitAdd3
cleq r0, r0, stSwap
cleq r0, r0, popWord
; Print
cleq r0, r0, popWord
xor r0, r0
or r0, r1
cleq r0, r0, writehexNybble
cleq r0, r0, popWord
xor r0, r0
or r0, r1
cleq r0, r0, writehexNybble
cleq r0, r0, popWord
xor r0, r0
or r0, r1
breq r0, r0, writehexNybble
fPrintOverflow:
load r0, #4f ; O
store ffff, r0
load r0, #76 ; v
store ffff, r0
load r0, #65 ; e
store ffff, r0
load r0, #72 ; r
store ffff, r0
load r0, #66 ; f
store ffff, r0
load r0, #6c ; l
store ffff, r0
load r0, #6f ; o
store ffff, r0
load r0, #77 ; w
store ffff, r0
ret
; ==================================================================
; Stack-based functions
; ==================================================================
; ------------------------------------------------------------------
; Arithmetic
; ------------------------------------------------------------------
; a b -- a+b
stAdd:
cleq r0, r0, popWord
cleq r0, r0, tmpStoreWord01
cleq r0, r0, popWord
cleq r0, r0, tmpLoadWord23
cleq r0, r0, addWord
breq r0, r0, pushWord
; a b -- a-b
stSub:
cleq r0, r0, popWord
cleq r0, r0, tmpStoreWord01
cleq r0, r0, popWord
cleq r0, r0, tmpLoadWord23
cleq r0, r0, subWord
breq r0, r0, pushWord
; a -- a+1
stInc:
cleq r0, r0, popWord
cleq r0, r0, incWord
breq r0, r0, pushWord
; a -- a-1
stDec:
cleq r0, r0, popWord
cleq r0, r0, decWord
breq r0, r0, pushWord
; a b -- a|b
stOr:
cleq r0, r0, popWord
cleq r0, r0, tmpStoreWord01
cleq r0, r0, popWord
cleq r0, r0, tmpLoadWord23
or r0, r2
or r1, r3
breq r0, r0, pushWord
; a -- a<<8
stShl8:
cleq r0, r0, popWord
xor r0, r0
or r0, r1
xor r1, r1
breq r0, r0, pushWord
; a -- a>>8
stShr8:
cleq r0, r0, popWord
xor r1, r1
or r1, r0
xor r0, r0
breq r0, r0, pushWord
; a -- a<<4
stShl4:
cleq r0, r0, popWord
shl r0, 4
xor r2, r2
or r2, r1
shr r2, 4
or r0, r2
shl r1, 4
breq r0, r0, pushWord
; a -- a>>4
stShr4:
cleq r0, r0, popWord
shr r1, 4
xor r2, r2
or r2, r0
shl r2, 4
or r1, r2
shr r0, 4
breq r0, r0, pushWord
; a -- a>>1
stShr1:
cleq r0, r0, popWord
shr r1, 1
xor r2, r2
or r2, r0
shl r2, 4
shl r2, 3
or r1, r2
shr r0, 1
breq r0, r0, pushWord
; a -- a&f
stLowNybble:
cleq r0, r0, popWord
xor r0, r0
load r2, #f
and r1, r2
breq r0, r0, pushWord
; a b --
; if a >= b then r0:r1 = 0001
; else r0:r1 = 0000
stGtEq:
cleq r0, r0, popWord
cleq r0, r0, tmpStoreWord01
cleq r0, r0, popWord
cleq r0, r0, tmpLoadWord23
cleq r0, r0, subWord
xor r0, r0
xor r1, r1
or r1, r2
ret
; ------------------------------------------------------------------
; I/O
; ------------------------------------------------------------------
; n -- n
stPrinthex:
cleq r0, r0, popWord
cleq r0, r0, writehexWord
breq r0, r0, newline
; -- c
stKey:
xor r0, r0
load r1, ffff
breq r0, r0, pushWord
; c --
stEmit:
cleq r0, r0, popWord
store ffff, r1
ret
; ------------------------------------------------------------------
; Stack manipulation
; ------------------------------------------------------------------
; a - a a
stDup:
cleq r0, r0, peekWord
breq r0, r0, pushWord
; a b -- a b a
stOver:
cleq r0, r0, popWord
cleq r0, r0, tmpStoreWord01
cleq r0, r0, peekWord
cleq r0, r0, tmp2StoreWord01
cleq r0, r0, tmpLoadWord01
cleq r0, r0, pushWord
cleq r0, r0, tmp2LoadWord01
breq r0, r0, pushWord
; a b -- b a
stSwap:
cleq r0, r0, popWord
cleq r0, r0, tmpStoreWord01
cleq r0, r0, popWord
cleq r0, r0, tmp2StoreWord01
cleq r0, r0, tmpLoadWord01
cleq r0, r0, pushWord
cleq r0, r0, tmp2LoadWord01
breq r0, r0, pushWord
; a b c -- b c a
stRot:
cleq r0, r0, popWord
cleq r0, r0, tmpStoreWord01
cleq r0, r0, popWord
cleq r0, r0, tmp2StoreWord01
cleq r0, r0, popWord
cleq r0, r0, tmp3StoreWord01
cleq r0, r0, tmp2LoadWord01
cleq r0, r0, pushWord
cleq r0, r0, tmpLoadWord01
cleq r0, r0, pushWord
cleq r0, r0, tmp3LoadWord01
breq r0, r0, pushWord
; ------------------------------------------------------------------
; Memory
; ------------------------------------------------------------------
; b ptr --
stStoreByte:
cleq r0, r0, popWord
store storeByteHigh, r0
store storeByteLow, r1
cleq r0, r0, popWord
xor r0, r0
or r0, r1
breq r0, r0, storeByte
; ptr -- b
stLoadByte:
cleq r0, r0, popWord
store loadByteHigh, r0
store loadByteLow, r1
cleq r0, r0, loadByte
xor r1, r1
or r1, r0
xor r0, r0
breq r0, r0, pushWord
; n ptr --
stStoreWord:
cleq r0, r0, popWord
cleq r0, r0, tmpStoreWord01
store storeByteHigh, r0
store storeByteLow, r1
cleq r0, r0, peekWord
cleq r0, r0, storeByte
cleq r0, r0, tmpLoadWord01
cleq r0, r0, incWord
store storeByteHigh, r0
store storeByteLow, r1
cleq r0, r0, popWord
xor r0, r0
or r0, r1
breq r0, r0, storeByte
; ptr -- n
stLoadWord:
cleq r0, r0, peekWord
store loadByteHigh, r0
store loadByteLow, r1
cleq r0, r0, loadByte
cleq r0, r0, tmpStoreWord01
cleq r0, r0, popWord
cleq r0, r0, incWord
store loadByteHigh, r0
store loadByteLow, r1
cleq r0, r0, loadByte
xor r2, r2
or r2, r0
cleq r0, r0, tmpLoadWord01
xor r1, r1
or r1, r2
breq r0, r0, pushWord
; ptr n --
stZeroMem:
cleq r0, r0, peekWord
or r0, r1
xor r2, r2
breq r0, r2, stZeroMemDone
cleq r0, r0, stDec
cleq r0, r0, stSwap
cleq r0, r0, stDup
xor r0, r0
xor r1, r1
cleq r0, r0, pushWord
cleq r0, r0, stSwap
cleq r0, r0, stStoreByte
cleq r0, r0, stInc
cleq r0, r0, stSwap
breq r0, r0, stZeroMem
stZeroMemDone:
ret
; ==================================================================
; Low-level functions
; ==================================================================
; ------------------------------------------------------------------
; Memory
; ------------------------------------------------------------------
tmpWordHigh: data 0
tmpWordLow: data 0
tmpWord2High: data 0
tmpWord2Low: data 0
tmpWord3High: data 0
tmpWord3Low: data 0
; in:
; r0:r1 = word
tmpStoreWord01:
store tmpWordHigh, r0
store tmpWordLow, r1
ret
; out:
; r0:r1 = word
tmpLoadWord01:
load r0, tmpWordHigh
load r1, tmpWordLow
ret
; out:
; r2:r3 = word
tmpLoadWord23:
load r2, tmpWordHigh
load r3, tmpWordLow
ret
; in:
; r0:r1 = word
tmp2StoreWord01:
store tmpWord2High, r0
store tmpWord2Low, r1
ret
; out:
; r0:r1 = word
tmp2LoadWord01:
load r0, tmpWord2High
load r1, tmpWord2Low
ret
; in:
; r0:r1 = word
tmp3StoreWord01:
store tmpWord3High, r0
store tmpWord3Low, r1
ret
; out:
; r0:r1 = word
tmp3LoadWord01:
load r0, tmpWord3High
load r1, tmpWord3Low
ret
; out:
; r0:r1 = word
; clobbers:
; r2, r3
peekWord:
cleq r0, r0, popWord
store peekWordHigh, r0
store peekWordLow, r1
; Return SP back to where it was
load r0, SP+0
load r1, SP+1
xor r2, r2
load r3, #02
cleq r0, r0, addWord
store SP+0, r0
store SP+1, r1
load r0, peekWordHigh
load r1, peekWordLow
ret
peekWordHigh: data 0
peekWordLow: data 0
; in:
; r0:r1 = word
; clobbers:
; r0, r1, r2, r3
pushWord:
store pushWordLow, r1
cleq r0, r0, pushByte
load r0, pushWordLow
breq r0, r0, pushByte
pushWordLow: data 0
; out:
; r0:r1
; clobbers:
; r2, r3
popWord:
cleq r0, r0, popByte
store popWordLow, r0
cleq r0, r0, popByte
load r1, popWordLow
ret
popWordLow: data 0
; in:
; r0 = byte
; clobbers:
; r0, r1, r2, r3
pushByte:
load r2, SP+0
store storeByteHigh, r2
load r3, SP+1
store storeByteLow, r3
cleq r0, r0, storeByte
load r0, SP+0
load r1, SP+1
cleq r0, r0, incWord
store SP+0, r0
store SP+1, r1
ret
; out:
; r0 = byte
; clobbers:
; r1, r2, r3
popByte:
load r0, SP+0
load r1, SP+1
load r2, SPStart+0
load r3, SPStart+1
brneq r0, r2, popByteNoUnderflow
breq r1, r3, underflow
popByteNoUnderflow:
cleq r0, r0, decWord
store SP+0, r0
store SP+1, r1
store loadByteHigh, r0
store loadByteLow, r1
breq r0, r0, loadByte
; in:
; storeByteHigh:storeByteLow = address
; r0 = byte
storeByte:
data b0 ; store addr, r0
storeByteHigh: data 0
storeByteLow: data 0
ret
; in:
; loadByteHigh:loadByteLow = address
; out:
; r0 = byte
loadByte:
data a0 ; load r0, addr
loadByteHigh: data 0
loadByteLow: data 0
ret
; ------------------------------------------------------------------
; Arithmetic
; ------------------------------------------------------------------
; in:
; r0:r1 = first addend
; r2:r3 = second addend
; out:
; r0:r1 = result
; r2 = carryout
; clobbers:
; r3
addWord:
store addWordFirstHigh, r0
store addWordSecondHigh, r2
; Add low bytes
xor r0, r0
or r0, r3
cleq r0, r0, addByte
store addWordResultLow, r0
; Add high bytes and carry (in r1)
load r0, addWordFirstHigh
load r2, addWordSecondHigh
cleq r0, r0, addByte3
; Move carryout to r2
xor r2, r2
or r2, r1
load r1, addWordResultLow
ret
addWordFirstHigh: data 0
addWordSecondHigh: data 0
addWordResultLow: data 0
; in:
; r0:r1 = minuend
; r2:r3 = subtrahend
; out:
; r0:r1 = result
; r2 = carryout
; clobbers:
; r3
subWord:
store subWordMinuendHigh, r0
store subWordSubtrahendHigh, r2
; Subtract low bytes
; a - b = a + (-b) = a + ~b + 1
xor r0, r0
or r0, r3
nand r0, r0
load r2, #01
cleq r0, r0, addByte3
store subWordResultLow, r0
; Subtract high bytes with carry-in in r1
load r0, subWordMinuendHigh
load r2, subWordSubtrahendHigh
nand r2, r2
cleq r0, r0, addByte3
; Move carryout to r2
xor r2, r2
or r2, r1
load r1, subWordResultLow
ret
subWordMinuendHigh: data 0
subWordSubtrahendHigh: data 0
subWordResultLow: data 0
; in/out:
; r0:r1 = word
; clobbers:
; r2, r3
incWord:
store incWordHigh, r0
; Add 1 to low byte
load r0, #01
cleq r0, r0, addByte
store incWordLow, r0
; Add carry to high byte
load r0, incWordHigh
cleq r0, r0, addByte
load r1, incWordLow
ret
incWordHigh: data 0
incWordLow: data 0
; in/out:
; r0:r1 = word
; clobbers:
; r2, r3
decWord:
store decWordHigh, r0
; Add ff to low byte
load r0, #ff
cleq r0, r0, addByte
store decWordLow, r0
; Add ff and carry to high byte
load r0, #ff
load r2, decWordHigh
cleq r0, r0, addByte3
load r1, decWordLow
ret
decWordHigh: data 0
decWordLow: data 0
; in:
; r0 = first addend
; r1 = second addend
; r2 = third addend
; out:
; r0 = result
; r1 = carryout
; clobbers:
; r2, r3
addByte3:
store addByte3ThirdAddend, r2
cleq r0, r0, addByte
store addByte3Carryout, r1
load r1, addByte3ThirdAddend
cleq r0, r0, addByte
load r3, addByte3Carryout
or r1, r3
ret
addByte3ThirdAddend: data 0
addByte3Carryout: data 0
; in:
; r0 = first addend
; r1 = second addend
; out:
; r0 = result
; r1 = carryout
; clobbers:
; r2, r3
addByte:
; Initiliaze carryout to 0
xor r2, r2
addByteLoop:
; Copy second addend to r3
xor r3, r3
or r3, r1
; Calculate carries in r3
and r3, r0
or r2, r3 ; Accumulate carry into carryout
shl r3, 1
; Calculate sums in r0
xor r0, r1
; Copy carries into second addend
xor r1, r1
or r1, r3
; Loop until second addend is 0
xor r3, r3
brneq r1, r3, addByteLoop
; Shift carryout, as we want carry from highest place = 1
shr r2, 4
shr r2, 3
; Move carryout to r1
; (We know r1 is already 0 since it's the loop condition)
or r1, r2
ret
; ------------------------------------------------------------------
; Hex conversion routines
; ------------------------------------------------------------------
; in:
; r0 = number in range [0, 0xf]
; out:
; r0 = ascii character corresponding to input
; clobbers:
; r3
nybble2hex:
xor r3, r3
breq r0, r3, nybble2hex0
load r3, #01
breq r0, r3, nybble2hex1
load r3, #02
breq r0, r3, nybble2hex2
load r3, #03
breq r0, r3, nybble2hex3
load r3, #04
breq r0, r3, nybble2hex4
load r3, #05
breq r0, r3, nybble2hex5
load r3, #06
breq r0, r3, nybble2hex6
load r3, #07
breq r0, r3, nybble2hex7
load r3, #08
breq r0, r3, nybble2hex8
load r3, #09
breq r0, r3, nybble2hex9
load r3, #0a
breq r0, r3, nybble2hexa
load r3, #0b
breq r0, r3, nybble2hexb
load r3, #0c
breq r0, r3, nybble2hexc
load r3, #0d
breq r0, r3, nybble2hexd
load r3, #0e
breq r0, r3, nybble2hexe
load r3, #0f
breq r0, r3, nybble2hexf
breq r0, r0, error
nybble2hex0:
load r0, #30
ret
nybble2hex1:
load r0, #31
ret
nybble2hex2:
load r0, #32
ret
nybble2hex3:
load r0, #33
ret
nybble2hex4:
load r0, #34
ret
nybble2hex5:
load r0, #35
ret
nybble2hex6:
load r0, #36
ret
nybble2hex7:
load r0, #37
ret
nybble2hex8:
load r0, #38
ret
nybble2hex9:
load r0, #39
ret
nybble2hexa:
load r0, #61
ret
nybble2hexb:
load r0, #62
ret
nybble2hexc:
load r0, #63
ret
nybble2hexd:
load r0, #64
ret
nybble2hexe:
load r0, #65
ret
nybble2hexf:
load r0, #66
ret
; in:
; r0 = ascii hex digit
; out:
; r0 = corresponding nybble
; clobbers:
; r3
hex2nybble:
load r3, #30
breq r0, r3, hex2nybble0
load r3, #31
breq r0, r3, hex2nybble1
load r3, #32
breq r0, r3, hex2nybble2
load r3, #33
breq r0, r3, hex2nybble3
load r3, #34
breq r0, r3, hex2nybble4
load r3, #35
breq r0, r3, hex2nybble5
load r3, #36
breq r0, r3, hex2nybble6
load r3, #37
breq r0, r3, hex2nybble7
load r3, #38
breq r0, r3, hex2nybble8
load r3, #39
breq r0, r3, hex2nybble9
load r3, #61
breq r0, r3, hex2nybblea
load r3, #62
breq r0, r3, hex2nybbleb
load r3, #63
breq r0, r3, hex2nybblec
load r3, #64
breq r0, r3, hex2nybbled
load r3, #65
breq r0, r3, hex2nybblee
load r3, #66
breq r0, r3, hex2nybblef
load r3, #41
breq r0, r3, hex2nybblea
load r3, #42
breq r0, r3, hex2nybbleb
load r3, #43
breq r0, r3, hex2nybblec
load r3, #44
breq r0, r3, hex2nybbled
load r3, #45
breq r0, r3, hex2nybblee
load r3, #46
breq r0, r3, hex2nybblef
breq r0, r0, error
hex2nybble0:
xor r0, r0
ret
hex2nybble1:
load r0, #01
ret
hex2nybble2:
load r0, #02
ret
hex2nybble3:
load r0, #03
ret
hex2nybble4:
load r0, #04
ret
hex2nybble5:
load r0, #05
ret
hex2nybble6:
load r0, #06
ret
hex2nybble7:
load r0, #07
ret
hex2nybble8:
load r0, #08
ret
hex2nybble9:
load r0, #09
ret
hex2nybblea:
load r0, #0a
ret
hex2nybbleb:
load r0, #0b
ret
hex2nybblec:
load r0, #0c
ret
hex2nybbled:
load r0, #0d
ret
hex2nybblee:
load r0, #0e
ret
hex2nybblef:
load r0, #0f
ret
; ------------------------------------------------------------------
; Hex output
; ------------------------------------------------------------------
; in:
; r1:r0 = word
; clobbers:
; r0, r2, r3
writehexWord:
cleq r0, r0, writehexByte
; Move low byte to r0
xor r0, r0
or r0, r1
breq r0, r0, writehexByte
; in:
; r0 = byte
; clobbers:
; r0, r2, r3
writehexByte:
; Store copy of the byte (as r0 is modified by nybble2hex)
xor r2, r2
or r2, r0
; High nybble
shr r0, 4
cleq r0, r0, nybble2hex
store ffff, r0
; Low nybble
load r0, #0f
and r0, r2
cleq r0, r0, nybble2hex
store ffff, r0
ret
; in:
; r0 = nybble
; clobbers:
; r3
writehexNybble:
cleq r0, r0, nybble2hex
store ffff, r0
ret
; ------------------------------------------------------------------
; Common output routines
; ------------------------------------------------------------------
; clobbers:
; r3
newline:
load r3, #0d
store ffff, r3
load r3, #0a
store ffff, r3
ret
; noreturn
underflow:
load r0, #55 ; U
store ffff, r0
load r0, #6e ; n
store ffff, r0
load r0, #64 ; d
store ffff, r0
load r0, #65 ; e
store ffff, r0
load r0, #72 ; r
store ffff, r0
load r0, #66 ; f
store ffff, r0
load r0, #6c ; l
store ffff, r0
load r0, #6f ; o
store ffff, r0
load r0, #77 ; w
store ffff, r0
load r0, #20
store ffff, r0
; noreturn
error:
load r0, #65 ; e
store ffff, r0
load r0, #72 ; r
store ffff, r0
store ffff, r0
load r1, #6f ; o
store ffff, r1
store ffff, r0
cleq r0, r0, newline
halt
; ==================================================================
; Data
; ==================================================================
linelen:
data 0
data 0
linebufStart: addr end
linebufSize:
data 00
data 80
SP: addr end + 80
SPStart: addr end + 80
end: