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
211 lines
4.5 KiB
NASM
211 lines
4.5 KiB
NASM
; Blits and fills.
|
|
;
|
|
; A blit moves a run of bytes between any two banks, including from one place in a bank
|
|
; to another. A fill writes one byte across a run. Both are instantaneous: waiting
|
|
; belongs to a peripheral that has something to wait for, not to the moving of bytes.
|
|
;
|
|
; Everything a transfer will touch is checked before any of it moves, so a transfer that
|
|
; would run out of bank refuses outright rather than stopping halfway and leaving memory
|
|
; in a state nobody asked for. The last case here proves that: the blit is refused, and
|
|
; the bytes it would have written are still what they were.
|
|
;
|
|
; Correct output is:
|
|
; blitted copied from one place in Data Memory to another
|
|
; .... filled with a byte of our choosing
|
|
; DE AD blitted from Data Memory into Program Memory
|
|
; untouched a refused blit moved nothing at all
|
|
; ABABCDEFGH an overlapping blit slid the bytes rather than repeating one
|
|
|
|
#Include print.asm
|
|
|
|
#Program
|
|
|
|
start:
|
|
; ---- A blit inside Data Memory, which is what a memcpy is here. ----
|
|
SETD.0 Message
|
|
CALL aimSource
|
|
SETD.0 Landing
|
|
CALL aimDest
|
|
INIA 0d8
|
|
CALL setLength ; "blitted" and its terminator.
|
|
INIA 0x01
|
|
OUTA 0xE8 ; Blit.
|
|
|
|
SETD.0 Landing
|
|
CALL printString
|
|
CALL lineFeed
|
|
|
|
; ---- A fill, with a byte of our choosing rather than only zero. ----
|
|
SETD.0 Landing
|
|
CALL aimDest
|
|
INIA 0x2E ; '.', into SourceLow, which is where a fill takes its byte.
|
|
OUTA 0xE2
|
|
INIA 0d4
|
|
CALL setLength
|
|
INIA 0x02
|
|
OUTA 0xE8 ; Fill.
|
|
|
|
; Terminate it so printString knows where to stop.
|
|
SETD.0 Landing
|
|
DPUP.0 0d04
|
|
RSTA
|
|
STA.0
|
|
SETD.0 Landing
|
|
CALL printString
|
|
CALL lineFeed
|
|
|
|
; ---- Data Memory into Program Memory. This is what a loader does. ----
|
|
SETD.0 Code
|
|
CALL aimSource
|
|
SETD.0 Target
|
|
CALL aimDestProgram
|
|
INIA 0d2
|
|
CALL setLength
|
|
INIA 0x01
|
|
OUTA 0xE8 ; Blit.
|
|
|
|
; Read it back out of Program Memory to prove it landed.
|
|
SETD.0 Target
|
|
CALL aimSourceProgram
|
|
CALL readAndPrint
|
|
CALL readAndPrint
|
|
CALL lineFeed
|
|
|
|
; ---- A blit that would run past the end of bank 2. ----
|
|
; Bank 2 is 2048 bytes. Starting at 2040 and asking for 16 does not fit, so the whole
|
|
; thing is refused and Landing keeps what it already had.
|
|
SETD.0 Untouched
|
|
CALL aimSource
|
|
SETD.0 Landing
|
|
CALL aimDest
|
|
INIA 0d10
|
|
CALL setLength
|
|
INIA 0x01
|
|
OUTA 0xE8 ; This one works, so Landing says "untouched".
|
|
|
|
INIA 0d2
|
|
OUTA 0xE0 ; SourceBank = 2, the bank table.
|
|
INIA 0x07
|
|
OUTA 0xE1
|
|
INIA 0xF8
|
|
OUTA 0xE2 ; 0x07F8, eight bytes from the end.
|
|
SETD.0 Landing
|
|
CALL aimDest
|
|
INIA 0d16
|
|
CALL setLength
|
|
INIA 0x01
|
|
OUTA 0xE8 ; Refused: it would read past the end of bank 2.
|
|
|
|
SETD.0 Landing
|
|
CALL printString
|
|
CALL lineFeed
|
|
|
|
; ---- A blit whose source and destination overlap. ----
|
|
; Sliding a run of bytes along inside its own bank is an ordinary thing to want, so it
|
|
; works rather than quietly repeating the first byte the way a naive forward copy does.
|
|
SETD.0 Slide
|
|
CALL aimSource
|
|
SETD.0 Slide
|
|
DPUP.0 0d02
|
|
CALL aimDest
|
|
INIA 0d8
|
|
CALL setLength
|
|
INIA 0x01
|
|
OUTA 0xE8
|
|
|
|
SETD.0 Slide
|
|
CALL printString
|
|
CALL lineFeed
|
|
HALT
|
|
|
|
; ---- Helpers. DP0 holds the address to aim at. ----
|
|
|
|
aimSource:
|
|
INIA 0d1 ; Bank 1, Data Memory.
|
|
OUTA 0xE0
|
|
BRI aimSourceAddress
|
|
aimSourceProgram:
|
|
RSTA ; Bank 0, Program Memory.
|
|
OUTA 0xE0
|
|
aimSourceAddress:
|
|
PSHD.0
|
|
POPA
|
|
POPB
|
|
OUTB 0xE1
|
|
OUTA 0xE2
|
|
RET
|
|
|
|
aimDest:
|
|
INIA 0d1 ; Bank 1, Data Memory.
|
|
OUTA 0xE3
|
|
BRI aimDestAddress
|
|
aimDestProgram:
|
|
RSTA ; Bank 0, Program Memory.
|
|
OUTA 0xE3
|
|
aimDestAddress:
|
|
PSHD.0
|
|
POPA
|
|
POPB
|
|
OUTB 0xE4
|
|
OUTA 0xE5
|
|
RET
|
|
|
|
; A is the length, which is never more than a byte in this program.
|
|
setLength:
|
|
PSHA
|
|
RSTA
|
|
OUTA 0xE6
|
|
POPA
|
|
OUTA 0xE7
|
|
RET
|
|
|
|
readAndPrint:
|
|
INA 0xE9
|
|
CALL printByteHex
|
|
CALL blankSpace
|
|
RET
|
|
|
|
; The refused blit is caught so the program can carry on and show that nothing moved.
|
|
; The command write is two bytes, an opcode and a port, so stepping by two gets past it.
|
|
refusedHandler:
|
|
MVSD.0
|
|
DPUP.0 0d14
|
|
LDA.0
|
|
CCF
|
|
INIB 0d2
|
|
ADD
|
|
MVQA
|
|
STA.0
|
|
BRC refusedCarried
|
|
RETI
|
|
refusedCarried:
|
|
DECD.0
|
|
LDA.0
|
|
INCA
|
|
STA.0
|
|
RETI
|
|
|
|
; Never executed, only written over.
|
|
Target:
|
|
0x00 0x00
|
|
|
|
#Data
|
|
|
|
Message:
|
|
"blitted"
|
|
Untouched:
|
|
"untouched"
|
|
Code:
|
|
0xDE 0xAD
|
|
|
|
Landing:
|
|
#Reserve 0d16
|
|
|
|
Slide:
|
|
"ABCDEFGH"
|
|
#Reserve 0d4
|
|
|
|
#Vectors
|
|
|
|
BankFault refusedHandler
|