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 $D000_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 $D000_0012
. . .
END MODULE