Merge branch 'bess' into gbs

This commit is contained in:
Lior Halphon 2021-04-09 23:12:03 +03:00
commit 77384a5f6a
3 changed files with 75 additions and 84 deletions

66
BESS.md
View File

@ -34,18 +34,18 @@ Every block is followed by another blocked, until the END block is reached.
The CORE block uses the `'CORE'` identifier, and is a required block that contains both core state information, as well as basic information about the BESS version used. This block must be the first block, except for the `NAME` block. The CORE block uses the `'CORE'` identifier, and is a required block that contains both core state information, as well as basic information about the BESS version used. This block must be the first block, except for the `NAME` block.
The length of the CORE block is 0x12E bytes, but implementations are expected to ignore any excess bytes. Following the BESS block header, the structure is as follows: The length of the CORE block is 0xCF bytes, but implementations are expected to ignore any excess bytes. Following the BESS block header, the structure is as follows:
| Offset | Content | | Offset | Content |
|--------|----------------------------------------| |--------|---------------------------------------|
| 0x000 | Major BESS version as a 16-bit integer | | 0x00 | Major BESS version as a 16-bit integer |
| 0x002 | Minor BESS version as a 16-bit integer | | 0x02 | Minor BESS version as a 16-bit integer |
Both major and minor versions should be 1. Implementations are expected to reject incompatible majors, but still attempt to read newer minor versions. Both major and minor versions should be 1. Implementations are expected to reject incompatible majors, but still attempt to read newer minor versions.
| Offset | Content | | Offset | Content |
|--------|-----------------------------------------| |--------|----------------------------------------|
| 0x004 | A four-character ASCII model identifier | | 0x04 | A four-character ASCII model identifier |
BESS uses a four-character string to identify Game Boy models: BESS uses a four-character string to identify Game Boy models:
@ -57,18 +57,17 @@ BESS uses a four-character string to identify Game Boy models:
For example; `'GD '` represents a DMG of an unspecified revision, `'S '` represents some model of the SGB family, and `CCE ` represent a CGB using CPU revision E. For example; `'GD '` represents a DMG of an unspecified revision, `'S '` represents some model of the SGB family, and `CCE ` represent a CGB using CPU revision E.
| Offset | Content | | Offset | Content |
|--------|--------------------------------------------------------| |--------|-------------------------------------------------------|
| 0x008 | The value of the PC register | | 0x08 | The value of the PC register |
| 0x00A | The value of the AF register | | 0x0A | The value of the AF register |
| 0x00C | The value of the BC register | | 0x0C | The value of the BC register |
| 0x00E | The value of the DE register | | 0x0E | The value of the DE register |
| 0x010 | The value of the HL register | | 0x10 | The value of the HL register |
| 0x012 | The value of the SP register | | 0x12 | The value of the SP register |
| 0x014 | The value of IME (0 or 1) | | 0x14 | The value of IME (0 or 1) |
| 0x015 | The value of the IE register | | 0x15 | The value of the IE register |
| 0x016 | Execution state (0 = running; 1 = halted; 2 = stopped) | | 0x16 | Execution state (0 = running; 1 = halted; 2 = stopped) |
| 0x017 | The values of every memory-mapped register (128 bytes) | | 0x17 | The values of every memory-mapped register (128 bytes) |
| 0x097 | The contents of HRAM (127 bytes) |
The values of memory-mapped registers should be written 'as-is' to memory as if the actual ROM wrote them, with the following exceptions and note: The values of memory-mapped registers should be written 'as-is' to memory as if the actual ROM wrote them, with the following exceptions and note:
* Unused registers have Don't-Care values which should be ignored * Unused registers have Don't-Care values which should be ignored
@ -83,15 +82,23 @@ The values of memory-mapped registers should be written 'as-is' to memory as if
* Implementation should apply care when ordering the write operations (For example, writes to NR52 must come before writes to the other APU registers) * Implementation should apply care when ordering the write operations (For example, writes to NR52 must come before writes to the other APU registers)
|Offset | Content | |Offset | Content |
|--------|--------------------------------------------------------| |-------|--------------------------------------------------------------------|
| 0x116 | The size of RAM (32-bit integer) | | 0x97 | The size of RAM (32-bit integer) |
| 0x11A | The offset of RAM from file start (32-bit integer) | | 0x9B | The offset of RAM from file start (32-bit integer) |
| 0x11E | The size of VRAM (32-bit integer) | | 0x9F | The size of VRAM (32-bit integer) |
| 0x122 | The offset of VRAM from file start (32-bit integer) | | 0xA3 | The offset of VRAM from file start (32-bit integer) |
| 0x126 | The size of MBC RAM (32-bit integer) | | 0xA7 | The size of MBC RAM (32-bit integer) |
| 0x12A | The offset of MBC RAM from file start (32-bit integer) | | 0xAB | The offset of MBC RAM from file start (32-bit integer) |
| 0xAF | The size of OAM (=0xA0, 32-bit integer) |
| 0xB3 | The offset of OAM from file start (32-bit integer) |
| 0xB7 | The size of HRAM (=0x7F, 32-bit integer) |
| 0xBB | The offset of HRAM from file start (32-bit integer) |
| 0xBF | The size of background palettes (=0x40 or 0, 32-bit integer) |
| 0xC3 | The offset of background palettes from file start (32-bit integer) |
| 0xC7 | The size of object palettes (=0x40 or 0, 32-bit integer) |
| 0xCB | The offset of object palettes from file start (32-bit integer) |
The contents of large RAM sizes are stored outside of BESS structure so data from an implementation's native save state format can be reused. The offsets are absolute offsets from the save state file's start. The contents of large buffers are stored outside of BESS structure so data from an implementation's native save state format can be reused. The offsets are absolute offsets from the save state file's start. Background and object palette sizes must be 0 for models prior to Game Boy Color.
#### NAME block #### NAME block
@ -100,13 +107,10 @@ The NAME block uses the `'NAME'` identifier, and is an optional block that conta
The length of the NAME block is variable, and it only contains the name and version of the originating emulator in ASCII. The length of the NAME block is variable, and it only contains the name and version of the originating emulator in ASCII.
#### OAM block #### XOAM block
The OAM block uses the `'OAM '` identifier, and is a required block that contains the data of OAM. This block length can be either 160 or 256. When 256 bytes of data are used, the addition bytes are used to set the values for the additional OAM range at `0xFEA0-0xFEFF`. Implementation that do not emulate that extra range are free to ignore the excess bytes. The XOAM block uses the `'XOAM'` identifier, and is an optional block that contains the data of extra OAM (addresses `0xFEA0-0xFEFF`). This block length must be `0x60`. Implementations that do not emulate this extra range are free to ignore the excess bytes, and to not create this block.
#### PALS block
The PALS block uses the `'PALS'` identifier, and is an optional block that is only used for GBC-compatible models. The length of this block is 0x80 bytes and it contains the contents of background palette RAM (0x40 bytes) followed by the contents of object palette RAM (another 0x40 bytes).
#### MBC block #### MBC block

