gpsp/x86/x86_stub.S
David Guillen Fandos 4f3c9a5e58 [all] Fix CPSR and CPU modes
gpsp doesn't differentiate between USER and SYSTEM mode, most likely
cause it is not that important for most games. This implements the modes
correctly and adds checks for privileged operations. Still some
bugs/hacks but it mostly fixes CPSR/SPSR reads/writes.

To implement PSR writes we are using a more refined masks and force mode
bit num. 4 to always be one. Reserved bits are forced to zero (this
needs to be validated on a real device).
2023-01-11 21:26:32 +01:00

628 lines
27 KiB
ArmAsm

# gameplaySP
#
# Copyright (C) 2006 Exophase <exophase@gmail.com>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#include "../gpsp_config.h"
.align 4
#define defsymbl(symbol) \
.global symbol ; \
.global _##symbol ; \
symbol: \
_##symbol:
// Windows 32 bit ABI prefixes functions with underscore
#if defined(_WIN32) && !defined(_WIN64)
#define fnm(name) _##name
#else
#define fnm(name) name
#endif
// Convention calls are different, and register allocations, which makes it tricky
// All functions in this file are called manually from the JIT arena (unless stated
// otherwise), where we use our own convention call. However calls to C code must
// follow the calling convention. x86 is built with regparm=2 to avoid stack usage.
#if defined(__x86_64__) || defined(__amd64__)
#define ADDR_TYPE .quad
#define ADDR_SIZE_BYTES 8
#define STACK_REG %rsp
#define FULLREG(rn) %r##rn
#define SAVE_REGISTERS push %rbx; push %rsi; push %rdi; push %rbp
#define REST_REGISTERS pop %rbp; pop %rdi; pop %rsi; pop %rbx
#define REG_BASE %rbx
#ifdef _WIN64
#define CARG1_REG %ecx // Windows x64 ABI, of course different :D
#define CARG2_REG %edx
#define CARG2_REGPTR %rdx
#define CALL_FUNC(name) \
sub $32, %rsp; \
call fnm(name); \
add $32, %rsp
#else
#define CARG1_REG %edi // SystemV AMD64 ABI
#define CARG2_REG %esi
#define CARG2_REGPTR %rsi
#define CALL_FUNC(name) \
call fnm(name)
#endif
#define SETUP_ARGS mov %eax, CARG1_REG; mov %edx, CARG2_REG;
#else
#define ADDR_TYPE .long
#define ADDR_SIZE_BYTES 4
#define STACK_REG %esp
#define FULLREG(rn) %e##rn
#define SAVE_REGISTERS sub $8, %esp; push %ebx; push %esi; push %edi; push %ebp
#define REST_REGISTERS pop %ebp; pop %edi; pop %esi; pop %ebx; add $8, %esp;
#define REG_BASE %ebx
#define CARG1_REG %eax
#define CARG2_REG %edx
#define CARG2_REGPTR %edx
#define SETUP_ARGS
#define CALL_FUNC(name) \
call fnm(name)
#endif
.equ REG_SP, (13 * 4)
.equ REG_LR, (14 * 4)
.equ REG_PC, (15 * 4)
.equ REG_CPSR, (16 * 4)
.equ CPU_MODE, (17 * 4)
.equ CPU_HALT_STATE, (18 * 4)
.equ REG_BUS_VALUE, (19 * 4)
.equ REG_N_FLAG, (20 * 4)
.equ REG_Z_FLAG, (21 * 4)
.equ REG_C_FLAG, (22 * 4)
.equ REG_V_FLAG, (23 * 4)
.equ CHANGED_PC_STATUS, (24 * 4)
.equ COMPLETED_FRAME, (25 * 4)
.equ OAM_UPDATED, (26 * 4)
.equ REG_SAVE, (27 * 4)
.equ REG_SAVE2, (28 * 4)
.equ REG_SAVE3, (29 * 4)
.equ REG_SAVE4, (30 * 4)
.equ REG_SAVE5, (31 * 4)
.equ load_u8_tbl, -(9 * 16 * ADDR_SIZE_BYTES)
.equ load_s8_tbl, -(8 * 16 * ADDR_SIZE_BYTES)
.equ load_u16_tbl, -(7 * 16 * ADDR_SIZE_BYTES)
.equ load_s16_tbl, -(6 * 16 * ADDR_SIZE_BYTES)
.equ load_u32_tbl, -(5 * 16 * ADDR_SIZE_BYTES)
.equ store_u8_tbl, -(4 * 16 * ADDR_SIZE_BYTES)
.equ store_u16_tbl, -(3 * 16 * ADDR_SIZE_BYTES)
.equ store_u32_tbl, -(2 * 16 * ADDR_SIZE_BYTES)
.equ store_aligned_u32_tbl, -(1 * 16 * ADDR_SIZE_BYTES)
.equ PALETTE_RAM_OFF, 0x0100
.equ PALETTE_RAM_CNV_OFF, 0x0500
.equ OAM_RAM_OFF, 0x0900
.equ IWRAM_OFF, 0x0D00
.equ VRAM_OFF, 0x10D00
.equ EWRAM_OFF, 0x28D00
.equ IORAM_OFF, 0xA8D00
.equ SPSR_OFF, 0xA9100
.equ RDMAP_OFF, 0xA9200
#define REG_CYCLES %ebp
# destroys ecx and edx
.macro collapse_flag offset, shift
mov \offset(REG_BASE), %ecx
shl $\shift, %ecx
or %ecx, %edx
.endm
.macro collapse_flags_no_update
xor %edx, %edx
collapse_flag REG_N_FLAG, 31
collapse_flag REG_Z_FLAG, 30
collapse_flag REG_C_FLAG, 29
collapse_flag REG_V_FLAG, 28
mov REG_CPSR(REG_BASE), %ecx
and $0xFF, %ecx
or %ecx, %edx
.endm
.macro collapse_flags
collapse_flags_no_update
mov %edx, REG_CPSR(REG_BASE)
.endm
.macro extract_flag shift, offset
mov REG_CPSR(REG_BASE), %edx
shr $\shift, %edx
and $0x01, %edx
mov %edx, \offset(REG_BASE)
.endm
.macro extract_flags
extract_flag 31, REG_N_FLAG
extract_flag 30, REG_Z_FLAG
extract_flag 29, REG_C_FLAG
extract_flag 28, REG_V_FLAG
.endm
# Process a hardware event. Since an interrupt might be
# raised we have to check if the PC has changed.
# arg0 (always in eax): current PC address
defsymbl(x86_update_gba)
mov %eax, REG_PC(REG_BASE) # current PC = eax
collapse_flags # update cpsr, trashes ecx and edx
mov REG_CYCLES, CARG1_REG # Load remaining cycles as arg0
CALL_FUNC(update_gba) # process the next event
mov %eax, REG_CYCLES # new cycle count
# did we just complete a frame? go back to main then
cmpl $0, COMPLETED_FRAME(REG_BASE)
jne return_to_main
# did the PC change?
cmpl $1, CHANGED_PC_STATUS(REG_BASE)
je lookup_pc
ret # if not, go back to caller
# Perform this on an indirect branch that will definitely go to
# ARM code, IE anything that changes the PC in ARM mode except
# for BX and data processing to PC with the S bit set.
# arg0 (always in eax): GBA address to branch to
defsymbl(x86_indirect_branch_arm)
mov %eax, CARG1_REG
CALL_FUNC(block_lookup_address_arm)
add $ADDR_SIZE_BYTES, STACK_REG # remove current return addr
jmp *FULLREG(ax)
# For indirect branches that'll definitely go to Thumb. In
# Thumb mode any indirect branches except for BX.
# arg0 (always in eax): GBA address to branch to
defsymbl(x86_indirect_branch_thumb)
mov %eax, CARG1_REG
CALL_FUNC(block_lookup_address_thumb)
add $ADDR_SIZE_BYTES, STACK_REG # remove current return addr
jmp *FULLREG(ax)
# For indirect branches that can go to either Thumb or ARM,
# mainly BX (also data processing to PC with S bit set, be
# sure to adjust the target with a 1 in the lowest bit for this)
# arg0 (always in eax): GBA address to branch to
defsymbl(x86_indirect_branch_dual)
mov %eax, CARG1_REG
CALL_FUNC(block_lookup_address_dual)
add $ADDR_SIZE_BYTES, STACK_REG # remove current return addr
jmp *FULLREG(ax)
# General ext memory routines
ext_store_rtc8: # No RTC writes on byte or word access
ext_store_rtc32:
ext_store_backup16: # Backup (flash) accessed via byte writes
ext_store_backup32:
ext_store_ignore:
ret # ignore these writes
ext_store_rtc16:
and $0xFFFF, %edx # make value 16bit
and $0xFF, %eax # mask address
SETUP_ARGS # Setup addr, value
CALL_FUNC(write_rtc) # write out RTC register
ret
ext_store_backup8:
and $0xFF, %edx # make value 8bit
and $0xFFFF, %eax # mask address
SETUP_ARGS # Setup addr, value
CALL_FUNC(write_backup) # perform backup write
ret
write_epilogue:
cmp $0, %eax # 0 return means nothing happened
jz no_alert # if so we can leave
collapse_flags # make sure flags are good for function call
cmp $2, %eax # see if it was an SMC trigger
je smc_write
alert_loop:
mov REG_CYCLES, CARG1_REG # Load remaining cycles as arg0
CALL_FUNC(update_gba) # process the next event
mov %eax, REG_CYCLES # load new cycle count
# did we just complete a frame? go back to main then
cmpl $0, COMPLETED_FRAME(REG_BASE)
jne return_to_main
# see if the halt status has changed
mov CPU_HALT_STATE(REG_BASE), %edx
cmp $0, %edx # 0 means it has
jnz alert_loop # if not go again
jmp lookup_pc # pc has definitely changed
no_alert:
ret
ext_store_eeprom:
CALL_FUNC(write_eeprom) # perform eeprom write
ret
# Register wrapping for various sizes
#define reg32(n) %e##n##x
#define reg16(n) %##n##x
#define reg8(n) %##n##l
# 16 bit bus results in duplicated 8bit accesses
#define dup8() mov %dl, %dh
#define noop()
# Writes to EWRAM and IWRAM must check for SMC
#define smc_check_store_aligned(opsuf, addrexp)
#define smc_check_store(opsuf, addrexp) ;\
cmp##opsuf $0, addrexp /* Check SMC mirror */ ;\
jne smc_write
# Memory write routines
#define write_stubs(fname, wsize, opsuf, regfn, regfn16, addrm, dup8fn) ;\
;\
/* eax: address to write to */ ;\
/* edx: value to write */ ;\
;\
defsymbl(execute_##fname##_u##wsize) ;\
mov %eax, %ecx /* ecx = address */ ;\
shr $24, %ecx /* ecx = address >> 24 */ ;\
cmp $15, %ecx ;\
ja ext_store_ignore ;\
/* ecx = ext_store_u*_jtable[address >> 24] */ ;\
jmp *fname##_u##wsize##_tbl(REG_BASE, FULLREG(cx), ADDR_SIZE_BYTES) ;\
;\
ext_##fname##_iwram##wsize: ;\
and $(0x7FFF & addrm), %eax /* Addr wrap */ ;\
mov regfn(d), (IWRAM_OFF+0x8000)(REG_BASE, FULLREG(ax)) /* Actual write */ ;\
smc_check_##fname(opsuf, IWRAM_OFF(REG_BASE, FULLREG(ax))) ;\
ret ;\
;\
ext_##fname##_ewram##wsize: ;\
and $(0x3FFFF & addrm), %eax /* Addr wrap */ ;\
mov regfn(d), EWRAM_OFF(REG_BASE, FULLREG(ax)) /* Actual write */ ;\
smc_check_##fname(opsuf, (EWRAM_OFF+0x40000)(REG_BASE, FULLREG(ax))) ;\
ret ;\
;\
ext_##fname##_vram##wsize: ;\
and $(0x1FFFE & addrm), %eax /* Addr wrap */ ;\
dup8fn() /* Double byte for 8b access */ ;\
cmp $0x18000, %eax /* Weird 96KB mirror */ ;\
jb 1f ;\
sub $0x8000, %eax /* Mirror last bank */ ;\
1: ;\
mov regfn16(d), VRAM_OFF(REG_BASE, FULLREG(ax)) /* Actual write */ ;\
ret ;\
;\
ext_##fname##_oam##wsize: ;\
and $(0x3FE & addrm), %eax /* Addr wrap */ ;\
movl $1, OAM_UPDATED(REG_BASE) /* flag OAM update */ ;\
dup8fn() /* Double byte for 8b access */ ;\
mov regfn16(d), OAM_RAM_OFF(REG_BASE, FULLREG(ax)) /* Actual write */ ;\
ret ;\
;\
ext_##fname##_io##wsize: ;\
and $(0x3FF & addrm), %eax /* Addr wrap */ ;\
SETUP_ARGS ;\
CALL_FUNC(write_io_register##wsize) /* Call C code */ ;\
jmp write_epilogue /* Might need an update */ ;\
write_stubs(store, 32, l, reg32, reg32, ~3, noop)
write_stubs(store, 16, w, reg16, reg16, ~1, noop)
write_stubs(store, 8, b, reg8, reg16, ~0, dup8)
write_stubs(store_aligned, 32, l, reg32, reg32, ~3, noop)
# Palette routines are a bit special, due to 16bit bus + decoded palette
ext_store_palette8:
and $0x3FE, %eax # wrap around address and align to 16bits
mov %dl, %dh # duplicate the byte to be written
jmp ext_store_palette16b # perform 16bit palette write
ext_store_palette16:
and $0x3FF, %eax # wrap around address
ext_store_palette16b: # entry point for 8bit write
mov %dx, PALETTE_RAM_OFF(REG_BASE, FULLREG(ax)) # write out palette value
mov %edx, %ecx # cx = dx
shl $11, %ecx # cx <<= 11 (red component is in high bits)
mov %dh, %cl # bottom bits of cx = top bits of dx
shr $2, %cl # move the blue component to the bottom of cl
and $0x03E0, %dx # isolate green component of dx
shl $1, %dx # make green component 6bits
or %edx, %ecx # combine green component into ecx
# write out the freshly converted palette value
mov %cx, PALETTE_RAM_CNV_OFF(REG_BASE, FULLREG(ax))
ret # done
ext_store_palette32:
and $0x3FF, %eax # wrap around address
call ext_store_palette16b # write first 16bits
add $2, %eax # go to next address
shr $16, %edx # go to next 16bits
jmp ext_store_palette16b # write next 16bits
# Memory load routines
#define load_stubs(rtype, movop, addrm, albits, slowfn) ;\
;\
/* eax: address to read */ ;\
/* edx: current PC address */ ;\
;\
defsymbl(execute_load_##rtype) ;\
mov %eax, %ecx /* ecx = address */ ;\
rol $8, %ecx /* ecx = ror(address, 24) */ ;\
and $((1<<(8+albits))-1), %ecx /* preserve align+msb */ ;\
cmp $15, %ecx ;\
ja ext_load_slow##rtype ;\
jmp *load_##rtype##_tbl(REG_BASE, FULLREG(cx), ADDR_SIZE_BYTES) ;\
;\
ext_load_iwram##rtype: ;\
and $(0x7FFF & addrm), %eax /* Addr wrap */ ;\
movop (IWRAM_OFF+0x8000)(REG_BASE, FULLREG(ax)), %eax /* Read mem */ ;\
ret ;\
;\
ext_load_ewram##rtype: ;\
and $(0x3FFFF & addrm), %eax /* Addr wrap */ ;\
movop EWRAM_OFF(REG_BASE, FULLREG(ax)), %eax /* Read mem */ ;\
ret ;\
;\
ext_load_vram##rtype: ;\
and $(0x1FFFF & addrm), %eax /* Addr wrap */ ;\
cmp $0x18000, %eax /* Weird 96KB mirror */ ;\
jb 1f ;\
sub $0x8000, %eax /* Mirror last bank */ ;\
1: ;\
movop VRAM_OFF(REG_BASE, FULLREG(ax)), %eax /* Read mem */ ;\
ret ;\
;\
ext_load_oam##rtype: ;\
and $(0x3FF & addrm), %eax /* Addr wrap */ ;\
movop OAM_RAM_OFF(REG_BASE, FULLREG(ax)), %eax /* Read mem */ ;\
ret ;\
;\
ext_load_palette##rtype: ;\
and $(0x3FF & addrm), %eax /* Addr wrap */ ;\
movop PALETTE_RAM_OFF(REG_BASE, FULLREG(ax)), %eax /* Read mem */ ;\
ret ;\
;\
ext_load_io##rtype: ;\
and $(0x3FF & addrm), %eax /* Addr wrap */ ;\
movop IORAM_OFF(REG_BASE, FULLREG(ax)), %eax /* Read mem */ ;\
ret ;\
;\
ext_load_rom##rtype: ;\
mov %eax, %ecx /* ecx = address */ ;\
shr $15, %ecx /* ecx = address >> 15 */ ;\
/* Read rdmap pointer */ ;\
mov RDMAP_OFF(REG_BASE, FULLREG(cx), ADDR_SIZE_BYTES), FULLREG(dx) ;\
mov %eax, %ecx /* ecx = address */ ;\
and $0x7FFF, %ecx /* ecx = address LSB */ ;\
movop (FULLREG(dx), FULLREG(cx)), %eax /* Read mem */ ;\
ret ;\
;\
ext_load_slow##rtype: ;\
mov %edx, REG_PC(REG_BASE) /* Store current PC */ ;\
SETUP_ARGS ;\
CALL_FUNC(slowfn) ;\
ret ;\
load_stubs(u32, mov, ~3, 2, read_memory32)
load_stubs(u16, movzwl, ~1, 1, read_memory16)
load_stubs(s16, movswl, ~1, 1, read_memory16s)
load_stubs( u8, movzbl, ~0, 0, read_memory8)
load_stubs( s8, movsbl, ~0, 0, read_memory8s)
# arg0 (%eax) = new_cpsr
# arg1 (%edx) = store_mask (user mode)
# arg2 (%ecx) = store_mask (system mode)
defsymbl(execute_store_cpsr)
testl $0x10, CPU_MODE(REG_BASE) # check privileged mode bit
cmovne %ecx, %edx # use system mode mask if set
mov %edx, %esi # save store_mask for later
mov %eax, %ecx # ecx = new_cpsr
and %edx, %ecx # ecx = new_cpsr & store_mask
mov REG_CPSR(REG_BASE), %eax # eax = cpsr
not %edx # edx = ~store_mask
and %edx, %eax # eax = cpsr & ~store_mask
or %ecx, %eax # eax = new cpsr combined with old
mov %eax, REG_CPSR(REG_BASE) # save new cpsr to register
# Check whether any side effects (IRQ) could have happened
test $0xff, %esi
jnz 1f
extract_flags # pull out flag vars from new CPSR
ret
1:
CALL_FUNC(execute_store_cpsr_body) # do the dirty work in this C function
extract_flags # pull out flag vars from new CPSR
cmp $0, %eax # see if return value is 0
jnz 2f # might have changed the PC
ret # return
2: # PC has changed, due to IRQ triggered
mov %eax, CARG1_REG # Returned addr from C function
CALL_FUNC(block_lookup_address_arm) # lookup new PC
add $ADDR_SIZE_BYTES, STACK_REG # get rid of current return address
jmp *FULLREG(ax)
# On writes that overwrite code, cache is flushed and execution re-started
smc_write:
CALL_FUNC(flush_translation_cache_ram)
lookup_pc:
movl $0, CHANGED_PC_STATUS(REG_BASE) # Lookup new block and jump to it
mov REG_PC(REG_BASE), CARG1_REG # Load PC as argument0
testl $0x20, REG_CPSR(REG_BASE)
jz 1f
### Thumb mode
CALL_FUNC(block_lookup_address_thumb)
add $ADDR_SIZE_BYTES, STACK_REG # Can't return, discard addr
jmp *FULLREG(ax)
1:# ARM mode
CALL_FUNC(block_lookup_address_arm)
add $ADDR_SIZE_BYTES, STACK_REG # Can't return, discard addr
jmp *FULLREG(ax)
# Called from C, args are platform dependant :/
# arg0 (eax/edi/ecx): cycle counter
# arg1 (edx/rsi/rdx): reg base pointer
defsymbl(execute_arm_translate_internal)
# Save main context, since we need to return gracefully
SAVE_REGISTERS # Pushes 16 or 32 bytes
# The stack here is aligned to 16 bytes minus 4 or 8 bytes.
mov CARG1_REG, REG_CYCLES # load cycle counter (arg0)
mov CARG2_REGPTR, REG_BASE # load base register (arg1)
extract_flags # load flag variables
# (if the CPU is halted, do not start executing but
# loop in the alert loop until it wakes up)
cmpl $0, CPU_HALT_STATE(REG_BASE)
je 1f
call alert_loop # Need to push something to the stack
1:
call lookup_pc # Go fetch and execute PC
return_to_main:
add $ADDR_SIZE_BYTES, STACK_REG # remove current return addr
REST_REGISTERS # Restore saved registers
ret
#define load_table(atype) ;\
ADDR_TYPE ext_load_slow##atype /* 0x00 BIOS */;\
ADDR_TYPE ext_load_slow##atype /* 0x01 open read */;\
ADDR_TYPE ext_load_ewram##atype /* 0x02 EWRAM */;\
ADDR_TYPE ext_load_iwram##atype /* 0x03 IWRAM */;\
ADDR_TYPE ext_load_io##atype /* 0x04 I/O registers */;\
ADDR_TYPE ext_load_palette##atype /* 0x05 Palette RAM */;\
ADDR_TYPE ext_load_vram##atype /* 0x06 VRAM */;\
ADDR_TYPE ext_load_oam##atype /* 0x07 OAM RAM */;\
ADDR_TYPE ext_load_rom##atype /* 0x08 gamepak (or RTC) */;\
ADDR_TYPE ext_load_rom##atype /* 0x09 gamepak */;\
ADDR_TYPE ext_load_rom##atype /* 0x0A gamepak */;\
ADDR_TYPE ext_load_rom##atype /* 0x0B gamepak */;\
ADDR_TYPE ext_load_rom##atype /* 0x0C gamepak */;\
ADDR_TYPE ext_load_slow##atype /* 0x0D EEPROM (possibly) */;\
ADDR_TYPE ext_load_slow##atype /* 0x0E Flash ROM/SRAM */;\
ADDR_TYPE ext_load_slow##atype /* 0x0F open read */;\
#define store_table(asize) ;\
ADDR_TYPE ext_store_ignore /* 0x00 BIOS, ignore */;\
ADDR_TYPE ext_store_ignore /* 0x01 invalid, ignore */;\
ADDR_TYPE ext_store_ewram##asize /* 0x02 EWRAM */;\
ADDR_TYPE ext_store_iwram##asize /* 0x03 IWRAM */;\
ADDR_TYPE ext_store_io##asize /* 0x04 I/O registers */;\
ADDR_TYPE ext_store_palette##asize /* 0x05 Palette RAM */;\
ADDR_TYPE ext_store_vram##asize /* 0x06 VRAM */;\
ADDR_TYPE ext_store_oam##asize /* 0x07 OAM RAM */;\
ADDR_TYPE ext_store_rtc##asize /* 0x08 gamepak (RTC or ignore) */;\
ADDR_TYPE ext_store_ignore /* 0x09 gamepak, ignore */;\
ADDR_TYPE ext_store_ignore /* 0x0A gamepak, ignore */;\
ADDR_TYPE ext_store_ignore /* 0x0B gamepak, ignore */;\
ADDR_TYPE ext_store_ignore /* 0x0C gamepak, ignore */;\
ADDR_TYPE ext_store_eeprom /* 0x0D EEPROM (possibly) */;\
ADDR_TYPE ext_store_backup##asize /* 0x0E Flash ROM/SRAM */;\
ADDR_TYPE ext_store_ignore /* 0x0F ignore */;\
.data
.align 16
defsymbl(x86_table_data)
load_table(u8)
load_table(s8)
load_table(u16)
load_table(s16)
load_table(u32)
store_table(8)
store_table(16)
store_table(32)
# aligned word writes (non SMC signaling)
ADDR_TYPE ext_store_ignore # 0x00 BIOS, ignore
ADDR_TYPE ext_store_ignore # 0x01 invalid, ignore
ADDR_TYPE ext_store_aligned_ewram32 # 0x02 EWRAM
ADDR_TYPE ext_store_aligned_iwram32 # 0x03 IWRAM
ADDR_TYPE ext_store_io32 # 0x04 I/O registers
ADDR_TYPE ext_store_palette32 # 0x05 Palette RAM
ADDR_TYPE ext_store_vram32 # 0x06 VRAM
ADDR_TYPE ext_store_oam32 # 0x07 OAM RAM
ADDR_TYPE ext_store_ignore # 0x08 gamepak, ignore (no RTC in 32bit)
ADDR_TYPE ext_store_ignore # 0x09 gamepak, ignore
ADDR_TYPE ext_store_ignore # 0x0A gamepak, ignore
ADDR_TYPE ext_store_ignore # 0x0B gamepak, ignore
ADDR_TYPE ext_store_ignore # 0x0C gamepak, ignore
ADDR_TYPE ext_store_eeprom # 0x0D EEPROM (possibly)
ADDR_TYPE ext_store_ignore # 0x0E Flash ROM/SRAM must be 8bit
ADDR_TYPE ext_store_ignore # 0x0F ignore
.bss
.align 64
defsymbl(x86_table_info)
.space 9*16*ADDR_SIZE_BYTES
defsymbl(reg)
.space 0x100
defsymbl(palette_ram)
.space 0x400
defsymbl(palette_ram_converted)
.space 0x400
defsymbl(oam_ram)
.space 0x400
defsymbl(iwram)
.space 0x10000
defsymbl(vram)
.space 0x18000
defsymbl(ewram)
.space 0x80000
defsymbl(io_registers)
.space 0x400
defsymbl(spsr)
.space 24
.space 8 # padding
defsymbl(reg_mode)
.space 196
.space 28 # padding
defsymbl(memory_map_read)
.space 8*1024*ADDR_SIZE_BYTES
#ifndef MMAP_JIT_CACHE
#error "x86 dynarec builds *require* MMAP_JIT_CACHE"
#endif