mirror of https://github.com/mgba-emu/mgba.git
GB MBC: New MBC7 implementation
This commit is contained in:
parent
b399afdf9f
commit
7b543df002
1
CHANGES
1
CHANGES
|
@ -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:
|
||||
|
|
|
@ -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 {
|
||||
|
|
231
src/gb/mbc.c
231
src/gb/mbc.c
|
@ -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;
|
||||
}
|
||||
mbc7->sr = 0x1FFFF;
|
||||
mbc7->state = GBMBC7_STATE_NULL;
|
||||
} else {
|
||||
mbc7->state = GBMBC7_STATE_IDLE;
|
||||
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->latch = 0;
|
||||
return;
|
||||
default:
|
||||
mLOG(GB_MBC, STUB, "MBC7 unknown register: %04X:%02X", address, value);
|
||||
return;
|
||||
case 0x80:
|
||||
break;
|
||||
}
|
||||
if (!GBMBC7FieldIsSK(old) && GBMBC7FieldIsSK(value)) {
|
||||
if (mbc7->state > GBMBC7_STATE_IDLE && mbc7->state != GBMBC7_STATE_READ) {
|
||||
GBMBC7Field old = memory->mbcState.mbc7.eeprom;
|
||||
value = GBMBC7FieldFillDO(value); // Hi-Z
|
||||
if (!GBMBC7FieldIsCS(old) && GBMBC7FieldIsCS(value)) {
|
||||
mbc7->state = GBMBC7_STATE_IDLE;
|
||||
}
|
||||
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;
|
||||
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;
|
||||
}
|
||||
if (mbc7->srBits == 10) {
|
||||
mbc7->state = 0x10 | (mbc7->sr >> 6);
|
||||
if (mbc7->state & 0xC) {
|
||||
mbc7->state &= ~0x3;
|
||||
}
|
||||
}
|
||||
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;
|
||||
mbc7->address = mbc7->sr & 0x7F;
|
||||
}
|
||||
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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue