ultimecia

A ps1 emulator in c
Log | Files | Refs

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 }