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

📄 spc700.asm

📁 NES game Emulator in Linux.c and asm codes.
💻 ASM
📖 第 1 页 / 共 3 页
字号:
.set_write_loop:
 mov [ebx],eax
 mov [ebx+1*4],edx
 mov [ebx+2*4],esi
 mov [ebx+3*4],edi
 add ebx,byte 4*4
 dec cl
 jnz .set_write_loop

 popa
 ret

ALIGNC
EXPORT_C Reset_SPC
 pusha

 ; Get ROM reset vector and setup Program Counter
 movzx eax,word [C_LABEL(SPC_ROM_CODE)+(0xFFFE-0xFFC0)]
 mov [_PC],eax

 mov eax,0  ;[C_LABEL(SNES_Cycles)]
 mov [SPC_last_cycles],eax

 ; Reset the sound DSP registers
 mov [C_LABEL(SPC_Cycles)],eax  ; Clear Cycle Count
 mov [C_LABEL(TotalCycles)],eax
 mov [SPC_PAGE],eax     ; Used to save looking up P flag for Direct page stuff!
 mov dword [_SP],0x01EF ; Reset registers
 mov [_YA],eax
 mov [_X],al
 mov [_PSW],al          ; Clear Flags Register
 mov [_N_flag],al       ; Clear Flags Register
 mov byte [_Z_flag],1
 mov [_H_flag],al
 mov [_V_flag],al
 mov [_I_flag],al
 mov [_P_flag],al
 mov [_B_flag],al
 mov [_C_flag],al

 mov byte [C_LABEL(SPCRAM)+SPC_CTRL],0x80
 mov dword [SPC_FFC0_Address],C_LABEL(SPC_ROM_CODE)-0xFFC0
 mov dword [SPC_Code_Base],C_LABEL(SPC_ROM_CODE)-0xFFC0

 ; Reset timers
 mov [C_LABEL(SPC_T0_counter)],al
 mov [C_LABEL(SPC_T1_counter)],al
 mov [C_LABEL(SPC_T2_counter)],al
 mov word [C_LABEL(SPC_T0_target)],256
 mov word [C_LABEL(SPC_T1_target)],256
 mov word [C_LABEL(SPC_T2_target)],256
 mov [C_LABEL(SPC_T0_position)],ax
 mov [C_LABEL(SPC_T1_position)],ax
 mov [C_LABEL(SPC_T2_position)],ax
 mov [C_LABEL(SPC_T0_cycle_latch)],eax
 mov [C_LABEL(SPC_T1_cycle_latch)],eax
 mov [C_LABEL(SPC_T2_cycle_latch)],eax
 mov [C_LABEL(sound_cycle_latch)],eax

 ; Reset SPC700 output ports
 mov [C_LABEL(SPC_PORT0W)],al
 mov [C_LABEL(SPC_PORT1W)],al
 mov [C_LABEL(SPC_PORT2W)],al
 mov [C_LABEL(SPC_PORT3W)],al

 ; Reset SPC700 input ports
 mov [C_LABEL(SPC_PORT0R)],al
 mov [C_LABEL(SPC_PORT1R)],al
 mov [C_LABEL(SPC_PORT2R)],al
 mov [C_LABEL(SPC_PORT3R)],al

 ; Reset sound DSP port address
 mov [C_LABEL(SPC_DSP)+SPC_DSP_ADDR],al
 mov [C_LABEL(SPC_DSP_DATA)],eax

 popa
 ret

SPC_SHOW_REGISTERS:
 pusha
 call C_LABEL(DisplaySPC)
 popa
 ret

ALIGNC
EXPORT_C get_SPC_PSW
 push dword R_Base
 LOAD_BASE
 SETUPFLAGS_SPC
 pop dword R_Base
 ret

ALIGNC
SPC_GET_WORD:
 GET_BYTE_SPC
 mov ah,al
 inc bx
 GET_BYTE_SPC
 ror ax,8
 ret

ALIGNC
EXPORT_C SPC_START
%ifdef WATCH_SPC_BREAKS
EXTERN_C BreaksLast
 inc dword [C_LABEL(BreaksLast)]
%endif

 mov al,[In_CPU]
 push eax
 mov byte [In_CPU],0

 LOAD_CYCLES
 LOAD_PC
 LOAD_BASE
 xor eax,eax
 jmp SPC_START_NEXT

ALIGNC
SPC_RETURN:
;cmp R_Base,SPC_Register_Base
;jne 0b

%ifdef DEBUG
;mov ebx,[SPC_TEMP_ADD]
;mov [_OLD_SPC_ADDRESS],ebx
%endif
 test R_Cycles,R_Cycles
 jg SPC_OUT             ; Do another instruction if cycles left

SPC_START_NEXT:

; This code is for a SPC-tracker dump... #define TRACKERS to make a dump
; of the CPU state before each instruction - uncomment the calls to
; _Wangle__Fv and _exit to force the emulator to exit when the buffer
; fills. TRACKERS must be defined to the size of the buffer to be used -
; which must be a power of two, and the variables required by this and the
; write in Wangle() (romload.cc) exist only if DEBUG and SPCTRACKERS are
; also defined in romload.cc.
%ifdef TRACKERS
EXTERN_C SPC_LastIns
EXTERN_C SPC_InsAddress
EXTERN_C Wangle__Fv
EXTERN_C exit
 mov edx,[_SPC_LastIns]     ;
 add edx,[_SPC_InsAddress]  ;
 SAVE_PC eax                ;
 mov [edx],ah               ;
 mov [1+edx],al             ;
 mov al,[_A]                ;
 mov [2+edx],al             ;
 mov al,[_X]                ;
 mov [3+edx],al             ;
 mov al,[_Y]                ;
 mov [4+edx],al             ;
 mov al,[_SP]               ;
 mov [5+edx],al             ;
 SETUPFLAGS_SPC             ;
 mov [6+edx],al             ;

 mov al,[esi]               ;
 mov [7+edx],al             ;
 mov eax,[1+esi]            ;
 mov [8+edx],eax            ;
 mov eax,[5+esi]            ;
 mov [12+edx],eax           ;

 mov edx,[_SPC_LastIns]     ;
 add edx,byte 16            ;
 and edx,(TRACKERS-1)       ;
 mov [_SPC_LastIns],edx     ;
 test edx,edx               ;
 jnz .buffer_not_full       ;
 call _Wangle__Fv           ;
 jmp C_LABEL(exit)          ;
                            ;
.buffer_not_full:           ;
 xor eax,eax                ;
%endif

;mov ebx,[_PC]          ; PC now setup
;mov R_NativePC,[SPC_Code_Base]
;add R_NativePC,ebx
%ifdef DEBUG
;mov [SPC_TEMP_ADD],ebx
%endif

 xor eax,eax
 mov al,[R_NativePC]    ; Fetch opcode
 xor ebx,ebx
 mov bl,[SPCCycleTable+eax]
 add R_Cycles,ebx               ; Update cycle counter
 jmp dword [SPCOpTable+eax*4]   ; jmp to opcode handler

ALIGNC
SPC_OUT:
 SAVE_PC R_NativePC
 SAVE_CYCLES    ; Set cycle counter

%ifdef INDEPENDENT_SPC
 ; Update SPC timers to prevent overflow
 Update_SPC_Timer 0
 Update_SPC_Timer 1
 Update_SPC_Timer 2
%endif

 pop eax
 mov [In_CPU],al
 ret                    ; Return to CPU emulation

%include "apu/spcaddr.inc"  ; Include addressing mode macros
%include "apu/spcmacro.inc" ; Include instruction macros

EXPORT_C spc_ops_start

ALIGNC
EXPORT_C SPC_INVALID
 mov [C_LABEL(Map_Byte)],al ; al contains opcode!

 SAVE_PC R_NativePC
 SAVE_CYCLES    ; Set cycle counter

 mov eax,[_PC]          ; Adjust address to correct for pre-increment
 mov [C_LABEL(Map_Address)],eax ; this just sets the error output up correctly!

EXTERN_C InvalidSPCOpcode
 jmp C_LABEL(InvalidSPCOpcode)  ; This exits.. avoids conflict with other things!

ALIGNC
EXPORT_C SPC_SET1
 shr eax,5
 mov ebx,B_SPC_PAGE
 mov bl,[1+R_NativePC]
 add R_NativePC,byte 2
 mov ah,[offset_to_bit+eax]
 GET_BYTE_SPC     
 or al,ah
 SET_BYTE_SPC
 OPCODE_EPILOG

ALIGNC
EXPORT_C SPC_CLR1
 shr eax,5
 mov ebx,B_SPC_PAGE
 mov bl,[1+R_NativePC]
 add R_NativePC,byte 2
 mov ah,[offset_to_not+eax]
 GET_BYTE_SPC     
 and al,ah
 SET_BYTE_SPC
 OPCODE_EPILOG

