mirror of https://github.com/inolen/redream.git
split memory system up to support mapping multiple address spaces to the same backing shared memory
This commit is contained in:
parent
18232d86d2
commit
7d639cb74a
|
@ -162,6 +162,7 @@ set(REDREAM_SOURCES
|
|||
src/hw/sh4/sh4.cc
|
||||
src/hw/sh4/sh4_code_cache.cc
|
||||
src/hw/debugger.cc
|
||||
src/hw/dreamcast.cc
|
||||
src/hw/machine.cc
|
||||
src/hw/memory.cc
|
||||
src/hw/scheduler.cc
|
||||
|
|
|
@ -1,45 +1,31 @@
|
|||
#include <algorithm>
|
||||
#include <gflags/gflags.h>
|
||||
#include "emu/emulator.h"
|
||||
#include "hw/aica/aica.h"
|
||||
#include "hw/arm7/arm7.h"
|
||||
#include "hw/gdrom/gdrom.h"
|
||||
#include "hw/holly/holly.h"
|
||||
#include "hw/holly/pvr2.h"
|
||||
#include "hw/holly/tile_accelerator.h"
|
||||
#include "hw/maple/maple.h"
|
||||
#include "hw/sh4/sh4.h"
|
||||
#include "hw/dreamcast.h"
|
||||
#include "hw/memory.h"
|
||||
#include "hw/scheduler.h"
|
||||
#include "ui/window.h"
|
||||
|
||||
using namespace re;
|
||||
using namespace re::emu;
|
||||
using namespace re::hw;
|
||||
using namespace re::hw::aica;
|
||||
using namespace re::hw::arm7;
|
||||
using namespace re::hw::gdrom;
|
||||
using namespace re::hw::holly;
|
||||
using namespace re::hw::maple;
|
||||
using namespace re::hw::sh4;
|
||||
using namespace re::renderer;
|
||||
using namespace re::ui;
|
||||
|
||||
DEFINE_string(bios, "dc_boot.bin", "Path to BIOS");
|
||||
DEFINE_string(flash, "dc_flash.bin", "Path to flash ROM");
|
||||
|
||||
Emulator::Emulator(ui::Window &window) : window_(window) {
|
||||
Emulator::Emulator(Window &window)
|
||||
: window_(window), dc_(window_.render_backend()) {
|
||||
window_.AddListener(this);
|
||||
}
|
||||
|
||||
Emulator::~Emulator() {
|
||||
window_.RemoveListener(this);
|
||||
|
||||
DestroyDreamcast();
|
||||
}
|
||||
Emulator::~Emulator() { window_.RemoveListener(this); }
|
||||
|
||||
void Emulator::Run(const char *path) {
|
||||
if (!CreateDreamcast()) {
|
||||
if (!dc_.Init()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -93,44 +79,10 @@ void Emulator::Run(const char *path) {
|
|||
}
|
||||
}
|
||||
|
||||
bool Emulator::CreateDreamcast() {
|
||||
dc_.sh4 = new SH4(dc_);
|
||||
dc_.arm7 = new ARM7(dc_);
|
||||
dc_.aica = new AICA(dc_);
|
||||
dc_.holly = new Holly(dc_);
|
||||
dc_.gdrom = new GDROM(dc_);
|
||||
dc_.maple = new Maple(dc_);
|
||||
dc_.pvr = new PVR2(dc_);
|
||||
dc_.ta = new TileAccelerator(dc_, window_.render_backend());
|
||||
|
||||
if (!dc_.Init()) {
|
||||
DestroyDreamcast();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Emulator::DestroyDreamcast() {
|
||||
delete dc_.sh4;
|
||||
dc_.sh4 = nullptr;
|
||||
delete dc_.arm7;
|
||||
dc_.arm7 = nullptr;
|
||||
delete dc_.aica;
|
||||
dc_.aica = nullptr;
|
||||
delete dc_.holly;
|
||||
dc_.holly = nullptr;
|
||||
delete dc_.gdrom;
|
||||
dc_.gdrom = nullptr;
|
||||
delete dc_.maple;
|
||||
dc_.maple = nullptr;
|
||||
delete dc_.pvr;
|
||||
dc_.pvr = nullptr;
|
||||
delete dc_.ta;
|
||||
dc_.ta = nullptr;
|
||||
}
|
||||
|
||||
bool Emulator::LoadBios(const char *path) {
|
||||
static const int BIOS_BEGIN = 0x00000000;
|
||||
static const int BIOS_SIZE = 0x00200000;
|
||||
|
||||
FILE *fp = fopen(path, "rb");
|
||||
if (!fp) {
|
||||
LOG_WARNING("Failed to open bios at \"%s\"", path);
|
||||
|
@ -147,7 +99,7 @@ bool Emulator::LoadBios(const char *path) {
|
|||
return false;
|
||||
}
|
||||
|
||||
uint8_t *bios = dc_.memory->TranslateVirtual(BIOS_BEGIN);
|
||||
uint8_t *bios = dc_.sh4()->space().Translate(BIOS_BEGIN);
|
||||
int n = static_cast<int>(fread(bios, sizeof(uint8_t), size, fp));
|
||||
fclose(fp);
|
||||
|
||||
|
@ -160,6 +112,9 @@ bool Emulator::LoadBios(const char *path) {
|
|||
}
|
||||
|
||||
bool Emulator::LoadFlash(const char *path) {
|
||||
static const int FLASH_BEGIN = 0x00200000;
|
||||
static const int FLASH_SIZE = 0x00020000;
|
||||
|
||||
FILE *fp = fopen(path, "rb");
|
||||
if (!fp) {
|
||||
LOG_WARNING("Failed to open flash at \"%s\"", path);
|
||||
|
@ -176,7 +131,7 @@ bool Emulator::LoadFlash(const char *path) {
|
|||
return false;
|
||||
}
|
||||
|
||||
uint8_t *flash = dc_.memory->TranslateVirtual(FLASH_BEGIN);
|
||||
uint8_t *flash = dc_.sh4()->space().Translate(FLASH_BEGIN);
|
||||
int n = static_cast<int>(fread(flash, sizeof(uint8_t), size, fp));
|
||||
fclose(fp);
|
||||
|
||||
|
@ -199,7 +154,7 @@ bool Emulator::LaunchBIN(const char *path) {
|
|||
fseek(fp, 0, SEEK_SET);
|
||||
|
||||
// load to 0x0c010000 (area 3) which is where 1ST_READ.BIN is loaded to
|
||||
uint8_t *data = dc_.memory->TranslateVirtual(0x0c010000);
|
||||
uint8_t *data = dc_.sh4()->space().Translate(0x0c010000);
|
||||
int n = static_cast<int>(fread(data, sizeof(uint8_t), size, fp));
|
||||
fclose(fp);
|
||||
|
||||
|
@ -208,8 +163,8 @@ bool Emulator::LaunchBIN(const char *path) {
|
|||
return false;
|
||||
}
|
||||
|
||||
dc_.gdrom->SetDisc(nullptr);
|
||||
dc_.sh4->SetPC(0x0c010000);
|
||||
dc_.gdrom()->SetDisc(nullptr);
|
||||
dc_.sh4()->SetPC(0x0c010000);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -221,8 +176,8 @@ bool Emulator::LaunchGDI(const char *path) {
|
|||
return false;
|
||||
}
|
||||
|
||||
dc_.gdrom->SetDisc(std::move(gdi));
|
||||
dc_.sh4->SetPC(0xa0000000);
|
||||
dc_.gdrom()->SetDisc(std::move(gdi));
|
||||
dc_.sh4()->SetPC(0xa0000000);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -20,9 +20,6 @@ class Emulator : public ui::WindowListener {
|
|||
void Run(const char *path);
|
||||
|
||||
private:
|
||||
bool CreateDreamcast();
|
||||
void DestroyDreamcast();
|
||||
|
||||
bool LoadBios(const char *path);
|
||||
bool LoadFlash(const char *path);
|
||||
bool LaunchBIN(const char *path);
|
||||
|
|
|
@ -149,7 +149,7 @@ void Tracer::OnPaint(bool show_main_menu) {
|
|||
}
|
||||
|
||||
// render the context
|
||||
rb_.BeginSurfaces(rctx_.projection, rctx_.verts.data(), rctx_.verts.size());
|
||||
rb_->BeginSurfaces(rctx_.projection, rctx_.verts.data(), rctx_.verts.size());
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
int idx = rctx_.sorted_surfs[i];
|
||||
|
@ -159,10 +159,10 @@ void Tracer::OnPaint(bool show_main_menu) {
|
|||
continue;
|
||||
}
|
||||
|
||||
rb_.DrawSurface(rctx_.surfs[idx]);
|
||||
rb_->DrawSurface(rctx_.surfs[idx]);
|
||||
}
|
||||
|
||||
rb_.EndSurfaces();
|
||||
rb_->EndSurfaces();
|
||||
}
|
||||
|
||||
void Tracer::OnKeyDown(Keycode code, int16_t value) {
|
||||
|
@ -258,6 +258,7 @@ void Tracer::RenderTextureMenu() {
|
|||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::Text("addr; 0x%08x", tex.tcw.texture_addr << 3);
|
||||
ImGui::Text("format: %s", s_pixel_format_names[tex.format]);
|
||||
ImGui::Text("filter: %s", s_filter_mode_names[tex.filter]);
|
||||
ImGui::Text("wrap_u: %s", s_wrap_mode_names[tex.wrap_u]);
|
||||
|
|
|
@ -79,7 +79,7 @@ class Tracer : public ui::WindowListener {
|
|||
void ResetParam();
|
||||
|
||||
ui::Window &window_;
|
||||
renderer::Backend &rb_;
|
||||
renderer::Backend *rb_;
|
||||
TraceTextureCache texcache_;
|
||||
hw::holly::TileRenderer tile_renderer_;
|
||||
hw::holly::TileContext tctx_;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "core/memory.h"
|
||||
#include "hw/aica/aica.h"
|
||||
#include "hw/arm7/arm7.h"
|
||||
#include "hw/sh4/sh4.h"
|
||||
#include "hw/dreamcast.h"
|
||||
#include "hw/memory.h"
|
||||
|
||||
|
@ -9,10 +10,6 @@ using namespace re::hw;
|
|||
using namespace re::hw::aica;
|
||||
using namespace re::hw::holly;
|
||||
|
||||
enum {
|
||||
AICA_CLOCK_FREQ = 22579200,
|
||||
};
|
||||
|
||||
namespace re {
|
||||
namespace hw {
|
||||
namespace aica {
|
||||
|
@ -23,10 +20,33 @@ uint32_t AICA::ReadWave(uint32_t addr);
|
|||
}
|
||||
}
|
||||
|
||||
AICA::AICA(Dreamcast &dc)
|
||||
: Device(dc),
|
||||
// clang-format off
|
||||
AM_BEGIN(AICA, reg_map)
|
||||
AM_RANGE(0x00000000, 0x00010fff) AM_HANDLE(&AICA::ReadRegister<uint8_t>,
|
||||
&AICA::ReadRegister<uint16_t>,
|
||||
&AICA::ReadRegister<uint32_t>,
|
||||
nullptr,
|
||||
&AICA::WriteRegister<uint8_t>,
|
||||
&AICA::WriteRegister<uint16_t>,
|
||||
&AICA::WriteRegister<uint32_t>,
|
||||
nullptr)
|
||||
AM_END()
|
||||
|
||||
AM_BEGIN(AICA, data_map)
|
||||
AM_RANGE(0x00000000, 0x001fffff) AM_HANDLE(&AICA::ReadWave<uint8_t>,
|
||||
&AICA::ReadWave<uint16_t>,
|
||||
&AICA::ReadWave<uint32_t>,
|
||||
nullptr,
|
||||
&AICA::WriteWave<uint8_t>,
|
||||
&AICA::WriteWave<uint16_t>,
|
||||
&AICA::WriteWave<uint32_t>,
|
||||
nullptr)
|
||||
AM_END()
|
||||
// clang-format on
|
||||
|
||||
AICA::AICA(Dreamcast &dc)
|
||||
: Device(dc, "aica"),
|
||||
ExecuteInterface(this),
|
||||
MemoryInterface(this),
|
||||
dc_(dc),
|
||||
sh4_(nullptr),
|
||||
arm7_(nullptr),
|
||||
|
@ -34,10 +54,10 @@ AICA::AICA(Dreamcast &dc)
|
|||
wave_ram_(nullptr) {}
|
||||
|
||||
bool AICA::Init() {
|
||||
sh4_ = dc_.sh4;
|
||||
arm7_ = dc_.arm7;
|
||||
aica_regs_ = dc_.memory->TranslateVirtual(AICA_REG_BEGIN);
|
||||
wave_ram_ = dc_.memory->TranslateVirtual(WAVE_RAM_BEGIN);
|
||||
sh4_ = dc_.sh4();
|
||||
arm7_ = dc_.arm7();
|
||||
aica_regs_ = sh4_->space().Translate(0x00700000);
|
||||
wave_ram_ = sh4_->space().Translate(0x00800000);
|
||||
common_data_ = reinterpret_cast<CommonData *>(aica_regs_ + 0x2800);
|
||||
|
||||
// start suspended
|
||||
|
@ -53,29 +73,6 @@ void AICA::Run(const std::chrono::nanoseconds &delta) {
|
|||
// }
|
||||
}
|
||||
|
||||
void AICA::MapPhysicalMemory(Memory &memory, MemoryMap &memmap) {
|
||||
RegionHandle aica_reg_handle = memory.AllocRegion(
|
||||
AICA_REG_BEGIN, AICA_REG_SIZE,
|
||||
make_delegate(&AICA::ReadRegister<uint8_t>, this),
|
||||
make_delegate(&AICA::ReadRegister<uint16_t>, this),
|
||||
make_delegate(&AICA::ReadRegister<uint32_t>, this), nullptr,
|
||||
make_delegate(&AICA::WriteRegister<uint8_t>, this),
|
||||
make_delegate(&AICA::WriteRegister<uint16_t>, this),
|
||||
make_delegate(&AICA::WriteRegister<uint32_t>, this), nullptr);
|
||||
|
||||
RegionHandle wave_ram_handle = memory.AllocRegion(
|
||||
WAVE_RAM_BEGIN, WAVE_RAM_SIZE,
|
||||
make_delegate(&AICA::ReadWave<uint8_t>, this),
|
||||
make_delegate(&AICA::ReadWave<uint16_t>, this),
|
||||
make_delegate(&AICA::ReadWave<uint32_t>, this), nullptr,
|
||||
make_delegate(&AICA::WriteWave<uint8_t>, this),
|
||||
make_delegate(&AICA::WriteWave<uint16_t>, this),
|
||||
make_delegate(&AICA::WriteWave<uint32_t>, this), nullptr);
|
||||
|
||||
memmap.Mount(aica_reg_handle, AICA_REG_SIZE, AICA_REG_BEGIN);
|
||||
memmap.Mount(wave_ram_handle, WAVE_RAM_SIZE, WAVE_RAM_BEGIN);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T AICA::ReadRegister(uint32_t addr) {
|
||||
return re::load<T>(&aica_regs_[addr]);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <stdint.h>
|
||||
#include "hw/aica/aica_types.h"
|
||||
#include "hw/machine.h"
|
||||
#include "hw/memory.h"
|
||||
|
||||
namespace re {
|
||||
namespace hw {
|
||||
|
@ -19,8 +20,11 @@ class SH4;
|
|||
|
||||
namespace aica {
|
||||
|
||||
class AICA : public Device, public ExecuteInterface, public MemoryInterface {
|
||||
class AICA : public Device, public ExecuteInterface {
|
||||
public:
|
||||
AM_DECLARE(reg_map);
|
||||
AM_DECLARE(data_map);
|
||||
|
||||
AICA(Dreamcast &dc);
|
||||
|
||||
bool Init() final;
|
||||
|
@ -29,9 +33,6 @@ class AICA : public Device, public ExecuteInterface, public MemoryInterface {
|
|||
// ExecuteInterface
|
||||
void Run(const std::chrono::nanoseconds &delta) final;
|
||||
|
||||
// MemoryInterface
|
||||
void MapPhysicalMemory(Memory &memory, MemoryMap &memmap) final;
|
||||
|
||||
template <typename T>
|
||||
T ReadRegister(uint32_t addr);
|
||||
template <typename T>
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#define AICA_TYPES_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "hw/regions.h"
|
||||
|
||||
namespace re {
|
||||
namespace hw {
|
||||
|
|
|
@ -12,14 +12,13 @@ enum {
|
|||
ARM7_CLOCK_FREQ = 22579200,
|
||||
};
|
||||
|
||||
ARM7::ARM7(Dreamcast &dc) : Device(dc), ExecuteInterface(this), dc_(dc) {}
|
||||
|
||||
bool ARM7::Init() {
|
||||
memory_ = dc_.memory;
|
||||
|
||||
return true;
|
||||
ARM7::ARM7(Dreamcast &dc)
|
||||
: Device(dc, "arm7"), ExecuteInterface(this), dc_(dc) {
|
||||
((void)dc_);
|
||||
}
|
||||
|
||||
bool ARM7::Init() { return true; }
|
||||
|
||||
void ARM7::Run(const std::chrono::nanoseconds &delta) {
|
||||
// ctx_.num_cycles = NANO_TO_CYCLES(delta, ARM7_CLOCK_FREQ);
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ class ARM7 : public Device, public ExecuteInterface {
|
|||
void Run(const std::chrono::nanoseconds &delta) final;
|
||||
|
||||
Dreamcast &dc_;
|
||||
Memory *memory_;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ Debugger::~Debugger() { gdb_server_destroy(sv_); }
|
|||
|
||||
bool Debugger::Init() {
|
||||
// use the first device found with a debug interface
|
||||
for (auto device : machine_.devices) {
|
||||
for (auto device : machine_.devices()) {
|
||||
debug_ = device->debug();
|
||||
|
||||
if (debug_) {
|
||||
|
|
|
@ -20,23 +20,23 @@ using namespace re::hw::sh4;
|
|||
using namespace re::renderer;
|
||||
|
||||
Dreamcast::Dreamcast(renderer::Backend *rb) {
|
||||
sh4 = new SH4(*this);
|
||||
arm7 = new ARM7(*this);
|
||||
aica = new AICA(*this);
|
||||
holly = new Holly(*this);
|
||||
gdrom = new GDROM(*this);
|
||||
maple = new Maple(*this);
|
||||
pvr = new PVR2(*this);
|
||||
ta = new TileAccelerator(*this, rb);
|
||||
sh4_ = new SH4(*this);
|
||||
arm7_ = new ARM7(*this);
|
||||
aica_ = new AICA(*this);
|
||||
holly_ = new Holly(*this);
|
||||
gdrom_ = new GDROM(*this);
|
||||
maple_ = new Maple(*this);
|
||||
pvr_ = new PVR2(*this);
|
||||
ta_ = new TileAccelerator(*this, rb);
|
||||
}
|
||||
|
||||
Dreamcast::~Dreamcast() {
|
||||
delete sh4;
|
||||
delete arm7;
|
||||
delete aica;
|
||||
delete holly;
|
||||
delete gdrom;
|
||||
delete maple;
|
||||
delete pvr;
|
||||
delete ta;
|
||||
delete sh4_;
|
||||
delete arm7_;
|
||||
delete aica_;
|
||||
delete holly_;
|
||||
delete gdrom_;
|
||||
delete maple_;
|
||||
delete pvr_;
|
||||
delete ta_;
|
||||
}
|
||||
|
|
|
@ -2,11 +2,19 @@
|
|||
#define DREAMCAST_H
|
||||
|
||||
#include "hw/machine.h"
|
||||
#include "hw/regions.h"
|
||||
|
||||
namespace re {
|
||||
|
||||
namespace renderer {
|
||||
class Backend;
|
||||
}
|
||||
|
||||
namespace hw {
|
||||
|
||||
class AddressMap;
|
||||
class AddressSpace;
|
||||
class Memory;
|
||||
|
||||
namespace aica {
|
||||
class AICA;
|
||||
}
|
||||
|
@ -35,24 +43,27 @@ class SH4;
|
|||
|
||||
class Dreamcast : public Machine {
|
||||
public:
|
||||
Dreamcast()
|
||||
: sh4(nullptr),
|
||||
arm7(nullptr),
|
||||
aica(nullptr),
|
||||
holly(nullptr),
|
||||
gdrom(nullptr),
|
||||
maple(nullptr),
|
||||
pvr(nullptr),
|
||||
ta(nullptr) {}
|
||||
Dreamcast(renderer::Backend *rb);
|
||||
~Dreamcast();
|
||||
|
||||
hw::sh4::SH4 *sh4;
|
||||
hw::arm7::ARM7 *arm7;
|
||||
hw::aica::AICA *aica;
|
||||
hw::holly::Holly *holly;
|
||||
hw::gdrom::GDROM *gdrom;
|
||||
hw::maple::Maple *maple;
|
||||
hw::holly::PVR2 *pvr;
|
||||
hw::holly::TileAccelerator *ta;
|
||||
hw::sh4::SH4 *sh4() { return sh4_; }
|
||||
hw::arm7::ARM7 *arm7() { return arm7_; }
|
||||
hw::aica::AICA *aica() { return aica_; }
|
||||
hw::holly::Holly *holly() { return holly_; }
|
||||
hw::gdrom::GDROM *gdrom() { return gdrom_; }
|
||||
hw::maple::Maple *maple() { return maple_; }
|
||||
hw::holly::PVR2 *pvr() { return pvr_; }
|
||||
hw::holly::TileAccelerator *ta() { return ta_; }
|
||||
|
||||
private:
|
||||
hw::sh4::SH4 *sh4_;
|
||||
hw::arm7::ARM7 *arm7_;
|
||||
hw::aica::AICA *aica_;
|
||||
hw::holly::Holly *holly_;
|
||||
hw::gdrom::GDROM *gdrom_;
|
||||
hw::maple::Maple *maple_;
|
||||
hw::holly::PVR2 *pvr_;
|
||||
hw::holly::TileAccelerator *ta_;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,9 +15,8 @@ using namespace re::hw::sh4;
|
|||
(((fad & 0xff) << 16) | (fad & 0x00ff00) | ((fad & 0xff0000) >> 16))
|
||||
|
||||
GDROM::GDROM(Dreamcast &dc)
|
||||
: Device(dc),
|
||||
: Device(dc, "gdrom"),
|
||||
dc_(dc),
|
||||
memory_(nullptr),
|
||||
holly_(nullptr),
|
||||
features_{0},
|
||||
intreason_{0},
|
||||
|
@ -32,27 +31,33 @@ GDROM::GDROM(Dreamcast &dc)
|
|||
current_disc_(nullptr) {}
|
||||
|
||||
bool GDROM::Init() {
|
||||
memory_ = dc_.memory;
|
||||
holly_ = dc_.holly;
|
||||
holly_ = dc_.holly();
|
||||
|
||||
GDROM_REGISTER_R32_DELEGATE(GD_ALTSTAT_DEVCTRL);
|
||||
GDROM_REGISTER_W32_DELEGATE(GD_ALTSTAT_DEVCTRL);
|
||||
GDROM_REGISTER_R32_DELEGATE(GD_DATA);
|
||||
GDROM_REGISTER_W32_DELEGATE(GD_DATA);
|
||||
GDROM_REGISTER_R32_DELEGATE(GD_ERROR_FEATURES);
|
||||
GDROM_REGISTER_W32_DELEGATE(GD_ERROR_FEATURES);
|
||||
GDROM_REGISTER_R32_DELEGATE(GD_INTREASON_SECTCNT);
|
||||
GDROM_REGISTER_W32_DELEGATE(GD_INTREASON_SECTCNT);
|
||||
GDROM_REGISTER_R32_DELEGATE(GD_SECTNUM);
|
||||
GDROM_REGISTER_W32_DELEGATE(GD_SECTNUM);
|
||||
GDROM_REGISTER_R32_DELEGATE(GD_BYCTLLO);
|
||||
GDROM_REGISTER_W32_DELEGATE(GD_BYCTLLO);
|
||||
GDROM_REGISTER_R32_DELEGATE(GD_BYCTLHI);
|
||||
GDROM_REGISTER_W32_DELEGATE(GD_BYCTLHI);
|
||||
GDROM_REGISTER_R32_DELEGATE(GD_DRVSEL);
|
||||
GDROM_REGISTER_W32_DELEGATE(GD_DRVSEL);
|
||||
GDROM_REGISTER_R32_DELEGATE(GD_STATUS_COMMAND);
|
||||
GDROM_REGISTER_W32_DELEGATE(GD_STATUS_COMMAND);
|
||||
// initialize registers
|
||||
#define GDROM_REG_R32(name) \
|
||||
holly_->reg(name##_OFFSET).read = make_delegate(&GDROM::name##_r, this)
|
||||
#define GDROM_REG_W32(name) \
|
||||
holly_->reg(name##_OFFSET).write = make_delegate(&GDROM::name##_w, this)
|
||||
GDROM_REG_R32(GD_ALTSTAT_DEVCTRL);
|
||||
GDROM_REG_W32(GD_ALTSTAT_DEVCTRL);
|
||||
GDROM_REG_R32(GD_DATA);
|
||||
GDROM_REG_W32(GD_DATA);
|
||||
GDROM_REG_R32(GD_ERROR_FEATURES);
|
||||
GDROM_REG_W32(GD_ERROR_FEATURES);
|
||||
GDROM_REG_R32(GD_INTREASON_SECTCNT);
|
||||
GDROM_REG_W32(GD_INTREASON_SECTCNT);
|
||||
GDROM_REG_R32(GD_SECTNUM);
|
||||
GDROM_REG_W32(GD_SECTNUM);
|
||||
GDROM_REG_R32(GD_BYCTLLO);
|
||||
GDROM_REG_W32(GD_BYCTLLO);
|
||||
GDROM_REG_R32(GD_BYCTLHI);
|
||||
GDROM_REG_W32(GD_BYCTLHI);
|
||||
GDROM_REG_R32(GD_DRVSEL);
|
||||
GDROM_REG_W32(GD_DRVSEL);
|
||||
GDROM_REG_R32(GD_STATUS_COMMAND);
|
||||
GDROM_REG_W32(GD_STATUS_COMMAND);
|
||||
#undef GDROM_REG_R32
|
||||
#undef GDROM_REG_W32
|
||||
|
||||
SetDisc(nullptr);
|
||||
|
||||
|
@ -524,17 +529,17 @@ int GDROM::ReadSectors(int fad, SectorFormat format, SectorMask mask,
|
|||
return total;
|
||||
}
|
||||
|
||||
GDROM_R32_DELEGATE(GD_ALTSTAT_DEVCTRL) {
|
||||
R32_DELEGATE(GDROM::GD_ALTSTAT_DEVCTRL) {
|
||||
// this register is the same as the status register, but it does not
|
||||
// clear DMA status information when it is accessed
|
||||
return status_.full;
|
||||
}
|
||||
|
||||
GDROM_W32_DELEGATE(GD_ALTSTAT_DEVCTRL) {
|
||||
W32_DELEGATE(GDROM::GD_ALTSTAT_DEVCTRL) {
|
||||
// LOG_INFO("GD_DEVCTRL 0x%x", (uint32_t)value);
|
||||
}
|
||||
|
||||
GDROM_R32_DELEGATE(GD_DATA) {
|
||||
R32_DELEGATE(GDROM::GD_DATA) {
|
||||
uint16_t v = re::load<uint16_t>(&pio_buffer_[pio_head_]);
|
||||
pio_head_ += 2;
|
||||
if (pio_head_ == pio_size_) {
|
||||
|
@ -543,7 +548,7 @@ GDROM_R32_DELEGATE(GD_DATA) {
|
|||
return v;
|
||||
}
|
||||
|
||||
GDROM_W32_DELEGATE(GD_DATA) {
|
||||
W32_DELEGATE(GDROM::GD_DATA) {
|
||||
re::store(&pio_buffer_[pio_head_], static_cast<uint16_t>(reg.value & 0xffff));
|
||||
pio_head_ += 2;
|
||||
|
||||
|
@ -554,45 +559,45 @@ GDROM_W32_DELEGATE(GD_DATA) {
|
|||
}
|
||||
}
|
||||
|
||||
GDROM_R32_DELEGATE(GD_ERROR_FEATURES) {
|
||||
R32_DELEGATE(GDROM::GD_ERROR_FEATURES) {
|
||||
// LOG_INFO("GD_ERROR");
|
||||
return 0;
|
||||
}
|
||||
|
||||
GDROM_W32_DELEGATE(GD_ERROR_FEATURES) { features_.full = reg.value; }
|
||||
W32_DELEGATE(GDROM::GD_ERROR_FEATURES) { features_.full = reg.value; }
|
||||
|
||||
GDROM_R32_DELEGATE(GD_INTREASON_SECTCNT) { return intreason_.full; }
|
||||
R32_DELEGATE(GDROM::GD_INTREASON_SECTCNT) { return intreason_.full; }
|
||||
|
||||
GDROM_W32_DELEGATE(GD_INTREASON_SECTCNT) {
|
||||
W32_DELEGATE(GDROM::GD_INTREASON_SECTCNT) {
|
||||
// LOG_INFO("GD_SECTCNT 0x%x", reg.value);
|
||||
}
|
||||
|
||||
GDROM_R32_DELEGATE(GD_SECTNUM) { return sectnum_.full; }
|
||||
R32_DELEGATE(GDROM::GD_SECTNUM) { return sectnum_.full; }
|
||||
|
||||
GDROM_W32_DELEGATE(GD_SECTNUM) { sectnum_.full = reg.value; }
|
||||
W32_DELEGATE(GDROM::GD_SECTNUM) { sectnum_.full = reg.value; }
|
||||
|
||||
GDROM_R32_DELEGATE(GD_BYCTLLO) { return byte_count_.lo; }
|
||||
R32_DELEGATE(GDROM::GD_BYCTLLO) { return byte_count_.lo; }
|
||||
|
||||
GDROM_W32_DELEGATE(GD_BYCTLLO) { byte_count_.lo = reg.value; }
|
||||
W32_DELEGATE(GDROM::GD_BYCTLLO) { byte_count_.lo = reg.value; }
|
||||
|
||||
GDROM_R32_DELEGATE(GD_BYCTLHI) { return byte_count_.hi; }
|
||||
R32_DELEGATE(GDROM::GD_BYCTLHI) { return byte_count_.hi; }
|
||||
|
||||
GDROM_W32_DELEGATE(GD_BYCTLHI) { byte_count_.hi = reg.value; }
|
||||
W32_DELEGATE(GDROM::GD_BYCTLHI) { byte_count_.hi = reg.value; }
|
||||
|
||||
GDROM_R32_DELEGATE(GD_DRVSEL) {
|
||||
R32_DELEGATE(GDROM::GD_DRVSEL) {
|
||||
// LOG_INFO("GD_DRVSEL");
|
||||
return 0;
|
||||
}
|
||||
|
||||
GDROM_W32_DELEGATE(GD_DRVSEL) {
|
||||
W32_DELEGATE(GDROM::GD_DRVSEL) {
|
||||
// LOG_INFO("GD_DRVSEL 0x%x", (uint32_t)reg.value);
|
||||
}
|
||||
|
||||
GDROM_R32_DELEGATE(GD_STATUS_COMMAND) {
|
||||
R32_DELEGATE(GDROM::GD_STATUS_COMMAND) {
|
||||
holly_->UnrequestInterrupt(HOLLY_INTC_G1GDINT);
|
||||
return status_.full;
|
||||
}
|
||||
|
||||
GDROM_W32_DELEGATE(GD_STATUS_COMMAND) {
|
||||
W32_DELEGATE(GDROM::GD_STATUS_COMMAND) {
|
||||
ProcessATACommand(static_cast<ATACommand>(reg.value));
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "hw/gdrom/disc.h"
|
||||
#include "hw/gdrom/gdrom_types.h"
|
||||
#include "hw/machine.h"
|
||||
#include "hw/register.h"
|
||||
|
||||
namespace re {
|
||||
namespace hw {
|
||||
|
@ -15,7 +16,6 @@ class Holly;
|
|||
|
||||
class Dreamcast;
|
||||
struct Register;
|
||||
class Memory;
|
||||
|
||||
namespace gdrom {
|
||||
|
||||
|
@ -138,18 +138,6 @@ struct Session {
|
|||
uint32_t start_fad : 24;
|
||||
};
|
||||
|
||||
#define GDROM_DECLARE_R32_DELEGATE(name) uint32_t name##_read(Register &)
|
||||
#define GDROM_DECLARE_W32_DELEGATE(name) void name##_write(Register &, uint32_t)
|
||||
|
||||
#define GDROM_REGISTER_R32_DELEGATE(name) \
|
||||
holly_->reg(name##_OFFSET).read = make_delegate(&GDROM::name##_read, this)
|
||||
#define GDROM_REGISTER_W32_DELEGATE(name) \
|
||||
holly_->reg(name##_OFFSET).write = make_delegate(&GDROM::name##_write, this)
|
||||
|
||||
#define GDROM_R32_DELEGATE(name) uint32_t GDROM::name##_read(Register ®)
|
||||
#define GDROM_W32_DELEGATE(name) \
|
||||
void GDROM::name##_write(Register ®, uint32_t old_value)
|
||||
|
||||
class GDROM : public Device {
|
||||
public:
|
||||
GDROM(Dreamcast &dc);
|
||||
|
@ -176,27 +164,26 @@ class GDROM : public Device {
|
|||
int ReadSectors(int fad, SectorFormat format, SectorMask mask,
|
||||
int num_sectors, uint8_t *dst, int dst_size);
|
||||
|
||||
GDROM_DECLARE_R32_DELEGATE(GD_ALTSTAT_DEVCTRL);
|
||||
GDROM_DECLARE_W32_DELEGATE(GD_ALTSTAT_DEVCTRL);
|
||||
GDROM_DECLARE_R32_DELEGATE(GD_DATA);
|
||||
GDROM_DECLARE_W32_DELEGATE(GD_DATA);
|
||||
GDROM_DECLARE_R32_DELEGATE(GD_ERROR_FEATURES);
|
||||
GDROM_DECLARE_W32_DELEGATE(GD_ERROR_FEATURES);
|
||||
GDROM_DECLARE_R32_DELEGATE(GD_INTREASON_SECTCNT);
|
||||
GDROM_DECLARE_W32_DELEGATE(GD_INTREASON_SECTCNT);
|
||||
GDROM_DECLARE_R32_DELEGATE(GD_SECTNUM);
|
||||
GDROM_DECLARE_W32_DELEGATE(GD_SECTNUM);
|
||||
GDROM_DECLARE_R32_DELEGATE(GD_BYCTLLO);
|
||||
GDROM_DECLARE_W32_DELEGATE(GD_BYCTLLO);
|
||||
GDROM_DECLARE_R32_DELEGATE(GD_BYCTLHI);
|
||||
GDROM_DECLARE_W32_DELEGATE(GD_BYCTLHI);
|
||||
GDROM_DECLARE_R32_DELEGATE(GD_DRVSEL);
|
||||
GDROM_DECLARE_W32_DELEGATE(GD_DRVSEL);
|
||||
GDROM_DECLARE_R32_DELEGATE(GD_STATUS_COMMAND);
|
||||
GDROM_DECLARE_W32_DELEGATE(GD_STATUS_COMMAND);
|
||||
DECLARE_R32_DELEGATE(GD_ALTSTAT_DEVCTRL);
|
||||
DECLARE_W32_DELEGATE(GD_ALTSTAT_DEVCTRL);
|
||||
DECLARE_R32_DELEGATE(GD_DATA);
|
||||
DECLARE_W32_DELEGATE(GD_DATA);
|
||||
DECLARE_R32_DELEGATE(GD_ERROR_FEATURES);
|
||||
DECLARE_W32_DELEGATE(GD_ERROR_FEATURES);
|
||||
DECLARE_R32_DELEGATE(GD_INTREASON_SECTCNT);
|
||||
DECLARE_W32_DELEGATE(GD_INTREASON_SECTCNT);
|
||||
DECLARE_R32_DELEGATE(GD_SECTNUM);
|
||||
DECLARE_W32_DELEGATE(GD_SECTNUM);
|
||||
DECLARE_R32_DELEGATE(GD_BYCTLLO);
|
||||
DECLARE_W32_DELEGATE(GD_BYCTLLO);
|
||||
DECLARE_R32_DELEGATE(GD_BYCTLHI);
|
||||
DECLARE_W32_DELEGATE(GD_BYCTLHI);
|
||||
DECLARE_R32_DELEGATE(GD_DRVSEL);
|
||||
DECLARE_W32_DELEGATE(GD_DRVSEL);
|
||||
DECLARE_R32_DELEGATE(GD_STATUS_COMMAND);
|
||||
DECLARE_W32_DELEGATE(GD_STATUS_COMMAND);
|
||||
|
||||
Dreamcast &dc_;
|
||||
Memory *memory_;
|
||||
holly::Holly *holly_;
|
||||
|
||||
GD_FEATURES_T features_;
|
||||
|
|
|
@ -13,9 +13,21 @@ using namespace re::hw::maple;
|
|||
using namespace re::hw::sh4;
|
||||
using namespace re::sys;
|
||||
|
||||
Holly::Holly(Dreamcast &dc)
|
||||
: Device(dc),
|
||||
MemoryInterface(this),
|
||||
// clang-format off
|
||||
AM_BEGIN(Holly, reg_map)
|
||||
AM_RANGE(0x00000000, 0x00001fff) AM_HANDLE(&Holly::ReadRegister<uint8_t>,
|
||||
&Holly::ReadRegister<uint16_t>,
|
||||
&Holly::ReadRegister<uint32_t>,
|
||||
nullptr,
|
||||
&Holly::WriteRegister<uint8_t>,
|
||||
&Holly::WriteRegister<uint16_t>,
|
||||
&Holly::WriteRegister<uint32_t>,
|
||||
nullptr)
|
||||
AM_END()
|
||||
// clang-format on
|
||||
|
||||
Holly::Holly(Dreamcast &dc)
|
||||
: Device(dc, "holly"),
|
||||
dc_(dc),
|
||||
gdrom_(nullptr),
|
||||
maple_(nullptr),
|
||||
|
@ -23,42 +35,47 @@ Holly::Holly(Dreamcast &dc)
|
|||
regs_() {}
|
||||
|
||||
bool Holly::Init() {
|
||||
gdrom_ = dc_.gdrom;
|
||||
maple_ = dc_.maple;
|
||||
sh4_ = dc_.sh4;
|
||||
gdrom_ = dc_.gdrom();
|
||||
maple_ = dc_.maple();
|
||||
sh4_ = dc_.sh4();
|
||||
|
||||
// initialize registers
|
||||
#define HOLLY_REG(addr, name, flags, default, type) \
|
||||
regs_[name##_OFFSET] = {flags, default};
|
||||
#define HOLLY_REG_R32(name) \
|
||||
regs_[name##_OFFSET].read = make_delegate(&Holly::name##_r, this)
|
||||
#define HOLLY_REG_W32(name) \
|
||||
regs_[name##_OFFSET].write = make_delegate(&Holly::name##_w, this)
|
||||
#include "hw/holly/holly_regs.inc"
|
||||
HOLLY_REG_R32(SB_ISTNRM);
|
||||
HOLLY_REG_W32(SB_ISTNRM);
|
||||
HOLLY_REG_W32(SB_ISTEXT);
|
||||
HOLLY_REG_W32(SB_ISTERR);
|
||||
HOLLY_REG_W32(SB_IML2NRM);
|
||||
HOLLY_REG_W32(SB_IML2EXT);
|
||||
HOLLY_REG_W32(SB_IML2ERR);
|
||||
HOLLY_REG_W32(SB_IML4NRM);
|
||||
HOLLY_REG_W32(SB_IML4EXT);
|
||||
HOLLY_REG_W32(SB_IML4ERR);
|
||||
HOLLY_REG_W32(SB_IML6NRM);
|
||||
HOLLY_REG_W32(SB_IML6EXT);
|
||||
HOLLY_REG_W32(SB_IML6ERR);
|
||||
HOLLY_REG_W32(SB_C2DST);
|
||||
HOLLY_REG_W32(SB_SDST);
|
||||
HOLLY_REG_W32(SB_GDST);
|
||||
HOLLY_REG_W32(SB_ADEN);
|
||||
HOLLY_REG_W32(SB_ADST);
|
||||
HOLLY_REG_W32(SB_E1EN);
|
||||
HOLLY_REG_W32(SB_E1ST);
|
||||
HOLLY_REG_W32(SB_E2EN);
|
||||
HOLLY_REG_W32(SB_E2ST);
|
||||
HOLLY_REG_W32(SB_DDEN);
|
||||
HOLLY_REG_W32(SB_DDST);
|
||||
HOLLY_REG_W32(SB_PDEN);
|
||||
HOLLY_REG_W32(SB_PDST);
|
||||
#undef HOLLY_REG
|
||||
|
||||
HOLLY_REGISTER_R32_DELEGATE(SB_ISTNRM);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_ISTNRM);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_ISTEXT);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_ISTERR);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_IML2NRM);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_IML2EXT);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_IML2ERR);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_IML4NRM);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_IML4EXT);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_IML4ERR);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_IML6NRM);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_IML6EXT);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_IML6ERR);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_C2DST);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_SDST);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_GDST);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_ADEN);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_ADST);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_E1EN);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_E1ST);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_E2EN);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_E2ST);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_DDEN);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_DDST);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_PDEN);
|
||||
HOLLY_REGISTER_W32_DELEGATE(SB_PDST);
|
||||
#undef HOLLY_REG_R32
|
||||
#undef HOLLY_REG_W32
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -69,7 +86,7 @@ void Holly::RequestInterrupt(HollyInterrupt intr) {
|
|||
uint32_t irq = static_cast<uint32_t>(intr & ~HOLLY_INTC_MASK);
|
||||
|
||||
if (intr == HOLLY_INTC_PCVOINT) {
|
||||
dc_.maple->VBlank();
|
||||
maple_->VBlank();
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
|
@ -111,19 +128,6 @@ void Holly::UnrequestInterrupt(HollyInterrupt intr) {
|
|||
UpdateSH4Interrupts();
|
||||
}
|
||||
|
||||
void Holly::MapPhysicalMemory(Memory &memory, MemoryMap &memmap) {
|
||||
RegionHandle holly_handle = memory.AllocRegion(
|
||||
HOLLY_REG_BEGIN, HOLLY_REG_SIZE,
|
||||
make_delegate(&Holly::ReadRegister<uint8_t>, this),
|
||||
make_delegate(&Holly::ReadRegister<uint16_t>, this),
|
||||
make_delegate(&Holly::ReadRegister<uint32_t>, this), nullptr,
|
||||
make_delegate(&Holly::WriteRegister<uint8_t>, this),
|
||||
make_delegate(&Holly::WriteRegister<uint16_t>, this),
|
||||
make_delegate(&Holly::WriteRegister<uint32_t>, this), nullptr);
|
||||
|
||||
memmap.Mount(holly_handle, HOLLY_REG_SIZE, HOLLY_REG_BEGIN);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T Holly::ReadRegister(uint32_t addr) {
|
||||
uint32_t offset = addr >> 2;
|
||||
|
@ -190,7 +194,7 @@ void Holly::UpdateSH4Interrupts() {
|
|||
}
|
||||
}
|
||||
|
||||
HOLLY_R32_DELEGATE(SB_ISTNRM) {
|
||||
R32_DELEGATE(Holly::SB_ISTNRM) {
|
||||
// Note that the two highest bits indicate the OR'ed result of all of the
|
||||
// bits in SB_ISTEXT and SB_ISTERR, respectively, and writes to these two
|
||||
// bits are ignored.
|
||||
|
@ -204,41 +208,41 @@ HOLLY_R32_DELEGATE(SB_ISTNRM) {
|
|||
return v;
|
||||
}
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_ISTNRM) {
|
||||
W32_DELEGATE(Holly::SB_ISTNRM) {
|
||||
// writing a 1 clears the interrupt
|
||||
reg.value = old_value & ~reg.value;
|
||||
UpdateSH4Interrupts();
|
||||
}
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_ISTEXT) {
|
||||
W32_DELEGATE(Holly::SB_ISTEXT) {
|
||||
reg.value = old_value & ~reg.value;
|
||||
UpdateSH4Interrupts();
|
||||
}
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_ISTERR) {
|
||||
W32_DELEGATE(Holly::SB_ISTERR) {
|
||||
reg.value = old_value & ~reg.value;
|
||||
UpdateSH4Interrupts();
|
||||
}
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_IML2NRM) { UpdateSH4Interrupts(); }
|
||||
W32_DELEGATE(Holly::SB_IML2NRM) { UpdateSH4Interrupts(); }
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_IML2EXT) { UpdateSH4Interrupts(); }
|
||||
W32_DELEGATE(Holly::SB_IML2EXT) { UpdateSH4Interrupts(); }
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_IML2ERR) { UpdateSH4Interrupts(); }
|
||||
W32_DELEGATE(Holly::SB_IML2ERR) { UpdateSH4Interrupts(); }
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_IML4NRM) { UpdateSH4Interrupts(); }
|
||||
W32_DELEGATE(Holly::SB_IML4NRM) { UpdateSH4Interrupts(); }
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_IML4EXT) { UpdateSH4Interrupts(); }
|
||||
W32_DELEGATE(Holly::SB_IML4EXT) { UpdateSH4Interrupts(); }
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_IML4ERR) { UpdateSH4Interrupts(); }
|
||||
W32_DELEGATE(Holly::SB_IML4ERR) { UpdateSH4Interrupts(); }
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_IML6NRM) { UpdateSH4Interrupts(); }
|
||||
W32_DELEGATE(Holly::SB_IML6NRM) { UpdateSH4Interrupts(); }
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_IML6EXT) { UpdateSH4Interrupts(); }
|
||||
W32_DELEGATE(Holly::SB_IML6EXT) { UpdateSH4Interrupts(); }
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_IML6ERR) { UpdateSH4Interrupts(); }
|
||||
W32_DELEGATE(Holly::SB_IML6ERR) { UpdateSH4Interrupts(); }
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_C2DST) {
|
||||
W32_DELEGATE(Holly::SB_C2DST) {
|
||||
if (!reg.value) {
|
||||
return;
|
||||
}
|
||||
|
@ -255,7 +259,7 @@ HOLLY_W32_DELEGATE(SB_C2DST) {
|
|||
RequestInterrupt(HOLLY_INTC_DTDE2INT);
|
||||
}
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_SDST) {
|
||||
W32_DELEGATE(Holly::SB_SDST) {
|
||||
if (!reg.value) {
|
||||
return;
|
||||
}
|
||||
|
@ -263,7 +267,7 @@ HOLLY_W32_DELEGATE(SB_SDST) {
|
|||
LOG_FATAL("Sort DMA not supported");
|
||||
}
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_GDST) {
|
||||
W32_DELEGATE(Holly::SB_GDST) {
|
||||
// if a "0" is written to this register, it is ignored
|
||||
reg.value |= old_value;
|
||||
|
||||
|
@ -307,7 +311,7 @@ HOLLY_W32_DELEGATE(SB_GDST) {
|
|||
RequestInterrupt(HOLLY_INTC_G1DEINT);
|
||||
}
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_ADEN) {
|
||||
W32_DELEGATE(Holly::SB_ADEN) {
|
||||
if (!reg.value) {
|
||||
return;
|
||||
}
|
||||
|
@ -315,7 +319,7 @@ HOLLY_W32_DELEGATE(SB_ADEN) {
|
|||
LOG_WARNING("Ignored aica DMA request");
|
||||
}
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_ADST) {
|
||||
W32_DELEGATE(Holly::SB_ADST) {
|
||||
if (!reg.value) {
|
||||
return;
|
||||
}
|
||||
|
@ -323,7 +327,7 @@ HOLLY_W32_DELEGATE(SB_ADST) {
|
|||
LOG_WARNING("Ignored aica DMA request");
|
||||
}
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_E1EN) {
|
||||
W32_DELEGATE(Holly::SB_E1EN) {
|
||||
if (!reg.value) {
|
||||
return;
|
||||
}
|
||||
|
@ -331,7 +335,7 @@ HOLLY_W32_DELEGATE(SB_E1EN) {
|
|||
LOG_WARNING("Ignored ext1 DMA request");
|
||||
}
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_E1ST) {
|
||||
W32_DELEGATE(Holly::SB_E1ST) {
|
||||
if (!reg.value) {
|
||||
return;
|
||||
}
|
||||
|
@ -339,7 +343,7 @@ HOLLY_W32_DELEGATE(SB_E1ST) {
|
|||
LOG_WARNING("Ignored ext1 DMA request");
|
||||
}
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_E2EN) {
|
||||
W32_DELEGATE(Holly::SB_E2EN) {
|
||||
if (!reg.value) {
|
||||
return;
|
||||
}
|
||||
|
@ -347,7 +351,7 @@ HOLLY_W32_DELEGATE(SB_E2EN) {
|
|||
LOG_WARNING("Ignored ext2 DMA request");
|
||||
}
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_E2ST) {
|
||||
W32_DELEGATE(Holly::SB_E2ST) {
|
||||
if (!reg.value) {
|
||||
return;
|
||||
}
|
||||
|
@ -355,7 +359,7 @@ HOLLY_W32_DELEGATE(SB_E2ST) {
|
|||
LOG_WARNING("Ignored ext2 DMA request");
|
||||
}
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_DDEN) {
|
||||
W32_DELEGATE(Holly::SB_DDEN) {
|
||||
if (!reg.value) {
|
||||
return;
|
||||
}
|
||||
|
@ -363,7 +367,7 @@ HOLLY_W32_DELEGATE(SB_DDEN) {
|
|||
LOG_WARNING("Ignored dev DMA request");
|
||||
}
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_DDST) {
|
||||
W32_DELEGATE(Holly::SB_DDST) {
|
||||
if (!reg.value) {
|
||||
return;
|
||||
}
|
||||
|
@ -371,7 +375,7 @@ HOLLY_W32_DELEGATE(SB_DDST) {
|
|||
LOG_WARNING("Ignored dev DMA request");
|
||||
}
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_PDEN) {
|
||||
W32_DELEGATE(Holly::SB_PDEN) {
|
||||
if (!reg.value) {
|
||||
return;
|
||||
}
|
||||
|
@ -379,7 +383,7 @@ HOLLY_W32_DELEGATE(SB_PDEN) {
|
|||
LOG_WARNING("Ignored pvr DMA request");
|
||||
}
|
||||
|
||||
HOLLY_W32_DELEGATE(SB_PDST) {
|
||||
W32_DELEGATE(Holly::SB_PDST) {
|
||||
if (!reg.value) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "core/delegate.h"
|
||||
#include "hw/holly/holly_types.h"
|
||||
#include "hw/machine.h"
|
||||
#include "hw/memory.h"
|
||||
#include "hw/register.h"
|
||||
|
||||
namespace re {
|
||||
|
@ -23,20 +24,10 @@ class Dreamcast;
|
|||
|
||||
namespace holly {
|
||||
|
||||
#define HOLLY_DECLARE_R32_DELEGATE(name) uint32_t name##_read(Register &)
|
||||
#define HOLLY_DECLARE_W32_DELEGATE(name) void name##_write(Register &, uint32_t)
|
||||
|
||||
#define HOLLY_REGISTER_R32_DELEGATE(name) \
|
||||
regs_[name##_OFFSET].read = make_delegate(&Holly::name##_read, this)
|
||||
#define HOLLY_REGISTER_W32_DELEGATE(name) \
|
||||
regs_[name##_OFFSET].write = make_delegate(&Holly::name##_write, this)
|
||||
|
||||
#define HOLLY_R32_DELEGATE(name) uint32_t Holly::name##_read(Register ®)
|
||||
#define HOLLY_W32_DELEGATE(name) \
|
||||
void Holly::name##_write(Register ®, uint32_t old_value)
|
||||
|
||||
class Holly : public Device, public MemoryInterface {
|
||||
class Holly : public Device {
|
||||
public:
|
||||
AM_DECLARE(reg_map);
|
||||
|
||||
Holly(Dreamcast &dc);
|
||||
|
||||
Register ®(int offset) { return regs_[offset]; }
|
||||
|
@ -52,8 +43,6 @@ class Holly : public Device, public MemoryInterface {
|
|||
#undef HOLLY_REG
|
||||
|
||||
private:
|
||||
// MemoryInterface
|
||||
void MapPhysicalMemory(Memory &memory, MemoryMap &memmap) final;
|
||||
template <typename T>
|
||||
T ReadRegister(uint32_t addr);
|
||||
template <typename T>
|
||||
|
@ -61,32 +50,32 @@ class Holly : public Device, public MemoryInterface {
|
|||
|
||||
void UpdateSH4Interrupts();
|
||||
|
||||
HOLLY_DECLARE_R32_DELEGATE(SB_ISTNRM);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_ISTNRM);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_ISTEXT);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_ISTERR);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_IML2NRM);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_IML2EXT);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_IML2ERR);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_IML4NRM);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_IML4EXT);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_IML4ERR);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_IML6NRM);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_IML6EXT);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_IML6ERR);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_C2DST);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_SDST);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_GDST);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_ADEN);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_ADST);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_E1EN);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_E1ST);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_E2EN);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_E2ST);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_DDEN);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_DDST);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_PDEN);
|
||||
HOLLY_DECLARE_W32_DELEGATE(SB_PDST);
|
||||
DECLARE_R32_DELEGATE(SB_ISTNRM);
|
||||
DECLARE_W32_DELEGATE(SB_ISTNRM);
|
||||
DECLARE_W32_DELEGATE(SB_ISTEXT);
|
||||
DECLARE_W32_DELEGATE(SB_ISTERR);
|
||||
DECLARE_W32_DELEGATE(SB_IML2NRM);
|
||||
DECLARE_W32_DELEGATE(SB_IML2EXT);
|
||||
DECLARE_W32_DELEGATE(SB_IML2ERR);
|
||||
DECLARE_W32_DELEGATE(SB_IML4NRM);
|
||||
DECLARE_W32_DELEGATE(SB_IML4EXT);
|
||||
DECLARE_W32_DELEGATE(SB_IML4ERR);
|
||||
DECLARE_W32_DELEGATE(SB_IML6NRM);
|
||||
DECLARE_W32_DELEGATE(SB_IML6EXT);
|
||||
DECLARE_W32_DELEGATE(SB_IML6ERR);
|
||||
DECLARE_W32_DELEGATE(SB_C2DST);
|
||||
DECLARE_W32_DELEGATE(SB_SDST);
|
||||
DECLARE_W32_DELEGATE(SB_GDST);
|
||||
DECLARE_W32_DELEGATE(SB_ADEN);
|
||||
DECLARE_W32_DELEGATE(SB_ADST);
|
||||
DECLARE_W32_DELEGATE(SB_E1EN);
|
||||
DECLARE_W32_DELEGATE(SB_E1ST);
|
||||
DECLARE_W32_DELEGATE(SB_E2EN);
|
||||
DECLARE_W32_DELEGATE(SB_E2ST);
|
||||
DECLARE_W32_DELEGATE(SB_DDEN);
|
||||
DECLARE_W32_DELEGATE(SB_DDST);
|
||||
DECLARE_W32_DELEGATE(SB_PDEN);
|
||||
DECLARE_W32_DELEGATE(SB_PDST);
|
||||
|
||||
Dreamcast &dc_;
|
||||
gdrom::GDROM *gdrom_;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef HOLLY_TYPES_H
|
||||
#define HOLLY_TYPES_H
|
||||
|
||||
#include "hw/regions.h"
|
||||
#include <stdint.h>
|
||||
|
||||
namespace re {
|
||||
namespace hw {
|
||||
|
@ -10,10 +10,10 @@ namespace holly {
|
|||
// registers
|
||||
enum {
|
||||
#define HOLLY_REG(addr, name, flags, default, type) \
|
||||
name##_OFFSET = (addr - HOLLY_REG_BEGIN) >> 2,
|
||||
name##_OFFSET = (addr - 0x005f6000) >> 2,
|
||||
#include "hw/holly/holly_regs.inc"
|
||||
#undef HOLLY_REG
|
||||
NUM_HOLLY_REGS = HOLLY_REG_SIZE >> 2,
|
||||
NUM_HOLLY_REGS = 0x00002000 >> 2,
|
||||
};
|
||||
|
||||
// interrupts
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "hw/holly/pvr2.h"
|
||||
#include "hw/holly/pvr2_types.h"
|
||||
#include "hw/holly/tile_accelerator.h"
|
||||
#include "hw/sh4/sh4.h"
|
||||
#include "hw/dreamcast.h"
|
||||
#include "hw/memory.h"
|
||||
|
||||
|
@ -11,9 +12,34 @@ using namespace re::hw::holly;
|
|||
using namespace re::hw::sh4;
|
||||
using namespace re::renderer;
|
||||
|
||||
PVR2::PVR2(Dreamcast &dc)
|
||||
: Device(dc),
|
||||
MemoryInterface(this),
|
||||
// clang-format off
|
||||
AM_BEGIN(PVR2, reg_map)
|
||||
AM_RANGE(0x00000000, 0x00000fff) AM_HANDLE(nullptr,
|
||||
nullptr,
|
||||
&PVR2::ReadRegister,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
&PVR2::WriteRegister,
|
||||
nullptr)
|
||||
AM_RANGE(0x00001000, 0x00001fff) AM_MOUNT()
|
||||
AM_END()
|
||||
|
||||
AM_BEGIN(PVR2, vram_map)
|
||||
AM_RANGE(0x00000000, 0x007fffff) AM_MOUNT()
|
||||
AM_RANGE(0x01000000, 0x017fffff) AM_HANDLE(&PVR2::ReadVRamInterleaved<uint8_t>,
|
||||
&PVR2::ReadVRamInterleaved<uint16_t>,
|
||||
&PVR2::ReadVRamInterleaved<uint32_t>,
|
||||
nullptr,
|
||||
&PVR2::WriteVRamInterleaved<uint8_t>,
|
||||
&PVR2::WriteVRamInterleaved<uint16_t>,
|
||||
&PVR2::WriteVRamInterleaved<uint32_t>,
|
||||
nullptr)
|
||||
AM_END()
|
||||
// clang-format on
|
||||
|
||||
PVR2::PVR2(Dreamcast &dc)
|
||||
: Device(dc, "pvr"),
|
||||
dc_(dc),
|
||||
scheduler_(nullptr),
|
||||
holly_(nullptr),
|
||||
|
@ -26,45 +52,30 @@ PVR2::PVR2(Dreamcast &dc)
|
|||
current_scanline_(0) {}
|
||||
|
||||
bool PVR2::Init() {
|
||||
scheduler_ = dc_.scheduler;
|
||||
holly_ = dc_.holly;
|
||||
ta_ = dc_.ta;
|
||||
palette_ram_ = dc_.memory->TranslateVirtual(PVR_PALETTE_BEGIN);
|
||||
video_ram_ = dc_.memory->TranslateVirtual(PVR_VRAM32_BEGIN);
|
||||
scheduler_ = dc_.scheduler();
|
||||
holly_ = dc_.holly();
|
||||
ta_ = dc_.ta();
|
||||
palette_ram_ = dc_.sh4()->space().Translate(0x005f9000);
|
||||
video_ram_ = dc_.sh4()->space().Translate(0x04000000);
|
||||
|
||||
// initialize registers
|
||||
#define PVR_REG(addr, name, flags, default, type) \
|
||||
regs_[name##_OFFSET] = {flags, default};
|
||||
#define PVR_REG_R32(name) \
|
||||
regs_[name##_OFFSET].read = make_delegate(&PVR2::name##_r, this)
|
||||
#define PVR_REG_W32(name) \
|
||||
regs_[name##_OFFSET].write = make_delegate(&PVR2::name##_w, this)
|
||||
#include "hw/holly/pvr2_regs.inc"
|
||||
PVR_REG_W32(SPG_LOAD);
|
||||
PVR_REG_W32(FB_R_CTRL);
|
||||
#undef PVR_REG
|
||||
|
||||
PVR2_REGISTER_W32_DELEGATE(SPG_LOAD);
|
||||
PVR2_REGISTER_W32_DELEGATE(FB_R_CTRL);
|
||||
|
||||
// configure initial vsync interval
|
||||
ReconfigureSPG();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PVR2::MapPhysicalMemory(Memory &memory, MemoryMap &memmap) {
|
||||
RegionHandle pvr_reg_handle = memory.AllocRegion(
|
||||
PVR_REG_BEGIN, PVR_REG_SIZE, nullptr, nullptr,
|
||||
make_delegate(&PVR2::ReadRegister, this), nullptr, nullptr, nullptr,
|
||||
make_delegate(&PVR2::WriteRegister, this), nullptr);
|
||||
|
||||
RegionHandle pvr_vram64_handle = memory.AllocRegion(
|
||||
PVR_VRAM64_BEGIN, PVR_VRAM64_SIZE,
|
||||
make_delegate(&PVR2::ReadVRamInterleaved<uint8_t>, this),
|
||||
make_delegate(&PVR2::ReadVRamInterleaved<uint16_t>, this),
|
||||
make_delegate(&PVR2::ReadVRamInterleaved<uint32_t>, this), nullptr,
|
||||
nullptr, make_delegate(&PVR2::WriteVRamInterleaved<uint16_t>, this),
|
||||
make_delegate(&PVR2::WriteVRamInterleaved<uint32_t>, this), nullptr);
|
||||
|
||||
memmap.Mount(pvr_reg_handle, PVR_REG_SIZE, PVR_REG_BEGIN);
|
||||
memmap.Mount(pvr_vram64_handle, PVR_VRAM64_SIZE, PVR_VRAM64_BEGIN);
|
||||
}
|
||||
|
||||
uint32_t PVR2::ReadRegister(uint32_t addr) {
|
||||
uint32_t offset = addr >> 2;
|
||||
Register ® = regs_[offset];
|
||||
|
@ -201,6 +212,6 @@ void PVR2::NextScanline() {
|
|||
re::make_delegate(&PVR2::NextScanline, this), HZ_TO_NANO(line_clock_));
|
||||
}
|
||||
|
||||
PVR2_W32_DELEGATE(SPG_LOAD) { ReconfigureSPG(); }
|
||||
W32_DELEGATE(PVR2::SPG_LOAD) { ReconfigureSPG(); }
|
||||
|
||||
PVR2_W32_DELEGATE(FB_R_CTRL) { ReconfigureSPG(); }
|
||||
W32_DELEGATE(PVR2::FB_R_CTRL) { ReconfigureSPG(); }
|
||||
|
|
|
@ -4,33 +4,25 @@
|
|||
#include <stdint.h>
|
||||
#include "hw/holly/pvr2_types.h"
|
||||
#include "hw/machine.h"
|
||||
#include "hw/memory.h"
|
||||
#include "hw/register.h"
|
||||
#include "hw/scheduler.h"
|
||||
|
||||
namespace re {
|
||||
namespace hw {
|
||||
|
||||
class Dreamcast;
|
||||
struct Register;
|
||||
|
||||
namespace holly {
|
||||
|
||||
class Holly;
|
||||
class TileAccelerator;
|
||||
|
||||
#define PVR2_DECLARE_R32_DELEGATE(name) uint32_t name##_read(Register &)
|
||||
#define PVR2_DECLARE_W32_DELEGATE(name) void name##_write(Register &, uint32_t)
|
||||
|
||||
#define PVR2_REGISTER_R32_DELEGATE(name) \
|
||||
regs_[name##_OFFSET].read = make_delegate(&PVR2::name##_read, this)
|
||||
#define PVR2_REGISTER_W32_DELEGATE(name) \
|
||||
regs_[name##_OFFSET].write = make_delegate(&PVR2::name##_write, this)
|
||||
|
||||
#define PVR2_R32_DELEGATE(name) uint32_t PVR2::name##_read(Register ®)
|
||||
#define PVR2_W32_DELEGATE(name) \
|
||||
void PVR2::name##_write(Register ®, uint32_t old_value)
|
||||
|
||||
class PVR2 : public Device, public MemoryInterface {
|
||||
class PVR2 : public Device {
|
||||
public:
|
||||
AM_DECLARE(reg_map);
|
||||
AM_DECLARE(vram_map);
|
||||
|
||||
PVR2(Dreamcast &dc);
|
||||
|
||||
Register ®(int offset) { return regs_[offset]; }
|
||||
|
@ -43,8 +35,6 @@ class PVR2 : public Device, public MemoryInterface {
|
|||
#undef PVR_REG
|
||||
|
||||
private:
|
||||
// MemoryInterface
|
||||
void MapPhysicalMemory(Memory &memory, MemoryMap &memmap) final;
|
||||
uint32_t ReadRegister(uint32_t addr);
|
||||
void WriteRegister(uint32_t addr, uint32_t value);
|
||||
template <typename T>
|
||||
|
@ -55,8 +45,8 @@ class PVR2 : public Device, public MemoryInterface {
|
|||
void ReconfigureSPG();
|
||||
void NextScanline();
|
||||
|
||||
PVR2_DECLARE_W32_DELEGATE(SPG_LOAD);
|
||||
PVR2_DECLARE_W32_DELEGATE(FB_R_CTRL);
|
||||
DECLARE_W32_DELEGATE(SPG_LOAD);
|
||||
DECLARE_W32_DELEGATE(FB_R_CTRL);
|
||||
|
||||
Dreamcast &dc_;
|
||||
Scheduler *scheduler_;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef PVR2_TYPES_H
|
||||
#define PVR2_TYPES_H
|
||||
|
||||
#include "hw/regions.h"
|
||||
#include <stdint.h>
|
||||
|
||||
namespace re {
|
||||
namespace hw {
|
||||
|
@ -191,10 +191,10 @@ union TA_ISP_BASE_T {
|
|||
|
||||
enum {
|
||||
#define PVR_REG(addr, name, flags, default_value, type) \
|
||||
name##_OFFSET = (addr - PVR_REG_BEGIN) >> 2,
|
||||
name##_OFFSET = (addr - 0x005f8000) >> 2,
|
||||
#include "hw/holly/pvr2_regs.inc"
|
||||
#undef PVR_REG
|
||||
NUM_PVR_REGS = PVR_REG_SIZE >> 2,
|
||||
NUM_PVR_REGS = 0x00001000 >> 2,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "hw/holly/tile_accelerator.h"
|
||||
#include "hw/holly/pvr2.h"
|
||||
#include "hw/holly/trace.h"
|
||||
#include "hw/sh4/sh4.h"
|
||||
#include "hw/dreamcast.h"
|
||||
#include "hw/memory.h"
|
||||
#include "sys/filesystem.h"
|
||||
|
@ -15,7 +16,28 @@ using namespace re::hw::holly;
|
|||
using namespace re::renderer;
|
||||
using namespace re::sys;
|
||||
|
||||
static void BuildLookupTables();
|
||||
// clang-format off
|
||||
AM_BEGIN(TileAccelerator, fifo_map)
|
||||
AM_RANGE(0x0000000, 0x07fffff) AM_HANDLE(nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
&TileAccelerator::WritePolyFIFO,
|
||||
nullptr)
|
||||
AM_RANGE(0x1000000, 0x1ffffff) AM_HANDLE(nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
&TileAccelerator::WriteTextureFIFO,
|
||||
nullptr)
|
||||
AM_END()
|
||||
// clang-format on
|
||||
|
||||
static void BuildLookupTables();
|
||||
static int GetParamSize_raw(const PCW &pcw, int vertex_type);
|
||||
static int GetPolyType_raw(const PCW &pcw);
|
||||
static int GetVertexType_raw(const PCW &pcw);
|
||||
|
@ -208,14 +230,13 @@ int TileAccelerator::GetVertexType(const PCW &pcw) {
|
|||
pcw.para_type * TA_NUM_LISTS + pcw.list_type];
|
||||
}
|
||||
|
||||
TileAccelerator::TileAccelerator(Dreamcast &dc, Backend &rb)
|
||||
: Device(dc),
|
||||
MemoryInterface(this),
|
||||
TileAccelerator::TileAccelerator(Dreamcast &dc, Backend *rb)
|
||||
: Device(dc, "ta"),
|
||||
WindowInterface(this),
|
||||
dc_(dc),
|
||||
rb_(rb),
|
||||
tile_renderer_(rb, *this),
|
||||
memory_(nullptr),
|
||||
sh4_(nullptr),
|
||||
holly_(nullptr),
|
||||
pvr_(nullptr),
|
||||
video_ram_(nullptr),
|
||||
|
@ -230,15 +251,24 @@ TileAccelerator::TileAccelerator(Dreamcast &dc, Backend &rb)
|
|||
}
|
||||
|
||||
bool TileAccelerator::Init() {
|
||||
memory_ = dc_.memory;
|
||||
holly_ = dc_.holly;
|
||||
pvr_ = dc_.pvr;
|
||||
video_ram_ = dc_.memory->TranslateVirtual(PVR_VRAM32_BEGIN);
|
||||
sh4_ = dc_.sh4();
|
||||
holly_ = dc_.holly();
|
||||
pvr_ = dc_.pvr();
|
||||
video_ram_ = sh4_->space().Translate(0x04000000);
|
||||
|
||||
TA_REGISTER_W32_DELEGATE(SOFTRESET);
|
||||
TA_REGISTER_W32_DELEGATE(TA_LIST_INIT);
|
||||
TA_REGISTER_W32_DELEGATE(TA_LIST_CONT);
|
||||
TA_REGISTER_W32_DELEGATE(STARTRENDER);
|
||||
// initialize registers
|
||||
#define TA_REG_R32(name) \
|
||||
pvr_->reg(name##_OFFSET).read = \
|
||||
make_delegate(&TileAccelerator::name##_r, this)
|
||||
#define TA_REG_W32(name) \
|
||||
pvr_->reg(name##_OFFSET).write = \
|
||||
make_delegate(&TileAccelerator::name##_w, this)
|
||||
TA_REG_W32(SOFTRESET);
|
||||
TA_REG_W32(TA_LIST_INIT);
|
||||
TA_REG_W32(TA_LIST_CONT);
|
||||
TA_REG_W32(STARTRENDER);
|
||||
#undef TA_REG_R32
|
||||
#undef TA_REG_W32
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -266,7 +296,7 @@ TextureHandle TileAccelerator::GetTexture(
|
|||
uint32_t texture_addr = tcw.texture_addr << 3;
|
||||
|
||||
// get the texture data
|
||||
uint8_t *video_ram = dc_.memory->TranslateVirtual(PVR_VRAM32_BEGIN);
|
||||
uint8_t *video_ram = sh4_->space().Translate(0x04000000);
|
||||
uint8_t *texture = &video_ram[texture_addr];
|
||||
int width = 8 << tsp.texture_u_size;
|
||||
int height = 8 << tsp.texture_v_size;
|
||||
|
@ -276,7 +306,7 @@ TextureHandle TileAccelerator::GetTexture(
|
|||
int texture_size = (width * height * element_size_bits) >> 3;
|
||||
|
||||
// get the palette data
|
||||
uint8_t *palette_ram = dc_.memory->TranslateVirtual(PVR_PALETTE_BEGIN);
|
||||
uint8_t *palette_ram = sh4_->space().Translate(0x005f9000);
|
||||
uint8_t *palette = nullptr;
|
||||
uint32_t palette_addr = 0;
|
||||
int palette_size = 0;
|
||||
|
@ -436,27 +466,12 @@ void TileAccelerator::FinalizeContext(uint32_t addr) {
|
|||
last_tctx_ = tctx;
|
||||
}
|
||||
|
||||
void TileAccelerator::MapPhysicalMemory(Memory &memory, MemoryMap &memmap) {
|
||||
RegionHandle ta_poly_handle = memory.AllocRegion(
|
||||
TA_POLY_BEGIN, TA_POLY_SIZE, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, make_delegate(&TileAccelerator::WritePolyFIFO, this), nullptr);
|
||||
|
||||
RegionHandle ta_texture_handle = memory.AllocRegion(
|
||||
TA_TEXTURE_BEGIN, TA_TEXTURE_SIZE, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, make_delegate(&TileAccelerator::WriteTextureFIFO, this),
|
||||
nullptr);
|
||||
|
||||
memmap.Mount(ta_poly_handle, TA_POLY_SIZE, TA_POLY_BEGIN);
|
||||
memmap.Mount(ta_texture_handle, TA_TEXTURE_SIZE, TA_TEXTURE_BEGIN);
|
||||
}
|
||||
|
||||
void TileAccelerator::WritePolyFIFO(uint32_t addr, uint32_t value) {
|
||||
WriteContext(pvr_->TA_ISP_BASE.base_address, value);
|
||||
}
|
||||
|
||||
void TileAccelerator::WriteTextureFIFO(uint32_t addr, uint32_t value) {
|
||||
addr &= 0xeeffffff;
|
||||
|
||||
re::store(&video_ram_[addr], value);
|
||||
}
|
||||
|
||||
|
@ -535,7 +550,7 @@ void TileAccelerator::InvalidateTexture(TextureCacheMap::iterator it) {
|
|||
RemoveAccessWatch(entry.palette_watch);
|
||||
}
|
||||
|
||||
rb_.FreeTexture(entry.handle);
|
||||
rb_->FreeTexture(entry.handle);
|
||||
|
||||
textures_.erase(it);
|
||||
}
|
||||
|
@ -573,7 +588,7 @@ void TileAccelerator::SaveRegisterState(TileContext *tctx) {
|
|||
if (!pvr_->FPU_PARAM_CFG.region_header_type) {
|
||||
tctx->autosort = !pvr_->ISP_FEED_CFG.presort;
|
||||
} else {
|
||||
uint32_t region_data = memory_->R32(PVR_VRAM64_BEGIN + pvr_->REGION_BASE);
|
||||
uint32_t region_data = sh4_->space().R32(0x05000000 + pvr_->REGION_BASE);
|
||||
tctx->autosort = !(region_data & 0x20000000);
|
||||
}
|
||||
|
||||
|
@ -602,13 +617,13 @@ void TileAccelerator::SaveRegisterState(TileContext *tctx) {
|
|||
// available at 0x0 when booting the bios, so masking this seems to be the
|
||||
// correct solution
|
||||
uint32_t vram_offset =
|
||||
PVR_VRAM64_BEGIN +
|
||||
0x05000000 +
|
||||
((tctx->addr + pvr_->ISP_BACKGND_T.tag_address * 4) & 0x7fffff);
|
||||
|
||||
// get surface parameters
|
||||
tctx->bg_isp.full = memory_->R32(vram_offset);
|
||||
tctx->bg_tsp.full = memory_->R32(vram_offset + 4);
|
||||
tctx->bg_tcw.full = memory_->R32(vram_offset + 8);
|
||||
tctx->bg_isp.full = sh4_->space().R32(vram_offset);
|
||||
tctx->bg_tsp.full = sh4_->space().R32(vram_offset + 4);
|
||||
tctx->bg_tcw.full = sh4_->space().R32(vram_offset + 8);
|
||||
vram_offset += 12;
|
||||
|
||||
// get the background depth
|
||||
|
@ -632,7 +647,8 @@ void TileAccelerator::SaveRegisterState(TileContext *tctx) {
|
|||
for (int i = 0, bg_offset = 0; i < 3; i++) {
|
||||
CHECK_LE(bg_offset + vertex_size, (int)sizeof(tctx->bg_vertices));
|
||||
|
||||
memory_->Memcpy(&tctx->bg_vertices[bg_offset], vram_offset, vertex_size);
|
||||
sh4_->space().Memcpy(&tctx->bg_vertices[bg_offset], vram_offset,
|
||||
vertex_size);
|
||||
|
||||
bg_offset += vertex_size;
|
||||
vram_offset += vertex_size;
|
||||
|
@ -668,7 +684,7 @@ void TileAccelerator::ToggleTracing() {
|
|||
}
|
||||
}
|
||||
|
||||
TA_W32_DELEGATE(SOFTRESET) {
|
||||
W32_DELEGATE(TileAccelerator::SOFTRESET) {
|
||||
if (!(reg.value & 0x1)) {
|
||||
return;
|
||||
}
|
||||
|
@ -676,7 +692,7 @@ TA_W32_DELEGATE(SOFTRESET) {
|
|||
SoftReset();
|
||||
}
|
||||
|
||||
TA_W32_DELEGATE(TA_LIST_INIT) {
|
||||
W32_DELEGATE(TileAccelerator::TA_LIST_INIT) {
|
||||
if (!(reg.value & 0x80000000)) {
|
||||
return;
|
||||
}
|
||||
|
@ -684,7 +700,7 @@ TA_W32_DELEGATE(TA_LIST_INIT) {
|
|||
InitContext(pvr_->TA_ISP_BASE.base_address);
|
||||
}
|
||||
|
||||
TA_W32_DELEGATE(TA_LIST_CONT) {
|
||||
W32_DELEGATE(TileAccelerator::TA_LIST_CONT) {
|
||||
if (!(reg.value & 0x80000000)) {
|
||||
return;
|
||||
}
|
||||
|
@ -692,7 +708,7 @@ TA_W32_DELEGATE(TA_LIST_CONT) {
|
|||
LOG_WARNING("Unsupported TA_LIST_CONT");
|
||||
}
|
||||
|
||||
TA_W32_DELEGATE(STARTRENDER) {
|
||||
W32_DELEGATE(TileAccelerator::STARTRENDER) {
|
||||
if (!reg.value) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include "hw/holly/tile_renderer.h"
|
||||
#include "hw/holly/trace.h"
|
||||
#include "hw/machine.h"
|
||||
#include "hw/memory.h"
|
||||
#include "hw/register.h"
|
||||
#include "renderer/backend.h"
|
||||
#include "sys/memory.h"
|
||||
|
||||
|
@ -15,8 +17,10 @@ namespace re {
|
|||
namespace hw {
|
||||
|
||||
class Dreamcast;
|
||||
class Memory;
|
||||
struct Register;
|
||||
|
||||
namespace sh4 {
|
||||
class SH4;
|
||||
}
|
||||
|
||||
namespace holly {
|
||||
|
||||
|
@ -41,23 +45,7 @@ struct TextureEntry {
|
|||
typedef std::unordered_map<uint32_t, TileContext *> TileContextMap;
|
||||
typedef std::queue<TileContext *> TileContextQueue;
|
||||
|
||||
#define TA_DECLARE_R32_DELEGATE(name) uint32_t name##_read(Register &)
|
||||
#define TA_DECLARE_W32_DELEGATE(name) void name##_write(Register &, uint32_t)
|
||||
|
||||
#define TA_REGISTER_R32_DELEGATE(name) \
|
||||
pvr_->reg(name##_OFFSET).read = \
|
||||
make_delegate(&TileAccelerator::name##_read, this)
|
||||
#define TA_REGISTER_W32_DELEGATE(name) \
|
||||
pvr_->reg(name##_OFFSET).write = \
|
||||
make_delegate(&TileAccelerator::name##_write, this)
|
||||
|
||||
#define TA_R32_DELEGATE(name) \
|
||||
uint32_t TileAccelerator::name##_read(Register ®)
|
||||
#define TA_W32_DELEGATE(name) \
|
||||
void TileAccelerator::name##_write(Register ®, uint32_t old_value)
|
||||
|
||||
class TileAccelerator : public Device,
|
||||
public MemoryInterface,
|
||||
public WindowInterface,
|
||||
public TextureProvider {
|
||||
friend class PVR2;
|
||||
|
@ -67,7 +55,9 @@ class TileAccelerator : public Device,
|
|||
static int GetPolyType(const PCW &pcw);
|
||||
static int GetVertexType(const PCW &pcw);
|
||||
|
||||
TileAccelerator(Dreamcast &dc, renderer::Backend &rb);
|
||||
AM_DECLARE(fifo_map);
|
||||
|
||||
TileAccelerator(Dreamcast &dc, renderer::Backend *rb);
|
||||
|
||||
bool Init() final;
|
||||
|
||||
|
@ -76,8 +66,6 @@ class TileAccelerator : public Device,
|
|||
RegisterTextureDelegate register_delegate) final;
|
||||
|
||||
private:
|
||||
// MemoryInterface
|
||||
void MapPhysicalMemory(Memory &memory, MemoryMap &memmap) final;
|
||||
void WritePolyFIFO(uint32_t addr, uint32_t value);
|
||||
void WriteTextureFIFO(uint32_t addr, uint32_t value);
|
||||
|
||||
|
@ -99,15 +87,15 @@ class TileAccelerator : public Device,
|
|||
|
||||
void ToggleTracing();
|
||||
|
||||
TA_DECLARE_W32_DELEGATE(SOFTRESET);
|
||||
TA_DECLARE_W32_DELEGATE(TA_LIST_INIT);
|
||||
TA_DECLARE_W32_DELEGATE(TA_LIST_CONT);
|
||||
TA_DECLARE_W32_DELEGATE(STARTRENDER);
|
||||
DECLARE_W32_DELEGATE(SOFTRESET);
|
||||
DECLARE_W32_DELEGATE(TA_LIST_INIT);
|
||||
DECLARE_W32_DELEGATE(TA_LIST_CONT);
|
||||
DECLARE_W32_DELEGATE(STARTRENDER);
|
||||
|
||||
Dreamcast &dc_;
|
||||
renderer::Backend &rb_;
|
||||
renderer::Backend *rb_;
|
||||
TileRenderer tile_renderer_;
|
||||
Memory *memory_;
|
||||
sh4::SH4 *sh4_;
|
||||
Holly *holly_;
|
||||
PVR2 *pvr_;
|
||||
uint8_t *video_ram_;
|
||||
|
|
|
@ -105,7 +105,7 @@ TextureKey TextureProvider::GetTextureKey(const TSP &tsp, const TCW &tcw) {
|
|||
return ((uint64_t)tsp.full << 32) | tcw.full;
|
||||
}
|
||||
|
||||
TileRenderer::TileRenderer(Backend &rb, TextureProvider &texture_provider)
|
||||
TileRenderer::TileRenderer(Backend *rb, TextureProvider &texture_provider)
|
||||
: rb_(rb), texture_provider_(texture_provider) {}
|
||||
|
||||
void TileRenderer::ParseContext(const TileContext &tctx,
|
||||
|
@ -179,13 +179,13 @@ void TileRenderer::RenderContext(const TileRenderContext &rctx) {
|
|||
auto &verts = rctx_.verts;
|
||||
auto &sorted_surfs = rctx_.sorted_surfs;
|
||||
|
||||
rb_.BeginSurfaces(rctx_.projection, verts.data(), verts.size());
|
||||
rb_->BeginSurfaces(rctx_.projection, verts.data(), verts.size());
|
||||
|
||||
for (int i = 0, n = surfs.size(); i < n; i++) {
|
||||
rb_.DrawSurface(surfs[sorted_surfs[i]]);
|
||||
rb_->DrawSurface(surfs[sorted_surfs[i]]);
|
||||
}
|
||||
|
||||
rb_.EndSurfaces();
|
||||
rb_->EndSurfaces();
|
||||
}
|
||||
|
||||
void TileRenderer::RenderContext(const TileContext &tctx) {
|
||||
|
@ -928,8 +928,8 @@ RegisterTextureResult TileRenderer::RegisterTexture(const TileContext &tctx,
|
|||
? WRAP_CLAMP_TO_EDGE
|
||||
: (tsp.flip_v ? WRAP_MIRRORED_REPEAT : WRAP_REPEAT);
|
||||
|
||||
TextureHandle handle = rb_.RegisterTexture(pixel_fmt, filter, wrap_u, wrap_v,
|
||||
mip_mapped, width, height, output);
|
||||
TextureHandle handle = rb_->RegisterTexture(
|
||||
pixel_fmt, filter, wrap_u, wrap_v, mip_mapped, width, height, output);
|
||||
|
||||
return {handle, pixel_fmt, filter, wrap_u, wrap_v, mip_mapped, width, height};
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ struct TileRenderContext {
|
|||
|
||||
class TileRenderer {
|
||||
public:
|
||||
TileRenderer(renderer::Backend &rb, TextureProvider &texture_provider);
|
||||
TileRenderer(renderer::Backend *rb, TextureProvider &texture_provider);
|
||||
|
||||
void ParseContext(const TileContext &tctx, TileRenderContext *rctx,
|
||||
bool map_params);
|
||||
|
@ -106,7 +106,7 @@ class TileRenderer {
|
|||
renderer::TextureHandle GetTexture(const TileContext &tctx, const TSP &tsp,
|
||||
const TCW &tcw);
|
||||
|
||||
renderer::Backend &rb_;
|
||||
renderer::Backend *rb_;
|
||||
TextureProvider &texture_provider_;
|
||||
|
||||
// keep a persistent instance of this for RenderContext. the instance will be
|
||||
|
|
|
@ -20,39 +20,47 @@ void ExecuteInterface::Suspend() { suspended_ = true; }
|
|||
|
||||
void ExecuteInterface::Resume() { suspended_ = false; }
|
||||
|
||||
MemoryInterface::MemoryInterface(Device *device) { device->memory_ = this; }
|
||||
MemoryInterface::MemoryInterface(Device *device, AddressMapper mapper)
|
||||
: mapper_(mapper), space_(*device->machine_.memory()) {
|
||||
device->memory_ = this;
|
||||
}
|
||||
|
||||
WindowInterface::WindowInterface(Device *device) { device->window_ = this; }
|
||||
|
||||
Device::Device(Machine &machine)
|
||||
: debug_(nullptr), execute_(nullptr), memory_(nullptr), window_(nullptr) {
|
||||
machine.devices.push_back(this);
|
||||
Device::Device(Machine &machine, const char *name)
|
||||
: machine_(machine),
|
||||
name_(name),
|
||||
debug_(nullptr),
|
||||
execute_(nullptr),
|
||||
memory_(nullptr),
|
||||
window_(nullptr) {
|
||||
machine_.RegisterDevice(this);
|
||||
}
|
||||
|
||||
bool Device::Init() { return true; }
|
||||
|
||||
Machine::Machine() : suspended_(false) {
|
||||
debugger = FLAGS_gdb ? new Debugger(*this) : nullptr;
|
||||
memory = new Memory(*this);
|
||||
scheduler = new Scheduler(*this);
|
||||
debugger_ = FLAGS_gdb ? new Debugger(*this) : nullptr;
|
||||
memory_ = new Memory(*this);
|
||||
scheduler_ = new Scheduler(*this);
|
||||
}
|
||||
|
||||
Machine::~Machine() {
|
||||
delete debugger;
|
||||
delete memory;
|
||||
delete scheduler;
|
||||
delete debugger_;
|
||||
delete memory_;
|
||||
delete scheduler_;
|
||||
}
|
||||
|
||||
bool Machine::Init() {
|
||||
if (debugger && !debugger->Init()) {
|
||||
if (debugger_ && !debugger_->Init()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!memory->Init()) {
|
||||
if (!memory_->Init()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto device : devices) {
|
||||
for (auto device : devices_) {
|
||||
if (!device->Init()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -66,31 +74,47 @@ void Machine::Suspend() { suspended_ = true; }
|
|||
void Machine::Resume() { suspended_ = false; }
|
||||
|
||||
void Machine::Tick(const std::chrono::nanoseconds &delta) {
|
||||
if (debugger) {
|
||||
debugger->PumpEvents();
|
||||
if (debugger_) {
|
||||
debugger_->PumpEvents();
|
||||
}
|
||||
|
||||
if (!suspended_) {
|
||||
scheduler->Tick(delta);
|
||||
scheduler_->Tick(delta);
|
||||
}
|
||||
}
|
||||
|
||||
Device *Machine::LookupDevice(const char *name) {
|
||||
for (auto device : devices_) {
|
||||
if (!strcmp(device->name(), name)) {
|
||||
return device;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Machine::RegisterDevice(Device *device) { devices_.push_back(device); }
|
||||
|
||||
void Machine::OnPaint(bool show_main_menu) {
|
||||
for (auto device : devices) {
|
||||
if (!device->window()) {
|
||||
for (auto device : devices_) {
|
||||
WindowInterface *window = device->window();
|
||||
|
||||
if (!window) {
|
||||
continue;
|
||||
}
|
||||
|
||||
device->window()->OnPaint(show_main_menu);
|
||||
window->OnPaint(show_main_menu);
|
||||
}
|
||||
}
|
||||
|
||||
void Machine::OnKeyDown(Keycode code, int16_t value) {
|
||||
for (auto device : devices) {
|
||||
if (!device->window()) {
|
||||
for (auto device : devices_) {
|
||||
WindowInterface *window = device->window();
|
||||
|
||||
if (!window) {
|
||||
continue;
|
||||
}
|
||||
|
||||
device->window()->OnKeyDown(code, value);
|
||||
window->OnKeyDown(code, value);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
#include "hw/memory.h"
|
||||
#include "ui/window.h"
|
||||
|
||||
namespace re {
|
||||
|
@ -12,7 +13,6 @@ class Device;
|
|||
class Debugger;
|
||||
class Machine;
|
||||
class Memory;
|
||||
class MemoryMap;
|
||||
class Scheduler;
|
||||
|
||||
class DebugInterface {
|
||||
|
@ -49,11 +49,15 @@ class ExecuteInterface {
|
|||
|
||||
class MemoryInterface {
|
||||
public:
|
||||
MemoryInterface(Device *device);
|
||||
MemoryInterface(Device *device, AddressMapper mapper);
|
||||
virtual ~MemoryInterface() = default;
|
||||
|
||||
virtual void MapPhysicalMemory(Memory &memory, MemoryMap &memmap) {}
|
||||
virtual void MapVirtualMemory(Memory &memory, MemoryMap &memmap) {}
|
||||
AddressMapper mapper() { return mapper_; }
|
||||
AddressSpace &space() { return space_; }
|
||||
|
||||
protected:
|
||||
AddressMapper mapper_;
|
||||
AddressSpace space_;
|
||||
};
|
||||
|
||||
class WindowInterface {
|
||||
|
@ -72,18 +76,20 @@ class Device {
|
|||
friend class WindowInterface;
|
||||
|
||||
public:
|
||||
Device(Machine &machine, const char *name);
|
||||
virtual ~Device() = default;
|
||||
|
||||
const char *name() { return name_; }
|
||||
DebugInterface *debug() { return debug_; }
|
||||
ExecuteInterface *execute() { return execute_; }
|
||||
MemoryInterface *memory() { return memory_; }
|
||||
WindowInterface *window() { return window_; }
|
||||
|
||||
Device(Machine &machine);
|
||||
|
||||
virtual bool Init();
|
||||
|
||||
private:
|
||||
Machine &machine_;
|
||||
const char *name_;
|
||||
DebugInterface *debug_;
|
||||
ExecuteInterface *execute_;
|
||||
MemoryInterface *memory_;
|
||||
|
@ -98,22 +104,28 @@ class Machine {
|
|||
virtual ~Machine();
|
||||
|
||||
bool suspended() { return suspended_; }
|
||||
Debugger *debugger() { return debugger_; }
|
||||
Memory *memory() { return memory_; }
|
||||
Scheduler *scheduler() { return scheduler_; }
|
||||
std::vector<Device *> &devices() { return devices_; }
|
||||
|
||||
bool Init();
|
||||
void Suspend();
|
||||
void Resume();
|
||||
|
||||
Device *LookupDevice(const char *name);
|
||||
void RegisterDevice(Device *device);
|
||||
|
||||
void Tick(const std::chrono::nanoseconds &delta);
|
||||
void OnPaint(bool show_main_menu);
|
||||
void OnKeyDown(ui::Keycode code, int16_t value);
|
||||
|
||||
Debugger *debugger;
|
||||
Memory *memory;
|
||||
Scheduler *scheduler;
|
||||
std::vector<Device *> devices;
|
||||
|
||||
private:
|
||||
bool suspended_;
|
||||
Debugger *debugger_;
|
||||
Memory *memory_;
|
||||
Scheduler *scheduler_;
|
||||
std::vector<Device *> devices_;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "hw/holly/holly.h"
|
||||
#include "hw/maple/maple.h"
|
||||
#include "hw/maple/maple_controller.h"
|
||||
#include "hw/sh4/sh4.h"
|
||||
#include "hw/dreamcast.h"
|
||||
#include "hw/memory.h"
|
||||
|
||||
|
@ -12,10 +13,10 @@ using namespace re::hw::sh4;
|
|||
using namespace re::ui;
|
||||
|
||||
Maple::Maple(Dreamcast &dc)
|
||||
: Device(dc),
|
||||
: Device(dc, "maple"),
|
||||
WindowInterface(this),
|
||||
dc_(dc),
|
||||
memory_(nullptr),
|
||||
sh4_(nullptr),
|
||||
holly_(nullptr),
|
||||
devices_() {
|
||||
// default controller device
|
||||
|
@ -23,10 +24,17 @@ Maple::Maple(Dreamcast &dc)
|
|||
}
|
||||
|
||||
bool Maple::Init() {
|
||||
memory_ = dc_.memory;
|
||||
holly_ = dc_.holly;
|
||||
sh4_ = dc_.sh4();
|
||||
holly_ = dc_.holly();
|
||||
|
||||
MAPLE_REGISTER_W32_DELEGATE(SB_MDST);
|
||||
// initialize registers
|
||||
#define MAPLE_REG_R32(name) \
|
||||
holly_->reg(name##_OFFSET).read = make_delegate(&Maple::name##_r, this)
|
||||
#define MAPLE_REG_W32(name) \
|
||||
holly_->reg(name##_OFFSET).write = make_delegate(&Maple::name##_w, this)
|
||||
MAPLE_REG_W32(SB_MDST);
|
||||
#undef MAPLE_REG_R32
|
||||
#undef MAPLE_REG_W32
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -61,15 +69,15 @@ void Maple::StartDMA() {
|
|||
MapleFrame frame, res;
|
||||
|
||||
do {
|
||||
desc.full = memory_->R64(start_addr);
|
||||
desc.full = sh4_->space().R64(start_addr);
|
||||
start_addr += 8;
|
||||
|
||||
// read input
|
||||
frame.header.full = memory_->R32(start_addr);
|
||||
frame.header.full = sh4_->space().R32(start_addr);
|
||||
start_addr += 4;
|
||||
|
||||
for (uint32_t i = 0; i < frame.header.num_words; i++) {
|
||||
frame.params[i] = memory_->R32(start_addr);
|
||||
frame.params[i] = sh4_->space().R32(start_addr);
|
||||
start_addr += 4;
|
||||
}
|
||||
|
||||
|
@ -77,15 +85,15 @@ void Maple::StartDMA() {
|
|||
std::unique_ptr<MapleDevice> &dev = devices_[desc.port];
|
||||
|
||||
if (dev && dev->HandleFrame(frame, res)) {
|
||||
memory_->W32(desc.result_addr, res.header.full);
|
||||
sh4_->space().W32(desc.result_addr, res.header.full);
|
||||
desc.result_addr += 4;
|
||||
|
||||
for (uint32_t i = 0; i < res.header.num_words; i++) {
|
||||
memory_->W32(desc.result_addr, res.params[i]);
|
||||
sh4_->space().W32(desc.result_addr, res.params[i]);
|
||||
desc.result_addr += 4;
|
||||
}
|
||||
} else {
|
||||
memory_->W32(desc.result_addr, 0xffffffff);
|
||||
sh4_->space().W32(desc.result_addr, 0xffffffff);
|
||||
}
|
||||
} while (!desc.last);
|
||||
|
||||
|
@ -93,7 +101,7 @@ void Maple::StartDMA() {
|
|||
holly_->RequestInterrupt(HOLLY_INTC_MDEINT);
|
||||
}
|
||||
|
||||
MAPLE_W32_DELEGATE(SB_MDST) {
|
||||
W32_DELEGATE(Maple::SB_MDST) {
|
||||
uint32_t enabled = holly_->SB_MDEN;
|
||||
if (enabled) {
|
||||
if (reg.value) {
|
||||
|
|
|
@ -3,17 +3,22 @@
|
|||
|
||||
#include <memory>
|
||||
#include "hw/machine.h"
|
||||
#include "hw/register.h"
|
||||
#include "ui/keycode.h"
|
||||
|
||||
namespace re {
|
||||
namespace hw {
|
||||
|
||||
namespace sh4 {
|
||||
class SH4;
|
||||
}
|
||||
|
||||
namespace holly {
|
||||
class Holly;
|
||||
}
|
||||
|
||||
class Dreamcast;
|
||||
struct Register;
|
||||
class Memory;
|
||||
class AddressSpace;
|
||||
|
||||
namespace maple {
|
||||
|
||||
|
@ -100,18 +105,6 @@ class MapleDevice {
|
|||
virtual bool HandleFrame(const MapleFrame &frame, MapleFrame &res) = 0;
|
||||
};
|
||||
|
||||
#define MAPLE_DECLARE_R32_DELEGATE(name) uint32_t name##_read(Register &)
|
||||
#define MAPLE_DECLARE_W32_DELEGATE(name) void name##_write(Register &, uint32_t)
|
||||
|
||||
#define MAPLE_REGISTER_R32_DELEGATE(name) \
|
||||
holly_->reg(name##_OFFSET).read = make_delegate(&Maple::name##_read, this)
|
||||
#define MAPLE_REGISTER_W32_DELEGATE(name) \
|
||||
holly_->reg(name##_OFFSET).write = make_delegate(&Maple::name##_write, this)
|
||||
|
||||
#define MAPLE_R32_DELEGATE(name) uint32_t Maple::name##_read(Register ®)
|
||||
#define MAPLE_W32_DELEGATE(name) \
|
||||
void Maple::name##_write(Register ®, uint32_t old_value)
|
||||
|
||||
class Maple : public Device, public WindowInterface {
|
||||
public:
|
||||
Maple(Dreamcast &dc);
|
||||
|
@ -127,10 +120,10 @@ class Maple : public Device, public WindowInterface {
|
|||
bool HandleFrame(const MapleFrame &frame, MapleFrame &res);
|
||||
void StartDMA();
|
||||
|
||||
MAPLE_DECLARE_W32_DELEGATE(SB_MDST);
|
||||
DECLARE_W32_DELEGATE(SB_MDST);
|
||||
|
||||
Dreamcast &dc_;
|
||||
Memory *memory_;
|
||||
sh4::SH4 *sh4_;
|
||||
holly::Holly *holly_;
|
||||
|
||||
std::unique_ptr<MapleDevice> devices_[MAX_PORTS];
|
||||
|
|
767
src/hw/memory.cc
767
src/hw/memory.cc
|
@ -7,239 +7,94 @@ using namespace re;
|
|||
using namespace re::hw;
|
||||
using namespace re::sys;
|
||||
|
||||
static inline bool PageAligned(uint32_t start, uint32_t size) {
|
||||
static bool PageAligned(uint32_t start, uint32_t size) {
|
||||
return (start & (PAGE_OFFSET_BITS - 1)) == 0 &&
|
||||
((start + size) & (PAGE_OFFSET_BITS - 1)) == 0;
|
||||
}
|
||||
|
||||
// map virtual addresses to pages
|
||||
static inline int PageIndex(uint32_t virtual_addr) {
|
||||
static int PageIndex(uint32_t virtual_addr) {
|
||||
return virtual_addr >> PAGE_OFFSET_BITS;
|
||||
}
|
||||
|
||||
static inline uint32_t PageOffset(uint32_t virtual_addr) {
|
||||
static uint32_t PageOffset(uint32_t virtual_addr) {
|
||||
return virtual_addr & PAGE_OFFSET_MASK;
|
||||
}
|
||||
|
||||
// pack and unpack page entry bitstrings
|
||||
static inline PageEntry PackEntry(const uint8_t *base,
|
||||
const MemoryRegion *region,
|
||||
uint32_t region_offset) {
|
||||
if (region->dynamic) {
|
||||
return region_offset | region->handle;
|
||||
static PageEntry PackEntry(const MemoryRegion ®ion, uint32_t region_offset) {
|
||||
return region_offset | (region.dynamic ? 0 : REGION_TYPE_MASK) |
|
||||
region.handle;
|
||||
}
|
||||
|
||||
static uint32_t RegionOffset(const PageEntry &page) {
|
||||
return page & REGION_OFFSET_MASK;
|
||||
}
|
||||
|
||||
static int RegionTypeIsStatic(const PageEntry &page) {
|
||||
return page & REGION_TYPE_MASK;
|
||||
}
|
||||
|
||||
static int RegionIndex(const PageEntry &page) {
|
||||
return page & REGION_INDEX_MASK;
|
||||
}
|
||||
|
||||
static bool ReserveAddressSpace(uint8_t **base) {
|
||||
// find a contiguous (1 << 32) byte chunk of memory to map an address space to
|
||||
int i = 64;
|
||||
|
||||
while (i > 32) {
|
||||
i--;
|
||||
|
||||
*base = reinterpret_cast<uint8_t *>(1ull << i);
|
||||
|
||||
if (!ReservePages(*base, ADDRESS_SPACE_SIZE)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// reservation was a success, release now so shared memory can be mapped
|
||||
// into it
|
||||
ReleasePages(*base, ADDRESS_SPACE_SIZE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return reinterpret_cast<uintptr_t>(base) | region->physical_addr |
|
||||
region_offset;
|
||||
}
|
||||
LOG_WARNING("Failed to reserve address space");
|
||||
|
||||
static inline int IsStaticRegion(const PageEntry &page) {
|
||||
return !(page & PAGE_REGION_MASK);
|
||||
}
|
||||
|
||||
static inline uint8_t *RegionPointer(const PageEntry &page) {
|
||||
return reinterpret_cast<uint8_t *>(static_cast<uintptr_t>(page));
|
||||
}
|
||||
|
||||
static inline uint32_t RegionOffset(const PageEntry &page) {
|
||||
return page & PAGE_REGION_OFFSET_MASK;
|
||||
}
|
||||
|
||||
static inline int RegionIndex(const PageEntry &page) {
|
||||
return page & PAGE_REGION_MASK;
|
||||
}
|
||||
|
||||
MemoryMap::MemoryMap() : entries_(), num_entries_(0) {}
|
||||
|
||||
void MemoryMap::Mount(RegionHandle handle, uint32_t size,
|
||||
uint32_t virtual_addr) {
|
||||
MapEntry *entry = AllocEntry();
|
||||
entry->type = MAP_ENTRY_MOUNT;
|
||||
entry->mount.handle = handle;
|
||||
entry->mount.size = size;
|
||||
entry->mount.virtual_addr = virtual_addr;
|
||||
}
|
||||
|
||||
void MemoryMap::Mirror(uint32_t physical_addr, uint32_t size,
|
||||
uint32_t virtual_addr) {
|
||||
MapEntry *entry = AllocEntry();
|
||||
entry->type = MAP_ENTRY_MIRROR;
|
||||
entry->mirror.physical_addr = physical_addr;
|
||||
entry->mirror.size = size;
|
||||
entry->mirror.virtual_addr = virtual_addr;
|
||||
}
|
||||
|
||||
MapEntry *MemoryMap::AllocEntry() {
|
||||
CHECK_LT(num_entries_, MAX_REGIONS);
|
||||
MapEntry *entry = &entries_[num_entries_];
|
||||
new (entry) MapEntry();
|
||||
entry->handle = num_entries_++;
|
||||
return entry;
|
||||
}
|
||||
|
||||
// helpers for emitted assembly
|
||||
uint8_t Memory::R8(void *memory, uint32_t addr) {
|
||||
return reinterpret_cast<Memory *>(memory)->R8(addr);
|
||||
}
|
||||
uint16_t Memory::R16(void *memory, uint32_t addr) {
|
||||
return reinterpret_cast<Memory *>(memory)->R16(addr);
|
||||
}
|
||||
uint32_t Memory::R32(void *memory, uint32_t addr) {
|
||||
return reinterpret_cast<Memory *>(memory)->R32(addr);
|
||||
}
|
||||
uint64_t Memory::R64(void *memory, uint32_t addr) {
|
||||
return reinterpret_cast<Memory *>(memory)->R64(addr);
|
||||
}
|
||||
void Memory::W8(void *memory, uint32_t addr, uint8_t value) {
|
||||
reinterpret_cast<Memory *>(memory)->W8(addr, value);
|
||||
}
|
||||
void Memory::W16(void *memory, uint32_t addr, uint16_t value) {
|
||||
reinterpret_cast<Memory *>(memory)->W16(addr, value);
|
||||
}
|
||||
void Memory::W32(void *memory, uint32_t addr, uint32_t value) {
|
||||
reinterpret_cast<Memory *>(memory)->W32(addr, value);
|
||||
}
|
||||
void Memory::W64(void *memory, uint32_t addr, uint64_t value) {
|
||||
reinterpret_cast<Memory *>(memory)->W64(addr, value);
|
||||
return false;
|
||||
}
|
||||
|
||||
Memory::Memory(Machine &machine)
|
||||
: machine_(machine),
|
||||
shmem_(SHMEM_INVALID),
|
||||
physical_base_(nullptr),
|
||||
virtual_base_(nullptr),
|
||||
protected_base_(nullptr) {
|
||||
num_regions_ = 1; // 0 page is reserved
|
||||
regions_ = new MemoryRegion[MAX_REGIONS]();
|
||||
pages_ = new PageEntry[NUM_PAGES]();
|
||||
: machine_(machine), shmem_(SHMEM_INVALID), shmem_size_(0) {
|
||||
// 0 page is reserved, meaning all valid page entries must be non-zero
|
||||
num_regions_ = 1;
|
||||
}
|
||||
|
||||
Memory::~Memory() {
|
||||
Unmap();
|
||||
|
||||
DestroySharedMemory();
|
||||
|
||||
delete[] regions_;
|
||||
delete[] pages_;
|
||||
}
|
||||
Memory::~Memory() { DestroySharedMemory(); }
|
||||
|
||||
bool Memory::Init() {
|
||||
// create the backing shared memory object
|
||||
if (!CreateSharedMemory()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// iterate each device, giving it a chance to append to the memory map
|
||||
MemoryMap memmap;
|
||||
// map each memory interface's address space
|
||||
for (auto device : machine_.devices()) {
|
||||
MemoryInterface *memory = device->memory();
|
||||
|
||||
for (auto device : machine_.devices) {
|
||||
if (!device->memory()) {
|
||||
if (!memory) {
|
||||
continue;
|
||||
}
|
||||
|
||||
device->memory()->MapPhysicalMemory(*this, memmap);
|
||||
// create the actual address map
|
||||
AddressMap map;
|
||||
AddressMapper mapper = memory->mapper();
|
||||
mapper(device, machine_, map, 0);
|
||||
|
||||
// apply the map to create the address space
|
||||
CHECK(memory->space().Map(map));
|
||||
}
|
||||
|
||||
for (auto device : machine_.devices) {
|
||||
if (!device->memory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
device->memory()->MapVirtualMemory(*this, memmap);
|
||||
}
|
||||
|
||||
return Map(memmap);
|
||||
}
|
||||
|
||||
uint8_t *Memory::TranslateVirtual(uint32_t addr) {
|
||||
return virtual_base_ + addr;
|
||||
}
|
||||
|
||||
uint8_t *Memory::TranslateProtected(uint32_t addr) {
|
||||
return protected_base_ + addr;
|
||||
}
|
||||
|
||||
uint8_t Memory::R8(uint32_t addr) {
|
||||
return ReadBytes<uint8_t, &MemoryRegion::r8>(addr);
|
||||
}
|
||||
|
||||
uint16_t Memory::R16(uint32_t addr) {
|
||||
return ReadBytes<uint16_t, &MemoryRegion::r16>(addr);
|
||||
}
|
||||
|
||||
uint32_t Memory::R32(uint32_t addr) {
|
||||
return ReadBytes<uint32_t, &MemoryRegion::r32>(addr);
|
||||
}
|
||||
|
||||
uint64_t Memory::R64(uint32_t addr) {
|
||||
return ReadBytes<uint64_t, &MemoryRegion::r64>(addr);
|
||||
}
|
||||
|
||||
void Memory::W8(uint32_t addr, uint8_t value) {
|
||||
WriteBytes<uint8_t, &MemoryRegion::w8>(addr, value);
|
||||
}
|
||||
|
||||
void Memory::W16(uint32_t addr, uint16_t value) {
|
||||
WriteBytes<uint16_t, &MemoryRegion::w16>(addr, value);
|
||||
}
|
||||
|
||||
void Memory::W32(uint32_t addr, uint32_t value) {
|
||||
WriteBytes<uint32_t, &MemoryRegion::w32>(addr, value);
|
||||
}
|
||||
|
||||
void Memory::W64(uint32_t addr, uint64_t value) {
|
||||
WriteBytes<uint64_t, &MemoryRegion::w64>(addr, value);
|
||||
}
|
||||
|
||||
void Memory::Memcpy(uint32_t virtual_dst, const void *ptr, uint32_t size) {
|
||||
CHECK(size % 4 == 0);
|
||||
|
||||
const uint8_t *src = reinterpret_cast<const uint8_t *>(ptr);
|
||||
uint32_t end = virtual_dst + size;
|
||||
while (virtual_dst < end) {
|
||||
W32(virtual_dst, re::load<uint32_t>(src));
|
||||
virtual_dst += 4;
|
||||
src += 4;
|
||||
}
|
||||
}
|
||||
|
||||
void Memory::Memcpy(void *ptr, uint32_t virtual_src, uint32_t size) {
|
||||
CHECK(size % 4 == 0);
|
||||
|
||||
uint8_t *dst = reinterpret_cast<uint8_t *>(ptr);
|
||||
uint8_t *end = dst + size;
|
||||
while (dst < end) {
|
||||
re::store(dst, R32(virtual_src));
|
||||
virtual_src += 4;
|
||||
dst += 4;
|
||||
}
|
||||
}
|
||||
|
||||
void Memory::Memcpy(uint32_t virtual_dst, uint32_t virtual_src, uint32_t size) {
|
||||
CHECK(size % 4 == 0);
|
||||
|
||||
uint32_t end = virtual_dst + size;
|
||||
while (virtual_dst < end) {
|
||||
W32(virtual_dst, R32(virtual_src));
|
||||
virtual_src += 4;
|
||||
virtual_dst += 4;
|
||||
}
|
||||
}
|
||||
|
||||
void Memory::Lookup(uint32_t virtual_addr, uint8_t **ptr, MemoryRegion **region,
|
||||
uint32_t *offset) {
|
||||
PageEntry page = pages_[PageIndex(virtual_addr)];
|
||||
uint32_t page_offset = PageOffset(virtual_addr);
|
||||
|
||||
if (IsStaticRegion(page)) {
|
||||
*ptr = RegionPointer(page) + page_offset;
|
||||
*region = nullptr;
|
||||
*offset = 0;
|
||||
} else {
|
||||
*ptr = nullptr;
|
||||
*region = ®ions_[RegionIndex(page)];
|
||||
*offset = RegionOffset(page) + page_offset;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Memory::CreateSharedMemory() {
|
||||
|
@ -256,206 +111,242 @@ bool Memory::CreateSharedMemory() {
|
|||
|
||||
void Memory::DestroySharedMemory() { ::DestroySharedMemory(shmem_); }
|
||||
|
||||
bool Memory::ReserveAddressSpace(uint8_t **base) {
|
||||
for (int i = 63; i >= 32; i--) {
|
||||
*base = reinterpret_cast<uint8_t *>(1ull << i);
|
||||
RegionHandle Memory::CreateRegion(uint32_t size) {
|
||||
CHECK(PageAligned(shmem_size_, size));
|
||||
|
||||
if (!ReservePages(*base, ADDRESS_SPACE_SIZE)) {
|
||||
continue;
|
||||
}
|
||||
MemoryRegion ®ion = AllocRegion(size);
|
||||
region.dynamic = false;
|
||||
|
||||
// reservation was a success, release and expect subsequent mappings to
|
||||
// succeed
|
||||
ReleasePages(*base, ADDRESS_SPACE_SIZE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG_WARNING("Failed to reserve address space");
|
||||
return false;
|
||||
return region.handle;
|
||||
}
|
||||
|
||||
MemoryRegion *Memory::AllocRegion() {
|
||||
RegionHandle Memory::CreateRegion(uint32_t size, R8Delegate r8, R16Delegate r16,
|
||||
R32Delegate r32, R64Delegate r64,
|
||||
W8Delegate w8, W16Delegate w16,
|
||||
W32Delegate w32, W64Delegate w64) {
|
||||
MemoryRegion ®ion = AllocRegion(size);
|
||||
|
||||
region.dynamic = true;
|
||||
region.r8 = r8;
|
||||
region.r16 = r16;
|
||||
region.r32 = r32;
|
||||
region.r64 = r64;
|
||||
region.w8 = w8;
|
||||
region.w16 = w16;
|
||||
region.w32 = w32;
|
||||
region.w64 = w64;
|
||||
|
||||
return region.handle;
|
||||
}
|
||||
|
||||
MemoryRegion &Memory::AllocRegion(uint32_t size) {
|
||||
CHECK_LT(num_regions_, MAX_REGIONS);
|
||||
MemoryRegion *region = ®ions_[num_regions_];
|
||||
new (region) MemoryRegion();
|
||||
region->handle = num_regions_++;
|
||||
CHECK(PageAligned(shmem_size_, size));
|
||||
|
||||
MemoryRegion ®ion = regions_[num_regions_];
|
||||
new (®ion) MemoryRegion();
|
||||
region.handle = num_regions_++;
|
||||
region.shmem_offset = shmem_size_;
|
||||
region.size = size;
|
||||
|
||||
shmem_size_ += size;
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
RegionHandle Memory::AllocRegion(uint32_t physical_addr, uint32_t size) {
|
||||
MemoryRegion *region = AllocRegion();
|
||||
region->dynamic = false;
|
||||
region->physical_addr = physical_addr;
|
||||
region->size = size;
|
||||
return region->handle;
|
||||
AddressMap::AddressMap() : entries_(), num_entries_(0) {}
|
||||
|
||||
void AddressMap::Mount(RegionHandle handle, uint32_t size,
|
||||
uint32_t virtual_addr) {
|
||||
MapEntry &entry = AllocEntry();
|
||||
entry.type = MAP_ENTRY_MOUNT;
|
||||
entry.mount.handle = handle;
|
||||
entry.mount.size = size;
|
||||
entry.mount.virtual_addr = virtual_addr;
|
||||
}
|
||||
|
||||
RegionHandle Memory::AllocRegion(uint32_t physical_addr, uint32_t size,
|
||||
R8Delegate r8, R16Delegate r16,
|
||||
R32Delegate r32, R64Delegate r64,
|
||||
W8Delegate w8, W16Delegate w16,
|
||||
W32Delegate w32, W64Delegate w64) {
|
||||
MemoryRegion *region = AllocRegion();
|
||||
region->dynamic = true;
|
||||
region->physical_addr = physical_addr;
|
||||
region->size = size;
|
||||
region->r8 = r8;
|
||||
region->r16 = r16;
|
||||
region->r32 = r32;
|
||||
region->r64 = r64;
|
||||
region->w8 = w8;
|
||||
region->w16 = w16;
|
||||
region->w32 = w32;
|
||||
region->w64 = w64;
|
||||
return region->handle;
|
||||
void AddressMap::Mirror(uint32_t physical_addr, uint32_t size,
|
||||
uint32_t virtual_addr) {
|
||||
MapEntry &entry = AllocEntry();
|
||||
entry.type = MAP_ENTRY_MIRROR;
|
||||
entry.mirror.physical_addr = physical_addr;
|
||||
entry.mirror.size = size;
|
||||
entry.mirror.virtual_addr = virtual_addr;
|
||||
}
|
||||
|
||||
bool Memory::Map(const MemoryMap &map) {
|
||||
// map regions into the physical space
|
||||
if (!MapPhysicalSpace()) {
|
||||
MapEntry &AddressMap::AllocEntry() {
|
||||
CHECK_LT(num_entries_, MAX_REGIONS);
|
||||
MapEntry &entry = entries_[num_entries_++];
|
||||
new (&entry) MapEntry();
|
||||
return entry;
|
||||
}
|
||||
|
||||
uint8_t AddressSpace::R8(void *space, uint32_t addr) {
|
||||
return reinterpret_cast<AddressSpace *>(space)->R8(addr);
|
||||
}
|
||||
uint16_t AddressSpace::R16(void *space, uint32_t addr) {
|
||||
return reinterpret_cast<AddressSpace *>(space)->R16(addr);
|
||||
}
|
||||
uint32_t AddressSpace::R32(void *space, uint32_t addr) {
|
||||
return reinterpret_cast<AddressSpace *>(space)->R32(addr);
|
||||
}
|
||||
uint64_t AddressSpace::R64(void *space, uint32_t addr) {
|
||||
return reinterpret_cast<AddressSpace *>(space)->R64(addr);
|
||||
}
|
||||
void AddressSpace::W8(void *space, uint32_t addr, uint8_t value) {
|
||||
reinterpret_cast<AddressSpace *>(space)->W8(addr, value);
|
||||
}
|
||||
void AddressSpace::W16(void *space, uint32_t addr, uint16_t value) {
|
||||
reinterpret_cast<AddressSpace *>(space)->W16(addr, value);
|
||||
}
|
||||
void AddressSpace::W32(void *space, uint32_t addr, uint32_t value) {
|
||||
reinterpret_cast<AddressSpace *>(space)->W32(addr, value);
|
||||
}
|
||||
void AddressSpace::W64(void *space, uint32_t addr, uint64_t value) {
|
||||
reinterpret_cast<AddressSpace *>(space)->W64(addr, value);
|
||||
}
|
||||
|
||||
AddressSpace::AddressSpace(Memory &memory)
|
||||
: memory_(memory), base_(nullptr), protected_base_(nullptr) {}
|
||||
|
||||
AddressSpace::~AddressSpace() { Unmap(); }
|
||||
|
||||
bool AddressSpace::Map(const AddressMap &map) {
|
||||
Unmap();
|
||||
|
||||
// flatten the supplied address map out into a virtual page table
|
||||
CreatePageTable(map);
|
||||
|
||||
// map the virtual page table into both the base and protected mirrors
|
||||
if (!ReserveAddressSpace(&base_) || !MapPageTable(base_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// flatten out the memory map into a page table
|
||||
if (!CreatePageTable(map)) {
|
||||
if (!ReserveAddressSpace(&protected_base_) ||
|
||||
!MapPageTable(protected_base_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// map page table to the virtual / protected address spaces
|
||||
if (!MapVirtualSpace()) {
|
||||
return false;
|
||||
// protect dynamic regions in the protected address space
|
||||
for (int page_index = 0; page_index < NUM_PAGES; page_index++) {
|
||||
PageEntry page = pages_[page_index];
|
||||
|
||||
if (RegionTypeIsStatic(page)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t virtual_addr = page_index * PAGE_BLKSIZE;
|
||||
ProtectPages(protected_base_ + virtual_addr, PAGE_BLKSIZE, ACC_NONE);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Memory::Unmap() {
|
||||
UnmapPhysicalSpace();
|
||||
UnmapVirtualSpace();
|
||||
void AddressSpace::Unmap() {
|
||||
UnmapPageTable(base_);
|
||||
UnmapPageTable(protected_base_);
|
||||
}
|
||||
|
||||
bool Memory::GetNextContiguousRegion(uint32_t *physical_start,
|
||||
uint32_t *physical_end) {
|
||||
// find the next lowest region
|
||||
MemoryRegion *lowest = nullptr;
|
||||
uint8_t *AddressSpace::Translate(uint32_t addr) { return base_ + addr; }
|
||||
|
||||
for (int i = 0; i < num_regions_; i++) {
|
||||
MemoryRegion *region = ®ions_[i];
|
||||
|
||||
if (region->physical_addr >= *physical_end &&
|
||||
(!lowest || region->physical_addr <= lowest->physical_addr)) {
|
||||
lowest = region;
|
||||
}
|
||||
}
|
||||
|
||||
// no more regions
|
||||
if (!lowest) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// find the extent of the contiguous region starting at lowest
|
||||
MemoryRegion *highest = lowest;
|
||||
|
||||
for (int i = 0; i < num_regions_; i++) {
|
||||
MemoryRegion *region = ®ions_[i];
|
||||
|
||||
uint32_t highest_end = highest->physical_addr + highest->size;
|
||||
uint32_t region_end = region->physical_addr + region->size;
|
||||
|
||||
if (region->physical_addr <= highest_end && region_end >= highest_end) {
|
||||
highest = region;
|
||||
}
|
||||
}
|
||||
|
||||
*physical_start = lowest->physical_addr;
|
||||
*physical_end = highest->physical_addr + highest->size;
|
||||
|
||||
return true;
|
||||
uint8_t *AddressSpace::TranslateProtected(uint32_t addr) {
|
||||
return protected_base_ + addr;
|
||||
}
|
||||
|
||||
bool Memory::MapPhysicalSpace() {
|
||||
UnmapPhysicalSpace();
|
||||
|
||||
ReserveAddressSpace(&physical_base_);
|
||||
|
||||
// map flattened physical regions into physical address space. it's important
|
||||
// to flatten them for two reasons:
|
||||
// 1.) to have as large of mappings as possible. Windows won't allow you to
|
||||
// map < 64k chunks
|
||||
// 2.) shared memory mappings can't overlap on Windows
|
||||
uint32_t physical_start = 0;
|
||||
uint32_t physical_end = 0;
|
||||
|
||||
while (GetNextContiguousRegion(&physical_start, &physical_end)) {
|
||||
uint32_t physical_size = physical_end - physical_start;
|
||||
|
||||
if (!MapSharedMemory(shmem_, physical_start,
|
||||
physical_base_ + physical_start, physical_size,
|
||||
ACC_READWRITE)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
uint8_t AddressSpace::R8(uint32_t addr) {
|
||||
return ReadBytes<uint8_t, &MemoryRegion::r8>(addr);
|
||||
}
|
||||
|
||||
void Memory::UnmapPhysicalSpace() {
|
||||
if (!physical_base_) {
|
||||
return;
|
||||
}
|
||||
uint16_t AddressSpace::R16(uint32_t addr) {
|
||||
return ReadBytes<uint16_t, &MemoryRegion::r16>(addr);
|
||||
}
|
||||
|
||||
uint32_t physical_start = 0;
|
||||
uint32_t physical_end = 0;
|
||||
uint32_t AddressSpace::R32(uint32_t addr) {
|
||||
return ReadBytes<uint32_t, &MemoryRegion::r32>(addr);
|
||||
}
|
||||
|
||||
while (GetNextContiguousRegion(&physical_start, &physical_end)) {
|
||||
uint32_t physical_size = physical_end - physical_start;
|
||||
uint64_t AddressSpace::R64(uint32_t addr) {
|
||||
return ReadBytes<uint64_t, &MemoryRegion::r64>(addr);
|
||||
}
|
||||
|
||||
UnmapSharedMemory(shmem_, physical_base_ + physical_start, physical_size);
|
||||
void AddressSpace::W8(uint32_t addr, uint8_t value) {
|
||||
WriteBytes<uint8_t, &MemoryRegion::w8>(addr, value);
|
||||
}
|
||||
|
||||
void AddressSpace::W16(uint32_t addr, uint16_t value) {
|
||||
WriteBytes<uint16_t, &MemoryRegion::w16>(addr, value);
|
||||
}
|
||||
|
||||
void AddressSpace::W32(uint32_t addr, uint32_t value) {
|
||||
WriteBytes<uint32_t, &MemoryRegion::w32>(addr, value);
|
||||
}
|
||||
|
||||
void AddressSpace::W64(uint32_t addr, uint64_t value) {
|
||||
WriteBytes<uint64_t, &MemoryRegion::w64>(addr, value);
|
||||
}
|
||||
|
||||
void AddressSpace::Memcpy(uint32_t virtual_dst, const void *ptr,
|
||||
uint32_t size) {
|
||||
CHECK(size % 4 == 0);
|
||||
|
||||
const uint8_t *src = reinterpret_cast<const uint8_t *>(ptr);
|
||||
uint32_t end = virtual_dst + size;
|
||||
while (virtual_dst < end) {
|
||||
W32(virtual_dst, re::load<uint32_t>(src));
|
||||
virtual_dst += 4;
|
||||
src += 4;
|
||||
}
|
||||
}
|
||||
|
||||
int Memory::GetNumAdjacentPhysicalPages(int first_page_index) {
|
||||
int i;
|
||||
void AddressSpace::Memcpy(void *ptr, uint32_t virtual_src, uint32_t size) {
|
||||
CHECK(size % 4 == 0);
|
||||
|
||||
for (i = first_page_index; i < NUM_PAGES - 1; i++) {
|
||||
PageEntry page = pages_[i];
|
||||
PageEntry next_page = pages_[i + 1];
|
||||
|
||||
uint32_t physical_addr = GetPhysicalAddress(page);
|
||||
uint32_t next_physical_addr = GetPhysicalAddress(next_page);
|
||||
|
||||
if ((next_physical_addr - physical_addr) != PAGE_BLKSIZE) {
|
||||
break;
|
||||
}
|
||||
uint8_t *dst = reinterpret_cast<uint8_t *>(ptr);
|
||||
uint8_t *end = dst + size;
|
||||
while (dst < end) {
|
||||
re::store(dst, R32(virtual_src));
|
||||
virtual_src += 4;
|
||||
dst += 4;
|
||||
}
|
||||
|
||||
return (i + 1) - first_page_index;
|
||||
}
|
||||
|
||||
uint32_t Memory::GetPhysicalAddress(const PageEntry &page) {
|
||||
if (IsStaticRegion(page)) {
|
||||
return static_cast<uint32_t>(RegionPointer(page) - physical_base_);
|
||||
}
|
||||
void AddressSpace::Memcpy(uint32_t virtual_dst, uint32_t virtual_src,
|
||||
uint32_t size) {
|
||||
CHECK(size % 4 == 0);
|
||||
|
||||
MemoryRegion ®ion = regions_[RegionIndex(page)];
|
||||
return region.physical_addr + RegionOffset(page);
|
||||
uint32_t end = virtual_dst + size;
|
||||
while (virtual_dst < end) {
|
||||
W32(virtual_dst, R32(virtual_src));
|
||||
virtual_src += 4;
|
||||
virtual_dst += 4;
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate regions in the supplied memory map in the other added, flattening
|
||||
// them out into a virtual page table.
|
||||
bool Memory::CreatePageTable(const MemoryMap &map) {
|
||||
void AddressSpace::Lookup(uint32_t virtual_addr, uint8_t **ptr,
|
||||
MemoryRegion **region, uint32_t *offset) {
|
||||
PageEntry page = pages_[PageIndex(virtual_addr)];
|
||||
|
||||
if (RegionTypeIsStatic(page)) {
|
||||
*ptr = base_ + virtual_addr;
|
||||
} else {
|
||||
*ptr = nullptr;
|
||||
}
|
||||
|
||||
*region = &memory_.regions_[RegionIndex(page)];
|
||||
*offset = RegionOffset(page) + PageOffset(virtual_addr);
|
||||
}
|
||||
|
||||
void AddressSpace::CreatePageTable(const AddressMap &map) {
|
||||
// iterate regions in the supplied memory map in the other added, flattening
|
||||
// them out into a virtual page table
|
||||
for (int i = 0, n = map.num_entries(); i < n; i++) {
|
||||
const MapEntry *entry = map.entry(i);
|
||||
MapEntry *entry = const_cast<MapEntry *>(map.entry(i));
|
||||
|
||||
switch (entry->type) {
|
||||
case MAP_ENTRY_MOUNT: {
|
||||
if (!PageAligned(entry->mount.virtual_addr,
|
||||
entry->mount.virtual_addr + entry->mount.size)) {
|
||||
return false;
|
||||
}
|
||||
CHECK(PageAligned(entry->mount.virtual_addr, entry->mount.size));
|
||||
|
||||
MemoryRegion *region = ®ions_[entry->mount.handle];
|
||||
MemoryRegion ®ion = memory_.regions_[entry->mount.handle];
|
||||
int first_virtual_page = PageIndex(entry->mount.virtual_addr);
|
||||
int num_pages = entry->mount.size >> PAGE_OFFSET_BITS;
|
||||
|
||||
|
@ -463,18 +354,13 @@ bool Memory::CreatePageTable(const MemoryMap &map) {
|
|||
for (int i = 0; i < num_pages; i++) {
|
||||
uint32_t region_offset = i * PAGE_BLKSIZE;
|
||||
|
||||
pages_[first_virtual_page + i] =
|
||||
PackEntry(physical_base_, region, region_offset);
|
||||
pages_[first_virtual_page + i] = PackEntry(region, region_offset);
|
||||
}
|
||||
} break;
|
||||
|
||||
case MAP_ENTRY_MIRROR: {
|
||||
if (!PageAligned(entry->mirror.virtual_addr,
|
||||
entry->mirror.virtual_addr + entry->mirror.size) ||
|
||||
!PageAligned(entry->mirror.physical_addr,
|
||||
entry->mirror.physical_addr + entry->mirror.size)) {
|
||||
return false;
|
||||
}
|
||||
CHECK(PageAligned(entry->mirror.virtual_addr, entry->mirror.size) &&
|
||||
PageAligned(entry->mirror.physical_addr, entry->mirror.size));
|
||||
|
||||
int first_virtual_page = PageIndex(entry->mirror.virtual_addr);
|
||||
int first_physical_page = PageIndex(entry->mirror.physical_addr);
|
||||
|
@ -488,125 +374,110 @@ bool Memory::CreatePageTable(const MemoryMap &map) {
|
|||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t AddressSpace::GetPageOffset(const PageEntry &page) const {
|
||||
const MemoryRegion ®ion = memory_.regions_[RegionIndex(page)];
|
||||
return region.shmem_offset + RegionOffset(page);
|
||||
}
|
||||
|
||||
int AddressSpace::GetNumAdjacentPages(int first_page_index) const {
|
||||
int i;
|
||||
|
||||
for (i = first_page_index; i < NUM_PAGES - 1; i++) {
|
||||
PageEntry page = pages_[i];
|
||||
PageEntry next_page = pages_[i + 1];
|
||||
|
||||
uint32_t page_offset = GetPageOffset(page);
|
||||
uint32_t next_page_offset = GetPageOffset(next_page);
|
||||
|
||||
if ((next_page_offset - page_offset) != PAGE_BLKSIZE) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (i + 1) - first_page_index;
|
||||
}
|
||||
|
||||
bool AddressSpace::MapPageTable(uint8_t *base) {
|
||||
for (int page_index = 0; page_index < NUM_PAGES;) {
|
||||
PageEntry page = pages_[page_index];
|
||||
|
||||
if (!page) {
|
||||
page_index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// batch map djacent pages, mmap is fairly slow
|
||||
int num_pages = GetNumAdjacentPages(page_index);
|
||||
uint32_t size = num_pages * PAGE_BLKSIZE;
|
||||
|
||||
// mmap the virtual address range to the raw address space
|
||||
uint32_t virtual_addr = page_index * PAGE_BLKSIZE;
|
||||
uint32_t page_offset = GetPageOffset(page);
|
||||
|
||||
if (!MapSharedMemory(memory_.shmem_, page_offset, base + virtual_addr, size,
|
||||
ACC_READWRITE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
page_index += num_pages;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Memory::MapVirtualSpace() {
|
||||
UnmapVirtualSpace();
|
||||
|
||||
// map page table to virtual and protected address spaces
|
||||
auto map_virtual = [&](uint8_t **base) {
|
||||
ReserveAddressSpace(base);
|
||||
|
||||
for (int page_index = 0; page_index < NUM_PAGES;) {
|
||||
PageEntry page = pages_[page_index];
|
||||
|
||||
if (!page) {
|
||||
page_index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// batch map virtual pages which map to adjacent physical pages, mmap is
|
||||
// fairly slow
|
||||
int num_pages = GetNumAdjacentPhysicalPages(page_index);
|
||||
uint32_t size = num_pages * PAGE_BLKSIZE;
|
||||
|
||||
// mmap the range in the physical address space to the shared memory
|
||||
// object
|
||||
uint32_t virtual_addr = page_index * PAGE_BLKSIZE;
|
||||
uint32_t physical_addr = GetPhysicalAddress(page);
|
||||
|
||||
if (!MapSharedMemory(shmem_, physical_addr, *base + virtual_addr, size,
|
||||
ACC_READWRITE)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
page_index += num_pages;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!map_virtual(&virtual_base_)) {
|
||||
return false;
|
||||
void AddressSpace::UnmapPageTable(uint8_t *base) {
|
||||
if (!base) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!map_virtual(&protected_base_)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// protect dynamic regions in protected address space
|
||||
for (int page_index = 0; page_index < NUM_PAGES; page_index++) {
|
||||
for (int page_index = 0; page_index < NUM_PAGES;) {
|
||||
PageEntry page = pages_[page_index];
|
||||
|
||||
if (!page || IsStaticRegion(page)) {
|
||||
if (!page) {
|
||||
page_index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t virtual_addr = page_index * PAGE_BLKSIZE;
|
||||
ProtectPages(protected_base_ + virtual_addr, PAGE_BLKSIZE, ACC_NONE);
|
||||
|
||||
int num_pages = GetNumAdjacentPages(page_index);
|
||||
uint32_t size = num_pages * PAGE_BLKSIZE;
|
||||
|
||||
CHECK(UnmapSharedMemory(memory_.shmem_, base + virtual_addr, size));
|
||||
|
||||
page_index += num_pages;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Memory::UnmapVirtualSpace() {
|
||||
auto unmap_virtual = [&](uint8_t *base) {
|
||||
if (!base) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int page_index = 0; page_index < NUM_PAGES;) {
|
||||
PageEntry page = pages_[page_index];
|
||||
|
||||
if (!page) {
|
||||
page_index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t virtual_addr = page_index * PAGE_BLKSIZE;
|
||||
|
||||
int num_pages = GetNumAdjacentPhysicalPages(page_index);
|
||||
uint32_t size = num_pages * PAGE_BLKSIZE;
|
||||
|
||||
CHECK(UnmapSharedMemory(shmem_, base + virtual_addr, size));
|
||||
|
||||
page_index += num_pages;
|
||||
}
|
||||
};
|
||||
|
||||
unmap_virtual(virtual_base_);
|
||||
unmap_virtual(protected_base_);
|
||||
}
|
||||
|
||||
template <typename INT, delegate<INT(uint32_t)> MemoryRegion::*DELEGATE>
|
||||
inline INT Memory::ReadBytes(uint32_t addr) {
|
||||
inline INT AddressSpace::ReadBytes(uint32_t addr) {
|
||||
PageEntry page = pages_[PageIndex(addr)];
|
||||
uint32_t page_offset = PageOffset(addr);
|
||||
DCHECK(page);
|
||||
|
||||
if (IsStaticRegion(page)) {
|
||||
return re::load<INT>(RegionPointer(page) + page_offset);
|
||||
if (RegionTypeIsStatic(page)) {
|
||||
return re::load<INT>(base_ + addr);
|
||||
}
|
||||
|
||||
MemoryRegion ®ion = regions_[RegionIndex(page)];
|
||||
MemoryRegion ®ion = memory_.regions_[RegionIndex(page)];
|
||||
uint32_t region_offset = RegionOffset(page);
|
||||
uint32_t page_offset = PageOffset(addr);
|
||||
return (region.*DELEGATE)(region_offset + page_offset);
|
||||
}
|
||||
|
||||
template <typename INT, delegate<void(uint32_t, INT)> MemoryRegion::*DELEGATE>
|
||||
inline void Memory::WriteBytes(uint32_t addr, INT value) {
|
||||
inline void AddressSpace::WriteBytes(uint32_t addr, INT value) {
|
||||
PageEntry page = pages_[PageIndex(addr)];
|
||||
uint32_t page_offset = PageOffset(addr);
|
||||
DCHECK(page);
|
||||
|
||||
if (IsStaticRegion(page)) {
|
||||
re::store(RegionPointer(page) + page_offset, value);
|
||||
if (RegionTypeIsStatic(page)) {
|
||||
re::store(base_ + addr, value);
|
||||
return;
|
||||
}
|
||||
|
||||
MemoryRegion ®ion = regions_[RegionIndex(page)];
|
||||
MemoryRegion ®ion = memory_.regions_[RegionIndex(page)];
|
||||
uint32_t region_offset = RegionOffset(page);
|
||||
uint32_t page_offset = PageOffset(addr);
|
||||
(region.*DELEGATE)(region_offset + page_offset, value);
|
||||
}
|
||||
|
|
310
src/hw/memory.h
310
src/hw/memory.h
|
@ -1,6 +1,7 @@
|
|||
#ifndef MEMORY_H
|
||||
#define MEMORY_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include "core/delegate.h"
|
||||
#include "sys/exception_handler.h"
|
||||
#include "sys/memory.h"
|
||||
|
@ -8,16 +9,138 @@
|
|||
namespace re {
|
||||
namespace hw {
|
||||
|
||||
class AddressMap;
|
||||
class Machine;
|
||||
class Memory;
|
||||
|
||||
typedef int MapEntryHandle;
|
||||
typedef uint8_t RegionHandle;
|
||||
typedef uintptr_t PageEntry;
|
||||
|
||||
enum {
|
||||
MAX_REGIONS = (1 << (sizeof(RegionHandle) * 8)),
|
||||
typedef delegate<uint8_t(uint32_t)> R8Delegate;
|
||||
typedef delegate<uint16_t(uint32_t)> R16Delegate;
|
||||
typedef delegate<uint32_t(uint32_t)> R32Delegate;
|
||||
typedef delegate<uint64_t(uint32_t)> R64Delegate;
|
||||
|
||||
typedef delegate<void(uint32_t, uint8_t)> W8Delegate;
|
||||
typedef delegate<void(uint32_t, uint16_t)> W16Delegate;
|
||||
typedef delegate<void(uint32_t, uint32_t)> W32Delegate;
|
||||
typedef delegate<void(uint32_t, uint64_t)> W64Delegate;
|
||||
|
||||
static const uint64_t ADDRESS_SPACE_SIZE = 1ull << 32;
|
||||
|
||||
// helpers for extracting page information out of a virtual address
|
||||
static const int PAGE_BITS = 20;
|
||||
static const int PAGE_OFFSET_BITS = 32 - PAGE_BITS;
|
||||
static const int PAGE_BLKSIZE = 1 << PAGE_OFFSET_BITS;
|
||||
static const int NUM_PAGES = 1 << PAGE_BITS;
|
||||
static const uint32_t PAGE_OFFSET_MASK = PAGE_BLKSIZE - 1;
|
||||
static const uint32_t PAGE_INDEX_MASK = ~PAGE_OFFSET_MASK;
|
||||
|
||||
// helpers for accessing region information out of a page table entry
|
||||
static const int MAX_REGIONS = 1 << (PAGE_OFFSET_BITS - 1);
|
||||
static const uintptr_t REGION_INDEX_MASK = MAX_REGIONS - 1;
|
||||
static const uintptr_t REGION_TYPE_MASK = MAX_REGIONS;
|
||||
static const uintptr_t REGION_OFFSET_MASK =
|
||||
~(REGION_TYPE_MASK | REGION_INDEX_MASK);
|
||||
|
||||
struct MemoryRegion {
|
||||
RegionHandle handle;
|
||||
uint32_t shmem_offset;
|
||||
uint32_t size;
|
||||
bool dynamic;
|
||||
|
||||
R8Delegate r8;
|
||||
R16Delegate r16;
|
||||
R32Delegate r32;
|
||||
R64Delegate r64;
|
||||
W8Delegate w8;
|
||||
W16Delegate w16;
|
||||
W32Delegate w32;
|
||||
W64Delegate w64;
|
||||
};
|
||||
|
||||
// Establish a mapping of physical memory regions to a virtual address space.
|
||||
typedef int MapEntryHandle;
|
||||
class Memory {
|
||||
friend class AddressSpace;
|
||||
|
||||
public:
|
||||
Memory(Machine &machine);
|
||||
~Memory();
|
||||
|
||||
bool Init();
|
||||
|
||||
RegionHandle CreateRegion(uint32_t size);
|
||||
RegionHandle CreateRegion(uint32_t size, R8Delegate r8, R16Delegate r16,
|
||||
R32Delegate r32, R64Delegate r64, W8Delegate w8,
|
||||
W16Delegate w16, W32Delegate w32, W64Delegate w64);
|
||||
|
||||
private:
|
||||
bool CreateSharedMemory();
|
||||
void DestroySharedMemory();
|
||||
|
||||
MemoryRegion &AllocRegion(uint32_t size);
|
||||
|
||||
Machine &machine_;
|
||||
sys::SharedMemoryHandle shmem_;
|
||||
uint32_t shmem_size_;
|
||||
|
||||
MemoryRegion regions_[MAX_REGIONS];
|
||||
int num_regions_;
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
|
||||
// macros to help generate static AddressMap creators
|
||||
#define AM_DECLARE(name) \
|
||||
static void name(void *, Machine &, AddressMap &, uint32_t);
|
||||
|
||||
#define AM_BEGIN(type, name) \
|
||||
void type::name(void *that, Machine &machine, AddressMap &map, uint32_t offset) { \
|
||||
type *self = static_cast<type *>(that); \
|
||||
Memory *memory = machine.memory(); \
|
||||
uint32_t begin_addr = 0; \
|
||||
uint32_t end_addr = 0; \
|
||||
uint32_t size = 0; \
|
||||
RegionHandle region = 0; \
|
||||
Device *device = nullptr; \
|
||||
(void)self; \
|
||||
(void)memory; \
|
||||
(void)begin_addr; \
|
||||
(void)end_addr; \
|
||||
(void)size; \
|
||||
(void)region; \
|
||||
(void)device;
|
||||
#define AM_RANGE(begin, end) \
|
||||
begin_addr = offset + begin; \
|
||||
end_addr = offset + end; \
|
||||
size = end_addr - begin_addr + 1;
|
||||
#define AM_MOUNT() \
|
||||
region = memory->CreateRegion(size); \
|
||||
map.Mount(region, size, begin_addr); \
|
||||
region = 0;
|
||||
#define AM_HANDLE(r8_, r16_, r32_, r64_, w8_, w16_, w32_, w64_) \
|
||||
region = memory->CreateRegion(size, \
|
||||
delegate<uint8_t(uint32_t)>(self, r8_), \
|
||||
delegate<uint16_t(uint32_t)>(self, r16_), \
|
||||
delegate<uint32_t(uint32_t)>(self, r32_), \
|
||||
delegate<uint64_t(uint32_t)>(self, r64_), \
|
||||
delegate<void(uint32_t, uint8_t)>(self, w8_) , \
|
||||
delegate<void(uint32_t, uint16_t)>(self, w16_), \
|
||||
delegate<void(uint32_t, uint32_t)>(self, w32_), \
|
||||
delegate<void(uint32_t, uint64_t)>(self, w64_)); \
|
||||
map.Mount(region, size, begin_addr); \
|
||||
region = 0;
|
||||
#define AM_DEVICE(name, type, cb) \
|
||||
device = machine.LookupDevice(name); \
|
||||
CHECK_NOTNULL(device); \
|
||||
type::cb(device, machine, map, begin_addr); \
|
||||
device = nullptr;
|
||||
#define AM_MIRROR(addr) \
|
||||
map.Mirror(addr, size, begin_addr);
|
||||
#define AM_END() \
|
||||
}
|
||||
|
||||
// clang-format on
|
||||
|
||||
enum MapEntryType {
|
||||
MAP_ENTRY_MOUNT,
|
||||
|
@ -25,7 +148,6 @@ enum MapEntryType {
|
|||
};
|
||||
|
||||
struct MapEntry {
|
||||
MapEntryHandle handle;
|
||||
MapEntryType type;
|
||||
|
||||
union {
|
||||
|
@ -43,135 +165,48 @@ struct MapEntry {
|
|||
};
|
||||
};
|
||||
|
||||
class MemoryMap {
|
||||
typedef void (*AddressMapper)(void *, Machine &, AddressMap &, uint32_t);
|
||||
|
||||
class AddressMap {
|
||||
public:
|
||||
MemoryMap();
|
||||
AddressMap();
|
||||
|
||||
const MapEntry *entry(int i) const { return &entries_[i]; }
|
||||
int num_entries() const { return num_entries_; }
|
||||
|
||||
// physical regions can be allocated through Memory::AllocRegion, and the
|
||||
// returned handle mounted here
|
||||
void Mount(RegionHandle handle, uint32_t virtual_addr, uint32_t size);
|
||||
|
||||
// mirror arbitary, page-aligned ranges of memory to a virtual address
|
||||
void Mount(RegionHandle handle, uint32_t size, uint32_t virtual_addr);
|
||||
void Mirror(uint32_t physical_addr, uint32_t size, uint32_t virtual_addr);
|
||||
|
||||
private:
|
||||
MapEntry *AllocEntry();
|
||||
MapEntry &AllocEntry();
|
||||
|
||||
MapEntry entries_[MAX_REGIONS];
|
||||
int num_entries_;
|
||||
};
|
||||
|
||||
// The memory system reserves a 4gb aligned chunk of memory in which the entire
|
||||
// 32-bit guest address space is subdivided into aligned pages.
|
||||
//
|
||||
// Regions of memory can be allocated inside the 32-bit address space with
|
||||
// Memory::AllocRegion. Allocated regions are not immediately available for
|
||||
// access, but can be mounted with AddressMap::Mount / AddressMap::Mirror and
|
||||
// Memory::MapRegions to bring the region into the accessible address space.
|
||||
//
|
||||
// Each page entry in the page table is represented by a uintptr_t value. In
|
||||
// the case of static pages which map directly to host ram, the value is just a
|
||||
// pointer to the start of the aligned page in host memory. In the case that
|
||||
// the page doesn't map directly to host ram, the low order offset bits are
|
||||
// set to denote that the page is a dynamic page. For these pages, the entry
|
||||
// is a packed bitstring of the format:
|
||||
// oooooooooooooooooooooooooooooooooooooooooooooooooooooooorrrrrrrr
|
||||
// 'o' represents an offset into the region backing the page and 'r' represents
|
||||
// the handle of the region backing the page.
|
||||
//
|
||||
// NOTE as a side effect of this, since static pages are a host pointer, and
|
||||
// dynamic pages have a 1-indexed region handle encoded in them, valid page
|
||||
// entries are always non-zero
|
||||
typedef uintptr_t PageEntry;
|
||||
|
||||
typedef delegate<uint8_t(uint32_t)> R8Delegate;
|
||||
typedef delegate<uint16_t(uint32_t)> R16Delegate;
|
||||
typedef delegate<uint32_t(uint32_t)> R32Delegate;
|
||||
typedef delegate<uint64_t(uint32_t)> R64Delegate;
|
||||
|
||||
typedef delegate<void(uint32_t, uint8_t)> W8Delegate;
|
||||
typedef delegate<void(uint32_t, uint16_t)> W16Delegate;
|
||||
typedef delegate<void(uint32_t, uint32_t)> W32Delegate;
|
||||
typedef delegate<void(uint32_t, uint64_t)> W64Delegate;
|
||||
|
||||
enum {
|
||||
PAGE_BITS = 20,
|
||||
PAGE_OFFSET_BITS = 32 - PAGE_BITS,
|
||||
PAGE_BLKSIZE = 1 << PAGE_OFFSET_BITS,
|
||||
NUM_PAGES = 1 << PAGE_BITS
|
||||
};
|
||||
|
||||
enum : uint64_t {
|
||||
ADDRESS_SPACE_SIZE = (1ull << 32),
|
||||
};
|
||||
|
||||
enum : uint32_t {
|
||||
PAGE_INDEX_MASK =
|
||||
static_cast<uint32_t>(((1 << PAGE_BITS) - 1) << PAGE_OFFSET_BITS),
|
||||
PAGE_OFFSET_MASK = (1 << PAGE_OFFSET_BITS) - 1,
|
||||
};
|
||||
|
||||
enum : PageEntry {
|
||||
PAGE_REGION_MASK = MAX_REGIONS - 1,
|
||||
PAGE_REGION_OFFSET_MASK = ~PAGE_REGION_MASK
|
||||
};
|
||||
|
||||
// For static page entries, the entry itself is a page-aligned pointer to host
|
||||
// memory. Being that the pointer is aligned, the low order offset bits are
|
||||
// unset. This is exploited, and a bit is set in these low order bits to
|
||||
// differentiate dynamic entries. Make sure that future page size adjustments
|
||||
// don't shift this flag out of the offset bits, and into the page index bits.
|
||||
static_assert((PAGE_INDEX_MASK & PAGE_REGION_MASK) == 0,
|
||||
"Dynamic bit intersects page index bits.");
|
||||
|
||||
struct MemoryRegion {
|
||||
RegionHandle handle;
|
||||
bool dynamic;
|
||||
uint32_t physical_addr;
|
||||
uint32_t size;
|
||||
void *ctx;
|
||||
|
||||
R8Delegate r8;
|
||||
R16Delegate r16;
|
||||
R32Delegate r32;
|
||||
R64Delegate r64;
|
||||
W8Delegate w8;
|
||||
W16Delegate w16;
|
||||
W32Delegate w32;
|
||||
W64Delegate w64;
|
||||
};
|
||||
|
||||
class Memory {
|
||||
class AddressSpace {
|
||||
public:
|
||||
static uint8_t R8(void *memory, uint32_t addr);
|
||||
static uint16_t R16(void *memory, uint32_t addr);
|
||||
static uint32_t R32(void *memory, uint32_t addr);
|
||||
static uint64_t R64(void *memory, uint32_t addr);
|
||||
static void W8(void *memory, uint32_t addr, uint8_t value);
|
||||
static void W16(void *memory, uint32_t addr, uint16_t value);
|
||||
static void W32(void *memory, uint32_t addr, uint32_t value);
|
||||
static void W64(void *memory, uint32_t addr, uint64_t value);
|
||||
static uint8_t R8(void *space, uint32_t addr);
|
||||
static uint16_t R16(void *space, uint32_t addr);
|
||||
static uint32_t R32(void *space, uint32_t addr);
|
||||
static uint64_t R64(void *space, uint32_t addr);
|
||||
static void W8(void *space, uint32_t addr, uint8_t value);
|
||||
static void W16(void *space, uint32_t addr, uint16_t value);
|
||||
static void W32(void *space, uint32_t addr, uint32_t value);
|
||||
static void W64(void *space, uint32_t addr, uint64_t value);
|
||||
|
||||
Memory(Machine &machine);
|
||||
~Memory();
|
||||
AddressSpace(Memory &memory);
|
||||
~AddressSpace();
|
||||
|
||||
size_t total_size() { return ADDRESS_SPACE_SIZE; }
|
||||
uint8_t *virtual_base() { return virtual_base_; }
|
||||
uint8_t *base() { return base_; }
|
||||
uint8_t *protected_base() { return protected_base_; }
|
||||
|
||||
bool Init();
|
||||
bool Map(const AddressMap &map);
|
||||
void Unmap();
|
||||
|
||||
RegionHandle AllocRegion(uint32_t physical_addr, uint32_t size);
|
||||
RegionHandle AllocRegion(uint32_t physical_addr, uint32_t size, R8Delegate r8,
|
||||
R16Delegate r16, R32Delegate r32, R64Delegate r64,
|
||||
W8Delegate w8, W16Delegate w16, W32Delegate w32,
|
||||
W64Delegate w64);
|
||||
|
||||
uint8_t *TranslateVirtual(uint32_t addr);
|
||||
uint8_t *Translate(uint32_t addr);
|
||||
uint8_t *TranslateProtected(uint32_t addr);
|
||||
|
||||
uint8_t R8(uint32_t addr);
|
||||
uint16_t R16(uint32_t addr);
|
||||
uint32_t R32(uint32_t addr);
|
||||
|
@ -180,6 +215,7 @@ class Memory {
|
|||
void W16(uint32_t addr, uint16_t value);
|
||||
void W32(uint32_t addr, uint32_t value);
|
||||
void W64(uint32_t addr, uint64_t value);
|
||||
|
||||
void Memcpy(uint32_t virtual_dest, const void *ptr, uint32_t size);
|
||||
void Memcpy(void *ptr, uint32_t virtual_src, uint32_t size);
|
||||
void Memcpy(uint32_t virtual_dest, uint32_t virtual_src, uint32_t size);
|
||||
|
@ -187,50 +223,20 @@ class Memory {
|
|||
uint32_t *offset);
|
||||
|
||||
private:
|
||||
bool CreateSharedMemory();
|
||||
void DestroySharedMemory();
|
||||
|
||||
bool ReserveAddressSpace(uint8_t **base);
|
||||
|
||||
MemoryRegion *AllocRegion();
|
||||
|
||||
bool Map(const MemoryMap &map);
|
||||
void Unmap();
|
||||
|
||||
bool GetNextContiguousRegion(uint32_t *physical_start,
|
||||
uint32_t *physical_end);
|
||||
bool MapPhysicalSpace();
|
||||
void UnmapPhysicalSpace();
|
||||
|
||||
int GetNumAdjacentPhysicalPages(int page_index);
|
||||
uint32_t GetPhysicalAddress(const PageEntry &page);
|
||||
bool CreatePageTable(const MemoryMap &map);
|
||||
bool MapVirtualSpace();
|
||||
void UnmapVirtualSpace();
|
||||
void CreatePageTable(const AddressMap &map);
|
||||
uint32_t GetPageOffset(const PageEntry &page) const;
|
||||
int GetNumAdjacentPages(int page_index) const;
|
||||
bool MapPageTable(uint8_t *base);
|
||||
void UnmapPageTable(uint8_t *base);
|
||||
|
||||
template <typename INT, delegate<INT(uint32_t)> MemoryRegion::*DELEGATE>
|
||||
INT ReadBytes(uint32_t addr);
|
||||
template <typename INT, delegate<void(uint32_t, INT)> MemoryRegion::*DELEGATE>
|
||||
void WriteBytes(uint32_t addr, INT value);
|
||||
|
||||
Machine &machine_;
|
||||
|
||||
// shared memory object where all physical data is written to
|
||||
sys::SharedMemoryHandle shmem_;
|
||||
|
||||
// physical regions of memory
|
||||
int num_regions_;
|
||||
MemoryRegion *regions_;
|
||||
|
||||
// map virtual addresses -> physical addresses
|
||||
PageEntry *pages_;
|
||||
|
||||
// base addresses for the 32-bit address space. physical base is where
|
||||
// the physical regions are mapped, virtual base is where the page table
|
||||
// gets mapped to, and protected base is the same as virtual, but with
|
||||
// dynamic regions mprotected so they segfaul on access
|
||||
uint8_t *physical_base_;
|
||||
uint8_t *virtual_base_;
|
||||
Memory &memory_;
|
||||
PageEntry pages_[NUM_PAGES];
|
||||
uint8_t *base_;
|
||||
uint8_t *protected_base_;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
#ifndef REGIONS_H
|
||||
#define REGIONS_H
|
||||
|
||||
namespace re {
|
||||
namespace hw {
|
||||
|
||||
#define MEMORY_REGION(name, start, end) \
|
||||
name##_BEGIN = start, name##_END = end, name##_SIZE = end - start + 1
|
||||
|
||||
// clang-format off
|
||||
enum {
|
||||
MEMORY_REGION(AREA0, 0x00000000, 0x03ffffff),
|
||||
MEMORY_REGION(BIOS, 0x00000000, 0x001fffff),
|
||||
MEMORY_REGION(FLASH, 0x00200000, 0x0021ffff),
|
||||
MEMORY_REGION(HOLLY_REG, 0x005f6000, 0x005f7fff),
|
||||
MEMORY_REGION(PVR_REG, 0x005f8000, 0x005f8fff),
|
||||
MEMORY_REGION(PVR_PALETTE, 0x005f9000, 0x005f9fff),
|
||||
MEMORY_REGION(MODEM_REG, 0x00600000, 0x0067ffff),
|
||||
MEMORY_REGION(AICA_REG, 0x00700000, 0x00710fff),
|
||||
MEMORY_REGION(WAVE_RAM, 0x00800000, 0x009fffff),
|
||||
MEMORY_REGION(EXPDEV, 0x01000000, 0x01ffffff),
|
||||
|
||||
MEMORY_REGION(AREA1, 0x04000000, 0x07ffffff),
|
||||
MEMORY_REGION(PVR_VRAM32, 0x04000000, 0x047fffff),
|
||||
MEMORY_REGION(PVR_VRAM64, 0x05000000, 0x057fffff),
|
||||
|
||||
MEMORY_REGION(AREA2, 0x08000000, 0x0bffffff),
|
||||
|
||||
MEMORY_REGION(AREA3, 0x0c000000, 0x0cffffff), // 16 mb ram, mirrored 4x
|
||||
MEMORY_REGION(MAIN_RAM_1, 0x0c000000, 0x0cffffff),
|
||||
MEMORY_REGION(MAIN_RAM_2, 0x0d000000, 0x0dffffff),
|
||||
MEMORY_REGION(MAIN_RAM_3, 0x0e000000, 0x0effffff),
|
||||
MEMORY_REGION(MAIN_RAM_4, 0x0f000000, 0x0fffffff),
|
||||
|
||||
MEMORY_REGION(AREA4, 0x10000000, 0x13ffffff),
|
||||
MEMORY_REGION(TA_POLY, 0x10000000, 0x107fffff),
|
||||
MEMORY_REGION(TA_TEXTURE, 0x11000000, 0x11ffffff),
|
||||
|
||||
MEMORY_REGION(AREA5, 0x14000000, 0x17ffffff),
|
||||
MEMORY_REGION(MODEM, 0x14000000, 0x17ffffff),
|
||||
|
||||
MEMORY_REGION(AREA6, 0x18000000, 0x1bffffff),
|
||||
MEMORY_REGION(UNASSIGNED, 0x18000000, 0x1bffffff),
|
||||
|
||||
MEMORY_REGION(AREA7, 0x1c000000, 0x1fffffff),
|
||||
MEMORY_REGION(SH4_REG, 0x1e000000, 0x1fffffff),
|
||||
MEMORY_REGION(SH4_CACHE, 0x7c000000, 0x7fffffff),
|
||||
MEMORY_REGION(SH4_SQ, 0xe0000000, 0xe3ffffff),
|
||||
|
||||
MEMORY_REGION(P0_1, 0x00000000, 0x1fffffff),
|
||||
MEMORY_REGION(P0_2, 0x20000000, 0x3fffffff),
|
||||
MEMORY_REGION(P0_3, 0x40000000, 0x5fffffff),
|
||||
MEMORY_REGION(P0_4, 0x60000000, 0x7fffffff),
|
||||
MEMORY_REGION(P1, 0x80000000, 0x9fffffff),
|
||||
MEMORY_REGION(P2, 0xa0000000, 0xbfffffff),
|
||||
MEMORY_REGION(P3, 0xc0000000, 0xdfffffff),
|
||||
MEMORY_REGION(P4, 0xe0000000, 0xffffffff),
|
||||
};
|
||||
// clang-format on
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -17,6 +17,12 @@ struct Register;
|
|||
typedef delegate<uint32_t(Register &)> RegisterReadDelegate;
|
||||
typedef delegate<void(Register &, uint32_t)> RegisterWriteDelegate;
|
||||
|
||||
#define DECLARE_R32_DELEGATE(name) uint32_t name##_r(Register &)
|
||||
#define DECLARE_W32_DELEGATE(name) void name##_w(Register &, uint32_t)
|
||||
|
||||
#define R32_DELEGATE(name) uint32_t name##_r(Register ®)
|
||||
#define W32_DELEGATE(name) void name##_w(Register ®, uint32_t old_value)
|
||||
|
||||
struct Register {
|
||||
Register() : flags(RW), value(0) {}
|
||||
Register(uint8_t flags, uint32_t value) : flags(flags), value(value) {}
|
||||
|
|
|
@ -29,7 +29,7 @@ void Scheduler::Tick(const std::chrono::nanoseconds &delta) {
|
|||
auto slice = next_time - base_time_;
|
||||
base_time_ += slice;
|
||||
|
||||
for (auto device : machine_.devices) {
|
||||
for (auto device : machine_.devices()) {
|
||||
ExecuteInterface *execute = device->execute();
|
||||
|
||||
if (!execute || execute->suspended()) {
|
||||
|
|
|
@ -8,8 +8,15 @@
|
|||
#include "hw/memory.h"
|
||||
#include "hw/scheduler.h"
|
||||
|
||||
#include "hw/aica/aica.h"
|
||||
#include "hw/holly/holly.h"
|
||||
#include "hw/holly/pvr2.h"
|
||||
#include "hw/holly/tile_accelerator.h"
|
||||
|
||||
using namespace re;
|
||||
using namespace re::hw;
|
||||
using namespace re::hw::aica;
|
||||
using namespace re::hw::holly;
|
||||
using namespace re::hw::sh4;
|
||||
using namespace re::jit;
|
||||
using namespace re::jit::backend;
|
||||
|
@ -24,20 +31,83 @@ static InterruptInfo interrupts[NUM_INTERRUPTS] = {
|
|||
#undef SH4_INT
|
||||
};
|
||||
|
||||
static SH4 *s_current_cpu = nullptr;
|
||||
// clang-format off
|
||||
AM_BEGIN(SH4, data_map)
|
||||
AM_RANGE(0x00000000, 0x03ffffff) AM_MOUNT() // area 0
|
||||
AM_RANGE(0x04000000, 0x07ffffff) AM_MOUNT() // area 1
|
||||
AM_RANGE(0x08000000, 0x0bffffff) AM_MOUNT() // area 2
|
||||
AM_RANGE(0x0c000000, 0x0cffffff) AM_MOUNT() // area 3
|
||||
AM_RANGE(0x10000000, 0x13ffffff) AM_MOUNT() // area 4
|
||||
AM_RANGE(0x14000000, 0x17ffffff) AM_MOUNT() // area 5
|
||||
AM_RANGE(0x18000000, 0x1bffffff) AM_MOUNT() // area 6
|
||||
AM_RANGE(0x1c000000, 0x1fffffff) AM_MOUNT() // area 7
|
||||
|
||||
// main ram mirrors
|
||||
AM_RANGE(0x0d000000, 0x0dffffff) AM_MIRROR(0x0c000000)
|
||||
AM_RANGE(0x0e000000, 0x0effffff) AM_MIRROR(0x0c000000)
|
||||
AM_RANGE(0x0f000000, 0x0fffffff) AM_MIRROR(0x0c000000)
|
||||
|
||||
// external devices
|
||||
AM_RANGE(0x005f6000, 0x005f7fff) AM_DEVICE("holly", Holly, reg_map)
|
||||
AM_RANGE(0x005f8000, 0x005f9fff) AM_DEVICE("pvr", PVR2, reg_map)
|
||||
AM_RANGE(0x00700000, 0x00710fff) AM_DEVICE("aica", AICA, reg_map)
|
||||
AM_RANGE(0x00800000, 0x009fffff) AM_DEVICE("aica", AICA, data_map)
|
||||
AM_RANGE(0x04000000, 0x057fffff) AM_DEVICE("pvr", PVR2, vram_map)
|
||||
AM_RANGE(0x10000000, 0x11ffffff) AM_DEVICE("ta", TileAccelerator, fifo_map)
|
||||
|
||||
// internal registers
|
||||
AM_RANGE(0x1e000000, 0x1fffffff) AM_HANDLE(&SH4::ReadRegister<uint8_t>,
|
||||
&SH4::ReadRegister<uint16_t>,
|
||||
&SH4::ReadRegister<uint32_t>,
|
||||
nullptr,
|
||||
&SH4::WriteRegister<uint8_t>,
|
||||
&SH4::WriteRegister<uint16_t>,
|
||||
&SH4::WriteRegister<uint32_t>,
|
||||
nullptr)
|
||||
|
||||
// physical mirrors
|
||||
AM_RANGE(0x20000000, 0x3fffffff) AM_MIRROR(0x00000000) // p0
|
||||
AM_RANGE(0x40000000, 0x5fffffff) AM_MIRROR(0x00000000) // p0
|
||||
AM_RANGE(0x60000000, 0x7fffffff) AM_MIRROR(0x00000000) // p0
|
||||
AM_RANGE(0x80000000, 0x9fffffff) AM_MIRROR(0x00000000) // p1
|
||||
AM_RANGE(0xa0000000, 0xbfffffff) AM_MIRROR(0x00000000) // p2
|
||||
AM_RANGE(0xc0000000, 0xdfffffff) AM_MIRROR(0x00000000) // p3
|
||||
AM_RANGE(0xe0000000, 0xffffffff) AM_MIRROR(0x00000000) // p4
|
||||
|
||||
// internal cache and sq only accessible through p4
|
||||
AM_RANGE(0x7c000000, 0x7fffffff) AM_HANDLE(&SH4::ReadCache<uint8_t>,
|
||||
&SH4::ReadCache<uint16_t>,
|
||||
&SH4::ReadCache<uint32_t>,
|
||||
&SH4::ReadCache<uint64_t>,
|
||||
&SH4::WriteCache<uint8_t>,
|
||||
&SH4::WriteCache<uint16_t>,
|
||||
&SH4::WriteCache<uint32_t>,
|
||||
&SH4::WriteCache<uint64_t>)
|
||||
|
||||
AM_RANGE(0xe0000000, 0xe3ffffff) AM_HANDLE(&SH4::ReadSQ<uint8_t>,
|
||||
&SH4::ReadSQ<uint16_t>,
|
||||
&SH4::ReadSQ<uint32_t>,
|
||||
nullptr,
|
||||
&SH4::WriteSQ<uint8_t>,
|
||||
&SH4::WriteSQ<uint16_t>,
|
||||
&SH4::WriteSQ<uint32_t>,
|
||||
nullptr)
|
||||
AM_END()
|
||||
// clang-format on
|
||||
|
||||
static SH4 *s_current_cpu = nullptr;
|
||||
|
||||
enum {
|
||||
SH4_CLOCK_FREQ = 200000000,
|
||||
};
|
||||
|
||||
SH4::SH4(Dreamcast &dc)
|
||||
: Device(dc),
|
||||
: Device(dc, "sh4"),
|
||||
DebugInterface(this),
|
||||
ExecuteInterface(this),
|
||||
MemoryInterface(this),
|
||||
MemoryInterface(this, data_map),
|
||||
WindowInterface(this),
|
||||
dc_(dc),
|
||||
memory_(nullptr),
|
||||
scheduler_(nullptr),
|
||||
code_cache_(nullptr),
|
||||
regs_(),
|
||||
|
@ -55,14 +125,15 @@ SH4::SH4(Dreamcast &dc)
|
|||
SH4::~SH4() { delete code_cache_; }
|
||||
|
||||
bool SH4::Init() {
|
||||
memory_ = dc_.memory;
|
||||
scheduler_ = dc_.scheduler;
|
||||
scheduler_ = dc_.scheduler();
|
||||
|
||||
code_cache_ =
|
||||
new SH4CodeCache({&ctx_, memory_->protected_base(), memory_, &Memory::R8,
|
||||
&Memory::R16, &Memory::R32, &Memory::R64, &Memory::W8,
|
||||
&Memory::W16, &Memory::W32, &Memory::W64},
|
||||
&SH4::CompilePC);
|
||||
// setup code cache
|
||||
code_cache_ = new SH4CodeCache(
|
||||
{&ctx_, space_.protected_base(), &space_, &AddressSpace::R8,
|
||||
&AddressSpace::R16, &AddressSpace::R32, &AddressSpace::R64,
|
||||
&AddressSpace::W8, &AddressSpace::W16, &AddressSpace::W32,
|
||||
&AddressSpace::W64},
|
||||
&SH4::CompilePC);
|
||||
|
||||
// initialize context
|
||||
memset(&ctx_, 0, sizeof(ctx_));
|
||||
|
@ -85,30 +156,35 @@ bool SH4::Init() {
|
|||
if (default != HELD) { \
|
||||
regs_[name##_OFFSET] = {flags, default}; \
|
||||
}
|
||||
#define SH4_REG_R32(name) \
|
||||
regs_[name##_OFFSET].read = make_delegate(&SH4::name##_r, this);
|
||||
#define SH4_REG_W32(name) \
|
||||
regs_[name##_OFFSET].write = make_delegate(&SH4::name##_w, this);
|
||||
#include "hw/sh4/sh4_regs.inc"
|
||||
SH4_REG_R32(PDTRA);
|
||||
SH4_REG_W32(MMUCR);
|
||||
SH4_REG_W32(CCR);
|
||||
SH4_REG_W32(CHCR0);
|
||||
SH4_REG_W32(CHCR1);
|
||||
SH4_REG_W32(CHCR2);
|
||||
SH4_REG_W32(CHCR3);
|
||||
SH4_REG_W32(DMAOR);
|
||||
SH4_REG_W32(IPRA);
|
||||
SH4_REG_W32(IPRB);
|
||||
SH4_REG_W32(IPRC);
|
||||
SH4_REG_W32(TSTR);
|
||||
SH4_REG_W32(TCR0);
|
||||
SH4_REG_W32(TCR1);
|
||||
SH4_REG_W32(TCR2);
|
||||
SH4_REG_R32(TCNT0);
|
||||
SH4_REG_W32(TCNT0);
|
||||
SH4_REG_R32(TCNT1);
|
||||
SH4_REG_W32(TCNT1);
|
||||
SH4_REG_R32(TCNT2);
|
||||
SH4_REG_W32(TCNT2);
|
||||
#undef SH4_REG
|
||||
|
||||
SH4_REGISTER_R32_DELEGATE(PDTRA);
|
||||
SH4_REGISTER_W32_DELEGATE(MMUCR);
|
||||
SH4_REGISTER_W32_DELEGATE(CCR);
|
||||
SH4_REGISTER_W32_DELEGATE(CHCR0);
|
||||
SH4_REGISTER_W32_DELEGATE(CHCR1);
|
||||
SH4_REGISTER_W32_DELEGATE(CHCR2);
|
||||
SH4_REGISTER_W32_DELEGATE(CHCR3);
|
||||
SH4_REGISTER_W32_DELEGATE(DMAOR);
|
||||
SH4_REGISTER_W32_DELEGATE(IPRA);
|
||||
SH4_REGISTER_W32_DELEGATE(IPRB);
|
||||
SH4_REGISTER_W32_DELEGATE(IPRC);
|
||||
SH4_REGISTER_W32_DELEGATE(TSTR);
|
||||
SH4_REGISTER_W32_DELEGATE(TCR0);
|
||||
SH4_REGISTER_W32_DELEGATE(TCR1);
|
||||
SH4_REGISTER_W32_DELEGATE(TCR2);
|
||||
SH4_REGISTER_R32_DELEGATE(TCNT0);
|
||||
SH4_REGISTER_W32_DELEGATE(TCNT0);
|
||||
SH4_REGISTER_R32_DELEGATE(TCNT1);
|
||||
SH4_REGISTER_W32_DELEGATE(TCNT1);
|
||||
SH4_REGISTER_R32_DELEGATE(TCNT2);
|
||||
SH4_REGISTER_W32_DELEGATE(TCNT2);
|
||||
#undef SH4_REG_R32
|
||||
#undef SH4_REG_W32
|
||||
|
||||
// reset interrupts
|
||||
ReprioritizeInterrupts();
|
||||
|
@ -159,9 +235,9 @@ void SH4::DDT(const DTR &dtr) {
|
|||
if (dtr.data) {
|
||||
// single address mode transfer
|
||||
if (dtr.rw) {
|
||||
memory_->Memcpy(dtr.addr, dtr.data, dtr.size);
|
||||
space_.Memcpy(dtr.addr, dtr.data, dtr.size);
|
||||
} else {
|
||||
memory_->Memcpy(dtr.data, dtr.addr, dtr.size);
|
||||
space_.Memcpy(dtr.data, dtr.addr, dtr.size);
|
||||
}
|
||||
} else {
|
||||
// dual address mode transfer
|
||||
|
@ -210,7 +286,7 @@ void SH4::DDT(const DTR &dtr) {
|
|||
uint32_t src = dtr.rw ? dtr.addr : *sar;
|
||||
uint32_t dst = dtr.rw ? *dar : dtr.addr;
|
||||
int size = *dmatcr * 32;
|
||||
memory_->Memcpy(dst, src, size);
|
||||
space_.Memcpy(dst, src, size);
|
||||
|
||||
// update src / addresses as well as remaining count
|
||||
*sar = src + size;
|
||||
|
@ -245,23 +321,23 @@ void SH4::Step() {
|
|||
|
||||
// recompile it with only one instruction and run it
|
||||
uint32_t guest_addr = ctx_.pc;
|
||||
uint8_t *host_addr = memory_->TranslateVirtual(guest_addr);
|
||||
uint8_t *host_addr = space_.Translate(guest_addr);
|
||||
int flags = GetCompileFlags() | SH4_SINGLE_INSTR;
|
||||
|
||||
CodePointer code = code_cache_->CompileCode(guest_addr, host_addr, flags);
|
||||
ctx_.pc = code();
|
||||
|
||||
// let the debugger know we've stopped
|
||||
dc_.debugger->Trap();
|
||||
dc_.debugger()->Trap();
|
||||
}
|
||||
|
||||
void SH4::AddBreakpoint(int type, uint32_t addr) {
|
||||
// save off the original instruction
|
||||
uint16_t instr = memory_->R16(addr);
|
||||
uint16_t instr = space_.R16(addr);
|
||||
breakpoints_.insert(std::make_pair(addr, instr));
|
||||
|
||||
// write out an invalid instruction
|
||||
memory_->W16(addr, 0);
|
||||
space_.W16(addr, 0);
|
||||
|
||||
code_cache_->RemoveBlocks(addr);
|
||||
}
|
||||
|
@ -274,13 +350,13 @@ void SH4::RemoveBreakpoint(int type, uint32_t addr) {
|
|||
breakpoints_.erase(it);
|
||||
|
||||
// overwrite the invalid instruction with the original
|
||||
memory_->W16(addr, instr);
|
||||
space_.W16(addr, instr);
|
||||
|
||||
code_cache_->RemoveBlocks(addr);
|
||||
}
|
||||
|
||||
void SH4::ReadMemory(uint32_t addr, uint8_t *buffer, int size) {
|
||||
memory_->Memcpy(buffer, addr, size);
|
||||
space_.Memcpy(buffer, addr, size);
|
||||
}
|
||||
|
||||
void SH4::ReadRegister(int n, uint64_t *value, int *size) {
|
||||
|
@ -321,72 +397,6 @@ void SH4::ReadRegister(int n, uint64_t *value, int *size) {
|
|||
*size = 4;
|
||||
}
|
||||
|
||||
void SH4::MapPhysicalMemory(Memory &memory, MemoryMap &memmap) {
|
||||
// area 2 and 4 are unused
|
||||
RegionHandle a0_handle = memory.AllocRegion(AREA0_BEGIN, AREA0_SIZE);
|
||||
RegionHandle a1_handle = memory.AllocRegion(AREA1_BEGIN, AREA1_SIZE);
|
||||
RegionHandle a3_handle = memory.AllocRegion(AREA3_BEGIN, AREA3_SIZE);
|
||||
RegionHandle a5_handle = memory.AllocRegion(AREA5_BEGIN, AREA5_SIZE);
|
||||
RegionHandle a6_handle = memory.AllocRegion(AREA6_BEGIN, AREA6_SIZE);
|
||||
RegionHandle a7_handle = memory.AllocRegion(AREA7_BEGIN, AREA7_SIZE);
|
||||
|
||||
RegionHandle sh4_reg_handle = memory.AllocRegion(
|
||||
SH4_REG_BEGIN, SH4_REG_SIZE,
|
||||
make_delegate(&SH4::ReadRegister<uint8_t>, this),
|
||||
make_delegate(&SH4::ReadRegister<uint16_t>, this),
|
||||
make_delegate(&SH4::ReadRegister<uint32_t>, this), nullptr,
|
||||
make_delegate(&SH4::WriteRegister<uint8_t>, this),
|
||||
make_delegate(&SH4::WriteRegister<uint16_t>, this),
|
||||
make_delegate(&SH4::WriteRegister<uint32_t>, this), nullptr);
|
||||
|
||||
memmap.Mount(a0_handle, AREA0_SIZE, AREA0_BEGIN);
|
||||
memmap.Mount(a1_handle, AREA1_SIZE, AREA1_BEGIN);
|
||||
memmap.Mount(a3_handle, AREA3_SIZE, AREA3_BEGIN);
|
||||
memmap.Mount(a5_handle, AREA5_SIZE, AREA5_BEGIN);
|
||||
memmap.Mount(a6_handle, AREA6_SIZE, AREA6_BEGIN);
|
||||
memmap.Mount(a7_handle, AREA7_SIZE, AREA7_BEGIN);
|
||||
memmap.Mount(sh4_reg_handle, SH4_REG_SIZE, SH4_REG_BEGIN);
|
||||
}
|
||||
|
||||
void SH4::MapVirtualMemory(Memory &memory, MemoryMap &memmap) {
|
||||
RegionHandle sh4_cache_handle =
|
||||
memory.AllocRegion(SH4_CACHE_BEGIN, SH4_CACHE_SIZE,
|
||||
make_delegate(&SH4::ReadCache<uint8_t>, this),
|
||||
make_delegate(&SH4::ReadCache<uint16_t>, this),
|
||||
make_delegate(&SH4::ReadCache<uint32_t>, this),
|
||||
make_delegate(&SH4::ReadCache<uint64_t>, this),
|
||||
make_delegate(&SH4::WriteCache<uint8_t>, this),
|
||||
make_delegate(&SH4::WriteCache<uint16_t>, this),
|
||||
make_delegate(&SH4::WriteCache<uint32_t>, this),
|
||||
make_delegate(&SH4::WriteCache<uint64_t>, this));
|
||||
|
||||
RegionHandle sh4_sq_handle = memory.AllocRegion(
|
||||
SH4_SQ_BEGIN, SH4_SQ_SIZE, make_delegate(&SH4::ReadSQ<uint8_t>, this),
|
||||
make_delegate(&SH4::ReadSQ<uint16_t>, this),
|
||||
make_delegate(&SH4::ReadSQ<uint32_t>, this), nullptr,
|
||||
make_delegate(&SH4::WriteSQ<uint8_t>, this),
|
||||
make_delegate(&SH4::WriteSQ<uint16_t>, this),
|
||||
make_delegate(&SH4::WriteSQ<uint32_t>, this), nullptr);
|
||||
|
||||
// main ram mirrors
|
||||
memmap.Mirror(MAIN_RAM_1_BEGIN, MAIN_RAM_1_SIZE, MAIN_RAM_2_BEGIN);
|
||||
memmap.Mirror(MAIN_RAM_1_BEGIN, MAIN_RAM_1_SIZE, MAIN_RAM_3_BEGIN);
|
||||
memmap.Mirror(MAIN_RAM_1_BEGIN, MAIN_RAM_1_SIZE, MAIN_RAM_4_BEGIN);
|
||||
|
||||
// physical mirrors (ignoring p, alt and cache bits in bits 31-29)
|
||||
memmap.Mirror(P0_1_BEGIN, P0_1_SIZE, P0_2_BEGIN);
|
||||
memmap.Mirror(P0_1_BEGIN, P0_1_SIZE, P0_3_BEGIN);
|
||||
memmap.Mirror(P0_1_BEGIN, P0_1_SIZE, P0_4_BEGIN);
|
||||
memmap.Mirror(P0_1_BEGIN, P0_1_SIZE, P1_BEGIN);
|
||||
memmap.Mirror(P0_1_BEGIN, P0_1_SIZE, P2_BEGIN);
|
||||
memmap.Mirror(P0_1_BEGIN, P0_1_SIZE, P3_BEGIN);
|
||||
memmap.Mirror(P0_1_BEGIN, P0_1_SIZE, P4_BEGIN);
|
||||
|
||||
// handle some special access only available in P4 after applying mirrors
|
||||
memmap.Mount(sh4_cache_handle, SH4_CACHE_SIZE, SH4_CACHE_BEGIN);
|
||||
memmap.Mount(sh4_sq_handle, SH4_SQ_SIZE, SH4_SQ_BEGIN);
|
||||
}
|
||||
|
||||
void SH4::OnPaint(bool show_main_menu) {
|
||||
if (show_main_menu && ImGui::BeginMainMenuBar()) {
|
||||
if (ImGui::BeginMenu("CPU")) {
|
||||
|
@ -425,7 +435,7 @@ void SH4::OnPaint(bool show_main_menu) {
|
|||
|
||||
uint32_t SH4::CompilePC() {
|
||||
uint32_t guest_addr = s_current_cpu->ctx_.pc;
|
||||
uint8_t *host_addr = s_current_cpu->memory_->TranslateVirtual(guest_addr);
|
||||
uint8_t *host_addr = s_current_cpu->space_.Translate(guest_addr);
|
||||
int flags = s_current_cpu->GetCompileFlags();
|
||||
|
||||
CodePointer code =
|
||||
|
@ -444,7 +454,7 @@ void SH4::InvalidInstruction(SH4Context *ctx, uint64_t data) {
|
|||
self->ctx_.num_cycles = 0;
|
||||
|
||||
// let the debugger know execution has stopped
|
||||
self->dc_.debugger->Trap();
|
||||
self->dc_.debugger()->Trap();
|
||||
}
|
||||
|
||||
void SH4::Prefetch(SH4Context *ctx, uint64_t data) {
|
||||
|
@ -467,7 +477,7 @@ void SH4::Prefetch(SH4Context *ctx, uint64_t data) {
|
|||
|
||||
// perform the "burst" 32-byte copy
|
||||
for (int i = 0; i < 8; i++) {
|
||||
self->memory_->W32(dest, ctx->sq[sqi][i]);
|
||||
self->space_.W32(dest, ctx->sq[sqi][i]);
|
||||
dest += 4;
|
||||
}
|
||||
}
|
||||
|
@ -810,7 +820,7 @@ void SH4::ExpireTimer() {
|
|||
RescheduleTimer(N, tcnt, tcr);
|
||||
}
|
||||
|
||||
SH4_R32_DELEGATE(PDTRA) {
|
||||
R32_DELEGATE(SH4::PDTRA) {
|
||||
// magic values to get past 0x8c00b948 in the boot rom:
|
||||
// void _8c00b92c(int arg1) {
|
||||
// sysvars->var1 = reg[PDTRA];
|
||||
|
@ -877,7 +887,7 @@ SH4_R32_DELEGATE(PDTRA) {
|
|||
return v;
|
||||
}
|
||||
|
||||
SH4_W32_DELEGATE(MMUCR) {
|
||||
W32_DELEGATE(SH4::MMUCR) {
|
||||
if (!reg.value) {
|
||||
return;
|
||||
}
|
||||
|
@ -885,49 +895,49 @@ SH4_W32_DELEGATE(MMUCR) {
|
|||
LOG_FATAL("MMU not currently supported");
|
||||
}
|
||||
|
||||
SH4_W32_DELEGATE(CCR) {
|
||||
W32_DELEGATE(SH4::CCR) {
|
||||
if (CCR.ICI) {
|
||||
ResetCache();
|
||||
}
|
||||
}
|
||||
|
||||
SH4_W32_DELEGATE(CHCR0) { CheckDMA(0); }
|
||||
W32_DELEGATE(SH4::CHCR0) { CheckDMA(0); }
|
||||
|
||||
SH4_W32_DELEGATE(CHCR1) { CheckDMA(1); }
|
||||
W32_DELEGATE(SH4::CHCR1) { CheckDMA(1); }
|
||||
|
||||
SH4_W32_DELEGATE(CHCR2) { CheckDMA(2); }
|
||||
W32_DELEGATE(SH4::CHCR2) { CheckDMA(2); }
|
||||
|
||||
SH4_W32_DELEGATE(CHCR3) { CheckDMA(3); }
|
||||
W32_DELEGATE(SH4::CHCR3) { CheckDMA(3); }
|
||||
|
||||
SH4_W32_DELEGATE(DMAOR) {
|
||||
W32_DELEGATE(SH4::DMAOR) {
|
||||
CheckDMA(0);
|
||||
CheckDMA(1);
|
||||
CheckDMA(2);
|
||||
CheckDMA(3);
|
||||
}
|
||||
|
||||
SH4_W32_DELEGATE(IPRA) { ReprioritizeInterrupts(); }
|
||||
W32_DELEGATE(SH4::IPRA) { ReprioritizeInterrupts(); }
|
||||
|
||||
SH4_W32_DELEGATE(IPRB) { ReprioritizeInterrupts(); }
|
||||
W32_DELEGATE(SH4::IPRB) { ReprioritizeInterrupts(); }
|
||||
|
||||
SH4_W32_DELEGATE(IPRC) { ReprioritizeInterrupts(); }
|
||||
W32_DELEGATE(SH4::IPRC) { ReprioritizeInterrupts(); }
|
||||
|
||||
SH4_W32_DELEGATE(TSTR) { UpdateTimerStart(); }
|
||||
W32_DELEGATE(SH4::TSTR) { UpdateTimerStart(); }
|
||||
|
||||
SH4_W32_DELEGATE(TCR0) { UpdateTimerControl(0); }
|
||||
W32_DELEGATE(SH4::TCR0) { UpdateTimerControl(0); }
|
||||
|
||||
SH4_W32_DELEGATE(TCR1) { UpdateTimerControl(1); }
|
||||
W32_DELEGATE(SH4::TCR1) { UpdateTimerControl(1); }
|
||||
|
||||
SH4_W32_DELEGATE(TCR2) { UpdateTimerControl(2); }
|
||||
W32_DELEGATE(SH4::TCR2) { UpdateTimerControl(2); }
|
||||
|
||||
SH4_R32_DELEGATE(TCNT0) { return TimerCount(0); }
|
||||
R32_DELEGATE(SH4::TCNT0) { return TimerCount(0); }
|
||||
|
||||
SH4_W32_DELEGATE(TCNT0) { UpdateTimerCount(0); }
|
||||
W32_DELEGATE(SH4::TCNT0) { UpdateTimerCount(0); }
|
||||
|
||||
SH4_R32_DELEGATE(TCNT1) { return TimerCount(1); }
|
||||
R32_DELEGATE(SH4::TCNT1) { return TimerCount(1); }
|
||||
|
||||
SH4_W32_DELEGATE(TCNT1) { UpdateTimerCount(1); }
|
||||
W32_DELEGATE(SH4::TCNT1) { UpdateTimerCount(1); }
|
||||
|
||||
SH4_R32_DELEGATE(TCNT2) { return TimerCount(2); }
|
||||
R32_DELEGATE(SH4::TCNT2) { return TimerCount(2); }
|
||||
|
||||
SH4_W32_DELEGATE(TCNT2) { UpdateTimerCount(2); }
|
||||
W32_DELEGATE(SH4::TCNT2) { UpdateTimerCount(2); }
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "hw/sh4/sh4_code_cache.h"
|
||||
#include "hw/sh4/sh4_types.h"
|
||||
#include "hw/machine.h"
|
||||
#include "hw/memory.h"
|
||||
#include "hw/register.h"
|
||||
#include "hw/scheduler.h"
|
||||
#include "jit/frontend/sh4/sh4_context.h"
|
||||
|
@ -39,18 +40,6 @@ struct DTR {
|
|||
int size;
|
||||
};
|
||||
|
||||
#define SH4_DECLARE_R32_DELEGATE(name) uint32_t name##_read(Register &)
|
||||
#define SH4_DECLARE_W32_DELEGATE(name) void name##_write(Register &, uint32_t)
|
||||
|
||||
#define SH4_REGISTER_R32_DELEGATE(name) \
|
||||
regs_[name##_OFFSET].read = make_delegate(&SH4::name##_read, this)
|
||||
#define SH4_REGISTER_W32_DELEGATE(name) \
|
||||
regs_[name##_OFFSET].write = make_delegate(&SH4::name##_write, this)
|
||||
|
||||
#define SH4_R32_DELEGATE(name) uint32_t SH4::name##_read(Register ®)
|
||||
#define SH4_W32_DELEGATE(name) \
|
||||
void SH4::name##_write(Register ®, uint32_t old_value)
|
||||
|
||||
class SH4 : public Device,
|
||||
public DebugInterface,
|
||||
public ExecuteInterface,
|
||||
|
@ -59,6 +48,8 @@ class SH4 : public Device,
|
|||
friend void RunSH4Test(const SH4Test &);
|
||||
|
||||
public:
|
||||
AM_DECLARE(data_map);
|
||||
|
||||
SH4(Dreamcast &dc);
|
||||
~SH4();
|
||||
|
||||
|
@ -89,10 +80,6 @@ class SH4 : public Device,
|
|||
void ReadMemory(uint32_t addr, uint8_t *buffer, int size) final;
|
||||
void ReadRegister(int n, uint64_t *value, int *size) final;
|
||||
|
||||
// MemoryInterface
|
||||
void MapPhysicalMemory(Memory &memory, MemoryMap &memmap) final;
|
||||
void MapVirtualMemory(Memory &memory, MemoryMap &memmap) final;
|
||||
|
||||
// WindowInterface
|
||||
void OnPaint(bool show_main_menu) final;
|
||||
|
||||
|
@ -143,30 +130,29 @@ class SH4 : public Device,
|
|||
template <int N>
|
||||
void ExpireTimer();
|
||||
|
||||
SH4_DECLARE_R32_DELEGATE(PDTRA);
|
||||
SH4_DECLARE_W32_DELEGATE(MMUCR);
|
||||
SH4_DECLARE_W32_DELEGATE(CCR);
|
||||
SH4_DECLARE_W32_DELEGATE(CHCR0);
|
||||
SH4_DECLARE_W32_DELEGATE(CHCR1);
|
||||
SH4_DECLARE_W32_DELEGATE(CHCR2);
|
||||
SH4_DECLARE_W32_DELEGATE(CHCR3);
|
||||
SH4_DECLARE_W32_DELEGATE(DMAOR);
|
||||
SH4_DECLARE_W32_DELEGATE(IPRA);
|
||||
SH4_DECLARE_W32_DELEGATE(IPRB);
|
||||
SH4_DECLARE_W32_DELEGATE(IPRC);
|
||||
SH4_DECLARE_W32_DELEGATE(TSTR);
|
||||
SH4_DECLARE_W32_DELEGATE(TCR0);
|
||||
SH4_DECLARE_W32_DELEGATE(TCR1);
|
||||
SH4_DECLARE_W32_DELEGATE(TCR2);
|
||||
SH4_DECLARE_R32_DELEGATE(TCNT0);
|
||||
SH4_DECLARE_W32_DELEGATE(TCNT0);
|
||||
SH4_DECLARE_R32_DELEGATE(TCNT1);
|
||||
SH4_DECLARE_W32_DELEGATE(TCNT1);
|
||||
SH4_DECLARE_R32_DELEGATE(TCNT2);
|
||||
SH4_DECLARE_W32_DELEGATE(TCNT2);
|
||||
DECLARE_R32_DELEGATE(PDTRA);
|
||||
DECLARE_W32_DELEGATE(MMUCR);
|
||||
DECLARE_W32_DELEGATE(CCR);
|
||||
DECLARE_W32_DELEGATE(CHCR0);
|
||||
DECLARE_W32_DELEGATE(CHCR1);
|
||||
DECLARE_W32_DELEGATE(CHCR2);
|
||||
DECLARE_W32_DELEGATE(CHCR3);
|
||||
DECLARE_W32_DELEGATE(DMAOR);
|
||||
DECLARE_W32_DELEGATE(IPRA);
|
||||
DECLARE_W32_DELEGATE(IPRB);
|
||||
DECLARE_W32_DELEGATE(IPRC);
|
||||
DECLARE_W32_DELEGATE(TSTR);
|
||||
DECLARE_W32_DELEGATE(TCR0);
|
||||
DECLARE_W32_DELEGATE(TCR1);
|
||||
DECLARE_W32_DELEGATE(TCR2);
|
||||
DECLARE_R32_DELEGATE(TCNT0);
|
||||
DECLARE_W32_DELEGATE(TCNT0);
|
||||
DECLARE_R32_DELEGATE(TCNT1);
|
||||
DECLARE_W32_DELEGATE(TCNT1);
|
||||
DECLARE_R32_DELEGATE(TCNT2);
|
||||
DECLARE_W32_DELEGATE(TCNT2);
|
||||
|
||||
Dreamcast &dc_;
|
||||
Memory *memory_;
|
||||
Scheduler *scheduler_;
|
||||
SH4CodeCache *code_cache_;
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#define SH4_REGS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "hw/regions.h"
|
||||
|
||||
namespace re {
|
||||
namespace hw {
|
||||
|
|
|
@ -17,7 +17,7 @@ ImGuiImpl::~ImGuiImpl() {
|
|||
|
||||
bool ImGuiImpl::Init() {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
Backend &rb = window_.render_backend();
|
||||
Backend *rb = window_.render_backend();
|
||||
|
||||
// don't really care if this is accurate
|
||||
io.DeltaTime = 1.0f / 60.0f;
|
||||
|
@ -57,8 +57,8 @@ bool ImGuiImpl::Init() {
|
|||
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||
|
||||
TextureHandle handle =
|
||||
rb.RegisterTexture(PXL_RGBA, FILTER_BILINEAR, WRAP_REPEAT, WRAP_REPEAT,
|
||||
false, width, height, pixels);
|
||||
rb->RegisterTexture(PXL_RGBA, FILTER_BILINEAR, WRAP_REPEAT, WRAP_REPEAT,
|
||||
false, width, height, pixels);
|
||||
|
||||
io.Fonts->TexID = reinterpret_cast<void *>(static_cast<intptr_t>(handle));
|
||||
|
||||
|
@ -81,7 +81,7 @@ void ImGuiImpl::OnPrePaint() {
|
|||
|
||||
void ImGuiImpl::OnPostPaint() {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
Backend &rb = window_.render_backend();
|
||||
Backend *rb = window_.render_backend();
|
||||
|
||||
// if there are any focused items, enable text input
|
||||
window_.EnableTextInput(ImGui::IsAnyItemActive());
|
||||
|
@ -93,7 +93,7 @@ void ImGuiImpl::OnPostPaint() {
|
|||
// get the latest draw batches, and pass them off out the render backend
|
||||
ImDrawData *data = ImGui::GetDrawData();
|
||||
|
||||
rb.Begin2D();
|
||||
rb->Begin2D();
|
||||
|
||||
for (int i = 0; i < data->CmdListsCount; ++i) {
|
||||
const auto cmd_list = data->CmdLists[i];
|
||||
|
@ -104,7 +104,7 @@ void ImGuiImpl::OnPostPaint() {
|
|||
uint16_t *indices = cmd_list->IdxBuffer.Data;
|
||||
int num_indices = cmd_list->IdxBuffer.size();
|
||||
|
||||
rb.BeginSurfaces2D(verts, num_verts, indices, num_indices);
|
||||
rb->BeginSurfaces2D(verts, num_verts, indices, num_indices);
|
||||
|
||||
int index_offset = 0;
|
||||
|
||||
|
@ -125,15 +125,15 @@ void ImGuiImpl::OnPostPaint() {
|
|||
surf.first_vert = index_offset;
|
||||
surf.num_verts = cmd.ElemCount;
|
||||
|
||||
rb.DrawSurface2D(surf);
|
||||
rb->DrawSurface2D(surf);
|
||||
|
||||
index_offset += cmd.ElemCount;
|
||||
}
|
||||
|
||||
rb.EndSurfaces2D();
|
||||
rb->EndSurfaces2D();
|
||||
}
|
||||
|
||||
rb.End2D();
|
||||
rb->End2D();
|
||||
}
|
||||
|
||||
void ImGuiImpl::OnTextInput(const char *text) {
|
||||
|
|
|
@ -1598,7 +1598,7 @@ MicroProfileImpl::MicroProfileImpl(Window &window)
|
|||
MicroProfileImpl::~MicroProfileImpl() { window_.RemoveListener(this); }
|
||||
|
||||
bool MicroProfileImpl::Init() {
|
||||
Backend &rb = window_.render_backend();
|
||||
Backend *rb = window_.render_backend();
|
||||
|
||||
// register and enable gpu and runtime group by default
|
||||
uint16_t gpu_group = MicroProfileGetGroup("gpu", MicroProfileTokenTypeCpu);
|
||||
|
@ -1612,7 +1612,7 @@ bool MicroProfileImpl::Init() {
|
|||
g_MicroProfile.nBars |= MP_DRAW_TIMERS | MP_DRAW_AVERAGE | MP_DRAW_CALL_COUNT;
|
||||
|
||||
// register the font texture
|
||||
font_tex_ = rb.RegisterTexture(
|
||||
font_tex_ = rb->RegisterTexture(
|
||||
PXL_RGBA, FILTER_NEAREST, WRAP_CLAMP_TO_EDGE, WRAP_CLAMP_TO_EDGE, false,
|
||||
FONT_WIDTH, FONT_HEIGHT, reinterpret_cast<const uint8_t *>(s_font_data));
|
||||
|
||||
|
@ -1627,18 +1627,18 @@ void MicroProfileImpl::OnPostPaint() {
|
|||
MicroProfileDraw(window_.width(), window_.height());
|
||||
|
||||
// render the surfaces
|
||||
Backend &rb = window_.render_backend();
|
||||
Backend *rb = window_.render_backend();
|
||||
|
||||
rb.Begin2D();
|
||||
rb.BeginSurfaces2D(verts_, num_verts_, nullptr, 0);
|
||||
rb->Begin2D();
|
||||
rb->BeginSurfaces2D(verts_, num_verts_, nullptr, 0);
|
||||
|
||||
for (int i = 0; i < num_surfs_; i++) {
|
||||
Surface2D &surf = surfs_[i];
|
||||
rb.DrawSurface2D(surf);
|
||||
rb->DrawSurface2D(surf);
|
||||
}
|
||||
|
||||
rb.EndSurfaces2D();
|
||||
rb.End2D();
|
||||
rb->EndSurfaces2D();
|
||||
rb->End2D();
|
||||
|
||||
// reset surfaces
|
||||
num_surfs_ = 0;
|
||||
|
|
|
@ -27,7 +27,7 @@ enum {
|
|||
class Window {
|
||||
public:
|
||||
SDL_Window *handle() { return window_; }
|
||||
renderer::Backend &render_backend() { return *rb_; }
|
||||
renderer::Backend *render_backend() { return rb_; }
|
||||
|
||||
int width() { return width_; }
|
||||
int height() { return height_; }
|
||||
|
|
|
@ -153,12 +153,8 @@ namespace hw {
|
|||
namespace sh4 {
|
||||
|
||||
void RunSH4Test(const SH4Test &test) {
|
||||
// initialize fake dreamcast device
|
||||
// TODO avoid initializing an entire 32-bit addres space for each test?
|
||||
// perhaps initialize the machine once, resetting the SH4 context between
|
||||
// runs?
|
||||
std::unique_ptr<Dreamcast> dc(new Dreamcast());
|
||||
std::unique_ptr<SH4> sh4(new SH4(*dc.get()));
|
||||
std::unique_ptr<Dreamcast> dc(new Dreamcast(nullptr));
|
||||
SH4 *sh4 = dc->sh4();
|
||||
|
||||
CHECK(dc->Init());
|
||||
|
||||
|
@ -183,7 +179,7 @@ void RunSH4Test(const SH4Test &test) {
|
|||
int aligned_size = re::align_up(test.buffer_size, 4);
|
||||
uint8_t *aligned_buffer = reinterpret_cast<uint8_t *>(alloca(aligned_size));
|
||||
memcpy(aligned_buffer, test.buffer, test.buffer_size);
|
||||
dc->memory->Memcpy(0x8c010000, aligned_buffer, aligned_size);
|
||||
sh4->space().Memcpy(0x8c010000, aligned_buffer, aligned_size);
|
||||
|
||||
// skip to the test's offset
|
||||
sh4->SetPC(0x8c010000 + test.buffer_offset);
|
||||
|
|
Loading…
Reference in New Issue