mirror of https://github.com/inolen/redream.git
refactored hw/ code adding Machine, Device and Device interfaces
This commit is contained in:
parent
2b42b9c85c
commit
ede8b2f758
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 ® = 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_));
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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_;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef SH4_DISASSEMBLER_H
|
||||
#define SH4_DISASSEMBLER_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace re {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue