Run clangformat everywhere

This commit is contained in:
SimoneN64
2024-08-27 21:35:07 +02:00
parent b3a4a302cb
commit e253627890
74 changed files with 5536 additions and 4358 deletions

View File

@@ -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

View File

@@ -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 &parallel) : 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 &regs = 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

View File

@@ -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

View File

@@ -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

View File

@@ -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"},
};
}

View File

@@ -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))

View File

@@ -1,7 +1,6 @@
#include <Netplay.hpp>
#include <log.hpp>
#include <PIF.hpp>
#include <array>
#include <log.hpp>
namespace Netplay {
}
namespace Netplay {}

View File

@@ -1,5 +1,4 @@
#pragma once
#include <PIF.hpp>
namespace Netplay {
}
namespace Netplay {}

View File

@@ -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

View File

@@ -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();
}
}
}

View File

@@ -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;

View File

@@ -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 &regs, ParallelRDP &parallel) :
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

View File

@@ -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};

View File

@@ -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 &parallel) : 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

View File

@@ -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 &parallel;
};
} // backend
} // namespace n64

View File

@@ -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:

View File

@@ -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 &regs;
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

View File

@@ -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};

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 &regs) : mem(mem), regs(regs) { }
AI::AI(Mem &mem, Registers &regs) : 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

View File

@@ -7,7 +7,7 @@ struct Mem;
struct Registers;
struct AI {
AI(Mem&, Registers& regs);
AI(Mem &, Registers &regs);
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 &regs;
};
}
} // namespace n64

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -5,9 +5,7 @@
#define MI_VERSION_REG 0x02020102
namespace n64 {
MI::MI(Registers& regs) : regs(regs) {
Reset();
}
MI::MI(Registers &regs) : 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

View File

@@ -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 &regs;
};
}
} // namespace n64

View File

@@ -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 &regs) : 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

View File

@@ -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 &regs;
};
}
} // namespace n64

View File

@@ -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

View File

@@ -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 &regs) : 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 &regs;
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

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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;
};
};

View File

@@ -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

View File

@@ -11,4 +11,4 @@ struct RI {
u32 mode{0xE}, config{0x40}, select{0x14}, refresh{0x63634};
};
}
} // namespace n64

View File

@@ -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 &regs) : 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

View File

@@ -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 &regs;
};
#define SI_DMA_DELAY (65536 * 2)
}
} // namespace n64

View File

@@ -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 &regs) : 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

View File

@@ -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 &regs;
};
} // backend
} // namespace n64

View File

@@ -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 &regs) : 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 = &regs.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

View File

@@ -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 &regs;
[[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

View File

@@ -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 &regs) : 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

View File

@@ -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 &regs;
};
}
} // namespace n64

View File

@@ -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

View File

@@ -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 &regs, 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 &regs, 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

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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();
};
};

View File

@@ -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);
}
}

View File

@@ -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();
};
};

View File

@@ -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);
}
}
}
}

View File

@@ -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();
}
};
};

View File

@@ -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;
}
}

View File

@@ -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();
};
};

View File

@@ -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>();
}
}

View File

@@ -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);
}

View File

@@ -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;
};
};

View File

@@ -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(); });
}

View File

@@ -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();
};
};

View File

@@ -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());
}
}

View File

@@ -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(); }
};
};

View File

@@ -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);
}
}

View File

@@ -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();
};
};

View File

@@ -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();
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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