diff --git a/ares/ng/apu/apu.cpp b/ares/ng/apu/apu.cpp index becb0c9fb1..04d46e2aec 100644 --- a/ares/ng/apu/apu.cpp +++ b/ares/ng/apu/apu.cpp @@ -9,7 +9,7 @@ APU apu; auto APU::load(Node::Object parent) -> void { node = parent->append("APU"); - ram.allocate(2_KiB); + ram.allocate(Model::NeoGeoCD() ? 64_KiB : 2_KiB); debugger.load(node); } @@ -50,4 +50,13 @@ auto APU::power(bool reset) -> void { rom = {}; } +auto APU::restart() -> void { + Z80::reset(); + Thread::restart({&APU::main, this}); + communication = {}; + nmi = {}; + irq = {}; + rom = {}; +} + } diff --git a/ares/ng/apu/apu.hpp b/ares/ng/apu/apu.hpp index a0ad6a0b9d..2dd4ee195c 100644 --- a/ares/ng/apu/apu.hpp +++ b/ares/ng/apu/apu.hpp @@ -30,6 +30,7 @@ struct APU : Z80, Z80::Bus, Thread { auto main() -> void; auto step(u32 clocks) -> void override; auto power(bool reset) -> void; + auto restart() -> void; //memory.cpp auto read(n16 address) -> n8 override; diff --git a/ares/ng/apu/memory.cpp b/ares/ng/apu/memory.cpp index cab3529944..df8e6f3d36 100644 --- a/ares/ng/apu/memory.cpp +++ b/ares/ng/apu/memory.cpp @@ -1,4 +1,5 @@ auto APU::read(n16 address) -> n8 { + if(Model::NeoGeoCD()) return ram[address]; if(address <= 0x7fff) return cartridge.readM(address); if(address <= 0xbfff) return cartridge.readM(rom.bankA << 14 | n14(address)); if(address <= 0xdfff) return cartridge.readM(rom.bankB << 13 | n13(address)); @@ -9,6 +10,11 @@ auto APU::read(n16 address) -> n8 { } auto APU::write(n16 address, n8 data) -> void { + if(Model::NeoGeoCD()) { + ram[address] = data; + return; + } + if(address >= 0xf800) { ram[address & 0x7ff] = data; return; diff --git a/ares/ng/cpu/cpu.cpp b/ares/ng/cpu/cpu.cpp index 3ad54cea90..fb309535f7 100644 --- a/ares/ng/cpu/cpu.cpp +++ b/ares/ng/cpu/cpu.cpp @@ -34,11 +34,13 @@ auto CPU::main() -> void { if(2 > r.i && lower(Interrupt::Timer)) { debugger.interrupt("Timer"); + if(Model::NeoGeoCD()) return interrupt(Vector::Level1, 1); return interrupt(Vector::Level2, 2); } if(1 > r.i && lower(Interrupt::Vblank)) { debugger.interrupt("Vblank"); + if(Model::NeoGeoCD()) return interrupt(Vector::Level2, 2); return interrupt(Vector::Level1, 1); } } diff --git a/ares/ng/cpu/memory.cpp b/ares/ng/cpu/memory.cpp index c486074282..69787e094f 100644 --- a/ares/ng/cpu/memory.cpp +++ b/ares/ng/cpu/memory.cpp @@ -8,19 +8,23 @@ auto CPU::read(n1 upper, n1 lower, n24 address, n16 data) -> n16 { if(auto result = platform->cheat(address)) return *result; - //cartridge program ROM - if(address <= 0x0fffff) { - return cartridge.readP(upper, lower, address, data); - } + if(!Model::NeoGeoCD()) { + //cartridge program ROM + if (address <= 0x0fffff) { + return cartridge.readP(upper, lower, address, data); + } - //work RAM - if(address <= 0x1fffff) { - return system.wram[address >> 1]; - } + //work RAM + if (address <= 0x1fffff) { + return system.wram[address >> 1]; + } - //cartridge program ROM (banked) - if(address <= 0x2fffff) { - return cartridge.readP(upper, lower, address, data); + //cartridge program ROM (banked) + if (address <= 0x2fffff) { + return cartridge.readP(upper, lower, address, data); + } + } else if (address <= 0x1fffff) { + return system.wram[address >> 1]; } //I/O registers @@ -52,30 +56,47 @@ auto CPU::read(n1 upper, n1 lower, n24 address, n16 data) -> n16 { return data; } - //CD-ROM - if(address <= 0xffffff) { - return data; + if(Model::NeoGeoCD()) { + if (address <= 0xefffff) { + switch (system.io.uploadZone) { + case 0: return system.spriteRam[(address & 0xfffff) >> 1]; + case 1: return system.pcmRam[(address & 0xfffff) >> 1]; + case 4: return apu.ram[(address & 0x1ffff) + upper]; + case 5: return system.fixRam[(address & 0x3ffff) >> 1]; + } + } + + //CD-ROM + if (address <= 0xffffff) { + return readIO(upper, lower, address, data); + } } return data; } auto CPU::write(n1 upper, n1 lower, n24 address, n16 data) -> void { - //cartridge program ROM - if(address <= 0x0fffff) { - return cartridge.writeP(upper, lower, address, data); - } + if(!Model::NeoGeoCD()) { + //cartridge program ROM + if (address <= 0x0fffff) { + return cartridge.writeP(upper, lower, address, data); + } - //work RAM - if(address <= 0x1fffff) { - if(upper) system.wram[address >> 1].byte(1) = data.byte(1); - if(lower) system.wram[address >> 1].byte(0) = data.byte(0); - return; - } + //work RAM + if (address <= 0x1fffff) { + if (upper) system.wram[address >> 1].byte(1) = data.byte(1); + if (lower) system.wram[address >> 1].byte(0) = data.byte(0); + return; + } - //cartridge program ROM (banked) - if(address <= 0x2fffff) { - return cartridge.writeP(upper, lower, address, data); + //cartridge program ROM (banked) + if (address <= 0x2fffff) { + return cartridge.writeP(upper, lower, address, data); + } + } else if (address <= 0x1fffff) { + if (upper) system.wram[address >> 1].byte(1) = data.byte(1); + if (lower) system.wram[address >> 1].byte(0) = data.byte(0); + return; } //I/O registers @@ -107,9 +128,32 @@ auto CPU::write(n1 upper, n1 lower, n24 address, n16 data) -> void { return; } - //CD-ROM - if(address <= 0xffffff) { - return; + if(Model::NeoGeoCD()) { + if(address <= 0xefffff) { + switch(system.io.uploadZone) { + case 0: address &= 0xfffff; + address.bit(20, 21) = system.io.spriteUploadBank; + if(upper) system.spriteRam[(address) >> 1].byte(1) = data.byte(1); + if(lower) system.spriteRam[(address) >> 1].byte(0) = data.byte(0); + return; + case 1: address &= 0xfffff; + address.bit(19) = system.io.pcmUploadBank; + address >>= 1; + if(lower) system.pcmRam[address] = data.byte(0); + return; + case 4: address >>= 1; + if(lower) apu.ram[(address)] = data.byte(0); + return; + case 5: address >>= 1; + if(lower) system.fixRam[address].byte(0) = data.byte(0); + return; + } + } + + //CD-ROM + if (address <= 0xffffff) { + return writeIO(upper, lower, address, data); + } } } @@ -117,6 +161,7 @@ auto CPU::readIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 { //REG_P1CNT if((address & 0xfe0000) == 0x300000 && upper) { data.byte(1) = controllerPort1.readButtons(); + return data; } //REG_DIPSW @@ -128,17 +173,20 @@ auto CPU::readIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 { data.bit(5) = 1; //enable multiplayer data.bit(6) = 1; //freeplay data.bit(7) = 1; //freeze + return data; } //REG_SYSTYPE if((address & 0xfe0080) == 0x300080 && lower) { data.bit(6) = 0; //0 = 2 slots; 1 = 4 or 6 slots data.bit(7) = 1; //test button + return data; } //REG_SOUND if((address & 0xfe0000) == 0x320000 && upper) { data.byte(1) = apu.communication.output; + return data; } //REG_STATUS_A @@ -151,11 +199,13 @@ auto CPU::readIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 { data.bit(5) = 0; //0 = 4-slot; 1 = 6-slot data.bit(6) = 0; //RTC time pulse data.bit(7) = 0; //RTC data bit + return data; } //REG_P2CNT if((address & 0xfe0000) == 0x340000 && upper) { data.byte(1) = controllerPort2.readButtons(); + return data; } //REG_STATUS_B @@ -165,21 +215,22 @@ auto CPU::readIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 { data.bit(12,13) = 0b11; //0b00 = memory card inserted data.bit(14) = cardSlot.lock != 0; data.bit(15) = Model::NeoGeoMVS(); //0 = AES; 1 = MVS + return data; } //REG_VRAMADDR if((address & 0xfe0006) == 0x3c0000) { - data = lspc.vram[lspc.io.vramAddress]; + return data = lspc.vram[lspc.io.vramAddress];; } //REG_VRAMRW if((address & 0xfe0006) == 0x3c0002) { - data = lspc.vram[lspc.io.vramAddress]; + return data = lspc.vram[lspc.io.vramAddress]; } //REG_VRAMMOD if((address & 0xfe0006) == 0x3c0004) { - data = lspc.io.vramIncrement; + return data = lspc.io.vramIncrement; } //REG_LSPCMODE @@ -188,8 +239,10 @@ auto CPU::readIO(n1 upper, n1 lower, n24 address, n16 data) -> n16 { data.bit(3) = 0; //0 = 60hz; 1 = 50hz data.bit(4, 6) = 0; //unused data.bit(7,15) = lspc.io.vcounter + 248; + return data; } + debug(unimplemented, "readIO: ", hex(address, 6L)); return data; } @@ -197,23 +250,27 @@ auto CPU::writeIO(n1 upper, n1 lower, n24 address, n16 data) -> void { //REG_DIPSW if((address & 0xfe0080) == 0x300000 && lower) { //todo: kick watchdog + return; } //REG_SOUND if((address & 0xfe0000) == 0x320000 && upper) { apu.communication.input = data.byte(1); apu.nmi.pending = 1; + return; } //REG_POUTPUT if((address & 0xfe0070) == 0x380000 && lower) { controllerPort1.writeOutputs(data.bit(0,2)); controllerPort2.writeOutputs(data.bit(3,5)); + return; } //REG_CRDBANK if((address & 0xfe0070) == 0x380010 && lower) { cardSlot.bank = data.bit(0,2); + return; } //REG_POUTPUT (mirror) (AES only) @@ -226,6 +283,7 @@ auto CPU::writeIO(n1 upper, n1 lower, n24 address, n16 data) -> void { if(Model::NeoGeoMVS()) { system.io.slotSelect = data.bit(0,2); } + return; } //REG_LEDLATCHES @@ -233,11 +291,13 @@ auto CPU::writeIO(n1 upper, n1 lower, n24 address, n16 data) -> void { system.io.ledMarquee = data.bit(3); system.io.ledLatch1 = data.bit(4); system.io.ledLatch2 = data.bit(5); + return; } //REG_LEDDATA if((address & 0xfe00f0) == 0x380040 && lower) { system.io.ledData = data.bit(0,7); + return; } //REG_RTCCTRL @@ -245,132 +305,158 @@ auto CPU::writeIO(n1 upper, n1 lower, n24 address, n16 data) -> void { //rtc.din = data.bit(0); //rtc.clock = data.bit(1); //rtc.strobe = data.bit(2); + return; } //REG_RESETCC1 if((address & 0xfe00f6) == 0x380060 && lower) { //todo: float coin counter 1 + return; } //REG_RESETCC2 if((address & 0xfe00f6) == 0x380062 && lower) { //todo: float coin counter 2 + return; } //REG_RESETCL1 if((address & 0xfe00f6) == 0x380064 && lower) { //todo: float coin lockout 1 + return; } //REG_RESETCL2 if((address & 0xfe00f6) == 0x380066 && lower) { //todo: float coin lockout 2 + return; } //REG_SETCC1 if((address & 0xfe00f6) == 0x3800e0 && lower) { //todo: sink coin counter 1 + return; } //REG_SETCC2 if((address & 0xfe00f6) == 0x3800e2 && lower) { //todo: sink coin counter 2 + return; } //REG_SETCL1 if((address & 0xfe00f6) == 0x3800e4 && lower) { //todo: sink coin lockout 1 + return; } //REG_SETCL2 if((address & 0xfe00f6) == 0x3800e6 && lower) { //todo: sink coin lockout 2 + return; } //REG_NOSHADOW if((address & 0xfe001e) == 0x3a0000 && lower) { lspc.io.shadow = 0; + return; } //REG_SWPBIOS if((address & 0xfe001e) == 0x3a0002 && lower) { io.vectorSelect = 0; + return; } //REG_CRDUNLOCK1 if((address & 0xfe001e) == 0x3a0004 && lower) { cardSlot.lock.bit(0) = 0; + return; } //REG_CRDLOCK2 if((address & 0xfe001e) == 0x3a0006 && lower) { cardSlot.lock.bit(1) = 1; + return; } //REG_CRDREGSEL if((address & 0xfe001e) == 0x3a0008 && lower) { cardSlot.select = 1; + return; } //REG_BRDFIX if((address & 0xfe001e) == 0x3a000a && lower) { io.fixSelect = 0; + return; } //REG_SRAMLOCK if((address & 0xfe001e) == 0x3a000c && lower) { system.io.sramLock = 1; + return; } //REG_PALBANK1 if((address & 0xfe001e) == 0x3a000e && lower) { lspc.io.pramBank = 1; + return; } //REG_SHADOW if((address & 0xfe001e) == 0x3a0010 && lower) { lspc.io.shadow = 1; + return; } //REG_SWPROM if((address & 0xfe001e) == 0x3a0012 && lower) { io.vectorSelect = 1; + return; } //REG_CRDLOCK1 if((address & 0xfe001e) == 0x3a0014 && lower) { cardSlot.lock.bit(0) = 1; + return; } //REG_CRDUNLOCK2 if((address & 0xfe001e) == 0x3a0016 && lower) { cardSlot.lock.bit(1) = 0; + return; } //REG_CRDNORMAL if((address & 0xfe001e) == 0x3a0018 && lower) { cardSlot.select = 0; + return; } //REG_CRTFIX if((address & 0xfe001e) == 0x3a001a && lower) { io.fixSelect = 1; + return; } //REG_SRAMUNLOCK if((address & 0xfe001e) == 0x3a001c && lower) { system.io.sramLock = 0; + return; } //REG_PALBANK0 if((address & 0xfe001e) == 0x3a001e && lower) { lspc.io.pramBank = 0; + return; } //REG_VRAMADDR if((address & 0xfe000e) == 0x3c0000) { if(upper) lspc.io.vramAddress.byte(1) = data.byte(1); if(lower) lspc.io.vramAddress.byte(0) = data.byte(0); + return; } //REG_VRAMRW @@ -378,12 +464,14 @@ auto CPU::writeIO(n1 upper, n1 lower, n24 address, n16 data) -> void { if(upper) lspc.vram[lspc.io.vramAddress].byte(1) = data.byte(1); if(lower) lspc.vram[lspc.io.vramAddress].byte(0) = data.byte(0); lspc.io.vramAddress.bit(0,14) += lspc.io.vramIncrement; + return; } //REG_VRAMMOD if((address & 0xfe000e) == 0x3c0004) { if(upper) lspc.io.vramIncrement.byte(1) = data.byte(1); if(lower) lspc.io.vramIncrement.byte(0) = data.byte(0); + return; } //REG_LSPCMODE @@ -398,12 +486,14 @@ auto CPU::writeIO(n1 upper, n1 lower, n24 address, n16 data) -> void { if(upper) { lspc.animation.speed = data.bit(8,15); } + return; } //REG_TIMERHIGH if((address & 0xfe000e) == 0x3c0008) { if(upper) lspc.timer.reload.byte(3) = data.byte(1); if(lower) lspc.timer.reload.byte(2) = data.byte(0); + return; } //REG_TIMERLOW @@ -413,6 +503,7 @@ auto CPU::writeIO(n1 upper, n1 lower, n24 address, n16 data) -> void { if(lspc.timer.reloadOnChange) { lspc.timer.counter = lspc.timer.reload; } + return; } //REG_IRQACK @@ -422,6 +513,7 @@ auto CPU::writeIO(n1 upper, n1 lower, n24 address, n16 data) -> void { lspc.irq.timerAcknowledge = data.bit(1); lspc.irq.vblankAcknowledge = data.bit(2); } + return; } //REG_TIMERSTOP @@ -429,7 +521,38 @@ auto CPU::writeIO(n1 upper, n1 lower, n24 address, n16 data) -> void { if(lower) { lspc.timer.stopPAL = data.bit(0); } + return; + } + + if(!Model::NeoGeoCD()) return; + + //REG_TRANSAREA + if((address & 0xfffe) == 0x0104) { + system.io.uploadZone = data; + print("upload zone: ", data, " (", system.io.uploadZone, ")\n"); + return; } + //REG_Z80RST + if((address & 0xfffe) == 0x0182) { + // TODO: do we need to pay attention to the value here to enable/disable reset or just trigger it + apu.restart(); + return; + } + + //REG_SPRBANK + if((address & 0xfffe) == 0x01a0) { + system.io.spriteUploadBank = data.bit(0,1); + return; + } + + //REG_PCMBANK + if((address & 0xfffe) == 0x01a2) { + system.io.pcmUploadBank = data.bit(0); + return; + } + + debug(unimplemented, "writeIO: ", hex(address, 6L), " = ", hex(data, 4L)); + return; } diff --git a/ares/ng/lspc/render.cpp b/ares/ng/lspc/render.cpp index aaa0ffa289..abaf8553a8 100644 --- a/ares/ng/lspc/render.cpp +++ b/ares/ng/lspc/render.cpp @@ -56,10 +56,10 @@ auto LSPC::render(n9 y) -> void { n13 pramAddress = io.pramBank << 12 | palette << 4; n27 tileAddress = (tileNumber << 5 | ry & 15) << 2; - n16 d0 = cartridge.readC(tileAddress + 0) << 8 | cartridge.readC(tileAddress + 64 + 0) << 0; - n16 d1 = cartridge.readC(tileAddress + 2) << 8 | cartridge.readC(tileAddress + 64 + 2) << 0; - n16 d2 = cartridge.readC(tileAddress + 1) << 8 | cartridge.readC(tileAddress + 64 + 1) << 0; - n16 d3 = cartridge.readC(tileAddress + 3) << 8 | cartridge.readC(tileAddress + 64 + 3) << 0; + n16 d0 = system.readC(tileAddress + 0) << 8 | system.readC(tileAddress + 64 + 0) << 0; + n16 d1 = system.readC(tileAddress + 2) << 8 | system.readC(tileAddress + 64 + 2) << 0; + n16 d2 = system.readC(tileAddress + 1) << 8 | system.readC(tileAddress + 64 + 1) << 0; + n16 d3 = system.readC(tileAddress + 3) << 8 | system.readC(tileAddress + 64 + 3) << 0; n9 px = 0; n4 bx = hflip; @@ -87,7 +87,7 @@ auto LSPC::render(n9 y) -> void { n4 palette = attributes.bit(12,15); n13 pramAddress = io.pramBank << 12 | palette << 4; n17 tileAddress = tileNumber << 5 | x << 2 & 24 ^ 16 | y & 7; - n8 tileData = cartridge.readS(tileAddress); + n8 tileData = system.readS(tileAddress); n4 color = tileData >> (x & 1) * 4; if(color) { output[x] = io.shadow << 16 | pram[pramAddress | color]; diff --git a/ares/ng/ng.hpp b/ares/ng/ng.hpp index b4402c096a..51d33ab66d 100644 --- a/ares/ng/ng.hpp +++ b/ares/ng/ng.hpp @@ -15,6 +15,7 @@ namespace ares::NeoGeo { struct Model { inline static auto NeoGeoAES() -> bool; inline static auto NeoGeoMVS() -> bool; + inline static auto NeoGeoCD() -> bool; }; #include diff --git a/ares/ng/opnb/opnb.cpp b/ares/ng/opnb/opnb.cpp index 19db0c1a55..de20515b7c 100644 --- a/ares/ng/opnb/opnb.cpp +++ b/ares/ng/opnb/opnb.cpp @@ -74,11 +74,11 @@ auto OPNB::write(n2 address, n8 data) -> void { } auto OPNB::readPCMA(u32 address) -> u8 { - return cartridge.readVA(address); + return system.readVA(address); } auto OPNB::readPCMB(u32 address) -> u8 { - return cartridge.readVB(address); + return system.readVB(address); } } diff --git a/ares/ng/system/debugger.cpp b/ares/ng/system/debugger.cpp index 1d5e97a8fe..a7e1d6c6f3 100644 --- a/ares/ng/system/debugger.cpp +++ b/ares/ng/system/debugger.cpp @@ -11,7 +11,7 @@ auto System::Debugger::load(Node::Object parent) -> void { //memory.srom = parent->append("System Static ROM"); memory.wram = parent->append("System Work RAM"); - memory.wram->setSize(64_KiB); + memory.wram->setSize(NeoGeo::Model::NeoGeoCD() ? 2_MiB : 64_KiB); memory.wram->setRead([&](u32 address) -> u8 { return system.wram[address >> 1].byte(!(address & 1)); }); diff --git a/ares/ng/system/serialization.cpp b/ares/ng/system/serialization.cpp index be7d084e11..96afc3d9dc 100644 --- a/ares/ng/system/serialization.cpp +++ b/ares/ng/system/serialization.cpp @@ -49,10 +49,16 @@ auto System::serialize(serializer& s, bool synchronize) -> void { s(opnb); s(wram); s(sram); + s(spriteRam); + s(pcmRam); + s(fixRam); s(io.sramLock); s(io.slotSelect); s(io.ledMarquee); s(io.ledLatch1); s(io.ledLatch2); s(io.ledData); + s(io.uploadZone); + s(io.spriteUploadBank); + s(io.pcmUploadBank); } diff --git a/ares/ng/system/system.cpp b/ares/ng/system/system.cpp index 1fac6e036a..228647da7c 100644 --- a/ares/ng/system/system.cpp +++ b/ares/ng/system/system.cpp @@ -6,6 +6,7 @@ auto enumerate() -> vector { return { "[SNK] Neo Geo AES", "[SNK] Neo Geo MVS", + "[SNK] Neo Geo CD", }; } @@ -24,6 +25,8 @@ auto System::game() -> string { return cartridge.title(); } + // TODO: disc.node + return "(no cartridge connected)"; } @@ -43,6 +46,10 @@ auto System::load(Node::System& root, string name) -> bool { information.name = "Neo Geo MVS"; information.model = Model::NeoGeoMVS; } + if(name.find("Neo Geo CD")) { + information.name = "Neo Geo CD"; + information.model = Model::NeoGeoCD; + } node = Node::System::create(information.name); node->setGame({&System::game, this}); @@ -55,7 +62,15 @@ auto System::load(Node::System& root, string name) -> bool { root = node; if(!node->setPak(pak = platform->pak(node))) return false; - wram.allocate(64_KiB >> 1); + if(NeoGeo::Model::NeoGeoCD()) { + wram.allocate(2_MiB >> 1); + spriteRam.allocate(4_MiB >> 1); + pcmRam.allocate(1_MiB); + fixRam.allocate(128_KiB); + } else { + wram.allocate(64_KiB >> 1); + } + if(NeoGeo::Model::NeoGeoMVS()) { sram.allocate(64_KiB >> 1); } @@ -65,10 +80,10 @@ auto System::load(Node::System& root, string name) -> bool { apu.load(node); lspc.load(node); opnb.load(node); - cartridgeSlot.load(node); + if(!NeoGeo::Model::NeoGeoCD()) cartridgeSlot.load(node); controllerPort1.load(node); controllerPort2.load(node); - cardSlot.load(node); + if(!NeoGeo::Model::NeoGeoCD()) cardSlot.load(node); debugger.load(node); return true; } @@ -81,19 +96,22 @@ auto System::unload() -> void { apu.unload(); lspc.unload(); opnb.unload(); - cartridgeSlot.unload(); + if(!NeoGeo::Model::NeoGeoCD()) cartridgeSlot.unload(); controllerPort1.unload(); controllerPort2.unload(); - cardSlot.unload(); + if(!NeoGeo::Model::NeoGeoCD()) cardSlot.unload(); wram.reset(); sram.reset(); + spriteRam.reset(); + pcmRam.reset(); + fixRam.reset(); pak.reset(); node.reset(); } auto System::save() -> void { if(!node) return; - cartridge.save(); + if(!NeoGeo::Model::NeoGeoCD()) cartridge.save(); cardSlot.save(); } @@ -116,8 +134,8 @@ auto System::power(bool reset) -> void { } } - if(cartridge.node) cartridge.power(); - cardSlot.power(reset); + if(!NeoGeo::Model::NeoGeoCD()) if(cartridge.node) cartridge.power(); + if(!NeoGeo::Model::NeoGeoCD()) cardSlot.power(reset); cpu.power(reset); apu.power(reset); lspc.power(reset); @@ -127,4 +145,24 @@ auto System::power(bool reset) -> void { io = {}; } +auto System::readC(n32 address) -> n8 { + if(NeoGeo::Model::NeoGeoCD()) return spriteRam.read(address >> 1).byte(address & 1); + return cartridge.readC(address); +} + +auto System::readS(n32 address) -> n8 { + if(NeoGeo::Model::NeoGeoCD()) return fixRam.read(address); + return cartridge.readS(address); +} + +auto System::readVA(n32 address) -> n8 { + if(NeoGeo::Model::NeoGeoCD()) return pcmRam.read(address); + return cartridge.readVA(address); +} + +auto System::readVB(n32 address) -> n8 { + if(NeoGeo::Model::NeoGeoCD()) return 0xff; + return cartridge.readVB(address); +} + }; diff --git a/ares/ng/system/system.hpp b/ares/ng/system/system.hpp index a8805dc70c..73c726992d 100644 --- a/ares/ng/system/system.hpp +++ b/ares/ng/system/system.hpp @@ -6,6 +6,11 @@ struct System { Memory::Writable wram; Memory::Writable sram; //MVS only + //CD Only + Memory::Writable spriteRam; // 4MiB + Memory::Writable pcmRam; // 1MiB + Memory::Writable fixRam; // 128KiB + struct Debugger { //debugger.cpp auto load(Node::Object) -> void; @@ -19,7 +24,7 @@ struct System { } memory; } debugger; - enum class Model : u32 { NeoGeoAES, NeoGeoMVS }; + enum class Model : u32 { NeoGeoAES, NeoGeoMVS, NeoGeoCD }; auto name() const -> string { return information.name; } auto model() const -> Model { return information.model; } @@ -33,6 +38,11 @@ struct System { auto save() -> void; auto power(bool reset = false) -> void; + auto readC(n32 address) -> n8; + auto readS(n32 address) -> n8; + auto readVA(n32 address) -> n8; + auto readVB(n32 address) -> n8; + //serialization.cpp auto serialize(bool synchronize) -> serializer; auto unserialize(serializer&) -> bool; @@ -44,6 +54,9 @@ struct System { n1 ledLatch1; n1 ledLatch2; n8 ledData; + n3 uploadZone; + n2 spriteUploadBank; + n1 pcmUploadBank; } io; private: @@ -60,3 +73,4 @@ extern System system; auto Model::NeoGeoAES() -> bool { return system.model() == System::Model::NeoGeoAES; } auto Model::NeoGeoMVS() -> bool { return system.model() == System::Model::NeoGeoMVS; } +auto Model::NeoGeoCD() -> bool { return system.model() == System::Model::NeoGeoCD; } diff --git a/desktop-ui/cmake/sources.cmake b/desktop-ui/cmake/sources.cmake index 2a4253bcdc..1c1c5ca93f 100644 --- a/desktop-ui/cmake/sources.cmake +++ b/desktop-ui/cmake/sources.cmake @@ -79,6 +79,7 @@ target_sources( emulator/myvision.cpp emulator/neo-geo-aes.cpp emulator/neo-geo-mvs.cpp + emulator/neo-geo-cd.cpp emulator/neo-geo-pocket-color.cpp emulator/neo-geo-pocket.cpp emulator/nintendo-64.cpp diff --git a/desktop-ui/emulator/emulators.cpp b/desktop-ui/emulator/emulators.cpp index b96ec7b157..6b2922cb48 100644 --- a/desktop-ui/emulator/emulators.cpp +++ b/desktop-ui/emulator/emulators.cpp @@ -86,6 +86,7 @@ namespace ares::Atari2600 { } #include "neo-geo-aes.cpp" #include "neo-geo-mvs.cpp" + #include "neo-geo-cd.cpp" #endif #ifdef CORE_NGP @@ -237,6 +238,7 @@ auto Emulator::construct() -> void { #ifdef CORE_NG emulators.append(new NeoGeoAES); emulators.append(new NeoGeoMVS); + emulators.append(new NeoGeoCD); #endif #ifdef CORE_NGP diff --git a/desktop-ui/emulator/neo-geo-cd.cpp b/desktop-ui/emulator/neo-geo-cd.cpp new file mode 100644 index 0000000000..d117fdb98e --- /dev/null +++ b/desktop-ui/emulator/neo-geo-cd.cpp @@ -0,0 +1,83 @@ +struct NeoGeoCD : Emulator { + NeoGeoCD(); + auto load() -> LoadResult override; + auto save() -> bool override; + auto pak(ares::Node::Object) -> shared_pointer override; + auto arcade() -> bool override { return !name.find("Neo Geo CD"); } +}; + +NeoGeoCD::NeoGeoCD() { + manufacturer = "SNK"; + name = "Neo Geo CD"; + medium = "Neo Geo CD"; + + firmware.append({"BIOS", "World", "c11c33589b2057008a7e4c7c700fcd989a8c0f95f869e7e7539fdd00414d99a7"}); + + for(auto id : range(2)) { + InputPort port{string{"Controller Port ", 1 + id}}; + + // Button layout mirrors Neo Geo CD pad + { InputDevice device{"Arcade Stick"}; + 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.south); + device.digital("B", virtualPorts[id].pad.east); + device.digital("C", virtualPorts[id].pad.west); + device.digital("D", virtualPorts[id].pad.north); + device.digital("Select", virtualPorts[id].pad.select); + device.digital("Start", virtualPorts[id].pad.start); + port.append(device); } + + ports.append(port); + } +} + +auto NeoGeoCD::load() -> LoadResult { + game = mia::Medium::create("Neo Geo CD"); + auto result = game->load(Emulator::load(game, configuration.game)); + if(result != successful) return result; + + system = mia::System::create("Neo Geo CD"); + result = system->load(firmware[0].location); + if(result != successful) { + result.firmwareSystemName = "Neo Geo CD"; + result.firmwareType = firmware[0].type; + result.firmwareRegion = firmware[0].region; + result.result = noFirmware; + return result; + } + + if(!ares::NeoGeo::load(root, "[SNK] Neo Geo CD")) return otherError; + + if(auto port = root->find("Disc Tray")) { + port->allocate(); + port->connect(); + } + + if(auto port = root->find("Controller Port 1")) { + port->allocate("Arcade Stick"); + port->connect(); + } + + if(auto port = root->find("Controller Port 2")) { + port->allocate("Arcade Stick"); + port->connect(); + } + + return successful; +} + +auto NeoGeoCD::save() -> bool { + root->save(); + system->save(system->location); + game->save(game->location); + return true; +} + +auto NeoGeoCD::pak(ares::Node::Object node) -> shared_pointer { + if(node->name() == "Neo Geo CD") return system->pak; + if(node->name() == "Neo Geo CD Disc") return game->pak; + return {}; +} diff --git a/mia/CMakeLists.txt b/mia/CMakeLists.txt index 3786053a58..740c8c4862 100644 --- a/mia/CMakeLists.txt +++ b/mia/CMakeLists.txt @@ -31,6 +31,7 @@ target_sources( medium/neo-geo-pocket-color.cpp medium/neo-geo-pocket.cpp medium/neo-geo.cpp + medium/neo-geo-cd.cpp medium/nintendo-64.cpp medium/nintendo-64dd.cpp medium/pc-engine-cd.cpp @@ -88,6 +89,7 @@ target_sources( system/myvision.cpp system/neo-geo-aes.cpp system/neo-geo-mvs.cpp + system/neo-geo-cd.cpp system/neo-geo-pocket-color.cpp system/neo-geo-pocket.cpp system/nintendo-64.cpp diff --git a/mia/medium/medium.cpp b/mia/medium/medium.cpp index e8812afa64..f0fdfa325e 100644 --- a/mia/medium/medium.cpp +++ b/mia/medium/medium.cpp @@ -18,6 +18,7 @@ namespace Media { #include "msx.cpp" #include "msx2.cpp" #include "neo-geo.cpp" + #include "neo-geo-cd.cpp" #include "neo-geo-pocket.cpp" #include "neo-geo-pocket-color.cpp" #include "nintendo-64.cpp" @@ -61,6 +62,7 @@ auto Medium::create(string name) -> shared_pointer { if(name == "MSX") return new Media::MSX; if(name == "MSX2") return new Media::MSX2; if(name == "Neo Geo") return new Media::NeoGeo; + if(name == "Neo Geo CD") return new Media::NeoGeoCD; if(name == "Neo Geo Pocket") return new Media::NeoGeoPocket; if(name == "Neo Geo Pocket Color") return new Media::NeoGeoPocketColor; if(name == "Nintendo 64") return new Media::Nintendo64; diff --git a/mia/medium/neo-geo-cd.cpp b/mia/medium/neo-geo-cd.cpp new file mode 100644 index 0000000000..ba3838c044 --- /dev/null +++ b/mia/medium/neo-geo-cd.cpp @@ -0,0 +1,44 @@ +struct NeoGeoCD : CompactDisc { + auto name() -> string override { return "Neo Geo CD"; } + auto extensions() -> vector override { return {"cue", "chd"}; } + auto load(string location) -> LoadResult override; + auto save(string location) -> bool override; + auto analyze(string location) -> string; +}; + +auto NeoGeoCD::load(string location) -> LoadResult { + if(!inode::exists(location)) return romNotFound; + + this->location = location; + this->manifest = analyze(location); + auto document = BML::unserialize(manifest); + if(!document) return couldNotParseManifest; + + pak = new vfs::directory; + pak->setAttribute("title", document["game/title"].string()); + pak->setAttribute("serial", document["game/serial"].string()); + pak->setAttribute("region", document["game/region"].string()); + pak->append("manifest.bml", manifest); + if(directory::exists(location)) { + pak->append("cd.rom", vfs::disk::open({location, "cd.rom"}, vfs::read)); + } + if(file::exists(location)) { + pak->append("cd.rom", vfs::cdrom::open(location)); + } + + return successful; +} + +auto NeoGeoCD::save(string location) -> bool { + auto document = BML::unserialize(manifest); + + return true; +} + +auto NeoGeoCD::analyze(string location) -> string { + string s; + s += "game\n"; + s +={" name: ", Medium::name(location), "\n"}; + s +={" title: ", Medium::name(location), "\n"}; + return s; +} diff --git a/mia/system/neo-geo-cd.cpp b/mia/system/neo-geo-cd.cpp new file mode 100644 index 0000000000..1bb7301bb3 --- /dev/null +++ b/mia/system/neo-geo-cd.cpp @@ -0,0 +1,38 @@ +struct NeoGeoCD : System { + auto name() -> string override { return "Neo Geo CD"; } + auto load(string location) -> LoadResult override; + auto save(string location) -> bool override; + auto endianSwap(vector&) -> void; +}; + +auto NeoGeoCD::load(string location) -> LoadResult { + this->location = locate(); + pak = new vfs::directory; + + auto bios = Pak::read(location); + if(!bios) return romNotFound; + + endianSwap(bios); + pak->append("bios.rom", bios); + pak->append("backup.ram", 8_KiB); + + if(auto fp = pak->write("backup.ram")) { + for(auto address : range(fp->size())) fp->write(0xff); + } + + Pak::load("backup.ram", ".bram"); + + return successful; +} + +auto NeoGeoCD::save(string location) -> bool { + Pak::save("backup.ram", ".bram"); + + return true; +} + +auto NeoGeoCD::endianSwap(vector& memory) -> void { + for(u32 offset = 0; offset < memory.size(); offset += 2) { + swap(memory[offset + 0], memory[offset + 1]); + } +} diff --git a/mia/system/system.cpp b/mia/system/system.cpp index 345af3e5d6..6927116374 100644 --- a/mia/system/system.cpp +++ b/mia/system/system.cpp @@ -17,6 +17,7 @@ namespace Systems { #include "msx2.cpp" #include "neo-geo-aes.cpp" #include "neo-geo-mvs.cpp" + #include "neo-geo-cd.cpp" #include "neo-geo-pocket.cpp" #include "neo-geo-pocket-color.cpp" #include "nintendo-64.cpp" @@ -54,6 +55,7 @@ auto System::create(string name) -> shared_pointer { if(name == "MSX2") return new Systems::MSX2; if(name == "Neo Geo AES") return new Systems::NeoGeoAES; if(name == "Neo Geo MVS") return new Systems::NeoGeoMVS; + if(name == "Neo Geo CD") return new Systems::NeoGeoCD; if(name == "Neo Geo Pocket") return new Systems::NeoGeoPocket; if(name == "Neo Geo Pocket Color") return new Systems::NeoGeoPocketColor; if(name == "Nintendo 64") return new Systems::Nintendo64;