Files

118 lines
3.4 KiB
NASM

; blinkdi.asm -- resident blink hooked on the display interrupt, via the
; firmware's own RAM trampoline at bit 00000DB0h.
;
; HOW IT HOOKS
; The CPU vector table lives in ROM, but RLONG(0xFFFFFEA0) = 00000DB0h and
; that RAM word is a 6-byte `JA <handler>` stub. `install` rewrites the
; stub's 32-bit target to `entry` (the opcode C080h is unchanged). The
; original target 3F7146D0h is the OS display handler.
;
; WHY THE RETURN-PC TRAMPOLINE
; A one-shot VRAM draw is repainted before it reaches the screen. Drawing
; inside the handler *before* the OS handler runs is also wiped by the
; handler's own full-screen work. So:
; entry (runs first) saves the interrupt's return PC, replaces it with
; `ret_hook`, then tail-jumps to the stock OS handler;
; ret_hook (runs after the OS handler's `reti`) draws the box and jumps
; back to the saved PC. The draw is therefore after the repaint.
;
; The box is at framebuffer (256,400), 64x64, on the Terminal Manager's white
; body. State toggles every 0600h invocations (~48 frames at the measured
; ~32 display interrupts/frame): black <-> white.
;
; Linked at bit 01840000h by dwt/visual/build.sh.
.text
_start: ; install, called by rdebug `jump`
pushst
dint
setf 32, 0, 0
movi entry, a0, L
move a0, @00000DC0h, 0 ; stub target (32 bits, two words)
setf 16, 0, 0
movi 0C080h, a0, L ; JA opcode (unchanged value)
move a0, @00000DB0h, 0
sub a0, a0
move a0, @0186F000h, 0 ; invocation counter = 0
move a0, @0186F002h, 0 ; state = 0 (black)
popst
rets
; ---- entry hook: save return PC, install ret_hook, run stock handler ------
entry:
move a0, @0186F010h, 1 ; save interrupted a0
setf 32, 0, 1
move *sp(020h), a0, 1 ; saved PC (SP -> ST, +20h -> PC)
move a0, @0186F004h, 1 ; orig_pc
movi ret_hook, a0, L
move a0, *sp(020h), 1 ; return via ret_hook instead
move @0186F010h, a0, 1 ; restore a0
ja 3F7146D0h ; stock OS display handler
; ---- ret hook: after the OS handler, draw, then go back -----------------
ret_hook:
move a0, @0186F010h, 1
getst a0
move a0, @0186F014h, 1 ; save interrupted ST
dint
move a1, @0186F020h, 1
move a2, @0186F024h, 1
move a3, @0186F028h, 1
move a4, @0186F02Ch, 1
move a5, @0186F030h, 1
move b0, @0186F034h, 1
setf 16, 0, 0
setf 32, 0, 1
move @0186F000h, a0, 0 ; counter
inc a0
move a0, @0186F000h, 0
cmpi 0600h, a0
jrne draw ; only flip state on wrap
sub a0, a0
move a0, @0186F000h, 0
move @0186F002h, a1, 0 ; current state
cmpi 0, a1
jrne was_white
movk 1, a1 ; 0 -> 1 (white)
jruc set_state
was_white:
sub a1, a1 ; 1 -> 0 (black)
set_state:
move a1, @0186F002h, 0
draw:
move @0186F002h, a1, 0
cmpi 0, a1
jrne pat_white
sub a2, a2 ; black
jruc pat_ready
pat_white:
movi 0FFFFh, a2, L ; white
pat_ready:
movi 04064100h, a3, L ; 04000000h + 400*1024 + 256
movi 64, a4, L ; 64 rows
movi 4, a5, L ; 4 words = 64 px
drow:
move a5, a1
dword:
move a2, *a3+, 0
dsjs a1, dword
addi 03C0h, a3 ; 1024 - 64 bits
dsjs a4, drow
move @0186F030h, a5, 1
move @0186F02Ch, a4, 1
move @0186F028h, a3, 1
move @0186F024h, a2, 1
move @0186F020h, a1, 1
; leave with orig_pc on the stack and every touched register restored
move @0186F004h, a0, 1 ; orig_pc
move a0, -*sp, 1 ; push return PC for `rets`
move @0186F010h, a0, 1 ; restore interrupted a0
move @0186F014h, b0, 1 ; interrupted ST
putst b0 ; restore IE/fields
move @0186F034h, b0, 1 ; restore interrupted b0
rets ; pop orig_pc and resume
.end