Give the machine a frame to wait for
V3. The screen interrupts at each frame on hardware vector 0x30, and WAIT finally has something worth sleeping on. THERE WAS NO CLOCK. Every program that wanted to happen at a certain speed counted instructions and hoped, which is why Snake's pause silently halved the day a cycle stopped being an instruction and became a memory access - the program was right and the thing it was counting changed underneath it. A screen finishing sixty times a second is a real beat, and it is counted in the MACHINE'S cycles rather than the host's, so the same program sees the same number of frames in the same number of cycles however fast anything really ran. That is what makes a frame something a test can count and a recorded result can hold. Status bit 0 goes up when a frame has gone by and reading the status port puts it down, so a program with no handler can watch for it instead. Control bit 0 asks to be interrupted, and is OFF when the machine starts: an interrupt with nothing installed to catch it is a fault, so a screen that began interrupting the moment it was switched on would take down every program written before frames existed. More than one frame can pass between two looks, and the flag and the line are each one thing, so several still mean one of each. A missed frame is missed. Programs/Examples/frames.asm prints a dot a frame for a second: 1,000,324 cycles, and 996,460 of them spent asleep. That split is the thing worth seeing - a program that polled instead would print the same sixty dots, take the same second, and spend every cycle of it on the bus. Its header explains why waiting is not spinning and why a machine with a beat can stop guessing at one. Six checks in Tests/video.sh, and two of them are about the clock rather than the output, because the output cannot tell the difference. That the machine slept through nearly all of ten frames, and that polling three frames actually took three frames - a status flag that stayed up once set would print exactly the same character and look perfectly correct. Breaking the frame interrupt on purpose left a machine asleep for ever and hung the whole suite, which is a worse way to be told than a failing check. Tests/video.sh bounds its runs at ten seconds now, the way Tests/run.sh always has. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
co-authored by
Claude Opus 5
parent
3da020898c
commit
1174bd9af5
@@ -0,0 +1,97 @@
|
||||
; frames.asm
|
||||
; Waiting for the screen, which is the only regular beat this machine has.
|
||||
; Written by Anachronaut
|
||||
;
|
||||
; ---- There is no clock ----
|
||||
;
|
||||
; Nothing on a SplitBit can tell you how long a second is. Every program that wanted to
|
||||
; happen at a certain speed has counted instructions and hoped - which is why Snake's pause
|
||||
; quietly halved the day a cycle stopped being an instruction and became a memory access.
|
||||
; The program was right; the thing it was counting had changed underneath it.
|
||||
;
|
||||
; A screen finishes drawing sixty times a second, and that is a real beat. It is counted in
|
||||
; the machine's own cycles rather than the host's, so this program sees sixty frames a second
|
||||
; whether the emulator is running at its proper rate or as fast as it possibly can.
|
||||
;
|
||||
; ---- Waiting rather than spinning ----
|
||||
;
|
||||
; WAIT stops the machine until something interrupts it. That is not the same as looping until
|
||||
; a flag goes up, even though both take the same time and print the same thing: a machine in
|
||||
; WAIT is not using memory, so its cycles are counted as idle rather than as bus. Run this
|
||||
; and the last line says so - nearly every cycle it spent, it spent asleep.
|
||||
;
|
||||
; Which is the whole argument for having a frame to wait for. On real hardware that is a
|
||||
; machine that could be doing something else, or nothing at all and drawing less current.
|
||||
|
||||
#Program
|
||||
|
||||
start:
|
||||
SETD.0 Frames
|
||||
RSTA
|
||||
STA.0
|
||||
|
||||
; Ask the screen to interrupt at each frame, then let interrupts in. The screen does not do
|
||||
; this unless it is asked: an interrupt with nothing installed to catch it is a fault, so a
|
||||
; machine that started interrupting on its own would take down every program that had never
|
||||
; heard of frames.
|
||||
INIA 0x01
|
||||
OUTA 0x35
|
||||
SIF
|
||||
|
||||
everyFrame:
|
||||
; A dot a frame, so there is something to watch.
|
||||
INIA 0d46
|
||||
OUTA 0x00
|
||||
|
||||
; And nothing at all until the next one.
|
||||
WAIT
|
||||
|
||||
SETD.0 Frames
|
||||
LDA.0
|
||||
INIB 0d60 ; One second of them
|
||||
CCF
|
||||
SUB
|
||||
BNQ everyFrame
|
||||
|
||||
; Put the screen back the way it was found, and stop asking to be interrupted before
|
||||
; taking away the thing that would catch it.
|
||||
CIF
|
||||
RSTA
|
||||
OUTA 0x35
|
||||
SETD.0 Done
|
||||
RCAL say
|
||||
HALT
|
||||
|
||||
; ---- Called sixty times a second ----
|
||||
;
|
||||
; A handler runs between two instructions of whatever was going on, so it saves everything it
|
||||
; touches - which for an interrupt the machine does itself. RETI puts it all back.
|
||||
frame:
|
||||
SETD.0 Frames
|
||||
LDA.0
|
||||
INCA
|
||||
STA.0
|
||||
RETI
|
||||
|
||||
say:
|
||||
LDA.0
|
||||
BRA sayDone
|
||||
OUTA 0x00
|
||||
INCD.0
|
||||
BRI say
|
||||
sayDone:
|
||||
RRET
|
||||
|
||||
#Data
|
||||
|
||||
Frames:
|
||||
0x00
|
||||
Done:
|
||||
"
|
||||
that was a second
|
||||
"
|
||||
|
||||
#Vectors
|
||||
|
||||
Boot start
|
||||
Device 0x30 frame
|
||||
Reference in New Issue
Block a user