Repository navigation
Support PAL - #272
Open
LunarLaurus wants to merge 4 commits into
Open
Support PAL#272LunarLaurus wants to merge 4 commits into
LunarLaurus wants to merge 4 commits into
Conversation
The runtime modelled only an NTSC console: 262 lines per field, $213F bit 4 clear and the NTSC master clock in every coprocessor conversion. snes_other.c already decoded the $FFD9 destination code into a PAL flag that nothing read. snes_loadRom now sets Snes.palTiming from that flag before cart_load, unless snes_set_region_override or SNESRECOMP_REGION=ntsc|pal says otherwise. snes_region.h holds the numbers both regions use. Every timing site reads them through the flag: - the beam walk, the IRQ and line schedulers and the field wrap use 312 lines on PAL; vblank still starts at line 225 - the SLHV latch position and the DMA start alignment use the field's lines - $213F reports bit 4 - the raster journal keys and their validation span the PAL field - the generic and beam frame drivers run one PAL field per frame - Cx4 and DSP-1 convert master clocks at 21281370 Hz on PAL - the SA-1 H/V timer counts 312 lines on PAL - the interpreter's SPC ratio and its vblank fast-forward use the PAL field kInterpApuPerMaster keeps its name as a macro over the region, because tests/v2/test_region_apu_boundary.py supplies its own value for that name. The flag sits in the padding before beamMasterLast, so no offset in the savestate blob moves. A zeroed Snes, Sa1, Cx4 or Dsp1 stays NTSC. NTSC behaviour and savestates are therefore unchanged.
The APU frame clock counted every frame as 357368 master clocks and 17088 SPC cycles. A PAL frame is one 312-line field of 425568 master clocks. At the runtime's nominal 50 Hz it holds 20505.6 SPC cycles, which RtlApuFramePeriod carries as a fraction. The NTSC period keeps the old arithmetic exactly, since its denominator is 1. The opt-in master-clock APU timeline divides by the PAL master clock (21281370 Hz) on PAL; NTSC keeps 21477270. The desktop host paces simulation at 50.0070 Hz when the loaded ROM is PAL and the port left simulation_hz at 0. --region=auto|ntsc|pal overrides the header for one run.
LunarLaurus
marked this pull request as ready for review
November 12, 2026 08:49
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
snesrecomp has always modelled an NTSC console. PAL cartridges therefore ran at the NTSC field rate, the PPU reported the wrong region in
$213Fbit 4, and the APU, host pacing, beam timing and coprocessor timers all used NTSC periods. PAL games ran too fast and any game that checked the console region saw the wrong machine.What changed
$FFD9destination code duringsnes_loadRom. PAL codes ($02-$0Cand$11) select PAL timing; everything else stays NTSC.--region=auto|ntsc|palandSNESRECOMP_REGION=auto|ntsc|pal.autoremains the default and follows the header. The override is applied beforecart_load, so coprocessors created during load see the selected region.$213Fbit 4 (0 = NTSC,1 = PAL).palTimingout of the savestate tail padding. Existing savestates keep their layout; a zeroedSnesis still NTSC, so existing code and tests behave as before.The PAL model deliberately keeps every scanline at 1364 clocks and keeps the progressive line count for interlaced fields. It does not model the one short NTSC line or the one long PAL line; this matches the existing NTSC approximation.
Tests
tests/region/region_header_test.ccovers Japan/USA NTSC headers, all PAL destination codes, theSNESRECOMP_REGIONoverride, precedence, and thatcart_loadsees the selected region.tests/host/host_args_test.ccovers--region=auto|ntsc|palparsing and rejection.tests/dma/beam_events_test.c,tests/ppu/ppu_raster_journal_test.c,tests/runtime_dispatch/apu_port_guest_time_test.c,tests/sa1/timer_span_test.c, andtests/test_audio_producer_pacing.py.tests/run_c_tests.shruns the new region test.Validation
--region=pal, and the original forced to PAL withSNESRECOMP_REGION=pal, compared against snes9x.80a41241): all passed. Jobs: unit 49657, suite (base) 49658, suite (pal) 49659.The fork PR has no GitHub Actions checks of its own; the validation above runs on the self-hosted Trampoline workflow.
Review focus
$FFD9mapping and theauto/override precedence.