Add hw/xbox sources from XQEMU 1.x @ 4d9107e

This commit is contained in:
Matt Borgerson 2018-03-24 01:04:15 -07:00
parent 4743c23509
commit 3f557e6d45
44 changed files with 29269 additions and 0 deletions

12
hw/xbox/Makefile.objs Normal file
View File

@ -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/

118
hw/xbox/acpi_xbox.c Normal file
View File

@ -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;
}

38
hw/xbox/acpi_xbox.h Normal file
View File

@ -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

256
hw/xbox/amd_smbus.c Normal file
View File

@ -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;
}

45
hw/xbox/amd_smbus.h Normal file
View File

@ -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 */

331
hw/xbox/chihiro-usb.c Normal file
View File

@ -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)

292
hw/xbox/chihiro.c Normal file
View File

@ -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);

View File

@ -0,0 +1 @@
obj-y += dsp.o dsp_cpu.o dsp_dma.o

476
hw/xbox/dsp/dsp.c Normal file
View File

@ -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;
}

59
hw/xbox/dsp/dsp.h Normal file
View File

@ -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 */

1431
hw/xbox/dsp/dsp_cpu.c Normal file

File diff suppressed because it is too large Load Diff

257
hw/xbox/dsp/dsp_cpu.h Normal file
View File

@ -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 */

2217
hw/xbox/dsp/dsp_dis.inl Normal file

File diff suppressed because it is too large Load Diff

247
hw/xbox/dsp/dsp_dma.c Normal file
View File

@ -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);
}
}

54
hw/xbox/dsp/dsp_dma.h Normal file
View File

@ -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

8043
hw/xbox/dsp/dsp_emu.inl Normal file

File diff suppressed because it is too large Load Diff

338
hw/xbox/g-lru-cache.c Normal file
View File

@ -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;
}

80
hw/xbox/g-lru-cache.h Normal file
View File

@ -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__ */

247
hw/xbox/lpc47m157.c Normal file
View File

@ -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)

99
hw/xbox/mcpx_aci.c Normal file
View File

@ -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);

651
hw/xbox/mcpx_apu.c Normal file
View File

@ -0,0 +1,651 @@
/*
* QEMU MCPX Audio Processing Unit 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/xbox/dsp/dsp.h"
#define NV_PAPU_ISTS 0x00001000
# define NV_PAPU_ISTS_GINTSTS (1 << 0)
# define NV_PAPU_ISTS_FETINTSTS (1 << 4)
#define NV_PAPU_IEN 0x00001004
#define NV_PAPU_FECTL 0x00001100
# define NV_PAPU_FECTL_FEMETHMODE 0x000000E0
# define NV_PAPU_FECTL_FEMETHMODE_FREE_RUNNING 0x00000000
# define NV_PAPU_FECTL_FEMETHMODE_HALTED 0x00000080
# define NV_PAPU_FECTL_FEMETHMODE_TRAPPED 0x000000E0
# define NV_PAPU_FECTL_FETRAPREASON 0x00000F00
# define NV_PAPU_FECTL_FETRAPREASON_REQUESTED 0x00000F00
#define NV_PAPU_FECV 0x00001110
#define NV_PAPU_FEAV 0x00001118
# define NV_PAPU_FEAV_VALUE 0x0000FFFF
# define NV_PAPU_FEAV_LST 0x00030000
#define NV_PAPU_FEDECMETH 0x00001300
#define NV_PAPU_FEDECPARAM 0x00001304
#define NV_PAPU_FEMEMADDR 0x00001324
#define NV_PAPU_FEMEMDATA 0x00001334
#define NV_PAPU_FETFORCE0 0x00001500
#define NV_PAPU_FETFORCE1 0x00001504
# define NV_PAPU_FETFORCE1_SE2FE_IDLE_VOICE (1 << 15)
#define NV_PAPU_SECTL 0x00002000
# define NV_PAPU_SECTL_XCNTMODE 0x00000018
# define NV_PAPU_SECTL_XCNTMODE_OFF 0
#define NV_PAPU_XGSCNT 0x0000200C
#define NV_PAPU_VPVADDR 0x0000202C
#define NV_PAPU_GPSADDR 0x00002040
#define NV_PAPU_EPSADDR 0x00002048
#define NV_PAPU_TVL2D 0x00002054
#define NV_PAPU_CVL2D 0x00002058
#define NV_PAPU_NVL2D 0x0000205C
#define NV_PAPU_TVL3D 0x00002060
#define NV_PAPU_CVL3D 0x00002064
#define NV_PAPU_NVL3D 0x00002068
#define NV_PAPU_TVLMP 0x0000206C
#define NV_PAPU_CVLMP 0x00002070
#define NV_PAPU_NVLMP 0x00002074
#define NV_PAPU_GPSMAXSGE 0x000020D4
#define NV_PAPU_EPSMAXSGE 0x000020DC
#define NV_PAPU_GPRST 0x0000FFFC
#define NV_PAPU_GPRST_GPRST (1 << 0)
#define NV_PAPU_GPRST_GPDSPRST (1 << 1)
#define NV_PAPU_GPRST_GPNMI (1 << 2)
#define NV_PAPU_GPRST_GPABORT (1 << 3)
#define NV_PAPU_EPXMEM 0x00000000
#define NV_PAPU_EPYMEM 0x00006000
#define NV_PAPU_EPPMEM 0x0000A000
#define NV_PAPU_EPRST 0x0000FFFC
static const struct {
hwaddr top, current, next;
} voice_list_regs[] = {
{NV_PAPU_TVL2D, NV_PAPU_CVL2D, NV_PAPU_NVL2D}, //2D
{NV_PAPU_TVL3D, NV_PAPU_CVL3D, NV_PAPU_NVL3D}, //3D
{NV_PAPU_TVLMP, NV_PAPU_CVLMP, NV_PAPU_NVLMP}, //MP
};
/* audio processor object / front-end messages */
#define NV1BA0_PIO_FREE 0x00000010
#define NV1BA0_PIO_SET_ANTECEDENT_VOICE 0x00000120
# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_HANDLE 0x0000FFFF
# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST 0x00030000
# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST_INHERIT 0
# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST_2D_TOP 1
# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST_3D_TOP 2
# define NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST_MP_TOP 3
#define NV1BA0_PIO_VOICE_ON 0x00000124
# define NV1BA0_PIO_VOICE_ON_HANDLE 0x0000FFFF
#define NV1BA0_PIO_VOICE_OFF 0x00000128
#define NV1BA0_PIO_VOICE_PAUSE 0x00000140
# define NV1BA0_PIO_VOICE_PAUSE_HANDLE 0x0000FFFF
# define NV1BA0_PIO_VOICE_PAUSE_ACTION (1 << 18)
#define NV1BA0_PIO_SET_CURRENT_VOICE 0x000002F8
#define SE2FE_IDLE_VOICE 0x00008000
/* voice structure */
#define NV_PAVS_SIZE 0x00000080
#define NV_PAVS_VOICE_PAR_STATE 0x00000054
# define NV_PAVS_VOICE_PAR_STATE_PAUSED (1 << 18)
# define NV_PAVS_VOICE_PAR_STATE_ACTIVE_VOICE (1 << 21)
#define NV_PAVS_VOICE_TAR_PITCH_LINK 0x0000007c
# define NV_PAVS_VOICE_TAR_PITCH_LINK_NEXT_VOICE_HANDLE 0x0000FFFF
#define MCPX_HW_MAX_VOICES 256
#define GET_MASK(v, mask) (((v) & (mask)) >> (ffs(mask)-1))
#define SET_MASK(v, mask, val) \
do { \
(v) &= ~(mask); \
(v) |= ((val) << (ffs(mask)-1)) & (mask); \
} while (0)
// #define MCPX_DEBUG
#ifdef MCPX_DEBUG
# define MCPX_DPRINTF(format, ...) printf(format, ## __VA_ARGS__)
#else
# define MCPX_DPRINTF(format, ...) do { } while (0)
#endif
typedef struct MCPXAPUState {
PCIDevice dev;
MemoryRegion mmio;
/* Setup Engine */
struct {
QEMUTimer *frame_timer;
} se;
/* Voice Processor */
struct {
MemoryRegion mmio;
} vp;
/* Global Processor */
struct {
MemoryRegion mmio;
DSPState *dsp;
uint32_t regs[0x10000];
} gp;
/* Encode Processor */
struct {
MemoryRegion mmio;
DSPState *dsp;
uint32_t regs[0x10000];
} ep;
uint32_t regs[0x20000];
} MCPXAPUState;
#define MCPX_APU_DEVICE(obj) \
OBJECT_CHECK(MCPXAPUState, (obj), "mcpx-apu")
static uint32_t voice_get_mask(MCPXAPUState *d,
unsigned int voice_handle,
hwaddr offset,
uint32_t mask)
{
assert(voice_handle < 0xFFFF);
hwaddr voice = d->regs[NV_PAPU_VPVADDR]
+ voice_handle * NV_PAVS_SIZE;
return (ldl_le_phys(voice + offset) & mask) >> (ffs(mask)-1);
}
static void voice_set_mask(MCPXAPUState *d,
unsigned int voice_handle,
hwaddr offset,
uint32_t mask,
uint32_t val)
{
assert(voice_handle < 0xFFFF);
hwaddr voice = d->regs[NV_PAPU_VPVADDR]
+ voice_handle * NV_PAVS_SIZE;
uint32_t v = ldl_le_phys(voice + offset) & ~mask;
stl_le_phys(voice + offset,
v | ((val << (ffs(mask)-1)) & mask));
}
static void update_irq(MCPXAPUState *d)
{
if ((d->regs[NV_PAPU_IEN] & NV_PAPU_ISTS_GINTSTS)
&& ((d->regs[NV_PAPU_ISTS] & ~NV_PAPU_ISTS_GINTSTS)
& d->regs[NV_PAPU_IEN])) {
d->regs[NV_PAPU_ISTS] |= NV_PAPU_ISTS_GINTSTS;
MCPX_DPRINTF("mcpx irq raise\n");
pci_irq_assert(&d->dev);
} else {
d->regs[NV_PAPU_ISTS] &= ~NV_PAPU_ISTS_GINTSTS;
MCPX_DPRINTF("mcpx irq lower\n");
pci_irq_deassert(&d->dev);
}
}
static uint64_t mcpx_apu_read(void *opaque,
hwaddr addr, unsigned int size)
{
MCPXAPUState *d = opaque;
uint64_t r = 0;
switch (addr) {
case NV_PAPU_XGSCNT:
r = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / 100; //???
break;
default:
if (addr < 0x20000) {
r = d->regs[addr];
}
break;
}
MCPX_DPRINTF("mcpx apu: read [0x%llx] -> 0x%llx\n", addr, r);
return r;
}
static void mcpx_apu_write(void *opaque, hwaddr addr,
uint64_t val, unsigned int size)
{
MCPXAPUState *d = opaque;
MCPX_DPRINTF("mcpx apu: [0x%llx] = 0x%llx\n", addr, val);
switch (addr) {
case NV_PAPU_ISTS:
/* the bits of the interrupts to clear are wrtten */
d->regs[NV_PAPU_ISTS] &= ~val;
update_irq(d);
break;
case NV_PAPU_SECTL:
if ( ((val & NV_PAPU_SECTL_XCNTMODE) >> 3)
== NV_PAPU_SECTL_XCNTMODE_OFF) {
timer_del(d->se.frame_timer);
} else {
timer_mod(d->se.frame_timer,
qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 10);
}
d->regs[addr] = val;
break;
case NV_PAPU_FEMEMDATA:
/* 'magic write'
* This value is expected to be written to FEMEMADDR on completion of
* something to do with notifies. Just do it now :/ */
stl_le_phys(d->regs[NV_PAPU_FEMEMADDR], val);
d->regs[addr] = val;
break;
default:
if (addr < 0x20000) {
d->regs[addr] = val;
}
break;
}
}
static const MemoryRegionOps mcpx_apu_mmio_ops = {
.read = mcpx_apu_read,
.write = mcpx_apu_write,
};
static void fe_method(MCPXAPUState *d,
uint32_t method, uint32_t argument)
{
MCPX_DPRINTF("mcpx fe_method 0x%x 0x%x\n", method, argument);
//assert((d->regs[NV_PAPU_FECTL] & NV_PAPU_FECTL_FEMETHMODE) == 0);
d->regs[NV_PAPU_FEDECMETH] = method;
d->regs[NV_PAPU_FEDECPARAM] = argument;
unsigned int selected_handle, list;
switch (method) {
case NV1BA0_PIO_SET_ANTECEDENT_VOICE:
d->regs[NV_PAPU_FEAV] = argument;
break;
case NV1BA0_PIO_VOICE_ON:
selected_handle = argument & NV1BA0_PIO_VOICE_ON_HANDLE;
list = GET_MASK(d->regs[NV_PAPU_FEAV], NV_PAPU_FEAV_LST);
if (list != NV1BA0_PIO_SET_ANTECEDENT_VOICE_LIST_INHERIT) {
/* voice is added to the top of the selected list */
unsigned int top_reg = voice_list_regs[list-1].top;
voice_set_mask(d, selected_handle,
NV_PAVS_VOICE_TAR_PITCH_LINK,
NV_PAVS_VOICE_TAR_PITCH_LINK_NEXT_VOICE_HANDLE,
d->regs[top_reg]);
d->regs[top_reg] = selected_handle;
} else {
unsigned int antecedent_voice =
GET_MASK(d->regs[NV_PAPU_FEAV], NV_PAPU_FEAV_VALUE);
/* voice is added after the antecedent voice */
assert(antecedent_voice != 0xFFFF);
uint32_t next_handle = voice_get_mask(d, antecedent_voice,
NV_PAVS_VOICE_TAR_PITCH_LINK,
NV_PAVS_VOICE_TAR_PITCH_LINK_NEXT_VOICE_HANDLE);
voice_set_mask(d, selected_handle,
NV_PAVS_VOICE_TAR_PITCH_LINK,
NV_PAVS_VOICE_TAR_PITCH_LINK_NEXT_VOICE_HANDLE,
next_handle);
voice_set_mask(d, antecedent_voice,
NV_PAVS_VOICE_TAR_PITCH_LINK,
NV_PAVS_VOICE_TAR_PITCH_LINK_NEXT_VOICE_HANDLE,
selected_handle);
voice_set_mask(d, selected_handle,
NV_PAVS_VOICE_PAR_STATE,
NV_PAVS_VOICE_PAR_STATE_ACTIVE_VOICE,
1);
}
break;
case NV1BA0_PIO_VOICE_OFF:
voice_set_mask(d, argument,
NV_PAVS_VOICE_PAR_STATE,
NV_PAVS_VOICE_PAR_STATE_ACTIVE_VOICE,
0);
break;
case NV1BA0_PIO_VOICE_PAUSE:
voice_set_mask(d, argument & NV1BA0_PIO_VOICE_PAUSE_HANDLE,
NV_PAVS_VOICE_PAR_STATE,
NV_PAVS_VOICE_PAR_STATE_PAUSED,
(argument & NV1BA0_PIO_VOICE_PAUSE_ACTION) != 0);
break;
case NV1BA0_PIO_SET_CURRENT_VOICE:
d->regs[NV_PAPU_FECV] = argument;
break;
case SE2FE_IDLE_VOICE:
if (d->regs[NV_PAPU_FETFORCE1] & NV_PAPU_FETFORCE1_SE2FE_IDLE_VOICE) {
d->regs[NV_PAPU_FECTL] &= ~NV_PAPU_FECTL_FEMETHMODE;
d->regs[NV_PAPU_FECTL] |= NV_PAPU_FECTL_FEMETHMODE_TRAPPED;
d->regs[NV_PAPU_FECTL] &= ~NV_PAPU_FECTL_FETRAPREASON;
d->regs[NV_PAPU_FECTL] |= NV_PAPU_FECTL_FETRAPREASON_REQUESTED;
d->regs[NV_PAPU_ISTS] |= NV_PAPU_ISTS_FETINTSTS;
update_irq(d);
} else {
assert(false);
}
break;
default:
assert(false);
break;
}
}
static uint64_t vp_read(void *opaque,
hwaddr addr, unsigned int size)
{
MCPX_DPRINTF("mcpx apu VP: read [0x%llx]\n", addr);
switch (addr) {
case NV1BA0_PIO_FREE:
/* we don't simulate the queue for now,
* pretend to always be empty */
return 0x80;
default:
break;
}
return 0;
}
static void vp_write(void *opaque, hwaddr addr,
uint64_t val, unsigned int size)
{
MCPXAPUState *d = opaque;
MCPX_DPRINTF("mcpx apu VP: [0x%llx] = 0x%llx\n", addr, val);
switch (addr) {
case NV1BA0_PIO_SET_ANTECEDENT_VOICE:
case NV1BA0_PIO_VOICE_ON:
case NV1BA0_PIO_VOICE_OFF:
case NV1BA0_PIO_VOICE_PAUSE:
case NV1BA0_PIO_SET_CURRENT_VOICE:
/* TODO: these should instead be queueing up fe commands */
fe_method(d, addr, val);
break;
default:
break;
}
}
static const MemoryRegionOps vp_ops = {
.read = vp_read,
.write = vp_write,
};
static void scratch_rw(hwaddr sge_base, unsigned int max_sge,
uint8_t* ptr, uint32_t addr, size_t len, bool dir)
{
int i;
for (i=0; i<len; i++) {
unsigned int entry = (addr + i) / TARGET_PAGE_SIZE;
assert(entry < max_sge);
uint32_t prd_address = ldl_le_phys(sge_base + entry*4*2);
uint32_t prd_control = ldl_le_phys(sge_base + entry*4*2 + 1);
hwaddr paddr = prd_address + (addr + i) % TARGET_PAGE_SIZE;
if (dir) {
stb_phys(paddr, ptr[i]);
} else {
ptr[i] = ldub_phys(paddr);
}
}
}
static void gp_scratch_rw(void *opaque, uint8_t* ptr, uint32_t addr, size_t len, bool dir)
{
MCPXAPUState *d = opaque;
scratch_rw(d->regs[NV_PAPU_GPSADDR], d->regs[NV_PAPU_GPSMAXSGE],
ptr, addr, len, dir);
}
static void ep_scratch_rw(void *opaque, uint8_t* ptr, uint32_t addr, size_t len, bool dir)
{
MCPXAPUState *d = opaque;
scratch_rw(d->regs[NV_PAPU_EPSADDR], d->regs[NV_PAPU_EPSMAXSGE],
ptr, addr, len, dir);
}
static void proc_rst_write(DSPState *dsp, uint32_t oldval, uint32_t val)
{
if (!(val & NV_PAPU_GPRST_GPRST) || !(val & NV_PAPU_GPRST_GPDSPRST)) {
dsp_reset(dsp);
} else if ((!(oldval & NV_PAPU_GPRST_GPRST)
|| !(oldval & NV_PAPU_GPRST_GPDSPRST))
&& ((val & NV_PAPU_GPRST_GPRST) && (val & NV_PAPU_GPRST_GPDSPRST)) ) {
dsp_bootstrap(dsp);
}
}
/* Global Processor - programmable DSP */
static uint64_t gp_read(void *opaque,
hwaddr addr, unsigned int size)
{
MCPXAPUState *d = opaque;
uint64_t r = d->gp.regs[addr];
MCPX_DPRINTF("mcpx apu GP: read [0x%llx] -> 0x%llx\n", addr, r);
return r;
}
static void gp_write(void *opaque, hwaddr addr,
uint64_t val, unsigned int size)
{
MCPXAPUState *d = opaque;
MCPX_DPRINTF("mcpx apu GP: [0x%llx] = 0x%llx\n", addr, val);
switch (addr) {
case NV_PAPU_GPRST:
proc_rst_write(d->gp.dsp, d->gp.regs[NV_PAPU_GPRST], val);
d->gp.regs[NV_PAPU_GPRST] = val;
break;
default:
d->gp.regs[addr] = val;
break;
}
}
static const MemoryRegionOps gp_ops = {
.read = gp_read,
.write = gp_write,
};
/* Encode Processor - encoding DSP */
static uint64_t ep_read(void *opaque,
hwaddr addr, unsigned int size)
{
MCPXAPUState *d = opaque;
uint64_t r = 0;
switch (addr) {
case NV_PAPU_EPXMEM ... NV_PAPU_EPXMEM + 0xC00*4: {
uint32_t xaddr = (addr - NV_PAPU_EPXMEM) / 4;
r = dsp_read_memory(d->ep.dsp, 'X', xaddr);
break;
}
case NV_PAPU_EPYMEM ... NV_PAPU_EPYMEM + 0x100*4: {
uint32_t yaddr = (addr - NV_PAPU_EPYMEM) / 4;
r = dsp_read_memory(d->ep.dsp, 'Y', yaddr);
break;
}
case NV_PAPU_EPPMEM ... NV_PAPU_EPPMEM + 0x1000*4: {
uint32_t paddr = (addr - NV_PAPU_EPPMEM) / 4;
r = dsp_read_memory(d->ep.dsp, 'P', paddr);
break;
}
default:
r = d->ep.regs[addr];
break;
}
MCPX_DPRINTF("mcpx apu EP: read [0x%llx] -> 0x%llx\n", addr, r);
return r;
}
static void ep_write(void *opaque, hwaddr addr,
uint64_t val, unsigned int size)
{
MCPXAPUState *d = opaque;
MCPX_DPRINTF("mcpx apu EP: [0x%llx] = 0x%llx\n", addr, val);
switch (addr) {
case NV_PAPU_EPXMEM ... NV_PAPU_EPXMEM + 0xC00*4: {
assert(false);
break;
}
case NV_PAPU_EPYMEM ... NV_PAPU_EPYMEM + 0x100*4: {
assert(false);
break;
}
case NV_PAPU_EPPMEM ... NV_PAPU_EPPMEM + 0x1000*4: {
assert(false);
break;
}
case NV_PAPU_EPRST:
proc_rst_write(d->ep.dsp, d->ep.regs[NV_PAPU_EPRST], val);
d->ep.regs[NV_PAPU_EPRST] = val;
break;
default:
d->ep.regs[addr] = val;
break;
}
}
static const MemoryRegionOps ep_ops = {
.read = ep_read,
.write = ep_write,
};
/* TODO: this should be on a thread so it waits on the voice lock */
static void se_frame(void *opaque)
{
MCPXAPUState *d = opaque;
timer_mod(d->se.frame_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 10);
MCPX_DPRINTF("mcpx frame ping\n");
int list;
for (list=0; list < 3; list++) {
hwaddr top, current, next;
top = voice_list_regs[list].top;
current = voice_list_regs[list].current;
next = voice_list_regs[list].next;
d->regs[current] = d->regs[top];
MCPX_DPRINTF("list %d current voice %d\n", list, d->regs[current]);
while (d->regs[current] != 0xFFFF) {
d->regs[next] = voice_get_mask(d, d->regs[current],
NV_PAVS_VOICE_TAR_PITCH_LINK,
NV_PAVS_VOICE_TAR_PITCH_LINK_NEXT_VOICE_HANDLE);
if (!voice_get_mask(d, d->regs[current],
NV_PAVS_VOICE_PAR_STATE,
NV_PAVS_VOICE_PAR_STATE_ACTIVE_VOICE)) {
MCPX_DPRINTF("voice %d not active...!\n", d->regs[current]);
fe_method(d, SE2FE_IDLE_VOICE, d->regs[current]);
}
MCPX_DPRINTF("next voice %d\n", d->regs[next]);
d->regs[current] = d->regs[next];
}
}
if ((d->gp.regs[NV_PAPU_GPRST] & NV_PAPU_GPRST_GPRST)
&& (d->gp.regs[NV_PAPU_GPRST] & NV_PAPU_GPRST_GPDSPRST)) {
dsp_start_frame(d->gp.dsp);
// hax
dsp_run(d->gp.dsp, 1000);
}
if ((d->ep.regs[NV_PAPU_EPRST] & NV_PAPU_GPRST_GPRST)
&& (d->ep.regs[NV_PAPU_EPRST] & NV_PAPU_GPRST_GPDSPRST)) {
dsp_start_frame(d->ep.dsp);
// hax
// dsp_run(d->ep.dsp, 1000);
}
}
static int mcpx_apu_initfn(PCIDevice *dev)
{
MCPXAPUState *d = MCPX_APU_DEVICE(dev);
dev->config[PCI_INTERRUPT_PIN] = 0x01;
memory_region_init_io(&d->mmio, OBJECT(dev), &mcpx_apu_mmio_ops, d,
"mcpx-apu-mmio", 0x80000);
memory_region_init_io(&d->vp.mmio, OBJECT(dev), &vp_ops, d,
"mcpx-apu-vp", 0x10000);
memory_region_add_subregion(&d->mmio, 0x20000, &d->vp.mmio);
memory_region_init_io(&d->gp.mmio, OBJECT(dev), &gp_ops, d,
"mcpx-apu-gp", 0x10000);
memory_region_add_subregion(&d->mmio, 0x30000, &d->gp.mmio);
memory_region_init_io(&d->ep.mmio, OBJECT(dev), &ep_ops, d,
"mcpx-apu-ep", 0x10000);
memory_region_add_subregion(&d->mmio, 0x50000, &d->ep.mmio);
pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio);
d->se.frame_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, se_frame, d);
d->gp.dsp = dsp_init(d, gp_scratch_rw);
d->ep.dsp = dsp_init(d, ep_scratch_rw);
return 0;
}
static void mcpx_apu_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_APU;
k->revision = 210;
k->class_id = PCI_CLASS_MULTIMEDIA_AUDIO;
k->init = mcpx_apu_initfn;
dc->desc = "MCPX Audio Processing Unit";
}
static const TypeInfo mcpx_apu_info = {
.name = "mcpx-apu",
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(MCPXAPUState),
.class_init = mcpx_apu_class_init,
};
static void mcpx_apu_register(void)
{
type_register_static(&mcpx_apu_info);
}
type_init(mcpx_apu_register);

