DEV9: Don't fake the FIFO

This commit is contained in:
TheLastRar 2024-10-15 17:19:31 +01:00 committed by Ty
parent 1f2d9ab4e5
commit 1bdd53a6c5
4 changed files with 240 additions and 94 deletions

View File

@ -167,8 +167,8 @@ public:
void Async(u32 cycles); void Async(u32 cycles);
void ATAreadDMA8Mem(u8* pMem, int size); int ReadDMAToFIFO(u8* buffer, int space);
void ATAwriteDMA8Mem(u8* pMem, int size); int WriteDMAFromFIFO(u8* buffer, int available);
u16 ATAreadPIO(); u16 ATAreadPIO();
//ATAwritePIO; //ATAwritePIO;

View File

@ -10,7 +10,7 @@ void ATA::DRQCmdDMADataToHost()
regStatus &= ~ATA_STAT_BUSY; regStatus &= ~ATA_STAT_BUSY;
regStatus |= ATA_STAT_DRQ; regStatus |= ATA_STAT_DRQ;
dmaReady = true; dmaReady = true;
_DEV9irq(SPD_INTR_ATA_FIFO_DATA, 1); DEV9runFIFO();
//PCSX2 will Start DMA //PCSX2 will Start DMA
} }
void ATA::PostCmdDMADataToHost() void ATA::PostCmdDMADataToHost()
@ -22,11 +22,9 @@ void ATA::PostCmdDMADataToHost()
regStatus &= ~ATA_STAT_BUSY; regStatus &= ~ATA_STAT_BUSY;
dmaReady = false; dmaReady = false;
dev9.irqcause &= ~SPD_INTR_ATA_FIFO_DATA;
pendingInterrupt = true; pendingInterrupt = true;
if (regControlEnableIRQ) if (regControlEnableIRQ)
_DEV9irq(ATA_INTR_INTRQ, 1); _DEV9irq(ATA_INTR_INTRQ, 1);
//PCSX2 Will Start DMA
} }
void ATA::DRQCmdDMADataFromHost() void ATA::DRQCmdDMADataFromHost()
@ -44,7 +42,7 @@ void ATA::DRQCmdDMADataFromHost()
regStatus &= ~ATA_STAT_BUSY; regStatus &= ~ATA_STAT_BUSY;
regStatus |= ATA_STAT_DRQ; regStatus |= ATA_STAT_DRQ;
dmaReady = true; dmaReady = true;
_DEV9irq(SPD_INTR_ATA_FIFO_DATA, 1); DEV9runFIFO();
//PCSX2 will Start DMA //PCSX2 will Start DMA
} }
void ATA::PostCmdDMADataFromHost() void ATA::PostCmdDMADataFromHost()
@ -62,8 +60,6 @@ void ATA::PostCmdDMADataFromHost()
regStatus &= ~ATA_STAT_DRQ; regStatus &= ~ATA_STAT_DRQ;
dmaReady = false; dmaReady = false;
dev9.irqcause &= ~SPD_INTR_ATA_FIFO_DATA;
if (fetWriteCacheEnabled) if (fetWriteCacheEnabled)
{ {
regStatus &= ~ATA_STAT_BUSY; regStatus &= ~ATA_STAT_BUSY;
@ -77,18 +73,16 @@ void ATA::PostCmdDMADataFromHost()
Async(-1); Async(-1);
} }
void ATA::ATAreadDMA8Mem(u8* pMem, int size) int ATA::ReadDMAToFIFO(u8* buffer, int space)
{ {
if ((udmaMode >= 0 || mdmaMode >= 0) && if (udmaMode >= 0 || mdmaMode >= 0)
(dev9.if_ctrl & SPD_IF_ATA_DMAEN) != 0)
{ {
if (size == 0 || nsector == -1) if (space == 0 || nsector == -1)
return; return 0;
DevCon.WriteLn("DEV9: DMA read, size %i, transferred %i, total size %i", size, rdTransferred, nsector * 512);
//read // Read to FIFO
size = std::min(size, nsector * 512 - rdTransferred); const int size = std::min(space, nsector * 512 - rdTransferred);
memcpy(pMem, &readBuffer[rdTransferred], size); memcpy(buffer, &readBuffer[rdTransferred], size);
rdTransferred += size; rdTransferred += size;
@ -100,21 +94,22 @@ void ATA::ATAreadDMA8Mem(u8* pMem, int size)
rdTransferred = 0; rdTransferred = 0;
PostCmdDMADataToHost(); PostCmdDMADataToHost();
} }
return size;
} }
return 0;
} }
void ATA::ATAwriteDMA8Mem(u8* pMem, int size) int ATA::WriteDMAFromFIFO(u8* buffer, int available)
{ {
if ((udmaMode >= 0 || mdmaMode >= 0) && if (udmaMode >= 0 || mdmaMode >= 0)
(dev9.if_ctrl & SPD_IF_ATA_DMAEN) != 0)
{ {
if (nsector == -1) if (available == 0 || nsector == -1)
return; return 0;
DevCon.WriteLn("DEV9: DMA write, size %i, transferred %i, total size %i", size, wrTransferred, nsector * 512);
//write // Write to FIFO
size = std::min(size, nsector * 512 - wrTransferred); const int size = std::min(available, nsector * 512 - wrTransferred);
memcpy(&currentWrite[wrTransferred], pMem, size); memcpy(&currentWrite[wrTransferred], buffer, size);
wrTransferred += size; wrTransferred += size;
@ -126,7 +121,10 @@ void ATA::ATAwriteDMA8Mem(u8* pMem, int size)
wrTransferred = 0; wrTransferred = 0;
PostCmdDMADataFromHost(); PostCmdDMADataFromHost();
} }
return size;
} }
return 0;
} }
//GENRAL FEATURE SET //GENRAL FEATURE SET

