mirror of
https://github.com/SonoSooS/PicoGB.git
synced 2025-06-18 17:05:34 -04:00
519 lines
14 KiB
C
519 lines
14 KiB
C
#include "microcode.h"
|
|
#include "microcode_dispatch.h"
|
|
#include "dbg.h"
|
|
|
|
|
|
#define self mb_state* __restrict
|
|
|
|
#define USE_MIC struct mb_mi_cache* __restrict mic = &mb->micache;
|
|
#define USE_MI struct mi_dispatch* __restrict mi = mb->mi;
|
|
|
|
//#define R_RELATIVE_ADDR (addr & MICACHE_R_SEL)
|
|
#define R_RELATIVE_ADDR (addr - (r_addr << MICACHE_R_BITS))
|
|
|
|
|
|
#pragma region Resolve uncached region + fabric interface
|
|
|
|
#if CONFIG_ENABLE_LRU
|
|
// Resolve an aligned(!) pointer to a ROM bank,
|
|
// based on an input address and current banking settings.
|
|
// addr < 0x8000
|
|
//TODO: get rid of this
|
|
PGB_FUNC static inline const r8* __restrict mch_resolve_mic_bank_internal(const self mb, word r_addr)
|
|
{
|
|
USE_MI;
|
|
|
|
const r8* __restrict ret = mi->dispatch_ROM_Bank(mi->userdata, r_addr << MICACHE_R_BITS, mi->BANK_ROM);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
PGB_FUNC static r8* __restrict mch_resolve_mic_r_direct(const self mb, word r_addr)
|
|
{
|
|
USE_MI;
|
|
|
|
if(r_addr < MICACHE_R_VALUE(0x8000))
|
|
{
|
|
// ROM area is unpredictable, can't calculate it here
|
|
__builtin_unreachable();
|
|
}
|
|
else if(r_addr < MICACHE_R_VALUE(0xA000))
|
|
{
|
|
r8* __restrict ptr = &mi->VRAM[mi->BANK_VRAM << 13];
|
|
|
|
r_addr -= MICACHE_R_VALUE(0x8000);
|
|
return &(ptr[r_addr << MICACHE_R_BITS]);
|
|
}
|
|
else if(r_addr < MICACHE_R_VALUE(0xC000))
|
|
{
|
|
r8* __restrict ptr = &mi->SRAM[mi->BANK_SRAM << 13];
|
|
|
|
r_addr -= MICACHE_R_VALUE(0xA000);
|
|
return &(ptr[r_addr << MICACHE_R_BITS]);
|
|
}
|
|
else // WRAM only [$C000; $FFFF] for OAMDMA
|
|
{
|
|
if(COMPILER_UNLIKELY(r_addr >= MICACHE_R_VALUE(0x10000)))
|
|
__builtin_unreachable();
|
|
|
|
if(r_addr < MICACHE_R_VALUE(0xE000))
|
|
r_addr -= MICACHE_R_VALUE(0xC000);
|
|
else
|
|
r_addr -= MICACHE_R_VALUE(0xE000);
|
|
|
|
if(r_addr < MICACHE_R_VALUE(0x1000))
|
|
{
|
|
return &(mi->WRAM[r_addr << MICACHE_R_BITS]);
|
|
}
|
|
else
|
|
{
|
|
var bank = mi->BANK_WRAM;
|
|
if(!bank)
|
|
bank = 1;
|
|
|
|
r_addr -= MICACHE_R_VALUE(0x1000);
|
|
return &(mi->WRAM[(bank << 12) + (r_addr << MICACHE_R_BITS)]);
|
|
}
|
|
}
|
|
}
|
|
|
|
PGB_FUNC static const r8* __restrict mch_resolve_mic_r_direct_ROM(const self mb, word r_addr)
|
|
{
|
|
USE_MI;
|
|
|
|
const r8* __restrict ret = NULL;
|
|
|
|
//TODO: optimize this for LRU
|
|
#if CONFIG_ENABLE_LRU
|
|
if(mi->ROM != NULL)
|
|
#endif
|
|
{
|
|
if(r_addr < MICACHE_R_VALUE(0x4000))
|
|
{
|
|
#if CONFIG_USE_FLAT_ROM
|
|
ret = &mi->ROM[0];
|
|
return &ret[r_addr << MICACHE_R_BITS];
|
|
#else
|
|
ret = mi->ROM[0];
|
|
if(ret != NULL)
|
|
return &ret[r_addr << MICACHE_R_BITS];
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#if CONFIG_USE_FLAT_ROM
|
|
r_addr -= MICACHE_R_VALUE(0x4000);
|
|
ret = &mi->ROM[mi->BANK_ROM << 14];
|
|
return &ret[r_addr << MICACHE_R_BITS];
|
|
#else
|
|
ret = mi->ROM[mi->BANK_ROM];
|
|
if(ret != NULL)
|
|
{
|
|
r_addr -= MICACHE_R_VALUE(0x4000);
|
|
return &ret[r_addr << MICACHE_R_BITS];
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if CONFIG_ENABLE_LRU
|
|
ret = mch_resolve_mic_bank_internal(mb, r_addr);
|
|
#endif
|
|
return ret;
|
|
}
|
|
|
|
// Uncached resolve aligned readable const memory area, based on address
|
|
PGB_FUNC ATTR_FORCE_NOINLINE static const r8* __restrict mch_resolve_mic_r_direct_read(const self mb, word r_addr)
|
|
{
|
|
if(r_addr < MICACHE_R_VALUE(0x8000))
|
|
{
|
|
return mch_resolve_mic_r_direct_ROM(mb, r_addr);
|
|
}
|
|
|
|
return mch_resolve_mic_r_direct(mb, r_addr);
|
|
}
|
|
|
|
PGB_FUNC ATTR_FORCE_NOINLINE static r8* __restrict mch_resolve_mic_r_direct_write(const self mb, word r_addr)
|
|
{
|
|
if(r_addr < MICACHE_R_VALUE(0x8000))
|
|
{
|
|
__builtin_unreachable();
|
|
|
|
// ROM is *Read-Only* Memory, can't write to it normally
|
|
return NULL;
|
|
}
|
|
|
|
return mch_resolve_mic_r_direct(mb, r_addr);
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Resolve cached memory region (by address)
|
|
/*
|
|
All functions in this region: cached memory resolve
|
|
- addr < 0xE000
|
|
*/
|
|
|
|
#pragma region Resolve (read)
|
|
|
|
PGB_FUNC ATTR_FORCE_NOINLINE __attribute__((optimize("Os"))) static const r8* __restrict mch_resolve_mic_read_slow(self mb, word addr)
|
|
{
|
|
const r8* __restrict ptr;
|
|
|
|
var r_addr = MICACHE_R_VALUE(addr);
|
|
|
|
ptr = mch_resolve_mic_r_direct_read(mb, r_addr);
|
|
if(COMPILER_LIKELY(ptr != NULL))
|
|
{
|
|
#if !CONFIG_MIC_CACHE_BYPASS
|
|
USE_MIC;
|
|
mic->mc_read[r_addr] = ptr;
|
|
#endif
|
|
|
|
return &ptr[R_RELATIVE_ADDR];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
PGB_FUNC ATTR_FORCE_NOINLINE __attribute__((optimize("Os"))) static word mch_resolve_mic_read_slow_deref(self mb, word addr)
|
|
{
|
|
return *mch_resolve_mic_read_slow(mb, addr);
|
|
}
|
|
|
|
PGB_FUNC ATTR_HOT __attribute__((optimize("O2"))) static word mch_resolve_mic_read_deref(self mb, word addr)
|
|
{
|
|
#if !CONFIG_MIC_CACHE_BYPASS
|
|
var r_addr = MICACHE_R_VALUE(addr);
|
|
|
|
const r8* __restrict ptr;
|
|
|
|
ptr = mb->micache.mc_read[r_addr];
|
|
if(COMPILER_LIKELY(ptr != NULL))
|
|
return ptr[R_RELATIVE_ADDR];
|
|
else
|
|
#endif
|
|
|
|
return mch_resolve_mic_read_slow_deref(mb, addr);
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Resolve (write)
|
|
|
|
PGB_FUNC ATTR_FORCE_NOINLINE __attribute__((optimize("Os"))) static void mch_resolve_mic_write_slow_deref(self mb, word addr, word data)
|
|
{
|
|
USE_MIC;
|
|
|
|
var r_addr = MICACHE_R_VALUE(addr);
|
|
|
|
r8* __restrict ptr;
|
|
ptr = mch_resolve_mic_r_direct_write(mb, r_addr);
|
|
if(COMPILER_LIKELY(ptr != NULL))
|
|
{
|
|
mic->mc_write[r_addr] = ptr;
|
|
ptr[R_RELATIVE_ADDR] = data;
|
|
return;
|
|
}
|
|
|
|
__builtin_unreachable();
|
|
*ptr = data;
|
|
}
|
|
|
|
PGB_FUNC ATTR_HOT __attribute__((optimize("Os"))) static void mch_resolve_mic_write_deref_helper(r8* __restrict ptr, word addr, word data)
|
|
{
|
|
var r_addr = MICACHE_R_VALUE(addr);
|
|
addr = R_RELATIVE_ADDR;
|
|
ptr[addr] = data;
|
|
}
|
|
|
|
PGB_FUNC ATTR_HOT ATTR_FORCE_NOINLINE __attribute__((optimize("O2"))) static void mch_resolve_mic_write_deref(self mb, word addr, word data)
|
|
{
|
|
#if !CONFIG_MIC_CACHE_BYPASS
|
|
var r_addr = MICACHE_R_VALUE(addr);
|
|
|
|
r8* __restrict ptr;
|
|
|
|
ptr = mb->micache.mc_write[r_addr];
|
|
if(ptr != NULL)
|
|
{
|
|
mch_resolve_mic_write_deref_helper(ptr, addr, data); //HACK: prevent register allocator spill, as it's slower than a tail call
|
|
return;
|
|
}
|
|
else
|
|
#endif
|
|
|
|
mch_resolve_mic_write_slow_deref(mb, addr, data);
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Resolve (execute)
|
|
|
|
PGB_FUNC ATTR_FORCE_NOINLINE __attribute__((optimize("Os"))) static const r8* __restrict mch_resolve_mic_execute_slow(self mb, word addr)
|
|
{
|
|
const r8* __restrict ptr;
|
|
|
|
var r_addr = MICACHE_R_VALUE(addr);
|
|
|
|
ptr = mch_resolve_mic_r_direct_read(mb, r_addr);
|
|
if(COMPILER_LIKELY(ptr != NULL))
|
|
{
|
|
#if !CONFIG_MIC_CACHE_BYPASS
|
|
USE_MIC;
|
|
mic->mc_execute[r_addr] = ptr;
|
|
#endif
|
|
|
|
return &ptr[R_RELATIVE_ADDR];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
PGB_FUNC ATTR_HOT ATTR_FORCE_INLINE __attribute__((optimize("O2"))) static inline const r8* __restrict mch_resolve_mic_execute(self mb, word addr)
|
|
{
|
|
|
|
#if !CONFIG_MIC_CACHE_BYPASS
|
|
var r_addr = MICACHE_R_VALUE(addr);
|
|
|
|
const r8* __restrict ptr;
|
|
|
|
ptr = mb->micache.mc_execute[r_addr];
|
|
if(COMPILER_LIKELY(ptr != NULL))
|
|
return &ptr[R_RELATIVE_ADDR];
|
|
#endif
|
|
|
|
return mch_resolve_mic_execute_slow(mb, addr);
|
|
}
|
|
|
|
PGB_FUNC ATTR_FORCE_NOINLINE __attribute__((optimize("Os"))) static word mch_resolve_mic_execute_slow_deref(self mb, word addr)
|
|
{
|
|
return *mch_resolve_mic_execute_slow(mb, addr);
|
|
}
|
|
|
|
PGB_FUNC ATTR_HOT ATTR_FORCE_INLINE __attribute__((optimize("O2"))) static inline word mch_resolve_mic_execute_deref(self mb, word addr)
|
|
{
|
|
#if !CONFIG_MIC_CACHE_BYPASS
|
|
var r_addr = MICACHE_R_VALUE(addr);
|
|
|
|
const r8* __restrict ptr;
|
|
|
|
ptr = mb->micache.mc_execute[r_addr];
|
|
if(COMPILER_LIKELY(ptr != NULL))
|
|
return ptr[R_RELATIVE_ADDR];
|
|
else
|
|
#endif
|
|
|
|
return mch_resolve_mic_execute_slow_deref(mb, addr);
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Dispatch
|
|
|
|
#pragma region Dispatch special (IO + ROM)
|
|
|
|
PGB_FUNC ATTR_FORCE_NOINLINE __attribute__((optimize("O2"))) static void mch_memory_dispatch_write_ROM(const self mb, word addr, word data)
|
|
{
|
|
mb->mi->dispatch_ROM(mb->mi->userdata, addr, data, MB_TYPE_WRITE);
|
|
}
|
|
|
|
PGB_FUNC ATTR_HOT ATTR_FORCE_NOINLINE __attribute__((optimize("O2"))) static word mch_memory_dispatch_read_IO(const self mb, word haddr)
|
|
{
|
|
return mb->mi->dispatch_IO(mb->mi->userdata, haddr, MB_DATA_DONTCARE, MB_TYPE_READ);
|
|
}
|
|
|
|
PGB_FUNC ATTR_HOT ATTR_FORCE_NOINLINE __attribute__((optimize("O2"))) static void mch_memory_dispatch_write_IO(const self mb, word haddr, word data)
|
|
{
|
|
mb->mi->dispatch_IO(mb->mi->userdata, haddr, data, MB_TYPE_WRITE);
|
|
}
|
|
|
|
PGB_FUNC ATTR_HOT ATTR_FORCE_NOINLINE __attribute__((optimize("O2"))) static word mch_memory_dispatch_read_HRAM(const self mb, word haddr)
|
|
{
|
|
if(haddr < 0xFF)
|
|
return mb->mi->HRAM[haddr - 0x80];
|
|
else
|
|
return mb->IE;
|
|
}
|
|
|
|
PGB_FUNC ATTR_HOT ATTR_FORCE_NOINLINE __attribute__((optimize("Os"))) word mch_memory_dispatch_read_Haddr(const self mb, word haddr)
|
|
{
|
|
if(haddr < 0x80)
|
|
return mch_memory_dispatch_read_IO(mb, haddr);
|
|
else
|
|
return mch_memory_dispatch_read_HRAM(mb, haddr);
|
|
}
|
|
|
|
PGB_FUNC ATTR_HOT ATTR_FORCE_NOINLINE __attribute__((optimize("Os"))) void mch_memory_dispatch_write_Haddr(self mb, word haddr, word data)
|
|
{
|
|
if(haddr < 0x80)
|
|
mch_memory_dispatch_write_IO(mb, haddr, data);
|
|
else if(haddr < 0xFF)
|
|
mb->mi->HRAM[haddr - 0x80] = data;
|
|
else
|
|
mb->IE = data;
|
|
}
|
|
|
|
PGB_FUNC ATTR_FORCE_NOINLINE __attribute__((optimize("O2"))) static word mch_memory_dispatch_read_fexx_ffxx(const self mb, word addr)
|
|
{
|
|
if(addr & 0x100)
|
|
return mch_memory_dispatch_read_Haddr(mb, addr & 0xFF);
|
|
|
|
return mb->mi->OAM[addr & 0xFF];
|
|
}
|
|
|
|
PGB_FUNC ATTR_FORCE_NOINLINE __attribute__((optimize("O2"))) static void mch_memory_dispatch_write_fexx_ffxx(self mb, word addr, word data)
|
|
{
|
|
if(addr & 0x100)
|
|
{
|
|
mch_memory_dispatch_write_Haddr(mb, addr & 0xFF, data);
|
|
return;
|
|
}
|
|
|
|
mb->mi->OAM[addr & 0xFF] = addr;
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Dispatch fetch (1)
|
|
|
|
PGB_FUNC ATTR_HOT ATTR_FORCE_NOINLINE __attribute__((optimize("Os"))) static word mch_memory_fetch_decode_1(self mb, word addr)
|
|
{
|
|
if(COMPILER_LIKELY(addr < 0xFE00))
|
|
return mch_resolve_mic_execute_deref(mb, addr);
|
|
else
|
|
return mch_memory_dispatch_read_fexx_ffxx(mb, addr);
|
|
}
|
|
|
|
PGB_FUNC ATTR_HOT word mch_memory_fetch_PC_op_1(self mb)
|
|
{
|
|
word addr = mb->PC;
|
|
mb->PC = (addr + 1) & 0xFFFF;
|
|
|
|
var res = mch_memory_fetch_decode_1(mb, addr);
|
|
DBGF("- /O1 %04X <> %02X\n", addr, res);
|
|
return res;
|
|
}
|
|
|
|
PGB_FUNC ATTR_HOT word mch_memory_fetch_PC(self mb)
|
|
{
|
|
word addr = mb->PC;
|
|
mb->PC = (addr + 1) & 0xFFFF;
|
|
|
|
var res = mch_memory_fetch_decode_1(mb, addr);
|
|
DBGF("- /M1 %04X <> %02X\n", addr, res);
|
|
return res;
|
|
}
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Dispatch fetch (2)
|
|
|
|
PGB_FUNC ATTR_FORCE_NOINLINE __attribute__((optimize("O1"))) static word mch_memory_fetch_decode_2_slow(self mb, word addr)
|
|
{
|
|
word addr2 = (addr + 1) & 0xFFFF;
|
|
|
|
word res1 = mch_memory_fetch_decode_1(mb, addr);
|
|
word res2 = mch_memory_fetch_decode_1(mb, addr2);
|
|
|
|
var res = res1;
|
|
res += (res2) << 8;
|
|
|
|
return res;
|
|
}
|
|
|
|
PGB_FUNC ATTR_HOT ATTR_FORCE_NOINLINE __attribute__((optimize("O1"))) static word mch_resolve_mic_execute_deref_2_aligned(self mb, word addr)
|
|
{
|
|
// This may have to be volatile, otherwise
|
|
// an LDRH is emitted, which is no cool, as
|
|
// the pointer will very likely be not aligned.
|
|
// This sadly wastes precious CPU cycles,
|
|
// but it's necessary to avoid unaligned data abort.
|
|
const r8* __restrict ptr = mch_resolve_mic_execute(mb, addr);
|
|
|
|
word nres = ptr[0];
|
|
nres += ptr[1] << 8;
|
|
|
|
return nres;
|
|
}
|
|
|
|
PGB_FUNC ATTR_HOT ATTR_FORCE_NOINLINE __attribute__((optimize("Os"))) static word mch_memory_fetch_decode_2(self mb, word addr)
|
|
{
|
|
#if !CONFIG_MIC_CACHE_BYPASS
|
|
|
|
if(COMPILER_LIKELY(addr < 0xFE00)) // yeah, this is an off-by-one error, and I don't care
|
|
{
|
|
//word r1 = MICACHE_R_VALUE(addr);
|
|
//word r2 = MICACHE_R_VALUE(addr + 1);
|
|
//if(r1 == r2)
|
|
|
|
if(COMPILER_LIKELY((addr + 1) & MICACHE_R_SEL)) // same as above commented code
|
|
{
|
|
return mch_resolve_mic_execute_deref_2_aligned(mb, addr);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return mch_memory_fetch_decode_2_slow(mb, addr);
|
|
}
|
|
|
|
PGB_FUNC ATTR_HOT word mch_memory_fetch_PC_op_2(self mb)
|
|
{
|
|
word addr = mb->PC;
|
|
mb->PC = (addr + 2) & 0xFFFF;
|
|
|
|
word resp = mch_memory_fetch_decode_2(mb, addr);
|
|
DBGF("- /O2 %04X <> %04X\n", addr, resp);
|
|
return resp;
|
|
}
|
|
|
|
|
|
#pragma endregion
|
|
|
|
#pragma region Dispatch read and write
|
|
|
|
PGB_FUNC ATTR_HOT ATTR_FORCE_NOINLINE __attribute__((optimize("Os"))) void mch_memory_dispatch_write(self mb, word addr, word data)
|
|
{
|
|
DBGF("- /WR %04X <- %02X\n", addr, data);
|
|
|
|
if(COMPILER_LIKELY(addr >= 0x8000))
|
|
{
|
|
if(COMPILER_LIKELY(addr < 0xFE00))
|
|
mch_resolve_mic_write_deref(mb, addr, data);
|
|
else
|
|
mch_memory_dispatch_write_fexx_ffxx(mb, addr, data);
|
|
}
|
|
else
|
|
mch_memory_dispatch_write_ROM(mb, addr, data);
|
|
}
|
|
|
|
#if !CONFIG_DBG
|
|
#define mch_memory_dispatch_read_ mch_memory_dispatch_read
|
|
#endif
|
|
|
|
PGB_FUNC ATTR_HOT ATTR_FORCE_NOINLINE __attribute__((optimize("Os"))) word mch_memory_dispatch_read_(self mb, word addr)
|
|
{
|
|
if(COMPILER_LIKELY(addr < 0xFE00))
|
|
return mch_resolve_mic_read_deref(mb, addr);
|
|
else
|
|
return mch_memory_dispatch_read_fexx_ffxx(mb, addr);
|
|
}
|
|
|
|
#if CONFIG_DBG
|
|
PGB_FUNC ATTR_HOT word mch_memory_dispatch_read(self mb, word addr)
|
|
{
|
|
DBGF("- /RD %04X -> ", addr);
|
|
word res = mch_memory_dispatch_read_(mb, addr);
|
|
DBGF("%02X\n", res);
|
|
return res;
|
|
}
|
|
#endif
|
|
|
|
#pragma endregion
|
|
|
|
#pragma endregion
|
|
|