Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions cores/taitox/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# JTSUPERMAN — Superman (Taito 1988) FPGA core

By Jose Tejada (aka jotego — @topapate)

You can show your appreciation through
* [Patreon](https://patreon.com/jotego)
* [Paypal](https://paypal.me/topapate)

## Hardware

This core targets the **Taito X System** PCB used by Superman in 1988
(Taito P0-039A, ROM-set prefix `b61`). The board was Seta-built and runs:

| Chip | Role | Clock |
|---|---|---|
| Motorola 68000 | Main CPU | 16 MHz / 2 = 8 MHz |
| Zilog Z80 | Sound CPU | 16 MHz / 4 = 4 MHz |
| Yamaha YM2610 | FM + ADPCM-A samples | 16 MHz / 2 = 8 MHz |
| Seta X1-001A / X1-002A | Sprites + tilemap | 16 MHz |
| Seta X1-006 / X1-007 | Palette / DAC | — |
| Seta X1-004 | I/O | — |
| Taito TC0140SYT | 68k↔Z80 sound comms | — |
| Taito C-chip (uPD78C11) | Security + cabinet I/O | 16 MHz / 2 = 8 MHz |

## Supported sets

Only Superman is supported (this core's name reflects that). The cousin
games on the same hardware — Ballbros, Gigandes, Last Striker, Twin Hawk
— may be added in a future re-spin called `taitox`.

| Setname | Notes |
|---|---|
| `superman` | World |
| `supermanu` | US |
| `supermanj` | Japan |

## Status

See `doc/STATUS.md` for current bring-up state, what's verified against
MAME's reference, and the per-block roadmap. **Short version:** 68k +
sound + framework all elaborate and sim cleanly; the C-chip (uPD78C11
MCU) is the remaining major piece blocking a full boot.

## Documentation

The cores have been developed by combining information in the MAME
drivers (mirrored in `doc/taito_x.cpp` and `doc/taitosnd.cpp`) with
public references for the Seta X1-001 chip (`doc/seta_x1-001.md`).
The F2 MiSTer core (GPLv2) was used as a structural reference for the
TC0140SYT — our implementation is a clean rewrite in JT style, not a
verbatim port; see `doc/tc0140syt.sv.ref` for the reference RTL.

## Build & sim

```bash
# Verilator sim from the repo root (requires jotego/simulator Docker image
# and a Superman ROM at ~/.mame/roms/superman.zip):
FRAMES=80 ./sim-core.sh superman superman

# Lint:
docker run --rm --platform linux/amd64 --network host \
-v "$(pwd)":/jtcores jotego/linter \
/jtcores/modules/jtframe/bin/lint-all.sh
```
52 changes: 52 additions & 0 deletions cores/taitox/cfg/files.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
superman:
- get:
- jtsuperman_game.v
- jtsuperman_main.v
- jtsuperman_snd.v
# New, Superman-specific module — TC0140SYT
- jtsuperman_syt.v
# C-chip: real uPD78C11 emulation (Fulvio's work, adapted) +
# game-specific wrapper (shared RAM, ASIC regs, 68k bus glue).
# Currently treated as Superman-internal; promote to a jt78c11
# module under modules/ once a second Taito-C-chip game lands.
- jtsuperman_upd78c11.v
- jtsuperman_cchip.v
# Sprite-position renderer (first cut) — places coloured 16x16 squares
# at each sprite's (X,Y) per X1-001A OBJ-RAM layout, no tile gfx yet.
- jtsuperman_obj_v2.v
# Kiwi-derived sprite engine — used WITH an address translator in
# game.v so its 12-bit LUT addressing maps onto X1-001A's
# OBJ word N / OBJ[0x200+N] split. Provides the line-buffered
# SDRAM-fetch rendering needed to display real sprite tile gfx.
- jtsuperman_obj.v
- jtsuperman_draw.v
jtframe:
- get:
- jtframe_ff.v
- from: clocking
get:
- jtframe_cen48.v
- jtframe_cen24.v
- jtframe_sync.v
- from: ram
get:
- jtframe_ram.v
- jtframe_dual_ram.v
- jtframe_dual_ram16.v
- jtframe_obj_buffer.v
- jtframe_prom.v
- from: video
get:
- jtframe_vtimer.v
- jtframe_blank.v
- from: cpu
get:
- jtframe_z80.yaml
- jtframe_m68k.yaml
jt12:
- get:
- jt12.yaml # pulls the entire jt12 module incl. jt10 (YM2610) wrapper
# jt680x for the Taito C-chip MCU (HD6305) — deferred until cchip lands
# jt680x:
# - get:
# - jt6805.yaml
97 changes: 97 additions & 0 deletions cores/taitox/cfg/macros.def
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
CORENAME=JTSUPERMAN

# Hardware: Taito X System (Seta-built P0-039A, ROM-set prefix b61)
# Reference: MAME taito/taito_x.cpp (mirrored in cores/superman/doc/)
#
# Target: Superman (1988, Taito X System). Region variants superman /
# supermanu / supermanj share the same hardware config. C-chip
# (HD6305-based MCU at 0x900000) is required to boot — implemented in
# jtsuperman_cchip.v using jt6805 + 2 KB IROM (b61_11.m11).
#
# Clocks: 16 MHz master XTAL
# 68000 = 16/2 = 8 MHz
# Z80 = 16/4 = 4 MHz
# YM2610 = 16/2 = 8 MHz
# C-chip = 16/2 = 8 MHz

# Controls — bumped to 3 to support Kyustrkr (Short Pass / Long Pass /
# Shoot). The other family games tie the third slot high so the extra
# button is harmless for them:
# Superman / Gigandes / Ballbros: in jtsuperman_game.v the third
# button slot is wired to joystick1[6] (= JTFRAME-provided B3 input).
# For 1/2-button games, MAME/MRA never reports B3 pressed → slot
# reads high → game's input handler ignores it.
# Kyustrkr: same wire, actually used by the game.
JTFRAME_BUTTONS=3
# Joystick reorder: the MiSTer HPS_IO board_joy1 lower 4 bits arrive in
# RLDU order (bit 0=RIGHT, 1=LEFT, 2=DOWN, 3=UP) — NOT the UDLR order
# the jtframe_joy_reorder source-comment implies as "default". MAME's
# TAITO_JOY_UDLR_2_BUTTONS_START expects UDLR at IN0/IN1 bits 3:0.
#
# Without a JTFRAME_JOY_* macro, joystick1 would arrive scrambled (UP
# press → game sees RIGHT, etc. — confirmed on MiSTer build run58).
# JTFRAME_JOY_RLDU applies a bit-reverse on bits 3:0
# ({joy_in[0], joy_in[1], joy_in[2], joy_in[3]}) which is exactly the
# permutation that maps RLDU → UDLR. Verified empirically against the
# Superman PCB schematic (PA0=UP, PA1=DOWN, PA2=LEFT, PA3=RIGHT).
#
# Previous attempt with JTFRAME_JOY_DURL (commit 290ca52bc, copied
# from kiwi) was the wrong macro for the same problem — DURL applies
# a different permutation than RLDU.
JTFRAME_JOY_RLDU

# Video: 384x240 horizontal raster, xRGB-555 palette
JTFRAME_COLORW=5
JTFRAME_WIDTH=384
JTFRAME_HEIGHT=240

# 48 MHz frame clock — gives clean integer cens for 16/8/4 MHz
JTFRAME_CLK48

# Pixel clock — 8 MHz (= 48 MHz / 6).
# MAME's taito_x.cpp specs a 416×256 grid at 57.43 Hz, implying a real
# PCB pixel rate of 6.118 MHz, which isn't a clean divisor of our 48 MHz.
# The framework only supports PXLCLK 6 or 8. At PXLCLK=6 the H rate
# (6 MHz / 416 = 14.42 kHz) lands well below the 15.625 kHz NTSC target
# that MiSTer's HDMI scaler and analog CRTs expect — produces bad
# vertical stripes on HDMI and CRT refuses to sync.
# Switching to PXLCLK=8 with a 512×272 grid (see jtsuperman_game.v's
# vtimer block) gives an exact 15.625 kHz H rate and 57.46 Hz refresh
# (within 0.05% of MAME), matching the proven pang/cal50 pattern.
# pxl_cen only drives video — internal gameplay timing (m68k, Z80,
# YM2610, C-chip) is unaffected.
JTFRAME_PXLCLK=8

# SDRAM layout — see cfg/mem.yaml. Offsets are placeholders subject to
# revision once ROM region sizes are pinned down per MAME's ROM_START.
JTFRAME_HEADER=16

# Bank 0 : 68k main ROM (max 512 KB) + work RAM (RW)
# Bank 1 : Z80 audio ROM (64 KB) + ADPCM-A samples (~512 KB)
# Bank 2 : Seta X1-001A graphics ROMs (sprite+tilemap, 32-bit, ~2 MB)
# Bank 3 : reserved (C-chip EPROM lives here once enabled)
#
# Bank offsets verified by mame2mra: BA2 must be ≥0x180000 (gfx1 wouldn't
# fit otherwise). CCHIP_START moved to 0x380000 for the same reason.
JTFRAME_BA1_START=0x080000
JTFRAME_BA2_START=0x180000
JTFRAME_BA3_START=0x380000
PCM_START=0x100000
CCHIP_START=0x380000

# debugging
JTFRAME_IOCTL_RD=2048
JTFRAME_STATUS

[mister]
JTFRAME_ARX=4
JTFRAME_ARY=3

[mist]
-JTFRAME_CREDITS
NOCREDITS
JTFRAME_OSD_NOCREDITS

[sidi]
# May not fit on smaller SiDi — leave as build-once and re-evaluate
JTFRAME_SKIP
170 changes: 170 additions & 0 deletions cores/taitox/cfg/mame2mra.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
[global]
author=["jotego"]

[parse]
sourcefile=[ "taito_x.cpp" ]
# Tier-1 Taito X family — TC0140SYT + YM2610 audio. All share the same
# jtsuperman.rbf bitstream; per-game hardware deltas are selected via
# JTFRAME_HEADER bytes (see [header] block below).
#
# Phase A (this commit) includes Superman + Gigandes (2-button games).
# Phase B will add ballbros (1 button) + kyustrkr (3 buttons) once the
# header-driven cchip-vs-direct-input multiplex is verified on MiSTer.
#
# Phase C (future) would cover Daisenpu / Twin Hawk which use PC060HA +
# YM2151 instead — too different to fit in the same bitstream cheaply,
# probably gets a separate core.
skip = { setnames = [
# Phase B complete — Superman + Gigandes + Ballbros + Kyustrkr all
# supported by this bitstream. JTFRAME_BUTTONS bumped to 3
# globally to accommodate Kyustrkr; the 1/2-button cousins
# ignore the extra slot. coin_lockout_inv header bit is set
# for Kyustrkr but the HDL ignores it for now (coin-lockout is
# an output to the coin door, not gameplay-critical).
# Phase C — different sound chips (PC060HA + YM2151). Out of scope
# for this bitstream; future separate core.
"daisenpu",
"twinhawk",
"twinhawku",
]}

[buttons]
names=[
# Superman: schematic-verified on the user's PCB: PA4 = Punch (B1),
# PA5 = Kick (B2). Earlier "Jump" label was wrong.
{ machines=["superman","supermanu","supermanj"], names="Punch,Kick" },
# Gigandes (East Tech vertical shmup): standard shmup 2-button.
{ machines=["gigandes","gigandesa"], names="Shot,Bomb" },
# Ballbros: single-button action game. Button 2 unused.
{ machines=["ballbros"], names="Action,-" },
# Kyustrkr (East Tech soccer): 3 buttons.
{ machines=["kyustrkr"], names="Short Pass,Long Pass,Shoot" },
]

[header]
info="""Byte 0 = per-game hardware-flag bits:
bit 0 = cchip_en (1 only for Superman)
bit 1 = direct_input_en (1 for cousins reading $900xxx directly)
bit 2 = adpcmb_en (1 for Gigandes YM2610 Delta-T)
bit 3 = coin_lockout_inv (1 only for Kyustrkr)
bit 4 = fg_yoff_alt (1 for cousins with X1-001A fg_yoffset=-0x0a;
0 for Superman/Daisenpu with -0x12)
bit 5 = force_flip (1 only for Kyustrkr — MAME's GAME() entry
is ROT180 because the PCB targets a
180°-rotated cabinet; the 68k writes
ctrl[0] bit 6 = 1 to pre-mirror its
output, and MAME's ROT180 cancels it.
We have no ROT180 path, so the HDL XORs
ctrl[0] bit 6 with this bit to match.)
bits 6..7 = reserved
Bytes 1..15 reserved for future use.
Decoded in jtsuperman_game.v on header-phase prog_we."""
data=[
{ setnames=["superman","supermanu","supermanj"], data="01" },
# Gigandes: direct_input + adpcmb = 0x02 | 0x04 = 0x06.
# fg_yoff_alt (bit 4) was tried but the K=8 path it selects looks
# WORSE on hardware than K=248 — the kiwi engine's actual residual
# is something other than the +24 lines I derived from MAME's
# geometry. Reverting to 0x06 until we have schematic confirmation
# of the chip's real Y-coordinate behaviour.
{ setnames=["gigandes","gigandesa"], data="06" },
# Ballbros: same flags as Gigandes (direct_input + adpcmb), no
# C-chip, no inverted coin-lockout, no fg_yoff_alt. Gfx ROM byte
# ordering handled by the per-game gfx1 region override (width=32,
# sequence=[1,3,0,2]) below.
{ setnames=["ballbros"], data="06" },
# Kyustrkr: direct_input + adpcmb + coin_lockout_inv + force_flip =
# 0x02 | 0x04 | 0x08 | 0x20 = 0x2E. Same gfx-ROM layout as Ballbros
# (4 single-plane ROMs via ROM_LOAD32_BYTE — see the gfx1 region
# override below). force_flip (bit 5) is set because MAME tags this
# game as ROT180: the 68k writes ctrl[0]=0x50 every frame to flip
# the X1-001A's output, MAME's ROT180 game-level rotation cancels
# the flip in software, and the player sees an upright picture.
# We have no ROT180 path on MiSTer, so force_flip=1 inverts the
# chip's flip bit at the wire instead — the user can still toggle
# Flip Screen in DIPs and it will work normally.
{ setnames=["kyustrkr"], data="2E" },
]

[dipsw]
delete=[{names=["Unused","Unknown"]}]

[ROM]
# Macros come from cfg/macros.def. Each region maps to a MAME ROM_REGION.
# C-chip EPROM only present on Superman; mame2mra will skip the cchip
# region for sets that don't define it (gigandes/ballbros/kyustrkr).
#
# ymsnd:adpcmb is present on ballbros / gigandes / gigandesa as Delta-T
# samples — appended to the adpcma slot so the ROM blob stays one stream.
# (jt10 has Delta-T support; we just don't wire it yet, so the samples
# sit unused but at known offsets when we do.)
regions = [
{ name="maincpu", width=16, reverse=true, no_offset=true },
{ name="audiocpu", start="JTFRAME_BA1_START" },
{ name="ymsnd:adpcma", rename="adpcma", start="PCM_START" },
# ymsnd:adpcmb (Gigandes Delta-T samples) intentionally NOT in this
# list for Phase A — adding it would overlap gfx1's SDRAM slot since
# Bank 1 only has 512 KB of headroom and adpcma alone already fills
# it. Gigandes boots fine without voice samples (FM + ADPCM-A still
# play); Phase B will reshape the SDRAM banks to add ADPCM-B space.
#
# Ballbros gfx region: per-game override.
#
# Per MAME taito_x.cpp the 4 gfx ROMs are loaded with
# ROM_LOAD32_BYTE, one ROM = one bit-plane:
# ROM_LOAD32_BYTE("3", 0x000003, ...) // Plane 0
# ROM_LOAD32_BYTE("2", 0x000002, ...) // Plane 1
# ROM_LOAD32_BYTE("1", 0x000001, ...) // Plane 2
# ROM_LOAD32_BYTE("0", 0x000000, ...) // Plane 3
#
# That puts MAME's planes 3,2,1,0 at bytes 0,1,2,3 of each 32-bit
# gfx word — the OPPOSITE of MAME's gfx_layout (which expects
# planes 0,1,2,3 at bytes 0,1,2,3 per `STEP4(0,8)`). The MAME-
# source comments above the ROM_LOAD lines are written in the
# ROM's "logical" plane-number-from-the-PCB-silkscreen sense, not
# the gfxdecode plane sense; the silkscreen numbering happens to
# be reversed. See `jtsuperman_draw.v` `pxl_in` extraction for
# the full byte→plane→palette-bit mapping our HDL expects.
#
# width=32 puts 4 ROMs into a 4-byte interleave (one ROM per
# byte). jtframe's default file order is alphabetical:
# "0","1","2","3" → bytes 0,1,2,3. Combined with the MAME silk
# mapping that's planes 3,2,1,0 at bytes 0..3. Our HDL needs
# MAME-tilelayout planes 0,1,2,3 at bytes 0..3 (which is the
# natural Superman layout that pxl_in was tuned for):
# byte 0 ← plane 0 ← ROM "3" (silkscreen "Plane 0")
# byte 1 ← plane 1 ← ROM "2" (silkscreen "Plane 1")
# byte 2 ← plane 2 ← ROM "1" (silkscreen "Plane 2")
# byte 3 ← plane 3 ← ROM "0" (silkscreen "Plane 3")
#
# That would be sequence=[3,2,1,0]. But we use sequence=[1,3,0,2]
# instead — because the kiwi-derived pxl_in extractor applies
# its OWN bit scramble on top of the tilelayout:
# pxl_in[3] ← byte 2 LSB (= palette bit 3 ← plane 2)
# pxl_in[2] ← byte 0 LSB (= palette bit 2 ← plane 0)
# pxl_in[1] ← byte 3 LSB (= palette bit 1 ← plane 3)
# pxl_in[0] ← byte 1 LSB (= palette bit 0 ← plane 1)
# So to land each MAME plane at the byte position the HDL maps
# to the matching palette bit, the ROM at each byte position
# must be the one whose plane lands "correctly" through the
# scramble. Working backwards from "palette bit must come from
# the plane the 68k expects" gives:
# byte 0 ← ROM "1" (silkscreen plane 2 = pxl_in bit 2)
# byte 1 ← ROM "3" (silkscreen plane 0 = pxl_in bit 0)
# byte 2 ← ROM "0" (silkscreen plane 3 = pxl_in bit 3)
# byte 3 ← ROM "2" (silkscreen plane 1 = pxl_in bit 1)
# sequence=[1,3,0,2] picks indices 1,3,0,2 from the alphabetical
# default ["0","1","2","3"] = ["1","3","0","2"]. Hardware-
# verified on the MiSTer: width=32 alone produced correct SHAPES
# but wrong COLORS (bits in right pixel positions but plane→
# palette-bit scrambled); sequence=[1,3,0,2] fixes the colors.
{ setnames=["ballbros","kyustrkr"], name="gfx1", start="JTFRAME_BA2_START", width=32, no_offset=true, sequence=[1,3,0,2] },
# Superman/Gigandes: 2 plane-pair ROMs loaded via
# ROM_LOAD32_WORD_SWAP. width=16 with 2 ROMs per 16-bit pair.
{ name="gfx1", start="JTFRAME_BA2_START", width=16, no_offset=true },
{ name="cchip:cchip_eprom", rename="cchip", start="CCHIP_START" },
]

order = [
"maincpu", "audiocpu", "adpcma", "gfx1", "cchip"
]
Loading
Loading