mirror of
https://github.com/imjasonh/nescript
synced 2026-07-08 08:55:38 +00:00
compiler: i16 / SRAM saves / inline-asm dot labels / docs
Another batch from the cc65/nesdoug catalogue. All gated on
parser-level opt-in or default-false attributes so existing
programs produce byte-identical ROMs (no committed .nes file
changed).
**§A — `i16` signed 16-bit type:**
- New `KwI16` lexer token, `NesType::I16` AST variant, parser
case in `parse_type`. Type-size and integer-type tables
treat `i16` like `u16` (2 bytes, integer).
- IR lowering accepts `i16` everywhere it accepts `u16` for
wide-load / wide-store / widen-narrow paths.
- New constant fold for `UnaryOp::Negate(IntLiteral(v))` that
emits the wide two's-complement form. Without it, `var vy:
i16 = -10` would zero-extend to `$00F6` (= 246) instead of
sign-extending to `$FFF6` (= -10). Negative literals now
store the right bytes.
- Comparisons reuse the existing unsigned 16-bit compare ops
(matching the existing `i8` behaviour). Documented in the
`NesType::I16` doc comment and in `future-work.md` §A.
- Example `examples/i16_demo.ne` with committed golden.
- Tests cover the literal-fold sign-extension and end-to-end
compile of the example.
**§S — SRAM / battery-backed saves:**
- New `save { var ... }` top-level block. Lexer + parser opt
into a dedicated `KwSave` token. Analyzer allocates save
vars from a separate `next_sram_addr` bump pointer starting
at `$6000`, capped at `$8000` (8 KB cartridge SRAM window).
- Linker reads `analysis.has_battery_saves` and flips iNES
byte-6 bit-1 via the new `RomBuilder::set_battery` /
`Linker::with_battery` chain.
- New `W0111` warning for save-var initializers — SRAM is
preserved across power cycles, so an init expression would
either silently never run or clobber persisted data on
every boot. The warning teaches the user about the
magic-byte sentinel pattern.
- Struct fields in save blocks are explicitly rejected for now
(the field-flattening path uses the main-RAM allocator).
- Example `examples/sram_demo.ne` with committed golden, plus
4 integration tests.
**§D (partial) — inline-asm `.label:` syntax:**
- Codegen-side mangler rewrites `.IDENT` → `__ilab_<N>_IDENT`
per inline-asm block, where `<N>` is the call site's
monotonic suffix. Two `asm { .loop: ... }` blocks in the
same function now coexist without colliding in the linker's
label table.
- Bounds checks on `.` placement: `$2002` and `name.field`
are unaffected; only `.IDENT` in label / branch context
triggers the rewrite. Two integration tests pin the
uniqueness and dollar-vs-dot disambiguation.
**§X follow-up — Mesen trace-log docs:**
- New "Debugger-assisted workflows" section in
`docs/nes-reference.md` walking through the Mesen / FCEUX
log workflows alongside the new `debug_port:` attribute.
**Misc:**
- `future-work.md` updated to mark the shipped items out of
the catalogue and reshuffle the priority ranking. Remaining
niche follow-ups (signedness on Cmp16, struct save fields,
inline-asm format specifiers) documented inline so future
passes know the design.
All 757 tests pass. Clippy clean. 46/46 emulator goldens match.
This commit is contained in:
parent
e0b268eea9
commit
7b4570eee5
28 changed files with 841 additions and 58 deletions
|
|
@ -201,3 +201,48 @@ The NEScript runtime handles this automatically. The programmer reads button sta
|
|||
| Vblank time | ~2,273 cycles | PPU updates must be fast |
|
||||
| Frame budget | ~29,780 cycles | Frame overrun detection in debug mode |
|
||||
| No multiply/divide HW | -- | Software routines, power-of-2 optimization|
|
||||
|
||||
---
|
||||
|
||||
## Debugger-assisted workflows
|
||||
|
||||
NEScript compiles three debugger-friendly sidecar files that Mesen,
|
||||
Mesen2, and FCEUX can load alongside the `.nes`. All three are
|
||||
off by default (release ROMs should be as small as possible) and
|
||||
enabled per-run via CLI flags:
|
||||
|
||||
| Flag | Format | Consumers |
|
||||
|---------------------------------|----------------------------------|-----------------|
|
||||
| `--symbols <path.mlb>` | Mesen labels (`P:` / `R:` lines) | Mesen, Mesen2 |
|
||||
| `--dbg <path.dbg>` | ca65 debug-info format | Mesen, Mesen2, FCEUX |
|
||||
| `--fceux-labels <prefix>` | `<prefix>.<bank>.nl` + `.ram.nl` | FCEUX |
|
||||
|
||||
### Mesen trace-log
|
||||
|
||||
Mesen supports address-based execution tracing via its `log` /
|
||||
`save-log` scripting APIs. Combined with NEScript's `debug.log`
|
||||
builtin, the standard workflow is:
|
||||
|
||||
1. Build with `--debug` and `--symbols out.mlb`. `debug.log(value)`
|
||||
writes `value` to the emulator debug port every time it executes.
|
||||
2. In the `game { }` block, set `debug_port: mesen` so writes land
|
||||
at `$4018` (Mesen's documented tracing port):
|
||||
|
||||
```
|
||||
game "MyGame" {
|
||||
mapper: NROM
|
||||
debug_port: mesen
|
||||
}
|
||||
```
|
||||
|
||||
3. In Mesen, enable the trace log (Debug → Trace Logger), load
|
||||
the `.mlb`, and filter by memory operation on `$4018`. Each
|
||||
`debug.log(x)` call appears in the trace with the source
|
||||
function + line resolved from the label table.
|
||||
|
||||
For FCEUX on Linux, keep the default `debug_port: fceux` (writes
|
||||
to `$4800`) and pass `--fceux-labels out` — FCEUX reads
|
||||
`out.<bank>.nl` automatically when it opens the ROM from the same
|
||||
directory. FCEUX's conditional-breakpoint syntax can match writes
|
||||
to `$4800` directly (`A(4800)!=0`) so you can break when any log
|
||||
fires.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue