DS Slot-1: Initial Slot-1 implementation

This commit is contained in:
Vicki Pfau 2017-02-20 23:55:59 -08:00
parent 035998e3f0
commit 7389176033
7 changed files with 220 additions and 18 deletions

View File

@ -76,20 +76,20 @@ enum DSIORegisters {
// Game card // Game card
DS_REG_AUXSPICNT = 0x1A0, DS_REG_AUXSPICNT = 0x1A0,
DS_REG_AUXSPIDATA = 0x1A2, DS_REG_AUXSPIDATA = 0x1A2,
DS_REG_SLOT1CNT_LO = 0x1A4, DS_REG_ROMCNT_LO = 0x1A4,
DS_REG_SLOT1CNT_HI = 0x1A6, DS_REG_ROMCNT_HI = 0x1A6,
DS_REG_SLOT1CMD_0 = 0x1A8, DS_REG_ROMCMD_0 = 0x1A8,
DS_REG_SLOT1CMD_1 = 0x1A9, DS_REG_ROMCMD_1 = 0x1A9,
DS_REG_SLOT1CMD_2 = 0x1AA, DS_REG_ROMCMD_2 = 0x1AA,
DS_REG_SLOT1CMD_3 = 0x1AB, DS_REG_ROMCMD_3 = 0x1AB,
DS_REG_SLOT1CMD_4 = 0x1AC, DS_REG_ROMCMD_4 = 0x1AC,
DS_REG_SLOT1CMD_5 = 0x1AD, DS_REG_ROMCMD_5 = 0x1AD,
DS_REG_SLOT1CMD_6 = 0x1AE, DS_REG_ROMCMD_6 = 0x1AE,
DS_REG_SLOT1CMD_7 = 0x1AF, DS_REG_ROMCMD_7 = 0x1AF,
DS_REG_SLOT1DATA_0 = 0x100010, DS_REG_ROMDATA_0 = 0x100010,
DS_REG_SLOT1DATA_1 = 0x100011, DS_REG_ROMDATA_1 = 0x100011,
DS_REG_SLOT1DATA_2 = 0x100012, DS_REG_ROMDATA_2 = 0x100012,
DS_REG_SLOT1DATA_3 = 0x100013, DS_REG_ROMDATA_3 = 0x100013,
// Interrupts // Interrupts
DS_REG_IME = 0x208, DS_REG_IME = 0x208,

View File

@ -15,6 +15,7 @@ CXX_GUARD_START
#include <mgba/internal/arm/arm.h> #include <mgba/internal/arm/arm.h>
#include <mgba/internal/ds/dma.h> #include <mgba/internal/ds/dma.h>
#include <mgba/internal/ds/io.h> #include <mgba/internal/ds/io.h>
#include <mgba/internal/ds/slot1.h>
enum DSMemoryRegion { enum DSMemoryRegion {
DS7_REGION_BIOS = 0x0, DS7_REGION_BIOS = 0x0,
@ -84,6 +85,7 @@ struct DSMemory {
uint32_t* rom; uint32_t* rom;
uint16_t io7[DS7_REG_MAX >> 1]; uint16_t io7[DS7_REG_MAX >> 1];
uint16_t io9[DS9_REG_MAX >> 1]; uint16_t io9[DS9_REG_MAX >> 1];
struct DSSlot1 slot1;
uint16_t vramMirror[9][0x40]; uint16_t vramMirror[9][0x40];
uint16_t vramMode[9][8]; uint16_t vramMode[9][8];
@ -96,6 +98,9 @@ struct DSMemory {
uint32_t dtcmBase; uint32_t dtcmBase;
uint32_t dtcmSize; uint32_t dtcmSize;
uint32_t itcmSize; uint32_t itcmSize;
bool slot1Owner;
bool slot2Owner;
}; };
struct DSCoreMemory { struct DSCoreMemory {
@ -114,6 +119,8 @@ struct DSCoreMemory {
struct GBADMA dma[4]; struct GBADMA dma[4];
struct mTimingEvent dmaEvent; struct mTimingEvent dmaEvent;
int activeDMA; int activeDMA;
bool slot1Access;
bool slot2Access;
}; };
struct DS; struct DS;
@ -149,5 +156,6 @@ uint32_t DS9StoreMultiple(struct ARMCore*, uint32_t baseAddress, int mask, enum
int* cycleCounter); int* cycleCounter);
void DSConfigureWRAM(struct DSMemory*, uint8_t config); void DSConfigureWRAM(struct DSMemory*, uint8_t config);
void DSConfigureExternalMemory(struct DS*, uint16_t config);
#endif #endif

View File

@ -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

View File

@ -279,6 +279,7 @@ void DS7Reset(struct ARMCore* cpu) {
struct DSCartridge* header = ds->romVf->map(ds->romVf, sizeof(*header), MAP_READ); struct DSCartridge* header = ds->romVf->map(ds->romVf, sizeof(*header), MAP_READ);
if (header) { if (header) {
memcpy(&ds->memory.ram[0x3FFE00 >> 2], header, 0x170); memcpy(&ds->memory.ram[0x3FFE00 >> 2], header, 0x170);
DS7IOWrite32(ds, DS_REG_ROMCNT_LO, header->busTiming | 0x2700000);
// TODO: Error check // TODO: Error check
ds->romVf->seek(ds->romVf, header->arm7Offset, SEEK_SET); ds->romVf->seek(ds->romVf, header->arm7Offset, SEEK_SET);
uint32_t base = header->arm7Base - DS_BASE_RAM; uint32_t base = header->arm7Base - DS_BASE_RAM;

View File

@ -8,6 +8,7 @@
#include <mgba/core/interface.h> #include <mgba/core/interface.h>
#include <mgba/internal/ds/ds.h> #include <mgba/internal/ds/ds.h>
#include <mgba/internal/ds/ipc.h> #include <mgba/internal/ds/ipc.h>
#include <mgba/internal/ds/slot1.h>
#include <mgba/internal/ds/spi.h> #include <mgba/internal/ds/spi.h>
mLOG_DEFINE_CATEGORY(DS_IO, "DS I/O"); 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; break;
// Cart bus // Cart bus
case DS_REG_SLOT1CNT_LO: case DS_REG_AUXSPICNT:
mLOG(DS_IO, STUB, "ROM control not implemented"); if (dscore->memory.slot1Access) {
value &= 0x7FFF; 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; break;
// Interrupts // Interrupts
@ -313,6 +343,8 @@ uint16_t DS7IORead(struct DS* ds, uint32_t address) {
case DS7_REG_SPIDATA: case DS7_REG_SPIDATA:
case DS_REG_IPCSYNC: case DS_REG_IPCSYNC:
case DS_REG_IPCFIFOCNT: case DS_REG_IPCFIFOCNT:
case DS_REG_ROMCNT_LO:
case DS_REG_ROMCNT_HI:
case DS_REG_IME: case DS_REG_IME:
case 0x20A: case 0x20A:
case DS_REG_IE_LO: case DS_REG_IE_LO:
@ -334,6 +366,13 @@ uint32_t DS7IORead32(struct DS* ds, uint32_t address) {
switch (address) { switch (address) {
case DS_REG_IPCFIFORECV_LO: case DS_REG_IPCFIFORECV_LO:
return DSIPCReadFIFO(&ds->ds7); 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: default:
return DS7IORead(ds, address & 0x00FFFFFC) | (DS7IORead(ds, (address & 0x00FFFFFC) | 2) << 16); 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)); memset(ds->memory.io9, 0, sizeof(ds->memory.io9));
ds->memory.io9[DS_REG_IPCFIFOCNT >> 1] = 0x0101; ds->memory.io9[DS_REG_IPCFIFOCNT >> 1] = 0x0101;
ds->memory.io9[DS_REG_POSTFLG >> 1] = 0x0001; 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) { 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); DSVideoConfigureVRAM(&ds->memory, 8, value >> 8);
break; break;
case DS9_REG_EXMEMCNT:
value &= 0xE8FF;
DSConfigureExternalMemory(ds, value);
break;
// Math // Math
case DS9_REG_DIVCNT: case DS9_REG_DIVCNT:
value = _scheduleDiv(ds, value); 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_TM3CNT_HI:
case DS_REG_IPCSYNC: case DS_REG_IPCSYNC:
case DS_REG_IPCFIFOCNT: case DS_REG_IPCFIFOCNT:
case DS_REG_ROMCNT_LO:
case DS_REG_ROMCNT_HI:
case DS_REG_IME: case DS_REG_IME:
case 0x20A: case 0x20A:
case DS_REG_IE_LO: case DS_REG_IE_LO:
@ -527,6 +573,13 @@ uint32_t DS9IORead32(struct DS* ds, uint32_t address) {
switch (address) { switch (address) {
case DS_REG_IPCFIFORECV_LO: case DS_REG_IPCFIFORECV_LO:
return DSIPCReadFIFO(&ds->ds9); 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: default:
return DS9IORead(ds, address & 0x00FFFFFC) | (DS9IORead(ds, (address & 0x00FFFFFC) | 2) << 16); return DS9IORead(ds, address & 0x00FFFFFC) | (DS9IORead(ds, (address & 0x00FFFFFC) | 2) << 16);
} }

View File

@ -176,6 +176,11 @@ void DSMemoryReset(struct DS* ds) {
ds->memory.wramSize9 = 0; ds->memory.wramSize9 = 0;
ds->memory.wramBase9 = NULL; 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, 0, 0);
DSVideoConfigureVRAM(&ds->memory, 1, 0); DSVideoConfigureVRAM(&ds->memory, 1, 0);
DSVideoConfigureVRAM(&ds->memory, 2, 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) { static unsigned _selectVRAM(struct DSMemory* memory, uint32_t offset) {
unsigned mask = 0; unsigned mask = 0;
offset &= 0x3FF; offset &= 0x3FF;

86
src/ds/slot1.c Normal file
View File

@ -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;
}