tinymon_pif.s
5.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/*
* 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)