Implemented IDE DMA transfers

Prefer references over pointers where applicable
This commit is contained in:
StrikerX3 2018-12-05 23:33:58 -02:00
parent bac54bfff3
commit 3f1a12a965
34 changed files with 682 additions and 178 deletions

View file

@ -18,7 +18,7 @@ namespace vixen {
namespace hw {
namespace ata {
ATA::ATA(IRQHandler *irqHandler) {
ATA::ATA(IRQHandler& irqHandler) {
m_channels[0] = new ATAChannel(ChanPrimary, irqHandler, kPrimaryIRQ);
m_channels[1] = new ATAChannel(ChanSecondary, irqHandler, kSecondaryIRQ);
}

View file

@ -24,7 +24,7 @@ namespace ata {
class ATA : public IODevice {
public:
ATA(IRQHandler *irqHandler);
ATA(IRQHandler& irqHandler);
virtual ~ATA();
void Reset();

View file

@ -18,7 +18,7 @@ namespace vixen {
namespace hw {
namespace ata {
ATAChannel::ATAChannel(Channel channel, IRQHandler *irqHandler, uint8_t irqNum)
ATAChannel::ATAChannel(Channel channel, IRQHandler& irqHandler, uint8_t irqNum)
: m_channel(channel)
, m_irqHandler(irqHandler)
, m_irqNum(irqNum)
@ -49,7 +49,7 @@ bool ATAChannel::ReadCommandPort(Register reg, uint32_t *value, uint8_t size) {
}
// Check that there is an attached device
bool attached = m_devs[GetSelectedDeviceIndex()]->IsAttached();
bool attached = m_devs[m_regs.GetSelectedDeviceIndex()]->IsAttached();
// If there is no attached device and the guest is attempting to read from
// a register other than Status, return 0
@ -141,9 +141,58 @@ bool ATAChannel::WriteControlPort(uint32_t value, uint8_t size) {
return false;
}
bool ATAChannel::ReadDMA(uint8_t dstBuffer[kSectorSize]) {
// Delegate to selected device
auto devIndex = m_regs.GetSelectedDeviceIndex();
auto dev = m_devs[devIndex];
return dev->ReadDMA(dstBuffer);
}
bool ATAChannel::WriteDMA(uint8_t srcBuffer[kSectorSize]) {
// Delegate to selected device
auto devIndex = m_regs.GetSelectedDeviceIndex();
auto dev = m_devs[devIndex];
return dev->WriteDMA(srcBuffer);
}
bool ATAChannel::IsDMAFinished() {
// Delegate to selected device
auto devIndex = m_regs.GetSelectedDeviceIndex();
auto dev = m_devs[devIndex];
return dev->IsDMAFinished();
}
bool ATAChannel::EndDMA() {
// Check if the DMA operation finished successfully
auto devIndex = m_regs.GetSelectedDeviceIndex();
auto dev = m_devs[devIndex];
// Handle result according to DMA command protocol [9.10]
if (dev->IsDMAFinished()) {
log_spew("ATAChannel::EndDMA: Operation finished successfully on channel %d\n", m_channel);
}
else {
log_spew("ATAChannel::EndDMA: Operation failed on channel %d\n", m_channel);
// Set error status
m_regs.status |= StError;
}
// Tell device that the DMA operation has finished
dev->EndDMA();
// Clear BSY=0 and DRQ=0
m_regs.status &= ~(StBusy | StDataRequest);
// Assert INTRQ if nIEN=0
// INTRQ will be negated when the host reads the Status register
SetInterrupt(true);
return m_regs.AreInterruptsEnabled();
}
void ATAChannel::ReadData(uint32_t *value, uint8_t size) {
// Read from device buffer
auto devIndex = GetSelectedDeviceIndex();
auto devIndex = m_regs.GetSelectedDeviceIndex();
auto dev = m_devs[devIndex];
// Clear the destination value before reading from the buffer, in case
@ -171,7 +220,7 @@ void ATAChannel::ReadStatus(uint8_t *value) {
}
void ATAChannel::WriteData(uint32_t value, uint8_t size) {
log_warning("ATAChannel::WriteData: Unimplemented! (channel = %d device = %d size = %d, value = 0x%x)\n", m_channel, GetSelectedDeviceIndex(), size, value);
log_warning("ATAChannel::WriteData: Unimplemented! (channel = %d device = %d size = %d, value = 0x%x)\n", m_channel, m_regs.GetSelectedDeviceIndex(), size, value);
}
void ATAChannel::WriteCommand(uint8_t value) {
@ -185,7 +234,7 @@ void ATAChannel::WriteCommand(uint8_t value) {
// Determine which protocol is used by the command and collect all data needed for execution
auto protocol = kCmdProtocols.at(cmd);
auto devIndex = GetSelectedDeviceIndex();
auto devIndex = m_regs.GetSelectedDeviceIndex();
auto dev = m_devs[devIndex];
bool succeeded;
@ -204,6 +253,9 @@ void ATAChannel::WriteCommand(uint8_t value) {
case CmdReadDMA:
succeeded = dev->BeginReadDMA();
break;
case CmdWriteDMA:
succeeded = dev->BeginWriteDMA();
break;
default:
log_warning("ATAChannel::WriteCommand: Unhandled command 0x%x for channel %d, device %d\n", cmd, m_channel, devIndex);
succeeded = false;
@ -226,18 +278,16 @@ void ATAChannel::WriteCommand(uint8_t value) {
if (!succeeded || protocol.assertINTRQOnSuccess) {
// INTRQ is not asserted if nIEN=1
// INTRQ will be negated when the host reads the Status register
if (AreInterruptsEnabled()) {
SetInterrupt(true);
}
SetInterrupt(true);
}
}
}
void ATAChannel::SetInterrupt(bool asserted) {
if (asserted != m_interrupt && AreInterruptsEnabled()) {
if (asserted != m_interrupt && m_regs.AreInterruptsEnabled()) {
m_interrupt = asserted;
m_irqHandler->HandleIRQ(m_irqNum, m_interrupt);
m_irqHandler.HandleIRQ(m_irqNum, m_interrupt);
}
}

View file

@ -29,7 +29,7 @@ namespace ata {
*/
class ATAChannel {
public:
ATAChannel(Channel channel, IRQHandler *irqHandler, uint8_t irqNum);
ATAChannel(Channel channel, IRQHandler& irqHandler, uint8_t irqNum);
~ATAChannel();
ATADevice& GetDevice(uint8_t deviceIndex) { return *m_devs[deviceIndex]; }
@ -42,6 +42,13 @@ public:
bool ReadControlPort(uint32_t *value, uint8_t size);
bool WriteControlPort(uint32_t value, uint8_t size);
// ----- DMA transfers ----------------------------------------------------
bool ReadDMA(uint8_t dstBuffer[kSectorSize]);
bool WriteDMA(uint8_t srcBuffer[kSectorSize]);
bool IsDMAFinished();
bool EndDMA();
private:
friend class ATA;
@ -63,7 +70,7 @@ private:
void SetInterrupt(bool asserted);
IRQHandler *m_irqHandler;
IRQHandler& m_irqHandler;
uint8_t m_irqNum;
// ----- Command port operations ------------------------------------------
@ -73,14 +80,6 @@ private:
void WriteData(uint32_t value, uint8_t size);
void WriteCommand(uint8_t value);
// ----- Utility functions ------------------------------------------------
// Retrieves the index of the currently selected device from bit 4
// (DEV - Device select) of the Device/Head register [7.10.6]
inline uint8_t GetSelectedDeviceIndex() const { return (m_regs.deviceHead >> kDevSelectorBit) & 1; }
inline bool AreInterruptsEnabled() const { return (m_regs.control & DevCtlNegateInterruptEnable) == 0; }
};
}

View file

@ -34,6 +34,15 @@ struct ATARegisters {
uint16_t cylinder = 0;
uint8_t deviceHead = 0;
uint8_t control = 0;
// ----- Utility functions ------------------------------------------------
// Retrieves the index of the currently selected device from bit 4
// (DEV - Device select) of the Device/Head register [7.10.6]
inline uint8_t GetSelectedDeviceIndex() const { return (deviceHead >> kDevSelectorBit) & 1; }
inline bool AreInterruptsEnabled() const { return (control & DevCtlNegateInterruptEnable) == 0; }
};
}

View file

@ -45,6 +45,317 @@ uint32_t ATADevice::GetRemainingBufferLength() {
return kSectorSize - m_dataBufferPos;
}
bool ATADevice::BeginReadDMA() {
// [8.23.7] As a prerequisite, DRDY must be set equal to one
if ((m_regs.status & StReady) == 0) {
return false;
}
// Sanity check: don't start a DMA transfer while another transfer is running
if (m_transferActive) {
return false;
}
// Determine if we're using LBA address or CHS numbers
bool useLBA = (m_regs.deviceHead & DevHeadDMALBA) != 0;
// Read address accordingly and validate parameters
if (useLBA) {
m_dma_startingLBA = ((m_regs.deviceHead & 0b1111) << 24) | (m_regs.cylinder << 8) | (m_regs.sectorNumber);
}
else {
// Convert from CHS to LBA
uint16_t cylinder = m_regs.cylinder;
uint8_t head = m_regs.deviceHead & 0b1111;
uint8_t sector = m_regs.sectorNumber;
m_dma_startingLBA = m_driver->CHSToLBA(cylinder, head, sector);
}
// Calculate ending LBA
if (m_regs.sectorCount == 0) {
m_dma_endingLBA = m_dma_startingLBA + 256;
}
else {
m_dma_endingLBA = m_dma_startingLBA + m_regs.sectorCount;
}
if (m_driver->IsLBAAddressUserAccessible(m_dma_startingLBA)) {
m_transferActive = true;
m_dma_isWrite = false;
m_dma_currentLBA = m_dma_startingLBA;
return true;
}
return false;
}
bool ATADevice::ReadDMA(uint8_t dstBuffer[kSectorSize]) {
// [8.23.7] As a prerequisite, DRDY must be set equal to one
if ((m_regs.status & StReady) == 0) {
return false;
}
bool succeeded = HandleReadDMA(dstBuffer);
if (succeeded) {
// Handle normal output as specified in [8.23.5]
// Device/Head register:
// "DEV shall indicate the selected device."
// Not necessary, but the spec says so
m_regs.deviceHead = (m_regs.deviceHead & ~(1 << kDevSelectorBit)) | (m_devIndex << kDevSelectorBit);
// Status register:
// "BSY shall be cleared to zero indicating command completion."
// Already handled by the caller
// "DRDY shall be set to one."
m_regs.status |= StReady;
// "DF(Device Fault) shall be cleared to zero."
// "DRQ shall be cleared to zero."
// "ERR shall be cleared to zero."
m_regs.status &= ~(StDeviceFault | StDataRequest | StError);
}
else {
// Handle error output as specified in [8.23.6]
// Error register is handled by the HandleReadDMA function
// Sector Number, Cylinder Low, Cylinder High, Device/Head:
// "shall be written with the address of first unrecoverable error."
m_driver->LBAToCHS(m_dma_currentLBA, &m_regs.cylinder, &m_regs.sectorNumber, &m_regs.deviceHead);
// Device/Head register:
// "DEV shall indicate the selected device."
// Not necessary, but the spec says so
m_regs.deviceHead = (m_regs.deviceHead & ~(1 << kDevSelectorBit)) | (m_devIndex << kDevSelectorBit);
// Status register:
// "BSY shall be cleared to zero indicating command completion."
// Already handled by the caller
// "DRDY shall be set to one."
m_regs.status |= StReady;
// "DF (Device Fault) shall be set to one if a device fault has occurred."
// Handled by HandleReadDMA
// "DRQ shall be cleared to zero."
m_regs.status &= ~StDataRequest;
// "ERR shall be set to one if an Error register bit is set to one."
if (m_regs.error) {
m_regs.status |= StError;
}
}
return succeeded;
}
bool ATADevice::HandleReadDMA(uint8_t dstBuffer[kSectorSize]) {
// Sanity check: don't read while a DMA write is in progress
if (m_dma_isWrite) {
m_regs.status |= StDeviceFault;
return false;
}
// Done reading
if (IsDMAFinished()) {
return false;
}
// If the device uses removable media, check if it has media
// TODO: implement removable media functions in the driver
/*if (m_driver->IsRemovableMedia() && !m_driver->HasMedia()) {
// [8.23.6]: "NM shall be set to one if no media is present in a removable media device."
m_regs.error |= ErrDMANoMedia;
return false;
}*/
// Check that the removable media was not changed while the operation is in progress
// TODO: implement removable media functions in the driver
/*if (m_driver->MediaChangeRequested()) {
// [8.23.6]: "MCR shall be set to one if a media change request has been detected by a removable media device."
m_regs.error |= ErrDMAMediaChangeRequest;
return false;
}*/
// Check that the next sector is accessible
if (!m_driver->IsLBAAddressUserAccessible(m_dma_currentLBA)) {
// [8.23.6]: "IDNF shall be set to one if a user-accessible address could not be found"
m_regs.error |= ErrDMADataNotFound;
return false;
}
// Try to read next sector
if (!m_driver->ReadSector(m_dma_currentLBA, dstBuffer)) {
m_regs.status |= StDeviceFault;
return false;
}
m_dma_currentLBA++;
return true;
}
bool ATADevice::BeginWriteDMA() {
// [8.45.7] As a prerequisite, DRDY must be set equal to one
if ((m_regs.status & StReady) == 0) {
return false;
}
// Sanity check: don't start a DMA transfer while another transfer is running
if (m_transferActive) {
return false;
}
// Determine if we're using LBA address or CHS numbers
bool useLBA = (m_regs.deviceHead & DevHeadDMALBA) != 0;
// Read address accordingly and validate parameters
if (useLBA) {
m_dma_startingLBA = ((m_regs.deviceHead & 0b1111) << 24) | (m_regs.cylinder << 8) | (m_regs.sectorNumber);
}
else {
// Convert from CHS to LBA
uint16_t cylinder = m_regs.cylinder;
uint8_t head = m_regs.deviceHead & 0b1111;
uint8_t sector = m_regs.sectorNumber;
m_dma_startingLBA = m_driver->CHSToLBA(cylinder, head, sector);
}
// Calculate ending LBA
if (m_regs.sectorCount == 0) {
m_dma_endingLBA = m_dma_startingLBA + 256;
}
else {
m_dma_endingLBA = m_dma_startingLBA + m_regs.sectorCount;
}
if (m_driver->IsLBAAddressUserAccessible(m_dma_startingLBA)) {
m_transferActive = true;
m_dma_isWrite = true;
m_dma_currentLBA = m_dma_startingLBA;
return true;
}
return false;
}
bool ATADevice::WriteDMA(uint8_t srcBuffer[kSectorSize]) {
// [8.45.7] As a prerequisite, DRDY must be set equal to one
if ((m_regs.status & StReady) == 0) {
return false;
}
bool succeeded = HandleWriteDMA(srcBuffer);
if (succeeded) {
// Handle normal output as specified in [8.45.5]
// Device/Head register:
// "DEV shall indicate the selected device."
// Not necessary, but the spec says so
m_regs.deviceHead = (m_regs.deviceHead & ~(1 << kDevSelectorBit)) | (m_devIndex << kDevSelectorBit);
// Status register:
// "BSY shall be cleared to zero indicating command completion."
// Already handled by the caller
// "DRDY shall be set to one."
m_regs.status |= StReady;
// "DF(Device Fault) shall be cleared to zero."
// "DRQ shall be cleared to zero."
// "ERR shall be cleared to zero."
m_regs.status &= ~(StDeviceFault | StDataRequest | StError);
}
else {
// Handle error output as specified in [8.45.6]
// Error register is handled by the HandleWriteDMA function
// Sector Number, Cylinder Low, Cylinder High, Device/Head:
// "shall be written with the address of first unrecoverable error."
m_driver->LBAToCHS(m_dma_currentLBA, &m_regs.cylinder, &m_regs.sectorNumber, &m_regs.deviceHead);
// Device/Head register:
// "DEV shall indicate the selected device."
// Not necessary, but the spec says so
m_regs.deviceHead = (m_regs.deviceHead & ~(1 << kDevSelectorBit)) | (m_devIndex << kDevSelectorBit);
// Status register:
// "BSY shall be cleared to zero indicating command completion."
// Already handled by the caller
// "DRDY shall be set to one."
m_regs.status |= StReady;
// "DF (Device Fault) shall be set to one if a device fault has occurred."
// Handled by HandleReadDMA
// "DRQ shall be cleared to zero."
m_regs.status &= ~StDataRequest;
// "ERR shall be set to one if an Error register bit is set to one."
if (m_regs.error) {
m_regs.status |= StError;
}
}
return succeeded;
}
bool ATADevice::HandleWriteDMA(uint8_t srcBuffer[kSectorSize]) {
// Sanity check: don't write while a DMA read is in progress
if (!m_dma_isWrite) {
m_regs.status |= StDeviceFault;
return false;
}
// Done writing
if (IsDMAFinished()) {
return false;
}
// If the device uses removable media, check if it has media
// TODO: implement removable media functions in the driver
/*if (m_driver->IsRemovableMedia() && !m_driver->HasMedia()) {
// [8.23.6]: "NM shall be set to one if no media is present in a removable media device."
m_regs.error |= ErrDMANoMedia;
return false;
}*/
// Check that the removable media was not changed while the operation is in progress
// TODO: implement removable media functions in the driver
/*if (m_driver->MediaChangeRequested()) {
// [8.23.6]: "MCR shall be set to one if a media change request has been detected by a removable media device."
m_regs.error |= ErrDMAMediaChangeRequest;
return false;
}*/
// Check that the next sector is accessible
if (!m_driver->IsLBAAddressUserAccessible(m_dma_currentLBA)) {
// [8.23.6]: "IDNF shall be set to one if a user-accessible address could not be found"
m_regs.error |= ErrDMADataNotFound;
return false;
}
// Try to write next sector
if (!m_driver->WriteSector(m_dma_currentLBA, srcBuffer)) {
m_regs.status |= StDeviceFault;
return false;
}
m_dma_currentLBA++;
return true;
}
void ATADevice::EndDMA() {
m_transferActive = false;
}
bool ATADevice::IdentifyDevice() {
// [8.12.7] As a prerequisite, DRDY must be set equal to one
if ((m_regs.status & StReady) == 0) {
@ -76,44 +387,11 @@ bool ATADevice::IdentifyDevice() {
// "DF (Device Fault) shall be cleared to zero."
// "DRQ shall be cleared to zero."
// "ERR shall be cleared to zero."
m_regs.status &= ~(StBit5 | StDataRequest | StError);
m_regs.status &= ~(StDeviceFault | StDataRequest | StError);
return true;
}
bool ATADevice::BeginReadDMA() {
// Sanity check: don't start a DMA transfer while another transfer is running
if (m_transferActive) {
return false;
}
// Get sector count
m_dma_sectorCount = m_regs.sectorCount;
// Determine if we're using LBA address or CHS numbers
m_dma_useLBA = (m_regs.deviceHead & DevHeadDMALBA) != 0;
// Read address accordingly and validate parameters
if (m_dma_useLBA) {
m_dma_lbaAddress = ((m_regs.deviceHead & 0b1111) << 24) | (m_regs.cylinder << 8) | (m_regs.sectorNumber);
if (m_driver->IsLBAAddressUserAccessible(m_dma_lbaAddress)) {
m_transferActive = true;
return true;
}
return false;
}
else {
m_dma_cylinder = m_regs.cylinder;
m_dma_head = m_regs.deviceHead & 0b1111;
m_dma_sector = m_regs.sectorNumber;
if (m_driver->IsCHSAddressUserAccessible(m_dma_cylinder, m_dma_head, m_dma_sector)) {
m_transferActive = true;
return true;
}
return false;
}
}
bool ATADevice::SetFeatures() {
// [8.37.4] The feature to be set is specified in the Features register
auto feature = m_regs.features;

View file

@ -34,20 +34,28 @@ public:
// ----- Device driver management -----------------------------------------
bool IsAttached() const { return m_driver->IsAttached(); }
void SetDeviceDriver(IATADeviceDriver *driver) { m_driver = driver; }
bool IsAttached() const { return m_driver->IsAttached(); }
// ----- PIO data buffer --------------------------------------------------
uint32_t ReadBuffer(uint8_t *dest, uint32_t length);
uint32_t GetRemainingBufferLength();
// ----- DMA transfer -----------------------------------------------------
bool ReadDMA(uint8_t dstBuffer[kSectorSize]);
bool WriteDMA(uint8_t srcBuffer[kSectorSize]);
bool IsDMAFinished() { return m_dma_currentLBA >= m_dma_endingLBA; }
void EndDMA();
// ----- Command handlers -------------------------------------------------
// These functions must return false on error
bool IdentifyDevice(); // [8.12] 0xEC Identify Device
bool BeginReadDMA(); // [8.23] 0xC8 Read DMA
bool SetFeatures(); // [8.37] 0xEF Set Features
bool BeginWriteDMA(); // [8.45] 0xCA Write DMA
// ----- Set Features subcommand handlers ---------------------------------
@ -76,23 +84,28 @@ private:
DMATransferType m_dmaTransferType = XferTypeMultiWordDMA;
uint8_t m_dmaTransferMode = 0;
// ----- Data buffer ------------------------------------------------------
// ----- Data buffer (for PIO transfers) ----------------------------------
uint8_t m_dataBuffer[kSectorSize];
uint32_t m_dataBufferPos;
// ----- State ------------------------------------------------------------
// true if any transfer is in progress (PIO or DMA)
bool m_transferActive;
// ----- DMA transfer parameters ------------------------------------------
// ----- DMA transfer -----------------------------------------------------
uint8_t m_dma_sectorCount; // Total number of sectors to transfer (0x00 = 1 sector and 0xFF = 256 sectors)
bool m_dma_useLBA; // If true, use LBA parameter; if false, use CHS parameters
uint32_t m_dma_lbaAddress; // Starting LBA address
uint16_t m_dma_cylinder; // Starting cylinder number
uint8_t m_dma_sector; // Starting sector number
uint8_t m_dma_head; // Starting head number
// Parameters
uint32_t m_dma_startingLBA;
uint32_t m_dma_endingLBA; // Exclusive
bool m_dma_isWrite; // Current DMA operation type (true = write, false = read), used for sanity check
// State
uint8_t m_dma_currentLBA;
bool HandleReadDMA(uint8_t dstBuffer[kSectorSize]);
bool HandleWriteDMA(uint8_t srcBuffer[kSectorSize]);
};
}

View file

@ -84,7 +84,7 @@ const uint8_t kRegSizes[] = {
enum StatusBits : uint8_t {
StBusy = (1 << 7), // [7.15.6.1] (BSY) The device is busy
StReady = (1 << 6), // [7.15.6.2] (DRDY) The device is ready to accept commands
StBit5 = (1 << 5), // [7.15.6.3] Command dependent bit
StDeviceFault = (1 << 5), // [--------] (DF) Device fault ([7.15.6.3] says that this is a command dependent bit)
StBit4 = (1 << 4), // [7.15.6.3] Command dependent bit
StDataRequest = (1 << 3), // [7.15.6.4] (DRQ) The device is ready to transfer a word of data
StError = (1 << 0), // [7.15.6.6] (ERR) An error occurred during execution of the previous command
@ -156,6 +156,7 @@ enum Command : uint8_t {
CmdReadDMA = 0xC8, // [8.23] Read DMA
CmdSecurityUnlock = 0xF2, // [8.34] Security Unlock
CmdSetFeatures = 0xEF, // [8.37] Set Features
CmdWriteDMA = 0xCA, // [8.45] Write DMA
};
// [8.37.8] Set Features subcommands (specified in the Features register)
@ -191,6 +192,7 @@ const std::unordered_map<Command, const CommandProtocol&, std::hash<uint8_t>> kC
{ CmdReadDMA, kCmdProtoDMA },
{ CmdSecurityUnlock, kCmdProtoPIODataOut },
{ CmdSetFeatures, kCmdProtoNonData },
{ CmdWriteDMA, kCmdProtoDMA },
};
// --- Command data -------------------------------------------------------------------------------

View file

@ -29,10 +29,22 @@ namespace ata {
class IATADeviceDriver {
public:
virtual ~IATADeviceDriver();
virtual bool IsAttached() = 0;
// ----- ATA commands -----------------------------------------------------
virtual void IdentifyDevice(IdentifyDeviceData *data) = 0;
// ----- Sector access ----------------------------------------------------
virtual bool ReadSector(uint32_t lbaAddress, uint8_t buffer[kSectorSize]) = 0;
virtual bool WriteSector(uint32_t lbaAddress, uint8_t buffer[kSectorSize]) = 0;
// ----- Utility functions ------------------------------------------------
virtual bool IsAttached() = 0;
virtual bool IsLBAAddressUserAccessible(uint32_t lbaAddress) = 0;
virtual bool IsCHSAddressUserAccessible(uint16_t cylinder, uint8_t sector, uint8_t head) = 0;
virtual uint32_t CHSToLBA(uint32_t cylinder, uint8_t head, uint8_t sector) = 0;
virtual void LBAToCHS(uint32_t lbaAddress, uint16_t *cylinder, uint8_t *head, uint8_t *sector) = 0;
};
}

View file

@ -37,7 +37,6 @@ DummyHardDriveATADeviceDriver::DummyHardDriveATADeviceDriver()
, m_numHeadsPerCylinder(16)
, m_numSectorsPerTrack(63)
{
}
DummyHardDriveATADeviceDriver::~DummyHardDriveATADeviceDriver() {
@ -89,19 +88,36 @@ void DummyHardDriveATADeviceDriver::IdentifyDevice(IdentifyDeviceData *data) {
data->securityStatus |= IDSecStatusSupported | IDSecStatusEnabled/* | IDSecStatusLocked*/;
}
bool DummyHardDriveATADeviceDriver::ReadSector(uint32_t lbaAddress, uint8_t destBuffer[kSectorSize]) {
// Fill with zeros, as if the disk was blank
memset(destBuffer, 0, kSectorSize);
// Always succeed
return true;
}
bool DummyHardDriveATADeviceDriver::WriteSector(uint32_t lbaAddress, uint8_t destBuffer[kSectorSize]) {
// Lie about writing, always succeed
return true;
}
bool DummyHardDriveATADeviceDriver::IsLBAAddressUserAccessible(uint32_t lbaAddress) {
uint32_t maxLBAAddress = m_numCylinders * m_numHeadsPerCylinder * m_numSectorsPerTrack;
return lbaAddress <= maxLBAAddress;
}
bool DummyHardDriveATADeviceDriver::IsCHSAddressUserAccessible(uint16_t cylinder, uint8_t head, uint8_t sector) {
if (cylinder > m_numCylinders) {
return false;
}
if (head > m_numHeadsPerCylinder) {
return false;
}
return sector > m_numSectorsPerTrack;
uint32_t DummyHardDriveATADeviceDriver::CHSToLBA(uint32_t cylinder, uint8_t head, uint8_t sector) {
return ((cylinder * m_numHeadsPerCylinder) + head) * m_numSectorsPerTrack + sector;
}
void DummyHardDriveATADeviceDriver::LBAToCHS(uint32_t lbaAddress, uint16_t *cylinder, uint8_t *head, uint8_t *sector) {
*sector = lbaAddress % m_numSectorsPerTrack;
lbaAddress /= m_numSectorsPerTrack;
*head = lbaAddress % m_numHeadsPerCylinder;
lbaAddress /= m_numHeadsPerCylinder;
*cylinder = lbaAddress;
}
}

View file

@ -26,10 +26,22 @@ class DummyHardDriveATADeviceDriver : public IATADeviceDriver {
public:
DummyHardDriveATADeviceDriver();
~DummyHardDriveATADeviceDriver() override;
bool IsAttached() override { return true; }
// ----- ATA commands -----------------------------------------------------
void IdentifyDevice(IdentifyDeviceData *data) override;
// ----- Sector access ----------------------------------------------------
bool ReadSector(uint32_t lbaAddress, uint8_t destBuffer[kSectorSize]) override;
bool WriteSector(uint32_t lbaAddress, uint8_t destBuffer[kSectorSize]) override;
// ----- Utility functions ------------------------------------------------
bool IsAttached() override { return true; }
bool IsLBAAddressUserAccessible(uint32_t lbaAddress) override;
bool IsCHSAddressUserAccessible(uint16_t cylinder, uint8_t head, uint8_t sector) override;
uint32_t CHSToLBA(uint32_t cylinder, uint8_t head, uint8_t sector) override;
void LBAToCHS(uint32_t lbaAddress, uint16_t *cylinder, uint8_t *head, uint8_t *sector) override;
private:
uint16_t m_numCylinders;

View file

@ -25,10 +25,22 @@ namespace ata {
class NullATADeviceDriver : public IATADeviceDriver {
public:
~NullATADeviceDriver() override;
bool IsAttached() override { return true; }
// ----- ATA commands -----------------------------------------------------
void IdentifyDevice(IdentifyDeviceData *data) override;
bool IsLBAAddressUserAccessible(uint32_t lbaAddress) { return false; }
bool IsCHSAddressUserAccessible(uint16_t cylinder, uint8_t head, uint8_t sector) { return false; }
// ----- Sector access ----------------------------------------------------
bool ReadSector(uint32_t lbaAddress, uint8_t destBuffer[kSectorSize]) override { return false; }
bool WriteSector(uint32_t lbaAddress, uint8_t destBuffer[kSectorSize]) override { return false; }
// ----- Utility functions ------------------------------------------------
bool IsAttached() override { return true; }
bool IsLBAAddressUserAccessible(uint32_t lbaAddress) override { return false; }
uint32_t CHSToLBA(uint32_t cylinder, uint8_t head, uint8_t sector) override { return 0; }
void LBAToCHS(uint32_t lbaAddress, uint16_t *cylinder, uint8_t *head, uint8_t *sector) override {}
};
extern NullATADeviceDriver g_nullATADeviceDriver;

View file

@ -44,7 +44,7 @@ static uint32_t i8254ThreadFunc(void *data) {
return 0;
}
i8254::i8254(IRQHandler *irqHandler, float tickRate)
i8254::i8254(IRQHandler& irqHandler, float tickRate)
: m_irqHandler(irqHandler)
, m_tickRate(tickRate)
, m_running(false)
@ -92,12 +92,12 @@ void i8254::Run() {
m_running = true;
while (m_running) {
m_irqHandler->HandleIRQ(0, 1);
m_irqHandler.HandleIRQ(0, 1);
nextStop += interval;
std::this_thread::sleep_until(nextStop);
m_irqHandler->HandleIRQ(0, 0);
m_irqHandler.HandleIRQ(0, 0);
}
}

View file

@ -18,7 +18,7 @@ namespace vixen {
class i8254 : public IODevice {
public:
i8254(IRQHandler *irqHandler, float tickRate = 1000.0f);
i8254(IRQHandler& irqHandler, float tickRate = 1000.0f);
virtual ~i8254();
void Reset();
@ -29,7 +29,7 @@ public:
void Run();
private:
IRQHandler *m_irqHandler;
IRQHandler& m_irqHandler;
float m_tickRate;
bool m_running;

View file

@ -66,8 +66,9 @@ using namespace vixen::cpu;
// TODO: Implement ELCR support
i8259::i8259(Cpu *cpu) {
m_cpu = cpu;
i8259::i8259(Cpu& cpu)
: m_cpu(cpu)
{
}
i8259::~i8259() {
@ -436,7 +437,7 @@ void i8259::UpdateIRQ(int pic) {
// If this was the master PIC, trigger the IRQ
if (pic == PIC_MASTER && m_InterruptOutput[PIC_MASTER]) {
m_cpu->Interrupt(GetCurrentIRQ());
m_cpu.Interrupt(GetCurrentIRQ());
}
}

View file

@ -28,7 +28,7 @@ using namespace vixen::cpu;
class i8259 : public IODevice, public IRQHandler {
public:
i8259(Cpu *cpu);
i8259(Cpu& cpu);
virtual ~i8259();
void Reset();
@ -41,7 +41,7 @@ public:
int GetCurrentIRQ();
private:
Cpu *m_cpu;
Cpu& m_cpu;
uint8_t m_PreviousIRR[2]; // used for edge-detection
uint8_t m_IRR[2];

View file

@ -124,7 +124,7 @@ void Serial::FifoTimeoutInterruptCB(void *userData) {
((Serial *)userData)->FifoTimeoutInterrupt();
}
Serial::Serial(IRQHandler *irqHandler, uint32_t ioBase)
Serial::Serial(IRQHandler& irqHandler, uint32_t ioBase)
: m_irqHandler(irqHandler)
, m_ioBase(ioBase)
{
@ -621,10 +621,10 @@ void Serial::UpdateIRQ() {
m_iir = tmp_iir | (m_iir & 0xF0);
if (tmp_iir != UART_IIR_NO_INT) {
m_irqHandler->HandleIRQ(m_irq, 1);
m_irqHandler.HandleIRQ(m_irq, 1);
}
else {
m_irqHandler->HandleIRQ(m_irq, 0);
m_irqHandler.HandleIRQ(m_irq, 0);
}
}

View file

@ -22,7 +22,7 @@ namespace vixen {
class Serial : public IODevice {
public:
Serial(IRQHandler *irqHandler, uint32_t ioBase);
Serial(IRQHandler& irqHandler, uint32_t ioBase);
virtual ~Serial();
bool Init(CharDriver *chr);
@ -61,7 +61,7 @@ private:
static void UpdateMSLCB(void *userData);
static void FifoTimeoutInterruptCB(void *userData);
IRQHandler *m_irqHandler;
IRQHandler& m_irqHandler;
uint32_t m_ioBase;
uint16_t m_divider = 0;

View file

@ -32,7 +32,7 @@ const static uint32_t kSerialPortIOBases[] = {
PORT_SERIAL_BASE_2
};
SuperIO::SuperIO(IRQHandler *irqHandler, CharDriver *chrs[SUPERIO_SERIAL_PORT_COUNT]) {
SuperIO::SuperIO(IRQHandler& irqHandler, CharDriver *chrs[SUPERIO_SERIAL_PORT_COUNT]) {
memset(m_configRegs, 0, sizeof(m_configRegs));
memset(m_deviceRegs, 0, sizeof(m_deviceRegs));

View file

@ -43,7 +43,7 @@ namespace vixen {
class SuperIO : public IODevice {
public:
SuperIO(IRQHandler *irqHandler, CharDriver *chrs[SUPERIO_SERIAL_PORT_COUNT]);
SuperIO(IRQHandler& irqHandler, CharDriver *chrs[SUPERIO_SERIAL_PORT_COUNT]);
virtual ~SuperIO();
void Init();

View file

@ -25,7 +25,7 @@
namespace vixen {
ISABus::ISABus(IRQ* irqs)
ISABus::ISABus(IRQ *irqs)
: m_irqs(irqs)
{
}
@ -35,7 +35,7 @@ ISABus::~ISABus() {
IRQ *ISABus::GetIRQ(uint8_t isaIRQ) {
if (isaIRQ > 15) {
log_warning("ISABus::GetIRQ: invalid ISA IRQ %u\n", isaIRQ);
log_warning("ISABus::GetIRQ: Invalid ISA IRQ %u\n", isaIRQ);
return nullptr;
}
return &m_irqs[isaIRQ];

View file

@ -8,7 +8,7 @@ namespace vixen {
class ISABus {
public:
ISABus(IRQ* irqs);
ISABus(IRQ *irqs);
~ISABus();
IRQ *GetIRQ(uint8_t isaIRQ);

View file

@ -214,7 +214,7 @@ static inline uint64_t Muldiv64(uint64_t a, uint32_t b, uint32_t c) {
#define OHCI_PAGE_MASK 0xFFFFF000
#define OHCI_OFFSET_MASK 0xFFF
OHCI::OHCI(Cpu* cpu, int Irq, USBPCIDevice* UsbObj)
OHCI::OHCI(Cpu& cpu, int Irq, USBPCIDevice* UsbObj)
: m_cpu(cpu)
{
int offset = 0;
@ -387,7 +387,7 @@ bool OHCI::OHCI_WriteHCCA(uint32_t Paddr, OHCI_HCCA* Hcca)
bool OHCI::OHCI_ReadED(uint32_t Paddr, OHCI_ED* Ed)
{
if (Paddr != 0) {
m_cpu->VMemRead(Paddr, sizeof(*Ed), Ed);
m_cpu.VMemRead(Paddr, sizeof(*Ed), Ed);
return false;
}
return true; // error
@ -398,7 +398,7 @@ bool OHCI::OHCI_WriteED(uint32_t Paddr, OHCI_ED* Ed)
if (Paddr != 0) {
// According to the standard, only the HeadP field is writable by the HC, so we'll write just that
size_t OffsetOfHeadP = offsetof(OHCI_ED, HeadP);
m_cpu->VMemWrite(Paddr, 4, Ed + OffsetOfHeadP);
m_cpu.VMemWrite(Paddr, 4, Ed + OffsetOfHeadP);
return false;
}
return true; // error
@ -407,7 +407,7 @@ bool OHCI::OHCI_WriteED(uint32_t Paddr, OHCI_ED* Ed)
bool OHCI::OHCI_ReadTD(uint32_t Paddr, OHCI_TD* Td)
{
if (Paddr != 0) {
m_cpu->VMemRead(Paddr, sizeof(*Td), Td);
m_cpu.VMemRead(Paddr, sizeof(*Td), Td);
return false;
}
return true; // error
@ -416,7 +416,7 @@ bool OHCI::OHCI_ReadTD(uint32_t Paddr, OHCI_TD* Td)
bool OHCI::OHCI_WriteTD(uint32_t Paddr, OHCI_TD* Td)
{
if (Paddr != 0) {
m_cpu->VMemWrite(Paddr, sizeof(*Td), Td);
m_cpu.VMemWrite(Paddr, sizeof(*Td), Td);
return false;
}
return true; // error
@ -424,7 +424,7 @@ bool OHCI::OHCI_WriteTD(uint32_t Paddr, OHCI_TD* Td)
bool OHCI::OHCI_ReadIsoTD(uint32_t Paddr, OHCI_ISO_TD* td) {
if (Paddr != 0) {
m_cpu->VMemRead(Paddr, sizeof(*td), td);
m_cpu.VMemRead(Paddr, sizeof(*td), td);
return false;
}
return true; // error
@ -432,7 +432,7 @@ bool OHCI::OHCI_ReadIsoTD(uint32_t Paddr, OHCI_ISO_TD* td) {
bool OHCI::OHCI_WriteIsoTD(uint32_t Paddr, OHCI_ISO_TD* td) {
if (Paddr != 0) {
m_cpu->VMemWrite(Paddr, sizeof(*td), td);
m_cpu.VMemWrite(Paddr, sizeof(*td), td);
return false;
}
return true; // error

View file

@ -148,7 +148,7 @@ public:
std::atomic_bool m_bFrameTime;
// constructor
OHCI(Cpu* cpu, int Irqn, USBPCIDevice* UsbObj);
OHCI(Cpu& cpu, int Irqn, USBPCIDevice* UsbObj);
// destructor
~OHCI() {}
// read a register
@ -157,7 +157,7 @@ public:
void OHCI_WriteRegister(uint32_t Addr, uint32_t Value);
private:
Cpu* m_cpu;
Cpu& m_cpu;
// pointer to g_USB0 or g_USB1
USBPCIDevice* m_UsbDevice = nullptr;
// all the registers available in the OHCI standard

View file

@ -12,15 +12,14 @@
namespace vixen {
using namespace hw::bmide;
using namespace hw::ata;
BMIDEDevice::BMIDEDevice(uint8_t *ram, uint32_t ramSize)
BMIDEDevice::BMIDEDevice(uint8_t *ram, uint32_t ramSize, ATA& ata)
: PCIDevice(PCI_HEADER_TYPE_NORMAL, PCI_VENDOR_ID_NVIDIA, 0x01BC, 0xD2,
0x01, 0x01, 0x8A) // IDE controller
, m_ram(ram)
, m_ramSize(ramSize)
{
m_channels[ChanPrimary] = new BMIDEChannel(ChanPrimary);
m_channels[ChanSecondary] = new BMIDEChannel(ChanSecondary);
m_channels[ChanPrimary] = new BMIDEChannel(ChanPrimary, ata.GetChannel(ChanPrimary), ram, ramSize);
m_channels[ChanSecondary] = new BMIDEChannel(ChanSecondary, ata.GetChannel(ChanSecondary), ram, ramSize);
}
BMIDEDevice::~BMIDEDevice() {

View file

@ -14,13 +14,14 @@
#include "pci.h"
#include "bmide/defs.h"
#include "bmide_channel.h"
#include "vixen/hw/ata/ata.h"
namespace vixen {
class BMIDEDevice : public PCIDevice {
public:
// constructor
BMIDEDevice(uint8_t *ram, uint32_t ramSize);
BMIDEDevice(uint8_t *ram, uint32_t ramSize, hw::ata::ATA& ata);
virtual ~BMIDEDevice();
// PCI Device functions
@ -30,13 +31,6 @@ public:
void PCIIORead(int barIndex, uint32_t port, uint32_t *value, uint8_t size) override;
void PCIIOWrite(int barIndex, uint32_t port, uint32_t value, uint8_t size) override;
private:
// ----- System memory ----------------------------------------------------
uint8_t *m_ram = nullptr;
uint32_t m_ramSize = 0;
// ----- Channels ---------------------------------------------------------
BMIDEChannel *m_channels[2];
};

View file

@ -26,7 +26,7 @@ enum Register {
};
enum CommandRegisterBits {
CmdReadWriteControl = (1 << 3), // Read/Write Control: 0 = read, 1 = write
CmdReadWriteControl = (1 << 3), // Read/Write Control: 0 = Bus Master read, 1 = Bus Master write
CmdStartStopBusMaster = (1 << 0), // Start/Stop Bus Master: 1 = start, 0 = stop (edge detected)
};
@ -38,22 +38,13 @@ enum StatusRegisterBits {
StDrive0DMACapable = (1 << 5), // Drive 0 is capable of DMA transfers
StInterrupt = (1 << 2), // Interrupt level (set to 1 when IDE raises the interrupt line, write 1 to clear)
StError = (1 << 1), // Error (set when a command fails, write 1 to clear)
StBusMasterIDEActive = (1 << 0), // Indicates ongoing transfer
StActive = (1 << 0), // Indicates ongoing transfer
};
const uint8_t kStatusRegMask = StSimplexOnly | StDrive1DMACapable | StDrive0DMACapable | StInterrupt | StError | StBusMasterIDEActive;
const uint8_t kStatusRegMask = StSimplexOnly | StDrive1DMACapable | StDrive0DMACapable | StInterrupt | StError | StActive;
const uint8_t kStatusRegWriteClearMask = StInterrupt | StError;
const uint8_t kStatusRegWriteMask = StDrive1DMACapable | StDrive0DMACapable;
// --- Channels ---------------------------------------------------------------
const uint8_t kNumChannels = 2;
enum Channel {
ChanPrimary = 0,
ChanSecondary = 1,
};
// --- Physical Region Descriptor ---------------------------------------------
// A Physical Region Descriptor (PRD) describes a range of physical memory to

View file

@ -12,9 +12,13 @@
namespace vixen {
using namespace hw::bmide;
using namespace hw::ata;
BMIDEChannel::BMIDEChannel(Channel channel)
BMIDEChannel::BMIDEChannel(Channel channel, ATAChannel& ataChannel, uint8_t *ram, uint32_t ramSize)
: m_channel(channel)
, m_ataChannel(ataChannel)
, m_ram(ram)
, m_ramSize(ramSize)
{
m_worker_running = true;
m_job_running = false;
@ -43,13 +47,13 @@ void BMIDEChannel::ReadStatus(uint32_t *value, uint8_t size) {
void BMIDEChannel::ReadPRDTableAddress(uint32_t *value, uint8_t size) {
if (size == 1) {
*(uint8_t*)value = m_prdTableAddrs;
*(uint8_t*)value = m_prdTableAddr;
}
else if (size == 2) {
*(uint16_t*)value = m_prdTableAddrs;
*(uint16_t*)value = m_prdTableAddr;
}
else {
*value = m_prdTableAddrs;
*value = m_prdTableAddr;
}
}
@ -83,39 +87,33 @@ void BMIDEChannel::WritePRDTableAddress(uint32_t value, uint8_t size) {
// Update register value
if (size == 1) {
m_prdTableAddrs = (uint8_t)value;
m_prdTableAddr = (uint8_t)value;
}
else if (size == 2) {
m_prdTableAddrs = (uint16_t)value;
m_prdTableAddr = (uint16_t)value;
}
else {
m_prdTableAddrs = value;
m_prdTableAddr = value;
}
log_spew("BMIDEChannel::WritePRDTableAddress: channel = %d, address = 0x%x\n", m_channel, m_prdTableAddrs);
log_spew("BMIDEChannel::WritePRDTableAddress: channel = %d, address = 0x%x\n", m_channel, m_prdTableAddr);
}
void BMIDEChannel::BeginWork() {
if (m_job_running) {
log_warning("BMIDEChannel::BeginWork: Attempted to start operation on channel = %d while an operation is already in progress!\n", m_channel);
return;
}
log_spew("BMIDEChannel::BeginWork: Starting operation on channel %d\n", m_channel);
m_status |= StActive;
// Prepare job and notify worker
m_job_running = true;
m_job_cancel = false;
m_jobCond.notify_one();
}
void BMIDEChannel::StopWork() {
if (!m_job_running) {
log_warning("BMIDEChannel::StopWork: Attempted to stop operation on channel %d while no operation is in progress!\n", m_channel);
}
log_spew("BMIDEChannel::StopWork: Stopping operation on channel = %d\n", m_channel);
// Tell worker to stop working on the job
m_job_running = false;
// Interrupt worker
m_job_cancel = true;
}
// Worker thread function
@ -130,6 +128,55 @@ uint32_t BMIDEChannel::WorkerThreadFunc(void *data) {
return 0;
}
static struct PRDHelper {
uint8_t *m_ram;
uint32_t m_ramSize;
PhysicalRegionDescriptor *m_currPRD;
uint32_t m_currByte;
uint32_t physAddr;
uint8_t *bufPtr;
PRDHelper(uint8_t *ram, uint32_t ramSize, uint32_t prdTableAddress)
: m_ram(ram)
, m_ramSize(ramSize)
{
// TODO: validate against ramSize to prevent buffer overflows
m_currPRD = reinterpret_cast<PhysicalRegionDescriptor*>(m_ram + prdTableAddress);
m_currByte = 0;
physAddr = 0;
bufPtr = nullptr;
}
bool NextSector() {
for (;;) {
// Get byte count from the PRD
uint16_t byteCount = m_currPRD->byteCount;
if (byteCount == 0) {
byteCount = KiB(64);
}
// Go to the next PRD if we reached the end of the current PRD
// and there are more entries
if (m_currByte >= m_currPRD->byteCount) {
// No more entries
if (m_currPRD->endOfTable) {
bufPtr = nullptr;
return false;
}
m_currPRD++;
continue;
}
// Prepare the pointer to the next block
physAddr = m_currPRD->basePhysicalAddress + m_currByte;
bufPtr = m_ram + physAddr;
m_currByte += kSectorSize;
return true;
}
}
};
void BMIDEChannel::RunWorker() {
while (m_worker_running) {
// Wait for work
@ -139,11 +186,71 @@ void BMIDEChannel::RunWorker() {
}
// Do work
PRDHelper helper(m_ram, m_ramSize, m_prdTableAddr);
while (m_job_running) {
bool isWrite = (m_command & CmdReadWriteControl) != 0;
log_spew("BMIDEChannel %d: %s\n", m_channel, (isWrite ? "write" : "read"));
// TODO: implement
m_job_running = false;
// The manual says that 1 means Bus Master write and 0 means Bus Master read,
// which is true from the perspective of the bus itself, but confusing to a programmer.
// From the programmer's perspective, 0 means write to device and 1 means read from device.
// See https://wiki.osdev.org/ATA/ATAPI_using_DMA#The_Command_Byte
bool isWrite = (m_command & CmdReadWriteControl) == 0;
if (m_ataChannel.IsDMAFinished()) {
// Operation finished; notify ATA channel
m_job_running = false;
// Set Interrupt flag if the ATA device triggered an interrupt
if (m_ataChannel.EndDMA()) {
m_status |= StInterrupt;
m_job_running = false;
break;
}
}
uint8_t buf[kSectorSize];
bool succeeded;
if (isWrite) {
succeeded = m_ataChannel.WriteDMA(buf);
}
else {
succeeded = m_ataChannel.ReadDMA(buf);
}
if (succeeded) {
// Copy to/from physical memory
if (helper.NextSector()) {
log_spew("BM IDE channel %d: %s physical address 0x%x\n", m_channel, (isWrite ? "read from" : "write to"), helper.physAddr);
memcpy(helper.bufPtr, buf, kSectorSize);
}
else {
log_spew("BM IDE channel %d: Ran out of PRDs\n", m_channel);
// Clear Active flag
m_status &= ~StActive;
// Set Interrupt flag if the ATA device triggered an interrupt
if (m_ataChannel.EndDMA()) {
m_status |= StInterrupt;
}
m_job_running = false;
}
}
else {
// Operation finished; notify ATA channel
if (m_ataChannel.EndDMA()) {
// Set Interrupt flag if the ATA device triggered an interrupt
m_status |= StInterrupt;
}
m_job_running = false;
}
}
if (m_job_cancel) {
// Clear Active flag if the job was cancelled
m_status &= ~StActive;
}
}
}

View file

@ -11,23 +11,31 @@
#include <mutex>
#include "bmide/defs.h"
#include "vixen/hw/ata/ata_common.h"
#include "vixen/hw/ata/ata.h"
namespace vixen {
class BMIDEChannel {
public:
BMIDEChannel(hw::bmide::Channel channel);
BMIDEChannel(hw::ata::Channel channel, hw::ata::ATAChannel& ataChannel, uint8_t *ram, uint32_t m_ramSize);
~BMIDEChannel();
private:
friend class BMIDEDevice;
hw::bmide::Channel m_channel;
hw::ata::Channel m_channel;
hw::ata::ATAChannel& m_ataChannel;
// ----- System memory ----------------------------------------------------
uint8_t *m_ram = nullptr;
uint32_t m_ramSize = 0;
// ----- Registers --------------------------------------------------------
uint8_t m_command = 0;
uint8_t m_status = 0;
uint32_t m_prdTableAddrs = 0;
uint32_t m_prdTableAddr = 0;
// ----- Operations -------------------------------------------------------
@ -49,6 +57,7 @@ private:
std::condition_variable m_jobCond;
bool m_worker_running;
bool m_job_running;
bool m_job_cancel;
void BeginWork();
void StopWork();

View file

@ -59,7 +59,7 @@ static inline uint32_t ldl_le_p(const void *p) {
case (v)+(step) * 3
NV2ADevice::NV2ADevice(uint8_t *pSystemRAM, uint32_t systemRAMSize, IRQHandler *irqHandler)
NV2ADevice::NV2ADevice(uint8_t *pSystemRAM, uint32_t systemRAMSize, IRQHandler& irqHandler)
: PCIDevice(PCI_HEADER_TYPE_NORMAL, PCI_VENDOR_ID_NVIDIA, 0x02A0, 0xA1,
0x03, 0x00, 0x00) // VGA-compatible controller
, m_pSystemRAM(pSystemRAM)
@ -2436,10 +2436,10 @@ void NV2ADevice::UpdateIRQ() {
uint8_t irq = Read8(m_configSpace, PCI_INTERRUPT_PIN);
if (m_PMC.pendingInterrupts && m_PMC.enabledInterrupts) {
m_irqHandler->HandleIRQ(irq, 1);
m_irqHandler.HandleIRQ(irq, 1);
}
else {
m_irqHandler->HandleIRQ(irq, 0);
m_irqHandler.HandleIRQ(irq, 0);
}
}

View file

@ -12,7 +12,7 @@ namespace vixen {
class NV2ADevice : public PCIDevice {
public:
NV2ADevice(uint8_t *pSystemRAM, uint32_t systemRAMSize, IRQHandler *irqHandler);
NV2ADevice(uint8_t *pSystemRAM, uint32_t systemRAMSize, IRQHandler& irqHandler);
virtual ~NV2ADevice();
@ -120,7 +120,7 @@ private:
uint8_t *m_pSystemRAM;
uint32_t m_systemRAMSize;
IRQHandler *m_irqHandler;
IRQHandler& m_irqHandler;
uint8_t* m_pRAMIN = nullptr;
uint8_t* m_VRAM = nullptr;

View file

@ -57,7 +57,7 @@ using namespace vixen::cpu;
#define SETUP_STATE_ACK 3
#define SETUP_STATE_PARAM 4
USBPCIDevice::USBPCIDevice(uint8_t irqn, Cpu* cpu)
USBPCIDevice::USBPCIDevice(uint8_t irqn, Cpu& cpu)
: PCIDevice(PCI_HEADER_TYPE_NORMAL, PCI_VENDOR_ID_NVIDIA, 0x02A5, 0xA1,
0x0c, 0x03, 0x10) // USB OHCI
, m_irqn(irqn)

View file

@ -66,7 +66,7 @@ class OHCI;
class USBPCIDevice : public PCIDevice {
public:
// constructor
USBPCIDevice(uint8_t irqn, Cpu *cpu);
USBPCIDevice(uint8_t irqn, Cpu& cpu);
virtual ~USBPCIDevice();
// PCI Device functions
@ -207,7 +207,7 @@ public:
const char* USBDesc_GetString(XboxDeviceState* dev, int index);
private:
uint8_t m_irqn;
Cpu* m_cpu;
Cpu& m_cpu;
};
}

View file

@ -341,8 +341,8 @@ EmulatorStatus Xbox::InitHardware() {
m_IRQs = AllocateIRQs(m_GSI, GSI_NUM_PINS);
// Create basic system devices
m_i8259 = new i8259(m_cpu);
m_i8254 = new i8254(m_i8259, m_settings.hw_sysclock_tickRate);
m_i8259 = new i8259(*m_cpu);
m_i8254 = new i8254(*m_i8259, m_settings.hw_sysclock_tickRate);
m_CMOS = new CMOS();
// TODO: make this configurable, similar to Super I/O port char drivers
@ -351,7 +351,7 @@ EmulatorStatus Xbox::InitHardware() {
m_ataDrivers[1][0] = new hw::ata::NullATADeviceDriver();
m_ataDrivers[1][1] = new hw::ata::NullATADeviceDriver();
m_ATA = new hw::ata::ATA(m_i8259);
m_ATA = new hw::ata::ATA(*m_i8259);
m_ATA->GetChannel(hw::ata::ChanPrimary).GetDevice(0).SetDeviceDriver(m_ataDrivers[0][0]);
m_ATA->GetChannel(hw::ata::ChanPrimary).GetDevice(1).SetDeviceDriver(m_ataDrivers[0][1]);
m_ATA->GetChannel(hw::ata::ChanSecondary).GetDevice(0).SetDeviceDriver(m_ataDrivers[1][0]);
@ -373,7 +373,7 @@ EmulatorStatus Xbox::InitHardware() {
}
m_CharDrivers[i]->Init();
}
m_SuperIO = new SuperIO(m_i8259, m_CharDrivers);
m_SuperIO = new SuperIO(*m_i8259, m_CharDrivers);
m_SuperIO->Init();
}
else {
@ -396,15 +396,15 @@ EmulatorStatus Xbox::InitHardware() {
m_HostBridge = new HostBridgeDevice();
m_MCPXRAM = new MCPXRAMDevice(mcpxRevision);
m_LPC = new LPCDevice(m_IRQs, m_rom, m_bios, m_biosSize, m_mcpxROM, m_settings.hw_model != DebugKit);
m_USB1 = new USBPCIDevice(1, m_cpu);
m_USB2 = new USBPCIDevice(9, m_cpu);
m_USB1 = new USBPCIDevice(1, *m_cpu);
m_USB2 = new USBPCIDevice(9, *m_cpu);
m_NVNet = new NVNetDevice();
m_NVAPU = new NVAPUDevice();
m_AC97 = new AC97Device();
m_PCIBridge = new PCIBridgeDevice();
m_BMIDE = new BMIDEDevice(m_ram, m_ramSize);
m_BMIDE = new BMIDEDevice(m_ram, m_ramSize, *m_ATA);
m_AGPBridge = new AGPBridgeDevice();
m_NV2A = new NV2ADevice(m_ram, m_ramSize, m_i8259);
m_NV2A = new NV2ADevice(m_ram, m_ramSize, *m_i8259);
// Configure IRQs
m_acpiIRQs = AllocateIRQs(m_LPC, 2);