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
This commit is contained in:
Anachronaut
2026-08-21 18:16:27 -04:00
co-authored by Claude Opus 5
parent 12cb489268
commit 4fd8bf7b3f
15 changed files with 56 additions and 56 deletions
+4 -4
View File
@@ -653,12 +653,12 @@ appendNode:
; DP2 is a two byte count. Takes one off it. ; DP2 is a two byte count. Takes one off it.
takeOneOff: takeOneOff:
DPUP.2 0d01 INCD.2
LDA.2 LDA.2
DECA DECA
STA.2 STA.2
BNC takeOneDone ; No borrow, so the high half is untouched. BNC takeOneDone ; No borrow, so the high half is untouched.
DPDN.2 0d01 DECD.2
LDA.2 LDA.2
DECA DECA
STA.2 STA.2
@@ -766,12 +766,12 @@ printWord:
; DP0 is a two byte number, A is a byte. Adds the one to the other. ; DP0 is a two byte number, A is a byte. Adds the one to the other.
addByteToWord: addByteToWord:
DPUP.0 0d01 INCD.0
LDB.0 LDB.0
CCF CCF
ADD ADD
STQ.0 STQ.0
DPDN.0 0d01 DECD.0
LDA.0 LDA.0
RSTB RSTB
ADD ADD
+3 -3
View File
@@ -290,11 +290,11 @@ checksumLoop:
INCD.2 INCD.2
LDA.2 LDA.2
BNA checksumLow BNA checksumLow
DPDN.2 0d01 DECD.2
LDA.2 LDA.2
DECA DECA
STA.2 ; Borrow out of the high byte. STA.2 ; Borrow out of the high byte.
DPUP.2 0d01 INCD.2
INIA 0xFF INIA 0xFF
STA.2 STA.2
BRI checksumTest BRI checksumTest
@@ -333,7 +333,7 @@ stepIndex:
INCA INCA
STA.2 STA.2
BNC stepIndexDone BNC stepIndexDone
DPDN.2 0d01 DECD.2
LDA.2 LDA.2
INCA INCA
STA.2 STA.2
+1 -1
View File
@@ -735,7 +735,7 @@ emitStringLoop:
CALL numStep CALL numStep
SETD.1 EmitWalk SETD.1 EmitWalk
LDD.0.1 LDD.0.1
DPDN.0 0d01 DECD.0
LDA.0 LDA.0
BNA emitStringLoop ; The zero goes out with the rest and then stops the loop. BNA emitStringLoop ; The zero goes out with the rest and then stops the loop.
BRI emitDone BRI emitDone
+12 -12
View File
@@ -28,16 +28,16 @@ numSet:
; The two byte number at DP0 becomes itself plus the one at DP2. ; The two byte number at DP0 becomes itself plus the one at DP2.
numAdd: numAdd:
DPUP.0 0d01 INCD.0
DPUP.2 0d01 INCD.2
LDA.0 LDA.0
LDB.2 LDB.2
CCF CCF
ADD ADD
MVQA MVQA
STA.0 STA.0
DPDN.0 0d01 DECD.0
DPDN.2 0d01 DECD.2
LDA.0 LDA.0
LDB.2 LDB.2
ADD ; Carries in from the low half. Nothing between touches it. ADD ; Carries in from the low half. Nothing between touches it.
@@ -47,16 +47,16 @@ numAdd:
; The two byte number at DP0 becomes itself less the one at DP2. ; The two byte number at DP0 becomes itself less the one at DP2.
numTake: numTake:
DPUP.0 0d01 INCD.0
DPUP.2 0d01 INCD.2
LDA.0 LDA.0
LDB.2 LDB.2
CCF CCF
SUB SUB
MVQA MVQA
STA.0 STA.0
DPDN.0 0d01 DECD.0
DPDN.2 0d01 DECD.2
LDA.0 LDA.0
LDB.2 LDB.2
SUB ; Borrows in from the low half. SUB ; Borrows in from the low half.
@@ -66,14 +66,14 @@ numTake:
; Adds the byte in A to the two byte number at DP0. ; Adds the byte in A to the two byte number at DP0.
numAddByte: numAddByte:
DPUP.0 0d01 INCD.0
LDB.0 LDB.0
CCF CCF
ADD ADD
MVQA MVQA
STA.0 STA.0
BNC numAddByteDone BNC numAddByteDone
DPDN.0 0d01 DECD.0
LDA.0 LDA.0
INCA INCA
STA.0 STA.0
@@ -82,12 +82,12 @@ numAddByteDone:
; Adds one to the two byte number at DP0. ; Adds one to the two byte number at DP0.
numStep: numStep:
DPUP.0 0d01 INCD.0
LDA.0 LDA.0
INCA INCA
STA.0 STA.0
BNC numStepDone ; It did not wrap, so the high byte is untouched. BNC numStepDone ; It did not wrap, so the high byte is untouched.
DPDN.0 0d01 DECD.0
LDA.0 LDA.0
INCA INCA
STA.0 STA.0
+5 -5
View File
@@ -262,7 +262,7 @@ dirCheck:
; A file's length is its block count times 256 plus its tail, which is the block count ; A file's length is its block count times 256 plus its tail, which is the block count
; in the high byte and the tail in the low one. Nothing has to multiply anything. ; in the high byte and the tail in the low one. Nothing has to multiply anything.
SETD.0 SbfsFileBlocks SETD.0 SbfsFileBlocks
DPUP.0 0d01 INCD.0
LDA.0 LDA.0
SETD.1 DirSize SETD.1 DirSize
STA.1 STA.1
@@ -1281,14 +1281,14 @@ breakAddress:
SETD.0 SRegText SETD.0 SRegText
PSHD.3 PSHD.3
POPD.1 POPD.1
DPUP.1 0d1 INCD.1
CALL breakByte CALL breakByte
; And which bits of it those are, since a debug dump that makes you look the number up is ; And which bits of it those are, since a debug dump that makes you look the number up is
; only half of one. Halt is not among them: the machine is plainly not halted. ; only half of one. Halt is not among them: the machine is plainly not halted.
PSHD.3 PSHD.3
POPD.1 POPD.1
DPUP.1 0d1 INCD.1
LDA.1 LDA.1
PSHA PSHA
INIB 0x01 INIB 0x01
@@ -2283,12 +2283,12 @@ assembleNeedsValue:
; DP0 is a two byte address and A is how far to move it on. ; DP0 is a two byte address and A is how far to move it on.
stepCursor: stepCursor:
DPUP.0 0d01 INCD.0
LDB.0 LDB.0
CCF CCF
ADD ADD
STQ.0 STQ.0
DPDN.0 0d01 DECD.0
LDA.0 LDA.0
RSTB RSTB
ADD ADD
+22 -22
View File
@@ -101,7 +101,7 @@ sbfsFind:
SETD.1 SbfsBlock SETD.1 SbfsBlock
CALL sbfsCopyWord CALL sbfsCopyWord
SETD.0 SbfsDirBlocks SETD.0 SbfsDirBlocks
DPUP.0 0d01 INCD.0
LDA.0 LDA.0
SETD.1 SbfsLeft SETD.1 SbfsLeft
STA.1 ; Only the low byte: a directory of 256 blocks is 2048 files. STA.1 ; Only the low byte: a directory of 256 blocks is 2048 files.
@@ -163,7 +163,7 @@ sbfsFindFound:
PSHD.3 PSHD.3
POPD.0 POPD.0
DPUP.0 0d01 INCD.0
SETD.1 SbfsFileStart SETD.1 SbfsFileStart
CALL sbfsCopyWord CALL sbfsCopyWord
@@ -208,7 +208,7 @@ sbfsFirst:
SETD.1 SbfsWalkBlock SETD.1 SbfsWalkBlock
CALL sbfsCopyWord CALL sbfsCopyWord
SETD.0 SbfsDirBlocks SETD.0 SbfsDirBlocks
DPUP.0 0d01 INCD.0
LDA.0 LDA.0
SETD.1 SbfsWalkLeft SETD.1 SbfsWalkLeft
STA.1 ; Only the low byte, the same as sbfsFind. STA.1 ; Only the low byte, the same as sbfsFind.
@@ -308,7 +308,7 @@ sbfsWalkEnd:
sbfsWalkTake: sbfsWalkTake:
SETD.1 SbfsWalkAt SETD.1 SbfsWalkAt
LDD.0.1 LDD.0.1
DPUP.0 0d01 INCD.0
SETD.1 SbfsFileStart SETD.1 SbfsFileStart
CALL sbfsCopyWord CALL sbfsCopyWord
@@ -428,7 +428,7 @@ sbfsRead:
CALL sbfsCopyWord CALL sbfsCopyWord
SETD.0 SbfsFileBlocks SETD.0 SbfsFileBlocks
DPUP.0 0d01 INCD.0
LDA.0 LDA.0
SETD.1 SbfsLeft SETD.1 SbfsLeft
STA.1 STA.1
@@ -456,7 +456,7 @@ sbfsReadGot:
; On by a whole block. DPUP carries one byte at a time, so 256 takes two of them. ; On by a whole block. DPUP carries one byte at a time, so 256 takes two of them.
DPUP.3 0xFF DPUP.3 0xFF
DPUP.3 0x01 INCD.3
SETD.0 SbfsBlock SETD.0 SbfsBlock
CALL sbfsStepWord CALL sbfsStepWord
@@ -534,7 +534,7 @@ sbfsEntryBounds:
POPD.3 POPD.3
PSHD.3 PSHD.3
POPD.0 POPD.0
DPUP.0 0d01 INCD.0
SETD.1 SbfsEntryStart SETD.1 SbfsEntryStart
CALL sbfsCopyWord CALL sbfsCopyWord
SETD.0 SbfsEntryEnd SETD.0 SbfsEntryEnd
@@ -586,7 +586,7 @@ sbfsAllocTry:
SETD.1 SbfsBlock SETD.1 SbfsBlock
CALL sbfsCopyWord CALL sbfsCopyWord
SETD.0 SbfsDirBlocks SETD.0 SbfsDirBlocks
DPUP.0 0d01 INCD.0
LDA.0 LDA.0
SETD.1 SbfsLeft SETD.1 SbfsLeft
STA.1 STA.1
@@ -682,16 +682,16 @@ sbfsSetWord:
; The two byte number at DP0 becomes itself less the one at DP2. ; The two byte number at DP0 becomes itself less the one at DP2.
sbfsSubWord: sbfsSubWord:
DPUP.0 0d01 INCD.0
DPUP.2 0d01 INCD.2
LDA.0 LDA.0
LDB.2 LDB.2
CCF CCF
SUB SUB
MVQA MVQA
STA.0 STA.0
DPDN.0 0d01 DECD.0
DPDN.2 0d01 DECD.2
LDA.0 LDA.0
LDB.2 LDB.2
SUB ; Borrows in from the low half. SUB ; Borrows in from the low half.
@@ -730,7 +730,7 @@ sbfsCreate:
SETD.1 SbfsBlock SETD.1 SbfsBlock
CALL sbfsCopyWord CALL sbfsCopyWord
SETD.0 SbfsDirBlocks SETD.0 SbfsDirBlocks
DPUP.0 0d01 INCD.0
LDA.0 LDA.0
SETD.1 SbfsLeft SETD.1 SbfsLeft
STA.1 STA.1
@@ -781,7 +781,7 @@ sbfsCreateFill:
PSHD.3 PSHD.3
POPD.1 POPD.1
DPUP.1 0d01 INCD.1
SETD.0 SbfsFileStart SETD.0 SbfsFileStart
CALL sbfsCopyWord CALL sbfsCopyWord
@@ -859,7 +859,7 @@ sbfsWriteFile:
CALL sbfsSetWord CALL sbfsSetWord
CALL sbfsFileExtent CALL sbfsFileExtent
SETD.0 SbfsWantBlocks SETD.0 SbfsWantBlocks
DPUP.0 0d01 INCD.0
LDA.0 LDA.0
SETD.1 SbfsLeft SETD.1 SbfsLeft
STA.1 STA.1
@@ -872,7 +872,7 @@ sbfsWriteLoop:
CALL sbfsWriteBlock CALL sbfsWriteBlock
BNQ sbfsWriteFailed BNQ sbfsWriteFailed
DPUP.3 0xFF DPUP.3 0xFF
DPUP.3 0x01 INCD.3
SETD.0 SbfsBlock SETD.0 SbfsBlock
CALL sbfsStepWord CALL sbfsStepWord
SETD.0 SbfsLeft SETD.0 SbfsLeft
@@ -983,12 +983,12 @@ sbfsCopyWord:
; Adds one to the two byte number at DP0. ; Adds one to the two byte number at DP0.
sbfsStepWord: sbfsStepWord:
DPUP.0 0d01 INCD.0
LDA.0 LDA.0
INCA INCA
STA.0 STA.0
BNC sbfsStepDone ; It did not wrap, so the high byte is untouched. BNC sbfsStepDone ; It did not wrap, so the high byte is untouched.
DPDN.0 0d01 DECD.0
LDA.0 LDA.0
INCA INCA
STA.0 STA.0
@@ -998,16 +998,16 @@ sbfsStepDone:
; The two byte number at DP0 becomes itself plus the one at DP2. Only memory changes, so ; The two byte number at DP0 becomes itself plus the one at DP2. Only memory changes, so
; it survives the return: a routine cannot hand back a pointer or a register. ; it survives the return: a routine cannot hand back a pointer or a register.
sbfsAddWord: sbfsAddWord:
DPUP.0 0d01 INCD.0
DPUP.2 0d01 INCD.2
LDA.0 LDA.0
LDB.2 LDB.2
CCF CCF
ADD ADD
MVQA MVQA
STA.0 STA.0
DPDN.0 0d01 DECD.0
DPDN.2 0d01 DECD.2
LDA.0 LDA.0
LDB.2 LDB.2
ADD ; Carries in from the low half. Nothing between the two touches it. ADD ; Carries in from the low half. Nothing between the two touches it.
+1 -1
View File
@@ -179,7 +179,7 @@ refusedHandler:
BRC refusedCarried BRC refusedCarried
RETI RETI
refusedCarried: refusedCarried:
DPDN.0 0d01 DECD.0
LDA.0 LDA.0
INCA INCA
STA.0 STA.0
@@ -102,7 +102,7 @@ stepPastRefusal:
BRC carried BRC carried
RETI RETI
carried: carried:
DPDN.0 0d01 DECD.0
LDA.0 LDA.0
INCA INCA
STA.0 STA.0
+1 -1
View File
@@ -41,7 +41,7 @@ faultHandler:
RETI RETI
carried: carried:
DPDN.0 0d01 DECD.0
LDA.0 LDA.0
INCA INCA
STA.0 STA.0
+1 -1
View File
@@ -143,7 +143,7 @@ guardHandler:
BRC stepCarried BRC stepCarried
RETI RETI
stepCarried: stepCarried:
DPDN.0 0d01 DECD.0
LDA.0 LDA.0
INCA INCA
STA.0 STA.0
+1 -1
View File
@@ -50,7 +50,7 @@ refusalHandler:
RETI RETI
carried: carried:
DPDN.0 0d01 DECD.0
LDA.0 LDA.0
INCA INCA
STA.0 STA.0
+1 -1
View File
@@ -114,7 +114,7 @@ bankHandler:
BRC bankCarried BRC bankCarried
RETI RETI
bankCarried: bankCarried:
DPDN.0 0d01 DECD.0
LDA.0 LDA.0
INCA INCA
STA.0 STA.0
+1 -1
View File
@@ -89,7 +89,7 @@ showFound:
showContents: showContents:
SETD.0 Landing SETD.0 Landing
SETD.1 SbfsFileBlocks SETD.1 SbfsFileBlocks
DPUP.1 0d01 INCD.1
LDA.1 LDA.1
SETD.1 LeftHigh SETD.1 LeftHigh
STA.1 STA.1
+1 -1
View File
@@ -60,7 +60,7 @@ walkCheck:
; A file is its blocks times 256 plus its tail, which is the block count in the high ; A file is its blocks times 256 plus its tail, which is the block count in the high
; byte and the tail in the low one. Nothing has to multiply anything. ; byte and the tail in the low one. Nothing has to multiply anything.
SETD.0 SbfsFileBlocks SETD.0 SbfsFileBlocks
DPUP.0 0d01 INCD.0
LDA.0 LDA.0
SETD.1 Size SETD.1 Size
STA.1 STA.1
+1 -1
View File
@@ -24,7 +24,7 @@ start:
; The Stack Pointer points at the next free slot, so the byte pushed last sits one ; The Stack Pointer points at the next free slot, so the byte pushed last sits one
; above it, and the one before that sits two above. ; above it, and the one before that sits two above.
MVSD.0 MVSD.0
DPUP.0 0d01 INCD.0
LDA.0 ; 'K', the last one pushed. LDA.0 ; 'K', the last one pushed.
INCD.0 INCD.0
LDB.0 ; 'O', the one before it. LDB.0 ; 'O', the one before it.