// SPDX-License-Identifier: MIT #include "platforms/platforms.h" #include #include #include #include "rominfo.h" #define M68K_CPU_TYPES M68K_CPU_TYPE_SCC68070 const char *cpu_types[M68K_CPU_TYPES] = { "68000", "68010", "68EC020", "68020", "68EC030", "68030", "68EC040", "68LC040", "68040", "SCC68070", }; const char *map_type_names[MAPTYPE_NUM] = { "NONE", "rom", "ram", "register", "ram_noalloc", "wtcram", }; const char *config_item_names[CONFITEM_NUM] = { "NONE", "cpu", "map", "loopcycles", "mouse", "keyboard", "platform", "setvar", "kbfile", }; const char *mapcmd_names[MAPCMD_NUM] = { "NONE", "type", "address", "size", "range", "file", "ovl", "id", "autodump_file", "autodump_mem", }; int get_config_item_type(char *cmd) { for (int i = 0; i < CONFITEM_NUM; i++) { if (strcmp(cmd, config_item_names[i]) == 0) { return i; } } return CONFITEM_NONE; } unsigned int get_m68k_cpu_type(char *name) { for (int i = 0; i < M68K_CPU_TYPES; i++) { if (strcmp(name, cpu_types[i]) == 0) { printf("[CFG] Set CPU type to %s.\n", cpu_types[i]); return i + 1; } } printf("[CFG] Invalid CPU type %s specified, defaulting to 68000.\n", name); return M68K_CPU_TYPE_68000; } unsigned int get_map_cmd(char *name) { for (int i = 1; i < MAPCMD_NUM; i++) { if (strcmp(name, mapcmd_names[i]) == 0) { return i; } } return MAPCMD_UNKNOWN; } unsigned int get_map_type(char *name) { for (int i = 1; i < MAPTYPE_NUM; i++) { if (strcmp(name, map_type_names[i]) == 0) { return i; } } return MAPTYPE_NONE; } void trim_whitespace(char *str) { while (strlen(str) != 0 && (str[strlen(str) - 1] == ' ' || str[strlen(str) - 1] == '\t' || str[strlen(str) - 1] == 0x0A || str[strlen(str) - 1] == 0x0D)) { str[strlen(str) - 1] = '\0'; } } unsigned int get_int(char *str) { if (strlen(str) == 0) return -1; int ret_int = 0; if (strlen(str) > 2 && str[0] == '0' && str[1] == 'x') { for (int i = 2; i < (int)strlen(str); i++) { if (str[i] >= '0' && str[i] <= '9') { ret_int = (str[i] - '0') | (ret_int << 4); } else { switch(str[i]) { case 'A': ret_int = 0xA | (ret_int << 4); break; case 'B': ret_int = 0xB | (ret_int << 4); break; case 'C': ret_int = 0xC | (ret_int << 4); break; case 'D': ret_int = 0xD | (ret_int << 4); break; case 'E': ret_int = 0xE | (ret_int << 4); break; case 'F': ret_int = 0xF | (ret_int << 4); break; case 'K': ret_int = ret_int * SIZE_KILO; break; case 'M': ret_int = ret_int * SIZE_MEGA; break; case 'G': ret_int = ret_int * SIZE_GIGA; break; default: printf("[CFG] Unknown character %c in hex value.\n", str[i]); break; } } } return ret_int; } else { ret_int = atoi(str); if (str[strlen(str) - 1] == 'K') ret_int = ret_int * SIZE_KILO; else if (str[strlen(str) - 1] == 'M') ret_int = ret_int * SIZE_MEGA; else if (str[strlen(str) - 1] == 'G') ret_int = ret_int * SIZE_GIGA; return ret_int; } } void get_next_string(char *str, char *str_out, int *strpos, char separator) { int str_pos = 0, out_pos = 0, startquote = 0, endstring = 0; if (!str_out) return; if (strpos) str_pos = *strpos; while ((str[str_pos] == ' ' || str[str_pos] == '\t') && str_pos < (int)strlen(str)) { str_pos++; } if (str[str_pos] == '\"') { str_pos++; startquote = 1; } for (int i = str_pos; i < (int)strlen(str); i++) { str_out[out_pos] = str[i]; if (startquote) { if (str[i] == '\"') endstring = 1; } else { if ((separator == ' ' && (str[i] == ' ' || str[i] == '\t')) || str[i] == separator) { endstring = 1; } } if (endstring) { str_out[out_pos] = '\0'; if (strpos) { *strpos = i + 1; } break; } out_pos++; if (i + 1 == (int)strlen(str) && strpos) { *strpos = i + 1; str_out[out_pos] = '\0'; } } } void add_mapping(struct emulator_config *cfg, unsigned int type, unsigned int addr, unsigned int size, int mirr_addr, char *filename, char *map_id, unsigned int autodump) { unsigned int index = 0, file_size = 0; FILE *in = NULL; while (index < MAX_NUM_MAPPED_ITEMS) { if (cfg->map_type[index] == MAPTYPE_NONE) break; index++; } if (index == MAX_NUM_MAPPED_ITEMS) { printf("[CFG] Unable to map item, only %d items can be mapped with current binary.\n", MAX_NUM_MAPPED_ITEMS); return; } cfg->map_type[index] = type; cfg->map_offset[index] = addr; cfg->map_size[index] = size; cfg->map_high[index] = addr + size; cfg->map_mirror[index] = mirr_addr; if (strlen(map_id)) { if (cfg->map_id[index]) { free(cfg->map_id[index]); } cfg->map_id[index] = (char *)malloc(strlen(map_id) + 1); strcpy(cfg->map_id[index], map_id); } switch(type) { case MAPTYPE_RAM_NOALLOC: printf("[CFG] Adding %d byte (%d MB) RAM mapping %s...\n", size, size / 1024 / 1024, map_id); cfg->map_data[index] = (unsigned char *)filename; break; case MAPTYPE_RAM_WTC: printf("[CFG] Allocating %d bytes for Write-Through Cached RAM mapping (%.1f MB)...\n", size, (float)size / 1024.0f / 1024.0f); goto alloc_mapram; break; case MAPTYPE_RAM: printf("[CFG] Allocating %d bytes for RAM mapping (%d MB)...\n", size, size / 1024 / 1024); alloc_mapram: cfg->map_data[index] = (unsigned char *)malloc(size); if (!cfg->map_data[index]) { printf("[CFG] ERROR: Unable to allocate memory for mapped RAM!\n"); goto mapping_failed; } memset(cfg->map_data[index], 0x00, size); if (type == MAPTYPE_RAM_WTC) { // This may look a bit weird, but it adds a read range for the WTC RAM. Writes still go through to the mapped read/write functions. m68k_add_rom_range(cfg->map_offset[index], cfg->map_high[index], cfg->map_data[index]); } break; case MAPTYPE_ROM: in = fopen(filename, "rb"); if (!in) { if (!autodump) { printf("[CFG] Failed to open file %s for ROM mapping. Using onboard ROM instead, if available.\n", filename); goto mapping_failed; } else if (autodump == MAPCMD_AUTODUMP_FILE) { printf("[CFG] Could not open file %s for ROM mapping. Autodump flag is set, dumping to file.\n", filename); dump_range_to_file(cfg->map_offset[index], cfg->map_size[index], filename); in = fopen(filename, "rb"); if (in == NULL) { printf("[CFG] Could not open dumped file for reading. Using onboard ROM instead, if available.\n"); goto mapping_failed; } } else if (autodump == MAPCMD_AUTODUMP_MEM) { printf("[CFG] Could not open file %s for ROM mapping. Autodump flag is set, dumping to memory.\n", filename); cfg->map_data[index] = dump_range_to_memory(cfg->map_offset[index], cfg->map_size[index]); cfg->rom_size[index] = cfg->map_size[index]; if (cfg->map_data[index] == NULL) { printf("[CFG] Could not dump range to memory. Using onboard ROM instead, if available.\n"); goto mapping_failed; } goto skip_file_ops; } } fseek(in, 0, SEEK_END); file_size = (int)ftell(in); if (size == 0) { cfg->map_size[index] = file_size; cfg->map_high[index] = addr + cfg->map_size[index]; } fseek(in, 0, SEEK_SET); cfg->map_data[index] = (unsigned char *)calloc(1, cfg->map_size[index]); cfg->rom_size[index] = (cfg->map_size[index] <= file_size) ? cfg->map_size[index] : file_size; if (!cfg->map_data[index]) { printf("[CFG] ERROR: Unable to allocate memory for mapped ROM!\n"); goto mapping_failed; } memset(cfg->map_data[index], 0x00, cfg->map_size[index]); fread(cfg->map_data[index], cfg->rom_size[index], 1, in); if (in) fclose(in); skip_file_ops: displayRomInfo(cfg->map_data[index], cfg->rom_size[index]); if (cfg->map_size[index] == cfg->rom_size[index]) m68k_add_rom_range(cfg->map_offset[index], cfg->map_high[index], cfg->map_data[index]); break; case MAPTYPE_REGISTER: default: break; } printf("[CFG] [MAP %d] Added %s mapping for range %.8lX-%.8lX ID: %s\n", index, map_type_names[type], cfg->map_offset[index], cfg->map_high[index] - 1, cfg->map_id[index] ? cfg->map_id[index] : "None"); return; mapping_failed:; cfg->map_type[index] = MAPTYPE_NONE; if (in) fclose(in); } void free_config_file(struct emulator_config *cfg) { if (!cfg) { printf("[CFG] Tried to free NULL config, aborting.\n"); } if (cfg->platform) { cfg->platform->shutdown(cfg); free(cfg->platform); cfg->platform = NULL; } for (int i = 0; i < MAX_NUM_MAPPED_ITEMS; i++) { if (cfg->map_data[i]) { if (cfg->map_type[i] != MAPTYPE_RAM_NOALLOC) { free(cfg->map_data[i]); } cfg->map_data[i] = NULL; } if (cfg->map_id[i]) { free(cfg->map_id[i]); cfg->map_id[i] = NULL; } } if (cfg->mouse_file) { free(cfg->mouse_file); cfg->mouse_file = NULL; } if (cfg->keyboard_file) { free(cfg->keyboard_file); cfg->keyboard_file = NULL; } m68k_clear_ranges(); printf("[CFG] Config file freed. Maybe.\n"); } struct emulator_config *load_config_file(char *filename) { FILE *in = fopen(filename, "rb"); if (in == NULL) { printf("[CFG] Failed to open config file %s for reading.\n", filename); return NULL; } char *parse_line = NULL; char cur_cmd[128]; struct emulator_config *cfg = NULL; int cur_line = 1; parse_line = (char *)calloc(1, 512); if (!parse_line) { printf("[CFG] Failed to allocate memory for config file line buffer.\n"); return NULL; } cfg = (struct emulator_config *)calloc(1, sizeof(struct emulator_config)); if (!cfg) { printf("[CFG] Failed to allocate memory for temporary emulator config.\n"); goto load_failed; } memset(cfg, 0x00, sizeof(struct emulator_config)); cfg->cpu_type = M68K_CPU_TYPE_68000; while (!feof(in)) { int str_pos = 0; memset(parse_line, 0x00, 512); fgets(parse_line, 512, in); if (strlen(parse_line) <= 2 || parse_line[0] == '#' || parse_line[0] == '/') goto skip_line; trim_whitespace(parse_line); get_next_string(parse_line, cur_cmd, &str_pos, ' '); switch (get_config_item_type(cur_cmd)) { case CONFITEM_CPUTYPE: cfg->cpu_type = get_m68k_cpu_type(parse_line + str_pos); break; case CONFITEM_MAP: { unsigned int maptype = 0, mapsize = 0, mapaddr = 0, autodump = 0; unsigned int mirraddr = ((unsigned int)-1); char mapfile[128], mapid[128]; memset(mapfile, 0x00, 128); memset(mapid, 0x00, 128); while (str_pos < (int)strlen(parse_line)) { get_next_string(parse_line, cur_cmd, &str_pos, '='); switch(get_map_cmd(cur_cmd)) { case MAPCMD_TYPE: get_next_string(parse_line, cur_cmd, &str_pos, ' '); maptype = get_map_type(cur_cmd); //printf("Type! %s\n", map_type_names[maptype]); break; case MAPCMD_ADDRESS: get_next_string(parse_line, cur_cmd, &str_pos, ' '); mapaddr = get_int(cur_cmd); //printf("Address! %.8X\n", mapaddr); break; case MAPCMD_SIZE: get_next_string(parse_line, cur_cmd, &str_pos, ' '); mapsize = get_int(cur_cmd); //printf("Size! %.8X\n", mapsize); break; case MAPCMD_RANGE: get_next_string(parse_line, cur_cmd, &str_pos, '-'); mapaddr = get_int(cur_cmd); get_next_string(parse_line, cur_cmd, &str_pos, ' '); mapsize = get_int(cur_cmd) - 1 - mapaddr; //printf("Range! %d-%d\n", mapaddr, mapaddr + mapsize); break; case MAPCMD_FILENAME: get_next_string(parse_line, cur_cmd, &str_pos, ' '); strcpy(mapfile, cur_cmd); //printf("File! %s\n", mapfile); break; case MAPCMD_MAP_ID: get_next_string(parse_line, cur_cmd, &str_pos, ' '); strcpy(mapid, cur_cmd); //printf("File! %s\n", mapfile); break; case MAPCMD_OVL_REMAP: get_next_string(parse_line, cur_cmd, &str_pos, ' '); mirraddr = get_int(cur_cmd); break; case MAPCMD_AUTODUMP_FILE: case MAPCMD_AUTODUMP_MEM: autodump = get_map_cmd(cur_cmd); break; default: printf("[CFG] Unknown/unhandled map argument %s on line %d.\n", cur_cmd, cur_line); break; } } add_mapping(cfg, maptype, mapaddr, mapsize, mirraddr, mapfile, mapid, autodump); break; } case CONFITEM_LOOPCYCLES: cfg->loop_cycles = get_int(parse_line + str_pos); printf("[CFG] Set CPU loop cycles to %d.\n", cfg->loop_cycles); break; case CONFITEM_MOUSE: get_next_string(parse_line, cur_cmd, &str_pos, ' '); cfg->mouse_file = (char *)calloc(1, strlen(cur_cmd) + 1); strcpy(cfg->mouse_file, cur_cmd); get_next_string(parse_line, cur_cmd, &str_pos, ' '); cfg->mouse_toggle_key = cur_cmd[0]; get_next_string(parse_line, cur_cmd, &str_pos, ' '); cfg->mouse_autoconnect = (strcmp(cur_cmd, "autoconnect") == 0) ? 1 : 0; cfg->mouse_enabled = 1; printf("[CFG] Enabled mouse event forwarding from file %s, toggle key %c.\n", cfg->mouse_file, cfg->mouse_toggle_key); break; case CONFITEM_KEYBOARD: get_next_string(parse_line, cur_cmd, &str_pos, ' '); cfg->keyboard_toggle_key = cur_cmd[0]; get_next_string(parse_line, cur_cmd, &str_pos, ' '); cfg->keyboard_grab = (strcmp(cur_cmd, "grab") == 0) ? 1 : 0; get_next_string(parse_line, cur_cmd, &str_pos, ' '); cfg->keyboard_autoconnect = (strcmp(cur_cmd, "autoconnect") == 0) ? 1 : 0; printf("[CFG] Enabled keyboard event forwarding, toggle key %c", cfg->keyboard_toggle_key); if (cfg->keyboard_grab) printf(", locking from host when connected"); if (cfg->keyboard_autoconnect) printf(", connected to guest at startup"); printf(".\n"); break; case CONFITEM_KBFILE: get_next_string(parse_line, cur_cmd, &str_pos, ' '); cfg->keyboard_file = (char *)calloc(1, strlen(cur_cmd) + 1); strcpy(cfg->keyboard_file, cur_cmd); printf("[CFG] Set keyboard event source file to %s.\n", cfg->keyboard_file); break; case CONFITEM_PLATFORM: { char platform_name[128], platform_sub[128]; memset(platform_name, 0x00, 128); memset(platform_sub, 0x00, 128); get_next_string(parse_line, platform_name, &str_pos, ' '); printf("[CFG] Setting platform to %s", platform_name); get_next_string(parse_line, platform_sub, &str_pos, ' '); if (strlen(platform_sub)) printf(" (sub: %s)", platform_sub); printf("\n"); cfg->platform = make_platform_config(platform_name, platform_sub); break; } case CONFITEM_SETVAR: { if (!cfg->platform) { printf("[CFG] Warning: setvar used in config file with no platform specified.\n"); break; } char var_name[128], var_value[128]; memset(var_name, 0x00, 128); memset(var_value, 0x00, 128); get_next_string(parse_line, var_name, &str_pos, ' '); get_next_string(parse_line, var_value, &str_pos, ' '); cfg->platform->setvar(cfg, var_name, var_value); break; } case CONFITEM_NONE: default: printf("[CFG] Unknown config item %s on line %d.\n", cur_cmd, cur_line); break; } skip_line: cur_line++; } goto load_successful; load_failed:; if (cfg) { for (int i = 0; i < MAX_NUM_MAPPED_ITEMS; i++) { if (cfg->map_data[i]) free(cfg->map_data[i]); cfg->map_data[i] = NULL; } free(cfg); cfg = NULL; } load_successful:; if (parse_line) free(parse_line); return cfg; } int get_named_mapped_item(struct emulator_config *cfg, char *name) { if (strlen(name) == 0) return -1; for (int i = 0; i < MAX_NUM_MAPPED_ITEMS; i++) { if (cfg->map_type[i] == MAPTYPE_NONE || !cfg->map_id[i]) continue; if (strcmp(name, cfg->map_id[i]) == 0) return i; } return -1; } int get_mapped_item_by_address(struct emulator_config *cfg, uint32_t address) { for (int i = 0; i < MAX_NUM_MAPPED_ITEMS; i++) { if (cfg->map_type[i] == MAPTYPE_NONE || !cfg->map_data[i]) continue; else if (address >= cfg->map_offset[i] && address < cfg->map_high[i]) { if (cfg->map_type[i] == MAPTYPE_RAM || cfg->map_type[i] == MAPTYPE_RAM_NOALLOC || cfg->map_type[i] == MAPTYPE_ROM) return i; } } return -1; } uint8_t *get_mapped_data_pointer_by_address(struct emulator_config *cfg, uint32_t address) { for (int i = 0; i < MAX_NUM_MAPPED_ITEMS; i++) { if (cfg->map_type[i] == MAPTYPE_NONE || !cfg->map_data[i]) continue; else if (address >= cfg->map_offset[i] && address < cfg->map_high[i]) { if (cfg->map_type[i] == MAPTYPE_RAM || cfg->map_type[i] == MAPTYPE_RAM_NOALLOC || cfg->map_type[i] == MAPTYPE_ROM) return cfg->map_data[i] + (address - cfg->map_offset[i]); } } return NULL; }