146 lines
3.1 KiB
Markdown
146 lines
3.1 KiB
Markdown
## PR-03 — Lowering: Host Contracts for Gfx/Input using deterministic syscalls
|
|
|
|
### Goal
|
|
|
|
Map PBS host contracts to stable syscalls with a deterministic ABI.
|
|
|
|
### Required host contracts in PBS surface
|
|
|
|
```pbs
|
|
pub declare contract Gfx host
|
|
{
|
|
fn clear(color: Color): void;
|
|
}
|
|
|
|
pub declare contract Input host
|
|
{
|
|
fn pad(): Pad;
|
|
fn touch(): Touch;
|
|
}
|
|
```
|
|
|
|
### Required lowering rules
|
|
|
|
1. `Gfx.clear(color)`
|
|
|
|
* Emit `SYSCALL_GFX_CLEAR`
|
|
* ABI: args = [Color.raw] as `bounded`
|
|
* returns: void
|
|
|
|
2. `Input.pad()`
|
|
|
|
* Emit `SYSCALL_INPUT_PAD`
|
|
* args: none
|
|
* returns: flattened `Pad` in field order as declared
|
|
|
|
3. `Input.touch()`
|
|
|
|
* Emit `SYSCALL_INPUT_TOUCH`
|
|
* args: none
|
|
* returns: flattened `Touch` in field order as declared
|
|
|
|
### Flattening order (binding)
|
|
|
|
**ButtonState** returns 4 slots in order:
|
|
|
|
1. pressed (bool)
|
|
2. released (bool)
|
|
3. down (bool)
|
|
4. hold_frames (bounded)
|
|
|
|
**Pad** returns 12 ButtonState blocks in this exact order:
|
|
`up, down, left, right, a, b, x, y, l, r, start, select`
|
|
|
|
**Touch** returns:
|
|
|
|
1. f (ButtonState block)
|
|
2. x (int)
|
|
3. y (int)
|
|
|
|
### Tests (mandatory)
|
|
|
|
* Lowering golden test: `Gfx.clear(Color.WHITE)` emits `SYSCALL_GFX_CLEAR` with 1 arg.
|
|
* Lowering golden test: `Input.pad()` emits `SYSCALL_INPUT_PAD` and assigns to local.
|
|
* Lowering golden test: `Input.touch()` emits `SYSCALL_INPUT_TOUCH`.
|
|
|
|
### Non-goals
|
|
|
|
* No runtime changes
|
|
* No VM heap
|
|
|
|
---
|
|
|
|
## PR-04 — Runtime: Implement syscalls for Color/Gfx and Input pad/touch + integration cartridge
|
|
|
|
### Goal
|
|
|
|
Make the new syscalls actually work and prove them with an integration test cartridge.
|
|
|
|
### Required syscall implementations
|
|
|
|
#### 1) `SYSCALL_GFX_CLEAR`
|
|
|
|
* Read 1 arg: `bounded` raw color
|
|
* Convert to `u16` internally (runtime-only)
|
|
|
|
* If raw > 0xFFFF, trap `TRAP_OOB` or `TRAP_TYPE` (choose one and document)
|
|
* Fill framebuffer with that RGB565 value
|
|
|
|
#### 2) `SYSCALL_INPUT_PAD`
|
|
|
|
* No args
|
|
* Snapshot the current runtime `Pad` and push a flattened `Pad` return:
|
|
|
|
* For each button: pressed, released, down, hold_frames
|
|
* hold_frames pushed as `bounded`
|
|
|
|
#### 3) `SYSCALL_INPUT_TOUCH`
|
|
|
|
* No args
|
|
* Snapshot `Touch` and push flattened `Touch` return:
|
|
|
|
* f ButtonState
|
|
* x int
|
|
* y int
|
|
|
|
### Integration cartridge (mandatory)
|
|
|
|
Add `test-cartridges/hw_hello` (or similar) with:
|
|
|
|
```pbs
|
|
fn frame(): void
|
|
{
|
|
// 1) clear screen white
|
|
Gfx.clear(Color.WHITE);
|
|
|
|
// 2) read pad and branch
|
|
let p: Pad = Input.pad();
|
|
if p.any() {
|
|
Gfx.clear(Color.MAGENTA);
|
|
}
|
|
|
|
// 3) read touch and branch on f.down
|
|
let t: Touch = Input.touch();
|
|
if t.f.down {
|
|
// choose a third color to prove the struct returned correctly
|
|
Gfx.clear(Color.BLUE);
|
|
}
|
|
}
|
|
```
|
|
|
|
### Acceptance criteria
|
|
|
|
* Cartridge runs without VM faults.
|
|
* With no input: screen is WHITE.
|
|
* With any pad button held: screen becomes MAGENTA.
|
|
* With touch f.down: screen becomes BLUE.
|
|
|
|
### Tests (mandatory)
|
|
|
|
* Runtime unit test: `SYSCALL_GFX_CLEAR` rejects raw > 0xFFFF deterministically.
|
|
* Runtime unit test: `SYSCALL_INPUT_PAD` returns correct number of stack slots (48).
|
|
* Runtime unit test: `SYSCALL_INPUT_TOUCH` returns correct number of stack slots (4 + 2 = 6).
|
|
|
|
---
|
|
|