mirror of
https://github.com/ares-emulator/ares.git
synced 2026-11-10 09:21:41 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee188dcb31 | ||
|
|
1f8b3f86b9 | ||
|
|
18e334a6cf | ||
|
|
24675b0161 | ||
|
|
f4caedcd5e | ||
|
|
bdc6313e46 | ||
|
|
fd3a56b0a2 | ||
|
|
663b6951e3 | ||
|
|
554430276c | ||
|
|
3748baf8e8 | ||
|
|
e1bd1bfdf9 | ||
|
|
5f6b75a013 | ||
|
|
2d6fe4aa16 | ||
|
|
b49e5496da | ||
|
|
645d481493 | ||
|
|
bc14c35cb5 | ||
|
|
16210cf1bf | ||
|
|
d34e54c808 | ||
|
|
76508d02bd | ||
|
|
32690bb6f0 | ||
|
|
9eca885d8b | ||
|
|
f9a53d2167 | ||
|
|
fd5b22c5cd | ||
|
|
2f6edc7efe | ||
|
|
12ac1b5534 | ||
|
|
c8fa67ec39 | ||
|
|
5341d0d7d4 | ||
|
|
ad0105de25 | ||
|
|
e3663a9eec | ||
|
|
a01ccb72f2 | ||
|
|
27996d81d4 | ||
|
|
47af07c507 | ||
|
|
cc6ab702c0 | ||
|
|
d633b92a57 | ||
|
|
579be2bb04 | ||
|
|
89db771c73 | ||
|
|
33b95510cb | ||
|
|
31eee5f6f1 | ||
|
|
7ba1c2925d | ||
|
|
dbc1b4ccc6 | ||
|
|
a3a1969775 | ||
|
|
ab67ec12cd | ||
|
|
02cb1268e4 | ||
|
|
50994a2c60 | ||
|
|
9e099e9b15 | ||
|
|
16cbd8b149 | ||
|
|
5491a6a9c8 | ||
|
|
d0a14c682d | ||
|
|
4eb4b6cdb7 | ||
|
|
9bc59159d0 | ||
|
|
f518f04ffa | ||
|
|
2ce06bc4d1 | ||
|
|
a3285b6959 | ||
|
|
8a7ac00d03 | ||
|
|
38cf1b0f8e | ||
|
|
d26ffccc9f | ||
|
|
9ee4afd17f | ||
|
|
61dbb64999 | ||
|
|
2d5e1ba552 | ||
|
|
a31b0a7768 | ||
|
|
7c9ff4ba6d | ||
|
|
0bc30dd26d | ||
|
|
f73032e17f | ||
|
|
2eee9bcd4e | ||
|
|
e3a3927e95 | ||
|
|
f04e55f863 | ||
|
|
08aecccc5a | ||
|
|
3471756c06 | ||
|
|
d40e6e1cd3 | ||
|
|
6a262e01ae | ||
|
|
2f6a51b3ef | ||
|
|
ee3b764b8e | ||
|
|
4216d5ae0a | ||
|
|
878efe0a05 | ||
|
|
98aee19e93 | ||
|
|
7810a05b60 | ||
|
|
b83e868a6e | ||
|
|
ce3153b10e | ||
|
|
259642ae2a | ||
|
|
30921061c7 | ||
|
|
757da94463 | ||
|
|
1c026f5edf | ||
|
|
2d90b6343d | ||
|
|
4c312ab2af | ||
|
|
55d197fdc3 | ||
|
|
53290b9c71 | ||
|
|
764a881d36 | ||
|
|
a10e089100 | ||
|
|
590ec4cb32 | ||
|
|
30d143092d | ||
|
|
2dbd5a43a0 | ||
|
|
08c013dc46 | ||
|
|
0d169fe641 |
No files matched your search
+34
-10
@@ -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,4 +1,4 @@
|
||||
# ares
|
||||

|
||||
|
||||
[](https://github.com/higan-emu/ares/blob/master/LICENSE)
|
||||
|
||||
|
||||
+2
-2
@@ -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>;
|
||||
|
||||
@@ -36,6 +36,7 @@ auto Screen::main(uintptr_t) -> void {
|
||||
auto Screen::quit() -> void {
|
||||
_kill = true;
|
||||
_thread.join();
|
||||
_sprites.reset();
|
||||
}
|
||||
|
||||
auto Screen::power() -> void {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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];
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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 {
|
||||
|
||||
+10
-13
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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];
|
||||
};
|
||||
@@ -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) {
|
||||
|
||||
@@ -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];
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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];
|
||||
};
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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/
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {};
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ struct pWindow : pObject {
|
||||
HBRUSH hbrush = nullptr;
|
||||
COLORREF hbrushColor = 0;
|
||||
Geometry windowedGeometry{128, 128, 256, 256};
|
||||
bool focus = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
+28
-29
@@ -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
@@ -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";
|
||||
|
||||
@@ -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
@@ -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"};
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user