summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--blastem.c409
-rw-r--r--blastem.h13
-rw-r--r--config.c60
-rw-r--r--config.h1
-rw-r--r--io.c115
-rw-r--r--render_sdl.c13
-rw-r--r--rom.db168
-rw-r--r--romdb.c780
-rw-r--r--romdb.h57
-rw-r--r--tern.c93
-rw-r--r--tern.h8
-rw-r--r--util.c11
-rw-r--r--util.h2
-rw-r--r--vdp.c9
15 files changed, 1308 insertions, 433 deletions
diff --git a/Makefile b/Makefile
index ceba9d3..3ec9807 100644
--- a/Makefile
+++ b/Makefile
@@ -99,7 +99,7 @@ Z80OBJS=z80inst.o z80_to_x86.o
AUDIOOBJS=ym2612.o psg.o wave.o
CONFIGOBJS=config.o tern.o util.o
-MAINOBJS=blastem.o debug.o gdb_remote.o vdp.o render_sdl.o io.o $(CONFIGOBJS) gst.o $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS)
+MAINOBJS=blastem.o debug.o gdb_remote.o vdp.o render_sdl.o io.o romdb.o $(CONFIGOBJS) gst.o $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS)
ifeq ($(CPU),x86_64)
CFLAGS+=-DX86_64 -m64
diff --git a/blastem.c b/blastem.c
index fcbcd52..51f188e 100644
--- a/blastem.c
+++ b/blastem.c
@@ -13,6 +13,7 @@
#include "gdb_remote.h"
#include "gst.h"
#include "util.h"
+#include "romdb.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -61,14 +62,23 @@ int load_smd_rom(long filesize, FILE * f)
fseek(f, SMD_HEADER_SIZE, SEEK_SET);
uint16_t * dst = cart;
+ int rom_size = filesize;
while (filesize > 0) {
fread(block, 1, SMD_BLOCK_SIZE, f);
for (uint8_t *low = block, *high = (block+SMD_BLOCK_SIZE/2), *end = block+SMD_BLOCK_SIZE; high < end; high++, low++) {
- *(dst++) = *high << 8 | *low;
+ *(dst++) = *low << 8 | *high;
}
filesize -= SMD_BLOCK_SIZE;
}
- return 1;
+ return filesize;
+}
+
+void byteswap_rom()
+{
+ for(unsigned short * cur = cart; cur - cart < CARTRIDGE_WORDS; ++cur)
+ {
+ *cur = (*cur >> 8) | (*cur << 8);
+ }
}
int load_rom(char * filename)
@@ -103,12 +113,7 @@ int load_rom(char * filename)
}
fread(cart, 2, filesize/2, f);
fclose(f);
- for(unsigned short * cur = cart; cur - cart < (filesize/2); ++cur)
- {
- *cur = (*cur >> 8) | (*cur << 8);
- }
- //TODO: Mirror ROM
- return 1;
+ return filesize;
}
uint16_t read_dma_value(uint32_t address)
@@ -762,127 +767,6 @@ void *z80_write_bank_reg(uint32_t location, void * vcontext, uint8_t value)
return context;
}
-uint16_t read_sram_w(uint32_t address, m68k_context * context)
-{
- genesis_context * gen = context->system;
- address &= gen->save_ram_mask;
- switch(gen->save_flags)
- {
- case RAM_FLAG_BOTH:
- return gen->save_ram[address] << 8 | gen->save_ram[address+1];
- case RAM_FLAG_EVEN:
- return gen->save_ram[address >> 1] << 8 | 0xFF;
- case RAM_FLAG_ODD:
- return gen->save_ram[address >> 1] | 0xFF00;
- }
- return 0xFFFF;//We should never get here
-}
-
-uint8_t read_sram_b(uint32_t address, m68k_context * context)
-{
- genesis_context * gen = context->system;
- address &= gen->save_ram_mask;
- switch(gen->save_flags)
- {
- case RAM_FLAG_BOTH:
- return gen->save_ram[address];
- case RAM_FLAG_EVEN:
- if (address & 1) {
- return 0xFF;
- } else {
- return gen->save_ram[address >> 1];
- }
- case RAM_FLAG_ODD:
- if (address & 1) {
- return gen->save_ram[address >> 1];
- } else {
- return 0xFF;
- }
- }
- return 0xFF;//We should never get here
-}
-
-m68k_context * write_sram_area_w(uint32_t address, m68k_context * context, uint16_t value)
-{
- genesis_context * gen = context->system;
- if ((gen->bank_regs[0] & 0x3) == 1) {
- address &= gen->save_ram_mask;
- switch(gen->save_flags)
- {
- case RAM_FLAG_BOTH:
- gen->save_ram[address] = value >> 8;
- gen->save_ram[address+1] = value;
- break;
- case RAM_FLAG_EVEN:
- gen->save_ram[address >> 1] = value >> 8;
- break;
- case RAM_FLAG_ODD:
- gen->save_ram[address >> 1] = value;
- break;
- }
- }
- return context;
-}
-
-m68k_context * write_sram_area_b(uint32_t address, m68k_context * context, uint8_t value)
-{
- genesis_context * gen = context->system;
- if ((gen->bank_regs[0] & 0x3) == 1) {
- address &= gen->save_ram_mask;
- switch(gen->save_flags)
- {
- case RAM_FLAG_BOTH:
- gen->save_ram[address] = value;
- break;
- case RAM_FLAG_EVEN:
- if (!(address & 1)) {
- gen->save_ram[address >> 1] = value;
- }
- break;
- case RAM_FLAG_ODD:
- if (address & 1) {
- gen->save_ram[address >> 1] = value;
- }
- break;
- }
- }
- return context;
-}
-
-m68k_context * write_bank_reg_w(uint32_t address, m68k_context * context, uint16_t value)
-{
- genesis_context * gen = context->system;
- address &= 0xE;
- address >>= 1;
- gen->bank_regs[address] = value;
- if (!address) {
- if (value & 1) {
- context->mem_pointers[2] = NULL;
- } else {
- context->mem_pointers[2] = cart + 0x200000/2;
- }
- }
- return context;
-}
-
-m68k_context * write_bank_reg_b(uint32_t address, m68k_context * context, uint8_t value)
-{
- if (address & 1) {
- genesis_context * gen = context->system;
- address &= 0xE;
- address >>= 1;
- gen->bank_regs[address] = value;
- if (!address) {
- if (value & 1) {
- context->mem_pointers[2] = NULL;
- } else {
- context->mem_pointers[2] = cart + 0x200000/2;
- }
- }
- }
- return context;
-}
-
void set_speed_percent(genesis_context * context, uint32_t percent)
{
uint32_t old_clock = context->master_clock;
@@ -894,17 +778,9 @@ void set_speed_percent(genesis_context * context, uint32_t percent)
psg_adjust_master_clock(context->psg, context->master_clock);
}
-#define ROM_END 0x1A4
-#define RAM_ID 0x1B0
-#define RAM_FLAGS 0x1B2
-#define RAM_START 0x1B4
-#define RAM_END 0x1B8
#define MAX_MAP_CHUNKS (4+7+1)
-#define RAM_FLAG_MASK 0x1800
-const memmap_chunk static_map[] = {
- {0, 0x400000, 0xFFFFFF, 0, MMAP_READ, cart,
- NULL, NULL, NULL, NULL},
+const memmap_chunk base_map[] = {
{0xE00000, 0x1000000, 0xFFFF, 0, MMAP_READ | MMAP_WRITE | MMAP_CODE, ram,
NULL, NULL, NULL, NULL},
{0xC00000, 0xE00000, 0x1FFFFF, 0, 0, NULL,
@@ -915,120 +791,49 @@ const memmap_chunk static_map[] = {
(read_8_fun)io_read, (write_8_fun)io_write}
};
-char * sram_filename;
+char * save_filename;
genesis_context * genesis;
-void save_sram()
+void persist_save()
{
- FILE * f = fopen(sram_filename, "wb");
+ FILE * f = fopen(save_filename, "wb");
if (!f) {
- fprintf(stderr, "Failed to open SRAM file %s for writing\n", sram_filename);
+ fprintf(stderr, "Failed to open %s file %s for writing\n", genesis->save_type == SAVE_I2C ? "EEPROM" : "SRAM", save_filename);
return;
}
- uint32_t size = genesis->save_ram_mask+1;
- if (genesis->save_flags != RAM_FLAG_BOTH) {
- size/= 2;
- }
- fwrite(genesis->save_ram, 1, size, f);
+ fwrite(genesis->save_storage, 1, genesis->save_size, f);
fclose(f);
- printf("Saved SRAM to %s\n", sram_filename);
+ printf("Saved %s to %s\n", genesis->save_type == SAVE_I2C ? "EEPROM" : "SRAM", save_filename);
}
-void init_run_cpu(genesis_context * gen, FILE * address_log, char * statefile, uint8_t * debugger)
+void init_run_cpu(genesis_context * gen, rom_info *rom, FILE * address_log, char * statefile, uint8_t * debugger)
{
m68k_options opts;
- memmap_chunk memmap[MAX_MAP_CHUNKS];
- uint32_t num_chunks;
- void * initial_mapped = NULL;
- gen->save_ram = NULL;
- //TODO: Handle carts larger than 4MB
- //TODO: Handle non-standard mappers
- uint32_t size;
- if ((cart[RAM_ID/2] & 0xFF) == 'A' && (cart[RAM_ID/2] >> 8) == 'R') {
- //Cart has save RAM
- uint32_t rom_end = ((cart[ROM_END/2] << 16) | cart[ROM_END/2+1]) + 1;
- uint32_t ram_start = (cart[RAM_START/2] << 16) | cart[RAM_START/2+1];
- uint32_t ram_end = (cart[RAM_END/2] << 16) | cart[RAM_END/2+1];
- uint16_t ram_flags = cart[RAM_FLAGS/2];
- gen->save_flags = ram_flags & RAM_FLAG_MASK;
- memset(memmap, 0, sizeof(memmap_chunk)*2);
- if (ram_start >= rom_end) {
- memmap[0].end = rom_end;
- memmap[0].mask = 0xFFFFFF;
- memmap[0].flags = MMAP_READ;
- memmap[0].buffer = cart;
-
- ram_start &= 0xFFFFFE;
- ram_end |= 1;
- memmap[1].start = ram_start;
- gen->save_ram_mask = memmap[1].mask = ram_end-ram_start;
- ram_end += 1;
- memmap[1].end = ram_end;
- memmap[1].flags = MMAP_READ | MMAP_WRITE;
- size = ram_end-ram_start;
- if ((ram_flags & RAM_FLAG_MASK) == RAM_FLAG_ODD) {
- memmap[1].flags |= MMAP_ONLY_ODD;
- size /= 2;
- } else if((ram_flags & RAM_FLAG_MASK) == RAM_FLAG_EVEN) {
- memmap[1].flags |= MMAP_ONLY_EVEN;
- size /= 2;
- }
- memmap[1].buffer = gen->save_ram = malloc(size);
-
- memcpy(memmap+2, static_map+1, sizeof(static_map)-sizeof(static_map[0]));
- num_chunks = sizeof(static_map)/sizeof(memmap_chunk)+1;
- } else {
- //Assume the standard Sega mapper for now
- memmap[0].end = 0x200000;
- memmap[0].mask = 0xFFFFFF;
- memmap[0].flags = MMAP_READ;
- memmap[0].buffer = cart;
-
- memmap[1].start = 0x200000;
- memmap[1].end = 0x400000;
- memmap[1].mask = 0x1FFFFF;
- ram_start &= 0xFFFFFE;
- ram_end |= 1;
- gen->save_ram_mask = ram_end-ram_start;
- memmap[1].flags = MMAP_READ | MMAP_PTR_IDX | MMAP_FUNC_NULL;
- memmap[1].ptr_index = 2;
- memmap[1].read_16 = (read_16_fun)read_sram_w;//these will only be called when mem_pointers[2] == NULL
- memmap[1].read_8 = (read_8_fun)read_sram_b;
- memmap[1].write_16 = (write_16_fun)write_sram_area_w;//these will be called all writes to the area
- memmap[1].write_8 = (write_8_fun)write_sram_area_b;
- memcpy(memmap+2, static_map+1, sizeof(static_map)-sizeof(static_map[0]));
- num_chunks = sizeof(static_map)/sizeof(memmap_chunk)+1;
- memset(memmap+num_chunks, 0, sizeof(memmap[num_chunks]));
- memmap[num_chunks].start = 0xA13000;
- memmap[num_chunks].end = 0xA13100;
- memmap[num_chunks].mask = 0xFF;
- memmap[num_chunks].write_16 = (write_16_fun)write_bank_reg_w;
- memmap[num_chunks].write_8 = (write_8_fun)write_bank_reg_b;
- num_chunks++;
- ram_end++;
- size = ram_end-ram_start;
- if ((ram_flags & RAM_FLAG_MASK) != RAM_FLAG_BOTH) {
- size /= 2;
- }
- gen->save_ram = malloc(size);
- memmap[1].buffer = initial_mapped = cart + 0x200000/2;
- }
- } else {
- memcpy(memmap, static_map, sizeof(static_map));
- num_chunks = sizeof(static_map)/sizeof(memmap_chunk);
- }
- if (gen->save_ram) {
- memset(gen->save_ram, 0, size);
- FILE * f = fopen(sram_filename, "rb");
+
+ gen->save_type = rom->save_type;
+ if (gen->save_type != SAVE_NONE) {
+ gen->save_ram_mask = rom->save_mask;
+ gen->save_size = rom->save_size;
+ gen->save_storage = rom->save_buffer;
+ gen->eeprom_map = rom->eeprom_map;
+ gen->num_eeprom = rom->num_eeprom;
+ memset(gen->save_storage, 0, rom->save_size);
+ FILE * f = fopen(save_filename, "rb");
if (f) {
- uint32_t read = fread(gen->save_ram, 1, size, f);
+ uint32_t read = fread(gen->save_storage, 1, rom->save_size, f);
fclose(f);
if (read > 0) {
- printf("Loaded SRAM from %s\n", sram_filename);
+ printf("Loaded %s from %s\n", rom->save_type == SAVE_I2C ? "EEPROM" : "SRAM", save_filename);
}
}
- atexit(save_sram);
+ atexit(persist_save);
+ if (gen->save_type == SAVE_I2C) {
+ eeprom_init(&gen->eeprom, gen->save_storage, gen->save_size);
+ }
+ } else {
+ gen->save_storage = NULL;
}
- init_m68k_opts(&opts, memmap, num_chunks, MCLKS_PER_68K);
+
+ init_m68k_opts(&opts, rom->map, rom->map_chunks, MCLKS_PER_68K);
opts.address_log = address_log;
m68k_context *context = init_68k_context(&opts);
gen->m68k = context;
@@ -1041,8 +846,8 @@ void init_run_cpu(genesis_context * gen, FILE * address_log, char * statefile, u
//work RAM
context->mem_pointers[1] = ram;
//save RAM/map
- context->mem_pointers[2] = initial_mapped;
- context->mem_pointers[3] = (uint16_t *)gen->save_ram;
+ context->mem_pointers[2] = rom->map[1].buffer;
+ context->mem_pointers[3] = (uint16_t *)gen->save_storage;
if (statefile) {
uint32_t pc = load_gst(gen, statefile);
@@ -1065,75 +870,36 @@ void init_run_cpu(genesis_context * gen, FILE * address_log, char * statefile, u
}
}
-char title[64];
+char *title;
-#define TITLE_START 0x150
-#define TITLE_END (TITLE_START+48)
-
-void update_title()
+void update_title(char *rom_name)
{
- uint16_t *last = cart + TITLE_END/2 - 1;
- while(last > cart + TITLE_START/2 && *last == 0x2020)
- {
- last--;
- }
- uint16_t *start = cart + TITLE_START/2;
- char *cur = title;
- char last_char = ' ';
- for (; start != last; start++)
- {
- if ((last_char != ' ' || (*start >> 8) != ' ') && (*start >> 8) < 0x80) {
- *(cur++) = *start >> 8;
- last_char = *start >> 8;
- }
- if (last_char != ' ' || (*start & 0xFF) != ' ' && (*start & 0xFF) < 0x80) {
- *(cur++) = *start;
- last_char = *start & 0xFF;
- }
- }
- *(cur++) = *start >> 8;
- if ((*start & 0xFF) != ' ') {
- *(cur++) = *start;
+ if (title) {
+ free(title);
+ title = NULL;
}
- strcpy(cur, " - BlastEm");
+ title = alloc_concat(rom_name, " - BlastEm");
}
-#define REGION_START 0x1F0
-
-int detect_specific_region(char region)
+void set_region(rom_info *info, uint8_t region)
{
- return (cart[REGION_START/2] & 0xFF) == region || (cart[REGION_START/2] >> 8) == region || (cart[REGION_START/2+1] & 0xFF) == region;
-}
-
-void detect_region()
-{
- if (detect_specific_region('U')|| detect_specific_region('B') || detect_specific_region('4')) {
- version_reg = NO_DISK | USA;
- } else if (detect_specific_region('J')) {
- version_reg = NO_DISK | JAP;
- } else if (detect_specific_region('E') || detect_specific_region('A')) {
- version_reg = NO_DISK | EUR;
- } else {
+ if (!region) {
char * def_region = tern_find_ptr(config, "default_region");
- if (def_region) {
- switch(*def_region)
- {
- case 'j':
- case 'J':
- version_reg = NO_DISK | JAP;
- break;
- case 'u':
- case 'U':
- version_reg = NO_DISK | USA;
- break;
- case 'e':
- case 'E':
- version_reg = NO_DISK | EUR;
- break;
- }
+ if (def_region && (!info->regions || (info->regions & translate_region_char(toupper(*def_region))))) {
+ region = translate_region_char(toupper(*def_region));
+ } else {
+ region = info->regions;
}
}
+ if (region & REGION_E) {
+ version_reg = NO_DISK | EUR;
+ } else if (region & REGION_J) {
+ version_reg = NO_DISK | JAP;
+ } else {
+ version_reg = NO_DISK | USA;
+ }
}
+
#ifndef NO_Z80
const memmap_chunk z80_map[] = {
{ 0x0000, 0x4000, 0x1FFF, 0, MMAP_READ | MMAP_WRITE | MMAP_CODE, z80_ram, NULL, NULL, NULL, NULL },
@@ -1161,6 +927,7 @@ int main(int argc, char ** argv)
char * romfname = NULL;
FILE *address_log = NULL;
char * statefile = NULL;
+ int rom_size;
uint8_t * debuggerfun = NULL;
uint8_t fullscreen = 0, use_gl = 1;
for (int i = 1; i < argc; i++) {
@@ -1204,21 +971,8 @@ int main(int argc, char ** argv)
fputs("-r must be followed by region (J, U or E)\n", stderr);
return 1;
}
- switch (argv[i][0])
- {
- case 'j':
- case 'J':
- force_version = NO_DISK | JAP;
- break;
- case 'u':
- case 'U':
- force_version = NO_DISK | USA;
- break;
- case 'e':
- case 'E':
- force_version = NO_DISK | EUR;
- break;
- default:
+ force_version = translate_region_char(toupper(argv[i][0]));
+ if (!force_version) {
fprintf(stderr, "'%c' is not a valid region character for the -r option\n", argv[i][0]);
return 1;
}
@@ -1255,7 +1009,7 @@ int main(int argc, char ** argv)
return 1;
}
} else if (!loaded) {
- if(!load_rom(argv[i])) {
+ if (!(rom_size = load_rom(argv[i]))) {
fprintf(stderr, "Failed to open %s for reading\n", argv[i]);
return 1;
}
@@ -1271,14 +1025,13 @@ int main(int argc, char ** argv)
fputs("You must specify a ROM filename!\n", stderr);
return 1;
}
- if (force_version) {
- version_reg = force_version;
- } else {
- detect_region();
- }
- update_title();
+ tern_node *rom_db = load_rom_db();
+ rom_info info = configure_rom(rom_db, cart, rom_size, base_map, sizeof(base_map)/sizeof(base_map[0]));
+ byteswap_rom();
+ set_region(&info, force_version);
+ update_title(info.name);
int def_width = 0;
- char *config_width = tern_find_ptr(config, "videowidth");
+ char *config_width = tern_find_path(config, "video\0width\0").ptrval;
if (config_width) {
def_width = atoi(config_width);
}
@@ -1301,7 +1054,7 @@ int main(int argc, char ** argv)
init_vdp_context(&v_context, version_reg & 0x40);
gen.frame_end = vdp_cycles_to_frame_end(&v_context);
- char * config_cycles = tern_find_ptr(config, "clocksmax_cycles");
+ char * config_cycles = tern_find_path(config, "clocks\0max_cycles\0").ptrval;
gen.max_cycles = config_cycles ? atoi(config_cycles) : 10000000;
ym2612_context y_context;
@@ -1330,20 +1083,22 @@ int main(int argc, char ** argv)
setup_io_devices(config, gen.ports);
int fname_size = strlen(romfname);
- sram_filename = malloc(fname_size+6);
- memcpy(sram_filename, romfname, fname_size);
+ char * ext = info.save_type == SAVE_I2C ? "eeprom" : "sram";
+ save_filename = malloc(fname_size+strlen(ext) + 2);
+ memcpy(save_filename, romfname, fname_size);
int i;
for (i = fname_size-1; fname_size >= 0; --i) {
- if (sram_filename[i] == '.') {
- strcpy(sram_filename + i + 1, "sram");
+ if (save_filename[i] == '.') {
+ strcpy(save_filename + i + 1, ext);
break;
}
}
if (i < 0) {
- strcpy(sram_filename + fname_size, ".sram");
+ save_filename[fname_size] = '.';
+ strcpy(save_filename + fname_size + 1, ext);
}
set_keybindings(gen.ports);
- init_run_cpu(&gen, address_log, statefile, debuggerfun);
+ init_run_cpu(&gen, &info, address_log, statefile, debuggerfun);
return 0;
}
diff --git a/blastem.h b/blastem.h
index 23c62dd..7d051b6 100644
--- a/blastem.h
+++ b/blastem.h
@@ -14,10 +14,7 @@
#include "psg.h"
#include "io.h"
#include "config.h"
-
-#define RAM_FLAG_ODD 0x1800
-#define RAM_FLAG_EVEN 0x1000
-#define RAM_FLAG_BOTH 0x0000
+#include "romdb.h"
typedef struct {
m68k_context *m68k;
@@ -25,16 +22,20 @@ typedef struct {
vdp_context *vdp;
ym2612_context *ym;
psg_context *psg;
- uint8_t *save_ram;
+ uint8_t *save_storage;
+ eeprom_map *eeprom_map;
+ uint32_t num_eeprom;
+ uint32_t save_size;
uint32_t save_ram_mask;
- uint32_t save_flags;
uint32_t master_clock; //Current master clock value
uint32_t normal_clock; //Normal master clock (used to restore master clock after turbo mode)
uint32_t frame_end;
uint32_t max_cycles;
uint8_t bank_regs[8];
+ uint8_t save_type;
io_port ports[3];
uint8_t bus_busy;
+ eeprom_state eeprom;
} genesis_context;
extern genesis_context * genesis;
diff --git a/config.c b/config.c
index eab0308..6fd29b5 100644
--- a/config.c
+++ b/config.c
@@ -9,8 +9,6 @@
#include <stdlib.h>
#include <string.h>
-#define MAX_NEST 30 //way more than I'll ever need
-
#ifdef _WIN32
char * strtok_r(char * input, char * sep, char ** state)
{
@@ -32,69 +30,59 @@ char * strtok_r(char * input, char * sep, char ** state)
}
#endif
-tern_node * parse_config(char * config_data)
+tern_node * parse_config_int(char **state, int started, int *line)
{
- char *state, *curline;
- char *prefix = NULL;
- int nest_level = 0;
- char * prefix_parts[MAX_NEST];
- int line = 1;
+ char *config_data, *curline;
tern_node * head = NULL;
- while ((curline = strtok_r(config_data, "\n", &state)))
+ config_data = started ? NULL : *state;
+ while ((curline = strtok_r(config_data, "\n", state)))
{
+
config_data = NULL;
curline = strip_ws(curline);
int len = strlen(curline);
if (!len) {
+ *line++;
continue;
}
if (curline[0] == '#') {
+ *line++;
continue;
}
if (curline[0] == '}') {
- if (!nest_level) {
- fprintf(stderr, "unexpected } on line %d\n", line);
- exit(1);
+ if (started) {
+ return head;
}
- if (prefix) {
- free(prefix);
- prefix = NULL;
- }
- nest_level--;
- curline = strip_ws(curline+1);
+ fprintf(stderr, "unexpected } on line %d\n", *line);
+ exit(1);
}
+
char * end = curline + len - 1;
if (*end == '{') {
*end = 0;
curline = strip_ws(curline);
- prefix_parts[nest_level++] = curline;
- if (prefix) {
- free(prefix);
- prefix = NULL;
- }
+ *line++;
+ head = tern_insert_node(head, curline, parse_config_int(state, 1, line));
} else {
- if (nest_level && !prefix) {
- prefix = alloc_concat_m(nest_level, prefix_parts);
- }
char * val = strip_ws(split_keyval(curline));
char * key = curline;
- if (*key) {
- if (prefix) {
- key = alloc_concat(prefix, key);
- }
+ if (*val) {
head = tern_insert_ptr(head, key, strdup(val));
- if (prefix) {
- free(key);
- }
+ } else {
+ fprintf(stderr, "Key %s is missing a value on line %d\n", key, *line);
}
+ *line++;
}
}
- if (prefix) {
- free(prefix);
- }
return head;
}
+tern_node * parse_config(char * config_data)
+{
+ int line = 1;
+ return parse_config_int(&config_data, 0, &line);
+}
+
tern_node * parse_config_file(char * config_path)
{
tern_node * ret = NULL;
diff --git a/config.h b/config.h
index f0e83a1..8817db1 100644
--- a/config.h
+++ b/config.h
@@ -7,6 +7,7 @@
#define CONFIG_H_
#include "tern.h"
+tern_node * parse_config_file(char * config_path);
tern_node * load_config();
#endif //CONFIG_H_
diff --git a/io.c b/io.c
index d6d55c7..50d4df7 100644
--- a/io.c
+++ b/io.c
@@ -248,7 +248,7 @@ void handle_binding_up(keybinding * binding)
{
case UI_DEBUG_MODE_INC:
ui_debug_mode++;
- if (ui_debug_mode == 4) {
+ if (ui_debug_mode == 7) {
ui_debug_mode = 0;
}
genesis->vdp->debug = ui_debug_mode;
@@ -536,7 +536,7 @@ static void cleanup_sockfile()
void setup_io_devices(tern_node * config, io_port * ports)
{
- tern_node *io_nodes = tern_find_prefix(config, "iodevices");
+ tern_node *io_nodes = tern_get_node(tern_find_path(config, "io\0devices\0"));
char * io_1 = tern_find_ptr(io_nodes, "1");
char * io_2 = tern_find_ptr(io_nodes, "2");
char * io_ext = tern_find_ptr(io_nodes, "ext");
@@ -550,7 +550,7 @@ void setup_io_devices(tern_node * config, io_port * ports)
#ifndef _WIN32
if (ports[i].device_type == IO_SEGA_PARALLEL)
{
- char *pipe_name = tern_find_ptr(config, "ioparallel_pipe");
+ char *pipe_name = tern_find_path(config, "io\0parallel_pipe\0").ptrval;
if (!pipe_name)
{
fprintf(stderr, "IO port %s is configured to use the sega parallel board, but no paralell_pipe is set!\n", io_name(i));
@@ -576,7 +576,7 @@ void setup_io_devices(tern_node * config, io_port * ports)
}
}
} else if (ports[i].device_type == IO_GENERIC) {
- char *sock_name = tern_find_ptr(config, "iosocket");
+ char *sock_name = tern_find_path(config, "io\0socket\0").ptrval;
if (!sock_name)
{
fprintf(stderr, "IO port %s is configured to use generic IO, but no socket is set!\n", io_name(i));
@@ -665,64 +665,73 @@ void set_keybindings(io_port *ports)
padbuttons = tern_insert_int(padbuttons, ".start", BUTTON_START);
padbuttons = tern_insert_int(padbuttons, ".mode", BUTTON_MODE);
- tern_node * keys = tern_find_prefix(config, "bindingskeys");
+ tern_node * keys = tern_get_node(tern_find_path(config, "bindings\0keys\0"));
process_keys(keys, special, padbuttons, NULL);
- char prefix[] = "bindingspads00";
- for (int i = 0; i < 100 && i < render_num_joysticks(); i++)
- {
- if (i < 10) {
- prefix[strlen("bindingspads")] = i + '0';
- prefix[strlen("bindingspads")+1] = 0;
- } else {
- prefix[strlen("bindingspads")] = i/10 + '0';
- prefix[strlen("bindingspads")+1] = i%10 + '0';
- }
- tern_node * pad = tern_find_prefix(config, prefix);
- if (pad) {
- char dprefix[] = "dpads0";
- for (int dpad = 0; dpad < 10 && dpad < render_joystick_num_hats(i); dpad++)
- {
- dprefix[strlen("dpads")] = dpad + '0';
- tern_node * pad_dpad = tern_find_prefix(pad, dprefix);
- char * dirs[] = {"up", "down", "left", "right"};
- int dirnums[] = {RENDER_DPAD_UP, RENDER_DPAD_DOWN, RENDER_DPAD_LEFT, RENDER_DPAD_RIGHT};
- for (int dir = 0; dir < sizeof(dirs)/sizeof(dirs[0]); dir++) {
- char * target = tern_find_ptr(pad_dpad, dirs[dir]);
- if (target) {
- int ui_func, padnum, button;
- int bindtype = parse_binding_target(target, padbuttons, &ui_func, &padnum, &button);
- if (bindtype == 1) {
- bind_dpad_gamepad(i, dpad, dirnums[dir], padnum, button);
- } else if (bindtype == 2) {
- bind_dpad_ui(i, dpad, dirnums[dir], ui_func, button);
+ char numstr[] = "00";
+ tern_node * pads = tern_get_node(tern_find_path(config, "bindings\0pads\0"));
+ if (pads) {
+ for (int i = 0; i < 100 && i < render_num_joysticks(); i++)
+ {
+
+ if (i < 10) {
+ numstr[0] = i + '0';
+ numstr[1] = 0;
+ } else {
+ numstr[0] = i/10 + '0';
+ numstr[1] = i%10 + '0';
+ }
+ tern_node * pad = tern_find_ptr(pads, numstr);
+ if (pad) {
+ tern_node * dpad_node = tern_find_ptr(pad, "dpads");
+ if (dpad_node) {
+ for (int dpad = 0; dpad < 10 && dpad < render_joystick_num_hats(i); dpad++)
+ {
+ numstr[0] = dpad + '0';
+ numstr[1] = 0;
+ tern_node * pad_dpad = tern_find_ptr(dpad_node, numstr);
+ char * dirs[] = {"up", "down", "left", "right"};
+ int dirnums[] = {RENDER_DPAD_UP, RENDER_DPAD_DOWN, RENDER_DPAD_LEFT, RENDER_DPAD_RIGHT};
+ for (int dir = 0; dir < sizeof(dirs)/sizeof(dirs[0]); dir++) {
+ char * target = tern_find_ptr(pad_dpad, dirs[dir]);
+ if (target) {
+ int ui_func, padnum, button;
+ int bindtype = parse_binding_target(target, padbuttons, &ui_func, &padnum, &button);
+ if (bindtype == 1) {
+ bind_dpad_gamepad(i, dpad, dirnums[dir], padnum, button);
+ } else if (bindtype == 2) {
+ bind_dpad_ui(i, dpad, dirnums[dir], ui_func, button);
+ }
+ }
}
}
}
- }
- char bprefix[] = "buttons00";
- for (int but = 0; but < 100 && but < render_joystick_num_buttons(i); but++)
- {
- if (but < 10) {
- bprefix[strlen("buttons")] = but + '0';
- bprefix[strlen("buttons")+1] = 0;
- } else {
- bprefix[strlen("buttons")] = but/10 + '0';
- bprefix[strlen("buttons")+1] = but%10 + '0';
- }
- char * target = tern_find_ptr(pad, bprefix);
- if (target) {
- int ui_func, padnum, button;
- int bindtype = parse_binding_target(target, padbuttons, &ui_func, &padnum, &button);
- if (bindtype == 1) {
- bind_button_gamepad(i, but, padnum, button);
- } else if (bindtype == 2) {
- bind_button_ui(i, but, ui_func, button);
+ tern_node *button_node = tern_find_ptr(pad, "buttons");
+ if (button_node) {
+ for (int but = 0; but < 100 && but < render_joystick_num_buttons(i); but++)
+ {
+ if (but < 10) {
+ numstr[0] = but + '0';
+ numstr[1] = 0;
+ } else {
+ numstr[0] = but/10 + '0';
+ numstr[1] = but%10 + '0';
+ }
+ char * target = tern_find_ptr(button_node, numstr);
+ if (target) {
+ int ui_func, padnum, button;
+ int bindtype = parse_binding_target(target, padbuttons, &ui_func, &padnum, &button);
+ if (bindtype == 1) {
+ bind_button_gamepad(i, but, padnum, button);
+ } else if (bindtype == 2) {
+ bind_button_ui(i, but, ui_func, button);
+ }
+ }
}
}
}
}
}
- tern_node * speed_nodes = tern_find_prefix(config, "clocksspeeds");
+ tern_node * speed_nodes = tern_get_node(tern_find_path(config, "clocks\0speeds\0"));
speeds = malloc(sizeof(uint32_t));
speeds[0] = 100;
process_speeds(speed_nodes, NULL);
diff --git a/render_sdl.c b/render_sdl.c
index 9de86dd..82f8b12 100644
--- a/render_sdl.c
+++ b/render_sdl.c
@@ -173,8 +173,10 @@ void render_alloc_surfaces(vdp_context * context)
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(element_data), element_data, GL_STATIC_DRAW);
- vshader = load_shader(tern_find_ptr_default(config, "videovertex_shader", "default.v.glsl"), GL_VERTEX_SHADER);
- fshader = load_shader(tern_find_ptr_default(config, "videofragment_shader", "default.f.glsl"), GL_FRAGMENT_SHADER);
+ tern_val def = {.ptrval = "default.v.glsl"};
+ vshader = load_shader(tern_find_path_default(config, "video\0vertex_shader\0", def).ptrval, GL_VERTEX_SHADER);
+ def.ptrval = "default.f.glsl";
+ fshader = load_shader(tern_find_path_default(config, "video\0fragment_shader\0", def).ptrval, GL_FRAGMENT_SHADER);
program = glCreateProgram();
glAttachShader(program, vshader);
glAttachShader(program, fshader);
@@ -239,7 +241,8 @@ void render_init(int width, int height, char * title, uint32_t fps, uint8_t full
exit(1);
}
float aspect = (float)width / height;
- if (fabs(aspect - 4.0/3.0) > 0.01 && strcmp(tern_find_ptr_default(config, "videoaspect", "normal"), "stretch")) {
+ tern_val def = {.ptrval = "normal"};
+ if (fabs(aspect - 4.0/3.0) > 0.01 && strcmp(tern_find_path_default(config, "video\0aspect\0", def).ptrval, "stretch")) {
for (int i = 0; i < 4; i++)
{
if (aspect > 4.0/3.0) {
@@ -272,7 +275,7 @@ void render_init(int width, int height, char * title, uint32_t fps, uint8_t full
audio_ready = SDL_CreateCond();
SDL_AudioSpec desired, actual;
- char * rate_str = tern_find_ptr(config, "audiorate");
+ char * rate_str = tern_find_path(config, "audio\0rate\0").ptrval;
int rate = rate_str ? atoi(rate_str) : 0;
if (!rate) {
rate = 48000;
@@ -280,7 +283,7 @@ void render_init(int width, int height, char * title, uint32_t fps, uint8_t full
desired.freq = rate;
desired.format = AUDIO_S16SYS;
desired.channels = 2;
- char * samples_str = tern_find_ptr(config, "audiobuffer");
+ char * samples_str = tern_find_path(config, "audio\0buffer\0").ptrval;
int samples = samples_str ? atoi(samples_str) : 0;
if (!samples) {
samples = 512;
diff --git a/rom.db b/rom.db
new file mode 100644
index 0000000..bc9eea2
--- /dev/null
+++ b/rom.db
@@ -0,0 +1,168 @@
+T-081326 {
+ name NBA Jam
+ EEPROM {
+ type i2c
+ size 256
+ }
+ map {
+ 0 {
+ device ROM
+ last 1FFFFF
+ }
+ 200000 {
+ device EEPROM
+ last 3FFFFF
+ bits_read {
+ 1 sda
+ }
+ bits_write {
+ 0 sda
+ 1 scl
+ }
+ }
+ }
+}
+T-81033 {
+ name NBA Jam
+ EEPROM {
+ type i2c
+ size 256
+ }
+ map {
+ 0 {
+ device ROM
+ last 1FFFFF
+ }
+ 200000 {
+ device EEPROM
+ last 3FFFFF
+ bits_read {
+ 1 sda
+ }
+ bits_write {
+ 0 sda
+ 1 scl
+ }
+ }
+ }
+}
+T-081276 {
+ name NFL Quarterback Club
+ EEPROM {
+ type i2c
+ size 256
+ }
+ map {
+ 0 {
+ device ROM
+ last 1FFFFF
+ }
+ 200000 {
+ device EEPROM
+ last 3FFFFF
+ bits_read {
+ 0 sda
+ }
+ bits_write {
+ 0 sda
+ 8 scl
+ }
+ }
+ }
+}
+T-81406 {
+ name NBA Jam TE
+ EEPROM {
+ type i2c
+ size 512
+ }
+ map {
+ 0 {
+ device ROM
+ last 1FFFFF
+ }
+ 200000 {
+ device EEPROM
+ last 3FFFFF
+ bits_read {
+ 0 sda
+ }
+ bits_write {
+ 0 sda
+ 8 scl
+ }
+ }
+ }
+}
+T-081586 {
+ name NFL Quarterback Club '96
+ EEPROM {
+ type i2c
+ size 2048
+ }
+ map {
+ 0 {
+ device ROM
+ last 1FFFFF
+ }
+ 200000 {
+ device EEPROM
+ last 3FFFFF
+ bits_read {
+ 0 sda
+ }
+ bits_write {
+ 0 sda
+ 8 scl
+ }
+ }
+ }
+}
+T-81576 {
+ name College Slam
+ EEPROM {
+ type i2c
+ size 8192
+ }
+ map {
+ 0 {
+ device ROM
+ last 1FFFFF
+ }
+ 200000 {
+ device EEPROM
+ last 3FFFFF
+ bits_read {
+ 0 sda
+ }
+ bits_write {
+ 0 sda
+ 8 scl
+ }
+ }
+ }
+}
+T-81476 {
+ name Frank Thomas Big Hurt Baseball
+ EEPROM {
+ type i2c
+ size 8192
+ }
+ map {
+ 0 {
+ device ROM
+ last 1FFFFF
+ }
+ 200000 {
+ device EEPROM
+ last 3FFFFF
+ bits_read {
+ 0 sda
+ }
+ bits_write {
+ 0 sda
+ 8 scl
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/romdb.c b/romdb.c
new file mode 100644
index 0000000..fc154c9
--- /dev/null
+++ b/romdb.c
@@ -0,0 +1,780 @@
+#include <stdlib.h>
+#include <string.h>
+#include "config.h"
+#include "romdb.h"
+#include "util.h"
+#include "blastem.h"
+
+#define TITLE_START 0x150
+#define TITLE_END (TITLE_START+48)
+#define GAME_ID_OFF 0x183
+#define GAME_ID_LEN 8
+#define ROM_END 0x1A4
+#define RAM_ID 0x1B0
+#define RAM_FLAGS 0x1B2
+#define RAM_START 0x1B4
+#define RAM_END 0x1B8
+#define REGION_START 0x1F0
+
+enum {
+ I2C_IDLE,
+ I2C_START,
+ I2C_DEVICE_ACK,
+ I2C_ADDRESS_HI,
+ I2C_ADDRESS_HI_ACK,
+ I2C_ADDRESS,
+ I2C_ADDRESS_ACK,
+ I2C_READ,
+ I2C_READ_ACK,
+ I2C_WRITE,
+ I2C_WRITE_ACK
+};
+
+char * i2c_states[] = {
+ "idle",
+ "start",
+ "device ack",
+ "address hi",
+ "address hi ack",
+ "address",
+ "address ack",
+ "read",
+ "read_ack",
+ "write",
+ "write_ack"
+};
+
+void eeprom_init(eeprom_state *state, uint8_t *buffer, uint32_t size)
+{
+ state->slave_sda = 1;
+ state->host_sda = state->scl = 0;
+ state->buffer = buffer;
+ state->size = size;
+ state->state = I2C_IDLE;
+}
+
+void set_host_sda(eeprom_state *state, uint8_t val)
+{
+ if (state->scl) {
+ if (val & ~state->host_sda) {
+ //low to high, stop condition
+ state->state = I2C_IDLE;
+ state->slave_sda = 1;
+ } else if (~val & state->host_sda) {
+ //high to low, start condition
+ state->state = I2C_START;
+ state->slave_sda = 1;
+ state->counter = 8;
+ }
+ }
+ state->host_sda = val;
+}
+
+void set_scl(eeprom_state *state, uint8_t val)
+{
+ if (val & ~state->scl) {
+ //low to high transition
+ switch (state->state)
+ {
+ case I2C_START:
+ case I2C_ADDRESS_HI:
+ case I2C_ADDRESS:
+ case I2C_WRITE:
+ state->latch = state->host_sda | state->latch << 1;
+ state->counter--;
+ if (!state->counter) {
+ switch (state->state & 0x7F)
+ {
+ case I2C_START:
+ state->state = I2C_DEVICE_ACK;
+ break;
+ case I2C_ADDRESS_HI:
+ state->address = state->latch << 8;
+ state->state = I2C_ADDRESS_HI_ACK;
+ break;
+ case I2C_ADDRESS:
+ state->address |= state->latch;
+ state->state = I2C_ADDRESS_ACK;
+ break;
+ case I2C_WRITE:
+ state->buffer[state->address] = state->latch;
+ state->state = I2C_WRITE_ACK;
+ state->address++;
+ //TODO: page mask
+ state->address &= state->size-1;
+ break;
+ }
+ }
+ break;
+ case I2C_DEVICE_ACK:
+ if (state->latch & 1) {
+ state->state = I2C_READ;
+ state->counter = 8;
+ state->latch = state->buffer[state->address];
+ state->address++;
+ //TODO: page mask
+ state->address &= state->size-1;
+ } else {
+ if (state->size < 256) {
+ state->address = state->latch >> 1;
+ state->state = I2C_WRITE;
+ } else if (state->size < 8192) {
+ state->address = state->latch << 8;
+ state->state = I2C_ADDRESS;
+ } else {
+ state->state = I2C_ADDRESS_HI;
+ }
+ state->counter = 8;
+ }
+ break;
+ case I2C_ADDRESS_HI_ACK:
+ state->state = I2C_ADDRESS;
+ state->counter = 8;
+ break;
+ case I2C_ADDRESS_ACK:
+ state->state = I2C_WRITE;
+ state->counter = 8;
+ break;
+ case I2C_READ:
+ state->counter--;
+ if (!state->counter) {
+ state->state = I2C_READ_ACK;
+ }
+ break;
+ case I2C_READ_ACK:
+ state->state = I2C_READ;
+ state->counter = 8;
+ break;
+ case I2C_WRITE_ACK:
+ state->state = I2C_WRITE;
+ state->counter = 8;
+ break;
+ }
+ } else if (~val & state->scl) {
+ //high to low transition
+ switch (state->state & 0x7F)
+ {
+ case I2C_DEVICE_ACK:
+ case I2C_ADDRESS_HI_ACK:
+ case I2C_ADDRESS_ACK:
+ case I2C_READ_ACK:
+ case I2C_WRITE_ACK:
+ state->slave_sda = 0;
+ break;
+ case I2C_READ:
+ state->slave_sda = state->latch >> 7;
+ state->latch = state->latch << 1;
+ break;
+ default:
+ state->slave_sda = 1;
+ break;
+ }
+ }
+ state->scl = val;
+}
+
+uint8_t get_sda(eeprom_state *state)
+{
+ return state->host_sda & state->slave_sda;
+}
+
+uint16_t read_sram_w(uint32_t address, m68k_context * context)
+{
+ genesis_context * gen = context->system;
+ address &= gen->save_ram_mask;
+ switch(gen->save_type)
+ {
+ case RAM_FLAG_BOTH:
+ return gen->save_storage[address] << 8 | gen->save_storage[address+1];
+ case RAM_FLAG_EVEN:
+ return gen->save_storage[address >> 1] << 8 | 0xFF;
+ case RAM_FLAG_ODD:
+ return gen->save_storage[address >> 1] | 0xFF00;
+ }
+ return 0xFFFF;//We should never get here
+}
+
+uint8_t read_sram_b(uint32_t address, m68k_context * context)
+{
+ genesis_context * gen = context->system;
+ address &= gen->save_ram_mask;
+ switch(gen->save_type)
+ {
+ case RAM_FLAG_BOTH:
+ return gen->save_storage[address];
+ case RAM_FLAG_EVEN:
+ if (address & 1) {
+ return 0xFF;
+ } else {
+ return gen->save_storage[address >> 1];
+ }
+ case RAM_FLAG_ODD:
+ if (address & 1) {
+ return gen->save_storage[address >> 1];
+ } else {
+ return 0xFF;
+ }
+ }
+ return 0xFF;//We should never get here
+}
+
+m68k_context * write_sram_area_w(uint32_t address, m68k_context * context, uint16_t value)
+{
+ genesis_context * gen = context->system;
+ if ((gen->bank_regs[0] & 0x3) == 1) {
+ address &= gen->save_ram_mask;
+ switch(gen->save_type)
+ {
+ case RAM_FLAG_BOTH:
+ gen->save_storage[address] = value >> 8;
+ gen->save_storage[address+1] = value;
+ break;
+ case RAM_FLAG_EVEN:
+ gen->save_storage[address >> 1] = value >> 8;
+ break;
+ case RAM_FLAG_ODD:
+ gen->save_storage[address >> 1] = value;
+ break;
+ }
+ }
+ return context;
+}
+
+m68k_context * write_sram_area_b(uint32_t address, m68k_context * context, uint8_t value)
+{
+ genesis_context * gen = context->system;
+ if ((gen->bank_regs[0] & 0x3) == 1) {
+ address &= gen->save_ram_mask;
+ switch(gen->save_type)
+ {
+ case RAM_FLAG_BOTH:
+ gen->save_storage[address] = value;
+ break;
+ case RAM_FLAG_EVEN:
+ if (!(address & 1)) {
+ gen->save_storage[address >> 1] = value;
+ }
+ break;
+ case RAM_FLAG_ODD:
+ if (address & 1) {
+ gen->save_storage[address >> 1] = value;
+ }
+ break;
+ }
+ }
+ return context;
+}
+
+m68k_context * write_bank_reg_w(uint32_t address, m68k_context * context, uint16_t value)
+{
+ genesis_context * gen = context->system;
+ address &= 0xE;
+ address >>= 1;
+ gen->bank_regs[address] = value;
+ if (!address) {
+ if (value & 1) {
+ context->mem_pointers[2] = NULL;
+ } else {
+ context->mem_pointers[2] = cart + 0x200000/2;
+ }
+ }
+ return context;
+}
+
+m68k_context * write_bank_reg_b(uint32_t address, m68k_context * context, uint8_t value)
+{
+ if (address & 1) {
+ genesis_context * gen = context->system;
+ address &= 0xE;
+ address >>= 1;
+ gen->bank_regs[address] = value;
+ if (!address) {
+ if (value & 1) {
+ context->mem_pointers[2] = NULL;
+ } else {
+ context->mem_pointers[2] = cart + 0x200000/2;
+ }
+ }
+ }
+ return context;
+}
+eeprom_map *find_eeprom_map(uint32_t address, genesis_context *gen)
+{
+ for (int i = 0; i < gen->num_eeprom; i++)
+ {
+ if (address >= gen->eeprom_map[i].start && address <= gen->eeprom_map[i].end) {
+ return gen->eeprom_map + i;
+ }
+ }
+ return NULL;
+}
+
+void * write_eeprom_i2c_w(uint32_t address, void * context, uint16_t value)
+{
+ genesis_context *gen = ((m68k_context *)context)->system;
+ eeprom_map *map = find_eeprom_map(address, gen);
+ if (!map) {
+ fprintf(stderr, "Could not find EEPROM map for address %X\n", address);
+ exit(1);
+ }
+ printf("EEPROM word write: %X - %X\n", address, value);
+ if (map->scl_mask) {
+ set_scl(&gen->eeprom, (value & map->scl_mask) != 0);
+ printf("scl: %d, state: %s, counter: %d, latch: %X\n", (value & map->scl_mask) != 0, i2c_states[gen->eeprom.state], gen->eeprom.counter, gen->eeprom.latch);
+ }
+ if (map->sda_write_mask) {
+ printf("sda: %d\n", (value & map->sda_write_mask) != 0);
+ set_host_sda(&gen->eeprom, (value & map->sda_write_mask) != 0);
+ }
+ return context;
+}
+
+void * write_eeprom_i2c_b(uint32_t address, void * context, uint8_t value)
+{
+ genesis_context *gen = ((m68k_context *)context)->system;
+ eeprom_map *map = find_eeprom_map(address, gen);
+ if (!map) {
+ fprintf(stderr, "Could not find EEPROM map for address %X\n", address);
+ exit(1);
+ }
+
+ uint16_t expanded, mask;
+ if (address & 1) {
+ expanded = value;
+ mask = 0xFF;
+ } else {
+ expanded = value << 8;
+ mask = 0xFF00;
+ }
+ printf("EEPROM byte write: %X - %X (using mask %X and expanded val %X)\n", address, value, mask, expanded);
+ if (map->scl_mask & mask) {
+ printf("scl: %d, state: %s\n", (expanded & map->scl_mask) != 0, i2c_states[gen->eeprom.state]);
+ set_scl(&gen->eeprom, (expanded & map->scl_mask) != 0);
+ }
+ if (map->sda_write_mask & mask) {
+ printf("sda: %d\n", (expanded & map->sda_write_mask) != 0);
+ set_host_sda(&gen->eeprom, (expanded & map->sda_write_mask) != 0);
+ }
+ return context;
+}
+
+uint16_t read_eeprom_i2c_w(uint32_t address, void * context)
+{
+ genesis_context *gen = ((m68k_context *)context)->system;
+ eeprom_map *map = find_eeprom_map(address, gen);
+ if (!map) {
+ fprintf(stderr, "Could not find EEPROM map for address %X\n", address);
+ exit(1);
+ }
+ uint16_t ret = 0;
+ if (map->sda_read_bit < 16) {
+ ret = get_sda(&gen->eeprom) << map->sda_read_bit;
+ }
+ printf("EEPROM word read: %X - %X\n", address, ret);
+ return ret;
+}
+
+uint8_t read_eeprom_i2c_b(uint32_t address, void * context)
+{
+ genesis_context *gen = ((m68k_context *)context)->system;
+ eeprom_map *map = find_eeprom_map(address, gen);
+ if (!map) {
+ fprintf(stderr, "Could not find EEPROM map for address %X\n", address);
+ exit(1);
+ }
+ uint8_t bit = address & 1 ? map->sda_read_bit : map->sda_read_bit - 8;
+ uint8_t ret = 0;
+ if (bit < 8) {
+ ret = get_sda(&gen->eeprom) << bit;
+ }
+ printf("EEPROM byte read: %X - %X\n", address, ret);
+ return ret;
+}
+
+tern_node *load_rom_db()
+{
+ char *exe_dir = get_exe_dir();
+ if (!exe_dir) {
+ fputs("Failed to find executable path\n", stderr);
+ exit(1);
+ }
+ char *path = alloc_concat(exe_dir, "/rom.db");
+ tern_node *db = parse_config_file(path);
+ free(path);
+ if (!db) {
+ fputs("Failed to load ROM DB\n", stderr);
+ }
+ return db;
+}
+
+char *get_header_name(uint8_t *rom)
+{
+ uint8_t *last = rom + TITLE_END - 1;
+ uint8_t *src = rom + TITLE_START;
+
+ while (last > src && (*last <= 0x20 || *last >= 0x80))
+ {
+ last--;
+ }
+ if (last == src) {
+ //TODO: Use other name field
+ return strdup("UNKNOWN");
+ } else {
+ last++;
+ char *ret = malloc(last - (rom + TITLE_START) + 1);
+ uint8_t *dst;
+ for (dst = ret; src < last; src++)
+ {
+ if (*src >= 0x20 && *src < 0x80) {
+ *(dst++) = *src;
+ }
+ }
+ *dst = 0;
+ return ret;
+ }
+}
+
+char *region_chars = "UB4JEA";
+uint8_t region_bits[] = {REGION_U, REGION_U, REGION_U, REGION_J, REGION_E, REGION_E};
+
+uint8_t translate_region_char(uint8_t c)
+{
+ for (int i = 0; i < sizeof(region_bits); i++)
+ {
+ if (c == region_chars[i]) {
+ return region_bits[i];
+ }
+ }
+ return 0;
+}
+
+uint8_t get_header_regions(uint8_t *rom)
+{
+ uint8_t regions = 0;
+ for (int i = 0; i < 3; i++)
+ {
+ regions |= translate_region_char(rom[REGION_START + i]);
+ }
+ return regions;
+}
+
+uint32_t get_u32be(uint8_t *data)
+{
+ return *data << 24 | data[1] << 16 | data[2] << 8 | data[3];
+}
+
+uint32_t calc_mask(uint32_t src_size, uint32_t start, uint32_t end)
+{
+ uint32_t map_size = end-start+1;
+ if (src_size < map_size) {
+ return nearest_pow2(src_size)-1;
+ } else if (!start) {
+ return 0xFFFFFF;
+ } else {
+ return nearest_pow2(map_size)-1;
+ }
+}
+
+void add_memmap_header(rom_info *info, uint8_t *rom, uint32_t size, memmap_chunk const *base_map, int base_chunks)
+{
+ if (rom[RAM_ID] == 'R' && rom[RAM_ID+1] == 'A') {
+ uint32_t rom_end = get_u32be(rom + ROM_END) + 1;
+ uint32_t ram_start = get_u32be(rom + RAM_START);
+ uint32_t ram_end = get_u32be(rom + RAM_END);
+ uint32_t ram_flags = info->save_type = rom[RAM_FLAGS] & RAM_FLAG_MASK;
+ ram_start &= 0xFFFFFE;
+ ram_end |= 1;
+ info->save_mask = ram_end - ram_start;
+ uint32_t size = info->save_mask + 1;
+ if (ram_flags != RAM_FLAG_BOTH) {
+ size /= 2;
+ }
+ info->save_size = size;
+ info->save_buffer = malloc(size);
+
+ info->map_chunks = base_chunks + (ram_start >= rom_end ? 2 : 3);
+ info->map = malloc(sizeof(memmap_chunk) * info->map_chunks);
+ memset(info->map, 0, sizeof(memmap_chunk)*2);
+ memcpy(info->map+2, base_map, sizeof(memmap_chunk) * base_chunks);
+
+ if (ram_start >= rom_end) {
+ info->map[0].end = rom_end;
+ //TODO: ROM mirroring
+ info->map[0].mask = 0xFFFFFF;
+ info->map[0].flags = MMAP_READ;
+ info->map[0].buffer = rom;
+
+ info->map[1].start = ram_start;
+ info->map[1].mask = info->save_mask;
+ info->map[1].end = ram_end + 1;
+ info->map[1].flags = MMAP_READ | MMAP_WRITE;
+
+ if (ram_flags == RAM_FLAG_ODD) {
+ info->map[1].flags |= MMAP_ONLY_ODD;
+ } else if (ram_flags == RAM_FLAG_EVEN) {
+ info->map[1].flags |= MMAP_ONLY_EVEN;
+ }
+ info->map[1].buffer = info->save_buffer;
+ } else {
+ //Assume the standard Sega mapper
+ info->map[0].end = 0x200000;
+ info->map[0].mask = 0xFFFFFF;
+ info->map[0].flags = MMAP_READ;
+ info->map[0].buffer = rom;
+
+ info->map[1].start = 0x200000;
+ info->map[1].end = 0x400000;
+ info->map[1].mask = 0x1FFFFF;
+ info->map[1].flags = MMAP_READ | MMAP_PTR_IDX | MMAP_FUNC_NULL;
+ info->map[1].ptr_index = 2;
+ info->map[1].read_16 = (read_16_fun)read_sram_w;//these will only be called when mem_pointers[2] == NULL
+ info->map[1].read_8 = (read_8_fun)read_sram_b;
+ info->map[1].write_16 = (write_16_fun)write_sram_area_w;//these will be called all writes to the area
+ info->map[1].write_8 = (write_8_fun)write_sram_area_b;
+ info->map[1].buffer = cart + 0x200000;
+
+ memmap_chunk *last = info->map + info->map_chunks - 1;
+ memset(last, 0, sizeof(memmap_chunk));
+ last->start = 0xA13000;
+ last->end = 0xA13100;
+ last->mask = 0xFF;
+ last->write_16 = (write_16_fun)write_bank_reg_w;
+ last->write_8 = (write_8_fun)write_bank_reg_b;
+ }
+ } else {
+ info->map_chunks = base_chunks + 1;
+ info->map = malloc(sizeof(memmap_chunk) * info->map_chunks);
+ memset(info->map, 0, sizeof(memmap_chunk));
+ memcpy(info->map+1, base_map, sizeof(memmap_chunk) * base_chunks);
+
+ info->map[0].end = 0x400000;
+ info->map[0].mask = 0xFFFFFF;
+ info->map[0].flags = MMAP_READ;
+ info->map[0].buffer = rom;
+ info->save_type = SAVE_NONE;
+ }
+}
+
+rom_info configure_rom_heuristics(uint8_t *rom, uint32_t rom_size, memmap_chunk const *base_map, uint32_t base_chunks)
+{
+ rom_info info;
+ info.name = get_header_name(rom);
+ info.regions = get_header_regions(rom);
+ add_memmap_header(&info, rom, rom_size, base_map, base_chunks);
+ return info;
+}
+
+typedef struct {
+ rom_info *info;
+ uint8_t *rom;
+ tern_node *root;
+ uint32_t rom_size;
+ int index;
+ int num_els;
+} map_iter_state;
+
+void eeprom_read_fun(char *key, tern_val val, void *data)
+{
+ int bit = atoi(key);
+ if (bit < 0 || bit > 15) {
+ fprintf(stderr, "bit %s is out of range", key);
+ return;
+ }
+ char *pin = val.ptrval;
+ if (strcmp(pin, "sda")) {
+ fprintf(stderr, "bit %s is connected to unrecognized read pin %s", key, pin);
+ return;
+ }
+ eeprom_map *map = data;
+ map->sda_read_bit = bit;
+}
+
+void eeprom_write_fun(char *key, tern_val val, void *data)
+{
+ int bit = atoi(key);
+ if (bit < 0 || bit > 15) {
+ fprintf(stderr, "bit %s is out of range", key);
+ return;
+ }
+ char *pin = val.ptrval;
+ eeprom_map *map = data;
+ if (!strcmp(pin, "sda")) {
+ map->sda_write_mask = 1 << bit;
+ return;
+ }
+ if (!strcmp(pin, "scl")) {
+ map->scl_mask = 1 << bit;
+ return;
+ }
+ fprintf(stderr, "bit %s is connected to unrecognized write pin %s", key, pin);
+}
+
+void map_iter_fun(char *key, tern_val val, void *data)
+{
+ map_iter_state *state = data;
+ tern_node *node = tern_get_node(val);
+ if (!node) {
+ fprintf(stderr, "ROM DB map entry %d with address %s is not a node\n", state->index, key);
+ exit(1);
+ }
+ uint32_t start = strtol(key, NULL, 16);
+ uint32_t end = strtol(tern_find_ptr_default(node, "last", "0"), NULL, 16);
+ if (!end || end < start) {
+ fprintf(stderr, "'last' value is missing or invalid for ROM DB map entry %d with address %s\n", state->index, key);
+ exit(1);
+ }
+ char * dtype = tern_find_ptr_default(node, "device", "ROM");
+ uint32_t offset = strtol(tern_find_ptr_default(node, "offset", "0"), NULL, 0);
+ memmap_chunk *map = state->info->map + state->index;
+ map->start = start;
+ map->end = end;
+ if (!strcmp(dtype, "ROM")) {
+ map->buffer = state->rom + offset;
+ map->flags = MMAP_READ;
+ map->mask = calc_mask(state->rom_size, start, end);
+ } else if (!strcmp(dtype, "EEPROM")) {
+ if (!state->info->save_size) {
+ char * size = tern_find_path(state->root, "EEPROM\0size\0").ptrval;
+ if (!size) {
+ fprintf(stderr, "ROM DB map entry %d with address %s has device type EEPROM, but the EEPROM size is not defined\n", state->index, key);
+ exit(1);
+ }
+ state->info->save_size = atoi(size);
+ if (!state->info->save_size) {
+ fprintf(stderr, "EEPROM size %s is invalid\n", size);
+ exit(1);
+ }
+ char *etype = tern_find_path(state->root, "EEPROM\0type\0").ptrval;
+ if (!etype) {
+ etype = "i2c";
+ }
+ if (!strcmp(etype, "i2c")) {
+ state->info->save_type = SAVE_I2C;
+ } else {
+ fprintf(stderr, "EEPROM type %s is invalid\n", etype);
+ exit(1);
+ }
+ state->info->save_buffer = malloc(state->info->save_size);
+ state->info->eeprom_map = malloc(sizeof(eeprom_map) * state->num_els);
+ memset(state->info->eeprom_map, 0, sizeof(eeprom_map) * state->num_els);
+ }
+ eeprom_map *eep_map = state->info->eeprom_map + state->info->num_eeprom;
+ eep_map->start = start;
+ eep_map->end = end;
+ eep_map->sda_read_bit = 0xFF;
+ tern_node * bits_read = tern_find_ptr(node, "bits_read");
+ if (bits_read) {
+ tern_foreach(bits_read, eeprom_read_fun, eep_map);
+ }
+ tern_node * bits_write = tern_find_ptr(node, "bits_write");
+ if (bits_write) {
+ tern_foreach(bits_write, eeprom_write_fun, eep_map);
+ }
+ printf("EEPROM address %X: sda read: %X, sda write: %X, scl: %X\n", start, eep_map->sda_read_bit, eep_map->sda_write_mask, eep_map->scl_mask);
+ state->info->num_eeprom++;
+ map->write_16 = write_eeprom_i2c_w;
+ map->write_8 = write_eeprom_i2c_b;
+ map->read_16 = read_eeprom_i2c_w;
+ map->read_8 = read_eeprom_i2c_b;
+ map->mask = 0xFFFFFF;
+ } else if (!strcmp(dtype, "SRAM")) {
+ if (!state->info->save_size) {
+ char * size = tern_find_path(state->root, "SRAM\0size\0").ptrval;
+ if (!size) {
+ fprintf(stderr, "ROM DB map entry %d with address %s has device type SRAM, but the SRAM size is not defined\n", state->index, key);
+ exit(1);
+ }
+ state->info->save_size = atoi(size);
+ if (!state->info->save_size) {
+ fprintf(stderr, "SRAM size %s is invalid\n", size);
+ exit(1);
+ }
+ state->info->save_buffer = malloc(state->info->save_size);
+ char *bus = tern_find_path(state->root, "SRAM\0bus\0").ptrval;
+ if (!strcmp(bus, "odd")) {
+ state->info->save_type = RAM_FLAG_ODD;
+ } else if(!strcmp(bus, "even")) {
+ state->info->save_type = RAM_FLAG_EVEN;
+ } else {
+ state->info->save_type = RAM_FLAG_BOTH;
+ }
+ }
+ map->buffer = state->info->save_buffer + offset;
+ map->flags = MMAP_READ | MMAP_WRITE;
+ if (state->info->save_type == RAM_FLAG_ODD) {
+ map->flags |= MMAP_ONLY_ODD;
+ } else if(state->info->save_type == RAM_FLAG_EVEN) {
+ map->flags |= MMAP_ONLY_EVEN;
+ }
+ map->mask = calc_mask(state->info->save_size, start, end);
+ } else {
+ fprintf(stderr, "Invalid device type for ROM DB map entry %d with address %s\n", state->index, key);
+ exit(1);
+ }
+ state->index++;
+}
+
+rom_info configure_rom(tern_node *rom_db, void *vrom, uint32_t rom_size, memmap_chunk const *base_map, uint32_t base_chunks)
+{
+ uint8_t product_id[GAME_ID_LEN+1];
+ uint8_t *rom = vrom;
+ product_id[GAME_ID_LEN] = 0;
+ for (int i = 0; i < GAME_ID_LEN; i++)
+ {
+ if (rom[GAME_ID_OFF + i] <= ' ') {
+ product_id[i] = 0;
+ break;
+ }
+ product_id[i] = rom[GAME_ID_OFF + i];
+
+ }
+ printf("Product ID: %s\n", product_id);
+ tern_node * entry = tern_find_ptr(rom_db, product_id);
+ if (!entry) {
+ puts("Not found in ROM DB, examining header\n");
+ return configure_rom_heuristics(rom, rom_size, base_map, base_chunks);
+ }
+ rom_info info;
+ info.name = tern_find_ptr(entry, "name");
+ if (info.name) {
+ printf("Found name: %s\n", info.name);
+ info.name = strdup(info.name);
+ } else {
+ info.name = get_header_name(rom);
+ }
+
+ char *dbreg = tern_find_ptr(entry, "regions");
+ info.regions = 0;
+ if (dbreg) {
+ while (*dbreg != 0)
+ {
+ info.regions |= translate_region_char(*(dbreg++));
+ }
+ }
+ if (!info.regions) {
+ info.regions = get_header_regions(rom);
+ }
+
+ tern_node *map = tern_find_ptr(entry, "map");
+ if (map) {
+ info.map_chunks = tern_count(map);
+ if (info.map_chunks) {
+ info.map_chunks += base_chunks;
+ info.save_buffer = NULL;
+ info.save_size = 0;
+ info.map = malloc(sizeof(memmap_chunk) * info.map_chunks);
+ info.eeprom_map = NULL;
+ info.num_eeprom = 0;
+ memset(info.map, 0, sizeof(memmap_chunk) * (info.map_chunks - base_chunks));
+ map_iter_state state = {&info, rom, entry, rom_size, 0, info.map_chunks - base_chunks};
+ tern_foreach(map, map_iter_fun, &state);
+ memcpy(info.map + state.index, base_map, sizeof(memmap_chunk) * base_chunks);
+ } else {
+ add_memmap_header(&info, rom, rom_size, base_map, base_chunks);
+ }
+ } else {
+ add_memmap_header(&info, rom, rom_size, base_map, base_chunks);
+ }
+
+ return info;
+}
diff --git a/romdb.h b/romdb.h
new file mode 100644
index 0000000..3bf9fa6
--- /dev/null
+++ b/romdb.h
@@ -0,0 +1,57 @@
+#ifndef ROMDB_H_
+#define ROMDB_H_
+
+#define REGION_J 1
+#define REGION_U 2
+#define REGION_E 4
+
+#define RAM_FLAG_ODD 0x18
+#define RAM_FLAG_EVEN 0x10
+#define RAM_FLAG_BOTH 0x00
+#define RAM_FLAG_MASK RAM_FLAG_ODD
+#define SAVE_I2C 0x01
+#define SAVE_NONE 0xFF
+
+#include "tern.h"
+#include "backend.h"
+
+typedef struct {
+ uint32_t start;
+ uint32_t end;
+ uint16_t sda_write_mask;
+ uint16_t scl_mask;
+ uint8_t sda_read_bit;
+} eeprom_map;
+
+typedef struct {
+ char *buffer;
+ uint32_t size;
+ uint16_t address;
+ uint8_t host_sda;
+ uint8_t slave_sda;
+ uint8_t scl;
+ uint8_t state;
+ uint8_t counter;
+ uint8_t latch;
+} eeprom_state;
+
+typedef struct {
+ char *name;
+ memmap_chunk *map;
+ uint8_t *save_buffer;
+ eeprom_map *eeprom_map;
+ uint32_t num_eeprom;
+ uint32_t map_chunks;
+ uint32_t save_size;
+ uint32_t save_mask;
+ uint8_t save_type;
+ uint8_t regions;
+} rom_info;
+
+tern_node *load_rom_db();
+rom_info configure_rom(tern_node *rom_db, void *vrom, uint32_t rom_size, memmap_chunk const *base_map, uint32_t base_chunks);
+rom_info configure_rom_heuristics(uint8_t *rom, uint32_t rom_size, memmap_chunk const *base_map, uint32_t base_chunks);
+uint8_t translate_region_char(uint8_t c);
+void eeprom_init(eeprom_state *state, uint8_t *buffer, uint32_t size);
+
+#endif //ROMDB_H_
diff --git a/tern.c b/tern.c
index 73ea08d..e4f80f8 100644
--- a/tern.c
+++ b/tern.c
@@ -6,6 +6,8 @@
#include "tern.h"
#include <stddef.h>
#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
tern_node * tern_insert(tern_node * head, char * key, tern_val value)
{
@@ -105,7 +107,11 @@ void * tern_find_ptr_default(tern_node * head, char * key, void * def)
{
tern_val ret;
if (tern_find(head, key, &ret)) {
- return ret.ptrval;
+ if (ret.intval & 1) {
+ return (void *)(ret.intval & ~1);
+ } else {
+ return ret.ptrval;
+ }
}
return def;
}
@@ -115,6 +121,32 @@ void * tern_find_ptr(tern_node * head, char * key)
return tern_find_ptr_default(head, key, NULL);
}
+tern_val tern_find_path_default(tern_node *head, char *key, tern_val def)
+{
+ tern_val ret;
+ while (*key)
+ {
+ if (!tern_find(head, key, &ret)) {
+ return def;
+ }
+ key = key + strlen(key) + 1;
+ if (*key) {
+ head = tern_get_node(ret);
+ if (!head) {
+ return def;
+ }
+ }
+ }
+ return ret;
+}
+
+tern_val tern_find_path(tern_node *head, char *key)
+{
+ tern_val def;
+ def.ptrval = NULL;
+ return tern_find_path_default(head, key, def);
+}
+
tern_node * tern_insert_ptr(tern_node * head, char * key, void * value)
{
tern_val val;
@@ -122,6 +154,60 @@ tern_node * tern_insert_ptr(tern_node * head, char * key, void * value)
return tern_insert(head, key, val);
}
+tern_node * tern_insert_node(tern_node *head, char *key, tern_node *value)
+{
+ tern_val val;
+ val.intval = ((intptr_t)value) | 1;
+ return tern_insert(head, key, val);
+}
+
+uint32_t tern_count(tern_node *head)
+{
+ uint32_t count = 0;
+ if (head->left) {
+ count += tern_count(head->left);
+ }
+ if (head->right) {
+ count += tern_count(head->right);
+ }
+ if (!head->el) {
+ count++;
+ } else if (head->straight.next) {
+ count += tern_count(head->straight.next);
+ }
+ return count;
+}
+
+#define MAX_ITER_KEY 127
+void tern_foreach_int(tern_node *head, iter_fun fun, void *data, char *keybuf, int pos)
+{
+ if (!head->el) {
+ keybuf[pos] = 0;
+ fun(keybuf, head->straight.value, data);
+ }
+ if (head->left) {
+ tern_foreach_int(head->left, fun, data, keybuf, pos);
+ }
+ if (head->el) {
+ if (pos == MAX_ITER_KEY) {
+ fputs("exceeded maximum key size", stderr);
+ exit(1);
+ }
+ keybuf[pos] = head->el;
+ tern_foreach_int(head->straight.next, fun, data, keybuf, pos+1);
+ }
+ if (head->right) {
+ tern_foreach_int(head->right, fun, data, keybuf, pos);
+ }
+}
+
+void tern_foreach(tern_node *head, iter_fun fun, void *data)
+{
+ //lame, but good enough for my purposes
+ char key[MAX_ITER_KEY+1];
+ tern_foreach_int(head, fun, data, key, 0);
+}
+
char * tern_int_key(uint32_t key, char * buf)
{
char * cur = buf;
@@ -133,3 +219,8 @@ char * tern_int_key(uint32_t key, char * buf)
*cur = 0;
return buf;
}
+
+tern_node * tern_get_node(tern_val value)
+{
+ return value.intval & 1 ? (tern_node *)(value.intval & ~1) : NULL;
+}
diff --git a/tern.h b/tern.h
index cdf6948..5c0dcfd 100644
--- a/tern.h
+++ b/tern.h
@@ -25,6 +25,8 @@ typedef struct tern_node {
char el;
} tern_node;
+typedef void (*iter_fun)(char *key, tern_val val, void *data);
+
tern_node * tern_insert(tern_node * head, char * key, tern_val value);
int tern_find(tern_node * head, char * key, tern_val *ret);
tern_node * tern_find_prefix(tern_node * head, char * key);
@@ -32,7 +34,13 @@ intptr_t tern_find_int(tern_node * head, char * key, intptr_t def);
tern_node * tern_insert_int(tern_node * head, char * key, intptr_t value);
void * tern_find_ptr_default(tern_node * head, char * key, void * def);
void * tern_find_ptr(tern_node * head, char * key);
+tern_val tern_find_path_default(tern_node *head, char *key, tern_val def);
+tern_val tern_find_path(tern_node *head, char *key);
tern_node * tern_insert_ptr(tern_node * head, char * key, void * value);
+tern_node * tern_insert_node(tern_node *head, char *key, tern_node *value);
+uint32_t tern_count(tern_node *head);
+void tern_foreach(tern_node *head, iter_fun fun, void *data);
char * tern_int_key(uint32_t key, char * buf);
+tern_node * tern_get_node(tern_val value);
#endif //TERN_H_
diff --git a/util.c b/util.c
index 89b5273..2a59b08 100644
--- a/util.c
+++ b/util.c
@@ -2,6 +2,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
+#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -68,6 +69,16 @@ char * split_keyval(char * text)
return text+1;
}
+uint32_t nearest_pow2(uint32_t val)
+{
+ uint32_t ret = 1;
+ while (ret < val)
+ {
+ ret = ret << 1;
+ }
+ return ret;
+}
+
static char * exe_str;
void set_exe_str(char * str)
diff --git a/util.h b/util.h
index 780f538..85b0c8f 100644
--- a/util.h
+++ b/util.h
@@ -15,6 +15,8 @@ long file_size(FILE * f);
char * strip_ws(char * text);
//Inserts a null after the first word, returns a pointer to the second word
char * split_keyval(char * text);
+//Gets the smallest power of two that is >= a certain value, won't work for values > 0x80000000
+uint32_t nearest_pow2(uint32_t val);
//Should be called by main with the value of argv[0] for use by get_exe_dir
void set_exe_str(char * str);
//Returns the directory the executable is in
diff --git a/vdp.c b/vdp.c
index bfc301c..11777c4 100644
--- a/vdp.c
+++ b/vdp.c
@@ -910,15 +910,16 @@ void render_map_output(uint32_t line, int32_t col, vdp_context * context)
}
}
} else {
- uint32_t cell = (line / 8) * (context->regs[REG_MODE_4] & BIT_H40 ? 40 : 32) + col;
- uint32_t address = cell * 32 + (line % 8) * 4;
+ uint32_t base = (context->debug - 3) * 0x200;
+ uint32_t cell = base + (line / 8) * (context->regs[REG_MODE_4] & BIT_H40 ? 40 : 32) + col;
+ uint32_t address = (cell * 32 + (line % 8) * 4) & 0xFFFF;
for (int32_t i = 0; i < 4; i ++) {
*(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] >> 4)];
*(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] & 0xF)];
address++;
}
cell++;
- address = cell * 32 + (line % 8) * 4;
+ address = (cell * 32 + (line % 8) * 4) & 0xFFFF;
for (int32_t i = 0; i < 4; i ++) {
*(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] >> 4)];
*(dst++) = context->colors[(context->debug_pal << 4) | (context->vdpmem[address] & 0xF)];
@@ -1739,7 +1740,7 @@ int vdp_data_port_write(vdp_context * context, uint16_t value)
cur->cycle = context->cycles + ((context->regs[REG_MODE_4] & BIT_H40) ? 16 : 20)*FIFO_LATENCY;
cur->address = context->address;
cur->value = value;
- if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) {
+ if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80 && (context->regs[REG_MODE_2] & BIT_DMA_ENABLE)) {
context->flags |= FLAG_DMA_RUN;
}
cur->cd = context->cd;