Line 900: |
Line 900: |
| == Control Flow == | | == Control Flow == |
| | | |
− | Control flow is implemented using three independent stacks: an 8-deep IF stack, a 4-deep CALL stack, and a 4-deep LOOP stack. All stacks are initially empty. After every instruction, the stacks are checked in the order CALL->IF->LOOP and if a stack is popped, the program counter update overwrites any previous update, even if set by a JMP instruction. The IF/LOOP stacks can only pop one entry per instruction, whereas the CALL stack can be completely emptied. If the CALL stack is popped four times in a single instruction, the last program counter update is missed. Executing a BREAK on an empty LOOP stack hangs the GPU. The stacks are actually ring buffers: overflow overwrites the oldest entry, the stack size is clamped. | + | Control flow is implemented using four independent stacks: |
| + | |
| + | * 4-deep CALL stack |
| + | * 8-deep IF stack |
| + | * 4-deep LOOP stack |
| + | |
| + | All stacks are initially empty. After every instruction but before JMP takes effect, the PC is incremented and a copy is sent to each stack. Each stack is checked against its copy of the PC. If an entry is popped from the stack, the copied PC is updated and used for the next check of this stack, although the IF/LOOP stacks can each only pop one entry per instruction, whereas the CALL stack is checked again until it doesn't match or the stack is empty. The PC copy from the stack with the highest priority wins: LOOP (highest), IF, CALL, JMP, original PC (lowest). |
| + | |
| + | Special cases: |
| + | * JMP overwrites the PC *after* the stacks checks (and only if no stack was popped). |
| + | * Executing a BREAK on an empty LOOP stack hangs the GPU. |
| + | * A stack overflow discards the oldest element, so you could think of it as a queue or a ring buffer. |
| + | * If the CALL stack is popped four times in a row, the fourth update to its copy of the PC is missed (the third PC update will be propagated). Probably a hardware bug. |