tinymon_pif.s 5.5 KB
/*
 * tinymon_pif.s
 */
#include "sys/regdef.h"
#include "sys/asm.h"
#include "tinymon.h"

        .text
        .set noat			# allows us to use register $1 ($at)
        .set noreorder	

LEAF (tinyMon)                       	# Address = 0xb0000400.

/*
 * Ramrom entry point; on reset, the R4300 fetches instructions starting at 
 * address 0xbfc00000; the pif rom there tells the R4300 to immediately jump 
 * to cartridge rom address 0xb0000400 (0x400 bytes into the cartridge rom),
 * and that''s us!
 *
 * This version of tinyMon is ramrom resident, and vectors off to the pif rom
 * whenever it needs to read or write cartridge rom addresses (to avoid the
 * memory arbitration conflict which results when the R4300 is simultaneously
 * fetching instructions from ramrom AND attempting to read/write ramrom).
 * 
 * Registers t0, t1, and ra are used when calling the pif co-routines, so we
 * avoid their usage below except when invoking the co-routines.
 *
 * Host to R4300 command protocol:
 *
 * Host writes "DG_RAMROM_REQUEST" (0x1) to the cart_int register;
 * Host then polls the gio_sync register for "DG_RAMROM_GRANT" (0x2).
 *
 *	R4300 polls the cart_int register for "DG_RAMROM_REQUEST"; when
 *	it sees it, it polls PI_STATUS_REG until the IO & DMA busy bits
 *	are clear, then writes "DG_RAMROM_GRANT" to the gio sync register.
 *	R4300 then polls the cart_int register for "DG_RAMROM_CMD_READY" (0x3).
 *
 * Host receives "DG_RAMROM_GRANT", writes the read/write command into the 
 * first two words of ramrom, sets the cart_int register to 
 * "DG_RAMROM_CMD_READY", then polls the gio_sync register for 
 * "DG_RAMROM_CMD_DONE" (0x4).
 *
 *	R4300 polls the cart_int register for "DG_RAMROM_CMD_READY"; when
 *	it sees it, it processes the read/write request, (for reads only) 
 *      waits for DMA/IO to complete (so the host can read ramrom), then sets 
 *	the gio sync register to "DG_RAMROM_CMD_DONE", and goes back to 
 *	polling the cart_int register, looking for "DG_RAMROM_REQUEST".
 *
 * Host receives "DG_RAMROM_CMD_DONE", and (for read commands) read the 
 * second word of ramrom (which has the readback value, written by the R4300).
 *
 * When the Host wants to issue another command, it writes "DG_RAMROM_REQUEST"
 * to the cart_int register, and this starts the next round of cmd processing.
 *
 */

 /*
  * On startup, set the gio sync register to indicate that the host can go
  * ahead & access ramrom.  Only do this once at startup; thereafter, we''ll
  * keep in synch with the host.  The default state of the ramrom is 
  * "DG_RAMROM_CMD_DONE", which means the host can go ahead & write to the
  * cartridge interrupt to arbitrate for access to ramrom.
  */
	lui	t0, GIO_BASE_REG_UPPER
	addiu	t0, t0, GIO_SYNC_REG_OFFSET
	li	t1, DG_RAMROM_CMD_DONE
	jal	PIFMON_WRITE_ADDR	# Can only write gio mem/regs via pif subroutine
	nop


waitForHostRequest:
	
	lui	t2, GIO_BASE_REG_UPPER
	lw	t3, CART_INTR_REG_OFFSET(t2)
	nop
	andi	t3, t3, DG_CARTREG_MASK
	li	t4, DG_RAMROM_REQUEST
	bne	t3, t4, waitForHostRequest
	nop

/*
 * Got a host request for access to ramrom.  Check to make sure no io/dma
 * is in progress, tell the host ramrom is theirs for the taking, then 
 * poll the cartridge interrupt register and wait for DG_RAMROM_CMD_READY.
 */

	lui	t2, PI_BASE_REG_UPPER

waitForIdle:

	lw	t3, PI_STATUS_REG_OFFSET(t2)
	nop
	andi	t4, t3, (PI_STATUS_DMA_BUSY | PI_STATUS_IO_BUSY)
	bne	t4, zero, waitForIdle
	nop

/*
 * Dma and/or IO is complete; tell host to go ahead & write the command to
 * ramrom, and wait for the host to tell us they''re done.
 */
	lui	t0, GIO_BASE_REG_UPPER
	addiu	t0, t0, GIO_SYNC_REG_OFFSET
	li	t1, DG_RAMROM_GRANT
	jal	PIFMON_WRITE_ADDR	# Can only write gio mem/regs via pif subroutine
	nop

	lui	t2, GIO_BASE_REG_UPPER

waitForCmdReady:

	lw	t3, CART_INTR_REG_OFFSET(t2)
	nop
	andi	t3, t3, DG_CARTREG_MASK
	li	t4, DG_RAMROM_CMD_READY
	bne	t3, t4, waitForCmdReady
	nop
	
/*
 * Find out what kind of operation this is (read or write) and vector off to
 * the mock pif rom co-routines to handle the operation.
 */

	lui	t0, RAMROM_BASE_UPPER
	lw	t1, 0(t0)
	nop
	li	t3, DG_TINYMON_READ_OP
	beq	t1, t3, doReadCmd
	nop
	li	t3, DG_TINYMON_WRITE_OP
	beq	t1, t3, doWriteCmd
	nop
/*
 * If it''s not a read or write operation, lie & say "command complete".
 */
	b	cmdDone
	nop

doWriteCmd:
	lui	t2, RAMROM_BASE_UPPER
	lw	t0, 4(t2)	# 2nd word of ramrom has write address
	lw	t1, 8(t2)	# 3rd word of ramrom has data to write
	nop
	jal	PIFMON_WRITE_ADDR # will poll for io/dma busy, won''t return
	nop			  # until status register says it''s done.
	b	cmdDone
	nop

doReadCmd:

	
	lui	t2, RAMROM_BASE_UPPER
	lw	t0, 4(t2)	# 2nd word of ramrom has read address
	nop
	lw	t1, 0(t0)
/*
 * Write the readback value (in t1) into the third word of ramrom.
 */
	lui	t0, RAMROM_BASE_UPPER
	addiu	t0, t0, 0x8
	jal	PIFMON_WRITE_ADDR
	nop
/*
 * For reads, we must wait for any IO/DMA activity to settle down prior to
 * returning to the host, otherwise their read of the third word of ramrom
 * might time out.
 */
	lui	t2, PI_BASE_REG_UPPER

waitForIdle2:

	lw	t3, PI_STATUS_REG_OFFSET(t2)
	nop
	andi	t4, t3, (PI_STATUS_DMA_BUSY | PI_STATUS_IO_BUSY)
	bne	t4, zero, waitForIdle2
	nop
/*
 * Fall through to cmdDone, which sets the gio sync register to 
 * "DG_RAMROM_CMD_DONE", then branches back to where we
 * poll the cart_int register, looking for "DG_RAMROM_REQUEST".
 */

cmdDone:

	lui	t0, GIO_BASE_REG_UPPER
	addiu	t0, t0, GIO_SYNC_REG_OFFSET
	li	t1, DG_RAMROM_CMD_DONE
	jal	PIFMON_WRITE_ADDR	# Can only write gio mem/regs via pif subroutine
	nop
	b	waitForHostRequest
	nop

END (tinyMon)