Compare commits

...
166 Commits
Author SHA1 Message Date
Luke Usher 1d8aa3fc3f ares v127 released
ares v127 brings significant improvements to Mega Drive and Nintendo 64
emulation, as well as improvements to NES / Famicom and SNES / Super Famicom.

Other than the usual emulation improvements, there have been the following notable
changes:

**Apple Silicon Support**

ares v127 fixes the recompiler for aarch64 architecture, meaning that it is now
possible to create Apple Silicon/M1 native builds, without relying on Rosetta
and without losing support for the high performance JIT recompilers.

In order to be Apple Silicon Native, it is currently required to compile ares
from source code as automated builds have not yet been configured, but users
who wish to do so will no longer lose functionality or suffer poor performance
as a result.

**MAME RDP**

ares's Nintendo 64 core uses paraLLEl-RDP by default; this brings fast and
accurate RDP emulation as long as Vulkan is present on the users machine; this
meant that Nintendo 64 emulation was completely broken for all configurations
without Vulkan support, including macOS.

ares v127 adds support for MAME's RDP implementation as a fallback, allowing
Nintendo 64 emulation to be used when Vulkan is not present. This is handled
automatically, however, a new option has been added to video settings to allow
Vulkan support to be toggled, giving all users the ability to test the MAME
RDP, if they wish to do so.

Although MAME RDP is now an option, paraLLEl-RDP is still the recommended
choice, for both performance and accuracy.

**Pixel Accuracy Mode**

ares has contains two implementations of some of our emulated hardware; one
optimised for performance, and another optimised for accuracy.
Historically, the choice of which path to use has never been exposed to the
user; higan *always* used the 'accurate' profiles, with ares always opting for
the 'performance' profiles; any user wishing to change this would be required
to compile ares themselves from source.

As of ares v127, we now provide a new option in the emulator settings: "Pixel
Accuracy"; when this is enabled, any emulator core that supports a pixel
accurate mode will use it.

For 99% of games, the default fast profiles will be sufficient, but enabling
"Pixel Accuracy" allows games that require mid-scanline effects, such as the
infamous "Air Strike Patrol" to function properly.

The following systems are currently support the Pixel Accuracy setting:

* NEC - PC-Engine / TurboGrafx
* Nintendo - Super Famicom / SNES

**Changelog:**
* desktop-ui: hook up pc-engine 6-button pads to virtual pads [Luke Usher]
* desktop-ui: implement frame advance [Luke Usher]
* fc: add bus conflicts to cnrom [encoded-byte]
* fc: check for ram on mmc1 [encoded-byte]
* fc: check if ram exists on mmc3 [encoded-byte]
* fc: clear oam address on each scanline [encoded-byte]
* fc: improve mmc3 irq behavior [encoded-byte]
* fc: improve ppu skipped clock timing [encoded-byte]
* fc: use hkrom for mmc6 [encoded-byte]
* m68000: allow recovery from zero divide [TascoDLX]
* m68000: reimplement DBcc instruction with correct timing[TascoDLX]
* md: A few fixes to SRAM save game [rasky]
* md: correct overscan / output when display is off [TascoDLX]
* md: correct reads of CRAM and VSRAM [rasky]
* md: detect region 'K' as NTSC-J [invertego]
* md: fix APU port in [rasky]
* md: fix debug register sprite masking [rasky]
* md: fix high bits in control port read [rasky]
* md: fix misaligned reads from VRAM [rasky]
* md: fix register masked write in mode5 [rasky]
* md: fix vblank bit toggling horizontal timing [rasky]
* md: fix VSRAM out of bound accesses [rasky]
* md: ignore erroneous device string used by Codemasters [invertego]
* md: implement undocumented VDP VRAM 8-bit reading mode [rasky]
* md: persist VDP state on reset [invertego]
* md: restore vdp free slot lost to refresh [TascoDLX]
* mia: Correct save type for Premier Manager 64 (N64) [sp1187]
* mia: Correct save type for Transformers: Beast Wars Transmetals (N64) [sp1187]
* mia: correct type for pak attribute [encoded-byte]
* mia: fix 32x sram [Luke Usher]
* mia: properly pass MD eeprom details to ares [Luke Usher]
* mia: updated famicom database [encoded-byte]
* mos6502: add illegal nops [encoded-byte]
* ms: correct overscan inc. dynamic screen resizing [TascoDLX]
* n64: add MAME RDP as a fallback for parallel-RDP [invertego]
* n64: allow vulkan to be disabled [Luke Usher]
* n64: change PI DMA to use 16 bit fetches [CasualPokePlayer]
* n64: fix mult/div opcode timings [rasky]
* n64: fix RSP halt condition to be more accurate [rasky]
* n64: fix several RDP regressions [invertego]
* n64: fix small bug in VMACQ [rasky]
* n64: fix SRA/SRAV opcodes [rasky]
* n64: fix vulkan detection [Luke Usher]
* n64: improve rsp recompiler pool allocation [invertego]
* n64: swap RSP/RDP order [CasualPokePlayer]
* n64: templatize rsp vpu [invertego]
* n64: vulkan tweaks [Luke Usher]
* nall: fix many compilation warnings on macOS [Luke Usher]
* nall: fix page protection on Apple silicon [invertego]
* nall: rewrite recompiler for machine-independence using sljit [invertego]
* pce: runtime pixel accurate VDP setting [invertego]
* sfc: fix horizontal off-screen test for sprites [jbo-85]
* sfc: fix missing sprite tile on Super Conflict title screen [jbo-85]
* sfc: fix missing sprites in Jurassic Park that are partly offscreen [jbo-85]
* sfc: runtime pixel accurate PPU setting [invertego]
* sh2: move registers into POD struct [invertego]
2022-03-03 11:12:11 +00:00
invertego 7692f102e5 pce: runtime pixel accurate VDP setting 2022-03-03 09:31:59 +00:00
invertego 3be1ced3ef sfc: runtime pixel accurate PPU setting 2022-03-02 09:59:35 +00:00
Luke Usher c78d931e28 Revert "ares: remove performance renderers"
This reverts commit dc5ff13b5c.
2022-02-26 21:59:51 +00:00
TascoDLX e7f24c06d5 m68000: reimplement DBcc instruction with correct timing 2022-02-26 12:13:56 +00:00
Luke Usher dc5ff13b5c ares: remove performance renderers
Some systems in ares contained two implementations of video hardware;
these systems were Mega Drive / Genesis, Super Famicom / SNES, and
PC-Engine / TurboGrafx 16.

For the Mega Drive, we switched to the accuracy core quite some time ago,
SNES and PC-Engine we were still using the performance core.

Going forward, we wish to only maintain the accuracy focused renderers.
2022-02-23 13:04:45 +00:00
Luke Usher 7140b64b13 nall: fix many compilation warnings on macOS 2022-02-23 13:00:45 +00:00
Giovanni Bajo 4d4d3308b9 md: fix debug register sprite masking
VDP always writes transparent pixels to the internal sprite buffers,
overwriting previous transparent pixels; then the first non-transparent
pixel sticks and wins. This implementation detail only reveals itself
when sprite plane masking is activated using the VDP debug register:
in that case, the palette number of transparent pixels concurs in the
AND of the visible pixels, and thus the correct palette number should
be found in the buffer.

