📄 emu65816.asm
字号:
.model flat, C
.486P
.data
;_DATA SEGMENT DWORD PUBLIC USE32 'DATA'
align 4
regpack struct
PC dd ?
Y dd ?
X dd ?
DBR_2less db ?
db ?
DBR db ?
db ?
D dd ?
A dd ?
P db 4 dup (?)
S dd ?
E db 4 dup (?)
regpack ends
FCARRY equ 1
FZERO equ 2
FINTERRUPT equ 4
FDECIMAL equ 8
FINDEX equ 16
FMEMORY equ 32
FOVERFLOW equ 64
FNEGATIVE equ 128
extrn _registers:dword near
extrn _scan_cycles:dword near
extrn _startaddr:dword near
extrn _debug_instr:dword near
extrn _rom:dword near ; pointer
extrn _state:byte near ; first two elements: end_wai, within_wai
extrn _returntogui:byte near
_reg regpack <>
PUBLIC _reg
NZ_8bit db 02h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h ; /* 00-0F */
db 00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h ; /* 10-1F */
db 00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h ; /* 20-2F */
db 00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h ; /* 30-3F */
db 00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h ; /* 40-4F */
db 00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h ; /* 50-5F */
db 00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h ; /* 60-6F */
db 00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h,00h ; /* 70-7F */
db 80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h ; /* 80-8F */
db 80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h ; /* 90-9F */
db 80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h ; /* A0-AF */
db 80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h ; /* B0-BF */
db 80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h ; /* C0-CF */
db 80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h ; /* D0-DF */
db 80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h ; /* E0-EF */
db 80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h,80h ; /* F0-FF */
;****************************************************************************
; SNES MEMORY ACCESS MACROS
;****************************************************************************
; EAX USED AS TEMPORARY; don't hold an arg in EAX.
convert_snesptr macro snesaddr
; Takes snes address and converts it to a PC pointer. reg is 32-bit.
; EAX destroyed.
mov eax, snesaddr
shr eax, 11
and snesaddr, 01FFFh ; offset
and al, 0FCh
mov eax, [_startaddr+eax] ; base pointer
add snesaddr, eax ; add 'em
endm convert_snesptr
trapandwrite8 macro pcptr, reg
; Takes data from reg and puts it in "snesaddr" and traps the write
local notrom, notrap, isrom
cmp pcptr, [_rom]
jb notrom
cmp pcptr, [_rom] + 300000h
jb isrom
notrom:
mov [pcptr], reg
cmp pcptr, offset _registers
jb notrap
cmp pcptr, offset _registers + 4000h
jae notrap
mov byte [_reg.P], cl
mov dword [_reg.PC], edx
mov dword [_scan_cycles], esi
push eax
push edx
push ecx
push dword 0
push pcptr
call _trapregwrite
pop eax
pop edx
pop ecx
pop edx
pop eax
notrap:
isrom:
endm writesnesmem
trapandwrite16 macro pcptr, lo, hi
; Takes data from reg and puts it in "snesaddr" and traps the write
local notrom, notrap, isrom
cmp pcptr, [_rom]
jb notrom
cmp pcptr, [_rom] + 300000h
jb isrom
notrom:
mov byte [pcptr], lo
mov byte [pcptr+1], hi
cmp pcptr, offset [_registers]
jb notrap
cmp pcptr, offset [_registers] + 4000h
jae notrap
mov byte [_reg.P], cl
mov dword [_reg.PC], edx
mov dword [_scan_cycles], esi
push eax
push edx
push ecx
push dword 1
push pcptr
call _trapregwrite
pop eax ; arg 1
pop edx ; arg 2
pop ecx
pop edx
pop eax
notrap:
isrom:
endm writesnesmem
trapandread8 macro pcptr
; Traps the read of a PC pointer pointing to a part of SNES address space
local notrap
cmp pcptr, offset _registers
jb notrap
cmp pcptr, offset _registers + 4000h
jae notrap
mov byte [_reg.P], cl
mov dword [_reg.PC], edx
mov dword [_scan_cycles], esi
push eax
push edx
push ecx
push dword 0
push pcptr
call _trapregread
pop eax ; arg 1
pop edx ; arg 2
pop ecx
pop edx
pop eax
mov esi, dword [_scan_cycles]
notrap:
mov pcptr, [pcptr]
endm trapandread
trapandread16 macro pcptr
; Traps the read of a PC pointer pointing to a part of SNES address space
local notrap
cmp pcptr, offset _registers
jb notrap
cmp pcptr, offset _registers + 4000h
jae notrap
mov byte [_reg.P], cl
mov dword [_reg.PC], edx
mov dword [_scan_cycles], esi
push eax
push edx
push ecx
push dword 1
push pcptr
call _trapregread
pop eax ; arg 1
pop edx ; arg 2
pop ecx
pop edx
pop eax
mov esi, dword [_scan_cycles]
notrap:
mov pcptr, [pcptr]
endm trapandread
;****************************************************************************
; ADDRESSING MODE MACROS
;****************************************************************************
; ADDRESSING MODE MACROS: These load EBX with the pointer to value; EAX
; used as temporary. Indirections are not read trapped. Assumes EBX
; contains OPDATA.
convert_abs_code macro
shr ebx, 8
mov eax, edx
and ebx, 0000FFFFh
and eax, 00FF0000h
or ebx, eax
endm load_absdataaddress
convert_abs_data macro ; Converts OPDATA (EBX) to SNES 24bit address
; value returned in EBX ; For absolute addressing modes
shr ebx, 8
and ebx, 0FFFFh
or ebx, dword [_reg.DBR_2less]
endm load_absdataaddress
convert_dir macro ; Converts OPDATA (EBX) to SNES 24bit address
; value returned in EBX ; For direct addressing modes
shr ebx, 8
and ebx, 0FFh
add ebx, dword [_reg.D]
; and ebx, 0FFFFh generally useless, but adds extra cycle!
endm load_absdataaddress
get_imm macro
shr ebx, 8
endm get_imm
load_abs macro ; (SNESMEM ((DBR << 16) + OPWORD))
convert_abs_data
convert_snesptr ebx
endm load_abs
load_abs_long macro ; (SNESMEM (OPLONG))
shr ebx, 8 ; now have 24bit address
convert_snesptr ebx
endm load_abs_long
load_dir macro ; (SNESMEM ((D + OPBYTE) & 0xFFFF))
convert_dir
convert_snesptr ebx
endm load_dir
load_dir_indir_index_y8 macro ; ( _tmp = *(word*)SNESMEM(D + OPBYTE) + (DBR << 16) + INDEX_Y, SNESMEM(_tmp) )
convert_dir
convert_snesptr ebx
mov ebx, [ebx]
and ebx, 0FFFFh
movzx eax, byte [_reg.Y]
or ebx, dword [_reg.DBR_2less]
add ebx, eax ; Indexes DO NOT wrap around on word boundaries, thank heaven
convert_snesptr ebx
endm load_dir_indir_index_y
load_dir_indir_index_y16 macro ; ( _tmp = *(word*)SNESMEM(D + OPBYTE) + (DBR << 16) + INDEX_Y, SNESMEM(_tmp) )
convert_dir
convert_snesptr ebx
mov ebx, [ebx]
and ebx, 0FFFFh
or ebx, dword [_reg.DBR_2less]
add ebx, dword [_reg.Y]
convert_snesptr ebx
endm load_dir_indir_index_y
load_dir_indir_long macro ; ( _tmp = (*(dword*)SNESMEM( (D + OPBYTE)&0xFFFF )) & 0xFFFFFF, SNESMEM (_tmp) )
convert_dir
convert_snesptr ebx
mov ebx, [ebx]
and ebx, 00FFFFFFh
convert_snesptr ebx
endm load_dir_indir_long
load_dir_indir_long_index_y8 macro ; ( _tmp = ((*(dword*)SNESMEM( (D+OPBYTE)&0xFFFF )) & 0xFFFFFF) + INDEX_Y, SNESMEM (_tmp) )
convert_dir
convert_snesptr ebx
mov ebx, [ebx]
and ebx, 00FFFFFFh
movzx eax, byte [_reg.Y]
add ebx, eax
convert_snesptr ebx
endm load_dir_indir_long_index_y8
load_dir_indir_long_index_y16 macro ; ( _tmp = ((*(dword*)SNESMEM( (D+OPBYTE)&0xFFFF )) & 0xFFFFFF) + INDEX_Y, SNESMEM (_tmp) )
convert_dir
convert_snesptr ebx
mov ebx, [ebx]
and ebx, 00FFFFFFh
add ebx, dword [_reg.Y]
convert_snesptr ebx
endm load_dir_indir_long_index_y16
load_dir_index_indir_x8 macro ; ( _tmp = *(word *)SNESMEM( (((D+OPBYTE)&0xFFFF) + INDEX_X) & 0xFFFF ) + (DBR << 16), SNESMEM (_tmp) )
convert_dir
movzx eax, byte [_reg.X]
add ebx, eax
convert_snesptr ebx
mov ebx, [ebx]
and ebx, 0FFFFh
or ebx, dword [_reg.DBR_2less]
convert_snesptr ebx
endm load_dir_index_indir_x8
load_dir_index_indir_x16 macro ; ( _tmp = *(word *)SNESMEM( (((D+OPBYTE)&0xFFFF) + INDEX_X) & 0xFFFF ) + (DBR << 16), SNESMEM (_tmp) )
convert_dir
add ebx, dword [_reg.X]
convert_snesptr ebx
mov ebx, [ebx]
and ebx, 0FFFFh
or ebx, dword [_reg.DBR_2less]
convert_snesptr ebx
endm load_dir_index_indir_x16
load_dir_index_x8 macro ; ( SNESMEM(D + OPBYTE + INDEX_X) )
convert_dir
xor eax, eax
mov al, byte [_reg.X]
add ebx, eax
convert_snesptr ebx
endm load_dir_index_x8
load_dir_index_x16 macro ; ( SNESMEM(D + OPBYTE + INDEX_X) )
convert_dir
add ebx, dword [_reg.X]
convert_snesptr ebx
endm load_dir_index_x16
load_dir_index_y8 macro ; ( SNESMEM(D + OPBYTE + INDEX_Y) )
convert_dir
xor eax, eax
mov al, byte [_reg.Y]
add ebx, eax
convert_snesptr ebx
endm load_dir_index_y8
load_dir_index_y16 macro ; ( SNESMEM(D + OPBYTE + INDEX_Y) )
convert_dir
add ebx, dword [_reg.Y]
convert_snesptr ebx
endm load_dir_index_y16
load_abs_index_x8 macro ; ( _tmp = (DBR << 16) + OPWORD + INDEX_X, SNESMEM(_tmp) )
convert_abs_data
xor eax, eax
mov al, byte [_reg.X]
add ebx, eax
convert_snesptr ebx
endm load_abs_index_x8
load_abs_index_x16 macro ; ( _tmp = (DBR << 16) + OPWORD + INDEX_X, SNESMEM(_tmp) )
convert_abs_data
add ebx, dword [_reg.X]
convert_snesptr ebx
endm load_abs_index_x16
load_abs_index_y8 macro ; ( _tmp = (DBR << 16) + OPWORD + INDEX_Y, SNESMEM(_tmp) )
convert_abs_data
xor eax, eax
mov al, byte [_reg.Y]
add ebx, eax
convert_snesptr ebx
endm load_abs_index_y8
load_abs_index_y16 macro ; ( _tmp = (DBR << 16) + OPWORD + INDEX_Y, SNESMEM(_tmp) )
convert_abs_data
add ebx, dword [_reg.Y]
convert_snesptr ebx
endm load_abs_index_y16
load_abs_long_index_x8 macro ; ( SNESMEM(OPLONG + INDEX_X) )
shr ebx, 8
xor eax, eax
mov al, byte [_reg.X]
add ebx, eax
convert_snesptr ebx
endm load_abs_long_index_x8
load_abs_long_index_x16 macro ; ( SNESMEM(OPLONG + INDEX_X) )
shr ebx, 8
add ebx, dword [_reg.X]
convert_snesptr ebx
endm load_abs_long_index_x16
load_abs_indir macro ; ( SNESMEM (OPWORD) ) Apparently you set PC=*ABS_INDIR
shr ebx, 8
and ebx, 0FFFFh
convert_snesptr ebx
endm load_abs_indir
load_dir_indir macro ; ( _tmp = *(word*)SNESMEM((D + OPBYTE) & 0xFFFF) + (DBR << 16), SNESMEM (_tmp) )
convert_dir
convert_snesptr ebx
mov ebx, [ebx]
and ebx, 0FFFFh
or ebx, dword [_reg.DBR_2less]
convert_snesptr ebx
endm load_dir_indir
load_abs_index_indir_x8_code macro ; ( _tmp = OPWORD + INDEX_X + (PC & 0xFF0000), *(word*)SNESMEM(_tmp) | (PC & 0xFF0000) )
shr ebx, 8
and ebx, 0FFFFh
mov eax, edx
and eax, 00FF0000h
mov al, byte [_reg.X]
add ebx, eax
convert_snesptr ebx ; EAX destroyed
mov ebx, [ebx]
mov eax, edx
and ebx, 0FFFFh
and eax, 00FF0000h ; PBR
or ebx, eax
; SET PC to this final value
endm load_abs_index_indir_x8
load_abs_index_indir_x16_code macro ; ( _tmp = OPWORD + INDEX_X + (PC & 0xFF0000), *(word*)SNESMEM(_tmp) | (PC & 0xFF0000) )
shr ebx, 8
and ebx, 0FFFFh
mov eax, edx
and eax, 00FF0000h
or eax, dword [_reg.X]
add ebx, eax
convert_snesptr ebx ; EAX destroyed
mov ebx, [ebx]
and ebx, 0FFFFh
mov eax, edx
and eax, 00FF0000h
or ebx, eax
; SET PC to this final value
endm load_abs_index_indir_x16
load_s_rel macro ; (SNESMEM (S + OPBYTE))
shr ebx, 8
and ebx, 0FFh
add ebx, dword [_reg.S]
convert_snesptr ebx
endm load_s_rel
load_s_rel_indir_index_y8 macro ; ( _tmp = *(word*)SNESMEM (S + OPBYTE) + (DBR << 16) + INDEX_Y, SNESMEM (_tmp) )
shr ebx, 8
and ebx, 0FFh
add ebx, dword [_reg.S]
convert_snesptr ebx
mov ebx, [ebx]
and ebx, 0FFFFh
or ebx, dword [_reg.DBR_2less]
xor eax, eax
mov al, byte [_reg.Y]
add ebx, eax
convert_snesptr ebx
endm load_s_rel_indir_index_y8
load_s_rel_indir_index_y16 macro ; ( _tmp = *(word*)SNESMEM (S + OPBYTE) + (DBR << 16) + INDEX_Y, SNESMEM (_tmp) )
shr ebx, 8
and ebx, 0FFh
add ebx, dword [_reg.S]
convert_snesptr ebx
mov ebx, [ebx]
and ebx, 0FFFFh
or ebx, dword [_reg.DBR_2less]
add ebx, dword [_reg.Y]
convert_snesptr ebx
endm load_s_rel_indir_index_y16
;****************************************************************************
; STACK PUSH/PULL MACROS
;****************************************************************************
; There don't seem to be any instances where edi and pushbyte are used in the same place...
pushbyte macro bytereg ; DESTROYS EDI and EAX!!! But not EBP, good since longcall uses EBP _and_ pushbyte
mov edi, dword [_reg.S]
convert_snesptr edi
mov byte [edi], bytereg
;dec word [_reg.S]
mov edi, dword [_reg.S]
dec edi
and edi, 0000FFFFh
mov dword [_reg.S], edi
endm pushbyte
pullbyte macro bytereg ; DESTROYS EDI and EAX and EBP!!!
inc_word [_reg.S]
mov edi, dword [_reg.S]
convert_snesptr edi
mov bytereg, byte [edi]
endm pushbyte
;****************************************************************************
; MISC. MACROS
;****************************************************************************
docount macro addtopc, subfromcycles
add edx, addtopc
sub esi, subfromcycles
endm docount
dobreak macro
ret
endm dobreak
dobreak_mxchange macro
pop eax ; Pop return address
jmp looptop_check
endm dobreak_mxchange
mainloop macro vectortable
@@:
mov ebx, edx ; Memory at PC
mov eax, ebx
shr eax, 11
and ebx, 01FFFh ; offset
and al, 0FCh
mov eax, [_startaddr+eax] ; base pointer
add ebx, eax ; add 'em
inc dword [_debug_instr]
mov ebx, [ebx]
movzx eax, bl
call [vectortable+eax*4]
test esi, esi
jns @B
jmp exitroutine
endm mainloop
; Workarounds for error in word handling in the assembler -- MODIFIES EBP!
dec_word macro what
mov ebp, dword what
dec ebp
and ebp, 0000FFFFh
mov dword what, ebp
endm dec_word
inc_word macro what
mov ebp, dword what
inc ebp
and ebp, 0000FFFFh
mov dword what, ebp
endm inc_word
;****************************************************************************
; INSTRUCTION MACROS
;****************************************************************************
vectorcall macro e_vector, not_e_vector
; Pushes PC and P, then changes PC to vector
mov ebx, edx
shr ebx, 8
pushbyte bh
pushbyte bl
pushbyte dl
pushbyte cl
mov edx, not_e_vector
or byte [_reg.E], 0
jz @F ; not emulation mode
mov edx, e_vector
@@: convert_snesptr edx
mov edx, [edx]
and edx, 0FFFFh
endm vectorcall
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -