diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e13bc573..9150273b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -58,8 +58,9 @@ set( app/emu/audio/spc700.cc app/emu/audio/dsp.cc app/emu/audio/internal/addressing.cc - app/emu/cpu/internal/instructions.cc + app/emu/audio/internal/instructions.cc app/emu/cpu/internal/addressing.cc + app/emu/cpu/internal/instructions.cc app/emu/cpu/cpu.cc app/emu/video/ppu.cc app/emu/memory/dma.cc diff --git a/src/app/editor/overworld_editor.cc b/src/app/editor/overworld_editor.cc index 7d3c661e..acf0071c 100644 --- a/src/app/editor/overworld_editor.cc +++ b/src/app/editor/overworld_editor.cc @@ -41,6 +41,7 @@ absl::Status OverworldEditor::Update() { gfx_group_editor_.InitBlockset(tile16_blockset_bmp_); all_gfx_loaded_ = true; } else if (!rom()->isLoaded() && all_gfx_loaded_) { + // TODO: Destroy the overworld graphics canvas. // Reset the editor if the ROM is unloaded Shutdown(); all_gfx_loaded_ = false; @@ -226,8 +227,9 @@ void OverworldEditor::DrawOverworldMapSettings() { ImGui::SetNextItemWidth(100.f); ImGui::Combo("##World", &game_state_, kGamePartComboString, 3); + // TODO: Make enable grid bool change the current canvas. TableNextColumn(); - ImGui::Checkbox("Show grid", &opt_enable_grid); // TODO + ImGui::Checkbox("Show grid", &opt_enable_grid); ImGui::EndTable(); } } @@ -521,14 +523,13 @@ void OverworldEditor::DrawTileSelector() { ImGuiTabBarFlags_FittingPolicyScroll)) { if (ImGui::BeginTabItem("Tile16")) { gui::BitmapCanvasPipeline(blockset_canvas_, tile16_blockset_bmp_, 0x100, - (8192 * 2), 0x20, map_blockset_loaded_, true, - 1); + (8192 * 2), 0x20, map_blockset_loaded_, true, + 1); ImGui::EndTabItem(); } if (ImGui::BeginTabItem("Tile8")) { - if (ImGui::BeginChild("##tile8viewer", - ImGui::GetContentRegionAvail(), true, - ImGuiWindowFlags_AlwaysVerticalScrollbar)) { + if (ImGui::BeginChild("##tile8viewer", ImGui::GetContentRegionAvail(), + true, ImGuiWindowFlags_AlwaysVerticalScrollbar)) { DrawTile8Selector(); } ImGui::EndChild(); @@ -536,8 +537,8 @@ void OverworldEditor::DrawTileSelector() { } if (ImGui::BeginTabItem("Area Graphics")) { gui::BitmapCanvasPipeline(current_gfx_canvas_, current_gfx_bmp_, 256, - 0x10 * 0x40, 0x20, overworld_.isLoaded(), true, - 3); + 0x10 * 0x40, 0x20, overworld_.isLoaded(), true, + 3); ImGui::EndTabItem(); } ImGui::EndTabBar(); @@ -557,13 +558,13 @@ absl::Status OverworldEditor::LoadGraphics() { // Create the area graphics image gui::BuildAndRenderBitmapPipeline(0x80, 0x200, 0x40, - overworld_.AreaGraphics(), *rom(), - current_gfx_bmp_, palette_); + overworld_.AreaGraphics(), *rom(), + current_gfx_bmp_, palette_); // Create the tile16 blockset image gui::BuildAndRenderBitmapPipeline(0x80, 0x2000, 0x80, - overworld_.Tile16Blockset(), *rom(), - tile16_blockset_bmp_, palette_); + overworld_.Tile16Blockset(), *rom(), + tile16_blockset_bmp_, palette_); map_blockset_loaded_ = true; // Copy the tile16 data into individual tiles. @@ -653,10 +654,10 @@ absl::Status OverworldEditor::DrawExperimentalModal() { &tile32_configuration_filename_); ImGui::SameLine(); gui::FileDialogPipeline("ImportTile32Key", ".DAT,.dat\0", "Tile32 Hex File", - [this]() { - tile32_configuration_filename_ = - ImGuiFileDialog::Instance()->GetFilePathName(); - }); + [this]() { + tile32_configuration_filename_ = + ImGuiFileDialog::Instance()->GetFilePathName(); + }); if (ImGui::Button("Load Prototype Overworld with ROM graphics")) { RETURN_IF_ERROR(LoadGraphics()) diff --git a/src/app/emu/audio/apu.cc b/src/app/emu/audio/apu.cc index 439487b3..ededc355 100644 --- a/src/app/emu/audio/apu.cc +++ b/src/app/emu/audio/apu.cc @@ -28,11 +28,9 @@ void APU::Init() { } void APU::Reset() { - // Reset the clock clock_.ResetAccumulatedTime(); - - // Reset the SPC700 - // ... + spc700_.Reset(); + dsp_.Reset(); } void APU::Update() { @@ -51,6 +49,33 @@ void APU::Update() { ProcessSamples(); } +void APU::Notify(uint32_t address, uint8_t data) { + if (address < 0x2140 || address > 0x2143) { + return; + } + auto offset = address - 0x2140; + spc700_.write(offset, data); + + // HACK - This is a temporary solution to get the APU to play audio + ports_[address - 0x2140] = data; + switch (address) { + case 0x2140: + if (data == BEGIN_SIGNAL) { + SignalReady(); + } + break; + case 0x2141: + // TODO: Handle data byte transfer here + break; + case 0x2142: + // TODO: Handle the setup of destination address + break; + case 0x2143: + // TODO: Handle additional communication/commands + break; + } +} + void APU::ProcessSamples() { // Fetch sample data from AudioRam // Iterate over all voices @@ -62,63 +87,51 @@ void APU::ProcessSamples() { int16_t processed_sample = dsp_.ProcessSample(voice_num, sample); // Add the processed sample to the audio buffer - audioSamples_.push_back(processed_sample); + audio_samples_.push_back(processed_sample); } } uint8_t APU::FetchSampleForVoice(uint8_t voice_num) { - // Define how you determine the address based on the voice_num uint16_t address = CalculateAddressForVoice(voice_num); return aram_.read(address); } uint16_t APU::CalculateAddressForVoice(uint8_t voice_num) { - // Placeholder logic to calculate the address in the AudioRam - // based on the voice number. - return voice_num; // Assuming each voice has a fixed size + // TODO: Calculate the address for the specified voice + return voice_num; } int16_t APU::GetNextSample() { - // This method fetches the next sample. If there's no sample available, it can - // return 0 or the last sample. - if (!audioSamples_.empty()) { - int16_t sample = audioSamples_.front(); - audioSamples_.erase(audioSamples_.begin()); + if (!audio_samples_.empty()) { + int16_t sample = audio_samples_.front(); + audio_samples_.erase(audio_samples_.begin()); return sample; } - return 0; // or return the last sample -} - -uint8_t APU::ReadRegister(uint16_t address) { - // ... -} - -void APU::WriteRegister(uint16_t address, uint8_t value) { - // ... + return 0; // TODO: Return the last sample instead of 0. } const std::vector& APU::GetAudioSamples() const { - // ... + return audio_samples_; } void APU::UpdateChannelSettings() { - // ... + // TODO: Implement this method to update the channel settings. } int16_t APU::GenerateSample(int channel) { - // ... + // TODO: Implement this method to generate a sample for the specified channel. } void APU::ApplyEnvelope(int channel) { - // ... + // TODO: Implement this method to apply an envelope to the specified channel. } -uint8_t APU::ReadDSPMemory(uint16_t address) { - // ... +uint8_t APU::ReadDspMemory(uint16_t address) { + return dsp_.ReadGlobalReg(address); } -void APU::WriteDSPMemory(uint16_t address, uint8_t value) { - // ... +void APU::WriteDspMemory(uint16_t address, uint8_t value) { + dsp_.WriteGlobalReg(address, value); } } // namespace emu diff --git a/src/app/emu/audio/apu.h b/src/app/emu/audio/apu.h index a0b4da25..fb4fe7af 100644 --- a/src/app/emu/audio/apu.h +++ b/src/app/emu/audio/apu.h @@ -39,134 +39,32 @@ const int apuClocksPerSample = 64; // 64 clocks per sample class APU : public Observer { public: - // Initializes the APU with the necessary resources and dependencies APU(MemoryImpl &memory, AudioRam &aram, Clock &clock) : aram_(aram), clock_(clock), memory_(memory) {} void Init(); - - // Resets the APU to its initial state void Reset(); - - // Runs the APU for one frame void Update(); + void Notify(uint32_t address, uint8_t data) override; void ProcessSamples(); - uint8_t FetchSampleForVoice(uint8_t voice_num); - uint16_t CalculateAddressForVoice(uint8_t voice_num); - int16_t GetNextSample(); - void Notify(uint32_t address, uint8_t data) override { - ports_[address - 0x2140] = data; - - switch (address) { - case 0x2140: - if (data == BEGIN_SIGNAL) { - BeginTransfer(); - } else { - AcknowledgeSignal(); - } - break; - case 0x2141: - // Handle data byte transfer here, if needed - break; - case 0x2142: - // Handle the setup of destination address, if needed - break; - case 0x2143: - // Handle additional communication or commands - break; - } - } - // Called upon a reset void Initialize() { spc700_.Reset(); dsp_.Reset(); - // Set stack pointer, zero-page values, etc. for the SPC700 SignalReady(); } + // Set Port 0 = $AA and Port 1 = $BB void SignalReady() { - // Set Port 0 = $AA and Port 1 = $BB - ports_[0] = READY_SIGNAL_0; - ports_[1] = READY_SIGNAL_1; memory_.WriteByte(0x2140, READY_SIGNAL_0); memory_.WriteByte(0x2141, READY_SIGNAL_1); } - bool IsReadySignalReceived() const { - return ports_[0] == READY_SIGNAL_0 && ports_[1] == READY_SIGNAL_1; - } - - void WaitForSignal() const { - // This might be an active wait or a passive state where APU does nothing - // until it's externally triggered by the main CPU writing to its ports. - while (ports_[0] != BEGIN_SIGNAL) - ; - } - - uint16_t ReadAddressFromPorts() const { - // Read 2 byte address from port 2 (low) and 3 (high) - return static_cast(ports_[2]) | - (static_cast(ports_[3]) << 8); - } - - void AcknowledgeSignal() { - // Read value from Port 0 and write it back to Port 0 - ports_[0] = ports_[0]; - } - - void BeginTransfer() { - const uint16_t startAddress = 0x0200; - - // Write the starting address to ports 0x2142 and 0x2143 - WriteToPort(2, static_cast(startAddress & 0xFF)); // Lower byte - WriteToPort(3, static_cast(startAddress >> 8)); // Upper byte - - // Trigger the actual data transfer process - TriggerDataTransfer(startAddress); - } - - void TriggerDataTransfer(uint16_t startAddress) { - const int DATA_SIZE = 0x1000; // Size of the data to be transferred - uint8_t audioData[DATA_SIZE]; // Buffer containing the audio data - - // Load audioData as needed... - - for (int i = 0; i < DATA_SIZE; ++i) { - WriteToPort(1, audioData[i]); // Write data byte - WriteToPort(0, i & 0xFF); // Write index and wait for acknowledgment - WaitForAcknowledgment(i & 0xFF); - } - - // After transferring all data, trigger the execution of the program - StartSpcProgram(startAddress); - } - - void WaitForAcknowledgment(uint8_t expectedIndex) { - while (ports_[0] != expectedIndex) { - // Active wait - consider implementing a more efficient mechanism - } - } - - void StartSpcProgram(uint16_t startAddress) { - // Send the start address for execution - WriteToPort(2, static_cast(startAddress & 0xFF)); // Lower byte - WriteToPort(3, static_cast(startAddress >> 8)); // Upper byte - - WriteToPort(1, 0x00); // Zero value indicates execution command - WriteToPort(0, 0xCE); // Send a unique signal to start execution - - // Wait for acknowledgment - WaitForAcknowledgment(0xCE); - } - - void ExecuteProgram() { spc700_.ExecuteInstructions(ReadAddressFromPorts()); } - void WriteToPort(uint8_t portNum, uint8_t value) { ports_[portNum] = value; switch (portNum) { @@ -185,10 +83,6 @@ class APU : public Observer { } } - void SetReadyCallback(std::function callback) { - ready_callback_ = callback; - } - void UpdateClock(int delta_time) { clock_.UpdateClock(delta_time); } // Method to fetch a sample from AudioRam @@ -197,13 +91,7 @@ class APU : public Observer { } // Method to push a processed sample to the audio buffer - void PushToAudioBuffer(int16_t sample) { audioSamples_.push_back(sample); } - - // Reads a byte from the specified APU register - uint8_t ReadRegister(uint16_t address); - - // Writes a byte to the specified APU register - void WriteRegister(uint16_t address, uint8_t value); + void PushToAudioBuffer(int16_t sample) { audio_samples_.push_back(sample); } // Returns the audio samples for the current frame const std::vector &GetAudioSamples() const; @@ -227,8 +115,8 @@ class APU : public Observer { void ApplyEnvelope(int channel); // Handles DSP (Digital Signal Processor) memory reads and writes - uint8_t ReadDSPMemory(uint16_t address); - void WriteDSPMemory(uint16_t address, uint8_t value); + uint8_t ReadDspMemory(uint16_t address); + void WriteDspMemory(uint16_t address, uint8_t value); // Member variables to store internal APU state and resources AudioRam &aram_; @@ -237,7 +125,7 @@ class APU : public Observer { DigitalSignalProcessor dsp_; Spc700 spc700_{aram_}; - std::vector audioSamples_; + std::vector audio_samples_; std::function ready_callback_; }; diff --git a/src/app/emu/audio/internal/addressing.cc b/src/app/emu/audio/internal/addressing.cc index f8f973b1..7fad0081 100644 --- a/src/app/emu/audio/internal/addressing.cc +++ b/src/app/emu/audio/internal/addressing.cc @@ -4,358 +4,89 @@ namespace yaze { namespace app { namespace emu { -void Spc700::MOV(uint8_t& dest, uint8_t operand) { - dest = operand; - PSW.Z = (operand == 0); - PSW.N = (operand & 0x80); +// Immediate +uint8_t Spc700::imm() { + PC++; + return read(PC); } -void Spc700::MOV_ADDR(uint16_t address, uint8_t operand) { - write(address, operand); - PSW.Z = (operand == 0); - PSW.N = (operand & 0x80); +// Direct page +uint8_t Spc700::dp() { + PC++; + uint8_t offset = read(PC); + return read((PSW.P << 8) + offset); } -void Spc700::ADC(uint8_t& dest, uint8_t operand) { - uint16_t result = dest + operand + PSW.C; - PSW.V = ((A ^ result) & (operand ^ result) & 0x80); - PSW.C = (result > 0xFF); - PSW.Z = ((result & 0xFF) == 0); - PSW.N = (result & 0x80); - PSW.H = ((A ^ operand ^ result) & 0x10); - dest = result & 0xFF; +uint8_t Spc700::get_dp_addr() { + PC++; + uint8_t offset = read(PC); + return (PSW.P << 8) + offset; } -void Spc700::SBC(uint8_t& dest, uint8_t operand) { - uint16_t result = dest - operand - (1 - PSW.C); - PSW.V = ((dest ^ result) & (dest ^ operand) & 0x80); - PSW.C = (result < 0x100); - PSW.Z = ((result & 0xFF) == 0); - PSW.N = (result & 0x80); - PSW.H = ((dest ^ operand ^ result) & 0x10); - dest = result & 0xFF; +// Direct page indexed by X +uint8_t Spc700::dp_plus_x() { + PC++; + uint8_t offset = read(PC); + return read((PSW.P << 8) + offset + X); } -void Spc700::CMP(uint8_t& dest, uint8_t operand) { - uint16_t result = dest - operand; - PSW.C = (result < 0x100); - PSW.Z = ((result & 0xFF) == 0); - PSW.N = (result & 0x80); +// Direct page indexed by Y +uint8_t Spc700::dp_plus_y() { + PC++; + uint8_t offset = read(PC); + return read((PSW.P << 8) + offset + Y); } -void Spc700::AND(uint8_t& dest, uint8_t operand) { - dest &= operand; - PSW.Z = (dest == 0); - PSW.N = (dest & 0x80); +// Indexed indirect (add index before 16-bit lookup). +uint16_t Spc700::dp_plus_x_indirect() { + PC++; + uint8_t offset = read(PC); + uint16_t addr = read((PSW.P << 8) + offset + X) | + (read((PSW.P << 8) + offset + X + 1) << 8); + return addr; } -void Spc700::OR(uint8_t& dest, uint8_t operand) { - dest |= operand; - PSW.Z = (dest == 0); - PSW.N = (dest & 0x80); +// Indirect indexed (add index after 16-bit lookup). +uint16_t Spc700::dp_indirect_plus_y() { + PC++; + uint8_t offset = read(PC); + uint16_t baseAddr = + read((PSW.P << 8) + offset) | (read((PSW.P << 8) + offset + 1) << 8); + return baseAddr + Y; } -void Spc700::EOR(uint8_t& dest, uint8_t operand) { - dest ^= operand; - PSW.Z = (dest == 0); - PSW.N = (dest & 0x80); +uint16_t Spc700::abs() { + PC++; + uint16_t addr = read(PC) | (read(PC) << 8); + return addr; } -void Spc700::ASL(uint8_t operand) { - PSW.C = (operand & 0x80); - operand <<= 1; - PSW.Z = (operand == 0); - PSW.N = (operand & 0x80); - // A = value; +int8_t Spc700::rel() { + PC++; + return static_cast(read(PC)); } -void Spc700::LSR(uint8_t& operand) { - PSW.C = (operand & 0x01); - operand >>= 1; - PSW.Z = (operand == 0); - PSW.N = (operand & 0x80); +uint8_t Spc700::i() { return read((PSW.P << 8) + X); } + +uint8_t Spc700::i_postinc() { + uint8_t value = read((PSW.P << 8) + X); + X++; + return value; } -void Spc700::ROL(uint8_t operand, bool isImmediate) { - uint8_t value = isImmediate ? imm() : operand; - uint8_t carry = PSW.C; - PSW.C = (value & 0x80); - value <<= 1; - value |= carry; - PSW.Z = (value == 0); - PSW.N = (value & 0x80); - // operand = value; +uint16_t Spc700::addr_plus_i() { + PC++; + uint16_t addr = read(PC) | (read(PC) << 8); + return read(addr) + X; } -void Spc700::XCN(uint8_t operand, bool isImmediate) { - uint8_t value = isImmediate ? imm() : operand; - value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4); - PSW.Z = (value == 0); - PSW.N = (value & 0x80); - // operand = value; +uint16_t Spc700::addr_plus_i_indexed() { + PC++; + uint16_t addr = read(PC) | (read(PC) << 8); + addr += X; + return read(addr) | (read(addr + 1) << 8); } -void Spc700::INC(uint8_t& operand) { - operand++; - PSW.Z = (operand == 0); - PSW.N = (operand & 0x80); -} - -void Spc700::DEC(uint8_t& operand) { - operand--; - PSW.Z = (operand == 0); - PSW.N = (operand & 0x80); -} - -void Spc700::MOVW(uint16_t& dest, uint16_t operand) { - dest = operand; - PSW.Z = (operand == 0); - PSW.N = (operand & 0x8000); -} - -void Spc700::INCW(uint16_t& operand) { - operand++; - PSW.Z = (operand == 0); - PSW.N = (operand & 0x8000); -} - -void Spc700::DECW(uint16_t& operand) { - operand--; - PSW.Z = (operand == 0); - PSW.N = (operand & 0x8000); -} - -void Spc700::ADDW(uint16_t& dest, uint16_t operand) { - uint32_t result = dest + operand; - PSW.C = (result > 0xFFFF); - PSW.Z = ((result & 0xFFFF) == 0); - PSW.N = (result & 0x8000); - PSW.V = ((dest ^ result) & (operand ^ result) & 0x8000); - dest = result & 0xFFFF; -} - -void Spc700::SUBW(uint16_t& dest, uint16_t operand) { - uint32_t result = dest - operand; - PSW.C = (result < 0x10000); - PSW.Z = ((result & 0xFFFF) == 0); - PSW.N = (result & 0x8000); - PSW.V = ((dest ^ result) & (dest ^ operand) & 0x8000); - dest = result & 0xFFFF; -} - -void Spc700::CMPW(uint16_t operand) { - uint32_t result = YA - operand; - PSW.C = (result < 0x10000); - PSW.Z = ((result & 0xFFFF) == 0); - PSW.N = (result & 0x8000); -} - -void Spc700::MUL(uint8_t operand) { - uint16_t result = A * operand; - YA = result; - PSW.Z = (result == 0); - PSW.N = (result & 0x8000); -} - -void Spc700::DIV(uint8_t operand) { - if (operand == 0) { - // Handle divide by zero error - return; - } - uint8_t quotient = A / operand; - uint8_t remainder = A % operand; - A = quotient; - Y = remainder; - PSW.Z = (quotient == 0); - PSW.N = (quotient & 0x80); -} - -void Spc700::BRA(int8_t offset) { PC += offset; } - -void Spc700::BEQ(int8_t offset) { - if (PSW.Z) { - PC += offset; - } -} - -void Spc700::BNE(int8_t offset) { - if (!PSW.Z) { - PC += offset; - } -} - -void Spc700::BCS(int8_t offset) { - if (PSW.C) { - PC += offset; - } -} - -void Spc700::BCC(int8_t offset) { - if (!PSW.C) { - PC += offset; - } -} - -void Spc700::BVS(int8_t offset) { - if (PSW.V) { - PC += offset; - } -} - -void Spc700::BVC(int8_t offset) { - if (!PSW.V) { - PC += offset; - } -} - -void Spc700::BMI(int8_t offset) { - if (PSW.N) { - PC += offset; - } -} - -void Spc700::BPL(int8_t offset) { - if (!PSW.N) { - PC += offset; - } -} - -void Spc700::BBS(uint8_t bit, uint8_t operand) { - if (operand & (1 << bit)) { - PC += rel(); - } -} - -void Spc700::BBC(uint8_t bit, uint8_t operand) { - if (!(operand & (1 << bit))) { - PC += rel(); - } -} - -// CBNE DBNZ -// JMP -void Spc700::JMP(uint16_t address) { PC = address; } - -void Spc700::CALL(uint16_t address) { - uint16_t return_address = PC + 2; - write(SP, return_address & 0xFF); - write(SP - 1, (return_address >> 8) & 0xFF); - SP -= 2; - PC = address; -} - -void Spc700::PCALL(uint8_t offset) { - uint16_t return_address = PC + 2; - write(SP, return_address & 0xFF); - write(SP - 1, (return_address >> 8) & 0xFF); - SP -= 2; - PC += offset; -} - -void Spc700::TCALL(uint8_t offset) { - uint16_t return_address = PC + 2; - write(SP, return_address & 0xFF); - write(SP - 1, (return_address >> 8) & 0xFF); - SP -= 2; - PC = 0xFFDE + offset; -} - -void Spc700::BRK() { - uint16_t return_address = PC + 2; - write(SP, return_address & 0xFF); - write(SP - 1, (return_address >> 8) & 0xFF); - SP -= 2; - PC = 0xFFDE; -} - -void Spc700::RET() { - uint16_t return_address = read(SP) | (read(SP + 1) << 8); - SP += 2; - PC = return_address; -} - -void Spc700::RETI() { - uint16_t return_address = read(SP) | (read(SP + 1) << 8); - SP += 2; - PC = return_address; - PSW.I = 1; -} - -void Spc700::PUSH(uint8_t operand) { - write(SP, operand); - SP--; -} - -void Spc700::POP(uint8_t& operand) { - SP++; - operand = read(SP); -} - -void Spc700::SET1(uint8_t bit, uint8_t& operand) { operand |= (1 << bit); } - -void Spc700::CLR1(uint8_t bit, uint8_t& operand) { operand &= ~(1 << bit); } - -void Spc700::TSET1(uint8_t bit, uint8_t& operand) { - PSW.C = (operand & (1 << bit)); - operand |= (1 << bit); -} - -void Spc700::TCLR1(uint8_t bit, uint8_t& operand) { - PSW.C = (operand & (1 << bit)); - operand &= ~(1 << bit); -} - -void Spc700::AND1(uint8_t bit, uint8_t& operand) { - operand &= (1 << bit); - PSW.Z = (operand == 0); - PSW.N = (operand & 0x80); -} - -void Spc700::OR1(uint8_t bit, uint8_t& operand) { - operand |= (1 << bit); - PSW.Z = (operand == 0); - PSW.N = (operand & 0x80); -} - -void Spc700::EOR1(uint8_t bit, uint8_t& operand) { - operand ^= (1 << bit); - PSW.Z = (operand == 0); - PSW.N = (operand & 0x80); -} - -void Spc700::NOT1(uint8_t bit, uint8_t& operand) { - operand ^= (1 << bit); - PSW.Z = (operand == 0); - PSW.N = (operand & 0x80); -} - -void Spc700::MOV1(uint8_t bit, uint8_t& operand) { - PSW.C = (operand & (1 << bit)); - operand |= (1 << bit); -} - -void Spc700::CLRC() { PSW.C = 0; } - -void Spc700::SETC() { PSW.C = 1; } - -void Spc700::NOTC() { PSW.C = !PSW.C; } - -void Spc700::CLRV() { PSW.V = 0; } - -void Spc700::CLRP() { PSW.P = 0; } - -void Spc700::SETP() { PSW.P = 1; } - -void Spc700::EI() { PSW.I = 1; } - -void Spc700::DI() { PSW.I = 0; } - -void Spc700::NOP() { PC++; } - -void Spc700::SLEEP() {} - -void Spc700::STOP() {} - } // namespace emu } // namespace app } // namespace yaze \ No newline at end of file diff --git a/src/app/emu/audio/internal/instructions.cc b/src/app/emu/audio/internal/instructions.cc new file mode 100644 index 00000000..f8f973b1 --- /dev/null +++ b/src/app/emu/audio/internal/instructions.cc @@ -0,0 +1,361 @@ +#include "app/emu/audio/spc700.h" + +namespace yaze { +namespace app { +namespace emu { + +void Spc700::MOV(uint8_t& dest, uint8_t operand) { + dest = operand; + PSW.Z = (operand == 0); + PSW.N = (operand & 0x80); +} + +void Spc700::MOV_ADDR(uint16_t address, uint8_t operand) { + write(address, operand); + PSW.Z = (operand == 0); + PSW.N = (operand & 0x80); +} + +void Spc700::ADC(uint8_t& dest, uint8_t operand) { + uint16_t result = dest + operand + PSW.C; + PSW.V = ((A ^ result) & (operand ^ result) & 0x80); + PSW.C = (result > 0xFF); + PSW.Z = ((result & 0xFF) == 0); + PSW.N = (result & 0x80); + PSW.H = ((A ^ operand ^ result) & 0x10); + dest = result & 0xFF; +} + +void Spc700::SBC(uint8_t& dest, uint8_t operand) { + uint16_t result = dest - operand - (1 - PSW.C); + PSW.V = ((dest ^ result) & (dest ^ operand) & 0x80); + PSW.C = (result < 0x100); + PSW.Z = ((result & 0xFF) == 0); + PSW.N = (result & 0x80); + PSW.H = ((dest ^ operand ^ result) & 0x10); + dest = result & 0xFF; +} + +void Spc700::CMP(uint8_t& dest, uint8_t operand) { + uint16_t result = dest - operand; + PSW.C = (result < 0x100); + PSW.Z = ((result & 0xFF) == 0); + PSW.N = (result & 0x80); +} + +void Spc700::AND(uint8_t& dest, uint8_t operand) { + dest &= operand; + PSW.Z = (dest == 0); + PSW.N = (dest & 0x80); +} + +void Spc700::OR(uint8_t& dest, uint8_t operand) { + dest |= operand; + PSW.Z = (dest == 0); + PSW.N = (dest & 0x80); +} + +void Spc700::EOR(uint8_t& dest, uint8_t operand) { + dest ^= operand; + PSW.Z = (dest == 0); + PSW.N = (dest & 0x80); +} + +void Spc700::ASL(uint8_t operand) { + PSW.C = (operand & 0x80); + operand <<= 1; + PSW.Z = (operand == 0); + PSW.N = (operand & 0x80); + // A = value; +} + +void Spc700::LSR(uint8_t& operand) { + PSW.C = (operand & 0x01); + operand >>= 1; + PSW.Z = (operand == 0); + PSW.N = (operand & 0x80); +} + +void Spc700::ROL(uint8_t operand, bool isImmediate) { + uint8_t value = isImmediate ? imm() : operand; + uint8_t carry = PSW.C; + PSW.C = (value & 0x80); + value <<= 1; + value |= carry; + PSW.Z = (value == 0); + PSW.N = (value & 0x80); + // operand = value; +} + +void Spc700::XCN(uint8_t operand, bool isImmediate) { + uint8_t value = isImmediate ? imm() : operand; + value = ((value & 0xF0) >> 4) | ((value & 0x0F) << 4); + PSW.Z = (value == 0); + PSW.N = (value & 0x80); + // operand = value; +} + +void Spc700::INC(uint8_t& operand) { + operand++; + PSW.Z = (operand == 0); + PSW.N = (operand & 0x80); +} + +void Spc700::DEC(uint8_t& operand) { + operand--; + PSW.Z = (operand == 0); + PSW.N = (operand & 0x80); +} + +void Spc700::MOVW(uint16_t& dest, uint16_t operand) { + dest = operand; + PSW.Z = (operand == 0); + PSW.N = (operand & 0x8000); +} + +void Spc700::INCW(uint16_t& operand) { + operand++; + PSW.Z = (operand == 0); + PSW.N = (operand & 0x8000); +} + +void Spc700::DECW(uint16_t& operand) { + operand--; + PSW.Z = (operand == 0); + PSW.N = (operand & 0x8000); +} + +void Spc700::ADDW(uint16_t& dest, uint16_t operand) { + uint32_t result = dest + operand; + PSW.C = (result > 0xFFFF); + PSW.Z = ((result & 0xFFFF) == 0); + PSW.N = (result & 0x8000); + PSW.V = ((dest ^ result) & (operand ^ result) & 0x8000); + dest = result & 0xFFFF; +} + +void Spc700::SUBW(uint16_t& dest, uint16_t operand) { + uint32_t result = dest - operand; + PSW.C = (result < 0x10000); + PSW.Z = ((result & 0xFFFF) == 0); + PSW.N = (result & 0x8000); + PSW.V = ((dest ^ result) & (dest ^ operand) & 0x8000); + dest = result & 0xFFFF; +} + +void Spc700::CMPW(uint16_t operand) { + uint32_t result = YA - operand; + PSW.C = (result < 0x10000); + PSW.Z = ((result & 0xFFFF) == 0); + PSW.N = (result & 0x8000); +} + +void Spc700::MUL(uint8_t operand) { + uint16_t result = A * operand; + YA = result; + PSW.Z = (result == 0); + PSW.N = (result & 0x8000); +} + +void Spc700::DIV(uint8_t operand) { + if (operand == 0) { + // Handle divide by zero error + return; + } + uint8_t quotient = A / operand; + uint8_t remainder = A % operand; + A = quotient; + Y = remainder; + PSW.Z = (quotient == 0); + PSW.N = (quotient & 0x80); +} + +void Spc700::BRA(int8_t offset) { PC += offset; } + +void Spc700::BEQ(int8_t offset) { + if (PSW.Z) { + PC += offset; + } +} + +void Spc700::BNE(int8_t offset) { + if (!PSW.Z) { + PC += offset; + } +} + +void Spc700::BCS(int8_t offset) { + if (PSW.C) { + PC += offset; + } +} + +void Spc700::BCC(int8_t offset) { + if (!PSW.C) { + PC += offset; + } +} + +void Spc700::BVS(int8_t offset) { + if (PSW.V) { + PC += offset; + } +} + +void Spc700::BVC(int8_t offset) { + if (!PSW.V) { + PC += offset; + } +} + +void Spc700::BMI(int8_t offset) { + if (PSW.N) { + PC += offset; + } +} + +void Spc700::BPL(int8_t offset) { + if (!PSW.N) { + PC += offset; + } +} + +void Spc700::BBS(uint8_t bit, uint8_t operand) { + if (operand & (1 << bit)) { + PC += rel(); + } +} + +void Spc700::BBC(uint8_t bit, uint8_t operand) { + if (!(operand & (1 << bit))) { + PC += rel(); + } +} + +// CBNE DBNZ +// JMP +void Spc700::JMP(uint16_t address) { PC = address; } + +void Spc700::CALL(uint16_t address) { + uint16_t return_address = PC + 2; + write(SP, return_address & 0xFF); + write(SP - 1, (return_address >> 8) & 0xFF); + SP -= 2; + PC = address; +} + +void Spc700::PCALL(uint8_t offset) { + uint16_t return_address = PC + 2; + write(SP, return_address & 0xFF); + write(SP - 1, (return_address >> 8) & 0xFF); + SP -= 2; + PC += offset; +} + +void Spc700::TCALL(uint8_t offset) { + uint16_t return_address = PC + 2; + write(SP, return_address & 0xFF); + write(SP - 1, (return_address >> 8) & 0xFF); + SP -= 2; + PC = 0xFFDE + offset; +} + +void Spc700::BRK() { + uint16_t return_address = PC + 2; + write(SP, return_address & 0xFF); + write(SP - 1, (return_address >> 8) & 0xFF); + SP -= 2; + PC = 0xFFDE; +} + +void Spc700::RET() { + uint16_t return_address = read(SP) | (read(SP + 1) << 8); + SP += 2; + PC = return_address; +} + +void Spc700::RETI() { + uint16_t return_address = read(SP) | (read(SP + 1) << 8); + SP += 2; + PC = return_address; + PSW.I = 1; +} + +void Spc700::PUSH(uint8_t operand) { + write(SP, operand); + SP--; +} + +void Spc700::POP(uint8_t& operand) { + SP++; + operand = read(SP); +} + +void Spc700::SET1(uint8_t bit, uint8_t& operand) { operand |= (1 << bit); } + +void Spc700::CLR1(uint8_t bit, uint8_t& operand) { operand &= ~(1 << bit); } + +void Spc700::TSET1(uint8_t bit, uint8_t& operand) { + PSW.C = (operand & (1 << bit)); + operand |= (1 << bit); +} + +void Spc700::TCLR1(uint8_t bit, uint8_t& operand) { + PSW.C = (operand & (1 << bit)); + operand &= ~(1 << bit); +} + +void Spc700::AND1(uint8_t bit, uint8_t& operand) { + operand &= (1 << bit); + PSW.Z = (operand == 0); + PSW.N = (operand & 0x80); +} + +void Spc700::OR1(uint8_t bit, uint8_t& operand) { + operand |= (1 << bit); + PSW.Z = (operand == 0); + PSW.N = (operand & 0x80); +} + +void Spc700::EOR1(uint8_t bit, uint8_t& operand) { + operand ^= (1 << bit); + PSW.Z = (operand == 0); + PSW.N = (operand & 0x80); +} + +void Spc700::NOT1(uint8_t bit, uint8_t& operand) { + operand ^= (1 << bit); + PSW.Z = (operand == 0); + PSW.N = (operand & 0x80); +} + +void Spc700::MOV1(uint8_t bit, uint8_t& operand) { + PSW.C = (operand & (1 << bit)); + operand |= (1 << bit); +} + +void Spc700::CLRC() { PSW.C = 0; } + +void Spc700::SETC() { PSW.C = 1; } + +void Spc700::NOTC() { PSW.C = !PSW.C; } + +void Spc700::CLRV() { PSW.V = 0; } + +void Spc700::CLRP() { PSW.P = 0; } + +void Spc700::SETP() { PSW.P = 1; } + +void Spc700::EI() { PSW.I = 1; } + +void Spc700::DI() { PSW.I = 0; } + +void Spc700::NOP() { PC++; } + +void Spc700::SLEEP() {} + +void Spc700::STOP() {} + +} // namespace emu +} // namespace app +} // namespace yaze \ No newline at end of file diff --git a/src/app/emu/audio/spc700.cc b/src/app/emu/audio/spc700.cc index 3bb7cf1b..4d6d5517 100644 --- a/src/app/emu/audio/spc700.cc +++ b/src/app/emu/audio/spc700.cc @@ -12,20 +12,21 @@ namespace yaze { namespace app { namespace emu { -void Spc700::Reset() {} +void Spc700::Reset() { + PC = 0; + A = 0; + X = 0; + Y = 0; + SP = 0xFF; + PSW = ByteToFlags(0x00); + aram_.reset(); +} void Spc700::BootIplRom() { PC = 0xFFC0; A = 0; X = 0; Y = 0; - - // for (int i = 0; i < 0x40; ++i) { - // uint8_t opcode = read(PC); - // ExecuteInstructions(opcode); - // PC++; - // } - int i = 0; while (PC != 0xFFC0 + 0x3F) { uint8_t opcode = read(PC); @@ -615,16 +616,27 @@ void Spc700::ExecuteInstructions(uint8_t opcode) { case 0x1F: // JMP [!abs+X] break; - // . subroutines - + // . subroutines case 0x3F: // CALL !abs + { + CALL(abs()); break; + } case 0x4F: // PCALL up + { + PCALL(imm()); break; + } case 0x6F: // RET + { + RET(); break; + } case 0x7F: // RETI + { + RETI(); break; + } // . stack case 0x2D: // PUSH A diff --git a/src/app/emu/audio/spc700.h b/src/app/emu/audio/spc700.h index f8a2ceb2..f40491b2 100644 --- a/src/app/emu/audio/spc700.h +++ b/src/app/emu/audio/spc700.h @@ -13,27 +13,23 @@ namespace emu { class AudioRam { public: virtual ~AudioRam() = default; - - // Read a byte from ARAM at the given address + virtual void reset() = 0; virtual uint8_t read(uint16_t address) const = 0; - - // Write a byte to ARAM at the given address virtual void write(uint16_t address, uint8_t value) = 0; }; class AudioRamImpl : public AudioRam { - static const size_t ARAM_SIZE = 64 * 1024; // 64 KB + static const int ARAM_SIZE = 0x10000; std::vector ram = std::vector(ARAM_SIZE, 0); public: AudioRamImpl() = default; + void reset() override { ram = std::vector(ARAM_SIZE, 0); } - // Read a byte from ARAM at the given address uint8_t read(uint16_t address) const override { return ram[address % ARAM_SIZE]; } - // Write a byte to ARAM at the given address void write(uint16_t address, uint8_t value) override { ram[address % ARAM_SIZE] = value; } @@ -54,17 +50,14 @@ class Spc700 { public: explicit Spc700(AudioRam& aram) : aram_(aram) {} - uint8_t test_register_; - uint8_t control_register_; - uint8_t dsp_address_register_; // Registers - uint8_t A = 0; // 8-bit accumulator - uint8_t X = 0; // 8-bit index - uint8_t Y = 0; // 8-bit index - uint16_t YA = 0; // 16-bit pair of A (lsb) and Y (msb) - uint16_t PC = 0; // program counter - uint8_t SP = 0; // stack pointer + uint8_t A = 0x00; // 8-bit accumulator + uint8_t X = 0x00; // 8-bit index + uint8_t Y = 0x00; // 8-bit index + uint16_t YA = 0x00; // 16-bit pair of A (lsb) and Y (msb) + uint16_t PC = 0xFFC0; // program counter + uint8_t SP = 0x00; // stack pointer struct Flags { uint8_t N : 1; // Negative flag @@ -105,42 +98,26 @@ class Spc700 { // Read a byte from the memory-mapped registers uint8_t read(uint16_t address) { - switch (address) { - case 0xF0: - return test_register_; - case 0xF1: - return control_register_; - case 0xF2: - return dsp_address_register_; - default: - if (address < 0xFFC0) { - return aram_.read(address); - } else { - // Handle IPL ROM or RAM reads here - return ipl_rom_[address - 0xFFC0]; - } + if (address < 0xFFC0) { + return aram_.read(address); + } else { + // Check if register is set to unmap the IPL ROM + if (read(0xF1) & 0x80) { + return aram_.read(address); + } + return ipl_rom_[address - 0xFFC0]; } - return 0; } // Write a byte to the memory-mapped registers void write(uint16_t address, uint8_t value) { - switch (address) { - case 0xF0: - test_register_ = value; - break; - case 0xF1: - control_register_ = value; - break; - case 0xF2: - dsp_address_register_ = value; - break; - default: - if (address < 0xFFC0) { - aram_.write(address, value); - } else { - // Handle IPL ROM or RAM writes here - } + if (address < 0xFFC0) { + aram_.write(address, value); + } else { + // Check if register is set to unmap the IPL ROM + if (read(0xF1) & 0x80) { + aram_.write(address, value); + } } } @@ -148,87 +125,36 @@ class Spc700 { // Addressing modes // Immediate - uint8_t imm() { - PC++; - return read(PC); - } + uint8_t imm(); // Direct page - uint8_t dp() { - PC++; - uint8_t offset = read(PC); - return read((PSW.P << 8) + offset); - } + uint8_t dp(); - uint8_t get_dp_addr() { - PC++; - uint8_t offset = read(PC); - return (PSW.P << 8) + offset; - } + uint8_t get_dp_addr(); // Direct page indexed by X - uint8_t dp_plus_x() { - PC++; - uint8_t offset = read(PC); - return read((PSW.P << 8) + offset + X); - } + uint8_t dp_plus_x(); // Direct page indexed by Y - uint8_t dp_plus_y() { - PC++; - uint8_t offset = read(PC); - return read((PSW.P << 8) + offset + Y); - } + uint8_t dp_plus_y(); // Indexed indirect (add index before 16-bit lookup). - uint16_t dp_plus_x_indirect() { - PC++; - uint8_t offset = read(PC); - uint16_t addr = read((PSW.P << 8) + offset + X) | - (read((PSW.P << 8) + offset + X + 1) << 8); - return addr; - } + uint16_t dp_plus_x_indirect(); // Indirect indexed (add index after 16-bit lookup). - uint16_t dp_indirect_plus_y() { - PC++; - uint8_t offset = read(PC); - uint16_t baseAddr = - read((PSW.P << 8) + offset) | (read((PSW.P << 8) + offset + 1) << 8); - return baseAddr + Y; - } + uint16_t dp_indirect_plus_y(); - uint16_t abs() { - PC++; - uint16_t addr = read(PC) | (read(PC) << 8); - return addr; - } + uint16_t abs(); - int8_t rel() { - PC++; - return static_cast(read(PC)); - } + int8_t rel(); - uint8_t i() { return read((PSW.P << 8) + X); } + uint8_t i(); - uint8_t i_postinc() { - uint8_t value = read((PSW.P << 8) + X); - X++; - return value; - } + uint8_t i_postinc(); - uint16_t addr_plus_i() { - PC++; - uint16_t addr = read(PC) | (read(PC) << 8); - return read(addr) + X; - } + uint16_t addr_plus_i(); - uint16_t addr_plus_i_indexed() { - PC++; - uint16_t addr = read(PC) | (read(PC) << 8); - addr += X; - return read(addr) | (read(addr + 1) << 8); - } + uint16_t addr_plus_i_indexed(); // ========================================================================== // Instructions diff --git a/src/app/emu/cpu/cpu.cc b/src/app/emu/cpu/cpu.cc index 8c1c5d03..62afea58 100644 --- a/src/app/emu/cpu/cpu.cc +++ b/src/app/emu/cpu/cpu.cc @@ -1876,6 +1876,7 @@ uint8_t CPU::GetInstructionLength(uint8_t opcode) { } } +// TODO: Implement 65816 interrupts. void CPU::HandleInterrupts() { if (GetInterruptFlag()) { return; diff --git a/src/app/emu/cpu/cpu.h b/src/app/emu/cpu/cpu.h index 84aae02a..d6019512 100644 --- a/src/app/emu/cpu/cpu.h +++ b/src/app/emu/cpu/cpu.h @@ -178,7 +178,6 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { // LDA addr, Y uint32_t AbsoluteIndexedY(); - // Test Me :) // Effective Address: // Bank: Program Bank Register (PBR) // High/low: The Indirect Address diff --git a/src/app/emu/snes.cc b/src/app/emu/snes.cc index 6617be78..13f5960a 100644 --- a/src/app/emu/snes.cc +++ b/src/app/emu/snes.cc @@ -329,17 +329,6 @@ void SNES::VBlankRoutine() { // ... } -void SNES::BootApuWithIPL() { - // 1. Check if the SPC700 is ready, else set a callback for when it becomes - // ready - if (!apu_.IsReadySignalReceived()) { - apu_.SetReadyCallback([this]() { this->StartApuDataTransfer(); }); - return; // Exit and wait for callback to be called - } - - StartApuDataTransfer(); -} - void SNES::StartApuDataTransfer() { // 2. Setting the starting address const uint16_t startAddress = 0x0200; diff --git a/src/app/gui/canvas.cc b/src/app/gui/canvas.cc index 201bd90f..f885bc92 100644 --- a/src/app/gui/canvas.cc +++ b/src/app/gui/canvas.cc @@ -389,6 +389,7 @@ void Canvas::DrawGrid(float grid_step) { draw_list_->PushClipRect(canvas_p0_, canvas_p1_, true); if (enable_grid_) { if (custom_step_ != 0.f) grid_step = custom_step_; + grid_step *= global_scale_; // Apply global scale to grid step for (float x = fmodf(scrolling_.x, grid_step); x < canvas_sz_.x; x += grid_step) diff --git a/src/app/gui/canvas.h b/src/app/gui/canvas.h index e7c5a26a..0db50e30 100644 --- a/src/app/gui/canvas.h +++ b/src/app/gui/canvas.h @@ -96,7 +96,7 @@ class Canvas { bool custom_canvas_size_ = false; bool is_hovered_ = false; - float custom_step_ = 8.0f; + float custom_step_ = 0.0f; float global_scale_ = 1.0f; ImDrawList* draw_list_; diff --git a/src/app/rom.cc b/src/app/rom.cc index 6b5563c0..f1c33777 100644 --- a/src/app/rom.cc +++ b/src/app/rom.cc @@ -202,6 +202,7 @@ absl::StatusOr ROM::Load2BppGraphics() { return sheet; } +// TODO: Load Links graphics from the ROM absl::Status ROM::LoadLinkGraphics() { const auto link_gfx_offset = 81920; // $10:8000 const auto link_gfx_length = 0x800; diff --git a/src/app/zelda3/dungeon/room_object.h b/src/app/zelda3/dungeon/room_object.h index 68d9d111..6233dc48 100644 --- a/src/app/zelda3/dungeon/room_object.h +++ b/src/app/zelda3/dungeon/room_object.h @@ -7,7 +7,7 @@ #include #include -#include "app/emu/cpu.h" +#include "app/emu/cpu/cpu.h" #include "app/emu/memory/memory.h" #include "app/emu/video/ppu.h" #include "app/gfx/snes_palette.h" @@ -147,7 +147,7 @@ class DungeonObjectRenderer : public SharedROM { int i = 0; while (true) { - uint8_t opcode = cpu.FetchByte(); + uint8_t opcode = cpu.ReadByte(cpu.PC); cpu.ExecuteInstruction(opcode); cpu.HandleInterrupts(); @@ -156,7 +156,7 @@ class DungeonObjectRenderer : public SharedROM { } i++; - UpdateObjectBitmap(); + // UpdateObjectBitmap(); } } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index dbd54e7f..ad9b0d36 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -26,6 +26,7 @@ add_executable( ../src/app/emu/cpu/internal/instructions.cc ../src/app/emu/cpu/internal/addressing.cc ../src/app/emu/audio/internal/addressing.cc + ../src/app/emu/audio/internal/instructions.cc ../src/app/emu/audio/apu.cc ../src/app/emu/video/ppu.cc ../src/app/emu/audio/dsp.cc diff --git a/test/emu/spc700_test.cc b/test/emu/spc700_test.cc index c63508c5..9ba1fb17 100644 --- a/test/emu/spc700_test.cc +++ b/test/emu/spc700_test.cc @@ -11,8 +11,9 @@ namespace emu { using testing::_; using testing::Return; -class MockAudioRAM : public AudioRam { +class MockAudioRam : public AudioRam { public: + MOCK_METHOD(void, reset, (), (override)); MOCK_METHOD(uint8_t, read, (uint16_t address), (const, override)); MOCK_METHOD(void, write, (uint16_t address, uint8_t value), (override)); }; @@ -21,7 +22,7 @@ class Spc700Test : public ::testing::Test { public: Spc700Test() = default; - testing::NiceMock audioRAM; + testing::NiceMock audioRAM; Spc700 spc700{audioRAM}; };