Fixes the logo effect of Overdrive 2 (ref #255)
2022-02-23 08:50:24 +00:00
Giovanni Bajo e02a7cc6fb md: fix APU port in
It's technically unused, but it must return 0xFF. Looks like some
game erroneously read it anyway.

Fixes #286
2022-02-23 08:50:13 +00:00
Giovanni Bajo ce981795f5 md: second attempt at vblank bit timing
After more analysis, it looks like the correct thing to do is to
check for vblank status on most of the raster line. Kabuto's doc says
that it is possibly to switch from V30 to V28 during line 224 and
still see the vblank triggered, which suggests that the hardware does
actually check it on most of the time.

This seems to fix #402 and does not seem to cause any immediate
regression in timing-sensitive ROMs.
2022-02-23 08:50:00 +00:00
Giovanni Bajo 51d16a58cd md: fix vblank bit toggling horizontal timing
Currently, the vblank bit is turned on/off at the same time vcounter
is incremented, which is a reasonable assumption, missing any real
data on this.

But it turns out this is responsible for most of the breakage of
Overdrive 1. I did some tests and it looks like anything in the
range >= 0xF0 (same line), or <= 0x80 (on next line) fixes those bugs.
I decided to bind it to vedge(), which means that vblank bit now is
triggered the same instant that VINT is raised, which also makes sense
when you think of it.

Updates #212
2022-02-22 11:05:19 +00:00
TascoDLX 89287dabf1 ms: correct overscan inc. dynamic screen resizing 2022-02-21 08:17:21 +00:00
TascoDLX 5b52f94aa1 md: correct overscan / output when display is off 2022-02-21 08:17:21 +00:00
Luke Usher f67373732c mia: properly pass MD eeprom details to ares 2022-02-21 08:00:10 +00:00
Luke Usher 8d43e67b96 fix eeprom after PR #357 2022-02-20 20:32:44 +00:00
Luke Usher 8a37b00fef mia: fix 32x sram 2022-02-20 20:20:49 +00:00
Luke Usher 80c2678930 desktop-ui: hook up pc-engine 6-button pads to virtual pads 2022-02-18 13:46:07 +00:00
TascoDLX 510cc55d76 m68000: allow recovery from zero divide 2022-02-18 09:15:28 +00:00
TascoDLX 5c285e3ce3 md: restore vdp free slot lost to refresh 2022-02-18 09:15:28 +00:00
Giovanni Bajo 21203b2401 md: fix misaligned reads from VRAM
VRAM misaligned addresses were implemented with partial slot reads.
This seems to be wrong, as proven by vdpfifotesting: once the slot is
read, the whole word is invalidated.
2022-02-17 19:04:06 +00:00
Giovanni Bajo 2c8f3ef84d md: implement undocumented VDP VRAM 8-bit reading mode
Using target 0xC, it is possible to do 8-bit reads from VRAM.
2022-02-17 19:04:06 +00:00
Giovanni Bajo 80348ae691 md: correct reads of CRAM and VSRAM
Reading unmapped bits in CRAM and VSRAM returns a copy of the same bits
in the top slot of the FIFO.
2022-02-17 19:04:06 +00:00
Giovanni Bajo 5e55f1a8a7 md: fix VSRAM out of bound accesses
VSRAM is actually made of 40 11-bit words, though the chip only accesses
the first 10 bits. The top bit on each word is still readable and writable
though, so they should be emulated.

Also, reading at addresses >= 40 returns a copy of the first word,
rather than 0.
2022-02-17 19:04:06 +00:00
Giovanni Bajo c63257029e md: fix register masked write in mode5
Mode5 (SMS compatibility mode) is unsupported and mostly undocumented,
but the vdpfifotesting ROM at least checks one know behavior: non-SMS
registers can't be written.
2022-02-17 19:04:06 +00:00
Giovanni Bajo 93d44072da md: fix high bits in control port read
Bits 10-15 in control port read (aka status register) are not fixed
as found in some online docs, but they are actually an open bus
that returns the instruction prefetched by m68k at that cycle.
2022-02-17 19:04:06 +00:00
Luke Usher 2cf921be41 desktop-ui: implement frame advance 2022-02-16 22:35:17 +00:00
Luke Usher cc8ccbf1bd point to the new ares website 2022-02-16 15:34:01 +00:00
Giovanni Bajo 10e589672f [MD] A few fixes to SRAM save game
* Allow MIA to declare address for mapping (typically from ROM header)
   rather than hardcoding 0x200000.
 * Autoenable SRAM if the ROM size is smaller. Also make the ramEnable
   I/O conditional to the same layout.
 * Add MIA entries for NBA Live 98, NHL 96 and NHL 98, which have a 8-bit
   SRAM not declared in the header.
2022-02-16 10:27:10 +00:00
jbo-85 3c28ecd8b6 sfc: fix missing sprites in Jurassic Park that are partly offscreen 2022-02-14 07:26:10 +00:00
jbo-85 cc64eb50c0 sfc: fix missing sprite tile on Super Conflict title screen; sprites that start on x==256 (offscreen) are considered for the range-tile-over checks 2022-02-14 07:26:10 +00:00
jbo-85 5cb877b49c sfc: fix horizontal off-screen test for sprites; fixes SNES test card Burn-In test, maybe others 2022-02-14 07:26:10 +00:00
CasualPokePlayer 275ba735de change PI DMA to use 16 bit fetches
Using the read<Byte> currently in use breaks DMAs from Flash (breaking Paper Mario, probably others), which always return 0. I assume 8 bit reads are just not allowed for Flash, although I can't verify this myself.
Checking https://github.com/n64dev/cen64/commit/8367698e20cca7eb262779f19c8c7847ae31bb29, it seems that fetches should always be 16 bits anyways, so I presume this is the correct behavior, and fixes DMAs from Flash.
No regressions with the original testrom have occured.
2022-02-12 10:15:37 +00:00
CasualPokePlayer 2b894add40 Swap RSP/RDP order, fixes #348 2022-02-11 12:13:33 +00:00
Luke Usher 58690cd5fc n64: fix vulkan detection 2022-02-09 07:22:20 +00:00
Luke Usher b03da384d8 n64: vulkan tweaks
- introduce a status api, so cores can report status messages to the UI
- avoid calling vulkan-related functions when vulkan is disabled
- show status messages so the user is aware of what RDP implementation is active
- Disabled 'loaded' status message, it prevented the Vulkan messages being shown
2022-02-08 20:34:05 +00:00
invertego f382a18d10 libco: update Windows thread information block
Update the stack base and limit in the thread information block. This is
required to support kernel stack unwinding, which is used for both C++
exception dispatch and setjmp/longjmp on Windows x64 and ARM64.

Add a cothread entry routine that consistently aborts when the user
supplied routine returns. Debugger stack backtraces now stop
consistently at this entry routine.
2022-02-07 08:56:29 +00:00
invertego f63cad6de9 md: ignore erroneous device string used by Codemasters 2022-02-06 16:09:57 +00:00
invertego 34d2c091c7 md: detect region 'K' as NTSC-J
The Korean release of Tiny Toon Adventures specifies its region with a
'K' and, like several other Korean releases, is region locked to
Japanese consoles. It may be the only one with a 'K' specifier, though.
2022-02-03 12:36:32 +00:00
invertego d845582d4a md: persist VDP state on reset
The game Skitchin' performs a fade out effect after reset with the
expectation that VDP state was persisted across the reset.
2022-02-02 09:55:28 +00:00
encoded-byte 9788752bc1 fc: check for ram on mmc1 2022-01-26 20:15:20 +00:00
encoded-byte 5f60ab2dde mia: updated fc database 2022-01-26 20:15:20 +00:00
encoded-byte 628432e75c fc: clear oam address on each scanline 2022-01-26 20:15:20 +00:00
Luke Usher 980e9ed687 n64: allow vulkan to be disabled 2022-01-19 19:52:19 +00:00
Giovanni Bajo e98a313ed6 n64: fix mult/div opcode timings
Taken from NEC VR4300 manual. These are easier than expected to
implement accurately as the CPU doesn't execute them in parallel but
stalls the entire pipeline while they are running.
2022-01-18 09:14:56 +00:00
Giovanni Bajo 90d42d1ea3 n64: fix SRA/SRAV opcodes
These two opcodes were incorrectly ignoring the higher part of the
register before shifting.
2022-01-18 08:02:23 +00:00
Giovanni Bajo 11d6b7c673 rsp: fix small bug in VMACQ
Real hardware testing showed that VMACQ does not modify the accumulator
if it is exactly zero. Fix it.

Also remove two "unconfirmed" comments which I have personally verified.
2022-01-13 08:41:58 +00:00
invertego c04bcf03e0 n64: fix several RDP regressions
- YUV texture fetch and color space conversion
- textured rectangle command parsing
- set primitive depth command parsing
2022-01-12 14:44:39 +00:00
Luke Usher c4d73cc427 Update split-subtrees.yml
Debugging CI action
2022-01-12 11:26:05 +00:00
Luke Usher 8634c11cff Update split-subtrees.yml 2022-01-12 11:22:37 +00:00
Luke Usher 8a5c48e3ed add split-subtrees github action 2022-01-12 11:18:27 +00:00
encoded-byte b22513a6e8 fc: check if ram exists on mmc3 2022-01-05 23:24:10 +00:00
Giovanni Bajo e5220e8fd0 n64: fix RSP halt condition to be more accurate
* Fix usage of break opcode in a delay slot. After unhalt, the
   RSP continues execution, taking the jump if needed. So the
   branch state should not be affected by the break opcode.
 * Reading from SP_PC produces garbage (open bus?) when the RSP is
   running. This fix is unlikely to fix game but may help homebrew
   developers.
 * Writing to SP_PC resets the RSP MIPS pipeline, so any pending branch
   is canceled.
2022-01-03 12:21:45 +00:00
encoded-byte 8099bf54fd mia: updated famicom database 2022-01-01 20:13:57 +00:00
encoded-byte bed4ce3659 fc: use hkrom for mmc6 2022-01-01 20:13:57 +00:00
encoded-byte f630a9418d fc: improve mmc3 irq behavior 2022-01-01 20:13:57 +00:00
invertego f9e4070b15 n64: improve rsp recompiler pool allocation
The existing implementation was allocating pools in the code cache only
to copy them to the heap. This was wasteful on multiple counts: the
space in the cache would go unused until the next allocator flush and
the data copied to the heap consisted almost entirely of zeroes. Best of
all, writing the hash code to the code cache was triggering a page
protection fault on Apple M1.

A small data structure refactor avoids the heap allocations, the copies,
the wasted space in the code cache, and the page protection issue.
2021-12-20 18:16:01 +00:00
invertego 5050d6c45d n64: add MAME RDP as a fallback for parallel-RDP
For systems that do not meet parallel-RDP's Vulkan requirements, use the
pure software RDP implementation from MAME as a fallback.

All of the source files taken from MAME are either BSD-3-Clause licensed
or public domain. Edits were kept to a minimum and guarded by ifdefs to
ease future integrations.
2021-12-20 18:16:01 +00:00
invertego b56dac15e9 recompiler: fix page protection on Apple silicon
- when dynamically allocating executable memory, use VirtualAlloc/mmap()
  for finer control over allocation flags (like MAP_JIT on macOS/arm64)
- toggle JIT memory protection around writes on macOS/arm64
- reduce recompiler code cache sizes to improve likelihood of dynamic
  allocations being close to ares image (affects branch perf)
- remove arch x86_64 override on macOS non-local builds; native arm64
  builds should now be at feature parity with x86_64
- sh2: prevent pollution of code cache with empty pools by invalidate()
2021-12-15 20:01:40 +00:00
invertego a8e35d0299 recompiler: rewrite for machine-independence 2021-12-14 09:02:52 +00:00
invertego 1462bac4c5 sljit: wire up to the build 2021-12-14 09:02:52 +00:00
invertego 5e2a8f08bf n64: templatize rsp vpu 2021-12-14 09:02:52 +00:00
invertego e88cb48f19 sh2: move registers into POD struct 2021-12-14 09:02:52 +00:00
invertego 70a669287b build: arm64 build fixes 2021-12-14 09:02:52 +00:00
invertego 9d3addc241 mia: fix build break 2021-12-14 09:02:52 +00:00
Luke Usher 4542ac5674 Merge commit 'f22e1405dc12ed4fdc05a3f6a9e5fa7ee966f627' as 'thirdparty/sljit' 2021-12-11 19:43:23 +00:00
Luke Usher f22e1405dc Squashed 'thirdparty/sljit/' content from commit d6a0fa61e
git-subtree-dir: thirdparty/sljit
git-subtree-split: d6a0fa61e09266ad2e36d8ccd56f775e37b749e9
2021-12-11 19:43:23 +00:00
encoded-byte 5c3ac88547 mia: updated famicom database 2021-12-10 15:55:35 +00:00
encoded-byte 85fcda5b42 mia: correct type for pak attribute 2021-12-10 15:55:35 +00:00
encoded-byte f512fdc969 fc: add bus conflicts to cnrom 2021-12-10 15:55:35 +00:00
encoded-byte 64286fbd46 fc: improve ppu skipped clock timing 2021-12-06 16:54:50 +00:00
Simon Eriksson e0c4cc76f2 mia: Correct save type for Transformers: Beast Wars Transmetals (N64) 2021-12-03 21:59:50 +00:00
encoded-byte 7b4a346336 component: add illegal nops to mos6502 cpu 2021-12-02 21:49:57 +00:00
Simon Eriksson 763f7da939 mia: Correct save type for Premier Manager 64 (N64) 2021-12-01 20:05:09 +00:00
Luke Usher ee188dcb31 ares: bump version number to v126 2021-11-28 08:00:14 +00:00
encoded-byte 1f8b3f86b9 fc: partial support for bandai datach 2021-11-27 14:30:51 +00:00
encoded-byte 18e334a6cf fc: support for bandai oeka board 2021-11-27 14:30:51 +00:00
encoded-byte 24675b0161 fc: fix saving on bandai lz93d50 + m24c02 2021-11-25 21:33:15 +00:00
encoded-byte f4caedcd5e component: fix eeprom m24c mode selection 2021-11-25 21:33:15 +00:00
encoded-byte bdc6313e46 fc: support for bandai karaoke board 2021-11-25 09:52:14 +00:00
encoded-byte fd3a56b0a2 fc: support for cprom board 2021-11-25 09:52:14 +00:00
encoded-byte 663b6951e3 fc: attach sunsoft 4 + ext rom 2021-11-25 09:52:14 +00:00
encoded-byte 554430276c fc: support for namco 163 board (no sound) 2021-11-22 20:47:23 +00:00
Luke Usher 3748baf8e8 Revert "libco: disable backing up SSE registers on Win64"
This reverts commit 32690bb6f0.

Skipping restoration of SSE registers turned out to be too fragile/risky
2021-11-22 15:11:28 +00:00
encoded-byte e1bd1bfdf9 fc: support txrom variants 2021-11-22 09:06:54 +00:00
encoded-byte 5f6b75a013 fc: support for cnrom with security 2021-11-22 09:06:54 +00:00
encoded-byte 2d6fe4aa16 fc: support for sxrom boards with mmc1a 2021-11-22 09:06:54 +00:00
encoded-byte b49e5496da fc: support for unrom variants 2021-11-22 09:06:54 +00:00
encoded-byte 645d481493 fc: support for bandai lz93d50 variant 2021-11-22 09:06:54 +00:00
Simon Eriksson bc14c35cb5 fc: Support for Action 52 board 2021-11-18 06:40:36 +00:00
encoded-byte 16210cf1bf fc: split bandai fcg / lz93d50 boards
To better describe chips and prepare support for other related boards
2021-11-17 09:04:23 +00:00
encoded-byte d34e54c808 fc: support for bandai 74161 boards 2021-11-17 09:04:23 +00:00
encoded-byte 76508d02bd mia: updated fc database
Games tested so far. Two new not working games discovered:
- Aladdin (Europe): Hangs after starting game
- Wizards & Warriors (Europe): Graphicals glitches on HUD, severe slowdowns. PAL timing related?
2021-11-17 09:04:23 +00:00
Luke Usher 32690bb6f0 libco: disable backing up SSE registers on Win64
For some users, this gives a dramatic performance boost, and finally
brings the Windows builds in-line with Linux/Mac builds in terms of
performance.

This technically goes against the Win64 ABI, which demands that SSE
registers are saved, however, in-practice, our testing has shown that
this has not caused any issues so far.
2021-11-16 11:10:41 +00:00
Simon Eriksson 9eca885d8b fc: Support for Color Dreams board 2021-11-16 06:28:36 +00:00
Luke Usher f9a53d2167 msx: remove rom debug print 2021-11-12 18:28:41 +00:00
Luke Usher fd5b22c5cd msx: use the slot layout and memory expected by cbios 2021-11-12 18:23:21 +00:00
Luke Usher 2f6edc7efe msx: add huristics for rom type detection 2021-11-12 18:22:48 +00:00
invertego 12ac1b5534 hiro: monitor activate messages for window focus
Monitor WM_ACTIVATE instead of WM_SETFOCUS/KILLFOCUS messages to avoid
responding to spurious notifications.
2021-11-11 09:09:50 +00:00
encoded-byte c8fa67ec39 mia: updated fc database
All entries tested and working (except for "Akira")
2021-11-10 22:17:27 +00:00
encoded-byte 5341d0d7d4 fc: support for namco 118 board 2021-11-10 22:17:27 +00:00
encoded-byte ad0105de25 fc: support for namco 175/340 boards 2021-11-10 22:17:27 +00:00
Luke Usher e3663a9eec bump version to v125.1 2021-11-10 21:34:06 +00:00
invertego a01ccb72f2 hiro: track window focus via messages, not polling
On Windows, track WM_SETFOCUS and WM_KILLFOCUS messages to determine
focused status rather than polling with GetForegroundWindow(). The
latter involves a syscall and causes noticeable slowdown when called
repeatedly, such as when GameBoy games poll joypad status in a loop.
2021-11-10 21:32:41 +00:00
Luke Usher 27996d81d4 fc: implement gtrom mapper 2021-11-10 21:01:42 +00:00
invertego 47af07c507 ms, sfc: fix light gun related crashes
Fix unloading systems with light gun peripherals active. The code
responsible for removing crosshair sprites was dereferencing a null
pointer. This cleanup now occurs in Screen::quit() when unloading.
2021-11-10 18:31:51 +00:00
invertego cc6ab702c0 mia: prefer known rom extensions in zip archives
Instead of just reading the first file from a zip archive, prefer files
that match known rom extensions. Extra files such as readmes should not
interfere with the loading of the game.
2021-11-10 11:08:33 +00:00
Luke Usher d633b92a57 v125 2021-11-09 20:20:53 +00:00
Luke Usher 579be2bb04 ms: pass "set VDPbuffer" tests in VDPTEST 2021-11-09 11:45:19 +00:00
Luke Usher 89db771c73 gg: add Mickey Mouse no Castle Illusion (Japan) to game database 2021-11-09 11:00:35 +00:00
invertego 33b95510cb sh2: fix several instructions, default to recompiler
Fix several correctness bugs in SH2 instructions in both the interpreter and
recompiler and switch the default implementation from the cached interpreter to
the recompiler. This brings the 32x games BC Racers and Zaxxon's
Motherbase 2000 to playable status. Fixed instructions:

Interpreter
- ADDC Rm,Rn
- ADDV Rm,Rn

Recompiler
- MAC.W @Rm+,@Rn+

Both
- CMP/STR Rm,Rn
- MOV.B Rm,@-Rn
- MOV.W Rm,@-Rn
- MOV.L Rm,@-Rn
- NEGC Rm,Rn
2021-11-09 08:50:44 +00:00
encoded-byte 31eee5f6f1 fc: pinout support for konami vrc7 2021-11-05 16:25:13 +00:00
encoded-byte 7ba1c2925d mia: add fc konami vrc2/4/6 to database 2021-11-05 16:25:13 +00:00
encoded-byte dbc1b4ccc6 fc: support for konami vrc2 variant 2021-11-05 16:25:13 +00:00
encoded-byte a3a1969775 fc: always check for ram presence
Some games without ram try to read this space, better be safe
2021-11-05 16:25:13 +00:00
encoded-byte ab67ec12cd fc: refactor of jaleco boards 2021-11-03 06:41:59 +00:00
encoded-byte 02cb1268e4 fc: support for jaleco jf13 board 2021-11-03 06:41:59 +00:00
encoded-byte 50994a2c60 fc: support for jaleco fc05 board 2021-11-03 06:41:59 +00:00
encoded-byte 9e099e9b15 fc: remove unneeded test
No need to test rom if board don't support ram
2021-11-03 06:41:59 +00:00
encoded-byte 16cbd8b149 fc: support for jaleco jf17/19 boards 2021-11-03 06:41:59 +00:00
encoded-byte 5491a6a9c8 fc: fix jaleco jf14 banking 2021-11-03 06:41:59 +00:00
encoded-byte d0a14c682d fc: support for irem if12 board 2021-11-01 13:01:30 +00:00
encoded-byte 4eb4b6cdb7 fc: support for irem lrog017 board 2021-11-01 13:01:30 +00:00
encoded-byte 9bc59159d0 fc: check for ram and simplify banking 2021-11-01 13:01:30 +00:00
encoded-byte f518f04ffa mia: database for fc 2021-11-01 13:01:30 +00:00
encoded-byte 2ce06bc4d1 fc: support for irem h3001 board 2021-11-01 13:01:30 +00:00
encoded-byte a3285b6959 fc: support for irem g101 board 2021-11-01 13:01:30 +00:00
jbo_85 8a7ac00d03 snes: Fix 'D-Port B+O+S' test in SPC7110 Check Program as reported in issue #228, this bug was introduced after the huge rewrite to the type system in v117 2021-10-27 08:15:35 +01:00
encoded-byte 38cf1b0f8e fc: fix address 2021-10-26 22:57:57 +01:00
encoded-byte d26ffccc9f fc: add chip type to manifest 2021-10-26 22:57:57 +01:00
encoded-byte 9ee4afd17f fc: support for taito x1-017 board 2021-10-26 22:57:57 +01:00
encoded-byte 61dbb64999 fc: support for taito x1-005 board 2021-10-26 22:57:57 +01:00
encoded-byte 2d5e1ba552 fc: support for taito tc0690 board 2021-10-26 22:57:57 +01:00
encoded-byte a31b0a7768 fc: support for taito tc0190 board 2021-10-26 22:57:57 +01:00
TascoDLX 7c9ff4ba6d md: sram support for Super Hydlide (J) 2021-10-25 09:14:15 +01:00
TascoDLX 0bc30dd26d md: fix interrupt ack special case 2021-10-25 09:14:15 +01:00
TascoDLX f73032e17f mcd: support for 3-button pad (as default) 2021-10-25 09:14:15 +01:00
TascoDLX 2eee9bcd4e mcd: autodetect disc region based on boot code 2021-10-25 09:14:15 +01:00
TascoDLX e3a3927e95 mcd: report error on unhandled cdd command 2021-10-25 09:14:15 +01:00
TascoDLX f04e55f863 mcd: allow continuous cd playback 2021-10-25 09:14:15 +01:00
TascoDLX 08aecccc5a mcd: clean up & fix graphics render 2021-10-25 09:14:15 +01:00
TascoDLX 3471756c06 mcd: reset timer on write 2021-10-25 09:14:15 +01:00
invertego d40e6e1cd3 n64: increase screen canvas to 576 lines
Certain PAL games require this. Affected games include:
- Michael Owen's WLS 2000
- Perfect Dark
- Top Gear Overdrive
2021-10-25 09:13:14 +01:00
encoded-byte 6a262e01ae fc: support for taito tc0690 board 2021-10-25 07:44:40 +01:00
encoded-byte 2f6a51b3ef fc: support for taito tc0190 board 2021-10-25 07:44:40 +01:00
TascoDLX ee3b764b8e ares: bump serializer version for MD changes 2021-10-22 19:16:39 +01:00
TascoDLX 4216d5ae0a md: improved timing/signaling for z80 busreq 2021-10-22 19:16:39 +01:00
TascoDLX 878efe0a05 md: fixed irq issues: timing, delays, ack 2021-10-22 19:16:39 +01:00
Luke Usher 98aee19e93 fc: support expanded bnrom & uxrom
These mappers are used in various homebrew/unlicenced titles
2021-10-21 20:08:23 +01:00
TascoDLX 7810a05b60 md: improved cpu open bus line accuracy 2021-10-21 15:57:15 +01:00
TascoDLX b83e868a6e md: fix z80 bus access & timing issues 2021-10-21 15:57:15 +01:00
TascoDLX ce3153b10e md: prevent sram from masking out high rom 2021-10-21 15:57:15 +01:00
TascoDLX 259642ae2a m68000: fix disassembly of pc-relative addresses 2021-10-21 15:57:15 +01:00
TascoDLX 30921061c7 m68000: restore TAS instruction for cpus other than MD main 2021-10-21 15:57:15 +01:00
TascoDLX 757da94463 m68000: avoid changing interrupt mask on exception 2021-10-21 15:57:15 +01:00
Sintendo 1c026f5edf libco/aarch64: Optimize co_swap_function
Improves thread-switching performance using the following techniques:

- Interleave stores/loads
- Restore the stack pointer and link register as early as possible
2021-10-19 12:15:56 +01:00
Sintendo 2d90b6343d libco/aarch64: Fix vector register handling
The calling convention specifies that d8-d15 (the lower 64 bits of
vector registers q8-q15) are callee-saved. However, libco was
erroneously saving and restoring general-purpose registers x8-x15
instead.
2021-10-19 12:15:56 +01:00
Giovanni Bajo 4c312ab2af Fix linking on old macOS toolchains
Ares JIT recently started to require the data segment to be executable
(see  https://github.com/ares-emulator/ares/pull/200 for details on why
this was done instead of allocating a new segment via mmap).

To do this, it uses mprotect() (bump_allocator::resize), but it also
needs to tell the linker that the data segment can be upgraded to
executable with -Wl,-segprot,__DATA,rwx,rw (nall/GNUmakefile).
Unfortunately, for quite some time, the clang LD had a bug and didn't
accept that option for the data segment.

The official CI has a newer linker without the bug, but for developers
compiling ares with an old toolchain, this small tool will patch the
binary to mark the data segment as potentially executable.
2021-10-19 11:00:23 +01:00
invertego 55d197fdc3 desktop-ui: fix file/folder browser dialog alignment
Align file/folder dialogs to the settings window when appropriate. The
absence of this alignment was triggering asserts in debug builds.
2021-10-19 10:59:51 +01:00
Kawa 53290b9c71 Add screenshot hotkey
As requested in #233
2021-10-17 22:55:49 +01:00
invertego 764a881d36 recompiler: on macOS allow data segment to be executable
Override the data segment's maximum protection from rw to rwx, allowing
mprotect() to be called on recompiler code blocks to make them
executable.
2021-10-17 12:38:14 +01:00
invertego a10e089100 ci: use MSYS2 on Windows, add Windows/Clang build
This change upgrades GCC from the version preinstalled by GitHub Actions
(8.1.0) to the latest MSYS2 version (10.3.0). It also adds a
Windows/Clang build configuration, though the release job still
grabs the GCC build.
2021-10-16 18:59:50 +01:00
TascoDLX 590ec4cb32 Various fixes to CD session handling 2021-10-16 18:59:14 +01:00
TascoDLX 30d143092d Tweak & fix generation of CD subchannel data 2021-10-16 18:59:14 +01:00
TascoDLX 2dbd5a43a0 Improved CD CUEsheet loading to handle PREGAPs & POSTGAPs 2021-10-16 18:59:14 +01:00
Shideravan 08c013dc46 Update README.md 2021-10-15 16:39:36 +01:00
Shideravan 0d169fe641 Adding the ares logo
The resource comes directly from https://ares-emulator.github.io/
This ends the editions in the readme page.
2021-10-15 16:39:36 +01:00
318 changed files with 122339 additions and 3446 deletions

No files matched your search

+34 -10
View File
@@ -11,27 +11,51 @@ jobs:
matrix:
program:
- ares
os:
platform:
- name: windows
version: latest
os: windows-latest
compiler: g++
shell: 'msys2 {0}'
msystem: mingw64
msys-env: x86_64
- name: windows-clang
os: windows-latest
compiler: clang++
shell: 'msys2 {0}'
msystem: clang64
msys-env: clang-x86_64
- name: macos
version: latest
os: macos-latest
compiler: clang++
shell: sh
- name: ubuntu
version: latest
runs-on: ${{ matrix.os.name }}-${{ matrix.os.version }}
os: ubuntu-latest
compiler: g++
shell: sh
name: ${{ matrix.program }}-${{ matrix.platform.name }}
runs-on: ${{ matrix.platform.os }}
defaults:
run:
shell: ${{ matrix.platform.shell }}
steps:
- uses: actions/checkout@v2
- name: Install Dependencies
if: matrix.os.name == 'ubuntu'
- name: Install MSYS2 Dependencies
if: matrix.platform.shell == 'msys2 {0}'
uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.platform.msystem }}
install: make mingw-w64-${{ matrix.platform.msys-env }}-toolchain
- name: Install Linux Dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update -y -qq
sudo apt-get install libsdl2-dev libgtksourceview2.0-dev libgtk2.0-dev libao-dev libopenal-dev
- uses: actions/checkout@v2
- name: Make
run: make -j4 -C desktop-ui build=optimized local=false lto=true
run: make -j4 -C desktop-ui build=optimized local=false lto=true compiler=${{ matrix.platform.compiler }}
- name: Upload
uses: actions/upload-artifact@v2
with:
name: ${{ matrix.program }}-${{ matrix.os.name }}
name: ${{ matrix.program }}-${{ matrix.platform.name }}
path: desktop-ui/out/*
+30
View File
@@ -0,0 +1,30 @@
name: 'Split Subtrees'
on:
push:
branches:
- master
tags:
- '*'
env:
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
jobs:
packages_split:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
package:
- nall
- hiro
- ruby
- libco
steps:
- uses: actions/checkout@v2
- if: "!startsWith(github.ref, 'refs/tags/')"
uses: "symplify/[email protected]"
with:
package_directory: '${{ matrix.package }}'
repository_organization: 'ares-emulator'
repository_name: '${{ matrix.package }}'
user_name: "Luke Usher"
user_email: "[email protected]"
+63
View File
@@ -1,3 +1,4 @@
----------------------------------------------------------------------
ares
Copyright (c) 2004-2021 ares team, Near et al
@@ -13,3 +14,65 @@ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
----------------------------------------------------------------------
----------------------------------------------------------------------
Stack-less Just-In-Time compiler
Copyright Zoltan Herczeg ([email protected]). All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) AND CONTRIBUTORS
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER(S) OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
----------------------------------------------------------------------
MAME
Copyright (c) 1997-2021 MAMEdev and contributors
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
+1 -1
View File
@@ -1,4 +1,4 @@
# ares
![ares logo](https://ares-emulator.github.io/images/logo.png)
[![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://github.com/higan-emu/ares/blob/master/LICENSE)
+5 -4
View File
@@ -1,6 +1,7 @@
#pragma once
#include <libco/libco.h>
#include <sljit.h>
#include <nall/platform.hpp>
#include <nall/adaptive-array.hpp>
@@ -37,16 +38,16 @@ using namespace nall;
namespace ares {
static const string Name = "ares";
static const string Version = "124";
static const string Version = "127";
static const string Copyright = "ares team, Near";
static const string License = "ISC";
static const string LicenseURI = "https://opensource.org/licenses/ISC";
static const string Website = "ares-emulator.github.io";
static const string WebsiteURI = "https://ares-emulator.github.io";
static const string Website = "ares-emu.net";
static const string WebsiteURI = "https://ares-emu.net/";
//incremented only when serialization format changes
static const u32 SerializerSignature = 0x31545342; //"BST1" (little-endian)
static const string SerializerVersion = "123.1";
static const string SerializerVersion = "127";
namespace VFS {
using Pak = shared_pointer<vfs::directory>;
+22 -16
View File
@@ -1,16 +1,22 @@
#include <ares/ares.hpp>
namespace ares::Memory {
alignas(4096) u8 fixedBuffer[1_GiB];
FixedAllocator::FixedAllocator() {
_allocator.resize(sizeof(fixedBuffer), 0, fixedBuffer);
}
auto FixedAllocator::get() -> bump_allocator& {
static FixedAllocator allocator;
return allocator._allocator;
}
}
#include <ares/ares.hpp>
namespace ares::Memory {
#if defined(PLATFORM_MACOS) && defined(ARCHITECTURE_ARM64)
//stub for unsupported platforms
FixedAllocator::FixedAllocator() {
}
#else
alignas(4096) u8 fixedBuffer[128_MiB];
FixedAllocator::FixedAllocator() {
_allocator.resize(sizeof(fixedBuffer), 0, fixedBuffer);
}
#endif
auto FixedAllocator::get() -> bump_allocator& {
static FixedAllocator allocator;
return allocator._allocator;
}
}
+1
View File
@@ -36,6 +36,7 @@ auto Screen::main(uintptr_t) -> void {
auto Screen::quit() -> void {
_kill = true;
_thread.join();
_sprites.reset();
}
auto Screen::power() -> void {
+1
View File
@@ -16,6 +16,7 @@ struct Platform {
virtual auto pak(Node::Object) -> shared_pointer<vfs::directory> { return {}; }
virtual auto event(Event) -> void {}
virtual auto log(string_view message) -> void {}
virtual auto status(string_view message) -> void {}
virtual auto video(Node::Video::Screen, const u32* data, u32 pitch, u32 width, u32 height) -> void {}
virtual auto audio(Node::Audio::Stream) -> void {}
virtual auto input(Node::Input::Input) -> void {}
+1 -1
View File
@@ -74,7 +74,7 @@ auto M24C::write() -> void {
case Mode::Device:
if(counter <= 8) {
device = device << 1 | data();
} else if(!select()) {
} else if(select() != Acknowledge) {
mode = Mode::Standby;
} else if(device & 1) {
mode = Mode::Read;
@@ -50,7 +50,7 @@ template<u32 Size> auto M68000::_address(EffectiveAddress& ea) -> string {
if(ea.mode == 2) return {_addressRegister(AddressRegister{ea.reg})};
if(ea.mode == 5) return {"$", hex(_readDisplacement(read(AddressRegister{ea.reg})), 6L)};
if(ea.mode == 6) return {"$", hex(_readIndex(read(AddressRegister{ea.reg})), 6L)};
if(ea.mode == 7) return {"$", hex((i16)_readPC<Word>(), 6L)};
if(ea.mode == 7) { auto imm = _readPC<Word>(); return {"$", imm >= 0x8000 ? hex((i16)imm, 6L, 'f') : hex((i16)imm, 6L, '0') }; }
if(ea.mode == 8) return {"$", hex(_readPC<Long>(), 6L)};
if(ea.mode == 9) return {"$", hex(_pc + (i16)_readPC(), 6L)};
if(ea.mode == 10) return {"$", hex(_readIndex(_pc), 6L)};
@@ -65,7 +65,7 @@ template<u32 Size> auto M68000::_effectiveAddress(EffectiveAddress& ea) -> strin
if(ea.mode == 4) return {"-(", _addressRegister(AddressRegister{ea.reg}), ")"};
if(ea.mode == 5) return {"($", hex(_readDisplacement(read(AddressRegister{ea.reg})), 6L), ")"};
if(ea.mode == 6) return {"($", hex(_readIndex(read(AddressRegister{ea.reg})), 6L), ")"};
if(ea.mode == 7) return {"($", hex((i16)_readPC<Word>(), 6L), ")"};
if(ea.mode == 7) { auto imm = _readPC<Word>(); return {"($", imm >= 0x8000 ? hex((i16)imm, 6L, 'f') : hex((i16)imm, 6L, '0'), ")"}; }
if(ea.mode == 8) return {"($", hex(_readPC<Long>(), 6L), ")"};
if(ea.mode == 9) return {"($", hex(_readDisplacement(_pc), 6L), ")"};
if(ea.mode == 10) return {"($", hex(_readIndex(_pc), 6L), ")"};
@@ -390,22 +390,30 @@ template<u32 Size> auto M68000::instructionCMPM(EffectiveAddress from, Effective
}
auto M68000::instructionDBCC(n4 test, DataRegister with) -> void {
auto displacement = extension<Word>();
if(condition(test)) {
idle(4);
} else {
idle(2);
r.pc -= 2;
if(!condition(test)) { // cc false
auto disp = sign<Word>(r.irc);
// predict branch
r.pc += disp;
prefetch();
// decrement register
n16 result = read<Word>(with);
write<Word>(with, result - 1);
if(result) {
idle(2);
r.pc -= 4;
r.pc += sign<Word>(displacement);
prefetch();
// branch taken
prefetch();
return;
} else {
idle(2);
// branch not taken
r.pc -= disp;
}
} else { // cc true
idle(2);
r.pc += 2;
}
prefetch();
prefetch();
}
auto M68000::instructionDIVS(EffectiveAddress from, DataRegister with) -> void {
@@ -413,6 +421,7 @@ auto M68000::instructionDIVS(EffectiveAddress from, DataRegister with) -> void {
n32 divisor = read<Word>(from) << 16, odivisor = divisor;
if(divisor == 0) {
prefetch();
return exception(Exception::DivisionByZero, Vector::DivisionByZero);
}
@@ -489,6 +498,7 @@ auto M68000::instructionDIVU(EffectiveAddress from, DataRegister with) -> void {
n32 divisor = read<Word>(from) << 16;
if(divisor == 0) {
prefetch();
return exception(Exception::DivisionByZero, Vector::DivisionByZero);
}
@@ -1262,7 +1272,7 @@ auto M68000::instructionSWAP(DataRegister with) -> void {
auto M68000::instructionTAS(EffectiveAddress with) -> void {
n32 data;
if(with.mode == DataRegisterDirect) {
if(lockable() || with.mode == DataRegisterDirect) {
data = read<Byte, Hold>(with);
prefetch();
write<Byte>(with, data | 0x80);
+1 -1
View File
@@ -56,7 +56,7 @@ auto M68000::exception(u32 exception, u32 vector, u32 priority) -> void {
auto sr = readSR();
if(!r.s) swap(r.a[7], r.sp);
r.i = priority;
if(priority) r.i = priority;
r.s = 1;
r.t = 0;
+4 -2
View File
@@ -9,8 +9,10 @@ struct M68000 {
virtual auto wait(u32 clocks) -> void = 0;
virtual auto read(n1 upper, n1 lower, n24 address, n16 data = 0) -> n16 = 0;
virtual auto write(n1 upper, n1 lower, n24 address, n16 data) -> void = 0;
virtual auto lockable() -> bool { return true; }
auto ird() const -> n16 { return r.ird; }
auto irc() const -> n16 { return r.irc; }
enum : bool { User, Supervisor };
enum : u32 { Byte, Word, Long };
@@ -70,8 +72,8 @@ struct M68000 {
M68000();
auto power() -> void;
auto supervisor() -> bool;
auto exception(u32 exception, u32 vector, u32 priority = 7) -> void;
auto interrupt(u32 vector, u32 priority = 7) -> void;
auto exception(u32 exception, u32 vector, u32 priority = 0) -> void;
auto interrupt(u32 vector, u32 priority = 0) -> void;
//registers.cpp
struct DataRegister {
@@ -57,19 +57,24 @@ auto MOS6502::disassembleInstruction(maybe<n16> _pc) -> string {
switch(auto opcode = readDebugger(pc)) {
op(0x00, "brk", immediate)
op(0x01, "ora", indirectX)
op(0x04, "nop", zeroPage)
op(0x05, "ora", zeroPage)
op(0x06, "asl", zeroPage)
op(0x08, "php", implied)
op(0x09, "ora", immediate)
op(0x0a, "asl", implied)
op(0x0c, "nop", absolute)
op(0x0d, "ora", absolute)
op(0x0e, "asl", absolute)
op(0x10, "bpl", branch)
op(0x11, "ora", indirectY)
op(0x14, "nop", zeroPageX)
op(0x15, "ora", zeroPageX)
op(0x16, "asl", zeroPageX)
op(0x18, "clc", implied)
op(0x19, "ora", absoluteY)
op(0x1a, "nop", implied)
op(0x1c, "nop", absoluteX)
op(0x1d, "ora", absoluteX)
op(0x1e, "asl", absoluteX)
op(0x20, "jsr", absolute)
@@ -85,14 +90,18 @@ auto MOS6502::disassembleInstruction(maybe<n16> _pc) -> string {
op(0x2e, "rol", absolute)
op(0x30, "bmi", branch)
op(0x31, "and", indirectY)
op(0x34, "nop", zeroPageX)
op(0x35, "and", zeroPageX)
op(0x36, "rol", zeroPageX)
op(0x38, "sec", implied)
op(0x39, "and", absoluteY)
op(0x3a, "nop", implied)
op(0x3c, "nop", absoluteX)
op(0x3d, "and", absoluteX)
op(0x3e, "rol", absoluteX)
op(0x40, "rti", implied)
op(0x41, "eor", indirectX)
op(0x44, "nop", zeroPage)
op(0x45, "eor", zeroPage)
op(0x46, "lsr", zeroPage)
op(0x48, "pha", implied)
@@ -103,14 +112,18 @@ auto MOS6502::disassembleInstruction(maybe<n16> _pc) -> string {
op(0x4e, "lsr", absolute)
op(0x50, "bvc", branch)
op(0x51, "eor", indirectY)
op(0x54, "nop", zeroPageX)
op(0x55, "eor", zeroPageX)
op(0x56, "lsr", zeroPageX)
op(0x58, "cli", implied)
op(0x59, "eor", absoluteY)
op(0x5a, "nop", implied)
op(0x5c, "nop", absoluteX)
op(0x5d, "eor", absoluteX)
op(0x5e, "lsr", absoluteX)
op(0x60, "rts", implied)
op(0x61, "adc", indirectX)
op(0x64, "nop", zeroPage)
op(0x65, "adc", zeroPage)
op(0x66, "ror", zeroPage)
op(0x68, "pla", implied)
@@ -121,17 +134,23 @@ auto MOS6502::disassembleInstruction(maybe<n16> _pc) -> string {
op(0x6e, "ror", absolute)
op(0x70, "bvs", branch)
op(0x71, "adc", indirectY)
op(0x74, "nop", zeroPageX)
op(0x75, "adc", zeroPageX)
op(0x76, "ror", zeroPageX)
op(0x78, "sei", implied)
op(0x79, "adc", absoluteY)
op(0x7a, "nop", implied)
op(0x7c, "nop", absoluteX)
op(0x7d, "adc", absoluteX)
op(0x7e, "ror", absoluteX)
op(0x80, "nop", immediate)
op(0x81, "sta", indirectX)
op(0x82, "nop", immediate)
op(0x84, "sty", zeroPage)
op(0x85, "sta", zeroPage)
op(0x86, "stx", zeroPage)
op(0x88, "dey", implied)
op(0x89, "nop", immediate)
op(0x8a, "txa", implied)
op(0x8c, "sty", absolute)
op(0x8d, "sta", absolute)
@@ -170,6 +189,7 @@ auto MOS6502::disassembleInstruction(maybe<n16> _pc) -> string {
op(0xbe, "ldx", absoluteY)
op(0xc0, "cpy", immediate)
op(0xc1, "cmp", indirectX)
op(0xc2, "nop", immediate)
op(0xc4, "cpy", zeroPage)
op(0xc5, "cmp", zeroPage)
op(0xc6, "dec", zeroPage)
@@ -181,14 +201,18 @@ auto MOS6502::disassembleInstruction(maybe<n16> _pc) -> string {
op(0xce, "dec", absolute)
op(0xd0, "bne", branch)
op(0xd1, "cmp", indirectY)
op(0xd4, "nop", zeroPageX)
op(0xd5, "cmp", zeroPageX)
op(0xd6, "dec", zeroPageX)
op(0xd8, "cld", implied)
op(0xd9, "cmp", absoluteY)
op(0xda, "nop", implied)
op(0xdc, "nop", absoluteX)
op(0xdd, "cmp", absoluteX)
op(0xde, "dec", absoluteX)
op(0xe0, "cpx", immediate)
op(0xe1, "sbc", indirectX)
op(0xe2, "nop", immediate)
op(0xe4, "cpx", zeroPage)
op(0xe5, "sbc", zeroPage)
op(0xe6, "inc", zeroPage)
@@ -200,10 +224,13 @@ auto MOS6502::disassembleInstruction(maybe<n16> _pc) -> string {
op(0xee, "inc", absolute)
op(0xf0, "beq", branch)
op(0xf1, "sbc", indirectY)
op(0xf4, "nop", zeroPageX)
op(0xf5, "sbc", zeroPageX)
op(0xf6, "inc", zeroPageX)
op(0xf8, "sed", implied)
op(0xf9, "sbc", absoluteY)
op(0xfa, "nop", implied)
op(0xfc, "nop", absoluteX)
op(0xfd, "sbc", absoluteX)
op(0xfe, "inc", absoluteX)
@@ -20,19 +20,24 @@ auto MOS6502::instruction() -> void {
switch(code) {
op(0x00, Break)
op(0x01, IndirectXRead, fp(ORA), A)
op(0x04, NoOperationZeroPage)
op(0x05, ZeroPageRead, fp(ORA), A)
op(0x06, ZeroPageModify, fp(ASL))
op(0x08, PushP)
op(0x09, Immediate, fp(ORA), A)
op(0x0a, Implied, fp(ASL), A)
op(0x0c, NoOperationAbsolute)
op(0x0d, AbsoluteRead, fp(ORA), A)
op(0x0e, AbsoluteModify, fp(ASL))
op(0x10, Branch, N == 0)
op(0x11, IndirectYRead, fp(ORA), A)
op(0x14, NoOperationZeroPage, X)
op(0x15, ZeroPageRead, fp(ORA), A, X)
op(0x16, ZeroPageModify, fp(ASL), X)
op(0x18, Clear, C)
op(0x19, AbsoluteRead, fp(ORA), A, Y)
op(0x1a, NoOperation)
op(0x1c, NoOperationAbsolute, X)
op(0x1d, AbsoluteRead, fp(ORA), A, X)
op(0x1e, AbsoluteModify, fp(ASL), X)
op(0x20, CallAbsolute)
@@ -48,14 +53,18 @@ auto MOS6502::instruction() -> void {
op(0x2e, AbsoluteModify, fp(ROL))
op(0x30, Branch, N == 1)
op(0x31, IndirectYRead, fp(AND), A)
op(0x34, NoOperationZeroPage, X)
op(0x35, ZeroPageRead, fp(AND), A, X)
op(0x36, ZeroPageModify, fp(ROL), X)
op(0x38, Set, C)
op(0x39, AbsoluteRead, fp(AND), A, Y)
op(0x3a, NoOperation)
op(0x3c, NoOperationAbsolute, X)
op(0x3d, AbsoluteRead, fp(AND), A, X)
op(0x3e, AbsoluteModify, fp(ROL), X)
op(0x40, ReturnInterrupt)
op(0x41, IndirectXRead, fp(EOR), A)
op(0x44, NoOperationZeroPage)
op(0x45, ZeroPageRead, fp(EOR), A)
op(0x46, ZeroPageModify, fp(LSR))
op(0x48, Push, A)
@@ -66,14 +75,18 @@ auto MOS6502::instruction() -> void {
op(0x4e, AbsoluteModify, fp(LSR))
op(0x50, Branch, V == 0)
op(0x51, IndirectYRead, fp(EOR), A)
op(0x54, NoOperationZeroPage, X)
op(0x55, ZeroPageRead, fp(EOR), A, X)
op(0x56, ZeroPageModify, fp(LSR), X)
op(0x58, Clear, I)
op(0x59, AbsoluteRead, fp(EOR), A, Y)
op(0x5a, NoOperation)
op(0x5c, NoOperationAbsolute, X)
op(0x5d, AbsoluteRead, fp(EOR), A, X)
op(0x5e, AbsoluteModify, fp(LSR), X)
op(0x60, ReturnSubroutine)
op(0x61, IndirectXRead, fp(ADC), A)
op(0x64, NoOperationZeroPage)
op(0x65, ZeroPageRead, fp(ADC), A)
op(0x66, ZeroPageModify, fp(ROR))
op(0x68, Pull, A)
@@ -84,17 +97,23 @@ auto MOS6502::instruction() -> void {
op(0x6e, AbsoluteModify, fp(ROR))
op(0x70, Branch, V == 1)
op(0x71, IndirectYRead, fp(ADC), A)
op(0x74, NoOperationZeroPage, X)
op(0x75, ZeroPageRead, fp(ADC), A, X)
op(0x76, ZeroPageModify, fp(ROR), X)
op(0x78, Set, I)
op(0x79, AbsoluteRead, fp(ADC), A, Y)
op(0x7a, NoOperation)
op(0x7c, NoOperationAbsolute, X)
op(0x7d, AbsoluteRead, fp(ADC), A, X)
op(0x7e, AbsoluteModify, fp(ROR), X)
op(0x80, NoOperationImmediate)
op(0x81, IndirectXWrite, A)
op(0x82, NoOperationImmediate)
op(0x84, ZeroPageWrite, Y)
op(0x85, ZeroPageWrite, A)
op(0x86, ZeroPageWrite, X)
op(0x88, Implied, fp(DEC), Y)
op(0x89, NoOperationImmediate)
op(0x8a, Transfer, X, A, 1)
op(0x8c, AbsoluteWrite, Y)
op(0x8d, AbsoluteWrite, A)
@@ -133,6 +152,7 @@ auto MOS6502::instruction() -> void {
op(0xbe, AbsoluteRead, fp(LD), X, Y)
op(0xc0, Immediate, fp(CPY), Y)
op(0xc1, IndirectXRead, fp(CMP), A)
op(0xc2, NoOperationImmediate)
op(0xc4, ZeroPageRead, fp(CPY), Y)
op(0xc5, ZeroPageRead, fp(CMP), A)
op(0xc6, ZeroPageModify, fp(DEC))
@@ -144,14 +164,18 @@ auto MOS6502::instruction() -> void {
op(0xce, AbsoluteModify, fp(DEC))
op(0xd0, Branch, Z == 0)
op(0xd1, IndirectYRead, fp(CMP), A)
op(0xd4, NoOperationZeroPage, X)
op(0xd5, ZeroPageRead, fp(CMP), A, X)
op(0xd6, ZeroPageModify, fp(DEC), X)
op(0xd8, Clear, D)
op(0xd9, AbsoluteRead, fp(CMP), A, Y)
op(0xda, NoOperation)
op(0xdc, NoOperationAbsolute, X)
op(0xdd, AbsoluteRead, fp(CMP), A, X)
op(0xde, AbsoluteModify, fp(DEC), X)
op(0xe0, Immediate, fp(CPX), X)
op(0xe1, IndirectXRead, fp(SBC), A)
op(0xe2, NoOperationImmediate)
op(0xe4, ZeroPageRead, fp(CPX), X)
op(0xe5, ZeroPageRead, fp(SBC), A)
op(0xe6, ZeroPageModify, fp(INC))
@@ -163,10 +187,13 @@ auto MOS6502::instruction() -> void {
op(0xee, AbsoluteModify, fp(INC))
op(0xf0, Branch, Z == 1)
op(0xf1, IndirectYRead, fp(SBC), A)
op(0xf4, NoOperationZeroPage, X)
op(0xf5, ZeroPageRead, fp(SBC), A, X)
op(0xf6, ZeroPageModify, fp(INC), X)
op(0xf8, Set, D)
op(0xf9, AbsoluteRead, fp(SBC), A, Y)
op(0xfa, NoOperation)
op(0xfc, NoOperationAbsolute, X)
op(0xfd, AbsoluteRead, fp(SBC), A, X)
op(0xfe, AbsoluteModify, fp(INC), X)
}
@@ -139,6 +139,34 @@ auto MOS6502::instructionNoOperation() -> void {
L idle();
}
auto MOS6502::instructionNoOperationAbsolute() -> void {
n16 absolute = operand();
absolute |= operand() << 8;
L read(absolute);
}
auto MOS6502::instructionNoOperationAbsolute(n8 index) -> void {
n16 absolute = operand();
absolute |= operand() << 8;
idlePageCrossed(absolute, absolute + index);
L read(absolute + index);
}
auto MOS6502::instructionNoOperationImmediate() -> void {
L operand();
}
auto MOS6502::instructionNoOperationZeroPage() -> void {
auto zeroPage = operand();
L load(zeroPage);
}
auto MOS6502::instructionNoOperationZeroPage(n8 index) -> void {
auto zeroPage = operand();
load(zeroPage);
L load(zeroPage + index);
}
auto MOS6502::instructionPull(n8& data) -> void {
idle();
idle();
@@ -70,6 +70,11 @@ struct MOS6502 {
auto instructionJumpAbsolute() -> void;
auto instructionJumpIndirect() -> void;
auto instructionNoOperation() -> void;
auto instructionNoOperationAbsolute() -> void;
auto instructionNoOperationAbsolute(n8 index) -> void;
auto instructionNoOperationImmediate() -> void;
auto instructionNoOperationZeroPage() -> void;
auto instructionNoOperationZeroPage(n8 index) -> void;
auto instructionPull(n8& data) -> void;
auto instructionPullP() -> void;
auto instructionPush(n8& data) -> void;
+25 -12
View File
@@ -1,3 +1,11 @@
auto SH2::Recompiler::invalidate(u32 address) -> void {
auto pool = pools[address >> 8 & 0xffffff];
if(!pool) return;
memory::jitprotect(false);
pool->blocks[address >> 1 & 0x7f] = nullptr;
memory::jitprotect(true);
}
auto SH2::Recompiler::pool(u32 address) -> Pool* {
auto& pool = pools[address >> 8 & 0xffffff];
if(!pool) pool = (Pool*)allocator.acquire(sizeof(Pool));
@@ -7,7 +15,9 @@ auto SH2::Recompiler::pool(u32 address) -> Pool* {
auto SH2::Recompiler::block(u32 address) -> Block* {
if(auto block = pool(address)->blocks[address >> 1 & 0x7f]) return block;
auto block = emit(address);
return pool(address)->blocks[address >> 1 & 0x7f] = block;
pool(address)->blocks[address >> 1 & 0x7f] = block;
memory::jitprotect(true);
return block;
}
alwaysinline auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
@@ -25,34 +35,37 @@ alwaysinline auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
return 0;
}
#define CCR mem(sreg(1), offsetof(Registers, CCR))
auto SH2::Recompiler::emit(u32 address) -> Block* {
if(unlikely(allocator.available() < 1_MiB)) {
print("SH2 allocator flush\n");
memory::jitprotect(false);
allocator.release(bump_allocator::zero_fill);
memory::jitprotect(true);
reset();
}
auto block = (Block*)allocator.acquire(sizeof(Block));
block->code = allocator.acquire();
bind({block->code, allocator.available()});
beginFunction(2);
bool hasBranched = 0;
while(true) {
u16 instruction = self.readWord(address);
bool branched = emitInstruction(instruction);
mov(rax, mem64(&self.CCR));
inc(rax);
mov(mem64(&self.CCR), rax);
call(&SH2::instructionEpilogue, &self);
add64(CCR, CCR, imm(1));
call(&SH2::instructionEpilogue);
address += 2;
if(hasBranched || (address & 0xfe) == 0) break; //block boundary
hasBranched = branched;
test(al, al);
jz(imm8(1));
ret();
testJumpEpilog();
}
ret();
jumpEpilog();
memory::jitprotect(false);
block->code = endFunction();
allocator.reserve(size());
return block;
}
#undef CCR
+1 -1
View File
@@ -45,7 +45,7 @@ auto SH2::instruction() -> void {
}
}
auto SH2::instructionEpilogue() -> bool {
auto SH2::instructionEpilogue() -> s32 {
switch(PPM) {
case Branch::Step: PC = PC + 2; return 0;
case Branch::Slot: PC = PC + 2; PPM = Branch::Take; return 0;
+20 -10
View File
@@ -5,9 +5,10 @@ auto SH2::ADD(u32 m, u32 n) -> void {
//ADDC Rm,Rn
auto SH2::ADDC(u32 m, u32 n) -> void {
bool c = (u64)R[n] + R[m] >> 32;
R[n] = R[n] + R[m] + SR.T;
SR.T = c;
u32 a = R[n];
u32 b = a + R[m];
R[n] = b + SR.T;
SR.T = a > b || b > R[n];
}
//ADD #imm,Rn
@@ -17,9 +18,11 @@ auto SH2::ADDI(u32 i, u32 n) -> void {
//ADDV Rm,Rn
auto SH2::ADDV(u32 m, u32 n) -> void {
bool v = ~(R[n] ^ R[m]) & (R[n] ^ R[n] + R[m]) >> 31;
u32 d = ((s32)R[n] < 0);
u32 s = ((s32)R[m] < 0) + d;
R[n] += R[m];
SR.T = v;
u32 r = ((s32)R[n] < 0) + d;
SR.T = s != 1 ? r == 1 : 0;
}
//AND Rm,Rn
@@ -140,7 +143,11 @@ auto SH2::CMPPZ(u32 n) -> void {
//CMP/STR Rm,Rn
auto SH2::CMPSTR(u32 m, u32 n) -> void {
SR.T = bool(R[n] ^ R[m]);
u32 t = R[n] ^ R[m];
SR.T = !(t & 0xff << 24)
|| !(t & 0xff << 16)
|| !(t & 0xff << 8)
|| !(t & 0xff);
}
//DIV0S Rm,Rn
@@ -353,8 +360,9 @@ auto SH2::MOVBLG(u32 d) -> void {
//MOV.B Rm,@-Rn
auto SH2::MOVBM(u32 m, u32 n) -> void {
u32 t = R[m];
R[n] -= 1;
writeByte(R[n], R[m]);
writeByte(R[n], t);
}
//MOV.B @Rm+,Rn
@@ -405,8 +413,9 @@ auto SH2::MOVWLG(u32 d) -> void {
//MOV.W Rm,@-Rn
auto SH2::MOVWM(u32 m, u32 n) -> void {
u32 t = R[m];
R[n] -= 2;
writeWord(R[n], R[m]);
writeWord(R[n], t);
}
//MOV.W @Rm+,Rn
@@ -457,8 +466,9 @@ auto SH2::MOVLLG(u32 d) -> void {
//MOV.L Rm,@-Rn
auto SH2::MOVLM(u32 m, u32 n) -> void {
u32 t = R[m];
R[n] -= 4;
writeLong(R[n], R[m]);
writeLong(R[n], t);
}
//MOV.L @Rm+,Rn
@@ -536,7 +546,7 @@ auto SH2::NEG(u32 m, u32 n) -> void {
auto SH2::NEGC(u32 m, u32 n) -> void {
u32 t = 0 - R[m];
R[n] = t - SR.T;
SR.T = (s32)t >= 0 || t < R[n];
SR.T = 0 < t || t < R[n];
}
//NOP
File diff suppressed because it is too large. Load diff
+36 -5
View File
@@ -3,14 +3,26 @@
namespace ares {
#define SP R[15]
#define SP R[15]
#define R regs.R
#define PC regs.PC
#define PR regs.PR
#define GBR regs.GBR
#define VBR regs.VBR
#define MAC regs.MAC
#define MACL regs.MACL
#define MACH regs.MACH
#define CCR regs.CCR
#define SR regs.SR
#define PPC regs.PPC
#define PPM regs.PPM
#define ET regs.ET
#define ID regs.ID
#include "sh7604/sh7604.cpp"
#include "exceptions.cpp"
#include "instruction.cpp"
#include "instructions.cpp"
#include "cached.cpp"
//#include "recompiler.cpp"
#include "serialization.cpp"
#include "disassembler.cpp"
@@ -47,10 +59,29 @@ auto SH2::power(bool reset) -> void {
cache.power();
if constexpr(Accuracy::Recompiler) {
auto buffer = ares::Memory::FixedAllocator::get().acquire(512_MiB);
recompiler.allocator.resize(512_MiB, bump_allocator::executable | bump_allocator::zero_fill, buffer);
auto buffer = ares::Memory::FixedAllocator::get().tryAcquire(64_MiB);
recompiler.allocator.resize(64_MiB, bump_allocator::executable | bump_allocator::zero_fill, buffer);
recompiler.reset();
}
}
#undef SP
#undef R
#undef PC
#undef PR
#undef GBR
#undef VBR
#undef MAC
#undef MACL
#undef MACH
#undef CCR
#undef SR
#undef PPC
#undef PPM
#undef ET
#undef ID
//#include "cached.cpp"
#include "recompiler.cpp"
}
+26 -37
View File
@@ -1,6 +1,6 @@
#pragma once
#include <nall/recompiler/amd64/amd64.hpp>
#include <nall/recompiler/generic/generic.hpp>
//Hitachi SH-2
@@ -16,7 +16,7 @@ struct SH2 {
virtual auto busWriteLong(u32 address, u32 data) -> void = 0;
auto inDelaySlot() const -> bool {
return PPM != Branch::Step;
return regs.PPM != Branch::Step;
}
//sh2.cpp
@@ -39,7 +39,7 @@ struct SH2 {
auto jump(u32 pc) -> void;
auto branch(u32 pc) -> void;
auto delaySlot(u32 pc) -> void;
auto instructionEpilogue() -> bool;
auto instructionEpilogue() -> s32;
auto instruction() -> void;
auto execute(u16 opcode) -> void;
@@ -220,21 +220,23 @@ struct SH2 {
u32 M;
};
u32 R[16]; //general purpose registers
u32 PC; //program counter
u32 PR; //procedure register
u32 GBR; //global base register
u32 VBR; //vector base register
union {
u64 MAC; //multiply-and-accumulate register
struct { u32 order_msb2(MACH, MACL); };
};
u32 CCR; //clock counter register
S32 SR; //status register
u32 PPC; //program counter for delay slots
u32 PPM; //delay slot mode
u32 ET; //exception triggered flag
u32 ID; //interrupts disabled flag
struct Registers {
u32 R[16]; //general purpose registers
u32 PC; //program counter
u32 PR; //procedure register
u32 GBR; //global base register
u32 VBR; //vector base register
union {
u64 MAC; //multiply-and-accumulate register
struct { u32 order_msb2(MACH, MACL); };
};
u32 CCR; //clock counter register
S32 SR; //status register
u32 PPC; //program counter for delay slots
u32 PPM; //delay slot mode
u32 ET; //exception triggered flag
u32 ID; //interrupts disabled flag
} regs;
enum : u32 {
ResetCold = 1 << 0,
@@ -244,14 +246,13 @@ struct SH2 {
};
u32 exceptions = 0; //delayed exception flags
struct Recompiler : recompiler::amd64 {
using recompiler::amd64::call;
struct Recompiler : recompiler::generic {
SH2& self;
Recompiler(SH2& self) : self(self) {}
Recompiler(SH2& self) : self(self), generic(allocator) {}
struct Block {
auto execute(SH2& self) -> void {
((void (*)(u32*, SH2*))code)(&self.R[0], &self);
((void (*)(SH2*, Registers*))code)(&self, &self.regs);
}
u8* code;
@@ -265,25 +266,13 @@ struct SH2 {
for(u32 index : range(1 << 24)) pools[index] = nullptr;
}
auto invalidate(u32 address) -> void {
pool(address)->blocks[address >> 1 & 0x7f] = nullptr;
}
auto invalidate(u32 address) -> void;
auto pool(u32 address) -> Pool*;
auto block(u32 address) -> Block*;
auto emit(u32 address) -> Block*;
auto emitInstruction(u16 opcode) -> bool;
template<typename V, typename... P>
alwaysinline auto call(V (SH2::*function)(P...)) -> void {
static_assert(sizeof...(P) <= 5);
if constexpr(ABI::Windows) {
if constexpr(sizeof...(P) >= 5) mov(dis8(rsp, 0x28), ra5);
if constexpr(sizeof...(P) >= 4) mov(dis8(rsp, 0x20), ra4);
}
mov(ra0, rbp);
call(imm64{function}, rax);
}
auto getSR(reg dst) -> void;
auto setSR(reg src) -> void;
bump_allocator allocator;
Pool* pools[1 << 24];
+64
View File
@@ -0,0 +1,64 @@
struct Bandai74161 : Interface {
static auto create(string id) -> Interface* {
if(id == "BANDAI-74161" ) return new Bandai74161(Revision::B74161);
if(id == "BANDAI-74161A") return new Bandai74161(Revision::B74161A);
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Readable<n8> characterROM;
enum class Revision : u32 {
B74161,
B74161A,
} revision;
Bandai74161(Revision revision) : revision(revision) {}
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterROM, "character.rom");
mirror = pak->attribute("mirror") == "vertical";
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
n3 bank = (address < 0xc000 ? programBank : (n3)0x7);
return programROM.read(bank << 14 | (n14)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x8000) return;
characterBank = data.bit(0,3);
programBank = data.bit(4,6);
nametableBank = data.bit(7);
}
auto addressCIRAM(n32 address) const -> n32 {
if(revision == Revision::B74161A) {
return nametableBank << 10 | (n10)address;
}
return address >> !mirror & 0x0400 | (n10)address;
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
return characterROM.read(characterBank << 13 | (n13)address);
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
}
auto serialize(serializer& s) -> void override {
s(mirror);
s(programBank);
s(characterBank);
s(nametableBank);
}
n1 mirror;
n3 programBank;
n4 characterBank;
n1 nametableBank;
};
+35 -78
View File
@@ -6,123 +6,80 @@ struct BandaiFCG : Interface {
Memory::Readable<n8> programROM;
Memory::Readable<n8> characterROM;
Memory::Writable<n8> characterRAM;
M24C eeprom;
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterROM, "character.rom");
Interface::load(characterRAM, "character.ram");
if(auto fp = pak->read("save.eeprom")) {
eeprom.load(M24C::Type::X24C01);
fp->read({eeprom.memory, eeprom.size()});
}
}
auto save() -> void override {
Interface::save(characterRAM, "save.ram");
if(auto fp = pak->write("save.eeprom")) {
fp->write({eeprom.memory, eeprom.size()});
}
}
auto main() -> void override {
if(irqCounterEnable) {
if(irqEnable) {
if(--irqCounter == 0xffff) {
cpu.irqLine(1);
irqCounterEnable = false;
irqEnable = false;
}
}
tick();
}
auto addressCIRAM(n32 address) const -> n32 {
switch(mirror) {
case 0: return address >> 0 & 0x0400 | address & 0x03ff;
case 1: return address >> 1 & 0x0400 | address & 0x03ff;
case 2: return 0x0000 | address & 0x03ff;
case 3: return 0x0400 | address & 0x03ff;
}
unreachable;
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address >= 0x6000 && address <= 0x7fff) {
data.bit(4) = eeprom.read();
return data;
}
if(address & 0x8000) {
n1 region = bool(address & 0x4000);
n4 bank = (region == 0 ? programBank : (n4)0x0f);
return programROM.read(bank << 14 | (n14)address);
}
return data;
if(address < 0x8000) return data;
n4 bank = (address < 0xc000 ? programBank : (n4)0x0f);
return programROM.read(bank << 14 | (n14)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address >= 0x6000) {
switch(address & 0xf) {
case 0x0: characterBank[0] = data; break;
case 0x1: characterBank[1] = data; break;
case 0x2: characterBank[2] = data; break;
case 0x3: characterBank[3] = data; break;
case 0x4: characterBank[4] = data; break;
case 0x5: characterBank[5] = data; break;
case 0x6: characterBank[6] = data; break;
case 0x7: characterBank[7] = data; break;
case 0x8: programBank = data.bit(0,3); break;
case 0x9: mirror = data.bit(0,1); break;
case 0xa:
cpu.irqLine(0);
irqCounterEnable = data.bit(0);
irqCounter = irqLatch;
break;
case 0xb: irqLatch.byte(0) = data; break;
case 0xc: irqLatch.byte(1) = data; break;
case 0xd:
eeprom.clock = data.bit(5);
eeprom.data = data.bit(6);
eeprom.write();
break;
}
switch(address & 0xe00f) {
case 0x6000: characterBank[0] = data; break;
case 0x6001: characterBank[1] = data; break;
case 0x6002: characterBank[2] = data; break;
case 0x6003: characterBank[3] = data; break;
case 0x6004: characterBank[4] = data; break;
case 0x6005: characterBank[5] = data; break;
case 0x6006: characterBank[6] = data; break;
case 0x6007: characterBank[7] = data; break;
case 0x6008: programBank = data.bit(0,3); break;
case 0x6009: mirror = data.bit(0,1); break;
case 0x600a:
irqEnable = data.bit(0);
cpu.irqLine(0);
break;
case 0x600b: irqCounter.byte(0) = data; break;
case 0x600c: irqCounter.byte(1) = data; break;
}
}
auto addressCIRAM(n32 address) const -> n32 {
switch(mirror) {
case 0: return address >> 0 & 0x0400 | (n10)address;
case 1: return address >> 1 & 0x0400 | (n10)address;
case 2: return 0x0000 | (n10)address;
case 3: return 0x0400 | (n10)address;
}
unreachable;
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
address = characterBank[address >> 10] << 10 | (n10)address;
if(characterROM) return characterROM.read(address);
if(characterRAM) return characterRAM.read(address);
return data;
return characterROM.read(address);
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
address = characterBank[address >> 10] << 10 | (n10)address;
if(characterRAM) return characterRAM.write(address, data);
}
auto power() -> void override {
eeprom.power();
}
auto serialize(serializer& s) -> void override {
s(characterRAM);
s(eeprom);
s(characterBank);
s(programBank);
s(mirror);
s(irqCounterEnable);
s(irqEnable);
s(irqCounter);
s(irqLatch);
}
n8 characterBank[8];
n4 programBank;
n2 mirror;
n1 irqCounterEnable;
n1 irqEnable;
n16 irqCounter;
n16 irqLatch;
};
@@ -0,0 +1,69 @@
struct BandaiKaraoke : Interface {
static auto create(string id) -> Interface* {
if(id == "BANDAI-KARAOKE") return new BandaiKaraoke;
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Readable<n8> optionROM;
Memory::Writable<n8> characterRAM;
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(optionROM, "option.rom");
Interface::load(characterRAM, "character.ram");
}
auto save() -> void override {
Interface::save(characterRAM, "character.ram");
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x6000) return data;
if(address < 0x8000) {
data.bit(0) = 1; //A button
data.bit(1) = 1; //B button
//data.bit(2) = 1; //1-bit ADC
return data;
}
if(address < 0xc000) {
if(!optionEnable) return programROM.read(programBank << 14 | (n14)address);
if(optionROM) return optionROM.read(programBank << 14 | (n14)address);
return data;
}
return programROM.read(0xf << 14 | (n14)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x8000) return;
programBank = data.bit(0,3);
optionEnable = !data.bit(4);
mirror = data.bit(5);
}
auto addressCIRAM(n32 address) -> n32 {
return address >> mirror & 0x0400 | (n10)address;
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
return characterRAM.read(address);
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
return characterRAM.write(address, data);
}
auto serialize(serializer& s) -> void override {
s(characterRAM);
s(programBank);
s(optionEnable);
s(mirror);
}
n4 programBank;
n1 optionEnable;
n1 mirror;
};
+139
View File
@@ -0,0 +1,139 @@
struct BandaiLZ93D50 : Interface {
static auto create(string id) -> Interface* {
if(id == "BANDAI-LZ93D50") return new BandaiLZ93D50;
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Writable<n8> programRAM;
Memory::Readable<n8> characterROM;
Memory::Writable<n8> characterRAM;
M24C eeprom;
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(programRAM, "save.ram");
Interface::load(characterROM, "character.rom");
Interface::load(characterRAM, "character.ram");
if(auto fp = pak->read("save.eeprom")) {
if(fp->size() == 128) eeprom.load(M24C::Type::X24C01);
if(fp->size() == 256) eeprom.load(M24C::Type::M24C02);
if(eeprom) fp->read({eeprom.memory, eeprom.size()});
}
}
auto save() -> void override {
Interface::save(programRAM, "save.ram");
Interface::save(characterRAM, "character.ram");
if(auto fp = pak->write("save.eeprom")) {
if(eeprom) fp->write({eeprom.memory, eeprom.size()});
}
}
auto main() -> void override {
if(irqEnable) {
if(--irqCounter == 0xffff) {
cpu.irqLine(1);
irqEnable = false;
}
}
tick();
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x6000) return data;
if(address < 0x8000) {
if(programRAM && ramEnable) return programRAM.read((n13)address);
if(eeprom) data.bit(4) = eeprom.read();
return data;
}
n5 bank = (address < 0xc000 ? (n5)programBank : (n5)0xf);
if(characterRAM) bank.bit(4) = bool(programOuterBank);
return programROM.read(bank << 14 | (n14)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x6000) return;
if(address < 0x8000) {
if(programRAM && ramEnable) return programRAM.write((n13)address, data);
}
switch(address & 0x800f) {
case 0x8000: characterBank[0] = data; programOuterBank.bit(0) = data.bit(0); break;
case 0x8001: characterBank[1] = data; programOuterBank.bit(1) = data.bit(0); break;
case 0x8002: characterBank[2] = data; programOuterBank.bit(2) = data.bit(0); break;
case 0x8003: characterBank[3] = data; programOuterBank.bit(3) = data.bit(0); break;
case 0x8004: characterBank[4] = data; break;
case 0x8005: characterBank[5] = data; break;
case 0x8006: characterBank[6] = data; break;
case 0x8007: characterBank[7] = data; break;
case 0x8008: programBank = data.bit(0,3); break;
case 0x8009: mirror = data.bit(0,1); break;
case 0x800a:
irqEnable = data.bit(0);
irqCounter = irqLatch;
cpu.irqLine(0);
break;
case 0x800b: irqLatch.byte(0) = data; break;
case 0x800c: irqLatch.byte(1) = data; break;
case 0x800d:
if(eeprom) {
eeprom.clock = data.bit(5);
eeprom.data = data.bit(6);
eeprom.write();
}
ramEnable = data.bit(5);
break;
}
}
auto addressCIRAM(n32 address) const -> n32 {
switch(mirror) {
case 0: return address >> 0 & 0x0400 | (n10)address;
case 1: return address >> 1 & 0x0400 | (n10)address;
case 2: return 0x0000 | (n10)address;
case 3: return 0x0400 | (n10)address;
}
unreachable;
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
if(characterRAM) return characterRAM.read(address);
address = characterBank[address >> 10] << 10 | (n10)address;
return characterROM.read(address);
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
if(characterRAM) return characterRAM.write(address, data);
}
auto power() -> void override {
eeprom.power();
}
auto serialize(serializer& s) -> void override {
s(programRAM);
s(characterRAM);
s(eeprom);
s(characterBank);
s(programBank);
s(programOuterBank);
s(mirror);
s(ramEnable);
s(irqEnable);
s(irqCounter);
s(irqLatch);
}
n8 characterBank[8];
n4 programBank;
n4 programOuterBank;
n2 mirror;
n1 ramEnable;
n1 irqEnable;
n16 irqCounter;
n16 irqLatch;
};
+76
View File
@@ -0,0 +1,76 @@
struct BandaiOeka : Interface {
static auto create(string id) -> Interface* {
if(id == "BANDAI-OEKA") return new BandaiOeka;
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Writable<n8> characterRAM;
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterRAM, "character.ram");
mirror = pak->attribute("mirror") == "vertical";
}
auto save() -> void override {
Interface::save(characterRAM, "character.ram");
}
auto addressTest(n32 address) -> void {
if((characterAddress >> 12) != 2 && (address >> 12) == 2) {
characterBank.bit(0,1) = address >> 8 & 3;
}
characterAddress = address;
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
return programROM.read(programBank << 15 | (n15)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address == 0x2006) {
if(++characterLatch) addressTest(data << 8);
}
if(address < 0x8000) return;
programBank = data.bit(0,1);
characterBank.bit(2) = data.bit(2);
}
auto addressCHR(n32 address) const -> n32 {
n3 bank = (address < 0x1000) ? characterBank : n3(characterBank & 4 | 3);
return bank << 12 | (n12)address;
}
auto addressCIRAM(n32 address) const -> n32 {
return address >> !mirror & 0x0400 | (n10)address;
}
auto readCHR(n32 address, n8 data) -> n8 override {
addressTest(address);
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
return characterRAM.read(addressCHR(address));
}
auto writeCHR(n32 address, n8 data) -> void override {
addressTest(address);
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
return characterRAM.write(addressCHR(address), data);
}
auto serialize(serializer& s) -> void override {
s(characterRAM);
s(mirror);
s(programBank);
s(characterBank);
s(characterLatch);
s(characterAddress);
}
n1 mirror;
n2 programBank;
n3 characterBank;
n1 characterLatch;
n32 characterAddress;
};
+44 -6
View File
@@ -1,8 +1,17 @@
namespace Board {
#include "bandai-74161.cpp"
#include "bandai-fcg.cpp"
#include "jaleco-jf.cpp"
#include "jaleco-jf11-jf14.cpp"
#include "bandai-karaoke.cpp"
#include "bandai-lz93d50.cpp"
#include "bandai-oeka.cpp"
#include "colordreams-74x377.cpp"
#include "gtrom.cpp"
#include "jaleco-jf05.cpp"
#include "jaleco-jf11.cpp"
#include "jaleco-jf16.cpp"
#include "jaleco-jf17.cpp"
#include "jaleco-jf23.cpp"
#include "konami-vrc1.cpp"
#include "konami-vrc2.cpp"
#include "konami-vrc3.cpp"
@@ -13,7 +22,6 @@ namespace Board {
#include "hvc-axrom.cpp"
#include "hvc-bnrom.cpp"
#include "hvc-cnrom.cpp"
#include "hvc-dxrom.cpp"
#include "hvc-exrom.cpp"
#include "hvc-fmr.cpp"
#include "hvc-fxrom.cpp"
@@ -24,20 +32,36 @@ namespace Board {
#include "hvc-sxrom.cpp"
#include "hvc-txrom.cpp"
#include "hvc-uxrom.cpp"
#include "irem-g101.cpp"
#include "irem-h3001.cpp"
#include "irem-lrog017.cpp"
#include "irem-tam-s1.cpp"
#include "mlt-action52.cpp"
#include "namco-118.cpp"
#include "namco-163.cpp"
#include "namco-340.cpp"
#include "sunsoft-1.cpp"
#include "sunsoft-2.cpp"
#include "sunsoft-3.cpp"
#include "sunsoft-4.cpp"
#include "sunsoft-5b.cpp"
#include "taito-tc0190.cpp"
#include "taito-tc0690.cpp"
#include "taito-x1-005.cpp"
#include "taito-x1-017.cpp"
auto Interface::create(string board) -> Interface* {
Interface* p = nullptr;
if(!p) p = Bandai74161::create(board);
if(!p) p = BandaiFCG::create(board);
if(!p) p = BandaiKaraoke::create(board);
if(!p) p = BandaiLZ93D50::create(board);
if(!p) p = BandaiOeka::create(board);
if(!p) p = ColorDreams_74x377::create(board);
if(!p) p = GTROM::create(board);
if(!p) p = HVC_AxROM::create(board);
if(!p) p = HVC_BNROM::create(board);
if(!p) p = HVC_CNROM::create(board);
if(!p) p = HVC_DxROM::create(board);
if(!p) p = HVC_ExROM::create(board);
if(!p) p = HVC_FMR::create(board);
if(!p) p = HVC_FxROM::create(board);
@@ -48,9 +72,15 @@ auto Interface::create(string board) -> Interface* {
if(!p) p = HVC_SxROM::create(board);
if(!p) p = HVC_TxROM::create(board);
if(!p) p = HVC_UxROM::create(board);
if(!p) p = IremG101::create(board);
if(!p) p = IremH3001::create(board);
if(!p) p = IremLROG017::create(board);
if(!p) p = IremTAMS1::create(board);
if(!p) p = JalecoJF::create(board);
if(!p) p = Jaleco_JF11_JF14::create(board);
if(!p) p = JalecoJF05::create(board);
if(!p) p = JalecoJF11::create(board);
if(!p) p = JalecoJF16::create(board);
if(!p) p = JalecoJF17::create(board);
if(!p) p = JalecoJF23::create(board);
if(!p) p = KonamiVRC1::create(board);
if(!p) p = KonamiVRC2::create(board);
if(!p) p = KonamiVRC3::create(board);
@@ -58,11 +88,19 @@ auto Interface::create(string board) -> Interface* {
if(!p) p = KonamiVRC5::create(board);
if(!p) p = KonamiVRC6::create(board);
if(!p) p = KonamiVRC7::create(board);
if(!p) p = MltAction52::create(board);
if(!p) p = Namco118::create(board);
if(!p) p = Namco163::create(board);
if(!p) p = Namco340::create(board);
if(!p) p = Sunsoft1::create(board);
if(!p) p = Sunsoft2::create(board);
if(!p) p = Sunsoft3::create(board);
if(!p) p = Sunsoft4::create(board);
if(!p) p = Sunsoft5B::create(board);
if(!p) p = TaitoTC0190::create(board);
if(!p) p = TaitoTC0690::create(board);
if(!p) p = TaitoX1005::create(board);
if(!p) p = TaitoX1017::create(board);
if(!p) p = new Interface;
return p;
}
@@ -0,0 +1,66 @@
struct ColorDreams_74x377 : Interface {
static auto create(string id) -> Interface* {
if(id == "COLORDREAMS-74*377") return new ColorDreams_74x377();
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Readable<n8> characterROM;
Memory::Writable<n8> characterRAM;
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterROM, "character.rom");
Interface::load(characterRAM, "character.ram");
mirror = pak->attribute("mirror") == "vertical";
}
auto save() -> void override {
Interface::save(characterRAM, "character.ram");
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
return programROM.read(programBank << 15 | (n15)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x8000) return;
programBank = data.bit(0,1);
characterBank = data.bit(4,7);
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) {
address = address >> !mirror & 0x0400 | (n10)address;
return ppu.readCIRAM(address);
}
address = characterBank << 13 | (n13)address;
if(characterROM) return characterROM.read(address);
if(characterRAM) return characterRAM.read(address);
return data;
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) {
address = address >> !mirror & 0x0400 | (n10)address;
return ppu.writeCIRAM(address, data);
}
address = characterBank << 13 | (n13)address;
if(characterRAM) return characterRAM.write(address, data);
}
auto power() -> void override {
}
auto serialize(serializer& s) -> void override {
s(characterRAM);
s(programBank);
s(characterBank);
s(mirror);
}
n2 programBank;
n4 characterBank;
n1 mirror;
};
+63
View File
@@ -0,0 +1,63 @@
struct GTROM : Interface {
static auto create(string id) -> Interface* {
if(id == "GTROM" ) return new GTROM();
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Writable<n8> characterRAM;
Memory::Writable<n8> videoRAM;
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterRAM, "character.ram");
videoRAM.allocate(16_KiB);
}
auto save() -> void override {
Interface::save(characterRAM, "character.ram");
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
return programROM.read(programBank << 15 | (n15)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address >= 0x5000 && address <= 0x5fff) {
programBank = data.bit(0, 3);
characterBank = data.bit(4);
videoBank = data.bit(5);
return;
}
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) return videoRAM.read(videoBank << 13 | (n13)address);
if(characterRAM) return characterRAM.read(characterBank << 13 | (n13)address);
return data;
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) return videoRAM.write(videoBank << 13 | (n13)address, data);
return characterRAM.write(characterBank << 13 | (n13)address, data);
}
auto power() -> void override {
programBank = 0;
characterBank = 0;
videoBank = 0;
}
auto serialize(serializer& s) -> void override {
s(characterRAM);
s(videoRAM);
s(programBank);
s(characterBank);
s(videoBank);
}
n4 programBank;
n1 characterBank;
n1 videoBank;
};
+2 -2
View File
@@ -26,7 +26,7 @@ struct HVC_BNROM : Interface {
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x8000) return;
programBank = data.bit(0,1);
programBank = data;
}
auto readCHR(n32 address, n8 data) -> n8 override {
@@ -57,5 +57,5 @@ struct HVC_BNROM : Interface {
}
n1 mirror; //0 = horizontal, 1 = vertical
n2 programBank;
n8 programBank;
};
+31 -10
View File
@@ -1,6 +1,8 @@
struct HVC_CNROM : Interface {
static auto create(string id) -> Interface* {
if(id == "HVC-CNROM") return new HVC_CNROM;
if(id == "HVC-CNROM" ) return new HVC_CNROM(Revision::CNROM);
if(id == "HVC-CNROM-SEC") return new HVC_CNROM(Revision::CNROMS);
if(id == "HVC-CPROM" ) return new HVC_CNROM(Revision::CPROM);
return nullptr;
}
@@ -8,11 +10,20 @@ struct HVC_CNROM : Interface {
Memory::Readable<n8> characterROM;
Memory::Writable<n8> characterRAM;
enum class Revision : u32 {
CNROM,
CNROMS,
CPROM,
} revision;
HVC_CNROM(Revision revision) : revision(revision) {}
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterROM, "character.rom");
Interface::load(characterRAM, "character.ram");
mirror = pak->attribute("mirror") == "vertical";
key = pak->attribute("chip/key").natural();
}
auto save() -> void override {
@@ -26,7 +37,8 @@ struct HVC_CNROM : Interface {
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x8000) return;
characterBank = data.bit(0,1);
characterBank = data & programROM.read((n15)address);
characterEnable = (data == key);
}
auto readCHR(n32 address, n8 data) -> n8 override {
@@ -34,10 +46,16 @@ struct HVC_CNROM : Interface {
address = address >> !mirror & 0x0400 | (n10)address;
return ppu.readCIRAM(address);
}
if(revision == Revision::CPROM) {
n2 bank = (address < 0x1000) ? (n2)0 : characterBank;
return characterRAM.read(bank << 12 | (n12)address);
}
if(revision == Revision::CNROMS) {
if(!characterEnable) return 0xff;
return characterROM.read((n13)address);
}
address = characterBank << 13 | (n13)address;
if(characterROM) return characterROM.read(address);
if(characterRAM) return characterRAM.read(address);
return data;
return characterROM.read(address);
}
auto writeCHR(n32 address, n8 data) -> void override {
@@ -45,19 +63,22 @@ struct HVC_CNROM : Interface {
address = address >> !mirror & 0x0400 | (n10)address;
return ppu.writeCIRAM(address, data);
}
address = characterBank << 13 | (n13)address;
if(characterRAM) return characterRAM.write(address, data);
}
auto power() -> void override {
if(revision == Revision::CPROM) {
n2 bank = (address < 0x1000) ? (n2)0 : characterBank;
return characterRAM.write(bank << 12 | (n12)address, data);
}
}
auto serialize(serializer& s) -> void override {
s(characterRAM);
s(mirror);
s(key);
s(characterBank);
s(characterEnable);
}
n1 mirror; //0 = horizontal, 1 = vertical
n8 key;
n2 characterBank;
n1 characterEnable;
};
-94
View File
@@ -1,94 +0,0 @@
struct HVC_DxROM : Interface {
static auto create(string id) -> Interface* {
if(id == "HVC-DEROM" ) return new HVC_DxROM(Revision::DEROM);
if(id == "HVC-DE1ROM") return new HVC_DxROM(Revision::DE1ROM);
if(id == "HVC-DRROM" ) return new HVC_DxROM(Revision::DRROM);
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Readable<n8> characterROM;
Memory::Writable<n8> characterCIRAM;
enum class Revision : u32 {
DEROM,
DE1ROM,
DRROM,
} revision;
HVC_DxROM(Revision revision) : revision(revision) {}
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterROM, "character.rom");
Interface::load(characterCIRAM, "character.ram");
}
auto save() -> void override {
Interface::save(characterCIRAM, "character.ram");
}
auto readPRG(n32 address, n8 data) -> n8 override {
switch(address) {
case 0x8000 ... 0x9fff: return programROM.read(programBank[0] << 13 | (n13)address);
case 0xa000 ... 0xbfff: return programROM.read(programBank[1] << 13 | (n13)address);
case 0xc000 ... 0xdfff: return programROM.read(0x0e << 13 | (n13)address);
case 0xe000 ... 0xffff: return programROM.read(0x0f << 13 | (n13)address);
}
return data;
}
auto writePRG(n32 address, n8 data) -> void override {
switch(address & 0xe001) {
case 0x8000:
bankSelect = data.bit(0,2);
break;
case 0x8001:
switch(bankSelect) {
case 0: characterBank[0] = data & ~1; break;
case 1: characterBank[1] = data & ~1; break;
case 2: characterBank[2] = data; break;
case 3: characterBank[3] = data; break;
case 4: characterBank[4] = data; break;
case 5: characterBank[5] = data; break;
case 6: programBank[0] = data; break;
case 7: programBank[1] = data; break;
}
break;
}
}
auto readCHR(n32 address, n8 data) -> n8 override {
switch(address) {
case 0x0000 ... 0x07ff: return characterROM.read(characterBank[0] << 10 | (n11)address);
case 0x0800 ... 0x0fff: return characterROM.read(characterBank[1] << 10 | (n11)address);
case 0x1000 ... 0x13ff: return characterROM.read(characterBank[2] << 10 | (n10)address);
case 0x1400 ... 0x17ff: return characterROM.read(characterBank[3] << 10 | (n10)address);
case 0x1800 ... 0x1bff: return characterROM.read(characterBank[4] << 10 | (n10)address);
case 0x1c00 ... 0x1fff: return characterROM.read(characterBank[5] << 10 | (n10)address);
case 0x2000 ... 0x27ff: return ppu.readCIRAM((n11)address);
case 0x2800 ... 0x2fff: return characterCIRAM.read((n11)address);
}
return data;
}
auto writeCHR(n32 address, n8 data) -> void override {
switch(address) {
case 0x2000 ... 0x27ff: return ppu.writeCIRAM((n11)address, data);
case 0x2800 ... 0x2fff: return characterCIRAM.write((n11)address, data);
}
}
auto power() -> void override {
}
auto serialize(serializer& s) -> void override {
s(bankSelect);
s(programBank);
s(characterBank);
}
n3 bankSelect;
n4 programBank[2];
n6 characterBank[6];
};
+7 -3
View File
@@ -31,8 +31,9 @@ struct HVC_HKROM : Interface { //MMC6
if(!(characterAddress & 0x1000) && (address & 0x1000)) {
if(irqDelay == 0) {
if(irqCounter == 0) {
irqCounter = irqLatch;
} else if(--irqCounter == 0) {
irqCounter = irqLatch + 1;
}
if(--irqCounter == 0) {
if(irqEnable) irqLine = 1;
}
}
@@ -64,6 +65,7 @@ struct HVC_HKROM : Interface { //MMC6
}
auto writePRG(n32 address, n8 data) -> void override {
if(address == 0x2006 && ++characterLatch) irqTest(data << 8);
if(address < 0x7000) return;
if(address < 0x8000) {
@@ -179,6 +181,7 @@ struct HVC_HKROM : Interface { //MMC6
s(irqEnable);
s(irqDelay);
s(irqLine);
s(characterLatch);
s(characterAddress);
}
@@ -194,7 +197,8 @@ struct HVC_HKROM : Interface { //MMC6
n8 irqLatch;
n8 irqCounter;
n1 irqEnable;
n1 irqDelay;
n8 irqDelay;
n1 irqLine;
n1 characterLatch;
n16 characterAddress;
};
+11 -3
View File
@@ -69,7 +69,13 @@ struct HVC_SxROM : Interface { //MMC1
HVC_SxROM(Revision revision) : revision(revision) {}
auto load() -> void override {
auto chip = pak->attribute("chip");
chipRevision = ChipRevision::MMC1B2;
if(chip == "MMC1" ) chipRevision = ChipRevision::MMC1;
if(chip == "MMC1A" ) chipRevision = ChipRevision::MMC1A;
if(chip == "MMC1B1") chipRevision = ChipRevision::MMC1B1;
if(chip == "MMC1B3") chipRevision = ChipRevision::MMC1B3;
if(chip == "MMC1C" ) chipRevision = ChipRevision::MMC1C;
Interface::load(programROM, "program.rom");
Interface::load(programRAM, "save.ram");
@@ -130,7 +136,7 @@ struct HVC_SxROM : Interface { //MMC1
if(revision == Revision::SNROM) {
if(characterBank[0].bit(4)) return data;
}
if(ramDisable) return 0x00;
if(!programRAM || ramDisable) return 0x00;
return programRAM.read(addressProgramRAM(address));
}
@@ -146,7 +152,7 @@ struct HVC_SxROM : Interface { //MMC1
if(revision == Revision::SNROM) {
if(characterBank[0].bit(4)) return;
}
if(ramDisable) return;
if(!programRAM || ramDisable) return;
return programRAM.write(addressProgramRAM(address), data);
}
@@ -180,7 +186,9 @@ struct HVC_SxROM : Interface { //MMC1
break;
case 3:
programBank = shiftValue.bit(0,3);
ramDisable = shiftValue.bit(4);
if(chipRevision > ChipRevision::MMC1A) {
ramDisable = shiftValue.bit(4);
}
break;
}
}
+44 -6
View File
@@ -65,8 +65,9 @@ struct HVC_TxROM : Interface { //MMC3
if(!(characterAddress & 0x1000) && (address & 0x1000)) {
if(irqDelay == 0) {
if(irqCounter == 0) {
irqCounter = irqLatch;
} else if(--irqCounter == 0) {
irqCounter = irqLatch + 1;
}
if(--irqCounter == 0) {
if(irqEnable) irqLine = 1;
}
}
@@ -79,7 +80,7 @@ struct HVC_TxROM : Interface { //MMC3
if(address < 0x6000) return data;
if(address < 0x8000) {
if(!ramEnable) return data;
if(!ramEnable || !programRAM) return data;
return programRAM.read((n13)address);
}
@@ -95,10 +96,11 @@ struct HVC_TxROM : Interface { //MMC3
}
auto writePRG(n32 address, n8 data) -> void override {
if(address == 0x2006 && ++characterLatch) irqTest(data << 8);
if(address < 0x6000) return;
if(address < 0x8000) {
if(!ramEnable || !ramWritable) return;
if(!ramEnable || !ramWritable || !programRAM) return;
return programRAM.write((n13)address, data);
}
@@ -163,20 +165,54 @@ struct HVC_TxROM : Interface { //MMC3
}
auto addressCIRAM(n32 address) const -> n32 {
if(revision == Revision::TKSROM || revision == Revision::TLSROM) {
address.bit(12) = 0;
if(characterMode == 0) {
if(address <= 0x27ff) return characterBank[0].bit(7) << 10 | (n10)address;
if(address <= 0x2fff) return characterBank[1].bit(7) << 10 | (n10)address;
} else {
if(address <= 0x23ff) return characterBank[2].bit(7) << 10 | (n10)address;
if(address <= 0x27ff) return characterBank[3].bit(7) << 10 | (n10)address;
if(address <= 0x2bff) return characterBank[4].bit(7) << 10 | (n10)address;
if(address <= 0x2fff) return characterBank[5].bit(7) << 10 | (n10)address;
}
unreachable;
}
return address >> mirror & 0x0400 | (n10)address;
}
auto readCHR(n32 address, n8 data) -> n8 override {
irqTest(address);
if(revision == Revision::TVROM) {
if(address < 0x2000) return characterROM.read(addressCHR(address));
address.bit(12) = 0;
if(address < 0x2800) return ppu.readCIRAM((n11)address);
return characterRAM.read((n11)address);
}
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
if(characterROM) return characterROM.read(addressCHR(address));
if(revision == Revision::TQROM) {
address = addressCHR(address);
if(address < 0x10000) return characterROM.read(address);
return characterRAM.read(address);
}
if(characterRAM) return characterRAM.read(addressCHR(address));
return data;
return characterROM.read(addressCHR(address));
}
auto writeCHR(n32 address, n8 data) -> void override {
irqTest(address);
if(revision == Revision::TVROM) {
if(address < 0x2000) return;
address.bit(12) = 0;
if(address < 0x2800) return ppu.writeCIRAM((n11)address, data);
return characterRAM.write((n11)address, data);
}
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
if(revision == Revision::TQROM) {
address = addressCHR(address);
if(address < 0x10000) return;
return characterRAM.write(address, data);
}
if(characterRAM) return characterRAM.write(addressCHR(address), data);
}
@@ -201,6 +237,7 @@ struct HVC_TxROM : Interface { //MMC3
s(irqEnable);
s(irqDelay);
s(irqLine);
s(characterLatch);
s(characterAddress);
}
@@ -217,5 +254,6 @@ struct HVC_TxROM : Interface { //MMC3
n1 irqEnable;
n8 irqDelay;
n1 irqLine;
n1 characterLatch;
n16 characterAddress;
};
+14 -5
View File
@@ -1,7 +1,9 @@
struct HVC_UxROM : Interface {
static auto create(string id) -> Interface* {
if(id == "HVC-UNROM") return new HVC_UxROM(Revision::UNROM);
if(id == "HVC-UOROM") return new HVC_UxROM(Revision::UOROM);
if(id == "HVC-UNROM" ) return new HVC_UxROM(Revision::UNROM);
if(id == "HVC-UNROMA") return new HVC_UxROM(Revision::UNROMA);
if(id == "HVC-UN1ROM") return new HVC_UxROM(Revision::UN1ROM);
if(id == "HVC-UOROM" ) return new HVC_UxROM(Revision::UOROM);
return nullptr;
}
@@ -11,6 +13,8 @@ struct HVC_UxROM : Interface {
enum class Revision : u32 {
UNROM,
UNROMA,
UN1ROM,
UOROM,
} revision;
@@ -29,13 +33,18 @@ struct HVC_UxROM : Interface {
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
n4 bank = (address < 0xc000 ? programBank : (n4)0xf);
n8 bank;
switch(address >> 14 & 1) {
case 0: bank = (revision == Revision::UNROMA ? (n8)0x00 : programBank); break;
case 1: bank = (revision == Revision::UNROMA ? programBank : (n8)0xff); break;
}
return programROM.read(bank << 14 | (n14)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x8000) return;
programBank = data.bit(0,3);
programBank = data;
if(revision == Revision::UN1ROM) programBank >>= 2;
}
auto readCHR(n32 address, n8 data) -> n8 override {
@@ -66,5 +75,5 @@ struct HVC_UxROM : Interface {
}
n1 mirror; //0 = horizontal, 1 = vertical
n4 programBank;
n8 programBank;
};
+89
View File
@@ -0,0 +1,89 @@
struct IremG101 : Interface {
static auto create(string id) -> Interface* {
if(id == "IREM-G101" ) return new IremG101(Revision::G101);
if(id == "IREM-G101A") return new IremG101(Revision::G101A);
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Writable<n8> programRAM;
Memory::Readable<n8> characterROM;
enum class Revision : u32 {
G101,
G101A,
} revision;
IremG101(Revision revision) : revision(revision) {}
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(programRAM, "save.ram");
Interface::load(characterROM, "character.rom");
}
auto save() -> void override {
Interface::save(programRAM, "save.ram");
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x6000) return data;
if(address < 0x8000 && !programRAM) return data;
if(address < 0x8000) return programRAM.read((n13)address);
n5 bank;
switch(address >> 13 & 3) {
case 0: bank = (programMode == 0 ? programBank[0] : (n5)0x1e); break;
case 1: bank = programBank[1]; break;
case 2: bank = (programMode == 1 ? programBank[0] : (n5)0x1e); break;
case 3: bank = 0x1f; break;
}
return programROM.read(bank << 13 | (n13)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x6000) return;
if(address < 0x8000 && !programRAM) return;
if(address < 0x8000) return programRAM.write((n13)address, data);
switch(address & 0xf000) {
case 0x8000: programBank[0] = data.bit(0,4); break;
case 0xa000: programBank[1] = data.bit(0,4); break;
case 0xb000: characterBank[address & 7] = data; break;
case 0x9000:
if(revision == Revision::G101) {
mirror = data.bit(0);
programMode = data.bit(1);
}
break;
}
}
auto addressCIRAM(n32 address) const -> n32 {
if(revision == Revision::G101A) return (n10)address;
return address >> mirror & 0x0400 | (n10)address;
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
n8 bank = characterBank[address >> 10 & 7];
return characterROM.read(bank << 10 | (n10)address);
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
}
auto serialize(serializer& s) -> void override {
s(programRAM);
s(programBank);
s(characterBank);
s(programMode);
s(mirror);
}
n5 programBank[2];
n8 characterBank[8];
n1 programMode;
n1 mirror;
};
+104
View File
@@ -0,0 +1,104 @@
struct IremH3001 : Interface {
static auto create(string id) -> Interface* {
if(id == "IREM-H3001") return new IremH3001;
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Readable<n8> characterROM;
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterROM, "character.rom");
}
auto main() -> void override {
if(irqEnable) {
if(irqCounter && --irqCounter == 0) irqLine = 1;
}
cpu.irqLine(irqLine);
tick();
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
n6 bank;
switch(address >> 13 & 3) {
case 0: bank = (programMode == 0 ? programBank[0] : (n6)0x3e); break;
case 1: bank = programBank[1]; break;
case 2: bank = (programMode == 1 ? programBank[0] : (n6)0x3e); break;
case 3: bank = 0x3f; break;
}
return programROM.read(bank << 13 | (n13)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x8000) return;
switch(address) {
case 0x8000: programBank[0] = data.bit(0,5); break;
case 0xa000: programBank[1] = data.bit(0,5); break;
case 0xb000: characterBank[0] = data; break;
case 0xb001: characterBank[1] = data; break;
case 0xb002: characterBank[2] = data; break;
case 0xb003: characterBank[3] = data; break;
case 0xb004: characterBank[4] = data; break;
case 0xb005: characterBank[5] = data; break;
case 0xb006: characterBank[6] = data; break;
case 0xb007: characterBank[7] = data; break;
case 0x9000: programMode = data.bit(7); break;
case 0x9001: mirror = data.bit(6,7); break;
case 0x9003:
irqEnable = data.bit(7);
irqLine = 0;
break;
case 0x9004:
irqCounter = irqLatch;
irqLine = 0;
break;
case 0x9005: irqLatch = irqLatch.bit(0,7) << 0 | data << 8; break;
case 0x9006: irqLatch = irqLatch.bit(8,15) << 8 | data << 0; break;
}
}
auto addressCIRAM(n32 address) const -> n32 {
switch(mirror) {
case 0: return address >> 0 & 0x0400 | (n10)address;
case 1: return (n10)address;
case 2: return address >> 1 & 0x0400 | (n10)address;
case 3: return (n10)address;
}
unreachable;
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
n8 bank = characterBank[address >> 10 & 7];
return characterROM.read(bank << 10 | (n10)address);
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
}
auto serialize(serializer& s) -> void override {
s(programBank);
s(characterBank);
s(programMode);
s(mirror);
s(irqLatch);
s(irqCounter);
s(irqEnable);
s(irqLine);
}
n6 programBank[2];
n8 characterBank[8];
n1 programMode;
n2 mirror;
n16 irqLatch;
n16 irqCounter;
n1 irqEnable;
n1 irqLine;
};
+61
View File
@@ -0,0 +1,61 @@
struct IremLROG017 : Interface {
static auto create(string id) -> Interface* {
if(id == "IREM-LROG017") return new IremLROG017;
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Readable<n8> characterROM;
Memory::Writable<n8> characterRAM;
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterROM, "character.rom");
Interface::load(characterRAM, "character.ram");
}
auto save() -> void override {
Interface::save(characterRAM, "character.ram");
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
return programROM.read(programBank << 15 | (n15)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x8000) return;
programBank = data.bit(0,3);
characterBank = data.bit(4,7);
}
auto addressCHR(n32 address) const -> n32 {
if(address < 0x1000) return 1 << 11 | (n11)address;
if(address < 0x1800) return 2 << 11 | (n11)address;
if(address < 0x2000) return 3 << 11 | (n11)address;
if(address < 0x2800) return 0 << 11 | (n11)address;
unreachable;
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address < 0x0800) return characterROM.read(characterBank << 11 | (n11)address);
if(address < 0x2800) return characterRAM.read(addressCHR(address));
if(address < 0x3000) return ppu.readCIRAM((n11)address);
return data;
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address < 0x0800) return;
if(address < 0x2800) return characterRAM.write(addressCHR(address), data);
if(address < 0x3000) return ppu.writeCIRAM((n11)address, data);
}
auto serialize(serializer& s) -> void override {
s(characterRAM);
s(programBank);
s(characterBank);
}
n4 programBank;
n4 characterBank;
};
+1 -1
View File
@@ -5,7 +5,7 @@ struct IremTAMS1 : Interface {
}
Memory::Readable<n8> programROM;
Memory::Writable<n8> characterROM;
Memory::Readable<n8> characterROM;
Memory::Writable<n8> characterRAM;
auto load() -> void override {
@@ -1,6 +1,10 @@
struct Jaleco_JF11_JF14 : Interface {
struct JalecoJF05 : Interface {
static auto create(string id) -> Interface* {
if(id == "JALECO-JF11-JF14") return new Jaleco_JF11_JF14;
if(id == "JALECO-JF-05") return new JalecoJF05;
if(id == "JALECO-JF-06") return new JalecoJF05;
if(id == "JALECO-JF-07") return new JalecoJF05;
if(id == "JALECO-JF-08") return new JalecoJF05;
if(id == "JALECO-JF-10") return new JalecoJF05;
return nullptr;
}
@@ -15,13 +19,12 @@ struct Jaleco_JF11_JF14 : Interface {
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
return programROM.read(programBank << 15 | (n15)address);
return programROM.read(address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x6000 || address > 0x7fff) return;
characterBank = data.bit(0,1);
programBank = data.bit(4,5);
characterBank = data.bit(0) << 1 | data.bit(1);
}
auto readCHR(n32 address, n8 data) -> n8 override {
@@ -30,8 +33,7 @@ struct Jaleco_JF11_JF14 : Interface {
return ppu.readCIRAM(address);
}
address = characterBank << 13 | (n13)address;
if(characterROM) return characterROM.read(address);
return data;
return characterROM.read(address);
}
auto writeCHR(n32 address, n8 data) -> void override {
@@ -41,16 +43,11 @@ struct Jaleco_JF11_JF14 : Interface {
}
}
auto power() -> void override {
}
auto serialize(serializer& s) -> void override {
s(mirror);
s(programBank);
s(characterBank);
}
n1 mirror; //0 = horizontal, 1 = vertical
n2 programBank;
n1 mirror;
n2 characterBank;
};
+70
View File
@@ -0,0 +1,70 @@
//todo: uPD7756 ADPCM unsupported
struct JalecoJF11 : Interface {
static auto create(string id) -> Interface* {
if(id == "JALECO-JF-11") return new JalecoJF11(Revision::JF11);
if(id == "JALECO-JF-13") return new JalecoJF11(Revision::JF13);
if(id == "JALECO-JF-14") return new JalecoJF11(Revision::JF14);
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Readable<n8> characterROM;
enum class Revision : u32 {
JF11,
JF13,
JF14,
} revision;
JalecoJF11(Revision revision) : revision(revision) {}
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterROM, "character.rom");
mirror = pak->attribute("mirror") == "vertical";
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
return programROM.read(programBank << 15 | (n15)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x6000 || address > 0x7fff) return;
if(revision == Revision::JF13 && address > 0x6fff) return;
programBank = data.bit(4,5);
if(revision == Revision::JF13) {
characterBank = data.bit(6) << 2 | data.bit(0,1);
} else {
characterBank = data.bit(0,3);
}
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) {
address = address >> !mirror & 0x0400 | (n10)address;
return ppu.readCIRAM(address);
}
address = characterBank << 13 | (n13)address;
return characterROM.read(address);
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) {
address = address >> !mirror & 0x0400 | (n10)address;
return ppu.writeCIRAM(address, data);
}
}
auto serialize(serializer& s) -> void override {
s(mirror);
s(programBank);
s(characterBank);
}
n1 mirror; //0 = horizontal, 1 = vertical
n2 programBank;
n4 characterBank;
};
+66
View File
@@ -0,0 +1,66 @@
struct JalecoJF16 : Interface {
static auto create(string id) -> Interface* {
if(id == "JALECO-JF-16") return new JalecoJF16(Revision::JF16);
if(id == "IREM-IF-12" ) return new JalecoJF16(Revision::IF12);
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Readable<n8> characterROM;
enum class Revision : u32 {
JF16,
IF12,
} revision;
JalecoJF16(Revision revision) : revision(revision) {}
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterROM, "character.rom");
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
n3 bank;
switch(address >> 14 & 1) {
case 0: bank = programBank; break;
case 1: bank = 7; break;
}
return programROM.read(bank << 14 | (n14)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x8000) return;
programBank = data.bit(0,2);
mirror = data.bit(3);
characterBank = data.bit(4,7);
}
auto addressCIRAM(n32 address) const -> n32 {
if(revision == Revision::IF12) {
return address >> !mirror & 0x0400 | (n10)address;
}
return mirror << 10 | (n10)address;
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
return characterROM.read(characterBank << 13 | (n13)address);
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
}
auto serialize(serializer& s) -> void override {
s(mirror);
s(programBank);
s(characterBank);
}
n1 mirror;
n3 programBank;
n4 characterBank;
};
+74
View File
@@ -0,0 +1,74 @@
//todo: uPD7756 ADPCM unsupported
struct JalecoJF17 : Interface {
static auto create(string id) -> Interface* {
if(id == "JALECO-JF-17") return new JalecoJF17(Revision::JF17);
if(id == "JALECO-JF-19") return new JalecoJF17(Revision::JF19);
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Readable<n8> characterROM;
enum class Revision : u32 {
JF17,
JF19,
} revision;
JalecoJF17(Revision revision) : revision(revision) {}
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterROM, "character.rom");
mirror = pak->attribute("mirror") == "vertical";
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
n4 bank;
switch(address >> 14 & 1) {
case 0: bank = (revision == Revision::JF17 ? programBank : (n4)0x0); break;
case 1: bank = (revision == Revision::JF19 ? programBank : (n4)0xf); break;
}
return programROM.read(bank << 14 | (n14)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x8000) return;
if(!programSelect && data.bit(7)) programBank = data.bit(0,3);
if(!characterSelect && data.bit(6)) characterBank = data.bit(0,3);
programSelect = data.bit(7);
characterSelect = data.bit(6);
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) {
address = address >> !mirror & 0x0400 | (n10)address;
return ppu.readCIRAM(address);
}
address = characterBank << 13 | (n13)address;
return characterROM.read(address);
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) {
address = address >> !mirror & 0x0400 | (n10)address;
return ppu.writeCIRAM(address, data);
}
}
auto serialize(serializer& s) -> void override {
s(mirror);
s(programSelect);
s(programBank);
s(characterSelect);
s(characterBank);
}
n1 mirror;
n1 programSelect;
n4 programBank;
n1 characterSelect;
n4 characterBank;
};
@@ -1,9 +1,14 @@
//JALECO-JF-(23A,24A,25,27B,29A,37,40)
//todo: uPD7756 ADPCM unsupported
struct JalecoJF : Interface {
struct JalecoJF23 : Interface {
static auto create(string id) -> Interface* {
if(id == "JALECO-JF") return new JalecoJF;
if(id == "JALECO-JF-23A") return new JalecoJF23;
if(id == "JALECO-JF-24A") return new JalecoJF23;
if(id == "JALECO-JF-25" ) return new JalecoJF23;
if(id == "JALECO-JF-27B") return new JalecoJF23;
if(id == "JALECO-JF-29A") return new JalecoJF23;
if(id == "JALECO-JF-37" ) return new JalecoJF23;
if(id == "JALECO-JF-40" ) return new JalecoJF23;
return nullptr;
}
+10 -1
View File
@@ -1,6 +1,7 @@
struct KonamiVRC2 : Interface {
static auto create(string id) -> Interface* {
if(id == "KONAMI-VRC-2") return new KonamiVRC2;
if(id == "KONAMI-VRC-2" ) return new KonamiVRC2(Revision::VRC2);
if(id == "KONAMI-VRC-2A") return new KonamiVRC2(Revision::VRC2A);
return nullptr;
}
@@ -9,6 +10,13 @@ struct KonamiVRC2 : Interface {
Memory::Readable<n8> characterROM;
Memory::Writable<n8> characterRAM;
enum class Revision : u32 {
VRC2,
VRC2A,
} revision;
KonamiVRC2(Revision revision) : revision(revision) {}
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(programRAM, "save.ram");
@@ -98,6 +106,7 @@ struct KonamiVRC2 : Interface {
auto addressCHR(n32 address) const -> n32 {
n8 bank = characterBank[address >> 10];
if(revision == Revision::VRC2A) bank >>= 1;
return bank << 10 | (n10)address;
}
+2
View File
@@ -43,6 +43,7 @@ struct KonamiVRC3 : Interface {
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x6000) return data;
if(address < 0x8000 && !programRAM) return data;
if(address < 0x8000) return programRAM.read((n13)address);
n4 bank = (address < 0xc000 ? programBank : (n4)0xf);
@@ -52,6 +53,7 @@ struct KonamiVRC3 : Interface {
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x6000) return;
if(address < 0x8000 && !programRAM) return;
if(address < 0x8000) return programRAM.write((n13)address, data);
switch(address & 0xf000) {
+2
View File
@@ -52,6 +52,7 @@ struct KonamiVRC4 : Interface {
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x6000) return data;
if(address < 0x8000 && !programRAM) return data;
if(address < 0x8000) return programRAM.read((n13)address);
n5 bank, banks = programROM.size() >> 13;
@@ -67,6 +68,7 @@ struct KonamiVRC4 : Interface {
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x6000) return;
if(address < 0x8000 && !programRAM) return;
if(address < 0x8000) return programRAM.write((n13)address, data);
bool a0 = address & pinA0;
+2
View File
@@ -147,12 +147,14 @@ struct KonamiVRC6 : Interface {
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x6000) return data;
if(address < 0x8000 && !programRAM) return data;
if(address < 0x8000) return programRAM.read((n13)address);
return programROM.read(addressPRG(address));
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x6000) return;
if(address < 0x8000 && !programRAM) return;
if(address < 0x8000) return programRAM.write((n13)address, data);
bool a0 = address & pinA0;
+19 -9
View File
@@ -16,6 +16,7 @@ struct KonamiVRC7 : Interface {
Interface::load(programRAM, "save.ram");
Interface::load(characterROM, "character.rom");
Interface::load(characterRAM, "character.ram");
pinA0 = 1 << pak->attribute("pinout/a0").natural();
stream = cartridge.node->append<Node::Audio::Stream>("YM2413");
stream->setChannels(1);
@@ -71,6 +72,7 @@ struct KonamiVRC7 : Interface {
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x6000) return data;
if(address < 0x8000 && !programRAM) return data;
if(address < 0x8000) return programRAM.read(address);
n8 bank;
@@ -86,29 +88,34 @@ struct KonamiVRC7 : Interface {
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x6000) return;
if(address < 0x8000 && !programRAM) return;
if(address < 0x8000) return programRAM.write(address, data);
bool a0 = address & pinA0;
address &= 0xf020;
address |= a0;
switch(address) {
case 0x8000: programBank[0] = data; break;
case 0x8010: programBank[1] = data; break;
case 0x8001: programBank[1] = data; break;
case 0x9000: programBank[2] = data; break;
case 0x9010: ym2413.address(data); break;
case 0x9030: ym2413.write(data); break;
case 0x9001: ym2413.address(data); break;
case 0x9021: ym2413.write(data); break;
case 0xa000: characterBank[0] = data; break;
case 0xa010: characterBank[1] = data; break;
case 0xa001: characterBank[1] = data; break;
case 0xb000: characterBank[2] = data; break;
case 0xb010: characterBank[3] = data; break;
case 0xb001: characterBank[3] = data; break;
case 0xc000: characterBank[4] = data; break;
case 0xc010: characterBank[5] = data; break;
case 0xc001: characterBank[5] = data; break;
case 0xd000: characterBank[6] = data; break;
case 0xd010: characterBank[7] = data; break;
case 0xd001: characterBank[7] = data; break;
case 0xe000:
if(disableFM && !data.bit(6)) ym2413.power(1);
mirror = data.bit(0,1);
disableFM = data.bit(6);
ramWritable = data.bit(7);
break;
case 0xe010:
case 0xe001:
irqLatch = data;
break;
case 0xf000:
@@ -121,7 +128,7 @@ struct KonamiVRC7 : Interface {
}
irqLine = 0;
break;
case 0xf010:
case 0xf001:
irqEnable = irqAcknowledge;
irqLine = 0;
break;
@@ -193,4 +200,7 @@ struct KonamiVRC7 : Interface {
i16 irqScalar;
n1 irqLine;
n6 divider;
//unserialized:
n8 pinA0;
};
+75
View File
@@ -0,0 +1,75 @@
struct MltAction52 : Interface {
static auto create(string id) -> Interface* {
if(id == "MLT-ACTION52") return new MltAction52();
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Readable<n8> characterROM;
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterROM, "character.rom");
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
n2 chipOffset;
switch(programChip) {
case 0: chipOffset = 0; break;
case 1: chipOffset = 1; break;
case 2: return data;
case 3: chipOffset = 2; break;
}
bool region = address.bit(14);
n5 bank = programMode ? programBank : (n5)(programBank & ~1 | region);
address = chipOffset << 19 | bank << 14 | (n14)address;
return programROM.read(address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x8000) return;
characterBank = address.bit(0,3) << 2 | data.bit(0,1);
programMode = address.bit(5);
programBank = address.bit(6,10);
programChip = address.bit(11,12);
mirror = address.bit(13);
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) {
address = address >> mirror & 0x0400 | (n10)address;
return ppu.readCIRAM(address);
}
address = characterBank << 13 | (n13)address;
if(characterROM) return characterROM.read(address);
return data;
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) {
address = address >> mirror & 0x0400 | (n10)address;
return ppu.writeCIRAM(address, data);
}
}
auto power() -> void override {
}
auto serialize(serializer& s) -> void override {
s(characterBank);
s(programMode);
s(programBank);
s(programChip);
s(mirror);
}
n6 characterBank;
n1 programMode;
n5 programBank;
n2 programChip;
n1 mirror;
};
+136
View File
@@ -0,0 +1,136 @@
struct Namco118 : Interface {
static auto create(string id) -> Interface* {
if(id == "NAMCO-118" ) return new Namco118(Revision::N118);
if(id == "NAMCO-3425") return new Namco118(Revision::N3425);
if(id == "NAMCO-3433") return new Namco118(Revision::N3433);
if(id == "NAMCO-3446") return new Namco118(Revision::N3446);
if(id == "NAMCO-3453") return new Namco118(Revision::N3453);
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Readable<n8> characterROM;
Memory::Writable<n8> characterCIRAM;
enum class Revision : u32 {
N118,
N3425,
N3433,
N3446,
N3453,
} revision;
Namco118(Revision revision) : revision(revision) {}
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterROM, "character.rom");
Interface::load(characterCIRAM, "character.ram");
mirror = pak->attribute("mirror") == "vertical";
}
auto save() -> void override {
Interface::save(characterCIRAM, "character.ram");
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
n4 bank;
switch(address >> 13 & 3) {
case 0: bank = programBank[0]; break;
case 1: bank = programBank[1]; break;
case 2: bank = 0x0e; break;
case 3: bank = 0x0f; break;
}
return programROM.read(bank << 13 | (n13)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address >= 0x8000) nametableBank = data.bit(6);
switch(address & 0xe001) {
case 0x8000:
bankSelect = data.bit(0,2);
break;
case 0x8001:
switch(bankSelect) {
case 0: characterBank[0] = data >> 1; break;
case 1: characterBank[1] = data >> 1; break;
case 2: characterBank[2] = data; break;
case 3: characterBank[3] = data; break;
case 4: characterBank[4] = data; break;
case 5: characterBank[5] = data; break;
case 6: programBank[0] = data; break;
case 7: programBank[1] = data; break;
}
break;
}
}
auto addressCHR(n32 address) const -> n32 {
n32 chrAddress = 0;
if(revision == Revision::N3446) {
switch(address) {
case 0x0000 ... 0x07ff: chrAddress = characterBank[2] << 11 | (n11)address; break;
case 0x0800 ... 0x0fff: chrAddress = characterBank[3] << 11 | (n11)address; break;
case 0x1000 ... 0x17ff: chrAddress = characterBank[4] << 11 | (n11)address; break;
case 0x1800 ... 0xffff: chrAddress = characterBank[5] << 11 | (n11)address; break;
}
} else {
switch(address) {
case 0x0000 ... 0x07ff: chrAddress = characterBank[0] << 11 | (n11)address; break;
case 0x0800 ... 0x0fff: chrAddress = characterBank[1] << 11 | (n11)address; break;
case 0x1000 ... 0x13ff: chrAddress = characterBank[2] << 10 | (n10)address; break;
case 0x1400 ... 0x17ff: chrAddress = characterBank[3] << 10 | (n10)address; break;
case 0x1800 ... 0x1bff: chrAddress = characterBank[4] << 10 | (n10)address; break;
case 0x1c00 ... 0x1fff: chrAddress = characterBank[5] << 10 | (n10)address; break;
}
chrAddress |= (address & 0x1000) << 4;
}
return chrAddress;
}
auto addressCIRAM(n32 address) const -> n32 {
if(revision == Revision::N3425) {
n6 bank = characterBank[address >> 11 & 1];
return bank.bit(4) << 10 | (n10)address;
}
if(revision == Revision::N3453) {
return nametableBank << 10 | (n10)address;
}
return address >> !mirror & 0x0400 | (n10)address;
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) {
if(!characterCIRAM) return ppu.readCIRAM(addressCIRAM(address));
if(address & 0x0800) return characterCIRAM.read((n11)address);
return ppu.readCIRAM((n11)address);
}
return characterROM.read(addressCHR(address));
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) {
if(!characterCIRAM) return ppu.writeCIRAM(addressCIRAM(address), data);
if(address & 0x0800) return characterCIRAM.write((n11)address, data);
return ppu.writeCIRAM((n11)address, data);
}
}
auto serialize(serializer& s) -> void override {
s(characterCIRAM);
s(mirror);
s(bankSelect);
s(programBank);
s(characterBank);
s(nametableBank);
}
n1 mirror;
n3 bankSelect;
n4 programBank[2];
n6 characterBank[6];
n1 nametableBank;
};
+148
View File
@@ -0,0 +1,148 @@
//todo: sound
struct Namco163 : Interface {
static auto create(string id) -> Interface* {
if(id == "NAMCO-163") return new Namco163;
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Writable<n8> programRAM;
Memory::Readable<n8> characterROM;
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(programRAM, "save.ram");
Interface::load(characterROM, "character.rom");
}
auto save() -> void override {
Interface::save(programRAM, "save.ram");
}
auto main() -> void override {
cpu.irqLine(irqLine);
if(irqEnable) {
if(irqCounter != 0x7fff && ++irqCounter == 0x7fff) irqLine = 1;
}
tick();
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x4800) return data;
if(address < 0x5000) return data;
//if(address < 0x5000) return soundRAM.read(soundAddress);
if(address < 0x6000) {
if(address < 0x5800) return irqCounter.bit(0,7);
if(address < 0x6000) return irqCounter.bit(8,14) | irqEnable << 7;
}
if(address < 0x8000) {
if(!programRAM) return data;
return programRAM.read((n13)address);
}
n6 bank;
switch(address >> 13 & 3) {
case 0: bank = programBank[0]; break;
case 1: bank = programBank[1]; break;
case 2: bank = programBank[2]; break;
case 3: bank = 0x3f; break;
}
return programROM.read(bank << 13 | (n13)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address >= 0x6000 && address < 0x8000) {
if(!programRAM) return;
if(!ramEnable[address >> 11 & 3]) return;
return programRAM.write((n13)address, data);
}
switch(address & 0xf800) {
case 0x4800:
//soundRAM.write(soundAddres, data);
break;
case 0x5000:
irqCounter.bit(0,7) = data;
irqLine = 0;
break;
case 0x5800:
irqCounter.bit(8,14) = data.bit(0,6);
irqEnable = data.bit(7);
irqLine = 0;
break;
case 0x8000: characterBank[0] = data; break;
case 0x8800: characterBank[1] = data; break;
case 0x9000: characterBank[2] = data; break;
case 0x9800: characterBank[3] = data; break;
case 0xa000: characterBank[4] = data; break;
case 0xa800: characterBank[5] = data; break;
case 0xb000: characterBank[6] = data; break;
case 0xb800: characterBank[7] = data; break;
case 0xc000: characterBank[8] = data; break;
case 0xc800: characterBank[9] = data; break;
case 0xd000: characterBank[10] = data; break;
case 0xd800: characterBank[11] = data; break;
case 0xe000:
programBank[0] = data.bit(0,5);
//soundEnable = !data.bit(6);
break;
case 0xe800:
programBank[1] = data.bit(0,5);
ciramEnable[0] = !data.bit(6);
ciramEnable[1] = !data.bit(7);
break;
case 0xf000:
programBank[2] = data.bit(0,5);
break;
case 0xf800:
ramEnable[0] = data.bit(4,7) == 4 && !data.bit(0);
ramEnable[1] = data.bit(4,7) == 4 && !data.bit(1);
ramEnable[2] = data.bit(4,7) == 4 && !data.bit(2);
ramEnable[3] = data.bit(4,7) == 4 && !data.bit(3);
//soundAddress = data.bit(0,6);
//soundRepeat = data.bit(7);
break;
}
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) address.bit(12) = 0;
bool ciramSelect = address & 0x2000 || ciramEnable[address >> 12 & 1];
n8 bank = characterBank[address >> 10];
if(bank >= 0xe0 && ciramSelect) {
return ppu.readCIRAM(bank.bit(0) << 10 | (n10)address);
}
return characterROM.read(bank << 10 | (n10)address);
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) address.bit(12) = 0;
bool ciramSelect = address & 0x2000 || ciramEnable[address >> 12 & 1];
n8 bank = characterBank[address >> 10];
if(bank >= 0xe0 && ciramSelect) {
return ppu.writeCIRAM(bank.bit(0) << 10 | (n10)address, data);
}
}
auto serialize(serializer& s) -> void override {
s(programRAM);
s(ramEnable);
s(ciramEnable);
s(programBank);
s(characterBank);
s(irqCounter);
s(irqEnable);
s(irqLine);
}
n1 ramEnable[4];
n1 ciramEnable[2];
n6 programBank[3];
n8 characterBank[12];
n15 irqCounter;
n1 irqEnable;
n1 irqLine;
};
+108
View File
@@ -0,0 +1,108 @@
struct Namco340 : Interface {
static auto create(string id) -> Interface* {
if(id == "NAMCO-175") return new Namco340(Revision::N175);
if(id == "NAMCO-340") return new Namco340(Revision::N340);
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Writable<n8> programRAM;
Memory::Readable<n8> characterROM;
enum class Revision : u32 {
N175,
N340,
} revision;
Namco340(Revision revision) : revision(revision) {}
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(programRAM, "save.ram");
Interface::load(characterROM, "character.rom");
staticMirror = pak->attribute("mirror") == "vertical";
}
auto save() -> void override {
Interface::save(programRAM, "save.ram");
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x6000) return data;
if(address < 0x8000) {
if(!ramEnable || !programRAM) return data;
return programRAM.read((n13)address);
}
n6 bank;
switch(address >> 13 & 3) {
case 0: bank = programBank[0]; break;
case 1: bank = programBank[1]; break;
case 2: bank = programBank[2]; break;
case 3: bank = 0x3f; break;
}
return programROM.read(bank << 13 | (n13)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x6000) return;
if(address < 0x8000) {
if(!ramEnable || !programRAM) return;
return programRAM.write((n13)address, data);
}
switch(address & 0xf800) {
case 0x8000: characterBank[0] = data; break;
case 0x8800: characterBank[1] = data; break;
case 0x9000: characterBank[2] = data; break;
case 0x9800: characterBank[3] = data; break;
case 0xa000: characterBank[4] = data; break;
case 0xa800: characterBank[5] = data; break;
case 0xb000: characterBank[6] = data; break;
case 0xb800: characterBank[7] = data; break;
case 0xc000: ramEnable = data.bit(0); break;
case 0xe000: programBank[0] = data.bit(0,5); mirror = data.bit(6,7); break;
case 0xe800: programBank[1] = data.bit(0,5); break;
case 0xf000: programBank[2] = data.bit(0,5); break;
}
}
auto addressCIRAM(n32 address) const -> n32 {
if(revision == Revision::N175) {
return address >> !staticMirror & 0x0400 | (n10)address;
}
switch(mirror) {
case 0: return 0x0000 | (n10)address;
case 1: return address >> 0 & 0x0400 | (n10)address;
case 2: return 0x0400 | (n10)address;
case 3: return address >> 1 & 0x0400 | (n10)address;
}
unreachable;
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
n8 bank = characterBank[address >> 10 & 7];
return characterROM.read(bank << 10 | (n10)address);
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
}
auto serialize(serializer& s) -> void override {
s(programRAM);
s(mirror);
s(staticMirror);
s(ramEnable);
s(programBank);
s(characterBank);
}
n2 mirror;
n1 staticMirror;
n1 ramEnable;
n6 programBank[3];
n8 characterBank[8];
};
+2 -2
View File
@@ -66,7 +66,7 @@ struct Sunsoft5B : Interface {
bank &= 0x3f;
if(ramSelect) {
if(!ramEnable) return data;
if(!ramEnable || !programRAM) return data;
return programRAM.read((n13)address);
}
@@ -76,7 +76,7 @@ struct Sunsoft5B : Interface {
auto writePRG(n32 address, n8 data) -> void override {
if((address & 0xe000) == 0x6000) {
programRAM.write((n13)address, data);
if(programRAM) programRAM.write((n13)address, data);
}
if(address == 0x8000) {
+80
View File
@@ -0,0 +1,80 @@
struct TaitoTC0190 : Interface {
static auto create(string id) -> Interface* {
if(id == "TAITO-TC0190") return new TaitoTC0190;
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Readable<n8> characterROM;
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterROM, "character.rom");
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
n6 bank;
switch(address >> 13 & 3) {
case 0: bank = programBank[0]; break;
case 1: bank = programBank[1]; break;
case 2: bank = 0x3e; break;
case 3: bank = 0x3f; break;
}
return programROM.read(bank << 13 | (n13)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x8000) return;
switch(address & 0xa003) {
case 0x8000:
programBank[0] = data.bit(0,5);
mirror = data.bit(6);
break;
case 0x8001:
programBank[1] = data.bit(0,5);
break;
case 0x8002: characterBank[0] = data; break;
case 0x8003: characterBank[1] = data; break;
case 0xa000: characterBank[2] = data; break;
case 0xa001: characterBank[3] = data; break;
case 0xa002: characterBank[4] = data; break;
case 0xa003: characterBank[5] = data; break;
}
}
auto addressCHR(n32 address) const -> n32 {
if(address <= 0x07ff) return characterBank[0] << 11 | (n11)address;
if(address <= 0x0fff) return characterBank[1] << 11 | (n11)address;
if(address <= 0x13ff) return characterBank[2] << 10 | (n10)address;
if(address <= 0x17ff) return characterBank[3] << 10 | (n10)address;
if(address <= 0x1bff) return characterBank[4] << 10 | (n10)address;
if(address <= 0x1fff) return characterBank[5] << 10 | (n10)address;
unreachable;
}
auto addressCIRAM(n32 address) const -> n32 {
return address >> mirror & 0x0400 | (n10)address;
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
return characterROM.read(addressCHR(address));
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
}
auto serialize(serializer& s) -> void override {
s(mirror);
s(programBank);
s(characterBank);
}
n1 mirror;
n6 programBank[2];
n8 characterBank[6];
};
+130
View File
@@ -0,0 +1,130 @@
struct TaitoTC0690 : Interface {
static auto create(string id) -> Interface* {
if(id == "TAITO-TC0690") return new TaitoTC0690;
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Readable<n8> characterROM;
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(characterROM, "character.rom");
}
auto main() -> void override {
if(irqDelay) irqDelay--;
if(irqLineDelay && --irqLineDelay == 0) irqLine = 1;
cpu.irqLine(irqLine);
tick();
}
auto irqTest(n16 address) -> void {
if(!(characterAddress & 0x1000) && (address & 0x1000)) {
if(irqDelay == 0) {
if(irqCounter == 0) {
irqCounter = irqLatch + 1;
}
if(--irqCounter == 0) {
if(irqEnable) irqLineDelay = 6;
}
}
irqDelay = 6;
}
characterAddress = address;
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x8000) return data;
n6 bank;
switch(address >> 13 & 3) {
case 0: bank = programBank[0]; break;
case 1: bank = programBank[1]; break;
case 2: bank = 0x3e; break;
case 3: bank = 0x3f; break;
}
return programROM.read(bank << 13 | (n13)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x8000) return;
switch(address & 0xe003) {
case 0x8000: case 0x8001:
programBank[address & 1] = data.bit(0,5);
break;
case 0x8002: characterBank[0] = data; break;
case 0x8003: characterBank[1] = data; break;
case 0xa000: characterBank[2] = data; break;
case 0xa001: characterBank[3] = data; break;
case 0xa002: characterBank[4] = data; break;
case 0xa003: characterBank[5] = data; break;
case 0xc000:
irqLatch = ~data;
break;
case 0xc001:
irqCounter = 0;
break;
case 0xc002:
irqEnable = 1;
break;
case 0xc003:
irqEnable = 0;
irqLine = 0;
break;
case 0xe000:
mirror = data.bit(6);
break;
}
}
auto addressCHR(n32 address) const -> n32 {
if(address <= 0x07ff) return characterBank[0] << 11 | (n11)address;
if(address <= 0x0fff) return characterBank[1] << 11 | (n11)address;
if(address <= 0x13ff) return characterBank[2] << 10 | (n10)address;
if(address <= 0x17ff) return characterBank[3] << 10 | (n10)address;
if(address <= 0x1bff) return characterBank[4] << 10 | (n10)address;
if(address <= 0x1fff) return characterBank[5] << 10 | (n10)address;
unreachable;
}
auto addressCIRAM(n32 address) const -> n32 {
return address >> mirror & 0x0400 | (n10)address;
}
auto readCHR(n32 address, n8 data) -> n8 override {
irqTest(address);
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
return characterROM.read(addressCHR(address));
}
auto writeCHR(n32 address, n8 data) -> void override {
irqTest(address);
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
}
auto serialize(serializer& s) -> void override {
s(mirror);
s(programBank);
s(characterBank);
s(irqLatch);
s(irqCounter);
s(irqEnable);
s(irqDelay);
s(irqLineDelay);
s(irqLine);
s(characterAddress);
}
n1 mirror;
n6 programBank[2];
n8 characterBank[6];
n8 irqLatch;
n8 irqCounter;
n1 irqEnable;
n8 irqDelay;
n8 irqLineDelay;
n1 irqLine;
n16 characterAddress;
};
+110
View File
@@ -0,0 +1,110 @@
struct TaitoX1005 : Interface {
static auto create(string id) -> Interface* {
if(id == "TAITO-X1-005" ) return new TaitoX1005(Revision::X1005);
if(id == "TAITO-X1-005A") return new TaitoX1005(Revision::X1005A);
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Writable<n8> programRAM;
Memory::Readable<n8> characterROM;
enum class Revision : u32 {
X1005,
X1005A,
} revision;
TaitoX1005(Revision revision) : revision(revision) {}
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(programRAM, "save.ram");
Interface::load(characterROM, "character.rom");
}
auto save() -> void override {
Interface::save(programRAM, "save.ram");
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address < 0x7f00) return data;
if(address < 0x8000) {
if(!ramEnable) return data;
return programRAM.read((n7)address);
}
n6 bank;
switch(address >> 13 & 3) {
case 0: bank = programBank[0]; break;
case 1: bank = programBank[1]; break;
case 2: bank = programBank[2]; break;
case 3: bank = 0x3f; break;
}
return programROM.read(bank << 13 | (n13)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address >= 0x7f00 && address < 0x8000) {
if(!ramEnable) return;
return programRAM.write((n7)address, data);
}
switch(address) {
case 0x7ef0 ... 0x7ef1:
characterBank[address & 1] = data >> 1;
nametableBank[address & 1] = data.bit(7);
break;
case 0x7ef2: characterBank[2] = data; break;
case 0x7ef3: characterBank[3] = data; break;
case 0x7ef4: characterBank[4] = data; break;
case 0x7ef5: characterBank[5] = data; break;
case 0x7ef6 ... 0x7ef7: mirror = data.bit(0); break;
case 0x7ef8 ... 0x7ef9: ramEnable = data == 0xa3; break;
case 0x7efa ... 0x7efb: programBank[0] = data.bit(0,5); break;
case 0x7efc ... 0x7efd: programBank[1] = data.bit(0,5); break;
case 0x7efe ... 0x7eff: programBank[2] = data.bit(0,5); break;
}
}
auto addressCHR(n32 address) const -> n32 {
if(address <= 0x07ff) return characterBank[0] << 11 | (n11)address;
if(address <= 0x0fff) return characterBank[1] << 11 | (n11)address;
if(address <= 0x13ff) return characterBank[2] << 10 | (n10)address;
if(address <= 0x17ff) return characterBank[3] << 10 | (n10)address;
if(address <= 0x1bff) return characterBank[4] << 10 | (n10)address;
if(address <= 0x1fff) return characterBank[5] << 10 | (n10)address;
unreachable;
}
auto addressCIRAM(n32 address) const -> n32 {
if(revision == Revision::X1005A) {
return nametableBank[address >> 12 & 1] << 10 | (n10)address;
}
return address >> !mirror & 0x0400 | (n10)address;
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
return characterROM.read(addressCHR(address));
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
}
auto serialize(serializer& s) -> void override {
s(programRAM);
s(mirror);
s(ramEnable);
s(programBank);
s(characterBank);
s(nametableBank);
}
n1 mirror;
n1 ramEnable;
n6 programBank[3];
n8 characterBank[6];
n1 nametableBank[2];
};
+102
View File
@@ -0,0 +1,102 @@
struct TaitoX1017 : Interface {
static auto create(string id) -> Interface* {
if(id == "TAITO-X1-017") return new TaitoX1017;
return nullptr;
}
Memory::Readable<n8> programROM;
Memory::Writable<n8> programRAM;
Memory::Readable<n8> characterROM;
auto load() -> void override {
Interface::load(programROM, "program.rom");
Interface::load(programRAM, "save.ram");
Interface::load(characterROM, "character.rom");
}
auto save() -> void override {
Interface::save(programRAM, "save.ram");
}
auto readPRG(n32 address, n8 data) -> n8 override {
if(address >= 0x6000 && address < 0x7400) {
if(!ramEnable[address >> 11 & 3]) return data;
return programRAM.read((n13)address);
}
if(address < 0x8000) return data;
n6 bank;
switch(address >> 13 & 3) {
case 0: bank = programBank[0]; break;
case 1: bank = programBank[1]; break;
case 2: bank = programBank[2]; break;
case 3: bank = 0x3f; break;
}
return programROM.read(bank << 13 | (n13)address);
}
auto writePRG(n32 address, n8 data) -> void override {
if(address >= 0x6000 && address < 0x7400) {
if(!ramEnable[address >> 11 & 3]) return;
return programRAM.write((n13)address, data);
}
switch(address) {
case 0x7ef0: characterBank[0] = data >> 1; break;
case 0x7ef1: characterBank[1] = data >> 1; break;
case 0x7ef2: characterBank[2] = data; break;
case 0x7ef3: characterBank[3] = data; break;
case 0x7ef4: characterBank[4] = data; break;
case 0x7ef5: characterBank[5] = data; break;
case 0x7ef6:
mirror = data.bit(0);
characterMode = data.bit(1);
break;
case 0x7ef7: ramEnable[0] = data == 0xca; break;
case 0x7ef8: ramEnable[1] = data == 0x69; break;
case 0x7ef9: ramEnable[2] = data == 0x84; break;
case 0x7efa: programBank[0] = data >> 2; break;
case 0x7efb: programBank[1] = data >> 2; break;
case 0x7efc: programBank[2] = data >> 2; break;
}
}
auto addressCHR(n32 address) const -> n32 {
if(characterMode) address ^= 0x1000;
if(address <= 0x07ff) return characterBank[0] << 11 | (n11)address;
if(address <= 0x0fff) return characterBank[1] << 11 | (n11)address;
if(address <= 0x13ff) return characterBank[2] << 10 | (n10)address;
if(address <= 0x17ff) return characterBank[3] << 10 | (n10)address;
if(address <= 0x1bff) return characterBank[4] << 10 | (n10)address;
if(address <= 0x1fff) return characterBank[5] << 10 | (n10)address;
unreachable;
}
auto addressCIRAM(n32 address) const -> n32 {
return address >> !mirror & 0x0400 | (n10)address;
}
auto readCHR(n32 address, n8 data) -> n8 override {
if(address & 0x2000) return ppu.readCIRAM(addressCIRAM(address));
return characterROM.read(addressCHR(address));
}
auto writeCHR(n32 address, n8 data) -> void override {
if(address & 0x2000) return ppu.writeCIRAM(addressCIRAM(address), data);
}
auto serialize(serializer& s) -> void override {
s(programRAM);
s(mirror);
s(ramEnable);
s(programBank);
s(characterBank);
s(characterMode);
}
n1 mirror;
n1 ramEnable[3];
n6 programBank[3];
n8 characterBank[6];
n1 characterMode;
};
+3 -3
View File
@@ -138,6 +138,7 @@ auto PPU::renderScanline() -> void {
//257-320
for(u32 sprite : range(8)) {
if(enable()) io.oamAddress = 0;
u32 nametable = loadCHR(0x2000 | (n12)io.v.address);
step(1);
@@ -198,9 +199,8 @@ auto PPU::renderScanline() -> void {
//337-338
loadCHR(0x2000 | (n12)io.v.address);
step(1);
bool skip = enable() && io.field == 1 && io.ly == vlines() - 1;
step(1);
bool skip = !Region::PAL() && enable() && io.field == 1 && io.ly == vlines() - 1;
step(2);
//339
loadCHR(0x2000 | (n12)io.v.address);
+2 -8
View File
@@ -20,15 +20,8 @@ auto APU::unload() -> void {
}
auto APU::main() -> void {
//handle a bus switching request from the CPU
if(state.resLine) {
//switching requires a minimum of 9 cycles to allow the 68K to read back unavailable bus state
if(state.busreqLine && !state.busreqLatch) step(9);
state.busreqLatch = state.busreqLine;
}
//stall the APU until the CPU relinquishes control of the bus
if(!state.resLine || !busownerAPU()) {
if(!state.resLine || state.busreqLatch) {
return step(1);
}
@@ -50,6 +43,7 @@ auto APU::main() -> void {
auto APU::step(u32 clocks) -> void {
Thread::step(clocks);
state.busreqLatch = busownerCPU() ? 1 : 0;
Thread::synchronize(cpu, vdp, vdp.psg, opn2);
}
+6 -2
View File
@@ -21,8 +21,12 @@ struct APU : Z80, Z80::Bus, Thread {
} debugger;
auto synchronizing() const -> bool override { return scheduler.synchronizing(); }
auto busownerAPU() const -> bool { return (state.resLine & state.busreqLatch) == 0; } //Z80 has bus access
auto busownerCPU() const -> bool { return (state.resLine & state.busreqLatch) == 1; } //68K has bus access
auto busgrantedCPU() const -> bool { return state.resLine & state.busreqLatch; }
auto busownerCPU() const -> bool { return state.resLine & state.busreqLine; }
// Note: bus ownership is flagged according to line state, since it could be too slow
// to wait for latching with only instruction-level granularity on the Z80 emulation.
// At worst, a positive signal would indicate bus grant is imminent, which should be safe.
// (fixes Arkagis Revolution - Trial Version [Rev 00])
//z80.cpp
auto load(Node::Object) -> void;
+1 -1
View File
@@ -89,7 +89,7 @@ auto APU::writeExternal(n24 address, n8 data) -> void {
auto APU::in(n16 address) -> n8 {
//unused on Mega Drive
return 0x00;
return 0xff;
}
auto APU::out(n16 address, n8 data) -> void {
+2 -2
View File
@@ -27,7 +27,7 @@ inline auto Bus::read(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
}
if(address >= 0xa00000 && address <= 0xa0ffff) {
if(!apu.busownerCPU()) return data;
if(!apu.busgrantedCPU()) return data;
if(cpu.active()) cpu.wait(3); //todo: inaccurate approximation of Z80 RAM access delay
address.bit(15) = 0; //a080000-a0ffff mirrors a00000-a07fff
//word reads load the even input byte into both output bytes
@@ -85,7 +85,7 @@ inline auto Bus::write(n1 upper, n1 lower, n24 address, n16 data) -> void {
}
if(address >= 0xa00000 && address <= 0xa0ffff) {
if(!apu.busownerCPU()) return;
if(!apu.busgrantedCPU()) return;
if(cpu.active()) cpu.wait(3); //todo: inaccurate approximation of Z80 RAM access delay
address.bit(15) = 0; //a08000-a0ffff mirrors a00000-a07fff
//word writes store the upper input byte into the lower output byte
+6 -5
View File
@@ -4,15 +4,16 @@ struct Banked : Interface {
Memory::Writable<n16> wram;
Memory::Writable<n8 > uram;
Memory::Writable<n8 > lram;
u32 sramAddr, sramSize;
M24C m24c;
auto load() -> void override {
Interface::load(rom, "program.rom");
if(auto fp = pak->read("save.ram")) {
Interface::load(wram, uram, lram, "save.ram");
Interface::load(sramAddr, sramSize, wram, uram, lram, "save.ram");
}
if(auto fp = pak->read("save.eeprom")) {
Interface::load(m24c, "save.eeprom");
Interface::load(sramAddr, sramSize, m24c, "save.eeprom");
rsda = fp->attribute("rsda").natural();
wsda = fp->attribute("wsda").natural();
wscl = fp->attribute("wscl").natural();
@@ -29,7 +30,7 @@ struct Banked : Interface {
}
auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16 override {
if(address >= 0x200000) {
if(address >= sramAddr && address < sramAddr+sramSize) {
if(wram && ramEnable) {
return wram[address >> 1];
}
@@ -62,7 +63,7 @@ struct Banked : Interface {
//emulating ramWritable will break commercial software:
//it does not appear that many (any?) games actually connect $a130f1.d1 to /WE;
//hence RAM ends up always being writable, and many games fail to set d1=1
if(address >= 0x200000) {
if(address >= sramAddr && address < sramAddr+sramSize) {
if(wram && ramEnable) {
if(upper) wram[address >> 1].byte(1) = data.byte(1);
if(lower) wram[address >> 1].byte(0) = data.byte(0);
@@ -80,7 +81,7 @@ struct Banked : Interface {
}
if(m24c) {
if(rom.size() * 2 > 0x200000 && upper && lower) {
if(rom.size() * 2 > sramAddr && upper && lower) {
eepromEnable = !data.bit(0);
return;
}
+6 -2
View File
@@ -26,11 +26,13 @@ auto Interface::load(Memory::Readable<n16>& rom, string name) -> bool {
return false;
}
auto Interface::load(Memory::Writable<n16>& wram, Memory::Writable<n8>& uram, Memory::Writable<n8>& lram, string name) -> bool {
auto Interface::load(u32& addr, u32& size, Memory::Writable<n16>& wram, Memory::Writable<n8>& uram, Memory::Writable<n8>& lram, string name) -> bool {
wram.reset();
uram.reset();
lram.reset();
if(auto fp = pak->read(name)) {
addr = fp->attribute("address").natural();
size = fp->size() << 1;
auto mode = fp->attribute("mode");
if(mode == "word") {
wram.allocate(fp->size() >> 1);
@@ -49,10 +51,12 @@ auto Interface::load(Memory::Writable<n16>& wram, Memory::Writable<n8>& uram, Me
return false;
}
auto Interface::load(M24C& m24c, string name) -> bool {
auto Interface::load(u32& addr, u32& size, M24C& m24c, string name) -> bool {
m24c.reset();
if(auto fp = pak->read(name)) {
auto mode = fp->attribute("mode");
addr = fp->attribute("address").natural();
size = 1;
m24c.reset();
if(mode == "X24C01" ) m24c.load(M24C::Type::X24C01 );
if(mode == "M24C01" ) m24c.load(M24C::Type::M24C01 );
+2 -2
View File
@@ -21,8 +21,8 @@ struct Interface {
virtual auto serialize(serializer&) -> void {}
auto load(Memory::Readable<n16>& rom, string name) -> bool;
auto load(Memory::Writable<n16>& wram, Memory::Writable<n8>& uram, Memory::Writable<n8>& lram, string name) -> bool;
auto load(M24C& m24c, string name) -> bool;
auto load(u32& addr, u32 &size, Memory::Writable<n16>& wram, Memory::Writable<n8>& uram, Memory::Writable<n8>& lram, string name) -> bool;
auto load(u32& addr, u32 &size, M24C& m24c, string name) -> bool;
auto save(Memory::Writable<n16>& wram, Memory::Writable<n8>& uram, Memory::Writable<n8>& lram, string name) -> bool;
auto save(M24C& m24c, string name) -> bool;
+9 -9
View File
@@ -4,15 +4,16 @@ struct Linear : Interface {
Memory::Writable<n16> wram;
Memory::Writable<n8 > uram;
Memory::Writable<n8 > lram;
u32 sramAddr, sramSize;
M24C m24c;
auto load() -> void override {
Interface::load(rom, "program.rom");
if(auto fp = pak->read("save.ram")) {
Interface::load(wram, uram, lram, "save.ram");
Interface::load(sramAddr, sramSize, wram, uram, lram, "save.ram");
}
if(auto fp = pak->read("save.eeprom")) {
Interface::load(m24c, "save.eeprom");
Interface::load(sramAddr, sramSize, m24c, "save.eeprom");
rsda = fp->attribute("rsda").natural();
wsda = fp->attribute("wsda").natural();
wscl = fp->attribute("wscl").natural();
@@ -29,7 +30,7 @@ struct Linear : Interface {
}
auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16 override {
if(address >= 0x200000) {
if(address >= sramAddr && address < sramAddr+sramSize) {
if(wram && ramEnable) {
return wram[address >> 1];
}
@@ -60,7 +61,7 @@ struct Linear : Interface {
//emulating ramWritable will break commercial software:
//it does not appear that many (any?) games actually connect $a130f1.d1 to /WE;
//hence RAM ends up always being writable, and many games fail to set d1=1
if(address >= 0x200000) {
if(address >= sramAddr && address < sramAddr+sramSize) {
if(wram && ramEnable) {
if(upper) wram[address >> 1].byte(1) = data.byte(1);
if(lower) wram[address >> 1].byte(0) = data.byte(0);
@@ -78,7 +79,7 @@ struct Linear : Interface {
}
if(m24c) {
if(rom.size() * 2 > 0x200000 && upper && lower) {
if(rom.size() * 2 > sramAddr && upper && lower) {
eepromEnable = !data.bit(0);
return;
}
@@ -98,17 +99,16 @@ struct Linear : Interface {
}
auto writeIO(n1 upper, n1 lower, n24 address, n16 data) -> void override {
if(!lower) return; //todo: unconfirmed
if(address == 0xa130f0) {
if(rom.size() * 2 > sramAddr && address == 0xa130f0) {
ramEnable = data.bit(0);
ramWritable = data.bit(1);
}
}
auto power(bool reset) -> void override {
ramEnable = 1;
ramEnable = rom.size() * 2 <= sramAddr;
ramWritable = 1;
eepromEnable = rom.size() * 2 <= 0x200000;
eepromEnable = rom.size() * 2 <= sramAddr;
m24c.power();
}
+5 -4
View File
@@ -3,6 +3,7 @@ struct Mega32X : Interface {
Memory::Writable<n16> wram;
Memory::Writable<n8 > uram;
Memory::Writable<n8 > lram;
u32 sramAddr, sramSize;
M24C m24c;
auto load() -> void override {
@@ -14,11 +15,11 @@ struct Mega32X : Interface {
}
if(auto fp = pak->read("save.ram")) {
Interface::load(wram, uram, lram, "save.ram");
Interface::load(sramAddr, sramSize, wram, uram, lram, "save.ram");
}
if(auto fp = pak->read("save.eeprom")) {
Interface::load(m24c, "save.eeprom");
Interface::load(sramAddr, sramSize, m24c, "save.eeprom");
rsda = fp->attribute("rsda").natural();
wsda = fp->attribute("wsda").natural();
wscl = fp->attribute("wscl").natural();
@@ -36,7 +37,7 @@ struct Mega32X : Interface {
}
auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16 override {
if(address >= 0x200000) {
if(address >= sramAddr && address < sramAddr+sramSize) {
if(wram) {
return wram[address >> 1];
}
@@ -60,7 +61,7 @@ struct Mega32X : Interface {
}
auto write(n1 upper, n1 lower, n22 address, n16 data) -> void override {
if(address >= 0x200000) {
if(address >= sramAddr && address < sramAddr+sramSize) {
if(wram) {
if(upper) wram[address >> 1].byte(1) = data.byte(1);
if(lower) wram[address >> 1].byte(0) = data.byte(0);
+3 -2
View File
@@ -1,6 +1,7 @@
auto CPU::read(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
auto CPU::read(n1 upper, n1 lower, n24 address, n16) -> n16 {
while(bus.acquired()) wait(1);
return bus.read(upper, lower, address, data);
//using m68k prefetch for open-bus data
return bus.read(upper, lower, address, r.irc);
}
auto CPU::write(n1 upper, n1 lower, n24 address, n16 data) -> void {
+5 -1
View File
@@ -5,6 +5,10 @@ struct CPU : M68000, Thread {
Memory::Readable<n16> tmss;
Memory::Writable<n16> ram;
// Bus locking is unavailable for the main cpu of MegaDrive models 1 & 2,
// preventing the TAS instruction from working correctly in most cases.
auto lockable() -> bool override { return false; }
struct Debugger {
//debugger.cpp
auto load(Node::Object) -> void;
@@ -43,7 +47,7 @@ struct CPU : M68000, Thread {
auto power(bool reset) -> void;
//bus.cpp
auto read(n1 upper, n1 lower, n24 address, n16 data = 0) -> n16 override;
auto read(n1 upper, n1 lower, n24 address, n16 _ = 0) -> n16 override;
auto write(n1 upper, n1 lower, n24 address, n16 data) -> void override;
//io.cpp
+4 -4
View File
@@ -2,9 +2,9 @@
auto CPU::readIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
if(address >= 0xa10000 && address <= 0xa100ff) {
if(!lower) return data; //even byte writes ignored
address.bit(5,7) = 0; //a10020-a100ff mirrors a10000-a1001f
if(!lower) return data.byte(0) << 8;
address.bit(5,7) = 0; //a10020-a100ff mirrors a10000-a1001f
switch(address) {
case 0xa10000:
data.bit(0) = io.version; //0 = Model 1; 1 = Model 2+
@@ -38,11 +38,11 @@ auto CPU::readIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 {
break;
}
return data;
return data.byte(1)=data.byte(0), data;
}
if(address >= 0xa11100 && address <= 0xa111ff) {
data.bit(8) = apu.busownerAPU();
data.bit(8) = !apu.busgrantedCPU();
return data;
}
+1 -1
View File
@@ -17,7 +17,7 @@ auto M32X::SH7604::Debugger::load(Node::Object parent) -> void {
}
auto M32X::SH7604::Debugger::instruction() -> void {
if(tracer.instruction->enabled() && tracer.instruction->address(self->PC - 4)) {
if(tracer.instruction->enabled() && tracer.instruction->address(self->regs.PC - 4)) {
tracer.instruction->notify(self->disassembleInstruction(), self->disassembleContext());
}
}
+10 -10
View File
@@ -14,27 +14,27 @@ auto M32X::SH7604::unload() -> void {
}
auto M32X::SH7604::main() -> void {
if(!SH2::inDelaySlot() && !SH2::ID) {
if(!SH2::inDelaySlot() && !regs.ID) {
if(irq.vres.active && irq.vres.enable) {
debugger.interrupt("VRES");
irq.vres.active = 0;
return ET = 1, interrupt(14, 71);
return regs.ET = 1, interrupt(14, 71);
}
if(irq.vint.active && irq.vint.enable && SR.I < 12) {
if(irq.vint.active && irq.vint.enable && regs.SR.I < 12) {
debugger.interrupt("VINT");
return ET = 1, interrupt(12, 70);
return regs.ET = 1, interrupt(12, 70);
}
if(irq.hint.active && irq.hint.enable && SR.I < 10) {
if(irq.hint.active && irq.hint.enable && regs.SR.I < 10) {
debugger.interrupt("HINT");
return ET = 1, interrupt(10, 69);
return regs.ET = 1, interrupt(10, 69);
}
if(irq.cmd.active && irq.cmd.enable && SR.I < 8) {
if(irq.cmd.active && irq.cmd.enable && regs.SR.I < 8) {
debugger.interrupt("CMD");
return ET = 1, interrupt(8, 68);
return regs.ET = 1, interrupt(8, 68);
}
if(irq.pwm.active && irq.pwm.enable && SR.I < 6) {
if(irq.pwm.active && irq.pwm.enable && regs.SR.I < 6) {
debugger.interrupt("PWM");
return ET = 1, interrupt(6, 67);
return regs.ET = 1, interrupt(6, 67);
}
}
+8 -8
View File
@@ -54,17 +54,15 @@ auto MCD::CDD::clock() -> void {
}
auto MCD::CDD::advance() -> void {
//is the next sector still within the current track?
if(auto track = session.inTrack(io.sector + 1)) {
if(track() == io.track) {
io.sector++;
io.sample = 0;
return;
}
io.track = track();
io.sector++;
io.sample = 0;
return;
}
//if not, then the track has been completed
io.status = Status::Paused;
io.status = Status::LeadOut;
io.track = 0xaa;
}
auto MCD::CDD::sample() -> void {
@@ -244,6 +242,8 @@ auto MCD::CDD::process() -> void {
io.status = Status::Playing;
} break;
default:
io.status = Status::CommandError;
}
status[0] = io.status;
+36 -24
View File
@@ -11,6 +11,7 @@ auto MCD::GPU::step(u32 clocks) -> void {
if(!--image.vdots) {
active = 0;
irq.raise();
break;
}
}
}
@@ -26,11 +27,20 @@ auto MCD::GPU::write(n19 address, n4 data) -> void {
}
auto MCD::GPU::render(n19 address, n9 width) -> void {
n8 stampShift = 11 + 4 + stamp.tile.size;
n8 mapShift = (4 << stamp.map.size) - stamp.tile.size;
n4 stampShift = 4;
n4 mapShift = 4 << stamp.map.size;
n11 indexMask = ~0;
n5 pixelOffsetMask = 0x0f;
n2 stampMask = !stamp.tile.size ? 1 : 3;
n23 mapMask = !stamp.map.size ? 0x07ffff : 0x7fffff;
if(stamp.tile.size) {
stampShift++;
mapShift--;
indexMask &= ~3;
pixelOffsetMask |= 0x10;
}
n19 imageWidth = image.vcells+1 << 6;
n24 mapMask = !stamp.map.size ? 0x07ffff : 0x7fffff;
n24 x = mcd.wram[vector.address++] << 8; //13.3 -> 13.11
n24 y = mcd.wram[vector.address++] << 8; //13.3 -> 13.11
@@ -44,30 +54,32 @@ auto MCD::GPU::render(n19 address, n9 width) -> void {
y &= mapMask;
}
n4 output;
n4 output = 0;
if(bool outside = (x | y) & ~mapMask; !outside) {
auto xstamp = x >> stampShift;
auto ystamp = y >> stampShift << mapShift;
auto xtrunc = x >> 11;
auto ytrunc = y >> 11;
auto xstamp = xtrunc >> stampShift;
auto ystamp = ytrunc >> stampShift;
auto data = mcd.wram[stamp.map.address + xstamp + ystamp];
auto index = n10(data >> 0);
auto lroll = n1 (data >> 13); //0 = 0 degrees; 1 = 90 degrees
auto hroll = n1 (data >> 14); //0 = 0 degrees; 1 = 180 degrees
auto hflip = n1 (data >> 15);
auto mapEntry = mcd.wram[stamp.map.address + (ystamp << mapShift) + xstamp];
n11 index = mapEntry & indexMask;
n1 lroll = mapEntry >> 13; //0 = 0 degrees; 1 = 90 degrees
n1 hroll = mapEntry >> 14; //0 = 0 degrees; 1 = 180 degrees
n1 hflip = mapEntry >> 15;
if(index) { //stamp index 0 is not rendered
auto xpixel = n6(x >> 11);
auto ypixel = n6(y >> 11);
if(index) {
if(hflip) { xtrunc = ~xtrunc; }
if(hroll) { xtrunc = ~xtrunc; ytrunc = ~ytrunc; }
if(lroll) { auto t = xtrunc; xtrunc = ~ytrunc; ytrunc = t; }
if(hflip) { xpixel = ~xpixel; }
if(hroll) { xpixel = ~xpixel; ypixel = ~ypixel; }
if(lroll) { auto tpixel = xpixel; xpixel = ~ypixel; ypixel = tpixel; }
n5 xpixel = xtrunc & pixelOffsetMask;
n5 ypixel = ytrunc & pixelOffsetMask;
n6 pixel = n3(xpixel) + n3(ypixel) * 8;
xpixel = xpixel >> 3 & stampMask;
ypixel = ypixel >> 3 & stampMask;
n4 cell = ypixel + xpixel * (1 + stampMask);
output = read(index << 8 | cell << 6 | pixel);
output = read(
index << 8 |
(xpixel & ~7) << stampShift |
ypixel << 3 |
xpixel & 7);
}
}
@@ -79,7 +91,7 @@ auto MCD::GPU::render(n19 address, n9 width) -> void {
case 3: output = input; break;
}
write(address, output);
if(!(++address & 7)) address += (image.vcells + 1 << 6) - 8;
if(!(++address & 7)) address += imageWidth - 8;
x += xstep;
y += ystep;
+1 -1
View File
@@ -252,7 +252,7 @@ auto MCD::writeIO(n1 upper, n1 lower, n24 address, n16 data) -> void {
if(address == 0xff8030) {
if(lower) {
timer.frequency = data.byte(0);
timer.counter = timer.frequency = data.byte(0);
}
}
+1 -1
View File
@@ -159,7 +159,7 @@ auto System::power(bool reset) -> void {
bus.power(reset);
cpu.power(reset);
apu.power(reset); //apu.power() calls opn2.power()
vdp.power(reset); //vdp.power() calls vdp.psg.power()
if(!reset) vdp.power(reset); //vdp.power() calls vdp.psg.power()
controllerPort1.power(reset);
controllerPort2.power(reset);
extensionPort.power(reset);
+7 -2
View File
@@ -1,6 +1,11 @@
auto VDP::DAC::pixel(u32 x) -> void {
if(!pixels) return;
if(!vdp.displayEnable() || vdp.vcounter() == 0x1ff) {
output(0b101 << 9 | vdp.cram.color(vdp.io.backgroundColor));
return;
}
Pixel g = {vdp.io.backgroundColor, 0, 1};
Pixel a = vdp.layerA.pixel(x);
Pixel b = vdp.layerB.pixel(x);
@@ -15,8 +20,8 @@ auto VDP::DAC::pixel(u32 x) -> void {
s = {};
}
auto& bg = a.above() || a.color && !b.above() ? a : b.color ? b : g;
auto& fg = s.above() || s.color && !b.above() && !a.above() ? s : bg;
auto& bg = a.above() || a.solid() && !b.above() ? a : b.solid() ? b : g;
auto& fg = s.above() || s.solid() && !b.above() && !a.above() ? s : bg;
auto pixel = fg;
auto mode = 1; //0 = shadow, 1 = normal, 2 = highlight
+21 -20
View File
@@ -151,22 +151,17 @@ auto VDP::readControlPort() -> n16 {
command.latch = 0;
n16 result;
result.bit( 0) = Region::PAL();
result.bit( 1) = command.pending;
result.bit( 2) = hblank();
result.bit( 3) = vblank() || !io.displayEnable;
result.bit( 4) = io.interlaceMode.bit(0) && field();
result.bit( 5) = sprite.collision;
result.bit( 6) = sprite.overflow;
result.bit( 7) = irq.vblank.pending;
result.bit( 8) = fifo.full();
result.bit( 9) = fifo.empty();
result.bit(10) = 1; //constants (bits 10-15)
result.bit(11) = 0; //todo: should these bits be open bus instead?
result.bit(12) = 1;
result.bit(13) = 1;
result.bit(14) = 0;
result.bit(15) = 0;
result.bit( 0) = Region::PAL();
result.bit( 1) = command.pending;
result.bit( 2) = hblank();
result.bit( 3) = vblank() || !io.displayEnable;
result.bit( 4) = io.interlaceMode.bit(0) && field();
result.bit( 5) = sprite.collision;
result.bit( 6) = sprite.overflow;
result.bit( 7) = irq.vblank.pending;
result.bit( 8) = fifo.full();
result.bit( 9) = fifo.empty();
result.bit(10,15) = cpu.irc().bit(10,15); // open bus
sprite.collision = 0;
sprite.overflow = 0;
@@ -175,6 +170,13 @@ auto VDP::readControlPort() -> n16 {
auto VDP::writeControlPort(n16 data) -> void {
//command write (lo)
// Note: A pending interrupt will be delayed when a register write
// is used to enable that interrupt. Further testing is required,
// but the current method should be sufficient to handle most cases.
// Required for Sesame Street Counting Cafe et al.
irq.delay = 2; // 4 pixel delay (4-6 M68k clocks) from this write
if(command.latch) {
command.latch = 0;
@@ -203,7 +205,9 @@ auto VDP::writeControlPort(n16 data) -> void {
debugger.io(n5(data >> 8), n8(data));
//register write (d13 is ignored)
switch(data.bit(8,12)) {
u5 reg = data.bit(8,12);
if(!io.videoMode5 && reg > 0xA) return;
switch(reg) {
//mode register 1
case 0x00: {
@@ -214,7 +218,6 @@ auto VDP::writeControlPort(n16 data) -> void {
irq.hblank.enable = data.bit(4);
io.leftColumnBlank = data.bit(5);
irq.poll();
if(!io.videoMode4) debug(unimplemented, "[VDP] M4=0");
return;
}
@@ -228,7 +231,6 @@ auto VDP::writeControlPort(n16 data) -> void {
io.displayEnable = data.bit(6);
vram.mode = data.bit(7);
irq.poll();
if(!io.videoMode5) debug(unimplemented, "[VDP] M5=0");
return;
}
@@ -281,7 +283,6 @@ auto VDP::writeControlPort(n16 data) -> void {
layers.vscrollMode = data.bit(2);
irq.external.enable = data.bit(3);
irq.poll();
return;
}
+11 -7
View File
@@ -1,32 +1,35 @@
auto VDP::IRQ::poll() -> void {
if(delay) { delay--; return; }
if(external.enable && external.pending) {
vdp.debugger.interrupt(CPU::Interrupt::External);
cpu.raise(CPU::Interrupt::External);
} else {
external.pending = 0;
cpu.lower(CPU::Interrupt::External);
}
if(hblank.enable && hblank.pending) {
vdp.debugger.interrupt(CPU::Interrupt::HorizontalBlank);
cpu.raise(CPU::Interrupt::HorizontalBlank);
} else {
// note: pending H-INT is not cleared when disabled
cpu.lower(CPU::Interrupt::HorizontalBlank);
}
if(vblank.enable && vblank.pending) {
vdp.debugger.interrupt(CPU::Interrupt::VerticalBlank);
cpu.raise(CPU::Interrupt::VerticalBlank);
} else {
vblank.pending = 0;
cpu.lower(CPU::Interrupt::VerticalBlank);
}
}
auto VDP::IRQ::acknowledge(u8 level) -> void {
if(level == 2) external.pending = 0;
if(level == 4) hblank.pending = 0;
if(level == 4) {
// H-INT ack can errantly cancel V-INT if they coincide.
// Required for Fatal Rewind et al.
if(vblank.pending && vblank.enable)
vblank.pending = 0;
else
hblank.pending = 0;
}
if(level == 6) vblank.pending = 0;
}
@@ -34,4 +37,5 @@ auto VDP::IRQ::power(bool reset) -> void {
external = {};
hblank = {};
vblank = {};
delay = 0;
}
+22 -15
View File
@@ -8,41 +8,53 @@ auto VDP::tick() -> void {
cycles += 2;
state.hcounter++;
if(h32()) {
if(hcounter() > 0x90 ||
hcounter() < 0x80) vblankcheck(); // exact bounds are unconfirmed
if(hcounter() == 0x00) hblank(0), vedge();
if(hcounter() == 0x81) vtick();
if(hcounter() == 0x93) hblank(1);
if(hcounter() == 0x94) state.hcounter = 0xe9;
}
if(h40()) {
if(hcounter() > 0xb0 ||
hcounter() < 0xa0) vblankcheck(); // exact bounds are unconfirmed
if(hcounter() == 0x00) hblank(0), vedge();
if(hcounter() == 0xa1) vtick();
if(hcounter() == 0xb3) hblank(1);
if(hcounter() == 0xb6) state.hcounter = 0xe4;
}
irq.poll();
vram.refreshing = 0;
}
auto VDP::vblankcheck() -> void {
if(v28()) {
if(vcounter() == 0x0e0) vblank(1);
if(vcounter() == 0x1ff) vblank(0);
}
if(v30()) {
if(vcounter() == 0x0f0) vblank(1);
if(vcounter() == 0x1ff) vblank(0);
}
}
auto VDP::vtick() -> void {
if(vblank()) {
irq.hblank.counter = irq.hblank.frequency;
} else if(irq.hblank.counter-- == 0) {
irq.hblank.counter = irq.hblank.frequency;
irq.hblank.pending = 1;
irq.poll();
debugger.interrupt(CPU::Interrupt::HorizontalBlank);
}
state.vcounter++;
if(v28()) {
if(vcounter() == 0x0e0) vblank(1);
if(vcounter() == 0x0eb && Region::NTSC()) state.vcounter = 0x1e5;
if(vcounter() == 0x103 && Region::PAL ()) state.vcounter = 0x1ca;
if(vcounter() == 0x1ff) vblank(0);
}
if(v30()) {
if(vcounter() == 0x0f0) vblank(1);
if(vcounter() == 0x200 && Region::NTSC()) state.vcounter = 0x000;
if(vcounter() == 0x10b && Region::PAL ()) state.vcounter = 0x1d2;
if(vcounter() == 0x1ff) vblank(0);
}
}
@@ -57,8 +69,8 @@ auto VDP::hblank(bool line) -> void {
}
auto VDP::vblank(bool line) -> void {
irq.vblank.transitioned |= state.vblank ^ line;
state.vblank = line;
irq.vblank.transitioned = 1;
}
auto VDP::vedge() -> void {
@@ -72,22 +84,17 @@ auto VDP::vedge() -> void {
cartridge.vblank(1);
apu.setINT(1);
irq.vblank.pending = 1;
irq.poll();
debugger.interrupt(CPU::Interrupt::VerticalBlank);
}
}
auto VDP::slot() -> void {
if(state.refreshing) {
state.refreshing = 0;
return;
}
if(!fifo.run()) prefetch.run();
dma.run();
}
auto VDP::refresh() -> void {
vram.refreshing = 1;
state.refreshing = !displayEnable();
vram.refreshing = 1;
}
auto VDP::main() -> void {
@@ -135,7 +142,7 @@ auto VDP::mainH32() -> void {
for(auto cycle : range(13)) {
tick(); sprite.patternFetch(cycle + 0);
}
tick(); displayEnable() ? slot() : refresh();
tick(); slot();
for(auto cycle : range(13)) {
tick(); sprite.patternFetch(cycle + 13);
}
@@ -196,7 +203,7 @@ auto VDP::mainH40() -> void {
for(auto cycle : range(23)) {
tick(); sprite.patternFetch(cycle + 0);
}
tick(); displayEnable() ? slot() : refresh();
tick(); slot();
for(auto cycle : range(11)) {
tick(); sprite.patternFetch(cycle + 23);
}
+3 -3
View File
@@ -28,12 +28,12 @@ auto VDP::VRAM::writeByte(n17 address, n8 data) -> void {
write(address >> 1, word);
}
auto VDP::VSRAM::read(n6 address) const -> n10 {
if(address >= 40) return 0x0000;
auto VDP::VSRAM::read(n6 address) const -> n11 {
if(address >= 40) address = 0;
return memory[address];
}
auto VDP::VSRAM::write(n6 address, n10 data) -> void {
auto VDP::VSRAM::write(n6 address, n11 data) -> void {
if(address >= 40) return;
memory[address] = data;
}
+18 -13
View File
@@ -2,18 +2,12 @@ auto VDP::Prefetch::run() -> bool {
if(full()) return false;
if(vdp.command.target == 0 && vdp.vram.mode == 0) {
if(!slot.lower) {
slot.lower = 1;
slot.data.byte(0) = vdp.vram.readByte(vdp.command.address & ~1 | 1);
vdp.command.ready = 1;
return true;
}
if(!slot.upper) {
slot.upper = 1;
slot.data.byte(1) = vdp.vram.readByte(vdp.command.address & ~1 | 0);
vdp.command.ready = 1;
return true;
}
slot.lower = 1;
slot.upper = 1;
slot.data.byte(0) = vdp.vram.readByte(vdp.command.address & ~1 | 1);
slot.data.byte(1) = vdp.vram.readByte(vdp.command.address & ~1 | 0);
vdp.command.ready = 1;
return true;
}
if(vdp.command.target == 0 && vdp.vram.mode == 1) {
@@ -29,6 +23,7 @@ auto VDP::Prefetch::run() -> bool {
slot.lower = 1;
slot.upper = 1;
slot.data = vdp.vsram.read(vdp.command.address >> 1);
slot.data = (slot.data & 0x07FF) | (vdp.fifo.slots[0].data & 0xF800);
vdp.command.ready = 1;
return true;
}
@@ -37,6 +32,16 @@ auto VDP::Prefetch::run() -> bool {
slot.lower = 1;
slot.upper = 1;
slot.data = vdp.cram.read(vdp.command.address >> 1);
slot.data = slot.data & 0x0EEE | vdp.fifo.slots[0].data & ~0x0EEE;
vdp.command.ready = 1;
return true;
}
if(vdp.command.target == 12) {
slot.lower = 1;
slot.upper = 1;
slot.data.byte(0) = vdp.vram.readByte(vdp.command.address ^ 1);
slot.data.byte(1) = vdp.fifo.slots[0].data.byte(1);
vdp.command.ready = 1;
return true;
}
@@ -50,7 +55,7 @@ auto VDP::Prefetch::run() -> bool {
auto VDP::Prefetch::read(n4 target, n17 address) -> void {
if(target.bit(0) != 0) return;
slot.upper = address.bit(0) && target == 0;
slot.upper = 0;
slot.lower = 0;
}
+1
View File
@@ -74,6 +74,7 @@ auto VDP::IRQ::serialize(serializer& s) -> void {
s(vblank.enable);
s(vblank.pending);
s(vblank.transitioned);
s(delay);
}
auto VDP::Slot::serialize(serializer& s) -> void {
+4 -3
View File
@@ -112,10 +112,11 @@ auto VDP::Sprite::patternFetch(u32) -> void {
n9 x = object.x + patternSlice * 8 + index - 128;
n6 color = data >> 28;
data <<= 4;
if(!color) continue;
if(pixels[x].color) {
collision = 1;
if(pixels[x].solid()) {
if (color) collision = 1;
} else {
// Transparent pixels must still be written to the sprite buffer.
// Test case: Titan - OverDrive 2 (logo scene)
color |= object.palette << 4;
pixels[x] = {color, object.priority};
}
+8 -2
View File
@@ -74,10 +74,16 @@ auto VDP::pixels() -> u32* {
output = screen->pixels().data() + (vcounter() - 8) * 2 * 1280;
}
if(overscan->value() == 1 && latch.overscan == 0) {
if(vcounter() >= 232) return nullptr;
output = screen->pixels().data() + (vcounter() + 8) * 2 * 1280;
if(vcounter() >= 0x1f8) { // top border
output = screen->pixels().data() + (vcounter() - 0x1f8) * 2 * 1280;
} else if(vcounter() >= 232) {
return nullptr;
} else {
output = screen->pixels().data() + (vcounter() + 8) * 2 * 1280;
}
}
if(overscan->value() == 1 && latch.overscan == 1) {
if(vcounter() >= 240) return nullptr;
output = screen->pixels().data() + (vcounter() + 0) * 2 * 1280;
}
if(latch.interlace) output += field() * 1280;
+10 -5
View File
@@ -67,6 +67,7 @@ struct VDP : Thread {
auto vtick() -> void;
auto hblank(bool line) -> void;
auto vblank(bool line) -> void;
auto vblankcheck() -> void;
auto vedge() -> void;
auto slot() -> void;
auto refresh() -> void;
@@ -137,6 +138,9 @@ struct VDP : Thread {
n1 pending;
n1 transitioned;
} vblank;
n8 delay;
} irq;
struct Slot {
@@ -207,8 +211,9 @@ struct VDP : Thread {
} dma;
struct Pixel {
auto above() const -> bool { return priority == 1 && color; }
auto below() const -> bool { return priority == 0 && color; }
auto solid() const -> bool { return color & 0xF; }
auto above() const -> bool { return priority == 1 && solid(); }
auto below() const -> bool { return priority == 0 && solid(); }
//serialization.cpp
auto serialize(serializer&) -> void;
@@ -415,13 +420,13 @@ private:
//vertical scroll RAM
struct VSRAM {
//memory.cpp
auto read(n6 address) const -> n10;
auto write(n6 address, n10 data) -> void;
auto read(n6 address) const -> n11;
auto write(n6 address, n11 data) -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
n10 memory[40];
n11 memory[40];
} vsram;
//color RAM
@@ -11,7 +11,7 @@ LightPhaser::LightPhaser(Node::Port parent) {
}
LightPhaser::~LightPhaser() {
vdp.screen->detach(sprite);
if(vdp.screen) vdp.screen->detach(sprite);
}
auto LightPhaser::read() -> n7 {
+2 -2
View File
@@ -1,5 +1,6 @@
auto VDP::DAC::setup(n8 y) -> void {
output = self.screen->pixels().data() + (24 + y) * 256;
self.screen->setFillColor(palette(16 | io.backdropColor));
output = self.screen->pixels().data() + ((240-self.vlines())/2 + y) * 256;
}
auto VDP::DAC::run(n8 x, n8 y) -> void {
@@ -11,7 +12,6 @@ auto VDP::DAC::run(n8 x, n8 y) -> void {
color = palette(16 | self.sprite.output.color);
}
}
if(!self.displayEnable()) color = 0;
output[x] = color;
}
+5
View File
@@ -60,6 +60,9 @@ auto VDP::status() -> n8 {
auto VDP::data(n8 data) -> void {
latch.control = 0;
// Writes to data port set VDP read buffer
latch.vram = data;
if(io.code <= 2) {
vram[io.address] = data;
} else {
@@ -114,6 +117,7 @@ auto VDP::registerWrite(n4 address, n8 data) -> void {
background.io.vscrollLock = data.bit(7);
irq.line.pending &= irq.line.enable;
irq.poll();
updateScreenSize();
return;
case 0x1: //mode control 2
@@ -125,6 +129,7 @@ auto VDP::registerWrite(n4 address, n8 data) -> void {
io.displayEnable = data.bit(6);
irq.frame.pending &= irq.frame.enable;
irq.poll();
updateScreenSize();
return;
case 0x2: //name table base address
+20 -4
View File
@@ -18,7 +18,7 @@ auto VDP::load(Node::Object parent) -> void {
node = parent->append<Node::Object>("VDP");
screen = node->append<Node::Video::Screen>("Screen", 256, 264);
screen = node->append<Node::Video::Screen>("Screen", 256, 240);
u32 defaultRevision = 2;
if((Model::MarkIII() || Model::MasterSystemI()) && Region::NTSCJ()) defaultRevision = 1;
@@ -30,6 +30,12 @@ auto VDP::load(Node::Object parent) -> void {
screen->setSize(256, 240);
screen->setScale(1.0, 1.0);
screen->setAspect(8.0, 7.0);
overscan = screen->append<Node::Setting::Boolean>("Overscan", true, [&](auto value) {
if(value == 0) screen->setSize(256, vlines());
if(value == 1) screen->setSize(256, 240);
});
overscan->setDynamic(true);
}
if(Display::LCD()) {
@@ -53,6 +59,7 @@ auto VDP::unload() -> void {
revision.reset();
screen->quit();
node->remove(screen);
overscan.reset();
screen.reset();
node.reset();
vram.reset();
@@ -95,9 +102,8 @@ auto VDP::main() -> void {
if(io.vcounter == 240) {
if(Mode::MasterSystem()) {
if(vlines() == 192) screen->setViewport(0, 0, 256, 240);
if(vlines() == 224) screen->setViewport(0, 16, 256, 240);
if(vlines() == 240) screen->setViewport(0, 24, 256, 240);
if(overscan->value() == 0) screen->setViewport(0, (240-vlines())/2, 256, vlines());
if(overscan->value() == 1) screen->setViewport(0, 0, 256, 240);
}
if(Mode::GameGear()) {
screen->setViewport(48, 48, 160, 144);
@@ -126,6 +132,16 @@ auto VDP::step(u32 clocks) -> void {
}
}
auto VDP::updateScreenSize() -> void {
if(Display::CRT()) {
if(overscan->value() == 0) screen->setSize(256, vlines());
if(overscan->value() == 1) screen->setSize(256, 240);
}
//if(Display::LCD()) {
// screen->setSize(160, 144);
//}
}
auto VDP::vlines() -> u32 {
if(revision->value() == 1) return 192;
+3
View File
@@ -5,6 +5,7 @@
struct VDP : Thread {
Node::Object node;
Node::Video::Screen screen;
Node::Setting::Boolean overscan;
Node::Setting::Natural revision;
Node::Setting::Boolean interframeBlending; //Game Gear only
Memory::Writable<n8 > vram; //16KB
@@ -37,6 +38,8 @@ struct VDP : Thread {
auto main() -> void;
auto step(u32 clocks) -> void;
auto updateScreenSize() -> void;
auto vlines() -> u32;
auto vblank() -> bool;
+1 -1
View File
@@ -9,7 +9,7 @@ CPU cpu;
auto CPU::load(Node::Object parent) -> void {
if(Model::MSX()) ram.allocate(64_KiB);
if(Model::MSX2()) ram.allocate(256_KiB);
if(Model::MSX2()) ram.allocate(512_KiB);
node = parent->append<Node::Object>("CPU");
+23 -8
View File
@@ -5,6 +5,9 @@ auto CPU::read(n16 address) -> n8 {
n2 primary = slot[page].primary;
n2 secondary = slot[primary].secondary[page];
// If MSX1, assume there are no secondary slots
if (Model::MSX()) secondary = 0;
if(primary == 0) {
return rom.bios.read(address);
}
@@ -18,14 +21,18 @@ auto CPU::read(n16 address) -> n8 {
}
if(primary == 3) {
if(secondary == 0) {
if(Model::MSX()) return ram.read(address);
if (secondary == 0 && Model::MSX()) {
return ram.read(address);
}
if(secondary == 0 && rom.sub) {
return rom.sub.read(address);
}
if(secondary == 2) {
n22 logical = slot[page].memory << 14 | (n14)address;
return ram.read(logical);
}
if(secondary == 1 && rom.sub) {
return rom.sub.read(address);
}
}
return 0xff;
@@ -38,6 +45,9 @@ auto CPU::write(n16 address, n8 data) -> void {
n2 primary = slot[page].primary;
n2 secondary = slot[primary].secondary[page];
// If MSX1, assume there are no secondary slots
if (Model::MSX()) secondary = 0;
if(primary == 0) {
return;
}
@@ -51,9 +61,14 @@ auto CPU::write(n16 address, n8 data) -> void {
}
if(primary == 3) {
if(Model::MSX()) return ram.write(address, data);
n22 logical = slot[page].memory << 14 | (n14)address;
return ram.write(logical, data);
if (secondary == 0 && Model::MSX()) {
return ram.write(address, data);
}
if(secondary == 2) {
n22 logical = slot[page].memory << 14 | (n14)address;
return ram.write(logical, data);
}
}
}
+42 -4
View File
@@ -13,8 +13,8 @@ ares.objects += ares-n64-ri
ares.objects += ares-n64-si
ares.objects += ares-n64-rdram
ares.objects += ares-n64-cpu
ares.objects += ares-n64-rdp
ares.objects += ares-n64-rsp
ares.objects += ares-n64-rdp
$(object.path)/ares-n64-memory.o: $(ares.path)/n64/memory/memory.cpp
$(object.path)/ares-n64-system.o: $(ares.path)/n64/system/system.cpp
@@ -31,11 +31,13 @@ $(object.path)/ares-n64-ri.o: $(ares.path)/n64/ri/ri.cpp
$(object.path)/ares-n64-si.o: $(ares.path)/n64/si/si.cpp
$(object.path)/ares-n64-rdram.o: $(ares.path)/n64/rdram/rdram.cpp
$(object.path)/ares-n64-cpu.o: $(ares.path)/n64/cpu/cpu.cpp
$(object.path)/ares-n64-rdp.o: $(ares.path)/n64/rdp/rdp.cpp
$(object.path)/ares-n64-rsp.o: $(ares.path)/n64/rsp/rsp.cpp
$(object.path)/ares-n64-rdp.o: $(ares.path)/n64/rdp/rdp.cpp
flags += -msse4.2
options += -msse4.2
ifeq ($(arch),amd64)
flags += -msse4.2
options += -msse4.2
endif
ifeq ($(vulkan),true)
ares.objects += ares-n64-vulkan
@@ -54,3 +56,39 @@ ifeq ($(vulkan),true)
@mkdir -p $(dir $@)
@$(call compile)
endif
ifeq ($(mame.rdp),true)
mame.path := $(thirdparty.path)/mame
ares.objects += mame-emu-emucore
ares.objects += mame-lib-util-delegate
ares.objects += mame-lib-util-strformat
ares.objects += mame-mame-video-n64
ares.objects += mame-mame-video-pin64
ares.objects += mame-mame-video-rdpblend
ares.objects += mame-mame-video-rdptpipe
ares.objects += mame-osd-osdcore
ares.objects += mame-osd-osdsync
$(object.path)/mame-emu-emucore.o: $(mame.path)/emu/emucore.cpp
$(object.path)/mame-lib-util-delegate.o: $(mame.path)/lib/util/delegate.cpp
$(object.path)/mame-lib-util-strformat.o: $(mame.path)/lib/util/strformat.cpp
$(object.path)/mame-mame-video-n64.o: $(mame.path)/mame/video/n64.cpp
$(object.path)/mame-mame-video-pin64.o: $(mame.path)/mame/video/pin64.cpp
$(object.path)/mame-mame-video-rdpblend.o: $(mame.path)/mame/video/rdpblend.cpp
$(object.path)/mame-mame-video-rdptpipe.o: $(mame.path)/mame/video/rdptpipe.cpp
$(object.path)/mame-osd-osdcore.o: $(mame.path)/osd/osdcore.cpp
$(object.path)/mame-osd-osdsync.o: $(mame.path)/osd/osdsync.cpp
flags += -I$(mame.path)/devices -I$(mame.path)/emu -I$(mame.path)/lib/util -I$(mame.path)/mame -I$(mame.path)/osd
flags += -DMAME_RDP -DLSB_FIRST -DPTR64
ifeq ($(platform),windows)
flags += -DOSD_WINDOWS=1
else ifeq ($(platform),macos)
flags += -DSDLMAME_MACOSX
else ifeq ($(platform),linux)
flags += -DSDLMAME_LINUX
else ifeq ($(platform),bsd)
flags += -DSDLMAME_BSD
endif
endif
+1 -1
View File
@@ -15,7 +15,7 @@ struct Accuracy {
static constexpr bool Recompiler = !Interpreter;
//VU instructions
static constexpr bool SISD = 0 | Reference;
static constexpr bool SISD = 0 | Reference | !Architecture::amd64;
static constexpr bool SIMD = !SISD;
};
+3 -3
View File
@@ -91,7 +91,7 @@ auto CPU::instruction() -> void {
}
}
auto CPU::instructionEpilogue() -> bool {
auto CPU::instructionEpilogue() -> s32 {
if constexpr(Accuracy::CPU::Recompiler) {
icache.step(ipu.pc); //simulates timings without performing actual icache loads
}
@@ -138,8 +138,8 @@ auto CPU::power(bool reset) -> void {
context.setMode();
if constexpr(Accuracy::CPU::Recompiler) {
auto buffer = ares::Memory::FixedAllocator::get().acquire(512_MiB);
recompiler.allocator.resize(512_MiB, bump_allocator::executable | bump_allocator::zero_fill, buffer);
auto buffer = ares::Memory::FixedAllocator::get().tryAcquire(64_MiB);
recompiler.allocator.resize(64_MiB, bump_allocator::executable | bump_allocator::zero_fill, buffer);
recompiler.reset();
}
}
Loaded 100 of 318 files, more files were not shown because too many files have changed in this diff. Show more