Added MBC7 BESS support, documented BESS TPP1
This commit is contained in:
parent
abf6e5632c
commit
4f91b19a94
42
BESS.md
42
BESS.md
@ -176,6 +176,48 @@ The length of this block is 0x11 bytes long and it follows the following structu
|
||||
| 0x0E | Scheduled alarm time days (16-bit) |
|
||||
| 0x10 | Alarm enabled flag (8-bits, either 0 or 1) |
|
||||
|
||||
#### TPP1 block
|
||||
The TPP1 block uses the `'TPP1'` identifier, and is an optional block that is used while emulating a TPP1 cartridge to store RTC information. This block can be omitted if the ROM header does not specify the inclusion of a RTC.
|
||||
|
||||
The length of this block is 0x11 bytes long and it follows the following structure:
|
||||
|
||||
| Offset | Content |
|
||||
|--------|-------------------------------------------------------|
|
||||
| 0x00 | UNIX timestamp at the time of the save state (64-bit) |
|
||||
| 0x08 | The current RTC data (4 bytes) |
|
||||
| 0x0C | The latched RTC data (4 bytes) |
|
||||
| 0x10 | The value of the MR4 register (8-bits) |
|
||||
|
||||
|
||||
#### MBC7 block
|
||||
The MBC7 block uses the `'MBC7'` identifier, and is an optional block that is used while emulating an MBC7 cartridge to store the EEPROM communication state and motion control state.
|
||||
|
||||
The length of this block is 0xA bytes long and it follows the following structure:
|
||||
|
||||
| Offset | Content |
|
||||
|--------|-------------------------------------------------------|
|
||||
| 0x00 | Flags (8-bits) |
|
||||
| 0x01 | Argument bits left (8-bits) |
|
||||
| 0x02 | Current EEPROM command (16-bits) |
|
||||
| 0x04 | Pending bits to read (16-bits) |
|
||||
| 0x06 | Latched gyro X value (16-bits) |
|
||||
| 0x08 | Latched gyro Y value (16-bits) |
|
||||
|
||||
The meaning of the individual bits in flags are:
|
||||
* Bit 0: Latch ready; set after writing `0x55` to `0xAX0X` and reset after writing `0xAA` to `0xAX1X`
|
||||
* Bit 1: EEPROM DO line
|
||||
* Bit 2: EEPROM DI line
|
||||
* Bit 3: EEPROM CLK line
|
||||
* Bit 4: EEPROM CS line
|
||||
* Bit 5: EEPROM write enable; set after an `EWEN` command, reset after an `EWDS` command
|
||||
* Bits 6-7: Unused.
|
||||
|
||||
The current EEPROM command field has bits pushed to its LSB first, padded with zeros. For example, if the ROM clocked a single `1` bit, this field should contain `0b1`; if the ROM later clocks a `0` bit, this field should contain `0b10`.
|
||||
|
||||
If the currently transmitted command has an argument, the "Argument bits left" field should contain the number argument bits remaining. Otherwise, it should contain 0.
|
||||
|
||||
The "Pending bits to read" field contains the pending bits waiting to be shifted into the DO signal, MSB first, padded with ones.
|
||||
|
||||
#### SGB block
|
||||
|
||||
The SGB block uses the `'SGB '` identifier, and is an optional block that is only used while emulating an SGB or SGB2 *and* SGB commands enabled. Implementations must not save this block on other models or when SGB commands are disabled, and should assume SGB commands are disabled if this block is missing.
|
||||
|
@ -479,7 +479,7 @@ struct GB_gameboy_internal_s {
|
||||
bool eeprom_cs:1;
|
||||
uint16_t eeprom_command:11;
|
||||
uint16_t read_bits;
|
||||
uint8_t bits_countdown:5;
|
||||
uint8_t argument_bits_left:5;
|
||||
bool secondary_ram_enable:1;
|
||||
bool eeprom_write_enabled:1;
|
||||
} mbc7;
|
||||
|
@ -1064,7 +1064,7 @@ static void write_mbc7_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
gb->mbc7.eeprom_do = gb->mbc7.read_bits >> 15;
|
||||
gb->mbc7.read_bits <<= 1;
|
||||
gb->mbc7.read_bits |= 1;
|
||||
if (gb->mbc7.bits_countdown == 0) {
|
||||
if (gb->mbc7.argument_bits_left == 0) {
|
||||
/* Not transferring extra bits for a command*/
|
||||
gb->mbc7.eeprom_command <<= 1;
|
||||
gb->mbc7.eeprom_command |= gb->mbc7.eeprom_di;
|
||||
@ -1095,7 +1095,7 @@ static void write_mbc7_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
if (gb->mbc7.eeprom_write_enabled) {
|
||||
((uint16_t *)gb->mbc_ram)[gb->mbc7.eeprom_command & 0x7F] = 0;
|
||||
}
|
||||
gb->mbc7.bits_countdown = 16;
|
||||
gb->mbc7.argument_bits_left = 16;
|
||||
// We still need to process this command, don't erase eeprom_command
|
||||
break;
|
||||
case 0xC:
|
||||
@ -1123,7 +1123,7 @@ static void write_mbc7_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
if (gb->mbc7.eeprom_write_enabled) {
|
||||
memset(gb->mbc_ram, 0, gb->mbc_ram_size);
|
||||
}
|
||||
gb->mbc7.bits_countdown = 16;
|
||||
gb->mbc7.argument_bits_left = 16;
|
||||
// We still need to process this command, don't erase eeprom_command
|
||||
break;
|
||||
}
|
||||
@ -1131,10 +1131,10 @@ static void write_mbc7_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
}
|
||||
else {
|
||||
// We're shifting in extra bits for a WRITE/WRAL command
|
||||
gb->mbc7.bits_countdown--;
|
||||
gb->mbc7.argument_bits_left--;
|
||||
gb->mbc7.eeprom_do = true;
|
||||
if (gb->mbc7.eeprom_di) {
|
||||
uint16_t bit = LE16(1 << gb->mbc7.bits_countdown);
|
||||
uint16_t bit = LE16(1 << gb->mbc7.argument_bits_left);
|
||||
if (gb->mbc7.eeprom_command & 0x100) {
|
||||
// WRITE
|
||||
((uint16_t *)gb->mbc_ram)[gb->mbc7.eeprom_command & 0x7F] |= bit;
|
||||
@ -1146,7 +1146,7 @@ static void write_mbc7_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (gb->mbc7.bits_countdown == 0) { // We're done
|
||||
if (gb->mbc7.argument_bits_left == 0) { // We're done
|
||||
gb->mbc7.eeprom_command = 0;
|
||||
gb->mbc7.read_bits = (gb->mbc7.eeprom_command & 0x100)? 0xFF : 0x3FFF; // Emulate some time to settle
|
||||
}
|
||||
|
@ -119,6 +119,28 @@ typedef struct __attribute__((packed)){
|
||||
GB_huc3_rtc_time_t data;
|
||||
} BESS_HUC3_t;
|
||||
|
||||
typedef struct __attribute__((packed)) {
|
||||
BESS_block_t header;
|
||||
|
||||
// Flags
|
||||
bool latch_ready:1;
|
||||
bool eeprom_do:1;
|
||||
bool eeprom_di:1;
|
||||
bool eeprom_clk:1;
|
||||
bool eeprom_cs:1;
|
||||
bool eeprom_write_enabled:1;
|
||||
uint8_t padding:2;
|
||||
|
||||
uint8_t argument_bits_left;
|
||||
|
||||
uint16_t eeprom_command;
|
||||
uint16_t read_bits;
|
||||
|
||||
uint16_t x_latch;
|
||||
uint16_t y_latch;
|
||||
|
||||
} BESS_MBC7_t;
|
||||
|
||||
typedef struct __attribute__((packed)){
|
||||
BESS_block_t header;
|
||||
uint64_t last_rtc_second;
|
||||
@ -232,6 +254,8 @@ static size_t bess_size_for_cartridge(const GB_cartridge_t *cart)
|
||||
return sizeof(BESS_block_t) + 3 * sizeof(BESS_MBC_pair_t) + (cart->has_rtc? sizeof(BESS_RTC_t) : 0);
|
||||
case GB_MBC5:
|
||||
return sizeof(BESS_block_t) + 4 * sizeof(BESS_MBC_pair_t);
|
||||
case GB_MBC7:
|
||||
return sizeof(BESS_block_t) + 3 * sizeof(BESS_MBC_pair_t) + sizeof(BESS_MBC7_t);
|
||||
case GB_MMM01:
|
||||
return sizeof(BESS_block_t) + 8 * sizeof(BESS_MBC_pair_t);
|
||||
case GB_HUC1:
|
||||
@ -270,7 +294,7 @@ size_t GB_get_save_state_size(GB_gameboy_t *gb)
|
||||
+ sizeof(BESS_CORE_t)
|
||||
+ sizeof(BESS_XOAM_t)
|
||||
+ (gb->sgb? sizeof(BESS_SGB_t) : 0)
|
||||
+ bess_size_for_cartridge(gb->cartridge_type) // MBC & RTC/HUC3/TPP1 block
|
||||
+ bess_size_for_cartridge(gb->cartridge_type) // MBC & RTC/HUC3/TPP1/MBC7 block
|
||||
+ sizeof(BESS_block_t) // END block
|
||||
+ sizeof(BESS_footer_t);
|
||||
}
|
||||
@ -435,6 +459,12 @@ static int save_bess_mbc_block(GB_gameboy_t *gb, virtual_file_t *file)
|
||||
pairs[3] = (BESS_MBC_pair_t){LE16(0x4000), gb->mbc5.ram_bank};
|
||||
mbc_block.size = 4 * sizeof(pairs[0]);
|
||||
break;
|
||||
case GB_MBC7:
|
||||
pairs[0] = (BESS_MBC_pair_t){LE16(0x0000), gb->mbc_ram_enable? 0xA : 0x0};
|
||||
pairs[1] = (BESS_MBC_pair_t){LE16(0x2000), gb->mbc7.rom_bank};
|
||||
pairs[2] = (BESS_MBC_pair_t){LE16(0x4000), gb->mbc7.secondary_ram_enable? 0x40 : 0};
|
||||
mbc_block.size = 3 * sizeof(pairs[0]);
|
||||
break;
|
||||
case GB_MMM01:
|
||||
pairs[0] = (BESS_MBC_pair_t){LE16(0x2000), (gb->mmm01.rom_bank_low & (gb->mmm01.rom_bank_mask << 1)) | (gb->mmm01.rom_bank_mid << 5)};
|
||||
pairs[1] = (BESS_MBC_pair_t){LE16(0x6000), gb->mmm01.mbc1_mode | (gb->mmm01.rom_bank_mask << 2) | (gb->mmm01.multiplex_mode << 6)};
|
||||
@ -700,6 +730,30 @@ static int save_state_internal(GB_gameboy_t *gb, virtual_file_t *file, bool appe
|
||||
}
|
||||
}
|
||||
|
||||
if (gb->cartridge_type ->mbc_type == GB_MBC7) {
|
||||
BESS_MBC7_t bess_mbc7 = {
|
||||
.latch_ready = gb->mbc7.latch_ready,
|
||||
.eeprom_do = gb->mbc7.eeprom_do,
|
||||
.eeprom_di = gb->mbc7.eeprom_di,
|
||||
.eeprom_clk = gb->mbc7.eeprom_clk,
|
||||
.eeprom_cs = gb->mbc7.eeprom_cs,
|
||||
.eeprom_write_enabled = gb->mbc7.eeprom_write_enabled,
|
||||
|
||||
.argument_bits_left = gb->mbc7.argument_bits_left,
|
||||
|
||||
.eeprom_command = LE16(gb->mbc7.eeprom_command),
|
||||
.read_bits = LE16(gb->mbc7.read_bits),
|
||||
|
||||
.x_latch = LE16(gb->mbc7.x_latch),
|
||||
.y_latch = LE16(gb->mbc7.y_latch),
|
||||
};
|
||||
bess_mbc7.header = (BESS_block_t){BE32('MBC7'), LE32(sizeof(bess_mbc7) - sizeof(bess_mbc7.header))};
|
||||
|
||||
if (file->write(file, &bess_mbc7, sizeof(bess_mbc7)) != sizeof(bess_mbc7)) {
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
bool needs_sgb_padding = false;
|
||||
if (gb->sgb) {
|
||||
/* BESS SGB */
|
||||
@ -1089,6 +1143,29 @@ static int load_bess_save(GB_gameboy_t *gb, virtual_file_t *file, bool is_samebo
|
||||
save.rtc_latched.data[i ^ 3] = bess_tpp1.latched_rtc_data[i];
|
||||
}
|
||||
save.tpp1_mr4 = bess_tpp1.mr4;
|
||||
break;
|
||||
case BE32('MBC7'):
|
||||
if (!found_core) goto parse_error;
|
||||
BESS_MBC7_t bess_mbc7;
|
||||
if (LE32(block.size) != sizeof(bess_mbc7) - sizeof(block)) goto parse_error;
|
||||
if (file->read(file, &bess_mbc7.header + 1, LE32(block.size)) != LE32(block.size)) goto error;
|
||||
if (gb->cartridge_type->mbc_type != GB_MBC7) break;
|
||||
|
||||
save.mbc7.latch_ready = bess_mbc7.latch_ready;
|
||||
save.mbc7.eeprom_do = bess_mbc7.eeprom_do;
|
||||
save.mbc7.eeprom_di = bess_mbc7.eeprom_di;
|
||||
save.mbc7.eeprom_clk = bess_mbc7.eeprom_clk;
|
||||
save.mbc7.eeprom_cs = bess_mbc7.eeprom_cs;
|
||||
save.mbc7.eeprom_write_enabled = bess_mbc7.eeprom_write_enabled;
|
||||
|
||||
save.mbc7.argument_bits_left = bess_mbc7.argument_bits_left;
|
||||
|
||||
save.mbc7.eeprom_command = LE16(bess_mbc7.eeprom_command);
|
||||
save.mbc7.read_bits = LE16(bess_mbc7.read_bits);
|
||||
|
||||
save.mbc7.x_latch = LE16(bess_mbc7.x_latch);
|
||||
save.mbc7.y_latch = LE16(bess_mbc7.y_latch);
|
||||
|
||||
break;
|
||||
case BE32('SGB '):
|
||||
if (!found_core) goto parse_error;
|
||||
|
@ -879,7 +879,7 @@ static void ld_##dhl##_##y(GB_gameboy_t *gb, uint8_t opcode) \
|
||||
cycle_write(gb, gb->hl, gb->y); \
|
||||
}
|
||||
|
||||
LD_X_Y(b,c) LD_X_Y(b,d) LD_X_Y(b,e) LD_X_Y(b,h) LD_X_Y(b,l) LD_X_DHL(b) LD_X_Y(b,a)
|
||||
LD_X_Y(b,c) LD_X_Y(b,d) LD_X_Y(b,e) LD_X_Y(b,h) LD_X_Y(b,l) LD_X_DHL(b) LD_X_Y(b,a)
|
||||
LD_X_Y(c,b) LD_X_Y(c,d) LD_X_Y(c,e) LD_X_Y(c,h) LD_X_Y(c,l) LD_X_DHL(c) LD_X_Y(c,a)
|
||||
LD_X_Y(d,b) LD_X_Y(d,c) LD_X_Y(d,e) LD_X_Y(d,h) LD_X_Y(d,l) LD_X_DHL(d) LD_X_Y(d,a)
|
||||
LD_X_Y(e,b) LD_X_Y(e,c) LD_X_Y(e,d) LD_X_Y(e,h) LD_X_Y(e,l) LD_X_DHL(e) LD_X_Y(e,a)
|
||||
|
Loading…
Reference in New Issue
Block a user