PPU upgrades, move Memory to own dir
This commit is contained in:
@@ -53,6 +53,7 @@ void Emulator::RenderNavBar() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::BeginMenu("Debug")) {
|
if (ImGui::BeginMenu("Debug")) {
|
||||||
|
ImGui::MenuItem("PPU Register Viewer", nullptr, &show_ppu_reg_viewer_);
|
||||||
MENU_ITEM("Debugger") {}
|
MENU_ITEM("Debugger") {}
|
||||||
MENU_ITEM("Memory Viewer") {}
|
MENU_ITEM("Memory Viewer") {}
|
||||||
MENU_ITEM("Tile Viewer") {}
|
MENU_ITEM("Tile Viewer") {}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ class Emulator : public SharedROM {
|
|||||||
emu::SNES snes_;
|
emu::SNES snes_;
|
||||||
|
|
||||||
bool running_ = false;
|
bool running_ = false;
|
||||||
|
bool show_ppu_reg_viewer_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace core
|
} // namespace core
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#include "app/emu/audio/dsp.h"
|
#include "app/emu/audio/dsp.h"
|
||||||
#include "app/emu/audio/spc700.h"
|
#include "app/emu/audio/spc700.h"
|
||||||
#include "app/emu/clock.h"
|
#include "app/emu/clock.h"
|
||||||
#include "app/emu/mem.h"
|
#include "app/emu/memory/memory.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
#include "app/emu/audio/dsp.h"
|
#include "app/emu/audio/dsp.h"
|
||||||
#include "app/emu/audio/spc700.h"
|
#include "app/emu/audio/spc700.h"
|
||||||
#include "app/emu/clock.h"
|
#include "app/emu/clock.h"
|
||||||
#include "app/emu/mem.h"
|
#include "app/emu/memory/memory.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include "app/emu/audio/dsp.h"
|
#include "app/emu/audio/dsp.h"
|
||||||
|
|
||||||
#include "app/emu/mem.h"
|
#include "app/emu/memory/memory.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "app/emu/mem.h"
|
#include "app/emu/memory/memory.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
#include "app/emu/clock.h"
|
#include "app/emu/clock.h"
|
||||||
#include "app/emu/debug/log.h"
|
#include "app/emu/debug/log.h"
|
||||||
#include "app/emu/mem.h"
|
#include "app/emu/memory/memory.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
@@ -683,6 +683,10 @@ class CPU : public Memory, public Loggable {
|
|||||||
bool GetFlag(uint8_t mask) const { return (status & mask) != 0; }
|
bool GetFlag(uint8_t mask) const { return (status & mask) != 0; }
|
||||||
|
|
||||||
// Appease the C++ Gods...
|
// Appease the C++ Gods...
|
||||||
|
std::vector<uint8_t> ReadByteVector(uint16_t address,
|
||||||
|
uint16_t size) const override {
|
||||||
|
return memory.ReadByteVector(address, size);
|
||||||
|
}
|
||||||
void PushByte(uint8_t value) override { memory.PushByte(value); }
|
void PushByte(uint8_t value) override { memory.PushByte(value); }
|
||||||
void PushWord(uint16_t value) override { memory.PushWord(value); }
|
void PushWord(uint16_t value) override { memory.PushWord(value); }
|
||||||
uint8_t PopByte() override { return memory.PopByte(); }
|
uint8_t PopByte() override { return memory.PopByte(); }
|
||||||
|
|||||||
59
src/app/emu/memory/dma.h
Normal file
59
src/app/emu/memory/dma.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
#ifndef YAZE_APP_EMU_MEMORY_DMA_H
|
||||||
|
#define YAZE_APP_EMU_MEMORY_DMA_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace app {
|
||||||
|
namespace emu {
|
||||||
|
|
||||||
|
// Direct Memory Address
|
||||||
|
class DMA {
|
||||||
|
public:
|
||||||
|
DMA() {
|
||||||
|
// Initialize DMA and HDMA channels
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
channels[i].DMAPn = 0;
|
||||||
|
channels[i].BBADn = 0;
|
||||||
|
channels[i].UNUSEDn = 0;
|
||||||
|
channels[i].A1Tn = 0xFFFFFF;
|
||||||
|
channels[i].DASn = 0xFFFF;
|
||||||
|
channels[i].A2An = 0xFFFF;
|
||||||
|
channels[i].NLTRn = 0xFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DMA Transfer Modes
|
||||||
|
enum class DMA_TRANSFER_TYPE {
|
||||||
|
OAM,
|
||||||
|
PPUDATA,
|
||||||
|
CGDATA,
|
||||||
|
FILL_VRAM,
|
||||||
|
CLEAR_VRAM,
|
||||||
|
RESET_VRAM
|
||||||
|
};
|
||||||
|
|
||||||
|
// Functions for handling DMA and HDMA transfers
|
||||||
|
void StartDMATransfer(uint8_t channels);
|
||||||
|
void EnableHDMATransfers(uint8_t channels);
|
||||||
|
|
||||||
|
// Structure for DMA and HDMA channel registers
|
||||||
|
struct Channel {
|
||||||
|
uint8_t DMAPn; // DMA/HDMA parameters
|
||||||
|
uint8_t BBADn; // B-bus address
|
||||||
|
uint8_t UNUSEDn; // Unused byte
|
||||||
|
uint32_t A1Tn; // DMA Current Address / HDMA Table Start Address
|
||||||
|
uint16_t DASn; // DMA Byte-Counter / HDMA indirect table address
|
||||||
|
uint16_t A2An; // HDMA Table Current Address
|
||||||
|
uint8_t NLTRn; // HDMA Line-Counter
|
||||||
|
};
|
||||||
|
Channel channels[8];
|
||||||
|
|
||||||
|
uint8_t MDMAEN = 0; // Start DMA transfer
|
||||||
|
uint8_t HDMAEN = 0; // Enable HDMA transfers
|
||||||
|
};
|
||||||
|
} // namespace emu
|
||||||
|
} // namespace app
|
||||||
|
} // namespace yaze
|
||||||
|
|
||||||
|
#endif // YAZE_APP_EMU_MEMORY_DMA_H
|
||||||
@@ -24,26 +24,6 @@
|
|||||||
// 2000-FFFF System RAM
|
// 2000-FFFF System RAM
|
||||||
// 7F 0000-FFFF System RAM
|
// 7F 0000-FFFF System RAM
|
||||||
|
|
||||||
// HiROM (Mode 21):
|
|
||||||
|
|
||||||
// Banks Offset Purpose
|
|
||||||
// 00-3F 0000-1FFF LowRAM (shadowed from 7E)
|
|
||||||
// 2000-2FFF PPU1, APU
|
|
||||||
// 3000-3FFF SFX, DSP, etc.
|
|
||||||
// 4000-41FF Controller
|
|
||||||
// 4200-5FFF PPU2, DMA, etc.
|
|
||||||
// 6000-7FFF SRAM (256KB)
|
|
||||||
// 8000-FFFF 32k ROM Chunk
|
|
||||||
// 40-6F 0000-FFFF 64k ROM Chunk
|
|
||||||
// 70-77 0000-FFFF SRAM (256KB)
|
|
||||||
// 78-7D 0000-FFFF Never Used
|
|
||||||
// 7E 0000-1FFF LowRAM
|
|
||||||
// 2000-7FFF HighRAM
|
|
||||||
// 8000-FFFF Expanded RAM
|
|
||||||
// 7F 0000-FFFF More Expanded RAM
|
|
||||||
// 80-EF 0000-FFFF Mirror of 00-6F
|
|
||||||
// F0-FF 0000-FFFF 64k ROM Chunk
|
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
@@ -115,6 +95,15 @@ class Observer {
|
|||||||
virtual void Notify(uint32_t address, uint8_t data) = 0;
|
virtual void Notify(uint32_t address, uint8_t data) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr uint32_t kROMStart = 0xC00000;
|
||||||
|
constexpr uint32_t kROMSize = 0x400000;
|
||||||
|
constexpr uint32_t kRAMStart = 0x7E0000;
|
||||||
|
constexpr uint32_t kRAMSize = 0x20000;
|
||||||
|
constexpr uint32_t kVRAMStart = 0x210000;
|
||||||
|
constexpr uint32_t kVRAMSize = 0x10000;
|
||||||
|
constexpr uint32_t kOAMStart = 0x218000;
|
||||||
|
constexpr uint32_t kOAMSize = 0x220;
|
||||||
|
|
||||||
// memory.h
|
// memory.h
|
||||||
class Memory {
|
class Memory {
|
||||||
public:
|
public:
|
||||||
@@ -122,6 +111,8 @@ class Memory {
|
|||||||
virtual uint8_t ReadByte(uint16_t address) const = 0;
|
virtual uint8_t ReadByte(uint16_t address) const = 0;
|
||||||
virtual uint16_t ReadWord(uint16_t address) const = 0;
|
virtual uint16_t ReadWord(uint16_t address) const = 0;
|
||||||
virtual uint32_t ReadWordLong(uint16_t address) const = 0;
|
virtual uint32_t ReadWordLong(uint16_t address) const = 0;
|
||||||
|
virtual std::vector<uint8_t> ReadByteVector(uint16_t address,
|
||||||
|
uint16_t length) const = 0;
|
||||||
|
|
||||||
virtual void WriteByte(uint32_t address, uint8_t value) = 0;
|
virtual void WriteByte(uint32_t address, uint8_t value) = 0;
|
||||||
virtual void WriteWord(uint32_t address, uint16_t value) = 0;
|
virtual void WriteWord(uint32_t address, uint16_t value) = 0;
|
||||||
@@ -154,8 +145,7 @@ class MemoryImpl : public Memory, public Loggable {
|
|||||||
const size_t HARDWARE_REGISTERS_SIZE = 0x4000; // 16 KB
|
const size_t HARDWARE_REGISTERS_SIZE = 0x4000; // 16 KB
|
||||||
|
|
||||||
// Clear memory
|
// Clear memory
|
||||||
memory_.clear();
|
std::fill(memory_.begin(), memory_.end(), 0);
|
||||||
memory_.resize(0x1000000, 0); // 24-bit address space
|
|
||||||
|
|
||||||
// Load ROM data into memory based on LoROM mapping
|
// Load ROM data into memory based on LoROM mapping
|
||||||
size_t romSize = romData.size();
|
size_t romSize = romData.size();
|
||||||
@@ -244,6 +234,13 @@ class MemoryImpl : public Memory, public Loggable {
|
|||||||
(static_cast<uint32_t>(memory_.at(mapped_address + 1)) << 8) |
|
(static_cast<uint32_t>(memory_.at(mapped_address + 1)) << 8) |
|
||||||
(static_cast<uint32_t>(memory_.at(mapped_address + 2)) << 16);
|
(static_cast<uint32_t>(memory_.at(mapped_address + 2)) << 16);
|
||||||
}
|
}
|
||||||
|
std::vector<uint8_t> ReadByteVector(uint16_t address,
|
||||||
|
uint16_t length) const override {
|
||||||
|
uint32_t mapped_address = GetMappedAddress(address);
|
||||||
|
NotifyObservers(mapped_address, /*data=*/0);
|
||||||
|
return std::vector<uint8_t>(memory_.begin() + mapped_address,
|
||||||
|
memory_.begin() + mapped_address + length);
|
||||||
|
}
|
||||||
|
|
||||||
void WriteByte(uint32_t address, uint8_t value) override {
|
void WriteByte(uint32_t address, uint8_t value) override {
|
||||||
uint32_t mapped_address = GetMappedAddress(address);
|
uint32_t mapped_address = GetMappedAddress(address);
|
||||||
@@ -305,9 +302,10 @@ class MemoryImpl : public Memory, public Loggable {
|
|||||||
// Stack Pointer access.
|
// Stack Pointer access.
|
||||||
int16_t SP() const override { return SP_; }
|
int16_t SP() const override { return SP_; }
|
||||||
void SetSP(int16_t value) override { SP_ = value; }
|
void SetSP(int16_t value) override { SP_ = value; }
|
||||||
|
void ClearMemory() override { std::fill(memory_.begin(), memory_.end(), 0); }
|
||||||
void SetMemory(const std::vector<uint8_t>& data) override { memory_ = data; }
|
void SetMemory(const std::vector<uint8_t>& data) override {
|
||||||
void ClearMemory() override { memory_.resize(64000, 0x00); }
|
std::copy(data.begin(), data.end(), memory_.begin());
|
||||||
|
}
|
||||||
void LoadData(const std::vector<uint8_t>& data) override {
|
void LoadData(const std::vector<uint8_t>& data) override {
|
||||||
std::copy(data.begin(), data.end(), memory_.begin());
|
std::copy(data.begin(), data.end(), memory_.begin());
|
||||||
}
|
}
|
||||||
@@ -357,15 +355,6 @@ class MemoryImpl : public Memory, public Loggable {
|
|||||||
return address; // Return the original address if no mapping is defined
|
return address; // Return the original address if no mapping is defined
|
||||||
}
|
}
|
||||||
|
|
||||||
static const uint32_t kROMStart = 0xC00000;
|
|
||||||
static const uint32_t kROMSize = 0x400000;
|
|
||||||
static const uint32_t kRAMStart = 0x7E0000;
|
|
||||||
static const uint32_t kRAMSize = 0x20000;
|
|
||||||
static const uint32_t kVRAMStart = 0x210000;
|
|
||||||
static const uint32_t kVRAMSize = 0x10000;
|
|
||||||
static const uint32_t kOAMStart = 0x218000;
|
|
||||||
static const uint32_t kOAMSize = 0x220;
|
|
||||||
|
|
||||||
void NotifyObservers(uint32_t address, uint8_t data) const {
|
void NotifyObservers(uint32_t address, uint8_t data) const {
|
||||||
for (auto observer : observers_) {
|
for (auto observer : observers_) {
|
||||||
observer->Notify(address, data);
|
observer->Notify(address, data);
|
||||||
@@ -375,7 +364,7 @@ class MemoryImpl : public Memory, public Loggable {
|
|||||||
std::vector<Observer*> observers_;
|
std::vector<Observer*> observers_;
|
||||||
|
|
||||||
// Memory (64KB)
|
// Memory (64KB)
|
||||||
std::vector<uint8_t> memory_;
|
std::array<uint8_t, 0x10000> memory_;
|
||||||
|
|
||||||
// Stack Pointer
|
// Stack Pointer
|
||||||
uint16_t SP_ = 0x01FF;
|
uint16_t SP_ = 0x01FF;
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
#include "app/emu/clock.h"
|
#include "app/emu/clock.h"
|
||||||
#include "app/emu/cpu.h"
|
#include "app/emu/cpu.h"
|
||||||
#include "app/emu/debug/debugger.h"
|
#include "app/emu/debug/debugger.h"
|
||||||
#include "app/emu/mem.h"
|
#include "app/emu/memory/memory.h"
|
||||||
#include "app/emu/video/ppu.h"
|
#include "app/emu/video/ppu.h"
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,17 @@
|
|||||||
|
#include <SDL_mixer.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
#include "app/emu/audio/apu.h"
|
#include "app/emu/audio/apu.h"
|
||||||
#include "app/emu/audio/spc700.h"
|
#include "app/emu/audio/spc700.h"
|
||||||
#include "app/emu/clock.h"
|
#include "app/emu/clock.h"
|
||||||
#include "app/emu/cpu.h"
|
#include "app/emu/cpu.h"
|
||||||
#include "app/emu/debug/debugger.h"
|
#include "app/emu/debug/debugger.h"
|
||||||
|
#include "app/emu/memory/dma.h"
|
||||||
|
#include "app/emu/memory/memory.h"
|
||||||
#include "app/emu/video/ppu.h"
|
#include "app/emu/video/ppu.h"
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
|
|
||||||
@@ -13,52 +19,6 @@ namespace yaze {
|
|||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
|
||||||
// Direct Memory Address
|
|
||||||
class DMA {
|
|
||||||
public:
|
|
||||||
DMA() {
|
|
||||||
// Initialize DMA and HDMA channels
|
|
||||||
for (int i = 0; i < 8; ++i) {
|
|
||||||
channels[i].DMAPn = 0;
|
|
||||||
channels[i].BBADn = 0;
|
|
||||||
channels[i].UNUSEDn = 0;
|
|
||||||
channels[i].A1Tn = 0xFFFFFF;
|
|
||||||
channels[i].DASn = 0xFFFF;
|
|
||||||
channels[i].A2An = 0xFFFF;
|
|
||||||
channels[i].NLTRn = 0xFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DMA Transfer Modes
|
|
||||||
enum class DMA_TRANSFER_TYPE {
|
|
||||||
OAM,
|
|
||||||
PPUDATA,
|
|
||||||
CGDATA,
|
|
||||||
FILL_VRAM,
|
|
||||||
CLEAR_VRAM,
|
|
||||||
RESET_VRAM
|
|
||||||
};
|
|
||||||
|
|
||||||
// Functions for handling DMA and HDMA transfers
|
|
||||||
void StartDMATransfer(uint8_t channels);
|
|
||||||
void EnableHDMATransfers(uint8_t channels);
|
|
||||||
|
|
||||||
// Structure for DMA and HDMA channel registers
|
|
||||||
struct Channel {
|
|
||||||
uint8_t DMAPn; // DMA/HDMA parameters
|
|
||||||
uint8_t BBADn; // B-bus address
|
|
||||||
uint8_t UNUSEDn; // Unused byte
|
|
||||||
uint32_t A1Tn; // DMA Current Address / HDMA Table Start Address
|
|
||||||
uint16_t DASn; // DMA Byte-Counter / HDMA indirect table address
|
|
||||||
uint16_t A2An; // HDMA Table Current Address
|
|
||||||
uint8_t NLTRn; // HDMA Line-Counter
|
|
||||||
};
|
|
||||||
Channel channels[8];
|
|
||||||
|
|
||||||
uint8_t MDMAEN = 0; // Start DMA transfer
|
|
||||||
uint8_t HDMAEN = 0; // Enable HDMA transfers
|
|
||||||
};
|
|
||||||
|
|
||||||
class SNES : public DMA {
|
class SNES : public DMA {
|
||||||
public:
|
public:
|
||||||
SNES() = default;
|
SNES() = default;
|
||||||
@@ -118,13 +78,13 @@ class SNES : public DMA {
|
|||||||
ROMInfo rom_info_;
|
ROMInfo rom_info_;
|
||||||
Debugger debugger;
|
Debugger debugger;
|
||||||
|
|
||||||
|
// Currently loaded ROM
|
||||||
std::vector<uint8_t> rom_data;
|
std::vector<uint8_t> rom_data;
|
||||||
|
|
||||||
// Byte flag to indicate if the VBlank routine should be executed or not
|
// Byte flag to indicate if the VBlank routine should be executed or not
|
||||||
std::atomic<bool> v_blank_flag_;
|
std::atomic<bool> v_blank_flag_;
|
||||||
|
|
||||||
// 32-bit counter to track the number of NMI interrupts (useful for clocks and
|
// 32-bit counter to track the number of NMI interrupts
|
||||||
// timers)
|
|
||||||
std::atomic<uint32_t> frame_counter_;
|
std::atomic<uint32_t> frame_counter_;
|
||||||
|
|
||||||
// Other private member variables
|
// Other private member variables
|
||||||
|
|||||||
@@ -4,12 +4,45 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "app/emu/mem.h"
|
#include "app/emu/memory/memory.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
|
||||||
|
using namespace PPURegisters;
|
||||||
|
|
||||||
|
void PPU::Update() {
|
||||||
|
auto cycles_to_run = clock_.GetCycleCount();
|
||||||
|
|
||||||
|
UpdateInternalState(cycles_to_run);
|
||||||
|
|
||||||
|
// Render however many scanlines we're supposed to.
|
||||||
|
if (current_scanline_ < visibleScanlines) {
|
||||||
|
// Render the current scanline
|
||||||
|
RenderScanline();
|
||||||
|
|
||||||
|
// Increment the current scanline
|
||||||
|
current_scanline_++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PPU::UpdateInternalState(int cycles) {
|
||||||
|
// Update the PPU's internal state based on the number of cycles
|
||||||
|
cycle_count_ += cycles;
|
||||||
|
|
||||||
|
// Check if it's time to move to the next scanline
|
||||||
|
if (cycle_count_ >= cyclesPerScanline) {
|
||||||
|
current_scanline_++;
|
||||||
|
cycle_count_ -= cyclesPerScanline;
|
||||||
|
|
||||||
|
// If we've reached the end of the frame, reset to the first scanline
|
||||||
|
if (current_scanline_ >= totalScanlines) {
|
||||||
|
current_scanline_ = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void PPU::RenderScanline() {
|
void PPU::RenderScanline() {
|
||||||
// Fetch the tile data from VRAM, tile map data from memory, and palette data
|
// Fetch the tile data from VRAM, tile map data from memory, and palette data
|
||||||
// from CGRAM
|
// from CGRAM
|
||||||
@@ -40,106 +73,65 @@ void PPU::RenderScanline() {
|
|||||||
// the frame buffer
|
// the frame buffer
|
||||||
|
|
||||||
// Display the frame buffer on the screen
|
// Display the frame buffer on the screen
|
||||||
DisplayFrameBuffer(); // Sends the frame buffer to the display hardware
|
DisplayFrameBuffer();
|
||||||
// (e.g., SDL2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PPU::Update() {
|
void PPU::Notify(uint32_t address, uint8_t data) {
|
||||||
auto cycles_to_run = clock_.GetCycleCount();
|
// Handle communication in the PPU.
|
||||||
|
if (address >= 0x2100 && address <= 0x213F) {
|
||||||
UpdateInternalState(cycles_to_run);
|
// Handle register notification
|
||||||
|
switch (address) {
|
||||||
// Render however many scanlines we're supposed to.
|
case PPURegisters::INIDISP:
|
||||||
if (currentScanline < visibleScanlines) {
|
enable_forced_blanking_ = (data >> 7) & 0x01;
|
||||||
// Render the current scanline
|
break;
|
||||||
// This involves fetching tile data, applying palette colors, handling
|
case PPURegisters::BGMODE:
|
||||||
// sprite spriorities, etc.
|
// Update the PPU mode settings
|
||||||
RenderScanline();
|
UpdateModeSettings();
|
||||||
|
break;
|
||||||
// Increment the current scanline
|
|
||||||
currentScanline++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PPU::UpdateInternalState(int cycles) {
|
|
||||||
// Update the PPU's internal state based on the number of cycles
|
|
||||||
cycleCount += cycles;
|
|
||||||
|
|
||||||
// Check if it's time to move to the next scanline
|
|
||||||
if (cycleCount >= cyclesPerScanline) {
|
|
||||||
currentScanline++;
|
|
||||||
cycleCount -= cyclesPerScanline;
|
|
||||||
|
|
||||||
// If we've reached the end of the frame, reset to the first scanline
|
|
||||||
if (currentScanline >= totalScanlines) {
|
|
||||||
currentScanline = 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reads a byte from the specified PPU register
|
|
||||||
uint8_t PPU::ReadRegister(uint16_t address) {
|
|
||||||
switch (address) {
|
|
||||||
case 0x2102: // OAM Address Register (low byte)
|
|
||||||
return oam_address_ & 0xFF;
|
|
||||||
case 0x2103: // OAM Address Register (high byte)
|
|
||||||
return (oam_address_ >> 8) & 0xFF;
|
|
||||||
// ... handle other PPU registers
|
|
||||||
default:
|
|
||||||
// Invalid register address, return 0
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Writes a byte to the specified PPU register
|
|
||||||
void PPU::WriteRegister(uint16_t address, uint8_t value) {
|
|
||||||
switch (address) {
|
|
||||||
case 0x2102: // OAM Address Register (low byte)
|
|
||||||
oam_address_ = (oam_address_ & 0xFF00) | value;
|
|
||||||
break;
|
|
||||||
case 0x2103: // OAM Address Register (high byte)
|
|
||||||
oam_address_ = (oam_address_ & 0x00FF) | (value << 8);
|
|
||||||
break;
|
|
||||||
// ... handle other PPU registers
|
|
||||||
default:
|
|
||||||
// Invalid register address, do nothing
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PPU::UpdateModeSettings() {
|
void PPU::UpdateModeSettings() {
|
||||||
// Read the PPU mode settings from the PPU registers
|
// Read the PPU mode settings from the PPU registers
|
||||||
uint8_t modeRegister = ReadRegister(PPURegisters::INIDISP);
|
uint8_t modeRegister = memory_.ReadByte(PPURegisters::INIDISP);
|
||||||
|
|
||||||
// Extract the PPU mode and other relevant settings from the register value
|
// Mode is stored in the lower 3 bits
|
||||||
BackgroundMode mode = static_cast<BackgroundMode>(
|
auto mode = static_cast<BackgroundMode>(modeRegister & 0x07);
|
||||||
modeRegister & 0x07); // Mode is stored in the lower 3 bits
|
|
||||||
bool backgroundEnabled =
|
|
||||||
(modeRegister >> 7) & 0x01; // Background enabled flag is stored in bit 7
|
|
||||||
bool spritesEnabled =
|
|
||||||
(modeRegister >> 6) & 0x01; // Sprites enabled flag is stored in bit 6
|
|
||||||
|
|
||||||
// Update the internal mode settings based on the extracted values
|
// Update the tilemap, tile data, and palette settings
|
||||||
// modeSettings_.backgroundEnabled = backgroundEnabled;
|
|
||||||
// modeSettings_.spritesEnabled = spritesEnabled;
|
|
||||||
|
|
||||||
// Update the tilemap, tile data, and palette settings based on the selected
|
|
||||||
// mode
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case BackgroundMode::Mode0:
|
case BackgroundMode::Mode0:
|
||||||
// Mode 0: 4 background layers, all with 2bpp
|
// Mode 0: 4 layers, each 2bpp (4 colors)
|
||||||
// Update the tilemap, tile data, and palette settings accordingly
|
|
||||||
// ...
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case BackgroundMode::Mode1:
|
case BackgroundMode::Mode1:
|
||||||
// Mode 1: 3 background layers (2 with 4bpp, 1 with 2bpp)
|
// Mode 1: 2 layers, 4bpp (16 colors), 1 layer, 2bpp (4 colors)
|
||||||
// Update the tilemap, tile data, and palette settings accordingly
|
|
||||||
// ...
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Handle other modes and update the settings accordingly
|
case BackgroundMode::Mode2:
|
||||||
// ...
|
// Mode 2: 2 layers, 4bpp (16 colors), 1 layer for offset-per-tile
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BackgroundMode::Mode3:
|
||||||
|
// Mode 3: 1 layer, 8bpp (256 colors), 1 layer, 4bpp (16 colors)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BackgroundMode::Mode4:
|
||||||
|
// Mode 4: 1 layer, 8bpp (256 colors), 1 layer, 2bpp (4 colors)
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BackgroundMode::Mode5:
|
||||||
|
// Mode 5: 1 layer, 4bpp (16 colors), 1 layer, 2bpp (4 colors) hi-res
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BackgroundMode::Mode6:
|
||||||
|
// Mode 6: 1 layer, 4bpp (16 colors), 1 layer for offset-per-tile, hi-res
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BackgroundMode::Mode7:
|
||||||
|
// Mode 7: 1 layer, 8bpp (256 colors), rotation/scaling
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// Invalid mode setting, handle the error or set default settings
|
// Invalid mode setting, handle the error or set default settings
|
||||||
@@ -151,31 +143,108 @@ void PPU::UpdateModeSettings() {
|
|||||||
// Update tile data, tilemaps, sprites, and palette based on the mode settings
|
// Update tile data, tilemaps, sprites, and palette based on the mode settings
|
||||||
UpdateTileData();
|
UpdateTileData();
|
||||||
UpdatePaletteData();
|
UpdatePaletteData();
|
||||||
// ...
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Internal methods to handle PPU rendering and operations
|
||||||
|
void PPU::UpdateTileData() {
|
||||||
|
// Fetch tile data from VRAM and store it in the internal buffer
|
||||||
|
for (uint16_t address = 0; address < tile_data_size_; ++address) {
|
||||||
|
tile_data_[address] = memory_.ReadByte(vram_base_address_ + address);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the tilemap entries based on the fetched tile data
|
||||||
|
for (uint16_t entryIndex = 0; entryIndex < tilemap_.entries.size();
|
||||||
|
++entryIndex) {
|
||||||
|
uint16_t tilemapAddress =
|
||||||
|
tilemap_base_address_ + entryIndex * sizeof(TilemapEntry);
|
||||||
|
// Assume ReadWord reads a 16-bit value from VRAM
|
||||||
|
uint16_t tileData = memory_.ReadWord(tilemapAddress);
|
||||||
|
|
||||||
|
// Extract tilemap entry attributes from the tile data
|
||||||
|
TilemapEntry entry;
|
||||||
|
// Tile number is stored in the lower 10 bits
|
||||||
|
entry.tileNumber = tileData & 0x03FF;
|
||||||
|
|
||||||
|
// Palette is stored in bits 10-12
|
||||||
|
entry.palette = (tileData >> 10) & 0x07;
|
||||||
|
|
||||||
|
// Priority is stored in bit 13
|
||||||
|
entry.priority = (tileData >> 13) & 0x01;
|
||||||
|
|
||||||
|
// Horizontal flip is stored in bit 14
|
||||||
|
entry.hFlip = (tileData >> 14) & 0x01;
|
||||||
|
|
||||||
|
// Vertical flip is stored in bit 15
|
||||||
|
entry.vFlip = (tileData >> 15) & 0x01;
|
||||||
|
|
||||||
|
tilemap_.entries[entryIndex] = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the sprites based on the fetched tile data
|
||||||
|
for (uint16_t spriteIndex = 0; spriteIndex < sprites_.size(); ++spriteIndex) {
|
||||||
|
uint16_t spriteAddress =
|
||||||
|
oam_address_ + spriteIndex * sizeof(SpriteAttributes);
|
||||||
|
// Assume ReadWord reads a 16-bit value from VRAM
|
||||||
|
uint16_t spriteData = memory_.ReadWord(spriteAddress);
|
||||||
|
|
||||||
|
// Extract sprite attributes from the sprite data
|
||||||
|
SpriteAttributes sprite;
|
||||||
|
|
||||||
|
sprite.x = memory_.ReadByte(spriteAddress);
|
||||||
|
sprite.y = memory_.ReadByte(spriteAddress + 1);
|
||||||
|
|
||||||
|
// Tile number is stored in the lower 9
|
||||||
|
sprite.tile = spriteData & 0x01FF;
|
||||||
|
|
||||||
|
// bits Palette is stored in bits 9-11
|
||||||
|
sprite.palette = (spriteData >> 9) & 0x07;
|
||||||
|
|
||||||
|
// Priority is stored in bits 12-13
|
||||||
|
sprite.priority = (spriteData >> 12) & 0x03;
|
||||||
|
|
||||||
|
// Horizontal flip is stored in bit 14
|
||||||
|
sprite.hFlip = (spriteData >> 14) & 0x01;
|
||||||
|
|
||||||
|
// Vertical flip is stored in bit 15
|
||||||
|
sprite.vFlip = (spriteData >> 15) & 0x01;
|
||||||
|
|
||||||
|
sprites_[spriteIndex] = sprite;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PPU::UpdateTileMapData() {}
|
||||||
|
|
||||||
void PPU::RenderBackground(int layer) {
|
void PPU::RenderBackground(int layer) {
|
||||||
|
auto bg1_tilemap_info = BGSC(0);
|
||||||
|
auto bg1_chr_data = BGNBA(0);
|
||||||
|
auto bg2_tilemap_info = BGSC(0);
|
||||||
|
auto bg2_chr_data = BGNBA(0);
|
||||||
|
auto bg3_tilemap_info = BGSC(0);
|
||||||
|
auto bg3_chr_data = BGNBA(0);
|
||||||
|
auto bg4_tilemap_info = BGSC(0);
|
||||||
|
auto bg4_chr_data = BGNBA(0);
|
||||||
|
|
||||||
switch (layer) {
|
switch (layer) {
|
||||||
case 1:
|
case 1:
|
||||||
// // Render the first background layer
|
// Render the first background layer
|
||||||
// auto bg1_tilemap_info =
|
bg1_tilemap_info = BGSC(memory_.ReadByte(BG1SC));
|
||||||
// PPURegisters::BGSC(ReadVRAM(PPURegisters::BG1SC)); auto bg1_chr_data =
|
bg1_chr_data = BGNBA(memory_.ReadByte(BG12NBA));
|
||||||
// PPURegisters::BGNBA(ReadVRAM(PPURegisters::BG12NBA)); break;
|
break;
|
||||||
// case 2:
|
case 2:
|
||||||
// // Render the second background layer
|
// Render the second background layer
|
||||||
// auto bg2_tilemap_info =
|
bg2_tilemap_info = BGSC(memory_.ReadByte(BG2SC));
|
||||||
// PPURegisters::BGSC(ReadVRAM(PPURegisters::BG2SC)); auto bg2_chr_data =
|
bg2_chr_data = BGNBA(memory_.ReadByte(BG12NBA));
|
||||||
// PPURegisters::BGNBA(ReadVRAM(PPURegisters::BG12NBA)); break;
|
break;
|
||||||
// case 3:
|
case 3:
|
||||||
// // Render the third background layer
|
// Render the third background layer
|
||||||
// auto bg3_tilemap_info =
|
bg3_tilemap_info = BGSC(memory_.ReadByte(BG3SC));
|
||||||
// PPURegisters::BGSC(ReadVRAM(PPURegisters::BG3SC)); auto bg3_chr_data =
|
bg3_chr_data = BGNBA(memory_.ReadByte(BG34NBA));
|
||||||
// PPURegisters::BGNBA(ReadVRAM(PPURegisters::BG34NBA)); break;
|
break;
|
||||||
// case 4:
|
case 4:
|
||||||
// // Render the fourth background layer
|
// Render the fourth background layer
|
||||||
// auto bg4_tilemap_info =
|
bg4_tilemap_info = BGSC(memory_.ReadByte(BG4SC));
|
||||||
// PPURegisters::BGSC(ReadVRAM(PPURegisters::BG4SC)); auto bg4_chr_data =
|
bg4_chr_data = BGNBA(memory_.ReadByte(BG34NBA));
|
||||||
// PPURegisters::BGNBA(ReadVRAM(PPURegisters::BG34NBA)); break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Invalid layer, do nothing
|
// Invalid layer, do nothing
|
||||||
break;
|
break;
|
||||||
@@ -186,83 +255,13 @@ void PPU::RenderSprites() {
|
|||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t PPU::GetPaletteColor(uint8_t colorIndex) {
|
void PPU::UpdatePaletteData() {}
|
||||||
return memory_.ReadWordLong(colorIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t PPU::ReadVRAM(uint16_t address) {
|
void PPU::ApplyEffects() {}
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
void PPU::WriteVRAM(uint16_t address, uint8_t value) {
|
void PPU::ComposeLayers() {}
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t PPU::ReadOAM(uint16_t address) { return memory_.ReadByte(address); }
|
void PPU::DisplayFrameBuffer() {}
|
||||||
|
|
||||||
void PPU::WriteOAM(uint16_t address, uint8_t value) {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t PPU::ReadCGRAM(uint16_t address) { return memory_.ReadByte(address); }
|
|
||||||
|
|
||||||
void PPU::WriteCGRAM(uint16_t address, uint8_t value) {
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal methods to handle PPU rendering and operations
|
|
||||||
void PPU::UpdateTileData() {
|
|
||||||
// Fetch tile data from VRAM and store it in the internal buffer
|
|
||||||
for (uint16_t address = 0; address < tileDataSize_; ++address) {
|
|
||||||
tileData_[address] = memory_.ReadByte(vramBaseAddress_ + address);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the tilemap entries based on the fetched tile data
|
|
||||||
for (uint16_t entryIndex = 0; entryIndex < tilemap_.entries.size();
|
|
||||||
++entryIndex) {
|
|
||||||
uint16_t tilemapAddress =
|
|
||||||
tilemapBaseAddress_ + entryIndex * sizeof(TilemapEntry);
|
|
||||||
uint16_t tileData = memory_.ReadWord(
|
|
||||||
tilemapAddress); // Assume ReadWord reads a 16-bit value from VRAM
|
|
||||||
|
|
||||||
// Extract tilemap entry attributes from the tile data
|
|
||||||
TilemapEntry entry;
|
|
||||||
entry.tileNumber =
|
|
||||||
tileData & 0x03FF; // Tile number is stored in the lower 10 bits
|
|
||||||
entry.palette = (tileData >> 10) & 0x07; // Palette is stored in bits 10-12
|
|
||||||
entry.priority = (tileData >> 13) & 0x01; // Priority is stored in bit 13
|
|
||||||
entry.hFlip =
|
|
||||||
(tileData >> 14) & 0x01; // Horizontal flip is stored in bit 14
|
|
||||||
entry.vFlip = (tileData >> 15) & 0x01; // Vertical flip is stored in bit 15
|
|
||||||
|
|
||||||
tilemap_.entries[entryIndex] = entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the sprites based on the fetched tile data
|
|
||||||
for (uint16_t spriteIndex = 0; spriteIndex < sprites_.size(); ++spriteIndex) {
|
|
||||||
uint16_t spriteAddress =
|
|
||||||
oam_address_ + spriteIndex * sizeof(SpriteAttributes);
|
|
||||||
uint16_t spriteData = memory_.ReadWord(
|
|
||||||
spriteAddress); // Assume ReadWord reads a 16-bit value from VRAM
|
|
||||||
|
|
||||||
// Extract sprite attributes from the sprite data
|
|
||||||
SpriteAttributes sprite;
|
|
||||||
sprite.x = memory_.ReadByte(spriteAddress);
|
|
||||||
sprite.y = memory_.ReadByte(spriteAddress + 1);
|
|
||||||
sprite.tile =
|
|
||||||
spriteData & 0x01FF; // Tile number is stored in the lower 9 bits
|
|
||||||
sprite.palette =
|
|
||||||
(spriteData >> 9) & 0x07; // Palette is stored in bits 9-11
|
|
||||||
sprite.priority =
|
|
||||||
(spriteData >> 12) & 0x03; // Priority is stored in bits 12-13
|
|
||||||
sprite.hFlip =
|
|
||||||
(spriteData >> 14) & 0x01; // Horizontal flip is stored in bit 14
|
|
||||||
sprite.vFlip =
|
|
||||||
(spriteData >> 15) & 0x01; // Vertical flip is stored in bit 15
|
|
||||||
|
|
||||||
sprites_[spriteIndex] = sprite;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace emu
|
} // namespace emu
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "app/emu/clock.h"
|
#include "app/emu/clock.h"
|
||||||
#include "app/emu/mem.h"
|
#include "app/emu/memory/memory.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
@@ -640,44 +640,37 @@ class PPU : public Observer {
|
|||||||
// Initializes the PPU with the necessary resources and dependencies
|
// Initializes the PPU with the necessary resources and dependencies
|
||||||
PPU(Memory& memory, Clock& clock) : memory_(memory), clock_(clock) {}
|
PPU(Memory& memory, Clock& clock) : memory_(memory), clock_(clock) {}
|
||||||
|
|
||||||
|
// Initialize the frame buffer
|
||||||
void Init() {
|
void Init() {
|
||||||
// Initialize the frame buffer with a size that corresponds to the
|
|
||||||
// screen resolution
|
|
||||||
clock_.SetFrequency(kPpuClockSpeed);
|
clock_.SetFrequency(kPpuClockSpeed);
|
||||||
frame_buffer_.resize(256 * 240, 0);
|
frame_buffer_.resize(256 * 240, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateClock(double delta_time) { clock_.UpdateClock(delta_time); }
|
|
||||||
|
|
||||||
// Resets the PPU to its initial state
|
// Resets the PPU to its initial state
|
||||||
void Reset() { std::fill(frame_buffer_.begin(), frame_buffer_.end(), 0); }
|
void Reset() { std::fill(frame_buffer_.begin(), frame_buffer_.end(), 0); }
|
||||||
|
|
||||||
// Runs the PPU for one frame.
|
// Runs the PPU for one frame.
|
||||||
void Update();
|
void Update();
|
||||||
|
void UpdateClock(double delta_time) { clock_.UpdateClock(delta_time); }
|
||||||
void UpdateInternalState(int cycles);
|
void UpdateInternalState(int cycles);
|
||||||
|
|
||||||
void Notify(uint32_t address, uint8_t data) override {
|
|
||||||
// Handle communication in the PPU.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reads a byte from the specified PPU register
|
|
||||||
uint8_t ReadRegister(uint16_t address);
|
|
||||||
|
|
||||||
// Writes a byte to the specified PPU register
|
|
||||||
void WriteRegister(uint16_t address, uint8_t value);
|
|
||||||
|
|
||||||
// Renders a scanline of the screen
|
// Renders a scanline of the screen
|
||||||
void RenderScanline();
|
void RenderScanline();
|
||||||
|
|
||||||
|
void Notify(uint32_t address, uint8_t data) override;
|
||||||
|
|
||||||
// Returns the pixel data for the current frame
|
// Returns the pixel data for the current frame
|
||||||
const std::vector<uint8_t>& GetFrameBuffer() const { return frame_buffer_; }
|
const std::vector<uint8_t>& GetFrameBuffer() const { return frame_buffer_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// Updates internal state based on PPU register settings
|
||||||
|
void UpdateModeSettings();
|
||||||
|
|
||||||
// Internal methods to handle PPU rendering and operations
|
// Internal methods to handle PPU rendering and operations
|
||||||
void UpdateTileData();
|
void UpdateTileData();
|
||||||
|
|
||||||
// Updates internal state based on PPU register settings
|
// Fetches the tile map data from memory and stores it in an internal buffer
|
||||||
void UpdateModeSettings();
|
void UpdateTileMapData();
|
||||||
|
|
||||||
// Renders a background layer
|
// Renders a background layer
|
||||||
void RenderBackground(int layer);
|
void RenderBackground(int layer);
|
||||||
@@ -685,73 +678,41 @@ class PPU : public Observer {
|
|||||||
// Renders sprites (also known as objects)
|
// Renders sprites (also known as objects)
|
||||||
void RenderSprites();
|
void RenderSprites();
|
||||||
|
|
||||||
void UpdateTileMapData() {
|
// Fetches the palette data from CGRAM and stores it in an internal buffer
|
||||||
// Fetches the tile map data from memory and stores it in an internal
|
void UpdatePaletteData();
|
||||||
// buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
void UpdatePaletteData() {
|
// Applies effects to the layers based on the current mode and register
|
||||||
// Fetches the palette data from CGRAM and stores it in an internal
|
void ApplyEffects();
|
||||||
// buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
void ApplyEffects() {
|
// Combines the layers into a single image and stores it in the frame buffer
|
||||||
// Applies effects to the layers based on the current mode and register
|
void ComposeLayers();
|
||||||
// settings
|
|
||||||
}
|
|
||||||
|
|
||||||
void ComposeLayers() {
|
// Sends the frame buffer to the display hardware (e.g., SDL2)
|
||||||
// Combines the layers into a single image and stores it in the frame
|
void DisplayFrameBuffer();
|
||||||
// buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
void DisplayFrameBuffer() {
|
|
||||||
// Sends the frame buffer to the display hardware (e.g., SDL2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieves a pixel color from the palette
|
|
||||||
uint32_t GetPaletteColor(uint8_t colorIndex);
|
|
||||||
|
|
||||||
// Handles VRAM (Video RAM) reads and writes
|
|
||||||
uint8_t ReadVRAM(uint16_t address);
|
|
||||||
void WriteVRAM(uint16_t address, uint8_t value);
|
|
||||||
|
|
||||||
// Handles OAM (Object Attribute Memory) reads and writes
|
|
||||||
uint8_t ReadOAM(uint16_t address);
|
|
||||||
void WriteOAM(uint16_t address, uint8_t value);
|
|
||||||
|
|
||||||
// Handle CGRAM (Color Palette RAM) reads and writes
|
|
||||||
uint8_t ReadCGRAM(uint16_t address);
|
|
||||||
void WriteCGRAM(uint16_t address, uint8_t value);
|
|
||||||
|
|
||||||
// ===========================================================
|
// ===========================================================
|
||||||
// Member variables to store internal PPU state and resources
|
// Member variables to store internal PPU state and resources
|
||||||
Memory& memory_;
|
Memory& memory_;
|
||||||
Clock& clock_;
|
Clock& clock_;
|
||||||
std::vector<uint8_t> frame_buffer_;
|
|
||||||
|
|
||||||
Tilemap tilemap_;
|
Tilemap tilemap_;
|
||||||
BackgroundMode bg_mode_;
|
BackgroundMode bg_mode_;
|
||||||
std::array<BackgroundLayer, 4> bg_layers_;
|
std::array<BackgroundLayer, 4> bg_layers_;
|
||||||
std::vector<SpriteAttributes> sprites_;
|
std::vector<SpriteAttributes> sprites_;
|
||||||
std::vector<uint8_t> tileData_;
|
std::vector<uint8_t> tile_data_;
|
||||||
|
std::vector<uint8_t> frame_buffer_;
|
||||||
|
|
||||||
uint16_t oam_address_;
|
uint16_t oam_address_;
|
||||||
uint16_t tileDataSize_;
|
uint16_t tile_data_size_;
|
||||||
uint16_t vramBaseAddress_;
|
uint16_t vram_base_address_;
|
||||||
uint16_t tilemapBaseAddress_;
|
uint16_t tilemap_base_address_;
|
||||||
|
|
||||||
// The VRAM memory area holds tiles and tile maps.
|
uint16_t screen_brightness_ = 0x00;
|
||||||
std::array<uint8_t, 64 * 1024> vram_;
|
|
||||||
|
|
||||||
// The OAM memory area holds sprite properties.
|
bool enable_forced_blanking_ = false;
|
||||||
std::array<uint8_t, 544> oam_;
|
|
||||||
|
|
||||||
// The CGRAM memory area holds the color palette data.
|
int cycle_count_ = 0;
|
||||||
std::array<uint8_t, 512> cgram_;
|
int current_scanline_ = 0;
|
||||||
|
|
||||||
int cycleCount = 0;
|
|
||||||
int currentScanline = 0;
|
|
||||||
const int cyclesPerScanline = 341; // SNES PPU has 341 cycles per scanline
|
const int cyclesPerScanline = 341; // SNES PPU has 341 cycles per scanline
|
||||||
const int totalScanlines = 262; // SNES PPU has 262 scanlines per frame
|
const int totalScanlines = 262; // SNES PPU has 262 scanlines per frame
|
||||||
const int visibleScanlines = 224; // SNES PPU renders 224 visible scanlines
|
const int visibleScanlines = 224; // SNES PPU renders 224 visible scanlines
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include "app/emu/clock.h"
|
#include "app/emu/clock.h"
|
||||||
#include "app/emu/mem.h"
|
#include "app/emu/memory/memory.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
@@ -24,6 +24,8 @@ class MockMemory : public Memory {
|
|||||||
MOCK_CONST_METHOD1(ReadByte, uint8_t(uint16_t address));
|
MOCK_CONST_METHOD1(ReadByte, uint8_t(uint16_t address));
|
||||||
MOCK_CONST_METHOD1(ReadWord, uint16_t(uint16_t address));
|
MOCK_CONST_METHOD1(ReadWord, uint16_t(uint16_t address));
|
||||||
MOCK_CONST_METHOD1(ReadWordLong, uint32_t(uint16_t address));
|
MOCK_CONST_METHOD1(ReadWordLong, uint32_t(uint16_t address));
|
||||||
|
MOCK_METHOD(std::vector<uint8_t>, ReadByteVector,
|
||||||
|
(uint16_t address, uint16_t length), (const, override));
|
||||||
|
|
||||||
MOCK_METHOD2(WriteByte, void(uint32_t address, uint8_t value));
|
MOCK_METHOD2(WriteByte, void(uint32_t address, uint8_t value));
|
||||||
MOCK_METHOD2(WriteWord, void(uint32_t address, uint16_t value));
|
MOCK_METHOD2(WriteWord, void(uint32_t address, uint16_t value));
|
||||||
@@ -84,6 +86,14 @@ class MockMemory : public Memory {
|
|||||||
(static_cast<uint32_t>(memory_.at(address + 1)) << 8) |
|
(static_cast<uint32_t>(memory_.at(address + 1)) << 8) |
|
||||||
(static_cast<uint32_t>(memory_.at(address + 2)) << 16);
|
(static_cast<uint32_t>(memory_.at(address + 2)) << 16);
|
||||||
});
|
});
|
||||||
|
ON_CALL(*this, ReadByteVector(::testing::_, ::testing::_))
|
||||||
|
.WillByDefault([this](uint16_t address, uint16_t length) {
|
||||||
|
std::vector<uint8_t> data;
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
data.push_back(memory_.at(address + i));
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
});
|
||||||
ON_CALL(*this, WriteByte(::testing::_, ::testing::_))
|
ON_CALL(*this, WriteByte(::testing::_, ::testing::_))
|
||||||
.WillByDefault([this](uint32_t address, uint8_t value) {
|
.WillByDefault([this](uint32_t address, uint8_t value) {
|
||||||
memory_[address] = value;
|
memory_[address] = value;
|
||||||
|
|||||||
Reference in New Issue
Block a user