ALIGNC
EXPORT_C SPC_BBS
 shr eax,5
 mov ebx,B_SPC_PAGE
 mov bl,[1+R_NativePC]
 add R_NativePC,byte 3
 mov ah,[offset_to_bit+eax]
 GET_BYTE_SPC
 test al,ah
 jz SPC_RETURN
 movsx eax,byte [-1+R_NativePC]
 short_branch
 OPCODE_EPILOG

ALIGNC
EXPORT_C SPC_BBC
 shr eax,5
 mov ebx,B_SPC_PAGE
 mov bl,[1+R_NativePC]
 add R_NativePC,byte 3
 mov ah,[offset_to_bit+eax]
 GET_BYTE_SPC         
 test al,ah
 jnz SPC_RETURN
 movsx eax,byte [-1+R_NativePC]
 short_branch
 OPCODE_EPILOG

%include "apu/spcops.inc"   ; Include opcodes

EXPORT_C SPC_READ_CTRL
EXPORT_C SPC_READ_DSP_ADDR
 mov al,[C_LABEL(SPCRAM)+ebx]
 ret

EXPORT_C SPC_READ_DSP_DATA
 push ecx
;push edx
 push eax
 call C_LABEL(SPC_READ_DSP)
 xor ecx,ecx
 mov cl,[C_LABEL(SPCRAM)+SPC_DSP_ADDR]
 pop eax
;pop edx
 mov al,[C_LABEL(SPC_DSP)+ecx]  ; read from DSP register
 pop ecx
 ret

EXPORT_C SPC_READ_PORT0R
 mov al,[C_LABEL(SPC_PORT0R)]
 ret
EXPORT_C SPC_READ_PORT1R
 mov al,[C_LABEL(SPC_PORT1R)]
 ret
EXPORT_C SPC_READ_PORT2R
 mov al,[C_LABEL(SPC_PORT2R)]
 ret
EXPORT_C SPC_READ_PORT3R
 mov al,[C_LABEL(SPC_PORT3R)]
 ret

; WOOPS... TIMER registers are write only, the actual timer clock is internal not accessible!

; COUNTERS ARE 4 BIT, upon read they reset to 0 status

EXPORT_C SPC_READ_COUNTER_0
 push ecx
;push edx
 push eax
 Update_SPC_Timer 0
;call C_LABEL(Update_SPC_Timer_0)
 pop eax
;pop edx
 pop ecx
 mov al,[C_LABEL(SPC_T0_counter)]
 mov [C_LABEL(SPC_T0_counter)],bh
 ret

EXPORT_C SPC_READ_COUNTER_1
 push ecx
;push edx
 push eax
 Update_SPC_Timer 1
;call C_LABEL(Update_SPC_Timer_1)
 pop eax
;pop edx
 pop ecx
 mov al,[C_LABEL(SPC_T1_counter)]
 mov byte [C_LABEL(SPC_T1_counter)],bh
 ret

EXPORT_C SPC_READ_COUNTER_2
 push ecx
;push edx
 push eax
 Update_SPC_Timer 2
;call C_LABEL(Update_SPC_Timer_2)
 pop eax
;pop edx
 pop ecx
 mov al,[C_LABEL(SPC_T2_counter)]
 mov byte [C_LABEL(SPC_T2_counter)],bh
 ret

; | ROMEN | TURBO | PC32  | PC10  | ----- |  ST2  |  ST1  |  ST0  |
;
; ROMEN - enable mask ROM in top 64-bytes of address space for CPU read
; TURBO - enable turbo CPU clock ???
; PC32  - clear SPC read ports 2 & 3
; PC10  - clear SPC read ports 0 & 1
; ST2   - start timer 2 (64kHz)
; ST1   - start timer 1 (8kHz)
; ST0   - start timer 0 (8kHz)

EXPORT_C SPC_WRITE_CTRL
 push eax
 mov ah,0
 test al,al     ; New for 0.25 - read hidden RAM
 mov edx,C_LABEL(SPCRAM)
 jns .rom_disabled
 mov edx,C_LABEL(SPC_ROM_CODE)-0xFFC0

.rom_disabled:
 mov [SPC_FFC0_Address],edx

 test al,0x10       ; Reset ports 0/1 to 00 if set
 jz .no_clear_01
 mov [C_LABEL(SPC_PORT0R)],ah   ; Ports read by SPC should be reset! 
 mov [C_LABEL(SPC_PORT1R)],ah   ; Thanks to Butcha for fix!

