WonderSwan Color
on a Tang Nano 20K

The MiSTer WonderSwan core alone needs more logic than this 20K-LUT FPGA has. So its CPU was rebuilt: a microcoded 80186 turned into a V30MZ, about 2.4× smaller, verified on 62 000 instruction tests and timed against real hardware. The board outputs HDMI and has a Pocket-style menu, and the whole build uses open-source tools.

GW2AR-18V30MZ from s80x86HDMI 480p + audio yosys · GHDL · nextpnr · ApiculaGPL-3.0
One Last Lap running on the Tang Nano 20K, captured over HDMI
OPEN POCKET WS

Making it fit

The GW2AR-18 has 20 736 LUTs. The MiSTer core synthesised to about 21 900 before any HDMI, SDRAM or menu logic was added. These are the steps that got it to 75%.

12.1k → 5kLUTs for the CPU: s80x86 with V30MZ behaviour instead of the MiSTer CPU
4.7k → 0.5kLUTs for the sprite unit, rewritten around a line buffer and pixel-identical in simulation
62 000single-instruction tests: NEC V20 hardware suite and lock-step against MiSTer
45 / 46block RAMs used; 32 of them hold the 64 KB WonderSwan Color RAM
timingtest pages on hardware
MiSTer timingtest running on the board: every opcode page matches the timings of a real WonderSwan, except POPA (2 cycles slow)

How the CPU was checked

• NEC V20 hardware tests. SingleStepTests, 200 per opcode form, run in a Verilator bench. They found real s80x86 bugs: CALL FAR with a segment prefix, DAA flags.

• Lock-step against MiSTer. Its V30MZ CPU, converted to Verilog with GHDL, runs the same inputs. Differences were fixed where the V30MZ behaves differently: logic-op AF, MUL ZF, AAM/AAD base 10, PUSH SP.

• Cycle-exact timing. Every instruction is padded to its V30MZ cycle count. The table was calibrated with real-hardware timing tests and handles odd jump targets, free prefixes and REP string ops.

• Open boot stub. 42 bytes replace the copyrighted Bandai IPL.

Features

A handheld-style system around the core, shared with Open Pocket GBC.

🎮

WonderSwan Color & mono

The mode is picked from the cartridge footer or forced in the menu. Games run from SDRAM, with an 8-byte burst cache for code.

📺

HDMI with audio

720×480p. The 224×144 picture is scaled 3× to 672×432, triple-buffered, with stereo audio.

📋

Pocket-style menu

Resume, reset, load game, LCD grid or scanlines, system mode. The game pauses behind the menu.

🕹️

Three input paths

DualShock 2, GPIO buttons and a USB remote from the PC keyboard, which can also run scripted sequences.

💾

Games in SPI flash

Up to 15 ROMs. The included games are the MIT-licensed SwanSong Originals.

🔓

No vendor tools

GHDL, yosys + slang, nextpnr-himbaechel, Apicula, openFPGALoader, Verilator.

Demo

Everything was captured from the board over HDMI, with the buttons pressed over USB by tools/record.py.

Build

The VHDL core goes through GHDL into a Verilog netlist. The CPU and the rest of the system are read by yosys-slang. nextpnr and Apicula produce the bitstream.

# bitstream (~10 min)
./build.sh
./flash.sh flash

# games at 0x200000
python tools/make_games.py build/games.bin \
    games/swansong/one_last_lap.wsc="One Last Lap"
./flash.sh games
# CPU instruction tests (Verilator)
./tools/build_tb.sh
./build/vcpu/VCore.exe build/v20/00.txt

# whole core with a ROM, random SDRAM latency
LAT=6 ./build/vws/Vws_core.exe games/timingtest.ws 40

Documentation

The detailed write-ups live in the repository.