|
| 1 | +#include <time.h> |
| 2 | +#include "bios/bios.h" |
| 3 | +#include "bios/flash.h" |
| 4 | +#include "core/math.h" |
| 5 | +#include "core/option.h" |
| 6 | +#include "dreamcast.h" |
| 7 | +#include "hw/aica/aica.h" |
| 8 | +#include "hw/rom/flash.h" |
| 9 | +#include "render/imgui.h" |
| 10 | + |
| 11 | +DEFINE_OPTION_STRING(region, "america", "System region"); |
| 12 | +DEFINE_OPTION_STRING(language, "english", "System language"); |
| 13 | +DEFINE_OPTION_STRING(broadcast, "ntsc", "System broadcast mode"); |
| 14 | + |
| 15 | +static const char *regions[] = { |
| 16 | + "japan", "america", "europe", |
| 17 | +}; |
| 18 | + |
| 19 | +static const char *languages[] = { |
| 20 | + "japanese", "english", "german", "french", "spanish", "italian", |
| 21 | +}; |
| 22 | + |
| 23 | +static const char *broadcasts[] = { |
| 24 | + "ntsc", "pal", "pal_m", "pal_n", |
| 25 | +}; |
| 26 | + |
| 27 | +struct bios { |
| 28 | + struct dreamcast *dc; |
| 29 | +}; |
| 30 | + |
| 31 | +static uint32_t bios_local_time() { |
| 32 | + /* dreamcast system time is relative to 1/1/1950 00:00 */ |
| 33 | + struct tm timeinfo; |
| 34 | + timeinfo.tm_year = 50; |
| 35 | + timeinfo.tm_mon = 0; |
| 36 | + timeinfo.tm_mday = 1; |
| 37 | + timeinfo.tm_hour = 0; |
| 38 | + timeinfo.tm_min = 0; |
| 39 | + timeinfo.tm_sec = 0; |
| 40 | + |
| 41 | + time_t base_time = mktime(&timeinfo); |
| 42 | + time_t curr_time = time(NULL); |
| 43 | + double delta = difftime(curr_time, base_time); |
| 44 | + return (uint32_t)delta; |
| 45 | +} |
| 46 | + |
| 47 | +static void bios_override_flash_settings(struct bios *bios) { |
| 48 | + struct dreamcast *dc = bios->dc; |
| 49 | + struct flash *flash = dc->flash; |
| 50 | + |
| 51 | + int region = 0; |
| 52 | + int lang = 0; |
| 53 | + int bcast = 0; |
| 54 | + uint32_t time = bios_local_time(); |
| 55 | + |
| 56 | + for (int i = 0; i < array_size(regions); i++) { |
| 57 | + if (!strcmp(OPTION_region, regions[i])) { |
| 58 | + region = i; |
| 59 | + break; |
| 60 | + } |
| 61 | + } |
| 62 | + |
| 63 | + for (int i = 0; i < array_size(languages); i++) { |
| 64 | + if (!strcmp(OPTION_language, languages[i])) { |
| 65 | + lang = i; |
| 66 | + break; |
| 67 | + } |
| 68 | + } |
| 69 | + |
| 70 | + for (int i = 0; i < array_size(broadcasts); i++) { |
| 71 | + if (!strcmp(OPTION_broadcast, broadcasts[i])) { |
| 72 | + bcast = i; |
| 73 | + break; |
| 74 | + } |
| 75 | + } |
| 76 | + |
| 77 | + /* the region, language and broadcast settings exist in two locations: |
| 78 | +
|
| 79 | + 1. 0x8c000070-74. this data seems to be the "factory settings" and is read |
| 80 | + from 0x1a000 of the flash rom on init. this data is read-only |
| 81 | +
|
| 82 | + 2. 0x8c000078-7f. this data seems to be the "user settings" and is copied |
| 83 | + from partition 2, logical block 5 of the flash rom on init |
| 84 | +
|
| 85 | + in order to force these settings, write to all of the locations in flash |
| 86 | + memory that they are ever read from */ |
| 87 | + |
| 88 | + /* overwrite factory flash settings */ |
| 89 | + char sysinfo[16]; |
| 90 | + memcpy(sysinfo, "00000Dreamcast ", sizeof(sysinfo)); |
| 91 | + sysinfo[2] = '0' + region; |
| 92 | + sysinfo[3] = '0' + lang; |
| 93 | + sysinfo[4] = '0' + bcast; |
| 94 | + |
| 95 | + flash_write(flash, 0x1a000, sysinfo, sizeof(sysinfo)); |
| 96 | + flash_write(flash, 0x1a0a0, sysinfo, sizeof(sysinfo)); |
| 97 | + |
| 98 | + /* overwrite user flash settings */ |
| 99 | + struct flash_syscfg_block syscfg; |
| 100 | + int res = flash_read_block(flash, FLASH_PT_USER, FLASH_USER_SYSCFG, &syscfg); |
| 101 | + CHECK_EQ(res, 1); |
| 102 | + |
| 103 | + syscfg.time_lo = time & 0xffff; |
| 104 | + syscfg.time_hi = (time & 0xffff0000) >> 16; |
| 105 | + syscfg.lang = lang; |
| 106 | + |
| 107 | + res = flash_write_block(flash, FLASH_PT_USER, FLASH_USER_SYSCFG, &syscfg); |
| 108 | + CHECK_EQ(res, 1); |
| 109 | + |
| 110 | + /* overwrite aica clock to match the bios */ |
| 111 | + aica_set_clock(dc->aica, time); |
| 112 | +} |
| 113 | + |
| 114 | +static void bios_validate_flash(struct bios *bios) { |
| 115 | + struct dreamcast *dc = bios->dc; |
| 116 | + struct flash *flash = dc->flash; |
| 117 | + struct flash_header_block header; |
| 118 | + |
| 119 | + /* validate partition 0 (factory settings) */ |
| 120 | + char sysinfo[2][16]; |
| 121 | + flash_read(flash, 0x1a000, sysinfo[0], sizeof(sysinfo[0])); |
| 122 | + flash_read(flash, 0x1a0a0, sysinfo[1], sizeof(sysinfo[1])); |
| 123 | + |
| 124 | + /* write out default sysinfo if missing */ |
| 125 | + if (memcmp(&sysinfo[0][5], "Dreamcast ", 11) != 0 || |
| 126 | + memcmp(&sysinfo[1][5], "Dreamcast ", 11) != 0) { |
| 127 | + memcpy(sysinfo[0], "00000Dreamcast ", sizeof(sysinfo[0])); |
| 128 | + flash_write(flash, 0x1a000, sysinfo[0], sizeof(sysinfo[0])); |
| 129 | + flash_write(flash, 0x1a0a0, sysinfo[0], sizeof(sysinfo[0])); |
| 130 | + } |
| 131 | + |
| 132 | + /* validate partition 1 (reserved) */ |
| 133 | + flash_erase_partition(flash, FLASH_PT_RESERVED); |
| 134 | + |
| 135 | + /* validate partition 2 (user settings, block allocated) */ |
| 136 | + if (!flash_read_block(flash, FLASH_PT_USER, 0, &header)) { |
| 137 | + flash_erase_partition(flash, FLASH_PT_USER); |
| 138 | + |
| 139 | + /* write out default user settings */ |
| 140 | + struct flash_syscfg_block syscfg; |
| 141 | + memset(&syscfg, 0xff, sizeof(syscfg)); |
| 142 | + syscfg.time_lo = 0; |
| 143 | + syscfg.time_hi = 0; |
| 144 | + syscfg.lang = 0; |
| 145 | + syscfg.mono = 0; |
| 146 | + syscfg.autostart = 1; |
| 147 | + |
| 148 | + int res = |
| 149 | + flash_write_block(flash, FLASH_PT_USER, FLASH_USER_SYSCFG, &syscfg); |
| 150 | + CHECK_EQ(res, 1); |
| 151 | + } |
| 152 | + |
| 153 | + /* validate partition 3 (game settings, block allocated) */ |
| 154 | + if (!flash_read_block(flash, FLASH_PT_GAME, 0, &header)) { |
| 155 | + flash_erase_partition(flash, FLASH_PT_GAME); |
| 156 | + } |
| 157 | + |
| 158 | + /* validate partition 4 (unknown, block allocated) */ |
| 159 | + if (!flash_read_block(flash, FLASH_PT_UNKNOWN, 0, &header)) { |
| 160 | + flash_erase_partition(flash, FLASH_PT_UNKNOWN); |
| 161 | + } |
| 162 | +} |
| 163 | + |
| 164 | +void bios_debug_menu(struct bios *bios) { |
| 165 | + int changed = 0; |
| 166 | + |
| 167 | + if (igBeginMainMenuBar()) { |
| 168 | + if (igBeginMenu("BIOS", 1)) { |
| 169 | + if (igBeginMenu("region", 1)) { |
| 170 | + for (int i = 0; i < array_size(regions); i++) { |
| 171 | + const char *region = regions[i]; |
| 172 | + int selected = !strcmp(OPTION_region, region); |
| 173 | + |
| 174 | + if (igMenuItem(region, NULL, selected, 1)) { |
| 175 | + changed = 1; |
| 176 | + strncpy(OPTION_region, region, sizeof(OPTION_region)); |
| 177 | + } |
| 178 | + } |
| 179 | + igEndMenu(); |
| 180 | + } |
| 181 | + |
| 182 | + if (igBeginMenu("language", 1)) { |
| 183 | + for (int i = 0; i < array_size(languages); i++) { |
| 184 | + const char *language = languages[i]; |
| 185 | + int selected = !strcmp(OPTION_language, language); |
| 186 | + |
| 187 | + if (igMenuItem(language, NULL, selected, 1)) { |
| 188 | + changed = 1; |
| 189 | + strncpy(OPTION_language, language, sizeof(OPTION_language)); |
| 190 | + } |
| 191 | + } |
| 192 | + igEndMenu(); |
| 193 | + } |
| 194 | + |
| 195 | + if (igBeginMenu("broadcast", 1)) { |
| 196 | + for (int i = 0; i < array_size(broadcasts); i++) { |
| 197 | + const char *broadcast = broadcasts[i]; |
| 198 | + int selected = !strcmp(OPTION_broadcast, broadcast); |
| 199 | + |
| 200 | + if (igMenuItem(broadcast, NULL, selected, 1)) { |
| 201 | + changed = 1; |
| 202 | + strncpy(OPTION_broadcast, broadcast, sizeof(OPTION_broadcast)); |
| 203 | + } |
| 204 | + } |
| 205 | + igEndMenu(); |
| 206 | + } |
| 207 | + |
| 208 | + igEndMenu(); |
| 209 | + } |
| 210 | + igEndMainMenuBar(); |
| 211 | + } |
| 212 | + |
| 213 | + if (changed) { |
| 214 | + LOG_WARNING("bios settings changed, restart for changes to take effect"); |
| 215 | + } |
| 216 | +} |
| 217 | + |
| 218 | +int bios_init(struct bios *bios) { |
| 219 | + bios_validate_flash(bios); |
| 220 | + |
| 221 | + bios_override_flash_settings(bios); |
| 222 | + |
| 223 | + return 1; |
| 224 | +} |
| 225 | + |
| 226 | +void bios_destroy(struct bios *bios) { |
| 227 | + free(bios); |
| 228 | +} |
| 229 | + |
| 230 | +struct bios *bios_create(struct dreamcast *dc) { |
| 231 | + struct bios *bios = calloc(1, sizeof(struct bios)); |
| 232 | + |
| 233 | + bios->dc = dc; |
| 234 | + |
| 235 | + return bios; |
| 236 | +} |
0 commit comments