Files
SplitBit-Emulator/Programs/CosmOS/Assembler/numbers.asm
T
AnachronautandClaude Opus 5 4fd8bf7b3f Step a Data Pointer with INCD and DECD, not DPUP and DPDN by one
DPUP takes an immediate, so an offset of one is legal and does exactly the
right thing. It is also three bytes where INCD is two, and reads as "offset
the pointer up by one" where INCD reads as "step the pointer".

56 of them across 15 files: the system, the assembler, the editor, and eight
test programs. CosmOS is 9,564 bytes to 9,537, the native assembler 11,648
to 11,635, and every program in the repository together 49 bytes lighter.

The worst offender was numbers.asm, written this week, where every sixteen
bit helper reaches the low byte and comes back the long way round. It is the
file every other part of the assembler includes, so it is the first thing
anybody reads when they go looking - and it was teaching them the long way.
Pattern matched off sbfs.asm rather than off the instruction table I had
just embedded in two programs.

THIS IS NOT TWO WAYS TO DO ONE THING. DPUP takes an arbitrary number, so one
is inevitably among them; INCD earns its place by making the common case a
byte cheaper. The overlap is structural and the choice is a usage question,
which is a linter's job rather than an ISA's - "DPUP.n 0d01: INCD.n does
this in a byte less" is a mechanical rule with no judgement in it.

Nothing needed re-recording, which was not a foregone conclusion: cosmosBreak
prints the system addresses the registers happened to hold, and they did not
move. Both assemblers still produce identical bytes and CosmOS still builds
itself to a fixed point.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
2026-08-21 18:16:27 -04:00

141 lines
2.8 KiB
NASM

; The small things every other part of the assembler needs: sixteen bit arithmetic, for
; something that counts in addresses, and one string comparison.
;
; sbfs.asm has routines like these and the assembler cannot use them: it does not include
; the filesystem, because it reaches the disk through the system's services instead. That
; is the no-linker tax, paid in about a hundred and fifty bytes, and it is cheaper than the
; two and a half kilobytes including sbfs.asm would cost.
;
; Everything here works on numbers in memory rather than in registers, because a CALL puts
; A, B and Data Pointers 0 to 2 back as it found them. Only memory survives a return.
;
; Numbers are stored most significant byte first, the way every number on this machine is.
;
; Written by Anachronaut
#Program
; The two byte number at DP0 becomes the one at DP2. Destination first, so a call reads
; the way an assignment does.
numSet:
LDA.2
STA.0
INCD.2
INCD.0
LDA.2
STA.0
RET
; The two byte number at DP0 becomes itself plus the one at DP2.
numAdd:
INCD.0
INCD.2
LDA.0
LDB.2
CCF
ADD
MVQA
STA.0
DECD.0
DECD.2
LDA.0
LDB.2
ADD ; Carries in from the low half. Nothing between touches it.
MVQA
STA.0
RET
; The two byte number at DP0 becomes itself less the one at DP2.
numTake:
INCD.0
INCD.2
LDA.0
LDB.2
CCF
SUB
MVQA
STA.0
DECD.0
DECD.2
LDA.0
LDB.2
SUB ; Borrows in from the low half.
MVQA
STA.0
RET
; Adds the byte in A to the two byte number at DP0.
numAddByte:
INCD.0
LDB.0
CCF
ADD
MVQA
STA.0
BNC numAddByteDone
DECD.0
LDA.0
INCA
STA.0
numAddByteDone:
RET
; Adds one to the two byte number at DP0.
numStep:
INCD.0
LDA.0
INCA
STA.0
BNC numStepDone ; It did not wrap, so the high byte is untouched.
DECD.0
LDA.0
INCA
STA.0
numStepDone:
RET
; Compares the two byte number at DP0 with the one at DP2. Q is zero if they are equal,
; and the Carry Flag is set if the one at DP0 is the smaller. Both come back, because
; neither Q nor the Status register is put back by a return.
numCompare:
LDA.0
LDB.2
CCF
SUB ; The high bytes settle it unless they are the same.
BNQ numCompareDone
INCD.0
INCD.2
LDA.0
LDB.2
CCF
SUB
numCompareDone:
RET
; Q is zero if the strings at DP0 and DP1 are the same, both ending in a zero byte.
;
; Down here rather than with the label table, where it started, because four separate
; parts want it: labels, vector names, which file has already been included, and which
; directive a keyword is.
sameText:
LDA.0
LDB.1
CCF
SUB
BNQ sameTextDone
LDA.0
BRA sameTextDone ; They ended together, so they matched all the way.
INCD.0
INCD.1
BRI sameText
sameTextDone:
RET
; The two byte number at DP0 becomes zero.
numZero:
RSTA
STA.0
INCD.0
STA.0
RET