# HELLO WORLD! - Neo Geo homebrew example

Minimal Neo Geo AES cartridge built with the WLA DX toolchain. The 68k program prints `HELLO WORLD` centered on the fix layer with white text on a black background, then sends three YM2610 SSG tone commands. In MAME this final accepted version is user-confirmed to produce one clear coin-like sound.

## Sources

| File | What it contains |
| --- | --- |
| [main68k.s](main68k.s) | 68k program. Pulls in `crt0.s`, selects the cartridge fixed layer, writes the text to VRAM, then sends beep commands `$10`, `$11`, `$12`. |
| [soundz80.s](soundz80.s) | Z80 sound driver. Handles Neo Geo BIOS sound commands and plays low/mid/high SSG tones for `$10`, `$11`, `$12`. |
| [make_s1.py](make_s1.py) | Generates the custom S1 fixed-layer ROM containing only the glyphs needed by the example. |
| [make_c_roms.py](make_c_roms.py) | Generates blank C1/C2 sprite ROMs. MAME needs a normal Neo Geo cart layout with sprite ROMs before the cartridge fixed layer is selected reliably. |
| [../../../include/neogeo/gen_softlist.py](../../../include/neogeo/gen_softlist.py) | Shared helper used by `neogeo.mk` to generate a local MAME `neogeo.xml` softlist entry with the current ROM CRC32/SHA1 hashes. |
| [../../../include/neogeo/make_blank_rom.py](../../../include/neogeo/make_blank_rom.py) | Shared helper used by minimal tests to generate blank S1/C1/C2 ROMs when they only care about P1/M1 behavior. |
| [../../../include/neogeo/neogeo.mk](../../../include/neogeo/neogeo.mk) | Shared packaging and MAME launch rules: `neogeo-romset`, `neogeo-softlist`, `neogeo-mame-stage`, and `neogeo-run`. |
| [linkfile.68k](linkfile.68k) / [linkfile.z80](linkfile.z80) | WLA DX link files. |
| [makefile](makefile) | Builds the ROMs, packages `hello.zip`, generates the softlist entry, stages the zip for MAME, and provides `make run`. |

## Build

From this directory:

```sh
make
```

The build produces:

* `hello-p1.bin` - 1 MiB P-ROM with 68k code.
* `hello-m1.bin` - 64 KiB M1 ROM with the Z80 sound driver.
* `hello-s1.bin` - 128 KiB S1 ROM with fixed-layer glyphs.
* `hello-c1.bin` / `hello-c2.bin` - blank 512 KiB sprite ROM pair.
* `hello.zip` - MAME-style romset containing all five ROM files.
* `hello-p1.sym` / `hello-m1.sym` - MAME-compatible flat symbol files.

The size checks in `make check` verify all generated ROM sizes.

## Running in MAME

Use the generated softlist path. Direct `aes -cart hello.zip` is misleading for this example and can boot into BIOS failure paths or render the BIOS fixed font instead of the cartridge S1 data.

```sh
make run
```

You can also run any Neo Geo test that includes [../../../include/neogeo/neogeo.mk](../../../include/neogeo/neogeo.mk) with the same pattern:

```sh
make run
```

For automated smoke checks, avoid opening an interactive MAME window by overriding `MAME_FLAGS`:

```sh
make run MAME_FLAGS="-seconds_to_run 2 -nothrottle"
```

Equivalent explicit command on this machine:

```sh
C:/Users/ville/Downloads/Mame/mame.exe aes \
  -hashpath ./mame_hashpath \
  -rompath "./mame_rompath;C:/Users/ville/Downloads/Mame/roms" \
  -cart1 hello \
  -window
```

The `mame_hashpath/neogeo.xml` file is generated by [../../../include/neogeo/gen_softlist.py](../../../include/neogeo/gen_softlist.py) through the shared [../../../include/neogeo/neogeo.mk](../../../include/neogeo/neogeo.mk) rules, and `mame_rompath/hello.zip` is copied from the freshly built `hello.zip`. The other Neo Geo tests now use the same generated-softlist launch path.

## Verified Behavior

Current verification was done with the softlist launch path above:

* The ROM builds and packages cleanly with P1, M1, S1, C1, and C2.
* MAME renders centered `HELLO WORLD` in white on a black background.
* Pixel checks of MAME snapshots found only black and white pixels in the final screen, with the text bounding box at `x=113..197`, `y=113..119`.
* User-confirmed audible behavior: one Mario-coin-like sound after the text appears.
* A MAME `-wavwrite` recording of the restored version contained non-silent audio samples (`max_abs=9696`), confirming the emulator produced sound.
* A debugger trace at 68k `send_cmd` confirmed the three sound commands are issued in order: `$10`, `$11`, `$12`.
* A Z80 `audiocpu` trace confirmed the sound driver leaves its startup wait, reaches the beep handlers, and writes YM2610 ports `$04/$05` for the tones.

## Useful Findings

* The generated softlist must use `<feature name="slot" value="rom"/>`. A previous `cart_type` feature was ignored by MAME's Neo Geo slot loader.
* `neogeo.mk` now provides shared MAME targets. Test makefiles set `NG_GAME_ID`, `NG_P1`, optional `NG_M1`/`NG_S1`/`NG_C1`/`NG_C2`, then use `run: all neogeo-run`.
* The softlist ROM names must match the names inside the zip. The shared packager stages files as `<game>-p1.bin`, `<game>-m1.bin`, `<game>-s1.bin`, `<game>-c1.bin`, and `<game>-c2.bin`, and the shared softlist generator emits those same names.
* MAME's fixed-layer renderer decodes S1 tiles as `offset = (tile_code << 5) | scanline`, then uses pixel offsets `{0x10, 0x18, 0x00, 0x08}`. [make_s1.py](make_s1.py) matches this layout.
* Runtime dumps showed two fixed regions: the root/BIOS fixed region and `:cslot1:fixed`. The generated S1 matched `:cslot1:fixed`, while the BIOS/root fixed region has solid blocks for ASCII tile codes like `$48`.
* Without C1/C2 sprite ROMs in the softlist and zip, this homebrew could execute correctly and write tile words like `$F048`, but MAME still rendered the BIOS/root fixed tiles, producing a solid white bar.
* Adding blank C1/C2 ROMs and a `sprites` dataarea with `load16_byte` entries (`offset="0x000000"` and `offset="0x000001"`) makes MAME treat the entry as a normal cartridge and render the custom S1 fixed layer correctly.
* Minimal tests that do not need custom graphics should still include blank S1/C1/C2 ROMs when they are meant to load through MAME. This keeps their softlist layout close to a normal Neo Geo cartridge and avoids debugging unrelated slot-loader behavior.
* The fix-layer tilemap is column-major: `addr(col,row) = $7000 + col*32 + row`. This example writes at `$71D0`, which is column 14, row 16.
* The text tile words use palette 15: `(15 << 12) | ASCII`, so `H` is written as `$F048`.
* The Z80 must not wait forever in `halt` for the BIOS reset command. This example enables NMIs with `out ($08),a` and parks in a tiny RAM loop at `$FFFE` until the 68k sound command NMI arrives.
* The sound commands are sent after the text is visible. The current accepted behavior is a single audible coin-like sound, even though the 68k side sends `$10`, `$11`, and `$12`.
* The working Z80 sound path is intentionally simple: each command maps directly to one SSG channel-A tone handler with a `$C000` hold loop. A later experiment that tried to make the Z80 generate multi-beep burst loops produced silence and should not be used as the baseline.

## Debugging Tips

Load symbols in the MAME debugger when needed:

```text
symlist hello-p1.sym
symlist hello-m1.sym
```

Useful current symbol addresses:

* 68k `main`: `$000002D6`
* 68k `print_next`: `$000002F8`
* 68k `print_end`: `$0000030A`
* 68k `send_cmd`: `$0000034A`
* Z80 `nmi_handler`: `$0066`
* Z80 `driver_start`: `$0115`
* Z80 `beep_low` / `beep_mid` / `beep_high`: `$014F` / `$0158` / `$0161`
* Z80 `ssg_beep`: `$016A`

For screenshots or audio captures in repeatable tests, pass explicit output paths to MAME so old generated files are not confused with a fresh run. Debugger scripts, trace logs, WAV captures, screenshots, and checkpoint backups are intentionally not kept in this final cleaned test folder.
