mirror of
https://github.com/StrikerX3/StrikeBox.git
synced 2024-06-21 13:52:36 -04:00
Implemented IDE DMA transfers
Prefer references over pointers where applicable
This commit is contained in:
parent
bac54bfff3
commit
3f1a12a965
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace ata {
|
|||
|
||||
class ATA : public IODevice {
|
||||
public:
|
||||
ATA(IRQHandler *irqHandler);
|
||||
ATA(IRQHandler& irqHandler);
|
||||
virtual ~ATA();
|
||||
void Reset();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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; }
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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 -------------------------------------------------------------------------------
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace vixen {
|
|||
|
||||
class ISABus {
|
||||
public:
|
||||
ISABus(IRQ* irqs);
|
||||
ISABus(IRQ *irqs);
|
||||
~ISABus();
|
||||
|
||||
IRQ *GetIRQ(uint8_t isaIRQ);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue