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
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update -qq
|
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
|
sudo apt-get install -y vulkan-tools libvulkan1 libvulkan-dev vulkan-validationlayers-dev spirv-tools
|
||||||
- name: Build natsukashii
|
- name: Build natsukashii
|
||||||
run: |
|
run: |
|
||||||
@@ -44,7 +44,10 @@ jobs:
|
|||||||
submodules: recursive
|
submodules: recursive
|
||||||
- uses: msys2/setup-msys2@v2
|
- uses: msys2/setup-msys2@v2
|
||||||
with:
|
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
|
- name: Build natsukashii
|
||||||
run: |
|
run: |
|
||||||
cmake \
|
cmake \
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,6 +1,5 @@
|
|||||||
*.toml
|
*.toml
|
||||||
*.ini
|
*.ini
|
||||||
*.json
|
|
||||||
*build*/
|
*build*/
|
||||||
.idea/
|
.idea/
|
||||||
roms/
|
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
|
// Main loop
|
||||||
const u8* state = SDL_GetKeyboardState(nullptr);
|
const u8* state = SDL_GetKeyboardState(nullptr);
|
||||||
while (!core.done) {
|
while (!core.done) {
|
||||||
core.Run(window);
|
core.Run(window, window.volumeL, window.volumeR);
|
||||||
core.UpdateController(state);
|
core.UpdateController(state);
|
||||||
|
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
@@ -33,7 +33,7 @@ void App::Run() {
|
|||||||
const nfdu8filteritem_t filter {"Nintendo 64 roms", "n64,z64,v64,N64,Z64,V64"};
|
const nfdu8filteritem_t filter {"Nintendo 64 roms", "n64,z64,v64,N64,Z64,V64"};
|
||||||
nfdresult_t result = NFD_OpenDialog(&outpath, &filter, 1, nullptr);
|
nfdresult_t result = NFD_OpenDialog(&outpath, &filter, 1, nullptr);
|
||||||
if(result == NFD_OKAY) {
|
if(result == NFD_OKAY) {
|
||||||
core.LoadROM(outpath);
|
LoadROM(outpath);
|
||||||
NFD_FreePath(outpath);
|
NFD_FreePath(outpath);
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ struct App {
|
|||||||
~App() = default;
|
~App() = default;
|
||||||
void Run();
|
void Run();
|
||||||
inline void LoadROM(const std::string& path) {
|
inline void LoadROM(const std::string& path) {
|
||||||
core.LoadROM(path);
|
window.LoadROM(core, path);
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
n64::Core core;
|
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(SDL2 REQUIRED)
|
||||||
find_package(fmt REQUIRED)
|
find_package(fmt REQUIRED)
|
||||||
|
find_package(nlohmann_json REQUIRED)
|
||||||
|
|
||||||
add_library(frontend-imgui STATIC
|
add_library(frontend-imgui STATIC
|
||||||
Window.cpp
|
Window.cpp
|
||||||
@@ -34,4 +35,4 @@ else()
|
|||||||
set(LIBRARIES )
|
set(LIBRARIES )
|
||||||
endif()
|
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 <Core.hpp>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <Audio.hpp>
|
#include <Audio.hpp>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
VkInstance instance{};
|
VkInstance instance{};
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
Window::Window(n64::Core& core) {
|
Window::Window(n64::Core& core) {
|
||||||
InitSDL();
|
InitSDL();
|
||||||
@@ -142,9 +145,30 @@ ImDrawData* Window::Present(n64::Core& core) {
|
|||||||
return ImGui::GetDrawData();
|
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) {
|
void Window::Render(n64::Core& core) {
|
||||||
ImGui::PushFont(uiFont);
|
ImGui::PushFont(uiFont);
|
||||||
if(windowID == SDL_GetWindowID(SDL_GetMouseFocus())) {
|
static bool showSettings = false;
|
||||||
|
bool showMainMenuBar = windowID == SDL_GetWindowID(SDL_GetMouseFocus());
|
||||||
|
if(showMainMenuBar) {
|
||||||
ImGui::BeginMainMenuBar();
|
ImGui::BeginMainMenuBar();
|
||||||
if (ImGui::BeginMenu("File")) {
|
if (ImGui::BeginMenu("File")) {
|
||||||
if (ImGui::MenuItem("Open", "O")) {
|
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"};
|
const nfdu8filteritem_t filter{"Nintendo 64 roms", "n64,z64,v64,N64,Z64,V64"};
|
||||||
nfdresult_t result = NFD_OpenDialog(&outpath, &filter, 1, nullptr);
|
nfdresult_t result = NFD_OpenDialog(&outpath, &filter, 1, nullptr);
|
||||||
if (result == NFD_OKAY) {
|
if (result == NFD_OKAY) {
|
||||||
core.LoadROM(outpath);
|
LoadROM(core, outpath);
|
||||||
NFD_FreePath(outpath);
|
NFD_FreePath(outpath);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -163,17 +187,51 @@ void Window::Render(n64::Core& core) {
|
|||||||
}
|
}
|
||||||
if (ImGui::BeginMenu("Emulation")) {
|
if (ImGui::BeginMenu("Emulation")) {
|
||||||
if (ImGui::MenuItem("Reset")) {
|
if (ImGui::MenuItem("Reset")) {
|
||||||
core.Reset();
|
LoadROM(core, core.rom);
|
||||||
}
|
}
|
||||||
if (ImGui::MenuItem("Stop")) {
|
if (ImGui::MenuItem("Stop")) {
|
||||||
|
windowTitle = "natsukashii";
|
||||||
|
SDL_SetWindowTitle(window, windowTitle.c_str());
|
||||||
core.Stop();
|
core.Stop();
|
||||||
}
|
}
|
||||||
if (ImGui::MenuItem(core.pause ? "Resume" : "Pause", nullptr, false, core.romLoaded)) {
|
if (ImGui::MenuItem(core.pause ? "Resume" : "Pause", nullptr, false, core.romLoaded)) {
|
||||||
core.TogglePause();
|
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::EndMenu();
|
||||||
}
|
}
|
||||||
ImGui::EndMainMenuBar();
|
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();
|
ImGui::PopFont();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,8 +15,13 @@ struct Window {
|
|||||||
[[nodiscard]] bool gotClosed(SDL_Event event);
|
[[nodiscard]] bool gotClosed(SDL_Event event);
|
||||||
ImFont *uiFont, *codeFont;
|
ImFont *uiFont, *codeFont;
|
||||||
u32 windowID;
|
u32 windowID;
|
||||||
|
float volumeL = 0.5, volumeR = 0.5;
|
||||||
|
void LoadROM(n64::Core& core, const std::string& path);
|
||||||
private:
|
private:
|
||||||
|
bool lockVolume = true;
|
||||||
|
bool showSettings = false;
|
||||||
SDL_Window* window;
|
SDL_Window* window;
|
||||||
|
std::string windowTitle;
|
||||||
void InitSDL();
|
void InitSDL();
|
||||||
void InitImgui();
|
void InitImgui();
|
||||||
void Render(n64::Core& core);
|
void Render(n64::Core& core);
|
||||||
|
|||||||
@@ -14,29 +14,18 @@ void Core::Stop() {
|
|||||||
mem.Reset();
|
mem.Reset();
|
||||||
pause = true;
|
pause = true;
|
||||||
romLoaded = false;
|
romLoaded = false;
|
||||||
rom.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Reset() {
|
u32 Core::LoadROM(const std::string& rom_) {
|
||||||
cpu.Reset();
|
|
||||||
mem.Reset();
|
|
||||||
pause = true;
|
|
||||||
romLoaded = false;
|
|
||||||
if(!rom.empty()) {
|
|
||||||
LoadROM(rom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Core::LoadROM(const std::string& rom_) {
|
|
||||||
rom = rom_;
|
rom = rom_;
|
||||||
cpu.Reset();
|
cpu.Reset();
|
||||||
mem.Reset();
|
mem.Reset();
|
||||||
mem.LoadROM(rom);
|
|
||||||
pause = false;
|
pause = false;
|
||||||
romLoaded = true;
|
romLoaded = true;
|
||||||
|
return mem.LoadROM(rom);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Core::Run(Window& window) {
|
void Core::Run(Window& window, float volumeL, float volumeR) {
|
||||||
MMIO& mmio = mem.mmio;
|
MMIO& mmio = mem.mmio;
|
||||||
Controller& controller = mmio.si.controller;
|
Controller& controller = mmio.si.controller;
|
||||||
int cycles = 0;
|
int cycles = 0;
|
||||||
@@ -54,7 +43,7 @@ void Core::Run(Window& window) {
|
|||||||
cpu.Step(mem);
|
cpu.Step(mem);
|
||||||
mmio.rsp.Step(mmio.mi, cpu.regs, mmio.rdp);
|
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;
|
cycles -= mmio.vi.cyclesPerHalfline;
|
||||||
@@ -67,7 +56,7 @@ void Core::Run(Window& window) {
|
|||||||
UpdateScreenParallelRdp(*this, window, GetVI());
|
UpdateScreenParallelRdp(*this, window, GetVI());
|
||||||
|
|
||||||
int missedCycles = N64_CYCLES_PER_FRAME - frameCycles;
|
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) {
|
} else if(pause && romLoaded) {
|
||||||
UpdateScreenParallelRdp(*this, window, GetVI());
|
UpdateScreenParallelRdp(*this, window, GetVI());
|
||||||
} else if(pause && !romLoaded) {
|
} else if(pause && !romLoaded) {
|
||||||
|
|||||||
@@ -10,9 +10,8 @@ struct Core {
|
|||||||
~Core() { Stop(); }
|
~Core() { Stop(); }
|
||||||
Core();
|
Core();
|
||||||
void Stop();
|
void Stop();
|
||||||
void Reset();
|
u32 LoadROM(const std::string&);
|
||||||
void LoadROM(const std::string&);
|
void Run(Window&, float volumeL, float volumeR);
|
||||||
void Run(Window&);
|
|
||||||
void UpdateController(const u8*);
|
void UpdateController(const u8*);
|
||||||
void TogglePause() { pause = !pause; }
|
void TogglePause() { pause = !pause; }
|
||||||
VI& GetVI() { return mem.mmio.vi; }
|
VI& GetVI() { return mem.mmio.vi; }
|
||||||
|
|||||||
@@ -65,12 +65,12 @@ void InitAudio() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PushSample(s16 left, s16 right) {
|
void PushSample(float left, float volumeL, float volumeR, float right) {
|
||||||
s16 samples[2]{ left, right };
|
float samples[2]{ left * volumeL, right * volumeR };
|
||||||
|
|
||||||
int availableBytes = SDL_AudioStreamAvailable(audioStream);
|
int availableBytes = SDL_AudioStreamAvailable(audioStream);
|
||||||
if(availableBytes < BYTES_PER_HALF_SECOND) {
|
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();
|
LockAudioMutex();
|
||||||
if(audioStream) SDL_FreeAudioStream(audioStream);
|
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();
|
UnlockAudioMutex();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
#include "common.hpp"
|
#include "common.hpp"
|
||||||
|
|
||||||
namespace n64 {
|
namespace n64 {
|
||||||
void PushSample(s16, s16);
|
void PushSample(float, float, float, float);
|
||||||
void InitAudio();
|
void InitAudio();
|
||||||
void AdjustSampleRate(int);
|
void AdjustSampleRate(int);
|
||||||
}
|
}
|
||||||
@@ -16,7 +16,7 @@ void Mem::Reset() {
|
|||||||
mmio.Reset();
|
mmio.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mem::LoadROM(const std::string& filename) {
|
u32 Mem::LoadROM(const std::string& filename) {
|
||||||
std::ifstream file(filename, std::ios::binary);
|
std::ifstream file(filename, std::ios::binary);
|
||||||
file.unsetf(std::ios::skipws);
|
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>());
|
cart.insert(cart.begin(), std::istream_iterator<u8>(file), std::istream_iterator<u8>());
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
util::SwapN64Rom(sizeAdjusted, cart.data());
|
|
||||||
|
u32 crc = 0;
|
||||||
|
util::SwapN64Rom(crc, sizeAdjusted, cart.data());
|
||||||
memcpy(mmio.rsp.dmem, cart.data(), 0x1000);
|
memcpy(mmio.rsp.dmem, cart.data(), 0x1000);
|
||||||
|
|
||||||
|
return crc;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool tlb>
|
template <bool tlb>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ struct Mem {
|
|||||||
~Mem() = default;
|
~Mem() = default;
|
||||||
Mem();
|
Mem();
|
||||||
void Reset();
|
void Reset();
|
||||||
void LoadROM(const std::string&);
|
u32 LoadROM(const std::string&);
|
||||||
[[nodiscard]] auto GetRDRAM() -> u8* {
|
[[nodiscard]] auto GetRDRAM() -> u8* {
|
||||||
return mmio.rdp.dram.data();
|
return mmio.rdp.dram.data();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,7 @@ void RSP::Reset() {
|
|||||||
nextPC = 0;
|
nextPC = 0;
|
||||||
spDMASPAddr.raw = 0;
|
spDMASPAddr.raw = 0;
|
||||||
spDMADRAMAddr.raw = 0;
|
spDMADRAMAddr.raw = 0;
|
||||||
spDMARDLen.raw = 0;
|
spDMALen.raw = 0;
|
||||||
spDMAWRLen.raw = 0;
|
|
||||||
memset(dmem, 0, DMEM_SIZE);
|
memset(dmem, 0, DMEM_SIZE);
|
||||||
memset(imem, 0, IMEM_SIZE);
|
memset(imem, 0, IMEM_SIZE);
|
||||||
memset(vpr, 0, 32 * sizeof(VPR));
|
memset(vpr, 0, 32 * sizeof(VPR));
|
||||||
@@ -44,8 +43,8 @@ auto RSP::Read(u32 addr) -> u32{
|
|||||||
switch (addr) {
|
switch (addr) {
|
||||||
case 0x04040000: return spDMASPAddr.raw & 0xFFFFF8;
|
case 0x04040000: return spDMASPAddr.raw & 0xFFFFF8;
|
||||||
case 0x04040004: return spDMADRAMAddr.raw & 0x1FF8;
|
case 0x04040004: return spDMADRAMAddr.raw & 0x1FF8;
|
||||||
case 0x04040008: return spDMARDLen.raw;
|
case 0x04040008:
|
||||||
case 0x0404000C: return spDMAWRLen.raw;
|
case 0x0404000C: return spDMALen.raw;
|
||||||
case 0x04040010: return spStatus.raw;
|
case 0x04040010: return spStatus.raw;
|
||||||
case 0x04040018: return 0;
|
case 0x04040018: return 0;
|
||||||
case 0x0404001C: return AcquireSemaphore();
|
case 0x0404001C: return AcquireSemaphore();
|
||||||
@@ -61,20 +60,22 @@ inline void DMA(SPDMALen len, RSP& rsp, u8* dst, u8* src) {
|
|||||||
|
|
||||||
length = (length + 0x7) & ~0x7;
|
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 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++) {
|
for (int i = 0; i < len.count + 1; i++) {
|
||||||
if(isDRAMdest) {
|
/*if(isDRAMdest) {
|
||||||
memcpy(&dst[dram_address], &src[mem_address], length);
|
memcpy(&dst[dram_address], &src[mem_address], length);
|
||||||
} else {
|
} else {
|
||||||
memcpy(&dst[mem_address], &src[dram_address], length);
|
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;
|
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;
|
dram_address += (length + skip) & 0xFFFFF8;
|
||||||
mem_address += length;
|
mem_address += length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rsp.spDMADRAMAddr.address = dram_address;
|
||||||
|
rsp.spDMASPAddr.address = mem_address;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RSP::Write(Mem& mem, Registers& regs, u32 addr, u32 value) {
|
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 0x04040000: spDMASPAddr.raw = value & 0x1FF8; break;
|
||||||
case 0x04040004: spDMADRAMAddr.raw = value & 0xFFFFF8; break;
|
case 0x04040004: spDMADRAMAddr.raw = value & 0xFFFFF8; break;
|
||||||
case 0x04040008: {
|
case 0x04040008: {
|
||||||
spDMARDLen.raw = value;
|
spDMALen.raw = value;
|
||||||
DMA<false>(spDMARDLen, *this, spDMASPAddr.bank ? imem : dmem, mem.GetRDRAM());
|
DMA<false>(spDMALen, *this, spDMASPAddr.bank ? imem : dmem, mem.GetRDRAM());
|
||||||
spDMARDLen.raw = 0xFF8 | (spDMARDLen.skip << 20);
|
spDMALen.raw = 0xFF8 | (spDMALen.skip << 20);
|
||||||
} break;
|
} break;
|
||||||
case 0x0404000C: {
|
case 0x0404000C: {
|
||||||
spDMAWRLen.raw = value;
|
spDMALen.raw = value;
|
||||||
DMA<true>(spDMAWRLen, *this, mem.GetRDRAM(), spDMASPAddr.bank ? imem : dmem);
|
DMA<true>(spDMALen, *this, mem.GetRDRAM(), spDMASPAddr.bank ? imem : dmem);
|
||||||
spDMAWRLen.raw = 0xFF8 | (spDMAWRLen.skip << 20);
|
spDMALen.raw = 0xFF8 | (spDMALen.skip << 20);
|
||||||
} break;
|
} break;
|
||||||
case 0x04040010: {
|
case 0x04040010: {
|
||||||
auto write = SPStatusWrite{.raw = value};
|
auto write = SPStatusWrite{.raw = value};
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
#include <n64/core/RDP.hpp>
|
#include <n64/core/RDP.hpp>
|
||||||
#include <n64/memory_regions.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 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 GET_RSP_WORD(addr) ((GET_RSP_HALF(addr) << 16) | GET_RSP_HALF((addr) + 2))
|
||||||
@@ -117,7 +117,7 @@ struct RSP {
|
|||||||
u16 oldPC{}, pc{}, nextPC{};
|
u16 oldPC{}, pc{}, nextPC{};
|
||||||
SPDMASPAddr spDMASPAddr{};
|
SPDMASPAddr spDMASPAddr{};
|
||||||
SPDMADRAMAddr spDMADRAMAddr{};
|
SPDMADRAMAddr spDMADRAMAddr{};
|
||||||
SPDMALen spDMARDLen{}, spDMAWRLen{};
|
SPDMALen spDMALen{};
|
||||||
u8 dmem[DMEM_SIZE]{}, imem[IMEM_SIZE]{};
|
u8 dmem[DMEM_SIZE]{}, imem[IMEM_SIZE]{};
|
||||||
VPR vpr[32]{};
|
VPR vpr[32]{};
|
||||||
s32 gpr[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;
|
cycles += cpuCycles;
|
||||||
while(cycles > dac.period) {
|
while(cycles > dac.period) {
|
||||||
if (dmaCount == 0) {
|
if (dmaCount == 0) {
|
||||||
@@ -85,7 +85,7 @@ void AI::Step(Mem& mem, Registers& regs, int cpuCycles) {
|
|||||||
|
|
||||||
s16 left = (s16)(data >> 16);
|
s16 left = (s16)(data >> 16);
|
||||||
s16 right = (s16)data;
|
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;
|
u32 address_lo = (dmaAddr[0] + 4) & 0x1fff;
|
||||||
dmaAddr[0] = (dmaAddr[0] & ~0x1fff) | address_lo;
|
dmaAddr[0] = (dmaAddr[0] & ~0x1fff) | address_lo;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ struct AI {
|
|||||||
void Reset();
|
void Reset();
|
||||||
auto Read(u32) const -> u32;
|
auto Read(u32) const -> u32;
|
||||||
void Write(Mem&, Registers&, u32, u32);
|
void Write(Mem&, Registers&, u32, u32);
|
||||||
void Step(Mem&, Registers&, int);
|
void Step(Mem&, Registers&, int, float, float);
|
||||||
bool dmaEnable{};
|
bool dmaEnable{};
|
||||||
u16 dacRate{};
|
u16 dacRate{};
|
||||||
u8 bitrate{};
|
u8 bitrate{};
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ inline auto GetCop0Reg(RSP& rsp, RDP& rdp, u8 index) -> u32{
|
|||||||
switch(index) {
|
switch(index) {
|
||||||
case 0: return rsp.spDMASPAddr.raw;
|
case 0: return rsp.spDMASPAddr.raw;
|
||||||
case 1: return rsp.spDMADRAMAddr.raw;
|
case 1: return rsp.spDMADRAMAddr.raw;
|
||||||
case 2: return rsp.spDMARDLen.raw;
|
case 2:
|
||||||
case 3: return rsp.spDMAWRLen.raw;
|
case 3: return rsp.spDMALen.raw;
|
||||||
case 4: return rsp.spStatus.raw;
|
case 4: return rsp.spStatus.raw;
|
||||||
case 5: return rsp.spStatus.dmaFull;
|
case 5: return rsp.spStatus.dmaFull;
|
||||||
case 6: return 0;
|
case 6: return 0;
|
||||||
@@ -36,8 +36,8 @@ inline void SetCop0Reg(MI& mi, Registers& regs, RSP& rsp, RDP& rdp, u8 index, u3
|
|||||||
switch(index) {
|
switch(index) {
|
||||||
case 0: rsp.spDMASPAddr.raw = val; break;
|
case 0: rsp.spDMASPAddr.raw = val; break;
|
||||||
case 1: rsp.spDMADRAMAddr.raw = val; break;
|
case 1: rsp.spDMADRAMAddr.raw = val; break;
|
||||||
case 2: rsp.spDMARDLen.raw = val; break;
|
case 2:
|
||||||
case 3: rsp.spDMAWRLen.raw = val; break;
|
case 3: rsp.spDMALen.raw = val; break;
|
||||||
case 4: rsp.spStatus.raw = val; break;
|
case 4: rsp.spStatus.raw = val; break;
|
||||||
case 7:
|
case 7:
|
||||||
if(val == 0) {
|
if(val == 0) {
|
||||||
@@ -227,12 +227,14 @@ void RSP::sqv(u32 instr) {
|
|||||||
|
|
||||||
void RSP::sllv(u32 instr) {
|
void RSP::sllv(u32 instr) {
|
||||||
u8 sa = gpr[RS(instr)] & 0x1F;
|
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) {
|
void RSP::srlv(u32 instr) {
|
||||||
u8 sa = gpr[RS(instr)] & 0x1F;
|
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) {
|
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 {
|
enum RomTypes {
|
||||||
Z64 = 0x80371240,
|
Z64 = 0x80371240,
|
||||||
N64 = 0x40123780,
|
N64 = 0x40123780,
|
||||||
V64 = 0x37804012
|
V64 = 0x37804012
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void SwapN64Rom(size_t size, u8* data) {
|
inline void SwapN64Rom(u32& crc, size_t size, u8* rom) {
|
||||||
RomTypes endianness;
|
RomTypes endianness;
|
||||||
memcpy(&endianness, data, 4);
|
memcpy(&endianness, rom, 4);
|
||||||
endianness = static_cast<RomTypes>(be32toh(endianness));
|
endianness = static_cast<RomTypes>(be32toh(endianness));
|
||||||
|
|
||||||
switch(endianness) {
|
switch(endianness) {
|
||||||
case V64:
|
case RomTypes::V64: {
|
||||||
SwapBuffer32(size, data);
|
u8* temp = (u8*)calloc(size, 1);
|
||||||
SwapBuffer16(size, data);
|
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;
|
break;
|
||||||
case N64: break;
|
|
||||||
case Z64: SwapBuffer32(size, data); break;
|
|
||||||
default:
|
default:
|
||||||
panic("Unrecognized rom format! Make sure this is a valid Nintendo 64 ROM dump!\n");
|
panic("Unrecognized rom format! Make sure this is a valid Nintendo 64 ROM dump!\n");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user