Files
SplitBit-Emulator/Programs/Examples/replCalculator.asm
T
Anachronaut e1273337c4 Two mechanical fixes SplitLint found: MVQA, and RSTA for zero
Twenty four places moved Q into A or B by pushing it and popping it back.
That is four bus cycles and two bytes to do what MVQA does in one of each,
and several of them are inside loops - Life, the calculator, int8. Nineteen
more loaded zero with INIA 0d0 where RSTA says the same thing in one byte.

Both are equivalent at the CPU rather than by assertion: RSTA and INIA both
leave Status alone, and PSHQ followed by POPA nets to A = Q with the Stack
Pointer where it started. The one difference is that the pair leaves a copy
of Q in memory just below the Stack Pointer and MVQA does not, which
nothing here reads.

Five recorded outputs moved and every one of them says the change worked:

- 16x16Life fits five more generations into the same cycle budget, the
  first 457 lines identical, because the loop got cheaper.
- Life.sbx is 1409 bytes rather than 1411, in three tests that list it.
- Edit.sbx is 1995 rather than 1996.

That last one broke a check I added this morning, and the hole is worth
recording: the CosmOS README's claim about Edit's size did not have the
word "Edit" on the same line as the number, because the subject was in the
sentence before, so the check that measures quoted sizes skipped it
silently. The sentence now names what it is talking about, which makes it
both checkable and clearer, and the check fails on a wrong number there.

Comments on either half of a replaced pair are carried onto the
instruction that replaces them, so nothing anybody wrote was lost.
2026-08-26 17:53:10 -04:00

401 lines
4.4 KiB
NASM

