📄 65c816.asm
字号:
%if 0
SNEeSe, an Open Source Super NES emulator.
Copyright (c) 1998-2004 Charles Bilyue'.
Portions Copyright (c) 2003-2004 Daniel Horchner.
This is free software. See 'LICENSE' for details.
You must read and accept the license prior to use.
%endif
; SNEeSe 65c816 CPU emulation core
; Originally written by Lee Hammerton in AT&T assembly
; Maintained/rewritten/ported to NASM by Charles Bilyue'
;
; Compile under NASM
; To do: move cycle addition into opcode handlers
; improved bus speed selection
;CPU instruction tracker is broken!
;DO NOT UNCOMMENT THE FOLLOWING LINE!
;%define TRACKERS 1048576
%define VERSION_NUMBER_5A22 2
;%define SINGLE_STEP
;%define WATCH_FLAG_BREAKS
;%define FAST_STACK_ACCESS_NATIVE_MODE
%define FAST_STACK_ACCESS_EMULATION_MODE
; This file contains:
; CPU core info
; Reset
; Execution Loop
; Invalid Opcode Handler
; Flag format conversion tables
; Variable definitions (registers, interrupt vectors, etc.)
; CPU opcode emulation handlers
; CPU opcode handler tables
; CPU opcode timing tables
;
; CPU core info:
; All general registers are now used in 65c816 emulation:
; EAX,EBX are used by the memory mapper;
; ECX is used to hold P register;
; EDX is used as memory mapper work register;
; EBP is used to hold scanline cycle counter;
; ESI is used by the opcode fetcher;
; EDI is used to hold base address to CPU register set.
;
; Accumulator - CPU_LABEL(A)
; X index - CPU_LABEL(X)
; Y index - CPU_LABEL(Y)
; Stack pointer - CPU_LABEL(S)
; Direct address - CPU_LABEL(D)
; Program Counter - CPU_LABEL(PC)
; Program Bank - CPU_LABEL(PB), CPU_LABEL(PB_Shifted)
; Data Bank - CPU_LABEL(DB), CPU_LABEL(DB_Shifted)
; Processor status - _P,_P_B,_P_W
; True x86 layout = |V|-|-|-|S|Z|-|A|-|-|-|C|
; Native mode 65816 layout = |E|N|V|M|X|D|I|Z|C|
; Emulation mode 65816 layout = |E|N|V|1|B|D|I|Z|C|
; Using |E|N|Z|I|D|X|M|V|C|
; |B|1|
;
; Identifiers/labels
; Identifiers beginning with "R_" are register aliases.
; Identifiers beginning with "B_" are 'based' data (1-byte offset) aliases.
; Identifiers beginning with "_" MAY BE local aliases for more
; complex global identifiers.
; FUTURE: Change prefix for complex identifier local aliases to "I_" or "L_".
; (Internal or Local)
; CPU timing
; According to Neill Corlett's SNES timing doc, main CPU clock is
; 21.47727MHz, 1360 clocks/scanline.
;
; SNES PPU timing and interrupt timing is specific to ROM country,
; set by ROM loader.
; 65c816 runs with many waitstates added in for bus access, bringing
; effective CPU speed between 2.68MHz (minimum) and 3.58MHz (maximum).
;
; Now adding base opcode cycle counts, *8 for SlowROM banks and *6 for
; FastROM banks. This is INACCURATE! But close enough for now.
;
; Core Flaws
; 'Fast' native mode stack is incorrect outside WRAM (0000-1FFF) - values
; read are undefined (no read is done) and values written are ignored:
; should fallback, disabled for now.
;
; SPC relative timing calculation (for parallel execution)
;
; SPC execution is on-demand, and CPU and SPC synchronize on
; certain CPU events and on CPU<>SPC communication. Executed CPU
; cycles are counted and depleted when SPC execution catches up.
;
;
%include "misc.inc"
%include "cycles.inc"
%include "cpu/cpumem.inc"
%include "ppu/screen.inc"
%include "cpu/regs.inc"
%include "ppu/ppu.inc"
%include "cpu/dma.inc"
EXTERN_C Map_Address,Map_Byte
EXTERN_C OLD_PB,OLD_PC
EXTERN_C RomAddress
EXTERN_C LastIns,InsAddress
EXTERN_C FRAME_SKIP_MIN,FRAME_SKIP_MAX,Timer_Counter_Throttle
EXTERN_C SNES_Screen8
EXTERN_C PaletteChanged
EXTERN_C BrightnessLevel
EXTERN_C Real_SNES_Palette,fixedpalettecheck,SetPalette
EXTERN_C ShowFPS,ShowBreaks
EXTERN_C Copy_Screen
EXTERN_C update_sound, update_sound_block
EXTERN_C SPC_ENABLED
EXTERN_C SPC_Cycles
EXTERN_C TotalCycles
EXTERN_C Wrap_SPC_Cyclecounter
EXTERN_C SPC_START
EXTERN_C SPC_CPU_cycle_divisor
EXTERN_C SPC_CPU_cycle_multiplicand
EXTERN_C InvalidOpcode,InvalidJump
EXTERN Invalidate_Tile_Caches
EXTERN_C Reset_CGRAM
%ifdef DEBUG
EXTERN_C Frames
;EXTERN_C Timer_Counter_FPS
%endif
section .text
EXPORT_C CPU_text_start
section .data
EXPORT_C CPU_data_start
section .bss
EXPORT_C CPU_bss_start
%define CPU_LABEL(x) C_LABEL(cpu_65c816_ %+ x)
%define R_Base R_65c816_Base ; Base pointer to register set
%define R_Cycles R_65c816_Cycles ; Cycle counter
%define R_PBPC R_65c816_PBPC
%define R_PC R_65c816_PC
; True 65816 layout, Native Mode = |E|N|V|M|X|D|I|Z|C|
; True 65816 layout, Emulation Mode = |E|N|V|1|B|D|I|Z|C|
; These are the bits for flag set/clr operations
; |E|N|Z|I|D|X|M|V|C|
; |B|1|
SNES_FLAG_C equ 1 ; Carry
SNES_FLAG_V equ 2 ; Overflow
SNES_FLAG_M equ 4 ; When E=0: Memory/accumulator operations 8-bit
SNES_FLAG_1 equ 4 ; When E=1: This bit is always set
SNES_FLAG_X equ 8 ; When E=0: Index registers 8-bit
SNES_FLAG_B equ 8 ; When E=1: Break (clear only on stack after IRQ/NMI)
SNES_FLAG_D equ 0x10 ; Decimal ADC/SBC mode
SNES_FLAG_I equ 0x20 ; Interrupt Disable
SNES_FLAG_Z equ 0x40 ; Zero result
SNES_FLAG_N equ 0x80 ; Negative result
SNES_FLAG_E equ 0x100 ; Emulation mode
SNES_FLAG_B1 equ (SNES_FLAG_B | SNES_FLAG_1)
SNES_FLAG_MX equ (SNES_FLAG_M | SNES_FLAG_X)
SNES_FLAG_NZ equ (SNES_FLAG_N | SNES_FLAG_Z)
SNES_FLAG_NZC equ (SNES_FLAG_NZ | SNES_FLAG_C)
REAL_SNES_FLAG_C equ 1 ; See descriptions above
REAL_SNES_FLAG_Z equ 2
REAL_SNES_FLAG_I equ 4
REAL_SNES_FLAG_D equ 8
REAL_SNES_FLAG_X equ 0x10
REAL_SNES_FLAG_B equ 0x10
REAL_SNES_FLAG_M equ 0x20
REAL_SNES_FLAG_1 equ 0x20
REAL_SNES_FLAG_V equ 0x40
REAL_SNES_FLAG_N equ 0x80
REAL_SNES_FLAG_E equ 0x100
section .bss
ALIGNB
%define B_IRQ_Nvector [R_Base-CPU_Register_Base+C_LABEL(IRQ_Nvector)]
%define B_NMI_Nvector [R_Base-CPU_Register_Base+C_LABEL(NMI_Nvector)]
%define B_BRK_Nvector [R_Base-CPU_Register_Base+C_LABEL(BRK_Nvector)]
%define B_COP_Nvector [R_Base-CPU_Register_Base+C_LABEL(COP_Nvector)]
%define B_IRQ_Evector [R_Base-CPU_Register_Base+C_LABEL(IRQ_Evector)]
%define B_NMI_Evector [R_Base-CPU_Register_Base+C_LABEL(NMI_Evector)]
%define B_COP_Evector [R_Base-CPU_Register_Base+C_LABEL(COP_Evector)]
%define B_RES_Evector [R_Base-CPU_Register_Base+C_LABEL(RES_Evector)]
%define B_PB_Shifted [R_Base-CPU_Register_Base+CPU_LABEL(PB_Shifted)]
%define B_PB [R_Base-CPU_Register_Base+CPU_LABEL(PB)]
%define B_PC [R_Base-CPU_Register_Base+CPU_LABEL(PC)]
%define B_P [R_Base-CPU_Register_Base+CPU_LABEL(P)]
%define B_SNES_Cycles [R_Base-CPU_Register_Base+C_LABEL(SNES_Cycles)]
%define B_EventTrip [R_Base-CPU_Register_Base+C_LABEL(EventTrip)]
%define B_A [R_Base-CPU_Register_Base+CPU_LABEL(A)]
%define B_B byte [R_Base-CPU_Register_Base+CPU_LABEL(B)]
%define B_X [R_Base-CPU_Register_Base+CPU_LABEL(X)]
%define B_XH byte [R_Base-CPU_Register_Base+CPU_LABEL(XH)]
%define B_Y [R_Base-CPU_Register_Base+CPU_LABEL(Y)]
%define B_YH byte [R_Base-CPU_Register_Base+CPU_LABEL(YH)]
%define B_D [R_Base-CPU_Register_Base+CPU_LABEL(D)]
%define B_DL byte [R_Base-CPU_Register_Base+CPU_LABEL(DL)]
%define B_DH byte [R_Base-CPU_Register_Base+CPU_LABEL(DH)]
%define B_S [R_Base-CPU_Register_Base+CPU_LABEL(S)]
%define B_SL byte [R_Base-CPU_Register_Base+CPU_LABEL(SL)]
%define B_SH byte [R_Base-CPU_Register_Base+CPU_LABEL(SH)]
%define B_DB_Shifted [R_Base-CPU_Register_Base+CPU_LABEL(DB_Shifted)]
%define B_DB [R_Base-CPU_Register_Base+CPU_LABEL(DB)]
%define B_OpTable [R_Base-CPU_Register_Base+OpTable]
%define B_FixedTrip [R_Base-CPU_Register_Base+FixedTrip]
%define B_SPC_last_cycles [R_Base-CPU_Register_Base+SPC_last_cycles]
%define B_SPC_CPU_cycles [R_Base-CPU_Register_Base+SPC_CPU_cycles]
%define B_SPC_cycles_left [R_Base-CPU_Register_Base+SPC_cycles_left]
%define B_SPC_CPU_cycles_mul [R_Base-CPU_Register_Base+SPC_CPU_cycles_mul]
%if 1
%define B_E_flag [R_Base-CPU_Register_Base+_E_flag]
%define B_N_flag [R_Base-CPU_Register_Base+_N_flag]
%define B_V_flag [R_Base-CPU_Register_Base+_V_flag]
%define B_M1_flag [R_Base-CPU_Register_Base+_M1_flag]
%define B_XB_flag [R_Base-CPU_Register_Base+_XB_flag]
%define B_D_flag [R_Base-CPU_Register_Base+_D_flag]
%define B_I_flag [R_Base-CPU_Register_Base+_I_flag]
%define B_Z_flag [R_Base-CPU_Register_Base+_Z_flag]
%define B_C_flag [R_Base-CPU_Register_Base+_C_flag]
%else
%define B_E_flag [_E_flag]
%define B_N_flag [_N_flag]
%define B_V_flag [_V_flag]
%define B_M1_flag [_M1_flag]
%define B_XB_flag [_XB_flag]
%define B_D_flag [_D_flag]
%define B_I_flag [_I_flag]
%define B_Z_flag [_Z_flag]
%define B_C_flag [_C_flag]
%endif
EXPORT_C IRQ_Nvector,skipl
EXPORT_C IRQ_Noffset,skipl
EXPORT_C NMI_Nvector,skipl
EXPORT_C NMI_Noffset,skipl
EXPORT_C BRK_Nvector,skipl
EXPORT_C BRK_Noffset,skipl
EXPORT_C COP_Nvector,skipl
EXPORT_C COP_Noffset,skipl
EXPORT_C IRQ_Evector,skipl
EXPORT_C IRQ_Eoffset,skipl
EXPORT_C NMI_Evector,skipl
EXPORT_C NMI_Eoffset,skipl
EXPORT_C COP_Evector,skipl
EXPORT_C COP_Eoffset,skipl
EXPORT_C RES_Evector,skipl
EXPORT_C RES_Eoffset,skipl
; v0.25 - New system for CPU timings... there are now ten paired tables,
; not five single tables. Each pair of tables has a 256-byte SlowROM
; table first, then a 256-byte FastROM table immediately following.
; The address of the current table pair is OpTable+0x400.
; v0.25 - OpTable holds pointer to current opcode emulation/timing tables,
; removes the need for multiple CPU loops.
CPU_Register_Base:
EXPORT CPU_LABEL(PB_Shifted),skipl ; Program Bank
EXPORT_EQU CPU_LABEL(PB),CPU_LABEL(PB_Shifted) + 2
EXPORT CPU_LABEL(PC),skipl ; Program Counter
EXPORT CPU_LABEL(P) ,skipl ; Processor status (flags)
EXPORT_C SNES_Cycles,skipl ; Scanline cycle count for CPU (0-EventTrip)
EXPORT_C EventTrip ,skipl ; Cycle of next event on this scanline
EXPORT CPU_LABEL(A) ,skipl ; Accumulator
EXPORT_EQU CPU_LABEL(B),CPU_LABEL(A)+1
EXPORT CPU_LABEL(X) ,skipl ; X and Y indices
EXPORT CPU_LABEL(Y) ,skipl
EXPORT_EQU CPU_LABEL(XH),CPU_LABEL(X)+1
EXPORT_EQU CPU_LABEL(YH),CPU_LABEL(Y)+1
EXPORT CPU_LABEL(D) ,skipl ; Direct address
EXPORT_EQU CPU_LABEL(DL),CPU_LABEL(D)
EXPORT_EQU CPU_LABEL(DH),CPU_LABEL(D)+1
EXPORT CPU_LABEL(S) ,skipl ; Stack pointer
EXPORT_EQU CPU_LABEL(SL),CPU_LABEL(S)
EXPORT_EQU CPU_LABEL(SH),CPU_LABEL(S)+1
EXPORT CPU_LABEL(DB_Shifted),skipl ; Data Bank
EXPORT_EQU CPU_LABEL(DB),CPU_LABEL(DB_Shifted) + 2
OpTable:skipl
EXPORT FixedTrip ,skipl ; Cycle of next fixed event on this scanline
EXPORT SPC_last_cycles ,skipl
EXPORT SPC_CPU_cycles ,skipl
EXPORT SPC_cycles_left ,skipl
EXPORT SPC_CPU_cycles_mul ,skipl
; For when I make the back color +/- hacks really do +/-
EXPORT RealColor0 ,skipl
_D_flag:skipb
_N_flag:skipb
_M1_flag:skipb
_XB_flag:skipb
_C_flag:skipb
_I_flag:skipb
EXPORT CPU_Execution_Mode,skipb
;CPU executing instructions normally
%define CEM_Normal_Execution 0
;CPU executing instruction immediately following one which
; caused an I-flag transition of high to low (CLI, PLP, RTI)
; The CPU will not acknowledge an IRQ until this instruction
; is completed.
%define CEM_Instruction_After_IRQ_Enable 1
;CPU in a state where no instructions are executed (this
; number and all above it)
%define CEM_Do_Not_Execute 2
;CPU is performing DMA transfer
%define CEM_In_DMA 2
;CPU is waiting for an interrupt after executing WAI opcode
%define CEM_Waiting_For_Interrupt 3
;CPU has stopped its clock after executing STP opcode
%define CEM_Clock_Stopped 4
EXPORT IRQ_pin ,skipb
_E_flag:skipb
_Z_flag:skipb
EXPORT In_CPU,skipb ; nonzero if CPU is executing
;NMI not raised
%define NMI_None 0
;NMI raised and acknowledged
%define NMI_Acknowledged 1
;NMI raised and not acknowledged
%define NMI_Raised 2
EXPORT NMI_pin ,skipb
EXPORT_C FPS_ENABLED ,skipb
_V_flag:skipb
EXPORT_C BREAKS_ENABLED ,skipb
section .data
ALIGND
OpTableE0:
dd C_LABEL(OpE0_0x00) ,C_LABEL(OpE0M0_0x01) ; 00
dd C_LABEL(OpE0_0x02) ,C_LABEL(OpM0_0x03)
dd C_LABEL(OpE0M0_0x04) ,C_LABEL(OpE0M0_0x05)
dd C_LABEL(OpE0M0_0x06) ,C_LABEL(OpE0M0_0x07)
dd C_LABEL(OpE0_0x08) ,C_LABEL(OpM0_0x09)
dd C_LABEL(OpM0_0x0A) ,C_LABEL(OpE0_0x0B)
dd C_LABEL(OpM0_0x0C) ,C_LABEL(OpM0_0x0D)
dd C_LABEL(OpM0_0x0E) ,C_LABEL(OpM0_0x0F)
dd C_LABEL(OpE0_0x10) ,C_LABEL(OpE0M0X0_0x11) ; 10
dd C_LABEL(OpE0M0_0x12) ,C_LABEL(OpM0_0x13)
dd C_LABEL(OpE0M0_0x14) ,C_LABEL(OpE0M0_0x15)
dd C_LABEL(OpE0M0_0x16) ,C_LABEL(OpE0M0_0x17)
dd C_LABEL(Op_0x18) ,C_LABEL(OpM0X0_0x19)
dd C_LABEL(OpM0_0x1A) ,C_LABEL(OpE0_0x1B)
dd C_LABEL(OpM0_0x1C) ,C_LABEL(OpM0X0_0x1D)
dd C_LABEL(OpM0_0x1E) ,C_LABEL(OpM0_0x1F)
dd C_LABEL(OpE0_0x20) ,C_LABEL(OpE0M0_0x21) ; 20
dd C_LABEL(OpE0_0x22) ,C_LABEL(OpM0_0x23)
dd C_LABEL(OpE0M0_0x24) ,C_LABEL(OpE0M0_0x25)
dd C_LABEL(OpE0M0_0x26) ,C_LABEL(OpE0M0_0x27)
dd C_LABEL(OpE0_0x28) ,C_LABEL(OpM0_0x29)
dd C_LABEL(OpM0_0x2A) ,C_LABEL(OpE0_0x2B)
dd C_LABEL(OpM0_0x2C) ,C_LABEL(OpM0_0x2D)
dd C_LABEL(OpM0_0x2E) ,C_LABEL(OpM0_0x2F)
dd C_LABEL(OpE0_0x30) ,C_LABEL(OpE0M0X0_0x31) ; 30
dd C_LABEL(OpE0M0_0x32) ,C_LABEL(OpM0_0x33)
dd C_LABEL(OpE0M0_0x34) ,C_LABEL(OpE0M0_0x35)
dd C_LABEL(OpE0M0_0x36) ,C_LABEL(OpE0M0_0x37)
dd C_LABEL(Op_0x38) ,C_LABEL(OpM0X0_0x39)
dd C_LABEL(OpM0_0x3A) ,C_LABEL(Op_0x3B)
dd C_LABEL(OpM0X0_0x3C) ,C_LABEL(OpM0X0_0x3D)
dd C_LABEL(OpM0_0x3E) ,C_LABEL(OpM0_0x3F)
dd C_LABEL(OpE0_0x40) ,C_LABEL(OpE0M0_0x41) ; 40
dd C_LABEL(ALL_INVALID) ,C_LABEL(OpM0_0x43)
dd C_LABEL(OpX0_0x44) ,C_LABEL(OpE0M0_0x45)
dd C_LABEL(OpE0M0_0x46) ,C_LABEL(OpE0M0_0x47)
dd C_LABEL(OpE0M0_0x48) ,C_LABEL(OpM0_0x49)
dd C_LABEL(OpM0_0x4A) ,C_LABEL(OpE0_0x4B)
dd C_LABEL(Op_0x4C) ,C_LABEL(OpM0_0x4D)
dd C_LABEL(OpM0_0x4E) ,C_LABEL(OpM0_0x4F)
dd C_LABEL(OpE0_0x50) ,C_LABEL(OpE0M0X0_0x51) ; 50
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -