RetroSharp.Tool 0.0.769

There is a newer version of this package available.
See the version list below for details.
dotnet tool install --global RetroSharp.Tool --version 0.0.769
                    
This package contains a .NET tool you can call from the shell/command line.
dotnet new tool-manifest
                    
if you are setting up this repo
dotnet tool install --local RetroSharp.Tool --version 0.0.769
                    
This package contains a .NET tool you can call from the shell/command line.
#tool dotnet:?package=RetroSharp.Tool&version=0.0.769
                    
nuke :add-package RetroSharp.Tool --version 0.0.769
                    

RetroSharp

RetroSharp is a C#-inspired language, compiler, and zero-cost 2D framework for 8-bit game systems.

I'm making this in my free time to learn about compilers and old-school game hardware. It was supposed to make me happier; it has also managed to reach the shaved-head milestone.

Documentation

  • Getting started — install the CLI, create a game, and build Game Boy and NES ROMs.
  • Language reference — types, declarations, operators, control flow, functions, arrays, and zero-cost abstractions.
  • Portable2D SDK reference — frames, input, worlds, cameras, sprites, animation, audio, actors, projectiles, and effects.
  • Documentation home — browse guides, target references, tooling, architecture, and contributor documentation.
  • Samples — runnable programs organized by feature and implementation layer.

Start without installing .NET with the standalone CLI guide. For a conceptual introduction, read the project overview.

Project shape

RetroSharp has three layers that should stay separate:

  • Language: target-neutral syntax, types, storage, constants, helpers, control flow, and zero-cost source ergonomics.
  • Portable 2D SDK: capability-checked game APIs for frames, input, worlds, cameras, sprites, palettes, music, animation, and actors.
  • Target intrinsics and lowering: Game Boy, NES, and future hardware-specific behavior.

The language should not know what a sprite, camera, tilemap, joypad, or palette register is. Those concepts belong in the SDK or in explicit target intrinsics.

Compilation paths

RetroSharp parses and prepares source through a shared target-neutral frontend, then lowers it directly through one of three cartridge targets. The CLI requires an explicit --target for standalone source files and manifests that do not declare target or targets:

  • --target nes: emits an iNES mapper 0 ROM for static background/tile drawing plus tick-based input, logical sprites, horizontal camera streaming, four-screen 2-axis camera movement, Tiled World.Load(...), runtime animation helpers, camera-relative runner collision, and VGM/VGZ-sourced 2A03 BGM playback. HUD and generic world-space collision are still not implemented.

  • --target gb: emits a 32 KiB ROM-only Game Boy cartridge when the program fits, or an MBC1 banked ROM when large music assets need more space. It supports static background/map setup and a first runtime sprite loop subset with local byte-backed variables, assignment, if/else if/else, while, for, half-open range for, Video.WaitVBlank(), tick-based input polling, Scroll.Set(...), position-based camera X/Y scrolling with runtime row/column streaming, Sprite.Set(...), simple source-map tile queries for collision, joypad button queries, hUGETracker .uge BGM playback, .gbapu/.gbapu.json APU trace playback, and VGM/VGZ-sourced DMG playback through the same compact on-ROM trace repack. Bank selection for banked BGM data is emitted by the runtime; source code keeps using Music.Asset(...), Music.Play(...), and Audio.Update().

  • --target sms: in development. Emits a 32 KiB Master System ROM for pure-language programs with static background drawing (Palette.Set, Tilemap.Set, Tilemap.Fill), sprites (Sprite.Asset, Sprite.Draw), World.Map worlds with Camera.* scrolling on both axes, music and sound effects from SN76489 VGM logs, or transcoded from NES and Game Boy ones (Music.*, Sfx.*, Audio.*), Video.Init(), Video.Present(), Video.WaitVBlank() and portable Input.* on the two-button pad; every other SDK operation fails with a capability diagnostic. See docs/MasterSystemTarget.md.

  • --target gg: in development. Builds the Master System target for the Game Gear and writes a .gg ROM: the program sees the 160x144 LCD window, colours are 12-bit, and Start is a held button. The camera scrolls inside the window. See docs/MasterSystemTarget.md (Game Gear).

The NES and Game Boy targets accept --runtime-abi-out <path> for a deterministic, versioned JSON projection of their target-owned runtime layout and compiled user-local symbols, bound to the emitted ROM content. It does not change ROM bytes. tools/gameboy/runtime_abi.py and tools/nes/runtime_abi.py validate the matching target contract, layout fingerprint, and ROM identity before diagnostics consume an address. Game Boy runtime regions are fixed in GameBoyRuntimeMemoryLayout, so its runtimeRegions collection is empty; NES adds the ROM-specific regions allocated by its build.