; Interactive hexadecimal calculator for SplitBit.
;
; Enter expressions as:
; HH operator HH
; Whitespace is optional. Supported operators are + - * & | and ^.
; Arithmetic wraps to eight bits. Enter Q to quit.
#Include print.asm
#Program
start:
SETD Welcome
CALL printString
repl:
SETD Prompt
CALL printString
CALL readNonSpace
MVQA
; Q, q, or end-of-file exits.
INIB 0xFF
XOR
BRQ quit
INIB 0x51
XOR
BRQ quit
INIB 0x71
XOR
BRQ quit
CALL readHexByteFirst
SETD LeftOperand
STQ
CALL parseIsInvalid
BRQ inputError
CALL readNonSpace
MVQA
SETD Operator
STA
CALL readNonSpace
MVQA
CALL readHexByteFirst
SETD RightOperand
STQ
CALL parseIsInvalid
BRQ inputError
CALL discardLine
CALL evaluate
SETD Result
STQ
CALL operatorIsInvalid
BRQ operatorError
SETD Result
LDA
CALL printByteHex
CALL lineFeed
BRI repl
inputError:
CALL discardLine
SETD BadInput
CALL printString
BRI repl
operatorError:
SETD BadOperator
CALL printString
BRI repl
quit:
SETD Goodbye
CALL printString
HALT
; Read a character other than space, tab, CR, or LF. Return it in Q.
readNonSpace:
readNonSpaceLoop:
INA 0x00
INIB 0x20
XOR
BRQ readNonSpaceLoop
INIB 0x09
XOR
BRQ readNonSpaceLoop
INIB 0x0A
XOR
BRQ readNonSpaceLoop
INIB 0x0D
XOR
BRQ readNonSpaceLoop
RSTB
CCF
ADD
RET
; A contains the first hexadecimal digit. Read the second and return the byte
; in Q. Q is 0xFF on malformed input.
readHexByteFirst:
CALL clearParseStatus
CALL hexNibble
MVQA
INIB 0xFF
XOR
BRQ invalidByte
RSTB
SHL
SHL
SHL
SHL
PSHA
CALL readNonSpace
MVQA
CALL hexNibble
MVQA
INIB 0xFF
XOR
BRQ invalidLowNibble
POPB
OR
RET
invalidLowNibble:
POPB
invalidByte:
PSHD
SETD ParseStatus
INIA 0x01
STA
POPD
INIA 0xFF
RSTB
CCF
ADD
RET
; Convert the ASCII hexadecimal digit in A to a value in Q.
; Uppercase and lowercase letters are accepted. Q=0xFF means invalid.
hexNibble:
PSHA
; Try 0 through 9.
INIB 0x30
CCF
SUB
BRC tryUpperHex
MVQA
INIB 0d10
CCF
SUB
BRC decimalNibble
tryUpperHex:
POPA
PSHA
INIB 0x41
CCF
SUB
BRC tryLowerHex
MVQA
INIB 0d06
CCF
SUB
BRC upperNibble
tryLowerHex:
POPA
PSHA
INIB 0x61
CCF
SUB
BRC badNibble
MVQA
INIB 0d06
CCF
SUB
BRC lowerNibble
badNibble:
POPA
INIA 0xFF
RSTB
CCF
ADD
RET
decimalNibble:
POPB
RSTB
CCF
ADD
RET
upperNibble:
POPB
INIB 0d10
CCF
ADD
RET
lowerNibble:
POPB
INIB 0d10
CCF
ADD
RET
; Return Q=0 if Q was 0xFF, otherwise return a nonzero value.
resultIsInvalid:
MVQA
INIB 0xFF
XOR
RET
clearParseStatus:
PSHD
SETD ParseStatus
RSTA
STA
POPD
RET
parseIsInvalid:
PSHD
SETD ParseStatus
LDA
INIB 0x01
XOR
POPD
RET
operatorIsInvalid:
PSHD
SETD OperatorStatus
LDA
INIB 0x01
XOR
POPD
RET
; Evaluate the stored expression and return its result in Q.
evaluate:
PSHD
SETD OperatorStatus
RSTA
STA
POPD
SETD Operator
LDA
INIB 0x2B
XOR
BRQ evaluateAdd
INIB 0x2D
XOR
BRQ evaluateSubtract
INIB 0x2A
XOR
BRQ evaluateMultiply
INIB 0x26
XOR
BRQ evaluateAnd
INIB 0x7C
XOR
BRQ evaluateOr
INIB 0x5E
XOR
BRQ evaluateXor
SETD OperatorStatus
INIA 0x01
STA
RSTA
RSTB
CCF
ADD
RET
evaluateAdd:
SETD LeftOperand
LDA
INCD
LDB
CCF
ADD
RET
evaluateSubtract:
SETD LeftOperand
LDA
INCD
LDB
CCF
SUB
RET
evaluateAnd:
SETD LeftOperand
LDA
INCD
LDB
AND
RET
evaluateOr:
SETD LeftOperand
LDA
INCD
LDB
OR
RET
evaluateXor:
SETD LeftOperand
LDA
INCD
LDB
XOR
RET
evaluateMultiply:
RSTA
SETD Product
STA
multiplyLoop:
SETD RightOperand
LDA
BRA multiplyDone
DECA
STA
SETD Product
LDA
SETD LeftOperand
LDB
CCF
ADD
SETD Product
STQ
BRI multiplyLoop
multiplyDone:
SETD Product
LDA
RSTB
CCF
ADD
RET
; Consume the rest of the current input line.
discardLine:
discardLoop:
INA 0x00
INIB 0x0A
XOR
BRQ discardDone
INIB 0xFF
XOR
BRQ discardDone
BRI discardLoop
discardDone:
RET
#Data
LeftOperand:
0x00
RightOperand:
0x00
Operator:
0x00
Product:
0x00
Result:
0x00
ParseStatus:
0x00
OperatorStatus:
0x00
Welcome:
"SplitBit calculator (+ - * & | ^), Q quits."
Prompt:
0x0A
"> "
BadInput:
"Invalid hexadecimal input."
BadOperator:
"Unknown operator."
Goodbye:
"Goodbye!"
0x0A