changed region patching to patch each game for all regions vs

patching the flash settings for a particular supported region

added post_init callback to device, removing the need for the
call to bios_preboot
This commit is contained in:
Anthony Pesch 2017-09-29 20:10:22 -04:00
parent adcebc3779
commit 7e7aa80c62
18 changed files with 122 additions and 89 deletions

View File

@ -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);

View File

@ -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++) {

View File

@ -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);

View File

@ -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;
}

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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 *);

View File

@ -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;
}

View File

@ -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);

View File

@ -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) \

View File

@ -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++) {

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);