Compare commits

...
93 Commits
Author SHA1 Message Date
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
95 changed files with 11221 additions and 544 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/*
+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)
+2 -2
View File
@@ -37,7 +37,7 @@ using namespace nall;
namespace ares {
static const string Name = "ares";
static const string Version = "124";
static const string Version = "126";
static const string Copyright = "ares team, Near";
static const string License = "ISC";
static const string LicenseURI = "https://opensource.org/licenses/ISC";
@@ -46,7 +46,7 @@ namespace ares {
//incremented only when serialization format changes
static const u32 SerializerSignature = 0x31545342; //"BST1" (little-endian)
static const string SerializerVersion = "123.1";
static const string SerializerVersion = "125";
namespace VFS {
using Pak = shared_pointer<vfs::directory>;
+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 -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), ")"};
@@ -1262,7 +1262,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;
+3 -2
View File
@@ -9,6 +9,7 @@ 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; }
@@ -70,8 +71,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 {
+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
+28 -21
View File
@@ -174,14 +174,12 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
addd(Rn, imm8(4));
call(readLong);
movsxd(rax, eax);
push(rax);
push(rax);
mov(r13, rax);
mov(ra1d, Rm);
addd(Rm, imm8(4));
call(readLong);
movsxd(rax, eax);
pop(rdx);
pop(rdx);
mov(rdx, r13);
imul(rdx);
mov(rdx, rax);
add(rdx, MAC);
@@ -232,9 +230,10 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0x24: {
mov(ra1d, Rn);
dec(ra1d);
mov(Rn, ra1d);
mov(r13d, ra1d);
mov(ra2d, Rm);
call(writeByte);
mov(Rn, r13d);
return 0;
}
@@ -242,9 +241,10 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0x25: {
mov(ra1d, Rn);
sub(ra1d, imm8(2));
mov(Rn, ra1d);
mov(r13d, ra1d);
mov(ra2d, Rm);
call(writeWord);
mov(Rn, r13d);
return 0;
}
@@ -252,9 +252,10 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
case 0x26: {
mov(ra1d, Rn);
sub(ra1d, imm8(4));
mov(Rn, ra1d);
mov(r13d, ra1d);
mov(ra2d, Rm);
call(writeLong);
mov(Rn, r13d);
return 0;
}
@@ -309,6 +310,14 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
mov(eax, Rn);
mov(edx, Rm);
xor(eax, edx);
test(al, al);
setz(dl);
for(auto _ : range(3)) {
shr(eax, imm8(8));
test(al, al);
setz(al);
or(dl, al);
}
setnz(T);
return 0;
}
@@ -484,7 +493,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
//ADDC Rm,Rn
case 0x3e: {
mov(rax, T);
mov(al, T);
rcr(al);
mov(eax, Rn);
adc(eax, Rm);
@@ -510,17 +519,15 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
addd(Rn, imm8(2));
call(readWord);
movsx(eax, ax);
push(rax);
push(rax);
mov(r13d, eax);
mov(ra1d, Rm);
addd(Rm, imm8(2));
call(readWord);
movsx(eax, ax);
pop(rdx);
pop(rdx);
mov(edx, r13d);
imul(edx);
shl(rdx, imm8(32));
or(edx, eax);
or(rdx, rax);
add(rdx, MAC);
mov(al, S);
test(al, al);
@@ -633,14 +640,14 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
//NEGC Rm,Rn
case 0x6a: {
xor(edx, edx);
sub(edx, Rm);
mov(al, T);
and(eax, imm8(1));
sub(edx, eax);
mov(Rn, edx);
setc(al);
mov(T, al);
mov(edx, Rm);
add(eax, edx);
neg(eax);
mov(Rn, eax);
or(eax, edx);
setnz(T);
return 0;
}
@@ -949,7 +956,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
mov(ra1d, GBR);
add(ra1d, imm32(d8*4));
call(readLong);
mov(R0, rax);
mov(R0, eax);
return 0;
}
@@ -1249,7 +1256,7 @@ auto SH2::Recompiler::emitInstruction(u16 opcode) -> bool {
mov(ra1d, Rn);
addd(Rn, imm8(4));
call(readLong);
mov(MACH, rax);
mov(MACH, eax);
return 0;
}
+2 -2
View File
@@ -9,8 +9,8 @@ namespace ares {
#include "exceptions.cpp"
#include "instruction.cpp"
#include "instructions.cpp"
#include "cached.cpp"
//#include "recompiler.cpp"
//#include "cached.cpp"
#include "recompiler.cpp"
#include "serialization.cpp"
#include "disassembler.cpp"
+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;
};
+30 -9
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 {
@@ -27,6 +38,7 @@ struct HVC_CNROM : Interface {
auto writePRG(n32 address, n8 data) -> void override {
if(address < 0x8000) return;
characterBank = data.bit(0,1);
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];
};
+9 -1
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");
@@ -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;
}
}
+36 -2
View File
@@ -163,20 +163,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);
}
+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;
};
+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;
+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
+1 -1
View File
@@ -29,7 +29,7 @@ struct Linear : Interface {
}
auto read(n1 upper, n1 lower, n22 address, n16 data) -> n16 override {
if(address >= 0x200000) {
if(address >= 0x200000 && address < 0x300000) {
if(wram && ramEnable) {
return wram[address >> 1];
}
+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;
}
+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);
}
}
+7 -3
View File
@@ -175,6 +175,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;
@@ -214,7 +221,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 +234,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 +286,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;
}
+3 -2
View File
@@ -19,6 +19,7 @@ auto VDP::tick() -> void {
if(hcounter() == 0xb3) hblank(1);
if(hcounter() == 0xb6) state.hcounter = 0xe4;
}
irq.poll();
vram.refreshing = 0;
}
@@ -28,7 +29,7 @@ auto VDP::vtick() -> void {
} 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++;
@@ -72,7 +73,7 @@ auto VDP::vedge() -> void {
cartridge.vblank(1);
apu.setINT(1);
irq.vblank.pending = 1;
irq.poll();
debugger.interrupt(CPU::Interrupt::VerticalBlank);
}
}
+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 {
+3
View File
@@ -137,6 +137,9 @@ struct VDP : Thread {
n1 pending;
n1 transitioned;
} vblank;
n8 delay;
} irq;
struct Slot {
@@ -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 {
+3
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 {
+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);
}
}
}
+2 -2
View File
@@ -11,9 +11,9 @@ auto VI::load(Node::Object parent) -> void {
node = parent->append<Node::Object>("VI");
#if defined(VULKAN)
screen = node->append<Node::Video::Screen>("Screen", vulkan.outputUpscale * 640, vulkan.outputUpscale * 480);
screen = node->append<Node::Video::Screen>("Screen", vulkan.outputUpscale * 640, vulkan.outputUpscale * 576);
#else
screen = node->append<Node::Video::Screen>("Screen", 640, 480);
screen = node->append<Node::Video::Screen>("Screen", 640, 576);
#endif
screen->setRefresh({&VI::refresh, this});
screen->colors((1 << 24) + (1 << 15), [&](n32 color) -> n64 {
+1 -1
View File
@@ -16,7 +16,7 @@ Justifier::Justifier(Node::Port parent) {
Justifier::~Justifier() {
cpu.peripherals.removeByValue(this);
ppu.screen->detach(sprite);
if(ppu.screen) ppu.screen->detach(sprite);
}
auto Justifier::main() -> void {
@@ -25,8 +25,10 @@ Justifiers::Justifiers(Node::Port parent) {
Justifiers::~Justifiers() {
cpu.peripherals.removeByValue(this);
ppu.screen->detach(sprite1);
ppu.screen->detach(sprite2);
if(ppu.screen) {
ppu.screen->detach(sprite1);
ppu.screen->detach(sprite2);
}
}
auto Justifiers::main() -> void {
@@ -30,7 +30,7 @@ SuperScope::SuperScope(Node::Port parent) {
SuperScope::~SuperScope() {
cpu.peripherals.removeByValue(this);
ppu.screen->detach(sprite);
if(ppu.screen) ppu.screen->detach(sprite);
}
auto SuperScope::main() -> void {
+6 -6
View File
@@ -14,15 +14,15 @@ auto SPC7110::setDataAdjust(n16 address) -> void { r4814 = address; r4815 = addr
auto SPC7110::dataPortRead() -> void {
n24 offset = dataOffset();
n16 adjust = r4818 & 2 ? dataAdjust() : (n16)0;
n32 adjust = r4818 & 2 ? dataAdjust() : (n16)0;
if(r4818 & 8) adjust = (i16)adjust;
r4810 = dataromRead(offset + adjust);
}
auto SPC7110::dataPortIncrement4810() -> void {
n24 offset = dataOffset();
n16 stride = r4818 & 1 ? dataStride() : (n16)1;
n16 adjust = dataAdjust();
n32 stride = r4818 & 1 ? dataStride() : (n16)1;
n32 adjust = dataAdjust();
if(r4818 & 4) stride = (i16)stride;
if(r4818 & 8) adjust = (i16)adjust;
if((r4818 & 16) == 0) setDataOffset(offset + stride);
@@ -33,7 +33,7 @@ auto SPC7110::dataPortIncrement4810() -> void {
auto SPC7110::dataPortIncrement4814() -> void {
if(r4818 >> 5 != 1) return;
n24 offset = dataOffset();
n16 adjust = dataAdjust();
n32 adjust = dataAdjust();
if(r4818 & 8) adjust = (i16)adjust;
setDataOffset(offset + adjust);
dataPortRead();
@@ -42,7 +42,7 @@ auto SPC7110::dataPortIncrement4814() -> void {
auto SPC7110::dataPortIncrement4815() -> void {
if(r4818 >> 5 != 2) return;
n24 offset = dataOffset();
n16 adjust = dataAdjust();
n32 adjust = dataAdjust();
if(r4818 & 8) adjust = (i16)adjust;
setDataOffset(offset + adjust);
dataPortRead();
@@ -51,7 +51,7 @@ auto SPC7110::dataPortIncrement4815() -> void {
auto SPC7110::dataPortIncrement481a() -> void {
if(r4818 >> 5 != 3) return;
n24 offset = dataOffset();
n16 adjust = dataAdjust();
n32 adjust = dataAdjust();
if(r4818 & 8) adjust = (i16)adjust;
setDataOffset(offset + adjust);
dataPortRead();
+3
View File
@@ -68,6 +68,9 @@ all: $(all.objects)
$(info Linking $(output.path)/$(name) ...)
+@$(compiler) -o $(output.path)/$(name) $(all.objects) $(all.options)
ifeq ($(platform),macos)
# Apply workaround for buggy linker in Xcode < 11.4.1
@$(compiler) -o $(output.path)/macos-fix-jit macos-fix-jit.cpp
$(output.path)/macos-fix-jit $(output.path)/$(name)
rm -rf $(output.path)/$(name).app
mkdir -p $(output.path)/$(name).app/Contents/MacOS/
mkdir -p $(output.path)/$(name).app/Contents/Resources/
+13 -2
View File
@@ -18,6 +18,17 @@ MegaCD::MegaCD() {
for(auto id : range(2)) {
InputPort port{string{"Controller Port ", 1 + id}};
{ InputDevice device{"Control Pad"};
device.digital("Up", virtualPorts[id].pad.up);
device.digital("Down", virtualPorts[id].pad.down);
device.digital("Left", virtualPorts[id].pad.left);
device.digital("Right", virtualPorts[id].pad.right);
device.digital("A", virtualPorts[id].pad.a);
device.digital("B", virtualPorts[id].pad.b);
device.digital("C", virtualPorts[id].pad.c);
device.digital("Start", virtualPorts[id].pad.start);
port.append(device); }
{ InputDevice device{"Fighting Pad"};
device.digital("Up", virtualPorts[id].pad.up);
device.digital("Down", virtualPorts[id].pad.down);
@@ -58,12 +69,12 @@ auto MegaCD::load() -> bool {
}
if(auto port = root->find<ares::Node::Port>("Controller Port 1")) {
port->allocate("Fighting Pad");
port->allocate("Control Pad");
port->connect();
}
if(auto port = root->find<ares::Node::Port>("Controller Port 2")) {
port->allocate("Fighting Pad");
port->allocate("Control Pad");
port->connect();
}
+5
View File
@@ -46,6 +46,11 @@ auto InputManager::createHotkeys() -> void {
program.rewindSetMode(Program::Rewind::Mode::Playing);
}));
hotkeys.append(InputHotkey("Capture Screenshot").onPress([&] {
if(!emulator) return;
program.requestScreenshot = true;
}));
hotkeys.append(InputHotkey("Save State").onPress([&] {
if(!emulator) return;
program.stateSave(program.state.slot);
+119
View File
@@ -0,0 +1,119 @@
// macos-fix-jit - Fix Ares JIT support on macOS with old linkers
//
// Ares JIT requires the data segment to be executable (see
// https://github.com/ares-emulator/ares/pull/200 for details on why
// this is 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 bug was fixed in Xcode 11.4.1.
//
// 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.
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#define VM_PROT_R 1
#define VM_PROT_W 2
#define VM_PROT_X 4
bool is_64bit = false;
bool is_le = false;
uint32_t read32(FILE *f) {
if (is_le) {
uint32_t x = fgetc(f);
x |= fgetc(f) << 8;
x |= fgetc(f) << 16;
x |= fgetc(f) << 24;
return x;
} else {
uint32_t x = fgetc(f) << 24;
x |= fgetc(f) << 16;
x |= fgetc(f) << 8;
x |= fgetc(f);
return x;
}
}
void write32(FILE *f, uint32_t x) {
if (is_le) {
fputc(x, f); fputc(x>>8, f); fputc(x>>16, f); fputc(x>>24, f);
} else {
fputc(x>>24, f); fputc(x>>16, f); fputc(x>>8, f); fputc(x, f);
}
}
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <ares-binary>\n", argv[0]);
return 1;
}
FILE *f = fopen(argv[1], "r+w");
if (!f) {
fprintf(stderr, "cannot open file: %s\n", argv[1]);
return 1;
}
uint32_t magic = read32(f);
switch (magic) {
case 4277009102: is_64bit = false; is_le = false; break;
case 4277009103: is_64bit = true; is_le = false; break;
case 3472551422: is_64bit = false; is_le = true; break;
case 3489328638: is_64bit = true; is_le = true; break;
case 3405691582: fprintf(stderr, "universal binary is not supported"); return 1;
default: fprintf(stderr, "invalid MachO magic number: %u\n", magic); return 1;
}
fseek(f, 3*4, SEEK_CUR);
uint32_t nlcs = read32(f);
fseek(f, (2+is_64bit)*4, SEEK_CUR);
if (nlcs > 1024) {
fprintf(stderr, "too many MachO load commands, header corrupted? (%08x)", nlcs);
return 1;
}
for (int i=0; i<nlcs; i++) {
uint32_t cmd = read32(f);
uint32_t sz = read32(f);
int next = ftell(f) + sz - 8;
// Search for the command LC_SEGMENT_64 on data segment
if (cmd == 0x19) {
char segname[17] = {0};
fread(segname, 1, 16, f);
if (!strcmp(segname, "__DATA")) {
fseek(f, 4*(1+is_64bit)*4, SEEK_CUR);
uint32_t maxprot = read32(f);
switch (maxprot) {
case VM_PROT_R | VM_PROT_W | VM_PROT_X:
return 0; // binary already patched, nothing to do
case VM_PROT_R | VM_PROT_W:
// Tell the kernel that the binary can upgrade __DATA to +x
maxprot |= VM_PROT_X;
fseek(f, -4, SEEK_CUR);
write32(f, maxprot);
fclose(f);
return 0;
default:
fprintf(stderr, "unexpected maxprot for __DATA segment: %x\n", maxprot);
return 1;
}
}
}
fseek(f, next, SEEK_SET);
}
fprintf(stderr, "cannot find LC_SEGMENT_64 for __DATA segment\n");
return 0;
}
+1
View File
@@ -73,6 +73,7 @@ auto FirmwareSettings::eventAssign() -> void {
BrowserDialog dialog;
dialog.setTitle({"Select ", name, " ", type, " (", region, ")"});
dialog.setPath(Path::desktop());
dialog.setAlignment(settingsWindow);
dialog.setFilters({"All|*"});
if(auto location = program.openFile(dialog)) {
firmware.location = location;
+4
View File
@@ -15,6 +15,7 @@ auto PathSettings::construct() -> void {
BrowserDialog dialog;
dialog.setTitle("Select Home Path");
dialog.setPath(Path::desktop());
dialog.setAlignment(settingsWindow);
if(auto location = program.selectFolder(dialog)) {
settings.paths.home = location;
refresh();
@@ -32,6 +33,7 @@ auto PathSettings::construct() -> void {
BrowserDialog dialog;
dialog.setTitle("Select Saves Path");
dialog.setPath(Path::desktop());
dialog.setAlignment(settingsWindow);
if(auto location = program.selectFolder(dialog)) {
settings.paths.saves = location;
refresh();
@@ -49,6 +51,7 @@ auto PathSettings::construct() -> void {
BrowserDialog dialog;
dialog.setTitle("Select Screenshots Path");
dialog.setPath(Path::desktop());
dialog.setAlignment(settingsWindow);
if(auto location = program.selectFolder(dialog)) {
settings.paths.screenshots = location;
refresh();
@@ -66,6 +69,7 @@ auto PathSettings::construct() -> void {
BrowserDialog dialog;
dialog.setTitle("Select Debugging Path");
dialog.setPath(Path::desktop());
dialog.setAlignment(settingsWindow);
if(auto location = program.selectFolder(dialog)) {
settings.paths.debugging = location;
refresh();
+6 -1
View File
@@ -62,7 +62,7 @@ auto pWindow::append(sStatusBar statusBar) -> void {
}
auto pWindow::focused() const -> bool {
return (GetForegroundWindow() == hwnd);
return focus;
}
auto pWindow::frameMargin() const -> Geometry {
@@ -329,6 +329,11 @@ auto pWindow::windowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -> m
}
}
if(msg == WM_ACTIVATE) {
focus = LOWORD(wparam) != WA_INACTIVE;
return false;
}
return {};
}
+1
View File
@@ -51,6 +51,7 @@ struct pWindow : pObject {
HBRUSH hbrush = nullptr;
COLORREF hbrushColor = 0;
Geometry windowedGeometry{128, 128, 256, 256};
bool focus = false;
};
}
+28 -29
View File
@@ -24,32 +24,31 @@ static void (*co_swap)(cothread_t, cothread_t) = 0;
section(text)
#endif
static const uint32_t co_swap_function[1024] = {
0xa9002428, /* stp x8,x9,[x1] */
0xa9012c2a, /* stp x10,x11,[x1,#16] */
0xa902342c, /* stp x12,x13,[x1,#32] */
0xa9033c2e, /* stp x14,x15,[x1,#48] */
0xf9002433, /* str x19,[x1,#72] */
0xa9055434, /* stp x20,x21,[x1,#80] */
0xa9065c36, /* stp x22,x23,[x1,#96] */
0xa9076438, /* stp x24,x25,[x1,#112] */
0xa9086c3a, /* stp x26,x27,[x1,#128] */
0xa909743c, /* stp x28,x29,[x1,#144] */
0x910003f0, /* mov x16,sp */
0xa90a7830, /* stp x16,x30,[x1,#160] */
0xa9402408, /* ldp x8,x9,[x0] */
0xa9412c0a, /* ldp x10,x11,[x0,#16] */
0xa942340c, /* ldp x12,x13,[x0,#32] */
0xa9433c0e, /* ldp x14,x15,[x0,#48] */
0xf9402413, /* ldr x19,[x0,#72] */
0xa9455414, /* ldp x20,x21,[x0,#80] */
0xa9465c16, /* ldp x22,x23,[x0,#96] */
0xa9476418, /* ldp x24,x25,[x0,#112] */
0xa9486c1a, /* ldp x26,x27,[x0,#128] */
0xa949741c, /* ldp x28,x29,[x0,#144] */
0xa94a4410, /* ldp x16,x17,[x0,#160] */
0x9100021f, /* mov sp,x16 */
0xd61f0220, /* br x17 */
0x910003f0, /* mov x16,sp */
0xa9007830, /* stp x16,x30,[x1] */
0xa9407810, /* ldp x16,x30,[x0] */
0x9100021f, /* mov sp,x16 */
0xa9015033, /* stp x19,x20,[x1, 16] */
0xa9415013, /* ldp x19,x20,[x0, 16] */
0xa9025835, /* stp x21,x22,[x1, 32] */
0xa9425815, /* ldp x21,x22,[x0, 32] */
0xa9036037, /* stp x23,x24,[x1, 48] */
0xa9436017, /* ldp x23,x24,[x0, 48] */
0xa9046839, /* stp x25,x26,[x1, 64] */
0xa9446819, /* ldp x25,x26,[x0, 64] */
0xa905703b, /* stp x27,x28,[x1, 80] */
0xa945701b, /* ldp x27,x28,[x0, 80] */
0xf900303d, /* str x29, [x1, 96] */
0xf940301d, /* ldr x29, [x0, 96] */
0x6d072428, /* stp d8, d9, [x1,112] */
0x6d472408, /* ldp d8, d9, [x0,112] */
0x6d082c2a, /* stp d10,d11,[x1,128] */
0x6d482c0a, /* ldp d10,d11,[x0,128] */
0x6d09342c, /* stp d12,d13,[x1,144] */
0x6d49340c, /* ldp d12,d13,[x0,144] */
0x6d0a3c2e, /* stp d14,d15,[x1,160] */
0x6d4a3c0e, /* ldp d14,d15,[x0,160] */
0xd61f03c0, /* br x30 */
};
static void co_init() {
@@ -77,9 +76,9 @@ cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void))
if(handle = (unsigned long*)memory) {
unsigned int offset = (size & ~15);
unsigned long* p = (unsigned long*)((unsigned char*)handle + offset);
handle[19] = (unsigned long)p; /* x29 (frame pointer) */
handle[20] = (unsigned long)p; /* x30 (stack pointer) */
handle[21] = (unsigned long)entrypoint; /* x31 (link register) */
handle[0] = (unsigned long)p; /* x16 (stack pointer) */
handle[1] = (unsigned long)entrypoint; /* x30 (link register) */
handle[12] = (unsigned long)p; /* x29 (frame pointer) */
}
return handle;
File diff suppressed because it is too large. Load diff
+226 -49
View File
@@ -30,6 +30,8 @@ auto Famicom::load(string location) -> bool {
pak->setAttribute("region", document["game/region"].string());
pak->setAttribute("board", document["game/board"].string());
pak->setAttribute("mirror", document["game/board/mirror/mode"].string());
pak->setAttribute("chip", document["game/board/chip/type"].string());
pak->setAttribute("chip/key", document["game/board/chip/key"].string());
pak->setAttribute("pinout/a0", document["game/board/chip/pinout/a0"].natural());
pak->setAttribute("pinout/a1", document["game/board/chip/pinout/a1"].natural());
pak->append("manifest.bml", manifest);
@@ -43,6 +45,10 @@ auto Famicom::load(string location) -> bool {
pak->append("program.rom", {view.data(), node["size"].natural()});
view += node["size"].natural();
}
if(auto node = document["game/board/memory(type=ROM,content=Option)"]) {
pak->append("option.rom", {view.data(), node["size"].natural()});
view += node["size"].natural();
}
if(auto node = document["game/board/memory(type=ROM,content=Character)"]) {
pak->append("character.rom", {view.data(), node["size"].natural()});
view += node["size"].natural();
@@ -124,6 +130,10 @@ auto Famicom::analyzeFDS(vector<u8>& data) -> string {
//iNES
auto Famicom::analyzeINES(vector<u8>& data) -> string {
string hash = Hash::SHA256({data.data() + 16, data.size() - 16}).digest();
string manifest = Medium::manifestDatabase(hash);
if(manifest) return manifest;
u32 mapper = ((data[7] >> 4) << 4) | (data[6] >> 4);
u32 mirror = ((data[6] & 0x08) >> 2) | (data[6] & 0x01);
u32 prgrom = data[4] * 0x4000;
@@ -131,7 +141,6 @@ auto Famicom::analyzeINES(vector<u8>& data) -> string {
u32 prgram = 0u;
u32 chrram = chrrom == 0u ? 8192u : 0u;
u32 eeprom = 0u;
string hash = Hash::SHA256({data.data() + 16, data.size() - 16}).digest();
string s;
s += "game\n";
@@ -140,31 +149,6 @@ auto Famicom::analyzeINES(vector<u8>& data) -> string {
s +={" title: ", Medium::name(location), "\n"};
s += " region: NTSC-J, NTSC-U, PAL\n"; //database required to detect region
//Family BASIC (Japan)
if(hash == "c8c0b6c21bdda7503bab7592aea0f945a0259c18504bb241aafb1eabe65846f3") {
prgram = 8_KiB;
}
//Gauntlet (USA)
if(hash == "fd2a8520314fb183e15fd62f48df97f92eb9c81140da4e6ab9ff0386e4797071") {
mapper = 206;
}
//Gauntlet (USA) [Unlicensed]
if(hash == "67b8a39744807dd740bdebcfe3d33bdac11a4d47b4807c0ffd35e322f8d670c2") {
mapper = 206;
}
//Jajamaru Gekimaden: Maboroshi no Kinmajou (Japan)
if(hash == "ea770788f68e4bb089e4205807931d64b83175a0106e7563d0a6d3ebac369991") {
prgram = 8_KiB;
}
//Mezase Top Pro: Green ni Kakeru Yume (Japan)
if(hash == "32c6893e0f8a14714dd2082803cfde62f8981010d23fc9cf00a5a18066d063cb") {
prgram = 8_KiB;
}
switch(mapper) {
default:
@@ -221,32 +205,52 @@ auto Famicom::analyzeINES(vector<u8>& data) -> string {
prgram = 8192;
break;
case 11:
s += " board: COLORDREAMS-74*377\n";
s +={" mirror mode=", !mirror ? "horizontal" : "vertical", "\n"};
break;
case 13:
s += " board: HVC-CPROM\n";
s +={" mirror mode=", !mirror ? "horizontal" : "vertical", "\n"};
chrram = 16384;
break;
case 16:
s += " board: BANDAI-FCG\n";
s += " board: BANDAI-LZ93D50\n";
s += " chip type=LZ93D50\n";
eeprom = 128;
eeprom = 256;
break;
case 18:
s += " board: JALECO-JF\n";
s += " board: JALECO-JF-23A\n";
s += " chip type=SS88006\n";
break;
case 19:
s += " board: NAMCO-163\n";
s += " chip type=163\n";
prgram = 8192;
break;
case 21:
case 23:
case 25:
//VRC4
s += " board: KONAMI-VRC-4\n";
s += " chip type=VRC4\n";
s += " pinout a0=1 a1=0\n";
s += " pinout a0=1 a1=2\n";
prgram = 8192;
break;
case 22:
//VRC2
s += " board: KONAMI-VRC-2A\n";
s += " chip type=VRC2\n";
s += " pinout a0=1 a1=0\n";
break;
case 23:
s += " board: KONAMI-VRC-2\n";
s += " chip type=VRC2\n";
s += " pinout a0=0 a1=1\n";
prgram = 8192;
break;
case 24:
@@ -255,6 +259,13 @@ auto Famicom::analyzeINES(vector<u8>& data) -> string {
s += " pinout a0=0 a1=1\n";
break;
case 25:
s += " board: KONAMI-VRC-4\n";
s += " chip type=VRC4\n";
s += " pinout a0=1 a1=0\n";
prgram = 8192;
break;
case 26:
s += " board: KONAMI-VRC-6\n";
s += " chip type=VRC6\n";
@@ -262,11 +273,32 @@ auto Famicom::analyzeINES(vector<u8>& data) -> string {
prgram = 8192;
break;
case 32:
s += " board: IREM-G101\n";
s += " chip type=G101\n";
prgram = 8192;
break;
case 33:
s += " board: TAITO-TC0190\n";
s += " chip type=TC0190\n";
break;
case 34:
s += " board: HVC-BNROM\n";
s +={" mirror mode=", !mirror ? "horizontal" : "vertical", "\n"};
break;
case 48:
s += " board: TAITO-TC0690\n";
s += " chip type=TC0690\n";
break;
case 65:
s += " board: IREM-H3001\n";
s += " chip type=H3001\n";
break;
case 66:
s += " board: HVC-GNROM\n";
s +={" mirror mode=", !mirror ? "horizontal" : "vertical", "\n"};
@@ -278,10 +310,6 @@ auto Famicom::analyzeINES(vector<u8>& data) -> string {
case 68:
s += " board: SUNSOFT-4\n";
s += " memory\n";
s += " type: ROM\n";
s += " size: 0x4000\n";
s += " content: Option\n";
prgram = 8192;
break;
@@ -291,58 +319,207 @@ auto Famicom::analyzeINES(vector<u8>& data) -> string {
prgram = 8192;
break;
case 70:
s += " board: BANDAI-74161\n";
s +={" mirror mode=", !mirror ? "horizontal" : "vertical", "\n"};
break;
case 72:
s += " board: JALECO-JF-17\n";
s +={" mirror mode=", !mirror ? "horizontal" : "vertical", "\n"};
break;
case 73:
s += " board: KONAMI-VRC-3\n";
s += " chip type=VRC3\n";
s +={" mirror mode=", !mirror ? "horizontal" : "vertical", "\n"};
prgram = 8192;
break;
case 75:
s += " board: KONAMI-VRC-1\n";
s += " chip type=VRC1\n";
break;
case 76:
s += " board: NAMCO-3446\n";
s += " chip type=118\n";
s +={" mirror mode=", !mirror ? "horizontal" : "vertical", "\n"};
break;
case 77:
s += " board: IREM-LROG017\n";
chrram = 8192;
break;
case 78:
s += " board: JALECO-JF-16\n";
break;
case 80:
s += " board: TAITO-X1-005\n";
s += " chip type=X1-005\n";
prgram = 128;
break;
case 82:
s += " board: TAITO-X1-017\n";
s += " chip type=X1-017\n";
prgram = 5120;
break;
case 85:
s += " board: KONAMI-VRC-7\n";
s += " chip type=VRC7\n";
s += " pinout a0=4\n";
prgram = 8192;
break;
case 86:
s += " board: JALECO-JF-13\n";
s +={" mirror mode=", !mirror ? "horizontal" : "vertical", "\n"};
break;
case 87:
s += " board: JALECO-JF-05\n";
s +={" mirror mode=", !mirror ? "horizontal" : "vertical", "\n"};
break;
case 88:
s += " board: NAMCO-3433\n";
s += " chip type=118\n";
s +={" mirror mode=", !mirror ? "horizontal" : "vertical", "\n"};
break;
case 89:
s += " board: SUNSOFT-2\n";
break;
case 92:
s += " board: JALECO-JF-19\n";
s +={" mirror mode=", !mirror ? "horizontal" : "vertical", "\n"};
break;
case 93:
s += " board: SUNSOFT-2\n";
s +={" mirror mode=", !mirror ? "horizontal" : "vertical", "\n"};
break;
case 97:
s += " board: IREM-TAM-S1\n";
break;
case 140:
s += " board: JALECO-JF11-JF14\n";
case 94:
s += " board: HVC-UN1ROM\n";
s +={" mirror mode=", !mirror ? "horizontal" : "vertical", "\n"};
break;
case 95:
s += " board: NAMCO-3425\n";
s += " chip type=118\n";
break;
case 96:
s += " board: BANDAI-OEKA\n";
s +={" mirror mode=", !mirror ? "horizontal" : "vertical", "\n"};
chrram = 32768;
break;
case 97:
s += " board: IREM-TAM-S1\n";
s += " chip type=TAM-S1\n";
break;
case 111:
s += " board: GTROM\n";
break;
case 118:
s += " board: HVC-TKSROM\n";
s += " chip type=MMC3B\n";
prgram = 8192;
break;
case 119:
s += " board: HVC-TQROM\n";
s += " chip type=MMC3B\n";
chrram = 8192;
break;
case 140:
s += " board: JALECO-JF-11\n";
s +={" mirror mode=", !mirror ? "horizontal" : "vertical", "\n"};
break;
case 152:
s += " board: BANDAI-74161A\n";
break;
case 153:
s += " board: BANDAI-LZ93D50\n";
s += " chip type=LZ93D50\n";
prgram = 8192;
break;
case 154:
s += " board: NAMCO-3453\n";
s += " chip type=118\n";
break;
case 155:
s += " board: HVC-SXROM\n";
s += " chip type=MMC1A\n";
prgram = 8192;
break;
case 157:
s += " board: BANDAI-LZ93D50\n";
s += " chip type=LZ93D50\n";
eeprom = 256;
break;
case 159:
s += " board: BANDAI-FCG\n";
s += " board: BANDAI-LZ93D50\n";
s += " chip type=LZ93D50\n";
eeprom = 128;
break;
case 180:
s += " board: HVC-UNROMA\n";
s +={" mirror mode=", !mirror ? "horizontal" : "vertical", "\n"};
break;
case 184:
s += " board: SUNSOFT-1\n";
s +={" mirror mode=", !mirror ? "horizontal" : "vertical", "\n"};
break;
case 206:
s += " board: HVC-DRROM\n";
chrram = 2048;
case 185:
s += " board: HVC-CNROM-SEC\n";
s += " chip type=SECURITY key=0x11\n";
s +={" mirror mode=", !mirror ? "horizontal" : "vertical", "\n"};
break;
case 188:
s += " board: BANDAI-KARAOKE\n";
break;
case 206:
s += " board: NAMCO-118\n";
s += " chip type=118\n";
s +={" mirror mode=", !mirror ? "horizontal" : "vertical", "\n"};
break;
case 207:
s += " board: TAITO-X1-005A\n";
s += " chip type=X1-005\n";
prgram = 128;
break;
case 210:
s += " board: NAMCO-340\n";
s += " chip type=340\n";
break;
case 228:
s += " board: MLT-ACTION52\n";
break;
}
s += " memory\n";
+6
View File
@@ -62,6 +62,12 @@ auto GameGear::analyze(vector<u8>& rom) -> string {
ms = 1;
}
//Mickey Mouse no Castle Illusion (Japan)
if(hash == "906e8da0e24899d30dffc4745b533e2a7c9d7ec2addbe853715165b552474d68") {
region = "NTSC-J";
ms = 1;
}
//Jang Pung II (Korea)
if(hash == "a685ad4118edd2fe6fb8ccc4bfc0c9ceac2f6affc3b5d93cc29afb2c9604d5ee") {
region = "NTSC-J";
+12 -28
View File
@@ -36,11 +36,20 @@ auto MegaCD::save(string location) -> bool {
auto MegaCD::analyze(string location) -> string {
auto sector = readDataSectorCUE(location, 0);
if(!sector) return CompactDisc::manifestAudio(location);
if(memory::compare(sector.data(), "SEGADISCSYSTEM ", 16)) {
if(!sector || memory::compare(sector.data(), "SEGA", 4))
return CompactDisc::manifestAudio(location);
vector<string> regions;
if(!memory::compare(sector.data()+4, "DISCSYSTEM ", 12)
|| !memory::compare(sector.data()+4, "BOOTDISC ", 12)) {
if( Hash::CRC32({sector.data()+0x200, 340}).value() == 0x4571f623) // JP boot
regions.append("NTSC-J");
else if(Hash::CRC32({sector.data()+0x200, 1390}).value() == 0x6ffb4732) // EU boot
regions.append("PAL");
else if(Hash::CRC32({sector.data()+0x200, 1412}).value() == 0xf361ab57) // US boot
regions.append("NTSC-U");
}
if(!regions) regions.append("NTSC-J","NTSC-U","PAL"); // unknown boot
vector<string> devices;
string device = slice((const char*)(sector.data() + 0x190), 0, 16).trimRight(" ");
@@ -64,31 +73,6 @@ auto MegaCD::analyze(string location) -> string {
if(id == 'V'); //paddle
}
vector<string> regions;
string region = slice((const char*)(sector.data() + 0x1f0), 0, 16).trimRight(" ");
if(!regions) {
if(region == "JAPAN" ) regions.append("NTSC-J");
if(region == "EUROPE") regions.append("PAL");
}
if(!regions) {
if(region.find("J")) regions.append("NTSC-J");
if(region.find("U")) regions.append("NTSC-U");
if(region.find("E")) regions.append("PAL");
}
if(!regions && region.size() == 1) {
maybe<u8> bits;
u8 field = region[0];
if(field >= '0' && field <= '9') bits = field - '0';
if(field >= 'A' && field <= 'F') bits = field - 'A' + 10;
if(bits && *bits & 1) regions.append("NTSC-J"); //domestic 60hz
if(bits && *bits & 2); //domestic 50hz
if(bits && *bits & 4) regions.append("NTSC-U"); //overseas 60hz
if(bits && *bits & 8) regions.append("PAL"); //overseas 50hz
}
if(!regions) {
regions.append("NTSC-J", "NTSC-U", "PAL");
}
string s;
s += "game\n";
s +={" name: ", Medium::name(location), "\n"};
+6
View File
@@ -302,6 +302,12 @@ auto MegaDrive::analyzeStorage(vector<u8>& rom, string hash) -> void {
ram.size = 32768;
}
//Super Hydlide (Japan)
if(hash == "30749097096807abf67cd1f7b2392f5789f5149ee33661e6d13113396f06a121") {
ram.mode = "lower";
ram.size = 8192;
}
//M28C16
//======
+41 -1
View File
@@ -39,15 +39,55 @@ auto MSX::save(string location) -> bool {
}
auto MSX::analyze(vector<u8>& rom) -> string {
string board = "Linear";
// If the rom is too big to be linear, attempt to guess the mapper
// based on the number of times specific banking instructions occur
// in the binary
if(rom.size() > 0x10000) {
const auto ASC16 = 0;
const auto ASC8 = 1;
const auto KONAMI = 2;
const auto KONAMI_SCC = 3;
int bankingCount[4] = {0,0,0,0};
for(auto i : range(rom.size() - 3)) {
if (rom[i] == 0x32) { // ld(nn),a
u16 value = rom[i + 1] + (rom[i + 2] << 8);
switch (value) {
case 0x5000: case 0x9000: case 0xb000: bankingCount[KONAMI_SCC]++; break;
case 0x4000: case 0x8000: case 0xa000: bankingCount[KONAMI]++; break;
case 0x6800: case 0x7800: bankingCount[ASC8]++; break;
case 0x6000: bankingCount[KONAMI]++; bankingCount[ASC8]++; bankingCount[ASC16]++; break;
case 0x7000: bankingCount[KONAMI_SCC]++; bankingCount[ASC8]++; bankingCount[ASC16]++; break;
case 0x77ff: bankingCount[ASC16]++; break;
}
}
}
auto mapperNum = 0, max = 0;
for(auto i : range(4)) {
if (bankingCount[i] > max) {max = bankingCount[i]; mapperNum = i;}
}
switch(mapperNum) {
case 0: board = "ASC16"; break;
case 1: board = "ASC8"; break;
case 2: board = "Konami"; break;
case 3: board = "KonamiSCC"; break;
}
}
string s;
s += "game\n";
s +={" name: ", Medium::name(location), "\n"};
s +={" title: ", Medium::name(location), "\n"};
s += " region: NTSC\n"; //database required to detect region
s += " board\n";
s +={" board: ", board, "\n"};
s += " memory\n";
s += " type: ROM\n";
s +={" size: 0x", hex(rom.size()), "\n"};
s += " content: Program\n";
return s;
}
+19 -5
View File
@@ -10,6 +10,18 @@ auto Pak::name(string location) const -> string {
return Location::prefix(location);
}
auto Pak::read(string location) -> vector<u8> {
//attempt to match known extensions
auto extensions = this->extensions();
for(auto& extension : extensions) extension.prepend("*.");
auto memory = read(location, extensions);
//failing that, read whatever exists
if(!memory) memory = read(location, {"*"});
return memory;
}
auto Pak::read(string location, vector<string> match) -> vector<u8> {
vector<u8> memory;
vector<u8> patch;
@@ -30,11 +42,13 @@ auto Pak::read(string location, vector<string> match) -> vector<u8> {
}
if(memory) break;
}
//support BPS patches inside the ZIP archive
for(auto& file : archive.file) {
if(file.name.imatch("*.bps")) {
patch = archive.extract(file);
break;
if(memory) {
//support BPS patches inside the ZIP archive
for(auto& file : archive.file) {
if(file.name.imatch("*.bps")) {
patch = archive.extract(file);
break;
}
}
}
}
+2 -1
View File
@@ -9,7 +9,8 @@ struct Pak {
virtual auto save(string location = {}) -> bool { return true; }
auto name(string location) const -> string;
auto read(string location, vector<string> match = {"*"}) -> vector<u8>;
auto read(string location) -> vector<u8>;
auto read(string location, vector<string> match) -> vector<u8>;
auto append(vector<u8>& data, string location) -> bool;
auto load(string name, string extension, string location = {}) -> bool;
auto save(string name, string extension, string location = {}) -> bool;
+2
View File
@@ -148,6 +148,8 @@ endif
ifeq ($(platform),macos)
flags += -stdlib=libc++ -mmacosx-version-min=10.9 -Wno-auto-var-id -fobjc-arc
options += -lc++ -lobjc -mmacosx-version-min=10.9
# allow mprotect() on dynamic recompiler code blocks
options += -Wl,-segprot,__DATA,rwx,rw
ifneq ($(local),true)
flags += -arch x86_64
options += -arch x86_64
+2 -1
View File
@@ -40,7 +40,8 @@ struct bump_allocator {
DWORD privileges;
VirtualProtect((void*)_memory, _capacity, PAGE_EXECUTE_READWRITE, &privileges);
#else
mprotect(_memory, _capacity, PROT_READ | PROT_WRITE | PROT_EXEC);
int ret = mprotect(_memory, _capacity, PROT_READ | PROT_WRITE | PROT_EXEC);
assert(ret == 0);
#endif
}
+29 -23
View File
@@ -13,9 +13,9 @@ struct BCD {
};
struct MSF {
u8 minute; //00-99
u8 second; //00-59
u8 frame = -1; //00-74
u8 minute; //00-99
u8 second; //00-59
u8 frame = 0xff; //00-74
MSF() = default;
MSF(u8 m, u8 s, u8 f) : minute(m), second(s), frame(f) {}
@@ -67,10 +67,9 @@ struct Index {
struct Track {
u8 control = 0b1111; //4-bit
u8 address = 0b1111; //4-bit
Index indices[100];
u8 firstIndex = -1;
u8 lastIndex = -1;
u8 firstIndex = 0xff;
u8 lastIndex = 0xff;
explicit operator bool() const {
return (bool)indices[1];
@@ -125,27 +124,27 @@ struct Session {
Index leadIn; //00
Track tracks[100]; //01-99
Index leadOut; //aa
u8 firstTrack = -1;
u8 lastTrack = -1;
u8 firstTrack = 0xff;
u8 lastTrack = 0xff;
auto inLeadIn(s32 lba) const -> bool {
return lba < 0;
return leadIn && lba <= leadIn.end;
}
auto inTrack(s32 lba) const -> maybe<u8> {
for(u8 trackID : range(100)) {
auto& track = tracks[trackID];
if(track && track.inRange(lba)) return trackID;
for(u8 trackID : range(99)) {
auto& track = tracks[trackID+1];
if(track && track.inRange(lba)) return trackID+1;
}
return {};
}
auto inLeadOut(s32 lba) const -> bool {
return lba >= leadOut.lba;
return leadOut && lba >= leadOut.lba;
}
auto track(u8 trackID) -> maybe<Track&> {
if(trackID < 100 && tracks[trackID]) return tracks[trackID];
if(trackID >= 1 && trackID < 100 && tracks[trackID]) return tracks[trackID];
return {};
}
@@ -173,7 +172,7 @@ struct Session {
auto& track = tracks[trackID];
if(!track) continue;
auto q = toQ(lba);
q[0] = track.control << 4 | track.address << 0;
q[0] = track.control << 4 | 1;
q[1] = 0x00;
q[2] = BCD::encode(trackID);
auto msf = MSF(lba);
@@ -189,7 +188,8 @@ struct Session {
q[10] = crc16 >> 8;
q[11] = crc16 >> 0;
if(++lba >= 0) break;
}}if( lba >= 0) break;
} if( lba >= 0) break;
} if( lba >= 0) break;
//first track
for(u32 repeat : range(3)) {
@@ -270,7 +270,7 @@ struct Session {
for(u32 index : range(12)) p[index] = byte;
auto q = toQ(lba);
q[0] = track.control << 4 | track.address << 0;
q[0] = track.control << 4 | 1;
q[1] = BCD::encode(trackID);
q[2] = BCD::encode(indexID);
auto msf = MSF(lba - track.indices[1].lba);
@@ -291,6 +291,13 @@ struct Session {
}
}
//pre-lead-out (2-3s at the end of last track)
for(auto i : range(150)) {
auto p = toP(leadOut.lba - 150 + i);
for(auto sig : range(12)) {
p[sig]= 0xff;
}}
//lead-out
for(s32 lba : range(sectors - abs(leadIn.lba) - leadOut.lba)) {
auto p = toP(leadOut.lba + lba);
@@ -300,7 +307,7 @@ struct Session {
byte = 0x00;
} else {
//2hz duty cycle; rounded downward (standard specifies 2% tolerance)
byte = (lba - 150) / (75 >> 1) & 1 ? 0xff : 0x00;
byte = (lba - 150) / (75 >> 1) & 1 ? 0x00 : 0xff;
}
for(u32 index : range(12)) p[index] = byte;
@@ -332,6 +339,7 @@ struct Session {
if(size != 12 && size != 96 && size != 2448) return false;
//determine lead-in sector count
leadIn.lba = InvalidLBA;
for(s32 lba : range(7500)) { //7500 max sectors scanned
u32 offset = lba * size;
if(size == 96) offset += 12;
@@ -348,8 +356,7 @@ struct Session {
if(address != 1) continue;
if(trackID != 0) continue;
auto msf = MSF::fromBCD(q[3], q[4], q[5]);
leadIn.lba = msf.toLBA() - lba;
leadIn.lba = lba - 7500;
break;
}
if(leadIn.lba == InvalidLBA || leadIn.lba >= 0) return false;
@@ -363,6 +370,7 @@ struct Session {
};
//lead-in
leadOut.lba = InvalidLBA;
for(s32 lba = leadIn.lba; lba < 0; lba++) {
auto q = toQ(lba);
if(!q) break;
@@ -381,7 +389,6 @@ struct Session {
if(trackID <= 99) { //00-99
auto& track = tracks[trackID];
track.control = control;
track.address = address;
track.indices[1].lba = MSF::fromBCD(q[7], q[8], q[9]).toLBA();
}
@@ -412,7 +419,7 @@ struct Session {
u8 trackID = BCD::decode(q[1]);
u8 indexID = BCD::decode(q[2]);
if(address != 1) continue;
if(trackID > 99) continue;
if(trackID == 0 || trackID > 99) continue;
if(indexID > 99) continue;
auto& track = tracks[trackID];
@@ -471,7 +478,6 @@ struct Session {
if(trackID == lastTrack) s.append( " last");
s.append("\n");
s.append(" control: ", binary(track.control, 4, '0'), "\n");
s.append(" address: ", binary(track.address, 4, '0'), "\n");
for(u32 indexID : range(100)) {
auto& index = track.indices[indexID];
if(!index) continue;
+13 -3
View File
@@ -11,7 +11,7 @@ struct CUE {
struct Index {
auto sectorCount() const -> u32;
s32 number; //01-99
u8 number = 0xff; //00-99
s32 lba = -1;
s32 end = -1;
};
@@ -20,7 +20,7 @@ struct CUE {
auto sectorCount() const -> u32;
auto sectorSize() const -> u32;
s32 number; //01-99
u8 number = 0xff; //01-99
string type;
vector<Index> indices;
maybe<s32> pregap;
@@ -67,10 +67,12 @@ inline auto CUE::load(const string& location) -> bool {
if(!files.first().tracks) return false;
if(!files.first().tracks.first().indices) return false;
// calculate index ends for all but the last index
for(auto& file : files) {
maybe<Index&> previous;
for(auto& track : file.tracks) {
for(auto& index : track.indices) {
if(index.lba < 0) continue; // ignore gaps (not in file)
if(previous) previous->end = index.lba - 1;
previous = index;
}
@@ -123,21 +125,27 @@ inline auto CUE::loadTrack(vector<string>& lines, u32& offset) -> Track {
if(lines[offset].ibeginsWith("TRACK ")) break;
if(lines[offset].ibeginsWith("INDEX ")) {
auto index = loadIndex(lines, offset);
if(index.number == 0 && track.number == 1)
index.lba = 0; // ignore track 1 index 0 (assume 1st pregap always starts at origin)
track.indices.append(index);
continue;
}
if(lines[offset].ibeginsWith("PREGAP ")) {
track.pregap = toLBA(lines[offset++].itrimLeft("PREGAP ", 1L));
Index index; index.number = 0; index.lba = -1;
track.indices.append(index); // placeholder
continue;
}
if(lines[offset].ibeginsWith("POSTGAP ")) {
track.postgap = toLBA(lines[offset++].itrimLeft("POSTGAP ", 1L));
Index index; index.number = track.indices.last().number + 1; index.lba = -1;
track.indices.append(index); // placeholder
continue;
}
offset++;
}
if(track.number > 99) return {};
if(track.number == 0 || track.number > 99) return {};
return track;
}
@@ -187,8 +195,10 @@ inline auto CUE::File::scan(const string& pathname) -> bool {
return false;
}
// calculate last index end for the file
for(auto& track : tracks) {
for(auto& index : track.indices) {
if(index.lba < 0) continue; // ignore gaps (not in file)
if(index.end >= 0) {
size -= track.sectorSize() * index.sectorCount();
} else {
+56 -35
View File
@@ -51,66 +51,87 @@ private:
CD::Session session;
session.leadIn.lba = -LeadInSectors;
session.leadIn.end = -1;
s32 lbaDisc = Track1Pregap;
s32 endDisc = lbaDisc;
s32 lbaFileBase = 0;
// add 2 sec pregap to 1st track
if(!cuesheet.files[0].tracks[0].pregap)
cuesheet.files[0].tracks[0].pregap = Track1Pregap;
else
cuesheet.files[0].tracks[0].pregap = Track1Pregap + cuesheet.files[0].tracks[0].pregap();
if(cuesheet.files[0].tracks[0].indices[0].number == 1) {
session.tracks[1].indices[0].lba = 0;
session.tracks[1].indices[0].end =
cuesheet.files[0].tracks[0].pregap() + cuesheet.files[0].tracks[0].indices[0].lba - 1;
}
s32 lbaIndex = 0;
for(auto& file : cuesheet.files) {
for(auto& track : file.tracks) {
session.tracks[track.number].control = track.type == "audio" ? 0b0000 : 0b0100;
session.tracks[track.number].address = 0b0001;
if(track.pregap) lbaFileBase += track.pregap();
for(auto& index : track.indices) {
session.tracks[track.number].indices[index.number].lba = lbaDisc + index.lba;
session.tracks[track.number].indices[index.number].end = lbaDisc + index.end;
if(index.lba >= 0) {
session.tracks[track.number].indices[index.number].lba = lbaFileBase + index.lba;
session.tracks[track.number].indices[index.number].end = lbaFileBase + index.end;
if(index.number == 0 && track.pregap) {
session.tracks[track.number].indices[index.number].lba -= track.pregap();
session.tracks[track.number].indices[index.number].end -= track.pregap();
}
} else {
// insert gap
session.tracks[track.number].indices[index.number].lba = lbaIndex;
if(index.number == 0)
session.tracks[track.number].indices[index.number].end = lbaIndex + track.pregap() - 1;
else
session.tracks[track.number].indices[index.number].end = lbaIndex + track.postgap() - 1;
}
lbaIndex = session.tracks[track.number].indices[index.number].end + 1;
}
if(track.postgap) lbaFileBase += track.postgap();
}
lbaDisc += file.tracks.last().indices.last().end + 1;
endDisc = lbaDisc;
lbaFileBase = lbaIndex;
}
session.leadOut.lba = endDisc;
session.leadOut.end = endDisc + LeadOutSectors - 1;
session.leadOut.lba = lbaFileBase;
session.leadOut.end = lbaFileBase + LeadOutSectors - 1;
// determine track and index ranges
session.firstTrack = 0xff;
for(u32 track : range(100)) {
if(!session.tracks[track]) continue;
session.firstTrack = track;
for(u32 index : range(100)) {
if(!session.tracks[track].indices[index]) continue;
session.tracks[track].firstIndex = index;
break;
if(session.firstTrack > 99) session.firstTrack = track;
// find first index
for(u32 indexID : range(100)) {
auto& index = session.tracks[track].indices[indexID];
if(index) { session.tracks[track].firstIndex = indexID; break; }
}
// find last index
for(u32 indexID : reverse(range(100))) {
auto& index = session.tracks[track].indices[indexID];
if(index) { session.tracks[track].lastIndex = indexID; break; }
}
break;
}
for(u32 track : reverse(range(100))) {
if(!session.tracks[track]) continue;
session.lastTrack = track;
for(u32 index : reverse(range(100))) {
if(!session.tracks[track].indices[index]) continue;
session.tracks[track].lastIndex = index;
break;
}
break;
}
session.tracks[1].indices[0].lba = 0; //track 1, index 0 is not present in CUE files
session.tracks[1].indices[0].end = Track1Pregap - 1;
_image.resize(2448 * (LeadInSectors + lbaFileBase + LeadOutSectors));
_image.resize(2448 * (LeadInSectors + endDisc + LeadOutSectors));
lbaDisc = Track1Pregap;
lbaFileBase = 0;
for(auto& file : cuesheet.files) {
auto location = string{Location::path(cueLocation), file.name};
auto filedata = nall::file::open(location, nall::file::mode::read);
if(file.type == "wave") filedata.seek(44); //skip RIFF header
u64 offset = 0;
for(auto& track : file.tracks) {
if(track.pregap) lbaFileBase += track.pregap();
for(auto& index : track.indices) {
if(index.lba < 0) continue; // ignore gaps (not in file)
for(s32 sector : range(index.sectorCount())) {
auto target = _image.data() + 2448ull * (LeadInSectors + lbaDisc + index.lba + sector);
auto target = _image.data() + 2448ull * (LeadInSectors + lbaFileBase + index.lba + sector);
auto length = track.sectorSize();
if(length == 2048) {
//ISO: generate header + parity data
memory::assign(target + 0, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff); //sync
memory::assign(target + 6, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00); //sync
auto [minute, second, frame] = CD::MSF(lbaDisc + index.lba + sector);
auto [minute, second, frame] = CD::MSF(lbaFileBase + index.lba + sector);
target[12] = CD::BCD::encode(minute);
target[13] = CD::BCD::encode(second);
target[14] = CD::BCD::encode(frame);
@@ -124,9 +145,9 @@ private:
}
}
}
offset += track.sectorSize() * track.sectorCount();
if(track.postgap) lbaFileBase += track.postgap();
}
lbaDisc += file.tracks.last().indices.last().end + 1;
lbaFileBase += file.tracks.last().indices.last().end + 1;
}
auto subchannel = session.encode(LeadInSectors + session.leadOut.end + 1);