refactored hw/ code adding Machine, Device and Device interfaces

This commit is contained in:
Anthony Pesch 2016-02-20 18:13:19 -08:00
parent 2b42b9c85c
commit ede8b2f758
37 changed files with 645 additions and 578 deletions

View File

@ -140,7 +140,7 @@ set(REDREAM_SOURCES
src/hw/maple/maple_controller.cc
src/hw/sh4/sh4.cc
src/hw/sh4/sh4_code_cache.cc
src/hw/dreamcast.cc
src/hw/machine.cc
src/hw/memory.cc
src/hw/scheduler.cc
src/jit/backend/interpreter/interpreter_backend.cc

View File

@ -2,10 +2,12 @@
#include <gflags/gflags.h>
#include "emu/emulator.h"
#include "emu/profiler.h"
#include "hw/aica/aica.h"
#include "hw/gdrom/gdrom.h"
#include "hw/holly/texture_cache.h"
#include "hw/holly/tile_renderer.h"
#include "hw/maple/maple.h"
#include "hw/sh4/sh4.h"
#include "hw/dreamcast.h"
#include "hw/memory.h"
#include "renderer/gl_backend.h"
@ -14,8 +16,11 @@
using namespace re;
using namespace re::emu;
using namespace re::hw;
using namespace re::hw::aica;
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::sys;
using namespace re::trace;
@ -24,7 +29,8 @@ DEFINE_string(bios, "dc_boot.bin", "Path to BIOS");
DEFINE_string(flash, "dc_flash.bin", "Path to flash ROM");
Emulator::Emulator()
: trace_writer_(nullptr),
: rb_(nullptr),
trace_writer_(nullptr),
tile_renderer_(nullptr),
core_events_(MAX_EVENTS),
speed_() {
@ -33,10 +39,11 @@ Emulator::Emulator()
Emulator::~Emulator() {
delete rb_;
DestroyDreamcast();
delete trace_writer_;
delete tile_renderer_;
DestroyDreamcast(dc_);
}
void Emulator::Run(const char *path) {
@ -48,7 +55,7 @@ void Emulator::Run(const char *path) {
return;
}
if (!CreateDreamcast(dc_, rb_)) {
if (!CreateDreamcast()) {
return;
}
@ -87,6 +94,45 @@ void Emulator::Run(const char *path) {
cpu_thread.join();
}
bool Emulator::CreateDreamcast() {
dc_.sh4 = new SH4(&dc_);
dc_.aica = new AICA(&dc_);
dc_.gdrom = new GDROM(&dc_);
dc_.holly = new Holly(&dc_);
dc_.maple = new Maple(&dc_);
dc_.pvr = new PVR2(&dc_);
dc_.ta = new TileAccelerator(&dc_);
dc_.texcache = new TextureCache(&dc_, rb_);
if (!dc_.Init()) {
DestroyDreamcast();
return false;
}
return true;
}
void Emulator::DestroyDreamcast() {
delete dc_.sh4;
dc_.sh4 = nullptr;
delete dc_.aica;
dc_.aica = nullptr;
delete dc_.gdrom;
dc_.gdrom = nullptr;
delete dc_.holly;
dc_.holly = nullptr;
delete dc_.maple;
dc_.maple = nullptr;
delete dc_.pvr;
dc_.pvr = nullptr;
delete dc_.ta;
dc_.ta = nullptr;
delete dc_.texcache;
dc_.texcache = nullptr;
dc_.trace_writer = nullptr;
}
bool Emulator::LoadBios(const char *path) {
FILE *fp = fopen(path, "rb");
if (!fp) {
@ -104,7 +150,8 @@ bool Emulator::LoadBios(const char *path) {
return false;
}
int n = static_cast<int>(fread(dc_.bios, sizeof(uint8_t), size, fp));
uint8_t *bios = dc_.memory->TranslateVirtual(BIOS_START);
int n = static_cast<int>(fread(bios, sizeof(uint8_t), size, fp));
fclose(fp);
if (n != size) {
@ -132,7 +179,8 @@ bool Emulator::LoadFlash(const char *path) {
return false;
}
int n = static_cast<int>(fread(dc_.flash, sizeof(uint8_t), size, fp));
uint8_t *flash = dc_.memory->TranslateVirtual(FLASH_START);
int n = static_cast<int>(fread(flash, sizeof(uint8_t), size, fp));
fclose(fp);
if (n != size) {
@ -153,21 +201,18 @@ bool Emulator::LaunchBIN(const char *path) {
int size = ftell(fp);
fseek(fp, 0, SEEK_SET);
uint8_t *data = reinterpret_cast<uint8_t *>(malloc(size));
// load to 0x0c010000 (area 3) which is where 1ST_READ.BIN is loaded to
uint32_t pc = 0x0c010000;
uint8_t *data = dc_.memory->TranslateVirtual(pc);
int n = static_cast<int>(fread(data, sizeof(uint8_t), size, fp));
fclose(fp);
if (n != size) {
free(data);
LOG_WARNING("BIN read failed");
return false;
}
// load to 0x0c010000 (area 3) which is where 1ST_READ.BIN is normally
// loaded to
dc_.memory->Memcpy(0x0c010000, data, size);
free(data);
dc_.sh4->SetPC(0x0c010000);
dc_.sh4->SetPC(pc);
return true;
}

View File

@ -19,6 +19,9 @@ class Emulator {
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

@ -1,20 +1,46 @@
#include "core/memory.h"
#include "hw/aica/aica.h"
#include "hw/dreamcast.h"
#include "hw/memory.h"
using namespace re::hw;
using namespace re::hw::aica;
using namespace re::hw::holly;
AICA::AICA(Dreamcast *dc) : dc_(dc) {}
AICA::AICA(Dreamcast *dc) : Device(*dc), MemoryInterface(this), dc_(dc) {}
bool AICA::Init() {
// aica_regs_ = dc_->aica_regs();
wave_ram_ = dc_->wave_ram;
wave_ram_ = dc_->memory->TranslateVirtual(WAVE_RAM_START);
return true;
}
void AICA::MapPhysicalMemory(Memory &memory, MemoryMap &memmap) {
// RegionHandle aica_reg_handle = memory.AllocRegion(
// AICA_REG_START, AICA_REG_SIZE, aica(),
// nullptr,
// nullptr,
// &AICA::ReadRegister,
// nullptr,
// nullptr,
// nullptr,
// &AICA::WriteRegister,
// nullptr);
RegionHandle wave_ram_handle = memory.AllocRegion(
WAVE_RAM_START, 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_START);
memmap.Mount(wave_ram_handle, WAVE_RAM_SIZE, WAVE_RAM_START);
}
// frequency 22579200
// int AICA::Run(int cycles) {
// // uint16_t MCIEB = re::load<uint16_t>(&aica_regs_[MCIEB_OFFSET]);
@ -40,9 +66,6 @@ bool AICA::Init() {
// re::store(&self->aica_regs_[addr], value);
// }
template uint8_t AICA::ReadWave(uint32_t addr);
template uint16_t AICA::ReadWave(uint32_t addr);
template uint32_t AICA::ReadWave(uint32_t addr);
template <typename T>
T AICA::ReadWave(uint32_t addr) {
if (sizeof(T) == 4) {
@ -59,9 +82,6 @@ T AICA::ReadWave(uint32_t addr) {
return re::load<T>(&wave_ram_[addr]);
}
template void AICA::WriteWave(uint32_t addr, uint8_t value);
template void AICA::WriteWave(uint32_t addr, uint16_t value);
template void AICA::WriteWave(uint32_t addr, uint32_t value);
template <typename T>
void AICA::WriteWave(uint32_t addr, T value) {
re::store(&wave_ram_[addr], value);

View File

@ -2,22 +2,22 @@
#define AICA_H
#include <stdint.h>
#include "hw/machine.h"
namespace re {
namespace hw {
struct Dreamcast;
extern bool MapMemory(Dreamcast &dc);
class Dreamcast;
namespace aica {
class AICA {
friend bool re::hw::MapMemory(Dreamcast &dc);
class AICA : public Device, public MemoryInterface {
public:
AICA(Dreamcast *dc);
bool Init();
bool Init() final;
protected:
void MapPhysicalMemory(Memory &memory, MemoryMap &memmap) final;
private:
// static uint32_t ReadRegister(void *ctx, uint32_t addr);
@ -25,7 +25,6 @@ class AICA {
template <typename T>
T ReadWave(uint32_t addr);
template <typename T>
void WriteWave(uint32_t addr, T value);

View File

@ -1,255 +0,0 @@
#include "hw/aica/aica.h"
#include "hw/gdrom/gdrom.h"
#include "hw/holly/holly.h"
#include "hw/holly/pvr2.h"
#include "hw/holly/texture_cache.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 "renderer/backend.h"
#include "trace/trace.h"
using namespace re;
using namespace re::hw;
using namespace re::hw::aica;
using namespace re::hw::gdrom;
using namespace re::hw::holly;
using namespace re::hw::maple;
using namespace re::hw::sh4;
using namespace re::jit;
using namespace re::renderer;
using namespace re::sys;
using namespace re::trace;
namespace re {
namespace hw {
// clang-format off
bool MapMemory(Dreamcast &dc) {
Memory *memory = dc.memory;
if (!memory->Init()) {
return false;
}
// first, allocate static regions
RegionHandle a0_handle = memory->AllocRegion(AREA0_START, AREA0_SIZE);
RegionHandle a1_handle = memory->AllocRegion(AREA1_START, AREA1_SIZE);
// area 2 unused
RegionHandle a3_handle = memory->AllocRegion(AREA3_START, AREA3_SIZE);
// area 4 unused
RegionHandle a5_handle = memory->AllocRegion(AREA5_START, AREA5_SIZE);
RegionHandle a6_handle = memory->AllocRegion(AREA6_START, AREA6_SIZE);
RegionHandle a7_handle = memory->AllocRegion(AREA7_START, AREA7_SIZE);
// second, allocate dynamic regions that overlap static regions
RegionHandle holly_handle = memory->AllocRegion(
HOLLY_REG_START, HOLLY_REG_SIZE,
make_delegate(&Holly::ReadRegister<uint8_t>, dc.holly),
make_delegate(&Holly::ReadRegister<uint16_t>, dc.holly),
make_delegate(&Holly::ReadRegister<uint32_t>, dc.holly),
nullptr,
make_delegate(&Holly::WriteRegister<uint8_t>, dc.holly),
make_delegate(&Holly::WriteRegister<uint16_t>, dc.holly),
make_delegate(&Holly::WriteRegister<uint32_t>, dc.holly),
nullptr);
RegionHandle pvr_reg_handle = memory->AllocRegion(
PVR_REG_START, PVR_REG_SIZE,
nullptr,
nullptr,
make_delegate(&PVR2::ReadRegister, dc.pvr),
nullptr,
nullptr,
nullptr,
make_delegate(&PVR2::WriteRegister, dc.pvr),
nullptr);
// RegionHandle aica_reg_handle = memory->AllocRegion(
// AICA_REG_START, AICA_REG_SIZE, aica(),
// nullptr,
// nullptr,
// &AICA::ReadRegister,
// nullptr,
// nullptr,
// nullptr,
// &AICA::WriteRegister,
// nullptr);
RegionHandle wave_ram_handle = memory->AllocRegion(
WAVE_RAM_START, WAVE_RAM_SIZE,
make_delegate(&AICA::ReadWave<uint8_t>, dc.aica),
make_delegate(&AICA::ReadWave<uint16_t>, dc.aica),
make_delegate(&AICA::ReadWave<uint32_t>, dc.aica),
nullptr,
make_delegate(&AICA::WriteWave<uint8_t>, dc.aica),
make_delegate(&AICA::WriteWave<uint16_t>, dc.aica),
make_delegate(&AICA::WriteWave<uint32_t>, dc.aica),
nullptr);
RegionHandle pvr_vram64_handle = memory->AllocRegion(
PVR_VRAM64_START, PVR_VRAM64_SIZE,
make_delegate(&PVR2::ReadVRamInterleaved<uint8_t>, dc.pvr),
make_delegate(&PVR2::ReadVRamInterleaved<uint16_t>, dc.pvr),
make_delegate(&PVR2::ReadVRamInterleaved<uint32_t>, dc.pvr),
nullptr,
nullptr,
make_delegate(&PVR2::WriteVRamInterleaved<uint16_t>, dc.pvr),
make_delegate(&PVR2::WriteVRamInterleaved<uint32_t>, dc.pvr),
nullptr);
RegionHandle ta_cmd_handle = memory->AllocRegion(
TA_CMD_START, TA_CMD_SIZE,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
make_delegate(&TileAccelerator::WriteCommand, dc.ta),
nullptr);
RegionHandle ta_texture_handle = memory->AllocRegion(
TA_TEXTURE_START, TA_TEXTURE_SIZE,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
make_delegate(&TileAccelerator::WriteTexture, dc.ta),
nullptr);
RegionHandle sh4_reg_handle = memory->AllocRegion(
SH4_REG_START, SH4_REG_SIZE,
make_delegate(&SH4::ReadRegister<uint8_t>, dc.sh4),
make_delegate(&SH4::ReadRegister<uint16_t>, dc.sh4),
make_delegate(&SH4::ReadRegister<uint32_t>, dc.sh4),
nullptr,
make_delegate(&SH4::WriteRegister<uint8_t>, dc.sh4),
make_delegate(&SH4::WriteRegister<uint16_t>, dc.sh4),
make_delegate(&SH4::WriteRegister<uint32_t>, dc.sh4),
nullptr);
RegionHandle sh4_cache_handle = memory->AllocRegion(
SH4_CACHE_START, SH4_CACHE_SIZE,
make_delegate(&SH4::ReadCache<uint8_t>, dc.sh4),
make_delegate(&SH4::ReadCache<uint16_t>, dc.sh4),
make_delegate(&SH4::ReadCache<uint32_t>, dc.sh4),
make_delegate(&SH4::ReadCache<uint64_t>, dc.sh4),
make_delegate(&SH4::WriteCache<uint8_t>, dc.sh4),
make_delegate(&SH4::WriteCache<uint16_t>, dc.sh4),
make_delegate(&SH4::WriteCache<uint32_t>, dc.sh4),
make_delegate(&SH4::WriteCache<uint64_t>, dc.sh4));
RegionHandle sh4_sq_handle = memory->AllocRegion(
SH4_SQ_START, SH4_SQ_SIZE,
make_delegate(&SH4::ReadSQ<uint8_t>, dc.sh4),
make_delegate(&SH4::ReadSQ<uint16_t>, dc.sh4),
make_delegate(&SH4::ReadSQ<uint32_t>, dc.sh4),
nullptr,
make_delegate(&SH4::WriteSQ<uint8_t>, dc.sh4),
make_delegate(&SH4::WriteSQ<uint16_t>, dc.sh4),
make_delegate(&SH4::WriteSQ<uint32_t>, dc.sh4),
nullptr);
// setup memory mapping for the allocated regions
MemoryMap memmap;
// mount physical regions to their respective physical address
memmap.Mount(a0_handle, AREA0_SIZE, AREA0_START);
memmap.Mount(a1_handle, AREA1_SIZE, AREA1_START);
memmap.Mount(a3_handle, AREA3_SIZE, AREA3_START);
memmap.Mount(a5_handle, AREA5_SIZE, AREA5_START);
memmap.Mount(a6_handle, AREA6_SIZE, AREA6_START);
memmap.Mount(a7_handle, AREA7_SIZE, AREA7_START);
memmap.Mount(holly_handle, HOLLY_REG_SIZE, HOLLY_REG_START);
memmap.Mount(pvr_reg_handle, PVR_REG_SIZE, PVR_REG_START);
// memmap.Mount(aica_reg_handle, AICA_REG_SIZE, AICA_REG_START);
memmap.Mount(wave_ram_handle, WAVE_RAM_SIZE, WAVE_RAM_START);
memmap.Mount(pvr_vram64_handle, PVR_VRAM64_SIZE, PVR_VRAM64_START);
memmap.Mount(ta_cmd_handle, TA_CMD_SIZE, TA_CMD_START);
memmap.Mount(ta_texture_handle, TA_TEXTURE_SIZE, TA_TEXTURE_START);
memmap.Mount(sh4_reg_handle, SH4_REG_SIZE, SH4_REG_START);
// main ram mirrors
memmap.Mirror(MAIN_RAM_1_START, MAIN_RAM_1_SIZE, MAIN_RAM_2_START);
memmap.Mirror(MAIN_RAM_1_START, MAIN_RAM_1_SIZE, MAIN_RAM_3_START);
memmap.Mirror(MAIN_RAM_1_START, MAIN_RAM_1_SIZE, MAIN_RAM_4_START);
// physical mirrors (ignoring p, alt and cache bits in bits 31-29)
memmap.Mirror(P0_1_START, P0_1_SIZE, P0_2_START);
memmap.Mirror(P0_1_START, P0_1_SIZE, P0_3_START);
memmap.Mirror(P0_1_START, P0_1_SIZE, P0_4_START);
memmap.Mirror(P0_1_START, P0_1_SIZE, P1_START);
memmap.Mirror(P0_1_START, P0_1_SIZE, P2_START);
memmap.Mirror(P0_1_START, P0_1_SIZE, P3_START);
memmap.Mirror(P0_1_START, P0_1_SIZE, P4_START);
// handle some special access only available in P4 after applying mirrors
memmap.Mount(sh4_cache_handle, SH4_CACHE_SIZE, SH4_CACHE_START);
memmap.Mount(sh4_sq_handle, SH4_SQ_SIZE, SH4_SQ_START);
if (!memory->Map(memmap)) {
return false;
}
dc.bios = memory->virtual_base() + BIOS_START;
dc.flash = memory->virtual_base() + FLASH_START;
dc.wave_ram = memory->virtual_base() + WAVE_RAM_START;
dc.palette_ram = memory->virtual_base() + PVR_PALETTE_START;
dc.video_ram = memory->virtual_base() + PVR_VRAM32_START;
// dc.aica_regs = memory->virtual_base() + AICA_REG_START;
dc.ram = memory->virtual_base() + MAIN_RAM_1_START;
return true;
}
// clang-format on
bool CreateDreamcast(Dreamcast &dc, renderer::Backend *rb) {
dc.scheduler = new Scheduler();
dc.memory = new Memory();
dc.aica = new AICA(&dc);
dc.gdrom = new GDROM(&dc);
dc.holly = new Holly(&dc);
dc.maple = new Maple(&dc);
dc.pvr = new PVR2(&dc);
dc.sh4 = new SH4(&dc);
dc.ta = new TileAccelerator(&dc);
dc.texcache = new TextureCache(&dc);
dc.rb = rb;
if (!MapMemory(dc) || //
!dc.aica->Init() || //
!dc.gdrom->Init() || //
!dc.holly->Init() || //
!dc.maple->Init() || //
!dc.pvr->Init() || //
!dc.sh4->Init() || //
!dc.ta->Init() || //
!dc.texcache->Init()) {
DestroyDreamcast(dc);
return false;
}
return true;
}
void DestroyDreamcast(Dreamcast &dc) {
delete dc.scheduler;
dc.scheduler = nullptr;
delete dc.memory;
dc.memory = nullptr;
delete dc.aica;
dc.aica = nullptr;
delete dc.gdrom;
dc.gdrom = nullptr;
delete dc.holly;
dc.holly = nullptr;
delete dc.maple;
dc.maple = nullptr;
delete dc.pvr;
dc.pvr = nullptr;
delete dc.sh4;
dc.sh4 = nullptr;
delete dc.ta;
dc.ta = nullptr;
delete dc.texcache;
dc.texcache = nullptr;
}
}
}

View File

@ -1,6 +1,8 @@
#ifndef DREAMCAST_H
#define DREAMCAST_H
#include "hw/machine.h"
// needed for register types
#include "hw/holly/holly.h"
#include "hw/holly/pvr2.h"
@ -136,11 +138,35 @@ enum {
#undef PVR_REG
};
struct Dreamcast {
// uint8_t *aica_regs;
class Dreamcast : public Machine {
public:
Dreamcast()
: holly_regs(),
pvr_regs(),
sh4(nullptr),
aica(nullptr),
gdrom(nullptr),
holly(nullptr),
maple(nullptr),
pvr(nullptr),
ta(nullptr),
texcache(nullptr),
trace_writer(nullptr) {}
Register holly_regs[HOLLY_REG_SIZE >> 2];
Register pvr_regs[PVR_REG_SIZE >> 2];
hw::sh4::SH4 *sh4;
hw::aica::AICA *aica;
hw::gdrom::GDROM *gdrom;
hw::holly::Holly *holly;
hw::maple::Maple *maple;
hw::holly::PVR2 *pvr;
hw::holly::TileAccelerator *ta;
hw::holly::TextureCache *texcache;
trace::TraceWriter *trace_writer;
#define HOLLY_REG(offset, name, flags, default, type) \
type &name = reinterpret_cast<type &>(holly_regs[name##_OFFSET].value);
#include "hw/holly/holly_regs.inc"
@ -150,32 +176,7 @@ struct Dreamcast {
type &name = reinterpret_cast<type &>(pvr_regs[name##_OFFSET].value);
#include "hw/holly/pvr2_regs.inc"
#undef PVR_REG
uint8_t *bios;
uint8_t *flash;
uint8_t *palette_ram;
uint8_t *ram;
uint8_t *video_ram;
uint8_t *wave_ram;
hw::Memory *memory;
hw::Scheduler *scheduler;
hw::aica::AICA *aica;
hw::gdrom::GDROM *gdrom;
hw::holly::Holly *holly;
hw::maple::Maple *maple;
hw::holly::PVR2 *pvr;
hw::sh4::SH4 *sh4;
hw::holly::TileAccelerator *ta;
hw::holly::TextureCache *texcache;
// not owned by us
renderer::Backend *rb;
trace::TraceWriter *trace_writer;
};
bool CreateDreamcast(Dreamcast &dc, renderer::Backend *rb);
void DestroyDreamcast(Dreamcast &dc);
}
}

View File

@ -13,7 +13,8 @@ using namespace re::hw::sh4;
(((fad & 0xff) << 16) | (fad & 0x00ff00) | ((fad & 0xff0000) >> 16))
GDROM::GDROM(Dreamcast *dc)
: dc_(dc),
: Device(*dc),
dc_(dc),
features_{0},
intreason_{0},
sectnum_{0},

View File

@ -3,6 +3,7 @@
#include <memory>
#include "hw/gdrom/disc.h"
#include "hw/machine.h"
namespace re {
namespace hw {
@ -10,7 +11,7 @@ namespace holly {
class Holly;
}
struct Dreamcast;
class Dreamcast;
struct Register;
class Memory;
@ -202,14 +203,14 @@ enum DataMask {
MASK_OTHER = 0x1
};
class GDROM {
class GDROM : public Device {
friend class holly::Holly;
public:
GDROM(Dreamcast *dc);
~GDROM();
bool Init();
bool Init() final;
void SetDisc(std::unique_ptr<Disc> disc);

View File

@ -2,6 +2,7 @@
#include "hw/holly/holly.h"
#include "hw/maple/maple.h"
#include "hw/dreamcast.h"
#include "hw/memory.h"
using namespace re;
using namespace re::hw;
@ -12,7 +13,7 @@ using namespace re::hw::sh4;
using namespace re::renderer;
using namespace re::sys;
Holly::Holly(Dreamcast *dc) : dc_(dc) {}
Holly::Holly(Dreamcast *dc) : Device(*dc), MemoryInterface(this), dc_(dc) {}
bool Holly::Init() {
holly_regs_ = dc_->holly_regs;
@ -77,9 +78,19 @@ void Holly::UnrequestInterrupt(HollyInterrupt intr) {
ForwardRequestInterrupts();
}
template uint8_t Holly::ReadRegister(uint32_t addr);
template uint16_t Holly::ReadRegister(uint32_t addr);
template uint32_t Holly::ReadRegister(uint32_t addr);
void Holly::MapPhysicalMemory(Memory &memory, MemoryMap &memmap) {
RegionHandle holly_handle = memory.AllocRegion(
HOLLY_REG_START, 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_START);
}
template <typename T>
T Holly::ReadRegister(uint32_t addr) {
// service all devices connected through the system bus
@ -117,9 +128,6 @@ T Holly::ReadRegister(uint32_t addr) {
return static_cast<T>(reg.value);
}
template void Holly::WriteRegister(uint32_t addr, uint8_t value);
template void Holly::WriteRegister(uint32_t addr, uint16_t value);
template void Holly::WriteRegister(uint32_t addr, uint32_t value);
template <typename T>
void Holly::WriteRegister(uint32_t addr, T value) {
// service all devices connected through the system bus

View File

@ -2,6 +2,7 @@
#define HOLLY_H
#include <stdint.h>
#include "hw/machine.h"
namespace re {
namespace hw {
@ -15,11 +16,9 @@ namespace sh4 {
class SH4;
}
struct Dreamcast;
class Dreamcast;
struct Register;
extern bool MapMemory(Dreamcast &dc);
namespace holly {
// interrupts
@ -157,17 +156,18 @@ enum HollyInterrupt : uint64_t {
HOLLY_INTC_CIHINT = HOLLY_INTC_ERR | 0x80000000
};
class Holly {
friend bool re::hw::MapMemory(Dreamcast &dc);
class Holly : public Device, public MemoryInterface {
public:
Holly(Dreamcast *dc);
bool Init();
bool Init() final;
void RequestInterrupt(HollyInterrupt intr);
void UnrequestInterrupt(HollyInterrupt intr);
protected:
void MapPhysicalMemory(Memory &memory, MemoryMap &memmap) final;
private:
template <typename T>
T ReadRegister(uint32_t addr);

View File

@ -2,6 +2,7 @@
#include "hw/holly/pvr2.h"
#include "hw/holly/tile_accelerator.h"
#include "hw/dreamcast.h"
#include "hw/memory.h"
using namespace re::hw;
using namespace re::hw::holly;
@ -9,7 +10,12 @@ using namespace re::hw::sh4;
using namespace re::renderer;
PVR2::PVR2(Dreamcast *dc)
: dc_(dc), line_timer_(INVALID_TIMER), current_scanline_(0), rps_(0.0f) {}
: Device(*dc),
MemoryInterface(this),
dc_(dc),
line_timer_(INVALID_TIMER),
current_scanline_(0),
rps_(0.0f) {}
bool PVR2::Init() {
scheduler_ = dc_->scheduler;
@ -17,8 +23,8 @@ bool PVR2::Init() {
ta_ = dc_->ta;
texcache_ = dc_->texcache;
pvr_regs_ = dc_->pvr_regs;
palette_ram_ = dc_->palette_ram;
video_ram_ = dc_->video_ram;
palette_ram_ = dc_->memory->TranslateVirtual(PVR_PALETTE_START);
video_ram_ = dc_->memory->TranslateVirtual(PVR_VRAM32_START);
// initialize registers
#define PVR_REG(addr, name, flags, default, type) \
@ -26,15 +32,30 @@ bool PVR2::Init() {
#include "hw/holly/pvr2_regs.inc"
#undef PVR_REG
// register scanline timer
line_timer_ = scheduler_->AllocTimer(std::bind(&PVR2::NextScanline, this));
// configure initial vsync interval
ReconfigureSPG();
return true;
}
void PVR2::MapPhysicalMemory(Memory &memory, MemoryMap &memmap) {
RegionHandle pvr_reg_handle = memory.AllocRegion(
PVR_REG_START, 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_START, 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_START);
memmap.Mount(pvr_vram64_handle, PVR_VRAM64_SIZE, PVR_VRAM64_START);
}
uint32_t PVR2::ReadRegister(uint32_t addr) {
uint32_t offset = addr >> 2;
Register &reg = pvr_regs_[offset];
@ -120,17 +141,12 @@ static uint32_t MAP64(uint32_t addr) {
(addr & 0x3));
}
template uint8_t PVR2::ReadVRamInterleaved(uint32_t addr);
template uint16_t PVR2::ReadVRamInterleaved(uint32_t addr);
template uint32_t PVR2::ReadVRamInterleaved(uint32_t addr);
template <typename T>
T PVR2::ReadVRamInterleaved(uint32_t addr) {
addr = MAP64(addr);
return re::load<T>(&video_ram_[addr]);
}
template void PVR2::WriteVRamInterleaved(uint32_t addr, uint16_t value);
template void PVR2::WriteVRamInterleaved(uint32_t addr, uint32_t value);
template <typename T>
void PVR2::WriteVRamInterleaved(uint32_t addr, T value) {
addr = MAP64(addr);
@ -145,19 +161,25 @@ void PVR2::ReconfigureSPG() {
}
// hcount is number of pixel clock cycles per line - 1
int line_clock = pixel_clock / (dc_->SPG_LOAD.hcount + 1);
line_clock_ = pixel_clock / (dc_->SPG_LOAD.hcount + 1);
if (dc_->SPG_CONTROL.interlace) {
line_clock *= 2;
line_clock_ *= 2;
}
LOG_INFO(
"ReconfigureSPG: pixel_clock %d, line_clock %d, vcount %d, hcount %d, "
"interlace %d, vbstart %d, vbend %d",
pixel_clock, line_clock, dc_->SPG_LOAD.vcount, dc_->SPG_LOAD.hcount,
pixel_clock, line_clock_, dc_->SPG_LOAD.vcount, dc_->SPG_LOAD.hcount,
dc_->SPG_CONTROL.interlace, dc_->SPG_VBLANK.vbstart,
dc_->SPG_VBLANK.vbend);
scheduler_->AdjustTimer(line_timer_, HZ_TO_NANO(line_clock));
if (line_timer_ != INVALID_TIMER) {
scheduler_->CancelTimer(line_timer_);
line_timer_ = nullptr;
}
line_timer_ = scheduler_->ScheduleTimer(
re::make_delegate(&PVR2::NextScanline, this), HZ_TO_NANO(line_clock_));
}
void PVR2::NextScanline() {
@ -190,4 +212,8 @@ void PVR2::NextScanline() {
// FIXME toggle SPG_STATUS.fieldnum on vblank?
// if (!was_vsync && dc_->SPG_STATUS.vsync) {
// }
// reschedule
line_timer_ = scheduler_->ScheduleTimer(
re::make_delegate(&PVR2::NextScanline, this), HZ_TO_NANO(line_clock_));
}

View File

@ -2,16 +2,15 @@
#define PVR_CLX2_H
#include <stdint.h>
#include "hw/machine.h"
#include "hw/scheduler.h"
namespace re {
namespace hw {
struct Dreamcast;
class Dreamcast;
struct Register;
extern bool MapMemory(Dreamcast &dc);
namespace holly {
class Holly;
@ -199,15 +198,16 @@ union TA_ISP_BASE_T {
};
};
class PVR2 {
friend bool re::hw::MapMemory(Dreamcast &dc);
class PVR2 : public Device, public MemoryInterface {
public:
PVR2(Dreamcast *dc);
float rps() { return rps_; }
bool Init();
bool Init() final;
protected:
void MapPhysicalMemory(Memory &memory, MemoryMap &memmap) final;
private:
uint32_t ReadRegister(uint32_t addr);
@ -231,6 +231,7 @@ class PVR2 {
uint8_t *video_ram_;
TimerHandle line_timer_;
int line_clock_;
uint32_t current_scanline_;
std::chrono::high_resolution_clock::time_point last_render_;

View File

@ -2,6 +2,7 @@
#include "hw/holly/texture_cache.h"
#include "hw/holly/tile_accelerator.h"
#include "hw/dreamcast.h"
#include "hw/memory.h"
#include "trace/trace.h"
using namespace re::hw;
@ -9,8 +10,8 @@ using namespace re::hw::holly;
using namespace re::renderer;
using namespace re::sys;
TextureCache::TextureCache(Dreamcast *dc)
: dc_(dc), trace_writer_(nullptr), num_invalidated_(0) {}
TextureCache::TextureCache(Dreamcast *dc, Backend *rb)
: dc_(dc), rb_(rb), trace_writer_(nullptr), num_invalidated_(0) {}
bool TextureCache::Init() { return true; }
@ -39,7 +40,7 @@ TextureHandle TextureCache::GetTexture(const TSP &tsp, const TCW &tcw,
uint32_t texture_addr = tcw.texture_addr << 3;
// get the texture data
uint8_t *video_ram = dc_->video_ram;
uint8_t *video_ram = dc_->memory->TranslateVirtual(PVR_VRAM32_START);
uint8_t *texture = &video_ram[texture_addr];
int width = 8 << tsp.texture_u_size;
int height = 8 << tsp.texture_v_size;
@ -49,7 +50,7 @@ TextureHandle TextureCache::GetTexture(const TSP &tsp, const TCW &tcw,
int texture_size = (width * height * element_size_bits) >> 3;
// get the palette data
uint8_t *palette_ram = dc_->palette_ram;
uint8_t *palette_ram = dc_->memory->TranslateVirtual(PVR_PALETTE_START);
uint8_t *palette = nullptr;
uint32_t palette_addr = 0;
int palette_size = 0;
@ -174,7 +175,7 @@ void TextureCache::Invalidate(TextureCacheMap::iterator it) {
RemoveAccessWatch(entry.palette_watch);
}
dc_->rb->FreeTexture(entry.handle);
rb_->FreeTexture(entry.handle);
textures_.erase(it);
}

View File

@ -5,8 +5,8 @@
#include <unordered_map>
#include "core/interval_tree.h"
#include "hw/holly/tile_renderer.h"
#include "hw/memory.h"
#include "renderer/backend.h"
#include "sys/memory.h"
namespace re {
namespace trace {
@ -14,7 +14,8 @@ class TraceWriter;
}
namespace hw {
struct Dreamcast;
class Dreamcast;
class Memory;
namespace holly {
@ -33,7 +34,7 @@ struct TextureEntry {
class TextureCache : public TextureProvider {
public:
TextureCache(hw::Dreamcast *dc);
TextureCache(hw::Dreamcast *dc, renderer::Backend *rb);
bool Init();
renderer::TextureHandle GetTexture(const TSP &tsp, const TCW &tcw,
@ -51,6 +52,7 @@ class TextureCache : public TextureProvider {
void Invalidate(TextureCacheMap::iterator it);
hw::Dreamcast *dc_;
renderer::Backend *rb_;
trace::TraceWriter *trace_writer_;
TextureCacheMap textures_;
TextureSet pending_invalidations_;

View File

@ -204,7 +204,8 @@ int TileAccelerator::GetVertexType(const PCW &pcw) {
pcw.para_type * TA_NUM_LISTS + pcw.list_type];
}
TileAccelerator::TileAccelerator(Dreamcast *dc) : dc_(dc), contexts_() {
TileAccelerator::TileAccelerator(Dreamcast *dc)
: Device(*dc), MemoryInterface(this), dc_(dc), contexts_() {
// initialize context queue
for (int i = 0; i < MAX_CONTEXTS; i++) {
free_contexts_.push(&contexts_[i]);
@ -215,7 +216,7 @@ bool TileAccelerator::Init() {
memory_ = dc_->memory;
holly_ = dc_->holly;
texcache_ = dc_->texcache;
video_ram_ = dc_->video_ram;
video_ram_ = dc_->memory->TranslateVirtual(PVR_VRAM32_START);
return true;
}
@ -347,6 +348,20 @@ TileContext *TileAccelerator::GetLastContext() {
return pending_contexts_.front();
}
void TileAccelerator::MapPhysicalMemory(Memory &memory, MemoryMap &memmap) {
RegionHandle ta_cmd_handle = memory.AllocRegion(
TA_CMD_START, TA_CMD_SIZE, nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, make_delegate(&TileAccelerator::WriteCommand, this), nullptr);
RegionHandle ta_texture_handle = memory.AllocRegion(
TA_TEXTURE_START, TA_TEXTURE_SIZE, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr, make_delegate(&TileAccelerator::WriteTexture, this),
nullptr);
memmap.Mount(ta_cmd_handle, TA_CMD_SIZE, TA_CMD_START);
memmap.Mount(ta_texture_handle, TA_TEXTURE_SIZE, TA_TEXTURE_START);
}
void TileAccelerator::WriteCommand(uint32_t addr, uint32_t value) {
WriteContext(dc_->TA_ISP_BASE.base_address, value);
}

View File

@ -5,6 +5,7 @@
#include <queue>
#include <unordered_map>
#include "hw/holly/tile_renderer.h"
#include "hw/machine.h"
#include "renderer/backend.h"
namespace re {
@ -14,11 +15,9 @@ class TraceWriter;
namespace hw {
struct Dreamcast;
class Dreamcast;
class Memory;
extern bool MapMemory(Dreamcast &dc);
namespace holly {
class Holly;
@ -483,9 +482,7 @@ struct TileContext {
typedef std::unordered_map<TextureKey, TileContext *> TileContextMap;
typedef std::queue<TileContext *> TileContextQueue;
class TileAccelerator {
friend bool re::hw::MapMemory(Dreamcast &dc);
class TileAccelerator : public Device, public MemoryInterface {
public:
static int GetParamSize(const PCW &pcw, int vertex_type);
static int GetPolyType(const PCW &pcw);
@ -493,7 +490,7 @@ class TileAccelerator {
TileAccelerator(Dreamcast *dc);
bool Init();
bool Init() final;
void SoftReset();
void InitContext(uint32_t addr);
@ -502,6 +499,9 @@ class TileAccelerator {
TileContext *GetLastContext();
protected:
void MapPhysicalMemory(Memory &memory, MemoryMap &memmap) final;
private:
void WriteCommand(uint32_t addr, uint32_t value);
void WriteTexture(uint32_t addr, uint32_t value);

38
src/hw/machine.cc Normal file
View File

@ -0,0 +1,38 @@
#include "hw/machine.h"
#include "hw/memory.h"
#include "hw/scheduler.h"
using namespace re;
using namespace re::hw;
ExecuteInterface::ExecuteInterface(Device *device) { device->execute_ = this; }
MemoryInterface::MemoryInterface(Device *device) { device->memory_ = this; }
Device::Device(Machine &machine) : execute_(nullptr), memory_(nullptr) {
machine.devices.push_back(this);
}
Machine::Machine() {
memory = new Memory(*this);
scheduler = new Scheduler(*this);
}
Machine::~Machine() {
delete memory;
delete scheduler;
}
bool Machine::Init() {
if (!memory->Init()) {
return false;
}
for (auto device : devices) {
if (!device->Init()) {
return false;
}
}
return true;
}

68
src/hw/machine.h Normal file
View File

@ -0,0 +1,68 @@
#ifndef MACHINE_H
#define MACHINE_H
#include <chrono>
#include <vector>
namespace re {
namespace hw {
class Device;
class Machine;
class Memory;
class MemoryMap;
class Scheduler;
class ExecuteInterface {
public:
ExecuteInterface(Device *device);
virtual ~ExecuteInterface() = default;
virtual void Run(const std::chrono::nanoseconds &delta) = 0;
};
class MemoryInterface {
public:
MemoryInterface(Device *device);
virtual ~MemoryInterface() = default;
virtual void MapPhysicalMemory(Memory &memory, MemoryMap &memmap) {}
virtual void MapVirtualMemory(Memory &memory, MemoryMap &memmap) {}
};
class Device {
friend class ExecuteInterface;
friend class MemoryInterface;
public:
virtual ~Device() = default;
ExecuteInterface *execute() { return execute_; }
MemoryInterface *memory() { return memory_; }
Device(Machine &machine);
virtual bool Init() { return true; }
private:
ExecuteInterface *execute_;
MemoryInterface *memory_;
};
class Machine {
friend class Device;
public:
Machine();
virtual ~Machine();
bool Init();
Memory *memory;
Scheduler *scheduler;
std::vector<Device *> devices;
};
}
}
#endif

View File

@ -10,7 +10,7 @@ using namespace re::hw::maple;
using namespace re::hw::sh4;
using namespace re::sys;
Maple::Maple(Dreamcast *dc) : dc_(dc), devices_() {
Maple::Maple(Dreamcast *dc) : Device(*dc), dc_(dc), devices_() {
// default controller device
devices_[0] = std::unique_ptr<MapleController>(new MapleController());
}

View File

@ -2,6 +2,7 @@
#define MAPLE_H
#include <memory>
#include "hw/machine.h"
#include "sys/keycode.h"
namespace re {
@ -10,7 +11,7 @@ namespace holly {
class Holly;
}
struct Dreamcast;
class Dreamcast;
struct Register;
class Memory;
@ -99,13 +100,13 @@ class MapleDevice {
virtual bool HandleFrame(const MapleFrame &frame, MapleFrame &res) = 0;
};
class Maple {
class Maple : public Device {
friend class holly::Holly;
public:
Maple(Dreamcast *dc);
bool Init();
bool Init() final;
bool HandleInput(int port, sys::Keycode key, int16_t value);
void VBlank();

View File

@ -1,4 +1,6 @@
#include "core/math.h"
#include "core/memory.h"
#include "hw/machine.h"
#include "hw/memory.h"
using namespace re;
@ -49,24 +51,22 @@ static inline int RegionIndex(const PageEntry &page) {
MemoryMap::MemoryMap() : entries_(), num_entries_(0) {}
MapEntryHandle MemoryMap::Mount(RegionHandle handle, uint32_t size,
uint32_t virtual_addr) {
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;
return entry->handle;
}
MapEntryHandle MemoryMap::Mirror(uint32_t physical_addr, uint32_t size,
uint32_t 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;
return entry->handle;
}
MapEntry *MemoryMap::AllocEntry() {
@ -101,8 +101,9 @@ void Memory::W64(Memory *memory, uint32_t addr, uint64_t value) {
memory->W64(addr, value);
}
Memory::Memory()
: shmem_(SHMEM_INVALID),
Memory::Memory(Machine &machine)
: machine_(machine),
shmem_(SHMEM_INVALID),
physical_base_(nullptr),
virtual_base_(nullptr),
protected_base_(nullptr) {
@ -122,11 +123,39 @@ Memory::~Memory() {
}
bool Memory::Init() {
// create the backing shared memory object
if (!CreateSharedMemory()) {
return false;
}
return true;
// iterate each device, giving it a chance to append to the memory map
MemoryMap memmap;
for (auto device : machine_.devices) {
if (!device->memory()) {
continue;
}
device->memory()->MapPhysicalMemory(*this, memmap);
}
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) {

View File

@ -9,6 +9,8 @@
namespace re {
namespace hw {
class Machine;
typedef uint8_t RegionHandle;
enum {
@ -49,14 +51,12 @@ class MemoryMap {
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.
MapEntryHandle Mount(RegionHandle handle, uint32_t virtual_addr,
uint32_t size);
// 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.
MapEntryHandle Mirror(uint32_t physical_addr, uint32_t size,
uint32_t virtual_addr);
// mirror arbitary, page-aligned ranges of memory to a virtual address
void Mirror(uint32_t physical_addr, uint32_t size, uint32_t virtual_addr);
private:
MapEntry *AllocEntry();
@ -156,7 +156,7 @@ class Memory {
static void W32(Memory *memory, uint32_t addr, uint32_t value);
static void W64(Memory *memory, uint32_t addr, uint64_t value);
Memory();
Memory(Machine &machine);
~Memory();
size_t total_size() { return ADDRESS_SPACE_SIZE; }
@ -171,8 +171,8 @@ class Memory {
W8Delegate w8, W16Delegate w16, W32Delegate w32,
W64Delegate w64);
bool Map(const MemoryMap &map);
uint8_t *TranslateVirtual(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);
@ -194,6 +194,7 @@ class Memory {
MemoryRegion *AllocRegion();
bool Map(const MemoryMap &map);
void Unmap();
bool GetNextContiguousRegion(uint32_t *physical_start,
@ -212,6 +213,8 @@ class Memory {
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_;

View File

@ -1,79 +1,96 @@
#include "core/assert.h"
#include "core/minmax_heap.h"
#include "hw/machine.h"
#include "hw/scheduler.h"
using namespace re::hw;
namespace re {
namespace hw {
const std::chrono::nanoseconds SUSPENDED(INT64_MAX);
}
}
Scheduler::Scheduler()
: base_time_(), timers_(), num_timers_(0), next_handle_(0) {}
Scheduler::Scheduler(Machine &machine)
: machine_(machine), arena_(sizeof(Timer) * 128), timers_(), base_time_() {}
void Scheduler::Tick(const std::chrono::nanoseconds &delta) {
auto next_time = base_time_ + delta;
auto target_time = base_time_ + delta;
// fire callbacks for all expired timers
for (int i = 0; i < num_timers_; i++) {
Timer &timer = timers_[i];
while (base_time_ < target_time) {
auto next_time = target_time;
if (timer.remaining == SUSPENDED) {
continue;
// run devices up to the next timer
Timer *next_timer = timers_.head();
if (next_timer && next_timer->expire < next_time) {
next_time = next_timer->expire;
}
timer.remaining -= delta;
// go ahead and update base time before running devices and expiring timers
// in case one of them schedules a new timer
auto slice = next_time - base_time_;
base_time_ += slice;
while (timer.remaining <= std::chrono::nanoseconds::zero()) {
timer.callback(timer.period);
timer.remaining += timer.period;
for (auto device : machine_.devices) {
if (!device->execute()) {
continue;
}
device->execute()->Run(slice);
}
// execute expired timers
while (next_timer) {
if (next_timer->expire > base_time_) {
break;
}
next_timer->delegate();
// free the timer
Timer *next_next_timer = next_timer->next();
timers_.Remove(next_timer);
free_timers_.Append(next_timer);
next_timer = next_next_timer;
}
}
base_time_ = next_time;
CHECK_EQ(base_time_, target_time);
}
TimerHandle Scheduler::AllocTimer(TimerCallback callback) {
CHECK_LT(num_timers_, MAX_TIMERS);
TimerHandle Scheduler::ScheduleTimer(TimerDelegate delegate,
const std::chrono::nanoseconds &period) {
// allocate a timer instance from the pool
Timer *timer = nullptr;
Timer &timer = timers_[num_timers_++];
timer.handle = next_handle_++;
timer.callback = callback;
timer.period = SUSPENDED;
timer.remaining = SUSPENDED;
if (free_timers_.head()) {
timer = free_timers_.head();
free_timers_.Remove(timer);
} else {
timer = arena_.Alloc<Timer>();
new (timer) Timer();
}
return timer.handle;
}
timer->expire = base_time_ + period;
timer->delegate = delegate;
void Scheduler::AdjustTimer(TimerHandle handle,
const std::chrono::nanoseconds &period) {
Timer &timer = GetTimer(handle);
timer.period = period;
timer.remaining = period;
}
// insert it into the sorted list
Timer *after = nullptr;
void Scheduler::FreeTimer(TimerHandle handle) {
// swap timer to be removed with last timer to keep a contiguous array
Timer &timer = GetTimer(handle);
timer = timers_[--num_timers_];
for (auto t : timers_) {
if (t->expire > timer->expire) {
break;
}
after = t;
}
timers_.Insert(after, timer);
return static_cast<TimerHandle>(timer);
}
std::chrono::nanoseconds Scheduler::RemainingTime(TimerHandle handle) {
Timer &timer = GetTimer(handle);
return timer.remaining;
Timer *timer = static_cast<Timer *>(handle);
return timer->expire - base_time_;
}
Timer &Scheduler::GetTimer(TimerHandle handle) {
Timer *timer = nullptr;
for (int i = 0; i < num_timers_; i++) {
Timer *t = &timers_[i];
if (t->handle == handle) {
timer = t;
break;
}
}
CHECK_NOTNULL(timer);
return *timer;
void Scheduler::CancelTimer(TimerHandle handle) {
Timer *timer = static_cast<Timer *>(handle);
timers_.Remove(timer);
free_timers_.Append(timer);
}

View File

@ -3,71 +3,60 @@
#include <stdint.h>
#include <chrono>
#include <functional>
#include "core/arena.h"
#include "core/delegate.h"
#include "core/intrusive_list.h"
namespace re {
namespace hw {
enum {
MAX_TIMERS = 16,
class Machine;
typedef re::delegate<void()> TimerDelegate;
struct Timer : public IntrusiveListNode<Timer> {
std::chrono::high_resolution_clock::time_point expire;
TimerDelegate delegate;
};
typedef int64_t TimerHandle;
enum : TimerHandle {
INVALID_TIMER = -1,
};
typedef std::function<void(const std::chrono::nanoseconds &)> TimerCallback;
struct Timer {
TimerHandle handle;
std::chrono::nanoseconds period;
std::chrono::nanoseconds remaining;
TimerCallback callback;
};
enum : int64_t {
NS_PER_SEC = 1000000000ll,
};
extern const std::chrono::nanoseconds SUSPENDED;
typedef Timer *TimerHandle;
static const TimerHandle INVALID_TIMER = nullptr;
static const int64_t NS_PER_SEC = 1000000000ll;
static inline std::chrono::nanoseconds HZ_TO_NANO(int64_t hz) {
return std::chrono::nanoseconds(
static_cast<int64_t>(NS_PER_SEC / static_cast<float>(hz)));
float nano = NS_PER_SEC / static_cast<float>(hz);
return std::chrono::nanoseconds(static_cast<int64_t>(nano));
}
static inline int64_t NANO_TO_CYCLES(const std::chrono::nanoseconds &ns,
int64_t hz) {
return static_cast<int64_t>((ns.count() / static_cast<float>(NS_PER_SEC)) *
hz);
float cycles = (ns.count() / static_cast<float>(NS_PER_SEC)) * hz;
return static_cast<int64_t>(cycles);
}
static inline std::chrono::nanoseconds CYCLES_TO_NANO(int64_t cycles,
int64_t hz) {
return std::chrono::nanoseconds(
static_cast<int64_t>((cycles / static_cast<float>(hz)) * NS_PER_SEC));
float nano = (cycles / static_cast<float>(hz)) * NS_PER_SEC;
return std::chrono::nanoseconds(static_cast<int64_t>(nano));
}
class Scheduler {
public:
Scheduler();
Scheduler(Machine &machine);
void Tick(const std::chrono::nanoseconds &delta);
TimerHandle AllocTimer(TimerCallback callback);
void FreeTimer(TimerHandle handle);
void AdjustTimer(TimerHandle, const std::chrono::nanoseconds &period);
TimerHandle ScheduleTimer(TimerDelegate delegate,
const std::chrono::nanoseconds &period);
std::chrono::nanoseconds RemainingTime(TimerHandle handle);
void CancelTimer(TimerHandle handle);
private:
Timer &GetTimer(TimerHandle handle);
Machine &machine_;
Arena arena_;
IntrusiveList<Timer> timers_;
IntrusiveList<Timer> free_timers_;
std::chrono::high_resolution_clock::time_point base_time_;
Timer timers_[MAX_TIMERS];
int num_timers_;
TimerHandle next_handle_;
};
}
}

View File

@ -28,11 +28,18 @@ enum {
};
SH4::SH4(Dreamcast *dc)
: dc_(dc),
: Device(*dc),
MemoryInterface(this),
ExecuteInterface(this),
dc_(dc),
code_cache_(nullptr),
pending_cache_reset_(false),
requested_interrupts_(0),
pending_interrupts_(0) {}
pending_interrupts_(0),
tmu_timers_{INVALID_TIMER, INVALID_TIMER, INVALID_TIMER},
tmu_delegates_{re::make_delegate(&SH4::ExpireTimer<0>, this),
re::make_delegate(&SH4::ExpireTimer<1>, this),
re::make_delegate(&SH4::ExpireTimer<2>, this)} {}
SH4::~SH4() { delete code_cache_; }
@ -68,27 +75,16 @@ bool SH4::Init() {
// reset interrupts
ReprioritizeInterrupts();
// register timer
TimerHandle timer =
scheduler_->AllocTimer(std::bind(&SH4::Run, this, std::placeholders::_1));
scheduler_->AdjustTimer(timer, std::chrono::nanoseconds(446428));
// initialize tmu timers
for (int i = 0; i < 3; i++) {
tmu_timers_[i] = scheduler_->AllocTimer(
[this, i](const std::chrono::nanoseconds &) { ExpireTimer(i); });
}
return true;
}
void SH4::SetPC(uint32_t pc) { ctx_.pc = pc; }
void SH4::Run(const std::chrono::nanoseconds &period) {
void SH4::Run(const std::chrono::nanoseconds &delta) {
PROFILER_RUNTIME("SH4::Execute");
// execute at least 1 cycle. the tests rely on this to step block by block
int64_t cycles = std::max(NANO_TO_CYCLES(period, SH4_CLOCK_FREQ), 1ll);
int64_t cycles = std::max(NANO_TO_CYCLES(delta, SH4_CLOCK_FREQ), 1ll);
// set current sh4 instance for CompilePC
s_current_cpu = this;
@ -143,6 +139,72 @@ void SH4::UnrequestInterrupt(Interrupt intr) {
UpdatePendingInterrupts();
}
void SH4::MapPhysicalMemory(Memory &memory, MemoryMap &memmap) {
// area 2 and 4 are unused
RegionHandle a0_handle = memory.AllocRegion(AREA0_START, AREA0_SIZE);
RegionHandle a1_handle = memory.AllocRegion(AREA1_START, AREA1_SIZE);
RegionHandle a3_handle = memory.AllocRegion(AREA3_START, AREA3_SIZE);
RegionHandle a5_handle = memory.AllocRegion(AREA5_START, AREA5_SIZE);
RegionHandle a6_handle = memory.AllocRegion(AREA6_START, AREA6_SIZE);
RegionHandle a7_handle = memory.AllocRegion(AREA7_START, AREA7_SIZE);
RegionHandle sh4_reg_handle = memory.AllocRegion(
SH4_REG_START, 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_START);
memmap.Mount(a1_handle, AREA1_SIZE, AREA1_START);
memmap.Mount(a3_handle, AREA3_SIZE, AREA3_START);
memmap.Mount(a5_handle, AREA5_SIZE, AREA5_START);
memmap.Mount(a6_handle, AREA6_SIZE, AREA6_START);
memmap.Mount(a7_handle, AREA7_SIZE, AREA7_START);
memmap.Mount(sh4_reg_handle, SH4_REG_SIZE, SH4_REG_START);
}
void SH4::MapVirtualMemory(Memory &memory, MemoryMap &memmap) {
RegionHandle sh4_cache_handle =
memory.AllocRegion(SH4_CACHE_START, 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_START, 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_START, MAIN_RAM_1_SIZE, MAIN_RAM_2_START);
memmap.Mirror(MAIN_RAM_1_START, MAIN_RAM_1_SIZE, MAIN_RAM_3_START);
memmap.Mirror(MAIN_RAM_1_START, MAIN_RAM_1_SIZE, MAIN_RAM_4_START);
// physical mirrors (ignoring p, alt and cache bits in bits 31-29)
memmap.Mirror(P0_1_START, P0_1_SIZE, P0_2_START);
memmap.Mirror(P0_1_START, P0_1_SIZE, P0_3_START);
memmap.Mirror(P0_1_START, P0_1_SIZE, P0_4_START);
memmap.Mirror(P0_1_START, P0_1_SIZE, P1_START);
memmap.Mirror(P0_1_START, P0_1_SIZE, P2_START);
memmap.Mirror(P0_1_START, P0_1_SIZE, P3_START);
memmap.Mirror(P0_1_START, P0_1_SIZE, P4_START);
// handle some special access only available in P4 after applying mirrors
memmap.Mount(sh4_cache_handle, SH4_CACHE_SIZE, SH4_CACHE_START);
memmap.Mount(sh4_sq_handle, SH4_SQ_SIZE, SH4_SQ_START);
}
uint32_t SH4::CompilePC() {
SH4CodeCache *code_cache = s_current_cpu->code_cache_;
SH4Context *ctx = &s_current_cpu->ctx_;
@ -211,9 +273,6 @@ void SH4::SwapFPRegisterBank() {
}
}
template uint8_t SH4::ReadRegister(uint32_t addr);
template uint16_t SH4::ReadRegister(uint32_t addr);
template uint32_t SH4::ReadRegister(uint32_t addr);
template <typename T>
T SH4::ReadRegister(uint32_t addr) {
// translate from 64mb space to our 16kb space
@ -299,9 +358,6 @@ T SH4::ReadRegister(uint32_t addr) {
return static_cast<T>(area7_[addr]);
}
template void SH4::WriteRegister(uint32_t addr, uint8_t value);
template void SH4::WriteRegister(uint32_t addr, uint16_t value);
template void SH4::WriteRegister(uint32_t addr, uint32_t value);
template <typename T>
void SH4::WriteRegister(uint32_t addr, T value) {
// translate from 64mb space to our 16kb space
@ -354,10 +410,6 @@ void SH4::WriteRegister(uint32_t addr, T value) {
#define CACHE_OFFSET(addr, OIX) \
((OIX ? ((addr & 0x2000000) >> 13) : ((addr & 0x2000) >> 1)) | (addr & 0xfff))
template uint8_t SH4::ReadCache(uint32_t addr);
template uint16_t SH4::ReadCache(uint32_t addr);
template uint32_t SH4::ReadCache(uint32_t addr);
template uint64_t SH4::ReadCache(uint32_t addr);
template <typename T>
T SH4::ReadCache(uint32_t addr) {
CHECK_EQ(CCR.ORA, 1u);
@ -365,10 +417,6 @@ T SH4::ReadCache(uint32_t addr) {
return re::load<T>(&cache_[addr]);
}
template void SH4::WriteCache(uint32_t addr, uint8_t value);
template void SH4::WriteCache(uint32_t addr, uint16_t value);
template void SH4::WriteCache(uint32_t addr, uint32_t value);
template void SH4::WriteCache(uint32_t addr, uint64_t value);
template <typename T>
void SH4::WriteCache(uint32_t addr, T value) {
CHECK_EQ(CCR.ORA, 1u);
@ -376,9 +424,6 @@ void SH4::WriteCache(uint32_t addr, T value) {
re::store(&cache_[addr], value);
}
template uint8_t SH4::ReadSQ(uint32_t addr);
template uint16_t SH4::ReadSQ(uint32_t addr);
template uint32_t SH4::ReadSQ(uint32_t addr);
template <typename T>
T SH4::ReadSQ(uint32_t addr) {
uint32_t sqi = (addr & 0x20) >> 5;
@ -386,9 +431,6 @@ T SH4::ReadSQ(uint32_t addr) {
return static_cast<T>(ctx_.sq[sqi][idx]);
}
template void SH4::WriteSQ(uint32_t addr, uint8_t value);
template void SH4::WriteSQ(uint32_t addr, uint16_t value);
template void SH4::WriteSQ(uint32_t addr, uint32_t value);
template <typename T>
void SH4::WriteSQ(uint32_t addr, T value) {
uint32_t sqi = (addr & 0x20) >> 5;
@ -514,12 +556,13 @@ void SH4::UpdateTimerStart() {
if (TSTR(i)) {
// schedule the timer if not already started
if (scheduler_->RemainingTime(handle) == SUSPENDED) {
ScheduleTimer(i, TCNT(i), TCR(i));
if (handle == INVALID_TIMER) {
RescheduleTimer(i, TCNT(i), TCR(i));
}
} else {
} else if (handle != INVALID_TIMER) {
// disable the timer
scheduler_->AdjustTimer(handle, SUSPENDED);
scheduler_->CancelTimer(handle);
handle = INVALID_TIMER;
}
}
}
@ -528,7 +571,7 @@ void SH4::UpdateTimerControl(uint32_t n) {
if (TSTR(n)) {
// timer is already scheduled, reschedule it with the current cycle count,
// but the new TCR value
ScheduleTimer(n, TimerCount(n), TCR(n));
RescheduleTimer(n, TimerCount(n), TCR(n));
}
// if the timer no longer cares about underflow interrupts, unrequest
@ -539,7 +582,7 @@ void SH4::UpdateTimerControl(uint32_t n) {
void SH4::UpdateTimerCount(uint32_t n) {
if (TSTR(n)) {
ScheduleTimer(n, TCNT(n), TCR(n));
RescheduleTimer(n, TCNT(n), TCR(n));
}
}
@ -564,32 +607,38 @@ uint32_t SH4::TimerCount(int n) {
return cycles;
}
void SH4::ScheduleTimer(int n, uint32_t tcnt, uint32_t tcr) {
void SH4::RescheduleTimer(int n, uint32_t tcnt, uint32_t tcr) {
TimerHandle &handle = tmu_timers_[n];
int64_t freq = PERIPHERAL_CLOCK_FREQ >> PERIPHERAL_SCALE[tcr & 7];
int64_t cycles = static_cast<int64_t>(tcnt);
std::chrono::nanoseconds remaining = CYCLES_TO_NANO(cycles, freq);
scheduler_->AdjustTimer(handle, remaining);
if (handle) {
scheduler_->CancelTimer(handle);
handle = nullptr;
}
handle = scheduler_->ScheduleTimer(tmu_delegates_[n], remaining);
}
void SH4::ExpireTimer(int n) {
uint32_t &tcor = TCOR(n);
uint32_t &tcnt = TCNT(n);
uint32_t &tcr = TCR(n);
template <int N>
void SH4::ExpireTimer() {
uint32_t &tcor = TCOR(N);
uint32_t &tcnt = TCNT(N);
uint32_t &tcr = TCR(N);
// timer expired, set the underflow flag
tcr |= 0x100;
// if interrupt generation on underflow is enabled, do so
if (tcr & 0x20) {
RequestInterrupt(TUNI(n));
RequestInterrupt(TUNI(N));
}
// reset TCNT with the value from TCOR
tcnt = tcor;
// reschedule the timer with the new count
ScheduleTimer(n, tcnt, tcr);
RescheduleTimer(N, tcnt, tcr);
}

View File

@ -1,8 +1,9 @@
#ifndef SH4_H
#define SH4_H
#include "hw/scheduler.h"
#include "hw/sh4/sh4_code_cache.h"
#include "hw/machine.h"
#include "hw/scheduler.h"
#include "jit/frontend/sh4/sh4_context.h"
struct SH4Test;
@ -10,9 +11,7 @@ struct SH4Test;
namespace re {
namespace hw {
struct Dreamcast;
extern bool MapMemory(Dreamcast &dc);
class Dreamcast;
namespace sh4 {
@ -108,17 +107,16 @@ enum DDTRW { //
DDT_W
};
class SH4 {
friend bool re::hw::MapMemory(Dreamcast &dc);
class SH4 : public Device, public MemoryInterface, public ExecuteInterface {
friend void RunSH4Test(const SH4Test &);
public:
SH4(Dreamcast *dc);
~SH4();
bool Init();
bool Init() final;
void SetPC(uint32_t pc);
void Run(const std::chrono::nanoseconds &period);
void Run(const std::chrono::nanoseconds &delta) final;
// DMAC
void DDT(int channel, DDTRW rw, uint32_t addr);
@ -127,6 +125,10 @@ class SH4 {
void RequestInterrupt(Interrupt intr);
void UnrequestInterrupt(Interrupt intr);
protected:
void MapPhysicalMemory(Memory &memory, MemoryMap &memmap) final;
void MapVirtualMemory(Memory &memory, MemoryMap &memmap) final;
private:
static uint32_t CompilePC();
static void Pref(jit::frontend::sh4::SH4Context *ctx, uint64_t addr);
@ -166,8 +168,9 @@ class SH4 {
void UpdateTimerControl(uint32_t n);
void UpdateTimerCount(uint32_t n);
uint32_t TimerCount(int n);
void ScheduleTimer(int n, uint32_t tcnt, uint32_t tcr);
void ExpireTimer(int n);
void RescheduleTimer(int n, uint32_t tcnt, uint32_t tcr);
template <int N>
void ExpireTimer();
Dreamcast *dc_;
Memory *memory_;
@ -189,6 +192,7 @@ class SH4 {
uint64_t pending_interrupts_;
hw::TimerHandle tmu_timers_[3];
TimerDelegate tmu_delegates_[3];
uint32_t area7_[0x4000]; // consolidated, 16kb area 7 memory
uint8_t cache_[0x2000]; // 8kb cache

View File

@ -2,11 +2,15 @@
#define BACKEND_H
#include <map>
#include "hw/memory.h"
#include "jit/ir/ir_builder.h"
#include "jit/source_map.h"
namespace re {
namespace hw {
class Memory;
}
namespace sys {
struct Exception;
}

View File

@ -1,6 +1,7 @@
#include <math.h>
#include <unordered_map>
#include "core/memory.h"
#include "hw/memory.h"
#include "jit/backend/interpreter/interpreter_backend.h"
#include "jit/backend/interpreter/interpreter_emitter.h"

View File

@ -3,6 +3,7 @@
#include <xbyak/xbyak.h>
#include "core/memory.h"
#include "emu/profiler.h"
#include "hw/memory.h"
#include "jit/backend/x64/x64_backend.h"
#include "jit/backend/x64/x64_disassembler.h"
#include "sys/exception_handler.h"

View File

@ -2,6 +2,7 @@
#include "core/math.h"
#include "core/memory.h"
#include "emu/profiler.h"
#include "hw/memory.h"
#include "jit/backend/x64/x64_backend.h"
#include "jit/backend/x64/x64_emitter.h"

View File

@ -3,10 +3,14 @@
#include <xbyak/xbyak.h>
#include "core/arena.h"
#include "hw/memory.h"
#include "jit/source_map.h"
namespace re {
namespace hw {
class Memory;
}
namespace jit {
namespace backend {
namespace x64 {

View File

@ -2,10 +2,14 @@
#define FRONTEND_H
#include <memory>
#include "hw/memory.h"
#include "jit/ir/ir_builder.h"
namespace re {
namespace hw {
class Memory;
}
namespace jit {
namespace frontend {

View File

@ -1,5 +1,6 @@
#include "core/assert.h"
#include "emu/profiler.h"
#include "hw/memory.h"
#include "jit/frontend/sh4/sh4_builder.h"
using namespace re;

View File

@ -1,12 +1,16 @@
#ifndef SH4_BUILDER_H
#define SH4_BUILDER_H
#include "hw/memory.h"
#include "jit/frontend/sh4/sh4_context.h"
#include "jit/frontend/sh4/sh4_disassembler.h"
#include "jit/ir/ir_builder.h"
namespace re {
namespace hw {
class Memory;
}
namespace jit {
namespace frontend {
namespace sh4 {

View File

@ -1,6 +1,7 @@
#ifndef SH4_DISASSEMBLER_H
#define SH4_DISASSEMBLER_H
#include <stddef.h>
#include <stdint.h>
namespace re {

View File

@ -154,34 +154,14 @@ namespace hw {
namespace sh4 {
void RunSH4Test(const SH4Test &test) {
static const uint32_t stack_address = 0x0;
static const uint32_t stack_size = PAGE_BLKSIZE;
static const uint32_t code_address = 0x8c010000;
const uint32_t code_size =
re::align_up(test.buffer_size, static_cast<uint32_t>(PAGE_BLKSIZE));
// setup stack and executable space in memory map
Memory memory;
CHECK(memory.Init());
RegionHandle stack_handle = memory.AllocRegion(stack_address, stack_size);
RegionHandle code_handle = memory.AllocRegion(code_address, code_size);
MemoryMap memmap;
memmap.Mount(stack_handle, stack_size, stack_address);
memmap.Mount(code_handle, code_size, code_address);
CHECK(memory.Map(memmap));
// fake scheduler
Scheduler scheduler;
// 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());
dc->memory = &memory;
dc->scheduler = &scheduler;
std::unique_ptr<SH4> sh4(new SH4(dc.get()));
// initialize cpu
SH4 sh4(dc.get());
CHECK(sh4.Init());
CHECK(dc->Init());
// setup in registers
for (int i = 0; i < sh4_num_test_regs; i++) {
@ -194,21 +174,21 @@ void RunSH4Test(const SH4Test &test) {
continue;
}
re::store(reinterpret_cast<uint8_t *>(&sh4.ctx_) + reg.offset, input);
re::store(reinterpret_cast<uint8_t *>(&sh4->ctx_) + reg.offset, input);
}
// setup initial stack pointer
sh4.ctx_.r[15] = stack_size;
sh4->ctx_.r[15] = 0x8d000000;
// load binary
memory.Memcpy(code_address, test.buffer, test.buffer_size);
dc->memory->Memcpy(0x8c010000, test.buffer, test.buffer_size);
// skip to the test's offset
sh4.SetPC(code_address + test.buffer_offset);
sh4->SetPC(0x8c010000 + test.buffer_offset);
// run until the function returns
while (sh4.ctx_.pc) {
sh4.Run(std::chrono::nanoseconds(1));
while (sh4->ctx_.pc) {
sh4->Run(std::chrono::nanoseconds(1));
}
// validate out registers
@ -223,7 +203,7 @@ void RunSH4Test(const SH4Test &test) {
}
uint32_t actual = re::load<uint32_t>(
reinterpret_cast<const uint8_t *>(&sh4.ctx_) + reg.offset);
reinterpret_cast<const uint8_t *>(&sh4->ctx_) + reg.offset);
ASSERT_EQ(expected, actual) << reg.name << " expected: 0x" << std::hex
<< expected << ", actual 0x" << actual;