ultimecia

A ps1 emulator in c
Log | Files | Refs

commit b5095fb6afc35d83145bfe8f303904e81784d822
parent fd282fe3e4bf4f32b895a3719da601f57c9c3d70
Author: root <root@fif>
Date:   Sat, 27 Apr 2024 10:08:08 +0000

COMPLETED DMA. still a lot to cover!!

Diffstat:
Msrc/interconnect.c | 136++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/interconnect.h | 1+
Msrc/mem.c | 35++++++++++++++++++++++++++++++++---
Msrc/mem.h | 17++++++++++-------
4 files changed, 164 insertions(+), 25 deletions(-)

diff --git a/src/interconnect.c b/src/interconnect.c @@ -123,6 +123,13 @@ INTER_load32(Interconnect* inter, u32 addr) return 0; } + contains = UTIL_contains(TIMERS_START, TIMERS_SIZE, abs_addr, &offset); + if (contains) + { + printf("TIMERS read %08X\n", offset); + return 0; + } + contains = UTIL_contains(DMA_START, DMA_SIZE, abs_addr, &offset); if (contains) { @@ -136,7 +143,7 @@ INTER_load32(Interconnect* inter, u32 addr) switch(offset) { - case 4: return 0x10000000; + case 4: return 0x1c000000; default: return 0; } } @@ -313,12 +320,17 @@ u32 INTER_dma_reg(Interconnect* inter, u32 offset) { u32 major, minor, val; + Channel* channel; major = (offset & 0x70) >> 4; minor = offset & 0xf; switch (major) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: - Channel* channel; + //if ((Port)major < 0) { + // fprintf(stderr, "WHATTTTTTTTT"); + // exit(1); + //} + channel = &inter->dma->channels[(Port)major]; switch (minor) { case 0: val = CHANNEL_base(channel); break; @@ -343,47 +355,51 @@ INTER_dma_reg(Interconnect* inter, u32 offset) void INTER_set_dma_reg(Interconnect* inter, u32 offset, u32 val) { - u32 major, minor, active_port; + u32 major, minor; + Channel* channel; + Port port, active_port; major = (offset & 0x70) >> 4; minor = offset & 0xf; - active_port = (Port)0; + active_port = PORT_INVALID; switch (major) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: - //printf("MAJOR: %d\n", major); - //printf("MINOR: %d\n", minor); - Channel* channel; - Port port; port = (Port)major; channel = &inter->dma->channels[port]; + switch (minor) { case 0: CHANNEL_set_base(channel, val); break; case 4: CHANNEL_set_block_control(channel, val); break; case 8: CHANNEL_set_control(channel, val); break; default: fprintf(stderr, "Unhandled DMA write at %08X\n", offset); exit(EXIT_FAILURE); } - if (CHANNEL_active(channel)) active_port = port; else active_port = -1; + if (CHANNEL_active(channel)) { + active_port = port; + } else { + active_port = PORT_INVALID; + } break; case 7: switch (minor) { case 0: DMA_set_control(inter->dma, val); break; case 4: DMA_set_interrupt(inter->dma, val); break; - default: active_port = -1; break;// fprintf(stderr, "unhandled DMA write %08X\n", offset); exit(EXIT_FAILURE); + default: active_port = PORT_INVALID; break;// fprintf(stderr, "unhandled DMA write %08X\n", offset); exit(EXIT_FAILURE); } break; default: fprintf(stderr, "unhandled DMA write %08X\n", offset); exit(EXIT_FAILURE); } - if (active_port > -1) - INTER_do_dma(inter, (Port)active_port); + if (active_port != PORT_INVALID) + INTER_do_dma(inter, (Port)active_port); } void INTER_do_dma(Interconnect* inter, Port port) { Channel* ch; - if ((Port)port < 0) { + if (port == PORT_INVALID) { + printf("%d\n", port); fprintf(stderr, "Invalid port doing dma\n"); exit(EXIT_FAILURE); } @@ -391,7 +407,7 @@ INTER_do_dma(Interconnect* inter, Port port) ch = &inter->dma->channels[(Port)port]; switch (ch->sync) { - case SYNC_LINKED_LIST: fprintf(stderr, "Linked list mode unsupported\n"); exit(EXIT_FAILURE); + case SYNC_LINKED_LIST: INTER_do_dma_linked_list(inter, port); break; default: INTER_do_dma_block(inter, port); break; } } @@ -400,9 +416,13 @@ void INTER_do_dma_block(Interconnect* inter, Port port) { Channel *ch; - u32 addr, transfer_size, increment; + u32 addr; + u32 increment; + u32 src_word; + u32 remsz; ch = &inter->dma->channels[port]; + remsz = 0; switch (ch->step) { case STEP_INCREMENT: increment = 4; break; @@ -410,5 +430,91 @@ INTER_do_dma_block(Interconnect* inter, Port port) default: fprintf(stderr, "Unreachable!\n"); exit(EXIT_FAILURE); } + addr = ch->base; + + // printf("%d\n", port); + // printf("%08X %08X %d\n", ch->block_size, ch->block_count, (Port)ch->step); + + if (!CHANNEL_transfer_size(ch, &remsz)) { + fprintf(stderr, "Couldn't figure out DMA block transfer size\n"); + exit(EXIT_FAILURE); + } + + while (remsz > 0) { + u32 cur_addr; + cur_addr = addr & 0x1ffffc; + + switch (ch->direction) { + case DIR_FROM_RAM: + src_word = RAM_load32(inter->ram, cur_addr); + switch(port) { + case PORT_GPU: + printf("GPU data %08X\n", src_word); + break; + default: + fprintf(stderr, "Unhandled DMA destination port: %d", (u8)port); + exit(EXIT_FAILURE); + } + break; + case DIR_TO_RAM: + switch(port) { + case PORT_OTC: + if (remsz == 1) { + src_word = 0xffffff; + } else { + src_word = (addr - 4) & 0x1fffff; + } + break; + default: + fprintf(stderr, "Unhandled DMA source port: %d", (u8)port); + exit(EXIT_FAILURE); + } + RAM_store32(inter->ram, cur_addr, src_word); + break; + default: break; + } + + addr += increment; + remsz--; + } + CHANNEL_done(ch); +} +void +INTER_do_dma_linked_list(Interconnect* inter, Port port) +{ + Channel *ch; + u32 addr, header, remsz, command; + + ch = &inter->dma->channels[port]; + addr = ch->base & 0x1ffffc; + + if (ch->direction == DIR_TO_RAM) { + fprintf(stderr, "Invalid DMA direction for linked list mode\n"); + exit(EXIT_FAILURE); + } + + if (port != PORT_GPU) { + fprintf(stderr, "Attempted linked list DMA on port %d\n", (u8)port); + exit(EXIT_FAILURE); + } + + while (1) { + header = RAM_load32(inter->ram, addr); + remsz = header >> 24; + + while (remsz > 0) { + addr = (addr + 4) & 0x1ffffc; + command = RAM_load32(inter->ram, addr); + printf("GPU command %08X\n", command); + remsz--; + } + + if ((header & 0x800000) != 0) + break; + + addr = header & 0x1ffffc; + + } + CHANNEL_done(ch); } diff --git a/src/interconnect.h b/src/interconnect.h @@ -24,3 +24,4 @@ u32 INTER_dma_reg(Interconnect*, u32); void INTER_set_dma_reg(Interconnect*, u32, u32); void INTER_do_dma(Interconnect*, Port); void INTER_do_dma_block(Interconnect*, Port); +void INTER_do_dma_linked_list(Interconnect*, Port); diff --git a/src/mem.c b/src/mem.c @@ -47,7 +47,7 @@ void CHANNEL_set_control(Channel* ch, u32 val) { ch->direction = (Direction)(val & 1); - ch->step = (Step)(val >> 1); + ch->step = (Step)((val >> 1) & 1); ch->chop = (val >> 8) & 1; ch->sync = (Sync)((val >> 9) & 3); ch->chop_dma_sz = (u8)((val >> 16) & 7); @@ -100,6 +100,28 @@ CHANNEL_active(Channel* ch) } +u8 +CHANNEL_transfer_size(Channel* ch, u32 *res) +{ + switch (ch->sync) { + case SYNC_MANUAL: + *res = (u32)ch->block_size; + return 1; + case SYNC_REQUEST: + *res = (u32)((u32)ch->block_size * (u32)(ch->block_count)); + return 1; + case SYNC_LINKED_LIST: return 0; + default: fprintf(stderr, "UNREACHABLE"); exit(EXIT_FAILURE); + } +} + +void +CHANNEL_done(Channel* ch) +{ + ch->enable = 0; + ch->trigger = 0; +} + DMA* DMA_new(void) { @@ -110,7 +132,14 @@ DMA_new(void) dma->channel_irq_flags = 0; dma->force_irq = 0; dma->irq_dummy = 0; + + //for (i = 0; i < 8; i++) + // dma->channels[i] = *CHANNEL_new(); + memset(dma->channels, 0, sizeof(Channel)*7); + + // printf("%d\n", (Port)dma->channels[1].step); + // exit(1); return dma; } @@ -156,9 +185,9 @@ DMA_set_interrupt(DMA* dma, u32 val) u32 ack; dma->irq_dummy = (u8)(val & 0x3f); - dma->force_irq = ((val >> 15) & 1) != 0; + dma->force_irq = (val >> 15) & 1; dma->channel_irq_en = (u8)((val >> 16) & 0x7f); - dma->irq_en = ((val >> 23) & 1) != 0; + dma->irq_en = (val >> 23) & 1; ack = (u8)((val >> 24) & 0x3f); dma->channel_irq_flags &= ~ack; diff --git a/src/mem.h b/src/mem.h @@ -20,13 +20,14 @@ typedef enum e_Sync { } Sync; typedef enum e_Port { - PORT_MDEC_IN, - PORT_MDEC_OUT, - PORT_GPU, - PORT_CD_ROM, - PORT_SPU, - PORT_PIO, - PORT_OTC + PORT_MDEC_IN = 0, + PORT_MDEC_OUT = 1, + PORT_GPU = 2, + PORT_CD_ROM = 3, + PORT_SPU = 4, + PORT_PIO = 5, + PORT_OTC = 6, + PORT_INVALID = 7, } Port; typedef struct RAM { @@ -75,6 +76,8 @@ void CHANNEL_set_base(Channel*, u32); u32 CHANNEL_block_control(Channel*); void CHANNEL_set_block_control(Channel*, u32); u8 CHANNEL_active(Channel*); +u8 CHANNEL_transfer_size(Channel*, u32*); +void CHANNEL_done(Channel*); RAM* RAM_new(void); u8 RAM_load8(RAM*, u32);