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
128 lines
2.4 KiB
NASM
128 lines
2.4 KiB
NASM
; Writes Program Memory through the controller, and gets turned away from what it may
|
|
; not touch.
|
|
;
|
|
; Writing code is the thing SplitBit's instruction set deliberately cannot do. The
|
|
; controller can, which is what makes a loader possible. The marker below is written and
|
|
; then read back through the controller, so the change is observed rather than assumed.
|
|
;
|
|
; The two refusals are the other half. Bank 2 is the bank table and is read only, so
|
|
; writing to it is a GuardViolation. Bank 200 has nothing registered in it, so naming it
|
|
; is a BankFault. Both handlers step the saved address past the instruction that was
|
|
; refused, the way any handler carrying on past a fault has to.
|
|
;
|
|
; Correct output is:
|
|
; AA BB the marker after being written through the controller
|
|
; readonly
|
|
; nobank
|
|
; done
|
|
|
|
#Include print.asm
|
|
|
|
#Program
|
|
|
|
start:
|
|
; Aim both ends of the controller at the marker.
|
|
SETD.0 Marker
|
|
PSHD.0
|
|
POPA
|
|
POPB
|
|
PSHA
|
|
PSHB
|
|
|
|
RSTA
|
|
OUTA 0xE0 ; SourceBank = 0
|
|
OUTA 0xE3 ; DestBank = 0
|
|
POPB
|
|
OUTB 0xE1 ; SourceHigh
|
|
OUTB 0xE4 ; DestHigh
|
|
POPA
|
|
OUTA 0xE2 ; SourceLow
|
|
OUTA 0xE5 ; DestLow
|
|
|
|
; Write two bytes over the marker. This is Program Memory being changed at run time.
|
|
INIA 0xAA
|
|
OUTA 0xE9
|
|
INIA 0xBB
|
|
OUTA 0xE9
|
|
|
|
; Read them back the same way, to prove they landed.
|
|
CALL readAndPrint
|
|
CALL readAndPrint
|
|
CALL lineFeed
|
|
|
|
; Bank 2 is the bank table, and it is read only.
|
|
INIA 0d2
|
|
OUTA 0xE3
|
|
RSTA
|
|
OUTA 0xE4
|
|
OUTA 0xE5
|
|
INIA 0d1
|
|
OUTA 0xE9 ; Refused: GuardViolation.
|
|
|
|
; Bank 200 has nothing in it.
|
|
INIA 0d200
|
|
OUTA 0xE3
|
|
INIA 0d1
|
|
OUTA 0xE9 ; Refused: BankFault.
|
|
|
|
SETD.0 Done
|
|
CALL printString
|
|
CALL lineFeed
|
|
HALT
|
|
|
|
readAndPrint:
|
|
INA 0xE9
|
|
CALL printByteHex
|
|
CALL blankSpace
|
|
RET
|
|
|
|
readOnlyHandler:
|
|
SETD.0 ReadOnly
|
|
CALL printString
|
|
CALL lineFeed
|
|
BRI stepPastRefusal
|
|
|
|
noBankHandler:
|
|
SETD.0 NoBank
|
|
CALL printString
|
|
CALL lineFeed
|
|
BRI stepPastRefusal
|
|
|
|
; Steps the saved address past the refused instruction. OUT is two bytes, an opcode and
|
|
; a port, so two is what it takes.
|
|
stepPastRefusal:
|
|
MVSD.0
|
|
DPUP.0 0d14
|
|
LDA.0
|
|
CCF
|
|
INIB 0d2
|
|
ADD
|
|
MVQA
|
|
STA.0
|
|
BRC carried
|
|
RETI
|
|
carried:
|
|
DECD.0
|
|
LDA.0
|
|
INCA
|
|
STA.0
|
|
RETI
|
|
|
|
; Never executed.
|
|
Marker:
|
|
0x00 0x00
|
|
|
|
#Data
|
|
|
|
ReadOnly:
|
|
"readonly"
|
|
NoBank:
|
|
"nobank"
|
|
Done:
|
|
"done"
|
|
|
|
#Vectors
|
|
|
|
GuardViolation readOnlyHandler
|
|
BankFault noBankHandler
|