Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preliminary support for Aleck64 #1758

Merged
merged 24 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
03bf626
mame2bml: print list of drivers used to generate bml
LukeUsher Jan 6, 2025
99fc864
arcade: show machine type in game browser
LukeUsher Jan 7, 2025
66759f6
arcade: import aleck64 romlist
LukeUsher Jan 7, 2025
bf0e8ce
mia: add support for aleck64 arcade roms
LukeUsher Jan 28, 2025
8fb9eee
arcade: update database
LukeUsher Jan 8, 2025
2ce2693
n64: preliminary support for aleck64 arcade system
LukeUsher Jan 8, 2025
83b2eb0
cleanup/debug print removal
LukeUsher Jan 8, 2025
5472723
aleck64: add support for Eleven Beat
LukeUsher Jan 8, 2025
a750e03
aleck64: fix unity builds
LukeUsher Jan 8, 2025
3e98b38
aleck64: fix legacy build
LukeUsher Jan 8, 2025
af27b7c
aleck64: set n64 core options correctly
LukeUsher Jan 8, 2025
394ba63
aleck64: add configs for all games that don't need special hardware
LukeUsher Jan 9, 2025
d6066e9
arcade: fix a crash when running multiple arcade games in one session
LukeUsher Jan 9, 2025
74fff88
arcade: resolve parent relationships when loading arcade roms
LukeUsher Jan 10, 2025
10408dd
mame2bml: only write a parent attribute if the game has a parent
LukeUsher Jan 10, 2025
281370e
aleck64: update CMakeLists.txt
LukeUsher Jan 10, 2025
a6d15a2
aleck64: mahjong input support (fixes the two Hi Pai games)
LukeUsher Jan 10, 2025
9c0675c
Apply suggestions from code review
LukeUsher Jan 11, 2025
d0bd56d
aleck64: fix build after merging changes
LukeUsher Jan 11, 2025
d1568e7
aleck64: swap prints for debug()
LukeUsher Jan 11, 2025
e80bc16
aleck64: alignment/formatting
LukeUsher Jan 11, 2025
82a805c
aleck64: reduce duplication, partial support for mtetrisc
LukeUsher Jan 16, 2025
11f73fa
aleck64: add serialization
LukeUsher Jan 19, 2025
a2a72e3
fix build after rebase
LukeUsher Jan 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions ares/n64/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions ares/n64/GNUmakefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
73 changes: 73 additions & 0 deletions ares/n64/aleck64/aleck64.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#include <n64/n64.hpp>

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<Node::Object>("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();
}
}
}
241 changes: 241 additions & 0 deletions ares/n64/aleck64/aleck64.hpp
Original file line number Diff line number Diff line change
@@ -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<u32 Size>
auto writeBurst(u32 address, u32 *value, const char *peripheral) -> void {
address = address & 0x00ff'ffff;
if(address >= size) return;
Memory::Writable::write<Word>(address | 0x00, value[0]);
Memory::Writable::write<Word>(address | 0x04, value[1]);
Memory::Writable::write<Word>(address | 0x08, value[2]);
Memory::Writable::write<Word>(address | 0x0c, value[3]);
if(Size == ICache) {
Memory::Writable::write<Word>(address | 0x10, value[4]);
Memory::Writable::write<Word>(address | 0x14, value[5]);
Memory::Writable::write<Word>(address | 0x18, value[6]);
Memory::Writable::write<Word>(address | 0x1c, value[7]);
}
}

template<u32 Size>
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<Word>(address | 0x00);
value[1] = Memory::Writable::read<Word>(address | 0x04);
value[2] = Memory::Writable::read<Word>(address | 0x08);
value[3] = Memory::Writable::read<Word>(address | 0x0c);
if(Size == ICache) {
value[4] = Memory::Writable::read<Word>(address | 0x10);
value[5] = Memory::Writable::read<Word>(address | 0x14);
value[6] = Memory::Writable::read<Word>(address | 0x18);
value[7] = Memory::Writable::read<Word>(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<u32 Size>
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<u32 Size> auto read(u32 address, Thread& thread) -> u32 {
if(address <= 0xc07f'ffff) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we maybe ignore reads from 0x8000'0000 to 0xc000'000?

return sdram.read<Size>(address & 0x00ff'ffff);
}

controls.poll();
if(address <= 0xc080'0fff) {
n32 value = 0xffff'ffff;

switch (address & 0xffff'fffc) {
case 0xc080'0000: return formatRead<Size>(address, readPort1());
case 0xc080'0004: return formatRead<Size>(address, readPort2());
case 0xc080'0008: return formatRead<Size>(address, readPort3());
case 0xc080'0100: return formatRead<Size>(address, readPort4());
}
}

if(gameConfig->type() == BoardType::E90) {
if(address >= 0xd000'0000 && address <= 0xd000'0fff) return vram.read<Size>(address & 0x0000'0fff);
if(address >= 0xd001'0000 && address <= 0xd001'0fff) return pram.read<Size>(address & 0x0000'0fff);
if(address >= 0xd003'0000 && address <= 0xd003'001f) return formatRead<Size>(address, vdp.readWord(address & 0x0000'001f));
} else if(gameConfig->type() == BoardType::E92) {
if(address >= 0xd080'0000 && address <= 0xd080'0fff) return vram.read<Size>(address & 0x0000'0fff);
if(address >= 0xd080'1000 && address <= 0xd080'1fff) return pram.read<Size>(address & 0x0000'0fff);
if(address >= 0xd080'2000 && address <= 0xd080'201f) return formatRead<Size>(address, vdp.readWord(address & 0x0000'001f));
}

debug(unusual, "[Aleck64::read] Unmapped address: 0x", hex(address, 8L));
return 0xffffffff;
}

template<u32 Size> auto write(u32 address, u32 data, Thread& thread) -> void {
if(address <= 0xc07f'ffff) {
return sdram.write<Size>(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<Size>(address & 0x0000'0fff, data);
if(address >= 0xd001'0000 && address <= 0xd001'0fff) return pram.write<Size>(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<Size>(address & 0x0000'0fff, data);
if(address >= 0xd080'1000 && address <= 0xd080'1fff) return pram.write<Size>(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> gameConfig;
};

extern Aleck64 aleck64;
Loading
Loading