6439
hw/xbox/nv2a.c Normal file

File diff suppressed because it is too large Load Diff

25
hw/xbox/nv2a.h Normal file
View File

@ -0,0 +1,25 @@
/*
* QEMU Geforce NV2A 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/>.
*/
#ifndef HW_NV2A_H
#define HW_NV2A_H
void nv2a_init(PCIBus *bus, int devfn, MemoryRegion *ram);
#endif

94
hw/xbox/nv2a_debug.c Normal file
View File

@ -0,0 +1,94 @@
/*
* QEMU Geforce NV2A debug helpers
*
* Copyright (c) 2015 Jannik Vogel
* 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/xbox/nv2a_debug.h"
#ifdef DEBUG_NV2A_GL
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include "gl/glextensions.h"
void gl_debug_message(bool cc, const char *fmt, ...)
{
size_t n;
char buffer[1024];
va_list ap;
va_start(ap, fmt);
n = vsnprintf(buffer, sizeof(buffer), fmt, ap);
assert(n <= sizeof(buffer));
va_end(ap);
if(glDebugMessageInsert) {
glDebugMessageInsert(GL_DEBUG_SOURCE_APPLICATION, GL_DEBUG_TYPE_MARKER,
0, GL_DEBUG_SEVERITY_NOTIFICATION, n, buffer);
}
if (cc) {
fwrite(buffer, sizeof(char), n, stdout);
fputc('\n', stdout);
}
}
void gl_debug_group_begin(const char *fmt, ...)
{
size_t n;
char buffer[1024];
va_list ap;
va_start(ap, fmt);
n = vsnprintf(buffer, sizeof(buffer), fmt, ap);
assert(n <= sizeof(buffer));
va_end(ap);
/* Check for errors before entering group */
assert(glGetError() == GL_NO_ERROR);
if (glPushDebugGroup) {
glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 0, n, buffer);
}
}
void gl_debug_group_end(void)
{
/* Check for errors when leaving group */
assert(glGetError() == GL_NO_ERROR);
if (glPopDebugGroup) {
glPopDebugGroup();
}
}
void gl_debug_label(GLenum target, GLuint name, const char *fmt, ...)
{
size_t n;
char buffer[1024];
va_list ap;
va_start(ap, fmt);
n = vsnprintf(buffer, sizeof(buffer), fmt, ap);
assert(n <= sizeof(buffer));
va_end(ap);
if (glObjectLabel) {
glObjectLabel(target, name, n, buffer);
}
}
#endif

60
hw/xbox/nv2a_debug.h Normal file
View File

@ -0,0 +1,60 @@
/*
* QEMU Geforce NV2A debug helpers
*
* Copyright (c) 2015 Jannik Vogel
* 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/>.
*/
#ifndef HW_NV2A_DEBUG_H
#define HW_NV2A_DEBUG_H
// #define DEBUG_NV2A
#ifdef DEBUG_NV2A
# define NV2A_DPRINTF(format, ...) printf("nv2a: " format, ## __VA_ARGS__)
#else
# define NV2A_DPRINTF(format, ...) do { } while (0)
#endif
// #define DEBUG_NV2A_GL
#ifdef DEBUG_NV2A_GL
#include <stdbool.h>
#include "gl/gloffscreen.h"
void gl_debug_message(bool cc, const char *fmt, ...);
void gl_debug_group_begin(const char *fmt, ...);
void gl_debug_group_end(void);
void gl_debug_label(GLenum target, GLuint name, const char *fmt, ...);
# define NV2A_GL_DPRINTF(cc, format, ...) \
gl_debug_message(cc, "nv2a: " format, ## __VA_ARGS__)
# define NV2A_GL_DGROUP_BEGIN(format, ...) \
gl_debug_group_begin("nv2a: " format, ## __VA_ARGS__)
# define NV2A_GL_DGROUP_END() \
gl_debug_group_end()
# define NV2A_GL_DLABEL(target, name, format, ...) \
gl_debug_label(target, name, "nv2a: { " format " }", ## __VA_ARGS__)
#else
# define NV2A_GL_DPRINTF(cc, format, ...) do { \
if (cc) NV2A_DPRINTF(format "\n", ##__VA_ARGS__ ); \
} while (0)
# define NV2A_GL_DGROUP_BEGIN(format, ...) do { } while (0)
# define NV2A_GL_DGROUP_END() do { } while (0)
# define NV2A_GL_DLABEL(target, name, format, ...) do { } while (0)
#endif
#endif

1284
hw/xbox/nv2a_int.h Normal file

File diff suppressed because it is too large Load Diff

847
hw/xbox/nv2a_psh.c Normal file
View File

@ -0,0 +1,847 @@
/*
* QEMU Geforce NV2A pixel shader translation
*
* Copyright (c) 2013 espes
* Copyright (c) 2015 Jannik Vogel
*
* Based on:
* Cxbx, PixelShader.cpp
* Copyright (c) 2004 Aaron Robinson <caustik@caustik.com>
* Kingofc <kingofc@freenet.de>
* Xeon, XBD3DPixelShader.cpp
* Copyright (c) 2003 _SF_
*
* 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include "qapi/qmp/qstring.h"
#include "hw/xbox/nv2a_shaders_common.h"
#include "hw/xbox/nv2a_psh.h"
/*
* This implements translation of register combiners into glsl
* fragment shaders, but all terminology is in terms of Xbox DirectX
* pixel shaders, since I wanted to be lazy while referencing existing
* work / stealing code.
*
* For some background, see the OpenGL extension:
* https://www.opengl.org/registry/specs/NV/register_combiners.txt
*/
enum PS_TEXTUREMODES
{ // valid in stage 0 1 2 3
PS_TEXTUREMODES_NONE= 0x00L, // * * * *
PS_TEXTUREMODES_PROJECT2D= 0x01L, // * * * *
PS_TEXTUREMODES_PROJECT3D= 0x02L, // * * * *
PS_TEXTUREMODES_CUBEMAP= 0x03L, // * * * *
PS_TEXTUREMODES_PASSTHRU= 0x04L, // * * * *
PS_TEXTUREMODES_CLIPPLANE= 0x05L, // * * * *
PS_TEXTUREMODES_BUMPENVMAP= 0x06L, // - * * *
PS_TEXTUREMODES_BUMPENVMAP_LUM= 0x07L, // - * * *
PS_TEXTUREMODES_BRDF= 0x08L, // - - * *
PS_TEXTUREMODES_DOT_ST= 0x09L, // - - * *
PS_TEXTUREMODES_DOT_ZW= 0x0aL, // - - * *
PS_TEXTUREMODES_DOT_RFLCT_DIFF= 0x0bL, // - - * -
PS_TEXTUREMODES_DOT_RFLCT_SPEC= 0x0cL, // - - - *
PS_TEXTUREMODES_DOT_STR_3D= 0x0dL, // - - - *
PS_TEXTUREMODES_DOT_STR_CUBE= 0x0eL, // - - - *
PS_TEXTUREMODES_DPNDNT_AR= 0x0fL, // - * * *
PS_TEXTUREMODES_DPNDNT_GB= 0x10L, // - * * *
PS_TEXTUREMODES_DOTPRODUCT= 0x11L, // - * * -
PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST= 0x12L, // - - - *
// 0x13-0x1f reserved
};
enum PS_INPUTMAPPING
{
PS_INPUTMAPPING_UNSIGNED_IDENTITY= 0x00L, // max(0,x) OK for final combiner
PS_INPUTMAPPING_UNSIGNED_INVERT= 0x20L, // 1 - max(0,x) OK for final combiner
PS_INPUTMAPPING_EXPAND_NORMAL= 0x40L, // 2*max(0,x) - 1 invalid for final combiner
PS_INPUTMAPPING_EXPAND_NEGATE= 0x60L, // 1 - 2*max(0,x) invalid for final combiner
PS_INPUTMAPPING_HALFBIAS_NORMAL= 0x80L, // max(0,x) - 1/2 invalid for final combiner
PS_INPUTMAPPING_HALFBIAS_NEGATE= 0xa0L, // 1/2 - max(0,x) invalid for final combiner
PS_INPUTMAPPING_SIGNED_IDENTITY= 0xc0L, // x invalid for final combiner
PS_INPUTMAPPING_SIGNED_NEGATE= 0xe0L, // -x invalid for final combiner
};
enum PS_REGISTER
{
PS_REGISTER_ZERO= 0x00L, // r
PS_REGISTER_DISCARD= 0x00L, // w
PS_REGISTER_C0= 0x01L, // r
PS_REGISTER_C1= 0x02L, // r
PS_REGISTER_FOG= 0x03L, // r
PS_REGISTER_V0= 0x04L, // r/w
PS_REGISTER_V1= 0x05L, // r/w
PS_REGISTER_T0= 0x08L, // r/w
PS_REGISTER_T1= 0x09L, // r/w
PS_REGISTER_T2= 0x0aL, // r/w
PS_REGISTER_T3= 0x0bL, // r/w
PS_REGISTER_R0= 0x0cL, // r/w
PS_REGISTER_R1= 0x0dL, // r/w
PS_REGISTER_V1R0_SUM= 0x0eL, // r
PS_REGISTER_EF_PROD= 0x0fL, // r
PS_REGISTER_ONE= PS_REGISTER_ZERO | PS_INPUTMAPPING_UNSIGNED_INVERT, // OK for final combiner
PS_REGISTER_NEGATIVE_ONE= PS_REGISTER_ZERO | PS_INPUTMAPPING_EXPAND_NORMAL, // invalid for final combiner
PS_REGISTER_ONE_HALF= PS_REGISTER_ZERO | PS_INPUTMAPPING_HALFBIAS_NEGATE, // invalid for final combiner
PS_REGISTER_NEGATIVE_ONE_HALF= PS_REGISTER_ZERO | PS_INPUTMAPPING_HALFBIAS_NORMAL, // invalid for final combiner
};
enum PS_COMBINERCOUNTFLAGS
{
PS_COMBINERCOUNT_MUX_LSB= 0x0000L, // mux on r0.a lsb
PS_COMBINERCOUNT_MUX_MSB= 0x0001L, // mux on r0.a msb
PS_COMBINERCOUNT_SAME_C0= 0x0000L, // c0 same in each stage
PS_COMBINERCOUNT_UNIQUE_C0= 0x0010L, // c0 unique in each stage
PS_COMBINERCOUNT_SAME_C1= 0x0000L, // c1 same in each stage
PS_COMBINERCOUNT_UNIQUE_C1= 0x0100L // c1 unique in each stage
};
enum PS_COMBINEROUTPUT
{
PS_COMBINEROUTPUT_IDENTITY= 0x00L, // y = x
PS_COMBINEROUTPUT_BIAS= 0x08L, // y = x - 0.5
PS_COMBINEROUTPUT_SHIFTLEFT_1= 0x10L, // y = x*2
PS_COMBINEROUTPUT_SHIFTLEFT_1_BIAS= 0x18L, // y = (x - 0.5)*2
PS_COMBINEROUTPUT_SHIFTLEFT_2= 0x20L, // y = x*4
PS_COMBINEROUTPUT_SHIFTRIGHT_1= 0x30L, // y = x/2
PS_COMBINEROUTPUT_AB_BLUE_TO_ALPHA= 0x80L, // RGB only
PS_COMBINEROUTPUT_CD_BLUE_TO_ALPHA= 0x40L, // RGB only
PS_COMBINEROUTPUT_AB_MULTIPLY= 0x00L,
PS_COMBINEROUTPUT_AB_DOT_PRODUCT= 0x02L, // RGB only
PS_COMBINEROUTPUT_CD_MULTIPLY= 0x00L,
PS_COMBINEROUTPUT_CD_DOT_PRODUCT= 0x01L, // RGB only
PS_COMBINEROUTPUT_AB_CD_SUM= 0x00L, // 3rd output is AB+CD
PS_COMBINEROUTPUT_AB_CD_MUX= 0x04L, // 3rd output is MUX(AB,CD) based on R0.a
};
enum PS_CHANNEL
{
PS_CHANNEL_RGB= 0x00, // used as RGB source
PS_CHANNEL_BLUE= 0x00, // used as ALPHA source
PS_CHANNEL_ALPHA= 0x10, // used as RGB or ALPHA source
};
enum PS_FINALCOMBINERSETTING
{
PS_FINALCOMBINERSETTING_CLAMP_SUM= 0x80, // V1+R0 sum clamped to [0,1]
PS_FINALCOMBINERSETTING_COMPLEMENT_V1= 0x40, // unsigned invert mapping
PS_FINALCOMBINERSETTING_COMPLEMENT_R0= 0x20, // unsigned invert mapping
};
// Structures to describe the PS definition
struct InputInfo {
int reg, mod, chan;
bool invert;
};
struct InputVarInfo {
struct InputInfo a, b, c, d;
};
struct FCInputInfo {
struct InputInfo a, b, c, d, e, f, g;
int c0, c1;
//uint32_t c0_value, c1_value;
bool c0_used, c1_used;
bool v1r0_sum, clamp_sum, inv_v1, inv_r0, enabled;
};
struct OutputInfo {
int ab, cd, muxsum, flags, ab_op, cd_op, muxsum_op,
mapping, ab_alphablue, cd_alphablue;
};
struct PSStageInfo {
struct InputVarInfo rgb_input, alpha_input;
struct OutputInfo rgb_output, alpha_output;
int c0, c1;
//uint32_t c0_value, c1_value;
bool c0_used, c1_used;
};
struct PixelShader {
PshState state;
int num_stages, flags;
struct PSStageInfo stage[8];
struct FCInputInfo final_input;
int tex_modes[4], input_tex[4];
//uint32_t dot_mapping, input_texture;
QString *varE, *varF;
QString *code;
int cur_stage;
int num_var_refs;
char var_refs[32][32];
int num_const_refs;
char const_refs[32][32];
};
static void add_var_ref(struct PixelShader *ps, const char *var)
{
int i;
for (i=0; i<ps->num_var_refs; i++) {
if (strcmp((char*)ps->var_refs[i], var) == 0) return;
}
strcpy((char*)ps->var_refs[ps->num_var_refs++], var);
}
static void add_const_ref(struct PixelShader *ps, const char *var)
{
int i;
for (i=0; i<ps->num_const_refs; i++) {
if (strcmp((char*)ps->const_refs[i], var) == 0) return;
}
strcpy((char*)ps->const_refs[ps->num_const_refs++], var);
}
// Get the code for a variable used in the program
static QString* get_var(struct PixelShader *ps, int reg, bool is_dest)
{
switch (reg) {
case PS_REGISTER_DISCARD:
if (is_dest) {
return qstring_from_str("");
} else {
return qstring_from_str("0.0");
}
break;
case PS_REGISTER_C0:
/* TODO: should the final stage really always be unique? */
if (ps->flags & PS_COMBINERCOUNT_UNIQUE_C0 || ps->cur_stage == 8) {
QString *reg = qstring_from_fmt("c_%d_%d", ps->cur_stage, 0);
add_const_ref(ps, qstring_get_str(reg));
if (ps->cur_stage == 8) {
ps->final_input.c0_used = true;
} else {
ps->stage[ps->cur_stage].c0_used = true;
}
return reg;
} else { // Same c0
add_const_ref(ps, "c_0_0");
ps->stage[0].c0_used = true;
return qstring_from_str("c_0_0");
}
break;
case PS_REGISTER_C1:
if (ps->flags & PS_COMBINERCOUNT_UNIQUE_C1 || ps->cur_stage == 8) {
QString *reg = qstring_from_fmt("c_%d_%d", ps->cur_stage, 1);
add_const_ref(ps, qstring_get_str(reg));
if (ps->cur_stage == 8) {
ps->final_input.c1_used = true;
} else {
ps->stage[ps->cur_stage].c1_used = true;
}
return reg;
} else { // Same c1
add_const_ref(ps, "c_0_1");
ps->stage[0].c1_used = true;
return qstring_from_str("c_0_1");
}
break;
case PS_REGISTER_FOG:
return qstring_from_str("pFog");
case PS_REGISTER_V0:
return qstring_from_str("v0");
case PS_REGISTER_V1:
return qstring_from_str("v1");
case PS_REGISTER_T0:
return qstring_from_str("t0");
case PS_REGISTER_T1:
return qstring_from_str("t1");
case PS_REGISTER_T2:
return qstring_from_str("t2");
case PS_REGISTER_T3:
return qstring_from_str("t3");
case PS_REGISTER_R0:
add_var_ref(ps, "r0");
return qstring_from_str("r0");
case PS_REGISTER_R1:
add_var_ref(ps, "r1");
return qstring_from_str("r1");
case PS_REGISTER_V1R0_SUM:
add_var_ref(ps, "r0");
return qstring_from_str("(v1 + r0)");
case PS_REGISTER_EF_PROD:
return qstring_from_fmt("(%s * %s)", qstring_get_str(ps->varE),
qstring_get_str(ps->varF));
default:
assert(false);
break;
}
}
// Get input variable code
static QString* get_input_var(struct PixelShader *ps, struct InputInfo in, bool is_alpha)
{
QString *reg = get_var(ps, in.reg, false);
if (strcmp(qstring_get_str(reg), "0.0") != 0
&& (in.reg != PS_REGISTER_EF_PROD
|| strstr(qstring_get_str(reg), ".a") == NULL)) {
switch (in.chan) {
case PS_CHANNEL_RGB:
if (is_alpha) {
qstring_append(reg, ".b");
} else {
qstring_append(reg, ".rgb");
}
break;
case PS_CHANNEL_ALPHA:
qstring_append(reg, ".a");
break;
default:
assert(false);
break;
}
}
QString *res;
switch (in.mod) {
case PS_INPUTMAPPING_SIGNED_IDENTITY:
case PS_INPUTMAPPING_UNSIGNED_IDENTITY:
QINCREF(reg);
res = reg;
break;
case PS_INPUTMAPPING_UNSIGNED_INVERT:
res = qstring_from_fmt("(1.0 - %s)", qstring_get_str(reg));
break;
case PS_INPUTMAPPING_EXPAND_NORMAL: // TODO: Change to max(0, x)??
res = qstring_from_fmt("(2.0 * %s - 1.0)", qstring_get_str(reg));
break;
case PS_INPUTMAPPING_EXPAND_NEGATE:
res = qstring_from_fmt("(1.0 - 2.0 * %s)", qstring_get_str(reg));
break;
case PS_INPUTMAPPING_HALFBIAS_NORMAL:
res = qstring_from_fmt("(%s - 0.5)", qstring_get_str(reg));
break;
case PS_INPUTMAPPING_HALFBIAS_NEGATE:
res = qstring_from_fmt("(0.5 - %s)", qstring_get_str(reg));
break;
case PS_INPUTMAPPING_SIGNED_NEGATE:
res = qstring_from_fmt("-%s", qstring_get_str(reg));
break;
default:
assert(false);
break;
}
QDECREF(reg);
return res;
}
// Get code for the output mapping of a stage
static QString* get_output(QString *reg, int mapping)
{
QString *res;
switch (mapping) {
case PS_COMBINEROUTPUT_IDENTITY:
QINCREF(reg);
res = reg;
break;
case PS_COMBINEROUTPUT_BIAS:
res = qstring_from_fmt("(%s - 0.5)", qstring_get_str(reg));
break;
case PS_COMBINEROUTPUT_SHIFTLEFT_1:
res = qstring_from_fmt("(%s * 2.0)", qstring_get_str(reg));
break;
case PS_COMBINEROUTPUT_SHIFTLEFT_1_BIAS:
res = qstring_from_fmt("((%s - 0.5) * 2.0)", qstring_get_str(reg));
break;
case PS_COMBINEROUTPUT_SHIFTLEFT_2:
res = qstring_from_fmt("(%s * 4.0)", qstring_get_str(reg));
break;
case PS_COMBINEROUTPUT_SHIFTRIGHT_1:
res = qstring_from_fmt("(%s / 2.0)", qstring_get_str(reg));
break;
default:
assert(false);
break;
}
return res;
}
// Add the HLSL code for a stage
static void add_stage_code(struct PixelShader *ps,
struct InputVarInfo input, struct OutputInfo output,
const char *write_mask, bool is_alpha)
{
QString *a = get_input_var(ps, input.a, is_alpha);
QString *b = get_input_var(ps, input.b, is_alpha);
QString *c = get_input_var(ps, input.c, is_alpha);
QString *d = get_input_var(ps, input.d, is_alpha);
const char *caster = "";
if (strlen(write_mask) == 3) {
caster = "vec3";
}
QString *ab;
if (output.ab_op == PS_COMBINEROUTPUT_AB_DOT_PRODUCT) {
ab = qstring_from_fmt("dot(%s, %s)",
qstring_get_str(a), qstring_get_str(b));
} else {
ab = qstring_from_fmt("(%s * %s)",
qstring_get_str(a), qstring_get_str(b));
}
QString *cd;
if (output.cd_op == PS_COMBINEROUTPUT_CD_DOT_PRODUCT) {
cd = qstring_from_fmt("dot(%s, %s)",
qstring_get_str(c), qstring_get_str(d));
} else {
cd = qstring_from_fmt("(%s * %s)",
qstring_get_str(c), qstring_get_str(d));
}
QString *ab_mapping = get_output(ab, output.mapping);
QString *cd_mapping = get_output(cd, output.mapping);
QString *ab_dest = get_var(ps, output.ab, true);
QString *cd_dest = get_var(ps, output.cd, true);
QString *sum_dest = get_var(ps, output.muxsum, true);
if (qstring_get_length(ab_dest)) {
qstring_append_fmt(ps->code, "%s.%s = %s(%s);\n",
qstring_get_str(ab_dest), write_mask, caster, qstring_get_str(ab_mapping));
} else {
QDECREF(ab_dest);
QINCREF(ab_mapping);
ab_dest = ab_mapping;
}
if (qstring_get_length(cd_dest)) {
qstring_append_fmt(ps->code, "%s.%s = %s(%s);\n",
qstring_get_str(cd_dest), write_mask, caster, qstring_get_str(cd_mapping));
} else {
QDECREF(cd_dest);
QINCREF(cd_mapping);
cd_dest = cd_mapping;
}
if (!is_alpha && output.flags & PS_COMBINEROUTPUT_AB_BLUE_TO_ALPHA) {
qstring_append_fmt(ps->code, "%s.a = %s.b;\n",
qstring_get_str(ab_dest), qstring_get_str(ab_dest));
}
if (!is_alpha && output.flags & PS_COMBINEROUTPUT_CD_BLUE_TO_ALPHA) {
qstring_append_fmt(ps->code, "%s.a = %s.b;\n",
qstring_get_str(cd_dest), qstring_get_str(cd_dest));
}
QString *sum;
if (output.muxsum_op == PS_COMBINEROUTPUT_AB_CD_SUM) {
sum = qstring_from_fmt("(%s + %s)", qstring_get_str(ab), qstring_get_str(cd));
} else {
sum = qstring_from_fmt("((r0.a >= 0.5) ? %s : %s)",
qstring_get_str(cd), qstring_get_str(ab));
}
QString *sum_mapping = get_output(sum, output.mapping);
if (qstring_get_length(sum_dest)) {
qstring_append_fmt(ps->code, "%s.%s = %s(%s);\n",
qstring_get_str(sum_dest), write_mask, caster, qstring_get_str(sum_mapping));
}
QDECREF(a);
QDECREF(b);
QDECREF(c);
QDECREF(d);
QDECREF(ab);
QDECREF(cd);
QDECREF(ab_mapping);
QDECREF(cd_mapping);
QDECREF(ab_dest);
QDECREF(cd_dest);
QDECREF(sum_dest);
QDECREF(sum);
QDECREF(sum_mapping);
}
// Add code for the final combiner stage
static void add_final_stage_code(struct PixelShader *ps, struct FCInputInfo final)
{
ps->varE = get_input_var(ps, final.e, false);
ps->varF = get_input_var(ps, final.f, false);
QString *a = get_input_var(ps, final.a, false);
QString *b = get_input_var(ps, final.b, false);
QString *c = get_input_var(ps, final.c, false);
QString *d = get_input_var(ps, final.d, false);
QString *g = get_input_var(ps, final.g, false);
add_var_ref(ps, "r0");
qstring_append_fmt(ps->code, "r0.rgb = %s + mix(vec3(%s), vec3(%s), vec3(%s));\n",
qstring_get_str(d), qstring_get_str(c),
qstring_get_str(b), qstring_get_str(a));
/* FIXME: Is .x correctly here? */
qstring_append_fmt(ps->code, "r0.a = vec3(%s).x;\n", qstring_get_str(g));
QDECREF(a);
QDECREF(b);
QDECREF(c);
QDECREF(d);
QDECREF(g);
QDECREF(ps->varE);
QDECREF(ps->varF);
ps->varE = ps->varF = NULL;
}
static QString* psh_convert(struct PixelShader *ps)
{
int i;
QString *preflight = qstring_new();
qstring_append(preflight, STRUCT_VERTEX_DATA);
qstring_append(preflight, "noperspective in VertexData g_vtx;\n");
qstring_append(preflight, "#define vtx g_vtx\n");
qstring_append(preflight, "\n");
qstring_append(preflight, "out vec4 fragColor;\n");
qstring_append(preflight, "\n");
qstring_append(preflight, "uniform vec4 fogColor;\n");
/* calculate perspective-correct inputs */
QString *vars = qstring_new();
qstring_append(vars, "vec4 pD0 = vtx.D0 / vtx.inv_w;\n");
qstring_append(vars, "vec4 pD1 = vtx.D1 / vtx.inv_w;\n");
qstring_append(vars, "vec4 pB0 = vtx.B0 / vtx.inv_w;\n");
qstring_append(vars, "vec4 pB1 = vtx.B1 / vtx.inv_w;\n");
qstring_append(vars, "vec4 pFog = vec4(fogColor.rgb, clamp(vtx.Fog / vtx.inv_w, 0.0, 1.0));\n");
qstring_append(vars, "vec4 pT0 = vtx.T0 / vtx.inv_w;\n");
qstring_append(vars, "vec4 pT1 = vtx.T1 / vtx.inv_w;\n");
qstring_append(vars, "vec4 pT2 = vtx.T2 / vtx.inv_w;\n");
qstring_append(vars, "vec4 pT3 = vtx.T3 / vtx.inv_w;\n");
qstring_append(vars, "\n");
qstring_append(vars, "vec4 v0 = pD0;\n");
qstring_append(vars, "vec4 v1 = pD1;\n");
ps->code = qstring_new();
for (i = 0; i < 4; i++) {
const char *sampler_type = NULL;
switch (ps->tex_modes[i]) {
case PS_TEXTUREMODES_NONE:
qstring_append_fmt(vars, "vec4 t%d = vec4(0.0); /* PS_TEXTUREMODES_NONE */\n",
i);
break;
case PS_TEXTUREMODES_PROJECT2D:
if (ps->state.rect_tex[i]) {
sampler_type = "sampler2DRect";
} else {
sampler_type = "sampler2D";
}
qstring_append_fmt(vars, "vec4 t%d = textureProj(texSamp%d, pT%d.xyw);\n",
i, i, i);
break;
case PS_TEXTUREMODES_PROJECT3D:
sampler_type = "sampler3D";
qstring_append_fmt(vars, "vec4 t%d = textureProj(texSamp%d, pT%d.xyzw);\n",
i, i, i);
break;
case PS_TEXTUREMODES_CUBEMAP:
sampler_type = "samplerCube";
qstring_append_fmt(vars, "vec4 t%d = texture(texSamp%d, pT%d.xyz / pT%d.w);\n",
i, i, i, i);
break;
case PS_TEXTUREMODES_PASSTHRU:
qstring_append_fmt(vars, "vec4 t%d = pT%d;\n", i, i);
break;
case PS_TEXTUREMODES_CLIPPLANE: {
int j;
qstring_append_fmt(vars, "vec4 t%d = vec4(0.0); /* PS_TEXTUREMODES_CLIPPLANE */\n",
i);
for (j = 0; j < 4; j++) {
qstring_append_fmt(vars, " if(pT%d.%c %s 0.0) { discard; };\n",
i, "xyzw"[j],
ps->state.compare_mode[i][j] ? ">=" : "<");
}
break;
}
case PS_TEXTUREMODES_BUMPENVMAP:
assert(!ps->state.rect_tex[i]);
sampler_type = "sampler2D";
qstring_append_fmt(preflight, "uniform mat2 bumpMat%d;\n", i);
/* FIXME: Do bumpMat swizzle on CPU before upload */
qstring_append_fmt(vars, "vec4 t%d = texture(texSamp%d, pT%d.xy + t%d.rg * mat2(bumpMat%d[0].xy,bumpMat%d[1].yx));\n",
i, i, i, ps->input_tex[i], i, i);
break;
case PS_TEXTUREMODES_BUMPENVMAP_LUM:
qstring_append_fmt(preflight, "uniform float bumpScale%d;\n", i);
qstring_append_fmt(preflight, "uniform float bumpOffset%d;\n", i);
qstring_append_fmt(ps->code, "/* BUMPENVMAP_LUM for stage %d */\n", i);
qstring_append_fmt(ps->code, "t%d = t%d * (bumpScale%d * t%d.b + bumpOffset%d);\n",
i, i, i, ps->input_tex[i], i);
/* Now the same as BUMPENVMAP */
assert(!ps->state.rect_tex[i]);
sampler_type = "sampler2D";
qstring_append_fmt(preflight, "uniform mat2 bumpMat%d;\n", i);
/* FIXME: Do bumpMat swizzle on CPU before upload */
qstring_append_fmt(vars, "vec4 t%d = texture(texSamp%d, pT%d.xy + t%d.rg * mat2(bumpMat%d[0].xy,bumpMat%d[1].yx));\n",
i, i, i, ps->input_tex[i], i, i);
break;
case PS_TEXTUREMODES_BRDF:
qstring_append_fmt(vars, "vec4 t%d = vec4(0.0); /* PS_TEXTUREMODES_BRDF */\n",
i);
assert(false); /* Unimplemented */
break;
case PS_TEXTUREMODES_DOT_ST:
qstring_append_fmt(vars, "vec4 t%d = vec4(0.0); /* PS_TEXTUREMODES_DOT_ST */\n",
i);
assert(false); /* Unimplemented */
break;
case PS_TEXTUREMODES_DOT_ZW:
qstring_append_fmt(vars, "vec4 t%d = vec4(0.0); /* PS_TEXTUREMODES_DOT_ZW */\n",
i);
assert(false); /* Unimplemented */
break;
case PS_TEXTUREMODES_DOT_RFLCT_DIFF:
qstring_append_fmt(vars, "vec4 t%d = vec4(0.0); /* PS_TEXTUREMODES_DOT_RFLCT_DIFF */\n",
i);
assert(false); /* Unimplemented */
break;
case PS_TEXTUREMODES_DOT_RFLCT_SPEC:
qstring_append_fmt(vars, "vec4 t%d = vec4(0.0); /* PS_TEXTUREMODES_DOT_RFLCT_SPEC */\n",
i);
assert(false); /* Unimplemented */
break;
case PS_TEXTUREMODES_DOT_STR_3D:
qstring_append_fmt(vars, "vec4 t%d = vec4(0.0); /* PS_TEXTUREMODES_DOT_STR_3D */\n",
i);
assert(false); /* Unimplemented */
break;
case PS_TEXTUREMODES_DOT_STR_CUBE:
qstring_append_fmt(vars, "vec4 t%d = vec4(0.0); /* PS_TEXTUREMODES_DOT_STR_CUBE */\n",
i);
assert(false); /* Unimplemented */
break;
case PS_TEXTUREMODES_DPNDNT_AR:
assert(!ps->state.rect_tex[i]);
sampler_type = "sampler2D";
qstring_append_fmt(vars, "vec4 t%d = texture(texSamp%d, t%d.ar);\n",
i, i, ps->input_tex[i]);
break;
case PS_TEXTUREMODES_DPNDNT_GB:
assert(!ps->state.rect_tex[i]);
sampler_type = "sampler2D";
qstring_append_fmt(vars, "vec4 t%d = texture(texSamp%d, t%d.gb);\n",
i, i, ps->input_tex[i]);
break;
case PS_TEXTUREMODES_DOTPRODUCT:
qstring_append_fmt(vars, "vec4 t%d = vec4(dot(pT%d.xyz, t%d.rgb));\n",
i, i, ps->input_tex[i]);
break;
case PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST:
qstring_append_fmt(vars, "vec4 t%d = vec4(0.0); /* PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST */\n",
i);
assert(false); /* Unimplemented */
break;
default:
fprintf(stderr, "Unknown ps tex mode: 0x%x\n", ps->tex_modes[i]);
assert(false);
break;
}
if (sampler_type != NULL) {
qstring_append_fmt(preflight, "uniform %s texSamp%d;\n", sampler_type, i);
/* As this means a texture fetch does happen, do alphakill */
if (ps->state.alphakill[i]) {
qstring_append_fmt(vars, "if (t%d.a == 0.0) { discard; };\n",
i);
}
}
}
for (i = 0; i < ps->num_stages; i++) {
ps->cur_stage = i;
qstring_append_fmt(ps->code, "// Stage %d\n", i);
add_stage_code(ps, ps->stage[i].rgb_input, ps->stage[i].rgb_output, "rgb", false);
add_stage_code(ps, ps->stage[i].alpha_input, ps->stage[i].alpha_output, "a", true);
}
if (ps->final_input.enabled) {
ps->cur_stage = 8;
qstring_append(ps->code, "// Final Combiner\n");
add_final_stage_code(ps, ps->final_input);
}
for (i = 0; i < ps->num_var_refs; i++) {
qstring_append_fmt(vars, "vec4 %s;\n", ps->var_refs[i]);
if (strcmp(ps->var_refs[i], "r0") == 0) {
if (ps->tex_modes[0] != PS_TEXTUREMODES_NONE) {
qstring_append(vars, "r0.a = t0.a;\n");
} else {
qstring_append(vars, "r0.a = 1.0;\n");
}
}
}
for (i = 0; i < ps->num_const_refs; i++) {
qstring_append_fmt(preflight, "uniform vec4 %s;\n", ps->const_refs[i]);
}
if (ps->state.alpha_test && ps->state.alpha_func != ALPHA_FUNC_ALWAYS) {
qstring_append_fmt(preflight, "uniform float alphaRef;\n");
if (ps->state.alpha_func == ALPHA_FUNC_NEVER) {
qstring_append(ps->code, "discard;\n");
} else {
const char* alpha_op;
switch (ps->state.alpha_func) {
case ALPHA_FUNC_LESS: alpha_op = "<"; break;
case ALPHA_FUNC_EQUAL: alpha_op = "=="; break;
case ALPHA_FUNC_LEQUAL: alpha_op = "<="; break;
case ALPHA_FUNC_GREATER: alpha_op = ">"; break;
case ALPHA_FUNC_NOTEQUAL: alpha_op = "!="; break;
case ALPHA_FUNC_GEQUAL: alpha_op = ">="; break;
default:
assert(false);
break;
}
qstring_append_fmt(ps->code, "if (!(r0.a %s alphaRef)) discard;\n",
alpha_op);
}
}
QString *final = qstring_new();
qstring_append(final, "#version 330\n\n");
qstring_append(final, qstring_get_str(preflight));
qstring_append(final, "void main() {\n");
qstring_append(final, qstring_get_str(vars));
qstring_append(final, qstring_get_str(ps->code));
qstring_append(final, "fragColor = r0;\n");
qstring_append(final, "}\n");
QDECREF(preflight);
QDECREF(vars);
QDECREF(ps->code);
return final;
}
static void parse_input(struct InputInfo *var, int value)
{
var->reg = value & 0xF;
var->chan = value & 0x10;
var->mod = value & 0xE0;
}
static void parse_combiner_inputs(uint32_t value,
struct InputInfo *a, struct InputInfo *b,
struct InputInfo *c, struct InputInfo *d)
{
parse_input(d, value & 0xFF);
parse_input(c, (value >> 8) & 0xFF);
parse_input(b, (value >> 16) & 0xFF);
parse_input(a, (value >> 24) & 0xFF);
}
static void parse_combiner_output(uint32_t value, struct OutputInfo *out)
{
out->cd = value & 0xF;
out->ab = (value >> 4) & 0xF;
out->muxsum = (value >> 8) & 0xF;
int flags = value >> 12;
out->flags = flags;
out->cd_op = flags & 1;
out->ab_op = flags & 2;
out->muxsum_op = flags & 4;
out->mapping = flags & 0x38;
out->ab_alphablue = flags & 0x80;
out->cd_alphablue = flags & 0x40;
}
QString *psh_translate(const PshState state)
{
int i;
struct PixelShader ps;
memset(&ps, 0, sizeof(ps));
ps.state = state;
ps.num_stages = state.combiner_control & 0xFF;
ps.flags = state.combiner_control >> 8;
for (i = 0; i < 4; i++) {
ps.tex_modes[i] = (state.shader_stage_program >> (i * 5)) & 0x1F;
}
ps.input_tex[0] = -1;
ps.input_tex[1] = 0;
ps.input_tex[2] = (state.other_stage_input >> 16) & 0xF;
ps.input_tex[3] = (state.other_stage_input >> 20) & 0xF;
for (i = 0; i < ps.num_stages; i++) {
parse_combiner_inputs(state.rgb_inputs[i],
&ps.stage[i].rgb_input.a, &ps.stage[i].rgb_input.b,
&ps.stage[i].rgb_input.c, &ps.stage[i].rgb_input.d);
parse_combiner_inputs(state.alpha_inputs[i],
&ps.stage[i].alpha_input.a, &ps.stage[i].alpha_input.b,
&ps.stage[i].alpha_input.c, &ps.stage[i].alpha_input.d);
parse_combiner_output(state.rgb_outputs[i], &ps.stage[i].rgb_output);
parse_combiner_output(state.alpha_outputs[i], &ps.stage[i].alpha_output);
//ps.stage[i].c0 = (pDef->PSC0Mapping >> (i * 4)) & 0xF;
//ps.stage[i].c1 = (pDef->PSC1Mapping >> (i * 4)) & 0xF;
//ps.stage[i].c0_value = constant_0[i];
//ps.stage[i].c1_value = constant_1[i];
}
struct InputInfo blank;
ps.final_input.enabled = state.final_inputs_0 || state.final_inputs_1;
if (ps.final_input.enabled) {
parse_combiner_inputs(state.final_inputs_0,
&ps.final_input.a, &ps.final_input.b,
&ps.final_input.c, &ps.final_input.d);
parse_combiner_inputs(state.final_inputs_1,
&ps.final_input.e, &ps.final_input.f,
&ps.final_input.g, &blank);
int flags = state.final_inputs_1 & 0xFF;
ps.final_input.clamp_sum = flags & PS_FINALCOMBINERSETTING_CLAMP_SUM;
ps.final_input.inv_v1 = flags & PS_FINALCOMBINERSETTING_COMPLEMENT_V1;
ps.final_input.inv_r0 = flags & PS_FINALCOMBINERSETTING_COMPLEMENT_R0;
//ps.final_input.c0 = (pDef->PSFinalCombinerConstants >> 0) & 0xF;
//ps.final_input.c1 = (pDef->PSFinalCombinerConstants >> 4) & 0xF;
//ps.final_input.c0_value = final_constant_0;
//ps.final_input.c1_value = final_constant_1;
}
return psh_convert(&ps);
}

59
hw/xbox/nv2a_psh.h Normal file
View File

@ -0,0 +1,59 @@
/*
* QEMU Geforce NV2A pixel shader translation
*
* Copyright (c) 2013 espes
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2 as published by the Free Software Foundation.
*
* 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/>
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#ifndef HW_NV2A_PSH_H
#define HW_NV2A_PSH_H
#include "qapi/qmp/qstring.h"
enum PshAlphaFunc {
ALPHA_FUNC_NEVER,
ALPHA_FUNC_LESS,
ALPHA_FUNC_EQUAL,
ALPHA_FUNC_LEQUAL,
ALPHA_FUNC_GREATER,
ALPHA_FUNC_NOTEQUAL,
ALPHA_FUNC_GEQUAL,
ALPHA_FUNC_ALWAYS,
};
typedef struct PshState {
/* fragment shader - register combiner stuff */
uint32_t combiner_control;
uint32_t shader_stage_program;
uint32_t other_stage_input;
uint32_t final_inputs_0;
uint32_t final_inputs_1;
uint32_t rgb_inputs[8], rgb_outputs[8];
uint32_t alpha_inputs[8], alpha_outputs[8];
bool rect_tex[4];
bool compare_mode[4][4];
bool alphakill[4];
bool alpha_test;
enum PshAlphaFunc alpha_func;
} PshState;
QString *psh_translate(const PshState state);
#endif

944
hw/xbox/nv2a_shaders.c Normal file
View File

@ -0,0 +1,944 @@
/*
* QEMU Geforce NV2A shader generator
*
* Copyright (c) 2015 espes
* Copyright (c) 2015 Jannik Vogel
*
* 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 "qemu-common.h"
#include "hw/xbox/nv2a_debug.h"
#include "hw/xbox/nv2a_shaders_common.h"
#include "hw/xbox/nv2a_shaders.h"
static QString* generate_geometry_shader(
enum ShaderPolygonMode polygon_front_mode,
enum ShaderPolygonMode polygon_back_mode,
enum ShaderPrimitiveMode primitive_mode,
GLenum *gl_primitive_mode)
{
/* FIXME: Missing support for 2-sided-poly mode */
assert(polygon_front_mode == polygon_back_mode);
enum ShaderPolygonMode polygon_mode = polygon_front_mode;
/* POINT mode shouldn't require any special work */
if (polygon_mode == POLY_MODE_POINT) {
*gl_primitive_mode = GL_POINTS;
return NULL;
}
/* Handle LINE and FILL mode */
const char *layout_in = NULL;
const char *layout_out = NULL;
const char *body = NULL;
switch (primitive_mode) {
case PRIM_TYPE_POINTS: *gl_primitive_mode = GL_POINTS; return NULL;
case PRIM_TYPE_LINES: *gl_primitive_mode = GL_LINES; return NULL;
case PRIM_TYPE_LINE_LOOP: *gl_primitive_mode = GL_LINE_LOOP; return NULL;
case PRIM_TYPE_LINE_STRIP: *gl_primitive_mode = GL_LINE_STRIP; return NULL;
case PRIM_TYPE_TRIANGLES:
*gl_primitive_mode = GL_TRIANGLES;
if (polygon_mode == POLY_MODE_FILL) { return NULL; }
assert(polygon_mode == POLY_MODE_LINE);
layout_in = "layout(triangles) in;\n";
layout_out = "layout(line_strip, max_vertices = 4) out;\n";
body = " emit_vertex(0);\n"
" emit_vertex(1);\n"
" emit_vertex(2);\n"
" emit_vertex(0);\n"
" EndPrimitive();\n";
break;
case PRIM_TYPE_TRIANGLE_STRIP:
*gl_primitive_mode = GL_TRIANGLE_STRIP;
if (polygon_mode == POLY_MODE_FILL) { return NULL; }
assert(polygon_mode == POLY_MODE_LINE);
layout_in = "layout(triangles) in;\n";
layout_out = "layout(line_strip, max_vertices = 4) out;\n";
/* Imagine a quad made of a tristrip, the comments tell you which
* vertex we are using */
body = " if ((gl_PrimitiveIDIn & 1) == 0) {\n"
" if (gl_PrimitiveIDIn == 0) {\n"
" emit_vertex(0);\n" /* bottom right */
" }\n"
" emit_vertex(1);\n" /* top right */
" emit_vertex(2);\n" /* bottom left */
" emit_vertex(0);\n" /* bottom right */
" } else {\n"
" emit_vertex(2);\n" /* bottom left */
" emit_vertex(1);\n" /* top left */
" emit_vertex(0);\n" /* top right */
" }\n"
" EndPrimitive();\n";
break;
case PRIM_TYPE_TRIANGLE_FAN:
*gl_primitive_mode = GL_TRIANGLE_FAN;
if (polygon_mode == POLY_MODE_FILL) { return NULL; }
assert(polygon_mode == POLY_MODE_LINE);
layout_in = "layout(triangles) in;\n";
layout_out = "layout(line_strip, max_vertices = 4) out;\n";
body = " if (gl_PrimitiveIDIn == 0) {\n"
" emit_vertex(0);\n"
" }\n"
" emit_vertex(1);\n"
" emit_vertex(2);\n"
" emit_vertex(0);\n"
" EndPrimitive();\n";
break;
case PRIM_TYPE_QUADS:
*gl_primitive_mode = GL_LINES_ADJACENCY;
layout_in = "layout(lines_adjacency) in;\n";
if (polygon_mode == POLY_MODE_LINE) {
layout_out = "layout(line_strip, max_vertices = 5) out;\n";
body = " emit_vertex(0);\n"
" emit_vertex(1);\n"
" emit_vertex(2);\n"
" emit_vertex(3);\n"
" emit_vertex(0);\n"
" EndPrimitive();\n";
} else if (polygon_mode == POLY_MODE_FILL) {
layout_out = "layout(triangle_strip, max_vertices = 4) out;\n";
body = " emit_vertex(0);\n"
" emit_vertex(1);\n"
" emit_vertex(3);\n"
" emit_vertex(2);\n"
" EndPrimitive();\n";
} else {
assert(false);
return NULL;
}
break;
case PRIM_TYPE_QUAD_STRIP:
*gl_primitive_mode = GL_LINE_STRIP_ADJACENCY;
layout_in = "layout(lines_adjacency) in;\n";
if (polygon_mode == POLY_MODE_LINE) {
layout_out = "layout(line_strip, max_vertices = 5) out;\n";
body = " if ((gl_PrimitiveIDIn & 1) != 0) { return; }\n"
" if (gl_PrimitiveIDIn == 0) {\n"
" emit_vertex(0);\n"
" }\n"
" emit_vertex(1);\n"
" emit_vertex(3);\n"
" emit_vertex(2);\n"
" emit_vertex(0);\n"
" EndPrimitive();\n";
} else if (polygon_mode == POLY_MODE_FILL) {
layout_out = "layout(triangle_strip, max_vertices = 4) out;\n";
body = " if ((gl_PrimitiveIDIn & 1) != 0) { return; }\n"
" emit_vertex(0);\n"
" emit_vertex(1);\n"
" emit_vertex(2);\n"
" emit_vertex(3);\n"
" EndPrimitive();\n";
} else {
assert(false);
return NULL;
}
break;
case PRIM_TYPE_POLYGON:
if (polygon_mode == POLY_MODE_LINE) {
*gl_primitive_mode = GL_LINE_LOOP;
} else if (polygon_mode == POLY_MODE_FILL) {
*gl_primitive_mode = GL_TRIANGLE_FAN;
} else {
assert(false);
}
return NULL;
default:
assert(false);
return NULL;
}
/* generate a geometry shader to support deprecated primitive types */
assert(layout_in);
assert(layout_out);
assert(body);
QString* s = qstring_from_str("#version 330\n"
"\n");
qstring_append(s, layout_in);
qstring_append(s, layout_out);
qstring_append(s, "\n"
STRUCT_VERTEX_DATA
"noperspective in VertexData v_vtx[];\n"
"noperspective out VertexData g_vtx;\n"
"\n"
"void emit_vertex(int index) {\n"
" gl_Position = gl_in[index].gl_Position;\n"
" gl_PointSize = gl_in[index].gl_PointSize;\n"
" g_vtx = v_vtx[index];\n"
" EmitVertex();\n"
"}\n"
"\n"
"void main() {\n");
qstring_append(s, body);
qstring_append(s, "}\n");
return s;
}
static void append_skinning_code(QString* str, bool mix,
unsigned int count, const char* type,
const char* output, const char* input,
const char* matrix, const char* swizzle)
{
if (count == 0) {
qstring_append_fmt(str, "%s %s = (%s * %s0).%s;\n",
type, output, input, matrix, swizzle);
} else {
qstring_append_fmt(str, "%s %s = %s(0.0);\n", type, output, type);
if (mix) {
/* Tweening */
if (count == 2) {
qstring_append_fmt(str,
"%s += mix((%s * %s1).%s,\n"
" (%s * %s0).%s, weight.x);\n",
output,
input, matrix, swizzle,
input, matrix, swizzle);
} else {
/* FIXME: Not sure how blend weights are calculated */
assert(false);
}
} else {
/* Individual matrices */
int i;
for (i = 0; i < count; i++) {
char c = "xyzw"[i];
qstring_append_fmt(str, "%s += (%s * %s%d * weight.%c).%s;\n",
output, input, matrix, i, c,
swizzle);
}
assert(false); /* FIXME: Untested */
}
}
}
#define GLSL_C(idx) "c[" stringify(idx) "]"
#define GLSL_LTCTXA(idx) "ltctxa[" stringify(idx) "]"
#define GLSL_C_MAT4(idx) \
"mat4(" GLSL_C(idx) ", " GLSL_C(idx+1) ", " \
GLSL_C(idx+2) ", " GLSL_C(idx+3) ")"
#define GLSL_DEFINE(a, b) "#define " stringify(a) " " b "\n"
static void generate_fixed_function(const ShaderState state,
QString *header, QString *body)
{
int i, j;
/* generate vertex shader mimicking fixed function */
qstring_append(header,
"#define position v0\n"
"#define weight v1\n"
"#define normal v2.xyz\n"
"#define diffuse v3\n"
"#define specular v4\n"
"#define fogCoord v5.x\n"
"#define pointSize v6\n"
"#define backDiffuse v7\n"
"#define backSpecular v8\n"
"#define texture0 v9\n"
"#define texture1 v10\n"
"#define texture2 v11\n"
"#define texture3 v12\n"
"#define reserved1 v13\n"
"#define reserved2 v14\n"
"#define reserved3 v15\n"
"\n"
"uniform vec4 ltctxa[" stringify(NV2A_LTCTXA_COUNT) "];\n"
"uniform vec4 ltctxb[" stringify(NV2A_LTCTXB_COUNT) "];\n"
"uniform vec4 ltc1[" stringify(NV2A_LTC1_COUNT) "];\n"
"\n"
GLSL_DEFINE(projectionMat, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_PMAT0))
GLSL_DEFINE(compositeMat, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_CMAT0))
"\n"
GLSL_DEFINE(texPlaneS0, GLSL_C(NV_IGRAPH_XF_XFCTX_TG0MAT + 0))
GLSL_DEFINE(texPlaneT0, GLSL_C(NV_IGRAPH_XF_XFCTX_TG0MAT + 1))
GLSL_DEFINE(texPlaneQ0, GLSL_C(NV_IGRAPH_XF_XFCTX_TG0MAT + 2))
GLSL_DEFINE(texPlaneR0, GLSL_C(NV_IGRAPH_XF_XFCTX_TG0MAT + 3))
"\n"
GLSL_DEFINE(texPlaneS1, GLSL_C(NV_IGRAPH_XF_XFCTX_TG1MAT + 0))
GLSL_DEFINE(texPlaneT1, GLSL_C(NV_IGRAPH_XF_XFCTX_TG1MAT + 1))
GLSL_DEFINE(texPlaneQ1, GLSL_C(NV_IGRAPH_XF_XFCTX_TG1MAT + 2))
GLSL_DEFINE(texPlaneR1, GLSL_C(NV_IGRAPH_XF_XFCTX_TG1MAT + 3))
"\n"
GLSL_DEFINE(texPlaneS2, GLSL_C(NV_IGRAPH_XF_XFCTX_TG2MAT + 0))
GLSL_DEFINE(texPlaneT2, GLSL_C(NV_IGRAPH_XF_XFCTX_TG2MAT + 1))
GLSL_DEFINE(texPlaneQ2, GLSL_C(NV_IGRAPH_XF_XFCTX_TG2MAT + 2))
GLSL_DEFINE(texPlaneR2, GLSL_C(NV_IGRAPH_XF_XFCTX_TG2MAT + 3))
"\n"
GLSL_DEFINE(texPlaneS3, GLSL_C(NV_IGRAPH_XF_XFCTX_TG3MAT + 0))
GLSL_DEFINE(texPlaneT3, GLSL_C(NV_IGRAPH_XF_XFCTX_TG3MAT + 1))
GLSL_DEFINE(texPlaneQ3, GLSL_C(NV_IGRAPH_XF_XFCTX_TG3MAT + 2))
GLSL_DEFINE(texPlaneR3, GLSL_C(NV_IGRAPH_XF_XFCTX_TG3MAT + 3))
"\n"
GLSL_DEFINE(modelViewMat0, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_MMAT0))
GLSL_DEFINE(modelViewMat1, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_MMAT1))
GLSL_DEFINE(modelViewMat2, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_MMAT2))
GLSL_DEFINE(modelViewMat3, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_MMAT3))
"\n"
GLSL_DEFINE(invModelViewMat0, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_IMMAT0))
GLSL_DEFINE(invModelViewMat1, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_IMMAT1))
GLSL_DEFINE(invModelViewMat2, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_IMMAT2))
GLSL_DEFINE(invModelViewMat3, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_IMMAT3))
"\n"
GLSL_DEFINE(eyePosition, GLSL_C(NV_IGRAPH_XF_XFCTX_EYEP))
"\n"
"#define lightAmbientColor(i) "
"ltctxb[" stringify(NV_IGRAPH_XF_LTCTXB_L0_AMB) " + (i)*6].xyz\n"
"#define lightDiffuseColor(i) "
"ltctxb[" stringify(NV_IGRAPH_XF_LTCTXB_L0_DIF) " + (i)*6].xyz\n"
"#define lightSpecularColor(i) "
"ltctxb[" stringify(NV_IGRAPH_XF_LTCTXB_L0_SPC) " + (i)*6].xyz\n"
"\n"
"#define lightSpotFalloff(i) "
"ltctxa[" stringify(NV_IGRAPH_XF_LTCTXA_L0_K) " + (i)*2].xyz\n"
"#define lightSpotDirection(i) "
"ltctxa[" stringify(NV_IGRAPH_XF_LTCTXA_L0_SPT) " + (i)*2]\n"
"\n"
"#define lightLocalRange(i) "
"ltc1[" stringify(NV_IGRAPH_XF_LTC1_r0) " + (i)].x\n"
"\n"
GLSL_DEFINE(sceneAmbientColor, GLSL_LTCTXA(NV_IGRAPH_XF_LTCTXA_FR_AMB) ".xyz")
"\n"
"uniform mat4 invViewport;\n"
"\n");
/* Skinning */
unsigned int count;
bool mix;
switch (state.skinning) {
case SKINNING_OFF:
mix = false; count = 0; break;
case SKINNING_1WEIGHTS:
mix = true; count = 2; break;
case SKINNING_2WEIGHTS:
mix = true; count = 3; break;
case SKINNING_3WEIGHTS:
mix = true; count = 4; break;
case SKINNING_2WEIGHTS2MATRICES:
mix = false; count = 2; break;
case SKINNING_3WEIGHTS3MATRICES:
mix = false; count = 3; break;
case SKINNING_4WEIGHTS4MATRICES:
mix = false; count = 4; break;
default:
assert(false);
break;
}
qstring_append_fmt(body, "/* Skinning mode %d */\n",
state.skinning);
append_skinning_code(body, mix, count, "vec4",
"tPosition", "position",
"modelViewMat", "xyzw");
append_skinning_code(body, mix, count, "vec3",
"tNormal", "vec4(normal, 0.0)",
"invModelViewMat", "xyz");
/* Normalization */
if (state.normalization) {
qstring_append(body, "tNormal = normalize(tNormal);\n");
}
/* Texgen */
for (i = 0; i < NV2A_MAX_TEXTURES; i++) {
qstring_append_fmt(body, "/* Texgen for stage %d */\n",
i);
/* Set each component individually */
/* FIXME: could be nicer if some channels share the same texgen */
for (j = 0; j < 4; j++) {
/* TODO: TexGen View Model missing! */
char c = "xyzw"[j];
char cSuffix = "STRQ"[j];
switch (state.texgen[i][j]) {
case TEXGEN_DISABLE:
qstring_append_fmt(body, "oT%d.%c = texture%d.%c;\n",
i, c, i, c);
break;
case TEXGEN_EYE_LINEAR:
qstring_append_fmt(body, "oT%d.%c = dot(texPlane%c%d, tPosition);\n",
i, c, cSuffix, i);
break;
case TEXGEN_OBJECT_LINEAR:
qstring_append_fmt(body, "oT%d.%c = dot(texPlane%c%d, position);\n",
i, c, cSuffix, i);
assert(false); /* Untested */
break;
case TEXGEN_SPHERE_MAP:
assert(i < 2); /* Channels S,T only! */
qstring_append(body, "{\n");
/* FIXME: u, r and m only have to be calculated once */
qstring_append(body, " vec3 u = normalize(tPosition.xyz);\n");
//FIXME: tNormal before or after normalization? Always normalize?
qstring_append(body, " vec3 r = reflect(u, tNormal);\n");
/* FIXME: This would consume 1 division fewer and *might* be
* faster than length:
* // [z=1/(2*x) => z=1/x*0.5]
* vec3 ro = r + vec3(0.0, 0.0, 1.0);
* float m = inversesqrt(dot(ro,ro))*0.5;
*/
qstring_append(body, " float invM = 1.0 / (2.0 * length(r + vec3(0.0, 0.0, 1.0)));\n");
qstring_append_fmt(body, " oT%d.%c = r.%c * invM + 0.5;\n",
i, c, c);
qstring_append(body, "}\n");
assert(false); /* Untested */
break;
case TEXGEN_REFLECTION_MAP:
assert(i < 3); /* Channels S,T,R only! */
qstring_append(body, "{\n");
/* FIXME: u and r only have to be calculated once, can share the one from SPHERE_MAP */
qstring_append(body, " vec3 u = normalize(tPosition.xyz);\n");
qstring_append(body, " vec3 r = reflect(u, tNormal);\n");
qstring_append_fmt(body, " oT%d.%c = r.%c;\n",
i, c, c);
qstring_append(body, "}\n");
break;
case TEXGEN_NORMAL_MAP:
assert(i < 3); /* Channels S,T,R only! */
qstring_append_fmt(body, "oT%d.%c = tNormal.%c;\n",
i, c, c);
break;
default:
assert(false);
break;
}
}
}
/* Apply texture matrices */
for (i = 0; i < NV2A_MAX_TEXTURES; i++) {
if (state.texture_matrix_enable[i]) {
qstring_append_fmt(body,
"oT%d = oT%d * texMat%d;\n",
i, i, i);
}
}
/* Lighting */
if (state.lighting) {
//FIXME: Do 2 passes if we want 2 sided-lighting?
qstring_append(body, "oD0 = vec4(sceneAmbientColor, diffuse.a);\n");
qstring_append(body, "oD1 = vec4(0.0, 0.0, 0.0, specular.a);\n");
for (i = 0; i < NV2A_MAX_LIGHTS; i++) {
if (state.light[i] == LIGHT_OFF) {
continue;
}
/* FIXME: It seems that we only have to handle the surface colors if
* they are not part of the material [= vertex colors].
* If they are material the cpu will premultiply light
* colors
*/
qstring_append_fmt(body, "/* Light %d */ {\n", i);
if (state.light[i] == LIGHT_LOCAL
|| state.light[i] == LIGHT_SPOT) {
qstring_append_fmt(header,
"uniform vec3 lightLocalPosition%d;\n"
"uniform vec3 lightLocalAttenuation%d;\n",
i, i);
qstring_append_fmt(body,
" vec3 VP = lightLocalPosition%d - tPosition.xyz/tPosition.w;\n"
" float d = length(VP);\n"
//FIXME: if (d > lightLocalRange) { .. don't process this light .. } /* inclusive?! */ - what about directional lights?
" VP = normalize(VP);\n"
" float attenuation = 1.0 / (lightLocalAttenuation%d.x\n"
" + lightLocalAttenuation%d.y * d\n"
" + lightLocalAttenuation%d.z * d * d);\n"
" vec3 halfVector = normalize(VP + eyePosition.xyz / eyePosition.w);\n" /* FIXME: Not sure if eyePosition is correct */
" float nDotVP = max(0.0, dot(tNormal, VP));\n"
" float nDotHV = max(0.0, dot(tNormal, halfVector));\n",
i, i, i, i);
}
switch(state.light[i]) {
case LIGHT_INFINITE:
/* lightLocalRange will be 1e+30 here */
qstring_append_fmt(header,
"uniform vec3 lightInfiniteHalfVector%d;\n"
"uniform vec3 lightInfiniteDirection%d;\n",
i, i);
qstring_append_fmt(body,
" float attenuation = 1.0;\n"
" float nDotVP = max(0.0, dot(tNormal, normalize(vec3(lightInfiniteDirection%d))));\n"
" float nDotHV = max(0.0, dot(tNormal, vec3(lightInfiniteHalfVector%d)));\n",
i, i);
/* FIXME: Do specular */
/* FIXME: tBackDiffuse */
break;
case LIGHT_LOCAL:
/* Everything done already */
break;
case LIGHT_SPOT:
assert(false);
/*FIXME: calculate falloff */
break;
default:
assert(false);
break;
}
qstring_append_fmt(body,
" float pf;\n"
" if (nDotVP == 0.0) {\n"
" pf = 0.0;\n"
" } else {\n"
" pf = pow(nDotHV, /* specular(l, m, n, l1, m1, n1) */ 0.001);\n"
" }\n"
" vec3 lightAmbient = lightAmbientColor(%d) * attenuation;\n"
" vec3 lightDiffuse = lightDiffuseColor(%d) * attenuation * nDotVP;\n"
" vec3 lightSpecular = lightSpecularColor(%d) * pf;\n",
i, i, i);
qstring_append(body,
" oD0.xyz += lightAmbient;\n");
qstring_append(body,
" oD0.xyz += diffuse.xyz * lightDiffuse;\n");
qstring_append(body,
" oD1.xyz += specular.xyz * lightSpecular;\n");
qstring_append(body, "}\n");
}
} else {
qstring_append(body, " oD0 = diffuse;\n");
qstring_append(body, " oD1 = specular;\n");
}
qstring_append(body, " oB0 = backDiffuse;\n");
qstring_append(body, " oB1 = backSpecular;\n");
/* Fog */
if (state.fog_enable) {
/* From: https://www.opengl.org/registry/specs/NV/fog_distance.txt */
switch(state.foggen) {
case FOGGEN_SPEC_ALPHA:
/* FIXME: Do we have to clamp here? */
qstring_append(body, " float fogDistance = clamp(specular.a, 0.0, 1.0);\n");
break;
case FOGGEN_RADIAL:
qstring_append(body, " float fogDistance = length(tPosition.xyz);\n");
break;
case FOGGEN_PLANAR:
case FOGGEN_ABS_PLANAR:
qstring_append(body, " float fogDistance = dot(fogPlane.xyz, tPosition.xyz) + fogPlane.w;\n");
if (state.foggen == FOGGEN_ABS_PLANAR) {
qstring_append(body, " fogDistance = abs(fogDistance);\n");
}
break;
case FOGGEN_FOG_X:
qstring_append(body, " float fogDistance = fogCoord;\n");
break;
default:
assert(false);
break;
}
}
/* If skinning is off the composite matrix already includes the MV matrix */
if (state.skinning == SKINNING_OFF) {
qstring_append(body, " tPosition = position;\n");
}
qstring_append(body,
" oPos = invViewport * (tPosition * compositeMat);\n"
" oPos.z = oPos.z * 2.0 - oPos.w;\n");
qstring_append(body, " vtx.inv_w = 1.0 / oPos.w;\n");
}
static QString *generate_vertex_shader(const ShaderState state,
char vtx_prefix)
{
int i;
QString *header = qstring_from_str(
"#version 330\n"
"\n"
"uniform vec2 clipRange;\n"
"uniform vec2 surfaceSize;\n"
"\n"
/* All constants in 1 array declaration */
"uniform vec4 c[" stringify(NV2A_VERTEXSHADER_CONSTANTS) "];\n"
"\n"
"uniform vec4 fogColor;\n"
"uniform float fogParam[2];\n"
"\n"
GLSL_DEFINE(fogPlane, GLSL_C(NV_IGRAPH_XF_XFCTX_FOG))
GLSL_DEFINE(texMat0, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_T0MAT))
GLSL_DEFINE(texMat1, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_T1MAT))
GLSL_DEFINE(texMat2, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_T2MAT))
GLSL_DEFINE(texMat3, GLSL_C_MAT4(NV_IGRAPH_XF_XFCTX_T3MAT))
"\n"
"vec4 oPos = vec4(0.0,0.0,0.0,1.0);\n"
"vec4 oD0 = vec4(0.0,0.0,0.0,1.0);\n"
"vec4 oD1 = vec4(0.0,0.0,0.0,1.0);\n"
"vec4 oB0 = vec4(0.0,0.0,0.0,1.0);\n"
"vec4 oB1 = vec4(0.0,0.0,0.0,1.0);\n"
"vec4 oPts = vec4(0.0,0.0,0.0,1.0);\n"
/* FIXME: NV_vertex_program says: "FOGC is the transformed vertex's fog
* coordinate. The register's first floating-point component is interpolated
* across the assembled primitive during rasterization and used as the fog
* distance to compute per-fragment the fog factor when fog is enabled.
* However, if both fog and vertex program mode are enabled, but the FOGC
* vertex result register is not written, the fog factor is overridden to
* 1.0. The register's other three components are ignored."
*
* That probably means it will read back as vec4(0.0, 0.0, 0.0, 1.0) but
* will be set to 1.0 AFTER the VP if it was never written?
* We should test on real hardware..
*
* We'll force 1.0 for oFog.x for now.
*/
"vec4 oFog = vec4(1.0,0.0,0.0,1.0);\n"
"vec4 oT0 = vec4(0.0,0.0,0.0,1.0);\n"
"vec4 oT1 = vec4(0.0,0.0,0.0,1.0);\n"
"vec4 oT2 = vec4(0.0,0.0,0.0,1.0);\n"
"vec4 oT3 = vec4(0.0,0.0,0.0,1.0);\n"
"\n"
STRUCT_VERTEX_DATA);
qstring_append_fmt(header, "noperspective out VertexData %c_vtx;\n",
vtx_prefix);
qstring_append_fmt(header, "#define vtx %c_vtx\n",
vtx_prefix);
qstring_append(header, "\n");
for(i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) {
qstring_append_fmt(header, "in vec4 v%d;\n", i);
}
qstring_append(header, "\n");
QString *body = qstring_from_str("void main() {\n");
if (state.fixed_function) {
generate_fixed_function(state, header, body);
} else if (state.vertex_program) {
vsh_translate(VSH_VERSION_XVS,
(uint32_t*)state.program_data,
state.program_length,
state.z_perspective,
header, body);
} else {
assert(false);
}
/* Fog */
if (state.fog_enable) {
if (state.vertex_program) {
/* FIXME: Does foggen do something here? Let's do some tracking..
*
* "RollerCoaster Tycoon" has
* state.vertex_program = true; state.foggen == FOGGEN_PLANAR
* but expects oFog.x as fogdistance?! Writes oFog.xyzw = v0.z
*/
qstring_append(body, " float fogDistance = oFog.x;\n");
}
/* FIXME: Do this per pixel? */
switch (state.fog_mode) {
case FOG_MODE_LINEAR:
case FOG_MODE_LINEAR_ABS:
/* f = (end - d) / (end - start)
* fogParam[1] = 1 / (end - start)
* fogParam[0] = 1 + end * fogParam[1];
*/
qstring_append(body, " float fogFactor = fogParam[0] + fogDistance * fogParam[1];\n");
qstring_append(body, " fogFactor -= 1.0;\n"); /* FIXME: WHHYYY?!! */
break;
case FOG_MODE_EXP:
case FOG_MODE_EXP_ABS:
/* f = 1 / (e^(d * density))
* fogParam[1] = -density / (2 * ln(256))
* fogParam[0] = 1.5
*/
qstring_append(body, " float fogFactor = fogParam[0] + exp2(fogDistance * fogParam[1] * 16.0);\n");
qstring_append(body, " fogFactor -= 1.5;\n"); /* FIXME: WHHYYY?!! */
break;
case FOG_MODE_EXP2:
case FOG_MODE_EXP2_ABS:
/* f = 1 / (e^((d * density)^2))
* fogParam[1] = -density / (2 * sqrt(ln(256)))
* fogParam[0] = 1.5
*/
qstring_append(body, " float fogFactor = fogParam[0] + exp2(-fogDistance * fogDistance * fogParam[1] * fogParam[1] * 32.0);\n");
qstring_append(body, " fogFactor -= 1.5;\n"); /* FIXME: WHHYYY?!! */
break;
default:
assert(false);
break;
}
/* Calculate absolute for the modes which need it */
switch (state.fog_mode) {
case FOG_MODE_LINEAR_ABS:
case FOG_MODE_EXP_ABS:
case FOG_MODE_EXP2_ABS:
qstring_append(body, " fogFactor = abs(fogFactor);\n");
break;
default:
break;
}
/* FIXME: What about fog alpha?! */
qstring_append(body, " oFog.xyzw = vec4(fogFactor);\n");
} else {
/* FIXME: Is the fog still calculated / passed somehow?!
*/
qstring_append(body, " oFog.xyzw = vec4(1.0);\n");
}
/* Set outputs */
qstring_append(body, "\n"
" vtx.D0 = clamp(oD0, 0.0, 1.0) * vtx.inv_w;\n"
" vtx.D1 = clamp(oD1, 0.0, 1.0) * vtx.inv_w;\n"
" vtx.B0 = clamp(oB0, 0.0, 1.0) * vtx.inv_w;\n"
" vtx.B1 = clamp(oB1, 0.0, 1.0) * vtx.inv_w;\n"
" vtx.Fog = oFog.x * vtx.inv_w;\n"
" vtx.T0 = oT0 * vtx.inv_w;\n"
" vtx.T1 = oT1 * vtx.inv_w;\n"
" vtx.T2 = oT2 * vtx.inv_w;\n"
" vtx.T3 = oT3 * vtx.inv_w;\n"
" gl_Position = oPos;\n"
" gl_PointSize = oPts.x;\n"
"\n"
"}\n");
/* Return combined header + source */
qstring_append(header, qstring_get_str(body));
QDECREF(body);
return header;
}
static GLuint create_gl_shader(GLenum gl_shader_type,
const char *code,
const char *name)
{
GLint compiled = 0;
NV2A_GL_DGROUP_BEGIN("Creating new %s", name);
NV2A_DPRINTF("compile new %s, code:\n%s\n", name, code);
GLuint shader = glCreateShader(gl_shader_type);
glShaderSource(shader, 1, &code, 0);
glCompileShader(shader);
/* Check it compiled */
compiled = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
if (!compiled) {
GLchar* log;
GLint log_length;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
log = g_malloc(log_length * sizeof(GLchar));
glGetShaderInfoLog(shader, log_length, NULL, log);
fprintf(stderr, "nv2a: %s compilation failed: %s\n", name, log);
g_free(log);
NV2A_GL_DGROUP_END();
abort();
}
NV2A_GL_DGROUP_END();
return shader;
}
ShaderBinding* generate_shaders(const ShaderState state)
{
int i, j;
char tmp[64];
char vtx_prefix;
GLuint program = glCreateProgram();
/* Create an option geometry shader and find primitive type */
GLenum gl_primitive_mode;
QString* geometry_shader_code =
generate_geometry_shader(state.polygon_front_mode,
state.polygon_back_mode,
state.primitive_mode,
&gl_primitive_mode);
if (geometry_shader_code) {
const char* geometry_shader_code_str =
qstring_get_str(geometry_shader_code);
GLuint geometry_shader = create_gl_shader(GL_GEOMETRY_SHADER,
geometry_shader_code_str,
"geometry shader");
glAttachShader(program, geometry_shader);
QDECREF(geometry_shader_code);
vtx_prefix = 'v';
} else {
vtx_prefix = 'g';
}
/* create the vertex shader */
QString *vertex_shader_code = generate_vertex_shader(state, vtx_prefix);
GLuint vertex_shader = create_gl_shader(GL_VERTEX_SHADER,
qstring_get_str(vertex_shader_code),
"vertex shader");
glAttachShader(program, vertex_shader);
QDECREF(vertex_shader_code);
/* Bind attributes for vertices */
for(i = 0; i < NV2A_VERTEXSHADER_ATTRIBUTES; i++) {
snprintf(tmp, sizeof(tmp), "v%d", i);
glBindAttribLocation(program, i, tmp);
}
/* generate a fragment shader from register combiners */
QString *fragment_shader_code = psh_translate(state.psh);
const char *fragment_shader_code_str = qstring_get_str(fragment_shader_code);
GLuint fragment_shader = create_gl_shader(GL_FRAGMENT_SHADER,
fragment_shader_code_str,
"fragment shader");
glAttachShader(program, fragment_shader);
QDECREF(fragment_shader_code);
/* link the program */
glLinkProgram(program);
GLint linked = 0;
glGetProgramiv(program, GL_LINK_STATUS, &linked);
if(!linked) {
GLchar log[2048];
glGetProgramInfoLog(program, 2048, NULL, log);
fprintf(stderr, "nv2a: shader linking failed: %s\n", log);
abort();
}
glUseProgram(program);
/* set texture samplers */
for (i = 0; i < NV2A_MAX_TEXTURES; i++) {
char samplerName[16];
snprintf(samplerName, sizeof(samplerName), "texSamp%d", i);
GLint texSampLoc = glGetUniformLocation(program, samplerName);
if (texSampLoc >= 0) {
glUniform1i(texSampLoc, i);
}
}
/* validate the program */
glValidateProgram(program);
GLint valid = 0;
glGetProgramiv(program, GL_VALIDATE_STATUS, &valid);
if (!valid) {
GLchar log[1024];
glGetProgramInfoLog(program, 1024, NULL, log);
fprintf(stderr, "nv2a: shader validation failed: %s\n", log);
abort();
}
ShaderBinding* ret = g_malloc0(sizeof(ShaderBinding));
ret->gl_program = program;
ret->gl_primitive_mode = gl_primitive_mode;
/* lookup fragment shader uniforms */
for (i=0; i<=8; i++) {
for (j=0; j<2; j++) {
snprintf(tmp, sizeof(tmp), "c_%d_%d", i, j);
ret->psh_constant_loc[i][j] = glGetUniformLocation(program, tmp);
}
}
ret->alpha_ref_loc = glGetUniformLocation(program, "alphaRef");
for (i = 1; i < NV2A_MAX_TEXTURES; i++) {
snprintf(tmp, sizeof(tmp), "bumpMat%d", i);
ret->bump_mat_loc[i] = glGetUniformLocation(program, tmp);
snprintf(tmp, sizeof(tmp), "bumpScale%d", i);
ret->bump_scale_loc[i] = glGetUniformLocation(program, tmp);
snprintf(tmp, sizeof(tmp), "bumpOffset%d", i);
ret->bump_offset_loc[i] = glGetUniformLocation(program, tmp);
}
/* lookup vertex shader uniforms */
for(i = 0; i < NV2A_VERTEXSHADER_CONSTANTS; i++) {
snprintf(tmp, sizeof(tmp), "c[%d]", i);
ret->vsh_constant_loc[i] = glGetUniformLocation(program, tmp);
}
ret->surface_size_loc = glGetUniformLocation(program, "surfaceSize");
ret->clip_range_loc = glGetUniformLocation(program, "clipRange");
ret->fog_color_loc = glGetUniformLocation(program, "fogColor");
ret->fog_param_loc[0] = glGetUniformLocation(program, "fogParam[0]");
ret->fog_param_loc[1] = glGetUniformLocation(program, "fogParam[1]");
ret->inv_viewport_loc = glGetUniformLocation(program, "invViewport");
for (i = 0; i < NV2A_LTCTXA_COUNT; i++) {
snprintf(tmp, sizeof(tmp), "ltctxa[%d]", i);
ret->ltctxa_loc[i] = glGetUniformLocation(program, tmp);
}
for (i = 0; i < NV2A_LTCTXB_COUNT; i++) {
snprintf(tmp, sizeof(tmp), "ltctxb[%d]", i);
ret->ltctxb_loc[i] = glGetUniformLocation(program, tmp);
}
for (i = 0; i < NV2A_LTC1_COUNT; i++) {
snprintf(tmp, sizeof(tmp), "ltc1[%d]", i);
ret->ltc1_loc[i] = glGetUniformLocation(program, tmp);
}
for (i = 0; i < NV2A_MAX_LIGHTS; i++) {
snprintf(tmp, sizeof(tmp), "lightInfiniteHalfVector%d", i);
ret->light_infinite_half_vector_loc[i] = glGetUniformLocation(program, tmp);
snprintf(tmp, sizeof(tmp), "lightInfiniteDirection%d", i);
ret->light_infinite_direction_loc[i] = glGetUniformLocation(program, tmp);
snprintf(tmp, sizeof(tmp), "lightLocalPosition%d", i);
ret->light_local_position_loc[i] = glGetUniformLocation(program, tmp);
snprintf(tmp, sizeof(tmp), "lightLocalAttenuation%d", i);
ret->light_local_attenuation_loc[i] = glGetUniformLocation(program, tmp);
}
return ret;
}

115
hw/xbox/nv2a_shaders.h Normal file
View File

@ -0,0 +1,115 @@
/*
* QEMU Geforce NV2A shader generator
*
* Copyright (c) 2015 espes
* Copyright (c) 2015 Jannik Vogel
*
* 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 HW_NV2A_SHADERS_H
#define HW_NV2A_SHADERS_H
#include "qapi/qmp/qstring.h"
#include "gl/gloffscreen.h"
#include "nv2a_vsh.h"
#include "nv2a_psh.h"
#include "nv2a_int.h"
enum ShaderPrimitiveMode {
PRIM_TYPE_NONE,
PRIM_TYPE_POINTS,
PRIM_TYPE_LINES,
PRIM_TYPE_LINE_LOOP,
PRIM_TYPE_LINE_STRIP,
PRIM_TYPE_TRIANGLES,
PRIM_TYPE_TRIANGLE_STRIP,
PRIM_TYPE_TRIANGLE_FAN,
PRIM_TYPE_QUADS,
PRIM_TYPE_QUAD_STRIP,
PRIM_TYPE_POLYGON,
};
enum ShaderPolygonMode {
POLY_MODE_FILL,
POLY_MODE_POINT,
POLY_MODE_LINE,
};
typedef struct ShaderState {
PshState psh;
bool texture_matrix_enable[4];
enum VshTexgen texgen[4][4];
bool fog_enable;
enum VshFoggen foggen;
enum VshFogMode fog_mode;
enum VshSkinning skinning;
bool normalization;
bool lighting;
enum VshLight light[NV2A_MAX_LIGHTS];
bool fixed_function;
/* vertex program */
bool vertex_program;
uint32_t program_data[NV2A_MAX_TRANSFORM_PROGRAM_LENGTH][VSH_TOKEN_SIZE];
int program_length;
bool z_perspective;
/* primitive format for geometry shader */
enum ShaderPolygonMode polygon_front_mode;
enum ShaderPolygonMode polygon_back_mode;
enum ShaderPrimitiveMode primitive_mode;
} ShaderState;
typedef struct ShaderBinding {
GLuint gl_program;
GLenum gl_primitive_mode;
GLint psh_constant_loc[9][2];
GLint alpha_ref_loc;
GLint bump_mat_loc[NV2A_MAX_TEXTURES];
GLint bump_scale_loc[NV2A_MAX_TEXTURES];
GLint bump_offset_loc[NV2A_MAX_TEXTURES];
GLint surface_size_loc;
GLint clip_range_loc;
GLint vsh_constant_loc[NV2A_VERTEXSHADER_CONSTANTS];
GLint inv_viewport_loc;
GLint ltctxa_loc[NV2A_LTCTXA_COUNT];
GLint ltctxb_loc[NV2A_LTCTXB_COUNT];
GLint ltc1_loc[NV2A_LTC1_COUNT];
GLint fog_color_loc;
GLint fog_param_loc[2];
GLint light_infinite_half_vector_loc[NV2A_MAX_LIGHTS];
GLint light_infinite_direction_loc[NV2A_MAX_LIGHTS];
GLint light_local_position_loc[NV2A_MAX_LIGHTS];
GLint light_local_attenuation_loc[NV2A_MAX_LIGHTS];
} ShaderBinding;
ShaderBinding* generate_shaders(const ShaderState state);
#endif

