mirror of
https://github.com/emu-russia/pureikyubu.git
synced 2025-04-02 10:42:15 -04:00
698 lines
22 KiB
C++
698 lines
22 KiB
C++
// EXI - expansion (or extension) interface.
|
|
#include "pch.h"
|
|
|
|
// IMPORTANT : all EXI transfers are completed instantly in emu (mean no DMA chain).
|
|
|
|
/* ---------------------------------------------------------------------------
|
|
|
|
Purpose :
|
|
---------
|
|
|
|
EXI is used for reading of SRAM, RTC, IPL font (from MX chip)
|
|
EXI used for memory cards and broad band adapter.
|
|
bootrom using EXI for AD16 writes ('AD' - what does it mean ?)
|
|
|
|
SRAM : little piece of battery-backed data. 64 bytes or so.
|
|
RTC : 32-bit counter of seconds, since current millenium
|
|
AD16 : debugging 32-bit register
|
|
|
|
memcard should be in another module (see MC.cpp)
|
|
broad band adapter should be in another module (see BBA.cpp)
|
|
|
|
EXI Device Map of dedicated MX Chip :
|
|
-------------------------------------
|
|
|
|
--------------------------------------------
|
|
|chan | dev | addr | description |
|
|
|============================================|
|
|
| 0 | 0 | | Memory Card (Slot A) |
|
|
|--------------------------------------------|
|
|
| 0 | 1 |00000000| Mask ROM | *
|
|
|--------------------------------------------|
|
|
| 0 | 1 |20000000| Real-Time Clock (RTC) |
|
|
|--------------------------------------------|
|
|
| 0 | 1 |20000100| SRAM |
|
|
|--------------------------------------------|
|
|
| 0 | 1 |20010000| UART |
|
|
|--------------------------------------------|
|
|
| 0 | 1 |20010100| GPIO (Panasonic Q) |
|
|
|--------------------------------------------|
|
|
| 1 | 0 | | Memory Card (Slot B) |
|
|
|--------------------------------------------|
|
|
| 2 | 0 | | AD16 (trace step) |
|
|
--------------------------------------------
|
|
|
|
* - not actually address, but command (addr << 6) | 0x20000000,
|
|
used as is for emulation, though.
|
|
|
|
Summary : EXI is sort of USB architecture.
|
|
|
|
Tip from monk :
|
|
---------------
|
|
|
|
If You wan't to support the OSReport on a lower level, You just have
|
|
to handle the immediate writes to UART (0x20010000, when read, should
|
|
return a value between 0 and 4 inclusive) and set the console type to
|
|
one of the devkits (I use 0x10000006).
|
|
|
|
--------------------------------------------------------------------------- */
|
|
|
|
using namespace Debug;
|
|
|
|
// SI state (registers and other data)
|
|
EIControl exi;
|
|
|
|
// bootrom copyright message (at offset 0). PAL only, NTSC has garbage.
|
|
// can be used by apps to detect PAL/NTSC cube.
|
|
static char palver[0x100] = "(C) 1999-2001 Nintendo. All rights reserved."
|
|
"(C) 1999 ArtX Inc. All rights reserved."
|
|
"PAL Revision 1.0 ";
|
|
// ^^^
|
|
static char ntscver[0x100]= "ABRACADABRA";
|
|
|
|
// forward refs on EXI transfers
|
|
void UnknownTransfer(); // ???
|
|
void MXTransfer(); // bootrom, RTC and SRAM
|
|
void ADTransfer(); // AD16
|
|
|
|
// EXI transfer bindings
|
|
static void (*EXITransfer[3][3])() = {
|
|
{ MCTransfer , MXTransfer , UnknownTransfer },
|
|
{ MCTransfer , UnknownTransfer, UnknownTransfer },
|
|
{ ADTransfer , UnknownTransfer, UnknownTransfer }
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// EXI utilities
|
|
|
|
//
|
|
// update EXI interrupt status
|
|
//
|
|
|
|
void EXIUpdateInterrupts()
|
|
{
|
|
if( // match interrupt with its mask
|
|
(
|
|
(exi.regs[0].csr & (exi.regs[0].csr << 1) & EXI_CSR_INTERRUPTS) ||
|
|
(exi.regs[1].csr & (exi.regs[1].csr << 1) & EXI_CSR_INTERRUPTS) ||
|
|
(exi.regs[2].csr & (exi.regs[2].csr << 1) & EXI_CSR_INTERRUPTS)
|
|
)
|
|
)
|
|
{
|
|
// assert CPU interrupt
|
|
PIAssertInt(PI_INTERRUPT_EXI);
|
|
}
|
|
else
|
|
{
|
|
// clear cpu interrupt
|
|
PIClearInt(PI_INTERRUPT_EXI);
|
|
}
|
|
}
|
|
|
|
//
|
|
// attach / detach device on EXI channel
|
|
//
|
|
|
|
void EXIAttach(int chan)
|
|
{
|
|
if(exi.log) Report(Channel::EXI, "attaching device at channel %i\n", chan);
|
|
|
|
// set attach flag
|
|
exi.regs[chan].csr |= EXI_CSR_EXT;
|
|
|
|
// assert attach interrupt
|
|
exi.regs[chan].csr |= EXI_CSR_EXTINT;
|
|
EXIUpdateInterrupts();
|
|
}
|
|
|
|
void EXIDetach(int chan)
|
|
{
|
|
if(exi.log) Report(Channel::EXI, "detaching device at channel %i\n", chan);
|
|
|
|
// clear attach flag
|
|
exi.regs[chan].csr &= ~EXI_CSR_EXT;
|
|
|
|
// assert detach interrupt
|
|
exi.regs[chan].csr |= EXI_CSR_EXTINT;
|
|
EXIUpdateInterrupts();
|
|
}
|
|
|
|
//
|
|
// SRAM saving/loading, using external binary file
|
|
//
|
|
|
|
static void SRAMLoad(SRAM *s)
|
|
{
|
|
/* Load data from file in temporary buffe. */
|
|
auto buffer = Util::FileLoad(SRAM_FILE);
|
|
std::memset(s, 0, sizeof(SRAM));
|
|
|
|
/* Copy less or equal bytes from buffer to SRAM. */
|
|
if (!buffer.empty())
|
|
{
|
|
auto load_size = (buffer.size() > sizeof(SRAM) ? sizeof(SRAM) : buffer.size());
|
|
std::memcpy(s, buffer.data(), load_size);
|
|
}
|
|
else
|
|
{
|
|
Report(Channel::EXI, "SRAM loading failed from %s\n\n", SRAM_FILE);
|
|
}
|
|
}
|
|
|
|
static void SRAMSave(SRAM *s)
|
|
{
|
|
auto ptr = (uint8_t*)s;
|
|
|
|
auto buffer = std::vector<uint8_t>(ptr, ptr + sizeof(SRAM));
|
|
Util::FileSave(SRAM_FILE, buffer);
|
|
}
|
|
|
|
//
|
|
// update real-time clock register
|
|
// bootrom is updating time-base registers, using RTC value
|
|
//
|
|
|
|
#define MAGIC_VALUE 0x386d4380 // seconds between 1970 and 2000
|
|
|
|
// use to get updated RTC
|
|
void RTCUpdate()
|
|
{
|
|
exi.rtcVal = 0;// (uint32_t)time(NULL) - MAGIC_VALUE;
|
|
}
|
|
|
|
//
|
|
// load ANSI and SJIS fonts
|
|
//
|
|
|
|
static void FontLoad(uint8_t **font, uint32_t fontsize, TCHAR *filename)
|
|
{
|
|
do
|
|
{
|
|
/* Allocate memory for font data. */
|
|
*font = (uint8_t*)malloc(fontsize);
|
|
if (*font == NULL)
|
|
{
|
|
break;
|
|
}
|
|
|
|
std::memset(*font, 0, fontsize); /* Clear */
|
|
|
|
/* Load data from file in temporary buffer. */
|
|
auto buffer = Util::FileLoad(filename);
|
|
if (!buffer.empty())
|
|
{
|
|
auto load_size = (buffer.size() > fontsize ? fontsize : buffer.size());
|
|
std::memcpy(*font, buffer.data(), load_size);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
return;
|
|
} while (false);
|
|
|
|
/* Loading failed. */
|
|
Halt("EXI: Cannot load bootrom font: %s\n", filename);
|
|
}
|
|
|
|
static void FontUnload(uint8_t **font)
|
|
{
|
|
if(*font)
|
|
{
|
|
free(*font);
|
|
*font = 0;
|
|
}
|
|
}
|
|
|
|
// format UART string (covert ESC-codes, to debugger color-codes)
|
|
static char * uartf(char *buf)
|
|
{
|
|
static char str[300];
|
|
char *ptr = str;
|
|
size_t len = strlen(buf);
|
|
for(int n=0; n<len; n++)
|
|
{
|
|
if(buf[n] == 13) buf[n] = '\n';
|
|
*ptr++ = buf[n];
|
|
} *ptr = 0;
|
|
return str;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// basic transfers (memcard and BBA transfers are too big to put them here)
|
|
|
|
// undefined transfer
|
|
void UnknownTransfer()
|
|
{
|
|
// dont do nothing on the exi transfer
|
|
if(exi.log)
|
|
{
|
|
Report(Channel::EXI, "unknown transfer (channel:%i, device:%i)\n", exi.chan, exi.sel);
|
|
}
|
|
}
|
|
|
|
// MX chip transfers (EXI device 0:1)
|
|
void MXTransfer()
|
|
{
|
|
uint32_t ofs;
|
|
BOOL dma = (exi.regs[0].cr & EXI_CR_DMA) ? (1) : (0);
|
|
|
|
// read or write ?
|
|
switch(EXI_CR_RW(exi.regs[0].cr))
|
|
{
|
|
case 0: // read
|
|
{
|
|
if(dma) // dma
|
|
{
|
|
ofs = exi.mxaddr & 0x7fffffff;
|
|
if(ofs == 0x20000100)
|
|
{
|
|
if(exi.regs[0].len > sizeof(SRAM))
|
|
{
|
|
Report(Channel::EXI, "wrong input buffer size for SRAM read dma\n");
|
|
return;
|
|
}
|
|
memcpy(&mi.ram[exi.regs[0].madr & RAMMASK], &exi.sram, sizeof(SRAM));
|
|
return;
|
|
}
|
|
if((ofs >= 0x001fcf00) && (ofs < (0x001fcf00 + ANSI_SIZE)))
|
|
{
|
|
if (mi.BootromPresent)
|
|
{
|
|
memcpy(
|
|
&mi.ram[exi.regs[0].madr & RAMMASK],
|
|
&mi.bootrom[ofs],
|
|
exi.regs[0].len
|
|
);
|
|
}
|
|
else
|
|
{
|
|
assert(exi.ansiFont);
|
|
memcpy(
|
|
&mi.ram[exi.regs[0].madr & RAMMASK],
|
|
&exi.ansiFont[ofs - 0x001fcf00],
|
|
exi.regs[0].len
|
|
);
|
|
}
|
|
if(exi.log) Report ( Channel::EXI, "ansi font copy to %08X (%i)\n",
|
|
exi.regs[0].madr | 0x80000000, exi.regs[0].len );
|
|
return;
|
|
}
|
|
if((ofs >= 0x001aff00) && (ofs < (0x001aff00 + SJIS_SIZE)))
|
|
{
|
|
if (mi.BootromPresent)
|
|
{
|
|
memcpy(
|
|
&mi.ram[exi.regs[0].madr & RAMMASK],
|
|
&mi.bootrom[ofs],
|
|
exi.regs[0].len
|
|
);
|
|
}
|
|
else
|
|
{
|
|
assert(exi.sjisFont);
|
|
memcpy(
|
|
&mi.ram[exi.regs[0].madr & RAMMASK],
|
|
&exi.sjisFont[ofs - 0x001aff00],
|
|
exi.regs[0].len
|
|
);
|
|
}
|
|
if(exi.log) Report ( Channel::EXI, "sjis font copy to %08X (%i)\n",
|
|
exi.regs[0].madr | 0x80000000, exi.regs[0].len );
|
|
return;
|
|
}
|
|
|
|
// Bootrom reads
|
|
|
|
if (ofs < mi.bootromSize && mi.BootromPresent)
|
|
{
|
|
memcpy(
|
|
&mi.ram[exi.regs[0].madr & RAMMASK],
|
|
&mi.bootrom[ofs],
|
|
exi.regs[0].len
|
|
);
|
|
if(exi.log) Report ( Channel::EXI, "bootrom copy to %08X (%i)\n",
|
|
exi.regs[0].madr | 0x80000000, exi.regs[0].len );
|
|
return;
|
|
}
|
|
|
|
if(ofs)
|
|
{
|
|
if(exi.log) Report ( Channel::EXI, "unknown MX chip dma read\n");
|
|
}
|
|
}
|
|
else // immediate access
|
|
{
|
|
ofs = exi.mxaddr & 0x7fffffff;
|
|
if(ofs == 0x20000000)
|
|
{
|
|
RTCUpdate();
|
|
exi.regs[0].data = exi.rtcVal;
|
|
return;
|
|
}
|
|
else if((ofs >= 0x20000100) && (ofs < (0x20000100 + (sizeof(SRAM) << 6))))
|
|
{
|
|
int len = EXI_CR_TLEN(exi.regs[0].cr);
|
|
uint8_t * sofs = (uint8_t *)&exi.sram + ((ofs >> 6) & 0xff) - 4;
|
|
uint8_t * rofs = (uint8_t *)&exi.regs[0].data;
|
|
switch(len)
|
|
{
|
|
case 0: // byte
|
|
rofs[0] =
|
|
rofs[2] =
|
|
rofs[3] = 0;
|
|
rofs[3] = sofs[0];
|
|
exi.mxaddr += 1 << 6;
|
|
break;
|
|
case 1: // hword
|
|
rofs[0] =
|
|
rofs[1] = 0;
|
|
rofs[2] = sofs[1];
|
|
rofs[3] = sofs[0];
|
|
exi.mxaddr += 2 << 6;
|
|
break;
|
|
case 2: // triplet
|
|
rofs[0] = 0;
|
|
rofs[1] = sofs[2];
|
|
rofs[2] = sofs[1];
|
|
rofs[3] = sofs[0];
|
|
exi.mxaddr += 3 << 6;
|
|
break;
|
|
case 3: // word
|
|
rofs[0] = sofs[3];
|
|
rofs[1] = sofs[2];
|
|
rofs[2] = sofs[1];
|
|
rofs[3] = sofs[0];
|
|
exi.mxaddr += 4 << 6;
|
|
break;
|
|
}
|
|
if(exi.log) Report(Channel::EXI, "immediate read SRAM (ofs:%i, len:%i)\n", ((ofs >> 6) & 0xff) - 4, len+1);
|
|
return;
|
|
}
|
|
else if(ofs == 0x20010000)
|
|
{
|
|
exi.regs[0].data = 0x03000000;
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
Halt("EXI: Unknown MX chip read immediate from %08X", ofs);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
case 1: // write
|
|
{
|
|
if(dma) // dma
|
|
{
|
|
Halt("EXI: unknown MX chip write dma\n");
|
|
return;
|
|
}
|
|
else // immediate access
|
|
{
|
|
if(exi.firstImm)
|
|
{
|
|
exi.firstImm = FALSE;
|
|
exi.mxaddr = exi.regs[0].data;
|
|
if(exi.mxaddr < 0x20000000) exi.mxaddr >>= 6;
|
|
}
|
|
else
|
|
{
|
|
uint32_t bytes = (EXI_CR_TLEN(exi.regs[0].cr) + 1);
|
|
uint32_t data = _byteswap_ulong(exi.regs[0].data);
|
|
|
|
ofs = exi.mxaddr & 0x7fffffff;
|
|
if((ofs >= 0x20000100) && (ofs <= 0x20001000))
|
|
{
|
|
// SRAM immediate writes
|
|
uint32_t pos = (((ofs - 256) >> 6) & 0x3F);
|
|
|
|
if(exi.log) Report(Channel::EXI, "SRAM write immediate pos %d data %08x bytes %08x\n",
|
|
pos, exi.regs[0].data, bytes );
|
|
|
|
memcpy(((uint8_t *)&exi.sram) + pos, &data, bytes);
|
|
exi.mxaddr += (bytes << 6);
|
|
}
|
|
else if((ofs >= 0x20010000) && (ofs < 0x20010100))
|
|
{
|
|
// UART I/O
|
|
uint8_t *buf = (uint8_t *)&data;
|
|
for(uint32_t n=0; n<bytes; n++)
|
|
{
|
|
exi.uart[exi.upos++] = buf[n];
|
|
|
|
// output UART buffer after de-select
|
|
if(buf[n] == 13)
|
|
{
|
|
exi.uart[exi.upos] = 0;
|
|
exi.upos = 0;
|
|
if(exi.osReport) Report(Channel::Info, "%s", uartf(exi.uart));
|
|
}
|
|
}
|
|
}
|
|
else Report(Channel::EXI, "Unknown MX chip write immediate to %08X", ofs);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
default:
|
|
{
|
|
if(EXI_CR_RW(exi.regs[0].cr))
|
|
{
|
|
Report(Channel::EXI, "unknown EXI transfer mode for MX chip\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// AD chip? transfer (EXI device 2:0)
|
|
void ADTransfer()
|
|
{
|
|
// read or write ?
|
|
switch(EXI_CR_RW(exi.regs[2].cr))
|
|
{
|
|
case 0: // read
|
|
{
|
|
if(exi.ad16_cmd == 0) exi.regs[2].data = 0x04120000;
|
|
else if(exi.ad16_cmd == 0xa2000000) exi.regs[2].data = exi.ad16 << 16;
|
|
else Report(Channel::EXI, "unknown AD16 command\n");
|
|
return;
|
|
}
|
|
|
|
case 1: // write
|
|
{
|
|
if(exi.firstImm)
|
|
{
|
|
exi.firstImm = FALSE;
|
|
exi.ad16_cmd = exi.regs[2].data;
|
|
}
|
|
else
|
|
{
|
|
if(exi.ad16_cmd != 0xa0000000)
|
|
{
|
|
Report(Channel::EXI, "unknown AD command (%08X)\n", exi.ad16_cmd);
|
|
return;
|
|
}
|
|
exi.ad16 = exi.regs[2].data >> 16;
|
|
if(exi.log) Report(Channel::EXI, "AD16 set to %04X\n", exi.ad16);
|
|
}
|
|
return;
|
|
}
|
|
|
|
default:
|
|
{
|
|
if(EXI_CR_RW(exi.regs[2].cr))
|
|
{
|
|
Report(Channel::EXI, "unknown EXI transfer mode for AD16\n");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// registers
|
|
|
|
//
|
|
// communication control
|
|
//
|
|
|
|
static void exi_select(int chan)
|
|
{
|
|
// set flag
|
|
exi.firstImm = TRUE;
|
|
|
|
if(exi.regs[chan].csr & EXI_CSR_CS0B)
|
|
{
|
|
exi.sel = 0;
|
|
return;
|
|
}
|
|
if(exi.regs[chan].csr & EXI_CSR_CS1B)
|
|
{
|
|
exi.sel = 1;
|
|
return;
|
|
}
|
|
if(exi.regs[chan].csr & EXI_CSR_CS2B)
|
|
{
|
|
exi.sel = 2;
|
|
return;
|
|
}
|
|
|
|
// no device selected
|
|
exi.sel = -1;
|
|
}
|
|
|
|
static void write_csr(int chan, uint32_t data)
|
|
{
|
|
// clear interrupts
|
|
exi.regs[chan].csr &= ~(data & EXI_CSR_INTERRUPTS);
|
|
|
|
// update register and do select
|
|
exi.regs[chan].csr = (exi.regs[chan].csr & EXI_CSR_READONLY) | (data & ~EXI_CSR_READONLY);
|
|
exi_select(chan);
|
|
EXIUpdateInterrupts();
|
|
}
|
|
|
|
static void exi0_read_csr(uint32_t addr, uint32_t *reg) { *reg = exi.regs[0].csr; }
|
|
static void exi1_read_csr(uint32_t addr, uint32_t *reg) { *reg = exi.regs[1].csr; }
|
|
static void exi2_read_csr(uint32_t addr, uint32_t *reg) { *reg = exi.regs[2].csr; }
|
|
static void exi0_write_csr(uint32_t addr, uint32_t data) { write_csr(0, data); }
|
|
static void exi1_write_csr(uint32_t addr, uint32_t data) { write_csr(1, data); }
|
|
static void exi2_write_csr(uint32_t addr, uint32_t data) { write_csr(2, data); }
|
|
|
|
//
|
|
// memory address for EXI DMA
|
|
//
|
|
|
|
static void exi0_read_madr(uint32_t addr, uint32_t *reg) { *reg = exi.regs[0].madr; }
|
|
static void exi1_read_madr(uint32_t addr, uint32_t *reg) { *reg = exi.regs[1].madr; }
|
|
static void exi2_read_madr(uint32_t addr, uint32_t *reg) { *reg = exi.regs[2].madr; }
|
|
static void exi0_write_madr(uint32_t addr, uint32_t data) { exi.regs[0].madr = data; }
|
|
static void exi1_write_madr(uint32_t addr, uint32_t data) { exi.regs[1].madr = data; }
|
|
static void exi2_write_madr(uint32_t addr, uint32_t data) { exi.regs[2].madr = data; }
|
|
|
|
//
|
|
// data length for DMA
|
|
//
|
|
|
|
static void exi0_read_len(uint32_t addr, uint32_t *reg) { *reg = exi.regs[0].len; }
|
|
static void exi1_read_len(uint32_t addr, uint32_t *reg) { *reg = exi.regs[1].len; }
|
|
static void exi2_read_len(uint32_t addr, uint32_t *reg) { *reg = exi.regs[2].len; }
|
|
static void exi0_write_len(uint32_t addr, uint32_t data) { exi.regs[0].len = data; }
|
|
static void exi1_write_len(uint32_t addr, uint32_t data) { exi.regs[1].len = data; }
|
|
static void exi2_write_len(uint32_t addr, uint32_t data) { exi.regs[2].len = data; }
|
|
|
|
//
|
|
// EXI control
|
|
//
|
|
|
|
static void exi_write_cr(int chan, uint32_t data)
|
|
{
|
|
EXIRegs *regs = &exi.regs[chan];
|
|
regs->cr = data;
|
|
|
|
if(regs->cr & EXI_CR_TSTART)
|
|
{
|
|
if(exi.sel == -1)
|
|
{
|
|
Report(Channel::EXI, "device should be selected before transfer\n");
|
|
return;
|
|
}
|
|
|
|
// start transfer
|
|
EXITransfer[exi.chan = chan][exi.sel]();
|
|
|
|
// complete transfer
|
|
regs->cr &= ~EXI_CR_TSTART;
|
|
|
|
// assert transfer complete interrupt
|
|
regs->csr |= EXI_CSR_TCINT;
|
|
EXIUpdateInterrupts();
|
|
}
|
|
}
|
|
|
|
static void exi0_read_cr(uint32_t addr, uint32_t *reg) { *reg = exi.regs[0].cr; }
|
|
static void exi1_read_cr(uint32_t addr, uint32_t *reg) { *reg = exi.regs[1].cr; }
|
|
static void exi2_read_cr(uint32_t addr, uint32_t *reg) { *reg = exi.regs[2].cr; }
|
|
static void exi0_write_cr(uint32_t addr, uint32_t data) { exi_write_cr(0, data); }
|
|
static void exi1_write_cr(uint32_t addr, uint32_t data) { exi_write_cr(1, data); }
|
|
static void exi2_write_cr(uint32_t addr, uint32_t data) { exi_write_cr(2, data); }
|
|
|
|
//
|
|
// EXI immediate data
|
|
//
|
|
|
|
static void exi0_read_data(uint32_t addr, uint32_t *reg) { *reg = exi.regs[0].data; }
|
|
static void exi1_read_data(uint32_t addr, uint32_t *reg) { *reg = exi.regs[1].data; }
|
|
static void exi2_read_data(uint32_t addr, uint32_t *reg) { *reg = exi.regs[2].data; }
|
|
static void exi0_write_data(uint32_t addr, uint32_t data) { exi.regs[0].data = data; }
|
|
static void exi1_write_data(uint32_t addr, uint32_t data) { exi.regs[1].data = data; }
|
|
static void exi2_write_data(uint32_t addr, uint32_t data) { exi.regs[2].data = data; }
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// init
|
|
|
|
void EIOpen(HWConfig * config)
|
|
{
|
|
Report(Channel::EXI, "External devices interface bus\n");
|
|
|
|
// clear registers
|
|
memset(&exi, 0, sizeof(EIControl));
|
|
|
|
// load user variables
|
|
exi.log = config->exi_log;
|
|
exi.osReport = config->exi_osReport;
|
|
|
|
// reset devices
|
|
exi.sel = -1; // deselect MX device
|
|
SRAMLoad(&exi.sram); // load sram
|
|
RTCUpdate();
|
|
if (!mi.BootromPresent)
|
|
{
|
|
FontLoad(&exi.ansiFont, ANSI_SIZE, config->ansiFilename);
|
|
FontLoad(&exi.sjisFont, SJIS_SIZE, config->sjisFilename);
|
|
}
|
|
|
|
// set traps for EXI channel 0 registers
|
|
MISetTrap(32, EXI0_CSR , exi0_read_csr , exi0_write_csr) ;
|
|
MISetTrap(32, EXI0_MADR, exi0_read_madr, exi0_write_madr);
|
|
MISetTrap(32, EXI0_LEN , exi0_read_len , exi0_write_len) ;
|
|
MISetTrap(32, EXI0_CR , exi0_read_cr , exi0_write_cr) ;
|
|
MISetTrap(32, EXI0_DATA, exi0_read_data, exi0_write_data);
|
|
|
|
// set traps for EXI channel 1 registers
|
|
MISetTrap(32, EXI1_CSR , exi1_read_csr , exi1_write_csr) ;
|
|
MISetTrap(32, EXI1_MADR, exi1_read_madr, exi1_write_madr);
|
|
MISetTrap(32, EXI1_LEN , exi1_read_len , exi1_write_len) ;
|
|
MISetTrap(32, EXI1_CR , exi1_read_cr , exi1_write_cr) ;
|
|
MISetTrap(32, EXI1_DATA, exi1_read_data, exi1_write_data);
|
|
|
|
// set traps for EXI channel 2 registers
|
|
MISetTrap(32, EXI2_CSR , exi2_read_csr , exi2_write_csr) ;
|
|
MISetTrap(32, EXI2_MADR, exi2_read_madr, exi2_write_madr);
|
|
MISetTrap(32, EXI2_LEN , exi2_read_len , exi2_write_len) ;
|
|
MISetTrap(32, EXI2_CR , exi2_read_cr , exi2_write_cr) ;
|
|
MISetTrap(32, EXI2_DATA, exi2_read_data, exi2_write_data);
|
|
|
|
// open memory cards
|
|
MCOpen(config);
|
|
|
|
// open broad band adapter
|
|
}
|
|
|
|
void EIClose()
|
|
{
|
|
// close memory cards
|
|
MCClose();
|
|
|
|
// close broad band adapter
|
|
|
|
// unload fonts
|
|
FontUnload(&exi.ansiFont);
|
|
FontUnload(&exi.sjisFont);
|
|
|
|
// sync sram
|
|
SRAMSave(&exi.sram);
|
|
}
|