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 | |
d01bb904 | 27 | #ifndef min |
28 | #define min(a, b) ((b) < (a) ? (b) : (a)) | |
29 | #endif | |
0300a353 | 30 | #ifndef PSXDMA_LOG |
31 | #define PSXDMA_LOG(...) | |
32 | #endif | |
d01bb904 | 33 | |
ef79bbde P |
34 | // Dma0/1 in Mdec.c |
35 | // Dma3 in CdRom.c | |
36 | ||
37 | void spuInterrupt() { | |
ad418c19 | 38 | if (HW_DMA4_CHCR & SWAP32(0x01000000)) |
39 | { | |
40 | HW_DMA4_CHCR &= SWAP32(~0x01000000); | |
41 | DMA_INTERRUPT(4); | |
42 | } | |
ef79bbde P |
43 | } |
44 | ||
45 | void psxDma4(u32 madr, u32 bcr, u32 chcr) { // SPU | |
0300a353 | 46 | u32 words, words_max = 0, words_copy; |
ef79bbde | 47 | u16 *ptr; |
ef79bbde | 48 | |
0300a353 | 49 | madr &= ~3; |
50 | ptr = getDmaRam(madr, &words_max); | |
51 | if (ptr == INVALID_PTR) | |
52 | log_unhandled("bad dma4 madr %x\n", madr); | |
53 | ||
54 | words = words_copy = (bcr >> 16) * (bcr & 0xffff); | |
55 | if (words_copy > words_max) { | |
56 | log_unhandled("bad dma4 madr %x bcr %x\n", madr, bcr); | |
57 | words_copy = words_max; | |
58 | } | |
59 | ||
ef79bbde P |
60 | switch (chcr) { |
61 | case 0x01000201: //cpu to spu transfer | |
ef79bbde | 62 | PSXDMA_LOG("*** DMA4 SPU - mem2spu *** %x addr = %x size = %x\n", chcr, madr, bcr); |
0300a353 | 63 | if (ptr == INVALID_PTR) |
ef79bbde | 64 | break; |
0300a353 | 65 | SPU_writeDMAMem(ptr, words_copy * 2, psxRegs.cycle); |
66 | HW_DMA4_MADR = SWAPu32(madr + words_copy * 2); | |
56e500f3 | 67 | // This should be much slower, like 12+ cycles/byte, it's like |
68 | // that because the CPU runs too fast and fifo is not emulated. | |
69 | // See also set_dma_end(). | |
d12f6683 | 70 | set_event(PSXINT_SPUDMA, words * 4 * 4); |
ef79bbde P |
71 | return; |
72 | ||
73 | case 0x01000200: //spu to cpu transfer | |
ef79bbde | 74 | PSXDMA_LOG("*** DMA4 SPU - spu2mem *** %x addr = %x size = %x\n", chcr, madr, bcr); |
0300a353 | 75 | if (ptr == INVALID_PTR) |
ef79bbde | 76 | break; |
0300a353 | 77 | SPU_readDMAMem(ptr, words_copy * 2, psxRegs.cycle); |
78 | psxCpu->Clear(madr, words_copy); | |
58ebb94c | 79 | |
0300a353 | 80 | HW_DMA4_MADR = SWAPu32(madr + words_copy * 4); |
d12f6683 | 81 | set_event(PSXINT_SPUDMA, words * 4 * 4); |
58ebb94c | 82 | return; |
ef79bbde | 83 | |
ef79bbde | 84 | default: |
7df2c03c | 85 | log_unhandled("*** DMA4 SPU - unknown *** %x addr = %x size = %x\n", chcr, madr, bcr); |
ef79bbde | 86 | break; |
ef79bbde P |
87 | } |
88 | ||
89 | HW_DMA4_CHCR &= SWAP32(~0x01000000); | |
90 | DMA_INTERRUPT(4); | |
91 | } | |
92 | ||
d02ab9fc | 93 | #if 0 |
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; | |
5b568098 | 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 | } | |
d02ab9fc | 134 | #endif |
57a757ce | 135 | |
ef79bbde | 136 | void psxDma2(u32 madr, u32 bcr, u32 chcr) { // GPU |
d02ab9fc | 137 | u32 *ptr, madr_next, *madr_next_p; |
0300a353 | 138 | u32 words, words_left, words_max, words_copy; |
d02ab9fc | 139 | int cycles_sum, cycles_last_cmd = 0, do_walking; |
ef79bbde | 140 | |
0300a353 | 141 | madr &= ~3; |
57a757ce | 142 | switch (chcr) { |
ef79bbde | 143 | case 0x01000200: // vram2mem |
57a757ce | 144 | PSXDMA_LOG("*** DMA2 GPU - vram2mem *** %lx addr = %lx size = %lx\n", chcr, madr, bcr); |
d01bb904 | 145 | ptr = getDmaRam(madr, &words_max); |
b012a437 | 146 | if (ptr == INVALID_PTR) { |
0300a353 | 147 | log_unhandled("bad dma2 madr %x\n", madr); |
ef79bbde P |
148 | break; |
149 | } | |
57a757ce | 150 | // BA blocks * BS words (word = 32-bits) |
0300a353 | 151 | words = words_copy = (bcr >> 16) * (bcr & 0xffff); |
152 | if (words > words_max) { | |
153 | log_unhandled("bad dma2 madr %x bcr %x\n", madr, bcr); | |
154 | words_copy = words_max; | |
155 | } | |
d01bb904 | 156 | GPU_readDataMem(ptr, words_copy); |
157 | psxCpu->Clear(madr, words_copy); | |
58ebb94c | 158 | |
0300a353 | 159 | HW_DMA2_MADR = SWAPu32(madr + words_copy * 4); |
57a757ce | 160 | |
1328fa32 | 161 | // careful: gpu_state_change() also messes with this |
8cba0a22 | 162 | psxRegs.gpuIdleAfter = psxRegs.cycle + words / 4 + 16; |
57a757ce | 163 | // already 32-bit word size ((size * 4) / 4) |
c6a249e3 | 164 | set_event(PSXINT_GPUDMA, words / 4); |
57a757ce | 165 | return; |
ef79bbde P |
166 | |
167 | case 0x01000201: // mem2vram | |
57a757ce | 168 | PSXDMA_LOG("*** DMA 2 - GPU mem2vram *** %lx addr = %lx size = %lx\n", chcr, madr, bcr); |
0300a353 | 169 | words = words_left = (bcr >> 16) * (bcr & 0xffff); |
170 | while (words_left > 0) { | |
171 | ptr = getDmaRam(madr, &words_max); | |
172 | if (ptr == INVALID_PTR) { | |
173 | log_unhandled("bad2 dma madr %x\n", madr); | |
174 | break; | |
175 | } | |
176 | words_copy = min(words_left, words_max); | |
177 | GPU_writeDataMem(ptr, words_copy); | |
178 | words_left -= words_copy; | |
179 | madr += words_copy * 4; | |
ef79bbde | 180 | } |
58ebb94c | 181 | |
0300a353 | 182 | HW_DMA2_MADR = SWAPu32(madr); |
57a757ce | 183 | |
1328fa32 | 184 | // careful: gpu_state_change() also messes with this |
8cba0a22 | 185 | psxRegs.gpuIdleAfter = psxRegs.cycle + words / 4 + 16; |
57a757ce | 186 | // already 32-bit word size ((size * 4) / 4) |
c6a249e3 | 187 | set_event(PSXINT_GPUDMA, words / 4); |
ef79bbde P |
188 | return; |
189 | ||
190 | case 0x01000401: // dma chain | |
57a757ce | 191 | PSXDMA_LOG("*** DMA 2 - GPU dma chain *** %lx addr = %lx size = %lx\n", chcr, madr, bcr); |
fae38d7a | 192 | // when not emulating walking progress, end immediately |
193 | madr_next = 0xffffff; | |
57a757ce | 194 | |
fae38d7a | 195 | do_walking = Config.GpuListWalking; |
6a3b3d86 | 196 | if (do_walking < 0 || Config.hacks.gpu_timing1024) |
fae38d7a | 197 | do_walking = Config.hacks.gpu_slow_list_walking; |
198 | madr_next_p = do_walking ? &madr_next : NULL; | |
199 | ||
d02ab9fc | 200 | cycles_sum = GPU_dmaChain((u32 *)psxM, madr & 0x1fffff, |
201 | madr_next_p, &cycles_last_cmd); | |
58ebb94c | 202 | |
fae38d7a | 203 | HW_DMA2_MADR = SWAPu32(madr_next); |
58ebb94c | 204 | |
6a3b3d86 | 205 | // a hack for Judge Dredd which is annoyingly sensitive to timing |
206 | if (Config.hacks.gpu_timing1024) | |
d02ab9fc | 207 | cycles_sum = 1024; |
57a757ce | 208 | |
d02ab9fc | 209 | psxRegs.gpuIdleAfter = psxRegs.cycle + cycles_sum + cycles_last_cmd; |
210 | set_event(PSXINT_GPUDMA, cycles_sum); | |
211 | //printf("%u dma2cf: %d,%d %08x\n", psxRegs.cycle, cycles_sum, | |
212 | // cycles_last_cmd, HW_DMA2_MADR); | |
57a757ce | 213 | return; |
ef79bbde | 214 | |
ef79bbde | 215 | default: |
7df2c03c | 216 | log_unhandled("*** DMA 2 - GPU unknown *** %x addr = %x size = %x\n", chcr, madr, bcr); |
ef79bbde | 217 | break; |
ef79bbde P |
218 | } |
219 | ||
220 | HW_DMA2_CHCR &= SWAP32(~0x01000000); | |
221 | DMA_INTERRUPT(2); | |
222 | } | |
223 | ||
224 | void gpuInterrupt() { | |
fae38d7a | 225 | if (HW_DMA2_CHCR == SWAP32(0x01000401) && !(HW_DMA2_MADR & SWAP32(0x800000))) |
226 | { | |
d02ab9fc | 227 | u32 madr_next = 0xffffff, madr = SWAPu32(HW_DMA2_MADR); |
228 | int cycles_sum, cycles_last_cmd = 0; | |
229 | cycles_sum = GPU_dmaChain((u32 *)psxM, madr & 0x1fffff, | |
230 | &madr_next, &cycles_last_cmd); | |
fae38d7a | 231 | HW_DMA2_MADR = SWAPu32(madr_next); |
d02ab9fc | 232 | if ((s32)(psxRegs.gpuIdleAfter - psxRegs.cycle) > 0) |
233 | cycles_sum += psxRegs.gpuIdleAfter - psxRegs.cycle; | |
234 | psxRegs.gpuIdleAfter = psxRegs.cycle + cycles_sum + cycles_last_cmd; | |
235 | set_event(PSXINT_GPUDMA, cycles_sum); | |
236 | //printf("%u dma2cn: %d,%d %08x\n", psxRegs.cycle, cycles_sum, | |
237 | // cycles_last_cmd, HW_DMA2_MADR); | |
fae38d7a | 238 | return; |
239 | } | |
ad418c19 | 240 | if (HW_DMA2_CHCR & SWAP32(0x01000000)) |
241 | { | |
242 | HW_DMA2_CHCR &= SWAP32(~0x01000000); | |
243 | DMA_INTERRUPT(2); | |
244 | } | |
ef79bbde P |
245 | } |
246 | ||
247 | void psxDma6(u32 madr, u32 bcr, u32 chcr) { | |
0300a353 | 248 | u32 words, words_max; |
249 | u32 *mem; | |
ef79bbde | 250 | |
ef79bbde | 251 | PSXDMA_LOG("*** DMA6 OT *** %x addr = %x size = %x\n", chcr, madr, bcr); |
ef79bbde P |
252 | |
253 | if (chcr == 0x11000002) { | |
162bbc5e | 254 | madr &= ~3; |
0300a353 | 255 | mem = getDmaRam(madr, &words_max); |
b012a437 | 256 | if (mem == INVALID_PTR) { |
0300a353 | 257 | log_unhandled("bad6 dma madr %x\n", madr); |
fc3afcae | 258 | HW_DMA6_CHCR &= SWAP32(~0x11000000); |
ef79bbde P |
259 | DMA_INTERRUPT(6); |
260 | return; | |
261 | } | |
262 | ||
57a757ce | 263 | // already 32-bit size |
fc4803bd | 264 | words = bcr; |
57a757ce | 265 | |
0300a353 | 266 | while (bcr-- && mem > (u32 *)psxM) { |
ef79bbde P |
267 | *mem-- = SWAP32((madr - 4) & 0xffffff); |
268 | madr -= 4; | |
269 | } | |
adb7d7ac | 270 | *++mem = SWAP32(0xffffff); |
57a757ce | 271 | |
fc4803bd | 272 | // halted |
273 | psxRegs.cycle += words; | |
c6a249e3 | 274 | set_event(PSXINT_GPUOTCDMA, 16); |
57a757ce | 275 | return; |
ef79bbde | 276 | } |
ef79bbde P |
277 | else { |
278 | // Unknown option | |
7df2c03c | 279 | log_unhandled("*** DMA6 OT - unknown *** %x addr = %x size = %x\n", chcr, madr, bcr); |
ef79bbde | 280 | } |
ef79bbde | 281 | |
fc3afcae | 282 | HW_DMA6_CHCR &= SWAP32(~0x11000000); |
ef79bbde P |
283 | DMA_INTERRUPT(6); |
284 | } | |
285 | ||
57a757ce | 286 | void gpuotcInterrupt() |
287 | { | |
ad418c19 | 288 | if (HW_DMA6_CHCR & SWAP32(0x01000000)) |
289 | { | |
fc3afcae | 290 | HW_DMA6_CHCR &= SWAP32(~0x11000000); |
ad418c19 | 291 | DMA_INTERRUPT(6); |
292 | } | |
57a757ce | 293 | } |