View File

@ -0,0 +1,37 @@
/*
* QEMU Geforce NV2A shader common definitions
*
* Copyright (c) 2015 espes
* Copyright (c) 2015 Jannik Vogel
*
* 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 HW_NV2A_SHADERS_COMMON_H
#define HW_NV2A_SHADERS_COMMON_H
#define STRUCT_VERTEX_DATA "struct VertexData {\n" \
" float inv_w;\n" \
" vec4 D0;\n" \
" vec4 D1;\n" \
" vec4 B0;\n" \
" vec4 B1;\n" \
" float Fog;\n" \
" vec4 T0;\n" \
" vec4 T1;\n" \
" vec4 T2;\n" \
" vec4 T3;\n" \
"};\n"
#endif

768
hw/xbox/nv2a_vsh.c Normal file
View File

@ -0,0 +1,768 @@
/*
* QEMU Geforce NV2A vertex shader translation
*
* Copyright (c) 2014 Jannik Vogel
* Copyright (c) 2012 espes
*
* Based on:
* Cxbx, VertexShader.cpp
* Copyright (c) 2004 Aaron Robinson <caustik@caustik.com>
* Kingofc <kingofc@freenet.de>
* Dxbx, uPushBuffer.pas
* Copyright (c) 2007 Shadow_tj, PatrickvL
*
* 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 <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include "hw/xbox/nv2a_shaders_common.h"
#include "hw/xbox/nv2a_vsh.h"
#define VSH_D3DSCM_CORRECTION 96
typedef enum {
PARAM_UNKNOWN = 0,
PARAM_R,
PARAM_V,
PARAM_C
} VshParameterType;
typedef enum {
OUTPUT_C = 0,
OUTPUT_O
} VshOutputType;
typedef enum {
OMUX_MAC = 0,
OMUX_ILU
} VshOutputMux;
typedef enum {
ILU_NOP = 0,
ILU_MOV,
ILU_RCP,
ILU_RCC,
ILU_RSQ,
ILU_EXP,
ILU_LOG,
ILU_LIT
} VshILU;
typedef enum {
MAC_NOP,
MAC_MOV,
MAC_MUL,
MAC_ADD,
MAC_MAD,
MAC_DP3,
MAC_DPH,
MAC_DP4,
MAC_DST,
MAC_MIN,
MAC_MAX,
MAC_SLT,
MAC_SGE,
MAC_ARL
} VshMAC;
typedef enum {
SWIZZLE_X = 0,
SWIZZLE_Y,
SWIZZLE_Z,
SWIZZLE_W
} VshSwizzle;
typedef struct VshFieldMapping {
VshFieldName field_name;
uint8_t subtoken;
uint8_t start_bit;
uint8_t bit_length;
} VshFieldMapping;
static const VshFieldMapping field_mapping[] = {
// Field Name DWORD BitPos BitSize
{ FLD_ILU, 1, 25, 3 },
{ FLD_MAC, 1, 21, 4 },
{ FLD_CONST, 1, 13, 8 },
{ FLD_V, 1, 9, 4 },
// INPUT A
{ FLD_A_NEG, 1, 8, 1 },
{ FLD_A_SWZ_X, 1, 6, 2 },
{ FLD_A_SWZ_Y, 1, 4, 2 },
{ FLD_A_SWZ_Z, 1, 2, 2 },
{ FLD_A_SWZ_W, 1, 0, 2 },
{ FLD_A_R, 2, 28, 4 },
{ FLD_A_MUX, 2, 26, 2 },
// INPUT B
{ FLD_B_NEG, 2, 25, 1 },
{ FLD_B_SWZ_X, 2, 23, 2 },
{ FLD_B_SWZ_Y, 2, 21, 2 },
{ FLD_B_SWZ_Z, 2, 19, 2 },
{ FLD_B_SWZ_W, 2, 17, 2 },
{ FLD_B_R, 2, 13, 4 },
{ FLD_B_MUX, 2, 11, 2 },
// INPUT C
{ FLD_C_NEG, 2, 10, 1 },
{ FLD_C_SWZ_X, 2, 8, 2 },
{ FLD_C_SWZ_Y, 2, 6, 2 },
{ FLD_C_SWZ_Z, 2, 4, 2 },
{ FLD_C_SWZ_W, 2, 2, 2 },
{ FLD_C_R_HIGH, 2, 0, 2 },
{ FLD_C_R_LOW, 3, 30, 2 },
{ FLD_C_MUX, 3, 28, 2 },
// Output
{ FLD_OUT_MAC_MASK, 3, 24, 4 },
{ FLD_OUT_R, 3, 20, 4 },
{ FLD_OUT_ILU_MASK, 3, 16, 4 },
{ FLD_OUT_O_MASK, 3, 12, 4 },
{ FLD_OUT_ORB, 3, 11, 1 },
{ FLD_OUT_ADDRESS, 3, 3, 8 },
{ FLD_OUT_MUX, 3, 2, 1 },
// Other
{ FLD_A0X, 3, 1, 1 },
{ FLD_FINAL, 3, 0, 1 }
};
typedef struct VshOpcodeParams {
bool A;
bool B;
bool C;
} VshOpcodeParams;
static const VshOpcodeParams ilu_opcode_params[] = {
/* ILU OP ParamA ParamB ParamC */
/* ILU_NOP */ { false, false, false }, // Dxbx note : Unused
/* ILU_MOV */ { false, false, true },
/* ILU_RCP */ { false, false, true },
/* ILU_RCC */ { false, false, true },
/* ILU_RSQ */ { false, false, true },
/* ILU_EXP */ { false, false, true },
/* ILU_LOG */ { false, false, true },
/* ILU_LIT */ { false, false, true },
};
static const VshOpcodeParams mac_opcode_params[] = {
/* MAC OP ParamA ParamB ParamC */
/* MAC_NOP */ { false, false, false }, // Dxbx note : Unused
/* MAC_MOV */ { true, false, false },
/* MAC_MUL */ { true, true, false },
/* MAC_ADD */ { true, false, true },
/* MAC_MAD */ { true, true, true },
/* MAC_DP3 */ { true, true, false },
/* MAC_DPH */ { true, true, false },
/* MAC_DP4 */ { true, true, false },
/* MAC_DST */ { true, true, false },
/* MAC_MIN */ { true, true, false },
/* MAC_MAX */ { true, true, false },
/* MAC_SLT */ { true, true, false },
/* MAC_SGE */ { true, true, false },
/* MAC_ARL */ { true, false, false },
};
static const char* mask_str[] = {
// xyzw xyzw
",", // 0000 ____
",w", // 0001 ___w
",z", // 0010 __z_
",zw", // 0011 __zw
",y", // 0100 _y__
",yw", // 0101 _y_w
",yz", // 0110 _yz_
",yzw", // 0111 _yzw
",x", // 1000 x___
",xw", // 1001 x__w
",xz", // 1010 x_z_
",xzw", // 1011 x_zw
",xy", // 1100 xy__
",xyw", // 1101 xy_w
",xyz", // 1110 xyz_
",xyzw" // 1111 xyzw
};
/* Note: OpenGL seems to be case-sensitive, and requires upper-case opcodes! */
static const char* mac_opcode[] = {
"NOP",
"MOV",
"MUL",
"ADD",
"MAD",
"DP3",
"DPH",
"DP4",
"DST",
"MIN",
"MAX",
"SLT",
"SGE",
"ARL A0.x", // Dxbx note : Alias for "mov a0.x"
};
static const char* ilu_opcode[] = {
"NOP",
"MOV",
"RCP",
"RCC",
"RSQ",
"EXP",
"LOG",
"LIT",
};
static bool ilu_force_scalar[] = {
false,
false,
true,
true,
true,
true,
true,
false,
};
static const char* out_reg_name[] = {
"oPos",
"???",
"???",
"oD0",
"oD1",
"oFog",
"oPts",
"oB0",
"oB1",
"oT0",
"oT1",
"oT2",
"oT3",
"???",
"???",
"A0.x",
};
// Retrieves a number of bits in the instruction token
static int vsh_get_from_token(const uint32_t *shader_token,
uint8_t subtoken,
uint8_t start_bit,
uint8_t bit_length)
{
return (shader_token[subtoken] >> start_bit) & ~(0xFFFFFFFF << bit_length);
}
uint8_t vsh_get_field(const uint32_t *shader_token, VshFieldName field_name)
{
return (uint8_t)(vsh_get_from_token(shader_token,
field_mapping[field_name].subtoken,
field_mapping[field_name].start_bit,
field_mapping[field_name].bit_length));
}
// Converts the C register address to disassembly format
static int16_t convert_c_register(const int16_t c_reg)
{
int16_t r = ((((c_reg >> 5) & 7) - 3) * 32) + (c_reg & 31);
r += VSH_D3DSCM_CORRECTION; /* to map -96..95 to 0..191 */
return r; //FIXME: = c_reg?!
}
static QString* decode_swizzle(const uint32_t *shader_token,
VshFieldName swizzle_field)
{
const char* swizzle_str = "xyzw";
VshSwizzle x, y, z, w;
/* some microcode instructions force a scalar value */
if (swizzle_field == FLD_C_SWZ_X
&& ilu_force_scalar[vsh_get_field(shader_token, FLD_ILU)]) {
x = y = z = w = vsh_get_field(shader_token, swizzle_field);
} else {
x = vsh_get_field(shader_token, swizzle_field++);
y = vsh_get_field(shader_token, swizzle_field++);
z = vsh_get_field(shader_token, swizzle_field++);
w = vsh_get_field(shader_token, swizzle_field);
}
if (x == SWIZZLE_X && y == SWIZZLE_Y
&& z == SWIZZLE_Z && w == SWIZZLE_W) {
/* Don't print the swizzle if it's .xyzw */
return qstring_from_str(""); // Will turn ".xyzw" into "."
/* Don't print duplicates */
} else if (x == y && y == z && z == w) {
return qstring_from_str((char[]){'.', swizzle_str[x], '\0'});
} else if (y == z && z == w) {
return qstring_from_str((char[]){'.',
swizzle_str[x], swizzle_str[y], '\0'});
} else if (z == w) {
return qstring_from_str((char[]){'.',
swizzle_str[x], swizzle_str[y], swizzle_str[z], '\0'});
} else {
return qstring_from_str((char[]){'.',
swizzle_str[x], swizzle_str[y],
swizzle_str[z], swizzle_str[w],
'\0'}); // Normal swizzle mask
}
}
static QString* decode_opcode_input(const uint32_t *shader_token,
VshParameterType param,
VshFieldName neg_field,
int reg_num)
{
/* This function decodes a vertex shader opcode parameter into a string.
* Input A, B or C is controlled via the Param and NEG fieldnames,
* the R-register address for each input is already given by caller. */
QString *ret_str = qstring_new();
if (vsh_get_field(shader_token, neg_field) > 0) {
qstring_append_chr(ret_str, '-');
}
/* PARAM_R uses the supplied reg_num, but the other two need to be
* determined */
char tmp[40];
switch (param) {
case PARAM_R:
snprintf(tmp, sizeof(tmp), "R%d", reg_num);
break;
case PARAM_V:
reg_num = vsh_get_field(shader_token, FLD_V);
snprintf(tmp, sizeof(tmp), "v%d", reg_num);
break;
case PARAM_C:
reg_num = convert_c_register(vsh_get_field(shader_token, FLD_CONST));
if (vsh_get_field(shader_token, FLD_A0X) > 0) {
//FIXME: does this really require the "correction" doe in convert_c_register?!
snprintf(tmp, sizeof(tmp), "c[A0+%d]", reg_num);
} else {
snprintf(tmp, sizeof(tmp), "c[%d]", reg_num);
}
break;
default:
fprintf(stderr, "Unknown vs param: 0x%x\n", param);
assert(false);
break;
}
qstring_append(ret_str, tmp);
{
/* swizzle bits are next to the neg bit */
QString *swizzle_str = decode_swizzle(shader_token, neg_field+1);
qstring_append(ret_str, qstring_get_str(swizzle_str));
QDECREF(swizzle_str);
}
return ret_str;
}
static QString* decode_opcode(const uint32_t *shader_token,
VshOutputMux out_mux,
uint32_t mask,
const char *opcode,
const char *inputs)
{
QString *ret = qstring_new();
int reg_num = vsh_get_field(shader_token, FLD_OUT_R);
/* Test for paired opcodes (in other words : Are both <> NOP?) */
if (out_mux == OMUX_MAC
&& vsh_get_field(shader_token, FLD_ILU) != ILU_NOP
&& reg_num == 1) {
/* Ignore paired MAC opcodes that write to R1 */
mask = 0;
} else if (out_mux == OMUX_ILU
&& vsh_get_field(shader_token, FLD_MAC) != MAC_NOP) {
/* Paired ILU opcodes can only write to R1 */
reg_num = 1;
}
if (strcmp(opcode, mac_opcode[MAC_ARL]) == 0) {
qstring_append_fmt(ret, " ARL(A0%s);\n", inputs);
} else if (mask > 0) {
qstring_append_fmt(ret, " %s(R%d%s%s);\n",
opcode, reg_num, mask_str[mask], inputs);
}
/* See if we must add a muxed opcode too: */
if (vsh_get_field(shader_token, FLD_OUT_MUX) == out_mux
/* Only if it's not masked away: */
&& vsh_get_field(shader_token, FLD_OUT_O_MASK) != 0) {
qstring_append(ret, " ");
qstring_append(ret, opcode);
qstring_append(ret, "(");
if (vsh_get_field(shader_token, FLD_OUT_ORB) == OUTPUT_C) {
/* TODO : Emulate writeable const registers */
qstring_append(ret, "c");
qstring_append_int(ret,
convert_c_register(
vsh_get_field(shader_token, FLD_OUT_ADDRESS)));
} else {
qstring_append(ret,
out_reg_name[
vsh_get_field(shader_token, FLD_OUT_ADDRESS) & 0xF]);
}
qstring_append(ret,
mask_str[
vsh_get_field(shader_token, FLD_OUT_O_MASK)]);
qstring_append(ret, inputs);
qstring_append(ret, ");\n");
}
return ret;
}
static QString* decode_token(const uint32_t *shader_token)
{
QString *ret;
/* Since it's potentially used twice, decode input C once: */
QString *input_c =
decode_opcode_input(shader_token,
vsh_get_field(shader_token, FLD_C_MUX),
FLD_C_NEG,
(vsh_get_field(shader_token, FLD_C_R_HIGH) << 2)
| vsh_get_field(shader_token, FLD_C_R_LOW));
/* See what MAC opcode is written to (if not masked away): */
VshMAC mac = vsh_get_field(shader_token, FLD_MAC);
if (mac != MAC_NOP) {
QString *inputs_mac = qstring_new();
if (mac_opcode_params[mac].A) {
QString *input_a =
decode_opcode_input(shader_token,
vsh_get_field(shader_token, FLD_A_MUX),
FLD_A_NEG,
vsh_get_field(shader_token, FLD_A_R));
qstring_append(inputs_mac, ", ");
qstring_append(inputs_mac, qstring_get_str(input_a));
QDECREF(input_a);
}
if (mac_opcode_params[mac].B) {
QString *input_b =
decode_opcode_input(shader_token,
vsh_get_field(shader_token, FLD_B_MUX),
FLD_B_NEG,
vsh_get_field(shader_token, FLD_B_R));
qstring_append(inputs_mac, ", ");
qstring_append(inputs_mac, qstring_get_str(input_b));
QDECREF(input_b);
}
if (mac_opcode_params[mac].C) {
qstring_append(inputs_mac, ", ");
qstring_append(inputs_mac, qstring_get_str(input_c));
}
/* Then prepend these inputs with the actual opcode, mask, and input : */
ret = decode_opcode(shader_token,
OMUX_MAC,
vsh_get_field(shader_token, FLD_OUT_MAC_MASK),
mac_opcode[mac],
qstring_get_str(inputs_mac));
QDECREF(inputs_mac);
} else {
ret = qstring_new();
}
/* See if a ILU opcode is present too: */
VshILU ilu = vsh_get_field(shader_token, FLD_ILU);
if (ilu != ILU_NOP) {
QString *inputs_c = qstring_from_str(", ");
qstring_append(inputs_c, qstring_get_str(input_c));
/* Append the ILU opcode, mask and (the already determined) input C: */
QString *ilu_op =
decode_opcode(shader_token,
OMUX_ILU,
vsh_get_field(shader_token, FLD_OUT_ILU_MASK),
ilu_opcode[ilu],
qstring_get_str(inputs_c));
qstring_append(ret, qstring_get_str(ilu_op));
QDECREF(inputs_c);
QDECREF(ilu_op);
}
QDECREF(input_c);
return ret;
}
static const char* vsh_header =
"\n"
"int A0 = 0;\n"
"\n"
"vec4 R0 = vec4(0.0,0.0,0.0,0.0);\n"
"vec4 R1 = vec4(0.0,0.0,0.0,0.0);\n"
"vec4 R2 = vec4(0.0,0.0,0.0,0.0);\n"
"vec4 R3 = vec4(0.0,0.0,0.0,0.0);\n"
"vec4 R4 = vec4(0.0,0.0,0.0,0.0);\n"
"vec4 R5 = vec4(0.0,0.0,0.0,0.0);\n"
"vec4 R6 = vec4(0.0,0.0,0.0,0.0);\n"
"vec4 R7 = vec4(0.0,0.0,0.0,0.0);\n"
"vec4 R8 = vec4(0.0,0.0,0.0,0.0);\n"
"vec4 R9 = vec4(0.0,0.0,0.0,0.0);\n"
"vec4 R10 = vec4(0.0,0.0,0.0,0.0);\n"
"vec4 R11 = vec4(0.0,0.0,0.0,0.0);\n"
"#define R12 oPos\n" /* R12 is a mirror of oPos */
"\n"
/* See:
* http://msdn.microsoft.com/en-us/library/windows/desktop/bb174703%28v=vs.85%29.aspx
* https://www.opengl.org/registry/specs/NV/vertex_program1_1.txt
*/
"\n"
//QQQ #ifdef NICE_CODE
"/* Converts the input to vec4, pads with last component */\n"
"vec4 _in(float v) { return vec4(v); }\n"
"vec4 _in(vec2 v) { return v.xyyy; }\n"
"vec4 _in(vec3 v) { return v.xyzz; }\n"
"vec4 _in(vec4 v) { return v.xyzw; }\n"
//#else
// "/* Make sure input is always a vec4 */\n"
// "#define _in(v) vec4(v)\n"
//#endif
"\n"
"#define INFINITY (1.0 / 0.0)\n"
"\n"
"#define MOV(dest, mask, src) dest.mask = _MOV(_in(src)).mask\n"
"vec4 _MOV(vec4 src)\n"
"{\n"
" return src;\n"
"}\n"
"\n"
"#define MUL(dest, mask, src0, src1) dest.mask = _MUL(_in(src0), _in(src1)).mask\n"
"vec4 _MUL(vec4 src0, vec4 src1)\n"
"{\n"
" return src0 * src1;\n"
"}\n"
"\n"
"#define ADD(dest, mask, src0, src1) dest.mask = _ADD(_in(src0), _in(src1)).mask\n"
"vec4 _ADD(vec4 src0, vec4 src1)\n"
"{\n"
" return src0 + src1;\n"
"}\n"
"\n"
"#define MAD(dest, mask, src0, src1, src2) dest.mask = _MAD(_in(src0), _in(src1), _in(src2)).mask\n"
"vec4 _MAD(vec4 src0, vec4 src1, vec4 src2)\n"
"{\n"
" return src0 * src1 + src2;\n"
"}\n"
"\n"
"#define DP3(dest, mask, src0, src1) dest.mask = _DP3(_in(src0), _in(src1)).mask\n"
"vec4 _DP3(vec4 src0, vec4 src1)\n"
"{\n"
" return vec4(dot(src0.xyz, src1.xyz));\n"
"}\n"
"\n"
"#define DPH(dest, mask, src0, src1) dest.mask = _DPH(_in(src0), _in(src1)).mask\n"
"vec4 _DPH(vec4 src0, vec4 src1)\n"
"{\n"
" return vec4(dot(vec4(src0.xyz, 1.0), src1));\n"
"}\n"
"\n"
"#define DP4(dest, mask, src0, src1) dest.mask = _DP4(_in(src0), _in(src1)).mask\n"
"vec4 _DP4(vec4 src0, vec4 src1)\n"
"{\n"
" return vec4(dot(src0, src1));\n"
"}\n"
"\n"
"#define DST(dest, mask, src0, src1) dest.mask = _DST(_in(src0), _in(src1)).mask\n"
"vec4 _DST(vec4 src0, vec4 src1)\n"
"{\n"
" return vec4(1.0,\n"
" src0.y * src1.y,\n"
" src0.z,\n"
" src1.w);\n"
"}\n"
"\n"
"#define MIN(dest, mask, src0, src1) dest.mask = _MIN(_in(src0), _in(src1)).mask\n"
"vec4 _MIN(vec4 src0, vec4 src1)\n"
"{\n"
" return min(src0, src1);\n"
"}\n"
"\n"
"#define MAX(dest, mask, src0, src1) dest.mask = _MAX(_in(src0), _in(src1)).mask\n"
"vec4 _MAX(vec4 src0, vec4 src1)\n"
"{\n"
" return max(src0, src1);\n"
"}\n"
"\n"
"#define SLT(dest, mask, src0, src1) dest.mask = _SLT(_in(src0), _in(src1)).mask\n"
"vec4 _SLT(vec4 src0, vec4 src1)\n"
"{\n"
" return vec4(lessThan(src0, src1));\n"
"}\n"
"\n"
"#define ARL(dest, src) dest = _ARL(_in(src).x)\n"
"int _ARL(float src)\n"
"{\n"
" return int(floor(src));\n"
"}\n"
"\n"
"#define SGE(dest, mask, src0, src1) dest.mask = _SGE(_in(src0), _in(src1)).mask\n"
"vec4 _SGE(vec4 src0, vec4 src1)\n"
"{\n"
" return vec4(greaterThanEqual(src0, src1));\n"
"}\n"
"\n"
"#define RCP(dest, mask, src) dest.mask = _RCP(_in(src).x).mask\n"
"vec4 _RCP(float src)\n"
"{\n"
" return vec4(1.0 / src);\n"
"}\n"
"\n"
"#define RCC(dest, mask, src) dest.mask = _RCC(_in(src).x).mask\n"
"vec4 _RCC(float src)\n"
"{\n"
" float t = 1.0 / src;\n"
" if (t > 0.0) {\n"
" t = clamp(t, 5.42101e-020, 1.884467e+019);\n"
" } else {\n"
" t = clamp(t, -1.884467e+019, -5.42101e-020);\n"
" }\n"
" return vec4(t);\n"
"}\n"
"\n"
"#define RSQ(dest, mask, src) dest.mask = _RSQ(_in(src).x).mask\n"
"vec4 _RSQ(float src)\n"
"{\n"
" if (src == 0.0) { return vec4(INFINITY); }\n"
" if (isinf(src)) { return vec4(0.0); }\n"
" return vec4(inversesqrt(abs(src)));\n"
"}\n"
"\n"
"#define EXP(dest, mask, src) dest.mask = _EXP(_in(src).x).mask\n"
"vec4 _EXP(float src)\n"
"{\n"
" return vec4(exp2(src));\n"
"}\n"
"\n"
"#define LOG(dest, mask, src) dest.mask = _LOG(_in(src).x).mask\n"
"vec4 _LOG(float src)\n"
"{\n"
" return vec4(log2(src));\n"
"}\n"
"\n"
"#define LIT(dest, mask, src) dest.mask = _LIT(_in(src)).mask\n"
"vec4 _LIT(vec4 src)\n"
"{\n"
" vec4 s = src;\n"
" float epsilon = 1.0 / 256.0;\n"
" s.w = clamp(s.w, -(128.0 - epsilon), 128.0 - epsilon);\n"
" s.x = max(s.x, 0.0);\n"
" s.y = max(s.y, 0.0);\n"
" vec4 t = vec4(1.0, 0.0, 0.0, 1.0);\n"
" t.y = s.x;\n"
#if 1
" t.z = (s.x > 0.0) ? exp2(s.w * log2(s.y)) : 0.0;\n"
#else
" t.z = (s.x > 0.0) ? pow(s.y, s.w) : 0.0;\n"
#endif
" return t;\n"
"}\n";
void vsh_translate(uint16_t version,
const uint32_t *tokens,
unsigned int length,
bool z_perspective,
QString *header, QString *body)
{
qstring_append(header, vsh_header);
bool has_final = false;
int slot;
for (slot=0; slot < length; slot++) {
const uint32_t* cur_token = &tokens[slot * VSH_TOKEN_SIZE];
QString *token_str = decode_token(cur_token);
qstring_append_fmt(body,
" /* Slot %d: 0x%08X 0x%08X 0x%08X 0x%08X */",
slot,
cur_token[0],cur_token[1],cur_token[2],cur_token[3]);
qstring_append(body, "\n");
qstring_append(body, qstring_get_str(token_str));
qstring_append(body, "\n");
QDECREF(token_str);
if (vsh_get_field(cur_token, FLD_FINAL)) {
has_final = true;
break;
}
}
assert(has_final);
/* pre-divide and output the generated W so we can do persepctive correct
* interpolation manually. OpenGL can't, since we give it a W of 1 to work
* around the perspective divide */
qstring_append(body,
" if (oPos.w == 0.0 || isinf(oPos.w)) {\n"
" vtx.inv_w = 1.0;\n"
" } else {\n"
" vtx.inv_w = 1.0 / oPos.w;\n"
" }\n"
);
qstring_append(body,
/* the shaders leave the result in screen space, while
* opengl expects it in clip space.
* TODO: the pixel-center co-ordinate differences should handled
*/
" oPos.x = 2.0 * (oPos.x - surfaceSize.x * 0.5) / surfaceSize.x;\n"
" oPos.y = -2.0 * (oPos.y - surfaceSize.y * 0.5) / surfaceSize.y;\n"
);
if (z_perspective) {
qstring_append(body, " oPos.z = oPos.w;\n");
}
qstring_append(body,
/* Map the clip range into clip space so z is clipped correctly.
* Note this makes the values in the depth buffer wrong. This should be
* handled with gl_ClipDistance instead, but that has performance issues
* on OS X.
*/
" if (clipRange.y != clipRange.x) {\n"
" oPos.z = (oPos.z - 0.5 * (clipRange.x + clipRange.y)) / (0.5 * (clipRange.y - clipRange.x));\n"
" }\n"
/* Correct for the perspective divide */
" if (oPos.w < 0.0) {\n"
/* undo the perspective divide in the case where the point would be
* clipped so opengl can clip it correctly */
" oPos.xyz *= oPos.w;\n"
" } else {\n"
/* we don't want the OpenGL perspective divide to happen, but we
* can't multiply by W because it could be meaningless here */
" oPos.w = 1.0;\n"
" }\n"
);
}

