Add HuC3 to BESS
This commit is contained in:
parent
4346b063f5
commit
5b993ed775
16
BESS.md
16
BESS.md
@ -135,7 +135,7 @@ This block contains an MBC-specific number of 3-byte-long pairs that represent t
|
|||||||
An implementation should parse this block as a series of writes to be made. Values outside the `0x0000-0x7FFF` range are not allowed.
|
An implementation should parse this block as a series of writes to be made. Values outside the `0x0000-0x7FFF` range are not allowed.
|
||||||
|
|
||||||
#### RTC block
|
#### RTC block
|
||||||
The RTC block uses the `'RTC '` identifier, and is an optional block that is used while emulating an MBC3 an RTC. The contents of this block are identical to 64-bit RTC saves from VBA, which are also used by SameBoy and different emulators such as BGB.
|
The RTC block uses the `'RTC '` identifier, and is an optional block that is used while emulating an MBC3 with an RTC. The contents of this block are identical to 64-bit RTC saves from VBA, which are also used by SameBoy and different emulators such as BGB.
|
||||||
|
|
||||||
The length of this block is 0x30 bytes long and it follows the following structure:
|
The length of this block is 0x30 bytes long and it follows the following structure:
|
||||||
|
|
||||||
@ -153,6 +153,20 @@ The length of this block is 0x30 bytes long and it follows the following structu
|
|||||||
| 0x24 | Latched high/overflow/running (1 byte), followed by 3 bytes of padding |
|
| 0x24 | Latched high/overflow/running (1 byte), followed by 3 bytes of padding |
|
||||||
| 0x28 | UNIX timestamp at the time of the save state (64-bit) |
|
| 0x28 | UNIX timestamp at the time of the save state (64-bit) |
|
||||||
|
|
||||||
|
#### HUC3 block
|
||||||
|
The HUC3 block uses the `'HUC3'` identifier, and is an optional block that is used while emulating an HuC3 cartridge to store RTC and alarm information. The contents of this block are identical to HuC3 RTC saves from SameBoy.
|
||||||
|
|
||||||
|
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 | RTC minutes (16-bit) |
|
||||||
|
| 0x0A | RTC days (16-bit) |
|
||||||
|
| 0x0C | Scheduled alarm time minutes (16-bit) |
|
||||||
|
| 0x0E | Scheduled alarm time days (16-bit) |
|
||||||
|
| 0x10 | Alarm enabled flag (8-bits, either 0 or 1) |
|
||||||
|
|
||||||
#### SGB block
|
#### 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.
|
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.
|
||||||
|
10
Core/gb.c
10
Core/gb.c
@ -578,14 +578,6 @@ typedef struct {
|
|||||||
uint8_t padding5[3];
|
uint8_t padding5[3];
|
||||||
} GB_vba_rtc_time_t;
|
} GB_vba_rtc_time_t;
|
||||||
|
|
||||||
typedef struct __attribute__((packed)) {
|
|
||||||
uint64_t last_rtc_second;
|
|
||||||
uint16_t minutes;
|
|
||||||
uint16_t days;
|
|
||||||
uint16_t alarm_minutes, alarm_days;
|
|
||||||
uint8_t alarm_enabled;
|
|
||||||
} GB_huc3_rtc_time_t;
|
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
struct __attribute__((packed)) {
|
struct __attribute__((packed)) {
|
||||||
GB_rtc_time_t rtc_real;
|
GB_rtc_time_t rtc_real;
|
||||||
@ -609,7 +601,7 @@ int GB_save_battery_size(GB_gameboy_t *gb)
|
|||||||
if (gb->mbc_ram_size == 0 && !gb->cartridge_type->has_rtc) return 0; /* Claims to have battery, but has no RAM or RTC */
|
if (gb->mbc_ram_size == 0 && !gb->cartridge_type->has_rtc) return 0; /* Claims to have battery, but has no RAM or RTC */
|
||||||
|
|
||||||
if (gb->cartridge_type->mbc_type == GB_HUC3) {
|
if (gb->cartridge_type->mbc_type == GB_HUC3) {
|
||||||
return gb->mbc_ram_size + sizeof(GB_huc3_rtc_time_t);
|
return gb->mbc_ram_size + sizeof(GB_huc3_rtc_time_t);
|
||||||
}
|
}
|
||||||
GB_rtc_save_t rtc_save_size;
|
GB_rtc_save_t rtc_save_size;
|
||||||
return gb->mbc_ram_size + (gb->cartridge_type->has_rtc ? sizeof(rtc_save_size.vba64) : 0);
|
return gb->mbc_ram_size + (gb->cartridge_type->has_rtc ? sizeof(rtc_save_size.vba64) : 0);
|
||||||
|
@ -101,6 +101,14 @@ typedef union {
|
|||||||
uint8_t data[5];
|
uint8_t data[5];
|
||||||
} GB_rtc_time_t;
|
} GB_rtc_time_t;
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
uint64_t last_rtc_second;
|
||||||
|
uint16_t minutes;
|
||||||
|
uint16_t days;
|
||||||
|
uint16_t alarm_minutes, alarm_days;
|
||||||
|
uint8_t alarm_enabled;
|
||||||
|
} GB_huc3_rtc_time_t;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
// GB_MODEL_DMG_0 = 0x000,
|
// GB_MODEL_DMG_0 = 0x000,
|
||||||
// GB_MODEL_DMG_A = 0x001,
|
// GB_MODEL_DMG_A = 0x001,
|
||||||
|
@ -100,6 +100,12 @@ typedef struct __attribute__((packed)){
|
|||||||
uint64_t last_rtc_second;
|
uint64_t last_rtc_second;
|
||||||
} BESS_RTC_t;
|
} BESS_RTC_t;
|
||||||
|
|
||||||
|
/* Same HuC3 RTC format as used by SameBoy and BGB in battery saves */
|
||||||
|
typedef struct __attribute__((packed)){
|
||||||
|
BESS_block_t header;
|
||||||
|
GB_huc3_rtc_time_t data;
|
||||||
|
} BESS_HUC3_t;
|
||||||
|
|
||||||
typedef struct __attribute__((packed)) {
|
typedef struct __attribute__((packed)) {
|
||||||
uint16_t address;
|
uint16_t address;
|
||||||
uint8_t value;
|
uint8_t value;
|
||||||
@ -208,7 +214,7 @@ static size_t bess_size_for_cartridge(const GB_cartridge_t *cart)
|
|||||||
case GB_HUC1:
|
case GB_HUC1:
|
||||||
return sizeof(BESS_block_t) + 4 * sizeof(BESS_MBC_pair_t);
|
return sizeof(BESS_block_t) + 4 * sizeof(BESS_MBC_pair_t);
|
||||||
case GB_HUC3:
|
case GB_HUC3:
|
||||||
return sizeof(BESS_block_t) + 3 * sizeof(BESS_MBC_pair_t);
|
return sizeof(BESS_block_t) + 3 * sizeof(BESS_MBC_pair_t) + sizeof(BESS_HUC3_t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,7 +239,7 @@ size_t GB_get_save_state_size(GB_gameboy_t *gb)
|
|||||||
+ sizeof(BESS_NAME) - 1
|
+ sizeof(BESS_NAME) - 1
|
||||||
+ sizeof(BESS_XOAM_t)
|
+ sizeof(BESS_XOAM_t)
|
||||||
+ (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/HUC3 block
|
||||||
+ sizeof(BESS_block_t) // END block
|
+ sizeof(BESS_block_t) // END block
|
||||||
+ sizeof(BESS_footer_t);
|
+ sizeof(BESS_footer_t);
|
||||||
}
|
}
|
||||||
@ -582,22 +588,40 @@ static int save_state_internal(GB_gameboy_t *gb, virtual_file_t *file)
|
|||||||
}
|
}
|
||||||
|
|
||||||
save_bess_mbc_block(gb, file);
|
save_bess_mbc_block(gb, file);
|
||||||
if (gb->cartridge_type->has_rtc && gb->cartridge_type->mbc_type == GB_MBC3) {
|
if (gb->cartridge_type->has_rtc) {
|
||||||
BESS_RTC_t bess_rtc = {0,};
|
if (gb->cartridge_type ->mbc_type != GB_HUC3) {
|
||||||
bess_rtc.header = (BESS_block_t){BE32('RTC '), LE32(sizeof(bess_rtc) - sizeof(bess_rtc.header))};
|
BESS_RTC_t bess_rtc = {0,};
|
||||||
bess_rtc.real.seconds = gb->rtc_real.seconds;
|
bess_rtc.header = (BESS_block_t){BE32('RTC '), LE32(sizeof(bess_rtc) - sizeof(bess_rtc.header))};
|
||||||
bess_rtc.real.minutes = gb->rtc_real.minutes;
|
bess_rtc.real.seconds = gb->rtc_real.seconds;
|
||||||
bess_rtc.real.hours = gb->rtc_real.hours;
|
bess_rtc.real.minutes = gb->rtc_real.minutes;
|
||||||
bess_rtc.real.days = gb->rtc_real.days;
|
bess_rtc.real.hours = gb->rtc_real.hours;
|
||||||
bess_rtc.real.high = gb->rtc_real.high;
|
bess_rtc.real.days = gb->rtc_real.days;
|
||||||
bess_rtc.latched.seconds = gb->rtc_latched.seconds;
|
bess_rtc.real.high = gb->rtc_real.high;
|
||||||
bess_rtc.latched.minutes = gb->rtc_latched.minutes;
|
bess_rtc.latched.seconds = gb->rtc_latched.seconds;
|
||||||
bess_rtc.latched.hours = gb->rtc_latched.hours;
|
bess_rtc.latched.minutes = gb->rtc_latched.minutes;
|
||||||
bess_rtc.latched.days = gb->rtc_latched.days;
|
bess_rtc.latched.hours = gb->rtc_latched.hours;
|
||||||
bess_rtc.latched.high = gb->rtc_latched.high;
|
bess_rtc.latched.days = gb->rtc_latched.days;
|
||||||
bess_rtc.last_rtc_second = LE64(gb->last_rtc_second);
|
bess_rtc.latched.high = gb->rtc_latched.high;
|
||||||
if (file->write(file, &bess_rtc, sizeof(bess_rtc)) != sizeof(bess_rtc)) {
|
bess_rtc.last_rtc_second = LE64(gb->last_rtc_second);
|
||||||
goto error;
|
if (file->write(file, &bess_rtc, sizeof(bess_rtc)) != sizeof(bess_rtc)) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
BESS_HUC3_t bess_huc3 = {0,};
|
||||||
|
bess_huc3.header = (BESS_block_t){BE32('HUC3'), LE32(sizeof(bess_huc3) - sizeof(bess_huc3.header))};
|
||||||
|
|
||||||
|
bess_huc3.data = (GB_huc3_rtc_time_t) {
|
||||||
|
LE64(gb->last_rtc_second),
|
||||||
|
LE16(gb->huc3_minutes),
|
||||||
|
LE16(gb->huc3_days),
|
||||||
|
LE16(gb->huc3_alarm_minutes),
|
||||||
|
LE16(gb->huc3_alarm_days),
|
||||||
|
gb->huc3_alarm_enabled,
|
||||||
|
};
|
||||||
|
if (file->write(file, &bess_huc3, sizeof(bess_huc3)) != sizeof(bess_huc3)) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -900,8 +924,8 @@ static int load_bess_save(GB_gameboy_t *gb, virtual_file_t *file, bool is_samebo
|
|||||||
if (!found_core) goto parse_error;
|
if (!found_core) goto parse_error;
|
||||||
BESS_RTC_t bess_rtc;
|
BESS_RTC_t bess_rtc;
|
||||||
if (LE32(block.size) != sizeof(bess_rtc) - sizeof(block)) goto parse_error;
|
if (LE32(block.size) != sizeof(bess_rtc) - sizeof(block)) goto parse_error;
|
||||||
if (gb->cartridge_type->has_rtc && gb->cartridge_type->mbc_type == GB_MBC3) {
|
if (file->read(file, &bess_rtc.header + 1, LE32(block.size)) != LE32(block.size)) goto error;
|
||||||
if (file->read(file, &bess_rtc.header + 1, LE32(block.size)) != LE32(block.size)) goto error;
|
if (gb->cartridge_type->has_rtc && gb->cartridge_type->mbc_type != GB_HUC3) {
|
||||||
gb->rtc_real.seconds = bess_rtc.real.seconds;
|
gb->rtc_real.seconds = bess_rtc.real.seconds;
|
||||||
gb->rtc_real.minutes = bess_rtc.real.minutes;
|
gb->rtc_real.minutes = bess_rtc.real.minutes;
|
||||||
gb->rtc_real.hours = bess_rtc.real.hours;
|
gb->rtc_real.hours = bess_rtc.real.hours;
|
||||||
@ -915,6 +939,20 @@ static int load_bess_save(GB_gameboy_t *gb, virtual_file_t *file, bool is_samebo
|
|||||||
gb->last_rtc_second = LE64(bess_rtc.last_rtc_second);
|
gb->last_rtc_second = LE64(bess_rtc.last_rtc_second);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case BE32('HUC3'):
|
||||||
|
if (!found_core) goto parse_error;
|
||||||
|
BESS_HUC3_t bess_huc3;
|
||||||
|
if (LE32(block.size) != sizeof(bess_huc3) - sizeof(block)) goto parse_error;
|
||||||
|
if (file->read(file, &bess_huc3.header + 1, LE32(block.size)) != LE32(block.size)) goto error;
|
||||||
|
if (gb->cartridge_type->mbc_type == GB_HUC3) {
|
||||||
|
gb->last_rtc_second = LE64(bess_huc3.data.last_rtc_second);
|
||||||
|
gb->huc3_minutes = LE16(bess_huc3.data.minutes);
|
||||||
|
gb->huc3_days = LE16(bess_huc3.data.days);
|
||||||
|
gb->huc3_alarm_minutes = LE16(bess_huc3.data.alarm_minutes);
|
||||||
|
gb->huc3_alarm_days = LE16(bess_huc3.data.alarm_days);
|
||||||
|
gb->huc3_alarm_enabled = bess_huc3.data.alarm_enabled;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case BE32('SGB '):
|
case BE32('SGB '):
|
||||||
if (!found_core) goto parse_error;
|
if (!found_core) goto parse_error;
|
||||||
if (LE32(block.size) != sizeof(BESS_SGB_t) - sizeof(block)) goto parse_error;
|
if (LE32(block.size) != sizeof(BESS_SGB_t) - sizeof(block)) goto parse_error;
|
||||||
|
Loading…
Reference in New Issue
Block a user