| 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 | |
| 38266cd6 | 30 | #ifndef PSXDMA_LOG |
| 31 | #define PSXDMA_LOG(...) | |
| 32 | #endif | |
| b4d5a91d | 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 | |
| 38266cd6 | 46 | u32 words, words_max = 0, words_copy; |
| ef79bbde | 47 | u16 *ptr; |
| ef79bbde | 48 | |
| 38266cd6 | 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); |
| 38266cd6 | 63 | if (ptr == INVALID_PTR) |
| ef79bbde | 64 | break; |
| 38266cd6 | 65 | SPU_writeDMAMem(ptr, words_copy * 2, psxRegs.cycle); |
| 66 | HW_DMA4_MADR = SWAPu32(madr + words_copy * 2); | |
| 3c7a8977 | 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(). | |
| f926a62f | 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); |
| 38266cd6 | 75 | if (ptr == INVALID_PTR) |
| ef79bbde | 76 | break; |
| 38266cd6 | 77 | SPU_readDMAMem(ptr, words_copy * 2, psxRegs.cycle); |
| 78 | psxCpu->Clear(madr, words_copy); | |
| 58ebb94c | 79 | |
| 38266cd6 | 80 | HW_DMA4_MADR = SWAPu32(madr + words_copy * 4); |
| f926a62f | 81 | set_event(PSXINT_SPUDMA, words * 4 * 4); |
| 58ebb94c | 82 | return; |
| ef79bbde | 83 | |
| ef79bbde | 84 | default: |
| 6c9db47c | 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 | ||
| 8412166f | 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; | |
| 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 | } | |
| 8412166f | 134 | #endif |
| 57a757ce | 135 | |
| ef79bbde | 136 | void psxDma2(u32 madr, u32 bcr, u32 chcr) { // GPU |
| 8412166f | 137 | u32 *ptr, madr_next, *madr_next_p; |
| 38266cd6 | 138 | u32 words, words_left, words_max, words_copy; |
| 49f5a273 PC |
139 | s32 cycles_last_cmd = 0; |
| 140 | int do_walking; | |
| 141 | long cycles_sum; | |
| ef79bbde | 142 | |
| 38266cd6 | 143 | madr &= ~3; |
| 57a757ce | 144 | switch (chcr) { |
| ef79bbde | 145 | case 0x01000200: // vram2mem |
| b8cdf26f | 146 | PSXDMA_LOG("*** DMA2 GPU - vram2mem *** %x addr = %x size = %x\n", chcr, madr, bcr); |
| b4d5a91d | 147 | ptr = getDmaRam(madr, &words_max); |
| 7a8d521f | 148 | if (ptr == INVALID_PTR) { |
| 38266cd6 | 149 | log_unhandled("bad dma2 madr %x\n", madr); |
| ef79bbde P |
150 | break; |
| 151 | } | |
| 57a757ce | 152 | // BA blocks * BS words (word = 32-bits) |
| 38266cd6 | 153 | words = words_copy = (bcr >> 16) * (bcr & 0xffff); |
| 154 | if (words > words_max) { | |
| 155 | log_unhandled("bad dma2 madr %x bcr %x\n", madr, bcr); | |
| 156 | words_copy = words_max; | |
| 157 | } | |
| b4d5a91d | 158 | GPU_readDataMem(ptr, words_copy); |
| 159 | psxCpu->Clear(madr, words_copy); | |
| 58ebb94c | 160 | |
| 38266cd6 | 161 | HW_DMA2_MADR = SWAPu32(madr + words_copy * 4); |
| 57a757ce | 162 | |
| abf09485 | 163 | // careful: gpu_state_change() also messes with this |
| f8896d18 | 164 | psxRegs.gpuIdleAfter = psxRegs.cycle + words / 4 + 16; |
| 57a757ce | 165 | // already 32-bit word size ((size * 4) / 4) |
| 9a0a61d2 | 166 | set_event(PSXINT_GPUDMA, words / 4); |
| 57a757ce | 167 | return; |
| ef79bbde P |
168 | |
| 169 | case 0x01000201: // mem2vram | |
| b8cdf26f | 170 | PSXDMA_LOG("*** DMA 2 - GPU mem2vram *** %x addr = %x size = %x\n", chcr, madr, bcr); |
| 38266cd6 | 171 | words = words_left = (bcr >> 16) * (bcr & 0xffff); |
| 172 | while (words_left > 0) { | |
| 173 | ptr = getDmaRam(madr, &words_max); | |
| 174 | if (ptr == INVALID_PTR) { | |
| 175 | log_unhandled("bad2 dma madr %x\n", madr); | |
| 176 | break; | |
| 177 | } | |
| 178 | words_copy = min(words_left, words_max); | |
| 179 | GPU_writeDataMem(ptr, words_copy); | |
| 180 | words_left -= words_copy; | |
| 181 | madr += words_copy * 4; | |
| ef79bbde | 182 | } |
| 58ebb94c | 183 | |
| 38266cd6 | 184 | HW_DMA2_MADR = SWAPu32(madr); |
| 57a757ce | 185 | |
| abf09485 | 186 | // careful: gpu_state_change() also messes with this |
| f8896d18 | 187 | psxRegs.gpuIdleAfter = psxRegs.cycle + words / 4 + 16; |
| 57a757ce | 188 | // already 32-bit word size ((size * 4) / 4) |
| 9a0a61d2 | 189 | set_event(PSXINT_GPUDMA, words / 4); |
| ef79bbde P |
190 | return; |
| 191 | ||
| 192 | case 0x01000401: // dma chain | |
| b8cdf26f | 193 | PSXDMA_LOG("*** DMA 2 - GPU dma chain *** %x addr = %x size = %x\n", chcr, madr, bcr); |
| 8c84ba5f | 194 | // when not emulating walking progress, end immediately |
| 78c8d214 | 195 | // (some games abort the dma and read madr so break out of that logic) |
| 8c84ba5f | 196 | madr_next = 0xffffff; |
| 57a757ce | 197 | |
| 8c84ba5f | 198 | do_walking = Config.GpuListWalking; |
| 78c8d214 | 199 | if (do_walking < 0) |
| 8c84ba5f | 200 | do_walking = Config.hacks.gpu_slow_list_walking; |
| 201 | madr_next_p = do_walking ? &madr_next : NULL; | |
| 202 | ||
| 8412166f | 203 | cycles_sum = GPU_dmaChain((u32 *)psxM, madr & 0x1fffff, |
| 204 | madr_next_p, &cycles_last_cmd); | |
| 58ebb94c | 205 | |
| 8c84ba5f | 206 | HW_DMA2_MADR = SWAPu32(madr_next); |
| 58ebb94c | 207 | |
| 78c8d214 | 208 | // timing hack with some lame heuristics |
| 4a15db3f | 209 | if (Config.gpu_timing_override && (do_walking || cycles_sum > 64) |
| 210 | && !(HW_GPU_STATUS & SWAP32(PSXGPU_DHEIGHT | PSXGPU_RGB24))) | |
| 78c8d214 | 211 | cycles_sum = Config.gpu_timing_override; |
| 57a757ce | 212 | |
| 8412166f | 213 | psxRegs.gpuIdleAfter = psxRegs.cycle + cycles_sum + cycles_last_cmd; |
| 214 | set_event(PSXINT_GPUDMA, cycles_sum); | |
| 95bcdd38 | 215 | //printf("%u dma2cf: %6ld,%4d %08x %08x %08x %08x\n", psxRegs.cycle, |
| 0e067f55 | 216 | // cycles_sum, cycles_last_cmd, madr, bcr, chcr, HW_DMA2_MADR); |
| 57a757ce | 217 | return; |
| ef79bbde | 218 | |
| ef79bbde | 219 | default: |
| 6c9db47c | 220 | log_unhandled("*** DMA 2 - GPU unknown *** %x addr = %x size = %x\n", chcr, madr, bcr); |
| ef79bbde | 221 | break; |
| ef79bbde P |
222 | } |
| 223 | ||
| 224 | HW_DMA2_CHCR &= SWAP32(~0x01000000); | |
| 225 | DMA_INTERRUPT(2); | |
| 226 | } | |
| 227 | ||
| 228 | void gpuInterrupt() { | |
| 8c84ba5f | 229 | if (HW_DMA2_CHCR == SWAP32(0x01000401) && !(HW_DMA2_MADR & SWAP32(0x800000))) |
| 230 | { | |
| 4a15db3f | 231 | u32 madr_next = SWAPu32(HW_DMA2_MADR); |
| 232 | s32 cycles_sum = psxRegs.gpuIdleAfter - psxRegs.cycle; | |
| 49f5a273 | 233 | s32 cycles_last_cmd = 0; |
| 49f5a273 | 234 | |
| 4a15db3f | 235 | do { |
| 236 | cycles_sum += cycles_last_cmd; | |
| 237 | cycles_sum += GPU_dmaChain((u32 *)psxM, madr_next & 0x1fffff, | |
| 238 | &madr_next, &cycles_last_cmd); | |
| 239 | } | |
| 240 | while (cycles_sum <= 0 && !(madr_next & 0x800000)); | |
| 8c84ba5f | 241 | HW_DMA2_MADR = SWAPu32(madr_next); |
| 8412166f | 242 | psxRegs.gpuIdleAfter = psxRegs.cycle + cycles_sum + cycles_last_cmd; |
| 243 | set_event(PSXINT_GPUDMA, cycles_sum); | |
| 4a15db3f | 244 | //printf("%u dma2cn: %6d,%4d %08x\n", psxRegs.cycle, cycles_sum, |
| 78c8d214 | 245 | // cycles_last_cmd, HW_DMA2_MADR); |
| 8c84ba5f | 246 | return; |
| 247 | } | |
| ad418c19 | 248 | if (HW_DMA2_CHCR & SWAP32(0x01000000)) |
| 249 | { | |
| 250 | HW_DMA2_CHCR &= SWAP32(~0x01000000); | |
| 251 | DMA_INTERRUPT(2); | |
| 252 | } | |
| ef79bbde P |
253 | } |
| 254 | ||
| 0e067f55 | 255 | void psxAbortDma2() { |
| 256 | psxRegs.gpuIdleAfter = psxRegs.cycle + 32; | |
| 257 | } | |
| 258 | ||
| ef79bbde | 259 | void psxDma6(u32 madr, u32 bcr, u32 chcr) { |
| 38266cd6 | 260 | u32 words, words_max; |
| 261 | u32 *mem; | |
| ef79bbde | 262 | |
| ef79bbde | 263 | PSXDMA_LOG("*** DMA6 OT *** %x addr = %x size = %x\n", chcr, madr, bcr); |
| ef79bbde P |
264 | |
| 265 | if (chcr == 0x11000002) { | |
| b54a1ac7 | 266 | madr &= ~3; |
| 38266cd6 | 267 | mem = getDmaRam(madr, &words_max); |
| 7a8d521f | 268 | if (mem == INVALID_PTR) { |
| 38266cd6 | 269 | log_unhandled("bad6 dma madr %x\n", madr); |
| 206a936e | 270 | HW_DMA6_CHCR &= SWAP32(~0x11000000); |
| ef79bbde P |
271 | DMA_INTERRUPT(6); |
| 272 | return; | |
| 273 | } | |
| 274 | ||
| 57a757ce | 275 | // already 32-bit size |
| fc4803bd | 276 | words = bcr; |
| 57a757ce | 277 | |
| 38266cd6 | 278 | while (bcr-- && mem > (u32 *)psxM) { |
| ef79bbde P |
279 | *mem-- = SWAP32((madr - 4) & 0xffffff); |
| 280 | madr -= 4; | |
| 281 | } | |
| 086adfff | 282 | *++mem = SWAP32(0xffffff); |
| 57a757ce | 283 | |
| fc4803bd | 284 | // halted |
| 285 | psxRegs.cycle += words; | |
| 9a0a61d2 | 286 | set_event(PSXINT_GPUOTCDMA, 16); |
| 57a757ce | 287 | return; |
| ef79bbde | 288 | } |
| ef79bbde P |
289 | else { |
| 290 | // Unknown option | |
| 6c9db47c | 291 | log_unhandled("*** DMA6 OT - unknown *** %x addr = %x size = %x\n", chcr, madr, bcr); |
| ef79bbde | 292 | } |
| ef79bbde | 293 | |
| 206a936e | 294 | HW_DMA6_CHCR &= SWAP32(~0x11000000); |
| ef79bbde P |
295 | DMA_INTERRUPT(6); |
| 296 | } | |
| 297 | ||
| 57a757ce | 298 | void gpuotcInterrupt() |
| 299 | { | |
| ad418c19 | 300 | if (HW_DMA6_CHCR & SWAP32(0x01000000)) |
| 301 | { | |
| 206a936e | 302 | HW_DMA6_CHCR &= SWAP32(~0x11000000); |
| ad418c19 | 303 | DMA_INTERRUPT(6); |
| 304 | } | |
| 57a757ce | 305 | } |