gpsp/arm/arm64_stub.S
David Guillen Fandos bcd3d1ca29 [aarch64] Adding new aarch64 dynarec!
This is based on the MIPS dynarec (more or less) with some ARM
borrowings. Seems to be quite fast (under my testing fixed results:
faster than ARM on A1 but not a lot faster than the interpreter on
Android Snapdragon 845) but still some optimizations are missing at the
moment.

Seems to pass my testing suite and compatibility wise is very similar to
arm.
2021-12-12 13:18:13 +01:00

705 lines
30 KiB
ArmAsm

# gameplaySP
#
# Copyright (C) 2021 David Guillen Fandos <david@davidgf.net>
#
# 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"
#define defsymbl(symbol) \
.align 2; \
.type symbol, %function ;\
.global symbol ; \
.global _##symbol ; \
symbol: \
_##symbol:
.text
.align 2
#define REG_R0 (0 * 4)
#define REG_R1 (1 * 4)
#define REG_R2 (2 * 4)
#define REG_R3 (3 * 4)
#define REG_R4 (4 * 4)
#define REG_R5 (5 * 4)
#define REG_R6 (6 * 4)
#define REG_R7 (7 * 4)
#define REG_R8 (8 * 4)
#define REG_R9 (9 * 4)
#define REG_R10 (10 * 4)
#define REG_R11 (11 * 4)
#define REG_R12 (12 * 4)
#define REG_R13 (13 * 4)
#define REG_R14 (14 * 4)
#define REG_SP (13 * 4)
#define REG_LR (14 * 4)
#define REG_PC (15 * 4)
#define REG_CPSR (16 * 4)
#define CPU_MODE (17 * 4)
#define CPU_HALT_STATE (18 * 4)
#define REG_N_FLAG (20 * 4)
#define REG_Z_FLAG (21 * 4)
#define REG_C_FLAG (22 * 4)
#define REG_V_FLAG (23 * 4)
#define CHANGED_PC_STATUS (24 * 4)
#define COMPLETED_FRAME (25 * 4)
#define OAM_UPDATED (26 * 4)
#define REG_SAVE (27 * 4)
#define REG_SAVE2 (28 * 4)
#define REG_SAVE3 (29 * 4)
#define REG_SAVE4 (30 * 4)
#define REG_SAVE5 (31 * 4)
#define reg_base x20
#define reg_cycles w21
#define reg_c_flag w22
#define reg_v_flag w23
#define reg_z_flag w24
#define reg_n_flag w25
// Memory offsets from reg_base to the different buffers
#define RDMAP_OFF -0xB9000 // 8K pointers (64KB)
#define IWRAM_OFF -0xA9000 // 32KB (double for shadow)
#define VRAM_OFF -0x99000 // 96KB
#define EWRAM_OFF -0x81000 // 256KB (double for shadow)
#define MEM_TBL_OFF -0x1000 // Some space for the tables
#define SPSR_RAM_OFF 0x100
#define REGMODE_RAM_OFF 0x118
#define OAM_RAM_OFF 0x200
#define PAL_RAM_OFF 0x600
#define IOREG_OFF 0xA00
#define PALCNV_RAM_OFF 0xE00
// Used for SWI handling
#define MODE_SUPERVISOR 3
#define SUPERVISOR_SPSR (SPSR_RAM_OFF + 3*4) // spsr[3]
#define SUPERVISOR_LR (REGMODE_RAM_OFF + (3 * (7 * 4)) + (6 * 4)) // reg_mode[3][6]
// Stores and restores registers to their register storage in RAM
#define load_registers() ;\
ldp w6, w7, [reg_base, #0] ;\
ldp w8, w9, [reg_base, #8] ;\
ldp w10, w11, [reg_base, #16] ;\
ldp w12, w13, [reg_base, #24] ;\
ldp w14, w15, [reg_base, #32] ;\
ldp w16, w17, [reg_base, #40] ;\
ldp w26, w27, [reg_base, #48] ;\
ldr w28, [reg_base, #56] ;\
#define store_registers() ;\
stp w6, w7, [reg_base, #0] ;\
stp w8, w9, [reg_base, #8] ;\
stp w10, w11, [reg_base, #16] ;\
stp w12, w13, [reg_base, #24] ;\
stp w14, w15, [reg_base, #32] ;\
stp w16, w17, [reg_base, #40] ;\
stp w26, w27, [reg_base, #48] ;\
str w28, [reg_base, #56] ;\
// Extracts flags from CPSR into the cache flag registers
#define extract_flags_reg(tmpreg) ;\
ubfx reg_n_flag, tmpreg, #31, #1 ;\
ubfx reg_z_flag, tmpreg, #30, #1 ;\
ubfx reg_c_flag, tmpreg, #29, #1 ;\
ubfx reg_v_flag, tmpreg, #28, #1 ;\
#define extract_flags(tmpreg) ;\
ldr tmpreg, [reg_base, #REG_CPSR] ;\
extract_flags_reg(tmpreg) ;\
// Collects cache flag bits and consolidates them to the CPSR reg
#define consolidate_flags(tmpreg) ;\
ldr tmpreg, [reg_base, #REG_CPSR] ;\
bfi tmpreg, reg_n_flag, #31, #1 ;\
bfi tmpreg, reg_z_flag, #30, #1 ;\
bfi tmpreg, reg_c_flag, #29, #1 ;\
bfi tmpreg, reg_v_flag, #28, #1 ;\
str tmpreg, [reg_base, #REG_CPSR] ;\
// Update the GBA hardware (video, sound, input, etc)
// w0: current PC
defsymbl(a64_update_gba)
str w0, [reg_base, #REG_PC] // update the PC value
str lr, [reg_base, #REG_SAVE] // Save LR for later if needed
consolidate_flags(w0) // update the CPSR
store_registers() // save out registers
bl update_gba // update GBA state
ldr w1, [reg_base, #COMPLETED_FRAME] // return to main if new frame
cbnz w1, return_to_main
// Resume execution (perhaps from a new PC)
mov reg_cycles, w0 // load new cycle count
extract_flags(w2) // reload flag cache bits
ldr w0, [reg_base, #CHANGED_PC_STATUS] // see if PC has change
cbnz w0, 1f // go start from new PC
ldr lr, [reg_base, #REG_SAVE] // Restore return point
load_registers() // reload registers
ret // resume execution, no PC change
1: // Resume from new PC
ldr w0, [reg_base, #REG_PC] // load new PC
tbnz w2, #5, 2f // CPSR.T means in thumb mode
bl block_lookup_address_arm
load_registers() // reload registers
br x0 // jump to new ARM block
2:
bl block_lookup_address_thumb
load_registers() // reload registers
br x0 // jump to new Thumb block
.size a64_update_gba, .-a64_update_gba
// Cheat hooks for master function
// This is called whenever PC == cheats-master-function
// Just calls the C function to process cheats
defsymbl(a64_cheat_hook)
store_registers()
str lr, [reg_base, #REG_SAVE]
bl process_cheats
ldr lr, [reg_base, #REG_SAVE]
load_registers()
ret
// These are b stubs for performing indirect branches. They are not
// linked to and don't return, instead they link elsewhere.
// Input:
// r0: PC to branch to
defsymbl(a64_indirect_branch_arm)
store_registers()
bl block_lookup_address_arm
load_registers()
br x0
defsymbl(a64_indirect_branch_thumb)
store_registers()
bl block_lookup_address_thumb
load_registers()
br x0
defsymbl(a64_indirect_branch_dual)
store_registers()
bl block_lookup_address_dual
load_registers()
br x0
// Read CPSR and SPSR values
defsymbl(execute_read_cpsr)
consolidate_flags(w0) // Consolidate on ret value
ret
defsymbl(execute_read_spsr)
ldr w1, [reg_base, #CPU_MODE] // read cpu mode to w1
add x0, reg_base, #SPSR_RAM_OFF // ptr to spsr table
ldr w0, [x0, x1, lsl #2] // Read actual value from trable
ret
// Update the cpsr.
// Input:
// w0: new cpsr value
// w1: bitmask of which bits in cpsr to update
// w2: current PC
defsymbl(execute_store_cpsr)
ldr w4, [reg_base, #REG_CPSR] // read current CPSR
and w3, w0, w1 // reg_flags = new_cpsr & store_mask
bic w4, w4, w1 // current_cpsr & ~store_mask
orr w0, w3, w4 // w3 = final CPSR value
extract_flags_reg(w0) // Update cached flags too
str lr, [reg_base, #REG_SAVE]
store_registers()
bl execute_store_cpsr_body // Do the remaining work in C mode
cbnz w0, 1f // If PC has changed due to this
ldr lr, [reg_base, #REG_SAVE] // Resume execution where we left it
load_registers()
ret
1:
// Returned value contains the PC, resume execution there
bl block_lookup_address_arm
load_registers()
br x0 // Resume in the returned block
.size execute_store_cpsr, .-execute_store_cpsr
// Write to SPSR
// w0: new SPSR value
// w1: store mask
defsymbl(execute_store_spsr)
ldr w2, [reg_base, #CPU_MODE] // read cpu mode to w1
add x2, reg_base, x2, lsl #2 // calculate table offset
ldr w3, [x2, #SPSR_RAM_OFF] // Read actual value from trable
and w0, w0, w1 // new-spsr & mask
bic w3, w3, w1 // old-spsr & ~mask
orr w0, w0, w3 // final spsr value
str w0, [x2, #SPSR_RAM_OFF] // Store new SPSR
ret
.size execute_store_spsr, .-execute_store_spsr
// Restore the cpsr from the mode spsr and mode shift.
// Input:
// r0: current pc
defsymbl(execute_spsr_restore)
ldr w1, [reg_base, #CPU_MODE] // w1 = cpu_mode
cbz w1, 1f // Ignore if in user mode
lsl w2, w1, #2 // We access 32 bit words
add w2, w2, #SPSR_RAM_OFF
ldr w3, [reg_base, x2] // w3 = spsr[cpu_mode]
str w3, [reg_base, #REG_CPSR] // update CPSR with SPSR value
extract_flags_reg(w3) // update cached flag values
// This function call will pass r0 (address) and return it.
str lr, [reg_base, #REG_SAVE]
store_registers() // save ARM registers
bl execute_spsr_restore_body
ldr lr, [reg_base, #REG_SAVE]
load_registers()
1:
ret
.size execute_spsr_restore, .-execute_spsr_restore
// Setup the mode transition work for calling an SWI.
// Input:
// r0: current pc
defsymbl(execute_swi)
str lr, [reg_base, #REG_SAVE]
str w0, [reg_base, #SUPERVISOR_LR] // Store next PC into supervisor LR
consolidate_flags(w1) // Calculate current CPSR flags
str w1, [reg_base, #SUPERVISOR_SPSR] // Store them in the SPSR
bic w1, w1, #0x3F // Clear mode bits
mov w2, #(0x13 | 0x80) // Set supervisor mode bits
orr w1, w1, w2
str w1, [reg_base, #REG_CPSR] // Update CPSR with new value
store_registers()
mov w0, #MODE_SUPERVISOR
bl set_cpu_mode // Set supervisor mode
ldr lr, [reg_base, #REG_SAVE]
load_registers()
ret
.size execute_swi, .-execute_swi
defsymbl(execute_arm_translate_internal)
// save registers that will be clobbered
sub sp, sp, #96
stp x19, x20, [sp, #0]
stp x21, x22, [sp, #16]
stp x23, x24, [sp, #32]
stp x25, x26, [sp, #48]
stp x27, x28, [sp, #64]
stp x29, x30, [sp, #80]
mov reg_cycles, w0 // load cycle counter
mov reg_base, x1 // init base_reg
// Check whether the CPU is sleeping already, we should just wait for IRQs
ldr w1, [reg_base, #CPU_HALT_STATE]
cmp w1, #0
bne alert_loop
ldr w0, [reg_base, #REG_PC] // r0 = current pc
ldr w1, [reg_base, #REG_CPSR] // r1 = flags
tst w1, #0x20 // see if Thumb bit is set
extract_flags(w2) // load flags
bne 1f // if so lookup thumb
bl block_lookup_address_arm
load_registers()
br x0 // jump to first ARM block
1:
bl block_lookup_address_thumb
load_registers()
br x0 // jump to first Thumb block
// Epilogue to return to the main thread (whatever called execute_arm_translate)
return_to_main:
// restore the saved regs and return
ldp x19, x20, [sp, #0]
ldp x21, x22, [sp, #16]
ldp x23, x24, [sp, #32]
ldp x25, x26, [sp, #48]
ldp x27, x28, [sp, #64]
ldp x29, x30, [sp, #80]
add sp, sp, #96
ret
// Memory read stub routines
#define execute_load_builder(load_type, ldop, ldmask, tblidx, ldfn) ;\
;\
defsymbl(execute_load_##load_type) ;\
tst w0, #(0xf0000000 | ldmask) ;\
lsr w3, w0, #24 ;\
csinc w3, wzr, w3, ne ;\
add x4, reg_base, (MEM_TBL_OFF + tblidx*136) ;\
ldr x3, [x4, x3, lsl #3] ;\
br x3 ;\
;\
ld_bios_##load_type: /* BIOS area, need to verify PC */;\
lsr w3, w1, #24 /* Are we running the BIOS */;\
cbnz w3, ld_slow_##load_type ;\
and w0, w0, #(0x7fff) /* BIOS only 16 KB */;\
add x3, reg_base, #(RDMAP_OFF) ;\
ldr x3, [x3] /* x3 = bios mem buffer */;\
ldop w0, [x3, x0] /* load actual value */;\
ret ;\
;\
ld_ewram_##load_type: /* EWRAM area */;\
and w0, w0, #(0x3ffff) ;\
add x3, reg_base, #EWRAM_OFF ;\
ldop w0, [x3, x0] ;\
ret ;\
;\
ld_iwram_##load_type: /* IWRAM area */;\
and w0, w0, #(0x7fff) ;\
add x3, reg_base, #(IWRAM_OFF+0x8000) ;\
ldop w0, [x3, x0] ;\
ret ;\
;\
ld_ioram_##load_type: /* I/O RAM area */;\
and w0, w0, #(0x3ff) ;\
add x3, reg_base, #(IOREG_OFF) ;\
ldop w0, [x3, x0] ;\
ret ;\
;\
ld_palram_##load_type: /* PAL RAM area */;\
and w0, w0, #(0x3ff) ;\
add x3, reg_base, #(PAL_RAM_OFF) ;\
ldop w0, [x3, x0] ;\
ret ;\
;\
ld_oamram_##load_type: /* OAM RAM area */;\
and w0, w0, #(0x3ff) ;\
add x3, reg_base, #(OAM_RAM_OFF) ;\
ldop w0, [x3, x0] ;\
ret ;\
;\
ld_rdmap_##load_type: ;\
lsr w4, w0, #15 /* Each block is 32KB */;\
add x3, reg_base, #(RDMAP_OFF) ;\
ldr x4, [x3, x4, lsl #3] /* x4 = table pointer */;\
and w0, w0, #(0x7fff) /* 32KB pages */;\
ldop w0, [x4, x0] /* load actual value */;\
ret ;\
;\
ld_slow_##load_type: /* Slow C path */;\
str w1, [reg_base, #REG_PC] /* write out PC */;\
str lr, [reg_base, #REG_SAVE] /* Save LR */;\
store_registers() ;\
bl ldfn ;\
ldr lr, [reg_base, #REG_SAVE] ;\
load_registers() ;\
ret ;\
.size execute_load_##load_type, .-execute_load_##load_type
#define load_lookup_table(load_type, aload_type) ;\
.quad ld_slow_##aload_type /* -1: Unaligned/Bad access */;\
.quad ld_bios_##aload_type /* 0x00: BIOS */;\
.quad ld_slow_##aload_type /* 0x01: Open bus */;\
.quad ld_ewram_##load_type /* 0x02: ewram */;\
.quad ld_iwram_##load_type /* 0x03: iwram */;\
.quad ld_ioram_##load_type /* 0x04: I/O regs */;\
.quad ld_palram_##load_type /* 0x05: palette RAM */;\
.quad ld_rdmap_##load_type /* 0x06: vram */;\
.quad ld_oamram_##load_type /* 0x07: oam ram */;\
.quad ld_rdmap_##load_type /* 0x08: gamepak: ignore */;\
.quad ld_rdmap_##load_type /* 0x09: gamepak: ignore */;\
.quad ld_rdmap_##load_type /* 0x0A: gamepak: ignore */;\
.quad ld_rdmap_##load_type /* 0x0B: gamepak: ignore */;\
.quad ld_rdmap_##load_type /* 0x0C: gamepak: ignore */;\
.quad ld_slow_##aload_type /* 0x0D: EEPROM */;\
.quad ld_slow_##aload_type /* 0x0E: backup */;\
.quad ld_slow_##aload_type /* 0x0F: ignore */;\
// Aligned load is a bit special
defsymbl(execute_aligned_load32)
tst w0, #(0xf0000000)
lsr w3, w0, #24
csinc w3, wzr, w3, ne
add x4, reg_base, (MEM_TBL_OFF + 5*136)
ldr x3, [x4, x3, lsl #3]
br x3
ld_slow_aligned_u32: // Slow C path for multiple loads
str lr, [reg_base, #REG_SAVE] // Save LR
store_registers()
bl read_memory32
ldr lr, [reg_base, #REG_SAVE]
load_registers()
ret
ld_bios_aligned_u32:
and w0, w0, #(0x7fff) // Do not verify PC on purpose
add x3, reg_base, #(RDMAP_OFF)
ldr x3, [x3]
ldr w0, [x3, x0]
ret
execute_load_builder( u8, ldrb, 0, 0, read_memory8)
execute_load_builder( s8, ldrsb, 0, 1, read_memory8s)
execute_load_builder(u16, ldrh, 1, 2, read_memory16)
execute_load_builder(s16, ldrsh, 1, 3, read_memory16s)
execute_load_builder(u32, ldr, 3, 4, read_memory32)
// Prepares for a external store (calls C code)
#define store_align_8() and w1, w1, #0xff
#define store_align_16() and w1, w1, #0xffff; bic w0, w0, #1
#define store_align_32() bic w0, w0, #3
// Write out to memory.
// Input:
// w0: address
// w1: value
// w2: PC value
#define execute_store_builder(store_type, str_op, str_op16, load_op, \
stmask, stmask16, tblidx) ;\
;\
defsymbl(execute_store_u##store_type) ;\
lsr w4, w0, #28 ;\
lsr w3, w0, #24 ;\
cbnz w4, ext_store_u##store_type ;\
add x4, reg_base, (MEM_TBL_OFF + 816 + tblidx*128) ;\
ldr x3, [x4, x3, lsl #3] ;\
br x3 ;\
;\
ext_store_u##store_type: ;\
ext_store_u##store_type##_safe: ;\
str w2, [reg_base, #REG_PC] /* write out PC */;\
str lr, [reg_base, #REG_SAVE] /* Preserve LR */;\
store_align_##store_type() ;\
store_registers() ;\
bl write_memory##store_type ;\
cbnz w0, write_epilogue /* handle additional write stuff */;\
ldr lr, [reg_base, #REG_SAVE] ;\
load_registers() ;\
ret /* resume if no side effects */;\
;\
ext_store_iwram_u##store_type: ;\
and w0, w0, #(0x7fff & ~stmask) /* Mask to mirror memory (+align)*/;\
add x3, reg_base, #(IWRAM_OFF+0x8000) /* x3 = iwram base */;\
str_op w1, [x0, x3] /* store data */;\
sub x3, x3, #0x8000 /* x3 = iwram smc base */;\
load_op w1, [x0, x3] /* w1 = SMC sentinel */;\
cbnz w1, 3f /* Check value, should be zero */;\
ret /* return */;\
;\
ext_store_ewram_u##store_type: ;\
and w0, w0, #(0x3ffff & ~stmask) /* Mask to mirror memory (+align)*/;\
add x3, reg_base, #EWRAM_OFF /* x3 = ewram base */;\
str_op w1, [x0, x3] /* store data */;\
add x3, x3, #0x40000 /* x3 = ewram smc base */;\
load_op w1, [x0, x3] /* w1 = SMC sentinel */;\
cbnz w1, 3f /* Check value, should be zero */;\
ret /* return */;\
;\
ext_store_vram_u##store_type: ;\
ext_store_vram_u##store_type##_safe: ;\
and w0, w0, #(0x1ffff & ~stmask16) /* Mask to mirror memory (+align)*/;\
sub w3, w0, #0x8000 /* Mirrored addr for last bank */;\
cmp w0, #0x18000 /* Check if exceeds 96KB */;\
csel w0, w3, w0, cs /* If it does, pick the mirror */;\
add x3, reg_base, #VRAM_OFF /* x3 = ewram base */;\
str_op16 w1, [x0, x3] /* store data */;\
ret /* return */;\
;\
ext_store_oam_ram_u##store_type: ;\
ext_store_oam_ram_u##store_type##_safe: ;\
and w0, w0, #(0x3ff & ~stmask16) /* Mask to mirror memory (+align)*/;\
add x3, reg_base, #OAM_RAM_OFF /* x3 = oam ram base */;\
str_op16 w1, [x0, x3] /* store data */;\
str w29, [reg_base, #OAM_UPDATED] /* write non zero to signal */;\
ret /* return */;\
;\
3: ;\
str w2, [reg_base, #REG_PC] /* write out PC */;\
store_registers() /* store registers */;\
consolidate_flags(w1) ;\
b smc_write /* perform smc write */;\
.size execute_store_u##store_type, .-execute_store_u##store_type
// for ignored areas, just return
ext_store_ignore:
ret // return
#define store_lookup_table(store_type) ;\
.quad ext_store_ignore /* 0x00: BIOS, ignore */;\
.quad ext_store_ignore /* 0x01: ignore */;\
.quad ext_store_ewram_u##store_type /* 0x02: ewram */;\
.quad ext_store_iwram_u##store_type /* 0x03: iwram */;\
.quad ext_store_u##store_type /* 0x04: I/O regs */;\
.quad ext_store_u##store_type /* 0x05: palette RAM */;\
.quad ext_store_vram_u##store_type /* 0x06: vram */;\
.quad ext_store_oam_ram_u##store_type /* 0x07: oam ram */;\
.quad ext_store_u##store_type /* 0x08: gamepak: ignore */;\
.quad ext_store_u##store_type /* 0x09: gamepak: ignore */;\
.quad ext_store_u##store_type /* 0x0A: gamepak: ignore */;\
.quad ext_store_u##store_type /* 0x0B: gamepak: ignore */;\
.quad ext_store_u##store_type /* 0x0C: gamepak: ignore */;\
.quad ext_store_u##store_type /* 0x0D: EEPROM */;\
.quad ext_store_u##store_type /* 0x0E: backup */;\
.quad ext_store_ignore /* 0x0F: ignore */;\
execute_store_builder(8, strb, strh, ldrb, 0, 1, 0)
execute_store_builder(16, strh, strh, ldrh, 1, 1, 1)
execute_store_builder(32, str, str, ldr, 3, 3, 2)
// This is a store that is executed in a strm case (so no SMC checks in-between)
defsymbl(execute_aligned_store32)
lsr w4, w0, #28
lsr w3, w0, #24
cbnz w4, ext_store_u32
add x4, reg_base, MEM_TBL_OFF + 816 + 3*128
ldr x3, [x4, x3, lsl #3]
br x3
ext_store_iwram_u32_safe:
and w0, w0, #(0x7fff) // Mask to mirror memory (no need to align!)
add x3, reg_base, #(IWRAM_OFF+0x8000) // x3 = iwram base
str w1, [x0, x3] // store data
ret // Return
ext_store_ewram_u32_safe:
and w0, w0, #(0x3ffff) // Mask to mirror memory (no need to align!)
add x3, reg_base, #(EWRAM_OFF) // x3 = ewram base
str w1, [x0, x3] // store data
ret // Return
.size execute_aligned_store32, .-execute_aligned_store32
// This is called whenever an external store with side effects was performed
write_epilogue:
consolidate_flags(w1) // update the CPSR before update
cmp w0, #2 // see if the alert is due to SMC
beq smc_write // if so, goto SMC handler
alert_loop:
bl update_gba // update GBA until CPU isn't halted
ldr w1, [reg_base, #COMPLETED_FRAME] // Check whether a frame was completed
cbnz w1, return_to_main // and return to caller function.
ldr w1, [reg_base, #CPU_HALT_STATE] // Check whether the CPU is halted
cbnz w1, alert_loop // and keep looping until it is
mov reg_cycles, w0 // load new cycle count
ldr w0, [reg_base, #REG_PC] // load new PC
b lookup_pc // Resume execution at that PC
smc_write:
bl flush_translation_cache_ram
ldr w0, [reg_base, #REG_PC] // load "current new" PC
// Resume execution at PC (at w0)
lookup_pc:
ldr w1, [reg_base, #REG_CPSR] // w1 = flags
extract_flags_reg(w1)
tbnz w1, #5, 2f // see if Thumb bit is set
// Lookup and jump to the right mode block
bl block_lookup_address_arm
load_registers()
br x0
2:
bl block_lookup_address_thumb
load_registers()
br x0
.data
.align 4
defsymbl(ldst_handler_functions)
load_lookup_table(u8, u8)
load_lookup_table(s8, s8)
load_lookup_table(u16, u16)
load_lookup_table(s16, s16)
load_lookup_table(u32, u32)
load_lookup_table(u32, aligned_u32)
store_lookup_table(8)
store_lookup_table(16)
store_lookup_table(32)
store_lookup_table(32_safe)
.bss
.align 4
defsymbl(memory_map_read)
.space 0x10000
defsymbl(iwram)
.space 0x10000
defsymbl(vram)
.space 0x18000
defsymbl(ewram)
.space 0x80000
defsymbl(ldst_lookup_tables)
.space 4096
defsymbl(reg)
.space 0x100
defsymbl(spsr)
.space 24
defsymbl(reg_mode)
.space 196
.space 36 // Padding
defsymbl(oam_ram)
.space 0x400
defsymbl(palette_ram)
.space 0x400
defsymbl(io_registers)
.space 0x400
defsymbl(palette_ram_converted)
.space 0x400