DS Slot-1: Initial Slot-1 implementation
@@ -76,20 +76,20 @@
// 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 @@ #include <mgba/core/timing.h>
#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 @@ uint32_t* wram7;
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 @@
uint32_t dtcmBase; uint32_t dtcmSize; uint32_t itcmSize; + + bool slot1Owner; + bool slot2Owner; }; struct DSCoreMemory {@@ -114,6 +119,8 @@
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 LSMDirection direction,
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 @@
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;
@@ -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 @@ value = DSIPCWriteFIFOCNT(dscore, value);
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 @@ case DS7_REG_SPICNT:
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) {@@ -365,6 +404,11 @@ case DS9_REG_VRAMCNT_H:
value &= 0x9F9F; DSVideoConfigureVRAM(&ds->memory, 7, value & 0xFF); DSVideoConfigureVRAM(&ds->memory, 8, value >> 8); + break; + + case DS9_REG_EXMEMCNT: + value &= 0xE8FF; + DSConfigureExternalMemory(ds, value); break; // Math@@ -482,6 +526,8 @@ case DS_REG_TM2CNT_HI:
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 @@ ds->memory.wramBase7 = ds->memory.wram;
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);@@ -1259,6 +1264,16 @@ memory->wramSize9 = 0;
memory->wramBase9 = NULL; break; } +} + +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) {
@@ -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; +}