#Lang "fblite"

#Include "crt.bi"
#Ifdef __FB_WIN32__
	#Include "windows.bi"
#Else
	#Include "crt/sys/linux/time.bi"
#EndIf
#Include "SDL\SDL.bi"
#Include "SDL\SDL_audio.bi"
Dim Shared wanted As SDL_AudioSpec
Dim Shared SCR_WIDTH, SCR_HEIGHT As Integer
Dim Shared video as SDL_Surface ptr
Dim Shared event as SDL_Event
Dim Shared As UInteger Ptr sdlpixel
SCR_WIDTH = 512
SCR_HEIGHT = 480

#Include "file.bi"
#Include "modShared.bi"
#Include "font.bi"
#Include "modMappers.bas"
#Include "modAPU.bas"
#Include "mod6502.bas"
#Include "modPPU.bas"
#Include "modInput.bas"

Const As String build = "v0.5.1.11"
Print "MoarNES " + build + " (c)2010-2011 Mike Chambers"
Print "[A NES emulator written in FreeBASIC]"
Print
ParseCommandLine
Print "Initializing SDL... ";
ressdl = SDL_Init(SDL_INIT_EVERYTHING)
If ressdl <> 0 Then
	Print "failed!"
	End
Else
	Print "OK!"
End If

Print "Creating SDL screen context... ";
video = SDL_SetVideoMode(SCR_WIDTH, SCR_HEIGHT, 32, 0)   'or SDL_FULLSCREEN
If video = 0 Then
	Print "failed!"
	SDL_Quit
	End
Else
	Print "OK!"
End If
SDL_WM_SetCaption("MoarNES " + build, 0)
SDL_ShowCursor(SDL_DISABLE)

If FileExists("keymap.cfg") = 0 Then
	Print "Controller configuration file not found!"
	Print
	SetupKeys
	dosetkeys = 0
Else
	Open "keymap.cfg" For Binary As #1
	For loadkeys = 0 To 7
		Get #1, , keymap1(loadkeys)
	Next loadkeys
	Close #1
End If

If dosetkeys = 1 Then SetupKeys

Dim audiothread As Any Ptr
Declare Sub audioloop()
If nosound = 0 Then audiothread = ThreadCreate(@audioloop)

Print "Loading ROM: " + Command(1)
If FileExists(Command(1)) = 0 Then
	Print "File does not exist!"
	SDL_Quit()
	End
End If
Dim tmpbyte As UByte
Open Command(1) For Binary As #1
Get #1, , ROM.id
Get #1, , tmpbyte: ROM.prgcount = Cast(UShort, tmpbyte) * 16
Get #1, , tmpbyte: ROM.chrcount = Cast(UShort, tmpbyte) * 8
Get #1, , ROM.flags1
Get #1, , ROM.flags2
If ROM.id <> "NES" + Chr$(&h1A) Then Close #1: Print "Not a valid iNES ROM file!": End

If ROM.flags2 = Asc("D") Then ROM.flags2 = 0 'fix DISKDUDE! crap if it's there
ROM.mapper = (ROM.flags1 Shr 4) Or (ROM.flags2 And &hF0)
Print "Mapper: " + Str$(ROM.mapper)
If (ROM.flags1 And 8) = 8 Then ROM.mirroring = 2 Else ROM.mirroring = ROM.flags1 And 1
Select Case ROM.mirroring
	Case 0 'horizontal
		ntmap(0) = &h2000
		ntmap(1) = &h2000
		ntmap(2) = &h2400
		ntmap(3) = &h2400
	Case 1 'vertical
		ntmap(0) = &h2000
		ntmap(1) = &h2400
		ntmap(2) = &h2000
		ntmap(3) = &h2400
	Case 2 'four-screen
		ntmap(0) = &h2000
		ntmap(1) = &h2400
		ntmap(2) = &h2800
		ntmap(3) = &h2C00
End Select
seek #1, 17
For n = 1 To ROM.prgcount*1024
	Get #1, , PRGMEM(n-1)
Next n
For n = 1 To ROM.chrcount*1024
	Get #1, , CHRMEM(n-1)
Next n
SetupMapper()
Print "Loaded " + Str$(ROM.prgcount) + " KB PRG, " + Str$(ROM.chrcount) + " KB CHR."
Close #1

init6502
reset6502

'Open "c:\audtest.raw" For Output As #2
Dim curtimer, lasttimer, timerfreq As ULongInt

#Ifdef __FB_WIN32__
	'Dim self As HANDLE
	'self = GetCurrentThread()
	'If SetThreadPriority(self, THREAD_PRIORITY_TIME_CRITICAL) = 0 Then
	'	Print "WARNING: Changing the timer thread priority failed. ("; GetLastError; ")"
	'End If
	QueryPerformanceFrequency(CPtr(Any Ptr, @timerfreq))
	'Print "[DEBUG] Timer resolution: "; Cast(ULongInt, timerfreq)
#Else
	'Dim timing As timeval
	'timerfreq = 1000000
	timerfreq = 1000
#EndIf

Dim timertemp As ULongInt
cpurunning = 1
Do
	PPU.vramwrite = 0
	For n = 0 To 255
		spritebuffer(n) = spriteRAM(n)
	Next n
	For curscanline = 0 To 239
		If (frame Mod frameskip) = 0 Then
			For n = 0 To 255
				sprfront(n) = 0: sprback(n) = 0: backgnd(n) = 0
				outputNES(curscanline, n) = VRAM(&h3F00)
			Next n		
			drawsprites(curscanline)
			If accuracy = 1 Then
				exec6502(85)
				For bgx = 0 To 255
					drawbackground(curscanline, bgx)
					If (sprback(bgx) > 0) And (PPU.sprvisible <> 0) Then outputNES(curscanline, bgx) = sprback(bgx)
					If (backgnd(bgx) > 0) And (PPU.bgvisible <> 0) Then outputNES(curscanline, bgx) = backgnd(bgx)
					If (sprfront(bgx) > 0) And (PPU.sprvisible <> 0) Then outputNES(curscanline, bgx) = sprfront(bgx)
				Next bgx
			Else
				For bgx = 0 To 255
					If (bgx Mod 3) = 0 Then
						exec6502(1)
					End If
					drawbackground(curscanline, bgx)
					If (sprback(bgx) > 0) And (PPU.sprvisible <> 0) Then outputNES(curscanline, bgx) = sprback(bgx)
					If (backgnd(bgx) > 0) And (PPU.bgvisible <> 0) Then outputNES(curscanline, bgx) = backgnd(bgx)
					If (sprfront(bgx) > 0) And (PPU.sprvisible <> 0) Then outputNES(curscanline, bgx) = sprfront(bgx)
				Next bgx
			End If
		Else
			exec6502(85)
		End If
		exec6502(29)
	Next curscanline

	PPU.vblank = 1
	PPU.vramwrite = 1
	If PPU.nmivblank Then nmi6502()
	For curscanline = 240 To 262
		exec6502(114)
	Next curscanline

	SDL_LockSurface (video)
	For drawy = 0 To 479
		For drawx = 0 To 511
			sdlpixel = video->pixels + drawy * video->pitch + drawx * len( integer )
			*sdlpixel = paletteNES(outputNES(drawy Shr 1, drawx Shr 1))
		Next drawx
	Next drawy
	SDL_UnlockSurface (video)
	SDL_Flip video

	GetInput()

	Do
		#Ifdef __FB_WIN32__
			QueryPerformanceCounter(CPtr(Any Ptr, @curtimer))
		#Else
			'gettimeofday(@timing, NULL)
			'curtimer = timing.tv_usec
			curtimer = SDL_GetTicks()
		#EndIf
		timertemp = Abs(Cast(ULongInt, curtimer) - Cast(ULongInt, lasttimer))
		'calculate auto-frameskip
		If autoskip = 1 Then
			If timertemp >= Cast(ULongInt, timerfreq / 57) Then frameskip = frameskip + 1
			If timertemp < Cast(ULongInt, timerfreq / 60) Then frameskip = frameskip - 1
			If frameskip > 30 Then frameskip = 30
			If frameskip < 1 Then frameskip = 1
		End If
		'end auto-frameskip calcs
		If timertemp >= Cast(ULongInt, timerfreq / 60) Then Exit Do
		#Ifdef __FB_WIN32__
			SwitchToThread() 'give up remainder of CPU timeslice if we shouldn't resume yet
		#EndIf
	Loop
	lasttimer = curtimer
	frame=frame+1
Loop Until cpurunning = 0
If nosound = 0 Then ThreadWait(audiothread)
SDL_Quit()
End

Sub ParseCommandLine()
	If Len(Command$) = 0 Then
		Print "You must specify the NES ROM filename on the command line."
		Print "Syntax: moarnes.exe romfile.nes [options]"
		Print
		Print "Options:"
		Print "    -fskip #      - Enable frameskip, might be required on slow systems."
		Print "                    Will show every # frames."
		Print
		Print "    -setkeys      - Remap controller keys. May be used without a ROM file."
		Print
		Print "    -silent       - Disable audio output."
		Print
		Print "    -scanline     - Scanline PPU render accuracy instead of cycle accuracy."
		Print "                    Can slightly improve performance, but breaks some games."
		End
	End If
	frameskip = 1
	autoskip = 1
	For n = 2 To 15
		Select Case LCase$(Command(n))
			Case "-fskip"
				n = n + 1
				frameskip = Val(Command(n))
				autoskip = 0
			Case "-silent"
				nosound = 1
			Case "-setkeys"
				dosetkeys = 1
			Case "-scanline"
				accuracy = 1
			Case ""
				Exit For
		End Select
	Next n
End Sub

Sub audioloop()
	'Print "Initializing audio... ";
	buflen = 2048
	wanted.freq = 44100
	wanted.format = AUDIO_S16
	wanted.channels = 1
	wanted.samples = 2048
	wanted.callback = @fillaudio
	wanted.userdata = NULL
	audinit = SDL_OpenAudio(@wanted, NULL)
	If audinit < 0 Then
		'Print "failed! ("; *SDL_GetError(); ")"
	Else
		'Print "OK!"
		SDL_PauseAudio(0)
	End If
	Do
		SDL_Delay(1000)
	Loop Until cpurunning = 0
End Sub
