/*
 * Snes9x - Portable Super Nintendo Entertainment System (TM) emulator.
 *
 * (c) Copyright 1996, 1997, 1998 Gary Henderson (gary@daniver.demon.co.uk) and
 *                                Jerremy Koot (jkoot@euronet.nl)
 *
 * Super FX C emulator code 
 * (c) Copyright 1997, 1998 Lestat (lstat@hotmail.com) and
 *                          Gary Henderson.
 * Super FX assembler emulator code (c) Copyright 1998 zsKnight and _Demo_.
 *
 * DSP1 emulator code (c) Copyright 1998 Lestat and Gary Henderson.
 * DOS port code contains the works of other authors. See headers in
 * individual files.
 *
 * Permission to use, copy, modify and distribute Snes9x in both binary and
 * source form, for non-commercial purposes, is hereby granted without fee,
 * providing that this license information and copyright notice appear with
 * all copies and any derived work.
 *
 * This software is provided 'as-is', without any express or implied
 * warranty. In no event shall the authors be held liable for any damages
 * arising from the use of this software.
 *
 * Snes9x is freeware for PERSONAL USE only. Commercial users should
 * seek permission of the copyright holders first. Commercial use includes
 * charging money for Snes9x or software derived from Snes9x.
 *
 * The copyright holders request that bug fixes and improvements to the code
 * should be forwarded to them so everyone can benefit from the modifications
 * in future versions.
 *
 * Super NES and Super Nintendo Entertainment System are trademarks of
 * Nintendo Co., Limited and its subsidiary companies.
 */

#include "asmstruc.h"
#include "asmops.h"
#include "spcops.h"

.globl MainAsmLoop

.text
	.align 4
.globl S9xMainLoop
S9xMainLoop:
	pushl %ebp
	pushl %edi
	pushl %esi
	pushl %ebx
	LOAD_REGISTERS
.L9:
	cmpb $0,APUExecuting
	je .L12
	decl APUCycles
	jnz .L12
	movl SPCTo65c816Ratio,%eax
	movl %eax, APUCycles
#ifdef DEBUGGER
	testb $2,APUFlags
	je .L14
	STORE_REGISTERS
	ccall S9xTraceAPU
	LOAD_REGISTERS
.L14:
#endif
	xorl %eax,%eax
	movb (APUPC),%al
	movl S9xApuOpcodes(,%eax,4),%eax
	call *%eax
.L12:
	cmpl $0, Flags
	je .L15
	movl Flags, %eax
	testb %al, %al
	jge .NO_NMI
	andb $~NMI_FLAG, %al
	movl %eax, Flags
	cmpb $0, WaitingForInterrupt
	je .L17
	movb $0, WaitingForInterrupt
	incl PC
.L17:
	call S9xOpcode_NMI
.NO_NMI:
	movl Flags, %eax
	testl $DELAYED_NMI_FLAG2, %eax
	jz .NO_DELAYED_NMI
	andl $~DELAYED_NMI_FLAG2, %eax
	orb $NMI_FLAG, %al
	movl %eax, Flags
.NO_DELAYED_NMI:
	testb $DELAYED_NMI_FLAG, %al
	jz .NO_DELAYED_NMI2
	andb $~DELAYED_NMI_FLAG, %al
	orl $DELAYED_NMI_FLAG2, %eax
	movl %eax, Flags
.NO_DELAYED_NMI2:	
#ifdef DEBUGGER
	testb $BREAK_FLAG, Flags
	jz .NO_BREAK_POINTS
	pushl %esi
	pushl %ebx
	movl $S9xBreakpoint, %esi
	movb PB, %bl
	xorl %edx, %edx
	movl PC, %ecx
	subl PCBase, %ecx

.BREAK_CHECK_LOOP:
	movzwl %dx, %eax
	sall $2, %eax
	cmpb $0, S9xBreakpoint(%eax)
	je .BREAK_MATCH_FAILED
	cmpb %bl, 1(%esi, %eax)
	jne .BREAK_MATCH_FAILED
	movzwl 2(%esi, %eax), %eax
	cmpl %ecx, %eax
	jne .BREAK_MATCH_FAILED
	orb $DEBUG_MODE_FLAG, Flags
.BREAK_MATCH_FAILED:
	incw %dx
	cmpw $6, %dx
	jne .BREAK_CHECK_LOOP
	popl %ebx
	popl %esi
.NO_BREAK_POINTS:
#endif

#if defined(__linux) && !defined(__msdos)
	movl Flags, %eax
	testl $PROCESS_SOUND_FLAG,%eax
	jz .NO_SOUND
	STORE_REGISTERS
	ccall S9xUnixProcessSound
	LOAD_REGISTERS
.NO_SOUND:
#endif
#ifdef DEBUGGER
	movl Flags, %eax
	testb $DEBUG_MODE_FLAG,%al
	jnz .L31
#else
	movl Flags, %eax
#endif
	testb $SCAN_KEYS_FLAG, %al
	jnz .L31
.L28:
#ifdef DEBUGGER
	testb $TRACE_FLAG, %al
	jz .NO_TRACE
	STORE_REGISTERS
	ccall S9xTrace
	LOAD_REGISTERS
