split memory system up to support mapping multiple address spaces to the same backing shared memory

This commit is contained in:
Anthony Pesch 2016-04-24 23:19:03 -07:00
parent 18232d86d2
commit 7d639cb74a
41 changed files with 1187 additions and 1389 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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);

View File

@ -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]);

View File

@ -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_;

View File

@ -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]);

View File

@ -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>

View File

@ -2,7 +2,6 @@
#define AICA_TYPES_H
#include <stdint.h>
#include "hw/regions.h"
namespace re {
namespace hw {

View File

@ -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);

View File

@ -20,7 +20,6 @@ class ARM7 : public Device, public ExecuteInterface {
void Run(const std::chrono::nanoseconds &delta) final;
Dreamcast &dc_;
Memory *memory_;
};
}
}

View File

@ -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_) {

View File

@ -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_;
}

View File

@ -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_;
};
}
}

View File

@ -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));
}

View File

@ -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 &reg)
#define GDROM_W32_DELEGATE(name) \
void GDROM::name##_write(Register &reg, 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_;

View File

@ -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;
}

View File

@ -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 &reg)
#define HOLLY_W32_DELEGATE(name) \
void Holly::name##_write(Register &reg, uint32_t old_value)
class Holly : public Device, public MemoryInterface {
class Holly : public Device {
public:
AM_DECLARE(reg_map);
Holly(Dreamcast &dc);
Register &reg(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_;

View File

@ -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

View File

@ -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 &reg = 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(); }

View File

@ -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 &reg)
#define PVR2_W32_DELEGATE(name) \
void PVR2::name##_write(Register &reg, 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 &reg(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_;

View File

@ -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,
};
}
}

View File

@ -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;
}

View File

@ -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 &reg)
#define TA_W32_DELEGATE(name) \
void TileAccelerator::name##_write(Register &reg, 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_;

View File

@ -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};
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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_;
};
}
}

View File

@ -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) {

View File

@ -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 &reg)
#define MAPLE_W32_DELEGATE(name) \
void Maple::name##_write(Register &reg, 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];

View File

@ -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 &region, 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 = &regions_[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 &region = 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 &region = 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 = &regions_[num_regions_];
new (region) MemoryRegion();
region->handle = num_regions_++;
CHECK(PageAligned(shmem_size_, size));
MemoryRegion &region = regions_[num_regions_];
new (&region) 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 = &regions_[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 = &regions_[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 &region = 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 = &regions_[entry->mount.handle];
MemoryRegion &region = 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 &region = 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 &region = regions_[RegionIndex(page)];
MemoryRegion &region = 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 &region = regions_[RegionIndex(page)];
MemoryRegion &region = memory_.regions_[RegionIndex(page)];
uint32_t region_offset = RegionOffset(page);
uint32_t page_offset = PageOffset(addr);
(region.*DELEGATE)(region_offset + page_offset, value);
}

View File

@ -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_;
};
}

View File

@ -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

View File

@ -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 &reg)
#define W32_DELEGATE(name) void name##_w(Register &reg, uint32_t old_value)
struct Register {
Register() : flags(RW), value(0) {}
Register(uint8_t flags, uint32_t value) : flags(flags), value(value) {}

View File

@ -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()) {

View File

@ -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); }

View File

@ -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 &reg)
#define SH4_W32_DELEGATE(name) \
void SH4::name##_write(Register &reg, 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_;

View File

@ -2,7 +2,6 @@
#define SH4_REGS_H
#include <stdint.h>
#include "hw/regions.h"
namespace re {
namespace hw {

View File

@ -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) {

View File

@ -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;

View File

@ -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_; }

View File

@ -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);