2026-03-24 13:40:22 +00:00

456 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 addr` | 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:**
* **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) >