Skip to main content

SLEEP

Temporarily put the CPU to sleep to save electrical power.

OpcodeBytesCyclesForm
$1312sleep

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_pending I/O location. ("IRQ" stands for interrupt request, although Hybrix currently does not yet provide interrupt handlers.)

  • After executing a sleep instruction, the CPU will wake when one of the io::irq_pending values matches io::irq_wake_mask; that is, if the bitwise AND of these two values becomes nonzero.

  • The total timing cost for sleep is 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