Basic hardware devices implementation

The emulator does something now, but seems to hang during initialization. Needs further investigation
This commit is contained in:
StrikerX3 2018-03-07 23:25:53 -03:00
parent 7b1a533b20
commit 79e3790f90
29 changed files with 2117 additions and 102 deletions

View file

@ -87,6 +87,7 @@ int main(int argc, const char *argv[]) {
settings.debug_dumpMemoryMapping = true;
settings.debug_dumpXBESectionContents = false;
settings.gdb_enable = false;
settings.hw_model = DebugKit;
settings.hw_sysclock_tickRate = 100.0f;
settings.rom_mcpx = mcpx_path;
settings.rom_bios = bios_path;

View file

@ -0,0 +1,32 @@
#pragma once
#include <cstdint>
namespace openxbox {
// Constants for SMBus device addresses.
// The constants point to the write address.
// Reads are done on write address + 1.
const uint8_t kSMBusAddress_MCPX = 0x10;
const uint8_t kSMBusAddress_TVEncoder = 0x88;
const uint8_t kSMBusAddress_SystemMicroController = 0x20;
const uint8_t kSMBusAddress_TemperatureMeasurement = 0x98;
const uint8_t kSMBusAddress_EEPROM = 0xA8;
const uint8_t kSMBusAddress_TVEncoder_ID_Conexant = 0x8A;
const uint8_t kSMBusAddress_TVEncoder_ID_Focus = 0xD4;
const uint8_t kSMBusAddress_TVEncoder_ID_XCalibur = 0xE0;
// Xbox hardware models.
// These affect the configuration of various hardware components.
typedef enum {
Revision1_0,
Revision1_1,
Revision1_2,
Revision1_3,
Revision1_4,
Revision1_5,
Revision1_6,
DebugKit
} HardwareModel;
}

View file

@ -0,0 +1,51 @@
#include "eeprom.h"
#include <memory>
namespace openxbox {
void EEPROMDevice::Init() {
// TODO
}
void EEPROMDevice::Reset() {
// TODO
}
void EEPROMDevice::QuickCommand(bool read) {
// TODO
}
uint8_t EEPROMDevice::ReceiveByte() {
return 0; // TODO
}
uint8_t EEPROMDevice::ReadByte(uint8_t command) {
return *(m_pEEPROM + command);
}
uint16_t EEPROMDevice::ReadWord(uint8_t command) {
return *((uint16_t*)(m_pEEPROM + command));
}
int EEPROMDevice::ReadBlock(uint8_t command, uint8_t *data) {
return 0; // TODO
}
void EEPROMDevice::SendByte(uint8_t data) {
// TODO
}
void EEPROMDevice::WriteByte(uint8_t command, uint8_t value) {
*((uint8_t*)(m_pEEPROM + command)) = value;
}
void EEPROMDevice::WriteWord(uint8_t command, uint16_t value) {
*((uint16_t*)(m_pEEPROM + command)) = value;
}
void EEPROMDevice::WriteBlock(uint8_t command, uint8_t* data, int length) {
memcpy(m_pEEPROM + command, data, length);
}
}

View file

@ -0,0 +1,30 @@
#pragma once
#include "sm.h"
namespace openxbox {
class EEPROMDevice : public SMDevice {
public:
// SMDevice functions
void Init();
void Reset();
void QuickCommand(bool read);
uint8_t ReceiveByte();
uint8_t ReadByte(uint8_t command);
uint16_t ReadWord(uint8_t command);
int ReadBlock(uint8_t command, uint8_t *data);
void SendByte(uint8_t data);
void WriteByte(uint8_t command, uint8_t value);
void WriteWord(uint8_t command, uint16_t value);
void WriteBlock(uint8_t command, uint8_t* data, int length);
// EEPROMDevice function
void SetEEPROM(uint8_t* pEEPROM) { m_pEEPROM = pEEPROM; };
private:
uint8_t * m_pEEPROM;
};
}

View file

@ -0,0 +1,59 @@
#pragma once
namespace openxbox {
namespace LED {
// See http://xboxdevwiki.net/PIC#The_LED
namespace Phase0 { enum { Off = 0, Red = 1 << 7, Green = 1 << 3, Orange = Red | Green }; };
namespace Phase1 { enum { Off = 0, Red = 1 << 6, Green = 1 << 2, Orange = Red | Green }; };
namespace Phase2 { enum { Off = 0, Red = 1 << 5, Green = 1 << 1, Orange = Red | Green }; };
namespace Phase3 { enum { Off = 0, Red = 1 << 4, Green = 1 << 0, Orange = Red | Green }; };
typedef uint8_t Sequence;
// Pre-defined LED Phase sequences
constexpr Sequence OFF = Phase0::Off | Phase1::Off | Phase2::Off | Phase3::Off;
// Solid single color: constant on
constexpr Sequence RED = Phase0::Red | Phase1::Red | Phase2::Red | Phase3::Red;
constexpr Sequence GREEN = Phase0::Green | Phase1::Green | Phase2::Green | Phase3::Green;
constexpr Sequence ORANGE = Phase0::Orange | Phase1::Orange | Phase2::Orange | Phase3::Orange;
// Signal single color: thrice on, once off
constexpr Sequence SIGNAL_RED = Phase0::Red | Phase1::Red | Phase2::Red | Phase3::Off;
constexpr Sequence SIGNAL_GREEN = Phase0::Green | Phase1::Green | Phase2::Green | Phase3::Off;
constexpr Sequence SIGNAL_ORANGE = Phase0::Orange | Phase1::Orange | Phase2::Orange | Phase3::Off;
// Blink single color: twice on, twice off
constexpr Sequence BLINK_RED = Phase0::Red | Phase1::Red | Phase2::Off | Phase3::Off;
constexpr Sequence BLINK_GREEN = Phase0::Green | Phase1::Green | Phase2::Off | Phase3::Off;
constexpr Sequence BLINK_ORANGE = Phase0::Orange | Phase1::Orange | Phase2::Off | Phase3::Off;
// Blip single color: once on, thrice off
constexpr Sequence BLIP_RED = Phase0::Red | Phase1::Off | Phase2::Off | Phase3::Off;
constexpr Sequence BLIP_GREEN = Phase0::Green | Phase1::Off | Phase2::Off | Phase3::Off;
constexpr Sequence BLIP_ORANGE = Phase0::Orange | Phase1::Off | Phase2::Off | Phase3::Off;
// Blip twice, single color: on, off, on, off
constexpr Sequence BLIP_RED_RED = Phase0::Red | Phase1::Off | Phase2::Red | Phase3::Off;
constexpr Sequence BLIP_GREEN_GREEN = Phase0::Green | Phase1::Off | Phase2::Green | Phase3::Off;
constexpr Sequence BLIP_ORANGE_ORANGE = Phase0::Orange | Phase1::Off | Phase2::Orange | Phase3::Off;
// Blip, two colors: first, off, second, off
constexpr Sequence BLIP_RED_GREEN = Phase0::Red | Phase1::Off | Phase2::Green | Phase3::Off;
constexpr Sequence BLIP_RED_ORANGE = Phase0::Red | Phase1::Off | Phase2::Orange | Phase3::Off;
constexpr Sequence BLIP_GREEN_ORANGE = Phase0::Green | Phase1::Off | Phase2::Orange | Phase3::Off;
// Solid, two colors: alternating
constexpr Sequence RED_GREEN = Phase0::Red | Phase1::Red | Phase2::Green | Phase3::Green;
constexpr Sequence RED_ORANGE = Phase0::Red | Phase1::Red | Phase2::Orange | Phase3::Orange;
constexpr Sequence GREEN_ORANGE = Phase0::Green | Phase1::Green | Phase2::Orange | Phase3::Orange;
// Solid, two colors: alternating quickly
constexpr Sequence FAST_RED_GREEN = Phase0::Red | Phase1::Green | Phase2::Red | Phase3::Green;
constexpr Sequence FAST_RED_ORANGE = Phase0::Red | Phase1::Orange | Phase2::Red | Phase3::Orange;
constexpr Sequence FAST_GREEN_ORANGE = Phase0::Green | Phase1::Orange | Phase2::Green | Phase3::Orange;
}
}

View file

@ -0,0 +1,55 @@
#include "defs.h"
#include "mcpx.h"
namespace openxbox {
MCPXRevision MCPXRevisionFromHardwareModel(HardwareModel hardwareModel) {
switch (hardwareModel) {
case Revision1_0:
case Revision1_1:
case Revision1_2:
case Revision1_3:
case Revision1_4:
case Revision1_5:
case Revision1_6:
return MCPXRevision::MCPX_X3;
case DebugKit:
// EmuWarning("Guessing MCPXVersion");
return MCPXRevision::MCPX_X2;
default:
// UNREACHABLE(hardwareModel);
return MCPXRevision::MCPX_X3;
}
}
MCPXDevice::MCPXDevice(MCPXRevision revision) {
m_revision = revision;
}
// PCI Device functions
void MCPXDevice::Init() {
// m_deviceID = ?;
// m_vendorID = PCI_VENDOR_ID_NVIDIA;
}
void MCPXDevice::Reset() {
}
uint32_t MCPXDevice::IORead(int barIndex, uint32_t port, unsigned size) {
return 0;
}
void MCPXDevice::IOWrite(int barIndex, uint32_t port, uint32_t value, unsigned size) {
}
uint32_t MCPXDevice::MMIORead(int barIndex, uint32_t addr, unsigned size) {
return 0;
}
void MCPXDevice::MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size) {
// TODO : Log unexpected bar access
}
}

View file

@ -0,0 +1,43 @@
#pragma once
#include <cstdint>
#include "defs.h"
#include "pci.h"
namespace openxbox {
// MCPX ROM versions.
typedef enum {
MCPX_1_0,
MCPX_1_1,
} MCPXROMVersion;
// MCPX revisions.
typedef enum {
MCPX_X2,
MCPX_X3,
} MCPXRevision;
MCPXRevision MCPXRevisionFromHardwareModel(HardwareModel hardwareModel);
class MCPXDevice : public PCIDevice {
public:
// constructor
MCPXDevice(MCPXRevision revision);
// PCI Device functions
void Init();
void Reset();
uint32_t IORead(int barIndex, uint32_t port, unsigned size);
void IOWrite(int barIndex, uint32_t port, uint32_t value, unsigned size);
uint32_t MMIORead(int barIndex, uint32_t addr, unsigned size);
void MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size);
private:
MCPXRevision m_revision;
};
}

View file

@ -0,0 +1,510 @@
#include "nvnet.h"
#include "openxbox/log.h"
namespace openxbox {
// NVNET Register Definitions
// Taken from XQEMU
enum {
NvRegIrqStatus = 0x000,
# define NVREG_IRQSTAT_BIT1 0x002
# define NVREG_IRQSTAT_BIT4 0x010
# define NVREG_IRQSTAT_MIIEVENT 0x040
# define NVREG_IRQSTAT_MASK 0x1ff
NvRegIrqMask = 0x004,
# define NVREG_IRQ_RX 0x0002
# define NVREG_IRQ_RX_NOBUF 0x0004
# define NVREG_IRQ_TX_ERR 0x0008
# define NVREG_IRQ_TX2 0x0010
# define NVREG_IRQ_TIMER 0x0020
# define NVREG_IRQ_LINK 0x0040
# define NVREG_IRQ_TX1 0x0100
# define NVREG_IRQMASK_WANTED_1 0x005f
# define NVREG_IRQMASK_WANTED_2 0x0147
# define NVREG_IRQ_UNKNOWN (~(NVREG_IRQ_RX|NVREG_IRQ_RX_NOBUF|\
NVREG_IRQ_TX_ERR|NVREG_IRQ_TX2|NVREG_IRQ_TIMER|NVREG_IRQ_LINK|\
NVREG_IRQ_TX1))
NvRegUnknownSetupReg6 = 0x008,
# define NVREG_UNKSETUP6_VAL 3
/*
* NVREG_POLL_DEFAULT is the interval length of the timer source on the nic
* NVREG_POLL_DEFAULT=97 would result in an interval length of 1 ms
*/
NvRegPollingInterval = 0x00c,
# define NVREG_POLL_DEFAULT 970
NvRegMisc1 = 0x080,
# define NVREG_MISC1_HD 0x02
# define NVREG_MISC1_FORCE 0x3b0f3c
NvRegTransmitterControl = 0x084,
# define NVREG_XMITCTL_START 0x01
NvRegTransmitterStatus = 0x088,
# define NVREG_XMITSTAT_BUSY 0x01
NvRegPacketFilterFlags = 0x8c,
# define NVREG_PFF_ALWAYS 0x7F0008
# define NVREG_PFF_PROMISC 0x80
# define NVREG_PFF_MYADDR 0x20
NvRegOffloadConfig = 0x90,
# define NVREG_OFFLOAD_HOMEPHY 0x601
# define NVREG_OFFLOAD_NORMAL 0x5ee
NvRegReceiverControl = 0x094,
# define NVREG_RCVCTL_START 0x01
NvRegReceiverStatus = 0x98,
# define NVREG_RCVSTAT_BUSY 0x01
NvRegRandomSeed = 0x9c,
# define NVREG_RNDSEED_MASK 0x00ff
# define NVREG_RNDSEED_FORCE 0x7f00
NvRegUnknownSetupReg1 = 0xA0,
# define NVREG_UNKSETUP1_VAL 0x16070f
NvRegUnknownSetupReg2 = 0xA4,
# define NVREG_UNKSETUP2_VAL 0x16
NvRegMacAddrA = 0xA8,
NvRegMacAddrB = 0xAC,
NvRegMulticastAddrA = 0xB0,
# define NVREG_MCASTADDRA_FORCE 0x01
NvRegMulticastAddrB = 0xB4,
NvRegMulticastMaskA = 0xB8,
NvRegMulticastMaskB = 0xBC,
NvRegTxRingPhysAddr = 0x100,
NvRegRxRingPhysAddr = 0x104,
NvRegRingSizes = 0x108,
# define NVREG_RINGSZ_TXSHIFT 0
# define NVREG_RINGSZ_RXSHIFT 16
NvRegUnknownTransmitterReg = 0x10c,
NvRegLinkSpeed = 0x110,
# define NVREG_LINKSPEED_FORCE 0x10000
# define NVREG_LINKSPEED_10 10
# define NVREG_LINKSPEED_100 100
# define NVREG_LINKSPEED_1000 1000
NvRegUnknownSetupReg5 = 0x130,
# define NVREG_UNKSETUP5_BIT31 (1<<31)
NvRegUnknownSetupReg3 = 0x134,
# define NVREG_UNKSETUP3_VAL1 0x200010
NvRegUnknownSetupReg8 = 0x13C,
# define NVREG_UNKSETUP8_VAL1 0x300010
NvRegUnknownSetupReg7 = 0x140,
# define NVREG_UNKSETUP7_VAL 0x300010
NvRegTxRxControl = 0x144,
# define NVREG_TXRXCTL_KICK 0x0001
# define NVREG_TXRXCTL_BIT1 0x0002
# define NVREG_TXRXCTL_BIT2 0x0004
# define NVREG_TXRXCTL_IDLE 0x0008
# define NVREG_TXRXCTL_RESET 0x0010
NvRegMIIStatus = 0x180,
# define NVREG_MIISTAT_ERROR 0x0001
# define NVREG_MIISTAT_LINKCHANGE 0x0008
# define NVREG_MIISTAT_MASK 0x000f
# define NVREG_MIISTAT_MASK2 0x000f
NvRegUnknownSetupReg4 = 0x184,
# define NVREG_UNKSETUP4_VAL 8
NvRegAdapterControl = 0x188,
# define NVREG_ADAPTCTL_START 0x02
# define NVREG_ADAPTCTL_LINKUP 0x04
# define NVREG_ADAPTCTL_PHYVALID 0x4000
# define NVREG_ADAPTCTL_RUNNING 0x100000
# define NVREG_ADAPTCTL_PHYSHIFT 24
NvRegMIISpeed = 0x18c,
# define NVREG_MIISPEED_BIT8 (1<<8)
# define NVREG_MIIDELAY 5
NvRegMIIControl = 0x190,
# define NVREG_MIICTL_INUSE 0x10000
# define NVREG_MIICTL_WRITE 0x08000
# define NVREG_MIICTL_ADDRSHIFT 5
NvRegMIIData = 0x194,
NvRegWakeUpFlags = 0x200,
# define NVREG_WAKEUPFLAGS_VAL 0x7770
# define NVREG_WAKEUPFLAGS_BUSYSHIFT 24
# define NVREG_WAKEUPFLAGS_ENABLESHIFT 16
# define NVREG_WAKEUPFLAGS_D3SHIFT 12
# define NVREG_WAKEUPFLAGS_D2SHIFT 8
# define NVREG_WAKEUPFLAGS_D1SHIFT 4
# define NVREG_WAKEUPFLAGS_D0SHIFT 0
# define NVREG_WAKEUPFLAGS_ACCEPT_MAGPAT 0x01
# define NVREG_WAKEUPFLAGS_ACCEPT_WAKEUPPAT 0x02
# define NVREG_WAKEUPFLAGS_ACCEPT_LINKCHANGE 0x04
NvRegPatternCRC = 0x204,
NvRegPatternMask = 0x208,
NvRegPowerCap = 0x268,
# define NVREG_POWERCAP_D3SUPP (1<<30)
# define NVREG_POWERCAP_D2SUPP (1<<26)
# define NVREG_POWERCAP_D1SUPP (1<<25)
NvRegPowerState = 0x26c,
# define NVREG_POWERSTATE_POWEREDUP 0x8000
# define NVREG_POWERSTATE_VALID 0x0100
# define NVREG_POWERSTATE_MASK 0x0003
# define NVREG_POWERSTATE_D0 0x0000
# define NVREG_POWERSTATE_D1 0x0001
# define NVREG_POWERSTATE_D2 0x0002
# define NVREG_POWERSTATE_D3 0x0003
};
#define IOPORT_SIZE 0x8
#define MMIO_SIZE 0x400
#define NV_TX_LASTPACKET (1<<0)
#define NV_TX_RETRYERROR (1<<3)
#define NV_TX_LASTPACKET1 (1<<8)
#define NV_TX_DEFERRED (1<<10)
#define NV_TX_CARRIERLOST (1<<11)
#define NV_TX_LATECOLLISION (1<<12)
#define NV_TX_UNDERFLOW (1<<13)
#define NV_TX_ERROR (1<<14)
#define NV_TX_VALID (1<<15)
#define NV_RX_DESCRIPTORVALID (1<<0)
#define NV_RX_MISSEDFRAME (1<<1)
#define NV_RX_SUBSTRACT1 (1<<3)
#define NV_RX_BIT4 (1<<4)
#define NV_RX_ERROR1 (1<<7)
#define NV_RX_ERROR2 (1<<8)
#define NV_RX_ERROR3 (1<<9)
#define NV_RX_ERROR4 (1<<10)
#define NV_RX_CRCERR (1<<11)
#define NV_RX_OVERFLOW (1<<12)
#define NV_RX_FRAMINGERR (1<<13)
#define NV_RX_ERROR (1<<14)
#define NV_RX_AVAIL (1<<15)
/* Miscelaneous hardware related defines: */
#define NV_PCI_REGSZ 0x270
/* various timeout delays: all in usec */
#define NV_TXRX_RESET_DELAY 4
#define NV_TXSTOP_DELAY1 10
#define NV_TXSTOP_DELAY1MAX 500000
#define NV_TXSTOP_DELAY2 100
#define NV_RXSTOP_DELAY1 10
#define NV_RXSTOP_DELAY1MAX 500000
#define NV_RXSTOP_DELAY2 100
#define NV_SETUP5_DELAY 5
#define NV_SETUP5_DELAYMAX 50000
#define NV_POWERUP_DELAY 5
#define NV_POWERUP_DELAYMAX 5000
#define NV_MIIBUSY_DELAY 50
#define NV_MIIPHY_DELAY 10
#define NV_MIIPHY_DELAYMAX 10000
#define NV_WAKEUPPATTERNS 5
#define NV_WAKEUPMASKENTRIES 4
/* General driver defaults */
#define NV_WATCHDOG_TIMEO (2*HZ)
#define DEFAULT_MTU 1500
#define RX_RING 4
#define TX_RING 2
/* limited to 1 packet until we understand NV_TX_LASTPACKET */
#define TX_LIMIT_STOP 10
#define TX_LIMIT_START 5
/* rx/tx mac addr + type + vlan + align + slack*/
#define RX_NIC_BUFSIZE (DEFAULT_MTU + 64)
/* even more slack */
#define RX_ALLOC_BUFSIZE (DEFAULT_MTU + 128)
#define OOM_REFILL (1+HZ/20)
#define POLL_WAIT (1+HZ/100)
#define MII_READ (-1)
#define MII_PHYSID1 0x02 /* PHYS ID 1 */
#define MII_PHYSID2 0x03 /* PHYS ID 2 */
#define MII_BMCR 0x00 /* Basic mode control register */
#define MII_BMSR 0x01 /* Basic mode status register */
#define MII_ADVERTISE 0x04 /* Advertisement control reg */
#define MII_LPA 0x05 /* Link partner ability reg */
#define BMSR_ANEGCOMPLETE 0x0020 /* Auto-negotiation complete */
#define BMSR_BIT2 0x0004 /* Unknown... */
/* Link partner ability register. */
#define LPA_SLCT 0x001f /* Same as advertise selector */
#define LPA_10HALF 0x0020 /* Can do 10mbps half-duplex */
#define LPA_10FULL 0x0040 /* Can do 10mbps full-duplex */
#define LPA_100HALF 0x0080 /* Can do 100mbps half-duplex */
#define LPA_100FULL 0x0100 /* Can do 100mbps full-duplex */
#define LPA_100BASE4 0x0200 /* Can do 100mbps 4k packets */
#define LPA_RESV 0x1c00 /* Unused... */
#define LPA_RFAULT 0x2000 /* Link partner faulted */
#define LPA_LPACK 0x4000 /* Link partner acked us */
#define LPA_NPAGE 0x8000 /* Next page bit */
/*******************************************************************************
* Primary State Structure
******************************************************************************/
struct NvNetState {
uint8_t regs[MMIO_SIZE / 4];
uint32_t phy_regs[6];
uint8_t tx_ring_index;
uint8_t tx_ring_size;
uint8_t rx_ring_index;
uint8_t rx_ring_size;
uint8_t txrx_dma_buf[RX_ALLOC_BUFSIZE];
FILE *packet_dump_file;
char *packet_dump_path;
} NvNetState;
struct RingDesc {
uint32_t packet_buffer;
uint16_t length;
uint16_t flags;
};
char* EmuNVNet_GetRegisterName(uint32_t addr) {
switch (addr) {
case NvRegIrqStatus: return "NvRegIrqStatus";
case NvRegIrqMask: return "NvRegIrqMask";
case NvRegUnknownSetupReg6: return "NvRegUnknownSetupReg6";
case NvRegPollingInterval: return "NvRegPollingInterval";
case NvRegMisc1: return "NvRegMisc1";
case NvRegTransmitterControl: return "NvRegTransmitterControl";
case NvRegTransmitterStatus: return "NvRegTransmitterStatus";
case NvRegPacketFilterFlags: return "NvRegPacketFilterFlags";
case NvRegOffloadConfig: return "NvRegOffloadConfig";
case NvRegReceiverControl: return "NvRegReceiverControl";
case NvRegReceiverStatus: return "NvRegReceiverStatus";
case NvRegRandomSeed: return "NvRegRandomSeed";
case NvRegUnknownSetupReg1: return "NvRegUnknownSetupReg1";
case NvRegUnknownSetupReg2: return "NvRegUnknownSetupReg2";
case NvRegMacAddrA: return "NvRegMacAddrA";
case NvRegMacAddrB: return "NvRegMacAddrB";
case NvRegMulticastAddrA: return "NvRegMulticastAddrA";
case NvRegMulticastAddrB: return "NvRegMulticastAddrB";
case NvRegMulticastMaskA: return "NvRegMulticastMaskA";
case NvRegMulticastMaskB: return "NvRegMulticastMaskB";
case NvRegTxRingPhysAddr: return "NvRegTxRingPhysAddr";
case NvRegRxRingPhysAddr: return "NvRegRxRingPhysAddr";
case NvRegRingSizes: return "NvRegRingSizes";
case NvRegUnknownTransmitterReg: return "NvRegUnknownTransmitterReg";
case NvRegLinkSpeed: return "NvRegLinkSpeed";
case NvRegUnknownSetupReg5: return "NvRegUnknownSetupReg5";
case NvRegUnknownSetupReg3: return "NvRegUnknownSetupReg3";
case NvRegUnknownSetupReg8: return "NvRegUnknownSetupReg8";
case NvRegUnknownSetupReg7: return "NvRegUnknownSetupReg7";
case NvRegTxRxControl: return "NvRegTxRxControl";
case NvRegMIIStatus: return "NvRegMIIStatus";
case NvRegUnknownSetupReg4: return "NvRegUnknownSetupReg4";
case NvRegAdapterControl: return "NvRegAdapterControl";
case NvRegMIISpeed: return "NvRegMIISpeed";
case NvRegMIIControl: return "NvRegMIIControl";
case NvRegMIIData: return "NvRegMIIData";
case NvRegWakeUpFlags: return "NvRegWakeUpFlags";
case NvRegPatternCRC: return "NvRegPatternCRC";
case NvRegPatternMask: return "NvRegPatternMask";
case NvRegPowerCap: return "NvRegPowerCap";
case NvRegPowerState: return "NvRegPowerState";
default: return "Unknown";
}
}
char* EmuNVNet_GetMiiRegisterName(uint8_t reg) {
switch (reg) {
case MII_PHYSID1: return "MII_PHYSID1";
case MII_PHYSID2: return "MII_PHYSID2";
case MII_BMCR: return "MII_BMCR";
case MII_BMSR: return "MII_BMSR";
case MII_ADVERTISE: return "MII_ADVERTISE";
case MII_LPA: return "MII_LPA";
default: return "Unknown";
}
}
uint32_t EmuNVNet_GetRegister(uint32_t addr, unsigned int size) {
switch (size) {
case sizeof(uint32_t) :
return ((uint32_t *)NvNetState.regs)[addr >> 2];
case sizeof(uint16_t) :
return ((uint16_t *)NvNetState.regs)[addr >> 1];
case sizeof(uint8_t) :
return NvNetState.regs[addr];
}
return 0;
}
void EmuNVNet_SetRegister(uint32_t addr, uint32_t value, unsigned int size) {
switch (size) {
case sizeof(uint32_t) :
((uint32_t *)NvNetState.regs)[addr >> 2] = value;
break;
case sizeof(uint16_t) :
((uint16_t *)NvNetState.regs)[addr >> 1] = (uint16_t)value;
break;
case sizeof(uint8_t) :
NvNetState.regs[addr] = (uint8_t)value;
break;
}
}
void EmuNVNet_UpdateIRQ() {
if (EmuNVNet_GetRegister(NvRegIrqMask, 4) &&
EmuNVNet_GetRegister(NvRegIrqStatus, 4)) {
log_debug("EmuNVNet: Asserting IRQ\n");
// TODO: HalSystemInterrupts[4].Assert(true);
}
else {
// TODO: HalSystemInterrupts[4].Assert(false);
}
}
int EmuNVNet_MiiReadWrite(uint64_t val) {
uint32_t mii_ctl;
int write, retval, phy_addr, reg;
retval = 0;
mii_ctl = EmuNVNet_GetRegister(NvRegMIIControl, 4);
phy_addr = (mii_ctl >> NVREG_MIICTL_ADDRSHIFT) & 0x1f;
reg = mii_ctl & ((1 << NVREG_MIICTL_ADDRSHIFT) - 1);
write = mii_ctl & NVREG_MIICTL_WRITE;
log_debug("nvnet mii %s: phy 0x%x %s [0x%x]\n", write ? "write" : "read", phy_addr, EmuNVNet_GetMiiRegisterName(reg), reg);
if (phy_addr != 1) {
return -1;
}
if (write) {
return retval;
}
switch (reg) {
case MII_BMSR:
/* Phy initialization code waits for BIT2 to be set.. If not set,
* software may report controller as not running */
retval = BMSR_ANEGCOMPLETE | BMSR_BIT2;
break;
case MII_ADVERTISE:
/* Fall through... */
case MII_LPA:
retval = LPA_10HALF | LPA_10FULL;
retval |= LPA_100HALF | LPA_100FULL | LPA_100BASE4;
break;
default:
break;
}
return retval;
}
uint32_t EmuNVNet_Read(uint32_t addr, int size) {
log_debug("NET : Read%d: %s (0x%.8X)\n", size, EmuNVNet_GetRegisterName(addr), addr);
switch (addr) {
case NvRegMIIData:
return EmuNVNet_MiiReadWrite(MII_READ);
case NvRegMIIControl:
return EmuNVNet_GetRegister(addr, size) & ~NVREG_MIICTL_INUSE;
case NvRegMIIStatus:
return 0;
}
return EmuNVNet_GetRegister(addr, size);
}
void EmuNVNet_Write(uint32_t addr, uint32_t value, int size) {
switch (addr) {
case NvRegRingSizes:
EmuNVNet_SetRegister(addr, value, size);
NvNetState.rx_ring_size = ((value >> NVREG_RINGSZ_RXSHIFT) & 0xffff) + 1;
NvNetState.tx_ring_size = ((value >> NVREG_RINGSZ_TXSHIFT) & 0xffff) + 1;
break;
case NvRegMIIData:
EmuNVNet_MiiReadWrite(value);
break;
case NvRegTxRxControl:
if (value == NVREG_TXRXCTL_KICK) {
log_debug("NvRegTxRxControl = NVREG_TXRXCTL_KICK!\n");
log_warning("TODO: nvnet_dma_packet_from_guest");
// nvnet_dma_packet_from_guest(s);
}
if (value & NVREG_TXRXCTL_BIT2) {
EmuNVNet_SetRegister(NvRegTxRxControl, NVREG_TXRXCTL_IDLE, 4);
break;
}
if (value & NVREG_TXRXCTL_BIT1) {
EmuNVNet_SetRegister(NvRegIrqStatus, 0, 4);
break;
}
else if (value == 0) {
uint32_t temp = EmuNVNet_GetRegister(NvRegUnknownSetupReg3, 4);
if (temp == NVREG_UNKSETUP3_VAL1) {
/* forcedeth waits for this bit to be set... */
EmuNVNet_SetRegister(NvRegUnknownSetupReg5,
NVREG_UNKSETUP5_BIT31, 4);
break;
}
}
EmuNVNet_SetRegister(NvRegTxRxControl, value, size);
break;
case NvRegIrqMask:
EmuNVNet_SetRegister(addr, value, size);
EmuNVNet_UpdateIRQ();
break;
case NvRegIrqStatus:
EmuNVNet_SetRegister(addr, EmuNVNet_GetRegister(addr, size) & ~value, size);
EmuNVNet_UpdateIRQ();
break;
default:
EmuNVNet_SetRegister(addr, value, size);
break;
}
log_debug("NET : Write%d: %s (0x%.8X) = 0x%.8X\n", size, EmuNVNet_GetRegisterName(addr), addr, value);
}
/* NVNetDevice */
// PCI Device functions
void NVNetDevice::Init() {
PCIBarRegister r;
// Register Memory bar :
r.Raw.type = PCI_BAR_TYPE_MEMORY;
r.Memory.address = NVNET_ADDR >> 4;
RegisterBAR(0, NVNET_SIZE, r.value);
// Register IO bar :
r.Raw.type = PCI_BAR_TYPE_IO;
r.IO.address = 0xE000;
RegisterBAR(1, 8, r.value);
m_deviceID = 0x01C3;
m_vendorID = PCI_VENDOR_ID_NVIDIA;
}
void NVNetDevice::Reset() {
}
uint32_t NVNetDevice::IORead(int barIndex, uint32_t port, unsigned size) {
if (barIndex != 1) {
return 0;
}
return 0;
}
void NVNetDevice::IOWrite(int barIndex, uint32_t port, uint32_t value, unsigned size) {
if (barIndex != 1) {
return;
}
}
uint32_t NVNetDevice::MMIORead(int barIndex, uint32_t addr, unsigned size) {
if (barIndex != 0) {
return 0;
}
return EmuNVNet_Read(addr, size * 8); // For now, forward
}
void NVNetDevice::MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size) {
if (barIndex != 0) {
return;
}
EmuNVNet_Write(addr, value, size * 8); // For now, forward
}
}

View file

@ -0,0 +1,21 @@
#pragma once
#include "pci.h"
namespace openxbox {
#define NVNET_ADDR 0xFEF00000
#define NVNET_SIZE 0x00000400
class NVNetDevice : public PCIDevice {
public:
// PCI Device functions
void Init();
void Reset();
uint32_t IORead(int barIndex, uint32_t port, unsigned size);
void IOWrite(int barIndex, uint32_t port, uint32_t value, unsigned size);
uint32_t MMIORead(int barIndex, uint32_t addr, unsigned size);
void MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size);
};
}

View file

@ -0,0 +1,102 @@
#include "pci.h"
#include "openxbox/log.h"
namespace openxbox {
bool PCIDevice::GetIOBar(uint32_t port, PCIBar* bar) {
for (auto it = m_BAR.begin(); it != m_BAR.end(); ++it) {
if (it->second.reg.Raw.type == PCI_BAR_TYPE_IO && (port >= it->second.reg.IO.address) && (port < it->second.reg.IO.address + it->second.size)) {
*bar = it->second;
return true;
}
}
return false;
}
bool PCIDevice::GetMMIOBar(uint32_t addr, PCIBar* bar) {
for (auto it = m_BAR.begin(); it != m_BAR.end(); ++it) {
if (it->second.reg.Raw.type == PCI_BAR_TYPE_MEMORY && (addr >= (it->second.reg.Memory.address << 4)) && (addr < (it->second.reg.Memory.address << 4) + it->second.size)) {
*bar = it->second;
return true;
}
}
return false;
}
bool PCIDevice::RegisterBAR(int index, uint32_t size, uint32_t defaultValue) {
if (m_BAR.find(index) != m_BAR.end()) {
log_warning("PCIDevice::RegisterBar: Trying to register a BAR that is already allocated (index: %d)\n", index);
return false;
}
PCIBar bar;
bar.size = size;
bar.pDevice = this;
bar.reg.value = defaultValue;
bar.index = index;
m_BAR[index] = bar;
return true;
}
bool PCIDevice::UpdateBAR(int index, uint32_t newValue) {
auto it = m_BAR.find(index);
if (it == m_BAR.end()) {
log_warning("PCIDevice::UpdateBAR: Trying to update a BAR that does not exist (index: %d, value 0x%08X)\n", index, newValue);
return false;
}
it->second.reg.value = newValue;
return true;
}
uint32_t PCIDevice::ReadConfigRegister(uint32_t reg) {
switch (reg) {
case PCI_CONFIG_DEVICE:
return (m_deviceID << 16) | m_vendorID;
case PCI_CONFIG_BAR_0:
case PCI_CONFIG_BAR_1:
case PCI_CONFIG_BAR_2:
case PCI_CONFIG_BAR_3:
case PCI_CONFIG_BAR_4:
case PCI_CONFIG_BAR_5:
{
int barIndex = (reg - PCI_CONFIG_BAR_0) / 4;
auto it = m_BAR.find(barIndex);
if (it == m_BAR.end()) {
log_warning("PCIDevice::ReadConfigRegister: Trying to Read a BAR that does not exist (index: %d)\n", barIndex);
return 0xFFFFFFFF;
}
return it->second.reg.value;
}
default:
log_warning("PCIDevice::ReadConfigRegister: Unhandled Register %X\n", reg);
break;
}
return 0;
}
void PCIDevice::WriteConfigRegister(uint32_t reg, uint32_t value) {
switch (reg) {
case PCI_CONFIG_BAR_0:
case PCI_CONFIG_BAR_1:
case PCI_CONFIG_BAR_2:
case PCI_CONFIG_BAR_3:
case PCI_CONFIG_BAR_4:
case PCI_CONFIG_BAR_5:
{
int barIndex = (reg - PCI_CONFIG_BAR_0) / 4;
UpdateBAR(barIndex, value);
break;
}
default:
log_warning("PCIDevice::WriteConfigRegister: Unhandled Register %X\n", reg);
break;
}
}
}

View file

@ -0,0 +1,87 @@
#pragma once
#include <cstdint>
#include <map>
namespace openxbox {
#define PCI_BAR_TYPE_IO 1
#define PCI_BAR_TYPE_MEMORY 0
#define PCI_CONFIG_DEVICE 0x00
#define PCI_CONFIG_BAR_0 0x10
#define PCI_CONFIG_BAR_1 0x14
#define PCI_CONFIG_BAR_2 0x18
#define PCI_CONFIG_BAR_3 0x1C
#define PCI_CONFIG_BAR_4 0x20
#define PCI_CONFIG_BAR_5 0x24
#define PCI_VENDOR_ID_NVIDIA 0x10DE
class PCIDevice;
typedef struct {
} PCIBarMemory;
typedef struct {
} PCIBarIO;
typedef struct {
union {
struct {
uint32_t type : 1;
uint32_t locatable : 2;
uint32_t prefetchable : 1;
uint32_t address : 28;
} Memory;
struct {
uint32_t type : 1;
uint32_t reserved : 1;
uint32_t address : 30;
} IO;
struct {
uint32_t type : 1;
uint32_t other : 31;
} Raw;
uint32_t value;
};
} PCIBarRegister;
typedef struct {
uint32_t size;
PCIBarRegister reg;
int index;
PCIDevice* pDevice;
} PCIBar;
class PCIDevice {
// PCI Device Interface
public:
virtual void Init() = 0;
virtual void Reset() = 0;
virtual uint32_t IORead(int barIndex, uint32_t port, unsigned size) = 0;
virtual void IOWrite(int barIndex, uint32_t port, uint32_t value, unsigned size) = 0;
virtual uint32_t MMIORead(int barIndex, uint32_t addr, unsigned size) = 0;
virtual void MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size) = 0;
// PCI Device Implementation
public:
bool GetIOBar(uint32_t port, PCIBar *bar);
bool GetMMIOBar(uint32_t addr, PCIBar *bar);
bool RegisterBAR(int index, uint32_t size, uint32_t defaultValue);
bool UpdateBAR(int index, uint32_t newValue);
uint32_t ReadConfigRegister(uint32_t reg);
void WriteConfigRegister(uint32_t reg, uint32_t value);
protected:
std::map<int, PCIBar> m_BAR;
uint16_t m_deviceID;
uint16_t m_vendorID;
};
}

View file

@ -0,0 +1,124 @@
#include "pcibus.h"
#include "openxbox/log.h"
namespace openxbox {
void PCIBus::ConnectDevice(uint32_t deviceId, PCIDevice *pDevice) {
if (m_Devices.find(deviceId) != m_Devices.end()) {
log_warning("PCIBus: Attempting to connect two devices to the same device address\n");
return;
}
m_Devices[deviceId] = pDevice;
pDevice->Init();
}
void PCIBus::IOWriteConfigAddress(uint32_t pData) {
memcpy(&m_configAddressRegister, &pData, sizeof(PCIConfigAddressRegister));
}
uint32_t PCIBus::IOReadConfigData() {
auto it = m_Devices.find(PCI_DEVID(m_configAddressRegister.busNumber, m_configAddressRegister.deviceNumber));
if (it != m_Devices.end()) {
return it->second->ReadConfigRegister(m_configAddressRegister.registerNumber & PCI_CONFIG_REGISTER_MASK);
}
log_warning("PCIBus::IOReadConfigData: Invalid Device Write (Bus: %d Slot: %d Function: %d)\n", m_configAddressRegister.busNumber,
PCI_SLOT(m_configAddressRegister.deviceNumber),
PCI_FUNC(m_configAddressRegister.deviceNumber));
// Unpopulated PCI slots return 0xFFFFFFFF
return 0xFFFFFFFF;
}
void PCIBus::IOWriteConfigData(uint32_t pData) {
auto it = m_Devices.find(PCI_DEVID(m_configAddressRegister.busNumber, m_configAddressRegister.deviceNumber));
if (it != m_Devices.end()) {
it->second->WriteConfigRegister(m_configAddressRegister.registerNumber & PCI_CONFIG_REGISTER_MASK, pData);
return;
}
log_warning("PCIBus::IOWriteConfigData: Invalid Device Write (Bus: %d Slot: %d Function: %d)\n", m_configAddressRegister.busNumber,
PCI_SLOT(m_configAddressRegister.deviceNumber),
PCI_FUNC(m_configAddressRegister.deviceNumber));
}
bool PCIBus::IORead(uint32_t addr, uint32_t* data, unsigned size) {
switch (addr) {
case PORT_PCI_CONFIG_DATA: // 0xCFC
if (size == sizeof(uint32_t)) {
*data = IOReadConfigData();
return true;
} // TODO : else log wrong size-access?
break;
default:
for (auto it = m_Devices.begin(); it != m_Devices.end(); ++it) {
PCIBar bar;
if (it->second->GetIOBar(addr, &bar)) {
*data = it->second->IORead(bar.index, addr - bar.reg.IO.address, size);
return true;
}
}
}
return false;
}
bool PCIBus::IOWrite(uint32_t addr, uint32_t value, unsigned size) {
switch (addr) {
case PORT_PCI_CONFIG_ADDRESS: // 0xCF8
if (size == sizeof(uint32_t)) {
IOWriteConfigAddress(value);
return true;
} // TODO : else log wrong size-access?
break;
case PORT_PCI_CONFIG_DATA: // 0xCFC
if (size == sizeof(uint32_t)) {
IOWriteConfigData(value);
return true; // TODO : Should IOWriteConfigData() success/failure be returned?
} // TODO : else log wrong size-access?
break;
default:
for (auto it = m_Devices.begin(); it != m_Devices.end(); ++it) {
PCIBar bar;
if (it->second->GetIOBar(addr, &bar)) {
it->second->IOWrite(bar.index, addr - bar.reg.IO.address, value, size);
return true;
}
}
}
return false;
}
bool PCIBus::MMIORead(uint32_t addr, uint32_t* data, unsigned size) {
for (auto it = m_Devices.begin(); it != m_Devices.end(); ++it) {
PCIBar bar;
if (it->second->GetMMIOBar(addr, &bar)) {
*data = it->second->MMIORead(bar.index, addr - (bar.reg.Memory.address << 4), size);
return true;
}
}
return false;
}
bool PCIBus::MMIOWrite(uint32_t addr, uint32_t value, unsigned size) {
for (auto it = m_Devices.begin(); it != m_Devices.end(); ++it) {
PCIBar bar;
if (it->second->GetMMIOBar(addr, &bar)) {
it->second->MMIOWrite(bar.index, addr - (bar.reg.Memory.address << 4), value, size);
return true;
}
}
return false;
}
void PCIBus::Reset() {
for (auto it = m_Devices.begin(); it != m_Devices.end(); ++it) {
it->second->Reset();
}
}
}

View file

@ -0,0 +1,51 @@
#pragma once
#include "pci.h"
#include <map>
namespace openxbox {
#define PORT_PCI_CONFIG_ADDRESS 0xCF8
#define PORT_PCI_CONFIG_DATA 0xCFC
#define PCI_CONFIG_REGISTER_MASK 0xFC
#define PCI_DEVFN(slot, func) ((((slot) & 0x1f) << 3) | ((func) & 0x07))
#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f) // 5 bits (PCIConfigAddressRegister.deviceNumber)
#define PCI_FUNC(devfn) ((devfn) & 0x07) // 3 bits (PCIConfigAddressRegister.functionNumber)
#define PCI_DEVID(bus, devfn) ((((uint16_t)(bus)) << 8) | (devfn))
#define PCI_BUS_NUM(x) (((x) >> 8) & 0xff)
typedef struct {
uint8_t registerNumber : 8;
uint8_t functionNumber : 3; // PCI_FUNC
uint8_t deviceNumber : 5; // PCI_SLOT
uint8_t busNumber : 8; // PCI_BUS_NUM
uint8_t reserved : 7;
uint8_t enable : 1;
} PCIConfigAddressRegister;
class PCIBus {
public:
void ConnectDevice(uint32_t deviceId, PCIDevice *pDevice);
bool IORead(uint32_t addr, uint32_t* value, unsigned size);
bool IOWrite(uint32_t addr, uint32_t value, unsigned size);
bool MMIORead(uint32_t addr, uint32_t * data, unsigned size);
bool MMIOWrite(uint32_t addr, uint32_t value, unsigned size);
void Reset();
private:
void IOWriteConfigAddress(uint32_t pData);
void IOWriteConfigData(uint32_t pData);
uint32_t IOReadConfigData();
std::map<uint32_t, PCIDevice*> m_Devices;
PCIConfigAddressRegister m_configAddressRegister;
};
}

View file

@ -0,0 +1,7 @@
#include "sm.h"
namespace openxbox {
}

24
src/core/openxbox/hw/sm.h Normal file
View file

@ -0,0 +1,24 @@
#pragma once
#include <cstdint>
namespace openxbox {
class SMDevice {
public:
virtual void Init() = 0;
virtual void Reset() = 0;
virtual void QuickCommand(bool read) = 0;
virtual uint8_t ReceiveByte() = 0;
virtual uint8_t ReadByte(uint8_t command) = 0;
virtual uint16_t ReadWord(uint8_t command) = 0;
virtual int ReadBlock(uint8_t command, uint8_t *data) = 0;
virtual void SendByte(uint8_t data) = 0;
virtual void WriteByte(uint8_t command, uint8_t value) = 0;
virtual void WriteWord(uint8_t command, uint16_t value) = 0;
virtual void WriteBlock(uint8_t command, uint8_t* data, int length) = 0;
};
}

View file

@ -0,0 +1,223 @@
#include "smbus.h"
#include "openxbox/log.h"
namespace openxbox {
void SMBus::Init() {
PCIBarRegister r;
r.Raw.type = PCI_BAR_TYPE_IO;
r.IO.address = 0xC000;
RegisterBAR(1, 32, r.value);
m_deviceID = 0x01B4;
m_vendorID = PCI_VENDOR_ID_NVIDIA;
}
void SMBus::Reset() {
m_Status = 0;
for (auto it = m_Devices.begin(); it != m_Devices.end(); ++it) {
it->second->Reset();
}
}
void SMBus::ConnectDevice(uint8_t addr, SMDevice *pDevice) {
uint8_t dev_addr = (addr >> 1) & 0x7f;
if (m_Devices.find(dev_addr) != m_Devices.end()) {
log_warning("SMBus: Attempting to connect two devices to the same device address\n");
return;
}
m_Devices[dev_addr] = pDevice;
pDevice->Init();
}
void SMBus::ExecuteTransaction() {
uint8_t prot = m_Control & GE_CYC_TYPE_MASK;
bool read = m_Address & 0x01;
uint8_t addr = (m_Address >> 1) & 0x7f;
auto it = m_Devices.find(addr);
if (it == m_Devices.end()) {
m_Status |= GS_PRERR_STS;
log_warning("SMBus::ExecuteTransaction: Invalid Device (Addr: 0x%x Read: %x Prot: 0x%x)\n", addr, read, prot);
return;
}
SMDevice *pDevice = it->second;
switch (prot) {
case AMD756_QUICK:
pDevice->QuickCommand(read);
break;
case AMD756_BYTE:
if (read) {
m_Data0 = pDevice->ReceiveByte();
}
else {
pDevice->SendByte(m_Data0); // TODO: Was m_Command correct?
}
break;
case AMD756_BYTE_DATA:
if (read) {
m_Data0 = pDevice->ReadByte(m_Command);
}
else {
pDevice->WriteByte(m_Command, m_Data0);
}
break;
case AMD756_WORD_DATA:
if (read) {
uint16_t val;
val = pDevice->ReadWord(m_Command);
m_Data0 = val & 0xFF;
m_Data1 = val >> 8;
}
else {
pDevice->WriteWord(m_Command, m_Data0);
}
break;
case AMD756_BLOCK_DATA:
if (read) {
m_Data0 = pDevice->ReadBlock(m_Command, m_Data);
}
else {
pDevice->WriteBlock(m_Command, m_Data, m_Data0);
}
break;
default:
m_Status |= GS_PRERR_STS;
return;
}
m_Status |= GS_HCYC_STS;
}
uint32_t SMBus::IORead(int barIndex, uint32_t addr, unsigned size) {
if (barIndex != 1) {
return 0;
}
// For now, make SMBus only support byte-reads
if (size != 1) {
return 0; // TODO: Set GS_PRERR_STS in m_Status too?
}
uint32_t value;
addr &= 0x3f;
switch (addr) {
case SMB_GLOBAL_STATUS:
value = m_Status;
break;
case SMB_GLOBAL_ENABLE:
value = m_Control & 0x1f;
break;
case SMB_HOST_COMMAND:
value = m_Command;
break;
case SMB_HOST_ADDRESS:
value = m_Address;
break;
case SMB_HOST_DATA:
value = m_Data0;
break;
case SMB_HOST_DATA + 1:
value = m_Data1;
break;
case SMB_HOST_BLOCK_DATA:
value = m_Data[m_Index++];
if (m_Index > 31) {
m_Index = 0;
}
break;
default:
value = 0;
break;
}
return value;
}
void SMBus::IOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size) {
if (barIndex != 1) {
return;
}
// For now, make SMBus only support byte-reads
if (size != 1) {
return; // TODO : Set GS_PRERR_STS in m_Status too?
}
addr &= 0x3f;
switch (addr) {
case SMB_GLOBAL_STATUS:
// If a new status is being set and interrupts are enabled, trigger an interrupt
if ((m_Control & GE_HCYC_EN) && ((value & GS_CLEAR_STS) & (~(m_Status & GS_CLEAR_STS)))) {
// TODO: RaiseIRQ
}
else {
// TODO LowerIRQ
}
if (value & GS_CLEAR_STS) {
m_Status = 0;
m_Index = 0;
}
else if (value & GS_HCYC_STS) {
m_Status = GS_HCYC_STS;
m_Index = 0;
}
else {
m_Status = GS_HCYC_STS;
m_Index = 0;
}
break;
case SMB_GLOBAL_ENABLE:
m_Control = value;
if (value & GE_ABORT) {
m_Status |= GS_ABRT_STS;
}
if (value & GE_HOST_STC) {
ExecuteTransaction();
if ((value & GE_HCYC_EN) && (m_Status & GS_CLEAR_STS)) {
// TODO: RaiseIRQ
}
}
break;
case SMB_HOST_COMMAND:
m_Command = value;
break;
case SMB_HOST_ADDRESS:
m_Address = value;
break;
case SMB_HOST_DATA:
m_Data0 = value;
break;
case SMB_HOST_DATA + 1:
m_Data1 = value;
break;
case SMB_HOST_BLOCK_DATA:
m_Data[m_Index++] = value;
if (m_Index > 31) {
m_Index = 0;
}
break;
default:
break;
}
}
uint32_t SMBus::MMIORead(int barIndex, uint32_t addr, unsigned size) {
return 0;
}
void SMBus::MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size) {
}
}

View file

@ -0,0 +1,82 @@
#pragma once
#include "pci.h"
#include "sm.h"
#include <map>
namespace openxbox {
#define SMB_ADDR_OFFSET 0xE0
#define SMB_IOSIZE 16
#define SMB_IRQ 11
#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
// SMB_GLOBAL_STATUS flags
#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)
class SMBus : public PCIDevice {
public:
using PCIDevice::PCIDevice;
// PCI Functions
void Init();
void Reset();
uint32_t IORead(int barIndex, uint32_t addr, unsigned size = sizeof(uint8_t));
void IOWrite(int barIndex, uint32_t addr, uint32_t data, unsigned size = sizeof(uint8_t));
uint32_t MMIORead(int barIndex, uint32_t addr, unsigned size);
void MMIOWrite(int barIndex, uint32_t addr, uint32_t value, unsigned size);
// Misc
void ConnectDevice(uint8_t addr, SMDevice *device);
private:
uint8_t m_Status;
uint8_t m_Control;
uint8_t m_Command;
uint8_t m_Address;
uint8_t m_Data0;
uint8_t m_Data1;
uint8_t m_Data[32];
uint8_t m_Index;
void ExecuteTransaction();
std::map<uint8_t, SMDevice*> m_Devices;
};
}

View file

@ -0,0 +1,160 @@
#include "defs.h"
#include "smc.h"
#include "led.h"
namespace openxbox {
SMCRevision SMCRevisionFromHardwareModel(HardwareModel hardwareModel) {
switch (hardwareModel) {
case Revision1_0:
return P01; // Our SCM returns PIC version string "P01"
case Revision1_1:
case Revision1_2:
case Revision1_3:
case Revision1_4:
case Revision1_5:
case Revision1_6:
// EmuWarning("Guessing SCMRevision");
return P2L; // Assumption; Our SCM returns PIC version string "P05"
case DebugKit:
return D01; // Our SCM returns PIC version string "DXB"
default:
// UNREACHABLE(hardwareModel);
return P2L;
}
}
SMCDevice::SMCDevice(SMCRevision revision) {
m_revision = revision;
}
void SMCDevice::Init() {
m_PICVersionStringIndex = 0;
memset(m_buffer, 0, sizeof(m_buffer));
m_buffer[SMCRegister::AVPack] = kSMC_AVPack_HDTV; // see http://xboxdevwiki.net/PIC#The_AV_Pack
m_buffer[SMCRegister::LEDSequence] = LED::GREEN;
m_buffer[SMCRegister::Scratch] = 0; // http://xboxdevwiki.net/PIC#Scratch_register_values
}
void SMCDevice::Reset() {
// TODO
}
void SMCDevice::QuickCommand(bool read) {
// TODO
}
uint8_t SMCDevice::ReceiveByte() {
return 0; // TODO
}
uint8_t SMCDevice::ReadByte(uint8_t command) {
switch (command) {
case SMCRegister::Version:
// See http://xboxdevwiki.net/PIC#PIC_version_string
switch (m_revision) {
case SMCRevision::P01: m_buffer[0] = "P01"[m_PICVersionStringIndex]; break;
case SMCRevision::P2L: m_buffer[0] = "P05"[m_PICVersionStringIndex]; break; // ??
case SMCRevision::D01: m_buffer[0] = "DXB"[m_PICVersionStringIndex]; break;
case SMCRevision::D05: m_buffer[0] = "D05"[m_PICVersionStringIndex]; break; // ??
// default: UNREACHABLE(m_revision);
}
m_PICVersionStringIndex = (m_PICVersionStringIndex + 1) % 3;
break;
//case SMCRegister::TrayState:
//case SMCRegister::AVPack:
//case SMCRegister::CPUTemp:
//case SMCRegister::GPUTemp:
case ReadScratchRegister:
return m_buffer[0x0E];
//case SMCRegister::PowerFanReadback:
//case SMCRegister::InterruptReason:
//case SMCRegister::Overheated:
//case SMCRegister::Scratch:
case SMCRegister::Challenge_1C:
case SMCRegister::Challenge_1D:
case SMCRegister::Challenge_1E:
case SMCRegister::Challenge_1F:
if (m_revision == SMCRevision::D01)
// See http://xboxdevwiki.net/PIC#PIC_Challenge_.28regs_0x1C.7E0x21.29
return 0;
break;
// case ...: TODO
}
return m_buffer[command];
}
uint16_t SMCDevice::ReadWord(uint8_t command) {
return 0; // TODO
}
int SMCDevice::ReadBlock(uint8_t command, uint8_t *data) {
return 0; // TODO
}
void SMCDevice::SendByte(uint8_t data) {
// TODO
}
void SMCDevice::WriteByte(uint8_t command, uint8_t value) {
switch (command) {
case SMCRegister::Version:
// NOTE: MAME Xbox/Chihiro driver doesn't check for zero
if (value == 0) {
m_PICVersionStringIndex = 0;
}
return;
case SMCRegister::Reset:
// See http://xboxdevwiki.net/PIC#Reset_and_Power_Off
switch (value) {
case kSMCReset_AssertReset: return; // TODO
case kSMCReset_AssertPowerCycle: return; // TODO
case kSMCReset_AssertShutdown: return; // TODO: Power off, terminating the emulation
}
// TODO: case PowerFanMode:
// TODO: case PowerFanRegister:
case SMCRegister::LEDMode:
switch (value) {
case 0: return; // TODO: Automatic LED management
case 1: return; // TODO: Custom sequence
default: // TODO:
// HalWriteSMBusValue(0x20, 0x08, false, x) and then HalWriteSMBusValue(0x20, 0x07, false, y > 1)
// will cause the led to be solid green, while the next pair of
// HalWriteSMBusValue with arbitrary y will cause the led to assume the color of the sequence x
// and afterwards this will repeat with whatever y; ntstatus is always 0
return;
}
case SMCRegister::LEDSequence: break; // Let the write go through to the buffer
// TODO: case SMCRegister::TrayEject:
// TODO: case SMCRegister::ScratchRegister:
// TODO: case SMCRegister::ResetOnEject:
// TODO: case SMCRegister::InterruptEnable:
case SMCRegister::Scratch:
// See http://xboxdevwiki.net/PIC#Scratch_register_values
switch (value) {
case kSMCScratch_TrayEjectPending: return; // TODO
case kSMCScratch_DisplayFatalError: return; // TODO
case kSMCScratch_ShortAnimation: return; // TODO
case kSMCScratch_DashboardBoot: return; // TODO
}
break;
// TODO: case SMCRegister::PICChallenge_20:
// TODO: case SMCRegister::PICChallenge_21:
}
m_buffer[command] = value;
}
void SMCDevice::WriteWord(uint8_t command, uint16_t value) {
// TODO
}
void SMCDevice::WriteBlock(uint8_t command, uint8_t* data, int length) {
// TODO
}
}

115
src/core/openxbox/hw/smc.h Normal file
View file

@ -0,0 +1,115 @@
#pragma once
#include <cstdint>
#include "defs.h"
#include "sm.h"
namespace openxbox {
// The picture linked below shows:
// https://upload.wikimedia.org/wikipedia/commons/9/94/Xbox-Motherboard-FR.jpg
// PIC16LC63A-04/SO
// (M) 0123857
//
// Producer: http://www.microchip.com/wwwproducts/en/en010145
// Datasheet: http://ww1.microchip.com/downloads/en/DeviceDoc/30605D.pdf
// NOTE: Instead of calling this device by its real name ("PIC16LC63A-04/SO"),
// we've decided to call this device "SMC", since we don't implement the
// low-level functionality of this PIC, but only the minimum set of high-level
// commands that are sufficient for the Xbox.
// Registers obtained from https://web.archive.org/web/20100617022549/http://www.xbox-linux.org/wiki/PIC:
typedef enum { // Read Write
Version = 0x01, // PIC version string Reset PIC version string counter
Reset = 0x02, // - Reset and power off control
TrayState = 0x03, // Tray state -
AVPack = 0x04, // A/V pack state -
PowerFanMode = 0x05, // - Power fan mode (0 = automatic; 1 = custom speed from reg 0x06)
PowerFanRegister = 0x06, // - Set custom power fan speed (0-50)
LEDMode = 0x07, // - LED mode (0 = automatic; 1 = custom sequence from reg 0x08)
LEDSequence = 0x08, // - LED flashing sequence
CPUTemp = 0x09, // CPU temperature (°C) -
GPUTemp = 0x0A, // GPU (board?) temperature (°C) -
TrayEject = 0x0C, // - Tray eject (0 = eject; 1 = load)
ScratchRegister = 0x0E, // - Another scratch register? Seems like an error code
ReadScratchRegister = 0x0F, // Reads scratch register written with 0x0E -
PowerFanReadback = 0x10, // - Current power fan speed (0-50)
InterruptReason = 0x11, // - Interrupt reason
Overheated = 0x18, // Locks up the Xbox in "overheated" state -
ResetOnEject = 0x19, // - Reset on eject (0 = enable; 1 = disable)
InterruptEnable = 0x1A, // - Interrupt enable (write 0x01 to enable; can't disable once enabled)
Scratch = 0x1B, // Scratch register for the original kernel Scratch register for the original kernel
Challenge_1C = 0x1C, // Random number for boot challenge -
Challenge_1D = 0x1D, // Random number for boot challenge -
Challenge_1E = 0x1E, // Random number for boot challenge -
Challenge_1F = 0x1F, // Random number for boot challenge -
PICChallenge_20 = 0x20, // - Response to PIC challenge (written first)
PICChallenge_21 = 0x21, // - Response to PIC challenge (written second)
} SMCRegister;
// Register values for SMC_COMMAND_RESET
const uint8_t kSMCReset_AssertReset = 0x01;
const uint8_t kSMCReset_AssertPowerCycle = 0x40;
const uint8_t kSMCReset_AssertShutdown = 0x80;
// Register values for SMC_COMMAND_SCRATCH
const uint8_t kSMCScratch_TrayEjectPending = 0x01;
const uint8_t kSMCScratch_DisplayFatalError = 0x02;
const uint8_t kSMCScratch_ShortAnimation = 0x04;
const uint8_t kSMCScratch_DashboardBoot = 0x08;
// http://xboxdevwiki.net/System_Management_Controller
typedef enum {
P01,
P2L,
D01, // Seen in a debug kit
D05, // Seen in a earlier model chihiro
} SMCRevision;
// A/V packs, as reported by the SMC
// http://xboxdevwiki.net/AV_Cables
const uint8_t kSMC_AVPack_SCART = 0; // = AV_PACK_SCART 3
const uint8_t kSMC_AVPack_HDTV = 1; // = AV_PACK_HDTV 4
const uint8_t kSMC_AVPack_VGA = 2; // = AV_PACK_VGA 5
const uint8_t kSMC_AVPack_RF = 3; // = AV_PACK_RFU 2
const uint8_t kSMC_AVPack_SVideo = 4; // = AV_PACK_SVIDEO 6
const uint8_t kSMC_AVPack_Unknown = 5; // = AV_PACK_NONE 0
const uint8_t kSMC_AVPack_Standard = 6; // = AV_PACK_STANDARD 1
const uint8_t kSMC_AVPack_None = 7; // = AV_PACK_NONE 0
SMCRevision SMCRevisionFromHardwareModel(HardwareModel hardwareModel);
class SMCDevice : public SMDevice {
public:
// constructor
SMCDevice(SMCRevision revision);
// SMDevice functions
void Init();
void Reset();
void QuickCommand(bool read);
uint8_t ReceiveByte();
uint8_t ReadByte(uint8_t command);
uint16_t ReadWord(uint8_t command);
int ReadBlock(uint8_t command, uint8_t *data);
void SendByte(uint8_t data);
void WriteByte(uint8_t command, uint8_t value);
void WriteWord(uint8_t command, uint16_t value);
void WriteBlock(uint8_t command, uint8_t* data, int length);
private:
SMCRevision m_revision;
int m_PICVersionStringIndex = 0;
uint8_t m_buffer[256] = {};
};
}

View file

@ -0,0 +1,29 @@
#include "defs.h"
#include "tvenc.h"
namespace openxbox {
TVEncoder TVEncoderFromHardwareModel(HardwareModel hardwareModel) {
switch (hardwareModel) {
case Revision1_0:
case Revision1_1:
case Revision1_2:
case Revision1_3:
return TVEncoder::Conexant;
case Revision1_4:
return TVEncoder::Focus;
case Revision1_5:
return TVEncoder::Focus; // Assumption
case Revision1_6:
return TVEncoder::XCalibur;
case DebugKit:
// LukeUsher : My debug kit and at least most of them (maybe all?)
// are equivalent to v1.0 and have Conexant encoders.
return TVEncoder::Conexant;
default:
// UNREACHABLE(hardwareModel);
return TVEncoder::Focus;
}
}
}

View file

@ -0,0 +1,20 @@
#pragma once
#include <cstdint>
#include "defs.h"
namespace openxbox {
// Xbox TV encoder chips.
typedef enum {
// http://xboxdevwiki.net/Hardware_Revisions#Video_encoder
Conexant,
Focus,
XCalibur
} TVEncoder;
TVEncoder TVEncoderFromHardwareModel(HardwareModel hardwareModel);
}

View file

@ -18,7 +18,7 @@ namespace openxbox {
// manually mapped to virtual address 0x80010000.
// FIXME: Get these from a real xbox
#define XBOX_RAM_SIZE MiB(64)
#define XBOX_ROM_SIZE MiB(16)
#define XBOX_RAM_SIZE MiB(64) // TODO: 128 MiB systems
#define XBOX_ROM_SIZE MiB(16)
}

View file

@ -1,5 +1,7 @@
#pragma once
#include "openxbox/hw/defs.h"
namespace openxbox {
struct OpenXBOXSettings {
@ -16,6 +18,9 @@ struct OpenXBOXSettings {
// true: dump memory address mappings (page tables) after execution
bool debug_dumpMemoryMapping = false;
// The Xbox hardware model to use
HardwareModel hw_model = DebugKit;
// The system clock tick rate
float hw_sysclock_tickRate = 1000.0f;

View file

@ -4,19 +4,24 @@
#include "openxbox/alloc.h"
#include "openxbox/debug.h"
#include "openxbox/hw/defs.h"
#include "openxbox/hw/tvenc.h"
#include <chrono>
namespace openxbox {
// CPU emulation thread function
static uint32_t EmuCpuThreadFunc(void *data) {
Xbox *xbox = (Xbox *)data;
return xbox->RunCpu();
Xbox *xbox = (Xbox *)data;
return xbox->RunCpu();
}
// System clock thread function
static uint32_t EmuSysClockThreadFunc(void *data) {
SystemClock *sysClock = (SystemClock *)data;
sysClock->Run();
return 0;
SystemClock *sysClock = (SystemClock *)data;
sysClock->Run();
return 0;
}
@ -24,9 +29,9 @@ static uint32_t EmuSysClockThreadFunc(void *data) {
* Constructor
*/
Xbox::Xbox(IOpenXBOXCPUModule *cpuModule)
: m_cpuModule(cpuModule)
: m_cpuModule(cpuModule)
{
m_sysClock = nullptr;
m_sysClock = nullptr;
}
/*!
@ -34,10 +39,10 @@ Xbox::Xbox(IOpenXBOXCPUModule *cpuModule)
*/
Xbox::~Xbox()
{
if (m_cpu) m_cpuModule->FreeCPU(m_cpu);
if (m_ram) vfree(m_ram);
if (m_rom) vfree(m_rom);
if (m_memRegion) delete m_memRegion;
if (m_cpu) m_cpuModule->FreeCPU(m_cpu);
if (m_ram) vfree(m_ram);
if (m_rom) vfree(m_rom);
if (m_memRegion) delete m_memRegion;
}
/*!
@ -46,31 +51,31 @@ Xbox::~Xbox()
int Xbox::Initialize(OpenXBOXSettings *settings)
{
m_settings = settings;
MemoryRegion *rgn;
MemoryRegion *rgn;
// Initialize 4 GiB address space
m_memRegion = new MemoryRegion(MEM_REGION_NONE, 0x00000000, 0x100000000ULL, NULL);
// Initialize 4 GiB address space
m_memRegion = new MemoryRegion(MEM_REGION_NONE, 0x00000000, 0x100000000ULL, NULL);
// ----- RAM --------------------------------------------------------------
// Create RAM region
log_debug("Allocating RAM (%d MiB)\n", XBOX_RAM_SIZE >> 20);
m_ram = (char *)valloc(XBOX_RAM_SIZE);
assert(m_ram != NULL);
memset(m_ram, 0, XBOX_RAM_SIZE);
// Create RAM region
log_debug("Allocating RAM (%d MiB)\n", XBOX_RAM_SIZE >> 20);
m_ram = (char *)valloc(XBOX_RAM_SIZE);
assert(m_ram != NULL);
memset(m_ram, 0, XBOX_RAM_SIZE);
// Map RAM at address 0x00000000
rgn = new MemoryRegion(MEM_REGION_RAM, 0x00000000, XBOX_RAM_SIZE, m_ram);
assert(rgn != NULL);
m_memRegion->AddSubRegion(rgn);
// Map RAM at address 0x00000000
rgn = new MemoryRegion(MEM_REGION_RAM, 0x00000000, XBOX_RAM_SIZE, m_ram);
assert(rgn != NULL);
m_memRegion->AddSubRegion(rgn);
// ----- ROM --------------------------------------------------------------
// Create ROM region
log_debug("Allocating ROM (%d MiB)\n", XBOX_ROM_SIZE >> 20);
m_rom = (char *)valloc(XBOX_ROM_SIZE);
assert(m_rom != NULL);
memset(m_rom, 0, XBOX_ROM_SIZE);
// Create ROM region
log_debug("Allocating ROM (%d MiB)\n", XBOX_ROM_SIZE >> 20);
m_rom = (char *)valloc(XBOX_ROM_SIZE);
assert(m_rom != NULL);
memset(m_rom, 0, XBOX_ROM_SIZE);
// Map ROM to address 0xFF000000
rgn = new MemoryRegion(MEM_REGION_ROM, 0xFF000000, XBOX_ROM_SIZE, m_rom);
@ -135,47 +140,100 @@ int Xbox::Initialize(OpenXBOXSettings *settings)
// ----- CPU --------------------------------------------------------------
// Initialize CPU
log_debug("Initializing CPU\n");
if (m_cpuModule == nullptr) {
log_fatal("No CPU module specified\n");
return -1;
}
m_cpu = m_cpuModule->GetCPU();
if (m_cpu == nullptr) {
log_fatal("CPU instantiation failed\n");
return -1;
}
m_cpu->Initialize();
// Initialize CPU
log_debug("Initializing CPU\n");
if (m_cpuModule == nullptr) {
log_fatal("No CPU module specified\n");
return -1;
}
m_cpu = m_cpuModule->GetCPU();
if (m_cpu == nullptr) {
log_fatal("CPU instantiation failed\n");
return -1;
}
m_cpu->Initialize(this);
// Allow CPU to update memory map based on device allocation, etc
m_cpu->MemMap(m_memRegion);
// Allow CPU to update memory map based on device allocation, etc
m_cpu->MemMap(m_memRegion);
// ----- Hardware ---------------------------------------------------------
// ----- Other hardware ---------------------------------------------------
// Initialize system clock
log_debug("Initializing System Clock\n");
m_sysClock = new SystemClock(m_cpu, settings->hw_sysclock_tickRate);
assert(m_sysClock != NULL);
log_debug("Initializing System Clock\n");
m_sysClock = new SystemClock(m_cpu, settings->hw_sysclock_tickRate);
assert(m_sysClock != NULL);
// Determine which revisions of which components should be used for the
// specified hardware model
MCPXRevision mcpxRevision = MCPXRevisionFromHardwareModel(settings->hw_model);
SMCRevision smcRevision = SMCRevisionFromHardwareModel(settings->hw_model);
TVEncoder tvEncoder = TVEncoderFromHardwareModel(settings->hw_model);
log_debug("Initializing devices\n");
// Create busses
m_PCIBus = new PCIBus();
m_SMBus = new SMBus();
// Create devices
m_MCPX = new MCPXDevice(mcpxRevision);
m_SMC = new SMCDevice(smcRevision);
m_EEPROM = new EEPROMDevice();
m_NVNet = new NVNetDevice();
// TODO: m_NV2A = new NV2ADevice();
// Connect devices to SMBus
m_SMBus->ConnectDevice(kSMBusAddress_SystemMicroController, m_SMC); // W 0x20 R 0x21
m_SMBus->ConnectDevice(kSMBusAddress_EEPROM, m_EEPROM); // W 0xA8 R 0xA9
// TODO: Other SMBus devices to connect
//m_SMBus->ConnectDevice(kSMBusAddress_MCPX, m_MCPX); // W 0x10 R 0x11 -- TODO : Is MCPX an SMBus and/or PCI device?
//m_SMBus->ConnectDevice(kSMBusAddress_TemperatureMeasurement, m_TemperatureMeasurement); // W 0x98 R 0x99
//m_SMBus->ConnectDevice(kSMBusAddress_TVEncoder, m_TVEncoder); // W 0x88 R 0x89
switch (tvEncoder) {
case TVEncoder::Conexant:
// g_SMBus->ConnectDevice(kSMBusAddress_TVEncoder_ID_Conexant, m_TVEncoderConexant); // W 0x8A R 0x8B
break;
case TVEncoder::Focus:
// g_SMBus->ConnectDevice(kSMBusAddress_TVEncoder_ID_Focus, m_TVEncoderFocus); // W 0xD4 R 0xD5
break;
case TVEncoder::XCalibur:
// g_SMBus->ConnectDevice(kSMBusAddress_TVEncoder_ID_XCalibur, m_TVEncoderXCalibur); // W 0xE0 R 0xE1
break;
}
// Connect devices to PCI bus
m_PCIBus->ConnectDevice(PCI_DEVID(0, PCI_DEVFN(1, 1)), m_SMBus);
m_PCIBus->ConnectDevice(PCI_DEVID(0, PCI_DEVFN(4, 0)), m_NVNet);
//m_PCIBus->ConnectDevice(PCI_DEVID(0, PCI_DEVFN(4, 1)), m_MCPX); // MCPX device ID = 0x0808 ?
//m_PCIBus->ConnectDevice(PCI_DEVID(0, PCI_DEVFN(5, 0)), m_NVAPU);
//m_PCIBus->ConnectDevice(PCI_DEVID(0, PCI_DEVFN(6, 0)), m_AC97);
//m_PCIBus->ConnectDevice(PCI_DEVID(1, PCI_DEVFN(0, 0)), m_NV2A);
// TODO: Handle other SMBUS Addresses, like PIC_ADDRESS, XCALIBUR_ADDRESS
// Resources:
// http://pablot.com/misc/fancontroller.cpp
// https://github.com/JayFoxRox/Chihiro-Launcher/blob/master/hook.h
// https://github.com/docbrown/vxb/wiki/Xbox-Hardware-Information
// https://web.archive.org/web/20100617022549/http://www.xbox-linux.org/wiki/PIC
// ----- Debugger ---------------------------------------------------------
// GDB Server
// GDB Server
if (m_settings->gdb_enable) {
log_debug("Starting GDB Server\n");
m_gdb = new GdbServer(m_cpu, "127.0.0.1", 9269);
m_gdb->Initialize();
}
return 0;
return 0;
}
void Xbox::InitializePreRun() {
#if ENABLE_GDB_SERVER
// Allow debugging before running so client can setup breakpoints, etc
m_gdb->WaitForConnection();
m_gdb->Debug(1);
#endif
if (m_settings->gdb_enable) {
// Allow debugging before running so client can setup breakpoints, etc
log_debug("Starting GDB Server\n");
m_gdb->WaitForConnection();
m_gdb->Debug(1);
}
}
int Xbox::Run() {
@ -214,8 +272,10 @@ int Xbox::RunCpu()
while (m_should_run) {
// Run CPU emulation
#if 0
#ifdef _DEBUG
t.Start();
#endif
#endif
if (m_settings->cpu_singleStep) {
result = m_cpu->Step();
@ -223,9 +283,11 @@ int Xbox::RunCpu()
else {
result = m_cpu->Run();
}
#if 0
#ifdef _DEBUG
t.Stop();
log_debug("CPU Executed for %lld ms\n", t.GetMillisecondsElapsed());
#endif
#endif
// Handle result
@ -244,11 +306,13 @@ int Xbox::RunCpu()
break;
}
// Pring CPU registers for debugging purposes
#if 1
#ifdef _DEBUG
uint32_t eip;
m_cpu->RegRead(REG_EIP, &eip);
DumpCPURegisters(m_cpu);
// Pring CPU registers for debugging purposes
uint32_t eip;
m_cpu->RegRead(REG_EIP, &eip);
DumpCPURegisters(m_cpu);
#endif
#endif
// Handle reason for the CPU to exit
@ -268,6 +332,77 @@ void Xbox::Stop() {
m_should_run = false;
}
void Xbox::IORead(uint32_t addr, uint32_t *value, uint16_t size) {
switch (addr) {
case 0x8008: { // TODO: Move 0x8008 TIMER to a device
if (size == sizeof(uint32_t)) {
// This timer counts at 3579545 Hz
auto t = std::chrono::high_resolution_clock::now();
*value = static_cast<uint32_t>(t.time_since_epoch().count() * 0.003579545);
}
break;
}
case 0x80C0: { // TODO: Move 0x80C0 TV encoder to a device
if (size == sizeof(uint8_t)) {
// field pin from tv encoder?
m_field_pin = (m_field_pin + 1) & 1;
*value = m_field_pin << 5;
}
break;
}
}
// Pass the IO Read to the PCI Bus.
// This will handle devices with BARs set to IO addresses
if (m_PCIBus->IORead(addr, value, size)) {
return;
}
log_warning("Unhandled I/O read! address = 0x%08x, size = %d\n", addr, size);
}
void Xbox::IOWrite(uint32_t addr, uint32_t value, uint16_t size) {
// Pass the IO Write to the PCI Bus.
// This will handle devices with BARs set to IO addresses
if (m_PCIBus->IOWrite(addr, value, size)) {
return;
}
log_warning("Unhandled I/O write! address = 0x%08x, size = %d, value = 0x%08x\n", addr, size, value);
}
void Xbox::MMIORead(uint32_t addr, uint32_t *value, uint8_t size) {
if ((addr & (size - 1)) != 0) {
log_warning("Unaligned MMIO read! address = 0x%08x, size = %d\n", addr, size);
return;
}
// Pass the read to the PCI Bus.
// This will handle devices with BARs set to MMIO addresses
if (m_PCIBus->MMIORead(addr, value, size)) {
return;
}
log_debug("Unhandled MMIO read! address = 0x%08x, size = %d\n", addr, size);
}
void Xbox::MMIOWrite(uint32_t addr, uint32_t value, uint8_t size) {
if ((addr & (size - 1)) != 0) {
log_warning("Unaligned MMIO write! address = 0x%08x, size = %d, value = 0x%08x\n", addr, size, value);
return;
}
// Pass the write to the PCI Bus.
// This will handle devices with BARs set to MMIO addresses
if (m_PCIBus->MMIOWrite(addr, value, size)) {
return;
}
log_debug("Unhandled MMIO write! address = 0x%08x, size = %d, value = 0x%08x\n", addr, size, value);
}
void Xbox::Cleanup() {
if (LOG_LEVEL >= LOG_LEVEL_DEBUG) {
log_debug("CPU state at the end of execution:\n");

View file

@ -21,6 +21,12 @@
#include "openxbox/settings.h"
#include "openxbox/hw/sysclock.h"
#include "openxbox/hw/smbus.h"
#include "openxbox/hw/pcibus.h"
#include "openxbox/hw/smc.h"
#include "openxbox/hw/mcpx.h"
#include "openxbox/hw/eeprom.h"
#include "openxbox/hw/nvnet.h"
#include "openxbox/cpu_module.h"
@ -32,7 +38,7 @@ namespace openxbox {
* This class is the top-level class, will perform initialization and high-level
* management of the overall emulation flow.
*/
class Xbox : Emulator {
class Xbox : Emulator, IOMapper {
protected:
// ----- Modules ----------------------------------------------------------
IOpenXBOXCPUModule * m_cpuModule;
@ -42,8 +48,17 @@ protected:
char *m_ram;
char *m_rom;
MemoryRegion *m_memRegion;
SystemClock *m_sysClock;
SystemClock *m_sysClock;
SMCDevice *m_SMC;
SMBus *m_SMBus;
PCIBus *m_PCIBus;
MCPXDevice *m_MCPX;
EEPROMDevice *m_EEPROM;
NVNetDevice *m_NVNet;
// TODO: move to TV encoder device
int m_field_pin = 0;
// ----- Configuration ----------------------------------------------------
OpenXBOXSettings *m_settings;
@ -65,6 +80,13 @@ public:
int Run();
int RunCpu();
void Stop();
// IOMapper implementation
void IORead(uint32_t addr, uint32_t *value, uint16_t size);
void IOWrite(uint32_t addr, uint32_t value, uint16_t size);
void MMIORead(uint32_t addr, uint32_t *value, uint8_t size);
void MMIOWrite(uint32_t addr, uint32_t value, uint8_t size);
};
}

View file

@ -34,7 +34,7 @@ HaxmCpu::~HaxmCpu() {
int HaxmCpu::InitializeImpl() {
if (m_haxm == nullptr) {
m_haxm = new Haxm();
m_haxm = new Haxm();
auto status = m_haxm->Initialize();
if (status != HXS_SUCCESS) {
@ -56,7 +56,7 @@ int HaxmCpu::InitializeImpl() {
}
m_interruptHandlerCredits = kInterruptHandlerMaxCredits;
}
}
return 0;
}
@ -97,10 +97,11 @@ int HaxmCpu::RunImpl() {
}
case HAX_EXIT_IO: m_exitInfo.reason = CPU_EXIT_NORMAL; // I/O (in / out instructions)
return HandleIO(tunnel->io._df, tunnel->io._port, tunnel->io._direction, tunnel->io._size, tunnel->io._count, m_vcpu->IOTunnel());
case HAX_EXIT_MMIO: m_exitInfo.reason = CPU_EXIT_NORMAL; return HandleMMIO(tunnel->mmio.gla, tunnel->io._direction); // MMIO
case HAX_EXIT_FAST_MMIO: m_exitInfo.reason = CPU_EXIT_NORMAL; return HandleFastMMIO((struct hax_fastmmio *)m_vcpu->IOTunnel()); // Fast MMIO
case HAX_EXIT_FAST_MMIO: m_exitInfo.reason = CPU_EXIT_NORMAL; // Fast MMIO
return HandleFastMMIO((struct hax_fastmmio *)m_vcpu->IOTunnel());
case HAX_EXIT_INTERRUPT: m_exitInfo.reason = CPU_EXIT_NORMAL; break; // Let HAXM handle this
case HAX_EXIT_PAUSED: m_exitInfo.reason = CPU_EXIT_NORMAL; break; // Let HAXM handle this
case HAX_EXIT_MMIO: m_exitInfo.reason = CPU_EXIT_ERROR; break; // Regular MMIO (cannot be implemented)
case HAX_EXIT_REALMODE: m_exitInfo.reason = CPU_EXIT_ERROR; break; // Real mode is not supported
case HAX_EXIT_UNKNOWN: m_exitInfo.reason = CPU_EXIT_ERROR; break; // VM failed for an unknown reason
case HAX_EXIT_STATECHANGE: m_exitInfo.reason = CPU_EXIT_SHUTDOWN; break; // The VM is shutting down
@ -357,50 +358,49 @@ int HaxmCpu::HandleIO(uint8_t df, uint16_t port, uint8_t direction, uint16_t siz
}
for (uint16_t i = 0; i < count; i++) {
// TODO: delegate to I/O mapper
// port: port number
// ptr: pointer to data buffer
// size: size of data buffer
// direction: read (HAX_IO_OUT) or write (HAX_IO_IN)
uint32_t value;
switch (size) {
case 1: value = *ptr; break;
case 2: value = *reinterpret_cast<uint16_t *>(ptr); break;
case 4: value = *reinterpret_cast<uint32_t *>(ptr); break;
default: assert(0); // should not happen
}
if (direction == HAX_IO_OUT) {
m_ioMapper->IOWrite(port, value, size);
}
else {
m_ioMapper->IORead(port, &value, size);
}
if (df) {
ptr -= size;
}
else {
ptr += size;
}
}
log_warning("I/O unimplemented! df: %d port: 0x%04x direction: %d size: %d count: %d\n", df, port, direction, size, count);
return 0;
}
int HaxmCpu::HandleMMIO(uint32_t physAddress, uint8_t direction) {
log_warning("MMIO unimplemented! address: 0x%08x direction: %d\n", physAddress, direction);
// TODO: handle MMIO at the given physical address
// direction: read (HAX_IO_OUT) or write (HAX_IO_IN)
return 0;
}
int HaxmCpu::HandleFastMMIO(struct hax_fastmmio *info) {
if (info->direction < 2) {
log_warning("Fast MMIO unimplemented! address: 0x%08x value: 0x%x size: %d direction: %d\n", info->gpa, info->value, info->size, info->direction);
// TODO: handle MMIO at the given physical address
// info->gpa: physical address
// info->value: value to read/write
// info->size: number of bytes to read/write
// info->direction: read (HAX_IO_OUT) or write (HAX_IO_IN)
//cpu_physical_memory_rw(info->gpa, (uint8_t *)&info->value, info->size, info->direction);
if (info->direction == HAX_IO_OUT) {
m_ioMapper->MMIOWrite(info->gpa, (uint32_t)info->value, info->size);
}
else {
m_ioMapper->MMIORead(info->gpa, (uint32_t*)&info->value, info->size);
}
}
else {
log_warning("Two-way fast MMIO unimplemented! address1: 0x%08x address2: 0x%08x size: %d\n", info->gpa, info->gpa2, info->size);
// TODO: handle MMIO between two physical addresses
// info->gpa: physical address to read from
// info->gpa2: physical address to write to
// info->size: number of bytes to read/write
// HAX API v4 supports transferring data between two MMIO addresses,
// info->gpa and info->gpa2 (instructions such as MOVS require this):
// info->direction == 2: gpa ==> gpa2
uint64_t value;
//cpu_physical_memory_rw(info->gpa, (uint8_t *)&value, info->size, 0);
//cpu_physical_memory_rw(info->gpa2, (uint8_t *)&value, info->size, 1);
uint32_t value;
m_ioMapper->MMIORead(info->gpa, &value, info->size);
m_ioMapper->MMIOWrite(info->gpa2, value, info->size);
}
return 0;
}

View file

@ -21,7 +21,8 @@ Cpu::~Cpu() {
// ----- Basic CPU operations -------------------------------------------------
int Cpu::Initialize() {
int Cpu::Initialize(IOMapper *ioMapper) {
m_ioMapper = ioMapper;
for (uint8_t i = 0; i < 0x40; i++) {
m_skippedInterrupts[i] = 0;
}

View file

@ -6,6 +6,7 @@
#include "openxbox/memregion.h"
#include "openxbox/gdt.h"
#include "openxbox/idt.h"
#include "openxbox/iomapper.h"
namespace openxbox {
@ -148,7 +149,7 @@ public:
/*!
* Initializes the CPU.
*/
int Initialize();
int Initialize(IOMapper *ioMapper);
/*!
* Runs the CPU until interrupted.
@ -400,6 +401,11 @@ protected:
*/
struct CpuExitInfo m_exitInfo;
/*!
* The I/O mapper that handles I/O and MMIO for the CPU.
*/
IOMapper *m_ioMapper;
/*!
* Allows the implementation to do further initialization.
*/
@ -420,8 +426,8 @@ protected:
* Sends an interrupt to the CPU.
*/
virtual InterruptResult InterruptImpl(uint8_t vector) = 0;
protected:
/*!
/*!
* Keeps track of how many interrupts were skipped because they were
* already enqueued.
*/

View file

@ -0,0 +1,20 @@
#pragma once
#include <cstdint>
namespace openxbox {
/*!
* Maps I/O and MMIO reads and writes to the corresponding devices.
*/
class IOMapper {
public:
virtual void IORead(uint32_t addr, uint32_t *value, uint16_t size) = 0;
virtual void IOWrite(uint32_t addr, uint32_t value, uint16_t size) = 0;
virtual void MMIORead(uint32_t addr, uint32_t *value, uint8_t size) = 0;
virtual void MMIOWrite(uint32_t addr, uint32_t value, uint8_t size) = 0;
};
}