www.pudn.com > sn068s.zip > TIMING.NI
;%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-223/239, dots 0- 21: Hblank
; Scanline 1-223/239, dots 22-277: Displayed screen
; Scanline 1-223/239, dots 278-339: Hblank, HDMA start
; Scanline 224/240: Hblank, HDMA start
; 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 ZERO_TRIP_CYCLE_COUNTER
%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.ni"
%include "screen.ni"
%include "DMA.ni"
%include "SPC.ni"
%include "sprites.ni"
%include "scankeys.ni"
%include "key.ni"
%include "cycles.ni"
extern _FPSTicks
extern _UPDATE_KEYS
%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 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
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 ; Fixed event following DMA
Render_Next_Event: skipl ; Fixed event following render
Render_Next_Trip: skipl ; Fixed event following render
NMI_Event_Handler: skipl ; Event handler for NMI
Vblank_Start: skipl ; Scanline of last Vblank start
EXPORT HTimer ,skipl ; Clock cycle position of H-IRQ delay start
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
MEMSEL: skipl ; FastROM switch
Latched_H: skipl ; These two are latched values!
Latched_V: skipl
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
EXPORT_C NMITIMEN,skipb ; a0yx000b a=NMI on/off,y=vert count,x=horiz count,b=joy read
EXPORT HVBJOY,skipb
section .text
;R_Cycles = _EventTrip - _SNES_Cycles, trips on <= zero
;%1 = Execute
%macro Update_Cycles 0-1 0
mov eax,[_SNES_Cycles] ; Update CPU and SPC cycles
mov edi,[_SNES_Cycles]
sub edi,[_EventTrip]
cmp eax,CYCLES_REFRESH_START
jb %%before_refresh
add eax,byte CYCLES_IN_REFRESH
%%before_refresh:
sub eax,[SPC_last_cycles]
mov [_SNES_Cycles],edi
mov [SPC_last_cycles],edi
%ifidni %1,Execute
cmp byte [_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,[_FPSMaxTicks]
xor eax,eax
cmp [_FPSTicks],edx
jb %%no_update
mov edx,[FPSCount]
je %%fps_update
cmp edx,byte 1
ja %%fps_update
mov dword [_FPSLast],0
jmp short %%less_than_1_fps
%%fps_update:
mov [_FPSLast],edx
%%less_than_1_fps:
mov [_FPSTicks],eax
mov [FPSCount],eax
ALIGNC
%%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 [_EventTrip],CYCLES_HBLANK_START
mov dword [FixedTrip],CYCLES_HBLANK_START
mov dword [Event_Handler],HDMA_Event
mov dword [Fixed_Event],HDMA_Event
mov dword [HDMA_Next_Event],IRQFirstRender
; Reset frameskip counter
mov ebx,1
cmp ebx,[_FRAME_SKIP_MIN]
ja .no_fix_framecount
mov ebx,[_FRAME_SKIP_MIN]
.no_fix_framecount:
mov [FrameCount],ebx
xor eax,eax
mov [_Timer_Counter_Throttle],eax ; Reset speed-throttle timer
mov [_Current_Line_Timing],eax ; Reset scanline counter
inc dword [FPSCount] ; For FPS counter - 0.25b14
RELATCH_HDMA
Update_FPS_Counter
ret
ALIGNC
HDMA_Event:
mov eax,[_EventTrip]
mov [Last_Trip],eax
mov eax,[HDMA_Next_Event]
mov dword [_EventTrip],CYCLES_NEW_SCANLINE
mov dword [FixedTrip],CYCLES_NEW_SCANLINE
mov [Event_Handler],eax
mov [Fixed_Event],eax
call do_HDMA
call HIRQ_Check_Late ; chain
LOAD_CYCLES
test R_Cycles,R_Cycles
jl .no_irq
jmp dword [Event_Handler]
.no_irq:
jmp near CPU_START ; Return to CPU
ALIGNC
DMA_Event:
mov eax,[_EventTrip]
mov [Last_Trip],eax
mov eax,[DMA_Next_Event]
mov [Event_Handler],eax
mov [Fixed_Event],eax
mov eax,[DMA_Next_Trip]
mov [_EventTrip],eax
mov [FixedTrip],eax
;do DMA here
call HIRQ_Check_Late ; chain
LOAD_CYCLES
test R_Cycles,R_Cycles
jl .no_irq
jmp dword [Event_Handler]
.no_irq:
jmp near CPU_START ; Return to CPU
ALIGNC
Render_Event:
mov eax,[_EventTrip]
mov [Last_Trip],eax
mov eax,[Render_Next_Event]
mov [Event_Handler],eax
mov [Fixed_Event],eax
mov eax,[Render_Next_Trip]
mov [_EventTrip],eax
mov [FixedTrip],eax
RenderScanline
call HIRQ_Check_Late ; chain
LOAD_CYCLES
test R_Cycles,R_Cycles
jl .no_irq
jmp dword [Event_Handler]
.no_irq:
jmp near CPU_START ; Return to CPU
ALIGNC
speed_cap_wait_hlt:
hlt
jmp near IRQNewFrame.speed_cap_wait
ALIGNC
EXPORT IRQNewFrame ; Check IRQ, Frame Skip
%ifdef DELAY_FRAMES
push dword DELAY_FRAMES
extern _rest
call _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 _update_sound ; Added by Butcha
mov dword [_EventTrip],CYCLES_HBLANK_START
mov dword [FixedTrip],CYCLES_HBLANK_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],VERSION_NUMBER_5A22 ; Clear NMI enabled bit in 0x4210
mov al,[_INIDISP]
test al,al
js .forced_blank
mov eax,[_OAMAddress_VBL]
; Restore OAM address and reset OAM read/write odd/even select
; every Vblank, unless we're in forced blank
mov [_OAMAddress],eax
xor eax,eax
mov [OAMHigh],al
.forced_blank:
xor eax,eax
dec dword [FrameCount] ; Should we redraw the screen this frame?
mov [_Current_Line_Timing],eax ; Reset scanline counter
mov edi,IRQFirst
jnz near .no_redraw
; this frame not skipped - determine how many frames following to skip
inc dword [FPSCount] ; For FPS counter - 0.25b14
mov edi,IRQFirstRender
; If fast-forward is on, skip the speed-throttle logic
JUMP_KEY_DOWN KEY_TILDE,.fast_forward,al
; If minimum frameskip set, don't wait for timer to tell us to draw one
cmp [_FRAME_SKIP_MIN],eax
jnz .no_speed_cap
; Wait for timer to tell us to draw a frame
.speed_cap_wait:
mov ebx,[_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,[_Timer_Counter_Throttle]
cmp ebx,[_FRAME_SKIP_MAX]
jb .no_cap_max_skip
.fast_forward:
mov ebx,[_FRAME_SKIP_MAX]
mov [_Timer_Counter_Throttle],eax ; Reset speed-throttle timer
jmp short .have_skip_count
.no_cap_max_skip:
sub [_Timer_Counter_Throttle],ebx ; Update speed-throttle timer
cmp ebx,[_FRAME_SKIP_MIN]
ja .no_cap_min_skip
mov ebx,[_FRAME_SKIP_MIN]
.no_cap_min_skip:
.have_skip_count:
mov [FrameCount],ebx ; Reset frame counter
%ifdef DEBUG
inc dword [_Frames]
%if FRAME_LIMIT
cmp dword [_Frames],FRAME_LIMIT
jne .no_limit
ret
.no_limit:
%endif
%endif
mov al,[_INIDISP]
and al,0x0F
cmp al,[_BrightnessLevel]
je .same_brightness
mov byte [_PaletteChanged],1
mov [_BrightnessLevel],al
.same_brightness:
.no_redraw:
mov [HDMA_Next_Event],edi
xor byte [STAT78],0x80 ; Toggle current field
RELATCH_HDMA
Update_FPS_Counter
pusha
call _update_sound_block
popa
xor eax,eax
jmp near CPU_START_IRQ ; Return to CPU
ALIGNC
EXPORT_C IRQFirstRender
IRQFirstRender: ; Check HDMA, IRQ, Render
mov dword [Last_Trip],0
mov dword [_EventTrip],CYCLES_DISPLAY_START
mov dword [FixedTrip],CYCLES_DISPLAY_START
mov dword [Event_Handler],Render_Event
mov dword [Fixed_Event],Render_Event
mov dword [Render_Next_Event],HDMA_Event
mov dword [Render_Next_Trip],CYCLES_HBLANK_START
mov dword [HDMA_Next_Event],IRQRender
Render_Start_Frame ; Reset framebuffer render address
Update_Cycles
mov eax,[_Current_Line_Timing] ; Get current scanline
inc eax
mov [_Current_Line_Timing],eax
jmp near CPU_START_IRQ ; Return to CPU
ALIGNC
EXPORT_C IRQRender
IRQRender: ; Check HDMA, IRQ, Render
mov dword [Last_Trip],0
Update_Cycles
mov dword [_EventTrip],CYCLES_DISPLAY_START
mov dword [FixedTrip],CYCLES_DISPLAY_START
mov dword [Event_Handler],Render_Event
mov dword [Fixed_Event],Render_Event
mov eax,[_Current_Line_Timing] ; Get current scanline
inc eax
mov [_Current_Line_Timing],eax
cmp [_LastRenderLine],eax
jbe .end_of_display
jmp near CPU_START_IRQ ; Return to CPU
ALIGNC
.end_of_display:
mov dword [NMI_Event_Handler],NMI
mov dword [Render_Next_Event],VBL_First_Line
mov dword [Render_Next_Trip],CYCLES_NEW_SCANLINE
jmp near CPU_START_IRQ ; Return to CPU
ALIGNC
VBL_First_Line:
mov dword [Last_Trip],0
Update_Cycles
mov al,[HVBJOY]
; VBlank on, controllers being read
or al,HVBJOY_IN_VBLANK | HVBJOY_CONTROLLERS_BUSY
mov [HVBJOY],al
mov al,RDNMI_VBLANK_START | VERSION_NUMBER_5A22
mov [RDNMI],al ; Set NMI enabled bit in 0x4210
mov eax,[NMI_Event_Handler]
mov dword [_EventTrip],CYCLES_NEW_SCANLINE
mov dword [FixedTrip],CYCLES_NEW_SCANLINE
mov [Event_Handler],eax
mov [Fixed_Event],eax
mov eax,[_Current_Line_Timing] ; Get current scanline
inc eax
mov [_Current_Line_Timing],eax
mov [Vblank_Start],eax
jmp near CPU_START_IRQ ; Return to CPU
ALIGNC
VBL_Update_Controllers:
mov dword [Last_Trip],0
Update_Cycles
mov eax,[_Current_Line_Timing]
inc eax
mov [_Current_Line_Timing],eax
sub eax,[Vblank_Start]
cmp eax,byte 3
jb .no_controller_update
call UPDATE_CONTROLLERS
mov dword [Event_Handler],VBL
mov dword [Fixed_Event],VBL
and byte [HVBJOY],~HVBJOY_CONTROLLERS_BUSY ; Controllers ready
.no_controller_update:
mov eax,[_Current_Line_Timing] ; Get current scanline
jmp near CPU_START_IRQ ; Return to CPU
ALIGNC
VBL:
mov dword [Last_Trip],0
Update_Cycles
mov eax,[_Current_Line_Timing] ; Get current scanline
inc eax
mov [_Current_Line_Timing],eax
cmp [_LastVBLLine],eax ; Needs adjustment for odd line
ja .not_end_of_frame
mov dword [Event_Handler],IRQNewFrame
mov dword [Fixed_Event],IRQNewFrame
.not_end_of_frame:
jmp near CPU_START_IRQ ; Return to CPU
ALIGNC
EXPORT_C IRQFirst
IRQFirst: ; Check HDMA, IRQ, Render
mov dword [Last_Trip],0
mov dword [_EventTrip],CYCLES_HBLANK_START
mov dword [FixedTrip],CYCLES_HBLANK_START
mov dword [Event_Handler],HDMA_Event
mov dword [Fixed_Event],HDMA_Event
mov dword [HDMA_Next_Event],IRQNoRender
Render_Start_Frame_Skipped
Update_Cycles
mov eax,[_Current_Line_Timing] ; Get current scanline
inc eax
mov [_Current_Line_Timing],eax
jmp near CPU_START_IRQ ; Return to CPU
ALIGNC
IRQNoRender: ; Check HDMA, IRQ
mov dword [Last_Trip],0
Update_Cycles
mov eax,[_Current_Line_Timing] ; Get current scanline
inc eax
mov [_Current_Line_Timing],eax
cmp [_LastRenderLine],eax
jbe .end_of_display
mov dword [_EventTrip],CYCLES_HBLANK_START
mov dword [FixedTrip],CYCLES_HBLANK_START
mov dword [Event_Handler],HDMA_Event
mov dword [Fixed_Event],HDMA_Event
jmp near CPU_START_IRQ ; Return to CPU
ALIGNC
.end_of_display:
mov dword [NMI_Event_Handler],NMI_NoRender
mov dword [Event_Handler],VBL_First_Line
mov dword [Fixed_Event],VBL_First_Line
jmp near CPU_START_IRQ ; Return to CPU
ALIGNC
EXPORT_C NMI
EXPORT NMI
SyncRender ;*
mov ax,[_Real_SNES_Palette]
push eax
mov al,[_fixedpalettecheck]
test al,al
jz .Do_Copy
.Fixed_Back_Color_Add_Hack:
%if 0
push ebx
mov ax,[_COLDATA]
mov bx,[_Real_SNES_Palette]
and eax,~((1<<5)+(1<<10)+(1<<15)) ; remove 'carry' bits
and ebx,~((1<<5)+(1<<10)+(1<<15)) ; remove 'carry' bits
add eax,ebx
test eax,((1<<5)+(1<<10)+(1<<15))
jz .Add_Hack_End
; Saturation
test al,0x20
jz .no_blue_saturation
or al,0x1F
.no_blue_saturation:
test ah,0x04
jz .no_green_saturation
or eax,0x3E0
.no_green_saturation:
test ah,0x80
jz .no_blue_saturation
or ah,0x7C
.no_blue_saturation:
and eax,~((1<<5)+(1<<10)+(1<<15)) ; remove 'carry' bits
.Add_Hack_End:
pop ebx
mov [_Real_SNES_Palette],ax
%endif
mov ax,[_COLDATA]
mov [_Real_SNES_Palette],ax
mov byte [_PaletteChanged],1
.Do_Copy:
cmp byte [_FPS_ENABLED],0
jz .no_fps_counter
%ifndef SNEeSe_No_GUI
call _ShowFPS
%endif
.no_fps_counter:
cmp byte [_BREAKS_ENABLED],0
jz .no_breaks_counter
%ifndef SNEeSe_No_GUI
call _ShowBreaks
%endif
.no_breaks_counter:
mov dword [_BreaksLast],0
%ifndef SINGLE_STEP
call _Copy_Screen
%endif
;call _Display_Debug
pop eax
mov [_Real_SNES_Palette],ax
EXPORT NMI_NoRender
EXPORT_C NMI_NoRender
mov dword [Last_Trip],0
mov dword [Event_Handler],VBL_Update_Controllers
mov dword [Fixed_Event],VBL_Update_Controllers
Update_Cycles
mov eax,[_Current_Line_Timing]
inc eax
mov [_Current_Line_Timing],eax
CheckNMI:
%ifdef DEBUG
mov al,[_PB]
mov [_OLD_PB],al ; Save program bank
mov eax,[_PC]
mov [_OLD_PC],eax ; Save program counter
%endif
test byte [_NMITIMEN],0x80
jz .CHECK_KEYS ; NMI off? (via hardware register)
; Neill's timing doc: 19-23 clock delay
mov dword [_EventTrip],CYCLES_NMI_DELAY
mov dword [FixedTrip],CYCLES_NMI_DELAY
mov dword [Event_Handler],NMI_Event
mov dword [Fixed_Event],NMI_Event
.CHECK_KEYS:
call _UPDATE_KEYS
test eax,eax
jnz .break
; Return to emulation
mov eax,[_Current_Line_Timing] ; Get current scanline
jmp near CPU_START_IRQ ; Return to CPU
.break:
;pusha
mov ebx,[_PB_Shifted]
mov bx,[_PC]
GET_BYTE
mov [_Map_Byte],al
;popa
mov eax,[_Current_Line_Timing] ; Get current scanline
CheckVIRQ
ret
ALIGNC
NMI_Event:
mov eax,[_EventTrip]
mov [Last_Trip],eax
mov dword [_EventTrip],CYCLES_NEW_SCANLINE
mov dword [FixedTrip],CYCLES_NEW_SCANLINE
mov dword [Event_Handler],VBL_Update_Controllers
mov dword [Fixed_Event],VBL_Update_Controllers
mov al,[CPU_Execution_Mode]
cmp al,CEM_Waiting_For_Interrupt
jne .no_wai
; WAI delay after interrupt signal: 2 IO
;add dword [_SNES_Cycles],byte 12 ;*
inc word [_PC]
mov byte [CPU_Execution_Mode],CEM_Normal_Execution
.no_wai:
push edx
LOAD_BASE
%ifdef FAST_STACK_ACCESS_EMULATION_MODE
JUMP_NOT_FLAG SNES_FLAG_E,E0_NMI,near ; Are we in emulation mode?
%else
JUMP_NOT_FLAG SNES_FLAG_E,E0_NMI,near ; Are we in emulation mode?
%endif
E1_NMI:
; Emulation mode NMI
mov eax,[_PC]
E1_PUSH_W
;CLR_FLAG SNES_FLAG_B ; Clear break bit on stack
E1_SETUPFLAGS 0 ; put flags into SNES packed flag format
;SET_FLAG SNES_FLAG_B
E1_PUSH_B
mov eax,[_NMI_Evector] ; Get Emulation mode NMI vector
mov ebx,[_NMI_Eoffset]
jmp NMI_completion
ALIGNC
E0_NMI:
add dword [_SNES_Cycles],byte 8 ; NMI processing: +1 bank0 for E=0
; Native mode NMI
mov al,[_PB]
E0_PUSH_B
mov eax,[_PC]
E0_PUSH_W
E0_SETUPFLAGS ; put flags into SNES packed flag format
E0_PUSH_B
mov eax,[_NMI_Nvector] ; Get Native mode NMI vector
mov ebx,[_NMI_Noffset]
NMI_completion:
mov [_PC],eax ; Setup PC vector
mov [_PBOffset],ebx
mov byte [_PB],0 ; Setup bank
add dword [_SNES_Cycles],byte 52 ; NMI processing: +1 bank0 for E=0
;SET_FLAG SNES_FLAG_I ; Disable IRQs
STORE_FLAGS_I 1
mov byte [CycleTable],4 ; SlowROM bank
;CLR_FLAG SNES_FLAG_D ; Disable decimal mode
STORE_FLAGS_D 0
pop edx
call HIRQ_Check_Late ; chain
LOAD_CYCLES
test R_Cycles,R_Cycles
jl .no_irq
jmp dword [Event_Handler]
.no_irq:
jmp near CPU_START
ALIGNC
VIRQ_Event:
mov eax,[_EventTrip]
mov [Last_Trip],eax
mov dh,0x80 ; V-IRQ
jmp short HIRQ_Event.do_IRQ
ALIGNC
HIRQ_Event:
mov eax,[_EventTrip]
mov [Last_Trip],eax
mov dh,0x40 ; H-IRQ
;test dl,0x20 ; Vertical IRQ enabled?
;jz .no_virq
;mov dh,0xC0 ; H+V-IRQ
.do_IRQ:
;mov dh,[_NMITIMEN]
;and dh,0x30
;shl dh,2
;mov byte [TIMEUP],0x80 ; Set IRQ recv'd flag
or [IRQ_pin],dh
mov al,[CPU_Execution_Mode]
cmp al,CEM_Waiting_For_Interrupt
jne .no_wai
; WAI delay after interrupt signal: 2 IO
;add dword [_SNES_Cycles],byte 12 ;*
inc word [_PC]
mov byte [CPU_Execution_Mode],CEM_Normal_Execution
.no_wai:
push edx
LOAD_BASE
JUMP_FLAG SNES_FLAG_I,.irq_disabled ; Interrupts disabled?
JUMP_NOT_FLAG SNES_FLAG_E,.native_irq
add dword [_SNES_Cycles],byte 52 ; IRQ processing: 2 IO + 5 bank0
call E1_IRQ
jmp .irq_return
ALIGNC
.native_irq:
add dword [_SNES_Cycles],byte 60 ; IRQ processing: 2 IO + 6 bank0
call E0_IRQ
.irq_return:
.irq_disabled:
pop edx
.no_irq:
mov edi,[FixedTrip]
mov ebx,[Fixed_Event]
mov [_EventTrip],edi
mov [Event_Handler],ebx
;call HIRQ_Check_Late ; chain
jmp near CPU_START
ALIGNC
; This can only trash ebx/edi!
HIRQ_Check:
cmp byte [In_CPU],0
jz .not_in_cpu
GET_CYCLES edi
jmp short .got_cycles
.not_in_cpu:
mov edi,[_SNES_Cycles]
.got_cycles:
push edi
mov bl,[_NMITIMEN]
test bl,0x10 ; H-IRQ enabled?
jz .no_irq
test bl,0x20 ; V-IRQ enabled?
jz .no_virq
mov edi,[_Current_Line_Timing]
cmp [_VTIMEL],edi
jne .no_irq
.no_virq:
mov ebx,[HTimer]
mov edi,[FixedTrip]
cmp [esp],ebx ; Check against current cycle
jae .past_irq ; Are we after the IRQ position?
cmp edi,ebx
jb .past_irq ; Next static event is before IRQ?
mov [_EventTrip],ebx
mov dword [Event_Handler],HIRQ_Event
jmp short .no_irq
.past_irq:
mov ebx,[Fixed_Event]
mov [_EventTrip],edi
mov [Event_Handler],ebx
.no_irq:
pop edi
ret
ALIGNC
; This can only trash ebx/edi!
HIRQ_Check_Late:
mov bl,[_NMITIMEN]
test bl,0x10 ; H-IRQ enabled?
jz .no_irq
test bl,0x20 ; V-IRQ enabled?
jz .no_virq
mov edi,[_Current_Line_Timing]
cmp [_VTIMEL],edi
jne .no_irq
.no_virq:
mov ebx,[HTimer]
mov edi,[FixedTrip]
cmp [Last_Trip],ebx ; Check against last event trip cycle
jae .past_irq ; Are we after the IRQ?
cmp edi,ebx
jb .past_irq ; Next static event is before IRQ?
mov [_EventTrip],ebx
mov dword [Event_Handler],HIRQ_Event
jmp short .no_irq
.past_irq:
mov ebx,[Fixed_Event]
mov [_EventTrip],edi
mov [Event_Handler],ebx
.no_irq:
ret
ALIGNC
EXPORT SNES_R2137 ; SLHV ; This latches the counter for horizontal/vertical data!
push ebx
and byte [IRQ_pin],0x1F
cmp byte [In_CPU],0
jz .not_in_cpu
GET_CYCLES ebx
jmp short .got_cycles
.not_in_cpu:
mov ebx,[_SNES_Cycles]
.got_cycles:
; Set up H counters to cycles executed / 4, V counter to current scanline
; All this below ensures bits 9-15 to be same as bits 1-7
shr ebx,2
mov al,0xFE
cmp ebx,128
jb .before_refresh
add ebx,byte 10 ; Adjust for memory refresh cycles
.before_refresh:
and bh,1
and al,bl
or bh,al
mov [Latched_H],ebx ; Cycles / 4 = rough H counter
mov ebx,[_Current_Line_Timing]
mov al,0xFE
and bh,1
and al,bl
or bh,al
mov [Latched_V],ebx
mov al,0
pop ebx
mov [OPHCT],al
mov [OPVCT],al
ret
ALIGNC
EXPORT SNES_R213C ; OPHCT
xor byte [OPHCT],1
jz .return_high
mov al,[Latched_H]
ret
.return_high:
mov al,[Latched_H+1]
ret
ALIGNC
EXPORT SNES_R213D ; OPVCT
xor byte [OPVCT],1
jz .return_high
mov al,[Latched_V]
ret
.return_high:
mov al,[Latched_V+1]
ret
ALIGNC
EXPORT SNES_R4200 ; NMITIMEN
mov al,[_NMITIMEN]
ret
ALIGNC
EXPORT SNES_R4210 ; RDNMI
mov al,[RDNMI]
mov byte [RDNMI],VERSION_NUMBER_5A22 ; NMI on/off + version #
ret
ALIGNC
EXPORT SNES_R4211 ; TIMEUP
; Should return bit 7 set if IRQ active, bit 6 set if cur line = V count
;mov al,[TIMEUP]
;mov byte [TIMEUP],0
mov al,[IRQ_pin]
test al,0xC0
jz .no_irq
mov al,0x80
.no_irq:
cmp byte [In_CPU],0
jz .not_in_cpu
GET_CYCLES edi
jmp short .got_cycles
.not_in_cpu:
mov edi,[_SNES_Cycles]
.got_cycles:
and byte [IRQ_pin],0x1F ; Clear H/V IRQ
cmp edi,CYCLES_HBLANK_START ; Hblank after display
jae .hblank
cmp edi,CYCLES_DISPLAY_START ; Hblank before display
jae .no_hblank
.hblank:
or al,HVBJOY_IN_HBLANK ; We're in Hblank...
.no_hblank:
ret
ALIGNC
EXPORT SNES_R4212 ; HVBJOY
; Excerpts: Neill Corlett's "SNES Timing: The Brutal Truth" Version: 1.0b
; <-22 dots-> <------------256 dots--------------> <-62 dots->
; 5. Something freaky
; -------------------
; On every scanline, starting at dot 128, the CPU appears to be frozen for 40
; master cycles. DMA is also apparently frozen during this time. This happens
; whether the scanline is visible or not, and whether HDMA is enabled or not.
;
; My current theory is that this time is used for refreshing RAM.
; *End excerpts*
cmp byte [In_CPU],0
jz .not_in_cpu
GET_CYCLES edi
jmp short .got_cycles
.not_in_cpu:
mov edi,[_SNES_Cycles]
.got_cycles:
mov al,[HVBJOY]
cmp edi,CYCLES_HBLANK_START ; Hblank after display
jae .hblank
cmp edi,CYCLES_DISPLAY_START ; Hblank before display
jae .no_hblank
.hblank:
or al,HVBJOY_IN_HBLANK ; We're in Hblank...
.no_hblank:
ret
ALIGNC
EXPORT SNES_W4200 ; NMITIMEN
; a0yx000b a=NMI on/off,y=vert count,x=horiz count,b=joy read
cmp [_NMITIMEN],al
je .no_change
push ebx
mov bl,[_NMITIMEN]
mov [_NMITIMEN],al
mov bh,al
and ebx,0x3030
cmp bh,bl
je .no_irq_change
call HIRQ_Check
.no_irq_change:
pop ebx
.no_change:
and byte [IRQ_pin],0x1F
ret
ALIGNC
EXPORT SNES_W4207 ; HTIMEL
push ebx
mov ebx,[_HTIMEL]
cmp al,bl
je .no_change
mov bl,al
mov [_HTIMEL],ebx
shl ebx,2
; Do some magic for the memory refresh 'missing' cycles
cmp ebx,CYCLES_REFRESH_START ; Before memory refresh
jb .not_in_refresh
cmp ebx,CYCLES_REFRESH_END ; During memory refresh
jb .in_refresh
sub ebx,byte CYCLES_IN_REFRESH
jmp short .not_in_refresh
ALIGNC
.in_refresh:
mov ebx,CYCLES_REFRESH_START ; During memory refresh
.not_in_refresh:
mov [HTimer],ebx
test byte [_NMITIMEN],0x10
jz .no_change
call HIRQ_Check
.no_change:
pop ebx
ret
ALIGNC
EXPORT SNES_W4208 ; HTIMEH
push eax
push ebx
mov ebx,[_HTIMEL]
and al,1
and bh,1
cmp al,bh
je .no_change
mov bh,al
mov [_HTIMEL],ebx
shl ebx,2
; Do some magic for the memory refresh 'missing' cycles
cmp ebx,CYCLES_REFRESH_START ; Before memory refresh
jb .not_in_refresh
cmp ebx,CYCLES_REFRESH_END ; During memory refresh
jb .in_refresh
sub ebx,byte CYCLES_IN_REFRESH
jmp short .not_in_refresh
ALIGNC
.in_refresh:
mov ebx,CYCLES_REFRESH_START ; During memory refresh
.not_in_refresh:
mov [HTimer],ebx
test byte [_NMITIMEN],0x10
jz .no_change
call HIRQ_Check
.no_change:
pop ebx
pop eax
ret
ALIGNC
EXPORT SNES_W4209 ; VTIMEL
push ebx
mov ebx,[_VTIMEL]
cmp al,bl
je .no_change
mov [_VTIMEL],al
test byte [_NMITIMEN],0x20
jz .no_change
call HIRQ_Check
.no_change:
pop ebx
ret
ALIGNC
EXPORT SNES_W420A ; VTIMEH ; Handled in screen core!
push eax
push ebx
mov ebx,[_VTIMEL]
and al,1
and bh,1
cmp al,bh
je .no_change
mov [_VTIMEH],al
test byte [_NMITIMEN],0x20
jz .no_change
call HIRQ_Check
.no_change:
pop ebx
pop eax
ret
ALIGNC
EXPORT SNES_W420D ; MEMSEL ; FastROM switch
push eax
and eax,byte 1
;shl eax,0x17 ; Set bit 7 of bank to switch flag
shl eax,7 ; Set bit 7 to switch flag
test [(_cpu_65c816_PB_Shifted+2)],al ; Check bus speed against PB
mov [MEMSEL],eax
mov al,5
jnz .fastrom
mov al,4
.fastrom:
mov [CycleTable],al ; Update bus speed
pop eax
ret
ALIGNC
EXPORT SNES_W4210 ; RDNMI
mov byte [RDNMI],VERSION_NUMBER_5A22 ; NMI on/off + version
ret
ALIGNC
EXPORT SNES_W4211 ; TIMEUP
;mov byte [TIMEUP],0
and byte [IRQ_pin],0x1F
ret