Commit Graph
61 Commits
Author SHA1 Message Date
LibretroAdmin ad3ee3c7b1 naomi 2: the machine
A flat NAOMI image whose header says "Naomi2" is run as a NAOMI 2, with
the BIOS from naomi2.zip (or .7z). Until now every such image was taken
for a NAOMI 1, and the BIOS answered "this game is not acceptable by
main board" - which is what upstream flycast does with one as well.

What a NAOMI 2 has that a NAOMI does not:

  - The geometry processor of the last patch, in area 2: its registers
    at 08800000, its command port at 09000000, its 32 MB at 0a000000,
    and at 085f6800 and 085f8000 the holly's and the PowerVR's registers
    written to both PowerVRs at once (core/hw/pvr/elan_host.cpp). What
    it sends goes into the tile accelerator the way a store queue write
    does.

  - A second PowerVR. The two are one here, as in upstream: one set of
    registers, one video memory. What the second has of its own is its
    normal interrupt status, at 025f6900: the end of a list and the end
    of a render are raised in both, and the game clears each.

  - The second PowerVR's video memory is the mirror 32 MB above the
    first's, and games write every texture there too. The texture
    cache did not watch the mirror ("the mirrors don't seem to be
    used"), so a texture changed through it kept its old picture. On a
    NAOMI 2 it is watched.

  - Frames several times the size of anything a NAOMI sends: the tile
    accelerator's buffer and the lists a frame is parsed into are four
    times as big on a NAOMI 2, and a buffer kept from another machine
    is not reused.

Save states: version V20 adds, on a NAOMI 2 only, the second interrupt
status, the chip's state and memory, and the time its texture transfer
ends. Older states load as before.

"Is this a NAOMI" is asked in a dozen places: SYSTEM_IS_NAOMI() answers
for both.

Tested with a flat image of Virtua Fighter 4 and naomi2.7z, in the
RetroArch nightly of 7 October under Xvfb (Mesa llvmpipe):

  - The BIOS starts the game; the attract mode's temple scene and the
    demonstration fight are drawn - letterbox, coin text, the stage, the
    fighters and their shadows - by the OpenGL and the Vulkan renderer,
    per triangle and per pixel, and look alike in all four.
  - Against upstream flycast (which lights in vertex shaders), made to
    run the same image as a NAOMI 2 by a change of three lines: the same
    scenes, the same picture to the eye. The two do not reach a scene on
    the same frame, so they were not compared pixel by pixel.
  - What the chip sends the tile accelerator over 1500 frames is the
    same, byte for byte, from run to run.
  - Threaded rendering on and off: 138 pixels of a 1440x1080 frame
    differ, where two runs with the same setting differ in 54. Those
    are Mesa's: a texture with mirrored repeat comes out differently
    from run to run on llvmpipe, and identically with plain repeat.
  - A state saved, the game run 50 frames on, the state loaded and the
    game run on: 100 frames later its state differs from a straight
    run's in no more bytes than two straight runs differ from each
    other (about 900 of 92 MB).
  - tools/threads/headless.sh, the Dreamcast test disc: passes.

Not done here: the chip's memory is not in the recompiler's direct
mapping, so its accesses go by the handlers; NAOMI 2 games in MAME's
zip form are not in the game table; starting without a BIOS.
2026-10-08 03:50:06 +02:00
LibretroAdmin 74b9d8f8f5 savestate: loading one no longer drops a sound about to start, or restarts CD audio that had stopped
Two things a loaded state did to the sound:

- A game starts sounds in two steps: it sets the key-on bit of each
  channel it wants, then starts them all with one write. Loading a
  state put every channel's envelope back through the function that
  changes its state - which, for a channel that is released, as every
  idle one is, also clears its key-on bit. A state loaded between the
  two steps lost the mark, and the sound never started. The envelope
  is put back without that. Upstream loads states the same way.

- The drive's audio status was read back as "playing or not": a drive
  that was paused, or had played to its end, came back playing. That
  reading is for states from when the status was a flag, and is kept
  for those only.

This is what the live test's "sound, with states saved and loaded"
run has been failing on, now and then and lately every time: it saves
and loads by the clock, and whether a save fell between the disc's
two steps depended on how fast the machine ran.

tools/threads/headless.c can now save a state before one frame and
load it before another (HEADLESS_SAVE, HEADLESS_LOAD), and
headless.sh has a sixth run that does, at fixed frames, with a channel
marked and not yet started: the sound from the load on has to be the
first run's from where the state was saved. On the old code it is not
(the marked channel stays silent); with this all six runs pass, and
the live test passes on gl and glcore.
2026-10-07 17:35:39 +02:00
LibretroAdmin 87a1fbb21a gdrom: a disc put in takes the drive a second to recognise
When the frontend put a disc in, the drive had it at once: ready, of
its kind, with "the medium may have changed" already in its sense
data. A real drive spins the disc up and reads its table of contents
first, and games that have the player change discs wait for what it
says while it does. Upstream gave it a second, and lists Skies of
Arcadia, Shenmue II, Alone in the Dark and Dancing Blade as games
whose disc change that fixed.

So now, as upstream has it:

- for a second after a disc goes in, the drive is busy and its sense
  data says "not ready, becoming ready" (2, 04h/01h); the kind of disc
  it reports is still what it reported with the lid open;
- then it has the disc, and says once "medium may have changed"
  (6, 28h), or 29h if there turned out to be nothing to read;
- unit attention sets CHECK in the status of the commands that follow,
  which upstream changed for D2's disc change;
- opening the lid no longer puts unit attention in the sense data.

A disc that is there when the game starts is ready at once, as before.
The second is in the save state (V19, eight bytes); a reset or the lid
opening again ends it.

The headless frontend can open and shut the lid (HEADLESS_SWAP), and
the headless test gains a fifth run that does. The disc's program
watches the drive every frame: it has to be busy for 55 to 65 frames,
then ready, then answer REQ_ERROR with 6/28h. With the second cut to
nothing the run fails (bad00c01).

Not tried with any of the games named: none is at hand.
2026-10-07 12:49:27 +02:00
LibretroAdmin c308deb2c2 maple: a device's answer arrives when the transfer is over, and takes as long as it does
Read against upstream's, which changed all of this for games:

- The answers were written into the game's memory the moment a
  transfer was started, and the interrupt came later. A game that
  looks before the interrupt found them there already. They are now
  kept until the transfer is over and written then, with the
  interrupt. (Upstream: "Maple dma xfer must not be executed
  immediately. Delay until interrupt is raised.")

- An answer with nowhere to go, its address not in main memory, was
  written to some other address made out of it. It is now not
  written, and the overrun interrupt is raised when the transfer is
  over. (Upstream: fixes Cheats 'N Codes Volume 1.)

- The time a transfer takes was every byte at 2 Mbit/s. Only the
  console sends that fast: devices answer at 724 to 738 kbit/s,
  measured on the wire, and each frame has its start, parity and stop
  as well. Now 2 Mbit/s out and 740 kbit/s back, as upstream has it.
  (Upstream: fixes Silent Scope's memory card load at boot. Its commit
  also notes that Power Drift then only boots with one controller
  connected; that is not looked into here.)

- In the mode where the bus sends the most significant byte first
  (SB_MMSEL 0), the last word of a frame was not byte-swapped on its
  way to the device.

The pending answers are in the save state, which becomes V18 and
16 KB larger; older states load as before. They are dropped on reset.

The disc gains a test: a controller's answer must not be in memory
right after the transfer is started and must be when it is over. With
answers written at once it fails (bad00901).
2026-10-07 12:12:06 +02:00
LibretroAdmin 42edf901d0 sh4: each store queue goes by its own QACR
The SH4 has two store queues, and each has a register (QACR0, QACR1)
naming the area its 32 bytes are sent to. With the MMU off the core
picked one routine for both queues from the area of whichever of the
two registers was written last, and the general routine read QACR0
for either queue. Games nearly always set the two alike, and then it
makes no difference. One that aims a queue at the Tile Accelerator
and the other at main memory had both go where the register written
second said.

Upstream goes by QACR0 for both, which is right for the first queue
and wrong for the second in the same case.

The routine is now picked from both registers: the quick ones for
main memory and for the TA when the two agree, as before, and the
general one when they do not, which now reads the register of the
queue being flushed. It is picked again when a save state is loaded,
from the registers, not from what the state says was in use. A QACR
reads back as its area bits only, as upstream has it.

The live test's disc has a test of it: the second queue aimed at main
memory, then the first at the TA, 32 bytes through the second, which
have to arrive in memory with nothing either side disturbed. If not,
the background is orange and the verdict word says so. Run headless on
the core as it was, the disc reports exactly that (0xBAD00006); with
this, it passes, under both SH4 timings, and the live test passes.
2026-10-07 07:01:25 +02:00
LibretroAdmin 890ee535bf arm7: a recompiler for x86-64
The sound chip's ARM7 was interpreted on x86-64, one instruction at a
time; only ARM hosts had a recompiler for it. This brings in the one
flycast has upstream for x86-64: arm7_rec.cpp turns the instructions
at the ARM's program counter into a block of simple operations, and
arm7_rec_x64.cpp turns the block into machine code. Instructions it
has no code for - block transfers of several registers, multiplies,
anything odd - are run by the interpreter from inside the block.
The recompiler ARM hosts use is the old one, untouched.

On the Soul Calibur state the ARM7 costs 690 cycles a sample this way
against 1049 interpreted, a third less. The driver's compiled code is
21 KB.

It is not upstream's as it stands. Run against the interpreter on
random programs (tools/arm7, next patch) it stopped on some valid
instructions and got others wrong. Fixed here:

- The two-operand instructions worked on their operands' own
  registers, and for some pairings of result and operand overwrote
  one before using it (SUB and SBC with the result in the second
  operand changed the first operand's register; RSC with the result
  in the first operand subtracted the second from itself). With r15
  as the first operand most could not be assembled at all and threw.
  Both operands are now taken by value before anything is written.
- RSB was done as negate and add, which has another carry.
- A shift by a register took the whole register for the count, where
  it is the low byte: a count of 0x102 was "32 or more".
- A flag-setting logical instruction with a shift by a register left
  the carry alone; so did one with a rotated constant under 256, and
  one with r15 as second operand took its carry from r15. These go
  to the interpreter. The carry of a constant was also looked for in
  the first operand.
- r15 shifted by 32 (LSR #0, ASR #0) was folded as a shift by 0, and
  r15 with a rotate, or with a shift whose carry is wanted, lost the
  shift.
- A block load or store of one register is rewritten as a plain load
  or store, and wrote the base back whether asked to or not; and the
  load turned the word by the address's low bits, as a plain load
  does and a block load does not.
- An instruction after a status register write with the same
  condition was run or skipped on the flags from before the write.
- Filling the code cache threw. It is flushed instead.

With these, the recompiler and the interpreter leave the same
registers, flags and memory behind on 26,391 random programs.

As upstream: code is compiled as it is first reached and kept until
the ARM is reset or a state is loaded, so a driver that rewrote its
own instructions while running would keep running the old ones; and
interrupts are taken between blocks of up to 32 instructions, not
between instructions.

The registers gain a scratch slot after the last, for the recompiler.
It is not counted with them, so a save state is what it was.

Compiled for Windows x64 and, for the parts shared with the old
recompiler, ARM64; run on Linux x86-64 only.
2026-10-06 18:12:44 +02:00
LibretroAdmin 26716b9a8d gdrom: the CDDA sector is lent from the image
The sound chip mixes the sector it is playing straight out of a mapped
track file through TrackFile::Lend(), which lends a sector for the
disc's lifetime; a decoded hunk cannot be lent and is still read into
the AICA's buffer. libCore_CDDA_Detach() copies a lent sector into
that buffer before the disc goes away and around a save state, so a
lid opening mid-sector or a state saved then is safe.

The live test's disc now starts its audio track playing, looping, so
the saves, loads, resets and lid cycles all happen on a lent sector.
2026-10-06 14:52:02 +02:00
LibretroAdmin cf0790a148 palette: convert it on the thread that draws, when it has changed
The palette's conversion to host formats, and the hashes paletted
textures are keyed by, were redone when pal_needs_update said so: a
flag set by the register writes and cleared by palette_update(), which
the emulation thread called when the game started a render.

With Threaded Rendering that is the wrong thread at the wrong time. The
frame before can still be drawing, on the frontend's thread, when the
game starts the next render, and palette_update() rewrote the very
tables that frame's texture decoding was reading. It also converted
from the live registers, which by then may hold the palette of a frame
the renderer is not on yet.

palette_update() is now called by the renderer, for each frame, before
the frame's textures are processed, and reads the copy of the registers
the frame is drawn with; TexCache.cpp reads that copy throughout, as
the renderers do. Whether the tables are stale is decided by comparing
the 4 KB of palette and the control register with what was converted
last, so there is no flag for two threads to share, and a palette
written back unchanged costs no conversion and no upload.
pal_needs_update is gone.

The live test's disc now draws its background with an 8-bit paletted
texture and, after the texture's last repaint, changes the palette
entry it uses to yellow: the yellow screen takes the texture write and
the palette write both. With the conversion taken out the screen is
black and the test fails.
2026-10-06 04:15:03 +02:00
LibretroAdmin 0ac9b1f99d fog: upload the fog table when it has changed, not when a flag says so
Whether to upload the fog table was decided by fog_needs_update, set by
the register write on the emulation thread and cleared by the renderer.
With Threaded Rendering those are two threads, and a frame is rendered
from a copy of the registers taken when the game started it. A table
written after that copy was taken set the flag; the renderer, still on
the earlier frame, uploaded the old table from its copy and cleared the
flag. The new table was then never uploaded, until the game wrote the
table again.

The table is 256 bytes as a texture. The renderers now build those
bytes from the registers the frame is rendered with, every frame, and
upload them when they differ from what the GPU has. There is no flag,
so nothing to lose, and a game that rewrites the same table every frame
no longer has it uploaded every frame. OpenGL, OpenGL per-pixel and
Vulkan all do it this way; fog_needs_update is gone.
2026-10-06 03:44:16 +02:00
LibretroAdmin 581abd98e0 serialize: no function addresses in the save state
register_serialize() wrote data32 out for every register. For one that
is read through a function that field is the low half of the function's
address, which is not state - register_unserialize() reads it into a
dummy - and is different in every process. About 1,300 of them went into
each state, so two states of the same machine at the same moment were
never the same file.

A register read through a function is now written as zero. The format
does not change and older states load as before.

With this and the read-only registers no longer holding an address
either, two runs of the same content for the same number of frames
leave states that differ in the real-time clock and nothing else: 4
places instead of 2,500 bytes. tools/threads/live_same.py now requires
that - more than a few bytes of difference between two identical runs
fails - and still compares the threaded state with the non-threaded one
everywhere but the clock.
2026-10-06 01:29:26 +02:00
libretroadmin 657cd85e4c pvr/spg: preserve raster position across savestate load
retro_unserialize() called CalculateSync() after dc_unserialize(). Besides
recomputing the derived video timing, CalculateSync() also reset
pvr_cur_scanline to 0 and re-armed the vblank scheduler event at
now + Line_Cycles, discarding the raster position and vblank schedule that
had just been restored from the state.

Because the running instance never does this, a saved-then-reloaded run
diverges from an uninterrupted one: the vblank event fires up to one
scanline early right after the load, shifting frame timing and cascading
into a full audio/CPU desync. It is most visible on the reios (HLE BIOS)
path, where the attract loop polls GD-ROM and is sensitive to frame
timing; a real BIOS happened to hide it. This breaks runahead and netplay
determinism when no BIOS is present.

Add a reschedule flag to CalculateSync() (default true, so register-write
and reset callers are unchanged) and pass false on the unserialize path so
only the derived timing is recomputed. Serialize pvr_cur_scanline as well,
bumping the savestate version to V15, so the raster line round-trips.
Pre-V15 states still load and keep the previous reset-to-0 behaviour.
2026-07-06 06:08:52 +02:00
libretroadmin b100ecb0aa determinism: drop wall-clock frame throttle, sync the RTC in savestates
Two host-clock dependencies in the emulation path:

QueueRender() had a wall-clock speed limiter under SynchronousRendering: it
measured emulated-cycles-per-real-second via os_GetSeconds() and waited if the
SH4 was running faster than real time. A libretro core must not throttle itself
against the host clock -- the frontend paces via audio/video sync -- and doing
so is non-deterministic and fights that pacing. Drop the wall-clock measurement;
keep only the render-to-texture wait (a queued RTT frame must finish before the
SH4 reads it back), which is a correctness sync, not a timer.

The AICA RTC increments deterministically off the SH4 scheduler (DreamcastSecond
every SH4_MAIN_CLOCK cycles), but its initial value is seeded from the host
clock and RealTimeClock was never serialized, so two netplay peers -- or a
runahead rollback -- diverged the moment a game read the clock. Serialize it
(V14) so it is synced with the rest of the state. The host-clock seed is kept:
it is the correct behaviour for a real-time clock, and is now deterministic
across the state transfer that netplay and runahead both perform.

Left in place (not emulation timing): log timestamps, the vendored Vulkan
allocator's QueryPerformanceCounter, the dead #if 0 fastmmu benchmark, the SH4
guest-side GetTickCount/QPC address decode, the NAOMI multiboard network thread's
pacing sleep, rumble duration (output-only), and the threaded serialize/reset
handshake timeouts.
2026-07-03 22:23:01 +02:00
libretroadmin 997eddee0a serialize: bound state deserialization against input and dest buffers
retro_unserialize() receives the frontend buffer length but dc_unserialize()
only ever used it to disambiguate two legacy save-state versions, never to
bound the reads. ra_unserialize() did an unconditional memcpy from the input
cursor, so a truncated or malformed state (netplay peer, runahead, corrupt
file) walked straight off the end of the input buffer, and several length
fields taken verbatim from the state were used as memcpy destinations:

  - UnserializeTAContext: 'size' copied into the fixed TA_DATA_SIZE buffer and
    'render_pass_count' used to index the fixed render_passes[10] array.
  - sys_nvmem_sram/flash: stored .size overwrote the live size and drove the
    copy length into the fixed data[] allocation.

Track the input base/limit in dc_unserialize() and reject any read that would
run past the end, latching a failure that propagates to the retro_unserialize()
return value. Clamp the attacker-controlled TA size, render_pass_count, and
nvmem copy lengths to their real destination capacities. Legitimate states
round-trip byte-for-byte unchanged.
2026-07-03 20:16:54 +02:00
twinaphex 5cff0ec4b4 Buildfix 2021-04-15 01:44:41 +02:00
twinaphex 8d8eab8fd5 - Backport https://github.com/flyinghead/flycast/commit/889fc8256d720ae15484c8517537e1b19152bf6d#diff-fc1d651a6c7be7577e1a14b6e860c10115ef82091c1439ca291e945ba55a4220
- Also backport miniupnpc from another commit, necessary for BBA
  emulation
2021-04-14 19:50:23 +02:00
twinaphex de6ccff686 Backport https://github.com/flyinghead/flycast/commit/04cd7db2cd81d516d880e5db4fda608cdf674a18#diff-19595e2c781f23388ecea5069f3af208a3423726fed0278d9c9b72ddadae8c43 2021-04-13 05:34:37 +02:00
twinaphex cb3bd71cc5 Backport https://github.com/flyinghead/flycast/commit/e99aac357567302e1d1daf4b59ec8e4139c879da#diff-7194c04007dd96b8c496118886760725f8eb7b8bda6916d63bf6e3ea967e44a7 2021-04-12 02:32:26 +02:00
barbudreadmon f0bf6ab087 (REND) backport https://github.com/flyinghead/flycast/commit/2191bb626e19f5dc16d82b0c3410ce766e00e7a4 2020-12-27 13:42:21 +01:00
barbudreadmon 2eb66879bb (SH4) backport https://github.com/flyinghead/flycast/commit/74aae115ee594c30dcfc402410018bb00e4ffa77
and some https://github.com/flyinghead/flycast/commit/959b6344b3c12907796c4fccdddc78bc7a03e1d6 serialize leftovers
2020-12-27 12:24:33 +01:00
barbudreadmon fa49de2997 (SH4) backport https://github.com/flyinghead/flycast/commit/333df13fce8a0bc577d6397c384abf86ea5daf24
(SH4) backport https://github.com/flyinghead/flycast/commit/a19c73de7b79b6c504798768a32922994f52c57f
sync soft/hard reset calls with upstream
2020-12-24 13:34:48 +01:00
barbudreadmon b7839962a5 Backport https://github.com/flyinghead/flycast/commit/959b6344b3c12907796c4fccdddc78bc7a03e1d6 2020-11-26 09:41:13 +01:00
barbudreadmon 1777eeacf4 Revert "(GDROM) Backport https://github.com/flyinghead/flycast/commit/959b6344b3c12907796c4fccdddc78bc7a03e1d6"
This reverts commit 5620ec7735.
2020-11-18 18:12:49 +01:00
twinaphex 5620ec7735 (GDROM) Backport https://github.com/flyinghead/flycast/commit/959b6344b3c12907796c4fccdddc78bc7a03e1d6 2020-11-15 20:13:04 +01:00
twinaphex 2700c9e528 Revert "replace Array<> with std::array. savestate: save correct TA ctx"
This reverts commit 811f127cbc.
2020-09-05 19:38:39 +02:00
Flyinghead 811f127cbc replace Array<> with std::array. savestate: save correct TA ctx 2020-07-05 15:39:11 +02:00
Flyinghead 198f0d4897 savestate: save sq_remap used for store queues when mini-mmu is on
Issue #928
2020-07-04 21:02:22 +02:00
Flyinghead ee4ffad9de savestate: Save current TA context. Save AW cart state.
Issue #928
2020-07-03 17:49:12 +02:00
Flyinghead 70f69b8781 various aica/arm/misc fixes
set SB_ADST to 1 when dma is in progress. Fixes Carrier audio glitches
(Issue #927)
move aica and rtc scheduler callbacks to aica
implement internal aica dma
slow mmu: missing instruction tlb protection violation. don't flush mmu
tables when changing state
sh4: sr.RB isn't set to 0 in user mode, simply ignored
various non-functional changes
2020-07-01 17:08:45 +02:00
Flyinghead c0c689186b pvr: reserve more opaque polys. Don't crash on TA overrun 2020-03-26 18:11:40 +01:00
M4xw 4cf6c1f92c [Switch] Initial Port 2020-03-26 18:11:40 +01:00
Flyinghead 0ef6827486 naomi: fix reboot. clean up
fix reboot (and exiting service menu) by disabling legacy dimm board
emulation
clean up. typo in var name
2020-01-24 18:51:14 +01:00
Flyinghead d2d7b0b2d9 build fix 2020-01-07 23:04:27 +01:00
Flyinghead 290fb1eac2 reduce savestates size by 20mb 2020-01-07 19:27:50 +01:00
Flyinghead a172182618 better serial port emulation 2019-12-12 19:26:44 +01:00
Flyinghead 42bb73ece4 pvr: use vclk_div to compute the pixel clock instead of ntsc/vga mode
Fixes NG:DEV TEAM games running too fast (Last Hope and others)
2019-11-05 18:53:23 +01:00
Flyinghead 3b15be26ef aica: use log scale for attack phase
Change AEG starting envelope value from 3ff to 280
Use log scale instead of linear during attack phase
2019-09-10 16:56:11 +02:00
Flyinghead 9a2b6d7f5c aica: implement pitch LFO and LPF envelope
simplify channel serialization
optimize aica channel reg write callbacks
2019-09-09 12:29:22 +02:00
Flyinghead 13f39bd2e4 Detect game region/vga support and set options accordingly
Better IP.BIN parsing (wince, vga)
Use wince flag and boot filename for wince detection
Switch to TV composite if vga unsupported
Switch to supported BIOS region if current is unsupported or default
Add default ISP information to flashrom
2019-09-04 11:03:16 +02:00
Flyinghead 8e7250fc6c proper hard reset of sh4 mm registers
clean up
2019-09-04 09:49:04 +02:00
Flyinghead 58ac148f07 minor changes and clean up 2019-08-31 16:10:52 +02:00
Flyinghead 5352f3d672 naomi: use new logging 2019-08-30 10:14:35 +02:00
Flyinghead 6b9565218b dyna clean up and minor changes 2019-08-29 11:24:31 +02:00
Flyinghead 4a4e414473 reios: serialize hle bios state. Fix savestates compatibility 2019-08-19 16:19:47 +02:00
Flyinghead 707f261502 hle bios improvements 2019-08-13 13:10:14 +02:00
Flyinghead ce1a1106d4 save states: compatibility with master branch 2019-07-12 13:36:48 +02:00
Flyinghead 8f41faa035 rec-arm64: use SSA, generate main loop
Fix xtrct canonical implementation
2019-06-14 13:45:11 +02:00
Flyinghead 8452fb11a2 wince: basic mmu, interpreter support, fpu disable exceptions 2019-05-01 19:35:26 +02:00
Flyinghead 0d448cbb34 Naomi: allow mapping P2 buttons onto P1. Fix coin chute issues.
Mappings for 18 Wheeler, Jambo Safari and Virtual On
2018-12-10 23:23:03 +01:00
Flyinghead 46317445a3 Naomi: support for M1 and M4 carts. BIOS version H supported. 2018-11-06 17:34:21 +01:00
Flyinghead a2214fc773 Auto save state changes
Initialize the emulator in retro_load_game() instead of in its thread or
retro_run(). If init fails, an error is returned to the frontend.
Add more data to save states, V3 introduced.
Initialize modem at init to have a fixed size scheduler list at runtime
Fixed init race conditions when using auto load state.
Don't serialize sh4 scheduler ids. Don't depend on their value for ser/deser.
2018-10-26 11:29:37 +02:00