joeylib2/AGI-SESSION-HANDOFF.md

218 lines
9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# AGI port — session handoff
This document captures the state of the AGI interpreter port as of
2026-05-15. Hand it to a fresh Claude Code session to pick up where
the previous one left off.
## Project context
JoeyLib is a unified C game-dev library targeting Apple IIgs (perf
floor / reference), Amiga, Atari ST, MS-DOS. The AGI port is a
from-scratch reimplementation of Sierra's Adventure Game Interpreter
v2 on top of JoeyLib's surface / draw / input APIs. Written from
public AGI format specs only -- NOT derived from ScummVM / NAGI /
Sarien (those are GPL'd; JoeyLib is not).
Test data: KQ3 v2, locally installed at
`examples/agi/gamedata/kq3/`. The user owns the game and is not
distributing it; gamedata is gitignored.
## Current phase
**Phase 1 sub-D** (callback-driven rendering): verified working.
Logic.0 + room 45 init paint KQ3's title screen on every supported
port. Ego sprite (VIEW 0, Graham) draws on top with priority masking,
WASD/arrow keys move him, ESC quits.
What's next: **Phase 1 sub-E** (input + animation opcodes). The
title screen does not auto-advance because the VM still stubs ~150
opcodes including `wait`, `set.menu`, `accept.input`, animation
timers, etc. Real KQ3 progression past the title requires implementing
enough of those to let logic.45 complete its intro and call new.room.
Phase 2 sub-A onward: parser, NPC actors, sound (NTP / PSG).
## What works (verified)
- Resource loader (LOGDIR / PICDIR / VIEWDIR / SNDDIR / VOL.x)
- PIC decoder (visual + priority planes, flood fill, vector draw)
- VIEW decoder (cel parse + RLE blit + priority masking + mirror)
- LOGIC VM: 256 vars, 256 flags, 8-deep call stack
- Real handlers: arithmetic (0x00-0x11), OP_IF (0xFF), OP_GOTO
(0xFE), OP_NEW_ROOM (0x12/0x13), OP_LOAD_LOGIC (0x14/0x15),
OP_CALL (0x16/0x17), OP_LOAD_PIC (0x18), OP_DRAW_PIC (0x19),
OP_SHOW_PIC (0x1A), OP_DISCARD_PIC (0x1B), OP_OVERLAY_PIC (0x1C)
- All ~180 other action opcodes are stubs (skip their arg bytes
via `kActionArgBytes[256]` table)
- Host callbacks: fetchLogic / loadPic / drawPic / showPic /
discardPic / overlayPic
- Per-platform save-under for the ego sprite (no full-stage memcpy
per frame; IIgs uses MVN-based codegen)
- Cross-platform binary works on all 4 ports (`AGI.EXE`,
`AGI.PRG`, `Agi`, IIgs `AGI`)
## What does NOT work
- **Title screen never advances.** Stubs the input / timer opcodes
the title uses. Workaround: starting-room override (argv[2]).
- **No NPC actors.** animate.obj / set.cel / set.loop are stubs.
- **No parser.** said() and the word-tokenize opcodes are stubs.
- **No sound.** Phase 3 work.
- **IIgs run path for AGI** is not wired. `scripts/run-agi.sh iigs`
prints an explicit "not yet wired" message describing the missing
disk-packager work (need bigger 2mg volume + case-folded ProDOS
names + game-data argument to `package-disk.sh`).
- **ST / Amiga can't pass argv.** Hatari `--auto` and Amiga
startup-sequence don't forward args, so the starting-room override
only works on DOS. ST / Amiga AGI starts at the title screen.
Fix needs either a config-file stage (`ROOM.TXT` read at startup)
or enough title opcodes for the title to advance naturally.
## How to test
```bash
source toolchains/env.sh
# Host-side parsers + VM (fast iteration, no emulator)
./scripts/test-agi.sh
# Cross-platform binary in DOSBox
./scripts/run-agi.sh dos # title screen
./scripts/run-agi.sh dos kq3 1 # jump to room 1
./scripts/run-agi.sh dos kq3 3 # jump to room 3
# Other platforms (no argv support yet, always title)
./scripts/run-agi.sh atarist
./scripts/run-agi.sh amiga
./scripts/run-agi.sh iigs # prints "not wired" message
```
## Files touched (this session)
New:
- `src/core/random.c` — xorshift32 PRNG (jlRandom / jlRandomRange / jlRandomSeed)
- `examples/agi/*.c` — agi.c, agiRes.c, agiPic.c, agiView.c, agiVm.c
- `examples/agi/agi.h` — all AGI types + public API
- `tests/agi/*.c` — testAgiRes / testAgiPic / testAgiView / testAgiVm / testAgiPipeline
- `scripts/test-agi.sh` — host-build runner for all five tests
- `scripts/run-agi.sh` — cross-platform emulator launcher (dos | amiga | atarist | iigs)
Modified:
- `include/joey/core.h` — random prototypes
- `include/joey/platform.h` — auto-detect from compiler defines
- `.gitignore` — examples/agi/gamedata/
- All four make/*.mk — added agi to EXAMPLES list
## Architectural decisions worth remembering
### Render loop: save-under, not full stage copy
The naive per-frame `jlSurfaceCopy(stage, gBackdrop)` is 32 KB of
memcpy + present overhead -- not acceptable on a 2.8 MHz IIgs. The
current code:
1. `cbShowPic` paints the picture onto the stage ONCE and snapshots
it as `gBackdrop`. Sets `gPicReady = true` and invalidates any
prior ego save-under.
2. Per frame: `jlSpriteRestoreUnder` the last-frame ego rect (puts
clean backdrop bytes back), `jlSpriteSaveUnder` the new ego rect,
then `agiViewDraw` paints the cel with priority masking.
3. Placeholder sprite for save-under is 4x5 tiles = 32x40 stage
pixels, sized to cover any AGI v2 ego cel. Compiled via
`jlSpriteCompile` so save/restore use the platform's compiled
fast path (MVN block-copy on IIgs, etc.) instead of the C
interpreter fallback.
Per-frame memory traffic: ~1.3 KB save+restore vs ~32 KB full copy.
### VM throttling
AGI's native cycle is ~6 Hz. Running logic.0 every frame (60 Hz) is
6800-byte switch dispatcher × 60 = ~400k opcodes/sec for nothing.
`agi.c` now runs runVmCycle once every 10 frames (~6 Hz on 60 Hz
refresh, ~5 Hz on 50 Hz PAL). Input polling, ego movement, render
still run at full refresh -- only the bytecode interp is throttled.
Startup pumps the VM until `gPicReady` (or 32 cycles) so the user
never sees a flash of COLOR_MISSING before the first room paints.
### VIEW mirror handling
Sierra's encoder writes `mirror=1, src=N` on BOTH loops in a mirror
pair, where N is the lower-numbered loop. `agiView.c:resolveMirror`
must only treat a cel as mirrored when `src != current_loop`. KQ3
view 0 hit this directly: both loop 0 (right) and loop 1 (left) had
`mirror=1, src=0`, and our original code flipped both -- so pressing
left and right both faced left. Fixed.
### Starting-room override
`agi.c` argv[2] = starting room number 1..255. When set,
`runVmCycle`'s NEW_ROOM handler substitutes the override for
`vm.newRoomId` on the FIRST NEW_ROOM halt only (one-shot). Lets the
user skip past KQ3's title room (45) into the actual game. Once the
override fires, subsequent transitions go through unchanged.
`scripts/run-agi.sh` accepts `<platform> [game] [room]` and forwards
the room to DOS via dosbox `-c "AGI.EXE . <room>"`. ST and Amiga
can't pass argv via autostart so their AGI starts at the title.
### Default game dir is "." not "agidemo"
`resolveGameDir` defaults to "." (CWD). On platforms whose autostart
can't pass argv (Hatari, AmigaDOS), `run-agi.sh` stages the AGI
binary plus the v2 game data files (LOGDIR / PICDIR / etc.) flat in
a temp directory and points the emulator at it; AGI runs from CWD =
that temp dir and finds the data files relative to "./".
## Likely next-step work
The user's natural next ask is "implement enough title opcodes to
let KQ3's title screen auto-advance." That probably means:
- `wait` (cycle.time gate)
- `input.line` / `accept.input`
- Animation timers: `cycle.time`, `current.loop`, etc.
- `set.menu` / `disable.item` (KQ3 title shows a menu)
Alternative path: skip the title work and implement enough core
opcodes to make a gameplay room functional. animate.obj,
set.view.v, set.cel, set.loop, position, draw, erase. That gets us
walking around KQ3 room 1 (Manannan's house) with Graham instead
of a static title.
The user should pick which to prioritize.
## Memory entries you should know about
The repo's auto-memory at
`~/.claude/projects/-home-scott-claude-joeylib/memory/` includes
JoeyLib-wide context (CMake quirks, ORCA-C gotchas, perf
constraints) plus `project_joeylib_agi_plan.md` which describes the
overall AGI port plan. All persist into the next session
automatically; no need to reload.
Specifically relevant to the AGI port:
- `project_joeylib_agi_plan.md` — overall plan, 3-4 month estimate
- `feedback_orca_c_atoi_segment.md` — don't use atoi in AGI code
(it pushes the IIgs ORCA-C stdio cluster past 64 KB; we use a
manual parseArgU8 instead)
- `project_perf_directive.md` — IIgs is the perf floor; every
other target must match or beat it
- `feedback_iigs_speed.md` — IIgs must be absolute fastest
technically possible; the save-under decision was made under
this constraint.
## Git / user preferences
- The user manages git themselves -- do NOT commit / push / branch
on JoeyLib without explicit ask (see memory
`feedback_git.md`).
- Always `source toolchains/env.sh` before any `make` -- the user
has added that to .claude/settings.local.json allow list.
- DJGPP / ORCA / vasm / m68k-atari-mint paths come from env.sh.
- The user does NOT need hand-holding -- they're an expert C dev.
Skip excessive explanation; describe what was done and move on.