Commit | Line | Data |
---|---|---|
ef79bbde P |
1 | /*************************************************************************** |
2 | * Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team * | |
3 | * * | |
4 | * This program is free software; you can redistribute it and/or modify * | |
5 | * it under the terms of the GNU General Public License as published by * | |
6 | * the Free Software Foundation; either version 2 of the License, or * | |
7 | * (at your option) any later version. * | |
8 | * * | |
9 | * This program is distributed in the hope that it will be useful, * | |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
12 | * GNU General Public License for more details. * | |
13 | * * | |
14 | * You should have received a copy of the GNU General Public License * | |
15 | * along with this program; if not, write to the * | |
16 | * Free Software Foundation, Inc., * | |
17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307 USA. * | |
18 | ***************************************************************************/ | |
19 | ||
20 | /* | |
21 | * Handles PSX DMA functions. | |
22 | */ | |
23 | ||
24 | #include "psxdma.h" | |
6c036261 | 25 | #include "gpu.h" |
ef79bbde P |
26 | |
27 | // Dma0/1 in Mdec.c | |
28 | // Dma3 in CdRom.c | |
29 | ||
30 | void spuInterrupt() { | |
ad418c19 | 31 | if (HW_DMA4_CHCR & SWAP32(0x01000000)) |
32 | { | |
33 | HW_DMA4_CHCR &= SWAP32(~0x01000000); | |
34 | DMA_INTERRUPT(4); | |
35 | } | |
ef79bbde P |
36 | } |
37 | ||
38 | void psxDma4(u32 madr, u32 bcr, u32 chcr) { // SPU | |
39 | u16 *ptr; | |
58ebb94c | 40 | u32 words; |
ef79bbde P |
41 | |
42 | switch (chcr) { | |
43 | case 0x01000201: //cpu to spu transfer | |
44 | #ifdef PSXDMA_LOG | |
45 | PSXDMA_LOG("*** DMA4 SPU - mem2spu *** %x addr = %x size = %x\n", chcr, madr, bcr); | |
46 | #endif | |
47 | ptr = (u16 *)PSXM(madr); | |
b012a437 | 48 | if (ptr == INVALID_PTR) { |
ef79bbde P |
49 | #ifdef CPU_LOG |
50 | CPU_LOG("*** DMA4 SPU - mem2spu *** NULL Pointer!!!\n"); | |
51 | #endif | |
52 | break; | |
53 | } | |
58ebb94c | 54 | words = (bcr >> 16) * (bcr & 0xffff); |
55 | SPU_writeDMAMem(ptr, words * 2, psxRegs.cycle); | |
56 | HW_DMA4_MADR = SWAPu32(madr + words * 4); | |
7b68d92c | 57 | SPUDMA_INT(words * 4); |
ef79bbde P |
58 | return; |
59 | ||
60 | case 0x01000200: //spu to cpu transfer | |
61 | #ifdef PSXDMA_LOG | |
62 | PSXDMA_LOG("*** DMA4 SPU - spu2mem *** %x addr = %x size = %x\n", chcr, madr, bcr); | |
63 | #endif | |
64 | ptr = (u16 *)PSXM(madr); | |
b012a437 | 65 | if (ptr == INVALID_PTR) { |
ef79bbde P |
66 | #ifdef CPU_LOG |
67 | CPU_LOG("*** DMA4 SPU - spu2mem *** NULL Pointer!!!\n"); | |
68 | #endif | |
69 | break; | |
70 | } | |
58ebb94c | 71 | words = (bcr >> 16) * (bcr & 0xffff); |
72 | SPU_readDMAMem(ptr, words * 2, psxRegs.cycle); | |
73 | psxCpu->Clear(madr, words); | |
74 | ||
75 | HW_DMA4_MADR = SWAPu32(madr + words * 4); | |
7b68d92c | 76 | SPUDMA_INT(words * 4); |
58ebb94c | 77 | return; |
ef79bbde | 78 | |
ef79bbde | 79 | default: |
7df2c03c | 80 | log_unhandled("*** DMA4 SPU - unknown *** %x addr = %x size = %x\n", chcr, madr, bcr); |
ef79bbde | 81 | break; |
ef79bbde P |
82 | } |
83 | ||
84 | HW_DMA4_CHCR &= SWAP32(~0x01000000); | |
85 | DMA_INTERRUPT(4); | |
86 | } | |
87 | ||
57a757ce | 88 | // Taken from PEOPS SOFTGPU |
89 | static inline boolean CheckForEndlessLoop(u32 laddr, u32 *lUsedAddr) { | |
90 | if (laddr == lUsedAddr[1]) return TRUE; | |
91 | if (laddr == lUsedAddr[2]) return TRUE; | |
92 | ||
93 | if (laddr < lUsedAddr[0]) lUsedAddr[1] = laddr; | |
94 | else lUsedAddr[2] = laddr; | |
95 | ||
96 | lUsedAddr[0] = laddr; | |
97 | ||
98 | return FALSE; | |
99 | } | |
100 | ||
101 | static u32 gpuDmaChainSize(u32 addr) { | |
102 | u32 size; | |
103 | u32 DMACommandCounter = 0; | |
104 | u32 lUsedAddr[3]; | |
105 | ||
106 | lUsedAddr[0] = lUsedAddr[1] = lUsedAddr[2] = 0xffffff; | |
107 | ||
108 | // initial linked list ptr (word) | |
109 | size = 1; | |
110 | ||
111 | do { | |
112 | addr &= 0x1ffffc; | |
113 | ||
114 | if (DMACommandCounter++ > 2000000) break; | |
115 | if (CheckForEndlessLoop(addr, lUsedAddr)) break; | |
116 | ||
117 | // # 32-bit blocks to transfer | |
118 | size += psxMu8( addr + 3 ); | |
119 | ||
120 | // next 32-bit pointer | |
121 | addr = psxMu32( addr & ~0x3 ) & 0xffffff; | |
122 | size += 1; | |
5b568098 | 123 | } while (!(addr & 0x800000)); // contrary to some documentation, the end-of-linked-list marker is not actually 0xFF'FFFF |
124 | // any pointer with bit 23 set will do. | |
57a757ce | 125 | |
126 | return size; | |
127 | } | |
128 | ||
ef79bbde P |
129 | void psxDma2(u32 madr, u32 bcr, u32 chcr) { // GPU |
130 | u32 *ptr; | |
58ebb94c | 131 | u32 words; |
ef79bbde P |
132 | u32 size; |
133 | ||
57a757ce | 134 | switch (chcr) { |
ef79bbde P |
135 | case 0x01000200: // vram2mem |
136 | #ifdef PSXDMA_LOG | |
57a757ce | 137 | PSXDMA_LOG("*** DMA2 GPU - vram2mem *** %lx addr = %lx size = %lx\n", chcr, madr, bcr); |
ef79bbde P |
138 | #endif |
139 | ptr = (u32 *)PSXM(madr); | |
b012a437 | 140 | if (ptr == INVALID_PTR) { |
ef79bbde P |
141 | #ifdef CPU_LOG |
142 | CPU_LOG("*** DMA2 GPU - vram2mem *** NULL Pointer!!!\n"); | |
143 | #endif | |
144 | break; | |
145 | } | |
57a757ce | 146 | // BA blocks * BS words (word = 32-bits) |
58ebb94c | 147 | words = (bcr >> 16) * (bcr & 0xffff); |
148 | GPU_readDataMem(ptr, words); | |
149 | psxCpu->Clear(madr, words); | |
150 | ||
151 | HW_DMA2_MADR = SWAPu32(madr + words * 4); | |
57a757ce | 152 | |
153 | // already 32-bit word size ((size * 4) / 4) | |
58ebb94c | 154 | GPUDMA_INT(words / 4); |
57a757ce | 155 | return; |
ef79bbde P |
156 | |
157 | case 0x01000201: // mem2vram | |
158 | #ifdef PSXDMA_LOG | |
57a757ce | 159 | PSXDMA_LOG("*** DMA 2 - GPU mem2vram *** %lx addr = %lx size = %lx\n", chcr, madr, bcr); |
ef79bbde P |
160 | #endif |
161 | ptr = (u32 *)PSXM(madr); | |
b012a437 | 162 | if (ptr == INVALID_PTR) { |
ef79bbde P |
163 | #ifdef CPU_LOG |
164 | CPU_LOG("*** DMA2 GPU - mem2vram *** NULL Pointer!!!\n"); | |
165 | #endif | |
166 | break; | |
167 | } | |
57a757ce | 168 | // BA blocks * BS words (word = 32-bits) |
58ebb94c | 169 | words = (bcr >> 16) * (bcr & 0xffff); |
170 | GPU_writeDataMem(ptr, words); | |
171 | ||
172 | HW_DMA2_MADR = SWAPu32(madr + words * 4); | |
57a757ce | 173 | |
174 | // already 32-bit word size ((size * 4) / 4) | |
58ebb94c | 175 | GPUDMA_INT(words / 4); |
ef79bbde P |
176 | return; |
177 | ||
178 | case 0x01000401: // dma chain | |
179 | #ifdef PSXDMA_LOG | |
57a757ce | 180 | PSXDMA_LOG("*** DMA 2 - GPU dma chain *** %lx addr = %lx size = %lx\n", chcr, madr, bcr); |
ef79bbde | 181 | #endif |
57a757ce | 182 | |
b03e0caf | 183 | size = GPU_dmaChain((u32 *)psxM, madr & 0x1fffff); |
184 | if ((int)size <= 0) | |
185 | size = gpuDmaChainSize(madr); | |
adb7d7ac | 186 | HW_GPU_STATUS &= SWAP32(~PSXGPU_nBUSY); |
58ebb94c | 187 | |
188 | // we don't emulate progress, just busy flag and end irq, | |
189 | // so pretend we're already at the last block | |
190 | HW_DMA2_MADR = SWAPu32(0xffffff); | |
191 | ||
57a757ce | 192 | // Tekken 3 = use 1.0 only (not 1.5x) |
193 | ||
194 | // Einhander = parse linked list in pieces (todo) | |
195 | // Final Fantasy 4 = internal vram time (todo) | |
196 | // Rebel Assault 2 = parse linked list in pieces (todo) | |
197 | // Vampire Hunter D = allow edits to linked list (todo) | |
198 | GPUDMA_INT(size); | |
199 | return; | |
ef79bbde | 200 | |
ef79bbde | 201 | default: |
7df2c03c | 202 | log_unhandled("*** DMA 2 - GPU unknown *** %x addr = %x size = %x\n", chcr, madr, bcr); |
ef79bbde | 203 | break; |
ef79bbde P |
204 | } |
205 | ||
206 | HW_DMA2_CHCR &= SWAP32(~0x01000000); | |
207 | DMA_INTERRUPT(2); | |
208 | } | |
209 | ||
210 | void gpuInterrupt() { | |
ad418c19 | 211 | if (HW_DMA2_CHCR & SWAP32(0x01000000)) |
212 | { | |
213 | HW_DMA2_CHCR &= SWAP32(~0x01000000); | |
214 | DMA_INTERRUPT(2); | |
215 | } | |
adb7d7ac | 216 | HW_GPU_STATUS |= SWAP32(PSXGPU_nBUSY); // GPU no longer busy |
ef79bbde P |
217 | } |
218 | ||
219 | void psxDma6(u32 madr, u32 bcr, u32 chcr) { | |
fc4803bd | 220 | u32 words; |
ef79bbde P |
221 | u32 *mem = (u32 *)PSXM(madr); |
222 | ||
223 | #ifdef PSXDMA_LOG | |
224 | PSXDMA_LOG("*** DMA6 OT *** %x addr = %x size = %x\n", chcr, madr, bcr); | |
225 | #endif | |
226 | ||
227 | if (chcr == 0x11000002) { | |
b012a437 | 228 | if (mem == INVALID_PTR) { |
ef79bbde P |
229 | #ifdef CPU_LOG |
230 | CPU_LOG("*** DMA6 OT *** NULL Pointer!!!\n"); | |
231 | #endif | |
232 | HW_DMA6_CHCR &= SWAP32(~0x01000000); | |
233 | DMA_INTERRUPT(6); | |
234 | return; | |
235 | } | |
236 | ||
57a757ce | 237 | // already 32-bit size |
fc4803bd | 238 | words = bcr; |
57a757ce | 239 | |
ef79bbde P |
240 | while (bcr--) { |
241 | *mem-- = SWAP32((madr - 4) & 0xffffff); | |
242 | madr -= 4; | |
243 | } | |
adb7d7ac | 244 | *++mem = SWAP32(0xffffff); |
57a757ce | 245 | |
fc4803bd | 246 | //GPUOTCDMA_INT(size); |
247 | // halted | |
248 | psxRegs.cycle += words; | |
249 | GPUOTCDMA_INT(16); | |
57a757ce | 250 | return; |
ef79bbde | 251 | } |
ef79bbde P |
252 | else { |
253 | // Unknown option | |
7df2c03c | 254 | log_unhandled("*** DMA6 OT - unknown *** %x addr = %x size = %x\n", chcr, madr, bcr); |
ef79bbde | 255 | } |
ef79bbde P |
256 | |
257 | HW_DMA6_CHCR &= SWAP32(~0x01000000); | |
258 | DMA_INTERRUPT(6); | |
259 | } | |
260 | ||
57a757ce | 261 | void gpuotcInterrupt() |
262 | { | |
ad418c19 | 263 | if (HW_DMA6_CHCR & SWAP32(0x01000000)) |
264 | { | |
265 | HW_DMA6_CHCR &= SWAP32(~0x01000000); | |
266 | DMA_INTERRUPT(6); | |
267 | } | |
57a757ce | 268 | } |