View File

@ -200,7 +200,7 @@ void GB_update_cheat(GB_gameboy_t *gb, const GB_cheat_t *_cheat, const char *des
GB_cheat_hash_t **hash = &gb->cheat_hash[hash_addr(cheat->address)]; GB_cheat_hash_t **hash = &gb->cheat_hash[hash_addr(cheat->address)];
for (unsigned i = 0; i < (*hash)->size; i++) { for (unsigned i = 0; i < (*hash)->size; i++) {
if ((*hash)->cheats[i] == cheat) { if ((*hash)->cheats[i] == cheat) {
(*hash)->cheats[i] = (*hash)->cheats[(*hash)->size--]; (*hash)->cheats[i] = (*hash)->cheats[--(*hash)->size];
if ((*hash)->size == 0) { if ((*hash)->size == 0) {
free(*hash); free(*hash);
*hash = NULL; *hash = NULL;

View File

@ -52,23 +52,20 @@ typedef struct __attribute__((packed)) {
uint8_t execution_mode; // 0 = running; 1 = halted; 2 = stopped uint8_t execution_mode; // 0 = running; 1 = halted; 2 = stopped
uint8_t io_registers[0x80]; uint8_t io_registers[0x80];
uint8_t hram[0x7f];
BESS_buffer_t ram; BESS_buffer_t ram;
BESS_buffer_t vram; BESS_buffer_t vram;
BESS_buffer_t mbc_ram; BESS_buffer_t mbc_ram;
BESS_buffer_t oam;
BESS_buffer_t hram;
BESS_buffer_t background_palettes;
BESS_buffer_t sprite_palettes;
} BESS_CORE_t; } BESS_CORE_t;
typedef struct __attribute__((packed)) { typedef struct __attribute__((packed)) {
BESS_block_t header; BESS_block_t header;
uint8_t oam[256]; uint8_t extra_oam[96];
} BESS_OAM_t; } BESS_XOAM_t;
typedef struct __attribute__((packed)) {
BESS_block_t header;
uint8_t background_palettes[0x40];
uint8_t sprite_palettes[0x40];
} BESS_PALS_t;
typedef struct __attribute__((packed)) { typedef struct __attribute__((packed)) {
BESS_block_t header; BESS_block_t header;
@ -233,8 +230,7 @@ size_t GB_get_save_state_size(GB_gameboy_t *gb)
+ sizeof(BESS_CORE_t) + sizeof(BESS_CORE_t)
+ sizeof(BESS_block_t) // NAME + sizeof(BESS_block_t) // NAME
+ sizeof(BESS_NAME) - 1 + sizeof(BESS_NAME) - 1
+ sizeof(BESS_OAM_t) + sizeof(BESS_XOAM_t)
+ (GB_is_cgb(gb)? sizeof(BESS_PALS_t) : 0)
+ (gb->sgb? sizeof(BESS_SGB_t) : 0) + (gb->sgb? sizeof(BESS_SGB_t) : 0)
+ bess_size_for_cartridge(gb->cartridge_type) // MBC & RTC block + bess_size_for_cartridge(gb->cartridge_type) // MBC & RTC block
+ sizeof(BESS_block_t) // END block + sizeof(BESS_block_t) // END block
@ -456,10 +452,12 @@ static int save_state_internal(GB_gameboy_t *gb, virtual_file_t *file)
if (!DUMP_SECTION(gb, file, core_state)) goto error; if (!DUMP_SECTION(gb, file, core_state)) goto error;
if (!DUMP_SECTION(gb, file, dma )) goto error; if (!DUMP_SECTION(gb, file, dma )) goto error;
if (!DUMP_SECTION(gb, file, mbc )) goto error; if (!DUMP_SECTION(gb, file, mbc )) goto error;
uint32_t hram_offset = file->tell(file) + 4;
if (!DUMP_SECTION(gb, file, hram )) goto error; if (!DUMP_SECTION(gb, file, hram )) goto error;
if (!DUMP_SECTION(gb, file, timing )) goto error; if (!DUMP_SECTION(gb, file, timing )) goto error;
if (!DUMP_SECTION(gb, file, apu )) goto error; if (!DUMP_SECTION(gb, file, apu )) goto error;
if (!DUMP_SECTION(gb, file, rtc )) goto error; if (!DUMP_SECTION(gb, file, rtc )) goto error;
uint32_t video_offset = file->tell(file) + 4;
if (!DUMP_SECTION(gb, file, video )) goto error; if (!DUMP_SECTION(gb, file, video )) goto error;
uint32_t sgb_offset = 0; uint32_t sgb_offset = 0;
@ -470,7 +468,7 @@ static int save_state_internal(GB_gameboy_t *gb, virtual_file_t *file)
if (!dump_section(file, gb->sgb, sizeof(*gb->sgb))) goto error; if (!dump_section(file, gb->sgb, sizeof(*gb->sgb))) goto error;
} }
BESS_CORE_t bess_core; BESS_CORE_t bess_core = {0,};
bess_core.mbc_ram.offset = LE32(file->tell(file)); bess_core.mbc_ram.offset = LE32(file->tell(file));
bess_core.mbc_ram.size = LE32(gb->mbc_ram_size); bess_core.mbc_ram.size = LE32(gb->mbc_ram_size);
@ -555,21 +553,30 @@ static int save_state_internal(GB_gameboy_t *gb, virtual_file_t *file)
bess_core.io_registers[GB_IO_DIV] = gb->div_counter >> 8; bess_core.io_registers[GB_IO_DIV] = gb->div_counter >> 8;
bess_core.io_registers[GB_IO_BANK] = gb->boot_rom_finished; bess_core.io_registers[GB_IO_BANK] = gb->boot_rom_finished;
bess_core.io_registers[GB_IO_KEY1] |= gb->cgb_double_speed? 0x80 : 0; bess_core.io_registers[GB_IO_KEY1] |= gb->cgb_double_speed? 0x80 : 0;
memcpy(bess_core.hram, gb->hram, sizeof(gb->hram)); bess_core.hram.size = LE32(sizeof(gb->hram));
bess_core.hram.offset = LE32(hram_offset + offsetof(GB_gameboy_t, hram) - GB_SECTION_OFFSET(hram));
bess_core.oam.size = LE32(sizeof(gb->oam));
bess_core.oam.offset = LE32(video_offset + offsetof(GB_gameboy_t, oam) - GB_SECTION_OFFSET(video));
if (GB_is_cgb(gb)) {
bess_core.background_palettes.size = LE32(sizeof(gb->background_palettes_data));
bess_core.background_palettes.offset = LE32(video_offset + offsetof(GB_gameboy_t, background_palettes_data) - GB_SECTION_OFFSET(video));
bess_core.sprite_palettes.size = LE32(sizeof(gb->sprite_palettes_data));
bess_core.sprite_palettes.offset = LE32(video_offset + offsetof(GB_gameboy_t, sprite_palettes_data) - GB_SECTION_OFFSET(video));
}
if (file->write(file, &bess_core, sizeof(bess_core)) != sizeof(bess_core)) { if (file->write(file, &bess_core, sizeof(bess_core)) != sizeof(bess_core)) {
goto error; goto error;
} }
/* BESS OAM */ /* BESS XOAM */
BESS_OAM_t bess_oam; BESS_XOAM_t bess_xoam = {0,};
bess_oam.header = (BESS_block_t){BE32('OAM '), LE32(sizeof(bess_oam) - sizeof(bess_oam.header))}; bess_xoam.header = (BESS_block_t){BE32('XOAM'), LE32(sizeof(bess_xoam) - sizeof(bess_xoam.header))};
memcpy(bess_oam.oam, gb->oam, sizeof(gb->oam)); if (GB_is_cgb(gb)) {
memcpy(bess_oam.oam + sizeof(gb->oam), gb->extra_oam, sizeof(gb->extra_oam)); memcpy(bess_xoam.extra_oam, gb->extra_oam, sizeof(bess_xoam.extra_oam));
}
if (file->write(file, &bess_oam, sizeof(bess_oam)) != sizeof(bess_oam)) { if (file->write(file, &bess_xoam, sizeof(bess_xoam)) != sizeof(bess_xoam)) {
goto error; goto error;
} }
@ -593,19 +600,6 @@ static int save_state_internal(GB_gameboy_t *gb, virtual_file_t *file)
} }
} }
if (GB_is_cgb(gb)) {
/* BESS PALS */
BESS_PALS_t bess_pals;
bess_pals.header = (BESS_block_t){BE32('PALS'), LE32(sizeof(bess_pals) - sizeof(bess_oam.header))};
memcpy(bess_pals.background_palettes, gb->background_palettes_data, sizeof(bess_pals.background_palettes));
memcpy(bess_pals.sprite_palettes, gb->sprite_palettes_data, sizeof(bess_pals.sprite_palettes));
if (file->write(file, &bess_pals, sizeof(bess_pals)) != sizeof(bess_pals)) {
goto error;
}
}
bool needs_sgb_padding = false; bool needs_sgb_padding = false;
if (gb->sgb) { if (gb->sgb) {
/* BESS SGB */ /* BESS SGB */
@ -808,8 +802,6 @@ static int load_bess_save(GB_gameboy_t *gb, virtual_file_t *file, bool is_samebo
save.halted = core.execution_mode == 1; save.halted = core.execution_mode == 1;
save.stopped = core.execution_mode == 2; save.stopped = core.execution_mode == 2;
memcpy(save.hram, core.hram, sizeof(save.hram));
// CPU related // CPU related
// Determines DMG mode // Determines DMG mode
@ -884,19 +876,10 @@ static int load_bess_save(GB_gameboy_t *gb, virtual_file_t *file, bool is_samebo
file->read(file, emulator_name, LE32(block.size)); file->read(file, emulator_name, LE32(block.size));
} }
break; break;
case BE32('OAM '): case BE32('XOAM'):
if (!found_core) goto parse_error; if (!found_core) goto parse_error;
if (LE32(block.size) != 256 && LE32(block.size) != 160) goto parse_error; if (LE32(block.size) != 96) goto parse_error;
file->read(file, save.oam, sizeof(save.oam));
if (LE32(block.size) == 256) {
file->read(file, save.extra_oam, sizeof(save.extra_oam)); file->read(file, save.extra_oam, sizeof(save.extra_oam));
}
break;
case BE32('PALS'):
if (!found_core) goto parse_error;
if (LE32(block.size) != sizeof(BESS_PALS_t) - sizeof(block)) goto parse_error;
file->read(file, save.background_palettes_data, sizeof(save.background_palettes_data));
file->read(file, save.sprite_palettes_data, sizeof(save.sprite_palettes_data));
break; break;
case BE32('MBC '): case BE32('MBC '):
if (!found_core) goto parse_error; if (!found_core) goto parse_error;
@ -950,6 +933,10 @@ done:
read_bess_buffer(&core.ram, file, gb->ram, gb->ram_size); read_bess_buffer(&core.ram, file, gb->ram, gb->ram_size);
read_bess_buffer(&core.vram, file, gb->vram, gb->vram_size); read_bess_buffer(&core.vram, file, gb->vram, gb->vram_size);
read_bess_buffer(&core.mbc_ram, file, gb->mbc_ram, gb->mbc_ram_size); read_bess_buffer(&core.mbc_ram, file, gb->mbc_ram, gb->mbc_ram_size);
read_bess_buffer(&core.oam, file, gb->oam, sizeof(gb->oam));
read_bess_buffer(&core.hram, file, gb->hram, sizeof(gb->hram));
read_bess_buffer(&core.background_palettes, file, gb->background_palettes_data, sizeof(gb->background_palettes_data));
read_bess_buffer(&core.sprite_palettes, file, gb->sprite_palettes_data, sizeof(gb->sprite_palettes_data));
if (gb->sgb) { if (gb->sgb) {
memset(gb->sgb, 0, sizeof(*gb->sgb)); memset(gb->sgb, 0, sizeof(*gb->sgb));
GB_sgb_load_default_data(gb); GB_sgb_load_default_data(gb);