diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 576d3adf89..fb2a6cc123 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -245,6 +245,11 @@ jobs: echo -e "\n\nCompiler Cache Stats:" ccache -s + - name: Test + run: | + pushd src/build + ./pyvenv/bin/meson test --suite xbox + popd - name: Generate AppImage run: | wget --no-verbose https://github.com/linuxdeploy/linuxdeploy/releases/latest/download/linuxdeploy-${{ matrix.arch }}.AppImage diff --git a/debian/control b/debian/control index eb495ec081..7f5871bbd7 100644 --- a/debian/control +++ b/debian/control @@ -6,6 +6,8 @@ Build-Depends: debhelper (>= 11), cmake, git, python3:any, + python3-pip, + python3-tomli, python3-yaml, python3-venv, ninja-build, diff --git a/hw/xbox/mcpx/apu.c b/hw/xbox/mcpx/apu.c index d5eec18f40..338e132956 100644 --- a/hw/xbox/mcpx/apu.c +++ b/hw/xbox/mcpx/apu.c @@ -2579,7 +2579,7 @@ const VMStateDescription vmstate_vp_dsp_core_state = { VMSTATE_UINT32(num_inst, dsp_core_t), VMSTATE_UINT32(cur_inst_len, dsp_core_t), VMSTATE_UINT32(cur_inst, dsp_core_t), - VMSTATE_BOOL(executing_for_disasm, dsp_core_t), + VMSTATE_UNUSED(1), VMSTATE_UINT32(disasm_memory_ptr, dsp_core_t), VMSTATE_BOOL(exception_debugging, dsp_core_t), VMSTATE_UINT32(disasm_prev_inst_pc, dsp_core_t), diff --git a/hw/xbox/mcpx/dsp/debug.c b/hw/xbox/mcpx/dsp/debug.c new file mode 100644 index 0000000000..e41a222bf9 --- /dev/null +++ b/hw/xbox/mcpx/dsp/debug.c @@ -0,0 +1,299 @@ +/* + * MCPX DSP emulator + * + * Copyright (c) 2015 espes + * Copyright (c) 2020-2025 Matt Borgerson + * + * Adapted from Hatari DSP M56001 emulation + * (C) 2001-2008 ARAnyM developer team + * Adaption to Hatari (C) 2008 by Thomas Huth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "qemu/osdep.h" + +#include +#include +#include +#include + +#include "dsp_cpu.h" +#include "dsp_dma.h" +#include "dsp_state.h" + +#include "dsp.h" +#include "debug.h" + +#if DEBUG_DSP +#define BITMASK(x) ((1<<(x))-1) + +/** + * Output memory values between given addresses in given DSP address space. + * Return next DSP address value. + */ +uint32_t dsp_disasm_memory(DSPState* dsp, uint32_t dsp_memdump_addr, uint32_t dsp_memdump_upper, char space) +{ + uint32_t mem, value; + + for (mem = dsp_memdump_addr; mem <= dsp_memdump_upper; mem++) { + value = dsp_read_memory(dsp, space, mem); + printf("%04x %06x\n", mem, value); + } + return dsp_memdump_upper+1; +} + +/** + * Show information on DSP core state which isn't + * shown by any of the other commands (dd, dm, dr). + */ +void dsp_info(DSPState* dsp) +{ + int i, j; + const char *stackname[] = { "SSH", "SSL" }; + + printf("DSP core information:\n"); + + for (i = 0; i < ARRAY_SIZE(stackname); i++) { + printf("- %s stack:", stackname[i]); + for (j = 0; j < ARRAY_SIZE(dsp->core.stack[0]); j++) { + printf(" %04x", dsp->core.stack[i][j]); + } + printf("\n"); + } + + printf("- Interrupt IPL:"); + for (i = 0; i < ARRAY_SIZE(dsp->core.interrupt_ipl); i++) { + printf(" %04x", dsp->core.interrupt_ipl[i]); + } + printf("\n"); + + printf("- Pending ints: "); + for (i = 0; i < ARRAY_SIZE(dsp->core.interrupt_is_pending); i++) { + printf(" %04hx", dsp->core.interrupt_is_pending[i]); + } + printf("\n"); +} + +/** + * Show DSP register contents + */ +void dsp_print_registers(DSPState* dsp) +{ + uint32_t i; + + printf("A: A2: %02x A1: %06x A0: %06x\n", + dsp->core.registers[DSP_REG_A2], dsp->core.registers[DSP_REG_A1], dsp->core.registers[DSP_REG_A0]); + printf("B: B2: %02x B1: %06x B0: %06x\n", + dsp->core.registers[DSP_REG_B2], dsp->core.registers[DSP_REG_B1], dsp->core.registers[DSP_REG_B0]); + + printf("X: X1: %06x X0: %06x\n", dsp->core.registers[DSP_REG_X1], dsp->core.registers[DSP_REG_X0]); + printf("Y: Y1: %06x Y0: %06x\n", dsp->core.registers[DSP_REG_Y1], dsp->core.registers[DSP_REG_Y0]); + + for (i=0; i<8; i++) { + printf("R%01x: %04x N%01x: %04x M%01x: %04x\n", + i, dsp->core.registers[DSP_REG_R0+i], + i, dsp->core.registers[DSP_REG_N0+i], + i, dsp->core.registers[DSP_REG_M0+i]); + } + + printf("LA: %04x LC: %04x PC: %04x\n", dsp->core.registers[DSP_REG_LA], dsp->core.registers[DSP_REG_LC], dsp->core.pc); + printf("SR: %04x OMR: %02x\n", dsp->core.registers[DSP_REG_SR], dsp->core.registers[DSP_REG_OMR]); + printf("SP: %02x SSH: %04x SSL: %04x\n", + dsp->core.registers[DSP_REG_SP], dsp->core.registers[DSP_REG_SSH], dsp->core.registers[DSP_REG_SSL]); +} + + +/** + * Get given DSP register address and required bit mask. + * Works for A0-2, B0-2, LA, LC, M0-7, N0-7, R0-7, X0-1, Y0-1, PC, SR, SP, + * OMR, SSH & SSL registers, but note that the SP, SSH & SSL registers + * need special handling (in DSP*SetRegister()) when they are set. + * Return the register width in bits or zero for an error. + */ +int dsp_get_register_address(DSPState* dsp, const char *regname, uint32_t **addr, uint32_t *mask) +{ +#define MAX_REGNAME_LEN 4 + typedef struct { + const char name[MAX_REGNAME_LEN]; + uint32_t *addr; + size_t bits; + uint32_t mask; + } reg_addr_t; + + /* sorted by name so that this can be bisected */ + const reg_addr_t registers[] = { + + /* 56-bit A register */ + { "A0", &dsp->core.registers[DSP_REG_A0], 32, BITMASK(24) }, + { "A1", &dsp->core.registers[DSP_REG_A1], 32, BITMASK(24) }, + { "A2", &dsp->core.registers[DSP_REG_A2], 32, BITMASK(8) }, + + /* 56-bit B register */ + { "B0", &dsp->core.registers[DSP_REG_B0], 32, BITMASK(24) }, + { "B1", &dsp->core.registers[DSP_REG_B1], 32, BITMASK(24) }, + { "B2", &dsp->core.registers[DSP_REG_B2], 32, BITMASK(8) }, + + /* 16-bit LA & LC registers */ + { "LA", &dsp->core.registers[DSP_REG_LA], 32, BITMASK(16) }, + { "LC", &dsp->core.registers[DSP_REG_LC], 32, BITMASK(16) }, + + /* 16-bit M registers */ + { "M0", &dsp->core.registers[DSP_REG_M0], 32, BITMASK(16) }, + { "M1", &dsp->core.registers[DSP_REG_M1], 32, BITMASK(16) }, + { "M2", &dsp->core.registers[DSP_REG_M2], 32, BITMASK(16) }, + { "M3", &dsp->core.registers[DSP_REG_M3], 32, BITMASK(16) }, + { "M4", &dsp->core.registers[DSP_REG_M4], 32, BITMASK(16) }, + { "M5", &dsp->core.registers[DSP_REG_M5], 32, BITMASK(16) }, + { "M6", &dsp->core.registers[DSP_REG_M6], 32, BITMASK(16) }, + { "M7", &dsp->core.registers[DSP_REG_M7], 32, BITMASK(16) }, + + /* 16-bit N registers */ + { "N0", &dsp->core.registers[DSP_REG_N0], 32, BITMASK(16) }, + { "N1", &dsp->core.registers[DSP_REG_N1], 32, BITMASK(16) }, + { "N2", &dsp->core.registers[DSP_REG_N2], 32, BITMASK(16) }, + { "N3", &dsp->core.registers[DSP_REG_N3], 32, BITMASK(16) }, + { "N4", &dsp->core.registers[DSP_REG_N4], 32, BITMASK(16) }, + { "N5", &dsp->core.registers[DSP_REG_N5], 32, BITMASK(16) }, + { "N6", &dsp->core.registers[DSP_REG_N6], 32, BITMASK(16) }, + { "N7", &dsp->core.registers[DSP_REG_N7], 32, BITMASK(16) }, + + { "OMR", &dsp->core.registers[DSP_REG_OMR], 32, 0x5f }, + + /* 16-bit program counter */ + { "PC", (uint32_t*)(&dsp->core.pc), 24, BITMASK(24) }, + + /* 16-bit DSP R (address) registers */ + { "R0", &dsp->core.registers[DSP_REG_R0], 32, BITMASK(16) }, + { "R1", &dsp->core.registers[DSP_REG_R1], 32, BITMASK(16) }, + { "R2", &dsp->core.registers[DSP_REG_R2], 32, BITMASK(16) }, + { "R3", &dsp->core.registers[DSP_REG_R3], 32, BITMASK(16) }, + { "R4", &dsp->core.registers[DSP_REG_R4], 32, BITMASK(16) }, + { "R5", &dsp->core.registers[DSP_REG_R5], 32, BITMASK(16) }, + { "R6", &dsp->core.registers[DSP_REG_R6], 32, BITMASK(16) }, + { "R7", &dsp->core.registers[DSP_REG_R7], 32, BITMASK(16) }, + + { "SSH", &dsp->core.registers[DSP_REG_SSH], 32, BITMASK(16) }, + { "SSL", &dsp->core.registers[DSP_REG_SSL], 32, BITMASK(16) }, + { "SP", &dsp->core.registers[DSP_REG_SP], 32, BITMASK(6) }, + + /* 16-bit status register */ + { "SR", &dsp->core.registers[DSP_REG_SR], 32, 0xefff }, + + /* 48-bit X register */ + { "X0", &dsp->core.registers[DSP_REG_X0], 32, BITMASK(24) }, + { "X1", &dsp->core.registers[DSP_REG_X1], 32, BITMASK(24) }, + + /* 48-bit Y register */ + { "Y0", &dsp->core.registers[DSP_REG_Y0], 32, BITMASK(24) }, + { "Y1", &dsp->core.registers[DSP_REG_Y1], 32, BITMASK(24) } + }; + /* left, right, middle, direction */ + int l, r, m, dir = 0; + unsigned int i, len; + char reg[MAX_REGNAME_LEN]; + + for (i = 0; i < sizeof(reg) && regname[i]; i++) { + reg[i] = toupper(regname[i]); + } + if (i < 2 || regname[i]) { + /* too short or longer than any of the names */ + return 0; + } + len = i; + + /* bisect */ + l = 0; + r = ARRAY_SIZE(registers) - 1; + do { + m = (l+r) >> 1; + for (i = 0; i < len; i++) { + dir = (int)reg[i] - registers[m].name[i]; + if (dir) { + break; + } + } + if (dir == 0) { + *addr = registers[m].addr; + *mask = registers[m].mask; + return registers[m].bits; + } + if (dir < 0) { + r = m-1; + } else { + l = m+1; + } + } while (l <= r); +#undef MAX_REGNAME_LEN + return 0; +} + + +/** + * Set given DSP register value, return false if unknown register given + */ +bool dsp_disasm_set_register(DSPState* dsp, const char *arg, uint32_t value) +{ + uint32_t *addr, mask, sp_value; + int bits; + + /* first check registers needing special handling... */ + if (arg[0]=='S' || arg[0]=='s') { + if (arg[1]=='P' || arg[1]=='p') { + dsp->core.registers[DSP_REG_SP] = value & BITMASK(6); + value &= BITMASK(4); + dsp->core.registers[DSP_REG_SSH] = dsp->core.stack[0][value]; + dsp->core.registers[DSP_REG_SSL] = dsp->core.stack[1][value]; + return true; + } + if (arg[1]=='S' || arg[1]=='s') { + sp_value = dsp->core.registers[DSP_REG_SP] & BITMASK(4); + if (arg[2]=='H' || arg[2]=='h') { + if (sp_value == 0) { + dsp->core.registers[DSP_REG_SSH] = 0; + dsp->core.stack[0][sp_value] = 0; + } else { + dsp->core.registers[DSP_REG_SSH] = value & BITMASK(16); + dsp->core.stack[0][sp_value] = value & BITMASK(16); + } + return true; + } + if (arg[2]=='L' || arg[2]=='l') { + if (sp_value == 0) { + dsp->core.registers[DSP_REG_SSL] = 0; + dsp->core.stack[1][sp_value] = 0; + } else { + dsp->core.registers[DSP_REG_SSL] = value & BITMASK(16); + dsp->core.stack[1][sp_value] = value & BITMASK(16); + } + return true; + } + } + } + + /* ...then registers where address & mask are enough */ + bits = dsp_get_register_address(dsp, arg, &addr, &mask); + switch (bits) { + case 32: + *addr = value & mask; + return true; + case 16: + *(uint16_t*)addr = value & mask; + return true; + } + return false; +} + +#endif diff --git a/hw/xbox/mcpx/dsp/debug.h b/hw/xbox/mcpx/dsp/debug.h new file mode 100644 index 0000000000..befef229b1 --- /dev/null +++ b/hw/xbox/mcpx/dsp/debug.h @@ -0,0 +1,37 @@ +/* + * MCPX DSP emulator + + * Copyright (c) 2025 Matt Borgerson + + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef HW_XBOX_MCPX_DSP_DEBUG_H +#define HW_XBOX_MCPX_DSP_DEBUG_H + +#ifndef DEBUG_DSP +#define DEBUG_DSP 0 +#endif + +#define TRACE_DSP_DISASM 0 +#define TRACE_DSP_DISASM_REG 0 +#define TRACE_DSP_DISASM_MEM 0 + +#define DPRINTF(fmt, ...) \ + do { \ + if (DEBUG_DSP) fprintf(stderr, fmt, ## __VA_ARGS__); \ + } while (0) + +#endif diff --git a/hw/xbox/mcpx/dsp/dsp.c b/hw/xbox/mcpx/dsp/dsp.c index d1e3b2a9b3..32b9c12d40 100644 --- a/hw/xbox/mcpx/dsp/dsp.c +++ b/hw/xbox/mcpx/dsp/dsp.c @@ -2,6 +2,7 @@ * MCPX DSP emulator * * Copyright (c) 2015 espes + * Copyright (c) 2020-2025 Matt Borgerson * * Adapted from Hatari DSP M56001 emulation * (C) 2001-2008 ARAnyM developer team @@ -23,17 +24,12 @@ */ #include "qemu/osdep.h" - -#include -#include -#include -#include - #include "dsp_cpu.h" #include "dsp_dma.h" #include "dsp_state.h" - #include "dsp.h" +#include "debug.h" +#include "trace.h" /* Defines */ #define BITMASK(x) ((1<<(x))-1) @@ -42,16 +38,6 @@ #define INTERRUPT_START_FRAME (1 << 1) #define INTERRUPT_DMA_EOL (1 << 7) -// #define DEBUG_DSP - -#ifdef DEBUG_DSP -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) -#endif - 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); @@ -91,8 +77,6 @@ void dsp_destroy(DSPState* dsp) static uint32_t read_peripheral(dsp_core_t* core, uint32_t address) { DSPState* dsp = container_of(core, DSPState, core); - DPRINTF("read_peripheral 0x%06x", address); - uint32_t v = 0xababa; switch(address) { case 0xFFFFB3: @@ -118,15 +102,13 @@ static uint32_t read_peripheral(dsp_core_t* core, uint32_t address) { break; } - DPRINTF(" -> 0x%06x\n", v); + trace_dsp_read_peripheral(address, v); return v; } static void write_peripheral(dsp_core_t* core, uint32_t address, uint32_t value) { DSPState* dsp = container_of(core, DSPState, core); - DPRINTF("write_peripheral [0x%06x] = 0x%06x\n", address, value); - switch(address) { case 0xFFFFC4: if (value & 1) { @@ -152,6 +134,8 @@ static void write_peripheral(dsp_core_t* core, uint32_t address, uint32_t value) dsp_dma_write(&dsp->dma, DMA_CONFIGURATION, value); break; } + + trace_dsp_write_peripheral(address, value); } @@ -201,7 +185,7 @@ void dsp_bootstrap(DSPState* dsp) (uint8_t*)dsp->core.pram, 0, 0x800*4, false); for (int i = 0; i < 0x800; i++) { if (dsp->core.pram[i] & 0xff000000) { - DPRINTF(stderr, "Bootstrap %04x: %08x\n", i, dsp->core.pram[i]); + DPRINTF("Bootstrap %04x: %08x\n", i, dsp->core.pram[i]); dsp->core.pram[i] &= 0x00ffffff; } } @@ -213,19 +197,6 @@ void dsp_start_frame(DSPState* dsp) dsp->interrupts |= INTERRUPT_START_FRAME; } -/** - * Disassemble DSP code between given addresses, return next PC address - */ -uint32_t dsp_disasm_address(DSPState* dsp, FILE *out, uint32_t lowerAdr, uint32_t UpperAdr) -{ - uint32_t dsp_pc; - - for (dsp_pc=lowerAdr; dsp_pc<=UpperAdr; dsp_pc++) { - dsp_pc += dsp56k_execute_one_disasm_instruction(&dsp->core, out, dsp_pc); - } - return dsp_pc; -} - uint32_t dsp_read_memory(DSPState* dsp, char space, uint32_t address) { int space_id; @@ -269,259 +240,3 @@ void dsp_write_memory(DSPState* dsp, char space, uint32_t address, uint32_t valu dsp56k_write_memory(&dsp->core, space_id, address, value); } - -/** - * Output memory values between given addresses in given DSP address space. - * Return next DSP address value. - */ -uint32_t dsp_disasm_memory(DSPState* dsp, uint32_t dsp_memdump_addr, uint32_t dsp_memdump_upper, char space) -{ - uint32_t mem, value; - - for (mem = dsp_memdump_addr; mem <= dsp_memdump_upper; mem++) { - value = dsp_read_memory(dsp, space, mem); - printf("%04x %06x\n", mem, value); - } - return dsp_memdump_upper+1; -} - -/** - * Show information on DSP core state which isn't - * shown by any of the other commands (dd, dm, dr). - */ -void dsp_info(DSPState* dsp) -{ - int i, j; - const char *stackname[] = { "SSH", "SSL" }; - - printf("DSP core information:\n"); - - for (i = 0; i < ARRAY_SIZE(stackname); i++) { - printf("- %s stack:", stackname[i]); - for (j = 0; j < ARRAY_SIZE(dsp->core.stack[0]); j++) { - printf(" %04x", dsp->core.stack[i][j]); - } - printf("\n"); - } - - printf("- Interrupt IPL:"); - for (i = 0; i < ARRAY_SIZE(dsp->core.interrupt_ipl); i++) { - printf(" %04x", dsp->core.interrupt_ipl[i]); - } - printf("\n"); - - printf("- Pending ints: "); - for (i = 0; i < ARRAY_SIZE(dsp->core.interrupt_is_pending); i++) { - printf(" %04hx", dsp->core.interrupt_is_pending[i]); - } - printf("\n"); -} - -/** - * Show DSP register contents - */ -void dsp_print_registers(DSPState* dsp) -{ - uint32_t i; - - printf("A: A2: %02x A1: %06x A0: %06x\n", - dsp->core.registers[DSP_REG_A2], dsp->core.registers[DSP_REG_A1], dsp->core.registers[DSP_REG_A0]); - printf("B: B2: %02x B1: %06x B0: %06x\n", - dsp->core.registers[DSP_REG_B2], dsp->core.registers[DSP_REG_B1], dsp->core.registers[DSP_REG_B0]); - - printf("X: X1: %06x X0: %06x\n", dsp->core.registers[DSP_REG_X1], dsp->core.registers[DSP_REG_X0]); - printf("Y: Y1: %06x Y0: %06x\n", dsp->core.registers[DSP_REG_Y1], dsp->core.registers[DSP_REG_Y0]); - - for (i=0; i<8; i++) { - printf("R%01x: %04x N%01x: %04x M%01x: %04x\n", - i, dsp->core.registers[DSP_REG_R0+i], - i, dsp->core.registers[DSP_REG_N0+i], - i, dsp->core.registers[DSP_REG_M0+i]); - } - - printf("LA: %04x LC: %04x PC: %04x\n", dsp->core.registers[DSP_REG_LA], dsp->core.registers[DSP_REG_LC], dsp->core.pc); - printf("SR: %04x OMR: %02x\n", dsp->core.registers[DSP_REG_SR], dsp->core.registers[DSP_REG_OMR]); - printf("SP: %02x SSH: %04x SSL: %04x\n", - dsp->core.registers[DSP_REG_SP], dsp->core.registers[DSP_REG_SSH], dsp->core.registers[DSP_REG_SSL]); -} - - -/** - * Get given DSP register address and required bit mask. - * Works for A0-2, B0-2, LA, LC, M0-7, N0-7, R0-7, X0-1, Y0-1, PC, SR, SP, - * OMR, SSH & SSL registers, but note that the SP, SSH & SSL registers - * need special handling (in DSP*SetRegister()) when they are set. - * Return the register width in bits or zero for an error. - */ -int dsp_get_register_address(DSPState* dsp, const char *regname, uint32_t **addr, uint32_t *mask) -{ -#define MAX_REGNAME_LEN 4 - typedef struct { - const char name[MAX_REGNAME_LEN]; - uint32_t *addr; - size_t bits; - uint32_t mask; - } reg_addr_t; - - /* sorted by name so that this can be bisected */ - const reg_addr_t registers[] = { - - /* 56-bit A register */ - { "A0", &dsp->core.registers[DSP_REG_A0], 32, BITMASK(24) }, - { "A1", &dsp->core.registers[DSP_REG_A1], 32, BITMASK(24) }, - { "A2", &dsp->core.registers[DSP_REG_A2], 32, BITMASK(8) }, - - /* 56-bit B register */ - { "B0", &dsp->core.registers[DSP_REG_B0], 32, BITMASK(24) }, - { "B1", &dsp->core.registers[DSP_REG_B1], 32, BITMASK(24) }, - { "B2", &dsp->core.registers[DSP_REG_B2], 32, BITMASK(8) }, - - /* 16-bit LA & LC registers */ - { "LA", &dsp->core.registers[DSP_REG_LA], 32, BITMASK(16) }, - { "LC", &dsp->core.registers[DSP_REG_LC], 32, BITMASK(16) }, - - /* 16-bit M registers */ - { "M0", &dsp->core.registers[DSP_REG_M0], 32, BITMASK(16) }, - { "M1", &dsp->core.registers[DSP_REG_M1], 32, BITMASK(16) }, - { "M2", &dsp->core.registers[DSP_REG_M2], 32, BITMASK(16) }, - { "M3", &dsp->core.registers[DSP_REG_M3], 32, BITMASK(16) }, - { "M4", &dsp->core.registers[DSP_REG_M4], 32, BITMASK(16) }, - { "M5", &dsp->core.registers[DSP_REG_M5], 32, BITMASK(16) }, - { "M6", &dsp->core.registers[DSP_REG_M6], 32, BITMASK(16) }, - { "M7", &dsp->core.registers[DSP_REG_M7], 32, BITMASK(16) }, - - /* 16-bit N registers */ - { "N0", &dsp->core.registers[DSP_REG_N0], 32, BITMASK(16) }, - { "N1", &dsp->core.registers[DSP_REG_N1], 32, BITMASK(16) }, - { "N2", &dsp->core.registers[DSP_REG_N2], 32, BITMASK(16) }, - { "N3", &dsp->core.registers[DSP_REG_N3], 32, BITMASK(16) }, - { "N4", &dsp->core.registers[DSP_REG_N4], 32, BITMASK(16) }, - { "N5", &dsp->core.registers[DSP_REG_N5], 32, BITMASK(16) }, - { "N6", &dsp->core.registers[DSP_REG_N6], 32, BITMASK(16) }, - { "N7", &dsp->core.registers[DSP_REG_N7], 32, BITMASK(16) }, - - { "OMR", &dsp->core.registers[DSP_REG_OMR], 32, 0x5f }, - - /* 16-bit program counter */ - { "PC", (uint32_t*)(&dsp->core.pc), 24, BITMASK(24) }, - - /* 16-bit DSP R (address) registers */ - { "R0", &dsp->core.registers[DSP_REG_R0], 32, BITMASK(16) }, - { "R1", &dsp->core.registers[DSP_REG_R1], 32, BITMASK(16) }, - { "R2", &dsp->core.registers[DSP_REG_R2], 32, BITMASK(16) }, - { "R3", &dsp->core.registers[DSP_REG_R3], 32, BITMASK(16) }, - { "R4", &dsp->core.registers[DSP_REG_R4], 32, BITMASK(16) }, - { "R5", &dsp->core.registers[DSP_REG_R5], 32, BITMASK(16) }, - { "R6", &dsp->core.registers[DSP_REG_R6], 32, BITMASK(16) }, - { "R7", &dsp->core.registers[DSP_REG_R7], 32, BITMASK(16) }, - - { "SSH", &dsp->core.registers[DSP_REG_SSH], 32, BITMASK(16) }, - { "SSL", &dsp->core.registers[DSP_REG_SSL], 32, BITMASK(16) }, - { "SP", &dsp->core.registers[DSP_REG_SP], 32, BITMASK(6) }, - - /* 16-bit status register */ - { "SR", &dsp->core.registers[DSP_REG_SR], 32, 0xefff }, - - /* 48-bit X register */ - { "X0", &dsp->core.registers[DSP_REG_X0], 32, BITMASK(24) }, - { "X1", &dsp->core.registers[DSP_REG_X1], 32, BITMASK(24) }, - - /* 48-bit Y register */ - { "Y0", &dsp->core.registers[DSP_REG_Y0], 32, BITMASK(24) }, - { "Y1", &dsp->core.registers[DSP_REG_Y1], 32, BITMASK(24) } - }; - /* left, right, middle, direction */ - int l, r, m, dir = 0; - unsigned int i, len; - char reg[MAX_REGNAME_LEN]; - - for (i = 0; i < sizeof(reg) && regname[i]; i++) { - reg[i] = toupper(regname[i]); - } - if (i < 2 || regname[i]) { - /* too short or longer than any of the names */ - return 0; - } - len = i; - - /* bisect */ - l = 0; - r = ARRAY_SIZE(registers) - 1; - do { - m = (l+r) >> 1; - for (i = 0; i < len; i++) { - dir = (int)reg[i] - registers[m].name[i]; - if (dir) { - break; - } - } - if (dir == 0) { - *addr = registers[m].addr; - *mask = registers[m].mask; - return registers[m].bits; - } - if (dir < 0) { - r = m-1; - } else { - l = m+1; - } - } while (l <= r); -#undef MAX_REGNAME_LEN - return 0; -} - - -/** - * Set given DSP register value, return false if unknown register given - */ -bool dsp_disasm_set_register(DSPState* dsp, const char *arg, uint32_t value) -{ - uint32_t *addr, mask, sp_value; - int bits; - - /* first check registers needing special handling... */ - if (arg[0]=='S' || arg[0]=='s') { - if (arg[1]=='P' || arg[1]=='p') { - dsp->core.registers[DSP_REG_SP] = value & BITMASK(6); - value &= BITMASK(4); - dsp->core.registers[DSP_REG_SSH] = dsp->core.stack[0][value]; - dsp->core.registers[DSP_REG_SSL] = dsp->core.stack[1][value]; - return true; - } - if (arg[1]=='S' || arg[1]=='s') { - sp_value = dsp->core.registers[DSP_REG_SP] & BITMASK(4); - if (arg[2]=='H' || arg[2]=='h') { - if (sp_value == 0) { - dsp->core.registers[DSP_REG_SSH] = 0; - dsp->core.stack[0][sp_value] = 0; - } else { - dsp->core.registers[DSP_REG_SSH] = value & BITMASK(16); - dsp->core.stack[0][sp_value] = value & BITMASK(16); - } - return true; - } - if (arg[2]=='L' || arg[2]=='l') { - if (sp_value == 0) { - dsp->core.registers[DSP_REG_SSL] = 0; - dsp->core.stack[1][sp_value] = 0; - } else { - dsp->core.registers[DSP_REG_SSL] = value & BITMASK(16); - dsp->core.stack[1][sp_value] = value & BITMASK(16); - } - return true; - } - } - } - - /* ...then registers where address & mask are enough */ - bits = dsp_get_register_address(dsp, arg, &addr, &mask); - switch (bits) { - case 32: - *addr = value & mask; - return true; - case 16: - *(uint16_t*)addr = value & mask; - return true; - } - return false; -} diff --git a/hw/xbox/mcpx/dsp/dsp.h b/hw/xbox/mcpx/dsp/dsp.h index 0b18fa0c45..d85e03d55e 100644 --- a/hw/xbox/mcpx/dsp/dsp.h +++ b/hw/xbox/mcpx/dsp/dsp.h @@ -36,7 +36,6 @@ typedef void (*dsp_scratch_rw_func)( typedef void (*dsp_fifo_rw_func)( void *opaque, uint8_t *ptr, unsigned int index, size_t len, bool dir); -/* Dsp commands */ DSPState *dsp_init(void *rw_opaque, dsp_scratch_rw_func scratch_rw, dsp_fifo_rw_func fifo_rw); @@ -49,16 +48,14 @@ void dsp_run(DSPState* dsp, int cycles); void dsp_bootstrap(DSPState* dsp); void dsp_start_frame(DSPState* dsp); - -/* Dsp Debugger commands */ uint32_t dsp_read_memory(DSPState* dsp, char space, uint32_t addr); void dsp_write_memory(DSPState* dsp, char space, uint32_t address, uint32_t value); -uint32_t dsp_disasm_memory(DSPState* dsp, uint32_t dsp_memdump_addr, uint32_t dsp_memdump_upper, char space); -uint32_t dsp_disasm_address(DSPState* dsp, FILE *out, uint32_t lowerAdr, uint32_t UpperAdr); + void dsp_info(DSPState* dsp); void dsp_print_registers(DSPState* dsp); int dsp_get_register_address(DSPState* dsp, const char *arg, uint32_t **addr, uint32_t *mask); +uint32_t dsp_disasm_memory(DSPState* dsp, uint32_t dsp_memdump_addr, uint32_t dsp_memdump_upper, char space); +uint32_t dsp_disasm_address(DSPState* dsp, FILE *out, uint32_t lowerAdr, uint32_t UpperAdr); bool dsp_disasm_set_register(DSPState* dsp, const char *arg, uint32_t value); - #endif /* DSP_H */ diff --git a/hw/xbox/mcpx/dsp/dsp_cpu.c b/hw/xbox/mcpx/dsp/dsp_cpu.c index 874dd494ce..380c07e1bb 100644 --- a/hw/xbox/mcpx/dsp/dsp_cpu.c +++ b/hw/xbox/mcpx/dsp/dsp_cpu.c @@ -2,6 +2,7 @@ * DSP56300 emulator * * Copyright (c) 2015 espes + * Copyright (c) 2020-2025 Matt Borgerson * * Adapted from Hatari DSP M56001 emulation * (C) 2003-2008 ARAnyM developer team @@ -23,20 +24,10 @@ */ #include "qemu/osdep.h" - -#include -#include -#include - #include "qemu/bswap.h" - #include "dsp_cpu.h" - -#define TRACE_DSP_DISASM 0 -#define TRACE_DSP_DISASM_REG 0 -#define TRACE_DSP_DISASM_MEM 0 - -#define DPRINTF(s, ...) printf(s, ## __VA_ARGS__) +#include "debug.h" +#include "trace.h" #define BITMASK(x) ((1<<(x))-1) @@ -80,18 +71,18 @@ static void dsp_rnd56(dsp_core_t* dsp, uint32_t *dest); static uint32_t dsp_signextend(int bits, uint32_t v); static const dsp_interrupt_t dsp_interrupt[12] = { - {DSP_INTER_RESET , 0x00, 0, "Reset"}, - {DSP_INTER_ILLEGAL , 0x3e, 0, "Illegal"}, - {DSP_INTER_STACK_ERROR , 0x02, 0, "Stack Error"}, - {DSP_INTER_TRACE , 0x04, 0, "Trace"}, - {DSP_INTER_SWI , 0x06, 0, "Swi"}, - {DSP_INTER_HOST_COMMAND , 0xff, 1, "Host Command"}, - {DSP_INTER_HOST_RCV_DATA, 0x20, 1, "Host receive"}, - {DSP_INTER_HOST_TRX_DATA, 0x22, 1, "Host transmit"}, - {DSP_INTER_SSI_RCV_DATA_E, 0x0e, 2, "SSI receive with exception"}, - {DSP_INTER_SSI_RCV_DATA , 0x0c, 2, "SSI receive"}, - {DSP_INTER_SSI_TRX_DATA_E, 0x12, 2, "SSI transmit with exception"}, - {DSP_INTER_SSI_TRX_DATA , 0x10, 2, "SSI tramsmit"} + { DSP_INTER_RESET, 0x00, 0, "Reset" }, + { DSP_INTER_ILLEGAL, 0x3e, 0, "Illegal" }, + { DSP_INTER_STACK_ERROR, 0x02, 0, "Stack Error" }, + { DSP_INTER_TRACE, 0x04, 0, "Trace" }, + { DSP_INTER_SWI, 0x06, 0, "Swi" }, + { DSP_INTER_HOST_COMMAND, 0xff, 1, "Host Command" }, + { DSP_INTER_HOST_RCV_DATA, 0x20, 1, "Host receive" }, + { DSP_INTER_HOST_TRX_DATA, 0x22, 1, "Host transmit" }, + { DSP_INTER_SSI_RCV_DATA_E, 0x0e, 2, "SSI receive with exception" }, + { DSP_INTER_SSI_RCV_DATA, 0x0c, 2, "SSI receive" }, + { DSP_INTER_SSI_TRX_DATA_E, 0x12, 2, "SSI transmit with exception" }, + { DSP_INTER_SSI_TRX_DATA, 0x10, 2, "SSI transmit" } }; static const int registers_tcc[16][2] = { @@ -138,9 +129,8 @@ static const int registers_mask[64] = { 16, 16, 16, 16 }; -#include "dsp_emu.inl" - -#include "dsp_dis.inl" +#include "dsp_emu.c.inc" +#include "dsp_dis.c.inc" typedef bool (*match_func_t)(uint32_t op); @@ -415,7 +405,6 @@ void dsp56k_reset_cpu(dsp_core_t* dsp) /* runtime shit */ - dsp->executing_for_disasm = false; // start_time = SDL_GetTicks(); dsp->num_inst = 0; @@ -462,7 +451,7 @@ static uint16_t disasm_instruction(dsp_core_t* dsp, dsp_trace_disasm_t mode) if (mode == DSP_TRACE_MODE) { if (dsp->disasm_prev_inst_pc == dsp->pc) { if (!dsp->disasm_is_looping) { - printf( "Looping on DSP instruction at PC = $%04x\n", dsp->disasm_prev_inst_pc); + DPRINTF("Looping on DSP instruction at PC = $%04x\n", dsp->disasm_prev_inst_pc); dsp->disasm_is_looping = true; } return 0; @@ -520,7 +509,7 @@ static void disasm_reg_compare(dsp_core_t* dsp) case DSP_REG_X1: case DSP_REG_Y0: case DSP_REG_Y1: - printf("\tReg: %s $%06x -> $%06x\n", + DPRINTF("\tReg: %s $%06x -> $%06x\n", registers_name[i], dsp->disasm_registers_save[i], dsp->registers[i]); break; case DSP_REG_R0: @@ -550,21 +539,21 @@ static void disasm_reg_compare(dsp_core_t* dsp) case DSP_REG_SR: case DSP_REG_LA: case DSP_REG_LC: - printf("\tReg: %s $%04x -> $%04x\n", + DPRINTF("\tReg: %s $%04x -> $%04x\n", registers_name[i], dsp->disasm_registers_save[i], dsp->registers[i]); break; case DSP_REG_OMR: case DSP_REG_SP: case DSP_REG_SSH: case DSP_REG_SSL: - printf("\tReg: %s $%02x -> $%02x\n", + DPRINTF("\tReg: %s $%02x -> $%02x\n", registers_name[i], dsp->disasm_registers_save[i], dsp->registers[i]); break; case DSP_REG_A0: case DSP_REG_A1: case DSP_REG_A2: if (bRegA == false) { - printf("\tReg: a $%02x:%06x:%06x -> $%02x:%06x:%06x\n", + DPRINTF("\tReg: a $%02x:%06x:%06x -> $%02x:%06x:%06x\n", dsp->disasm_registers_save[DSP_REG_A2], dsp->disasm_registers_save[DSP_REG_A1], dsp->disasm_registers_save[DSP_REG_A0], dsp->registers[DSP_REG_A2], dsp->registers[DSP_REG_A1], dsp->registers[DSP_REG_A0] ); @@ -575,7 +564,7 @@ static void disasm_reg_compare(dsp_core_t* dsp) case DSP_REG_B1: case DSP_REG_B2: if (bRegB == false) { - printf("\tReg: b $%02x:%06x:%06x -> $%02x:%06x:%06x\n", + DPRINTF("\tReg: b $%02x:%06x:%06x -> $%02x:%06x:%06x\n", dsp->disasm_registers_save[DSP_REG_B2], dsp->disasm_registers_save[DSP_REG_B1], dsp->disasm_registers_save[DSP_REG_B0], dsp->registers[DSP_REG_B2], dsp->registers[DSP_REG_B1], dsp->registers[DSP_REG_B0] ); @@ -587,70 +576,28 @@ static void disasm_reg_compare(dsp_core_t* dsp) #ifdef DSP_DISASM_REG_PC if (pc_save != dsp->pc) { - printf("\tReg: pc $%04x -> $%04x\n", pc_save, dsp->pc); + DPRINTF("\tReg: pc $%04x -> $%04x\n", pc_save, dsp->pc); } #endif } static const char* disasm_get_instruction_text(dsp_core_t* dsp) { - // const int len = sizeof(dsp->disasm_str_instr); - // uint64_t count, cycles; - // uint16_t cycle_diff; - // float percentage; - // int offset; - if (dsp->disasm_is_looping) { dsp->disasm_str_instr2[0] = 0; } - // if (dsp->disasm_cur_inst_len == 1) { - // offset = sprintf(dsp->disasm_str_instr2, "p:%04x %06x (%02d cyc) %-*s\n", dsp->disasm_prev_inst_pc, dsp->disasm_cur_inst, dsp->instr_cycle, len, dsp->disasm_str_instr); - // } else { - // offset = sprintf(dsp->disasm_str_instr2, "p:%04x %06x %06x (%02d cyc) %-*s\n", dsp->disasm_prev_inst_pc, dsp->disasm_cur_inst, read_memory_p(dsp, dsp->disasm_prev_inst_pc + 1), dsp->instr_cycle, len, dsp->disasm_str_instr); - // } - // if (offset > 2 && Profile_DspAddressData(dsp->disasm_prev_inst_pc, &percentage, &count, &cycles, &cycle_diff)) { - // offset -= 2; - // sprintf(str_instr2+offset, "%5.2f%% (%"PRId64", %"PRId64", %d)\n", - // percentage, count, cycles, cycle_diff); - // } + if (dsp->disasm_cur_inst_len == 1) { + snprintf(dsp->disasm_str_instr2, sizeof(dsp->disasm_str_instr2), "p:%04x %06x (%02d cyc) %s", dsp->disasm_prev_inst_pc, dsp->disasm_cur_inst, dsp->instr_cycle, dsp->disasm_str_instr); + } else { + snprintf(dsp->disasm_str_instr2, sizeof(dsp->disasm_str_instr2), "p:%04x %06x %06x (%02d cyc) %s", dsp->disasm_prev_inst_pc, dsp->disasm_cur_inst, read_memory_p(dsp, dsp->disasm_prev_inst_pc + 1), dsp->instr_cycle, dsp->disasm_str_instr); + } return dsp->disasm_str_instr2; } -/** - * Execute one instruction in trace mode at a given PC address. - * */ -uint16_t dsp56k_execute_one_disasm_instruction(dsp_core_t* dsp, FILE *out, uint32_t pc) -{ - dsp_core_t dsp_core_save; - - /* Set DSP in disasm mode */ - dsp->executing_for_disasm = true; - - /* Save DSP context before executing instruction */ - memcpy(&dsp_core_save, dsp, sizeof(dsp_core_t)); - - /* execute and disasm instruction */ - dsp->pc = pc; - - /* Disasm instruction */ - uint16_t instruction_length = disasm_instruction(dsp, DSP_DISASM_MODE) - 1; - - /* Execute instruction at address given in parameter to get the number of cycles it takes */ - dsp56k_execute_instruction(dsp); - - fprintf(out, "%s", disasm_get_instruction_text(dsp)); - - /* Restore DSP context after executing instruction */ - memcpy(dsp, &dsp_core_save, sizeof(dsp_core_t)); - - /* Unset DSP in disasm mode */ - dsp->executing_for_disasm = false; - - return instruction_length; -} - void dsp56k_execute_instruction(dsp_core_t* dsp) { + trace_dsp56k_execute_instruction(dsp->is_gp, dsp->pc); + uint32_t disasm_return = 0; dsp->disasm_memory_ptr = 0; @@ -661,17 +608,18 @@ void dsp56k_execute_instruction(dsp_core_t* dsp) dsp->cur_inst_len = 1; dsp->instr_cycle = 2; - /* Disasm current instruction ? (trace mode only) */ - if (TRACE_DSP_DISASM) { - /* Call disasm_instruction only when DSP is called in trace mode */ - if (!dsp->executing_for_disasm) { - disasm_return = disasm_instruction(dsp, DSP_TRACE_MODE); + bool tracing = TRACE_DSP_DISASM || trace_event_get_state(TRACE_DSP56K_EXECUTE_INSTRUCTION_DISASM); - if (disasm_return) { - printf( "%s", disasm_get_instruction_text(dsp)); + /* Disasm current instruction ? (trace mode only) */ + if (tracing) { + disasm_return = disasm_instruction(dsp, DSP_TRACE_MODE); + if (disasm_return) { + const char *text = disasm_get_instruction_text(dsp); + trace_dsp56k_execute_instruction_disasm(text); + if (TRACE_DSP_DISASM) { + DPRINTF("%s\n", text); } - if (disasm_return != 0 && TRACE_DSP_DISASM_REG) { - /* DSP regs trace enabled only if DSP DISASM is enabled */ + if (TRACE_DSP_DISASM_REG) { disasm_reg_save(dsp); } } @@ -686,7 +634,7 @@ void dsp56k_execute_instruction(dsp_core_t* dsp) if (op->emu_func) { op->emu_func(dsp); } else { - printf("%x - %s\n", dsp->cur_inst, op->name); + DPRINTF("%x - %s\n", dsp->cur_inst, op->name); emu_undefined(dsp); } } else { @@ -695,26 +643,18 @@ void dsp56k_execute_instruction(dsp_core_t* dsp) } /* Disasm current instruction ? (trace mode only) */ - if (TRACE_DSP_DISASM) { - /* Display only when DSP is called in trace mode */ - if (!dsp->executing_for_disasm) { - if (disasm_return != 0) { - // printf( "%s", disasm_get_instruction_text(dsp)); - - /* DSP regs trace enabled only if DSP DISASM is enabled */ - if (TRACE_DSP_DISASM_REG) - disasm_reg_compare(dsp); - - if (TRACE_DSP_DISASM_MEM) { - /* 1 memory change to display ? */ - if (dsp->disasm_memory_ptr == 1) - printf( "\t%s\n", dsp->str_disasm_memory[0]); - /* 2 memory changes to display ? */ - else if (dsp->disasm_memory_ptr == 2) { - printf( "\t%s\n", dsp->str_disasm_memory[0]); - printf( "\t%s\n", dsp->str_disasm_memory[1]); - } - } + if (tracing && disasm_return) { + if (TRACE_DSP_DISASM_REG) { + disasm_reg_compare(dsp); + } + if (TRACE_DSP_DISASM_MEM) { + /* 1 memory change to display ? */ + if (dsp->disasm_memory_ptr == 1) + DPRINTF("\t%s\n", dsp->str_disasm_memory[0]); + /* 2 memory changes to display ? */ + else if (dsp->disasm_memory_ptr == 2) { + DPRINTF("\t%s\n", dsp->str_disasm_memory[0]); + DPRINTF("\t%s\n", dsp->str_disasm_memory[1]); } } } @@ -734,7 +674,7 @@ void dsp56k_execute_instruction(dsp_core_t* dsp) /* Evaluate time after instructions have been executed to avoid asking too frequently */ uint32_t cur_time = SDL_GetTicks(); if (cur_time-start_time>1000) { - printf( "Dsp: %d i/s\n", (dsp->num_inst*1000)/(cur_time-start_time)); + DPRINTF("Dsp: %d i/s\n", (dsp->num_inst*1000)/(cur_time-start_time)); start_time=cur_time; dsp->num_inst=0; } @@ -1021,9 +961,6 @@ uint32_t dsp56k_read_memory(dsp_core_t* dsp, int space, uint32_t address) void dsp56k_write_memory(dsp_core_t* dsp, int space, uint32_t address, uint32_t value) { - assert((value & 0xFF000000) == 0); - assert((address & 0xFF000000) == 0); - if (TRACE_DSP_DISASM_MEM) write_memory_disasm(dsp, space, address, value); else @@ -1122,9 +1059,7 @@ static void dsp_write_reg(dsp_core_t* dsp, uint32_t numreg, uint32_t value) /* Stack underflow or overflow detected, raise interrupt */ dsp56k_add_interrupt(dsp, DSP_INTER_STACK_ERROR); dsp->registers[DSP_REG_SP] = value & (3<executing_for_disasm) { - printf( "Dsp: Stack Overflow or Underflow\n"); - } + DPRINTF("Dsp: Stack Overflow or Underflow\n"); if (dsp->exception_debugging) { assert(false); } @@ -1167,8 +1102,7 @@ static void dsp_stack_push(dsp_core_t* dsp, uint32_t curpc, uint32_t cursr, uint if ((stack_error==0) && (stack & (1<executing_for_disasm) - printf("Dsp: Stack Overflow\n"); + DPRINTF("Dsp: Stack Overflow\n"); if (dsp->exception_debugging) assert(false); } @@ -1204,8 +1138,7 @@ static void dsp_stack_pop(dsp_core_t* dsp, uint32_t *newpc, uint32_t *newsr) if ((stack_error==0) && (stack & (1<executing_for_disasm) - printf("Dsp: Stack underflow\n"); + DPRINTF("Dsp: Stack underflow\n"); if (dsp->exception_debugging) assert(false); } @@ -1458,5 +1391,3 @@ static uint32_t dsp_signextend(int bits, uint32_t v) { assert(shift > 0); return (uint32_t)(((int32_t)v << shift) >> shift); } - - diff --git a/hw/xbox/mcpx/dsp/dsp_cpu.h b/hw/xbox/mcpx/dsp/dsp_cpu.h index 6876b4d20a..4cffd2631d 100644 --- a/hw/xbox/mcpx/dsp/dsp_cpu.h +++ b/hw/xbox/mcpx/dsp/dsp_cpu.h @@ -29,115 +29,7 @@ #include #include -#define DSP_OMR_MA 0x00 -#define DSP_OMR_MB 0x01 -#define DSP_OMR_DE 0x02 -#define DSP_OMR_SD 0x06 -#define DSP_OMR_EA 0x07 - -#define DSP_SR_C 0x00 -#define DSP_SR_V 0x01 -#define DSP_SR_Z 0x02 -#define DSP_SR_N 0x03 -#define DSP_SR_U 0x04 -#define DSP_SR_E 0x05 -#define DSP_SR_L 0x06 - -#define DSP_SR_I0 0x08 -#define DSP_SR_I1 0x09 -#define DSP_SR_S0 0x0a -#define DSP_SR_S1 0x0b -#define DSP_SR_T 0x0d -#define DSP_SR_LF 0x0f - -#define DSP_SP_SE 0x04 -#define DSP_SP_UF 0x05 - -/* Registers numbers in dsp.registers[] */ -#define DSP_REG_X0 0x04 -#define DSP_REG_X1 0x05 -#define DSP_REG_Y0 0x06 -#define DSP_REG_Y1 0x07 -#define DSP_REG_A0 0x08 -#define DSP_REG_B0 0x09 -#define DSP_REG_A2 0x0a -#define DSP_REG_B2 0x0b -#define DSP_REG_A1 0x0c -#define DSP_REG_B1 0x0d -#define DSP_REG_A 0x0e -#define DSP_REG_B 0x0f - -#define DSP_REG_R0 0x10 -#define DSP_REG_R1 0x11 -#define DSP_REG_R2 0x12 -#define DSP_REG_R3 0x13 -#define DSP_REG_R4 0x14 -#define DSP_REG_R5 0x15 -#define DSP_REG_R6 0x16 -#define DSP_REG_R7 0x17 - -#define DSP_REG_N0 0x18 -#define DSP_REG_N1 0x19 -#define DSP_REG_N2 0x1a -#define DSP_REG_N3 0x1b -#define DSP_REG_N4 0x1c -#define DSP_REG_N5 0x1d -#define DSP_REG_N6 0x1e -#define DSP_REG_N7 0x1f - -#define DSP_REG_M0 0x20 -#define DSP_REG_M1 0x21 -#define DSP_REG_M2 0x22 -#define DSP_REG_M3 0x23 -#define DSP_REG_M4 0x24 -#define DSP_REG_M5 0x25 -#define DSP_REG_M6 0x26 -#define DSP_REG_M7 0x27 - -#define DSP_REG_SR 0x39 -#define DSP_REG_OMR 0x3a -#define DSP_REG_SP 0x3b -#define DSP_REG_SSH 0x3c -#define DSP_REG_SSL 0x3d -#define DSP_REG_LA 0x3e -#define DSP_REG_LC 0x3f - -#define DSP_REG_NULL 0x00 -#define DSP_REG_LCSAVE 0x30 - -#define DSP_REG_MAX 0x40 - -/* Memory spaces for dsp.ram[], dsp.rom[] */ -#define DSP_SPACE_X 0x00 -#define DSP_SPACE_Y 0x01 -#define DSP_SPACE_P 0x02 - -#define DSP_XRAM_SIZE 4096 -#define DSP_YRAM_SIZE 2048 -#define DSP_PRAM_SIZE 4096 - -#define DSP_MIXBUFFER_BASE 0x001400 -#define DSP_MIXBUFFER_SIZE 1024 - -#define DSP_PERIPH_BASE 0xFFFF80 -#define DSP_PERIPH_SIZE 128 - -#define DSP_INTERRUPT_NONE 0x0 -#define DSP_INTERRUPT_DISABLED 0x1 -#define DSP_INTERRUPT_LONG 0x2 - -#define DSP_INTER_RESET 0x0 -#define DSP_INTER_ILLEGAL 0x1 -#define DSP_INTER_STACK_ERROR 0x2 -#define DSP_INTER_TRACE 0x3 -#define DSP_INTER_SWI 0x4 -#define DSP_INTER_HOST_COMMAND 0x5 -#define DSP_INTER_HOST_RCV_DATA 0x6 -#define DSP_INTER_HOST_TRX_DATA 0x7 -#define DSP_INTER_SSI_RCV_DATA_E 0x8 -#define DSP_INTER_SSI_RCV_DATA 0x9 -#define DSP_INTER_SSI_TRX_DATA_E 0xa -#define DSP_INTER_SSI_TRX_DATA 0xb +#include "dsp_cpu_regs.h" typedef enum { DSP_TRACE_MODE, @@ -209,10 +101,6 @@ struct dsp_core_s { /* Current instruction */ uint32_t cur_inst; - /* DSP is in disasm mode ? */ - /* If yes, stack overflow, underflow and illegal instructions messages are not displayed */ - bool executing_for_disasm; - char str_disasm_memory[2][50]; /* Buffer for memory change text in disasm mode */ uint32_t disasm_memory_ptr; /* Pointer for memory change in disasm mode */ @@ -250,7 +138,6 @@ struct dsp_core_s { /* Functions */ void dsp56k_reset_cpu(dsp_core_t* dsp); /* Set dsp_core to use */ void dsp56k_execute_instruction(dsp_core_t* dsp); /* Execute 1 instruction */ -uint16_t dsp56k_execute_one_disasm_instruction(dsp_core_t* dsp, FILE *out, uint32_t pc); /* Execute 1 instruction in disasm mode */ uint32_t dsp56k_read_memory(dsp_core_t* dsp, int space, uint32_t address); void dsp56k_write_memory(dsp_core_t* dsp, int space, uint32_t address, uint32_t value); diff --git a/hw/xbox/mcpx/dsp/dsp_cpu_regs.h b/hw/xbox/mcpx/dsp/dsp_cpu_regs.h new file mode 100644 index 0000000000..75b2009b2f --- /dev/null +++ b/hw/xbox/mcpx/dsp/dsp_cpu_regs.h @@ -0,0 +1,138 @@ +/* + * DSP56300 emulator + * + * Copyright (c) 2015 espes + * + * Adapted from Hatari DSP M56001 emulation + * (C) 2003-2008 ARAnyM developer team + * Adaption to Hatari (C) 2008 by Thomas Huth + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef HW_XBOX_MCPX_DSP_DSP_CPU_REGS_H +#define HW_XBOX_MCPX_DSP_DSP_CPU_REGS_H + +#define DSP_OMR_MA 0x00 +#define DSP_OMR_MB 0x01 +#define DSP_OMR_DE 0x02 +#define DSP_OMR_SD 0x06 +#define DSP_OMR_EA 0x07 + +#define DSP_SR_C 0x00 +#define DSP_SR_V 0x01 +#define DSP_SR_Z 0x02 +#define DSP_SR_N 0x03 +#define DSP_SR_U 0x04 +#define DSP_SR_E 0x05 +#define DSP_SR_L 0x06 + +#define DSP_SR_I0 0x08 +#define DSP_SR_I1 0x09 +#define DSP_SR_S0 0x0a +#define DSP_SR_S1 0x0b +#define DSP_SR_T 0x0d +#define DSP_SR_LF 0x0f + +#define DSP_SP_SE 0x04 +#define DSP_SP_UF 0x05 + +/* Registers numbers in dsp.registers[] */ +#define DSP_REG_X0 0x04 +#define DSP_REG_X1 0x05 +#define DSP_REG_Y0 0x06 +#define DSP_REG_Y1 0x07 +#define DSP_REG_A0 0x08 +#define DSP_REG_B0 0x09 +#define DSP_REG_A2 0x0a +#define DSP_REG_B2 0x0b +#define DSP_REG_A1 0x0c +#define DSP_REG_B1 0x0d +#define DSP_REG_A 0x0e +#define DSP_REG_B 0x0f + +#define DSP_REG_R0 0x10 +#define DSP_REG_R1 0x11 +#define DSP_REG_R2 0x12 +#define DSP_REG_R3 0x13 +#define DSP_REG_R4 0x14 +#define DSP_REG_R5 0x15 +#define DSP_REG_R6 0x16 +#define DSP_REG_R7 0x17 + +#define DSP_REG_N0 0x18 +#define DSP_REG_N1 0x19 +#define DSP_REG_N2 0x1a +#define DSP_REG_N3 0x1b +#define DSP_REG_N4 0x1c +#define DSP_REG_N5 0x1d +#define DSP_REG_N6 0x1e +#define DSP_REG_N7 0x1f + +#define DSP_REG_M0 0x20 +#define DSP_REG_M1 0x21 +#define DSP_REG_M2 0x22 +#define DSP_REG_M3 0x23 +#define DSP_REG_M4 0x24 +#define DSP_REG_M5 0x25 +#define DSP_REG_M6 0x26 +#define DSP_REG_M7 0x27 + +#define DSP_REG_SR 0x39 +#define DSP_REG_OMR 0x3a +#define DSP_REG_SP 0x3b +#define DSP_REG_SSH 0x3c +#define DSP_REG_SSL 0x3d +#define DSP_REG_LA 0x3e +#define DSP_REG_LC 0x3f + +#define DSP_REG_NULL 0x00 +#define DSP_REG_LCSAVE 0x30 + +#define DSP_REG_MAX 0x40 + +/* Memory spaces for dsp.ram[], dsp.rom[] */ +#define DSP_SPACE_X 0x00 +#define DSP_SPACE_Y 0x01 +#define DSP_SPACE_P 0x02 + +#define DSP_XRAM_SIZE 4096 +#define DSP_YRAM_SIZE 2048 +#define DSP_PRAM_SIZE 4096 + +#define DSP_MIXBUFFER_BASE 0x001400 +#define DSP_MIXBUFFER_SIZE 1024 + +#define DSP_PERIPH_BASE 0xFFFF80 +#define DSP_PERIPH_SIZE 128 + +#define DSP_INTERRUPT_NONE 0x0 +#define DSP_INTERRUPT_DISABLED 0x1 +#define DSP_INTERRUPT_LONG 0x2 + +#define DSP_INTER_RESET 0x0 +#define DSP_INTER_ILLEGAL 0x1 +#define DSP_INTER_STACK_ERROR 0x2 +#define DSP_INTER_TRACE 0x3 +#define DSP_INTER_SWI 0x4 +#define DSP_INTER_HOST_COMMAND 0x5 +#define DSP_INTER_HOST_RCV_DATA 0x6 +#define DSP_INTER_HOST_TRX_DATA 0x7 +#define DSP_INTER_SSI_RCV_DATA_E 0x8 +#define DSP_INTER_SSI_RCV_DATA 0x9 +#define DSP_INTER_SSI_TRX_DATA_E 0xa +#define DSP_INTER_SSI_TRX_DATA 0xb + +#endif diff --git a/hw/xbox/mcpx/dsp/dsp_dis.inl b/hw/xbox/mcpx/dsp/dsp_dis.c.inc similarity index 100% rename from hw/xbox/mcpx/dsp/dsp_dis.inl rename to hw/xbox/mcpx/dsp/dsp_dis.c.inc diff --git a/hw/xbox/mcpx/dsp/dsp_dma.c b/hw/xbox/mcpx/dsp/dsp_dma.c index 377475fb76..98f7bc3cb7 100644 --- a/hw/xbox/mcpx/dsp/dsp_dma.c +++ b/hw/xbox/mcpx/dsp/dsp_dma.c @@ -18,48 +18,14 @@ * License along with this library; if not, see . */ -#include -#include -#include -#include - -#include +#include "qemu/osdep.h" #include "qemu/compiler.h" #include "dsp_dma.h" +#include "dsp_dma_regs.h" #include "dsp_state.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) - - -// #define DEBUG #ifdef DEBUG -# define DPRINTF(s, ...) printf(s, ## __VA_ARGS__) -#else -# define DPRINTF(s, ...) do { } while (0) -#endif -#ifdef DEBUG const char *buffer_names[] = { "fifo0", /* 0x0 */ "fifo1", /* 0x1 */ @@ -98,8 +64,6 @@ const char *space_names[] = { #endif -#define MIN(a,b) (((a)<(b))?(a):(b)) - static void scratch_circular_copy( DSPDMAState *s, uint32_t scratch_base, @@ -142,8 +106,6 @@ static void dsp_dma_run(DSPDMAState *s) return; } - // DSPState *dsp = container_of(s, DSPState, dma); - while (!(s->next_block & NODE_POINTER_EOL)) { uint32_t addr = s->next_block & NODE_POINTER_VAL; uint32_t block_addr = 0; diff --git a/hw/xbox/mcpx/dsp/dsp_dma_regs.h b/hw/xbox/mcpx/dsp/dsp_dma_regs.h new file mode 100644 index 0000000000..a373cb7c14 --- /dev/null +++ b/hw/xbox/mcpx/dsp/dsp_dma_regs.h @@ -0,0 +1,46 @@ +/* + * MCPX DSP DMA + * + * Copyright (c) 2015 espes + * Copyright (c) 2020-2025 Matt Borgerson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef HW_XBOX_MCPX_DSP_DSP_DMA_REGS_H +#define HW_XBOX_MCPX_DSP_DSP_DMA_REGS_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) + +#endif diff --git a/hw/xbox/mcpx/dsp/dsp_emu.inl b/hw/xbox/mcpx/dsp/dsp_emu.c.inc similarity index 99% rename from hw/xbox/mcpx/dsp/dsp_emu.inl rename to hw/xbox/mcpx/dsp/dsp_emu.c.inc index e509baa2d7..b67ea13398 100644 --- a/hw/xbox/mcpx/dsp/dsp_emu.inl +++ b/hw/xbox/mcpx/dsp/dsp_emu.c.inc @@ -27,15 +27,10 @@ typedef void (*emu_func_t)(dsp_core_t* dsp); static void emu_undefined(dsp_core_t* dsp) { - if (!dsp->executing_for_disasm) { - dsp->cur_inst_len = 0; - printf("Dsp: 0x%04x: 0x%06x Illegal instruction\n",dsp->pc, dsp->cur_inst); - /* Add some artificial CPU cycles to avoid being stuck in an infinite loop */ - dsp->instr_cycle += 100; - } else { - dsp->cur_inst_len = 1; - dsp->instr_cycle = 0; - } + dsp->cur_inst_len = 0; + printf("Dsp: 0x%04x: 0x%06x Illegal instruction\n",dsp->pc, dsp->cur_inst); + /* Add some artificial CPU cycles to avoid being stuck in an infinite loop */ + dsp->instr_cycle += 100; if (dsp->exception_debugging) { assert(false); } diff --git a/hw/xbox/mcpx/dsp/meson.build b/hw/xbox/mcpx/dsp/meson.build new file mode 100644 index 0000000000..4e06c59496 --- /dev/null +++ b/hw/xbox/mcpx/dsp/meson.build @@ -0,0 +1,2 @@ +libdsp = static_library('dsp', files(['debug.c', 'dsp.c', 'dsp_cpu.c', 'dsp_dma.c']) + genh) +dsp = declare_dependency(objects: libdsp.extract_all_objects(recursive: false)) diff --git a/hw/xbox/mcpx/dsp/trace-events b/hw/xbox/mcpx/dsp/trace-events new file mode 100644 index 0000000000..82f10a3f92 --- /dev/null +++ b/hw/xbox/mcpx/dsp/trace-events @@ -0,0 +1,9 @@ +# See docs/devel/tracing.rst for syntax documentation. + +# dsp.c +dsp_read_peripheral(uint32_t addr, uint32_t val) "addr 0x%"PRIx32" val 0x%"PRIx32 +dsp_write_peripheral(uint32_t addr, uint32_t val) "addr 0x%"PRIx32" val 0x%"PRIx32 + +# dsp_cpu.c +dsp56k_execute_instruction(uint32_t id, uint32_t pc) "[gp=%d]: pc=0x%"PRIx32 +dsp56k_execute_instruction_disasm(const char *disasm) "%s" diff --git a/hw/xbox/mcpx/dsp/trace.h b/hw/xbox/mcpx/dsp/trace.h new file mode 100644 index 0000000000..6ab110fe5b --- /dev/null +++ b/hw/xbox/mcpx/dsp/trace.h @@ -0,0 +1 @@ +#include "trace/trace-hw_xbox_mcpx_dsp.h" diff --git a/hw/xbox/mcpx/meson.build b/hw/xbox/mcpx/meson.build index fd87ef118e..5f6807337d 100644 --- a/hw/xbox/mcpx/meson.build +++ b/hw/xbox/mcpx/meson.build @@ -1,10 +1,9 @@ +subdir('dsp') + mcpx_ss = ss.source_set() -mcpx_ss.add(sdl, libsamplerate, files( +mcpx_ss.add(sdl, libsamplerate, dsp, files( 'apu.c', 'aci.c', - 'dsp/dsp.c', - 'dsp/dsp_cpu.c', - 'dsp/dsp_dma.c', )) specific_ss.add_all(mcpx_ss) \ No newline at end of file diff --git a/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c b/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c index 0530e7ea7b..922284262b 100644 --- a/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c +++ b/hw/xbox/nv2a/pgraph/glsl/vsh-prog.c @@ -614,17 +614,6 @@ static const char* vsh_header = * https://www.opengl.org/registry/specs/NV/vertex_program1_1.txt */ "\n" -//QQQ #ifdef NICE_CODE - "/* Converts the input to vec4, pads with last component */\n" - "vec4 _in(float v) { return vec4(v); }\n" - "vec4 _in(vec2 v) { return v.xyyy; }\n" - "vec4 _in(vec3 v) { return v.xyzz; }\n" - "vec4 _in(vec4 v) { return v.xyzw; }\n" -//#else -// "/* Make sure input is always a vec4 */\n" -// "#define _in(v) vec4(v)\n" -//#endif - "\n" "#define INFINITY (1.0 / 0.0)\n" "\n" "#define MOV(dest, mask, src) dest.mask = _MOV(_in(src)).mask\n" diff --git a/hw/xbox/nv2a/pgraph/glsl/vsh.c b/hw/xbox/nv2a/pgraph/glsl/vsh.c index 25c846bbde..5b2b426e35 100644 --- a/hw/xbox/nv2a/pgraph/glsl/vsh.c +++ b/hw/xbox/nv2a/pgraph/glsl/vsh.c @@ -128,6 +128,24 @@ MString *pgraph_gen_vsh_glsl(const ShaderState *state, bool prefix_outputs) } } } + + mstring_append(header, + "/* Converts the input to vec4, pads with last component */\n" + "vec4 _in(float v) { return vec4(v); }\n" + "vec4 _in(vec2 v) { return v.xyyy; }\n" + "vec4 _in(vec3 v) { return v.xyzz; }\n" + "vec4 _in(vec4 v) { return v.xyzw; }\n" + "\n" + "#define FixNaN(src, mask) _FixNaN(_in(src)).mask\n" + "vec4 _FixNaN(vec4 src)\n" + "{\n" + " bvec4 nans = isnan(src);\n" + " if (!any(nans)) {\n" + " return src;\n" + " }\n" + " return mix(src, sign(floatBitsToInt(src)), nans);\n" + "}\n"); + mstring_append(header, "\n"); MString *body = mstring_from_str("void main() {\n"); @@ -232,11 +250,11 @@ MString *pgraph_gen_vsh_glsl(const ShaderState *state, bool prefix_outputs) break; } - mstring_append(body, " oFog.xyzw = vec4(fogFactor);\n"); + mstring_append(body, " oFog = FixNaN(fogFactor, xyzw);\n"); } else { /* FIXME: Is the fog still calculated / passed somehow?! */ - mstring_append(body, " oFog.xyzw = vec4(1.0);\n"); + mstring_append(body, " oFog = vec4(1.0);\n"); } /* Set outputs */ diff --git a/meson.build b/meson.build index 72e7049e02..c1e9612bbf 100644 --- a/meson.build +++ b/meson.build @@ -3772,6 +3772,7 @@ if have_system 'hw/remote', 'hw/xbox/nv2a', 'hw/xbox/mcpx', + 'hw/xbox/mcpx/dsp', 'hw/xbox', ] endif diff --git a/tests/meson.build b/tests/meson.build index 907a4c1c98..febbe02806 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -86,3 +86,4 @@ subdir('qapi-schema') subdir('qtest') subdir('migration') subdir('functional') +subdir('xbox') diff --git a/tests/xbox/dsp/data/Makefile b/tests/xbox/dsp/data/Makefile new file mode 100644 index 0000000000..8bd271a422 --- /dev/null +++ b/tests/xbox/dsp/data/Makefile @@ -0,0 +1,4 @@ +all: basic + +%: %.a56 + a56 -o $@ $< diff --git a/tests/xbox/dsp/data/basic b/tests/xbox/dsp/data/basic new file mode 100644 index 0000000000..7b11e507ea --- /dev/null +++ b/tests/xbox/dsp/data/basic @@ -0,0 +1,12 @@ +P 0000 0C0040 +P 0040 0AF080 +P 0041 000042 +P 0042 56F400 +P 0043 123456 +P 0044 567000 +P 0045 000003 +P 0046 08F484 +P 0047 000001 +P 0048 0C0042 +I 000040 start +I 000042 mainloop diff --git a/tests/xbox/dsp/data/basic.a56 b/tests/xbox/dsp/data/basic.a56 new file mode 100644 index 0000000000..0be7483dba --- /dev/null +++ b/tests/xbox/dsp/data/basic.a56 @@ -0,0 +1,12 @@ + org p:$0000 + jmp . + */ + +#include "qemu/osdep.h" +#include "hw/xbox/mcpx/dsp/dsp.h" + +static void scratch_rw(void *opaque, uint8_t *ptr, uint32_t addr, size_t len, bool dir) +{ + assert(!"Not implemented"); +} + +static void fifo_rw(void *opaque, uint8_t *ptr, unsigned int index, size_t len, bool dir) +{ + assert(!"Not implemented"); +} + +static void load_prog(DSPState *s, const char *path) +{ + FILE *file = fopen(path, "r"); + assert(file && "Error opening file"); + + char type, line[100], arg1[20], arg2[20]; + int addr, value; + + while (fgets(line, sizeof(line), file)) { + if (sscanf(line, "%c %s %s", &type, arg1, arg2) >= 2) { + switch (type) { + case 'P': + case 'X': + case 'Y': { + assert(sscanf(arg1, "%x", &addr) == 1); + assert(sscanf(arg2, "%x", &value) == 1); + dsp_write_memory(s, type, addr, value); + break; + } + } + } else { + printf("Invalid line: %s\n", line); + assert(0); + } + } + + fclose(file); +} + +static void test_dsp_basic(void) +{ + g_autofree gchar *path = g_test_build_filename(G_TEST_DIST, "data", "basic", NULL); + + DSPState *s = dsp_init(NULL, scratch_rw, fifo_rw); + + load_prog(s, path); + dsp_run(s, 1000); + + uint32_t v = dsp_read_memory(s, 'X', 3); + g_assert_cmphex(v, ==, 0x123456); + + dsp_destroy(s); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/basic", test_dsp_basic); + + return g_test_run(); +} diff --git a/tests/xbox/meson.build b/tests/xbox/meson.build new file mode 100644 index 0000000000..e836218210 --- /dev/null +++ b/tests/xbox/meson.build @@ -0,0 +1 @@ +subdir('dsp')