From d86939b5b6d36f3c3b6e58e4bc256af09e422689 Mon Sep 17 00:00:00 2001 From: Jeffrey Pfau Date: Thu, 18 Feb 2016 21:51:27 -0800 Subject: [PATCH] GB Memory: Support MBC7 --- src/gb/memory.c | 200 +++++++++++++++++++++++++++++++++++++++++++++++- src/gb/memory.h | 35 +++++++++ src/gb/video.c | 3 + 3 files changed, 236 insertions(+), 2 deletions(-) diff --git a/src/gb/memory.c b/src/gb/memory.c index c515cf9e9..07fbe49ed 100644 --- a/src/gb/memory.c +++ b/src/gb/memory.c @@ -30,6 +30,8 @@ static void _GBMBC3(struct GBMemory*, uint16_t address, uint8_t value); static void _GBMBC5(struct GBMemory*, uint16_t address, uint8_t value); static void _GBMBC6(struct GBMemory*, uint16_t address, uint8_t value); static void _GBMBC7(struct GBMemory*, uint16_t address, uint8_t value); +static uint8_t _GBMBC7Read(struct GBMemory*, uint16_t address); +static void _GBMBC7Write(struct GBMemory*, uint16_t address, uint8_t value); static void GBSetActiveRegion(struct LR35902Core* cpu, uint16_t address) { // TODO @@ -134,6 +136,7 @@ void GBMemoryReset(struct GB* gb) { case 0x22: gb->memory.mbc = _GBMBC7; gb->memory.mbcType = GB_MBC7; + memset(&gb->memory.mbcState.mbc7, 0, sizeof(gb->memory.mbcState.mbc7)); break; } @@ -174,6 +177,8 @@ uint8_t GBLoad8(struct LR35902Core* cpu, uint16_t address) { return gb->memory.rtcRegs[memory->activeRtcReg]; } else if (memory->sramAccess) { return gb->memory.sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)]; + } else if (memory->mbcType == GB_MBC7) { + return _GBMBC7Read(memory, address); } return 0xFF; case GB_REGION_WORKING_RAM_BANK0: @@ -230,6 +235,8 @@ void GBStore8(struct LR35902Core* cpu, uint16_t address, int8_t value) { gb->memory.rtcRegs[memory->activeRtcReg] = value; } else if (memory->sramAccess) { gb->memory.sramBank[address & (GB_SIZE_EXTERNAL_RAM - 1)] = value; + } else if (gb->memory.mbcType == GB_MBC7) { + _GBMBC7Write(&gb->memory, address, value); } return; case GB_REGION_WORKING_RAM_BANK0: @@ -525,6 +532,195 @@ void _GBMBC6(struct GBMemory* memory, uint16_t address, uint8_t value) { } void _GBMBC7(struct GBMemory* memory, uint16_t address, uint8_t value) { - // TODO - mLOG(GB_MBC, STUB, "MBC7 unimplemented"); + int bank = value & 0x7F; + switch (address >> 13) { + case 0x1: + _switchBank(memory, bank); + break; + case 0x2: + if (value < 0x10) { + _switchSramBank(memory, value); + } + break; + default: + // TODO + mLOG(GB_MBC, STUB, "MBC7 unknown address: %04X:%02X", address, value); + break; + } +} + +uint8_t _GBMBC7Read(struct GBMemory* memory, uint16_t address) { + struct GBMBC7State* mbc7 = &memory->mbcState.mbc7; + switch (address & 0xF0) { + case 0x00: + case 0x10: + case 0x60: + case 0x70: + return 0; + case 0x20: + if (memory->rotation && memory->rotation->readTiltX) { + int32_t x = -memory->rotation->readTiltX(memory->rotation); + x >>= 21; + x += 2047; + return x; + } + return 0xFF; + case 0x30: + if (memory->rotation && memory->rotation->readTiltX) { + int32_t x = -memory->rotation->readTiltX(memory->rotation); + x >>= 21; + x += 2047; + return x >> 8; + } + return 7; + case 0x40: + if (memory->rotation && memory->rotation->readTiltY) { + int32_t y = -memory->rotation->readTiltY(memory->rotation); + y >>= 21; + y += 2047; + return y; + } + return 0xFF; + case 0x50: + if (memory->rotation && memory->rotation->readTiltY) { + int32_t y = -memory->rotation->readTiltY(memory->rotation); + y >>= 21; + y += 2047; + return y >> 8; + } + return 7; + case 0x80: + return (mbc7->sr >> 16) & 1; + default: + return 0xFF; + } +} + +void _GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value) { + if ((address & 0xF0) != 0x80) { + return; + } + struct GBMBC7State* mbc7 = &memory->mbcState.mbc7; + GBMBC7Field old = memory->mbcState.mbc7.field; + mbc7->field = GBMBC7FieldClearIO(value); + if (!GBMBC7FieldIsCS(old) && GBMBC7FieldIsCS(value)) { + if (mbc7->state == GBMBC7_STATE_WRITE) { + if (mbc7->writable) { + memory->sramBank[mbc7->address * 2] = mbc7->sr >> 8; + memory->sramBank[mbc7->address * 2 + 1] = mbc7->sr; + } + mbc7->sr = 0x1FFFF; + mbc7->state = GBMBC7_STATE_NULL; + } else { + mbc7->state = GBMBC7_STATE_IDLE; + } + } + if (!GBMBC7FieldIsSK(old) && GBMBC7FieldIsSK(value)) { + if (mbc7->state > GBMBC7_STATE_IDLE && mbc7->state != GBMBC7_STATE_READ) { + mbc7->sr <<= 1; + mbc7->sr |= GBMBC7FieldGetIO(value); + ++mbc7->srBits; + } + switch (mbc7->state) { + case GBMBC7_STATE_IDLE: + if (GBMBC7FieldIsIO(value)) { + mbc7->state = GBMBC7_STATE_READ_COMMAND; + mbc7->srBits = 0; + mbc7->sr = 0; + } + break; + case GBMBC7_STATE_READ_COMMAND: + if (mbc7->srBits == 2) { + mbc7->state = GBMBC7_STATE_READ_ADDRESS; + mbc7->srBits = 0; + mbc7->command = mbc7->sr; + } + break; + case GBMBC7_STATE_READ_ADDRESS: + if (mbc7->srBits == 8) { + mbc7->state = GBMBC7_STATE_COMMAND_0 + mbc7->command; + mbc7->srBits = 0; + mbc7->address = mbc7->sr; + if (mbc7->state == GBMBC7_STATE_COMMAND_0) { + switch (mbc7->address >> 6) { + case 0: + mbc7->writable = false; + mbc7->state = GBMBC7_STATE_NULL; + break; + case 3: + mbc7->writable = true; + mbc7->state = GBMBC7_STATE_NULL; + break; + } + } + } + break; + case GBMBC7_STATE_COMMAND_0: + if (mbc7->srBits == 16) { + switch (mbc7->address >> 6) { + case 0: + mbc7->writable = false; + mbc7->state = GBMBC7_STATE_NULL; + break; + case 1: + mbc7->state = GBMBC7_STATE_WRITE; + if (mbc7->writable) { + int i; + for (i = 0; i < 256; ++i) { + memory->sramBank[i * 2] = mbc7->sr >> 8; + memory->sramBank[i * 2 + 1] = mbc7->sr; + } + } + break; + case 2: + mbc7->state = GBMBC7_STATE_WRITE; + if (mbc7->writable) { + int i; + for (i = 0; i < 256; ++i) { + memory->sramBank[i * 2] = 0xFF; + memory->sramBank[i * 2 + 1] = 0xFF; + } + } + break; + case 3: + mbc7->writable = true; + mbc7->state = GBMBC7_STATE_NULL; + break; + } + } + break; + case GBMBC7_STATE_COMMAND_SR_WRITE: + if (mbc7->srBits == 16) { + mbc7->srBits = 0; + mbc7->state = GBMBC7_STATE_WRITE; + } + break; + case GBMBC7_STATE_COMMAND_SR_READ: + if (mbc7->srBits == 1) { + mbc7->sr = memory->sramBank[mbc7->address * 2] << 8; + mbc7->sr |= memory->sramBank[mbc7->address * 2 + 1]; + mbc7->srBits = 0; + mbc7->state = GBMBC7_STATE_READ; + } + break; + case GBMBC7_STATE_COMMAND_SR_FILL: + if (mbc7->srBits == 16) { + mbc7->sr = 0xFFFF; + mbc7->srBits = 0; + mbc7->state = GBMBC7_STATE_WRITE; + } + break; + default: + break; + } + } else if (GBMBC7FieldIsSK(old) && !GBMBC7FieldIsSK(value)) { + if (mbc7->state == GBMBC7_STATE_READ) { + mbc7->sr <<= 1; + ++mbc7->srBits; + if (mbc7->srBits == 16) { + mbc7->srBits = 0; + mbc7->state = GBMBC7_STATE_NULL; + } + } + } } diff --git a/src/gb/memory.h b/src/gb/memory.h index 9dc525da3..603bb2e16 100644 --- a/src/gb/memory.h +++ b/src/gb/memory.h @@ -70,11 +70,45 @@ enum GBMemoryBankControllerType { struct GBMemory; typedef void (*GBMemoryBankController)(struct GBMemory*, uint16_t address, uint8_t value); +DECL_BITFIELD(GBMBC7Field, uint8_t); +DECL_BIT(GBMBC7Field, SK, 6); +DECL_BIT(GBMBC7Field, CS, 7); +DECL_BIT(GBMBC7Field, IO, 1); + +enum GBMBC7MachineState { + GBMBC7_STATE_NULL = -1, + GBMBC7_STATE_IDLE = 0, + GBMBC7_STATE_READ_COMMAND = 1, + GBMBC7_STATE_READ_ADDRESS = 2, + GBMBC7_STATE_COMMAND_0 = 3, + GBMBC7_STATE_COMMAND_SR_WRITE = 4, + GBMBC7_STATE_COMMAND_SR_READ = 5, + GBMBC7_STATE_COMMAND_SR_FILL = 6, + GBMBC7_STATE_READ = 7, + GBMBC7_STATE_WRITE = 8, +}; + +struct GBMBC7State { + enum GBMBC7MachineState state; + uint32_t sr; + uint8_t address; + bool writable; + int srBits; + int command; + GBMBC7Field field; +}; + +union GBMBCState { + struct GBMBC7State mbc7; +}; + +struct mRotationSource; struct GBMemory { uint8_t* rom; uint8_t* romBank; enum GBMemoryBankControllerType mbcType; GBMemoryBankController mbc; + union GBMBCState mbcState; int currentBank; uint8_t* wram; @@ -110,6 +144,7 @@ struct GBMemory { int rtcLatched; uint8_t rtcRegs[5]; struct mRTCSource* rtc; + struct mRotationSource* rotation; }; void GBMemoryInit(struct GB* gb); diff --git a/src/gb/video.c b/src/gb/video.c index d91303bf7..e9a0758c4 100644 --- a/src/gb/video.c +++ b/src/gb/video.c @@ -161,6 +161,9 @@ int32_t GBVideoProcessEvents(struct GBVideo* video, int32_t cycles) { if (GBRegisterSTATIsLYCIRQ(video->stat) && lyc == video->ly) { video->p->memory.io[REG_IF] |= (1 << GB_IRQ_LCDSTAT); } + if (video->p->memory.mbcType == GB_MBC7 && video->p->memory.rotation && video->p->memory.rotation->sample) { + video->p->memory.rotation->sample(video->p->memory.rotation); + } GBUpdateIRQs(video->p); break; case 2: