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

📄 timing.inc

📁 NES game Emulator in Linux.c and asm codes.
💻 INC
📖 第 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

;%define SNEeSe_No_GUI
;%define DELAY_FRAMES 5
; CPU/interrupt/graphics timing implementation

; Fixed events (always at same time in frame):
; Scanline 0: hidden
; Scanline 1-224/239, dots   0- 21: Hblank
; Scanline 1-224/239, dots  22-277: Displayed screen
; Scanline 1-224/239, dots 278-339: Hblank, HDMA start
; Scanline 225/240: Vblank, NMI

; Variable events (software-specified time in frame):
;  H-IRQ - on a specific point on every scanline
;  V-IRQ - short time after beginning of a specific scanline
;  H+V-IRQ - on a specific point on the frame

;%define REPORT_EVENTS

%define ZERO_TRIP_CYCLE_COUNTER


%define JOYC1_OPEN_BUS_BITS (((1 << 6) - 1) << 2)
%define JOYC2_OPEN_BUS_BITS (((1 << 3) - 1) << 5)
%define JOYC2_ALWAYS_HIGH_BITS (((1 << 3) - 1) << 2)

%define RDNMI_OPEN_BUS_BITS (((1 << 3) - 1) << 4)
%define TIMEUP_OPEN_BUS_BITS ((1 << 7) - 1)
%define HVBJOY_OPEN_BUS_BITS (((1 << 5) - 1) << 1)

%define RDNMI_VBLANK_START (1 << 7)
%define HVBJOY_IN_VBLANK (1 << 7)
%define HVBJOY_IN_HBLANK (1 << 6)
%define HVBJOY_CONTROLLERS_BUSY (1 << 0)

%include "misc.inc"
%include "ppu/screen.inc"
%include "cpu/dma.inc"
%include "apu/spc.inc"
%include "ppu/sprites.inc"
%include "cycles.inc"

EXTERN_C FPSTicks
EXTERN_C UPDATE_KEYS
EXTERN_C CONTROLLER_1_TYPE,CONTROLLER_2_TYPE
EXTERN_C MickeyMouse,MickeyRead,MouseButtons

%define FRAME_LIMIT 0
; Set this to force emulation loop to break after x frames have been
;  displayed (not emulated!) This does not save CPU regs, etc.!
;  Primary use is for profiling!
%define FRAME_LIMIT_PROFILE 0
; Set this to change behavior of FRAME_LIMIT to enable internal
;  profiling code
;%define VAR_FRAME_LIMIT
; Set this to force emulation loop to break after FrameLimit frames

section .bss
%if 0
ALIGND
EXPORT LineBG1OFS   ,skipw 2*239
ALIGND
EXPORT LineBG2OFS   ,skipw 2*239
ALIGND
EXPORT LineBG3OFS   ,skipw 2*239
ALIGND
EXPORT LineBG4OFS   ,skipw 2*239
ALIGND
EXPORT LineM7       ,skipw 6*239
EXPORT ScreenLineHoles  ,skipb (256+128)/8
%endif

JOYC1:      skipb   ; This holds the controller read control byte
EXPORT_C Controller1_Pos,skipb  ; Shift count for controller 1 read
EXPORT_C Controller23_Pos,skipb ; Shift count for controller 2/3 read
EXPORT_C Controller45_Pos,skipb ; Shift count for controller 4/5 read

ALIGNB
; These are updated per VBL rather than per read!
EXPORT_C JOY1L,skipb
EXPORT_C JOY1H,skipb 3
EXPORT_C JOY2L,skipb
EXPORT_C JOY2H,skipb 3
; These are not updated yet
EXPORT_C JOY3L,skipb
EXPORT_C JOY3H,skipb 3
EXPORT_C JOY4L,skipb
EXPORT_C JOY4H,skipb 3

RDDIV:
RDDIVL:     skipb   ; Quotient of divide
RDDIVH:     skipb
RDMPY:
RDMPYL:     skipb   ; Multiplication or remainder
RDMPYH:     skipb
WRMPY:
WRMPYA:     skipb   ; Multiplicand A
WRMPYB:     skipb   ; Multiplicand B
WRDIV:
WRDIVL:     skipb   ; Dividend C
WRDIVH:     skipb

EXPORT_C LastRenderLine ,skipl
EXPORT_C LastVBLLine    ,skipl

