457 lines
12 KiB
Markdown
457 lines
12 KiB
Markdown
< [Back](chapter-1.md) | [Summary](table-of-contents.md) | [Next](chapter-3.md) >
|
|
|
|
# ⚙️ ** PVM (PROMETEU VM) — Instruction Set**
|
|
|
|
## 1. Overview
|
|
|
|
The **PROMETEU VM** is a mandatory virtual machine always present in the logical hardware:
|
|
|
|
* **stack-based**
|
|
* deterministic
|
|
* cycle-oriented
|
|
* designed for teaching and inspection
|
|
|
|
It exists to:
|
|
|
|
* map high-level language concepts
|
|
* make computational cost visible
|
|
* allow execution analysis
|
|
* serve as the foundation of the PROMETEU cartridge
|
|
|
|
> The PROMETEU VM is simple by choice.
|
|
> Simplicity is a pedagogical tool.
|
|
|
|
---
|
|
|
|
## 2. Execution Model
|
|
|
|
### 2.1 Main Components
|
|
|
|
The VM has:
|
|
|
|
* **PC (Program Counter)** — next instruction
|
|
* **Operand Stack** — value stack
|
|
* **Call Stack** — stores execution frames for function calls
|
|
* **Scope Stack** — stores frames for blocks within a function
|
|
* **Heap** — dynamic memory
|
|
* **Globals** — global variables
|
|
* **Constant Pool** — literals and references
|
|
* **ROM** — cartridge bytecode
|
|
* **RAM** — mutable data
|
|
|
|
---
|
|
|
|
### 2.2 Execution Cycle
|
|
|
|
Each instruction executes:
|
|
|
|
```
|
|
FETCH → DECODE → EXECUTE → ADVANCE PC
|
|
```
|
|
|
|
Properties:
|
|
|
|
* every instruction has a fixed cost in cycles
|
|
* there is no invisible implicit work
|
|
* execution is fully deterministic
|
|
|
|
---
|
|
|
|
## 3. Fundamental Types
|
|
|
|
| Type | Description |
|
|
| --------- | ------------------------- |
|
|
| `int32` | 32-bit signed integer |
|
|
| `int64` | 64-bit signed integer |
|
|
| `float` | 64-bit floating point |
|
|
| `boolean` | true/false |
|
|
| `string` | immutable UTF-8 |
|
|
| `null` | absence of value |
|
|
| `ref` | heap reference |
|
|
|
|
### 3.1 Numeric Promotion
|
|
The VM promotes types automatically during operations:
|
|
* `int32` + `int32` → `int32`
|
|
* `int32` + `int64` → `int64`
|
|
* `int` + `float` → `float`
|
|
* Bitwise operations promote `int32` to `int64` if any operand is `int64`.
|
|
|
|
Do not exist:
|
|
|
|
* magic coercions
|
|
* implicit casts
|
|
* silent overflows
|
|
|
|
---
|
|
|
|
## 4. Stack Conventions & Calling ABI
|
|
|
|
* Operations use the top of the stack.
|
|
* Results always return to the stack.
|
|
* **LIFO Order:** Last pushed = first consumed.
|
|
* **Mandatory Return:** Every function (`Call`) and `Syscall` MUST leave exactly **one** value on the stack upon completion. If there is no meaningful value to return, `Null` must be pushed.
|
|
|
|
### 4.1 Calling Convention (Call / Ret)
|
|
|
|
1. **Arguments:** The caller pushes arguments in order (arg0, arg1, ..., argN-1).
|
|
2. **Execution:** The `Call` instruction specifies `args_count`. These `N` values become the **locals** of the new frame (local 0 = arg0, local 1 = arg1, etc.).
|
|
3. **Return Value:** Before executing `Ret`, the callee MUST push its return value.
|
|
4. **Cleanup:** The `Ret` instruction is responsible for:
|
|
- Popping the return value.
|
|
- Removing all locals (the arguments) from the operand stack.
|
|
- Re-pushing the return value.
|
|
- Restoring the previous frame and PC.
|
|
|
|
### 4.2 Syscall Convention
|
|
|
|
1. **Arguments:** The caller pushes arguments in order.
|
|
2. **Execution:** The native implementation pops arguments as needed. Since it's a stack, it will pop them in reverse order (argN-1 first, then argN-2, ..., arg0).
|
|
3. **Return Value:** The native implementation MUST push exactly one value onto the stack before returning to the VM.
|
|
4. **Cleanup:** The native implementation is responsible for popping all arguments it expects.
|
|
|
|
Example:
|
|
|
|
```
|
|
PUSH_CONST 3
|
|
PUSH_CONST 4
|
|
ADD
|
|
```
|
|
|
|
State:
|
|
|
|
```
|
|
[3]
|
|
[3, 4]
|
|
[7]
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Instruction Categories
|
|
|
|
1. Flow control
|
|
2. Stack
|
|
3. Arithmetic and logic
|
|
4. Variables
|
|
5. Functions
|
|
6. Heap and structures
|
|
7. Peripherals (syscalls)
|
|
8. System
|
|
|
|
---
|
|
|
|
## 6. Instructions — VM Set 1
|
|
|
|
### 6.1 Execution Control
|
|
|
|
| Instruction | Cycles | Description |
|
|
| ------------------- | ------ | ------------------------- |
|
|
| `NOP` | 1 | Does nothing |
|
|
| `HALT` | 1 | Terminates execution |
|
|
| `JMP addr` | 2 | Unconditional jump |
|
|
| `JMP_IF_FALSE addr` | 3 | Jumps if top is false |
|
|
| `JMP_IF_TRUE addr` | 3 | Jumps if top is true |
|
|
|
|
---
|
|
|
|
### 6.2 Stack
|
|
|
|
| Instruction | Cycles | Description |
|
|
| -------------- | ------ | ------------------- |
|
|
| `PUSH_CONST k` | 2 | Pushes constant |
|
|
| `POP` | 1 | Removes top |
|
|
| `DUP` | 1 | Duplicates top |
|
|
| `SWAP` | 1 | Swaps two tops |
|
|
| `PUSH_I32 v` | 2 | Pushes 32-bit int |
|
|
| `PUSH_I64 v` | 2 | Pushes 64-bit int |
|
|
| `PUSH_F64 v` | 2 | Pushes 64-bit float |
|
|
| `PUSH_BOOL v` | 2 | Pushes boolean |
|
|
|
|
---
|
|
|
|
### 6.3 Arithmetic
|
|
|
|
| Instruction | Cycles |
|
|
| ----------- | ------ |
|
|
| `ADD` | 2 |
|
|
| `SUB` | 2 |
|
|
| `MUL` | 4 |
|
|
| `DIV` | 6 |
|
|
|
|
---
|
|
|
|
### 6.4 Comparison and Logic
|
|
|
|
| Instruction | Cycles |
|
|
| ----------- | ------ |
|
|
| `EQ` | 2 |
|
|
| `NEQ` | 2 |
|
|
| `LT` | 2 |
|
|
| `GT` | 2 |
|
|
| `LTE` | 2 |
|
|
| `GTE` | 2 |
|
|
| `AND` | 2 |
|
|
| `OR` | 2 |
|
|
| `NOT` | 1 |
|
|
| `BIT_AND` | 2 |
|
|
| `BIT_OR` | 2 |
|
|
| `BIT_XOR` | 2 |
|
|
| `SHL` | 2 |
|
|
| `SHR` | 2 |
|
|
| `NEG` | 1 |
|
|
|
|
---
|
|
|
|
### 6.5 Variables
|
|
|
|
| Instruction | Cycles | Description |
|
|
| -------------- | ------ | ---------------- |
|
|
| `GET_GLOBAL i` | 3 | Reads global |
|
|
| `SET_GLOBAL i` | 3 | Writes global |
|
|
| `GET_LOCAL i` | 2 | Reads local |
|
|
| `SET_LOCAL i` | 2 | Writes local |
|
|
|
|
---
|
|
|
|
### 6.6 Functions
|
|
|
|
| Instruction | Cycles | Description |
|
|
|----------------------| ------ |-----------------------------------------------|
|
|
| `CALL <u32 func_id>` | 5 | Saves PC and creates a new call frame |
|
|
| `RET` | 4 | Returns from function, restoring PC |
|
|
| `PUSH_SCOPE` | 3 | Creates a scope within the current function |
|
|
| `POP_SCOPE` | 3 | Removes current scope and its local variables |
|
|
|
|
**ABI Rules for Functions:**
|
|
* **`func_id`:** A 32-bit index into the **final FunctionTable**, assigned by the compiler linker at build time.
|
|
* **Mandatory Return Value:** Every function MUST leave exactly one value on the stack before `RET`. If the function logic doesn't return a value, it must push `null`.
|
|
* **Stack Cleanup:** `RET` automatically clears all local variables (based on `stack_base`) and re-pushes the return value.
|
|
|
|
---
|
|
|
|
### 6.7 Heap
|
|
|
|
| Instruction | Cycles | Description |
|
|
| --------------- | ------ | --------------- |
|
|
| `ALLOC size` | 10 | Allocates on heap |
|
|
| `LOAD_REF off` | 3 | Reads field |
|
|
| `STORE_REF off` | 3 | Writes field |
|
|
|
|
Heap is:
|
|
|
|
* finite
|
|
* monitored
|
|
* accounted for in the CAP
|
|
|
|
---
|
|
|
|
### 6.8 Peripherals (Syscalls)
|
|
|
|
| Instruction | Cycles | Description |
|
|
|--------------| -------- | --------------------- |
|
|
| `SYSCALL id` | variable | Call to hardware |
|
|
|
|
**ABI Rules for Syscalls:**
|
|
* **Argument Order:** Arguments must be pushed in the order they appear in the call (LIFO stack behavior).
|
|
* Example: `gfx.draw_rect(x, y, w, h, color)` means:
|
|
1. `PUSH x`
|
|
2. `PUSH y`
|
|
3. `PUSH w`
|
|
4. `PUSH h`
|
|
5. `PUSH color`
|
|
6. `SYSCALL 0x1002`
|
|
* **Consumption:** The native function MUST pop all its arguments from the stack.
|
|
* **Return Value:** If the syscall returns a value, it will be pushed onto the stack by the native implementation. If not, the stack state for the caller remains as it was before pushing arguments.
|
|
|
|
#### Implemented Syscalls (v0.1)
|
|
|
|
| ID | Name | Arguments (Stack) | Return |
|
|
| ---------- | ----------------- | ---------------------------- | ------- |
|
|
| `0x0001` | `system.has_cart` | - | `bool` |
|
|
| `0x0002` | `system.run_cart` | - | - |
|
|
| `0x1001` | `gfx.clear` | `color_idx` | - |
|
|
| `0x1002` | `gfx.draw_rect` | `x, y, w, h, color_idx` | - |
|
|
| `0x1003` | `gfx.draw_line` | `x1, y1, x2, y2, color_idx` | - |
|
|
| `0x1004` | `gfx.draw_circle` | `xc, yc, r, color_idx` | - |
|
|
| `0x1005` | `gfx.draw_disc` | `xc, yc, r, b_col, f_col` | - |
|
|
| `0x1006` | `gfx.draw_square` | `x, y, w, h, b_col, f_col` | - |
|
|
| `0x2001` | `input.get_pad` | `button_id` | `bool` |
|
|
| `0x3001` | `audio.play` | `s_id, v_id, vol, pan, pitch`| - |
|
|
|
|
**Button IDs:**
|
|
- `0`: Up, `1`: Down, `2`: Left, `3`: Right
|
|
- `4`: A, `5`: B, `6`: X, `7`: Y
|
|
- `8`: L, `9`: R
|
|
- `10`: Start, `11`: Select
|
|
|
|
---
|
|
|
|
## 7. Execution Errors
|
|
|
|
Errors are:
|
|
|
|
* explicit
|
|
* fatal
|
|
* never silent
|
|
|
|
Types:
|
|
|
|
* stack underflow
|
|
* invalid type
|
|
* invalid heap
|
|
* invalid frame
|
|
|
|
Generate:
|
|
|
|
* clear message
|
|
* state dump
|
|
* stack trace
|
|
|
|
---
|
|
|
|
## 8. Determinism
|
|
|
|
Guarantees:
|
|
|
|
* same input → same result
|
|
* same sequence → same cycles
|
|
* no speculative execution
|
|
* no invisible optimizations
|
|
|
|
> If you see the instruction, you pay for it.
|
|
|
|
---
|
|
|
|
## 9. Relationship with Languages
|
|
|
|
Java, TypeScript, Lua etc:
|
|
|
|
* are source languages
|
|
* compiled to this bytecode
|
|
* never executed directly
|
|
|
|
All run on the **same VM**.
|
|
|
|
---
|
|
|
|
## 10. Example
|
|
|
|
Source:
|
|
|
|
```java
|
|
x = 3 + 4;
|
|
```
|
|
|
|
Bytecode:
|
|
|
|
```
|
|
PUSH_CONST 3
|
|
PUSH_CONST 4
|
|
ADD
|
|
SET_GLOBAL 0
|
|
```
|
|
|
|
Cost:
|
|
|
|
```
|
|
2 + 2 + 2 + 3 = 9 cycles
|
|
```
|
|
|
|
---
|
|
|
|
## 11. Execution per Tick
|
|
|
|
The VM does not run infinitely.
|
|
|
|
It executes:
|
|
|
|
* until consuming the **logical frame** budget
|
|
* or until `HALT`
|
|
|
|
The budget is defined by the PROMETEU logical hardware (e.g., `CYCLES_PER_FRAME`).
|
|
|
|
Example:
|
|
|
|
```
|
|
vm.step_budget(10_000)
|
|
```
|
|
|
|
This feeds:
|
|
|
|
* CAP
|
|
* profiling
|
|
* certification
|
|
|
|
---
|
|
|
|
## 12. Logical Frame and `FRAME_SYNC`
|
|
|
|
PROMETEU defines **logical frame** as the minimum unit of consistent game update.
|
|
|
|
* **Input is latched per logical frame** (does not change until the logical frame is completed).
|
|
* The **cycle budget** is applied to the logical frame.
|
|
* A new logical frame only starts when the current frame ends.
|
|
|
|
### 12.1 System Instruction: `FRAME_SYNC`
|
|
|
|
The `FRAME_SYNC` instruction marks the **end of the logical frame**.
|
|
|
|
| Instruction | Cycles | Description |
|
|
| ------------ | ------ | ---------------------------------- |
|
|
| `FRAME_SYNC` | 1 | Finalizes the current logical frame |
|
|
|
|
Properties:
|
|
|
|
* `FRAME_SYNC` is a **system instruction**.
|
|
* It should not be exposed as a "manual" API to the user.
|
|
* Tooling/compiler can **inject** `FRAME_SYNC` automatically at the end of the main loop.
|
|
|
|
### 12.2 Semantics (what happens when it executes)
|
|
|
|
When executing `FRAME_SYNC`, the core:
|
|
|
|
1. **Finalizes** the current logical frame.
|
|
2. **Presents** the frame (`gfx.present()` or `gfx.compose_and_present()` depending on the GFX model).
|
|
3. **Releases** the input latch.
|
|
4. **Resets** the budget for the next logical frame.
|
|
|
|
### 12.3 Overbudget (when the frame doesn't finish on time)
|
|
|
|
If the logical frame budget runs out **before** the VM reaches `FRAME_SYNC`:
|
|
|
|
* the VM **pauses** (PC and stacks remain at the exact point)
|
|
* there is **no present**
|
|
* the input latch is **maintained**
|
|
* on the next host tick, the VM **continues** from where it left off, still in the same logical frame
|
|
|
|
Practical effect:
|
|
|
|
* if the code needs 2 budgets to reach `FRAME_SYNC`, the game updates at ~30 FPS (logical frame takes 2 ticks)
|
|
* this is deterministic and reportable in the CAP
|
|
|
|
---
|
|
|
|
## 15. Extensibility
|
|
|
|
The Instruction Set is versioned.
|
|
|
|
Future:
|
|
|
|
* DMA
|
|
* streaming
|
|
* vectors
|
|
* fictitious coprocessors
|
|
|
|
No existing instruction changes its meaning.
|
|
|
|
---
|
|
|
|
## 16. Summary
|
|
|
|
* stack-based VM
|
|
* explicit cost
|
|
* deterministic execution
|
|
* integrated with CAP
|
|
* foundation of every PROMETEU cartridge
|
|
|
|
< [Back](chapter-1.md) | [Summary](table-of-contents.md) | [Next](chapter-3.md) > |