.NO_TRACE:
	movl Flags, %eax
	testb $SINGLE_STEP_FLAG, %al
	jz .L15
	andb $~SINGLE_STEP_FLAG, %al
	orb $DEBUG_MODE_FLAG, %al
	movl %eax, Flags
#endif
.L15:
	xorl %eax,%eax

#ifdef VAR_CYCLES
	addl MemSpeed, CYCLES
	movb (PC), %al
#else
	xorl %ecx, %ecx
	movl CPUSpeed, %edx
	movb (PC), %al
	movb (%edx,%eax), %cl
	addl %ecx, CYCLES
#endif
	movl CPUOpcodes, %ecx
	movl (%ecx,%eax,4), %eax
#ifdef CPU_SHUTDOWN
	movl PC, PCAtOpcodeStart
#endif	
	incl PC
	jmp *%eax
MainAsmLoop:
	cmpl NextEvent, CYCLES
	jl .L9
	STORE_REGISTERS
	call S9xDoHBlankProcessing
	LOAD_REGISTERS
	jmp .L9
.L31:
	S9xPackStatus S9xMainLoop
	STORE_REGISTERS
	subl PCBase, PC
	movw %di, PCR
	subl APURAM, APUPC
	movw %bp, APUPCR
	APUS9xPackStatus S9xMainLoop
	movl Flags, %eax
	testb $SCAN_KEYS_FLAG, %al
	jz .NoScanKeys
	andb $~SCAN_KEYS_FLAG, %al
	movl %eax, Flags
#ifdef DEBUGGER
	testl $FRAME_ADVANCE_FLAG, %eax
	jnz .NoScanKeys
#endif
	ccall S9xSyncSpeed
.NoScanKeys:
	popl %ebx
	popl %esi
	popl %edi
	popl %ebp
	ret

.globl S9xDoHBlankProcessing

S9xDoHBlankProcessing:
	pushl %edi
	pushl %esi
	pushl %ebx
#ifdef CPU_SHUTDOWN
	incl WaitCounter
#endif
	movb WhichEvent,%bl
	andl $255,%ebx
	cmpl $1,%ebx
	je .L160
	jg .L196
	testl %ebx,%ebx
	je .L158
	jmp .L157
.L196:
	cmpl $2,%ebx
	je .L189
	jmp .L157
.L158:
	movb HDMA,%dl
	testb %dl,%dl
	je .L157
	movl V_Counter, %eax
	cmpw ScreenHeight, %ax
	jae .L157 
	xorl %eax,%eax
	movb %dl,%al
	pushl %eax
	ccall S9xDoHDMA
	movb %al,HDMA
	addl $4,%esp
	jmp .L157
.L160:
#ifdef EXECUTE_SUPERFX_PER_LINE
	ccall S9xSuperFXExec
#endif
	movl H_Max,%eax
	movl Cycles,%ecx
	subl %eax,%ecx
	movl %ecx,Cycles
	movl V_Counter,%ecx
	incl %ecx
	movl $-1,NextEvent
	movl %ecx,V_Counter
	testb $0xff, PAL
	jz .ntsc_tv
	cmpl $312,%ecx
	jbe .L161
	jmp .endofframe
.ntsc_tv:
	cmpl $262,%ecx
	jbe .L161
.endofframe:
	movw SavedOAMAddr,%ax
	xorl %edx,%edx
	movl Flags,%ecx
	movw %ax,OAMAddr
	movb %dl,OAMFlip
	orl $16,%ecx
	movl %edx,V_Counter
	movb %dl,HVBeamCounterLatched
	movl %ecx,Flags
	ccall S9xStartHDMA
.L161:
	movb VTimerEnabled,%al
	testb %al,%al
	je .L162
	movb HTimerEnabled,%dl
	testb %dl,%dl
	jne .L162
	xorl %eax,%eax
	movw IRQVBeamPos,%ax
	movl V_Counter,%ecx
	cmpl %eax,%ecx
	jne .L162
	orb $2,IRQActive
	movb WaitingForInterrupt,%al
	testb %al,%al
	je .L163
	incl PCS
	movb %dl,WaitingForInterrupt
.L163:
	movb DisableIRQ,%al
	testb %al,%al
	jne .L162
	testb $4,PP
	jne .L162
	PUSH_REGISTERS
	LOAD_REGISTERS
	call S9xOpcode_IRQ
	STORE_REGISTERS
	POP_REGISTERS
.L162:
	xorl %eax,%eax
	movw ScreenHeight,%ax
	movl V_Counter,%edx
	incl %eax
	cmpl %eax,%edx
	jne .L165
	ccall S9xEndScreenRefresh
	xorl %ecx,%ecx
	movb %cl,FirstSprite
	movb %cl,HDMA
	movl FillRAM,%eax
	movl $128,%edx
	movb %dl,16912(%eax)
	movb Brightness,%al
	xorl %ecx,%ecx
	movb %al,MaxBrightness
	movl FillRAM,%eax
	movb 8448(%eax),%cl
	shrb $7,%cl
	movb %cl,ForcedBlanking
	movb 16896(%eax),%dl
	testb %dl,%dl
	jge .L166
	movb WaitingForInterrupt,%cl
	testb %cl,%cl
	je .L167
	incl PCS
	movb $0,WaitingForInterrupt
