diff --git a/CMakeLists.txt b/CMakeLists.txt index 28ce3c19..393f6ce0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,10 +124,9 @@ set(DREAVM_SOURCES src/cpu/ir/passes/register_allocation_pass.cc src/cpu/ir/passes/validate_pass.cc src/cpu/sh4.cc - src/cpu/sh4_context.cc src/cpu/runtime.cc src/emu/disc.cc - src/emu/emulator.cc + src/emu/dreamcast.cc src/emu/profiler.cc src/emu/scheduler.cc src/holly/gdrom.cc @@ -281,36 +280,14 @@ endif() # build test binary set(DREAVM_TEST_SOURCES - src/core/assert.cc - src/core/filesystem.cc - src/core/log.cc - src/cpu/backend/interpreter/interpreter_backend.cc - src/cpu/backend/interpreter/interpreter_block.cc - src/cpu/backend/interpreter/interpreter_callbacks.cc - src/cpu/backend/x64/x64_backend.cc - src/cpu/backend/x64/x64_block.cc - src/cpu/backend/x64/x64_emitter.cc - src/cpu/frontend/sh4/sh4_builder.cc - src/cpu/frontend/sh4/sh4_emit.cc - src/cpu/frontend/sh4/sh4_frontend.cc - src/cpu/frontend/sh4/sh4_instr.cc - src/cpu/ir/ir_builder.cc - src/cpu/ir/passes/constant_propagation_pass.cc - src/cpu/ir/passes/context_promotion_pass.cc - src/cpu/ir/passes/control_flow_analysis_pass.cc - src/cpu/ir/passes/pass_runner.cc - src/cpu/ir/passes/register_allocation_pass.cc - src/cpu/ir/passes/validate_pass.cc - src/cpu/sh4.cc - src/cpu/sh4_context.cc - src/cpu/runtime.cc - src/emu/profiler.cc + ${DREAVM_SOURCES} test/sh4_test.cc test/test_intrusive_list.cc - test/test_memory.cc + #test/test_memory.cc test/test_ring_buffer.cc test/test_sh4.cc ${asm_inc}) +list(REMOVE_ITEM DREAVM_TEST_SOURCES src/main.cc) # assign source groups for visual studio projects source_group_by_dir(DREAVM_TEST_SOURCES) diff --git a/src/cpu/backend/backend.h b/src/cpu/backend/backend.h index 5e445fa7..29485f66 100644 --- a/src/cpu/backend/backend.h +++ b/src/cpu/backend/backend.h @@ -25,7 +25,6 @@ class Backend { virtual const Register *registers() const = 0; virtual int num_registers() const = 0; - virtual bool Init() = 0; virtual void Reset() = 0; virtual bool AssembleBlock(ir::IRBuilder &builder, RuntimeBlock *block) = 0; diff --git a/src/cpu/backend/interpreter/interpreter_backend.cc b/src/cpu/backend/interpreter/interpreter_backend.cc index feca0236..c382f59d 100644 --- a/src/cpu/backend/interpreter/interpreter_backend.cc +++ b/src/cpu/backend/interpreter/interpreter_backend.cc @@ -75,8 +75,6 @@ int InterpreterBackend::num_registers() const { return sizeof(int_registers) / sizeof(Register); } -bool InterpreterBackend::Init() { return true; } - void InterpreterBackend::Reset() { codegen_ = codegen_begin_; } bool InterpreterBackend::AssembleBlock(IRBuilder &builder, diff --git a/src/cpu/backend/interpreter/interpreter_backend.h b/src/cpu/backend/interpreter/interpreter_backend.h index 02377e5e..733952f0 100644 --- a/src/cpu/backend/interpreter/interpreter_backend.h +++ b/src/cpu/backend/interpreter/interpreter_backend.h @@ -87,7 +87,6 @@ class InterpreterBackend : public Backend { const Register *registers() const; int num_registers() const; - bool Init(); void Reset(); bool AssembleBlock(ir::IRBuilder &builder, RuntimeBlock *block); diff --git a/src/cpu/backend/x64/x64_backend.cc b/src/cpu/backend/x64/x64_backend.cc index 23197e82..802248ba 100644 --- a/src/cpu/backend/x64/x64_backend.cc +++ b/src/cpu/backend/x64/x64_backend.cc @@ -20,8 +20,6 @@ int X64Backend::num_registers() const { return sizeof(x64_registers) / sizeof(Register); } -bool X64Backend::Init() { return true; } - void X64Backend::Reset() { codegen_.reset(); } bool X64Backend::AssembleBlock(IRBuilder &builder, RuntimeBlock *block) { diff --git a/src/cpu/backend/x64/x64_backend.h b/src/cpu/backend/x64/x64_backend.h index 8580c0a7..f0f04a7c 100644 --- a/src/cpu/backend/x64/x64_backend.h +++ b/src/cpu/backend/x64/x64_backend.h @@ -32,7 +32,6 @@ class X64Backend : public Backend { const Register *registers() const; int num_registers() const; - bool Init(); void Reset(); bool AssembleBlock(ir::IRBuilder &builder, RuntimeBlock *block); diff --git a/src/cpu/frontend/frontend.h b/src/cpu/frontend/frontend.h index 82638b24..16c534c8 100644 --- a/src/cpu/frontend/frontend.h +++ b/src/cpu/frontend/frontend.h @@ -14,7 +14,6 @@ class Frontend { Frontend(emu::Memory &memory) : memory_(memory) {} virtual ~Frontend() {} - virtual bool Init() = 0; virtual std::unique_ptr BuildBlock(uint32_t addr, const void *guest_ctx) = 0; diff --git a/src/cpu/frontend/sh4/sh4_builder.cc b/src/cpu/frontend/sh4/sh4_builder.cc index ebf95f69..edeaa608 100644 --- a/src/cpu/frontend/sh4/sh4_builder.cc +++ b/src/cpu/frontend/sh4/sh4_builder.cc @@ -116,7 +116,7 @@ Value *SH4Builder::LoadSR() { void SH4Builder::StoreSR(Value *v) { CHECK_EQ(v->type(), VALUE_I32); StoreAndPreserveContext(offsetof(SH4Context, sr), v, IF_INVALIDATE_CONTEXT); - CallExternal((ExternalFn)&SRUpdated); + CallExternal((ExternalFn)&SH4Context::SRUpdated); } ir::Value *SH4Builder::LoadT() { return And(LoadSR(), AllocConstant(T)); } @@ -144,7 +144,7 @@ void SH4Builder::StoreFPSCR(ir::Value *v) { CHECK_EQ(v->type(), VALUE_I32); v = And(v, AllocConstant(0x003fffff)); StoreAndPreserveContext(offsetof(SH4Context, fpscr), v); - CallExternal((ExternalFn)&FPSCRUpdated); + CallExternal((ExternalFn)&SH4Context::FPSCRUpdated); } ir::Value *SH4Builder::LoadPR() { diff --git a/src/cpu/frontend/sh4/sh4_frontend.cc b/src/cpu/frontend/sh4/sh4_frontend.cc index 4521f273..47a4845c 100644 --- a/src/cpu/frontend/sh4/sh4_frontend.cc +++ b/src/cpu/frontend/sh4/sh4_frontend.cc @@ -7,8 +7,6 @@ using namespace dreavm::emu; SH4Frontend::SH4Frontend(Memory &memory) : Frontend(memory) {} -bool SH4Frontend::Init() { return true; } - std::unique_ptr SH4Frontend::BuildBlock(uint32_t addr, const void *guest_ctx) { std::unique_ptr builder(new SH4Builder(memory_)); diff --git a/src/cpu/frontend/sh4/sh4_frontend.h b/src/cpu/frontend/sh4/sh4_frontend.h index 792160d4..addb3eab 100644 --- a/src/cpu/frontend/sh4/sh4_frontend.h +++ b/src/cpu/frontend/sh4/sh4_frontend.h @@ -15,7 +15,6 @@ class SH4Frontend : public Frontend { public: SH4Frontend(emu::Memory &memory); - bool Init(); std::unique_ptr BuildBlock(uint32_t addr, const void *guest_ctx); }; diff --git a/src/cpu/runtime.cc b/src/cpu/runtime.cc index e1274a0a..336f90ce 100644 --- a/src/cpu/runtime.cc +++ b/src/cpu/runtime.cc @@ -27,38 +27,24 @@ static inline uint32_t BlockOffset(uint32_t addr) { return (addr & BLOCK_ADDR_MASK) >> BLOCK_ADDR_SHIFT; } -Runtime::Runtime(Memory &memory) +Runtime::Runtime(Memory &memory, frontend::Frontend &frontend, + backend::Backend &backend) : memory_(memory), - frontend_(nullptr), - backend_(nullptr), + frontend_(frontend), + backend_(backend), pending_reset_(false) { blocks_ = new RuntimeBlock[MAX_BLOCKS]; -} - -Runtime::~Runtime() { delete[] blocks_; } - -bool Runtime::Init(frontend::Frontend *frontend, backend::Backend *backend) { - frontend_ = frontend; - backend_ = backend; - - if (!frontend_->Init()) { - return false; - } - - if (!backend_->Init()) { - return false; - } pass_runner_.AddPass(std::unique_ptr(new ValidatePass())); pass_runner_.AddPass(std::unique_ptr(new ControlFlowAnalysisPass())); pass_runner_.AddPass(std::unique_ptr(new ContextPromotionPass())); pass_runner_.AddPass(std::unique_ptr(new ConstantPropagationPass())); pass_runner_.AddPass( - std::unique_ptr(new RegisterAllocationPass(*backend_))); - - return true; + std::unique_ptr(new RegisterAllocationPass(backend_))); } +Runtime::~Runtime() { delete[] blocks_; } + // TODO should the block caching be part of the frontend? // this way, the SH4Frontend can cache based on FPU state RuntimeBlock *Runtime::GetBlock(uint32_t addr, const void *guest_ctx) { @@ -86,7 +72,7 @@ RuntimeBlock *Runtime::GetBlock(uint32_t addr, const void *guest_ctx) { void Runtime::QueueResetBlocks() { pending_reset_ = true; } void Runtime::ResetBlocks() { - backend_->Reset(); + backend_.Reset(); memset(blocks_, 0, sizeof(RuntimeBlock) * MAX_BLOCKS); } @@ -95,12 +81,12 @@ void Runtime::CompileBlock(uint32_t addr, const void *guest_ctx, RuntimeBlock *block) { PROFILER_RUNTIME("Runtime::CompileBlock"); - std::unique_ptr builder = frontend_->BuildBlock(addr, guest_ctx); + std::unique_ptr builder = frontend_.BuildBlock(addr, guest_ctx); // run optimization passes pass_runner_.Run(*builder); - if (!backend_->AssembleBlock(*builder, block)) { + if (!backend_.AssembleBlock(*builder, block)) { LOG_INFO("Assembler overflow, resetting block cache"); // the backend overflowed, reset the block cache @@ -108,7 +94,7 @@ void Runtime::CompileBlock(uint32_t addr, const void *guest_ctx, // if the backend fails to assemble on an empty cache, there's nothing to be // done - CHECK(backend_->AssembleBlock(*builder, block), + CHECK(backend_.AssembleBlock(*builder, block), "Backend assembler buffer overflow"); } } diff --git a/src/cpu/runtime.h b/src/cpu/runtime.h index 56f6c37e..9172f872 100644 --- a/src/cpu/runtime.h +++ b/src/cpu/runtime.h @@ -27,12 +27,12 @@ struct RuntimeBlock { class Runtime { public: - Runtime(emu::Memory &memory); + Runtime(emu::Memory &memory, frontend::Frontend &frontend, + backend::Backend &backend); ~Runtime(); emu::Memory &memory() { return memory_; } - bool Init(frontend::Frontend *frontend, backend::Backend *backend); RuntimeBlock *GetBlock(uint32_t addr, const void *guest_ctx); void QueueResetBlocks(); @@ -41,8 +41,8 @@ class Runtime { void CompileBlock(uint32_t addr, const void *guest_ctx, RuntimeBlock *block); emu::Memory &memory_; - frontend::Frontend *frontend_; - backend::Backend *backend_; + frontend::Frontend &frontend_; + backend::Backend &backend_; ir::passes::PassRunner pass_runner_; RuntimeBlock *blocks_; bool pending_reset_; diff --git a/src/cpu/sh4.cc b/src/cpu/sh4.cc index 86e939de..8470ec59 100644 --- a/src/cpu/sh4.cc +++ b/src/cpu/sh4.cc @@ -1,6 +1,7 @@ #include "core/core.h" #include "cpu/runtime.h" #include "cpu/sh4.h" +#include "emu/memory.h" #include "emu/profiler.h" using namespace dreavm::core; @@ -15,25 +16,92 @@ InterruptInfo interrupts[NUM_INTERRUPTS] = { #undef SH4_INT }; -SH4::SH4(Memory &memory) : memory_(memory) {} - -SH4::~SH4() {} - -bool SH4::Init(Runtime *runtime) { - runtime_ = runtime; - - InitMemory(); - Reset(); - - return true; +static void SetRegisterBank(SH4Context *ctx, int bank) { + if (bank == 0) { + for (int s = 0; s < 8; s++) { + ctx->rbnk[1][s] = ctx->r[s]; + ctx->r[s] = ctx->rbnk[0][s]; + } + } else { + for (int s = 0; s < 8; s++) { + ctx->rbnk[0][s] = ctx->r[s]; + ctx->r[s] = ctx->rbnk[1][s]; + } + } } -void SH4::Reset(uint32_t pc) { - Reset(); +static void SwapFPRegisters(SH4Context *ctx) { + uint32_t z; - ctx_.pc = pc; + for (int s = 0; s <= 15; s++) { + z = ctx->fr[s]; + ctx->fr[s] = ctx->xf[s]; + ctx->xf[s] = z; + } } +static void SwapFPCouples(SH4Context *ctx) { + uint32_t z; + + for (int s = 0; s <= 15; s = s + 2) { + z = ctx->fr[s]; + ctx->fr[s] = ctx->fr[s + 1]; + ctx->fr[s + 1] = z; + + z = ctx->xf[s]; + ctx->xf[s] = ctx->xf[s + 1]; + ctx->xf[s + 1] = z; + } +} + +void SH4Context::SRUpdated(SH4Context *ctx) { + if (ctx->sr.RB != ctx->old_sr.RB) { + SetRegisterBank(ctx, ctx->sr.RB ? 1 : 0); + } + + ctx->old_sr = ctx->sr; +} + +void SH4Context::FPSCRUpdated(SH4Context *ctx) { + if (ctx->fpscr.FR != ctx->old_fpscr.FR) { + SwapFPRegisters(ctx); + } + + if (ctx->fpscr.PR != ctx->old_fpscr.PR) { + SwapFPCouples(ctx); + } + + ctx->old_fpscr = ctx->fpscr; +} + +SH4::SH4(Memory &memory, Runtime &runtime) + : memory_(memory), + runtime_(runtime), + requested_interrupts_(0), + pending_interrupts_(0) {} + +void SH4::Init() { + memset(&ctx_, 0, sizeof(ctx_)); + ctx_.pc = 0xa0000000; + ctx_.pr = 0xdeadbeef; + ctx_.sr.full = ctx_.old_sr.full = 0x700000f0; + ctx_.fpscr.full = ctx_.old_fpscr.full = 0x00040001; + + memset(area7_, 0, sizeof(area7_)); +#define SH4_REG(addr, name, flags, default, reset, sleep, standby, type) \ + if (default != HELD) { \ + *(uint32_t *)&area7_[name##_OFFSET] = default; \ + } +#include "cpu/sh4_regs.inc" +#undef SH4_REG + + memset(cache_, 0, sizeof(cache_)); + + ReprioritizeInterrupts(); +} + +void SH4::SetPC(uint32_t pc) { ctx_.pc = pc; } + uint32_t SH4::Execute(uint32_t cycles) { PROFILER_RUNTIME("SH4::Execute"); @@ -48,9 +116,8 @@ uint32_t SH4::Execute(uint32_t cycles) { } while (ctx_.pc != 0xdeadbeef) { - // translate PC to 29-bit physical space - uint32_t pc = ctx_.pc & ~MIRROR_MASK; - RuntimeBlock *block = runtime_->GetBlock(pc, &ctx_); + uint32_t pc = ctx_.pc & ADDR_MASK; + RuntimeBlock *block = runtime_.GetBlock(pc, &ctx_); // be careful not to wrap around uint32_t next_remaining = remaining - block->guest_cycles; @@ -105,64 +172,15 @@ void SH4::UnrequestInterrupt(Interrupt intr) { UpdatePendingInterrupts(); } -namespace dreavm { -namespace cpu { - -// with OIX, bit 25, rather than bit 13, determines which 4kb bank to use -#define CACHE_OFFSET(addr, OIX) \ - ((OIX ? ((addr & 0x2000000) >> 13) : ((addr & 0x2000) >> 1)) | (addr & 0xfff)) - -template -T SH4::ReadCache(void *ctx, uint32_t addr) { - SH4 *sh4 = (SH4 *)ctx; - CHECK_EQ(sh4->CCR.ORA, 1u); - uint32_t offset = CACHE_OFFSET(addr, sh4->CCR.OIX); - return *reinterpret_cast(&sh4->cache_[offset]); +uint8_t SH4::ReadRegister8(uint32_t addr) { + return static_cast(ReadRegister32(addr)); } -template -void SH4::WriteCache(void *ctx, uint32_t addr, T value) { - SH4 *sh4 = (SH4 *)ctx; - CHECK_EQ(sh4->CCR.ORA, 1u); - uint32_t offset = CACHE_OFFSET(addr, sh4->CCR.OIX); - *reinterpret_cast(&sh4->cache_[offset]) = value; +uint16_t SH4::ReadRegister16(uint32_t addr) { + return static_cast(ReadRegister32(addr)); } -template -T SH4::ReadSQ(void *ctx, uint32_t addr) { - return static_cast(ReadSQ(ctx, addr)); -} - -template <> -uint32_t SH4::ReadSQ(void *ctx, uint32_t addr) { - SH4 *sh4 = (SH4 *)ctx; - uint32_t sqi = (addr & 0x20) >> 5; - unsigned idx = (addr & 0x1c) >> 2; - return sh4->ctx_.sq[sqi][idx]; -} - -template -void SH4::WriteSQ(void *ctx, uint32_t addr, T value) { - WriteSQ(ctx, addr, static_cast(value)); -} - -template <> -void SH4::WriteSQ(void *ctx, uint32_t addr, uint32_t value) { - SH4 *sh4 = (SH4 *)ctx; - uint32_t sqi = (addr & 0x20) >> 5; - unsigned idx = (addr & 0x1c) >> 2; - sh4->ctx_.sq[sqi][idx] = value; -} - -template -T SH4::ReadArea7(void *ctx, uint32_t addr) { - return static_cast(ReadArea7(ctx, addr)); -} - -template <> -uint32_t SH4::ReadArea7(void *ctx, uint32_t addr) { - SH4 *sh4 = (SH4 *)ctx; - +uint32_t SH4::ReadRegister32(uint32_t addr) { // translate from 64mb space to our 16kb space addr = ((addr & 0x1fe0000) >> 11) | ((addr & 0xfc) >> 2); @@ -195,8 +213,8 @@ uint32_t SH4::ReadArea7(void *ctx, uint32_t addr) { // reg[PDTRA] = reg[PDTRA] & 0xfffd; // _8c00b92c(0); // reg[PCTRA] = old_PCTRA; - uint32_t pctra = sh4->PCTRA; - uint32_t pdtra = sh4->PDTRA; + uint32_t pctra = PCTRA; + uint32_t pdtra = PDTRA; uint32_t v = 0; if ((pctra & 0xf) == 0x8 || ((pctra & 0xf) == 0xb && (pdtra & 0xf) != 0x2) || @@ -236,22 +254,22 @@ uint32_t SH4::ReadArea7(void *ctx, uint32_t addr) { } } - return sh4->area7_[addr]; + return area7_[addr]; } -template -void SH4::WriteArea7(void *ctx, uint32_t addr, T value) { - WriteArea7(ctx, addr, static_cast(value)); +void SH4::WriteRegister8(uint32_t addr, uint8_t value) { + WriteRegister32(addr, static_cast(value)); } -template <> -void SH4::WriteArea7(void *ctx, uint32_t addr, uint32_t value) { - SH4 *sh4 = (SH4 *)ctx; +void SH4::WriteRegister16(uint32_t addr, uint16_t value) { + WriteRegister32(addr, static_cast(value)); +} +void SH4::WriteRegister32(uint32_t addr, uint32_t value) { // translate from 64mb space to our 16kb space addr = ((addr & 0x1fe0000) >> 11) | ((addr & 0xfc) >> 2); - sh4->area7_[addr] = value; + area7_[addr] = value; switch (addr) { case MMUCR_OFFSET: @@ -263,79 +281,111 @@ void SH4::WriteArea7(void *ctx, uint32_t addr, uint32_t value) { // it seems the only aspect of the cache control register that needs to be // emulated is the instruction cache invalidation case CCR_OFFSET: - if (sh4->CCR.ICI) { - sh4->ResetInstructionCache(); + if (CCR.ICI) { + ResetInstructionCache(); } break; // when a PREF instruction is encountered, the high order bits of the // address are filled in from the queue address control register case QACR0_OFFSET: - sh4->ctx_.sq_ext_addr[0] = (value & 0x1c) << 24; + ctx_.sq_ext_addr[0] = (value & 0x1c) << 24; break; case QACR1_OFFSET: - sh4->ctx_.sq_ext_addr[1] = (value & 0x1c) << 24; + ctx_.sq_ext_addr[1] = (value & 0x1c) << 24; break; case IPRA_OFFSET: case IPRB_OFFSET: case IPRC_OFFSET: - sh4->ReprioritizeInterrupts(); + ReprioritizeInterrupts(); break; // TODO UnrequestInterrupt on TCR write } } -} + +// with OIX, bit 25, rather than bit 13, determines which 4kb bank to use +#define CACHE_OFFSET(addr, OIX) \ + ((OIX ? ((addr & 0x2000000) >> 13) : ((addr & 0x2000) >> 1)) | (addr & 0xfff)) + +uint8_t SH4::ReadCache8(uint32_t addr) { + CHECK_EQ(CCR.ORA, 1u); + addr = CACHE_OFFSET(addr, CCR.OIX); + return *reinterpret_cast(&cache_[addr]); } -void SH4::InitMemory() { - // mount internal cpu register area - memory_.Handle(SH4_REG_START, SH4_REG_END, MIRROR_MASK, this, - &SH4::ReadArea7, &SH4::ReadArea7, - &SH4::ReadArea7, nullptr, &SH4::WriteArea7, - &SH4::WriteArea7, &SH4::WriteArea7, - nullptr); - - // map cache - memory_.Handle(0x7c000000, 0x7fffffff, 0x0, this, &SH4::ReadCache, - &SH4::ReadCache, &SH4::ReadCache, - &SH4::ReadCache, &SH4::WriteCache, - &SH4::WriteCache, &SH4::WriteCache, - &SH4::WriteCache); - - // map store queues - memory_.Handle(0xe0000000, 0xe3ffffff, 0x0, this, &SH4::ReadSQ, - &SH4::ReadSQ, &SH4::ReadSQ, nullptr, - &SH4::WriteSQ, &SH4::WriteSQ, - &SH4::WriteSQ, nullptr); +uint16_t SH4::ReadCache16(uint32_t addr) { + CHECK_EQ(CCR.ORA, 1u); + addr = CACHE_OFFSET(addr, CCR.OIX); + return *reinterpret_cast(&cache_[addr]); } -void SH4::Reset() { - // reset context - memset(&ctx_, 0, sizeof(ctx_)); - ctx_.pc = 0xa0000000; - ctx_.pr = 0xdeadbeef; - ctx_.sr.full = ctx_.old_sr.full = 0x700000f0; - ctx_.fpscr.full = ctx_.old_fpscr.full = 0x00040001; - -#define SH4_REG(addr, name, flags, default, reset, sleep, standby, type) \ - if (default != HELD) { \ - *(uint32_t *)&area7_[name##_OFFSET] = default; \ - } -#include "cpu/sh4_regs.inc" -#undef SH4_REG - - // reset interrupts - requested_interrupts_ = 0; - ReprioritizeInterrupts(); - - // reset memory - memset(area7_, 0, sizeof(area7_)); - memset(cache_, 0, sizeof(cache_)); +uint32_t SH4::ReadCache32(uint32_t addr) { + CHECK_EQ(CCR.ORA, 1u); + addr = CACHE_OFFSET(addr, CCR.OIX); + return *reinterpret_cast(&cache_[addr]); } -void SH4::ResetInstructionCache() { runtime_->QueueResetBlocks(); } +uint64_t SH4::ReadCache64(uint32_t addr) { + CHECK_EQ(CCR.ORA, 1u); + addr = CACHE_OFFSET(addr, CCR.OIX); + return *reinterpret_cast(&cache_[addr]); +} + +void SH4::WriteCache8(uint32_t addr, uint8_t value) { + CHECK_EQ(CCR.ORA, 1u); + addr = CACHE_OFFSET(addr, CCR.OIX); + *reinterpret_cast(&cache_[addr]) = value; +} + +void SH4::WriteCache16(uint32_t addr, uint16_t value) { + CHECK_EQ(CCR.ORA, 1u); + addr = CACHE_OFFSET(addr, CCR.OIX); + *reinterpret_cast(&cache_[addr]) = value; +} + +void SH4::WriteCache32(uint32_t addr, uint32_t value) { + CHECK_EQ(CCR.ORA, 1u); + addr = CACHE_OFFSET(addr, CCR.OIX); + *reinterpret_cast(&cache_[addr]) = value; +} + +void SH4::WriteCache64(uint32_t addr, uint64_t value) { + CHECK_EQ(CCR.ORA, 1u); + addr = CACHE_OFFSET(addr, CCR.OIX); + *reinterpret_cast(&cache_[addr]) = value; +} + +uint8_t SH4::ReadSQ8(uint32_t addr) { + return static_cast(ReadSQ32(addr)); +} + +uint16_t SH4::ReadSQ16(uint32_t addr) { + return static_cast(ReadSQ32(addr)); +} + +uint32_t SH4::ReadSQ32(uint32_t addr) { + uint32_t sqi = (addr & 0x20) >> 5; + unsigned idx = (addr & 0x1c) >> 2; + return ctx_.sq[sqi][idx]; +} + +void SH4::WriteSQ8(uint32_t addr, uint8_t value) { + WriteSQ32(addr, static_cast(value)); +} + +void SH4::WriteSQ16(uint32_t addr, uint16_t value) { + WriteSQ32(addr, static_cast(value)); +} + +void SH4::WriteSQ32(uint32_t addr, uint32_t value) { + uint32_t sqi = (addr & 0x20) >> 5; + unsigned idx = (addr & 0x1c) >> 2; + ctx_.sq[sqi][idx] = value; +} + +void SH4::ResetInstructionCache() { runtime_.QueueResetBlocks(); } // Generate a sorted set of interrupts based on their priority. These sorted // ids are used to represent all of the currently requested interrupts as a @@ -411,7 +461,7 @@ void SH4::CheckPendingInterrupts() { ctx_.sr.RB = 1; ctx_.pc = ctx_.vbr + 0x600; - SRUpdated(&ctx_); + SH4Context::SRUpdated(&ctx_); } bool SH4::TimerEnabled(int n) { // diff --git a/src/cpu/sh4.h b/src/cpu/sh4.h index bdfba4d0..f7fd804f 100644 --- a/src/cpu/sh4.h +++ b/src/cpu/sh4.h @@ -1,31 +1,64 @@ #ifndef SH4_H #define SH4_H -#include "cpu/sh4_context.h" #include "emu/device.h" -#include "emu/memory.h" struct SH4Test; namespace dreavm { +namespace emu { +class Memory; +} + namespace cpu { class Runtime; +// translate address to 29-bit physical space, ignoring all modifier bits +enum { ADDR_MASK = 0x1fffffff }; + // registers enum { UNDEFINED = 0x0, HELD = 0x1, }; -enum { -#define SH4_REG(addr, name, flags, default, reset, sleep, standby, type) \ - name##_OFFSET = ((addr & 0x1fe0000) >> 11) | ((addr & 0xfc) >> 2), -#include "cpu/sh4_regs.inc" -#undef SH4_REG +union SR_T { + uint32_t full; + struct { + uint32_t T : 1; + uint32_t S : 1; + uint32_t reserved : 2; + uint32_t IMASK : 4; + uint32_t Q : 1; + uint32_t M : 1; + uint32_t reserved1 : 5; + uint32_t FD : 1; + uint32_t reserved2 : 12; + uint32_t BL : 1; + uint32_t RB : 1; + uint32_t MD : 1; + uint32_t reserved3 : 1; + }; +}; + +union FPSCR_T { + uint32_t full; + struct { + uint32_t RM : 2; + uint32_t flag : 5; + uint32_t enable : 5; + uint32_t cause : 6; + uint32_t DN : 1; + uint32_t PR : 1; + uint32_t SZ : 1; + uint32_t FR : 1; + uint32_t reserved : 10; + }; }; union CCR_T { + uint32_t full; struct { uint32_t OCE : 1; uint32_t WT : 1; @@ -43,10 +76,10 @@ union CCR_T { uint32_t reserved4 : 15; uint32_t EMODE : 1; }; - uint32_t full; }; union CHCR_T { + uint32_t full; struct { uint32_t DE : 1; uint32_t TE : 1; @@ -67,10 +100,10 @@ union CHCR_T { uint32_t STC : 1; uint32_t SSA : 3; }; - uint32_t full; }; union DMAOR_T { + uint32_t full; struct { uint32_t DME : 1; uint32_t NMIF : 1; @@ -83,7 +116,13 @@ union DMAOR_T { uint32_t DDT : 1; uint32_t reserved2 : 16; }; - uint32_t full; +}; + +enum { +#define SH4_REG(addr, name, flags, default, reset, sleep, standby, type) \ + name##_OFFSET = ((addr & 0x1fe0000) >> 11) | ((addr & 0xfc) >> 2), +#include "cpu/sh4_regs.inc" +#undef SH4_REG }; // interrupts @@ -104,18 +143,36 @@ enum DDTRW { // DDT_W }; +struct SH4Context { + static void SRUpdated(SH4Context *ctx); + static void FPSCRUpdated(SH4Context *ctx); + + uint32_t pc, spc; + uint32_t pr; + uint32_t gbr, vbr; + uint32_t mach, macl; + uint32_t r[16], rbnk[2][8], sgr; + uint32_t fr[16], xf[16]; + uint32_t fpul; + uint32_t dbr; + uint32_t sq[2][8]; + uint32_t sq_ext_addr[2]; + uint32_t preserve; + SR_T sr, ssr, old_sr; + FPSCR_T fpscr, old_fpscr; +}; + class SH4 : public emu::Device { friend void SRUpdated(SH4Context *ctx); friend void RunSH4Test(const SH4Test &); public: - SH4(emu::Memory &memory); - virtual ~SH4(); + SH4(emu::Memory &memory, cpu::Runtime &runtime); uint32_t GetClockFrequency() { return 200000000; } - bool Init(Runtime *runtime); - void Reset(uint32_t pc); + void Init(); + void SetPC(uint32_t pc); uint32_t Execute(uint32_t cycles); // DMAC @@ -125,23 +182,31 @@ class SH4 : public emu::Device { void RequestInterrupt(Interrupt intr); void UnrequestInterrupt(Interrupt intr); + uint8_t ReadRegister8(uint32_t addr); + uint16_t ReadRegister16(uint32_t addr); + uint32_t ReadRegister32(uint32_t addr); + void WriteRegister8(uint32_t addr, uint8_t value); + void WriteRegister16(uint32_t addr, uint16_t value); + void WriteRegister32(uint32_t addr, uint32_t value); + + uint8_t ReadCache8(uint32_t addr); + uint16_t ReadCache16(uint32_t addr); + uint32_t ReadCache32(uint32_t addr); + uint64_t ReadCache64(uint32_t addr); + void WriteCache8(uint32_t addr, uint8_t value); + void WriteCache16(uint32_t addr, uint16_t value); + void WriteCache32(uint32_t addr, uint32_t value); + void WriteCache64(uint32_t addr, uint64_t value); + + uint8_t ReadSQ8(uint32_t addr); + uint16_t ReadSQ16(uint32_t addr); + uint32_t ReadSQ32(uint32_t addr); + + void WriteSQ8(uint32_t addr, uint8_t value); + void WriteSQ16(uint32_t addr, uint16_t value); + void WriteSQ32(uint32_t addr, uint32_t value); + protected: - template - static T ReadCache(void *ctx, uint32_t addr); - template - static void WriteCache(void *ctx, uint32_t addr, T value); - template - static T ReadSQ(void *ctx, uint32_t addr); - template - static void WriteSQ(void *ctx, uint32_t addr, T value); - template - static T ReadArea7(void *ctx, uint32_t addr); - template - static void WriteArea7(void *ctx, uint32_t addr, T value); - - void InitMemory(); - void Reset(); - // CCN void ResetInstructionCache(); @@ -155,7 +220,7 @@ class SH4 : public emu::Device { void RunTimer(int n, uint32_t cycles); emu::Memory &memory_; - Runtime *runtime_; + Runtime &runtime_; SH4Context ctx_; SR_T old_sr_; diff --git a/src/cpu/sh4_context.cc b/src/cpu/sh4_context.cc deleted file mode 100644 index 1aeb7b1c..00000000 --- a/src/cpu/sh4_context.cc +++ /dev/null @@ -1,66 +0,0 @@ -#include "core/core.h" -#include "cpu/sh4.h" -#include "cpu/sh4_context.h" - -namespace dreavm { -namespace cpu { - -static void SetRegisterBank(SH4Context *ctx, int bank) { - if (bank == 0) { - for (int s = 0; s < 8; s++) { - ctx->rbnk[1][s] = ctx->r[s]; - ctx->r[s] = ctx->rbnk[0][s]; - } - } else { - for (int s = 0; s < 8; s++) { - ctx->rbnk[0][s] = ctx->r[s]; - ctx->r[s] = ctx->rbnk[1][s]; - } - } -} - -static void SwapFPRegisters(SH4Context *ctx) { - uint32_t z; - - for (int s = 0; s <= 15; s++) { - z = ctx->fr[s]; - ctx->fr[s] = ctx->xf[s]; - ctx->xf[s] = z; - } -} - -static void SwapFPCouples(SH4Context *ctx) { - uint32_t z; - - for (int s = 0; s <= 15; s = s + 2) { - z = ctx->fr[s]; - ctx->fr[s] = ctx->fr[s + 1]; - ctx->fr[s + 1] = z; - - z = ctx->xf[s]; - ctx->xf[s] = ctx->xf[s + 1]; - ctx->xf[s + 1] = z; - } -} - -void SRUpdated(SH4Context *ctx) { - if (ctx->sr.RB != ctx->old_sr.RB) { - SetRegisterBank(ctx, ctx->sr.RB ? 1 : 0); - } - - ctx->old_sr = ctx->sr; -} - -void FPSCRUpdated(SH4Context *ctx) { - if (ctx->fpscr.FR != ctx->old_fpscr.FR) { - SwapFPRegisters(ctx); - } - - if (ctx->fpscr.PR != ctx->old_fpscr.PR) { - SwapFPCouples(ctx); - } - - ctx->old_fpscr = ctx->fpscr; -} -} -} diff --git a/src/cpu/sh4_context.h b/src/cpu/sh4_context.h deleted file mode 100644 index d82e54d9..00000000 --- a/src/cpu/sh4_context.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef SH4_CONTEXT_H -#define SH4_CONTEXT_H - -#include - -namespace dreavm { -namespace cpu { - -union SR_T { - struct { - uint32_t T : 1; - uint32_t S : 1; - uint32_t reserved : 2; - uint32_t IMASK : 4; - uint32_t Q : 1; - uint32_t M : 1; - uint32_t reserved1 : 5; - uint32_t FD : 1; - uint32_t reserved2 : 12; - uint32_t BL : 1; - uint32_t RB : 1; - uint32_t MD : 1; - uint32_t reserved3 : 1; - }; - uint32_t full; -}; - -union FPSCR_T { - struct { - uint32_t RM : 2; - uint32_t flag : 5; - uint32_t enable : 5; - uint32_t cause : 6; - uint32_t DN : 1; - uint32_t PR : 1; - uint32_t SZ : 1; - uint32_t FR : 1; - uint32_t reserved : 10; - }; - uint32_t full; -}; - -struct SH4Context { - uint32_t pc, spc; - uint32_t pr; - uint32_t gbr, vbr; - uint32_t mach, macl; - uint32_t r[16], rbnk[2][8], sgr; - uint32_t fr[16], xf[16]; - uint32_t fpul; - uint32_t dbr; - uint32_t sq[2][8]; - uint32_t sq_ext_addr[2]; - uint32_t preserve; - SR_T sr, ssr, old_sr; - FPSCR_T fpscr, old_fpscr; -}; - -void SRUpdated(SH4Context *ctx); -void FPSCRUpdated(SH4Context *ctx); -} -} - -#endif diff --git a/src/emu/dreamcast.cc b/src/emu/dreamcast.cc new file mode 100644 index 00000000..963e81b7 --- /dev/null +++ b/src/emu/dreamcast.cc @@ -0,0 +1,433 @@ +#include "core/core.h" +#include "cpu/backend/interpreter/interpreter_backend.h" +#include "cpu/backend/x64/x64_backend.h" +#include "cpu/frontend/sh4/sh4_frontend.h" +#include "emu/dreamcast.h" +#include "emu/profiler.h" +#include "holly/maple_controller.h" +#include "renderer/gl_backend.h" + +using namespace dreavm::core; +using namespace dreavm::cpu; +using namespace dreavm::cpu::backend::interpreter; +using namespace dreavm::cpu::backend::x64; +using namespace dreavm::cpu::frontend::sh4; +using namespace dreavm::emu; +using namespace dreavm::holly; +using namespace dreavm::renderer; +using namespace dreavm::system; +using namespace dreavm::trace; + +DEFINE_string(bios, "dc_bios.bin", "Path to BIOS"); +DEFINE_string(flash, "dc_flash.bin", "Path to flash ROM"); + +Dreamcast::Dreamcast() { + scheduler_ = std::unique_ptr(new Scheduler()); + memory_ = std::unique_ptr(new Memory()); + rb_ = std::unique_ptr(new GLBackend(sys_)); + rt_frontend_ = + std::unique_ptr(new SH4Frontend(*memory())); + rt_backend_ = std::unique_ptr(new X64Backend(*memory())); + runtime_ = std::unique_ptr( + new Runtime(*memory(), *rt_frontend_.get(), *rt_backend_.get())); + cpu_ = std::unique_ptr(new SH4(*memory(), *runtime())); + holly_ = std::unique_ptr(new Holly(this)); + pvr_ = std::unique_ptr(new PVR2(this)); + ta_ = std::unique_ptr(new TileAccelerator(this)); + gdrom_ = std::unique_ptr(new GDROM(this)); + maple_ = std::unique_ptr(new Maple(this)); +} + +void Dreamcast::Run(const char *path) { + if (!Init()) { + LOG_WARNING("Failed to initialize emulator"); + return; + } + + if (!LoadBios(FLAGS_bios.c_str())) { + return; + } + + if (!LoadFlash(FLAGS_flash.c_str())) { + return; + } + + if (path) { + LOG_INFO("Launching %s", path); + + if ((strstr(path, ".bin") && !LaunchBIN(path)) || + (strstr(path, ".gdi") && !LaunchGDI(path))) { + LOG_WARNING("Failed to launch %s", path); + return; + } + } + + static const std::chrono::nanoseconds step = HZ_TO_NANO(60); + std::chrono::nanoseconds time_remaining = std::chrono::nanoseconds(0); + auto current_time = std::chrono::high_resolution_clock::now(); + auto last_time = current_time; + + while (true) { + current_time = std::chrono::high_resolution_clock::now(); + time_remaining += current_time - last_time; + last_time = current_time; + + if (time_remaining < step) { + continue; + } + + time_remaining -= step; + + PumpEvents(); + + scheduler_->Tick(step); + + RenderFrame(); + } +} + +bool Dreamcast::Init() { + if (!sys_.Init()) { + return false; + } + + if (!rb_->Init()) { + return false; + } + + Profiler::Init(); + + InitMemory(); + InitRegisters(); + + cpu_->Init(); + holly_->Init(); + pvr_->Init(); + ta_->Init(); + gdrom_->Init(); + maple_->Init(); + + scheduler_->AddDevice(cpu()); + + return true; +} + +void Dreamcast::InitMemory() { + using namespace std::placeholders; + + memset(ram_, 0, sizeof(ram_)); + memset(unassigned_, 0, sizeof(unassigned_)); + memset(modem_mem_, 0, sizeof(modem_mem_)); + memset(aica_mem_, 0, sizeof(aica_mem_)); + memset(audio_ram_, 0, sizeof(audio_ram_)); + memset(expdev_mem_, 0, sizeof(expdev_mem_)); + memset(video_ram_, 0, sizeof(video_ram_)); + memset(palette_ram_, 0, sizeof(palette_ram_)); + + // main ram + memory_->Mount(BIOS_START, BIOS_END, MIRROR_MASK, bios_); + memory_->Mount(FLASH_START, FLASH_END, MIRROR_MASK, flash_); + memory_->Mount(MAIN_RAM_START, MAIN_RAM_END, MAIN_RAM_MIRROR_MASK, ram_); + memory_->Mount(UNASSIGNED_START, UNASSIGNED_END, MIRROR_MASK, unassigned_); + + // holly + memory_->Handle(HOLLY_REG_START, HOLLY_REG_END, MIRROR_MASK, + nullptr, // + nullptr, // + std::bind(&Holly::ReadRegister32, holly(), _1), // + nullptr, // + nullptr, // + nullptr, // + std::bind(&Holly::WriteRegister32, holly(), _1, _2), // + nullptr); + memory_->Mount(MODEM_REG_START, MODEM_REG_END, MIRROR_MASK, modem_mem_); + memory_->Mount(AICA_REG_START, AICA_REG_END, MIRROR_MASK, aica_mem_); + memory_->Handle(AUDIO_RAM_START, AUDIO_RAM_END, MIRROR_MASK, + nullptr, // + nullptr, // + std::bind(&Holly::ReadAudio32, holly(), _1), // + nullptr, // + nullptr, // + nullptr, // + std::bind(&Holly::WriteAudio32, holly(), _1, _2), // + nullptr); + memory_->Mount(EXPDEV_START, EXPDEV_END, MIRROR_MASK, expdev_mem_); + + // gdrom + memory_->Handle(GDROM_REG_START, GDROM_REG_END, MIRROR_MASK, + std::bind(&GDROM::ReadRegister8, gdrom(), _1), // + std::bind(&GDROM::ReadRegister16, gdrom(), _1), // + std::bind(&GDROM::ReadRegister32, gdrom(), _1), // + nullptr, // + std::bind(&GDROM::WriteRegister8, gdrom(), _1, _2), // + std::bind(&GDROM::WriteRegister16, gdrom(), _1, _2), // + std::bind(&GDROM::WriteRegister32, gdrom(), _1, _2), // + nullptr); + + // maple + memory_->Handle(MAPLE_REG_START, MAPLE_REG_END, MIRROR_MASK, + nullptr, // + nullptr, // + std::bind(&Maple::ReadRegister32, maple(), _1), // + nullptr, // + nullptr, // + nullptr, // + std::bind(&Maple::WriteRegister32, maple(), _1, _2), // + nullptr); + + // pvr2 + memory_->Mount(PVR_VRAM32_START, PVR_VRAM32_END, MIRROR_MASK, video_ram_); + memory_->Handle(PVR_VRAM64_START, PVR_VRAM64_END, MIRROR_MASK, + std::bind(&PVR2::ReadInterleaved8, pvr(), _1), // + std::bind(&PVR2::ReadInterleaved16, pvr(), _1), // + std::bind(&PVR2::ReadInterleaved32, pvr(), _1), // + nullptr, // + nullptr, // + std::bind(&PVR2::WriteInterleaved16, pvr(), _1, _2), // + std::bind(&PVR2::WriteInterleaved32, pvr(), _1, _2), // + nullptr); + memory_->Handle(PVR_REG_START, PVR_REG_END, MIRROR_MASK, + nullptr, // + nullptr, // + std::bind(&PVR2::ReadRegister32, pvr(), _1), // + nullptr, // + nullptr, // + nullptr, // + std::bind(&PVR2::WriteRegister32, pvr(), _1, _2), // + nullptr); + memory_->Mount(PVR_PALETTE_START, PVR_PALETTE_END, MIRROR_MASK, palette_ram_); + + // ta + // TODO handle YUV transfers from 0x10800000 - 0x10ffffe0 + memory_->Handle(TA_CMD_START, TA_CMD_END, 0x0, + nullptr, // + nullptr, // + nullptr, // + nullptr, // + nullptr, // + nullptr, // + std::bind(&TileAccelerator::WriteCommand32, ta(), _1, _2), // + nullptr); + memory_->Handle(TA_TEXTURE_START, TA_TEXTURE_END, 0x0, + nullptr, // + nullptr, // + nullptr, // + nullptr, // + nullptr, // + nullptr, // + std::bind(&TileAccelerator::WriteTexture32, ta(), _1, _2), // + nullptr); + + // cpu + memory_->Handle(SH4_REG_START, SH4_REG_END, MIRROR_MASK, + std::bind(&SH4::ReadRegister8, cpu(), _1), // + std::bind(&SH4::ReadRegister16, cpu(), _1), // + std::bind(&SH4::ReadRegister32, cpu(), _1), // + nullptr, // + std::bind(&SH4::WriteRegister8, cpu(), _1, _2), // + std::bind(&SH4::WriteRegister16, cpu(), _1, _2), // + std::bind(&SH4::WriteRegister32, cpu(), _1, _2), // + nullptr); + memory_->Handle(SH4_CACHE_START, SH4_CACHE_END, 0x0, + std::bind(&SH4::ReadCache8, cpu(), _1), // + std::bind(&SH4::ReadCache16, cpu(), _1), // + std::bind(&SH4::ReadCache32, cpu(), _1), // + std::bind(&SH4::ReadCache64, cpu(), _1), // + std::bind(&SH4::WriteCache8, cpu(), _1, _2), // + std::bind(&SH4::WriteCache16, cpu(), _1, _2), // + std::bind(&SH4::WriteCache32, cpu(), _1, _2), // + std::bind(&SH4::WriteCache64, cpu(), _1, _2)); + memory_->Handle(SH4_SQ_START, SH4_SQ_END, 0x0, + std::bind(&SH4::ReadSQ8, cpu(), _1), // + std::bind(&SH4::ReadSQ16, cpu(), _1), // + std::bind(&SH4::ReadSQ32, cpu(), _1), // + nullptr, // + std::bind(&SH4::WriteSQ8, cpu(), _1, _2), // + std::bind(&SH4::WriteSQ16, cpu(), _1, _2), // + std::bind(&SH4::WriteSQ32, cpu(), _1, _2), // + nullptr); +} + +void Dreamcast::InitRegisters() { +#define HOLLY_REG(addr, name, flags, default, type) \ + holly_regs_[name##_OFFSET] = {flags, default}; +#include "holly/holly_regs.inc" +#undef HOLLY_REG + +#define PVR_REG(addr, name, flags, default, type) \ + pvr_regs_[name##_OFFSET] = {flags, default}; +#include "holly/pvr2_regs.inc" +#undef PVR_REG +} + +bool Dreamcast::LoadBios(const char *path) { + FILE *fp = fopen(path, "rb"); + if (!fp) { + LOG_WARNING("Failed to open bios at \"%s\"", path); + return false; + } + + fseek(fp, 0, SEEK_END); + int size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + if (size != BIOS_SIZE) { + LOG_WARNING("Bios size mismatch, is %d, expected %d", size, BIOS_SIZE); + fclose(fp); + return false; + } + + int n = static_cast(fread(bios_, sizeof(uint8_t), size, fp)); + fclose(fp); + + if (n != size) { + LOG_WARNING("Bios read failed"); + return false; + } + + return true; +} + +bool Dreamcast::LoadFlash(const char *path) { + FILE *fp = fopen(path, "rb"); + if (!fp) { + LOG_WARNING("Failed to open flash at \"%s\"", path); + return false; + } + + fseek(fp, 0, SEEK_END); + int size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + if (size != FLASH_SIZE) { + LOG_WARNING("Flash size mismatch, is %d, expected %d", size, FLASH_SIZE); + fclose(fp); + return false; + } + + int n = static_cast(fread(flash_, sizeof(uint8_t), size, fp)); + fclose(fp); + + if (n != size) { + LOG_WARNING("Flash read failed"); + return false; + } + + return true; +} + +bool Dreamcast::LaunchBIN(const char *path) { + FILE *fp = fopen(path, "rb"); + if (!fp) { + return false; + } + + fseek(fp, 0, SEEK_END); + int size = ftell(fp); + fseek(fp, 0, SEEK_SET); + + uint8_t *data = reinterpret_cast(malloc(size)); + int n = static_cast(fread(data, sizeof(uint8_t), size, fp)); + fclose(fp); + + if (n != size) { + free(data); + return false; + } + + // load to 0x0c010000 (area 3) which is where 1ST_READ.BIN is normally + // loaded to + memory_->Memcpy(0x0c010000, data, size); + free(data); + + cpu_->SetPC(0x0c010000); + + return true; +} + +bool Dreamcast::LaunchGDI(const char *path) { + std::unique_ptr gdi(new GDI()); + + if (!gdi->Load(path)) { + return false; + } + + gdrom_->SetDisc(std::move(gdi)); + cpu_->SetPC(0xa0000000); + + return true; +} + +void Dreamcast::PumpEvents() { + SystemEvent ev; + + sys_.PumpEvents(); + + while (sys_.PollEvent(&ev)) { + switch (ev.type) { + case SE_KEY: { + // let the profiler take a stab at the input first + if (!Profiler::HandleInput(ev.key.code, ev.key.value)) { + // debug tracing + if (ev.key.code == K_F2) { + if (ev.key.value) { + ToggleTracing(); + } + } + // else, forward to maple + else { + maple_->HandleInput(0, ev.key.code, ev.key.value); + } + } + } break; + + case SE_MOUSEMOVE: { + Profiler::HandleMouseMove(ev.mousemove.x, ev.mousemove.y); + } break; + + case SE_RESIZE: { + rb_->ResizeVideo(ev.resize.width, ev.resize.height); + } break; + } + } +} + +void Dreamcast::ToggleTracing() { + if (!trace_writer_) { + char filename[PATH_MAX]; + GetNextTraceFilename(filename, sizeof(filename)); + + trace_writer_ = std::unique_ptr(new TraceWriter()); + + if (!trace_writer_->Open(filename)) { + trace_writer_ = nullptr; + + LOG_INFO("Failed to start tracing"); + + return; + } + + LOG_INFO("Begin tracing to %s", filename); + } else { + trace_writer_ = nullptr; + + LOG_INFO("End tracing"); + } +} + +void Dreamcast::RenderFrame() { + rb_->BeginFrame(); + + ta_->RenderLastContext(); + + // render stats + char stats[512]; + snprintf(stats, sizeof(stats), "%.2f fps, %.2f vbps", pvr_->fps(), + pvr_->vbps()); + rb_->RenderText2D(0, 0, 12.0f, 0xffffffff, stats); + + // render profiler + Profiler::Render(rb()); + + rb_->EndFrame(); +} diff --git a/src/emu/dreamcast.h b/src/emu/dreamcast.h new file mode 100644 index 00000000..506beeea --- /dev/null +++ b/src/emu/dreamcast.h @@ -0,0 +1,173 @@ +#ifndef DREAMCAST_H +#define DREAMCAST_H + +#include +#include "cpu/backend/backend.h" +#include "cpu/frontend/frontend.h" +#include "cpu/runtime.h" +#include "cpu/sh4.h" +#include "emu/memory.h" +#include "emu/scheduler.h" +#include "holly/gdrom.h" +#include "holly/holly.h" +#include "holly/maple.h" +#include "holly/pvr2.h" +#include "holly/tile_accelerator.h" +#include "renderer/backend.h" +#include "system/system.h" +#include "trace/trace.h" + +namespace dreavm { +namespace emu { + +// +// memory layout +// +#define MEMORY_REGION(name, start, end) \ + name##_START = start, name##_END = end, name##_SIZE = end - start + 1 + +enum { + // ignore all access modifier bits + MIRROR_MASK = ~cpu::ADDR_MASK, + + // main ram is mirrored an additional four times: + // 0x0c000000 - 0x0cffffff + // 0x0d000000 - 0x0dffffff + // 0x0e000000 - 0x0effffff + // 0x0f000000 - 0x0fffffff + MAIN_RAM_MIRROR_MASK = MIRROR_MASK | 0x03000000, + + MEMORY_REGION(BIOS, 0x00000000, 0x001fffff), + MEMORY_REGION(FLASH, 0x00200000, 0x0021ffff), + MEMORY_REGION(HOLLY_REG, 0x005f6000, 0x005f7fff), + MEMORY_REGION(MAPLE_REG, 0x005f6c00, 0x005f6fff), + MEMORY_REGION(GDROM_REG, 0x005f7000, 0x005f77ff), + MEMORY_REGION(PVR_REG, 0x005f8000, 0x005f8fff), + MEMORY_REGION(PVR_PALETTE, 0x005f9000, 0x005f9fff), + MEMORY_REGION(MODEM_REG, 0x00600000, 0x0067ffff), + MEMORY_REGION(AICA_REG, 0x00700000, 0x00710fff), + MEMORY_REGION(AUDIO_RAM, 0x00800000, 0x009fffff), + MEMORY_REGION(EXPDEV, 0x01000000, 0x01ffffff), + MEMORY_REGION(PVR_VRAM32, 0x04000000, 0x047fffff), + MEMORY_REGION(PVR_VRAM64, 0x05000000, 0x057fffff), + MEMORY_REGION(MAIN_RAM, 0x0c000000, 0x0cffffff), + MEMORY_REGION(TA_CMD, 0x10000000, 0x107fffff), + MEMORY_REGION(TA_TEXTURE, 0x11000000, 0x11ffffff), + MEMORY_REGION(UNASSIGNED, 0x14000000, 0x1bffffff), + MEMORY_REGION(SH4_REG, 0x1c000000, 0x1fffffff), + MEMORY_REGION(SH4_CACHE, 0x7c000000, 0x7fffffff), + MEMORY_REGION(SH4_SQ, 0xe0000000, 0xe3ffffff) +}; + +// +// registers +// +enum { // + R = 0x1, + W = 0x2, + RW = 0x3, + UNDEFINED = 0x0 +}; + +struct Register { + Register() : flags(RW), value(0) {} + Register(uint8_t flags, uint32_t value) : flags(flags), value(value) {} + + uint8_t flags; + uint32_t value; +}; + +enum { +#define HOLLY_REG(addr, name, flags, default, type) \ + name##_OFFSET = (addr - emu::HOLLY_REG_START) >> 2, +#include "holly/holly_regs.inc" +#undef HOLLY_REG + +#define PVR_REG(addr, name, flags, default_value, type) \ + name##_OFFSET = (addr - emu::PVR_REG_START) >> 2, +#include "holly/pvr2_regs.inc" +#undef PVR_REG +}; + +class Dreamcast { + public: + emu::Scheduler *scheduler() { return scheduler_.get(); } + emu::Memory *memory() { return memory_.get(); } + renderer::Backend *rb() { return rb_.get(); } + cpu::Runtime *runtime() { return runtime_.get(); } + cpu::SH4 *cpu() { return cpu_.get(); } + holly::Holly *holly() { return holly_.get(); } + holly::PVR2 *pvr() { return pvr_.get(); } + holly::TileAccelerator *ta() { return ta_.get(); } + holly::GDROM *gdrom() { return gdrom_.get(); } + holly::Maple *maple() { return maple_.get(); } + trace::TraceWriter *trace_writer() { return trace_writer_.get(); } + + Register *holly_regs() { return holly_regs_; } + Register *pvr_regs() { return pvr_regs_; } + + uint8_t *audio_ram() { return audio_ram_; } + uint8_t *palette_ram() { return palette_ram_; } + uint8_t *video_ram() { return video_ram_; } + + Dreamcast(); + + void Run(const char *path); + +#define HOLLY_REG(offset, name, flags, default, type) \ + type &name{reinterpret_cast(holly_regs_[name##_OFFSET].value)}; +#include "holly/holly_regs.inc" +#undef HOLLY_REG + +#define PVR_REG(offset, name, flags, default, type) \ + type &name{reinterpret_cast(pvr_regs_[name##_OFFSET].value)}; +#include "holly/pvr2_regs.inc" +#undef PVR_REG + + private: + bool Init(); + void InitMemory(); + void InitRegisters(); + + bool LoadBios(const char *path); + bool LoadFlash(const char *path); + bool LaunchBIN(const char *path); + bool LaunchGDI(const char *path); + + void PumpEvents(); + void ToggleTracing(); + void RenderFrame(); + + system::System sys_; + std::unique_ptr scheduler_; + std::unique_ptr memory_; + std::unique_ptr rb_; + std::unique_ptr rt_frontend_; + std::unique_ptr rt_backend_; + std::unique_ptr runtime_; + std::unique_ptr cpu_; + std::unique_ptr holly_; + std::unique_ptr pvr_; + std::unique_ptr ta_; + std::unique_ptr gdrom_; + std::unique_ptr maple_; + std::unique_ptr trace_writer_; + + Register holly_regs_[HOLLY_REG_SIZE >> 2]; + Register pvr_regs_[PVR_REG_SIZE >> 2]; + + uint8_t bios_[BIOS_SIZE]; + uint8_t flash_[FLASH_SIZE]; + uint8_t ram_[MAIN_RAM_SIZE]; + uint8_t unassigned_[UNASSIGNED_SIZE]; + uint8_t modem_mem_[MODEM_REG_SIZE]; + uint8_t aica_mem_[AICA_REG_SIZE]; + uint8_t audio_ram_[AUDIO_RAM_SIZE]; + uint8_t expdev_mem_[EXPDEV_SIZE]; + uint8_t video_ram_[PVR_VRAM32_SIZE]; + uint8_t palette_ram_[PVR_PALETTE_SIZE]; +}; +} +} + +#endif diff --git a/src/emu/emulator.cc b/src/emu/emulator.cc deleted file mode 100644 index f81be9da..00000000 --- a/src/emu/emulator.cc +++ /dev/null @@ -1,305 +0,0 @@ -#include "core/core.h" -#include "cpu/backend/interpreter/interpreter_backend.h" -#include "cpu/backend/x64/x64_backend.h" -#include "cpu/frontend/sh4/sh4_frontend.h" -#include "emu/emulator.h" -#include "emu/profiler.h" -#include "holly/maple_controller.h" -#include "renderer/gl_backend.h" - -using namespace dreavm::core; -using namespace dreavm::cpu; -using namespace dreavm::cpu::backend::interpreter; -using namespace dreavm::cpu::backend::x64; -using namespace dreavm::cpu::frontend::sh4; -using namespace dreavm::emu; -using namespace dreavm::holly; -using namespace dreavm::renderer; -using namespace dreavm::system; - -DEFINE_string(bios, "dc_bios.bin", "Path to BIOS"); -DEFINE_string(flash, "dc_flash.bin", "Path to flash ROM"); - -Emulator::Emulator() { - bios_ = new uint8_t[BIOS_SIZE]; - flash_ = new uint8_t[FLASH_SIZE]; - ram_ = new uint8_t[MAIN_RAM_M0_SIZE]; - unassigned_ = new uint8_t[UNASSIGNED_SIZE]; - - scheduler_ = new Scheduler(); - memory_ = new Memory(); - runtime_ = new Runtime(*memory_); - processor_ = new SH4(*memory_); - holly_ = new Holly(*scheduler_, *memory_, *processor_); - rt_frontend_ = new SH4Frontend(*memory_); - // rt_backend_ = new InterpreterBackend(*memory_); - rt_backend_ = new X64Backend(*memory_); - rb_ = new GLBackend(sys_); -} - -Emulator::~Emulator() { - Profiler::Shutdown(); - - delete[] bios_; - delete[] flash_; - delete[] ram_; - delete[] unassigned_; - - delete scheduler_; - delete memory_; - delete runtime_; - delete processor_; - delete holly_; - delete rt_frontend_; - delete rt_backend_; - delete rb_; -} - -void Emulator::Run(const char *path) { - if (!Init()) { - LOG_WARNING("Failed to initialize emulator"); - return; - } - - if (path) { - LOG_INFO("Launching %s", path); - - if ((strstr(path, ".bin") && !LaunchBIN(path)) || - (strstr(path, ".gdi") && !LaunchGDI(path))) { - LOG_WARNING("Failed to launch %s", path); - return; - } - } - - static const std::chrono::nanoseconds step = HZ_TO_NANO(60); - std::chrono::nanoseconds time_remaining = std::chrono::nanoseconds(0); - auto current_time = std::chrono::high_resolution_clock::now(); - auto last_time = current_time; - - while (true) { - current_time = std::chrono::high_resolution_clock::now(); - time_remaining += current_time - last_time; - last_time = current_time; - - if (time_remaining < step) { - continue; - } - - time_remaining -= step; - - PumpEvents(); - - scheduler_->Tick(step); - - RenderFrame(); - } -} - -bool Emulator::Init() { - InitMemory(); - - if (!sys_.Init()) { - return false; - } - - if (!rb_->Init()) { - return false; - } - - if (!Profiler::Init()) { - return false; - } - - if (!LoadBios(FLAGS_bios.c_str())) { - return false; - } - - if (!LoadFlash(FLAGS_flash.c_str())) { - return false; - } - - if (!runtime_->Init(rt_frontend_, rt_backend_)) { - return false; - } - - if (!holly_->Init(rb_)) { - return false; - } - - if (!processor_->Init(runtime_)) { - return false; - } - - scheduler_->AddDevice(processor_); - - Reset(); - - return true; -} - -void Emulator::InitMemory() { - memory_->Mount(BIOS_START, BIOS_END, MIRROR_MASK, bios_); - memory_->Mount(FLASH_START, FLASH_END, MIRROR_MASK, flash_); - memory_->Mount(MAIN_RAM_M0_START, MAIN_RAM_M0_END, MIRROR_MASK, ram_); - memory_->Mount(MAIN_RAM_M1_START, MAIN_RAM_M1_END, MIRROR_MASK, ram_); - memory_->Mount(MAIN_RAM_M2_START, MAIN_RAM_M2_END, MIRROR_MASK, ram_); - memory_->Mount(MAIN_RAM_M3_START, MAIN_RAM_M3_END, MIRROR_MASK, ram_); - memory_->Mount(UNASSIGNED_START, UNASSIGNED_END, MIRROR_MASK, unassigned_); -} - -bool Emulator::LoadBios(const char *path) { - FILE *fp = fopen(path, "rb"); - if (!fp) { - LOG_WARNING("Failed to open bios at \"%s\"", path); - return false; - } - - fseek(fp, 0, SEEK_END); - int size = ftell(fp); - fseek(fp, 0, SEEK_SET); - - if (size != BIOS_SIZE) { - LOG_WARNING("Bios size mismatch, is %d, expected %d", size, BIOS_SIZE); - fclose(fp); - return false; - } - - int n = static_cast(fread(bios_, sizeof(uint8_t), size, fp)); - fclose(fp); - - if (n != size) { - LOG_WARNING("Bios read failed"); - return false; - } - - return true; -} - -bool Emulator::LoadFlash(const char *path) { - FILE *fp = fopen(path, "rb"); - if (!fp) { - LOG_WARNING("Failed to open flash at \"%s\"", path); - return false; - } - - fseek(fp, 0, SEEK_END); - int size = ftell(fp); - fseek(fp, 0, SEEK_SET); - - if (size != FLASH_SIZE) { - LOG_WARNING("Flash size mismatch, is %d, expected %d", size, FLASH_SIZE); - fclose(fp); - return false; - } - - int n = static_cast(fread(flash_, sizeof(uint8_t), size, fp)); - fclose(fp); - - if (n != size) { - LOG_WARNING("Flash read failed"); - return false; - } - - return true; -} - -void Emulator::Reset() { - memset(ram_, 0, MAIN_RAM_M0_SIZE); - memset(unassigned_, 0, UNASSIGNED_SIZE); -} - -bool Emulator::LaunchBIN(const char *path) { - FILE *fp = fopen(path, "rb"); - if (!fp) { - return false; - } - - fseek(fp, 0, SEEK_END); - int size = ftell(fp); - fseek(fp, 0, SEEK_SET); - - uint8_t *data = reinterpret_cast(malloc(size)); - int n = static_cast(fread(data, sizeof(uint8_t), size, fp)); - fclose(fp); - - if (n != size) { - free(data); - return false; - } - - // load to 0x0c010000 (area 3) which is where 1ST_READ.BIN is normally - // loaded to - memory_->Memcpy(0x0c010000, data, size); - free(data); - - // restart to where the bin was loaded - processor_->Reset(0x0c010000); - - return true; -} - -bool Emulator::LaunchGDI(const char *path) { - std::unique_ptr gdi(new GDI()); - - if (!gdi->Load(path)) { - return false; - } - - holly_->gdrom().SetDisc(std::move(gdi)); - - // restart to bios - processor_->Reset(0xa0000000); - - return true; -} - -void Emulator::PumpEvents() { - SystemEvent ev; - - sys_.PumpEvents(); - - while (sys_.PollEvent(&ev)) { - switch (ev.type) { - case SE_KEY: { - // let the profiler take a stab at the input first - if (!Profiler::HandleInput(ev.key.code, ev.key.value)) { - // debug tracing - if (ev.key.code == K_F2) { - if (ev.key.value) { - holly_->pvr().ToggleTracing(); - } - } - // else, forward to maple - else { - holly_->maple().HandleInput(0, ev.key.code, ev.key.value); - } - } - } break; - - case SE_MOUSEMOVE: { - Profiler::HandleMouseMove(ev.mousemove.x, ev.mousemove.y); - } break; - - case SE_RESIZE: { - rb_->ResizeVideo(ev.resize.width, ev.resize.height); - } break; - } - } -} - -void Emulator::RenderFrame() { - rb_->BeginFrame(); - - holly_->pvr().RenderLastFrame(); - - // render stats - char stats[512]; - snprintf(stats, sizeof(stats), "%.2f fps, %.2f vbps", holly_->pvr().fps(), - holly_->pvr().vbps()); - rb_->RenderText2D(0, 0, 12.0f, 0xffffffff, stats); - - // render profiler - Profiler::Render(rb_); - - rb_->EndFrame(); -} diff --git a/src/emu/emulator.h b/src/emu/emulator.h deleted file mode 100644 index a55bf366..00000000 --- a/src/emu/emulator.h +++ /dev/null @@ -1,51 +0,0 @@ -#ifndef EMULATOR_H -#define EMULATOR_H - -#include "cpu/sh4.h" -#include "cpu/backend/backend.h" -#include "cpu/frontend/frontend.h" -#include "cpu/runtime.h" -#include "holly/holly.h" -#include "renderer/backend.h" -#include "system/system.h" - -namespace dreavm { -namespace emu { - -class Emulator { - public: - Emulator(); - ~Emulator(); - - void Run(const char *path); - - private: - bool Init(); - void InitMemory(); - bool LoadBios(const char *path); - bool LoadFlash(const char *path); - - void Reset(); - bool LaunchBIN(const char *path); - bool LaunchGDI(const char *path); - void PumpEvents(); - void RenderFrame(); - - system::System sys_; - emu::Scheduler *scheduler_; - emu::Memory *memory_; - cpu::Runtime *runtime_; - cpu::SH4 *processor_; - holly::Holly *holly_; - cpu::frontend::Frontend *rt_frontend_; - cpu::backend::Backend *rt_backend_; - renderer::Backend *rb_; - uint8_t *bios_; - uint8_t *flash_; - uint8_t *ram_; - uint8_t *unassigned_; -}; -} -} - -#endif diff --git a/src/emu/memory.h b/src/emu/memory.h index b31511b7..368cc838 100644 --- a/src/emu/memory.h +++ b/src/emu/memory.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include "core/core.h" @@ -11,40 +12,6 @@ namespace dreavm { namespace emu { -// -// memory map. the dreamcast uses 32-bit logical addresses, but the physical -// address range is only 29-bits -// -#define MEMORY_REGION(name, start, end) \ - name##_START = start, name##_END = end, name##_SIZE = end - start + 1 - -enum { - // ignore all modifier bits - MIRROR_MASK = 0xe0000000, - - // the Memory class doesn't actually mount these regions, but it's nice - // having them listed in a single place to visualize - MEMORY_REGION(BIOS, 0x00000000, 0x001fffff), - MEMORY_REGION(FLASH, 0x00200000, 0x0021ffff), - MEMORY_REGION(HOLLY_REG, 0x005f6000, 0x005f7fff), - MEMORY_REGION(PVR_REG, 0x005f8000, 0x005f8fff), - MEMORY_REGION(PVR_PALETTE, 0x005f9000, 0x005f9fff), - MEMORY_REGION(MODEM_REG, 0x00600000, 0x0067ffff), - MEMORY_REGION(AICA_REG, 0x00700000, 0x00710fff), - MEMORY_REGION(AUDIO_RAM, 0x00800000, 0x009fffff), - MEMORY_REGION(EXPDEV, 0x01000000, 0x01ffffff), - MEMORY_REGION(PVR_VRAM32, 0x04000000, 0x047fffff), - MEMORY_REGION(PVR_VRAM64, 0x05000000, 0x057fffff), - MEMORY_REGION(MAIN_RAM_M0, 0x0c000000, 0x0cffffff), - MEMORY_REGION(MAIN_RAM_M1, 0x0d000000, 0x0dffffff), - MEMORY_REGION(MAIN_RAM_M2, 0x0e000000, 0x0effffff), - MEMORY_REGION(MAIN_RAM_M3, 0x0f000000, 0x0fffffff), - MEMORY_REGION(TA_CMD, 0x10000000, 0x107fffff), - MEMORY_REGION(TA_TEXTURE, 0x11000000, 0x11ffffff), - MEMORY_REGION(UNASSIGNED, 0x14000000, 0x1bffffff), - MEMORY_REGION(SH4_REG, 0x1c000000, 0x1fffffff) -}; - // // single level page table implementation // @@ -52,7 +19,7 @@ typedef uint8_t TableHandle; enum { UNMAPPED = (TableHandle)0, - PAGE_BITS = 20, + PAGE_BITS = 22, OFFSET_BITS = 32 - PAGE_BITS, MAX_PAGE_SIZE = 1 << OFFSET_BITS, MAX_ENTRIES = 1 << PAGE_BITS, @@ -124,14 +91,14 @@ class PageTable { // // physical memory emulation // -typedef uint8_t (*R8Handler)(void *, uint32_t); -typedef uint16_t (*R16Handler)(void *, uint32_t); -typedef uint32_t (*R32Handler)(void *, uint32_t); -typedef uint64_t (*R64Handler)(void *, uint32_t); -typedef void (*W8Handler)(void *, uint32_t, uint8_t); -typedef void (*W16Handler)(void *, uint32_t, uint16_t); -typedef void (*W32Handler)(void *, uint32_t, uint32_t); -typedef void (*W64Handler)(void *, uint32_t, uint64_t); +typedef std::function R8Handler; +typedef std::function R16Handler; +typedef std::function R32Handler; +typedef std::function R64Handler; +typedef std::function W8Handler; +typedef std::function W16Handler; +typedef std::function W32Handler; +typedef std::function W64Handler; struct MemoryBank { MemoryBank() @@ -139,7 +106,6 @@ struct MemoryBank { mirror_mask(0), logical_addr(0), physical_addr(nullptr), - ctx(nullptr), r8(nullptr), r16(nullptr), r32(nullptr), @@ -153,7 +119,6 @@ struct MemoryBank { uint32_t mirror_mask; uint32_t logical_addr; uint8_t *physical_addr; - void *ctx; R8Handler r8; R16Handler r16; R32Handler r32; @@ -197,13 +162,12 @@ class Memory { } void Handle(uint32_t logical_start, uint32_t logical_end, - uint32_t mirror_mask, void *ctx, R8Handler r8, R16Handler r16, + uint32_t mirror_mask, R8Handler r8, R16Handler r16, R32Handler r32, R64Handler r64, W8Handler w8, W16Handler w16, W32Handler w32, W64Handler w64) { MemoryBank &bank = AllocBank(); bank.mirror_mask = ~mirror_mask; bank.logical_addr = logical_start; - bank.ctx = ctx; bank.r8 = r8; bank.r16 = r16; bank.r32 = r32; @@ -299,7 +263,7 @@ class Memory { return bank; } - template + template MemoryBank::*HANDLER> inline INT ReadBytes(uint32_t addr) { TableHandle handle = table_.Lookup(addr); MemoryBank &bank = banks_[handle]; @@ -307,7 +271,7 @@ class Memory { if (bank.physical_addr) { return *(INT *)(bank.physical_addr + offset); } else if (bank.*HANDLER) { - return (bank.*HANDLER)(bank.ctx, offset); + return (bank.*HANDLER)(offset); } else { LOG_FATAL("Attempting to read from unmapped address 0x%x", addr); } @@ -315,7 +279,8 @@ class Memory { return 0; } - template + template MemoryBank::*HANDLER> inline void WriteBytes(uint32_t addr, INT value) { TableHandle handle = table_.Lookup(addr); MemoryBank &bank = banks_[handle]; @@ -323,7 +288,7 @@ class Memory { if (bank.physical_addr) { *(INT *)(bank.physical_addr + offset) = value; } else if (bank.*HANDLER) { - (bank.*HANDLER)(bank.ctx, offset, value); + (bank.*HANDLER)(offset, value); } else { LOG_FATAL("Attempting to write to unmapped address 0x%x", addr); } diff --git a/src/emu/profiler.cc b/src/emu/profiler.cc index ae2a264c..b24ac26b 100644 --- a/src/emu/profiler.cc +++ b/src/emu/profiler.cc @@ -25,7 +25,7 @@ uint32_t Profiler::ScopeColor(const char *name) { return hash(std::string(name)) & 0xffffff; } -bool Profiler::Init() { +void Profiler::Init() { MicroProfileOnThreadCreate("main"); // register and enable gpu and runtime group by default @@ -38,12 +38,8 @@ bool Profiler::Init() { // render time / average time bars by default g_MicroProfile.nBars |= MP_DRAW_TIMERS | MP_DRAW_AVERAGE | MP_DRAW_CALL_COUNT; - - return true; } -void Profiler::Shutdown() {} - bool Profiler::HandleInput(Keycode key, int16_t value) { if (key == K_F1) { if (value) { diff --git a/src/emu/profiler.h b/src/emu/profiler.h index 534374a5..8e3c1085 100644 --- a/src/emu/profiler.h +++ b/src/emu/profiler.h @@ -21,8 +21,8 @@ class Profiler { public: static uint32_t ScopeColor(const char *name); - static bool Init(); - static void Shutdown(); + static void Init(); + static bool HandleInput(system::Keycode key, int16_t value); static bool HandleMouseMove(int x, int y); static void Render(renderer::Backend *backend); diff --git a/src/holly/gdrom.cc b/src/holly/gdrom.cc index 6060eaf9..cc6d5aac 100644 --- a/src/holly/gdrom.cc +++ b/src/holly/gdrom.cc @@ -1,6 +1,5 @@ #include "core/core.h" -#include "holly/holly.h" -#include "holly/gdrom.h" +#include "emu/dreamcast.h" #include "holly/gdrom_replies.inc" using namespace dreavm::core; @@ -11,29 +10,29 @@ using namespace dreavm::holly; #define SWAP_24(fad) \ (((fad & 0xff) << 16) | (fad & 0x00ff00) | ((fad & 0xff0000) >> 16)) -GDROM::GDROM(Memory &memory, Holly &holly) - : memory_(memory), - holly_(holly), - current_disc_(nullptr), +GDROM::GDROM(Dreamcast *dc) + : dc_(dc), + features_{0}, + intreason_{0}, + sectnum_{0}, + byte_count_{0}, + status_{0}, pio_idx_(0), pio_size_(0), dma_size_(0), - state_(STATE_STANDBY) { - dma_buffer_ = reinterpret_cast(malloc(0x1000000)); + state_(STATE_STANDBY), + current_disc_(nullptr) { + dma_buffer_ = new uint8_t[0x1000000]; } -GDROM::~GDROM() { free(dma_buffer_); } +GDROM::~GDROM() { delete[] dma_buffer_; } -bool GDROM::Init() { - features_.full = 0; - intreason_.full = 0; - sectnum_.full = 0; - byte_count_.full = 0; - status_.full = 0; +void GDROM::Init() { + memory_ = dc_->memory(); + holly_ = dc_->holly(); + holly_regs_ = dc_->holly_regs(); SetDisc(nullptr); - - return true; } void GDROM::SetDisc(std::unique_ptr disc) { @@ -47,8 +46,24 @@ void GDROM::SetDisc(std::unique_ptr disc) { status_.BSY = 0; } -uint32_t GDROM::ReadRegister(Register ®, uint32_t addr) { - switch (addr) { +uint8_t GDROM::ReadRegister8(uint32_t addr) { + return static_cast(ReadRegister32(addr)); +} + +uint16_t GDROM::ReadRegister16(uint32_t addr) { + return static_cast(ReadRegister32(addr)); +} + +uint32_t GDROM::ReadRegister32(uint32_t addr) { + uint32_t offset = (0x1000 + addr) >> 2; + Register ® = holly_regs_[offset]; + + if (!(reg.flags & R)) { + LOG_WARNING("Invalid read access at 0x%x", addr); + return 0; + } + + switch (offset) { // gdrom regs case GD_ALTSTAT_DEVCTRL_OFFSET: // this register is the same as the status register, but it does not @@ -85,7 +100,7 @@ uint32_t GDROM::ReadRegister(Register ®, uint32_t addr) { return 0; case GD_STATUS_COMMAND_OFFSET: - holly_.UnrequestInterrupt(HOLLY_INTC_G1GDINT); + holly_->UnrequestInterrupt(HOLLY_INTC_G1GDINT); return status_.full; // g1 bus regs @@ -94,12 +109,27 @@ uint32_t GDROM::ReadRegister(Register ®, uint32_t addr) { return reg.value; } -void GDROM::WriteRegister(Register ®, uint32_t addr, uint32_t value) { - uint32_t old_value = reg.value; +void GDROM::WriteRegister8(uint32_t addr, uint8_t value) { + WriteRegister32(addr, static_cast(value)); +} +void GDROM::WriteRegister16(uint32_t addr, uint16_t value) { + WriteRegister32(addr, static_cast(value)); +} + +void GDROM::WriteRegister32(uint32_t addr, uint32_t value) { + uint32_t offset = (0x1000 + addr) >> 2; + Register ® = holly_regs_[offset]; + + if (!(reg.flags & W)) { + LOG_WARNING("Invalid write access at 0x%x", addr); + return; + } + + uint32_t old_value = reg.value; reg.value = value; - switch (addr) { + switch (offset) { // gdrom regs case GD_ALTSTAT_DEVCTRL_OFFSET: // LOG_INFO("GD_DEVCTRL 0x%x", (uint32_t)value); @@ -159,23 +189,23 @@ void GDROM::WriteRegister(Register ®, uint32_t addr, uint32_t value) { // SB_GDSTAR, SB_GDLEN, or SB_GDDIR register is overwritten while a DMA // operation is in progress, the new setting has no effect on the // current DMA operation. - CHECK_EQ(holly_.SB_GDEN, 1); // dma enabled - CHECK_EQ(holly_.SB_GDDIR, 1); // gd-rom -> system memory - CHECK_EQ(holly_.SB_GDLEN, (uint32_t)dma_size_); + CHECK_EQ(dc_->SB_GDEN, 1); // dma enabled + CHECK_EQ(dc_->SB_GDDIR, 1); // gd-rom -> system memory + CHECK_EQ(dc_->SB_GDLEN, (uint32_t)dma_size_); - int transfer_size = holly_.SB_GDLEN; - uint32_t start = holly_.SB_GDSTAR; + int transfer_size = dc_->SB_GDLEN; + uint32_t start = dc_->SB_GDSTAR; printf("GD DMA START 0x%x -> 0x%x, 0x%x bytes\n", start, start + transfer_size, transfer_size); - memory_.Memcpy(start, dma_buffer_, transfer_size); + memory_->Memcpy(start, dma_buffer_, transfer_size); // done - holly_.SB_GDSTARD = start + transfer_size; - holly_.SB_GDLEND = transfer_size; - holly_.SB_GDST = 0; - holly_.RequestInterrupt(HOLLY_INTC_G1DEINT); + dc_->SB_GDSTARD = start + transfer_size; + dc_->SB_GDLEND = transfer_size; + dc_->SB_GDST = 0; + holly_->RequestInterrupt(HOLLY_INTC_G1DEINT); // finish off CD_READ command TriggerEvent(EV_SPI_CMD_DONE); @@ -194,7 +224,7 @@ void GDROM::TriggerEvent(GDEvent ev, intptr_t arg0, intptr_t arg1) { status_.DRDY = 1; status_.BSY = 0; - holly_.RequestInterrupt(HOLLY_INTC_G1GDINT); + holly_->RequestInterrupt(HOLLY_INTC_G1GDINT); state_ = STATE_STANDBY; } break; @@ -229,7 +259,7 @@ void GDROM::TriggerEvent(GDEvent ev, intptr_t arg0, intptr_t arg1) { status_.DRQ = 1; status_.BSY = 0; - holly_.RequestInterrupt(HOLLY_INTC_G1GDINT); + holly_->RequestInterrupt(HOLLY_INTC_G1GDINT); state_ = STATE_SPI_READ_DATA; } break; @@ -264,7 +294,7 @@ void GDROM::TriggerEvent(GDEvent ev, intptr_t arg0, intptr_t arg1) { status_.DRQ = 1; status_.BSY = 0; - holly_.RequestInterrupt(HOLLY_INTC_G1GDINT); + holly_->RequestInterrupt(HOLLY_INTC_G1GDINT); state_ = STATE_SPI_WRITE_DATA; } break; @@ -285,7 +315,7 @@ void GDROM::TriggerEvent(GDEvent ev, intptr_t arg0, intptr_t arg1) { status_.BSY = 0; status_.DRQ = 0; - holly_.RequestInterrupt(HOLLY_INTC_G1GDINT); + holly_->RequestInterrupt(HOLLY_INTC_G1GDINT); state_ = STATE_STANDBY; } break; diff --git a/src/holly/gdrom.h b/src/holly/gdrom.h index 4ac01fc5..cd89670d 100644 --- a/src/holly/gdrom.h +++ b/src/holly/gdrom.h @@ -3,11 +3,18 @@ #include #include "emu/disc.h" -#include "emu/memory.h" namespace dreavm { +namespace emu { +class Dreamcast; +class Memory; +struct Register; +} + namespace holly { +class Holly; + enum GDState { // STATE_STANDBY, STATE_SPI_READ_CMD, @@ -73,12 +80,12 @@ enum AudioStatus { }; union TOCEntry { + uint32_t full; struct { uint32_t adr : 4; uint32_t ctrl : 4; uint32_t fad : 24; }; - uint32_t full; }; struct TOC { @@ -121,34 +128,34 @@ enum DiscType { }; union GD_FEATURES_T { + uint32_t full; struct { uint32_t dma : 1; uint32_t reserved : 31; }; - - uint32_t full; }; union GD_INTREASON_T { + uint32_t full; struct { uint32_t CoD : 1; // "0" indicates data and "1" indicates a command. uint32_t IO : 1; // "1" indicates transfer from device to host, and "0" // from host to device. uint32_t reserved : 30; }; - uint32_t full; }; union GD_SECTNUM_T { + uint32_t full; struct { uint32_t status : 4; uint32_t format : 4; uint32_t reserved : 24; }; - uint32_t full; }; union GD_STATUS_T { + uint32_t full; struct { uint32_t CHECK : 1; // Becomes "1" when an error has occurred during // execution of the command the previous time. @@ -166,16 +173,15 @@ union GD_STATUS_T { // command block. uint32_t reserved : 24; }; - uint32_t full; }; union GD_BYTECT_T { + uint32_t full; struct { uint32_t lo : 8; uint32_t hi : 8; uint32_t reserved : 16; }; - uint32_t full; }; enum SectorFormat { @@ -195,27 +201,24 @@ enum DataMask { MASK_OTHER = 0x1 }; -class Holly; -struct Register; - class GDROM { public: - GDROM(emu::Memory &memory, Holly &holly); + GDROM(emu::Dreamcast *dc); ~GDROM(); - bool Init(); + void Init(); + void SetDisc(std::unique_ptr disc); - uint32_t ReadRegister(Register ®, uint32_t addr); - void WriteRegister(Register ®, uint32_t addr, uint32_t value); + uint8_t ReadRegister8(uint32_t addr); + uint16_t ReadRegister16(uint32_t addr); + uint32_t ReadRegister32(uint32_t addr); + + void WriteRegister8(uint32_t addr, uint8_t value); + void WriteRegister16(uint32_t addr, uint16_t value); + void WriteRegister32(uint32_t addr, uint32_t value); private: - GD_FEATURES_T features_; - GD_INTREASON_T intreason_; - GD_SECTNUM_T sectnum_; - GD_BYTECT_T byte_count_; - GD_STATUS_T status_; - void TriggerEvent(GDEvent ev); void TriggerEvent(GDEvent ev, intptr_t arg0, intptr_t arg1); void ProcessATACommand(ATACommand cmd); @@ -227,18 +230,26 @@ class GDROM { int ReadSectors(int fad, SectorFormat format, DataMask mask, int num_sectors, uint8_t *dst); - emu::Memory &memory_; - Holly &holly_; - std::unique_ptr current_disc_; + emu::Dreamcast *dc_; + emu::Memory *memory_; + holly::Holly *holly_; + emu::Register *holly_regs_; + + GD_FEATURES_T features_; + GD_INTREASON_T intreason_; + GD_SECTNUM_T sectnum_; + GD_BYTECT_T byte_count_; + GD_STATUS_T status_; uint8_t pio_buffer_[0xfa00]; int pio_idx_; int pio_size_; uint8_t *dma_buffer_; int dma_size_; - GDState state_; int spi_read_offset_; + + std::unique_ptr current_disc_; }; } } diff --git a/src/holly/holly.cc b/src/holly/holly.cc index 5950c9ea..15962b37 100644 --- a/src/holly/holly.cc +++ b/src/holly/holly.cc @@ -1,9 +1,5 @@ #include "core/core.h" -#include "cpu/sh4.h" -#include "holly/gdrom.h" -#include "holly/holly.h" -#include "holly/maple.h" -#include "holly/pvr2.h" +#include "emu/dreamcast.h" using namespace dreavm::core; using namespace dreavm::cpu; @@ -12,43 +8,12 @@ using namespace dreavm::holly; using namespace dreavm::renderer; using namespace dreavm::system; -Holly::Holly(Scheduler &scheduler, Memory &memory, SH4 &sh4) - : memory_(memory), - sh4_(sh4), - pvr_(scheduler, memory, *this), - gdrom_(memory, *this), - maple_(memory, sh4, *this) { - modem_mem_ = new uint8_t[MODEM_REG_SIZE]; - aica_mem_ = new uint8_t[AICA_REG_SIZE]; - audio_ram_ = new uint8_t[AUDIO_RAM_SIZE]; - expdev_mem_ = new uint8_t[EXPDEV_SIZE]; -} +Holly::Holly(Dreamcast *dc) : dc_(dc) {} -Holly::~Holly() { - delete[] modem_mem_; - delete[] aica_mem_; - delete[] audio_ram_; - delete[] expdev_mem_; -} - -bool Holly::Init(Backend *rb) { - InitMemory(); - - if (!pvr_.Init(rb)) { - return false; - } - - if (!gdrom_.Init()) { - return false; - } - - if (!maple_.Init()) { - return false; - } - - Reset(); - - return true; +void Holly::Init() { + cpu_ = dc_->cpu(); + holly_regs_ = dc_->holly_regs(); + audio_ram_ = dc_->audio_ram(); } void Holly::RequestInterrupt(HollyInterrupt intr) { @@ -57,20 +22,20 @@ void Holly::RequestInterrupt(HollyInterrupt intr) { uint32_t irq = static_cast(intr & ~HOLLY_INTC_MASK); if (intr == HOLLY_INTC_PCVOINT) { - maple_.VBlank(); + dc_->maple()->VBlank(); } switch (type) { case HOLLY_INTC_NRM: - SB_ISTNRM |= irq; + dc_->SB_ISTNRM |= irq; break; case HOLLY_INTC_EXT: - SB_ISTEXT |= irq; + dc_->SB_ISTEXT |= irq; break; case HOLLY_INTC_ERR: - SB_ISTERR |= irq; + dc_->SB_ISTERR |= irq; break; } @@ -84,54 +49,40 @@ void Holly::UnrequestInterrupt(HollyInterrupt intr) { switch (type) { case HOLLY_INTC_NRM: - SB_ISTNRM &= ~irq; + dc_->SB_ISTNRM &= ~irq; break; case HOLLY_INTC_EXT: - SB_ISTEXT &= ~irq; + dc_->SB_ISTEXT &= ~irq; break; case HOLLY_INTC_ERR: - SB_ISTERR &= ~irq; + dc_->SB_ISTERR &= ~irq; break; } ForwardRequestInterrupts(); } -namespace dreavm { -namespace holly { - -template -T Holly::ReadRegister(void *ctx, uint32_t addr) { - return static_cast(ReadRegister(ctx, addr)); -} - -template <> -uint32_t Holly::ReadRegister(void *ctx, uint32_t addr) { - Holly *holly = (Holly *)ctx; - Register ® = holly->regs_[addr >> 2]; +uint32_t Holly::ReadRegister32(uint32_t addr) { + uint32_t offset = addr >> 2; + Register ® = holly_regs_[offset]; if (!(reg.flags & R)) { - LOG_FATAL("Invalid read access at 0x%x", addr); + LOG_WARNING("Invalid read access at 0x%x", addr); + return 0; } - if (addr >= SB_MDSTAR_OFFSET && addr <= SB_MRXDBD_OFFSET) { - return holly->maple_.ReadRegister(reg, addr); - } else if (addr >= GD_ALTSTAT_DEVCTRL_OFFSET && addr <= SB_GDLEND_OFFSET) { - return holly->gdrom_.ReadRegister(reg, addr); - } - - switch (reg.offset) { + switch (offset) { case SB_ISTNRM_OFFSET: { // Note that the two highest bits indicate the OR'ed result of all of the // bits in SB_ISTEXT and SB_ISTERR, respectively, and writes to these two // bits are ignored. uint32_t v = reg.value & 0x3fffffff; - if (holly->SB_ISTEXT) { + if (dc_->SB_ISTEXT) { v |= 0x40000000; } - if (holly->SB_ISTERR) { + if (dc_->SB_ISTERR) { v |= 0x80000000; } return v; @@ -141,38 +92,25 @@ uint32_t Holly::ReadRegister(void *ctx, uint32_t addr) { return reg.value; } -template -void Holly::WriteRegister(void *ctx, uint32_t addr, T value) { - WriteRegister(ctx, addr, static_cast(value)); -} - -template <> -void Holly::WriteRegister(void *ctx, uint32_t addr, uint32_t value) { - Holly *holly = (Holly *)ctx; - Register ® = holly->regs_[addr >> 2]; +void Holly::WriteRegister32(uint32_t addr, uint32_t value) { + uint32_t offset = addr >> 2; + Register ® = holly_regs_[offset]; if (!(reg.flags & W)) { - LOG_FATAL("Invalid write access at 0x%x", addr); - } - - if (addr >= SB_MDSTAR_OFFSET && addr <= SB_MRXDBD_OFFSET) { - holly->maple_.WriteRegister(reg, addr, value); - return; - } else if (addr >= GD_ALTSTAT_DEVCTRL_OFFSET && addr <= SB_GDLEND_OFFSET) { - holly->gdrom_.WriteRegister(reg, addr, value); + LOG_WARNING("Invalid write access at 0x%x", addr); return; } uint32_t old = reg.value; reg.value = value; - switch (reg.offset) { + switch (offset) { case SB_ISTNRM_OFFSET: case SB_ISTEXT_OFFSET: case SB_ISTERR_OFFSET: { // writing a 1 clears the interrupt reg.value = old & ~value; - holly->ForwardRequestInterrupts(); + ForwardRequestInterrupts(); } break; case SB_IML2NRM_OFFSET: @@ -184,18 +122,18 @@ void Holly::WriteRegister(void *ctx, uint32_t addr, uint32_t value) { case SB_IML6NRM_OFFSET: case SB_IML6EXT_OFFSET: case SB_IML6ERR_OFFSET: - holly->ForwardRequestInterrupts(); + ForwardRequestInterrupts(); break; case SB_C2DST_OFFSET: if (value) { - holly->CH2DMATransfer(); + CH2DMATransfer(); } break; case SB_SDST_OFFSET: if (value) { - holly->SortDMATransfer(); + SortDMATransfer(); } break; @@ -219,76 +157,30 @@ void Holly::WriteRegister(void *ctx, uint32_t addr, uint32_t value) { } } -template -T Holly::ReadAudio(void *ctx, uint32_t addr) { - Holly *holly = (Holly *)ctx; - return *reinterpret_cast(holly->audio_ram_[addr]); -} - -template <> -uint32_t Holly::ReadAudio(void *ctx, uint32_t addr) { - Holly *holly = (Holly *)ctx; - +uint32_t Holly::ReadAudio32(uint32_t addr) { // FIXME temp hack for unsupported audio regs hanging in Crazy Taxi 2 if (addr == 0x5c) { return 0x54494e49; } - return *reinterpret_cast(&holly->audio_ram_[addr]); + return *reinterpret_cast(&audio_ram_[addr]); } -template -void Holly::WriteAudio(void *ctx, uint32_t addr, T value) { - Holly *holly = (Holly *)ctx; - *reinterpret_cast(&holly->audio_ram_[addr]) = value; -} -} -} - -void Holly::InitMemory() { - memory_.Handle(HOLLY_REG_START, HOLLY_REG_END, MIRROR_MASK, this, - &Holly::ReadRegister, &Holly::ReadRegister, - &Holly::ReadRegister, nullptr, - &Holly::WriteRegister, - &Holly::WriteRegister, - &Holly::WriteRegister, nullptr); - memory_.Mount(MODEM_REG_START, MODEM_REG_END, MIRROR_MASK, modem_mem_); - memory_.Mount(AICA_REG_START, AICA_REG_END, MIRROR_MASK, aica_mem_); - memory_.Handle(AUDIO_RAM_START, AUDIO_RAM_END, MIRROR_MASK, this, - &Holly::ReadAudio, &Holly::ReadAudio, - &Holly::ReadAudio, nullptr, - &Holly::WriteAudio, &Holly::WriteAudio, - &Holly::WriteAudio, nullptr); - memory_.Mount(EXPDEV_START, EXPDEV_END, MIRROR_MASK, expdev_mem_); -} - -void Holly::Reset() { - memset(modem_mem_, 0, MODEM_REG_SIZE); - memset(aica_mem_, 0, AICA_REG_SIZE); - memset(audio_ram_, 0, AUDIO_RAM_SIZE); - memset(expdev_mem_, 0, EXPDEV_SIZE); - - // FIXME temp hack for unsupported audio regs hanging in Crazy Taxi - *reinterpret_cast(&audio_ram_[0x5c]) = 0x54494e49; - -// initialize registers -#define HOLLY_REG(addr, name, flags, default, type) \ - regs_[name##_OFFSET >> 2] = {name##_OFFSET, flags, default}; -#include "holly/holly_regs.inc" -#undef HOLLY_REG +void Holly::WriteAudio32(uint32_t addr, uint32_t value) { + *reinterpret_cast(&audio_ram_[addr]) = value; } // FIXME what are SB_LMMODE0 / SB_LMMODE1 void Holly::CH2DMATransfer() { - sh4_.DDT(2, DDT_W, SB_C2DSTAT); + cpu_->DDT(2, DDT_W, dc_->SB_C2DSTAT); - SB_C2DLEN = 0; - SB_C2DST = 0; + dc_->SB_C2DLEN = 0; + dc_->SB_C2DST = 0; RequestInterrupt(HOLLY_INTC_DTDE2INT); } void Holly::SortDMATransfer() { - SB_SDST = 0; + dc_->SB_SDST = 0; RequestInterrupt(HOLLY_INTC_DTDESINT); } @@ -296,29 +188,32 @@ void Holly::ForwardRequestInterrupts() { // trigger the respective level-encoded interrupt on the sh4 interrupt // controller { - if ((SB_ISTNRM & SB_IML6NRM) || (SB_ISTERR & SB_IML6ERR) || - (SB_ISTEXT & SB_IML6EXT)) { - sh4_.RequestInterrupt(SH4_INTC_IRL_9); + if ((dc_->SB_ISTNRM & dc_->SB_IML6NRM) || + (dc_->SB_ISTERR & dc_->SB_IML6ERR) || + (dc_->SB_ISTEXT & dc_->SB_IML6EXT)) { + cpu_->RequestInterrupt(SH4_INTC_IRL_9); } else { - sh4_.UnrequestInterrupt(SH4_INTC_IRL_9); + cpu_->UnrequestInterrupt(SH4_INTC_IRL_9); } } { - if ((SB_ISTNRM & SB_IML4NRM) || (SB_ISTERR & SB_IML4ERR) || - (SB_ISTEXT & SB_IML4EXT)) { - sh4_.RequestInterrupt(SH4_INTC_IRL_11); + if ((dc_->SB_ISTNRM & dc_->SB_IML4NRM) || + (dc_->SB_ISTERR & dc_->SB_IML4ERR) || + (dc_->SB_ISTEXT & dc_->SB_IML4EXT)) { + cpu_->RequestInterrupt(SH4_INTC_IRL_11); } else { - sh4_.UnrequestInterrupt(SH4_INTC_IRL_11); + cpu_->UnrequestInterrupt(SH4_INTC_IRL_11); } } { - if ((SB_ISTNRM & SB_IML2NRM) || (SB_ISTERR & SB_IML2ERR) || - (SB_ISTEXT & SB_IML2EXT)) { - sh4_.RequestInterrupt(SH4_INTC_IRL_13); + if ((dc_->SB_ISTNRM & dc_->SB_IML2NRM) || + (dc_->SB_ISTERR & dc_->SB_IML2ERR) || + (dc_->SB_ISTEXT & dc_->SB_IML2EXT)) { + cpu_->RequestInterrupt(SH4_INTC_IRL_13); } else { - sh4_.UnrequestInterrupt(SH4_INTC_IRL_13); + cpu_->UnrequestInterrupt(SH4_INTC_IRL_13); } } } diff --git a/src/holly/holly.h b/src/holly/holly.h index e8222773..9fc6adad 100644 --- a/src/holly/holly.h +++ b/src/holly/holly.h @@ -1,31 +1,17 @@ #ifndef HOLLY_H #define HOLLY_H -#include "emu/memory.h" -#include "emu/scheduler.h" -#include "holly/gdrom.h" -#include "holly/maple.h" -#include "holly/pvr2.h" -#include "holly/register.h" -#include "renderer/backend.h" -#include "system/keys.h" - namespace dreavm { - namespace cpu { class SH4; } +namespace emu { +class Dreamcast; +struct Register; +} namespace holly { -// registers -enum { -#define HOLLY_REG(addr, name, flags, default, type) \ - name##_OFFSET = addr - emu::HOLLY_REG_START, -#include "holly/holly_regs.inc" -#undef HOLLY_REG -}; - // interrupts #define HOLLY_INTC_MASK 0xf00000000 @@ -162,53 +148,29 @@ enum HollyInterrupt : uint64_t { }; class Holly { - friend class GDROM; - friend class Maple; - public: - Holly(emu::Scheduler &scheduler, emu::Memory &memory, cpu::SH4 &sh4); - ~Holly(); + Holly(emu::Dreamcast *dc); - PVR2 &pvr() { return pvr_; } - GDROM &gdrom() { return gdrom_; } - Maple &maple() { return maple_; } + void Init(); - bool Init(renderer::Backend *rb); void RequestInterrupt(HollyInterrupt intr); void UnrequestInterrupt(HollyInterrupt intr); + uint32_t ReadRegister32(uint32_t addr); + void WriteRegister32(uint32_t addr, uint32_t value); + + uint32_t ReadAudio32(uint32_t addr); + void WriteAudio32(uint32_t addr, uint32_t value); + private: - template - static T ReadRegister(void *ctx, uint32_t addr); - template - static void WriteRegister(void *ctx, uint32_t addr, T value); - template - static T ReadAudio(void *ctx, uint32_t addr); - template - static void WriteAudio(void *ctx, uint32_t addr, T value); - - void InitMemory(); - - void Reset(); void CH2DMATransfer(); void SortDMATransfer(); void ForwardRequestInterrupts(); - emu::Memory &memory_; - cpu::SH4 &sh4_; - PVR2 pvr_; - GDROM gdrom_; - Maple maple_; - Register regs_[emu::HOLLY_REG_SIZE >> 2]; - uint8_t *modem_mem_; - uint8_t *aica_mem_; + emu::Dreamcast *dc_; + cpu::SH4 *cpu_; + emu::Register *holly_regs_; uint8_t *audio_ram_; - uint8_t *expdev_mem_; - -#define HOLLY_REG(offset, name, flags, default, type) \ - type &name{reinterpret_cast(regs_[name##_OFFSET >> 2].value)}; -#include "holly/holly_regs.inc" -#undef HOLLY_REG }; } } diff --git a/src/holly/maple.cc b/src/holly/maple.cc index 35508742..7609d599 100644 --- a/src/holly/maple.cc +++ b/src/holly/maple.cc @@ -1,6 +1,5 @@ #include "core/core.h" -#include "holly/holly.h" -#include "holly/maple.h" +#include "emu/dreamcast.h" #include "holly/maple_controller.h" using namespace dreavm::core; @@ -9,13 +8,16 @@ using namespace dreavm::emu; using namespace dreavm::holly; using namespace dreavm::system; -Maple::Maple(Memory &memory, SH4 &sh4, Holly &holly) - : memory_(memory), /*sh4_(sh4),*/ holly_(holly), devices_() { +Maple::Maple(Dreamcast *dc) : dc_(dc), devices_() { // default controller device devices_[0] = std::unique_ptr(new MapleController()); } -bool Maple::Init() { return true; } +void Maple::Init() { + memory_ = dc_->memory(); + holly_ = dc_->holly(); + holly_regs_ = dc_->holly_regs(); +} bool Maple::HandleInput(int port, Keycode key, int16_t value) { CHECK_LT(port, MAX_PORTS); @@ -30,8 +32,8 @@ bool Maple::HandleInput(int port, Keycode key, int16_t value) { // in synchronization with the V-BLANK signal. These methods are selected // through the trigger selection register (SB_MDTSEL). void Maple::VBlank() { - uint32_t enabled = holly_.SB_MDEN; - uint32_t vblank_initiate = holly_.SB_MDTSEL; + uint32_t enabled = dc_->SB_MDEN; + uint32_t vblank_initiate = dc_->SB_MDTSEL; if (enabled && vblank_initiate) { StartDMA(); @@ -40,39 +42,59 @@ void Maple::VBlank() { // TODO maple vblank interrupt? } -uint32_t Maple::ReadRegister(Register ®, uint32_t addr) { return reg.value; } +uint32_t Maple::ReadRegister32(uint32_t addr) { + uint32_t offset = (0xc00 + addr) >> 2; + Register ® = holly_regs_[offset]; -void Maple::WriteRegister(Register ®, uint32_t addr, uint32_t value) { - // T old = reg.value; + if (!(reg.flags & R)) { + LOG_WARNING("Invalid read access at 0x%x", addr); + return 0; + } + + return reg.value; +} + +void Maple::WriteRegister32(uint32_t addr, uint32_t value) { + uint32_t offset = (0xc00 + addr) >> 2; + Register ® = holly_regs_[offset]; + + if (!(reg.flags & W)) { + LOG_WARNING("Invalid write access at 0x%x", addr); + return; + } + + // uint32_t old = reg.value; reg.value = value; - if (addr == SB_MDST_OFFSET) { - uint32_t enabled = holly_.SB_MDEN; - if (enabled) { - if (value) { - StartDMA(); + switch (offset) { + case SB_MDST_OFFSET: { + uint32_t enabled = dc_->SB_MDEN; + if (enabled) { + if (value) { + StartDMA(); + } + } else { + reg.value = 0; } - } else { - reg.value = 0; - } + } break; } } void Maple::StartDMA() { - uint32_t start_addr = holly_.SB_MDSTAR; + uint32_t start_addr = dc_->SB_MDSTAR; MapleTransferDesc desc; MapleFrame frame, res; do { - desc.full = memory_.R64(start_addr); + desc.full = memory_->R64(start_addr); start_addr += 8; // read input - frame.header.full = memory_.R32(start_addr); + frame.header.full = memory_->R32(start_addr); start_addr += 4; for (uint32_t i = 0; i < frame.header.num_words; i++) { - frame.params[i] = memory_.R32(start_addr); + frame.params[i] = memory_->R32(start_addr); start_addr += 4; } @@ -80,18 +102,18 @@ void Maple::StartDMA() { std::unique_ptr &dev = devices_[desc.port]; if (dev && dev->HandleFrame(frame, res)) { - memory_.W32(desc.result_addr, res.header.full); + memory_->W32(desc.result_addr, res.header.full); desc.result_addr += 4; for (uint32_t i = 0; i < res.header.num_words; i++) { - memory_.W32(desc.result_addr, res.params[i]); + memory_->W32(desc.result_addr, res.params[i]); desc.result_addr += 4; } } else { - memory_.W32(desc.result_addr, 0xffffffff); + memory_->W32(desc.result_addr, 0xffffffff); } } while (!desc.last); - holly_.SB_MDST = 0; - holly_.RequestInterrupt(HOLLY_INTC_MDEINT); + dc_->SB_MDST = 0; + holly_->RequestInterrupt(HOLLY_INTC_MDEINT); } diff --git a/src/holly/maple.h b/src/holly/maple.h index 89d7862d..faf40fe5 100644 --- a/src/holly/maple.h +++ b/src/holly/maple.h @@ -1,20 +1,18 @@ #ifndef MAPLE_H #define MAPLE_H -#include "emu/memory.h" -#include "holly/register.h" #include "system/keys.h" namespace dreavm { - -namespace cpu { -class SH4; +namespace emu { +class Dreamcast; +class Memory; +struct Register; } namespace holly { class Holly; -struct Register; enum { MAX_PORTS = 4 }; @@ -101,22 +99,25 @@ class MapleDevice { class Maple { public: - Maple(emu::Memory &memory, cpu::SH4 &sh4, Holly &holly); + Maple(emu::Dreamcast *dc); + + void Init(); - bool Init(); bool HandleInput(int port, system::Keycode key, int16_t value); void VBlank(); - uint32_t ReadRegister(Register ®, uint32_t addr); - void WriteRegister(Register ®, uint32_t addr, uint32_t value); + uint32_t ReadRegister32(uint32_t addr); + void WriteRegister32(uint32_t addr, uint32_t value); private: bool HandleFrame(const MapleFrame &frame, MapleFrame &res); void StartDMA(); - emu::Memory &memory_; - // cpu::SH4 &sh4_; - Holly &holly_; + emu::Dreamcast *dc_; + emu::Memory *memory_; + holly::Holly *holly_; + emu::Register *holly_regs_; + std::unique_ptr devices_[MAX_PORTS]; }; } diff --git a/src/holly/maple_controller.cc b/src/holly/maple_controller.cc index add07ef6..ca3655ac 100644 --- a/src/holly/maple_controller.cc +++ b/src/holly/maple_controller.cc @@ -1,6 +1,7 @@ #include #include #include +#include "core/core.h" #include "holly/maple_controller.h" using namespace dreavm::holly; diff --git a/src/holly/pvr2.cc b/src/holly/pvr2.cc index 702eb440..04d0be12 100644 --- a/src/holly/pvr2.cc +++ b/src/holly/pvr2.cc @@ -1,48 +1,81 @@ -#include "holly/holly.h" -#include "holly/pvr2.h" +#include "core/core.h" +#include "emu/dreamcast.h" using namespace dreavm::cpu; using namespace dreavm::emu; using namespace dreavm::holly; using namespace dreavm::renderer; -PVR2::PVR2(Scheduler &scheduler, Memory &memory, Holly &holly) - : scheduler_(scheduler), - memory_(memory), - holly_(holly), - ta_(memory, holly, *this), +PVR2::PVR2(Dreamcast *dc) + : dc_(dc), line_timer_(INVALID_HANDLE), current_scanline_(0), fps_(0), - vbps_(0) { - vram_ = new uint8_t[PVR_VRAM32_SIZE]; - pram_ = new uint8_t[PVR_PALETTE_SIZE]; + vbps_(0) {} + +void PVR2::Init() { + scheduler_ = dc_->scheduler(); + holly_ = dc_->holly(); + ta_ = dc_->ta(); + pvr_regs_ = dc_->pvr_regs(); + video_ram_ = dc_->video_ram(); + + ReconfigureSPG(); } -PVR2::~PVR2() { - delete[] vram_; - delete[] pram_; -} +uint32_t PVR2::ReadRegister32(uint32_t addr) { + uint32_t offset = addr >> 2; + Register ® = pvr_regs_[offset]; -bool PVR2::Init(Backend *rb) { - InitMemory(); - - if (!ta_.Init(rb)) { - return false; + if (!(reg.flags & R)) { + LOG_WARNING("Invalid read access at 0x%x", addr); + return 0; } - Reset(); - ReconfigureSPG(); - - return true; + return reg.value; } -void PVR2::RenderLastFrame() { ta_.RenderLastContext(); } +void PVR2::WriteRegister32(uint32_t addr, uint32_t value) { + uint32_t offset = addr >> 2; + Register ® = pvr_regs_[offset]; -void PVR2::ToggleTracing() { ta_.ToggleTracing(); } + if (!(reg.flags & W)) { + LOG_WARNING("Invalid write access at 0x%x", addr); + return; + } -namespace dreavm { -namespace holly { + reg.value = value; + + switch (offset) { + case SOFTRESET_OFFSET: { + bool reset_ta = value & 0x1; + if (reset_ta) { + ta_->SoftReset(); + } + } break; + + case TA_LIST_INIT_OFFSET: { + ta_->InitContext(dc_->TA_ISP_BASE.base_address); + } break; + + case STARTRENDER_OFFSET: { + { + auto now = std::chrono::high_resolution_clock::now(); + auto delta = std::chrono::duration_cast( + now - last_frame_); + last_frame_ = now; + fps_ = 1000000000.0f / delta.count(); + } + + ta_->SaveLastContext(dc_->PARAM_BASE.base_address); + } break; + + case SPG_LOAD_OFFSET: + case FB_R_CTRL_OFFSET: { + ReconfigureSPG(); + } break; + } +} // the dreamcast has 8MB of vram, split into two 4MB banks, with two ways of // accessing it: @@ -65,104 +98,29 @@ static uint32_t MAP64(uint32_t addr) { (addr & 0x3)); } -template -T PVR2::ReadInterleaved(void *ctx, uint32_t addr) { - PVR2 *pvr = reinterpret_cast(ctx); +uint8_t PVR2::ReadInterleaved8(uint32_t addr) { addr = MAP64(addr); - return *reinterpret_cast(&pvr->vram_[addr]); + return *reinterpret_cast(&video_ram_[addr]); } -template -void PVR2::WriteInterleaved(void *ctx, uint32_t addr, T value) { - PVR2 *pvr = reinterpret_cast(ctx); +uint16_t PVR2::ReadInterleaved16(uint32_t addr) { addr = MAP64(addr); - *reinterpret_cast(&pvr->vram_[addr]) = value; + return *reinterpret_cast(&video_ram_[addr]); } -template -T PVR2::ReadRegister(void *ctx, uint32_t addr) { - return static_cast(ReadRegister(ctx, addr)); +uint32_t PVR2::ReadInterleaved32(uint32_t addr) { + addr = MAP64(addr); + return *reinterpret_cast(&video_ram_[addr]); } -template <> -uint32_t PVR2::ReadRegister(void *ctx, uint32_t addr) { - PVR2 *pvr = (PVR2 *)ctx; - Register ® = pvr->regs_[addr >> 2]; - - if (!(reg.flags & R)) { - LOG_WARNING("Invalid read access at 0x%x", addr); - return 0; - } - - return reg.value; +void PVR2::WriteInterleaved16(uint32_t addr, uint16_t value) { + addr = MAP64(addr); + *reinterpret_cast(&video_ram_[addr]) = value; } -template -void PVR2::WriteRegister(void *ctx, uint32_t addr, T value) { - WriteRegister(ctx, addr, static_cast(value)); -} - -template <> -void PVR2::WriteRegister(void *ctx, uint32_t addr, uint32_t value) { - PVR2 *pvr = (PVR2 *)ctx; - Register ® = pvr->regs_[addr >> 2]; - - if (!(reg.flags & W)) { - LOG_WARNING("Invalid write access at 0x%x", addr); - return; - } - - reg.value = value; - - if (reg.offset == SOFTRESET_OFFSET) { - bool reset_ta = value & 0x1; - if (reset_ta) { - pvr->ta_.SoftReset(); - } - } else if (reg.offset == TA_LIST_INIT_OFFSET) { - pvr->ta_.InitContext(pvr->TA_ISP_BASE.base_address); - } else if (reg.offset == STARTRENDER_OFFSET) { - { - auto now = std::chrono::high_resolution_clock::now(); - auto delta = std::chrono::duration_cast( - now - pvr->last_frame_); - pvr->last_frame_ = now; - pvr->fps_ = 1000000000.0f / delta.count(); - } - - pvr->ta_.SaveLastContext(pvr->PARAM_BASE.base_address); - } else if (reg.offset == SPG_LOAD_OFFSET || reg.offset == FB_R_CTRL_OFFSET) { - pvr->ReconfigureSPG(); - } -} -} -} - -void PVR2::InitMemory() { - memory_.Mount(PVR_VRAM32_START, PVR_VRAM32_END, MIRROR_MASK, vram_); - memory_.Handle(PVR_VRAM64_START, PVR_VRAM64_END, MIRROR_MASK, this, - &PVR2::ReadInterleaved, - &PVR2::ReadInterleaved, - &PVR2::ReadInterleaved, nullptr, nullptr, - &PVR2::WriteInterleaved, - &PVR2::WriteInterleaved, nullptr); - memory_.Handle(PVR_REG_START, PVR_REG_END, MIRROR_MASK, this, - &PVR2::ReadRegister, &PVR2::ReadRegister, - &PVR2::ReadRegister, nullptr, - &PVR2::WriteRegister, &PVR2::WriteRegister, - &PVR2::WriteRegister, nullptr); - memory_.Mount(PVR_PALETTE_START, PVR_PALETTE_END, MIRROR_MASK, pram_); -} - -void PVR2::Reset() { - memset(vram_, 0, PVR_VRAM32_SIZE); - memset(pram_, 0, PVR_PALETTE_SIZE); - -// initialize registers -#define PVR_REG(addr, name, flags, default, type) \ - regs_[name##_OFFSET >> 2] = {name##_OFFSET, flags, default}; -#include "holly/pvr2_regs.inc" -#undef PVR_REG +void PVR2::WriteInterleaved32(uint32_t addr, uint32_t value) { + addr = MAP64(addr); + *reinterpret_cast(&video_ram_[addr]) = value; } void PVR2::ReconfigureSPG() { @@ -174,57 +132,58 @@ void PVR2::ReconfigureSPG() { // specify "number of lines per field/2 - 1." (default = 0x106) // PAL interlaced = vcount 624, vbstart 620, vbend 44. why isn't vcount ~200? // VGA non-interlaced = vcount 524, vbstart 520, vbend 40 - int pixel_clock = FB_R_CTRL.vclk_div ? PIXEL_CLOCK : (PIXEL_CLOCK / 2); - int line_clock = pixel_clock / (SPG_LOAD.hcount + 1); + int pixel_clock = dc_->FB_R_CTRL.vclk_div ? PIXEL_CLOCK : (PIXEL_CLOCK / 2); + int line_clock = pixel_clock / (dc_->SPG_LOAD.hcount + 1); // HACK seems to get interlaced mode to vsync reasonably - if (SPG_CONTROL.interlace) { + if (dc_->SPG_CONTROL.interlace) { 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, SPG_LOAD.vcount, SPG_LOAD.hcount, - SPG_CONTROL.interlace, SPG_VBLANK.vbstart, SPG_VBLANK.vbend); + pixel_clock, line_clock, dc_->SPG_LOAD.vcount, dc_->SPG_LOAD.hcount, + dc_->SPG_CONTROL.interlace, dc_->SPG_VBLANK.vbstart, + dc_->SPG_VBLANK.vbend); if (line_timer_ != INVALID_HANDLE) { - scheduler_.RemoveTimer(line_timer_); + scheduler_->RemoveTimer(line_timer_); line_timer_ = INVALID_HANDLE; } - line_timer_ = scheduler_.AddTimer(HZ_TO_NANO(line_clock), - std::bind(&PVR2::LineClockUpdate, this)); + line_timer_ = scheduler_->AddTimer(HZ_TO_NANO(line_clock), + std::bind(&PVR2::LineClockUpdate, this)); } void PVR2::LineClockUpdate() { - uint32_t num_scanlines = SPG_LOAD.vcount + 1; + uint32_t num_scanlines = dc_->SPG_LOAD.vcount + 1; if (current_scanline_ > num_scanlines) { current_scanline_ = 0; } // vblank in - if (current_scanline_ == SPG_VBLANK_INT.vblank_in_line_number) { - holly_.RequestInterrupt(HOLLY_INTC_PCVIINT); + if (current_scanline_ == dc_->SPG_VBLANK_INT.vblank_in_line_number) { + holly_->RequestInterrupt(HOLLY_INTC_PCVIINT); } // vblank out - if (current_scanline_ == SPG_VBLANK_INT.vblank_out_line_number) { - holly_.RequestInterrupt(HOLLY_INTC_PCVOINT); + if (current_scanline_ == dc_->SPG_VBLANK_INT.vblank_out_line_number) { + holly_->RequestInterrupt(HOLLY_INTC_PCVOINT); } // hblank in - holly_.RequestInterrupt(HOLLY_INTC_PCHIINT); + holly_->RequestInterrupt(HOLLY_INTC_PCHIINT); - bool was_vsync = SPG_STATUS.vsync; - SPG_STATUS.vsync = SPG_VBLANK.vbstart < SPG_VBLANK.vbend - ? (current_scanline_ >= SPG_VBLANK.vbstart && - current_scanline_ < SPG_VBLANK.vbend) - : (current_scanline_ >= SPG_VBLANK.vbstart || - current_scanline_ < SPG_VBLANK.vbend); - SPG_STATUS.scanline = current_scanline_++; + bool was_vsync = dc_->SPG_STATUS.vsync; + dc_->SPG_STATUS.vsync = dc_->SPG_VBLANK.vbstart < dc_->SPG_VBLANK.vbend + ? (current_scanline_ >= dc_->SPG_VBLANK.vbstart && + current_scanline_ < dc_->SPG_VBLANK.vbend) + : (current_scanline_ >= dc_->SPG_VBLANK.vbstart || + current_scanline_ < dc_->SPG_VBLANK.vbend); + dc_->SPG_STATUS.scanline = current_scanline_++; - if (!was_vsync && SPG_STATUS.vsync) { + if (!was_vsync && dc_->SPG_STATUS.vsync) { // track vblank stats auto now = std::chrono::high_resolution_clock::now(); auto delta = std::chrono::duration_cast( diff --git a/src/holly/pvr2.h b/src/holly/pvr2.h index 43a9df70..bf9e23cf 100644 --- a/src/holly/pvr2.h +++ b/src/holly/pvr2.h @@ -1,34 +1,29 @@ #ifndef PVR_CLX2_H #define PVR_CLX2_H -#include -#include "emu/memory.h" #include "emu/scheduler.h" -#include "holly/register.h" -#include "holly/tile_accelerator.h" -#include "renderer/backend.h" namespace dreavm { +namespace emu { +class Dreamcast; +class Scheduler; +} + namespace holly { class Holly; - -enum { -#define PVR_REG(addr, name, flags, default_value, type) \ - name##_OFFSET = addr - emu::PVR_REG_START, -#include "holly/pvr2_regs.inc" -#undef PVR_REG -}; +class TileAccelerator; union PARAM_BASE_T { + uint32_t full; struct { uint32_t base_address : 24; uint32_t reserved : 8; }; - uint32_t full; }; union FB_R_CTRL_T { + uint32_t full; struct { uint32_t fb_enable : 1; uint32_t fb_line_double : 1; @@ -41,10 +36,10 @@ union FB_R_CTRL_T { uint32_t vclk_div : 1; uint32_t reserved1 : 8; }; - uint32_t full; }; union FB_W_CTRL_T { + uint32_t full; struct { uint32_t fb_packmode : 3; uint32_t fb_dither : 1; @@ -53,19 +48,19 @@ union FB_W_CTRL_T { uint32_t fb_alpha_threshhold : 8; uint32_t reserved1 : 8; }; - uint32_t full; }; union FPU_SHAD_SCALE_T { + uint32_t full; struct { uint32_t scale_factor : 8; uint32_t intensity_volume_mode : 1; uint32_t reserved : 23; }; - uint32_t full; }; union FPU_PARAM_CFG_T { + uint32_t full; struct { uint32_t first_ptr_burst_size : 4; uint32_t ptr_burst_size : 4; @@ -75,10 +70,10 @@ union FPU_PARAM_CFG_T { uint32_t region_header_type : 1; uint32_t reserved1 : 10; }; - uint32_t full; }; union ISP_BACKGND_T_T { + uint32_t full; struct { uint32_t tag_offset : 3; uint32_t tag_address : 21; @@ -86,10 +81,10 @@ union ISP_BACKGND_T_T { uint32_t shadow : 1; uint32_t cache_bypass : 1; }; - uint32_t full; }; union ISP_FEED_CFG_T { + uint32_t full; struct { uint32_t presort : 1; uint32_t reserved : 2; @@ -98,10 +93,10 @@ union ISP_FEED_CFG_T { uint32_t cache_size : 10; uint32_t reserved1 : 8; }; - uint32_t full; }; union SPG_HBLANK_INT_T { + uint32_t full; struct { uint32_t line_comp_val : 10; uint32_t reserved : 2; @@ -110,20 +105,20 @@ union SPG_HBLANK_INT_T { uint32_t hblank_in_interrupt : 10; uint32_t reserved3 : 6; }; - uint32_t full; }; union SPG_VBLANK_INT_T { + uint32_t full; struct { uint32_t vblank_in_line_number : 10; uint32_t reserved : 6; uint32_t vblank_out_line_number : 10; uint32_t reserved2 : 6; }; - uint32_t full; }; union SPG_CONTROL_T { + uint32_t full; struct { uint32_t mhsync_pol : 1; uint32_t mvsync_pol : 1; @@ -137,30 +132,30 @@ union SPG_CONTROL_T { uint32_t csync_on_h : 1; uint32_t reserved : 22; }; - uint32_t full; }; union SPG_LOAD_T { + uint32_t full; struct { uint32_t hcount : 10; uint32_t reserved : 6; uint32_t vcount : 10; uint32_t reserved2 : 6; }; - uint32_t full; }; union SPG_VBLANK_T { + uint32_t full; struct { uint32_t vbstart : 10; uint32_t reserved : 6; uint32_t vbend : 10; uint32_t reserved2 : 6; }; - uint32_t full; }; union TEXT_CONTROL_T { + uint32_t full; struct { uint32_t stride : 5; uint32_t reserved : 3; @@ -170,18 +165,18 @@ union TEXT_CONTROL_T { uint32_t codebook_endian : 1; uint32_t reserved3 : 14; }; - uint32_t full; }; union PAL_RAM_CTRL_T { + uint32_t full; struct { uint32_t pixel_format : 2; uint32_t reserved0 : 30; }; - uint32_t full; }; union SPG_STATUS_T { + uint32_t full; struct { uint32_t scanline : 10; uint32_t fieldnum : 1; @@ -190,67 +185,51 @@ union SPG_STATUS_T { uint32_t vsync : 1; uint32_t reserved : 18; }; - uint32_t full; }; union TA_ISP_BASE_T { + uint32_t full; struct { uint32_t base_address : 24; uint32_t reserved : 8; }; - uint32_t full; }; class PVR2 { - friend class TileAccelerator; - friend class TileTextureCache; - public: - PVR2(emu::Scheduler &scheduler, emu::Memory &memory, Holly &holly); - ~PVR2(); + PVR2(emu::Dreamcast *dc); float fps() { return fps_; } float vbps() { return vbps_; } - bool Init(renderer::Backend *rb); - void RenderLastFrame(); - void ToggleTracing(); + void Init(); + + uint32_t ReadRegister32(uint32_t addr); + void WriteRegister32(uint32_t addr, uint32_t value); + + uint8_t ReadInterleaved8(uint32_t addr); + uint16_t ReadInterleaved16(uint32_t addr); + uint32_t ReadInterleaved32(uint32_t addr); + void WriteInterleaved16(uint32_t addr, uint16_t value); + void WriteInterleaved32(uint32_t addr, uint32_t value); private: - template - static T ReadInterleaved(void *ctx, uint32_t addr); - template - static void WriteInterleaved(void *ctx, uint32_t addr, T value); - template - static T ReadRegister(void *ctx, uint32_t addr); - template - static void WriteRegister(void *ctx, uint32_t addr, T value); - - void InitMemory(); - void Reset(); void ReconfigureSPG(); void LineClockUpdate(); - emu::Scheduler &scheduler_; - emu::Memory &memory_; - Holly &holly_; - TileAccelerator ta_; + emu::Dreamcast *dc_; + emu::Scheduler *scheduler_; + holly::Holly *holly_; + holly::TileAccelerator *ta_; + emu::Register *pvr_regs_; + uint8_t *video_ram_; emu::TimerHandle line_timer_; uint32_t current_scanline_; - - std::chrono::high_resolution_clock::time_point last_frame_, last_vblank_; - float fps_, vbps_; - - uint8_t *vram_; - uint8_t *pram_; - - Register regs_[emu::PVR_REG_SIZE >> 2]; - -#define PVR_REG(offset, name, flags, default, type) \ - type &name{reinterpret_cast(regs_[name##_OFFSET >> 2].value)}; -#include "holly/pvr2_regs.inc" -#undef PVR_REG + std::chrono::high_resolution_clock::time_point last_frame_; + float fps_; + std::chrono::high_resolution_clock::time_point last_vblank_; + float vbps_; }; } } diff --git a/src/holly/pvr2_regs.inc b/src/holly/pvr2_regs.inc index 176fc832..e829373c 100644 --- a/src/holly/pvr2_regs.inc +++ b/src/holly/pvr2_regs.inc @@ -3,12 +3,12 @@ PVR_REG(0x005f8004, REVISION, R, 0x00000011, uint32_t) PVR_REG(0x005f8008, SOFTRESET, RW, 0x00000007, uint32_t) PVR_REG(0x005f8014, STARTRENDER, RW, 0x00000000, uint32_t) PVR_REG(0x005f8018, TEST_SELECT, RW, 0x00000000, uint32_t) -PVR_REG(0x005f8020, PARAM_BASE, RW, 0x00000000, PARAM_BASE_T) +PVR_REG(0x005f8020, PARAM_BASE, RW, 0x00000000, holly::PARAM_BASE_T) PVR_REG(0x005f802c, REGION_BASE, RW, 0x00000000, uint32_t) PVR_REG(0x005f8030, SPAN_SORT_CFG, RW, 0x00000000, uint32_t) PVR_REG(0x005f8040, VO_BORDER_COL, RW, 0x00000000, uint32_t) -PVR_REG(0x005f8044, FB_R_CTRL, RW, 0x00000000, FB_R_CTRL_T) -PVR_REG(0x005f8048, FB_W_CTRL, RW, 0x00000000, FB_W_CTRL_T) +PVR_REG(0x005f8044, FB_R_CTRL, RW, 0x00000000, holly::FB_R_CTRL_T) +PVR_REG(0x005f8048, FB_W_CTRL, RW, 0x00000000, holly::FB_W_CTRL_T) PVR_REG(0x005f804c, FB_W_LINESTRIDE, RW, 0x00000000, uint32_t) PVR_REG(0x005f8050, FB_R_SOF1, RW, 0x00000000, uint32_t) PVR_REG(0x005f8054, FB_R_SOF2, RW, 0x00000000, uint32_t) @@ -17,14 +17,14 @@ PVR_REG(0x005f8060, FB_W_SOF1, RW, 0x00000000, uint32_t) PVR_REG(0x005f8064, FB_W_SOF2, RW, 0x00000000, uint32_t) PVR_REG(0x005f8068, FB_X_CLIP, RW, 0x00000000, uint32_t) PVR_REG(0x005f806c, FB_Y_CLIP, RW, 0x00000000, uint32_t) -PVR_REG(0x005f8074, FPU_SHAD_SCALE, RW, 0x00000000, FPU_SHAD_SCALE_T) +PVR_REG(0x005f8074, FPU_SHAD_SCALE, RW, 0x00000000, holly::FPU_SHAD_SCALE_T) PVR_REG(0x005f8078, FPU_CULL_VAL, RW, 0x00000000, uint32_t) -PVR_REG(0x005f807c, FPU_PARAM_CFG, RW, 0x0007df77, FPU_PARAM_CFG_T) +PVR_REG(0x005f807c, FPU_PARAM_CFG, RW, 0x0007df77, holly::FPU_PARAM_CFG_T) PVR_REG(0x005f8080, HALF_OFFSET, RW, 0x00000007, uint32_t) PVR_REG(0x005f8084, FPU_PERP_VAL, RW, 0x00000000, uint32_t) PVR_REG(0x005f8088, ISP_BACKGND_D, RW, 0x00000000, uint32_t) -PVR_REG(0x005f808c, ISP_BACKGND_T, RW, 0x00000000, ISP_BACKGND_T_T) -PVR_REG(0x005f8098, ISP_FEED_CFG, RW, 0x00402000, ISP_FEED_CFG_T) +PVR_REG(0x005f808c, ISP_BACKGND_T, RW, 0x00000000, holly::ISP_BACKGND_T_T) +PVR_REG(0x005f8098, ISP_FEED_CFG, RW, 0x00402000, holly::ISP_FEED_CFG_T) PVR_REG(0x005f80a0, SDRAM_REFRESH, RW, 0x00000020, uint32_t) PVR_REG(0x005f80a4, SDRAM_ARB_CFG, RW, 0x0000001f, uint32_t) PVR_REG(0x005f80a8, SDRAM_CFG, RW, 0x15f28997, uint32_t) @@ -34,26 +34,26 @@ PVR_REG(0x005f80b8, FOG_DENSITY, RW, 0x00000000, uint32_t) PVR_REG(0x005f80bc, FOG_CLAMP_MAX, RW, 0x00000000, uint32_t) PVR_REG(0x005f80c0, FOG_CLAMP_MIN, RW, 0x00000000, uint32_t) PVR_REG(0x005f80c4, SPG_TRIGGER_POS, RW, 0x00000000, uint32_t) -PVR_REG(0x005f80c8, SPG_HBLANK_INT, RW, 0x031d0000, SPG_HBLANK_INT_T) -PVR_REG(0x005f80cc, SPG_VBLANK_INT, RW, 0x01500104, SPG_VBLANK_INT_T) -PVR_REG(0x005f80d0, SPG_CONTROL, RW, 0x00000000, SPG_CONTROL_T) +PVR_REG(0x005f80c8, SPG_HBLANK_INT, RW, 0x031d0000, holly::SPG_HBLANK_INT_T) +PVR_REG(0x005f80cc, SPG_VBLANK_INT, RW, 0x01500104, holly::SPG_VBLANK_INT_T) +PVR_REG(0x005f80d0, SPG_CONTROL, RW, 0x00000000, holly::SPG_CONTROL_T) PVR_REG(0x005f80d4, SPG_HBLANK, RW, 0x007e0345, uint32_t) -PVR_REG(0x005f80d8, SPG_LOAD, RW, 0x01060359, SPG_LOAD_T) -PVR_REG(0x005f80dc, SPG_VBLANK, RW, 0x01500104, SPG_VBLANK_T) +PVR_REG(0x005f80d8, SPG_LOAD, RW, 0x01060359, holly::SPG_LOAD_T) +PVR_REG(0x005f80dc, SPG_VBLANK, RW, 0x01500104, holly::SPG_VBLANK_T) PVR_REG(0x005f80e0, SPG_WIDTH, RW, 0x07f1933f, uint32_t) -PVR_REG(0x005f80e4, TEXT_CONTROL, RW, 0x00000000, TEXT_CONTROL_T) +PVR_REG(0x005f80e4, TEXT_CONTROL, RW, 0x00000000, holly::TEXT_CONTROL_T) PVR_REG(0x005f80e8, VO_CONTROL, RW, 0x00000108, uint32_t) PVR_REG(0x005f80ec, VO_STARTX, RW, 0x0000009d, uint32_t) PVR_REG(0x005f80f0, VO_STARTY, RW, 0x00000015, uint32_t) PVR_REG(0x005f80f4, SCALER_CTL, RW, 0x00000400, uint32_t) -PVR_REG(0x005f8108, PAL_RAM_CTRL, RW, 0x00000000, PAL_RAM_CTRL_T) -PVR_REG(0x005f810c, SPG_STATUS, R, 0x00000000, SPG_STATUS_T) +PVR_REG(0x005f8108, PAL_RAM_CTRL, RW, 0x00000000, holly::PAL_RAM_CTRL_T) +PVR_REG(0x005f810c, SPG_STATUS, R, 0x00000000, holly::SPG_STATUS_T) PVR_REG(0x005f8110, FB_BURSTCTRL, RW, 0x00090639, uint32_t) PVR_REG(0x005f8114, FB_C_SOF, R, 0x00000000, uint32_t) PVR_REG(0x005f8118, Y_COEFF, RW, 0x00000000, uint32_t) PVR_REG(0x005f811c, PT_ALPHA_REF, RW, 0x000000ff, uint32_t) PVR_REG(0x005f8124, TA_OL_BASE, RW, 0x00000000, uint32_t) -PVR_REG(0x005f8128, TA_ISP_BASE, RW, 0x00000000, TA_ISP_BASE_T) +PVR_REG(0x005f8128, TA_ISP_BASE, RW, 0x00000000, holly::TA_ISP_BASE_T) PVR_REG(0x005f812c, TA_OL_LIMIT, RW, 0x00000000, uint32_t) PVR_REG(0x005f8130, TA_ISP_LIMIT, RW, 0x00000000, uint32_t) PVR_REG(0x005f8134, TA_NEXT_OPB, R, 0x00000000, uint32_t) diff --git a/src/holly/register.h b/src/holly/register.h deleted file mode 100644 index e2e1d396..00000000 --- a/src/holly/register.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef REGISTER_H -#define REGISTER_H - -namespace dreavm { -namespace holly { - -enum { R = 0x1, W = 0x2, RW = 0x3, UNDEFINED = 0x0 }; - -struct Register { - Register() : offset(-1), flags(RW), value(0) {} - Register(uint32_t offset, uint8_t flags, uint32_t value) - : offset(offset), flags(flags), value(value) {} - - uint32_t offset; - uint8_t flags; - uint32_t value; -}; -} -} - -#endif diff --git a/src/holly/tile_accelerator.cc b/src/holly/tile_accelerator.cc index dcabd5dd..c1f00752 100644 --- a/src/holly/tile_accelerator.cc +++ b/src/holly/tile_accelerator.cc @@ -1,9 +1,6 @@ #include "core/core.h" -#include "holly/holly.h" +#include "emu/dreamcast.h" #include "holly/pixel_convert.h" -#include "holly/pvr2.h" -#include "holly/tile_accelerator.h" -#include "trace/trace.h" using namespace dreavm::core; using namespace dreavm::emu; @@ -167,7 +164,8 @@ static int GetVertexType_raw(const PCW &pcw) { return 0; } -TileTextureCache::TileTextureCache(TileAccelerator &ta) : ta_(ta) {} +TileTextureCache::TileTextureCache(Dreamcast *dc) + : dc_(dc), trace_writer_(nullptr) {} void TileTextureCache::Clear() { for (auto it : textures_) { @@ -175,7 +173,7 @@ void TileTextureCache::Clear() { continue; } - ta_.rb_->FreeTexture(it.second); + dc_->rb()->FreeTexture(it.second); } textures_.clear(); @@ -188,7 +186,7 @@ void TileTextureCache::RemoveTexture(uint32_t addr) { } TextureHandle handle = it->second; - ta_.rb_->FreeTexture(handle); + dc_->rb()->FreeTexture(handle); textures_.erase(it); } @@ -196,6 +194,12 @@ TextureHandle TileTextureCache::GetTexture( const TSP &tsp, const TCW &tcw, RegisterTextureCallback register_cb) { uint32_t texture_key = TextureCache::GetTextureKey(tsp, tcw); + // if the trace writer has changed, clear the cache to force insert events + if (dc_->trace_writer() != trace_writer_) { + Clear(); + trace_writer_ = dc_->trace_writer(); + } + // see if we already have an entry auto it = textures_.find(texture_key); if (it != textures_.end()) { @@ -212,7 +216,7 @@ TextureHandle TileTextureCache::GetTexture( ? 8 : tcw.pixel_format == TA_PIXEL_4BPP ? 4 : 16; int texture_size = (width * height * element_size_bits) >> 3; - const uint8_t *texture = &ta_.pvr_.vram_[texture_addr]; + const uint8_t *texture = &dc_->video_ram()[texture_addr]; // get the palette data int palette_size = 0; @@ -229,7 +233,7 @@ TextureHandle TileTextureCache::GetTexture( } palette_size = 0x1000; - palette = &ta_.pvr_.pram_[palette_addr]; + palette = &dc_->palette_ram()[palette_addr]; } // register and insert into the cache @@ -238,9 +242,9 @@ TextureHandle TileTextureCache::GetTexture( CHECK(result.second, "Texture already in the map?"); // add insert to trace - if (ta_.trace_writer_) { - ta_.trace_writer_->WriteInsertTexture(tsp, tcw, texture, texture_size, - palette, palette_size); + if (trace_writer_) { + trace_writer_->WriteInsertTexture(tsp, tcw, texture, texture_size, palette, + palette_size); } return result.first->second; @@ -264,11 +268,9 @@ int TileAccelerator::GetVertexType(const PCW &pcw) { pcw.para_type * TA_NUM_LISTS + pcw.list_type]; } -TileAccelerator::TileAccelerator(Memory &memory, Holly &holly, PVR2 &pvr) - : memory_(memory), - holly_(holly), - pvr_(pvr), - texcache_(*this), +TileAccelerator::TileAccelerator(Dreamcast *dc) + : dc_(dc), + texcache_(dc_), tile_renderer_(texcache_), last_context_(&scratch_context_) {} @@ -283,12 +285,10 @@ TileAccelerator::~TileAccelerator() { } } -bool TileAccelerator::Init(Backend *rb) { - rb_ = rb; - - InitMemory(); - - return true; +void TileAccelerator::Init() { + memory_ = dc_->memory(); + holly_ = dc_->holly(); + video_ram_ = dc_->video_ram(); } void TileAccelerator::SoftReset() { @@ -329,7 +329,7 @@ void TileAccelerator::WriteContext(uint32_t addr, uint32_t value) { } if (pcw.para_type == TA_PARAM_END_OF_LIST) { - holly_.RequestInterrupt(list_interrupts[tactx->list_type]); + holly_->RequestInterrupt(list_interrupts[tactx->list_type]); tactx->last_poly = nullptr; tactx->last_vertex = nullptr; @@ -365,88 +365,31 @@ void TileAccelerator::SaveLastContext(uint32_t addr) { WriteBackgroundState(last_context_); // tell holly that rendering is complete - holly_.RequestInterrupt(HOLLY_INTC_PCEOVINT); - holly_.RequestInterrupt(HOLLY_INTC_PCEOIINT); - holly_.RequestInterrupt(HOLLY_INTC_PCEOTINT); + holly_->RequestInterrupt(HOLLY_INTC_PCEOVINT); + holly_->RequestInterrupt(HOLLY_INTC_PCEOIINT); + holly_->RequestInterrupt(HOLLY_INTC_PCEOTINT); } void TileAccelerator::RenderLastContext() { - tile_renderer_.RenderContext(last_context_, rb_); + tile_renderer_.RenderContext(last_context_, dc_->rb()); // add render to trace - if (trace_writer_) { - trace_writer_->WriteRenderContext(last_context_); + if (dc_->trace_writer()) { + dc_->trace_writer()->WriteRenderContext(last_context_); } } -void TileAccelerator::ToggleTracing() { - if (!trace_writer_) { - char filename[PATH_MAX]; - GetNextTraceFilename(filename, sizeof(filename)); - - trace_writer_ = std::unique_ptr(new TraceWriter()); - if (!trace_writer_->Open(filename)) { - LOG_INFO("Failed to start tracing"); - trace_writer_ = nullptr; - return; - } - - LOG_INFO("Begin tracing to %s", filename); - - // clear the texture cache, so the next render will write out insert - // texture commands for any textures in use - texcache_.Clear(); - } else { - trace_writer_ = nullptr; - - LOG_INFO("End tracing"); - } +void TileAccelerator::WriteCommand32(uint32_t addr, uint32_t value) { + WriteContext(dc_->TA_ISP_BASE.base_address, value); } -namespace dreavm { -namespace holly { - -template -void TileAccelerator::WriteCommand(void *ctx, uint32_t addr, T value) { - WriteCommand(ctx, addr, static_cast(value)); -} - -template <> -void TileAccelerator::WriteCommand(void *ctx, uint32_t addr, uint32_t value) { - TileAccelerator *ta = (TileAccelerator *)ctx; - - ta->WriteContext(ta->pvr_.TA_ISP_BASE.base_address, value); -} - -template -void TileAccelerator::WriteTexture(void *ctx, uint32_t addr, T value) { - WriteTexture(ctx, addr, static_cast(value)); -} - -template <> -void TileAccelerator::WriteTexture(void *ctx, uint32_t addr, uint32_t value) { - TileAccelerator *ta = (TileAccelerator *)ctx; - +void TileAccelerator::WriteTexture32(uint32_t addr, uint32_t value) { addr &= 0xeeffffff; // FIXME this is terrible - ta->texcache_.RemoveTexture(addr); + texcache_.RemoveTexture(addr); - *reinterpret_cast(&ta->pvr_.vram_[addr]) = value; -} -} -} - -void TileAccelerator::InitMemory() { - // TODO handle YUV transfers from 0x10800000 - 0x10ffffe0 - memory_.Handle(TA_CMD_START, TA_CMD_END, 0x0, this, nullptr, nullptr, nullptr, - nullptr, &TileAccelerator::WriteCommand, - &TileAccelerator::WriteCommand, - &TileAccelerator::WriteCommand, nullptr); - memory_.Handle(TA_TEXTURE_START, TA_TEXTURE_END, 0x0, this, nullptr, nullptr, - nullptr, nullptr, &TileAccelerator::WriteTexture, - &TileAccelerator::WriteTexture, - &TileAccelerator::WriteTexture, nullptr); + *reinterpret_cast(&video_ram_[addr]) = value; } TileContextIterator TileAccelerator::FindContext(uint32_t addr) { @@ -472,23 +415,23 @@ TileContext *TileAccelerator::GetContext(uint32_t addr) { void TileAccelerator::WritePVRState(TileContext *tactx) { // autosort - if (!pvr_.FPU_PARAM_CFG.region_header_type) { - tactx->autosort = !pvr_.ISP_FEED_CFG.presort; + if (!dc_->FPU_PARAM_CFG.region_header_type) { + tactx->autosort = !dc_->ISP_FEED_CFG.presort; } else { - uint32_t region_data = memory_.R32(PVR_VRAM64_START + pvr_.REGION_BASE); + uint32_t region_data = memory_->R32(PVR_VRAM64_START + dc_->REGION_BASE); tactx->autosort = !(region_data & 0x20000000); } // texture stride - tactx->stride = pvr_.TEXT_CONTROL.stride * 32; + tactx->stride = dc_->TEXT_CONTROL.stride * 32; // texture palette pixel format - tactx->pal_pxl_format = pvr_.PAL_RAM_CTRL.pixel_format; + tactx->pal_pxl_format = dc_->PAL_RAM_CTRL.pixel_format; // write out video width to help with unprojecting the screen space // coordinates - if (pvr_.SPG_CONTROL.interlace || - (!pvr_.SPG_CONTROL.NTSC && !pvr_.SPG_CONTROL.PAL)) { + if (dc_->SPG_CONTROL.interlace || + (!dc_->SPG_CONTROL.NTSC && !dc_->SPG_CONTROL.PAL)) { // interlaced and VGA mode both render at full resolution tactx->video_width = 640; tactx->video_height = 480; @@ -507,35 +450,35 @@ void TileAccelerator::WriteBackgroundState(TileContext *tactx) { // be the correct solution uint32_t vram_offset = PVR_VRAM64_START + - ((tactx->addr + pvr_.ISP_BACKGND_T.tag_address * 4) & 0x7fffff); + ((tactx->addr + dc_->ISP_BACKGND_T.tag_address * 4) & 0x7fffff); // get surface parameters - tactx->bg_isp.full = memory_.R32(vram_offset); - tactx->bg_tsp.full = memory_.R32(vram_offset + 4); - tactx->bg_tcw.full = memory_.R32(vram_offset + 8); + tactx->bg_isp.full = memory_->R32(vram_offset); + tactx->bg_tsp.full = memory_->R32(vram_offset + 4); + tactx->bg_tcw.full = memory_->R32(vram_offset + 8); vram_offset += 12; // get the background depth - tactx->bg_depth = *reinterpret_cast(&pvr_.ISP_BACKGND_D); + tactx->bg_depth = *reinterpret_cast(&dc_->ISP_BACKGND_D); // get the byte size for each vertex. normally, the byte size is // ISP_BACKGND_T.skip + 3, but if parameter selection volume mode is in // effect and the shadow bit is 1, then the byte size is // ISP_BACKGND_T.skip * 2 + 3 - int vertex_size = pvr_.ISP_BACKGND_T.skip; - if (!pvr_.FPU_SHAD_SCALE.intensity_volume_mode && pvr_.ISP_BACKGND_T.shadow) { + int vertex_size = dc_->ISP_BACKGND_T.skip; + if (!dc_->FPU_SHAD_SCALE.intensity_volume_mode && dc_->ISP_BACKGND_T.shadow) { vertex_size *= 2; } vertex_size = (vertex_size + 3) * 4; // skip to the first vertex - vram_offset += pvr_.ISP_BACKGND_T.tag_offset * vertex_size; + vram_offset += dc_->ISP_BACKGND_T.tag_offset * vertex_size; // copy vertex data to context for (int i = 0, bg_offset = 0; i < 3; i++) { CHECK_LE(bg_offset + vertex_size, (int)sizeof(tactx->bg_vertices)); - memory_.Memcpy(&tactx->bg_vertices[bg_offset], vram_offset, vertex_size); + memory_->Memcpy(&tactx->bg_vertices[bg_offset], vram_offset, vertex_size); bg_offset += vertex_size; vram_offset += vertex_size; diff --git a/src/holly/tile_accelerator.h b/src/holly/tile_accelerator.h index 058cef24..bdcc86aa 100644 --- a/src/holly/tile_accelerator.h +++ b/src/holly/tile_accelerator.h @@ -3,11 +3,15 @@ #include #include -#include "emu/memory.h" #include "holly/tile_renderer.h" #include "renderer/backend.h" namespace dreavm { +namespace emu { +class Dreamcast; +class Memory; +} + namespace trace { class TraceWriter; } @@ -15,7 +19,6 @@ class TraceWriter; namespace holly { class Holly; -class PVR2; enum { // control params @@ -474,11 +477,9 @@ struct TileContext { int vertex_type; }; -class TileAccelerator; - class TileTextureCache : public TextureCache { public: - TileTextureCache(TileAccelerator &ta); + TileTextureCache(emu::Dreamcast *dc); void Clear(); void RemoveTexture(uint32_t addr); @@ -486,7 +487,9 @@ class TileTextureCache : public TextureCache { RegisterTextureCallback register_cb); private: - TileAccelerator &ta_; + emu::Dreamcast *dc_; + + trace::TraceWriter *trace_writer_; std::unordered_map textures_; }; @@ -494,17 +497,15 @@ typedef std::unordered_map TileContextMap; typedef TileContextMap::iterator TileContextIterator; class TileAccelerator { - friend class TileTextureCache; - public: static int GetParamSize(const PCW &pcw, int vertex_type); static int GetPolyType(const PCW &pcw); static int GetVertexType(const PCW &pcw); - TileAccelerator(emu::Memory &memory, Holly &holly, PVR2 &pvr); + TileAccelerator(emu::Dreamcast *dc); ~TileAccelerator(); - bool Init(renderer::Backend *rb); + void Init(); void SoftReset(); void InitContext(uint32_t addr); @@ -512,32 +513,25 @@ class TileAccelerator { void SaveLastContext(uint32_t addr); void RenderLastContext(); - void ToggleTracing(); + void WriteCommand32(uint32_t addr, uint32_t value); + void WriteTexture32(uint32_t addr, uint32_t value); private: - template - static void WriteCommand(void *ctx, uint32_t addr, T value); - template - static void WriteTexture(void *ctx, uint32_t addr, T value); - - void InitMemory(); TileContextIterator FindContext(uint32_t addr); TileContext *GetContext(uint32_t addr); void WritePVRState(TileContext *tactx); void WriteBackgroundState(TileContext *tactx); - emu::Memory &memory_; - Holly &holly_; - PVR2 &pvr_; - renderer::Backend *rb_; + emu::Dreamcast *dc_; + emu::Memory *memory_; + holly::Holly *holly_; + uint8_t *video_ram_; TileTextureCache texcache_; TileRenderer tile_renderer_; TileContextMap contexts_; TileContext scratch_context_; TileContext *last_context_; - - std::unique_ptr trace_writer_; }; } } diff --git a/src/holly/tile_renderer.cc b/src/holly/tile_renderer.cc index 6b88cc76..136c9b4e 100644 --- a/src/holly/tile_renderer.cc +++ b/src/holly/tile_renderer.cc @@ -1,3 +1,4 @@ +#include "core/core.h" #include "emu/profiler.h" #include "holly/pixel_convert.h" #include "holly/tile_accelerator.h" diff --git a/src/main.cc b/src/main.cc index 7750c98f..932ca1c1 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,5 +1,7 @@ +#include #include -#include "emu/emulator.h" +#include "core/core.h" +#include "emu/dreamcast.h" #include "trace/trace_viewer.h" using namespace dreavm::core; @@ -34,13 +36,12 @@ int main(int argc, char **argv) { InitFlags(&argc, &argv); const char *load = argc > 1 ? argv[1] : nullptr; - if (load && strstr(load, ".trace")) { - TraceViewer tracer; - tracer.Run(load); + std::unique_ptr tracer(new TraceViewer()); + tracer->Run(load); } else { - Emulator emu; - emu.Run(load); + std::unique_ptr emu(new Dreamcast()); + emu->Run(load); } ShutdownFlags(); diff --git a/src/renderer/gl_backend.cc b/src/renderer/gl_backend.cc index 24b8653a..32e64c26 100644 --- a/src/renderer/gl_backend.cc +++ b/src/renderer/gl_backend.cc @@ -76,7 +76,6 @@ bool GLBackend::Init() { InitTextures(); InitShaders(); InitVertexBuffers(); - SetupDefaultState(); return true; diff --git a/src/renderer/gl_backend.h b/src/renderer/gl_backend.h index 92c3edca..441428ca 100644 --- a/src/renderer/gl_backend.h +++ b/src/renderer/gl_backend.h @@ -60,8 +60,6 @@ struct BackendState { }; class GLBackend : public Backend { - friend class GLProfilerBackend; - public: GLBackend(GLContext &ctx); ~GLBackend(); diff --git a/test/asm/movb.s b/test/asm/movb.s index 46474220..187e900b 100644 --- a/test/asm/movb.s +++ b/test/asm/movb.s @@ -7,8 +7,8 @@ start: mov.l .L2, r0 mov.b @r0, r2 add r2, r2 - mov.b r2, @r15 - mov.b @r15, r2 + mov.b r2, @r0 + mov.b @r0, r2 # MOV.B Rm,@-Rn # MOV.B @Rm+,Rn mov.l .L2, r0 diff --git a/test/asm/movl.s b/test/asm/movl.s index d93399e4..c5c73e52 100644 --- a/test/asm/movl.s +++ b/test/asm/movl.s @@ -10,8 +10,8 @@ start: add #4, r0 mov.l @r0, r3 add r3, r3 - mov.l r3, @r15 - mov.l @r15, r3 + mov.l r3, @r0 + mov.l @r0, r3 # MOV.L Rm,@-Rn # MOV.L @Rm+,Rn mov.l .L2, r0 diff --git a/test/asm/movw.s b/test/asm/movw.s index f27c42b5..fe29043d 100644 --- a/test/asm/movw.s +++ b/test/asm/movw.s @@ -10,8 +10,8 @@ start: add #2, r0 mov.w @r0, r3 add r3, r3 - mov.w r3, @r15 - mov.w @r15, r3 + mov.w r3, @r0 + mov.w @r0, r3 # MOV.W Rm,@-Rn # MOV.W @Rm+,Rn mov.l .L2, r0 diff --git a/test/asm/sh4_test.inc b/test/asm/sh4_test.inc index 0f39d485..704688bd 100644 --- a/test/asm/sh4_test.inc +++ b/test/asm/sh4_test.inc @@ -56,10 +56,10 @@ SH4_TEST(ldcl, SH4Test {(uint8_t *)"\x01\xe2\x3b\xd0\x07\x40\x63\xe2\x31\xd1\x03 SH4_TEST(lds, SH4Test {(uint8_t *)"\x0a\x40\x0a\x02\x1a\x40\x1a\x03\x2a\x05\x2a\x40\x2a\x04\x2a\x45\x6a\x41\x6a\x05\x5a\x40\x5a\x06\x0b\x00\x09\x00", 28, {{ SH4CTX_R0, 13 }, { SH4CTX_R1, 0xffd40001 }}, {{ SH4CTX_R2, 13 }, { SH4CTX_R3, 13 }, { SH4CTX_R4, 13 }, { SH4CTX_R5, 0x00140001 }, { SH4CTX_R6, 13 }}}) SH4_TEST(ldsl, SH4Test {(uint8_t *)"\x1f\xd0\x23\xd1\x0a\xe2\x22\x20\x06\x40\x02\x41\x12\x63\x00\x31\x29\x00\x0c\x33\x1a\xd0\x1e\xd1\x0b\xe2\x22\x20\x16\x40\x12\x41\x12\x64\x00\x31\x29\x00\x0c\x34\x2a\x06\x15\xd0\x18\xd1\x0c\xe2\x22\x20\x26\x40\x22\x41\x12\x65\x00\x31\x29\x00\x0c\x35\x2a\x46\x0f\xd0\x13\xd1\x16\xd2\x22\x20\x66\x40\x62\x41\x12\x66\x00\x31\x29\x00\x0c\x36\x0a\xd0\x0e\xd1\x0d\xe2\x22\x20\x56\x40\x52\x41\x12\x67\x00\x31\x29\x00\x0c\x37\x0b\x00\x09\x00\x09\x00\x09\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x09\x00\x09\x00\x09\x00\x70\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x78\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x01\x00\xd4\xff\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00", 176, {}, {{ SH4CTX_R3, 0xb }, { SH4CTX_R4, 0xc }, { SH4CTX_R5, 0xd }, { SH4CTX_R6, 0x00140002 }, { SH4CTX_R7, 0xe }}}) SH4_TEST(mova, SH4Test {(uint8_t *)"\x03\xc7\x02\x62\x0b\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xe8\xff\xff\xff\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00", 32, {}, {{ SH4CTX_R2, -24 }}}) -SH4_TEST(movb, SH4Test {(uint8_t *)"\x13\xd0\x00\x62\x2c\x32\x20\x2f\xf0\x62\x11\xd0\x01\x70\x04\x63\x3c\x33\x34\x20\x00\x63\x0e\xd4\x42\x84\x0c\x30\x42\x80\x42\x84\x03\x64\x0b\xd0\x03\xe1\x1c\x05\x5c\x35\x54\x01\x1c\x05\x08\xd0\x1e\x40\x04\xc4\x0c\x30\x04\xc0\x04\xc4\x03\x66\x0b\x00\x09\x00\xf4\xf3\xf2\xf1\xf0\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x40\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00", 96, {}, {{ SH4CTX_R2, -24 }, { SH4CTX_R3, -26 }, { SH4CTX_R4, -28 }, { SH4CTX_R5, -30 }, { SH4CTX_R6, -32 }}}) -SH4_TEST(movl, SH4Test {(uint8_t *)"\x13\xd2\x1b\xd0\x04\x70\x02\x63\x3c\x33\x32\x2f\xf2\x63\x18\xd0\x08\x70\x06\x64\x4c\x34\x46\x20\x02\x64\x15\xd5\x53\x50\x0c\x30\x03\x15\x53\x50\x03\x65\x12\xd0\x10\xe1\x1e\x06\x6c\x36\x66\x01\x1e\x06\x0f\xd0\x1e\x40\x05\xc6\x0c\x30\x05\xc2\x05\xc6\x03\x67\x0b\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xe8\xff\xff\xff\xf3\xff\xff\xff\xf2\xff\xff\xff\xf1\xff\xff\xff\xf0\xff\xff\xff\xef\xff\xff\xff\x09\x00\x09\x00\x09\x00\x09\x00\x50\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00", 128, {}, {{ SH4CTX_R2, -24 }, { SH4CTX_R3, -26 }, { SH4CTX_R4, -28 }, { SH4CTX_R5, -30 }, { SH4CTX_R6, -32 }, { SH4CTX_R7, -34 }}}) +SH4_TEST(movb, SH4Test {(uint8_t *)"\x13\xd0\x00\x62\x2c\x32\x20\x20\x00\x62\x11\xd0\x01\x70\x04\x63\x3c\x33\x34\x20\x00\x63\x0e\xd4\x42\x84\x0c\x30\x42\x80\x42\x84\x03\x64\x0b\xd0\x03\xe1\x1c\x05\x5c\x35\x54\x01\x1c\x05\x08\xd0\x1e\x40\x04\xc4\x0c\x30\x04\xc0\x04\xc4\x03\x66\x0b\x00\x09\x00\xf4\xf3\xf2\xf1\xf0\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x40\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00", 96, {}, {{ SH4CTX_R2, -24 }, { SH4CTX_R3, -26 }, { SH4CTX_R4, -28 }, { SH4CTX_R5, -30 }, { SH4CTX_R6, -32 }}}) +SH4_TEST(movl, SH4Test {(uint8_t *)"\x13\xd2\x1b\xd0\x04\x70\x02\x63\x3c\x33\x32\x20\x02\x63\x18\xd0\x08\x70\x06\x64\x4c\x34\x46\x20\x02\x64\x15\xd5\x53\x50\x0c\x30\x03\x15\x53\x50\x03\x65\x12\xd0\x10\xe1\x1e\x06\x6c\x36\x66\x01\x1e\x06\x0f\xd0\x1e\x40\x05\xc6\x0c\x30\x05\xc2\x05\xc6\x03\x67\x0b\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xe8\xff\xff\xff\xf3\xff\xff\xff\xf2\xff\xff\xff\xf1\xff\xff\xff\xf0\xff\xff\xff\xef\xff\xff\xff\x09\x00\x09\x00\x09\x00\x09\x00\x50\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00", 128, {}, {{ SH4CTX_R2, -24 }, { SH4CTX_R3, -26 }, { SH4CTX_R4, -28 }, { SH4CTX_R5, -30 }, { SH4CTX_R6, -32 }, { SH4CTX_R7, -34 }}}) SH4_TEST(movt, SH4Test {(uint8_t *)"\x03\x88\x29\x01\x05\x88\x29\x02\x0b\x00\x09\x00", 12, {{ SH4CTX_R0, 3 }}, {{ SH4CTX_R1, 1 }, { SH4CTX_R2, 0 }}}) -SH4_TEST(movw, SH4Test {(uint8_t *)"\x26\x92\x17\xd0\x02\x70\x01\x63\x3c\x33\x31\x2f\xf1\x63\x14\xd0\x04\x70\x05\x64\x4c\x34\x45\x20\x01\x64\x11\xd5\x53\x85\x0c\x30\x53\x81\x53\x85\x03\x65\x0e\xd0\x08\xe1\x1d\x06\x6c\x36\x65\x01\x1d\x06\x0b\xd0\x1e\x40\x05\xc5\x0c\x30\x05\xc1\x05\xc5\x03\x67\x0b\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xe8\xff\xf3\xff\xf2\xff\xf1\xff\xf0\xff\xef\xff\x09\x00\x09\x00\x50\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00", 112, {}, {{ SH4CTX_R2, -24 }, { SH4CTX_R3, -26 }, { SH4CTX_R4, -28 }, { SH4CTX_R5, -30 }, { SH4CTX_R6, -32 }, { SH4CTX_R7, -34 }}}) +SH4_TEST(movw, SH4Test {(uint8_t *)"\x26\x92\x17\xd0\x02\x70\x01\x63\x3c\x33\x31\x20\x01\x63\x14\xd0\x04\x70\x05\x64\x4c\x34\x45\x20\x01\x64\x11\xd5\x53\x85\x0c\x30\x53\x81\x53\x85\x03\x65\x0e\xd0\x08\xe1\x1d\x06\x6c\x36\x65\x01\x1d\x06\x0b\xd0\x1e\x40\x05\xc5\x0c\x30\x05\xc1\x05\xc5\x03\x67\x0b\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\xe8\xff\xf3\xff\xf2\xff\xf1\xff\xf0\xff\xef\xff\x09\x00\x09\x00\x50\x00\x01\x8c\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00\x09\x00", 112, {}, {{ SH4CTX_R2, -24 }, { SH4CTX_R3, -26 }, { SH4CTX_R4, -28 }, { SH4CTX_R5, -30 }, { SH4CTX_R6, -32 }, { SH4CTX_R7, -34 }}}) SH4_TEST(mul, SH4Test {(uint8_t *)"\x07\x01\x1a\x01\x2f\x23\x1a\x03\x0b\x00\x09\x00", 12, {{ SH4CTX_R0, 0xfffffffe }, { SH4CTX_R1, 0x00005555 }, { SH4CTX_R2, 0xfffffffe }, { SH4CTX_R3, 0x00005555 }, { SH4CTX_R4, 0x00000002 }, { SH4CTX_R5, 0xffffaaaa }}, {{ SH4CTX_R1, 0xffff5556 }, { SH4CTX_R3, 0xffff5556 }}}) SH4_TEST(neg, SH4Test {(uint8_t *)"\x2b\x64\x0e\x40\x2a\x65\x02\x06\x0e\x40\x3a\x67\x02\x08\x0e\x41\x2a\x69\x02\x0a\x0e\x41\x3a\x6b\x02\x0c\x0b\x00\x09\x00", 30, {{ SH4CTX_R0, 0x700000f0 }, { SH4CTX_R1, 0x700000f1 }, { SH4CTX_R2, -4 }, { SH4CTX_R3, 0 }}, {{ SH4CTX_R4, 4 }, { SH4CTX_R5, 4 }, { SH4CTX_R6, 0x700000f1 }, { SH4CTX_R7, 0 }, { SH4CTX_R8, 0x700000f0 }, { SH4CTX_R9, 3 }, { SH4CTX_R10, 0x700000f1 }, { SH4CTX_R11, 0xffffffff }, { SH4CTX_R12, 0x700000f1 }}}) SH4_TEST(not, SH4Test {(uint8_t *)"\x07\x61\x0b\x00\x09\x00", 6, {{ SH4CTX_R0, 0xf0f0f0f0 }}, {{ SH4CTX_R1, 0x0f0f0f0f }}}) diff --git a/test/test_sh4.cc b/test/test_sh4.cc index 44bc19d1..ee40fa68 100644 --- a/test/test_sh4.cc +++ b/test/test_sh4.cc @@ -18,30 +18,29 @@ namespace dreavm { namespace cpu { void RunSH4Test(const SH4Test &test) { - Memory memory; + static uint32_t pc = 0x8c010000; - // initialize runtime + Memory memory; frontend::sh4::SH4Frontend rt_frontend(memory); backend::x64::X64Backend rt_backend(memory); - // backend::interpreter::InterpreterBackend rt_backend(memory); - Runtime runtime(memory); - ASSERT_TRUE(runtime.Init(&rt_frontend, &rt_backend)); + Runtime runtime(memory, rt_frontend, rt_backend); // initialize device - SH4 sh4(memory); - ASSERT_TRUE(sh4.Init(&runtime)); - sh4.ctx_.pc = 0x8c010000; + SH4 sh4(memory, runtime); + sh4.Init(); + sh4.SetPC(pc); - // mount the test binary and a small stack + // mount a small stack (stack grows down) uint8_t stack[MAX_PAGE_SIZE]; + sh4.ctx_.r[15] = sizeof(stack); + memory.Mount(0x0, sizeof(stack) - 1, ~ADDR_MASK, stack); + // mount the test binary uint32_t binary_size = core::align(static_cast(test.buffer_size), static_cast(MAX_PAGE_SIZE)); uint8_t *binary = new uint8_t[binary_size]; memcpy(binary, test.buffer, test.buffer_size); - - memory.Mount(0x0, sizeof(stack) - 1, MIRROR_MASK, stack); - memory.Mount(sh4.ctx_.pc, sh4.ctx_.pc + binary_size - 1, MIRROR_MASK, binary); + memory.Mount(pc, pc + binary_size - 1, ~ADDR_MASK, binary); // setup in registers for (auto it : test.r_in) {