diff --git a/ares/n64/CMakeLists.txt b/ares/n64/CMakeLists.txt index 97ea264c87..5f5957ad4d 100644 --- a/ares/n64/CMakeLists.txt +++ b/ares/n64/CMakeLists.txt @@ -23,6 +23,7 @@ ares_add_sources( cpu/cpu.cpp rsp/rsp.cpp rdp/rdp.cpp + aleck64/aleck64.cpp ) target_enable_feature(ares "N64 Vulkan rendering with paraLLEl-RDP" VULKAN) @@ -54,6 +55,29 @@ ares_add_sources( ai/serialization.cpp ) +ares_add_sources( + CORE # + n64 + INCLUDED # + aleck64/aleck64.hpp + aleck64/controls.cpp + aleck64/debugger.cpp + aleck64/serialization.cpp + aleck64/io.cpp + aleck64/vdp.cpp + aleck64/game-config/11beat.hpp + aleck64/game-config/doncdoon.hpp + aleck64/game-config/kurufev.hpp + aleck64/game-config/mayjin3.hpp + aleck64/game-config/starsldr.hpp + aleck64/game-config/twrshaft.hpp + aleck64/game-config/vivdolls.hpp + aleck64/game-config/hipai.hpp + aleck64/game-config/hipai2.hpp + aleck64/game-config/srmvs.hpp + aleck64/game-config/mtetrisc.hpp +) + ares_add_sources( CORE # n64 @@ -88,6 +112,14 @@ ares_add_sources( controller/port.hpp ) +ares_add_sources( + CORE # + n64 + INCLUDED # + controller/aleck64/aleck64.cpp + controller/aleck64/aleck64.hpp +) + ares_add_sources( CORE # n64 diff --git a/ares/n64/GNUmakefile b/ares/n64/GNUmakefile index db23f04696..f57af7aab3 100644 --- a/ares/n64/GNUmakefile +++ b/ares/n64/GNUmakefile @@ -17,6 +17,7 @@ ares.objects += ares-n64-rdram ares.objects += ares-n64-cpu ares.objects += ares-n64-rsp ares.objects += ares-n64-rdp +ares.objects += ares-n64-aleck64 $(object.path)/ares-n64-memory.o: $(ares.path)/n64/memory/memory.cpp $(object.path)/ares-n64-system.o: $(ares.path)/n64/system/system.cpp @@ -37,6 +38,7 @@ $(object.path)/ares-n64-rdram.o: $(ares.path)/n64/rdram/rdram.cpp $(object.path)/ares-n64-cpu.o: $(ares.path)/n64/cpu/cpu.cpp $(object.path)/ares-n64-rsp.o: $(ares.path)/n64/rsp/rsp.cpp $(object.path)/ares-n64-rdp.o: $(ares.path)/n64/rdp/rdp.cpp +$(object.path)/ares-n64-aleck64.o: $(ares.path)/n64/aleck64/aleck64.cpp ifeq ($(vulkan),true) ifeq ($(platform),macos) diff --git a/ares/n64/aleck64/aleck64.cpp b/ares/n64/aleck64/aleck64.cpp new file mode 100644 index 0000000000..ea7c3d3ff8 --- /dev/null +++ b/ares/n64/aleck64/aleck64.cpp @@ -0,0 +1,73 @@ +#include + +namespace ares::Nintendo64 { + Aleck64 aleck64; + + #include "io.cpp" + #include "controls.cpp" + #include "debugger.cpp" + #include "vdp.cpp" + #include "serialization.cpp" + + #include "game-config/11beat.hpp" + #include "game-config/starsldr.hpp" + #include "game-config/doncdoon.hpp" + #include "game-config/kurufev.hpp" + #include "game-config/mayjin3.hpp" + #include "game-config/vivdolls.hpp" + #include "game-config/twrshaft.hpp" + #include "game-config/hipai.hpp" + #include "game-config/hipai2.hpp" + #include "game-config/srmvs.hpp" + #include "game-config/mtetrisc.hpp" + + auto Aleck64::load(Node::Object parent) -> void { + sdram.allocate(4_MiB); + vram.allocate(4_KiB); + pram.allocate(4_KiB); + controls.load(parent); + gameConfig.reset(); + dipSwitchNode = parent->append("DIP Switches"); + + debugger.load(parent); + } + + auto Aleck64::unload() -> void { + sdram.reset(); + debugger.unload(); + } + + auto Aleck64::save() -> void { + + } + + auto Aleck64::power(bool reset) -> void { + if(!reset) { + dipSwitch[0] = 0xffff'ffff; + dipSwitch[1] = 0xffff'ffff; + + //NOTE: We can't do this at 'load' time because cartridges are not attached yet... + auto name = cartridge.pak->attribute("name"); + + if(name == "11beat" ) gameConfig = new _11beat(); + if(name == "starsldr") gameConfig = new starsldr(); + if(name == "doncdoon") gameConfig = new doncdoon(); + if(name == "kurufev" ) gameConfig = new kurufev(); + if(name == "mayjin3" ) gameConfig = new mayjin3(); + if(name == "vivdolls") gameConfig = new vivdolls(); + if(name == "twrshaft") gameConfig = new twrshaft(); + if(name == "hipai" ) gameConfig = new hipai(); + if(name == "hipai2" ) gameConfig = new hipai2(); + if(name == "srmvs" ) gameConfig = new srmvs(); + if(name == "srmvsa" ) gameConfig = new srmvs(); + if(name == "mtetrisc") gameConfig = new mtetrisc(); + if(!gameConfig) gameConfig = new GameConfig(); //Fallback to default implementation + + gameConfig->dipSwitches(dipSwitchNode); + + sdram.fill(); + vram.fill(); + pram.fill(); + } + } +} diff --git a/ares/n64/aleck64/aleck64.hpp b/ares/n64/aleck64/aleck64.hpp new file mode 100644 index 0000000000..cf2e5e35e7 --- /dev/null +++ b/ares/n64/aleck64/aleck64.hpp @@ -0,0 +1,241 @@ +struct Aleck64 { + Node::Object dipSwitchNode; + enum BoardType { E90, E92 }; + + struct Writable : public Memory::Writable { + Aleck64& self; + + Writable(Aleck64& self) : self(self) {} + + template + auto writeBurst(u32 address, u32 *value, const char *peripheral) -> void { + address = address & 0x00ff'ffff; + if(address >= size) return; + Memory::Writable::write(address | 0x00, value[0]); + Memory::Writable::write(address | 0x04, value[1]); + Memory::Writable::write(address | 0x08, value[2]); + Memory::Writable::write(address | 0x0c, value[3]); + if(Size == ICache) { + Memory::Writable::write(address | 0x10, value[4]); + Memory::Writable::write(address | 0x14, value[5]); + Memory::Writable::write(address | 0x18, value[6]); + Memory::Writable::write(address | 0x1c, value[7]); + } + } + + template + auto readBurst(u32 address, u32 *value, const char *peripheral) -> void { + address = address & 0x00ff'ffff; + if(address >= size) { + value[0] = value[1] = value[2] = value[3] = 0; + if(Size == ICache) + value[4] = value[5] = value[6] = value[7] = 0; + return; + } + value[0] = Memory::Writable::read(address | 0x00); + value[1] = Memory::Writable::read(address | 0x04); + value[2] = Memory::Writable::read(address | 0x08); + value[3] = Memory::Writable::read(address | 0x0c); + if(Size == ICache) { + value[4] = Memory::Writable::read(address | 0x10); + value[5] = Memory::Writable::read(address | 0x14); + value[6] = Memory::Writable::read(address | 0x18); + value[7] = Memory::Writable::read(address | 0x1c); + } + } + + } sdram{*this}; + + Memory::Writable vram; + Memory::Writable pram; + + struct Debugger { + Node::Object parent; + //debugger.cpp + auto load(Node::Object) -> void; + auto unload() -> void; + + struct Memory { + Node::Debugger::Memory sdram; + Node::Debugger::Memory vram; + Node::Debugger::Memory pram; + } memory; + } debugger; + + auto load(Node::Object) -> void; + auto unload() -> void; + auto power(bool reset) -> void; + auto save() -> void; + + template + auto formatRead(u32 address, u32 data) -> u32 { + if constexpr(Size == Byte) {; + switch(address & 3) { + case 0: return data >> 24; + case 1: return data >> 16; + case 2: return data >> 8; + case 3: return data >> 0; + } + } + if constexpr(Size == Half) { + switch(address & 2) { + case 0: return data >> 16; + case 2: return data >> 0; + } + } + if constexpr(Size == Word) { + return data; + } + + unreachable; + } + + template auto read(u32 address, Thread& thread) -> u32 { + if(address <= 0xc07f'ffff) { + return sdram.read(address & 0x00ff'ffff); + } + + controls.poll(); + if(address <= 0xc080'0fff) { + n32 value = 0xffff'ffff; + + switch (address & 0xffff'fffc) { + case 0xc080'0000: return formatRead(address, readPort1()); + case 0xc080'0004: return formatRead(address, readPort2()); + case 0xc080'0008: return formatRead(address, readPort3()); + case 0xc080'0100: return formatRead(address, readPort4()); + } + } + + if(gameConfig->type() == BoardType::E90) { + if(address >= 0xd000'0000 && address <= 0xd000'0fff) return vram.read(address & 0x0000'0fff); + if(address >= 0xd001'0000 && address <= 0xd001'0fff) return pram.read(address & 0x0000'0fff); + if(address >= 0xd003'0000 && address <= 0xd003'001f) return formatRead(address, vdp.readWord(address & 0x0000'001f)); + } else if(gameConfig->type() == BoardType::E92) { + if(address >= 0xd080'0000 && address <= 0xd080'0fff) return vram.read(address & 0x0000'0fff); + if(address >= 0xd080'1000 && address <= 0xd080'1fff) return pram.read(address & 0x0000'0fff); + if(address >= 0xd080'2000 && address <= 0xd080'201f) return formatRead(address, vdp.readWord(address & 0x0000'001f)); + } + + debug(unusual, "[Aleck64::read] Unmapped address: 0x", hex(address, 8L)); + return 0xffffffff; + } + + template auto write(u32 address, u32 data, Thread& thread) -> void { + if(address <= 0xc07f'ffff) { + return sdram.write(address & 0x00ff'ffff, data); + } + + if(address <= 0xc080'0fff) { + switch (address & 0xffff'fffc) { + case 0xc080'0008: return writePort3(data); + case 0xc080'0100: return writePort4(data); + } + } + + if(gameConfig->type() == BoardType::E90) { + if(address >= 0xd000'0000 && address <= 0xd000'0fff) return vram.write(address & 0x0000'0fff, data); + if(address >= 0xd001'0000 && address <= 0xd001'0fff) return pram.write(address & 0x0000'0fff, data); + if(address >= 0xd003'0000 && address <= 0xd003'001f) return vdp.writeWord (address & 0x0000'001f, data); + } else if(gameConfig->type() == BoardType::E92) { + if(address >= 0xd080'0000 && address <= 0xd080'0fff) return vram.write(address & 0x0000'0fff, data); + if(address >= 0xd080'1000 && address <= 0xd080'1fff) return pram.write(address & 0x0000'0fff, data); + if(address >= 0xd080'2000 && address <= 0xd080'201f) return vdp.writeWord (address & 0x0000'001f, data); + } + + debug(unusual, "[Aleck64::write] ", hex(address, 8L), " = ", hex(data, 8L)); + } + + //io.cpp + auto readPort1() -> u32; + auto readPort2() -> u32; + auto readPort3() -> u32; + auto readPort4() -> u32; + auto writePort3(n32 data) -> void; + auto writePort4(n32 data) -> void; + + //serialization.cpp + auto serialize(serializer&) -> void; + + struct Controls { + Aleck64& self; + Node::Object node; + + Node::Input::Button service; + Node::Input::Button test; + + Node::Input::Axis p1x; + Node::Input::Axis p1y; + Node::Input::Button p1up; + Node::Input::Button p1down; + Node::Input::Button p1left; + Node::Input::Button p1right; + Node::Input::Button p1[9]; + Node::Input::Button p1start; + Node::Input::Button p1coin; + + Node::Input::Axis p2x; + Node::Input::Axis p2y; + Node::Input::Button p2up; + Node::Input::Button p2down; + Node::Input::Button p2left; + Node::Input::Button p2right; + Node::Input::Button p2[9]; + Node::Input::Button p2start; + Node::Input::Button p2coin; + + Node::Input::Button mahjongA; + Node::Input::Button mahjongB; + Node::Input::Button mahjongC; + Node::Input::Button mahjongD; + Node::Input::Button mahjongE; + Node::Input::Button mahjongF; + Node::Input::Button mahjongG; + Node::Input::Button mahjongH; + Node::Input::Button mahjongI; + Node::Input::Button mahjongJ; + Node::Input::Button mahjongK; + Node::Input::Button mahjongL; + Node::Input::Button mahjongM; + Node::Input::Button mahjongN; + Node::Input::Button mahjongKan; + Node::Input::Button mahjongPon; + Node::Input::Button mahjongChi; + Node::Input::Button mahjongReach; + Node::Input::Button mahjongRon; + + //controls.cpp + auto load(Node::Object) -> void; + auto poll() -> void; + + auto controllerButton(int playerIndex, string button) -> bool; + auto controllerAxis(int playerIndex, string axis) -> s64; + auto mahjong(n8 row) -> n8; + + } controls{*this}; + + struct VDP { + auto readWord(u32 address) -> u32; + auto writeWord(u32 address, n32 data) -> void; + auto render(Node::Video::Screen screen) -> void; + + struct IO { + n1 enable; + } io; + } vdp; + + n8 dipSwitch[2]; + + struct GameConfig { + virtual ~GameConfig() = default; + virtual auto type() -> BoardType { return BoardType::E92; } + virtual auto dpadDisabled() -> n1 { return 0; } + virtual auto dipSwitches(Node::Object parent) -> void {}; + virtual auto readExpansionPort() -> u32 { return 0xffff'ffff; } + virtual auto writeExpansionPort(n32 data) -> void {}; + }; + + shared_pointer gameConfig; +}; + +extern Aleck64 aleck64; diff --git a/ares/n64/aleck64/controls.cpp b/ares/n64/aleck64/controls.cpp new file mode 100644 index 0000000000..c5f05b448b --- /dev/null +++ b/ares/n64/aleck64/controls.cpp @@ -0,0 +1,185 @@ +auto Aleck64::Controls::load(Node::Object parent) -> void { + node = parent->append("Controls"); + + service = node->append("Service"); + test = node->append("Test"); + + p1x = node->append("Player 1 X-Axis"); + p1y = node->append("Player 1 Y-Axis"); + p1up = node->append("Player 1 Up"); + p1down = node->append("Player 1 Down"); + p1left = node->append("Player 1 Left"); + p1right = node->append("Player 1 Right"); + p1start = node->append("Player 1 Start"); + p1coin = node->append("Player 1 Coin"); + + for(auto n: range(9)) { + string name = {"Player 1 Button ", 1 + n}; + p1[n] = node->append(name); + } + + p2x = node->append("Player 2 X-Axis"); + p2y = node->append("Player 2 Y-Axis"); + p2up = node->append("Player 2 Up"); + p2down = node->append("Player 2 Down"); + p2left = node->append("Player 2 Left"); + p2right = node->append("Player 2 Right"); + p2start = node->append("Player 2 Start"); + p2coin = node->append("Player 2 Coin"); + + for(auto n: range(9)) { + string name = {"Player 2 Button ", 1 + n}; + p2[n] = node->append(name); + } + + mahjongA = node->append("Mahjong A"); + mahjongB = node->append("Mahjong B"); + mahjongC = node->append("Mahjong C"); + mahjongD = node->append("Mahjong D"); + mahjongE = node->append("Mahjong E"); + mahjongF = node->append("Mahjong F"); + mahjongG = node->append("Mahjong G"); + mahjongH = node->append("Mahjong H"); + mahjongI = node->append("Mahjong I"); + mahjongJ = node->append("Mahjong J"); + mahjongK = node->append("Mahjong K"); + mahjongL = node->append("Mahjong L"); + mahjongM = node->append("Mahjong M"); + mahjongN = node->append("Mahjong N"); + mahjongKan = node->append("Mahjong カン"); + mahjongPon = node->append("Mahjong ポン"); + mahjongChi = node->append("Mahjong チー"); + mahjongReach = node->append("Mahjong リーチ"); + mahjongRon = node->append("Mahjong ロン"); +} + +auto Aleck64::Controls::poll() -> void { + platform->input(service); + platform->input(test); + + platform->input(p1x); + platform->input(p1y); + platform->input(p1up); + platform->input(p1down); + platform->input(p1left); + platform->input(p1right); + platform->input(p1start); + platform->input(p1coin); + + for(auto n : range(9)) { + platform->input(p1[n]); + } + + platform->input(p2x); + platform->input(p2y); + platform->input(p2up); + platform->input(p2down); + platform->input(p2left); + platform->input(p2right); + platform->input(p2start); + platform->input(p2coin); + + for(auto n : range(9)) { + platform->input(p2[n]); + } + + platform->input(mahjongA); + platform->input(mahjongB); + platform->input(mahjongC); + platform->input(mahjongD); + platform->input(mahjongE); + platform->input(mahjongF); + platform->input(mahjongG); + platform->input(mahjongH); + platform->input(mahjongI); + platform->input(mahjongJ); + platform->input(mahjongK); + platform->input(mahjongL); + platform->input(mahjongM); + platform->input(mahjongN); + platform->input(mahjongKan); + platform->input(mahjongPon); + platform->input(mahjongChi); + platform->input(mahjongReach); + platform->input(mahjongRon); +} + +auto Aleck64::Controls::controllerButton(int playerIndex, string button) -> bool { + if(playerIndex == 1) { + if(button == "Up" ) return aleck64.gameConfig->dpadDisabled() ? 1 : aleck64.controls.p1up->value(); + if(button == "Down" ) return aleck64.gameConfig->dpadDisabled() ? 1 : aleck64.controls.p1down->value(); + if(button == "Left" ) return aleck64.gameConfig->dpadDisabled() ? 1 : aleck64.controls.p1left->value(); + if(button == "Right" ) return aleck64.gameConfig->dpadDisabled() ? 1 : aleck64.controls.p1right->value(); + if(button == "Start" ) return aleck64.controls.p1start->value(); + if(button == "A" ) return aleck64.controls.p1[0]->value(); + if(button == "B" ) return aleck64.controls.p1[1]->value(); + if(button == "R" ) return aleck64.controls.p1[2]->value(); + if(button == "C-Right") return aleck64.controls.p1[3]->value(); + } + + if(playerIndex == 2) { + if(button == "Up" ) return aleck64.gameConfig->dpadDisabled() ? 1 : aleck64.controls.p2up->value(); + if(button == "Down" ) return aleck64.gameConfig->dpadDisabled() ? 1 : aleck64.controls.p2down->value(); + if(button == "Left" ) return aleck64.gameConfig->dpadDisabled() ? 1 : aleck64.controls.p2left->value(); + if(button == "Right" ) return aleck64.gameConfig->dpadDisabled() ? 1 : aleck64.controls.p2right->value(); + if(button == "Start" ) return aleck64.controls.p2start->value(); + if(button == "A" ) return aleck64.controls.p2[0]->value(); + if(button == "B" ) return aleck64.controls.p2[1]->value(); + if(button == "R" ) return aleck64.controls.p2[2]->value(); + if(button == "C-Right") return aleck64.controls.p2[3]->value(); + } + + return 0; +} + +auto Aleck64::Controls::controllerAxis(int playerIndex, string axis) -> s64 { + if(playerIndex == 1) { + if(axis == "X") return aleck64.controls.p1x->value(); + if(axis == "Y") return aleck64.controls.p1y->value(); + } + + if(playerIndex == 2) { + if(axis == "X") return aleck64.controls.p2x->value(); + if(axis == "Y") return aleck64.controls.p2y->value(); + } + + return 0; +} + +auto Aleck64::Controls::mahjong(n8 row) -> n8 { + n8 value = 0xff; + + if(row.bit(0)) { + value.bit(1) &= !aleck64.controls.mahjongB->value(); + value.bit(2) &= !aleck64.controls.mahjongF->value(); + value.bit(3) &= !aleck64.controls.mahjongJ->value(); + value.bit(4) &= !aleck64.controls.mahjongN->value(); + value.bit(5) &= !aleck64.controls.mahjongReach->value(); + } + + if(row.bit(1)) { + value.bit(0) &= !aleck64.controls.p1start->value(); + value.bit(1) &= !aleck64.controls.mahjongA->value(); + value.bit(2) &= !aleck64.controls.mahjongE->value(); + value.bit(3) &= !aleck64.controls.mahjongI->value(); + value.bit(4) &= !aleck64.controls.mahjongM->value(); + value.bit(5) &= !aleck64.controls.mahjongKan->value(); + } + + if(row.bit(2)) { + value.bit(1) &= !aleck64.controls.mahjongC->value(); + value.bit(2) &= !aleck64.controls.mahjongG->value(); + value.bit(3) &= !aleck64.controls.mahjongK->value(); + value.bit(4) &= !aleck64.controls.mahjongChi->value(); + value.bit(5) &= !aleck64.controls.mahjongRon->value(); + } + + if(row.bit(3)) { + value.bit(1) &= !aleck64.controls.mahjongD->value(); + value.bit(2) &= !aleck64.controls.mahjongH->value(); + value.bit(3) &= !aleck64.controls.mahjongL->value(); + value.bit(4) &= !aleck64.controls.mahjongPon->value(); + } + + return value; +} \ No newline at end of file diff --git a/ares/n64/aleck64/debugger.cpp b/ares/n64/aleck64/debugger.cpp new file mode 100644 index 0000000000..17e08df3ef --- /dev/null +++ b/ares/n64/aleck64/debugger.cpp @@ -0,0 +1,35 @@ +auto Aleck64::Debugger::load(Node::Object parent) -> void { + + memory.sdram = parent->append("Aleck64 SDRAM"); + memory.sdram->setSize(4_MiB ); + memory.sdram->setRead([&](u32 address) -> u8 { + return aleck64.sdram.read(address); + }); + memory.sdram->setWrite([&](u32 address, u8 data) -> void { + return aleck64.sdram.write(address, data); + }); + + memory.vram = parent->append("Aleck64 VRAM"); + memory.vram->setSize(4_KiB); + memory.vram->setRead([&](u32 address) -> u8 { + return aleck64.vram.read(address); + }); + memory.vram->setWrite([&](u32 address, u8 data) -> void { + return aleck64.vram.write(address, data); + }); + + memory.pram = parent->append("Aleck64 PRAM"); + memory.pram->setSize(4_KiB); + memory.pram->setRead([&](u32 address) -> u8 { + return aleck64.pram.read(address); + }); + memory.pram->setWrite([&](u32 address, u8 data) -> void { + return aleck64.pram.write(address, data); + }); +} + +auto Aleck64::Debugger::unload() -> void { + memory.sdram.reset(); + memory.vram.reset(); + memory.pram.reset(); +} diff --git a/ares/n64/aleck64/game-config/11beat.hpp b/ares/n64/aleck64/game-config/11beat.hpp new file mode 100644 index 0000000000..612c053581 --- /dev/null +++ b/ares/n64/aleck64/game-config/11beat.hpp @@ -0,0 +1,11 @@ +struct _11beat : Aleck64::GameConfig { + auto dpadDisabled() -> n1 { return true; } + + auto dipSwitches(Node::Object parent) -> void { + Node::Setting::Boolean testMode = parent->append("Test Mode", false, [&](auto value) { + aleck64.dipSwitch[1].bit(7) = !value; + }); + testMode->setDynamic(true); + testMode->modify(testMode->value()); + } +}; diff --git a/ares/n64/aleck64/game-config/doncdoon.hpp b/ares/n64/aleck64/game-config/doncdoon.hpp new file mode 100644 index 0000000000..55c1f8da74 --- /dev/null +++ b/ares/n64/aleck64/game-config/doncdoon.hpp @@ -0,0 +1,9 @@ +struct doncdoon : Aleck64::GameConfig { + auto dipSwitches(Node::Object parent) -> void { + Node::Setting::Boolean testMode = parent->append("Test Mode", false, [&](auto value) { + aleck64.dipSwitch[0].bit(0) = !value; + }); + testMode->setDynamic(true); + testMode->modify(testMode->value()); + } +}; diff --git a/ares/n64/aleck64/game-config/hipai.hpp b/ares/n64/aleck64/game-config/hipai.hpp new file mode 100644 index 0000000000..b135b88229 --- /dev/null +++ b/ares/n64/aleck64/game-config/hipai.hpp @@ -0,0 +1,76 @@ +struct hipai : Aleck64::GameConfig { + u8 mahjongRow = 0; + + auto dipSwitches(Node::Object parent) -> void { + Node::Setting::Boolean testMode = parent->append("Test Mode", false, [&](auto value) { + aleck64.dipSwitch[1].bit(7) = !value; + }); + testMode->setDynamic(true); + testMode->modify(testMode->value()); + + Node::Setting::Boolean freePlay = parent->append("Free Play", false, [&](auto value) { + aleck64.dipSwitch[0].bit(7) = !value; + }); + freePlay->setDynamic(true); + freePlay->modify(freePlay->value()); + + + Node::Setting::String coinage = parent->append("Coinage", "1 Coin 1 Credit", [&](auto value) { + if(value == "1 Coin 1 Credit" ) aleck64.dipSwitch[0].bit(0,2) = 7; + if(value == "1 Coin 2 Credits" ) aleck64.dipSwitch[0].bit(0,2) = 3; + if(value == "1 Coin 3 Credits" ) aleck64.dipSwitch[0].bit(0,2) = 5; + if(value == "1 Coin 4 Credits" ) aleck64.dipSwitch[0].bit(0,2) = 4; + if(value == "1 Coins 2 Credits") aleck64.dipSwitch[0].bit(0,2) = 6; + if(value == "3 Coins 1 Credit" ) aleck64.dipSwitch[0].bit(0,2) = 2; + if(value == "4 Coins 1 Credit" ) aleck64.dipSwitch[0].bit(0,2) = 1; + if(value == "5 Coins 1 Credit" ) aleck64.dipSwitch[0].bit(0,2) = 0; + }); + coinage->setAllowedValues({"1 Coin 1 Credit" , "1 Coin 2 Credits" , "1 Coin 3 Credits" , "1 Coin 4 Credits", + "1 Coins 2 Credit", "3 Coins 1 Credit", "4 Coins 1 Credit", "5 Coins 1 Credit", + }); + coinage->setDynamic(true); + coinage->modify(coinage->value()); + + Node::Setting::Boolean demoSounds = parent->append("Demo Sounds", true, [&](auto value) { + aleck64.dipSwitch[1].bit(5) = !value; + }); + demoSounds->setDynamic(true); + demoSounds->modify(demoSounds->value()); + + Node::Setting::Boolean allowContinue = parent->append("Allow Continue", false, [&](auto value) { + aleck64.dipSwitch[1].bit(4) = value; + }); + allowContinue->setDynamic(true); + allowContinue->modify(allowContinue->value()); + + Node::Setting::Boolean kuitan = parent->append("Kuitan", false, [&](auto value) { + aleck64.dipSwitch[1].bit(3) = value; + }); + kuitan->setDynamic(true); + kuitan->modify(kuitan->value()); + + Node::Setting::String difficulty = parent->append("Difficulty", "Normal", [&](auto value) { + if(value == "Normal" ) aleck64.dipSwitch[1].bit(0,2) = 7; + if(value == "Easiest" ) aleck64.dipSwitch[1].bit(0,2) = 6; + if(value == "Very Easy") aleck64.dipSwitch[1].bit(0,2) = 5; + if(value == "Easy" ) aleck64.dipSwitch[1].bit(0,2) = 4; + if(value == "Normal+" ) aleck64.dipSwitch[1].bit(0,2) = 3; + if(value == "Hard" ) aleck64.dipSwitch[1].bit(0,2) = 2; + if(value == "Very Hard") aleck64.dipSwitch[1].bit(0,2) = 1; + if(value == "Most Hard") aleck64.dipSwitch[1].bit(0,2) = 0; + }); + difficulty->setAllowedValues({"Normal", "Easiest", "Very Easy", "Easy", "Normal+", "Hard", "Very Hard", "Most Hard"}); + difficulty->setDynamic(true); + difficulty->modify(difficulty->value()); + } + + auto readExpansionPort() -> u32 { + n32 value = 0xffff'ffff; + value.bit(16, 23) = aleck64.controls.mahjong(mahjongRow); + return value; + } + + auto writeExpansionPort(n32 data) -> void { + mahjongRow = data.bit(8, 15); + }; +}; diff --git a/ares/n64/aleck64/game-config/hipai2.hpp b/ares/n64/aleck64/game-config/hipai2.hpp new file mode 100644 index 0000000000..6f3756117e --- /dev/null +++ b/ares/n64/aleck64/game-config/hipai2.hpp @@ -0,0 +1,81 @@ +struct hipai2 : Aleck64::GameConfig { + u8 mahjongRow = 0; + + auto dipSwitches(Node::Object parent) -> void { + Node::Setting::Boolean testMode = parent->append("Test Mode", false, [&](auto value) { + aleck64.dipSwitch[1].bit(7) = !value; + }); + testMode->setDynamic(true); + testMode->modify(testMode->value()); + + Node::Setting::Boolean freePlay = parent->append("Free Play", false, [&](auto value) { + aleck64.dipSwitch[0].bit(7) = !value; + }); + freePlay->setDynamic(true); + freePlay->modify(freePlay->value()); + + Node::Setting::Boolean backupSettings = parent->append("Backup Settings", false, [&](auto value) { + aleck64.dipSwitch[0].bit(6) = !value; + }); + backupSettings->setDynamic(true); + backupSettings->modify(backupSettings->value()); + + Node::Setting::String coinage = parent->append("Coinage", "1 Coin 1 Credit", [&](auto value) { + if(value == "1 Coin 1 Credit" ) aleck64.dipSwitch[0].bit(0,2) = 7; + if(value == "1 Coin 2 Credits" ) aleck64.dipSwitch[0].bit(0,2) = 3; + if(value == "1 Coin 3 Credits" ) aleck64.dipSwitch[0].bit(0,2) = 5; + if(value == "1 Coin 4 Credits" ) aleck64.dipSwitch[0].bit(0,2) = 4; + if(value == "1 Coins 2 Credits") aleck64.dipSwitch[0].bit(0,2) = 6; + if(value == "3 Coins 1 Credit" ) aleck64.dipSwitch[0].bit(0,2) = 2; + if(value == "4 Coins 1 Credit" ) aleck64.dipSwitch[0].bit(0,2) = 1; + if(value == "5 Coins 1 Credit" ) aleck64.dipSwitch[0].bit(0,2) = 0; + }); + coinage->setAllowedValues({"1 Coin 1 Credit" , "1 Coin 2 Credits" , "1 Coin 3 Credits" , "1 Coin 4 Credits", + "1 Coins 2 Credit", "3 Coins 1 Credit", "4 Coins 1 Credit", "5 Coins 1 Credit", + }); + coinage->setDynamic(true); + coinage->modify(coinage->value()); + + Node::Setting::Boolean demoSounds = parent->append("Demo Sounds", true, [&](auto value) { + aleck64.dipSwitch[1].bit(5) = !value; + }); + demoSounds->setDynamic(true); + demoSounds->modify(demoSounds->value()); + + Node::Setting::Boolean allowContinue = parent->append("Allow Continue", false, [&](auto value) { + aleck64.dipSwitch[1].bit(4) = value; + }); + allowContinue->setDynamic(true); + allowContinue->modify(allowContinue->value()); + + Node::Setting::Boolean kuitan = parent->append("Kuitan", false, [&](auto value) { + aleck64.dipSwitch[1].bit(3) = value; + }); + kuitan->setDynamic(true); + kuitan->modify(kuitan->value()); + + Node::Setting::String difficulty = parent->append("Difficulty", "Normal", [&](auto value) { + if(value == "Normal" ) aleck64.dipSwitch[1].bit(0,2) = 7; + if(value == "Easiest" ) aleck64.dipSwitch[1].bit(0,2) = 6; + if(value == "Very Easy") aleck64.dipSwitch[1].bit(0,2) = 5; + if(value == "Easy" ) aleck64.dipSwitch[1].bit(0,2) = 4; + if(value == "Normal+" ) aleck64.dipSwitch[1].bit(0,2) = 3; + if(value == "Hard" ) aleck64.dipSwitch[1].bit(0,2) = 2; + if(value == "Very Hard") aleck64.dipSwitch[1].bit(0,2) = 1; + if(value == "Most Hard") aleck64.dipSwitch[1].bit(0,2) = 0; + }); + difficulty->setAllowedValues({"Normal", "Easiest", "Very Easy", "Easy", "Normal+", "Hard", "Very Hard", "Most Hard"}); + difficulty->setDynamic(true); + difficulty->modify(difficulty->value()); + } + + auto readExpansionPort() -> u32 { + n32 value = 0xffff'ffff; + value.bit(16, 23) = aleck64.controls.mahjong(mahjongRow); + return value; + } + + auto writeExpansionPort(n32 data) -> void { + mahjongRow = data.bit(8, 15); + }; +}; diff --git a/ares/n64/aleck64/game-config/kurufev.hpp b/ares/n64/aleck64/game-config/kurufev.hpp new file mode 100644 index 0000000000..7a97d46a5a --- /dev/null +++ b/ares/n64/aleck64/game-config/kurufev.hpp @@ -0,0 +1,9 @@ +struct kurufev : Aleck64::GameConfig { + auto dipSwitches(Node::Object parent) -> void { + Node::Setting::Boolean testMode = parent->append("Test Mode", false, [&](auto value) { + aleck64.dipSwitch[0].bit(0) = !value; + }); + testMode->setDynamic(true); + testMode->modify(testMode->value()); + } +}; diff --git a/ares/n64/aleck64/game-config/mayjin3.hpp b/ares/n64/aleck64/game-config/mayjin3.hpp new file mode 100644 index 0000000000..1c3097fe6c --- /dev/null +++ b/ares/n64/aleck64/game-config/mayjin3.hpp @@ -0,0 +1,9 @@ +struct mayjin3 : Aleck64::GameConfig { + auto dipSwitches(Node::Object parent) -> void { + Node::Setting::Boolean testMode = parent->append("Test Mode", false, [&](auto value) { + aleck64.dipSwitch[1].bit(7) = !value; + }); + testMode->setDynamic(true); + testMode->modify(testMode->value()); + } +}; diff --git a/ares/n64/aleck64/game-config/mtetrisc.hpp b/ares/n64/aleck64/game-config/mtetrisc.hpp new file mode 100644 index 0000000000..babf7d8b9c --- /dev/null +++ b/ares/n64/aleck64/game-config/mtetrisc.hpp @@ -0,0 +1,11 @@ +struct mtetrisc : Aleck64::GameConfig { + auto type() -> Aleck64::BoardType { return Aleck64::BoardType::E90; } + + auto dipSwitches(Node::Object parent) -> void { + Node::Setting::Boolean testMode = parent->append("Test Mode", false, [&](auto value) { + aleck64.dipSwitch[1].bit(7) = !value; + }); + testMode->setDynamic(true); + testMode->modify(testMode->value()); + } +}; diff --git a/ares/n64/aleck64/game-config/srmvs.hpp b/ares/n64/aleck64/game-config/srmvs.hpp new file mode 100644 index 0000000000..2ab8e036ad --- /dev/null +++ b/ares/n64/aleck64/game-config/srmvs.hpp @@ -0,0 +1,21 @@ +struct srmvs : Aleck64::GameConfig { + u8 mahjongRow = 0; + + auto dipSwitches(Node::Object parent) -> void { + Node::Setting::Boolean testMode = parent->append("Test Mode", false, [&](auto value) { + aleck64.dipSwitch[1].bit(7) = !value; + }); + testMode->setDynamic(true); + testMode->modify(testMode->value()); + } + + auto readExpansionPort() -> u32 { + n32 value = 0xffff'ffff; + value.bit(16, 23) = aleck64.controls.mahjong(mahjongRow); + return value; + } + + auto writeExpansionPort(n32 data) -> void { + mahjongRow = data.bit(8, 15); + }; +}; diff --git a/ares/n64/aleck64/game-config/starsldr.hpp b/ares/n64/aleck64/game-config/starsldr.hpp new file mode 100644 index 0000000000..61f2e07068 --- /dev/null +++ b/ares/n64/aleck64/game-config/starsldr.hpp @@ -0,0 +1,92 @@ +struct starsldr : Aleck64::GameConfig { + auto dipSwitches(Node::Object parent) -> void { + Node::Setting::Boolean testMode = parent->append("Test Mode", false, [&](auto value) { + aleck64.dipSwitch[1].bit(7) = !value; + }); + testMode->setDynamic(true); + testMode->modify(testMode->value()); + + Node::Setting::String joystickType = parent->append("Joystick Type", "2D", [&](auto value) { + aleck64.dipSwitch[0].bit(7) = value == "3D"; + }); + joystickType->setAllowedValues({ "2D", "3D" }); + joystickType->setDynamic(true); + joystickType->modify(joystickType->value()); + + Node::Setting::String player = parent->append("Player", "3", [&](auto value) { + if(value == "3") aleck64.dipSwitch[0].bit(3,4) = 3; + if(value == "4") aleck64.dipSwitch[0].bit(3,4) = 2; + if(value == "2") aleck64.dipSwitch[0].bit(3,4) = 1; + if(value == "1") aleck64.dipSwitch[0].bit(3,4) = 0; + }); + player->setAllowedValues({"4", "3", "2", "1"}); + player->setDynamic(true); + player->modify(player->value()); + + Node::Setting::String autoLevel = parent->append("Auto Level", "Normal", [&](auto value) { + if(value == "Normal") aleck64.dipSwitch[0].bit(5,6) = 3; + if(value == "Slow" ) aleck64.dipSwitch[0].bit(5,6) = 2; + if(value == "Fast1" ) aleck64.dipSwitch[0].bit(5,6) = 1; + if(value == "Fast2" ) aleck64.dipSwitch[0].bit(5,6) = 0; + }); + autoLevel->setAllowedValues({ "Normal", "Slow", "Fast1", "Fast2" }); + autoLevel->setDynamic(true); + autoLevel->modify(autoLevel->value()); + + Node::Setting::String coinage = parent->append("Coinage", "1 Coin 1 Credit", [&](auto value) { + if(value == "1 Coin 1 Credit" ) aleck64.dipSwitch[0].bit(0,2) = 7; + if(value == "1 Coin 2 Credits" ) aleck64.dipSwitch[0].bit(0,2) = 6; + if(value == "1 Coin 3 Credits" ) aleck64.dipSwitch[0].bit(0,2) = 5; + if(value == "1 Coin 4 Credits" ) aleck64.dipSwitch[0].bit(0,2) = 4; + if(value == "2 Coins 1 Credit" ) aleck64.dipSwitch[0].bit(0,2) = 3; + if(value == "3 Coins 1 Credit") aleck64.dipSwitch[0].bit(0,2) = 2; + if(value == "4 Coins 1 Credit") aleck64.dipSwitch[0].bit(0,2) = 1; + if(value == "5 Coins 1 Credit") aleck64.dipSwitch[0].bit(0,2) = 0; + }); + + coinage->setAllowedValues({ "1 Coin 1 Credit", "1 Coin 2 Credits", "1 Coin 3 Credits", "1 Coin 4 Credits", + "2 Coins 1 Credit", "3 Coins 1 Credit", "4 Coins 1 Credit", "5 Coins 1 Credit" }); + coinage->setDynamic(true); + coinage->modify(coinage->value()); + + Node::Setting::String language = parent->append("Language", "English", [&](auto value) { + aleck64.dipSwitch[1].bit(6) = value == "English"; + }); + language->setAllowedValues({ "English", "Japanese" }); + language->setDynamic(true); + language->modify(language->value()); + + Node::Setting::Boolean demoSound = parent->append("Demo Sound", true, [&](auto value) { + aleck64.dipSwitch[1].bit(5) = !value; + }); + demoSound->setDynamic(true); + demoSound->modify(demoSound->value()); + + Node::Setting::Boolean rapid = parent->append("Rapid", false, [&](auto value) { + aleck64.dipSwitch[1].bit(4) = value; + }); + rapid->setDynamic(true); + rapid->modify(rapid->value()); + + Node::Setting::String extend = parent->append("Extend", "30000000", [&](auto value) { + if(value == "30,000,000") aleck64.dipSwitch[1].bit(2,3) = 3; + if(value == "50,000,000") aleck64.dipSwitch[1].bit(2,3) = 2; + if(value == "70,000,000") aleck64.dipSwitch[1].bit(2,3) = 1; + if(value == "None" ) aleck64.dipSwitch[1].bit(2,3) = 0; + }); + extend->setAllowedValues({ "30,000,000", "50,000,000", "70,000,000", "None" }); + extend->setDynamic(true); + extend->modify(extend->value()); + + Node::Setting::String difficulty = parent->append("Difficulty", "Normal", [&](auto value) { + if(value == "Normal") aleck64.dipSwitch[1].bit(0,1) = 3; + if(value == "Easy" ) aleck64.dipSwitch[1].bit(0,1) = 2; + if(value == "Hard1" ) aleck64.dipSwitch[1].bit(0,1) = 1; + if(value == "Hard2" ) aleck64.dipSwitch[1].bit(0,1) = 0; + }); + + difficulty->setAllowedValues({ "Normal", "Easy", "Hard1", "Hard2" }); + difficulty->setDynamic(true); + difficulty->modify(difficulty->value()); + } +}; diff --git a/ares/n64/aleck64/game-config/twrshaft.hpp b/ares/n64/aleck64/game-config/twrshaft.hpp new file mode 100644 index 0000000000..b410297d87 --- /dev/null +++ b/ares/n64/aleck64/game-config/twrshaft.hpp @@ -0,0 +1,9 @@ +struct twrshaft : Aleck64::GameConfig { + auto dipSwitches(Node::Object parent) -> void { + Node::Setting::Boolean testMode = parent->append("Test Mode", false, [&](auto value) { + aleck64.dipSwitch[1].bit(7) = !value; + }); + testMode->setDynamic(true); + testMode->modify(testMode->value()); + } +}; diff --git a/ares/n64/aleck64/game-config/vivdolls.hpp b/ares/n64/aleck64/game-config/vivdolls.hpp new file mode 100644 index 0000000000..02c7d6fe86 --- /dev/null +++ b/ares/n64/aleck64/game-config/vivdolls.hpp @@ -0,0 +1,61 @@ +struct vivdolls : Aleck64::GameConfig { + auto dipSwitches(Node::Object parent) -> void { + Node::Setting::Boolean testMode = parent->append("Test Mode", false, [&](auto value) { + aleck64.dipSwitch[1].bit(7) = !value; + }); + testMode->setDynamic(true); + testMode->modify(testMode->value()); + + Node::Setting::String coinage = parent->append("Coinage", "1 Coin 1 Credit", [&](auto value) { + if(value == "1 Coin 1 Credit" ) aleck64.dipSwitch[0].bit(0,2) = 7; + if(value == "1 Coin 2 Credits") aleck64.dipSwitch[0].bit(0,2) = 3; + if(value == "1 Coin 3 Credits") aleck64.dipSwitch[0].bit(0,2) = 5; + if(value == "1 Coin 4 Credits") aleck64.dipSwitch[0].bit(0,2) = 1; + if(value == "2 Coins 1 Credit") aleck64.dipSwitch[0].bit(0,2) = 6; + if(value == "3 Coins 1 Credit") aleck64.dipSwitch[0].bit(0,2) = 2; + if(value == "4 Coins 1 Credit") aleck64.dipSwitch[0].bit(0,2) = 4; + if(value == "5 Coins 1 Credit") aleck64.dipSwitch[0].bit(0,2) = 0; + }); + + coinage->setAllowedValues({ "1 Coin 1 Credit", "1 Coin 2 Credits", "1 Coin 3 Credits", "1 Coin 4 Credits", + "2 Coins 1 Credit", "3 Coins 1 Credit", "4 Coins 1 Credit", "5 Coins 1 Credit" }); + coinage->setDynamic(true); + coinage->modify(coinage->value()); + + Node::Setting::String qualifyArea = parent->append("Qualify Area", "70%", [&](auto value) { + if(value == "70%") aleck64.dipSwitch[1].bit(5) = 0; + if(value == "80%") aleck64.dipSwitch[1].bit(5) = 1; + }); + qualifyArea->setAllowedValues({ "70%", "80%" }); + qualifyArea->setDynamic(true); + qualifyArea->modify(qualifyArea->value()); + + Node::Setting::String controls = parent->append("Controls", "JOYSTICK", [&](auto value) { + if(value == "JAMMASTICK") aleck64.dipSwitch[1].bit(5) = 0; + if(value == "JOYSTICK" ) aleck64.dipSwitch[1].bit(5) = 1; + }); + controls->setAllowedValues({ "JAMMASTICK", "JOYSTICK" }); + controls->setDynamic(true); + controls->modify(controls->value()); + + Node::Setting::String lives = parent->append("Lives", "4", [&](auto value) { + if(value == "4") aleck64.dipSwitch[1].bit(2,3) = 3; + if(value == "3") aleck64.dipSwitch[1].bit(2,3) = 2; + if(value == "2") aleck64.dipSwitch[1].bit(2,3) = 1; + if(value == "1") aleck64.dipSwitch[1].bit(2,3) = 0; + }); + lives->setAllowedValues({ "4", "3", "2", "1" }); + lives->setDynamic(true); + lives->modify(lives->value()); + + Node::Setting::String difficulty = parent->append("Difficulty", "Normal", [&](auto value) { + if(value == "Normal" ) aleck64.dipSwitch[1].bit(0,1) = 3; + if(value == "Easy" ) aleck64.dipSwitch[1].bit(0,1) = 2; + if(value == "Hard" ) aleck64.dipSwitch[1].bit(0,1) = 1; + if(value == "Hardest") aleck64.dipSwitch[1].bit(0,1) = 0; + }); + difficulty->setAllowedValues({ "Normal", "Easy", "Hard", "Hardest" }); + difficulty->setDynamic(true); + difficulty->modify(difficulty->value()); + } +}; diff --git a/ares/n64/aleck64/io.cpp b/ares/n64/aleck64/io.cpp new file mode 100644 index 0000000000..c4ebf50110 --- /dev/null +++ b/ares/n64/aleck64/io.cpp @@ -0,0 +1,79 @@ +auto Aleck64::readPort1() -> u32 { + n32 value = 0xffff'ffff; + + if(gameConfig->type() == BoardType::E92) { + value.bit(0) = !aleck64.controls.p1up->value(); + value.bit(1) = !aleck64.controls.p1down->value(); + value.bit(2) = !aleck64.controls.p1left->value(); + value.bit(3) = !aleck64.controls.p1right->value(); + value.bit(4) = !aleck64.controls.p1[0]->value(); + value.bit(5) = !aleck64.controls.p1[1]->value(); + value.bit(6) = !aleck64.controls.p1[2]->value(); + value.bit(7) = !aleck64.controls.p1[3]->value(); + value.bit(8) = !aleck64.controls.p2up->value(); + value.bit(9) = !aleck64.controls.p2down->value(); + value.bit(10) = !aleck64.controls.p2left->value(); + value.bit(11) = !aleck64.controls.p2right->value(); + value.bit(12) = !aleck64.controls.p2[0]->value(); + value.bit(13) = !aleck64.controls.p2[1]->value(); + value.bit(14) = !aleck64.controls.p2[2]->value(); + value.bit(15) = !aleck64.controls.p2[3]->value(); + } else if(gameConfig->type() == BoardType::E90) { + value.bit( 0) = !aleck64.controls.p1up->value(); + value.bit( 1) = !aleck64.controls.p1down->value(); + value.bit( 2) = !aleck64.controls.p1left->value(); + value.bit( 3) = !aleck64.controls.p1right->value(); + value.bit( 4) = !aleck64.controls.p1[0]->value(); + value.bit( 5) = !aleck64.controls.p1[1]->value(); + value.bit( 7) = !aleck64.controls.p1start->value(); + value.bit( 8) = !aleck64.controls.p2up->value(); + value.bit( 9) = !aleck64.controls.p2down->value(); + value.bit(10) = !aleck64.controls.p2left->value(); + value.bit(11) = !aleck64.controls.p2right->value(); + value.bit(12) = !aleck64.controls.p2[0]->value(); + value.bit(13) = !aleck64.controls.p2[1]->value(); + value.bit(15) = !aleck64.controls.p2start->value(); + } + + value.bit(16, 23) = dipSwitch[1]; + value.bit(24, 31) = dipSwitch[0]; + + return value; +} + +auto Aleck64::readPort2() -> u32 { + n32 value = 0xffff'ffff; + + if(gameConfig->type() == BoardType::E92) { + value.bit(16) = !aleck64.controls.p1start->value(); + value.bit(17) = !aleck64.controls.p2start->value(); + value.bit(18) = !aleck64.controls.p1coin->value(); + value.bit(19) = !aleck64.controls.p2coin->value(); + value.bit(20) = !aleck64.controls.service->value(); + value.bit(21) = !aleck64.controls.test->value(); + } else if(gameConfig->type() == BoardType::E90) { + value.bit (0) = !aleck64.controls.p1coin->value(); + value.bit( 1) = !aleck64.controls.p2coin->value(); + value.bit( 4) = !aleck64.controls.service->value(); + value.bit( 5) = !aleck64.controls.test->value(); + } + + return value; +} + +auto Aleck64::readPort3() -> u32 { + return gameConfig->readExpansionPort(); +} + +auto Aleck64::writePort3(n32 data) -> void { + return gameConfig->writeExpansionPort(data); +} + +auto Aleck64::readPort4() -> u32 { + debug(unimplemented, "[Aleck64::readPort4]"); + return 0x0; +} + +auto Aleck64::writePort4(n32 data) -> void { + debug(unimplemented, "[Aleck64::writePort3] ", hex(data, 8L)); +} diff --git a/ares/n64/aleck64/serialization.cpp b/ares/n64/aleck64/serialization.cpp new file mode 100644 index 0000000000..c4e7ca90ed --- /dev/null +++ b/ares/n64/aleck64/serialization.cpp @@ -0,0 +1,9 @@ +auto Aleck64::serialize(serializer& s) -> void { + s(sdram); + s(vram); + s(pram); + + s(vdp.io.enable); + s(dipSwitch[0]); + s(dipSwitch[1]); +} diff --git a/ares/n64/aleck64/vdp.cpp b/ares/n64/aleck64/vdp.cpp new file mode 100644 index 0000000000..a76c55f467 --- /dev/null +++ b/ares/n64/aleck64/vdp.cpp @@ -0,0 +1,26 @@ +//TODO: Implement the VDP used by tetris on the E90 board +//Verified to be available on cartridges for E92 board by Zoinkitty's E90->E92 conversion patch for mtetrisc +//Hardware is mostly unknown, seems to use an (encrypted?) character rom (tet-01m.u5) + +auto Aleck64::VDP::readWord(u32 address) -> u32 { + n32 value = 0xffffffff; + + if(address == 0x00) { + value = !io.enable; //TODO: Verify on Hardware; mtetrisc expects this behavior + } + + debug(unusual, "[Aleck64::VDP::readWord] ", hex(address, 8L)); + return value; +} + +auto Aleck64::VDP::writeWord(u32 address, n32 data) -> void { + if(address == 0x1e) { + io.enable = data.bit(0); + } + + debug(unusual, "[Aleck64::VDP::writeWord] ", hex(address, 8L), " = ", hex(data, 8L)); +} + +auto Aleck64::VDP::render(Node::Video::Screen screen) -> void { + if(!io.enable) return; +} diff --git a/ares/n64/cic/cic.cpp b/ares/n64/cic/cic.cpp index b6949ad939..78f024d6da 100644 --- a/ares/n64/cic/cic.cpp +++ b/ares/n64/cic/cic.cpp @@ -8,9 +8,16 @@ CIC cic; #include "serialization.cpp" auto CIC::power(bool reset) -> void { - model = cartridge.node ? cartridge.cic() : dd.cic(); + if(Model::Aleck64()) { + model = "CIC-NUS-5101"; + } else { + model = cartridge.node ? cartridge.cic() : dd.cic(); + } + type = Cartridge; challengeAlgo = DummyChallenge; + + if(model == "CIC-NUS-5101") region = NTSC, seed = 0xac, checksum = 0x93e983a8f152ull; if(model == "CIC-NUS-6101") region = NTSC, seed = 0x3f, checksum = 0x45cc73ee317aull; if(model == "CIC-NUS-6102") region = NTSC, seed = 0x3f, checksum = 0xa536c0f1d859ull; if(model == "CIC-NUS-7101") region = PAL, seed = 0x3f, checksum = 0xa536c0f1d859ull; diff --git a/ares/n64/controller/aleck64/aleck64.cpp b/ares/n64/controller/aleck64/aleck64.cpp new file mode 100644 index 0000000000..dfb8abf2b3 --- /dev/null +++ b/ares/n64/controller/aleck64/aleck64.cpp @@ -0,0 +1,128 @@ +Aleck64Controls::Aleck64Controls(Node::Port parent) { + node = parent->append("Aleck64"); + + //Detect which port we are connected to and map the right arcade controls accordingly + if(parent->name() == "Controller Port 1") { + playerIndex = 1; + } else if(parent->name() == "Controller Port 2") { + playerIndex = 2; + } +} + +Aleck64Controls::~Aleck64Controls() { + +} + +auto Aleck64Controls::comm(n8 send, n8 recv, n8 input[], n8 output[]) -> n2 { + b1 valid = 0; + b1 over = 0; + + //status + if(input[0] == 0x00 || input[0] == 0xff) { + output[0] = 0x05; //0x05 = gamepad; 0x02 = mouse + output[1] = 0x00; + output[2] = 0x02; //0x02 = nothing present in controller slot + valid = 1; + } + + //read controller state + if(input[0] == 0x01) { + u32 data = read(); + output[0] = data >> 24; + output[1] = data >> 16; + output[2] = data >> 8; + output[3] = data >> 0; + if(recv <= 4) { + over = 0; + } else { + over = 1; + } + valid = 1; + } + + n2 status = 0; + status.bit(0) = valid; + status.bit(1) = over; + return status; +} + +auto Aleck64Controls::read() -> n32 { + aleck64.controls.poll(); + + auto cardinalMax = 85.0; + auto diagonalMax = 69.0; + auto innerDeadzone = 7.0; // default should remain 7 (~8.2% of 85) as the deadzone is axial in nature and fights cardinalMax + auto outerDeadzoneRadiusMax = 2.0 / sqrt(2.0) * (diagonalMax / cardinalMax * (cardinalMax - innerDeadzone) + innerDeadzone); //from linear scaling equation, substitute outerDeadzoneRadiusMax*sqrt(2)/2 for lengthAbsoluteX and set diagonalMax as the result then solve for outerDeadzoneRadiusMax + + //scale {-32768 ... +32767} to {-outerDeadzoneRadiusMax ... +outerDeadzoneRadiusMax} + auto ax = aleck64.controls.controllerAxis(playerIndex, "X") * outerDeadzoneRadiusMax / 32767.0; + auto ay = aleck64.controls.controllerAxis(playerIndex, "Y") * outerDeadzoneRadiusMax / 32767.0; + + //create inner axial dead-zone in range {-innerDeadzone ... +innerDeadzone} and scale from it up to outer circular dead-zone of radius outerDeadzoneRadiusMax + auto length = sqrt(ax * ax + ay * ay); + if(length <= outerDeadzoneRadiusMax) { + auto lengthAbsoluteX = abs(ax); + auto lengthAbsoluteY = abs(ay); + if(lengthAbsoluteX <= innerDeadzone) { + lengthAbsoluteX = 0.0; + } else { + lengthAbsoluteX = (lengthAbsoluteX - innerDeadzone) * cardinalMax / (cardinalMax - innerDeadzone) / lengthAbsoluteX; + } + ax *= lengthAbsoluteX; + if(lengthAbsoluteY <= innerDeadzone) { + lengthAbsoluteY = 0.0; + } else { + lengthAbsoluteY = (lengthAbsoluteY - innerDeadzone) * cardinalMax / (cardinalMax - innerDeadzone) / lengthAbsoluteY; + } + ay *= lengthAbsoluteY; + } else { + length = outerDeadzoneRadiusMax / length; + ax *= length; + ay *= length; + } + + //bound diagonals to an octagonal range {-diagonalMax ... +diagonalMax} + if(ax != 0.0 && ay != 0.0) { + auto slope = ay / ax; + auto edgex = copysign(cardinalMax / (abs(slope) + (cardinalMax - diagonalMax) / diagonalMax), ax); + auto edgey = copysign(min(abs(edgex * slope), cardinalMax / (1.0 / abs(slope) + (cardinalMax - diagonalMax) / diagonalMax)), ay); + edgex = edgey / slope; + + length = sqrt(ax * ax + ay * ay); + auto distanceToEdge = sqrt(edgex * edgex + edgey * edgey); + if(length > distanceToEdge) { + ax = edgex; + ay = edgey; + } + } + + //keep cardinal input within positive and negative bounds of cardinalMax + if(abs(ax) > cardinalMax) ax = copysign(cardinalMax, ax); + if(abs(ay) > cardinalMax) ay = copysign(cardinalMax, ay); + + //add epsilon to counteract floating point precision error + ax = copysign(abs(ax) + 1e-09, ax); + ay = copysign(abs(ay) + 1e-09, ay); + + n32 data; + data.byte(0) = s8(-ay); + data.byte(1) = s8(+ax); + data.bit(16) = aleck64.controls.controllerButton(playerIndex, "C-Right"); + data.bit(17) = aleck64.controls.controllerButton(playerIndex, "C-Left"); + data.bit(18) = aleck64.controls.controllerButton(playerIndex, "C-Down"); + data.bit(19) = aleck64.controls.controllerButton(playerIndex, "C-Up"); + data.bit(20) = aleck64.controls.controllerButton(playerIndex, "R"); + data.bit(21) = aleck64.controls.controllerButton(playerIndex, "L"); + data.bit(22) = 0; //GND + data.bit(23) = 0; //RST + data.bit(24) = aleck64.controls.controllerButton(playerIndex, "Right"); + data.bit(25) = aleck64.controls.controllerButton(playerIndex, "Left"); + data.bit(26) = aleck64.controls.controllerButton(playerIndex, "Down"); + data.bit(27) = aleck64.controls.controllerButton(playerIndex, "Up"); + data.bit(28) = aleck64.controls.controllerButton(playerIndex, "Start"); + data.bit(29) = aleck64.controls.controllerButton(playerIndex, "Z"); + data.bit(30) = aleck64.controls.controllerButton(playerIndex, "B"); + data.bit(31) = aleck64.controls.controllerButton(playerIndex, "A"); + + return data; +} diff --git a/ares/n64/controller/aleck64/aleck64.hpp b/ares/n64/controller/aleck64/aleck64.hpp new file mode 100644 index 0000000000..c714e11199 --- /dev/null +++ b/ares/n64/controller/aleck64/aleck64.hpp @@ -0,0 +1,9 @@ +struct Aleck64Controls : Controller { + Aleck64Controls(Node::Port); + ~Aleck64Controls(); + auto comm(n8 send, n8 recv, n8 input[], n8 output[]) -> n2 override; + auto read() -> n32 override; + + int playerIndex; +}; + diff --git a/ares/n64/controller/controller.cpp b/ares/n64/controller/controller.cpp index 110a531092..9e0fdc7d8c 100644 --- a/ares/n64/controller/controller.cpp +++ b/ares/n64/controller/controller.cpp @@ -5,5 +5,6 @@ namespace ares::Nintendo64 { #include "port.cpp" #include "gamepad/gamepad.cpp" #include "mouse/mouse.cpp" +#include "aleck64/aleck64.cpp" } diff --git a/ares/n64/controller/controller.hpp b/ares/n64/controller/controller.hpp index 64b9db84c7..fe8ec7ba82 100644 --- a/ares/n64/controller/controller.hpp +++ b/ares/n64/controller/controller.hpp @@ -12,3 +12,4 @@ struct Controller { #include "port.hpp" #include "gamepad/gamepad.hpp" #include "mouse/mouse.hpp" +#include "aleck64/aleck64.hpp" diff --git a/ares/n64/controller/port.cpp b/ares/n64/controller/port.cpp index 9cd9bc6ac4..6c8aacfe6e 100644 --- a/ares/n64/controller/port.cpp +++ b/ares/n64/controller/port.cpp @@ -10,10 +10,17 @@ auto ControllerPort::load(Node::Object parent) -> void { port = parent->append(name); port->setFamily("Nintendo 64"); port->setType("Controller"); - port->setHotSwappable(true); + + if(Model::Aleck64()) { + port->setHotSwappable(false); + port->setSupported({"Aleck64"}); + } else { + port->setHotSwappable(true); + port->setSupported({"Gamepad", "Mouse"}); + } + port->setAllocate([&](auto name) { return allocate(name); }); port->setDisconnect([&] { device.reset(); }); - port->setSupported({"Gamepad", "Mouse"}); } auto ControllerPort::unload() -> void { @@ -28,6 +35,7 @@ auto ControllerPort::save() -> void { auto ControllerPort::allocate(string name) -> Node::Peripheral { if(name == "Gamepad") device = new Gamepad(port); if(name == "Mouse" ) device = new Mouse(port); + if(name == "Aleck64") device = new Aleck64Controls(port); if(device) return device->node; return {}; } diff --git a/ares/n64/memory/bus.hpp b/ares/n64/memory/bus.hpp index 61dd10a29f..ffab4f48e1 100644 --- a/ares/n64/memory/bus.hpp +++ b/ares/n64/memory/bus.hpp @@ -20,6 +20,7 @@ inline auto Bus::read(u32 address, Thread& thread, const char *peripheral) -> u6 if(address <= 0x1fbf'ffff) return pi.read(address, thread); if(address <= 0x1fcf'ffff) return si.read(address, thread); if(address <= 0x7fff'ffff) return pi.read(address, thread); + if(Model::Aleck64()) return aleck64.read(address, thread); return freezeUnmapped(address), 0; } @@ -43,6 +44,11 @@ inline auto Bus::readBurst(u32 address, u32 *data, Thread& thread) -> void { return; } + if(Model::Aleck64()) { + if(address <= 0xbfff'ffff) return freezeUncached(address); + if(address <= 0xc07f'ffff) return aleck64.sdram.readBurst(address, data, "CPU"); + } + return freezeUncached(address); } @@ -71,6 +77,7 @@ inline auto Bus::write(u32 address, u64 data, Thread& thread, const char *periph if(address <= 0x1fbf'ffff) return pi.write(address, data, thread); if(address <= 0x1fcf'ffff) return si.write(address, data, thread); if(address <= 0x7fff'ffff) return pi.write(address, data, thread); + if(Model::Aleck64()) return aleck64.write(address, data, thread); return freezeUnmapped(address); } @@ -88,6 +95,11 @@ inline auto Bus::writeBurst(u32 address, u32 *data, Thread& thread) -> void { return; } + if(Model::Aleck64()) { + if(address <= 0xbfff'ffff) return freezeUncached(address); + if(address <= 0xc07f'ffff) return aleck64.sdram.writeBurst(address, data, "CPU"); + } + return freezeUncached(address); } diff --git a/ares/n64/n64.cpp b/ares/n64/n64.cpp index b6b15584d8..48c6e57036 100644 --- a/ares/n64/n64.cpp +++ b/ares/n64/n64.cpp @@ -17,6 +17,7 @@ #include #include #include +#include //#include //#include diff --git a/ares/n64/n64.hpp b/ares/n64/n64.hpp index ef2810b94c..1cf42caf3a 100644 --- a/ares/n64/n64.hpp +++ b/ares/n64/n64.hpp @@ -44,6 +44,11 @@ namespace ares::Nintendo64 { enum : u32 { Read, Write }; enum : u32 { Byte = 1, Half = 2, Word = 4, Dual = 8, DCache = 16, ICache = 32 }; + struct Model { + inline static auto Nintendo64() -> bool; + inline static auto Aleck64() -> bool; + }; + struct Region { static inline auto NTSC() -> bool; static inline auto PAL() -> bool; @@ -97,6 +102,7 @@ namespace ares::Nintendo64 { #include #include #include + #include #include #include #include diff --git a/ares/n64/pif/pif.cpp b/ares/n64/pif/pif.cpp index 84db5243e7..134a87d0a8 100644 --- a/ares/n64/pif/pif.cpp +++ b/ares/n64/pif/pif.cpp @@ -32,9 +32,16 @@ auto PIF::main() -> void { auto PIF::power(bool reset) -> void { Thread::reset(); - string pifrom = Region::PAL() ? "pif.pal.rom" : "pif.ntsc.rom"; - if(auto fp = system.pak->read(pifrom)) { - rom.load(fp); + if(Model::Aleck64()) { + string pifrom = "pif.aleck64.rom"; + if (auto fp = cartridge.pak->read(pifrom)) { + rom.load(fp); + } + } else { + string pifrom = Region::PAL() ? "pif.pal.rom" : "pif.ntsc.rom"; + if (auto fp = system.pak->read(pifrom)) { + rom.load(fp); + } } ram.fill(); diff --git a/ares/n64/system/serialization.cpp b/ares/n64/system/serialization.cpp index d06abdf467..beb3d211cc 100644 --- a/ares/n64/system/serialization.cpp +++ b/ares/n64/system/serialization.cpp @@ -1,4 +1,4 @@ -static const string SerializerVersion = "v141.1"; +static const string SerializerVersion = "v141.2"; auto System::serialize(bool synchronize) -> serializer { serializer s; @@ -56,4 +56,5 @@ auto System::serialize(serializer& s, bool synchronize) -> void { s(rdp); s(rsp); s(dd); + s(aleck64); } diff --git a/ares/n64/system/system.cpp b/ares/n64/system/system.cpp index 0338abd2c1..b6392372d3 100644 --- a/ares/n64/system/system.cpp +++ b/ares/n64/system/system.cpp @@ -11,6 +11,7 @@ auto enumerate() -> vector { "[Nintendo] Nintendo 64DD (NTSC-U)", "[Nintendo] Nintendo 64DD (NTSC-J)", "[Nintendo] Nintendo 64DD (NTSC-DEV)", + "[SETA] Aleck 64", }; } @@ -40,7 +41,7 @@ auto option(string name, string value) -> bool { rsp.recompiler.enabled = value.boolean(); } } - if(name == "Expansion Pak") system.expansionPak = value.boolean(); + if(Model::Nintendo64() && name == "Expansion Pak") system.expansionPak = value.boolean(); return true; } @@ -68,20 +69,23 @@ auto System::load(Node::System& root, string name) -> bool { if(node) unload(); information = {}; - if(name.match("[Nintendo] Nintendo 64 (*)")) { - information.name = "Nintendo 64"; - information.dd = 0; - } - if(name.match("[Nintendo] Nintendo 64DD (*)")) { - information.name = "Nintendo 64"; - information.dd = 1; + information.name = "Nintendo 64"; + + if(name == "[SETA] Aleck 64") { + information.name = "Arcade"; + information.model = Model::Aleck64; + information.region = Region::NTSC; + information.videoFrequency = 48'681'818; + system.expansionPak = true; //Aleck 64 has the 8MB as standard + } else { + information.dd = name.find("64DD") ? true : false; } - if(name.find("NTSC")) { + if (name.find("NTSC")) { information.region = Region::NTSC; information.videoFrequency = 48'681'818; } - if(name.find("PAL")) { + if (name.find("PAL")) { information.region = Region::PAL; information.videoFrequency = 49'656'530; } @@ -114,6 +118,7 @@ auto System::load(Node::System& root, string name) -> bool { rsp.load(node); rdp.load(node); if(_DD()) dd.load(node); + if(model() == Model::Aleck64) aleck64.load(node); #if defined(VULKAN) vulkan.load(node); #endif @@ -306,6 +311,7 @@ auto System::unload() -> void { rsp.unload(); rdp.unload(); if(_DD()) dd.unload(); + if(model() == Model::Aleck64) aleck64.unload(); pak.reset(); node.reset(); } @@ -313,11 +319,14 @@ auto System::unload() -> void { auto System::save() -> void { if(!node) return; cartridge.save(); + controllerPort1.save(); controllerPort2.save(); controllerPort3.save(); controllerPort4.save(); if(_DD()) dd.save(); + + if(model() == Model::Aleck64) aleck64.save(); } auto System::power(bool reset) -> void { @@ -341,6 +350,7 @@ auto System::power(bool reset) -> void { cpu.power(reset); rsp.power(reset); rdp.power(reset); + if(model() == Model::Aleck64) aleck64.power(reset); } } diff --git a/ares/n64/system/system.hpp b/ares/n64/system/system.hpp index 956b42ee8a..465e4691c7 100644 --- a/ares/n64/system/system.hpp +++ b/ares/n64/system/system.hpp @@ -4,9 +4,11 @@ struct System { bool homebrewMode = false; bool expansionPak = true; + enum class Model : u32 { Nintendo64, Aleck64 }; enum class Region : u32 { NTSC, PAL }; auto name() const -> string { return information.name; } + auto model() const -> Model { return information.model; } auto region() const -> Region { return information.region; } auto _DD() const -> bool { return information.dd; } auto frequency() const -> u32 { return information.frequency; } @@ -27,6 +29,7 @@ struct System { private: struct Information { string name = "Nintendo 64"; + Model model = Model::Nintendo64; Region region = Region::NTSC; u32 frequency = 93'750'000 * 2; u32 videoFrequency = 48'681'818; @@ -41,6 +44,8 @@ struct System { extern System system; +auto Model::Nintendo64() -> bool { return system.model() == System::Model::Nintendo64; } +auto Model::Aleck64() -> bool { return system.model() == System::Model::Aleck64; } auto Region::NTSC() -> bool { return system.region() == System::Region::NTSC; } auto Region::PAL() -> bool { return system.region() == System::Region::PAL; } auto _DD() -> bool { return system._DD(); } diff --git a/ares/n64/vi/vi.cpp b/ares/n64/vi/vi.cpp index cfa612ee2d..ab667a2faf 100644 --- a/ares/n64/vi/vi.cpp +++ b/ares/n64/vi/vi.cpp @@ -144,6 +144,8 @@ auto VI::refresh() -> void { } vulkan.unmapScanoutRead(); vulkan.endScanout(); + + if(Model::Aleck64()) aleck64.vdp.render(screen); //aleck64 supports overlay graphics return; } #endif diff --git a/desktop-ui/emulator/arcade.cpp b/desktop-ui/emulator/arcade.cpp index 1634797aa1..271fab5be3 100644 --- a/desktop-ui/emulator/arcade.cpp +++ b/desktop-ui/emulator/arcade.cpp @@ -5,6 +5,7 @@ struct Arcade : Emulator { auto pak(ares::Node::Object) -> shared_pointer override; auto group() -> string override { return "Arcade"; } auto arcade() -> bool override { return true; } + auto input(ares::Node::Input::Input) -> void override; string systemPakName = "Arcade"; string gamePakName = "Arcade"; }; @@ -14,18 +15,33 @@ Arcade::Arcade() { name = "Arcade"; medium = "Arcade"; + { InputPort port{string{"Arcade"}}; - { InputDevice device{"Controls"}; + { InputDevice device{"Controls"} ; + device.digital("Service", virtualPorts[0].pad.lstick_click); + device.digital("Test", virtualPorts[0].pad.rstick_click); for(auto n : range(2)) { device.digital({"Player ", n + 1, " Up" }, virtualPorts[n].pad.up); device.digital({"Player ", n + 1, " Down" }, virtualPorts[n].pad.down); device.digital({"Player ", n + 1, " Left" }, virtualPorts[n].pad.left); device.digital({"Player ", n + 1, " Right" }, virtualPorts[n].pad.right); - device.digital({"Player ", n + 1, " Button 1"}, virtualPorts[n].pad.south); - device.digital({"Player ", n + 1, " Button 2"}, virtualPorts[n].pad.east); + device.digital({"Player ", n + 1, " Button 1"}, virtualPorts[n].pad.west); + device.digital({"Player ", n + 1, " Button 2"}, virtualPorts[n].pad.south); + device.digital({"Player ", n + 1, " Button 3"}, virtualPorts[n].pad.east); + device.digital({"Player ", n + 1, " Button 4"}, virtualPorts[n].pad.north); + device.digital({"Player ", n + 1, " Button 5"}, virtualPorts[n].pad.l_bumper); + device.digital({"Player ", n + 1, " Button 6"}, virtualPorts[n].pad.r_bumper); + device.digital({"Player ", n + 1, " Button 7"}, virtualPorts[n].pad.l_trigger); + device.digital({"Player ", n + 1, " Button 8"}, virtualPorts[n].pad.r_trigger); device.digital({"Player ", n + 1, " Start" }, virtualPorts[n].pad.start); device.digital({"Player ", n + 1, " Coin" }, virtualPorts[n].pad.select); + device.analog ({"Player ", n + 1, " Y-Axis -"}, virtualPorts[n].pad.lstick_up); + device.analog ({"Player ", n + 1, " Y-Axis +"}, virtualPorts[n].pad.lstick_down); + device.analog ({"Player ", n + 1, " X-Axis -"}, virtualPorts[n].pad.lstick_left); + device.analog ({"Player ", n + 1, " X-Axis +"}, virtualPorts[n].pad.lstick_right); + device.analog ({"Player ", n + 1, " X-Axis" }, virtualPorts[n].pad.lstick_left, virtualPorts[n].pad.lstick_right); + device.analog ({"Player ", n + 1, " Y-Axis" }, virtualPorts[n].pad.lstick_up, virtualPorts[n].pad.lstick_down); } port.append(device); } @@ -34,6 +50,7 @@ Arcade::Arcade() { } auto Arcade::load() -> LoadResult { + systemPakName = "Arcade"; game = mia::Medium::create("Arcade"); string location = Emulator::load(game, configuration.game); if(!location) return noFileSelected; @@ -59,6 +76,45 @@ auto Arcade::load() -> LoadResult { } #endif +#ifdef CORE_N64 + if(game->pak->attribute("board") == "nintendo/aleck64") { + if(!ares::Nintendo64::load(root, {"[SETA] Aleck 64"})) { + return otherError; + } + systemPakName = "Aleck 64"; + gamePakName = "Arcade Cartridge"; + + if(auto port = root->find("Cartridge Slot")) { + port->allocate(); + port->connect(); + } + + if(auto port = root->find("Controller Port 1")) { + port->allocate("Aleck64"); + port->connect(); + } + + if(auto port = root->find("Controller Port 2")) { + port->allocate("Aleck64"); + port->connect(); + } + + ares::Nintendo64::option("Quality", settings.video.quality); + ares::Nintendo64::option("Supersampling", settings.video.supersampling); +#if defined(VULKAN) + ares::Nintendo64::option("Enable GPU acceleration", true); +#else + ares::Nintendo64::option("Enable GPU acceleration", false); +#endif + ares::Nintendo64::option("Disable Video Interface Processing", settings.video.disableVideoInterfaceProcessing); + ares::Nintendo64::option("Weave Deinterlacing", settings.video.weaveDeinterlacing); + ares::Nintendo64::option("Homebrew Mode", settings.general.homebrewMode); + ares::Nintendo64::option("Recompiler", !settings.general.forceInterpreter); + + return successful; + } +#endif + return otherError; } @@ -74,3 +130,37 @@ auto Arcade::pak(ares::Node::Object node) -> shared_pointer { if(node->name() == gamePakName) return game->pak; return {}; } + +auto Arcade::input(ares::Node::Input::Input input) -> void { + if(input->name().beginsWith("Mahjong")) { + auto button = input->cast(); + if(input->name() == "Mahjong A") return button->setValue(inputKeyboard("A")); + if(input->name() == "Mahjong B") return button->setValue(inputKeyboard("B")); + if(input->name() == "Mahjong C") return button->setValue(inputKeyboard("C")); + if(input->name() == "Mahjong D") return button->setValue(inputKeyboard("D")); + if(input->name() == "Mahjong E") return button->setValue(inputKeyboard("E")); + if(input->name() == "Mahjong F") return button->setValue(inputKeyboard("F")); + if(input->name() == "Mahjong G") return button->setValue(inputKeyboard("G")); + if(input->name() == "Mahjong H") return button->setValue(inputKeyboard("H")); + if(input->name() == "Mahjong I") return button->setValue(inputKeyboard("I")); + if(input->name() == "Mahjong J") return button->setValue(inputKeyboard("J")); + if(input->name() == "Mahjong K") return button->setValue(inputKeyboard("K")); + if(input->name() == "Mahjong L") return button->setValue(inputKeyboard("L")); + if(input->name() == "Mahjong M") return button->setValue(inputKeyboard("M")); + if(input->name() == "Mahjong N") return button->setValue(inputKeyboard("N")); + if(input->name() == "Mahjong カン") return button->setValue(inputKeyboard("T")); + if(input->name() == "Mahjong ポン") return button->setValue(inputKeyboard("Y")); + if(input->name() == "Mahjong チー") return button->setValue(inputKeyboard("U")); + if(input->name() == "Mahjong リーチ") return button->setValue(inputKeyboard("O")); + if(input->name() == "Mahjong ロン") return button->setValue(inputKeyboard("P")); + } + + auto device = ares::Node::parent(input); + if(!device) return; + + auto port = ares::Node::parent(device); + if(!port) return; + + return Emulator::input(input); + +} diff --git a/desktop-ui/game-browser/game-browser.cpp b/desktop-ui/game-browser/game-browser.cpp index c61a789121..be2ef5528b 100644 --- a/desktop-ui/game-browser/game-browser.cpp +++ b/desktop-ui/game-browser/game-browser.cpp @@ -31,12 +31,14 @@ auto GameBrowserWindow::show(shared_pointer emulator) -> void { auto db = tmp->database(); for(auto node : db.list) { + if(node["type"].string() != "game") continue; auto path = settings.paths.arcadeRoms; if(!path) path = {mia::homeLocation(), "Arcade"}; + path = {path, "/", node["name"].string(), ".zip"}; if(inode::exists(path)) { - games.append({node["title"].string(), node["name"].string(), path}); + games.append({node["title"].string(), node["name"].string(), node["board"].string(), path}); } } @@ -50,11 +52,13 @@ auto GameBrowserWindow::show(shared_pointer emulator) -> void { gameList.reset(); gameList.append(TableViewColumn().setText("Game Title").setExpandable()); + gameList.append(TableViewColumn().setText("Board").setExpandable()); gameList.append(TableViewColumn().setText("MAME Name")); for(auto& game : games) { TableViewItem item{&gameList}; item.append(TableViewCell().setText(game.title)); + item.append(TableViewCell().setText(game.board)); item.append(TableViewCell().setText(game.name)); } diff --git a/desktop-ui/game-browser/game-browser.hpp b/desktop-ui/game-browser/game-browser.hpp index c0fe5865ed..7972b98479 100644 --- a/desktop-ui/game-browser/game-browser.hpp +++ b/desktop-ui/game-browser/game-browser.hpp @@ -1,6 +1,7 @@ struct GameBrowserEntry { string title; string name; + string board; string path; }; diff --git a/desktop-ui/presentation/presentation.cpp b/desktop-ui/presentation/presentation.cpp index 637acbbdc5..524189a18b 100644 --- a/desktop-ui/presentation/presentation.cpp +++ b/desktop-ui/presentation/presentation.cpp @@ -489,10 +489,10 @@ auto Presentation::refreshSystemMenu() -> void { for(auto dip : ares::Node::enumerate(emulator->root)) { MenuCheckItem item{&dipSwitchMenu}; item.setText(dip->name()); - item.setAttribute("dip", dip); + item.setAttribute("dip", dip); item.setChecked(dip->value()); item.onToggle([=] { - auto dip = item.attribute("dip"); + auto dip = item.attribute("dip"); dip->setValue(item.checked()); }); } @@ -508,8 +508,8 @@ auto Presentation::refreshSystemMenu() -> void { group.append(optionItem); if(dip->value() == option) optionItem.setChecked(); optionItem.onActivate([=] { - auto dip = item.attribute("dip"); - dip->setValue(optionItem.text()); + auto dip = item.attribute("dip"); + dip->setValue(optionItem.text()); }); } } diff --git a/mia/Database/Arcade.bml b/mia/Database/Arcade.bml index 69ec8efb94..516a38c9e4 100644 --- a/mia/Database/Arcade.bml +++ b/mia/Database/Arcade.bml @@ -1,10 +1,72 @@ database - revision: 2024-04-03 - romset: 0.258 (mame0258) + revision: 2025-01-10 + drivers: sega/sg1000a.cpp, nintendo/aleck64.cpp, + romset: 0.273 (mame0273) +game + name: 11beat + title: Eleven Beat + board: nintendo/aleck64 + type: game + parent: aleck64 + user1 + rom + name: pifdata.bin + offset: 0x0 + size: 2048 + crc: 5ec82be9 + sha1: 9174eadc0f0ea2654c95fd941406ab46b9dc9bdd + user2 + rom + name: nus-zhaj.u3 + offset: 0x0 + size: 8388608 + crc: 02faa8a7 + sha1: 824911452639cedf6a8186c05cd046e61fc98896 + normpoint + rom + name: normpnt.rom + offset: 0x0 + size: 128 + crc: e7f2a005 + sha1: c27b4a364a24daeee6e99fd286753fd6216362b4 + normslope + rom + name: normslp.rom + offset: 0x0 + size: 128 + crc: 4f2ae525 + sha1: eab43f8cc52c8551d9cff6fced18ef80eaba6f05 +game + name: aleck64 + title: Aleck64 PIF BIOS + board: nintendo/aleck64 + type: bios + user1 + rom + name: pifdata.bin + offset: 0x0 + size: 2048 + crc: 5ec82be9 + sha1: 9174eadc0f0ea2654c95fd941406ab46b9dc9bdd + normpoint + rom + name: normpnt.rom + offset: 0x0 + size: 128 + crc: e7f2a005 + sha1: c27b4a364a24daeee6e99fd286753fd6216362b4 + normslope + rom + name: normslp.rom + offset: 0x0 + size: 128 + crc: 4f2ae525 + sha1: eab43f8cc52c8551d9cff6fced18ef80eaba6f05 game name: chboxing title: Champion Boxing board: sega/sg1000a + type: game maincpu rom name: cb6105.bin @@ -28,6 +90,7 @@ game name: chwrestl title: Champion Pro Wrestling board: sega/sg1000a + type: game maincpu rom name: 5732 @@ -51,6 +114,7 @@ game name: dokidoki title: Doki Doki Penguin Land board: sega/sg1000a + type: game maincpu rom name: epr-7356.ic1 @@ -70,10 +134,281 @@ game size: 16384 crc: c6f26b0b sha1: 3753e05b6e77159832dbe88562ba7a818120d1a3 +game + name: doncdoon + title: Hanabi de Doon! - Don-chan Puzzle + board: nintendo/aleck64 + type: game + parent: aleck64 + user1 + rom + name: pifdata.bin + offset: 0x0 + size: 2048 + crc: 5ec82be9 + sha1: 9174eadc0f0ea2654c95fd941406ab46b9dc9bdd + user2 + rom + name: ua3003-all01.u3 + offset: 0x0 + size: 16777216 + crc: f362fa82 + sha1: 4f41ee23edc18110be1218ba333d1c58376ab175 + rom + name: ua3003-alh01.u4 + offset: 0x1000000 + size: 16777216 + crc: 47c56387 + sha1: c8cc6c0a456b593aef711d0a75b2342ba2f8203f + user3 + rom + name: nus-zsij-0.u1 + offset: 0x0 + size: 8388608 + crc: 547d8122 + sha1: 347f0785767265acb0f0c21646e06cbe6f561821 + normpoint + rom + name: normpnt.rom + offset: 0x0 + size: 128 + crc: e7f2a005 + sha1: c27b4a364a24daeee6e99fd286753fd6216362b4 + normslope + rom + name: normslp.rom + offset: 0x0 + size: 128 + crc: 4f2ae525 + sha1: eab43f8cc52c8551d9cff6fced18ef80eaba6f05 +game + name: hipai + title: Hi Pai Paradise + board: nintendo/aleck64 + type: game + parent: aleck64 + user1 + rom + name: pifdata.bin + offset: 0x0 + size: 2048 + crc: 5ec82be9 + sha1: 9174eadc0f0ea2654c95fd941406ab46b9dc9bdd + user2 + rom + name: ua2011-all02.u3 + offset: 0x0 + size: 16777216 + crc: eb4b96d0 + sha1: e909ea5b71b81087da07821c4f57244576363678 + rom + name: ua2011-alh02.u4 + offset: 0x1000000 + size: 16777216 + crc: b8e35ddf + sha1: 7c3e59f6520dc3f0aa592e682fa82e30ffd1f4d0 + user3 + rom + name: nus-nsij-0.u1 + offset: 0x0 + size: 8388608 + crc: 94cf9f8d + sha1: cd624d1f5de2be3bec3ece06556a2e39bef66d77 + normpoint + rom + name: normpnt.rom + offset: 0x0 + size: 128 + crc: e7f2a005 + sha1: c27b4a364a24daeee6e99fd286753fd6216362b4 + normslope + rom + name: normslp.rom + offset: 0x0 + size: 128 + crc: 4f2ae525 + sha1: eab43f8cc52c8551d9cff6fced18ef80eaba6f05 +game + name: hipai2 + title: Hi Pai Paradise 2 + board: nintendo/aleck64 + type: game + parent: aleck64 + user1 + rom + name: pifdata.bin + offset: 0x0 + size: 2048 + crc: 5ec82be9 + sha1: 9174eadc0f0ea2654c95fd941406ab46b9dc9bdd + user2 + rom + name: ua3029-all01.u3 + offset: 0x0 + size: 16777216 + crc: 88b83e93 + sha1: 279a006436069232383b2065e7416d8ccc76e3ca + rom + name: ua3029-alh01.u4 + offset: 0x1000000 + size: 16777216 + crc: 4f79fa94 + sha1: a6d2d805f96f0ad7294ebf41b13573320154b2a4 + user3 + rom + name: nus-zsij-0.u1 + offset: 0x0 + size: 8388608 + crc: 2389576f + sha1: dc22b2eab4d7a02cb918827a62e6c120b3a84e6c + normpoint + rom + name: normpnt.rom + offset: 0x0 + size: 128 + crc: e7f2a005 + sha1: c27b4a364a24daeee6e99fd286753fd6216362b4 + normslope + rom + name: normslp.rom + offset: 0x0 + size: 128 + crc: 4f2ae525 + sha1: eab43f8cc52c8551d9cff6fced18ef80eaba6f05 +game + name: kurufev + title: Kurukuru Fever + board: nintendo/aleck64 + type: game + parent: aleck64 + user1 + rom + name: pifdata.bin + offset: 0x0 + size: 2048 + crc: 5ec82be9 + sha1: 9174eadc0f0ea2654c95fd941406ab46b9dc9bdd + user2 + rom + name: ua3088-all01.u3 + offset: 0x0 + size: 16777216 + crc: 00db4dbc + sha1: 824fdce01fffdfcbcc9b1fbda4ab389a10b2b418 + rom + name: ua3088-alh04.u4 + offset: 0x1000000 + size: 16777216 + crc: c96bc7c0 + sha1: 2b6ca1a769dee74e112c2b287dacd0bf46dda091 + user3 + rom + name: nus-zsij-0.u1 + offset: 0x0 + size: 8388608 + crc: 2389576f + sha1: dc22b2eab4d7a02cb918827a62e6c120b3a84e6c + normpoint + rom + name: normpnt.rom + offset: 0x0 + size: 128 + crc: e7f2a005 + sha1: c27b4a364a24daeee6e99fd286753fd6216362b4 + normslope + rom + name: normslp.rom + offset: 0x0 + size: 128 + crc: 4f2ae525 + sha1: eab43f8cc52c8551d9cff6fced18ef80eaba6f05 +game + name: mayjin3 + title: Mayjinsen 3 + board: nintendo/aleck64 + type: game + parent: aleck64 + user1 + rom + name: pifdata.bin + offset: 0x0 + size: 2048 + crc: 5ec82be9 + sha1: 9174eadc0f0ea2654c95fd941406ab46b9dc9bdd + user2 + rom + name: nus-zscj-0.u3 + offset: 0x0 + size: 8388608 + crc: 52a37340 + sha1: b5834bfde5b8a7e20415b2593abd76ec95ab27c7 + normpoint + rom + name: normpnt.rom + offset: 0x0 + size: 128 + crc: e7f2a005 + sha1: c27b4a364a24daeee6e99fd286753fd6216362b4 + normslope + rom + name: normslp.rom + offset: 0x0 + size: 128 + crc: 4f2ae525 + sha1: eab43f8cc52c8551d9cff6fced18ef80eaba6f05 +game + name: mtetrisc + title: Magical Tetris Challenge (981009 Japan) + board: nintendo/aleck64 + type: game + parent: aleck64 + user1 + rom + name: pifdata.bin + offset: 0x0 + size: 2048 + crc: 5ec82be9 + sha1: 9174eadc0f0ea2654c95fd941406ab46b9dc9bdd + user2 + rom + name: nus-zcaj.u4 + offset: 0x0 + size: 16777216 + crc: c9de64db + sha1: 59932c70b43ff8e9264c670f37b3abbe939b7f95 + user3 + rom + name: tet-01m.u5 + offset: 0x0 + size: 1048576 + crc: f78f859b + sha1: b07c85e0453869fe43792f42081f64a5327e58e6 + user4 + rom + name: at24c01.u34 + offset: 0x0 + size: 128 + crc: ba7e503f + sha1: 454aa4fdde7d8694d1affaf25cd750fa678686bb + normpoint + rom + name: normpnt.rom + offset: 0x0 + size: 128 + crc: e7f2a005 + sha1: c27b4a364a24daeee6e99fd286753fd6216362b4 + normslope + rom + name: normslp.rom + offset: 0x0 + size: 128 + crc: 4f2ae525 + sha1: eab43f8cc52c8551d9cff6fced18ef80eaba6f05 game name: sderby2s title: Super Derby II (satellite board) board: sega/sg1000a + type: game maincpu rom name: epr-6450d.ic10 @@ -91,6 +426,7 @@ game name: sderbys title: Super Derby (satellite board) board: sega/sg1000a + type: game maincpu rom name: v1.2.ic10 @@ -98,3 +434,180 @@ game size: 16384 crc: cf29b579 sha1: e695da9c61167d1d30b32bd70d342ac23b29f087 +game + name: srmvs + title: Super Real Mahjong VS (Rev A) + board: nintendo/aleck64 + type: game + parent: aleck64 + user1 + rom + name: pifdata.bin + offset: 0x0 + size: 2048 + crc: 5ec82be9 + sha1: 9174eadc0f0ea2654c95fd941406ab46b9dc9bdd + user2 + rom + name: nus-zsej-1.u2 + offset: 0x0 + size: 33554432 + crc: dd7dd81f + sha1: ff020fa957a11bf0f18819d16a010f51b4a4cda4 + normpoint + rom + name: normpnt.rom + offset: 0x0 + size: 128 + crc: e7f2a005 + sha1: c27b4a364a24daeee6e99fd286753fd6216362b4 + normslope + rom + name: normslp.rom + offset: 0x0 + size: 128 + crc: 4f2ae525 + sha1: eab43f8cc52c8551d9cff6fced18ef80eaba6f05 +game + name: srmvsa + title: Super Real Mahjong VS + board: nintendo/aleck64 + type: game + parent: srmvs + user1 + rom + name: pifdata.bin + offset: 0x0 + size: 2048 + crc: 5ec82be9 + sha1: 9174eadc0f0ea2654c95fd941406ab46b9dc9bdd + user2 + rom + name: nus-zsej-0.u2 + offset: 0x0 + size: 33554432 + crc: 44f40102 + sha1: a78de955f2fcd99dda14e782984368b320eb5415 + normpoint + rom + name: normpnt.rom + offset: 0x0 + size: 128 + crc: e7f2a005 + sha1: c27b4a364a24daeee6e99fd286753fd6216362b4 + normslope + rom + name: normslp.rom + offset: 0x0 + size: 128 + crc: 4f2ae525 + sha1: eab43f8cc52c8551d9cff6fced18ef80eaba6f05 +game + name: starsldr + title: Star Soldier: Vanishing Earth + board: nintendo/aleck64 + type: game + parent: aleck64 + user1 + rom + name: pifdata.bin + offset: 0x0 + size: 2048 + crc: 5ec82be9 + sha1: 9174eadc0f0ea2654c95fd941406ab46b9dc9bdd + user2 + rom + name: nus-zhbj-0.u3 + offset: 0x0 + size: 12582912 + crc: a4edac93 + sha1: 3794606c008fb69f5d16dcccece94d03da23bf8a + normpoint + rom + name: normpnt.rom + offset: 0x0 + size: 128 + crc: e7f2a005 + sha1: c27b4a364a24daeee6e99fd286753fd6216362b4 + normslope + rom + name: normslp.rom + offset: 0x0 + size: 128 + crc: 4f2ae525 + sha1: eab43f8cc52c8551d9cff6fced18ef80eaba6f05 +game + name: twrshaft + title: Tower & Shaft + board: nintendo/aleck64 + type: game + parent: aleck64 + user1 + rom + name: pifdata.bin + offset: 0x0 + size: 2048 + crc: 5ec82be9 + sha1: 9174eadc0f0ea2654c95fd941406ab46b9dc9bdd + user2 + rom + name: ua3012-all02.u3 + offset: 0x0 + size: 16777216 + crc: 904a91a7 + sha1: 7dfa3447d2c489c0448c4004dc12d3037c05a0f3 + user3 + rom + name: nus-zsij-0.u1 + offset: 0x0 + size: 8388608 + crc: 2389576f + sha1: dc22b2eab4d7a02cb918827a62e6c120b3a84e6c + normpoint + rom + name: normpnt.rom + offset: 0x0 + size: 128 + crc: e7f2a005 + sha1: c27b4a364a24daeee6e99fd286753fd6216362b4 + normslope + rom + name: normslp.rom + offset: 0x0 + size: 128 + crc: 4f2ae525 + sha1: eab43f8cc52c8551d9cff6fced18ef80eaba6f05 +game + name: vivdolls + title: Vivid Dolls + board: nintendo/aleck64 + type: game + parent: aleck64 + user1 + rom + name: pifdata.bin + offset: 0x0 + size: 2048 + crc: 5ec82be9 + sha1: 9174eadc0f0ea2654c95fd941406ab46b9dc9bdd + user2 + rom + name: nus-zsaj-0.u3 + offset: 0x0 + size: 8388608 + crc: f3220e29 + sha1: 06d8b808cc19378b046803f4dc75c7d791b7767f + normpoint + rom + name: normpnt.rom + offset: 0x0 + size: 128 + crc: e7f2a005 + sha1: c27b4a364a24daeee6e99fd286753fd6216362b4 + normslope + rom + name: normslp.rom + offset: 0x0 + size: 128 + crc: 4f2ae525 + sha1: eab43f8cc52c8551d9cff6fced18ef80eaba6f05 diff --git a/mia/medium/arcade.cpp b/mia/medium/arcade.cpp index a2d3e61928..4a7872e93b 100644 --- a/mia/medium/arcade.cpp +++ b/mia/medium/arcade.cpp @@ -23,6 +23,7 @@ auto Arcade::load(string location) -> LoadResult { pak = new vfs::directory; pak->setAttribute("board", document["game/board" ].string()); + pak->setAttribute("name", document["game/name" ].string()); pak->setAttribute("title", document["game/title"].string()); pak->setAttribute("region", document["game/region"].string()); pak->append("manifest.bml", manifest); @@ -31,6 +32,32 @@ auto Arcade::load(string location) -> LoadResult { return successful; } + //Aleck 64 + if(document["game/board"].string() == "nintendo/aleck64") { + vector rom = loadRoms(location, document, "user2"); + if(!rom) return { invalidROM, "Ensure your ROM is in a MAME-compatible .zip format." }; + + //MAME stores roms in Byte-Swapped (v64) format, but we need them in their native Big-Endian (z64) + endianSwap(rom); + + vector pif = loadRoms(location, document, "user1"); + if(!rom) return { invalidROM, "Ensure your ROM is in a MAME-compatible .zip format." }; + + this->location = location; + + pak = new vfs::directory; + pak->setAttribute("board", document["game/board" ].string()); + pak->setAttribute("name", document["game/name" ].string()); + pak->setAttribute("title", document["game/title"].string()); + pak->setAttribute("region", document["game/region"].string()); + pak->setAttribute("cic", "CIC-NUS-5101"); + pak->append("manifest.bml", manifest); + pak->append("program.rom", rom); + pak->append("pif.aleck64.rom", pif); + + return successful; + } + return otherError; } diff --git a/mia/medium/mame.cpp b/mia/medium/mame.cpp index d70ecd07d3..e656ed0304 100644 --- a/mia/medium/mame.cpp +++ b/mia/medium/mame.cpp @@ -1,6 +1,7 @@ // Helper/Utility for importing roms for systems that are using MAME romsets struct Mame : Medium { auto loadRoms(string location, Markup::Node& info, string sectionName) -> vector; + auto loadRomFile(string location, string filename, Markup::Node& currentInfo) -> vector; auto endianSwap(vector& memory, u32 address = 0, int size = -1) -> void; }; @@ -23,11 +24,8 @@ auto Mame::loadRoms(string location, Markup::Node& info, string sectionName) -> filename = section["name"].string().strip(); if(filename) { input = {}; - for(auto &file: archive.file) { - if(!file.name.iequals(filename)) continue; - input = archive.extract(file); - readOffset = 0; - } + input = loadRomFile(location, filename, info); + readOffset = 0; if (!input || input.size() == 0) return output; } } @@ -63,6 +61,27 @@ auto Mame::loadRoms(string location, Markup::Node& info, string sectionName) -> return output; } +auto Mame::loadRomFile(string location, string filename, Markup::Node& info) -> vector { + Decode::ZIP archive; + if(!archive.open(location)) return {}; + + for(auto& file : archive.file) { + if(file.name.iequals(filename)) { + return archive.extract(file); + } + } + + if(auto parent = info["game/parent"].string()) { + auto manifest = manifestDatabaseArcade(parent); + auto document = BML::unserialize(manifest); + if(!document) return {}; + location = {Location::path(location), "/", document["game/name"].string(), ".zip"}; + return loadRomFile(location, filename, document); + } + + return {}; +}; + auto Mame::endianSwap(vector& memory, u32 address, int size) -> void { if(size < 0) size = memory.size(); diff --git a/mia/medium/nintendo-64.cpp b/mia/medium/nintendo-64.cpp index d1875d1430..eb3f284d3b 100644 --- a/mia/medium/nintendo-64.cpp +++ b/mia/medium/nintendo-64.cpp @@ -233,6 +233,9 @@ auto Nintendo64::analyze(vector& data) -> string { if (!cic) switch (ipl2checksum(0x85, ipl3)) { case 0x2bbad4e6eb74ull: cic = ntsc ? "CIC-NUS-6106" : "CIC-NUS-7106"; break; } + if (!cic) switch (ipl2checksum(0xac, ipl3)) { + case 0x93e983a8f152ull: cic = "CIC-NUS-5101"; break; //Aleck64 (and conversion hacks) + } if (!cic) switch (ipl2checksum(0xdd, ipl3)) { case 0x32b294e2ab90ull: cic = "CIC-NUS-8303"; break; //64DD Retail IPL (Japanese) case 0x6ee8d9e84970ull: cic = "CIC-NUS-8401"; break; //64DD Development IPL (Japanese) diff --git a/scripts/update-arcade-rom-db.sh b/scripts/update-arcade-rom-db.sh index 1b30024074..88003b3630 100644 --- a/scripts/update-arcade-rom-db.sh +++ b/scripts/update-arcade-rom-db.sh @@ -5,7 +5,7 @@ # mame2bml is also required to be present and up-to-date. # Specify the mame drivers to include in the database -export CORES="sega/sg1000a.cpp" +export CORES="sega/sg1000a.cpp nintendo/aleck64.cpp" export PATH=$PATH:$(pwd)/tools/mame2bml/out if ! command -v mame2bml &> /dev/null diff --git a/tools/mame2bml/mame2bml.cpp b/tools/mame2bml/mame2bml.cpp index fedebf6d1b..f3881d461f 100644 --- a/tools/mame2bml/mame2bml.cpp +++ b/tools/mame2bml/mame2bml.cpp @@ -35,6 +35,11 @@ auto Mame2BML::main(Arguments arguments) -> void { output.print("database\n"); output.print(" revision: ", chrono::local::date(), "\n"); + output.print(" drivers: "); + for(auto driver : driverNames) { + output.print(driver, ", "); + } + output.print("\n"); pathname = Location::path(markupName); auto document = XML::unserialize(markup); @@ -48,15 +53,19 @@ auto Mame2BML::main(Arguments arguments) -> void { if(machine.name() != "machine") continue; string driverName = machine["sourcefile"].string(); if(!driverNames.find(driverName)) continue; + string type = "game"; string IsBIOS = machine["isbios"].string(); - if(IsBIOS == "yes") continue; + string parent = machine["romof"].string(); + if(IsBIOS == "yes") type = "bios"; - print("found game: ", machine["name"].string(), " (", machine["description"].string(), ")\n"); + print("found ", type, ": ", machine["name"].string(), " (", machine["description"].string(), ")\n"); output.print("game\n"); output.print(" name: ", machine["name"].string(), "\n"); output.print(" title: ", machine["description"].string(), "\n"); output.print(" board: ", driverName.trimRight(".cpp"), "\n"); + output.print(" type: ", type, "\n"); + if(parent) output.print(" parent: ", parent, "\n"); string region = ""; for(auto rom : machine) {