EXPORT FrameCount       ,skipl  ; Used for frameskipping
EXPORT Event_Handler    ,skipl  ; Used for render/HDMA timing/NMI/IRQ
EXPORT Fixed_Event      ,skipl  ; Used for render/HDMA timing
EXPORT Last_Trip        ,skipl  ; Clock cycle position of last scheduled event

HDMA_Next_Event:    skipl   ; Fixed event following HDMA

DMA_Next_Event:     skipl   ; Fixed event following DMA
DMA_Next_Trip:      skipl

Render_Next_Event:  skipl   ; Fixed event following render
Render_Next_Trip:   skipl

NMI_Event_Handler:  skipl   ; Event handler for Vblank start
NMI_Next_Event:     skipl   ; Fixed event following render
NMI_Next_Trip:      skipl

Vblank_Start:       skipl   ; Scanline of last Vblank start
EXPORT HTimer           ,skipl  ; IRQ position on scanline
EXPORT HTimer_Set       ,skipl  ; H-IRQ position set in $4207-8

EXPORT_C HTIMEL ,skipb  ; H IRQ position low
EXPORT_C HTIMEH ,skipb  ; H IRQ position high
                 skipb
                 skipb
EXPORT_C VTIMEL ,skipb  ; V IRQ position low
EXPORT_C VTIMEH ,skipb  ; V IRQ position high
                 skipb
                 skipb

FPSCount:       skipl   ; Count of frames executed this second
EXPORT_C FPSMaxTicks,skipl  ; Number of timer ticks after which FPS is calc'd
EXPORT_C FPSLast    ,skipl  ; Calculated FPS for last second
EXPORT_C BreaksLast ,skipl  ; Render breaks for last frame

Latched_H:  skipl   ; These two are latched values!
Latched_V:  skipl

MEMSEL:     skipb   ; FastROM switch
; Hack for FF6 glitch, to be removed when timing improved
EXPORT_C FastROM_offset,skipb
EXPORT OPHCT,skipb  ; Whether reading lo or high byte
EXPORT OPVCT,skipb  ; Whether reading lo or high byte
RDNMI:      skipb   ; x000vvvv  x=disable/enable NMI,vvvv=version

;mask for RDNMI - (only) bit 7 cleared if NMI source raised and cleared
; during this frame
RDNMI_mask:skipb

EXPORT_C NMITIMEN,skipb ; a0yx000b  a=NMI on/off,y=vert count,x=horiz count,b=joy read
; I/O port register - bit 7 affects H/V counter, all bits used for output
EXPORT_C WRIO,skipb     ; lxxxxxxx  l=counter latched when set
; I/O port register - value CPU is receiving
; RDIO register reads return this value ANDed with value in WRIO
EXPORT_C RDIO,skipb
EXPORT HVBJOY,skipb
; Nonzero if an IRQ set for current line has not been reached yet
;IRQ_set_this_line:skipb

section .text

;R_Cycles = C_LABEL(EventTrip) - C_LABEL(SNES_Cycles), trips on <= zero

;%1 = Execute
%macro Update_Cycles 0-1 0
 mov eax,[C_LABEL(SNES_Cycles)] ; Update CPU and SPC cycles

 mov edi,[C_LABEL(SNES_Cycles)]
 sub edi,[C_LABEL(EventTrip)]

 cmp eax,CYCLES_REFRESH_START
 jb %%before_refresh
 add eax,byte CYCLES_IN_REFRESH
%%before_refresh:

 sub eax,[SPC_last_cycles]
 mov [C_LABEL(SNES_Cycles)],edi
 mov [SPC_last_cycles],edi

%ifidni %1,Execute
 cmp byte [C_LABEL(SPC_ENABLED)],0
 jz %%no_spc
 add eax,[SPC_CPU_cycles]
 Execute_SPC
%%no_spc:
%else
 add [SPC_CPU_cycles],eax
%endif
%endmacro

; Uses 012-local labels, corrupts edx, zeroes eax
%macro Update_FPS_Counter 0
 mov edx,[C_LABEL(FPSMaxTicks)]
 xor eax,eax
 cmp [C_LABEL(FPSTicks)],edx
 jb %%no_update
 mov edx,[FPSCount]
 je %%fps_update
 cmp edx,byte 1
 ja %%fps_update
 mov dword [C_LABEL(FPSLast)],0
 jmp %%less_than_1_fps
