mirror of
https://github.com/mlaux/gb6.git
synced 2026-04-19 17:16:48 +00:00
Merge pull request #3 from tannerfokkens-maker/rtc-support
Add MBC3 RTC support using Mac system time
This commit is contained in:
@@ -7,6 +7,10 @@
|
||||
#include "dmg.h"
|
||||
#include "rom.h"
|
||||
|
||||
#include <OSUtils.h>
|
||||
|
||||
#define RTC_SAVE_SIZE 48
|
||||
|
||||
static int is_mbc2(int type)
|
||||
{
|
||||
return type == 0x05 || type == 0x06;
|
||||
@@ -69,8 +73,73 @@ static int mbc2_write(struct mbc *mbc, struct dmg *dmg, u16 addr, u8 data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get current Mac system time in seconds since Jan 1, 1904
|
||||
static u32 get_mac_seconds(void)
|
||||
{
|
||||
unsigned long secs;
|
||||
GetDateTime(&secs);
|
||||
return (u32)secs;
|
||||
}
|
||||
|
||||
// Reset RTC reference point to current time with current register values
|
||||
static void mbc3_reset_rtc_reference(struct mbc *mbc)
|
||||
{
|
||||
mbc->rtc_base_secs = get_mac_seconds();
|
||||
mbc->rtc_base_days = ((mbc->rtc_dh & 0x01) << 8) | mbc->rtc_dl;
|
||||
mbc->rtc_base_h = mbc->rtc_h;
|
||||
mbc->rtc_base_m = mbc->rtc_m;
|
||||
mbc->rtc_base_s = mbc->rtc_s;
|
||||
mbc->rtc_halted = (mbc->rtc_dh & 0x40) ? 1 : 0;
|
||||
}
|
||||
|
||||
// Compute current RTC values from elapsed time since reference
|
||||
static void mbc3_update_rtc(struct mbc *mbc)
|
||||
{
|
||||
u32 elapsed_secs;
|
||||
u32 total_secs;
|
||||
u16 days;
|
||||
|
||||
// If halted, don't update - registers stay at their set values
|
||||
if (mbc->rtc_halted) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate seconds elapsed since reference point
|
||||
elapsed_secs = get_mac_seconds() - mbc->rtc_base_secs;
|
||||
|
||||
// Convert base time to total seconds
|
||||
total_secs = (u32)mbc->rtc_base_days * 86400UL
|
||||
+ (u32)mbc->rtc_base_h * 3600UL
|
||||
+ (u32)mbc->rtc_base_m * 60UL
|
||||
+ (u32)mbc->rtc_base_s;
|
||||
|
||||
// Add elapsed time
|
||||
total_secs += elapsed_secs;
|
||||
|
||||
// Extract components
|
||||
mbc->rtc_s = total_secs % 60;
|
||||
total_secs /= 60;
|
||||
mbc->rtc_m = total_secs % 60;
|
||||
total_secs /= 60;
|
||||
mbc->rtc_h = total_secs % 24;
|
||||
total_secs /= 24;
|
||||
days = (u16)total_secs;
|
||||
|
||||
// Day counter is 9 bits (0-511), with carry flag if overflow
|
||||
mbc->rtc_dl = days & 0xff;
|
||||
mbc->rtc_dh = (mbc->rtc_dh & 0x40) // preserve halt flag
|
||||
| ((days >> 8) & 0x01) // day counter bit 8
|
||||
| ((days > 511) ? 0x80 : 0); // carry flag if >511 days
|
||||
}
|
||||
|
||||
static void mbc3_latch_rtc(struct mbc *mbc)
|
||||
{
|
||||
// First compute current RTC values from Mac time
|
||||
if (mbc->has_rtc) {
|
||||
mbc3_update_rtc(mbc);
|
||||
}
|
||||
|
||||
// Then copy to latched registers
|
||||
mbc->rtc_latched[0] = mbc->rtc_s;
|
||||
mbc->rtc_latched[1] = mbc->rtc_m;
|
||||
mbc->rtc_latched[2] = mbc->rtc_h;
|
||||
@@ -202,6 +271,10 @@ struct mbc *mbc_new(int type)
|
||||
// 0x13: MBC3+RAM+BATTERY
|
||||
mbc.has_rtc = (type == 0x0f || type == 0x10);
|
||||
mbc.has_battery = (type == 0x0f || type == 0x10 || type == 0x13);
|
||||
// Initialize RTC base timestamp (will be overwritten by mbc_load_ram if save exists)
|
||||
if (mbc.has_rtc) {
|
||||
mbc.rtc_base_secs = get_mac_seconds();
|
||||
}
|
||||
} else if (is_mbc5(type)) {
|
||||
// 0x19: MBC5
|
||||
// 0x1a: MBC5+RAM
|
||||
@@ -277,13 +350,29 @@ int mbc_ram_write(struct mbc *mbc, u16 addr, u8 data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// RTC register write
|
||||
// RTC register write - update register and reset reference point
|
||||
switch (mbc->rtc_select) {
|
||||
case 0x08: mbc->rtc_s = data; return 1;
|
||||
case 0x09: mbc->rtc_m = data; return 1;
|
||||
case 0x0a: mbc->rtc_h = data; return 1;
|
||||
case 0x0b: mbc->rtc_dl = data; return 1;
|
||||
case 0x0c: mbc->rtc_dh = data; return 1;
|
||||
case 0x08:
|
||||
mbc->rtc_s = data & 0x3f;
|
||||
mbc3_reset_rtc_reference(mbc);
|
||||
return 1;
|
||||
case 0x09:
|
||||
mbc->rtc_m = data & 0x3f;
|
||||
mbc3_reset_rtc_reference(mbc);
|
||||
return 1;
|
||||
case 0x0a:
|
||||
mbc->rtc_h = data & 0x1f;
|
||||
mbc3_reset_rtc_reference(mbc);
|
||||
return 1;
|
||||
case 0x0b:
|
||||
mbc->rtc_dl = data;
|
||||
mbc3_reset_rtc_reference(mbc);
|
||||
return 1;
|
||||
case 0x0c:
|
||||
mbc->rtc_dh = data & 0xc1;
|
||||
mbc->rtc_halted = (data & 0x40) ? 1 : 0;
|
||||
mbc3_reset_rtc_reference(mbc);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -291,6 +380,7 @@ int mbc_ram_write(struct mbc *mbc, u16 addr, u8 data)
|
||||
int mbc_save_ram(struct mbc *mbc, const char *filename)
|
||||
{
|
||||
FILE *fp;
|
||||
u8 rtc_data[RTC_SAVE_SIZE];
|
||||
|
||||
if (!mbc->has_battery) {
|
||||
return 0;
|
||||
@@ -306,6 +396,33 @@ int mbc_save_ram(struct mbc *mbc, const char *filename)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Write RTC data if this cartridge has RTC
|
||||
if (mbc->has_rtc) {
|
||||
// Update RTC to current time before saving
|
||||
mbc3_update_rtc(mbc);
|
||||
|
||||
memset(rtc_data, 0, RTC_SAVE_SIZE);
|
||||
rtc_data[0] = mbc->rtc_s;
|
||||
rtc_data[1] = mbc->rtc_m;
|
||||
rtc_data[2] = mbc->rtc_h;
|
||||
rtc_data[3] = mbc->rtc_dl;
|
||||
rtc_data[4] = mbc->rtc_dh;
|
||||
rtc_data[5] = mbc->rtc_halted;
|
||||
// Store save timestamp at offset 8 (4 bytes, big endian)
|
||||
{
|
||||
u32 ts = get_mac_seconds();
|
||||
rtc_data[8] = (ts >> 24) & 0xff;
|
||||
rtc_data[9] = (ts >> 16) & 0xff;
|
||||
rtc_data[10] = (ts >> 8) & 0xff;
|
||||
rtc_data[11] = ts & 0xff;
|
||||
}
|
||||
|
||||
if (fwrite(rtc_data, 1, RTC_SAVE_SIZE, fp) < RTC_SAVE_SIZE) {
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return 1;
|
||||
}
|
||||
@@ -313,6 +430,8 @@ int mbc_save_ram(struct mbc *mbc, const char *filename)
|
||||
int mbc_load_ram(struct mbc *mbc, const char *filename)
|
||||
{
|
||||
FILE *fp;
|
||||
u8 rtc_data[RTC_SAVE_SIZE];
|
||||
long file_size;
|
||||
|
||||
if (!mbc->has_battery) {
|
||||
return 0;
|
||||
@@ -320,6 +439,16 @@ int mbc_load_ram(struct mbc *mbc, const char *filename)
|
||||
|
||||
fp = fopen(filename, "r");
|
||||
if (!fp) {
|
||||
// No save file exists - initialize RTC to midnight if has_rtc
|
||||
if (mbc->has_rtc) {
|
||||
mbc->rtc_s = 0;
|
||||
mbc->rtc_m = 0;
|
||||
mbc->rtc_h = 0;
|
||||
mbc->rtc_dl = 0;
|
||||
mbc->rtc_dh = 0;
|
||||
mbc->rtc_halted = 0;
|
||||
mbc3_reset_rtc_reference(mbc);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -328,6 +457,35 @@ int mbc_load_ram(struct mbc *mbc, const char *filename)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Try to read RTC data if cartridge has RTC
|
||||
if (mbc->has_rtc) {
|
||||
if (file_size >= RAM_SIZE + RTC_SAVE_SIZE &&
|
||||
fread(rtc_data, 1, RTC_SAVE_SIZE, fp) == RTC_SAVE_SIZE) {
|
||||
// New format with RTC data
|
||||
u32 save_timestamp;
|
||||
|
||||
mbc->rtc_s = rtc_data[0];
|
||||
mbc->rtc_m = rtc_data[1];
|
||||
mbc->rtc_h = rtc_data[2];
|
||||
mbc->rtc_dl = rtc_data[3];
|
||||
mbc->rtc_dh = rtc_data[4];
|
||||
mbc->rtc_halted = rtc_data[5];
|
||||
|
||||
// Read save timestamp (big endian)
|
||||
save_timestamp = ((u32)rtc_data[8] << 24)
|
||||
| ((u32)rtc_data[9] << 16)
|
||||
| ((u32)rtc_data[10] << 8)
|
||||
| (u32)rtc_data[11];
|
||||
|
||||
// Set base to the saved timestamp so elapsed time is automatically added
|
||||
mbc->rtc_base_secs = save_timestamp;
|
||||
mbc->rtc_base_days = ((mbc->rtc_dh & 0x01) << 8) | mbc->rtc_dl;
|
||||
mbc->rtc_base_h = mbc->rtc_h;
|
||||
mbc->rtc_base_m = mbc->rtc_m;
|
||||
mbc->rtc_base_s = mbc->rtc_s;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return 1;
|
||||
}
|
||||
@@ -25,6 +25,14 @@ struct mbc {
|
||||
u8 rtc_latched[5]; // latched values
|
||||
u8 rtc_latch_state; // for 0x00->0x01 latch sequence
|
||||
int rtc_select; // which RTC register is mapped (0x08-0x0c), or -1 for RAM
|
||||
|
||||
// RTC timestamp tracking for on-demand time computation
|
||||
u32 rtc_base_secs; // Mac timestamp when reference was set (secs since Jan 1, 1904)
|
||||
u16 rtc_base_days; // Day counter at reference point
|
||||
u8 rtc_base_h; // Hour at reference point
|
||||
u8 rtc_base_m; // Minute at reference point
|
||||
u8 rtc_base_s; // Second at reference point
|
||||
u8 rtc_halted; // 1 if halt flag (bit 6 of rtc_dh) is set
|
||||
};
|
||||
|
||||
struct mbc *mbc_new(int type);
|
||||
|
||||
Reference in New Issue
Block a user