; 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 ` 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