Both cartridge targets accept --symbols-out <path> for a deterministic UTF-8 debugger symbol file. Each line has the MCP-compatible form AAAA name and names may contain spaces. The export includes the target's runtime-memory ranges and named addresses, compiled variables, and any ROM-specific runtime regions reported by that target. It intentionally excludes ROM code labels, fixed linker symbols, and banked segments in this first version. Because one symbol file describes one target build, the option requires a single target and cannot be combined with --world-budget-report.

Every target accepts --build-report-out <path> for a deterministic JSON build report (retrosharp.build-report v1): the ROM size and SHA-256, the bytes each owner placed in each bank, the code and data labels with their bank and address, and the static per-frame CPU estimate. The Master System and Game Gear reports carry only the ROM size and hash so far. The report is a diagnostic for comparing two builds with tools/roms/diff_roms.py, not a stable contract, and it requires a single target.

For example, compile the runner and export both diagnostic sidecars:

dotnet run --project src/RetroSharp.Cli/RetroSharp.Cli.csproj -- \
  --target gb \
  --out /tmp/runner.gb \
  --runtime-abi-out /tmp/runner.gb.runtime-abi.json \
  --symbols-out /tmp/runner.gb.sym \
  games/runner/runner.retrosharp.json

dotnet run --project src/RetroSharp.Cli/RetroSharp.Cli.csproj -- \
  --target nes \
  --out /tmp/runner.nes \
  --runtime-abi-out /tmp/runner.nes.runtime-abi.json \
  --symbols-out /tmp/runner.nes.sym \
  games/runner/runner.retrosharp.json

In the matching Game Boy or NES debug MCP, call load_rom with the generated ROM, load_symbols with its .sym file, then resolve_symbol or read_symbol with a canonical name such as camera.XLow, camera.X, packed camera.CommitCount, WorldPack.VisualSlot0, or player.x.

The actor framework acceptance slices live in samples/actor-framework and samples/actor-contact-lifecycle. The former shows fixed pools, declarative ActorPool enemies = Actors.Pool(...) runtime state, EnemyDefinition Goomba = Enemies.Def(...) compile-time metadata with qualified EnemyBehavior values, Tiled object-layer spawns, and runtime camera-window activation. The latter proves transient top-contact capture, conditional defeat, preserved behavior state, configured defeated lifetime, and slot release on Game Boy and NES. Both lower without heap allocation or runtime dispatch. The completed plan is docs/history/ActorFrameworkRoadmap.md; the current public surface is in docs/Portable2DSdkV1.md. The measured generated-code scaling work and CPU-budget plan live in docs/history/GeneratedCodePerformanceRoadmap.md.

Language surface

Right now, RetroSharp can compile simple programs with:

  • Basic arithmetic and logic operations
  • Variables and assignments
  • Decimal, hexadecimal (0x2A), binary (0b1010_0000), _-separated, and width-suffixed (255u8, 0x1234u16) integer literals
  • Type aliases normalized at compile time
  • Compound assignment with +=, -=, &=, |=, and ^=, plus statement-only ++ and --
  • Top-level and block-local constants with optional type annotations folded at compile time, including constant boolean and conditional expressions
  • sizeof(type) folded at compile time for primitive, reserved internal pointer-size marker, enum, and plain struct types
  • offsetof(type, field) folded at compile time for direct fields of plain struct types
  • countof(array) folded at compile time for fixed-size local arrays
  • Top-level enums with qualified members folded at compile time
  • Plain local structs with named and shorthand initializer lists plus field access in the current cartridge targets
  • Restricted static classes with fixed-layout fields, instance methods lowered to receiver helpers, and static constants/methods lowered without heap allocation or dispatch
  • Fixed-size local byte arrays with initializer lists, initializer-inferred lengths, constant index access, and byte-backed runtime index access in the current cartridge targets
  • Function calls, including inline helper calls with parameters, named arguments, default parameter values, single-return expression helpers, and => expression-bodied helpers in the current cartridge targets
  • Control flow (if/else if/else, no-fallthrough switch with multi-value and half-open range cases, while, do while, for, half-open range for, break, continue)
  • Half-open range membership expressions such as tile in 1..4, lowered like tile >= 1 && tile < 4
  • Short-circuit &&/|| and unary ! conditions, plus byte-backed 0/1 logical value expressions, in the current cartridge targets
  • Conditional value expressions (condition ? whenTrue : whenFalse) lowered to direct branches in the current cartridge targets
  • Byte-backed bitwise &, |, and ^, plus folded constant ~ masks, in the current cartridge targets
  • Explicit casts such as (u8)expr as validated zero-cost expression markers in the current cartridge targets
  • Canonical fixed-width types (u8, i8, u16, i16, bool) plus source aliases where the current target accepts them
  • Immutable let locals with compile-time width inference: i16/u16 fields, locals, helper results, and arithmetic keep two-byte storage; expressions proven byte-valued keep one byte
  • Public gameplay code stays at the SDK/resource level; raw buffers, hardware addresses, pointer member access, and ptr<T> APIs remain reserved for internal SDK/backend addressability policy

let never narrows a statically word-valued expression. A width suffix on a single literal remains authoritative, and an unsuffixed literal keeps the existing u8 default. For non-literal expressions, an unambiguous i16 or u16 operand makes the result use that same word type; otherwise a proven byte-valued expression remains byte-backed. Mixing signed and unsigned word values, or using an expression whose scalar type cannot be proven, is a compile-time error; an explicit cast disambiguates the declaration. This rule changes storage selection only and adds no runtime type metadata or dispatch.

Example program:

i16 Main()
{
    return 2 * 3 * 4;
}

Framework surface

The portable 2D SDK gives game code a common vocabulary for tile-and-sprite machines while keeping each target's limits explicit. Published projects pin RetroSharp.Portable2D in manifest sdkDependencies; checkout-local projects can select the bundled copy through libraries. Game code then uses frame/input calls, Tiled World.Load(...) with native tileset animation and a runtime background-animation toggle, collision flags, camera-relative AABB queries, logical palettes, logical sprites, animation, music declarations, and fixed-pool actor framework sugar. Standalone source files can still declare import RetroSharp.Portable2D;; unknown imports are rejected. Generic compiler and frontend APIs start with an empty SdkLibraryRegistry: a direct C# host must compose every source library it wants to expose, including Portable2D. The CLI is that host for normal command-line use: it supplies its bundled Portable2D package alongside any --lib-path libraries. Pass --no-default-libraries to build a source file or project without that bundle; then use --lib-path and explicit imports for every library required. A library package is a directory with retrosharp-library.json and one or more RetroSharp source files:

{
  "import": "Acme.Wait",
  "rootNamespace": "Acme.Wait",
  "sourceRoot": "src",
  "namespaceMode": "physical",
  "sources": [
    "src/api.rs",
    "src/timing/rules.rs"
  ],
  "targets": [ "gb", "nes" ]
}

--lib-path may point at one package directory or at a directory containing package subdirectories. Local paths remain the fastest editing workflow.

Versioned source libraries and SDKs

Projects can also restore source libraries and SDKs from one isolated NuGet feed. Versions in packageDependencies are direct, exact versions; the CLI writes the complete direct-and-transitive graph to the portable retrosharp.lock.json and extracts packages under .retrosharp/packages. The same graph is prepared once before a multi-target GB/NES build. A package must contain the ADR's retrosharp-package.json with kind: "source-library" and its library manifest at retrosharp/retrosharp-library.json.

An SDK package uses kind: "sdk-plugin" and includes that source manifest plus its descriptor assembly under plugin/net10.0/. Declare it once to restore and activate both parts:

{
  "targets": ["gb", "nes"],
  "sources": ["src/main.rs"],
  "packageSources": ["./local-feed"],
  "sdkDependencies": { "Acme.Probe": "1.0.0" }
}

packageDependencies remains for source-only packages. A transitively restored SDK remains passive; declare it directly in sdkDependencies before importing and using it. The host checks package identity/version and the source import against the descriptor. An explicit RetroSharp.Portable2D package replaces both bundled pieces. --no-default-libraries removes the bundle while retaining explicit packages.

{
  "targets": ["gb", "nes"],
  "sources": ["src/main.rs"],
  "packageSources": ["./local-feed"],
  "packageDependencies": { "Acme.Timing": "1.2.3" }
}

The normal build only invokes restore if the local material is absent and never rewrites an existing lock. Use --restore to create or intentionally update a lock, --locked-mode to require it, and --no-restore or --offline to fail without contacting the feed when material is absent. One feed needs no extra mapping; multiple feeds must declare packageSourceMappings from each source entry to its package-id patterns, so a failed feed cannot silently fall back to another. Restore inspects archive paths and rejects build content and undeclared executables. It never loads package DLLs, props, targets, tools, or scripts. SDK entry points run only during explicitly activated builds.

For local development, use --lib-path and --sdk-plugin-path <retrosharp-package.json-or-dll> together to replace one product. External DLLs need package metadata; bare DLLs are invalid. A complete local pair overrides a restored or bundled SDK; a partial override fails. Sample and smoke builds that use the CLI bundle therefore pass neither path; external SDK tests pass both paths with --no-default-libraries.

Source-only libraries cannot add new compiler semantics. When a feature needs a new namespaced operation, resource, capability, validator, or per-target lowering hook, the host registers a static in-process SDK plugin instead. Pass an explicit trusted local plugin with repeatable --sdk-plugin-path <dll-or-retrosharp-package.json>. External DLLs require declarative package metadata; bare DLLs are rejected. --sdk-plugin <id> and project plugin ids have no production registry. Plugin paths work with source and project builds, resolve relative to the current working directory, and are intentionally not stored in manifests. A loaded assembly exposes exactly one public ISdkPluginEntryPoint with a public parameterless constructor. Unknown ids, invalid assemblies, incompatible contracts, and duplicate ids are rejected before compiling. Packaged plugins are checked through metadata and PE references before loading; descriptor coherence is checked after construction. Use --sdk-plugin-diagnostics to include technical exception details. A registered plugin is enforced per target: a target lowers a plugin operation only when it provides a matching lowering hook and grants the operation's required capabilities, otherwise the build fails before lowering with a diagnostic naming the plugin feature and target id. The experimental reference plugin is RetroSharp.Platformer2D (Platformer.GroundProbe()), which lowers on Game Boy and is unsupported on NES. Path-loaded plugins execute arbitrary trusted code inside the compiler process; there is no sandbox, implicit discovery, package resolution, unloading, or stable cross-version binary compatibility promise. See docs/SdkPluginBoundary.md.

Static plugins can also import declarative project resources at compile time. The project names the importer, asset identity, and path explicitly:

{
  "plugins": ["RetroSharp.PushBox"],
  "resources": [
    {
      "name": "levels",
      "importer": "RetroSharp.PushBox.Levels",
      "path": "assets/levels.sok"
    }
  ]
}

Resource paths are resolved relative to the project manifest. The importer runs inside the registered host plugin and contributes deterministic immutable byte tables plus static u8(u8) lookup functions; Game Boy and NES retain ownership of ROM placement and lookup emission. This is not a pre-build script or a runtime parser.

When a library package opts into namespaceMode: "physical", folder names under sourceRoot become compile-time namespaces just like project sources. Root source files commonly act as the package's public facade after import, while helper files under folders can reuse names without colliding with the game or with other packages.

Portability is capability-based, not magic. A shared source file is portable only when each selected target can support the requested scrolling mode, sprite budget, palette slots, audio format, runtime tile writes, and asset shape. See docs/Portable2DSdkV1.md for the current API reference.

Project objectives

  • Keep the language small, explicit, and independent from any one machine.
  • Let the framework make small real 2D games practical without hiding hardware budgets.
  • Keep raw machine access available through target intrinsics.
  • Make Game Boy and NES the first compatibility pair for portable SDK work.
  • Prefer zero-cost ergonomics over managed-runtime features: no heap, GC, RTTI, boxing, virtual dispatch, closures, delegates, or hidden object identity.

Which platforms does it compile for?

The active targets are Game Boy and NES, implemented under src/RetroSharp.GameBoy and src/RetroSharp.NES. A Master System target is in development under src/RetroSharp.MasterSystem; see docs/MasterSystemTarget.md. Runnable samples live under samples/; only samples marked portable-sdk in samples/manifest.json are cross-target SDK evidence.

The retired Z80 compiler path, its exact recovery commit, and the two external forks it used are recorded in docs/history/LegacyZ80Compiler.md.

Installation

RetroSharp is distributed as a .NET tool:

dotnet tool install --global RetroSharp.Tool

Then use it to compile your programs:

retrosharp --target gb --out myprogram.gb myprogram.rs

Build the Game Boy runner from source:

dotnet run --project src/RetroSharp.Cli/RetroSharp.Cli.csproj -- \
  --target gb \
  --out games/runner/bin/runner.gb \
  games/runner/runner.retrosharp.json

For games split across several source files, use a RetroSharp project manifest. It is a small JSON file owned by RetroSharp, not an MSBuild project:

{
  "targets": [ "gb", "nes" ],
  "outputs": {
    "gb": "bin/runner.gb",
    "nes": "bin/runner.nes"
  },
  "rootNamespace": "Runner",
  "sourceRoot": "src",
  "namespaceMode": "physical",
  "sources": [
    "src/Program.rs",
    "src/player/State.rs",
    "src/camera/State.rs"
  ],
  "libraryPaths": [
    "lib"
  ],
  "libraries": [
    "RetroSharp.Portable2D"
  ]
}

Then build the project file directly:

dotnet run --project src/RetroSharp.Cli/RetroSharp.Cli.csproj -- retrosharp.json

Inspect a Tiled world's deterministic Game Boy or NES WorldPack and target budgets without building or writing a ROM by opting into --world-budget-report:

dotnet run --no-launch-profile --project src/RetroSharp.Cli/RetroSharp.Cli.csproj -- \
  --target gb \
  --world-budget-report \
  samples/tiled-cross-target-2d-scroll/cross-target-2d-scroll.tmj

The option accepts one importer-ready .tmx or .tmj, requires --target gb or --target nes, cannot be combined with --out, writes one deterministic retrosharp.world-budget/v1 JSON object to stdout, and writes nothing to stderr on success. Without this option, successful GB/NES stdout and stderr remain unchanged. --no-launch-profile prevents the .NET launcher from prepending its own status line to the CLI's JSON stdout.

The JSON reports source and hardware dimensions, metatile placements and unique metatiles, chunk count, the exact visual/collision stored bytes from the real WorldPack chunk directory, total serialized bytes, generated target tiles, staging RAM, ROM/PRG bytes and required banks, and the bounded VBlank tile/attribute commit shape. Diagnostics are always ordered as addressing, rom-prg, chr-tile-count, staging-ram, and vblank; each carries measured usage, the evaluated limit/profile, and an actionable remedy when it overflows. selectedProfile is deliberately null: the report names current and accepted-future profiles but never selects a banker or mapper.

stagingRam.usedBytes is the pack's actual fixed-slot shape; stagingRam.limitBytes is the accepted WorldPack v1 maximum (554 bytes on GB, 594 on NES), while physicalRamCapacityBytes separately names the machine's total RAM capacity. Addressing permits a 32,768-pixel extent because its last coordinate is 32,767.

NES keeps nes-mapper-0-current separate from the accepted but unimplemented nes-mmc3-tvrom-v1-accepted-future requirements. The report likewise keeps currentPhysicalChrCapacityBytes, acceptedFuturePhysicalChrCapacityBytes, the 8 KiB resident CHR limit, and the 256-entry tile-index budget as separate facts. Game Boy cartridge.romPrgBytesUsed is measured content; allocatedRomBytes stays null, so a padded output ROM length is never misreported as bytes used.

See how full an NES cartridge already is, and where the next change will land, by adding --capacity-report to a normal NES build:

dotnet run --no-launch-profile --project src/RetroSharp.Cli/RetroSharp.Cli.csproj -- \
  --target nes \
  --capacity-report \
  --out games/runner/bin/runner.nes \
  games/runner/runner.retrosharp.json

The option requires --target nes, a single target, and cannot be combined with --world-budget-report, which inspects a world without building. It writes one retrosharp.nes-capacity/v1 JSON object to stdout and does not change a single emitted byte: the ROM is identical with and without it.

The report leads with bindingConstraint, the scarce resource currently closest to its own capacity. It also names the selected profile and PRG/CHR size; the fixed PRG region's used bytes, remaining headroom and capacity; the banked program's phase-to-bank map with per-bank occupancy and remaining R6 headroom; the bytes attributed to fixed veneers, pinned R7, boot R7 and resident CHR; the top user-function duplication holders; and per-frame CPU work for each named window. resources repeats fixed PRG, whole R6, the hot phase's per-bank cap, and the CPU windows with the same vocabulary: name, unit, use, capacity, headroom, binding status, next unit cost, and named remedy. Region capacity is used plus headroom as the build measured it, not a re-derived board constant, and bankedProgram is null for a program that is entirely fixed-resident. Shared SDK subroutines and outlined user functions are counted with their call sites rather than sized, because the build measures no byte total for either.

Two figures are deliberately labelled rather than trusted. duplication is a lower bound: pure functions the compiler answered from a generated ROM table never reach the user-function path, so it is not total recoverable headroom. callsPerFrame is static: it is not path sensitive and does not multiply loop iterations, so it is a lower bound where repeatsPerFrame is set and an upper bound over mutually exclusive branches otherwise. cpuWork is an observation per named window — frame is the whole NTSC frame, video-safe is the VBlank window. The video-safe figure is the full cost the NES frame plan already imposes, including NMI entry reserve, frame-boundary bookkeeping, packed column commit, and retained-OAM publication. Other incomplete CPU windows still mean uncalibrated work exists outside the known figure.

When any reported resource's headroom falls below five per cent, including the hot phase's per-bank cap or a CPU window, the report adds a near-cliff warning and the CLI repeats it on stderr. That is a warning, never a gate: spilling into R6 and escalating the board are designed behaviours where allowed, so the build still succeeds and nothing here can fail it.

Project paths are resolved relative to the JSON file. --target, --out, and additional --lib-path options still work as command-line overrides. Use project sources for code that belongs to the game itself; use retrosharp-library.json packages when code should behave like an external, imported dependency. libraryPaths tells the CLI where to discover local packages; libraries names the package import paths that this project loads. Source-level import still works as the explicit transitional form, but C#-style project code normally declares loaded libraries in the manifest and uses using only for namespace lookup inside source files.

