Move memory dispatch code separate from the microcode

This commit is contained in:
SonoSooS 2025-02-22 05:49:22 +01:00
parent 38160087f6
commit ad57534181
4 changed files with 529 additions and 508 deletions

View File

@ -1,12 +1,12 @@
#include <stdio.h>
#include "microcode.h"
#include "microcode_dispatch.h"
#include "dbg.h"
#define self mb_state* __restrict
#if GBA
#define IR_F_COL (IR & 7)
#define IR_F_ROW ((IR >> 3) & 7)
@ -19,515 +19,9 @@
#define MB_AF_W(v) {mb->reg.A = ((v) >> 8) & 0xFF; mb->reg.F = (v) & MB_FLAG_BITS;}
#define MB_CC_CHECK (mbh_cc_check(IR, mb->reg.F))
#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 Microcode I/O
#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"))) static 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"))) static 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 static 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 static 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 static 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"))) static 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);
}
PGB_FUNC ATTR_HOT ATTR_FORCE_NOINLINE __attribute__((optimize("Os"))) static 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 static 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;
}
#else
#define mch_memory_dispatch_read mch_memory_dispatch_read_
#endif
#pragma endregion
#pragma endregion
#pragma region Flag mode control

518
microcode_dispatch.c Normal file
View File

@ -0,0 +1,518 @@
#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

9
microcode_dispatch.h Normal file
View File

@ -0,0 +1,9 @@
word mch_memory_dispatch_read_Haddr(const mb_state* __restrict mb, word haddr);
void mch_memory_dispatch_write_Haddr(mb_state* __restrict mb, word haddr, word data);
word mch_memory_fetch_PC_op_2(mb_state* __restrict mb);
word mch_memory_fetch_PC_op_1(mb_state* __restrict mb);
word mch_memory_fetch_PC(mb_state* __restrict mb);
void mch_memory_dispatch_write(mb_state* __restrict mb, word addr, word data);
word mch_memory_dispatch_read(mb_state* __restrict mb, word addr);

View File

@ -1,2 +1,2 @@
@gcc -o tst.exe -g -Og -DPICOGB_CUSTOM=1 -I. ..\microcode.c ..\mi.c ..\ppu.c ..\apu.c ..\fabric.c winmain.c %* -lgdi32 -lwinmm
@gcc -o tst.exe -g -Og -DPICOGB_CUSTOM=1 -I. ..\microcode_dispatch.c ..\microcode.c ..\mi.c ..\ppu.c ..\apu.c ..\fabric.c winmain.c %* -lgdi32 -lwinmm
@IF %ERRORLEVEL% NEQ 0 @(pause)