A Sega 32X cartridge music player that replays compact S3M-derived tracker data through an eight-voice software mixer and the 32X stereo PWM FIFOs. It includes an interactive 32X UI, three example tracks, priority sound effects, source conversion, and PicoDrive point-to-point tests.
Compiled ROM:
release/PWMTracker32X-v1.0.32x
Open it in PicoDrive as a 32X cartridge.
| Pad input | Action |
|---|---|
| A | Pause / resume tracker replay |
| B | Trigger priority noise/laser SFX |
| C | Trigger priority square/impact SFX |
| Left / Right | Previous / next bundled track |
| Start | Next bundled track |
| Up / Down | Master volume |
The 68000 sidecar polls pad 1 and writes the active-high pad bitmap into 32X communication RAM. The SH-2 does every part of audio playback and visual output.
The ROM embeds four tracker arrangements:
PWM-Mix8-Demo.s3m— original eight-voice mixer demonstration; the default track. Its first row intentionally starts eight simultaneous notes, proving that the SH-2 is software-mixing eight virtual streams down to the two physical PWM outputs.JazzJackrabbit-Menu.s3m— 4 source channelsJazzJackrabbit-Orbitus.s3m— 4 source channelsJazzJackrabbit-Exoticus.s3m— 4 note-producing source channels (the file declares eight routes, but its note data uses four).
The three Jazz Jackrabbit examples came from the Mirsoft archive URL supplied with the request. Original composition and game music rights remain with their respective copyright holders; see CREDITS.md and assets/source/MIRS0FT-INFO.txt. Do not treat this example ROM as a license to redistribute the tracks separately or commercially.
src/main.c holds exactly eight Voice structures. Tracker row events address channels 1–8; the native renderer maintains independent sample position, loop bounds, pitch, volume and stereo pan per voice. Exoticus exercises all eight source channels.
The player mixes the voices at 11,025 Hz to a left and right 12-bit-ish PWM amplitude. Its only audio output registers are:
0x20004030— PWM control0x20004032— PWM cycle0x20004034— PWM left FIFO0x20004036— PWM right FIFO
It does not use YM2612, PSG, CD PCM, or another audio chip.
The mixer has eight simultaneous slots. B/C SFX have priority and temporarily steal voice 8; they never add a ninth hidden hardware or software channel. The next music note on that lane restores normal tracker ownership. This makes SFX behavior deterministic and keeps the advertised eight-channel budget.
The runtime format (T32XTrack) is intentionally compact and predictable for SH-2 replay. tools/s3m_to_t32x.py converts S3M source at build time and preserves:
- the first eight active S3M channels;
- signed 8-bit mono PCM samples and forward loops;
- order list, patterns, notes, instruments and volumes;
- S3M
Aspeed,Ttempo,Border jump andCpattern-break commands.
Packed/compressed, 16-bit and stereo S3M samples are rejected during conversion rather than being decoded incorrectly. Effects outside the list above are deliberately not replayed. The converter makes it straightforward to replace the bundled examples with rights-cleared modules that fit this supported subset.
- POSIX shell, Python 3, GNU Make, host GCC
- Chilly Willy’s Sega MD/CD/32X DevKit (32XDK release)
- Test dependencies: a PicoDrive libretro core and libopenmpt (the independent S3M reference player)
The released devkit convention is /opt/toolchains/sega. If yours is elsewhere, pass GENDEV explicitly.
# From this directory
make GENDEV=/opt/toolchains/sega -j2 all
make GENDEV=/opt/toolchains/sega packageThe build produces:
build/PWMTracker32X.32x— build artifactrelease/PWMTracker32X-v1.0.32x— user-facing cartridge image
Generated data is checked in, but conversion can be forced after changing source modules:
rm -f assets/generated/generated_tracks.c assets/generated/generated_tracks.h
make GENDEV=/opt/toolchains/sega assets allpython3 tests/test_assets.pyThis validates all three source files as S3M, confirms no source arrangement exceeds the eight-channel replay budget, and verifies deterministic conversion output.
Build PicoDrive’s libretro core (or set PICODRIVE_CORE to an existing picodrive_libretro.so):
# Debian/Ubuntu: independent S3M reference player used by the audio test
sudo apt install libopenmpt-dev
git clone --recurse-submodules https://github.com/notaz/picodrive ../vendor/picodrive
make -C ../vendor/picodrive -f Makefile.libretro -j2
make GENDEV=/opt/toolchains/sega PICODRIVE_CORE=../vendor/picodrive/picodrive_libretro.so testtests/test_picodrive.py is a tiny headless libretro frontend. It first runs the untouched ROM for 160 frames, then runs a 540-frame input sequence. It asserts all of the following:
- PicoDrive loads the 32X ROM and produces at least a 256×224 video frame.
- The final frame contains more than 5% non-black pixels and at least eight RGB565 colors — a hard guard against a black/stalled screen.
- Before any scripted input, it captures six seconds of PicoDrive PCM and separately renders the first six seconds of the unconverted eight-voice
PWM-Mix8-Demo.s3musing libopenmpt/OpenMPT. The test compares a normalized Goertzel tonal fingerprint with ±0.8 seconds of alignment tolerance and checks relative loudness. It rejects silence, weak PWM buzz, wrong sample-address decoding, wrong tracker timing, and a regression that drops virtual voices from the mix. - It injects Right, B, and Start, and verifies that the post-Right framebuffer differs from the boot checkpoint, covering controller polling, track switching and SFX.
The captured visible proof is written to build/test-output/picodrive_p2p.ppm (viewable as a PPM image). The test is intentionally emulator-based rather than relying only on static ROM/header checks.
src/main.c SH-2 player, UI, PWM stream and mixer
src/crt0.s, src/mars.ld 32X startup/link layout derived from 32XDK/Doom 32X conventions
m68k/pad_poller.s Small 68000 controller sidecar
src/tracker.h Compact runtime module ABI
tools/s3m_to_t32x.py S3M -> T32X event/sample converter
tools/create_mix8_s3m.py Original deterministic eight-voice S3M source generator
assets/source/ Provided module sources + archive information
assets/generated/ Checked-in generated C tracker data
tests/test_assets.py Host-side converter/track checks
tests/test_picodrive.py Headless PicoDrive P2P + OpenMPT audio-conformance check
tools/run_picodrive.py Minimal libretro test frontend / PCM capture
tools/openmpt_reference.py ctypes wrapper for independent libopenmpt replay
tools/music_reference_test.py Tolerant PCM loudness + tonal-fingerprint comparator
- The primary SH-2 owns the mixer and display; the secondary SH-2 idles safely.
- PWM is FIFO-fed by polling at 11,025 Hz. This favors a small, robust demo ROM and deterministic emulation over a DMA implementation.
- The startup code does not wait for a full 68000 service framework before launching the primary SH-2; the bundled 68000 code is deliberately only a controller poller. This avoids a boot dependency on unrelated CD/FM service code.
- The UI is a native 256×224 indexed 32X framebuffer, with both frame buffers initialized before PWM starts. It is only redrawn after an explicit pad action: redrawing a 32X framebuffer every VBlank starves the three-entry PWM FIFO and produces a frame-rate buzz instead of music.
- 32X startup conventions and hardware references: Chilly Willy’s 32XDK and the Doom 32X: Resurrection codebase.
- Emulator test target: PicoDrive.
- Example music archive metadata: Mirsoft; original Jazz Jackrabbit composers are listed in CREDITS.md.
0 comments
log in to comment.