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