#include #include #include #include #include "callbacks.h" #include #include #include #include #include #include #include // Functions added by us to peek at static structs // (this is much less invasive than not making them static FYI) extern int eeprom_i2c_get_size(void); extern int sms_cart_is_codies(void); extern int sms_cart_bootrom_size(void); struct config_t config; char GG_ROM[256] = "GG_ROM"; // game genie rom char AR_ROM[256] = "AR_ROM"; // actin replay rom char SK_ROM[256] = "SK_ROM"; // sanic and knuckles char SK_UPMEM[256] = "SK_UPMEM"; // sanic and knuckles char GG_BIOS[256] = "GG_BIOS"; // game gear bootrom char CD_BIOS_EU[256] = "CD_BIOS_EU"; // cd bioses char CD_BIOS_US[256] = "CD_BIOS_US"; char CD_BIOS_JP[256] = "CD_BIOS_JP"; char MD_BIOS[256] = "MD_BIOS"; // genesis tmss bootrom char MS_BIOS_US[256] = "MS_BIOS_US"; // master system bioses char MS_BIOS_EU[256] = "MS_BIOS_EU"; char MS_BIOS_JP[256] = "MS_BIOS_JP"; char romextension[4]; static int16 soundbuffer[4096]; static int nsamples; int cinterface_force_sram; int cinterface_render_bga = 1; int cinterface_render_bgb = 1; int cinterface_render_bgw = 1; int cinterface_render_obj = 1; uint8 cinterface_custom_backdrop = 0; uint32 cinterface_custom_backdrop_color = 0xffff00ff; // pink extern uint8 border; #define GPGX_EX ECL_EXPORT static int vwidth; static int vheight; static uint8_t brm_format[0x40] = { 0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x5f,0x00,0x00,0x00,0x00,0x40, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x53,0x45,0x47,0x41,0x5f,0x43,0x44,0x5f,0x52,0x4f,0x4d,0x00,0x01,0x00,0x00,0x00, 0x52,0x41,0x4d,0x5f,0x43,0x41,0x52,0x54,0x52,0x49,0x44,0x47,0x45,0x5f,0x5f,0x5f }; ECL_ENTRY void (*biz_execcb)(unsigned addr); ECL_ENTRY void (*biz_readcb)(unsigned addr); ECL_ENTRY void (*biz_writecb)(unsigned addr); CDCallback biz_cdcb = NULL; ECL_ENTRY void (*cdd_readcallback)(int lba, void *dest, int subcode); uint8 *tempsram; static void update_viewport(void) { vwidth = bitmap.viewport.w + (bitmap.viewport.x * 2); vheight = bitmap.viewport.h + (bitmap.viewport.y * 2); if (config.ntsc) { if (reg[12] & 1) vwidth = MD_NTSC_OUT_WIDTH(vwidth); else vwidth = SMS_NTSC_OUT_WIDTH(vwidth); } if (config.render && interlaced) { vheight = vheight * 2; } } GPGX_EX void gpgx_get_video(int *w, int *h, int *pitch, void **buffer) { if (w) *w = vwidth; if (h) *h = vheight; if (pitch) *pitch = bitmap.pitch; if (buffer) *buffer = bitmap.data; } GPGX_EX void gpgx_get_audio(int *n, void **buffer) { if (n) *n = nsamples; if (buffer) *buffer = soundbuffer; } // this is most certainly wrong for interlacing GPGX_EX void gpgx_get_fps(int *num, int *den) { if (vdp_pal) { if (num) *num = 53203424; if (den) *den = 3420 * 313; } else { if (num) *num = 53693175; if (den) *den = 3420 * 262; } } void osd_input_update(void) { } ECL_ENTRY void (*input_callback_cb)(void); void real_input_callback(void) { if (input_callback_cb) input_callback_cb(); } GPGX_EX void gpgx_set_input_callback(ECL_ENTRY void (*fecb)(void)) { input_callback_cb = fecb; } GPGX_EX void gpgx_set_cdd_callback(ECL_ENTRY void (*cddcb)(int lba, void *dest, int subcode)) { cdd_readcallback = cddcb; } ECL_ENTRY int (*load_archive_cb)(const char *filename, unsigned char *buffer, int maxsize); // return 0 on failure, else actual loaded size // extension, if not null, should be populated with the extension of the file loaded // (up to 3 chars and null terminator, no more) int load_archive(const char *filename, unsigned char *buffer, int maxsize, char *extension) { if (extension) memcpy(extension, romextension, 4); return load_archive_cb(filename, buffer, maxsize); } GPGX_EX int gpgx_get_control(t_input *dest, int bytes) { if (bytes != sizeof(t_input)) return 0; memcpy(dest, &input, sizeof(t_input)); return 1; } GPGX_EX int gpgx_put_control(t_input *src, int bytes) { if (bytes != sizeof(t_input)) return 0; memcpy(&input, src, sizeof(t_input)); return 1; } GPGX_EX void gpgx_advance(void) { if (system_hw == SYSTEM_MCD) system_frame_scd(0); else if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) system_frame_gen(0); else system_frame_sms(0); if (bitmap.viewport.changed & 1) { bitmap.viewport.changed &= ~1; update_viewport(); } nsamples = audio_update(soundbuffer); } extern toc_t pending_toc; extern int8 cd_index; GPGX_EX void gpgx_swap_disc(const toc_t* toc, int8 index) { if (system_hw == SYSTEM_MCD) { if (toc) { char header[0x210]; cd_index = index; memcpy(&pending_toc, toc, sizeof(toc_t)); cdd_load("HOTSWAP_CD", header); } else { cd_index = -1; cdd_unload(); } cdd_reset(); } } typedef struct { uint32 width; // in cells uint32 height; uint32 baseaddr; } nametable_t; typedef struct { uint8 *vram; // 64K vram uint8 *patterncache; // every pattern, first normal, then hflip, vflip, bothflip uint32 *colorcache; // 64 colors nametable_t nta; nametable_t ntb; nametable_t ntw; } vdpview_t; extern uint8 bg_pattern_cache[0x80000]; uint32_t pixel[0x100]; GPGX_EX void gpgx_get_vdp_view(vdpview_t *view) { view->vram = vram; view->patterncache = bg_pattern_cache; view->colorcache = pixel + 0x40; view->nta.width = 1 << (playfield_shift - 1); view->ntb.width = 1 << (playfield_shift - 1); view->nta.height = (playfield_row_mask + 1) >> 3; view->ntb.height = (playfield_row_mask + 1) >> 3; view->ntw.width = 1 << (5 + (reg[12] & 1)); view->ntw.height = 32; view->nta.baseaddr = ntab; view->ntb.baseaddr = ntbb; view->ntw.baseaddr = ntwb; } // internal: computes sram size (no brams) static int saveramsize(void) { // the variables in SRAM_T are all part of "configuration", so we don't have to save those. // the only thing that needs to be saved is the SRAM itself and the SEEPROM struct (if applicable) if (!sram.on) return 0; switch (sram.custom) { case 0: // plain bus access saveram break; case 1: // i2c return eeprom_i2c_get_size(); case 2: // spi return sizeof(sram.sram); // it doesn't appear to mask anything internally case 3: // 93c return 128; // limited to 128 bytes (note: SMS only) default: return sizeof(sram.sram); // ??? } // figure size for plain bus access saverams { int startaddr = sram.start / 8192; int endaddr = sram.end / 8192 + 1; int size = (endaddr - startaddr) * 8192; return size; } } GPGX_EX void gpgx_clear_sram(void) { // clear sram if (sram.on) memset(sram.sram, 0xff, 0x10000); if (cdd.loaded) { // clear and format bram memset(scd.bram, 0, 0x2000); brm_format[0x10] = brm_format[0x12] = brm_format[0x14] = brm_format[0x16] = 0x00; brm_format[0x11] = brm_format[0x13] = brm_format[0x15] = brm_format[0x17] = (0x2000 / 64) - 3; memcpy(scd.bram + 0x2000 - 0x40, brm_format, 0x40); if (scd.cartridge.id) { // clear and format ebram memset(scd.cartridge.area, 0x00, scd.cartridge.mask + 1); brm_format[0x10] = brm_format[0x12] = brm_format[0x14] = brm_format[0x16] = (((scd.cartridge.mask + 1) / 64) - 3) >> 8; brm_format[0x11] = brm_format[0x13] = brm_format[0x15] = brm_format[0x17] = (((scd.cartridge.mask + 1) / 64) - 3) & 0xff; memcpy(scd.cartridge.area + scd.cartridge.mask + 1 - 0x40, brm_format, 0x40); } } } // a bit hacky: // in order to present a single memory block to the frontend, // we copy the both the bram and the ebram to another area GPGX_EX void* gpgx_get_sram(int *size) { if (sram.on) { // codies is not actually battery backed, don't expose it as SRAM if (sms_cart_is_codies()) { *size = 0; return NULL; } *size = saveramsize(); return sram.sram; } else if (cdd.loaded && scd.cartridge.id) { int sz = scd.cartridge.mask + 1; memcpy(tempsram, scd.cartridge.area, sz); memcpy(tempsram + sz, scd.bram, 0x2000); *size = sz + 0x2000; return tempsram; } else if (cdd.loaded) { *size = 0x2000; return scd.bram; } else if (scd.cartridge.id) { *size = scd.cartridge.mask + 1; return scd.cartridge.area; } else { *size = 0; return NULL; } } GPGX_EX int gpgx_put_sram(const uint8 *data, int size) { if (sram.on) { if (size != saveramsize()) return 0; memcpy(sram.sram, data, size); return 1; } else if (cdd.loaded && scd.cartridge.id) { int sz = scd.cartridge.mask + 1; if (size != sz + 0x2000) return 0; memcpy(scd.cartridge.area, data, sz); memcpy(scd.bram, data + sz, 0x2000); return 1; } else if (cdd.loaded) { if (size != 0x2000) return 0; memcpy(scd.bram, data, size); return 1; } else if (scd.cartridge.id) { int sz = scd.cartridge.mask + 1; if (size != sz) return 0; memcpy(scd.cartridge.area, data, size); return 1; } else { if (size != 0) return 0; return 1; // "successful"? } } GPGX_EX void gpgx_poke_cram(int addr, uint8 val) { uint16 *p; uint16 data; int index; p = (uint16 *)&cram[addr & 0x7E]; data = *p; if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) { data = ((data & 0x1C0) << 3) | ((data & 0x038) << 2) | ((data & 0x007) << 1); } if (addr & 1) { data &= 0xFF00; data |= val; } else { data &= 0x00FF; data |= val << 8; } if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) { data = ((data & 0xE00) >> 3) | ((data & 0x0E0) >> 2) | ((data & 0x00E) >> 1); } if (*p != data) { index = (addr >> 1) & 0x3F; *p = data; if (index & 0x0F) { color_update_m5(index, data); } if (index == border) { color_update_m5(0x00, data); } } } GPGX_EX void gpgx_poke_vram(int addr, uint8 val) { uint8 *p; addr &= 0xFFFF; p = &vram[addr]; if (*p != val) { int name; *p = val; // copy of MARK_BG_DIRTY(addr) (to avoid putting this code in vdp_ctrl.c) name = (addr >> 5) & 0x7FF; if (bg_name_dirty[name] == 0) { bg_name_list[bg_list_index++] = name; } bg_name_dirty[name] |= (1 << ((addr >> 2) & 7)); } } GPGX_EX void gpgx_flush_vram(void) { if (bg_list_index) { update_bg_pattern_cache(bg_list_index); bg_list_index = 0; } } GPGX_EX const char* gpgx_get_memdom(int which, void **area, int *size) { if (!area || !size) return NULL; switch (which) { case 0: if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) { *area = work_ram; *size = 0x10000; return "68K RAM"; } else if (system_hw == SYSTEM_SG) { *area = work_ram; *size = 0x400; return "Main RAM"; } else if (system_hw == SYSTEM_SGII) { *area = work_ram; *size = 0x800; return "Main RAM"; } else { *area = work_ram; *size = 0x2000; return "Main RAM"; } case 1: if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) { *area = zram; *size = 0x2000; return "Z80 RAM"; } else return NULL; case 2: if (!cdd.loaded) { *area = ext.md_cart.rom; *size = ext.md_cart.romsize; if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) { return "MD CART"; } else { return "ROM"; } } else if (scd.cartridge.id) { *area = scd.cartridge.area; *size = scd.cartridge.mask + 1; return "EBRAM"; } else return NULL; case 3: if (cdd.loaded) { *area = scd.bootrom; *size = 0x20000; return "CD BOOT ROM"; } else return NULL; case 4: if (cdd.loaded) { *area = scd.prg_ram; *size = 0x80000; return "CD PRG RAM"; } else return NULL; case 5: if (cdd.loaded) { *area = scd.word_ram[0]; *size = 0x20000; return "CD WORD RAM[0] (1M)"; } else return NULL; case 6: if (cdd.loaded) { *area = scd.word_ram[1]; *size = 0x20000; return "CD WORD RAM[1] (1M)"; } else return NULL; case 7: if (cdd.loaded) { *area = scd.word_ram_2M; *size = 0x40000; return "CD WORD RAM (2M)"; } else return NULL; case 8: if (cdd.loaded) { *area = scd.bram; *size = 0x2000; return "CD BRAM"; } else return NULL; case 9: if (system_bios & SYSTEM_MD) { *area = boot_rom; *size = 0x800; return "MD BOOT ROM"; } else if (system_bios & (SYSTEM_SMS | SYSTEM_GG)) { *area = &ext.md_cart.rom[0x400000]; *size = sms_cart_bootrom_size(); return "BOOT ROM"; } else return NULL; case 10: // these should be mutually exclusive if (sram.on) { *area = sram.sram; *size = saveramsize(); // Codemasters mapper SRAM is only used by 1 game // and that 1 game does not actually have a battery // (this also mimics SMSHawk's behavior) if (sms_cart_is_codies()) { return "Cart (Volatile) RAM"; } return "SRAM"; } else if ((system_hw & SYSTEM_PBC) != SYSTEM_MD) { *area = &work_ram[0x2000]; *size = sms_cart_ram_size(); return "Cart (Volatile) RAM"; } else return NULL; case 11: if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) { // MD has more CRAM *size = 0x80; } else { *size = 0x40; } *area = cram; return "CRAM"; case 12: *area = vsram; *size = 128; return "VSRAM"; case 13: if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) { // MD has more VRAM *size = 0x10000; } else { *size = 0x4000; } *area = vram; return "VRAM"; default: return NULL; } } GPGX_EX void gpgx_write_z80_bus(unsigned addr, unsigned data) { // note: this is not valid for MD z80_writemap[addr >> 10][addr & 0x3FF] = data; } GPGX_EX void gpgx_write_m68k_bus(unsigned addr, unsigned data) { cpu_memory_map m = m68k.memory_map[addr >> 16 & 0xff]; if (m.base && !m.write8) m.base[(addr & 0xffff) ^ 1] = data; } GPGX_EX void gpgx_write_s68k_bus(unsigned addr, unsigned data) { cpu_memory_map m = s68k.memory_map[addr >> 16 & 0xff]; if (m.base && !m.write8) m.base[(addr & 0xffff) ^ 1] = data; } GPGX_EX unsigned gpgx_peek_z80_bus(unsigned addr) { // note: this is not valid for MD return z80_readmap[addr >> 10][addr & 0x3FF]; } GPGX_EX unsigned gpgx_peek_m68k_bus(unsigned addr) { cpu_memory_map m = m68k.memory_map[addr >> 16 & 0xff]; if (m.base && !m.read8) return m.base[(addr & 0xffff) ^ 1]; else return 0xff; } GPGX_EX unsigned gpgx_peek_s68k_bus(unsigned addr) { cpu_memory_map m = s68k.memory_map[addr >> 16 & 0xff]; if (m.base && !m.read8) return m.base[(addr & 0xffff) ^ 1]; else return 0xff; } enum SMSFMSoundChipType { YM2413_DISABLED, YM2413_MAME, YM2413_NUKED }; enum GenesisFMSoundChipType { MAME_YM2612, MAME_ASIC_YM3438, MAME_Enhanced_YM3438, Nuked_YM2612, Nuked_YM3438 }; struct InitSettings { uint32_t BackdropColor; int32_t Region; int32_t ForceVDP; uint16_t LowPassRange; int16_t LowFreq; int16_t HighFreq; int16_t LowGain; int16_t MidGain; int16_t HighGain; uint8_t Filter; uint8_t InputSystemA; uint8_t InputSystemB; uint8_t SixButton; uint8_t ForceSram; uint8_t SMSFMSoundChip; uint8_t GenesisFMSoundChip; uint8_t SpritesAlwaysOnTop; uint8_t LoadBios; uint8_t Overscan; uint8_t GGExtra; }; #ifdef HOOK_CPU #ifdef USE_BIZHAWK_CALLBACKS void CDLog68k(uint addr, uint flags) { addr &= 0x00FFFFFF; //check for sram region first if(sram.on) { if(addr >= sram.start && addr <= sram.end) { biz_cdcb(addr - sram.start, eCDLog_AddrType_SRAM, flags); return; } } if(addr < 0x400000) { uint block64k_rom; //apply memory map to process rom address unsigned char* block64k = m68k.memory_map[((addr)>>16)&0xff].base; //outside the ROM range. complex mapping logic/accessories; not sure how to handle any of this if(block64k < cart.rom || block64k >= cart.rom + cart.romsize) return; block64k_rom = block64k - cart.rom; addr = ((addr) & 0xffff) + block64k_rom; //outside the ROM range somehow if(addr >= cart.romsize) return; biz_cdcb(addr, eCDLog_AddrType_MDCART, flags); return; } if(addr > 0xFF0000) { //no memory map needed biz_cdcb(addr & 0xFFFF, eCDLog_AddrType_RAM68k, flags); return; } } void bk_cpu_hook(hook_type_t type, int width, unsigned int address, unsigned int value) { switch (type) { case HOOK_M68K_E: { if (biz_execcb) biz_execcb(address); if (biz_cdcb) { CDLog68k(address, eCDLog_Flags_Exec68k); CDLog68k(address + 1, eCDLog_Flags_Exec68k); } break; } case HOOK_M68K_R: { if (biz_readcb) biz_readcb(address); break; } case HOOK_M68K_W: { if (biz_writecb) biz_writecb(address); break; } default: break; } } #endif // USE_BIZHAWK_CALLBACKS #endif // HOOK_CPU GPGX_EX int gpgx_init(const char* feromextension, ECL_ENTRY int (*feload_archive_cb)(const char *filename, unsigned char *buffer, int maxsize), struct InitSettings *settings) { fprintf(stderr, "Initializing GPGX native...\n"); cinterface_force_sram = settings->ForceSram; memset(&bitmap, 0, sizeof(bitmap)); strncpy(romextension, feromextension, 3); romextension[3] = 0; load_archive_cb = feload_archive_cb; bitmap.width = 1024; bitmap.height = 512; bitmap.pitch = 1024 * 4; bitmap.data = alloc_invisible(2 * 1024 * 1024); tempsram = alloc_invisible(0x100000 + 0x2000); // Initializing ram deepfreeze list #ifdef USE_RAM_DEEPFREEZE deepfreeze_list_size = 0; #endif /* sound options */ config.psg_preamp = 150; config.fm_preamp = 100; config.cdda_volume = 100; config.pcm_volume = 100; config.hq_fm = 1; config.hq_psg = 1; config.filter = settings->Filter; //0; /* no filter */ config.lp_range = settings->LowPassRange; //0x9999; /* 0.6 in 16.16 fixed point */ config.low_freq = settings->LowFreq; //880; config.high_freq = settings->HighFreq; //5000; config.lg = settings->LowGain; //100; config.mg = settings->MidGain; //100; config.hg = settings->HighGain; //100; config.mono = 0; config.ym3438 = 0; // Selecting FM Sound chip to use for SMS / GG emulation. Using a default for now, until we also // accept this core for SMS/GG emulation in BizHawk switch (settings->SMSFMSoundChip) { case YM2413_DISABLED: config.opll = 0; config.ym2413 = 0; break; case YM2413_MAME: config.opll = 0; config.ym2413 = 1; break; case YM2413_NUKED: config.opll = 1; config.ym2413 = 1; break; } // Selecting FM Sound chip to use for Genesis / Megadrive / CD emulation switch (settings->GenesisFMSoundChip) { case MAME_YM2612: config.ym2612 = YM2612_DISCRETE; YM2612Config(YM2612_DISCRETE); break; case MAME_ASIC_YM3438: config.ym2612 = YM2612_INTEGRATED; YM2612Config(YM2612_INTEGRATED); break; case MAME_Enhanced_YM3438: config.ym2612 = YM2612_ENHANCED; YM2612Config(YM2612_ENHANCED); break; case Nuked_YM2612: OPN2_SetChipType(ym3438_mode_ym2612); config.ym3438 = 1; break; case Nuked_YM3438: OPN2_SetChipType(ym3438_mode_readmode); config.ym3438 = 2; break; } /* system options */ config.system = 0; /* = AUTO (or SYSTEM_SG, SYSTEM_SGII, SYSTEM_SGII_RAM_EXT, SYSTEM_MARKIII, SYSTEM_SMS, SYSTEM_SMS2, SYSTEM_GG, SYSTEM_MD) */ config.region_detect = settings->Region; /* 0 = AUTO, 1 = USA, 2 = EUROPE, 3 = JAPAN/NTSC, 4 = JAPAN/PAL */ config.vdp_mode = settings->ForceVDP; /* 0 = AUTO, 1 = NTSC, 2 = PAL */ config.master_clock = 0; /* = AUTO (1 = NTSC, 2 = PAL) */ config.force_dtack = 0; config.addr_error = 1; config.bios = settings->LoadBios ? 3 : 0; /* Load BIOS and do NOT unload cartridge (bit 0: load bios, bit 1: keep cartridge loaded) */ config.lock_on = 0; /* = OFF (or TYPE_SK, TYPE_GG & TYPE_AR) */ config.add_on = 0; /* = HW_ADDON_AUTO (or HW_ADDON_MEGACD, HW_ADDON_MEGASD & HW_ADDON_ONE) */ config.cd_latency = 1; /* display options */ config.overscan = settings->Overscan; /* 3 = all borders (0 = no borders , 1 = vertical borders only, 2 = horizontal borders only) */ config.gg_extra = settings->GGExtra; /* 1 = show extended Game Gear screen (256x192) */ config.render = 1; /* 1 = double resolution output (only when interlaced mode 2 is enabled) */ config.ntsc = 0; config.lcd = 0; /* 0.8 fixed point */ config.enhanced_vscroll = 0; config.enhanced_vscroll_limit = 8; config.sprites_always_on_top = settings->SpritesAlwaysOnTop; // set overall input system type // usual is MD GAMEPAD or NONE // TEAMPLAYER, WAYPLAY, ACTIVATOR, XEA1P, MOUSE need to be specified // everything else is auto or master system only // XEA1P is port 1 only // WAYPLAY is both ports at same time only input.system[0] = settings->InputSystemA; input.system[1] = settings->InputSystemB; cinterface_custom_backdrop_color = settings->BackdropColor; // Default: Genesis // apparently, the only part of config.input used is the padtype identifier, // and that's used only for choosing pad type when system_md for (int i = 0; i < MAX_INPUTS; i++) config.input[i].padtype = settings->SixButton ? DEVICE_PAD6B : DEVICE_PAD3B; // Hacky but effective. Setting the correct controller type here if this is sms or GG // note that we can't use system_hw yet, that's set in load_rom if (memcmp("GEN", &romextension[0], 3) != 0) { for (int i = 0; i < MAX_INPUTS; i++) config.input[i].padtype = DEVICE_PAD2B; } else { // gpgx won't load the genesis bootrom itself, we have to do that manually if (config.bios & 1) { // not fatal if this fails (unless we're recording a movie, which we handle elsewhere) if (load_archive(MD_BIOS, boot_rom, sizeof(boot_rom), NULL) != 0) { #ifdef LSB_FIRST // gpgx also expects us to byteswap the boot rom for (int i = 0; i < sizeof(boot_rom); i += 2) { uint8 temp = boot_rom[i]; boot_rom[i] = boot_rom[i+1]; boot_rom[i+1] = temp; } #endif system_bios |= SYSTEM_MD; } } } // first try to load our main CD if (!load_rom("PRIMARY_CD")) { // otherwise, try to load our ROM if (!load_rom("PRIMARY_ROM")) { return 0; } } audio_init(44100, 0); system_init(); system_reset(); update_viewport(); gpgx_clear_sram(); load_archive_cb = NULL; // don't hold onto load_archive_cb for longer than we need it for return 1; } #ifdef USE_RAM_DEEPFREEZE GPGX_EX int gpgx_add_deepfreeze_list_entry(const int address, const uint8_t value) { // Prevent overflowing if (deepfreeze_list_size == MAX_DEEP_FREEZE_ENTRIES) return -1; deepfreeze_list[deepfreeze_list_size].address = address; deepfreeze_list[deepfreeze_list_size].value = value; deepfreeze_list_size++; return 0; } GPGX_EX void gpgx_clear_deepfreeze_list() { deepfreeze_list_size = 0; } #endif GPGX_EX void gpgx_reset(int hard) { if (hard) system_reset(); else gen_reset(0); } GPGX_EX void gpgx_set_mem_callback(ECL_ENTRY void (*read)(unsigned), ECL_ENTRY void (*write)(unsigned), ECL_ENTRY void (*exec)(unsigned)) { biz_readcb = read; biz_writecb = write; biz_execcb = exec; set_cpu_hook((biz_readcb || biz_writecb || biz_execcb || biz_cdcb) ? bk_cpu_hook : NULL); } GPGX_EX void gpgx_set_cd_callback(CDCallback cdcallback) { biz_cdcb = cdcallback; set_cpu_hook((biz_readcb || biz_writecb || biz_execcb || biz_cdcb) ? bk_cpu_hook : NULL); } GPGX_EX void gpgx_set_draw_mask(int mask) { cinterface_render_bga = !!(mask & 1); cinterface_render_bgb = !!(mask & 2); cinterface_render_bgw = !!(mask & 4); cinterface_render_obj = !!(mask & 8); cinterface_custom_backdrop = !!(mask & 16); if (reg[1] & 0x04) { if (cinterface_custom_backdrop) color_update_m5(0, 0); else color_update_m5(0x00, *(uint16 *)&cram[border << 1]); } } GPGX_EX void gpgx_set_sprite_limit_enabled(int enabled) { config.no_sprite_limit = !enabled; } GPGX_EX void gpgx_invalidate_pattern_cache(void) { bg_list_index = (reg[1] & 0x04) ? 0x800 : 0x200; for (int i = 0; i < bg_list_index; i++) { bg_name_list[i] = i; bg_name_dirty[i] = 0xFF; } } typedef struct { unsigned int value; const char *name; } gpregister_t; GPGX_EX int gpgx_getmaxnumregs(void) { return 57; } GPGX_EX int gpgx_getregs(gpregister_t *regs) { int ret = 0; // 22 if ((system_hw & SYSTEM_PBC) == SYSTEM_MD) { #define MAKEREG(x) regs->name = "M68K " #x; regs->value = m68k_get_reg(M68K_REG_##x); regs++; ret++; MAKEREG(A0); MAKEREG(A1); MAKEREG(A2); MAKEREG(A3); MAKEREG(A4); MAKEREG(A5); MAKEREG(A6); MAKEREG(A7); MAKEREG(D0); MAKEREG(D1); MAKEREG(D2); MAKEREG(D3); MAKEREG(D4); MAKEREG(D5); MAKEREG(D6); MAKEREG(D7); MAKEREG(PC); MAKEREG(SR); MAKEREG(SP); MAKEREG(USP); MAKEREG(ISP); MAKEREG(IR); #undef MAKEREG } // 13 #define MAKEREG(x) regs->name = "Z80 " #x; regs->value = Z80.x.d; regs++; ret++; MAKEREG(pc); MAKEREG(sp); MAKEREG(af); MAKEREG(bc); MAKEREG(de); MAKEREG(hl); MAKEREG(ix); MAKEREG(iy); MAKEREG(wz); MAKEREG(af2); MAKEREG(bc2); MAKEREG(de2); MAKEREG(hl2); #undef MAKEREG // 22 if (system_hw == SYSTEM_MCD) { #define MAKEREG(x) regs->name = "S68K " #x; regs->value = s68k_get_reg(M68K_REG_##x); regs++; ret++; MAKEREG(A0); MAKEREG(A1); MAKEREG(A2); MAKEREG(A3); MAKEREG(A4); MAKEREG(A5); MAKEREG(A6); MAKEREG(A7); MAKEREG(D0); MAKEREG(D1); MAKEREG(D2); MAKEREG(D3); MAKEREG(D4); MAKEREG(D5); MAKEREG(D6); MAKEREG(D7); MAKEREG(PC); MAKEREG(SR); MAKEREG(SP); MAKEREG(USP); MAKEREG(ISP); MAKEREG(IR); #undef MAKEREG } return ret; }