.L167:
	movl Flags,%ecx
	orl $64,%ecx
	movl %ecx,Flags
.L166:
.L165:
	xorl %eax, %eax
	movw ScreenHeight,%ax
	addl $3,%eax
	cmpl V_Counter,%eax
	jne .NoJoypadUpdate
	ccall S9xUpdateJoypads
.NoJoypadUpdate:
	movl V_Counter,%eax
	cmpl $1,%eax
	jne .L177
	movl FillRAM,%eax
	xorl %edx,%edx
	movb %dl,16912(%eax)
	ccall S9xStartScreenRefresh
.L177:
	movl V_Counter,%edx
	testl %edx,%edx
	je .L178
	xorl %eax,%eax
	movw ScreenHeight,%ax
	incl %eax
	cmpl %eax,%edx
	jae .L178
	movb V_Counter,%al
	decb %al
	andl $255,%eax
	pushl %eax
	ccall RenderLine
	addl $4,%esp
.L178:
	movl APUTimerErrorCounter,%eax
	incl %eax
	movl %eax,APUTimerErrorCounter
	andl $63,%eax
	jz .L157

	movb APUTimerEnabled + 2,%cl
	testb %cl,%cl
	je .L179
	movw APUTimer + 4,%ax
	addl $4,%eax
	movw %ax,APUTimer + 4
	cmpw %ax,APUTimerTarget + 4
	ja .L179
.L182:
	movl APURAM,%edx
	movb 255(%edx),%al
	incb %al
	andb $15,%al
	movb %al,255(%edx)
	movw APUTimerTarget + 4,%dx
	movw APUTimer + 4,%ax
	movl APUWaitCounter,%ecx
	movl $1,%ebx
	subl %edx,%eax
	incl %ecx
	movb %bl,APUExecuting
	movw %ax,APUTimer + 4
	movl %ecx,APUWaitCounter
	cmpw %dx,%ax
	jae .L182
.L179:
	testb $1,V_Counter
	je .L157
	movb APUTimerEnabled,%al
	testb %al,%al
	je .L185
	incw APUTimer
	movw APUTimerTarget,%ax
	cmpw %ax,APUTimer
	jb .L185
	movl APURAM,%edx
	movb 253(%edx),%al
	incb %al
	andb $15,%al
	movb %al,253(%edx)
	movl APUWaitCounter,%edx
	movl $1,%ecx
	incl %edx
	movw $0,APUTimer
	movb %cl,APUExecuting
	movl %edx,APUWaitCounter
.L185:
	movb APUTimerEnabled + 1,%al
	testb %al,%al
	je .L157
	incw APUTimer + 2
	movw APUTimerTarget + 2,%ax
	cmpw %ax,APUTimer + 2
	jb .L157
	movl APURAM,%edx
	movb 254(%edx),%al
	incb %al
	andb $15,%al
	movb %al,254(%edx)
	movl APUWaitCounter,%edx
	movl $1,%ecx
	incl %edx
	movw $0,APUTimer + 2
	movb %cl,APUExecuting
	movl %edx,APUWaitCounter
	jmp .L157
.L189:
	movb VTimerEnabled,%al
	testb %al,%al
	je .L191
	xorl %eax,%eax
	movw IRQVBeamPos,%ax
	movl V_Counter,%edx
	cmpl %eax,%edx
	jne .L157
.L191:
	orb $1,IRQActive
	movb WaitingForInterrupt,%al
	testb %al,%al
	je .L192
	movb $0, WaitingForInterrupt
	incl PCS
.L192:
	movb DisableIRQ,%al
	testb %al,%al
	jne .L157
	testb $4,PP
	jne .L157
	PUSH_REGISTERS
	LOAD_REGISTERS
	call S9xOpcode_IRQ
	STORE_REGISTERS
	POP_REGISTERS
.L157:
	movl HBlankStart,%edx
	movl NextEvent,%ecx
	cmpl %edx,%ecx
#if 0
	jge .L199
	xorl %eax,%eax
	movw ScreenHeight,%ax
	movl V_Counter,%ecx
	incl %eax
	cmpl %eax,%ecx
	jb .L198
.L199:
#else
	jl .L198
#endif
	movb $1,%bl
	movl H_Max,%edx
	jmp .L200
.L198:
	xorl %ebx,%ebx
.L200:
	movswl HTimerPosition,%esi
	cmpl %edx,%esi
	jge .L201
	movl NextEvent,%eax
	cmpl %eax,%esi
	jle .L201
	movb VTimerEnabled,%cl
	testb %cl,%cl
	je .L202
	xorl %eax,%eax
	movw IRQVBeamPos,%ax
	movl V_Counter,%ecx
	cmpl %eax,%ecx
	jne .L201
.L202:
	movb $2,%bl
	movl %esi,%edx
.L201:
	movb %bl,WhichEvent
	popl %ebx
	popl %esi
	movl %edx,NextEvent
	popl %edi
	ret
