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
133 lines
3.2 KiB
NASM
133 lines
3.2 KiB
NASM
; Giving a bank number to a device's memory.
|
|
;
|
|
; This is the whole sequence an OS goes through: ask the bus registry which ports bring
|
|
; memory, give one of them a bank number, and then reach it with the controller like any
|
|
; other bank. A device's memory is unreachable until it has a number, and the controller
|
|
; is the only thing that can reach it even then.
|
|
;
|
|
; How big the bank is comes from the device rather than from this program. Capacity was
|
|
; settled when the machine was built, so software asserting it could only ever be wrong.
|
|
;
|
|
; The device on port 0x12 owns 256 bytes and fills them with whatever byte is written to
|
|
; its port, which stands in for a disk controller reading a sector.
|
|
;
|
|
; Correct output is:
|
|
; flags 01 the registry says port 0x12 brings memory
|
|
; 01 12 01 00 bank 5's record: present, owned by port 0x12, 256 bytes
|
|
; 5A 5A the device's memory, reached through the controller
|
|
; refused registering over one of the machine's own banks
|
|
; refused and registering memory from a port that brings none
|
|
|
|
#Include print.asm
|
|
|
|
#Program
|
|
|
|
start:
|
|
; Ask the registry whether port 0x12 brings memory.
|
|
INIA 0x12
|
|
OUTA 0xFF
|
|
INA 0xFF ; Class, which we do not need here.
|
|
INA 0xFF ; Flags: bit 0 says it brings memory.
|
|
PSHA
|
|
SETD.0 Flags
|
|
CALL printString
|
|
CALL blankSpace
|
|
POPA
|
|
CALL printByteHex
|
|
CALL lineFeed
|
|
|
|
; Tell the device to fill its memory, the way a disk controller would be told to read.
|
|
INIA 0x5A
|
|
OUTA 0x12
|
|
|
|
; Give that memory bank number 5.
|
|
INIA 0d5
|
|
OUTA 0xE3 ; DestBank = 5, the number being handed out.
|
|
INIA 0x12
|
|
OUTA 0xE2 ; SourceLow = the port that owns it.
|
|
INIA 0x03
|
|
OUTA 0xE8 ; RegisterBank.
|
|
|
|
; Read bank 5's record out of the bank table. Eight bytes a bank, so bank 5 is at 40.
|
|
INIA 0d2
|
|
OUTA 0xE0
|
|
RSTA
|
|
OUTA 0xE1
|
|
INIA 0d40
|
|
OUTA 0xE2
|
|
CALL readAndPrint ; Flags: present.
|
|
CALL readAndPrint ; Owner: port 0x12.
|
|
CALL readAndPrint ; Capacity, high byte.
|
|
CALL readAndPrint ; Capacity, low byte.
|
|
CALL lineFeed
|
|
|
|
; Now reach the device's memory the way any other bank is reached.
|
|
INIA 0d5
|
|
OUTA 0xE0
|
|
RSTA
|
|
OUTA 0xE1
|
|
OUTA 0xE2
|
|
CALL readAndPrint
|
|
CALL readAndPrint
|
|
CALL lineFeed
|
|
|
|
; Banks 0 to 2 are the machine's own and cannot be handed out.
|
|
INIA 0d1
|
|
OUTA 0xE3
|
|
INIA 0x12
|
|
OUTA 0xE2
|
|
INIA 0x03
|
|
OUTA 0xE8 ; Refused.
|
|
|
|
; Neither can memory from a port that brings none. Port 0x10 is the test device.
|
|
INIA 0d6
|
|
OUTA 0xE3
|
|
INIA 0x10
|
|
OUTA 0xE2
|
|
INIA 0x03
|
|
OUTA 0xE8 ; Refused.
|
|
HALT
|
|
|
|
readAndPrint:
|
|
INA 0xE9
|
|
CALL printByteHex
|
|
CALL blankSpace
|
|
RET
|
|
|
|
; Both refusals arrive here, and they really are the same fault: a bank that cannot be
|
|
; registered, for two different reasons. Saying so twice is more honest than inventing a
|
|
; distinction the machine does not draw.
|
|
bankHandler:
|
|
SETD.0 Refused
|
|
CALL printString
|
|
CALL lineFeed
|
|
|
|
; Step past the refused command write, which is an opcode and a port.
|
|
MVSD.0
|
|
DPUP.0 0d14
|
|
LDA.0
|
|
CCF
|
|
INIB 0d2
|
|
ADD
|
|
MVQA
|
|
STA.0
|
|
BRC bankCarried
|
|
RETI
|
|
bankCarried:
|
|
DECD.0
|
|
LDA.0
|
|
INCA
|
|
STA.0
|
|
RETI
|
|
|
|
#Data
|
|
|
|
Flags:
|
|
"flags"
|
|
Refused:
|
|
"refused"
|
|
|
|
#Vectors
|
|
|
|
BankFault bankHandler
|