.no_clear_01:
 test al,0x20       ; Reset ports 2/3 to 00 if set
 jz .no_clear_23
 mov [C_LABEL(SPC_PORT2R)],ah
 mov [C_LABEL(SPC_PORT3R)],ah

.no_clear_23:
 mov edx,[C_LABEL(TotalCycles)]
 test byte [C_LABEL(SPCRAM)+ebx],4
 jnz .no_enable_timer_2
 test al,4
 jz  .no_enable_timer_2
 mov byte [C_LABEL(SPC_T2_counter)],0
%ifdef FAST_SPC
 and edx,~31
%else
 and edx,~15
%endif
 mov word [C_LABEL(SPC_T2_position)],0
 mov [C_LABEL(SPC_T2_cycle_latch)],edx

.no_enable_timer_2:
%ifdef FAST_SPC
 mov dl,0
%else
 and edx,~127
%endif
 test byte [C_LABEL(SPCRAM)+ebx],2
 jnz .no_enable_timer_1
 test al,2
 jz .no_enable_timer_1
 mov byte [C_LABEL(SPC_T1_counter)],0
 mov word [C_LABEL(SPC_T1_position)],0
 mov [C_LABEL(SPC_T1_cycle_latch)],edx

.no_enable_timer_1:
 test byte [C_LABEL(SPCRAM)+ebx],1
 jnz .no_enable_timer_0
 test al,1
 jz .no_enable_timer_0
 mov byte [C_LABEL(SPC_T0_counter)],0
 mov word [C_LABEL(SPC_T0_position)],0
 mov [C_LABEL(SPC_T0_cycle_latch)],edx

.no_enable_timer_0:
 pop eax
 mov [C_LABEL(SPCRAM)+ebx],al
 ret

EXPORT_C SPC_WRITE_DSP_ADDR
 mov [C_LABEL(SPCRAM)+ebx],al
 ret

EXPORT_C SPC_WRITE_DSP_DATA
 mov [C_LABEL(SPC_DSP_DATA)],al
 push ecx
;push edx
 push eax
 call C_LABEL(SPC_WRITE_DSP)
 pop eax
;pop edx
 pop ecx
 ret

EXPORT_C SPC_WRITE_PORT0W
 mov [C_LABEL(SPC_PORT0W)],al
 ret
EXPORT_C SPC_WRITE_PORT1W
 mov [C_LABEL(SPC_PORT1W)],al
 ret
EXPORT_C SPC_WRITE_PORT2W
 mov [C_LABEL(SPC_PORT2W)],al
 ret
EXPORT_C SPC_WRITE_PORT3W
 mov [C_LABEL(SPC_PORT3W)],al
 ret

EXPORT_C SPC_WRITE_TIMER_0
 cmp [C_LABEL(SPC_T0_target)],al
 je .no_change
 push ecx
;push edx
 push eax
 Update_SPC_Timer 0
;call C_LABEL(Update_SPC_Timer_0)   ; Timer must catch up before changing target
 pop eax
;pop edx
 pop ecx
 test al,al
 mov [C_LABEL(SPC_T0_target)],al    ; (0.32) Butcha - timer targets are writable
 setz [C_LABEL(SPC_T0_target)+1]    ; 0 = 256
.no_change:
 ret

EXPORT_C SPC_WRITE_TIMER_1
 cmp [C_LABEL(SPC_T1_target)],al
 je .no_change
 push ecx
;push edx
 push eax
 Update_SPC_Timer 1
;call C_LABEL(Update_SPC_Timer_1)   ; Timer must catch up before changing target
 pop eax
;pop edx
 pop ecx
 test al,al
 mov [C_LABEL(SPC_T1_target)],al    ; (0.32) Butcha - timer targets are writable
 setz [C_LABEL(SPC_T1_target)+1]    ; 0 = 256
.no_change:
 ret

EXPORT_C SPC_WRITE_TIMER_2
 cmp [C_LABEL(SPC_T2_target)],al
 je .no_change
 push ecx
;push edx
 push eax
 Update_SPC_Timer 2
;call C_LABEL(Update_SPC_Timer_2)   ; Timer must catch up before changing target
 pop eax
;pop edx
 pop ecx
 test al,al
 mov [C_LABEL(SPC_T2_target)],al    ; (0.32) Butcha - timer targets are writable
 setz [C_LABEL(SPC_T2_target)+1]    ; 0 = 256
.no_change:
 ret

section .text
ALIGNC
section .data
ALIGND
section .bss
ALIGNB

⌨️ 快捷键说明

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