When namespaceMode is physical, RetroSharp derives zero-cost source namespaces from the path under sourceRoot: src/player/State.rs belongs to Runner.Player, while src/camera/State.rs belongs to Runner.Camera. The compiler lowers those names to unique internal symbols, so folders can contain same-named helper classes such as Rules; source can refer to static constants with path-qualified names like Player.Rules.Start.

Export a GBS subsong into a Game Boy APU trace when you want faithful playback of the original register writes:

dotnet run --project src/RetroSharp.Cli/RetroSharp.Cli.csproj -- \
  gbs-to-gbapu \
  --in path/to/theme.gbs \
  --subsong 1 \
  --seconds 120 \
  --out path/to/theme.gbapu

This writes a compact binary .gbapu (use --emit-json for a JSON debug view, or gbapu-dump <file> to print the register writes). The loop is auto-detected and the capture is trimmed to one loop body (--no-auto-loop/--loop-cycle override). Use the result directly with Music.Asset(...). The source keeps cycle deltas for every supported APU register write; the ROM compiler maps them onto DMG VBlank frames, removes redundant non-trigger writes, deduplicates repeated frame groups into a pool, and stores full Wave RAM uploads as compact block commands for Audio.Update() playback.

The helper requires gbsplay on PATH unless --gbsplay <path> is supplied. GBS files are not loaded directly by Music.Asset(...). See docs/GameBoyApuTraceFormat.md for the format shape, runtime use, limitations, and future alternatives.

Build the first cross-target camera sample for both cartridge targets:

dotnet run --project src/RetroSharp.Cli/RetroSharp.Cli.csproj -- \
  --target gb \
  --out /tmp/cross-camera.gb \
  samples/cross-target-camera/camera.rs

dotnet run --project src/RetroSharp.Cli/RetroSharp.Cli.csproj -- \
  --target nes \
  --out /tmp/cross-camera.nes \
  samples/cross-target-camera/camera.rs

The sample sources have been migrated to the current language surface: symbolic const values and enums for static data, aliases where they clarify byte-backed values, let for immutable frame-local values, inline/pure helper contracts, SDK dot-calls, restricted static classes and receiver methods where they clarify ownership, switch expressions, pipelines, while (true) for infinite runtime loops, and compound mutation syntax where it maps directly to the old explicit assignments.

AI agent orientation

Repository-specific instructions live in AGENTS.md. For tasks that need architecture, acceptance, or ownership context, docs/AgentContext.md routes to the relevant owner document. Completed roadmaps are historical context, not a startup bundle. llms.txt is the compact documentation index.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

Version Downloads Last Updated
0.0.1254 106 10/30/2026
0.0.1246 99 10/29/2026
0.0.1242 104 10/29/2026
0.0.1240 100 10/29/2026
0.0.1231 106 10/28/2026
0.0.902 121 10/13/2026
0.0.777 110 10/5/2026
0.0.769 111 10/5/2026
0.0.662 121 9/27/2026
0.0.661 119 9/24/2026
0.0.660 115 9/24/2026
0.0.659 116 9/24/2026
0.0.658 115 9/24/2026
0.0.657 112 9/24/2026
0.0.656 104 9/24/2026
0.0.655 122 9/24/2026
0.0.654 112 9/23/2026
0.0.653 119 9/23/2026
0.0.652 120 9/23/2026
0.0.651 118 9/23/2026
Loading failed

# Changelog 0.0.769

## Changes since v0.0.662

