mirror of https://github.com/mgba-emu/mgba.git
DS Slot-1: Initial Slot-1 implementation
This commit is contained in:
parent
035998e3f0
commit
7389176033
|
@ -76,20 +76,20 @@ enum DSIORegisters {
|
|||
// Game card
|
||||
DS_REG_AUXSPICNT = 0x1A0,
|
||||
DS_REG_AUXSPIDATA = 0x1A2,
|
||||
DS_REG_SLOT1CNT_LO = 0x1A4,
|
||||
DS_REG_SLOT1CNT_HI = 0x1A6,
|
||||
DS_REG_SLOT1CMD_0 = 0x1A8,
|
||||
DS_REG_SLOT1CMD_1 = 0x1A9,
|
||||
DS_REG_SLOT1CMD_2 = 0x1AA,
|
||||
DS_REG_SLOT1CMD_3 = 0x1AB,
|
||||
DS_REG_SLOT1CMD_4 = 0x1AC,
|
||||
DS_REG_SLOT1CMD_5 = 0x1AD,
|
||||
DS_REG_SLOT1CMD_6 = 0x1AE,
|
||||
DS_REG_SLOT1CMD_7 = 0x1AF,
|
||||
DS_REG_SLOT1DATA_0 = 0x100010,
|
||||
DS_REG_SLOT1DATA_1 = 0x100011,
|
||||
DS_REG_SLOT1DATA_2 = 0x100012,
|
||||
DS_REG_SLOT1DATA_3 = 0x100013,
|
||||
DS_REG_ROMCNT_LO = 0x1A4,
|
||||
DS_REG_ROMCNT_HI = 0x1A6,
|
||||
DS_REG_ROMCMD_0 = 0x1A8,
|
||||
DS_REG_ROMCMD_1 = 0x1A9,
|
||||
DS_REG_ROMCMD_2 = 0x1AA,
|
||||
DS_REG_ROMCMD_3 = 0x1AB,
|
||||
DS_REG_ROMCMD_4 = 0x1AC,
|
||||
DS_REG_ROMCMD_5 = 0x1AD,
|
||||
DS_REG_ROMCMD_6 = 0x1AE,
|
||||
DS_REG_ROMCMD_7 = 0x1AF,
|
||||
DS_REG_ROMDATA_0 = 0x100010,
|
||||
DS_REG_ROMDATA_1 = 0x100011,
|
||||
DS_REG_ROMDATA_2 = 0x100012,
|
||||
DS_REG_ROMDATA_3 = 0x100013,
|
||||
|
||||
// Interrupts
|
||||
DS_REG_IME = 0x208,
|
||||
|
|
|
@ -15,6 +15,7 @@ CXX_GUARD_START
|
|||
#include <mgba/internal/arm/arm.h>
|
||||
#include <mgba/internal/ds/dma.h>
|
||||
#include <mgba/internal/ds/io.h>
|
||||
#include <mgba/internal/ds/slot1.h>
|
||||
|
||||
enum DSMemoryRegion {
|
||||
DS7_REGION_BIOS = 0x0,
|
||||
|
@ -84,6 +85,7 @@ struct DSMemory {
|
|||
uint32_t* rom;
|
||||
uint16_t io7[DS7_REG_MAX >> 1];
|
||||
uint16_t io9[DS9_REG_MAX >> 1];
|
||||
struct DSSlot1 slot1;
|
||||
|
||||
uint16_t vramMirror[9][0x40];
|
||||
uint16_t vramMode[9][8];
|
||||
|
@ -96,6 +98,9 @@ struct DSMemory {
|
|||
uint32_t dtcmBase;
|
||||
uint32_t dtcmSize;
|
||||
uint32_t itcmSize;
|
||||
|
||||
bool slot1Owner;
|
||||
bool slot2Owner;
|
||||
};
|
||||
|
||||
struct DSCoreMemory {
|
||||
|
@ -114,6 +119,8 @@ struct DSCoreMemory {
|
|||
struct GBADMA dma[4];
|
||||
struct mTimingEvent dmaEvent;
|
||||
int activeDMA;
|
||||
bool slot1Access;
|
||||
bool slot2Access;
|
||||
};
|
||||
|
||||
struct DS;
|
||||
|
@ -149,5 +156,6 @@ uint32_t DS9StoreMultiple(struct ARMCore*, uint32_t baseAddress, int mask, enum
|
|||
int* cycleCounter);
|
||||
|
||||
void DSConfigureWRAM(struct DSMemory*, uint8_t config);
|
||||
void DSConfigureExternalMemory(struct DS*, uint16_t config);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#ifndef DS_SLOT1_H
|
||||
#define DS_SLOT1_H
|
||||
|
||||
#include <mgba-util/common.h>
|
||||
|
||||
CXX_GUARD_START
|
||||
|
||||
#include <mgba/core/log.h>
|
||||
|
||||
mLOG_DECLARE_CATEGORY(DS_SLOT1);
|
||||
|
||||
DECL_BITFIELD(DSSlot1AUXSPICNT, uint16_t);
|
||||
|
||||
DECL_BITFIELD(DSSlot1ROMCNT, uint32_t);
|
||||
DECL_BIT(DSSlot1ROMCNT, WordReady, 23);
|
||||
DECL_BITS(DSSlot1ROMCNT, BlockSize, 24, 3);
|
||||
DECL_BIT(DSSlot1ROMCNT, BlockBusy, 31);
|
||||
|
||||
struct DSSlot1 {
|
||||
uint8_t command[8];
|
||||
uint32_t address;
|
||||
uint32_t transferSize;
|
||||
uint32_t transferRemaining;
|
||||
uint8_t readBuffer[4];
|
||||
};
|
||||
|
||||
struct DS;
|
||||
DSSlot1AUXSPICNT DSSlot1Configure(struct DS* ds, DSSlot1AUXSPICNT config);
|
||||
DSSlot1ROMCNT DSSlot1Control(struct DS* ds, DSSlot1ROMCNT control);
|
||||
uint32_t DSSlot1Read(struct DS* ds);
|
||||
|
||||
CXX_GUARD_END
|
||||
|
||||
#endif
|
|
@ -279,6 +279,7 @@ void DS7Reset(struct ARMCore* cpu) {
|
|||
struct DSCartridge* header = ds->romVf->map(ds->romVf, sizeof(*header), MAP_READ);
|
||||
if (header) {
|
||||
memcpy(&ds->memory.ram[0x3FFE00 >> 2], header, 0x170);
|
||||
DS7IOWrite32(ds, DS_REG_ROMCNT_LO, header->busTiming | 0x2700000);
|
||||
// TODO: Error check
|
||||
ds->romVf->seek(ds->romVf, header->arm7Offset, SEEK_SET);
|
||||
uint32_t base = header->arm7Base - DS_BASE_RAM;
|
||||
|
|
61
src/ds/io.c
61
src/ds/io.c
|
@ -8,6 +8,7 @@
|
|||
#include <mgba/core/interface.h>
|
||||
#include <mgba/internal/ds/ds.h>
|
||||
#include <mgba/internal/ds/ipc.h>
|
||||
#include <mgba/internal/ds/slot1.h>
|
||||
#include <mgba/internal/ds/spi.h>
|
||||
|
||||
mLOG_DEFINE_CATEGORY(DS_IO, "DS I/O");
|
||||
|
@ -101,9 +102,38 @@ static uint32_t DSIOWrite(struct DSCommon* dscore, uint32_t address, uint16_t va
|
|||
break;
|
||||
|
||||
// Cart bus
|
||||
case DS_REG_SLOT1CNT_LO:
|
||||
mLOG(DS_IO, STUB, "ROM control not implemented");
|
||||
value &= 0x7FFF;
|
||||
case DS_REG_AUXSPICNT:
|
||||
if (dscore->memory.slot1Access) {
|
||||
value = DSSlot1Configure(dscore->p, value);
|
||||
dscore->ipc->memory.io[address >> 1] = value;
|
||||
} else {
|
||||
mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case DS_REG_ROMCNT_HI:
|
||||
if (dscore->memory.slot1Access) {
|
||||
DSSlot1ROMCNT cnt = value << 16;
|
||||
cnt |= dscore->memory.io[(address - 2) >> 1];
|
||||
cnt = DSSlot1Control(dscore->p, cnt);
|
||||
value = cnt >> 16;
|
||||
dscore->ipc->memory.io[address >> 1] = value;
|
||||
} else {
|
||||
mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
case DS_REG_ROMCNT_LO:
|
||||
case DS_REG_ROMCMD_0:
|
||||
case DS_REG_ROMCMD_2:
|
||||
case DS_REG_ROMCMD_4:
|
||||
case DS_REG_ROMCMD_6:
|
||||
if (dscore->memory.slot1Access) {
|
||||
dscore->ipc->memory.io[address >> 1] = value;
|
||||
} else {
|
||||
mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
// Interrupts
|
||||
|
@ -313,6 +343,8 @@ uint16_t DS7IORead(struct DS* ds, uint32_t address) {
|
|||
case DS7_REG_SPIDATA:
|
||||
case DS_REG_IPCSYNC:
|
||||
case DS_REG_IPCFIFOCNT:
|
||||
case DS_REG_ROMCNT_LO:
|
||||
case DS_REG_ROMCNT_HI:
|
||||
case DS_REG_IME:
|
||||
case 0x20A:
|
||||
case DS_REG_IE_LO:
|
||||
|
@ -334,6 +366,13 @@ uint32_t DS7IORead32(struct DS* ds, uint32_t address) {
|
|||
switch (address) {
|
||||
case DS_REG_IPCFIFORECV_LO:
|
||||
return DSIPCReadFIFO(&ds->ds7);
|
||||
case DS_REG_ROMDATA_0:
|
||||
if (ds->ds7.memory.slot1Access) {
|
||||
return DSSlot1Read(ds);
|
||||
} else {
|
||||
mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return DS7IORead(ds, address & 0x00FFFFFC) | (DS7IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
|
||||
}
|
||||
|
@ -343,7 +382,7 @@ void DS9IOInit(struct DS* ds) {
|
|||
memset(ds->memory.io9, 0, sizeof(ds->memory.io9));
|
||||
ds->memory.io9[DS_REG_IPCFIFOCNT >> 1] = 0x0101;
|
||||
ds->memory.io9[DS_REG_POSTFLG >> 1] = 0x0001;
|
||||
ds->memory.io9[DS9_REG_VRAMCNT_G >> 1] = 0x0300;
|
||||
DS9IOWrite(ds, DS9_REG_VRAMCNT_G, 0x0300);
|
||||
}
|
||||
|
||||
void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
|
||||
|
@ -367,6 +406,11 @@ void DS9IOWrite(struct DS* ds, uint32_t address, uint16_t value) {
|
|||
DSVideoConfigureVRAM(&ds->memory, 8, value >> 8);
|
||||
break;
|
||||
|
||||
case DS9_REG_EXMEMCNT:
|
||||
value &= 0xE8FF;
|
||||
DSConfigureExternalMemory(ds, value);
|
||||
break;
|
||||
|
||||
// Math
|
||||
case DS9_REG_DIVCNT:
|
||||
value = _scheduleDiv(ds, value);
|
||||
|
@ -482,6 +526,8 @@ uint16_t DS9IORead(struct DS* ds, uint32_t address) {
|
|||
case DS_REG_TM3CNT_HI:
|
||||
case DS_REG_IPCSYNC:
|
||||
case DS_REG_IPCFIFOCNT:
|
||||
case DS_REG_ROMCNT_LO:
|
||||
case DS_REG_ROMCNT_HI:
|
||||
case DS_REG_IME:
|
||||
case 0x20A:
|
||||
case DS_REG_IE_LO:
|
||||
|
@ -527,6 +573,13 @@ uint32_t DS9IORead32(struct DS* ds, uint32_t address) {
|
|||
switch (address) {
|
||||
case DS_REG_IPCFIFORECV_LO:
|
||||
return DSIPCReadFIFO(&ds->ds9);
|
||||
case DS_REG_ROMDATA_0:
|
||||
if (ds->ds9.memory.slot1Access) {
|
||||
return DSSlot1Read(ds);
|
||||
} else {
|
||||
mLOG(DS_IO, GAME_ERROR, "Invalid cart access");
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return DS9IORead(ds, address & 0x00FFFFFC) | (DS9IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
|
||||
}
|
||||
|
|
|
@ -176,6 +176,11 @@ void DSMemoryReset(struct DS* ds) {
|
|||
ds->memory.wramSize9 = 0;
|
||||
ds->memory.wramBase9 = NULL;
|
||||
|
||||
ds->memory.slot1Owner = true;
|
||||
ds->memory.slot2Owner = true;
|
||||
ds->ds7.memory.slot1Access = true;
|
||||
ds->ds9.memory.slot1Access = false;
|
||||
|
||||
DSVideoConfigureVRAM(&ds->memory, 0, 0);
|
||||
DSVideoConfigureVRAM(&ds->memory, 1, 0);
|
||||
DSVideoConfigureVRAM(&ds->memory, 2, 0);
|
||||
|
@ -1261,6 +1266,16 @@ void DSConfigureWRAM(struct DSMemory* memory, uint8_t config) {
|
|||
}
|
||||
}
|
||||
|
||||
void DSConfigureExternalMemory(struct DS* ds, uint16_t config) {
|
||||
// TODO: GBA params
|
||||
ds->memory.slot1Owner = config & 0x0800;
|
||||
ds->memory.slot2Owner = config & 0x0080;
|
||||
ds->memory.io7[DS7_REG_EXMEMSTAT >> 1] = config;
|
||||
|
||||
ds->ds7.memory.slot1Access = ds->memory.slot1Owner;
|
||||
ds->ds9.memory.slot1Access = !ds->memory.slot1Owner;
|
||||
}
|
||||
|
||||
static unsigned _selectVRAM(struct DSMemory* memory, uint32_t offset) {
|
||||
unsigned mask = 0;
|
||||
offset &= 0x3FF;
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
#include <mgba/internal/ds/slot1.h>
|
||||
|
||||
#include <mgba/internal/arm/macros.h>
|
||||
#include <mgba/internal/ds/ds.h>
|
||||
#include <mgba-util/vfs.h>
|
||||
|
||||
mLOG_DEFINE_CATEGORY(DS_SLOT1, "DS Slot-1");
|
||||
|
||||
static void DSSlot1StepTransfer(struct DS* ds) {
|
||||
DSSlot1ROMCNT romcnt;
|
||||
LOAD_32(romcnt, DS_REG_ROMCNT_LO, ds->memory.io7);
|
||||
if (ds->memory.slot1.transferRemaining) {
|
||||
ds->romVf->read(ds->romVf, ds->memory.slot1.readBuffer, 4);
|
||||
// TODO: Error check
|
||||
ds->memory.slot1.address += 4;
|
||||
ds->memory.slot1.transferRemaining -= 4;
|
||||
romcnt = DSSlot1ROMCNTFillWordReady(romcnt);
|
||||
} else {
|
||||
memset(ds->memory.slot1.readBuffer, 0, 4);
|
||||
romcnt = DSSlot1ROMCNTClearWordReady(romcnt);
|
||||
// TODO: IRQ
|
||||
romcnt = DSSlot1ROMCNTClearBlockBusy(romcnt);
|
||||
}
|
||||
STORE_32(romcnt, DS_REG_ROMCNT_LO, ds->memory.io7);
|
||||
STORE_32(romcnt, DS_REG_ROMCNT_LO, ds->memory.io9);
|
||||
}
|
||||
|
||||
static void DSSlot1StartTransfer(struct DS* ds) {
|
||||
size_t i;
|
||||
for (i = 0; i < 8; i += 2) {
|
||||
uint16_t bytes;
|
||||
LOAD_16(bytes, DS_REG_ROMCMD_0 + i, ds->memory.io7);
|
||||
ds->memory.slot1.command[i] = bytes & 0xFF;
|
||||
ds->memory.slot1.command[i + 1] = bytes >> 8;
|
||||
}
|
||||
switch (ds->memory.slot1.command[0]) {
|
||||
case 0xB7:
|
||||
ds->memory.slot1.address = ds->memory.slot1.command[1] << 24;
|
||||
ds->memory.slot1.address |= ds->memory.slot1.command[2] << 16;
|
||||
ds->memory.slot1.address |= ds->memory.slot1.command[3] << 8;
|
||||
ds->memory.slot1.address |= ds->memory.slot1.command[4];
|
||||
if (ds->romVf) {
|
||||
ds->romVf->seek(ds->romVf, ds->memory.slot1.address, SEEK_SET);
|
||||
}
|
||||
ds->memory.slot1.transferRemaining = ds->memory.slot1.transferSize;
|
||||
DSSlot1StepTransfer(ds);
|
||||
break;
|
||||
default:
|
||||
mLOG(DS_SLOT1, STUB, "Unimplemented card command: %02X%02X%02X%02X%02X%02X%02X%02X",
|
||||
ds->memory.slot1.command[0], ds->memory.slot1.command[1],
|
||||
ds->memory.slot1.command[2], ds->memory.slot1.command[3],
|
||||
ds->memory.slot1.command[4], ds->memory.slot1.command[5],
|
||||
ds->memory.slot1.command[6], ds->memory.slot1.command[7]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DSSlot1AUXSPICNT DSSlot1Configure(struct DS* ds, DSSlot1AUXSPICNT config) {
|
||||
mLOG(DS_SLOT1, STUB, "Unimplemented SPI AUX config: %04X", config);
|
||||
return config;
|
||||
}
|
||||
|
||||
DSSlot1ROMCNT DSSlot1Control(struct DS* ds, DSSlot1ROMCNT control) {
|
||||
ds->memory.slot1.transferSize = DSSlot1ROMCNTGetBlockSize(control);
|
||||
if (ds->memory.slot1.transferSize != 0 && ds->memory.slot1.transferSize != 7) {
|
||||
ds->memory.slot1.transferSize = 0x100 << ds->memory.slot1.transferSize;
|
||||
}
|
||||
if (DSSlot1ROMCNTIsBlockBusy(control)) {
|
||||
DSSlot1StartTransfer(ds);
|
||||
// TODO timing
|
||||
control = DSSlot1ROMCNTFillWordReady(control);
|
||||
}
|
||||
return control;
|
||||
}
|
||||
|
||||
uint32_t DSSlot1Read(struct DS* ds) {
|
||||
uint32_t result;
|
||||
LOAD_32(result, 0, ds->memory.slot1.readBuffer);
|
||||
DSSlot1StepTransfer(ds);
|
||||
return result;
|
||||
}
|
Loading…
Reference in New Issue