Merge branch 'master' into bess
This commit is contained in:
commit
976f5e4d02
@ -1523,7 +1523,9 @@ static bool mbc(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
|
||||
const GB_cartridge_t *cartridge = gb->cartridge_type;
|
||||
|
||||
if (cartridge->has_ram) {
|
||||
GB_log(gb, "Cartridge includes%s RAM: $%x bytes\n", cartridge->has_battery? " battery-backed": "", gb->mbc_ram_size);
|
||||
bool has_battery = gb->cartridge_type->has_battery &&
|
||||
(gb->cartridge_type->mbc_type != GB_TPP1 || (gb->rom[0x153] & 8));
|
||||
GB_log(gb, "Cartridge includes%s RAM: $%x bytes\n", has_battery? " battery-backed": "", gb->mbc_ram_size);
|
||||
}
|
||||
else {
|
||||
GB_log(gb, "No cartridge RAM\n");
|
||||
@ -1565,7 +1567,8 @@ static bool mbc(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugg
|
||||
GB_log(gb, "No MBC\n");
|
||||
}
|
||||
|
||||
if (cartridge->has_rumble) {
|
||||
if (gb->cartridge_type->has_rumble &&
|
||||
(gb->cartridge_type->mbc_type != GB_TPP1 || (gb->rom[0x153] & 1))) {
|
||||
GB_log(gb, "Cart contains a Rumble Pak\n");
|
||||
}
|
||||
|
||||
|
@ -579,7 +579,6 @@ static void render_pixel_if_possible(GB_gameboy_t *gb)
|
||||
else if (gb->model & GB_MODEL_NO_SFC_BIT) {
|
||||
if (gb->icd_pixel_callback) {
|
||||
icd_pixel = pixel;
|
||||
//gb->icd_pixel_callback(gb, pixel);
|
||||
}
|
||||
}
|
||||
else if (gb->cgb_palettes_ppu_blocked) {
|
||||
|
@ -598,6 +598,8 @@ typedef union {
|
||||
int GB_save_battery_size(GB_gameboy_t *gb)
|
||||
{
|
||||
if (!gb->cartridge_type->has_battery) return 0; // Nothing to save.
|
||||
if (gb->cartridge_type->mbc_type == GB_TPP1 && !(gb->rom[0x153] & 8)) return 0; // Nothing to save.
|
||||
|
||||
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) {
|
||||
@ -610,6 +612,7 @@ int GB_save_battery_size(GB_gameboy_t *gb)
|
||||
int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size)
|
||||
{
|
||||
if (!gb->cartridge_type->has_battery) return 0; // Nothing to save.
|
||||
if (gb->cartridge_type->mbc_type == GB_TPP1 && !(gb->rom[0x153] & 8)) return 0; // Nothing to save.
|
||||
if (gb->mbc_ram_size == 0 && !gb->cartridge_type->has_rtc) return 0; /* Claims to have battery, but has no RAM or RTC */
|
||||
|
||||
if (size < GB_save_battery_size(gb)) return EIO;
|
||||
@ -667,6 +670,7 @@ int GB_save_battery_to_buffer(GB_gameboy_t *gb, uint8_t *buffer, size_t size)
|
||||
int GB_save_battery(GB_gameboy_t *gb, const char *path)
|
||||
{
|
||||
if (!gb->cartridge_type->has_battery) return 0; // Nothing to save.
|
||||
if (gb->cartridge_type->mbc_type == GB_TPP1 && !(gb->rom[0x153] & 8)) return 0; // Nothing to save.
|
||||
if (gb->mbc_ram_size == 0 && !gb->cartridge_type->has_rtc) return 0; /* Claims to have battery, but has no RAM or RTC */
|
||||
FILE *f = fopen(path, "wb");
|
||||
if (!f) {
|
||||
|
@ -468,10 +468,10 @@ struct GB_gameboy_internal_s {
|
||||
uint16_t mbc_rom0_bank; /* For some MBC1 wirings. */
|
||||
bool camera_registers_mapped;
|
||||
uint8_t camera_registers[0x36];
|
||||
bool rumble_state;
|
||||
uint8_t rumble_strength;
|
||||
bool cart_ir;
|
||||
|
||||
// TODO: move to huc3/mbc3 struct when breaking save compat
|
||||
// TODO: move to huc3/mbc3/tpp1 struct when breaking save compat
|
||||
uint8_t huc3_mode;
|
||||
uint8_t huc3_access_index;
|
||||
uint16_t huc3_minutes, huc3_days;
|
||||
@ -480,6 +480,9 @@ struct GB_gameboy_internal_s {
|
||||
uint8_t huc3_read;
|
||||
uint8_t huc3_access_flags;
|
||||
bool mbc3_rtc_mapped;
|
||||
uint16_t tpp1_rom_bank;
|
||||
uint8_t tpp1_ram_bank;
|
||||
uint8_t tpp1_mode;
|
||||
);
|
||||
|
||||
|
||||
|
17
Core/mbc.c
17
Core/mbc.c
@ -111,12 +111,24 @@ void GB_update_mbc_mappings(GB_gameboy_t *gb)
|
||||
gb->mbc_rom_bank = gb->huc3.rom_bank;
|
||||
gb->mbc_ram_bank = gb->huc3.ram_bank;
|
||||
break;
|
||||
case GB_TPP1:
|
||||
gb->mbc_rom_bank = gb->tpp1_rom_bank;
|
||||
gb->mbc_ram_bank = gb->tpp1_ram_bank;
|
||||
gb->mbc_ram_enable = (gb->tpp1_mode == 2) || (gb->tpp1_mode == 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GB_configure_cart(GB_gameboy_t *gb)
|
||||
{
|
||||
gb->cartridge_type = &GB_cart_defs[gb->rom[0x147]];
|
||||
if (gb->rom[0x147] == 0xbc &&
|
||||
gb->rom[0x149] == 0xc1 &&
|
||||
gb->rom[0x14a] == 0x65) {
|
||||
static const GB_cartridge_t tpp1 = {GB_TPP1, GB_STANDARD_MBC, true, true, true, true};
|
||||
gb->cartridge_type = &tpp1;
|
||||
gb->tpp1_rom_bank = 1;
|
||||
}
|
||||
|
||||
if (gb->rom[0x147] == 0 && gb->rom_size > 0x8000) {
|
||||
GB_log(gb, "ROM header reports no MBC, but file size is over 32Kb. Assuming cartridge uses MBC3.\n");
|
||||
@ -130,6 +142,11 @@ void GB_configure_cart(GB_gameboy_t *gb)
|
||||
if (gb->cartridge_type->mbc_type == GB_MBC2) {
|
||||
gb->mbc_ram_size = 0x200;
|
||||
}
|
||||
else if (gb->cartridge_type->mbc_type == GB_TPP1) {
|
||||
if (gb->rom[0x152] >= 1 && gb->rom[0x152] <= 9) {
|
||||
gb->mbc_ram_size = 0x2000 << (gb->rom[0x152] - 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
static const unsigned ram_sizes[256] = {0, 0x800, 0x2000, 0x8000, 0x20000, 0x10000};
|
||||
gb->mbc_ram_size = ram_sizes[gb->rom[0x149]];
|
||||
|
@ -12,6 +12,7 @@ typedef struct {
|
||||
GB_MBC5,
|
||||
GB_HUC1,
|
||||
GB_HUC3,
|
||||
GB_TPP1,
|
||||
} mbc_type;
|
||||
enum {
|
||||
GB_STANDARD_MBC,
|
||||
|
130
Core/memory.c
130
Core/memory.c
@ -178,7 +178,42 @@ static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr)
|
||||
}
|
||||
}
|
||||
|
||||
if ((!gb->mbc_ram_enable) &&
|
||||
if (gb->cartridge_type->mbc_type == GB_TPP1) {
|
||||
switch (gb->tpp1_mode) {
|
||||
case 0:
|
||||
switch (addr & 3) {
|
||||
case 0: return gb->tpp1_rom_bank;
|
||||
case 1: return gb->tpp1_rom_bank >> 8;
|
||||
case 2: return gb->tpp1_ram_bank;
|
||||
case 3: return gb->rumble_strength | (((gb->rtc_real.high & 0xC0) ^ 0x40) >> 4);
|
||||
}
|
||||
case 2:
|
||||
case 3:
|
||||
break; // Read RAM
|
||||
case 5:
|
||||
switch (addr & 3) {
|
||||
case 0: { // Week count
|
||||
unsigned total_days = (((gb->rtc_latched.high & 7) << 8) + gb->rtc_latched.days);
|
||||
if (gb->rtc_latched.high & 0x20) {
|
||||
return total_days / 7 - 1;
|
||||
}
|
||||
return total_days / 7;
|
||||
}
|
||||
case 1: { // Week count
|
||||
unsigned total_days = (((gb->rtc_latched.high & 7) << 8) + gb->rtc_latched.days);
|
||||
if (gb->rtc_latched.high & 0x20) {
|
||||
return gb->rtc_latched.hours | 0xe0; // Hours and weekday
|
||||
}
|
||||
return gb->rtc_latched.hours | ((total_days % 7) << 5); // Hours and weekday
|
||||
}
|
||||
case 2: return gb->rtc_latched.minutes;
|
||||
case 3: return gb->rtc_latched.seconds;
|
||||
}
|
||||
default:
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
||||
else if ((!gb->mbc_ram_enable) &&
|
||||
gb->cartridge_type->mbc_subtype != GB_CAMERA &&
|
||||
gb->cartridge_type->mbc_type != GB_HUC1 &&
|
||||
gb->cartridge_type->mbc_type != GB_HUC3) {
|
||||
@ -335,9 +370,7 @@ static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr)
|
||||
}
|
||||
|
||||
if (addr < 0xFF00) {
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
if (addr < 0xFF80) {
|
||||
@ -539,8 +572,8 @@ static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
case 0x3000: gb->mbc5.rom_bank_high = value; break;
|
||||
case 0x4000: case 0x5000:
|
||||
if (gb->cartridge_type->has_rumble) {
|
||||
if (!!(value & 8) != gb->rumble_state) {
|
||||
gb->rumble_state = !gb->rumble_state;
|
||||
if (!!(value & 8) != !!gb->rumble_strength) {
|
||||
gb->rumble_strength = gb->rumble_strength? 0 : 3;
|
||||
}
|
||||
value &= 7;
|
||||
}
|
||||
@ -567,6 +600,56 @@ static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
case 0x4000: case 0x5000: gb->huc3.ram_bank = value; break;
|
||||
}
|
||||
break;
|
||||
case GB_TPP1:
|
||||
switch (addr & 3) {
|
||||
case 0:
|
||||
gb->tpp1_rom_bank &= 0xFF00;
|
||||
gb->tpp1_rom_bank |= value;
|
||||
break;
|
||||
case 1:
|
||||
gb->tpp1_rom_bank &= 0xFF;
|
||||
gb->tpp1_rom_bank |= value << 8;
|
||||
break;
|
||||
case 2:
|
||||
gb->tpp1_ram_bank = value;
|
||||
break;
|
||||
case 3:
|
||||
switch (value) {
|
||||
case 0:
|
||||
case 2:
|
||||
case 3:
|
||||
case 5:
|
||||
gb->tpp1_mode = value;
|
||||
break;
|
||||
case 0x10:
|
||||
memcpy(&gb->rtc_latched, &gb->rtc_real, sizeof(gb->rtc_real));
|
||||
break;
|
||||
case 0x11: {
|
||||
uint8_t flags = gb->rtc_real.high & 0xc0;
|
||||
memcpy(&gb->rtc_real, &gb->rtc_latched, sizeof(gb->rtc_real));
|
||||
gb->rtc_real.high &= ~0xe0;
|
||||
gb->rtc_real.high |= flags;
|
||||
break;
|
||||
}
|
||||
case 0x14:
|
||||
gb->rtc_real.high &= ~0x80;
|
||||
break;
|
||||
case 0x18:
|
||||
gb->rtc_real.high |= 0x40;
|
||||
break;
|
||||
case 0x19:
|
||||
gb->rtc_real.high &= ~0x40;
|
||||
break;
|
||||
|
||||
case 0x20:
|
||||
case 0x21:
|
||||
case 0x22:
|
||||
case 0x23:
|
||||
gb->rumble_strength = value & 3;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
GB_update_mbc_mappings(gb);
|
||||
}
|
||||
@ -688,6 +771,43 @@ static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value)
|
||||
return;
|
||||
}
|
||||
|
||||
if (gb->cartridge_type->mbc_type == GB_TPP1) {
|
||||
switch (gb->tpp1_mode) {
|
||||
case 3:
|
||||
break;
|
||||
case 5:
|
||||
switch (addr & 3) {
|
||||
case 0: {
|
||||
unsigned total_days = (((gb->rtc_latched.high & 7) << 8) + gb->rtc_latched.days);
|
||||
total_days = total_days % 7 + value * 7;
|
||||
bool had_illegal_weekday = gb->rtc_latched.high & 0x20;
|
||||
gb->rtc_latched.days = total_days;
|
||||
gb->rtc_latched.high = total_days >> 8;
|
||||
if (had_illegal_weekday) {
|
||||
gb->rtc_latched.high |= 0x20;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case 1: {
|
||||
unsigned total_days = (((gb->rtc_latched.high & 7) << 8) + gb->rtc_latched.days);
|
||||
total_days = total_days / 7 * 7 + (value >> 5);
|
||||
gb->rtc_latched.hours = value & 0x1F;
|
||||
gb->rtc_latched.days = total_days;
|
||||
gb->rtc_latched.high = total_days >> 8;
|
||||
if ((value & 0xE0) == 0xE0) { // Illegal weekday
|
||||
gb->rtc_latched.high |= 0x20;
|
||||
}
|
||||
return;
|
||||
}
|
||||
case 2: gb->rtc_latched.minutes = value; return;
|
||||
case 3: gb->rtc_latched.seconds = value; return;
|
||||
}
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ((!gb->mbc_ram_enable)
|
||||
&& gb->cartridge_type->mbc_type != GB_HUC1) return;
|
||||
|
||||
|
@ -15,7 +15,8 @@ void GB_handle_rumble(GB_gameboy_t *gb)
|
||||
if (gb->rumble_mode == GB_RUMBLE_DISABLED) {
|
||||
return;
|
||||
}
|
||||
if (gb->cartridge_type->has_rumble) {
|
||||
if (gb->cartridge_type->has_rumble &&
|
||||
(gb->cartridge_type->mbc_type != GB_TPP1 || (gb->rom[0x153] & 1))) {
|
||||
if (gb->rumble_on_cycles + gb->rumble_off_cycles) {
|
||||
gb->rumble_callback(gb, gb->rumble_on_cycles / (double)(gb->rumble_on_cycles + gb->rumble_off_cycles));
|
||||
gb->rumble_on_cycles = gb->rumble_off_cycles = 0;
|
||||
|
@ -267,7 +267,8 @@ static void command_ready(GB_gameboy_t *gb)
|
||||
#endif
|
||||
uint8_t x = command->x;
|
||||
uint8_t y = command->y;
|
||||
if (x >= 20 || y >= 18 || (count + 3) / 4 > sizeof(gb->sgb->command) - sizeof(*command) - 1) {
|
||||
count = MIN(count, 20 * 18);
|
||||
if (x >= 20 || y >= 18) {
|
||||
/* TODO: Verify with the SFC BIOS */
|
||||
break;
|
||||
}
|
||||
|
@ -288,10 +288,22 @@ static void GB_rtc_run(GB_gameboy_t *gb, uint8_t cycles)
|
||||
while (gb->last_rtc_second + 60 * 60 * 24 < current_time) {
|
||||
gb->last_rtc_second += 60 * 60 * 24;
|
||||
if (++gb->rtc_real.days == 0) {
|
||||
if (gb->rtc_real.high & 1) { /* Bit 8 of days*/
|
||||
gb->rtc_real.high |= 0x80; /* Overflow bit */
|
||||
if (gb->cartridge_type->mbc_type == GB_TPP1) {
|
||||
if ((gb->rtc_real.high & 7) >= 6) { /* Bit 8 of days*/
|
||||
gb->rtc_real.high &= 0x40;
|
||||
gb->rtc_real.high |= 0x80; /* Overflow bit */
|
||||
}
|
||||
else {
|
||||
gb->rtc_real.high++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (gb->rtc_real.high & 1) { /* Bit 8 of days*/
|
||||
gb->rtc_real.high |= 0x80; /* Overflow bit */
|
||||
}
|
||||
|
||||
gb->rtc_real.high ^= 1;
|
||||
}
|
||||
gb->rtc_real.high ^= 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -308,11 +320,22 @@ static void GB_rtc_run(GB_gameboy_t *gb, uint8_t cycles)
|
||||
|
||||
if (++gb->rtc_real.days != 0) continue;
|
||||
|
||||
if (gb->rtc_real.high & 1) { /* Bit 8 of days*/
|
||||
gb->rtc_real.high |= 0x80; /* Overflow bit */
|
||||
if (gb->cartridge_type->mbc_type == GB_TPP1) {
|
||||
if ((gb->rtc_real.high & 7) >= 6) { /* Bit 8 of days*/
|
||||
gb->rtc_real.high &= 0x40;
|
||||
gb->rtc_real.high |= 0x80; /* Overflow bit */
|
||||
}
|
||||
else {
|
||||
gb->rtc_real.high++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (gb->rtc_real.high & 1) { /* Bit 8 of days*/
|
||||
gb->rtc_real.high |= 0x80; /* Overflow bit */
|
||||
}
|
||||
|
||||
gb->rtc_real.high ^= 1;
|
||||
}
|
||||
|
||||
gb->rtc_real.high ^= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -344,13 +367,9 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles)
|
||||
gb->cycles_since_last_sync += cycles;
|
||||
gb->cycles_since_run += cycles;
|
||||
|
||||
if (gb->rumble_state) {
|
||||
gb->rumble_on_cycles++;
|
||||
}
|
||||
else {
|
||||
gb->rumble_off_cycles++;
|
||||
}
|
||||
|
||||
gb->rumble_on_cycles += gb->rumble_strength & 3;
|
||||
gb->rumble_off_cycles += (gb->rumble_strength & 3) ^ 3;
|
||||
|
||||
if (!gb->stopped) { // TODO: Verify what happens in STOP mode
|
||||
GB_dma_run(gb);
|
||||
GB_hdma_run(gb);
|
||||
|
@ -166,6 +166,7 @@ typedef union {
|
||||
double _sentRumbleAmp;
|
||||
unsigned _rumbleCounter;
|
||||
bool _deviceCantSendReports;
|
||||
dispatch_queue_t _rumbleQueue;
|
||||
}
|
||||
|
||||
- (instancetype)initWithDevice:(IOHIDDeviceRef) device hacks:(NSDictionary *)hacks
|
||||
@ -490,9 +491,11 @@ typedef union {
|
||||
{.timeEnabled = 0, .dutyLength = 0, .enabled = 0, .dutyOff = 0, .dutyOn = 0},
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
_rumbleQueue = dispatch_queue_create([NSString stringWithFormat:@"Rumble Queue for %@", self.deviceName].UTF8String,
|
||||
NULL);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@ -564,7 +567,9 @@ typedef union {
|
||||
}
|
||||
}
|
||||
}
|
||||
[self updateRumble];
|
||||
dispatch_async(_rumbleQueue, ^{
|
||||
[self updateRumble];
|
||||
});
|
||||
}
|
||||
|
||||
- (void)elementChanged:(IOHIDElementRef)element
|
||||
@ -699,7 +704,9 @@ typedef union {
|
||||
_physicallyConnected = false;
|
||||
[exposedControllers removeObject:self];
|
||||
[self setRumbleAmplitude:0];
|
||||
[self updateRumble];
|
||||
dispatch_sync(_rumbleQueue, ^{
|
||||
[self updateRumble];
|
||||
});
|
||||
_device = nil;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user