diff --git a/src/app/emu/apu.cc b/src/app/emu/apu.cc index c89461d0..fddec712 100644 --- a/src/app/emu/apu.cc +++ b/src/app/emu/apu.cc @@ -13,6 +13,9 @@ namespace emu { APU::APU(Memory& memory) : memory_(memory) {} void APU::Init() { + // Set the clock frequency + SetFrequency(kApuClockSpeed); + // Initialize registers // ... } @@ -25,7 +28,7 @@ void APU::Reset() { // ... } -void APU::Run(int cycles) { +void APU::Update() { // ... } diff --git a/src/app/emu/apu.h b/src/app/emu/apu.h index 5cf6df46..e1de33eb 100644 --- a/src/app/emu/apu.h +++ b/src/app/emu/apu.h @@ -3,6 +3,7 @@ #include +#include "app/emu/clock.h" #include "app/emu/mem.h" #include "app/emu/spc700.h" @@ -10,7 +11,11 @@ namespace yaze { namespace app { namespace emu { -class APU : public SPC700 { +const int kApuClockSpeed = 1024000; // 1.024 MHz +const int apuSampleRate = 32000; // 32 KHz +const int apuClocksPerSample = 64; // 64 clocks per sample + +class APU : public SPC700, public Clock { public: // Initializes the APU with the necessary resources and dependencies APU(Memory &memory); @@ -20,8 +25,8 @@ class APU : public SPC700 { // Resets the APU to its initial state void Reset(); - // Runs the APU for a specified number of clock cycles - void Run(int cycles); + // Runs the APU for one frame + void Update(); // Reads a byte from the specified APU register uint8_t ReadRegister(uint16_t address); diff --git a/src/app/emu/clock.h b/src/app/emu/clock.h new file mode 100644 index 00000000..a33ebcbe --- /dev/null +++ b/src/app/emu/clock.h @@ -0,0 +1,51 @@ +#ifndef YAZE_APP_EMU_CLOCK_H_ +#define YAZE_APP_EMU_CLOCK_H_ + +#include + +namespace yaze { +namespace app { +namespace emu { + +class Clock { + public: + Clock() = default; + virtual ~Clock() = default; + + void UpdateCycleCount(double deltaTime) { + accumulatedTime += deltaTime; + double cycleTime = 1.0 / frequency; + + while (accumulatedTime >= cycleTime) { + Cycle(); + accumulatedTime -= cycleTime; + } + } + + void Cycle() { + cycle++; + cycleCount++; + } + + void UpdateClock(double delta) { + UpdateCycleCount(delta); + ResetAccumulatedTime(); + } + + unsigned long long GetCycleCount() const { return cycleCount; } + float GetFrequency() const { return frequency; } + void SetFrequency(float new_frequency) { this->frequency = new_frequency; } + void ResetAccumulatedTime() { accumulatedTime = 0.0; } + + private: + uint64_t cycle = 0; // Current cycle + float frequency = 0.0; // Frequency of the clock in Hz + unsigned long long cycleCount = 0; // Total number of cycles executed + double accumulatedTime = 0.0; // Accumulated time since the last cycle update +}; + +} // namespace emu +} // namespace app +} // namespace yaze + +#endif // YAZE_APP_EMU_CLOCK_H_ \ No newline at end of file diff --git a/src/app/emu/cpu.cc b/src/app/emu/cpu.cc index 031adda7..43977572 100644 --- a/src/app/emu/cpu.cc +++ b/src/app/emu/cpu.cc @@ -69,18 +69,15 @@ uint8_t CPU::FetchByteDirectPage(uint8_t operand) { return fetchedByte; } -void CPU::Run() { - while (true) { - // Fetch the next opcode from memory at the current program counter - uint8_t opcode = memory.ReadByte(PC); +void CPU::Update() { + auto cycles_to_run = GetCycleCount(); - // Increment the program counter to point to the next instruction - PC++; + // Execute the calculated number of cycles + for (int i = 0; i < cycles_to_run; i++) { + // Fetch and execute an instruction + ExecuteInstruction(FetchByte()); - // Execute the instruction corresponding to the fetched opcode - ExecuteInstruction(opcode); - - // Optionally, handle interrupts or other external events + // Handle any interrupts, if necessary HandleInterrupts(); } } diff --git a/src/app/emu/cpu.h b/src/app/emu/cpu.h index 0c8c8fca..b104a530 100644 --- a/src/app/emu/cpu.h +++ b/src/app/emu/cpu.h @@ -6,6 +6,7 @@ #include #include +#include "app/emu/clock.h" #include "app/emu/log.h" #include "app/emu/mem.h" @@ -69,37 +70,15 @@ const std::unordered_map opcode_to_mnemonic = { }; -class Clock { - public: - Clock() = default; - virtual ~Clock() = default; - - void Cycle() { cycle++; } - void SetFrequency(float frequency) { this->frequency = frequency; } - float GetFrequency() const { return frequency; } - unsigned long long GetCycleCount() const { return cycleCount; } - - private: - uint64_t cycle; // Current cycle - float frequency; // Frequency of the clock in Hz - unsigned long long cycleCount; // Total number of cycles executed -}; +const int kCpuClockSpeed = 21477272; // 21.477272 MHz class CPU : public Memory, public Clock, public Loggable { public: explicit CPU(Memory& mem) : memory(mem) {} - void Init() { memory.ClearMemory(); } - - uint8_t ReadByte(uint16_t address) const override; - uint16_t ReadWord(uint16_t address) const override; - uint32_t ReadWordLong(uint16_t address) const override; - void WriteByte(uint32_t address, uint8_t value) override; - void WriteWord(uint32_t address, uint16_t value) override; - void SetMemory(const std::vector& data) override { - memory.SetMemory(data); + void Init() { + SetFrequency(kCpuClockSpeed); + memory.ClearMemory(); } - int16_t SP() const override { return memory.SP(); } - void SetSP(int16_t value) override { memory.SetSP(value); } uint8_t FetchByte(); uint16_t FetchWord(); @@ -109,7 +88,7 @@ class CPU : public Memory, public Clock, public Loggable { uint8_t FetchByteDirectPage(uint8_t operand); - void Run(); + void Update(); void ExecuteInstruction(uint8_t opcode); void HandleInterrupts(); @@ -1048,6 +1027,17 @@ class CPU : public Memory, public Clock, public Loggable { E = carry; } + uint8_t ReadByte(uint16_t address) const override; + uint16_t ReadWord(uint16_t address) const override; + uint32_t ReadWordLong(uint16_t address) const override; + void WriteByte(uint32_t address, uint8_t value) override; + void WriteWord(uint32_t address, uint16_t value) override; + void SetMemory(const std::vector& data) override { + memory.SetMemory(data); + } + int16_t SP() const override { return memory.SP(); } + void SetSP(int16_t value) override { memory.SetSP(value); } + private: void compare(uint16_t register_value, uint16_t memory_value) { uint16_t result; diff --git a/src/app/emu/ppu.cc b/src/app/emu/ppu.cc index 3926c652..b582b4cf 100644 --- a/src/app/emu/ppu.cc +++ b/src/app/emu/ppu.cc @@ -20,7 +20,7 @@ void PPU::RenderScanline() { // ... } -void PPU::Run(int cycles) { +void PPU::Update() { // Fetch the tile data from VRAM, tile map data from memory, and palette data // from CGRAM UpdateTileData(); // Fetches the tile data from VRAM and stores it in an diff --git a/src/app/emu/ppu.h b/src/app/emu/ppu.h index f78ae641..9785aaae 100644 --- a/src/app/emu/ppu.h +++ b/src/app/emu/ppu.h @@ -5,6 +5,7 @@ #include #include +#include "app/emu/clock.h" #include "app/emu/mem.h" namespace yaze { @@ -585,14 +586,14 @@ struct JoypadRegisters { // DMA Registers struct DmaRegisters { - uint8_t startDmaTransfer; // Register $420B - uint8_t enableHDmaTransfer; // Register $420C - uint8_t dmaControlRegister[8]; // Register $43?0 - uint8_t dmaDestinationAddress[8]; // Register $43?1 - uint32_t dmaSourceAddress[8]; // Register $43?2/$43?3/$43?4 - uint16_t bytesToTransfer[8]; // Register $43?5/$43?6/$43?7 - uint16_t hdmaCountPointer[8]; // Register $43?8/$43?9 - uint8_t scanlinesLeft[8]; // Register $43?A + uint8_t startDmaTransfer; // Register $420B + uint8_t enableHDmaTransfer; // Register $420C + uint8_t dmacontrol_register_ister[8]; // Register $43?0 + uint8_t dmaDestinationAddress[8]; // Register $43?1 + uint32_t dmaSourceAddress[8]; // Register $43?2/$43?3/$43?4 + uint16_t bytesToTransfer[8]; // Register $43?5/$43?6/$43?7 + uint16_t hdmaCountPointer[8]; // Register $43?8/$43?9 + uint8_t scanlinesLeft[8]; // Register $43?A }; // WRAM access Registers @@ -625,7 +626,9 @@ struct BackgroundLayer { bool enabled; // Whether the background layer is enabled }; -class PPU { +const int kPpuClockSpeed = 5369318; // 5.369318 MHz + +class PPU : public Clock { public: // Initializes the PPU with the necessary resources and dependencies PPU(Memory& memory); @@ -633,14 +636,15 @@ class PPU { void Init() { // Initialize the frame buffer with a size that corresponds to the // screen resolution + SetFrequency(kPpuClockSpeed); frame_buffer_.resize(256 * 240, 0); } // Resets the PPU to its initial state void Reset() { std::fill(frame_buffer_.begin(), frame_buffer_.end(), 0); } - // Runs the PPU for a specified number of clock cycles - void Run(int cycles); + // Runs the PPU for one frame. + void Update(); // Reads a byte from the specified PPU register uint8_t ReadRegister(uint16_t address); diff --git a/src/app/emu/snes.cc b/src/app/emu/snes.cc index aba2f6ad..a540f1b6 100644 --- a/src/app/emu/snes.cc +++ b/src/app/emu/snes.cc @@ -265,52 +265,37 @@ void SNES::Init(ROM& rom) { void SNES::Run() { running_ = true; - const int cpuClockSpeed = 21477272; // 21.477272 MHz - const int ppuClockSpeed = 5369318; // 5.369318 MHz - const int apuClockSpeed = 32000; // 32 KHz - const double targetFPS = 60.0; // 60 frames per second + const double targetFPS = 60.0; // 60 frames per second - const double cpuCycleTime = 1.0 / cpuClockSpeed; - const double ppuCycleTime = 1.0 / ppuClockSpeed; - const double apuCycleTime = 1.0 / apuClockSpeed; const double frameTime = 1.0 / targetFPS; - double cpuAccumulatedTime = 0.0; - double ppuAccumulatedTime = 0.0; - double apuAccumulatedTime = 0.0; - double frameAccumulatedTime = 0.0; + double frame_accumulated_time = 0.0; - auto lastTime = std::chrono::high_resolution_clock::now(); + auto last_time = std::chrono::high_resolution_clock::now(); if (running_) { - auto currentTime = std::chrono::high_resolution_clock::now(); - double deltaTime = - std::chrono::duration(currentTime - lastTime).count(); - lastTime = currentTime; + auto current_time = std::chrono::high_resolution_clock::now(); + double delta_time = + std::chrono::duration(current_time - last_time).count(); + last_time = current_time; - cpuAccumulatedTime += deltaTime; - ppuAccumulatedTime += deltaTime; - apuAccumulatedTime += deltaTime; - frameAccumulatedTime += deltaTime; + frame_accumulated_time += delta_time; - while (cpuAccumulatedTime >= cpuCycleTime) { - cpu.ExecuteInstruction(cpu.FetchByte()); - cpuAccumulatedTime -= cpuCycleTime; - } + // Update the CPU + cpu.UpdateClock(delta_time); + cpu.Update(); - while (ppuAccumulatedTime >= ppuCycleTime) { - RenderScanline(); - ppuAccumulatedTime -= ppuCycleTime; - } + // Update the PPU + ppu.UpdateClock(delta_time); + ppu.Update(); - while (apuAccumulatedTime >= apuCycleTime) { - // apu.Update(); - apuAccumulatedTime -= apuCycleTime; - } + // Update the APU + apu.UpdateClock(delta_time); + apu.Update(); - if (frameAccumulatedTime >= frameTime) { + if (frame_accumulated_time >= frameTime) { // renderer.Render(); - frameAccumulatedTime -= frameTime; + frame_accumulated_time -= frameTime; } HandleInput(); @@ -319,7 +304,7 @@ void SNES::Run() { // Enable NMI Interrupts void SNES::EnableVBlankInterrupts() { - vBlankFlag = false; + v_blank_flag_ = false; // Clear the RDNMI VBlank flag memory_.ReadByte(0x4210); // RDNMI @@ -330,10 +315,10 @@ void SNES::EnableVBlankInterrupts() { // Wait until the VBlank routine has been processed void SNES::WaitForVBlank() { - vBlankFlag = true; + v_blank_flag_ = true; - // Loop until `vBlankFlag` is clear - while (vBlankFlag) { + // Loop until `v_blank_flag_` is clear + while (v_blank_flag_) { std::this_thread::yield(); } } @@ -350,15 +335,15 @@ void SNES::NmiIsr() { cpu.DB = 0x80; // Assuming bank 0x80, can be changed to 0x00 cpu.D = 0; - if (vBlankFlag) { + if (v_blank_flag_) { VBlankRoutine(); - // Clear `vBlankFlag` - vBlankFlag = false; + // Clear `v_blank_flag_` + v_blank_flag_ = false; } - // Increment 32-bit frameCounter - frameCounter++; + // Increment 32-bit frame_counter_ + frame_counter_++; // Restore CPU registers cpu.PHB(); @@ -371,24 +356,6 @@ void SNES::VBlankRoutine() { // ... } -void SNES::RenderScanline() { - // Render background layers - for (int layer = 0; layer < 4; layer++) { - DrawBackgroundLayer(layer); - } - - // Render sprites - DrawSprites(); -} - -void SNES::DrawBackgroundLayer(int layer) { - // ... -} - -void SNES::DrawSprites() { - // ... -} - void SNES::HandleInput() { // ... } diff --git a/src/app/emu/snes.h b/src/app/emu/snes.h index 5ded5a6f..3b2b945e 100644 --- a/src/app/emu/snes.h +++ b/src/app/emu/snes.h @@ -82,11 +82,6 @@ class SNES : public DMA { // VBlank routine void VBlankRoutine(); - // Functions for PPU-related operations - void RenderScanline(); - void DrawBackgroundLayer(int layer); - void DrawSprites(); - // Controller input handling void HandleInput(); @@ -118,11 +113,11 @@ class SNES : public DMA { std::vector rom_data; // Byte flag to indicate if the VBlank routine should be executed or not - std::atomic vBlankFlag; + std::atomic v_blank_flag_; // 32-bit counter to track the number of NMI interrupts (useful for clocks and // timers) - std::atomic frameCounter; + std::atomic frame_counter_; // Other private member variables bool running_; diff --git a/src/app/emu/spc700.cc b/src/app/emu/spc700.cc index f1b23c90..3d3f34f6 100644 --- a/src/app/emu/spc700.cc +++ b/src/app/emu/spc700.cc @@ -11,6 +11,7 @@ void SPC700::ExecuteInstructions(uint8_t opcode) { switch (opcode) { // 8-bit Move Memory to Register case 0xE8: // MOV A, #imm + MOV(A, true); break; case 0xE6: // MOV A, (X) break; diff --git a/src/app/emu/spc700.h b/src/app/emu/spc700.h index 86c96e97..9fb606dd 100644 --- a/src/app/emu/spc700.h +++ b/src/app/emu/spc700.h @@ -65,9 +65,9 @@ class SDSP { class SPC700 { AudioRAM aram; SDSP sdsp; - uint8_t testReg; - uint8_t controlReg; - uint8_t dspAddrReg; + uint8_t test_register_; + uint8_t control_register_; + uint8_t dsp_address_register_; // Registers uint8_t A; // 8-bit accumulator @@ -95,13 +95,13 @@ class SPC700 { uint8_t read(uint16_t address) { switch (address) { case 0xF0: - return testReg; + return test_register_; case 0xF1: - return controlReg; + return control_register_; case 0xF2: - return dspAddrReg; + return dsp_address_register_; case 0xF3: - return sdsp.readGlobalReg(dspAddrReg); + return sdsp.readGlobalReg(dsp_address_register_); default: if (address < 0xFFC0) { return aram.read(address); @@ -116,16 +116,16 @@ class SPC700 { void write(uint16_t address, uint8_t value) { switch (address) { case 0xF0: - testReg = value; + test_register_ = value; break; case 0xF1: - controlReg = value; + control_register_ = value; break; case 0xF2: - dspAddrReg = value; + dsp_address_register_ = value; break; case 0xF3: - sdsp.writeGlobalReg(dspAddrReg, value); + sdsp.writeGlobalReg(dsp_address_register_, value); break; default: if (address < 0xFFC0) { @@ -182,7 +182,7 @@ class SPC700 { // Instructions // MOV - void MOV(uint8_t operand, bool isImmediate) { + void MOV(uint8_t operand, bool isImmediate = false) { uint8_t value = isImmediate ? imm() : operand; operand = value; PSW.Z = (operand == 0);