GB MBC: New MBC7 implementation

This commit is contained in:
Vicki Pfau 2017-06-24 17:45:20 -07:00
parent b399afdf9f
commit 7b543df002
4 changed files with 137 additions and 127 deletions

View File

@ -140,6 +140,7 @@ Misc:
- GB: Trust ROM header for number of SRAM banks (fixes mgba.io/i/726)
- Core: Config values can now be hexadecimal
- GB: Reset with initial state of DIV register
- GB MBC: New MBC7 implementation
0.5.2: (2016-12-31)
Bugfixes:

View File

@ -67,21 +67,23 @@ typedef void (*GBMemoryBankControllerWrite)(struct GB*, uint16_t address, uint8_
typedef uint8_t (*GBMemoryBankControllerRead)(struct GBMemory*, uint16_t address);
DECL_BITFIELD(GBMBC7Field, uint8_t);
DECL_BIT(GBMBC7Field, SK, 6);
DECL_BIT(GBMBC7Field, CS, 7);
DECL_BIT(GBMBC7Field, IO, 1);
DECL_BIT(GBMBC7Field, CLK, 6);
DECL_BIT(GBMBC7Field, DI, 1);
DECL_BIT(GBMBC7Field, DO, 0);
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,
GBMBC7_STATE_DO = 2,
GBMBC7_STATE_EEPROM_EWDS = 0x10,
GBMBC7_STATE_EEPROM_WRAL = 0x11,
GBMBC7_STATE_EEPROM_ERAL = 0x12,
GBMBC7_STATE_EEPROM_EWEN = 0x13,
GBMBC7_STATE_EEPROM_WRITE = 0x14,
GBMBC7_STATE_EEPROM_READ = 0x18,
GBMBC7_STATE_EEPROM_ERASE = 0x1C,
};
struct GBMBC1State {
@ -91,12 +93,13 @@ struct GBMBC1State {
struct GBMBC7State {
enum GBMBC7MachineState state;
uint32_t sr;
uint16_t sr;
uint8_t address;
bool writable;
int srBits;
int command;
GBMBC7Field field;
uint8_t access;
uint8_t latch;
GBMBC7Field eeprom;
};
struct GBPocketCamState {

View File

@ -198,7 +198,7 @@ void GBMBCInit(struct GB* gb) {
case GB_MBC7:
gb->memory.mbcWrite = _GBMBC7;
gb->memory.mbcRead = _GBMBC7Read;
gb->sramSize = GB_SIZE_EXTERNAL_RAM;
gb->sramSize = 0x100;
break;
case GB_MMM01:
mLOG(GB_MBC, WARN, "unimplemented MBC: MMM01");
@ -472,12 +472,25 @@ void _GBMBC6(struct GB* gb, uint16_t address, uint8_t value) {
void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) {
int bank = value & 0x7F;
switch (address >> 13) {
case 0x0:
switch (value) {
default:
case 0:
gb->memory.mbcState.mbc7.access = 0;
break;
case 0xA:
gb->memory.mbcState.mbc7.access |= 1;
break;
}
break;
case 0x1:
GBMBCSwitchBank(gb, bank);
break;
case 0x2:
if (value < 0x10) {
GBMBCSwitchSramBank(gb, value);
if (value == 0x40) {
gb->memory.mbcState.mbc7.access |= 2;
} else {
gb->memory.mbcState.mbc7.access &= ~2;
}
break;
default:
@ -489,17 +502,15 @@ void _GBMBC7(struct GB* gb, uint16_t address, uint8_t value) {
uint8_t _GBMBC7Read(struct GBMemory* memory, uint16_t address) {
struct GBMBC7State* mbc7 = &memory->mbcState.mbc7;
if (mbc7->access != 3) {
return 0xFF;
}
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;
x += 0x81D0;
return x;
}
return 0xFF;
@ -507,7 +518,7 @@ uint8_t _GBMBC7Read(struct GBMemory* memory, uint16_t address) {
if (memory->rotation && memory->rotation->readTiltX) {
int32_t x = -memory->rotation->readTiltX(memory->rotation);
x >>= 21;
x += 2047;
x += 0x81D0;
return x >> 8;
}
return 7;
@ -515,7 +526,7 @@ uint8_t _GBMBC7Read(struct GBMemory* memory, uint16_t address) {
if (memory->rotation && memory->rotation->readTiltY) {
int32_t y = -memory->rotation->readTiltY(memory->rotation);
y >>= 21;
y += 2047;
y += 0x81D0;
return y;
}
return 0xFF;
@ -523,144 +534,142 @@ uint8_t _GBMBC7Read(struct GBMemory* memory, uint16_t address) {
if (memory->rotation && memory->rotation->readTiltY) {
int32_t y = -memory->rotation->readTiltY(memory->rotation);
y >>= 21;
y += 2047;
y += 0x81D0;
return y >> 8;
}
return 7;
case 0x60:
return 0;
case 0x80:
return (mbc7->sr >> 16) & 1;
return mbc7->eeprom;
default:
return 0xFF;
}
}
void GBMBC7Write(struct GBMemory* memory, uint16_t address, uint8_t value) {
if ((address & 0xF0) != 0x80) {
struct GBMBC7State* mbc7 = &memory->mbcState.mbc7;
if (mbc7->access != 3) {
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;
switch (address & 0xF0) {
case 0x00:
mbc7->latch = (value & 0x55) == 0x55;
return;
case 0x10:
mbc7->latch |= (value & 0xAA);
if (mbc7->latch == 0xFF && memory->rotation && memory->rotation->sample) {
memory->rotation->sample(memory->rotation);
}
mbc7->sr = 0x1FFFF;
mbc7->state = GBMBC7_STATE_NULL;
} else {
mbc7->latch = 0;
return;
default:
mLOG(GB_MBC, STUB, "MBC7 unknown register: %04X:%02X", address, value);
return;
case 0x80:
break;
}
GBMBC7Field old = memory->mbcState.mbc7.eeprom;
value = GBMBC7FieldFillDO(value); // Hi-Z
if (!GBMBC7FieldIsCS(old) && GBMBC7FieldIsCS(value)) {
mbc7->state = GBMBC7_STATE_IDLE;
}
}
if (!GBMBC7FieldIsSK(old) && GBMBC7FieldIsSK(value)) {
if (mbc7->state > GBMBC7_STATE_IDLE && mbc7->state != GBMBC7_STATE_READ) {
if (!GBMBC7FieldIsCLK(old) && GBMBC7FieldIsCLK(value)) {
if (mbc7->state == GBMBC7_STATE_READ_COMMAND || mbc7->state == GBMBC7_STATE_EEPROM_WRITE || mbc7->state == GBMBC7_STATE_EEPROM_WRAL) {
mbc7->sr <<= 1;
mbc7->sr |= GBMBC7FieldGetIO(value);
mbc7->sr |= GBMBC7FieldGetDI(value);
++mbc7->srBits;
}
switch (mbc7->state) {
case GBMBC7_STATE_IDLE:
if (GBMBC7FieldIsIO(value)) {
if (GBMBC7FieldIsDI(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;
if (mbc7->srBits == 10) {
mbc7->state = 0x10 | (mbc7->sr >> 6);
if (mbc7->state & 0xC) {
mbc7->state &= ~0x3;
}
mbc7->srBits = 0;
mbc7->command = mbc7->sr;
mbc7->address = mbc7->sr & 0x7F;
}
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;
case GBMBC7_STATE_DO:
value = GBMBC7FieldSetDO(value, mbc7->sr >> 15);
mbc7->sr <<= 1;
--mbc7->srBits;
if (!mbc7->srBits) {
mbc7->state = GBMBC7_STATE_IDLE;
}
break;
default:
break;
}
} else if (GBMBC7FieldIsSK(old) && !GBMBC7FieldIsSK(value)) {
if (mbc7->state == GBMBC7_STATE_READ) {
mbc7->sr <<= 1;
++mbc7->srBits;
switch (mbc7->state) {
case GBMBC7_STATE_EEPROM_EWEN:
mbc7->writable = true;
mbc7->state = GBMBC7_STATE_IDLE;
break;
case GBMBC7_STATE_EEPROM_EWDS:
mbc7->writable = false;
mbc7->state = GBMBC7_STATE_IDLE;
break;
case GBMBC7_STATE_EEPROM_WRITE:
if (mbc7->srBits == 16) {
mbc7->srBits = 0;
mbc7->state = GBMBC7_STATE_NULL;
if (mbc7->writable) {
memory->sram[mbc7->address * 2] = mbc7->sr >> 8;
memory->sram[mbc7->address * 2 + 1] = mbc7->sr;
}
mbc7->state = GBMBC7_STATE_IDLE;
}
break;
case GBMBC7_STATE_EEPROM_ERASE:
if (mbc7->writable) {
memory->sram[mbc7->address * 2] = 0xFF;
memory->sram[mbc7->address * 2 + 1] = 0xFF;
}
mbc7->state = GBMBC7_STATE_IDLE;
break;
case GBMBC7_STATE_EEPROM_READ:
mbc7->srBits = 16;
mbc7->sr = memory->sram[mbc7->address * 2] << 8;
mbc7->sr |= memory->sram[mbc7->address * 2 + 1];
mbc7->state = GBMBC7_STATE_DO;
value = GBMBC7FieldClearDO(value);
break;
case GBMBC7_STATE_EEPROM_WRAL:
if (mbc7->srBits == 16) {
if (mbc7->writable) {
int i;
for (i = 0; i < 128; ++i) {
memory->sram[i * 2] = mbc7->sr >> 8;
memory->sram[i * 2 + 1] = mbc7->sr;
}
}
mbc7->state = GBMBC7_STATE_IDLE;
}
break;
case GBMBC7_STATE_EEPROM_ERAL:
if (mbc7->writable) {
int i;
for (i = 0; i < 128; ++i) {
memory->sram[i * 2] = 0xFF;
memory->sram[i * 2 + 1] = 0xFF;
}
}
mbc7->state = GBMBC7_STATE_IDLE;
break;
default:
break;
}
} else if (GBMBC7FieldIsCS(value) && GBMBC7FieldIsCLK(old) && !GBMBC7FieldIsCLK(value)) {
value = GBMBC7FieldSetDO(value, GBMBC7FieldGetDO(old));
}
mbc7->eeprom = value;
}
void _GBHuC3(struct GB* gb, uint16_t address, uint8_t value) {

View File

@ -185,9 +185,6 @@ void _endMode1(struct mTiming* timing, void* context, uint32_t cyclesLate) {
next = GB_VIDEO_MODE_2_LENGTH + (video->p->memory.io[REG_SCX] & 7);
video->mode = 2;
video->modeEvent.callback = _endMode2;
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);
}
} else if (video->ly == GB_VIDEO_VERTICAL_TOTAL_PIXELS) {
video->p->memory.io[REG_LY] = 0;
next = GB_VIDEO_HORIZONTAL_LENGTH - 8;