mirror of
https://github.com/StrikerX3/StrikeBox.git
synced 2024-06-22 06:12:30 -04:00
Basic hardware devices implementation
The emulator does something now, but seems to hang during initialization. Needs further investigation
This commit is contained in:
parent
7b1a533b20
commit
79e3790f90
|
@ -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;
|
||||
|
|
32
src/core/openxbox/hw/defs.h
Normal file
32
src/core/openxbox/hw/defs.h
Normal 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;
|
||||
|
||||
}
|
51
src/core/openxbox/hw/eeprom.cpp
Normal file
51
src/core/openxbox/hw/eeprom.cpp
Normal 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);
|
||||
}
|
||||
|
||||
}
|
30
src/core/openxbox/hw/eeprom.h
Normal file
30
src/core/openxbox/hw/eeprom.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
59
src/core/openxbox/hw/led.h
Normal file
59
src/core/openxbox/hw/led.h
Normal 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;
|
||||
|
||||
}
|
||||
}
|
55
src/core/openxbox/hw/mcpx.cpp
Normal file
55
src/core/openxbox/hw/mcpx.cpp
Normal 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
|
||||
}
|
||||
|
||||
}
|
43
src/core/openxbox/hw/mcpx.h
Normal file
43
src/core/openxbox/hw/mcpx.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
510
src/core/openxbox/hw/nvnet.cpp
Normal file
510
src/core/openxbox/hw/nvnet.cpp
Normal 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
|
||||
}
|
||||
|
||||
}
|
21
src/core/openxbox/hw/nvnet.h
Normal file
21
src/core/openxbox/hw/nvnet.h
Normal 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);
|
||||
};
|
||||
|
||||
}
|
102
src/core/openxbox/hw/pci.cpp
Normal file
102
src/core/openxbox/hw/pci.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
87
src/core/openxbox/hw/pci.h
Normal file
87
src/core/openxbox/hw/pci.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
124
src/core/openxbox/hw/pcibus.cpp
Normal file
124
src/core/openxbox/hw/pcibus.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
51
src/core/openxbox/hw/pcibus.h
Normal file
51
src/core/openxbox/hw/pcibus.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
7
src/core/openxbox/hw/sm.cpp
Normal file
7
src/core/openxbox/hw/sm.cpp
Normal file
|
@ -0,0 +1,7 @@
|
|||
#include "sm.h"
|
||||
|
||||
namespace openxbox {
|
||||
|
||||
|
||||
|
||||
}
|
24
src/core/openxbox/hw/sm.h
Normal file
24
src/core/openxbox/hw/sm.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
223
src/core/openxbox/hw/smbus.cpp
Normal file
223
src/core/openxbox/hw/smbus.cpp
Normal 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) {
|
||||
|
||||
}
|
||||
|
||||
}
|
82
src/core/openxbox/hw/smbus.h
Normal file
82
src/core/openxbox/hw/smbus.h
Normal 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;
|
||||
};
|
||||
|
||||
}
|
160
src/core/openxbox/hw/smc.cpp
Normal file
160
src/core/openxbox/hw/smc.cpp
Normal 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
115
src/core/openxbox/hw/smc.h
Normal 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] = {};
|
||||
};
|
||||
|
||||
|
||||
}
|
29
src/core/openxbox/hw/tvenc.cpp
Normal file
29
src/core/openxbox/hw/tvenc.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
20
src/core/openxbox/hw/tvenc.h
Normal file
20
src/core/openxbox/hw/tvenc.h
Normal 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);
|
||||
|
||||
}
|
|
@ -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)
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
20
src/cpu-module/openxbox/iomapper.h
Normal file
20
src/cpu-module/openxbox/iomapper.h
Normal 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;
|
||||
};
|
||||
|
||||
|
||||
}
|
Loading…
Reference in a new issue