apu: Add new DSP56300 emulator with JIT execution engine

- Fixes a few bugs in the existing C interpreter
- Adds a state abstraction layer to switch between engines at runtime
  and maintain snapshot compatibility
This commit is contained in:
Matt Borgerson
2026-03-20 13:09:34 -07:00
committed by mborgerson
parent a34bb0f8c2
commit 67cc79e663
30 changed files with 2644 additions and 685 deletions
+3
View File
@@ -314,6 +314,9 @@ audio:
type: integer
default: 0 # 0 = auto
use_dsp: bool
use_dsp_jit:
type: bool
default: true
hrtf:
type: bool
default: true
+72 -47
View File
@@ -214,8 +214,6 @@ static void throttle(MCPXAPUState *d)
d->next_frame_time_us += (queued_bytes > mid) - (queued_bytes < mid);
}
}
d->sleep_acc_us += qemu_clock_get_us(QEMU_CLOCK_REALTIME) - start_us;
}
static void se_frame(MCPXAPUState *d)
@@ -225,22 +223,21 @@ static void se_frame(MCPXAPUState *d)
g_dbg.gp_realtime = d->gp.realtime;
g_dbg.ep_realtime = d->ep.realtime;
int64_t now_ms = qemu_clock_get_ms(QEMU_CLOCK_REALTIME);
int64_t elapsed_ms = now_ms - d->frame_count_time_ms;
if (elapsed_ms >= 1000) {
int64_t start_us = qemu_clock_get_us(QEMU_CLOCK_REALTIME);
int64_t elapsed_us = start_us - d->frame_count_time_us;
if (elapsed_us >= 1000000) {
/* A rudimentary calculation to determine approximately how taxed the APU
* thread is, by measuring how much time we spend waiting for buffer to drain
* versus working on building frames.
* thread is, by measuring how much time we spend building frames.
* =1: thread is not sleeping and likely falling behind realtime
* <1: thread is able to complete work on time
*/
g_dbg.utilization = 1.0 - d->sleep_acc_us / (elapsed_ms * 1000.0);
g_dbg.frames_processed = (int)(d->frame_count * 1000.0 / elapsed_ms + 0.5);
g_dbg.utilization = (double)d->frame_work_acc_us / (double)elapsed_us;
g_dbg.frames_processed = (int)(d->frame_count * 1000000.0 / elapsed_us + 0.5);
throttle_publish_debug(d);
d->frame_count_time_ms = now_ms;
d->frame_count_time_us = start_us;
d->frame_count = 0;
d->sleep_acc_us = 0;
d->frame_work_acc_us = 0;
}
d->frame_count++;
@@ -252,6 +249,7 @@ static void se_frame(MCPXAPUState *d)
mcpx_apu_monitor_frame(d);
d->ep_frame_div++;
d->frame_work_acc_us += qemu_clock_get_us(QEMU_CLOCK_REALTIME) - start_us;
mcpx_debug_end_frame();
}
@@ -326,10 +324,8 @@ static void mcpx_apu_reset_locked(MCPXAPUState *d)
mcpx_apu_vp_reset(d);
// FIXME: Reset DSP state
memset(d->gp.dsp->core.pram_opcache, 0,
sizeof(d->gp.dsp->core.pram_opcache));
memset(d->ep.dsp->core.pram_opcache, 0,
sizeof(d->ep.dsp->core.pram_opcache));
dsp_invalidate_opcache(d->gp.dsp);
dsp_invalidate_opcache(d->ep.dsp);
d->set_irq = false;
}
@@ -357,10 +353,22 @@ static void mcpx_apu_vm_state_change(void *opaque, bool running, RunState state)
bql_unlock();
qemu_mutex_lock(&d->lock);
mcpx_apu_wait_for_idle(d);
if (d->gp.dsp) {
dsp_sync_to_vm(d->gp.dsp);
}
if (d->ep.dsp) {
dsp_sync_to_vm(d->ep.dsp);
}
qemu_mutex_unlock(&d->lock);
bql_lock();
} else {
qemu_mutex_lock(&d->lock);
if (d->gp.dsp) {
dsp_sync_from_vm(d->gp.dsp);
}
if (d->ep.dsp) {
dsp_sync_from_vm(d->ep.dsp);
}
mcpx_apu_resume(d);
qemu_mutex_unlock(&d->lock);
}
@@ -440,6 +448,23 @@ static void mcpx_apu_exitfn(PCIDevice *dev)
mcpx_apu_monitor_finalize(d);
}
static bool vp_dsp_dma_read_count_needed(void *opaque)
{
DSPDMAState *s = opaque;
return s->dma_read_count != 0;
}
static const VMStateDescription vmstate_vp_dsp_dma_read_count = {
.name = "mcpx-apu/dsp-state/dma/dma_read_count",
.version_id = 1,
.minimum_version_id = 1,
.needed = vp_dsp_dma_read_count_needed,
.fields = (VMStateField[]) {
VMSTATE_UINT32(dma_read_count, DSPDMAState),
VMSTATE_END_OF_LIST()
}
};
const VMStateDescription vmstate_vp_dsp_dma_state = {
.name = "mcpx-apu/dsp-state/dma",
.version_id = 1,
@@ -452,6 +477,10 @@ const VMStateDescription vmstate_vp_dsp_dma_state = {
VMSTATE_BOOL(error, DSPDMAState),
VMSTATE_BOOL(eol, DSPDMAState),
VMSTATE_END_OF_LIST()
},
.subsections = (const VMStateDescription * const []) {
&vmstate_vp_dsp_dma_read_count,
NULL
}
};
@@ -460,37 +489,33 @@ const VMStateDescription vmstate_vp_dsp_core_state = {
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
// FIXME: Remove unnecessary fields
VMSTATE_UINT16(instr_cycle, dsp_core_t),
VMSTATE_UINT32(pc, dsp_core_t),
VMSTATE_UINT32_ARRAY(registers, dsp_core_t, DSP_REG_MAX),
VMSTATE_UINT32_2DARRAY(stack, dsp_core_t, 2, 16),
VMSTATE_UINT32_ARRAY(xram, dsp_core_t, DSP_XRAM_SIZE),
VMSTATE_UINT32_ARRAY(yram, dsp_core_t, DSP_YRAM_SIZE),
VMSTATE_UINT32_ARRAY(pram, dsp_core_t, DSP_PRAM_SIZE),
VMSTATE_UINT32_ARRAY(mixbuffer, dsp_core_t, DSP_MIXBUFFER_SIZE),
VMSTATE_UINT32_ARRAY(periph, dsp_core_t, DSP_PERIPH_SIZE),
VMSTATE_UINT32(loop_rep, dsp_core_t),
VMSTATE_UINT32(pc_on_rep, dsp_core_t),
VMSTATE_UINT16(interrupt_state, dsp_core_t),
VMSTATE_UINT16(interrupt_instr_fetch, dsp_core_t),
VMSTATE_UINT16(interrupt_save_pc, dsp_core_t),
VMSTATE_UINT16(interrupt_counter, dsp_core_t),
VMSTATE_UINT16(interrupt_ipl_to_raise, dsp_core_t),
VMSTATE_UINT16(interrupt_pipeline_count, dsp_core_t),
VMSTATE_INT16_ARRAY(interrupt_ipl, dsp_core_t, 12),
VMSTATE_UINT16_ARRAY(interrupt_is_pending, dsp_core_t, 12),
VMSTATE_UINT32(num_inst, dsp_core_t),
VMSTATE_UINT32(cur_inst_len, dsp_core_t),
VMSTATE_UINT32(cur_inst, 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),
VMSTATE_BOOL(disasm_is_looping, dsp_core_t),
VMSTATE_UINT32(disasm_cur_inst, dsp_core_t),
VMSTATE_UINT16(disasm_cur_inst_len, dsp_core_t),
VMSTATE_UINT32_ARRAY(disasm_registers_save, dsp_core_t, 64),
VMSTATE_UINT16(instr_cycle, DspCoreState),
VMSTATE_UINT32(pc, DspCoreState),
VMSTATE_UINT32_ARRAY(registers, DspCoreState, DSP_REG_MAX),
VMSTATE_UINT32_2DARRAY(stack, DspCoreState, 2, 16),
VMSTATE_UINT32_ARRAY(xram, DspCoreState, DSP_XRAM_SIZE),
VMSTATE_UINT32_ARRAY(yram, DspCoreState, DSP_YRAM_SIZE),
VMSTATE_UINT32_ARRAY(pram, DspCoreState, DSP_PRAM_SIZE),
VMSTATE_UINT32_ARRAY(mixbuffer, DspCoreState, DSP_MIXBUFFER_SIZE),
VMSTATE_UINT32_ARRAY(periph, DspCoreState, DSP_PERIPH_SIZE),
VMSTATE_UINT32(loop_rep, DspCoreState),
VMSTATE_UINT32(pc_on_rep, DspCoreState),
VMSTATE_UINT16(interrupt_state, DspCoreState),
VMSTATE_UINT16(interrupt_instr_fetch, DspCoreState),
VMSTATE_UINT16(interrupt_save_pc, DspCoreState),
VMSTATE_UINT16(interrupt_counter, DspCoreState),
VMSTATE_UINT16(interrupt_ipl_to_raise, DspCoreState),
VMSTATE_UINT16(interrupt_pipeline_count, DspCoreState),
VMSTATE_INT16_ARRAY(interrupt_ipl, DspCoreState, 12),
VMSTATE_UINT16_ARRAY(interrupt_is_pending, DspCoreState, 12),
VMSTATE_UNUSED(4), /* was num_inst */
VMSTATE_UINT32(cur_inst_len, DspCoreState),
VMSTATE_UINT32(cur_inst, DspCoreState),
VMSTATE_UNUSED(273), /* was: unused(1) + disasm_memory_ptr(4) +
* exception_debugging(1) + disasm_prev_inst_pc(4) +
* disasm_is_looping(1) + disasm_cur_inst(4) +
* disasm_cur_inst_len(2) +
* disasm_registers_save(256) */
VMSTATE_END_OF_LIST()
}
};
@@ -500,7 +525,7 @@ const VMStateDescription vmstate_vp_dsp_state = {
.version_id = 1,
.minimum_version_id = 1,
.fields = (VMStateField[]) {
VMSTATE_STRUCT(core, DSPState, 1, vmstate_vp_dsp_core_state, dsp_core_t),
VMSTATE_STRUCT(core, DSPState, 1, vmstate_vp_dsp_core_state, DspCoreState),
VMSTATE_STRUCT(dma, DSPState, 1, vmstate_vp_dsp_dma_state, DSPDMAState),
VMSTATE_INT32(save_cycles, DSPState),
VMSTATE_UINT32(interrupts, DSPState),
+2 -2
View File
@@ -96,9 +96,9 @@ typedef struct MCPXAPUState {
uint32_t regs[0x20000];
int ep_frame_div;
int sleep_acc_us;
int frame_work_acc_us;
int frame_count;
int64_t frame_count_time_ms;
int64_t frame_count_time_us;
int64_t next_frame_time_us;
struct {
-299
View File
@@ -1,299 +0,0 @@
/*
* 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 <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#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
-4
View File
@@ -25,10 +25,6 @@
#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__); \
+123 -135
View File
@@ -24,61 +24,27 @@
*/
#include "qemu/osdep.h"
#include "dsp_cpu.h"
#include "dsp_dma.h"
#include "dsp_state.h"
#include "dsp.h"
#include "debug.h"
#include "dsp_internal.h"
#include "trace.h"
#include "ui/xemu-settings.h"
/* Defines */
#define BITMASK(x) ((1<<(x))-1)
#define BITMASK(x) ((1 << (x)) - 1)
#define INTERRUPT_ABORT_FRAME (1 << 0)
#define INTERRUPT_START_FRAME (1 << 1)
#define INTERRUPT_DMA_EOL (1 << 7)
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);
static int g_gp_frame_count = 0;
DSPState *dsp_init(void *rw_opaque,
dsp_scratch_rw_func scratch_rw,
dsp_fifo_rw_func fifo_rw)
/*
* Shared peripheral I/O helpers, used by both backends via callbacks.
*/
uint32_t read_peripheral(DSPState *dsp, uint32_t address)
{
DPRINTF("dsp_init\n");
DSPState* dsp = (DSPState*)malloc(sizeof(DSPState));
memset(dsp, 0, sizeof(*dsp));
dsp->core.read_peripheral = read_peripheral;
dsp->core.write_peripheral = write_peripheral;
dsp->dma.core = &dsp->core;
dsp->dma.rw_opaque = rw_opaque;
dsp->dma.scratch_rw = scratch_rw;
dsp->dma.fifo_rw = fifo_rw;
dsp_reset(dsp);
return dsp;
}
void dsp_reset(DSPState* dsp)
{
dsp56k_reset_cpu(&dsp->core);
dsp->save_cycles = 0;
}
void dsp_destroy(DSPState* dsp)
{
free(dsp);
}
static uint32_t read_peripheral(dsp_core_t* core, uint32_t address) {
DSPState* dsp = container_of(core, DSPState, core);
uint32_t v = 0xababa;
switch(address) {
switch (address) {
case 0xFFFFB3:
v = 0; // core->num_inst; // ??
break;
@@ -106,13 +72,12 @@ static uint32_t read_peripheral(dsp_core_t* core, uint32_t address) {
return v;
}
static void write_peripheral(dsp_core_t* core, uint32_t address, uint32_t value) {
DSPState* dsp = container_of(core, DSPState, core);
switch(address) {
void write_peripheral(DSPState *dsp, uint32_t address, uint32_t value)
{
switch (address) {
case 0xFFFFC4:
if (value & 1) {
core->is_idle = true;
dsp_set_halt_requested(dsp, true);
}
break;
case 0xFFFFC5:
@@ -138,105 +103,128 @@ static void write_peripheral(dsp_core_t* core, uint32_t address, uint32_t value)
trace_dsp_write_peripheral(address, value);
}
void dsp_step(DSPState* dsp)
{
dsp56k_execute_instruction(&dsp->core);
}
void dsp_run(DSPState* dsp, int cycles)
{
dsp->save_cycles += cycles;
if (dsp->save_cycles <= 0) return;
int dma_timer = 0;
while (dsp->save_cycles > 0)
{
dsp56k_execute_instruction(&dsp->core);
dsp->save_cycles -= dsp->core.instr_cycle;
dsp->core.cycle_count++;
if (dsp->dma.control & DMA_CONTROL_RUNNING) {
dma_timer++;
}
if (dma_timer > 2) {
dma_timer = 0;
dsp->dma.control &= ~DMA_CONTROL_RUNNING;
dsp->dma.control |= DMA_CONTROL_STOPPED;
}
if (dsp->core.is_idle) break;
}
/* FIXME: DMA timing be done cleaner. Xbox enables running
* then polls to make sure its running. But we complete DMA instantaneously,
* so when is it supposed to be signaled that it stopped? Maybe just wait at
* least one cycle? How long does hardware wait?
*/
}
void dsp_bootstrap(DSPState* dsp)
{
// scratch memory is dma'd in to pram by the bootrom
dsp->dma.scratch_rw(dsp->dma.rw_opaque,
(uint8_t*)dsp->core.pram, 0, 0x800*4, false);
for (int i = 0; i < 0x800; i++) {
if (dsp->core.pram[i] & 0xff000000) {
DPRINTF("Bootstrap %04x: %08x\n", i, dsp->core.pram[i]);
dsp->core.pram[i] &= 0x00ffffff;
}
}
memset(dsp->core.pram_opcache, 0, sizeof(dsp->core.pram_opcache));
}
void dsp_start_frame(DSPState* dsp)
void dsp_start_frame_impl(DSPState *dsp)
{
dsp->interrupts |= INTERRUPT_START_FRAME;
}
uint32_t dsp_read_memory(DSPState* dsp, char space, uint32_t address)
DSPState *dsp_init(void *rw_opaque, dsp_scratch_rw_func scratch_rw,
dsp_fifo_rw_func fifo_rw, bool is_gp)
{
int space_id;
DSPState *dsp = g_new0(DSPState, 1);
dsp->is_gp = is_gp;
dsp->core.is_gp = is_gp;
switch (space) {
case 'X':
space_id = DSP_SPACE_X;
break;
case 'Y':
space_id = DSP_SPACE_Y;
break;
case 'P':
space_id = DSP_SPACE_P;
break;
default:
assert(!"Invalid dsp space when reading from memory");
return 0;
dsp->dma.rw_opaque = rw_opaque;
dsp->dma.scratch_rw = scratch_rw;
dsp->dma.fifo_rw = fifo_rw;
if (g_config.audio.use_dsp_jit) {
dsp_jit_init(dsp);
} else {
dsp_c_init(dsp);
}
return dsp56k_read_memory(&dsp->core, space_id, address);
dsp_reset(dsp);
return dsp;
}
void dsp_write_memory(DSPState* dsp, char space, uint32_t address, uint32_t value)
void dsp_destroy(DSPState *dsp)
{
int space_id;
dsp->ops->finalize(dsp);
g_free(dsp);
}
switch (space) {
case 'X':
space_id = DSP_SPACE_X;
break;
case 'Y':
space_id = DSP_SPACE_Y;
break;
case 'P':
space_id = DSP_SPACE_P;
break;
default:
assert(!"Invalid dsp space when writing to memory");
void dsp_reset(DSPState *dsp)
{
dsp->ops->reset(dsp);
}
void dsp_step(DSPState *dsp)
{
dsp->ops->step(dsp);
}
void dsp_run(DSPState *dsp, int cycles)
{
dsp->ops->run(dsp, cycles);
}
void dsp_bootstrap(DSPState *dsp)
{
dsp->ops->bootstrap(dsp);
}
void dsp_start_frame(DSPState *dsp)
{
if (dsp->is_gp) {
g_gp_frame_count++;
}
dsp->ops->start_frame(dsp);
}
uint32_t dsp_read_memory(DSPState *dsp, char space, uint32_t address)
{
return dsp->ops->read_memory(dsp, space, address);
}
void dsp_write_memory(DSPState *dsp, char space, uint32_t address,
uint32_t value)
{
dsp->ops->write_memory(dsp, space, address, value);
}
bool dsp_get_halt_requested(DSPState *dsp)
{
return dsp->ops->get_halt_requested(dsp);
}
void dsp_set_halt_requested(DSPState *dsp, bool idle)
{
dsp->ops->set_halt_requested(dsp, idle);
}
uint32_t dsp_get_cycle_count(DSPState *dsp)
{
return dsp->ops->get_cycle_count(dsp);
}
void dsp_set_cycle_count(DSPState *dsp, uint32_t count)
{
dsp->ops->set_cycle_count(dsp, count);
}
void dsp_invalidate_opcache(DSPState *dsp)
{
dsp->ops->invalidate_opcache(dsp);
}
void dsp_sync_to_vm(DSPState *dsp)
{
dsp->ops->sync_to_vm(dsp);
}
void dsp_sync_from_vm(DSPState *dsp)
{
dsp->ops->sync_from_vm(dsp);
}
void dsp_set_engine(DSPState *dsp, bool use_jit)
{
bool currently_jit = (dsp->ops == &jit_dsp_ops);
if (use_jit == currently_jit) {
return;
}
dsp56k_write_memory(&dsp->core, space_id, address, value);
dsp_sync_to_vm(dsp);
dsp->ops->finalize(dsp);
if (use_jit) {
dsp_jit_init(dsp);
} else {
dsp_c_init(dsp);
}
dsp_sync_from_vm(dsp);
}
+105 -27
View File
@@ -1,22 +1,22 @@
/*
* MCPX DSP emulator
*
* Copyright (c) 2015 espes
*
* 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
@@ -27,35 +27,113 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "interp/dsp_cpu_regs.h"
#include "dsp_dma.h"
typedef struct DSPState DSPState;
typedef void (*dsp_scratch_rw_func)(
void *opaque, uint8_t *ptr, uint32_t addr, size_t len, bool dir);
typedef void (*dsp_fifo_rw_func)(
void *opaque, uint8_t *ptr, unsigned int index, size_t len, bool dir);
typedef struct DSPOps {
void (*finalize)(DSPState *dsp);
void (*reset)(DSPState *dsp);
void (*step)(DSPState *dsp);
void (*run)(DSPState *dsp, int cycles);
void (*bootstrap)(DSPState *dsp);
void (*start_frame)(DSPState *dsp);
uint32_t (*read_memory)(DSPState *dsp, char space, uint32_t addr);
void (*write_memory)(DSPState *dsp, char space, uint32_t addr,
uint32_t value);
bool (*get_halt_requested)(DSPState *dsp);
void (*set_halt_requested)(DSPState *dsp, bool idle);
uint32_t (*get_cycle_count)(DSPState *dsp);
void (*set_cycle_count)(DSPState *dsp, uint32_t count);
void (*invalidate_opcache)(DSPState *dsp);
void (*sync_to_vm)(DSPState *dsp);
void (*sync_from_vm)(DSPState *dsp);
} DSPOps;
DSPState *dsp_init(void *rw_opaque,
dsp_scratch_rw_func scratch_rw,
dsp_fifo_rw_func fifo_rw);
void dsp_destroy(DSPState* dsp);
void dsp_reset(DSPState* dsp);
/*
* Shared VM state for save/load and backend synchronization.
* Both the C interpreter and JIT backends sync to/from this struct.
*
* TODO: This struct mirrors the old C interpreter's dsp_core_t layout for
* snapshot compatibility. It uses 16-bit interrupt fields and only 4 interrupt
* slots, losing fidelity when saving JIT state (which has 24-bit addresses and
* 128 IVT slots). Once the C interpreter is removed, simplify this to match
* the JIT's native state and bump the vmstate version.
*/
typedef struct DspCoreState {
uint32_t pc;
uint32_t registers[DSP_REG_MAX];
uint32_t stack[2][16];
void dsp_step(DSPState* dsp);
void dsp_run(DSPState* dsp, int cycles);
uint32_t xram[DSP_XRAM_SIZE];
uint32_t yram[DSP_YRAM_SIZE];
uint32_t pram[DSP_PRAM_SIZE];
uint32_t mixbuffer[DSP_MIXBUFFER_SIZE];
uint32_t periph[DSP_PERIPH_SIZE];
void dsp_bootstrap(DSPState* dsp);
void dsp_start_frame(DSPState* dsp);
uint32_t cycle_count;
uint16_t instr_cycle;
bool halt_requested;
bool is_gp;
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 loop_rep;
uint32_t pc_on_rep;
uint32_t cur_inst;
uint32_t cur_inst_len;
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);
uint16_t interrupt_state;
uint16_t interrupt_instr_fetch;
uint16_t interrupt_save_pc;
uint16_t interrupt_counter;
uint16_t interrupt_ipl_to_raise;
uint16_t interrupt_pipeline_count;
int16_t interrupt_ipl[12]; /* only [0..3] used; size 12 for snapshot compat */
uint16_t interrupt_is_pending[12]; /* only [0..3] used; size 12 for snapshot compat */
} DspCoreState;
struct DSPState {
const DSPOps *ops;
DspCoreState core;
DSPDMAState dma;
int save_cycles;
uint32_t interrupts;
bool is_gp;
void *backend;
};
DSPState *dsp_init(void *rw_opaque, dsp_scratch_rw_func scratch_rw,
dsp_fifo_rw_func fifo_rw, bool is_gp);
void dsp_destroy(DSPState *dsp);
void dsp_reset(DSPState *dsp);
void dsp_step(DSPState *dsp);
void dsp_run(DSPState *dsp, int cycles);
void dsp_bootstrap(DSPState *dsp);
void dsp_start_frame(DSPState *dsp);
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);
/* Accessor functions for backend-independent state access */
bool dsp_get_halt_requested(DSPState *dsp);
void dsp_set_halt_requested(DSPState *dsp, bool idle);
uint32_t dsp_get_cycle_count(DSPState *dsp);
void dsp_set_cycle_count(DSPState *dsp, uint32_t count);
void dsp_invalidate_opcache(DSPState *dsp);
/* Backend synchronization - sync backend state to/from DspCoreState */
void dsp_sync_to_vm(DSPState *dsp);
void dsp_sync_from_vm(DSPState *dsp);
/* Engine switching */
void dsp_set_engine(DSPState *dsp, bool use_jit);
#endif /* DSP_H */
+299
View File
@@ -0,0 +1,299 @@
/*
* MCPX DSP emulator - C interpreter backend
*
* 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 "dsp_internal.h"
#include "interp/dsp_cpu.h"
#include "debug.h"
static dsp_core_t *c_core(DSPState *dsp)
{
return (dsp_core_t *)dsp->backend;
}
static uint32_t c_dma_mem_read(void *opaque, int space, uint32_t addr)
{
return dsp56k_read_memory((dsp_core_t *)opaque, space, addr);
}
static void c_dma_mem_write(void *opaque, int space, uint32_t addr,
uint32_t value)
{
dsp56k_write_memory((dsp_core_t *)opaque, space, addr, value);
}
static uint32_t c_read_peripheral(dsp_core_t *core, uint32_t address)
{
return read_peripheral((DSPState *)core->opaque, address);
}
static void c_write_peripheral(dsp_core_t *core, uint32_t address,
uint32_t value)
{
write_peripheral((DSPState *)core->opaque, address, value);
}
static void dsp_c_reset(DSPState *dsp)
{
dsp56k_reset_cpu(c_core(dsp));
dsp->save_cycles = 0;
}
static void dsp_c_step(DSPState *dsp)
{
dsp56k_execute_instruction(c_core(dsp));
}
static void dsp_c_run(DSPState *dsp, int cycles)
{
dsp_core_t *core = c_core(dsp);
dsp->save_cycles += cycles;
if (dsp->save_cycles <= 0)
return;
while (dsp->save_cycles > 0) {
dsp56k_execute_instruction(core);
dsp->save_cycles -= core->instr_cycle;
core->cycle_count += core->instr_cycle;
if (core->is_idle) {
break;
}
}
}
static void dsp_c_bootstrap(DSPState *dsp)
{
dsp_core_t *core = c_core(dsp);
// scratch memory is dma'd in to pram by the bootrom
dsp->dma.scratch_rw(dsp->dma.rw_opaque, (uint8_t *)core->pram, 0, 0x800 * 4,
false);
for (int i = 0; i < 0x800; i++) {
if (core->pram[i] & 0xff000000) {
DPRINTF("Bootstrap %04x: %08x\n", i, core->pram[i]);
core->pram[i] &= 0x00ffffff;
}
}
memset(core->pram_opcache, 0, sizeof(core->pram_opcache));
}
static uint32_t dsp_c_read_memory(DSPState *dsp, char space, uint32_t address)
{
int space_id;
switch (space) {
case 'X':
space_id = DSP_SPACE_X;
break;
case 'Y':
space_id = DSP_SPACE_Y;
break;
case 'P':
space_id = DSP_SPACE_P;
break;
default:
assert(!"Invalid dsp space when reading from memory");
return 0;
}
return dsp56k_read_memory(c_core(dsp), space_id, address);
}
static void dsp_c_write_memory(DSPState *dsp, char space, uint32_t address,
uint32_t value)
{
int space_id;
switch (space) {
case 'X':
space_id = DSP_SPACE_X;
break;
case 'Y':
space_id = DSP_SPACE_Y;
break;
case 'P':
space_id = DSP_SPACE_P;
break;
default:
assert(!"Invalid dsp space when writing to memory");
return;
}
dsp56k_write_memory(c_core(dsp), space_id, address, value);
}
static bool dsp_c_get_halt_requested(DSPState *dsp)
{
return c_core(dsp)->is_idle;
}
static void dsp_c_set_halt_requested(DSPState *dsp, bool halt)
{
c_core(dsp)->is_idle = halt;
}
static uint32_t dsp_c_get_cycle_count(DSPState *dsp)
{
return c_core(dsp)->cycle_count;
}
static void dsp_c_set_cycle_count(DSPState *dsp, uint32_t count)
{
c_core(dsp)->cycle_count = count;
}
static void dsp_c_invalidate_opcache(DSPState *dsp)
{
memset(c_core(dsp)->pram_opcache, 0, sizeof(c_core(dsp)->pram_opcache));
}
/*
* Sync: C interpreter -> DspCoreState (before VM save / debug)
*/
static void dsp_c_sync_to_vm(DSPState *dsp)
{
dsp_core_t *core = c_core(dsp);
DspCoreState *vm = &dsp->core;
vm->pc = core->pc;
vm->cycle_count = core->cycle_count;
vm->instr_cycle = core->instr_cycle;
vm->halt_requested = core->is_idle;
vm->is_gp = core->is_gp;
vm->loop_rep = core->loop_rep;
vm->pc_on_rep = core->pc_on_rep;
vm->cur_inst = core->cur_inst;
vm->cur_inst_len = core->cur_inst_len;
memcpy(vm->registers, core->registers, sizeof(vm->registers));
memcpy(vm->stack, core->stack, sizeof(vm->stack));
memcpy(vm->xram, core->xram, sizeof(vm->xram));
memcpy(vm->yram, core->yram, sizeof(vm->yram));
memcpy(vm->pram, core->pram, sizeof(vm->pram));
memcpy(vm->mixbuffer, core->mixbuffer, sizeof(vm->mixbuffer));
memcpy(vm->periph, core->periph, sizeof(vm->periph));
vm->interrupt_state = core->interrupt_state;
vm->interrupt_instr_fetch = core->interrupt_instr_fetch;
vm->interrupt_save_pc = core->interrupt_save_pc;
vm->interrupt_counter = core->interrupt_counter;
vm->interrupt_ipl_to_raise = core->interrupt_ipl_to_raise;
vm->interrupt_pipeline_count = core->interrupt_pipeline_count;
memcpy(vm->interrupt_ipl, core->interrupt_ipl, sizeof(core->interrupt_ipl));
memcpy(vm->interrupt_is_pending, core->interrupt_is_pending,
sizeof(core->interrupt_is_pending));
}
/*
* Sync: DspCoreState -> C interpreter (after VM load / engine switch)
*/
static void dsp_c_sync_from_vm(DSPState *dsp)
{
dsp_core_t *core = c_core(dsp);
DspCoreState *vm = &dsp->core;
core->pc = vm->pc;
core->cycle_count = vm->cycle_count;
core->instr_cycle = vm->instr_cycle;
core->is_idle = vm->halt_requested;
core->is_gp = vm->is_gp;
core->loop_rep = vm->loop_rep;
core->pc_on_rep = vm->pc_on_rep;
core->cur_inst = vm->cur_inst;
core->cur_inst_len = vm->cur_inst_len;
memcpy(core->registers, vm->registers, sizeof(vm->registers));
memcpy(core->stack, vm->stack, sizeof(vm->stack));
memcpy(core->xram, vm->xram, sizeof(vm->xram));
memcpy(core->yram, vm->yram, sizeof(vm->yram));
memcpy(core->pram, vm->pram, sizeof(vm->pram));
memcpy(core->mixbuffer, vm->mixbuffer, sizeof(vm->mixbuffer));
memcpy(core->periph, vm->periph, sizeof(vm->periph));
core->interrupt_state = vm->interrupt_state;
core->interrupt_instr_fetch = vm->interrupt_instr_fetch;
core->interrupt_save_pc = vm->interrupt_save_pc;
core->interrupt_counter = vm->interrupt_counter;
core->interrupt_ipl_to_raise = vm->interrupt_ipl_to_raise;
core->interrupt_pipeline_count = vm->interrupt_pipeline_count;
memcpy(core->interrupt_ipl, vm->interrupt_ipl, sizeof(core->interrupt_ipl));
memcpy(core->interrupt_is_pending, vm->interrupt_is_pending,
sizeof(core->interrupt_is_pending));
memset(core->pram_opcache, 0, sizeof(core->pram_opcache));
}
void dsp_c_init(DSPState *dsp)
{
dsp_core_t *core = g_new0(dsp_core_t, 1);
core->opaque = dsp;
core->read_peripheral = c_read_peripheral;
core->write_peripheral = c_write_peripheral;
dsp->backend = core;
dsp->ops = &c_dsp_ops;
dsp->dma.mem_opaque = core;
dsp->dma.mem_read = c_dma_mem_read;
dsp->dma.mem_write = c_dma_mem_write;
/* Ensure the interpreter's opcode decoder tables are initialized.
* dsp56k_reset_cpu populates the static nonparallel_matches[] array
* on first call. The runtime state it writes (registers, PC, etc.)
* will be overwritten by the subsequent sync_from_vm. */
dsp56k_reset_cpu(core);
memset(core->pram, 0xCA, DSP_PRAM_SIZE * sizeof(uint32_t));
memset(core->xram, 0xCA, DSP_XRAM_SIZE * sizeof(uint32_t));
memset(core->yram, 0xCA, DSP_YRAM_SIZE * sizeof(uint32_t));
dsp->ops->invalidate_opcache(dsp);
}
static void dsp_c_finalize(DSPState *dsp)
{
g_free(dsp->backend);
dsp->backend = NULL;
}
const DSPOps c_dsp_ops = {
.bootstrap = dsp_c_bootstrap,
.finalize = dsp_c_finalize,
.get_cycle_count = dsp_c_get_cycle_count,
.get_halt_requested = dsp_c_get_halt_requested,
.invalidate_opcache = dsp_c_invalidate_opcache,
.read_memory = dsp_c_read_memory,
.reset = dsp_c_reset,
.run = dsp_c_run,
.set_cycle_count = dsp_c_set_cycle_count,
.set_halt_requested = dsp_c_set_halt_requested,
.start_frame = dsp_start_frame_impl,
.step = dsp_c_step,
.sync_from_vm = dsp_c_sync_from_vm,
.sync_to_vm = dsp_c_sync_to_vm,
.write_memory = dsp_c_write_memory,
};
+27 -12
View File
@@ -20,9 +20,10 @@
#include "qemu/osdep.h"
#include "qemu/compiler.h"
#include "debug.h"
#include "dsp_dma.h"
#include "dsp_dma_regs.h"
#include "dsp_state.h"
#include "interp/dsp_cpu_regs.h"
#ifdef DEBUG
@@ -127,13 +128,14 @@ static void dsp_dma_run(DSPDMAState *s)
assert(!"Dsp dma space address out of range");
}
uint32_t next_block = dsp56k_read_memory(s->core, block_space, block_addr);
uint32_t control = dsp56k_read_memory(s->core, block_space, block_addr+1);
uint32_t count = dsp56k_read_memory(s->core, block_space, block_addr+2);
uint32_t dsp_offset = dsp56k_read_memory(s->core, block_space, block_addr+3);
uint32_t scratch_offset = dsp56k_read_memory(s->core, block_space, block_addr+4);
uint32_t scratch_base = dsp56k_read_memory(s->core, block_space, block_addr+5);
uint32_t scratch_size = dsp56k_read_memory(s->core, block_space, block_addr+6)+1;
uint32_t next_block = s->mem_read(s->mem_opaque, block_space, block_addr);
uint32_t control = s->mem_read(s->mem_opaque, block_space, block_addr+1);
uint32_t count = s->mem_read(s->mem_opaque, block_space, block_addr+2);
uint32_t dsp_offset = s->mem_read(s->mem_opaque, block_space, block_addr+3);
uint32_t scratch_offset = s->mem_read(s->mem_opaque, block_space, block_addr+4);
uint32_t scratch_base = s->mem_read(s->mem_opaque, block_space, block_addr+5);
uint32_t scratch_size = s->mem_read(s->mem_opaque, block_space, block_addr+6)+1;
s->next_block = next_block;
if (s->next_block & NODE_POINTER_EOL) {
@@ -164,6 +166,10 @@ static void dsp_dma_run(DSPDMAState *s)
// bool lsb = (format == 6); // FIXME
switch(format) {
case 0:
item_size = 1;
item_mask = 0x000000ff;
break;
case 1:
item_size = 2;
item_mask = 0x0000ffff;
@@ -219,7 +225,7 @@ static void dsp_dma_run(DSPDMAState *s)
// Interleave samples
for (int i = 0; i < block_count; i++) {
for (int ch = 0; ch < channel_count; ch++) {
uint32_t v = dsp56k_read_memory(s->core,
uint32_t v = s->mem_read(s->mem_opaque,
mem_space, mem_address+ch*block_count+i);
switch(item_size) {
case 2:
@@ -236,7 +242,7 @@ static void dsp_dma_run(DSPDMAState *s)
}
} else {
for (int i = 0; i < count; i++) {
uint32_t v = dsp56k_read_memory(s->core, mem_space, mem_address+i);
uint32_t v = s->mem_read(s->mem_opaque, mem_space, mem_address+i);
switch(item_size) {
case 2:
*(uint16_t*)(scratch_buf + i*2) = v >> 8;
@@ -298,12 +304,12 @@ static void dsp_dma_run(DSPDMAState *s)
break;
}
dsp56k_write_memory(s->core, mem_space, mem_address+i, v);
s->mem_write(s->mem_opaque, mem_space, mem_address+i, v);
}
}
if (buffer_offset_writeback) {
dsp56k_write_memory(s->core, block_space, block_addr+4, scratch_offset);
s->mem_write(s->mem_opaque, block_space, block_addr+4, scratch_offset);
}
}
@@ -315,6 +321,14 @@ uint32_t dsp_dma_read(DSPDMAState *s, DSPDMARegister reg)
case DMA_CONFIGURATION:
return s->configuration;
case DMA_CONTROL:
if (s->control & DMA_CONTROL_RUNNING) {
s->dma_read_count++;
if (s->dma_read_count > 2) {
s->control &= ~DMA_CONTROL_RUNNING;
s->control |= DMA_CONTROL_STOPPED;
s->dma_read_count = 0;
}
}
return s->control;
case DMA_START_BLOCK:
return s->start_block;
@@ -337,6 +351,7 @@ void dsp_dma_write(DSPDMAState *s, DSPDMARegister reg, uint32_t v)
case DMA_CONTROL_ACTION_START:
s->control |= DMA_CONTROL_RUNNING;
s->control &= ~DMA_CONTROL_STOPPED;
s->dma_read_count = 0;
break;
case DMA_CONTROL_ACTION_STOP:
s->control |= DMA_CONTROL_STOPPED;
+19 -4
View File
@@ -22,9 +22,12 @@
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "dsp.h"
#include "dsp_cpu.h"
typedef void (*dsp_scratch_rw_func)(
void *opaque, uint8_t *ptr, uint32_t addr, size_t len, bool dir);
typedef void (*dsp_fifo_rw_func)(
void *opaque, uint8_t *ptr, unsigned int index, size_t len, bool dir);
#define DMA_CONTROL_RUNNING (1 << 4)
#define DMA_CONTROL_STOPPED (1 << 5)
@@ -36,9 +39,17 @@ typedef enum DSPDMARegister {
DMA_NEXT_BLOCK,
} DSPDMARegister;
typedef struct DSPDMAState {
dsp_core_t* core;
/* Memory access callbacks for backend-agnostic DMA */
typedef uint32_t (*dsp_dma_mem_read_func)(void *opaque, int space, uint32_t addr);
typedef void (*dsp_dma_mem_write_func)(void *opaque, int space, uint32_t addr, uint32_t value);
typedef struct DSPDMAState {
/* DSP memory access (backend-agnostic) */
void *mem_opaque;
dsp_dma_mem_read_func mem_read;
dsp_dma_mem_write_func mem_write;
/* System memory access */
void *rw_opaque;
dsp_scratch_rw_func scratch_rw;
dsp_fifo_rw_func fifo_rw;
@@ -50,6 +61,10 @@ typedef struct DSPDMAState {
bool error;
bool eol;
/* DMA completion timer: counts reads of DMA_CONTROL while RUNNING.
* After 3 reads, transitions RUNNING -> STOPPED. */
uint32_t dma_read_count;
} DSPDMAState;
uint32_t dsp_dma_read(DSPDMAState *s, DSPDMARegister reg);
+28
View File
@@ -0,0 +1,28 @@
/*
* MCPX DSP emulator - internal declarations
*
* Copyright (c) 2015 espes
* Copyright (c) 2020-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.
*/
#ifndef DSP_INTERNAL_H
#define DSP_INTERNAL_H
#include "dsp.h"
uint32_t read_peripheral(DSPState *dsp, uint32_t address);
void write_peripheral(DSPState *dsp, uint32_t address, uint32_t value);
void dsp_start_frame_impl(DSPState *dsp);
extern const DSPOps c_dsp_ops;
void dsp_c_init(DSPState *dsp);
extern const DSPOps jit_dsp_ops;
void dsp_jit_init(DSPState *dsp);
#endif /* DSP_INTERNAL_H */
+362
View File
@@ -0,0 +1,362 @@
/*
* MCPX DSP emulator - Rust JIT backend
*
* Copyright (c) 2026 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
*/
#include "qemu/osdep.h"
#include "dsp_internal.h"
#include "debug.h"
#include <dsp56300.h>
/* Map C interpreter interrupt indices to architectural IVT slots.
* Architectural slot = vectorAddr / 2 (per DSP56300FM Table 2-2). */
static const int cinterp_to_arch[4] = {
0, /* RESET: vec $00, slot 0 */
2, /* ILLEGAL: vec $04, slot 2 */
1, /* STACK_ERROR: vec $02, slot 1 */
4, /* TRAP: vec $08, slot 4 */
};
typedef struct JitBackend {
Dsp56300Jit *jit;
uint32_t *xram;
uint32_t *yram;
uint32_t *pram;
} JitBackend;
static JitBackend *jit_be(DSPState *dsp)
{
return (JitBackend *)dsp->backend;
}
static uint32_t jit_dma_mem_read(void *opaque, int space, uint32_t addr)
{
return dsp56300_read_memory((const Dsp56300Jit *)opaque,
(Dsp56300MemSpace)space, addr);
}
static void jit_dma_mem_write(void *opaque, int space, uint32_t addr,
uint32_t value)
{
dsp56300_write_memory((Dsp56300Jit *)opaque, (Dsp56300MemSpace)space, addr,
value);
}
static uint32_t jit_read_peripheral(void *opaque, uint32_t address)
{
return read_peripheral((DSPState *)opaque, address);
}
static void jit_write_peripheral(void *opaque, uint32_t address, uint32_t value)
{
write_peripheral((DSPState *)opaque, address, value);
}
static void dsp_jit_reset(DSPState *dsp)
{
dsp56300_reset(jit_be(dsp)->jit);
}
static void dsp_jit_step(DSPState *dsp)
{
dsp56300_step(jit_be(dsp)->jit);
}
static void dsp_jit_run(DSPState *dsp, int cycles)
{
dsp56300_run(jit_be(dsp)->jit, cycles);
}
static void dsp_jit_bootstrap(DSPState *dsp)
{
JitBackend *be = jit_be(dsp);
/* Load scratch memory into PRAM (C-side owned buffer) */
dsp->dma.scratch_rw(dsp->dma.rw_opaque, (uint8_t *)be->pram, 0, 0x800 * 4,
false);
for (int i = 0; i < 0x800; i++) {
if (be->pram[i] & 0xff000000) {
DPRINTF("Bootstrap %04x: %08x\n", i, be->pram[i]);
be->pram[i] &= 0x00ffffff;
}
}
dsp56300_invalidate_cache(be->jit);
}
static uint32_t dsp_jit_read_memory(DSPState *dsp, char space, uint32_t addr)
{
Dsp56300MemSpace space_id = (space == 'X') ? DSP56300_MEM_SPACE_X :
(space == 'Y') ? DSP56300_MEM_SPACE_Y :
DSP56300_MEM_SPACE_P;
return dsp56300_read_memory(jit_be(dsp)->jit, space_id, addr);
}
static void dsp_jit_write_memory(DSPState *dsp, char space, uint32_t addr,
uint32_t value)
{
Dsp56300MemSpace space_id = (space == 'X') ? DSP56300_MEM_SPACE_X :
(space == 'Y') ? DSP56300_MEM_SPACE_Y :
DSP56300_MEM_SPACE_P;
dsp56300_write_memory(jit_be(dsp)->jit, space_id, addr, value);
}
static bool dsp_jit_get_halt_requested(DSPState *dsp)
{
return dsp56300_halt_requested(jit_be(dsp)->jit);
}
static void dsp_jit_set_halt_requested(DSPState *dsp, bool idle)
{
dsp56300_set_halt_requested(jit_be(dsp)->jit, idle);
}
static uint32_t dsp_jit_get_cycle_count(DSPState *dsp)
{
return dsp56300_cycle_count(jit_be(dsp)->jit);
}
static void dsp_jit_set_cycle_count(DSPState *dsp, uint32_t count)
{
dsp56300_set_cycle_count(jit_be(dsp)->jit, count);
}
static void dsp_jit_invalidate_opcache(DSPState *dsp)
{
dsp56300_invalidate_cache(jit_be(dsp)->jit);
}
/*
* Sync: JIT -> DspCoreState (before VM save / debug)
*/
static void dsp_jit_sync_to_vm(DSPState *dsp)
{
JitBackend *be = jit_be(dsp);
Dsp56300Jit *jit = be->jit;
DspCoreState *vm = &dsp->core;
/* Scalar state via bulk struct */
Dsp56300State ss;
dsp56300_get_state(jit, &ss);
vm->pc = ss.pc;
vm->cur_inst_len = ss.pc_advance;
vm->loop_rep = (uint32_t)ss.loop_rep;
vm->pc_on_rep = (uint32_t)ss.pc_on_rep;
vm->cycle_count = ss.cycle_count;
vm->halt_requested = ss.halt_requested;
vm->interrupt_state = ss.interrupts.state;
vm->interrupt_instr_fetch = (uint16_t)ss.interrupts.vector_addr;
vm->interrupt_save_pc = (uint16_t)ss.interrupts.saved_pc;
vm->interrupt_ipl_to_raise = ss.interrupts.ipl_to_raise;
vm->interrupt_pipeline_count = ss.interrupts.pipeline_stage;
vm->interrupt_counter = 0;
for (int i = 0; i < 4; i++) {
int slot = cinterp_to_arch[i];
vm->interrupt_ipl[i] = (int16_t)ss.interrupts.ipl[slot];
vm->interrupt_is_pending[i] = (ss.interrupts.pending_bits[slot / 64] >> (slot % 64)) & 1;
vm->interrupt_counter += vm->interrupt_is_pending[i];
}
dsp->save_cycles = ss.cycle_budget;
/* Register/stack arrays from bulk state */
memcpy(vm->registers, ss.registers, DSP_REG_MAX * sizeof(uint32_t));
memcpy(vm->stack[0], ss.stack[0], 16 * sizeof(uint32_t));
memcpy(vm->stack[1], ss.stack[1], 16 * sizeof(uint32_t));
/* Memory arrays from C-side owned buffers */
memcpy(vm->pram, be->pram, DSP_PRAM_SIZE * sizeof(uint32_t));
memcpy(vm->xram, be->xram, DSP_XRAM_SIZE * sizeof(uint32_t));
memcpy(vm->yram, be->yram, DSP_YRAM_SIZE * sizeof(uint32_t));
/* Mixbuffer is aliased at xram[0xC00] */
memcpy(vm->mixbuffer, be->xram + 0xC00,
DSP_MIXBUFFER_SIZE * sizeof(uint32_t));
/* Peripheral state: read current values via callbacks */
for (int i = 0; i < DSP_PERIPH_SIZE; i++) {
vm->periph[i] =
dsp56300_read_memory(jit, DSP56300_MEM_SPACE_X, 0xFFFF80 + i);
}
}
/*
* Sync: DspCoreState -> JIT (after VM load / engine switch)
*/
static void dsp_jit_sync_from_vm(DSPState *dsp)
{
JitBackend *be = jit_be(dsp);
Dsp56300Jit *jit = be->jit;
DspCoreState *vm = &dsp->core;
/* Memory arrays into C-side owned buffers */
memcpy(be->pram, vm->pram, DSP_PRAM_SIZE * sizeof(uint32_t));
memcpy(be->xram, vm->xram, DSP_XRAM_SIZE * sizeof(uint32_t));
memcpy(be->yram, vm->yram, DSP_YRAM_SIZE * sizeof(uint32_t));
/* Mixbuffer is aliased at xram[0xC00] */
memcpy(be->xram + 0xC00, vm->mixbuffer,
DSP_MIXBUFFER_SIZE * sizeof(uint32_t));
/* Scalar state via bulk struct */
Dsp56300State ss = {
.pc = vm->pc,
.pc_advance = vm->cur_inst_len,
.pc_on_rep = (bool)vm->pc_on_rep,
.cycle_count = vm->cycle_count,
.cycle_budget = dsp->save_cycles,
.loop_rep = (bool)vm->loop_rep,
.halt_requested = vm->halt_requested,
.power_state = 0,
.registers = {0},
.stack = {{0}},
.interrupts = {
.state = (uint8_t)vm->interrupt_state,
.vector_addr = vm->interrupt_instr_fetch,
.saved_pc = vm->interrupt_save_pc,
.ipl_to_raise = (uint8_t)vm->interrupt_ipl_to_raise,
.pipeline_stage = (uint8_t)vm->interrupt_pipeline_count,
},
};
ss.interrupts.pending_bits[0] = 0;
ss.interrupts.pending_bits[1] = 0;
for (int i = 0; i < 4; i++) {
int slot = cinterp_to_arch[i];
ss.interrupts.ipl[slot] = (int8_t)vm->interrupt_ipl[i];
ss.interrupts.pending_bits[slot / 64] |=
(uint64_t)(vm->interrupt_is_pending[i] != 0) << (slot % 64);
}
memcpy(ss.registers, vm->registers, DSP_REG_MAX * sizeof(uint32_t));
memcpy(ss.stack[0], vm->stack[0], 16 * sizeof(uint32_t));
memcpy(ss.stack[1], vm->stack[1], 16 * sizeof(uint32_t));
/* Widen legacy 16-bit register values from old snapshots.
* The C interpreter used 16-bit R/N/M/SSH/SSL/LA/LC registers.
* M: $FFFF meant linear addressing at 16-bit; JIT needs $FFFFFF.
* N: Signed offsets need sign extension from 16-bit to 24-bit.
*/
for (int i = 0; i < 8; i++) {
if (ss.registers[DSP_REG_M0 + i] == 0x00FFFF) {
ss.registers[DSP_REG_M0 + i] = 0x00FFFFFF;
}
if (ss.registers[DSP_REG_N0 + i] & 0x8000) {
ss.registers[DSP_REG_N0 + i] |= 0xFF0000;
}
}
dsp56300_set_state(jit, &ss);
dsp56300_invalidate_cache(jit);
}
static JitBackend *dsp_create_jit_backend(DSPState *dsp)
{
JitBackend *be = g_new0(JitBackend, 1);
/* Allocate memory buffers owned by the C side */
be->xram = g_new(uint32_t, DSP_XRAM_SIZE);
memset(be->xram, 0xCA, DSP_XRAM_SIZE * sizeof(uint32_t));
be->yram = g_new(uint32_t, DSP_YRAM_SIZE);
memset(be->yram, 0xCA, DSP_YRAM_SIZE * sizeof(uint32_t));
be->pram = g_new(uint32_t, DSP_PRAM_SIZE);
memset(be->pram, 0xCA, DSP_PRAM_SIZE * sizeof(uint32_t));
/* X-space: XRAM [0, 0x1000), mixbuf alias [0x1400, 0x1800), peripherals */
Dsp56300MemoryRegion x_regions[3] = {
{ .start = 0x0000,
.end = 0x1000,
.kind = DSP56300_REGION_BUFFER,
.data = { .buffer = { .base = be->xram, .offset = 0 } } },
{ .start = 0x1400,
.end = 0x1800,
.kind = DSP56300_REGION_BUFFER,
.data = { .buffer = { .base = be->xram, .offset = 0xC00 } } },
{ .start = 0xFFFF80,
.end = 0x1000000,
.kind = DSP56300_REGION_CALLBACK,
.data = { .callback = { .opaque = dsp,
.read = jit_read_peripheral,
.write = jit_write_peripheral } } },
};
/* Y-space: YRAM [0, 0x800) */
Dsp56300MemoryRegion y_regions[1] = {
{ .start = 0x0000,
.end = 0x0800,
.kind = DSP56300_REGION_BUFFER,
.data = { .buffer = { .base = be->yram, .offset = 0 } } },
};
/* P-space: PRAM [0, 0x1000) */
Dsp56300MemoryRegion p_regions[1] = {
{ .start = 0x0000,
.end = 0x1000,
.kind = DSP56300_REGION_BUFFER,
.data = { .buffer = { .base = be->pram, .offset = 0 } } },
};
Dsp56300CreateInfo info = {
.memory_map = {
.x_regions = x_regions,
.x_count = ARRAY_SIZE(x_regions),
.y_regions = y_regions,
.y_count = ARRAY_SIZE(y_regions),
.p_regions = p_regions,
.p_count = ARRAY_SIZE(p_regions),
},
};
be->jit = dsp56300_create(&info);
return be;
}
void dsp_jit_init(DSPState *dsp)
{
JitBackend *be = dsp_create_jit_backend(dsp);
dsp->backend = be;
dsp->ops = &jit_dsp_ops;
dsp->dma.mem_opaque = be->jit;
dsp->dma.mem_read = jit_dma_mem_read;
dsp->dma.mem_write = jit_dma_mem_write;
}
static void dsp_jit_finalize(DSPState *dsp)
{
JitBackend *be = jit_be(dsp);
dsp56300_destroy(be->jit);
g_free(be->xram);
g_free(be->yram);
g_free(be->pram);
g_free(be);
dsp->backend = NULL;
}
const DSPOps jit_dsp_ops = {
.finalize = dsp_jit_finalize,
.reset = dsp_jit_reset,
.step = dsp_jit_step,
.run = dsp_jit_run,
.bootstrap = dsp_jit_bootstrap,
.start_frame = dsp_start_frame_impl,
.read_memory = dsp_jit_read_memory,
.write_memory = dsp_jit_write_memory,
.get_halt_requested = dsp_jit_get_halt_requested,
.set_halt_requested = dsp_jit_set_halt_requested,
.get_cycle_count = dsp_jit_get_cycle_count,
.set_cycle_count = dsp_jit_set_cycle_count,
.invalidate_opcache = dsp_jit_invalidate_opcache,
.sync_to_vm = dsp_jit_sync_to_vm,
.sync_from_vm = dsp_jit_sync_from_vm,
};
-41
View File
@@ -1,41 +0,0 @@
/*
* MCPX DSP emulator
* Copyright (c) 2015 espes
* 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
*/
#ifndef DSP_STATE_H
#define DSP_STATE_H
#include "dsp_cpu.h"
#include "dsp_dma.h"
struct DSPState {
dsp_core_t core;
DSPDMAState dma;
int save_cycles;
uint32_t interrupts;
bool is_gp;
};
#endif /* DSP_STATE_H */
+31 -47
View File
@@ -25,23 +25,27 @@ static const int16_t ep_silence[256][2] = { 0 };
void mcpx_apu_update_dsp_preference(MCPXAPUState *d)
{
static int last_known_preference = -1;
static int last_known_dsp_pref = -1;
static int last_known_jit_pref = -1;
if (last_known_preference == (int)g_config.audio.use_dsp) {
return;
if (last_known_dsp_pref != (int)g_config.audio.use_dsp) {
if (g_config.audio.use_dsp) {
d->monitor.point = MCPX_APU_DEBUG_MON_GP_OR_EP;
d->gp.realtime = true;
d->ep.realtime = true;
} else {
d->monitor.point = MCPX_APU_DEBUG_MON_VP;
d->gp.realtime = false;
d->ep.realtime = false;
}
last_known_dsp_pref = g_config.audio.use_dsp;
}
if (g_config.audio.use_dsp) {
d->monitor.point = MCPX_APU_DEBUG_MON_GP_OR_EP;
d->gp.realtime = true;
d->ep.realtime = true;
} else {
d->monitor.point = MCPX_APU_DEBUG_MON_VP;
d->gp.realtime = false;
d->ep.realtime = false;
if (last_known_jit_pref != (int)g_config.audio.use_dsp_jit) {
dsp_set_engine(d->gp.dsp, g_config.audio.use_dsp_jit);
dsp_set_engine(d->ep.dsp, g_config.audio.use_dsp_jit);
last_known_jit_pref = g_config.audio.use_dsp_jit;
}
last_known_preference = g_config.audio.use_dsp;
}
static void scatter_gather_rw(MCPXAPUState *d, hwaddr sge_base,
@@ -454,12 +458,12 @@ void mcpx_apu_dsp_frame(MCPXAPUState *d, float mixbins[NUM_MIXBINS][NUM_SAMPLES_
if ((d->gp.regs[NV_PAPU_GPRST] & NV_PAPU_GPRST_GPRST) &&
(d->gp.regs[NV_PAPU_GPRST] & NV_PAPU_GPRST_GPDSPRST)) {
dsp_start_frame(d->gp.dsp);
d->gp.dsp->core.is_idle = false;
d->gp.dsp->core.cycle_count = 0;
dsp_set_halt_requested(d->gp.dsp, false);
dsp_set_cycle_count(d->gp.dsp, 0);
do {
dsp_run(d->gp.dsp, 1000);
} while (!d->gp.dsp->core.is_idle && d->gp.realtime);
g_dbg.gp.cycles = d->gp.dsp->core.cycle_count;
} while (!dsp_get_halt_requested(d->gp.dsp) && d->gp.realtime);
g_dbg.gp.cycles = dsp_get_cycle_count(d->gp.dsp);
if ((d->monitor.point == MCPX_APU_DEBUG_MON_GP) ||
(d->monitor.point == MCPX_APU_DEBUG_MON_GP_OR_EP && !ep_enabled)) {
@@ -479,45 +483,25 @@ void mcpx_apu_dsp_frame(MCPXAPUState *d, float mixbins[NUM_MIXBINS][NUM_SAMPLES_
(d->ep.regs[NV_PAPU_EPRST] & NV_PAPU_GPRST_GPDSPRST)) {
if (d->ep_frame_div % 8 == 0) {
dsp_start_frame(d->ep.dsp);
d->ep.dsp->core.is_idle = false;
d->ep.dsp->core.cycle_count = 0;
dsp_set_halt_requested(d->ep.dsp, false);
dsp_set_cycle_count(d->ep.dsp, 0);
do {
dsp_run(d->ep.dsp, 1000);
} while (!d->ep.dsp->core.is_idle && d->ep.realtime);
g_dbg.ep.cycles = d->ep.dsp->core.cycle_count;
} while (!dsp_get_halt_requested(d->ep.dsp) && d->ep.realtime);
g_dbg.ep.cycles = dsp_get_cycle_count(d->ep.dsp);
}
}
}
void mcpx_apu_dsp_init(MCPXAPUState *d)
{
d->gp.dsp = dsp_init(d, gp_scratch_rw, gp_fifo_rw);
for (int i = 0; i < DSP_PRAM_SIZE; i++) {
d->gp.dsp->core.pram[i] = 0xCACACACA;
}
memset(d->gp.dsp->core.pram_opcache, 0,
sizeof(d->gp.dsp->core.pram_opcache));
d->gp.dsp->is_gp = true;
d->gp.dsp->core.is_gp = true;
d->gp.dsp->core.is_idle = false;
d->gp.dsp->core.cycle_count = 0;
d->gp.dsp = dsp_init(d, gp_scratch_rw, gp_fifo_rw, true);
dsp_set_halt_requested(d->gp.dsp, false);
dsp_set_cycle_count(d->gp.dsp, 0);
d->ep.dsp = dsp_init(d, ep_scratch_rw, ep_fifo_rw);
for (int i = 0; i < DSP_PRAM_SIZE; i++) {
d->ep.dsp->core.pram[i] = 0xCACACACA;
}
memset(d->ep.dsp->core.pram_opcache, 0,
sizeof(d->ep.dsp->core.pram_opcache));
for (int i = 0; i < DSP_XRAM_SIZE; i++) {
d->ep.dsp->core.xram[i] = 0xCACACACA;
}
for (int i = 0; i < DSP_YRAM_SIZE; i++) {
d->ep.dsp->core.yram[i] = 0xCACACACA;
}
d->ep.dsp->is_gp = false;
d->ep.dsp->core.is_gp = false;
d->ep.dsp->core.is_idle = false;
d->ep.dsp->core.cycle_count = 0;
d->ep.dsp = dsp_init(d, ep_scratch_rw, ep_fifo_rw, false);
dsp_set_halt_requested(d->ep.dsp, false);
dsp_set_cycle_count(d->ep.dsp, 0);
/* Until DSP is more performant, a switch to decide whether or not we should
* use the full audio pipeline or not.
-3
View File
@@ -27,9 +27,6 @@
#include "hw/xbox/mcpx/apu/apu_regs.h"
#include "dsp.h"
#include "dsp_dma.h"
#include "dsp_cpu.h"
#include "dsp_state.h"
typedef struct MCPXAPUState MCPXAPUState;
+293
View File
@@ -0,0 +1,293 @@
/*
* 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 <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include "dsp_cpu.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(dsp_core_t* dsp, uint32_t dsp_memdump_addr, uint32_t dsp_memdump_upper, int space)
{
uint32_t mem, value;
for (mem = dsp_memdump_addr; mem <= dsp_memdump_upper; mem++) {
value = dsp56k_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(dsp_core_t* 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->stack[0]); j++) {
printf(" %04x", dsp->stack[i][j]);
}
printf("\n");
}
printf("- Interrupt IPL:");
for (i = 0; i < ARRAY_SIZE(dsp->interrupt_ipl); i++) {
printf(" %04x", dsp->interrupt_ipl[i]);
}
printf("\n");
printf("- Pending ints: ");
for (i = 0; i < ARRAY_SIZE(dsp->interrupt_is_pending); i++) {
printf(" %04hx", dsp->interrupt_is_pending[i]);
}
printf("\n");
}
/**
* Show DSP register contents
*/
void dsp_print_registers(dsp_core_t* dsp)
{
uint32_t i;
printf("A: A2: %02x A1: %06x A0: %06x\n",
dsp->registers[DSP_REG_A2], dsp->registers[DSP_REG_A1], dsp->registers[DSP_REG_A0]);
printf("B: B2: %02x B1: %06x B0: %06x\n",
dsp->registers[DSP_REG_B2], dsp->registers[DSP_REG_B1], dsp->registers[DSP_REG_B0]);
printf("X: X1: %06x X0: %06x\n", dsp->registers[DSP_REG_X1], dsp->registers[DSP_REG_X0]);
printf("Y: Y1: %06x Y0: %06x\n", dsp->registers[DSP_REG_Y1], dsp->registers[DSP_REG_Y0]);
for (i=0; i<8; i++) {
printf("R%01x: %04x N%01x: %04x M%01x: %04x\n",
i, dsp->registers[DSP_REG_R0+i],
i, dsp->registers[DSP_REG_N0+i],
i, dsp->registers[DSP_REG_M0+i]);
}
printf("LA: %04x LC: %04x PC: %04x\n", dsp->registers[DSP_REG_LA], dsp->registers[DSP_REG_LC], dsp->pc);
printf("SR: %04x OMR: %02x\n", dsp->registers[DSP_REG_SR], dsp->registers[DSP_REG_OMR]);
printf("SP: %02x SSH: %04x SSL: %04x\n",
dsp->registers[DSP_REG_SP], dsp->registers[DSP_REG_SSH], dsp->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(dsp_core_t* 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->registers[DSP_REG_A0], 32, BITMASK(24) },
{ "A1", &dsp->registers[DSP_REG_A1], 32, BITMASK(24) },
{ "A2", &dsp->registers[DSP_REG_A2], 32, BITMASK(8) },
/* 56-bit B register */
{ "B0", &dsp->registers[DSP_REG_B0], 32, BITMASK(24) },
{ "B1", &dsp->registers[DSP_REG_B1], 32, BITMASK(24) },
{ "B2", &dsp->registers[DSP_REG_B2], 32, BITMASK(8) },
/* 16-bit LA & LC registers */
{ "LA", &dsp->registers[DSP_REG_LA], 32, BITMASK(16) },
{ "LC", &dsp->registers[DSP_REG_LC], 32, BITMASK(16) },
/* 16-bit M registers */
{ "M0", &dsp->registers[DSP_REG_M0], 32, BITMASK(16) },
{ "M1", &dsp->registers[DSP_REG_M1], 32, BITMASK(16) },
{ "M2", &dsp->registers[DSP_REG_M2], 32, BITMASK(16) },
{ "M3", &dsp->registers[DSP_REG_M3], 32, BITMASK(16) },
{ "M4", &dsp->registers[DSP_REG_M4], 32, BITMASK(16) },
{ "M5", &dsp->registers[DSP_REG_M5], 32, BITMASK(16) },
{ "M6", &dsp->registers[DSP_REG_M6], 32, BITMASK(16) },
{ "M7", &dsp->registers[DSP_REG_M7], 32, BITMASK(16) },
/* 16-bit N registers */
{ "N0", &dsp->registers[DSP_REG_N0], 32, BITMASK(16) },
{ "N1", &dsp->registers[DSP_REG_N1], 32, BITMASK(16) },
{ "N2", &dsp->registers[DSP_REG_N2], 32, BITMASK(16) },
{ "N3", &dsp->registers[DSP_REG_N3], 32, BITMASK(16) },
{ "N4", &dsp->registers[DSP_REG_N4], 32, BITMASK(16) },
{ "N5", &dsp->registers[DSP_REG_N5], 32, BITMASK(16) },
{ "N6", &dsp->registers[DSP_REG_N6], 32, BITMASK(16) },
{ "N7", &dsp->registers[DSP_REG_N7], 32, BITMASK(16) },
{ "OMR", &dsp->registers[DSP_REG_OMR], 32, 0x5f },
/* 16-bit program counter */
{ "PC", (uint32_t*)(&dsp->pc), 24, BITMASK(24) },
/* 16-bit DSP R (address) registers */
{ "R0", &dsp->registers[DSP_REG_R0], 32, BITMASK(16) },
{ "R1", &dsp->registers[DSP_REG_R1], 32, BITMASK(16) },
{ "R2", &dsp->registers[DSP_REG_R2], 32, BITMASK(16) },
{ "R3", &dsp->registers[DSP_REG_R3], 32, BITMASK(16) },
{ "R4", &dsp->registers[DSP_REG_R4], 32, BITMASK(16) },
{ "R5", &dsp->registers[DSP_REG_R5], 32, BITMASK(16) },
{ "R6", &dsp->registers[DSP_REG_R6], 32, BITMASK(16) },
{ "R7", &dsp->registers[DSP_REG_R7], 32, BITMASK(16) },
{ "SSH", &dsp->registers[DSP_REG_SSH], 32, BITMASK(16) },
{ "SSL", &dsp->registers[DSP_REG_SSL], 32, BITMASK(16) },
{ "SP", &dsp->registers[DSP_REG_SP], 32, BITMASK(6) },
/* 16-bit status register */
{ "SR", &dsp->registers[DSP_REG_SR], 32, 0xefff },
/* 48-bit X register */
{ "X0", &dsp->registers[DSP_REG_X0], 32, BITMASK(24) },
{ "X1", &dsp->registers[DSP_REG_X1], 32, BITMASK(24) },
/* 48-bit Y register */
{ "Y0", &dsp->registers[DSP_REG_Y0], 32, BITMASK(24) },
{ "Y1", &dsp->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(dsp_core_t* 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->registers[DSP_REG_SP] = value & BITMASK(6);
value &= BITMASK(4);
dsp->registers[DSP_REG_SSH] = dsp->stack[0][value];
dsp->registers[DSP_REG_SSL] = dsp->stack[1][value];
return true;
}
if (arg[1]=='S' || arg[1]=='s') {
sp_value = dsp->registers[DSP_REG_SP] & BITMASK(4);
if (arg[2]=='H' || arg[2]=='h') {
if (sp_value == 0) {
dsp->registers[DSP_REG_SSH] = 0;
dsp->stack[0][sp_value] = 0;
} else {
dsp->registers[DSP_REG_SSH] = value & BITMASK(16);
dsp->stack[0][sp_value] = value & BITMASK(16);
}
return true;
}
if (arg[2]=='L' || arg[2]=='l') {
if (sp_value == 0) {
dsp->registers[DSP_REG_SSL] = 0;
dsp->stack[1][sp_value] = 0;
} else {
dsp->registers[DSP_REG_SSL] = value & BITMASK(16);
dsp->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
@@ -31,6 +31,10 @@
#define BITMASK(x) ((1<<(x))-1)
#define TRACE_DSP_DISASM 0
#define TRACE_DSP_DISASM_REG 0
#define TRACE_DSP_DISASM_MEM 0
// #define DSP_COUNT_IPS /* Count instruction per seconds */
@@ -70,19 +74,12 @@ static void dsp_mul56(uint32_t source1, uint32_t source2, uint32_t *dest, uint8_
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] = {
/* Vector addresses per DSP56300FM Table 2-2, indexed by DSP_INTER_* */
static const dsp_interrupt_t dsp_interrupt[4] = {
{ DSP_INTER_RESET, 0x00, 0, "Reset" },
{ DSP_INTER_ILLEGAL, 0x3e, 0, "Illegal" },
{ DSP_INTER_ILLEGAL, 0x04, 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" }
{ DSP_INTER_TRAP, 0x08, 0, "Trap" },
};
static const int registers_tcc[16][2] = {
@@ -392,12 +389,9 @@ void dsp56k_reset_cpu(dsp_core_t* dsp)
dsp->interrupt_save_pc = -1;
dsp->interrupt_counter = 0;
dsp->interrupt_pipeline_count = 0;
for (i=0;i<5;i++) {
for (i=0;i<4;i++) {
dsp->interrupt_ipl[i] = 3;
}
for (i=5;i<12;i++) {
dsp->interrupt_ipl[i] = -1;
}
/* Misc */
dsp->loop_rep = 0;
@@ -783,7 +777,7 @@ static void dsp_postexecute_interrupts(dsp_core_t* dsp)
if ( ((instr & 0xfff000) == 0x0d0000) || ((instr & 0xffc0ff) == 0x0bc080) ) {
dsp->interrupt_state = DSP_INTERRUPT_LONG;
dsp_stack_push(dsp, dsp->interrupt_save_pc, dsp->registers[DSP_REG_SR], 0);
dsp->registers[DSP_REG_SR] &= BITMASK(16)-((1<<DSP_SR_LF)|(1<<DSP_SR_T) |
dsp->registers[DSP_REG_SR] &= BITMASK(16)-((1<<DSP_SR_LF)|(1<<DSP_SR_FV) |
(1<<DSP_SR_S1)|(1<<DSP_SR_S0) |
(1<<DSP_SR_I0)|(1<<DSP_SR_I1));
dsp->registers[DSP_REG_SR] |= dsp->interrupt_ipl_to_raise<<DSP_SR_I0;
@@ -797,7 +791,7 @@ static void dsp_postexecute_interrupts(dsp_core_t* dsp)
if ( ((instr & 0xfff000) == 0x0d0000) || ((instr & 0xffc0ff) == 0x0bc080) ) {
dsp->interrupt_state = DSP_INTERRUPT_LONG;
dsp_stack_push(dsp, dsp->interrupt_save_pc, dsp->registers[DSP_REG_SR], 0);
dsp->registers[DSP_REG_SR] &= BITMASK(16)-((1<<DSP_SR_LF)|(1<<DSP_SR_T) |
dsp->registers[DSP_REG_SR] &= BITMASK(16)-((1<<DSP_SR_LF)|(1<<DSP_SR_FV) |
(1<<DSP_SR_S1)|(1<<DSP_SR_S0) |
(1<<DSP_SR_I0)|(1<<DSP_SR_I1));
dsp->registers[DSP_REG_SR] |= dsp->interrupt_ipl_to_raise<<DSP_SR_I0;
@@ -829,11 +823,6 @@ static void dsp_postexecute_interrupts(dsp_core_t* dsp)
}
}
/* Trace Interrupt ? */
if (dsp->registers[DSP_REG_SR] & (1<<DSP_SR_T)) {
dsp56k_add_interrupt(dsp, DSP_INTER_TRACE);
}
/* No interrupt to execute */
if (dsp->interrupt_counter == 0) {
return;
@@ -845,7 +834,7 @@ static void dsp_postexecute_interrupts(dsp_core_t* dsp)
ipl_to_raise = -1;
/* Arbitrate between all pending interrupts */
for (i=0; i<12; i++) {
for (i=0; i<4; i++) {
if (dsp->interrupt_is_pending[i] == 1) {
/* level 3 interrupt ? */
@@ -890,29 +879,6 @@ static void dsp_postexecute_interrupts(dsp_core_t* dsp)
dsp->interrupt_ipl_to_raise = ipl_to_raise;
DPRINTF("Dsp interrupt: %s\n", dsp_interrupt[index].name);
/* SSI receive data with exception ? */
if (dsp->interrupt_instr_fetch == 0xe) {
// dsp->periph[DSP_SPACE_X][DSP_SSI_SR] &= 0xff-(1<<DSP_SSI_SR_ROE);
assert(!"SSI receive data failed");
}
/* SSI transmit data with exception ? */
else if (dsp->interrupt_instr_fetch == 0x12) {
// dsp->periph[DSP_SPACE_X][DSP_SSI_SR] &= 0xff-(1<<DSP_SSI_SR_TUE);
assert(!"SSI transmit data failed");
}
/* host command ? */
else if (dsp->interrupt_instr_fetch == 0xff) {
/* Clear HC and HCP interrupt */
// dsp->periph[DSP_SPACE_X][DSP_HOST_HSR] &= 0xff - (1<<DSP_HOST_HSR_HCP);
// dsp->hostport[CPU_HOST_CVR] &= 0xff - (1<<CPU_HOST_CVR_HC);
// dsp->interrupt_instr_fetch = dsp->hostport[CPU_HOST_CVR] & BITMASK(5);
// dsp->interrupt_instr_fetch *= 2;
assert(!"Host command failure");
}
}
/**********************************
@@ -81,8 +81,11 @@ struct dsp_core_s {
uint16_t interrupt_counter; /* count number of pending interrupts */
uint16_t interrupt_ipl_to_raise; /* save the IPL level to save in the SR register */
uint16_t interrupt_pipeline_count; /* used to prefetch correctly the 2 inter instructions */
int16_t interrupt_ipl[12]; /* store the current IPL for each interrupt */
uint16_t interrupt_is_pending[12]; /* store if interrupt is pending for each interrupt */
int16_t interrupt_ipl[4];
uint16_t interrupt_is_pending[4];
/* Back-pointer to owning DSPState (set by dsp_c.c) */
void *opaque;
/* callbacks */
uint32_t (*read_peripheral)(dsp_core_t* core, uint32_t address);
@@ -43,8 +43,10 @@
#define DSP_SR_I1 0x09
#define DSP_SR_S0 0x0a
#define DSP_SR_S1 0x0b
#define DSP_SR_T 0x0d
#define DSP_SR_SC 0x0d
#define DSP_SR_DM 0x0e
#define DSP_SR_LF 0x0f
#define DSP_SR_FV 0x10
#define DSP_SP_SE 0x04
#define DSP_SP_UF 0x05
@@ -122,17 +124,9 @@
#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
#define DSP_INTER_RESET 0
#define DSP_INTER_ILLEGAL 1
#define DSP_INTER_STACK_ERROR 2
#define DSP_INTER_TRAP 3
#endif
@@ -1331,7 +1331,7 @@ static void dis_lua(dsp_core_t* dsp)
char addr_name[12];
dis_calc_ea(dsp, (dsp->disasm_cur_inst>>8) & BITMASK(5), addr_name);
uint32_t numreg = dsp->disasm_cur_inst & BITMASK(4);
uint32_t numreg = dsp->disasm_cur_inst & BITMASK(5);
sprintf(dsp->disasm_str_instr,"lua %s,%s", addr_name, registers_name[numreg]);
}
+5
View File
@@ -0,0 +1,5 @@
libdsp_cpu_interp = static_library(
'dsp_cpu_interp',
files(['dsp_cpu.c', 'debug.c']) + genh,
include_directories: include_directories('..'),
)
+17 -2
View File
@@ -1,4 +1,19 @@
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))
dsp56300_emu_ffi = dependency('dsp56300-emu-ffi', fallback: ['dsp56300', 'dsp56300_dep'])
subdir('interp')
libdsp = static_library(
'dsp',
files(['dsp.c', 'dsp_c.c', 'dsp_jit.c', 'dsp_dma.c']) + genh,
dependencies: [dsp56300_emu_ffi],
)
dsp = declare_dependency(
objects: [
libdsp.extract_all_objects(recursive: false),
libdsp_cpu_interp.extract_all_objects(recursive: false),
],
dependencies: [dsp56300_emu_ffi],
)
mcpx_ss.add(dsp, files('gp_ep.c'))
File diff suppressed because it is too large Load Diff
+11
View File
@@ -122,4 +122,15 @@ for sp in "${subprojects[@]}"; do
tar --append --file "$tar_file" --exclude=.git --transform "s,^./,$tar_prefix," ./subprojects/"$(subproject_dir $sp)"
test $? -ne 0 && error "failed to append subproject $sp to $tar_file"
done
# Pre-download dsp56300 pre-built libraries for offline builds (e.g. PPA).
dsp56300_version=$(sed -n "s/^project.*version: *'\([^']*\)'.*/\1/p" subprojects/dsp56300/meson.build)
for target in x86_64-unknown-linux-gnu aarch64-unknown-linux-gnu; do
archive="dsp56300-${dsp56300_version}-${target}.tar.gz"
curl -fsSL "https://github.com/mborgerson/dsp56300/releases/download/v${dsp56300_version}/${archive}" \
-o "subprojects/dsp56300/${archive}"
tar --append --file "$tar_file" --transform "s,^./,$tar_prefix," "./subprojects/dsp56300/${archive}"
rm "subprojects/dsp56300/${archive}"
done
exit 0
+11
View File
@@ -510,6 +510,17 @@ LIBS = [
platform={windows},
submodule=Submodule("subprojects/curl.wrap"),
),
Lib(
"dsp56300",
"https://github.com/mborgerson/dsp56300",
mit,
# Note: This URL points to the dsp56300 license, but the distributed library includes
# additional dependencies. The license file at licenses/dsp56300.license.txt contains
# the dsp56300 license as well as its dependencies.
"https://raw.githubusercontent.com/mborgerson/dsp56300/refs/heads/main/LICENSE",
ships_static=all_platforms,
version="0.1.3",
),
]
+2
View File
@@ -39,3 +39,5 @@ genconfig
curl-*
json-*
SDL3-*
/dsp56300/*
!/dsp56300/meson.build
+48
View File
@@ -0,0 +1,48 @@
project('dsp56300', version: '0.1.3')
fs = import('fs')
host_cpu = host_machine.cpu_family()
host_os = host_machine.system()
if host_os == 'darwin'
os_suffix = 'apple-darwin'
elif host_os == 'windows'
os_suffix = (host_cpu == 'aarch64') ? 'pc-windows-gnullvm' : 'pc-windows-gnu'
elif host_os == 'linux'
os_suffix = 'unknown-linux-gnu'
else
error('Unsupported host OS: ' + host_os)
endif
rust_target = host_cpu + '-' + os_suffix
ver = meson.project_version()
prefix = 'dsp56300-@0@-@1@'.format(ver, rust_target)
archive = '@0@.tar.gz'.format(prefix)
url = 'https://github.com/mborgerson/dsp56300/releases/download/v@0@/@1@'.format(ver, archive)
lib = meson.current_build_dir() / prefix / 'lib' / 'libdsp56300_emu_ffi.a'
if not fs.exists(lib)
src_archive = meson.current_source_dir() / archive
if not fs.exists(src_archive)
message('Downloading dsp56300 @0@ for @1@...'.format(ver, rust_target))
run_command(find_program('curl'), '-fsSL', url, '-o', src_archive, check: true)
endif
run_command('tar', 'xzf', src_archive, '-C', meson.current_build_dir(), check: true)
endif
link_args = []
if host_os == 'linux'
link_args = ['-lpthread', '-ldl', '-lm']
elif host_os == 'windows'
link_args = ['-lbcrypt', '-lntdll', '-luserenv', '-lws2_32']
elif host_os == 'darwin'
link_args = ['-lpthread', '-lm', '-framework', 'Security']
endif
dsp56300_dep = declare_dependency(
link_args: [lib] + link_args,
include_directories: include_directories(prefix / 'include'),
)
meson.override_dependency('dsp56300-emu-ffi', dsp56300_dep)
+2
View File
@@ -207,6 +207,8 @@ void DebugApuWindow::Draw()
mcpx_apu_debug_set_monitor((McpxApuDebugMonitorPoint)mon);
}
ImGui::Checkbox("JIT Engine\n", &g_config.audio.use_dsp_jit);
static bool gp_realtime;
gp_realtime = dbg->gp_realtime;
if (ImGui::Checkbox("GP Realtime\n", &gp_realtime)) {
+2
View File
@@ -846,6 +846,8 @@ void MainMenuAudioView::Draw()
SectionTitle("Quality");
Toggle("Real-time DSP processing", &g_config.audio.use_dsp,
"Enable improved audio accuracy (experimental)");
Toggle("DSP JIT engine", &g_config.audio.use_dsp_jit,
"Use DSP JIT engine");
}