View File

@ -202,6 +202,7 @@ void DEV9close()
{ {
DevCon.WriteLn("DEV9: DEV9close"); DevCon.WriteLn("DEV9: DEV9close");
dev9.dma_iop_ptr = nullptr;
dev9.ata->Close(); dev9.ata->Close();
TermNet(); TermNet();
isRunning = false; isRunning = false;
@ -228,49 +229,124 @@ void _DEV9irq(int cause, int cycles)
dev9Irq(cycles); dev9Irq(cycles);
} }
//Fakes SPEED FIFO // SPEED <-> HDD FIFO
void HDDWriteFIFO() void HDDWriteFIFO()
{ {
if (dev9.ata->dmaReady && (dev9.if_ctrl & SPD_IF_ATA_DMAEN)) pxAssert(dev9.ata->dmaReady && (dev9.if_ctrl & SPD_IF_ATA_DMAEN));
{ pxAssert((dev9.if_ctrl & SPD_IF_READ));
const int unread = (dev9.fifo_bytes_write - dev9.fifo_bytes_read);
const int spaceSectors = (SPD_DBUF_AVAIL_MAX * 512 - unread) / 512;
if (spaceSectors < 0)
{
Console.Error("DEV9: No Space on SPEED FIFO");
pxAssert(false);
abort();
}
const int readSectors = dev9.ata->nsectorLeft < spaceSectors ? dev9.ata->nsectorLeft : spaceSectors; const int unread = (dev9.fifo_bytes_write - dev9.fifo_bytes_read);
dev9.fifo_bytes_write += readSectors * 512; const int space = (SPD_DBUF_AVAIL_MAX * 512 - unread);
dev9.ata->nsectorLeft -= readSectors; const int base = dev9.fifo_bytes_write % (SPD_DBUF_AVAIL_MAX * 512);
pxAssert(unread <= SPD_DBUF_AVAIL_MAX * 512);
int read;
if (base + space > SPD_DBUF_AVAIL_MAX * 512)
{
const int was = SPD_DBUF_AVAIL_MAX * 512 - base;
read = dev9.ata->ReadDMAToFIFO(dev9.fifo + base, was);
if (read == was)
read += dev9.ata->ReadDMAToFIFO(dev9.fifo, space - was);
} }
//FIFOIntr(); else
{
read = dev9.ata->ReadDMAToFIFO(dev9.fifo + base, space);
}
dev9.fifo_bytes_write += read;
} }
void HDDReadFIFO() void HDDReadFIFO()
{ {
if (dev9.ata->dmaReady && (dev9.if_ctrl & SPD_IF_ATA_DMAEN)) pxAssert(dev9.ata->dmaReady && (dev9.if_ctrl & SPD_IF_ATA_DMAEN));
pxAssert((dev9.if_ctrl & SPD_IF_READ) == 0);
const int unread = (dev9.fifo_bytes_write - dev9.fifo_bytes_read);
const int base = dev9.fifo_bytes_read % (SPD_DBUF_AVAIL_MAX * 512);
pxAssert(unread <= SPD_DBUF_AVAIL_MAX * 512);
int write;
if (base + unread > SPD_DBUF_AVAIL_MAX * 512)
{ {
const int writeSectors = (dev9.fifo_bytes_write - dev9.fifo_bytes_read) / 512; const int was = SPD_DBUF_AVAIL_MAX * 512 - base;
dev9.fifo_bytes_read += writeSectors * 512; write = dev9.ata->WriteDMAFromFIFO(dev9.fifo + base, was);
dev9.ata->nsectorLeft -= writeSectors; if (write == was)
write += dev9.ata->WriteDMAFromFIFO(dev9.fifo, unread - was);
} }
//FIFOIntr(); else
{
write = dev9.ata->WriteDMAFromFIFO(dev9.fifo + base, unread);
}
dev9.fifo_bytes_read += write;
} }
void IOPReadFIFO(int bytes) void IOPReadFIFO()
{ {
dev9.fifo_bytes_read += bytes; pxAssert((dev9.dma_iop_ptr != nullptr) && (dev9.xfr_ctrl & SPD_XFR_DMAEN));
pxAssert((dev9.xfr_ctrl & SPD_XFR_WRITE) == 0);
const int unread = (dev9.fifo_bytes_write - dev9.fifo_bytes_read);
const int base = dev9.fifo_bytes_read % (SPD_DBUF_AVAIL_MAX * 512);
const int remain = dev9.dma_iop_size - dev9.dma_iop_transfered;
const int read = std::min(remain, unread);
pxAssert(unread <= SPD_DBUF_AVAIL_MAX * 512);
if (read == 0)
return;
if (base + read > SPD_DBUF_AVAIL_MAX * 512)
{
const int was = SPD_DBUF_AVAIL_MAX * 512 - base;
std::memcpy(dev9.dma_iop_ptr + dev9.dma_iop_transfered, dev9.fifo + base, was);
std::memcpy(dev9.dma_iop_ptr + dev9.dma_iop_transfered + was, dev9.fifo, read - was);
}
else
{
std::memcpy(dev9.dma_iop_ptr + dev9.dma_iop_transfered, dev9.fifo + base, read);
}
dev9.dma_iop_transfered += read;
dev9.fifo_bytes_read += read;
if (dev9.fifo_bytes_read > dev9.fifo_bytes_write) if (dev9.fifo_bytes_read > dev9.fifo_bytes_write)
Console.Error("DEV9: UNDERFLOW BY IOP"); Console.Error("DEV9: UNDERFLOW BY IOP");
//FIFOIntr();
} }
void IOPWriteFIFO(int bytes) void IOPWriteFIFO()
{ {
dev9.fifo_bytes_write += bytes; pxAssert((dev9.dma_iop_ptr != nullptr) && (dev9.xfr_ctrl & SPD_XFR_DMAEN));
if (dev9.fifo_bytes_write - SPD_DBUF_AVAIL_MAX * 512 > dev9.fifo_bytes_read) pxAssert(dev9.xfr_ctrl & SPD_XFR_WRITE);
const int unread = (dev9.fifo_bytes_write - dev9.fifo_bytes_read);
const int space = (SPD_DBUF_AVAIL_MAX * 512 - unread);
const int base = dev9.fifo_bytes_write % (SPD_DBUF_AVAIL_MAX * 512);
const int remain = dev9.dma_iop_size - dev9.dma_iop_transfered;
const int write = std::min(remain, space);
pxAssert(unread <= SPD_DBUF_AVAIL_MAX * 512);
if (write == 0)
return;
if (base + write > SPD_DBUF_AVAIL_MAX * 512)
{
const int was = SPD_DBUF_AVAIL_MAX * 512 - base;
std::memcpy(dev9.fifo + base, dev9.dma_iop_ptr + dev9.dma_iop_transfered, was);
std::memcpy(dev9.fifo + base, dev9.dma_iop_ptr + dev9.dma_iop_transfered + was, write - was);
}
else
{
std::memcpy(dev9.fifo + base, dev9.dma_iop_ptr + dev9.dma_iop_transfered, write);
}
dev9.dma_iop_transfered += write;
dev9.fifo_bytes_write += write;
if ((dev9.fifo_bytes_write - dev9.fifo_bytes_read) > SPD_DBUF_AVAIL_MAX * 512)
Console.Error("DEV9: OVERFLOW BY IOP"); Console.Error("DEV9: OVERFLOW BY IOP");
//FIFOIntr();
} }
void FIFOIntr() void FIFOIntr()
{ {
@ -279,14 +355,83 @@ void FIFOIntr()
if (unread == 0) if (unread == 0)
{ {
dev9.irqcause &= ~SPD_INTR_ATA_FIFO_DATA;
if ((dev9.irqcause & SPD_INTR_ATA_FIFO_EMPTY) == 0) if ((dev9.irqcause & SPD_INTR_ATA_FIFO_EMPTY) == 0)
_DEV9irq(SPD_INTR_ATA_FIFO_EMPTY, 1); _DEV9irq(SPD_INTR_ATA_FIFO_EMPTY, 1);
} }
else
{
dev9.irqcause &= ~SPD_INTR_ATA_FIFO_EMPTY;
if ((dev9.irqcause & SPD_INTR_ATA_FIFO_DATA) == 0)
_DEV9irq(SPD_INTR_ATA_FIFO_DATA, 1);
}
if (unread == SPD_DBUF_AVAIL_MAX * 512) if (unread == SPD_DBUF_AVAIL_MAX * 512)
{ {
//Log_Error("FIFO Full"); if ((dev9.irqcause & SPD_INTR_ATA_FIFO_FULL) == 0)
//INTR Full? _DEV9irq(SPD_INTR_ATA_FIFO_FULL, 1);
} }
else
{
dev9.irqcause &= ~SPD_INTR_ATA_FIFO_FULL;
}
// is DMA finished
if ((dev9.dma_iop_ptr != nullptr) &&
(dev9.dma_iop_transfered == dev9.dma_iop_size))
{
dev9.dma_iop_ptr = nullptr;
psxDMA8Interrupt();
}
}
// FIFO counters operate based on the direction set in SPD_R_XFR_CTRL
// Both might have to set to the same direction for (SPEED <-> HDD) to work
void DEV9runFIFO()
{
const bool iopWrite = dev9.xfr_ctrl & SPD_XFR_WRITE; // IOP writes to FIFO
const bool hddRead = dev9.if_ctrl & SPD_IF_READ; // HDD writes to FIFO
const bool iopXfer = (dev9.dma_iop_ptr != nullptr) && (dev9.xfr_ctrl & SPD_XFR_DMAEN);
const bool hddXfer = dev9.ata->dmaReady && (dev9.if_ctrl & SPD_IF_ATA_DMAEN);
// Order operations based on iopWrite to ensure DMA has data/space to work with.
if (iopWrite)
{
// Perform DMA from IOP.
if (iopXfer)
IOPWriteFIFO();
// Drain the FIFO
if (hddXfer && !hddRead)
{
HDDReadFIFO();
}
}
else
{
// Ensure FIFO has data.
if (hddXfer && hddRead)
{
HDDWriteFIFO();
}
if (iopXfer)
{
// Perform DMA to IOP.
IOPReadFIFO();
// Refill FIFO after DMA.
// Need to recheck dmaReady incase prior
// HDDWriteFIFO competed the transfer from HDD
if (hddXfer && hddRead && dev9.ata->dmaReady)
{
HDDWriteFIFO();
}
}
}
FIFOIntr();
} }
u16 SpeedRead(u32 addr, int width) u16 SpeedRead(u32 addr, int width)
@ -359,18 +504,8 @@ u16 SpeedRead(u32 addr, int width)
return dev9.xfr_ctrl; return dev9.xfr_ctrl;
case SPD_R_DBUF_STAT: case SPD_R_DBUF_STAT:
{ {
if (dev9.if_ctrl & SPD_IF_READ) // Semi async
{
HDDWriteFIFO(); // Yes this is not a typo
}
else
{
HDDReadFIFO();
}
FIFOIntr();
const u8 count = static_cast<u8>((dev9.fifo_bytes_write - dev9.fifo_bytes_read) / 512); const u8 count = static_cast<u8>((dev9.fifo_bytes_write - dev9.fifo_bytes_read) / 512);
if (dev9.xfr_ctrl & SPD_XFR_WRITE) // or ifRead? if (dev9.xfr_ctrl & SPD_XFR_WRITE)
{ {
hard = static_cast<u8>(SPD_DBUF_AVAIL_MAX - count); hard = static_cast<u8>(SPD_DBUF_AVAIL_MAX - count);
hard |= (count == 0) ? SPD_DBUF_STAT_1 : static_cast<u16>(0); hard |= (count == 0) ? SPD_DBUF_STAT_1 : static_cast<u16>(0);
@ -518,9 +653,14 @@ void SpeedWrite(u32 addr, u16 value, int width)
break; break;
case SPD_R_XFR_CTRL: case SPD_R_XFR_CTRL:
{
//DevCon.WriteLn("DEV9: SPD_R_XFR_CTRL %dbit write %x", width, value); //DevCon.WriteLn("DEV9: SPD_R_XFR_CTRL %dbit write %x", width, value);
const u16 oldValue = dev9.xfr_ctrl;
dev9.xfr_ctrl = value; dev9.xfr_ctrl = value;
if ((value & SPD_XFR_WRITE) != (oldValue & SPD_XFR_WRITE))
DEV9runFIFO();
//if (value & SPD_XFR_WRITE) //if (value & SPD_XFR_WRITE)
// DevCon.WriteLn("DEV9: SPD_R_XFR_CTRL Set Write"); // DevCon.WriteLn("DEV9: SPD_R_XFR_CTRL Set Write");
//else //else
@ -532,8 +672,11 @@ void SpeedWrite(u32 addr, u16 value, int width)
//if ((value & (1 << 2)) != 0) //if ((value & (1 << 2)) != 0)
// DevCon.WriteLn("DEV9: SPD_R_XFR_CTRL Unknown Bit 2"); // DevCon.WriteLn("DEV9: SPD_R_XFR_CTRL Unknown Bit 2");
//if (value & SPD_XFR_DMAEN) if (value & SPD_XFR_DMAEN)
// DevCon.WriteLn("DEV9: SPD_R_XFR_CTRL For DMA Enabled"); {
//DevCon.WriteLn("DEV9: SPD_R_XFR_CTRL For DMA Enabled");
DEV9runFIFO();
}
//else //else
// DevCon.WriteLn("DEV9: SPD_R_XFR_CTRL For DMA Disabled"); // DevCon.WriteLn("DEV9: SPD_R_XFR_CTRL For DMA Disabled");
@ -541,6 +684,7 @@ void SpeedWrite(u32 addr, u16 value, int width)
Console.Error("DEV9: SPD_R_XFR_CTRL Unknown value written %x", value); Console.Error("DEV9: SPD_R_XFR_CTRL Unknown value written %x", value);
break; break;
}
case SPD_R_DBUF_STAT: case SPD_R_DBUF_STAT:
//DevCon.WriteLn("DEV9: SPD_R_DBUF_STAT %dbit write %x", width, value); //DevCon.WriteLn("DEV9: SPD_R_DBUF_STAT %dbit write %x", width, value);
@ -560,13 +704,19 @@ void SpeedWrite(u32 addr, u16 value, int width)
break; break;
case SPD_R_IF_CTRL: case SPD_R_IF_CTRL:
{
//DevCon.WriteLn("DEV9: SPD_R_IF_CTRL %dbit write %x", width, value); //DevCon.WriteLn("DEV9: SPD_R_IF_CTRL %dbit write %x", width, value);
const u16 oldValue = dev9.if_ctrl;
dev9.if_ctrl = value; dev9.if_ctrl = value;
//if (value & SPD_IF_UDMA) //if (value & SPD_IF_UDMA)
// DevCon.WriteLn("DEV9: IF_CTRL UDMA Enabled"); // DevCon.WriteLn("DEV9: IF_CTRL UDMA Enabled");
//else //else
// DevCon.WriteLn("DEV9: IF_CTRL UDMA Disabled"); // DevCon.WriteLn("DEV9: IF_CTRL UDMA Disabled");
if ((value & SPD_IF_READ) != (oldValue & SPD_IF_READ))
DEV9runFIFO();
//if (value & SPD_IF_READ) //if (value & SPD_IF_READ)
// DevCon.WriteLn("DEV9: IF_CTRL DMA Is ATA Read"); // DevCon.WriteLn("DEV9: IF_CTRL DMA Is ATA Read");
//else //else
@ -575,15 +725,7 @@ void SpeedWrite(u32 addr, u16 value, int width)
if (value & SPD_IF_ATA_DMAEN) if (value & SPD_IF_ATA_DMAEN)
{ {
//DevCon.WriteLn("DEV9: IF_CTRL ATA DMA Enabled"); //DevCon.WriteLn("DEV9: IF_CTRL ATA DMA Enabled");
if (value & SPD_IF_READ) //Semi async DEV9runFIFO();
{
HDDWriteFIFO(); //Yes this is not a typo
}
else
{
HDDReadFIFO();
}
FIFOIntr();
} }
//else //else
// DevCon.WriteLn("DEV9: IF_CTRL ATA DMA Disabled"); // DevCon.WriteLn("DEV9: IF_CTRL ATA DMA Disabled");
@ -617,6 +759,7 @@ void SpeedWrite(u32 addr, u16 value, int width)
Console.Error("DEV9: IF_CTRL Unknown Bit(s) %x", (value & 0xFF00)); Console.Error("DEV9: IF_CTRL Unknown Bit(s) %x", (value & 0xFF00));
break; break;
}
case SPD_R_PIO_MODE: //ATA only? or includes EEPROM? case SPD_R_PIO_MODE: //ATA only? or includes EEPROM?
//DevCon.WriteLn("DEV9: SPD_R_PIO_MODE 16bit %dbit write %x", width, value); //DevCon.WriteLn("DEV9: SPD_R_PIO_MODE 16bit %dbit write %x", width, value);
dev9.pio_mode = value; dev9.pio_mode = value;
@ -924,14 +1067,14 @@ void DEV9readDMA8Mem(u32* pMem, int size)
} }
else else
{ {
if (dev9.xfr_ctrl & SPD_XFR_DMAEN && if (!(dev9.xfr_ctrl & SPD_XFR_WRITE))
!(dev9.xfr_ctrl & SPD_XFR_WRITE))
{ {
HDDWriteFIFO(); pxAssert(size <= SPD_DBUF_AVAIL_MAX * 512);
IOPReadFIFO(size); dev9.dma_iop_ptr = reinterpret_cast<u8*>(pMem);
dev9.ata->ATAreadDMA8Mem((u8*)pMem, size); dev9.dma_iop_size = size;
FIFOIntr(); dev9.dma_iop_transfered = 0;
psxDMA8Interrupt();
DEV9runFIFO();
} }
} }
@ -954,18 +1097,16 @@ void DEV9writeDMA8Mem(u32* pMem, int size)
} }
else else
{ {
if (dev9.xfr_ctrl & SPD_XFR_DMAEN && if (dev9.xfr_ctrl & SPD_XFR_WRITE)
dev9.xfr_ctrl & SPD_XFR_WRITE)
{ {
IOPWriteFIFO(size); pxAssert(size <= SPD_DBUF_AVAIL_MAX * 512);
HDDReadFIFO(); dev9.dma_iop_ptr = reinterpret_cast<u8*>(pMem);
dev9.ata->ATAwriteDMA8Mem((u8*)pMem, size); dev9.dma_iop_size = size;
FIFOIntr(); dev9.dma_iop_transfered = 0;
psxDMA8Interrupt();
DEV9runFIFO();
} }
} }
//TODO, track if write was successful
} }
void DEV9async(u32 cycles) void DEV9async(u32 cycles)

View File

@ -69,9 +69,15 @@ typedef struct
u16 mdma_mode; u16 mdma_mode;
u16 udma_mode; u16 udma_mode;
//Non-Regs // FIFO
int fifo_bytes_read; int fifo_bytes_read;
int fifo_bytes_write; int fifo_bytes_write;
u8 fifo[16 * 512];
// DMA
u8* dma_iop_ptr;
int dma_iop_transfered;
int dma_iop_size;
} dev9Struct; } dev9Struct;
//EEPROM states //EEPROM states
@ -672,6 +678,7 @@ void FLASHwrite32(u32 addr, u32 value, int size);
void _DEV9irq(int cause, int cycles); void _DEV9irq(int cause, int cycles);
int DEV9irqHandler(void); int DEV9irqHandler(void);
void DEV9async(u32 cycles); void DEV9async(u32 cycles);
void DEV9runFIFO();
void DEV9writeDMA8Mem(u32* pMem, int size); void DEV9writeDMA8Mem(u32* pMem, int size);
void DEV9readDMA8Mem(u32* pMem, int size); void DEV9readDMA8Mem(u32* pMem, int size);
u8 DEV9read8(u32 addr); u8 DEV9read8(u32 addr);