⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 spc700.asm

📁 NES game Emulator in Linux.c and asm codes.
💻 ASM
📖 第 1 页 / 共 3 页
字号:
%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 SPC700 CPU emulation core
; Originally written by Lee Hammerton in AT&T assembly
; Maintained/rewritten/ported to NASM by Charles Bilyue'
;
; Compile under NASM
; This code assumes preservation of ebx, ebp, esi, edi in C/C++ calls

;%define TRACKERS 1048576
;%define WATCH_SPC_BREAKS
;%define LOG_SOUND_DSP_READ
;%define LOG_SOUND_DSP_WRITE
;%define TRAP_INVALID_READ
;%define TRAP_INVALID_WRITE

%define UPDATE_SOUND_ON_RAM_WRITE

; 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 table
;  CPU opcode timing table
;
; CPU core info:
;  Nearly all general registers are now used in SPC700 emulation:
;   EAX,EBX are used by the memory mapper;
;   EDX is used as memory mapper work register;
;   EBP is used to hold cycle counter;
;   ESI is used by the opcode fetcher;
;   EDI is used as CPU work register.
;
;    A register              - _A
;    Y register              - _Y
;    YA register pair        - _YA
;    X register              - _X
;    Stack pointer           - _SP
;    Program Counter         - _PC
;    Processor status word   - _PSW
;       True x86 layout = |V|-|-|-|S|Z|-|A|-|-|-|C|
;    True SPC700 layout =         |N|V|P|B|H|I|Z|C|
;                   Using         |N|Z|P|H|B|I|V|C|
;
; SPC timers
;  SPC700 timing is not directly related to 65c816 timing, but for
;   simplicity in emulation we act as if it is. SPC gets 11264 cycles
;   for every 118125 (21.47727..MHz) 65c816 cycles. Since the timers
;   run at ~8KHz and ~64KHz and the main chip runs at 2.048Mhz, the
;   timers are clocked as follows:
;    2.048MHz / 8KHz  = 256 cycles    (Timers 0 and 1)
;    2.048MHz / 64KHz = 32  cycles    (Timer 2)
;
;

%define SNEeSe_SPC700_asm

%include "misc.inc"
%include "apu/spc.inc"
%include "apu/regs.inc"
%include "ppu/ppu.inc"

EXTERN_C SPC_CPU_cycle_multiplicand,SPC_CPU_cycle_divisor
EXTERN SPC_CPU_cycles_mul,SPC_CPU_cycles
EXTERN_C sound_cycle_latch
EXTERN_C SPC_DSP
EXTERN_C SPC_DSP_DATA
EXTERN_C SPC_READ_DSP,SPC_WRITE_DSP
EXTERN_C Update_SPC_Timer_0,Update_SPC_Timer_1,Update_SPC_Timer_2
EXTERN_C Wrap_SPC_Cyclecounter
EXTERN_C Map_Byte,Map_Address

EXTERN_C SNES_Cycles,EventTrip
EXTERN SPC_last_cycles,In_CPU
EXTERN_C DisplaySPC

section .text
EXPORT_C SPC_text_start
section .data
EXPORT_C SPC_data_start
section .bss
EXPORT_C SPC_bss_start

%define SPC_CTRL 0xF1
%define SPC_DSP_ADDR 0xF2

; These are the bits for flag set/clr operations
SPC_FLAG_C equ 1    ; Carry
SPC_FLAG_V equ 2    ; Overflow
SPC_FLAG_I equ 4    ; Interrupt Disable
SPC_FLAG_B equ 8    ; Break
SPC_FLAG_H equ 0x10 ; Half-carry
SPC_FLAG_P equ 0x20 ; Page (direct page)
SPC_FLAG_Z equ 0x40 ; Zero result
SPC_FLAG_N equ 0x80 ; Negative result

SPC_FLAG_NZ equ (SPC_FLAG_N | SPC_FLAG_Z)
SPC_FLAG_NZC equ (SPC_FLAG_NZ | SPC_FLAG_C)
SPC_FLAG_NHZC equ (SPC_FLAG_NZC | SPC_FLAG_H)

REAL_SPC_FLAG_C equ 1       ; Carry
REAL_SPC_FLAG_Z equ 2       ; Zero result
REAL_SPC_FLAG_I equ 4       ; Interrupt Disable
REAL_SPC_FLAG_H equ 8       ; Half-carry
REAL_SPC_FLAG_B equ 0x10    ; Break
REAL_SPC_FLAG_P equ 0x20    ; Page (direct page)
REAL_SPC_FLAG_V equ 0x40    ; Overflow
REAL_SPC_FLAG_N equ 0x80    ; Negative result

%define _PSW C_LABEL(_SPC_PSW)
%define _YA C_LABEL(_SPC_YA)
%define _A  C_LABEL(_SPC_A)
%define _Y  C_LABEL(_SPC_Y)
%define _X  C_LABEL(_SPC_X)
%define _SP C_LABEL(_SPC_SP)
%define _PC C_LABEL(_SPC_PC)

%define R_Base       R_SPC700_Base
%define R_Cycles     R_SPC700_Cycles
%define R_NativePC   R_SPC700_NativePC


%define B_SPC_Code_Base     [R_Base-SPC_Register_Base+SPC_Code_Base]
%define B_PC                [R_Base-SPC_Register_Base+_PC]
%define B_YA                [R_Base-SPC_Register_Base+_YA]
%define B_A                 [R_Base-SPC_Register_Base+_A]
%define B_Y                 [R_Base-SPC_Register_Base+_Y]
%define B_SPC_PAGE          [R_Base-SPC_Register_Base+SPC_PAGE]
%define B_SPC_PAGE_H        byte [R_Base-SPC_Register_Base+SPC_PAGE_H]
%define B_SP                [R_Base-SPC_Register_Base+_SP]
%define B_SPC_Cycles        [R_Base-SPC_Register_Base+C_LABEL(SPC_Cycles)]
%define B_PSW               [R_Base-SPC_Register_Base+_PSW]
%define B_X                 [R_Base-SPC_Register_Base+_X]

%define B_N_flag            [R_Base-SPC_Register_Base+_N_flag]
%define B_V_flag            [R_Base-SPC_Register_Base+_V_flag]
%define B_P_flag            [R_Base-SPC_Register_Base+_P_flag]
%define B_H_flag            [R_Base-SPC_Register_Base+_H_flag]
%define B_Z_flag            [R_Base-SPC_Register_Base+_Z_flag]
%define B_I_flag            [R_Base-SPC_Register_Base+_I_flag]
%define B_B_flag            [R_Base-SPC_Register_Base+_B_flag]
%define B_C_flag            [R_Base-SPC_Register_Base+_C_flag]

%define B_SPC_PORT0R        [R_Base-SPC_Register_Base+C_LABEL(SPC_PORT0R)]
%define B_SPC_PORT1R        [R_Base-SPC_Register_Base+C_LABEL(SPC_PORT1R)]
%define B_SPC_PORT2R        [R_Base-SPC_Register_Base+C_LABEL(SPC_PORT2R)]
%define B_SPC_PORT3R        [R_Base-SPC_Register_Base+C_LABEL(SPC_PORT3R)]
%define B_SPC_PORT0W        [R_Base-SPC_Register_Base+C_LABEL(SPC_PORT0W)]
%define B_SPC_PORT1W        [R_Base-SPC_Register_Base+C_LABEL(SPC_PORT1W)]
%define B_SPC_PORT2W        [R_Base-SPC_Register_Base+C_LABEL(SPC_PORT2W)]
%define B_SPC_PORT3W        [R_Base-SPC_Register_Base+C_LABEL(SPC_PORT3W)]
%ifdef DEBUG
%define B_SPC_TEMP_ADD      [R_Base-SPC_Register_Base+SPC_TEMP_ADD]
%endif

; Load cycle counter to register R_Cycles
%macro LOAD_CYCLES 0
 mov eax,[C_LABEL(SPC_Cycles)]
 mov dword R_Cycles,[C_LABEL(TotalCycles)]
 sub dword R_Cycles,eax
%endmacro

; Get cycle counter to register argument
%macro GET_CYCLES 1
 mov dword %1,[C_LABEL(SPC_Cycles)]
 add dword %1,R_Cycles
%endmacro

; Save register R_Cycles to cycle counter
%macro SAVE_CYCLES 0
 GET_CYCLES R_SPC700_MemMap_Trash
 mov [C_LABEL(TotalCycles)],R_SPC700_MemMap_Trash
%endmacro

; Load base pointer to CPU register set
%macro LOAD_BASE 0
 mov dword R_Base,SPC_Register_Base
%endmacro

; Load register R_NativePC with pointer to code at PC
%macro LOAD_PC 0
 mov dword R_NativePC,[SPC_Code_Base]
 add dword R_NativePC,[_PC]
%endmacro

; Get PC from register R_NativePC
;%1 = with
%macro GET_PC 1
%ifnidn %1,R_NativePC
 mov dword %1,R_NativePC
%endif
 sub dword %1,[SPC_Code_Base]
%endmacro

; Save PC from register R_NativePC
;%1 = with
%macro SAVE_PC 1
 GET_PC %1
 mov dword [_PC],%1
%endmacro

; Set up the flags from PC flag format to SPC flag format
; Corrupts arg 2, returns value in arg 3 (default to cl, al)
;%1 = break flag, %2 = scratchpad, %3 = output
%macro SETUPFLAGS_SPC 0-3 1,cl,al
;%macro Flags_Native_to_SPC 0-3 1,cl,al
 mov byte %3,B_N_flag
 shr byte %3,7

 mov byte %2,B_V_flag
 add byte %2,-1
 adc byte %3,%3

 mov byte %2,B_P_flag
 add byte %2,-1
 adc byte %3,%3

 mov byte %2,B_H_flag
 add byte %3,%3
%if %1 != 0
 inc byte %3
%endif

 shl byte %2,4
 adc byte %3,%3

 mov byte %2,B_I_flag
 add byte %2,-1
 adc byte %3,%3

 mov byte %2,B_Z_flag
 cmp byte %2,1
 adc byte %3,%3

 mov byte %2,B_C_flag
 add byte %2,-1
 adc byte %3,%3
%endmacro

; Restore the flags from SPC flag format to PC flag format
; Corrupts arg 2, returns value in arg 3 (default to cl, al)
;%1 = break flag, %2 = scratchpad, %3 = input
%macro RESTOREFLAGS_SPC 0-3 1,cl,al
;%macro Flags_SPC_to_Native 0-3 1,cl,al
 mov byte B_N_flag,%3   ;negative
 shl byte %3,2  ;start next (overflow)

 sbb byte %2,%2
 add byte %3,%3 ;start next (direct page)
 mov byte B_V_flag,%2

 mov byte %2,0
 adc byte %2,%2
 add byte %3,%3 ;start next (break flag, ignore)
 mov byte B_P_flag,%2
 add byte %3,%3 ;start next (half-carry)
 mov byte B_SPC_PAGE_H,%2

 sbb byte %2,%2
 mov byte B_B_flag,%1

;and byte %2,0x10
 add byte %3,%3 ;start next (interrupt enable)
 mov byte B_H_flag,%2

 sbb byte %2,%2
 add byte %3,%3 ;start next (zero)
 mov byte B_I_flag,%2

 sbb byte %2,%2
 xor byte %2,0xFF
 add byte %3,%3 ;start next (carry)
 mov byte B_Z_flag,%2

 sbb byte %2,%2
 mov byte B_C_flag,%2
%endmacro


; SPC MEMORY MAPPER IS PLACED HERE (ITS SIMPLER THAN THE CPU ONE!)

; bx - contains the actual address, al is where the info should be stored, edx is free
; NB bx is not corrupted! edx is corrupted!
; NB eax is not corrupted barring returnvalue in al... e.g. ah should not be used etc!

section .text
%macro OPCODE_EPILOG 0
%if 0
 test R_Cycles,R_Cycles
 jle SPC_START_NEXT
 jmp SPC_OUT
%else
 jmp SPC_RETURN
%endif
%endmacro

ALIGNC
EXPORT_C SPC_READ_MAPPER
;and ebx,0xFFFF
 test bh,bh
 jz C_LABEL(SPC_READ_ZERO_PAGE)

 cmp ebx,0xFFC0
 jae C_LABEL(SPC_READ_RAM_ROM)
EXPORT_C SPC_READ_RAM
 mov al,[C_LABEL(SPCRAM)+ebx]
 ret

ALIGNC
EXPORT_C SPC_READ_RAM_ROM
 mov edx,[SPC_FFC0_Address]
 mov al,[ebx + edx]
 ret

ALIGNC
EXPORT_C SPC_READ_ZERO_PAGE
 cmp bl,0xF0
 jb C_LABEL(SPC_READ_RAM)

EXPORT_C SPC_READ_FUNC
%ifdef LOG_SOUND_DSP_READ
 SAVE_PC edx
%endif
 SAVE_CYCLES    ; Set cycle counter
 jmp dword [Read_Func_Map - 0xF0 * 4 + ebx * 4]

ALIGNC
EXPORT_C SPC_READ_INVALID
 mov al,0xFF    ; v0.15
%ifdef TRAP_INVALID_READ
%ifdef DEBUG
EXTERN_C InvalidSPCHWRead
;and ebx,0xFFFF
 mov [C_LABEL(Map_Address)],ebx ; Set up Map Address so message works!
 mov [C_LABEL(Map_Byte)],al ; Set up Map Byte so message works

 pusha
 call _InvalidSPCHWRead ; Display read from invalid HW warning
 popa
%endif
%endif
 ret

;   --------

EXPORT_C SPC_WRITE_MAPPER
;and ebx,0xFFFF
 test bh,bh
 jz C_LABEL(SPC_WRITE_ZERO_PAGE)

EXPORT_C SPC_WRITE_RAM
%ifdef UPDATE_SOUND_ON_RAM_WRITE
 push ecx
;push edx
 push eax
 SAVE_CYCLES    ; Set cycle counter
EXTERN_C update_sound
 call C_LABEL(update_sound)
 pop eax
;pop edx
 pop ecx
%endif
 mov [C_LABEL(SPCRAM)+ebx],al
 ret

ALIGNC
EXPORT_C SPC_WRITE_ZERO_PAGE
 cmp bl,0xF0
 jb C_LABEL(SPC_WRITE_RAM)

EXPORT_C SPC_WRITE_FUNC
%ifdef LOG_SOUND_DSP_WRITE
 SAVE_PC edx
%endif
 SAVE_CYCLES    ; Set cycle counter
 jmp dword [Write_Func_Map - 0xF0 * 4 + ebx * 4]

EXPORT_C SPC_WRITE_INVALID
%ifdef TRAP_INVALID_WRITE
%ifdef DEBUG
EXTERN_C InvalidSPCHWWrite
;and ebx,0xFFFF
 mov [C_LABEL(Map_Address)],ebx ; Set up Map Address so message works!
 mov [C_LABEL(Map_Byte)],al ; Set up Map Byte so message works

 pusha
 call _InvalidSPCHWWrite    ; Display write to invalid HW warning
 popa
%endif
%endif
 ret

; GET_BYTE & GET_WORD now assume ebx contains the read address and 
; eax the place to store value also, corrupts edx

%macro GET_BYTE_SPC 0
;call _SPC_READ_MAPPER
 cmp ebx,0xFFC0
 jnb %%read_mapper

 test bh,bh
 jnz %%read_direct

 cmp bl,0xF0
 jb %%read_direct
 call C_LABEL(SPC_READ_FUNC)
 jmp %%done
%%read_mapper:
 call C_LABEL(SPC_READ_RAM_ROM)
 jmp %%done
%%read_direct:
 mov al,[C_LABEL(SPCRAM)+ebx]
%%done:
%endmacro

%macro GET_WORD_SPC 0
 cmp ebx,0xFFC0-1
 jnb %%read_mapper

 test bh,bh
 jnz %%read_direct

 cmp bl,0xF0-1
 jb %%read_direct
 je %%read_mapper

 cmp bl,0xFF
 je %%read_mapper

 call C_LABEL(SPC_READ_FUNC)
 mov ah,al
 inc ebx
 call C_LABEL(SPC_READ_FUNC)
 ror ax,8
 jmp %%done
%%read_mapper:
 call SPC_GET_WORD
 jmp %%done
%%read_direct:
 mov ax,[C_LABEL(SPCRAM)+ebx]
 inc ebx
%%done:
%endmacro

; SET_BYTE & SET_WORD now assume ebx contains the write address and 
; eax the value to write, corrupts edx

%macro SET_BYTE_SPC 0
%ifdef UPDATE_SOUND_ON_RAM_WRITE
 call C_LABEL(SPC_WRITE_MAPPER)
%else
 test bh,bh
 jnz %%write_direct
 cmp bl,0xF0
 jb %%write_direct
 call C_LABEL(SPC_WRITE_FUNC)
 jmp %%done
%%write_direct:
 mov [C_LABEL(SPCRAM)+ebx],al
%%done:
%endif
%endmacro

%macro SET_WORD_SPC 0
 SET_BYTE_SPC
 mov al,ah
 inc bx
 SET_BYTE_SPC
%endmacro

; Push / Pop Macros assume eax contains value - corrupt ebx,edx
%macro PUSH_B 0         ; Push Byte (SP--)
 mov ebx,B_SP
 mov [C_LABEL(SPCRAM)+ebx],al   ; Store data on stack
 dec ebx
 mov B_SP,bl            ; Decrement S (Byte)
%endmacro

%macro POP_B 0          ; Pop Byte (++SP)
 mov ebx,B_SP
 inc bl
 mov B_SP,bl
 mov al,[C_LABEL(SPCRAM)+ebx]   ; Fetch data from stack
%endmacro

%macro PUSH_W 0         ; Push Word (SP--)
 mov ebx,B_SP
 mov [C_LABEL(SPCRAM)+ebx],ah   ; Store data on stack
 mov [C_LABEL(SPCRAM)+ebx-1],al ; Store data on stack
 sub bl,2
 mov B_SP,bl            ; Postdecrement SP
%endmacro

%macro POP_W 0          ; Pop Word (++SP)
 mov ebx,B_SP
 add bl,2               ; Preincrement SP
 mov B_SP,bl
 mov ah,[C_LABEL(SPCRAM)+ebx]   ; Fetch data from stack
 mov al,[C_LABEL(SPCRAM)+ebx-1] ; Fetch data from stack
%endmacro

; --- Ease up on the finger cramps ;-)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -