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 | 26 | |
b4d5a91d | 27 | #ifndef min |
28 | #define min(a, b) ((b) < (a) ? (b) : (a)) | |
29 | #endif | |
30 | ||
ef79bbde P |
31 | // Dma0/1 in Mdec.c |
32 | // Dma3 in CdRom.c | |
33 | ||
34 | void spuInterrupt() { | |
ad418c19 | 35 | if (HW_DMA4_CHCR & SWAP32(0x01000000)) |
36 | { | |
37 | HW_DMA4_CHCR &= SWAP32(~0x01000000); | |
38 | DMA_INTERRUPT(4); | |
39 | } | |
ef79bbde P |
40 | } |
41 | ||
42 | void psxDma4(u32 madr, u32 bcr, u32 chcr) { // SPU | |
b4d5a91d | 43 | u32 words, words_max, size; |
ef79bbde | 44 | u16 *ptr; |
ef79bbde P |
45 | |
46 | switch (chcr) { | |
47 | case 0x01000201: //cpu to spu transfer | |
48 | #ifdef PSXDMA_LOG | |
49 | PSXDMA_LOG("*** DMA4 SPU - mem2spu *** %x addr = %x size = %x\n", chcr, madr, bcr); | |
50 | #endif | |
b4d5a91d | 51 | ptr = getDmaRam(madr, &words_max); |
7a8d521f | 52 | if (ptr == INVALID_PTR) { |
ef79bbde P |
53 | #ifdef CPU_LOG |
54 | CPU_LOG("*** DMA4 SPU - mem2spu *** NULL Pointer!!!\n"); | |
55 | #endif | |
56 | break; | |
57 | } | |
58ebb94c | 58 | words = (bcr >> 16) * (bcr & 0xffff); |
b4d5a91d | 59 | size = min(words, words_max) * 2; |
60 | SPU_writeDMAMem(ptr, size, psxRegs.cycle); | |
61 | HW_DMA4_MADR = SWAPu32((madr & ~3) + words * 4); | |
4f329f16 | 62 | SPUDMA_INT(words * 4); |
ef79bbde P |
63 | return; |
64 | ||
65 | case 0x01000200: //spu to cpu transfer | |
66 | #ifdef PSXDMA_LOG | |
67 | PSXDMA_LOG("*** DMA4 SPU - spu2mem *** %x addr = %x size = %x\n", chcr, madr, bcr); | |
68 | #endif | |
b4d5a91d | 69 | ptr = getDmaRam(madr, &words_max); |
7a8d521f | 70 | if (ptr == INVALID_PTR) { |
ef79bbde P |
71 | #ifdef CPU_LOG |
72 | CPU_LOG("*** DMA4 SPU - spu2mem *** NULL Pointer!!!\n"); | |
73 | #endif | |
74 | break; | |
75 | } | |
58ebb94c | 76 | words = (bcr >> 16) * (bcr & 0xffff); |
b4d5a91d | 77 | size = min(words, words_max) * 2; |
78 | SPU_readDMAMem(ptr, size, psxRegs.cycle); | |
58ebb94c | 79 | psxCpu->Clear(madr, words); |
80 | ||
81 | HW_DMA4_MADR = SWAPu32(madr + words * 4); | |
4f329f16 | 82 | SPUDMA_INT(words * 4); |
58ebb94c | 83 | return; |
ef79bbde | 84 | |
ef79bbde | 85 | default: |
6c9db47c | 86 | log_unhandled("*** DMA4 SPU - unknown *** %x addr = %x size = %x\n", chcr, madr, bcr); |
ef79bbde | 87 | break; |
ef79bbde P |
88 | } |
89 | ||
90 | HW_DMA4_CHCR &= SWAP32(~0x01000000); | |
91 | DMA_INTERRUPT(4); | |
92 | } | |
93 | ||
57a757ce | 94 | // Taken from PEOPS SOFTGPU |
95 | static inline boolean CheckForEndlessLoop(u32 laddr, u32 *lUsedAddr) { | |
96 | if (laddr == lUsedAddr[1]) return TRUE; | |
97 | if (laddr == lUsedAddr[2]) return TRUE; | |
98 | ||
99 | if (laddr < lUsedAddr[0]) lUsedAddr[1] = laddr; | |
100 | else lUsedAddr[2] = laddr; | |
101 | ||
102 | lUsedAddr[0] = laddr; | |
103 | ||
104 | return FALSE; | |
105 | } | |
106 | ||
107 | static u32 gpuDmaChainSize(u32 addr) { | |
108 | u32 size; | |
109 | u32 DMACommandCounter = 0; | |
110 | u32 lUsedAddr[3]; | |
111 | ||
112 | lUsedAddr[0] = lUsedAddr[1] = lUsedAddr[2] = 0xffffff; | |
113 | ||
114 | // initial linked list ptr (word) | |
115 | size = 1; | |
116 | ||
117 | do { | |
118 | addr &= 0x1ffffc; | |
119 | ||
120 | if (DMACommandCounter++ > 2000000) break; | |
121 | if (CheckForEndlessLoop(addr, lUsedAddr)) break; | |
122 | ||
123 | // # 32-bit blocks to transfer | |
124 | size += psxMu8( addr + 3 ); | |
125 | ||
126 | // next 32-bit pointer | |
127 | addr = psxMu32( addr & ~0x3 ) & 0xffffff; | |
128 | size += 1; | |
502ea36e | 129 | } while (!(addr & 0x800000)); // contrary to some documentation, the end-of-linked-list marker is not actually 0xFF'FFFF |
130 | // any pointer with bit 23 set will do. | |
57a757ce | 131 | |
132 | return size; | |
133 | } | |
134 | ||
ef79bbde | 135 | void psxDma2(u32 madr, u32 bcr, u32 chcr) { // GPU |
b4d5a91d | 136 | u32 *ptr, madr_next, *madr_next_p, size; |
137 | u32 words, words_max, words_copy; | |
8c84ba5f | 138 | int do_walking; |
ef79bbde | 139 | |
57a757ce | 140 | switch (chcr) { |
ef79bbde P |
141 | case 0x01000200: // vram2mem |
142 | #ifdef PSXDMA_LOG | |
57a757ce | 143 | PSXDMA_LOG("*** DMA2 GPU - vram2mem *** %lx addr = %lx size = %lx\n", chcr, madr, bcr); |
ef79bbde | 144 | #endif |
b4d5a91d | 145 | ptr = getDmaRam(madr, &words_max); |
7a8d521f | 146 | if (ptr == INVALID_PTR) { |
ef79bbde P |
147 | #ifdef CPU_LOG |
148 | CPU_LOG("*** DMA2 GPU - vram2mem *** NULL Pointer!!!\n"); | |
149 | #endif | |
150 | break; | |
151 | } | |
57a757ce | 152 | // BA blocks * BS words (word = 32-bits) |
58ebb94c | 153 | words = (bcr >> 16) * (bcr & 0xffff); |
b4d5a91d | 154 | words_copy = min(words, words_max); |
155 | GPU_readDataMem(ptr, words_copy); | |
156 | psxCpu->Clear(madr, words_copy); | |
58ebb94c | 157 | |
b4d5a91d | 158 | HW_DMA2_MADR = SWAPu32((madr & ~3) + words * 4); |
57a757ce | 159 | |
160 | // already 32-bit word size ((size * 4) / 4) | |
58ebb94c | 161 | GPUDMA_INT(words / 4); |
57a757ce | 162 | return; |
ef79bbde P |
163 | |
164 | case 0x01000201: // mem2vram | |
165 | #ifdef PSXDMA_LOG | |
57a757ce | 166 | PSXDMA_LOG("*** DMA 2 - GPU mem2vram *** %lx addr = %lx size = %lx\n", chcr, madr, bcr); |
ef79bbde | 167 | #endif |
b4d5a91d | 168 | ptr = getDmaRam(madr, &words_max); |
7a8d521f | 169 | if (ptr == INVALID_PTR) { |
ef79bbde P |
170 | #ifdef CPU_LOG |
171 | CPU_LOG("*** DMA2 GPU - mem2vram *** NULL Pointer!!!\n"); | |
172 | #endif | |
173 | break; | |
174 | } | |
57a757ce | 175 | // BA blocks * BS words (word = 32-bits) |
58ebb94c | 176 | words = (bcr >> 16) * (bcr & 0xffff); |
b4d5a91d | 177 | GPU_writeDataMem(ptr, min(words, words_max)); |
58ebb94c | 178 | |
b4d5a91d | 179 | HW_DMA2_MADR = SWAPu32((madr & ~3) + words * 4); |
57a757ce | 180 | |
181 | // already 32-bit word size ((size * 4) / 4) | |
58ebb94c | 182 | GPUDMA_INT(words / 4); |
ef79bbde P |
183 | return; |
184 | ||
185 | case 0x01000401: // dma chain | |
186 | #ifdef PSXDMA_LOG | |
57a757ce | 187 | PSXDMA_LOG("*** DMA 2 - GPU dma chain *** %lx addr = %lx size = %lx\n", chcr, madr, bcr); |
ef79bbde | 188 | #endif |
8c84ba5f | 189 | // when not emulating walking progress, end immediately |
190 | madr_next = 0xffffff; | |
57a757ce | 191 | |
8c84ba5f | 192 | do_walking = Config.GpuListWalking; |
193 | if (do_walking < 0) | |
194 | do_walking = Config.hacks.gpu_slow_list_walking; | |
195 | madr_next_p = do_walking ? &madr_next : NULL; | |
196 | ||
197 | size = GPU_dmaChain((u32 *)psxM, madr & 0x1fffff, madr_next_p); | |
b03e0caf | 198 | if ((int)size <= 0) |
199 | size = gpuDmaChainSize(madr); | |
58ebb94c | 200 | |
8c84ba5f | 201 | HW_GPU_STATUS &= SWAP32(~PSXGPU_nBUSY); |
202 | HW_DMA2_MADR = SWAPu32(madr_next); | |
58ebb94c | 203 | |
57a757ce | 204 | // Tekken 3 = use 1.0 only (not 1.5x) |
205 | ||
206 | // Einhander = parse linked list in pieces (todo) | |
57a757ce | 207 | // Rebel Assault 2 = parse linked list in pieces (todo) |
57a757ce | 208 | GPUDMA_INT(size); |
209 | return; | |
ef79bbde | 210 | |
ef79bbde | 211 | default: |
6c9db47c | 212 | log_unhandled("*** DMA 2 - GPU unknown *** %x addr = %x size = %x\n", chcr, madr, bcr); |
ef79bbde | 213 | break; |
ef79bbde P |
214 | } |
215 | ||
216 | HW_DMA2_CHCR &= SWAP32(~0x01000000); | |
217 | DMA_INTERRUPT(2); | |
218 | } | |
219 | ||
220 | void gpuInterrupt() { | |
8c84ba5f | 221 | if (HW_DMA2_CHCR == SWAP32(0x01000401) && !(HW_DMA2_MADR & SWAP32(0x800000))) |
222 | { | |
223 | u32 size, madr_next = 0xffffff; | |
224 | size = GPU_dmaChain((u32 *)psxM, HW_DMA2_MADR & 0x1fffff, &madr_next); | |
225 | HW_DMA2_MADR = SWAPu32(madr_next); | |
226 | GPUDMA_INT(size); | |
227 | return; | |
228 | } | |
ad418c19 | 229 | if (HW_DMA2_CHCR & SWAP32(0x01000000)) |
230 | { | |
231 | HW_DMA2_CHCR &= SWAP32(~0x01000000); | |
232 | DMA_INTERRUPT(2); | |
233 | } | |
086adfff | 234 | HW_GPU_STATUS |= SWAP32(PSXGPU_nBUSY); // GPU no longer busy |
ef79bbde P |
235 | } |
236 | ||
237 | void psxDma6(u32 madr, u32 bcr, u32 chcr) { | |
fc4803bd | 238 | u32 words; |
ef79bbde P |
239 | u32 *mem = (u32 *)PSXM(madr); |
240 | ||
241 | #ifdef PSXDMA_LOG | |
242 | PSXDMA_LOG("*** DMA6 OT *** %x addr = %x size = %x\n", chcr, madr, bcr); | |
243 | #endif | |
244 | ||
245 | if (chcr == 0x11000002) { | |
7a8d521f | 246 | if (mem == INVALID_PTR) { |
ef79bbde P |
247 | #ifdef CPU_LOG |
248 | CPU_LOG("*** DMA6 OT *** NULL Pointer!!!\n"); | |
249 | #endif | |
250 | HW_DMA6_CHCR &= SWAP32(~0x01000000); | |
251 | DMA_INTERRUPT(6); | |
252 | return; | |
253 | } | |
254 | ||
57a757ce | 255 | // already 32-bit size |
fc4803bd | 256 | words = bcr; |
57a757ce | 257 | |
ef79bbde P |
258 | while (bcr--) { |
259 | *mem-- = SWAP32((madr - 4) & 0xffffff); | |
260 | madr -= 4; | |
261 | } | |
086adfff | 262 | *++mem = SWAP32(0xffffff); |
57a757ce | 263 | |
fc4803bd | 264 | //GPUOTCDMA_INT(size); |
265 | // halted | |
266 | psxRegs.cycle += words; | |
267 | GPUOTCDMA_INT(16); | |
57a757ce | 268 | return; |
ef79bbde | 269 | } |
ef79bbde P |
270 | else { |
271 | // Unknown option | |
6c9db47c | 272 | log_unhandled("*** DMA6 OT - unknown *** %x addr = %x size = %x\n", chcr, madr, bcr); |
ef79bbde | 273 | } |
ef79bbde P |
274 | |
275 | HW_DMA6_CHCR &= SWAP32(~0x01000000); | |
276 | DMA_INTERRUPT(6); | |
277 | } | |
278 | ||
57a757ce | 279 | void gpuotcInterrupt() |
280 | { | |
ad418c19 | 281 | if (HW_DMA6_CHCR & SWAP32(0x01000000)) |
282 | { | |
283 | HW_DMA6_CHCR &= SWAP32(~0x01000000); | |
284 | DMA_INTERRUPT(6); | |
285 | } | |
57a757ce | 286 | } |