Run clangformat everywhere
This commit is contained in:
@@ -1,31 +1,37 @@
|
||||
#pragma once
|
||||
#include <common.hpp>
|
||||
#include <ctime>
|
||||
#include <discord_rpc.h>
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
|
||||
namespace Util {
|
||||
enum State {
|
||||
Idling,
|
||||
Playing,
|
||||
Paused
|
||||
MovieReplay,
|
||||
Paused,
|
||||
};
|
||||
|
||||
FORCE_INLINE void UpdateRPC(State state, const std::string& game = "") {
|
||||
FORCE_INLINE void UpdateRPC(State state, const std::string &game = "", const std::string &movieName = "") {
|
||||
DiscordRichPresence presence{};
|
||||
std::string textState, textDetails;
|
||||
|
||||
switch(state) {
|
||||
case Idling:
|
||||
textDetails = "Idling";
|
||||
break;
|
||||
case Playing:
|
||||
textDetails = "In-game";
|
||||
textState = "Playing \"" + game + "\"";
|
||||
break;
|
||||
case Paused:
|
||||
textDetails = "In-game";
|
||||
textState = "Playing \"" + game + "\" (Paused)";
|
||||
break;
|
||||
switch (state) {
|
||||
case Idling:
|
||||
textDetails = "Idling";
|
||||
break;
|
||||
case Playing:
|
||||
textDetails = "In-game";
|
||||
textState = "Playing \"" + game + "\"";
|
||||
break;
|
||||
case MovieReplay:
|
||||
textDetails = "In-game";
|
||||
textState = "Replaying movie \"" + movieName + "\" in \"" + game + "\"";
|
||||
break;
|
||||
case Paused:
|
||||
textDetails = "In-game";
|
||||
textState = "Playing \"" + game + "\" (Paused)";
|
||||
break;
|
||||
}
|
||||
|
||||
presence.details = textDetails.c_str();
|
||||
@@ -36,7 +42,5 @@ FORCE_INLINE void UpdateRPC(State state, const std::string& game = "") {
|
||||
Discord_UpdatePresence(&presence);
|
||||
}
|
||||
|
||||
FORCE_INLINE void ClearRPC() {
|
||||
Discord_ClearPresence();
|
||||
}
|
||||
}
|
||||
FORCE_INLINE void ClearRPC() { Discord_ClearPresence(); }
|
||||
} // namespace Util
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#include <Core.hpp>
|
||||
#include <Scheduler.hpp>
|
||||
#include <ParallelRDPWrapper.hpp>
|
||||
#include <Scheduler.hpp>
|
||||
|
||||
namespace n64 {
|
||||
Core::Core(ParallelRDP& parallel) : cpu(std::make_unique<JIT>(parallel)) {}
|
||||
Core::Core(ParallelRDP ¶llel) : cpu(std::make_unique<JIT>(parallel)) {}
|
||||
|
||||
void Core::Stop() {
|
||||
render = false;
|
||||
@@ -12,26 +12,23 @@ void Core::Stop() {
|
||||
cpu->Reset();
|
||||
}
|
||||
|
||||
bool Core::LoadTAS(const fs::path &path) const {
|
||||
return cpu->GetMem().mmio.si.pif.movie.Load(path);
|
||||
}
|
||||
bool Core::LoadTAS(const fs::path &path) const { return cpu->GetMem().mmio.si.pif.movie.Load(path); }
|
||||
|
||||
void Core::LoadROM(const std::string& rom_) {
|
||||
void Core::LoadROM(const std::string &rom_) {
|
||||
pause = true;
|
||||
rom = rom_;
|
||||
cpu->Reset();
|
||||
romLoaded = true;
|
||||
|
||||
std::string archive_types[] = {".zip",".7z",".rar",".tar"};
|
||||
std::string archive_types[] = {".zip", ".7z", ".rar", ".tar"};
|
||||
|
||||
auto extension = fs::path(rom).extension().string();
|
||||
bool isArchive = std::any_of(std::begin(archive_types), std::end(archive_types), [&extension](const auto& e) {
|
||||
return e == extension;
|
||||
});
|
||||
bool isArchive = std::any_of(std::begin(archive_types), std::end(archive_types),
|
||||
[&extension](const auto &e) { return e == extension; });
|
||||
|
||||
cpu->GetMem().LoadROM(isArchive, rom);
|
||||
GameDB::match(cpu->GetMem());
|
||||
if(cpu->GetMem().rom.gameNameDB.empty()) {
|
||||
if (cpu->GetMem().rom.gameNameDB.empty()) {
|
||||
cpu->GetMem().rom.gameNameDB = fs::path(rom).stem();
|
||||
}
|
||||
cpu->GetMem().mmio.vi.isPal = cpu->GetMem().IsROMPAL();
|
||||
@@ -46,9 +43,9 @@ void Core::LoadROM(const std::string& rom_) {
|
||||
}
|
||||
|
||||
void Core::Run(float volumeL, float volumeR) {
|
||||
Mem& mem = cpu->GetMem();
|
||||
MMIO& mmio = mem.mmio;
|
||||
Registers& regs = cpu->GetRegs();
|
||||
Mem &mem = cpu->GetMem();
|
||||
MMIO &mmio = mem.mmio;
|
||||
Registers ®s = cpu->GetRegs();
|
||||
|
||||
for (int field = 0; field < mmio.vi.numFields; field++) {
|
||||
u32 frameCycles = 0;
|
||||
@@ -59,21 +56,21 @@ void Core::Run(float volumeL, float volumeR) {
|
||||
mmio.mi.InterruptRaise(MI::Interrupt::VI);
|
||||
}
|
||||
|
||||
for(; cycles < mem.mmio.vi.cyclesPerHalfline; cycles++, frameCycles++) {
|
||||
for (; cycles < mem.mmio.vi.cyclesPerHalfline; cycles++, frameCycles++) {
|
||||
u32 taken = cpu->Step();
|
||||
taken += regs.PopStalledCycles();
|
||||
|
||||
regs.steps += taken;
|
||||
if(mmio.rsp.spStatus.halt) {
|
||||
if (mmio.rsp.spStatus.halt) {
|
||||
regs.steps = 0;
|
||||
mmio.rsp.steps = 0;
|
||||
} else {
|
||||
while(regs.steps > 2) {
|
||||
while (regs.steps > 2) {
|
||||
mmio.rsp.steps += 2;
|
||||
regs.steps -= 3;
|
||||
}
|
||||
|
||||
while(mmio.rsp.steps > 0) {
|
||||
while (mmio.rsp.steps > 0) {
|
||||
mmio.rsp.steps--;
|
||||
mmio.rsp.Step();
|
||||
}
|
||||
@@ -110,14 +107,14 @@ void Core::Serialize() {
|
||||
|
||||
void Core::Deserialize() {
|
||||
std::vector<u8> dVER(serialized[slot].begin(), serialized[slot].begin() + verSize);
|
||||
if(dVER[0] != (KAIZEN_VERSION >> 8)
|
||||
|| dVER[1] != (KAIZEN_VERSION >> 4)
|
||||
|| dVER[2] != (KAIZEN_VERSION & 0xFF)) {
|
||||
if (dVER[0] != (KAIZEN_VERSION >> 8) || dVER[1] != (KAIZEN_VERSION >> 4) || dVER[2] != (KAIZEN_VERSION & 0xFF)) {
|
||||
Util::panic("PROBLEMI!");
|
||||
}
|
||||
|
||||
cpu->GetMem().Deserialize(std::vector<u8>(serialized[slot].begin() + verSize, serialized[slot].begin() + verSize + memSize));
|
||||
cpu->Deserialize(std::vector<u8>(serialized[slot].begin() + verSize + memSize, serialized[slot].begin() + verSize + memSize + cpuSize));
|
||||
cpu->GetMem().Deserialize(
|
||||
std::vector<u8>(serialized[slot].begin() + verSize, serialized[slot].begin() + verSize + memSize));
|
||||
cpu->Deserialize(std::vector<u8>(serialized[slot].begin() + verSize + memSize,
|
||||
serialized[slot].begin() + verSize + memSize + cpuSize));
|
||||
serialized[slot].erase(serialized[slot].begin(), serialized[slot].end());
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -8,15 +8,15 @@ struct Event;
|
||||
|
||||
namespace n64 {
|
||||
struct Core {
|
||||
Core(ParallelRDP&);
|
||||
Core(ParallelRDP &);
|
||||
void Stop();
|
||||
void LoadROM(const std::string&);
|
||||
bool LoadTAS(const fs::path&) const;
|
||||
void LoadROM(const std::string &);
|
||||
bool LoadTAS(const fs::path &) const;
|
||||
void Run(float volumeL, float volumeR);
|
||||
void Serialize();
|
||||
void Deserialize();
|
||||
void TogglePause() { pause = !pause; }
|
||||
[[nodiscard]] VI& GetVI() const { return cpu->GetMem().mmio.vi; }
|
||||
[[nodiscard]] VI &GetVI() const { return cpu->GetMem().mmio.vi; }
|
||||
|
||||
u32 breakpoint = 0;
|
||||
|
||||
@@ -30,4 +30,4 @@ struct Core {
|
||||
size_t memSize{}, cpuSize{}, verSize{};
|
||||
int slot = 0;
|
||||
};
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
#include <execution>
|
||||
|
||||
namespace n64 {
|
||||
void GameDB::match(Mem& mem) {
|
||||
ROM& rom = mem.rom;
|
||||
void GameDB::match(Mem &mem) {
|
||||
ROM &rom = mem.rom;
|
||||
bool found = false;
|
||||
std::for_each(std::execution::par, std::begin(gamedb), std::end(gamedb), [&](const auto& i) {
|
||||
std::for_each(std::execution::par, std::begin(gamedb), std::end(gamedb), [&](const auto &i) {
|
||||
bool matches_code = i.code == rom.code;
|
||||
bool matches_region = false;
|
||||
|
||||
@@ -24,8 +24,9 @@ void GameDB::match(Mem& mem) {
|
||||
mem.rom.gameNameDB = i.name;
|
||||
return;
|
||||
} else {
|
||||
Util::warn("Matched code for {}, but not region! Game supposedly exists in regions [{}] but this image has region {}",
|
||||
i.name, i.regions, rom.header.countryCode[0]);
|
||||
Util::warn(
|
||||
"Matched code for {}, but not region! Game supposedly exists in regions [{}] but this image has region {}",
|
||||
i.name, i.regions, rom.header.countryCode[0]);
|
||||
mem.saveType = i.saveType;
|
||||
mem.rom.gameNameDB = i.name;
|
||||
return;
|
||||
@@ -33,11 +34,11 @@ void GameDB::match(Mem& mem) {
|
||||
}
|
||||
});
|
||||
|
||||
if(!found) {
|
||||
if (!found) {
|
||||
Util::debug("Did not match any Game DB entries. Code: {} Region: {}", mem.rom.code, mem.rom.header.countryCode[0]);
|
||||
|
||||
mem.rom.gameNameDB = "";
|
||||
mem.saveType = SAVE_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -2,13 +2,7 @@
|
||||
#include <string>
|
||||
|
||||
namespace n64 {
|
||||
enum SaveType {
|
||||
SAVE_NONE,
|
||||
SAVE_EEPROM_4k,
|
||||
SAVE_EEPROM_16k,
|
||||
SAVE_FLASH_1m,
|
||||
SAVE_SRAM_256k
|
||||
};
|
||||
enum SaveType { SAVE_NONE, SAVE_EEPROM_4k, SAVE_EEPROM_16k, SAVE_FLASH_1m, SAVE_SRAM_256k };
|
||||
|
||||
struct Mem;
|
||||
struct GameDBEntry {
|
||||
@@ -19,192 +13,192 @@ struct GameDBEntry {
|
||||
};
|
||||
|
||||
namespace GameDB {
|
||||
void match(Mem &mem);
|
||||
void match(Mem &mem);
|
||||
}
|
||||
|
||||
static const GameDBEntry gamedb[] = {
|
||||
{"NNM", "E", SAVE_NONE, "Namco Museum 64"},
|
||||
{"NDM", "E", SAVE_NONE, "Doom 64"},
|
||||
{"NGN", "E", SAVE_EEPROM_4k, "GoldenEye 007"},
|
||||
{"NNM", "E", SAVE_NONE, "Namco Museum 64"},
|
||||
{"NDM", "E", SAVE_NONE, "Doom 64"},
|
||||
{"NGN", "E", SAVE_EEPROM_4k, "GoldenEye 007"},
|
||||
// Copied from CEN64 with small edits: https://github.com/n64dev/cen64/blob/master/device/cart_db.c
|
||||
{"CFZ", "EJ", SAVE_SRAM_256k, "F-Zero X (NTSC)"},
|
||||
{"CLB", "EJ", SAVE_EEPROM_4k, "Mario Party (NTSC)"},
|
||||
{"CP2", "J", SAVE_FLASH_1m, "Pokémon Stadium 2 (Japan)"},
|
||||
{"CPS", "J", SAVE_SRAM_256k, "Pokémon Stadium (Japan)"},
|
||||
{"CZL", "EJ", SAVE_SRAM_256k, "Legend of Zelda: Ocarina of Time (NTSC)"},
|
||||
{"N3D", "J", SAVE_EEPROM_16k, "Doraemon 3: Nobita no Machi SOS!"},
|
||||
{"N3H", "J", SAVE_SRAM_256k, "Ganbare! Nippon! Olympics 2000"},
|
||||
{"NA2", "J", SAVE_SRAM_256k, "Virtual Pro Wrestling 2"},
|
||||
{"NAB", "JP", SAVE_EEPROM_4k, "Air Boarder 64"},
|
||||
{"NAD", "E", SAVE_EEPROM_4k, "Worms Armageddon (USA)"},
|
||||
{"NAF", "J", SAVE_FLASH_1m, "Doubutsu no Mori"},
|
||||
{"NAG", "EJP", SAVE_EEPROM_4k, "AeroGauge"},
|
||||
{"NAL", "EJPU", SAVE_SRAM_256k, "Super Smash Bros"},
|
||||
{"NB5", "J", SAVE_SRAM_256k, "Biohazard 2"},
|
||||
{"NB6", "J", SAVE_EEPROM_4k, "Super B-Daman: Battle Phoenix 64"},
|
||||
{"NB7", "EJPU", SAVE_EEPROM_16k, "Banjo-Tooie"},
|
||||
{"NBC", "EJP", SAVE_EEPROM_4k, "Blast Corps"},
|
||||
{"NBD", "EJP", SAVE_EEPROM_4k, "Bomberman Hero"},
|
||||
{"NBH", "EP", SAVE_EEPROM_4k, "Body Harvest"},
|
||||
{"NBK", "EJP", SAVE_EEPROM_4k, "Banjo-Kazooie"},
|
||||
{"NBM", "EJP", SAVE_EEPROM_4k, "Bomberman 64"},
|
||||
{"NBN", "J", SAVE_EEPROM_4k, "Bakuretsu Muteki Bangaioh"},
|
||||
{"NBV", "EJ", SAVE_EEPROM_4k, "Bomberman 64: The Second Attack!"},
|
||||
{"NCC", "DEP", SAVE_FLASH_1m, "Command & Conquer"},
|
||||
{"NCG", "J", SAVE_EEPROM_4k, "Choro Q 64 2: Hacha-Mecha Grand Prix Race"},
|
||||
{"NCH", "EP", SAVE_EEPROM_4k, "Chopper Attack"},
|
||||
{"NCK", "E", SAVE_FLASH_1m, "NBA Courtside 2"},
|
||||
{"NCR", "EJP", SAVE_EEPROM_4k, "Penny Racers"},
|
||||
{"NCT", "EJP", SAVE_EEPROM_4k, "Chameleon Twist"},
|
||||
{"NCU", "EP", SAVE_EEPROM_4k, "Cruis'n USA"},
|
||||
{"NCW", "EP", SAVE_EEPROM_16k, "Cruis'n World"},
|
||||
{"NCX", "J", SAVE_EEPROM_4k, "Custom Robo"},
|
||||
{"NCZ", "J", SAVE_EEPROM_16k, "Custom Robo V2"},
|
||||
{"ND2", "J", SAVE_EEPROM_16k, "Doraemon 2: Nobita to Hikari no Shinden"},
|
||||
{"ND3", "J", SAVE_EEPROM_16k, "Akumajou Dracula Mokushiroku"},
|
||||
{"ND4", "J", SAVE_EEPROM_16k, "Akumajou Dracula Mokushiroku Gaiden: Legend of Cornell"},
|
||||
{"ND6", "J", SAVE_EEPROM_16k, "Densha de Go! 64"},
|
||||
{"NDA", "J", SAVE_FLASH_1m, "Derby Stallion 64"},
|
||||
{"NDK", "J", SAVE_EEPROM_4k, "Space Dynamites"},
|
||||
{"NDO", "EJP", SAVE_EEPROM_16k, "Donkey Kong 64"},
|
||||
{"NDP", "E", SAVE_FLASH_1m, "Dinosaur Planet"},
|
||||
{"NDR", "J", SAVE_EEPROM_4k, "Doraemon: Nobita to 3tsu no Seireiseki"},
|
||||
{"NDU", "EP", SAVE_EEPROM_4k, "Duck Dodgers"},
|
||||
{"NDY", "EJP", SAVE_EEPROM_4k, "Diddy Kong Racing"},
|
||||
{"NEA", "EP", SAVE_EEPROM_4k, "PGA European Tour"},
|
||||
{"NEP", "EJP", SAVE_EEPROM_16k, "Star Wars Episode I: Racer"},
|
||||
{"NER", "E", SAVE_EEPROM_4k, "AeroFighters Assault (USA)"},
|
||||
{"NEV", "J", SAVE_EEPROM_16k, "Neon Genesis Evangelion"},
|
||||
{"NF2", "P", SAVE_EEPROM_4k, "F-1 World Grand Prix II"},
|
||||
{"NFG", "E", SAVE_EEPROM_4k, "Fighter Destiny 2"},
|
||||
{"NFH", "EP", SAVE_EEPROM_4k, "Bass Hunter 64"},
|
||||
{"NFU", "EP", SAVE_EEPROM_16k, "Conker's Bad Fur Day"},
|
||||
{"NFW", "DEFJP", SAVE_EEPROM_4k, "F-1 World Grand Prix"},
|
||||
{"NFX", "EJPU", SAVE_EEPROM_4k, "Star Fox 64"},
|
||||
{"NFY", "J", SAVE_EEPROM_4k, "Kakutou Denshou: F-Cup Maniax"},
|
||||
{"NFZ", "P", SAVE_SRAM_256k, "F-Zero X (PAL)"},
|
||||
{"NG6", "J", SAVE_SRAM_256k, "Ganbare Goemon: Dero Dero Douchuu Obake Tenkomori"},
|
||||
{"NGC", "EP", SAVE_EEPROM_16k, "GT 64: Championship Edition"},
|
||||
{"NGE", "EJP", SAVE_EEPROM_4k, "GoldenEye 007"},
|
||||
{"NGL", "J", SAVE_EEPROM_4k, "Getter Love!!"},
|
||||
{"NGP", "J", SAVE_SRAM_256k, "Goemon: Mononoke Sugoroku"},
|
||||
{"NGT", "J", SAVE_EEPROM_16k, "City-Tour GP: Zen-Nihon GT Senshuken"},
|
||||
{"NGU", "J", SAVE_EEPROM_4k, "Tsumi to Batsu: Hoshi no Keishousha"},
|
||||
{"NGV", "EP", SAVE_EEPROM_4k, "Glover"},
|
||||
{"NHA", "J", SAVE_EEPROM_4k, "Bomber Man 64 (Japan)"},
|
||||
{"NHF", "J", SAVE_EEPROM_4k, "64 Hanafuda: Tenshi no Yakusoku"},
|
||||
{"NHP", "J", SAVE_EEPROM_4k, "Heiwa Pachinko World 64"},
|
||||
{"NHY", "J", SAVE_SRAM_256k, "Hybrid Heaven (Japan)"},
|
||||
{"NIB", "J", SAVE_SRAM_256k, "Itoi Shigesato no Bass Tsuri No. 1 Kettei Ban!"},
|
||||
{"NIC", "E", SAVE_EEPROM_4k, "Indy Racing 2000"},
|
||||
{"NIJ", "EP", SAVE_EEPROM_4k, "Indiana Jones and the Infernal Machine"},
|
||||
{"NIM", "J", SAVE_EEPROM_16k, "Ide Yosuke no Mahjong Juku"},
|
||||
{"NIR", "J", SAVE_EEPROM_4k, "Utchan Nanchan no Hono no Challenger: Denryuu Ira Ira Bou"},
|
||||
{"NJ5", "J", SAVE_SRAM_256k, "Jikkyou Powerful Pro Yakyuu 5"},
|
||||
{"NJD", "E", SAVE_FLASH_1m, "Jet Force Gemini (Kiosk Demo)"},
|
||||
{"NJF", "EJP", SAVE_FLASH_1m, "Jet Force Gemini"},
|
||||
{"NJG", "J", SAVE_SRAM_256k, "Jinsei Game 64"},
|
||||
{"NJM", "EP", SAVE_EEPROM_4k, "Earthworm Jim 3D"},
|
||||
{"NK2", "EJP", SAVE_EEPROM_4k, "Snowboard Kids 2"},
|
||||
{"NK4", "EJP", SAVE_EEPROM_16k, "Kirby 64: The Crystal Shards"},
|
||||
{"NKA", "DEFJP", SAVE_EEPROM_4k, "Fighters Destiny"},
|
||||
{"NKG", "EP", SAVE_SRAM_256k, "MLB featuring Ken Griffey Jr."},
|
||||
{"NKI", "EP", SAVE_EEPROM_4k, "Killer Instinct Gold"},
|
||||
{"NKJ", "E", SAVE_FLASH_1m, "Ken Griffey Jr.'s Slugfest"},
|
||||
{"NKT", "EJP", SAVE_EEPROM_4k, "Mario Kart 64"},
|
||||
{"NLB", "P", SAVE_EEPROM_4k, "Mario Party (PAL)"},
|
||||
{"NLL", "J", SAVE_EEPROM_4k, "Last Legion UX"},
|
||||
{"NLR", "EJP", SAVE_EEPROM_4k, "Lode Runner 3D"},
|
||||
{"NM6", "E", SAVE_FLASH_1m, "Mega Man 64"},
|
||||
{"NM8", "EJP", SAVE_EEPROM_16k, "Mario Tennis"},
|
||||
{"NMF", "EJP", SAVE_SRAM_256k, "Mario Golf"},
|
||||
{"NMG", "DEP", SAVE_EEPROM_4k, "Monaco Grand Prix"},
|
||||
{"NMI", "DEFIPS", SAVE_EEPROM_4k, "Mission: Impossible"},
|
||||
{"NML", "EJP", SAVE_EEPROM_4k, "Mickey's Speedway USA"},
|
||||
{"NMO", "E", SAVE_EEPROM_4k, "Monopoly"},
|
||||
{"NMQ", "EJP", SAVE_FLASH_1m, "Paper Mario"},
|
||||
{"NMR", "EJP", SAVE_EEPROM_4k, "Multi Racing Championship"},
|
||||
{"NMS", "J", SAVE_EEPROM_4k, "Morita Shougi 64"},
|
||||
{"NMU", "E", SAVE_EEPROM_4k, "Big Mountain 2000"},
|
||||
{"NMV", "EJP", SAVE_EEPROM_16k, "Mario Party 3"},
|
||||
{"NMW", "EJP", SAVE_EEPROM_4k, "Mario Party 2"},
|
||||
{"NMX", "EJP", SAVE_EEPROM_16k, "Excitebike 64"},
|
||||
{"NN6", "E", SAVE_EEPROM_4k, "Dr. Mario 64"},
|
||||
{"NNA", "EP", SAVE_EEPROM_4k, "Star Wars Episode I: Battle for Naboo"},
|
||||
{"NNB", "EP", SAVE_EEPROM_16k, "Kobe Bryant in NBA Courtside"},
|
||||
{"NOB", "EJ", SAVE_SRAM_256k, "Ogre Battle 64: Person of Lordly Caliber"},
|
||||
{"NOS", "J", SAVE_EEPROM_4k, "64 Oozumou"},
|
||||
{"NP2", "J", SAVE_EEPROM_4k, "Chou Kuukan Nighter Pro Yakyuu King 2"},
|
||||
{"NP3", "DEFIJPS", SAVE_FLASH_1m, "Pokémon Stadium 2"},
|
||||
{"NP6", "J", SAVE_SRAM_256k, "Jikkyou Powerful Pro Yakyuu 6"},
|
||||
{"NPA", "J", SAVE_SRAM_256k, "Jikkyou Powerful Pro Yakyuu 2000"},
|
||||
{"NPD", "EJP", SAVE_EEPROM_16k, "Perfect Dark"},
|
||||
{"NPE", "J", SAVE_SRAM_256k, "Jikkyou Powerful Pro Yakyuu Basic Ban 2001"},
|
||||
{"NPF", "DEFIJPSU", SAVE_FLASH_1m, "Pokémon Snap"},
|
||||
{"NPG", "EJ", SAVE_EEPROM_4k, "Hey You, Pikachu!"},
|
||||
{"NPH", "E", SAVE_FLASH_1m, "Pokémon Snap Station (Kiosk Demo)"},
|
||||
{"NPM", "P", SAVE_SRAM_256k, "Premier Manager 64"},
|
||||
{"NPN", "DEFP", SAVE_FLASH_1m, "Pokémon Puzzle League"},
|
||||
{"NPO", "DEFIPS", SAVE_FLASH_1m, "Pokémon Stadium (USA, PAL)"},
|
||||
{"NPP", "J", SAVE_EEPROM_16k, "Parlor! Pro 64: Pachinko Jikki Simulation Game"},
|
||||
{"NPS", "J", SAVE_SRAM_256k, "Jikkyou J.League 1999: Perfect Striker 2"},
|
||||
{"NPT", "J", SAVE_EEPROM_4k, "Puyo Puyon Party"},
|
||||
{"NPW", "EJP", SAVE_EEPROM_4k, "Pilotwings 64"},
|
||||
{"NPY", "J", SAVE_EEPROM_4k, "Puyo Puyo Sun 64"},
|
||||
{"NR7", "J", SAVE_EEPROM_16k, "Robot Poncots 64: 7tsu no Umi no Caramel"},
|
||||
{"NRA", "J", SAVE_EEPROM_4k, "Rally '99"},
|
||||
{"NRC", "EJP", SAVE_EEPROM_4k, "Top Gear Overdrive"},
|
||||
{"NRE", "EP", SAVE_SRAM_256k, "Resident Evil 2"},
|
||||
{"NRH", "J", SAVE_FLASH_1m, "Rockman Dash"},
|
||||
{"NRI", "EP", SAVE_SRAM_256k, "The New Tetris"},
|
||||
{"NRS", "EJP", SAVE_EEPROM_4k, "Star Wars: Rogue Squadron"},
|
||||
{"NRZ", "EP", SAVE_EEPROM_16k, "Ridge Racer 64"},
|
||||
{"NS4", "J", SAVE_SRAM_256k, "Super Robot Taisen 64"},
|
||||
{"NS6", "EJ", SAVE_EEPROM_4k, "Star Soldier: Vanishing Earth"},
|
||||
{"NSA", "JP", SAVE_EEPROM_4k, "AeroFighters Assault (PAL, Japan)"},
|
||||
{"NSC", "EP", SAVE_EEPROM_4k, "Starshot: Space Circus Fever"},
|
||||
{"NSI", "J", SAVE_SRAM_256k, "Fushigi no Dungeon: Fuurai no Shiren 2"},
|
||||
{"NSM", "EJP", SAVE_EEPROM_4k, "Super Mario 64"},
|
||||
{"NSN", "J", SAVE_EEPROM_4k, "Snow Speeder"},
|
||||
{"NSQ", "EP", SAVE_FLASH_1m, "StarCraft 64"},
|
||||
{"NSS", "J", SAVE_EEPROM_4k, "Super Robot Spirits"},
|
||||
{"NSU", "EP", SAVE_EEPROM_4k, "Rocket: Robot on Wheels"},
|
||||
{"NSV", "EP", SAVE_EEPROM_4k, "SpaceStation Silicon Valley"},
|
||||
{"NSW", "EJP", SAVE_EEPROM_4k, "Star Wars: Shadows of the Empire"},
|
||||
{"NT3", "J", SAVE_SRAM_256k, "Toukon Road 2"},
|
||||
{"NT6", "J", SAVE_EEPROM_4k, "Tetris 64"},
|
||||
{"NT9", "EP", SAVE_FLASH_1m, "Tigger's Honey Hunt"},
|
||||
{"NTB", "J", SAVE_EEPROM_4k, "Transformers: Beast Wars Metals 64"},
|
||||
{"NTC", "J", SAVE_EEPROM_4k, "64 Trump Collection"},
|
||||
{"NTE", "AP", SAVE_SRAM_256k, "1080 Snowboarding"},
|
||||
{"NTJ", "EP", SAVE_EEPROM_4k, "Tom and Jerry in Fists of Furry"},
|
||||
{"NTM", "EJP", SAVE_EEPROM_4k, "Mischief Makers"},
|
||||
{"NTN", "EP", SAVE_EEPROM_4k, "All-Star Tennis 99"},
|
||||
{"NTP", "EP", SAVE_EEPROM_4k, "Tetrisphere"},
|
||||
{"NTR", "JP", SAVE_EEPROM_4k, "Top Gear Rally (PAL, Japan)"},
|
||||
{"NTW", "J", SAVE_EEPROM_4k, "64 de Hakken!! Tamagotchi"},
|
||||
{"NTX", "EP", SAVE_EEPROM_4k, "Taz Express"},
|
||||
{"NUB", "J", SAVE_EEPROM_16k, "PD Ultraman Battle Collection 64"},
|
||||
{"NUM", "J", SAVE_SRAM_256k, "Nushi Zuri 64: Shiokaze ni Notte"},
|
||||
{"NUT", "J", SAVE_SRAM_256k, "Nushi Zuri 64"},
|
||||
{"NVB", "J", SAVE_SRAM_256k, "Bass Rush: ECOGEAR PowerWorm Championship"},
|
||||
{"NVL", "EP", SAVE_EEPROM_4k, "V-Rally 99 (USA, PAL)"},
|
||||
{"NVP", "J", SAVE_SRAM_256k, "Virtual Pro Wrestling 64"},
|
||||
{"NVY", "J", SAVE_EEPROM_4k, "V-Rally 99 (Japan)"},
|
||||
{"NW2", "EP", SAVE_SRAM_256k, "WCW/nWo Revenge"},
|
||||
{"NW4", "EP", SAVE_FLASH_1m, "WWF No Mercy"},
|
||||
{"NWC", "J", SAVE_EEPROM_4k, "Wild Choppers"},
|
||||
{"NWL", "EP", SAVE_SRAM_256k, "Waialae Country Club: True Golf Classics"},
|
||||
{"NWQ", "E", SAVE_EEPROM_4k, "Rally Challenge 2000"},
|
||||
{"NWR", "EJP", SAVE_EEPROM_4k, "Wave Race 64"},
|
||||
{"NWT", "J", SAVE_EEPROM_4k, "Wetrix (Japan)"},
|
||||
{"NWU", "P", SAVE_EEPROM_4k, "Worms Armageddon (PAL)"},
|
||||
{"NWX", "EJP", SAVE_SRAM_256k, "WWF WrestleMania 2000"},
|
||||
{"NXO", "E", SAVE_EEPROM_4k, "Cruis'n Exotica"},
|
||||
{"NYK", "J", SAVE_EEPROM_4k, "Yakouchuu II: Satsujin Kouro"},
|
||||
{"NYS", "EJP", SAVE_EEPROM_16k, "Yoshi's Story"},
|
||||
{"NYW", "EJ", SAVE_SRAM_256k, "Harvest Moon 64"},
|
||||
{"NZL", "P", SAVE_SRAM_256k, "Legend of Zelda: Ocarina of Time (PAL)"},
|
||||
{"NZS", "EJP", SAVE_FLASH_1m, "Legend of Zelda: Majora's Mask"},
|
||||
{"CFZ", "EJ", SAVE_SRAM_256k, "F-Zero X (NTSC)"},
|
||||
{"CLB", "EJ", SAVE_EEPROM_4k, "Mario Party (NTSC)"},
|
||||
{"CP2", "J", SAVE_FLASH_1m, "Pokémon Stadium 2 (Japan)"},
|
||||
{"CPS", "J", SAVE_SRAM_256k, "Pokémon Stadium (Japan)"},
|
||||
{"CZL", "EJ", SAVE_SRAM_256k, "Legend of Zelda: Ocarina of Time (NTSC)"},
|
||||
{"N3D", "J", SAVE_EEPROM_16k, "Doraemon 3: Nobita no Machi SOS!"},
|
||||
{"N3H", "J", SAVE_SRAM_256k, "Ganbare! Nippon! Olympics 2000"},
|
||||
{"NA2", "J", SAVE_SRAM_256k, "Virtual Pro Wrestling 2"},
|
||||
{"NAB", "JP", SAVE_EEPROM_4k, "Air Boarder 64"},
|
||||
{"NAD", "E", SAVE_EEPROM_4k, "Worms Armageddon (USA)"},
|
||||
{"NAF", "J", SAVE_FLASH_1m, "Doubutsu no Mori"},
|
||||
{"NAG", "EJP", SAVE_EEPROM_4k, "AeroGauge"},
|
||||
{"NAL", "EJPU", SAVE_SRAM_256k, "Super Smash Bros"},
|
||||
{"NB5", "J", SAVE_SRAM_256k, "Biohazard 2"},
|
||||
{"NB6", "J", SAVE_EEPROM_4k, "Super B-Daman: Battle Phoenix 64"},
|
||||
{"NB7", "EJPU", SAVE_EEPROM_16k, "Banjo-Tooie"},
|
||||
{"NBC", "EJP", SAVE_EEPROM_4k, "Blast Corps"},
|
||||
{"NBD", "EJP", SAVE_EEPROM_4k, "Bomberman Hero"},
|
||||
{"NBH", "EP", SAVE_EEPROM_4k, "Body Harvest"},
|
||||
{"NBK", "EJP", SAVE_EEPROM_4k, "Banjo-Kazooie"},
|
||||
{"NBM", "EJP", SAVE_EEPROM_4k, "Bomberman 64"},
|
||||
{"NBN", "J", SAVE_EEPROM_4k, "Bakuretsu Muteki Bangaioh"},
|
||||
{"NBV", "EJ", SAVE_EEPROM_4k, "Bomberman 64: The Second Attack!"},
|
||||
{"NCC", "DEP", SAVE_FLASH_1m, "Command & Conquer"},
|
||||
{"NCG", "J", SAVE_EEPROM_4k, "Choro Q 64 2: Hacha-Mecha Grand Prix Race"},
|
||||
{"NCH", "EP", SAVE_EEPROM_4k, "Chopper Attack"},
|
||||
{"NCK", "E", SAVE_FLASH_1m, "NBA Courtside 2"},
|
||||
{"NCR", "EJP", SAVE_EEPROM_4k, "Penny Racers"},
|
||||
{"NCT", "EJP", SAVE_EEPROM_4k, "Chameleon Twist"},
|
||||
{"NCU", "EP", SAVE_EEPROM_4k, "Cruis'n USA"},
|
||||
{"NCW", "EP", SAVE_EEPROM_16k, "Cruis'n World"},
|
||||
{"NCX", "J", SAVE_EEPROM_4k, "Custom Robo"},
|
||||
{"NCZ", "J", SAVE_EEPROM_16k, "Custom Robo V2"},
|
||||
{"ND2", "J", SAVE_EEPROM_16k, "Doraemon 2: Nobita to Hikari no Shinden"},
|
||||
{"ND3", "J", SAVE_EEPROM_16k, "Akumajou Dracula Mokushiroku"},
|
||||
{"ND4", "J", SAVE_EEPROM_16k, "Akumajou Dracula Mokushiroku Gaiden: Legend of Cornell"},
|
||||
{"ND6", "J", SAVE_EEPROM_16k, "Densha de Go! 64"},
|
||||
{"NDA", "J", SAVE_FLASH_1m, "Derby Stallion 64"},
|
||||
{"NDK", "J", SAVE_EEPROM_4k, "Space Dynamites"},
|
||||
{"NDO", "EJP", SAVE_EEPROM_16k, "Donkey Kong 64"},
|
||||
{"NDP", "E", SAVE_FLASH_1m, "Dinosaur Planet"},
|
||||
{"NDR", "J", SAVE_EEPROM_4k, "Doraemon: Nobita to 3tsu no Seireiseki"},
|
||||
{"NDU", "EP", SAVE_EEPROM_4k, "Duck Dodgers"},
|
||||
{"NDY", "EJP", SAVE_EEPROM_4k, "Diddy Kong Racing"},
|
||||
{"NEA", "EP", SAVE_EEPROM_4k, "PGA European Tour"},
|
||||
{"NEP", "EJP", SAVE_EEPROM_16k, "Star Wars Episode I: Racer"},
|
||||
{"NER", "E", SAVE_EEPROM_4k, "AeroFighters Assault (USA)"},
|
||||
{"NEV", "J", SAVE_EEPROM_16k, "Neon Genesis Evangelion"},
|
||||
{"NF2", "P", SAVE_EEPROM_4k, "F-1 World Grand Prix II"},
|
||||
{"NFG", "E", SAVE_EEPROM_4k, "Fighter Destiny 2"},
|
||||
{"NFH", "EP", SAVE_EEPROM_4k, "Bass Hunter 64"},
|
||||
{"NFU", "EP", SAVE_EEPROM_16k, "Conker's Bad Fur Day"},
|
||||
{"NFW", "DEFJP", SAVE_EEPROM_4k, "F-1 World Grand Prix"},
|
||||
{"NFX", "EJPU", SAVE_EEPROM_4k, "Star Fox 64"},
|
||||
{"NFY", "J", SAVE_EEPROM_4k, "Kakutou Denshou: F-Cup Maniax"},
|
||||
{"NFZ", "P", SAVE_SRAM_256k, "F-Zero X (PAL)"},
|
||||
{"NG6", "J", SAVE_SRAM_256k, "Ganbare Goemon: Dero Dero Douchuu Obake Tenkomori"},
|
||||
{"NGC", "EP", SAVE_EEPROM_16k, "GT 64: Championship Edition"},
|
||||
{"NGE", "EJP", SAVE_EEPROM_4k, "GoldenEye 007"},
|
||||
{"NGL", "J", SAVE_EEPROM_4k, "Getter Love!!"},
|
||||
{"NGP", "J", SAVE_SRAM_256k, "Goemon: Mononoke Sugoroku"},
|
||||
{"NGT", "J", SAVE_EEPROM_16k, "City-Tour GP: Zen-Nihon GT Senshuken"},
|
||||
{"NGU", "J", SAVE_EEPROM_4k, "Tsumi to Batsu: Hoshi no Keishousha"},
|
||||
{"NGV", "EP", SAVE_EEPROM_4k, "Glover"},
|
||||
{"NHA", "J", SAVE_EEPROM_4k, "Bomber Man 64 (Japan)"},
|
||||
{"NHF", "J", SAVE_EEPROM_4k, "64 Hanafuda: Tenshi no Yakusoku"},
|
||||
{"NHP", "J", SAVE_EEPROM_4k, "Heiwa Pachinko World 64"},
|
||||
{"NHY", "J", SAVE_SRAM_256k, "Hybrid Heaven (Japan)"},
|
||||
{"NIB", "J", SAVE_SRAM_256k, "Itoi Shigesato no Bass Tsuri No. 1 Kettei Ban!"},
|
||||
{"NIC", "E", SAVE_EEPROM_4k, "Indy Racing 2000"},
|
||||
{"NIJ", "EP", SAVE_EEPROM_4k, "Indiana Jones and the Infernal Machine"},
|
||||
{"NIM", "J", SAVE_EEPROM_16k, "Ide Yosuke no Mahjong Juku"},
|
||||
{"NIR", "J", SAVE_EEPROM_4k, "Utchan Nanchan no Hono no Challenger: Denryuu Ira Ira Bou"},
|
||||
{"NJ5", "J", SAVE_SRAM_256k, "Jikkyou Powerful Pro Yakyuu 5"},
|
||||
{"NJD", "E", SAVE_FLASH_1m, "Jet Force Gemini (Kiosk Demo)"},
|
||||
{"NJF", "EJP", SAVE_FLASH_1m, "Jet Force Gemini"},
|
||||
{"NJG", "J", SAVE_SRAM_256k, "Jinsei Game 64"},
|
||||
{"NJM", "EP", SAVE_EEPROM_4k, "Earthworm Jim 3D"},
|
||||
{"NK2", "EJP", SAVE_EEPROM_4k, "Snowboard Kids 2"},
|
||||
{"NK4", "EJP", SAVE_EEPROM_16k, "Kirby 64: The Crystal Shards"},
|
||||
{"NKA", "DEFJP", SAVE_EEPROM_4k, "Fighters Destiny"},
|
||||
{"NKG", "EP", SAVE_SRAM_256k, "MLB featuring Ken Griffey Jr."},
|
||||
{"NKI", "EP", SAVE_EEPROM_4k, "Killer Instinct Gold"},
|
||||
{"NKJ", "E", SAVE_FLASH_1m, "Ken Griffey Jr.'s Slugfest"},
|
||||
{"NKT", "EJP", SAVE_EEPROM_4k, "Mario Kart 64"},
|
||||
{"NLB", "P", SAVE_EEPROM_4k, "Mario Party (PAL)"},
|
||||
{"NLL", "J", SAVE_EEPROM_4k, "Last Legion UX"},
|
||||
{"NLR", "EJP", SAVE_EEPROM_4k, "Lode Runner 3D"},
|
||||
{"NM6", "E", SAVE_FLASH_1m, "Mega Man 64"},
|
||||
{"NM8", "EJP", SAVE_EEPROM_16k, "Mario Tennis"},
|
||||
{"NMF", "EJP", SAVE_SRAM_256k, "Mario Golf"},
|
||||
{"NMG", "DEP", SAVE_EEPROM_4k, "Monaco Grand Prix"},
|
||||
{"NMI", "DEFIPS", SAVE_EEPROM_4k, "Mission: Impossible"},
|
||||
{"NML", "EJP", SAVE_EEPROM_4k, "Mickey's Speedway USA"},
|
||||
{"NMO", "E", SAVE_EEPROM_4k, "Monopoly"},
|
||||
{"NMQ", "EJP", SAVE_FLASH_1m, "Paper Mario"},
|
||||
{"NMR", "EJP", SAVE_EEPROM_4k, "Multi Racing Championship"},
|
||||
{"NMS", "J", SAVE_EEPROM_4k, "Morita Shougi 64"},
|
||||
{"NMU", "E", SAVE_EEPROM_4k, "Big Mountain 2000"},
|
||||
{"NMV", "EJP", SAVE_EEPROM_16k, "Mario Party 3"},
|
||||
{"NMW", "EJP", SAVE_EEPROM_4k, "Mario Party 2"},
|
||||
{"NMX", "EJP", SAVE_EEPROM_16k, "Excitebike 64"},
|
||||
{"NN6", "E", SAVE_EEPROM_4k, "Dr. Mario 64"},
|
||||
{"NNA", "EP", SAVE_EEPROM_4k, "Star Wars Episode I: Battle for Naboo"},
|
||||
{"NNB", "EP", SAVE_EEPROM_16k, "Kobe Bryant in NBA Courtside"},
|
||||
{"NOB", "EJ", SAVE_SRAM_256k, "Ogre Battle 64: Person of Lordly Caliber"},
|
||||
{"NOS", "J", SAVE_EEPROM_4k, "64 Oozumou"},
|
||||
{"NP2", "J", SAVE_EEPROM_4k, "Chou Kuukan Nighter Pro Yakyuu King 2"},
|
||||
{"NP3", "DEFIJPS", SAVE_FLASH_1m, "Pokémon Stadium 2"},
|
||||
{"NP6", "J", SAVE_SRAM_256k, "Jikkyou Powerful Pro Yakyuu 6"},
|
||||
{"NPA", "J", SAVE_SRAM_256k, "Jikkyou Powerful Pro Yakyuu 2000"},
|
||||
{"NPD", "EJP", SAVE_EEPROM_16k, "Perfect Dark"},
|
||||
{"NPE", "J", SAVE_SRAM_256k, "Jikkyou Powerful Pro Yakyuu Basic Ban 2001"},
|
||||
{"NPF", "DEFIJPSU", SAVE_FLASH_1m, "Pokémon Snap"},
|
||||
{"NPG", "EJ", SAVE_EEPROM_4k, "Hey You, Pikachu!"},
|
||||
{"NPH", "E", SAVE_FLASH_1m, "Pokémon Snap Station (Kiosk Demo)"},
|
||||
{"NPM", "P", SAVE_SRAM_256k, "Premier Manager 64"},
|
||||
{"NPN", "DEFP", SAVE_FLASH_1m, "Pokémon Puzzle League"},
|
||||
{"NPO", "DEFIPS", SAVE_FLASH_1m, "Pokémon Stadium (USA, PAL)"},
|
||||
{"NPP", "J", SAVE_EEPROM_16k, "Parlor! Pro 64: Pachinko Jikki Simulation Game"},
|
||||
{"NPS", "J", SAVE_SRAM_256k, "Jikkyou J.League 1999: Perfect Striker 2"},
|
||||
{"NPT", "J", SAVE_EEPROM_4k, "Puyo Puyon Party"},
|
||||
{"NPW", "EJP", SAVE_EEPROM_4k, "Pilotwings 64"},
|
||||
{"NPY", "J", SAVE_EEPROM_4k, "Puyo Puyo Sun 64"},
|
||||
{"NR7", "J", SAVE_EEPROM_16k, "Robot Poncots 64: 7tsu no Umi no Caramel"},
|
||||
{"NRA", "J", SAVE_EEPROM_4k, "Rally '99"},
|
||||
{"NRC", "EJP", SAVE_EEPROM_4k, "Top Gear Overdrive"},
|
||||
{"NRE", "EP", SAVE_SRAM_256k, "Resident Evil 2"},
|
||||
{"NRH", "J", SAVE_FLASH_1m, "Rockman Dash"},
|
||||
{"NRI", "EP", SAVE_SRAM_256k, "The New Tetris"},
|
||||
{"NRS", "EJP", SAVE_EEPROM_4k, "Star Wars: Rogue Squadron"},
|
||||
{"NRZ", "EP", SAVE_EEPROM_16k, "Ridge Racer 64"},
|
||||
{"NS4", "J", SAVE_SRAM_256k, "Super Robot Taisen 64"},
|
||||
{"NS6", "EJ", SAVE_EEPROM_4k, "Star Soldier: Vanishing Earth"},
|
||||
{"NSA", "JP", SAVE_EEPROM_4k, "AeroFighters Assault (PAL, Japan)"},
|
||||
{"NSC", "EP", SAVE_EEPROM_4k, "Starshot: Space Circus Fever"},
|
||||
{"NSI", "J", SAVE_SRAM_256k, "Fushigi no Dungeon: Fuurai no Shiren 2"},
|
||||
{"NSM", "EJP", SAVE_EEPROM_4k, "Super Mario 64"},
|
||||
{"NSN", "J", SAVE_EEPROM_4k, "Snow Speeder"},
|
||||
{"NSQ", "EP", SAVE_FLASH_1m, "StarCraft 64"},
|
||||
{"NSS", "J", SAVE_EEPROM_4k, "Super Robot Spirits"},
|
||||
{"NSU", "EP", SAVE_EEPROM_4k, "Rocket: Robot on Wheels"},
|
||||
{"NSV", "EP", SAVE_EEPROM_4k, "SpaceStation Silicon Valley"},
|
||||
{"NSW", "EJP", SAVE_EEPROM_4k, "Star Wars: Shadows of the Empire"},
|
||||
{"NT3", "J", SAVE_SRAM_256k, "Toukon Road 2"},
|
||||
{"NT6", "J", SAVE_EEPROM_4k, "Tetris 64"},
|
||||
{"NT9", "EP", SAVE_FLASH_1m, "Tigger's Honey Hunt"},
|
||||
{"NTB", "J", SAVE_EEPROM_4k, "Transformers: Beast Wars Metals 64"},
|
||||
{"NTC", "J", SAVE_EEPROM_4k, "64 Trump Collection"},
|
||||
{"NTE", "AP", SAVE_SRAM_256k, "1080 Snowboarding"},
|
||||
{"NTJ", "EP", SAVE_EEPROM_4k, "Tom and Jerry in Fists of Furry"},
|
||||
{"NTM", "EJP", SAVE_EEPROM_4k, "Mischief Makers"},
|
||||
{"NTN", "EP", SAVE_EEPROM_4k, "All-Star Tennis 99"},
|
||||
{"NTP", "EP", SAVE_EEPROM_4k, "Tetrisphere"},
|
||||
{"NTR", "JP", SAVE_EEPROM_4k, "Top Gear Rally (PAL, Japan)"},
|
||||
{"NTW", "J", SAVE_EEPROM_4k, "64 de Hakken!! Tamagotchi"},
|
||||
{"NTX", "EP", SAVE_EEPROM_4k, "Taz Express"},
|
||||
{"NUB", "J", SAVE_EEPROM_16k, "PD Ultraman Battle Collection 64"},
|
||||
{"NUM", "J", SAVE_SRAM_256k, "Nushi Zuri 64: Shiokaze ni Notte"},
|
||||
{"NUT", "J", SAVE_SRAM_256k, "Nushi Zuri 64"},
|
||||
{"NVB", "J", SAVE_SRAM_256k, "Bass Rush: ECOGEAR PowerWorm Championship"},
|
||||
{"NVL", "EP", SAVE_EEPROM_4k, "V-Rally 99 (USA, PAL)"},
|
||||
{"NVP", "J", SAVE_SRAM_256k, "Virtual Pro Wrestling 64"},
|
||||
{"NVY", "J", SAVE_EEPROM_4k, "V-Rally 99 (Japan)"},
|
||||
{"NW2", "EP", SAVE_SRAM_256k, "WCW/nWo Revenge"},
|
||||
{"NW4", "EP", SAVE_FLASH_1m, "WWF No Mercy"},
|
||||
{"NWC", "J", SAVE_EEPROM_4k, "Wild Choppers"},
|
||||
{"NWL", "EP", SAVE_SRAM_256k, "Waialae Country Club: True Golf Classics"},
|
||||
{"NWQ", "E", SAVE_EEPROM_4k, "Rally Challenge 2000"},
|
||||
{"NWR", "EJP", SAVE_EEPROM_4k, "Wave Race 64"},
|
||||
{"NWT", "J", SAVE_EEPROM_4k, "Wetrix (Japan)"},
|
||||
{"NWU", "P", SAVE_EEPROM_4k, "Worms Armageddon (PAL)"},
|
||||
{"NWX", "EJP", SAVE_SRAM_256k, "WWF WrestleMania 2000"},
|
||||
{"NXO", "E", SAVE_EEPROM_4k, "Cruis'n Exotica"},
|
||||
{"NYK", "J", SAVE_EEPROM_4k, "Yakouchuu II: Satsujin Kouro"},
|
||||
{"NYS", "EJP", SAVE_EEPROM_16k, "Yoshi's Story"},
|
||||
{"NYW", "EJ", SAVE_SRAM_256k, "Harvest Moon 64"},
|
||||
{"NZL", "P", SAVE_SRAM_256k, "Legend of Zelda: Ocarina of Time (PAL)"},
|
||||
{"NZS", "EJP", SAVE_FLASH_1m, "Legend of Zelda: Majora's Mask"},
|
||||
};
|
||||
}
|
||||
@@ -37,9 +37,9 @@
|
||||
#define CART_REGION_END_2_1 0x05FFFFFF
|
||||
#define CART_REGION_END_2_2 0x0FFFFFFF
|
||||
|
||||
#define RDRAM_REGION RDRAM_REGION_START ... RDRAM_REGION_END
|
||||
#define RSP_MEM_REGION DMEM_REGION_START ... 0x0403FFFF
|
||||
#define MMIO_REGION 0x04040000 ... 0x041FFFFF: case 0x04300000 ... 0x048FFFFF
|
||||
#define RDRAM_REGION RDRAM_REGION_START... RDRAM_REGION_END
|
||||
#define RSP_MEM_REGION DMEM_REGION_START... 0x0403FFFF
|
||||
#define MMIO_REGION 0x04040000 ... 0x041FFFFF : case 0x04300000 ... 0x048FFFFF
|
||||
#define SP_REGION 0x04040000 ... 0x040FFFFF
|
||||
#define DP_CMD_REGION 0x04100000 ... 0x041FFFFF
|
||||
#define RSP_REGION 0x04040000 ... 0x040FFFFF
|
||||
@@ -51,9 +51,9 @@
|
||||
#define PI_REGION 0x04600000 ... 0x046FFFFF
|
||||
#define RI_REGION 0x04700000 ... 0x047FFFFF
|
||||
#define SI_REGION 0x04800000 ... 0x048FFFFF
|
||||
#define REGION_CART CART_REGION_START_2_1 ... CART_REGION_END_1_2
|
||||
#define PIF_ROM_REGION PIF_ROM_REGION_START ... PIF_ROM_REGION_END
|
||||
#define PIF_RAM_REGION PIF_RAM_REGION_START ... PIF_RAM_REGION_END
|
||||
#define REGION_CART CART_REGION_START_2_1... CART_REGION_END_1_2
|
||||
#define PIF_ROM_REGION PIF_ROM_REGION_START... PIF_ROM_REGION_END
|
||||
#define PIF_RAM_REGION PIF_RAM_REGION_START... PIF_RAM_REGION_END
|
||||
|
||||
#define START_VREGION_KUSEG 0x00000000
|
||||
#define START_VREGION_KSEG0 0x80000000
|
||||
@@ -67,62 +67,56 @@
|
||||
#define END_VREGION_KSSEG 0xDFFFFFFF
|
||||
#define END_VREGION_KSEG3 0xFFFFFFFF
|
||||
|
||||
#define VREGION_KUSEG START_VREGION_KUSEG ... END_VREGION_KUSEG
|
||||
#define VREGION_KSEG0 START_VREGION_KSEG0 ... END_VREGION_KSEG0
|
||||
#define VREGION_KSEG1 START_VREGION_KSEG1 ... END_VREGION_KSEG1
|
||||
#define VREGION_KSSEG START_VREGION_KSSEG ... END_VREGION_KSSEG
|
||||
#define VREGION_KSEG3 START_VREGION_KSEG3 ... END_VREGION_KSEG3
|
||||
#define VREGION_KUSEG START_VREGION_KUSEG... END_VREGION_KUSEG
|
||||
#define VREGION_KSEG0 START_VREGION_KSEG0... END_VREGION_KSEG0
|
||||
#define VREGION_KSEG1 START_VREGION_KSEG1... END_VREGION_KSEG1
|
||||
#define VREGION_KSSEG START_VREGION_KSSEG... END_VREGION_KSSEG
|
||||
#define VREGION_KSEG3 START_VREGION_KSEG3... END_VREGION_KSEG3
|
||||
|
||||
#define DIRECT_MAP_MASK 0x1FFFFFFF
|
||||
|
||||
#define VREGION_XKUSEG 0x0000000000000000 ... 0x000000FFFFFFFFFF
|
||||
#define VREGION_XBAD1 0x0000010000000000 ... 0x3FFFFFFFFFFFFFFF
|
||||
#define VREGION_XBAD1 0x0000010000000000 ... 0x3FFFFFFFFFFFFFFF
|
||||
#define VREGION_XKSSEG 0x4000000000000000 ... 0x400000FFFFFFFFFF
|
||||
#define VREGION_XBAD2 0x4000010000000000 ... 0x7FFFFFFFFFFFFFFF
|
||||
#define VREGION_XBAD2 0x4000010000000000 ... 0x7FFFFFFFFFFFFFFF
|
||||
#define VREGION_XKPHYS 0x8000000000000000 ... 0xBFFFFFFFFFFFFFFF
|
||||
#define VREGION_XKSEG 0xC000000000000000 ... 0xC00000FF7FFFFFFF
|
||||
#define VREGION_XBAD3 0xC00000FF80000000 ... 0xFFFFFFFF7FFFFFFF
|
||||
#define VREGION_XKSEG 0xC000000000000000 ... 0xC00000FF7FFFFFFF
|
||||
#define VREGION_XBAD3 0xC00000FF80000000 ... 0xFFFFFFFF7FFFFFFF
|
||||
#define VREGION_CKSEG0 0xFFFFFFFF80000000 ... 0xFFFFFFFF9FFFFFFF
|
||||
#define VREGION_CKSEG1 0xFFFFFFFFA0000000 ... 0xFFFFFFFFBFFFFFFF
|
||||
#define VREGION_CKSSEG 0xFFFFFFFFC0000000 ... 0xFFFFFFFFDFFFFFFF
|
||||
#define VREGION_CKSEG3 0xFFFFFFFFE0000000 ... 0xFFFFFFFFFFFFFFFF
|
||||
|
||||
#define SREGION_PI_UNKNOWN 0x00000000
|
||||
#define SREGION_PI_UNKNOWN 0x00000000
|
||||
#define SREGION_PI_64DD_REG 0x05000000
|
||||
#define SREGION_PI_64DD_ROM 0x06000000
|
||||
#define SREGION_PI_SRAM 0x08000000
|
||||
#define SREGION_PI_ROM 0x10000000
|
||||
#define SREGION_PI_SRAM 0x08000000
|
||||
#define SREGION_PI_ROM 0x10000000
|
||||
|
||||
#define EREGION_PI_UNKNOWN 0x04FFFFFF
|
||||
#define EREGION_PI_UNKNOWN 0x04FFFFFF
|
||||
#define EREGION_PI_64DD_REG 0x05FFFFFF
|
||||
#define EREGION_PI_64DD_ROM 0x07FFFFFF
|
||||
#define EREGION_PI_SRAM 0x0FFFFFFF
|
||||
#define EREGION_PI_ROM 0xFFFFFFFF
|
||||
#define EREGION_PI_SRAM 0x0FFFFFFF
|
||||
#define EREGION_PI_ROM 0xFFFFFFFF
|
||||
|
||||
#define REGION_PI_UNKNOWN SREGION_PI_UNKNOWN ... EREGION_PI_UNKNOWN
|
||||
#define REGION_PI_64DD_REG SREGION_PI_64DD_REG ... EREGION_PI_64DD_REG
|
||||
#define REGION_PI_64DD_ROM SREGION_PI_64DD_ROM ... EREGION_PI_64DD_ROM
|
||||
#define REGION_PI_SRAM SREGION_PI_SRAM ... EREGION_PI_SRAM
|
||||
#define REGION_PI_ROM SREGION_PI_ROM ... EREGION_PI_ROM
|
||||
#define REGION_PI_UNKNOWN SREGION_PI_UNKNOWN... EREGION_PI_UNKNOWN
|
||||
#define REGION_PI_64DD_REG SREGION_PI_64DD_REG... EREGION_PI_64DD_REG
|
||||
#define REGION_PI_64DD_ROM SREGION_PI_64DD_ROM... EREGION_PI_64DD_ROM
|
||||
#define REGION_PI_SRAM SREGION_PI_SRAM... EREGION_PI_SRAM
|
||||
#define REGION_PI_ROM SREGION_PI_ROM... EREGION_PI_ROM
|
||||
|
||||
#define CART_ISVIEWER_FLUSH 0x13FF0014
|
||||
#define CART_ISVIEWER_FLUSH 0x13FF0014
|
||||
#define SREGION_CART_ISVIEWER_BUFFER 0x13FF0020
|
||||
#define EREGION_CART_ISVIEWER_BUFFER 0x13FFFFFF
|
||||
#define CART_ISVIEWER_SIZE (EREGION_CART_ISVIEWER_BUFFER - SREGION_CART_ISVIEWER_BUFFER)
|
||||
#define REGION_CART_ISVIEWER_BUFFER SREGION_CART_ISVIEWER_BUFFER ... EREGION_CART_ISVIEWER_BUFFER
|
||||
#define REGION_CART_ISVIEWER_BUFFER SREGION_CART_ISVIEWER_BUFFER... EREGION_CART_ISVIEWER_BUFFER
|
||||
|
||||
constexpr u64 operator""_kb(unsigned long long int x) {
|
||||
return 1024ULL * x;
|
||||
}
|
||||
constexpr u64 operator""_kb(unsigned long long int x) { return 1024ULL * x; }
|
||||
|
||||
constexpr u64 operator""_mb(unsigned long long int x) {
|
||||
return 1024_kb * x;
|
||||
}
|
||||
constexpr u64 operator""_mb(unsigned long long int x) { return 1024_kb * x; }
|
||||
|
||||
constexpr u64 operator""_gb(unsigned long long int x) {
|
||||
return 1024_mb * x;
|
||||
}
|
||||
constexpr u64 operator""_gb(unsigned long long int x) { return 1024_mb * x; }
|
||||
|
||||
#define ADDRESS_RANGE_SIZE 0x80000000ull
|
||||
#define PAGE_SIZE 4_kb
|
||||
#define PAGE_COUNT ((ADDRESS_RANGE_SIZE) / (PAGE_SIZE))
|
||||
#define PAGE_COUNT ((ADDRESS_RANGE_SIZE) / (PAGE_SIZE))
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#include <Netplay.hpp>
|
||||
#include <log.hpp>
|
||||
#include <PIF.hpp>
|
||||
#include <array>
|
||||
#include <log.hpp>
|
||||
|
||||
namespace Netplay {
|
||||
}
|
||||
namespace Netplay {}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
#include <PIF.hpp>
|
||||
|
||||
namespace Netplay {
|
||||
}
|
||||
namespace Netplay {}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#include <log.hpp>
|
||||
#include <MemoryHelpers.hpp>
|
||||
#include <log.hpp>
|
||||
|
||||
namespace Util {
|
||||
#define Z64 0x80371200
|
||||
@@ -10,9 +10,9 @@ namespace Util {
|
||||
template <bool toBE = false>
|
||||
FORCE_INLINE void SwapN64Rom(std::vector<u8> &rom, u32 endianness) {
|
||||
u8 altByteShift = 0;
|
||||
if((endianness >> 24) != 0x80) {
|
||||
if((endianness & 0xFF) != 0x80) {
|
||||
if(((endianness >> 16) & 0xff) != 0x80) {
|
||||
if ((endianness >> 24) != 0x80) {
|
||||
if ((endianness & 0xFF) != 0x80) {
|
||||
if (((endianness >> 16) & 0xff) != 0x80) {
|
||||
Util::panic("TODO: Unrecognized rom endianness. Ideally, this should be more robust");
|
||||
} else {
|
||||
altByteShift = 12;
|
||||
@@ -27,21 +27,21 @@ FORCE_INLINE void SwapN64Rom(std::vector<u8> &rom, u32 endianness) {
|
||||
endianness &= ~(0xFF << altByteShift);
|
||||
|
||||
switch (endianness) {
|
||||
case V64:
|
||||
SwapBuffer16(rom);
|
||||
if constexpr(!toBE)
|
||||
SwapBuffer32(rom);
|
||||
break;
|
||||
case N64:
|
||||
if constexpr(toBE)
|
||||
SwapBuffer32(rom);
|
||||
break;
|
||||
case Z64:
|
||||
if constexpr(!toBE)
|
||||
SwapBuffer32(rom);
|
||||
break;
|
||||
default:
|
||||
panic("Unrecognized rom format! Make sure this is a valid Nintendo 64 ROM dump!");
|
||||
case V64:
|
||||
SwapBuffer16(rom);
|
||||
if constexpr (!toBE)
|
||||
SwapBuffer32(rom);
|
||||
break;
|
||||
case N64:
|
||||
if constexpr (toBE)
|
||||
SwapBuffer32(rom);
|
||||
break;
|
||||
case Z64:
|
||||
if constexpr (!toBE)
|
||||
SwapBuffer32(rom);
|
||||
break;
|
||||
default:
|
||||
panic("Unrecognized rom format! Make sure this is a valid Nintendo 64 ROM dump!");
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace Util
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
#include <Scheduler.hpp>
|
||||
#include <core/registers/Registers.hpp>
|
||||
#include <core/Mem.hpp>
|
||||
#include <core/registers/Registers.hpp>
|
||||
|
||||
Scheduler scheduler;
|
||||
|
||||
void Scheduler::EnqueueRelative(u64 t, const EventType type) {
|
||||
EnqueueAbsolute(t + ticks, type);
|
||||
}
|
||||
void Scheduler::EnqueueRelative(u64 t, const EventType type) { EnqueueAbsolute(t + ticks, type); }
|
||||
|
||||
void Scheduler::EnqueueAbsolute(u64 t, const EventType type) {
|
||||
events.push({t, type});
|
||||
}
|
||||
void Scheduler::EnqueueAbsolute(u64 t, const EventType type) { events.push({t, type}); }
|
||||
|
||||
u64 Scheduler::Remove(EventType type) {
|
||||
for (auto& e : events) {
|
||||
if(e.type == type) {
|
||||
for (auto &e : events) {
|
||||
if (e.type == type) {
|
||||
u64 ret = e.time - ticks;
|
||||
e.type = NONE;
|
||||
e.time = ticks;
|
||||
@@ -25,31 +21,31 @@ u64 Scheduler::Remove(EventType type) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Scheduler::Tick(u64 t, n64::Mem& mem) {
|
||||
void Scheduler::Tick(u64 t, n64::Mem &mem) {
|
||||
ticks += t;
|
||||
n64::MI& mi = mem.mmio.mi;
|
||||
n64::SI& si = mem.mmio.si;
|
||||
n64::PI& pi = mem.mmio.pi;
|
||||
n64::MI &mi = mem.mmio.mi;
|
||||
n64::SI &si = mem.mmio.si;
|
||||
n64::PI &pi = mem.mmio.pi;
|
||||
|
||||
while(ticks >= events.top().time) {
|
||||
switch(auto type = events.top().type) {
|
||||
case SI_DMA:
|
||||
si.DMA();
|
||||
break;
|
||||
case PI_DMA_COMPLETE:
|
||||
mi.InterruptRaise(n64::MI::Interrupt::PI);
|
||||
pi.dmaBusy = false;
|
||||
break;
|
||||
case PI_BUS_WRITE_COMPLETE:
|
||||
pi.ioBusy = false;
|
||||
break;
|
||||
case NONE:
|
||||
break;
|
||||
case IMPOSSIBLE:
|
||||
Util::panic("Congratulations on keeping the emulator on for about 5 billion years, I guess, nerd.");
|
||||
default:
|
||||
Util::panic("Unknown scheduler event type {}", static_cast<int>(type));
|
||||
while (ticks >= events.top().time) {
|
||||
switch (auto type = events.top().type) {
|
||||
case SI_DMA:
|
||||
si.DMA();
|
||||
break;
|
||||
case PI_DMA_COMPLETE:
|
||||
mi.InterruptRaise(n64::MI::Interrupt::PI);
|
||||
pi.dmaBusy = false;
|
||||
break;
|
||||
case PI_BUS_WRITE_COMPLETE:
|
||||
pi.ioBusy = false;
|
||||
break;
|
||||
case NONE:
|
||||
break;
|
||||
case IMPOSSIBLE:
|
||||
Util::panic("Congratulations on keeping the emulator on for about 5 billion years, I guess, nerd.");
|
||||
default:
|
||||
Util::panic("Unknown scheduler event type {}", static_cast<int>(type));
|
||||
}
|
||||
events.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,63 +1,50 @@
|
||||
#pragma once
|
||||
#include <queue>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <log.hpp>
|
||||
#include <queue>
|
||||
|
||||
namespace n64 {
|
||||
struct Mem;
|
||||
struct Registers;
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
enum EventType {
|
||||
NONE,
|
||||
PI_BUS_WRITE_COMPLETE,
|
||||
PI_DMA_COMPLETE,
|
||||
SI_DMA,
|
||||
IMPOSSIBLE
|
||||
};
|
||||
enum EventType { NONE, PI_BUS_WRITE_COMPLETE, PI_DMA_COMPLETE, SI_DMA, IMPOSSIBLE };
|
||||
|
||||
struct Event {
|
||||
u64 time;
|
||||
EventType type;
|
||||
|
||||
friend bool operator<(const Event& rhs, const Event& lhs) {
|
||||
return rhs.time < lhs.time;
|
||||
}
|
||||
friend bool operator<(const Event &rhs, const Event &lhs) { return rhs.time < lhs.time; }
|
||||
|
||||
friend bool operator>(const Event& rhs, const Event& lhs) {
|
||||
return rhs.time > lhs.time;
|
||||
}
|
||||
friend bool operator>(const Event &rhs, const Event &lhs) { return rhs.time > lhs.time; }
|
||||
|
||||
friend bool operator>=(const Event& rhs, const Event& lhs) {
|
||||
return rhs.time >= lhs.time;
|
||||
}
|
||||
friend bool operator>=(const Event &rhs, const Event &lhs) { return rhs.time >= lhs.time; }
|
||||
};
|
||||
|
||||
struct IterableEvents {
|
||||
std::priority_queue<Event, std::vector<Event>, std::greater<>> events;
|
||||
|
||||
public:
|
||||
explicit IterableEvents() = default;
|
||||
[[nodiscard]] auto top() const { return events.top(); }
|
||||
auto pop() { events.pop(); }
|
||||
[[nodiscard]] auto begin() const { return (Event*)(&events.top()); }
|
||||
[[nodiscard]] auto begin() const { return (Event *)(&events.top()); }
|
||||
[[nodiscard]] auto end() const { return begin() + events.size(); }
|
||||
auto push(Event e) { events.push(e); }
|
||||
};
|
||||
|
||||
struct Scheduler {
|
||||
Scheduler() {
|
||||
EnqueueAbsolute(std::numeric_limits<u64>::max(), IMPOSSIBLE);
|
||||
}
|
||||
Scheduler() { EnqueueAbsolute(std::numeric_limits<u64>::max(), IMPOSSIBLE); }
|
||||
|
||||
void EnqueueRelative(u64, EventType);
|
||||
void EnqueueAbsolute(u64, EventType);
|
||||
u64 Remove(EventType);
|
||||
void Tick(u64 t, n64::Mem&);
|
||||
void Tick(u64 t, n64::Mem &);
|
||||
|
||||
IterableEvents events;
|
||||
u64 ticks = 0;
|
||||
u8 index = 0;
|
||||
};
|
||||
|
||||
extern Scheduler scheduler;
|
||||
extern Scheduler scheduler;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
#pragma once
|
||||
#include <core/mmio/VI.hpp>
|
||||
#include <core/mmio/MI.hpp>
|
||||
#include <core/RDP.hpp>
|
||||
#include <core/RSP.hpp>
|
||||
#include <core/mmio/AI.hpp>
|
||||
#include <core/mmio/MI.hpp>
|
||||
#include <core/mmio/PI.hpp>
|
||||
#include <core/mmio/RI.hpp>
|
||||
#include <core/mmio/SI.hpp>
|
||||
#include <core/RSP.hpp>
|
||||
#include <core/RDP.hpp>
|
||||
#include <core/mmio/VI.hpp>
|
||||
|
||||
class ParallelRDP;
|
||||
|
||||
@@ -15,7 +15,8 @@ struct Mem;
|
||||
struct Registers;
|
||||
|
||||
struct MMIO {
|
||||
MMIO(Mem& mem, Registers& regs, ParallelRDP& parallel) : vi(mem, regs), mi(regs), ai(mem, regs), pi(mem, regs), si(mem, regs), rsp(mem, regs), rdp(mem, parallel) {
|
||||
MMIO(Mem &mem, Registers ®s, ParallelRDP ¶llel) :
|
||||
vi(mem, regs), mi(regs), ai(mem, regs), pi(mem, regs), si(mem, regs), rsp(mem, regs), rdp(mem, parallel) {
|
||||
Reset();
|
||||
}
|
||||
void Reset();
|
||||
@@ -32,6 +33,6 @@ struct MMIO {
|
||||
u32 Read(u32);
|
||||
void Write(u32, u32);
|
||||
std::vector<u8> Serialize();
|
||||
void Deserialize(const std::vector<u8>&);
|
||||
void Deserialize(const std::vector<u8> &);
|
||||
};
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -2,36 +2,40 @@
|
||||
#include <common.hpp>
|
||||
|
||||
static constexpr u16 rcpRom[] = {
|
||||
0xffff, 0xff00, 0xfe01, 0xfd04, 0xfc07, 0xfb0c, 0xfa11, 0xf918, 0xf81f, 0xf727, 0xf631, 0xf53b, 0xf446, 0xf352, 0xf25f, 0xf16d,
|
||||
0xf07c, 0xef8b, 0xee9c, 0xedae, 0xecc0, 0xebd3, 0xeae8, 0xe9fd, 0xe913, 0xe829, 0xe741, 0xe65a, 0xe573, 0xe48d, 0xe3a9, 0xe2c5,
|
||||
0xe1e1, 0xe0ff, 0xe01e, 0xdf3d, 0xde5d, 0xdd7e, 0xdca0, 0xdbc2, 0xdae6, 0xda0a, 0xd92f, 0xd854, 0xd77b, 0xd6a2, 0xd5ca, 0xd4f3,
|
||||
0xd41d, 0xd347, 0xd272, 0xd19e, 0xd0cb, 0xcff8, 0xcf26, 0xce55, 0xcd85, 0xccb5, 0xcbe6, 0xcb18, 0xca4b, 0xc97e, 0xc8b2, 0xc7e7,
|
||||
0xc71c, 0xc652, 0xc589, 0xc4c0, 0xc3f8, 0xc331, 0xc26b, 0xc1a5, 0xc0e0, 0xc01c, 0xbf58, 0xbe95, 0xbdd2, 0xbd10, 0xbc4f, 0xbb8f,
|
||||
0xbacf, 0xba10, 0xb951, 0xb894, 0xb7d6, 0xb71a, 0xb65e, 0xb5a2, 0xb4e8, 0xb42e, 0xb374, 0xb2bb, 0xb203, 0xb14b, 0xb094, 0xafde,
|
||||
0xaf28, 0xae73, 0xadbe, 0xad0a, 0xac57, 0xaba4, 0xaaf1, 0xaa40, 0xa98e, 0xa8de, 0xa82e, 0xa77e, 0xa6d0, 0xa621, 0xa574, 0xa4c6,
|
||||
0xa41a, 0xa36e, 0xa2c2, 0xa217, 0xa16d, 0xa0c3, 0xa01a, 0x9f71, 0x9ec8, 0x9e21, 0x9d79, 0x9cd3, 0x9c2d, 0x9b87, 0x9ae2, 0x9a3d,
|
||||
0x9999, 0x98f6, 0x9852, 0x97b0, 0x970e, 0x966c, 0x95cb, 0x952b, 0x948b, 0x93eb, 0x934c, 0x92ad, 0x920f, 0x9172, 0x90d4, 0x9038,
|
||||
0x8f9c, 0x8f00, 0x8e65, 0x8dca, 0x8d30, 0x8c96, 0x8bfc, 0x8b64, 0x8acb, 0x8a33, 0x899c, 0x8904, 0x886e, 0x87d8, 0x8742, 0x86ad,
|
||||
0x8618, 0x8583, 0x84f0, 0x845c, 0x83c9, 0x8336, 0x82a4, 0x8212, 0x8181, 0x80f0, 0x8060, 0x7fd0, 0x7f40, 0x7eb1, 0x7e22, 0x7d93,
|
||||
0x7d05, 0x7c78, 0x7beb, 0x7b5e, 0x7ad2, 0x7a46, 0x79ba, 0x792f, 0x78a4, 0x781a, 0x7790, 0x7706, 0x767d, 0x75f5, 0x756c, 0x74e4,
|
||||
0x745d, 0x73d5, 0x734f, 0x72c8, 0x7242, 0x71bc, 0x7137, 0x70b2, 0x702e, 0x6fa9, 0x6f26, 0x6ea2, 0x6e1f, 0x6d9c, 0x6d1a, 0x6c98,
|
||||
0x6c16, 0x6b95, 0x6b14, 0x6a94, 0x6a13, 0x6993, 0x6914, 0x6895, 0x6816, 0x6798, 0x6719, 0x669c, 0x661e, 0x65a1, 0x6524, 0x64a8,
|
||||
0x642c, 0x63b0, 0x6335, 0x62ba, 0x623f, 0x61c5, 0x614b, 0x60d1, 0x6058, 0x5fdf, 0x5f66, 0x5eed, 0x5e75, 0x5dfd, 0x5d86, 0x5d0f,
|
||||
0x5c98, 0x5c22, 0x5bab, 0x5b35, 0x5ac0, 0x5a4b, 0x59d6, 0x5961, 0x58ed, 0x5879, 0x5805, 0x5791, 0x571e, 0x56ac, 0x5639, 0x55c7,
|
||||
0x5555, 0x54e3, 0x5472, 0x5401, 0x5390, 0x5320, 0x52af, 0x5240, 0x51d0, 0x5161, 0x50f2, 0x5083, 0x5015, 0x4fa6, 0x4f38, 0x4ecb,
|
||||
0x4e5e, 0x4df1, 0x4d84, 0x4d17, 0x4cab, 0x4c3f, 0x4bd3, 0x4b68, 0x4afd, 0x4a92, 0x4a27, 0x49bd, 0x4953, 0x48e9, 0x4880, 0x4817,
|
||||
0x47ae, 0x4745, 0x46dc, 0x4674, 0x460c, 0x45a5, 0x453d, 0x44d6, 0x446f, 0x4408, 0x43a2, 0x433c, 0x42d6, 0x4270, 0x420b, 0x41a6,
|
||||
0x4141, 0x40dc, 0x4078, 0x4014, 0x3fb0, 0x3f4c, 0x3ee8, 0x3e85, 0x3e22, 0x3dc0, 0x3d5d, 0x3cfb, 0x3c99, 0x3c37, 0x3bd6, 0x3b74,
|
||||
0x3b13, 0x3ab2, 0x3a52, 0x39f1, 0x3991, 0x3931, 0x38d2, 0x3872, 0x3813, 0x37b4, 0x3755, 0x36f7, 0x3698, 0x363a, 0x35dc, 0x357f,
|
||||
0x3521, 0x34c4, 0x3467, 0x340a, 0x33ae, 0x3351, 0x32f5, 0x3299, 0x323e, 0x31e2, 0x3187, 0x312c, 0x30d1, 0x3076, 0x301c, 0x2fc2,
|
||||
0x2f68, 0x2f0e, 0x2eb4, 0x2e5b, 0x2e02, 0x2da9, 0x2d50, 0x2cf8, 0x2c9f, 0x2c47, 0x2bef, 0x2b97, 0x2b40, 0x2ae8, 0x2a91, 0x2a3a,
|
||||
0x29e4, 0x298d, 0x2937, 0x28e0, 0x288b, 0x2835, 0x27df, 0x278a, 0x2735, 0x26e0, 0x268b, 0x2636, 0x25e2, 0x258d, 0x2539, 0x24e5,
|
||||
0x2492, 0x243e, 0x23eb, 0x2398, 0x2345, 0x22f2, 0x22a0, 0x224d, 0x21fb, 0x21a9, 0x2157, 0x2105, 0x20b4, 0x2063, 0x2012, 0x1fc1,
|
||||
0x1f70, 0x1f1f, 0x1ecf, 0x1e7f, 0x1e2e, 0x1ddf, 0x1d8f, 0x1d3f, 0x1cf0, 0x1ca1, 0x1c52, 0x1c03, 0x1bb4, 0x1b66, 0x1b17, 0x1ac9,
|
||||
0x1a7b, 0x1a2d, 0x19e0, 0x1992, 0x1945, 0x18f8, 0x18ab, 0x185e, 0x1811, 0x17c4, 0x1778, 0x172c, 0x16e0, 0x1694, 0x1648, 0x15fd,
|
||||
0x15b1, 0x1566, 0x151b, 0x14d0, 0x1485, 0x143b, 0x13f0, 0x13a6, 0x135c, 0x1312, 0x12c8, 0x127f, 0x1235, 0x11ec, 0x11a3, 0x1159,
|
||||
0x1111, 0x10c8, 0x107f, 0x1037, 0x0fef, 0x0fa6, 0x0f5e, 0x0f17, 0x0ecf, 0x0e87, 0x0e40, 0x0df9, 0x0db2, 0x0d6b, 0x0d24, 0x0cdd,
|
||||
0x0c97, 0x0c50, 0x0c0a, 0x0bc4, 0x0b7e, 0x0b38, 0x0af2, 0x0aad, 0x0a68, 0x0a22, 0x09dd, 0x0998, 0x0953, 0x090f, 0x08ca, 0x0886,
|
||||
0x0842, 0x07fd, 0x07b9, 0x0776, 0x0732, 0x06ee, 0x06ab, 0x0668, 0x0624, 0x05e1, 0x059e, 0x055c, 0x0519, 0x04d6, 0x0494, 0x0452,
|
||||
0x0410, 0x03ce, 0x038c, 0x034a, 0x0309, 0x02c7, 0x0286, 0x0245, 0x0204, 0x01c3, 0x0182, 0x0141, 0x0101, 0x00c0, 0x0080, 0x0040
|
||||
};
|
||||
0xffff, 0xff00, 0xfe01, 0xfd04, 0xfc07, 0xfb0c, 0xfa11, 0xf918, 0xf81f, 0xf727, 0xf631, 0xf53b, 0xf446, 0xf352,
|
||||
0xf25f, 0xf16d, 0xf07c, 0xef8b, 0xee9c, 0xedae, 0xecc0, 0xebd3, 0xeae8, 0xe9fd, 0xe913, 0xe829, 0xe741, 0xe65a,
|
||||
0xe573, 0xe48d, 0xe3a9, 0xe2c5, 0xe1e1, 0xe0ff, 0xe01e, 0xdf3d, 0xde5d, 0xdd7e, 0xdca0, 0xdbc2, 0xdae6, 0xda0a,
|
||||
0xd92f, 0xd854, 0xd77b, 0xd6a2, 0xd5ca, 0xd4f3, 0xd41d, 0xd347, 0xd272, 0xd19e, 0xd0cb, 0xcff8, 0xcf26, 0xce55,
|
||||
0xcd85, 0xccb5, 0xcbe6, 0xcb18, 0xca4b, 0xc97e, 0xc8b2, 0xc7e7, 0xc71c, 0xc652, 0xc589, 0xc4c0, 0xc3f8, 0xc331,
|
||||
0xc26b, 0xc1a5, 0xc0e0, 0xc01c, 0xbf58, 0xbe95, 0xbdd2, 0xbd10, 0xbc4f, 0xbb8f, 0xbacf, 0xba10, 0xb951, 0xb894,
|
||||
0xb7d6, 0xb71a, 0xb65e, 0xb5a2, 0xb4e8, 0xb42e, 0xb374, 0xb2bb, 0xb203, 0xb14b, 0xb094, 0xafde, 0xaf28, 0xae73,
|
||||
0xadbe, 0xad0a, 0xac57, 0xaba4, 0xaaf1, 0xaa40, 0xa98e, 0xa8de, 0xa82e, 0xa77e, 0xa6d0, 0xa621, 0xa574, 0xa4c6,
|
||||
0xa41a, 0xa36e, 0xa2c2, 0xa217, 0xa16d, 0xa0c3, 0xa01a, 0x9f71, 0x9ec8, 0x9e21, 0x9d79, 0x9cd3, 0x9c2d, 0x9b87,
|
||||
0x9ae2, 0x9a3d, 0x9999, 0x98f6, 0x9852, 0x97b0, 0x970e, 0x966c, 0x95cb, 0x952b, 0x948b, 0x93eb, 0x934c, 0x92ad,
|
||||
0x920f, 0x9172, 0x90d4, 0x9038, 0x8f9c, 0x8f00, 0x8e65, 0x8dca, 0x8d30, 0x8c96, 0x8bfc, 0x8b64, 0x8acb, 0x8a33,
|
||||
0x899c, 0x8904, 0x886e, 0x87d8, 0x8742, 0x86ad, 0x8618, 0x8583, 0x84f0, 0x845c, 0x83c9, 0x8336, 0x82a4, 0x8212,
|
||||
0x8181, 0x80f0, 0x8060, 0x7fd0, 0x7f40, 0x7eb1, 0x7e22, 0x7d93, 0x7d05, 0x7c78, 0x7beb, 0x7b5e, 0x7ad2, 0x7a46,
|
||||
0x79ba, 0x792f, 0x78a4, 0x781a, 0x7790, 0x7706, 0x767d, 0x75f5, 0x756c, 0x74e4, 0x745d, 0x73d5, 0x734f, 0x72c8,
|
||||
0x7242, 0x71bc, 0x7137, 0x70b2, 0x702e, 0x6fa9, 0x6f26, 0x6ea2, 0x6e1f, 0x6d9c, 0x6d1a, 0x6c98, 0x6c16, 0x6b95,
|
||||
0x6b14, 0x6a94, 0x6a13, 0x6993, 0x6914, 0x6895, 0x6816, 0x6798, 0x6719, 0x669c, 0x661e, 0x65a1, 0x6524, 0x64a8,
|
||||
0x642c, 0x63b0, 0x6335, 0x62ba, 0x623f, 0x61c5, 0x614b, 0x60d1, 0x6058, 0x5fdf, 0x5f66, 0x5eed, 0x5e75, 0x5dfd,
|
||||
0x5d86, 0x5d0f, 0x5c98, 0x5c22, 0x5bab, 0x5b35, 0x5ac0, 0x5a4b, 0x59d6, 0x5961, 0x58ed, 0x5879, 0x5805, 0x5791,
|
||||
0x571e, 0x56ac, 0x5639, 0x55c7, 0x5555, 0x54e3, 0x5472, 0x5401, 0x5390, 0x5320, 0x52af, 0x5240, 0x51d0, 0x5161,
|
||||
0x50f2, 0x5083, 0x5015, 0x4fa6, 0x4f38, 0x4ecb, 0x4e5e, 0x4df1, 0x4d84, 0x4d17, 0x4cab, 0x4c3f, 0x4bd3, 0x4b68,
|
||||
0x4afd, 0x4a92, 0x4a27, 0x49bd, 0x4953, 0x48e9, 0x4880, 0x4817, 0x47ae, 0x4745, 0x46dc, 0x4674, 0x460c, 0x45a5,
|
||||
0x453d, 0x44d6, 0x446f, 0x4408, 0x43a2, 0x433c, 0x42d6, 0x4270, 0x420b, 0x41a6, 0x4141, 0x40dc, 0x4078, 0x4014,
|
||||
0x3fb0, 0x3f4c, 0x3ee8, 0x3e85, 0x3e22, 0x3dc0, 0x3d5d, 0x3cfb, 0x3c99, 0x3c37, 0x3bd6, 0x3b74, 0x3b13, 0x3ab2,
|
||||
0x3a52, 0x39f1, 0x3991, 0x3931, 0x38d2, 0x3872, 0x3813, 0x37b4, 0x3755, 0x36f7, 0x3698, 0x363a, 0x35dc, 0x357f,
|
||||
0x3521, 0x34c4, 0x3467, 0x340a, 0x33ae, 0x3351, 0x32f5, 0x3299, 0x323e, 0x31e2, 0x3187, 0x312c, 0x30d1, 0x3076,
|
||||
0x301c, 0x2fc2, 0x2f68, 0x2f0e, 0x2eb4, 0x2e5b, 0x2e02, 0x2da9, 0x2d50, 0x2cf8, 0x2c9f, 0x2c47, 0x2bef, 0x2b97,
|
||||
0x2b40, 0x2ae8, 0x2a91, 0x2a3a, 0x29e4, 0x298d, 0x2937, 0x28e0, 0x288b, 0x2835, 0x27df, 0x278a, 0x2735, 0x26e0,
|
||||
0x268b, 0x2636, 0x25e2, 0x258d, 0x2539, 0x24e5, 0x2492, 0x243e, 0x23eb, 0x2398, 0x2345, 0x22f2, 0x22a0, 0x224d,
|
||||
0x21fb, 0x21a9, 0x2157, 0x2105, 0x20b4, 0x2063, 0x2012, 0x1fc1, 0x1f70, 0x1f1f, 0x1ecf, 0x1e7f, 0x1e2e, 0x1ddf,
|
||||
0x1d8f, 0x1d3f, 0x1cf0, 0x1ca1, 0x1c52, 0x1c03, 0x1bb4, 0x1b66, 0x1b17, 0x1ac9, 0x1a7b, 0x1a2d, 0x19e0, 0x1992,
|
||||
0x1945, 0x18f8, 0x18ab, 0x185e, 0x1811, 0x17c4, 0x1778, 0x172c, 0x16e0, 0x1694, 0x1648, 0x15fd, 0x15b1, 0x1566,
|
||||
0x151b, 0x14d0, 0x1485, 0x143b, 0x13f0, 0x13a6, 0x135c, 0x1312, 0x12c8, 0x127f, 0x1235, 0x11ec, 0x11a3, 0x1159,
|
||||
0x1111, 0x10c8, 0x107f, 0x1037, 0x0fef, 0x0fa6, 0x0f5e, 0x0f17, 0x0ecf, 0x0e87, 0x0e40, 0x0df9, 0x0db2, 0x0d6b,
|
||||
0x0d24, 0x0cdd, 0x0c97, 0x0c50, 0x0c0a, 0x0bc4, 0x0b7e, 0x0b38, 0x0af2, 0x0aad, 0x0a68, 0x0a22, 0x09dd, 0x0998,
|
||||
0x0953, 0x090f, 0x08ca, 0x0886, 0x0842, 0x07fd, 0x07b9, 0x0776, 0x0732, 0x06ee, 0x06ab, 0x0668, 0x0624, 0x05e1,
|
||||
0x059e, 0x055c, 0x0519, 0x04d6, 0x0494, 0x0452, 0x0410, 0x03ce, 0x038c, 0x034a, 0x0309, 0x02c7, 0x0286, 0x0245,
|
||||
0x0204, 0x01c3, 0x0182, 0x0141, 0x0101, 0x00c0, 0x0080, 0x0040};
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#include <core/RDP.hpp>
|
||||
#include <log.hpp>
|
||||
#include <core/RSP.hpp>
|
||||
#include <log.hpp>
|
||||
#include <parallel-rdp/ParallelRDPWrapper.hpp>
|
||||
|
||||
namespace n64 {
|
||||
RDP::RDP(Mem& mem, ParallelRDP& parallel) : mem(mem), parallel(parallel) {
|
||||
RDP::RDP(Mem &mem, ParallelRDP ¶llel) : mem(mem), parallel(parallel) {
|
||||
rdram.resize(RDRAM_SIZE);
|
||||
std::fill(rdram.begin(), rdram.end(), 0);
|
||||
memset(cmd_buf, 0, 0x100000);
|
||||
@@ -18,84 +18,106 @@ void RDP::Reset() {
|
||||
memset(cmd_buf, 0, 0x100000);
|
||||
}
|
||||
|
||||
template<> void RDP::WriteRDRAM<u8>(size_t idx, u8 v) {
|
||||
template <>
|
||||
void RDP::WriteRDRAM<u8>(size_t idx, u8 v) {
|
||||
size_t real = BYTE_ADDRESS(idx);
|
||||
if(real < RDRAM_SIZE) {
|
||||
if (real < RDRAM_SIZE) {
|
||||
rdram[real] = v;
|
||||
}
|
||||
}
|
||||
|
||||
template<> void RDP::WriteRDRAM<u16>(size_t idx, u16 v) {
|
||||
template <>
|
||||
void RDP::WriteRDRAM<u16>(size_t idx, u16 v) {
|
||||
size_t real = HALF_ADDRESS(idx);
|
||||
if(real < RDRAM_SIZE) {
|
||||
if (real < RDRAM_SIZE) {
|
||||
Util::WriteAccess<u16>(rdram, real, v);
|
||||
}
|
||||
}
|
||||
|
||||
template<> void RDP::WriteRDRAM<u32>(size_t idx, u32 v) {
|
||||
if(idx < RDRAM_SIZE) {
|
||||
template <>
|
||||
void RDP::WriteRDRAM<u32>(size_t idx, u32 v) {
|
||||
if (idx < RDRAM_SIZE) {
|
||||
Util::WriteAccess<u32>(rdram, idx, v);
|
||||
}
|
||||
}
|
||||
|
||||
template<> void RDP::WriteRDRAM<u64>(size_t idx, u64 v) {
|
||||
if(idx < RDRAM_SIZE) {
|
||||
template <>
|
||||
void RDP::WriteRDRAM<u64>(size_t idx, u64 v) {
|
||||
if (idx < RDRAM_SIZE) {
|
||||
Util::WriteAccess<u64>(rdram, idx, v);
|
||||
}
|
||||
}
|
||||
|
||||
template<> u8 RDP::ReadRDRAM<u8>(size_t idx) {
|
||||
template <>
|
||||
u8 RDP::ReadRDRAM<u8>(size_t idx) {
|
||||
size_t real = BYTE_ADDRESS(idx);
|
||||
if(real >= RDRAM_SIZE) return 0;
|
||||
if (real >= RDRAM_SIZE)
|
||||
return 0;
|
||||
return rdram[real];
|
||||
}
|
||||
|
||||
template<> u16 RDP::ReadRDRAM<u16>(size_t idx) {
|
||||
template <>
|
||||
u16 RDP::ReadRDRAM<u16>(size_t idx) {
|
||||
size_t real = HALF_ADDRESS(idx);
|
||||
if(real >= RDRAM_SIZE) return 0;
|
||||
if (real >= RDRAM_SIZE)
|
||||
return 0;
|
||||
return Util::ReadAccess<u16>(rdram, real);
|
||||
}
|
||||
|
||||
template<> u32 RDP::ReadRDRAM<u32>(size_t idx) {
|
||||
if(idx >= RDRAM_SIZE) return 0;
|
||||
template <>
|
||||
u32 RDP::ReadRDRAM<u32>(size_t idx) {
|
||||
if (idx >= RDRAM_SIZE)
|
||||
return 0;
|
||||
return Util::ReadAccess<u32>(rdram, idx);
|
||||
}
|
||||
|
||||
template<> u64 RDP::ReadRDRAM<u64>(size_t idx) {
|
||||
if(idx >= RDRAM_SIZE) return 0;
|
||||
template <>
|
||||
u64 RDP::ReadRDRAM<u64>(size_t idx) {
|
||||
if (idx >= RDRAM_SIZE)
|
||||
return 0;
|
||||
return Util::ReadAccess<u64>(rdram, idx);
|
||||
}
|
||||
|
||||
static const int cmd_lens[64] = {
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 8, 12, 24, 28, 24, 28, 40, 44,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 4, 4, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
|
||||
};
|
||||
static const int cmd_lens[64] = {2, 2, 2, 2, 2, 2, 2, 2, 8, 12, 24, 28, 24, 28, 40, 44, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 4, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2};
|
||||
|
||||
auto RDP::Read(u32 addr) const -> u32 {
|
||||
switch(addr) {
|
||||
case 0x04100000: return dpc.start;
|
||||
case 0x04100004: return dpc.end;
|
||||
case 0x04100008: return dpc.current;
|
||||
case 0x0410000C:
|
||||
return dpc.status.raw;
|
||||
case 0x04100010: return dpc.clock;
|
||||
case 0x04100014: return dpc.status.cmdBusy;
|
||||
case 0x04100018: return dpc.status.pipeBusy;
|
||||
case 0x0410001C: return dpc.tmem;
|
||||
default:
|
||||
Util::panic("Unhandled DP Command Registers read (addr: {:08X})", addr);
|
||||
switch (addr) {
|
||||
case 0x04100000:
|
||||
return dpc.start;
|
||||
case 0x04100004:
|
||||
return dpc.end;
|
||||
case 0x04100008:
|
||||
return dpc.current;
|
||||
case 0x0410000C:
|
||||
return dpc.status.raw;
|
||||
case 0x04100010:
|
||||
return dpc.clock;
|
||||
case 0x04100014:
|
||||
return dpc.status.cmdBusy;
|
||||
case 0x04100018:
|
||||
return dpc.status.pipeBusy;
|
||||
case 0x0410001C:
|
||||
return dpc.tmem;
|
||||
default:
|
||||
Util::panic("Unhandled DP Command Registers read (addr: {:08X})", addr);
|
||||
}
|
||||
}
|
||||
|
||||
void RDP::Write(u32 addr, u32 val) {
|
||||
switch(addr) {
|
||||
case 0x04100000: WriteStart(val); break;
|
||||
case 0x04100004: WriteEnd(val); break;
|
||||
case 0x0410000C: WriteStatus(val); break;
|
||||
default:
|
||||
Util::panic("Unhandled DP Command Registers write (addr: {:08X}, val: {:08X})", addr, val);
|
||||
switch (addr) {
|
||||
case 0x04100000:
|
||||
WriteStart(val);
|
||||
break;
|
||||
case 0x04100004:
|
||||
WriteEnd(val);
|
||||
break;
|
||||
case 0x0410000C:
|
||||
WriteStatus(val);
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unhandled DP Command Registers write (addr: {:08X}, val: {:08X})", addr, val);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,20 +126,21 @@ void RDP::WriteStatus(u32 val) {
|
||||
temp.raw = val;
|
||||
|
||||
CLEAR_SET(dpc.status.xbusDmemDma, temp.clearXbusDmemDma, temp.setXbusDmemDma);
|
||||
if(temp.clearFreeze) {
|
||||
if (temp.clearFreeze) {
|
||||
dpc.status.freeze = false;
|
||||
}
|
||||
|
||||
if(temp.setFreeze) {
|
||||
if (temp.setFreeze) {
|
||||
dpc.status.freeze = true;
|
||||
}
|
||||
CLEAR_SET(dpc.status.flush, temp.clearFlush, temp.setFlush);
|
||||
CLEAR_SET(dpc.status.cmdBusy, temp.clearCmd, false);
|
||||
if(temp.clearClock) dpc.clock = 0;
|
||||
if (temp.clearClock)
|
||||
dpc.clock = 0;
|
||||
CLEAR_SET(dpc.status.pipeBusy, temp.clearPipe, false);
|
||||
CLEAR_SET(dpc.status.tmemBusy, temp.clearTmem, false);
|
||||
|
||||
if(!dpc.status.freeze) {
|
||||
if (!dpc.status.freeze) {
|
||||
RunCommand();
|
||||
}
|
||||
}
|
||||
@@ -169,7 +192,7 @@ void RDP::RunCommand() {
|
||||
}
|
||||
dpc.status.pipeBusy = true;
|
||||
dpc.status.startGclk = true;
|
||||
if(dpc.end > dpc.current) {
|
||||
if (dpc.end > dpc.current) {
|
||||
dpc.status.freeze = true;
|
||||
|
||||
static int remaining_cmds = 0;
|
||||
@@ -178,7 +201,8 @@ void RDP::RunCommand() {
|
||||
const u32 end = dpc.end & 0xFFFFF8;
|
||||
|
||||
int len = end - current;
|
||||
if (len <= 0) return;
|
||||
if (len <= 0)
|
||||
return;
|
||||
|
||||
if (len + (remaining_cmds * 4) > 0xFFFFF) {
|
||||
Util::panic("Too many RDP commands");
|
||||
@@ -255,4 +279,4 @@ void RDP::OnFullSync() {
|
||||
dpc.status.cbufReady = false;
|
||||
mem.mmio.mi.InterruptRaise(MI::Interrupt::DP);
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -13,32 +13,32 @@ struct Registers;
|
||||
union DPCStatusWrite {
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned clearXbusDmemDma:1;
|
||||
unsigned setXbusDmemDma:1;
|
||||
unsigned clearFreeze:1;
|
||||
unsigned setFreeze:1;
|
||||
unsigned clearFlush:1;
|
||||
unsigned setFlush:1;
|
||||
unsigned clearTmem:1;
|
||||
unsigned clearPipe:1;
|
||||
unsigned clearCmd:1;
|
||||
unsigned clearClock:1;
|
||||
unsigned clearXbusDmemDma : 1;
|
||||
unsigned setXbusDmemDma : 1;
|
||||
unsigned clearFreeze : 1;
|
||||
unsigned setFreeze : 1;
|
||||
unsigned clearFlush : 1;
|
||||
unsigned setFlush : 1;
|
||||
unsigned clearTmem : 1;
|
||||
unsigned clearPipe : 1;
|
||||
unsigned clearCmd : 1;
|
||||
unsigned clearClock : 1;
|
||||
};
|
||||
};
|
||||
|
||||
union DPCStatus {
|
||||
struct {
|
||||
unsigned xbusDmemDma:1;
|
||||
unsigned freeze:1;
|
||||
unsigned flush:1;
|
||||
unsigned startGclk:1;
|
||||
unsigned tmemBusy:1;
|
||||
unsigned pipeBusy:1;
|
||||
unsigned cmdBusy:1;
|
||||
unsigned cbufReady:1;
|
||||
unsigned dmaBusy:1;
|
||||
unsigned endValid:1;
|
||||
unsigned startValid:1;
|
||||
unsigned xbusDmemDma : 1;
|
||||
unsigned freeze : 1;
|
||||
unsigned flush : 1;
|
||||
unsigned startGclk : 1;
|
||||
unsigned tmemBusy : 1;
|
||||
unsigned pipeBusy : 1;
|
||||
unsigned cmdBusy : 1;
|
||||
unsigned cbufReady : 1;
|
||||
unsigned dmaBusy : 1;
|
||||
unsigned endValid : 1;
|
||||
unsigned startValid : 1;
|
||||
};
|
||||
u32 raw;
|
||||
};
|
||||
@@ -56,7 +56,7 @@ struct RDP {
|
||||
DPC dpc{};
|
||||
u32 cmd_buf[0xFFFFF]{};
|
||||
|
||||
RDP(Mem&, ParallelRDP&);
|
||||
RDP(Mem &, ParallelRDP &);
|
||||
void Reset();
|
||||
|
||||
[[nodiscard]] auto Read(u32 addr) const -> u32;
|
||||
@@ -66,7 +66,7 @@ struct RDP {
|
||||
void OnFullSync();
|
||||
|
||||
FORCE_INLINE void WriteStart(u32 val) {
|
||||
if(!dpc.status.startValid) {
|
||||
if (!dpc.status.startValid) {
|
||||
dpc.start = val & 0xFFFFF8;
|
||||
}
|
||||
dpc.status.startValid = true;
|
||||
@@ -74,23 +74,24 @@ struct RDP {
|
||||
|
||||
FORCE_INLINE void WriteEnd(u32 val) {
|
||||
dpc.end = val & 0xFFFFF8;
|
||||
if(dpc.status.startValid) {
|
||||
if (dpc.status.startValid) {
|
||||
dpc.current = dpc.start;
|
||||
dpc.status.startValid = false;
|
||||
}
|
||||
RunCommand();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
void WriteRDRAM(size_t, T);
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
T ReadRDRAM(size_t);
|
||||
|
||||
private:
|
||||
friend struct Mem;
|
||||
friend struct MMIO;
|
||||
std::vector<u8> rdram{};
|
||||
|
||||
Mem& mem;
|
||||
ParallelRDP& parallel;
|
||||
Mem &mem;
|
||||
ParallelRDP ∥
|
||||
};
|
||||
} // backend
|
||||
} // namespace n64
|
||||
|
||||
@@ -177,14 +177,14 @@ void RSP::Write(u32 addr, u32 val) {
|
||||
switch (addr) {
|
||||
case 0x04040000: spDMASPAddr.raw = val & 0x1FF8; break;
|
||||
case 0x04040004: spDMADRAMAddr.raw = val & 0xFFFFFC; break;
|
||||
case 0x04040008: {
|
||||
case 0x04040008:
|
||||
spDMALen.raw = val;
|
||||
DMA<false>();
|
||||
} break;
|
||||
case 0x0404000C: {
|
||||
break;
|
||||
case 0x0404000C:
|
||||
spDMALen.raw = val;
|
||||
DMA<true>();
|
||||
} break;
|
||||
break;
|
||||
case 0x04040010: WriteStatus(val); break;
|
||||
case 0x0404001C: ReleaseSemaphore(); break;
|
||||
case 0x04080000:
|
||||
|
||||
@@ -1,93 +1,103 @@
|
||||
#pragma once
|
||||
#include <core/mmio/MI.hpp>
|
||||
#include <core/RDP.hpp>
|
||||
#include <MemoryRegions.hpp>
|
||||
#include <MemoryHelpers.hpp>
|
||||
#include <MemoryRegions.hpp>
|
||||
#include <array>
|
||||
#include <core/RDP.hpp>
|
||||
#include <core/mmio/MI.hpp>
|
||||
|
||||
#define RSP_BYTE(addr) (dmem[BYTE_ADDRESS(addr) & 0xFFF])
|
||||
#define GET_RSP_HALF(addr) ((RSP_BYTE(addr) << 8) | RSP_BYTE((addr) + 1))
|
||||
#define SET_RSP_HALF(addr, value) do { RSP_BYTE(addr) = ((value) >> 8) & 0xFF; RSP_BYTE((addr) + 1) = (value) & 0xFF;} while(0)
|
||||
#define SET_RSP_HALF(addr, value) \
|
||||
do { \
|
||||
RSP_BYTE(addr) = ((value) >> 8) & 0xFF; \
|
||||
RSP_BYTE((addr) + 1) = (value) & 0xFF; \
|
||||
} \
|
||||
while (0)
|
||||
#define GET_RSP_WORD(addr) ((GET_RSP_HALF(addr) << 16) | GET_RSP_HALF((addr) + 2))
|
||||
#define SET_RSP_WORD(addr, value) do { SET_RSP_HALF(addr, ((value) >> 16) & 0xFFFF); SET_RSP_HALF((addr) + 2, (value) & 0xFFFF);} while(0)
|
||||
#define SET_RSP_WORD(addr, value) \
|
||||
do { \
|
||||
SET_RSP_HALF(addr, ((value) >> 16) & 0xFFFF); \
|
||||
SET_RSP_HALF((addr) + 2, (value) & 0xFFFF); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
namespace n64 {
|
||||
union SPStatus {
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned halt:1;
|
||||
unsigned broke:1;
|
||||
unsigned dmaBusy:1;
|
||||
unsigned dmaFull:1;
|
||||
unsigned ioFull:1;
|
||||
unsigned singleStep:1;
|
||||
unsigned interruptOnBreak:1;
|
||||
unsigned signal0:1;
|
||||
unsigned signal1:1;
|
||||
unsigned signal2:1;
|
||||
unsigned signal3:1;
|
||||
unsigned signal4:1;
|
||||
unsigned signal5:1;
|
||||
unsigned signal6:1;
|
||||
unsigned signal7:1;
|
||||
unsigned:17;
|
||||
unsigned halt : 1;
|
||||
unsigned broke : 1;
|
||||
unsigned dmaBusy : 1;
|
||||
unsigned dmaFull : 1;
|
||||
unsigned ioFull : 1;
|
||||
unsigned singleStep : 1;
|
||||
unsigned interruptOnBreak : 1;
|
||||
unsigned signal0 : 1;
|
||||
unsigned signal1 : 1;
|
||||
unsigned signal2 : 1;
|
||||
unsigned signal3 : 1;
|
||||
unsigned signal4 : 1;
|
||||
unsigned signal5 : 1;
|
||||
unsigned signal6 : 1;
|
||||
unsigned signal7 : 1;
|
||||
unsigned : 17;
|
||||
};
|
||||
};
|
||||
|
||||
union SPStatusWrite {
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned clearHalt:1;
|
||||
unsigned setHalt:1;
|
||||
unsigned clearBroke:1;
|
||||
unsigned clearIntr:1;
|
||||
unsigned setIntr:1;
|
||||
unsigned clearSstep:1;
|
||||
unsigned setSstep:1;
|
||||
unsigned clearIntrOnBreak:1;
|
||||
unsigned setIntrOnBreak:1;
|
||||
unsigned clearSignal0:1;
|
||||
unsigned setSignal0:1;
|
||||
unsigned clearSignal1:1;
|
||||
unsigned setSignal1:1;
|
||||
unsigned clearSignal2:1;
|
||||
unsigned setSignal2:1;
|
||||
unsigned clearSignal3:1;
|
||||
unsigned setSignal3:1;
|
||||
unsigned clearSignal4:1;
|
||||
unsigned setSignal4:1;
|
||||
unsigned clearSignal5:1;
|
||||
unsigned setSignal5:1;
|
||||
unsigned clearSignal6:1;
|
||||
unsigned setSignal6:1;
|
||||
unsigned clearSignal7:1;
|
||||
unsigned setSignal7:1;
|
||||
unsigned:7;
|
||||
unsigned clearHalt : 1;
|
||||
unsigned setHalt : 1;
|
||||
unsigned clearBroke : 1;
|
||||
unsigned clearIntr : 1;
|
||||
unsigned setIntr : 1;
|
||||
unsigned clearSstep : 1;
|
||||
unsigned setSstep : 1;
|
||||
unsigned clearIntrOnBreak : 1;
|
||||
unsigned setIntrOnBreak : 1;
|
||||
unsigned clearSignal0 : 1;
|
||||
unsigned setSignal0 : 1;
|
||||
unsigned clearSignal1 : 1;
|
||||
unsigned setSignal1 : 1;
|
||||
unsigned clearSignal2 : 1;
|
||||
unsigned setSignal2 : 1;
|
||||
unsigned clearSignal3 : 1;
|
||||
unsigned setSignal3 : 1;
|
||||
unsigned clearSignal4 : 1;
|
||||
unsigned setSignal4 : 1;
|
||||
unsigned clearSignal5 : 1;
|
||||
unsigned setSignal5 : 1;
|
||||
unsigned clearSignal6 : 1;
|
||||
unsigned setSignal6 : 1;
|
||||
unsigned clearSignal7 : 1;
|
||||
unsigned setSignal7 : 1;
|
||||
unsigned : 7;
|
||||
};
|
||||
};
|
||||
|
||||
union SPDMALen {
|
||||
struct {
|
||||
unsigned len:12;
|
||||
unsigned count:8;
|
||||
unsigned skip:12;
|
||||
unsigned len : 12;
|
||||
unsigned count : 8;
|
||||
unsigned skip : 12;
|
||||
};
|
||||
u32 raw;
|
||||
};
|
||||
|
||||
union SPDMASPAddr {
|
||||
struct {
|
||||
unsigned address:12;
|
||||
unsigned bank:1;
|
||||
unsigned: 19;
|
||||
unsigned address : 12;
|
||||
unsigned bank : 1;
|
||||
unsigned : 19;
|
||||
};
|
||||
u32 raw;
|
||||
};
|
||||
|
||||
union SPDMADRAMAddr {
|
||||
struct {
|
||||
unsigned address:24;
|
||||
unsigned:8;
|
||||
unsigned address : 24;
|
||||
unsigned : 8;
|
||||
};
|
||||
u32 raw;
|
||||
};
|
||||
@@ -106,13 +116,17 @@ struct Mem;
|
||||
struct Registers;
|
||||
|
||||
#define DE(x) (((x) >> 11) & 0x1F)
|
||||
#define CLEAR_SET(val, clear, set) do { \
|
||||
if(clear && !set) (val) = 0; \
|
||||
if(set && !clear) (val) = 1; \
|
||||
} while(0)
|
||||
#define CLEAR_SET(val, clear, set) \
|
||||
do { \
|
||||
if (clear && !set) \
|
||||
(val) = 0; \
|
||||
if (set && !clear) \
|
||||
(val) = 1; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
struct RSP {
|
||||
RSP(Mem&, Registers&);
|
||||
RSP(Mem &, Registers &);
|
||||
void Reset();
|
||||
|
||||
FORCE_INLINE void Step() {
|
||||
@@ -161,10 +175,10 @@ struct RSP {
|
||||
|
||||
FORCE_INLINE s64 GetACC(int e) const {
|
||||
s64 val = u64(acc.h.element[e]) << 32;
|
||||
val |= u64(acc.m.element[e]) << 16;
|
||||
val |= u64(acc.l.element[e]) << 00;
|
||||
if((val & 0x0000800000000000) != 0) {
|
||||
val |= 0xFFFF000000000000;
|
||||
val |= u64(acc.m.element[e]) << 16;
|
||||
val |= u64(acc.l.element[e]) << 00;
|
||||
if ((val & 0x0000800000000000) != 0) {
|
||||
val |= 0xFFFF000000000000;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
@@ -199,7 +213,7 @@ struct RSP {
|
||||
|
||||
FORCE_INLINE u8 GetVCE() const {
|
||||
u8 value = 0;
|
||||
for(int i = 0; i < 8; i++) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
bool l = vce.element[ELEMENT_INDEX(i)] != 0;
|
||||
value |= (l << i);
|
||||
}
|
||||
@@ -237,7 +251,7 @@ struct RSP {
|
||||
}
|
||||
|
||||
FORCE_INLINE bool AcquireSemaphore() {
|
||||
if(semaphore) {
|
||||
if (semaphore) {
|
||||
return true;
|
||||
} else {
|
||||
semaphore = true;
|
||||
@@ -245,9 +259,7 @@ struct RSP {
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE void ReleaseSemaphore() {
|
||||
semaphore = false;
|
||||
}
|
||||
FORCE_INLINE void ReleaseSemaphore() { semaphore = false; }
|
||||
|
||||
void add(u32 instr);
|
||||
void addi(u32 instr);
|
||||
@@ -352,7 +364,7 @@ struct RSP {
|
||||
void vor(u32 instr);
|
||||
void vnor(u32 instr);
|
||||
void vzero(u32 instr);
|
||||
void mfc0(RDP& rdp, u32 instr);
|
||||
void mfc0(RDP &rdp, u32 instr);
|
||||
void mtc0(u32 instr);
|
||||
void mfc2(u32 instr);
|
||||
void mtc2(u32 instr);
|
||||
@@ -360,17 +372,18 @@ struct RSP {
|
||||
template <bool toRdram>
|
||||
void DMA();
|
||||
void WriteStatus(u32 value);
|
||||
|
||||
private:
|
||||
Registers& regs;
|
||||
Mem& mem;
|
||||
Registers ®s;
|
||||
Mem &mem;
|
||||
FORCE_INLINE void branch(u16 address, bool cond) {
|
||||
if(cond) {
|
||||
if (cond) {
|
||||
nextPC = address & 0xFFC;
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE void branch_likely(u16 address, bool cond) {
|
||||
if(cond) {
|
||||
if (cond) {
|
||||
nextPC = address & 0xFFC;
|
||||
} else {
|
||||
pc = nextPC & 0xFFC;
|
||||
@@ -378,4 +391,4 @@ private:
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -2,36 +2,40 @@
|
||||
#include <common.hpp>
|
||||
|
||||
static constexpr u16 rsqRom[] = {
|
||||
0xffff, 0xff00, 0xfe02, 0xfd06, 0xfc0b, 0xfb12, 0xfa1a, 0xf923, 0xf82e, 0xf73b, 0xf648, 0xf557, 0xf467, 0xf379, 0xf28c, 0xf1a0,
|
||||
0xf0b6, 0xefcd, 0xeee5, 0xedff, 0xed19, 0xec35, 0xeb52, 0xea71, 0xe990, 0xe8b1, 0xe7d3, 0xe6f6, 0xe61b, 0xe540, 0xe467, 0xe38e,
|
||||
0xe2b7, 0xe1e1, 0xe10d, 0xe039, 0xdf66, 0xde94, 0xddc4, 0xdcf4, 0xdc26, 0xdb59, 0xda8c, 0xd9c1, 0xd8f7, 0xd82d, 0xd765, 0xd69e,
|
||||
0xd5d7, 0xd512, 0xd44e, 0xd38a, 0xd2c8, 0xd206, 0xd146, 0xd086, 0xcfc7, 0xcf0a, 0xce4d, 0xcd91, 0xccd6, 0xcc1b, 0xcb62, 0xcaa9,
|
||||
0xc9f2, 0xc93b, 0xc885, 0xc7d0, 0xc71c, 0xc669, 0xc5b6, 0xc504, 0xc453, 0xc3a3, 0xc2f4, 0xc245, 0xc198, 0xc0eb, 0xc03f, 0xbf93,
|
||||
0xbee9, 0xbe3f, 0xbd96, 0xbced, 0xbc46, 0xbb9f, 0xbaf8, 0xba53, 0xb9ae, 0xb90a, 0xb867, 0xb7c5, 0xb723, 0xb681, 0xb5e1, 0xb541,
|
||||
0xb4a2, 0xb404, 0xb366, 0xb2c9, 0xb22c, 0xb191, 0xb0f5, 0xb05b, 0xafc1, 0xaf28, 0xae8f, 0xadf7, 0xad60, 0xacc9, 0xac33, 0xab9e,
|
||||
0xab09, 0xaa75, 0xa9e1, 0xa94e, 0xa8bc, 0xa82a, 0xa799, 0xa708, 0xa678, 0xa5e8, 0xa559, 0xa4cb, 0xa43d, 0xa3b0, 0xa323, 0xa297,
|
||||
0xa20b, 0xa180, 0xa0f6, 0xa06c, 0x9fe2, 0x9f59, 0x9ed1, 0x9e49, 0x9dc2, 0x9d3b, 0x9cb4, 0x9c2f, 0x9ba9, 0x9b25, 0x9aa0, 0x9a1c,
|
||||
0x9999, 0x9916, 0x9894, 0x9812, 0x9791, 0x9710, 0x968f, 0x960f, 0x9590, 0x9511, 0x9492, 0x9414, 0x9397, 0x931a, 0x929d, 0x9221,
|
||||
0x91a5, 0x9129, 0x90af, 0x9034, 0x8fba, 0x8f40, 0x8ec7, 0x8e4f, 0x8dd6, 0x8d5e, 0x8ce7, 0x8c70, 0x8bf9, 0x8b83, 0x8b0d, 0x8a98,
|
||||
0x8a23, 0x89ae, 0x893a, 0x88c6, 0x8853, 0x87e0, 0x876d, 0x86fb, 0x8689, 0x8618, 0x85a7, 0x8536, 0x84c6, 0x8456, 0x83e7, 0x8377,
|
||||
0x8309, 0x829a, 0x822c, 0x81bf, 0x8151, 0x80e4, 0x8078, 0x800c, 0x7fa0, 0x7f34, 0x7ec9, 0x7e5e, 0x7df4, 0x7d8a, 0x7d20, 0x7cb6,
|
||||
0x7c4d, 0x7be5, 0x7b7c, 0x7b14, 0x7aac, 0x7a45, 0x79de, 0x7977, 0x7911, 0x78ab, 0x7845, 0x77df, 0x777a, 0x7715, 0x76b1, 0x764d,
|
||||
0x75e9, 0x7585, 0x7522, 0x74bf, 0x745d, 0x73fa, 0x7398, 0x7337, 0x72d5, 0x7274, 0x7213, 0x71b3, 0x7152, 0x70f2, 0x7093, 0x7033,
|
||||
0x6fd4, 0x6f76, 0x6f17, 0x6eb9, 0x6e5b, 0x6dfd, 0x6da0, 0x6d43, 0x6ce6, 0x6c8a, 0x6c2d, 0x6bd1, 0x6b76, 0x6b1a, 0x6abf, 0x6a64,
|
||||
0x6a09, 0x6955, 0x68a1, 0x67ef, 0x673e, 0x668d, 0x65de, 0x6530, 0x6482, 0x63d6, 0x632b, 0x6280, 0x61d7, 0x612e, 0x6087, 0x5fe0,
|
||||
0x5f3a, 0x5e95, 0x5df1, 0x5d4e, 0x5cac, 0x5c0b, 0x5b6b, 0x5acb, 0x5a2c, 0x598f, 0x58f2, 0x5855, 0x57ba, 0x5720, 0x5686, 0x55ed,
|
||||
0x5555, 0x54be, 0x5427, 0x5391, 0x52fc, 0x5268, 0x51d5, 0x5142, 0x50b0, 0x501f, 0x4f8e, 0x4efe, 0x4e6f, 0x4de1, 0x4d53, 0x4cc6,
|
||||
0x4c3a, 0x4baf, 0x4b24, 0x4a9a, 0x4a10, 0x4987, 0x48ff, 0x4878, 0x47f1, 0x476b, 0x46e5, 0x4660, 0x45dc, 0x4558, 0x44d5, 0x4453,
|
||||
0x43d1, 0x434f, 0x42cf, 0x424f, 0x41cf, 0x4151, 0x40d2, 0x4055, 0x3fd8, 0x3f5b, 0x3edf, 0x3e64, 0x3de9, 0x3d6e, 0x3cf5, 0x3c7c,
|
||||
0x3c03, 0x3b8b, 0x3b13, 0x3a9c, 0x3a26, 0x39b0, 0x393a, 0x38c5, 0x3851, 0x37dd, 0x3769, 0x36f6, 0x3684, 0x3612, 0x35a0, 0x352f,
|
||||
0x34bf, 0x344f, 0x33df, 0x3370, 0x3302, 0x3293, 0x3226, 0x31b9, 0x314c, 0x30df, 0x3074, 0x3008, 0x2f9d, 0x2f33, 0x2ec8, 0x2e5f,
|
||||
0x2df6, 0x2d8d, 0x2d24, 0x2cbc, 0x2c55, 0x2bee, 0x2b87, 0x2b21, 0x2abb, 0x2a55, 0x29f0, 0x298b, 0x2927, 0x28c3, 0x2860, 0x27fd,
|
||||
0x279a, 0x2738, 0x26d6, 0x2674, 0x2613, 0x25b2, 0x2552, 0x24f2, 0x2492, 0x2432, 0x23d3, 0x2375, 0x2317, 0x22b9, 0x225b, 0x21fe,
|
||||
0x21a1, 0x2145, 0x20e8, 0x208d, 0x2031, 0x1fd6, 0x1f7b, 0x1f21, 0x1ec7, 0x1e6d, 0x1e13, 0x1dba, 0x1d61, 0x1d09, 0x1cb1, 0x1c59,
|
||||
0x1c01, 0x1baa, 0x1b53, 0x1afc, 0x1aa6, 0x1a50, 0x19fa, 0x19a5, 0x1950, 0x18fb, 0x18a7, 0x1853, 0x17ff, 0x17ab, 0x1758, 0x1705,
|
||||
0x16b2, 0x1660, 0x160d, 0x15bc, 0x156a, 0x1519, 0x14c8, 0x1477, 0x1426, 0x13d6, 0x1386, 0x1337, 0x12e7, 0x1298, 0x1249, 0x11fb,
|
||||
0x11ac, 0x115e, 0x1111, 0x10c3, 0x1076, 0x1029, 0x0fdc, 0x0f8f, 0x0f43, 0x0ef7, 0x0eab, 0x0e60, 0x0e15, 0x0dca, 0x0d7f, 0x0d34,
|
||||
0x0cea, 0x0ca0, 0x0c56, 0x0c0c, 0x0bc3, 0x0b7a, 0x0b31, 0x0ae8, 0x0aa0, 0x0a58, 0x0a10, 0x09c8, 0x0981, 0x0939, 0x08f2, 0x08ab,
|
||||
0x0865, 0x081e, 0x07d8, 0x0792, 0x074d, 0x0707, 0x06c2, 0x067d, 0x0638, 0x05f3, 0x05af, 0x056a, 0x0526, 0x04e2, 0x049f, 0x045b,
|
||||
0x0418, 0x03d5, 0x0392, 0x0350, 0x030d, 0x02cb, 0x0289, 0x0247, 0x0206, 0x01c4, 0x0183, 0x0142, 0x0101, 0x00c0, 0x0080, 0x0040
|
||||
};
|
||||
0xffff, 0xff00, 0xfe02, 0xfd06, 0xfc0b, 0xfb12, 0xfa1a, 0xf923, 0xf82e, 0xf73b, 0xf648, 0xf557, 0xf467, 0xf379,
|
||||
0xf28c, 0xf1a0, 0xf0b6, 0xefcd, 0xeee5, 0xedff, 0xed19, 0xec35, 0xeb52, 0xea71, 0xe990, 0xe8b1, 0xe7d3, 0xe6f6,
|
||||
0xe61b, 0xe540, 0xe467, 0xe38e, 0xe2b7, 0xe1e1, 0xe10d, 0xe039, 0xdf66, 0xde94, 0xddc4, 0xdcf4, 0xdc26, 0xdb59,
|
||||
0xda8c, 0xd9c1, 0xd8f7, 0xd82d, 0xd765, 0xd69e, 0xd5d7, 0xd512, 0xd44e, 0xd38a, 0xd2c8, 0xd206, 0xd146, 0xd086,
|
||||
0xcfc7, 0xcf0a, 0xce4d, 0xcd91, 0xccd6, 0xcc1b, 0xcb62, 0xcaa9, 0xc9f2, 0xc93b, 0xc885, 0xc7d0, 0xc71c, 0xc669,
|
||||
0xc5b6, 0xc504, 0xc453, 0xc3a3, 0xc2f4, 0xc245, 0xc198, 0xc0eb, 0xc03f, 0xbf93, 0xbee9, 0xbe3f, 0xbd96, 0xbced,
|
||||
0xbc46, 0xbb9f, 0xbaf8, 0xba53, 0xb9ae, 0xb90a, 0xb867, 0xb7c5, 0xb723, 0xb681, 0xb5e1, 0xb541, 0xb4a2, 0xb404,
|
||||
0xb366, 0xb2c9, 0xb22c, 0xb191, 0xb0f5, 0xb05b, 0xafc1, 0xaf28, 0xae8f, 0xadf7, 0xad60, 0xacc9, 0xac33, 0xab9e,
|
||||
0xab09, 0xaa75, 0xa9e1, 0xa94e, 0xa8bc, 0xa82a, 0xa799, 0xa708, 0xa678, 0xa5e8, 0xa559, 0xa4cb, 0xa43d, 0xa3b0,
|
||||
0xa323, 0xa297, 0xa20b, 0xa180, 0xa0f6, 0xa06c, 0x9fe2, 0x9f59, 0x9ed1, 0x9e49, 0x9dc2, 0x9d3b, 0x9cb4, 0x9c2f,
|
||||
0x9ba9, 0x9b25, 0x9aa0, 0x9a1c, 0x9999, 0x9916, 0x9894, 0x9812, 0x9791, 0x9710, 0x968f, 0x960f, 0x9590, 0x9511,
|
||||
0x9492, 0x9414, 0x9397, 0x931a, 0x929d, 0x9221, 0x91a5, 0x9129, 0x90af, 0x9034, 0x8fba, 0x8f40, 0x8ec7, 0x8e4f,
|
||||
0x8dd6, 0x8d5e, 0x8ce7, 0x8c70, 0x8bf9, 0x8b83, 0x8b0d, 0x8a98, 0x8a23, 0x89ae, 0x893a, 0x88c6, 0x8853, 0x87e0,
|
||||
0x876d, 0x86fb, 0x8689, 0x8618, 0x85a7, 0x8536, 0x84c6, 0x8456, 0x83e7, 0x8377, 0x8309, 0x829a, 0x822c, 0x81bf,
|
||||
0x8151, 0x80e4, 0x8078, 0x800c, 0x7fa0, 0x7f34, 0x7ec9, 0x7e5e, 0x7df4, 0x7d8a, 0x7d20, 0x7cb6, 0x7c4d, 0x7be5,
|
||||
0x7b7c, 0x7b14, 0x7aac, 0x7a45, 0x79de, 0x7977, 0x7911, 0x78ab, 0x7845, 0x77df, 0x777a, 0x7715, 0x76b1, 0x764d,
|
||||
0x75e9, 0x7585, 0x7522, 0x74bf, 0x745d, 0x73fa, 0x7398, 0x7337, 0x72d5, 0x7274, 0x7213, 0x71b3, 0x7152, 0x70f2,
|
||||
0x7093, 0x7033, 0x6fd4, 0x6f76, 0x6f17, 0x6eb9, 0x6e5b, 0x6dfd, 0x6da0, 0x6d43, 0x6ce6, 0x6c8a, 0x6c2d, 0x6bd1,
|
||||
0x6b76, 0x6b1a, 0x6abf, 0x6a64, 0x6a09, 0x6955, 0x68a1, 0x67ef, 0x673e, 0x668d, 0x65de, 0x6530, 0x6482, 0x63d6,
|
||||
0x632b, 0x6280, 0x61d7, 0x612e, 0x6087, 0x5fe0, 0x5f3a, 0x5e95, 0x5df1, 0x5d4e, 0x5cac, 0x5c0b, 0x5b6b, 0x5acb,
|
||||
0x5a2c, 0x598f, 0x58f2, 0x5855, 0x57ba, 0x5720, 0x5686, 0x55ed, 0x5555, 0x54be, 0x5427, 0x5391, 0x52fc, 0x5268,
|
||||
0x51d5, 0x5142, 0x50b0, 0x501f, 0x4f8e, 0x4efe, 0x4e6f, 0x4de1, 0x4d53, 0x4cc6, 0x4c3a, 0x4baf, 0x4b24, 0x4a9a,
|
||||
0x4a10, 0x4987, 0x48ff, 0x4878, 0x47f1, 0x476b, 0x46e5, 0x4660, 0x45dc, 0x4558, 0x44d5, 0x4453, 0x43d1, 0x434f,
|
||||
0x42cf, 0x424f, 0x41cf, 0x4151, 0x40d2, 0x4055, 0x3fd8, 0x3f5b, 0x3edf, 0x3e64, 0x3de9, 0x3d6e, 0x3cf5, 0x3c7c,
|
||||
0x3c03, 0x3b8b, 0x3b13, 0x3a9c, 0x3a26, 0x39b0, 0x393a, 0x38c5, 0x3851, 0x37dd, 0x3769, 0x36f6, 0x3684, 0x3612,
|
||||
0x35a0, 0x352f, 0x34bf, 0x344f, 0x33df, 0x3370, 0x3302, 0x3293, 0x3226, 0x31b9, 0x314c, 0x30df, 0x3074, 0x3008,
|
||||
0x2f9d, 0x2f33, 0x2ec8, 0x2e5f, 0x2df6, 0x2d8d, 0x2d24, 0x2cbc, 0x2c55, 0x2bee, 0x2b87, 0x2b21, 0x2abb, 0x2a55,
|
||||
0x29f0, 0x298b, 0x2927, 0x28c3, 0x2860, 0x27fd, 0x279a, 0x2738, 0x26d6, 0x2674, 0x2613, 0x25b2, 0x2552, 0x24f2,
|
||||
0x2492, 0x2432, 0x23d3, 0x2375, 0x2317, 0x22b9, 0x225b, 0x21fe, 0x21a1, 0x2145, 0x20e8, 0x208d, 0x2031, 0x1fd6,
|
||||
0x1f7b, 0x1f21, 0x1ec7, 0x1e6d, 0x1e13, 0x1dba, 0x1d61, 0x1d09, 0x1cb1, 0x1c59, 0x1c01, 0x1baa, 0x1b53, 0x1afc,
|
||||
0x1aa6, 0x1a50, 0x19fa, 0x19a5, 0x1950, 0x18fb, 0x18a7, 0x1853, 0x17ff, 0x17ab, 0x1758, 0x1705, 0x16b2, 0x1660,
|
||||
0x160d, 0x15bc, 0x156a, 0x1519, 0x14c8, 0x1477, 0x1426, 0x13d6, 0x1386, 0x1337, 0x12e7, 0x1298, 0x1249, 0x11fb,
|
||||
0x11ac, 0x115e, 0x1111, 0x10c3, 0x1076, 0x1029, 0x0fdc, 0x0f8f, 0x0f43, 0x0ef7, 0x0eab, 0x0e60, 0x0e15, 0x0dca,
|
||||
0x0d7f, 0x0d34, 0x0cea, 0x0ca0, 0x0c56, 0x0c0c, 0x0bc3, 0x0b7a, 0x0b31, 0x0ae8, 0x0aa0, 0x0a58, 0x0a10, 0x09c8,
|
||||
0x0981, 0x0939, 0x08f2, 0x08ab, 0x0865, 0x081e, 0x07d8, 0x0792, 0x074d, 0x0707, 0x06c2, 0x067d, 0x0638, 0x05f3,
|
||||
0x05af, 0x056a, 0x0526, 0x04e2, 0x049f, 0x045b, 0x0418, 0x03d5, 0x0392, 0x0350, 0x030d, 0x02cb, 0x0289, 0x0247,
|
||||
0x0206, 0x01c4, 0x0183, 0x0142, 0x0101, 0x00c0, 0x0080, 0x0040};
|
||||
|
||||
@@ -1,70 +1,172 @@
|
||||
#include <CpuDefinitions.hpp>
|
||||
#include <core/Interpreter.hpp>
|
||||
#include <log.hpp>
|
||||
#include <CpuDefinitions.hpp>
|
||||
|
||||
namespace n64 {
|
||||
void Interpreter::special(u32 instr) {
|
||||
u8 mask = (instr & 0x3F);
|
||||
// 00rr_rccc
|
||||
switch (mask) { // TODO: named constants for clearer code
|
||||
case SLL:
|
||||
if (instr != 0) {
|
||||
sll(instr);
|
||||
}
|
||||
break;
|
||||
case SRL: srl(instr); break;
|
||||
case SRA: sra(instr); break;
|
||||
case SLLV: sllv(instr); break;
|
||||
case SRLV: srlv(instr); break;
|
||||
case SRAV: srav(instr); break;
|
||||
case JR: jr(instr); break;
|
||||
case JALR: jalr(instr); break;
|
||||
case SYSCALL: regs.cop0.FireException(ExceptionCode::Syscall, 0, regs.oldPC); break;
|
||||
case BREAK: regs.cop0.FireException(ExceptionCode::Breakpoint, 0, regs.oldPC); break;
|
||||
case SYNC: break; // SYNC
|
||||
case MFHI: mfhi(instr); break;
|
||||
case MTHI: mthi(instr); break;
|
||||
case MFLO: mflo(instr); break;
|
||||
case MTLO: mtlo(instr); break;
|
||||
case DSLLV: dsllv(instr); break;
|
||||
case DSRLV: dsrlv(instr); break;
|
||||
case DSRAV: dsrav(instr); break;
|
||||
case MULT: mult(instr); break;
|
||||
case MULTU: multu(instr); break;
|
||||
case DIV: div(instr); break;
|
||||
case DIVU: divu(instr); break;
|
||||
case DMULT: dmult(instr); break;
|
||||
case DMULTU: dmultu(instr); break;
|
||||
case DDIV: ddiv(instr); break;
|
||||
case DDIVU: ddivu(instr); break;
|
||||
case ADD: add(instr); break;
|
||||
case ADDU: addu(instr); break;
|
||||
case SUB: sub(instr); break;
|
||||
case SUBU: subu(instr); break;
|
||||
case AND: and_(instr); break;
|
||||
case OR: or_(instr); break;
|
||||
case XOR: xor_(instr); break;
|
||||
case NOR: nor(instr); break;
|
||||
case SLT: slt(instr); break;
|
||||
case SLTU: sltu(instr); break;
|
||||
case DADD: dadd(instr); break;
|
||||
case DADDU: daddu(instr); break;
|
||||
case DSUB: dsub(instr); break;
|
||||
case DSUBU: dsubu(instr); break;
|
||||
case TGE: trap(regs.Read<s64>(RS(instr)) >= regs.Read<s64>(RT(instr))); break;
|
||||
case TGEU: trap(regs.Read<u64>(RS(instr)) >= regs.Read<u64>(RT(instr))); break;
|
||||
case TLT: trap(regs.Read<s64>(RS(instr)) < regs.Read<s64>(RT(instr))); break;
|
||||
case TLTU: trap(regs.Read<u64>(RS(instr)) < regs.Read<u64>(RT(instr))); break;
|
||||
case TEQ: trap(regs.Read<s64>(RS(instr)) == regs.Read<s64>(RT(instr))); break;
|
||||
case TNE: trap(regs.Read<s64>(RS(instr)) != regs.Read<s64>(RT(instr))); break;
|
||||
case DSLL: dsll(instr); break;
|
||||
case DSRL: dsrl(instr); break;
|
||||
case DSRA: dsra(instr); break;
|
||||
case DSLL32: dsll32(instr); break;
|
||||
case DSRL32: dsrl32(instr); break;
|
||||
case DSRA32: dsra32(instr); break;
|
||||
default:
|
||||
Util::panic("Unimplemented special {} {} ({:08X}) (pc: {:016X})", (mask >> 3) & 7, mask & 7, instr, (u64)regs.oldPC);
|
||||
case SLL:
|
||||
if (instr != 0) {
|
||||
sll(instr);
|
||||
}
|
||||
break;
|
||||
case SRL:
|
||||
srl(instr);
|
||||
break;
|
||||
case SRA:
|
||||
sra(instr);
|
||||
break;
|
||||
case SLLV:
|
||||
sllv(instr);
|
||||
break;
|
||||
case SRLV:
|
||||
srlv(instr);
|
||||
break;
|
||||
case SRAV:
|
||||
srav(instr);
|
||||
break;
|
||||
case JR:
|
||||
jr(instr);
|
||||
break;
|
||||
case JALR:
|
||||
jalr(instr);
|
||||
break;
|
||||
case SYSCALL:
|
||||
regs.cop0.FireException(ExceptionCode::Syscall, 0, regs.oldPC);
|
||||
break;
|
||||
case BREAK:
|
||||
regs.cop0.FireException(ExceptionCode::Breakpoint, 0, regs.oldPC);
|
||||
break;
|
||||
case SYNC:
|
||||
break; // SYNC
|
||||
case MFHI:
|
||||
mfhi(instr);
|
||||
break;
|
||||
case MTHI:
|
||||
mthi(instr);
|
||||
break;
|
||||
case MFLO:
|
||||
mflo(instr);
|
||||
break;
|
||||
case MTLO:
|
||||
mtlo(instr);
|
||||
break;
|
||||
case DSLLV:
|
||||
dsllv(instr);
|
||||
break;
|
||||
case DSRLV:
|
||||
dsrlv(instr);
|
||||
break;
|
||||
case DSRAV:
|
||||
dsrav(instr);
|
||||
break;
|
||||
case MULT:
|
||||
mult(instr);
|
||||
break;
|
||||
case MULTU:
|
||||
multu(instr);
|
||||
break;
|
||||
case DIV:
|
||||
div(instr);
|
||||
break;
|
||||
case DIVU:
|
||||
divu(instr);
|
||||
break;
|
||||
case DMULT:
|
||||
dmult(instr);
|
||||
break;
|
||||
case DMULTU:
|
||||
dmultu(instr);
|
||||
break;
|
||||
case DDIV:
|
||||
ddiv(instr);
|
||||
break;
|
||||
case DDIVU:
|
||||
ddivu(instr);
|
||||
break;
|
||||
case ADD:
|
||||
add(instr);
|
||||
break;
|
||||
case ADDU:
|
||||
addu(instr);
|
||||
break;
|
||||
case SUB:
|
||||
sub(instr);
|
||||
break;
|
||||
case SUBU:
|
||||
subu(instr);
|
||||
break;
|
||||
case AND:
|
||||
and_(instr);
|
||||
break;
|
||||
case OR:
|
||||
or_(instr);
|
||||
break;
|
||||
case XOR:
|
||||
xor_(instr);
|
||||
break;
|
||||
case NOR:
|
||||
nor(instr);
|
||||
break;
|
||||
case SLT:
|
||||
slt(instr);
|
||||
break;
|
||||
case SLTU:
|
||||
sltu(instr);
|
||||
break;
|
||||
case DADD:
|
||||
dadd(instr);
|
||||
break;
|
||||
case DADDU:
|
||||
daddu(instr);
|
||||
break;
|
||||
case DSUB:
|
||||
dsub(instr);
|
||||
break;
|
||||
case DSUBU:
|
||||
dsubu(instr);
|
||||
break;
|
||||
case TGE:
|
||||
trap(regs.Read<s64>(RS(instr)) >= regs.Read<s64>(RT(instr)));
|
||||
break;
|
||||
case TGEU:
|
||||
trap(regs.Read<u64>(RS(instr)) >= regs.Read<u64>(RT(instr)));
|
||||
break;
|
||||
case TLT:
|
||||
trap(regs.Read<s64>(RS(instr)) < regs.Read<s64>(RT(instr)));
|
||||
break;
|
||||
case TLTU:
|
||||
trap(regs.Read<u64>(RS(instr)) < regs.Read<u64>(RT(instr)));
|
||||
break;
|
||||
case TEQ:
|
||||
trap(regs.Read<s64>(RS(instr)) == regs.Read<s64>(RT(instr)));
|
||||
break;
|
||||
case TNE:
|
||||
trap(regs.Read<s64>(RS(instr)) != regs.Read<s64>(RT(instr)));
|
||||
break;
|
||||
case DSLL:
|
||||
dsll(instr);
|
||||
break;
|
||||
case DSRL:
|
||||
dsrl(instr);
|
||||
break;
|
||||
case DSRA:
|
||||
dsra(instr);
|
||||
break;
|
||||
case DSLL32:
|
||||
dsll32(instr);
|
||||
break;
|
||||
case DSRL32:
|
||||
dsrl32(instr);
|
||||
break;
|
||||
case DSRA32:
|
||||
dsra32(instr);
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unimplemented special {} {} ({:08X}) (pc: {:016X})", (mask >> 3) & 7, mask & 7, instr,
|
||||
(u64)regs.oldPC);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,102 +174,249 @@ void Interpreter::regimm(u32 instr) {
|
||||
u8 mask = ((instr >> 16) & 0x1F);
|
||||
// 000r_rccc
|
||||
switch (mask) { // TODO: named constants for clearer code
|
||||
case BLTZ: b(instr, regs.Read<s64>(RS(instr)) < 0); break;
|
||||
case BGEZ: b(instr, regs.Read<s64>(RS(instr)) >= 0); break;
|
||||
case BLTZL: bl(instr, regs.Read<s64>(RS(instr)) < 0); break;
|
||||
case BGEZL: bl(instr, regs.Read<s64>(RS(instr)) >= 0); break;
|
||||
case TGEI: trap(regs.Read<s64>(RS(instr)) >= s64(s16(instr))); break;
|
||||
case TGEIU: trap(regs.Read<u64>(RS(instr)) >= u64(s64(s16(instr)))); break;
|
||||
case TLTI: trap(regs.Read<s64>(RS(instr)) < s64(s16(instr))); break;
|
||||
case TLTIU: trap(regs.Read<u64>(RS(instr)) < u64(s64(s16(instr)))); break;
|
||||
case TEQI: trap(regs.Read<s64>(RS(instr)) == s64(s16(instr))); break;
|
||||
case TNEI: trap(regs.Read<s64>(RS(instr)) != s64(s16(instr))); break;
|
||||
case BLTZAL: blink(instr, regs.Read<s64>(RS(instr)) < 0); break;
|
||||
case BGEZAL: blink(instr, regs.Read<s64>(RS(instr)) >= 0); break;
|
||||
case BLTZALL: bllink(instr, regs.Read<s64>(RS(instr)) < 0); break;
|
||||
case BGEZALL: bllink(instr, regs.Read<s64>(RS(instr)) >= 0); break;
|
||||
default:
|
||||
Util::panic("Unimplemented regimm {} {} ({:08X}) (pc: {:016X})", (mask >> 3) & 3, mask & 7, instr, (u64)regs.oldPC);
|
||||
case BLTZ:
|
||||
b(instr, regs.Read<s64>(RS(instr)) < 0);
|
||||
break;
|
||||
case BGEZ:
|
||||
b(instr, regs.Read<s64>(RS(instr)) >= 0);
|
||||
break;
|
||||
case BLTZL:
|
||||
bl(instr, regs.Read<s64>(RS(instr)) < 0);
|
||||
break;
|
||||
case BGEZL:
|
||||
bl(instr, regs.Read<s64>(RS(instr)) >= 0);
|
||||
break;
|
||||
case TGEI:
|
||||
trap(regs.Read<s64>(RS(instr)) >= s64(s16(instr)));
|
||||
break;
|
||||
case TGEIU:
|
||||
trap(regs.Read<u64>(RS(instr)) >= u64(s64(s16(instr))));
|
||||
break;
|
||||
case TLTI:
|
||||
trap(regs.Read<s64>(RS(instr)) < s64(s16(instr)));
|
||||
break;
|
||||
case TLTIU:
|
||||
trap(regs.Read<u64>(RS(instr)) < u64(s64(s16(instr))));
|
||||
break;
|
||||
case TEQI:
|
||||
trap(regs.Read<s64>(RS(instr)) == s64(s16(instr)));
|
||||
break;
|
||||
case TNEI:
|
||||
trap(regs.Read<s64>(RS(instr)) != s64(s16(instr)));
|
||||
break;
|
||||
case BLTZAL:
|
||||
blink(instr, regs.Read<s64>(RS(instr)) < 0);
|
||||
break;
|
||||
case BGEZAL:
|
||||
blink(instr, regs.Read<s64>(RS(instr)) >= 0);
|
||||
break;
|
||||
case BLTZALL:
|
||||
bllink(instr, regs.Read<s64>(RS(instr)) < 0);
|
||||
break;
|
||||
case BGEZALL:
|
||||
bllink(instr, regs.Read<s64>(RS(instr)) >= 0);
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unimplemented regimm {} {} ({:08X}) (pc: {:016X})", (mask >> 3) & 3, mask & 7, instr, (u64)regs.oldPC);
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::cop2Decode(u32 instr) {
|
||||
if(!regs.cop0.status.cu2) {
|
||||
if (!regs.cop0.status.cu2) {
|
||||
regs.cop0.FireException(ExceptionCode::CoprocessorUnusable, 2, regs.oldPC);
|
||||
return;
|
||||
}
|
||||
switch(RS(instr)) {
|
||||
case 0x00: mfc2(instr); break;
|
||||
case 0x01: dmfc2(instr); break;
|
||||
case 0x02: cfc2(instr); break;
|
||||
case 0x04: mtc2(instr); break;
|
||||
case 0x05: dmtc2(instr); break;
|
||||
case 0x06: ctc2(instr); break;
|
||||
default:
|
||||
regs.cop0.FireException(ExceptionCode::ReservedInstruction, 2, regs.oldPC);
|
||||
switch (RS(instr)) {
|
||||
case 0x00:
|
||||
mfc2(instr);
|
||||
break;
|
||||
case 0x01:
|
||||
dmfc2(instr);
|
||||
break;
|
||||
case 0x02:
|
||||
cfc2(instr);
|
||||
break;
|
||||
case 0x04:
|
||||
mtc2(instr);
|
||||
break;
|
||||
case 0x05:
|
||||
dmtc2(instr);
|
||||
break;
|
||||
case 0x06:
|
||||
ctc2(instr);
|
||||
break;
|
||||
default:
|
||||
regs.cop0.FireException(ExceptionCode::ReservedInstruction, 2, regs.oldPC);
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::Exec(u32 instr) {
|
||||
u8 mask = (instr >> 26) & 0x3f;
|
||||
// 00rr_rccc
|
||||
switch(mask) { // TODO: named constants for clearer code
|
||||
case SPECIAL: special(instr); break;
|
||||
case REGIMM: regimm(instr); break;
|
||||
case J: j(instr); break;
|
||||
case JAL: jal(instr); break;
|
||||
case BEQ: b(instr, regs.Read<s64>(RS(instr)) == regs.Read<s64>(RT(instr))); break;
|
||||
case BNE: b(instr, regs.Read<s64>(RS(instr)) != regs.Read<s64>(RT(instr))); break;
|
||||
case BLEZ: b(instr, regs.Read<s64>(RS(instr)) <= 0); break;
|
||||
case BGTZ: b(instr, regs.Read<s64>(RS(instr)) > 0); break;
|
||||
case ADDI: addi(instr); break;
|
||||
case ADDIU: addiu(instr); break;
|
||||
case SLTI: slti(instr); break;
|
||||
case SLTIU: sltiu(instr); break;
|
||||
case ANDI: andi(instr); break;
|
||||
case ORI: ori(instr); break;
|
||||
case XORI: xori(instr); break;
|
||||
case LUI: lui(instr); break;
|
||||
case COP0: regs.cop0.decode(*this, instr); break;
|
||||
case COP1: regs.cop1.decode(*this, instr); break;
|
||||
case COP2: cop2Decode(instr); break;
|
||||
case BEQL: bl(instr, regs.Read<s64>(RS(instr)) == regs.Read<s64>(RT(instr))); break;
|
||||
case BNEL: bl(instr, regs.Read<s64>(RS(instr)) != regs.Read<s64>(RT(instr))); break;
|
||||
case BLEZL: bl(instr, regs.Read<s64>(RS(instr)) <= 0); break;
|
||||
case BGTZL: bl(instr, regs.Read<s64>(RS(instr)) > 0); break;
|
||||
case DADDI: daddi(instr); break;
|
||||
case DADDIU: daddiu(instr); break;
|
||||
case LDL: ldl(instr); break;
|
||||
case LDR: ldr(instr); break;
|
||||
case 0x1F: regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC); break;
|
||||
case LB: lb(instr); break;
|
||||
case LH: lh(instr); break;
|
||||
case LWL: lwl(instr); break;
|
||||
case LW: lw(instr); break;
|
||||
case LBU: lbu(instr); break;
|
||||
case LHU: lhu(instr); break;
|
||||
case LWR: lwr(instr); break;
|
||||
case LWU: lwu(instr); break;
|
||||
case SB: sb(instr); break;
|
||||
case SH: sh(instr); break;
|
||||
case SWL: swl(instr); break;
|
||||
case SW: sw(instr); break;
|
||||
case SDL: sdl(instr); break;
|
||||
case SDR: sdr(instr); break;
|
||||
case SWR: swr(instr); break;
|
||||
case CACHE: break; // CACHE
|
||||
case LL: ll(instr); break;
|
||||
case LWC1: regs.cop1.lwc1(*this, mem, instr); break;
|
||||
case LLD: lld(instr); break;
|
||||
case LDC1: regs.cop1.ldc1(*this, mem, instr); break;
|
||||
case LD: ld(instr); break;
|
||||
case SC: sc(instr); break;
|
||||
case SWC1: regs.cop1.swc1(*this, mem, instr); break;
|
||||
case SCD: scd(instr); break;
|
||||
case SDC1: regs.cop1.sdc1(*this, mem, instr); break;
|
||||
case SD: sd(instr); break;
|
||||
default:
|
||||
Util::panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", mask, instr, (u64)regs.oldPC);
|
||||
switch (mask) { // TODO: named constants for clearer code
|
||||
case SPECIAL:
|
||||
special(instr);
|
||||
break;
|
||||
case REGIMM:
|
||||
regimm(instr);
|
||||
break;
|
||||
case J:
|
||||
j(instr);
|
||||
break;
|
||||
case JAL:
|
||||
jal(instr);
|
||||
break;
|
||||
case BEQ:
|
||||
b(instr, regs.Read<s64>(RS(instr)) == regs.Read<s64>(RT(instr)));
|
||||
break;
|
||||
case BNE:
|
||||
b(instr, regs.Read<s64>(RS(instr)) != regs.Read<s64>(RT(instr)));
|
||||
break;
|
||||
case BLEZ:
|
||||
b(instr, regs.Read<s64>(RS(instr)) <= 0);
|
||||
break;
|
||||
case BGTZ:
|
||||
b(instr, regs.Read<s64>(RS(instr)) > 0);
|
||||
break;
|
||||
case ADDI:
|
||||
addi(instr);
|
||||
break;
|
||||
case ADDIU:
|
||||
addiu(instr);
|
||||
break;
|
||||
case SLTI:
|
||||
slti(instr);
|
||||
break;
|
||||
case SLTIU:
|
||||
sltiu(instr);
|
||||
break;
|
||||
case ANDI:
|
||||
andi(instr);
|
||||
break;
|
||||
case ORI:
|
||||
ori(instr);
|
||||
break;
|
||||
case XORI:
|
||||
xori(instr);
|
||||
break;
|
||||
case LUI:
|
||||
lui(instr);
|
||||
break;
|
||||
case COP0:
|
||||
regs.cop0.decode(*this, instr);
|
||||
break;
|
||||
case COP1:
|
||||
regs.cop1.decode(*this, instr);
|
||||
break;
|
||||
case COP2:
|
||||
cop2Decode(instr);
|
||||
break;
|
||||
case BEQL:
|
||||
bl(instr, regs.Read<s64>(RS(instr)) == regs.Read<s64>(RT(instr)));
|
||||
break;
|
||||
case BNEL:
|
||||
bl(instr, regs.Read<s64>(RS(instr)) != regs.Read<s64>(RT(instr)));
|
||||
break;
|
||||
case BLEZL:
|
||||
bl(instr, regs.Read<s64>(RS(instr)) <= 0);
|
||||
break;
|
||||
case BGTZL:
|
||||
bl(instr, regs.Read<s64>(RS(instr)) > 0);
|
||||
break;
|
||||
case DADDI:
|
||||
daddi(instr);
|
||||
break;
|
||||
case DADDIU:
|
||||
daddiu(instr);
|
||||
break;
|
||||
case LDL:
|
||||
ldl(instr);
|
||||
break;
|
||||
case LDR:
|
||||
ldr(instr);
|
||||
break;
|
||||
case 0x1F:
|
||||
regs.cop0.FireException(ExceptionCode::ReservedInstruction, 0, regs.oldPC);
|
||||
break;
|
||||
case LB:
|
||||
lb(instr);
|
||||
break;
|
||||
case LH:
|
||||
lh(instr);
|
||||
break;
|
||||
case LWL:
|
||||
lwl(instr);
|
||||
break;
|
||||
case LW:
|
||||
lw(instr);
|
||||
break;
|
||||
case LBU:
|
||||
lbu(instr);
|
||||
break;
|
||||
case LHU:
|
||||
lhu(instr);
|
||||
break;
|
||||
case LWR:
|
||||
lwr(instr);
|
||||
break;
|
||||
case LWU:
|
||||
lwu(instr);
|
||||
break;
|
||||
case SB:
|
||||
sb(instr);
|
||||
break;
|
||||
case SH:
|
||||
sh(instr);
|
||||
break;
|
||||
case SWL:
|
||||
swl(instr);
|
||||
break;
|
||||
case SW:
|
||||
sw(instr);
|
||||
break;
|
||||
case SDL:
|
||||
sdl(instr);
|
||||
break;
|
||||
case SDR:
|
||||
sdr(instr);
|
||||
break;
|
||||
case SWR:
|
||||
swr(instr);
|
||||
break;
|
||||
case CACHE:
|
||||
break; // CACHE
|
||||
case LL:
|
||||
ll(instr);
|
||||
break;
|
||||
case LWC1:
|
||||
regs.cop1.lwc1(*this, mem, instr);
|
||||
break;
|
||||
case LLD:
|
||||
lld(instr);
|
||||
break;
|
||||
case LDC1:
|
||||
regs.cop1.ldc1(*this, mem, instr);
|
||||
break;
|
||||
case LD:
|
||||
ld(instr);
|
||||
break;
|
||||
case SC:
|
||||
sc(instr);
|
||||
break;
|
||||
case SWC1:
|
||||
regs.cop1.swc1(*this, mem, instr);
|
||||
break;
|
||||
case SCD:
|
||||
scd(instr);
|
||||
break;
|
||||
case SDC1:
|
||||
regs.cop1.sdc1(*this, mem, instr);
|
||||
break;
|
||||
case SD:
|
||||
sd(instr);
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unimplemented instruction {:02X} ({:08X}) (pc: {:016X})", mask, instr, (u64)regs.oldPC);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -8,7 +8,7 @@ void Interpreter::add(u32 instr) {
|
||||
u32 rs = regs.Read<s32>(RS(instr));
|
||||
u32 rt = regs.Read<s32>(RT(instr));
|
||||
u32 result = rs + rt;
|
||||
if(check_signed_overflow(rs, rt, result)) {
|
||||
if (check_signed_overflow(rs, rt, result)) {
|
||||
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
||||
} else {
|
||||
regs.Write(RD(instr), s32(result));
|
||||
@@ -26,7 +26,7 @@ void Interpreter::addi(u32 instr) {
|
||||
u32 rs = regs.Read<s64>(RS(instr));
|
||||
u32 imm = s32(s16(instr));
|
||||
u32 result = rs + imm;
|
||||
if(check_signed_overflow(rs, imm, result)) {
|
||||
if (check_signed_overflow(rs, imm, result)) {
|
||||
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
||||
} else {
|
||||
regs.Write(RT(instr), s32(result));
|
||||
@@ -44,7 +44,7 @@ void Interpreter::dadd(u32 instr) {
|
||||
u64 rs = regs.Read<s64>(RS(instr));
|
||||
u64 rt = regs.Read<s64>(RT(instr));
|
||||
u64 result = rt + rs;
|
||||
if(check_signed_overflow(rs, rt, result)) {
|
||||
if (check_signed_overflow(rs, rt, result)) {
|
||||
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
||||
} else {
|
||||
regs.Write(RD(instr), result);
|
||||
@@ -61,7 +61,7 @@ void Interpreter::daddi(u32 instr) {
|
||||
u64 imm = s64(s16(instr));
|
||||
u64 rs = regs.Read<s64>(RS(instr));
|
||||
u64 result = imm + rs;
|
||||
if(check_signed_overflow(rs, imm, result)) {
|
||||
if (check_signed_overflow(rs, imm, result)) {
|
||||
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
||||
} else {
|
||||
regs.Write(RT(instr), result);
|
||||
@@ -78,9 +78,9 @@ void Interpreter::div(u32 instr) {
|
||||
s64 dividend = regs.Read<s32>(RS(instr));
|
||||
s64 divisor = regs.Read<s32>(RT(instr));
|
||||
|
||||
if(divisor == 0) {
|
||||
if (divisor == 0) {
|
||||
regs.hi = dividend;
|
||||
if(dividend >= 0) {
|
||||
if (dividend >= 0) {
|
||||
regs.lo = s64(-1);
|
||||
} else {
|
||||
regs.lo = s64(1);
|
||||
@@ -96,7 +96,7 @@ void Interpreter::div(u32 instr) {
|
||||
void Interpreter::divu(u32 instr) {
|
||||
u32 dividend = regs.Read<s64>(RS(instr));
|
||||
u32 divisor = regs.Read<s64>(RT(instr));
|
||||
if(divisor == 0) {
|
||||
if (divisor == 0) {
|
||||
regs.lo = -1;
|
||||
regs.hi = (s32)dividend;
|
||||
} else {
|
||||
@@ -113,9 +113,9 @@ void Interpreter::ddiv(u32 instr) {
|
||||
if (dividend == 0x8000000000000000 && divisor == 0xFFFFFFFFFFFFFFFF) {
|
||||
regs.lo = dividend;
|
||||
regs.hi = 0;
|
||||
} else if(divisor == 0) {
|
||||
} else if (divisor == 0) {
|
||||
regs.hi = dividend;
|
||||
if(dividend >= 0) {
|
||||
if (dividend >= 0) {
|
||||
regs.lo = -1;
|
||||
} else {
|
||||
regs.lo = 1;
|
||||
@@ -131,7 +131,7 @@ void Interpreter::ddiv(u32 instr) {
|
||||
void Interpreter::ddivu(u32 instr) {
|
||||
u64 dividend = regs.Read<s64>(RS(instr));
|
||||
u64 divisor = regs.Read<s64>(RT(instr));
|
||||
if(divisor == 0) {
|
||||
if (divisor == 0) {
|
||||
regs.lo = -1;
|
||||
regs.hi = (s64)dividend;
|
||||
} else {
|
||||
@@ -197,11 +197,11 @@ void Interpreter::lui(u32 instr) {
|
||||
void Interpreter::lb(u32 instr) {
|
||||
u64 address = regs.Read<s64>(RS(instr)) + (s16)instr;
|
||||
u32 paddr = 0;
|
||||
if(!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
||||
if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
||||
regs.cop0.HandleTLBException(address);
|
||||
regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
||||
} else {
|
||||
regs.Write(RT(instr), (s8) mem.Read<u8>(regs, paddr));
|
||||
regs.Write(RT(instr), (s8)mem.Read<u8>(regs, paddr));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,11 +214,11 @@ void Interpreter::lh(u32 instr) {
|
||||
}
|
||||
|
||||
u32 paddr = 0;
|
||||
if(!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
||||
if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
||||
regs.cop0.HandleTLBException(address);
|
||||
regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
||||
} else {
|
||||
regs.Write(RT(instr), (s16) mem.Read<u16>(regs, paddr));
|
||||
regs.Write(RT(instr), (s16)mem.Read<u16>(regs, paddr));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,7 +236,7 @@ void Interpreter::lw(u32 instr) {
|
||||
regs.cop0.HandleTLBException(address);
|
||||
regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
||||
} else {
|
||||
regs.Write(RT(instr), (s32) mem.Read<u32>(regs, physical));
|
||||
regs.Write(RT(instr), (s32)mem.Read<u32>(regs, physical));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@ void Interpreter::ll(u32 instr) {
|
||||
regs.cop0.FireException(ExceptionCode::AddressErrorLoad, 0, regs.oldPC);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
regs.Write(RT(instr), result);
|
||||
|
||||
regs.cop0.llbit = true;
|
||||
@@ -263,7 +263,7 @@ void Interpreter::ll(u32 instr) {
|
||||
void Interpreter::lwl(u32 instr) {
|
||||
u64 address = regs.Read<s64>(RS(instr)) + (s16)instr;
|
||||
u32 paddr = 0;
|
||||
if(!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
||||
if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
||||
regs.cop0.HandleTLBException(address);
|
||||
regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
||||
} else {
|
||||
@@ -278,7 +278,7 @@ void Interpreter::lwl(u32 instr) {
|
||||
void Interpreter::lwr(u32 instr) {
|
||||
u64 address = regs.Read<s64>(RS(instr)) + (s16)instr;
|
||||
u32 paddr = 0;
|
||||
if(!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
||||
if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
||||
regs.cop0.HandleTLBException(address);
|
||||
regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
||||
} else {
|
||||
@@ -299,7 +299,7 @@ void Interpreter::ld(u32 instr) {
|
||||
}
|
||||
|
||||
u32 paddr = 0;
|
||||
if(!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
||||
if (!regs.cop0.MapVAddr(Cop0::LOAD, address, paddr)) {
|
||||
regs.cop0.HandleTLBException(address);
|
||||
regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::LOAD), 0, regs.oldPC);
|
||||
} else {
|
||||
@@ -340,7 +340,7 @@ void Interpreter::ldl(u32 instr) {
|
||||
s32 shift = 8 * ((address ^ 0) & 7);
|
||||
u64 mask = 0xFFFFFFFFFFFFFFFF << shift;
|
||||
u64 data = mem.Read<u64>(regs, paddr & ~7);
|
||||
s64 result = (s64) ((regs.Read<s64>(RT(instr)) & ~mask) | (data << shift));
|
||||
s64 result = (s64)((regs.Read<s64>(RT(instr)) & ~mask) | (data << shift));
|
||||
regs.Write(RT(instr), result);
|
||||
}
|
||||
}
|
||||
@@ -355,7 +355,7 @@ void Interpreter::ldr(u32 instr) {
|
||||
s32 shift = 8 * ((address ^ 7) & 7);
|
||||
u64 mask = 0xFFFFFFFFFFFFFFFF >> shift;
|
||||
u64 data = mem.Read<u64>(regs, paddr & ~7);
|
||||
s64 result = (s64) ((regs.Read<s64>(RT(instr)) & ~mask) | (data >> shift));
|
||||
s64 result = (s64)((regs.Read<s64>(RT(instr)) & ~mask) | (data >> shift));
|
||||
regs.Write(RT(instr), result);
|
||||
}
|
||||
}
|
||||
@@ -421,7 +421,7 @@ void Interpreter::sb(u32 instr) {
|
||||
void Interpreter::sc(u32 instr) {
|
||||
u64 address = regs.Read<s64>(RS(instr)) + (s16)instr;
|
||||
|
||||
if(regs.cop0.llbit) {
|
||||
if (regs.cop0.llbit) {
|
||||
regs.cop0.llbit = false;
|
||||
|
||||
if (check_address_error(0b11, address)) {
|
||||
@@ -432,7 +432,7 @@ void Interpreter::sc(u32 instr) {
|
||||
}
|
||||
|
||||
u32 paddr = 0;
|
||||
if(!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) {
|
||||
if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) {
|
||||
regs.Write(RT(instr), 0);
|
||||
regs.cop0.HandleTLBException(address);
|
||||
regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||
@@ -453,7 +453,7 @@ void Interpreter::scd(u32 instr) {
|
||||
|
||||
s64 address = regs.Read<s64>(RS(instr)) + (s16)instr;
|
||||
|
||||
if(regs.cop0.llbit) {
|
||||
if (regs.cop0.llbit) {
|
||||
regs.cop0.llbit = false;
|
||||
|
||||
if (check_address_error(0b111, address)) {
|
||||
@@ -464,7 +464,7 @@ void Interpreter::scd(u32 instr) {
|
||||
}
|
||||
|
||||
u32 paddr = 0;
|
||||
if(!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) {
|
||||
if (!regs.cop0.MapVAddr(Cop0::STORE, address, paddr)) {
|
||||
regs.Write(RT(instr), 0);
|
||||
regs.cop0.HandleTLBException(address);
|
||||
regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||
@@ -481,7 +481,7 @@ void Interpreter::sh(u32 instr) {
|
||||
s64 address = regs.Read<s64>(RS(instr)) + (s16)instr;
|
||||
|
||||
u32 physical;
|
||||
if(!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) {
|
||||
if (!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) {
|
||||
regs.cop0.HandleTLBException(address);
|
||||
regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||
} else {
|
||||
@@ -499,7 +499,7 @@ void Interpreter::sw(u32 instr) {
|
||||
}
|
||||
|
||||
u32 physical;
|
||||
if(!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) {
|
||||
if (!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) {
|
||||
regs.cop0.HandleTLBException(address);
|
||||
regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||
} else {
|
||||
@@ -516,7 +516,7 @@ void Interpreter::sd(u32 instr) {
|
||||
}
|
||||
|
||||
u32 physical;
|
||||
if(!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) {
|
||||
if (!regs.cop0.MapVAddr(Cop0::STORE, address, physical)) {
|
||||
regs.cop0.HandleTLBException(address);
|
||||
regs.cop0.FireException(regs.cop0.GetTLBExceptionCode(regs.cop0.tlbError, Cop0::STORE), 0, regs.oldPC);
|
||||
} else {
|
||||
@@ -590,13 +590,9 @@ void Interpreter::ori(u32 instr) {
|
||||
regs.Write(RT(instr), result);
|
||||
}
|
||||
|
||||
void Interpreter::or_(u32 instr) {
|
||||
regs.Write(RD(instr), regs.Read<s64>(RS(instr)) | regs.Read<s64>(RT(instr)));
|
||||
}
|
||||
void Interpreter::or_(u32 instr) { regs.Write(RD(instr), regs.Read<s64>(RS(instr)) | regs.Read<s64>(RT(instr))); }
|
||||
|
||||
void Interpreter::nor(u32 instr) {
|
||||
regs.Write(RD(instr), ~(regs.Read<s64>(RS(instr)) | regs.Read<s64>(RT(instr))));
|
||||
}
|
||||
void Interpreter::nor(u32 instr) { regs.Write(RD(instr), ~(regs.Read<s64>(RS(instr)) | regs.Read<s64>(RT(instr)))); }
|
||||
|
||||
void Interpreter::j(u32 instr) {
|
||||
s32 target = (instr & 0x3ffffff) << 2;
|
||||
@@ -632,43 +628,35 @@ void Interpreter::sltiu(u32 instr) {
|
||||
regs.Write(RT(instr), regs.Read<u64>(RS(instr)) < imm);
|
||||
}
|
||||
|
||||
void Interpreter::slt(u32 instr) {
|
||||
regs.Write(RD(instr), regs.Read<s64>(RS(instr)) < regs.Read<s64>(RT(instr)));
|
||||
}
|
||||
void Interpreter::slt(u32 instr) { regs.Write(RD(instr), regs.Read<s64>(RS(instr)) < regs.Read<s64>(RT(instr))); }
|
||||
|
||||
void Interpreter::sltu(u32 instr) {
|
||||
regs.Write(RD(instr), regs.Read<u64>(RS(instr)) < regs.Read<u64>(RT(instr)));
|
||||
}
|
||||
void Interpreter::sltu(u32 instr) { regs.Write(RD(instr), regs.Read<u64>(RS(instr)) < regs.Read<u64>(RT(instr))); }
|
||||
|
||||
void Interpreter::xori(u32 instr) {
|
||||
s64 imm = (u16)instr;
|
||||
regs.Write(RT(instr), regs.Read<s64>(RS(instr)) ^ imm);
|
||||
}
|
||||
|
||||
void Interpreter::xor_(u32 instr) {
|
||||
regs.Write(RD(instr), regs.Read<s64>(RT(instr)) ^ regs.Read<s64>(RS(instr)));
|
||||
}
|
||||
void Interpreter::xor_(u32 instr) { regs.Write(RD(instr), regs.Read<s64>(RT(instr)) ^ regs.Read<s64>(RS(instr))); }
|
||||
|
||||
void Interpreter::andi(u32 instr) {
|
||||
s64 imm = (u16)instr;
|
||||
regs.Write(RT(instr), regs.Read<s64>(RS(instr)) & imm);
|
||||
}
|
||||
|
||||
void Interpreter::and_(u32 instr) {
|
||||
regs.Write(RD(instr), regs.Read<s64>(RS(instr)) & regs.Read<s64>(RT(instr)));
|
||||
}
|
||||
void Interpreter::and_(u32 instr) { regs.Write(RD(instr), regs.Read<s64>(RS(instr)) & regs.Read<s64>(RT(instr))); }
|
||||
|
||||
void Interpreter::sll(u32 instr) {
|
||||
u8 sa = ((instr >> 6) & 0x1f);
|
||||
s32 result = regs.Read<s64>(RT(instr)) << sa;
|
||||
regs.Write(RD(instr), (s64) result);
|
||||
regs.Write(RD(instr), (s64)result);
|
||||
}
|
||||
|
||||
void Interpreter::sllv(u32 instr) {
|
||||
u8 sa = (regs.Read<s64>(RS(instr))) & 0x1F;
|
||||
u32 rt = regs.Read<s64>(RT(instr));
|
||||
s32 result = rt << sa;
|
||||
regs.Write(RD(instr), (s64) result);
|
||||
regs.Write(RD(instr), (s64)result);
|
||||
}
|
||||
|
||||
void Interpreter::dsll32(u32 instr) {
|
||||
@@ -693,14 +681,14 @@ void Interpreter::srl(u32 instr) {
|
||||
u32 rt = regs.Read<s64>(RT(instr));
|
||||
u8 sa = ((instr >> 6) & 0x1f);
|
||||
u32 result = rt >> sa;
|
||||
regs.Write(RD(instr), (s32) result);
|
||||
regs.Write(RD(instr), (s32)result);
|
||||
}
|
||||
|
||||
void Interpreter::srlv(u32 instr) {
|
||||
u8 sa = (regs.Read<s64>(RS(instr)) & 0x1F);
|
||||
u32 rt = regs.Read<s64>(RT(instr));
|
||||
s32 result = rt >> sa;
|
||||
regs.Write(RD(instr), (s64) result);
|
||||
regs.Write(RD(instr), (s64)result);
|
||||
}
|
||||
|
||||
void Interpreter::dsrl(u32 instr) {
|
||||
@@ -765,7 +753,7 @@ void Interpreter::dsub(u32 instr) {
|
||||
s64 rt = regs.Read<s64>(RT(instr));
|
||||
s64 rs = regs.Read<s64>(RS(instr));
|
||||
s64 result = rs - rt;
|
||||
if(check_signed_underflow(rs, rt, result)) {
|
||||
if (check_signed_underflow(rs, rt, result)) {
|
||||
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
||||
} else {
|
||||
regs.Write(RD(instr), result);
|
||||
@@ -783,7 +771,7 @@ void Interpreter::sub(u32 instr) {
|
||||
s32 rt = regs.Read<s64>(RT(instr));
|
||||
s32 rs = regs.Read<s64>(RS(instr));
|
||||
s32 result = rs - rt;
|
||||
if(check_signed_underflow(rs, rt, result)) {
|
||||
if (check_signed_underflow(rs, rt, result)) {
|
||||
regs.cop0.FireException(ExceptionCode::Overflow, 0, regs.oldPC);
|
||||
} else {
|
||||
regs.Write(RD(instr), result);
|
||||
@@ -794,7 +782,7 @@ void Interpreter::subu(u32 instr) {
|
||||
u32 rt = regs.Read<s64>(RT(instr));
|
||||
u32 rs = regs.Read<s64>(RS(instr));
|
||||
u32 result = rs - rt;
|
||||
regs.Write(RD(instr), (s64) ((s32) result));
|
||||
regs.Write(RD(instr), (s64)((s32)result));
|
||||
}
|
||||
|
||||
void Interpreter::dmultu(u32 instr) {
|
||||
@@ -829,50 +817,32 @@ void Interpreter::mult(u32 instr) {
|
||||
regs.hi = (s64)((s32)(result >> 32));
|
||||
}
|
||||
|
||||
void Interpreter::mflo(u32 instr) {
|
||||
regs.Write(RD(instr), regs.lo);
|
||||
}
|
||||
void Interpreter::mflo(u32 instr) { regs.Write(RD(instr), regs.lo); }
|
||||
|
||||
void Interpreter::mfhi(u32 instr) {
|
||||
regs.Write(RD(instr), regs.hi);
|
||||
}
|
||||
void Interpreter::mfhi(u32 instr) { regs.Write(RD(instr), regs.hi); }
|
||||
|
||||
void Interpreter::mtlo(u32 instr) {
|
||||
regs.lo = regs.Read<s64>(RS(instr));
|
||||
}
|
||||
void Interpreter::mtlo(u32 instr) { regs.lo = regs.Read<s64>(RS(instr)); }
|
||||
|
||||
void Interpreter::mthi(u32 instr) {
|
||||
regs.hi = regs.Read<s64>(RS(instr));
|
||||
}
|
||||
void Interpreter::mthi(u32 instr) { regs.hi = regs.Read<s64>(RS(instr)); }
|
||||
|
||||
void Interpreter::trap(bool cond) {
|
||||
if(cond) {
|
||||
if (cond) {
|
||||
regs.cop0.FireException(ExceptionCode::Trap, 0, regs.oldPC);
|
||||
}
|
||||
}
|
||||
|
||||
void Interpreter::mtc2(u32 instr) {
|
||||
cop2Latch = regs.Read<s64>(RT(instr));
|
||||
}
|
||||
void Interpreter::mtc2(u32 instr) { cop2Latch = regs.Read<s64>(RT(instr)); }
|
||||
|
||||
void Interpreter::mfc2(u32 instr) {
|
||||
s32 value = cop2Latch;
|
||||
regs.Write(RT(instr), value);
|
||||
}
|
||||
|
||||
void Interpreter::dmtc2(u32 instr) {
|
||||
cop2Latch = regs.Read<s64>(RT(instr));
|
||||
}
|
||||
void Interpreter::dmtc2(u32 instr) { cop2Latch = regs.Read<s64>(RT(instr)); }
|
||||
|
||||
void Interpreter::dmfc2(u32 instr) {
|
||||
regs.Write(RT(instr), cop2Latch);
|
||||
}
|
||||
void Interpreter::dmfc2(u32 instr) { regs.Write(RT(instr), cop2Latch); }
|
||||
|
||||
void Interpreter::ctc2(u32) {
|
||||
void Interpreter::ctc2(u32) {}
|
||||
|
||||
}
|
||||
|
||||
void Interpreter::cfc2(u32) {
|
||||
|
||||
}
|
||||
}
|
||||
void Interpreter::cfc2(u32) {}
|
||||
} // namespace n64
|
||||
|
||||
@@ -4,21 +4,41 @@ namespace n64 {
|
||||
static inline bool SpecialEndsBlock(u32 instr) {
|
||||
u8 mask = instr & 0x3F;
|
||||
switch (mask) {
|
||||
case JR: case JALR: case SYSCALL: case BREAK:
|
||||
case TGE: case TGEU: case TLT: case TLTU:
|
||||
case TEQ: case TNE: return true;
|
||||
default: return false;
|
||||
case JR:
|
||||
case JALR:
|
||||
case SYSCALL:
|
||||
case BREAK:
|
||||
case TGE:
|
||||
case TGEU:
|
||||
case TLT:
|
||||
case TLTU:
|
||||
case TEQ:
|
||||
case TNE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool InstrEndsBlock(u32 instr) {
|
||||
u8 mask = (instr >> 26) & 0x3f;
|
||||
switch (mask) {
|
||||
case SPECIAL: return SpecialEndsBlock(instr);
|
||||
case REGIMM: case J: case JAL: case BEQ:
|
||||
case BNE: case BLEZ: case BGTZ: case BEQL:
|
||||
case BNEL: case BLEZL: case BGTZL: return true;
|
||||
default: return false;
|
||||
case SPECIAL:
|
||||
return SpecialEndsBlock(instr);
|
||||
case REGIMM:
|
||||
case J:
|
||||
case JAL:
|
||||
case BEQ:
|
||||
case BNE:
|
||||
case BLEZ:
|
||||
case BGTZ:
|
||||
case BEQL:
|
||||
case BNEL:
|
||||
case BLEZL:
|
||||
case BGTZL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,8 +15,8 @@ void Flash::Reset() {
|
||||
writeBuf = {};
|
||||
}
|
||||
|
||||
void Flash::Load(SaveType saveType, const std::string& path) {
|
||||
if(saveType == SAVE_FLASH_1m) {
|
||||
void Flash::Load(SaveType saveType, const std::string &path) {
|
||||
if (saveType == SAVE_FLASH_1m) {
|
||||
fs::path flashPath_ = path;
|
||||
if (!savePath.empty()) {
|
||||
flashPath_ = savePath / flashPath_.filename();
|
||||
@@ -25,12 +25,14 @@ void Flash::Load(SaveType saveType, const std::string& path) {
|
||||
std::error_code error;
|
||||
if (saveData.is_mapped()) {
|
||||
saveData.sync(error);
|
||||
if (error) { Util::panic("Could not sync {}", flashPath); }
|
||||
if (error) {
|
||||
Util::panic("Could not sync {}", flashPath);
|
||||
}
|
||||
saveData.unmap();
|
||||
}
|
||||
|
||||
auto flashVec = Util::ReadFileBinary(flashPath);
|
||||
if(flashVec.empty()) {
|
||||
if (flashVec.empty()) {
|
||||
std::vector<u8> dummy{};
|
||||
dummy.resize(FLASH_SIZE);
|
||||
Util::WriteFileBinary(dummy, flashPath);
|
||||
@@ -41,40 +43,41 @@ void Flash::Load(SaveType saveType, const std::string& path) {
|
||||
Util::panic("Corrupt SRAM!");
|
||||
}
|
||||
|
||||
saveData = mio::make_mmap_sink(
|
||||
flashPath, 0, mio::map_entire_file, error);
|
||||
if (error) { Util::panic("Could not make mmap {}", flashPath); }
|
||||
saveData = mio::make_mmap_sink(flashPath, 0, mio::map_entire_file, error);
|
||||
if (error) {
|
||||
Util::panic("Could not make mmap {}", flashPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Flash::CommandExecute() {
|
||||
Util::trace("Flash::CommandExecute");
|
||||
switch (state) {
|
||||
case FlashState::Idle:
|
||||
break;
|
||||
case FlashState::Erase:
|
||||
if(saveData.is_mapped()) {
|
||||
for (int i = 0; i < 128; i++) {
|
||||
saveData[eraseOffs + i] = 0xFF;
|
||||
}
|
||||
} else {
|
||||
Util::panic("Accessing flash when not mapped!");
|
||||
case FlashState::Idle:
|
||||
break;
|
||||
case FlashState::Erase:
|
||||
if (saveData.is_mapped()) {
|
||||
for (int i = 0; i < 128; i++) {
|
||||
saveData[eraseOffs + i] = 0xFF;
|
||||
}
|
||||
break;
|
||||
case FlashState::Write:
|
||||
if(saveData.is_mapped()) {
|
||||
for (int i = 0; i < 128; i++) {
|
||||
saveData[writeOffs + i] = writeBuf[i];
|
||||
}
|
||||
} else {
|
||||
Util::panic("Accessing flash when not mapped!");
|
||||
} else {
|
||||
Util::panic("Accessing flash when not mapped!");
|
||||
}
|
||||
break;
|
||||
case FlashState::Write:
|
||||
if (saveData.is_mapped()) {
|
||||
for (int i = 0; i < 128; i++) {
|
||||
saveData[writeOffs + i] = writeBuf[i];
|
||||
}
|
||||
break;
|
||||
case FlashState::Read:
|
||||
Util::panic("Execute command when flash in read state");
|
||||
break;
|
||||
case FlashState::Status:
|
||||
break;
|
||||
} else {
|
||||
Util::panic("Accessing flash when not mapped!");
|
||||
}
|
||||
break;
|
||||
case FlashState::Read:
|
||||
Util::panic("Execute command when flash in read state");
|
||||
break;
|
||||
case FlashState::Status:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,9 +86,7 @@ void Flash::CommandStatus() {
|
||||
status = 0x1111800100C20000;
|
||||
}
|
||||
|
||||
void Flash::CommandSetEraseOffs(u32 val) {
|
||||
eraseOffs = (val & 0xffff) << 7;
|
||||
}
|
||||
void Flash::CommandSetEraseOffs(u32 val) { eraseOffs = (val & 0xffff) << 7; }
|
||||
|
||||
void Flash::CommandErase() {
|
||||
state = FlashState::Erase;
|
||||
@@ -97,9 +98,7 @@ void Flash::CommandSetWriteOffs(u32 val) {
|
||||
status = 0x1111800400C20000LL;
|
||||
}
|
||||
|
||||
void Flash::CommandWrite() {
|
||||
state = FlashState::Write;
|
||||
}
|
||||
void Flash::CommandWrite() { state = FlashState::Write; }
|
||||
|
||||
void Flash::CommandRead() {
|
||||
state = FlashState::Read;
|
||||
@@ -109,12 +108,7 @@ void Flash::CommandRead() {
|
||||
std::vector<u8> Flash::Serialize() {
|
||||
std::vector<u8> res{};
|
||||
|
||||
res.resize(
|
||||
sizeof(state) +
|
||||
sizeof(status) +
|
||||
sizeof(eraseOffs) +
|
||||
sizeof(writeOffs) +
|
||||
128);
|
||||
res.resize(sizeof(state) + sizeof(status) + sizeof(eraseOffs) + sizeof(writeOffs) + 128);
|
||||
|
||||
u32 index = 0;
|
||||
memcpy(res.data() + index, &state, sizeof(state));
|
||||
@@ -130,7 +124,7 @@ std::vector<u8> Flash::Serialize() {
|
||||
return res;
|
||||
}
|
||||
|
||||
void Flash::Deserialize(const std::vector<u8>& data) {
|
||||
void Flash::Deserialize(const std::vector<u8> &data) {
|
||||
u32 index = 0;
|
||||
memcpy(&state, data.data() + index, sizeof(state));
|
||||
index += sizeof(state);
|
||||
@@ -143,44 +137,70 @@ void Flash::Deserialize(const std::vector<u8>& data) {
|
||||
std::copy(data.begin() + index, data.begin() + index + 128, writeBuf.begin());
|
||||
}
|
||||
|
||||
template <> void Flash::Write<u32>(u32 index, u32 val) {
|
||||
if(index > 0) {
|
||||
template <>
|
||||
void Flash::Write<u32>(u32 index, u32 val) {
|
||||
if (index > 0) {
|
||||
u8 cmd = val >> 24;
|
||||
switch(cmd) {
|
||||
case FLASH_COMMAND_EXECUTE: CommandExecute(); break;
|
||||
case FLASH_COMMAND_STATUS: CommandStatus(); break;
|
||||
case FLASH_COMMAND_SET_ERASE_OFFSET: CommandSetEraseOffs(val); break;
|
||||
case FLASH_COMMAND_ERASE: CommandErase(); break;
|
||||
case FLASH_COMMAND_SET_WRITE_OFFSET: CommandSetWriteOffs(val); break;
|
||||
case FLASH_COMMAND_WRITE: CommandWrite(); break;
|
||||
case FLASH_COMMAND_READ: CommandRead(); break;
|
||||
default: Util::warn("Invalid flash command: {:02X}", cmd);
|
||||
switch (cmd) {
|
||||
case FLASH_COMMAND_EXECUTE:
|
||||
CommandExecute();
|
||||
break;
|
||||
case FLASH_COMMAND_STATUS:
|
||||
CommandStatus();
|
||||
break;
|
||||
case FLASH_COMMAND_SET_ERASE_OFFSET:
|
||||
CommandSetEraseOffs(val);
|
||||
break;
|
||||
case FLASH_COMMAND_ERASE:
|
||||
CommandErase();
|
||||
break;
|
||||
case FLASH_COMMAND_SET_WRITE_OFFSET:
|
||||
CommandSetWriteOffs(val);
|
||||
break;
|
||||
case FLASH_COMMAND_WRITE:
|
||||
CommandWrite();
|
||||
break;
|
||||
case FLASH_COMMAND_READ:
|
||||
CommandRead();
|
||||
break;
|
||||
default:
|
||||
Util::warn("Invalid flash command: {:02X}", cmd);
|
||||
}
|
||||
} else {
|
||||
Util::warn("Flash Write of {:08X} @ {:08X}", val, index);
|
||||
}
|
||||
}
|
||||
|
||||
template <> void Flash::Write<u8>(u32 index, u8 val) {
|
||||
switch(state) {
|
||||
case FlashState::Idle: Util::panic("Invalid FlashState::Idle with Write<u8>");
|
||||
case FlashState::Status: Util::panic("Invalid FlashState::Status with Write<u8>");
|
||||
case FlashState::Erase: Util::panic("Invalid FlashState::Erase with Write<u8>");
|
||||
case FlashState::Read: Util::panic("Invalid FlashState::Read with Write<u8>");
|
||||
case FlashState::Write:
|
||||
assert(index <= 0x7F && "Out of range flash Write8");
|
||||
writeBuf[index] = val;
|
||||
break;
|
||||
default: Util::warn("Invalid flash state on Write<u8>: {:02X}", static_cast<u8>(state));
|
||||
template <>
|
||||
void Flash::Write<u8>(u32 index, u8 val) {
|
||||
switch (state) {
|
||||
case FlashState::Idle:
|
||||
Util::panic("Invalid FlashState::Idle with Write<u8>");
|
||||
case FlashState::Status:
|
||||
Util::panic("Invalid FlashState::Status with Write<u8>");
|
||||
case FlashState::Erase:
|
||||
Util::panic("Invalid FlashState::Erase with Write<u8>");
|
||||
case FlashState::Read:
|
||||
Util::panic("Invalid FlashState::Read with Write<u8>");
|
||||
case FlashState::Write:
|
||||
assert(index <= 0x7F && "Out of range flash Write8");
|
||||
writeBuf[index] = val;
|
||||
break;
|
||||
default:
|
||||
Util::warn("Invalid flash state on Write<u8>: {:02X}", static_cast<u8>(state));
|
||||
}
|
||||
}
|
||||
|
||||
template <> u8 Flash::Read<u8>(u32 index) const {
|
||||
template <>
|
||||
u8 Flash::Read<u8>(u32 index) const {
|
||||
switch (state) {
|
||||
case FlashState::Idle: Util::panic("Flash read byte while in state FLASH_STATE_IDLE");
|
||||
case FlashState::Write: Util::panic("Flash read byte while in state FLASH_STATE_WRITE");
|
||||
case FlashState::Read: {
|
||||
if(saveData.is_mapped()) {
|
||||
case FlashState::Idle:
|
||||
Util::panic("Flash read byte while in state FLASH_STATE_IDLE");
|
||||
case FlashState::Write:
|
||||
Util::panic("Flash read byte while in state FLASH_STATE_WRITE");
|
||||
case FlashState::Read:
|
||||
{
|
||||
if (saveData.is_mapped()) {
|
||||
u8 value = saveData[index];
|
||||
Util::trace("Flash read byte in state read: index {:08X} = {:02X}", index, value);
|
||||
return value;
|
||||
@@ -188,17 +208,20 @@ template <> u8 Flash::Read<u8>(u32 index) const {
|
||||
Util::panic("Accessing flash when not mapped!");
|
||||
}
|
||||
}
|
||||
case FlashState::Status: {
|
||||
case FlashState::Status:
|
||||
{
|
||||
u32 offset = (7 - (index % 8)) * 8;
|
||||
u8 value = (status >> offset) & 0xFF;
|
||||
Util::trace("Flash read byte in state status: index {:08X} = {:02X}", index, value);
|
||||
return value;
|
||||
}
|
||||
default: Util::panic("Flash read byte while in unknown state");
|
||||
default:
|
||||
Util::panic("Flash read byte while in unknown state");
|
||||
}
|
||||
}
|
||||
|
||||
template <> u32 Flash::Read<u32>(u32) const {
|
||||
template <>
|
||||
u32 Flash::Read<u32>(u32) const {
|
||||
return status >> 32;
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#include <core/mmio/AI.hpp>
|
||||
#include <log.hpp>
|
||||
#include <core/Mem.hpp>
|
||||
#include <core/mmio/AI.hpp>
|
||||
#include <core/registers/Registers.hpp>
|
||||
#include <log.hpp>
|
||||
|
||||
namespace n64 {
|
||||
AI::AI(Mem &mem, Registers ®s) : mem(mem), regs(regs) { }
|
||||
AI::AI(Mem &mem, Registers ®s) : mem(mem), regs(regs) {}
|
||||
|
||||
void AI::Reset() {
|
||||
dmaEnable = false;
|
||||
@@ -21,7 +21,7 @@ void AI::Reset() {
|
||||
// https://github.com/ares-emulator/ares/blob/master/ares/n64/ai/io.cpp
|
||||
// https://github.com/ares-emulator/ares/blob/master/LICENSE
|
||||
auto AI::Read(u32 addr) const -> u32 {
|
||||
if(addr == 0x0450000C) {
|
||||
if (addr == 0x0450000C) {
|
||||
u32 val = 0;
|
||||
val |= (dmaCount > 1);
|
||||
val |= 1 << 20;
|
||||
@@ -36,60 +36,65 @@ auto AI::Read(u32 addr) const -> u32 {
|
||||
}
|
||||
|
||||
void AI::Write(u32 addr, u32 val) {
|
||||
switch(addr) {
|
||||
case 0x04500000:
|
||||
if(dmaCount < 2) {
|
||||
dmaAddr[dmaCount] = val & 0xFFFFFF & ~7;
|
||||
}
|
||||
break;
|
||||
case 0x04500004: {
|
||||
switch (addr) {
|
||||
case 0x04500000:
|
||||
if (dmaCount < 2) {
|
||||
dmaAddr[dmaCount] = val & 0xFFFFFF & ~7;
|
||||
}
|
||||
break;
|
||||
case 0x04500004:
|
||||
{
|
||||
u32 len = (val & 0x3FFFF) & ~7;
|
||||
if(dmaCount < 2) {
|
||||
if(dmaCount == 0) mem.mmio.mi.InterruptRaise(MI::Interrupt::AI);
|
||||
if (dmaCount < 2) {
|
||||
if (dmaCount == 0)
|
||||
mem.mmio.mi.InterruptRaise(MI::Interrupt::AI);
|
||||
dmaLen[dmaCount] = len;
|
||||
dmaCount++;
|
||||
}
|
||||
} break;
|
||||
case 0x04500008:
|
||||
dmaEnable = val & 1;
|
||||
break;
|
||||
case 0x0450000C:
|
||||
mem.mmio.mi.InterruptLower(MI::Interrupt::AI);
|
||||
break;
|
||||
case 0x04500010: {
|
||||
}
|
||||
break;
|
||||
case 0x04500008:
|
||||
dmaEnable = val & 1;
|
||||
break;
|
||||
case 0x0450000C:
|
||||
mem.mmio.mi.InterruptLower(MI::Interrupt::AI);
|
||||
break;
|
||||
case 0x04500010:
|
||||
{
|
||||
u32 oldDacFreq = dac.freq;
|
||||
dacRate = val & 0x3FFF;
|
||||
dac.freq = std::max(1.f, (float)GetVideoFrequency(mem.IsROMPAL()) / (dacRate + 1)) * 1.037;
|
||||
dac.period = N64_CPU_FREQ / dac.freq;
|
||||
if(oldDacFreq != dac.freq) {
|
||||
if (oldDacFreq != dac.freq) {
|
||||
device.AdjustSampleRate(dac.freq);
|
||||
}
|
||||
} break;
|
||||
case 0x04500014:
|
||||
bitrate = val & 0xF;
|
||||
dac.precision = bitrate + 1;
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unhandled AI write at addr {:08X} with val {:08X}", addr, val);
|
||||
}
|
||||
break;
|
||||
case 0x04500014:
|
||||
bitrate = val & 0xF;
|
||||
dac.precision = bitrate + 1;
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unhandled AI write at addr {:08X} with val {:08X}", addr, val);
|
||||
}
|
||||
}
|
||||
|
||||
void AI::Step(u32 cpuCycles, float volumeL, float volumeR) {
|
||||
cycles += cpuCycles;
|
||||
while(cycles > dac.period) {
|
||||
while (cycles > dac.period) {
|
||||
if (dmaCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(dmaLen[0] && dmaEnable) {
|
||||
if (dmaLen[0] && dmaEnable) {
|
||||
u32 addrHi = ((dmaAddr[0] >> 13) + dmaAddrCarry) & 0x7FF;
|
||||
dmaAddr[0] = (addrHi << 13) | (dmaAddr[0] & 0x1FFF);
|
||||
u32 data = mem.mmio.rdp.ReadRDRAM<u32>(dmaAddr[0]);
|
||||
s16 l = s16(data >> 16);
|
||||
s16 r = s16(data);
|
||||
|
||||
if(volumeR > 0 && volumeL > 0) {
|
||||
device.PushSample((float) l / INT16_MAX, volumeL, (float) r / INT16_MAX, volumeR);
|
||||
if (volumeR > 0 && volumeL > 0) {
|
||||
device.PushSample((float)l / INT16_MAX, volumeL, (float)r / INT16_MAX, volumeR);
|
||||
}
|
||||
|
||||
u32 addrLo = (dmaAddr[0] + 4) & 0x1FFF;
|
||||
@@ -98,8 +103,8 @@ void AI::Step(u32 cpuCycles, float volumeL, float volumeR) {
|
||||
dmaLen[0] -= 4;
|
||||
}
|
||||
|
||||
if(!dmaLen[0]) {
|
||||
if(--dmaCount > 0) {
|
||||
if (!dmaLen[0]) {
|
||||
if (--dmaCount > 0) {
|
||||
mem.mmio.mi.InterruptRaise(MI::Interrupt::AI);
|
||||
dmaAddr[0] = dmaAddr[1];
|
||||
dmaLen[0] = dmaLen[1];
|
||||
@@ -110,4 +115,4 @@ void AI::Step(u32 cpuCycles, float volumeL, float volumeR) {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -7,7 +7,7 @@ struct Mem;
|
||||
struct Registers;
|
||||
|
||||
struct AI {
|
||||
AI(Mem&, Registers& regs);
|
||||
AI(Mem &, Registers ®s);
|
||||
void Reset();
|
||||
auto Read(u32) const -> u32;
|
||||
void Write(u32, u32);
|
||||
@@ -27,8 +27,9 @@ struct AI {
|
||||
u32 period{N64_CPU_FREQ / freq};
|
||||
u32 precision{16};
|
||||
} dac;
|
||||
|
||||
private:
|
||||
Mem& mem;
|
||||
Registers& regs;
|
||||
Mem &mem;
|
||||
Registers ®s;
|
||||
};
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include <Audio.hpp>
|
||||
#include <log.hpp>
|
||||
#include <SDL2/SDL.h>
|
||||
#include <log.hpp>
|
||||
|
||||
namespace n64 {
|
||||
#define AUDIO_SAMPLE_RATE 44100
|
||||
@@ -8,8 +8,8 @@ namespace n64 {
|
||||
#define SYSTEM_SAMPLE_SIZE 4
|
||||
#define BYTES_PER_HALF_SECOND (((float)AUDIO_SAMPLE_RATE / 2) * SYSTEM_SAMPLE_SIZE)
|
||||
|
||||
void audioCallback(void* user, Uint8* stream, int length) {
|
||||
auto audioDevice = (AudioDevice*)user;
|
||||
void audioCallback(void *user, Uint8 *stream, int length) {
|
||||
auto audioDevice = (AudioDevice *)user;
|
||||
int gotten = 0, available = 0;
|
||||
|
||||
if (audioDevice) {
|
||||
@@ -22,7 +22,7 @@ void audioCallback(void* user, Uint8* stream, int length) {
|
||||
}
|
||||
|
||||
int gottenSamples = (int)(gotten / sizeof(float));
|
||||
auto* out = (float*)stream;
|
||||
auto *out = (float *)stream;
|
||||
out += gottenSamples;
|
||||
|
||||
for (int i = gottenSamples; i < length / sizeof(float); i++) {
|
||||
@@ -38,11 +38,13 @@ AudioDevice::~AudioDevice() {
|
||||
SDL_DestroyMutex(audioStreamMutex);
|
||||
}
|
||||
|
||||
AudioDevice::AudioDevice() : audioStream(SDL_NewAudioStream(SYSTEM_SAMPLE_FORMAT, 2, AUDIO_SAMPLE_RATE, SYSTEM_SAMPLE_FORMAT, 2, AUDIO_SAMPLE_RATE)),
|
||||
audioStreamMutex(SDL_CreateMutex()) {
|
||||
AudioDevice::AudioDevice() :
|
||||
audioStream(
|
||||
SDL_NewAudioStream(SYSTEM_SAMPLE_FORMAT, 2, AUDIO_SAMPLE_RATE, SYSTEM_SAMPLE_FORMAT, 2, AUDIO_SAMPLE_RATE)),
|
||||
audioStreamMutex(SDL_CreateMutex()) {
|
||||
SDL_InitSubSystem(SDL_INIT_AUDIO);
|
||||
|
||||
if(!audioStreamMutex) {
|
||||
if (!audioStreamMutex) {
|
||||
Util::panic("Unable to initialize audio mutex: {}", SDL_GetError());
|
||||
}
|
||||
|
||||
@@ -51,11 +53,11 @@ AudioDevice::AudioDevice() : audioStream(SDL_NewAudioStream(SYSTEM_SAMPLE_FORMAT
|
||||
request.channels = 2;
|
||||
request.samples = 1024;
|
||||
request.callback = audioCallback;
|
||||
request.userdata = (void*)this;
|
||||
request.userdata = (void *)this;
|
||||
|
||||
handle = SDL_OpenAudioDevice(nullptr, 0, &request, &audioSpec, 0);
|
||||
|
||||
if(!handle) {
|
||||
if (!handle) {
|
||||
Util::panic("Failed to initialize SDL Audio: {}", SDL_GetError());
|
||||
}
|
||||
|
||||
@@ -65,10 +67,10 @@ AudioDevice::AudioDevice() : audioStream(SDL_NewAudioStream(SYSTEM_SAMPLE_FORMAT
|
||||
void AudioDevice::PushSample(float left, float volumeL, float right, float volumeR) {
|
||||
float adjustedL = left * volumeL;
|
||||
float adjustedR = right * volumeR;
|
||||
float samples[2]{ adjustedL, adjustedR };
|
||||
float samples[2]{adjustedL, adjustedR};
|
||||
|
||||
auto availableBytes = (float)SDL_AudioStreamAvailable(audioStream);
|
||||
if(availableBytes <= BYTES_PER_HALF_SECOND) {
|
||||
if (availableBytes <= BYTES_PER_HALF_SECOND) {
|
||||
SDL_AudioStreamPut(audioStream, samples, 2 * sizeof(float));
|
||||
}
|
||||
}
|
||||
@@ -79,4 +81,4 @@ void AudioDevice::AdjustSampleRate(int sampleRate) {
|
||||
audioStream = SDL_NewAudioStream(SYSTEM_SAMPLE_FORMAT, 2, sampleRate, SYSTEM_SAMPLE_FORMAT, 2, AUDIO_SAMPLE_RATE);
|
||||
UnlockMutex();
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -10,7 +10,7 @@ struct AudioDevice {
|
||||
void PushSample(float, float, float, float);
|
||||
void AdjustSampleRate(int);
|
||||
void LockMutex() {
|
||||
if(audioStreamMutex)
|
||||
if (audioStreamMutex)
|
||||
SDL_LockMutex(audioStreamMutex);
|
||||
}
|
||||
void UnlockMutex() {
|
||||
@@ -18,13 +18,14 @@ struct AudioDevice {
|
||||
SDL_UnlockMutex(audioStreamMutex);
|
||||
}
|
||||
|
||||
SDL_AudioStream* GetStream() { return audioStream; }
|
||||
SDL_AudioStream *GetStream() { return audioStream; }
|
||||
|
||||
private:
|
||||
SDL_AudioStream* audioStream;
|
||||
SDL_mutex* audioStreamMutex;
|
||||
SDL_AudioStream *audioStream;
|
||||
SDL_mutex *audioStreamMutex;
|
||||
SDL_AudioSpec audioSpec{};
|
||||
SDL_AudioSpec request{};
|
||||
SDL_AudioDeviceID handle{};
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -3,50 +3,50 @@
|
||||
|
||||
namespace n64 {
|
||||
void MI::InterruptRaise(Interrupt intr) {
|
||||
switch(intr) {
|
||||
case Interrupt::VI:
|
||||
miIntr.vi = true;
|
||||
break;
|
||||
case Interrupt::SI:
|
||||
miIntr.si = true;
|
||||
break;
|
||||
case Interrupt::PI:
|
||||
miIntr.pi = true;
|
||||
break;
|
||||
case Interrupt::AI:
|
||||
miIntr.ai = true;
|
||||
break;
|
||||
case Interrupt::DP:
|
||||
miIntr.dp = true;
|
||||
break;
|
||||
case Interrupt::SP:
|
||||
miIntr.sp = true;
|
||||
break;
|
||||
switch (intr) {
|
||||
case Interrupt::VI:
|
||||
miIntr.vi = true;
|
||||
break;
|
||||
case Interrupt::SI:
|
||||
miIntr.si = true;
|
||||
break;
|
||||
case Interrupt::PI:
|
||||
miIntr.pi = true;
|
||||
break;
|
||||
case Interrupt::AI:
|
||||
miIntr.ai = true;
|
||||
break;
|
||||
case Interrupt::DP:
|
||||
miIntr.dp = true;
|
||||
break;
|
||||
case Interrupt::SP:
|
||||
miIntr.sp = true;
|
||||
break;
|
||||
}
|
||||
|
||||
UpdateInterrupt();
|
||||
}
|
||||
|
||||
void MI::InterruptLower(Interrupt intr) {
|
||||
switch(intr) {
|
||||
case Interrupt::VI:
|
||||
miIntr.vi = false;
|
||||
break;
|
||||
case Interrupt::SI:
|
||||
miIntr.si = false;
|
||||
break;
|
||||
case Interrupt::PI:
|
||||
miIntr.pi = false;
|
||||
break;
|
||||
case Interrupt::AI:
|
||||
miIntr.ai = false;
|
||||
break;
|
||||
case Interrupt::DP:
|
||||
miIntr.dp = false;
|
||||
break;
|
||||
case Interrupt::SP:
|
||||
miIntr.sp = false;
|
||||
break;
|
||||
switch (intr) {
|
||||
case Interrupt::VI:
|
||||
miIntr.vi = false;
|
||||
break;
|
||||
case Interrupt::SI:
|
||||
miIntr.si = false;
|
||||
break;
|
||||
case Interrupt::PI:
|
||||
miIntr.pi = false;
|
||||
break;
|
||||
case Interrupt::AI:
|
||||
miIntr.ai = false;
|
||||
break;
|
||||
case Interrupt::DP:
|
||||
miIntr.dp = false;
|
||||
break;
|
||||
case Interrupt::SP:
|
||||
miIntr.sp = false;
|
||||
break;
|
||||
}
|
||||
|
||||
UpdateInterrupt();
|
||||
@@ -56,4 +56,4 @@ void MI::UpdateInterrupt() {
|
||||
bool interrupt = miIntr.raw & miIntrMask.raw;
|
||||
regs.cop0.cause.ip2 = interrupt;
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
#define MI_VERSION_REG 0x02020102
|
||||
|
||||
namespace n64 {
|
||||
MI::MI(Registers& regs) : regs(regs) {
|
||||
Reset();
|
||||
}
|
||||
MI::MI(Registers ®s) : regs(regs) { Reset(); }
|
||||
|
||||
void MI::Reset() {
|
||||
miIntrMask.raw = 0;
|
||||
@@ -16,68 +14,74 @@ void MI::Reset() {
|
||||
}
|
||||
|
||||
auto MI::Read(u32 paddr) const -> u32 {
|
||||
switch(paddr & 0xF) {
|
||||
case 0x0: return miMode & 0x3FF;
|
||||
case 0x4: return MI_VERSION_REG;
|
||||
case 0x8: return miIntr.raw & 0x3F;
|
||||
case 0xC: return miIntrMask.raw & 0x3F;
|
||||
default:
|
||||
Util::panic("Unhandled MI[{:08X}] read", paddr);
|
||||
switch (paddr & 0xF) {
|
||||
case 0x0:
|
||||
return miMode & 0x3FF;
|
||||
case 0x4:
|
||||
return MI_VERSION_REG;
|
||||
case 0x8:
|
||||
return miIntr.raw & 0x3F;
|
||||
case 0xC:
|
||||
return miIntrMask.raw & 0x3F;
|
||||
default:
|
||||
Util::panic("Unhandled MI[{:08X}] read", paddr);
|
||||
}
|
||||
}
|
||||
|
||||
void MI::Write(u32 paddr, u32 val) {
|
||||
switch(paddr & 0xF) {
|
||||
case 0x0:
|
||||
miMode &= 0xFFFFFF80;
|
||||
miMode |= val & 0x7F;
|
||||
if (val & (1 << 7)) {
|
||||
miMode &= ~(1 << 7);
|
||||
switch (paddr & 0xF) {
|
||||
case 0x0:
|
||||
miMode &= 0xFFFFFF80;
|
||||
miMode |= val & 0x7F;
|
||||
if (val & (1 << 7)) {
|
||||
miMode &= ~(1 << 7);
|
||||
}
|
||||
|
||||
if (val & (1 << 8)) {
|
||||
miMode |= 1 << 7;
|
||||
}
|
||||
|
||||
if (val & (1 << 9)) {
|
||||
miMode &= ~(1 << 8);
|
||||
}
|
||||
|
||||
if (val & (1 << 10)) {
|
||||
miMode |= 1 << 8;
|
||||
}
|
||||
|
||||
if (val & (1 << 11)) {
|
||||
InterruptLower(Interrupt::DP);
|
||||
}
|
||||
|
||||
if (val & (1 << 12)) {
|
||||
miMode &= ~(1 << 9);
|
||||
}
|
||||
|
||||
if (val & (1 << 13)) {
|
||||
miMode |= 1 << 9;
|
||||
}
|
||||
break;
|
||||
case 0x4:
|
||||
case 0x8:
|
||||
break;
|
||||
case 0xC:
|
||||
for (int bit = 0; bit < 6; bit++) {
|
||||
int clearbit = bit << 1;
|
||||
int setbit = (bit << 1) + 1;
|
||||
|
||||
if (val & (1 << clearbit)) {
|
||||
miIntrMask.raw &= ~(1 << bit);
|
||||
}
|
||||
|
||||
if (val & (1 << 8)) {
|
||||
miMode |= 1 << 7;
|
||||
if (val & (1 << setbit)) {
|
||||
miIntrMask.raw |= 1 << bit;
|
||||
}
|
||||
}
|
||||
|
||||
if (val & (1 << 9)) {
|
||||
miMode &= ~(1 << 8);
|
||||
}
|
||||
|
||||
if (val & (1 << 10)) {
|
||||
miMode |= 1 << 8;
|
||||
}
|
||||
|
||||
if (val & (1 << 11)) {
|
||||
InterruptLower(Interrupt::DP);
|
||||
}
|
||||
|
||||
if (val & (1 << 12)) {
|
||||
miMode &= ~(1 << 9);
|
||||
}
|
||||
|
||||
if (val & (1 << 13)) {
|
||||
miMode |= 1 << 9;
|
||||
}
|
||||
break;
|
||||
case 0x4: case 0x8: break;
|
||||
case 0xC:
|
||||
for (int bit = 0; bit < 6; bit++) {
|
||||
int clearbit = bit << 1;
|
||||
int setbit = (bit << 1) + 1;
|
||||
|
||||
if (val & (1 << clearbit)) {
|
||||
miIntrMask.raw &= ~(1 << bit);
|
||||
}
|
||||
|
||||
if (val & (1 << setbit)) {
|
||||
miIntrMask.raw |= 1 << bit;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateInterrupt();
|
||||
break;
|
||||
default:
|
||||
Util::panic_trace("Unhandled MI write @ 0x{:08X} with value 0x{:08X}", paddr, val);
|
||||
UpdateInterrupt();
|
||||
break;
|
||||
default:
|
||||
Util::panic_trace("Unhandled MI write @ 0x{:08X} with value 0x{:08X}", paddr, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -5,13 +5,13 @@ namespace n64 {
|
||||
|
||||
union MIIntr {
|
||||
struct {
|
||||
unsigned sp: 1;
|
||||
unsigned si: 1;
|
||||
unsigned ai: 1;
|
||||
unsigned vi: 1;
|
||||
unsigned pi: 1;
|
||||
unsigned dp: 1;
|
||||
unsigned: 26;
|
||||
unsigned sp : 1;
|
||||
unsigned si : 1;
|
||||
unsigned ai : 1;
|
||||
unsigned vi : 1;
|
||||
unsigned pi : 1;
|
||||
unsigned dp : 1;
|
||||
unsigned : 26;
|
||||
};
|
||||
u32 raw;
|
||||
};
|
||||
@@ -19,11 +19,9 @@ union MIIntr {
|
||||
struct Registers;
|
||||
|
||||
struct MI {
|
||||
enum class Interrupt : u8 {
|
||||
VI, SI, PI, AI, DP, SP
|
||||
};
|
||||
enum class Interrupt : u8 { VI, SI, PI, AI, DP, SP };
|
||||
|
||||
MI(Registers&);
|
||||
MI(Registers &);
|
||||
void Reset();
|
||||
[[nodiscard]] auto Read(u32) const -> u32;
|
||||
void Write(u32, u32);
|
||||
@@ -33,6 +31,6 @@ struct MI {
|
||||
|
||||
u32 miMode{};
|
||||
MIIntr miIntr{}, miIntrMask{};
|
||||
Registers& regs;
|
||||
Registers ®s;
|
||||
};
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
#include <core/mmio/PI.hpp>
|
||||
#include <log.hpp>
|
||||
#include <Core.hpp>
|
||||
#include <Scheduler.hpp>
|
||||
#include <core/mmio/PI.hpp>
|
||||
#include <log.hpp>
|
||||
|
||||
namespace n64 {
|
||||
PI::PI(Mem& mem, Registers& regs) : mem(mem), regs(regs) {
|
||||
Reset();
|
||||
}
|
||||
PI::PI(Mem &mem, Registers ®s) : mem(mem), regs(regs) { Reset(); }
|
||||
|
||||
void PI::Reset() {
|
||||
dmaBusy = false;
|
||||
@@ -46,86 +44,107 @@ bool PI::ReadLatch() {
|
||||
return true;
|
||||
}
|
||||
|
||||
template<> auto PI::BusRead<u8, true>(u32 addr) -> u8 {
|
||||
template <>
|
||||
auto PI::BusRead<u8, true>(u32 addr) -> u8 {
|
||||
switch (addr) {
|
||||
case REGION_PI_UNKNOWN:
|
||||
Util::panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, returning FF because it is not emulated", addr);
|
||||
case REGION_PI_64DD_REG:
|
||||
Util::panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, returning FF because it is not emulated", addr);
|
||||
case REGION_PI_64DD_ROM:
|
||||
Util::warn("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM - This is the N64DD, returning FF because it is not emulated", addr);
|
||||
return 0xFF;
|
||||
case REGION_PI_SRAM:
|
||||
return mem.BackupRead<u8>(addr - SREGION_PI_SRAM);
|
||||
case REGION_PI_ROM: {
|
||||
case REGION_PI_UNKNOWN:
|
||||
Util::panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, "
|
||||
"returning FF because it is not emulated",
|
||||
addr);
|
||||
case REGION_PI_64DD_REG:
|
||||
Util::panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, "
|
||||
"returning FF because it is not emulated",
|
||||
addr);
|
||||
case REGION_PI_64DD_ROM:
|
||||
Util::warn("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM - This is the N64DD, "
|
||||
"returning FF because it is not emulated",
|
||||
addr);
|
||||
return 0xFF;
|
||||
case REGION_PI_SRAM:
|
||||
return mem.BackupRead<u8>(addr - SREGION_PI_SRAM);
|
||||
case REGION_PI_ROM:
|
||||
{
|
||||
// round to nearest 4 byte boundary, keeping old LSB
|
||||
u32 index = BYTE_ADDRESS(addr) - SREGION_PI_ROM;
|
||||
if (index >= mem.rom.cart.size()) {
|
||||
Util::warn("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM! ({}/0x{:016X})", addr, index, index, mem.rom.cart.size(), mem.rom.cart.size());
|
||||
Util::warn("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM! ({}/0x{:016X})", addr,
|
||||
index, index, mem.rom.cart.size(), mem.rom.cart.size());
|
||||
return 0xFF;
|
||||
}
|
||||
return mem.rom.cart[index];
|
||||
}
|
||||
default:
|
||||
Util::panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr);
|
||||
default:
|
||||
Util::panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr);
|
||||
}
|
||||
}
|
||||
|
||||
template<> auto PI::BusRead<u8, false>(u32 addr) -> u8 {
|
||||
template <>
|
||||
auto PI::BusRead<u8, false>(u32 addr) -> u8 {
|
||||
if (!ReadLatch()) [[unlikely]] {
|
||||
return latch >> 24;
|
||||
}
|
||||
|
||||
switch (addr) {
|
||||
case REGION_PI_UNKNOWN:
|
||||
Util::panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, returning FF because it is not emulated", addr);
|
||||
case REGION_PI_64DD_REG:
|
||||
Util::panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, returning FF because it is not emulated", addr);
|
||||
case REGION_PI_64DD_ROM:
|
||||
Util::warn("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM - This is the N64DD, returning FF because it is not emulated", addr);
|
||||
return 0xFF;
|
||||
case REGION_PI_SRAM:
|
||||
return mem.BackupRead<u8>(addr - SREGION_PI_SRAM);
|
||||
case REGION_PI_ROM: {
|
||||
case REGION_PI_UNKNOWN:
|
||||
Util::panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, "
|
||||
"returning FF because it is not emulated",
|
||||
addr);
|
||||
case REGION_PI_64DD_REG:
|
||||
Util::panic("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, "
|
||||
"returning FF because it is not emulated",
|
||||
addr);
|
||||
case REGION_PI_64DD_ROM:
|
||||
Util::warn("Reading byte from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM - This is the N64DD, "
|
||||
"returning FF because it is not emulated",
|
||||
addr);
|
||||
return 0xFF;
|
||||
case REGION_PI_SRAM:
|
||||
return mem.BackupRead<u8>(addr - SREGION_PI_SRAM);
|
||||
case REGION_PI_ROM:
|
||||
{
|
||||
addr = (addr + 2) & ~2;
|
||||
// round to nearest 4 byte boundary, keeping old LSB
|
||||
u32 index = BYTE_ADDRESS(addr) - SREGION_PI_ROM;
|
||||
if (index >= mem.rom.cart.size()) {
|
||||
Util::warn("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM! ({}/0x{:016X})", addr, index, index, mem.rom.cart.size(), mem.rom.cart.size());
|
||||
Util::warn("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM! ({}/0x{:016X})", addr,
|
||||
index, index, mem.rom.cart.size(), mem.rom.cart.size());
|
||||
return 0xFF;
|
||||
}
|
||||
return mem.rom.cart[index];
|
||||
}
|
||||
default:
|
||||
Util::panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr);
|
||||
default:
|
||||
Util::panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr);
|
||||
}
|
||||
}
|
||||
|
||||
template<> void PI::BusWrite<u8, true>(u32 addr, u32 val) {
|
||||
template <>
|
||||
void PI::BusWrite<u8, true>(u32 addr, u32 val) {
|
||||
switch (addr) {
|
||||
case REGION_PI_UNKNOWN:
|
||||
Util::panic("Writing byte 0x{:02X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr);
|
||||
case REGION_PI_64DD_REG:
|
||||
if (addr == 0x05000020) {
|
||||
fprintf(stderr, "%c", val);
|
||||
} else {
|
||||
Util::warn("Writing byte 0x{:02X} to address 0x{:08X} in region: REGION_PI_64DD_ROM, this is the 64DD, ignoring!", val, addr);
|
||||
}
|
||||
break;
|
||||
case REGION_PI_64DD_ROM:
|
||||
Util::panic("Writing byte 0x{:02X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", val, addr);
|
||||
case REGION_PI_SRAM:
|
||||
mem.BackupWrite<u8>(addr - SREGION_PI_SRAM, val);
|
||||
break;
|
||||
case REGION_PI_ROM:
|
||||
Util::warn("Writing byte 0x{:02X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr);
|
||||
break;
|
||||
default:
|
||||
Util::panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr);
|
||||
case REGION_PI_UNKNOWN:
|
||||
Util::panic("Writing byte 0x{:02X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr);
|
||||
case REGION_PI_64DD_REG:
|
||||
if (addr == 0x05000020) {
|
||||
fprintf(stderr, "%c", val);
|
||||
} else {
|
||||
Util::warn("Writing byte 0x{:02X} to address 0x{:08X} in region: REGION_PI_64DD_ROM, this is the 64DD, ignoring!",
|
||||
val, addr);
|
||||
}
|
||||
break;
|
||||
case REGION_PI_64DD_ROM:
|
||||
Util::panic("Writing byte 0x{:02X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", val, addr);
|
||||
case REGION_PI_SRAM:
|
||||
mem.BackupWrite<u8>(addr - SREGION_PI_SRAM, val);
|
||||
break;
|
||||
case REGION_PI_ROM:
|
||||
Util::warn("Writing byte 0x{:02X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr);
|
||||
break;
|
||||
default:
|
||||
Util::panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr);
|
||||
}
|
||||
}
|
||||
|
||||
template<> void PI::BusWrite<u8, false>(u32 addr, u32 val) {
|
||||
template <>
|
||||
void PI::BusWrite<u8, false>(u32 addr, u32 val) {
|
||||
u8 latch_shift = 24 - (addr & 1) * 8;
|
||||
|
||||
if (!WriteLatch(val << latch_shift) && addr != 0x05000020) [[unlikely]] {
|
||||
@@ -135,21 +154,29 @@ template<> void PI::BusWrite<u8, false>(u32 addr, u32 val) {
|
||||
BusWrite<u8, true>(addr, val);
|
||||
}
|
||||
|
||||
template <> auto PI::BusRead<u16, false>(u32 addr) -> u16 {
|
||||
template <>
|
||||
auto PI::BusRead<u16, false>(u32 addr) -> u16 {
|
||||
if (!ReadLatch()) [[unlikely]] {
|
||||
return latch >> 16;
|
||||
}
|
||||
|
||||
switch (addr) {
|
||||
case REGION_PI_UNKNOWN:
|
||||
Util::panic("Reading half from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, returning FF because it is not emulated", addr);
|
||||
case REGION_PI_64DD_REG:
|
||||
Util::panic("Reading half from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, returning FF because it is not emulated", addr);
|
||||
case REGION_PI_64DD_ROM:
|
||||
Util::panic("Reading half from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM - This is the N64DD, returning FF because it is not emulated", addr);
|
||||
case REGION_PI_SRAM:
|
||||
Util::panic("Reading half from address 0x{:08X} in unsupported region: REGION_PI_SRAM", addr);
|
||||
case REGION_PI_ROM: {
|
||||
case REGION_PI_UNKNOWN:
|
||||
Util::panic("Reading half from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, "
|
||||
"returning FF because it is not emulated",
|
||||
addr);
|
||||
case REGION_PI_64DD_REG:
|
||||
Util::panic("Reading half from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, "
|
||||
"returning FF because it is not emulated",
|
||||
addr);
|
||||
case REGION_PI_64DD_ROM:
|
||||
Util::panic("Reading half from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM - This is the N64DD, "
|
||||
"returning FF because it is not emulated",
|
||||
addr);
|
||||
case REGION_PI_SRAM:
|
||||
Util::panic("Reading half from address 0x{:08X} in unsupported region: REGION_PI_SRAM", addr);
|
||||
case REGION_PI_ROM:
|
||||
{
|
||||
addr = (addr + 2) & ~3;
|
||||
u32 index = HALF_ADDRESS(addr) - SREGION_PI_ROM;
|
||||
if (index > mem.rom.cart.size() - 1) {
|
||||
@@ -157,67 +184,80 @@ template <> auto PI::BusRead<u16, false>(u32 addr) -> u16 {
|
||||
}
|
||||
return Util::ReadAccess<u16>(mem.rom.cart, index);
|
||||
}
|
||||
default:
|
||||
Util::panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr);
|
||||
default:
|
||||
Util::panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr);
|
||||
}
|
||||
}
|
||||
|
||||
template <> auto PI::BusRead<u16, true>(u32 addr) -> u16 {
|
||||
template <>
|
||||
auto PI::BusRead<u16, true>(u32 addr) -> u16 {
|
||||
return BusRead<u16, false>(addr);
|
||||
}
|
||||
|
||||
template <> void PI::BusWrite<u16, false>(u32 addr, u32 val) {
|
||||
template <>
|
||||
void PI::BusWrite<u16, false>(u32 addr, u32 val) {
|
||||
if (!WriteLatch(val << 16)) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (addr) {
|
||||
case REGION_PI_UNKNOWN:
|
||||
Util::panic("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr);
|
||||
case REGION_PI_64DD_REG:
|
||||
Util::panic("Writing half 0x{:04X} to address 0x{:08X} in region: REGION_PI_64DD_ROM, this is the 64DD, ignoring!", val, addr);
|
||||
case REGION_PI_64DD_ROM:
|
||||
Util::panic("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", val, addr);
|
||||
case REGION_PI_SRAM:
|
||||
Util::panic("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_SRAM", val, addr);
|
||||
case REGION_PI_ROM:
|
||||
Util::warn("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr);
|
||||
break;
|
||||
default:
|
||||
Util::panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr);
|
||||
case REGION_PI_UNKNOWN:
|
||||
Util::panic("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr);
|
||||
case REGION_PI_64DD_REG:
|
||||
Util::panic("Writing half 0x{:04X} to address 0x{:08X} in region: REGION_PI_64DD_ROM, this is the 64DD, ignoring!",
|
||||
val, addr);
|
||||
case REGION_PI_64DD_ROM:
|
||||
Util::panic("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", val, addr);
|
||||
case REGION_PI_SRAM:
|
||||
Util::panic("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_SRAM", val, addr);
|
||||
case REGION_PI_ROM:
|
||||
Util::warn("Writing half 0x{:04X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr);
|
||||
break;
|
||||
default:
|
||||
Util::panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr);
|
||||
}
|
||||
}
|
||||
|
||||
template <> void PI::BusWrite<u16, true>(u32 addr, u32 val) {
|
||||
template <>
|
||||
void PI::BusWrite<u16, true>(u32 addr, u32 val) {
|
||||
BusWrite<u16, false>(addr, val);
|
||||
}
|
||||
|
||||
template <> auto PI::BusRead<u32, false>(u32 addr) -> u32 {
|
||||
template <>
|
||||
auto PI::BusRead<u32, false>(u32 addr) -> u32 {
|
||||
if (!ReadLatch()) [[unlikely]] {
|
||||
return latch;
|
||||
}
|
||||
|
||||
switch (addr) {
|
||||
case REGION_PI_UNKNOWN:
|
||||
Util::warn("Reading word from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, returning FF because it is not emulated", addr);
|
||||
return 0xFF;
|
||||
case REGION_PI_64DD_REG:
|
||||
Util::warn("Reading word from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, returning FF because it is not emulated", addr);
|
||||
return 0xFF;
|
||||
case REGION_PI_64DD_ROM:
|
||||
Util::warn("Reading word from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM - This is the N64DD, returning FF because it is not emulated", addr);
|
||||
return 0xFF;
|
||||
case REGION_PI_SRAM:
|
||||
return mem.BackupRead<u32>(addr);
|
||||
case REGION_PI_ROM: {
|
||||
case REGION_PI_UNKNOWN:
|
||||
Util::warn("Reading word from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN - This is the N64DD, "
|
||||
"returning FF because it is not emulated",
|
||||
addr);
|
||||
return 0xFF;
|
||||
case REGION_PI_64DD_REG:
|
||||
Util::warn("Reading word from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG - This is the N64DD, "
|
||||
"returning FF because it is not emulated",
|
||||
addr);
|
||||
return 0xFF;
|
||||
case REGION_PI_64DD_ROM:
|
||||
Util::warn("Reading word from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM - This is the N64DD, "
|
||||
"returning FF because it is not emulated",
|
||||
addr);
|
||||
return 0xFF;
|
||||
case REGION_PI_SRAM:
|
||||
return mem.BackupRead<u32>(addr);
|
||||
case REGION_PI_ROM:
|
||||
{
|
||||
u32 index = addr - SREGION_PI_ROM;
|
||||
if (index > mem.rom.cart.size() - 3) { // -3 because we're reading an entire word
|
||||
switch (addr) {
|
||||
case REGION_CART_ISVIEWER_BUFFER:
|
||||
return htobe32(Util::ReadAccess<u32>(mem.isviewer, addr - SREGION_CART_ISVIEWER_BUFFER));
|
||||
case CART_ISVIEWER_FLUSH:
|
||||
Util::panic("Read from ISViewer flush!");
|
||||
default: break;
|
||||
case REGION_CART_ISVIEWER_BUFFER:
|
||||
return htobe32(Util::ReadAccess<u32>(mem.isviewer, addr - SREGION_CART_ISVIEWER_BUFFER));
|
||||
case CART_ISVIEWER_FLUSH:
|
||||
Util::panic("Read from ISViewer flush!");
|
||||
default:
|
||||
break;
|
||||
}
|
||||
Util::warn("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM!", addr, index, index);
|
||||
return 0;
|
||||
@@ -225,66 +265,71 @@ template <> auto PI::BusRead<u32, false>(u32 addr) -> u32 {
|
||||
return Util::ReadAccess<u32>(mem.rom.cart, index);
|
||||
}
|
||||
}
|
||||
default:
|
||||
Util::panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr);
|
||||
default:
|
||||
Util::panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr);
|
||||
}
|
||||
}
|
||||
|
||||
template <> auto PI::BusRead<u32, true>(u32 addr) -> u32 {
|
||||
template <>
|
||||
auto PI::BusRead<u32, true>(u32 addr) -> u32 {
|
||||
return BusRead<u32, false>(addr);
|
||||
}
|
||||
|
||||
template <> void PI::BusWrite<u32, false>(u32 addr, u32 val) {
|
||||
template <>
|
||||
void PI::BusWrite<u32, false>(u32 addr, u32 val) {
|
||||
switch (addr) {
|
||||
case REGION_PI_UNKNOWN:
|
||||
if (!WriteLatch(val)) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
Util::warn("Writing word 0x{:08X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr);
|
||||
case REGION_PI_UNKNOWN:
|
||||
if (!WriteLatch(val)) [[unlikely]] {
|
||||
return;
|
||||
case REGION_PI_64DD_REG:
|
||||
if (!WriteLatch(val)) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
Util::warn("Writing word 0x{:08X} to address 0x{:08X} in region: REGION_PI_64DD_ROM, this is the 64DD, ignoring!", val, addr);
|
||||
}
|
||||
Util::warn("Writing word 0x{:08X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr);
|
||||
return;
|
||||
case REGION_PI_64DD_REG:
|
||||
if (!WriteLatch(val)) [[unlikely]] {
|
||||
return;
|
||||
case REGION_PI_64DD_ROM:
|
||||
if (!WriteLatch(val)) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
Util::warn("Writing word 0x{:08X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", val, addr);
|
||||
}
|
||||
Util::warn("Writing word 0x{:08X} to address 0x{:08X} in region: REGION_PI_64DD_ROM, this is the 64DD, ignoring!",
|
||||
val, addr);
|
||||
return;
|
||||
case REGION_PI_64DD_ROM:
|
||||
if (!WriteLatch(val)) [[unlikely]] {
|
||||
return;
|
||||
case REGION_PI_SRAM:
|
||||
if (!WriteLatch(val)) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
mem.BackupWrite<u32>(addr - SREGION_PI_SRAM, val);
|
||||
}
|
||||
Util::warn("Writing word 0x{:08X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", val, addr);
|
||||
return;
|
||||
case REGION_PI_SRAM:
|
||||
if (!WriteLatch(val)) [[unlikely]] {
|
||||
return;
|
||||
case REGION_PI_ROM:
|
||||
switch (addr) {
|
||||
case REGION_CART_ISVIEWER_BUFFER:
|
||||
Util::WriteAccess<u32>(mem.isviewer, addr - SREGION_CART_ISVIEWER_BUFFER, be32toh(val));
|
||||
break;
|
||||
case CART_ISVIEWER_FLUSH: {
|
||||
if (val < CART_ISVIEWER_SIZE) {
|
||||
std::string message(val + 1, 0);
|
||||
std::copy(mem.isviewer.begin(), mem.isviewer.begin() + val, message.begin());
|
||||
Util::print<Util::Always>("{}", message);
|
||||
} else {
|
||||
Util::panic("ISViewer buffer size is emulated at {} bytes, but received a flush command for {} bytes!", CART_ISVIEWER_SIZE, val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
mem.BackupWrite<u32>(addr - SREGION_PI_SRAM, val);
|
||||
return;
|
||||
case REGION_PI_ROM:
|
||||
switch (addr) {
|
||||
case REGION_CART_ISVIEWER_BUFFER:
|
||||
Util::WriteAccess<u32>(mem.isviewer, addr - SREGION_CART_ISVIEWER_BUFFER, be32toh(val));
|
||||
break;
|
||||
case CART_ISVIEWER_FLUSH:
|
||||
{
|
||||
if (val < CART_ISVIEWER_SIZE) {
|
||||
std::string message(val + 1, 0);
|
||||
std::copy(mem.isviewer.begin(), mem.isviewer.begin() + val, message.begin());
|
||||
Util::print<Util::Always>("{}", message);
|
||||
} else {
|
||||
Util::panic("ISViewer buffer size is emulated at {} bytes, but received a flush command for {} bytes!",
|
||||
CART_ISVIEWER_SIZE, val);
|
||||
}
|
||||
default:
|
||||
if (!WriteLatch(val)) [[unlikely]] {
|
||||
Util::warn("Couldn't latch PI bus, ignoring write to REGION_PI_ROM");
|
||||
return;
|
||||
}
|
||||
Util::warn("Writing word 0x{:08X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
default:
|
||||
Util::panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr);
|
||||
if (!WriteLatch(val)) [[unlikely]] {
|
||||
Util::warn("Couldn't latch PI bus, ignoring write to REGION_PI_ROM");
|
||||
return;
|
||||
}
|
||||
Util::warn("Writing word 0x{:08X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr);
|
||||
}
|
||||
return;
|
||||
default:
|
||||
Util::panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,69 +338,79 @@ void PI::BusWrite<u32, true>(u32 addr, u32 val) {
|
||||
BusWrite<u32, false>(addr, val);
|
||||
}
|
||||
|
||||
template <> auto PI::BusRead<u64, false>(u32 addr) -> u64 {
|
||||
template <>
|
||||
auto PI::BusRead<u64, false>(u32 addr) -> u64 {
|
||||
if (!ReadLatch()) [[unlikely]] {
|
||||
return (u64)latch << 32;
|
||||
}
|
||||
|
||||
switch (addr) {
|
||||
case REGION_PI_UNKNOWN:
|
||||
Util::panic("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", addr);
|
||||
case REGION_PI_64DD_REG:
|
||||
Util::panic("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG", addr);
|
||||
case REGION_PI_64DD_ROM:
|
||||
Util::panic("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", addr);
|
||||
case REGION_PI_SRAM:
|
||||
Util::panic("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_SRAM", addr);
|
||||
case REGION_PI_ROM: {
|
||||
case REGION_PI_UNKNOWN:
|
||||
Util::panic("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", addr);
|
||||
case REGION_PI_64DD_REG:
|
||||
Util::panic("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_64DD_REG", addr);
|
||||
case REGION_PI_64DD_ROM:
|
||||
Util::panic("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", addr);
|
||||
case REGION_PI_SRAM:
|
||||
Util::panic("Reading dword from address 0x{:08X} in unsupported region: REGION_PI_SRAM", addr);
|
||||
case REGION_PI_ROM:
|
||||
{
|
||||
u32 index = addr - SREGION_PI_ROM;
|
||||
if (index > mem.rom.cart.size() - 7) { // -7 because we're reading an entire dword
|
||||
Util::panic("Address 0x{:08X} accessed an index {}/0x{:X} outside the bounds of the ROM!", addr, index, index);
|
||||
}
|
||||
return Util::ReadAccess<u64>(mem.rom.cart, index);
|
||||
}
|
||||
default:
|
||||
Util::panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr);
|
||||
default:
|
||||
Util::panic("Should never end up here! Access to address {:08X} which did not match any PI bus regions!", addr);
|
||||
}
|
||||
}
|
||||
|
||||
template <> auto PI::BusRead<u64, true>(u32 addr) -> u64 {
|
||||
template <>
|
||||
auto PI::BusRead<u64, true>(u32 addr) -> u64 {
|
||||
return BusRead<u64, false>(addr);
|
||||
}
|
||||
|
||||
template <> void PI::BusWrite<false>(u32 addr, u64 val) {
|
||||
template <>
|
||||
void PI::BusWrite<false>(u32 addr, u64 val) {
|
||||
if (!WriteLatch(val >> 32)) [[unlikely]] {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (addr) {
|
||||
case REGION_PI_UNKNOWN:
|
||||
Util::panic("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr);
|
||||
case REGION_PI_64DD_REG:
|
||||
Util::panic("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_REG", val, addr);
|
||||
case REGION_PI_64DD_ROM:
|
||||
Util::panic("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", val, addr);
|
||||
case REGION_PI_SRAM:
|
||||
Util::panic("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_SRAM", val, addr);
|
||||
case REGION_PI_ROM:
|
||||
Util::warn("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr);
|
||||
break;
|
||||
default:
|
||||
Util::panic("Should never end up here! Access to address %08X which did not match any PI bus regions!", addr);
|
||||
case REGION_PI_UNKNOWN:
|
||||
Util::panic("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_UNKNOWN", val, addr);
|
||||
case REGION_PI_64DD_REG:
|
||||
Util::panic("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_REG", val, addr);
|
||||
case REGION_PI_64DD_ROM:
|
||||
Util::panic("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_64DD_ROM", val, addr);
|
||||
case REGION_PI_SRAM:
|
||||
Util::panic("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_SRAM", val, addr);
|
||||
case REGION_PI_ROM:
|
||||
Util::warn("Writing dword 0x{:016X} to address 0x{:08X} in unsupported region: REGION_PI_ROM", val, addr);
|
||||
break;
|
||||
default:
|
||||
Util::panic("Should never end up here! Access to address %08X which did not match any PI bus regions!", addr);
|
||||
}
|
||||
}
|
||||
|
||||
template <> void PI::BusWrite<true>(u32 addr, u64 val) {
|
||||
template <>
|
||||
void PI::BusWrite<true>(u32 addr, u64 val) {
|
||||
BusWrite<false>(addr, val);
|
||||
}
|
||||
|
||||
auto PI::Read(u32 addr) const -> u32 {
|
||||
switch(addr) {
|
||||
case 0x04600000: return dramAddr & 0x00FFFFFE;
|
||||
case 0x04600004: return cartAddr & 0xFFFFFFFE;
|
||||
case 0x04600008: return rdLen;
|
||||
case 0x0460000C: return wrLen;
|
||||
case 0x04600010: {
|
||||
switch (addr) {
|
||||
case 0x04600000:
|
||||
return dramAddr & 0x00FFFFFE;
|
||||
case 0x04600004:
|
||||
return cartAddr & 0xFFFFFFFE;
|
||||
case 0x04600008:
|
||||
return rdLen;
|
||||
case 0x0460000C:
|
||||
return wrLen;
|
||||
case 0x04600010:
|
||||
{
|
||||
u32 value = 0;
|
||||
value |= (dmaBusy << 0); // Is PI DMA active? No, because it's instant
|
||||
value |= (ioBusy << 1); // Is PI IO busy? No, because it's instant
|
||||
@@ -363,30 +418,38 @@ auto PI::Read(u32 addr) const -> u32 {
|
||||
value |= (mem.mmio.mi.miIntr.pi << 3); // PI interrupt?
|
||||
return value;
|
||||
}
|
||||
case 0x04600014: return piBsdDom1Lat;
|
||||
case 0x04600018: return piBsdDom1Pwd;
|
||||
case 0x0460001C: return piBsdDom1Pgs;
|
||||
case 0x04600020: return piBsdDom1Rls;
|
||||
case 0x04600024: return piBsdDom2Lat;
|
||||
case 0x04600028: return piBsdDom2Pwd;
|
||||
case 0x0460002C: return piBsdDom2Pgs;
|
||||
case 0x04600030: return piBsdDom2Rls;
|
||||
default:
|
||||
Util::panic("Unhandled PI[{:08X}] read", addr);
|
||||
case 0x04600014:
|
||||
return piBsdDom1Lat;
|
||||
case 0x04600018:
|
||||
return piBsdDom1Pwd;
|
||||
case 0x0460001C:
|
||||
return piBsdDom1Pgs;
|
||||
case 0x04600020:
|
||||
return piBsdDom1Rls;
|
||||
case 0x04600024:
|
||||
return piBsdDom2Lat;
|
||||
case 0x04600028:
|
||||
return piBsdDom2Pwd;
|
||||
case 0x0460002C:
|
||||
return piBsdDom2Pgs;
|
||||
case 0x04600030:
|
||||
return piBsdDom2Rls;
|
||||
default:
|
||||
Util::panic("Unhandled PI[{:08X}] read", addr);
|
||||
}
|
||||
}
|
||||
|
||||
u8 PI::GetDomain(u32 address) {
|
||||
switch (address) {
|
||||
case REGION_PI_UNKNOWN:
|
||||
case REGION_PI_64DD_ROM:
|
||||
case REGION_PI_ROM:
|
||||
return 1;
|
||||
case REGION_PI_64DD_REG:
|
||||
case REGION_PI_SRAM:
|
||||
return 2;
|
||||
default:
|
||||
Util::panic("Unknown PI domain for address {:08X}!", address);
|
||||
case REGION_PI_UNKNOWN:
|
||||
case REGION_PI_64DD_ROM:
|
||||
case REGION_PI_ROM:
|
||||
return 1;
|
||||
case REGION_PI_64DD_REG:
|
||||
case REGION_PI_SRAM:
|
||||
return 2;
|
||||
default:
|
||||
Util::panic("Unknown PI domain for address {:08X}!", address);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,20 +462,20 @@ u32 PI::AccessTiming(u8 domain, u32 length) const {
|
||||
uint32_t pages;
|
||||
|
||||
switch (domain) {
|
||||
case 1:
|
||||
latency = piBsdDom1Lat + 1;
|
||||
pulse_width = piBsdDom1Pwd + 1;
|
||||
release = piBsdDom1Rls + 1;
|
||||
page_size = std::pow(2, (piBsdDom1Pgs + 2));
|
||||
break;
|
||||
case 2:
|
||||
latency = piBsdDom2Lat + 1;
|
||||
pulse_width = piBsdDom2Pwd + 1;
|
||||
release = piBsdDom2Rls + 1;
|
||||
page_size = std::pow(2, (piBsdDom2Pgs + 2));
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unknown PI domain: {}\n", domain);
|
||||
case 1:
|
||||
latency = piBsdDom1Lat + 1;
|
||||
pulse_width = piBsdDom1Pwd + 1;
|
||||
release = piBsdDom1Rls + 1;
|
||||
page_size = std::pow(2, (piBsdDom1Pgs + 2));
|
||||
break;
|
||||
case 2:
|
||||
latency = piBsdDom2Lat + 1;
|
||||
pulse_width = piBsdDom2Pwd + 1;
|
||||
release = piBsdDom2Rls + 1;
|
||||
page_size = std::pow(2, (piBsdDom2Pgs + 2));
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unknown PI domain: {}\n", domain);
|
||||
}
|
||||
|
||||
pages = ceil((double)length / page_size);
|
||||
@@ -424,11 +487,12 @@ u32 PI::AccessTiming(u8 domain, u32 length) const {
|
||||
}
|
||||
|
||||
// rdram -> cart
|
||||
template <> void PI::DMA<false>() {
|
||||
template <>
|
||||
void PI::DMA<false>() {
|
||||
s32 len = rdLen + 1;
|
||||
Util::trace("PI DMA from RDRAM to CARTRIDGE (size: {} B, {:08X} to {:08X})", len, dramAddr, cartAddr);
|
||||
|
||||
if(mem.saveType == SAVE_FLASH_1m && cartAddr >= SREGION_PI_SRAM && cartAddr < 0x08010000) {
|
||||
if (mem.saveType == SAVE_FLASH_1m && cartAddr >= SREGION_PI_SRAM && cartAddr < 0x08010000) {
|
||||
cartAddr = SREGION_PI_SRAM | ((cartAddr & 0xFFFFF) << 1);
|
||||
}
|
||||
|
||||
@@ -438,60 +502,88 @@ template <> void PI::DMA<false>() {
|
||||
dramAddr += len;
|
||||
dramAddr = (dramAddr + 7) & ~7;
|
||||
cartAddr += len;
|
||||
if(cartAddr & 1) cartAddr += 1;
|
||||
if (cartAddr & 1)
|
||||
cartAddr += 1;
|
||||
|
||||
dmaBusy = true;
|
||||
scheduler.EnqueueRelative(AccessTiming(GetDomain(cartAddr), rdLen), PI_DMA_COMPLETE);
|
||||
}
|
||||
|
||||
// cart -> rdram
|
||||
template <> void PI::DMA<true>() {
|
||||
template <>
|
||||
void PI::DMA<true>() {
|
||||
s32 len = wrLen + 1;
|
||||
Util::trace("PI DMA from CARTRIDGE to RDRAM (size: {} B, {:08X} to {:08X})", len, cartAddr, dramAddr);
|
||||
|
||||
if(mem.saveType == SAVE_FLASH_1m && cartAddr >= SREGION_PI_SRAM && cartAddr < 0x08010000) {
|
||||
if (mem.saveType == SAVE_FLASH_1m && cartAddr >= SREGION_PI_SRAM && cartAddr < 0x08010000) {
|
||||
cartAddr = SREGION_PI_SRAM | ((cartAddr & 0xFFFFF) << 1);
|
||||
}
|
||||
|
||||
for(u32 i = 0; i < len; i++) {
|
||||
for (u32 i = 0; i < len; i++) {
|
||||
mem.mmio.rdp.WriteRDRAM<u8>(dramAddr + i, BusRead<u8, true>(cartAddr + i));
|
||||
}
|
||||
dramAddr += len;
|
||||
dramAddr = (dramAddr + 7) & ~7;
|
||||
cartAddr += len;
|
||||
if(cartAddr & 1) cartAddr += 1;
|
||||
if (cartAddr & 1)
|
||||
cartAddr += 1;
|
||||
|
||||
dmaBusy = true;
|
||||
scheduler.EnqueueRelative(AccessTiming(GetDomain(cartAddr), len), PI_DMA_COMPLETE);
|
||||
}
|
||||
|
||||
void PI::Write(u32 addr, u32 val) {
|
||||
MI& mi = mem.mmio.mi;
|
||||
switch(addr) {
|
||||
case 0x04600000: dramAddr = val & 0x00FFFFFE; break;
|
||||
case 0x04600004: cartAddr = val & 0xFFFFFFFE; break;
|
||||
case 0x04600008: {
|
||||
MI &mi = mem.mmio.mi;
|
||||
switch (addr) {
|
||||
case 0x04600000:
|
||||
dramAddr = val & 0x00FFFFFE;
|
||||
break;
|
||||
case 0x04600004:
|
||||
cartAddr = val & 0xFFFFFFFE;
|
||||
break;
|
||||
case 0x04600008:
|
||||
{
|
||||
rdLen = val & 0x00FFFFFF;
|
||||
DMA<false>();
|
||||
} break;
|
||||
case 0x0460000C: {
|
||||
}
|
||||
break;
|
||||
case 0x0460000C:
|
||||
{
|
||||
wrLen = val & 0x00FFFFFF;
|
||||
DMA<true>();
|
||||
} break;
|
||||
case 0x04600010:
|
||||
if(val & 2) {
|
||||
mi.InterruptLower(MI::Interrupt::PI);
|
||||
} break;
|
||||
case 0x04600014: piBsdDom1Lat = val & 0xff; break;
|
||||
case 0x04600018: piBsdDom1Pwd = val & 0xff; break;
|
||||
case 0x0460001C: piBsdDom1Pgs = val & 0xff; break;
|
||||
case 0x04600020: piBsdDom1Rls = val & 0xff; break;
|
||||
case 0x04600024: piBsdDom2Lat = val & 0xff; break;
|
||||
case 0x04600028: piBsdDom2Pwd = val & 0xff; break;
|
||||
case 0x0460002C: piBsdDom2Pgs = val & 0xff; break;
|
||||
case 0x04600030: piBsdDom2Rls = val & 0xff; break;
|
||||
default:
|
||||
Util::panic("Unhandled PI[{:08X}] write ({:08X})", val, addr);
|
||||
}
|
||||
break;
|
||||
case 0x04600010:
|
||||
if (val & 2) {
|
||||
mi.InterruptLower(MI::Interrupt::PI);
|
||||
}
|
||||
break;
|
||||
case 0x04600014:
|
||||
piBsdDom1Lat = val & 0xff;
|
||||
break;
|
||||
case 0x04600018:
|
||||
piBsdDom1Pwd = val & 0xff;
|
||||
break;
|
||||
case 0x0460001C:
|
||||
piBsdDom1Pgs = val & 0xff;
|
||||
break;
|
||||
case 0x04600020:
|
||||
piBsdDom1Rls = val & 0xff;
|
||||
break;
|
||||
case 0x04600024:
|
||||
piBsdDom2Lat = val & 0xff;
|
||||
break;
|
||||
case 0x04600028:
|
||||
piBsdDom2Pwd = val & 0xff;
|
||||
break;
|
||||
case 0x0460002C:
|
||||
piBsdDom2Pgs = val & 0xff;
|
||||
break;
|
||||
case 0x04600030:
|
||||
piBsdDom2Rls = val & 0xff;
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unhandled PI[{:08X}] write ({:08X})", val, addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -7,17 +7,17 @@ struct Mem;
|
||||
struct Registers;
|
||||
|
||||
struct PI {
|
||||
PI(Mem&, Registers&);
|
||||
PI(Mem &, Registers &);
|
||||
void Reset();
|
||||
auto Read(u32) const -> u32;
|
||||
void Write(u32, u32);
|
||||
|
||||
template<typename T, bool isDma>
|
||||
template <typename T, bool isDma>
|
||||
void BusWrite(u32, u32);
|
||||
template<bool isDma>
|
||||
template <bool isDma>
|
||||
void BusWrite(u32, u64);
|
||||
|
||||
template<typename T, bool isDma>
|
||||
template <typename T, bool isDma>
|
||||
auto BusRead(u32) -> T;
|
||||
|
||||
bool ReadLatch();
|
||||
@@ -33,11 +33,12 @@ struct PI {
|
||||
u32 piBsdDom1Pwd{}, piBsdDom2Pwd{};
|
||||
u32 piBsdDom1Pgs{}, piBsdDom2Pgs{};
|
||||
u32 piBsdDom1Rls{}, piBsdDom2Rls{};
|
||||
|
||||
private:
|
||||
template <bool toDram>
|
||||
void DMA();
|
||||
|
||||
Mem& mem;
|
||||
Registers& regs;
|
||||
Mem &mem;
|
||||
Registers ®s;
|
||||
};
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#include <core/mmio/PIF.hpp>
|
||||
#include <Netplay.hpp>
|
||||
#include <cassert>
|
||||
#include <cic_nus_6105/n64_cic_nus_6105.hpp>
|
||||
#include <core/Mem.hpp>
|
||||
#include <core/mmio/PIF.hpp>
|
||||
#include <core/registers/Registers.hpp>
|
||||
#include <log.hpp>
|
||||
#include <cic_nus_6105/n64_cic_nus_6105.hpp>
|
||||
#include <cassert>
|
||||
#include <Netplay.hpp>
|
||||
|
||||
#define MEMPAK_SIZE 32768
|
||||
|
||||
@@ -15,14 +15,18 @@ void PIF::Reset() {
|
||||
bootrom = {};
|
||||
ram = {};
|
||||
std::error_code error;
|
||||
if(mempak.is_mapped()) {
|
||||
if (mempak.is_mapped()) {
|
||||
mempak.sync(error);
|
||||
if (error) { Util::panic("Could not sync {}", mempakPath); }
|
||||
if (error) {
|
||||
Util::panic("Could not sync {}", mempakPath);
|
||||
}
|
||||
mempak.unmap();
|
||||
}
|
||||
if(eeprom.is_mapped()) {
|
||||
if (eeprom.is_mapped()) {
|
||||
eeprom.sync(error);
|
||||
if (error) { Util::panic("Could not sync {}", eepromPath); }
|
||||
if (error) {
|
||||
Util::panic("Could not sync {}", eepromPath);
|
||||
}
|
||||
eeprom.unmap();
|
||||
}
|
||||
|
||||
@@ -31,7 +35,7 @@ void PIF::Reset() {
|
||||
}
|
||||
|
||||
void PIF::MaybeLoadMempak() {
|
||||
if(!mempakOpen) {
|
||||
if (!mempakOpen) {
|
||||
fs::path mempakPath_ = mempakPath;
|
||||
if (!savePath.empty()) {
|
||||
mempakPath_ = savePath / mempakPath_.filename();
|
||||
@@ -40,12 +44,14 @@ void PIF::MaybeLoadMempak() {
|
||||
std::error_code error;
|
||||
if (mempak.is_mapped()) {
|
||||
mempak.sync(error);
|
||||
if (error) { Util::panic("Could not sync {}", mempakPath); }
|
||||
if (error) {
|
||||
Util::panic("Could not sync {}", mempakPath);
|
||||
}
|
||||
mempak.unmap();
|
||||
}
|
||||
|
||||
auto mempakVec = Util::ReadFileBinary(mempakPath);
|
||||
if(mempak.empty()) {
|
||||
if (mempak.empty()) {
|
||||
Util::WriteFileBinary(std::array<u8, MEMPAK_SIZE>{}, mempakPath);
|
||||
mempakVec = Util::ReadFileBinary(mempakPath);
|
||||
}
|
||||
@@ -54,32 +60,33 @@ void PIF::MaybeLoadMempak() {
|
||||
Util::panic("Corrupt mempak!");
|
||||
}
|
||||
|
||||
mempak = mio::make_mmap_sink(
|
||||
mempakPath, 0, mio::map_entire_file, error);
|
||||
if (error) { Util::panic("Could not open {}", mempakPath); }
|
||||
mempak = mio::make_mmap_sink(mempakPath, 0, mio::map_entire_file, error);
|
||||
if (error) {
|
||||
Util::panic("Could not open {}", mempakPath);
|
||||
}
|
||||
mempakOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE size_t GetSaveSize(SaveType saveType) {
|
||||
switch (saveType) {
|
||||
case SAVE_NONE:
|
||||
return 0;
|
||||
case SAVE_EEPROM_4k:
|
||||
return 512;
|
||||
case SAVE_EEPROM_16k:
|
||||
return 2048;
|
||||
case SAVE_SRAM_256k:
|
||||
return 32768;
|
||||
case SAVE_FLASH_1m:
|
||||
return 131072;
|
||||
default:
|
||||
Util::panic("Unknown save type!");
|
||||
case SAVE_NONE:
|
||||
return 0;
|
||||
case SAVE_EEPROM_4k:
|
||||
return 512;
|
||||
case SAVE_EEPROM_16k:
|
||||
return 2048;
|
||||
case SAVE_SRAM_256k:
|
||||
return 32768;
|
||||
case SAVE_FLASH_1m:
|
||||
return 131072;
|
||||
default:
|
||||
Util::panic("Unknown save type!");
|
||||
}
|
||||
}
|
||||
|
||||
void PIF::LoadEeprom(SaveType saveType, const std::string& path) {
|
||||
if(saveType == SAVE_EEPROM_16k || saveType == SAVE_EEPROM_4k) {
|
||||
void PIF::LoadEeprom(SaveType saveType, const std::string &path) {
|
||||
if (saveType == SAVE_EEPROM_16k || saveType == SAVE_EEPROM_4k) {
|
||||
fs::path eepromPath_ = path;
|
||||
if (!savePath.empty()) {
|
||||
eepromPath_ = savePath / eepromPath_.filename();
|
||||
@@ -88,14 +95,16 @@ void PIF::LoadEeprom(SaveType saveType, const std::string& path) {
|
||||
std::error_code error;
|
||||
if (eeprom.is_mapped()) {
|
||||
eeprom.sync(error);
|
||||
if (error) { Util::panic("Could not sync {}", eepromPath); }
|
||||
if (error) {
|
||||
Util::panic("Could not sync {}", eepromPath);
|
||||
}
|
||||
eeprom.unmap();
|
||||
}
|
||||
|
||||
eepromSize = GetSaveSize(saveType);
|
||||
|
||||
auto eepromVec = Util::ReadFileBinary(eepromPath);
|
||||
if(eepromVec.empty()) {
|
||||
if (eepromVec.empty()) {
|
||||
std::vector<u8> dummy{};
|
||||
dummy.resize(GetSaveSize(saveType));
|
||||
Util::WriteFileBinary(dummy, eepromPath);
|
||||
@@ -106,18 +115,14 @@ void PIF::LoadEeprom(SaveType saveType, const std::string& path) {
|
||||
Util::panic("Corrupt eeprom!");
|
||||
}
|
||||
|
||||
eeprom = mio::make_mmap_sink(
|
||||
eepromPath, 0, mio::map_entire_file, error);
|
||||
if (error) { Util::panic("Could not open {}", eepromPath); }
|
||||
eeprom = mio::make_mmap_sink(eepromPath, 0, mio::map_entire_file, error);
|
||||
if (error) {
|
||||
Util::panic("Could not open {}", eepromPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum CMDIndexes {
|
||||
CMD_LEN = 0,
|
||||
CMD_RES_LEN,
|
||||
CMD_IDX,
|
||||
CMD_START
|
||||
};
|
||||
enum CMDIndexes { CMD_LEN = 0, CMD_RES_LEN, CMD_IDX, CMD_START };
|
||||
|
||||
void PIF::CICChallenge() {
|
||||
u8 challenge[30];
|
||||
@@ -129,14 +134,14 @@ void PIF::CICChallenge() {
|
||||
challenge[i * 2 + 1] = (ram[0x30 + i] >> 0) & 0x0F;
|
||||
}
|
||||
|
||||
n64_cic_nus_6105((char*)challenge, (char*)response, CHL_LEN - 2);
|
||||
n64_cic_nus_6105((char *)challenge, (char *)response, CHL_LEN - 2);
|
||||
|
||||
for (int i = 0; i < 15; i++) {
|
||||
ram[0x30 + i] = (response[i * 2] << 4) + response[i * 2 + 1];
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE u8 DataCRC(const u8* data) {
|
||||
FORCE_INLINE u8 DataCRC(const u8 *data) {
|
||||
u8 crc = 0;
|
||||
for (int i = 0; i <= 32; i++) {
|
||||
for (int j = 7; j >= 0; j--) {
|
||||
@@ -165,7 +170,7 @@ void PIF::ProcessCommands(Mem &mem) {
|
||||
channel = 0;
|
||||
int i = 0;
|
||||
while (i < 63) {
|
||||
u8* cmd = &ram[i++];
|
||||
u8 *cmd = &ram[i++];
|
||||
u8 cmdlen = cmd[CMD_LEN] & 0x3F;
|
||||
|
||||
if (cmdlen == 0 || cmdlen == 0x3D) {
|
||||
@@ -180,58 +185,65 @@ void PIF::ProcessCommands(Mem &mem) {
|
||||
break;
|
||||
}
|
||||
u8 reslen = r & 0x3F;
|
||||
u8* res = &ram[i + cmdlen];
|
||||
u8 *res = &ram[i + cmdlen];
|
||||
|
||||
switch (cmd[CMD_IDX]) {
|
||||
case 0: case 0xff:
|
||||
ControllerID(res);
|
||||
channel++;
|
||||
break;
|
||||
case 0:
|
||||
case 0xff:
|
||||
ControllerID(res);
|
||||
channel++;
|
||||
break;
|
||||
case 1:
|
||||
if (!ReadButtons(res)) {
|
||||
cmd[1] |= 0x80;
|
||||
}
|
||||
channel++;
|
||||
break;
|
||||
case 2:
|
||||
MempakRead(cmd, res);
|
||||
break;
|
||||
case 3:
|
||||
MempakWrite(cmd, res);
|
||||
break;
|
||||
case 4:
|
||||
EepromRead(cmd, res, mem);
|
||||
break;
|
||||
case 5:
|
||||
EepromWrite(cmd, res, mem);
|
||||
break;
|
||||
case 6:
|
||||
res[0] = 0x00;
|
||||
res[1] = 0x10;
|
||||
res[2] = 0x80;
|
||||
break;
|
||||
case 7:
|
||||
switch (cmd[CMD_START]) {
|
||||
case 0:
|
||||
case 1:
|
||||
if(!ReadButtons(res)) {
|
||||
cmd[1] |= 0x80;
|
||||
}
|
||||
channel++;
|
||||
case 3:
|
||||
break;
|
||||
case 2:
|
||||
MempakRead(cmd, res);
|
||||
break;
|
||||
case 3:
|
||||
MempakWrite(cmd, res);
|
||||
break;
|
||||
case 4:
|
||||
EepromRead(cmd, res, mem);
|
||||
break;
|
||||
case 5:
|
||||
EepromWrite(cmd, res, mem);
|
||||
break;
|
||||
case 6:
|
||||
res[0] = 0x00;
|
||||
res[1] = 0x10;
|
||||
res[2] = 0x80;
|
||||
break;
|
||||
case 7:
|
||||
switch(cmd[CMD_START]) {
|
||||
case 0: case 1: case 3:
|
||||
break;
|
||||
case 2: {
|
||||
auto now = std::time(nullptr);
|
||||
auto* gmtm = gmtime(&now);
|
||||
res[0] = BCD_ENCODE(gmtm->tm_sec);
|
||||
res[1] = BCD_ENCODE(gmtm->tm_min);
|
||||
res[2] = BCD_ENCODE(gmtm->tm_hour) + 0x80;
|
||||
res[3] = BCD_ENCODE(gmtm->tm_mday);
|
||||
res[4] = BCD_ENCODE(gmtm->tm_wday);
|
||||
res[5] = BCD_ENCODE(gmtm->tm_mon);
|
||||
res[6] = BCD_ENCODE(gmtm->tm_year);
|
||||
res[7] = (gmtm->tm_year - 1900) >= 100 ? 1 : 0;
|
||||
} break;
|
||||
default: Util::panic("Invalid read RTC block {}", cmd[CMD_START]);
|
||||
{
|
||||
auto now = std::time(nullptr);
|
||||
auto *gmtm = gmtime(&now);
|
||||
res[0] = BCD_ENCODE(gmtm->tm_sec);
|
||||
res[1] = BCD_ENCODE(gmtm->tm_min);
|
||||
res[2] = BCD_ENCODE(gmtm->tm_hour) + 0x80;
|
||||
res[3] = BCD_ENCODE(gmtm->tm_mday);
|
||||
res[4] = BCD_ENCODE(gmtm->tm_wday);
|
||||
res[5] = BCD_ENCODE(gmtm->tm_mon);
|
||||
res[6] = BCD_ENCODE(gmtm->tm_year);
|
||||
res[7] = (gmtm->tm_year - 1900) >= 100 ? 1 : 0;
|
||||
}
|
||||
break;
|
||||
case 8: break;
|
||||
default:
|
||||
Util::panic("Invalid PIF command: {:X}", cmd[2]);
|
||||
Util::panic("Invalid read RTC block {}", cmd[CMD_START]);
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
break;
|
||||
default:
|
||||
Util::panic("Invalid PIF command: {:X}", cmd[2]);
|
||||
}
|
||||
|
||||
i += cmdlen + reslen;
|
||||
@@ -253,59 +265,60 @@ void PIF::ProcessCommands(Mem &mem) {
|
||||
}
|
||||
}
|
||||
|
||||
void PIF::MempakRead(const u8* cmd, u8* res) {
|
||||
void PIF::MempakRead(const u8 *cmd, u8 *res) {
|
||||
MaybeLoadMempak();
|
||||
u16 offset = cmd[3] << 8;
|
||||
offset |= cmd[4];
|
||||
|
||||
// low 5 bits are the CRC
|
||||
//byte crc = offset & 0x1F;
|
||||
// byte crc = offset & 0x1F;
|
||||
// offset must be 32-byte aligned
|
||||
offset &= ~0x1F;
|
||||
|
||||
switch (GetAccessoryType()) {
|
||||
case ACCESSORY_NONE:
|
||||
break;
|
||||
case ACCESSORY_MEMPACK:
|
||||
if (offset <= MEMPAK_SIZE - 0x20) {
|
||||
std::copy_n(mempak.begin() + offset, 32, res);
|
||||
}
|
||||
break;
|
||||
case ACCESSORY_RUMBLE_PACK:
|
||||
memset(res, 0x80, 32);
|
||||
break;
|
||||
case ACCESSORY_NONE:
|
||||
break;
|
||||
case ACCESSORY_MEMPACK:
|
||||
if (offset <= MEMPAK_SIZE - 0x20) {
|
||||
std::copy_n(mempak.begin() + offset, 32, res);
|
||||
}
|
||||
break;
|
||||
case ACCESSORY_RUMBLE_PACK:
|
||||
memset(res, 0x80, 32);
|
||||
break;
|
||||
}
|
||||
|
||||
// CRC byte
|
||||
res[32] = DataCRC(res);
|
||||
}
|
||||
|
||||
void PIF::MempakWrite(u8* cmd, u8* res) {
|
||||
void PIF::MempakWrite(u8 *cmd, u8 *res) {
|
||||
MaybeLoadMempak();
|
||||
// First two bytes in the command are the offset
|
||||
u16 offset = cmd[3] << 8;
|
||||
offset |= cmd[4];
|
||||
|
||||
// low 5 bits are the CRC
|
||||
//byte crc = offset & 0x1F;
|
||||
// byte crc = offset & 0x1F;
|
||||
// offset must be 32-byte aligned
|
||||
offset &= ~0x1F;
|
||||
|
||||
switch (GetAccessoryType()) {
|
||||
case ACCESSORY_NONE:
|
||||
break;
|
||||
case ACCESSORY_MEMPACK:
|
||||
if (offset <= MEMPAK_SIZE - 0x20) {
|
||||
std::copy_n(cmd + 5, 32, mempak.begin() + offset);
|
||||
}
|
||||
break;
|
||||
case ACCESSORY_RUMBLE_PACK: break;
|
||||
case ACCESSORY_NONE:
|
||||
break;
|
||||
case ACCESSORY_MEMPACK:
|
||||
if (offset <= MEMPAK_SIZE - 0x20) {
|
||||
std::copy_n(cmd + 5, 32, mempak.begin() + offset);
|
||||
}
|
||||
break;
|
||||
case ACCESSORY_RUMBLE_PACK:
|
||||
break;
|
||||
}
|
||||
// CRC byte
|
||||
res[0] = DataCRC(&cmd[5]);
|
||||
}
|
||||
|
||||
void PIF::EepromRead(const u8* cmd, u8* res, const Mem& mem) const {
|
||||
void PIF::EepromRead(const u8 *cmd, u8 *res, const Mem &mem) const {
|
||||
assert(mem.saveType == SAVE_EEPROM_4k || mem.saveType == SAVE_EEPROM_16k);
|
||||
if (channel == 4) {
|
||||
u8 offset = cmd[3];
|
||||
@@ -319,7 +332,7 @@ void PIF::EepromRead(const u8* cmd, u8* res, const Mem& mem) const {
|
||||
}
|
||||
}
|
||||
|
||||
void PIF::EepromWrite(const u8* cmd, u8* res, const Mem& mem) {
|
||||
void PIF::EepromWrite(const u8 *cmd, u8 *res, const Mem &mem) {
|
||||
assert(mem.saveType == SAVE_EEPROM_4k || mem.saveType == SAVE_EEPROM_16k);
|
||||
if (channel == 4) {
|
||||
u8 offset = cmd[3];
|
||||
@@ -338,259 +351,259 @@ void PIF::EepromWrite(const u8* cmd, u8* res, const Mem& mem) {
|
||||
void PIF::HLE(bool pal, CICType cicType) {
|
||||
mem.Write<u32>(regs, PIF_RAM_REGION_START + 0x24, cicSeeds[cicType]);
|
||||
|
||||
switch(cicType) {
|
||||
case UNKNOWN_CIC_TYPE:
|
||||
Util::warn("Unknown CIC type!");
|
||||
break;
|
||||
case CIC_NUS_6101:
|
||||
regs.Write(0, 0x0000000000000000);
|
||||
regs.Write(1, 0x0000000000000000);
|
||||
regs.Write(2, 0xFFFFFFFFDF6445CC);
|
||||
regs.Write(3, 0xFFFFFFFFDF6445CC);
|
||||
regs.Write(4, 0x00000000000045CC);
|
||||
regs.Write(5, 0x0000000073EE317A);
|
||||
regs.Write(6, 0xFFFFFFFFA4001F0C);
|
||||
regs.Write(7, 0xFFFFFFFFA4001F08);
|
||||
regs.Write(8, 0x00000000000000C0);
|
||||
regs.Write(9, 0x0000000000000000);
|
||||
regs.Write(10, 0x0000000000000040);
|
||||
regs.Write(11, 0xFFFFFFFFA4000040);
|
||||
regs.Write(12, 0xFFFFFFFFC7601FAC);
|
||||
regs.Write(13, 0xFFFFFFFFC7601FAC);
|
||||
regs.Write(14, 0xFFFFFFFFB48E2ED6);
|
||||
regs.Write(15, 0xFFFFFFFFBA1A7D4B);
|
||||
regs.Write(16, 0x0000000000000000);
|
||||
regs.Write(17, 0x0000000000000000);
|
||||
regs.Write(18, 0x0000000000000000);
|
||||
regs.Write(19, 0x0000000000000000);
|
||||
regs.Write(20, 0x0000000000000001);
|
||||
regs.Write(21, 0x0000000000000000);
|
||||
regs.Write(23, 0x0000000000000001);
|
||||
regs.Write(24, 0x0000000000000002);
|
||||
regs.Write(25, 0xFFFFFFFF905F4718);
|
||||
regs.Write(26, 0x0000000000000000);
|
||||
regs.Write(27, 0x0000000000000000);
|
||||
regs.Write(28, 0x0000000000000000);
|
||||
regs.Write(29, 0xFFFFFFFFA4001FF0);
|
||||
regs.Write(30, 0x0000000000000000);
|
||||
regs.Write(31, 0xFFFFFFFFA4001550);
|
||||
switch (cicType) {
|
||||
case UNKNOWN_CIC_TYPE:
|
||||
Util::warn("Unknown CIC type!");
|
||||
break;
|
||||
case CIC_NUS_6101:
|
||||
regs.Write(0, 0x0000000000000000);
|
||||
regs.Write(1, 0x0000000000000000);
|
||||
regs.Write(2, 0xFFFFFFFFDF6445CC);
|
||||
regs.Write(3, 0xFFFFFFFFDF6445CC);
|
||||
regs.Write(4, 0x00000000000045CC);
|
||||
regs.Write(5, 0x0000000073EE317A);
|
||||
regs.Write(6, 0xFFFFFFFFA4001F0C);
|
||||
regs.Write(7, 0xFFFFFFFFA4001F08);
|
||||
regs.Write(8, 0x00000000000000C0);
|
||||
regs.Write(9, 0x0000000000000000);
|
||||
regs.Write(10, 0x0000000000000040);
|
||||
regs.Write(11, 0xFFFFFFFFA4000040);
|
||||
regs.Write(12, 0xFFFFFFFFC7601FAC);
|
||||
regs.Write(13, 0xFFFFFFFFC7601FAC);
|
||||
regs.Write(14, 0xFFFFFFFFB48E2ED6);
|
||||
regs.Write(15, 0xFFFFFFFFBA1A7D4B);
|
||||
regs.Write(16, 0x0000000000000000);
|
||||
regs.Write(17, 0x0000000000000000);
|
||||
regs.Write(18, 0x0000000000000000);
|
||||
regs.Write(19, 0x0000000000000000);
|
||||
regs.Write(20, 0x0000000000000001);
|
||||
regs.Write(21, 0x0000000000000000);
|
||||
regs.Write(23, 0x0000000000000001);
|
||||
regs.Write(24, 0x0000000000000002);
|
||||
regs.Write(25, 0xFFFFFFFF905F4718);
|
||||
regs.Write(26, 0x0000000000000000);
|
||||
regs.Write(27, 0x0000000000000000);
|
||||
regs.Write(28, 0x0000000000000000);
|
||||
regs.Write(29, 0xFFFFFFFFA4001FF0);
|
||||
regs.Write(30, 0x0000000000000000);
|
||||
regs.Write(31, 0xFFFFFFFFA4001550);
|
||||
|
||||
regs.lo = 0xFFFFFFFFBA1A7D4Bll;
|
||||
regs.hi = 0xFFFFFFFF997EC317ll;
|
||||
break;
|
||||
case CIC_NUS_7102:
|
||||
regs.Write(0, 0x0000000000000000);
|
||||
regs.Write(1, 0x0000000000000001);
|
||||
regs.Write(2, 0x000000001E324416);
|
||||
regs.Write(3, 0x000000001E324416);
|
||||
regs.Write(4, 0x0000000000004416);
|
||||
regs.Write(5, 0x000000000EC5D9AF);
|
||||
regs.Write(6, 0xFFFFFFFFA4001F0C);
|
||||
regs.Write(7, 0xFFFFFFFFA4001F08);
|
||||
regs.Write(8, 0x00000000000000C0);
|
||||
regs.Write(9, 0x0000000000000000);
|
||||
regs.Write(10, 0x0000000000000040);
|
||||
regs.Write(11, 0xFFFFFFFFA4000040);
|
||||
regs.Write(12, 0x00000000495D3D7B);
|
||||
regs.Write(13, 0xFFFFFFFF8B3DFA1E);
|
||||
regs.Write(14, 0x000000004798E4D4);
|
||||
regs.Write(15, 0xFFFFFFFFF1D30682);
|
||||
regs.Write(16, 0x0000000000000000);
|
||||
regs.Write(17, 0x0000000000000000);
|
||||
regs.Write(18, 0x0000000000000000);
|
||||
regs.Write(19, 0x0000000000000000);
|
||||
regs.lo = 0xFFFFFFFFBA1A7D4Bll;
|
||||
regs.hi = 0xFFFFFFFF997EC317ll;
|
||||
break;
|
||||
case CIC_NUS_7102:
|
||||
regs.Write(0, 0x0000000000000000);
|
||||
regs.Write(1, 0x0000000000000001);
|
||||
regs.Write(2, 0x000000001E324416);
|
||||
regs.Write(3, 0x000000001E324416);
|
||||
regs.Write(4, 0x0000000000004416);
|
||||
regs.Write(5, 0x000000000EC5D9AF);
|
||||
regs.Write(6, 0xFFFFFFFFA4001F0C);
|
||||
regs.Write(7, 0xFFFFFFFFA4001F08);
|
||||
regs.Write(8, 0x00000000000000C0);
|
||||
regs.Write(9, 0x0000000000000000);
|
||||
regs.Write(10, 0x0000000000000040);
|
||||
regs.Write(11, 0xFFFFFFFFA4000040);
|
||||
regs.Write(12, 0x00000000495D3D7B);
|
||||
regs.Write(13, 0xFFFFFFFF8B3DFA1E);
|
||||
regs.Write(14, 0x000000004798E4D4);
|
||||
regs.Write(15, 0xFFFFFFFFF1D30682);
|
||||
regs.Write(16, 0x0000000000000000);
|
||||
regs.Write(17, 0x0000000000000000);
|
||||
regs.Write(18, 0x0000000000000000);
|
||||
regs.Write(19, 0x0000000000000000);
|
||||
regs.Write(20, 0x0000000000000000);
|
||||
regs.Write(21, 0x0000000000000000);
|
||||
regs.Write(22, 0x000000000000003F);
|
||||
regs.Write(23, 0x0000000000000007);
|
||||
regs.Write(24, 0x0000000000000000);
|
||||
regs.Write(25, 0x0000000013D05CAB);
|
||||
regs.Write(26, 0x0000000000000000);
|
||||
regs.Write(27, 0x0000000000000000);
|
||||
regs.Write(28, 0x0000000000000000);
|
||||
regs.Write(29, 0xFFFFFFFFA4001FF0);
|
||||
regs.Write(30, 0x0000000000000000);
|
||||
regs.Write(31, 0xFFFFFFFFA4001554);
|
||||
|
||||
regs.lo = 0xFFFFFFFFF1D30682ll;
|
||||
regs.hi = 0x0000000010054A98;
|
||||
break;
|
||||
case CIC_NUS_6102_7101:
|
||||
regs.Write(0, 0x0000000000000000);
|
||||
regs.Write(1, 0x0000000000000001);
|
||||
regs.Write(2, 0x000000000EBDA536);
|
||||
regs.Write(3, 0x000000000EBDA536);
|
||||
regs.Write(4, 0x000000000000A536);
|
||||
regs.Write(5, 0xFFFFFFFFC0F1D859);
|
||||
regs.Write(6, 0xFFFFFFFFA4001F0C);
|
||||
regs.Write(7, 0xFFFFFFFFA4001F08);
|
||||
regs.Write(8, 0x00000000000000C0);
|
||||
regs.Write(9, 0x0000000000000000);
|
||||
regs.Write(10, 0x0000000000000040);
|
||||
regs.Write(11, 0xFFFFFFFFA4000040);
|
||||
regs.Write(12, 0xFFFFFFFFED10D0B3);
|
||||
regs.Write(13, 0x000000001402A4CC);
|
||||
regs.Write(14, 0x000000002DE108EA);
|
||||
regs.Write(15, 0x000000003103E121);
|
||||
regs.Write(16, 0x0000000000000000);
|
||||
regs.Write(17, 0x0000000000000000);
|
||||
regs.Write(18, 0x0000000000000000);
|
||||
regs.Write(19, 0x0000000000000000);
|
||||
regs.Write(20, 0x0000000000000001);
|
||||
regs.Write(21, 0x0000000000000000);
|
||||
regs.Write(23, 0x0000000000000000);
|
||||
regs.Write(24, 0x0000000000000000);
|
||||
regs.Write(25, 0xFFFFFFFF9DEBB54F);
|
||||
regs.Write(26, 0x0000000000000000);
|
||||
regs.Write(27, 0x0000000000000000);
|
||||
regs.Write(28, 0x0000000000000000);
|
||||
regs.Write(29, 0xFFFFFFFFA4001FF0);
|
||||
regs.Write(30, 0x0000000000000000);
|
||||
regs.Write(31, 0xFFFFFFFFA4001550);
|
||||
|
||||
regs.hi = 0x000000003FC18657;
|
||||
regs.lo = 0x000000003103E121;
|
||||
|
||||
if (pal) {
|
||||
regs.Write(20, 0x0000000000000000);
|
||||
regs.Write(21, 0x0000000000000000);
|
||||
regs.Write(22, 0x000000000000003F);
|
||||
regs.Write(23, 0x0000000000000007);
|
||||
regs.Write(24, 0x0000000000000000);
|
||||
regs.Write(25, 0x0000000013D05CAB);
|
||||
regs.Write(26, 0x0000000000000000);
|
||||
regs.Write(27, 0x0000000000000000);
|
||||
regs.Write(28, 0x0000000000000000);
|
||||
regs.Write(29, 0xFFFFFFFFA4001FF0);
|
||||
regs.Write(30, 0x0000000000000000);
|
||||
regs.Write(23, 0x0000000000000006);
|
||||
regs.Write(31, 0xFFFFFFFFA4001554);
|
||||
}
|
||||
break;
|
||||
case CIC_NUS_6103_7103:
|
||||
regs.Write(0, 0x0000000000000000);
|
||||
regs.Write(1, 0x0000000000000001);
|
||||
regs.Write(2, 0x0000000049A5EE96);
|
||||
regs.Write(3, 0x0000000049A5EE96);
|
||||
regs.Write(4, 0x000000000000EE96);
|
||||
regs.Write(5, 0xFFFFFFFFD4646273);
|
||||
regs.Write(6, 0xFFFFFFFFA4001F0C);
|
||||
regs.Write(7, 0xFFFFFFFFA4001F08);
|
||||
regs.Write(8, 0x00000000000000C0);
|
||||
regs.Write(9, 0x0000000000000000);
|
||||
regs.Write(10, 0x0000000000000040);
|
||||
regs.Write(11, 0xFFFFFFFFA4000040);
|
||||
regs.Write(12, 0xFFFFFFFFCE9DFBF7);
|
||||
regs.Write(13, 0xFFFFFFFFCE9DFBF7);
|
||||
regs.Write(14, 0x000000001AF99984);
|
||||
regs.Write(15, 0x0000000018B63D28);
|
||||
regs.Write(16, 0x0000000000000000);
|
||||
regs.Write(17, 0x0000000000000000);
|
||||
regs.Write(18, 0x0000000000000000);
|
||||
regs.Write(19, 0x0000000000000000);
|
||||
regs.Write(20, 0x0000000000000001);
|
||||
regs.Write(21, 0x0000000000000000);
|
||||
regs.Write(23, 0x0000000000000000);
|
||||
regs.Write(24, 0x0000000000000000);
|
||||
regs.Write(25, 0xFFFFFFFF825B21C9);
|
||||
regs.Write(26, 0x0000000000000000);
|
||||
regs.Write(27, 0x0000000000000000);
|
||||
regs.Write(28, 0x0000000000000000);
|
||||
regs.Write(29, 0xFFFFFFFFA4001FF0);
|
||||
regs.Write(30, 0x0000000000000000);
|
||||
regs.Write(31, 0xFFFFFFFFA4001550);
|
||||
|
||||
regs.lo = 0xFFFFFFFFF1D30682ll;
|
||||
regs.hi = 0x0000000010054A98;
|
||||
break;
|
||||
case CIC_NUS_6102_7101:
|
||||
regs.Write(0, 0x0000000000000000);
|
||||
regs.Write(1, 0x0000000000000001);
|
||||
regs.Write(2, 0x000000000EBDA536);
|
||||
regs.Write(3, 0x000000000EBDA536);
|
||||
regs.Write(4, 0x000000000000A536);
|
||||
regs.Write(5, 0xFFFFFFFFC0F1D859);
|
||||
regs.Write(6, 0xFFFFFFFFA4001F0C);
|
||||
regs.Write(7, 0xFFFFFFFFA4001F08);
|
||||
regs.Write(8, 0x00000000000000C0);
|
||||
regs.Write(9, 0x0000000000000000);
|
||||
regs.Write(10, 0x0000000000000040);
|
||||
regs.Write(11, 0xFFFFFFFFA4000040);
|
||||
regs.Write(12, 0xFFFFFFFFED10D0B3);
|
||||
regs.Write(13, 0x000000001402A4CC);
|
||||
regs.Write(14, 0x000000002DE108EA);
|
||||
regs.Write(15, 0x000000003103E121);
|
||||
regs.Write(16, 0x0000000000000000);
|
||||
regs.Write(17, 0x0000000000000000);
|
||||
regs.Write(18, 0x0000000000000000);
|
||||
regs.Write(19, 0x0000000000000000);
|
||||
regs.Write(20, 0x0000000000000001);
|
||||
regs.Write(21, 0x0000000000000000);
|
||||
regs.Write(23, 0x0000000000000000);
|
||||
regs.Write(24, 0x0000000000000000);
|
||||
regs.Write(25, 0xFFFFFFFF9DEBB54F);
|
||||
regs.Write(26, 0x0000000000000000);
|
||||
regs.Write(27, 0x0000000000000000);
|
||||
regs.Write(28, 0x0000000000000000);
|
||||
regs.Write(29, 0xFFFFFFFFA4001FF0);
|
||||
regs.Write(30, 0x0000000000000000);
|
||||
regs.Write(31, 0xFFFFFFFFA4001550);
|
||||
regs.lo = 0x0000000018B63D28;
|
||||
regs.hi = 0x00000000625C2BBE;
|
||||
|
||||
regs.hi = 0x000000003FC18657;
|
||||
regs.lo = 0x000000003103E121;
|
||||
if (pal) {
|
||||
regs.Write(20, 0x0000000000000000);
|
||||
regs.Write(23, 0x0000000000000006);
|
||||
regs.Write(31, 0xFFFFFFFFA4001554);
|
||||
}
|
||||
break;
|
||||
case CIC_NUS_6105_7105:
|
||||
regs.Write(0, 0x0000000000000000);
|
||||
regs.Write(1, 0x0000000000000000);
|
||||
regs.Write(2, 0xFFFFFFFFF58B0FBF);
|
||||
regs.Write(3, 0xFFFFFFFFF58B0FBF);
|
||||
regs.Write(4, 0x0000000000000FBF);
|
||||
regs.Write(5, 0xFFFFFFFFDECAAAD1);
|
||||
regs.Write(6, 0xFFFFFFFFA4001F0C);
|
||||
regs.Write(7, 0xFFFFFFFFA4001F08);
|
||||
regs.Write(8, 0x00000000000000C0);
|
||||
regs.Write(9, 0x0000000000000000);
|
||||
regs.Write(10, 0x0000000000000040);
|
||||
regs.Write(11, 0xFFFFFFFFA4000040);
|
||||
regs.Write(12, 0xFFFFFFFF9651F81E);
|
||||
regs.Write(13, 0x000000002D42AAC5);
|
||||
regs.Write(14, 0x00000000489B52CF);
|
||||
regs.Write(15, 0x0000000056584D60);
|
||||
regs.Write(16, 0x0000000000000000);
|
||||
regs.Write(17, 0x0000000000000000);
|
||||
regs.Write(18, 0x0000000000000000);
|
||||
regs.Write(19, 0x0000000000000000);
|
||||
regs.Write(20, 0x0000000000000001);
|
||||
regs.Write(21, 0x0000000000000000);
|
||||
regs.Write(23, 0x0000000000000000);
|
||||
regs.Write(24, 0x0000000000000002);
|
||||
regs.Write(25, 0xFFFFFFFFCDCE565F);
|
||||
regs.Write(26, 0x0000000000000000);
|
||||
regs.Write(27, 0x0000000000000000);
|
||||
regs.Write(28, 0x0000000000000000);
|
||||
regs.Write(29, 0xFFFFFFFFA4001FF0);
|
||||
regs.Write(30, 0x0000000000000000);
|
||||
regs.Write(31, 0xFFFFFFFFA4001550);
|
||||
|
||||
if (pal) {
|
||||
regs.Write(20, 0x0000000000000000);
|
||||
regs.Write(23, 0x0000000000000006);
|
||||
regs.Write(31, 0xFFFFFFFFA4001554);
|
||||
}
|
||||
break;
|
||||
case CIC_NUS_6103_7103:
|
||||
regs.Write(0, 0x0000000000000000);
|
||||
regs.Write(1, 0x0000000000000001);
|
||||
regs.Write(2, 0x0000000049A5EE96);
|
||||
regs.Write(3, 0x0000000049A5EE96);
|
||||
regs.Write(4, 0x000000000000EE96);
|
||||
regs.Write(5, 0xFFFFFFFFD4646273);
|
||||
regs.Write(6, 0xFFFFFFFFA4001F0C);
|
||||
regs.Write(7, 0xFFFFFFFFA4001F08);
|
||||
regs.Write(8, 0x00000000000000C0);
|
||||
regs.Write(9, 0x0000000000000000);
|
||||
regs.Write(10, 0x0000000000000040);
|
||||
regs.Write(11, 0xFFFFFFFFA4000040);
|
||||
regs.Write(12, 0xFFFFFFFFCE9DFBF7);
|
||||
regs.Write(13, 0xFFFFFFFFCE9DFBF7);
|
||||
regs.Write(14, 0x000000001AF99984);
|
||||
regs.Write(15, 0x0000000018B63D28);
|
||||
regs.Write(16, 0x0000000000000000);
|
||||
regs.Write(17, 0x0000000000000000);
|
||||
regs.Write(18, 0x0000000000000000);
|
||||
regs.Write(19, 0x0000000000000000);
|
||||
regs.Write(20, 0x0000000000000001);
|
||||
regs.Write(21, 0x0000000000000000);
|
||||
regs.Write(23, 0x0000000000000000);
|
||||
regs.Write(24, 0x0000000000000000);
|
||||
regs.Write(25, 0xFFFFFFFF825B21C9);
|
||||
regs.Write(26, 0x0000000000000000);
|
||||
regs.Write(27, 0x0000000000000000);
|
||||
regs.Write(28, 0x0000000000000000);
|
||||
regs.Write(29, 0xFFFFFFFFA4001FF0);
|
||||
regs.Write(30, 0x0000000000000000);
|
||||
regs.Write(31, 0xFFFFFFFFA4001550);
|
||||
regs.lo = 0x0000000056584D60;
|
||||
regs.hi = 0x000000004BE35D1F;
|
||||
|
||||
regs.lo = 0x0000000018B63D28;
|
||||
regs.hi = 0x00000000625C2BBE;
|
||||
if (pal) {
|
||||
regs.Write(20, 0x0000000000000000);
|
||||
regs.Write(23, 0x0000000000000006);
|
||||
regs.Write(31, 0xFFFFFFFFA4001554);
|
||||
}
|
||||
|
||||
if (pal) {
|
||||
regs.Write(20, 0x0000000000000000);
|
||||
regs.Write(23, 0x0000000000000006);
|
||||
regs.Write(31, 0xFFFFFFFFA4001554);
|
||||
}
|
||||
break;
|
||||
case CIC_NUS_6105_7105:
|
||||
regs.Write(0, 0x0000000000000000);
|
||||
regs.Write(1, 0x0000000000000000);
|
||||
regs.Write(2, 0xFFFFFFFFF58B0FBF);
|
||||
regs.Write(3, 0xFFFFFFFFF58B0FBF);
|
||||
regs.Write(4, 0x0000000000000FBF);
|
||||
regs.Write(5, 0xFFFFFFFFDECAAAD1);
|
||||
regs.Write(6, 0xFFFFFFFFA4001F0C);
|
||||
regs.Write(7, 0xFFFFFFFFA4001F08);
|
||||
regs.Write(8, 0x00000000000000C0);
|
||||
regs.Write(9, 0x0000000000000000);
|
||||
regs.Write(10, 0x0000000000000040);
|
||||
regs.Write(11, 0xFFFFFFFFA4000040);
|
||||
regs.Write(12, 0xFFFFFFFF9651F81E);
|
||||
regs.Write(13, 0x000000002D42AAC5);
|
||||
regs.Write(14, 0x00000000489B52CF);
|
||||
regs.Write(15, 0x0000000056584D60);
|
||||
regs.Write(16, 0x0000000000000000);
|
||||
regs.Write(17, 0x0000000000000000);
|
||||
regs.Write(18, 0x0000000000000000);
|
||||
regs.Write(19, 0x0000000000000000);
|
||||
regs.Write(20, 0x0000000000000001);
|
||||
regs.Write(21, 0x0000000000000000);
|
||||
regs.Write(23, 0x0000000000000000);
|
||||
regs.Write(24, 0x0000000000000002);
|
||||
regs.Write(25, 0xFFFFFFFFCDCE565F);
|
||||
regs.Write(26, 0x0000000000000000);
|
||||
regs.Write(27, 0x0000000000000000);
|
||||
regs.Write(28, 0x0000000000000000);
|
||||
regs.Write(29, 0xFFFFFFFFA4001FF0);
|
||||
regs.Write(30, 0x0000000000000000);
|
||||
regs.Write(31, 0xFFFFFFFFA4001550);
|
||||
mem.Write<u32>(regs, IMEM_REGION_START + 0x00, 0x3C0DBFC0);
|
||||
mem.Write<u32>(regs, IMEM_REGION_START + 0x04, 0x8DA807FC);
|
||||
mem.Write<u32>(regs, IMEM_REGION_START + 0x08, 0x25AD07C0);
|
||||
mem.Write<u32>(regs, IMEM_REGION_START + 0x0C, 0x31080080);
|
||||
mem.Write<u32>(regs, IMEM_REGION_START + 0x10, 0x5500FFFC);
|
||||
mem.Write<u32>(regs, IMEM_REGION_START + 0x14, 0x3C0DBFC0);
|
||||
mem.Write<u32>(regs, IMEM_REGION_START + 0x18, 0x8DA80024);
|
||||
mem.Write<u32>(regs, IMEM_REGION_START + 0x1C, 0x3C0BB000);
|
||||
break;
|
||||
case CIC_NUS_6106_7106:
|
||||
regs.Write(0, 0x0000000000000000);
|
||||
regs.Write(1, 0x0000000000000000);
|
||||
regs.Write(2, 0xFFFFFFFFA95930A4);
|
||||
regs.Write(3, 0xFFFFFFFFA95930A4);
|
||||
regs.Write(4, 0x00000000000030A4);
|
||||
regs.Write(5, 0xFFFFFFFFB04DC903);
|
||||
regs.Write(6, 0xFFFFFFFFA4001F0C);
|
||||
regs.Write(7, 0xFFFFFFFFA4001F08);
|
||||
regs.Write(8, 0x00000000000000C0);
|
||||
regs.Write(9, 0x0000000000000000);
|
||||
regs.Write(10, 0x0000000000000040);
|
||||
regs.Write(11, 0xFFFFFFFFA4000040);
|
||||
regs.Write(12, 0xFFFFFFFFBCB59510);
|
||||
regs.Write(13, 0xFFFFFFFFBCB59510);
|
||||
regs.Write(14, 0x000000000CF85C13);
|
||||
regs.Write(15, 0x000000007A3C07F4);
|
||||
regs.Write(16, 0x0000000000000000);
|
||||
regs.Write(17, 0x0000000000000000);
|
||||
regs.Write(18, 0x0000000000000000);
|
||||
regs.Write(19, 0x0000000000000000);
|
||||
regs.Write(20, 0x0000000000000001);
|
||||
regs.Write(21, 0x0000000000000000);
|
||||
regs.Write(23, 0x0000000000000000);
|
||||
regs.Write(24, 0x0000000000000002);
|
||||
regs.Write(25, 0x00000000465E3F72);
|
||||
regs.Write(26, 0x0000000000000000);
|
||||
regs.Write(27, 0x0000000000000000);
|
||||
regs.Write(28, 0x0000000000000000);
|
||||
regs.Write(29, 0xFFFFFFFFA4001FF0);
|
||||
regs.Write(30, 0x0000000000000000);
|
||||
regs.Write(31, 0xFFFFFFFFA4001550);
|
||||
regs.lo = 0x000000007A3C07F4;
|
||||
regs.hi = 0x0000000023953898;
|
||||
|
||||
regs.lo = 0x0000000056584D60;
|
||||
regs.hi = 0x000000004BE35D1F;
|
||||
|
||||
if (pal) {
|
||||
regs.Write(20, 0x0000000000000000);
|
||||
regs.Write(23, 0x0000000000000006);
|
||||
regs.Write(31, 0xFFFFFFFFA4001554);
|
||||
}
|
||||
|
||||
mem.Write<u32>(regs, IMEM_REGION_START + 0x00, 0x3C0DBFC0);
|
||||
mem.Write<u32>(regs, IMEM_REGION_START + 0x04, 0x8DA807FC);
|
||||
mem.Write<u32>(regs, IMEM_REGION_START + 0x08, 0x25AD07C0);
|
||||
mem.Write<u32>(regs, IMEM_REGION_START + 0x0C, 0x31080080);
|
||||
mem.Write<u32>(regs, IMEM_REGION_START + 0x10, 0x5500FFFC);
|
||||
mem.Write<u32>(regs, IMEM_REGION_START + 0x14, 0x3C0DBFC0);
|
||||
mem.Write<u32>(regs, IMEM_REGION_START + 0x18, 0x8DA80024);
|
||||
mem.Write<u32>(regs, IMEM_REGION_START + 0x1C, 0x3C0BB000);
|
||||
break;
|
||||
case CIC_NUS_6106_7106:
|
||||
regs.Write(0, 0x0000000000000000);
|
||||
regs.Write(1, 0x0000000000000000);
|
||||
regs.Write(2, 0xFFFFFFFFA95930A4);
|
||||
regs.Write(3, 0xFFFFFFFFA95930A4);
|
||||
regs.Write(4, 0x00000000000030A4);
|
||||
regs.Write(5, 0xFFFFFFFFB04DC903);
|
||||
regs.Write(6, 0xFFFFFFFFA4001F0C);
|
||||
regs.Write(7, 0xFFFFFFFFA4001F08);
|
||||
regs.Write(8, 0x00000000000000C0);
|
||||
regs.Write(9, 0x0000000000000000);
|
||||
regs.Write(10, 0x0000000000000040);
|
||||
regs.Write(11, 0xFFFFFFFFA4000040);
|
||||
regs.Write(12, 0xFFFFFFFFBCB59510);
|
||||
regs.Write(13, 0xFFFFFFFFBCB59510);
|
||||
regs.Write(14, 0x000000000CF85C13);
|
||||
regs.Write(15, 0x000000007A3C07F4);
|
||||
regs.Write(16, 0x0000000000000000);
|
||||
regs.Write(17, 0x0000000000000000);
|
||||
regs.Write(18, 0x0000000000000000);
|
||||
regs.Write(19, 0x0000000000000000);
|
||||
regs.Write(20, 0x0000000000000001);
|
||||
regs.Write(21, 0x0000000000000000);
|
||||
regs.Write(23, 0x0000000000000000);
|
||||
regs.Write(24, 0x0000000000000002);
|
||||
regs.Write(25, 0x00000000465E3F72);
|
||||
regs.Write(26, 0x0000000000000000);
|
||||
regs.Write(27, 0x0000000000000000);
|
||||
regs.Write(28, 0x0000000000000000);
|
||||
regs.Write(29, 0xFFFFFFFFA4001FF0);
|
||||
regs.Write(30, 0x0000000000000000);
|
||||
regs.Write(31, 0xFFFFFFFFA4001550);
|
||||
regs.lo = 0x000000007A3C07F4;
|
||||
regs.hi = 0x0000000023953898;
|
||||
|
||||
if (pal) {
|
||||
regs.Write(20, 0x0000000000000000);
|
||||
regs.Write(23, 0x0000000000000006);
|
||||
regs.Write(31, 0xFFFFFFFFA4001554);
|
||||
}
|
||||
break;
|
||||
if (pal) {
|
||||
regs.Write(20, 0x0000000000000000);
|
||||
regs.Write(23, 0x0000000000000006);
|
||||
regs.Write(31, 0xFFFFFFFFA4001554);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
regs.Write(22, (cicSeeds[cicType] >> 8) & 0xFF);
|
||||
@@ -604,18 +617,18 @@ void PIF::Execute() {
|
||||
CICType cicType = mem.rom.cicType;
|
||||
bool pal = mem.rom.pal;
|
||||
mem.Write<u32>(regs, PIF_RAM_REGION_START + 0x24, cicSeeds[cicType]);
|
||||
switch(cicType) {
|
||||
case UNKNOWN_CIC_TYPE:
|
||||
Util::warn("Unknown CIC type!");
|
||||
break;
|
||||
case CIC_NUS_6101 ... CIC_NUS_6103_7103:
|
||||
mem.Write<u32>(regs, 0x318, RDRAM_SIZE);
|
||||
break;
|
||||
case CIC_NUS_6105_7105:
|
||||
mem.Write<u32>(regs, 0x3F0, RDRAM_SIZE);
|
||||
break;
|
||||
case CIC_NUS_6106_7106:
|
||||
break;
|
||||
switch (cicType) {
|
||||
case UNKNOWN_CIC_TYPE:
|
||||
Util::warn("Unknown CIC type!");
|
||||
break;
|
||||
case CIC_NUS_6101 ... CIC_NUS_6103_7103:
|
||||
mem.Write<u32>(regs, 0x318, RDRAM_SIZE);
|
||||
break;
|
||||
case CIC_NUS_6105_7105:
|
||||
mem.Write<u32>(regs, 0x3F0, RDRAM_SIZE);
|
||||
break;
|
||||
case CIC_NUS_6106_7106:
|
||||
break;
|
||||
}
|
||||
|
||||
HLE(pal, cicType);
|
||||
@@ -623,17 +636,11 @@ void PIF::Execute() {
|
||||
|
||||
std::vector<u8> PIF::Serialize() {
|
||||
std::vector<u8> res{};
|
||||
res.resize(
|
||||
6*sizeof(JoybusDevice) +
|
||||
PIF_BOOTROM_SIZE +
|
||||
PIF_RAM_SIZE +
|
||||
mempak.size() +
|
||||
eeprom.size() +
|
||||
sizeof(int));
|
||||
res.resize(6 * sizeof(JoybusDevice) + PIF_BOOTROM_SIZE + PIF_RAM_SIZE + mempak.size() + eeprom.size() + sizeof(int));
|
||||
|
||||
u32 index = 0;
|
||||
memcpy(res.data() + index, joybusDevices.data(), 6*sizeof(JoybusDevice));
|
||||
index += 6*sizeof(JoybusDevice);
|
||||
memcpy(res.data() + index, joybusDevices.data(), 6 * sizeof(JoybusDevice));
|
||||
index += 6 * sizeof(JoybusDevice);
|
||||
memcpy(res.data() + index, bootrom.data(), PIF_BOOTROM_SIZE);
|
||||
index += PIF_BOOTROM_SIZE;
|
||||
memcpy(res.data() + index, ram.data(), PIF_RAM_SIZE);
|
||||
@@ -646,4 +653,4 @@ std::vector<u8> PIF::Serialize() {
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
#pragma once
|
||||
#include <MemoryRegions.hpp>
|
||||
#include <GameDB.hpp>
|
||||
#include <MemoryRegions.hpp>
|
||||
#include <array>
|
||||
#include <filesystem>
|
||||
#include <mio/mmap.hpp>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include "MupenMovie.hpp"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace n64 {
|
||||
|
||||
enum AccessoryType : u8 {
|
||||
ACCESSORY_NONE,
|
||||
ACCESSORY_MEMPACK,
|
||||
ACCESSORY_RUMBLE_PACK
|
||||
};
|
||||
enum AccessoryType : u8 { ACCESSORY_NONE, ACCESSORY_MEMPACK, ACCESSORY_RUMBLE_PACK };
|
||||
|
||||
struct Controller {
|
||||
union {
|
||||
@@ -23,27 +19,27 @@ struct Controller {
|
||||
union {
|
||||
u8 byte1;
|
||||
struct {
|
||||
bool dpRight: 1;
|
||||
bool dpLeft: 1;
|
||||
bool dpDown: 1;
|
||||
bool dpUp: 1;
|
||||
bool start: 1;
|
||||
bool z: 1;
|
||||
bool b: 1;
|
||||
bool a: 1;
|
||||
bool dpRight : 1;
|
||||
bool dpLeft : 1;
|
||||
bool dpDown : 1;
|
||||
bool dpUp : 1;
|
||||
bool start : 1;
|
||||
bool z : 1;
|
||||
bool b : 1;
|
||||
bool a : 1;
|
||||
};
|
||||
};
|
||||
union {
|
||||
u8 byte2;
|
||||
struct {
|
||||
bool cRight: 1;
|
||||
bool cLeft: 1;
|
||||
bool cDown: 1;
|
||||
bool cUp: 1;
|
||||
bool r: 1;
|
||||
bool l: 1;
|
||||
bool zero: 1;
|
||||
bool joyReset: 1;
|
||||
bool cRight : 1;
|
||||
bool cLeft : 1;
|
||||
bool cDown : 1;
|
||||
bool cUp : 1;
|
||||
bool r : 1;
|
||||
bool l : 1;
|
||||
bool zero : 1;
|
||||
bool joyReset : 1;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -53,7 +49,7 @@ struct Controller {
|
||||
|
||||
u32 raw;
|
||||
};
|
||||
Controller& operator=(const Controller& other) {
|
||||
Controller &operator=(const Controller &other) {
|
||||
byte1 = other.byte1;
|
||||
byte2 = other.byte2;
|
||||
joyX = other.joyX;
|
||||
@@ -62,40 +58,70 @@ struct Controller {
|
||||
return *this;
|
||||
}
|
||||
|
||||
enum Key {
|
||||
A, B, Z, Start, DUp, DDown, DLeft, DRight, CUp, CDown, CLeft, CRight, LT, RT
|
||||
};
|
||||
enum Key { A, B, Z, Start, DUp, DDown, DLeft, DRight, CUp, CDown, CLeft, CRight, LT, RT };
|
||||
|
||||
enum Axis { X, Y };
|
||||
|
||||
Controller() = default;
|
||||
void UpdateButton(Key k, bool state) {
|
||||
switch(k) {
|
||||
case A: a = state; break;
|
||||
case B: b = state; break;
|
||||
case Z: z = state; break;
|
||||
case Start: start = state; break;
|
||||
case DUp: dpUp = state; break;
|
||||
case DDown: dpDown = state; break;
|
||||
case DLeft: dpLeft = state; break;
|
||||
case DRight: dpRight = state; break;
|
||||
case CUp: cUp = state; break;
|
||||
case CDown: cDown = state; break;
|
||||
case CLeft: cLeft = state; break;
|
||||
case CRight: cRight = state; break;
|
||||
case LT: l = state; break;
|
||||
case RT: r = state; break;
|
||||
switch (k) {
|
||||
case A:
|
||||
a = state;
|
||||
break;
|
||||
case B:
|
||||
b = state;
|
||||
break;
|
||||
case Z:
|
||||
z = state;
|
||||
break;
|
||||
case Start:
|
||||
start = state;
|
||||
break;
|
||||
case DUp:
|
||||
dpUp = state;
|
||||
break;
|
||||
case DDown:
|
||||
dpDown = state;
|
||||
break;
|
||||
case DLeft:
|
||||
dpLeft = state;
|
||||
break;
|
||||
case DRight:
|
||||
dpRight = state;
|
||||
break;
|
||||
case CUp:
|
||||
cUp = state;
|
||||
break;
|
||||
case CDown:
|
||||
cDown = state;
|
||||
break;
|
||||
case CLeft:
|
||||
cLeft = state;
|
||||
break;
|
||||
case CRight:
|
||||
cRight = state;
|
||||
break;
|
||||
case LT:
|
||||
l = state;
|
||||
break;
|
||||
case RT:
|
||||
r = state;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAxis(Axis a, s8 state) {
|
||||
switch(a) {
|
||||
case X: joyX = state; break;
|
||||
case Y: joyY = state; break;
|
||||
switch (a) {
|
||||
case X:
|
||||
joyX = state;
|
||||
break;
|
||||
case Y:
|
||||
joyY = state;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Controller& operator=(u32 v) {
|
||||
Controller &operator=(u32 v) {
|
||||
joyY = v & 0xff;
|
||||
joyX = v >> 8;
|
||||
byte2 = v >> 16;
|
||||
@@ -153,30 +179,28 @@ enum CICType {
|
||||
};
|
||||
|
||||
struct PIF {
|
||||
PIF(Mem& mem, Registers& regs) : mem(mem), regs(regs) {}
|
||||
PIF(Mem &mem, Registers ®s) : mem(mem), regs(regs) {}
|
||||
~PIF() = default;
|
||||
void Reset();
|
||||
void MaybeLoadMempak();
|
||||
void LoadEeprom(SaveType, const std::string&);
|
||||
void ProcessCommands(Mem&);
|
||||
void LoadEeprom(SaveType, const std::string &);
|
||||
void ProcessCommands(Mem &);
|
||||
void InitDevices(SaveType);
|
||||
void CICChallenge();
|
||||
void Execute();
|
||||
void HLE(bool pal, CICType cicType);
|
||||
bool ReadButtons(u8*);
|
||||
void ControllerID(u8*) const;
|
||||
void MempakRead(const u8*, u8*);
|
||||
void MempakWrite(u8*, u8*);
|
||||
void EepromRead(const u8*, u8*, const Mem&) const;
|
||||
void EepromWrite(const u8*, u8*, const Mem&);
|
||||
bool ReadButtons(u8 *);
|
||||
void ControllerID(u8 *) const;
|
||||
void MempakRead(const u8 *, u8 *);
|
||||
void MempakWrite(u8 *, u8 *);
|
||||
void EepromRead(const u8 *, u8 *, const Mem &) const;
|
||||
void EepromWrite(const u8 *, u8 *, const Mem &);
|
||||
std::vector<u8> Serialize();
|
||||
void UpdateButton(int index, Controller::Key k, bool state) {
|
||||
joybusDevices[index].controller.UpdateButton(k, state);
|
||||
}
|
||||
|
||||
void UpdateAxis(int index, Controller::Axis a, s8 state) {
|
||||
joybusDevices[index].controller.UpdateAxis(a, state);
|
||||
}
|
||||
void UpdateAxis(int index, Controller::Axis a, s8 state) { joybusDevices[index].controller.UpdateAxis(a, state); }
|
||||
|
||||
bool mempakOpen = false;
|
||||
std::array<JoybusDevice, 6> joybusDevices{};
|
||||
@@ -187,18 +211,20 @@ struct PIF {
|
||||
std::string mempakPath{}, eepromPath{};
|
||||
size_t eepromSize{};
|
||||
MupenMovie movie;
|
||||
Mem& mem;
|
||||
Registers& regs;
|
||||
Mem &mem;
|
||||
Registers ®s;
|
||||
|
||||
FORCE_INLINE u8 Read(u32 addr) {
|
||||
addr &= 0x7FF;
|
||||
if(addr < 0x7c0) return bootrom[addr];
|
||||
if (addr < 0x7c0)
|
||||
return bootrom[addr];
|
||||
return ram[addr & PIF_RAM_DSIZE];
|
||||
}
|
||||
|
||||
FORCE_INLINE void Write(u32 addr, u8 val) {
|
||||
addr &= 0x7FF;
|
||||
if(addr < 0x7c0) return;
|
||||
if (addr < 0x7c0)
|
||||
return;
|
||||
ram[addr & PIF_RAM_DSIZE] = val;
|
||||
}
|
||||
|
||||
@@ -210,4 +236,4 @@ struct PIF {
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#include <Netplay.hpp>
|
||||
#include <PIF.hpp>
|
||||
#include <PIF/MupenMovie.hpp>
|
||||
#include <Netplay.hpp>
|
||||
#include <log.hpp>
|
||||
|
||||
namespace n64 {
|
||||
void PIF::InitDevices(SaveType saveType) {
|
||||
joybusDevices[0].type = JOYBUS_CONTROLLER;
|
||||
joybusDevices[0].accessoryType = ACCESSORY_MEMPACK;
|
||||
for (int i = 1; i < 4; i++) { //TODO: make this configurable
|
||||
for (int i = 1; i < 4; i++) { // TODO: make this configurable
|
||||
joybusDevices[i].type = JOYBUS_NONE;
|
||||
joybusDevices[i].accessoryType = ACCESSORY_NONE;
|
||||
}
|
||||
@@ -25,59 +25,59 @@ void PIF::InitDevices(SaveType saveType) {
|
||||
void PIF::ControllerID(u8 *res) const {
|
||||
if (channel < 6) {
|
||||
switch (joybusDevices[channel].type) {
|
||||
case JOYBUS_NONE:
|
||||
res[0] = 0x00;
|
||||
res[1] = 0x00;
|
||||
res[2] = 0x00;
|
||||
break;
|
||||
case JOYBUS_CONTROLLER:
|
||||
res[0] = 0x05;
|
||||
res[1] = 0x00;
|
||||
res[2] = joybusDevices[channel].accessoryType != ACCESSORY_NONE ? 0x01 : 0x02;
|
||||
break;
|
||||
case JOYBUS_DANCEPAD:
|
||||
res[0] = 0x05;
|
||||
res[1] = 0x00;
|
||||
res[2] = 0x00;
|
||||
break;
|
||||
case JOYBUS_VRU:
|
||||
res[0] = 0x00;
|
||||
res[1] = 0x01;
|
||||
res[2] = 0x00;
|
||||
break;
|
||||
case JOYBUS_MOUSE:
|
||||
res[0] = 0x02;
|
||||
res[1] = 0x00;
|
||||
res[2] = 0x00;
|
||||
break;
|
||||
case JOYBUS_RANDNET_KEYBOARD:
|
||||
res[0] = 0x00;
|
||||
res[1] = 0x02;
|
||||
res[2] = 0x00;
|
||||
break;
|
||||
case JOYBUS_DENSHA_DE_GO:
|
||||
res[0] = 0x20;
|
||||
res[1] = 0x04;
|
||||
res[2] = 0x00;
|
||||
break;
|
||||
case JOYBUS_4KB_EEPROM:
|
||||
res[0] = 0x00;
|
||||
res[1] = 0x80;
|
||||
res[2] = 0x00;
|
||||
break;
|
||||
case JOYBUS_16KB_EEPROM:
|
||||
res[0] = 0x00;
|
||||
res[1] = 0xC0;
|
||||
res[2] = 0x00;
|
||||
break;
|
||||
case JOYBUS_NONE:
|
||||
res[0] = 0x00;
|
||||
res[1] = 0x00;
|
||||
res[2] = 0x00;
|
||||
break;
|
||||
case JOYBUS_CONTROLLER:
|
||||
res[0] = 0x05;
|
||||
res[1] = 0x00;
|
||||
res[2] = joybusDevices[channel].accessoryType != ACCESSORY_NONE ? 0x01 : 0x02;
|
||||
break;
|
||||
case JOYBUS_DANCEPAD:
|
||||
res[0] = 0x05;
|
||||
res[1] = 0x00;
|
||||
res[2] = 0x00;
|
||||
break;
|
||||
case JOYBUS_VRU:
|
||||
res[0] = 0x00;
|
||||
res[1] = 0x01;
|
||||
res[2] = 0x00;
|
||||
break;
|
||||
case JOYBUS_MOUSE:
|
||||
res[0] = 0x02;
|
||||
res[1] = 0x00;
|
||||
res[2] = 0x00;
|
||||
break;
|
||||
case JOYBUS_RANDNET_KEYBOARD:
|
||||
res[0] = 0x00;
|
||||
res[1] = 0x02;
|
||||
res[2] = 0x00;
|
||||
break;
|
||||
case JOYBUS_DENSHA_DE_GO:
|
||||
res[0] = 0x20;
|
||||
res[1] = 0x04;
|
||||
res[2] = 0x00;
|
||||
break;
|
||||
case JOYBUS_4KB_EEPROM:
|
||||
res[0] = 0x00;
|
||||
res[1] = 0x80;
|
||||
res[2] = 0x00;
|
||||
break;
|
||||
case JOYBUS_16KB_EEPROM:
|
||||
res[0] = 0x00;
|
||||
res[1] = 0xC0;
|
||||
res[2] = 0x00;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
Util::panic("Device ID on unknown channel {}", channel);
|
||||
}
|
||||
}
|
||||
|
||||
bool PIF::ReadButtons(u8* res) {
|
||||
if(channel >= 6) {
|
||||
bool PIF::ReadButtons(u8 *res) {
|
||||
if (channel >= 6) {
|
||||
res[0] = 0;
|
||||
res[1] = 0;
|
||||
res[2] = 0;
|
||||
@@ -86,36 +86,36 @@ bool PIF::ReadButtons(u8* res) {
|
||||
}
|
||||
|
||||
switch (joybusDevices[channel].type) {
|
||||
case JOYBUS_NONE:
|
||||
res[0] = 0x00;
|
||||
res[1] = 0x00;
|
||||
res[2] = 0x00;
|
||||
res[3] = 0x00;
|
||||
return false; // Device not present
|
||||
case JOYBUS_4KB_EEPROM:
|
||||
case JOYBUS_16KB_EEPROM:
|
||||
case JOYBUS_CONTROLLER:
|
||||
if (movie.IsLoaded()) {
|
||||
Controller controller = movie.NextInputs();
|
||||
res[0] = controller.byte1;
|
||||
res[1] = controller.byte2;
|
||||
res[2] = controller.joyX;
|
||||
res[3] = controller.joyY;
|
||||
} else {
|
||||
res[0] = joybusDevices[channel].controller.byte1;
|
||||
res[1] = joybusDevices[channel].controller.byte2;
|
||||
res[2] = joybusDevices[channel].controller.joyX;
|
||||
res[3] = joybusDevices[channel].controller.joyY;
|
||||
}
|
||||
return true;
|
||||
case JOYBUS_DANCEPAD:
|
||||
case JOYBUS_VRU:
|
||||
case JOYBUS_MOUSE:
|
||||
case JOYBUS_RANDNET_KEYBOARD:
|
||||
case JOYBUS_DENSHA_DE_GO:
|
||||
return false;
|
||||
case JOYBUS_NONE:
|
||||
res[0] = 0x00;
|
||||
res[1] = 0x00;
|
||||
res[2] = 0x00;
|
||||
res[3] = 0x00;
|
||||
return false; // Device not present
|
||||
case JOYBUS_4KB_EEPROM:
|
||||
case JOYBUS_16KB_EEPROM:
|
||||
case JOYBUS_CONTROLLER:
|
||||
if (movie.IsLoaded()) {
|
||||
Controller controller = movie.NextInputs();
|
||||
res[0] = controller.byte1;
|
||||
res[1] = controller.byte2;
|
||||
res[2] = controller.joyX;
|
||||
res[3] = controller.joyY;
|
||||
} else {
|
||||
res[0] = joybusDevices[channel].controller.byte1;
|
||||
res[1] = joybusDevices[channel].controller.byte2;
|
||||
res[2] = joybusDevices[channel].controller.joyX;
|
||||
res[3] = joybusDevices[channel].controller.joyY;
|
||||
}
|
||||
return true;
|
||||
case JOYBUS_DANCEPAD:
|
||||
case JOYBUS_VRU:
|
||||
case JOYBUS_MOUSE:
|
||||
case JOYBUS_RANDNET_KEYBOARD:
|
||||
case JOYBUS_DENSHA_DE_GO:
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -5,20 +5,20 @@
|
||||
|
||||
union TASMovieControllerData {
|
||||
struct {
|
||||
unsigned dpadRight: 1;
|
||||
unsigned dpadLeft: 1;
|
||||
unsigned dpadDown: 1;
|
||||
unsigned dpadUp: 1;
|
||||
unsigned start: 1;
|
||||
unsigned z: 1;
|
||||
unsigned b: 1;
|
||||
unsigned a: 1;
|
||||
unsigned cRight: 1;
|
||||
unsigned cLeft: 1;
|
||||
unsigned cDown: 1;
|
||||
unsigned cUp: 1;
|
||||
unsigned r: 1;
|
||||
unsigned l: 1;
|
||||
unsigned dpadRight : 1;
|
||||
unsigned dpadLeft : 1;
|
||||
unsigned dpadDown : 1;
|
||||
unsigned dpadUp : 1;
|
||||
unsigned start : 1;
|
||||
unsigned z : 1;
|
||||
unsigned b : 1;
|
||||
unsigned a : 1;
|
||||
unsigned cRight : 1;
|
||||
unsigned cLeft : 1;
|
||||
unsigned cDown : 1;
|
||||
unsigned cUp : 1;
|
||||
unsigned r : 1;
|
||||
unsigned l : 1;
|
||||
unsigned : 2;
|
||||
signed analogX : 8;
|
||||
signed analogY : 8;
|
||||
@@ -30,14 +30,15 @@ static_assert(sizeof(TASMovieControllerData) == 4);
|
||||
|
||||
bool MupenMovie::Load(const fs::path &path) {
|
||||
loadedTasMovie = Util::ReadFileBinary(path.string());
|
||||
if(!IsLoaded()) {
|
||||
if (!IsLoaded()) {
|
||||
Util::error("Error loading movie!");
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(&loadedTasMovieHeader, loadedTasMovie.data(), sizeof(TASMovieHeader));
|
||||
|
||||
if (loadedTasMovieHeader.signature[0] != 0x4D || loadedTasMovieHeader.signature[1] != 0x36 || loadedTasMovieHeader.signature[2] != 0x34 || loadedTasMovieHeader.signature[3] != 0x1A) {
|
||||
if (loadedTasMovieHeader.signature[0] != 0x4D || loadedTasMovieHeader.signature[1] != 0x36 ||
|
||||
loadedTasMovieHeader.signature[2] != 0x34 || loadedTasMovieHeader.signature[3] != 0x1A) {
|
||||
Util::error("Failed to load movie: incorrect signature. Are you sure this is a valid movie?");
|
||||
return false;
|
||||
}
|
||||
@@ -48,7 +49,8 @@ bool MupenMovie::Load(const fs::path &path) {
|
||||
}
|
||||
|
||||
if (loadedTasMovieHeader.startType != 2) {
|
||||
Util::error("Movie start type is {} - only movies with a start type of 2 are supported (start at power on)", loadedTasMovieHeader.startType);
|
||||
Util::error("Movie start type is {} - only movies with a start type of 2 are supported (start at power on)",
|
||||
loadedTasMovieHeader.startType);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -66,18 +68,19 @@ bool MupenMovie::Load(const fs::path &path) {
|
||||
}
|
||||
|
||||
MupenMovie::MupenMovie(const fs::path &path) {
|
||||
if(!Load(path)) {
|
||||
if (!Load(path)) {
|
||||
Util::panic("");
|
||||
}
|
||||
}
|
||||
|
||||
void MupenMovie::Reset() {
|
||||
if(!IsLoaded()) return;
|
||||
if (!IsLoaded())
|
||||
return;
|
||||
|
||||
loadedTasMovieIndex = sizeof(TASMovieHeader) - 4; // skip header
|
||||
}
|
||||
|
||||
FORCE_INLINE void LogController(const n64::Controller& controller) {
|
||||
FORCE_INLINE void LogController(const n64::Controller &controller) {
|
||||
Util::debug("c_right: {}", controller.cRight);
|
||||
Util::debug("c_left: {}", controller.cLeft);
|
||||
Util::debug("c_down: {}", controller.cDown);
|
||||
@@ -133,4 +136,4 @@ n64::Controller MupenMovie::NextInputs() {
|
||||
LogController(controller);
|
||||
|
||||
return controller;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,13 +47,14 @@ static_assert(sizeof(TASMovieHeader) == 1024);
|
||||
|
||||
struct MupenMovie {
|
||||
MupenMovie() = default;
|
||||
MupenMovie(const fs::path&);
|
||||
bool Load(const fs::path&);
|
||||
MupenMovie(const fs::path &);
|
||||
bool Load(const fs::path &);
|
||||
void Reset();
|
||||
n64::Controller NextInputs();
|
||||
bool IsLoaded() const { return !loadedTasMovie.empty(); }
|
||||
|
||||
private:
|
||||
std::vector<u8> loadedTasMovie = {};
|
||||
TASMovieHeader loadedTasMovieHeader = {};
|
||||
uint32_t loadedTasMovieIndex = 0;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
#include <log.hpp>
|
||||
|
||||
namespace n64 {
|
||||
RI::RI() {
|
||||
Reset();
|
||||
}
|
||||
RI::RI() { Reset(); }
|
||||
|
||||
void RI::Reset() {
|
||||
mode = 0xE;
|
||||
@@ -14,24 +12,36 @@ void RI::Reset() {
|
||||
}
|
||||
|
||||
auto RI::Read(u32 addr) const -> u32 {
|
||||
switch(addr) {
|
||||
case 0x04700000: return mode;
|
||||
case 0x04700004: return config;
|
||||
case 0x0470000C: return select;
|
||||
case 0x04700010: return refresh;
|
||||
default:
|
||||
Util::panic("Unhandled RI[{:08X}] read", addr);
|
||||
switch (addr) {
|
||||
case 0x04700000:
|
||||
return mode;
|
||||
case 0x04700004:
|
||||
return config;
|
||||
case 0x0470000C:
|
||||
return select;
|
||||
case 0x04700010:
|
||||
return refresh;
|
||||
default:
|
||||
Util::panic("Unhandled RI[{:08X}] read", addr);
|
||||
}
|
||||
}
|
||||
|
||||
void RI::Write(u32 addr, u32 val) {
|
||||
switch(addr) {
|
||||
case 0x04700000: mode = val; break;
|
||||
case 0x04700004: config = val; break;
|
||||
case 0x0470000C: select = val; break;
|
||||
case 0x04700010: refresh = val; break;
|
||||
default:
|
||||
Util::panic("Unhandled RI[{:08X}] write with val {:08X}", addr, val);
|
||||
switch (addr) {
|
||||
case 0x04700000:
|
||||
mode = val;
|
||||
break;
|
||||
case 0x04700004:
|
||||
config = val;
|
||||
break;
|
||||
case 0x0470000C:
|
||||
select = val;
|
||||
break;
|
||||
case 0x04700010:
|
||||
refresh = val;
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unhandled RI[{:08X}] write with val {:08X}", addr, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -11,4 +11,4 @@ struct RI {
|
||||
u32 mode{0xE}, config{0x40}, select{0x14}, refresh{0x63634};
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
#include <core/mmio/SI.hpp>
|
||||
#include <core/Mem.hpp>
|
||||
#include <Scheduler.hpp>
|
||||
#include <core/Mem.hpp>
|
||||
#include <core/mmio/SI.hpp>
|
||||
|
||||
namespace n64 {
|
||||
SI::SI(Mem& mem, Registers& regs) : mem(mem), regs(regs), pif(mem, regs) {
|
||||
Reset();
|
||||
}
|
||||
SI::SI(Mem &mem, Registers ®s) : mem(mem), regs(regs), pif(mem, regs) { Reset(); }
|
||||
|
||||
void SI::Reset() {
|
||||
status.raw = 0;
|
||||
@@ -16,35 +14,40 @@ void SI::Reset() {
|
||||
}
|
||||
|
||||
auto SI::Read(u32 addr) const -> u32 {
|
||||
switch(addr) {
|
||||
case 0x04800000: return dramAddr;
|
||||
case 0x04800004: case 0x04800010: return pifAddr;
|
||||
case 0x0480000C: return 0;
|
||||
case 0x04800018: {
|
||||
u32 val = 0;
|
||||
val |= status.dmaBusy;
|
||||
val |= (0 << 1);
|
||||
val |= (0 << 3);
|
||||
val |= (mem.mmio.mi.miIntr.si << 12);
|
||||
return val;
|
||||
}
|
||||
default:
|
||||
Util::panic("Unhandled SI[{:08X}] read", addr);
|
||||
switch (addr) {
|
||||
case 0x04800000:
|
||||
return dramAddr;
|
||||
case 0x04800004:
|
||||
case 0x04800010:
|
||||
return pifAddr;
|
||||
case 0x0480000C:
|
||||
return 0;
|
||||
case 0x04800018:
|
||||
u32 val = 0;
|
||||
val |= status.dmaBusy;
|
||||
val |= (0 << 1);
|
||||
val |= (0 << 3);
|
||||
val |= (mem.mmio.mi.miIntr.si << 12);
|
||||
return val;
|
||||
default:
|
||||
Util::panic("Unhandled SI[{:08X}] read", addr);
|
||||
}
|
||||
}
|
||||
|
||||
// pif -> rdram
|
||||
template <> void SI::DMA<true>() {
|
||||
template <>
|
||||
void SI::DMA<true>() {
|
||||
pif.ProcessCommands(mem);
|
||||
for(int i = 0; i < 64; i++) {
|
||||
for (int i = 0; i < 64; i++) {
|
||||
mem.mmio.rdp.WriteRDRAM<u8>(dramAddr + i, pif.Read(pifAddr + i));
|
||||
}
|
||||
Util::trace("SI DMA from PIF RAM to RDRAM ({:08X} to {:08X})", pifAddr, dramAddr);
|
||||
}
|
||||
|
||||
// rdram -> pif
|
||||
template <> void SI::DMA<false>() {
|
||||
for(int i = 0; i < 64; i++) {
|
||||
template <>
|
||||
void SI::DMA<false>() {
|
||||
for (int i = 0; i < 64; i++) {
|
||||
pif.Write(pifAddr + i, mem.mmio.rdp.ReadRDRAM<u8>(dramAddr + i));
|
||||
}
|
||||
Util::trace("SI DMA from RDRAM to PIF RAM ({:08X} to {:08X})", dramAddr, pifAddr);
|
||||
@@ -52,33 +55,35 @@ template <> void SI::DMA<false>() {
|
||||
|
||||
void SI::DMA() {
|
||||
status.dmaBusy = false;
|
||||
if (toDram) DMA<true>();
|
||||
else DMA<false>();
|
||||
if (toDram)
|
||||
DMA<true>();
|
||||
else
|
||||
DMA<false>();
|
||||
mem.mmio.mi.InterruptRaise(MI::Interrupt::SI);
|
||||
}
|
||||
|
||||
void SI::Write(u32 addr, u32 val) {
|
||||
switch(addr) {
|
||||
case 0x04800000:
|
||||
dramAddr = val & RDRAM_DSIZE;
|
||||
break;
|
||||
case 0x04800004: {
|
||||
pifAddr = val & 0x1FFFFFFF;
|
||||
status.dmaBusy = true;
|
||||
toDram = true;
|
||||
scheduler.EnqueueRelative(SI_DMA_DELAY, SI_DMA);
|
||||
} break;
|
||||
case 0x04800010: {
|
||||
pifAddr = val & 0x1FFFFFFF;
|
||||
status.dmaBusy = true;
|
||||
toDram = false;
|
||||
scheduler.EnqueueRelative(SI_DMA_DELAY, SI_DMA);
|
||||
} break;
|
||||
case 0x04800018:
|
||||
mem.mmio.mi.InterruptLower(MI::Interrupt::SI);
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unhandled SI[{:08X}] write ({:08X})", addr, val);
|
||||
switch (addr) {
|
||||
case 0x04800000:
|
||||
dramAddr = val & RDRAM_DSIZE;
|
||||
break;
|
||||
case 0x04800004:
|
||||
pifAddr = val & 0x1FFFFFFF;
|
||||
status.dmaBusy = true;
|
||||
toDram = true;
|
||||
scheduler.EnqueueRelative(SI_DMA_DELAY, SI_DMA);
|
||||
break;
|
||||
case 0x04800010:
|
||||
pifAddr = val & 0x1FFFFFFF;
|
||||
status.dmaBusy = true;
|
||||
toDram = false;
|
||||
scheduler.EnqueueRelative(SI_DMA_DELAY, SI_DMA);
|
||||
break;
|
||||
case 0x04800018:
|
||||
mem.mmio.mi.InterruptLower(MI::Interrupt::SI);
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unhandled SI[{:08X}] write ({:08X})", addr, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -8,19 +8,19 @@ namespace n64 {
|
||||
union SIStatus {
|
||||
u32 raw{};
|
||||
struct {
|
||||
unsigned dmaBusy:1;
|
||||
unsigned ioBusy:1;
|
||||
unsigned reserved:1;
|
||||
unsigned dmaErr:1;
|
||||
unsigned:8;
|
||||
unsigned intr:1;
|
||||
unsigned dmaBusy : 1;
|
||||
unsigned ioBusy : 1;
|
||||
unsigned reserved : 1;
|
||||
unsigned dmaErr : 1;
|
||||
unsigned : 8;
|
||||
unsigned intr : 1;
|
||||
};
|
||||
};
|
||||
|
||||
struct Mem;
|
||||
|
||||
struct SI {
|
||||
SI(Mem&, Registers&);
|
||||
SI(Mem &, Registers &);
|
||||
void Reset();
|
||||
SIStatus status{};
|
||||
u32 dramAddr{};
|
||||
@@ -33,10 +33,11 @@ struct SI {
|
||||
void DMA();
|
||||
void DMA();
|
||||
PIF pif;
|
||||
|
||||
private:
|
||||
Mem& mem;
|
||||
Registers& regs;
|
||||
Mem &mem;
|
||||
Registers ®s;
|
||||
};
|
||||
|
||||
#define SI_DMA_DELAY (65536 * 2)
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
#include <core/mmio/VI.hpp>
|
||||
#include <log.hpp>
|
||||
#include <core/registers/Registers.hpp>
|
||||
#include <core/Mem.hpp>
|
||||
#include <core/mmio/VI.hpp>
|
||||
#include <core/registers/Registers.hpp>
|
||||
#include <log.hpp>
|
||||
|
||||
namespace n64 {
|
||||
VI::VI (Mem& mem, Registers& regs) : mem(mem), regs(regs) {
|
||||
Reset();
|
||||
}
|
||||
VI::VI(Mem &mem, Registers ®s) : mem(mem), regs(regs) { Reset(); }
|
||||
|
||||
void VI::Reset() {
|
||||
status.raw = 0xF;
|
||||
@@ -27,65 +25,95 @@ void VI::Reset() {
|
||||
}
|
||||
|
||||
u32 VI::Read(u32 paddr) const {
|
||||
switch(paddr) {
|
||||
case 0x04400000: return status.raw;
|
||||
case 0x04400004: return origin;
|
||||
case 0x04400008: return width;
|
||||
case 0x0440000C: return intr;
|
||||
case 0x04400010: return current << 1;
|
||||
case 0x04400014: return burst.raw;
|
||||
case 0x04400018: return vsync;
|
||||
case 0x0440001C: return hsync;
|
||||
case 0x04400020: return hsyncLeap.raw;
|
||||
case 0x04400024: return hstart.raw;
|
||||
case 0x04400028: return vstart.raw;
|
||||
case 0x0440002C: return vburst;
|
||||
case 0x04400030: return xscale.raw;
|
||||
case 0x04400034: return yscale.raw;
|
||||
default:
|
||||
Util::panic("Unimplemented VI[%08X] read", paddr);
|
||||
switch (paddr) {
|
||||
case 0x04400000:
|
||||
return status.raw;
|
||||
case 0x04400004:
|
||||
return origin;
|
||||
case 0x04400008:
|
||||
return width;
|
||||
case 0x0440000C:
|
||||
return intr;
|
||||
case 0x04400010:
|
||||
return current << 1;
|
||||
case 0x04400014:
|
||||
return burst.raw;
|
||||
case 0x04400018:
|
||||
return vsync;
|
||||
case 0x0440001C:
|
||||
return hsync;
|
||||
case 0x04400020:
|
||||
return hsyncLeap.raw;
|
||||
case 0x04400024:
|
||||
return hstart.raw;
|
||||
case 0x04400028:
|
||||
return vstart.raw;
|
||||
case 0x0440002C:
|
||||
return vburst;
|
||||
case 0x04400030:
|
||||
return xscale.raw;
|
||||
case 0x04400034:
|
||||
return yscale.raw;
|
||||
default:
|
||||
Util::panic("Unimplemented VI[%08X] read", paddr);
|
||||
}
|
||||
}
|
||||
|
||||
void VI::Write(u32 paddr, u32 val) {
|
||||
switch(paddr) {
|
||||
case 0x04400000:
|
||||
status.raw = val;
|
||||
numFields = status.serrate ? 2 : 1;
|
||||
break;
|
||||
case 0x04400004: {
|
||||
switch (paddr) {
|
||||
case 0x04400000:
|
||||
status.raw = val;
|
||||
numFields = status.serrate ? 2 : 1;
|
||||
break;
|
||||
case 0x04400004:
|
||||
{
|
||||
u32 masked = val & 0xFFFFFF;
|
||||
if(origin != masked) {
|
||||
if (origin != masked) {
|
||||
swaps++;
|
||||
}
|
||||
origin = masked;
|
||||
} break;
|
||||
case 0x04400008: {
|
||||
width = val & 0x7FF;
|
||||
} break;
|
||||
case 0x0440000C: {
|
||||
intr = val & 0x3FF;
|
||||
} break;
|
||||
case 0x04400010:
|
||||
mem.mmio.mi.InterruptLower(MI::Interrupt::VI);
|
||||
break;
|
||||
case 0x04400014: burst.raw = val; break;
|
||||
case 0x04400018: {
|
||||
vsync = val & 0x3FF;
|
||||
numHalflines = vsync >> 1;
|
||||
cyclesPerHalfline = GetCyclesPerFrame(isPal) / numHalflines;
|
||||
} break;
|
||||
case 0x0440001C: {
|
||||
hsync = val & 0x3FF;
|
||||
} break;
|
||||
case 0x04400020: hsyncLeap.raw = val; break;
|
||||
case 0x04400024: hstart.raw = val; break;
|
||||
case 0x04400028: vstart.raw = val; break;
|
||||
case 0x0440002C: vburst = val; break;
|
||||
case 0x04400030: xscale.raw = val; break;
|
||||
case 0x04400034: yscale.raw = val; break;
|
||||
default:
|
||||
Util::panic("Unimplemented VI[%08X] write (%08X)", paddr, val);
|
||||
}
|
||||
break;
|
||||
case 0x04400008:
|
||||
width = val & 0x7FF;
|
||||
break;
|
||||
case 0x0440000C:
|
||||
intr = val & 0x3FF;
|
||||
break;
|
||||
case 0x04400010:
|
||||
mem.mmio.mi.InterruptLower(MI::Interrupt::VI);
|
||||
break;
|
||||
case 0x04400014:
|
||||
burst.raw = val;
|
||||
break;
|
||||
case 0x04400018:
|
||||
vsync = val & 0x3FF;
|
||||
numHalflines = vsync >> 1;
|
||||
cyclesPerHalfline = GetCyclesPerFrame(isPal) / numHalflines;
|
||||
break;
|
||||
case 0x0440001C:
|
||||
hsync = val & 0x3FF;
|
||||
break;
|
||||
case 0x04400020:
|
||||
hsyncLeap.raw = val;
|
||||
break;
|
||||
case 0x04400024:
|
||||
hstart.raw = val;
|
||||
break;
|
||||
case 0x04400028:
|
||||
vstart.raw = val;
|
||||
break;
|
||||
case 0x0440002C:
|
||||
vburst = val;
|
||||
break;
|
||||
case 0x04400030:
|
||||
xscale.raw = val;
|
||||
break;
|
||||
case 0x04400034:
|
||||
yscale.raw = val;
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unimplemented VI[%08X] write (%08X)", paddr, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -21,42 +21,37 @@ union VIHsyncLeap {
|
||||
unsigned:6;
|
||||
};*/
|
||||
u32 raw;
|
||||
} ;
|
||||
};
|
||||
|
||||
union AxisScale {
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned scaleDecimal:10;
|
||||
unsigned scaleInteger:2;
|
||||
unsigned subpixelOffsetDecimal:10;
|
||||
unsigned subpixelOffsetInteger:2;
|
||||
unsigned:4;
|
||||
unsigned scaleDecimal : 10;
|
||||
unsigned scaleInteger : 2;
|
||||
unsigned subpixelOffsetDecimal : 10;
|
||||
unsigned subpixelOffsetInteger : 2;
|
||||
unsigned : 4;
|
||||
};
|
||||
struct {
|
||||
unsigned scale:12;
|
||||
unsigned subpixelOffset:12;
|
||||
unsigned:4;
|
||||
unsigned scale : 12;
|
||||
unsigned subpixelOffset : 12;
|
||||
unsigned : 4;
|
||||
};
|
||||
};
|
||||
|
||||
enum VIFormat {
|
||||
blank = 0,
|
||||
reserved = 1,
|
||||
f5553 = 2,
|
||||
f8888 = 3
|
||||
};
|
||||
enum VIFormat { blank = 0, reserved = 1, f5553 = 2, f8888 = 3 };
|
||||
|
||||
union VIStatus {
|
||||
struct {
|
||||
u8 type:2;
|
||||
bool gamma_dither_enable:1;
|
||||
bool gamma_enable:1;
|
||||
bool divot_enable:1;
|
||||
bool reserved_always_off:1;
|
||||
bool serrate:1;
|
||||
bool reserved_diagnostics_only:1;
|
||||
unsigned antialias_mode:3;
|
||||
unsigned:21;
|
||||
u8 type : 2;
|
||||
bool gamma_dither_enable : 1;
|
||||
bool gamma_enable : 1;
|
||||
bool divot_enable : 1;
|
||||
bool reserved_always_off : 1;
|
||||
bool serrate : 1;
|
||||
bool reserved_diagnostics_only : 1;
|
||||
unsigned antialias_mode : 3;
|
||||
unsigned : 21;
|
||||
};
|
||||
|
||||
u32 raw;
|
||||
@@ -65,10 +60,10 @@ union VIStatus {
|
||||
union AxisStart {
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned end:10;
|
||||
unsigned:6;
|
||||
unsigned start:10;
|
||||
unsigned:6;
|
||||
unsigned end : 10;
|
||||
unsigned : 6;
|
||||
unsigned start : 10;
|
||||
unsigned : 6;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -76,7 +71,7 @@ struct Mem;
|
||||
struct Registers;
|
||||
|
||||
struct VI {
|
||||
VI(Mem&, Registers&);
|
||||
VI(Mem &, Registers &);
|
||||
void Reset();
|
||||
[[nodiscard]] u32 Read(u32) const;
|
||||
void Write(u32, u32);
|
||||
@@ -93,8 +88,9 @@ struct VI {
|
||||
int numHalflines{};
|
||||
int numFields{};
|
||||
int cyclesPerHalfline{};
|
||||
|
||||
private:
|
||||
Mem& mem;
|
||||
Registers& regs;
|
||||
Mem &mem;
|
||||
Registers ®s;
|
||||
};
|
||||
} // backend
|
||||
} // namespace n64
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
#include <log.hpp>
|
||||
#include <core/registers/Registers.hpp>
|
||||
#include <core/Interpreter.hpp>
|
||||
#include <core/registers/Registers.hpp>
|
||||
#include <log.hpp>
|
||||
|
||||
namespace n64 {
|
||||
Cop0::Cop0(Registers& regs) : regs(regs) {
|
||||
Reset();
|
||||
}
|
||||
Cop0::Cop0(Registers ®s) : regs(regs) { Reset(); }
|
||||
|
||||
void Cop0::Reset() {
|
||||
cause.raw = 0xB000007C;
|
||||
@@ -40,155 +38,251 @@ void Cop0::Reset() {
|
||||
ParityError = {}, CacheError = {}, TagLo = {}, TagHi = {};
|
||||
ErrorEPC = {};
|
||||
r31 = {};
|
||||
memset(tlb, 0, sizeof(TLBEntry)*32);
|
||||
memset(tlb, 0, sizeof(TLBEntry) * 32);
|
||||
tlbError = NONE;
|
||||
openbus = {};
|
||||
}
|
||||
|
||||
u32 Cop0::GetReg32(u8 addr) {
|
||||
switch(addr) {
|
||||
case COP0_REG_INDEX: return index.raw & INDEX_MASK;
|
||||
case COP0_REG_RANDOM: return GetRandom();
|
||||
case COP0_REG_ENTRYLO0: return entryLo0.raw;
|
||||
case COP0_REG_ENTRYLO1: return entryLo1.raw;
|
||||
case COP0_REG_CONTEXT: return context.raw;
|
||||
case COP0_REG_PAGEMASK: return pageMask.raw;
|
||||
case COP0_REG_WIRED: return wired;
|
||||
case COP0_REG_BADVADDR: return badVaddr;
|
||||
case COP0_REG_COUNT: return GetCount();
|
||||
case COP0_REG_ENTRYHI: return entryHi.raw;
|
||||
case COP0_REG_COMPARE: return compare;
|
||||
case COP0_REG_STATUS: return status.raw;
|
||||
case COP0_REG_CAUSE: return cause.raw;
|
||||
case COP0_REG_EPC: return EPC;
|
||||
case COP0_REG_PRID: return PRId;
|
||||
case COP0_REG_CONFIG: return Config;
|
||||
case COP0_REG_LLADDR: return LLAddr;
|
||||
case COP0_REG_WATCHLO: return WatchLo;
|
||||
case COP0_REG_WATCHHI: return WatchHi;
|
||||
case COP0_REG_XCONTEXT: return xcontext.raw;
|
||||
case COP0_REG_PARITY_ERR: return ParityError;
|
||||
case COP0_REG_CACHE_ERR: return CacheError;
|
||||
case COP0_REG_TAGLO: return TagLo;
|
||||
case COP0_REG_TAGHI: return TagHi;
|
||||
case COP0_REG_ERROREPC: return ErrorEPC;
|
||||
case 7: case 21: case 22:
|
||||
case 23: case 24: case 25:
|
||||
case 31: return openbus;
|
||||
default:
|
||||
Util::panic("Unsupported word read from COP0 register {}", addr);
|
||||
switch (addr) {
|
||||
case COP0_REG_INDEX:
|
||||
return index.raw & INDEX_MASK;
|
||||
case COP0_REG_RANDOM:
|
||||
return GetRandom();
|
||||
case COP0_REG_ENTRYLO0:
|
||||
return entryLo0.raw;
|
||||
case COP0_REG_ENTRYLO1:
|
||||
return entryLo1.raw;
|
||||
case COP0_REG_CONTEXT:
|
||||
return context.raw;
|
||||
case COP0_REG_PAGEMASK:
|
||||
return pageMask.raw;
|
||||
case COP0_REG_WIRED:
|
||||
return wired;
|
||||
case COP0_REG_BADVADDR:
|
||||
return badVaddr;
|
||||
case COP0_REG_COUNT:
|
||||
return GetCount();
|
||||
case COP0_REG_ENTRYHI:
|
||||
return entryHi.raw;
|
||||
case COP0_REG_COMPARE:
|
||||
return compare;
|
||||
case COP0_REG_STATUS:
|
||||
return status.raw;
|
||||
case COP0_REG_CAUSE:
|
||||
return cause.raw;
|
||||
case COP0_REG_EPC:
|
||||
return EPC;
|
||||
case COP0_REG_PRID:
|
||||
return PRId;
|
||||
case COP0_REG_CONFIG:
|
||||
return Config;
|
||||
case COP0_REG_LLADDR:
|
||||
return LLAddr;
|
||||
case COP0_REG_WATCHLO:
|
||||
return WatchLo;
|
||||
case COP0_REG_WATCHHI:
|
||||
return WatchHi;
|
||||
case COP0_REG_XCONTEXT:
|
||||
return xcontext.raw;
|
||||
case COP0_REG_PARITY_ERR:
|
||||
return ParityError;
|
||||
case COP0_REG_CACHE_ERR:
|
||||
return CacheError;
|
||||
case COP0_REG_TAGLO:
|
||||
return TagLo;
|
||||
case COP0_REG_TAGHI:
|
||||
return TagHi;
|
||||
case COP0_REG_ERROREPC:
|
||||
return ErrorEPC;
|
||||
case 7:
|
||||
case 21:
|
||||
case 22:
|
||||
case 23:
|
||||
case 24:
|
||||
case 25:
|
||||
case 31:
|
||||
return openbus;
|
||||
default:
|
||||
Util::panic("Unsupported word read from COP0 register {}", addr);
|
||||
}
|
||||
}
|
||||
|
||||
u64 Cop0::GetReg64(u8 addr) const {
|
||||
switch(addr) {
|
||||
case COP0_REG_ENTRYLO0: return entryLo0.raw;
|
||||
case COP0_REG_ENTRYLO1: return entryLo1.raw;
|
||||
case COP0_REG_CONTEXT: return context.raw;
|
||||
case COP0_REG_BADVADDR: return badVaddr;
|
||||
case COP0_REG_ENTRYHI: return entryHi.raw;
|
||||
case COP0_REG_STATUS: return status.raw;
|
||||
case COP0_REG_EPC: return EPC;
|
||||
case COP0_REG_PRID: return PRId;
|
||||
case COP0_REG_LLADDR: return LLAddr;
|
||||
case COP0_REG_XCONTEXT: return xcontext.raw & 0xFFFFFFFFFFFFFFF0;
|
||||
case COP0_REG_ERROREPC: return ErrorEPC;
|
||||
case 7: case 21: case 22:
|
||||
case 23: case 24: case 25:
|
||||
case 31: return openbus;
|
||||
default:
|
||||
Util::panic("Unsupported dword read from COP0 register {}", addr);
|
||||
switch (addr) {
|
||||
case COP0_REG_ENTRYLO0:
|
||||
return entryLo0.raw;
|
||||
case COP0_REG_ENTRYLO1:
|
||||
return entryLo1.raw;
|
||||
case COP0_REG_CONTEXT:
|
||||
return context.raw;
|
||||
case COP0_REG_BADVADDR:
|
||||
return badVaddr;
|
||||
case COP0_REG_ENTRYHI:
|
||||
return entryHi.raw;
|
||||
case COP0_REG_STATUS:
|
||||
return status.raw;
|
||||
case COP0_REG_EPC:
|
||||
return EPC;
|
||||
case COP0_REG_PRID:
|
||||
return PRId;
|
||||
case COP0_REG_LLADDR:
|
||||
return LLAddr;
|
||||
case COP0_REG_XCONTEXT:
|
||||
return xcontext.raw & 0xFFFFFFFFFFFFFFF0;
|
||||
case COP0_REG_ERROREPC:
|
||||
return ErrorEPC;
|
||||
case 7:
|
||||
case 21:
|
||||
case 22:
|
||||
case 23:
|
||||
case 24:
|
||||
case 25:
|
||||
case 31:
|
||||
return openbus;
|
||||
default:
|
||||
Util::panic("Unsupported dword read from COP0 register {}", addr);
|
||||
}
|
||||
}
|
||||
|
||||
void Cop0::SetReg32(u8 addr, u32 value) {
|
||||
openbus = value & 0xFFFFFFFF;
|
||||
switch(addr) {
|
||||
case COP0_REG_INDEX: index.raw = value & INDEX_MASK; break;
|
||||
case COP0_REG_RANDOM: break;
|
||||
case COP0_REG_ENTRYLO0:
|
||||
entryLo0.raw = value & ENTRY_LO_MASK;
|
||||
break;
|
||||
case COP0_REG_ENTRYLO1:
|
||||
entryLo1.raw = value & ENTRY_LO_MASK;
|
||||
break;
|
||||
case COP0_REG_CONTEXT:
|
||||
context.raw = (s64(s32(value)) & 0xFFFFFFFFFF800000) | (context.raw & 0x7FFFFF);
|
||||
break;
|
||||
case COP0_REG_PAGEMASK: pageMask.raw = value & PAGEMASK_MASK; break;
|
||||
case COP0_REG_WIRED: wired = value & 63; break;
|
||||
case COP0_REG_BADVADDR: break;
|
||||
case COP0_REG_COUNT: count = (u64)value << 1; break;
|
||||
case COP0_REG_ENTRYHI:
|
||||
entryHi.raw = s64(s32(value)) & ENTRY_HI_MASK;
|
||||
break;
|
||||
case COP0_REG_COMPARE:
|
||||
compare = value;
|
||||
cause.ip7 = false;
|
||||
break;
|
||||
case COP0_REG_STATUS:
|
||||
status.raw &= ~STATUS_MASK;
|
||||
status.raw |= (value & STATUS_MASK);
|
||||
Update();
|
||||
break;
|
||||
case COP0_REG_CAUSE: {
|
||||
switch (addr) {
|
||||
case COP0_REG_INDEX:
|
||||
index.raw = value & INDEX_MASK;
|
||||
break;
|
||||
case COP0_REG_RANDOM:
|
||||
break;
|
||||
case COP0_REG_ENTRYLO0:
|
||||
entryLo0.raw = value & ENTRY_LO_MASK;
|
||||
break;
|
||||
case COP0_REG_ENTRYLO1:
|
||||
entryLo1.raw = value & ENTRY_LO_MASK;
|
||||
break;
|
||||
case COP0_REG_CONTEXT:
|
||||
context.raw = (s64(s32(value)) & 0xFFFFFFFFFF800000) | (context.raw & 0x7FFFFF);
|
||||
break;
|
||||
case COP0_REG_PAGEMASK:
|
||||
pageMask.raw = value & PAGEMASK_MASK;
|
||||
break;
|
||||
case COP0_REG_WIRED:
|
||||
wired = value & 63;
|
||||
break;
|
||||
case COP0_REG_BADVADDR:
|
||||
break;
|
||||
case COP0_REG_COUNT:
|
||||
count = (u64)value << 1;
|
||||
break;
|
||||
case COP0_REG_ENTRYHI:
|
||||
entryHi.raw = s64(s32(value)) & ENTRY_HI_MASK;
|
||||
break;
|
||||
case COP0_REG_COMPARE:
|
||||
compare = value;
|
||||
cause.ip7 = false;
|
||||
break;
|
||||
case COP0_REG_STATUS:
|
||||
status.raw &= ~STATUS_MASK;
|
||||
status.raw |= (value & STATUS_MASK);
|
||||
Update();
|
||||
break;
|
||||
case COP0_REG_CAUSE:
|
||||
{
|
||||
Cop0Cause tmp{};
|
||||
tmp.raw = value;
|
||||
cause.ip0 = tmp.ip0;
|
||||
cause.ip1 = tmp.ip1;
|
||||
} break;
|
||||
case COP0_REG_EPC:
|
||||
EPC = s64(s32(value));
|
||||
break;
|
||||
case COP0_REG_PRID: break;
|
||||
case COP0_REG_CONFIG: {
|
||||
Config &= ~CONFIG_MASK;
|
||||
Config |= (value & CONFIG_MASK);
|
||||
} break;
|
||||
case COP0_REG_LLADDR: LLAddr = value; break;
|
||||
case COP0_REG_WATCHLO: WatchLo = value; break;
|
||||
case COP0_REG_WATCHHI: WatchHi = value; break;
|
||||
case COP0_REG_XCONTEXT:
|
||||
xcontext.raw = (s64(s32(value)) & 0xFFFFFFFE00000000) | (xcontext.raw & 0x1FFFFFFFF);
|
||||
break;
|
||||
case COP0_REG_PARITY_ERR: ParityError = value & 0xff; break;
|
||||
case COP0_REG_CACHE_ERR: break;
|
||||
case COP0_REG_TAGLO: TagLo = value; break;
|
||||
case COP0_REG_TAGHI: TagHi = value; break;
|
||||
case COP0_REG_ERROREPC: ErrorEPC = s64(s32(value)); break;
|
||||
case 7: case 21: case 22:
|
||||
case 23: case 24: case 25:
|
||||
case 31: break;
|
||||
default:
|
||||
Util::panic("Unsupported word write from COP0 register {}", addr);
|
||||
}
|
||||
break;
|
||||
case COP0_REG_EPC:
|
||||
EPC = s64(s32(value));
|
||||
break;
|
||||
case COP0_REG_PRID:
|
||||
break;
|
||||
case COP0_REG_CONFIG:
|
||||
Config &= ~CONFIG_MASK;
|
||||
Config |= (value & CONFIG_MASK);
|
||||
break;
|
||||
case COP0_REG_LLADDR:
|
||||
LLAddr = value;
|
||||
break;
|
||||
case COP0_REG_WATCHLO:
|
||||
WatchLo = value;
|
||||
break;
|
||||
case COP0_REG_WATCHHI:
|
||||
WatchHi = value;
|
||||
break;
|
||||
case COP0_REG_XCONTEXT:
|
||||
xcontext.raw = (s64(s32(value)) & 0xFFFFFFFE00000000) | (xcontext.raw & 0x1FFFFFFFF);
|
||||
break;
|
||||
case COP0_REG_PARITY_ERR:
|
||||
ParityError = value & 0xff;
|
||||
break;
|
||||
case COP0_REG_CACHE_ERR:
|
||||
break;
|
||||
case COP0_REG_TAGLO:
|
||||
TagLo = value;
|
||||
break;
|
||||
case COP0_REG_TAGHI:
|
||||
TagHi = value;
|
||||
break;
|
||||
case COP0_REG_ERROREPC:
|
||||
ErrorEPC = s64(s32(value));
|
||||
break;
|
||||
case 7:
|
||||
case 21:
|
||||
case 22:
|
||||
case 23:
|
||||
case 24:
|
||||
case 25:
|
||||
case 31:
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unsupported word write from COP0 register {}", addr);
|
||||
}
|
||||
}
|
||||
|
||||
void Cop0::SetReg64(u8 addr, u64 value) {
|
||||
openbus = value;
|
||||
switch(addr) {
|
||||
case COP0_REG_ENTRYLO0: entryLo0.raw = value & ENTRY_LO_MASK; break;
|
||||
case COP0_REG_ENTRYLO1: entryLo1.raw = value & ENTRY_LO_MASK; break;
|
||||
case COP0_REG_CONTEXT:
|
||||
context.raw = (value & 0xFFFFFFFFFF800000) | (context.raw & 0x7FFFFF);
|
||||
break;
|
||||
case COP0_REG_XCONTEXT:
|
||||
xcontext.raw = (value & 0xFFFFFFFE00000000) | (xcontext.raw & 0x1FFFFFFFF);
|
||||
break;
|
||||
case COP0_REG_ENTRYHI: entryHi.raw = value & ENTRY_HI_MASK; break;
|
||||
case COP0_REG_STATUS: status.raw = value; break;
|
||||
case COP0_REG_CAUSE: {
|
||||
switch (addr) {
|
||||
case COP0_REG_ENTRYLO0:
|
||||
entryLo0.raw = value & ENTRY_LO_MASK;
|
||||
break;
|
||||
case COP0_REG_ENTRYLO1:
|
||||
entryLo1.raw = value & ENTRY_LO_MASK;
|
||||
break;
|
||||
case COP0_REG_CONTEXT:
|
||||
context.raw = (value & 0xFFFFFFFFFF800000) | (context.raw & 0x7FFFFF);
|
||||
break;
|
||||
case COP0_REG_XCONTEXT:
|
||||
xcontext.raw = (value & 0xFFFFFFFE00000000) | (xcontext.raw & 0x1FFFFFFFF);
|
||||
break;
|
||||
case COP0_REG_ENTRYHI:
|
||||
entryHi.raw = value & ENTRY_HI_MASK;
|
||||
break;
|
||||
case COP0_REG_STATUS:
|
||||
status.raw = value;
|
||||
break;
|
||||
case COP0_REG_CAUSE:
|
||||
{
|
||||
Cop0Cause tmp{};
|
||||
tmp.raw = value;
|
||||
cause.ip0 = tmp.ip0;
|
||||
cause.ip1 = tmp.ip1;
|
||||
} break;
|
||||
case COP0_REG_BADVADDR: break;
|
||||
case COP0_REG_EPC: EPC = (s64)value; break;
|
||||
case COP0_REG_LLADDR: LLAddr = value; break;
|
||||
case COP0_REG_ERROREPC: ErrorEPC = (s64)value; break;
|
||||
default:
|
||||
Util::panic("Unsupported dword write to COP0 register {}", addr);
|
||||
}
|
||||
break;
|
||||
case COP0_REG_BADVADDR:
|
||||
break;
|
||||
case COP0_REG_EPC:
|
||||
EPC = (s64)value;
|
||||
break;
|
||||
case COP0_REG_LLADDR:
|
||||
LLAddr = value;
|
||||
break;
|
||||
case COP0_REG_ERROREPC:
|
||||
ErrorEPC = (s64)value;
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unsupported dword write to COP0 register {}", addr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,10 +293,10 @@ static FORCE_INLINE u64 getVPN(u64 addr, u64 pageMask) {
|
||||
return vpn & ~mask;
|
||||
}
|
||||
|
||||
TLBEntry* Cop0::TLBTryMatch(u64 vaddr, int* match) {
|
||||
for(int i = 0; i < 32; i++) {
|
||||
TLBEntry *Cop0::TLBTryMatch(u64 vaddr, int *match) {
|
||||
for (int i = 0; i < 32; i++) {
|
||||
TLBEntry *entry = ®s.cop0.tlb[i];
|
||||
if(entry->initialized) {
|
||||
if (entry->initialized) {
|
||||
u64 entry_vpn = getVPN(entry->entryHi.raw, entry->pageMask.raw);
|
||||
u64 vaddr_vpn = getVPN(vaddr, entry->pageMask.raw);
|
||||
|
||||
@@ -221,9 +315,9 @@ TLBEntry* Cop0::TLBTryMatch(u64 vaddr, int* match) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Cop0::ProbeTLB(TLBAccessType access_type, u64 vaddr, u32& paddr, int* match) {
|
||||
TLBEntry* entry = TLBTryMatch(vaddr, match);
|
||||
if(!entry) {
|
||||
bool Cop0::ProbeTLB(TLBAccessType access_type, u64 vaddr, u32 &paddr, int *match) {
|
||||
TLBEntry *entry = TLBTryMatch(vaddr, match);
|
||||
if (!entry) {
|
||||
regs.cop0.tlbError = MISS;
|
||||
return false;
|
||||
}
|
||||
@@ -232,25 +326,25 @@ bool Cop0::ProbeTLB(TLBAccessType access_type, u64 vaddr, u32& paddr, int* match
|
||||
u32 odd = vaddr & (mask + 1);
|
||||
u32 pfn;
|
||||
|
||||
if(!odd) {
|
||||
if(!entry->entryLo0.v) {
|
||||
if (!odd) {
|
||||
if (!entry->entryLo0.v) {
|
||||
regs.cop0.tlbError = INVALID;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(access_type == STORE && !entry->entryLo0.d) {
|
||||
if (access_type == STORE && !entry->entryLo0.d) {
|
||||
regs.cop0.tlbError = MODIFICATION;
|
||||
return false;
|
||||
}
|
||||
|
||||
pfn = entry->entryLo0.pfn;
|
||||
} else {
|
||||
if(!entry->entryLo1.v) {
|
||||
if (!entry->entryLo1.v) {
|
||||
regs.cop0.tlbError = INVALID;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(access_type == STORE && !entry->entryLo1.d) {
|
||||
if (access_type == STORE && !entry->entryLo1.d) {
|
||||
regs.cop0.tlbError = MODIFICATION;
|
||||
return false;
|
||||
}
|
||||
@@ -263,21 +357,25 @@ bool Cop0::ProbeTLB(TLBAccessType access_type, u64 vaddr, u32& paddr, int* match
|
||||
return true;
|
||||
}
|
||||
|
||||
FORCE_INLINE bool Is64BitAddressing(Cop0& cp0, u64 addr) {
|
||||
FORCE_INLINE bool Is64BitAddressing(Cop0 &cp0, u64 addr) {
|
||||
u8 region = (addr >> 62) & 3;
|
||||
switch(region) {
|
||||
case 0b00: return cp0.status.ux;
|
||||
case 0b01: return cp0.status.sx;
|
||||
case 0b11: return cp0.status.kx;
|
||||
default: return false;
|
||||
switch (region) {
|
||||
case 0b00:
|
||||
return cp0.status.ux;
|
||||
case 0b01:
|
||||
return cp0.status.sx;
|
||||
case 0b11:
|
||||
return cp0.status.kx;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void Cop0::FireException(ExceptionCode code, int cop, s64 pc) {
|
||||
bool old_exl = regs.cop0.status.exl;
|
||||
|
||||
if(!regs.cop0.status.exl) {
|
||||
if((regs.cop0.cause.branchDelay = regs.prevDelaySlot)) {
|
||||
if (!regs.cop0.status.exl) {
|
||||
if ((regs.cop0.cause.branchDelay = regs.prevDelaySlot)) {
|
||||
pc -= 4;
|
||||
}
|
||||
|
||||
@@ -288,29 +386,38 @@ void Cop0::FireException(ExceptionCode code, int cop, s64 pc) {
|
||||
regs.cop0.cause.copError = cop;
|
||||
regs.cop0.cause.exceptionCode = static_cast<u8>(code);
|
||||
|
||||
if(regs.cop0.status.bev) {
|
||||
if (regs.cop0.status.bev) {
|
||||
Util::panic("BEV bit set!");
|
||||
} else {
|
||||
switch(code) {
|
||||
case ExceptionCode::Interrupt: case ExceptionCode::TLBModification:
|
||||
case ExceptionCode::AddressErrorLoad: case ExceptionCode::AddressErrorStore:
|
||||
case ExceptionCode::InstructionBusError: case ExceptionCode::DataBusError:
|
||||
case ExceptionCode::Syscall: case ExceptionCode::Breakpoint:
|
||||
case ExceptionCode::ReservedInstruction: case ExceptionCode::CoprocessorUnusable:
|
||||
case ExceptionCode::Overflow: case ExceptionCode::Trap:
|
||||
case ExceptionCode::FloatingPointError: case ExceptionCode::Watch:
|
||||
switch (code) {
|
||||
case ExceptionCode::Interrupt:
|
||||
case ExceptionCode::TLBModification:
|
||||
case ExceptionCode::AddressErrorLoad:
|
||||
case ExceptionCode::AddressErrorStore:
|
||||
case ExceptionCode::InstructionBusError:
|
||||
case ExceptionCode::DataBusError:
|
||||
case ExceptionCode::Syscall:
|
||||
case ExceptionCode::Breakpoint:
|
||||
case ExceptionCode::ReservedInstruction:
|
||||
case ExceptionCode::CoprocessorUnusable:
|
||||
case ExceptionCode::Overflow:
|
||||
case ExceptionCode::Trap:
|
||||
case ExceptionCode::FloatingPointError:
|
||||
case ExceptionCode::Watch:
|
||||
regs.SetPC32(s32(0x80000180));
|
||||
break;
|
||||
case ExceptionCode::TLBLoad:
|
||||
case ExceptionCode::TLBStore:
|
||||
if (old_exl || regs.cop0.tlbError == INVALID) {
|
||||
regs.SetPC32(s32(0x80000180));
|
||||
break;
|
||||
case ExceptionCode::TLBLoad: case ExceptionCode::TLBStore:
|
||||
if(old_exl || regs.cop0.tlbError == INVALID) {
|
||||
regs.SetPC32(s32(0x80000180));
|
||||
} else if(Is64BitAddressing(regs.cop0, regs.cop0.badVaddr)) {
|
||||
regs.SetPC32(s32(0x80000080));
|
||||
} else {
|
||||
regs.SetPC32(s32(0x80000000));
|
||||
}
|
||||
break;
|
||||
default: Util::panic("Unhandled exception! {}", static_cast<u8>(code));
|
||||
} else if (Is64BitAddressing(regs.cop0, regs.cop0.badVaddr)) {
|
||||
regs.SetPC32(s32(0x80000080));
|
||||
} else {
|
||||
regs.SetPC32(s32(0x80000000));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unhandled exception! {}", static_cast<u8>(code));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,63 +436,82 @@ void Cop0::HandleTLBException(u64 vaddr) {
|
||||
}
|
||||
|
||||
ExceptionCode Cop0::GetTLBExceptionCode(TLBError error, TLBAccessType accessType) {
|
||||
switch(error) {
|
||||
case NONE: Util::panic("Getting TLB exception with error NONE");
|
||||
case INVALID: case MISS:
|
||||
return accessType == LOAD ?
|
||||
ExceptionCode::TLBLoad : ExceptionCode::TLBStore;
|
||||
case MODIFICATION:
|
||||
return ExceptionCode::TLBModification;
|
||||
case DISALLOWED_ADDRESS:
|
||||
return accessType == LOAD ?
|
||||
ExceptionCode::AddressErrorLoad : ExceptionCode::AddressErrorStore;
|
||||
default:
|
||||
Util::panic("Getting TLB exception for unknown error code! ({})", static_cast<u8>(error));
|
||||
switch (error) {
|
||||
case NONE:
|
||||
Util::panic("Getting TLB exception with error NONE");
|
||||
case INVALID:
|
||||
case MISS:
|
||||
return accessType == LOAD ? ExceptionCode::TLBLoad : ExceptionCode::TLBStore;
|
||||
case MODIFICATION:
|
||||
return ExceptionCode::TLBModification;
|
||||
case DISALLOWED_ADDRESS:
|
||||
return accessType == LOAD ? ExceptionCode::AddressErrorLoad : ExceptionCode::AddressErrorStore;
|
||||
default:
|
||||
Util::panic("Getting TLB exception for unknown error code! ({})", static_cast<u8>(error));
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void Cop0::decode(T& cpu, u32 instr) {
|
||||
if constexpr (std::is_same_v<decltype(cpu), Interpreter&>) {
|
||||
template <class T>
|
||||
void Cop0::decode(T &cpu, u32 instr) {
|
||||
if constexpr (std::is_same_v<decltype(cpu), Interpreter &>) {
|
||||
decodeInterp(instr);
|
||||
} else if constexpr (std::is_same_v<decltype(cpu), JIT&>) {
|
||||
} else if constexpr (std::is_same_v<decltype(cpu), JIT &>) {
|
||||
decodeJIT(cpu, instr);
|
||||
} else {
|
||||
Util::panic("What the fuck did you just give me?!!");
|
||||
}
|
||||
}
|
||||
|
||||
template void Cop0::decode<Interpreter>(Interpreter&, u32);
|
||||
template void Cop0::decode<JIT>(JIT&, u32);
|
||||
template void Cop0::decode<Interpreter>(Interpreter &, u32);
|
||||
template void Cop0::decode<JIT>(JIT &, u32);
|
||||
|
||||
void Cop0::decodeJIT(JIT& cpu, u32 instr) {
|
||||
|
||||
}
|
||||
void Cop0::decodeJIT(JIT &cpu, u32 instr) {}
|
||||
|
||||
void Cop0::decodeInterp(u32 instr) {
|
||||
u8 mask_cop = (instr >> 21) & 0x1F;
|
||||
u8 mask_cop2 = instr & 0x3F;
|
||||
switch(mask_cop) {
|
||||
case 0x00: mfc0(instr); break;
|
||||
case 0x01: dmfc0(instr); break;
|
||||
case 0x04: mtc0(instr); break;
|
||||
case 0x05: dmtc0(instr); break;
|
||||
case 0x10 ... 0x1F:
|
||||
switch(mask_cop2) {
|
||||
case 0x01: tlbr(); break;
|
||||
case 0x02: tlbw(index.i); break;
|
||||
case 0x06: tlbw(GetRandom()); break;
|
||||
case 0x08: tlbp(); break;
|
||||
case 0x18: eret(); break;
|
||||
default: Util::panic("Unimplemented COP0 function {} {} ({:08X}) ({:016lX})", mask_cop2 >> 3, mask_cop2 & 7, instr, regs.oldPC);
|
||||
}
|
||||
switch (mask_cop) {
|
||||
case 0x00:
|
||||
mfc0(instr);
|
||||
break;
|
||||
case 0x01:
|
||||
dmfc0(instr);
|
||||
break;
|
||||
case 0x04:
|
||||
mtc0(instr);
|
||||
break;
|
||||
case 0x05:
|
||||
dmtc0(instr);
|
||||
break;
|
||||
case 0x10 ... 0x1F:
|
||||
switch (mask_cop2) {
|
||||
case 0x01:
|
||||
tlbr();
|
||||
break;
|
||||
default: Util::panic("Unimplemented COP0 instruction {} {}", mask_cop >> 4, mask_cop & 7);
|
||||
case 0x02:
|
||||
tlbw(index.i);
|
||||
break;
|
||||
case 0x06:
|
||||
tlbw(GetRandom());
|
||||
break;
|
||||
case 0x08:
|
||||
tlbp();
|
||||
break;
|
||||
case 0x18:
|
||||
eret();
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unimplemented COP0 function {} {} ({:08X}) ({:016lX})", mask_cop2 >> 3, mask_cop2 & 7, instr,
|
||||
regs.oldPC);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unimplemented COP0 instruction {} {}", mask_cop >> 4, mask_cop & 7);
|
||||
}
|
||||
}
|
||||
|
||||
bool Cop0::MapVAddr(TLBAccessType accessType, u64 vaddr, u32& paddr) {
|
||||
if(regs.cop0.is64BitAddressing) [[unlikely]] {
|
||||
bool Cop0::MapVAddr(TLBAccessType accessType, u64 vaddr, u32 &paddr) {
|
||||
if (regs.cop0.is64BitAddressing) [[unlikely]] {
|
||||
if (regs.cop0.kernelMode) [[likely]] {
|
||||
return MapVAddr64(accessType, vaddr, paddr);
|
||||
} else if (regs.cop0.userMode) {
|
||||
@@ -408,46 +534,50 @@ bool Cop0::MapVAddr(TLBAccessType accessType, u64 vaddr, u32& paddr) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Cop0::UserMapVAddr32(TLBAccessType accessType, u64 vaddr, u32& paddr) {
|
||||
bool Cop0::UserMapVAddr32(TLBAccessType accessType, u64 vaddr, u32 &paddr) {
|
||||
switch (vaddr) {
|
||||
case VREGION_KUSEG:
|
||||
return ProbeTLB(accessType, s64(s32(vaddr)), paddr, nullptr);
|
||||
default:
|
||||
regs.cop0.tlbError = DISALLOWED_ADDRESS;
|
||||
return false;
|
||||
case VREGION_KUSEG:
|
||||
return ProbeTLB(accessType, s64(s32(vaddr)), paddr, nullptr);
|
||||
default:
|
||||
regs.cop0.tlbError = DISALLOWED_ADDRESS;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Cop0::MapVAddr32(TLBAccessType accessType, u64 vaddr, u32& paddr) {
|
||||
switch((u32(vaddr) >> 29) & 7) {
|
||||
case 0 ... 3: case 7:
|
||||
return ProbeTLB(accessType, s64(s32(vaddr)), paddr, nullptr);
|
||||
case 4 ... 5:
|
||||
paddr = vaddr & 0x1FFFFFFF;
|
||||
return true;
|
||||
case 6: Util::panic("Unimplemented virtual mapping in KSSEG! ({:08X})", vaddr);
|
||||
default:
|
||||
Util::panic("Should never end up in default case in map_vaddr! ({:08X})", vaddr);
|
||||
bool Cop0::MapVAddr32(TLBAccessType accessType, u64 vaddr, u32 &paddr) {
|
||||
switch ((u32(vaddr) >> 29) & 7) {
|
||||
case 0 ... 3:
|
||||
case 7:
|
||||
return ProbeTLB(accessType, s64(s32(vaddr)), paddr, nullptr);
|
||||
case 4 ... 5:
|
||||
paddr = vaddr & 0x1FFFFFFF;
|
||||
return true;
|
||||
case 6:
|
||||
Util::panic("Unimplemented virtual mapping in KSSEG! ({:08X})", vaddr);
|
||||
default:
|
||||
Util::panic("Should never end up in default case in map_vaddr! ({:08X})", vaddr);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Cop0::UserMapVAddr64(TLBAccessType accessType, u64 vaddr, u32& paddr) {
|
||||
bool Cop0::UserMapVAddr64(TLBAccessType accessType, u64 vaddr, u32 &paddr) {
|
||||
switch (vaddr) {
|
||||
case VREGION_XKUSEG:
|
||||
return ProbeTLB(accessType, vaddr, paddr, nullptr);
|
||||
default:
|
||||
regs.cop0.tlbError = DISALLOWED_ADDRESS;
|
||||
return false;
|
||||
case VREGION_XKUSEG:
|
||||
return ProbeTLB(accessType, vaddr, paddr, nullptr);
|
||||
default:
|
||||
regs.cop0.tlbError = DISALLOWED_ADDRESS;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Cop0::MapVAddr64(TLBAccessType accessType, u64 vaddr, u32& paddr) {
|
||||
bool Cop0::MapVAddr64(TLBAccessType accessType, u64 vaddr, u32 &paddr) {
|
||||
switch (vaddr) {
|
||||
case VREGION_XKUSEG: case VREGION_XKSSEG:
|
||||
return ProbeTLB(accessType, vaddr, paddr, nullptr);
|
||||
case VREGION_XKPHYS: {
|
||||
case VREGION_XKUSEG:
|
||||
case VREGION_XKSSEG:
|
||||
return ProbeTLB(accessType, vaddr, paddr, nullptr);
|
||||
case VREGION_XKPHYS:
|
||||
{
|
||||
if (!regs.cop0.kernelMode) {
|
||||
Util::panic("Access to XKPHYS address 0x{:016X} when outside kernel mode!", vaddr);
|
||||
}
|
||||
@@ -466,33 +596,33 @@ bool Cop0::MapVAddr64(TLBAccessType accessType, u64 vaddr, u32& paddr) {
|
||||
paddr = vaddr & 0xFFFFFFFF;
|
||||
return true;
|
||||
}
|
||||
case VREGION_XKSEG:
|
||||
return ProbeTLB(accessType, vaddr, paddr, nullptr);
|
||||
case VREGION_CKSEG0:
|
||||
// Identical to kseg0 in 32 bit mode.
|
||||
// Unmapped translation. Subtract the base address of the space to get the physical address.
|
||||
paddr = vaddr - START_VREGION_KSEG0; // Implies cutting off the high 32 bits
|
||||
Util::trace("CKSEG0: Translated 0x{:016X} to 0x{:08X}", vaddr, paddr);
|
||||
return true;
|
||||
case VREGION_CKSEG1:
|
||||
// Identical to kseg1 in 32 bit mode.
|
||||
// Unmapped translation. Subtract the base address of the space to get the physical address.
|
||||
paddr = vaddr - START_VREGION_KSEG1; // Implies cutting off the high 32 bits
|
||||
Util::trace("KSEG1: Translated 0x{:016X} to 0x{:08X}", vaddr, paddr);
|
||||
return true;
|
||||
case VREGION_CKSSEG:
|
||||
Util::panic("Resolving virtual address 0x{:016X} (VREGION_CKSSEG) in 64 bit mode", vaddr);
|
||||
case VREGION_CKSEG3:
|
||||
return ProbeTLB(accessType, vaddr, paddr, nullptr);
|
||||
case VREGION_XBAD1:
|
||||
case VREGION_XBAD2:
|
||||
case VREGION_XBAD3:
|
||||
regs.cop0.tlbError = DISALLOWED_ADDRESS;
|
||||
return false;
|
||||
default:
|
||||
Util::panic("Resolving virtual address 0x{:016X} in 64 bit mode", vaddr);
|
||||
case VREGION_XKSEG:
|
||||
return ProbeTLB(accessType, vaddr, paddr, nullptr);
|
||||
case VREGION_CKSEG0:
|
||||
// Identical to kseg0 in 32 bit mode.
|
||||
// Unmapped translation. Subtract the base address of the space to get the physical address.
|
||||
paddr = vaddr - START_VREGION_KSEG0; // Implies cutting off the high 32 bits
|
||||
Util::trace("CKSEG0: Translated 0x{:016X} to 0x{:08X}", vaddr, paddr);
|
||||
return true;
|
||||
case VREGION_CKSEG1:
|
||||
// Identical to kseg1 in 32 bit mode.
|
||||
// Unmapped translation. Subtract the base address of the space to get the physical address.
|
||||
paddr = vaddr - START_VREGION_KSEG1; // Implies cutting off the high 32 bits
|
||||
Util::trace("KSEG1: Translated 0x{:016X} to 0x{:08X}", vaddr, paddr);
|
||||
return true;
|
||||
case VREGION_CKSSEG:
|
||||
Util::panic("Resolving virtual address 0x{:016X} (VREGION_CKSSEG) in 64 bit mode", vaddr);
|
||||
case VREGION_CKSEG3:
|
||||
return ProbeTLB(accessType, vaddr, paddr, nullptr);
|
||||
case VREGION_XBAD1:
|
||||
case VREGION_XBAD2:
|
||||
case VREGION_XBAD3:
|
||||
regs.cop0.tlbError = DISALLOWED_ADDRESS;
|
||||
return false;
|
||||
default:
|
||||
Util::panic("Resolving virtual address 0x{:016X} in 64 bit mode", vaddr);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -44,60 +44,60 @@ struct Mem;
|
||||
union Cop0Cause {
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned: 8;
|
||||
unsigned interruptPending: 8;
|
||||
unsigned: 16;
|
||||
unsigned : 8;
|
||||
unsigned interruptPending : 8;
|
||||
unsigned : 16;
|
||||
} __attribute__((__packed__));
|
||||
struct {
|
||||
unsigned: 2;
|
||||
unsigned exceptionCode: 5;
|
||||
unsigned: 1;
|
||||
unsigned ip0: 1;
|
||||
unsigned ip1: 1;
|
||||
unsigned ip2: 1;
|
||||
unsigned ip3: 1;
|
||||
unsigned ip4: 1;
|
||||
unsigned ip5: 1;
|
||||
unsigned ip6: 1;
|
||||
unsigned ip7: 1;
|
||||
unsigned: 12;
|
||||
unsigned copError: 2;
|
||||
unsigned: 1;
|
||||
unsigned branchDelay: 1;
|
||||
unsigned : 2;
|
||||
unsigned exceptionCode : 5;
|
||||
unsigned : 1;
|
||||
unsigned ip0 : 1;
|
||||
unsigned ip1 : 1;
|
||||
unsigned ip2 : 1;
|
||||
unsigned ip3 : 1;
|
||||
unsigned ip4 : 1;
|
||||
unsigned ip5 : 1;
|
||||
unsigned ip6 : 1;
|
||||
unsigned ip7 : 1;
|
||||
unsigned : 12;
|
||||
unsigned copError : 2;
|
||||
unsigned : 1;
|
||||
unsigned branchDelay : 1;
|
||||
} __attribute__((__packed__));
|
||||
};
|
||||
|
||||
union Cop0Status {
|
||||
struct {
|
||||
unsigned ie: 1;
|
||||
unsigned exl: 1;
|
||||
unsigned erl: 1;
|
||||
unsigned ksu: 2;
|
||||
unsigned ux: 1;
|
||||
unsigned sx: 1;
|
||||
unsigned kx: 1;
|
||||
unsigned im: 8;
|
||||
unsigned ds: 9;
|
||||
unsigned re: 1;
|
||||
unsigned fr: 1;
|
||||
unsigned rp: 1;
|
||||
unsigned cu0: 1;
|
||||
unsigned cu1: 1;
|
||||
unsigned cu2: 1;
|
||||
unsigned cu3: 1;
|
||||
unsigned ie : 1;
|
||||
unsigned exl : 1;
|
||||
unsigned erl : 1;
|
||||
unsigned ksu : 2;
|
||||
unsigned ux : 1;
|
||||
unsigned sx : 1;
|
||||
unsigned kx : 1;
|
||||
unsigned im : 8;
|
||||
unsigned ds : 9;
|
||||
unsigned re : 1;
|
||||
unsigned fr : 1;
|
||||
unsigned rp : 1;
|
||||
unsigned cu0 : 1;
|
||||
unsigned cu1 : 1;
|
||||
unsigned cu2 : 1;
|
||||
unsigned cu3 : 1;
|
||||
} __attribute__((__packed__));
|
||||
struct {
|
||||
unsigned: 16;
|
||||
unsigned de: 1;
|
||||
unsigned ce: 1;
|
||||
unsigned ch: 1;
|
||||
unsigned: 1;
|
||||
unsigned sr: 1;
|
||||
unsigned ts: 1;
|
||||
unsigned bev: 1;
|
||||
unsigned: 1;
|
||||
unsigned its: 1;
|
||||
unsigned: 7;
|
||||
unsigned : 16;
|
||||
unsigned de : 1;
|
||||
unsigned ce : 1;
|
||||
unsigned ch : 1;
|
||||
unsigned : 1;
|
||||
unsigned sr : 1;
|
||||
unsigned ts : 1;
|
||||
unsigned bev : 1;
|
||||
unsigned : 1;
|
||||
unsigned its : 1;
|
||||
unsigned : 7;
|
||||
} __attribute__((__packed__));
|
||||
u32 raw;
|
||||
} __attribute__((__packed__));
|
||||
@@ -105,41 +105,41 @@ union Cop0Status {
|
||||
union EntryLo {
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned g:1;
|
||||
unsigned v:1;
|
||||
unsigned d:1;
|
||||
unsigned c:3;
|
||||
unsigned pfn:20;
|
||||
unsigned:6;
|
||||
unsigned g : 1;
|
||||
unsigned v : 1;
|
||||
unsigned d : 1;
|
||||
unsigned c : 3;
|
||||
unsigned pfn : 20;
|
||||
unsigned : 6;
|
||||
};
|
||||
};
|
||||
|
||||
union EntryHi {
|
||||
u64 raw;
|
||||
struct {
|
||||
u64 asid:8;
|
||||
u64:5;
|
||||
u64 vpn2:27;
|
||||
u64 fill:22;
|
||||
u64 r:2;
|
||||
u64 asid : 8;
|
||||
u64 : 5;
|
||||
u64 vpn2 : 27;
|
||||
u64 fill : 22;
|
||||
u64 r : 2;
|
||||
} __attribute__((__packed__));
|
||||
};
|
||||
|
||||
union PageMask {
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned: 13;
|
||||
unsigned mask: 12;
|
||||
unsigned: 7;
|
||||
unsigned : 13;
|
||||
unsigned mask : 12;
|
||||
unsigned : 7;
|
||||
};
|
||||
};
|
||||
|
||||
union Index {
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned i:6;
|
||||
unsigned:25;
|
||||
unsigned p:1;
|
||||
unsigned i : 6;
|
||||
unsigned : 25;
|
||||
unsigned p : 1;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -148,12 +148,12 @@ struct TLBEntry {
|
||||
union {
|
||||
u32 raw;
|
||||
struct {
|
||||
unsigned:1;
|
||||
unsigned v:1;
|
||||
unsigned d:1;
|
||||
unsigned c:3;
|
||||
unsigned pfn:20;
|
||||
unsigned:6;
|
||||
unsigned : 1;
|
||||
unsigned v : 1;
|
||||
unsigned d : 1;
|
||||
unsigned c : 3;
|
||||
unsigned pfn : 20;
|
||||
unsigned : 6;
|
||||
};
|
||||
} entryLo0, entryLo1;
|
||||
EntryHi entryHi;
|
||||
@@ -162,13 +162,7 @@ struct TLBEntry {
|
||||
bool global;
|
||||
};
|
||||
|
||||
enum TLBError : u8 {
|
||||
NONE,
|
||||
MISS,
|
||||
INVALID,
|
||||
MODIFICATION,
|
||||
DISALLOWED_ADDRESS
|
||||
};
|
||||
enum TLBError : u8 { NONE, MISS, INVALID, MODIFICATION, DISALLOWED_ADDRESS };
|
||||
|
||||
enum class ExceptionCode : u8 {
|
||||
Interrupt = 0,
|
||||
@@ -192,24 +186,24 @@ enum class ExceptionCode : u8 {
|
||||
union Cop0Context {
|
||||
u64 raw;
|
||||
struct {
|
||||
u64: 4;
|
||||
u64 badvpn2: 19;
|
||||
u64 ptebase: 41;
|
||||
u64 : 4;
|
||||
u64 badvpn2 : 19;
|
||||
u64 ptebase : 41;
|
||||
};
|
||||
};
|
||||
|
||||
union Cop0XContext {
|
||||
u64 raw;
|
||||
struct {
|
||||
u64: 4;
|
||||
u64 badvpn2: 27;
|
||||
u64 r: 2;
|
||||
u64 ptebase: 31;
|
||||
u64 : 4;
|
||||
u64 badvpn2 : 27;
|
||||
u64 r : 2;
|
||||
u64 ptebase : 31;
|
||||
} __attribute__((__packed__));
|
||||
};
|
||||
|
||||
struct Cop0 {
|
||||
Cop0(Registers&);
|
||||
Cop0(Registers &);
|
||||
|
||||
u32 GetReg32(u8);
|
||||
[[nodiscard]] u64 GetReg64(u8) const;
|
||||
@@ -245,12 +239,12 @@ struct Cop0 {
|
||||
TLBError tlbError = NONE;
|
||||
s64 openbus{};
|
||||
template <class T>
|
||||
void decode(T&, u32);
|
||||
void decode(T &, u32);
|
||||
FORCE_INLINE u32 GetRandom() {
|
||||
u32 val = rand();
|
||||
auto wired_ = GetWired();
|
||||
u32 lower, upper;
|
||||
if(wired_ > 31) {
|
||||
if (wired_ > 31) {
|
||||
lower = 0;
|
||||
upper = 64;
|
||||
} else {
|
||||
@@ -265,37 +259,33 @@ struct Cop0 {
|
||||
FORCE_INLINE void Update() {
|
||||
bool exception = status.exl || status.erl;
|
||||
|
||||
kernelMode = exception || status.ksu == 0;
|
||||
kernelMode = exception || status.ksu == 0;
|
||||
supervisorMode = !exception && status.ksu == 1;
|
||||
userMode = !exception && status.ksu == 2;
|
||||
is64BitAddressing =
|
||||
(kernelMode && status.kx)
|
||||
|| (supervisorMode && status.sx)
|
||||
|| (userMode && status.ux);
|
||||
userMode = !exception && status.ksu == 2;
|
||||
is64BitAddressing = (kernelMode && status.kx) || (supervisorMode && status.sx) || (userMode && status.ux);
|
||||
}
|
||||
|
||||
enum TLBAccessType {
|
||||
LOAD, STORE
|
||||
};
|
||||
enum TLBAccessType { LOAD, STORE };
|
||||
|
||||
bool ProbeTLB(TLBAccessType access_type, u64 vaddr, u32& paddr, int* match);
|
||||
bool ProbeTLB(TLBAccessType access_type, u64 vaddr, u32 &paddr, int *match);
|
||||
void FireException(ExceptionCode code, int cop, s64 pc);
|
||||
bool MapVAddr(TLBAccessType accessType, u64 vaddr, u32& paddr);
|
||||
bool UserMapVAddr32(TLBAccessType accessType, u64 vaddr, u32& paddr);
|
||||
bool MapVAddr32(TLBAccessType accessType, u64 vaddr, u32& paddr);
|
||||
bool UserMapVAddr64(TLBAccessType accessType, u64 vaddr, u32& paddr);
|
||||
bool MapVAddr64(TLBAccessType accessType, u64 vaddr, u32& paddr);
|
||||
bool MapVAddr(TLBAccessType accessType, u64 vaddr, u32 &paddr);
|
||||
bool UserMapVAddr32(TLBAccessType accessType, u64 vaddr, u32 &paddr);
|
||||
bool MapVAddr32(TLBAccessType accessType, u64 vaddr, u32 &paddr);
|
||||
bool UserMapVAddr64(TLBAccessType accessType, u64 vaddr, u32 &paddr);
|
||||
bool MapVAddr64(TLBAccessType accessType, u64 vaddr, u32 &paddr);
|
||||
|
||||
TLBEntry* TLBTryMatch(u64 vaddr, int* match);
|
||||
TLBEntry *TLBTryMatch(u64 vaddr, int *match);
|
||||
void HandleTLBException(u64 vaddr);
|
||||
ExceptionCode GetTLBExceptionCode(TLBError error, TLBAccessType access_type);
|
||||
|
||||
private:
|
||||
Registers& regs;
|
||||
Registers ®s;
|
||||
[[nodiscard]] FORCE_INLINE u32 GetWired() const { return wired & 0x3F; }
|
||||
[[nodiscard]] FORCE_INLINE u32 GetCount() const { return u32(u64(count >> 1)); }
|
||||
|
||||
void decodeInterp(u32);
|
||||
void decodeJIT(JIT&, u32);
|
||||
void decodeJIT(JIT &, u32);
|
||||
void mtc0(u32);
|
||||
void dmtc0(u32);
|
||||
void mfc0(u32);
|
||||
@@ -306,4 +296,4 @@ private:
|
||||
void tlbw(int);
|
||||
void tlbp();
|
||||
};
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
#include <core/Interpreter.hpp>
|
||||
#include <core/registers/Cop1.hpp>
|
||||
#include <core/registers/Registers.hpp>
|
||||
#include <core/Interpreter.hpp>
|
||||
#include <log.hpp>
|
||||
|
||||
namespace n64 {
|
||||
Cop1::Cop1(Registers& regs) : regs(regs) {
|
||||
Reset();
|
||||
}
|
||||
Cop1::Cop1(Registers ®s) : regs(regs) { Reset(); }
|
||||
|
||||
void Cop1::Reset() {
|
||||
fcr0 = 0xa00;
|
||||
@@ -15,136 +13,322 @@ void Cop1::Reset() {
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void Cop1::decode(T& cpu, u32 instr) {
|
||||
if constexpr (std::is_same_v<decltype(cpu), Interpreter&>) {
|
||||
void Cop1::decode(T &cpu, u32 instr) {
|
||||
if constexpr (std::is_same_v<decltype(cpu), Interpreter &>) {
|
||||
decodeInterp(cpu, instr);
|
||||
} else {
|
||||
Util::panic("What the fuck did you just give me?!");
|
||||
}
|
||||
}
|
||||
|
||||
template void Cop1::decode<Interpreter>(Interpreter&, u32);
|
||||
template void Cop1::decode<JIT>(JIT&, u32);
|
||||
template void Cop1::decode<Interpreter>(Interpreter &, u32);
|
||||
template void Cop1::decode<JIT>(JIT &, u32);
|
||||
|
||||
void Cop1::decodeInterp(Interpreter &cpu, u32 instr) {
|
||||
|
||||
u8 mask_sub = (instr >> 21) & 0x1F;
|
||||
u8 mask_fun = instr & 0x3F;
|
||||
u8 mask_branch = (instr >> 16) & 0x1F;
|
||||
switch(mask_sub) {
|
||||
// 000r_rccc
|
||||
case 0x00: mfc1(instr); break;
|
||||
case 0x01: dmfc1(instr); break;
|
||||
case 0x02: cfc1(instr); break;
|
||||
case 0x03: unimplemented(); break;
|
||||
case 0x04: mtc1(instr); break;
|
||||
case 0x05: dmtc1(instr); break;
|
||||
case 0x06: ctc1(instr); break;
|
||||
case 0x07: unimplemented(); break;
|
||||
switch (mask_sub) {
|
||||
// 000r_rccc
|
||||
case 0x00:
|
||||
mfc1(instr);
|
||||
break;
|
||||
case 0x01:
|
||||
dmfc1(instr);
|
||||
break;
|
||||
case 0x02:
|
||||
cfc1(instr);
|
||||
break;
|
||||
case 0x03:
|
||||
unimplemented();
|
||||
break;
|
||||
case 0x04:
|
||||
mtc1(instr);
|
||||
break;
|
||||
case 0x05:
|
||||
dmtc1(instr);
|
||||
break;
|
||||
case 0x06:
|
||||
ctc1(instr);
|
||||
break;
|
||||
case 0x07:
|
||||
unimplemented();
|
||||
break;
|
||||
case 0x08:
|
||||
switch (mask_branch) {
|
||||
case 0:
|
||||
if (!CheckFPUUsable())
|
||||
return;
|
||||
cpu.b(instr, !fcr31.compare);
|
||||
break;
|
||||
case 1:
|
||||
if (!CheckFPUUsable())
|
||||
return;
|
||||
cpu.b(instr, fcr31.compare);
|
||||
break;
|
||||
case 2:
|
||||
if (!CheckFPUUsable())
|
||||
return;
|
||||
cpu.bl(instr, !fcr31.compare);
|
||||
break;
|
||||
case 3:
|
||||
if (!CheckFPUUsable())
|
||||
return;
|
||||
cpu.bl(instr, fcr31.compare);
|
||||
break;
|
||||
default:
|
||||
Util::panic("Undefined BC COP1 {:02X}", mask_branch);
|
||||
}
|
||||
break;
|
||||
case 0x10: // s
|
||||
switch (mask_fun) {
|
||||
case 0x00:
|
||||
adds(instr);
|
||||
break;
|
||||
case 0x01:
|
||||
subs(instr);
|
||||
break;
|
||||
case 0x02:
|
||||
muls(instr);
|
||||
break;
|
||||
case 0x03:
|
||||
divs(instr);
|
||||
break;
|
||||
case 0x04:
|
||||
sqrts(instr);
|
||||
break;
|
||||
case 0x05:
|
||||
abss(instr);
|
||||
break;
|
||||
case 0x06:
|
||||
movs(instr);
|
||||
break;
|
||||
case 0x07:
|
||||
negs(instr);
|
||||
break;
|
||||
case 0x08:
|
||||
switch(mask_branch) {
|
||||
case 0: if(!CheckFPUUsable()) return; cpu.b(instr, !fcr31.compare); break;
|
||||
case 1: if(!CheckFPUUsable()) return; cpu.b(instr, fcr31.compare); break;
|
||||
case 2: if(!CheckFPUUsable()) return; cpu.bl(instr, !fcr31.compare); break;
|
||||
case 3: if(!CheckFPUUsable()) return; cpu.bl(instr, fcr31.compare); break;
|
||||
default: Util::panic("Undefined BC COP1 {:02X}", mask_branch);
|
||||
}
|
||||
roundls(instr);
|
||||
break;
|
||||
case 0x10: // s
|
||||
switch(mask_fun) {
|
||||
case 0x00: adds(instr); break;
|
||||
case 0x01: subs(instr); break;
|
||||
case 0x02: muls(instr); break;
|
||||
case 0x03: divs(instr); break;
|
||||
case 0x04: sqrts(instr); break;
|
||||
case 0x05: abss(instr); break;
|
||||
case 0x06: movs(instr); break;
|
||||
case 0x07: negs(instr); break;
|
||||
case 0x08: roundls(instr); break;
|
||||
case 0x09: truncls(instr); break;
|
||||
case 0x0A: ceills(instr); break;
|
||||
case 0x0B: floorls(instr); break;
|
||||
case 0x0C: roundws(instr); break;
|
||||
case 0x0D: truncws(instr); break;
|
||||
case 0x0E: ceilws(instr); break;
|
||||
case 0x0F: floorws(instr); break;
|
||||
case 0x21: cvtds(instr); break;
|
||||
case 0x24: cvtws(instr); break;
|
||||
case 0x25: cvtls(instr); break;
|
||||
case 0x30: cf<float>(instr); break;
|
||||
case 0x31: cun<float>(instr); break;
|
||||
case 0x32: ceq<float>(instr); break;
|
||||
case 0x33: cueq<float>(instr); break;
|
||||
case 0x34: colt<float>(instr); break;
|
||||
case 0x35: cult<float>(instr); break;
|
||||
case 0x36: cole<float>(instr); break;
|
||||
case 0x37: cule<float>(instr); break;
|
||||
case 0x38: csf<float>(instr); break;
|
||||
case 0x39: cngle<float>(instr); break;
|
||||
case 0x3A: cseq<float>(instr); break;
|
||||
case 0x3B: cngl<float>(instr); break;
|
||||
case 0x3C: clt<float>(instr); break;
|
||||
case 0x3D: cnge<float>(instr); break;
|
||||
case 0x3E: cle<float>(instr); break;
|
||||
case 0x3F: cngt<float>(instr); break;
|
||||
default: unimplemented();
|
||||
}
|
||||
case 0x09:
|
||||
truncls(instr);
|
||||
break;
|
||||
case 0x11: // d
|
||||
switch(mask_fun) {
|
||||
case 0x00: addd(instr); break;
|
||||
case 0x01: subd(instr); break;
|
||||
case 0x02: muld(instr); break;
|
||||
case 0x03: divd(instr); break;
|
||||
case 0x04: sqrtd(instr); break;
|
||||
case 0x05: absd(instr); break;
|
||||
case 0x06: movd(instr); break;
|
||||
case 0x07: negd(instr); break;
|
||||
case 0x08: roundld(instr); break;
|
||||
case 0x09: truncld(instr); break;
|
||||
case 0x0A: ceilld(instr); break;
|
||||
case 0x0B: floorld(instr); break;
|
||||
case 0x0C: roundwd(instr); break;
|
||||
case 0x0D: truncwd(instr); break;
|
||||
case 0x0E: ceilwd(instr); break;
|
||||
case 0x0F: floorwd(instr); break;
|
||||
case 0x20: cvtsd(instr); break;
|
||||
case 0x24: cvtwd(instr); break;
|
||||
case 0x25: cvtld(instr); break;
|
||||
case 0x30: cf<double>(instr); break;
|
||||
case 0x31: cun<double>(instr); break;
|
||||
case 0x32: ceq<double>(instr); break;
|
||||
case 0x33: cueq<double>(instr); break;
|
||||
case 0x34: colt<double>(instr); break;
|
||||
case 0x35: cult<double>(instr); break;
|
||||
case 0x36: cole<double>(instr); break;
|
||||
case 0x37: cule<double>(instr); break;
|
||||
case 0x38: csf<double>(instr); break;
|
||||
case 0x39: cngle<double>(instr); break;
|
||||
case 0x3A: cseq<double>(instr); break;
|
||||
case 0x3B: cngl<double>(instr); break;
|
||||
case 0x3C: clt<double>(instr); break;
|
||||
case 0x3D: cnge<double>(instr); break;
|
||||
case 0x3E: cle<double>(instr); break;
|
||||
case 0x3F: cngt<double>(instr); break;
|
||||
default: unimplemented();
|
||||
}
|
||||
case 0x0A:
|
||||
ceills(instr);
|
||||
break;
|
||||
case 0x14: // w
|
||||
switch(mask_fun) {
|
||||
case 0x20: cvtsw(instr); break;
|
||||
case 0x21: cvtdw(instr); break;
|
||||
default: unimplemented();
|
||||
}
|
||||
case 0x0B:
|
||||
floorls(instr);
|
||||
break;
|
||||
case 0x15: // l
|
||||
switch(mask_fun) {
|
||||
case 0x20: cvtsl(instr); break;
|
||||
case 0x21: cvtdl(instr); break;
|
||||
default: unimplemented();
|
||||
}
|
||||
case 0x0C:
|
||||
roundws(instr);
|
||||
break;
|
||||
default: Util::panic("Unimplemented COP1 instruction {} {}", mask_sub >> 3, mask_sub & 7);
|
||||
case 0x0D:
|
||||
truncws(instr);
|
||||
break;
|
||||
case 0x0E:
|
||||
ceilws(instr);
|
||||
break;
|
||||
case 0x0F:
|
||||
floorws(instr);
|
||||
break;
|
||||
case 0x21:
|
||||
cvtds(instr);
|
||||
break;
|
||||
case 0x24:
|
||||
cvtws(instr);
|
||||
break;
|
||||
case 0x25:
|
||||
cvtls(instr);
|
||||
break;
|
||||
case 0x30:
|
||||
cf<float>(instr);
|
||||
break;
|
||||
case 0x31:
|
||||
cun<float>(instr);
|
||||
break;
|
||||
case 0x32:
|
||||
ceq<float>(instr);
|
||||
break;
|
||||
case 0x33:
|
||||
cueq<float>(instr);
|
||||
break;
|
||||
case 0x34:
|
||||
colt<float>(instr);
|
||||
break;
|
||||
case 0x35:
|
||||
cult<float>(instr);
|
||||
break;
|
||||
case 0x36:
|
||||
cole<float>(instr);
|
||||
break;
|
||||
case 0x37:
|
||||
cule<float>(instr);
|
||||
break;
|
||||
case 0x38:
|
||||
csf<float>(instr);
|
||||
break;
|
||||
case 0x39:
|
||||
cngle<float>(instr);
|
||||
break;
|
||||
case 0x3A:
|
||||
cseq<float>(instr);
|
||||
break;
|
||||
case 0x3B:
|
||||
cngl<float>(instr);
|
||||
break;
|
||||
case 0x3C:
|
||||
clt<float>(instr);
|
||||
break;
|
||||
case 0x3D:
|
||||
cnge<float>(instr);
|
||||
break;
|
||||
case 0x3E:
|
||||
cle<float>(instr);
|
||||
break;
|
||||
case 0x3F:
|
||||
cngt<float>(instr);
|
||||
break;
|
||||
default:
|
||||
unimplemented();
|
||||
}
|
||||
break;
|
||||
case 0x11: // d
|
||||
switch (mask_fun) {
|
||||
case 0x00:
|
||||
addd(instr);
|
||||
break;
|
||||
case 0x01:
|
||||
subd(instr);
|
||||
break;
|
||||
case 0x02:
|
||||
muld(instr);
|
||||
break;
|
||||
case 0x03:
|
||||
divd(instr);
|
||||
break;
|
||||
case 0x04:
|
||||
sqrtd(instr);
|
||||
break;
|
||||
case 0x05:
|
||||
absd(instr);
|
||||
break;
|
||||
case 0x06:
|
||||
movd(instr);
|
||||
break;
|
||||
case 0x07:
|
||||
negd(instr);
|
||||
break;
|
||||
case 0x08:
|
||||
roundld(instr);
|
||||
break;
|
||||
case 0x09:
|
||||
truncld(instr);
|
||||
break;
|
||||
case 0x0A:
|
||||
ceilld(instr);
|
||||
break;
|
||||
case 0x0B:
|
||||
floorld(instr);
|
||||
break;
|
||||
case 0x0C:
|
||||
roundwd(instr);
|
||||
break;
|
||||
case 0x0D:
|
||||
truncwd(instr);
|
||||
break;
|
||||
case 0x0E:
|
||||
ceilwd(instr);
|
||||
break;
|
||||
case 0x0F:
|
||||
floorwd(instr);
|
||||
break;
|
||||
case 0x20:
|
||||
cvtsd(instr);
|
||||
break;
|
||||
case 0x24:
|
||||
cvtwd(instr);
|
||||
break;
|
||||
case 0x25:
|
||||
cvtld(instr);
|
||||
break;
|
||||
case 0x30:
|
||||
cf<double>(instr);
|
||||
break;
|
||||
case 0x31:
|
||||
cun<double>(instr);
|
||||
break;
|
||||
case 0x32:
|
||||
ceq<double>(instr);
|
||||
break;
|
||||
case 0x33:
|
||||
cueq<double>(instr);
|
||||
break;
|
||||
case 0x34:
|
||||
colt<double>(instr);
|
||||
break;
|
||||
case 0x35:
|
||||
cult<double>(instr);
|
||||
break;
|
||||
case 0x36:
|
||||
cole<double>(instr);
|
||||
break;
|
||||
case 0x37:
|
||||
cule<double>(instr);
|
||||
break;
|
||||
case 0x38:
|
||||
csf<double>(instr);
|
||||
break;
|
||||
case 0x39:
|
||||
cngle<double>(instr);
|
||||
break;
|
||||
case 0x3A:
|
||||
cseq<double>(instr);
|
||||
break;
|
||||
case 0x3B:
|
||||
cngl<double>(instr);
|
||||
break;
|
||||
case 0x3C:
|
||||
clt<double>(instr);
|
||||
break;
|
||||
case 0x3D:
|
||||
cnge<double>(instr);
|
||||
break;
|
||||
case 0x3E:
|
||||
cle<double>(instr);
|
||||
break;
|
||||
case 0x3F:
|
||||
cngt<double>(instr);
|
||||
break;
|
||||
default:
|
||||
unimplemented();
|
||||
}
|
||||
break;
|
||||
case 0x14: // w
|
||||
switch (mask_fun) {
|
||||
case 0x20:
|
||||
cvtsw(instr);
|
||||
break;
|
||||
case 0x21:
|
||||
cvtdw(instr);
|
||||
break;
|
||||
default:
|
||||
unimplemented();
|
||||
}
|
||||
break;
|
||||
case 0x15: // l
|
||||
switch (mask_fun) {
|
||||
case 0x20:
|
||||
cvtsl(instr);
|
||||
break;
|
||||
case 0x21:
|
||||
cvtdl(instr);
|
||||
break;
|
||||
default:
|
||||
unimplemented();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unimplemented COP1 instruction {} {}", mask_sub >> 3, mask_sub & 7);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -8,33 +8,33 @@ struct Cop1;
|
||||
union FCR31 {
|
||||
FCR31() = default;
|
||||
struct {
|
||||
unsigned rounding_mode:2;
|
||||
unsigned rounding_mode : 2;
|
||||
struct {
|
||||
unsigned inexact_operation:1;
|
||||
unsigned underflow:1;
|
||||
unsigned overflow:1;
|
||||
unsigned division_by_zero:1;
|
||||
unsigned invalid_operation:1;
|
||||
unsigned inexact_operation : 1;
|
||||
unsigned underflow : 1;
|
||||
unsigned overflow : 1;
|
||||
unsigned division_by_zero : 1;
|
||||
unsigned invalid_operation : 1;
|
||||
} flag;
|
||||
struct {
|
||||
unsigned inexact_operation:1;
|
||||
unsigned underflow:1;
|
||||
unsigned overflow:1;
|
||||
unsigned division_by_zero:1;
|
||||
unsigned invalid_operation:1;
|
||||
unsigned inexact_operation : 1;
|
||||
unsigned underflow : 1;
|
||||
unsigned overflow : 1;
|
||||
unsigned division_by_zero : 1;
|
||||
unsigned invalid_operation : 1;
|
||||
} enable;
|
||||
struct {
|
||||
unsigned inexact_operation:1;
|
||||
unsigned underflow:1;
|
||||
unsigned overflow:1;
|
||||
unsigned division_by_zero:1;
|
||||
unsigned invalid_operation:1;
|
||||
unsigned unimplemented_operation:1;
|
||||
unsigned inexact_operation : 1;
|
||||
unsigned underflow : 1;
|
||||
unsigned overflow : 1;
|
||||
unsigned division_by_zero : 1;
|
||||
unsigned invalid_operation : 1;
|
||||
unsigned unimplemented_operation : 1;
|
||||
} cause;
|
||||
unsigned:5;
|
||||
unsigned compare:1;
|
||||
unsigned fs:1;
|
||||
unsigned:7;
|
||||
unsigned : 5;
|
||||
unsigned compare : 1;
|
||||
unsigned fs : 1;
|
||||
unsigned : 7;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
[[nodiscard]] u32 read() const {
|
||||
@@ -85,12 +85,7 @@ union FCR31 {
|
||||
}
|
||||
};
|
||||
|
||||
enum CompConds {
|
||||
F, UN, EQ, UEQ,
|
||||
OLT, ULT, OLE, ULE,
|
||||
SF, NGLE, SEQ, NGL,
|
||||
LT, NGE, LE, NGT
|
||||
};
|
||||
enum CompConds { F, UN, EQ, UEQ, OLT, ULT, OLE, ULE, SF, NGLE, SEQ, NGL, LT, NGE, LE, NGT };
|
||||
|
||||
union FloatingPointReg {
|
||||
struct {
|
||||
@@ -121,28 +116,28 @@ struct JIT;
|
||||
struct Registers;
|
||||
|
||||
struct Cop1 {
|
||||
explicit Cop1(Registers&);
|
||||
explicit Cop1(Registers &);
|
||||
u32 fcr0{};
|
||||
FCR31 fcr31{};
|
||||
FloatingPointReg fgr[32]{};
|
||||
|
||||
void Reset();
|
||||
template <class T> // either JIT or Interpreter
|
||||
void decode(T&, u32);
|
||||
void decode(T &, u32);
|
||||
friend struct Interpreter;
|
||||
|
||||
template <bool preserveCause = false>
|
||||
bool CheckFPUUsable();
|
||||
template <typename T>
|
||||
bool CheckResult(T&);
|
||||
bool CheckResult(T &);
|
||||
template <typename T>
|
||||
bool CheckArg(T&);
|
||||
bool CheckArg(T &);
|
||||
template <typename T>
|
||||
bool CheckArgs(T&, T&);
|
||||
bool CheckArgs(T &, T &);
|
||||
template <typename T>
|
||||
bool isqnan(T);
|
||||
|
||||
template<typename T, bool quiet, bool cf>
|
||||
template <typename T, bool quiet, bool cf>
|
||||
bool XORDERED(T fs, T ft);
|
||||
|
||||
template <typename T>
|
||||
@@ -158,15 +153,16 @@ struct Cop1 {
|
||||
bool SetCauseDivisionByZero();
|
||||
bool SetCauseOverflow();
|
||||
bool SetCauseInvalid();
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
auto FGR_T(Cop0Status&, u32) -> T&;
|
||||
auto FGR_T(Cop0Status &, u32) -> T &;
|
||||
template <typename T>
|
||||
auto FGR_S(Cop0Status&, u32) -> T&;
|
||||
auto FGR_S(Cop0Status &, u32) -> T &;
|
||||
template <typename T>
|
||||
auto FGR_D(Cop0Status&, u32) -> T&;
|
||||
void decodeInterp(Interpreter&, u32);
|
||||
void decodeJIT(JIT&, u32);
|
||||
auto FGR_D(Cop0Status &, u32) -> T &;
|
||||
void decodeInterp(Interpreter &, u32);
|
||||
void decodeJIT(JIT &, u32);
|
||||
void absd(u32 instr);
|
||||
void abss(u32 instr);
|
||||
void adds(u32 instr);
|
||||
@@ -240,31 +236,23 @@ private:
|
||||
void negd(u32 instr);
|
||||
void sqrts(u32 instr);
|
||||
void sqrtd(u32 instr);
|
||||
template<class T>
|
||||
void lwc1(T&, Mem&, u32);
|
||||
template<class T>
|
||||
void swc1(T&, Mem&, u32);
|
||||
template<class T>
|
||||
void ldc1(T&, Mem&, u32);
|
||||
template<class T>
|
||||
void sdc1(T&, Mem&, u32);
|
||||
template <class T>
|
||||
void lwc1(T &, Mem &, u32);
|
||||
template <class T>
|
||||
void swc1(T &, Mem &, u32);
|
||||
template <class T>
|
||||
void ldc1(T &, Mem &, u32);
|
||||
template <class T>
|
||||
void sdc1(T &, Mem &, u32);
|
||||
|
||||
void lwc1Interp(Mem&, u32);
|
||||
void swc1Interp(Mem&, u32);
|
||||
void ldc1Interp(Mem&, u32);
|
||||
void sdc1Interp(Mem&, u32);
|
||||
void lwc1JIT(JIT&, Mem&, u32) {
|
||||
Util::panic("[JIT]: lwc1 not implemented!");
|
||||
}
|
||||
void swc1JIT(JIT&, Mem&, u32) {
|
||||
Util::panic("[JIT]: swc1 not implemented!");
|
||||
}
|
||||
void ldc1JIT(JIT&, Mem&, u32) {
|
||||
Util::panic("[JIT]: ldc1 not implemented!");
|
||||
}
|
||||
void sdc1JIT(JIT&, Mem&, u32) {
|
||||
Util::panic("[JIT]: sdc1 not implemented!");
|
||||
}
|
||||
void lwc1Interp(Mem &, u32);
|
||||
void swc1Interp(Mem &, u32);
|
||||
void ldc1Interp(Mem &, u32);
|
||||
void sdc1Interp(Mem &, u32);
|
||||
void lwc1JIT(JIT &, Mem &, u32) { Util::panic("[JIT]: lwc1 not implemented!"); }
|
||||
void swc1JIT(JIT &, Mem &, u32) { Util::panic("[JIT]: swc1 not implemented!"); }
|
||||
void ldc1JIT(JIT &, Mem &, u32) { Util::panic("[JIT]: ldc1 not implemented!"); }
|
||||
void sdc1JIT(JIT &, Mem &, u32) { Util::panic("[JIT]: sdc1 not implemented!"); }
|
||||
void mfc1(u32 instr);
|
||||
void dmfc1(u32 instr);
|
||||
void mtc1(u32 instr);
|
||||
@@ -274,6 +262,6 @@ private:
|
||||
void truncls(u32 instr);
|
||||
void truncld(u32 instr);
|
||||
|
||||
Registers& regs;
|
||||
Registers ®s;
|
||||
};
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
@@ -3,24 +3,16 @@
|
||||
#include <log.hpp>
|
||||
|
||||
namespace n64 {
|
||||
void Cop0::mtc0(u32 instr) {
|
||||
SetReg32(RD(instr), regs.Read<u32>(RT(instr)));
|
||||
}
|
||||
void Cop0::mtc0(u32 instr) { SetReg32(RD(instr), regs.Read<u32>(RT(instr))); }
|
||||
|
||||
void Cop0::dmtc0(u32 instr) {
|
||||
SetReg64(RD(instr), regs.Read<u64>(RT(instr)));
|
||||
}
|
||||
void Cop0::dmtc0(u32 instr) { SetReg64(RD(instr), regs.Read<u64>(RT(instr))); }
|
||||
|
||||
void Cop0::mfc0(u32 instr) {
|
||||
regs.Write(RT(instr), s32(GetReg32(RD(instr))));
|
||||
}
|
||||
void Cop0::mfc0(u32 instr) { regs.Write(RT(instr), s32(GetReg32(RD(instr)))); }
|
||||
|
||||
void Cop0::dmfc0(u32 instr) const {
|
||||
regs.Write(RT(instr), s64(GetReg64(RD(instr))));
|
||||
}
|
||||
void Cop0::dmfc0(u32 instr) const { regs.Write(RT(instr), s64(GetReg64(RD(instr)))); }
|
||||
|
||||
void Cop0::eret() {
|
||||
if(status.erl) {
|
||||
if (status.erl) {
|
||||
regs.SetPC64(ErrorEPC);
|
||||
status.erl = false;
|
||||
} else {
|
||||
@@ -54,11 +46,11 @@ void Cop0::tlbw(int index_) {
|
||||
u32 top = page_mask.mask & 0xAAA;
|
||||
page_mask.mask = top | (top >> 1);
|
||||
|
||||
if(index_ >= 32) {
|
||||
if (index_ >= 32) {
|
||||
Util::panic("TLBWI with TLB index {}", index_);
|
||||
}
|
||||
|
||||
tlb[index_].entryHi.raw = entryHi.raw;
|
||||
tlb[index_].entryHi.raw = entryHi.raw;
|
||||
tlb[index_].entryHi.vpn2 &= ~page_mask.mask;
|
||||
|
||||
tlb[index_].entryLo0.raw = entryLo0.raw & 0x03FFFFFE;
|
||||
@@ -71,8 +63,8 @@ void Cop0::tlbw(int index_) {
|
||||
|
||||
void Cop0::tlbp() {
|
||||
int match = -1;
|
||||
TLBEntry* entry = TLBTryMatch(entryHi.raw, &match);
|
||||
if(entry && match >= 0) {
|
||||
TLBEntry *entry = TLBTryMatch(entryHi.raw, &match);
|
||||
if (entry && match >= 0) {
|
||||
index.raw = match;
|
||||
} else {
|
||||
index.raw = 0;
|
||||
@@ -80,4 +72,4 @@ void Cop0::tlbp() {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,183 +1,376 @@
|
||||
#include <core/RSP.hpp>
|
||||
#include <log.hpp>
|
||||
#include <core/registers/Registers.hpp>
|
||||
#include <Mem.hpp>
|
||||
#include <core/RSP.hpp>
|
||||
#include <core/registers/Registers.hpp>
|
||||
#include <log.hpp>
|
||||
|
||||
namespace n64 {
|
||||
FORCE_INLINE void special(MI& mi, Registers& regs, RSP& rsp, u32 instr) {
|
||||
FORCE_INLINE void special(MI &mi, Registers ®s, RSP &rsp, u32 instr) {
|
||||
u8 mask = instr & 0x3f;
|
||||
//Util::print("rsp special {:02X}", mask);
|
||||
switch(mask) {
|
||||
case 0x00:
|
||||
if(instr != 0) {
|
||||
rsp.sll(instr);
|
||||
}
|
||||
break;
|
||||
case 0x02: rsp.srl(instr); break;
|
||||
case 0x03: rsp.sra(instr); break;
|
||||
case 0x04: rsp.sllv(instr); break;
|
||||
case 0x06: rsp.srlv(instr); break;
|
||||
case 0x07: rsp.srav(instr); break;
|
||||
case 0x08: rsp.jr(instr); break;
|
||||
case 0x09: rsp.jalr(instr); break;
|
||||
case 0x0D:
|
||||
rsp.spStatus.halt = true;
|
||||
rsp.steps = 0;
|
||||
rsp.spStatus.broke = true;
|
||||
if(rsp.spStatus.interruptOnBreak) {
|
||||
mi.InterruptRaise(MI::Interrupt::SP);
|
||||
}
|
||||
break;
|
||||
case 0x20: case 0x21:
|
||||
rsp.add(instr);
|
||||
break;
|
||||
case 0x22: case 0x23:
|
||||
rsp.sub(instr);
|
||||
break;
|
||||
case 0x24: rsp.and_(instr); break;
|
||||
case 0x25: rsp.or_(instr); break;
|
||||
case 0x26: rsp.xor_(instr); break;
|
||||
case 0x27: rsp.nor(instr); break;
|
||||
case 0x2A: rsp.slt(instr); break;
|
||||
case 0x2B: rsp.sltu(instr); break;
|
||||
default: Util::panic("Unhandled RSP special instruction ({:06b})", mask);
|
||||
// Util::print("rsp special {:02X}", mask);
|
||||
switch (mask) {
|
||||
case 0x00:
|
||||
if (instr != 0) {
|
||||
rsp.sll(instr);
|
||||
}
|
||||
break;
|
||||
case 0x02:
|
||||
rsp.srl(instr);
|
||||
break;
|
||||
case 0x03:
|
||||
rsp.sra(instr);
|
||||
break;
|
||||
case 0x04:
|
||||
rsp.sllv(instr);
|
||||
break;
|
||||
case 0x06:
|
||||
rsp.srlv(instr);
|
||||
break;
|
||||
case 0x07:
|
||||
rsp.srav(instr);
|
||||
break;
|
||||
case 0x08:
|
||||
rsp.jr(instr);
|
||||
break;
|
||||
case 0x09:
|
||||
rsp.jalr(instr);
|
||||
break;
|
||||
case 0x0D:
|
||||
rsp.spStatus.halt = true;
|
||||
rsp.steps = 0;
|
||||
rsp.spStatus.broke = true;
|
||||
if (rsp.spStatus.interruptOnBreak) {
|
||||
mi.InterruptRaise(MI::Interrupt::SP);
|
||||
}
|
||||
break;
|
||||
case 0x20:
|
||||
case 0x21:
|
||||
rsp.add(instr);
|
||||
break;
|
||||
case 0x22:
|
||||
case 0x23:
|
||||
rsp.sub(instr);
|
||||
break;
|
||||
case 0x24:
|
||||
rsp.and_(instr);
|
||||
break;
|
||||
case 0x25:
|
||||
rsp.or_(instr);
|
||||
break;
|
||||
case 0x26:
|
||||
rsp.xor_(instr);
|
||||
break;
|
||||
case 0x27:
|
||||
rsp.nor(instr);
|
||||
break;
|
||||
case 0x2A:
|
||||
rsp.slt(instr);
|
||||
break;
|
||||
case 0x2B:
|
||||
rsp.sltu(instr);
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unhandled RSP special instruction ({:06b})", mask);
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE void regimm(RSP& rsp, u32 instr) {
|
||||
FORCE_INLINE void regimm(RSP &rsp, u32 instr) {
|
||||
u8 mask = ((instr >> 16) & 0x1F);
|
||||
//Util::print("rsp regimm {:02X}", mask);
|
||||
switch(mask) {
|
||||
case 0x00: rsp.b(instr, (s32)rsp.gpr[RS(instr)] < 0); break;
|
||||
case 0x01: rsp.b(instr, (s32)rsp.gpr[RS(instr)] >= 0); break;
|
||||
case 0x10: rsp.blink(instr, (s32)rsp.gpr[RS(instr)] < 0); break;
|
||||
case 0x11: rsp.blink(instr, (s32)rsp.gpr[RS(instr)] >= 0); break;
|
||||
default: Util::panic("Unhandled RSP regimm instruction ({:05b})", mask);
|
||||
// Util::print("rsp regimm {:02X}", mask);
|
||||
switch (mask) {
|
||||
case 0x00:
|
||||
rsp.b(instr, (s32)rsp.gpr[RS(instr)] < 0);
|
||||
break;
|
||||
case 0x01:
|
||||
rsp.b(instr, (s32)rsp.gpr[RS(instr)] >= 0);
|
||||
break;
|
||||
case 0x10:
|
||||
rsp.blink(instr, (s32)rsp.gpr[RS(instr)] < 0);
|
||||
break;
|
||||
case 0x11:
|
||||
rsp.blink(instr, (s32)rsp.gpr[RS(instr)] >= 0);
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unhandled RSP regimm instruction ({:05b})", mask);
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE void lwc2(RSP& rsp, u32 instr) {
|
||||
FORCE_INLINE void lwc2(RSP &rsp, u32 instr) {
|
||||
u8 mask = (instr >> 11) & 0x1F;
|
||||
//Util::print("lwc2 {:02X}", mask);
|
||||
switch(mask) {
|
||||
case 0x00: rsp.lbv(instr); break;
|
||||
case 0x01: rsp.lsv(instr); break;
|
||||
case 0x02: rsp.llv(instr); break;
|
||||
case 0x03: rsp.ldv(instr); break;
|
||||
case 0x04: rsp.lqv(instr); break;
|
||||
case 0x05: rsp.lrv(instr); break;
|
||||
case 0x06: rsp.lpv(instr); break;
|
||||
case 0x07: rsp.luv(instr); break;
|
||||
case 0x08: rsp.lhv(instr); break;
|
||||
case 0x09: rsp.lfv(instr); break;
|
||||
case 0x0A: break;
|
||||
case 0x0B: rsp.ltv(instr); break;
|
||||
default: Util::panic("Unhandled RSP LWC2 {:05b}", mask);
|
||||
// Util::print("lwc2 {:02X}", mask);
|
||||
switch (mask) {
|
||||
case 0x00:
|
||||
rsp.lbv(instr);
|
||||
break;
|
||||
case 0x01:
|
||||
rsp.lsv(instr);
|
||||
break;
|
||||
case 0x02:
|
||||
rsp.llv(instr);
|
||||
break;
|
||||
case 0x03:
|
||||
rsp.ldv(instr);
|
||||
break;
|
||||
case 0x04:
|
||||
rsp.lqv(instr);
|
||||
break;
|
||||
case 0x05:
|
||||
rsp.lrv(instr);
|
||||
break;
|
||||
case 0x06:
|
||||
rsp.lpv(instr);
|
||||
break;
|
||||
case 0x07:
|
||||
rsp.luv(instr);
|
||||
break;
|
||||
case 0x08:
|
||||
rsp.lhv(instr);
|
||||
break;
|
||||
case 0x09:
|
||||
rsp.lfv(instr);
|
||||
break;
|
||||
case 0x0A:
|
||||
break;
|
||||
case 0x0B:
|
||||
rsp.ltv(instr);
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unhandled RSP LWC2 {:05b}", mask);
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE void swc2(RSP& rsp, u32 instr) {
|
||||
FORCE_INLINE void swc2(RSP &rsp, u32 instr) {
|
||||
u8 mask = (instr >> 11) & 0x1F;
|
||||
//Util::print("swc2 {:02X}", mask);
|
||||
switch(mask) {
|
||||
case 0x00: rsp.sbv(instr); break;
|
||||
case 0x01: rsp.ssv(instr); break;
|
||||
case 0x02: rsp.slv(instr); break;
|
||||
case 0x03: rsp.sdv(instr); break;
|
||||
case 0x04: rsp.sqv(instr); break;
|
||||
case 0x05: rsp.srv(instr); break;
|
||||
case 0x06: rsp.spv(instr); break;
|
||||
case 0x07: rsp.suv(instr); break;
|
||||
case 0x08: rsp.shv(instr); break;
|
||||
case 0x09: rsp.sfv(instr); break;
|
||||
case 0x0A: rsp.swv(instr); break;
|
||||
case 0x0B: rsp.stv(instr); break;
|
||||
default: Util::panic("Unhandled RSP SWC2 {:05b}", mask);
|
||||
// Util::print("swc2 {:02X}", mask);
|
||||
switch (mask) {
|
||||
case 0x00:
|
||||
rsp.sbv(instr);
|
||||
break;
|
||||
case 0x01:
|
||||
rsp.ssv(instr);
|
||||
break;
|
||||
case 0x02:
|
||||
rsp.slv(instr);
|
||||
break;
|
||||
case 0x03:
|
||||
rsp.sdv(instr);
|
||||
break;
|
||||
case 0x04:
|
||||
rsp.sqv(instr);
|
||||
break;
|
||||
case 0x05:
|
||||
rsp.srv(instr);
|
||||
break;
|
||||
case 0x06:
|
||||
rsp.spv(instr);
|
||||
break;
|
||||
case 0x07:
|
||||
rsp.suv(instr);
|
||||
break;
|
||||
case 0x08:
|
||||
rsp.shv(instr);
|
||||
break;
|
||||
case 0x09:
|
||||
rsp.sfv(instr);
|
||||
break;
|
||||
case 0x0A:
|
||||
rsp.swv(instr);
|
||||
break;
|
||||
case 0x0B:
|
||||
rsp.stv(instr);
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unhandled RSP SWC2 {:05b}", mask);
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE void cop2(RSP& rsp, u32 instr) {
|
||||
FORCE_INLINE void cop2(RSP &rsp, u32 instr) {
|
||||
u8 mask = instr & 0x3F;
|
||||
u8 mask_sub = (instr >> 21) & 0x1F;
|
||||
//Util::print("Cop2 {:02X}", mask);
|
||||
switch(mask) {
|
||||
case 0x00:
|
||||
if((instr >> 25) & 1) {
|
||||
rsp.vmulf(instr);
|
||||
} else {
|
||||
switch (mask_sub) {
|
||||
case 0x00: rsp.mfc2(instr); break;
|
||||
case 0x02: rsp.cfc2(instr); break;
|
||||
case 0x04: rsp.mtc2(instr); break;
|
||||
case 0x06: rsp.ctc2(instr); break;
|
||||
default: Util::panic("Unhandled RSP COP2 sub ({:05b})", mask_sub);
|
||||
}
|
||||
// Util::print("Cop2 {:02X}", mask);
|
||||
switch (mask) {
|
||||
case 0x00:
|
||||
if ((instr >> 25) & 1) {
|
||||
rsp.vmulf(instr);
|
||||
} else {
|
||||
switch (mask_sub) {
|
||||
case 0x00:
|
||||
rsp.mfc2(instr);
|
||||
break;
|
||||
case 0x02:
|
||||
rsp.cfc2(instr);
|
||||
break;
|
||||
case 0x04:
|
||||
rsp.mtc2(instr);
|
||||
break;
|
||||
case 0x06:
|
||||
rsp.ctc2(instr);
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unhandled RSP COP2 sub ({:05b})", mask_sub);
|
||||
}
|
||||
break;
|
||||
case 0x01: rsp.vmulu(instr); break;
|
||||
case 0x02: rsp.vrndp(instr); break;
|
||||
case 0x03: rsp.vmulq(instr); break;
|
||||
case 0x04: rsp.vmudl(instr); break;
|
||||
case 0x05: rsp.vmudm(instr); break;
|
||||
case 0x06: rsp.vmudn(instr); break;
|
||||
case 0x07: rsp.vmudh(instr); break;
|
||||
case 0x08: rsp.vmacf(instr); break;
|
||||
case 0x09: rsp.vmacu(instr); break;
|
||||
case 0x0A: rsp.vrndn(instr); break;
|
||||
case 0x0B: rsp.vmacq(instr); break;
|
||||
case 0x0C: rsp.vmadl(instr); break;
|
||||
case 0x0D: rsp.vmadm(instr); break;
|
||||
case 0x0E: rsp.vmadn(instr); break;
|
||||
case 0x0F: rsp.vmadh(instr); break;
|
||||
case 0x10: rsp.vadd(instr); break;
|
||||
case 0x11: rsp.vsub(instr); break;
|
||||
case 0x12: rsp.vzero(instr); break;
|
||||
case 0x13: rsp.vabs(instr); break;
|
||||
case 0x14: rsp.vaddc(instr); break;
|
||||
case 0x15: rsp.vsubc(instr); break;
|
||||
case 0x16 ... 0x1C: case 0x1E: case 0x1F: case 0x2E: case 0x2F:
|
||||
rsp.vzero(instr);
|
||||
break;
|
||||
case 0x1D: rsp.vsar(instr); break;
|
||||
case 0x20: rsp.vlt(instr); break;
|
||||
case 0x21: rsp.veq(instr); break;
|
||||
case 0x22: rsp.vne(instr); break;
|
||||
case 0x23: rsp.vge(instr); break;
|
||||
case 0x24: rsp.vcl(instr); break;
|
||||
case 0x25: rsp.vch(instr); break;
|
||||
case 0x26: rsp.vcr(instr); break;
|
||||
case 0x27: rsp.vmrg(instr); break;
|
||||
case 0x28: rsp.vand(instr); break;
|
||||
case 0x29: rsp.vnand(instr); break;
|
||||
case 0x2A: rsp.vor(instr); break;
|
||||
case 0x2B: rsp.vnor(instr); break;
|
||||
case 0x2C: rsp.vxor(instr); break;
|
||||
case 0x2D: rsp.vnxor(instr); break;
|
||||
case 0x31: rsp.vrcpl(instr); break;
|
||||
case 0x35: rsp.vrsql(instr); break;
|
||||
case 0x32: case 0x36:
|
||||
rsp.vrcph(instr);
|
||||
break;
|
||||
case 0x30: rsp.vrcp(instr); break;
|
||||
case 0x33: rsp.vmov(instr); break;
|
||||
case 0x34: rsp.vrsq(instr); break;
|
||||
case 0x38 ... 0x3E: rsp.vzero(instr); break;
|
||||
case 0x37: case 0x3F: break;
|
||||
default: Util::panic("Unhandled RSP COP2 ({:06b})", mask);
|
||||
}
|
||||
break;
|
||||
case 0x01:
|
||||
rsp.vmulu(instr);
|
||||
break;
|
||||
case 0x02:
|
||||
rsp.vrndp(instr);
|
||||
break;
|
||||
case 0x03:
|
||||
rsp.vmulq(instr);
|
||||
break;
|
||||
case 0x04:
|
||||
rsp.vmudl(instr);
|
||||
break;
|
||||
case 0x05:
|
||||
rsp.vmudm(instr);
|
||||
break;
|
||||
case 0x06:
|
||||
rsp.vmudn(instr);
|
||||
break;
|
||||
case 0x07:
|
||||
rsp.vmudh(instr);
|
||||
break;
|
||||
case 0x08:
|
||||
rsp.vmacf(instr);
|
||||
break;
|
||||
case 0x09:
|
||||
rsp.vmacu(instr);
|
||||
break;
|
||||
case 0x0A:
|
||||
rsp.vrndn(instr);
|
||||
break;
|
||||
case 0x0B:
|
||||
rsp.vmacq(instr);
|
||||
break;
|
||||
case 0x0C:
|
||||
rsp.vmadl(instr);
|
||||
break;
|
||||
case 0x0D:
|
||||
rsp.vmadm(instr);
|
||||
break;
|
||||
case 0x0E:
|
||||
rsp.vmadn(instr);
|
||||
break;
|
||||
case 0x0F:
|
||||
rsp.vmadh(instr);
|
||||
break;
|
||||
case 0x10:
|
||||
rsp.vadd(instr);
|
||||
break;
|
||||
case 0x11:
|
||||
rsp.vsub(instr);
|
||||
break;
|
||||
case 0x12:
|
||||
rsp.vzero(instr);
|
||||
break;
|
||||
case 0x13:
|
||||
rsp.vabs(instr);
|
||||
break;
|
||||
case 0x14:
|
||||
rsp.vaddc(instr);
|
||||
break;
|
||||
case 0x15:
|
||||
rsp.vsubc(instr);
|
||||
break;
|
||||
case 0x16 ... 0x1C:
|
||||
case 0x1E:
|
||||
case 0x1F:
|
||||
case 0x2E:
|
||||
case 0x2F:
|
||||
rsp.vzero(instr);
|
||||
break;
|
||||
case 0x1D:
|
||||
rsp.vsar(instr);
|
||||
break;
|
||||
case 0x20:
|
||||
rsp.vlt(instr);
|
||||
break;
|
||||
case 0x21:
|
||||
rsp.veq(instr);
|
||||
break;
|
||||
case 0x22:
|
||||
rsp.vne(instr);
|
||||
break;
|
||||
case 0x23:
|
||||
rsp.vge(instr);
|
||||
break;
|
||||
case 0x24:
|
||||
rsp.vcl(instr);
|
||||
break;
|
||||
case 0x25:
|
||||
rsp.vch(instr);
|
||||
break;
|
||||
case 0x26:
|
||||
rsp.vcr(instr);
|
||||
break;
|
||||
case 0x27:
|
||||
rsp.vmrg(instr);
|
||||
break;
|
||||
case 0x28:
|
||||
rsp.vand(instr);
|
||||
break;
|
||||
case 0x29:
|
||||
rsp.vnand(instr);
|
||||
break;
|
||||
case 0x2A:
|
||||
rsp.vor(instr);
|
||||
break;
|
||||
case 0x2B:
|
||||
rsp.vnor(instr);
|
||||
break;
|
||||
case 0x2C:
|
||||
rsp.vxor(instr);
|
||||
break;
|
||||
case 0x2D:
|
||||
rsp.vnxor(instr);
|
||||
break;
|
||||
case 0x31:
|
||||
rsp.vrcpl(instr);
|
||||
break;
|
||||
case 0x35:
|
||||
rsp.vrsql(instr);
|
||||
break;
|
||||
case 0x32:
|
||||
case 0x36:
|
||||
rsp.vrcph(instr);
|
||||
break;
|
||||
case 0x30:
|
||||
rsp.vrcp(instr);
|
||||
break;
|
||||
case 0x33:
|
||||
rsp.vmov(instr);
|
||||
break;
|
||||
case 0x34:
|
||||
rsp.vrsq(instr);
|
||||
break;
|
||||
case 0x38 ... 0x3E:
|
||||
rsp.vzero(instr);
|
||||
break;
|
||||
case 0x37:
|
||||
case 0x3F:
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unhandled RSP COP2 ({:06b})", mask);
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE void cop0(Registers& regs, Mem& mem, u32 instr) {
|
||||
FORCE_INLINE void cop0(Registers ®s, Mem &mem, u32 instr) {
|
||||
u8 mask = (instr >> 21) & 0x1F;
|
||||
MMIO& mmio = mem.mmio;
|
||||
RSP& rsp = mmio.rsp;
|
||||
RDP& rdp = mmio.rdp;
|
||||
//Util::print("Cop0 {:02X}", mask);
|
||||
if((instr & 0x7FF) == 0) {
|
||||
MMIO &mmio = mem.mmio;
|
||||
RSP &rsp = mmio.rsp;
|
||||
RDP &rdp = mmio.rdp;
|
||||
// Util::print("Cop0 {:02X}", mask);
|
||||
if ((instr & 0x7FF) == 0) {
|
||||
switch (mask) {
|
||||
case 0x00: rsp.mfc0(rdp, instr); break;
|
||||
case 0x04: rsp.mtc0(instr); break;
|
||||
default: Util::panic("Unhandled RSP COP0 ({:05b})", mask);
|
||||
case 0x00:
|
||||
rsp.mfc0(rdp, instr);
|
||||
break;
|
||||
case 0x04:
|
||||
rsp.mtc0(instr);
|
||||
break;
|
||||
default:
|
||||
Util::panic("Unhandled RSP COP0 ({:05b})", mask);
|
||||
}
|
||||
} else {
|
||||
Util::panic("RSP COP0 unknown {:08X}", instr);
|
||||
@@ -186,42 +379,96 @@ FORCE_INLINE void cop0(Registers& regs, Mem& mem, u32 instr) {
|
||||
|
||||
void RSP::Exec(u32 instr) {
|
||||
u8 mask = (instr >> 26) & 0x3F;
|
||||
MMIO& mmio = mem.mmio;
|
||||
MI& mi = mmio.mi;
|
||||
//Util::print("RSP {:02X}", mask);
|
||||
switch(mask) {
|
||||
case 0x00: special(mi, regs, *this, instr); break;
|
||||
case 0x01: regimm(*this, instr); break;
|
||||
case 0x02: j(instr); break;
|
||||
case 0x03: jal(instr); break;
|
||||
case 0x04: b(instr, (s32)gpr[RT(instr)] == (s32)gpr[RS(instr)]); break;
|
||||
case 0x05: b(instr, (s32)gpr[RT(instr)] != (s32)gpr[RS(instr)]); break;
|
||||
case 0x06: b(instr, (s32)gpr[RS(instr)] <= 0); break;
|
||||
case 0x07: b(instr, (s32)gpr[RS(instr)] > 0); break;
|
||||
case 0x08: case 0x09: addi(instr); break;
|
||||
case 0x0A: slti(instr); break;
|
||||
case 0x0B: sltiu(instr); break;
|
||||
case 0x0C: andi(instr); break;
|
||||
case 0x0D: ori(instr); break;
|
||||
case 0x0E: xori(instr); break;
|
||||
case 0x0F: lui(instr); break;
|
||||
case 0x10: cop0(regs, mem, instr); break;
|
||||
case 0x12: cop2(*this, instr); break;
|
||||
case 0x20: lb(instr); break;
|
||||
case 0x21: lh(instr); break;
|
||||
case 0x23: case 0x27:
|
||||
lw(instr);
|
||||
break;
|
||||
case 0x24: lbu(instr); break;
|
||||
case 0x25: lhu(instr); break;
|
||||
case 0x28: sb(instr); break;
|
||||
case 0x29: sh(instr); break;
|
||||
case 0x2B: sw(instr); break;
|
||||
case 0x32: lwc2(*this, instr); break;
|
||||
case 0x3A: swc2(*this, instr); break;
|
||||
default:
|
||||
mem.DumpIMEM();
|
||||
Util::panic("Unhandled RSP instruction ({:06b}, {:04X})", mask, oldPC);
|
||||
MMIO &mmio = mem.mmio;
|
||||
MI &mi = mmio.mi;
|
||||
// Util::print("RSP {:02X}", mask);
|
||||
switch (mask) {
|
||||
case 0x00:
|
||||
special(mi, regs, *this, instr);
|
||||
break;
|
||||
case 0x01:
|
||||
regimm(*this, instr);
|
||||
break;
|
||||
case 0x02:
|
||||
j(instr);
|
||||
break;
|
||||
case 0x03:
|
||||
jal(instr);
|
||||
break;
|
||||
case 0x04:
|
||||
b(instr, (s32)gpr[RT(instr)] == (s32)gpr[RS(instr)]);
|
||||
break;
|
||||
case 0x05:
|
||||
b(instr, (s32)gpr[RT(instr)] != (s32)gpr[RS(instr)]);
|
||||
break;
|
||||
case 0x06:
|
||||
b(instr, (s32)gpr[RS(instr)] <= 0);
|
||||
break;
|
||||
case 0x07:
|
||||
b(instr, (s32)gpr[RS(instr)] > 0);
|
||||
break;
|
||||
case 0x08:
|
||||
case 0x09:
|
||||
addi(instr);
|
||||
break;
|
||||
case 0x0A:
|
||||
slti(instr);
|
||||
break;
|
||||
case 0x0B:
|
||||
sltiu(instr);
|
||||
break;
|
||||
case 0x0C:
|
||||
andi(instr);
|
||||
break;
|
||||
case 0x0D:
|
||||
ori(instr);
|
||||
break;
|
||||
case 0x0E:
|
||||
xori(instr);
|
||||
break;
|
||||
case 0x0F:
|
||||
lui(instr);
|
||||
break;
|
||||
case 0x10:
|
||||
cop0(regs, mem, instr);
|
||||
break;
|
||||
case 0x12:
|
||||
cop2(*this, instr);
|
||||
break;
|
||||
case 0x20:
|
||||
lb(instr);
|
||||
break;
|
||||
case 0x21:
|
||||
lh(instr);
|
||||
break;
|
||||
case 0x23:
|
||||
case 0x27:
|
||||
lw(instr);
|
||||
break;
|
||||
case 0x24:
|
||||
lbu(instr);
|
||||
break;
|
||||
case 0x25:
|
||||
lhu(instr);
|
||||
break;
|
||||
case 0x28:
|
||||
sb(instr);
|
||||
break;
|
||||
case 0x29:
|
||||
sh(instr);
|
||||
break;
|
||||
case 0x2B:
|
||||
sw(instr);
|
||||
break;
|
||||
case 0x32:
|
||||
lwc2(*this, instr);
|
||||
break;
|
||||
case 0x3A:
|
||||
swc2(*this, instr);
|
||||
break;
|
||||
default:
|
||||
mem.DumpIMEM();
|
||||
Util::panic("Unhandled RSP instruction ({:06b}, {:04X})", mask, oldPC);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace n64
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <bitset>
|
||||
#include <cstdint>
|
||||
#include <emmintrin.h>
|
||||
|
||||
using u8 = uint8_t;
|
||||
@@ -43,20 +43,20 @@ static FORCE_INLINE constexpr u32 GetVideoFrequency(bool pal) {
|
||||
#define RD(x) (((x) >> 11) & 0x1F)
|
||||
#define RT(x) (((x) >> 16) & 0x1F)
|
||||
#define RS(x) (((x) >> 21) & 0x1F)
|
||||
#define FD(x) (((x) >> 6) & 0x1F)
|
||||
#define FD(x) (((x) >> 6) & 0x1F)
|
||||
#define FT(x) RT(x)
|
||||
#define FS(x) RD(x)
|
||||
#define BASE(x) RS(x)
|
||||
#define VT(x) (((x) >> 16) & 0x1F)
|
||||
#define VS(x) (((x) >> 11) & 0x1F)
|
||||
#define VD(x) (((x) >> 6) & 0x1F)
|
||||
#define E1(x) (((x) >> 7) & 0x0F)
|
||||
#define VD(x) (((x) >> 6) & 0x1F)
|
||||
#define E1(x) (((x) >> 7) & 0x0F)
|
||||
#define E2(x) (((x) >> 21) & 0x0F)
|
||||
#define ELEMENT_INDEX(i) (7 - (i))
|
||||
#define BYTE_INDEX(i) (15 - (i))
|
||||
#define BYTE_INDEX(i) (15 - (i))
|
||||
|
||||
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
|
||||
#define ABI_WINDOWS
|
||||
#else
|
||||
#define ABI_UNIX
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
AudioSettings::AudioSettings(nlohmann::json& settings) : settings(settings), QWidget(nullptr) {
|
||||
AudioSettings::AudioSettings(nlohmann::json &settings) : settings(settings), QWidget(nullptr) {
|
||||
lockChannels->setChecked(JSONGetField<bool>(settings, "audio", "lock"));
|
||||
volumeL->setValue(JSONGetField<float>(settings, "audio", "volumeL") * 100);
|
||||
volumeR->setValue(JSONGetField<float>(settings, "audio", "volumeR") * 100);
|
||||
@@ -34,12 +34,12 @@ AudioSettings::AudioSettings(nlohmann::json& settings) : settings(settings), QWi
|
||||
emit modified();
|
||||
});
|
||||
|
||||
QLabel* labelLock = new QLabel("Lock channels:");
|
||||
QLabel* labelL = new QLabel("Volume L");
|
||||
QLabel* labelR = new QLabel("Volume R");
|
||||
auto labelLock = new QLabel("Lock channels:");
|
||||
auto labelL = new QLabel("Volume L");
|
||||
auto labelR = new QLabel("Volume R");
|
||||
|
||||
QVBoxLayout* mainLayout = new QVBoxLayout;
|
||||
QHBoxLayout* volLayout = new QHBoxLayout;
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
auto volLayout = new QHBoxLayout;
|
||||
mainLayout->addWidget(labelLock);
|
||||
mainLayout->addWidget(lockChannels);
|
||||
volLayout->addWidget(labelL);
|
||||
@@ -49,4 +49,4 @@ AudioSettings::AudioSettings(nlohmann::json& settings) : settings(settings), QWi
|
||||
mainLayout->addLayout(volLayout);
|
||||
mainLayout->addStretch();
|
||||
setLayout(mainLayout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
#pragma once
|
||||
#include <QWidget>
|
||||
#include <QSlider>
|
||||
#include <QCheckBox>
|
||||
#include <JSONUtils.hpp>
|
||||
#include <QCheckBox>
|
||||
#include <QSlider>
|
||||
#include <QWidget>
|
||||
|
||||
class AudioSettings : public QWidget {
|
||||
QCheckBox* lockChannels = new QCheckBox;
|
||||
QCheckBox *lockChannels = new QCheckBox;
|
||||
Q_OBJECT
|
||||
public:
|
||||
QSlider* volumeL = new QSlider(Qt::Horizontal), * volumeR = new QSlider(Qt::Horizontal);
|
||||
AudioSettings(nlohmann::json&);
|
||||
nlohmann::json& settings;
|
||||
QSlider *volumeL = new QSlider(Qt::Horizontal), *volumeR = new QSlider(Qt::Horizontal);
|
||||
AudioSettings(nlohmann::json &);
|
||||
nlohmann::json &settings;
|
||||
Q_SIGNALS:
|
||||
void modified();
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#include <CPUSettings.hpp>
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <JSONUtils.hpp>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
#include <log.hpp>
|
||||
|
||||
CPUSettings::CPUSettings(nlohmann::json& settings) : settings(settings), QWidget(nullptr) {
|
||||
cpuTypes->addItems({ "Interpreter", "Dynamic Recompiler" });
|
||||
CPUSettings::CPUSettings(nlohmann::json &settings) : settings(settings), QWidget(nullptr) {
|
||||
cpuTypes->addItems({"Interpreter", "Dynamic Recompiler"});
|
||||
|
||||
if (JSONGetField<std::string>(settings, "cpu", "type") == "jit") {
|
||||
cpuTypes->setCurrentIndex(1);
|
||||
@@ -25,11 +25,11 @@ CPUSettings::CPUSettings(nlohmann::json& settings) : settings(settings), QWidget
|
||||
emit modified();
|
||||
});
|
||||
|
||||
QLabel* label = new QLabel("CPU type:");
|
||||
auto label = new QLabel("CPU type:");
|
||||
|
||||
QVBoxLayout* mainLayout = new QVBoxLayout;
|
||||
auto mainLayout = new QVBoxLayout;
|
||||
mainLayout->addWidget(label);
|
||||
mainLayout->addWidget(cpuTypes);
|
||||
mainLayout->addStretch();
|
||||
setLayout(mainLayout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#pragma once
|
||||
#include <QWidget>
|
||||
#include <QComboBox>
|
||||
#include <JSONUtils.hpp>
|
||||
#include <QComboBox>
|
||||
#include <QWidget>
|
||||
|
||||
class CPUSettings : public QWidget {
|
||||
QComboBox* cpuTypes = new QComboBox;
|
||||
QComboBox *cpuTypes = new QComboBox;
|
||||
Q_OBJECT
|
||||
public:
|
||||
CPUSettings(nlohmann::json&);
|
||||
nlohmann::json& settings;
|
||||
CPUSettings(nlohmann::json &);
|
||||
nlohmann::json &settings;
|
||||
Q_SIGNALS:
|
||||
void modified();
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
#include <EmuThread.hpp>
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
EmuThread::EmuThread(const std::shared_ptr<QtInstanceFactory>& instance_, const std::shared_ptr<Vulkan::WSIPlatform>& wsiPlatform_, const std::shared_ptr<ParallelRDP::WindowInfo>& windowInfo_, SettingsWindow& settings) noexcept
|
||||
: instance(instance_), wsiPlatform(wsiPlatform_),
|
||||
windowInfo(windowInfo_),
|
||||
core(parallel), settings(settings) {}
|
||||
EmuThread::EmuThread(const std::shared_ptr<QtInstanceFactory> &instance_,
|
||||
const std::shared_ptr<Vulkan::WSIPlatform> &wsiPlatform_,
|
||||
const std::shared_ptr<ParallelRDP::WindowInfo> &windowInfo_, SettingsWindow &settings) noexcept :
|
||||
instance(instance_), wsiPlatform(wsiPlatform_), windowInfo(windowInfo_), core(parallel), settings(settings) {}
|
||||
|
||||
[[noreturn]] void EmuThread::run() noexcept {
|
||||
parallel.Init(instance, wsiPlatform, windowInfo, core.cpu->GetMem().GetRDRAMPtr());
|
||||
SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
|
||||
bool controllerConnected = false;
|
||||
|
||||
if(SDL_GameControllerAddMappingsFromFile("resources/gamecontrollerdb.txt") < 0) {
|
||||
if (SDL_GameControllerAddMappingsFromFile("resources/gamecontrollerdb.txt") < 0) {
|
||||
Util::warn("[SDL] Could not load game controller DB");
|
||||
}
|
||||
|
||||
auto pollEvents = [&]() {
|
||||
SDL_Event e;
|
||||
while(SDL_PollEvent(&e)) {
|
||||
switch(e.type) {
|
||||
case SDL_CONTROLLERDEVICEADDED: {
|
||||
while (SDL_PollEvent(&e)) {
|
||||
switch (e.type) {
|
||||
case SDL_CONTROLLERDEVICEADDED:
|
||||
{
|
||||
const int index = e.cdevice.which;
|
||||
controller = SDL_GameControllerOpen(index);
|
||||
Util::info("Found controller!");
|
||||
@@ -30,11 +31,14 @@ EmuThread::EmuThread(const std::shared_ptr<QtInstanceFactory>& instance_, const
|
||||
Util::info("\tSerial: {}", serial ? serial : "Not available");
|
||||
Util::info("\tPath: {}", path ? path : "Not available");
|
||||
controllerConnected = true;
|
||||
} break;
|
||||
case SDL_CONTROLLERDEVICEREMOVED: {
|
||||
}
|
||||
break;
|
||||
case SDL_CONTROLLERDEVICEREMOVED:
|
||||
{
|
||||
controllerConnected = false;
|
||||
SDL_GameControllerClose(controller);
|
||||
} break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -42,36 +46,48 @@ EmuThread::EmuThread(const std::shared_ptr<QtInstanceFactory>& instance_, const
|
||||
while (true) {
|
||||
if (!core.pause) {
|
||||
core.Run(settings.getVolumeL(), settings.getVolumeR());
|
||||
if(core.render) {
|
||||
if (core.render) {
|
||||
parallel.UpdateScreen(core.cpu->GetMem().mmio.vi);
|
||||
}
|
||||
} else {
|
||||
if(core.render) {
|
||||
if (core.render) {
|
||||
parallel.UpdateScreen(core.cpu->GetMem().mmio.vi, true);
|
||||
}
|
||||
}
|
||||
|
||||
pollEvents();
|
||||
|
||||
if(controllerConnected) {
|
||||
n64::PIF& pif = core.cpu->GetMem().mmio.si.pif;
|
||||
if (controllerConnected) {
|
||||
n64::PIF &pif = core.cpu->GetMem().mmio.si.pif;
|
||||
pif.UpdateButton(0, n64::Controller::Key::A, SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_A));
|
||||
pif.UpdateButton(0, n64::Controller::Key::B, SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_X));
|
||||
pif.UpdateButton(0, n64::Controller::Key::Z, SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT) == SDL_JOYSTICK_AXIS_MAX);
|
||||
pif.UpdateButton(0, n64::Controller::Key::Start, SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_START));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DUp, SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_UP));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DDown, SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_DOWN));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DLeft, SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_LEFT));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DRight, SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_RIGHT));
|
||||
pif.UpdateButton(0, n64::Controller::Key::LT, SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_LEFTSHOULDER));
|
||||
pif.UpdateButton(0, n64::Controller::Key::RT, SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER));
|
||||
pif.UpdateButton(0, n64::Controller::Key::CUp, SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY) <= -127);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CDown, SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY) >= 127);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CLeft, SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX) <= -127);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CRight, SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX) >= 127);
|
||||
pif.UpdateButton(0, n64::Controller::Key::Z,
|
||||
SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT) == SDL_JOYSTICK_AXIS_MAX);
|
||||
pif.UpdateButton(0, n64::Controller::Key::Start,
|
||||
SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_START));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DUp,
|
||||
SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_UP));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DDown,
|
||||
SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_DOWN));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DLeft,
|
||||
SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_LEFT));
|
||||
pif.UpdateButton(0, n64::Controller::Key::DRight,
|
||||
SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_DPAD_RIGHT));
|
||||
pif.UpdateButton(0, n64::Controller::Key::LT,
|
||||
SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_LEFTSHOULDER));
|
||||
pif.UpdateButton(0, n64::Controller::Key::RT,
|
||||
SDL_GameControllerGetButton(controller, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER));
|
||||
pif.UpdateButton(0, n64::Controller::Key::CUp,
|
||||
SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY) <= -127);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CDown,
|
||||
SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY) >= 127);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CLeft,
|
||||
SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX) <= -127);
|
||||
pif.UpdateButton(0, n64::Controller::Key::CRight,
|
||||
SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX) >= 127);
|
||||
|
||||
float xclamped = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
|
||||
if(xclamped < 0) {
|
||||
if (xclamped < 0) {
|
||||
xclamped /= float(std::abs(SDL_JOYSTICK_AXIS_MIN));
|
||||
} else {
|
||||
xclamped /= SDL_JOYSTICK_AXIS_MAX;
|
||||
@@ -80,7 +96,7 @@ EmuThread::EmuThread(const std::shared_ptr<QtInstanceFactory>& instance_, const
|
||||
xclamped *= 86;
|
||||
|
||||
float yclamped = SDL_GameControllerGetAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
|
||||
if(yclamped < 0) {
|
||||
if (yclamped < 0) {
|
||||
yclamped /= float(std::abs(SDL_JOYSTICK_AXIS_MIN));
|
||||
} else {
|
||||
yclamped /= SDL_JOYSTICK_AXIS_MAX;
|
||||
@@ -92,4 +108,4 @@ EmuThread::EmuThread(const std::shared_ptr<QtInstanceFactory>& instance_, const
|
||||
pif.UpdateAxis(0, n64::Controller::Axis::X, xclamped);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,32 @@
|
||||
#pragma once
|
||||
#include <RenderWidget.hpp>
|
||||
#include <QThread>
|
||||
#include <Core.hpp>
|
||||
#include <QThread>
|
||||
#include <RenderWidget.hpp>
|
||||
#include <SDL2/SDL_gamecontroller.h>
|
||||
#include <SettingsWindow.hpp>
|
||||
#include <memory>
|
||||
#include <SDL2/SDL_gamecontroller.h>
|
||||
|
||||
class EmuThread : public QThread
|
||||
{
|
||||
class EmuThread : public QThread {
|
||||
Q_OBJECT
|
||||
std::shared_ptr<QtInstanceFactory> instance;
|
||||
std::shared_ptr<Vulkan::WSIPlatform> wsiPlatform;
|
||||
std::shared_ptr<ParallelRDP::WindowInfo> windowInfo;
|
||||
|
||||
public:
|
||||
explicit EmuThread(const std::shared_ptr<QtInstanceFactory>& instance, const std::shared_ptr<Vulkan::WSIPlatform>& wsiPlatform, const std::shared_ptr<ParallelRDP::WindowInfo>& windowInfo, SettingsWindow&) noexcept;
|
||||
explicit EmuThread(const std::shared_ptr<QtInstanceFactory> &instance,
|
||||
const std::shared_ptr<Vulkan::WSIPlatform> &wsiPlatform,
|
||||
const std::shared_ptr<ParallelRDP::WindowInfo> &windowInfo, SettingsWindow &) noexcept;
|
||||
|
||||
[[noreturn]] void run() noexcept override;
|
||||
|
||||
SDL_GameController* controller = nullptr;
|
||||
SDL_GameController *controller = nullptr;
|
||||
ParallelRDP parallel;
|
||||
n64::Core core;
|
||||
SettingsWindow& settings;
|
||||
SettingsWindow &settings;
|
||||
|
||||
void TogglePause() {
|
||||
core.pause = !core.pause;
|
||||
}
|
||||
void TogglePause() { core.pause = !core.pause; }
|
||||
|
||||
void SetRender(bool v) {
|
||||
core.render = v;
|
||||
}
|
||||
void SetRender(bool v) { core.render = v; }
|
||||
|
||||
void Reset() {
|
||||
core.pause = true;
|
||||
@@ -42,4 +40,4 @@ public:
|
||||
core.pause = true;
|
||||
core.Stop();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
#include <InputSettings.hpp>
|
||||
#include <QVBoxLayout>
|
||||
#include <QKeyEvent>
|
||||
#include <QVBoxLayout>
|
||||
#include <log.hpp>
|
||||
|
||||
InputSettings::InputSettings(nlohmann::json& settings) : settings(settings), QWidget(nullptr) {
|
||||
InputSettings::InputSettings(nlohmann::json &settings) : settings(settings), QWidget(nullptr) {
|
||||
n64_button_labels[0] = new QLabel("A");
|
||||
n64_button_labels[1] = new QLabel("B");
|
||||
n64_button_labels[2] = new QLabel("Z");
|
||||
@@ -22,7 +22,7 @@ InputSettings::InputSettings(nlohmann::json& settings) : settings(settings), QWi
|
||||
n64_button_labels[15] = new QLabel("Analog Down");
|
||||
n64_button_labels[16] = new QLabel("Analog Left");
|
||||
n64_button_labels[17] = new QLabel("Analog Right");
|
||||
|
||||
|
||||
auto str = JSONGetField<std::string>(settings, "input", "A");
|
||||
kb_buttons[0] = new QPushButton(str.c_str());
|
||||
str = JSONGetField<std::string>(settings, "input", "B");
|
||||
@@ -132,10 +132,11 @@ InputSettings::InputSettings(nlohmann::json& settings) : settings(settings), QWi
|
||||
}
|
||||
|
||||
|
||||
void InputSettings::keyPressEvent(QKeyEvent* e) {
|
||||
void InputSettings::keyPressEvent(QKeyEvent *e) {
|
||||
if (grabbing) {
|
||||
auto k = QKeySequence(e->key()).toString();
|
||||
JSONSetField<std::string>(settings, "input", n64_button_labels[which_grabbing]->text().toStdString(), k.toStdString());
|
||||
JSONSetField<std::string>(settings, "input", n64_button_labels[which_grabbing]->text().toStdString(),
|
||||
k.toStdString());
|
||||
kb_buttons[which_grabbing]->setText(k);
|
||||
grabbing = false;
|
||||
which_grabbing = -1;
|
||||
@@ -155,4 +156,4 @@ std::array<Qt::Key, 18> InputSettings::GetMappedKeys() {
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
#pragma once
|
||||
#include <QWidget>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <JSONUtils.hpp>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
#include <QWidget>
|
||||
|
||||
class InputSettings : public QWidget {
|
||||
bool grabbing = false;
|
||||
int which_grabbing = -1;
|
||||
QPushButton* kb_buttons[18];
|
||||
QLabel* n64_button_labels[18];
|
||||
QPushButton *kb_buttons[18];
|
||||
QLabel *n64_button_labels[18];
|
||||
Q_OBJECT
|
||||
public:
|
||||
InputSettings(nlohmann::json&);
|
||||
nlohmann::json& settings;
|
||||
void keyPressEvent(QKeyEvent*) override;
|
||||
InputSettings(nlohmann::json &);
|
||||
nlohmann::json &settings;
|
||||
void keyPressEvent(QKeyEvent *) override;
|
||||
std::array<Qt::Key, 18> GetMappedKeys();
|
||||
Q_SIGNALS:
|
||||
void modified();
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#pragma once
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
static inline nlohmann::json JSONOpenOrCreate(const std::string& path) {
|
||||
static inline nlohmann::json JSONOpenOrCreate(const std::string &path) {
|
||||
auto fileExists = fs::exists(path);
|
||||
|
||||
|
||||
if (fileExists) {
|
||||
auto file = std::fstream(path, std::fstream::in | std::fstream::out);
|
||||
auto json = nlohmann::json::parse(file);
|
||||
@@ -50,11 +50,12 @@ static inline nlohmann::json JSONOpenOrCreate(const std::string& path) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline void JSONSetField(nlohmann::json& json, const std::string& field1, const std::string& field2, const T& value) {
|
||||
static inline void JSONSetField(nlohmann::json &json, const std::string &field1, const std::string &field2,
|
||||
const T &value) {
|
||||
json[field1][field2] = value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static inline T JSONGetField(nlohmann::json& json, const std::string& field1, const std::string& field2) {
|
||||
static inline T JSONGetField(nlohmann::json &json, const std::string &field1, const std::string &field2) {
|
||||
return json[field1][field2].get<T>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,18 @@
|
||||
#include <KaizenQt.hpp>
|
||||
#include <QMessageBox>
|
||||
#include <QApplication>
|
||||
#include <QDropEvent>
|
||||
#include <QMessageBox>
|
||||
#include <QMimeData>
|
||||
#include <filesystem>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
KaizenQt::KaizenQt() noexcept :
|
||||
QWidget(nullptr) {
|
||||
KaizenQt::KaizenQt() noexcept : QWidget(nullptr) {
|
||||
mainWindow = std::make_unique<MainWindowController>();
|
||||
settingsWindow = std::make_unique<SettingsWindow>();
|
||||
emuThread = std::make_unique<EmuThread>(
|
||||
std::move(mainWindow->view.vulkanWidget->instance),
|
||||
std::move(mainWindow->view.vulkanWidget->wsiPlatform),
|
||||
std::move(mainWindow->view.vulkanWidget->windowInfo),
|
||||
*settingsWindow);
|
||||
emuThread = std::make_unique<EmuThread>(std::move(mainWindow->view.vulkanWidget->instance),
|
||||
std::move(mainWindow->view.vulkanWidget->wsiPlatform),
|
||||
std::move(mainWindow->view.vulkanWidget->windowInfo), *settingsWindow);
|
||||
|
||||
ConnectMainWindowSignalsToSlots();
|
||||
|
||||
@@ -25,22 +22,16 @@ KaizenQt::KaizenQt() noexcept :
|
||||
grabKeyboard();
|
||||
mainWindow->show();
|
||||
settingsWindow->hide();
|
||||
connect(settingsWindow.get(), &SettingsWindow::regrabKeyboard, this, [&]() {
|
||||
grabKeyboard();
|
||||
});
|
||||
connect(settingsWindow.get(), &SettingsWindow::regrabKeyboard, this, [&]() { grabKeyboard(); });
|
||||
}
|
||||
|
||||
void KaizenQt::ConnectMainWindowSignalsToSlots() noexcept {
|
||||
connect(mainWindow.get(), &MainWindowController::OpenSettings, this, [this]() {
|
||||
settingsWindow->show();
|
||||
});
|
||||
connect(mainWindow.get(), &MainWindowController::OpenSettings, this, [this]() { settingsWindow->show(); });
|
||||
connect(mainWindow.get(), &MainWindowController::OpenROM, this, &KaizenQt::LoadROM);
|
||||
connect(mainWindow.get(), &MainWindowController::Exit, this, &KaizenQt::Quit);
|
||||
connect(mainWindow.get(), &MainWindowController::Reset, emuThread.get(), &EmuThread::Reset);
|
||||
connect(mainWindow.get(), &MainWindowController::Stop, emuThread.get(), &EmuThread::Stop);
|
||||
connect(mainWindow.get(), &MainWindowController::Stop, this, [this]() {
|
||||
mainWindow->setWindowTitle("Kaizen");
|
||||
});
|
||||
connect(mainWindow.get(), &MainWindowController::Stop, this, [this]() { mainWindow->setWindowTitle("Kaizen"); });
|
||||
connect(mainWindow.get(), &MainWindowController::Pause, emuThread.get(), &EmuThread::TogglePause);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
#pragma once
|
||||
#include <Discord.hpp>
|
||||
#include <EmuThread.hpp>
|
||||
#include <MainWindow.hpp>
|
||||
#include <SettingsWindow.hpp>
|
||||
|
||||
enum class CompositorCategory {
|
||||
Windows, MacOS, XCB, Wayland
|
||||
};
|
||||
enum class CompositorCategory { Windows, MacOS, XCB, Wayland };
|
||||
|
||||
static inline CompositorCategory GetOSCompositorCategory() {
|
||||
const QString platform_name = QGuiApplication::platformName();
|
||||
@@ -13,8 +12,7 @@ static inline CompositorCategory GetOSCompositorCategory() {
|
||||
return CompositorCategory::Windows;
|
||||
else if (platform_name == QStringLiteral("xcb"))
|
||||
return CompositorCategory::XCB;
|
||||
else if (platform_name == QStringLiteral("wayland") ||
|
||||
platform_name == QStringLiteral("wayland-egl"))
|
||||
else if (platform_name == QStringLiteral("wayland") || platform_name == QStringLiteral("wayland-egl"))
|
||||
return CompositorCategory::Wayland;
|
||||
else if (platform_name == QStringLiteral("cocoa") || platform_name == QStringLiteral("ios"))
|
||||
return CompositorCategory::MacOS;
|
||||
@@ -27,16 +25,17 @@ class KaizenQt : public QWidget {
|
||||
Q_OBJECT
|
||||
public:
|
||||
KaizenQt() noexcept;
|
||||
void LoadTAS(const QString& path) const noexcept;
|
||||
void LoadROM(const QString& path) noexcept;
|
||||
void dropEvent(QDropEvent*) override;
|
||||
void dragEnterEvent(QDragEnterEvent*) override;
|
||||
void keyPressEvent(QKeyEvent*) override;
|
||||
void keyReleaseEvent(QKeyEvent*) override;
|
||||
void LoadTAS(const QString &path) const noexcept;
|
||||
void LoadROM(const QString &path) noexcept;
|
||||
void dropEvent(QDropEvent *) override;
|
||||
void dragEnterEvent(QDragEnterEvent *) override;
|
||||
void keyPressEvent(QKeyEvent *) override;
|
||||
void keyReleaseEvent(QKeyEvent *) override;
|
||||
|
||||
private:
|
||||
void Quit() noexcept;
|
||||
void ConnectMainWindowSignalsToSlots() noexcept;
|
||||
std::unique_ptr<MainWindowController> mainWindow;
|
||||
std::unique_ptr<SettingsWindow> settingsWindow;
|
||||
std::unique_ptr<EmuThread> emuThread;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
#include <QKeyEvent>
|
||||
#include <MainWindow.hpp>
|
||||
#include <QFileDialog>
|
||||
#include <QKeyEvent>
|
||||
#include <QMessageBox>
|
||||
#include <QSlider>
|
||||
|
||||
MainWindowController::MainWindowController() noexcept {
|
||||
@@ -15,7 +15,8 @@ MainWindowController::MainWindowController() noexcept {
|
||||
|
||||
void MainWindowController::ConnectSignalsToSlots() noexcept {
|
||||
connect(view.actionOpen, &QAction::triggered, this, [this]() {
|
||||
QString file_name = QFileDialog::getOpenFileName(this, "Nintendo 64 executable", QString(),
|
||||
QString file_name = QFileDialog::getOpenFileName(
|
||||
this, "Nintendo 64 executable", QString(),
|
||||
"All supported types (*.zip *.ZIP *.7z *.7Z *.rar *.RAR *.tar *.TAR *.n64 *.N64 *.v64 *.V64 *.z64 *.Z64)");
|
||||
|
||||
if (!file_name.isEmpty()) {
|
||||
@@ -24,17 +25,11 @@ void MainWindowController::ConnectSignalsToSlots() noexcept {
|
||||
}
|
||||
});
|
||||
|
||||
connect(view.actionExit, &QAction::triggered, this, [this]() {
|
||||
emit Exit();
|
||||
});
|
||||
connect(view.actionExit, &QAction::triggered, this, [this]() { emit Exit(); });
|
||||
|
||||
connect(this, &MainWindowController::destroyed, this, [this]() {
|
||||
emit Exit();
|
||||
});
|
||||
connect(this, &MainWindowController::destroyed, this, [this]() { emit Exit(); });
|
||||
|
||||
connect(view.actionReset, &QAction::triggered, this, [this]() {
|
||||
emit Reset();
|
||||
});
|
||||
connect(view.actionReset, &QAction::triggered, this, [this]() { emit Reset(); });
|
||||
|
||||
connect(view.actionStop, &QAction::triggered, this, [this]() {
|
||||
view.vulkanWidget->hide();
|
||||
@@ -51,15 +46,12 @@ void MainWindowController::ConnectSignalsToSlots() noexcept {
|
||||
});
|
||||
|
||||
connect(view.actionAbout, &QAction::triggered, this, [this]() {
|
||||
QMessageBox::about(
|
||||
this, tr("About Kaizen"),
|
||||
tr("Kaizen is a Nintendo 64 emulator that strives to offer a friendly user "
|
||||
"experience and great compatibility.\n"
|
||||
"Kaizen is licensed under the BSD 3-clause license.\n"
|
||||
"Nintendo 64 is a registered trademarks of Nintendo Co., Ltd."));
|
||||
QMessageBox::about(this, tr("About Kaizen"),
|
||||
tr("Kaizen is a Nintendo 64 emulator that strives to offer a friendly user "
|
||||
"experience and great compatibility.\n"
|
||||
"Kaizen is licensed under the BSD 3-clause license.\n"
|
||||
"Nintendo 64 is a registered trademarks of Nintendo Co., Ltd."));
|
||||
});
|
||||
|
||||
connect(view.actionSettings, &QAction::triggered, this, [this]() {
|
||||
emit OpenSettings();
|
||||
});
|
||||
}
|
||||
connect(view.actionSettings, &QAction::triggered, this, [this]() { emit OpenSettings(); });
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
#pragma once
|
||||
#include "ui_mainwindow.h"
|
||||
#include <RenderWidget.hpp>
|
||||
#include <QMainWindow>
|
||||
#include <QApplication>
|
||||
#include <QMainWindow>
|
||||
#include <RenderWidget.hpp>
|
||||
#include "ui_mainwindow.h"
|
||||
|
||||
class MainWindowController : public QMainWindow
|
||||
{
|
||||
class MainWindowController : public QMainWindow {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MainWindowController() noexcept;
|
||||
|
||||
Ui::MainWindow view;
|
||||
|
||||
private:
|
||||
void ConnectSignalsToSlots() noexcept;
|
||||
|
||||
@@ -19,9 +19,9 @@ private:
|
||||
|
||||
Q_SIGNALS:
|
||||
void OpenSettings();
|
||||
void OpenROM(const QString& rom_file);
|
||||
void OpenROM(const QString &rom_file);
|
||||
void Exit();
|
||||
void Reset();
|
||||
void Stop();
|
||||
void Pause();
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <RenderWidget.hpp>
|
||||
#include <KaizenQt.hpp>
|
||||
#include <RenderWidget.hpp>
|
||||
|
||||
RenderWidget::RenderWidget(QWidget* parent) : QWidget(parent) {
|
||||
RenderWidget::RenderWidget(QWidget *parent) : QWidget(parent) {
|
||||
setAttribute(Qt::WA_NativeWindow);
|
||||
setAttribute(Qt::WA_PaintOnScreen);
|
||||
if (GetOSCompositorCategory() == CompositorCategory::Wayland) {
|
||||
@@ -10,12 +10,11 @@ RenderWidget::RenderWidget(QWidget* parent) : QWidget(parent) {
|
||||
|
||||
if (GetOSCompositorCategory() == CompositorCategory::MacOS) {
|
||||
windowHandle()->setSurfaceType(QWindow::MetalSurface);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
windowHandle()->setSurfaceType(QWindow::VulkanSurface);
|
||||
}
|
||||
|
||||
if(!Vulkan::Context::init_loader(nullptr)) {
|
||||
if (!Vulkan::Context::init_loader(nullptr)) {
|
||||
Util::panic("Could not initialize Vulkan ICD");
|
||||
}
|
||||
|
||||
@@ -25,4 +24,4 @@ RenderWidget::RenderWidget(QWidget* parent) : QWidget(parent) {
|
||||
|
||||
wsiPlatform = std::make_unique<QtWSIPlatform>(windowHandle());
|
||||
windowInfo = std::make_unique<QtParallelRdpWindowInfo>(windowHandle());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
#pragma once
|
||||
#undef signals
|
||||
#include <ParallelRDPWrapper.hpp>
|
||||
#include <QWidget>
|
||||
#include <QWindow>
|
||||
#include <QVulkanInstance>
|
||||
#include <QVulkanWindow>
|
||||
#include <QWidget>
|
||||
#include <QWindow>
|
||||
|
||||
struct QtInstanceFactory : Vulkan::InstanceFactory {
|
||||
VkInstance create_instance(const VkInstanceCreateInfo *info) override {
|
||||
qVkInstance.setApiVersion({1,3,0});
|
||||
qVkInstance.setApiVersion({1, 3, 0});
|
||||
QByteArrayList exts;
|
||||
for(int i = 0; i < info->enabledExtensionCount; i++) {
|
||||
for (int i = 0; i < info->enabledExtensionCount; i++) {
|
||||
exts.push_back(QByteArray::fromStdString(info->ppEnabledExtensionNames[i]));
|
||||
}
|
||||
QByteArrayList layers;
|
||||
for(int i = 0; i < info->enabledLayerCount; i++) {
|
||||
for (int i = 0; i < info->enabledLayerCount; i++) {
|
||||
layers.push_back(QByteArray::fromStdString(info->ppEnabledLayerNames[i]));
|
||||
}
|
||||
qVkInstance.setExtensions(exts);
|
||||
qVkInstance.setLayers(layers);
|
||||
qVkInstance.setApiVersion({1,3,0});
|
||||
qVkInstance.setApiVersion({1, 3, 0});
|
||||
qVkInstance.create();
|
||||
|
||||
return qVkInstance.vkInstance();
|
||||
@@ -30,22 +30,23 @@ struct QtInstanceFactory : Vulkan::InstanceFactory {
|
||||
|
||||
class QtParallelRdpWindowInfo : public ParallelRDP::WindowInfo {
|
||||
public:
|
||||
explicit QtParallelRdpWindowInfo(QWindow* window) : window(window) {}
|
||||
explicit QtParallelRdpWindowInfo(QWindow *window) : window(window) {}
|
||||
CoordinatePair get_window_size() override {
|
||||
return CoordinatePair{ static_cast<float>(window->width()), static_cast<float>(window->height()) };
|
||||
return CoordinatePair{static_cast<float>(window->width()), static_cast<float>(window->height())};
|
||||
}
|
||||
|
||||
private:
|
||||
QWindow* window;
|
||||
QWindow *window;
|
||||
};
|
||||
|
||||
class QtWSIPlatform final : public Vulkan::WSIPlatform {
|
||||
public:
|
||||
explicit QtWSIPlatform(QWindow* window) : window(window) {}
|
||||
explicit QtWSIPlatform(QWindow *window) : window(window) {}
|
||||
|
||||
std::vector<const char*> get_instance_extensions() override {
|
||||
auto vec = std::vector<const char*>();
|
||||
std::vector<const char *> get_instance_extensions() override {
|
||||
auto vec = std::vector<const char *>();
|
||||
|
||||
for (const auto& ext : window->vulkanInstance()->supportedExtensions()) {
|
||||
for (const auto &ext : window->vulkanInstance()->supportedExtensions()) {
|
||||
vec.push_back(ext.name);
|
||||
}
|
||||
|
||||
@@ -57,44 +58,32 @@ public:
|
||||
return QVulkanInstance::surfaceForWindow(window);
|
||||
}
|
||||
|
||||
void destroy_surface(VkInstance, VkSurfaceKHR) override { }
|
||||
void destroy_surface(VkInstance, VkSurfaceKHR) override {}
|
||||
|
||||
uint32_t get_surface_width() override {
|
||||
return 640;
|
||||
}
|
||||
uint32_t get_surface_width() override { return 640; }
|
||||
|
||||
uint32_t get_surface_height() override {
|
||||
return 480;
|
||||
}
|
||||
uint32_t get_surface_height() override { return 480; }
|
||||
|
||||
bool alive(Vulkan::WSI&) override {
|
||||
return true;
|
||||
}
|
||||
bool alive(Vulkan::WSI &) override { return true; }
|
||||
|
||||
void poll_input() override {}
|
||||
void poll_input_async(Granite::InputTrackerHandler* handler) override {}
|
||||
void poll_input_async(Granite::InputTrackerHandler *handler) override {}
|
||||
|
||||
void event_frame_tick(double frame, double elapsed) override { }
|
||||
void event_frame_tick(double frame, double elapsed) override {}
|
||||
|
||||
const VkApplicationInfo* get_application_info() override {
|
||||
return &appInfo;
|
||||
}
|
||||
const VkApplicationInfo *get_application_info() override { return &appInfo; }
|
||||
|
||||
VkApplicationInfo appInfo{.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, .apiVersion = VK_API_VERSION_1_3};
|
||||
|
||||
VkApplicationInfo appInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||||
.apiVersion = VK_API_VERSION_1_3
|
||||
};
|
||||
private:
|
||||
QWindow* window;
|
||||
QWindow *window;
|
||||
};
|
||||
|
||||
class RenderWidget : public QWidget {
|
||||
public:
|
||||
explicit RenderWidget(QWidget* parent);
|
||||
explicit RenderWidget(QWidget *parent);
|
||||
|
||||
[[nodiscard]] QPaintEngine* paintEngine() const override {
|
||||
return nullptr;
|
||||
}
|
||||
[[nodiscard]] QPaintEngine *paintEngine() const override { return nullptr; }
|
||||
|
||||
std::unique_ptr<ParallelRDP::WindowInfo> windowInfo;
|
||||
std::unique_ptr<Vulkan::WSIPlatform> wsiPlatform;
|
||||
@@ -102,4 +91,4 @@ public:
|
||||
Q_SIGNALS:
|
||||
void Show() { show(); }
|
||||
void Hide() { hide(); }
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#include <SettingsWindow.hpp>
|
||||
#include <QGroupBox>
|
||||
#include <QVBoxLayout>
|
||||
#include <QButtonGroup>
|
||||
#include <QFileDialog>
|
||||
#include <QGroupBox>
|
||||
#include <QVBoxLayout>
|
||||
#include <SettingsWindow.hpp>
|
||||
#include <fmt/core.h>
|
||||
|
||||
std::string savePath = "";
|
||||
@@ -44,7 +44,7 @@ SettingsWindow::SettingsWindow() : QWidget(nullptr) {
|
||||
generalLayoutV->addStretch();
|
||||
generalSettings->setLayout(generalLayoutV);
|
||||
|
||||
auto* tabs = new QTabWidget;
|
||||
auto *tabs = new QTabWidget;
|
||||
tabs->addTab(generalSettings, tr("General"));
|
||||
tabs->addTab(cpuSettings, tr("CPU"));
|
||||
tabs->addTab(audioSettings, tr("Audio"));
|
||||
@@ -52,17 +52,11 @@ SettingsWindow::SettingsWindow() : QWidget(nullptr) {
|
||||
|
||||
apply->setEnabled(false);
|
||||
|
||||
connect(cpuSettings, &CPUSettings::modified, this, [&]() {
|
||||
apply->setEnabled(true);
|
||||
});
|
||||
connect(cpuSettings, &CPUSettings::modified, this, [&]() { apply->setEnabled(true); });
|
||||
|
||||
connect(audioSettings, &AudioSettings::modified, this, [&]() {
|
||||
apply->setEnabled(true);
|
||||
});
|
||||
connect(audioSettings, &AudioSettings::modified, this, [&]() { apply->setEnabled(true); });
|
||||
|
||||
connect(inputSettings, &InputSettings::modified, this, [&]() {
|
||||
apply->setEnabled(true);
|
||||
});
|
||||
connect(inputSettings, &InputSettings::modified, this, [&]() { apply->setEnabled(true); });
|
||||
|
||||
connect(apply, &QPushButton::pressed, this, [&]() {
|
||||
auto newMap = inputSettings->GetMappedKeys();
|
||||
@@ -78,11 +72,11 @@ SettingsWindow::SettingsWindow() : QWidget(nullptr) {
|
||||
|
||||
connect(cancel, &QPushButton::pressed, this, &QWidget::hide);
|
||||
|
||||
QVBoxLayout* mainLayout = new QVBoxLayout;
|
||||
QHBoxLayout* buttonsLayout = new QHBoxLayout;
|
||||
QVBoxLayout *mainLayout = new QVBoxLayout;
|
||||
QHBoxLayout *buttonsLayout = new QHBoxLayout;
|
||||
buttonsLayout->addWidget(apply);
|
||||
buttonsLayout->addWidget(cancel);
|
||||
mainLayout->addWidget(tabs);
|
||||
mainLayout->addLayout(buttonsLayout);
|
||||
setLayout(mainLayout);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
#pragma once
|
||||
#include <QWidget>
|
||||
#include <QTabWidget>
|
||||
#include <QPushButton>
|
||||
#include <QFileIconProvider>
|
||||
#include <CPUSettings.hpp>
|
||||
#include <AudioSettings.hpp>
|
||||
#include <CPUSettings.hpp>
|
||||
#include <InputSettings.hpp>
|
||||
#include <QFileIconProvider>
|
||||
#include <QPushButton>
|
||||
#include <QTabWidget>
|
||||
#include <QWidget>
|
||||
|
||||
class SettingsWindow : public QWidget {
|
||||
QPushButton* cancel = new QPushButton("Cancel");
|
||||
QPushButton* apply = new QPushButton("Apply");
|
||||
QFileIconProvider* iconProv = new QFileIconProvider;
|
||||
QPushButton* folderBtn = new QPushButton(iconProv->icon(QFileIconProvider::Folder), "");
|
||||
QLabel* folderLabelPrefix;
|
||||
QLabel* folderLabel;
|
||||
QPushButton *cancel = new QPushButton("Cancel");
|
||||
QPushButton *apply = new QPushButton("Apply");
|
||||
QFileIconProvider *iconProv = new QFileIconProvider;
|
||||
QPushButton *folderBtn = new QPushButton(iconProv->icon(QFileIconProvider::Folder), "");
|
||||
QLabel *folderLabelPrefix;
|
||||
QLabel *folderLabel;
|
||||
Q_OBJECT
|
||||
public:
|
||||
float getVolumeL() { return float(audioSettings->volumeL->value()) / 100.f; }
|
||||
@@ -21,10 +21,10 @@ public:
|
||||
std::array<Qt::Key, 18> keyMap{};
|
||||
SettingsWindow();
|
||||
nlohmann::json settings;
|
||||
CPUSettings* cpuSettings;
|
||||
AudioSettings* audioSettings;
|
||||
InputSettings* inputSettings;
|
||||
QWidget* generalSettings;
|
||||
CPUSettings *cpuSettings;
|
||||
AudioSettings *audioSettings;
|
||||
InputSettings *inputSettings;
|
||||
QWidget *generalSettings;
|
||||
Q_SIGNALS:
|
||||
void regrabKeyboard();
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <QApplication>
|
||||
#include <QCommandLineParser>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int main(int argc, char **argv) {
|
||||
QApplication app(argc, argv);
|
||||
app.setStyle("fusion");
|
||||
QCoreApplication::setOrganizationName("kaizen");
|
||||
@@ -10,12 +10,9 @@ int main(int argc, char** argv) {
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(QCoreApplication::applicationName());
|
||||
parser.addHelpOption();
|
||||
parser.addOptions({
|
||||
{"rom", "Rom to launch from command-line", "path"},
|
||||
{"movie", "Mupen Movie to replay", "path"}
|
||||
});
|
||||
parser.addOptions({{"rom", "Rom to launch from command-line", "path"}, {"movie", "Mupen Movie to replay", "path"}});
|
||||
parser.process(app);
|
||||
|
||||
|
||||
KaizenQt kaizenQt;
|
||||
if (parser.isSet("rom")) {
|
||||
kaizenQt.LoadROM(parser.value("rom"));
|
||||
@@ -25,4 +22,4 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,18 +3,18 @@
|
||||
#include <log.hpp>
|
||||
|
||||
namespace Util {
|
||||
FORCE_INLINE std::vector<u8> ReadFileBinary(const std::string& path) {
|
||||
FORCE_INLINE std::vector<u8> ReadFileBinary(const std::string &path) {
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
return {std::istreambuf_iterator{file}, {}};
|
||||
}
|
||||
|
||||
FORCE_INLINE void WriteFileBinary(const std::vector<u8>& data, const std::string& path) {
|
||||
FORCE_INLINE void WriteFileBinary(const std::vector<u8> &data, const std::string &path) {
|
||||
std::ofstream file(path, std::ios::binary);
|
||||
std::copy(data.begin(), data.end(), std::ostreambuf_iterator{file});
|
||||
}
|
||||
|
||||
template <size_t Size>
|
||||
FORCE_INLINE void WriteFileBinary(const std::array<u8, Size>& data, const std::string& path) {
|
||||
FORCE_INLINE void WriteFileBinary(const std::array<u8, Size> &data, const std::string &path) {
|
||||
std::ofstream file(path, std::ios::binary);
|
||||
std::copy(data.begin(), data.end(), std::ostreambuf_iterator{file});
|
||||
}
|
||||
@@ -30,4 +30,4 @@ FORCE_INLINE size_t NextPow2(size_t num) {
|
||||
num |= num >> 16;
|
||||
return num + 1;
|
||||
}
|
||||
}
|
||||
} // namespace Util
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include <common.hpp>
|
||||
#include <cmath>
|
||||
#include <common.hpp>
|
||||
#include <immintrin.h>
|
||||
|
||||
namespace Util {
|
||||
@@ -52,7 +52,7 @@ static inline T roundNearest(double f) {
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
static inline T roundCurrent(float f) {
|
||||
#ifdef SIMD_SUPPORT
|
||||
auto t = _mm_set_ss(f);
|
||||
@@ -63,7 +63,7 @@ static inline T roundCurrent(float f) {
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
static inline T roundCurrent(double f) {
|
||||
#ifdef SIMD_SUPPORT
|
||||
auto t = _mm_set_sd(f);
|
||||
@@ -78,11 +78,11 @@ static inline T roundCurrent(double f) {
|
||||
template <typename T>
|
||||
static inline T roundFloor(float f) {
|
||||
#ifdef SIMD_SUPPORT
|
||||
__m128 t = _mm_set_ss(f);
|
||||
__m128 t = _mm_set_ss(f);
|
||||
t = _mm_round_ss(t, t, _MM_FROUND_TO_NEG_INF);
|
||||
return _mm_cvtss_f32(t);
|
||||
#else
|
||||
return floor(f);
|
||||
return floor(f);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -118,4 +118,4 @@ static inline T roundTrunc(double f) {
|
||||
return trunc(f);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} // namespace Util
|
||||
|
||||
@@ -1,113 +1,113 @@
|
||||
#pragma once
|
||||
#include <common.hpp>
|
||||
#include <cstring>
|
||||
#include <portable_endian_bswap.h>
|
||||
#include <log.hpp>
|
||||
#include <functional>
|
||||
#include <log.hpp>
|
||||
#include <portable_endian_bswap.h>
|
||||
|
||||
namespace Util {
|
||||
template<typename T>
|
||||
static FORCE_INLINE T ReadAccess(const u8* data, u32 index) {
|
||||
template <typename T>
|
||||
static FORCE_INLINE T ReadAccess(const u8 *data, u32 index) {
|
||||
if constexpr (sizeof(T) == 8) {
|
||||
u32 hi = *reinterpret_cast<const u32*>(&data[index + 0]);
|
||||
u32 lo = *reinterpret_cast<const u32*>(&data[index + 4]);
|
||||
u32 hi = *reinterpret_cast<const u32 *>(&data[index + 0]);
|
||||
u32 lo = *reinterpret_cast<const u32 *>(&data[index + 4]);
|
||||
T result = ((T)hi << 32) | (T)lo;
|
||||
return result;
|
||||
} else {
|
||||
return *reinterpret_cast<const T*>(&data[index]);
|
||||
return *reinterpret_cast<const T *>(&data[index]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static FORCE_INLINE T ReadAccess(const std::vector<u8>& data, u32 index) {
|
||||
template <typename T>
|
||||
static FORCE_INLINE T ReadAccess(const std::vector<u8> &data, u32 index) {
|
||||
if constexpr (sizeof(T) == 8) {
|
||||
u32 hi = *reinterpret_cast<const u32*>(&data[index + 0]);
|
||||
u32 lo = *reinterpret_cast<const u32*>(&data[index + 4]);
|
||||
u32 hi = *reinterpret_cast<const u32 *>(&data[index + 0]);
|
||||
u32 lo = *reinterpret_cast<const u32 *>(&data[index + 4]);
|
||||
T result = ((T)hi << 32) | (T)lo;
|
||||
return result;
|
||||
} else {
|
||||
return *reinterpret_cast<const T*>(&data[index]);
|
||||
return *reinterpret_cast<const T *>(&data[index]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, size_t Size>
|
||||
static FORCE_INLINE T ReadAccess(const std::array<u8, Size>& data, u32 index) {
|
||||
template <typename T, size_t Size>
|
||||
static FORCE_INLINE T ReadAccess(const std::array<u8, Size> &data, u32 index) {
|
||||
if constexpr (sizeof(T) == 8) {
|
||||
u32 hi = *reinterpret_cast<const u32*>(&data[index + 0]);
|
||||
u32 lo = *reinterpret_cast<const u32*>(&data[index + 4]);
|
||||
u32 hi = *reinterpret_cast<const u32 *>(&data[index + 0]);
|
||||
u32 lo = *reinterpret_cast<const u32 *>(&data[index + 4]);
|
||||
T result = ((T)hi << 32) | (T)lo;
|
||||
return result;
|
||||
} else {
|
||||
return *reinterpret_cast<const T*>(&data[index]);
|
||||
return *reinterpret_cast<const T *>(&data[index]);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, size_t Size>
|
||||
static FORCE_INLINE void WriteAccess(std::array<u8, Size>& data, u32 index, T val) {
|
||||
template <typename T, size_t Size>
|
||||
static FORCE_INLINE void WriteAccess(std::array<u8, Size> &data, u32 index, T val) {
|
||||
if constexpr (sizeof(T) == 8) {
|
||||
u32 hi = val >> 32;
|
||||
u32 lo = val;
|
||||
|
||||
*reinterpret_cast<u32*>(&data[index + 0]) = hi;
|
||||
*reinterpret_cast<u32*>(&data[index + 4]) = lo;
|
||||
*reinterpret_cast<u32 *>(&data[index + 0]) = hi;
|
||||
*reinterpret_cast<u32 *>(&data[index + 4]) = lo;
|
||||
} else {
|
||||
*reinterpret_cast<T*>(&data[index]) = val;
|
||||
*reinterpret_cast<T *>(&data[index]) = val;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static FORCE_INLINE void WriteAccess(std::vector<u8>& data, u32 index, T val) {
|
||||
template <typename T>
|
||||
static FORCE_INLINE void WriteAccess(std::vector<u8> &data, u32 index, T val) {
|
||||
if constexpr (sizeof(T) == 8) {
|
||||
u32 hi = val >> 32;
|
||||
u32 lo = val;
|
||||
|
||||
*reinterpret_cast<u32*>(&data[index + 0]) = hi;
|
||||
*reinterpret_cast<u32*>(&data[index + 4]) = lo;
|
||||
*reinterpret_cast<u32 *>(&data[index + 0]) = hi;
|
||||
*reinterpret_cast<u32 *>(&data[index + 4]) = lo;
|
||||
} else {
|
||||
*reinterpret_cast<T*>(&data[index]) = val;
|
||||
*reinterpret_cast<T *>(&data[index]) = val;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
template <typename T>
|
||||
static FORCE_INLINE void WriteAccess(u8 *data, u32 index, T val) {
|
||||
if constexpr (sizeof(T) == 8) {
|
||||
u32 hi = val >> 32;
|
||||
u32 lo = val;
|
||||
|
||||
*reinterpret_cast<u32*>(&data[index + 0]) = hi;
|
||||
*reinterpret_cast<u32*>(&data[index + 4]) = lo;
|
||||
*reinterpret_cast<u32 *>(&data[index + 0]) = hi;
|
||||
*reinterpret_cast<u32 *>(&data[index + 4]) = lo;
|
||||
} else {
|
||||
*reinterpret_cast<T*>(&data[index]) = val;
|
||||
*reinterpret_cast<T *>(&data[index]) = val;
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE void SwapBuffer32(std::vector<u8> &data) {
|
||||
for (size_t i = 0; i < data.size(); i += 4) {
|
||||
u32 original = *(u32 *) &data[i];
|
||||
*(u32 *) &data[i] = bswap_32(original);
|
||||
u32 original = *(u32 *)&data[i];
|
||||
*(u32 *)&data[i] = bswap_32(original);
|
||||
}
|
||||
}
|
||||
|
||||
FORCE_INLINE void SwapBuffer16(std::vector<u8> &data) {
|
||||
for (size_t i = 0; i < data.size(); i += 2) {
|
||||
u16 original = *(u16 *) &data[i];
|
||||
*(u16 *) &data[i] = bswap_16(original);
|
||||
u16 original = *(u16 *)&data[i];
|
||||
*(u16 *)&data[i] = bswap_16(original);
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t Size>
|
||||
FORCE_INLINE void SwapBuffer32(std::array<u8, Size> &data) {
|
||||
for (size_t i = 0; i < Size; i += 4) {
|
||||
u32 original = *(u32 *) &data[i];
|
||||
*(u32 *) &data[i] = bswap_32(original);
|
||||
u32 original = *(u32 *)&data[i];
|
||||
*(u32 *)&data[i] = bswap_32(original);
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t Size>
|
||||
FORCE_INLINE void SwapBuffer16(std::array<u8, Size> &data) {
|
||||
for (size_t i = 0; i < Size; i += 2) {
|
||||
u16 original = *(u16 *) &data[i];
|
||||
*(u16 *) &data[i] = bswap_16(original);
|
||||
u16 original = *(u16 *)&data[i];
|
||||
*(u16 *)&data[i] = bswap_16(original);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,27 +134,19 @@ FORCE_INLINE u32 crc32(u32 crc, const u8 *buf, size_t len) {
|
||||
|
||||
crc = ~crc;
|
||||
for (int i = 0; i < len; i++) {
|
||||
octet = buf[i]; /* Cast to unsigned octet. */
|
||||
octet = buf[i]; /* Cast to unsigned octet. */
|
||||
crc = (crc >> 8) ^ table[(crc & 0xff) ^ octet];
|
||||
}
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
FORCE_INLINE void* aligned_alloc(size_t alignment, size_t size) {
|
||||
return _aligned_malloc(size, alignment);
|
||||
}
|
||||
FORCE_INLINE void *aligned_alloc(size_t alignment, size_t size) { return _aligned_malloc(size, alignment); }
|
||||
|
||||
FORCE_INLINE void aligned_free(void* ptr) {
|
||||
_aligned_free(ptr);
|
||||
}
|
||||
FORCE_INLINE void aligned_free(void *ptr) { _aligned_free(ptr); }
|
||||
#else
|
||||
FORCE_INLINE void* aligned_alloc(size_t alignment, size_t size) {
|
||||
return std::aligned_alloc(alignment, size);
|
||||
}
|
||||
FORCE_INLINE void *aligned_alloc(size_t alignment, size_t size) { return std::aligned_alloc(alignment, size); }
|
||||
|
||||
FORCE_INLINE void aligned_free(void* ptr) {
|
||||
std::free(ptr);
|
||||
}
|
||||
FORCE_INLINE void aligned_free(void *ptr) { std::free(ptr); }
|
||||
#endif
|
||||
}
|
||||
} // namespace Util
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
#pragma once
|
||||
#include <common.hpp>
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/color.h>
|
||||
#include <fmt/format.h>
|
||||
#include <string>
|
||||
#if !defined(NDEBUG) && !defined(_WIN32)
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
namespace Util {
|
||||
enum LogLevel : u8 {
|
||||
Trace, Debug, Warn, Info, Error, Always
|
||||
};
|
||||
enum LogLevel : u8 { Trace, Debug, Warn, Info, Error, Always };
|
||||
|
||||
#ifndef NDEBUG
|
||||
static constexpr auto globalLogLevel = Debug;
|
||||
@@ -18,23 +16,23 @@ static constexpr auto globalLogLevel = Debug;
|
||||
static constexpr auto globalLogLevel = Info;
|
||||
#endif
|
||||
|
||||
template <LogLevel messageType = Info, typename ...Args>
|
||||
constexpr void print(const std::string& fmt, Args... args) {
|
||||
if constexpr(messageType >= globalLogLevel) {
|
||||
template <LogLevel messageType = Info, typename... Args>
|
||||
constexpr void print(const std::string &fmt, Args... args) {
|
||||
if constexpr (messageType >= globalLogLevel) {
|
||||
#ifndef _WIN32
|
||||
if constexpr(messageType == Error) {
|
||||
if constexpr (messageType == Error) {
|
||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red), fmt, args...);
|
||||
} else if constexpr(messageType == Warn) {
|
||||
} else if constexpr (messageType == Warn) {
|
||||
fmt::print(fg(fmt::color::yellow), fmt, args...);
|
||||
} else if constexpr(messageType == Info || messageType == Trace || messageType == Always) {
|
||||
} else if constexpr (messageType == Info || messageType == Trace || messageType == Always) {
|
||||
fmt::print(fmt, args...);
|
||||
} else if constexpr(messageType == Debug) {
|
||||
} else if constexpr (messageType == Debug) {
|
||||
#ifndef NDEBUG
|
||||
fmt::print(fmt, args...);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
if constexpr(messageType == Debug) {
|
||||
if constexpr (messageType == Debug) {
|
||||
#ifndef NDEBUG
|
||||
fmt::print(fmt, args...);
|
||||
#endif
|
||||
@@ -45,61 +43,61 @@ constexpr void print(const std::string& fmt, Args... args) {
|
||||
}
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
constexpr void panic(const std::string& fmt, Args... args) {
|
||||
template <typename... Args>
|
||||
constexpr void panic(const std::string &fmt, Args... args) {
|
||||
print<Error>("[FATAL] " + fmt + "\n", args...);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
constexpr void error(const std::string& fmt, Args... args) {
|
||||
template <typename... Args>
|
||||
constexpr void error(const std::string &fmt, Args... args) {
|
||||
print<Error>("[ERROR] " + fmt + "\n", args...);
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
constexpr void warn(const std::string& fmt, Args... args) {
|
||||
template <typename... Args>
|
||||
constexpr void warn(const std::string &fmt, Args... args) {
|
||||
print<Warn>("[WARN] " + fmt + "\n", args...);
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
constexpr void info(const std::string& fmt, Args... args) {
|
||||
template <typename... Args>
|
||||
constexpr void info(const std::string &fmt, Args... args) {
|
||||
print("[INFO] " + fmt + "\n", args...);
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
constexpr void debug(const std::string& fmt, Args... args) {
|
||||
template <typename... Args>
|
||||
constexpr void debug(const std::string &fmt, Args... args) {
|
||||
print<Debug>("[DEBUG] " + fmt + "\n", args...);
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
constexpr void trace(const std::string& fmt, Args... args) {
|
||||
template <typename... Args>
|
||||
constexpr void trace(const std::string &fmt, Args... args) {
|
||||
print<Trace>("[TRACE] " + fmt + "\n", args...);
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
constexpr void always(const std::string& fmt, Args... args) {
|
||||
template <typename... Args>
|
||||
constexpr void always(const std::string &fmt, Args... args) {
|
||||
print<Always>(fmt + "\n", args...);
|
||||
}
|
||||
|
||||
template <typename ...Args>
|
||||
constexpr void panic_trace(const std::string& fmt, Args... args) {
|
||||
template <typename... Args>
|
||||
constexpr void panic_trace(const std::string &fmt, Args... args) {
|
||||
#if !defined(NDEBUG) && !defined(_WIN32)
|
||||
Dl_info info;
|
||||
auto tmp = fmt::format(fmt + '\n', args...);
|
||||
tmp += "Called by:\n";
|
||||
if(dladdr(__builtin_return_address(0), &info))
|
||||
if (dladdr(__builtin_return_address(0), &info))
|
||||
tmp += fmt::format("\t-> {}\n", info.dli_sname);
|
||||
if(dladdr(__builtin_return_address(1), &info))
|
||||
if (dladdr(__builtin_return_address(1), &info))
|
||||
tmp += fmt::format("\t-> {}\n", info.dli_sname);
|
||||
if(dladdr(__builtin_return_address(2), &info))
|
||||
if (dladdr(__builtin_return_address(2), &info))
|
||||
tmp += fmt::format("\t-> {}\n", info.dli_sname);
|
||||
if(dladdr(__builtin_return_address(3), &info))
|
||||
if (dladdr(__builtin_return_address(3), &info))
|
||||
tmp += fmt::format("\t-> {}\n", info.dli_sname);
|
||||
if(dladdr(__builtin_return_address(4), &info))
|
||||
if (dladdr(__builtin_return_address(4), &info))
|
||||
tmp += fmt::format("\t-> {}\n", info.dli_sname);
|
||||
if(dladdr(__builtin_return_address(5), &info))
|
||||
if (dladdr(__builtin_return_address(5), &info))
|
||||
tmp += fmt::format("\t-> {}\n", info.dli_sname);
|
||||
if(dladdr(__builtin_return_address(6), &info))
|
||||
if (dladdr(__builtin_return_address(6), &info))
|
||||
tmp += fmt::format("\t-> {}\n", info.dli_sname);
|
||||
|
||||
print<Error>("[FATAL TRACE] " + tmp);
|
||||
@@ -108,4 +106,4 @@ constexpr void panic_trace(const std::string& fmt, Args... args) {
|
||||
panic(fmt, args...);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} // namespace Util
|
||||
|
||||
Reference in New Issue
Block a user