1
0
Fork 0
mirror of https://github.com/imjasonh/nescript synced 2026-07-08 17:06:04 +00:00

lang: NES 2.0 headers and u16 struct fields

Implements two items from docs/future-work.md's language-feature gaps:

NES 2.0 header support: `RomBuilder` gains a `header_format` field
and a matching `enable_nes2()` method. When enabled, byte 7 bits 2-3
are set to `10` and bytes 8-15 are populated per the NES 2.0 spec
(submapper, PRG/CHR MSBs, PRG/CHR RAM, timing). The header stays
16 bytes. Programs opt in via `game Foo { header: nes2 }`; the
default remains iNES 1.0 so every committed example ROM is byte
identical. `validate_ines` now detects and reports which format it
parsed.

u16 struct fields: the analyzer's `register_struct` accepts `u16`
fields with a two-byte size and the struct-variable allocator tracks
per-field sizes so the synthesized `pos.x`/`pos.y` globals get the
right address span. IR lowering's `LValue::Field` and
`Expr::FieldAccess` follow the same wide path as u16 globals, and
struct-literal initialization writes both bytes for u16 fields.
Array and nested-struct fields stay rejected with a clearer
message. Existing u8/i8/bool struct programs are unaffected.

https://claude.ai/code/session_01MaNVcDmK9gsspRkdxowQAM
This commit is contained in:
Claude 2026-04-14 02:05:51 +00:00
parent 65a63f9a68
commit 33351f8b32
No known key found for this signature in database
13 changed files with 588 additions and 26 deletions

View file

@ -4,7 +4,7 @@ mod tests;
use crate::asm;
use crate::asm::{AddressingMode as AM, Instruction, Opcode::*};
use crate::assets::{BackgroundData, MusicData, PaletteData, SfxData};
use crate::parser::ast::{Mapper, Mirroring};
use crate::parser::ast::{HeaderFormat, Mapper, Mirroring};
use crate::rom::RomBuilder;
use crate::runtime;
@ -12,6 +12,7 @@ use crate::runtime;
pub struct Linker {
mirroring: Mirroring,
mapper: Mapper,
header_format: HeaderFormat,
}
/// CHR data for a sprite, placed at a specific tile index in CHR ROM.
@ -115,11 +116,25 @@ impl Linker {
Self {
mirroring,
mapper: Mapper::NROM,
header_format: HeaderFormat::Ines1,
}
}
pub fn with_mapper(mirroring: Mirroring, mapper: Mapper) -> Self {
Self { mirroring, mapper }
Self {
mirroring,
mapper,
header_format: HeaderFormat::Ines1,
}
}
/// Opt into the NES 2.0 header format for the emitted ROM.
/// Chainable builder method — returns `self` so callers can
/// write `Linker::with_mapper(m, p).with_header(HeaderFormat::Nes2)`.
#[must_use]
pub fn with_header(mut self, header: HeaderFormat) -> Self {
self.header_format = header;
self
}
/// Link all code sections into a .nes ROM.
@ -454,6 +469,9 @@ impl Linker {
// Build ROM
let mut builder = RomBuilder::new(self.mirroring);
builder.set_mapper(crate::rom::mapper_number(self.mapper));
if self.header_format == HeaderFormat::Nes2 {
builder.enable_nes2();
}
// Multi-bank layout: each switchable bank is an independent
// 16 KB slot whose contents the linker takes verbatim from