%%fps_update:
 mov [C_LABEL(FPSLast)],edx
%%less_than_1_fps:
 mov [C_LABEL(FPSTicks)],eax
 mov [FPSCount],eax
%%no_update:
%endmacro

; CPU/Render/Display/Blanking timing
; Master clock: 21.47MHz   Dot clock: masterclock/4
; Vblank: Lines (DisplayEnd + 1) to 0
; Display: Lines 1 to DisplayEnd
; < 22d Hblank><256d display       >< 64d Hblank> (render)
; <512c exec  >< 40c memory refresh><816c exec  > (execution)

; Keep one fixed event cycle-target/handler pointer
; Keep one variable (may be fixed or IRQ)
ALIGNC
EXPORT IRQNewFrameReset

 mov dword [Last_Trip],0

 mov dword [C_LABEL(EventTrip)],CYCLES_HDMA_START
 mov dword [FixedTrip],CYCLES_HDMA_START
 mov dword [Event_Handler],HDMA_Event
 mov dword [Fixed_Event],HDMA_Event
 mov dword [HDMA_Next_Event],C_LABEL(IRQFirstRender)

 ; Reset frameskip counter
 mov ebx,1
 cmp ebx,[C_LABEL(FRAME_SKIP_MIN)]
 ja .no_fix_framecount
 mov ebx,[C_LABEL(FRAME_SKIP_MIN)]
.no_fix_framecount:
 mov [FrameCount],ebx

 xor eax,eax
 mov [C_LABEL(Timer_Counter_Throttle)],eax  ; Reset speed-throttle timer
 mov [C_LABEL(Current_Line_Timing)],eax     ; Reset scanline counter
 inc dword [FPSCount]           ; For FPS counter - 0.25b14

;%define TRAP_HDMA_RELATCH
%ifdef TRAP_HDMA_RELATCH
EXTERN_C Dump_DMA
 pusha
 push byte 0
 call C_LABEL(Dump_DMA)
 pop edi
 popa
 RELATCH_HDMA
 pusha
 push byte 1
 call C_LABEL(Dump_DMA)
 pop edi
 popa
%else
 RELATCH_HDMA
%endif

 Update_FPS_Counter
 ret

EXTERN_C print_str,print_decnum,print_hexnum
section .data
gap_str:db " ",0
nl_str:db 10,0
section .text


%macro report_event 0
%ifdef REPORT_EVENTS
 push 8
 push dword [C_LABEL(EventTrip)]
 call C_LABEL(print_hexnum)

 push gap_str
 call C_LABEL(print_str)

 push 8
 push dword [C_LABEL(SNES_Cycles)]
 call C_LABEL(print_hexnum)

 push .str
 call C_LABEL(print_str)

 push dword [C_LABEL(Current_Line_Timing)]
 call C_LABEL(print_decnum)

 push nl_str
 call C_LABEL(print_str)
 add esp,4*8
%endif
%endmacro

ALIGNC
HDMA_Event:
section .data
.str:db " HDMA ",0
section .text

 report_event

 mov eax,[C_LABEL(EventTrip)]
 mov [Last_Trip],eax

 mov eax,[HDMA_Next_Event]
;mov dword [C_LABEL(EventTrip)],CYCLES_NEW_SCANLINE
 mov dword [FixedTrip],CYCLES_NEW_SCANLINE
;mov [Event_Handler],eax
 mov [Fixed_Event],eax
 cmp byte [HDMAON],0
 jz .no_HDMA
 LOAD_CYCLES
 add R_65c816_Cycles,byte 18    ; HDMA processing
 call do_HDMA
 SAVE_CYCLES
%ifdef TRAP_HDMA_RELATCH
 pusha
 push byte 2
 call C_LABEL(Dump_DMA)
 pop edi
 popa
%endif
.no_HDMA:
 call IRQ_Check_Late    ; chain

 LOAD_CYCLES
 test R_Cycles,R_Cycles
 jl .no_chain
 jmp dword [Event_Handler]
.no_chain:

 jmp CPU_START  ; Return to CPU

ALIGNC
Enabling_IRQ_Event:
section .data
.str:db " enabling IRQ ",0
section .text

 report_event

 cmp byte [NMI_pin],NMI_Raised
 je .nmi_waiting

 mov al,CEM_Instruction_After_IRQ_Enable
 mov [CPU_Execution_Mode],al

