mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
Add hw/xbox sources from XQEMU 1.x @ 4d9107e
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
obj-y += xbox.o chihiro.o
|
||||
obj-y += xbox_pci.o acpi_xbox.o
|
||||
obj-y += amd_smbus.o smbus_xbox_smc.o smbus_cx25871.o smbus_adm1032.o
|
||||
obj-y += nvnet.o
|
||||
obj-y += nv2a.o nv2a_vsh.o nv2a_psh.o nv2a_shaders.o nv2a_debug.o
|
||||
obj-y += swizzle.o g-lru-cache.o
|
||||
obj-y += mcpx_apu.o mcpx_aci.o
|
||||
obj-y += lpc47m157.o
|
||||
obj-y += xid.o
|
||||
obj-y += chihiro-usb.o
|
||||
|
||||
obj-y += dsp/
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* Xbox ACPI implementation
|
||||
*
|
||||
* Copyright (c) 2012 espes
|
||||
*
|
||||
* Based on acpi.c, acpi_ich9.c, acpi_piix4.c
|
||||
* Copyright (c) 2006 Fabrice Bellard
|
||||
* Copyright (c) 2009 Isaku Yamahata <yamahata at valinux co jp>
|
||||
* VA Linux Systems Japan K.K.
|
||||
* Copyright (c) 2012 Jason Baron <jbaron@redhat.com>
|
||||
*
|
||||
* 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 or
|
||||
* (at your option) version 3 of the License.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "qemu/timer.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "hw/acpi/acpi.h"
|
||||
#include "hw/xbox/xbox_pci.h"
|
||||
|
||||
#include "hw/xbox/acpi_xbox.h"
|
||||
|
||||
#define DEBUG
|
||||
#ifdef DEBUG
|
||||
# define XBOX_DPRINTF(format, ...) printf(format, ## __VA_ARGS__)
|
||||
#else
|
||||
# define XBOX_DPRINTF(format, ...) do { } while (0)
|
||||
#endif
|
||||
|
||||
#define XBOX_PM_GPIO_BASE 0xC0
|
||||
#define XBOX_PM_GPIO_LEN 26
|
||||
|
||||
static int field_pin = 0;
|
||||
|
||||
static uint64_t xbox_pm_gpio_read(void *opaque, hwaddr addr, unsigned width)
|
||||
{
|
||||
uint64_t r = 0;
|
||||
switch (addr) {
|
||||
case 0:
|
||||
// field pin from tv encoder?
|
||||
field_pin = (field_pin+1)&1;
|
||||
r = field_pin << 5;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
XBOX_DPRINTF("pm gpio read [0x%llx] -> 0x%llx\n", addr, r);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void xbox_pm_gpio_write(void *opaque, hwaddr addr, uint64_t val,
|
||||
unsigned width)
|
||||
{
|
||||
XBOX_DPRINTF("pm gpio write [0x%llx] = 0x%llx\n", addr, val);
|
||||
}
|
||||
|
||||
static const MemoryRegionOps xbox_pm_gpio_ops = {
|
||||
.read = xbox_pm_gpio_read,
|
||||
.write = xbox_pm_gpio_write,
|
||||
};
|
||||
|
||||
static void pm_update_sci(XBOX_PMRegs *pm)
|
||||
{
|
||||
int sci_level, pm1a_sts;
|
||||
|
||||
pm1a_sts = acpi_pm1_evt_get_sts(&pm->acpi_regs);
|
||||
|
||||
sci_level = (((pm1a_sts & pm->acpi_regs.pm1.evt.en) &
|
||||
(ACPI_BITMASK_RT_CLOCK_ENABLE |
|
||||
ACPI_BITMASK_POWER_BUTTON_ENABLE |
|
||||
ACPI_BITMASK_GLOBAL_LOCK_ENABLE |
|
||||
ACPI_BITMASK_TIMER_ENABLE)) != 0);
|
||||
qemu_set_irq(pm->irq, sci_level);
|
||||
|
||||
/* schedule a timer interruption if needed */
|
||||
acpi_pm_tmr_update(&pm->acpi_regs,
|
||||
(pm->acpi_regs.pm1.evt.en & ACPI_BITMASK_TIMER_ENABLE) &&
|
||||
!(pm1a_sts & ACPI_BITMASK_TIMER_STATUS));
|
||||
}
|
||||
|
||||
static void xbox_pm_update_sci_fn(ACPIREGS *regs)
|
||||
{
|
||||
XBOX_PMRegs *pm = container_of(regs, XBOX_PMRegs, acpi_regs);
|
||||
pm_update_sci(pm);
|
||||
}
|
||||
|
||||
#define XBOX_PM_BASE_BAR 0
|
||||
|
||||
void xbox_pm_init(PCIDevice *dev, XBOX_PMRegs *pm, qemu_irq sci_irq) {
|
||||
|
||||
memory_region_init(&pm->io, OBJECT(dev), "xbox-pm", 256);
|
||||
|
||||
pci_register_bar(dev, XBOX_PM_BASE_BAR, PCI_BASE_ADDRESS_SPACE_IO,
|
||||
&pm->io);
|
||||
|
||||
acpi_pm_tmr_init(&pm->acpi_regs, xbox_pm_update_sci_fn, &pm->io);
|
||||
acpi_pm1_evt_init(&pm->acpi_regs, xbox_pm_update_sci_fn, &pm->io);
|
||||
acpi_pm1_cnt_init(&pm->acpi_regs, &pm->io, 2);
|
||||
|
||||
memory_region_init_io(&pm->io_gpio, OBJECT(dev), &xbox_pm_gpio_ops, pm,
|
||||
"xbox-pm-gpio", XBOX_PM_GPIO_LEN);
|
||||
memory_region_add_subregion(&pm->io, XBOX_PM_GPIO_BASE, &pm->io_gpio);
|
||||
|
||||
pm->irq = sci_irq;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* QEMU Xbox PM Emulation
|
||||
*
|
||||
* Copyright (c) 2012 espes
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef HW_ACPI_XBOX_H
|
||||
#define HW_ACPI_XBOX_H
|
||||
|
||||
#include "hw/acpi/acpi.h"
|
||||
|
||||
typedef struct XBOX_PMRegs {
|
||||
ACPIREGS acpi_regs;
|
||||
|
||||
MemoryRegion io;
|
||||
MemoryRegion io_gpio;
|
||||
|
||||
qemu_irq irq;
|
||||
} XBOX_PMRegs;
|
||||
|
||||
void xbox_pm_init(PCIDevice *dev, XBOX_PMRegs *pm, qemu_irq sci_irq);
|
||||
//void xbox_pm_iospace_update(MCPX_PMRegs *pm, uint32_t pm_io_base);
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* AMD756 SMBus implementation
|
||||
*
|
||||
* Copyright (C) 2012 espes
|
||||
*
|
||||
* Based on pm_smbus.c
|
||||
* Copyright (c) 2006 Fabrice Bellard
|
||||
* Based on Linux drivers/i2c/busses/i2c-amd756.c
|
||||
* Copyright (c) 1999-2002 Merlin Hughes <merlin@merlin.org>
|
||||
*
|
||||
* 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 or
|
||||
* (at your option) version 3 of the License.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/xbox/amd_smbus.h"
|
||||
#include "hw/i2c/smbus.h"
|
||||
|
||||
/* AMD756 SMBus address offsets */
|
||||
#define SMB_ADDR_OFFSET 0xE0
|
||||
#define SMB_IOSIZE 16
|
||||
|
||||
#define SMB_GLOBAL_STATUS 0x0
|
||||
#define SMB_GLOBAL_ENABLE 0x2
|
||||
#define SMB_HOST_ADDRESS 0x4
|
||||
#define SMB_HOST_DATA 0x6
|
||||
#define SMB_HOST_COMMAND 0x8
|
||||
#define SMB_HOST_BLOCK_DATA 0x9
|
||||
#define SMB_HAS_DATA 0xA
|
||||
#define SMB_HAS_DEVICE_ADDRESS 0xC
|
||||
#define SMB_HAS_HOST_ADDRESS 0xE
|
||||
#define SMB_SNOOP_ADDRESS 0xF
|
||||
|
||||
/* AMD756 constants */
|
||||
#define AMD756_QUICK 0x00
|
||||
#define AMD756_BYTE 0x01
|
||||
#define AMD756_BYTE_DATA 0x02
|
||||
#define AMD756_WORD_DATA 0x03
|
||||
#define AMD756_PROCESS_CALL 0x04
|
||||
#define AMD756_BLOCK_DATA 0x05
|
||||
|
||||
/*
|
||||
SMBUS event = I/O 28-29 bit 11
|
||||
see E0 for the status bits and enabled in E2
|
||||
*/
|
||||
#define GS_ABRT_STS (1 << 0)
|
||||
#define GS_COL_STS (1 << 1)
|
||||
#define GS_PRERR_STS (1 << 2)
|
||||
#define GS_HST_STS (1 << 3)
|
||||
#define GS_HCYC_STS (1 << 4)
|
||||
#define GS_TO_STS (1 << 5)
|
||||
#define GS_SMB_STS (1 << 11)
|
||||
|
||||
#define GS_CLEAR_STS (GS_ABRT_STS | GS_COL_STS | GS_PRERR_STS | \
|
||||
GS_HCYC_STS | GS_TO_STS )
|
||||
|
||||
#define GE_CYC_TYPE_MASK (7)
|
||||
#define GE_HOST_STC (1 << 3)
|
||||
|
||||
#define GE_HCYC_EN (1 << 4)
|
||||
#define GE_ABORT (1 << 5)
|
||||
|
||||
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
#ifdef DEBUG
|
||||
# define SMBUS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__)
|
||||
#else
|
||||
# define SMBUS_DPRINTF(format, ...) do { } while (0)
|
||||
#endif
|
||||
|
||||
static void amd756_smb_transaction(AMD756SMBus *s)
|
||||
{
|
||||
uint8_t prot = s->smb_ctl & GE_CYC_TYPE_MASK;
|
||||
uint8_t read = s->smb_addr & 0x01;
|
||||
uint8_t cmd = s->smb_cmd;
|
||||
uint8_t addr = (s->smb_addr >> 1) & 0x7f;
|
||||
i2c_bus *bus = s->smbus;
|
||||
|
||||
SMBUS_DPRINTF("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot);
|
||||
switch(prot) {
|
||||
case AMD756_QUICK:
|
||||
smbus_quick_command(bus, addr, read);
|
||||
break;
|
||||
case AMD756_BYTE:
|
||||
if (read) {
|
||||
s->smb_data0 = smbus_receive_byte(bus, addr);
|
||||
} else {
|
||||
smbus_send_byte(bus, addr, cmd);
|
||||
}
|
||||
break;
|
||||
case AMD756_BYTE_DATA:
|
||||
if (read) {
|
||||
s->smb_data0 = smbus_read_byte(bus, addr, cmd);
|
||||
} else {
|
||||
smbus_write_byte(bus, addr, cmd, s->smb_data0);
|
||||
}
|
||||
break;
|
||||
case AMD756_WORD_DATA:
|
||||
if (read) {
|
||||
uint16_t val;
|
||||
val = smbus_read_word(bus, addr, cmd);
|
||||
s->smb_data0 = val;
|
||||
s->smb_data1 = val >> 8;
|
||||
} else {
|
||||
smbus_write_word(bus, addr, cmd, s->smb_data0);
|
||||
}
|
||||
break;
|
||||
case AMD756_BLOCK_DATA:
|
||||
if (read) {
|
||||
s->smb_data0 = smbus_read_block(bus, addr, cmd, s->smb_data);
|
||||
} else {
|
||||
smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
|
||||
s->smb_stat |= GS_HCYC_STS;
|
||||
|
||||
return;
|
||||
|
||||
|
||||
error:
|
||||
s->smb_stat |= GS_PRERR_STS;
|
||||
}
|
||||
|
||||
void amd756_smb_ioport_writeb(void *opaque, uint32_t addr, uint32_t val)
|
||||
{
|
||||
AMD756SMBus *s = opaque;
|
||||
addr &= 0x3f;
|
||||
SMBUS_DPRINTF("SMB writeb port=0x%04x val=0x%02x\n", addr, val);
|
||||
switch(addr) {
|
||||
case SMB_GLOBAL_STATUS:
|
||||
|
||||
if (s->irq) {
|
||||
/* Raise an irq if interrupts are enabled and a new
|
||||
* status is being set */
|
||||
if ((s->smb_ctl & GE_HCYC_EN)
|
||||
&& ((val & GS_CLEAR_STS)
|
||||
& (~(s->smb_stat & GS_CLEAR_STS)))) {
|
||||
|
||||
qemu_irq_raise(s->irq);
|
||||
} else {
|
||||
qemu_irq_lower(s->irq);
|
||||
}
|
||||
}
|
||||
|
||||
if (val & GS_CLEAR_STS) {
|
||||
s->smb_stat = 0;
|
||||
s->smb_index = 0;
|
||||
} else if (val & GS_HCYC_STS) {
|
||||
s->smb_stat = GS_HCYC_STS;
|
||||
s->smb_index = 0;
|
||||
} else {
|
||||
s->smb_stat = GS_HCYC_STS;
|
||||
s->smb_index = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
case SMB_GLOBAL_ENABLE:
|
||||
s->smb_ctl = val;
|
||||
if (val & GE_ABORT)
|
||||
s->smb_stat |= GS_ABRT_STS;
|
||||
if (val & GE_HOST_STC) {
|
||||
amd756_smb_transaction(s);
|
||||
|
||||
if (s->irq
|
||||
&& (val & GE_HCYC_EN)
|
||||
&& (s->smb_stat & GS_CLEAR_STS)) {
|
||||
qemu_irq_raise(s->irq);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
case SMB_HOST_COMMAND:
|
||||
s->smb_cmd = val;
|
||||
break;
|
||||
case SMB_HOST_ADDRESS:
|
||||
s->smb_addr = val;
|
||||
break;
|
||||
case SMB_HOST_DATA:
|
||||
s->smb_data0 = val;
|
||||
break;
|
||||
case SMB_HOST_DATA+1:
|
||||
s->smb_data1 = val;
|
||||
break;
|
||||
case SMB_HOST_BLOCK_DATA:
|
||||
s->smb_data[s->smb_index++] = val;
|
||||
if (s->smb_index > 31)
|
||||
s->smb_index = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t amd756_smb_ioport_readb(void *opaque, uint32_t addr)
|
||||
{
|
||||
AMD756SMBus *s = opaque;
|
||||
uint32_t val;
|
||||
|
||||
addr &= 0x3f;
|
||||
switch(addr) {
|
||||
case SMB_GLOBAL_STATUS:
|
||||
val = s->smb_stat;
|
||||
break;
|
||||
case SMB_GLOBAL_ENABLE:
|
||||
//s->smb_index = 0;
|
||||
val = s->smb_ctl & 0x1f;
|
||||
break;
|
||||
case SMB_HOST_COMMAND:
|
||||
val = s->smb_cmd;
|
||||
break;
|
||||
case SMB_HOST_ADDRESS:
|
||||
val = s->smb_addr;
|
||||
break;
|
||||
case SMB_HOST_DATA:
|
||||
val = s->smb_data0;
|
||||
break;
|
||||
case SMB_HOST_DATA+1:
|
||||
val = s->smb_data1;
|
||||
break;
|
||||
case SMB_HOST_BLOCK_DATA:
|
||||
val = s->smb_data[s->smb_index++];
|
||||
if (s->smb_index > 31)
|
||||
s->smb_index = 0;
|
||||
break;
|
||||
default:
|
||||
val = 0;
|
||||
break;
|
||||
}
|
||||
SMBUS_DPRINTF("SMB readb port=0x%04x val=0x%02x\n", addr, val);
|
||||
return val;
|
||||
}
|
||||
|
||||
void amd756_smbus_init(DeviceState *parent, AMD756SMBus *smb, qemu_irq irq)
|
||||
{
|
||||
smb->smbus = i2c_init_bus(parent, "i2c");
|
||||
smb->smb_stat = 0;
|
||||
|
||||
smb->irq = irq;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* AMD756 SMBus implementation
|
||||
*
|
||||
* Copyright (C) 2012 espes
|
||||
*
|
||||
* Based on pm_smbus.c
|
||||
* Copyright (c) 2006 Fabrice Bellard
|
||||
*
|
||||
* 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 or
|
||||
* (at your option) version 3 of the License.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef AMD_SMBUS_H
|
||||
#define AMD_SMBUS_H
|
||||
|
||||
typedef struct AMD756SMBus {
|
||||
i2c_bus *smbus;
|
||||
|
||||
uint8_t smb_stat;
|
||||
uint8_t smb_ctl;
|
||||
uint8_t smb_cmd;
|
||||
uint8_t smb_addr;
|
||||
uint8_t smb_data0;
|
||||
uint8_t smb_data1;
|
||||
uint8_t smb_data[32];
|
||||
uint8_t smb_index;
|
||||
|
||||
qemu_irq irq;
|
||||
} AMD756SMBus;
|
||||
|
||||
void amd756_smbus_init(DeviceState *parent, AMD756SMBus *smb, qemu_irq irq);
|
||||
void amd756_smb_ioport_writeb(void *opaque, uint32_t addr, uint32_t val);
|
||||
uint32_t amd756_smb_ioport_readb(void *opaque, uint32_t addr);
|
||||
|
||||
#endif /* !AMD_SMBUS_H */
|
||||
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
* QEMU Chihiro USB Devices
|
||||
*
|
||||
* Copyright (c) 2016 espes
|
||||
*
|
||||
* 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 or
|
||||
* (at your option) version 3 of the License.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "ui/console.h"
|
||||
#include "hw/usb.h"
|
||||
#include "hw/usb/desc.h"
|
||||
|
||||
#define DEBUG_CUSB
|
||||
#ifdef DEBUG_CUSB
|
||||
#define DPRINTF(s, ...) printf("chihiro-usb: " s, ## __VA_ARGS__)
|
||||
#else
|
||||
#define DPRINTF(...)
|
||||
#endif
|
||||
|
||||
typedef struct ChihiroUSBState {
|
||||
USBDevice dev;
|
||||
} ChihiroUSBState;
|
||||
|
||||
enum chihiro_usb_strings {
|
||||
STRING_SERIALNUMBER,
|
||||
STRING_MANUFACTURER,
|
||||
STRING_PRODUCT,
|
||||
};
|
||||
|
||||
static const USBDescStrings chihiro_usb_stringtable = {
|
||||
[STRING_SERIALNUMBER] = "\x00",
|
||||
[STRING_MANUFACTURER] = "SEGA",
|
||||
[STRING_PRODUCT] = "BASEBD" // different for qc?
|
||||
};
|
||||
|
||||
static const USBDescIface desc_iface_chihiro_an2131qc = {
|
||||
.bInterfaceNumber = 0,
|
||||
.bNumEndpoints = 10,
|
||||
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
|
||||
.bInterfaceSubClass = 0x00,
|
||||
.bInterfaceProtocol = 0x00,
|
||||
.eps = (USBDescEndpoint[]) {
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_OUT | 0x01,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 0x0040,
|
||||
.bInterval = 0,
|
||||
},
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_OUT | 0x02,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 0x0040,
|
||||
.bInterval = 0,
|
||||
},
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_OUT | 0x03,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 0x0040,
|
||||
.bInterval = 0,
|
||||
},
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_OUT | 0x04,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 0x0040,
|
||||
.bInterval = 0,
|
||||
},
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_OUT | 0x05,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 0x0040,
|
||||
.bInterval = 0,
|
||||
},
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_IN | 0x01,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 0x0040,
|
||||
.bInterval = 0,
|
||||
},
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_IN | 0x02,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 0x0040,
|
||||
.bInterval = 0,
|
||||
},
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_IN | 0x03,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 0x0040,
|
||||
.bInterval = 0,
|
||||
},
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_IN | 0x04,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 0x0040,
|
||||
.bInterval = 0,
|
||||
},
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_IN | 0x05,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 0x0040,
|
||||
.bInterval = 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const USBDescDevice desc_device_chihiro_an2131qc = {
|
||||
.bcdUSB = 0x0100,
|
||||
.bMaxPacketSize0 = 0x40,
|
||||
.bNumConfigurations = 1,
|
||||
.confs = (USBDescConfig[]) {
|
||||
{
|
||||
.bNumInterfaces = 1,
|
||||
.bConfigurationValue = 1,
|
||||
.bmAttributes = 0x80,
|
||||
.bMaxPower = 0x96,
|
||||
.nif = 1,
|
||||
.ifs = &desc_iface_chihiro_an2131qc,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const USBDesc desc_chihiro_an2131qc = {
|
||||
.id = {
|
||||
.idVendor = 0x0CA3,
|
||||
.idProduct = 0x0002,
|
||||
.bcdDevice = 0x0108,
|
||||
.iManufacturer = STRING_MANUFACTURER,
|
||||
.iProduct = STRING_PRODUCT,
|
||||
.iSerialNumber = STRING_SERIALNUMBER,
|
||||
},
|
||||
.full = &desc_device_chihiro_an2131qc,
|
||||
.str = chihiro_usb_stringtable,
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static const USBDescIface desc_iface_chihiro_an2131sc = {
|
||||
.bInterfaceNumber = 0,
|
||||
.bNumEndpoints = 6,
|
||||
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
|
||||
.bInterfaceSubClass = 0x00,
|
||||
.bInterfaceProtocol = 0x00,
|
||||
.eps = (USBDescEndpoint[]) {
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_OUT | 0x01,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 0x0040,
|
||||
.bInterval = 0,
|
||||
},
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_OUT | 0x02,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 0x0040,
|
||||
.bInterval = 0,
|
||||
},
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_OUT | 0x03,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 0x0040,
|
||||
.bInterval = 0,
|
||||
},
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_IN | 0x01,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 0x0040,
|
||||
.bInterval = 0,
|
||||
},
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_IN | 0x02,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 0x0040,
|
||||
.bInterval = 0,
|
||||
},
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_IN | 0x03,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 0x0040,
|
||||
.bInterval = 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const USBDescDevice desc_device_chihiro_an2131sc = {
|
||||
.bcdUSB = 0x0100,
|
||||
.bMaxPacketSize0 = 0x40,
|
||||
.bNumConfigurations = 1,
|
||||
.confs = (USBDescConfig[]) {
|
||||
{
|
||||
.bNumInterfaces = 1,
|
||||
.bConfigurationValue = 1,
|
||||
.bmAttributes = 0x80,
|
||||
.bMaxPower = 0x96,
|
||||
.nif = 1,
|
||||
.ifs = &desc_iface_chihiro_an2131sc,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const USBDesc desc_chihiro_an2131sc = {
|
||||
.id = {
|
||||
.idVendor = 0x0CA3,
|
||||
.idProduct = 0x0003,
|
||||
.bcdDevice = 0x0110,
|
||||
.iManufacturer = STRING_MANUFACTURER,
|
||||
.iProduct = STRING_PRODUCT,
|
||||
.iSerialNumber = STRING_SERIALNUMBER,
|
||||
},
|
||||
.full = &desc_device_chihiro_an2131sc,
|
||||
.str = chihiro_usb_stringtable,
|
||||
};
|
||||
|
||||
|
||||
|
||||
static void handle_reset(USBDevice *dev)
|
||||
{
|
||||
DPRINTF("usb reset\n");
|
||||
}
|
||||
|
||||
static void handle_control(USBDevice *dev, USBPacket *p,
|
||||
int request, int value, int index, int length, uint8_t *data)
|
||||
{
|
||||
DPRINTF("handle control %d %d %d %d\n", request, value, index, length);
|
||||
|
||||
int ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
|
||||
if (ret >= 0) {
|
||||
DPRINTF("handled by usb_desc_handle_control: %d\n", ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_data(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
DPRINTF("handle_data 0x%x %d 0x%zx\n", p->pid, p->ep->nr, p->iov.size);
|
||||
}
|
||||
|
||||
static void handle_destroy(USBDevice *dev)
|
||||
{
|
||||
DPRINTF("usb reset\n");
|
||||
}
|
||||
|
||||
|
||||
static int chihiro_an2131qc_initfn(USBDevice *dev)
|
||||
{
|
||||
// ChihiroUSBState *s = DO_UPCAST(ChihiroUSBState, dev, dev);
|
||||
usb_desc_init(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void chihiro_an2131qc_class_initfn(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
|
||||
|
||||
uc->init = chihiro_an2131qc_initfn;
|
||||
uc->product_desc = "Chihiro an2131qc";
|
||||
uc->usb_desc = &desc_chihiro_an2131qc;
|
||||
|
||||
uc->handle_reset = handle_reset;
|
||||
uc->handle_control = handle_control;
|
||||
uc->handle_data = handle_data;
|
||||
uc->handle_destroy = handle_destroy;
|
||||
uc->handle_attach = usb_desc_attach;
|
||||
|
||||
//dc->vmsd = &vmstate_usb_kbd;
|
||||
}
|
||||
|
||||
static const TypeInfo chihiro_an2131qc_info = {
|
||||
.name = "chihiro-an2131qc",
|
||||
.parent = TYPE_USB_DEVICE,
|
||||
.instance_size = sizeof(ChihiroUSBState),
|
||||
.class_init = chihiro_an2131qc_class_initfn,
|
||||
};
|
||||
|
||||
|
||||
|
||||
static int chihiro_an2131sc_initfn(USBDevice *dev)
|
||||
{
|
||||
// ChihiroUSBState *s = DO_UPCAST(ChihiroUSBState, dev, dev);
|
||||
usb_desc_init(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void chihiro_an2131sc_class_initfn(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
|
||||
|
||||
uc->init = chihiro_an2131sc_initfn;
|
||||
uc->product_desc = "Chihiro an2131sc";
|
||||
uc->usb_desc = &desc_chihiro_an2131sc;
|
||||
|
||||
uc->handle_reset = handle_reset;
|
||||
uc->handle_control = handle_control;
|
||||
uc->handle_data = handle_data;
|
||||
uc->handle_destroy = handle_destroy;
|
||||
uc->handle_attach = usb_desc_attach;
|
||||
|
||||
//dc->vmsd = &vmstate_usb_kbd;
|
||||
}
|
||||
|
||||
static const TypeInfo chihiro_an2131sc_info = {
|
||||
.name = "chihiro-an2131sc",
|
||||
.parent = TYPE_USB_DEVICE,
|
||||
.instance_size = sizeof(ChihiroUSBState),
|
||||
.class_init = chihiro_an2131sc_class_initfn,
|
||||
};
|
||||
|
||||
|
||||
static void chihiro_usb_register_types(void)
|
||||
{
|
||||
type_register_static(&chihiro_an2131qc_info);
|
||||
type_register_static(&chihiro_an2131sc_info);
|
||||
}
|
||||
|
||||
type_init(chihiro_usb_register_types)
|
||||
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* QEMU Chihiro emulation
|
||||
*
|
||||
* Copyright (c) 2013 espes
|
||||
*
|
||||
* 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 or
|
||||
* (at your option) version 3 of the License.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hw/hw.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/boards.h"
|
||||
#include "hw/ide.h"
|
||||
#include "hw/loader.h"
|
||||
#include "hw/isa/isa.h"
|
||||
#include "exec/memory.h"
|
||||
#include "qemu/config-file.h"
|
||||
#include "sysemu/blockdev.h"
|
||||
#include "block/blkmemory.h"
|
||||
#include "hw/xbox/xbox.h"
|
||||
|
||||
|
||||
#define SEGA_CHIP_REVISION 0xF0
|
||||
# define SEGA_CHIP_REVISION_CHIP_ID 0xFF00
|
||||
# define SEGA_CHIP_REVISION_FPGA_CHIP_ID 0x0000
|
||||
# define SEGA_CHIP_REVISION_ASIC_CHIP_ID 0x0100
|
||||
# define SEGA_CHIP_REVISION_REVISION_ID_MASK 0x00FF
|
||||
#define SEGA_DIMM_SIZE 0xF4
|
||||
# define SEGA_DIMM_SIZE_128M 0
|
||||
# define SEGA_DIMM_SIZE_256M 1
|
||||
# define SEGA_DIMM_SIZE_512M 2
|
||||
# define SEGA_DIMM_SIZE_1024M 3
|
||||
|
||||
//#define DEBUG_CHIHIRO
|
||||
|
||||
typedef struct ChihiroLPCState {
|
||||
ISADevice dev;
|
||||
MemoryRegion ioport;
|
||||
} ChihiroLPCState;
|
||||
|
||||
#define CHIHIRO_LPC_DEVICE(obj) \
|
||||
OBJECT_CHECK(ChihiroLPCState, (obj), "chihiro-lpc")
|
||||
|
||||
|
||||
static uint64_t chhiro_lpc_io_read(void *opaque, hwaddr addr,
|
||||
unsigned size)
|
||||
{
|
||||
uint64_t r = 0;
|
||||
switch (addr) {
|
||||
case SEGA_CHIP_REVISION:
|
||||
r = SEGA_CHIP_REVISION_ASIC_CHIP_ID;
|
||||
break;
|
||||
case SEGA_DIMM_SIZE:
|
||||
r = SEGA_DIMM_SIZE_128M;
|
||||
break;
|
||||
}
|
||||
#ifdef DEBUG_CHIHIRO
|
||||
printf("chihiro lpc read [0x%llx] -> 0x%llx\n", addr, r);
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
|
||||
static void chhiro_lpc_io_write(void *opaque, hwaddr addr, uint64_t val,
|
||||
unsigned size)
|
||||
{
|
||||
#ifdef DEBUG_CHIHIRO
|
||||
printf("chihiro lpc write [0x%llx] = 0x%llx\n", addr, val);
|
||||
#endif
|
||||
}
|
||||
|
||||
static const MemoryRegionOps chihiro_lpc_io_ops = {
|
||||
.read = chhiro_lpc_io_read,
|
||||
.write = chhiro_lpc_io_write,
|
||||
.impl = {
|
||||
.min_access_size = 2,
|
||||
.max_access_size = 2,
|
||||
},
|
||||
};
|
||||
|
||||
static void chihiro_lpc_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
ChihiroLPCState *s = CHIHIRO_LPC_DEVICE(dev);
|
||||
ISADevice *isa = ISA_DEVICE(dev);
|
||||
|
||||
memory_region_init_io(&s->ioport, OBJECT(dev), &chihiro_lpc_io_ops, s,
|
||||
"chihiro-lpc-io", 0x100);
|
||||
isa_register_ioport(isa, &s->ioport, 0x4000);
|
||||
}
|
||||
|
||||
static void chihiro_lpc_class_initfn(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
dc->realize = chihiro_lpc_realize;
|
||||
dc->desc = "Chihiro LPC";
|
||||
}
|
||||
|
||||
static const TypeInfo chihiro_lpc_info = {
|
||||
.name = "chihiro-lpc",
|
||||
.parent = TYPE_ISA_DEVICE,
|
||||
.instance_size = sizeof(ChihiroLPCState),
|
||||
.class_init = chihiro_lpc_class_initfn,
|
||||
};
|
||||
|
||||
static void chihiro_register_types(void)
|
||||
{
|
||||
type_register_static(&chihiro_lpc_info);
|
||||
}
|
||||
|
||||
type_init(chihiro_register_types)
|
||||
|
||||
|
||||
/* The chihiro baseboard communicates with the xbox by acting as an IDE
|
||||
* device. The device maps the boot rom from the mediaboard, a communication
|
||||
* area for interfacing with the network board, and the ram on the baseboard.
|
||||
* The baseboard ram is populated at boot from the gd-rom drive on the
|
||||
* mediaboard containing something like a combined disc+hdd image.
|
||||
*/
|
||||
|
||||
#define FILESYSTEM_START 0
|
||||
#define ROM_START 0x8000000
|
||||
#define ROM_SECTORS 0x2000
|
||||
#define COMMUNICATION_START 0x9000000
|
||||
#define COMMUNICATION_SECTORS 0x10000
|
||||
#define SECTOR_SIZE 512
|
||||
|
||||
static void chihiro_ide_interface_init(const char *rom_file,
|
||||
const char *filesystem_file)
|
||||
{
|
||||
if (drive_get(IF_IDE, 0, 1)) {
|
||||
fprintf(stderr, "chihiro ide interface needs to be attached "
|
||||
"to IDE device 1 but it's already in use.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
MemoryRegion *interface, *rom, *filesystem;
|
||||
interface = g_malloc(sizeof(*interface));
|
||||
memory_region_init(interface, NULL, "chihiro.interface",
|
||||
(uint64_t)0x10000000 * SECTOR_SIZE);
|
||||
|
||||
rom = g_malloc(sizeof(*rom));
|
||||
memory_region_init_ram(rom, NULL, "chihiro.interface.rom",
|
||||
ROM_SECTORS * SECTOR_SIZE);
|
||||
memory_region_add_subregion(interface,
|
||||
(uint64_t)ROM_START * SECTOR_SIZE, rom);
|
||||
|
||||
|
||||
/* limited by the size of the board ram, which we emulate as 128M for now */
|
||||
filesystem = g_malloc(sizeof(*filesystem));
|
||||
memory_region_init_ram(filesystem, NULL, "chihiro.interface.filesystem",
|
||||
128 * 1024 * 1024);
|
||||
memory_region_add_subregion(interface,
|
||||
(uint64_t)FILESYSTEM_START * SECTOR_SIZE,
|
||||
filesystem);
|
||||
|
||||
|
||||
AddressSpace *interface_space;
|
||||
interface_space = g_malloc(sizeof(*interface_space));
|
||||
address_space_init(interface_space, interface, "chihiro-interface");
|
||||
|
||||
/* read files */
|
||||
int rc, fd = -1;
|
||||
|
||||
if (!rom_file) rom_file = "fpr21042_m29w160et.bin";
|
||||
char *rom_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, rom_file);
|
||||
if (rom_filename) {
|
||||
int rom_size = get_image_size(rom_filename);
|
||||
assert(rom_size < memory_region_size(rom));
|
||||
|
||||
fd = open(rom_filename, O_RDONLY | O_BINARY);
|
||||
assert(fd != -1);
|
||||
rc = read(fd, memory_region_get_ram_ptr(rom), rom_size);
|
||||
assert(rc == rom_size);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
|
||||
if (filesystem_file) {
|
||||
assert(access(filesystem_file, R_OK) == 0);
|
||||
|
||||
int filesystem_size = get_image_size(filesystem_file);
|
||||
assert(filesystem_size < memory_region_size(filesystem));
|
||||
|
||||
fd = open(filesystem_file, O_RDONLY | O_BINARY);
|
||||
assert(fd != -1);
|
||||
rc = read(fd, memory_region_get_ram_ptr(rom), filesystem_size);
|
||||
assert(rc == filesystem_size);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/* create the device */
|
||||
DriveInfo *dinfo;
|
||||
dinfo = g_malloc0(sizeof(*dinfo));
|
||||
dinfo->id = g_strdup("chihiro-interface");
|
||||
dinfo->bdrv = bdrv_new(dinfo->id);
|
||||
dinfo->type = IF_IDE;
|
||||
dinfo->bus = 0;
|
||||
dinfo->unit = 1;
|
||||
dinfo->refcount = 1;
|
||||
|
||||
assert(!bdrv_memory_open(dinfo->bdrv, interface_space,
|
||||
memory_region_size(interface)));
|
||||
|
||||
drive_append(dinfo);
|
||||
}
|
||||
|
||||
static void chihiro_init(QEMUMachineInitArgs *args)
|
||||
{
|
||||
/* Placeholder blank eeprom for chihiro:
|
||||
* Serial number 000000000000
|
||||
* Mac address 00:00:00:00:00:00
|
||||
* ...etc.
|
||||
*/
|
||||
const uint8_t eeprom[] = {
|
||||
0xA7, 0x65, 0x60, 0x76, 0xB7, 0x2F, 0xFE, 0xD8,
|
||||
0x20, 0xBC, 0x8B, 0x15, 0x13, 0xBF, 0x73, 0x9C,
|
||||
0x8C, 0x3F, 0xD8, 0x07, 0x75, 0x55, 0x5F, 0x8B,
|
||||
0x09, 0xD1, 0x25, 0xD1, 0x1A, 0xA2, 0xD5, 0xB7,
|
||||
0x01, 0x7D, 0x9A, 0x31, 0xCD, 0x9C, 0x83, 0x6B,
|
||||
0x2C, 0xAB, 0xAD, 0x6F, 0xAC, 0x36, 0xDE, 0xEF,
|
||||
0x6F, 0x6E, 0x2F, 0x6F, 0x30, 0x30, 0x30, 0x30,
|
||||
0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
|
||||
QemuOpts *machine_opts = qemu_opts_find(qemu_find_opts("machine"), NULL);
|
||||
if (machine_opts) {
|
||||
const char *mediaboard_rom_file =
|
||||
qemu_opt_get(machine_opts, "mediaboard_rom");
|
||||
const char *mediaboard_filesystem_file =
|
||||
qemu_opt_get(machine_opts, "mediaboard_filesystem");
|
||||
|
||||
if (mediaboard_rom_file || mediaboard_filesystem_file) {
|
||||
chihiro_ide_interface_init(mediaboard_rom_file,
|
||||
mediaboard_filesystem_file);
|
||||
}
|
||||
}
|
||||
|
||||
ISABus *isa_bus;
|
||||
xbox_init_common(args, (uint8_t*)eeprom, &isa_bus);
|
||||
|
||||
isa_create_simple(isa_bus, "chihiro-lpc");
|
||||
}
|
||||
|
||||
|
||||
static QEMUMachine chihiro_machine = {
|
||||
.name = "chihiro",
|
||||
.desc = "Sega Chihiro",
|
||||
.init = chihiro_init,
|
||||
.max_cpus = 1,
|
||||
.no_floppy = 1,
|
||||
.no_cdrom = 1,
|
||||
.no_sdcard = 1,
|
||||
PC_DEFAULT_MACHINE_OPTIONS
|
||||
};
|
||||
|
||||
static void chihiro_machine_init(void) {
|
||||
qemu_register_machine(&chihiro_machine);
|
||||
}
|
||||
machine_init(chihiro_machine_init);
|
||||
@@ -0,0 +1 @@
|
||||
obj-y += dsp.o dsp_cpu.o dsp_dma.o
|
||||
@@ -0,0 +1,476 @@
|
||||
/*
|
||||
* 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
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "qemu-common.h"
|
||||
|
||||
#include "dsp_cpu.h"
|
||||
#include "dsp_dma.h"
|
||||
|
||||
#include "dsp.h"
|
||||
|
||||
/* Defines */
|
||||
#define BITMASK(x) ((1<<(x))-1)
|
||||
#define ARRAYSIZE(x) (int)(sizeof(x)/sizeof(x[0]))
|
||||
|
||||
#define INTERRUPT_ABORT_FRAME (1 << 0)
|
||||
#define INTERRUPT_START_FRAME (1 << 1)
|
||||
#define INTERRUPT_DMA_EOL (1 << 7)
|
||||
|
||||
#define DPRINTF(s, ...) printf(s, ## __VA_ARGS__)
|
||||
|
||||
struct DSPState {
|
||||
dsp_core_t core;
|
||||
DSPDMAState dma;
|
||||
int save_cycles;
|
||||
|
||||
uint32_t interrupts;
|
||||
};
|
||||
|
||||
static uint32_t read_peripheral(dsp_core_t* core, uint32_t address);
|
||||
static void write_peripheral(dsp_core_t* core, uint32_t address, uint32_t value);
|
||||
|
||||
DSPState* dsp_init(void* scratch_rw_opaque, dsp_scratch_rw_func scratch_rw)
|
||||
{
|
||||
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.scratch_rw_opaque = scratch_rw_opaque;
|
||||
dsp->dma.scratch_rw = scratch_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);
|
||||
|
||||
// printf("read_peripheral 0x%06x\n", address);
|
||||
|
||||
uint32_t v = 0xababa;
|
||||
switch(address) {
|
||||
case 0xFFFFC5:
|
||||
v = dsp->interrupts;
|
||||
if (dsp->dma.eol) {
|
||||
v |= INTERRUPT_DMA_EOL;
|
||||
}
|
||||
break;
|
||||
case 0xFFFFD4:
|
||||
v = dsp_dma_read(&dsp->dma, DMA_NEXT_BLOCK);
|
||||
break;
|
||||
case 0xFFFFD5:
|
||||
v = dsp_dma_read(&dsp->dma, DMA_START_BLOCK);
|
||||
break;
|
||||
case 0xFFFFD6:
|
||||
v = dsp_dma_read(&dsp->dma, DMA_CONTROL);
|
||||
break;
|
||||
case 0xFFFFD7:
|
||||
v = dsp_dma_read(&dsp->dma, DMA_CONFIGURATION);
|
||||
break;
|
||||
}
|
||||
|
||||
// printf(" -> 0x%06x\n", v);
|
||||
return v;
|
||||
}
|
||||
|
||||
static void write_peripheral(dsp_core_t* core, uint32_t address, uint32_t value) {
|
||||
DSPState* dsp = container_of(core, DSPState, core);
|
||||
|
||||
// printf("write_peripheral [0x%06x] = 0x%06x\n", address, value);
|
||||
|
||||
switch(address) {
|
||||
case 0xFFFFC5:
|
||||
dsp->interrupts &= ~value;
|
||||
if (value & INTERRUPT_DMA_EOL) {
|
||||
dsp->dma.eol = false;
|
||||
}
|
||||
break;
|
||||
case 0xFFFFD4:
|
||||
dsp_dma_write(&dsp->dma, DMA_NEXT_BLOCK, value);
|
||||
break;
|
||||
case 0xFFFFD5:
|
||||
dsp_dma_write(&dsp->dma, DMA_START_BLOCK, value);
|
||||
break;
|
||||
case 0xFFFFD6:
|
||||
dsp_dma_write(&dsp->dma, DMA_CONTROL, value);
|
||||
break;
|
||||
case 0xFFFFD7:
|
||||
dsp_dma_write(&dsp->dma, DMA_CONFIGURATION, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
|
||||
// if (unlikely(bDspDebugging)) {
|
||||
// while (dsp->core.save_cycles > 0)
|
||||
// {
|
||||
// dsp56k_execute_instruction();
|
||||
// dsp->core.save_cycles -= dsp->core.instr_cycle;
|
||||
// DebugDsp_Check();
|
||||
// }
|
||||
// } else {
|
||||
// printf("--> %d\n", dsp->core.save_cycles);
|
||||
while (dsp->save_cycles > 0)
|
||||
{
|
||||
dsp56k_execute_instruction(&dsp->core);
|
||||
dsp->save_cycles -= dsp->core.instr_cycle;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void dsp_bootstrap(DSPState* dsp)
|
||||
{
|
||||
// scratch memory is dma'd in to pram by the bootrom
|
||||
dsp->dma.scratch_rw(dsp->dma.scratch_rw_opaque,
|
||||
(uint8_t*)dsp->core.pram, 0, 0x800*4, false);
|
||||
}
|
||||
|
||||
void dsp_start_frame(DSPState* dsp)
|
||||
{
|
||||
dsp->interrupts |= INTERRUPT_START_FRAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disassemble DSP code between given addresses, return next PC address
|
||||
*/
|
||||
uint32_t dsp_disasm_address(DSPState* dsp, FILE *out, uint32_t lowerAdr, uint32_t UpperAdr)
|
||||
{
|
||||
uint32_t dsp_pc;
|
||||
|
||||
for (dsp_pc=lowerAdr; dsp_pc<=UpperAdr; dsp_pc++) {
|
||||
dsp_pc += dsp56k_execute_one_disasm_instruction(&dsp->core, out, dsp_pc);
|
||||
}
|
||||
return dsp_pc;
|
||||
}
|
||||
|
||||
uint32_t dsp_read_memory(DSPState* dsp, char space_id, uint32_t address)
|
||||
{
|
||||
int space;
|
||||
|
||||
switch (space_id) {
|
||||
case 'X':
|
||||
space = DSP_SPACE_X;
|
||||
break;
|
||||
case 'Y':
|
||||
space = DSP_SPACE_Y;
|
||||
break;
|
||||
case 'P':
|
||||
space = DSP_SPACE_P;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return dsp56k_read_memory(&dsp->core, space, address);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 < ARRAYSIZE(stackname); i++) {
|
||||
printf("- %s stack:", stackname[i]);
|
||||
for (j = 0; j < ARRAYSIZE(dsp->core.stack[0]); j++) {
|
||||
printf(" %04x", dsp->core.stack[i][j]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
printf("- Interrupt IPL:");
|
||||
for (i = 0; i < ARRAYSIZE(dsp->core.interrupt_ipl); i++) {
|
||||
printf(" %04x", dsp->core.interrupt_ipl[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
printf("- Pending ints: ");
|
||||
for (i = 0; i < ARRAYSIZE(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 = ARRAYSIZE(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;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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_H
|
||||
#define DSP_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
typedef struct DSPState DSPState;
|
||||
|
||||
typedef void (*dsp_scratch_rw_func)(
|
||||
void* opaque, uint8_t* ptr, uint32_t addr, size_t len, bool dir);
|
||||
|
||||
/* Dsp commands */
|
||||
DSPState* dsp_init(void* scratch_rw_opaque, dsp_scratch_rw_func scratch_rw);
|
||||
void dsp_destroy(DSPState* dsp);
|
||||
void dsp_reset(DSPState* dsp);
|
||||
|
||||
void dsp_step(DSPState* dsp);
|
||||
void dsp_run(DSPState* dsp, int cycles);
|
||||
|
||||
void dsp_bootstrap(DSPState* dsp);
|
||||
void dsp_start_frame(DSPState* dsp);
|
||||
|
||||
|
||||
/* Dsp Debugger commands */
|
||||
uint32_t dsp_read_memory(DSPState* dsp, char space, uint32_t addr);
|
||||
uint32_t dsp_disasm_memory(DSPState* dsp, uint32_t dsp_memdump_addr, uint32_t dsp_memdump_upper, char space);
|
||||
uint32_t dsp_disasm_address(DSPState* dsp, FILE *out, uint32_t lowerAdr, uint32_t UpperAdr);
|
||||
void dsp_info(DSPState* dsp);
|
||||
void dsp_print_registers(DSPState* dsp);
|
||||
int dsp_get_register_address(DSPState* dsp, const char *arg, uint32_t **addr, uint32_t *mask);
|
||||
bool dsp_disasm_set_register(DSPState* dsp, const char *arg, uint32_t value);
|
||||
|
||||
|
||||
#endif /* DSP_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* DSP56300 emulator
|
||||
*
|
||||
* Copyright (c) 2015 espes
|
||||
*
|
||||
* Adapted from Hatari DSP M56001 emulation
|
||||
* (C) 2003-2008 ARAnyM developer team
|
||||
* Adaption to Hatari (C) 2008 by Thomas Huth
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef DSP_CPU_H
|
||||
#define DSP_CPU_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define DSP_OMR_MA 0x00
|
||||
#define DSP_OMR_MB 0x01
|
||||
#define DSP_OMR_DE 0x02
|
||||
#define DSP_OMR_SD 0x06
|
||||
#define DSP_OMR_EA 0x07
|
||||
|
||||
#define DSP_SR_C 0x00
|
||||
#define DSP_SR_V 0x01
|
||||
#define DSP_SR_Z 0x02
|
||||
#define DSP_SR_N 0x03
|
||||
#define DSP_SR_U 0x04
|
||||
#define DSP_SR_E 0x05
|
||||
#define DSP_SR_L 0x06
|
||||
|
||||
#define DSP_SR_I0 0x08
|
||||
#define DSP_SR_I1 0x09
|
||||
#define DSP_SR_S0 0x0a
|
||||
#define DSP_SR_S1 0x0b
|
||||
#define DSP_SR_T 0x0d
|
||||
#define DSP_SR_LF 0x0f
|
||||
|
||||
#define DSP_SP_SE 0x04
|
||||
#define DSP_SP_UF 0x05
|
||||
|
||||
/* Registers numbers in dsp.registers[] */
|
||||
#define DSP_REG_X0 0x04
|
||||
#define DSP_REG_X1 0x05
|
||||
#define DSP_REG_Y0 0x06
|
||||
#define DSP_REG_Y1 0x07
|
||||
#define DSP_REG_A0 0x08
|
||||
#define DSP_REG_B0 0x09
|
||||
#define DSP_REG_A2 0x0a
|
||||
#define DSP_REG_B2 0x0b
|
||||
#define DSP_REG_A1 0x0c
|
||||
#define DSP_REG_B1 0x0d
|
||||
#define DSP_REG_A 0x0e
|
||||
#define DSP_REG_B 0x0f
|
||||
|
||||
#define DSP_REG_R0 0x10
|
||||
#define DSP_REG_R1 0x11
|
||||
#define DSP_REG_R2 0x12
|
||||
#define DSP_REG_R3 0x13
|
||||
#define DSP_REG_R4 0x14
|
||||
#define DSP_REG_R5 0x15
|
||||
#define DSP_REG_R6 0x16
|
||||
#define DSP_REG_R7 0x17
|
||||
|
||||
#define DSP_REG_N0 0x18
|
||||
#define DSP_REG_N1 0x19
|
||||
#define DSP_REG_N2 0x1a
|
||||
#define DSP_REG_N3 0x1b
|
||||
#define DSP_REG_N4 0x1c
|
||||
#define DSP_REG_N5 0x1d
|
||||
#define DSP_REG_N6 0x1e
|
||||
#define DSP_REG_N7 0x1f
|
||||
|
||||
#define DSP_REG_M0 0x20
|
||||
#define DSP_REG_M1 0x21
|
||||
#define DSP_REG_M2 0x22
|
||||
#define DSP_REG_M3 0x23
|
||||
#define DSP_REG_M4 0x24
|
||||
#define DSP_REG_M5 0x25
|
||||
#define DSP_REG_M6 0x26
|
||||
#define DSP_REG_M7 0x27
|
||||
|
||||
#define DSP_REG_SR 0x39
|
||||
#define DSP_REG_OMR 0x3a
|
||||
#define DSP_REG_SP 0x3b
|
||||
#define DSP_REG_SSH 0x3c
|
||||
#define DSP_REG_SSL 0x3d
|
||||
#define DSP_REG_LA 0x3e
|
||||
#define DSP_REG_LC 0x3f
|
||||
|
||||
#define DSP_REG_NULL 0x00
|
||||
#define DSP_REG_LCSAVE 0x30
|
||||
|
||||
#define DSP_REG_MAX 0x40
|
||||
|
||||
/* Memory spaces for dsp.ram[], dsp.rom[] */
|
||||
#define DSP_SPACE_X 0x00
|
||||
#define DSP_SPACE_Y 0x01
|
||||
#define DSP_SPACE_P 0x02
|
||||
|
||||
#define DSP_XRAM_SIZE 3072
|
||||
#define DSP_YRAM_SIZE 2048
|
||||
#define DSP_PRAM_SIZE 4096
|
||||
|
||||
#define DSP_MIXBUFFER_BASE 3072
|
||||
#define DSP_MIXBUFFER_SIZE 1024
|
||||
#define DSP_MIXBUFFER_READ_BASE 5120
|
||||
|
||||
#define DSP_PERIPH_BASE 0xFFFF80
|
||||
#define DSP_PERIPH_SIZE 128
|
||||
|
||||
#define DSP_INTERRUPT_NONE 0x0
|
||||
#define DSP_INTERRUPT_DISABLED 0x1
|
||||
#define DSP_INTERRUPT_LONG 0x2
|
||||
|
||||
#define DSP_INTER_RESET 0x0
|
||||
#define DSP_INTER_ILLEGAL 0x1
|
||||
#define DSP_INTER_STACK_ERROR 0x2
|
||||
#define DSP_INTER_TRACE 0x3
|
||||
#define DSP_INTER_SWI 0x4
|
||||
#define DSP_INTER_HOST_COMMAND 0x5
|
||||
#define DSP_INTER_HOST_RCV_DATA 0x6
|
||||
#define DSP_INTER_HOST_TRX_DATA 0x7
|
||||
#define DSP_INTER_SSI_RCV_DATA_E 0x8
|
||||
#define DSP_INTER_SSI_RCV_DATA 0x9
|
||||
#define DSP_INTER_SSI_TRX_DATA_E 0xa
|
||||
#define DSP_INTER_SSI_TRX_DATA 0xb
|
||||
|
||||
typedef enum {
|
||||
DSP_TRACE_MODE,
|
||||
DSP_DISASM_MODE
|
||||
} dsp_trace_disasm_t;
|
||||
|
||||
typedef struct dsp_interrupt_s {
|
||||
const uint16_t inter;
|
||||
const uint16_t vectorAddr;
|
||||
const uint16_t periph;
|
||||
const char *name;
|
||||
} dsp_interrupt_t;
|
||||
|
||||
typedef struct dsp_core_s dsp_core_t;
|
||||
|
||||
struct dsp_core_s {
|
||||
/* DSP instruction Cycle counter */
|
||||
uint16_t instr_cycle;
|
||||
|
||||
/* Registers */
|
||||
uint32_t pc;
|
||||
uint32_t registers[DSP_REG_MAX];
|
||||
|
||||
/* stack[0=ssh], stack[1=ssl] */
|
||||
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];
|
||||
|
||||
/* peripheral space, x:0xffff80-0xffffff */
|
||||
uint32_t periph[DSP_PERIPH_SIZE];
|
||||
|
||||
/* Misc */
|
||||
uint32_t loop_rep; /* executing rep ? */
|
||||
uint32_t pc_on_rep; /* True if PC is on REP instruction */
|
||||
|
||||
/* Interruptions */
|
||||
uint16_t interrupt_state; /* NONE, FAST or LONG interrupt */
|
||||
uint16_t interrupt_instr_fetch; /* vector of the current interrupt */
|
||||
uint16_t interrupt_save_pc; /* save next pc value before interrupt */
|
||||
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 */
|
||||
|
||||
/* callbacks */
|
||||
uint32_t (*read_peripheral)(dsp_core_t* core, uint32_t address);
|
||||
void (*write_peripheral)(dsp_core_t* core, uint32_t address, uint32_t value);
|
||||
|
||||
/* runtime data */
|
||||
|
||||
/* Instructions per second */
|
||||
#ifdef DSP_COUNT_IPS
|
||||
uint32_t start_time;
|
||||
#endif
|
||||
uint32_t num_inst;
|
||||
|
||||
/* Length of current instruction */
|
||||
uint32_t cur_inst_len; /* =0:jump, >0:increment */
|
||||
/* Current instruction */
|
||||
uint32_t cur_inst;
|
||||
|
||||
/* DSP is in disasm mode ? */
|
||||
/* If yes, stack overflow, underflow and illegal instructions messages are not displayed */
|
||||
bool executing_for_disasm;
|
||||
|
||||
char str_disasm_memory[2][50]; /* Buffer for memory change text in disasm mode */
|
||||
uint32_t disasm_memory_ptr; /* Pointer for memory change in disasm mode */
|
||||
|
||||
bool exception_debugging;
|
||||
|
||||
|
||||
/* disasm data */
|
||||
|
||||
/* Previous instruction */
|
||||
uint32_t disasm_prev_inst_pc;
|
||||
bool disasm_is_looping;
|
||||
|
||||
/* Used to display dc instead of unknown instruction for illegal opcodes */
|
||||
dsp_trace_disasm_t disasm_mode;
|
||||
|
||||
uint32_t disasm_cur_inst;
|
||||
uint16_t disasm_cur_inst_len;
|
||||
|
||||
/* Current instruction */
|
||||
char disasm_str_instr[128];
|
||||
char disasm_str_instr2[128];
|
||||
char disasm_parallelmove_name[64];
|
||||
|
||||
/**********************************
|
||||
* Register change
|
||||
**********************************/
|
||||
|
||||
uint32_t disasm_registers_save[64];
|
||||
#ifdef DSP_DISASM_REG_PC
|
||||
uint32_t pc_save;
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
/* Functions */
|
||||
void dsp56k_reset_cpu(dsp_core_t* dsp); /* Set dsp_core to use */
|
||||
void dsp56k_execute_instruction(dsp_core_t* dsp); /* Execute 1 instruction */
|
||||
uint16_t dsp56k_execute_one_disasm_instruction(dsp_core_t* dsp, FILE *out, uint32_t pc); /* Execute 1 instruction in disasm mode */
|
||||
|
||||
uint32_t dsp56k_read_memory(dsp_core_t* dsp, int space, uint32_t address);
|
||||
void dsp56k_write_memory(dsp_core_t* dsp, int space, uint32_t address, uint32_t value);
|
||||
|
||||
/* Interrupt relative functions */
|
||||
void dsp56k_add_interrupt(dsp_core_t* dsp, uint16_t inter);
|
||||
|
||||
#endif /* DSP_CPU_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* MCPX DSP DMA
|
||||
*
|
||||
* Copyright (c) 2015 espes
|
||||
*
|
||||
* 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 or
|
||||
* (at your option) version 3 of the License.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "dsp_dma.h"
|
||||
|
||||
#define DMA_CONFIGURATION_AUTOSTART (1 << 0)
|
||||
#define DMA_CONFIGURATION_AUTOREADY (1 << 1)
|
||||
#define DMA_CONFIGURATION_IOC_CLEAR (1 << 2)
|
||||
#define DMA_CONFIGURATION_EOL_CLEAR (1 << 3)
|
||||
#define DMA_CONFIGURATION_ERR_CLEAR (1 << 4)
|
||||
|
||||
#define DMA_CONTROL_ACTION 0x7
|
||||
#define DMA_CONTROL_ACTION_NOP 0
|
||||
#define DMA_CONTROL_ACTION_START 1
|
||||
#define DMA_CONTROL_ACTION_STOP 2
|
||||
#define DMA_CONTROL_ACTION_FREEZE 3
|
||||
#define DMA_CONTROL_ACTION_UNFREEZE 4
|
||||
#define DMA_CONTROL_ACTION_ABORT 5
|
||||
#define DMA_CONTROL_FROZEN (1 << 3)
|
||||
#define DMA_CONTROL_RUNNING (1 << 4)
|
||||
#define DMA_CONTROL_STOPPED (1 << 5)
|
||||
|
||||
#define NODE_POINTER_VAL 0x3fff
|
||||
#define NODE_POINTER_EOL (1 << 14)
|
||||
|
||||
#define NODE_CONTROL_DIRECTION (1 << 1)
|
||||
|
||||
|
||||
// #define DEBUG
|
||||
#ifdef DEBUG
|
||||
# define DPRINTF(s, ...) printf(s, ## __VA_ARGS__)
|
||||
#else
|
||||
# define DPRINTF(s, ...) do { } while (0)
|
||||
#endif
|
||||
|
||||
static void dsp_dma_run(DSPDMAState *s)
|
||||
{
|
||||
if (!(s->control & DMA_CONTROL_RUNNING)
|
||||
|| (s->control & DMA_CONTROL_FROZEN)) {
|
||||
return;
|
||||
}
|
||||
while (!(s->next_block & NODE_POINTER_EOL)) {
|
||||
uint32_t addr = s->next_block & NODE_POINTER_VAL;
|
||||
assert((addr+6) < sizeof(s->core->xram));
|
||||
|
||||
uint32_t next_block = dsp56k_read_memory(s->core, DSP_SPACE_X, addr);
|
||||
uint32_t control = dsp56k_read_memory(s->core, DSP_SPACE_X, addr+1);
|
||||
uint32_t count = dsp56k_read_memory(s->core, DSP_SPACE_X, addr+2);
|
||||
uint32_t dsp_offset = dsp56k_read_memory(s->core, DSP_SPACE_X, addr+3);
|
||||
uint32_t scratch_offset = dsp56k_read_memory(s->core, DSP_SPACE_X, addr+4);
|
||||
uint32_t scratch_base = dsp56k_read_memory(s->core, DSP_SPACE_X, addr+5);
|
||||
uint32_t scratch_size = dsp56k_read_memory(s->core, DSP_SPACE_X, addr+6)+1;
|
||||
|
||||
s->next_block = next_block;
|
||||
if (s->next_block & NODE_POINTER_EOL) {
|
||||
s->eol = true;
|
||||
}
|
||||
|
||||
|
||||
DPRINTF("\n\n\nDMA addr %x, control %x, count %x, "
|
||||
"dsp_offset %x, scratch_offset %x, base %x, size %x\n\n\n",
|
||||
addr, control, count, dsp_offset,
|
||||
scratch_offset, scratch_base, scratch_size);
|
||||
|
||||
uint32_t format = (control >> 10) & 7;
|
||||
unsigned int item_size;
|
||||
uint32_t item_mask = 0xffffffff;
|
||||
switch(format) {
|
||||
case 1:
|
||||
item_size = 2;
|
||||
break;
|
||||
case 2: //big-endian?
|
||||
case 6:
|
||||
item_size = 4;
|
||||
item_mask = 0x00FFFFFF;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Unknown dsp dma format: 0x%x\n", format);
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
uint32_t buf_id = (control >> 5) & 0xf;
|
||||
|
||||
size_t scratch_addr;
|
||||
if (buf_id == 0xe) { // 'circular'?
|
||||
// assert(scratch_offset == 0);
|
||||
// assert(scratch_offset + count * item_size < scratch_size);
|
||||
if (scratch_offset + count * item_size >= scratch_size) {
|
||||
// This happens during the startup sound effect.
|
||||
// I think it might actually be a bug in the code...
|
||||
DPRINTF("skipping bad dma...\n");
|
||||
continue;
|
||||
}
|
||||
scratch_addr = scratch_base + scratch_offset; //??
|
||||
} else {
|
||||
// assert(buf_id == 0xf) // 'offset'
|
||||
scratch_addr = scratch_offset;
|
||||
}
|
||||
|
||||
uint32_t mem_address;
|
||||
int mem_space;
|
||||
if (dsp_offset < 0x1800) {
|
||||
assert(dsp_offset+count < 0x1800);
|
||||
mem_space = DSP_SPACE_X;
|
||||
mem_address = dsp_offset;
|
||||
} else if (dsp_offset >= 0x1800 && dsp_offset < 0x2000) { //?
|
||||
assert(dsp_offset+count < 0x2000);
|
||||
mem_space = DSP_SPACE_Y;
|
||||
mem_address = dsp_offset - 0x1800;
|
||||
} else if (dsp_offset >= 0x2800 && dsp_offset < 0x3800) { //?
|
||||
assert(dsp_offset+count < 0x3800);
|
||||
mem_space = DSP_SPACE_P;
|
||||
mem_address = dsp_offset - 0x2800;
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
uint8_t* scratch_buf = calloc(count, item_size);
|
||||
|
||||
if (control & NODE_CONTROL_DIRECTION) {
|
||||
int i;
|
||||
for (i=0; i<count; i++) {
|
||||
uint32_t v = dsp56k_read_memory(s->core,
|
||||
mem_space, mem_address+i);
|
||||
switch(item_size) {
|
||||
case 2:
|
||||
*(uint16_t*)(scratch_buf + i*2) = v;
|
||||
break;
|
||||
case 4:
|
||||
*(uint32_t*)(scratch_buf + i*4) = v;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// write to scratch memory
|
||||
s->scratch_rw(s->scratch_rw_opaque,
|
||||
scratch_buf, scratch_addr, count*item_size, 1);
|
||||
} else {
|
||||
// read from scratch memory
|
||||
s->scratch_rw(s->scratch_rw_opaque,
|
||||
scratch_buf, scratch_addr, count*item_size, 0);
|
||||
|
||||
int i;
|
||||
for (i=0; i<count; i++) {
|
||||
uint32_t v;
|
||||
switch(item_size) {
|
||||
case 2:
|
||||
v = *(uint16_t*)(scratch_buf + i*2);
|
||||
break;
|
||||
case 4:
|
||||
v = (*(uint32_t*)(scratch_buf + i*4)) & item_mask;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
// DPRINTF("... %06x\n", v);
|
||||
dsp56k_write_memory(s->core, mem_space, mem_address+i, v);
|
||||
}
|
||||
}
|
||||
|
||||
free(scratch_buf);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t dsp_dma_read(DSPDMAState *s, DSPDMARegister reg)
|
||||
{
|
||||
switch (reg) {
|
||||
case DMA_CONFIGURATION:
|
||||
return s->configuration;
|
||||
case DMA_CONTROL:
|
||||
return s->control;
|
||||
case DMA_START_BLOCK:
|
||||
return s->start_block;
|
||||
case DMA_NEXT_BLOCK:
|
||||
return s->next_block;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dsp_dma_write(DSPDMAState *s, DSPDMARegister reg, uint32_t v)
|
||||
{
|
||||
switch (reg) {
|
||||
case DMA_CONFIGURATION:
|
||||
s->configuration = v;
|
||||
break;
|
||||
case DMA_CONTROL:
|
||||
switch(v & DMA_CONTROL_ACTION) {
|
||||
case DMA_CONTROL_ACTION_START:
|
||||
s->control |= DMA_CONTROL_RUNNING;
|
||||
s->control &= ~DMA_CONTROL_STOPPED;
|
||||
break;
|
||||
case DMA_CONTROL_ACTION_STOP:
|
||||
s->control |= DMA_CONTROL_STOPPED;
|
||||
s->control &= ~DMA_CONTROL_RUNNING;
|
||||
break;
|
||||
case DMA_CONTROL_ACTION_FREEZE:
|
||||
s->control |= DMA_CONTROL_FROZEN;
|
||||
break;
|
||||
case DMA_CONTROL_ACTION_UNFREEZE:
|
||||
s->control &= ~DMA_CONTROL_FROZEN;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
dsp_dma_run(s);
|
||||
break;
|
||||
case DMA_START_BLOCK:
|
||||
s->start_block = v;
|
||||
break;
|
||||
case DMA_NEXT_BLOCK:
|
||||
s->next_block = v;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* MCPX DSP DMA
|
||||
*
|
||||
* Copyright (c) 2015 espes
|
||||
*
|
||||
* 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 or
|
||||
* (at your option) version 3 of the License.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef DSP_DMA_H
|
||||
#define DSP_DMA_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "dsp.h"
|
||||
#include "dsp_cpu.h"
|
||||
|
||||
typedef enum DSPDMARegister {
|
||||
DMA_CONFIGURATION,
|
||||
DMA_CONTROL,
|
||||
DMA_START_BLOCK,
|
||||
DMA_NEXT_BLOCK,
|
||||
} DSPDMARegister;
|
||||
|
||||
typedef struct DSPDMAState {
|
||||
dsp_core_t* core;
|
||||
|
||||
void* scratch_rw_opaque;
|
||||
dsp_scratch_rw_func scratch_rw;
|
||||
|
||||
uint32_t configuration;
|
||||
uint32_t control;
|
||||
uint32_t start_block;
|
||||
uint32_t next_block;
|
||||
|
||||
bool error;
|
||||
bool eol;
|
||||
} DSPDMAState;
|
||||
|
||||
uint32_t dsp_dma_read(DSPDMAState *s, DSPDMARegister reg);
|
||||
void dsp_dma_write(DSPDMAState *s, DSPDMARegister reg, uint32_t v);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,338 @@
|
||||
/* g-lru-cache.c
|
||||
*
|
||||
* Copyright (C) 2009 - Christian Hergert
|
||||
*
|
||||
* This is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
/*
|
||||
* Ideally, you want to use fast_get. This is because we are using a
|
||||
* GStaticRWLock which is indeed slower than a mutex if you have lots of writer
|
||||
* acquisitions. This doesn't make it a true LRU, though, as the oldest
|
||||
* retrieval from strorage is the first item evicted.
|
||||
*/
|
||||
|
||||
#include "g-lru-cache.h"
|
||||
|
||||
// #define DEBUG
|
||||
|
||||
#define LRU_CACHE_PRIVATE(object) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE((object), \
|
||||
G_TYPE_LRU_CACHE, \
|
||||
GLruCachePrivate))
|
||||
|
||||
struct _GLruCachePrivate
|
||||
{
|
||||
GStaticRWLock rw_lock;
|
||||
guint max_size;
|
||||
gboolean fast_get;
|
||||
|
||||
GHashTable *hash_table;
|
||||
GEqualFunc key_equal_func;
|
||||
GCopyFunc key_copy_func;
|
||||
GList *newest;
|
||||
GList *oldest;
|
||||
|
||||
GLookupFunc retrieve_func;
|
||||
|
||||
gpointer user_data;
|
||||
GDestroyNotify user_destroy_func;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GLruCache, g_lru_cache, G_TYPE_OBJECT);
|
||||
|
||||
static void
|
||||
g_lru_cache_finalize (GObject *object)
|
||||
{
|
||||
GLruCachePrivate *priv = LRU_CACHE_PRIVATE (object);
|
||||
|
||||
if (priv->user_data && priv->user_destroy_func)
|
||||
priv->user_destroy_func (priv->user_data);
|
||||
|
||||
priv->user_data = NULL;
|
||||
priv->user_destroy_func = NULL;
|
||||
|
||||
g_hash_table_destroy (priv->hash_table);
|
||||
priv->hash_table = NULL;
|
||||
|
||||
g_list_free (priv->newest);
|
||||
priv->newest = NULL;
|
||||
priv->oldest = NULL;
|
||||
|
||||
G_OBJECT_CLASS (g_lru_cache_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
g_lru_cache_class_init (GLruCacheClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = g_lru_cache_finalize;
|
||||
|
||||
g_type_class_add_private (object_class, sizeof (GLruCachePrivate));
|
||||
}
|
||||
|
||||
static void
|
||||
g_lru_cache_init (GLruCache *self)
|
||||
{
|
||||
self->priv = LRU_CACHE_PRIVATE (self);
|
||||
|
||||
self->priv->max_size = 1024;
|
||||
self->priv->fast_get = FALSE;
|
||||
g_static_rw_lock_init (&self->priv->rw_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
g_lru_cache_evict_n_oldest_locked (GLruCache *self, gint n)
|
||||
{
|
||||
GList *victim;
|
||||
gint i;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
victim = self->priv->oldest;
|
||||
|
||||
if (victim == NULL)
|
||||
return;
|
||||
|
||||
if (victim->prev)
|
||||
victim->prev->next = NULL;
|
||||
|
||||
self->priv->oldest = victim->prev;
|
||||
g_hash_table_remove (self->priv->hash_table, victim->data);
|
||||
|
||||
if (self->priv->newest == victim)
|
||||
self->priv->newest = NULL;
|
||||
|
||||
g_list_free1 (victim); /* victim->data is owned by hashtable */
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
g_assert (g_hash_table_size (self->priv->hash_table) == g_list_length (self->priv->newest));
|
||||
#endif
|
||||
}
|
||||
|
||||
GLruCache*
|
||||
g_lru_cache_new (GHashFunc hash_func,
|
||||
GEqualFunc key_equal_func,
|
||||
GCopyFunc key_copy_func,
|
||||
GLookupFunc retrieve_func,
|
||||
GDestroyNotify key_destroy_func,
|
||||
GDestroyNotify value_destroy_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy_func)
|
||||
{
|
||||
GLruCache *self = g_object_new (G_TYPE_LRU_CACHE, NULL);
|
||||
|
||||
self->priv->hash_table = g_hash_table_new_full (hash_func,
|
||||
key_equal_func,
|
||||
key_destroy_func,
|
||||
value_destroy_func);
|
||||
|
||||
self->priv->key_equal_func = key_equal_func;
|
||||
self->priv->key_copy_func = key_copy_func;
|
||||
self->priv->retrieve_func = retrieve_func;
|
||||
self->priv->user_data = user_data;
|
||||
self->priv->user_destroy_func = user_destroy_func;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
g_lru_cache_set_max_size (GLruCache *self, guint max_size)
|
||||
{
|
||||
g_return_if_fail (G_IS_LRU_CACHE (self));
|
||||
|
||||
guint old_max_size = self->priv->max_size;
|
||||
|
||||
g_static_rw_lock_writer_lock (&(self->priv->rw_lock));
|
||||
|
||||
self->priv->max_size = max_size;
|
||||
|
||||
if (old_max_size > max_size)
|
||||
g_lru_cache_evict_n_oldest_locked (self, old_max_size - max_size);
|
||||
|
||||
g_static_rw_lock_writer_unlock (&(self->priv->rw_lock));
|
||||
}
|
||||
|
||||
guint
|
||||
g_lru_cache_get_max_size (GLruCache *self)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_LRU_CACHE (self), -1);
|
||||
return self->priv->max_size;
|
||||
}
|
||||
|
||||
guint
|
||||
g_lru_cache_get_size (GLruCache *self)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_LRU_CACHE (self), -1);
|
||||
return g_hash_table_size (self->priv->hash_table);
|
||||
}
|
||||
|
||||
gpointer
|
||||
g_lru_cache_get (GLruCache *self, gpointer key)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_LRU_CACHE (self), NULL);
|
||||
|
||||
gpointer value;
|
||||
|
||||
g_static_rw_lock_reader_lock (&(self->priv->rw_lock));
|
||||
|
||||
value = g_hash_table_lookup (self->priv->hash_table, key);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (value)
|
||||
g_debug ("Cache Hit!");
|
||||
else
|
||||
g_debug ("Cache miss");
|
||||
#endif
|
||||
|
||||
g_static_rw_lock_reader_unlock (&(self->priv->rw_lock));
|
||||
|
||||
if (!value)
|
||||
{
|
||||
g_static_rw_lock_writer_lock (&(self->priv->rw_lock));
|
||||
|
||||
if (!g_hash_table_lookup (self->priv->hash_table, key))
|
||||
{
|
||||
if (g_hash_table_size (self->priv->hash_table) >= self->priv->max_size)
|
||||
#ifdef DEBUG
|
||||
{
|
||||
g_debug ("We are at capacity, must evict oldest");
|
||||
#endif
|
||||
g_lru_cache_evict_n_oldest_locked (self, 1);
|
||||
#ifdef DEBUG
|
||||
}
|
||||
|
||||
g_debug ("Retrieving value from external resource");
|
||||
#endif
|
||||
|
||||
value = self->priv->retrieve_func (key, self->priv->user_data);
|
||||
|
||||
if (self->priv->key_copy_func)
|
||||
g_hash_table_insert (self->priv->hash_table,
|
||||
self->priv->key_copy_func (key, self->priv->user_data),
|
||||
value);
|
||||
else
|
||||
g_hash_table_insert (self->priv->hash_table, key, value);
|
||||
|
||||
self->priv->newest = g_list_prepend (self->priv->newest, key);
|
||||
|
||||
if (self->priv->oldest == NULL)
|
||||
self->priv->oldest = self->priv->newest;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
else g_debug ("Lost storage race with another thread");
|
||||
#endif
|
||||
|
||||
g_static_rw_lock_writer_unlock (&(self->priv->rw_lock));
|
||||
}
|
||||
|
||||
/* fast_get means that we do not reposition the item to the head
|
||||
* of the list. it essentially makes the lru, a lru from storage,
|
||||
* not lru to user.
|
||||
*/
|
||||
|
||||
else if (!self->priv->fast_get &&
|
||||
!self->priv->key_equal_func (key, self->priv->newest->data))
|
||||
{
|
||||
#ifdef DEBUG
|
||||
g_debug ("Making item most recent");
|
||||
#endif
|
||||
|
||||
g_static_rw_lock_writer_lock (&(self->priv->rw_lock));
|
||||
|
||||
GList *list = self->priv->newest;
|
||||
GList *tmp;
|
||||
GEqualFunc equal = self->priv->key_equal_func;
|
||||
|
||||
for (tmp = list; tmp; tmp = tmp->next)
|
||||
{
|
||||
if (equal (key, tmp->data))
|
||||
{
|
||||
GList *tmp1 = g_list_remove_link (list, tmp);
|
||||
self->priv->newest = g_list_prepend (tmp1, tmp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_static_rw_lock_writer_unlock (&(self->priv->rw_lock));
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void
|
||||
g_lru_cache_evict (GLruCache *self, gpointer key)
|
||||
{
|
||||
g_return_if_fail (G_IS_LRU_CACHE (self));
|
||||
|
||||
GEqualFunc equal = self->priv->key_equal_func;
|
||||
GList *list = NULL;
|
||||
|
||||
g_static_rw_lock_writer_lock (&(self->priv->rw_lock));
|
||||
|
||||
if (equal (key, self->priv->oldest))
|
||||
{
|
||||
g_lru_cache_evict_n_oldest_locked (self, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (list = self->priv->newest; list; list = list->next)
|
||||
{
|
||||
/* key, list->data is owned by hashtable */
|
||||
if (equal (key, list->data))
|
||||
{
|
||||
self->priv->newest = g_list_remove_link (self->priv->newest, list);
|
||||
g_list_free (list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_hash_table_remove (self->priv->hash_table, key);
|
||||
}
|
||||
|
||||
g_static_rw_lock_writer_unlock (&(self->priv->rw_lock));
|
||||
}
|
||||
|
||||
void
|
||||
g_lru_cache_clear (GLruCache *self)
|
||||
{
|
||||
g_return_if_fail (G_IS_LRU_CACHE (self));
|
||||
|
||||
g_static_rw_lock_writer_lock (&(self->priv->rw_lock));
|
||||
|
||||
g_hash_table_remove_all (self->priv->hash_table);
|
||||
g_list_free (self->priv->newest);
|
||||
|
||||
self->priv->oldest = NULL;
|
||||
self->priv->newest = NULL;
|
||||
|
||||
g_static_rw_lock_writer_unlock (&(self->priv->rw_lock));
|
||||
}
|
||||
|
||||
void
|
||||
g_lru_cache_set_fast_get (GLruCache *self, gboolean fast_get)
|
||||
{
|
||||
g_return_if_fail (G_IS_LRU_CACHE (self));
|
||||
self->priv->fast_get = fast_get;
|
||||
}
|
||||
|
||||
gboolean
|
||||
g_lru_cache_get_fast_get (GLruCache *self)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_LRU_CACHE (self), FALSE);
|
||||
return self->priv->fast_get;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/* g-lru-cache.h
|
||||
*
|
||||
* Copyright (C) 2009 - Christian Hergert
|
||||
*
|
||||
* This is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef __G_LRU_CACHE_H__
|
||||
#define __G_LRU_CACHE_H__
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib-object.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define G_TYPE_LRU_CACHE (g_lru_cache_get_type ())
|
||||
#define G_LRU_CACHE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_LRU_CACHE, GLruCache))
|
||||
#define G_LRU_CACHE_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_LRU_CACHE, GLruCache const))
|
||||
#define G_LRU_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_LRU_CACHE, GLruCacheClass))
|
||||
#define G_IS_LRU_CACHE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_LRU_CACHE))
|
||||
#define G_IS_LRU_CACHE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_LRU_CACHE))
|
||||
#define G_LRU_CACHE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_LRU_CACHE, GLruCacheClass))
|
||||
#define G_LOOKUP_FUNC(func) ((GLookupFunc)func)
|
||||
|
||||
typedef struct _GLruCache GLruCache;
|
||||
typedef struct _GLruCacheClass GLruCacheClass;
|
||||
typedef struct _GLruCachePrivate GLruCachePrivate;
|
||||
|
||||
typedef gpointer (*GLookupFunc) (gpointer key, gpointer user_data);
|
||||
|
||||
struct _GLruCache
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
GLruCachePrivate *priv;
|
||||
};
|
||||
|
||||
struct _GLruCacheClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
GType g_lru_cache_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GLruCache* g_lru_cache_new (GHashFunc hash_func,
|
||||
GEqualFunc key_equal_func,
|
||||
GCopyFunc key_copy_func,
|
||||
GLookupFunc retrieve_func,
|
||||
GDestroyNotify key_destroy_func,
|
||||
GDestroyNotify value_destroy_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy_func);
|
||||
|
||||
void g_lru_cache_set_max_size (GLruCache *self, guint max_size);
|
||||
guint g_lru_cache_get_max_size (GLruCache *self);
|
||||
|
||||
guint g_lru_cache_get_size (GLruCache *self);
|
||||
|
||||
gpointer g_lru_cache_get (GLruCache *self, gpointer key);
|
||||
void g_lru_cache_evict (GLruCache *self, gpointer key);
|
||||
void g_lru_cache_clear (GLruCache *self);
|
||||
|
||||
gboolean g_lru_cache_get_fast_get (GLruCache *self);
|
||||
void g_lru_cache_set_fast_get (GLruCache *self, gboolean fast_get);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __G_LRU_CACHE_H__ */
|
||||
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* QEMU SMSC LPC47M157 (Super I/O)
|
||||
*
|
||||
* Copyright (c) 2013 espes
|
||||
*
|
||||
* 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 or
|
||||
* (at your option) version 3 of the License.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "hw/isa/isa.h"
|
||||
#include "hw/char/serial.h"
|
||||
#include "sysemu/sysemu.h"
|
||||
#include "sysemu/char.h"
|
||||
#include "qapi/qmp/qerror.h"
|
||||
|
||||
#define MAX_DEVICE 0xC
|
||||
#define DEVICE_FDD 0x0
|
||||
#define DEVICE_PARALLEL_PORT 0x3
|
||||
#define DEVICE_SERIAL_PORT_1 0x4
|
||||
#define DEVICE_SERIAL_PORT_2 0x5
|
||||
#define DEVICE_KEYBOARD 0x7
|
||||
#define DEVICE_GAME_PORT 0x9
|
||||
#define DEVICE_PME 0xA
|
||||
#define DEVICE_MPU_401 0xB
|
||||
|
||||
#define ENTER_CONFIG_KEY 0x55
|
||||
#define EXIT_CONFIG_KEY 0xAA
|
||||
|
||||
#define MAX_CONFIG_REG 0x30
|
||||
#define MAX_DEVICE_REGS 0xFF
|
||||
|
||||
#define CONFIG_DEVICE_NUMBER 0x07
|
||||
#define CONFIG_PORT_LOW 0x26
|
||||
#define CONFIG_PORT_HIGH 0x27
|
||||
|
||||
#define CONFIG_DEVICE_ACTIVATE 0x30
|
||||
#define CONFIG_DEVICE_BASE_ADDRESS_HIGH 0x60
|
||||
#define CONFIG_DEVICE_BASE_ADDRESS_LOW 0x61
|
||||
#define CONFIG_DEVICE_INETRRUPT 0x70
|
||||
|
||||
#define DEBUG_LPC47M157
|
||||
|
||||
typedef struct LPC47M157State {
|
||||
ISADevice dev;
|
||||
|
||||
MemoryRegion io;
|
||||
|
||||
bool configuration_mode;
|
||||
uint32_t selected_reg;
|
||||
|
||||
uint8_t config_regs[MAX_CONFIG_REG];
|
||||
uint8_t device_regs[MAX_DEVICE][MAX_DEVICE_REGS];
|
||||
|
||||
struct {
|
||||
bool active;
|
||||
SerialState state;
|
||||
} serial[2];
|
||||
} LPC47M157State;
|
||||
|
||||
#define LPC47M157_DEVICE(obj) \
|
||||
OBJECT_CHECK(LPC47M157State, (obj), "lpc47m157")
|
||||
|
||||
static void update_devices(LPC47M157State *s)
|
||||
{
|
||||
ISADevice *isadev = ISA_DEVICE(s);
|
||||
|
||||
/* init serial devices */
|
||||
int i;
|
||||
for (i=0; i<2; i++) {
|
||||
uint8_t *dev = s->device_regs[DEVICE_SERIAL_PORT_1 + i];
|
||||
if (dev[CONFIG_DEVICE_ACTIVATE] && !s->serial[i].active) {
|
||||
|
||||
uint32_t iobase = (dev[CONFIG_DEVICE_BASE_ADDRESS_HIGH] << 8)
|
||||
| dev[CONFIG_DEVICE_BASE_ADDRESS_LOW];
|
||||
uint32_t irq = dev[CONFIG_DEVICE_INETRRUPT];
|
||||
|
||||
SerialState *ss = &s->serial[i].state;
|
||||
if (irq != 0) {
|
||||
isa_init_irq(isadev, &ss->irq, irq);
|
||||
}
|
||||
isa_register_ioport(isadev, &ss->io, iobase);
|
||||
|
||||
s->serial[i].active = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void lpc47m157_io_write(void *opaque, hwaddr addr, uint64_t val,
|
||||
unsigned int size)
|
||||
{
|
||||
LPC47M157State *s = opaque;
|
||||
|
||||
#ifdef DEBUG_LPC47M157
|
||||
printf("lpc47m157 io write 0x%llx = 0x%llx\n", addr, val);
|
||||
#endif
|
||||
|
||||
if (addr == 0) { //INDEX_PORT
|
||||
if (val == ENTER_CONFIG_KEY) {
|
||||
assert(!s->configuration_mode);
|
||||
s->configuration_mode = true;
|
||||
} else if (val == EXIT_CONFIG_KEY) {
|
||||
assert(s->configuration_mode);
|
||||
s->configuration_mode = false;
|
||||
|
||||
update_devices(s);
|
||||
} else {
|
||||
s->selected_reg = val;
|
||||
}
|
||||
} else if (addr == 1) { //DATA_PORT
|
||||
if (s->selected_reg < MAX_CONFIG_REG) {
|
||||
/* global configuration register */
|
||||
s->config_regs[s->selected_reg] = val;
|
||||
} else {
|
||||
/* device register */
|
||||
assert(s->config_regs[CONFIG_DEVICE_NUMBER] < MAX_DEVICE);
|
||||
uint8_t* dev = s->device_regs[s->config_regs[CONFIG_DEVICE_NUMBER]];
|
||||
dev[s->selected_reg] = val;
|
||||
#ifdef DEBUG_LPC47M157
|
||||
printf("lpc47m157 dev %x . %x = %llx\n", s->config_regs[CONFIG_DEVICE_NUMBER], s->selected_reg, val);
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t lpc47m157_io_read(void *opaque, hwaddr addr, unsigned int size)
|
||||
{
|
||||
LPC47M157State *s = opaque;
|
||||
uint32_t val = 0;
|
||||
|
||||
if (addr == 0) { //INDEX_PORT
|
||||
|
||||
} else if (addr == 1) { //DATA_PORT
|
||||
if (s->selected_reg < MAX_CONFIG_REG) {
|
||||
val = s->config_regs[s->selected_reg];
|
||||
} else {
|
||||
assert(s->config_regs[CONFIG_DEVICE_NUMBER] < MAX_DEVICE);
|
||||
uint8_t* dev = s->device_regs[s->config_regs[CONFIG_DEVICE_NUMBER]];
|
||||
val = dev[s->selected_reg];
|
||||
}
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_LPC47M157
|
||||
printf("lpc47m157 io read 0x%llx -> 0x%x\n", addr, val);
|
||||
#endif
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static const MemoryRegionOps lpc47m157_io_ops = {
|
||||
.read = lpc47m157_io_read,
|
||||
.write = lpc47m157_io_write,
|
||||
.valid = {
|
||||
.min_access_size = 1,
|
||||
.max_access_size = 1,
|
||||
},
|
||||
};
|
||||
|
||||
static void lpc47m157_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
LPC47M157State *s = LPC47M157_DEVICE(dev);
|
||||
ISADevice *isa = ISA_DEVICE(dev);
|
||||
|
||||
const uint32_t iobase = 0x2e; //0x4e if SYSOPT pin, make it a property
|
||||
s->config_regs[CONFIG_PORT_LOW] = iobase & 0xFF;
|
||||
s->config_regs[CONFIG_PORT_HIGH] = iobase >> 8;
|
||||
|
||||
memory_region_init_io(&s->io, OBJECT(s),
|
||||
&lpc47m157_io_ops, s, "lpc47m157", 2);
|
||||
isa_register_ioport(isa, &s->io, iobase);
|
||||
|
||||
/* init serial cores */
|
||||
int i;
|
||||
for (i=0; i<2; i++) {
|
||||
CharDriverState *chr = serial_hds[i];
|
||||
if (chr == NULL) {
|
||||
char name[5];
|
||||
snprintf(name, sizeof(name), "ser%d", i);
|
||||
chr = qemu_chr_new(name, "null", NULL);
|
||||
}
|
||||
|
||||
SerialState *ss = &s->serial[i].state;
|
||||
ss->chr = chr;
|
||||
ss->baudbase = 115200;
|
||||
|
||||
Error *err = NULL;
|
||||
serial_realize_core(ss, &err);
|
||||
if (err != NULL) {
|
||||
qerror_report_err(err);
|
||||
error_free(err);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memory_region_init_io(&ss->io, OBJECT(s),
|
||||
&serial_io_ops, ss, "serial", 8);
|
||||
}
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_lpc47m157= {
|
||||
.name = "lpc47m157",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField[]) {
|
||||
VMSTATE_STRUCT(serial[0].state, LPC47M157State, 0,
|
||||
vmstate_serial, SerialState),
|
||||
VMSTATE_STRUCT(serial[1].state, LPC47M157State, 0,
|
||||
vmstate_serial, SerialState),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static void lpc47m157_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
|
||||
dc->realize = lpc47m157_realize;
|
||||
dc->vmsd = &vmstate_lpc47m157;
|
||||
//dc->reset = pc87312_reset;
|
||||
//dc->props = pc87312_properties;
|
||||
}
|
||||
|
||||
static const TypeInfo lpc47m157_type_info = {
|
||||
.name = "lpc47m157",
|
||||
.parent = TYPE_ISA_DEVICE,
|
||||
.instance_size = sizeof(LPC47M157State),
|
||||
.class_init = lpc47m157_class_init,
|
||||
};
|
||||
|
||||
static void lpc47m157_register_types(void)
|
||||
{
|
||||
type_register_static(&lpc47m157_type_info);
|
||||
}
|
||||
|
||||
type_init(lpc47m157_register_types)
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* QEMU MCPX Audio Codec Interface implementation
|
||||
*
|
||||
* Copyright (c) 2012 espes
|
||||
*
|
||||
* 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 or
|
||||
* (at your option) version 3 of the License.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "hw/hw.h"
|
||||
#include "hw/i386/pc.h"
|
||||
#include "hw/pci/pci.h"
|
||||
#include "hw/audio/ac97_int.h"
|
||||
|
||||
typedef struct MCPXACIState {
|
||||
PCIDevice dev;
|
||||
|
||||
AC97LinkState ac97;
|
||||
|
||||
|
||||
MemoryRegion io_nam, io_nabm;
|
||||
|
||||
MemoryRegion mmio;
|
||||
MemoryRegion nam_mmio, nabm_mmio;
|
||||
} MCPXACIState;
|
||||
|
||||
|
||||
#define MCPX_ACI_DEVICE(obj) \
|
||||
OBJECT_CHECK(MCPXACIState, (obj), "mcpx-aci")
|
||||
|
||||
|
||||
static int mcpx_aci_initfn(PCIDevice *dev)
|
||||
{
|
||||
MCPXACIState *d = MCPX_ACI_DEVICE(dev);
|
||||
|
||||
dev->config[PCI_INTERRUPT_PIN] = 0x01;
|
||||
|
||||
//mmio
|
||||
memory_region_init(&d->mmio, OBJECT(dev), "mcpx-aci-mmio", 0x1000);
|
||||
|
||||
memory_region_init_io(&d->io_nam, OBJECT(dev), &ac97_io_nam_ops, &d->ac97,
|
||||
"mcpx-aci-nam", 0x100);
|
||||
memory_region_init_io(&d->io_nabm, OBJECT(dev), &ac97_io_nabm_ops, &d->ac97,
|
||||
"mcpx-aci-nabm", 0x80);
|
||||
|
||||
/*pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &d->io_nam);
|
||||
pci_register_bar(&d->dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->io_nabm);
|
||||
|
||||
memory_region_init_alias(&d->nam_mmio, NULL, &d->io_nam, 0, 0x100);
|
||||
memory_region_add_subregion(&d->mmio, 0x0, &d->nam_mmio);
|
||||
|
||||
memory_region_init_alias(&d->nabm_mmio, NULL, &d->io_nabm, 0, 0x80);
|
||||
memory_region_add_subregion(&d->mmio, 0x100, &d->nabm_mmio);*/
|
||||
|
||||
memory_region_add_subregion(&d->mmio, 0x0, &d->io_nam);
|
||||
memory_region_add_subregion(&d->mmio, 0x100, &d->io_nabm);
|
||||
|
||||
pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
|
||||
|
||||
ac97_common_init(&d->ac97, &d->dev, pci_get_address_space(&d->dev));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mcpx_aci_class_init(ObjectClass *klass, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(klass);
|
||||
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
|
||||
|
||||
k->vendor_id = PCI_VENDOR_ID_NVIDIA;
|
||||
k->device_id = PCI_DEVICE_ID_NVIDIA_MCPX_ACI;
|
||||
k->revision = 210;
|
||||
k->class_id = PCI_CLASS_MULTIMEDIA_AUDIO;
|
||||
k->init = mcpx_aci_initfn;
|
||||
|
||||
dc->desc = "MCPX Audio Codec Interface";
|
||||
}
|
||||
|
||||
static const TypeInfo mcpx_aci_info = {
|
||||
.name = "mcpx-aci",
|
||||
.parent = TYPE_PCI_DEVICE,
|
||||
.instance_size = sizeof(MCPXACIState),
|
||||
.class_init = mcpx_aci_class_init,
|
||||
};
|
||||
|
||||
static void mcpx_aci_register(void)
|
||||
{
|
||||
type_register_static(&mcpx_aci_info);
|
||||
}
|
||||
type_init(mcpx_aci_register);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user