From 7f336a762e6d2de4a21feb9da9b2b8732e60bf60 Mon Sep 17 00:00:00 2001 From: espes Date: Sat, 13 Jun 2015 19:55:48 +1000 Subject: [PATCH] dma kinda --- hw/xbox/dsp/dsp.c | 66 +++++++++++++---- hw/xbox/dsp/dsp.h | 6 +- hw/xbox/dsp/dsp_dma.c | 153 +++++++++++++++++++++++++++++++++++++++- hw/xbox/dsp/dsp_dma.h | 12 ++++ hw/xbox/dsp/dsp_emu.inl | 24 +++++-- 5 files changed, 239 insertions(+), 22 deletions(-) diff --git a/hw/xbox/dsp/dsp.c b/hw/xbox/dsp/dsp.c index 9ef5bfa9ba..06b8a6acdb 100644 --- a/hw/xbox/dsp/dsp.c +++ b/hw/xbox/dsp/dsp.c @@ -27,7 +27,7 @@ #include #include -#include +#include "qemu-common.h" #include "dsp_cpu.h" #include "dsp_dma.h" @@ -38,12 +38,9 @@ #define BITMASK(x) ((1<<(x))-1) #define ARRAYSIZE(x) (int)(sizeof(x)/sizeof(x[0])) -#ifndef container_of -#define container_of(ptr, type, member) ({ \ - const typeof(((type *) 0)->member) *__mptr = (ptr); \ - (type *) ((char *) __mptr - offsetof(type, member));}) -#endif - +#define INTERRUPT_ABORT_FRAME (1 << 0) +#define INTERRUPT_START_FRAME (1 << 1) +#define INTERRUPT_DMA_EOL (1 << 7) #define DPRINTF(s, ...) fprintf(stderr, s, ## __VA_ARGS__) @@ -51,12 +48,14 @@ struct DSPState { dsp_core_t core; DSPDMAState dma; int save_cycles; + + uint32_t interrupts; }; static uint32_t read_peripheral(dsp_core_t* core, uint32_t address); static void write_peripheral(dsp_core_t* core, uint32_t address, uint32_t value); -DSPState* dsp_init(void) +DSPState* dsp_init(void* scratch_rw_opaque, dsp_scratch_rw_func scratch_rw) { DPRINTF("dsp_init\n"); @@ -66,7 +65,12 @@ DSPState* dsp_init(void) dsp->core.read_peripheral = read_peripheral; dsp->core.write_peripheral = write_peripheral; + dsp->dma.core = &dsp->core; + dsp->dma.scratch_rw_opaque = scratch_rw_opaque; + dsp->dma.scratch_rw = scratch_rw; + dsp_reset(dsp); + return dsp; } @@ -86,13 +90,30 @@ static uint32_t read_peripheral(dsp_core_t* core, uint32_t address) { printf("read_peripheral 0x%06x\n", address); + uint32_t v = 0xababa; switch(address) { - // case 0xFFFFC5: // interrupt + case 0xFFFFC5: + v = dsp->interrupts; + if (dsp->dma.eol) { + v |= INTERRUPT_DMA_EOL; + } + break; + case 0xFFFFD4: + v = dsp_dma_read(&dsp->dma, DMA_NEXT_BLOCK); + break; + case 0xFFFFD5: + v = dsp_dma_read(&dsp->dma, DMA_START_BLOCK); + break; case 0xFFFFD6: - return dsp_dma_read(&dsp->dma, DMA_CONTROL); + v = dsp_dma_read(&dsp->dma, DMA_CONTROL); + break; + case 0xFFFFD7: + v = dsp_dma_read(&dsp->dma, DMA_CONFIGURATION); + break; } - return 0xababa; + printf(" -> 0x%06x\n", v); + return v; } static void write_peripheral(dsp_core_t* core, uint32_t address, uint32_t value) { @@ -101,9 +122,24 @@ static void write_peripheral(dsp_core_t* core, uint32_t address, uint32_t value) printf("write_peripheral [0x%06x] = 0x%06x\n", address, value); switch(address) { - // case 0xFFFFC5: // interrupt + case 0xFFFFC5: + dsp->interrupts &= ~value; + if (value & INTERRUPT_DMA_EOL) { + dsp->dma.eol = false; + } + break; + case 0xFFFFD4: + dsp_dma_write(&dsp->dma, DMA_NEXT_BLOCK, value); + break; + case 0xFFFFD5: + dsp_dma_write(&dsp->dma, DMA_START_BLOCK, value); + break; case 0xFFFFD6: - return dsp_dma_write(&dsp->dma, DMA_CONTROL, value); + dsp_dma_write(&dsp->dma, DMA_CONTROL, value); + break; + case 0xFFFFD7: + dsp_dma_write(&dsp->dma, DMA_CONFIGURATION, value); + break; } } @@ -142,6 +178,10 @@ void dsp_bootstrap(DSPState* dsp, const uint32_t* pram, size_t len) memcpy(dsp->core.pram, pram, len); } +void dsp_start_frame(DSPState* dsp) +{ + dsp->interrupts |= INTERRUPT_START_FRAME; +} /** * Disassemble DSP code between given addresses, return next PC address diff --git a/hw/xbox/dsp/dsp.h b/hw/xbox/dsp/dsp.h index f8f84799d2..f2b014313d 100644 --- a/hw/xbox/dsp/dsp.h +++ b/hw/xbox/dsp/dsp.h @@ -31,8 +31,11 @@ typedef struct DSPState DSPState; +typedef void (*dsp_scratch_rw_func)( + uint8_t* ptr, uint64_t addr, size_t len, bool dir); + /* Dsp commands */ -DSPState* dsp_init(void); +DSPState* dsp_init(void* scratch_rw_opaque, dsp_scratch_rw_func scratch_rw); void dsp_destroy(DSPState* dsp); void dsp_reset(DSPState* dsp); @@ -40,6 +43,7 @@ void dsp_step(DSPState* dsp); void dsp_run(DSPState* dsp, int cycles); void dsp_bootstrap(DSPState* dsp, const uint32_t* pmem, size_t len); +void dsp_start_frame(DSPState* dsp); /* Dsp Debugger commands */ diff --git a/hw/xbox/dsp/dsp_dma.c b/hw/xbox/dsp/dsp_dma.c index 1c9db71e02..5c96b4120d 100644 --- a/hw/xbox/dsp/dsp_dma.c +++ b/hw/xbox/dsp/dsp_dma.c @@ -1,9 +1,138 @@ -#include +#include #include +#include #include #include "dsp_dma.h" +#define DMA_CONFIGURATION_AUTOSTART (1 << 0) +#define DMA_CONFIGURATION_AUTOREADY (1 << 1) +#define DMA_CONFIGURATION_IOC_CLEAR (1 << 2) +#define DMA_CONFIGURATION_EOL_CLEAR (1 << 3) +#define DMA_CONFIGURATION_ERR_CLEAR (1 << 4) + +#define DMA_CONTROL_ACTION 0x7 +#define DMA_CONTROL_ACTION_NOP 0 +#define DMA_CONTROL_ACTION_START 1 +#define DMA_CONTROL_ACTION_STOP 2 +#define DMA_CONTROL_ACTION_FREEZE 3 +#define DMA_CONTROL_ACTION_UNFREEZE 4 +#define DMA_CONTROL_ACTION_ABORT 5 +#define DMA_CONTROL_FROZEN (1 << 3) +#define DMA_CONTROL_RUNNING (1 << 4) +#define DMA_CONTROL_STOPPED (1 << 5) + +#define NODE_POINTER_VAL 0x3fff +#define NODE_POINTER_EOL (1 << 14) + +#define NODE_CONTROL_DIRECTION (1 << 1) + +static void dsp_dma_run(DSPDMAState *s) +{ + if (!(s->control & DMA_CONTROL_RUNNING) + || (s->control & DMA_CONTROL_FROZEN)) { + return; + } + while (!(s->next_block & NODE_POINTER_EOL)) { + uint32_t addr = s->next_block & NODE_POINTER_VAL; + assert((addr+6) < sizeof(s->core->xram)); + + uint32_t next_block = s->core->xram[addr]; + uint32_t control = s->core->xram[addr+1]; + uint32_t count = s->core->xram[addr+2]; + uint32_t dsp_offset = s->core->xram[addr+3]; + uint32_t scratch_offset = s->core->xram[addr+4]; + uint32_t scratch_base = s->core->xram[addr+5]; + uint32_t scratch_size = s->core->xram[addr+6]+1; + + printf("\n\n\nQQQ DMA addr %x, control %x, count %x, dsp_offset %x, scratch_offset %x, base %x, size %x\n\n\n", + addr, control, count, dsp_offset, scratch_offset, scratch_base, scratch_size); + + uint32_t format = (control >> 10) & 7; + unsigned int item_size; + uint32_t item_mask; + switch(format) { + case 2: //big-endian? + case 6: + item_size = 4; + item_mask = 0x00FFFFFF; + break; + default: + assert(false); + break; + } + + uint32_t buf_id = (control >> 5) & 0xf; + + size_t scratch_addr = scratch_offset; + if (buf_id == 0xe) { // 'circular'? + assert(scratch_addr == 0); + assert(scratch_offset + count * item_size < scratch_size); + scratch_addr += scratch_base; //?? + } else if (buf_id != 0xf) { // 'offset'? + assert(false); + } + + uint32_t* dsp_ptr; + if (dsp_offset < 0x1800) { + assert(dsp_offset+count < sizeof(s->core->xram)); + dsp_ptr = s->core->xram + dsp_offset; + } else if (dsp_offset >= 0x1800 && dsp_offset < 0x2000) { //? + assert(dsp_offset-0x1800 + count < sizeof(s->core->yram)); + dsp_ptr = s->core->yram + dsp_offset - 0x1800; + } else if (dsp_offset >= 0x2800 && dsp_offset < 0x3800) { //? + assert(dsp_offset-0x2800 + count < sizeof(s->core->pram)); + dsp_ptr = s->core->pram + dsp_offset - 0x2800; + } else { + assert(false); + } + + uint8_t* scratch_buf = calloc(count, item_size); + + assert(s->scratch_rw); + + if (control & NODE_CONTROL_DIRECTION) { + int i; + for (i=0; iscratch_rw(scratch_buf, scratch_addr, count*item_size, 1); + } else { + // read from scratch memory + s->scratch_rw(scratch_buf, scratch_addr, count*item_size, 0); + + int i; + for (i=0; inext_block = next_block; + + if (s->next_block & NODE_POINTER_EOL) { + s->eol = true; + } + } +} + uint32_t dsp_dma_read(DSPDMAState *s, DSPDMARegister reg) { switch (reg) { @@ -20,6 +149,7 @@ uint32_t dsp_dma_read(DSPDMAState *s, DSPDMARegister reg) } return 0; } + void dsp_dma_write(DSPDMAState *s, DSPDMARegister reg, uint32_t v) { switch (reg) { @@ -27,7 +157,26 @@ void dsp_dma_write(DSPDMAState *s, DSPDMARegister reg, uint32_t v) s->configuration = v; break; case DMA_CONTROL: - s->control = v; + switch(v & DMA_CONTROL_ACTION) { + case DMA_CONTROL_ACTION_START: + s->control |= DMA_CONTROL_RUNNING; + s->control &= ~DMA_CONTROL_STOPPED; + break; + case DMA_CONTROL_ACTION_STOP: + s->control |= DMA_CONTROL_STOPPED; + s->control &= ~DMA_CONTROL_RUNNING; + break; + case DMA_CONTROL_ACTION_FREEZE: + s->control |= DMA_CONTROL_FROZEN; + break; + case DMA_CONTROL_ACTION_UNFREEZE: + s->control &= ~DMA_CONTROL_FROZEN; + break; + default: + assert(false); + break; + } + dsp_dma_run(s); break; case DMA_START_BLOCK: s->start_block = v; diff --git a/hw/xbox/dsp/dsp_dma.h b/hw/xbox/dsp/dsp_dma.h index 3cfccc54be..9b39c61370 100644 --- a/hw/xbox/dsp/dsp_dma.h +++ b/hw/xbox/dsp/dsp_dma.h @@ -2,6 +2,10 @@ #define DSP_DMA_H #include +#include + +#include "dsp.h" +#include "dsp_cpu.h" typedef enum DSPDMARegister { DMA_CONFIGURATION, @@ -11,10 +15,18 @@ typedef enum DSPDMARegister { } DSPDMARegister; typedef struct DSPDMAState { + dsp_core_t* core; + + void* scratch_rw_opaque; + dsp_scratch_rw_func scratch_rw; + uint32_t configuration; uint32_t control; uint32_t start_block; uint32_t next_block; + + bool error; + bool eol; } DSPDMAState; uint32_t dsp_dma_read(DSPDMAState *s, DSPDMARegister reg); diff --git a/hw/xbox/dsp/dsp_emu.inl b/hw/xbox/dsp/dsp_emu.inl index 69fb76a30c..0ae000db77 100644 --- a/hw/xbox/dsp/dsp_emu.inl +++ b/hw/xbox/dsp/dsp_emu.inl @@ -7349,10 +7349,16 @@ static void emu_move_x_long(dsp_core_t* dsp) { uint32_t numreg = dsp->cur_inst & BITMASK(6); uint32_t x_addr = (dsp->registers[offreg] + xxxx) & BITMASK(24); - if (W) { - dsp56k_write_memory(dsp, DSP_SPACE_X, x_addr, dsp->registers[numreg]); + if (!W) { + uint32_t value; + if (numreg == DSP_REG_A || numreg == DSP_REG_B) { + emu_pm_read_accu24(dsp, numreg, &value); + } else { + value = dsp->registers[numreg]; + } + dsp56k_write_memory(dsp, DSP_SPACE_X, x_addr, value); } else { - dsp->registers[numreg] = dsp56k_read_memory(dsp, DSP_SPACE_X, x_addr); + dsp_write_reg(dsp, numreg, dsp56k_read_memory(dsp, DSP_SPACE_X, x_addr)); } // TODO: cycles @@ -7367,10 +7373,16 @@ static void emu_move_x_imm(dsp_core_t* dsp) { uint32_t numreg = dsp->disasm_cur_inst & BITMASK(4); uint32_t x_addr = (dsp->registers[offreg] + dsp_signextend(7, xxx)) & BITMASK(24); - if (W) { - dsp56k_write_memory(dsp, DSP_SPACE_X, x_addr, dsp->registers[numreg]); + if (!W) { + uint32_t value; + if (numreg == DSP_REG_A || numreg == DSP_REG_B) { + emu_pm_read_accu24(dsp, numreg, &value); + } else { + value = dsp->registers[numreg]; + } + dsp56k_write_memory(dsp, DSP_SPACE_X, x_addr, value); } else { - dsp->registers[numreg] = dsp56k_read_memory(dsp, DSP_SPACE_X, x_addr); + dsp_write_reg(dsp, numreg, dsp56k_read_memory(dsp, DSP_SPACE_X, x_addr)); } // TODO: cycles