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:
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);