gpsp/x86/x86_stub.S

522 lines
18 KiB
ArmAsm
Raw Normal View History

# 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:
2011-09-02 23:58:39 +02:00
#ifndef _WIN32
# External symbols (data + functions)
2011-09-02 23:58:39 +02:00
#define _update_gba update_gba
#define _block_lookup_address_arm block_lookup_address_arm
#define _block_lookup_address_thumb block_lookup_address_thumb
#define _block_lookup_address_dual block_lookup_address_dual
#define _write_io_register8 write_io_register8
#define _write_io_register16 write_io_register16
#define _write_io_register32 write_io_register32
#define _flush_translation_cache_ram flush_translation_cache_ram
#define _write_eeprom write_eeprom
#define _write_backup write_backup
#define _write_rtc write_rtc
#define _execute_store_cpsr_body execute_store_cpsr_body
#endif
.extern _spsr
.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_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)
2021-11-02 23:16:47 +01:00
.equ store_aligned_u32_tbl, -(16 * 4)
.equ store_u32_tbl, -(32 * 4)
.equ store_u16_tbl, -(48 * 4)
.equ store_u8_tbl, -(64 * 4)
.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
2021-09-17 20:00:37 +02:00
.equ IORAM_OFF, 0xA8D00
.equ SPSR_OFF, 0xA9100
#define REG_CYCLES %ebp
# destroys ecx and edx
.macro collapse_flag offset, shift
mov \offset(%ebx), %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(%ebx), %ecx
and $0xFF, %ecx
or %ecx, %edx
.endm
.macro collapse_flags
collapse_flags_no_update
mov %edx, REG_CPSR(%ebx)
.endm
.macro extract_flag shift, offset
mov REG_CPSR(%ebx), %edx
shr $\shift, %edx
and $0x01, %edx
2011-09-02 23:58:39 +02:00
mov %edx, \offset(%ebx)
.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.
# eax: current address
st:
.asciz "u\n"
defsymbl(x86_update_gba)
mov %eax, REG_PC(%ebx) # current PC = eax
collapse_flags # update cpsr, trashes ecx and edx
call _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(%ebx)
jne return_to_main
# did the PC change?
cmpl $1, CHANGED_PC_STATUS(%ebx)
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.
# eax: GBA address to branch to
defsymbl(x86_indirect_branch_arm)
call _block_lookup_address_arm
jmp *%eax
# For indirect branches that'll definitely go to Thumb. In
# Thumb mode any indirect branches except for BX.
defsymbl(x86_indirect_branch_thumb)
call _block_lookup_address_thumb
jmp *%eax
# 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)
defsymbl(x86_indirect_branch_dual)
call _block_lookup_address_dual
jmp *%eax
# General ext memory routines
ext_store_ignore:
ret # ignore these writes
2021-11-02 23:16:47 +01:00
ext_store_rtc:
and $0xFFFF, %edx # make value 16bit
and $0xFF, %eax # mask address
jmp _write_rtc # write out RTC register
ext_store_backup:
and $0xFF, %edx # make value 8bit
and $0xFFFF, %eax # mask address
jmp _write_backup # perform backup write
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:
call _update_gba # process the next event
# did we just complete a frame? go back to main then
cmpl $0, COMPLETED_FRAME(%ebx)
jne return_to_main
# see if the halt status has changed
mov CPU_HALT_STATE(%ebx), %edx
cmp $0, %edx # 0 means it has
jnz alert_loop # if not go again
mov %eax, REG_CYCLES # load new cycle count
jmp lookup_pc # pc has definitely changed
no_alert:
ret
ext_store_eeprom:
jmp _write_eeprom # perform eeprom write
2021-11-02 23:16:47 +01:00
# Register wrapping for various sizes
#define reg32(n) %e##n##x
#define reg16(n) %##n##x
#define reg8(n) %##n##l
2021-11-02 23:16:47 +01:00
# 16 bit bus results in duplicated 8bit accesses
#define dup8() mov %dl, %dh
#define noop()
2021-11-02 23:16:47 +01:00
# 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
2021-11-02 23:16:47 +01:00
# 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(%ebx, %ecx, 4) ;\
;\
ext_##fname##_iwram##wsize: ;\
and $(0x7FFF & addrm), %eax /* Addr wrap */ ;\
mov regfn(d), (IWRAM_OFF+0x8000)(%ebx, %eax) /* Actual write */ ;\
smc_check_##fname(opsuf, IWRAM_OFF(%ebx, %eax)) ;\
ret ;\
;\
ext_##fname##_ewram##wsize: ;\
and $(0x3FFFF & addrm), %eax /* Addr wrap */ ;\
mov regfn(d), EWRAM_OFF(%ebx, %eax) /* Actual write */ ;\
smc_check_##fname(opsuf, (EWRAM_OFF+0x40000)(%ebx, %eax)) ;\
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(%ebx, %eax) /* Actual write */ ;\
ret ;\
;\
ext_##fname##_oam##wsize: ;\
and $(0x3FE & addrm), %eax /* Addr wrap */ ;\
movl $1, OAM_UPDATED(%ebx) /* flag OAM update */ ;\
dup8fn() /* Double byte for 8b access */ ;\
mov regfn16(d), OAM_RAM_OFF(%ebx, %eax) /* Actual write */ ;\
ret ;\
;\
ext_##fname##_io##wsize: ;\
and $(0x3FF & addrm), %eax /* Addr wrap */ ;\
call _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
2021-11-02 23:16:47 +01:00
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(%ebx, %eax) # 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(%ebx, %eax)
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
# %eax = new_cpsr
# %edx = store_mask
defsymbl(execute_store_cpsr)
mov %edx, REG_SAVE(%ebx) # save store_mask
mov %eax, %ecx # ecx = new_cpsr
and %edx, %ecx # ecx = new_cpsr & store_mask
mov REG_CPSR(%ebx), %eax # eax = cpsr
not %edx # edx = ~store_mask
and %edx, %eax # eax = cpsr & ~store_mask
or %ecx, %eax # eax = new cpsr combined with old
call _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 changed_pc_cpsr # might have changed the PC
ret # return
changed_pc_cpsr:
add $4, %esp # get rid of current return address
call _block_lookup_address_arm # lookup new PC
jmp *%eax
smc_write:
call _flush_translation_cache_ram
lookup_pc:
add $4, %esp # Can't return, discard addr
movl $0, CHANGED_PC_STATUS(%ebx) # Lookup new block and jump to it
mov REG_PC(%ebx), %eax
testl $0x20, REG_CPSR(%ebx)
jz lookup_pc_arm
lookup_pc_thumb:
call _block_lookup_address_thumb
jmp *%eax
lookup_pc_arm:
call _block_lookup_address_arm
jmp *%eax
# eax: cycle counter
defsymbl(execute_arm_translate_internal)
# Save main context, since we need to return gracefully
pushl %ebx
pushl %esi
pushl %edi
pushl %ebp
movl %edx, %ebx # load base register (arg1)
extract_flags # load flag variables
movl %eax, REG_CYCLES # load cycle counter (arg0)
movl REG_PC(%ebx), %eax # load PC
# (if the CPU is halted, do not start executing but
# loop in the alert loop until it wakes up)
cmpl $0, CPU_HALT_STATE(%ebx)
je 1f
call alert_loop # Need to push something to the stack
1:
testl $0x20, REG_CPSR(%ebx)
jnz 2f
call _block_lookup_address_arm
jmp *%eax # jump to it
2:
call _block_lookup_address_thumb
jmp *%eax
return_to_main:
add $4, %esp # remove current return addr
popl %ebp
popl %edi
popl %esi
popl %ebx
ret
.data
defsymbl(x86_table_data)
ext_store_u8_jtable:
.long ext_store_ignore # 0x00 BIOS, ignore
.long ext_store_ignore # 0x01 invalid, ignore
.long ext_store_ewram8 # 0x02 EWRAM
.long ext_store_iwram8 # 0x03 IWRAM
.long ext_store_io8 # 0x04 I/O registers
.long ext_store_palette8 # 0x05 Palette RAM
.long ext_store_vram8 # 0x06 VRAM
.long ext_store_oam8 # 0x07 OAM RAM
.long ext_store_ignore # 0x08 gamepak (no RTC accepted in 8bit)
.long ext_store_ignore # 0x09 gamepak, ignore
.long ext_store_ignore # 0x0A gamepak, ignore
.long ext_store_ignore # 0x0B gamepak, ignore
.long ext_store_ignore # 0x0C gamepak, ignore
.long ext_store_eeprom # 0x0D EEPROM (possibly)
.long ext_store_backup # 0x0E Flash ROM/SRAM
.long ext_store_ignore # 0x0F ignore
ext_store_u16_jtable:
.long ext_store_ignore # 0x00 BIOS, ignore
.long ext_store_ignore # 0x01 invalid, ignore
.long ext_store_ewram16 # 0x02 EWRAM
.long ext_store_iwram16 # 0x03 IWRAM
.long ext_store_io16 # 0x04 I/O registers
.long ext_store_palette16 # 0x05 Palette RAM
.long ext_store_vram16 # 0x06 VRAM
.long ext_store_oam16 # 0x07 OAM RAM
.long ext_store_rtc # 0x08 gamepak or RTC
.long ext_store_ignore # 0x09 gamepak, ignore
.long ext_store_ignore # 0x0A gamepak, ignore
.long ext_store_ignore # 0x0B gamepak, ignore
.long ext_store_ignore # 0x0C gamepak, ignore
.long ext_store_eeprom # 0x0D EEPROM (possibly)
.long ext_store_ignore # 0x0E Flash ROM/SRAM must be 8bit
.long ext_store_ignore # 0x0F ignore
ext_store_u32_jtable:
.long ext_store_ignore # 0x00 BIOS, ignore
.long ext_store_ignore # 0x01 invalid, ignore
.long ext_store_ewram32 # 0x02 EWRAM
.long ext_store_iwram32 # 0x03 IWRAM
.long ext_store_io32 # 0x04 I/O registers
.long ext_store_palette32 # 0x05 Palette RAM
.long ext_store_vram32 # 0x06 VRAM
.long ext_store_oam32 # 0x07 OAM RAM
.long ext_store_ignore # 0x08 gamepak, ignore (no RTC in 32bit)
.long ext_store_ignore # 0x09 gamepak, ignore
.long ext_store_ignore # 0x0A gamepak, ignore
.long ext_store_ignore # 0x0B gamepak, ignore
.long ext_store_ignore # 0x0C gamepak, ignore
.long ext_store_eeprom # 0x0D EEPROM (possibly)
.long ext_store_ignore # 0x0E Flash ROM/SRAM must be 8bit
.long ext_store_ignore # 0x0F ignore
2021-11-02 23:16:47 +01:00
ext_store_aligned_u32_jtable:
.long ext_store_ignore # 0x00 BIOS, ignore
.long ext_store_ignore # 0x01 invalid, ignore
.long ext_store_aligned_ewram32 # 0x02 EWRAM
.long ext_store_aligned_iwram32 # 0x03 IWRAM
.long ext_store_io32 # 0x04 I/O registers
.long ext_store_palette32 # 0x05 Palette RAM
.long ext_store_vram32 # 0x06 VRAM
.long ext_store_oam32 # 0x07 OAM RAM
.long ext_store_ignore # 0x08 gamepak, ignore (no RTC in 32bit)
.long ext_store_ignore # 0x09 gamepak, ignore
.long ext_store_ignore # 0x0A gamepak, ignore
.long ext_store_ignore # 0x0B gamepak, ignore
.long ext_store_ignore # 0x0C gamepak, ignore
.long ext_store_eeprom # 0x0D EEPROM (possibly)
.long ext_store_ignore # 0x0E Flash ROM/SRAM must be 8bit
.long ext_store_ignore # 0x0F ignore
.bss
.align 64
defsymbl(x86_table_info)
2021-11-02 23:16:47 +01:00
.space 4*4*16
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
defsymbl(reg_mode)
.space 196
defsymbl(memory_map_read)
2021-03-18 01:18:39 +01:00
.space 0x8000
#if !defined(HAVE_MMAP)
# Make this section executable!
.text
.section .jit,"awx",%nobits
.align 4
defsymbl(rom_translation_cache)
.space ROM_TRANSLATION_CACHE_SIZE
defsymbl(ram_translation_cache)
.space RAM_TRANSLATION_CACHE_SIZE
#endif