Skip to content

Split the emulator into core, headless and desktop Maven modules - #34

Merged
dimiro1 merged 3 commits into
masterfrom
dimiro1/split-core-and-ui-modules
Aug 18, 2026
Merged

Split the emulator into core, headless and desktop Maven modules#34
dimiro1 merged 3 commits into
masterfrom
dimiro1/split-core-and-ui-modules

Conversation

@dimiro1

@dimiro1 dimiro1 commented Aug 18, 2026

Copy link
Copy Markdown
Owner

The emulator is three Maven modules now — mynes-core (the console, with no compile-scope dependencies at all), mynes-headless (the command line) and mynes-desktop (the Swing window, FlatLaf and MigLayout) — so the rule that the console and the CLI must not reach into the window is enforced by the class path rather than by a sentence in CLAUDE.md. The palette tables moved from mynes.ui.palette down into mynes.palette in the core, since both front ends draw with them and neither owns them, while PaletteDialog stayed behind in mynes.ui. The jar and the release zip now build under mynes-desktop/target/, which the docs, both workflows, all four scripts and .conductor/settings.toml follow, and the Harte fixtures move to mynes-core/testdata because Surefire resolves that path relative to the module rather than the checkout. The parent declares exec-maven-plugin skipped — with an executable named, since Maven refuses the goal while injecting that required parameter before the mojo can notice the skip — so mvn -q compile exec:exec and exec:exec@headless still work unqualified from the root.

Nothing about the emulation changed: against a jar built before the split, 400 frames of nestest give a byte-identical save state, memory dumps, PNG and WAV on the same CPU cycle, all 1567 tests pass (1348/120/99 across the modules), and the distribution smoke test still finds 12 palettes in the zip.

🤖 Generated with Claude Code

dimiro1 and others added 3 commits August 17, 2026 23:11
`NESPalette` is a table of ARGB integers and `Palettes` reads files; neither
has ever imported Swing, and both were only under `ui` because the window was
the first thing to want them. The headless mode has wanted them for as long as
it has existed, which is why CLAUDE.md carried a carve-out saying so.

They move to `mynes.palette`, which is where the next commit needs them: with
the core in a module of its own, a front end reaching down for a palette is
fine and the core reaching up for one is a cycle. `PaletteDialog` stays behind
in `mynes.ui` next to `GameUIFrame`, its only caller, rather than leaving a
one-class package where the tables used to be.

The carve-out goes with them. Nothing outside `mynes.ui` imports it now.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The boundary was a convention: CLAUDE.md asked that nothing in the console or
the command line reach into the window, and only a reader enforced it. It is
three Maven modules now, so a chip that wants a JFrame does not compile.

  mynes-core       the console and the picture it makes; nothing at compile scope
  mynes-headless   the command line;                     core
  mynes-desktop    the window and Main;                  core, headless, FlatLaf, MigLayout

Desktop depends on headless rather than the reverse because Main is the one way
into either mode, which is also why the fat jar and the release zip are built
there. They move with it: `mynes-desktop/target/mynes.jar`, and the same for the
zip, which every document and both workflows now name.

Two paths had to move rather than merely be renamed. The Harte fixtures go to
mynes-core/testdata, because HarteCaseLoader resolves them relative to wherever
Surefire runs and that is the module now -- and getting it wrong is silent, a
quiet fall back to the committed 500-case subset. And two cartridges are copied
into the headless module, which needs a path on disk to hand to --rom rather
than a resource on the class path.

Nothing about the emulation changed. Against the jar built before the split, the
same 400 frames of nestest give an identical save state, identical RAM, OAM,
palette, nametable, PRG-RAM and CHR dumps, an identical PNG and an identical
WAV, on the same CPU cycle.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…at the ROM

The exec configuration lives in mynes-desktop now, and a goal invoked from the
command line is invoked on every project in the reactor -- so the short command
died on the parent before it reached the module that could serve it.

Naming the module on the command line does not help: -am pulls the parent back
in, and without -am a fresh clone has nothing to resolve mynes-core against. So
the other three declare the plugin skipped instead, and both commands stay as
short as they were.

Skipped and given an executable, which looks redundant and is not: `executable`
is a required parameter, so Maven refuses the goal while injecting it, before
the mojo has run far enough to notice it was told to skip. The execution id has
to be spelled out too -- `exec:exec@headless` on a project with no execution of
that name runs with an empty configuration rather than with the plugin's.

The forked process also gets an explicit working directory, the checkout rather
than the module, so that a --rom path means what it looks like and the report
still lands in target/headless beside the other target directories.

.conductor/settings.toml keeps both commands unchanged and follows nestest to
mynes-core/src/test/resources.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@dimiro1
dimiro1 merged commit 2806b2e into master Aug 18, 2026
2 checks passed
@dimiro1
dimiro1 deleted the dimiro1/split-core-and-ui-modules branch August 18, 2026 20:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant