interconnect.c (14606B)
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 #include "interconnect.h" 5 #include "bios.h" 6 #include "mem.h" 7 #include "util.h" 8 #include "types.h" 9 #include "gpu.h" 10 #include "cdrom.h" 11 #include "log.h" 12 #include "cpu.h" 13 14 extern CPU *cpu; 15 16 Interconnect* 17 new_interconnect(void) { 18 Interconnect* inter = (Interconnect*)malloc(sizeof(Interconnect)); 19 inter->bios = BIOS_new("roms/scph1001.bin"); 20 inter->ram = RAM_new(); 21 inter->dma = DMA_new(); 22 inter->gpu = GPU_new(); 23 inter->cdrom = cdrom_new(); 24 inter->irq = irq_new(); 25 return inter; 26 } 27 28 u8 29 INTER_load8(Interconnect* inter, u32 addr) 30 { 31 u32 offset; 32 u32 abs_addr; 33 u32 contains; 34 35 offset = 0; 36 contains = 0; 37 abs_addr = mask_region(addr); 38 39 /* Assert abs_address Mappings */ 40 contains = UTIL_contains(BIOS_START, BIOS_SIZE, abs_addr, &offset); 41 if (contains) 42 return BIOS_load8(inter->bios, offset); 43 44 contains = UTIL_contains(EXPANSION1_START, EXPANSION1_SIZE, abs_addr, &offset); 45 if (contains) 46 return 0xff; 47 48 contains = UTIL_contains(RAM_START, RAM_SIZE, abs_addr, &offset); 49 if (contains) 50 return RAM_load8(inter->ram, offset); 51 52 contains = UTIL_contains(IRQ_CONTROL_START, IRQ_CONTROL_SIZE, abs_addr, &offset); 53 if (contains) 54 { 55 log_debug("IRQ CONTROL read %08X", offset); 56 return 0; 57 } 58 59 contains = UTIL_contains(CDROM_START, CDROM_SIZE, abs_addr, &offset); 60 if (contains) 61 { 62 return cdrom_load(inter->cdrom, offset); 63 } 64 65 log_fatal("Unhandled Load8 At Address %08X", addr); 66 exit(EXIT_FAILURE); 67 } 68 69 u16 70 INTER_load16(Interconnect* inter, u32 addr) 71 { 72 u32 offset; 73 u32 abs_addr; 74 u32 contains; 75 76 offset = 0; 77 contains = 0; 78 abs_addr = mask_region(addr); 79 80 if (addr % 2 != 0) 81 { 82 log_fatal("Unaligned_load16_abs_address: %08X", abs_addr); 83 exit(EXIT_FAILURE); 84 } 85 86 /* Assert abs_address Mappings */ 87 contains = UTIL_contains(SPU_START, SPU_SIZE, abs_addr, &offset); 88 if (contains) 89 { 90 log_warn("Unhandled read from SPU register: %08X", offset); 91 return 0; 92 } 93 94 contains = UTIL_contains(RAM_START, RAM_SIZE, abs_addr, &offset); 95 if (contains) 96 return RAM_load16(inter->ram, offset); 97 98 contains = UTIL_contains(IRQ_CONTROL_START, IRQ_CONTROL_SIZE, abs_addr, &offset); 99 if (contains) 100 { 101 log_debug("IRQ CONTROL read %08X", offset); 102 return 0; 103 } 104 105 log_fatal("Unhandled Load16 At Address %08X", abs_addr); 106 exit(EXIT_FAILURE); 107 } 108 109 u32 110 INTER_load32(Interconnect* inter, u32 addr) 111 { 112 u32 offset; 113 u32 abs_addr; 114 u32 contains; 115 116 offset = 0; 117 contains = 0; 118 abs_addr = mask_region(addr); 119 120 if (addr % 4 != 0) 121 { 122 log_fatal("Unaligned_load32_abs_address: %08X", abs_addr); 123 exit(EXIT_FAILURE); 124 } 125 126 /* Assert abs_address Mappings */ 127 contains = UTIL_contains(BIOS_START, BIOS_SIZE, abs_addr, &offset); 128 if (contains) 129 return BIOS_load32(inter->bios, offset); 130 131 contains = UTIL_contains(RAM_START, RAM_SIZE, abs_addr, &offset); 132 if (contains) 133 return RAM_load32(inter->ram, offset); 134 135 contains = UTIL_contains(IRQ_CONTROL_START, IRQ_CONTROL_SIZE, abs_addr, &offset); 136 if (contains) 137 { 138 fprintf(stderr, "[PC] %08X", cpu->pc); 139 return irq_load(&inter->irq, offset); 140 //log_debug("IRQ CONTROL read %08X", offset); 141 } 142 143 contains = UTIL_contains(TIMERS_START, TIMERS_SIZE, abs_addr, &offset); 144 if (contains) 145 { 146 log_debug("TIMERS read %08X", offset); 147 return 0; 148 } 149 150 contains = UTIL_contains(DMA_START, DMA_SIZE, abs_addr, &offset); 151 if (contains) 152 { 153 return INTER_dma_reg(inter, offset); 154 } 155 156 contains = UTIL_contains(GPU_START, GPU_SIZE, abs_addr, &offset); 157 if (contains) 158 { 159 160 log_debug("GPU read %08X to offset %08X", abs_addr, offset); 161 switch(offset) 162 { 163 case 0: return GPU_read(inter->gpu); /* NOT COMPLETE */ 164 case 4: return GPU_status(inter->gpu); 165 default: return 0; 166 } 167 } 168 169 log_fatal("Unhandled Load32 At Address %08X", addr); 170 exit(EXIT_FAILURE); 171 } 172 173 void 174 INTER_store8(Interconnect* inter, u32 addr, u8 val) 175 { 176 u32 offset; 177 u32 contains; 178 u32 abs_addr; 179 180 offset = 0; 181 contains = 0; 182 abs_addr = mask_region(addr); 183 184 contains = UTIL_contains(EXPANSION2_START, EXPANSION2_SIZE, abs_addr, &offset); 185 if (contains) 186 { 187 log_debug("Ignoring write to EXPANSION2 register at: %08X", abs_addr); 188 return; 189 } 190 191 contains = UTIL_contains(RAM_START, RAM_SIZE, abs_addr, &offset); 192 if (contains) 193 { 194 RAM_store8(inter->ram, offset, val); 195 return; 196 } 197 198 contains = UTIL_contains(CDROM_START, CDROM_SIZE, abs_addr, &offset); 199 if (contains) 200 { 201 cdrom_write(inter->cdrom, offset, val); 202 return; 203 } 204 205 log_fatal("Unhandled Store8 At Address %08X", abs_addr); 206 exit(EXIT_FAILURE); 207 } 208 209 void 210 INTER_store16(Interconnect* inter, u32 addr, u16 val) 211 { 212 u32 offset; 213 u32 contains; 214 u32 abs_addr; 215 216 offset = 0; 217 contains = 0; 218 abs_addr = mask_region(addr); 219 220 if (addr % 2 != 0) 221 { 222 log_fatal("Unaligned_store16_address: %08X", addr); 223 exit(EXIT_FAILURE); 224 } 225 226 contains = UTIL_contains(SPU_START, SPU_SIZE, abs_addr, &offset); 227 if (contains) 228 { 229 log_debug("Ignoring SPU register write: %X", abs_addr); 230 return; 231 } 232 233 contains = UTIL_contains(TIMERS_START, TIMERS_SIZE, abs_addr, &offset); 234 if (contains) 235 { 236 log_debug("Ignoring TIMER register write to offset: %X", offset); 237 return; 238 } 239 240 contains = UTIL_contains(RAM_START, RAM_SIZE, abs_addr, &offset); 241 if (contains) 242 { 243 RAM_store16(inter->ram, offset, val); 244 return; 245 } 246 247 contains = UTIL_contains(IRQ_CONTROL_START, IRQ_CONTROL_SIZE, abs_addr, &offset); 248 if (contains) 249 { 250 log_debug("IRQ control write %08X at address %08X", val, offset); 251 return; 252 } 253 254 log_fatal("Unhandled Store16 At Address %08X", abs_addr); 255 exit(EXIT_FAILURE); 256 } 257 258 void 259 INTER_store32(Interconnect* inter, u32 addr, u32 val) 260 { 261 u32 offset; 262 u32 contains; 263 u32 abs_addr; 264 265 offset = 0; 266 contains = 0; 267 abs_addr = mask_region(addr); 268 269 if (addr % 4 != 0) 270 log_warn("Unaligned_store32_address: %08X", addr); 271 272 contains = UTIL_contains(RAM_START, RAM_SIZE, abs_addr, &offset); 273 if (contains) { 274 RAM_store32(inter->ram, offset, val); 275 return; 276 } 277 278 contains = UTIL_contains(RAM_SIZE_START, RAM_SIZE_SIZE, abs_addr, &offset); 279 if (contains) 280 { 281 log_debug("Ignoring RAM_SIZE register write %X", abs_addr); 282 return; 283 } 284 285 contains = UTIL_contains(CACHECONTROL_START, CACHECONTROL_SIZE, abs_addr, &offset); 286 if (contains) 287 { 288 log_debug("Ignoring CACHECONTROL abs_address write: %X", abs_addr); 289 return; 290 } 291 292 contains = UTIL_contains(IRQ_CONTROL_START, IRQ_CONTROL_SIZE, abs_addr, &offset); 293 if (contains) 294 { 295 log_debug("Ignoring IRQ CONTROL write %08X to address %08X", val, offset); 296 return; 297 } 298 299 contains = UTIL_contains(DMA_START, DMA_SIZE, abs_addr, &offset); 300 if (contains) 301 { 302 INTER_set_dma_reg(inter, offset, val); 303 return; 304 } 305 306 contains = UTIL_contains(TIMERS_START, TIMERS_SIZE, abs_addr, &offset); 307 if (contains) 308 { 309 log_debug("TIMER register write %08X to offset: %08X", val, offset); 310 return; 311 } 312 313 contains = UTIL_contains(GPU_START, GPU_SIZE, abs_addr, &offset); 314 if (contains) 315 { 316 log_debug("GPU write %08X to address %08X", val, offset); 317 switch (offset) { 318 case 0: GPU_gp0(inter->gpu, val); break; 319 case 4: GPU_gp1(inter->gpu, val); break; 320 default: log_fatal("GPU write %08X: %08X", offset, val); exit(EXIT_FAILURE); 321 } 322 323 return; 324 } 325 326 contains = UTIL_contains(SYSCONTROL_START, SYSCONTROL_SIZE, abs_addr, &offset); 327 if (contains) 328 { 329 switch(offset) { 330 case 0: 331 if (val != 0x1F000000) { 332 log_fatal("Bad Expansion 1 base abs_address: %08X", val); 333 exit(EXIT_FAILURE); 334 } 335 break; 336 case 4: 337 if (val != 0x1F802000) { 338 log_fatal("Bad expansion 2 base abs_address: %08X", val); 339 exit(EXIT_FAILURE); 340 } 341 break; 342 default: 343 log_warn("Unhandled write to SYSCONTROL register"); 344 return; 345 } 346 return; 347 } 348 349 log_fatal("Unhandled Store32 At Address %08X", addr); 350 exit(EXIT_FAILURE); 351 } 352 353 u32 354 INTER_dma_reg(Interconnect* inter, u32 offset) 355 { 356 u32 major, minor, val; 357 Channel* channel; 358 major = (offset & 0x70) >> 4; 359 minor = offset & 0xf; 360 361 switch (major) { 362 case 0: case 1: case 2: case 3: case 4: case 5: case 6: 363 364 channel = &inter->dma->channels[(Port)major]; 365 switch (minor) { 366 case 0: val = CHANNEL_base(channel); break; 367 case 4: val = CHANNEL_block_control(channel); break; 368 case 8: val = CHANNEL_control(channel); break; 369 default: log_fatal("Unhandled DMA read at %08X", offset); exit(EXIT_FAILURE); 370 } 371 break; 372 case 7: 373 switch (minor) { 374 case 0: val = DMA_control(inter->dma); break; 375 case 4: val = DMA_interrupt(inter->dma); break; 376 default: log_fatal("Unhandled DMA access %08X", offset); exit(EXIT_FAILURE); 377 } 378 break; 379 default: log_fatal("Unhandled DMA access %08X", offset); exit(EXIT_FAILURE); 380 } 381 382 return val; 383 } 384 385 void 386 INTER_set_dma_reg(Interconnect* inter, u32 offset, u32 val) 387 { 388 u32 major, minor; 389 Channel* channel; 390 Port port, active_port; 391 major = (offset & 0x70) >> 4; 392 minor = offset & 0xf; 393 active_port = PORT_INVALID; 394 395 switch (major) { 396 case 0: case 1: case 2: case 3: case 4: case 5: case 6: 397 398 port = (Port)major; 399 channel = &inter->dma->channels[port]; 400 401 switch (minor) { 402 case 0: CHANNEL_set_base(channel, val); break; 403 case 4: CHANNEL_set_block_control(channel, val); break; 404 case 8: CHANNEL_set_control(channel, val); break; 405 default: log_fatal("Unhandled DMA write at %08X", offset); exit(EXIT_FAILURE); 406 } 407 if (CHANNEL_active(channel)) { 408 active_port = port; 409 } else { 410 active_port = PORT_INVALID; 411 } 412 break; 413 case 7: 414 switch (minor) { 415 case 0: DMA_set_control(inter->dma, val); break; 416 case 4: DMA_set_interrupt(inter->dma, val); break; 417 default: active_port = PORT_INVALID; break;/* fprintf(stderr, "unhandled DMA write %08X\n", offset); exit(EXIT_FAILURE); */ 418 } 419 break; 420 default: log_fatal("unhandled DMA write %08X", offset); exit(EXIT_FAILURE); 421 } 422 423 if (active_port != PORT_INVALID) 424 INTER_do_dma(inter, (Port)active_port); 425 } 426 427 void 428 INTER_do_dma(Interconnect* inter, Port port) 429 { 430 Channel* ch; 431 if (port == PORT_INVALID) { 432 log_fatal("Invalid port doing dma"); 433 exit(EXIT_FAILURE); 434 } 435 436 ch = &inter->dma->channels[(Port)port]; 437 438 switch (ch->sync) { 439 case SYNC_LINKED_LIST: INTER_do_dma_linked_list(inter, port); break; 440 default: INTER_do_dma_block(inter, port); break; 441 } 442 } 443 444 void 445 INTER_do_dma_block(Interconnect* inter, Port port) 446 { 447 Channel *ch; 448 u32 addr; 449 u32 increment; 450 u32 src_word; 451 u32 remsz; 452 453 ch = &inter->dma->channels[port]; 454 remsz = 0; 455 456 switch (ch->step) { 457 case STEP_INCREMENT: increment = 4; break; 458 case STEP_DECREMENT: increment = -4; break; 459 default: log_fatal("Unreachable!"); exit(EXIT_FAILURE); 460 } 461 462 addr = ch->base; 463 464 /* printf("%d\n", port); 465 / printf("%08X %08X %d\n", ch->block_size, ch->block_count, (Port)ch->step); */ 466 467 if (!CHANNEL_transfer_size(ch, &remsz)) { 468 log_fatal("Couldn't figure out DMA block transfer size"); 469 exit(EXIT_FAILURE); 470 } 471 472 while (remsz > 0) { 473 u32 cur_addr; 474 cur_addr = addr & 0x1ffffc; 475 476 switch (ch->direction) { 477 case DIR_FROM_RAM: 478 src_word = RAM_load32(inter->ram, cur_addr); 479 switch(port) { 480 case PORT_GPU: 481 GPU_gp0(inter->gpu, src_word); 482 log_debug("GPU data %08X", src_word); 483 break; 484 default: 485 log_fatal("Unhandled DMA destination port: %d", (u8)port); 486 exit(EXIT_FAILURE); 487 } 488 break; 489 case DIR_TO_RAM: 490 switch(port) { 491 case PORT_OTC: 492 if (remsz == 1) { 493 src_word = 0xffffff; 494 } else { 495 src_word = (addr - 4) & 0x1fffff; 496 } 497 break; 498 default: 499 log_fatal("Unhandled DMA source port: %d", (u8)port); 500 exit(EXIT_FAILURE); 501 } 502 RAM_store32(inter->ram, cur_addr, src_word); 503 break; 504 default: break; 505 } 506 507 addr += increment; 508 remsz--; 509 } 510 CHANNEL_done(ch); 511 } 512 513 void 514 INTER_do_dma_linked_list(Interconnect* inter, Port port) 515 { 516 Channel *ch; 517 u32 addr, header, remsz, command; 518 519 ch = &inter->dma->channels[port]; 520 addr = ch->base & 0x1ffffc; 521 522 if (ch->direction == DIR_TO_RAM) { 523 log_fatal("Invalid DMA direction for linked list mode"); 524 exit(EXIT_FAILURE); 525 } 526 527 if (port != PORT_GPU) { 528 log_fatal("Attempted linked list DMA on port %d", (u8)port); 529 exit(EXIT_FAILURE); 530 } 531 532 while (1) { 533 header = RAM_load32(inter->ram, addr); 534 remsz = header >> 24; 535 536 while (remsz > 0) { 537 addr = (addr + 4) & 0x1ffffc; 538 command = RAM_load32(inter->ram, addr); 539 GPU_gp0(inter->gpu, command); 540 log_debug("GPU command %08X", command); 541 remsz--; 542 } 543 544 if ((header & 0x800000) != 0) 545 break; 546 547 addr = header & 0x1ffffc; 548 549 } 550 CHANNEL_done(ch); 551 }