# LibPSn00b Example Programs
# Part of the PSn00bSDk project
#
# TurboBoot Example by Lameguy64
#


# Uncomment either PAR or XPLORER depending on the cartridge
# you're going to use (makes disabling turbo boot via switch to work)

#.set PAR, 0
#.set XPLORER, 1


.set noreorder

.include "cop0.inc"				# Contains definitions for cop0 registers

.set SP_base,		0x801ffff0
.set BREAK_ADDR,	0xa0000040	# cop0 breakpoint vector address


.set RAM_buff,		2048
.set RAM_handle,	2052
.set RAM_tcb,		2056
.set RAM_evcb,		2060
.set RAM_stack,		2064
.set RAM_psexe,		2068


.set EXE_pc0,		0			# PS-EXE header offsets
.set EXE_gp0,		4
.set EXE_taddr,		8
.set EXE_tsize,		12
.set EXE_daddr,		16
.set EXE_dsize,		20
.set EXE_baddr,		24
.set EXE_bsize,		28
.set EXE_spaddr,	32
.set EXE_sp_size,	36
.set EXE_sp,		40
.set EXE_fp,		44
.set EXE_gp,		48
.set EXE_ret,		52
.set EXE_base,		56
.set EXE_datapos,	60


.section .text


# ROM header
#
# The Licensed by... strings are essential otherwise the BIOS will not
# execute the boot vectors. Always make sure the tty message fields (string
# after Licensed by) must be no more than 80 bytes long and must have a null
# terminating byte.
#
# Postboot vector isn't particularly practical as its only executed in between
# the PS boot logo and the point where game execution occurs.
#
header:
	# Postboot vector
	.word	0
	.ascii	"Licensed by Sony Computer Entertainment Inc."
	.ascii 	"Not officially licensed or endorsed by Sony Computer Entertainment Inc."
	
	.balign	0x80	# This keeps things in the header aligned
	
	# Preboot vector
	.word	preboot
	.ascii	"Licensed by Sony Computer Entertainment Inc."
	.ascii 	"Cart ROM example for PSn00bSDK https://github.com/lameguy64/psn00bsdk"
	
	.balign	0x80	# This keeps things in the header aligned
	
	
# Preboot vector
#
# All it does is it initializes a breakpoint vector at 0x40
# and sets a cop0 breakpoint at 0x80030000 to perform a midboot
# exploit as preboot doesn't have the kernel area initialized.
#
preboot:
	
	li		$v0, 1
	
.ifdef XPLORER
	lui		$a0, 0x1f06				# Read switch status for Xplorer
	lbu		$v0, 0($a0)
.endif

.ifdef PAR
	lui		$a0, 0x1f02				# Read switch status for PAR/GS devices
	lbu		$v0, 0x18($a0)
.endif

	nop
	andi	$v0, 0x1
	beqz	$v0, .no_rom			# If switch is off don't install hook
	nop								# and effectively disables the cartridge
	
	li		$v0, BREAK_ADDR			# Patch a jump at cop0 breakpoint vector
	
	li		$a0, 0x3c1a1f00			# lui $k0, $1f00
	sw		$a0, 0($v0)
	la		$a1, entry				# ori $k0, < address to code entrypoint >
	andi	$a1, 0xffff
	lui		$a0, 0x375a
	or		$a0, $a1
	sw		$a0, 4($v0)
	li		$a0, 0x03400008			# jr  $k0
	sw		$a0, 8($v0)
	sw		$0 , 12($v0)			# nop
	
	lui		$v0, 0xffff				# Set BPCM and BDAM masks
	ori		$v0, 0xffff
	mtc0	$v0, BDAM
	mtc0	$v0, BPCM
	
	
	li		$v0, 0x80030000			# Set break on PC and data-write address
	
	mtc0	$v0, BDA				# BPC break is for compatibility with no$psx
	mtc0	$v0, BPC				# as it does not emulate break on BDA
	
	lui		$v0, 0xeb80				# Enable break on data-write and and break
	mtc0	$v0, DCIC				# on PC to DCIC control register
	
.no_rom:

	jr		$ra						# Return to BIOS
	nop


# Actual ROM entrypoint
.global entry
entry:
	
	mtc0	$0 , DCIC				# Clear DCIC register
	
	la		$sp, SP_base			# Set stack base
	la		$gp, 0x8000c000			# Set GP address as RAM base addr in this case
	
	jal		SetDefaultExitFromException		# Set default exit handler just in case
	nop
	jal		ExitCriticalSection		# Exit out of critical section (brings back interrupts)
	nop
	
	# Beyond this point, the PS1 is in full control to the ROM
	
	la		$a0, m_banner			# Print out program banner
	jal		printf
	addiu	$sp, -4
	addiu	$sp, 4
	
	la		$a0, m_cdinit
	jal		printf
	addiu	$sp, -4
	addiu	$sp, 4
	
	jal		_96_init				# Initialize the CD
	nop
	
	la		$a0, m_ok				# Print OK message if init didn't crash
	jal		printf
	addiu	$sp, -4
	addiu	$sp, 4
	
	la		$a0, m_readfile
	la		$a1, s_systemcnf
	jal		printf
	addiu	$sp, -8
	addiu	$sp, 8
	
	la		$a0, s_systemcnf		# Attempt to open the SYSTEM.CNF file on CD
	li		$a1, 1
	jal		open
	addiu	$sp, -8
	addiu	$sp, 8
	
	bltz	$v0, .no_systemcnf		# Fallback to loading PSX.EXE if not found
	nop
	
	sw		$v0, RAM_handle($gp)	# Save file handle
	
	move	$a0, $v0				# Read file contents of SYSTEM.CNF
	move	$a1, $gp
	li		$a2, 0x0800
	jal		read
	addiu	$sp, -12
	addiu	$sp, 12
	
	lw		$a0, RAM_handle($gp)	# Close file
	jal		close
	addiu	$sp, -4
	addiu	$sp, 4
	
	la		$a0, m_ok				# Output ok message
	jal		printf
	addiu	$sp, -4
	addiu	$sp, 4
	
	# Parse CNF file
	
	la		$a0, m_parsecnf
	jal		printf
	nop
	
	la		$a1, s_tcb				# Get TCB number
	jal		strcasestr
	move	$a0, $gp
	jal		skipspace				# Skip spaces
	addiu	$a0, $v0, 3
	addiu	$a0, $v0, -2			# Step two charactters back and inject '0x'
	li		$v0, '0'
	sb		$v0, 0($a0)
	li		$v0, 'x'
	sb		$v0, 1($a0)
	jal		atoi
	addiu	$sp, -4
	addiu	$sp, 4
	move	$s1, $v0
	
	la		$a1, s_evcb				# Get EVCB number
	jal		strcasestr
	move	$a0, $gp
	jal		skipspace
	addiu	$a0, $v0, 5
	addiu	$a0, $v0, -2
	li		$v0, '0'
	sb		$v0, 0($a0)
	li		$v0, 'x'
	sb		$v0, 1($a0)
	jal		atoi
	addiu	$sp, -4
	addiu	$sp, 4
	move	$s0, $v0
	
	la		$a1, s_stack			# Get STACK address
	jal		strcasestr
	move	$a0, $gp
	jal		skipspace
	addiu	$a0, $v0, 5
	addiu	$a0, $v0, -2
	li		$v0, '0'
	sb		$v0, 0($a0)
	li		$v0, 'x'
	sb		$v0, 1($a0)
	jal		atoi
	addiu	$sp, -4
	addiu	$sp, 4
	move	$s2, $v0
	
	la		$a1, s_boot				# Get the PS-EXE file name
	jal		strcasestr
	move	$a0, $gp
	
	jal		skipspace				# Skip spaces
	addiu	$a0, $v0, 4
	
	addiu	$a0, $gp, RAM_psexe		# Extract the line
	jal		getline
	move	$a1, $v0
	
	la		$a0, m_ok				# Print successful parsing
	jal		printf
	addiu	$sp, -4
	addiu	$sp, 4
	
	la		$a0, m_readfile
	addiu	$a1, $gp, RAM_psexe
	jal		printf
	addiu	$sp, -8
	addiu	$sp, 8
	
	b		.do_load				# Proceed loading PS-EXE
	addiu	$a0, $gp, RAM_psexe
	
.no_systemcnf:						# Load fallback

	la		$a0, m_notfound
	jal		printf
	addiu	$sp, -4
	addiu	$sp, 4
	
	la		$a0, m_fallback
	jal		printf
	addiu	$sp, -4
	addiu	$sp, 4
	
	li		$s0, 0x10				# Default EvCBs and TCBs
	li		$s1, 0x04
	li		$s2, SP_base			# Default stack
	la		$a0, s_psxexe			# Attempt loading PSX.EXE
	
.do_load:

	jal		LoadExe					# Load PS-EXE
	move	$a1, $gp
	
	beqz	$v0, load_fail
	nop

	la		$a0, m_ok
	jal		printf
	addiu	$sp, -4
	addiu	$sp, 4
	
	sw		$s2, EXE_sp($gp)		# Patch the header
	sw		$s2, EXE_spaddr($gp)
	
	la		$a0, m_boot
	jal		printf
	addiu	$sp, -4
	addiu	$sp, 4
	
	jal		EnterCriticalSection	# Disable interrupt handling
	nop
	
	move	$a0, $s0				# Set configuration (EvCBs and TCBs)
	move	$a1, $s1
	move	$a2, $s2
	jal		SetConf
	addiu	$sp, -12
	addiu	$sp, 12
	
	move	$a0, $gp				# Transfer execution
	move	$a1, $0
	move	$a2, $0
	jal		DoExec
	addiu	$sp, -12
	addiu	$sp, 12
	

load_fail:							# Fail state
	la		$a0, m_loadfail
	jal		printf
	nop
.fail_loop:
	b		.fail_loop
	nop

	
.include "parse.inc"
.include "bios.inc"


# Strings

s_boot:
	.asciz "BOOT"
	.balign 4
s_tcb:
	.asciz "TCB"
	.balign 4
s_evcb:
	.asciz "EVENT"
	.balign 4
s_stack:
	.asciz "STACK"
	.balign 4
s_systemcnf:
	.asciz "cdrom:SYSTEM.CNF;1"
	.balign 4
s_psxexe:
	.asciz "cdrom:PSX.EXE;1"
	.balign 4
	

# Message strings

m_banner:
	.asciz "\nCARTROM Bootstrap Example by Lameguy64\nPart of the PSn00bSDK Project\n\n"
	.balign 4
m_cdinit:
	.asciz "Initializing CD-ROM (BIOS)... "
	.balign 4
m_readfile:
	.asciz "Attempting to read %s... "
	.balign 4
m_parsecnf:
	.asciz "Parsing CNF file... "
	.balign 4
m_fallback:
	.asciz "Falling back to loading PSX.EXE... "
	.balign 4
m_notfound:
	.asciiz "Not found.\n"
	.balign 4
m_ok:
	.asciz "Ok.\n"
	.balign 4
m_boot:
	.asciz "Boot!\n"
	.balign 4
m_loadfail:
	.asciz "Failed to load PS-EXE file.\n"
	.balign 4
