mirror of
https://github.com/xemu-project/xemu.git
synced 2026-07-11 01:24:41 +02:00
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:
committed by
mborgerson
parent
a34bb0f8c2
commit
67cc79e663
@@ -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
@@ -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),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
@@ -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__); \
|
||||
|
||||
+122
-134
@@ -24,12 +24,9 @@
|
||||
*/
|
||||
|
||||
#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)
|
||||
@@ -38,45 +35,14 @@
|
||||
#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) {
|
||||
case 0xFFFFB3:
|
||||
@@ -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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
DSPState *dsp_init(void *rw_opaque, dsp_scratch_rw_func scratch_rw,
|
||||
dsp_fifo_rw_func fifo_rw, bool is_gp)
|
||||
{
|
||||
DSPState *dsp = g_new0(DSPState, 1);
|
||||
dsp->is_gp = is_gp;
|
||||
dsp->core.is_gp = is_gp;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
dsp_reset(dsp);
|
||||
|
||||
return dsp;
|
||||
}
|
||||
|
||||
void dsp_destroy(DSPState *dsp)
|
||||
{
|
||||
dsp->ops->finalize(dsp);
|
||||
g_free(dsp);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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 dsp->ops->read_memory(dsp, space, address);
|
||||
}
|
||||
|
||||
return dsp56k_read_memory(&dsp->core, space_id, address);
|
||||
}
|
||||
|
||||
void dsp_write_memory(DSPState* dsp, char space, uint32_t address, uint32_t value)
|
||||
void dsp_write_memory(DSPState *dsp, char space, uint32_t address,
|
||||
uint32_t value)
|
||||
{
|
||||
int space_id;
|
||||
dsp->ops->write_memory(dsp, space, address, value);
|
||||
}
|
||||
|
||||
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");
|
||||
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);
|
||||
}
|
||||
|
||||
+98
-20
@@ -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,18 +27,88 @@
|
||||
|
||||
#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);
|
||||
/*
|
||||
* 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];
|
||||
|
||||
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];
|
||||
|
||||
uint32_t cycle_count;
|
||||
uint16_t instr_cycle;
|
||||
bool halt_requested;
|
||||
bool is_gp;
|
||||
|
||||
uint32_t loop_rep;
|
||||
uint32_t pc_on_rep;
|
||||
uint32_t cur_inst;
|
||||
uint32_t cur_inst_len;
|
||||
|
||||
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);
|
||||
|
||||
@@ -49,13 +119,21 @@ 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);
|
||||
void dsp_write_memory(DSPState *dsp, char space, uint32_t address,
|
||||
uint32_t value);
|
||||
|
||||
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);
|
||||
/* 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 */
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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 */
|
||||
@@ -25,12 +25,10 @@ static const int16_t ep_silence[256][2] = { 0 };
|
||||
|
||||
void mcpx_apu_update_dsp_preference(MCPXAPUState *d)
|
||||
{
|
||||
static int last_known_preference = -1;
|
||||
|
||||
if (last_known_preference == (int)g_config.audio.use_dsp) {
|
||||
return;
|
||||
}
|
||||
static int last_known_dsp_pref = -1;
|
||||
static int last_known_jit_pref = -1;
|
||||
|
||||
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;
|
||||
@@ -40,8 +38,14 @@ void mcpx_apu_update_dsp_preference(MCPXAPUState *d)
|
||||
d->gp.realtime = false;
|
||||
d->ep.realtime = false;
|
||||
}
|
||||
last_known_dsp_pref = g_config.audio.use_dsp;
|
||||
}
|
||||
|
||||
last_known_preference = g_config.audio.use_dsp;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
libdsp_cpu_interp = static_library(
|
||||
'dsp_cpu_interp',
|
||||
files(['dsp_cpu.c', 'debug.c']) + genh,
|
||||
include_directories: include_directories('..'),
|
||||
)
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -39,3 +39,5 @@ genconfig
|
||||
curl-*
|
||||
json-*
|
||||
SDL3-*
|
||||
/dsp56300/*
|
||||
!/dsp56300/meson.build
|
||||
|
||||
@@ -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)
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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");
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user