.nmi_waiting:
 call IRQ_Check_Late    ; chain

 LOAD_CYCLES
 test R_Cycles,R_Cycles
 jl .no_chain
 jmp dword [Event_Handler]
.no_chain:
 jmp CPU_START  ; Return to CPU

ALIGNC
IRQ_Enabled_Event:
section .data
.str:db " enabled IRQ ",0
section .text

 report_event

 mov al,[CPU_Execution_Mode]
 cmp al,CEM_Instruction_After_IRQ_Enable
 jne .mode_changed

 mov al,CEM_Normal_Execution
 mov [CPU_Execution_Mode],al

.mode_changed:
 mov al,[IRQ_pin]
 test al,al
 jz .no_irq

 mov al,[CPU_Execution_Mode]
 cmp al,CEM_In_DMA
 je .no_irq
 cmp al,CEM_Clock_Stopped
 je .no_irq
 cmp al,CEM_Waiting_For_Interrupt
 jne .no_wai

%ifdef WAI_DELAY
 ; WAI delay after interrupt signal: 2 IO
 add dword [C_LABEL(SNES_Cycles)],byte 12   ;*
%endif

 inc word [CPU_LABEL(PC)]

 mov byte [CPU_Execution_Mode],CEM_Normal_Execution

.no_wai:
 push edi
 LOAD_BASE
 JUMP_FLAG SNES_FLAG_I,.irq_disabled    ; Interrupts disabled?
 LOAD_CYCLES
 add R_65c816_Cycles,byte 12    ; IRQ processing: 2 IO
 JUMP_NOT_FLAG SNES_FLAG_E,.native_irq
 call E1_IRQ
 jmp .irq_return
ALIGNC
.native_irq:
 call E0_IRQ
.irq_return:
 SAVE_CYCLES
.irq_disabled:
 pop edi

.no_irq:
 call IRQ_Check_Late    ; chain

 LOAD_CYCLES
 test R_Cycles,R_Cycles
 jl .no_chain
 jmp dword [Event_Handler]
.no_chain:
 jmp CPU_START  ; Return to CPU

ALIGNC
DMA_Event:
section .data
.str:db " DMA ",0
section .text

 report_event

 mov byte [CPU_Execution_Mode],CEM_In_DMA

 mov eax,[DMA_Next_Event]
 mov [Fixed_Event],eax
 mov eax,[DMA_Next_Trip]
 mov [FixedTrip],eax

 call IRQ_Check_Late    ; chain

 LOAD_CYCLES
 test R_Cycles,R_Cycles
 jl .no_chain
 jmp dword [Event_Handler]
.no_chain:

 jmp CPU_START  ; Return to CPU

ALIGNC
Render_Event:
section .data
.str:db " render ",0
section .text

 report_event

 mov eax,[C_LABEL(EventTrip)]
 mov [Last_Trip],eax

 mov eax,[Render_Next_Event]
;mov [Event_Handler],eax
 mov [Fixed_Event],eax
 mov eax,[Render_Next_Trip]
;mov [C_LABEL(EventTrip)],eax
 mov [FixedTrip],eax
 RenderScanline
 call IRQ_Check_Late    ; chain

 LOAD_CYCLES
 test R_Cycles,R_Cycles
 jl .no_chain
 jmp dword [Event_Handler]
.no_chain:

 jmp CPU_START  ; Return to CPU

ALIGNC
speed_cap_wait_hlt:
 hlt
 jmp IRQNewFrame.speed_cap_wait
ALIGNC
EXPORT IRQNewFrame              ; Check IRQ, Frame Skip
section .data
.str:db " new frame ",0
section .text

 report_event

%ifdef DELAY_FRAMES
 push dword DELAY_FRAMES
EXTERN_C rest
 call C_LABEL(rest)
 add esp,4
%endif

 mov dword [Last_Trip],0

 ; Force SPC to catch up to eliminate lags and improve sound
 Update_Cycles Execute

 ; Update SPC timers to prevent overflow
 Update_SPC_Timer 0
 Update_SPC_Timer 1
 Update_SPC_Timer 2

 call C_LABEL(update_sound) ; Added by Butcha

