Introduce game db + fallback to stem of filename + volume control
This commit is contained in:
7
.github/workflows/build.yml
vendored
7
.github/workflows/build.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y build-essential libgtk-3-dev libsdl2-dev libfmt-dev git ninja-build
|
||||
sudo apt-get install -y build-essential libgtk-3-dev libsdl2-dev libfmt-dev git ninja-build nlohmann-json3-dev
|
||||
sudo apt-get install -y vulkan-tools libvulkan1 libvulkan-dev vulkan-validationlayers-dev spirv-tools
|
||||
- name: Build natsukashii
|
||||
run: |
|
||||
@@ -44,7 +44,10 @@ jobs:
|
||||
submodules: recursive
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
install: make git mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-SDL2 mingw-w64-x86_64-vulkan-devel mingw-w64-x86_64-fmt mingw-w64-x86_64-ninja
|
||||
install:
|
||||
make git mingw-w64-x86_64-gcc mingw-w64-x86_64-cmake mingw-w64-x86_64-SDL2
|
||||
mingw-w64-x86_64-vulkan-devel mingw-w64-x86_64-fmt mingw-w64-x86_64-ninja
|
||||
mingw-w64-x86_64-nlohmann-json
|
||||
- name: Build natsukashii
|
||||
run: |
|
||||
cmake \
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,5 @@
|
||||
*.toml
|
||||
*.ini
|
||||
*.json
|
||||
*build*/
|
||||
.idea/
|
||||
roms/
|
||||
|
||||
3110
resources/game_db.json
Normal file
3110
resources/game_db.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -6,7 +6,7 @@ void App::Run() {
|
||||
// Main loop
|
||||
const u8* state = SDL_GetKeyboardState(nullptr);
|
||||
while (!core.done) {
|
||||
core.Run(window);
|
||||
core.Run(window, window.volumeL, window.volumeR);
|
||||
core.UpdateController(state);
|
||||
|
||||
SDL_Event event;
|
||||
@@ -33,7 +33,7 @@ void App::Run() {
|
||||
const nfdu8filteritem_t filter {"Nintendo 64 roms", "n64,z64,v64,N64,Z64,V64"};
|
||||
nfdresult_t result = NFD_OpenDialog(&outpath, &filter, 1, nullptr);
|
||||
if(result == NFD_OKAY) {
|
||||
core.LoadROM(outpath);
|
||||
LoadROM(outpath);
|
||||
NFD_FreePath(outpath);
|
||||
}
|
||||
} break;
|
||||
|
||||
@@ -6,7 +6,7 @@ struct App {
|
||||
~App() = default;
|
||||
void Run();
|
||||
inline void LoadROM(const std::string& path) {
|
||||
core.LoadROM(path);
|
||||
window.LoadROM(core, path);
|
||||
}
|
||||
private:
|
||||
n64::Core core;
|
||||
|
||||
@@ -7,6 +7,7 @@ SET(NFD_PORTAL ON CACHE BOOL "Use dbus for native file dialog instead of gtk")
|
||||
|
||||
find_package(SDL2 REQUIRED)
|
||||
find_package(fmt REQUIRED)
|
||||
find_package(nlohmann_json REQUIRED)
|
||||
|
||||
add_library(frontend-imgui STATIC
|
||||
Window.cpp
|
||||
@@ -34,4 +35,4 @@ else()
|
||||
set(LIBRARIES )
|
||||
endif()
|
||||
|
||||
target_link_libraries(frontend-imgui PUBLIC SDL2main SDL2 ${LIBRARIES} imgui nfd fmt)
|
||||
target_link_libraries(frontend-imgui PUBLIC nlohmann_json::nlohmann_json SDL2main SDL2 ${LIBRARIES} imgui nfd fmt)
|
||||
@@ -4,8 +4,11 @@
|
||||
#include <Core.hpp>
|
||||
#include <utility>
|
||||
#include <Audio.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <filesystem>
|
||||
|
||||
VkInstance instance{};
|
||||
using json = nlohmann::json;
|
||||
|
||||
Window::Window(n64::Core& core) {
|
||||
InitSDL();
|
||||
@@ -142,9 +145,30 @@ ImDrawData* Window::Present(n64::Core& core) {
|
||||
return ImGui::GetDrawData();
|
||||
}
|
||||
|
||||
void Window::LoadROM(n64::Core& core, const std::string &path) {
|
||||
if(!path.empty()) {
|
||||
u32 crc = core.LoadROM(path);
|
||||
std::ifstream gameDbFile("resources/game_db.json");
|
||||
json gameDb = json::parse(gameDbFile);
|
||||
auto entry = gameDb[fmt::format("{:08x}", crc)]["name"];
|
||||
std::string name{};
|
||||
if(!entry.empty()) {
|
||||
name = entry.get<std::string>();
|
||||
} else {
|
||||
name = std::filesystem::path(path).stem().string();
|
||||
}
|
||||
|
||||
windowTitle = "natsukashii - " + name;
|
||||
|
||||
SDL_SetWindowTitle(window, windowTitle.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Window::Render(n64::Core& core) {
|
||||
ImGui::PushFont(uiFont);
|
||||
if(windowID == SDL_GetWindowID(SDL_GetMouseFocus())) {
|
||||
static bool showSettings = false;
|
||||
bool showMainMenuBar = windowID == SDL_GetWindowID(SDL_GetMouseFocus());
|
||||
if(showMainMenuBar) {
|
||||
ImGui::BeginMainMenuBar();
|
||||
if (ImGui::BeginMenu("File")) {
|
||||
if (ImGui::MenuItem("Open", "O")) {
|
||||
@@ -152,7 +176,7 @@ void Window::Render(n64::Core& core) {
|
||||
const nfdu8filteritem_t filter{"Nintendo 64 roms", "n64,z64,v64,N64,Z64,V64"};
|
||||
nfdresult_t result = NFD_OpenDialog(&outpath, &filter, 1, nullptr);
|
||||
if (result == NFD_OKAY) {
|
||||
core.LoadROM(outpath);
|
||||
LoadROM(core, outpath);
|
||||
NFD_FreePath(outpath);
|
||||
}
|
||||
}
|
||||
@@ -163,17 +187,51 @@ void Window::Render(n64::Core& core) {
|
||||
}
|
||||
if (ImGui::BeginMenu("Emulation")) {
|
||||
if (ImGui::MenuItem("Reset")) {
|
||||
core.Reset();
|
||||
LoadROM(core, core.rom);
|
||||
}
|
||||
if (ImGui::MenuItem("Stop")) {
|
||||
windowTitle = "natsukashii";
|
||||
SDL_SetWindowTitle(window, windowTitle.c_str());
|
||||
core.Stop();
|
||||
}
|
||||
if (ImGui::MenuItem(core.pause ? "Resume" : "Pause", nullptr, false, core.romLoaded)) {
|
||||
core.TogglePause();
|
||||
std::string paused = "| Paused";
|
||||
if(core.pause) {
|
||||
windowTitle += paused;
|
||||
} else {
|
||||
auto pausedPos = windowTitle.find_first_of(paused);
|
||||
if(pausedPos != std::string::npos) {
|
||||
windowTitle.erase(pausedPos, paused.length());
|
||||
}
|
||||
}
|
||||
SDL_SetWindowTitle(window, windowTitle.c_str());
|
||||
}
|
||||
if (ImGui::MenuItem("Settings")) {
|
||||
showSettings = true;
|
||||
}
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
ImGui::EndMainMenuBar();
|
||||
}
|
||||
|
||||
if(showSettings) {
|
||||
ImGui::OpenPopup("Settings");
|
||||
if(ImGui::BeginPopupModal("Settings", &showSettings)) {
|
||||
ImGui::Checkbox("Lock channels", &lockVolume);
|
||||
ImGui::SliderFloat("Volume L", &volumeL, 0, 1, "%.2f", ImGuiSliderFlags_NoInput);
|
||||
if (!lockVolume) {
|
||||
ImGui::SliderFloat("Volume R", &volumeR, 0, 1, "%.2f", ImGuiSliderFlags_NoInput);
|
||||
} else {
|
||||
ImGui::PushStyleColor(ImGuiCol_SliderGrabActive, 0x11111111);
|
||||
ImGui::BeginDisabled();
|
||||
ImGui::SliderFloat("Volume R", &volumeR, 0, 1, "%.2f", ImGuiSliderFlags_NoInput);
|
||||
ImGui::EndDisabled();
|
||||
ImGui::PopStyleColor();
|
||||
volumeR = volumeL;
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
ImGui::PopFont();
|
||||
}
|
||||
|
||||
@@ -15,8 +15,13 @@ struct Window {
|
||||
[[nodiscard]] bool gotClosed(SDL_Event event);
|
||||
ImFont *uiFont, *codeFont;
|
||||
u32 windowID;
|
||||
float volumeL = 0.5, volumeR = 0.5;
|
||||
void LoadROM(n64::Core& core, const std::string& path);
|
||||
private:
|
||||
bool lockVolume = true;
|
||||
bool showSettings = false;
|
||||
SDL_Window* window;
|
||||
std::string windowTitle;
|
||||
void InitSDL();
|
||||
void InitImgui();
|
||||
void Render(n64::Core& core);
|
||||
|
||||
@@ -14,29 +14,18 @@ void Core::Stop() {
|
||||
mem.Reset();
|
||||
pause = true;
|
||||
romLoaded = false;
|
||||
rom.clear();
|
||||
}
|
||||
|
||||
void Core::Reset() {
|
||||
cpu.Reset();
|
||||
mem.Reset();
|
||||
pause = true;
|
||||
romLoaded = false;
|
||||
if(!rom.empty()) {
|
||||
LoadROM(rom);
|
||||
}
|
||||
}
|
||||
|
||||
void Core::LoadROM(const std::string& rom_) {
|
||||
u32 Core::LoadROM(const std::string& rom_) {
|
||||
rom = rom_;
|
||||
cpu.Reset();
|
||||
mem.Reset();
|
||||
mem.LoadROM(rom);
|
||||
pause = false;
|
||||
romLoaded = true;
|
||||
return mem.LoadROM(rom);
|
||||
}
|
||||
|
||||
void Core::Run(Window& window) {
|
||||
void Core::Run(Window& window, float volumeL, float volumeR) {
|
||||
MMIO& mmio = mem.mmio;
|
||||
Controller& controller = mmio.si.controller;
|
||||
int cycles = 0;
|
||||
@@ -54,7 +43,7 @@ void Core::Run(Window& window) {
|
||||
cpu.Step(mem);
|
||||
mmio.rsp.Step(mmio.mi, cpu.regs, mmio.rdp);
|
||||
|
||||
mmio.ai.Step(mem, cpu.regs, 1);
|
||||
mmio.ai.Step(mem, cpu.regs, 1, volumeL, volumeR);
|
||||
}
|
||||
|
||||
cycles -= mmio.vi.cyclesPerHalfline;
|
||||
@@ -67,7 +56,7 @@ void Core::Run(Window& window) {
|
||||
UpdateScreenParallelRdp(*this, window, GetVI());
|
||||
|
||||
int missedCycles = N64_CYCLES_PER_FRAME - frameCycles;
|
||||
mmio.ai.Step(mem, cpu.regs, missedCycles);
|
||||
mmio.ai.Step(mem, cpu.regs, missedCycles, volumeL, volumeR);
|
||||
} else if(pause && romLoaded) {
|
||||
UpdateScreenParallelRdp(*this, window, GetVI());
|
||||
} else if(pause && !romLoaded) {
|
||||
|
||||
@@ -10,9 +10,8 @@ struct Core {
|
||||
~Core() { Stop(); }
|
||||
Core();
|
||||
void Stop();
|
||||
void Reset();
|
||||
void LoadROM(const std::string&);
|
||||
void Run(Window&);
|
||||
u32 LoadROM(const std::string&);
|
||||
void Run(Window&, float volumeL, float volumeR);
|
||||
void UpdateController(const u8*);
|
||||
void TogglePause() { pause = !pause; }
|
||||
VI& GetVI() { return mem.mmio.vi; }
|
||||
|
||||
@@ -65,12 +65,12 @@ void InitAudio() {
|
||||
}
|
||||
}
|
||||
|
||||
void PushSample(s16 left, s16 right) {
|
||||
s16 samples[2]{ left, right };
|
||||
void PushSample(float left, float volumeL, float volumeR, float right) {
|
||||
float samples[2]{ left * volumeL, right * volumeR };
|
||||
|
||||
int availableBytes = SDL_AudioStreamAvailable(audioStream);
|
||||
if(availableBytes < BYTES_PER_HALF_SECOND) {
|
||||
SDL_AudioStreamPut(audioStream, samples, 2 * sizeof(16));
|
||||
SDL_AudioStreamPut(audioStream, samples, 2 * sizeof(float));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ void AdjustSampleRate(int sampleRate) {
|
||||
LockAudioMutex();
|
||||
if(audioStream) SDL_FreeAudioStream(audioStream);
|
||||
|
||||
audioStream = SDL_NewAudioStream(AUDIO_S16SYS, 2, sampleRate, SYSTEM_SAMPLE_FORMAT, 2, AUDIO_SAMPLE_RATE);
|
||||
audioStream = SDL_NewAudioStream(AUDIO_F32SYS, 2, sampleRate, SYSTEM_SAMPLE_FORMAT, 2, AUDIO_SAMPLE_RATE);
|
||||
UnlockAudioMutex();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "common.hpp"
|
||||
|
||||
namespace n64 {
|
||||
void PushSample(s16, s16);
|
||||
void PushSample(float, float, float, float);
|
||||
void InitAudio();
|
||||
void AdjustSampleRate(int);
|
||||
}
|
||||
@@ -16,7 +16,7 @@ void Mem::Reset() {
|
||||
mmio.Reset();
|
||||
}
|
||||
|
||||
void Mem::LoadROM(const std::string& filename) {
|
||||
u32 Mem::LoadROM(const std::string& filename) {
|
||||
std::ifstream file(filename, std::ios::binary);
|
||||
file.unsetf(std::ios::skipws);
|
||||
|
||||
@@ -35,8 +35,12 @@ void Mem::LoadROM(const std::string& filename) {
|
||||
cart.insert(cart.begin(), std::istream_iterator<u8>(file), std::istream_iterator<u8>());
|
||||
|
||||
file.close();
|
||||
util::SwapN64Rom(sizeAdjusted, cart.data());
|
||||
|
||||
u32 crc = 0;
|
||||
util::SwapN64Rom(crc, sizeAdjusted, cart.data());
|
||||
memcpy(mmio.rsp.dmem, cart.data(), 0x1000);
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
template <bool tlb>
|
||||
|
||||
@@ -11,7 +11,7 @@ struct Mem {
|
||||
~Mem() = default;
|
||||
Mem();
|
||||
void Reset();
|
||||
void LoadROM(const std::string&);
|
||||
u32 LoadROM(const std::string&);
|
||||
[[nodiscard]] auto GetRDRAM() -> u8* {
|
||||
return mmio.rdp.dram.data();
|
||||
}
|
||||
|
||||
@@ -16,8 +16,7 @@ void RSP::Reset() {
|
||||
nextPC = 0;
|
||||
spDMASPAddr.raw = 0;
|
||||
spDMADRAMAddr.raw = 0;
|
||||
spDMARDLen.raw = 0;
|
||||
spDMAWRLen.raw = 0;
|
||||
spDMALen.raw = 0;
|
||||
memset(dmem, 0, DMEM_SIZE);
|
||||
memset(imem, 0, IMEM_SIZE);
|
||||
memset(vpr, 0, 32 * sizeof(VPR));
|
||||
@@ -44,8 +43,8 @@ auto RSP::Read(u32 addr) -> u32{
|
||||
switch (addr) {
|
||||
case 0x04040000: return spDMASPAddr.raw & 0xFFFFF8;
|
||||
case 0x04040004: return spDMADRAMAddr.raw & 0x1FF8;
|
||||
case 0x04040008: return spDMARDLen.raw;
|
||||
case 0x0404000C: return spDMAWRLen.raw;
|
||||
case 0x04040008:
|
||||
case 0x0404000C: return spDMALen.raw;
|
||||
case 0x04040010: return spStatus.raw;
|
||||
case 0x04040018: return 0;
|
||||
case 0x0404001C: return AcquireSemaphore();
|
||||
@@ -61,20 +60,22 @@ inline void DMA(SPDMALen len, RSP& rsp, u8* dst, u8* src) {
|
||||
|
||||
length = (length + 0x7) & ~0x7;
|
||||
|
||||
u32 last_addr = rsp.spDMASPAddr.address + length;
|
||||
if (last_addr > 0x1000) {
|
||||
u32 overshoot = last_addr - 0x1000;
|
||||
length -= overshoot;
|
||||
}
|
||||
|
||||
u32 dram_address = rsp.spDMADRAMAddr.address & 0xFFFFF8;
|
||||
u32 mem_address = rsp.spDMASPAddr.address & 0x1FF8;
|
||||
u32 mem_address = rsp.spDMASPAddr.address & 0xFF8;
|
||||
|
||||
for (int i = 0; i < len.count + 1; i++) {
|
||||
if(isDRAMdest) {
|
||||
/*if(isDRAMdest) {
|
||||
memcpy(&dst[dram_address], &src[mem_address], length);
|
||||
} else {
|
||||
memcpy(&dst[mem_address], &src[dram_address], length);
|
||||
}*/
|
||||
|
||||
for(int j = 0; j < length; j++) {
|
||||
if constexpr (isDRAMdest) {
|
||||
dst[dram_address + j] = src[(mem_address + j) & 0xFFF];
|
||||
} else {
|
||||
dst[(mem_address + j) & 0xFFF] = src[dram_address + j];
|
||||
}
|
||||
}
|
||||
|
||||
int skip = i == len.count ? 0 : len.skip;
|
||||
@@ -82,6 +83,9 @@ inline void DMA(SPDMALen len, RSP& rsp, u8* dst, u8* src) {
|
||||
dram_address += (length + skip) & 0xFFFFF8;
|
||||
mem_address += length;
|
||||
}
|
||||
|
||||
rsp.spDMADRAMAddr.address = dram_address;
|
||||
rsp.spDMASPAddr.address = mem_address;
|
||||
}
|
||||
|
||||
void RSP::Write(Mem& mem, Registers& regs, u32 addr, u32 value) {
|
||||
@@ -90,14 +94,14 @@ void RSP::Write(Mem& mem, Registers& regs, u32 addr, u32 value) {
|
||||
case 0x04040000: spDMASPAddr.raw = value & 0x1FF8; break;
|
||||
case 0x04040004: spDMADRAMAddr.raw = value & 0xFFFFF8; break;
|
||||
case 0x04040008: {
|
||||
spDMARDLen.raw = value;
|
||||
DMA<false>(spDMARDLen, *this, spDMASPAddr.bank ? imem : dmem, mem.GetRDRAM());
|
||||
spDMARDLen.raw = 0xFF8 | (spDMARDLen.skip << 20);
|
||||
spDMALen.raw = value;
|
||||
DMA<false>(spDMALen, *this, spDMASPAddr.bank ? imem : dmem, mem.GetRDRAM());
|
||||
spDMALen.raw = 0xFF8 | (spDMALen.skip << 20);
|
||||
} break;
|
||||
case 0x0404000C: {
|
||||
spDMAWRLen.raw = value;
|
||||
DMA<true>(spDMAWRLen, *this, mem.GetRDRAM(), spDMASPAddr.bank ? imem : dmem);
|
||||
spDMAWRLen.raw = 0xFF8 | (spDMAWRLen.skip << 20);
|
||||
spDMALen.raw = value;
|
||||
DMA<true>(spDMALen, *this, mem.GetRDRAM(), spDMASPAddr.bank ? imem : dmem);
|
||||
spDMALen.raw = 0xFF8 | (spDMALen.skip << 20);
|
||||
} break;
|
||||
case 0x04040010: {
|
||||
auto write = SPStatusWrite{.raw = value};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <n64/core/RDP.hpp>
|
||||
#include <n64/memory_regions.hpp>
|
||||
|
||||
#define RSP_BYTE(addr) (dmem[BYTE_ADDRESS(addr) & 0xfff])
|
||||
#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 GET_RSP_WORD(addr) ((GET_RSP_HALF(addr) << 16) | GET_RSP_HALF((addr) + 2))
|
||||
@@ -117,7 +117,7 @@ struct RSP {
|
||||
u16 oldPC{}, pc{}, nextPC{};
|
||||
SPDMASPAddr spDMASPAddr{};
|
||||
SPDMADRAMAddr spDMADRAMAddr{};
|
||||
SPDMALen spDMARDLen{}, spDMAWRLen{};
|
||||
SPDMALen spDMALen{};
|
||||
u8 dmem[DMEM_SIZE]{}, imem[IMEM_SIZE]{};
|
||||
VPR vpr[32]{};
|
||||
s32 gpr[32]{};
|
||||
|
||||
@@ -72,7 +72,7 @@ void AI::Write(Mem& mem, Registers& regs, u32 addr, u32 val) {
|
||||
}
|
||||
}
|
||||
|
||||
void AI::Step(Mem& mem, Registers& regs, int cpuCycles) {
|
||||
void AI::Step(Mem& mem, Registers& regs, int cpuCycles, float volumeL, float volumeR) {
|
||||
cycles += cpuCycles;
|
||||
while(cycles > dac.period) {
|
||||
if (dmaCount == 0) {
|
||||
@@ -85,7 +85,7 @@ void AI::Step(Mem& mem, Registers& regs, int cpuCycles) {
|
||||
|
||||
s16 left = (s16)(data >> 16);
|
||||
s16 right = (s16)data;
|
||||
PushSample(left, right);
|
||||
PushSample((float)left / INT16_MAX, volumeL, volumeR, (float)right / INT16_MAX);
|
||||
|
||||
u32 address_lo = (dmaAddr[0] + 4) & 0x1fff;
|
||||
dmaAddr[0] = (dmaAddr[0] & ~0x1fff) | address_lo;
|
||||
|
||||
@@ -11,7 +11,7 @@ struct AI {
|
||||
void Reset();
|
||||
auto Read(u32) const -> u32;
|
||||
void Write(Mem&, Registers&, u32, u32);
|
||||
void Step(Mem&, Registers&, int);
|
||||
void Step(Mem&, Registers&, int, float, float);
|
||||
bool dmaEnable{};
|
||||
u16 dacRate{};
|
||||
u8 bitrate{};
|
||||
|
||||
@@ -20,8 +20,8 @@ inline auto GetCop0Reg(RSP& rsp, RDP& rdp, u8 index) -> u32{
|
||||
switch(index) {
|
||||
case 0: return rsp.spDMASPAddr.raw;
|
||||
case 1: return rsp.spDMADRAMAddr.raw;
|
||||
case 2: return rsp.spDMARDLen.raw;
|
||||
case 3: return rsp.spDMAWRLen.raw;
|
||||
case 2:
|
||||
case 3: return rsp.spDMALen.raw;
|
||||
case 4: return rsp.spStatus.raw;
|
||||
case 5: return rsp.spStatus.dmaFull;
|
||||
case 6: return 0;
|
||||
@@ -36,8 +36,8 @@ inline void SetCop0Reg(MI& mi, Registers& regs, RSP& rsp, RDP& rdp, u8 index, u3
|
||||
switch(index) {
|
||||
case 0: rsp.spDMASPAddr.raw = val; break;
|
||||
case 1: rsp.spDMADRAMAddr.raw = val; break;
|
||||
case 2: rsp.spDMARDLen.raw = val; break;
|
||||
case 3: rsp.spDMAWRLen.raw = val; break;
|
||||
case 2:
|
||||
case 3: rsp.spDMALen.raw = val; break;
|
||||
case 4: rsp.spStatus.raw = val; break;
|
||||
case 7:
|
||||
if(val == 0) {
|
||||
@@ -227,12 +227,14 @@ void RSP::sqv(u32 instr) {
|
||||
|
||||
void RSP::sllv(u32 instr) {
|
||||
u8 sa = gpr[RS(instr)] & 0x1F;
|
||||
gpr[RD(instr)] = gpr[RT(instr)] << sa;
|
||||
s32 rt = gpr[RT(instr)];
|
||||
gpr[RD(instr)] = rt << sa;
|
||||
}
|
||||
|
||||
void RSP::srlv(u32 instr) {
|
||||
u8 sa = gpr[RS(instr)] & 0x1F;
|
||||
gpr[RD(instr)] = (u32)gpr[RT(instr)] >> sa;
|
||||
u32 rt = gpr[RT(instr)];
|
||||
gpr[RD(instr)] = rt >> sa;
|
||||
}
|
||||
|
||||
void RSP::srav(u32 instr) {
|
||||
|
||||
62
src/util.hpp
62
src/util.hpp
@@ -131,23 +131,71 @@ inline void SwapBuffer16(size_t size, u8* data) {
|
||||
}
|
||||
}
|
||||
|
||||
inline u32 crc32(u32 crc, const u8 *buf, size_t len) {
|
||||
static u32 table[256];
|
||||
static int have_table = 0;
|
||||
u32 rem;
|
||||
u8 octet;
|
||||
int i, j;
|
||||
const u8 *p, *q;
|
||||
|
||||
if (have_table == 0) {
|
||||
for (i = 0; i < 256; i++) {
|
||||
rem = i;
|
||||
for (j = 0; j < 8; j++) {
|
||||
if (rem & 1) {
|
||||
rem >>= 1;
|
||||
rem ^= 0xedb88320;
|
||||
} else
|
||||
rem >>= 1;
|
||||
}
|
||||
table[i] = rem;
|
||||
}
|
||||
have_table = 1;
|
||||
}
|
||||
|
||||
crc = ~crc;
|
||||
q = buf + len;
|
||||
for (p = buf; p < q; p++) {
|
||||
octet = *p; /* Cast to unsigned octet. */
|
||||
crc = (crc >> 8) ^ table[(crc & 0xff) ^ octet];
|
||||
}
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
enum RomTypes {
|
||||
Z64 = 0x80371240,
|
||||
N64 = 0x40123780,
|
||||
V64 = 0x37804012
|
||||
};
|
||||
|
||||
inline void SwapN64Rom(size_t size, u8* data) {
|
||||
inline void SwapN64Rom(u32& crc, size_t size, u8* rom) {
|
||||
RomTypes endianness;
|
||||
memcpy(&endianness, data, 4);
|
||||
memcpy(&endianness, rom, 4);
|
||||
endianness = static_cast<RomTypes>(be32toh(endianness));
|
||||
|
||||
switch(endianness) {
|
||||
case V64:
|
||||
SwapBuffer32(size, data);
|
||||
SwapBuffer16(size, data);
|
||||
case RomTypes::V64: {
|
||||
u8* temp = (u8*)calloc(size, 1);
|
||||
memcpy(temp, rom, size);
|
||||
SwapBuffer16(size, temp);
|
||||
crc = crc32(0, temp, size);
|
||||
free(temp);
|
||||
|
||||
SwapBuffer32(size, rom);
|
||||
SwapBuffer16(size, rom);
|
||||
} break;
|
||||
case RomTypes::N64: {
|
||||
u8* temp = (u8*)calloc(size, 1);
|
||||
memcpy(temp, rom, size);
|
||||
SwapBuffer32(size, temp);
|
||||
crc = crc32(0, temp, size);
|
||||
free(temp);
|
||||
} break;
|
||||
case RomTypes::Z64:
|
||||
crc = crc32(0, rom, size);
|
||||
SwapBuffer32(size, rom);
|
||||
break;
|
||||
case N64: break;
|
||||
case Z64: SwapBuffer32(size, data); break;
|
||||
default:
|
||||
panic("Unrecognized rom format! Make sure this is a valid Nintendo 64 ROM dump!\n");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user