diff --git a/src/emulator.c b/src/emulator.c index fd9e4c1d..76905593 100644 --- a/src/emulator.c +++ b/src/emulator.c @@ -601,9 +601,11 @@ static void emu_run_frame(struct emu *emu) { void emu_render_frame(struct emu *emu) { prof_counter_add(COUNTER_frames, 1); - /* check that we're not being called between calls to emu_video_destroyed - and emu_video_created */ - CHECK_NOTNULL(emu->r); + if (!dc_running(emu->dc)) { + /* not running, just build debug menu */ + emu_debug_menu(emu); + return; + } int width = r_width(emu->r); int height = r_height(emu->r); diff --git a/src/guest/aica/aica.c b/src/guest/aica/aica.c index b08d4be7..98d000f1 100644 --- a/src/guest/aica/aica.c +++ b/src/guest/aica/aica.c @@ -1016,7 +1016,7 @@ struct aica *aica_create(struct dreamcast *dc) { aica_init_tables(); struct aica *aica = - dc_create_device(dc, sizeof(struct aica), "aica", &aica_init); + dc_create_device(dc, sizeof(struct aica), "aica", &aica_init, NULL); /* assign ids */ for (int i = 0; i < AICA_NUM_CHANNELS; i++) { diff --git a/src/guest/arm7/arm7.c b/src/guest/arm7/arm7.c index 89d82765..848c5054 100644 --- a/src/guest/arm7/arm7.c +++ b/src/guest/arm7/arm7.c @@ -238,7 +238,7 @@ void arm7_destroy(struct arm7 *arm) { struct arm7 *arm7_create(struct dreamcast *dc) { struct arm7 *arm = - dc_create_device(dc, sizeof(struct arm7), "arm", &arm7_init); + dc_create_device(dc, sizeof(struct arm7), "arm", &arm7_init, NULL); arm->execute_if = dc_create_execute_interface(&arm7_run, 0); arm->memory_if = dc_create_memory_interface(dc, &arm7_data_map); diff --git a/src/guest/bios/bios.c b/src/guest/bios/bios.c index ae45a26d..30d0ca9d 100644 --- a/src/guest/bios/bios.c +++ b/src/guest/bios/bios.c @@ -12,13 +12,13 @@ #include "guest/sh4/sh4.h" #include "imgui.h" -DEFINE_PERSISTENT_OPTION_STRING(region, "auto", "System region"); +DEFINE_PERSISTENT_OPTION_STRING(region, "usa", "System region"); DEFINE_PERSISTENT_OPTION_STRING(language, "english", "System language"); DEFINE_PERSISTENT_OPTION_STRING(broadcast, "ntsc", "System broadcast mode"); /* system settings */ static const char *regions[] = { - "japan", "usa", "europe", "auto", + "japan", "usa", "europe", }; static const char *languages[] = { @@ -94,15 +94,6 @@ static void bios_override_settings(struct bios *bios) { } } - /* if "auto" is specified, select first supported region */ - if (region == ARRAY_SIZE(regions) - 1) { - int supported_regions = DISC_REGION_ALL; - if (gdrom_has_disc(gd)) { - supported_regions = gdrom_get_regions(gd); - } - region = ctz32((uint32_t)supported_regions); - } - LOG_INFO("bios_override_settings region=%s lang=%s bcast=%s", regions[region], languages[lang], broadcasts[bcast]); @@ -304,8 +295,31 @@ static int bios_boot(struct bios *bios) { return 1; } -static int bios_init(struct device *dev) { +static int bios_post_init(struct device *dev) { struct bios *bios = (struct bios *)dev; + + bios_validate_flash(bios); + + bios_override_settings(bios); + +/* this code enables a "hybrid" hle mode. in this mode, syscalls are patched + to trap into their hle handlers, but the real bios can still be ran to + test if bugs exist in the syscall emulation or bootstrap emulation */ +#if 0 + /* write out invalid instructions at syscall entry points. note, the boot rom + does a bootstrap on startup which copies the boot rom into system ram. due + to this, the invalid instructions are written to the original rom, not the + system ram (or else, they would be overwritten by the bootstrap process) */ + struct boot *boot = bios->dc->boot; + uint16_t invalid = 0x0; + + boot_write(boot, SYSCALL_FONTROM, &invalid, 2); + boot_write(boot, SYSCALL_SYSINFO, &invalid, 2); + boot_write(boot, SYSCALL_FLASHROM, &invalid, 2); + boot_write(boot, SYSCALL_GDROM, &invalid, 2); + /*boot_write(boot, SYSCALL_MENU, &invalid, 2);*/ +#endif + return 1; } @@ -407,38 +421,12 @@ int bios_invalid_instr(struct bios *bios) { return handled; } -int bios_preboot(struct bios *bios) { - bios_validate_flash(bios); - - bios_override_settings(bios); - -/* this code enables a "hybrid" hle mode. in this mode, syscalls are patched - to trap into their hle handlers, but the real bios can still be ran to - test if bugs exist in the syscall emulation or bootstrap emulation */ -#if 0 - /* write out invalid instructions at syscall entry points. note, the boot rom - does a bootstrap on startup which copies the boot rom into system ram. due - to this, the invalid instructions are written to the original rom, not the - system ram (or else, they would be overwritten by the bootstrap process) */ - struct boot *boot = bios->dc->boot; - uint16_t invalid = 0x0; - - boot_write(boot, SYSCALL_FONTROM, &invalid, 2); - boot_write(boot, SYSCALL_SYSINFO, &invalid, 2); - boot_write(boot, SYSCALL_FLASHROM, &invalid, 2); - boot_write(boot, SYSCALL_GDROM, &invalid, 2); - /*boot_write(boot, SYSCALL_MENU, &invalid, 2);*/ -#endif - - return 1; -} - void bios_destroy(struct bios *bios) { free(bios); } struct bios *bios_create(struct dreamcast *dc) { struct bios *bios = - dc_create_device(dc, sizeof(struct bios), "bios", &bios_init); + dc_create_device(dc, sizeof(struct bios), "bios", NULL, &bios_post_init); return bios; } diff --git a/src/guest/bios/bios.h b/src/guest/bios/bios.h index 99cf22ba..19c07973 100644 --- a/src/guest/bios/bios.h +++ b/src/guest/bios/bios.h @@ -18,9 +18,7 @@ struct bios { struct bios *bios_create(struct dreamcast *dc); void bios_destroy(struct bios *bios); -int bios_preboot(struct bios *bios); int bios_invalid_instr(struct bios *bios); - void bios_debug_menu(struct bios *bios); #endif diff --git a/src/guest/dreamcast.c b/src/guest/dreamcast.c index 6e618e22..b8dee542 100644 --- a/src/guest/dreamcast.c +++ b/src/guest/dreamcast.c @@ -70,28 +70,28 @@ void dc_suspend(struct dreamcast *dc) { dc->running = 0; } -int dc_load(struct dreamcast *dc, const char *path) { - struct disc *disc = NULL; +int dc_running(struct dreamcast *dc) { + return dc->running; +} +int dc_load(struct dreamcast *dc, const char *path) { if (path) { LOG_INFO("dc_load path=%s", path); struct disc *disc = disc_create(path); + if (!disc) { - LOG_WARNING("dc_load failed"); + LOG_WARNING("dc_load_game failed"); return 0; } - /* boot to bios bootstrap if disc is valid */ gdrom_set_disc(dc->gdrom, disc); - bios_preboot(dc->bios); - sh4_reset(dc->sh4, 0xa0000000); } else { - /* boot to main menu of no path specified */ - bios_preboot(dc->bios); - sh4_reset(dc->sh4, 0xa0000000); + LOG_INFO("dc_load no path supplied, loading bios"); } + /* boot to bios bootstrap */ + sh4_reset(dc->sh4, 0xa0000000); dc_resume(dc); return 1; @@ -129,8 +129,15 @@ int dc_init(struct dreamcast *dc) { /* initialize each device */ list_for_each_entry(dev, &dc->devices, struct device, it) { - if (!dev->init(dev)) { - LOG_WARNING("dc_init failed to initialize device '%s'", dev->name); + if (dev->init && !dev->init(dev)) { + LOG_WARNING("dc_init init callback failed for '%s'", dev->name); + return 0; + } + } + + list_for_each_entry(dev, &dc->devices, struct device, it) { + if (dev->post_init && !dev->post_init(dev)) { + LOG_WARNING("dc_init post_init callback failed for '%s'", dev->name); return 0; } } @@ -200,12 +207,13 @@ struct device *dc_get_device(struct dreamcast *dc, const char *name) { } void *dc_create_device(struct dreamcast *dc, size_t size, const char *name, - device_init_cb init) { + device_init_cb init, device_post_init_cb post_init) { struct device *dev = calloc(1, size); dev->dc = dc; dev->name = name; dev->init = init; + dev->post_init = post_init; list_add(&dc->devices, &dev->it); diff --git a/src/guest/dreamcast.h b/src/guest/dreamcast.h index f5594455..c0fe6b23 100644 --- a/src/guest/dreamcast.h +++ b/src/guest/dreamcast.h @@ -89,12 +89,21 @@ struct memory_interface { * device */ typedef int (*device_init_cb)(struct device *); +typedef int (*device_post_init_cb)(struct device *); struct device { struct dreamcast *dc; const char *name; + + /* called for each device during dc_init. at this point each device should + initialize their own state, but not depend on the state of others */ device_init_cb init; + /* called for each device during dc_init, immediately after each device's + init callback has been called. devices should perform initialization + that depends on other device's state here */ + device_post_init_cb post_init; + /* optional interfaces */ struct debug_interface *debug_if; struct execute_interface *execute_if; @@ -161,7 +170,7 @@ struct dreamcast *dc_create(); void dc_destroy(struct dreamcast *dc); void *dc_create_device(struct dreamcast *dc, size_t size, const char *name, - device_init_cb init); + device_init_cb init, device_post_init_cb post_init); struct device *dc_get_device(struct dreamcast *dc, const char *name); void dc_destroy_device(struct device *dev); @@ -183,6 +192,7 @@ void dc_destroy_memory_interface(struct memory_interface *memory); int dc_init(struct dreamcast *dc); int dc_load(struct dreamcast *dc, const char *path); +int dc_running(struct dreamcast *dc); void dc_suspend(struct dreamcast *dc); void dc_resume(struct dreamcast *dc); void dc_tick(struct dreamcast *dc, int64_t ns); diff --git a/src/guest/gdrom/disc.c b/src/guest/gdrom/disc.c index be6d6131..3d9225d8 100644 --- a/src/guest/gdrom/disc.c +++ b/src/guest/gdrom/disc.c @@ -6,6 +6,14 @@ #include "guest/gdrom/gdi.h" #include "guest/gdrom/iso.h" +/* ip.bin layout */ +#define IP_OFFSET_META 0x0000 /* meta information */ +#define IP_OFFSET_TOC 0x0100 /* table of contents */ +#define IP_OFFSET_LICENSE 0x0300 /* license screen code */ +#define IP_OFFSET_AREAS 0x3700 /* area protection symbols */ +#define IP_OFFSET_BOOT1 0x3800 /* bootstrap 1 */ +#define IP_OFFSET_BOOT2 0x6000 /* bootstrap 2 */ + /* meta information found in the ip.bin */ struct disc_meta { char hardware_id[16]; @@ -31,6 +39,31 @@ static void disc_get_meta(struct disc *disc, struct disc_meta *meta) { memcpy(meta, tmp, sizeof(*meta)); } +static void disc_patch_regions(struct disc *disc, int fad, uint8_t *data) { + /* patch discs to boot in all regions by patching data read from the disk. for + a disc to be boot for a region, the region must be enabled in two places: + + 1.) in the meta information section of the ip.bin + 2.) in the area protection symbols section of the ip.bin */ + if (fad == disc->meta_fad) { + /* the area symbols in the meta information contains 8 characters, each of + which is either a space, or the first letter of the area if supported */ + struct disc_meta *meta = (struct disc_meta *)data; + strncpy_pad_spaces(meta->area_symbols, "JUE", sizeof(meta->area_symbols)); + } else if (fad == disc->area_fad) { + /* the area protection symbols section contains 8 slots, each of which is + either spaces, or the name of the area if supported. note, each slot + has a 4-byte code prefix which jumps past it as part of the bootstrap + control flow */ + char *slot0 = (char *)data; + char *slot1 = (char *)data + 32; + char *slot2 = (char *)data + 64; + strncpy_pad_spaces(slot0 + 4, "For JAPAN,TAIWAN,PHILIPINES.", 28); + strncpy_pad_spaces(slot1 + 4, "For USA and CANADA.", 28); + strncpy_pad_spaces(slot2 + 4, "For EUROPE.", 28); + } +} + int disc_read_bytes(struct disc *disc, int fad, int len, uint8_t *dst, int dst_size) { CHECK_LE(len, dst_size); @@ -69,6 +102,7 @@ int disc_read_sectors(struct disc *disc, int fad, int num_sectors, for (int i = fad; i < endfad; i++) { CHECK_LE(read + track->data_size, dst_size); disc->read_sector(disc, track, i, dst + read); + disc_patch_regions(disc, i, dst + read); read += track->data_size; } @@ -205,6 +239,13 @@ struct disc *disc_create(const char *filename) { return NULL; } + /* cache off information about the IP.BIN file location for region patching */ + struct session *session = disc_get_session(disc, 1); + struct track *first_track = disc_get_track(disc, session->first_track); + disc->meta_fad = first_track->fad; + disc->area_fad = first_track->fad + IP_OFFSET_AREAS / first_track->data_size; + disc->area_off = IP_OFFSET_AREAS % first_track->data_size; + /* extract meta information from the IP.BIN */ struct disc_meta meta; disc_get_meta(disc, &meta); @@ -219,20 +260,6 @@ struct disc *disc_create(const char *filename) { sizeof(meta.device_info) - 5); strncpy_trim_space(disc->bootname, meta.bootname, sizeof(meta.bootname)); - /* the area symbols array contains characters, which are either a space or a - specific character corresponding to a particular region the disc is valid - for. if the character for a particular region is a space, the disc is not - valid for that region */ - if (meta.area_symbols[0] == 'J') { - disc->regions |= DISC_REGION_JAPAN; - } - if (meta.area_symbols[1] == 'U') { - disc->regions |= DISC_REGION_USA; - } - if (meta.area_symbols[2] == 'E') { - disc->regions |= DISC_REGION_EUROPE; - } - /* generate unique id for the disc */ snprintf(disc->uid, sizeof(disc->uid), "%s %s %s %s", disc->product_name, disc->product_number, disc->product_version, disc->media_config); diff --git a/src/guest/gdrom/disc.h b/src/guest/gdrom/disc.h index 7d2b9617..06b0fa65 100644 --- a/src/guest/gdrom/disc.h +++ b/src/guest/gdrom/disc.h @@ -44,6 +44,12 @@ struct session { }; struct disc { + /* information about the IP.BIN location on disc, cached to quickly patch + region information */ + int meta_fad; + int area_fad; + int area_off; + /* meta information extracted from IP.BIN */ char uid[DISC_UID_SIZE]; char product_name[DISC_STRING_SIZE]; @@ -51,7 +57,6 @@ struct disc { char product_version[7]; char media_config[12]; char bootname[17]; - int regions; /* media-specific interface */ void (*destroy)(struct disc *); diff --git a/src/guest/gdrom/gdrom.c b/src/guest/gdrom/gdrom.c index 10291200..2b83b4f7 100644 --- a/src/guest/gdrom/gdrom.c +++ b/src/guest/gdrom/gdrom.c @@ -527,12 +527,6 @@ void gdrom_get_bootfile(struct gdrom *gd, int *fad, int *len) { CHECK(res); } -int gdrom_get_regions(struct gdrom *gd) { - CHECK_NOTNULL(gd->disc); - - return gd->disc->regions; -} - void gdrom_get_subcode(struct gdrom *gd, int format, uint8_t *data, int size) { CHECK_NOTNULL(gd->disc); CHECK_GE(size, GD_SPI_SCD_SIZE); @@ -726,7 +720,7 @@ void gdrom_destroy(struct gdrom *gd) { struct gdrom *gdrom_create(struct dreamcast *dc) { struct gdrom *gd = - dc_create_device(dc, sizeof(struct gdrom), "gdrom", &gdrom_init); + dc_create_device(dc, sizeof(struct gdrom), "gdrom", &gdrom_init, NULL); return gd; } diff --git a/src/guest/gdrom/gdrom.h b/src/guest/gdrom/gdrom.h index a46e4b3c..e9fe7b8a 100644 --- a/src/guest/gdrom/gdrom.h +++ b/src/guest/gdrom/gdrom.h @@ -28,7 +28,6 @@ void gdrom_get_session(struct gdrom *gd, int session, struct gd_spi_session *ses); void gdrom_get_subcode(struct gdrom *gd, int format, uint8_t *data, int size); -int gdrom_get_regions(struct gdrom *gd); void gdrom_get_bootfile(struct gdrom *gd, int *fad, int *len); int gdrom_find_file(struct gdrom *gd, const char *filename, int *fad, int *len); diff --git a/src/guest/holly/holly.c b/src/guest/holly/holly.c index 002cf19a..7cea03a7 100644 --- a/src/guest/holly/holly.c +++ b/src/guest/holly/holly.c @@ -390,7 +390,7 @@ void holly_destroy(struct holly *hl) { struct holly *holly_create(struct dreamcast *dc) { struct holly *hl = - dc_create_device(dc, sizeof(struct holly), "holly", &holly_init); + dc_create_device(dc, sizeof(struct holly), "holly", &holly_init, NULL); /* init registers */ #define HOLLY_REG(addr, name, default, type) \ diff --git a/src/guest/maple/maple.c b/src/guest/maple/maple.c index df069ff6..ccb48bd2 100644 --- a/src/guest/maple/maple.c +++ b/src/guest/maple/maple.c @@ -139,7 +139,7 @@ void maple_destroy(struct maple *mp) { struct maple *maple_create(struct dreamcast *dc) { struct maple *mp = - dc_create_device(dc, sizeof(struct maple), "maple", &maple_init); + dc_create_device(dc, sizeof(struct maple), "maple", &maple_init, NULL); /* register a controller and vmu for all ports by default */ for (int i = 0; i < MAPLE_NUM_PORTS; i++) { diff --git a/src/guest/pvr/pvr.c b/src/guest/pvr/pvr.c index 2a831e05..2c4e445c 100644 --- a/src/guest/pvr/pvr.c +++ b/src/guest/pvr/pvr.c @@ -246,7 +246,8 @@ void pvr_destroy(struct pvr *pvr) { } struct pvr *pvr_create(struct dreamcast *dc) { - struct pvr *pvr = dc_create_device(dc, sizeof(struct pvr), "pvr", &pvr_init); + struct pvr *pvr = + dc_create_device(dc, sizeof(struct pvr), "pvr", &pvr_init, NULL); return pvr; } diff --git a/src/guest/pvr/ta.c b/src/guest/pvr/ta.c index 238fbe6e..4e93be63 100644 --- a/src/guest/pvr/ta.c +++ b/src/guest/pvr/ta.c @@ -832,7 +832,7 @@ void ta_destroy(struct ta *ta) { struct ta *ta_create(struct dreamcast *dc) { ta_init_tables(); - struct ta *ta = dc_create_device(dc, sizeof(struct ta), "ta", &ta_init); + struct ta *ta = dc_create_device(dc, sizeof(struct ta), "ta", &ta_init, NULL); return ta; } diff --git a/src/guest/rom/boot.c b/src/guest/rom/boot.c index 56990f62..f5d0668a 100644 --- a/src/guest/rom/boot.c +++ b/src/guest/rom/boot.c @@ -111,7 +111,7 @@ void boot_destroy(struct boot *boot) { struct boot *boot_create(struct dreamcast *dc) { struct boot *boot = - dc_create_device(dc, sizeof(struct boot), "boot", &boot_init); + dc_create_device(dc, sizeof(struct boot), "boot", &boot_init, NULL); return boot; } diff --git a/src/guest/rom/flash.c b/src/guest/rom/flash.c index 3c6fa725..bed5f18d 100644 --- a/src/guest/rom/flash.c +++ b/src/guest/rom/flash.c @@ -201,7 +201,7 @@ void flash_destroy(struct flash *flash) { struct flash *flash_create(struct dreamcast *dc) { struct flash *flash = - dc_create_device(dc, sizeof(struct flash), "flash", &flash_init); + dc_create_device(dc, sizeof(struct flash), "flash", &flash_init, NULL); return flash; } diff --git a/src/guest/sh4/sh4.c b/src/guest/sh4/sh4.c index 1785d2b3..24f5bd23 100644 --- a/src/guest/sh4/sh4.c +++ b/src/guest/sh4/sh4.c @@ -321,7 +321,8 @@ void sh4_destroy(struct sh4 *sh4) { } struct sh4 *sh4_create(struct dreamcast *dc) { - struct sh4 *sh4 = dc_create_device(dc, sizeof(struct sh4), "sh", &sh4_init); + struct sh4 *sh4 = + dc_create_device(dc, sizeof(struct sh4), "sh", &sh4_init, NULL); sh4->debug_if = dc_create_debug_interface( &sh4_dbg_num_registers, &sh4_dbg_step, &sh4_dbg_add_breakpoint, &sh4_dbg_remove_breakpoint, &sh4_dbg_read_memory, &sh4_dbg_read_register);