- Standalone: single-file publishes self-extract so the macOS package builds again (#1221) (`baeadc4a`)
- Portable2D: Random draws reserve no RAM of their own (#1220) (`6608d990`)
- A word comparison evaluates each computed operand once (#1203) (#1219) (`112677bd`)
- Language server and Claude Code plugin: retrosharp lsp, retrosharp check (#1218) (`958303d9`)
- Portable2D: deterministic Random source (#1199) (#1217) (`e3ba09ca`)
- Portable2D: World.Strip composes a scrolling world from borders and fill for any screen width (#1214) (#1215) (`a91b7592`)
- Portable2D: World.RepeatVertically scrolls a repeating world without end (#1198) (#1213) (`5a97eb95`)
- Stop tracking generated ROMs; tests compile what they check (#1207) (#1212) (`bd66df9e`)
- Use the renamed Zafiro debugger packages (#1211) (`386b9c74`)
- Runtime multiplication, division, remainder and shifts on every target (#1197) (#1209) (`124853ee`)
- Shorter test suite: faster banked compiles, long classes first, fewer repeated compiles (#1201) (#1208) (`6a286c7e`)
- Samples and generated actor/projectile code test camera AABB overlap as bool (#1202) (`235ec0f4`)
- GB camera publishes its scroll before streaming the edge (#1046) (#1195) (`bd88c39b`)
- Word assignments read the destination before overwriting it (#1194) (`52ef9327`)
- Camera AABB overlap answers bool (#1192) (`cea7ecf8`)
- Relational conditions branch directly when the jump is taken on true (#1191) (`b793d1a3`)
- Sprites: pieces past a screen edge no longer show on the other side (#1185) (`63a936a8`)
- push-box builds for the Game Gear with the Game Boy layout (#1172) (`6bf32c04`)
- snow-bros-jr builds for the Game Gear (#1171) (`a613d89f`)
- runner and runner-lift build for the Game Gear (#1170) (`c27d6bf4`)
- Game Gear: the camera scrolls and streams inside the 160x144 window (#1175) (`39991add`)
- Game Gear builds as a profile of the Master System target (#1169) (`3bccbe40`)
- Master System: actor pools draw actors left of the camera with a signed X (#1167) (`25a9666a`)
- snow-bros-jr builds for the Master System (#1141) (`63ad532a`)
- Master System: streamed sprite tiles take the room of an unwritten camera edge (#1155) (`5c217d23`)
- push-box builds for the Master System (#1142) (`88e54ba2`)
- Master System: sprite asset tiles as background (#1152) (`a73e0e69`)
- Master System: runtime Tilemap.Set queued for VBlank (#1151) (`05f56682`)
- Master System: stream sprite tiles past the pattern table (#1149) (`14abddd3`)
- Master System: wider frame margin for runner-lift (#1146) (`050d37c7`)
- Master System: border and blanked left column show the world's background color (#1145) (`48879ce4`)
- Master System: explicit layout form of Sprite.Asset (#1148) (`1302115c`)
- Master System: runner-lift as an acceptance game; byte-cast sprite coordinates; Camera.Init leaves scroll registers to Apply (#1140) (`57543e47`)
- Master System: runner as an acceptance game (#1139) (`ec87754d`)
- Master System: actors reset on world activation; world-activation sample (#1134) (`9684da7a`)
- Master System: Animation.Clip/Frame through the shared Z80 lookup, actor pool budgets and actor samples (#1133) (`513d8d1d`)
- Master System: sprite art falls back to NES then Game Boy variants; platform samples with acceptance (#1132) (`abea82a8`)
- Screen collision queries answer the rows the screen shows over a band world (#1124) (`c2e09761`)
- Master System: Tiled background tile animation and BackgroundAnimation.SetEnabled (#1119) (`a48c1ae1`)
- Master System: audio samples with functional scenarios (#1097) (`172ced12`)
- Master System: Button.Select reads as never held with a warning, and managed-audio (#1121) (`75e0dc5f`)
- Master System: music-switch from DMG VGM logs beside its Game Boy songs (#1113) (`154ad8df`)
- Master System: functional audio service and progress evidence (#1112) (`11c0204a`)
- Master System: shots samples and projectile acceptance; shots-bouncy floor on screen (#1034) (`485b06bf`)
- Master System: Camera.Follow, Sprite.DrawWorld and actor camera reads (#1107) (`d91fc4c1`)
- Master System: logical palettes Palette.Background and Palette.Sprite (#1111) (`b6730058`)
- Master System: world activation over WorldAsset worlds (#1106) (`b407eebf`)
- Master System: large worlds split across banks, and tiled-tall (#1105) (`a8099212`)
- Master System: World.Load Tiled worlds through the raw route (#1104) (`15328a59`)
- Master System: AABB collision over World.Map and World.Flags (#1103) (`9b38a421`)
- Master System: sprite flips through mirrored copies and cross-target-camera acceptance (#1101) (`87e77b5e`)
- Master System: read-only data in switchable Sega mapper banks (#1098) (#1110) (`1c929564`)
- Master System: transcode NES 2A03 and Game Boy DMG VGM logs to the PSG (#1096) (#1109) (`b44721a9`)
- Master System: SN76489 PSG player for music and sound effects (#1095) (#1108) (`36af5b15`)
- Master System: camera and hardware scrolling over World.Map worlds (#1033) (#1102) (`f0f437d1`)
- Master System: Sprite.Draw through the SAT and the cross-target sprite-draw sample (#1093) (#1100) (`791f10bb`)
- Master System: Sega mapper program banking (#1036) (#1099) (`199e8ad3`)
- Master System: 4bpp sprite assets and the sprite palette from the neutral plan (#1092) (#1094) (`a6d83f5a`)
- Master System: portable input on the two-button pad and Pause as Start (#1031) (#1091) (`e0aaedf5`)
- Master System: static drawing through the VDP and the static-drawing sample (#1088) (#1090) (`d1bb2b95`)
- Master System: minimal ROM compiler with boot, VDP init and frame loop (#1087) (#1089) (`16d93047`)
- Z80 family: move the language core into RetroSharp.Z80Family (#1084) (#1086) (`0f3f3d24`)
- Z80 family: language core as its own class with explicit dependencies (#1083) (#1085) (`63c4f442`)
- Z80 family: storage layout for the language core (#1076) (#1082) (`f0f395bf`)
- Z80 family: target lowering port for the language core (#1075) (#1081) (`717eba82`)
- NES: sprite palette slot sharing from a neutral plan (#1072) (#1080) (`5ee8ba24`)
- NES: per-asset sprite palettes from the neutral palette plan (#1071) (#1079) (`f2125059`)
- NES: background palettes from the neutral palette plan (#1070) (#1078) (`c7481870`)
- Z80 family: injectable banking model with MBC1 for Game Boy (#1057) (#1074) (`d2ab6eec`)
- Z80 family: shared builder and SM83/Z80 encoding dialects (#1056) (#1073) (`d10c7ead`)
- Game Boy: palettes from the neutral palette plan (#1054) (#1069) (`3d7c9c43`)
- Portable2D: neutral palette plan derived from art (#1053) (#1068) (`6543f6ab`)
- Portable2D: color capabilities and ADR 0005 (#1052) (#1067) (`c3b343f4`)
- SDK: neutral packed-camera slot schema (#1051) (#1066) (`8bbcbc68`)
- Master System: target descriptor, capabilities, runtime memory layout and ABI (#1029) (#1064) (`2c850f49`)
- Master System tooling: headless test machine and functional acceptance (#1028) (#1063) (`21b7b0d7`)
- SDK: neutral camera stream plan (#1050) (#1062) (`1790d972`)
- SDK: neutral camera streaming features (#1049) (#1061) (`73538530`)
- SDK: neutral WorldPack runtime geometry and directory layout (#1048) (#1060) (`b371b0aa`)
- ADR 0004: shared Z80-family codegen (#1027) (#1059) (`70a72e5f`)
- GB: keep internal jump spans in one switchable bank (#1015) (#1047) (`4691d852`)
- SDK: follow camera with static policy constants and one runtime (#1014) (#1045) (`435b8368`)
- fix(gb): hide sprite draws skipped since the last publication (#1021) (#1043) (`8dac689c`)
- Fix dotnet tool and standalone release packaging (#1042) (`654fb272`)
- SDK collision: share the neutral AABB probe plan between Game Boy and NES (#925) (`da2310c5`)
- fix(nes): keep small-world collision grids resident (#1020) (#1040) (`c2ee9372`)
- Test: centralize Portable2D test-host package resolution (#1039) (`36b7349b`)
- Fix NES running-jump camera stutter (#1022) (`51b799c5`)
- feat(compiler): prune constant-selected branches at emission (#1019) (`57f3a06c`)
- feat(sdk): configurable follow camera profiles (`28d6f7f6`)
- docs: streamline agent guidance (`b05ba06b`)
- fix: resume horizontal motion after clearing a wall (#1012) (`6e02d1be`)
- sync: Update ROMs and sidecars (`1f617da3`)
- feat: animate every runner-lift waterfall tile on GB and NES (`1d445fc2`)
- feat(runner-lift): animate Part2 grass within NES video budget (#1010) (`d817bc1e`)
- assets(runner-lift): prepare grass animation frames (#1009) (`02a6ee93`)
- feat(runner-lift): update map collisions to use TSX Platform properties and regenerate ROMs (`4c905cc2`)
- fix(runner-lift): optimize Part2 tileset to 30 unique tiles (tol 55) merging all duplicate groups (`303ff6a2`)
- fix(runner-lift): optimize Part2 map (tolerance 40, GID 0 transparency, 46 unique tiles) (`fc9f0032`)
- fix(runner-lift): optimize Part2 map tileset to 64 tiles using tolerance deduplication (`1b2873e9`)
- feat: add portable kinematics source library (#1007) (`13c77107`)
- research: evaluate portable kinematic controller (#993) (`6b5e04b6`)
- fix(nes): keep fast WorldPack lookups for partial chunks (#1003) (`a868a862`)
- test: freeze runner-lift scroll cadence replay (#1001) (`5455bd31`)
- chore: balance Codex defaults and parallelize solution gate (#1000) (`f59e2315`)
- feat: derive asset palettes and require PNG sprites (#996) (#999) (`e60687f4`)
- Build: eliminar warnings del propio código y activar TreatWarningsAsErrors (#956) (#998) (`80cf9d15`)