HuC-3 alarm clock emulation
This commit is contained in:
parent
369410f370
commit
7af66387de
@ -1,6 +1,6 @@
|
|||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
@interface AppDelegate : NSObject <NSApplicationDelegate>
|
@interface AppDelegate : NSObject <NSApplicationDelegate, NSUserNotificationCenterDelegate>
|
||||||
|
|
||||||
@property IBOutlet NSWindow *preferencesWindow;
|
@property IBOutlet NSWindow *preferencesWindow;
|
||||||
@property (strong) IBOutlet NSView *graphicsTab;
|
@property (strong) IBOutlet NSView *graphicsTab;
|
||||||
|
@ -50,6 +50,8 @@
|
|||||||
JOYAxes2DEmulateButtonsKey: @YES,
|
JOYAxes2DEmulateButtonsKey: @YES,
|
||||||
JOYHatsEmulateButtonsKey: @YES,
|
JOYHatsEmulateButtonsKey: @YES,
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
[NSUserNotificationCenter defaultUserNotificationCenter].delegate = self;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (IBAction)toggleDeveloperMode:(id)sender
|
- (IBAction)toggleDeveloperMode:(id)sender
|
||||||
@ -101,4 +103,8 @@
|
|||||||
return YES;
|
return YES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification
|
||||||
|
{
|
||||||
|
[[NSDocumentController sharedDocumentController] openDocumentWithContentsOfFile:notification.identifier display:YES];
|
||||||
|
}
|
||||||
@end
|
@end
|
||||||
|
@ -358,6 +358,23 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp)
|
|||||||
}
|
}
|
||||||
NSTimer *hex_timer = [NSTimer timerWithTimeInterval:0.25 target:self selector:@selector(reloadMemoryView) userInfo:nil repeats:YES];
|
NSTimer *hex_timer = [NSTimer timerWithTimeInterval:0.25 target:self selector:@selector(reloadMemoryView) userInfo:nil repeats:YES];
|
||||||
[[NSRunLoop mainRunLoop] addTimer:hex_timer forMode:NSDefaultRunLoopMode];
|
[[NSRunLoop mainRunLoop] addTimer:hex_timer forMode:NSDefaultRunLoopMode];
|
||||||
|
|
||||||
|
/* Clear pending alarms, don't play alarms while playing*/
|
||||||
|
NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
|
||||||
|
for (NSUserNotification *notification in [center scheduledNotifications]) {
|
||||||
|
if ([notification.identifier isEqualToString:self.fileName]) {
|
||||||
|
[center removeScheduledNotification:notification];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (NSUserNotification *notification in [center deliveredNotifications]) {
|
||||||
|
if ([notification.identifier isEqualToString:self.fileName]) {
|
||||||
|
[center removeDeliveredNotification:notification];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
while (running) {
|
while (running) {
|
||||||
if (rewind) {
|
if (rewind) {
|
||||||
rewind = false;
|
rewind = false;
|
||||||
@ -381,6 +398,22 @@ static void rumbleCallback(GB_gameboy_t *gb, double amp)
|
|||||||
self.view.mouseHidingEnabled = NO;
|
self.view.mouseHidingEnabled = NO;
|
||||||
GB_save_battery(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sav"] UTF8String]);
|
GB_save_battery(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"sav"] UTF8String]);
|
||||||
GB_save_cheats(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"cht"] UTF8String]);
|
GB_save_cheats(&gb, [[[self.fileName stringByDeletingPathExtension] stringByAppendingPathExtension:@"cht"] UTF8String]);
|
||||||
|
unsigned time_to_alarm = GB_time_to_alarm(&gb);
|
||||||
|
|
||||||
|
if (time_to_alarm) {
|
||||||
|
NSUserNotification *notification = [[NSUserNotification alloc] init];
|
||||||
|
NSString *friendlyName = [[self.fileName lastPathComponent] stringByDeletingPathExtension];
|
||||||
|
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"\\([^)]+\\)|\\[[^\\]]+\\]" options:0 error:nil];
|
||||||
|
friendlyName = [regex stringByReplacingMatchesInString:friendlyName options:0 range:NSMakeRange(0, [friendlyName length]) withTemplate:@""];
|
||||||
|
friendlyName = [friendlyName stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
|
||||||
|
|
||||||
|
notification.title = [NSString stringWithFormat:@"%@ Played an Alarm", friendlyName];
|
||||||
|
notification.informativeText = [NSString stringWithFormat:@"%@ requested your attention by playing a scheduled alarm", friendlyName];
|
||||||
|
notification.identifier = self.fileName;
|
||||||
|
notification.deliveryDate = [NSDate dateWithTimeIntervalSinceNow:time_to_alarm];
|
||||||
|
notification.soundName = NSUserNotificationDefaultSoundName;
|
||||||
|
[center scheduleNotification:notification];
|
||||||
|
}
|
||||||
[_view setRumble:0];
|
[_view setRumble:0];
|
||||||
stopping = false;
|
stopping = false;
|
||||||
}
|
}
|
||||||
|
39
Core/gb.c
39
Core/gb.c
@ -564,6 +564,8 @@ typedef struct __attribute__((packed)) {
|
|||||||
uint64_t last_rtc_second;
|
uint64_t last_rtc_second;
|
||||||
uint16_t minutes;
|
uint16_t minutes;
|
||||||
uint16_t days;
|
uint16_t days;
|
||||||
|
uint16_t alarm_minutes, alarm_days;
|
||||||
|
uint8_t alarm_enabled;
|
||||||
} GB_huc3_rtc_time_t;
|
} GB_huc3_rtc_time_t;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
@ -612,12 +614,18 @@ int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size)
|
|||||||
__builtin_bswap64(gb->last_rtc_second),
|
__builtin_bswap64(gb->last_rtc_second),
|
||||||
__builtin_bswap16(gb->huc3_minutes),
|
__builtin_bswap16(gb->huc3_minutes),
|
||||||
__builtin_bswap16(gb->huc3_days),
|
__builtin_bswap16(gb->huc3_days),
|
||||||
|
__builtin_bswap16(gb->huc3_alarm_minutes),
|
||||||
|
__builtin_bswap16(gb->huc3_alarm_days),
|
||||||
|
gb->huc3_alarm_enabled,
|
||||||
};
|
};
|
||||||
#else
|
#else
|
||||||
GB_huc3_rtc_time_t rtc_save = {
|
GB_huc3_rtc_time_t rtc_save = {
|
||||||
gb->last_rtc_second,
|
gb->last_rtc_second,
|
||||||
gb->huc3_minutes,
|
gb->huc3_minutes,
|
||||||
gb->huc3_days,
|
gb->huc3_days,
|
||||||
|
gb->huc3_alarm_minutes,
|
||||||
|
gb->huc3_alarm_days,
|
||||||
|
gb->huc3_alarm_enabled,
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
memcpy(buffer, &rtc_save, sizeof(rtc_save));
|
memcpy(buffer, &rtc_save, sizeof(rtc_save));
|
||||||
@ -666,12 +674,18 @@ int GB_save_battery(GB_gameboy_t *gb, const char *path)
|
|||||||
__builtin_bswap64(gb->last_rtc_second),
|
__builtin_bswap64(gb->last_rtc_second),
|
||||||
__builtin_bswap16(gb->huc3_minutes),
|
__builtin_bswap16(gb->huc3_minutes),
|
||||||
__builtin_bswap16(gb->huc3_days),
|
__builtin_bswap16(gb->huc3_days),
|
||||||
|
__builtin_bswap16(gb->huc3_alarm_minutes),
|
||||||
|
__builtin_bswap16(gb->huc3_alarm_days),
|
||||||
|
gb->huc3_alarm_enabled,
|
||||||
};
|
};
|
||||||
#else
|
#else
|
||||||
GB_huc3_rtc_time_t rtc_save = {
|
GB_huc3_rtc_time_t rtc_save = {
|
||||||
gb->last_rtc_second,
|
gb->last_rtc_second,
|
||||||
gb->huc3_minutes,
|
gb->huc3_minutes,
|
||||||
gb->huc3_days,
|
gb->huc3_days,
|
||||||
|
gb->huc3_alarm_minutes,
|
||||||
|
gb->huc3_alarm_days,
|
||||||
|
gb->huc3_alarm_enabled,
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -726,10 +740,16 @@ void GB_load_battery_from_buffer(GB_gameboy_t *gb, const uint8_t *buffer, size_t
|
|||||||
gb->last_rtc_second = __builtin_bswap64(rtc_save.last_rtc_second);
|
gb->last_rtc_second = __builtin_bswap64(rtc_save.last_rtc_second);
|
||||||
gb->huc3_minutes = __builtin_bswap16(rtc_save.minutes);
|
gb->huc3_minutes = __builtin_bswap16(rtc_save.minutes);
|
||||||
gb->huc3_days = __builtin_bswap16(rtc_save.days);
|
gb->huc3_days = __builtin_bswap16(rtc_save.days);
|
||||||
|
gb->huc3_alarm_minutes = __builtin_bswap16(rtc_save.alarm_minutes);
|
||||||
|
gb->huc3_alarm_days = __builtin_bswap16(rtc_save.alarm_days);
|
||||||
|
gb->huc3_alarm_enabled = rtc_save.alarm_enabled;
|
||||||
#else
|
#else
|
||||||
gb->last_rtc_second = rtc_save.last_rtc_second;
|
gb->last_rtc_second = rtc_save.last_rtc_second;
|
||||||
gb->huc3_minutes = rtc_save.minutes;
|
gb->huc3_minutes = rtc_save.minutes;
|
||||||
gb->huc3_days = rtc_save.days;
|
gb->huc3_days = rtc_save.days;
|
||||||
|
gb->huc3_alarm_minutes = rtc_save.alarm_minutes;
|
||||||
|
gb->huc3_alarm_days = rtc_save.alarm_days;
|
||||||
|
gb->huc3_alarm_enabled = rtc_save.alarm_enabled;
|
||||||
#endif
|
#endif
|
||||||
if (gb->last_rtc_second > time(NULL)) {
|
if (gb->last_rtc_second > time(NULL)) {
|
||||||
/* We must reset RTC here, or it will not advance. */
|
/* We must reset RTC here, or it will not advance. */
|
||||||
@ -802,6 +822,7 @@ reset_rtc:
|
|||||||
gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */
|
gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */
|
||||||
gb->huc3_days = 0xFFFF;
|
gb->huc3_days = 0xFFFF;
|
||||||
gb->huc3_minutes = 0xFFF;
|
gb->huc3_minutes = 0xFFF;
|
||||||
|
gb->huc3_alarm_enabled = false;
|
||||||
exit:
|
exit:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -827,10 +848,16 @@ void GB_load_battery(GB_gameboy_t *gb, const char *path)
|
|||||||
gb->last_rtc_second = __builtin_bswap64(rtc_save.last_rtc_second);
|
gb->last_rtc_second = __builtin_bswap64(rtc_save.last_rtc_second);
|
||||||
gb->huc3_minutes = __builtin_bswap16(rtc_save.minutes);
|
gb->huc3_minutes = __builtin_bswap16(rtc_save.minutes);
|
||||||
gb->huc3_days = __builtin_bswap16(rtc_save.days);
|
gb->huc3_days = __builtin_bswap16(rtc_save.days);
|
||||||
|
gb->huc3_alarm_minutes = __builtin_bswap16(rtc_save.alarm_minutes);
|
||||||
|
gb->huc3_alarm_days = __builtin_bswap16(rtc_save.alarm_days);
|
||||||
|
gb->huc3_alarm_enabled = rtc_save.alarm_enabled;
|
||||||
#else
|
#else
|
||||||
gb->last_rtc_second = rtc_save.last_rtc_second;
|
gb->last_rtc_second = rtc_save.last_rtc_second;
|
||||||
gb->huc3_minutes = rtc_save.minutes;
|
gb->huc3_minutes = rtc_save.minutes;
|
||||||
gb->huc3_days = rtc_save.days;
|
gb->huc3_days = rtc_save.days;
|
||||||
|
gb->huc3_alarm_minutes = rtc_save.alarm_minutes;
|
||||||
|
gb->huc3_alarm_days = rtc_save.alarm_days;
|
||||||
|
gb->huc3_alarm_enabled = rtc_save.alarm_enabled;
|
||||||
#endif
|
#endif
|
||||||
if (gb->last_rtc_second > time(NULL)) {
|
if (gb->last_rtc_second > time(NULL)) {
|
||||||
/* We must reset RTC here, or it will not advance. */
|
/* We must reset RTC here, or it will not advance. */
|
||||||
@ -902,6 +929,7 @@ reset_rtc:
|
|||||||
gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */
|
gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */
|
||||||
gb->huc3_days = 0xFFFF;
|
gb->huc3_days = 0xFFFF;
|
||||||
gb->huc3_minutes = 0xFFF;
|
gb->huc3_minutes = 0xFFF;
|
||||||
|
gb->huc3_alarm_enabled = false;
|
||||||
exit:
|
exit:
|
||||||
fclose(f);
|
fclose(f);
|
||||||
return;
|
return;
|
||||||
@ -1568,3 +1596,14 @@ void GB_set_boot_rom_load_callback(GB_gameboy_t *gb, GB_boot_rom_load_callback_t
|
|||||||
gb->boot_rom_load_callback = callback;
|
gb->boot_rom_load_callback = callback;
|
||||||
request_boot_rom(gb);
|
request_boot_rom(gb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned GB_time_to_alarm(GB_gameboy_t *gb)
|
||||||
|
{
|
||||||
|
if (gb->cartridge_type->mbc_type != GB_HUC3) return 0;
|
||||||
|
if (!gb->huc3_alarm_enabled) return 0;
|
||||||
|
if (!(gb->huc3_alarm_days & 0x2000)) return 0;
|
||||||
|
unsigned current_time = (gb->huc3_days & 0x1FFF) * 24 * 60 * 60 + gb->huc3_minutes * 60 + (time(NULL) % 60);
|
||||||
|
unsigned alarm_time = (gb->huc3_alarm_days & 0x1FFF) * 24 * 60 * 60 + gb->huc3_alarm_minutes * 60;
|
||||||
|
if (current_time > alarm_time) return 0;
|
||||||
|
return alarm_time - current_time;
|
||||||
|
}
|
||||||
|
@ -438,6 +438,8 @@ struct GB_gameboy_internal_s {
|
|||||||
uint8_t huc3_mode;
|
uint8_t huc3_mode;
|
||||||
uint8_t huc3_access_index;
|
uint8_t huc3_access_index;
|
||||||
uint16_t huc3_minutes, huc3_days;
|
uint16_t huc3_minutes, huc3_days;
|
||||||
|
uint16_t huc3_alarm_minutes, huc3_alarm_days;
|
||||||
|
bool huc3_alarm_enabled;
|
||||||
uint8_t huc3_read;
|
uint8_t huc3_read;
|
||||||
uint8_t huc3_access_flags;
|
uint8_t huc3_access_flags;
|
||||||
);
|
);
|
||||||
@ -779,6 +781,9 @@ void GB_serial_set_data_bit(GB_gameboy_t *gb, bool data);
|
|||||||
|
|
||||||
void GB_disconnect_serial(GB_gameboy_t *gb);
|
void GB_disconnect_serial(GB_gameboy_t *gb);
|
||||||
|
|
||||||
|
/* For cartridges with an alarm clock */
|
||||||
|
unsigned GB_time_to_alarm(GB_gameboy_t *gb); // 0 if no alarm
|
||||||
|
|
||||||
/* For integration with SFC/SNES emulators */
|
/* For integration with SFC/SNES emulators */
|
||||||
void GB_set_joyp_write_callback(GB_gameboy_t *gb, GB_joyp_write_callback_t callback);
|
void GB_set_joyp_write_callback(GB_gameboy_t *gb, GB_joyp_write_callback_t callback);
|
||||||
void GB_set_icd_pixel_callback(GB_gameboy_t *gb, GB_icd_pixel_callback_t callback);
|
void GB_set_icd_pixel_callback(GB_gameboy_t *gb, GB_icd_pixel_callback_t callback);
|
||||||
|
156
Core/memory.c
156
Core/memory.c
@ -554,77 +554,97 @@ static void write_vram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
|||||||
gb->vram[(addr & 0x1FFF) + (uint16_t) gb->cgb_vram_bank * 0x2000] = value;
|
gb->vram[(addr & 0x1FFF) + (uint16_t) gb->cgb_vram_bank * 0x2000] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool huc3_write(GB_gameboy_t *gb, uint8_t value)
|
||||||
|
{
|
||||||
|
switch (gb->huc3_mode) {
|
||||||
|
case 0xB: // RTC Write
|
||||||
|
switch (value >> 4) {
|
||||||
|
case 1:
|
||||||
|
if (gb->huc3_access_index < 3) {
|
||||||
|
gb->huc3_read = (gb->huc3_minutes >> (gb->huc3_access_index * 4)) & 0xF;
|
||||||
|
}
|
||||||
|
else if (gb->huc3_access_index < 7) {
|
||||||
|
gb->huc3_read = (gb->huc3_days >> ((gb->huc3_access_index - 3) * 4)) & 0xF;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// GB_log(gb, "Attempting to read from unsupported HuC-3 register: %03x\n", gb->huc3_access_index);
|
||||||
|
}
|
||||||
|
gb->huc3_access_index++;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
if (gb->huc3_access_index < 3) {
|
||||||
|
gb->huc3_minutes &= ~(0xF << (gb->huc3_access_index * 4));
|
||||||
|
gb->huc3_minutes |= ((value & 0xF) << (gb->huc3_access_index * 4));
|
||||||
|
}
|
||||||
|
else if (gb->huc3_access_index < 7) {
|
||||||
|
gb->huc3_days &= ~(0xF << ((gb->huc3_access_index - 3) * 4));
|
||||||
|
gb->huc3_days |= ((value & 0xF) << ((gb->huc3_access_index - 3) * 4));
|
||||||
|
}
|
||||||
|
else if (gb->huc3_access_index >= 0x58 && gb->huc3_access_index <= 0x5a) {
|
||||||
|
gb->huc3_alarm_minutes &= ~(0xF << ((gb->huc3_access_index - 0x58) * 4));
|
||||||
|
gb->huc3_alarm_minutes |= ((value & 0xF) << ((gb->huc3_access_index - 0x58) * 4));
|
||||||
|
}
|
||||||
|
else if (gb->huc3_access_index >= 0x5b && gb->huc3_access_index <= 0x5e) {
|
||||||
|
gb->huc3_alarm_days &= ~(0xF << ((gb->huc3_access_index - 0x5b) * 4));
|
||||||
|
gb->huc3_alarm_days |= ((value & 0xF) << ((gb->huc3_access_index - 0x5b) * 4));
|
||||||
|
}
|
||||||
|
else if (gb->huc3_access_index == 0x5f) {
|
||||||
|
gb->huc3_alarm_enabled = value & 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// GB_log(gb, "Attempting to write %x to unsupported HuC-3 register: %03x\n", value & 0xF, gb->huc3_access_index);
|
||||||
|
}
|
||||||
|
if ((value >> 4) == 3) {
|
||||||
|
gb->huc3_access_index++;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
gb->huc3_access_index &= 0xF0;
|
||||||
|
gb->huc3_access_index |= value & 0xF;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
gb->huc3_access_index &= 0x0F;
|
||||||
|
gb->huc3_access_index |= (value & 0xF) << 4;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
gb->huc3_access_flags = (value & 0xF);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
case 0xD: // RTC status
|
||||||
|
// Not sure what writes here mean, they're always 0xFE
|
||||||
|
return true;
|
||||||
|
case 0xE: { // IR mode
|
||||||
|
bool old_input = effective_ir_input(gb);
|
||||||
|
gb->cart_ir = value & 1;
|
||||||
|
bool new_input = effective_ir_input(gb);
|
||||||
|
if (new_input != old_input) {
|
||||||
|
if (gb->infrared_callback) {
|
||||||
|
gb->infrared_callback(gb, new_input, gb->cycles_since_ir_change);
|
||||||
|
}
|
||||||
|
gb->cycles_since_ir_change = 0;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case 0xC:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
case 0: // Disabled
|
||||||
|
case 0xA: // RAM
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||||
{
|
{
|
||||||
if (gb->cartridge_type->mbc_type == GB_HUC3) {
|
if (gb->cartridge_type->mbc_type == GB_HUC3) {
|
||||||
switch (gb->huc3_mode) {
|
if (huc3_write(gb, value)) return;
|
||||||
case 0xB: // RTC Write
|
|
||||||
switch (value >> 4) {
|
|
||||||
case 1:
|
|
||||||
if (gb->huc3_access_index < 3) {
|
|
||||||
gb->huc3_read = (gb->huc3_minutes >> (gb->huc3_access_index * 4)) & 0xF;
|
|
||||||
}
|
|
||||||
else if (gb->huc3_access_index < 7) {
|
|
||||||
gb->huc3_read = (gb->huc3_days >> ((gb->huc3_access_index - 3) * 4)) & 0xF;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
GB_log(gb, "Attempting to read from unsupported HuC-3 register: %03x\n", gb->huc3_access_index);
|
|
||||||
}
|
|
||||||
gb->huc3_access_index++;
|
|
||||||
return;
|
|
||||||
case 2:
|
|
||||||
case 3:
|
|
||||||
if (gb->huc3_access_index < 3) {
|
|
||||||
gb->huc3_minutes &= ~(0xF << (gb->huc3_access_index * 4));
|
|
||||||
gb->huc3_minutes |= ((value & 0xF) << (gb->huc3_access_index * 4));
|
|
||||||
}
|
|
||||||
else if (gb->huc3_access_index < 7) {
|
|
||||||
gb->huc3_days &= ~(0xF << ((gb->huc3_access_index - 3) * 4));
|
|
||||||
gb->huc3_days |= ((value & 0xF) << ((gb->huc3_access_index - 3) * 4));
|
|
||||||
}
|
|
||||||
if ((value >> 4) == 3) {
|
|
||||||
gb->huc3_access_index++;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
case 4:
|
|
||||||
gb->huc3_access_index &= 0xF0;
|
|
||||||
gb->huc3_access_index |= value & 0xF;
|
|
||||||
return;
|
|
||||||
case 5:
|
|
||||||
gb->huc3_access_index &= 0x0F;
|
|
||||||
gb->huc3_access_index |= (value & 0xF) << 4;
|
|
||||||
return;
|
|
||||||
case 6:
|
|
||||||
gb->huc3_access_flags = (value & 0xF);
|
|
||||||
return;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
case 0xD: // RTC status
|
|
||||||
// Not sure what writes here mean, they're always 0xFE
|
|
||||||
return;
|
|
||||||
case 0xE: { // IR mode
|
|
||||||
bool old_input = effective_ir_input(gb);
|
|
||||||
gb->cart_ir = value & 1;
|
|
||||||
bool new_input = effective_ir_input(gb);
|
|
||||||
if (new_input != old_input) {
|
|
||||||
if (gb->infrared_callback) {
|
|
||||||
gb->infrared_callback(gb, new_input, gb->cycles_since_ir_change);
|
|
||||||
}
|
|
||||||
gb->cycles_since_ir_change = 0;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
GB_log(gb, "Unsupported HuC-3 mode %x write: [%04x] = %02x\n", gb->huc3_mode, addr, value);
|
|
||||||
return;
|
|
||||||
case 0: // Disabled
|
|
||||||
case 0xA: // RAM
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gb->camera_registers_mapped) {
|
if (gb->camera_registers_mapped) {
|
||||||
|
Loading…
Reference in New Issue
Block a user