mirror of
https://github.com/emu-russia/pureikyubu.git
synced 2025-04-02 10:42:15 -04:00
391 lines
10 KiB
C++
391 lines
10 KiB
C++
// DI - Flipper Disk Interface
|
|
#include "pch.h"
|
|
|
|
// DI state (registers and other data)
|
|
DIControl di;
|
|
|
|
static uint8_t DIHostToDduCallbackCommand();
|
|
static uint8_t DIHostToDduCallbackData();
|
|
static void DIDduToHostCallback(uint8_t data);
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// cover control. Callbacks are issued from DDU
|
|
|
|
static void DIOpenCover()
|
|
{
|
|
// cover interrupt
|
|
DICVR |= DI_CVR_CVRINT;
|
|
if(DICVR & DI_CVR_CVRINTMSK)
|
|
{
|
|
PIAssertInt(PI_INTERRUPT_DI);
|
|
}
|
|
}
|
|
|
|
static void DICloseCover()
|
|
{
|
|
// cover interrupt
|
|
DICVR |= DI_CVR_CVRINT;
|
|
if(DICVR & DI_CVR_CVRINTMSK)
|
|
{
|
|
PIAssertInt(PI_INTERRUPT_DI);
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Device Error
|
|
|
|
static void DIErrorCallback()
|
|
{
|
|
DICR &= ~DI_CR_TSTART;
|
|
|
|
DISR |= DI_SR_DEINT;
|
|
if (DISR & DI_SR_DEINTMSK)
|
|
{
|
|
PIAssertInt(PI_INTERRUPT_DI);
|
|
}
|
|
|
|
DVD::DDU->SetTransferCallbacks(DIHostToDduCallbackCommand, DIDduToHostCallback);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Transfer
|
|
|
|
// DI breaks itself only after finishing next 32 Byte chunk of data
|
|
static void DIBreak()
|
|
{
|
|
DICR &= ~DI_CR_TSTART;
|
|
DISR &= ~DI_SR_BRK;
|
|
|
|
DISR |= DI_SR_BRKINT;
|
|
if (DISR & DI_SR_BRKINTMSK)
|
|
{
|
|
PIAssertInt(PI_INTERRUPT_DI);
|
|
}
|
|
|
|
DVD::DDU->SetTransferCallbacks(DIHostToDduCallbackCommand, DIDduToHostCallback);
|
|
}
|
|
|
|
// DDU transfer complete interrupt
|
|
static void DITransferComplete()
|
|
{
|
|
DICR &= ~DI_CR_TSTART;
|
|
|
|
DISR |= DI_SR_TCINT;
|
|
if (DISR & DI_SR_TCINTMSK)
|
|
{
|
|
PIAssertInt(PI_INTERRUPT_DI);
|
|
}
|
|
|
|
DVD::DDU->SetTransferCallbacks(DIHostToDduCallbackCommand, DIDduToHostCallback);
|
|
}
|
|
|
|
static uint8_t DIHostToDduCallbackCommand()
|
|
{
|
|
uint8_t data = 0;
|
|
|
|
// DI Imm Write Command (DILEN ignored)
|
|
|
|
if (di.hostToDduByteCounter < sizeof(di.cmdbuf))
|
|
{
|
|
data = di.cmdbuf[di.hostToDduByteCounter++];
|
|
}
|
|
|
|
if (di.hostToDduByteCounter >= sizeof(di.cmdbuf))
|
|
{
|
|
// Dont stop DDU Bus clock
|
|
|
|
// Issue transfer data
|
|
|
|
DVD::DDU->SetTransferCallbacks(DIHostToDduCallbackData, DIDduToHostCallback);
|
|
DVD::DDU->StartTransfer(DICR & DI_CR_RW ? DVD::DduBusDirection::HostToDdu : DVD::DduBusDirection::DduToHost);
|
|
|
|
if (DICR & DI_CR_RW)
|
|
{
|
|
di.hostToDduByteCounter = 32; // A special value that overloads the FIFO before reading the first byte from the DDU side.
|
|
}
|
|
else
|
|
{
|
|
di.dduToHostByteCounter = 0;
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
static uint8_t DIHostToDduCallbackData()
|
|
{
|
|
uint8_t data = 0;
|
|
|
|
if (DICR & DI_CR_DMA)
|
|
{
|
|
// DI Dma Write
|
|
|
|
if (di.hostToDduByteCounter >= 32)
|
|
{
|
|
di.hostToDduByteCounter = 0;
|
|
|
|
if (DILEN)
|
|
{
|
|
uint32_t dimar = DIMAR & DI_DIMAR_MASK;
|
|
MIReadBurst(dimar, di.dmaFifo);
|
|
DIMAR += 32;
|
|
DILEN -= 32;
|
|
}
|
|
|
|
if (DILEN == 0)
|
|
{
|
|
DVD::DDU->TransferComplete(); // Stop DDU Bus clock
|
|
DITransferComplete();
|
|
return 0;
|
|
}
|
|
|
|
if (DISR & DI_SR_BRK)
|
|
{
|
|
// Can break only after writing next chunk
|
|
DIBreak();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
data = di.dmaFifo[di.hostToDduByteCounter];
|
|
di.hostToDduByteCounter++;
|
|
}
|
|
else
|
|
{
|
|
if (di.hostToDduByteCounter < sizeof(di.immbuf))
|
|
{
|
|
data = di.immbuf[di.hostToDduByteCounter++];
|
|
}
|
|
|
|
if (di.hostToDduByteCounter >= sizeof(di.immbuf))
|
|
{
|
|
DVD::DDU->TransferComplete(); // Stop DDU Bus clock
|
|
DITransferComplete();
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
static void DIDduToHostCallback(uint8_t data)
|
|
{
|
|
if (DICR & DI_CR_DMA)
|
|
{
|
|
// DI Dma Read
|
|
|
|
di.dmaFifo[di.dduToHostByteCounter] = data;
|
|
di.dduToHostByteCounter++;
|
|
if (di.dduToHostByteCounter >= 32)
|
|
{
|
|
di.dduToHostByteCounter = 0;
|
|
|
|
if (DISR & DI_SR_BRK)
|
|
{
|
|
// Can break only after reading next chunk
|
|
DIBreak();
|
|
return;
|
|
}
|
|
|
|
if (DILEN)
|
|
{
|
|
uint32_t dimar = DIMAR & DI_DIMAR_MASK;
|
|
MIWriteBurst(dimar, di.dmaFifo);
|
|
DIMAR += 32;
|
|
DILEN -= 32;
|
|
}
|
|
|
|
if (DILEN == 0)
|
|
{
|
|
DVD::DDU->TransferComplete(); // Stop DDU Bus clock
|
|
DITransferComplete();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// DI Imm Read (DILEN ignored)
|
|
|
|
if (di.dduToHostByteCounter < sizeof(di.immbuf))
|
|
{
|
|
di.immbuf[di.dduToHostByteCounter] = data;
|
|
di.dduToHostByteCounter++;
|
|
}
|
|
|
|
if (di.dduToHostByteCounter >= sizeof(di.immbuf))
|
|
{
|
|
di.dduToHostByteCounter = 0;
|
|
DVD::DDU->TransferComplete(); // Stop DDU Bus clock
|
|
DITransferComplete();
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// DI register traps
|
|
|
|
// status register
|
|
static void write_sr(uint32_t addr, uint32_t data)
|
|
{
|
|
// set masks
|
|
if(data & DI_SR_BRKINTMSK) DISR |= DI_SR_BRKINTMSK;
|
|
else DISR &= ~DI_SR_BRKINTMSK;
|
|
if(data & DI_SR_TCINTMSK) DISR |= DI_SR_TCINTMSK;
|
|
else DISR &= ~DI_SR_TCINTMSK;
|
|
|
|
// clear interrupts
|
|
if(data & DI_SR_BRKINT)
|
|
{
|
|
DISR &= ~DI_SR_BRKINT;
|
|
}
|
|
if(data & DI_SR_TCINT)
|
|
{
|
|
DISR &= ~DI_SR_TCINT;
|
|
}
|
|
if (data & DI_SR_DEINT)
|
|
{
|
|
DISR &= ~DI_SR_DEINT;
|
|
}
|
|
if ((DISR & DI_SR_BRKINT) == 0 && (DISR & DI_SR_TCINT) == 0 && (DISR & DI_SR_DEINT) == 0)
|
|
{
|
|
PIClearInt(PI_INTERRUPT_DI);
|
|
}
|
|
|
|
// Issue break
|
|
if(data & DI_SR_BRK)
|
|
{
|
|
// Send break to DDU immediately (DIBRK signal)
|
|
DVD::DDU->Break();
|
|
}
|
|
}
|
|
|
|
static void read_sr(uint32_t addr, uint32_t *reg)
|
|
{
|
|
*reg = (uint16_t)DISR;
|
|
}
|
|
|
|
// control register
|
|
static void write_cr(uint32_t addr, uint32_t data)
|
|
{
|
|
DICR = (uint16_t)data;
|
|
|
|
// start command
|
|
if(DICR & DI_CR_TSTART)
|
|
{
|
|
// Issue command
|
|
|
|
di.hostToDduByteCounter = 0;
|
|
DVD::DDU->SetTransferCallbacks(DIHostToDduCallbackCommand, DIDduToHostCallback);
|
|
DVD::DDU->StartTransfer(DVD::DduBusDirection::HostToDdu);
|
|
}
|
|
}
|
|
|
|
static void read_cr(uint32_t addr, uint32_t *reg)
|
|
{
|
|
*reg = (uint16_t)DICR;
|
|
}
|
|
|
|
// cover register
|
|
static void write_cvr(uint32_t addr, uint32_t data)
|
|
{
|
|
// clear cover interrupt
|
|
if(data & DI_CVR_CVRINT)
|
|
{
|
|
DICVR &= ~DI_CVR_CVRINT;
|
|
PIClearInt(PI_INTERRUPT_DI);
|
|
}
|
|
|
|
// set mask
|
|
if(data & DI_CVR_CVRINTMSK) DICVR |= DI_CVR_CVRINTMSK;
|
|
else DICVR &= ~DI_CVR_CVRINTMSK;
|
|
}
|
|
|
|
static void read_cvr(uint32_t addr, uint32_t *reg)
|
|
{
|
|
uint32_t value = DICVR & ~DI_CVR_CVR;
|
|
|
|
if (DVD::DDU->GetCoverStatus() == DVD::CoverStatus::Open)
|
|
{
|
|
value |= DI_CVR_CVR;
|
|
}
|
|
|
|
*reg = value;
|
|
}
|
|
|
|
// dma registers
|
|
static void read_mar(uint32_t addr, uint32_t *reg) { *reg = DIMAR & DI_DIMAR_MASK; }
|
|
static void write_mar(uint32_t addr, uint32_t data) { DIMAR = data; }
|
|
static void read_len(uint32_t addr, uint32_t *reg) { *reg = DILEN & ~0x1f; }
|
|
static void write_len(uint32_t addr, uint32_t data) { DILEN = data; }
|
|
|
|
static void DISetCommandBuffer(int n, uint32_t value)
|
|
{
|
|
volatile uint8_t* ptr = &di.cmdbuf[n * 4];
|
|
*(uint32_t*)ptr = _byteswap_ulong(value);
|
|
}
|
|
|
|
static uint32_t DIGetCommandBuffer(int n)
|
|
{
|
|
volatile uint8_t* ptr = &di.cmdbuf[n * 4];
|
|
return _byteswap_ulong(*(uint32_t*)ptr);
|
|
}
|
|
|
|
// di buffers
|
|
static void read_cmdbuf0(uint32_t addr, uint32_t *reg) { *reg = DIGetCommandBuffer(0); }
|
|
static void write_cmdbuf0(uint32_t addr, uint32_t data) { DISetCommandBuffer(0, data); }
|
|
static void read_cmdbuf1(uint32_t addr, uint32_t *reg) { *reg = DIGetCommandBuffer(1); }
|
|
static void write_cmdbuf1(uint32_t addr, uint32_t data) { DISetCommandBuffer(1, data); }
|
|
static void read_cmdbuf2(uint32_t addr, uint32_t *reg) { *reg = DIGetCommandBuffer(2); }
|
|
static void write_cmdbuf2(uint32_t addr, uint32_t data) { DISetCommandBuffer(2, data); }
|
|
static void read_immbuf(uint32_t addr, uint32_t *reg) { *reg = _byteswap_ulong(*(uint32_t *)di.immbuf); }
|
|
static void write_immbuf(uint32_t addr, uint32_t data) { *(uint32_t*)di.immbuf = _byteswap_ulong(data); }
|
|
|
|
// register is read only.
|
|
// currently, bit 0 is used for ROM scramble disable (which ROM?), bits 1-7 are reserved
|
|
// used in EXISync->__OSGetDIConfig call, return 0 and forget.
|
|
static void read_cfg(uint32_t addr, uint32_t *reg) { *reg = 0; }
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// init
|
|
|
|
void DIOpen()
|
|
{
|
|
Debug::Report(Debug::Channel::DI, "DVD interface hardware\n");
|
|
|
|
// Current DVD is set by Loader, or when disk is swapped by UI.
|
|
|
|
// clear registers
|
|
memset(&di, 0, sizeof(DIControl));
|
|
|
|
di.log = true;
|
|
|
|
// Register DDU callbacks
|
|
DVD::DDU->SetCoverOpenCallback(DIOpenCover);
|
|
DVD::DDU->SetCoverCloseCallback(DICloseCover);
|
|
DVD::DDU->SetErrorCallback(DIErrorCallback);
|
|
DVD::DDU->SetTransferCallbacks(DIHostToDduCallbackCommand, DIDduToHostCallback);
|
|
|
|
// set 32-bit register traps
|
|
MISetTrap(32, DI_SR , read_sr , write_sr);
|
|
MISetTrap(32, DI_CVR , read_cvr , write_cvr);
|
|
MISetTrap(32, DI_CMDBUF0, read_cmdbuf0 , write_cmdbuf0);
|
|
MISetTrap(32, DI_CMDBUF1, read_cmdbuf1 , write_cmdbuf1);
|
|
MISetTrap(32, DI_CMDBUF2, read_cmdbuf2 , write_cmdbuf2);
|
|
MISetTrap(32, DI_MAR , read_mar , write_mar);
|
|
MISetTrap(32, DI_LEN , read_len , write_len);
|
|
MISetTrap(32, DI_CR , read_cr , write_cr);
|
|
MISetTrap(32, DI_IMMBUF , read_immbuf , write_immbuf);
|
|
MISetTrap(32, DI_CFG , read_cfg , NULL);
|
|
}
|
|
|
|
void DIClose()
|
|
{
|
|
DVD::DDU->TransferComplete();
|
|
|
|
DVD::DDU->SetCoverOpenCallback(nullptr);
|
|
DVD::DDU->SetCoverCloseCallback(nullptr);
|
|
DVD::DDU->SetErrorCallback(nullptr);
|
|
DVD::DDU->SetTransferCallbacks(nullptr, nullptr);
|
|
|
|
di.dduToHostByteCounter = 0;
|
|
di.hostToDduByteCounter = 32;
|
|
}
|