142
hw/xbox/nv2a_vsh.h Normal file
View File

@ -0,0 +1,142 @@
/*
* QEMU Geforce NV2A vertex shader translation
*
* 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 version 2 as published by the Free Software Foundation.
*
* 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/>
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#ifndef HW_NV2A_VSH_H
#define HW_NV2A_VSH_H
#include <stdbool.h>
#include "qapi/qmp/qstring.h"
enum VshLight {
LIGHT_OFF,
LIGHT_INFINITE,
LIGHT_LOCAL,
LIGHT_SPOT
};
enum VshTexgen {
TEXGEN_DISABLE,
TEXGEN_EYE_LINEAR,
TEXGEN_OBJECT_LINEAR,
TEXGEN_SPHERE_MAP,
TEXGEN_NORMAL_MAP,
TEXGEN_REFLECTION_MAP,
};
enum VshFogMode {
FOG_MODE_LINEAR,
FOG_MODE_EXP,
FOG_MODE_ERROR2, /* Doesn't exist */
FOG_MODE_EXP2,
FOG_MODE_LINEAR_ABS,
FOG_MODE_EXP_ABS,
FOG_MODE_ERROR6, /* Doesn't exist */
FOG_MODE_EXP2_ABS
};
enum VshFoggen {
FOGGEN_SPEC_ALPHA,
FOGGEN_RADIAL,
FOGGEN_PLANAR,
FOGGEN_ABS_PLANAR,
FOGGEN_ERROR4,
FOGGEN_ERROR5,
FOGGEN_FOG_X
};
enum VshSkinning {
SKINNING_OFF,
SKINNING_1WEIGHTS,
SKINNING_2WEIGHTS,
SKINNING_3WEIGHTS,
SKINNING_2WEIGHTS2MATRICES,
SKINNING_3WEIGHTS3MATRICES,
SKINNING_4WEIGHTS4MATRICES,
};
// vs.1.1, not an official value
#define VSH_VERSION_VS 0xF078
// Xbox vertex shader
#define VSH_VERSION_XVS 0x2078
// Xbox vertex state shader
#define VSH_VERSION_XVSS 0x7378
// Xbox vertex read/write shader
#define VSH_VERSION_XVSW 0x7778
#define VSH_TOKEN_SIZE 4
typedef enum {
FLD_ILU = 0,
FLD_MAC,
FLD_CONST,
FLD_V,
// Input A
FLD_A_NEG,
FLD_A_SWZ_X,
FLD_A_SWZ_Y,
FLD_A_SWZ_Z,
FLD_A_SWZ_W,
FLD_A_R,
FLD_A_MUX,
// Input B
FLD_B_NEG,
FLD_B_SWZ_X,
FLD_B_SWZ_Y,
FLD_B_SWZ_Z,
FLD_B_SWZ_W,
FLD_B_R,
FLD_B_MUX,
// Input C
FLD_C_NEG,
FLD_C_SWZ_X,
FLD_C_SWZ_Y,
FLD_C_SWZ_Z,
FLD_C_SWZ_W,
FLD_C_R_HIGH,
FLD_C_R_LOW,
FLD_C_MUX,
// Output
FLD_OUT_MAC_MASK,
FLD_OUT_R,
FLD_OUT_ILU_MASK,
FLD_OUT_O_MASK,
FLD_OUT_ORB,
FLD_OUT_ADDRESS,
FLD_OUT_MUX,
// Relative addressing
FLD_A0X,
// Final instruction
FLD_FINAL
} VshFieldName;
uint8_t vsh_get_field(const uint32_t *shader_token, VshFieldName field_name);
void vsh_translate(uint16_t version,
const uint32_t *tokens,
unsigned int length,
bool z_perspective,
QString *header, QString *body);
#endif

1033
hw/xbox/nvnet.c Normal file

File diff suppressed because it is too large Load Diff

86
hw/xbox/smbus_adm1032.c Normal file
View File

@ -0,0 +1,86 @@
/*
* QEMU SMBus ADM1032 Temperature Monitor
*
* Copyright (c) 2012 espes
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "hw/hw.h"
#include "hw/i2c/i2c.h"
#include "hw/i2c/smbus.h"
#define DEBUG
static uint8_t tm_read_data(SMBusDevice *dev, uint8_t cmd, int n)
{
#ifdef DEBUG
printf("tm_read_data: addr=0x%02x cmd=0x%02x n=%d\n",
dev->i2c.address, cmd, n);
#endif
switch (cmd) {
case 0x0:
case 0x1:
return 50;
default:
break;
}
return 0;
}
static int tm_init(SMBusDevice *dev)
{
return 0;
}
static void smbus_adm1032_class_initfn(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(klass);
sc->init = tm_init;
sc->read_data = tm_read_data;
}
static TypeInfo smbus_adm1032_info = {
.name = "smbus-adm1032",
.parent = TYPE_SMBUS_DEVICE,
.instance_size = sizeof(SMBusDevice),
.class_init = smbus_adm1032_class_initfn,
};
static void smbus_adm1032_register_devices(void)
{
type_register_static(&smbus_adm1032_info);
}
type_init(smbus_adm1032_register_devices)
void smbus_adm1032_init(i2c_bus *smbus, int address)
{
DeviceState *tm;
tm = qdev_create((BusState *)smbus, "smbus-adm1032");
qdev_prop_set_uint8(tm, "address", address);
qdev_init_nofail(tm);
}

117
hw/xbox/smbus_cx25871.c Normal file
View File

@ -0,0 +1,117 @@
/*
* QEMU SMBus Conexant CX25871 Video Encoder
*
* 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/i2c/i2c.h"
#include "hw/i2c/smbus.h"
typedef struct SMBusCX25871Device {
SMBusDevice smbusdev;
uint8_t registers[256];
} SMBusCX25871Device;
//#define DEBUG
static void cx_quick_cmd(SMBusDevice *dev, uint8_t read)
{
#ifdef DEBUG
printf("cx_quick_cmd: addr=0x%02x read=%d\n", dev->i2c.address, read);
#endif
}
static void cx_send_byte(SMBusDevice *dev, uint8_t val)
{
#ifdef DEBUG
printf("cx_send_byte: addr=0x%02x val=0x%02x\n",
dev->i2c.address, val);
#endif
}
static uint8_t cx_receive_byte(SMBusDevice *dev)
{
#ifdef DEBUG
printf("cx_receive_byte: addr=0x%02x\n",
dev->i2c.address);
#endif
return 0;
}
static void cx_write_data(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len)
{
SMBusCX25871Device *cx = (SMBusCX25871Device *) dev;
#ifdef DEBUG
printf("cx_write_byte: addr=0x%02x cmd=0x%02x val=0x%02x\n",
dev->i2c.address, cmd, buf[0]);
#endif
memcpy(cx->registers+cmd, buf, MIN(len, 256-cmd));
}
static uint8_t cx_read_data(SMBusDevice *dev, uint8_t cmd, int n)
{
SMBusCX25871Device *cx = (SMBusCX25871Device *) dev;
#ifdef DEBUG
printf("cx_read_data: addr=0x%02x cmd=0x%02x n=%d\n",
dev->i2c.address, cmd, n);
#endif
return cx->registers[cmd];
}
static int smbus_cx_init(SMBusDevice *dev)
{
return 0;
}
static void smbus_cx25871_class_initfn(ObjectClass *klass, void *data)
{
SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(klass);
sc->init = smbus_cx_init;
sc->quick_cmd = cx_quick_cmd;
sc->send_byte = cx_send_byte;
sc->receive_byte = cx_receive_byte;
sc->write_data = cx_write_data;
sc->read_data = cx_read_data;
}
static TypeInfo smbus_cx25871_info = {
.name = "smbus-cx25871",
.parent = TYPE_SMBUS_DEVICE,
.instance_size = sizeof(SMBusCX25871Device),
.class_init = smbus_cx25871_class_initfn,
};
static void smbus_cx25871_register_devices(void)
{
type_register_static(&smbus_cx25871_info);
}
type_init(smbus_cx25871_register_devices)
void smbus_cx25871_init(i2c_bus *smbus, int address)
{
DeviceState *cx;
cx = qdev_create((BusState *)smbus, "smbus-cx25871");
qdev_prop_set_uint8(cx, "address", address);
qdev_init_nofail(cx);
}

227
hw/xbox/smbus_xbox_smc.c Normal file
View File

@ -0,0 +1,227 @@
/*
* QEMU SMBus Xbox System Management Controller
*
* Copyright (c) 2011 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/i2c/i2c.h"
#include "hw/i2c/smbus.h"
#include "qemu/config-file.h"
#include "sysemu/sysemu.h"
/*
* Hardware is a PIC16LC
* http://www.xbox-linux.org/wiki/PIC
*/
#define SMC_REG_VER 0x01
#define SMC_REG_POWER 0x02
#define SMC_REG_POWER_RESET 0x01
#define SMC_REG_POWER_CYCLE 0x40
#define SMC_REG_POWER_SHUTDOWN 0x80
#define SMC_REG_TRAYSTATE 0x03
#define SMC_REG_AVPACK 0x04
#define SMC_REG_AVPACK_SCART 0x00
#define SMC_REG_AVPACK_HDTV 0x01
#define SMC_REG_AVPACK_VGA_SOG 0x02
#define SMC_REG_AVPACK_SVIDEO 0x04
#define SMC_REG_AVPACK_COMPOSITE 0x06
#define SMC_REG_AVPACK_VGA 0x07
#define SMC_REG_FANMODE 0x05
#define SMC_REG_FANSPEED 0x06
#define SMC_REG_LEDMODE 0x07
#define SMC_REG_LEDSEQ 0x08
#define SMC_REG_CPUTEMP 0x09
#define SMC_REG_BOARDTEMP 0x0a
#define SMC_REG_TRAYEJECT 0x0c
#define SMC_REG_INTACK 0x0d
#define SMC_REG_INTSTATUS 0x11
#define SMC_REG_INTSTATUS_POWER 0x01
#define SMC_REG_INTSTATUS_TRAYCLOSED 0x02
#define SMC_REG_INTSTATUS_TRAYOPENING 0x04
#define SMC_REG_INTSTATUS_AVPACK_PLUG 0x08
#define SMC_REG_INTSTATUS_AVPACK_UNPLUG 0x10
#define SMC_REG_INTSTATUS_EJECT_BUTTON 0x20
#define SMC_REG_INTSTATUS_TRAYCLOSING 0x40
#define SMC_REG_RESETONEJECT 0x19
#define SMC_REG_INTEN 0x1a
#define SMC_REG_SCRATCH 0x1b
#define SMC_REG_SCRATCH_SHORT_ANIMATION 0x04
static const char* smc_version_string = "P01";
//#define DEBUG
typedef struct SMBusSMCDevice {
SMBusDevice smbusdev;
int version_string_index;
uint8_t scratch_reg;
} SMBusSMCDevice;
static void smc_quick_cmd(SMBusDevice *dev, uint8_t read)
{
#ifdef DEBUG
printf("smc_quick_cmd: addr=0x%02x read=%d\n", dev->i2c.address, read);
#endif
}
static void smc_send_byte(SMBusDevice *dev, uint8_t val)
{
#ifdef DEBUG
printf("smc_send_byte: addr=0x%02x val=0x%02x\n",
dev->i2c.address, val);
#endif
}
static uint8_t smc_receive_byte(SMBusDevice *dev)
{
#ifdef DEBUG
printf("smc_receive_byte: addr=0x%02x\n",
dev->i2c.address);
#endif
return 0;
}
static void smc_write_data(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len)
{
SMBusSMCDevice *smc = (SMBusSMCDevice *) dev;
#ifdef DEBUG
printf("smc_write_byte: addr=0x%02x cmd=0x%02x val=0x%02x\n",
dev->i2c.address, cmd, buf[0]);
#endif
switch(cmd) {
case SMC_REG_VER:
/* version string reset */
smc->version_string_index = buf[0];
break;
case SMC_REG_POWER:
if (buf[0] & (SMC_REG_POWER_RESET | SMC_REG_POWER_CYCLE))
qemu_system_reset_request();
else if (buf[0] & SMC_REG_POWER_SHUTDOWN)
qemu_system_shutdown_request();
break;
case SMC_REG_SCRATCH:
smc->scratch_reg = buf[0];
break;
/* challenge response
* (http://www.xbox-linux.org/wiki/PIC_Challenge_Handshake_Sequence) */
case 0x20:
break;
case 0x21:
break;
default:
break;
}
}
static uint8_t smc_read_data(SMBusDevice *dev, uint8_t cmd, int n)
{
SMBusSMCDevice *smc = (SMBusSMCDevice *) dev;
#ifdef DEBUG
printf("smc_read_data: addr=0x%02x cmd=0x%02x n=%d\n",
dev->i2c.address, cmd, n);
#endif
switch(cmd) {
case SMC_REG_VER:
return smc_version_string[
smc->version_string_index++%(sizeof(smc_version_string)-1)];
case SMC_REG_AVPACK:
/* pretend to have a composite av pack plugged in */
return SMC_REG_AVPACK_COMPOSITE;
case SMC_REG_SCRATCH:
return smc->scratch_reg;
/* challenge request:
* must be non-0 */
case 0x1c:
return 0x52;
case 0x1d:
return 0x72;
case 0x1e:
return 0xea;
case 0x1f:
return 0x46;
default:
break;
}
return 0;
}
static int smbus_smc_init(SMBusDevice *dev)
{
QemuOpts *opts;
SMBusSMCDevice *smc = (SMBusSMCDevice *)dev;
smc->version_string_index = 0;
smc->scratch_reg = 0;
opts = qemu_opts_find(qemu_find_opts("machine"), NULL);
if (opts && qemu_opt_get_bool(opts, "short_animation", 0)) {
smc->scratch_reg = SMC_REG_SCRATCH_SHORT_ANIMATION;
}
return 0;
}
static void smbus_smc_class_initfn(ObjectClass *klass, void *data)
{
SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(klass);
sc->init = smbus_smc_init;
sc->quick_cmd = smc_quick_cmd;
sc->send_byte = smc_send_byte;
sc->receive_byte = smc_receive_byte;
sc->write_data = smc_write_data;
sc->read_data = smc_read_data;
}
static TypeInfo smbus_smc_info = {
.name = "smbus-xbox-smc",
.parent = TYPE_SMBUS_DEVICE,
.instance_size = sizeof(SMBusSMCDevice),
.class_init = smbus_smc_class_initfn,
};
static void smbus_smc_register_devices(void)
{
type_register_static(&smbus_smc_info);
}
type_init(smbus_smc_register_devices)
void smbus_xbox_smc_init(i2c_bus *smbus, int address)
{
DeviceState *smc;
smc = qdev_create((BusState *)smbus, "smbus-xbox-smc");
qdev_prop_set_uint8(smc, "address", address);
qdev_init_nofail(smc);
}

165
hw/xbox/swizzle.c Normal file
View File

@ -0,0 +1,165 @@
/*
* QEMU texture swizzling routines
*
* Copyright (c) 2015 Jannik Vogel
* Copyright (c) 2013 espes
* Copyright (c) 2007-2010 The Nouveau Project.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2 as published by the Free Software Foundation.
*
* 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/>
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#include <stdint.h>
#include <string.h>
#include <assert.h>
#include "qemu/osdep.h"
#include "hw/xbox/swizzle.h"
/* This should be pretty straightforward.
* It creates a bit pattern like ..zyxzyxzyx from ..xxx, ..yyy and ..zzz
* If there are no bits left from any component it will pack the other masks
* more tighly (Example: zzxzxzyx = Fewer x than z and even fewer y)
*/
static void generate_swizzle_masks(unsigned int width,
unsigned int height,
unsigned int depth,
uint32_t* mask_x,
uint32_t* mask_y,
uint32_t* mask_z)
{
uint32_t x = 0, y = 0, z = 0;
uint32_t bit = 1;
uint32_t mask_bit = 1;
bool done;
do {
done = true;
if (bit < width) { x |= mask_bit; mask_bit <<= 1; done = false; }
if (bit < height) { y |= mask_bit; mask_bit <<= 1; done = false; }
if (bit < depth) { z |= mask_bit; mask_bit <<= 1; done = false; }
bit <<= 1;
} while(!done);
assert(x ^ y ^ z == (mask_bit - 1));
*mask_x = x;
*mask_y = y;
*mask_z = z;
}
/* This fills a pattern with a value if your value has bits abcd and your
* pattern is 11010100100 this will return: 0a0b0c00d00
*/
static uint32_t fill_pattern(uint32_t pattern, uint32_t value)
{
uint32_t result = 0;
uint32_t bit = 1;
while(value) {
if (pattern & bit) {
/* Copy bit to result */
result |= value & 1 ? bit : 0;
value >>= 1;
}
bit <<= 1;
}
return result;
}
static unsigned int get_swizzled_offset(
unsigned int x, unsigned int y, unsigned int z,
uint32_t mask_x, uint32_t mask_y, uint32_t mask_z,
unsigned int bytes_per_pixel)
{
return bytes_per_pixel * (fill_pattern(mask_x, x)
| fill_pattern(mask_y, y)
| fill_pattern(mask_z, z));
}
void swizzle_box(
const uint8_t *src_buf,
unsigned int width,
unsigned int height,
unsigned int depth,
uint8_t *dst_buf,
unsigned int row_pitch,
unsigned int slice_pitch,
unsigned int bytes_per_pixel)
{
uint32_t mask_x, mask_y, mask_z;
generate_swizzle_masks(width, height, depth, &mask_x, &mask_y, &mask_z);
int x, y, z;
for (z = 0; z < depth; z++) {
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
const uint8_t *src = src_buf
+ y * row_pitch + x * bytes_per_pixel;
uint8_t *dst = dst_buf + get_swizzled_offset(x, y, 0,
mask_x, mask_y, 0,
bytes_per_pixel);
memcpy(dst, src, bytes_per_pixel);
}
}
src_buf += slice_pitch;
}
}
void unswizzle_box(
const uint8_t *src_buf,
unsigned int width,
unsigned int height,
unsigned int depth,
uint8_t *dst_buf,
unsigned int row_pitch,
unsigned int slice_pitch,
unsigned int bytes_per_pixel)
{
uint32_t mask_x, mask_y, mask_z;
generate_swizzle_masks(width, height, depth, &mask_x, &mask_y, &mask_z);
int x, y, z;
for (z = 0; z < depth; z++) {
for (y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
const uint8_t *src = src_buf
+ get_swizzled_offset(x, y, z, mask_x, mask_y, mask_z,
bytes_per_pixel);
uint8_t *dst = dst_buf + y * row_pitch + x * bytes_per_pixel;
memcpy(dst, src, bytes_per_pixel);
}
}
dst_buf += slice_pitch;
}
}
void unswizzle_rect(
const uint8_t *src_buf,
unsigned int width,
unsigned int height,
uint8_t *dst_buf,
unsigned int pitch,
unsigned int bytes_per_pixel)
{
unswizzle_box(src_buf, width, height, 1, dst_buf, pitch, 0, bytes_per_pixel);
}
void swizzle_rect(
const uint8_t *src_buf,
unsigned int width,
unsigned int height,
uint8_t *dst_buf,
unsigned int pitch,
unsigned int bytes_per_pixel)
{
swizzle_box(src_buf, width, height, 1, dst_buf, pitch, 0, bytes_per_pixel);
}

62
hw/xbox/swizzle.h Normal file
View File

@ -0,0 +1,62 @@
/*
* QEMU texture swizzling routines
*
* Copyright (c) 2015 Jannik Vogel
* Copyright (c) 2013 espes
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2 as published by the Free Software Foundation.
*
* 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/>
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#ifndef HW_XBOX_SWIZZLE_H
#define HW_XBOX_SWIZZLE_H
void swizzle_box(
const uint8_t *src_buf,
unsigned int width,
unsigned int height,
unsigned int depth,
uint8_t *dst_buf,
unsigned int row_pitch,
unsigned int slice_pitch,
unsigned int bytes_per_pixel);
void unswizzle_box(
const uint8_t *src_buf,
unsigned int width,
unsigned int height,
unsigned int depth,
uint8_t *dst_buf,
unsigned int row_pitch,
unsigned int slice_pitch,
unsigned int bytes_per_pixel);
void unswizzle_rect(
const uint8_t *src_buf,
unsigned int width,
unsigned int height,
uint8_t *dst_buf,
unsigned int pitch,
unsigned int bytes_per_pixel);
void swizzle_rect(
const uint8_t *src_buf,
unsigned int width,
unsigned int height,
uint8_t *dst_buf,
unsigned int pitch,
unsigned int bytes_per_pixel);
#endif

358
hw/xbox/xbox.c Normal file
View File

@ -0,0 +1,358 @@
/*
* QEMU Xbox System Emulator
*
* Copyright (c) 2012 espes
*
* Based on pc.c
* Copyright (c) 2003-2004 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/>.
*/
#include "hw/hw.h"
#include "sysemu/arch_init.h"
#include "hw/i386/pc.h"
#include "hw/pci/pci.h"
#include "hw/boards.h"
#include "hw/ide.h"
#include "hw/timer/mc146818rtc.h"
#include "hw/timer/i8254.h"
#include "hw/audio/pcspk.h"
#include "sysemu/sysemu.h"
#include "hw/cpu/icc_bus.h"
#include "hw/sysbus.h"
#include "hw/i2c/smbus.h"
#include "sysemu/blockdev.h"
#include "hw/loader.h"
#include "exec/address-spaces.h"
#include "hw/xbox/xbox_pci.h"
#include "hw/xbox/nv2a.h"
#include "hw/xbox/xbox.h"
#include "net/net.h"
/* mostly from pc_memory_init */
static void xbox_memory_init(MemoryRegion *system_memory,
ram_addr_t mem_size,
MemoryRegion *rom_memory,
MemoryRegion **ram_memory)
{
MemoryRegion *ram;
int ret;
char *filename;
int bios_size;
MemoryRegion *bios;
MemoryRegion *map_bios;
uint32_t map_loc;
/* Allocate RAM. We allocate it as a single memory region and use
* aliases to address portions of it, mostly for backwards compatibility
* with older qemus that used qemu_ram_alloc().
*/
ram = g_malloc(sizeof(*ram));
memory_region_init_ram(ram, NULL, "xbox.ram", mem_size);
vmstate_register_ram_global(ram);
*ram_memory = ram;
memory_region_add_subregion(system_memory, 0, ram);
/* Load the bios. (mostly from pc_sysfw)
* Can't use it verbatim, since we need the bios repeated
* over top 1MB of memory.
*/
if (bios_name == NULL) {
bios_name = "bios.bin";
}
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
if (filename) {
bios_size = get_image_size(filename);
} else {
bios_size = -1;
}
if (bios_size <= 0 ||
(bios_size % 65536) != 0) {
goto bios_error;
}
bios = g_malloc(sizeof(*bios));
memory_region_init_ram(bios, NULL, "xbox.bios", bios_size);
vmstate_register_ram_global(bios);
memory_region_set_readonly(bios, true);
ret = rom_add_file_fixed(bios_name, (uint32_t)(-bios_size), -1);
if (ret != 0) {
bios_error:
fprintf(stderr, "qemu: could not load xbox BIOS '%s'\n", bios_name);
exit(1);
}
if (filename) {
g_free(filename);
}
/* map the bios repeated at the top of memory */
for (map_loc=(uint32_t)(-bios_size); map_loc >= 0xff000000; map_loc-=bios_size) {
map_bios = g_malloc(sizeof(*map_bios));
memory_region_init_alias(map_bios, NULL, NULL, bios, 0, bios_size);
memory_region_add_subregion(rom_memory, map_loc, map_bios);
memory_region_set_readonly(map_bios, true);
}
/*memory_region_add_subregion(rom_memory,
(uint32_t)(-bios_size),
bios);
*/
}
/* mostly from pc_init1 */
void xbox_init_common(QEMUMachineInitArgs *args,
const uint8_t *default_eeprom,
ISABus **out_isa_bus)
{
int i;
ram_addr_t ram_size = args->ram_size;
const char *cpu_model = args->cpu_model;
PCIBus *host_bus;
ISABus *isa_bus;
MemoryRegion *ram_memory;
MemoryRegion *pci_memory;
qemu_irq *cpu_irq;
qemu_irq *gsi;
qemu_irq *i8259;
GSIState *gsi_state;
PCIDevice *ide_dev;
BusState *idebus[MAX_IDE_BUS];
ISADevice *rtc_state;
ISADevice *pit;
i2c_bus *smbus;
PCIBus *agp_bus;
DeviceState *icc_bridge;
icc_bridge = qdev_create(NULL, TYPE_ICC_BRIDGE);
object_property_add_child(qdev_get_machine(), "icc-bridge",
OBJECT(icc_bridge), NULL);
pc_cpus_init(cpu_model, icc_bridge);
pci_memory = g_new(MemoryRegion, 1);
memory_region_init(pci_memory, NULL, "pci", INT64_MAX);
/* allocate ram and load rom/bios */
xbox_memory_init(get_system_memory(), ram_size,
pci_memory, &ram_memory);
gsi_state = g_malloc0(sizeof(*gsi_state));
gsi = qemu_allocate_irqs(gsi_handler, gsi_state, GSI_NUM_PINS);
/* init buses */
xbox_pci_init(gsi,
get_system_memory(), get_system_io(),
pci_memory, ram_memory,
&host_bus,
&isa_bus,
&smbus,
&agp_bus);
/* irq shit */
isa_bus_irqs(isa_bus, gsi);
cpu_irq = pc_allocate_cpu_irq();
i8259 = i8259_init(isa_bus, cpu_irq[0]);
for (i = 0; i < ISA_NUM_IRQS; i++) {
gsi_state->i8259_irq[i] = i8259[i];
}
/* basic device init */
rtc_state = rtc_init(isa_bus, 2000, NULL);
pit = pit_init(isa_bus, 0x40, 0, NULL);
/* does apparently have a pc speaker, though not used? */
pcspk_init(isa_bus, pit);
/* IDE shit
* piix3's ide be right for now, maybe
*/
DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS];
ide_drive_get(hd, MAX_IDE_BUS);
ide_dev = pci_piix3_ide_init(host_bus, hd, PCI_DEVFN(9, 0));
idebus[0] = qdev_get_child_bus(&ide_dev->qdev, "ide.0");
idebus[1] = qdev_get_child_bus(&ide_dev->qdev, "ide.1");
// xbox bios wants this bit pattern set to mark the data as valid
uint8_t bits = 0x55;
for (i = 0x10; i < 0x70; i++) {
rtc_set_memory(rtc_state, i, bits);
bits = ~bits;
}
bits = 0x55;
for (i = 0x80; i < 0x100; i++) {
rtc_set_memory(rtc_state, i, bits);
bits = ~bits;
}
/* smbus devices */
uint8_t *eeprom_buf = g_malloc0(256);
memcpy(eeprom_buf, default_eeprom, 256);
smbus_eeprom_init_single(smbus, 0x54, eeprom_buf);
smbus_xbox_smc_init(smbus, 0x10);
smbus_cx25871_init(smbus, 0x45);
smbus_adm1032_init(smbus, 0x4c);
/* USB */
PCIDevice *usb1 = pci_create(host_bus, PCI_DEVFN(3, 0), "pci-ohci");
qdev_prop_set_uint32(&usb1->qdev, "num-ports", 4);
qdev_init_nofail(&usb1->qdev);
PCIDevice *usb0 = pci_create(host_bus, PCI_DEVFN(2, 0), "pci-ohci");
qdev_prop_set_uint32(&usb0->qdev, "num-ports", 4);
qdev_init_nofail(&usb0->qdev);
/* Ethernet! */
PCIDevice *nvnet = pci_create(host_bus, PCI_DEVFN(4, 0), "nvnet");
for (i = 0; i < nb_nics; i++) {
NICInfo *nd = &nd_table[i];
qemu_check_nic_model(nd, "nvnet");
qdev_set_nic_properties(&nvnet->qdev, nd);
qdev_init_nofail(&nvnet->qdev);
}
/* APU! */
PCIDevice *apu = pci_create_simple(host_bus, PCI_DEVFN(5, 0), "mcpx-apu");
/* ACI! */
PCIDevice *aci = pci_create_simple(host_bus, PCI_DEVFN(6, 0), "mcpx-aci");
/* GPU! */
nv2a_init(agp_bus, PCI_DEVFN(0, 0), ram_memory);
*out_isa_bus = isa_bus;
}
static void xbox_init(QEMUMachineInitArgs *args)
{
#if 0
/* Placeholder blank eeprom for xbox 1.0:
* Serial number 000000000000
* Mac address 00:00:00:00:00:00
* ...etc.
*/
const uint8_t eeprom[] = {
0x25, 0x42, 0x88, 0x24, 0xA3, 0x1A, 0x7D, 0xF4,
0xEE, 0x53, 0x3F, 0x39, 0x5D, 0x27, 0x98, 0x0E,
0x58, 0xB3, 0x26, 0xC3, 0x70, 0x82, 0xE5, 0xC6,
0xF7, 0xC5, 0x54, 0x38, 0xA0, 0x58, 0xB9, 0x5D,
0xB7, 0x27, 0xC7, 0xB1, 0x67, 0xCF, 0x99, 0x3E,
0xC8, 0x6E, 0xC8, 0x53, 0xEF, 0x7C, 0x01, 0x37,
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
};
#endif
/* bunnie's eeprom */
const uint8_t eeprom[] = {
0xe3, 0x1c, 0x5c, 0x23, 0x6a, 0x58, 0x68, 0x37,
0xb7, 0x12, 0x26, 0x6c, 0x99, 0x11, 0x30, 0xd1,
0xe2, 0x3e, 0x4d, 0x56, 0xf7, 0x73, 0x2b, 0x73,
0x85, 0xfe, 0x7f, 0x0a, 0x08, 0xef, 0x15, 0x3c,
0x77, 0xee, 0x6d, 0x4e, 0x93, 0x2f, 0x28, 0xee,
0xf8, 0x61, 0xf7, 0x94, 0x17, 0x1f, 0xfc, 0x11,
0x0b, 0x84, 0x44, 0xed, 0x31, 0x30, 0x35, 0x35,
0x38, 0x31, 0x31, 0x31, 0x34, 0x30, 0x30, 0x33,
0x00, 0x50, 0xf2, 0x4f, 0x65, 0x52, 0x00, 0x00,
0x0a, 0x1e, 0x35, 0x33, 0x71, 0x85, 0x31, 0x4d,
0x59, 0x12, 0x38, 0x48, 0x1c, 0x91, 0x53, 0x60,
0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
0x75, 0x61, 0x57, 0xfb, 0x2c, 0x01, 0x00, 0x00,
0x45, 0x53, 0x54, 0x00, 0x45, 0x44, 0x54, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0a, 0x05, 0x00, 0x02, 0x04, 0x01, 0x00, 0x02,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xc4, 0xff, 0xff, 0xff,
0x01, 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
};
ISABus *isa_bus;
xbox_init_common(args, eeprom, &isa_bus);
}
static QEMUMachine xbox_machine = {
.name = "xbox",
.desc = "Microsoft Xbox",
.init = xbox_init,
.max_cpus = 1,
.no_floppy = 1,
.no_cdrom = 1,
.no_sdcard = 1,
PC_DEFAULT_MACHINE_OPTIONS
};
static void xbox_machine_init(void) {
qemu_register_machine(&xbox_machine);
}
machine_init(xbox_machine_init);

32
hw/xbox/xbox.h Normal file
View File

@ -0,0 +1,32 @@
/*
* QEMU Xbox System Emulator
*
* Copyright (c) 2013 espes
*
* Based on pc.c
* Copyright (c) 2003-2004 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 HW_XBOX_H
#define HW_XBOX_H
#define MAX_IDE_BUS 2
void xbox_init_common(QEMUMachineInitArgs *args,
const uint8_t *default_eeprom,
ISABus **out_isa_bus);
#endif

555
hw/xbox/xbox_pci.c Normal file
View File

@ -0,0 +1,555 @@
/*
* QEMU Xbox PCI buses 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 "qemu/range.h"
#include "hw/isa/isa.h"
#include "hw/sysbus.h"
#include "hw/loader.h"
#include "qemu/config-file.h"
#include "hw/i386/pc.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_bus.h"
#include "hw/pci/pci_bridge.h"
#include "exec/address-spaces.h"
#include "qemu-common.h"
#include "hw/xbox/acpi_xbox.h"
#include "hw/xbox/amd_smbus.h"
#include "hw/xbox/xbox_pci.h"
/*
* xbox chipset based on nForce 420, which was based on AMD-760
*
* http://support.amd.com/us/ChipsetMotherboard_TechDocs/24494.pdf
* http://support.amd.com/us/ChipsetMotherboard_TechDocs/24416.pdf
* http://support.amd.com/us/ChipsetMotherboard_TechDocs/24467.pdf
*
* http://support.amd.com/us/ChipsetMotherboard_TechDocs/24462.pdf
*
* - 'NV2A' combination northbridge/gpu
* - 'MCPX' combination southbridge/apu
*/
//#define DEBUG
#ifdef DEBUG
# define XBOXPCI_DPRINTF(format, ...) printf(format, ## __VA_ARGS__)
#else
# define XBOXPCI_DPRINTF(format, ...) do { } while (0)
#endif
#define XBOX_NUM_INT_IRQS 8
#define XBOX_NUM_PIRQS 4
#define XBOX_NUM_PIC_IRQS 16
#define XBOX_LPC_ACPI_IRQ_ROUT 0x64
#define XBOX_LPC_PIRQ_ROUT 0x68
#define XBOX_LPC_INT_IRQ_ROUT 0x6C
static void xbox_lpc_set_irq(void *opaque, int pirq, int level)
{
XBOX_LPCState *lpc = opaque;
assert(pirq >= 0);
assert(pirq < XBOX_NUM_INT_IRQS + XBOX_NUM_PIRQS);
int pic_irq = 0;
if (pirq < XBOX_NUM_INT_IRQS) {
/* devices on the internal bus */
uint32_t routing = pci_get_long(lpc->dev.config + XBOX_LPC_INT_IRQ_ROUT);
pic_irq = (routing >> (pirq*4)) & 0xF;
if (pic_irq == 0) {
return;
}
} else {
/* pirqs */
pirq -= XBOX_NUM_INT_IRQS;
pic_irq = lpc->dev.config[XBOX_LPC_PIRQ_ROUT + pirq];
}
if (pic_irq >= XBOX_NUM_PIC_IRQS) {
return;
}
qemu_set_irq(lpc->pic[pic_irq], level);
}
static int xbox_lpc_map_irq(PCIDevice *pci_dev, int intx)
{
int slot = PCI_SLOT(pci_dev->devfn);
switch (slot) {
/* devices on the internal bus */
case 2: return 0; /* usb0 */
case 3: return 1; /* usb1 */
case 4: return 2; /* nic */
case 5: return 3; /* apu */
case 6: return 4; /* aci */
case 9: return 6; /* ide */
case 30: /* agp bridge -> PIRQC? */
return XBOX_NUM_INT_IRQS + 2;
default:
/* don't actually know how this should work */
assert(false);
return XBOX_NUM_INT_IRQS + ((slot + intx) & 3);
}
}
static void xbox_lpc_set_acpi_irq(void *opaque, int irq_num, int level)
{
XBOX_LPCState *lpc = opaque;
assert(irq_num == 0 || irq_num == 1);
uint32_t routing = pci_get_long(lpc->dev.config + XBOX_LPC_ACPI_IRQ_ROUT);
int irq = (routing >> (irq_num*8)) & 0xff;
if (irq == 0 || irq >= XBOX_NUM_PIC_IRQS) {
return;
}
qemu_set_irq(lpc->pic[irq], level);
}
void xbox_pci_init(qemu_irq *pic,
MemoryRegion *address_space_mem,
MemoryRegion *address_space_io,
MemoryRegion *pci_memory,
MemoryRegion *ram_memory,
PCIBus **out_host_bus,
ISABus **out_isa_bus,
i2c_bus **out_smbus,
PCIBus **out_agp_bus)
{
DeviceState *host;
PCIHostState *host_state;
PCIBus *host_bus;
PCIDevice *bridge;
XBOX_PCIState *bridge_state;
/* pci host bus */
host = qdev_create(NULL, "xbox-pcihost");
host_state = PCI_HOST_BRIDGE(host);
host_bus = pci_bus_new(host, NULL,
pci_memory, address_space_io, 0, TYPE_PCI_BUS);
host_state->bus = host_bus;
qdev_init_nofail(host);
bridge = pci_create_simple_multifunction(host_bus, PCI_DEVFN(0, 0),
true, "xbox-pci");
bridge_state = XBOX_PCI_DEVICE(bridge);
bridge_state->ram_memory = ram_memory;
bridge_state->pci_address_space = pci_memory;
bridge_state->system_memory = address_space_mem;
/* PCI hole */
/* TODO: move to xbox-pci init */
memory_region_init_alias(&bridge_state->pci_hole, OBJECT(bridge),
"pci-hole",
bridge_state->pci_address_space,
ram_size,
0x100000000ULL - ram_size);
memory_region_add_subregion(bridge_state->system_memory, ram_size,
&bridge_state->pci_hole);
/* lpc bridge */
PCIDevice *lpc = pci_create_simple_multifunction(host_bus, PCI_DEVFN(1, 0),
true, "xbox-lpc");
XBOX_LPCState *lpc_state = XBOX_LPC_DEVICE(lpc);
lpc_state->pic = pic;
pci_bus_irqs(host_bus, xbox_lpc_set_irq, xbox_lpc_map_irq, lpc_state,
XBOX_NUM_INT_IRQS + XBOX_NUM_PIRQS);
qemu_irq *acpi_irq = qemu_allocate_irqs(xbox_lpc_set_acpi_irq,
lpc_state, 2);
xbox_pm_init(lpc, &lpc_state->pm, acpi_irq[0]);
//xbox_lpc_reset(&s->dev.qdev);
/* smbus */
PCIDevice *smbus = pci_create_simple_multifunction(host_bus, PCI_DEVFN(1, 1),
true, "xbox-smbus");
XBOX_SMBState *smbus_state = XBOX_SMBUS_DEVICE(smbus);
amd756_smbus_init(&smbus->qdev, &smbus_state->smb, acpi_irq[1]);
/* AGP bus */
PCIDevice *agp = pci_create_simple(host_bus, PCI_DEVFN(30, 0), "xbox-agp");
//qdev = &br->dev.qdev;
//qdev_init_nofail(qdev);
PCIBus *agp_bus = pci_bridge_get_sec_bus(PCI_BRIDGE(agp));
*out_host_bus = host_bus;
*out_isa_bus = lpc_state->isa_bus;
*out_smbus = smbus_state->smb.smbus;
*out_agp_bus = agp_bus;
}
#define XBOX_SMBUS_BASE_BAR 1
static void xbox_smb_ioport_writeb(void *opaque, hwaddr addr,
uint64_t val, unsigned size)
{
XBOX_SMBState *s = opaque;
uint64_t offset = addr - s->dev.io_regions[XBOX_SMBUS_BASE_BAR].addr;
amd756_smb_ioport_writeb(&s->smb, offset, val);
}
static uint64_t xbox_smb_ioport_readb(void *opaque, hwaddr addr,
unsigned size)
{
XBOX_SMBState *s = opaque;
uint64_t offset = addr - s->dev.io_regions[XBOX_SMBUS_BASE_BAR].addr;
return amd756_smb_ioport_readb(&s->smb, offset);
}
static const MemoryRegionOps xbox_smbus_ops = {
.read = xbox_smb_ioport_readb,
.write = xbox_smb_ioport_writeb,
.endianness = DEVICE_LITTLE_ENDIAN,
.impl = {
.min_access_size = 1,
.max_access_size = 1,
},
};
static int xbox_smbus_initfn(PCIDevice *dev)
{
XBOX_SMBState *s = XBOX_SMBUS_DEVICE(dev);
memory_region_init_io(&s->smb_bar, OBJECT(dev), &xbox_smbus_ops,
s, "xbox-smbus-bar", 32);
pci_register_bar(dev, XBOX_SMBUS_BASE_BAR, PCI_BASE_ADDRESS_SPACE_IO,
&s->smb_bar);
return 0;
}
static void xbox_smbus_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
k->init = xbox_smbus_initfn;
k->vendor_id = PCI_VENDOR_ID_NVIDIA;
k->device_id = PCI_DEVICE_ID_NVIDIA_NFORCE_SMBUS;
k->revision = 161;
k->class_id = PCI_CLASS_SERIAL_SMBUS;
dc->desc = "nForce PCI System Management";
dc->no_user = 1;
}
static const TypeInfo xbox_smbus_info = {
.name = "xbox-smbus",
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(XBOX_SMBState),
.class_init = xbox_smbus_class_init,
};
static int xbox_lpc_initfn(PCIDevice *d)
{
XBOX_LPCState *s = XBOX_LPC_DEVICE(d);
ISABus *isa_bus;
isa_bus = isa_bus_new(&d->qdev, get_system_io());
s->isa_bus = isa_bus;
/* southbridge chip contains and controls bootrom image.
* can't load it through loader.c because it overlaps with the bios...
* We really should just commandeer the entire top 16Mb.
*/
QemuOpts *machine_opts = qemu_opts_find(qemu_find_opts("machine"), NULL);
if (machine_opts) {
const char *bootrom_file = qemu_opt_get(machine_opts, "bootrom");
int rc, fd = -1;
if (bootrom_file) {
char *filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bootrom_file);
assert(filename);
s->bootrom_size = get_image_size(filename);
if (s->bootrom_size != 512) {
fprintf(stderr, "MCPX bootrom should be 512 bytes, got %d\n",
s->bootrom_size);
return -1;
}
fd = open(filename, O_RDONLY | O_BINARY);
assert(fd >= 0);
rc = read(fd, s->bootrom_data, s->bootrom_size);
assert(rc == s->bootrom_size);
close(fd);
}
}
return 0;
}
static void xbox_lpc_reset(DeviceState *dev)
{
PCIDevice *d = PCI_DEVICE(dev);
XBOX_LPCState *s = XBOX_LPC_DEVICE(d);
if (s->bootrom_size) {
/* qemu's memory region shit is actually kinda broken -
* Trying to execute off a non-page-aligned memory region
* is fucked, so we can't just map in the bootrom.
*
* We need to be able to disable it at runtime, and
* it shouldn't be visible ontop of the bios mirrors. It'll have to
* be a hack.
*
* Be lazy for now and just write it ontop of the bios.
*
* (We do this here since loader.c loads roms into memory in a reset
* handler, and here we /should/ be handled after it.)
*/
hwaddr bootrom_addr = (uint32_t)(-s->bootrom_size);
cpu_physical_memory_write_rom(bootrom_addr,
s->bootrom_data,
s->bootrom_size);
}
}
#if 0
/* Xbox 1.1 uses a config register instead of a bar to set the pm base address */
#define XBOX_LPC_PMBASE 0x84
#define XBOX_LPC_PMBASE_ADDRESS_MASK 0xff00
#define XBOX_LPC_PMBASE_DEFAULT 0x1
static void xbox_lpc_pmbase_update(XBOX_LPCState *s)
{
uint32_t pm_io_base = pci_get_long(s->dev.config + XBOX_LPC_PMBASE);
pm_io_base &= XBOX_LPC_PMBASE_ADDRESS_MASK;
xbox_pm_iospace_update(&s->pm, pm_io_base);
}
static void xbox_lpc_reset(DeviceState *dev)
{
PCIDevice *d = PCI_DEVICE(dev);
XBOX_LPCState *s = XBOX_LPC_DEVICE(d);
pci_set_long(s->dev.config + XBOX_LPC_PMBASE, XBOX_LPC_PMBASE_DEFAULT);
xbox_lpc_pmbase_update(s);
}
static void xbox_lpc_config_write(PCIDevice *dev,
uint32_t addr, uint32_t val, int len)
{
XBOX_LPCState *s = XBOX_LPC_DEVICE(dev);
pci_default_write_config(dev, addr, val, len);
if (ranges_overlap(addr, len, XBOX_LPC_PMBASE, 2)) {
xbox_lpc_pmbase_update(s);
}
}
static int xbox_lpc_post_load(void *opaque, int version_id)
{
XBOX_LPCState *s = opaque;
xbox_lpc_pmbase_update(s);
return 0;
}
static const VMStateDescription vmstate_xbox_lpc = {
.name = "XBOX LPC",
.version_id = 1,
.post_load = xbox_lpc_post_load,
};
#endif
static void xbox_lpc_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
k->no_hotplug = 1;
k->init = xbox_lpc_initfn;
//k->config_write = xbox_lpc_config_write;
k->vendor_id = PCI_VENDOR_ID_NVIDIA;
k->device_id = PCI_DEVICE_ID_NVIDIA_NFORCE_LPC;
k->revision = 212;
k->class_id = PCI_CLASS_BRIDGE_ISA;
dc->desc = "nForce LPC Bridge";
dc->no_user = 1;
dc->reset = xbox_lpc_reset;
//dc->vmsd = &vmstate_xbox_lpc;
}
static const TypeInfo xbox_lpc_info = {
.name = "xbox-lpc",
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(XBOX_LPCState),
.class_init = xbox_lpc_class_init,
};
static int xbox_agp_initfn(PCIDevice *d)
{
pci_set_word(d->config + PCI_PREF_MEMORY_BASE, PCI_PREF_RANGE_TYPE_32);
pci_set_word(d->config + PCI_PREF_MEMORY_LIMIT, PCI_PREF_RANGE_TYPE_32);
return pci_bridge_initfn(d, TYPE_PCI_BUS);
}
static void xbox_agp_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
k->init = xbox_agp_initfn;
k->exit = pci_bridge_exitfn;
k->config_write = pci_bridge_write_config;
k->is_bridge = 1;
k->vendor_id = PCI_VENDOR_ID_NVIDIA;
k->device_id = PCI_DEVICE_ID_NVIDIA_NFORCE_AGP;
k->revision = 161;
dc->desc = "nForce AGP to PCI Bridge";
dc->reset = pci_bridge_reset;
}
static const TypeInfo xbox_agp_info = {
.name = "xbox-agp",
.parent = TYPE_PCI_BRIDGE,
.instance_size = sizeof(PCIBridge),
.class_init = xbox_agp_class_init,
};
static int xbox_pci_initfn(PCIDevice *d)
{
//XBOX_PCIState *s = DO_UPCAST(XBOX_PCIState, dev, dev);
return 0;
}
static void xbox_pci_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
k->no_hotplug = 1;
k->init = xbox_pci_initfn;
//k->config_write = xbox_pci_write_config;
k->vendor_id = PCI_VENDOR_ID_NVIDIA;
k->device_id = PCI_DEVICE_ID_NVIDIA_XBOX_PCHB;
k->revision = 161;
k->class_id = PCI_CLASS_BRIDGE_HOST;
dc->desc = "Xbox PCI Host";
dc->no_user = 1;
}
static const TypeInfo xbox_pci_info = {
.name = "xbox-pci",
.parent = TYPE_PCI_DEVICE,
.instance_size = sizeof(XBOX_PCIState),
.class_init = xbox_pci_class_init,
};
#define CONFIG_ADDR 0xcf8
#define CONFIG_DATA 0xcfc
static int xbox_pcihost_initfn(SysBusDevice *dev)
{
PCIHostState *s = PCI_HOST_BRIDGE(dev);
memory_region_init_io(&s->conf_mem, OBJECT(dev),
&pci_host_conf_le_ops, s,
"pci-conf-idx", 4);
sysbus_add_io(dev, CONFIG_ADDR, &s->conf_mem);
sysbus_init_ioports(&s->busdev, CONFIG_ADDR, 4);
memory_region_init_io(&s->data_mem, OBJECT(dev),
&pci_host_data_le_ops, s,
"pci-conf-data", 4);
sysbus_add_io(dev, CONFIG_DATA, &s->data_mem);
sysbus_init_ioports(&s->busdev, CONFIG_DATA, 4);
return 0;
}
static void xbox_pcihost_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
k->init = xbox_pcihost_initfn;
dc->no_user = 1;
}
static const TypeInfo xbox_pcihost_info = {
.name = "xbox-pcihost",
.parent = TYPE_PCI_HOST_BRIDGE,
.instance_size = sizeof(PCIHostState),
.class_init = xbox_pcihost_class_init,
};
static void xboxpci_register_types(void)
{
type_register(&xbox_pcihost_info);
type_register(&xbox_pci_info);
type_register(&xbox_agp_info);
type_register(&xbox_lpc_info);
type_register(&xbox_smbus_info);
}
type_init(xboxpci_register_types)

