Files
SplitBit-Emulator/Programs/testPrograms/rawCallAndOffsets.asm
T
AnachronautandClaude Opus 5 af0360128b Sixty four instructions becomes seventy
The six settled back on the twenty fourth, built now.

RCAL and RRET are a call that puts nothing back. CALL restores A, B and Data
Pointers 0 through 2, which costs ten bytes of Stack and is why a subroutine
here can only hand anything back through Q, DP3 or memory. RCAL costs two and
restores nothing, which is what a short leaf routine wants and is unsafe in
exactly the way the name says.

They are a pair because the frames are different sizes: returning from one
through the other walks the Stack to somewhere that was never a return address.
That was the user's correction to the original proposal, which had a raw call
and no raw return.

DPUA and DPDA offset a Data Pointer by A; DPUW and DPDW by A and B together,
most significant first. DPUP and DPDN take a byte written into the program, so
moving a pointer by something just worked out meant storing it and loading it
back. Down as well as up on symmetry grounds, which was also the user's call -
the argument against it came from counting uses in a corpus written under the
constraint.

The opcodes sit where they belong: 0x16 and 0x1E immediately below CALL and RET,
and 0x4E through 0x51 at the end of the Data Pointer family. All six fit shapes
that already existed, so instructiontable.py needed only set membership and both
machine side copies of the table regenerated from it unchanged.

Checked at every level it exists at: the emulator runs them, the host assembler
encodes them, the monitor disassembles all six with the right lengths, and the
assembler that runs on the machine builds a program using them byte for byte
identically to the host - and that program runs.

The recorded test measures what the two calls COST as well as what they put
back, because an RCAL that quietly did what CALL does would still return to the
right place. It does not survive that: returned through RRET, it hangs.

docs.sh can read a two word number now. The count of instructions taking a Data
Pointer went past twenty, and the pattern only allowed one word, so the check
would have reported that the manual had stopped saying it rather than that the
number was wrong.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
2026-08-25 17:29:43 -04:00

130 lines
2.9 KiB
NASM

; The six instructions added after the first sixty four, and what each is for.
;
; Two of them are a call that puts nothing back, and four move a Data Pointer by a value
; worked out while the program is running rather than one written into it.
;
; WHY A SECOND KIND OF CALL. CALL puts A, B and Data Pointers 0 through 2 back the way it
; found them, which costs ten bytes of Stack and means a subroutine can only hand anything
; back through Q, DP3 or memory. That is the right default and it is what almost everything
; here uses. RCAL costs two bytes and puts nothing back at all, which is what a short leaf
; routine wants - and it is unsafe in exactly the way its name says, because everything the
; callee touches, the caller has lost.
;
; The two frames are different sizes, so RCAL must be returned from with RRET and CALL with
; RET. Crossing them walks the Stack to somewhere that was never a return address.
;
; WHY OFFSET BY A REGISTER. DPUP and DPDN take a byte written into the program, so moving a
; pointer by something just worked out meant storing it and loading it back. DPUA and DPDA
; take A; DPUW and DPDW take A and B together, which is how every sixteen bit value on this
; machine is carried between registers.
;
; Written by Anachronaut
#Program
start:
; ---- Offsetting by a byte, up and then back down ----
SETD.0 Text
INIA 0d7
DPUA.0
LDA.0
OUTA 0x00 ; the eighth character
INIA 0d4
DPDA.0
LDA.0
OUTA 0x00 ; and four before it
; ---- Offsetting by a whole sixteen bit value ----
SETD.1 Text
INIA 0x00
INIB 0d13
DPUW.1
LDA.1
OUTA 0x00
INIA 0x00
INIB 0d13
DPDW.1
LDA.1
OUTA 0x00 ; back where it started
; ---- What each kind of call puts back ----
;
; The same callee is reached both ways and sets A to something else. After the raw call
; that is what A holds; after the safe one it is not.
INIA 0x41
RCAL wrecker
OUTA 0x00 ; Z - the callee's
INIA 0x41
CALL polite
OUTA 0x00 ; A - put back
; ---- And what each costs ----
;
; Printed as a digit added to '0', so two bytes reads as "2" and ten reads as the
; character ten along from it.
MVSD.0
PSHD.0
POPB
POPA
SETD.2 Before
STB.2
RCAL rawCost
CALL safeCost
INIA 0x0A
OUTA 0x00
HALT
wrecker:
INIA 0x5A
RRET
polite:
INIA 0x5A
RET
; WRITTEN OUT TWICE RATHER THAN CALLED, because a call would put its own frame down on
; top of the one being measured and each of these would report ten bytes more than it
; costs. The first version of this did exactly that and printed twelve and twenty.
rawCost:
MVSD.1
PSHD.1
POPB
POPA
SETD.2 Before
LDA.2
CCF
SUB
MVQA
INIB 0x30
CCF
ADD
MVQA
OUTA 0x00
RRET
safeCost:
MVSD.1
PSHD.1
POPB
POPA
SETD.2 Before
LDA.2
CCF
SUB
MVQA
INIB 0x30
CCF
ADD
MVQA
OUTA 0x00
RET
#Data
Text:
"0123456789abcdefg"
Before:
0x00