SLEEP
Temporarily put the CPU to sleep to save electrical power.
| Opcode | Bytes | Cycles | Form |
|---|---|---|---|
| $13 | 1 | 2 | sleep |
Notes
-
This instruction temporarily stops the virtual computer's clock, preventing any instructions from being executed.
-
Whenever certain events occur, the hardware automatically sets corresponding bits in the
io::irq_pendingI/O location. ("IRQ" stands for interrupt request, although Hybrix currently does not yet provide interrupt handlers.) -
After executing a
sleepinstruction, the CPU will wake when one of theio::irq_pendingvalues matchesio::irq_wake_mask; that is, if the bitwise AND of these two values becomes nonzero. -
The total timing cost for
sleepis 1 cycle transitioning to sleep, plus 1 cycle transitioning to wake, plus whatever time was spent waiting. -
This instruction does not affect the CPU condition flags.
Example usage
The framework's engine::wait_for_paint() function uses the sleep instruction to wait for the Palix video display to finish painting:
# ---------------------------------------------------------------------------
# wait for the next video frame to get painted.
# this also synchronizes the main loop to at most 30 fps.
func wait_for_paint()
1 -> io::irq_pending # "write 1 to clear" irq_pending
3 -> io::paint_mode # request paint
1 -> io::irq_wake_mask # sleep until the paint finishes
kernel::sleep()
end func
Note that io::irq_pending must be reset before sleeping. We clear its bits by writing 1s instead of 0s, which is the so-called "Write 1 to Clear" (W1C) convention. (W1C avoids a hardware race condition that could otherwise occur if a program had to read the old value, clear some of its bits, and then store the new value.)
Also, note that we clear io::irq_pending before requesting a paint. This avoids another race condition that might occur if painting happened immediately before sleep. In general, io::irq_pending should be cleared before starting whatever operation it will be tracking.
Here's another example that sleeps while waiting to generate audio events:
func start()
sound::init()
var track_index: int
io::cpu_event_value -> track_index
sound::play_song(art::songs[track_index])
loop
2 -> io::irq_pending # "write 1 to clear" irq_pending
sound::think()
2 -> io::irq_wake_mask # sleep until next jamdac read
kernel::sleep()
end loop
end func
I/O definitions
module io
. . .
# Used with irq_wake_mask below. Whenever the specified event occurs,
# the hardware sets the corresponding bit. The program can clear a bit
# by setting it (the "write 1 to clear" or "W1C" convention).
# +1 = whenever palix paints a video frame (buffered or unbuffered)
# +2 = whenever jamdac advances an io_audio_queue::read_address
var irq_pending: byte located at $d0_0010
# To save electricity, the CPU can be temporary paused using the "sleep"
# instruction. The CPU will wake up again when irq_pending matches any of
# the irq_wake_mask bits (level triggering).
var irq_wake_mask: byte located at $d0_0012
. . .
end module