83
hw/xbox/xbox_pci.h Normal file
View File

@ -0,0 +1,83 @@
/*
* QEMU Xbox PCI buses implementation
*
* 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 version 2 as published by the Free Software Foundation.
*
* 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/>
*
* Contributions after 2012-01-13 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*/
#ifndef HW_XBOX_PCI_H
#define HW_XBOX_PCI_H
#include "hw/hw.h"
#include "hw/isa/isa.h"
#include "hw/pci/pci.h"
#include "hw/pci/pci_host.h"
#include "hw/pci/pci_bus.h"
#include "hw/xbox/amd_smbus.h"
#include "hw/acpi/acpi.h"
#include "hw/xbox/acpi_xbox.h"
typedef struct XBOX_PCIState {
PCIDevice dev;
MemoryRegion *ram_memory;
MemoryRegion *pci_address_space;
MemoryRegion *system_memory;
MemoryRegion pci_hole;
} XBOX_PCIState;
typedef struct XBOX_SMBState {
PCIDevice dev;
AMD756SMBus smb;
MemoryRegion smb_bar;
} XBOX_SMBState;
typedef struct XBOX_LPCState {
PCIDevice dev;
ISABus *isa_bus;
XBOX_PMRegs pm;
qemu_irq *pic;
int bootrom_size;
uint8_t bootrom_data[512];
} XBOX_LPCState;
#define XBOX_PCI_DEVICE(obj) \
OBJECT_CHECK(XBOX_PCIState, (obj), "xbox-pci")
#define XBOX_SMBUS_DEVICE(obj) \
OBJECT_CHECK(XBOX_SMBState, (obj), "xbox-smbus")
#define XBOX_LPC_DEVICE(obj) \
OBJECT_CHECK(XBOX_LPCState, (obj), "xbox-lpc")
void xbox_pci_init(qemu_irq *pic,
MemoryRegion *address_space_mem,
MemoryRegion *address_space_io,
MemoryRegion *pci_memory,
MemoryRegion *ram_memory,
PCIBus **out_host_bus,
ISABus **out_isa_bus,
i2c_bus **out_smbus,
PCIBus **out_agp_bus);
#endif

445
hw/xbox/xid.c Normal file
View File

@ -0,0 +1,445 @@
/*
* QEMU USB XID Devices
*
* 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 "ui/console.h"
#include "hw/usb.h"
#include "hw/usb/desc.h"
//#define DEBUG_XID
#ifdef DEBUG_XID
#define DPRINTF printf
#else
#define DPRINTF(...)
#endif
/*
* http://xbox-linux.cvs.sourceforge.net/viewvc/xbox-linux/kernel-2.6/drivers/usb/input/xpad.c
* http://euc.jp/periphs/xbox-controller.en.html
* http://euc.jp/periphs/xbox-pad-desc.txt
*/
#define USB_CLASS_XID 0x58
#define USB_DT_XID 0x42
#define HID_GET_REPORT 0x01
#define HID_SET_REPORT 0x09
#define XID_GET_CAPABILITIES 0x01
typedef struct XIDDesc {
uint8_t bLength;
uint8_t bDescriptorType;
uint16_t bcdXid;
uint8_t bType;
uint8_t bSubType;
uint8_t bMaxInputReportSize;
uint8_t bMaxOutputReportSize;
uint16_t wAlternateProductIds[4];
} QEMU_PACKED XIDDesc;
typedef struct XIDGamepadReport {
uint8_t bReportId;
uint8_t bLength;
uint16_t wButtons;
uint8_t bAnalogButtons[8];
int16_t sThumbLX;
int16_t sThumbLY;
int16_t sThumbRX;
int16_t sThumbRY;
} QEMU_PACKED XIDGamepadReport;
typedef struct XIDGamepadOutputReport {
uint8_t report_id; //FIXME: is this correct?
uint8_t length;
uint16_t left_actuator_strength;
uint16_t right_actuator_strength;
} QEMU_PACKED XIDGamepadOutputReport;
typedef struct USBXIDState {
USBDevice dev;
USBEndpoint *intr;
const XIDDesc *xid_desc;
QEMUPutKbdEntry *kbd_entry;
bool in_dirty;
XIDGamepadReport in_state;
XIDGamepadOutputReport out_state;
} USBXIDState;
static const USBDescIface desc_iface_xbox_gamepad = {
.bInterfaceNumber = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_XID,
.bInterfaceSubClass = 0x42,
.bInterfaceProtocol = 0x00,
.eps = (USBDescEndpoint[]) {
{
.bEndpointAddress = USB_DIR_IN | 0x02,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = 0x20,
.bInterval = 4,
},
{
.bEndpointAddress = USB_DIR_OUT | 0x02,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = 0x20,
.bInterval = 4,
},
},
};
static const USBDescDevice desc_device_xbox_gamepad = {
.bcdUSB = 0x0110,
.bMaxPacketSize0 = 0x40,
.bNumConfigurations = 1,
.confs = (USBDescConfig[]) {
{
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.bmAttributes = 0x80,
.bMaxPower = 50,
.nif = 1,
.ifs = &desc_iface_xbox_gamepad,
},
},
};
static const USBDesc desc_xbox_gamepad = {
.id = {
.idVendor = 0x045e,
.idProduct = 0x0202,
.bcdDevice = 0x0100,
},
.full = &desc_device_xbox_gamepad,
};
static const XIDDesc desc_xid_xbox_gamepad = {
.bLength = 0x10,
.bDescriptorType = USB_DT_XID,
.bcdXid = 1,
.bType = 1,
.bSubType = 1,
.bMaxInputReportSize = 0x20,
.bMaxOutputReportSize = 0x6,
.wAlternateProductIds = {-1, -1, -1, -1},
};
#define GAMEPAD_A 0
#define GAMEPAD_B 1
#define GAMEPAD_X 2
#define GAMEPAD_Y 3
#define GAMEPAD_BLACK 4
#define GAMEPAD_WHITE 5
#define GAMEPAD_LEFT_TRIGGER 6
#define GAMEPAD_RIGHT_TRIGGER 7
#define GAMEPAD_DPAD_UP 8
#define GAMEPAD_DPAD_DOWN 9
#define GAMEPAD_DPAD_LEFT 10
#define GAMEPAD_DPAD_RIGHT 11
#define GAMEPAD_START 12
#define GAMEPAD_BACK 13
#define GAMEPAD_LEFT_THUMB 14
#define GAMEPAD_RIGHT_THUMB 15
#define GAMEPAD_LEFT_THUMB_UP 16
#define GAMEPAD_LEFT_THUMB_DOWN 17
#define GAMEPAD_LEFT_THUMB_LEFT 18
#define GAMEPAD_LEFT_THUMB_RIGHT 19
#define GAMEPAD_RIGHT_THUMB_UP 20
#define GAMEPAD_RIGHT_THUMB_DOWN 21
#define GAMEPAD_RIGHT_THUMB_LEFT 22
#define GAMEPAD_RIGHT_THUMB_RIGHT 23
static const int gamepad_mapping[] = {
[0 ... Q_KEY_CODE_MAX] = -1,
[Q_KEY_CODE_UP] = GAMEPAD_DPAD_UP,
[Q_KEY_CODE_KP_8] = GAMEPAD_DPAD_UP,
[Q_KEY_CODE_DOWN] = GAMEPAD_DPAD_DOWN,
[Q_KEY_CODE_KP_2] = GAMEPAD_DPAD_DOWN,
[Q_KEY_CODE_LEFT] = GAMEPAD_DPAD_LEFT,
[Q_KEY_CODE_KP_4] = GAMEPAD_DPAD_LEFT,
[Q_KEY_CODE_RIGHT] = GAMEPAD_DPAD_RIGHT,
[Q_KEY_CODE_KP_6] = GAMEPAD_DPAD_RIGHT,
[Q_KEY_CODE_RET] = GAMEPAD_START,
[Q_KEY_CODE_BACKSPACE] = GAMEPAD_BACK,
[Q_KEY_CODE_W] = GAMEPAD_X,
[Q_KEY_CODE_E] = GAMEPAD_Y,
[Q_KEY_CODE_S] = GAMEPAD_A,
[Q_KEY_CODE_D] = GAMEPAD_B,
[Q_KEY_CODE_X] = GAMEPAD_WHITE,
[Q_KEY_CODE_C] = GAMEPAD_BLACK,
[Q_KEY_CODE_Q] = GAMEPAD_LEFT_TRIGGER,
[Q_KEY_CODE_R] = GAMEPAD_RIGHT_TRIGGER,
[Q_KEY_CODE_V] = GAMEPAD_LEFT_THUMB,
[Q_KEY_CODE_T] = GAMEPAD_LEFT_THUMB_UP,
[Q_KEY_CODE_F] = GAMEPAD_LEFT_THUMB_LEFT,
[Q_KEY_CODE_G] = GAMEPAD_LEFT_THUMB_DOWN,
[Q_KEY_CODE_H] = GAMEPAD_LEFT_THUMB_RIGHT,
[Q_KEY_CODE_M] = GAMEPAD_RIGHT_THUMB,
[Q_KEY_CODE_I] = GAMEPAD_RIGHT_THUMB_UP,
[Q_KEY_CODE_J] = GAMEPAD_RIGHT_THUMB_LEFT,
[Q_KEY_CODE_K] = GAMEPAD_RIGHT_THUMB_DOWN,
[Q_KEY_CODE_L] = GAMEPAD_RIGHT_THUMB_RIGHT,
};
static void xbox_gamepad_keyboard_event(void *opaque, int keycode)
{
USBXIDState *s = opaque;
bool up = keycode & 0x80;
QKeyCode code = index_from_keycode(keycode & 0x7f);
if (code >= Q_KEY_CODE_MAX) return;
int button = gamepad_mapping[code];
DPRINTF("xid keyboard_event %x - %d %d %d\n", keycode, code, button, up);
uint16_t mask;
switch (button) {
case GAMEPAD_A ... GAMEPAD_RIGHT_TRIGGER:
s->in_state.bAnalogButtons[button] = up?0:0xff;
break;
case GAMEPAD_DPAD_UP ... GAMEPAD_RIGHT_THUMB:
mask = (1 << (button-GAMEPAD_DPAD_UP));
s->in_state.wButtons &= ~mask;
if (!up) s->in_state.wButtons |= mask;
break;
case GAMEPAD_LEFT_THUMB_UP:
s->in_state.sThumbLY = up?0:32767;
break;
case GAMEPAD_LEFT_THUMB_DOWN:
s->in_state.sThumbLY = up?0:-32768;
break;
case GAMEPAD_LEFT_THUMB_LEFT:
s->in_state.sThumbLX = up?0:-32768;
break;
case GAMEPAD_LEFT_THUMB_RIGHT:
s->in_state.sThumbLX = up?0:32767;
break;
case GAMEPAD_RIGHT_THUMB_UP:
s->in_state.sThumbRY = up?0:32767;
break;
case GAMEPAD_RIGHT_THUMB_DOWN:
s->in_state.sThumbRY = up?0:-32768;
break;
case GAMEPAD_RIGHT_THUMB_LEFT:
s->in_state.sThumbRX = up?0:-32768;
break;
case GAMEPAD_RIGHT_THUMB_RIGHT:
s->in_state.sThumbRX = up?0:32767;
break;
default:
break;
}
s->in_dirty = true;
}
static void usb_xid_handle_reset(USBDevice *dev)
{
DPRINTF("xid reset\n");
}
static void usb_xid_handle_control(USBDevice *dev, USBPacket *p,
int request, int value, int index, int length, uint8_t *data)
{
USBXIDState *s = DO_UPCAST(USBXIDState, dev, dev);
DPRINTF("xid handle_control 0x%x 0x%x\n", request, value);
int ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) {
DPRINTF("xid handled by usb_desc_handle_control: %d\n", ret);
return;
}
switch (request) {
/* HID requests */
case ClassInterfaceRequest | HID_GET_REPORT:
DPRINTF("xid GET_REPORT 0x%x\n", value);
if (value == 0x100) { /* input */
assert(s->in_state.bLength <= length);
// s->in_state.bReportId++; /* FIXME: I'm not sure if bReportId is just a counter */
memcpy(data, &s->in_state, s->in_state.bLength);
p->actual_length = s->in_state.bLength;
} else {
assert(false);
}
break;
case ClassInterfaceOutRequest | HID_SET_REPORT:
DPRINTF("xid SET_REPORT 0x%x\n", value);
if (value == 0x200) { /* output */
/* Read length, then the entire packet */
memcpy(&s->out_state, data, sizeof(s->out_state));
assert(s->out_state.length == sizeof(s->out_state));
assert(s->out_state.length <= length);
//FIXME: Check actuator endianess
DPRINTF("Set rumble power to 0x%x, 0x%x\n",
s->out_state.left_actuator_strength,
s->out_state.right_actuator_strength);
p->actual_length = s->out_state.length;
} else {
assert(false);
}
break;
/* XID requests */
case VendorInterfaceRequest | USB_REQ_GET_DESCRIPTOR:
DPRINTF("xid GET_DESCRIPTOR 0x%x\n", value);
if (value == 0x4200) {
assert(s->xid_desc->bLength <= length);
memcpy(data, s->xid_desc, s->xid_desc->bLength);
p->actual_length = s->xid_desc->bLength;
} else {
assert(false);
}
break;
case VendorInterfaceRequest | XID_GET_CAPABILITIES:
DPRINTF("xid XID_GET_CAPABILITIES 0x%x\n", value);
/* FIXME: ! */
p->status = USB_RET_STALL;
//assert(false);
break;
case ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_DEVICE)<<8)
| USB_REQ_GET_DESCRIPTOR:
/* FIXME: ! */
DPRINTF("xid unknown xpad request 0x%x: value = 0x%x\n",
request, value);
memset(data, 0x00, length);
//FIXME: Intended for the hub: usbd_get_hub_descriptor, UT_READ_CLASS?!
p->status = USB_RET_STALL;
//assert(false);
break;
case ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8)
| USB_REQ_CLEAR_FEATURE:
/* FIXME: ! */
DPRINTF("xid unknown xpad request 0x%x: value = 0x%x\n",
request, value);
memset(data, 0x00, length);
p->status = USB_RET_STALL;
break;
default:
DPRINTF("xid USB stalled on request 0x%x value 0x%x\n", request, value);
p->status = USB_RET_STALL;
assert(false);
break;
}
}
static void usb_xid_handle_data(USBDevice *dev, USBPacket *p)
{
USBXIDState *s = DO_UPCAST(USBXIDState, dev, dev);
DPRINTF("xid handle_data 0x%x %d 0x%zx\n", p->pid, p->ep->nr, p->iov.size);
switch (p->pid) {
case USB_TOKEN_IN:
if (p->ep->nr == 2) {
if (s->in_dirty) {
usb_packet_copy(p, &s->in_state, s->in_state.bLength);
s->in_dirty = false;
} else {
p->status = USB_RET_NAK;
}
} else {
assert(false);
}
break;
case USB_TOKEN_OUT:
p->status = USB_RET_STALL;
break;
default:
p->status = USB_RET_STALL;
assert(false);
break;
}
}
static void usb_xid_handle_destroy(USBDevice *dev)
{
DPRINTF("xid handle_destroy\n");
}
static void usb_xid_class_initfn(ObjectClass *klass, void *data)
{
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
uc->handle_reset = usb_xid_handle_reset;
uc->handle_control = usb_xid_handle_control;
uc->handle_data = usb_xid_handle_data;
uc->handle_destroy = usb_xid_handle_destroy;
uc->handle_attach = usb_desc_attach;
}
static int usb_xbox_gamepad_initfn(USBDevice *dev)
{
USBXIDState *s = DO_UPCAST(USBXIDState, dev, dev);
usb_desc_init(dev);
s->intr = usb_ep_get(dev, USB_TOKEN_IN, 2);
s->in_state.bLength = sizeof(s->in_state);
s->out_state.length = sizeof(s->out_state);
s->kbd_entry = qemu_add_kbd_event_handler(xbox_gamepad_keyboard_event, s);
s->xid_desc = &desc_xid_xbox_gamepad;
return 0;
}
static void usb_xbox_gamepad_class_initfn(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
usb_xid_class_initfn(klass, data);
uc->init = usb_xbox_gamepad_initfn;
uc->product_desc = "Microsoft Xbox Controller";
uc->usb_desc = &desc_xbox_gamepad;
//dc->vmsd = &vmstate_usb_kbd;
set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
}
static const TypeInfo usb_xbox_gamepad_info = {
.name = "usb-xbox-gamepad",
.parent = TYPE_USB_DEVICE,
.instance_size = sizeof(USBXIDState),
.class_init = usb_xbox_gamepad_class_initfn,
};
static void usb_xid_register_types(void)
{
type_register_static(&usb_xbox_gamepad_info);
}
type_init(usb_xid_register_types)