;mov dword [C_LABEL(EventTrip)],CYCLES_HDMA_START
 mov dword [FixedTrip],CYCLES_HDMA_START
;mov dword [Event_Handler],HDMA_Event
 mov dword [Fixed_Event],HDMA_Event

 mov al,[HVBJOY]
 and al,~HVBJOY_IN_VBLANK   ; VBlank off
 mov [HVBJOY],al

 mov byte [RDNMI_mask],0xFF
 mov byte [RDNMI],VERSION_NUMBER_5A22   ; Clear NMI enabled bit in 0x4210
 mov byte [NMI_pin],NMI_None            ;clear NMI input

 mov al,[C_LABEL(INIDISP)]
 test al,al
 js .forced_blank
 mov eax,[C_LABEL(OAMAddress_VBL)]
 ; Restore OAM address and reset OAM read/write odd/even select
 ;  every Vblank, unless we're in forced blank
 mov [C_LABEL(OAMAddress)],eax
 xor eax,eax
 mov [OAMHigh],al
.forced_blank:

 xor eax,eax
 dec dword [FrameCount] ; Should we redraw the screen this frame?
 mov [C_LABEL(Current_Line_Timing)],eax ; Reset scanline counter
 mov edi,C_LABEL(IRQFirst)
 jnz .no_redraw

 ; this frame not skipped - determine how many frames following to skip
 inc dword [FPSCount]   ; For FPS counter - 0.25b14
 mov edi,C_LABEL(IRQFirstRender)

; If fast-forward is on, skip the speed-throttle logic
EXTERN_C fast_forward_enabled
 push ecx
 push edx
 call C_LABEL(fast_forward_enabled)
 pop edx
 pop ecx
 test al,al
 mov eax,0
 jnz .fast_forward

; If minimum frameskip set, don't wait for timer to tell us to draw one
 cmp [C_LABEL(FRAME_SKIP_MIN)],eax
 jnz .no_speed_cap

; Wait for timer to tell us to draw a frame
.speed_cap_wait:
 mov ebx,[C_LABEL(Timer_Counter_Throttle)]
 test ebx,ebx
; HLT should be more multi-tasking friendly but appears
; to cause problems with pure DOS in some cases?
 jz .speed_cap_wait    ;speed_cap_wait_hlt

.no_speed_cap:
 mov ebx,[C_LABEL(Timer_Counter_Throttle)]
 cmp ebx,[C_LABEL(FRAME_SKIP_MAX)]
 jb .no_cap_max_skip

.fast_forward:
 mov ebx,[C_LABEL(FRAME_SKIP_MAX)]
 mov [C_LABEL(Timer_Counter_Throttle)],eax  ; Reset speed-throttle timer
 jmp .have_skip_count

.no_cap_max_skip:
 sub [C_LABEL(Timer_Counter_Throttle)],ebx  ; Update speed-throttle timer

 cmp ebx,[C_LABEL(FRAME_SKIP_MIN)]
 ja .no_cap_min_skip
 mov ebx,[C_LABEL(FRAME_SKIP_MIN)]

.no_cap_min_skip:
.have_skip_count:
 mov [FrameCount],ebx   ; Reset frame counter

%ifdef DEBUG
 inc dword [C_LABEL(Frames)]

%if FRAME_LIMIT
 cmp dword [C_LABEL(Frames)],FRAME_LIMIT
 jne .no_limit
%if FRAME_LIMIT_PROFILE
EXTERN_C Profiler_Start
 mov byte [C_LABEL(Profiler_Start)],1
%else
 ret
%endif
.no_limit:
%endif
%endif

 mov al,[C_LABEL(INIDISP)]
 and al,0x0F
 cmp al,[C_LABEL(BrightnessLevel)]
 je .same_brightness
 mov byte [C_LABEL(PaletteChanged)],1
 mov [C_LABEL(BrightnessLevel)],al
.same_brightness:

.no_redraw:
 mov [HDMA_Next_Event],edi

 xor byte [STAT78],0x80 ; Toggle current field

%ifdef TRAP_HDMA_RELATCH
 pusha
 push byte 0
 call C_LABEL(Dump_DMA)
 pop edi
 popa
 RELATCH_HDMA
 pusha
 push byte 1
 call C_LABEL(Dump_DMA)
 pop edi

⌨️ 快捷键说明

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