From 5d758f6938dc3c73e2c858255748f781aac17010 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Tue, 9 Jul 2013 20:51:42 -0700 Subject: Added ternary tree implementation and a simple test program for it --- tern.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tern.h | 28 ++++++++++++++++++ testtern.c | 21 ++++++++++++++ 3 files changed, 147 insertions(+) create mode 100644 tern.c create mode 100644 tern.h create mode 100644 testtern.c diff --git a/tern.c b/tern.c new file mode 100644 index 0000000..c0d3abf --- /dev/null +++ b/tern.c @@ -0,0 +1,98 @@ +#include "tern.h" +#include +#include + +tern_node * tern_insert(tern_node * head, char * key, tern_val value) +{ + tern_node ** cur = &head; + while(*key) + { + if (*cur) { + while(*cur && (*cur)->el != *key) + { + if (*key < (*cur)->el) { + cur = &(*cur)->left; + } else { + cur = &(*cur)->right; + } + } + } + if (!*cur) { + *cur = malloc(sizeof(tern_node)); + (*cur)->left = NULL; + (*cur)->right = NULL; + (*cur)->straight.next = NULL; + (*cur)->el = *key; + } + cur = &((*cur)->straight.next); + key++; + } + while(*cur && (*cur)->el) + { + cur = &(*cur)->left; + } + if (!*cur) { + *cur = malloc(sizeof(tern_node)); + (*cur)->left = NULL; + (*cur)->right = NULL; + (*cur)->el = 0; + } + (*cur)->straight.value = value; + return head; +} + +int tern_find(tern_node * head, char * key, tern_val *ret) +{ + tern_node * cur = head; + while (cur) + { + if (cur->el == *key) { + if (*key) { + cur = cur->straight.next; + key++; + } else { + *ret = cur->straight.value; + return 1; + } + } else if (*key < cur->el) { + cur = cur->left; + } else { + cur = cur->right; + } + } + return 0; +} + +intptr_t tern_find_int(tern_node * head, char * key, intptr_t def) +{ + tern_val ret; + if (tern_find(head, key, &ret)) { + return ret.intval; + } + return def; +} + +tern_node * tern_insert_int(tern_node * head, char * key, intptr_t value) +{ + tern_val val; + val.intval = value; + return tern_insert(head, key, val); +} + +void * tern_find_ptr(tern_node * head, char * key) +{ + tern_val ret; + if (tern_find(head, key, &ret)) { + return ret.ptrval; + } + return NULL; +} + +tern_node * tern_insert_ptr(tern_node * head, char * key, void * value) +{ + tern_val val; + val.ptrval = value; + return tern_insert(head, key, val); +} + + diff --git a/tern.h b/tern.h new file mode 100644 index 0000000..0c5935a --- /dev/null +++ b/tern.h @@ -0,0 +1,28 @@ +#ifndef TERN_H_ +#define TERN_H_ + +#include + +typedef union { + void *ptrval; + intptr_t intval; +} tern_val; + +typedef struct tern_node { + struct tern_node *left; + union { + struct tern_node *next; + tern_val value; + } straight; + struct tern_node *right; + char el; +} tern_node; + +tern_node * tern_insert(tern_node * head, char * key, tern_val value); +int tern_find(tern_node * head, char * key, tern_val *ret); +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(tern_node * head, char * key); +tern_node * tern_insert_ptr(tern_node * head, char * key, void * value); + +#endif //TERN_H_ diff --git a/testtern.c b/testtern.c new file mode 100644 index 0000000..6898371 --- /dev/null +++ b/testtern.c @@ -0,0 +1,21 @@ +#include "tern.h" +#include +#include + +int main(int argc, char ** argv) +{ + tern_node * tree = tern_insert_ptr(NULL, "foo", "bar"); + tree = tern_insert_ptr(tree, "foobar", "baz"); + tree = tern_insert_ptr(tree, "goobar", "qux"); + tree = tern_insert_int(tree, "foobarbaz", 42); + tree = tern_insert_int(tree, "goobarbaz", 21); + printf("foo: %s\n", (char *)tern_find_ptr(tree, "foo")); + printf("foobar: %s\n", (char *)tern_find_ptr(tree, "foobar")); + printf("goobar: %s\n", (char *)tern_find_ptr(tree, "goobar")); + printf("foob: %s\n", (char *)tern_find_ptr(tree, "foob")); + printf("foobarbaz: %d\n", (int)tern_find_int(tree, "foobarbaz", 0)); + printf("goobarbaz: %d\n", (int)tern_find_int(tree, "goobarbaz", 0)); + printf("foobarb: %d\n", (int)tern_find_int(tree, "foobarb", 0)); + return 0; +} + -- cgit v1.2.3 From 28b8256fddf53446898120f9b51a43f132d946bb Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Wed, 10 Jul 2013 09:38:05 -0700 Subject: Add config file parser and default config file --- Makefile | 4 +- blastem.c | 5 +- blastem.h | 2 + config.c | 229 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ config.h | 8 +++ default.cfg | 50 +++++++++++++ 6 files changed, 295 insertions(+), 3 deletions(-) create mode 100644 config.c create mode 100644 config.h create mode 100644 default.cfg diff --git a/Makefile b/Makefile index b593bb9..7c38e95 100644 --- a/Makefile +++ b/Makefile @@ -18,8 +18,8 @@ AUDIOOBJS=ym2612.o psg.o wave.o all : dis trans stateview blastem -blastem : blastem.o vdp.o render_sdl.o io.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS) - $(CC) -ggdb -o blastem blastem.o vdp.o render_sdl.o io.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS) $(LDFLAGS) +blastem : blastem.o vdp.o render_sdl.o io.o config.o tern.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS) + $(CC) -ggdb -o blastem blastem.o vdp.o render_sdl.o io.o config.o tern.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS) $(LDFLAGS) dis : dis.o 68kinst.o $(CC) -o dis dis.o 68kinst.o diff --git a/blastem.c b/blastem.c index dfa950d..2a02b31 100644 --- a/blastem.c +++ b/blastem.c @@ -35,6 +35,8 @@ int headless = 0; int z80_enabled = 1; int frame_limit = 0; +tern_node * config; + #ifndef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #endif @@ -1871,13 +1873,14 @@ void detect_region() int main(int argc, char ** argv) { if (argc < 2) { - fputs("Usage: blastem FILENAME\n", stderr); + fputs("Usage: blastem FILENAME [options]\n", stderr); return 1; } if(!load_rom(argv[1])) { fprintf(stderr, "Failed to open %s for reading\n", argv[1]); return 1; } + config = load_config(argv[0]); detect_region(); int width = -1; int height = -1; diff --git a/blastem.h b/blastem.h index 63571d2..635794d 100644 --- a/blastem.h +++ b/blastem.h @@ -8,6 +8,7 @@ #include "vdp.h" #include "psg.h" #include "io.h" +#include "config.h" #define RAM_FLAG_ODD 0x1800 #define RAM_FLAG_EVEN 0x1000 @@ -30,6 +31,7 @@ typedef struct { extern genesis_context * genesis; extern int break_on_sync; +extern tern_node * config; uint16_t read_dma_value(uint32_t address); m68k_context * debugger(m68k_context * context, uint32_t address); diff --git a/config.c b/config.c new file mode 100644 index 0000000..06e36cd --- /dev/null +++ b/config.c @@ -0,0 +1,229 @@ +#include "tern.h" +#include +#include +#include +#include +#include + +#include +#include +#include + +char * alloc_concat(char * first, char * second) +{ + int flen = strlen(first); + int slen = strlen(second); + char * ret = malloc(flen + slen + 1); + memcpy(ret, first, flen); + memcpy(ret+flen, second, slen+1); + return ret; +} + +char * alloc_concat_m(int num_parts, char ** parts) +{ + int total = 0; + for (int i = 0; i < num_parts; i++) { + total += strlen(parts[i]); + } + char * ret = malloc(total + 1); + *ret = 0; + for (int i = 0; i < num_parts; i++) { + strcat(ret, parts[i]); + } + return ret; +} + +long file_size(FILE * f) +{ + fseek(f, 0, SEEK_END); + long fsize = ftell(f); + fseek(f, 0, SEEK_SET); + return fsize; +} + +char * strip_ws(char * text) +{ + while (*text && (!isprint(*text) || isblank(*text))) + { + text++; + } + char * ret = text; + text = ret + strlen(ret) - 1; + while (text > ret && (!isprint(*text) || isblank(*text))) + { + *text = 0; + text--; + } + return ret; +} + +char * split_keyval(char * text) +{ + while (*text && !isblank(*text)) + { + text++; + } + if (!*text) { + return text; + } + *text = 0; + return text+1; +} + +#define MAX_NEST 30 //way more than I'll ever need + +tern_node * parse_config(char * config_data) +{ + char *state, *curline; + char *prefix = NULL; + int nest_level = 0; + char * prefix_parts[MAX_NEST]; + int line = 1; + tern_node * head = NULL; + while ((curline = strtok_r(config_data, "\n", &state))) + { + config_data = NULL; + curline = strip_ws(curline); + int len = strlen(curline); + if (!len) { + continue; + } + if (curline[0] == '#') { + continue; + } + if (curline[0] == '}') { + if (!nest_level) { + fprintf(stderr, "unexpected } on line %d\n", line); + exit(1); + } + if (prefix) { + free(prefix); + prefix = NULL; + } + nest_level--; + curline = strip_ws(curline+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; + } + } 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); + } + head = tern_insert_ptr(head, key, val); + if (prefix) { + free(key); + } + } + } + } + if (prefix) { + free(prefix); + } + return head; +} + +tern_node * parse_config_file(char * config_path) +{ + tern_node * ret = NULL; + FILE * config_file = fopen(config_path, "r"); + if (!config_file) { + goto open_fail; + } + long config_size = file_size(config_file); + if (!config_size) { + goto config_empty; + } + char * config_data = malloc(config_size); + if (fread(config_data, 1, config_size, config_file) != config_size) { + goto config_read_fail; + } + ret = parse_config(config_data); +config_read_fail: + free(config_data); +config_empty: + fclose(config_file); +open_fail: + return ret; +} + +char * readlink_alloc(char * path) +{ + char * linktext = NULL; + ssize_t linksize = 512; + ssize_t cursize = 0; + do { + if (linksize > cursize) { + cursize = linksize; + if (linktext) { + free(linktext); + } + } + linktext = malloc(cursize); + linksize = readlink(path, linktext, cursize-1); + if (linksize == -1) { + perror("readlink"); + free(linktext); + linktext = NULL; + } + } while (linksize > cursize); + return linktext; +} + +tern_node * load_config(char * expath) +{ + char * linktext; + char * home = getenv("HOME"); + if (!home) { + goto load_in_app_dir; + } + char * path = alloc_concat(home, "/.config/blastem/blastem.cfg"); + tern_node * ret = parse_config_file(path); + if (ret) { + goto success; + } + free(path); +load_in_app_dir: + + linktext = readlink_alloc("/proc/self/exe"); + if (!linktext) { + goto link_prob; + } + char * cur; + int linksize = strlen(linktext); + for(cur = linktext + linksize - 1; cur != linktext; cur--) + { + if (*cur == '/') { + *cur = 0; + break; + } + } + if (cur == linktext) { + goto link_prob; + } + path = alloc_concat(linktext, "/default.cfg"); + ret = parse_config_file(path); +success: + return ret; +link_prob: + if (linktext) { + free(linktext); + } +no_proc: + //TODO: Fall back to using expath if /proc is not available + fputs("Failed to find a config file in ~/.config/blastem/blastem.cfg or in the blastem executable directory\n", stderr); + exit(1); +} + diff --git a/config.h b/config.h new file mode 100644 index 0000000..3850c53 --- /dev/null +++ b/config.h @@ -0,0 +1,8 @@ +#ifndef CONFIG_H_ +#define CONFIG_H_ +#include "tern.h" + +tern_node * load_config(char * expath); + +#endif //CONFIG_H_ + diff --git a/default.cfg b/default.cfg new file mode 100644 index 0000000..ac75505 --- /dev/null +++ b/default.cfg @@ -0,0 +1,50 @@ + +bindings { + keys { + UP gamepads.1.up + DOWN gamepads.1.down + LEFT gamepads.1.left + RIGHT gamepads.1.right + a gamepads.1.a + s gamepads.1.b + d gamepads.1.c + q gamepads.1.x + w gamepads.1.y + e gamepads.1.z + f gamepads.1.mode + ENTER gamepads.1.start + + [ ui.vdp_debug_mode + ] ui.vdp_debug_pal + u ui.enter_debugger + } + pads { + 0 { + dpads { + 0 { + up gamepads.2.up + down gamepads.2.down + left gamepads.2.left + right gamepads.2.right + } + } + buttons { + 0 gamepads.2.a + 1 gamepads.2.b + 2 gamepads.2.c + 3 gamepads.2.x + 4 gamepads.2.y + 5 gamepads.2.z + 6 gamepads.2.mode + 7 gamepads.2.start + } + } + } +} + +video { + width 640 +} + +default_region U + -- cgit v1.2.3 From 122bf9de037f578cc4862b1dc775b6589d56057d Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Wed, 10 Jul 2013 22:48:17 -0700 Subject: Read key bindings from config file --- config.c | 2 +- default.cfg | 10 +++--- io.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++--------- tern.c | 17 +++++++++++ tern.h | 1 + 5 files changed, 109 insertions(+), 21 deletions(-) diff --git a/config.c b/config.c index 06e36cd..d106f27 100644 --- a/config.c +++ b/config.c @@ -122,7 +122,7 @@ tern_node * parse_config(char * config_data) if (prefix) { key = alloc_concat(prefix, key); } - head = tern_insert_ptr(head, key, val); + head = tern_insert_ptr(head, key, strdup(val)); if (prefix) { free(key); } diff --git a/default.cfg b/default.cfg index ac75505..d2cdde8 100644 --- a/default.cfg +++ b/default.cfg @@ -1,10 +1,10 @@ bindings { keys { - UP gamepads.1.up - DOWN gamepads.1.down - LEFT gamepads.1.left - RIGHT gamepads.1.right + up gamepads.1.up + down gamepads.1.down + left gamepads.1.left + right gamepads.1.right a gamepads.1.a s gamepads.1.b d gamepads.1.c @@ -12,7 +12,7 @@ bindings { w gamepads.1.y e gamepads.1.z f gamepads.1.mode - ENTER gamepads.1.start + enter gamepads.1.start [ ui.vdp_debug_mode ] ui.vdp_debug_pal diff --git a/io.c b/io.c index c951739..9fc3ad3 100644 --- a/io.c +++ b/io.c @@ -252,23 +252,93 @@ void handle_joy_dpad(int joystick, int dpadnum, uint8_t value) } } +void process_keys(tern_node * cur, tern_node * special, tern_node * padbuttons, char * prefix) +{ + char * curstr; + int len; + if (!cur) { + return; + } + char onec[2]; + if (prefix) { + len = strlen(prefix); + curstr = malloc(len + 2); + memcpy(curstr, prefix, len); + } else { + curstr = onec; + len = 0; + } + curstr[len] = cur->el; + if (cur->el) { + curstr[len+1] = 0; + process_keys(cur->straight.next, special, padbuttons, curstr); + } else { + int keycode = tern_find_int(special, curstr, 0); + if (!keycode) { + keycode = curstr[0]; + if (curstr[1] != 0) { + fprintf(stderr, "%s is not recognized as a key identifier, truncating to %c\n", curstr, curstr[0]); + } + } + char * target = cur->straight.value.ptrval; + int gpadslen = strlen("gamepads."); + if (!memcmp(target, "gamepads.", gpadslen)) { + if (target[gpadslen] >= '1' && target[gpadslen] <= '8') { + int padnum = target[gpadslen] - '0'; + int button = tern_find_int(padbuttons, target + gpadslen + 1, 0); + if (button) { + bind_gamepad(keycode, padnum, button); + } else { + if (target[gpadslen+1]) { + fprintf(stderr, "Gamepad mapping string '%s' refers to an invalid button '%s'\n", target, target + gpadslen + 1); + } else { + fprintf(stderr, "Gamepad mapping string '%s' has no button component\n", target); + } + } + } else { + fprintf(stderr, "Gamepad mapping string '%s' refers to an invalid gamepad number %c\n", target, target[gpadslen]); + } + } else if(!memcmp(target, "ui.", strlen("ui."))) { + if (!strcmp(target + 3, "vdp_debug_mode")) { + bind_ui(keycode, UI_DEBUG_MODE_INC); + } else if(!strcmp(target + 3, "vdp_debug_pal")) { + bind_ui(keycode, UI_DEBUG_PAL_INC); + } else if(!strcmp(target + 3, "enter_debugger")) { + bind_ui(keycode, UI_ENTER_DEBUGGER); + } else { + fprintf(stderr, "Unreconized UI binding type %s for key %s\n", target, curstr); + } + } else { + fprintf(stderr, "Unrecognized binding type %s for key %s\n", target, curstr); + } + } + process_keys(cur->left, special, padbuttons, prefix); + process_keys(cur->right, special, padbuttons, prefix); +} + void set_keybindings() { - bind_gamepad(RENDERKEY_UP, 1, DPAD_UP); - bind_gamepad(RENDERKEY_DOWN, 1, DPAD_DOWN); - bind_gamepad(RENDERKEY_LEFT, 1, DPAD_LEFT); - bind_gamepad(RENDERKEY_RIGHT, 1, DPAD_RIGHT); - bind_gamepad('a', 1, BUTTON_A); - bind_gamepad('s', 1, BUTTON_B); - bind_gamepad('d', 1, BUTTON_C); - bind_gamepad('q', 1, BUTTON_X); - bind_gamepad('w', 1, BUTTON_Y); - bind_gamepad('e', 1, BUTTON_Z); - bind_gamepad('\r', 1, BUTTON_START); - bind_gamepad('f', 1, BUTTON_MODE); - bind_ui('[', UI_DEBUG_MODE_INC); - bind_ui(']', UI_DEBUG_PAL_INC); - bind_ui('u', UI_ENTER_DEBUGGER); + tern_node * special = tern_insert_int(NULL, "up", RENDERKEY_UP); + special = tern_insert_int(special, "down", RENDERKEY_DOWN); + special = tern_insert_int(special, "left", RENDERKEY_LEFT); + special = tern_insert_int(special, "right", RENDERKEY_RIGHT); + special = tern_insert_int(special, "enter", '\r'); + + tern_node * padbuttons = tern_insert_int(NULL, ".up", DPAD_UP); + padbuttons = tern_insert_int(padbuttons, ".down", DPAD_DOWN); + padbuttons = tern_insert_int(padbuttons, ".left", DPAD_LEFT); + padbuttons = tern_insert_int(padbuttons, ".right", DPAD_RIGHT); + padbuttons = tern_insert_int(padbuttons, ".a", BUTTON_A); + padbuttons = tern_insert_int(padbuttons, ".b", BUTTON_B); + padbuttons = tern_insert_int(padbuttons, ".c", BUTTON_C); + padbuttons = tern_insert_int(padbuttons, ".x", BUTTON_X); + padbuttons = tern_insert_int(padbuttons, ".y", BUTTON_Y); + padbuttons = tern_insert_int(padbuttons, ".z", BUTTON_Z); + padbuttons = tern_insert_int(padbuttons, ".start", BUTTON_START); + padbuttons = tern_insert_int(padbuttons, ".mode", BUTTON_MODE); + + tern_node * keys = tern_find_prefix(config, "bindingskeys"); + process_keys(keys, special, padbuttons, NULL); bind_dpad_gamepad(0, 0, RENDER_DPAD_UP, 2, DPAD_UP); bind_dpad_gamepad(0, 0, RENDER_DPAD_DOWN, 2, DPAD_DOWN); diff --git a/tern.c b/tern.c index c0d3abf..7e60a22 100644 --- a/tern.c +++ b/tern.c @@ -63,6 +63,23 @@ int tern_find(tern_node * head, char * key, tern_val *ret) return 0; } +tern_node * tern_find_prefix(tern_node * head, char * key) +{ + tern_node * cur = head; + while (cur && *key) + { + if (cur->el == *key) { + cur = cur->straight.next; + key++; + } else if (*key < cur->el) { + cur = cur->left; + } else { + cur = cur->right; + } + } + return cur; +} + intptr_t tern_find_int(tern_node * head, char * key, intptr_t def) { tern_val ret; diff --git a/tern.h b/tern.h index 0c5935a..c731bf6 100644 --- a/tern.h +++ b/tern.h @@ -20,6 +20,7 @@ typedef struct tern_node { 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); 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(tern_node * head, char * key); -- cgit v1.2.3 From 931a3998b096b27d881f4e4576759ef0e0bff4e1 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Wed, 10 Jul 2013 23:47:48 -0700 Subject: Read joystick bindings from config file --- io.c | 140 +++++++++++++++++++++++++++++++++++++++++------------------ render.h | 1 + render_sdl.c | 5 +++ 3 files changed, 104 insertions(+), 42 deletions(-) diff --git a/io.c b/io.c index 9fc3ad3..a8296df 100644 --- a/io.c +++ b/io.c @@ -252,6 +252,45 @@ void handle_joy_dpad(int joystick, int dpadnum, uint8_t value) } } +int parse_binding_target(char * target, tern_node * padbuttons, int * ui_out, int * padnum_out, int * padbutton_out) +{ + int gpadslen = strlen("gamepads."); + if (!memcmp(target, "gamepads.", gpadslen)) { + if (target[gpadslen] >= '1' && target[gpadslen] <= '8') { + int padnum = target[gpadslen] - '0'; + int button = tern_find_int(padbuttons, target + gpadslen + 1, 0); + if (button) { + *padnum_out = padnum; + *padbutton_out = button; + return 1; + } else { + if (target[gpadslen+1]) { + fprintf(stderr, "Gamepad mapping string '%s' refers to an invalid button '%s'\n", target, target + gpadslen + 1); + } else { + fprintf(stderr, "Gamepad mapping string '%s' has no button component\n", target); + } + } + } else { + fprintf(stderr, "Gamepad mapping string '%s' refers to an invalid gamepad number %c\n", target, target[gpadslen]); + } + } else if(!memcmp(target, "ui.", strlen("ui."))) { + if (!strcmp(target + 3, "vdp_debug_mode")) { + *ui_out = UI_DEBUG_MODE_INC; + } else if(!strcmp(target + 3, "vdp_debug_pal")) { + *ui_out = UI_DEBUG_PAL_INC; + } else if(!strcmp(target + 3, "enter_debugger")) { + *ui_out = UI_ENTER_DEBUGGER; + } else { + fprintf(stderr, "Unreconized UI binding type %s\n", target); + return 0; + } + return 2; + } else { + fprintf(stderr, "Unrecognized binding type %s\n", target); + } + return 0; +} + void process_keys(tern_node * cur, tern_node * special, tern_node * padbuttons, char * prefix) { char * curstr; @@ -281,35 +320,12 @@ void process_keys(tern_node * cur, tern_node * special, tern_node * padbuttons, } } char * target = cur->straight.value.ptrval; - int gpadslen = strlen("gamepads."); - if (!memcmp(target, "gamepads.", gpadslen)) { - if (target[gpadslen] >= '1' && target[gpadslen] <= '8') { - int padnum = target[gpadslen] - '0'; - int button = tern_find_int(padbuttons, target + gpadslen + 1, 0); - if (button) { - bind_gamepad(keycode, padnum, button); - } else { - if (target[gpadslen+1]) { - fprintf(stderr, "Gamepad mapping string '%s' refers to an invalid button '%s'\n", target, target + gpadslen + 1); - } else { - fprintf(stderr, "Gamepad mapping string '%s' has no button component\n", target); - } - } - } else { - fprintf(stderr, "Gamepad mapping string '%s' refers to an invalid gamepad number %c\n", target, target[gpadslen]); - } - } else if(!memcmp(target, "ui.", strlen("ui."))) { - if (!strcmp(target + 3, "vdp_debug_mode")) { - bind_ui(keycode, UI_DEBUG_MODE_INC); - } else if(!strcmp(target + 3, "vdp_debug_pal")) { - bind_ui(keycode, UI_DEBUG_PAL_INC); - } else if(!strcmp(target + 3, "enter_debugger")) { - bind_ui(keycode, UI_ENTER_DEBUGGER); - } else { - fprintf(stderr, "Unreconized UI binding type %s for key %s\n", target, curstr); - } - } else { - fprintf(stderr, "Unrecognized binding type %s for key %s\n", target, curstr); + int ui_func, padnum, button; + int bindtype = parse_binding_target(target, padbuttons, &ui_func, &padnum, &button); + if (bindtype == 1) { + bind_gamepad(keycode, padnum, button); + } else if(bindtype == 2) { + bind_ui(keycode, ui_func); } } process_keys(cur->left, special, padbuttons, prefix); @@ -339,19 +355,59 @@ void set_keybindings() tern_node * keys = tern_find_prefix(config, "bindingskeys"); process_keys(keys, special, padbuttons, NULL); - - bind_dpad_gamepad(0, 0, RENDER_DPAD_UP, 2, DPAD_UP); - bind_dpad_gamepad(0, 0, RENDER_DPAD_DOWN, 2, DPAD_DOWN); - bind_dpad_gamepad(0, 0, RENDER_DPAD_LEFT, 2, DPAD_LEFT); - bind_dpad_gamepad(0, 0, RENDER_DPAD_RIGHT, 2, DPAD_RIGHT); - bind_button_gamepad(0, 0, 2, BUTTON_A); - bind_button_gamepad(0, 1, 2, BUTTON_B); - bind_button_gamepad(0, 2, 2, BUTTON_C); - bind_button_gamepad(0, 3, 2, BUTTON_X); - bind_button_gamepad(0, 4, 2, BUTTON_Y); - bind_button_gamepad(0, 5, 2, BUTTON_Z); - bind_button_gamepad(0, 6, 2, BUTTON_START); - bind_button_gamepad(0, 7, 2, BUTTON_MODE); + 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); + } + //TODO: Handle UI bindings + } + } + } + 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); + } + //TODO: Handle UI bindings + } + } + } + } } #define TH 0x40 diff --git a/render.h b/render.h index 24456be..48f22b4 100644 --- a/render.h +++ b/render.h @@ -20,6 +20,7 @@ void render_debug_pal(uint8_t pal); void process_events(); int render_joystick_num_buttons(int joystick); int render_joystick_num_hats(int joystick); +int render_num_joysticks(); //TODO: Throw an ifdef in here once there's more than one renderer #include diff --git a/render_sdl.c b/render_sdl.c index 5fbf518..6148c96 100644 --- a/render_sdl.c +++ b/render_sdl.c @@ -73,6 +73,11 @@ void render_close_audio() SDL_Joystick * joysticks[MAX_JOYSTICKS]; int num_joysticks; +int render_num_joysticks() +{ + return num_joysticks; +} + uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b) { return SDL_MapRGB(screen->format, r, g, b); -- cgit v1.2.3 From 99c967da1e83f5073c7134cbe9853bbb33fb9243 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Wed, 10 Jul 2013 23:52:09 -0700 Subject: Read default render width from config file --- blastem.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/blastem.c b/blastem.c index 2a02b31..1b93263 100644 --- a/blastem.c +++ b/blastem.c @@ -1953,7 +1953,15 @@ int main(int argc, char ** argv) } } update_title(); - width = width < 320 ? 640 : width; + int def_width = 0; + char *config_width = tern_find_ptr(config, "videowidth"); + if (config_width) { + def_width = atoi(config_width); + } + if (!def_width) { + def_width = 640; + } + width = width < 320 ? def_width : width; height = height < 240 ? (width/320) * 240 : height; uint32_t fps = 60; if (version_reg & 0x40) { -- cgit v1.2.3 From 919293aa6622ad3c73062a4a401482d5ca5cd19e Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Wed, 10 Jul 2013 23:55:11 -0700 Subject: Read default region from config file --- blastem.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/blastem.c b/blastem.c index 1b93263..0e5e547 100644 --- a/blastem.c +++ b/blastem.c @@ -1867,6 +1867,25 @@ void detect_region() version_reg = NO_DISK | JAP; } else if (detect_specific_region('E') || detect_specific_region('A')) { version_reg = NO_DISK | EUR; + } else { + 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; + } + } } } -- cgit v1.2.3 From ee31d8d00be8f08e446939280cb8d71bb606f899 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Thu, 11 Jul 2013 08:34:38 -0700 Subject: Updated todo list --- todo.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/todo.txt b/todo.txt index 72c0af4..490b604 100644 --- a/todo.txt +++ b/todo.txt @@ -6,10 +6,10 @@ Z80 mostly done 10 |--Banked mem complete |--Test gen complete -PSG not started 9 -YM-2612 not started 9 +PSG complete 9 +YM-2612 partial 9 H-Ints complete 8 -Interlace not started 7 +Interlace complete 7 save states partial 6 mappers partial 5 save ram complete 5 -- cgit v1.2.3 From f55d4a3023f1dc520b54bca1cd4bcb334a0ba25c Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Fri, 12 Jul 2013 19:11:55 -0700 Subject: Implement the scroll ring buffer properly without memcpy --- vdp.c | 54 ++++++++++++++++++++++++++++-------------------------- vdp.h | 2 ++ 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/vdp.c b/vdp.c index bc39390..265b006 100644 --- a/vdp.c +++ b/vdp.c @@ -12,7 +12,8 @@ #define MAP_BIT_V_FLIP 0x1000 #define SCROLL_BUFFER_SIZE 32 -#define SCROLL_BUFFER_DRAW 16 +#define SCROLL_BUFFER_MASK (SCROLL_BUFFER_SIZE-1) +#define SCROLL_BUFFER_DRAW (SCROLL_BUFFER_SIZE/2) #define FIFO_SIZE 4 @@ -612,7 +613,7 @@ void read_map_scroll_b(uint16_t column, uint32_t line, vdp_context * context) read_map_scroll(column, 1, line, (context->regs[REG_SCROLL_B] & 0x7) << 13, context->hscroll_b, context); } -void render_map(uint16_t col, uint8_t * tmp_buf, vdp_context * context) +void render_map(uint16_t col, uint8_t * tmp_buf, uint8_t offset, vdp_context * context) { uint16_t address; uint8_t shift, add; @@ -633,33 +634,36 @@ void render_map(uint16_t col, uint8_t * tmp_buf, vdp_context * context) uint16_t pal_priority = (col >> 9) & 0x70; int32_t dir; if (col & MAP_BIT_H_FLIP) { - tmp_buf += 7; + offset += 7; + offset &= SCROLL_BUFFER_MASK; dir = -1; } else { dir = 1; } for (uint32_t i=0; i < 4; i++, address++) { - *tmp_buf = pal_priority | (context->vdpmem[address] >> 4); - tmp_buf += dir; - *tmp_buf = pal_priority | (context->vdpmem[address] & 0xF); - tmp_buf += dir; + tmp_buf[offset] = pal_priority | (context->vdpmem[address] >> 4); + offset += dir; + offset &= SCROLL_BUFFER_MASK; + tmp_buf[offset] = pal_priority | (context->vdpmem[address] & 0xF); + offset += dir; + offset &= SCROLL_BUFFER_MASK; } } void render_map_1(vdp_context * context) { - render_map(context->col_1, context->tmp_buf_a+SCROLL_BUFFER_DRAW, context); + render_map(context->col_1, context->tmp_buf_a, context->buf_a_off, context); } void render_map_2(vdp_context * context) { - render_map(context->col_2, context->tmp_buf_a+SCROLL_BUFFER_DRAW+8, context); + render_map(context->col_2, context->tmp_buf_a, context->buf_a_off+8, context); } void render_map_3(vdp_context * context) { - render_map(context->col_1, context->tmp_buf_b+SCROLL_BUFFER_DRAW, context); + render_map(context->col_1, context->tmp_buf_b, context->buf_b_off, context); } void render_map_output(uint32_t line, int32_t col, vdp_context * context) @@ -667,10 +671,11 @@ void render_map_output(uint32_t line, int32_t col, vdp_context * context) if (line >= 240) { return; } - render_map(context->col_2, context->tmp_buf_b+SCROLL_BUFFER_DRAW+8, context); + render_map(context->col_2, context->tmp_buf_b, context->buf_b_off+8, context); uint16_t *dst; uint32_t *dst32; - uint8_t *sprite_buf, *plane_a, *plane_b; + uint8_t *sprite_buf, *plane_a, *plane_b; + int plane_a_off, plane_b_off; if (col) { col-=2; @@ -684,20 +689,21 @@ void render_map_output(uint32_t line, int32_t col, vdp_context * context) sprite_buf = context->linebuf + col * 8; uint16_t a_src; if (context->flags & FLAG_WINDOW) { - plane_a = context->tmp_buf_a + SCROLL_BUFFER_DRAW; + plane_a_off = context->buf_a_off; //a_src = FBUF_SRC_W; } else { - plane_a = context->tmp_buf_a + SCROLL_BUFFER_DRAW - (context->hscroll_a & 0xF); + plane_a_off = context->buf_a_off - (context->hscroll_a & 0xF); //a_src = FBUF_SRC_A; } - plane_b = context->tmp_buf_b + SCROLL_BUFFER_DRAW - (context->hscroll_b & 0xF); + plane_b_off = context->buf_b_off - (context->hscroll_b & 0xF); uint16_t src; //printf("A | tmp_buf offset: %d\n", 8 - (context->hscroll_a & 0x7)); if (context->regs[REG_MODE_4] & BIT_HILIGHT) { - for (int i = 0; i < 16; ++plane_a, ++plane_b, ++sprite_buf, ++i) { + for (int i = 0; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) { uint8_t pixel; - + plane_a = context->tmp_buf_a + (plane_a_off & SCROLL_BUFFER_MASK); + plane_b = context->tmp_buf_b + (plane_b_off & SCROLL_BUFFER_MASK); src = 0; uint8_t sprite_color = *sprite_buf & 0x3F; if (sprite_color == 0x3E || sprite_color == 0x3F) { @@ -765,8 +771,10 @@ void render_map_output(uint32_t line, int32_t col, vdp_context * context) //*dst = (context->cram[pixel & 0x3F] & 0xEEE) | ((pixel & BUF_BIT_PRIORITY) ? FBUF_BIT_PRIORITY : 0) | src; } } else { - for (int i = 0; i < 16; ++plane_a, ++plane_b, ++sprite_buf, ++i) { + for (int i = 0; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) { uint8_t pixel; + plane_a = context->tmp_buf_a + (plane_a_off & SCROLL_BUFFER_MASK); + plane_b = context->tmp_buf_b + (plane_b_off & SCROLL_BUFFER_MASK); if (*sprite_buf & BUF_BIT_PRIORITY && *sprite_buf & 0xF) { pixel = *sprite_buf; //src = FBUF_SRC_S; @@ -804,14 +812,8 @@ void render_map_output(uint32_t line, int32_t col, vdp_context * context) //plane_b = context->tmp_buf_b + 16 - (context->hscroll_b & 0x7); //end = dst + 8; } - - uint16_t remaining; - if (!(context->flags & FLAG_WINDOW)) { - remaining = context->hscroll_a & 0xF; - memcpy(context->tmp_buf_a + SCROLL_BUFFER_DRAW - remaining, context->tmp_buf_a + SCROLL_BUFFER_SIZE - remaining, remaining); - } - remaining = context->hscroll_b & 0xF; - memcpy(context->tmp_buf_b + SCROLL_BUFFER_DRAW - remaining, context->tmp_buf_b + SCROLL_BUFFER_SIZE - remaining, remaining); + context->buf_a_off = (context->buf_a_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK; + context->buf_b_off = (context->buf_b_off + SCROLL_BUFFER_DRAW) & SCROLL_BUFFER_MASK; } #define COLUMN_RENDER_BLOCK(column, startcyc) \ diff --git a/vdp.h b/vdp.h index c43a2d9..2cad1f3 100644 --- a/vdp.h +++ b/vdp.h @@ -147,6 +147,8 @@ typedef struct { uint8_t flags2; uint8_t double_res; uint8_t b32; + uint8_t buf_a_off; + uint8_t buf_b_off; uint8_t *tmp_buf_a; uint8_t *tmp_buf_b; } vdp_context; -- cgit v1.2.3 From de1c3145d57e6efc3022012d26744f7a79e3cde2 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Mon, 15 Jul 2013 23:07:45 -0700 Subject: Restore one of the VDP debugging modes --- io.c | 2 +- vdp.c | 133 +++++++++++++++++++++++++++++++++++++++++++++++++----------------- vdp.h | 18 +++++---- 3 files changed, 111 insertions(+), 42 deletions(-) diff --git a/io.c b/io.c index a8296df..fa9e2b2 100644 --- a/io.c +++ b/io.c @@ -197,7 +197,7 @@ void handle_binding_up(keybinding * binding) if (ui_debug_mode == 4) { ui_debug_mode = 0; } - render_debug_mode(ui_debug_mode); + genesis->vdp->debug = ui_debug_mode; break; case UI_DEBUG_PAL_INC: ui_debug_pal++; diff --git a/vdp.c b/vdp.c index 265b006..0eb0655 100644 --- a/vdp.c +++ b/vdp.c @@ -31,6 +31,14 @@ int32_t color_map[1 << 12]; uint8_t levels[] = {0, 27, 49, 71, 87, 103, 119, 130, 146, 157, 174, 190, 206, 228, 255}; +uint8_t debug_base[][3] = { + {127, 127, 127}, //BG + {0, 0, 127}, //A + {127, 0, 0}, //Window + {0, 127, 0}, //B + {127, 0, 127} //Sprites +}; + uint8_t color_map_init_done; void init_vdp_context(vdp_context * context) @@ -70,6 +78,47 @@ void init_vdp_context(vdp_context * context) } color_map_init_done = 1; } + for (uint8_t color = 0; color < (1 << (3 + 1 + 1 + 1)); color++) + { + uint8_t src = color & DBG_SRC_MASK; + if (src > DBG_SRC_S) { + context->debugcolors[color] = 0; + } else { + uint8_t r,g,b; + b = debug_base[src][0]; + g = debug_base[src][1]; + r = debug_base[src][2]; + if (color & DBG_PRIORITY) + { + if (b) { + b += 48; + } + if (g) { + g += 48; + } + if (r) { + r += 48; + } + } + if (color & DBG_SHADOW) { + b /= 2; + g /= 2; + r /=2 ; + } + if (color & DBG_HILIGHT) { + if (b) { + b += 72; + } + if (g) { + g += 72; + } + if (r) { + r += 72; + } + } + context->debugcolors[color] = render_map_color(r, g, b); + } + } } void render_sprite_cells(vdp_context * context) @@ -687,16 +736,15 @@ void render_map_output(uint32_t line, int32_t col, vdp_context * context) dst += line * 320 + col * 8; } sprite_buf = context->linebuf + col * 8; - uint16_t a_src; + uint8_t a_src, src; if (context->flags & FLAG_WINDOW) { plane_a_off = context->buf_a_off; - //a_src = FBUF_SRC_W; + a_src = DBG_SRC_W; } else { plane_a_off = context->buf_a_off - (context->hscroll_a & 0xF); - //a_src = FBUF_SRC_A; + a_src = DBG_SRC_A; } plane_b_off = context->buf_b_off - (context->hscroll_b & 0xF); - uint16_t src; //printf("A | tmp_buf offset: %d\n", 8 - (context->hscroll_a & 0x7)); if (context->regs[REG_MODE_4] & BIT_HILIGHT) { @@ -704,103 +752,120 @@ void render_map_output(uint32_t line, int32_t col, vdp_context * context) uint8_t pixel; plane_a = context->tmp_buf_a + (plane_a_off & SCROLL_BUFFER_MASK); plane_b = context->tmp_buf_b + (plane_b_off & SCROLL_BUFFER_MASK); + uint32_t * colors = context->colors; src = 0; uint8_t sprite_color = *sprite_buf & 0x3F; if (sprite_color == 0x3E || sprite_color == 0x3F) { if (sprite_color == 0x3F) { - src = CRAM_SIZE;//FBUF_SHADOW; + colors += CRAM_SIZE; + src = DBG_SHADOW; } else { - src = CRAM_SIZE*2;//FBUF_HILIGHT; + colors += CRAM_SIZE*2; + src = DBG_HILIGHT; } if (*plane_a & BUF_BIT_PRIORITY && *plane_a & 0xF) { pixel = *plane_a; - //src |= a_src; + src |= a_src; } else if (*plane_b & BUF_BIT_PRIORITY && *plane_b & 0xF) { pixel = *plane_b; - //src |= FBUF_SRC_B; + src |= DBG_SRC_B; } else if (*plane_a & 0xF) { pixel = *plane_a; - //src |= a_src; + src |= a_src; } else if (*plane_b & 0xF){ pixel = *plane_b; - //src |= FBUF_SRC_B; + src |= DBG_SRC_B; } else { pixel = context->regs[REG_BG_COLOR] & 0x3F; - //src |= FBUF_SRC_BG; + src |= DBG_SRC_BG; } } else { if (*sprite_buf & BUF_BIT_PRIORITY && *sprite_buf & 0xF) { pixel = *sprite_buf; - //src = FBUF_SRC_S; + src = DBG_SRC_S; } else if (*plane_a & BUF_BIT_PRIORITY && *plane_a & 0xF) { pixel = *plane_a; - //src = a_src; + src = a_src; } else if (*plane_b & BUF_BIT_PRIORITY && *plane_b & 0xF) { pixel = *plane_b; - //src = FBUF_SRC_B; + src = DBG_SRC_B; } else { if (!(*plane_a & BUF_BIT_PRIORITY || *plane_a & BUF_BIT_PRIORITY)) { - src = CRAM_SIZE;//FBUF_SHADOW; + colors += CRAM_SIZE; + src = DBG_SHADOW; } if (*sprite_buf & 0xF) { pixel = *sprite_buf; if (*sprite_buf & 0xF == 0xE) { - src = 0;//FBUF_SRC_S; - } /*else { - src |= FBUF_SRC_S; - }*/ + colors = context->colors; + src = DBG_SRC_S; + } else { + src |= DBG_SRC_S; + } } else if (*plane_a & 0xF) { pixel = *plane_a; - //src |= a_src; + src |= a_src; } else if (*plane_b & 0xF){ pixel = *plane_b; - //src |= FBUF_SRC_B; + src |= DBG_SRC_B; } else { pixel = context->regs[REG_BG_COLOR] & 0x3F; - //src |= FBUF_SRC_BG; + src |= DBG_SRC_BG; } } } pixel &= 0x3F; - pixel += src; + uint32_t outpixel; + if (context->debug) { + outpixel = context->debugcolors[src]; + } else { + outpixel = colors[pixel]; + } if (context->b32) { - *(dst32++) = context->colors[pixel]; + *(dst32++) = outpixel; } else { - *(dst++) = context->colors[pixel]; + *(dst++) = outpixel; } //*dst = (context->cram[pixel & 0x3F] & 0xEEE) | ((pixel & BUF_BIT_PRIORITY) ? FBUF_BIT_PRIORITY : 0) | src; } } else { for (int i = 0; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) { uint8_t pixel; + src = 0; plane_a = context->tmp_buf_a + (plane_a_off & SCROLL_BUFFER_MASK); plane_b = context->tmp_buf_b + (plane_b_off & SCROLL_BUFFER_MASK); if (*sprite_buf & BUF_BIT_PRIORITY && *sprite_buf & 0xF) { pixel = *sprite_buf; - //src = FBUF_SRC_S; + src = DBG_SRC_S; } else if (*plane_a & BUF_BIT_PRIORITY && *plane_a & 0xF) { pixel = *plane_a; - //src = a_src; + src = a_src; } else if (*plane_b & BUF_BIT_PRIORITY && *plane_b & 0xF) { pixel = *plane_b; - //src = FBUF_SRC_B; + src = DBG_SRC_B; } else if (*sprite_buf & 0xF) { pixel = *sprite_buf; - //src = FBUF_SRC_S; + src = DBG_SRC_S; } else if (*plane_a & 0xF) { pixel = *plane_a; - //src = a_src; + src = a_src; } else if (*plane_b & 0xF){ pixel = *plane_b; - //src = FBUF_SRC_B; + src = DBG_SRC_B; } else { pixel = context->regs[REG_BG_COLOR] & 0x3F; - //src = FBUF_SRC_BG; + src = DBG_SRC_BG; + } + uint32_t outpixel; + if (context->debug) { + outpixel = context->debugcolors[src]; + } else { + outpixel = context->colors[pixel & 0x3F]; } if (context->b32) { - *(dst32++) = context->colors[pixel & 0x3F]; + *(dst32++) = outpixel; } else { - *(dst++) = context->colors[pixel & 0x3F]; + *(dst++) = outpixel; } //*dst = (context->cram[pixel & 0x3F] & 0xEEE) | ((pixel & BUF_BIT_PRIORITY) ? FBUF_BIT_PRIORITY : 0) | src; } diff --git a/vdp.h b/vdp.h index 2cad1f3..97be57b 100644 --- a/vdp.h +++ b/vdp.h @@ -19,13 +19,15 @@ #define FBUF_SHADOW 0x0001 #define FBUF_HILIGHT 0x0010 -#define FBUF_BIT_PRIORITY 0x1000 -#define FBUF_SRC_MASK 0xE000 -#define FBUF_SRC_A 0x0000 -#define FBUF_SRC_W 0x2000 -#define FBUF_SRC_B 0x4000 -#define FBUF_SRC_S 0x6000 -#define FBUF_SRC_BG 0x8000 +#define DBG_SHADOW 0x10 +#define DBG_HILIGHT 0x20 +#define DBG_PRIORITY 0x8 +#define DBG_SRC_MASK 0x7 +#define DBG_SRC_A 0x1 +#define DBG_SRC_W 0x2 +#define DBG_SRC_B 0x3 +#define DBG_SRC_S 0x4 +#define DBG_SRC_BG 0x0 #define MCLKS_LINE 3420 @@ -128,6 +130,7 @@ typedef struct { void *evenbuf; uint16_t cram[CRAM_SIZE]; uint32_t colors[CRAM_SIZE*3]; + uint32_t debugcolors[1 << (3 + 1 + 1 + 1)];//3 bits for source, 1 bit for priority, 1 bit for shadow, 1 bit for hilight uint16_t vsram[VSRAM_SIZE]; uint8_t latched_mode; uint16_t hscroll_a; @@ -149,6 +152,7 @@ typedef struct { uint8_t b32; uint8_t buf_a_off; uint8_t buf_b_off; + uint8_t debug; uint8_t *tmp_buf_a; uint8_t *tmp_buf_b; } vdp_context; -- cgit v1.2.3 From 079a2c40b940e7ed09aa727effb2981a1eed4839 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Tue, 16 Jul 2013 23:16:14 -0700 Subject: Add address/cd registers to VDP debug message --- vdp.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/vdp.c b/vdp.c index 0eb0655..7154548 100644 --- a/vdp.c +++ b/vdp.c @@ -207,6 +207,11 @@ void vdp_print_reg_explain(vdp_context * context) context->regs[REG_HINT], context->regs[REG_HINT], context->regs[REG_AUTOINC], context->regs[REG_AUTOINC], context->regs[REG_SCROLL], sizes[context->regs[REG_SCROLL] & 0x3], sizes[context->regs[REG_SCROLL] >> 4 & 0x3]); + printf("\n**Internal Group**\n" + "Address: %X\n" + "CD: %X\n" + "Pending: %s\n", + context->address, context->cd, (context->flags & FLAG_PENDING) ? "true" : "false"); //TODO: Window Group, DMA Group } -- cgit v1.2.3 From ee8dd1c801e8aba14b1c0edafd18c5305d05154e Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Tue, 16 Jul 2013 23:16:38 -0700 Subject: Add support for single operand instructions to 68K test generator --- gentests.py | 41 ++++++++++++++++++++++++++++++++++------- testcases.txt | 17 +++++++++-------- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/gentests.py b/gentests.py index edfb55b..000f73e 100755 --- a/gentests.py +++ b/gentests.py @@ -72,6 +72,16 @@ class Program(object): def get_dreg(self): return Register('d', self.avail_dregs.pop()) +class Dummy(object): + def __str__(self): + return '' + def write_init(self, outfile, size, already): + pass + def consume_regs(self, program): + pass + +dummy_op = Dummy() + class Register(object): def __init__(self, kind, num): self.kind = kind @@ -441,30 +451,47 @@ class Inst2Op(object): self.src.consume_regs(program) self.dst.consume_regs(program) +class Inst1Op(Inst2Op): + def __init__(self, name, size, dst): + super(Inst1Op, self).__init__(name, size, dummy_op, dst) + + def __str__(self): + return self.name + '.' + self.size + ' ' + str(self.dst) + class Entry(object): def __init__(self, line): fields = split_fields(line) self.name = fields[0] sizes = fields[1] sources = fields[2].split(';') - dests = fields[3].split(';') + if len(fields) > 3: + dests = fields[3].split(';') + else: + dests = None combos = [] for size in sizes: for source in sources: if size != 'b' or source != 'a': - for dest in dests: - if size != 'b' or dest != 'a': - combos.append((size, source, dest)) + if dests: + for dest in dests: + if size != 'b' or dest != 'a': + combos.append((size, source, dest)) + else: + combos.append((size, None, source)) self.cases = combos def programs(self): res = [] for (size, src, dst) in self.cases: - sources = get_variations(src, size) dests = get_variations(dst, size) - for source in sources: + if src: + sources = get_variations(src, size) + for source in sources: + for dest in dests: + res.append(Program(Inst2Op(self.name, size, source, dest))) + else: for dest in dests: - res.append(Program(Inst2Op(self.name, size, source, dest))) + res.append(Program(Inst1Op(self.name, size, dest))) return res def process_entries(f): diff --git a/testcases.txt b/testcases.txt index b9b92fd..bf988de 100644 --- a/testcases.txt +++ b/testcases.txt @@ -48,12 +48,13 @@ Name Sizes Src Modes Dst Modes #cmpa wl d;a;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l;#n;(n,pc);(n,pc,x) a #cmpi bwl #n d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l #cmpm bwl (a)+ (a)+ -eor bwl d d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l -eori bwl #n d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l -exg l d d;a -exg l a a -link w a #n -or bwl d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l;#n;(n,pc);(n,pc,x) d -or bwl d (a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l -ori bwl #n d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#eor bwl d d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#eori bwl #n d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#exg l d d;a +#exg l a a +#link w a #n +#or bwl d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l;#n;(n,pc);(n,pc,x) d +#or bwl d (a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#ori bwl #n d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +clr bwl d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l -- cgit v1.2.3 From b1c5ca7410c3eb512cf51e1f4d0be0ffdcd24ffd Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Tue, 16 Jul 2013 23:16:50 -0700 Subject: Fix 68K test harness --- comparetests.py | 2 +- m68k_to_x86.c | 2 +- trans.c | 34 +++++++++++++++++++++++++++------- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/comparetests.py b/comparetests.py index 5156895..ce62ae2 100755 --- a/comparetests.py +++ b/comparetests.py @@ -31,7 +31,7 @@ for path in glob('generated_tests/*/*.bin'): if not good: continue try: - b = subprocess.check_output(['./blastem', path, '-v']) + b = subprocess.check_output(['./trans', path]) try: m = subprocess.check_output(['musashi/mustrans', path]) #_,_,b = b.partition('\n') diff --git a/m68k_to_x86.c b/m68k_to_x86.c index 6ebc96d..f7e4d02 100644 --- a/m68k_to_x86.c +++ b/m68k_to_x86.c @@ -4333,7 +4333,7 @@ uint8_t * gen_mem_fun(x86_68k_options * opts, memmap_chunk * memmap, uint32_t nu void init_x86_68k_opts(x86_68k_options * opts, memmap_chunk * memmap, uint32_t num_chunks) { - opts->flags = 0; + memset(opts, 0, sizeof(*opts)); for (int i = 0; i < 8; i++) opts->dregs[i] = opts->aregs[i] = -1; opts->dregs[0] = R10; diff --git a/trans.c b/trans.c index 357a5f7..33f4dd5 100644 --- a/trans.c +++ b/trans.c @@ -3,6 +3,15 @@ #include "mem.h" #include #include +#include + +m68k_context * sync_components(m68k_context * context, uint32_t address) +{ + if (context->current_cycle > 0x80000000) { + context->current_cycle -= 0x80000000; + } + return context; +} int main(int argc, char ** argv) { @@ -16,23 +25,34 @@ int main(int argc, char ** argv) fseek(f, 0, SEEK_END); filesize = ftell(f); fseek(f, 0, SEEK_SET); - filebuf = malloc(filesize); + filebuf = malloc(filesize > 0x400000 ? filesize : 0x400000); fread(filebuf, 2, filesize/2, f); fclose(f); for(cur = filebuf; cur - filebuf < (filesize/2); ++cur) { *cur = (*cur >> 8) | (*cur << 8); } - init_x86_68k_opts(&opts); + memmap_chunk memmap[2]; + memset(memmap, 0, sizeof(memmap_chunk)*2); + memmap[0].end = 0x400000; + memmap[0].mask = 0xFFFFFF; + memmap[0].flags = MMAP_READ; + memmap[0].buffer = filebuf; + + memmap[1].start = 0xE00000; + memmap[1].end = 0x1000000; + memmap[1].mask = 0xFFFF; + memmap[1].flags = MMAP_READ | MMAP_WRITE | MMAP_CODE; + memmap[1].buffer = malloc(64 * 1024); + init_x86_68k_opts(&opts, memmap, 2); init_68k_context(&context, opts.native_code_map, &opts); - //cartridge ROM - context.mem_pointers[0] = filebuf; - context.target_cycle = 0x7FFFFFFF; - //work RAM - context.mem_pointers[1] = malloc(64 * 1024); + context.mem_pointers[0] = memmap[0].buffer; + context.mem_pointers[1] = memmap[1].buffer; + context.target_cycle = context.sync_cycle = 0x80000000; uint32_t address; address = filebuf[2] << 16 | filebuf[3]; translate_m68k_stream(address, &context); m68k_reset(&context); return 0; } + -- cgit v1.2.3 From 04322d3657b54684db1c5650b03561a06184fca4 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Wed, 17 Jul 2013 00:17:42 -0700 Subject: Add a bunch of 68K test cases for single operand instructions --- testcases.txt | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/testcases.txt b/testcases.txt index bf988de..b7f7d34 100644 --- a/testcases.txt +++ b/testcases.txt @@ -56,5 +56,32 @@ Name Sizes Src Modes Dst Modes #or bwl d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l;#n;(n,pc);(n,pc,x) d #or bwl d (a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l #ori bwl #n d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l -clr bwl d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#clr bwl d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#ext wl d +#neg bwl d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#negx bwl d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#not bwl d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#pea l (a);(n,a);(n,a,x);(n).w;(n).l;(n,pc);(n,pc,x) +#rol w (a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#ror w (a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#roxl w (a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#roxr w (a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#st b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#sf b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#shi b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#sls b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#scc b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#scs b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#sne b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#seq b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#svc b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#svs b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#spl b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#smi b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#sge b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#slt b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#sgt b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#sle b d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l +#swap w d +tst bwl d;(a);(a)+;-(a);(n,a);(n,a,x);(n).w;(n).l -- cgit v1.2.3 From e077eae365233923860becc64cec4b990f7f0b88 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Wed, 17 Jul 2013 00:18:28 -0700 Subject: Fix carry flag on rotate when the register provided rotate bit count is exactly 32 --- m68k_to_x86.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/m68k_to_x86.c b/m68k_to_x86.c index f7e4d02..a5cdd57 100644 --- a/m68k_to_x86.c +++ b/m68k_to_x86.c @@ -3557,6 +3557,7 @@ uint8_t * translate_m68k(uint8_t * dst, m68kinst * inst, x86_68k_options * opts) dst = cmp_ir(dst, 32, SCRATCH1, SZ_B); norm_off = dst+1; dst = jcc(dst, CC_L, dst+2); + dst = sub_ir(dst, 32, SCRATCH1, SZ_B); if (dst_op.mode == MODE_REG_DIRECT) { if (inst->op == M68K_ROL) { dst = rol_ir(dst, 31, dst_op.base, inst->extra.size); @@ -3574,7 +3575,6 @@ uint8_t * translate_m68k(uint8_t * dst, m68kinst * inst, x86_68k_options * opts) dst = ror_irdisp8(dst, 1, dst_op.base, dst_op.disp, inst->extra.size); } } - dst = sub_ir(dst, 32, SCRATCH1, SZ_B); *norm_off = dst - (norm_off+1); if (dst_op.mode == MODE_REG_DIRECT) { if (inst->op == M68K_ROL) { -- cgit v1.2.3 From 1482ef3c532eca86baf1d81a02822ff0962cedf8 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Wed, 17 Jul 2013 00:23:45 -0700 Subject: Fix sign flag on swap --- m68k_to_x86.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/m68k_to_x86.c b/m68k_to_x86.c index a5cdd57..9c475da 100644 --- a/m68k_to_x86.c +++ b/m68k_to_x86.c @@ -3840,9 +3840,12 @@ uint8_t * translate_m68k(uint8_t * dst, m68kinst * inst, x86_68k_options * opts) dst = cycles(dst, BUS); if (src_op.mode == MODE_REG_DIRECT) { dst = rol_ir(dst, 16, src_op.base, SZ_D); + dst = cmp_ir(dst, 0, src_op.base, SZ_D); } else{ dst = rol_irdisp8(dst, 16, src_op.base, src_op.disp, SZ_D); + dst = cmp_irdisp8(dst, 0, src_op.base, src_op.disp, SZ_D); } + dst = mov_ir(dst, 0, FLAG_C, SZ_B); dst = setcc_r(dst, CC_Z, FLAG_Z); dst = setcc_r(dst, CC_S, FLAG_N); -- cgit v1.2.3 From b94c9fd1cd3731c9a0bdcabcb15e7bead85021dc Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Wed, 17 Jul 2013 22:26:11 -0700 Subject: Add fullscreen support and add a keybinding for exiting the emulator --- blastem.c | 5 +++-- default.cfg | 1 + io.c | 8 +++++++- render.h | 3 ++- render_sdl.c | 13 ++++++++++--- 5 files changed, 23 insertions(+), 7 deletions(-) diff --git a/blastem.c b/blastem.c index 0e5e547..d5d8720 100644 --- a/blastem.c +++ b/blastem.c @@ -1907,6 +1907,7 @@ int main(int argc, char ** argv) int ym_log = 0; FILE *address_log = NULL; char * statefile = NULL; + uint8_t fullscreen = 0; for (int i = 2; i < argc; i++) { if (argv[i][0] == '-') { switch(argv[i][1]) { @@ -1914,7 +1915,7 @@ int main(int argc, char ** argv) debug = 1; break; case 'f': - frame_limit = 1; + fullscreen = 1; break; case 'l': address_log = fopen("address.log", "w"); @@ -1988,7 +1989,7 @@ int main(int argc, char ** argv) fps = 50; } if (!headless) { - render_init(width, height, title, fps); + render_init(width, height, title, fps, fullscreen); } vdp_context v_context; diff --git a/default.cfg b/default.cfg index d2cdde8..1bfcd7d 100644 --- a/default.cfg +++ b/default.cfg @@ -17,6 +17,7 @@ bindings { [ ui.vdp_debug_mode ] ui.vdp_debug_pal u ui.enter_debugger + esc ui.exit } pads { 0 { diff --git a/io.c b/io.c index fa9e2b2..b277c3c 100644 --- a/io.c +++ b/io.c @@ -12,7 +12,8 @@ enum { typedef enum { UI_DEBUG_MODE_INC, UI_DEBUG_PAL_INC, - UI_ENTER_DEBUGGER + UI_ENTER_DEBUGGER, + UI_EXIT } ui_action; typedef struct { @@ -209,6 +210,8 @@ void handle_binding_up(keybinding * binding) case UI_ENTER_DEBUGGER: break_on_sync = 1; break; + case UI_EXIT: + exit(0); } break; } @@ -280,6 +283,8 @@ int parse_binding_target(char * target, tern_node * padbuttons, int * ui_out, in *ui_out = UI_DEBUG_PAL_INC; } else if(!strcmp(target + 3, "enter_debugger")) { *ui_out = UI_ENTER_DEBUGGER; + } else if(!strcmp(target + 3, "exit")) { + *ui_out = UI_EXIT; } else { fprintf(stderr, "Unreconized UI binding type %s\n", target); return 0; @@ -339,6 +344,7 @@ void set_keybindings() special = tern_insert_int(special, "left", RENDERKEY_LEFT); special = tern_insert_int(special, "right", RENDERKEY_RIGHT); special = tern_insert_int(special, "enter", '\r'); + special = tern_insert_int(special, "esc", RENDERKEY_ESC); tern_node * padbuttons = tern_insert_int(NULL, ".up", DPAD_UP); padbuttons = tern_insert_int(padbuttons, ".down", DPAD_DOWN); diff --git a/render.h b/render.h index 48f22b4..fb3e596 100644 --- a/render.h +++ b/render.h @@ -6,7 +6,7 @@ #include "ym2612.h" uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b); uint8_t render_depth(); -void render_init(int width, int height, char * title, uint32_t fps); +void render_init(int width, int height, char * title, uint32_t fps, uint8_t fullscreen); void render_context(vdp_context * context); void render_wait_quit(vdp_context * context); void render_wait_psg(psg_context * context); @@ -28,6 +28,7 @@ int render_num_joysticks(); #define RENDERKEY_DOWN SDLK_DOWN #define RENDERKEY_LEFT SDLK_LEFT #define RENDERKEY_RIGHT SDLK_RIGHT +#define RENDERKEY_ESC SDLK_ESCAPE #define RENDER_DPAD_UP SDL_HAT_UP #define RENDER_DPAD_DOWN SDL_HAT_DOWN #define RENDER_DPAD_LEFT SDL_HAT_LEFT diff --git a/render_sdl.c b/render_sdl.c index 6148c96..bde6825 100644 --- a/render_sdl.c +++ b/render_sdl.c @@ -88,7 +88,7 @@ uint8_t render_depth() return screen->format->BytesPerPixel * 8; } -void render_init(int width, int height, char * title, uint32_t fps) +void render_init(int width, int height, char * title, uint32_t fps, uint8_t fullscreen) { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0) { fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError()); @@ -97,7 +97,13 @@ void render_init(int width, int height, char * title, uint32_t fps) atexit(SDL_Quit); atexit(render_close_audio); printf("width: %d, height: %d\n", width, height); - screen = SDL_SetVideoMode(width, height, 32, SDL_SWSURFACE | SDL_ANYFORMAT); + uint32_t flags = SDL_ANYFORMAT; + if (fullscreen) { + flags |= SDL_FULLSCREEN | SDL_HWSURFACE | SDL_DOUBLEBUF; + } else { + flags |= SDL_SWSURFACE; + } + screen = SDL_SetVideoMode(width, height, 32, flags); if (!screen) { fprintf(stderr, "Unable to get SDL surface: %s\n", SDL_GetError()); exit(1); @@ -216,7 +222,8 @@ void render_context(vdp_context * context) if ( SDL_MUSTLOCK(screen) ) { SDL_UnlockSurface(screen); } - SDL_UpdateRect(screen, 0, 0, screen->clip_rect.w, screen->clip_rect.h); + //SDL_UpdateRect(screen, 0, 0, screen->clip_rect.w, screen->clip_rect.h); + SDL_Flip(screen); if (context->regs[REG_MODE_4] & BIT_INTERLACE) { context->framebuf = context->framebuf == context->oddbuf ? context->evenbuf : context->oddbuf; -- cgit v1.2.3 From ac8549baa9e68eae2ecf8551f8dc6e1a0ba99ce4 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Thu, 18 Jul 2013 09:59:39 -0700 Subject: Add config values for audio sample rate and buffer size --- default.cfg | 5 +++++ render_sdl.c | 15 +++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/default.cfg b/default.cfg index 1bfcd7d..ba4559e 100644 --- a/default.cfg +++ b/default.cfg @@ -47,5 +47,10 @@ video { width 640 } +audio { + rate 48000 + buffer 512 +} + default_region U diff --git a/render_sdl.c b/render_sdl.c index bde6825..408c821 100644 --- a/render_sdl.c +++ b/render_sdl.c @@ -135,10 +135,21 @@ void render_init(int width, int height, char * title, uint32_t fps, uint8_t full audio_ready = SDL_CreateCond(); SDL_AudioSpec desired, actual; - desired.freq = 48000; + char * rate_str = tern_find_ptr(config, "audiorate"); + int rate = rate_str ? atoi(rate_str) : 0; + if (!rate) { + rate = 48000; + } + desired.freq = rate; desired.format = AUDIO_S16SYS; desired.channels = 2; - desired.samples = 2048;//1024; + char * samples_str = tern_find_ptr(config, "audiobuffer"); + int samples = samples_str ? atoi(samples_str) : 0; + if (!samples) { + samples = 512; + } + printf("config says: %d\n", samples); + desired.samples = samples*2; desired.callback = audio_callback; desired.userdata = NULL; -- cgit v1.2.3 From 88d08dacd5f844767933be389d3668a7ffa73ef3 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Fri, 19 Jul 2013 22:44:00 -0700 Subject: Implement 68K stop instruction --- blastem.c | 6 ++++++ m68k_to_x86.c | 26 ++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/blastem.c b/blastem.c index d5d8720..819071b 100644 --- a/blastem.c +++ b/blastem.c @@ -258,6 +258,12 @@ m68k_context * sync_components(m68k_context * context, uint32_t address) context->int_ack = 0; } adjust_int_cycle(context, v_context); + if (context->current_cycle <= context->sync_cycle) { + context->sync_cycle = context->current_cycle + 4; + if (context->sync_cycle < context->int_cycle) { + context->target_cycle = context->sync_cycle; + } + } if (break_on_sync && address) { break_on_sync = 0; debugger(context, address); diff --git a/m68k_to_x86.c b/m68k_to_x86.c index 9c475da..d32bd6f 100644 --- a/m68k_to_x86.c +++ b/m68k_to_x86.c @@ -3781,8 +3781,30 @@ uint8_t * translate_m68k(uint8_t * dst, m68kinst * inst, x86_68k_options * opts) } dst = m68k_save_result(inst, dst, opts); break; - /*case M68K_STOP: - break;*/ + case M68K_STOP: { + //TODO: Trap if not in system mode + //manual says 4 cycles, but it has to be at least 8 since it's a 2-word instruction + //possibly even 12 since that's how long MOVE to SR takes + dst = cycles(dst, BUS*2); + dst = mov_ir(dst, src_op.disp & 0x1, FLAG_C, SZ_B); + dst = mov_ir(dst, (src_op.disp >> 1) & 0x1, FLAG_V, SZ_B); + dst = mov_ir(dst, (src_op.disp >> 2) & 0x1, FLAG_Z, SZ_B); + dst = mov_ir(dst, (src_op.disp >> 3) & 0x1, FLAG_N, SZ_B); + dst = mov_irind(dst, (src_op.disp >> 4) & 0x1, CONTEXT, SZ_B); + dst = mov_irdisp8(dst, (src_op.disp >> 8), CONTEXT, offsetof(m68k_context, status), SZ_B); + if (!((inst->src.params.immed >> 8) & (1 << BIT_SUPERVISOR))) { + //leave supervisor mode + dst = mov_rr(dst, opts->aregs[7], SCRATCH1, SZ_D); + dst = mov_rdisp8r(dst, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, opts->aregs[7], SZ_D); + dst = mov_rrdisp8(dst, SCRATCH1, CONTEXT, offsetof(m68k_context, aregs) + sizeof(uint32_t) * 8, SZ_D); + } + uint8_t * loop_top = dst; + dst = call(dst, (uint8_t *)do_sync); + dst = mov_rr(dst, LIMIT, CYCLES, SZ_D); + dst = cmp_rdisp8r(dst, CONTEXT, offsetof(m68k_context, int_cycle), CYCLES, SZ_D); + dst = jcc(dst, CC_C, loop_top); + break; + } case M68K_SUB: size = inst->dst.addr_mode == MODE_AREG ? OPSIZE_LONG : inst->extra.size; dst = cycles(dst, BUS); -- cgit v1.2.3 From 7806b1cde3629889344bda468d05542b329b2f6e Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Sat, 20 Jul 2013 23:40:28 -0700 Subject: Fix performance regression from stop instruction work --- blastem.c | 54 ++++++++++++++++++++------------------------- m68k_to_x86.c | 70 +++++++++++++++++++++++++++++++++-------------------------- 2 files changed, 63 insertions(+), 61 deletions(-) diff --git a/blastem.c b/blastem.c index 819071b..a4afa0b 100644 --- a/blastem.c +++ b/blastem.c @@ -52,7 +52,7 @@ int load_smd_rom(long filesize, FILE * f) uint8_t block[SMD_BLOCK_SIZE]; filesize -= SMD_HEADER_SIZE; fseek(f, SMD_HEADER_SIZE, SEEK_SET); - + uint16_t * dst = cart; while (filesize > 0) { fread(block, 1, SMD_BLOCK_SIZE, f); @@ -139,15 +139,15 @@ void adjust_int_cycle(m68k_context * context, vdp_context * v_context) if (next_hint < context->int_cycle) { context->int_cycle = next_hint; context->int_num = 4; - + } } } } context->target_cycle = context->int_cycle < context->sync_cycle ? context->int_cycle : context->sync_cycle; - /*printf("Cyc: %d, Trgt: %d, Int Cyc: %d, Int: %d, Mask: %X, V: %d, H: %d, HICount: %d, HReg: %d, Line: %d\n", - context->current_cycle, context->target_cycle, context->int_cycle, context->int_num, (context->status & 0x7), + /*printf("Cyc: %d, Trgt: %d, Int Cyc: %d, Int: %d, Mask: %X, V: %d, H: %d, HICount: %d, HReg: %d, Line: %d\n", + context->current_cycle, context->target_cycle, context->int_cycle, context->int_num, (context->status & 0x7), v_context->regs[REG_MODE_2] & 0x20, v_context->regs[REG_MODE_1] & 0x10, v_context->hint_counter, v_context->regs[REG_HINT], v_context->cycles / MCLKS_LINE);*/ } @@ -199,7 +199,7 @@ void sync_sound(genesis_context * gen, uint32_t target) //printf("YM | Cycle: %d, bpos: %d, PSG | Cycle: %d, bpos: %d\n", gen->ym->current_cycle, gen->ym->buffer_pos, gen->psg->cycles, gen->psg->buffer_pos * 2); psg_run(gen->psg, target); ym_run(gen->ym, target); - + //printf("Target: %d, YM bufferpos: %d, PSG bufferpos: %d\n", target, gen->ym->buffer_pos, gen->psg->buffer_pos * 2); } @@ -221,7 +221,7 @@ m68k_context * sync_components(m68k_context * context, uint32_t address) } //printf("reached frame end | 68K Cycles: %d, MCLK Cycles: %d\n", context->current_cycle, mclks); vdp_run_context(v_context, mclks_per_frame); - + if (!headless) { break_on_sync |= wait_render_frame(v_context, frame_limit); } @@ -258,12 +258,6 @@ m68k_context * sync_components(m68k_context * context, uint32_t address) context->int_ack = 0; } adjust_int_cycle(context, v_context); - if (context->current_cycle <= context->sync_cycle) { - context->sync_cycle = context->current_cycle + 4; - if (context->sync_cycle < context->int_cycle) { - context->target_cycle = context->sync_cycle; - } - } if (break_on_sync && address) { break_on_sync = 0; debugger(context, address); @@ -504,7 +498,7 @@ m68k_context * io_write(uint32_t location, m68k_context * context, uint8_t value } if (value & 1) { dputs("bus requesting Z80"); - + if(!reset && !busreq) { busack_cycle = ((gen->z80->current_cycle + Z80_ACK_DELAY) * MCLKS_PER_Z80) / MCLKS_PER_68K;//context->current_cycle + Z80_ACK_DELAY; new_busack = Z80_REQ_ACK; @@ -526,7 +520,7 @@ m68k_context * io_write(uint32_t location, m68k_context * context, uint8_t value } //busack_cycle = CYCLE_NEVER; //busack = Z80_REQ_BUSY; - + } } else if (location == 0x1200) { sync_z80(gen->z80, context->current_cycle * MCLKS_PER_68K); @@ -1441,7 +1435,7 @@ m68k_context * debugger(m68k_context * context, uint32_t address) //Z80 debug commands switch(input_buf[1]) { - case 'b': + case 'b': param = find_param(input_buf); if (!param) { fputs("zb command requires a parameter\n", stderr); @@ -1560,7 +1554,7 @@ uint8_t z80_load_gst(z80_context * context, FILE * gstfile) context->flags[ZF_Z] = f & 1; f >>= 1; context->flags[ZF_S] = f; - + context->regs[Z80_A] = *curpos; curpos += 3; for (int reg = Z80_C; reg <= Z80_IYH; reg++) { @@ -1648,7 +1642,7 @@ uint32_t load_gst(genesis_context * gen, char * fname) adjust_int_cycle(gen->m68k, gen->vdp); fclose(gstfile); return pc; - + error_close: fclose(gstfile); error: @@ -1666,7 +1660,7 @@ error: const memmap_chunk static_map[] = { {0, 0x400000, 0xFFFFFF, 0, MMAP_READ, cart, NULL, NULL, NULL, NULL}, - {0xE00000, 0x1000000, 0xFFFF, 0, MMAP_READ | MMAP_WRITE | MMAP_CODE, ram, + {0xE00000, 0x1000000, 0xFFFF, 0, MMAP_READ | MMAP_WRITE | MMAP_CODE, ram, NULL, NULL, NULL, NULL}, {0xC00000, 0xE00000, 0x1FFFFF, 0, 0, NULL, (read_16_fun)vdp_port_read, (write_16_fun)vdp_port_write, @@ -1719,7 +1713,7 @@ void init_run_cpu(genesis_context * gen, int debug, FILE * address_log, char * s memmap[0].mask = 0xFFFFFF; memmap[0].flags = MMAP_READ; memmap[0].buffer = cart; - + ram_start &= 0xFFFFFE; ram_end |= 1; memmap[1].start = ram_start; @@ -1736,7 +1730,7 @@ void init_run_cpu(genesis_context * gen, int debug, FILE * address_log, char * s 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 { @@ -1745,7 +1739,7 @@ void init_run_cpu(genesis_context * gen, int debug, FILE * address_log, char * s 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; @@ -1765,7 +1759,7 @@ void init_run_cpu(genesis_context * gen, int debug, FILE * address_log, char * s 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; + memmap[num_chunks].write_8 = (write_8_fun)write_bank_reg_b; num_chunks++; ram_end++; size = ram_end-ram_start; @@ -1794,7 +1788,7 @@ void init_run_cpu(genesis_context * gen, int debug, FILE * address_log, char * s init_x86_68k_opts(&opts, memmap, num_chunks); opts.address_log = address_log; init_68k_context(&context, opts.native_code_map, &opts); - + context.video_context = gen->vdp; context.system = gen; //cartridge ROM @@ -1998,15 +1992,15 @@ int main(int argc, char ** argv) render_init(width, height, title, fps, fullscreen); } vdp_context v_context; - + init_vdp_context(&v_context); - + ym2612_context y_context; ym_init(&y_context, render_sample_rate(), fps == 60 ? MCLKS_NTSC : MCLKS_PAL, MCLKS_PER_YM, render_audio_buffer(), ym_log ? YM_OPT_WAVE_LOG : 0); - + psg_context p_context; psg_init(&p_context, render_sample_rate(), fps == 60 ? MCLKS_NTSC : MCLKS_PAL, MCLKS_PER_PSG, render_audio_buffer()); - + z80_context z_context; x86_z80_options z_opts; init_x86_z80_opts(&z_opts); @@ -2020,13 +2014,13 @@ int main(int argc, char ** argv) z_context.sync_cycle = z_context.target_cycle = mclks_per_frame/MCLKS_PER_Z80; z_context.int_cycle = CYCLE_NEVER; z_context.mem_pointers[1] = z_context.mem_pointers[2] = (uint8_t *)cart; - + gen.z80 = &z_context; gen.vdp = &v_context; gen.ym = &y_context; gen.psg = &p_context; genesis = &gen; - + int fname_size = strlen(argv[1]); sram_filename = malloc(fname_size+6); memcpy(sram_filename, argv[1], fname_size); @@ -2041,7 +2035,7 @@ int main(int argc, char ** argv) strcpy(sram_filename + fname_size, ".sram"); } set_keybindings(); - + init_run_cpu(&gen, debug, address_log, statefile); return 0; } diff --git a/m68k_to_x86.c b/m68k_to_x86.c index d32bd6f..785dac3 100644 --- a/m68k_to_x86.c +++ b/m68k_to_x86.c @@ -123,9 +123,9 @@ uint8_t * translate_m68k_src(m68kinst * inst, x86_ea * ea, uint8_t * out, x86_68 //We only get one memory parameter, so if the dst operand is a register in memory, //we need to copy this to a temp register first reg = native_reg(&(inst->dst), opts); - if (reg >= 0 || inst->dst.addr_mode == MODE_UNUSED || !(inst->dst.addr_mode == MODE_REG || inst->dst.addr_mode == MODE_AREG) + if (reg >= 0 || inst->dst.addr_mode == MODE_UNUSED || !(inst->dst.addr_mode == MODE_REG || inst->dst.addr_mode == MODE_AREG) || inst->op == M68K_EXG) { - + ea->mode = MODE_REG_DISPLACE8; ea->base = CONTEXT; ea->disp = reg_offset(&(inst->src)); @@ -150,7 +150,7 @@ uint8_t * translate_m68k_src(m68kinst * inst, x86_ea * ea, uint8_t * out, x86_68 out = sub_irdisp8(out, dec_amount, CONTEXT, reg_offset(&(inst->src)), SZ_D); } case MODE_AREG_INDIRECT: - case MODE_AREG_POSTINC: + case MODE_AREG_POSTINC: if (opts->aregs[inst->src.params.regs.pri] >= 0) { out = mov_rr(out, opts->aregs[inst->src.params.regs.pri], SCRATCH1, SZ_D); } else { @@ -168,7 +168,7 @@ uint8_t * translate_m68k_src(m68kinst * inst, x86_ea * ea, uint8_t * out, x86_68 out = call(out, opts->read_32); break; } - + if (inst->src.addr_mode == MODE_AREG_POSTINC) { inc_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : (inst->src.params.regs.pri == 7 ? 2 : 1)); if (opts->aregs[inst->src.params.regs.pri] >= 0) { @@ -441,7 +441,7 @@ uint8_t * translate_m68k_dst(m68kinst * inst, x86_ea * ea, uint8_t * out, x86_68 out = mov_rdisp8r(out, CONTEXT, reg_offset(&(inst->dst)), SCRATCH2, SZ_D); } } - + if (inst->dst.addr_mode == MODE_AREG_POSTINC) { inc_amount = inst->extra.size == OPSIZE_WORD ? 2 : (inst->extra.size == OPSIZE_LONG ? 4 : (inst->dst.params.regs.pri == 7 ? 2 : 1)); if (opts->aregs[inst->dst.params.regs.pri] >= 0) { @@ -781,7 +781,7 @@ uint8_t * translate_m68k_move(uint8_t * dst, m68kinst * inst, x86_68k_options * dst = mov_ir(dst, 0, FLAG_V, SZ_B); dst = mov_ir(dst, 0, FLAG_C, SZ_B); } - + if (inst->dst.addr_mode != MODE_AREG) { if (src.mode == MODE_REG_DIRECT) { flags_reg = src.base; @@ -2459,7 +2459,7 @@ uint8_t * translate_m68k_movep(uint8_t * dst, m68kinst * inst, x86_68k_options * dst = pop_r(dst, SCRATCH2); dst = mov_rr(dst, reg, SCRATCH1, SZ_D); dst = shr_ir(dst, 16, SCRATCH1, SZ_D); - + } else { dst = mov_rdisp8r(dst, CONTEXT, reg_offset(&(inst->src))+3, SCRATCH1, SZ_B); dst = push_r(dst, SCRATCH2); @@ -2527,7 +2527,7 @@ uint8_t * translate_m68k_movep(uint8_t * dst, m68kinst * inst, x86_68k_options * dst = push_r(dst, SCRATCH1); dst = call(dst, opts->read_8); if (reg >= 0) { - + dst = shl_ir(dst, 8, SCRATCH1, SZ_W); dst = mov_rr(dst, SCRATCH1, reg, SZ_W); dst = pop_r(dst, SCRATCH1); @@ -2628,7 +2628,7 @@ uint8_t * translate_shift(uint8_t * dst, m68kinst * inst, x86_ea *src_op, x86_ea } else { dst = mov_rdisp8r(dst, src_op->base, src_op->disp, RCX, SZ_B); } - + } dst = and_ir(dst, 63, RCX, SZ_D); nz_off = dst+1; @@ -2676,7 +2676,7 @@ uint8_t * translate_shift(uint8_t * dst, m68kinst * inst, x86_ea *src_op, x86_ea if (inst->extra.size == OPSIZE_LONG) { uint8_t * neq_32_off = dst + 1; dst = jcc(dst, CC_NZ, dst+2); - + //set the carry bit to the lsb if (dst_op->mode == MODE_REG_DIRECT) { dst = special(dst, 1, dst_op->base, SZ_D); @@ -2703,7 +2703,7 @@ uint8_t * translate_shift(uint8_t * dst, m68kinst * inst, x86_ea *src_op, x86_ea dst = shift_irdisp8(dst, 31, dst_op->base, dst_op->disp, inst->extra.size); dst = shift_irdisp8(dst, 1, dst_op->base, dst_op->disp, inst->extra.size); } - + } end_off = dst+1; dst = jmp(dst, dst+2); @@ -2715,7 +2715,7 @@ uint8_t * translate_shift(uint8_t * dst, m68kinst * inst, x86_ea *src_op, x86_ea } } } - + } if (!special && end_off) { *end_off = dst - (end_off + 1); @@ -3084,7 +3084,7 @@ uint8_t * translate_m68k(uint8_t * dst, m68kinst * inst, x86_68k_options * opts) default: isize = 2; } - uint8_t * passed = dst+1; + uint8_t * passed = dst+1; dst = jcc(dst, CC_GE, dst+2); dst = mov_ir(dst, 1, FLAG_N, SZ_B); dst = mov_ir(dst, VECTOR_CHK, SCRATCH2, SZ_D); @@ -3322,7 +3322,7 @@ uint8_t * translate_m68k(uint8_t * dst, m68kinst * inst, x86_68k_options * opts) } dst = call(dst, (uint8_t *)(inst->op == M68K_MOVE_SR ? set_sr : set_ccr)); dst = cycles(dst, 12); - + } break; case M68K_MOVE_USP: @@ -3446,7 +3446,7 @@ uint8_t * translate_m68k(uint8_t * dst, m68kinst * inst, x86_68k_options * opts) dst = not_rdisp8(dst, dst_op.base, dst_op.disp, inst->extra.size); dst = cmp_irdisp8(dst, 0, dst_op.base, dst_op.disp, inst->extra.size); } - + dst = mov_ir(dst, 0, FLAG_C, SZ_B); dst = setcc_r(dst, CC_Z, FLAG_Z); dst = setcc_r(dst, CC_S, FLAG_N); @@ -3800,7 +3800,15 @@ uint8_t * translate_m68k(uint8_t * dst, m68kinst * inst, x86_68k_options * opts) } uint8_t * loop_top = dst; dst = call(dst, (uint8_t *)do_sync); + dst = cmp_rr(dst, LIMIT, CYCLES, SZ_D); + uint8_t * normal_cycle_up = dst + 1; + dst = jcc(dst, CC_A, dst+2); + dst = cycles(dst, BUS); + uint8_t * after_cycle_up = dst + 1; + dst = jmp(dst, dst+2); + *normal_cycle_up = dst - (normal_cycle_up + 1); dst = mov_rr(dst, LIMIT, CYCLES, SZ_D); + *after_cycle_up = dst - (after_cycle_up+1); dst = cmp_rdisp8r(dst, CONTEXT, offsetof(m68k_context, int_cycle), CYCLES, SZ_D); dst = jcc(dst, CC_C, loop_top); break; @@ -3867,7 +3875,7 @@ uint8_t * translate_m68k(uint8_t * dst, m68kinst * inst, x86_68k_options * opts) dst = rol_irdisp8(dst, 16, src_op.base, src_op.disp, SZ_D); dst = cmp_irdisp8(dst, 0, src_op.base, src_op.disp, SZ_D); } - + dst = mov_ir(dst, 0, FLAG_C, SZ_B); dst = setcc_r(dst, CC_Z, FLAG_Z); dst = setcc_r(dst, CC_S, FLAG_N); @@ -3937,7 +3945,7 @@ uint8_t * translate_m68k_stream(uint32_t address, m68k_context * context) m68kinst instbuf; x86_68k_options * opts = context->options; uint8_t * dst = opts->cur_code; - uint8_t * dst_end = opts->code_end; + uint8_t * dst_end = opts->code_end; address &= 0xFFFFFF; if(get_native_address(opts->native_code_map, address)) { return dst; @@ -4065,7 +4073,7 @@ void * m68k_retranslate_inst(uint32_t address, m68k_context * context) return orig_start; } } - + map_native_address(context, instbuf.address, dst, (after-inst)*2, MAX_NATIVE_SIZE); opts->cur_code = dst+MAX_NATIVE_SIZE; jmp(orig_start, dst); @@ -4112,12 +4120,12 @@ void insert_breakpoint(m68k_context * context, uint32_t address, uint8_t * bp_ha } bp_stub = dst; native = call(native, bp_stub); - + //Calculate length of prologue dst = check_cycles_int(dst, address, opts); int check_int_size = dst-bp_stub; dst = bp_stub; - + //Save context and call breakpoint handler dst = call(dst, (uint8_t *)m68k_save_context); dst = push_r(dst, SCRATCH1); @@ -4195,7 +4203,7 @@ uint8_t * gen_mem_fun(x86_68k_options * opts, memmap_chunk * memmap, uint32_t nu ub_jcc = dst + 1; dst = jcc(dst, CC_NC, dst+2); } - + if (memmap[chunk].mask != 0xFFFFFF) { dst = and_ir(dst, memmap[chunk].mask, adr_reg, SZ_D); } @@ -4239,7 +4247,7 @@ uint8_t * gen_mem_fun(x86_68k_options * opts, memmap_chunk * memmap, uint32_t nu dst = mov_rr(dst, RAX, SCRATCH1, size); } dst = jmp(dst, (uint8_t *)m68k_load_context); - + *not_null = dst - (not_null + 1); } if (size == SZ_B) { @@ -4248,7 +4256,7 @@ uint8_t * gen_mem_fun(x86_68k_options * opts, memmap_chunk * memmap, uint32_t nu dst = add_rdisp8r(dst, CONTEXT, offsetof(m68k_context, mem_pointers) + sizeof(void*) * memmap[chunk].ptr_index, adr_reg, SZ_Q); if (is_write) { dst = mov_rrind(dst, SCRATCH1, SCRATCH2, size); - + } else { dst = mov_rindr(dst, SCRATCH1, SCRATCH1, size); } @@ -4377,14 +4385,14 @@ void init_x86_68k_opts(x86_68k_options * opts, memmap_chunk * memmap, uint32_t n opts->code_end = opts->cur_code + size; opts->ram_inst_sizes = malloc(sizeof(uint8_t *) * 64); memset(opts->ram_inst_sizes, 0, sizeof(uint8_t *) * 64); - + opts->read_16 = gen_mem_fun(opts, memmap, num_chunks, READ_16); opts->read_8 = gen_mem_fun(opts, memmap, num_chunks, READ_8); opts->write_16 = gen_mem_fun(opts, memmap, num_chunks, WRITE_16); opts->write_8 = gen_mem_fun(opts, memmap, num_chunks, WRITE_8); - + uint8_t * dst = opts->cur_code; - + opts->read_32 = dst; dst = push_r(dst, SCRATCH1); dst = call(dst, opts->read_16); @@ -4398,7 +4406,7 @@ void init_x86_68k_opts(x86_68k_options * opts, memmap_chunk * memmap, uint32_t n dst = shl_ir(dst, 16, SCRATCH2, SZ_D); dst = or_rr(dst, SCRATCH2, SCRATCH1, SZ_D); dst = retn(dst); - + opts->write_32_lowfirst = dst; dst = push_r(dst, SCRATCH2); dst = push_r(dst, SCRATCH1); @@ -4408,7 +4416,7 @@ void init_x86_68k_opts(x86_68k_options * opts, memmap_chunk * memmap, uint32_t n dst = pop_r(dst, SCRATCH2); dst = shr_ir(dst, 16, SCRATCH1, SZ_D); dst = jmp(dst, opts->write_16); - + opts->write_32_highfirst = dst; dst = push_r(dst, SCRATCH1); dst = push_r(dst, SCRATCH2); @@ -4418,7 +4426,7 @@ void init_x86_68k_opts(x86_68k_options * opts, memmap_chunk * memmap, uint32_t n dst = pop_r(dst, SCRATCH1); dst = add_ir(dst, 2, SCRATCH2, SZ_D); dst = jmp(dst, opts->write_16); - + opts->handle_cycle_limit_int = dst; dst = cmp_rdisp8r(dst, CONTEXT, offsetof(m68k_context, int_cycle), CYCLES, SZ_D); uint8_t * do_int = dst+1; @@ -4470,7 +4478,7 @@ void init_x86_68k_opts(x86_68k_options * opts, memmap_chunk * memmap, uint32_t n //discard function return address dst = pop_r(dst, SCRATCH2); dst = jmp_r(dst, SCRATCH1); - + opts->trap = dst; dst = push_r(dst, SCRATCH2); //swap USP and SSP if not already in supervisor mode @@ -4499,7 +4507,7 @@ void init_x86_68k_opts(x86_68k_options * opts, memmap_chunk * memmap, uint32_t n dst = call(dst, (uint8_t *)m68k_native_addr_and_sync); dst = cycles(dst, 18); dst = jmp_r(dst, SCRATCH1); - + opts->cur_code = dst; } -- cgit v1.2.3 From f09e868c52d367731accb654a3de3c95c5694519 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Sat, 20 Jul 2013 23:49:31 -0700 Subject: Fix handling of key on in YM2612 core --- ym2612.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/ym2612.c b/ym2612.c index facba3a..811cf48 100644 --- a/ym2612.c +++ b/ym2612.c @@ -162,7 +162,7 @@ void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t master_clo //populate sine table for (int32_t i = 0; i < 512; i++) { double sine = sin( ((double)(i*2+1) / SINE_TABLE_SIZE) * M_PI_2 ); - + //table stores 4.8 fixed pointed representation of the base 2 log sine_table[i] = round_fixed_point(-log2(sine), 8); } @@ -308,7 +308,7 @@ void ym_run(ym2612_context * context, uint32_t to_cycle) } } else { if (first_key_on) { - dfprintf(debug_file, "Changing op %d envelope %d by %d in %s phase\n", op, operator->envelope, envelope_inc, + dfprintf(debug_file, "Changing op %d envelope %d by %d in %s phase\n", op, operator->envelope, envelope_inc, operator->env_phase == PHASE_SUSTAIN ? "sustain" : (operator->env_phase == PHASE_DECAY ? "decay": "release")); } operator->envelope += envelope_inc; @@ -328,7 +328,7 @@ void ym_run(ym2612_context * context, uint32_t to_cycle) context->env_counter++; } } - + //Update Phase Generator uint32_t channel = context->current_op / 4; if (channel != 5 || !context->dac_enable) { @@ -348,7 +348,7 @@ void ym_run(ym2612_context * context, uint32_t to_cycle) } else { lfo_mod >>= 1; } - operator->phase_counter += lfo_mod; + operator->phase_counter += lfo_mod; } int16_t mod = 0; switch (op % 4) @@ -413,7 +413,7 @@ void ym_run(ym2612_context * context, uint32_t to_cycle) dfprintf(debug_file, "op %d, base phase: %d, mod: %d, sine: %d, out: %d\n", op, phase, mod, sine_table[(phase+mod) & 0x1FF], pow_table[sine_table[phase & 0x1FF] + env]); } phase += mod; - + int16_t output = pow_table[sine_table[phase & 0x1FF] + env]; if (phase & 0x200) { output = -output; @@ -561,7 +561,7 @@ void ym_update_phase_inc(ym2612_context * context, ym_operator * operator, uint3 } //detune detune = detune_table[channel->keycode][operator->detune & 0x3]; - } + } if (operator->detune & 0x40) { inc -= detune; //this can underflow, mask to 17-bit result @@ -600,7 +600,7 @@ void ym_data_write(ym2612_context * context, uint8_t value) context->lfo_am_step = context->lfo_pm_step = 0; } context->lfo_freq = value & 0x7; - + break; case REG_TIMERA_HIGH: context->timer_a_load &= 0x3; @@ -644,11 +644,13 @@ void ym_data_write(ym2612_context * context, uint8_t value) } for (uint8_t op = channel * 4, bit = 0x10; op < (channel + 1) * 4; op++, bit <<= 1) { if (value & bit) { - first_key_on = 1; - //printf("Key On for operator %d in channel %d\n", op, channel); - context->operators[op].phase_counter = 0; - context->operators[op].env_phase = PHASE_ATTACK; - context->operators[op].envelope = MAX_ENVELOPE; + if (context->operators[op].env_phase == PHASE_RELEASE) + { + first_key_on = 1; + //printf("Key On for operator %d in channel %d\n", op, channel); + context->operators[op].phase_counter = 0; + context->operators[op].env_phase = PHASE_ATTACK; + } } else { //printf("Key Off for operator %d in channel %d\n", op, channel); context->operators[op].env_phase = PHASE_RELEASE; @@ -753,7 +755,7 @@ void ym_data_write(ym2612_context * context, uint8_t value) } } } - + context->write_cycle = context->current_cycle; context->status |= 0x80; } -- cgit v1.2.3 From a75eb24bfbbf0dd3492c8414cfaafa0569a1539a Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Fri, 26 Jul 2013 19:55:04 -0700 Subject: Added support for saving savestates. Added gst savestate format test harness --- Makefile | 13 +- blastem.c | 206 +++---------------------- blastem.h | 3 + default.cfg | 3 +- gst.c | 478 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ gst.h | 8 + io.c | 12 +- testgst.c | 78 ++++++++++ vdp.c | 91 ++---------- vdp.h | 5 +- ym2612.c | 34 ++--- ym2612.h | 11 +- z80_to_x86.h | 1 + zruntime.S | 16 +- 14 files changed, 656 insertions(+), 303 deletions(-) create mode 100644 gst.c create mode 100644 gst.h create mode 100644 testgst.c diff --git a/Makefile b/Makefile index 7c38e95..81f33d3 100644 --- a/Makefile +++ b/Makefile @@ -18,18 +18,18 @@ AUDIOOBJS=ym2612.o psg.o wave.o all : dis trans stateview blastem -blastem : blastem.o vdp.o render_sdl.o io.o config.o tern.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS) - $(CC) -ggdb -o blastem blastem.o vdp.o render_sdl.o io.o config.o tern.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS) $(LDFLAGS) +blastem : blastem.o vdp.o render_sdl.o io.o config.o tern.o gst.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS) + $(CC) -ggdb -o blastem blastem.o vdp.o render_sdl.o io.o config.o tern.o gst.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS) $(LDFLAGS) dis : dis.o 68kinst.o $(CC) -o dis dis.o 68kinst.o zdis : zdis.o z80inst.o $(CC) -o zdis zdis.o z80inst.o - + libemu68k.a : $(M68KOBJS) $(TRANSOBJS) ar rcs libemu68k.a $(M68KOBJS) $(TRANSOBJS) - + trans : trans.o $(M68KOBJS) $(TRANSOBJS) $(CC) -o trans trans.o $(M68KOBJS) $(TRANSOBJS) @@ -48,6 +48,9 @@ stateview : stateview.o vdp.o render_sdl.o vgmplay : vgmplay.o render_sdl.o $(AUDIOOBJS) $(CC) -o vgmplay vgmplay.o render_sdl.o $(AUDIOOBJS) `pkg-config --libs $(LIBS)` +testgst : testgst.o gst.o + $(CC) -o testgst testgst.o gst.o + test_x86 : test_x86.o gen_x86.o $(CC) -o test_x86 test_x86.o gen_x86.o @@ -56,7 +59,7 @@ gen_fib : gen_fib.o gen_x86.o mem.o offsets : offsets.c z80_to_x86.h m68k_to_x86.h $(CC) -o offsets offsets.c - + %.o : %.S $(CC) -c -o $@ $< diff --git a/blastem.c b/blastem.c index a4afa0b..4e76afc 100644 --- a/blastem.c +++ b/blastem.c @@ -5,6 +5,7 @@ #include "vdp.h" #include "render.h" #include "blastem.h" +#include "gst.h" #include #include #include @@ -152,6 +153,7 @@ void adjust_int_cycle(m68k_context * context, vdp_context * v_context) } int break_on_sync = 0; +int save_state = 0; uint8_t reset = 1; uint8_t need_reset = 0; @@ -258,9 +260,19 @@ m68k_context * sync_components(m68k_context * context, uint32_t address) context->int_ack = 0; } adjust_int_cycle(context, v_context); - if (break_on_sync && address) { - break_on_sync = 0; - debugger(context, address); + if (address) { + if (break_on_sync) { + break_on_sync = 0; + debugger(context, address); + } + if (save_state) { + save_state = 0; + while (!z_context->pc) + { + sync_z80(z_context, z_context->current_cycle * MCLKS_PER_Z80 + MCLKS_PER_Z80); + } + save_gst(gen, "savestate.gst", address); + } } return context; } @@ -1465,190 +1477,6 @@ m68k_context * debugger(m68k_context * context, uint32_t address) return context; } -#define GST_68K_REGS 0x80 -#define GST_68K_REG_SIZE (0xDA-GST_68K_REGS) -#define GST_68K_PC_OFFSET (0xC8-GST_68K_REGS) -#define GST_68K_SR_OFFSET (0xD0-GST_68K_REGS) -#define GST_68K_USP_OFFSET (0xD2-GST_68K_REGS) -#define GST_68K_SSP_OFFSET (0xD6-GST_68K_REGS) -#define GST_68K_RAM 0x2478 -#define GST_Z80_REGS 0x404 -#define GST_Z80_REG_SIZE (0x440-GST_Z80_REGS) -#define GST_Z80_RAM 0x474 - -uint32_t read_le_32(uint8_t * data) -{ - return data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0]; -} - -uint16_t read_le_16(uint8_t * data) -{ - return data[1] << 8 | data[0]; -} - -uint16_t read_be_16(uint8_t * data) -{ - return data[0] << 8 | data[1]; -} - -uint32_t m68k_load_gst(m68k_context * context, FILE * gstfile) -{ - uint8_t buffer[4096]; - fseek(gstfile, GST_68K_REGS, SEEK_SET); - if (fread(buffer, 1, GST_68K_REG_SIZE, gstfile) != GST_68K_REG_SIZE) { - fputs("Failed to read 68K registers from savestate\n", stderr); - return 0; - } - uint8_t * curpos = buffer; - for (int i = 0; i < 8; i++) { - context->dregs[i] = read_le_32(curpos); - curpos += sizeof(uint32_t); - } - for (int i = 0; i < 8; i++) { - context->aregs[i] = read_le_32(curpos); - curpos += sizeof(uint32_t); - } - uint32_t pc = read_le_32(buffer + GST_68K_PC_OFFSET); - uint16_t sr = read_le_16(buffer + GST_68K_SR_OFFSET); - context->status = sr >> 8; - for (int flag = 4; flag >= 0; flag--) { - context->flags[flag] = sr & 1; - sr >>= 1; - } - if (context->status & (1 << 5)) { - context->aregs[8] = read_le_32(buffer + GST_68K_USP_OFFSET); - } else { - context->aregs[8] = read_le_32(buffer + GST_68K_SSP_OFFSET); - } - fseek(gstfile, GST_68K_RAM, SEEK_SET); - for (int i = 0; i < (32*1024);) { - if (fread(buffer, 1, sizeof(buffer), gstfile) != sizeof(buffer)) { - fputs("Failed to read 68K RAM from savestate\n", stderr); - return 0; - } - for(curpos = buffer; curpos < (buffer + sizeof(buffer)); curpos += sizeof(uint16_t)) { - context->mem_pointers[1][i++] = read_be_16(curpos); - } - } - return pc; -} - -uint8_t z80_load_gst(z80_context * context, FILE * gstfile) -{ - uint8_t regdata[GST_Z80_REG_SIZE]; - fseek(gstfile, GST_Z80_REGS, SEEK_SET); - if (fread(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) { - fputs("Failed to read Z80 registers from savestate\n", stderr); - return 0; - } - uint8_t * curpos = regdata; - uint8_t f = *(curpos++); - context->flags[ZF_C] = f & 1; - f >>= 1; - context->flags[ZF_N] = f & 1; - f >>= 1; - context->flags[ZF_PV] = f & 1; - f >>= 2; - context->flags[ZF_H] = f & 1; - f >>= 2; - context->flags[ZF_Z] = f & 1; - f >>= 1; - context->flags[ZF_S] = f; - - context->regs[Z80_A] = *curpos; - curpos += 3; - for (int reg = Z80_C; reg <= Z80_IYH; reg++) { - context->regs[reg++] = *(curpos++); - context->regs[reg] = *curpos; - curpos += 3; - } - uint16_t pc = read_le_16(curpos); - curpos += 4; - context->sp = read_le_16(curpos); - curpos += 4; - f = *(curpos++); - context->alt_flags[ZF_C] = f & 1; - f >>= 1; - context->alt_flags[ZF_N] = f & 1; - f >>= 1; - context->alt_flags[ZF_PV] = f & 1; - f >>= 2; - context->alt_flags[ZF_H] = f & 1; - f >>= 2; - context->alt_flags[ZF_Z] = f & 1; - f >>= 1; - context->alt_flags[ZF_S] = f; - context->alt_regs[Z80_A] = *curpos; - curpos += 3; - for (int reg = Z80_C; reg <= Z80_H; reg++) { - context->alt_regs[reg++] = *(curpos++); - context->alt_regs[reg] = *curpos; - curpos += 3; - } - context->regs[Z80_I] = *curpos; - curpos += 2; - context->iff1 = context->iff2 = *curpos; - curpos += 2; - reset = !*(curpos++); - busreq = *curpos; - curpos += 3; - uint32_t bank = read_le_32(curpos); - if (bank < 0x400000) { - context->mem_pointers[1] = context->mem_pointers[2] + bank; - } else { - context->mem_pointers[1] = NULL; - } - context->bank_reg = bank >> 15; - fseek(gstfile, GST_Z80_RAM, SEEK_SET); - if(fread(context->mem_pointers[0], 1, 8*1024, gstfile) != (8*1024)) { - fputs("Failed to read Z80 RAM from savestate\n", stderr); - return 0; - } - context->native_pc = z80_get_native_address_trans(context, pc); - return 1; -} - -uint32_t load_gst(genesis_context * gen, char * fname) -{ - FILE * gstfile = fopen(fname, "rb"); - if (!gstfile) { - fprintf(stderr, "Could not open file %s for reading\n", fname); - goto error; - } - char ident[5]; - if (fread(ident, 1, sizeof(ident), gstfile) != sizeof(ident)) { - fprintf(stderr, "Could not read ident code from %s\n", fname); - goto error_close; - } - if (memcmp(ident, "GST\xE0\x40", 3) != 0) { - fprintf(stderr, "%s doesn't appear to be a GST savestate. The ident code is %c%c%c\\x%X\\x%X instead of GST\\xE0\\x40.\n", fname, ident[0], ident[1], ident[2], ident[3], ident[4]); - goto error_close; - } - uint32_t pc = m68k_load_gst(gen->m68k, gstfile); - if (!pc) { - goto error_close; - } - if (!vdp_load_gst(gen->vdp, gstfile)) { - goto error_close; - } - if (!ym_load_gst(gen->ym, gstfile)) { - goto error_close; - } - if (!z80_load_gst(gen->z80, gstfile)) { - goto error_close; - } - gen->ports[0].control = 0x40; - gen->ports[1].control = 0x40; - adjust_int_cycle(gen->m68k, gen->vdp); - fclose(gstfile); - return pc; - -error_close: - fclose(gstfile); -error: - return 0; -} - #define ROM_END 0x1A4 #define RAM_ID 0x1B0 #define RAM_FLAGS 0x1B2 @@ -1805,11 +1633,15 @@ void init_run_cpu(genesis_context * gen, int debug, FILE * address_log, char * s if (statefile) { uint32_t pc = load_gst(gen, statefile); if (!pc) { + fprintf(stderr, "Failed to load save state %s\n", statefile); exit(1); } + printf("Loaded %s\n", statefile); if (debug) { insert_breakpoint(&context, pc, (uint8_t *)debugger); } + adjust_int_cycle(gen->m68k, gen->vdp); + gen->z80->native_pc = z80_get_native_address_trans(gen->z80, gen->z80->pc); start_68k_context(&context, pc); } else { if (debug) { diff --git a/blastem.h b/blastem.h index 635794d..da7953a 100644 --- a/blastem.h +++ b/blastem.h @@ -31,7 +31,10 @@ typedef struct { extern genesis_context * genesis; extern int break_on_sync; +extern int save_state; extern tern_node * config; +extern uint8_t busreq; +extern uint8_t reset; uint16_t read_dma_value(uint32_t address); m68k_context * debugger(m68k_context * context, uint32_t address); diff --git a/default.cfg b/default.cfg index ba4559e..6d6fd14 100644 --- a/default.cfg +++ b/default.cfg @@ -13,11 +13,12 @@ bindings { e gamepads.1.z f gamepads.1.mode enter gamepads.1.start - + [ ui.vdp_debug_mode ] ui.vdp_debug_pal u ui.enter_debugger esc ui.exit + ` ui.save_state } pads { 0 { diff --git a/gst.c b/gst.c new file mode 100644 index 0000000..ac651dd --- /dev/null +++ b/gst.c @@ -0,0 +1,478 @@ +#include "gst.h" +#include + +#define GST_68K_REGS 0x80 +#define GST_68K_REG_SIZE (0xDA-GST_68K_REGS) +#define GST_68K_PC_OFFSET (0xC8-GST_68K_REGS) +#define GST_68K_SR_OFFSET (0xD0-GST_68K_REGS) +#define GST_68K_USP_OFFSET (0xD2-GST_68K_REGS) +#define GST_68K_SSP_OFFSET (0xD6-GST_68K_REGS) +#define GST_68K_RAM 0x2478 +#define GST_Z80_REGS 0x404 +#define GST_Z80_REG_SIZE (0x440-GST_Z80_REGS) +#define GST_Z80_RAM 0x474 +#define GST_VDP_REGS 0xFA +#define GST_VDP_MEM 0x12478 +#define GST_YM_OFFSET 0x1E4 +#define GST_YM_SIZE (0x3E4-GST_YM_OFFSET) + +uint32_t read_le_32(uint8_t * data) +{ + return data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0]; +} + +uint16_t read_le_16(uint8_t * data) +{ + return data[1] << 8 | data[0]; +} + +uint16_t read_be_16(uint8_t * data) +{ + return data[0] << 8 | data[1]; +} + +void write_le_32(uint8_t * dst, uint32_t val) +{ + dst[0] = val; + dst[1] = val >> 8; + dst[2] = val >> 16; + dst[3] = val >> 24; +} + +void write_le_16(uint8_t * dst, uint16_t val) +{ + dst[0] = val; + dst[1] = val >> 8; +} + +void write_be_32(uint8_t * dst, uint32_t val) +{ + dst[0] = val >> 24; + dst[1] = val >> 16; + dst[2] = val >> 8; + dst[3] = val; +} + +void write_be_16(uint8_t * dst, uint16_t val) +{ + dst[0] = val >> 8; + dst[1] = val; +} + +uint32_t m68k_load_gst(m68k_context * context, FILE * gstfile) +{ + uint8_t buffer[4096]; + fseek(gstfile, GST_68K_REGS, SEEK_SET); + if (fread(buffer, 1, GST_68K_REG_SIZE, gstfile) != GST_68K_REG_SIZE) { + fputs("Failed to read 68K registers from savestate\n", stderr); + return 0; + } + uint8_t * curpos = buffer; + for (int i = 0; i < 8; i++) { + context->dregs[i] = read_le_32(curpos); + curpos += sizeof(uint32_t); + } + for (int i = 0; i < 8; i++) { + context->aregs[i] = read_le_32(curpos); + curpos += sizeof(uint32_t); + } + uint32_t pc = read_le_32(buffer + GST_68K_PC_OFFSET); + uint16_t sr = read_le_16(buffer + GST_68K_SR_OFFSET); + context->status = sr >> 8; + for (int flag = 4; flag >= 0; flag--) { + context->flags[flag] = sr & 1; + sr >>= 1; + } + if (context->status & (1 << 5)) { + context->aregs[8] = read_le_32(buffer + GST_68K_USP_OFFSET); + } else { + context->aregs[8] = read_le_32(buffer + GST_68K_SSP_OFFSET); + } + fseek(gstfile, GST_68K_RAM, SEEK_SET); + for (int i = 0; i < (32*1024);) { + if (fread(buffer, 1, sizeof(buffer), gstfile) != sizeof(buffer)) { + fputs("Failed to read 68K RAM from savestate\n", stderr); + return 0; + } + for(curpos = buffer; curpos < (buffer + sizeof(buffer)); curpos += sizeof(uint16_t)) { + context->mem_pointers[1][i++] = read_be_16(curpos); + } + } + return pc; +} + +uint8_t m68k_save_gst(m68k_context * context, uint32_t pc, FILE * gstfile) +{ + uint8_t buffer[4096]; + uint8_t * curpos = buffer; + for (int i = 0; i < 8; i++) { + write_le_32(curpos, context->dregs[i]); + curpos += sizeof(uint32_t); + } + for (int i = 0; i < 8; i++) { + write_le_32(curpos, context->aregs[i]); + curpos += sizeof(uint32_t); + } + write_le_32(buffer + GST_68K_PC_OFFSET, pc); + uint16_t sr = context->status << 3; + for (int flag = 4; flag >= 0; flag--) { + sr <<= 1; + sr |= context->flags[flag]; + } + write_le_16(buffer + GST_68K_SR_OFFSET, sr); + if (context->status & (1 << 5)) { + write_le_32(buffer + GST_68K_USP_OFFSET, context->aregs[8]); + write_le_32(buffer + GST_68K_SSP_OFFSET, context->aregs[7]); + } else { + write_le_32(buffer + GST_68K_USP_OFFSET, context->aregs[7]); + write_le_32(buffer + GST_68K_SSP_OFFSET, context->aregs[8]); + } + fseek(gstfile, GST_68K_REGS, SEEK_SET); + if (fwrite(buffer, 1, GST_68K_REG_SIZE, gstfile) != GST_68K_REG_SIZE) { + fputs("Failed to write 68K registers to savestate\n", stderr); + return 0; + } + + fseek(gstfile, GST_68K_RAM, SEEK_SET); + for (int i = 0; i < (32*1024);) { + for(curpos = buffer; curpos < (buffer + sizeof(buffer)); curpos += sizeof(uint16_t)) { + write_be_16(curpos, context->mem_pointers[1][i++]); + } + if (fwrite(buffer, 1, sizeof(buffer), gstfile) != sizeof(buffer)) { + fputs("Failed to write 68K RAM to savestate\n", stderr); + return 0; + } + } + return 1; +} + +uint8_t z80_load_gst(z80_context * context, FILE * gstfile) +{ + uint8_t regdata[GST_Z80_REG_SIZE]; + fseek(gstfile, GST_Z80_REGS, SEEK_SET); + if (fread(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) { + fputs("Failed to read Z80 registers from savestate\n", stderr); + return 0; + } + uint8_t * curpos = regdata; + uint8_t f = *(curpos++); + context->flags[ZF_C] = f & 1; + f >>= 1; + context->flags[ZF_N] = f & 1; + f >>= 1; + context->flags[ZF_PV] = f & 1; + f >>= 2; + context->flags[ZF_H] = f & 1; + f >>= 2; + context->flags[ZF_Z] = f & 1; + f >>= 1; + context->flags[ZF_S] = f; + + context->regs[Z80_A] = *curpos; + curpos += 3; + for (int reg = Z80_C; reg <= Z80_IYH; reg++) { + context->regs[reg++] = *(curpos++); + context->regs[reg] = *curpos; + curpos += 3; + } + context->pc = read_le_16(curpos); + curpos += 4; + context->sp = read_le_16(curpos); + curpos += 4; + f = *(curpos++); + context->alt_flags[ZF_C] = f & 1; + f >>= 1; + context->alt_flags[ZF_N] = f & 1; + f >>= 1; + context->alt_flags[ZF_PV] = f & 1; + f >>= 2; + context->alt_flags[ZF_H] = f & 1; + f >>= 2; + context->alt_flags[ZF_Z] = f & 1; + f >>= 1; + context->alt_flags[ZF_S] = f; + context->alt_regs[Z80_A] = *curpos; + curpos += 3; + for (int reg = Z80_C; reg <= Z80_H; reg++) { + context->alt_regs[reg++] = *(curpos++); + context->alt_regs[reg] = *curpos; + curpos += 3; + } + context->regs[Z80_I] = *curpos; + curpos += 2; + context->iff1 = context->iff2 = *curpos; + curpos += 2; + reset = !*(curpos++); + busreq = *curpos; + curpos += 3; + uint32_t bank = read_le_32(curpos); + if (bank < 0x400000) { + context->mem_pointers[1] = context->mem_pointers[2] + bank; + } else { + context->mem_pointers[1] = NULL; + } + context->bank_reg = bank >> 15; + fseek(gstfile, GST_Z80_RAM, SEEK_SET); + if(fread(context->mem_pointers[0], 1, 8*1024, gstfile) != (8*1024)) { + fputs("Failed to read Z80 RAM from savestate\n", stderr); + return 0; + } + return 1; +} + +uint8_t vdp_load_gst(vdp_context * context, FILE * state_file) +{ + uint8_t tmp_buf[CRAM_SIZE*2]; + fseek(state_file, GST_VDP_REGS, SEEK_SET); + if (fread(context->regs, 1, VDP_REGS, state_file) != VDP_REGS) { + fputs("Failed to read VDP registers from savestate\n", stderr); + return 0; + } + context->double_res = (context->regs[REG_MODE_4] & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES); + if (!context->double_res) { + context->framebuf = context->oddbuf; + } + latch_mode(context); + if (fread(tmp_buf, 1, sizeof(tmp_buf), state_file) != sizeof(tmp_buf)) { + fputs("Failed to read CRAM from savestate\n", stderr); + return 0; + } + for (int i = 0; i < CRAM_SIZE; i++) { + uint16_t value; + context->cram[i] = value = (tmp_buf[i*2+1] << 8) | tmp_buf[i*2]; + context->colors[i] = color_map[value & 0xEEE]; + context->colors[i + CRAM_SIZE] = color_map[(value & 0xEEE) | FBUF_SHADOW]; + context->colors[i + CRAM_SIZE*2] = color_map[(value & 0xEEE) | FBUF_HILIGHT]; + } + if (fread(tmp_buf, 2, VSRAM_SIZE, state_file) != VSRAM_SIZE) { + fputs("Failed to read VSRAM from savestate\n", stderr); + return 0; + } + for (int i = 0; i < VSRAM_SIZE; i++) { + context->vsram[i] = (tmp_buf[i*2+1] << 8) | tmp_buf[i*2]; + } + fseek(state_file, GST_VDP_MEM, SEEK_SET); + if (fread(context->vdpmem, 1, VRAM_SIZE, state_file) != VRAM_SIZE) { + fputs("Failed to read VRAM from savestate\n", stderr); + return 0; + } + return 1; +} + +uint8_t vdp_save_gst(vdp_context * context, FILE * outfile) +{ + uint8_t tmp_buf[CRAM_SIZE*2]; + fseek(outfile, GST_VDP_REGS, SEEK_SET); + if(fwrite(context->regs, 1, VDP_REGS, outfile) != VDP_REGS) { + fputs("Error writing VDP regs to savestate\n", stderr); + return 0; + } + for (int i = 0; i < CRAM_SIZE; i++) + { + tmp_buf[i*2] = context->cram[i]; + tmp_buf[i*2+1] = context->cram[i] >> 8; + } + if (fwrite(tmp_buf, 1, sizeof(tmp_buf), outfile) != sizeof(tmp_buf)) { + fputs("Error writing CRAM to savestate\n", stderr); + return 0; + } + for (int i = 0; i < VSRAM_SIZE; i++) + { + tmp_buf[i*2] = context->vsram[i]; + tmp_buf[i*2+1] = context->vsram[i] >> 8; + } + if (fwrite(tmp_buf, 2, VSRAM_SIZE, outfile) != VSRAM_SIZE) { + fputs("Error writing VSRAM to savestate\n", stderr); + return 0; + } + fseek(outfile, GST_VDP_MEM, SEEK_SET); + if (fwrite(context->vdpmem, 1, VRAM_SIZE, outfile) != VRAM_SIZE) { + fputs("Error writing VRAM to savestate\n", stderr); + return 0; + } + return 1; +} + +uint8_t z80_save_gst(z80_context * context, FILE * gstfile) +{ + uint8_t regdata[GST_Z80_REG_SIZE]; + uint8_t * curpos = regdata; + memset(regdata, 0, sizeof(regdata)); + uint8_t f = context->flags[ZF_S]; + f <<= 1; + f |= context->flags[ZF_Z] ; + f <<= 2; + f |= context->flags[ZF_H]; + f <<= 2; + f |= context->flags[ZF_PV]; + f <<= 1; + f |= context->flags[ZF_N]; + f <<= 1; + f |= context->flags[ZF_C]; + *(curpos++) = f; + *curpos = context->regs[Z80_A]; + + curpos += 3; + for (int reg = Z80_C; reg <= Z80_IYH; reg++) { + *(curpos++) = context->regs[reg++]; + *curpos = context->regs[reg]; + curpos += 3; + } + write_le_16(curpos, context->pc); + curpos += 4; + write_le_16(curpos, context->sp); + curpos += 4; + f = context->alt_flags[ZF_S]; + f <<= 1; + f |= context->alt_flags[ZF_Z] ; + f <<= 2; + f |= context->alt_flags[ZF_H]; + f <<= 2; + f |= context->alt_flags[ZF_PV]; + f <<= 1; + f |= context->alt_flags[ZF_N]; + f <<= 1; + f |= context->alt_flags[ZF_C]; + *(curpos++) = f; + *curpos = context->alt_regs[Z80_A]; + curpos += 3; + for (int reg = Z80_C; reg <= Z80_H; reg++) { + *(curpos++) = context->alt_regs[reg++]; + *curpos = context->alt_regs[reg]; + curpos += 3; + } + *curpos = context->regs[Z80_I]; + curpos += 2; + *curpos = context->iff1; + curpos += 2; + *(curpos++) = !reset; + *curpos = busreq; + curpos += 3; + uint32_t bank = context->bank_reg << 15; + write_le_32(curpos, bank); + fseek(gstfile, GST_Z80_REGS, SEEK_SET); + if (fwrite(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) { + return 0; + } + fseek(gstfile, GST_Z80_RAM, SEEK_SET); + if(fwrite(context->mem_pointers[0], 1, 8*1024, gstfile) != (8*1024)) { + fputs("Failed to write Z80 RAM to savestate\n", stderr); + return 0; + } + return 1; +} + +uint8_t ym_load_gst(ym2612_context * context, FILE * gstfile) +{ + uint8_t regdata[GST_YM_SIZE]; + fseek(gstfile, GST_YM_OFFSET, SEEK_SET); + if (fread(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) { + return 0; + } + for (int i = 0; i < sizeof(regdata); i++) { + if (i & 0x100) { + ym_address_write_part2(context, i & 0xFF); + } else { + ym_address_write_part1(context, i); + } + ym_data_write(context, regdata[i]); + } + return 1; +} + +uint8_t ym_save_gst(ym2612_context * context, FILE * gstfile) +{ + uint8_t regdata[GST_YM_SIZE]; + for (int i = 0; i < sizeof(regdata); i++) { + if (i & 0x100) { + int reg = (i & 0xFF); + if (reg >= YM_PART2_START && reg < YM_REG_END) { + regdata[i] = context->part2_regs[reg-YM_PART2_START]; + } else { + regdata[i] = 0xFF; + } + } else { + if (i >= YM_PART1_START && i < YM_REG_END) { + regdata[i] = context->part1_regs[i-YM_PART1_START]; + } else { + regdata[i] = 0xFF; + } + } + } + fseek(gstfile, GST_YM_OFFSET, SEEK_SET); + if (fwrite(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) { + return 0; + } + return 1; +} + +uint32_t load_gst(genesis_context * gen, char * fname) +{ + FILE * gstfile = fopen(fname, "rb"); + if (!gstfile) { + fprintf(stderr, "Could not open file %s for reading\n", fname); + goto error; + } + char ident[5]; + if (fread(ident, 1, sizeof(ident), gstfile) != sizeof(ident)) { + fprintf(stderr, "Could not read ident code from %s\n", fname); + goto error_close; + } + if (memcmp(ident, "GST\x40\xE0", 5) != 0) { + fprintf(stderr, "%s doesn't appear to be a GST savestate. The ident code is %c%c%c\\x%X\\x%X instead of GST\\x40\\xE0.\n", fname, ident[0], ident[1], ident[2], ident[3], ident[4]); + goto error_close; + } + uint32_t pc = m68k_load_gst(gen->m68k, gstfile); + if (!pc) { + goto error_close; + } + if (!vdp_load_gst(gen->vdp, gstfile)) { + goto error_close; + } + if (!ym_load_gst(gen->ym, gstfile)) { + goto error_close; + } + if (!z80_load_gst(gen->z80, gstfile)) { + goto error_close; + } + gen->ports[0].control = 0x40; + gen->ports[1].control = 0x40; + fclose(gstfile); + return pc; + +error_close: + fclose(gstfile); +error: + return 0; +} + +uint8_t save_gst(genesis_context * gen, char *fname, uint32_t m68k_pc) +{ + FILE * gstfile = fopen(fname, "wb"); + if (!gstfile) { + fprintf(stderr, "Could not open %s for writing\n", fname); + goto error; + } + if (fwrite("GST\x40\xE0", 1, 5, gstfile) != 5) { + fputs("Error writing signature to savestate\n", stderr); + goto error_close; + } + if (!m68k_save_gst(gen->m68k, m68k_pc, gstfile)) { + goto error_close; + } + if (!z80_save_gst(gen->z80, gstfile)) { + goto error_close; + } + if (!vdp_save_gst(gen->vdp, gstfile)) { + goto error_close; + } + if (!ym_save_gst(gen->ym, gstfile)) { + goto error_close; + } + return 1; + +error_close: + fclose(gstfile); +error: + return 0; +} diff --git a/gst.h b/gst.h new file mode 100644 index 0000000..ff5d98c --- /dev/null +++ b/gst.h @@ -0,0 +1,8 @@ +#ifndef GST_H_ +#define GST_H_ +#include "blastem.h" + +uint8_t save_gst(genesis_context * gen, char *fname, uint32_t m68k_pc); +uint32_t load_gst(genesis_context * gen, char * fname); + +#endif //GST_H_ diff --git a/io.c b/io.c index b277c3c..03875c5 100644 --- a/io.c +++ b/io.c @@ -13,6 +13,7 @@ typedef enum { UI_DEBUG_MODE_INC, UI_DEBUG_PAL_INC, UI_ENTER_DEBUGGER, + UI_SAVE_STATE, UI_EXIT } ui_action; @@ -107,7 +108,7 @@ void bind_dpad(int joystick, int dpad, int direction, uint8_t bind_type, uint8_t void bind_gamepad(int keycode, int gamepadnum, int button) { - + if (gamepadnum < 1 || gamepadnum > 2) { return; } @@ -210,6 +211,9 @@ void handle_binding_up(keybinding * binding) case UI_ENTER_DEBUGGER: break_on_sync = 1; break; + case UI_SAVE_STATE: + save_state = 1; + break; case UI_EXIT: exit(0); } @@ -283,6 +287,8 @@ int parse_binding_target(char * target, tern_node * padbuttons, int * ui_out, in *ui_out = UI_DEBUG_PAL_INC; } else if(!strcmp(target + 3, "enter_debugger")) { *ui_out = UI_ENTER_DEBUGGER; + } else if(!strcmp(target + 3, "save_state")) { + *ui_out = UI_SAVE_STATE; } else if(!strcmp(target + 3, "exit")) { *ui_out = UI_EXIT; } else { @@ -345,7 +351,7 @@ void set_keybindings() special = tern_insert_int(special, "right", RENDERKEY_RIGHT); special = tern_insert_int(special, "enter", '\r'); special = tern_insert_int(special, "esc", RENDERKEY_ESC); - + tern_node * padbuttons = tern_insert_int(NULL, ".up", DPAD_UP); padbuttons = tern_insert_int(padbuttons, ".down", DPAD_DOWN); padbuttons = tern_insert_int(padbuttons, ".left", DPAD_LEFT); @@ -358,7 +364,7 @@ void set_keybindings() padbuttons = tern_insert_int(padbuttons, ".z", BUTTON_Z); padbuttons = tern_insert_int(padbuttons, ".start", BUTTON_START); padbuttons = tern_insert_int(padbuttons, ".mode", BUTTON_MODE); - + tern_node * keys = tern_find_prefix(config, "bindingskeys"); process_keys(keys, special, padbuttons, NULL); char prefix[] = "bindingspads00"; diff --git a/testgst.c b/testgst.c new file mode 100644 index 0000000..9b0a88b --- /dev/null +++ b/testgst.c @@ -0,0 +1,78 @@ +#include "gst.h" +#include +#include + +uint8_t busreq; +uint8_t reset; + +int32_t color_map[1 << 12]; + +void latch_mode(vdp_context * context) +{ +} +void ym_data_write(ym2612_context * context, uint8_t value) +{ + if (context->selected_reg >= YM_REG_END) { + return; + } + if (context->selected_part) { + if (context->selected_reg < YM_PART2_START) { + return; + } + context->part2_regs[context->selected_reg - YM_PART2_START] = value; + } else { + if (context->selected_reg < YM_PART1_START) { + return; + } + context->part1_regs[context->selected_reg - YM_PART1_START] = value; + } +} + +void ym_address_write_part1(ym2612_context * context, uint8_t address) +{ + //printf("address_write_part1: %X\n", address); + context->selected_reg = address; + context->selected_part = 0; +} + +void ym_address_write_part2(ym2612_context * context, uint8_t address) +{ + //printf("address_write_part2: %X\n", address); + context->selected_reg = address; + context->selected_part = 1; +} + +uint16_t ram[64*1024]; +uint8_t zram[8*1024]; + + +int main(int argc, char ** argv) +{ + vdp_context vdp; + ym2612_context ym; + psg_context psg; + m68k_context m68k; + z80_context z80; + genesis_context gen; + if (argc < 3) { + fputs("Usage: testgst infile outfile\n", stderr); + return 1; + } + memset(&gen, 0, sizeof(gen)); + memset(&m68k, 0, sizeof(m68k)); + memset(&z80, 0, sizeof(z80)); + memset(&ym, 0, sizeof(ym)); + memset(&vdp, 0, sizeof(vdp)); + memset(&psg, 0, sizeof(psg)); + m68k.mem_pointers[1] = ram; + z80.mem_pointers[0] = zram; + vdp.vdpmem = malloc(VRAM_SIZE); + gen.vdp = &vdp; + gen.ym = &ym; + gen.psg = &psg; + gen.m68k = &m68k; + gen.z80 = &z80; + uint32_t pc = load_gst(&gen, argv[1]); + save_gst(&gen, argv[2], pc); + return 0; +} diff --git a/vdp.c b/vdp.c index 7154548..6e20009 100644 --- a/vdp.c +++ b/vdp.c @@ -125,7 +125,7 @@ void render_sprite_cells(vdp_context * context) { if (context->cur_slot >= context->sprite_draws) { sprite_draw * d = context->sprite_draw_list + context->cur_slot; - + uint16_t dir; int16_t x; if (d->h_flip) { @@ -164,7 +164,7 @@ void vdp_print_sprite_table(vdp_context * context) uint16_t link = context->vdpmem[address+3] & 0x7F; uint8_t pal = context->vdpmem[address + 4] >> 5 & 0x3; uint8_t pri = context->vdpmem[address + 4] >> 7; - uint16_t pattern = ((context->vdpmem[address + 4] << 8 | context->vdpmem[address + 5]) & 0x7FF) << 5; + uint16_t pattern = ((context->vdpmem[address + 4] << 8 | context->vdpmem[address + 5]) & 0x7FF) << 5; //printf("Sprite %d: X=%d(%d), Y=%d(%d), Width=%u, Height=%u, Link=%u, Pal=%u, Pri=%u, Pat=%X\n", current_index, x, x-128, y, y-128, width, height, link, pal, pri, pattern); current_index = link; count++; @@ -179,9 +179,9 @@ void vdp_print_reg_explain(vdp_context * context) "01: %.2X | Display %s, V-ints %s, Height: %d, Mode %d\n" "0B: %.2X | E-ints %s, V-Scroll: %s, H-Scroll: %s\n" "0C: %.2X | Width: %d, Shadow/Highlight: %s\n", - context->regs[REG_MODE_1], context->regs[REG_MODE_1] & BIT_HINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_1] & BIT_PAL_SEL != 0, + context->regs[REG_MODE_1], context->regs[REG_MODE_1] & BIT_HINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_1] & BIT_PAL_SEL != 0, context->regs[REG_MODE_1] & BIT_HVC_LATCH ? "enabled" : "disabled", context->regs[REG_MODE_1] & BIT_DISP_DIS ? "disabled" : "enabled", - context->regs[REG_MODE_2], context->regs[REG_MODE_2] & BIT_DISP_EN ? "enabled" : "disabled", context->regs[REG_MODE_2] & BIT_VINT_EN ? "enabled" : "disabled", + context->regs[REG_MODE_2], context->regs[REG_MODE_2] & BIT_DISP_EN ? "enabled" : "disabled", context->regs[REG_MODE_2] & BIT_VINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_2] & BIT_PAL ? 30 : 28, context->regs[REG_MODE_2] & BIT_MODE_5 ? 5 : 4, context->regs[REG_MODE_3], context->regs[REG_MODE_3] & BIT_EINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_3] & BIT_VSCROLL ? "2 cell" : "full", hscroll[context->regs[REG_MODE_3] & 0x3], @@ -203,8 +203,8 @@ void vdp_print_reg_explain(vdp_context * context) "0A: %.2X | H-Int Counter: %u\n" "0F: %.2X | Auto-increment: $%X\n" "10: %.2X | Scroll A/B Size: %sx%s\n", - context->regs[REG_BG_COLOR], context->regs[REG_BG_COLOR] & 0x3F, - context->regs[REG_HINT], context->regs[REG_HINT], + context->regs[REG_BG_COLOR], context->regs[REG_BG_COLOR] & 0x3F, + context->regs[REG_HINT], context->regs[REG_HINT], context->regs[REG_AUTOINC], context->regs[REG_AUTOINC], context->regs[REG_SCROLL], sizes[context->regs[REG_SCROLL] & 0x3], sizes[context->regs[REG_SCROLL] >> 4 & 0x3]); printf("\n**Internal Group**\n" @@ -212,8 +212,8 @@ void vdp_print_reg_explain(vdp_context * context) "CD: %X\n" "Pending: %s\n", context->address, context->cd, (context->flags & FLAG_PENDING) ? "true" : "false"); - - //TODO: Window Group, DMA Group + + //TODO: Window Group, DMA Group } void scan_sprite_table(uint32_t line, vdp_context * context) @@ -295,7 +295,7 @@ void read_sprite_x(uint32_t line, vdp_context * context) height *= 2; } uint16_t att_addr = ((context->regs[REG_SAT] & 0x7F) << 9) + context->sprite_info_list[context->cur_slot].index * 8 + 4; - uint16_t tileinfo = (context->vdpmem[att_addr] << 8) | context->vdpmem[att_addr+1]; + uint16_t tileinfo = (context->vdpmem[att_addr] << 8) | context->vdpmem[att_addr+1]; uint8_t pal_priority = (tileinfo >> 9) & 0x70; uint8_t row; if (tileinfo & MAP_BIT_V_FLIP) { @@ -315,7 +315,7 @@ void read_sprite_x(uint32_t line, vdp_context * context) } else if(context->flags & (FLAG_CAN_MASK | FLAG_DOT_OFLOW)) { context->flags |= FLAG_MASKED; } - + context->flags &= ~FLAG_DOT_OFLOW; int16_t i; if (context->flags & FLAG_MASKED) { @@ -415,7 +415,7 @@ void external_slot(vdp_context * context) context->dma_val = (context->dma_val << 8) | ((context->dma_val >> 8) & 0xFF); break; case CRAM_WRITE: - write_cram(context, context->address, context->dma_val); + write_cram(context, context->address, context->dma_val); //printf("CRAM DMA Fill | %X set to %X at %d\n", (context->address/2) & (CRAM_SIZE-1), context->cram[(context->address/2) & (CRAM_SIZE-1)], context->cycles); break; case VSRAM_WRITE: @@ -575,7 +575,7 @@ void read_map_scroll(uint16_t column, uint16_t vsram_off, uint32_t line, uint16_ address &= 0xF000; line_offset = (((line) >> vscroll_shift) * 64 * 2) & 0xFFF; mask = 0x7F; - + } else { address &= 0xF800; line_offset = (((line) >> vscroll_shift) * 32 * 2) & 0xFFF; @@ -751,7 +751,7 @@ void render_map_output(uint32_t line, int32_t col, vdp_context * context) } plane_b_off = context->buf_b_off - (context->hscroll_b & 0xF); //printf("A | tmp_buf offset: %d\n", 8 - (context->hscroll_a & 0x7)); - + if (context->regs[REG_MODE_4] & BIT_HILIGHT) { for (int i = 0; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) { uint8_t pixel; @@ -1402,9 +1402,9 @@ void vdp_run_context(vdp_context * context, uint32_t target_cycles) } if ((line < active_lines || (line == active_lines && linecyc < (context->latched_mode & BIT_H40 ? 64 : 80))) && context->regs[REG_MODE_2] & DISPLAY_ENABLE) { //first sort-of active line is treated as 255 internally - //it's used for gathering sprite info for line + //it's used for gathering sprite info for line line = (line - 1) & 0xFF; - + //Convert to slot number if (context->latched_mode & BIT_H40){ vdp_h40(line, slot, context); @@ -1789,64 +1789,3 @@ void vdp_int_ack(vdp_context * context, uint16_t int_num) } } -#define GST_VDP_REGS 0xFA -#define GST_VDP_MEM 0x12478 - -uint8_t vdp_load_gst(vdp_context * context, FILE * state_file) -{ - uint8_t tmp_buf[CRAM_SIZE*2]; - fseek(state_file, GST_VDP_REGS, SEEK_SET); - if (fread(context->regs, 1, VDP_REGS, state_file) != VDP_REGS) { - fputs("Failed to read VDP registers from savestate\n", stderr); - return 0; - } - context->double_res = (context->regs[REG_MODE_4] & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES); - if (!context->double_res) { - context->framebuf = context->oddbuf; - } - latch_mode(context); - if (fread(tmp_buf, 1, sizeof(tmp_buf), state_file) != sizeof(tmp_buf)) { - fputs("Failed to read CRAM from savestate\n", stderr); - return 0; - } - for (int i = 0; i < CRAM_SIZE; i++) { - uint16_t value; - context->cram[i] = value = (tmp_buf[i*2+1] << 8) | tmp_buf[i*2]; - context->colors[i] = color_map[value & 0xEEE]; - context->colors[i + CRAM_SIZE] = color_map[(value & 0xEEE) | FBUF_SHADOW]; - context->colors[i + CRAM_SIZE*2] = color_map[(value & 0xEEE) | FBUF_HILIGHT]; - } - if (fread(tmp_buf, 2, VSRAM_SIZE, state_file) != VSRAM_SIZE) { - fputs("Failed to read VSRAM from savestate\n", stderr); - return 0; - } - for (int i = 0; i < VSRAM_SIZE; i++) { - context->vsram[i] = (tmp_buf[i*2+1] << 8) | tmp_buf[i*2]; - } - fseek(state_file, GST_VDP_MEM, SEEK_SET); - if (fread(context->vdpmem, 1, VRAM_SIZE, state_file) != VRAM_SIZE) { - fputs("Failed to read VRAM from savestate\n", stderr); - return 0; - } - return 1; -} - -void vdp_save_state(vdp_context * context, FILE * outfile) -{ - uint8_t tmp_buf[CRAM_SIZE*2]; - fseek(outfile, GST_VDP_REGS, SEEK_SET); - fwrite(context->regs, 1, VDP_REGS, outfile); - for (int i = 0; i < CRAM_SIZE; i++) { - tmp_buf[i*2] = context->cram[i]; - tmp_buf[i*2+1] = context->cram[i] >> 8; - } - fwrite(tmp_buf, 1, sizeof(tmp_buf), outfile); - for (int i = 0; i < VSRAM_SIZE; i++) { - tmp_buf[i*2] = context->vsram[i]; - tmp_buf[i*2+1] = context->vsram[i] >> 8; - } - fwrite(tmp_buf, 2, VSRAM_SIZE, outfile); - fseek(outfile, GST_VDP_MEM, SEEK_SET); - fwrite(context->vdpmem, 1, VRAM_SIZE, outfile); -} - diff --git a/vdp.h b/vdp.h index 97be57b..15f86b1 100644 --- a/vdp.h +++ b/vdp.h @@ -164,7 +164,7 @@ uint32_t vdp_run_to_vblank(vdp_context * context); //runs until the target cycle is reached or the current DMA operation has completed, whicever comes first void vdp_run_dma_done(vdp_context * context, uint32_t target_cycles); uint8_t vdp_load_gst(vdp_context * context, FILE * state_file); -void vdp_save_state(vdp_context * context, FILE * outfile); +uint8_t vdp_save_gst(vdp_context * context, FILE * outfile); int vdp_control_port_write(vdp_context * context, uint16_t value); int vdp_data_port_write(vdp_context * context, uint16_t value); uint16_t vdp_control_port_read(vdp_context * context); @@ -177,5 +177,8 @@ uint32_t vdp_next_vint_z80(vdp_context * context); void vdp_int_ack(vdp_context * context, uint16_t int_num); void vdp_print_sprite_table(vdp_context * context); void vdp_print_reg_explain(vdp_context * context); +void latch_mode(vdp_context * context); + +extern int32_t color_map[1 << 12]; #endif //VDP_H_ diff --git a/ym2612.c b/ym2612.c index 811cf48..fda0249 100644 --- a/ym2612.c +++ b/ym2612.c @@ -582,9 +582,20 @@ void ym_update_phase_inc(ym2612_context * context, ym_operator * operator, uint3 void ym_data_write(ym2612_context * context, uint8_t value) { - if (context->selected_reg < 0x21 || context->selected_reg > 0xB6 || (context->selected_reg < 0x30 && context->selected_part)) { + if (context->selected_reg >= YM_REG_END) { return; } + if (context->selected_part) { + if (context->selected_reg < YM_PART2_START) { + return; + } + context->part2_regs[context->selected_reg - YM_PART2_START] = value; + } else { + if (context->selected_reg < YM_PART1_START) { + return; + } + context->part1_regs[context->selected_reg - YM_PART1_START] = value; + } dfprintf(debug_file, "write of %X to reg %X in part %d\n", value, context->selected_reg, context->selected_part+1); if (context->selected_reg < 0x30) { //Shared regs @@ -765,24 +776,3 @@ uint8_t ym_read_status(ym2612_context * context) return context->status; } -#define GST_YM_OFFSET 0x1E4 -#define GST_YM_SIZE (0x3E4-GST_YM_OFFSET) - -uint8_t ym_load_gst(ym2612_context * context, FILE * gstfile) -{ - uint8_t regdata[GST_YM_SIZE]; - fseek(gstfile, GST_YM_OFFSET, SEEK_SET); - if (fread(regdata, 1, sizeof(regdata), gstfile) != sizeof(regdata)) { - return 0; - } - for (int i = 0; i < sizeof(regdata); i++) { - if (i & 0x100) { - ym_address_write_part2(context, i & 0xFF); - } else { - ym_address_write_part1(context, i); - } - ym_data_write(context, regdata[i]); - } - return 1; -} - diff --git a/ym2612.h b/ym2612.h index b0e3526..b693507 100644 --- a/ym2612.h +++ b/ym2612.h @@ -45,6 +45,12 @@ typedef struct { uint8_t keycode; } ym_supp; +#define YM_PART1_START 0x21 +#define YM_PART2_START 0x30 +#define YM_REG_END 0xB8 +#define YM_PART1_REGS (YM_REG_END-YM_PART1_START) +#define YM_PART2_REGS (YM_REG_END-YM_PART2_START) + typedef struct { int16_t *audio_buffer; int16_t *back_buffer; @@ -66,7 +72,7 @@ typedef struct { uint8_t ch3_mode; uint8_t current_op; uint8_t current_env_op; - + uint8_t timer_control; uint8_t dac_enable; uint8_t lfo_enable; @@ -77,6 +83,8 @@ typedef struct { uint8_t status; uint8_t selected_reg; uint8_t selected_part; + uint8_t part1_regs[YM_PART1_REGS]; + uint8_t part2_regs[YM_PART2_REGS]; } ym2612_context; void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t sample_limit, uint32_t options); @@ -86,6 +94,7 @@ void ym_address_write_part2(ym2612_context * context, uint8_t address); void ym_data_write(ym2612_context * context, uint8_t value); uint8_t ym_read_status(ym2612_context * context); uint8_t ym_load_gst(ym2612_context * context, FILE * gstfile); +uint8_t ym_save_gst(ym2612_context * context, FILE * gstfile); #endif //YM2612_H_ diff --git a/z80_to_x86.h b/z80_to_x86.h index 9f70803..e45a823 100644 --- a/z80_to_x86.h +++ b/z80_to_x86.h @@ -50,6 +50,7 @@ typedef struct { void * system; uint8_t ram_code_flags[(8 * 1024)/128/8]; uint32_t int_enable_cycle; + uint16_t pc; } z80_context; void translate_z80_stream(z80_context * context, uint32_t address); diff --git a/zruntime.S b/zruntime.S index 8ef8745..a4a53ef 100644 --- a/zruntime.S +++ b/zruntime.S @@ -19,12 +19,13 @@ do_limit: cmp 112(%rsi), %ebp jb no_sync sync_io: + movw $0, 164(%rsi) call z80_save_context_scratch pop %rax /*return address in read/write func*/ pop 104(%rsi) /*return address in native code*/ sub $5, %rax /* adjust return addres to point to the call instruction that got us here */ mov %rax, (%rsi) - + pop %r15 /* restore callee saved regsiters */ pop %r14 pop %r13 @@ -32,7 +33,7 @@ sync_io: pop %rbp pop %rbx ret /* return to caller of z80_run */ - + .global z80_handle_cycle_limit_int z80_handle_cycle_limit_int: cmp 116(%rsi), %ebp @@ -63,6 +64,7 @@ z80_handle_cycle_limit_int: zskip_int: cmp 112(%rsi), %ebp jb zskip_sync +mov %r13w, 164(%rsi) .global z80_do_sync z80_do_sync: call z80_save_context @@ -244,7 +246,7 @@ z80_io_write: call z_inccycles_io /* genesis Z80 has no IO port hardware and writes have no effect */ ret - + .global z80_retrans_stub z80_retrans_stub: pop %r14 @@ -264,7 +266,7 @@ z80_retrans_stub: z80_native_addr: call z80_save_context push %rsi - mov %rsi, %rdi + mov %rsi, %rdi movzx %r13w, %esi call z80_get_native_address_trans mov %rax, %r13 @@ -275,7 +277,7 @@ z80_native_addr: z80_save_context_scratch: mov %r13w, 98(%rsi) /* scratch1 */ mov %r14w, 100(%rsi) /* scratch2 */ - + .global z80_save_context z80_save_context: mov %r9w, 8(%rsi) /* SP */ @@ -295,7 +297,7 @@ z80_save_context: z80_load_context_scratch: mov 98(%rsi), %r13w /* scratch1 */ mov 100(%rsi), %r14w /* scratch2 */ - + .global z80_load_context z80_load_context: mov 8(%rsi), %r9w /* SP */ @@ -328,4 +330,4 @@ z80_run: movq $0, 104(%rsi) no_extra: jmp *(%rsi) - + -- cgit v1.2.3 From 48e201cf39a18628e33d51a7fba1ee706af18ade Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Sun, 1 Sep 2013 12:11:28 -0700 Subject: Fix bug that caused a DMA fill to start after another DMA operation completed if the FIFO is not empty --- blastem.c | 6 ++++-- vdp.c | 45 ++++++++++++++++++++++++--------------------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/blastem.c b/blastem.c index a4afa0b..432e98b 100644 --- a/blastem.c +++ b/blastem.c @@ -219,7 +219,7 @@ m68k_context * sync_components(m68k_context * context, uint32_t address) if (gen->ym->write_cycle != CYCLE_NEVER) { gen->ym->write_cycle = gen->ym->write_cycle >= mclks_per_frame/MCLKS_PER_68K ? gen->ym->write_cycle - mclks_per_frame/MCLKS_PER_68K : 0; } - //printf("reached frame end | 68K Cycles: %d, MCLK Cycles: %d\n", context->current_cycle, mclks); + printf("reached frame end | 68K Cycles: %d, MCLK Cycles: %d\n", context->current_cycle, mclks); vdp_run_context(v_context, mclks_per_frame); if (!headless) { @@ -284,6 +284,7 @@ m68k_context * vdp_port_write(uint32_t vdp_port, m68k_context * context, uint16_ vdp_run_dma_done(v_context, mclks_per_frame); if (v_context->cycles >= mclks_per_frame) { if (!headless) { + printf("reached frame end | 68K Cycles: %d, MCLK Cycles: %d\n", context->current_cycle, v_context->cycles); wait_render_frame(v_context, frame_limit); } vdp_adjust_cycles(v_context, mclks_per_frame); @@ -301,7 +302,7 @@ m68k_context * vdp_port_write(uint32_t vdp_port, m68k_context * context, uint16_ } } } - context->current_cycle = v_context->cycles / MCLKS_PER_68K; + //context->current_cycle = v_context->cycles / MCLKS_PER_68K; } } else if(vdp_port < 8) { blocked = vdp_control_port_write(v_context, value); @@ -343,6 +344,7 @@ m68k_context * vdp_port_write(uint32_t vdp_port, m68k_context * context, uint16_ exit(1); } if (v_context->cycles != before_cycle) { + printf("68K paused for %d cycles at cycle %d\n", v_context->cycles / MCLKS_PER_68K - context->current_cycle, context->current_cycle); context->current_cycle = v_context->cycles / MCLKS_PER_68K; } } else if (vdp_port < 0x18) { diff --git a/vdp.c b/vdp.c index 7154548..8b2bdaf 100644 --- a/vdp.c +++ b/vdp.c @@ -125,7 +125,7 @@ void render_sprite_cells(vdp_context * context) { if (context->cur_slot >= context->sprite_draws) { sprite_draw * d = context->sprite_draw_list + context->cur_slot; - + uint16_t dir; int16_t x; if (d->h_flip) { @@ -164,7 +164,7 @@ void vdp_print_sprite_table(vdp_context * context) uint16_t link = context->vdpmem[address+3] & 0x7F; uint8_t pal = context->vdpmem[address + 4] >> 5 & 0x3; uint8_t pri = context->vdpmem[address + 4] >> 7; - uint16_t pattern = ((context->vdpmem[address + 4] << 8 | context->vdpmem[address + 5]) & 0x7FF) << 5; + uint16_t pattern = ((context->vdpmem[address + 4] << 8 | context->vdpmem[address + 5]) & 0x7FF) << 5; //printf("Sprite %d: X=%d(%d), Y=%d(%d), Width=%u, Height=%u, Link=%u, Pal=%u, Pri=%u, Pat=%X\n", current_index, x, x-128, y, y-128, width, height, link, pal, pri, pattern); current_index = link; count++; @@ -179,9 +179,9 @@ void vdp_print_reg_explain(vdp_context * context) "01: %.2X | Display %s, V-ints %s, Height: %d, Mode %d\n" "0B: %.2X | E-ints %s, V-Scroll: %s, H-Scroll: %s\n" "0C: %.2X | Width: %d, Shadow/Highlight: %s\n", - context->regs[REG_MODE_1], context->regs[REG_MODE_1] & BIT_HINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_1] & BIT_PAL_SEL != 0, + context->regs[REG_MODE_1], context->regs[REG_MODE_1] & BIT_HINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_1] & BIT_PAL_SEL != 0, context->regs[REG_MODE_1] & BIT_HVC_LATCH ? "enabled" : "disabled", context->regs[REG_MODE_1] & BIT_DISP_DIS ? "disabled" : "enabled", - context->regs[REG_MODE_2], context->regs[REG_MODE_2] & BIT_DISP_EN ? "enabled" : "disabled", context->regs[REG_MODE_2] & BIT_VINT_EN ? "enabled" : "disabled", + context->regs[REG_MODE_2], context->regs[REG_MODE_2] & BIT_DISP_EN ? "enabled" : "disabled", context->regs[REG_MODE_2] & BIT_VINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_2] & BIT_PAL ? 30 : 28, context->regs[REG_MODE_2] & BIT_MODE_5 ? 5 : 4, context->regs[REG_MODE_3], context->regs[REG_MODE_3] & BIT_EINT_EN ? "enabled" : "disabled", context->regs[REG_MODE_3] & BIT_VSCROLL ? "2 cell" : "full", hscroll[context->regs[REG_MODE_3] & 0x3], @@ -203,8 +203,8 @@ void vdp_print_reg_explain(vdp_context * context) "0A: %.2X | H-Int Counter: %u\n" "0F: %.2X | Auto-increment: $%X\n" "10: %.2X | Scroll A/B Size: %sx%s\n", - context->regs[REG_BG_COLOR], context->regs[REG_BG_COLOR] & 0x3F, - context->regs[REG_HINT], context->regs[REG_HINT], + context->regs[REG_BG_COLOR], context->regs[REG_BG_COLOR] & 0x3F, + context->regs[REG_HINT], context->regs[REG_HINT], context->regs[REG_AUTOINC], context->regs[REG_AUTOINC], context->regs[REG_SCROLL], sizes[context->regs[REG_SCROLL] & 0x3], sizes[context->regs[REG_SCROLL] >> 4 & 0x3]); printf("\n**Internal Group**\n" @@ -212,8 +212,8 @@ void vdp_print_reg_explain(vdp_context * context) "CD: %X\n" "Pending: %s\n", context->address, context->cd, (context->flags & FLAG_PENDING) ? "true" : "false"); - - //TODO: Window Group, DMA Group + + //TODO: Window Group, DMA Group } void scan_sprite_table(uint32_t line, vdp_context * context) @@ -295,7 +295,7 @@ void read_sprite_x(uint32_t line, vdp_context * context) height *= 2; } uint16_t att_addr = ((context->regs[REG_SAT] & 0x7F) << 9) + context->sprite_info_list[context->cur_slot].index * 8 + 4; - uint16_t tileinfo = (context->vdpmem[att_addr] << 8) | context->vdpmem[att_addr+1]; + uint16_t tileinfo = (context->vdpmem[att_addr] << 8) | context->vdpmem[att_addr+1]; uint8_t pal_priority = (tileinfo >> 9) & 0x70; uint8_t row; if (tileinfo & MAP_BIT_V_FLIP) { @@ -315,7 +315,7 @@ void read_sprite_x(uint32_t line, vdp_context * context) } else if(context->flags & (FLAG_CAN_MASK | FLAG_DOT_OFLOW)) { context->flags |= FLAG_MASKED; } - + context->flags &= ~FLAG_DOT_OFLOW; int16_t i; if (context->flags & FLAG_MASKED) { @@ -415,7 +415,7 @@ void external_slot(vdp_context * context) context->dma_val = (context->dma_val << 8) | ((context->dma_val >> 8) & 0xFF); break; case CRAM_WRITE: - write_cram(context, context->address, context->dma_val); + write_cram(context, context->address, context->dma_val); //printf("CRAM DMA Fill | %X set to %X at %d\n", (context->address/2) & (CRAM_SIZE-1), context->cram[(context->address/2) & (CRAM_SIZE-1)], context->cycles); break; case VSRAM_WRITE: @@ -478,13 +478,15 @@ void external_slot(vdp_context * context) context->regs[REG_DMALEN_H] = dma_len >> 8; context->regs[REG_DMALEN_L] = dma_len; if (!dma_len) { + printf("DMA end at cycle %d\n", context->cycles); context->flags &= ~FLAG_DMA_RUN; } } } else { fifo_entry * start = (context->fifo_end - FIFO_SIZE); if (context->fifo_cur != start && start->cycle <= context->cycles) { - if ((context->regs[REG_MODE_2] & BIT_DMA_ENABLE) && (context->cd & DMA_START)) { + if ((context->regs[REG_MODE_2] & BIT_DMA_ENABLE) && (context->cd & DMA_START) && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) { + printf("DMA fill started at %d\n", context->cycles); context->flags |= FLAG_DMA_RUN; context->dma_val = start->value; context->address = start->address; //undo auto-increment @@ -575,7 +577,7 @@ void read_map_scroll(uint16_t column, uint16_t vsram_off, uint32_t line, uint16_ address &= 0xF000; line_offset = (((line) >> vscroll_shift) * 64 * 2) & 0xFFF; mask = 0x7F; - + } else { address &= 0xF800; line_offset = (((line) >> vscroll_shift) * 32 * 2) & 0xFFF; @@ -751,7 +753,7 @@ void render_map_output(uint32_t line, int32_t col, vdp_context * context) } plane_b_off = context->buf_b_off - (context->hscroll_b & 0xF); //printf("A | tmp_buf offset: %d\n", 8 - (context->hscroll_a & 0x7)); - + if (context->regs[REG_MODE_4] & BIT_HILIGHT) { for (int i = 0; i < 16; ++plane_a_off, ++plane_b_off, ++sprite_buf, ++i) { uint8_t pixel; @@ -1402,9 +1404,9 @@ void vdp_run_context(vdp_context * context, uint32_t target_cycles) } if ((line < active_lines || (line == active_lines && linecyc < (context->latched_mode & BIT_H40 ? 64 : 80))) && context->regs[REG_MODE_2] & DISPLAY_ENABLE) { //first sort-of active line is treated as 255 internally - //it's used for gathering sprite info for line + //it's used for gathering sprite info for line line = (line - 1) & 0xFF; - + //Convert to slot number if (context->latched_mode & BIT_H40){ vdp_h40(line, slot, context); @@ -1458,7 +1460,7 @@ void vdp_run_dma_done(vdp_context * context, uint32_t target_cycles) int vdp_control_port_write(vdp_context * context, uint16_t value) { - //printf("control port write: %X\n", value); + printf("control port write: %X at %d\n", value, context->cycles); if (context->flags & FLAG_DMA_RUN) { return -1; } @@ -1466,13 +1468,14 @@ int vdp_control_port_write(vdp_context * context, uint16_t value) context->address = (context->address & 0x3FFF) | (value << 14); context->cd = (context->cd & 0x3) | ((value >> 2) & 0x3C); context->flags &= ~FLAG_PENDING; - //printf("New Address: %X, New CD: %X\n", context->address, context->cd); + printf("New Address: %X, New CD: %X\n", context->address, context->cd); if (context->cd & 0x20 && (context->regs[REG_MODE_2] & BIT_DMA_ENABLE)) { // if((context->regs[REG_DMASRC_H] & 0xC0) != 0x80) { //DMA copy or 68K -> VDP, transfer starts immediately context->flags |= FLAG_DMA_RUN; context->dma_cd = context->cd; + printf("DMA start at cycle %d\n", context->cycles); if (!(context->regs[REG_DMASRC_H] & 0x80)) { //printf("DMA Address: %X, New CD: %X, Source: %X, Length: %X\n", context->address, context->cd, (context->regs[REG_DMASRC_H] << 17) | (context->regs[REG_DMASRC_M] << 9) | (context->regs[REG_DMASRC_L] << 1), context->regs[REG_DMALEN_H] << 8 | context->regs[REG_DMALEN_L]); return 1; @@ -1480,7 +1483,7 @@ int vdp_control_port_write(vdp_context * context, uint16_t value) //printf("DMA Copy Address: %X, New CD: %X, Source: %X\n", context->address, context->cd, (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]); } } else { - //printf("DMA Fill Address: %X, New CD: %X\n", context->address, context->cd); + printf("DMA Fill Address: %X, New CD: %X\n", context->address, context->cd); } } } else { @@ -1488,7 +1491,7 @@ int vdp_control_port_write(vdp_context * context, uint16_t value) //Register write uint8_t reg = (value >> 8) & 0x1F; if (reg < VDP_REGS) { - //printf("register %d set to %X\n", reg, value & 0xFF); + printf("register %d set to %X\n", reg, value & 0xFF); context->regs[reg] = value; if (reg == REG_MODE_2) { //printf("Display is now %s\n", (context->regs[REG_MODE_2] & DISPLAY_ENABLE) ? "enabled" : "disabled"); @@ -1511,7 +1514,7 @@ int vdp_control_port_write(vdp_context * context, uint16_t value) int vdp_data_port_write(vdp_context * context, uint16_t value) { - //printf("data port write: %X\n", value); + printf("data port write: %X at %d\n", value, context->cycles); if (context->flags & FLAG_DMA_RUN) { return -1; } -- cgit v1.2.3 From addb532950e13115b29d888782ce3dac20d62664 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Mon, 2 Sep 2013 00:20:56 -0700 Subject: Adjust VBLANK flag and refresh timing to be in line with logic analyzer and visual observations of direct color DMA demos. Remove debug print statements. --- blastem.c | 6 +++--- vdp.c | 31 +++++++++++++++++++------------ 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/blastem.c b/blastem.c index 3e431c9..a2cae6c 100644 --- a/blastem.c +++ b/blastem.c @@ -221,7 +221,7 @@ m68k_context * sync_components(m68k_context * context, uint32_t address) if (gen->ym->write_cycle != CYCLE_NEVER) { gen->ym->write_cycle = gen->ym->write_cycle >= mclks_per_frame/MCLKS_PER_68K ? gen->ym->write_cycle - mclks_per_frame/MCLKS_PER_68K : 0; } - printf("reached frame end | 68K Cycles: %d, MCLK Cycles: %d\n", context->current_cycle, mclks); + //printf("reached frame end | 68K Cycles: %d, MCLK Cycles: %d\n", context->current_cycle, mclks); vdp_run_context(v_context, mclks_per_frame); if (!headless) { @@ -296,7 +296,7 @@ m68k_context * vdp_port_write(uint32_t vdp_port, m68k_context * context, uint16_ vdp_run_dma_done(v_context, mclks_per_frame); if (v_context->cycles >= mclks_per_frame) { if (!headless) { - printf("reached frame end | 68K Cycles: %d, MCLK Cycles: %d\n", context->current_cycle, v_context->cycles); + //printf("reached frame end | 68K Cycles: %d, MCLK Cycles: %d\n", context->current_cycle, v_context->cycles); wait_render_frame(v_context, frame_limit); } vdp_adjust_cycles(v_context, mclks_per_frame); @@ -356,7 +356,7 @@ m68k_context * vdp_port_write(uint32_t vdp_port, m68k_context * context, uint16_ exit(1); } if (v_context->cycles != before_cycle) { - printf("68K paused for %d cycles at cycle %d\n", v_context->cycles / MCLKS_PER_68K - context->current_cycle, context->current_cycle); + //printf("68K paused for %d cycles at cycle %d\n", v_context->cycles / MCLKS_PER_68K - context->current_cycle, context->current_cycle); context->current_cycle = v_context->cycles / MCLKS_PER_68K; } } else if (vdp_port < 0x18) { diff --git a/vdp.c b/vdp.c index ceffc77..76aea0d 100644 --- a/vdp.c +++ b/vdp.c @@ -478,7 +478,7 @@ void external_slot(vdp_context * context) context->regs[REG_DMALEN_H] = dma_len >> 8; context->regs[REG_DMALEN_L] = dma_len; if (!dma_len) { - printf("DMA end at cycle %d\n", context->cycles); + //printf("DMA end at cycle %d\n", context->cycles); context->flags &= ~FLAG_DMA_RUN; } } @@ -486,7 +486,7 @@ void external_slot(vdp_context * context) fifo_entry * start = (context->fifo_end - FIFO_SIZE); if (context->fifo_cur != start && start->cycle <= context->cycles) { if ((context->regs[REG_MODE_2] & BIT_DMA_ENABLE) && (context->cd & DMA_START) && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) { - printf("DMA fill started at %d\n", context->cycles); + //printf("DMA fill started at %d\n", context->cycles); context->flags |= FLAG_DMA_RUN; context->dma_val = start->value; context->address = start->address; //undo auto-increment @@ -1241,12 +1241,19 @@ void latch_mode(vdp_context * context) int is_refresh(vdp_context * context, uint32_t slot) { if (context->latched_mode & BIT_H40) { - //TODO: Figure out the exact behavior that reduces DMA slots for direct color DMA demos - return (slot == 37 || slot == 69 || slot == 102 || slot == 133 || slot == 165 || slot == 197 || slot >= 210 || (slot < 6 && (context->flags & FLAG_DMA_RUN) && ((context->dma_cd & 0xF) == CRAM_WRITE))); + //TODO: Determine behavior for DMA fills and copies + return (slot == 37 || slot == 69 || slot == 102 || slot == 133 || slot == 165 || slot == 197 || slot >= 210 + || ((context->flags & FLAG_DMA_RUN) && ((context->dma_cd & 0xF) != VRAM_WRITE)) && ( + //both of the missed reads occurred right next to each other, but there seems + //to be some buffering involved, these values produce similar artifacts + //to what I see on my Model 2 + slot == 34 || slot == 66 || slot == 99 || slot == 130 || slot == 162 || slot == 194)); } else { //TODO: Figure out which slots are refresh when display is off in 32-cell mode //These numbers are guesses based on H40 numbers - return (slot == 24 || slot == 56 || slot == 88 || slot == 120 || slot == 152 || (slot < 5 && (context->flags & FLAG_DMA_RUN) && ((context->dma_cd & 0xF) == CRAM_WRITE))); + return (slot == 24 || slot == 56 || slot == 88 || slot == 120 || slot == 152 + || ((context->flags & FLAG_DMA_RUN) && ((context->dma_cd & 0xF) != VRAM_WRITE)) && ( + slot == 21 || slot == 53 || slot == 85 || slot == 117 || slot == 149)); //The numbers below are the refresh slots during active display //return (slot == 66 || slot == 98 || slot == 130 || slot == 162); } @@ -1460,7 +1467,7 @@ void vdp_run_dma_done(vdp_context * context, uint32_t target_cycles) int vdp_control_port_write(vdp_context * context, uint16_t value) { - printf("control port write: %X at %d\n", value, context->cycles); + //printf("control port write: %X at %d\n", value, context->cycles); if (context->flags & FLAG_DMA_RUN) { return -1; } @@ -1468,14 +1475,14 @@ int vdp_control_port_write(vdp_context * context, uint16_t value) context->address = (context->address & 0x3FFF) | (value << 14); context->cd = (context->cd & 0x3) | ((value >> 2) & 0x3C); context->flags &= ~FLAG_PENDING; - printf("New Address: %X, New CD: %X\n", context->address, context->cd); + //printf("New Address: %X, New CD: %X\n", context->address, context->cd); if (context->cd & 0x20 && (context->regs[REG_MODE_2] & BIT_DMA_ENABLE)) { // if((context->regs[REG_DMASRC_H] & 0xC0) != 0x80) { //DMA copy or 68K -> VDP, transfer starts immediately context->flags |= FLAG_DMA_RUN; context->dma_cd = context->cd; - printf("DMA start at cycle %d\n", context->cycles); + //printf("DMA start at cycle %d\n", context->cycles); if (!(context->regs[REG_DMASRC_H] & 0x80)) { //printf("DMA Address: %X, New CD: %X, Source: %X, Length: %X\n", context->address, context->cd, (context->regs[REG_DMASRC_H] << 17) | (context->regs[REG_DMASRC_M] << 9) | (context->regs[REG_DMASRC_L] << 1), context->regs[REG_DMALEN_H] << 8 | context->regs[REG_DMALEN_L]); return 1; @@ -1483,7 +1490,7 @@ int vdp_control_port_write(vdp_context * context, uint16_t value) //printf("DMA Copy Address: %X, New CD: %X, Source: %X\n", context->address, context->cd, (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]); } } else { - printf("DMA Fill Address: %X, New CD: %X\n", context->address, context->cd); + //printf("DMA Fill Address: %X, New CD: %X\n", context->address, context->cd); } } } else { @@ -1491,7 +1498,7 @@ int vdp_control_port_write(vdp_context * context, uint16_t value) //Register write uint8_t reg = (value >> 8) & 0x1F; if (reg < VDP_REGS) { - printf("register %d set to %X\n", reg, value & 0xFF); + //printf("register %d set to %X\n", reg, value & 0xFF); context->regs[reg] = value; if (reg == REG_MODE_2) { //printf("Display is now %s\n", (context->regs[REG_MODE_2] & DISPLAY_ENABLE) ? "enabled" : "disabled"); @@ -1514,7 +1521,7 @@ int vdp_control_port_write(vdp_context * context, uint16_t value) int vdp_data_port_write(vdp_context * context, uint16_t value) { - printf("data port write: %X at %d\n", value, context->cycles); + //printf("data port write: %X at %d\n", value, context->cycles); if (context->flags & FLAG_DMA_RUN) { return -1; } @@ -1557,7 +1564,7 @@ uint16_t vdp_control_port_read(vdp_context * context) } uint32_t line= context->cycles / MCLKS_LINE; uint32_t linecyc = context->cycles % MCLKS_LINE; - if (line >= (context->latched_mode & BIT_PAL ? PAL_ACTIVE : NTSC_ACTIVE)) { + if (line >= (context->latched_mode & BIT_PAL ? PAL_ACTIVE : NTSC_ACTIVE) || context->cycles < (context->latched_mode & BIT_H40 ? 16*4 : 16*5)) { value |= 0x8; } if (linecyc < (context->latched_mode & BIT_H40 ? HBLANK_CLEAR_H40 : HBLANK_CLEAR_H32)) { -- cgit v1.2.3 From 437d581f120e762a70a254f5eae030e4eae87679 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Mon, 2 Sep 2013 01:02:18 -0700 Subject: Fix per-column scrolling bug --- vdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vdp.c b/vdp.c index 76aea0d..35b0acb 100644 --- a/vdp.c +++ b/vdp.c @@ -619,7 +619,7 @@ void read_map_scroll(uint16_t column, uint16_t vsram_off, uint32_t line, uint16_ vscroll <<= 1; vscroll |= 1; } - vscroll &= (context->vsram[(context->regs[REG_MODE_3] & BIT_VSCROLL ? column : 0) + vsram_off] + line); + vscroll &= (context->vsram[(context->regs[REG_MODE_3] & BIT_VSCROLL ? (column-2)&63 : 0) + vsram_off] + line); context->v_offset = vscroll & v_offset_mask; //printf("%s | line %d, vsram: %d, vscroll: %d, v_offset: %d\n",(vsram_off ? "B" : "A"), line, context->vsram[context->regs[REG_MODE_3] & 0x4 ? column : 0], vscroll, context->v_offset); vscroll >>= vscroll_shift; -- cgit v1.2.3 From dc502d20a102160fdbad0f5abc02a9f44e3143c0 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Mon, 2 Sep 2013 01:03:08 -0700 Subject: Added analysis script used for investigating direct color DMA timing --- analyze_olp.py | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100755 analyze_olp.py diff --git a/analyze_olp.py b/analyze_olp.py new file mode 100755 index 0000000..652edc4 --- /dev/null +++ b/analyze_olp.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python + +from zipfile import ZipFile +from sys import exit, argv + +def detect_rise(last, sample, bit): + mask = 1 << bit + return (not last & mask) and (sample & mask) + +def detect_fall(last, sample, bit): + mask = 1 << bit + return (last & mask) and (not sample & mask) + +def detect_high(sample, bit): + mask = 1 << bit + return sample & mask + +def detect_low(sample, bit): + mask = 1 << bit + return not sample & mask + +def analyze_delays(chanmap, datafile): + m68k_clk = chanmap['M68K CLK'] + m_as = chanmap['!AS'] + last = False + clks = 0 + as_start = 0 + for line in datafile.readlines(): + line = line.strip() + if line and not line.startswith(';'): + sample,_,num = line.partition('@') + sample = int(sample, 16) + if not (last is False): + if detect_rise(last, sample, m68k_clk): + clks = clks + 1 + if detect_rise(last, sample, m_as): + as_clks = clks - as_start + if as_clks > 2: + print '!AS held for', as_clks, 'cycles starting (delay of ' + str(as_clks - 2) + ') at', as_start, 'and ending at', clks + elif detect_fall(last, sample, m_as): + as_start = clks + last = sample + +def main(args): + if len(args) < 2: + print 'Usage: analyze_olp.py filename' + exit(1) + olpfile = ZipFile(args[1], "r") + channelfile = olpfile.open('channel.labels') + channels = [line.strip() for line in channelfile.readlines()] + channelfile.close() + chanmap = {} + for i in xrange(0, len(channels)): + chanmap[channels[i]] = i + datafile = olpfile.open('data.ols') + analyze_delays(chanmap, datafile) + + +if __name__ == '__main__': + main(argv) -- cgit v1.2.3 From 13c9ce6e0b47a724bf97584a0eafa3ad065f3741 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Wed, 4 Sep 2013 19:34:19 -0700 Subject: Initial work on GDB remote debugging support --- gdb_remote.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) create mode 100644 gdb_remote.c diff --git a/gdb_remote.c b/gdb_remote.c new file mode 100644 index 0000000..888bbb8 --- /dev/null +++ b/gdb_remote.c @@ -0,0 +1,123 @@ +#include "blastem.h" +#include +#include +#include +#include +#include + +#define INITIAL_BUFFER_SIZE 4096 + +char * buf = NULL; +char * curbuf = NULL; +size_t bufsize; +int cont = 0; +int expect_break_response=0; +uint32_t resume_pc; + +void gdb_debug_enter(genesis_context * gen, uint32_t pc) +{ + resume_pc = pc; + while(!cont) + { + } + cont = 0; +} + +void gdb_run_command(genesis_context * gen, char * command) +{ + switch(*command) + { + case 'c': + if (*(command+1) != 0) { + resume_pc = + } + cont = 1; + expect_break_response = 1; + break; + case 's': + + } +} + +void gdb_run_commands(genesis_context * gen) +{ + int enter_debugger = 0; + char * cur = buf; + while(cur < curbuf); + { + if(*cur == '$') { + cur++ + char * start = cur; + while (cur < curbuf && *cur != '#') { + cur++; + } + if (*cur == '#') { + //check to make sure we've received the checksum bytes + if (curbuf-cur >= 2) { + //TODO: verify checksum + //Null terminate payload + //send acknowledgement + write(FILENO_STDOUT, "+", 1); + gdb_run_command(genesis_context * gen, start); + cur += 2; + } else { + cur = start - 1; + break; + } + } else { + cur = start - 1; + break; + } + } else { + if (*cur == 0x03) { + enter_debugger = 1; + } + cur++; + } + } + + //FIXME + if (consumed == curbuf-buf) { + curbuf = buf; + } else if (consumed > 0) { + memmove(buf, buf + consumed, curbuf - buf - consumed); + curbuf -= consumed; + } +} + +void gdb_command_poll(genesis_context * gen) +{ + for(;;) + { + if (curbuf == buf + bufsize) { + //buffer is full, expand it + bufsize *= 2; + buf = realloc(buf, bufsize); + if (!buf) { + fprintf(stderr, "Failed to grow GDB command buffer to %d bytes\n", (int)bufsize); + exit(1); + } + curbuf = buf + bufsize/2; + } + int numread = read(STDIN_FILENO, buf, bufsize - (curbuf-buf)); + if (numread < 0) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + return; + } else { + fprintf(stderr, "Error %d while reading GDB commands from stdin", errno); + exit(1); + } + } else if (numread == 0) { + exit(0); + } + gdb_run_commands(genesis_context * gen); + } +} + +void gdb_remote_init() +{ + fcntl(STDIN_FILENO, FD_SETFL, O_NONBLOCK); + buf = malloc(INITIAL_BUFFER_SIZE); + curbuf = buf; + bufzie = INITIAL_BUFFER_SIZE; +} -- cgit v1.2.3 From 6f68fce01e5256d87c1f8ff48d3d397b646c4a4f Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Sun, 8 Sep 2013 20:46:25 -0700 Subject: Fix bit instruction timing --- m68k_to_x86.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/m68k_to_x86.c b/m68k_to_x86.c index 785dac3..e78fe0d 100644 --- a/m68k_to_x86.c +++ b/m68k_to_x86.c @@ -2952,7 +2952,9 @@ uint8_t * translate_m68k(uint8_t * dst, m68kinst * inst, x86_68k_options * opts) case M68K_BCLR: case M68K_BSET: case M68K_BTST: - dst = cycles(dst, inst->extra.size == OPSIZE_BYTE ? 4 : 6); + dst = cycles(dst, inst->extra.size == OPSIZE_BYTE ? 4 : ( + inst->op == M68K_BTST ? 6 : (inst->op == M68K_BCLR ? 10 : 8)) + ); if (src_op.mode == MODE_IMMED) { if (inst->extra.size == OPSIZE_BYTE) { src_op.disp &= 0x7; -- cgit v1.2.3 From 1b9e30c264d0af85666d89e8d62d054d847d2628 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Sun, 8 Sep 2013 20:47:01 -0700 Subject: Remove extra 68K/VDP cycle sync --- blastem.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/blastem.c b/blastem.c index a2cae6c..c093402 100644 --- a/blastem.c +++ b/blastem.c @@ -347,7 +347,6 @@ m68k_context * vdp_port_write(uint32_t vdp_port, m68k_context * context, uint16_ blocked = 0; } } - context->current_cycle = v_context->cycles / MCLKS_PER_68K; } else { adjust_int_cycle(context, v_context); } @@ -356,7 +355,7 @@ m68k_context * vdp_port_write(uint32_t vdp_port, m68k_context * context, uint16_ exit(1); } if (v_context->cycles != before_cycle) { - //printf("68K paused for %d cycles at cycle %d\n", v_context->cycles / MCLKS_PER_68K - context->current_cycle, context->current_cycle); + //printf("68K paused for %d (%d) cycles at cycle %d (%d)\n", v_context->cycles / MCLKS_PER_68K - context->current_cycle, v_context->cycles - before_cycle, context->current_cycle, before_cycle); context->current_cycle = v_context->cycles / MCLKS_PER_68K; } } else if (vdp_port < 0x18) { @@ -420,7 +419,6 @@ uint16_t vdp_port_read(uint32_t vdp_port, m68k_context * context) value = vdp_hv_counter_read(v_context); //printf("HV Counter: %X at cycle %d\n", value, v_context->cycles); } - context->current_cycle = v_context->cycles/MCLKS_PER_68K; } else { printf("Illegal read from PSG or test register port %X\n", vdp_port); exit(1); -- cgit v1.2.3 From 773d8bb91c3d4684e3e6a76e8584e6a97eda31ec Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Sun, 8 Sep 2013 20:48:33 -0700 Subject: Revert change to VBLANK flag timing based on new direct color DMA test --- vdp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vdp.c b/vdp.c index 35b0acb..829cb23 100644 --- a/vdp.c +++ b/vdp.c @@ -1564,7 +1564,7 @@ uint16_t vdp_control_port_read(vdp_context * context) } uint32_t line= context->cycles / MCLKS_LINE; uint32_t linecyc = context->cycles % MCLKS_LINE; - if (line >= (context->latched_mode & BIT_PAL ? PAL_ACTIVE : NTSC_ACTIVE) || context->cycles < (context->latched_mode & BIT_H40 ? 16*4 : 16*5)) { + if (line >= (context->latched_mode & BIT_PAL ? PAL_ACTIVE : NTSC_ACTIVE)) { value |= 0x8; } if (linecyc < (context->latched_mode & BIT_H40 ? HBLANK_CLEAR_H40 : HBLANK_CLEAR_H32)) { @@ -1576,6 +1576,7 @@ uint16_t vdp_control_port_read(vdp_context * context) if (context->latched_mode & BIT_PAL) {//Not sure about this, need to verify value |= 0x1; } + //printf("status read at cycle %d returned %X\n", context->cycles, value); //TODO: Sprite overflow, sprite collision, odd frame flag return value; } -- cgit v1.2.3 From 929bc24ebe9477fec8993be92b24dd0a96d001cd Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Tue, 10 Sep 2013 00:29:46 -0700 Subject: Implement FIFO latency and improve DMA accuracy --- vdp.c | 283 ++++++++++++++++++++++++++++-------------------------------------- vdp.h | 2 +- 2 files changed, 121 insertions(+), 164 deletions(-) diff --git a/vdp.c b/vdp.c index 35b0acb..af14ef4 100644 --- a/vdp.c +++ b/vdp.c @@ -27,6 +27,7 @@ #define HSYNC_END_H32 (33 * MCLKS_SLOT_H32) #define HBLANK_CLEAR_H40 (MCLK_WEIRD_END+61*4) #define HBLANK_CLEAR_H32 (HSYNC_END_H32 + 46*5) +#define FIFO_LATENCY 3 int32_t color_map[1 << 12]; uint8_t levels[] = {0, 27, 49, 71, 87, 103, 119, 130, 146, 157, 174, 190, 206, 228, 255}; @@ -121,6 +122,19 @@ void init_vdp_context(vdp_context * context) } } +int is_refresh(vdp_context * context, uint32_t slot) +{ + if (context->latched_mode & BIT_H40) { + return (slot == 37 || slot == 69 || slot == 102 || slot == 133 || slot == 165 || slot == 197 || slot >= 210); + } else { + //TODO: Figure out which slots are refresh when display is off in 32-cell mode + //These numbers are guesses based on H40 numbers + return (slot == 24 || slot == 56 || slot == 88 || slot == 120 || slot == 152); + //The numbers below are the refresh slots during active display + //return (slot == 66 || slot == 98 || slot == 130 || slot == 162); + } +} + void render_sprite_cells(vdp_context * context) { if (context->cur_slot >= context->sprite_draws) { @@ -371,162 +385,115 @@ void write_cram(vdp_context * context, uint16_t address, uint16_t value) void external_slot(vdp_context * context) { - //TODO: Figure out what happens if CD bit 4 is not set in DMA copy mode - //TODO: Figure out what happens when CD:0-3 is not set to a write mode in DMA operations - //TODO: Figure out what happens if DMA gets disabled part way through a DMA fill or DMA copy - if(context->flags & FLAG_DMA_RUN) { - uint16_t dma_len; - switch(context->regs[REG_DMASRC_H] & 0xC0) + fifo_entry * start = (context->fifo_end - FIFO_SIZE); + if (context->fifo_cur != start && start->cycle <= context->cycles) { + switch (start->cd & 0xF) { - //68K -> VDP - case 0: - case 0x40: - switch(context->dma_cd & 0xF) - { - case VRAM_WRITE: - if (context->flags & FLAG_DMA_PROG) { - context->vdpmem[context->address ^ 1] = context->dma_val; - context->flags &= ~FLAG_DMA_PROG; - } else { - context->dma_val = read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]); - context->vdpmem[context->address] = context->dma_val >> 8; - context->flags |= FLAG_DMA_PROG; - } - break; - case CRAM_WRITE: { - write_cram(context, context->address, read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L])); - //printf("CRAM DMA | %X set to %X from %X at %d\n", (context->address/2) & (CRAM_SIZE-1), context->cram[(context->address/2) & (CRAM_SIZE-1)], (context->regs[REG_DMASRC_H] << 17) | (context->regs[REG_DMASRC_M] << 9) | (context->regs[REG_DMASRC_L] << 1), context->cycles); - break; + case VRAM_WRITE: + if (start->partial) { + //printf("VRAM Write: %X to %X\n", start->value, context->address ^ 1); + context->vdpmem[start->address ^ 1] = start->value; + } else { + //printf("VRAM Write High: %X to %X\n", start->value >> 8, context->address); + context->vdpmem[start->address] = start->value >> 8; + start->partial = 1; + //skip auto-increment and removal of entry from fifo + return; } - case VSRAM_WRITE: - if (((context->address/2) & 63) < VSRAM_SIZE) { - context->vsram[(context->address/2) & 63] = read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]); - } - break; + break; + case CRAM_WRITE: { + //printf("CRAM Write | %X to %X\n", start->value, (start->address/2) & (CRAM_SIZE-1)); + write_cram(context, start->address, start->value); + break; + } + case VSRAM_WRITE: + if (((start->address/2) & 63) < VSRAM_SIZE) { + //printf("VSRAM Write: %X to %X\n", start->value, context->address); + context->vsram[(start->address/2) & 63] = start->value; } + break; - //Fill - case 0x80: + } + fifo_entry * cur = start+1; + if (cur < context->fifo_cur) { + memmove(start, cur, sizeof(fifo_entry) * (context->fifo_cur - cur)); + } + context->fifo_cur -= 1; + } else { + context->flags |= FLAG_UNUSED_SLOT; + } +} + +void run_dma_src(vdp_context * context, uint32_t slot) +{ + //TODO: Figure out what happens if CD bit 4 is not set in DMA copy mode + //TODO: Figure out what happens when CD:0-3 is not set to a write mode in DMA operations + //TODO: Figure out what happens if DMA gets disabled part way through a DMA fill or DMA copy + if (context->fifo_cur == context->fifo_end) { + return; + } + uint16_t read_val; + uint8_t ran_source = 0, partial = 0; + uint16_t dma_len; + switch(context->regs[REG_DMASRC_H] & 0xC0) + { + //68K -> VDP + case 0: + case 0x40: + if (!slot || !is_refresh(context, slot-1)) { + read_val = read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]); + ran_source = 1; + } + break; + //Copy + case 0xC0: + if (context->flags & FLAG_UNUSED_SLOT) { switch(context->dma_cd & 0xF) { case VRAM_WRITE: - //Charles MacDonald's VDP doc says that the low byte gets written first - context->vdpmem[context->address] = context->dma_val; - context->dma_val = (context->dma_val << 8) | ((context->dma_val >> 8) & 0xFF); + read_val = context->vdpmem[(context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]]; break; case CRAM_WRITE: - write_cram(context, context->address, context->dma_val); - //printf("CRAM DMA Fill | %X set to %X at %d\n", (context->address/2) & (CRAM_SIZE-1), context->cram[(context->address/2) & (CRAM_SIZE-1)], context->cycles); + read_val = context->cram[context->regs[REG_DMASRC_L] & (CRAM_SIZE-1)]; break; case VSRAM_WRITE: - if (((context->address/2) & 63) < VSRAM_SIZE) { - context->vsram[(context->address/2) & 63] = context->dma_val; + if ((context->regs[REG_DMASRC_L] & 63) < VSRAM_SIZE) { + read_val = context->vsram[context->regs[REG_DMASRC_L] & 63]; + } else { + read_val = 0; } break; } - break; - //Copy - case 0xC0: - if (context->flags & FLAG_DMA_PROG) { - switch(context->dma_cd & 0xF) - { - case VRAM_WRITE: - context->vdpmem[context->address] = context->dma_val; - break; - case CRAM_WRITE: { - write_cram(context, context->address, context->dma_val); - //printf("CRAM DMA Copy | %X set to %X from %X at %d\n", (context->address/2) & (CRAM_SIZE-1), context->cram[(context->address/2) & (CRAM_SIZE-1)], context->regs[REG_DMASRC_L] & (CRAM_SIZE-1), context->cycles); - break; - } - case VSRAM_WRITE: - if (((context->address/2) & 63) < VSRAM_SIZE) { - context->vsram[(context->address/2) & 63] = context->dma_val; - } - break; - } - context->flags &= ~FLAG_DMA_PROG; - } else { - //I assume, that DMA copy copies from the same RAM as the destination - //but it's possible I'm mistaken - switch(context->dma_cd & 0xF) - { - case VRAM_WRITE: - context->dma_val = context->vdpmem[(context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]]; - break; - case CRAM_WRITE: - context->dma_val = context->cram[context->regs[REG_DMASRC_L] & (CRAM_SIZE-1)]; - break; - case VSRAM_WRITE: - if ((context->regs[REG_DMASRC_L] & 63) < VSRAM_SIZE) { - context->dma_val = context->vsram[context->regs[REG_DMASRC_L] & 63]; - } else { - context->dma_val = 0; - } - break; - } - context->flags |= FLAG_DMA_PROG; - } - break; + ran_source = 1; + context->flags &= ~FLAG_UNUSED_SLOT; } - if (!(context->flags & FLAG_DMA_PROG)) { - context->address += context->regs[REG_AUTOINC]; - context->regs[REG_DMASRC_L] += 1; - if (!context->regs[REG_DMASRC_L]) { - context->regs[REG_DMASRC_M] += 1; - } - dma_len = ((context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L]) - 1; - context->regs[REG_DMALEN_H] = dma_len >> 8; - context->regs[REG_DMALEN_L] = dma_len; - if (!dma_len) { - //printf("DMA end at cycle %d\n", context->cycles); - context->flags &= ~FLAG_DMA_RUN; - } + break; + case 0x80: + read_val = (context->cd & 0xF) == VRAM_WRITE ? context->last_write_val >> 8 : context->last_write_val; + partial = 1; + ran_source = 1; + break; + } + + if (ran_source) { + context->fifo_cur->cycle = context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)*FIFO_LATENCY; + context->fifo_cur->address = context->address; + context->fifo_cur->value = read_val; + context->fifo_cur->cd = context->cd; + context->fifo_cur->partial = partial; + context->fifo_cur++; + context->regs[REG_DMASRC_L] += 1; + if (!context->regs[REG_DMASRC_L]) { + context->regs[REG_DMASRC_M] += 1; } - } else { - fifo_entry * start = (context->fifo_end - FIFO_SIZE); - if (context->fifo_cur != start && start->cycle <= context->cycles) { - if ((context->regs[REG_MODE_2] & BIT_DMA_ENABLE) && (context->cd & DMA_START) && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) { - //printf("DMA fill started at %d\n", context->cycles); - context->flags |= FLAG_DMA_RUN; - context->dma_val = start->value; - context->address = start->address; //undo auto-increment - context->dma_cd = context->cd; - } else { - switch (start->cd & 0xF) - { - case VRAM_WRITE: - if (start->partial) { - //printf("VRAM Write: %X to %X\n", start->value, context->address ^ 1); - context->vdpmem[start->address ^ 1] = start->value; - } else { - //printf("VRAM Write High: %X to %X\n", start->value >> 8, context->address); - context->vdpmem[start->address] = start->value >> 8; - start->partial = 1; - //skip auto-increment and removal of entry from fifo - return; - } - break; - case CRAM_WRITE: { - //printf("CRAM Write | %X to %X\n", start->value, (start->address/2) & (CRAM_SIZE-1)); - write_cram(context, start->address, start->value); - break; - } - case VSRAM_WRITE: - if (((start->address/2) & 63) < VSRAM_SIZE) { - //printf("VSRAM Write: %X to %X\n", start->value, context->address); - context->vsram[(start->address/2) & 63] = start->value; - } - break; - } - //context->address += context->regs[REG_AUTOINC]; - } - fifo_entry * cur = start+1; - if (cur < context->fifo_cur) { - memmove(start, cur, sizeof(fifo_entry) * (context->fifo_cur - cur)); - } - context->fifo_cur -= 1; - } else { - context->flags |= FLAG_UNUSED_SLOT; + context->address += context->regs[REG_AUTOINC]; + dma_len = ((context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L]) - 1; + context->regs[REG_DMALEN_H] = dma_len >> 8; + context->regs[REG_DMALEN_L] = dma_len; + if (!dma_len) { + //printf("DMA end at cycle %d\n", context->cycles); + context->flags &= ~FLAG_DMA_RUN; + context->cd &= 0xF; } } } @@ -1238,27 +1205,6 @@ void latch_mode(vdp_context * context) context->latched_mode = (context->regs[REG_MODE_4] & 0x81) | (context->regs[REG_MODE_2] & BIT_PAL); } -int is_refresh(vdp_context * context, uint32_t slot) -{ - if (context->latched_mode & BIT_H40) { - //TODO: Determine behavior for DMA fills and copies - return (slot == 37 || slot == 69 || slot == 102 || slot == 133 || slot == 165 || slot == 197 || slot >= 210 - || ((context->flags & FLAG_DMA_RUN) && ((context->dma_cd & 0xF) != VRAM_WRITE)) && ( - //both of the missed reads occurred right next to each other, but there seems - //to be some buffering involved, these values produce similar artifacts - //to what I see on my Model 2 - slot == 34 || slot == 66 || slot == 99 || slot == 130 || slot == 162 || slot == 194)); - } else { - //TODO: Figure out which slots are refresh when display is off in 32-cell mode - //These numbers are guesses based on H40 numbers - return (slot == 24 || slot == 56 || slot == 88 || slot == 120 || slot == 152 - || ((context->flags & FLAG_DMA_RUN) && ((context->dma_cd & 0xF) != VRAM_WRITE)) && ( - slot == 21 || slot == 53 || slot == 85 || slot == 117 || slot == 149)); - //The numbers below are the refresh slots during active display - //return (slot == 66 || slot == 98 || slot == 130 || slot == 162); - } -} - void check_render_bg(vdp_context * context, int32_t line, uint32_t slot) { if (line > 0) { @@ -1299,6 +1245,7 @@ void vdp_run_context(vdp_context * context, uint32_t target_cycles) { while(context->cycles < target_cycles) { + context->flags &= ~FLAG_UNUSED_SLOT; uint32_t line = context->cycles / MCLKS_LINE; uint32_t active_lines = context->latched_mode & BIT_PAL ? PAL_ACTIVE : NTSC_ACTIVE; if (!context->cycles) { @@ -1428,6 +1375,9 @@ void vdp_run_context(vdp_context * context, uint32_t target_cycles) check_render_bg(context, line, slot); } } + if (context->flags & FLAG_DMA_RUN && !is_refresh(context, slot)) { + run_dma_src(context, slot); + } context->cycles += inccycles; } } @@ -1522,7 +1472,7 @@ int vdp_control_port_write(vdp_context * context, uint16_t value) int vdp_data_port_write(vdp_context * context, uint16_t value) { //printf("data port write: %X at %d\n", value, context->cycles); - if (context->flags & FLAG_DMA_RUN) { + if (context->flags & FLAG_DMA_RUN && (context->regs[REG_DMASRC_H] & 0xC0) != 0x80) { return -1; } if (!(context->cd & 1)) { @@ -1533,12 +1483,19 @@ int vdp_data_port_write(vdp_context * context, uint16_t value) /*if (context->fifo_cur == context->fifo_end) { printf("FIFO full, waiting for space before next write at cycle %X\n", context->cycles); }*/ + if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) { + context->flags &= ~FLAG_DMA_RUN; + } while (context->fifo_cur == context->fifo_end) { vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)); } - context->fifo_cur->cycle = context->cycles; + context->fifo_cur->cycle = context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)*FIFO_LATENCY; context->fifo_cur->address = context->address; context->fifo_cur->value = value; + context->last_write_val = value; + if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) { + context->flags |= FLAG_DMA_RUN; + } context->fifo_cur->cd = context->cd; context->fifo_cur->partial = 0; context->fifo_cur++; diff --git a/vdp.h b/vdp.h index 15f86b1..eda72fe 100644 --- a/vdp.h +++ b/vdp.h @@ -143,7 +143,7 @@ typedef struct { sprite_info sprite_info_list[MAX_SPRITES_LINE]; uint16_t col_1; uint16_t col_2; - uint16_t dma_val; + uint16_t last_write_val; uint8_t v_offset; uint8_t dma_cd; uint8_t hint_counter; -- cgit v1.2.3 From ef94225ba181d6bf6c164731ec101356ab4d5220 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Tue, 10 Sep 2013 09:55:12 -0700 Subject: Fix timing of backdrop rendering when the display is turned off --- vdp.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/vdp.c b/vdp.c index 04fd705..74c0857 100644 --- a/vdp.c +++ b/vdp.c @@ -1211,14 +1211,20 @@ void check_render_bg(vdp_context * context, int32_t line, uint32_t slot) line -= 1; int starti = -1; if (context->latched_mode & BIT_H40) { - if (slot >= 50 && slot < 210) { - uint32_t x = (slot-50)*2; + if (slot >= 55 && slot < 210) { + uint32_t x = (slot-55)*2; starti = line * 320 + x; + } else if (slot < 5) { + uint32_t x = (slot + 155)*2; + starti = (line-1)*320 + x; } } else { - if (slot >= 43 && slot < 171) { - uint32_t x = (slot-43)*2; + if (slot >= 48 && slot < 171) { + uint32_t x = (slot-48)*2; starti = line * 320 + x; + } else if (slot < 5) { + uint32_t x = (slot + 123)*2; + starti = (line-1)*320 + x; } } if (starti >= 0) { -- cgit v1.2.3 From 53144fd4940eff16e04a129286290868039bc666 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Tue, 10 Sep 2013 20:32:59 -0700 Subject: Added -h help text option --- blastem.c | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/blastem.c b/blastem.c index c093402..4a6cff1 100644 --- a/blastem.c +++ b/blastem.c @@ -1724,11 +1724,7 @@ void detect_region() int main(int argc, char ** argv) { if (argc < 2) { - fputs("Usage: blastem FILENAME [options]\n", stderr); - return 1; - } - if(!load_rom(argv[1])) { - fprintf(stderr, "Failed to open %s for reading\n", argv[1]); + fputs("Usage: blastem ROMFILE [OPTIONS] [WIDTH] [HEIGHT]\n", stderr); return 1; } config = load_config(argv[0]); @@ -1737,10 +1733,11 @@ int main(int argc, char ** argv) int height = -1; int debug = 0; int ym_log = 0; + int loaded = 0; FILE *address_log = NULL; char * statefile = NULL; uint8_t fullscreen = 0; - for (int i = 2; i < argc; i++) { + for (int i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch(argv[i][1]) { case 'd': @@ -1752,9 +1749,9 @@ int main(int argc, char ** argv) case 'l': address_log = fopen("address.log", "w"); break; - case 'v': - headless = 1; - break; +// case 'v': +// headless = 1; +// break; case 'n': z80_enabled = 0; break; @@ -1794,16 +1791,40 @@ int main(int argc, char ** argv) case 'y': ym_log = 1; break; + case 'h': + puts( + "Usage: blastem ROMFILE [OPTIONS] [WIDTH] [HEIGHT]\n" + "Options:\n" + " -h Print this help text\n" + " -r (J|U|E) Force region to Japan, US or Europe respectively\n" + " -f Start in fullscreen mode\n" + " -s FILE Load a GST format savestate from FILE\n" + " -d Enter debugger on startup\n" + " -n Disable Z80\n" + " -l Log 68K code addresses (useful for assemblers)\n" + " -y Log individual YM-2612 channels to WAVE files\n" + ); + return 0; default: fprintf(stderr, "Unrecognized switch %s\n", argv[i]); return 1; } + } else if (!loaded) { + if(!load_rom(argv[i])) { + fprintf(stderr, "Failed to open %s for reading\n", argv[1]); + return 1; + } + loaded = 1; } else if (width < 0) { width = atoi(argv[i]); } else if (height < 0) { height = atoi(argv[i]); } } + if (!loaded) { + fputs("You must specify a ROM filename!\n", stderr); + return 1; + } update_title(); int def_width = 0; char *config_width = tern_find_ptr(config, "videowidth"); -- cgit v1.2.3 From 88d818869f619b915dcac1ec472402bfb308dff3 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Tue, 10 Sep 2013 20:36:05 -0700 Subject: Added version flag --- blastem.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/blastem.c b/blastem.c index 4a6cff1..96896e8 100644 --- a/blastem.c +++ b/blastem.c @@ -10,6 +10,8 @@ #include #include +#define BLASTEM_VERSION "0.1.0" + #define CARTRIDGE_WORDS 0x200000 #define RAM_WORDS 32 * 1024 #define Z80_RAM_BYTES 8 * 1024 @@ -1749,9 +1751,10 @@ int main(int argc, char ** argv) case 'l': address_log = fopen("address.log", "w"); break; -// case 'v': -// headless = 1; -// break; + case 'v': + printf("blastem %s\n", BLASTEM_VERSION); + return 0; + break; case 'n': z80_enabled = 0; break; @@ -1801,6 +1804,7 @@ int main(int argc, char ** argv) " -s FILE Load a GST format savestate from FILE\n" " -d Enter debugger on startup\n" " -n Disable Z80\n" + " -v Display version number and exit\n" " -l Log 68K code addresses (useful for assemblers)\n" " -y Log individual YM-2612 channels to WAVE files\n" ); -- cgit v1.2.3 From c624a02df2f2086f9fe86ec05659959d2f25a580 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Tue, 10 Sep 2013 21:07:13 -0700 Subject: Fix stateview. Update "all" target in Makefile. --- Makefile | 6 +++--- stateview.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 81f33d3..c6d5f6d 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ M68KOBJS=68kinst.o m68k_to_x86.o runtime.o Z80OBJS=z80inst.o z80_to_x86.o zruntime.o AUDIOOBJS=ym2612.o psg.o wave.o -all : dis trans stateview blastem +all : dis zdis stateview vgmplay blastem blastem : blastem.o vdp.o render_sdl.o io.o config.o tern.o gst.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS) $(CC) -ggdb -o blastem blastem.o vdp.o render_sdl.o io.o config.o tern.o gst.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS) $(LDFLAGS) @@ -42,8 +42,8 @@ ztestrun : ztestrun.o $(Z80OBJS) $(TRANSOBJS) ztestgen : ztestgen.o z80inst.o $(CC) -o ztestgen ztestgen.o z80inst.o -stateview : stateview.o vdp.o render_sdl.o - $(CC) -o stateview stateview.o vdp.o render_sdl.o `pkg-config --libs $(LIBS)` +stateview : stateview.o vdp.o render_sdl.o config.o tern.o gst.o + $(CC) -o stateview stateview.o vdp.o render_sdl.o config.o tern.o gst.o `pkg-config --libs $(LIBS)` vgmplay : vgmplay.o render_sdl.o $(AUDIOOBJS) $(CC) -o vgmplay vgmplay.o render_sdl.o $(AUDIOOBJS) `pkg-config --libs $(LIBS)` diff --git a/stateview.c b/stateview.c index 10120e9..c3a85d6 100644 --- a/stateview.c +++ b/stateview.c @@ -7,12 +7,48 @@ //not used, but referenced by the renderer since it handles input io_port gamepad_1; io_port gamepad_2; +uint8_t reset = 1; +uint8_t busreq = 0; uint16_t read_dma_value(uint32_t address) { return 0; } +void ym_data_write(ym2612_context * context, uint8_t value) +{ +} + +void ym_address_write_part1(ym2612_context * context, uint8_t address) +{ +} + +void ym_address_write_part2(ym2612_context * context, uint8_t address) +{ +} + +void handle_keydown(int keycode) +{ +} + +void handle_keyup(int keycode) +{ +} + +void handle_joydown(int joystick, int button) +{ +} + +void handle_joyup(int joystick, int button) +{ +} + +void handle_joy_dpad(int joystick, int dpadnum, uint8_t value) +{ +} + +tern_node * config; + int main(int argc, char ** argv) { if (argc < 2) { @@ -24,23 +60,33 @@ int main(int argc, char ** argv) fprintf(stderr, "Failed to open %s\n", argv[1]); exit(1); } - int width = 320; - int height = 240; + config = load_config(argv[0]); + int width = -1; + int height = -1; if (argc > 2) { width = atoi(argv[2]); if (argc > 3) { height = atoi(argv[3]); - } else { - height = (width/320) * 240; } } + int def_width = 0; + char *config_width = tern_find_ptr(config, "videowidth"); + if (config_width) { + def_width = atoi(config_width); + } + if (!def_width) { + def_width = 640; + } + width = width < 320 ? def_width : width; + height = height < 240 ? (width/320) * 240 : height; + vdp_context context; + render_init(width, height, "GST State Viewer", 60, 0); init_vdp_context(&context); vdp_load_gst(&context, state_file); vdp_run_to_vblank(&context); vdp_print_sprite_table(&context); printf("Display %s\n", (context.regs[REG_MODE_2] & DISPLAY_ENABLE) ? "enabled" : "disabled"); - render_init(width, height); render_context(&context); render_wait_quit(&context); return 0; -- cgit v1.2.3 From e9ad8ed37b736c91d0bece08a648decbe3bccc56 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Tue, 10 Sep 2013 21:20:54 -0700 Subject: Fix vgmplay --- Makefile | 4 ++-- vgmplay.c | 31 +++++++++++++++++++++++-------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/Makefile b/Makefile index c6d5f6d..40c070a 100644 --- a/Makefile +++ b/Makefile @@ -45,8 +45,8 @@ ztestgen : ztestgen.o z80inst.o stateview : stateview.o vdp.o render_sdl.o config.o tern.o gst.o $(CC) -o stateview stateview.o vdp.o render_sdl.o config.o tern.o gst.o `pkg-config --libs $(LIBS)` -vgmplay : vgmplay.o render_sdl.o $(AUDIOOBJS) - $(CC) -o vgmplay vgmplay.o render_sdl.o $(AUDIOOBJS) `pkg-config --libs $(LIBS)` +vgmplay : vgmplay.o render_sdl.o config.o tern.o $(AUDIOOBJS) + $(CC) -o vgmplay vgmplay.o render_sdl.o config.o tern.o $(AUDIOOBJS) $(LDFLAGS) testgst : testgst.o gst.o $(CC) -o testgst testgst.o gst.o diff --git a/vgmplay.c b/vgmplay.c index 98fb7f1..05777d9 100644 --- a/vgmplay.c +++ b/vgmplay.c @@ -1,6 +1,7 @@ #include "render.h" #include "ym2612.h" #include "psg.h" +#include "config.h" #include #include @@ -72,14 +73,27 @@ void handle_keyup(int keycode) { } +void handle_joydown(int joystick, int button) +{ +} + +void handle_joyup(int joystick, int button) +{ +} + +void handle_joy_dpad(int joystick, int dpadnum, uint8_t value) +{ +} + #define CYCLE_LIMIT MCLKS_NTSC/60 +tern_node * config; void wait(ym2612_context * y_context, psg_context * p_context, uint32_t * current_cycle, uint32_t cycles) { *current_cycle += cycles; psg_run(p_context, *current_cycle); ym_run(y_context, *current_cycle); - + if (*current_cycle > CYCLE_LIMIT) { *current_cycle -= CYCLE_LIMIT; p_context->cycles -= CYCLE_LIMIT; @@ -91,15 +105,16 @@ void wait(ym2612_context * y_context, psg_context * p_context, uint32_t * curren int main(int argc, char ** argv) { uint32_t fps = 60; - render_init(320, 240, "vgm play", 60); - - + config = load_config(argv[0]); + render_init(320, 240, "vgm play", 60, 0); + + ym2612_context y_context; ym_init(&y_context, render_sample_rate(), MCLKS_NTSC, MCLKS_PER_YM, render_audio_buffer(), 0); - + psg_context p_context; psg_init(&p_context, render_sample_rate(), MCLKS_NTSC, MCLKS_PER_PSG, render_audio_buffer()); - + FILE * f = fopen(argv[1], "rb"); vgm_header header; fread(&header, sizeof(header), 1, f); @@ -111,9 +126,9 @@ int main(int argc, char ** argv) uint8_t * data = malloc(data_size); fread(data, 1, data_size, f); fclose(f); - + uint32_t mclks_sample = MCLKS_NTSC / 44100; - + uint8_t * end = data + data_size; uint8_t * cur = data; uint32_t current_cycle = 0; -- cgit v1.2.3 From 2c302a78d201d9b594774cec505d14c22e03662c Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Tue, 10 Sep 2013 23:31:08 -0700 Subject: Added copyright notice to source files and added GPL license text in COPYING --- 68kinst.c | 5 + 68kinst.h | 5 + COPYING | 674 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ blastem.c | 5 + blastem.h | 5 + config.c | 5 + config.h | 5 + dis.c | 5 + gdb_remote.c | 5 + gen_x86.c | 5 + gen_x86.h | 5 + gst.c | 5 + gst.h | 5 + io.c | 5 + io.h | 5 + m68k_to_x86.c | 5 + m68k_to_x86.h | 5 + mem.c | 5 + mem.h | 5 + psg.c | 5 + psg.h | 5 + render.h | 5 + render_sdl.c | 5 + stateview.c | 5 + tern.c | 5 + tern.h | 5 + test_x86.c | 5 + testgst.c | 5 + testtern.c | 5 + trans.c | 5 + transz80.c | 5 + vdp.c | 5 + vdp.h | 5 + vgmplay.c | 5 + wave.c | 5 + wave.h | 5 + x86_backend.c | 5 + x86_backend.h | 5 + ym2612.c | 5 + ym2612.h | 5 + z80_to_x86.c | 5 + z80_to_x86.h | 5 + z80inst.c | 5 + z80inst.h | 5 + zdis.c | 5 + ztestgen.c | 5 + ztestrun.c | 5 + 47 files changed, 904 insertions(+) create mode 100644 COPYING diff --git a/68kinst.c b/68kinst.c index 4f469b8..e72b66b 100644 --- a/68kinst.c +++ b/68kinst.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "68kinst.h" #include #include diff --git a/68kinst.h b/68kinst.h index 96210b4..0dc5a3f 100644 --- a/68kinst.h +++ b/68kinst.h @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef M68KINST_H_ #define M68KINST_H_ diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/blastem.c b/blastem.c index 96896e8..73fc053 100644 --- a/blastem.c +++ b/blastem.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "68kinst.h" #include "m68k_to_x86.h" #include "z80_to_x86.h" diff --git a/blastem.h b/blastem.h index da7953a..68bccfd 100644 --- a/blastem.h +++ b/blastem.h @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef BLASTEM_H_ #define BLASTEM_H_ diff --git a/config.c b/config.c index d106f27..bff974d 100644 --- a/config.c +++ b/config.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "tern.h" #include #include diff --git a/config.h b/config.h index 3850c53..b520d19 100644 --- a/config.h +++ b/config.h @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef CONFIG_H_ #define CONFIG_H_ #include "tern.h" diff --git a/dis.c b/dis.c index 99355eb..8ec5bb2 100644 --- a/dis.c +++ b/dis.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "68kinst.h" #include #include diff --git a/gdb_remote.c b/gdb_remote.c index 888bbb8..c58d534 100644 --- a/gdb_remote.c +++ b/gdb_remote.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "blastem.h" #include #include diff --git a/gen_x86.c b/gen_x86.c index aa68700..096059f 100644 --- a/gen_x86.c +++ b/gen_x86.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "gen_x86.h" #include "68kinst.h" #include diff --git a/gen_x86.h b/gen_x86.h index b47bae4..aa9d851 100644 --- a/gen_x86.h +++ b/gen_x86.h @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef GEN_X86_H_ #define GEN_X86_H_ diff --git a/gst.c b/gst.c index ac651dd..adf034d 100644 --- a/gst.c +++ b/gst.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "gst.h" #include diff --git a/gst.h b/gst.h index ff5d98c..67bec11 100644 --- a/gst.h +++ b/gst.h @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef GST_H_ #define GST_H_ #include "blastem.h" diff --git a/io.c b/io.c index 03875c5..b9e95c1 100644 --- a/io.c +++ b/io.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "io.h" #include "blastem.h" #include "render.h" diff --git a/io.h b/io.h index 65b81b2..b0fd1e9 100644 --- a/io.h +++ b/io.h @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef IO_H_ #define IO_H_ #include diff --git a/m68k_to_x86.c b/m68k_to_x86.c index e78fe0d..a9e71e4 100644 --- a/m68k_to_x86.c +++ b/m68k_to_x86.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "gen_x86.h" #include "m68k_to_x86.h" #include "68kinst.h" diff --git a/m68k_to_x86.h b/m68k_to_x86.h index c2408f4..68009c8 100644 --- a/m68k_to_x86.h +++ b/m68k_to_x86.h @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef M68K_TO_X86_H_ #define M68K_TO_X86_H_ #include diff --git a/mem.c b/mem.c index cc07985..d019c6f 100644 --- a/mem.c +++ b/mem.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include #include #include diff --git a/mem.h b/mem.h index 22b4a79..69e4f09 100644 --- a/mem.h +++ b/mem.h @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef MEM_H_ #define MEM_H_ diff --git a/psg.c b/psg.c index f615151..036a693 100644 --- a/psg.c +++ b/psg.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "psg.h" #include "render.h" #include diff --git a/psg.h b/psg.h index 295a319..503bac2 100644 --- a/psg.h +++ b/psg.h @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef PSG_CONTEXT_H_ #define PSG_CONTEXT_H_ diff --git a/render.h b/render.h index fb3e596..acca888 100644 --- a/render.h +++ b/render.h @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef RENDER_H_ #define RENDER_H_ diff --git a/render_sdl.c b/render_sdl.c index 408c821..8954deb 100644 --- a/render_sdl.c +++ b/render_sdl.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include #include #include "render.h" diff --git a/stateview.c b/stateview.c index c3a85d6..ee34407 100644 --- a/stateview.c +++ b/stateview.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include #include #include "vdp.h" diff --git a/tern.c b/tern.c index 7e60a22..96a0985 100644 --- a/tern.c +++ b/tern.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "tern.h" #include #include diff --git a/tern.h b/tern.h index c731bf6..e727599 100644 --- a/tern.h +++ b/tern.h @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef TERN_H_ #define TERN_H_ diff --git a/test_x86.c b/test_x86.c index bad0e1e..e13a814 100644 --- a/test_x86.c +++ b/test_x86.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "gen_x86.h" #include "m68k_to_x86.h" #include diff --git a/testgst.c b/testgst.c index 9b0a88b..88b31b8 100644 --- a/testgst.c +++ b/testgst.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "gst.h" #include #include diff --git a/testtern.c b/testtern.c index 6898371..b707f25 100644 --- a/testtern.c +++ b/testtern.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "tern.h" #include #include diff --git a/trans.c b/trans.c index 33f4dd5..ac4bf22 100644 --- a/trans.c +++ b/trans.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "68kinst.h" #include "m68k_to_x86.h" #include "mem.h" diff --git a/transz80.c b/transz80.c index 2c8c417..80f3f33 100644 --- a/transz80.c +++ b/transz80.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "z80inst.h" #include "z80_to_x86.h" #include "mem.h" diff --git a/vdp.c b/vdp.c index 74c0857..cf27ff2 100644 --- a/vdp.c +++ b/vdp.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "vdp.h" #include "blastem.h" #include diff --git a/vdp.h b/vdp.h index eda72fe..79f28c0 100644 --- a/vdp.h +++ b/vdp.h @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef VDP_H_ #define VDP_H_ diff --git a/vgmplay.c b/vgmplay.c index 05777d9..81c7ae7 100644 --- a/vgmplay.c +++ b/vgmplay.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "render.h" #include "ym2612.h" #include "psg.h" diff --git a/wave.c b/wave.c index cf3d11e..af0b8b7 100644 --- a/wave.c +++ b/wave.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "wave.h" #include #include diff --git a/wave.h b/wave.h index 4fa2440..01808bf 100644 --- a/wave.h +++ b/wave.h @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef WAVE_H_ #define WAVE_H_ diff --git a/x86_backend.c b/x86_backend.c index e3fe0ce..c5c441a 100644 --- a/x86_backend.c +++ b/x86_backend.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "x86_backend.h" #include diff --git a/x86_backend.h b/x86_backend.h index 02b9ad2..57c1e21 100644 --- a/x86_backend.h +++ b/x86_backend.h @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef X86_BACKEND_H_ #define X86_BACKEND_H_ diff --git a/ym2612.c b/ym2612.c index fda0249..a1ab1a0 100644 --- a/ym2612.c +++ b/ym2612.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include #include #include diff --git a/ym2612.h b/ym2612.h index b693507..8516492 100644 --- a/ym2612.h +++ b/ym2612.h @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef YM2612_H_ #define YM2612_H_ diff --git a/z80_to_x86.c b/z80_to_x86.c index 7f0ccf3..63574fa 100644 --- a/z80_to_x86.c +++ b/z80_to_x86.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "z80inst.h" #include "z80_to_x86.h" #include "gen_x86.h" diff --git a/z80_to_x86.h b/z80_to_x86.h index e45a823..dd5864b 100644 --- a/z80_to_x86.h +++ b/z80_to_x86.h @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef Z80_TO_X86_H_ #define Z80_TO_X86_H_ #include "z80inst.h" diff --git a/z80inst.c b/z80inst.c index 3643f43..0605ebc 100644 --- a/z80inst.c +++ b/z80inst.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "z80inst.h" #include #include diff --git a/z80inst.h b/z80inst.h index 985a561..f2d7566 100644 --- a/z80inst.h +++ b/z80inst.h @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #ifndef Z80INST_H_ #define Z80INST_H_ diff --git a/zdis.c b/zdis.c index 00a8886..390c82e 100644 --- a/zdis.c +++ b/zdis.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "z80inst.h" #include #include diff --git a/ztestgen.c b/ztestgen.c index 1c8bf8c..31665be 100644 --- a/ztestgen.c +++ b/ztestgen.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "z80inst.h" #include #include diff --git a/ztestrun.c b/ztestrun.c index 162a94d..9f500f1 100644 --- a/ztestrun.c +++ b/ztestrun.c @@ -1,3 +1,8 @@ +/* + Copyright 2013 Michael Pavone + This file is part of BlastEm. + BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. +*/ #include "z80inst.h" #include "z80_to_x86.h" #include "mem.h" -- cgit v1.2.3 From 4d20ddd615f7f07ec56d70ee90f2c61d76a7ad7f Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Wed, 11 Sep 2013 00:08:33 -0700 Subject: Added README file --- README | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ default.cfg | 2 +- 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 README diff --git a/README b/README new file mode 100644 index 0000000..499be78 --- /dev/null +++ b/README @@ -0,0 +1,87 @@ +BlastEm 0.1.0 +------------- + +Installation +------------ + +Extract this tarball to a directory of your choosing. If you wish to change the +configuration settings, copy default.cfg to ~/.config/blastem/blastem.cfg and +modify the copy. You may also whish to add the blastem directory to your PATH +environment variable. + +Configuration +------------- + +Configuration is read from the file at ~/.config/blastem/blastem.cfg if it +exists othwerise it is read from default.cfg from the same directory as the +blastem executable. Sections are denoted by a section name followed by an open +curly bracket, the section's contents and a closing curly bracket. Individual +configuration values are set by entering the value's name followed by a space +or tab and followed by the desired value. + +Bindings +-------- + +The keys subsection of bindings maps keyboard keys to gamepad buttons or UI +actions. The key name goes on the left and the action is on the right. +Most keys are named for the character they produce when pressed. Additionally, +the arrow, enter and escape keys have the symbolic names up, down, left, right, +enter and esc respectively. Other keys that do not produce characters are not +yet supported. + +The pads subsection is used to map gamepads and joysticks. Analog axes are not +currently supported. An example configuration is provided in default.cfg to map +SDL joystick 0 to the second controller. + +Video +----- + +Currently the only setting in the video section is "width", which is the width +of the window in pixels. Height is calculated from this value. Both width and +height can be overridden from the command line. + +Audio +----- + +The audio section has two config values: rate and buffer. rate selects the +sample rate and buffer sets the size of the output buffer in samples. 512 is +generally a good value, but if you're experiencing audio dropouts you might +want to increase it to 1024. + +Debugger +-------- + +BlastEm has an integrated command-line debugger loosely based on GDB's +interface. The interface is very rough at the moment. Available commands in the +68K debugger are: + b ADDRESS - Set a breakpoint at ADDRESS + a ADDRESS - Advance to address + n - Advance to next instruction + c - Continue + p[/(x|X|d|c)] VALUE - Print a register or memory location + vs - Print VDP sprite list + vr - Print VDP register info + zb - Set a Z80 breakpoint + q - Quit BlastEm +Available commands in the Z80 debugger are: + b ADDRESS - Set a breakpoint at ADDRESS + a ADDRESS - Advance to address + n - Advance to next instruction + c - Continue + p[/(x|X|d|c)] VALUE - Print a register or memory location + di[/(x|X|d|c)] VALUE - Print VALUE before every debugger prompt + de BREAKPOINT - Delete a Z80 breakpoint + q - Quit BlastEm + +The -d flag can be used to cause BlastEm to start in the debugger. +Alternatively, you can use the ui.enter_debugger action (mapped to the 'u' key +by default)to enter the debugger while a game is running. + +License +------- + +BlastEm is free software distributed under the terms of the GNU General Public +License version 3 or higher. This gives you the right to redistribute and/or +modify the program as long as you follow the terms of the license. See the file +COPYING for full license details. + diff --git a/default.cfg b/default.cfg index 6d6fd14..1959d5c 100644 --- a/default.cfg +++ b/default.cfg @@ -18,7 +18,7 @@ bindings { ] ui.vdp_debug_pal u ui.enter_debugger esc ui.exit - ` ui.save_state + ` ui.save_state } pads { 0 { -- cgit v1.2.3 From 98cea1dba54cfd7ea8e8753076acd0bb79604e0b Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Wed, 11 Sep 2013 19:26:35 -0700 Subject: Fix argument handling so that the rom filename does not need to be specified first --- blastem.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/blastem.c b/blastem.c index 73fc053..17cd14c 100644 --- a/blastem.c +++ b/blastem.c @@ -1,6 +1,6 @@ /* Copyright 2013 Michael Pavone - This file is part of BlastEm. + This file is part of BlastEm. BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. */ #include "68kinst.h" @@ -1731,7 +1731,7 @@ void detect_region() int main(int argc, char ** argv) { if (argc < 2) { - fputs("Usage: blastem ROMFILE [OPTIONS] [WIDTH] [HEIGHT]\n", stderr); + fputs("Usage: blastem [OPTIONS] ROMFILE [WIDTH] [HEIGHT]\n", stderr); return 1; } config = load_config(argv[0]); @@ -1741,6 +1741,8 @@ int main(int argc, char ** argv) int debug = 0; int ym_log = 0; int loaded = 0; + uint8_t force_version = 0; + char * romfname = NULL; FILE *address_log = NULL; char * statefile = NULL; uint8_t fullscreen = 0; @@ -1773,15 +1775,15 @@ int main(int argc, char ** argv) { case 'j': case 'J': - version_reg = NO_DISK | JAP; + force_version = NO_DISK | JAP; break; case 'u': case 'U': - version_reg = NO_DISK | USA; + force_version = NO_DISK | USA; break; case 'e': case 'E': - version_reg = NO_DISK | EUR; + force_version = NO_DISK | EUR; break; default: fprintf(stderr, "'%c' is not a valid region character for the -r option\n", argv[i][0]); @@ -1801,7 +1803,7 @@ int main(int argc, char ** argv) break; case 'h': puts( - "Usage: blastem ROMFILE [OPTIONS] [WIDTH] [HEIGHT]\n" + "Usage: blastem [OPTIONS] ROMFILE [WIDTH] [HEIGHT]\n" "Options:\n" " -h Print this help text\n" " -r (J|U|E) Force region to Japan, US or Europe respectively\n" @@ -1820,9 +1822,10 @@ int main(int argc, char ** argv) } } else if (!loaded) { if(!load_rom(argv[i])) { - fprintf(stderr, "Failed to open %s for reading\n", argv[1]); + fprintf(stderr, "Failed to open %s for reading\n", argv[i]); return 1; } + romfname = argv[i]; loaded = 1; } else if (width < 0) { width = atoi(argv[i]); @@ -1834,6 +1837,9 @@ int main(int argc, char ** argv) fputs("You must specify a ROM filename!\n", stderr); return 1; } + if (force_version) { + version_reg = force_version; + } update_title(); int def_width = 0; char *config_width = tern_find_ptr(config, "videowidth"); @@ -1883,9 +1889,9 @@ int main(int argc, char ** argv) gen.psg = &p_context; genesis = &gen; - int fname_size = strlen(argv[1]); + int fname_size = strlen(romfname); sram_filename = malloc(fname_size+6); - memcpy(sram_filename, argv[1], fname_size); + memcpy(sram_filename, romfname, fname_size); int i; for (i = fname_size-1; fname_size >= 0; --i) { if (sram_filename[i] == '.') { -- cgit v1.2.3 From 0f6f021093e9b54ca2379fd7d9359648cca03734 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Fri, 13 Sep 2013 19:22:46 -0700 Subject: Properly delay 68K on VDP reads. Dummy VDP test port implementation. Initial stab at handling undefined bits of VSRAM and CRAM. --- blastem.c | 15 +++++++++++---- vdp.c | 40 +++++++++++++++++++++++++++++++++------- vdp.h | 6 +++++- 3 files changed, 49 insertions(+), 12 deletions(-) diff --git a/blastem.c b/blastem.c index 17cd14c..b7bcbc6 100644 --- a/blastem.c +++ b/blastem.c @@ -362,7 +362,7 @@ m68k_context * vdp_port_write(uint32_t vdp_port, m68k_context * context, uint16_ exit(1); } if (v_context->cycles != before_cycle) { - //printf("68K paused for %d (%d) cycles at cycle %d (%d)\n", v_context->cycles / MCLKS_PER_68K - context->current_cycle, v_context->cycles - before_cycle, context->current_cycle, before_cycle); + printf("68K paused for %d (%d) cycles at cycle %d (%d) for write\n", v_context->cycles / MCLKS_PER_68K - context->current_cycle, v_context->cycles - before_cycle, context->current_cycle, before_cycle); context->current_cycle = v_context->cycles / MCLKS_PER_68K; } } else if (vdp_port < 0x18) { @@ -402,7 +402,7 @@ z80_context * z80_vdp_port_write(uint16_t vdp_port, z80_context * context, uint8 sync_sound(gen, context->current_cycle * MCLKS_PER_Z80); psg_write(gen->psg, value); } else { - //TODO: Implement undocumented test register(s) + vdp_test_port_write(gen->vdp, value); } return context; } @@ -417,6 +417,7 @@ uint16_t vdp_port_read(uint32_t vdp_port, m68k_context * context) uint16_t value; sync_components(context, 0); vdp_context * v_context = context->video_context; + uint32_t before_cycle = v_context->cycles; if (vdp_port < 0x10) { if (vdp_port < 4) { value = vdp_data_port_read(v_context); @@ -426,9 +427,15 @@ uint16_t vdp_port_read(uint32_t vdp_port, m68k_context * context) value = vdp_hv_counter_read(v_context); //printf("HV Counter: %X at cycle %d\n", value, v_context->cycles); } - } else { - printf("Illegal read from PSG or test register port %X\n", vdp_port); + } else if (vdp_port < 0x18){ + printf("Illegal read from PSG port %X\n", vdp_port); exit(1); + } else { + value = vdp_test_port_read(v_context); + } + if (v_context->cycles != before_cycle) { + printf("68K paused for %d (%d) cycles at cycle %d (%d) for read\n", v_context->cycles / MCLKS_PER_68K - context->current_cycle, v_context->cycles - before_cycle, context->current_cycle, before_cycle); + context->current_cycle = v_context->cycles / MCLKS_PER_68K; } return value; } diff --git a/vdp.c b/vdp.c index cf27ff2..ca84554 100644 --- a/vdp.c +++ b/vdp.c @@ -1,6 +1,6 @@ /* Copyright 2013 Michael Pavone - This file is part of BlastEm. + This file is part of BlastEm. BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. */ #include "vdp.h" @@ -391,15 +391,21 @@ void write_cram(vdp_context * context, uint16_t address, uint16_t value) void external_slot(vdp_context * context) { fifo_entry * start = (context->fifo_end - FIFO_SIZE); + if (context->flags2 & FLAG2_READ_PENDING) { + context->flags2 &= ~FLAG2_READ_PENDING; + context->flags |= FLAG_UNUSED_SLOT; + return; + } if (context->fifo_cur != start && start->cycle <= context->cycles) { switch (start->cd & 0xF) { case VRAM_WRITE: if (start->partial) { - //printf("VRAM Write: %X to %X\n", start->value, context->address ^ 1); + printf("VRAM Write: %X to %X at %d (line %d, slot %d)\n", start->value, start->address ^ 1, context->cycles, context->cycles/MCLKS_LINE, (context->cycles%MCLKS_LINE)/16); + context->last_fifo_val = start->value; context->vdpmem[start->address ^ 1] = start->value; } else { - //printf("VRAM Write High: %X to %X\n", start->value >> 8, context->address); + printf("VRAM Write High: %X to %X at %d (line %d, slot %d)\n", start->value >> 8, start->address, context->cycles, context->cycles/MCLKS_LINE, (context->cycles%MCLKS_LINE)/16); context->vdpmem[start->address] = start->value >> 8; start->partial = 1; //skip auto-increment and removal of entry from fifo @@ -409,12 +415,14 @@ void external_slot(vdp_context * context) case CRAM_WRITE: { //printf("CRAM Write | %X to %X\n", start->value, (start->address/2) & (CRAM_SIZE-1)); write_cram(context, start->address, start->value); + context->last_fifo_val = start->value; break; } case VSRAM_WRITE: if (((start->address/2) & 63) < VSRAM_SIZE) { //printf("VSRAM Write: %X to %X\n", start->value, context->address); context->vsram[(start->address/2) & 63] = start->value; + context->last_fifo_val = start->value; } break; @@ -1428,7 +1436,7 @@ void vdp_run_dma_done(vdp_context * context, uint32_t target_cycles) int vdp_control_port_write(vdp_context * context, uint16_t value) { - //printf("control port write: %X at %d\n", value, context->cycles); + printf("control port write: %X at %d\n", value, context->cycles); if (context->flags & FLAG_DMA_RUN) { return -1; } @@ -1482,7 +1490,7 @@ int vdp_control_port_write(vdp_context * context, uint16_t value) int vdp_data_port_write(vdp_context * context, uint16_t value) { - //printf("data port write: %X at %d\n", value, context->cycles); + printf("data port write: %X at %d\n", value, context->cycles); if (context->flags & FLAG_DMA_RUN && (context->regs[REG_DMASRC_H] & 0xC0) != 0x80) { return -1; } @@ -1514,6 +1522,11 @@ int vdp_data_port_write(vdp_context * context, uint16_t value) return 0; } +void vdp_test_port_write(vdp_context * context, uint16_t value) +{ + //TODO: implement test register +} + uint16_t vdp_control_port_read(vdp_context * context) { context->flags &= ~FLAG_PENDING; @@ -1549,6 +1562,9 @@ uint16_t vdp_control_port_read(vdp_context * context) return value; } +#define CRAM_BITS 0xEEE +#define VSRAM_BITS 0x3FF + uint16_t vdp_data_port_read(vdp_context * context) { context->flags &= ~FLAG_PENDING; @@ -1557,6 +1573,7 @@ uint16_t vdp_data_port_read(vdp_context * context) } //Not sure if the FIFO should be drained before processing a read or not, but it would make sense context->flags &= ~FLAG_UNUSED_SLOT; + context->flags2 |= FLAG2_READ_PENDING; while (!(context->flags & FLAG_UNUSED_SLOT)) { vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)); } @@ -1566,17 +1583,20 @@ uint16_t vdp_data_port_read(vdp_context * context) case VRAM_READ: value = context->vdpmem[context->address] << 8; context->flags &= ~FLAG_UNUSED_SLOT; + context->flags2 |= FLAG2_READ_PENDING; while (!(context->flags & FLAG_UNUSED_SLOT)) { vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)); } value |= context->vdpmem[context->address ^ 1]; break; case CRAM_READ: - value = context->cram[(context->address/2) & (CRAM_SIZE-1)]; + value = context->cram[(context->address/2) & (CRAM_SIZE-1)] & CRAM_BITS; + value |= context->last_fifo_val & ~CRAM_BITS; break; case VSRAM_READ: if (((context->address / 2) & 63) < VSRAM_SIZE) { - value = context->vsram[context->address & 63]; + value = context->vsram[context->address & 63] & VSRAM_BITS; + value |= context->last_fifo_val & ~VSRAM_BITS; } break; } @@ -1693,6 +1713,12 @@ uint16_t vdp_hv_counter_read(vdp_context * context) return (line << 8) | linecyc; } +uint16_t vdp_test_port_read(vdp_context * context) +{ + //TODO: Find out what actually gets returned here + return 0xFFFF; +} + void vdp_adjust_cycles(vdp_context * context, uint32_t deduction) { context->cycles -= deduction; diff --git a/vdp.h b/vdp.h index 79f28c0..4cb61e2 100644 --- a/vdp.h +++ b/vdp.h @@ -1,6 +1,6 @@ /* Copyright 2013 Michael Pavone - This file is part of BlastEm. + This file is part of BlastEm. BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. */ #ifndef VDP_H_ @@ -47,6 +47,7 @@ #define FLAG2_VINT_PENDING 0x01 #define FLAG2_HINT_PENDING 0x02 +#define FLAG2_READ_PENDING 0x04 #define DISPLAY_ENABLE 0x40 @@ -149,6 +150,7 @@ typedef struct { uint16_t col_1; uint16_t col_2; uint16_t last_write_val; + uint16_t last_fifo_val; uint8_t v_offset; uint8_t dma_cd; uint8_t hint_counter; @@ -172,9 +174,11 @@ uint8_t vdp_load_gst(vdp_context * context, FILE * state_file); uint8_t vdp_save_gst(vdp_context * context, FILE * outfile); int vdp_control_port_write(vdp_context * context, uint16_t value); int vdp_data_port_write(vdp_context * context, uint16_t value); +void vdp_test_port_write(vdp_context * context, uint16_t value); uint16_t vdp_control_port_read(vdp_context * context); uint16_t vdp_data_port_read(vdp_context * context); uint16_t vdp_hv_counter_read(vdp_context * context); +uint16_t vdp_test_port_read(vdp_context * context); void vdp_adjust_cycles(vdp_context * context, uint32_t deduction); uint32_t vdp_next_hint(vdp_context * context); uint32_t vdp_next_vint(vdp_context * context); -- cgit v1.2.3 From e154139fdaa57c8c805167641b9ac0c30bb9d115 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Sun, 15 Sep 2013 22:20:43 -0700 Subject: Implement FIFO as a ring buffer so the behavior of reads from invalid CRAM and VSRAM bits can be implemented properly --- blastem.c | 4 +-- vdp.c | 90 ++++++++++++++++++++++++++++++++++----------------------------- vdp.h | 7 +++-- 3 files changed, 55 insertions(+), 46 deletions(-) diff --git a/blastem.c b/blastem.c index b7bcbc6..6c2bdb1 100644 --- a/blastem.c +++ b/blastem.c @@ -362,7 +362,7 @@ m68k_context * vdp_port_write(uint32_t vdp_port, m68k_context * context, uint16_ exit(1); } if (v_context->cycles != before_cycle) { - printf("68K paused for %d (%d) cycles at cycle %d (%d) for write\n", v_context->cycles / MCLKS_PER_68K - context->current_cycle, v_context->cycles - before_cycle, context->current_cycle, before_cycle); + //printf("68K paused for %d (%d) cycles at cycle %d (%d) for write\n", v_context->cycles / MCLKS_PER_68K - context->current_cycle, v_context->cycles - before_cycle, context->current_cycle, before_cycle); context->current_cycle = v_context->cycles / MCLKS_PER_68K; } } else if (vdp_port < 0x18) { @@ -434,7 +434,7 @@ uint16_t vdp_port_read(uint32_t vdp_port, m68k_context * context) value = vdp_test_port_read(v_context); } if (v_context->cycles != before_cycle) { - printf("68K paused for %d (%d) cycles at cycle %d (%d) for read\n", v_context->cycles / MCLKS_PER_68K - context->current_cycle, v_context->cycles - before_cycle, context->current_cycle, before_cycle); + //printf("68K paused for %d (%d) cycles at cycle %d (%d) for read\n", v_context->cycles / MCLKS_PER_68K - context->current_cycle, v_context->cycles - before_cycle, context->current_cycle, before_cycle); context->current_cycle = v_context->cycles / MCLKS_PER_68K; } return value; diff --git a/vdp.c b/vdp.c index ca84554..8e5eb81 100644 --- a/vdp.c +++ b/vdp.c @@ -20,8 +20,6 @@ #define SCROLL_BUFFER_MASK (SCROLL_BUFFER_SIZE-1) #define SCROLL_BUFFER_DRAW (SCROLL_BUFFER_SIZE/2) -#define FIFO_SIZE 4 - #define MCLKS_SLOT_H40 16 #define MCLKS_SLOT_H32 20 #define VINT_CYCLE_H40 (21*MCLKS_SLOT_H40+332+9*MCLKS_SLOT_H40) //21 slots before HSYNC, 16 during, 10 after @@ -61,8 +59,8 @@ void init_vdp_context(vdp_context * context) context->tmp_buf_a = context->linebuf + LINEBUF_SIZE; context->tmp_buf_b = context->tmp_buf_a + SCROLL_BUFFER_SIZE; context->sprite_draws = MAX_DRAWS; - context->fifo_cur = malloc(sizeof(fifo_entry) * FIFO_SIZE); - context->fifo_end = context->fifo_cur + FIFO_SIZE; + context->fifo_write = 0; + context->fifo_read = -1; context->b32 = render_depth() == 32; if (!color_map_init_done) { uint8_t b,g,r; @@ -390,22 +388,22 @@ void write_cram(vdp_context * context, uint16_t address, uint16_t value) void external_slot(vdp_context * context) { - fifo_entry * start = (context->fifo_end - FIFO_SIZE); + fifo_entry * start = context->fifo + context->fifo_read; if (context->flags2 & FLAG2_READ_PENDING) { context->flags2 &= ~FLAG2_READ_PENDING; context->flags |= FLAG_UNUSED_SLOT; return; } - if (context->fifo_cur != start && start->cycle <= context->cycles) { + if (context->fifo_read >= 0 && start->cycle <= context->cycles) { switch (start->cd & 0xF) { case VRAM_WRITE: if (start->partial) { - printf("VRAM Write: %X to %X at %d (line %d, slot %d)\n", start->value, start->address ^ 1, context->cycles, context->cycles/MCLKS_LINE, (context->cycles%MCLKS_LINE)/16); + //printf("VRAM Write: %X to %X at %d (line %d, slot %d)\n", start->value, start->address ^ 1, context->cycles, context->cycles/MCLKS_LINE, (context->cycles%MCLKS_LINE)/16); context->last_fifo_val = start->value; context->vdpmem[start->address ^ 1] = start->value; } else { - printf("VRAM Write High: %X to %X at %d (line %d, slot %d)\n", start->value >> 8, start->address, context->cycles, context->cycles/MCLKS_LINE, (context->cycles%MCLKS_LINE)/16); + //printf("VRAM Write High: %X to %X at %d (line %d, slot %d)\n", start->value >> 8, start->address, context->cycles, context->cycles/MCLKS_LINE, (context->cycles%MCLKS_LINE)/16); context->vdpmem[start->address] = start->value >> 8; start->partial = 1; //skip auto-increment and removal of entry from fifo @@ -427,11 +425,10 @@ void external_slot(vdp_context * context) break; } - fifo_entry * cur = start+1; - if (cur < context->fifo_cur) { - memmove(start, cur, sizeof(fifo_entry) * (context->fifo_cur - cur)); + context->fifo_read = (context->fifo_read+1) & (FIFO_SIZE-1); + if (context->fifo_read == context->fifo_write) { + context->fifo_read = -1; } - context->fifo_cur -= 1; } else { context->flags |= FLAG_UNUSED_SLOT; } @@ -442,7 +439,7 @@ void run_dma_src(vdp_context * context, uint32_t slot) //TODO: Figure out what happens if CD bit 4 is not set in DMA copy mode //TODO: Figure out what happens when CD:0-3 is not set to a write mode in DMA operations //TODO: Figure out what happens if DMA gets disabled part way through a DMA fill or DMA copy - if (context->fifo_cur == context->fifo_end) { + if (context->fifo_write == context->fifo_read) { return; } uint16_t read_val; @@ -489,12 +486,16 @@ void run_dma_src(vdp_context * context, uint32_t slot) } if (ran_source) { - context->fifo_cur->cycle = context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)*FIFO_LATENCY; - context->fifo_cur->address = context->address; - context->fifo_cur->value = read_val; - context->fifo_cur->cd = context->cd; - context->fifo_cur->partial = partial; - context->fifo_cur++; + fifo_entry * cur = context->fifo + context->fifo_write; + cur->cycle = context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)*FIFO_LATENCY; + cur->address = context->address; + cur->value = read_val; + cur->cd = context->cd; + cur->partial = partial; + if (context->fifo_read < 0) { + context->fifo_read = context->fifo_write; + } + context->fifo_write = (context->fifo_write+1) & (FIFO_SIZE-1); context->regs[REG_DMASRC_L] += 1; if (!context->regs[REG_DMASRC_L]) { context->regs[REG_DMASRC_M] += 1; @@ -1436,7 +1437,7 @@ void vdp_run_dma_done(vdp_context * context, uint32_t target_cycles) int vdp_control_port_write(vdp_context * context, uint16_t value) { - printf("control port write: %X at %d\n", value, context->cycles); + //printf("control port write: %X at %d\n", value, context->cycles); if (context->flags & FLAG_DMA_RUN) { return -1; } @@ -1490,14 +1491,10 @@ int vdp_control_port_write(vdp_context * context, uint16_t value) int vdp_data_port_write(vdp_context * context, uint16_t value) { - printf("data port write: %X at %d\n", value, context->cycles); + //printf("data port write: %X at %d\n", value, context->cycles); if (context->flags & FLAG_DMA_RUN && (context->regs[REG_DMASRC_H] & 0xC0) != 0x80) { return -1; } - if (!(context->cd & 1)) { - //ignore writes when cd is configured for read - return 0; - } context->flags &= ~FLAG_PENDING; /*if (context->fifo_cur == context->fifo_end) { printf("FIFO full, waiting for space before next write at cycle %X\n", context->cycles); @@ -1505,19 +1502,23 @@ int vdp_data_port_write(vdp_context * context, uint16_t value) if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) { context->flags &= ~FLAG_DMA_RUN; } - while (context->fifo_cur == context->fifo_end) { + while (context->fifo_write == context->fifo_read) { vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)); } - context->fifo_cur->cycle = context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)*FIFO_LATENCY; - context->fifo_cur->address = context->address; - context->fifo_cur->value = value; + fifo_entry * cur = context->fifo + context->fifo_write; + cur->cycle = context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)*FIFO_LATENCY; + cur->address = context->address; + cur->value = value; context->last_write_val = value; if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) { context->flags |= FLAG_DMA_RUN; } - context->fifo_cur->cd = context->cd; - context->fifo_cur->partial = 0; - context->fifo_cur++; + cur->cd = context->cd; + cur->partial = 0; + if (context->fifo_read < 0) { + context->fifo_read = context->fifo_write; + } + context->fifo_write = (context->fifo_write + 1) & (FIFO_SIZE-1); context->address += context->regs[REG_AUTOINC]; return 0; } @@ -1531,10 +1532,10 @@ uint16_t vdp_control_port_read(vdp_context * context) { context->flags &= ~FLAG_PENDING; uint16_t value = 0x3400; - if (context->fifo_cur == (context->fifo_end - FIFO_SIZE)) { + if (context->fifo_read < 0) { value |= 0x200; } - if (context->fifo_cur == context->fifo_end) { + if (context->fifo_read == context->fifo_write) { value |= 0x100; } if (context->flags2 & FLAG2_VINT_PENDING) { @@ -1564,6 +1565,7 @@ uint16_t vdp_control_port_read(vdp_context * context) #define CRAM_BITS 0xEEE #define VSRAM_BITS 0x3FF +#define VSRAM_DIRTY_BITS 0xF800 uint16_t vdp_data_port_read(vdp_context * context) { @@ -1591,12 +1593,12 @@ uint16_t vdp_data_port_read(vdp_context * context) break; case CRAM_READ: value = context->cram[(context->address/2) & (CRAM_SIZE-1)] & CRAM_BITS; - value |= context->last_fifo_val & ~CRAM_BITS; + value |= context->fifo[context->fifo_write].value & ~CRAM_BITS; break; case VSRAM_READ: if (((context->address / 2) & 63) < VSRAM_SIZE) { value = context->vsram[context->address & 63] & VSRAM_BITS; - value |= context->last_fifo_val & ~VSRAM_BITS; + value |= context->fifo[context->fifo_write].value & VSRAM_DIRTY_BITS; } break; } @@ -1722,12 +1724,16 @@ uint16_t vdp_test_port_read(vdp_context * context) void vdp_adjust_cycles(vdp_context * context, uint32_t deduction) { context->cycles -= deduction; - for(fifo_entry * start = (context->fifo_end - FIFO_SIZE); start < context->fifo_cur; start++) { - if (start->cycle >= deduction) { - start->cycle -= deduction; - } else { - start->cycle = 0; - } + if (context->fifo_read >= 0) { + int32_t idx = context->fifo_read; + do { + if (context->fifo[idx].cycle >= deduction) { + context->fifo[idx].cycle -= deduction; + } else { + context->fifo[idx].cycle = 0; + } + idx = (idx+1) & (FIFO_SIZE-1); + } while(idx != context->fifo_write); } } diff --git a/vdp.h b/vdp.h index 4cb61e2..75a603d 100644 --- a/vdp.h +++ b/vdp.h @@ -110,6 +110,8 @@ typedef struct { int16_t y; } sprite_info; +#define FIFO_SIZE 4 + typedef struct { uint32_t cycle; uint16_t address; @@ -119,8 +121,9 @@ typedef struct { } fifo_entry; typedef struct { - fifo_entry *fifo_cur; - fifo_entry *fifo_end; + fifo_entry fifo[FIFO_SIZE]; + int32_t fifo_write; + int32_t fifo_read; uint16_t address; uint8_t cd; uint8_t flags; -- cgit v1.2.3 From 4437c3730dfbe7e038bc28da6531d140ddbfbcd4 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Sun, 15 Sep 2013 22:43:01 -0700 Subject: Fix VSRAM reads --- vdp.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vdp.c b/vdp.c index 8e5eb81..87e3d59 100644 --- a/vdp.c +++ b/vdp.c @@ -1583,13 +1583,13 @@ uint16_t vdp_data_port_read(vdp_context * context) switch (context->cd & 0xF) { case VRAM_READ: - value = context->vdpmem[context->address] << 8; + value = context->vdpmem[context->address & 0xFFFE] << 8; context->flags &= ~FLAG_UNUSED_SLOT; context->flags2 |= FLAG2_READ_PENDING; while (!(context->flags & FLAG_UNUSED_SLOT)) { vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)); } - value |= context->vdpmem[context->address ^ 1]; + value |= context->vdpmem[context->address | 1]; break; case CRAM_READ: value = context->cram[(context->address/2) & (CRAM_SIZE-1)] & CRAM_BITS; @@ -1597,7 +1597,7 @@ uint16_t vdp_data_port_read(vdp_context * context) break; case VSRAM_READ: if (((context->address / 2) & 63) < VSRAM_SIZE) { - value = context->vsram[context->address & 63] & VSRAM_BITS; + value = context->vsram[(context->address / 2) & 63] & VSRAM_BITS; value |= context->fifo[context->fifo_write].value & VSRAM_DIRTY_BITS; } break; -- cgit v1.2.3 From a47dff5923490fd2a5ce5f53ee894f41e6e81962 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Sun, 15 Sep 2013 23:00:17 -0700 Subject: Implement undocumented 8-bit VRAM read --- vdp.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/vdp.c b/vdp.c index 87e3d59..ffa46f7 100644 --- a/vdp.c +++ b/vdp.c @@ -378,12 +378,22 @@ void write_cram(vdp_context * context, uint16_t address, uint16_t value) context->colors[addr + CRAM_SIZE*2] = color_map[(value & 0xEEE) | FBUF_HILIGHT]; } -#define VRAM_READ 0 -#define VRAM_WRITE 1 -#define CRAM_READ 8 -#define CRAM_WRITE 3 -#define VSRAM_READ 4 -#define VSRAM_WRITE 5 +#define VRAM_READ 0 //0000 +#define VRAM_WRITE 1 //0001 +//2 would trigger register write 0010 +#define CRAM_WRITE 3 //0011 +#define VSRAM_READ 4 //0100 +#define VSRAM_WRITE 5//0101 +//6 would trigger regsiter write 0110 +//7 is a mystery +#define CRAM_READ 8 //1000 +//9 is also a mystery //1001 +//A would trigger register write 1010 +//B is a mystery 1011 +#define VRAM_READ8 0xC //1100 +//D is a mystery 1101 +//E would trigger register write 1110 +//F is a mystery 1111 #define DMA_START 0x20 void external_slot(vdp_context * context) @@ -1591,6 +1601,10 @@ uint16_t vdp_data_port_read(vdp_context * context) } value |= context->vdpmem[context->address | 1]; break; + case VRAM_READ8: + value = context->vdpmem[context->address ^ 1]; + value |= context->fifo[context->fifo_write].value & 0xFF00; + break; case CRAM_READ: value = context->cram[(context->address/2) & (CRAM_SIZE-1)] & CRAM_BITS; value |= context->fifo[context->fifo_write].value & ~CRAM_BITS; -- cgit v1.2.3 From 87c57b78120c4abbf386634b803ab87b2ec53498 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Sun, 15 Sep 2013 23:33:24 -0700 Subject: Remove read pending stuff, that had been added in an attempt to fix CRAM/VSRAM undefined bit results. Set number of bits actually saved in VSRAM to 11 --- vdp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/vdp.c b/vdp.c index ffa46f7..69afcc2 100644 --- a/vdp.c +++ b/vdp.c @@ -399,11 +399,11 @@ void write_cram(vdp_context * context, uint16_t address, uint16_t value) void external_slot(vdp_context * context) { fifo_entry * start = context->fifo + context->fifo_read; - if (context->flags2 & FLAG2_READ_PENDING) { + /*if (context->flags2 & FLAG2_READ_PENDING) { context->flags2 &= ~FLAG2_READ_PENDING; context->flags |= FLAG_UNUSED_SLOT; return; - } + }*/ if (context->fifo_read >= 0 && start->cycle <= context->cycles) { switch (start->cd & 0xF) { @@ -1574,7 +1574,7 @@ uint16_t vdp_control_port_read(vdp_context * context) } #define CRAM_BITS 0xEEE -#define VSRAM_BITS 0x3FF +#define VSRAM_BITS 0x7FF #define VSRAM_DIRTY_BITS 0xF800 uint16_t vdp_data_port_read(vdp_context * context) @@ -1585,7 +1585,7 @@ uint16_t vdp_data_port_read(vdp_context * context) } //Not sure if the FIFO should be drained before processing a read or not, but it would make sense context->flags &= ~FLAG_UNUSED_SLOT; - context->flags2 |= FLAG2_READ_PENDING; + //context->flags2 |= FLAG2_READ_PENDING; while (!(context->flags & FLAG_UNUSED_SLOT)) { vdp_run_context(context, context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)); } -- cgit v1.2.3 From 0d6a2b753c9733f07932c1bd104a4371cc603894 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Sun, 15 Sep 2013 23:40:18 -0700 Subject: Don't allow register writes to regs above when in Mode 4 --- vdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vdp.c b/vdp.c index 69afcc2..f1f28fc 100644 --- a/vdp.c +++ b/vdp.c @@ -1477,7 +1477,7 @@ int vdp_control_port_write(vdp_context * context, uint16_t value) if ((value & 0xC000) == 0x8000) { //Register write uint8_t reg = (value >> 8) & 0x1F; - if (reg < VDP_REGS) { + if (reg < (context->regs[REG_MODE_2] & BIT_MODE_5 ? VDP_REGS : 0xA)) { //printf("register %d set to %X\n", reg, value & 0xFF); context->regs[reg] = value; if (reg == REG_MODE_2) { -- cgit v1.2.3 From 0b85aa8c6991e91f9927f4d77e3f6bf5600dd36c Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Sun, 15 Sep 2013 23:49:09 -0700 Subject: Clear the low 2 bits of CD when a register is written to --- vdp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/vdp.c b/vdp.c index f1f28fc..efe7dac 100644 --- a/vdp.c +++ b/vdp.c @@ -1489,6 +1489,7 @@ int vdp_control_port_write(vdp_context * context, uint16_t value) context->framebuf = context->oddbuf; } } + context->cd &= 0x3C; } } else { context->flags |= FLAG_PENDING; -- cgit v1.2.3 From a020d55258a5ebf5db027072c4ef28194aa6564b Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Mon, 16 Sep 2013 09:44:22 -0700 Subject: Partial fix for DMA copy --- vdp.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/vdp.c b/vdp.c index efe7dac..5de80e5 100644 --- a/vdp.c +++ b/vdp.c @@ -455,6 +455,7 @@ void run_dma_src(vdp_context * context, uint32_t slot) uint16_t read_val; uint8_t ran_source = 0, partial = 0; uint16_t dma_len; + uint8_t cd = context->cd; switch(context->regs[REG_DMASRC_H] & 0xC0) { //68K -> VDP @@ -468,22 +469,9 @@ void run_dma_src(vdp_context * context, uint32_t slot) //Copy case 0xC0: if (context->flags & FLAG_UNUSED_SLOT) { - switch(context->dma_cd & 0xF) - { - case VRAM_WRITE: - read_val = context->vdpmem[(context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]]; - break; - case CRAM_WRITE: - read_val = context->cram[context->regs[REG_DMASRC_L] & (CRAM_SIZE-1)]; - break; - case VSRAM_WRITE: - if ((context->regs[REG_DMASRC_L] & 63) < VSRAM_SIZE) { - read_val = context->vsram[context->regs[REG_DMASRC_L] & 63]; - } else { - read_val = 0; - } - break; - } + read_val = context->vdpmem[(context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L] ^ 1] | (context->fifo[context->fifo_write].value & 0xFF00); + cd = VRAM_WRITE; + partial = 1; ran_source = 1; context->flags &= ~FLAG_UNUSED_SLOT; } @@ -500,7 +488,7 @@ void run_dma_src(vdp_context * context, uint32_t slot) cur->cycle = context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)*FIFO_LATENCY; cur->address = context->address; cur->value = read_val; - cur->cd = context->cd; + cur->cd = cd; cur->partial = partial; if (context->fifo_read < 0) { context->fifo_read = context->fifo_write; -- cgit v1.2.3 From 74af15299f37e5c95f4498cfb2f9456c9d4d61bc Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Tue, 17 Sep 2013 00:11:45 -0700 Subject: Fix DMA fill so that it does not cause observable changes to the FIFO. Get DMA copy mostly correct from an observable ffect perspective. DMA copy probably does not reflect internal implementation still given that evidence seems to suggest no FIFO usage at all. --- vdp.c | 61 ++++++++++++++++++++++++++++++------------------------------- vdp.h | 2 -- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/vdp.c b/vdp.c index 5de80e5..b75f063 100644 --- a/vdp.c +++ b/vdp.c @@ -410,8 +410,7 @@ void external_slot(vdp_context * context) case VRAM_WRITE: if (start->partial) { //printf("VRAM Write: %X to %X at %d (line %d, slot %d)\n", start->value, start->address ^ 1, context->cycles, context->cycles/MCLKS_LINE, (context->cycles%MCLKS_LINE)/16); - context->last_fifo_val = start->value; - context->vdpmem[start->address ^ 1] = start->value; + context->vdpmem[start->address ^ 1] = start->partial == 2 ? start->value >> 8 : start->value; } else { //printf("VRAM Write High: %X to %X at %d (line %d, slot %d)\n", start->value >> 8, start->address, context->cycles, context->cycles/MCLKS_LINE, (context->cycles%MCLKS_LINE)/16); context->vdpmem[start->address] = start->value >> 8; @@ -423,14 +422,12 @@ void external_slot(vdp_context * context) case CRAM_WRITE: { //printf("CRAM Write | %X to %X\n", start->value, (start->address/2) & (CRAM_SIZE-1)); write_cram(context, start->address, start->value); - context->last_fifo_val = start->value; break; } case VSRAM_WRITE: if (((start->address/2) & 63) < VSRAM_SIZE) { //printf("VSRAM Write: %X to %X\n", start->value, context->address); context->vsram[(start->address/2) & 63] = start->value; - context->last_fifo_val = start->value; } break; @@ -452,54 +449,57 @@ void run_dma_src(vdp_context * context, uint32_t slot) if (context->fifo_write == context->fifo_read) { return; } - uint16_t read_val; - uint8_t ran_source = 0, partial = 0; - uint16_t dma_len; - uint8_t cd = context->cd; + fifo_entry * cur = NULL; switch(context->regs[REG_DMASRC_H] & 0xC0) { //68K -> VDP case 0: case 0x40: if (!slot || !is_refresh(context, slot-1)) { - read_val = read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]); - ran_source = 1; + cur = context->fifo + context->fifo_write; + cur->cycle = context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)*FIFO_LATENCY; + cur->address = context->address; + cur->value = read_dma_value((context->regs[REG_DMASRC_H] << 16) | (context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L]); + cur->cd = context->cd; + cur->partial = 0; + if (context->fifo_read < 0) { + context->fifo_read = context->fifo_write; + } + context->fifo_write = (context->fifo_write + 1) & (FIFO_SIZE-1); } break; //Copy case 0xC0: - if (context->flags & FLAG_UNUSED_SLOT) { - read_val = context->vdpmem[(context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L] ^ 1] | (context->fifo[context->fifo_write].value & 0xFF00); - cd = VRAM_WRITE; - partial = 1; - ran_source = 1; + if (context->flags & FLAG_UNUSED_SLOT && context->fifo_read < 0) { + //TODO: Fix this to not use the FIFO at all once read-caching is properly implemented + context->fifo_read = (context->fifo_write-1) & (FIFO_SIZE-1); + cur = context->fifo + context->fifo_read; + cur->cycle = context->cycles; + cur->address = context->address; + cur->partial = 1; + cur->value = context->vdpmem[(context->regs[REG_DMASRC_M] << 8) | context->regs[REG_DMASRC_L] ^ 1] | (cur->value & 0xFF00); + cur->cd = VRAM_WRITE; context->flags &= ~FLAG_UNUSED_SLOT; } break; case 0x80: - read_val = (context->cd & 0xF) == VRAM_WRITE ? context->last_write_val >> 8 : context->last_write_val; - partial = 1; - ran_source = 1; + if (context->fifo_read < 0) { + context->fifo_read = (context->fifo_write-1) & (FIFO_SIZE-1); + cur = context->fifo + context->fifo_read; + cur->cycle = context->cycles; + cur->address = context->address; + cur->partial = 2; + } break; } - if (ran_source) { - fifo_entry * cur = context->fifo + context->fifo_write; - cur->cycle = context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)*FIFO_LATENCY; - cur->address = context->address; - cur->value = read_val; - cur->cd = cd; - cur->partial = partial; - if (context->fifo_read < 0) { - context->fifo_read = context->fifo_write; - } - context->fifo_write = (context->fifo_write+1) & (FIFO_SIZE-1); + if (cur) { context->regs[REG_DMASRC_L] += 1; if (!context->regs[REG_DMASRC_L]) { context->regs[REG_DMASRC_M] += 1; } context->address += context->regs[REG_AUTOINC]; - dma_len = ((context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L]) - 1; + uint16_t dma_len = ((context->regs[REG_DMALEN_H] << 8) | context->regs[REG_DMALEN_L]) - 1; context->regs[REG_DMALEN_H] = dma_len >> 8; context->regs[REG_DMALEN_L] = dma_len; if (!dma_len) { @@ -1508,7 +1508,6 @@ int vdp_data_port_write(vdp_context * context, uint16_t value) cur->cycle = context->cycles + ((context->latched_mode & BIT_H40) ? 16 : 20)*FIFO_LATENCY; cur->address = context->address; cur->value = value; - context->last_write_val = value; if (context->cd & 0x20 && (context->regs[REG_DMASRC_H] & 0xC0) == 0x80) { context->flags |= FLAG_DMA_RUN; } diff --git a/vdp.h b/vdp.h index 75a603d..9d976e4 100644 --- a/vdp.h +++ b/vdp.h @@ -152,8 +152,6 @@ typedef struct { sprite_info sprite_info_list[MAX_SPRITES_LINE]; uint16_t col_1; uint16_t col_2; - uint16_t last_write_val; - uint16_t last_fifo_val; uint8_t v_offset; uint8_t dma_cd; uint8_t hint_counter; -- cgit v1.2.3 From 7f8e47eb7e81a7001f0c6a00f2cf52f1e50a96a2 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Tue, 17 Sep 2013 00:42:49 -0700 Subject: Implement funny behavior for DMA fill to CRAM and VSRAM. Return VSRAM address 0 for reads to VSRAM at >= 40 --- vdp.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/vdp.c b/vdp.c index b75f063..5d02ce6 100644 --- a/vdp.c +++ b/vdp.c @@ -421,13 +421,13 @@ void external_slot(vdp_context * context) break; case CRAM_WRITE: { //printf("CRAM Write | %X to %X\n", start->value, (start->address/2) & (CRAM_SIZE-1)); - write_cram(context, start->address, start->value); + write_cram(context, start->address, start->partial == 2 ? context->fifo[context->fifo_write].value : start->value); break; } case VSRAM_WRITE: if (((start->address/2) & 63) < VSRAM_SIZE) { //printf("VSRAM Write: %X to %X\n", start->value, context->address); - context->vsram[(start->address/2) & 63] = start->value; + context->vsram[(start->address/2) & 63] = start->partial == 2 ? context->fifo[context->fifo_write].value : start->value; } break; @@ -1597,12 +1597,15 @@ uint16_t vdp_data_port_read(vdp_context * context) value = context->cram[(context->address/2) & (CRAM_SIZE-1)] & CRAM_BITS; value |= context->fifo[context->fifo_write].value & ~CRAM_BITS; break; - case VSRAM_READ: - if (((context->address / 2) & 63) < VSRAM_SIZE) { - value = context->vsram[(context->address / 2) & 63] & VSRAM_BITS; - value |= context->fifo[context->fifo_write].value & VSRAM_DIRTY_BITS; + case VSRAM_READ: { + uint16_t address = (context->address /2) & 63; + if (address >= VSRAM_SIZE) { + address = 0; } + value = context->vsram[address] & VSRAM_BITS; + value |= context->fifo[context->fifo_write].value & VSRAM_DIRTY_BITS; break; + } } context->address += context->regs[REG_AUTOINC]; return value; -- cgit v1.2.3 From b1170b0ee50fdb523fdb65e6f99851fe67acd1c1 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Tue, 17 Sep 2013 09:45:14 -0700 Subject: Implement HV counter latch --- vdp.c | 10 ++++++---- vdp.h | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/vdp.c b/vdp.c index 5d02ce6..4d21f1c 100644 --- a/vdp.c +++ b/vdp.c @@ -1467,10 +1467,10 @@ int vdp_control_port_write(vdp_context * context, uint16_t value) uint8_t reg = (value >> 8) & 0x1F; if (reg < (context->regs[REG_MODE_2] & BIT_MODE_5 ? VDP_REGS : 0xA)) { //printf("register %d set to %X\n", reg, value & 0xFF); - context->regs[reg] = value; - if (reg == REG_MODE_2) { - //printf("Display is now %s\n", (context->regs[REG_MODE_2] & DISPLAY_ENABLE) ? "enabled" : "disabled"); + if (reg == REG_MODE_1 && (value & BIT_HVC_LATCH) && !(context->regs[reg] & BIT_HVC_LATCH)) { + context->hv_latch = vdp_hv_counter_read(context); } + context->regs[reg] = value; if (reg == REG_MODE_4) { context->double_res = (value & (BIT_INTERLACE | BIT_DOUBLE_RES)) == (BIT_INTERLACE | BIT_DOUBLE_RES); if (!context->double_res) { @@ -1613,7 +1613,9 @@ uint16_t vdp_data_port_read(vdp_context * context) uint16_t vdp_hv_counter_read(vdp_context * context) { - //TODO: deal with clock adjustemnts handled in vdp_run_context + if (context->regs[REG_MODE_1] & BIT_HVC_LATCH) { + return context->hv_latch; + } uint32_t line= context->cycles / MCLKS_LINE; if (!line) { line = 0xFF; diff --git a/vdp.h b/vdp.h index 9d976e4..9b99b97 100644 --- a/vdp.h +++ b/vdp.h @@ -152,6 +152,7 @@ typedef struct { sprite_info sprite_info_list[MAX_SPRITES_LINE]; uint16_t col_1; uint16_t col_2; + uint16_t hv_latch; uint8_t v_offset; uint8_t dma_cd; uint8_t hint_counter; -- cgit v1.2.3 From b5767c17b138cfb9b264dbc9cad38252cdd2731c Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Tue, 17 Sep 2013 19:10:00 -0700 Subject: Set VBLANK flag in status register when display is disabled --- vdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vdp.c b/vdp.c index 4d21f1c..c1e04f0 100644 --- a/vdp.c +++ b/vdp.c @@ -1544,7 +1544,7 @@ uint16_t vdp_control_port_read(vdp_context * context) } uint32_t line= context->cycles / MCLKS_LINE; uint32_t linecyc = context->cycles % MCLKS_LINE; - if (line >= (context->latched_mode & BIT_PAL ? PAL_ACTIVE : NTSC_ACTIVE)) { + if (line >= (context->latched_mode & BIT_PAL ? PAL_ACTIVE : NTSC_ACTIVE) || !(context->regs[REG_MODE_2] & BIT_DISP_EN)) { value |= 0x8; } if (linecyc < (context->latched_mode & BIT_H40 ? HBLANK_CLEAR_H40 : HBLANK_CLEAR_H32)) { -- cgit v1.2.3 From 5830e5828c4680aafe27210935dd3fed9ece0448 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Wed, 18 Sep 2013 19:10:54 -0700 Subject: Theoretically more correct timing of Z80 bus request --- blastem.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/blastem.c b/blastem.c index 6c2bdb1..1b5e138 100644 --- a/blastem.c +++ b/blastem.c @@ -517,7 +517,6 @@ m68k_context * io_write(uint32_t location, m68k_context * context, uint8_t value } } else { if (location == 0x1100) { - sync_z80(gen->z80, context->current_cycle * MCLKS_PER_68K); if (busack_cycle <= context->current_cycle) { busack = new_busack; busack_cycle = CYCLE_NEVER; @@ -526,11 +525,13 @@ m68k_context * io_write(uint32_t location, m68k_context * context, uint8_t value dputs("bus requesting Z80"); if(!reset && !busreq) { - busack_cycle = ((gen->z80->current_cycle + Z80_ACK_DELAY) * MCLKS_PER_Z80) / MCLKS_PER_68K;//context->current_cycle + Z80_ACK_DELAY; + sync_z80(gen->z80, context->current_cycle * MCLKS_PER_68K + Z80_ACK_DELAY*MCLKS_PER_Z80); + busack_cycle = (gen->z80->current_cycle * MCLKS_PER_Z80) / MCLKS_PER_68K;//context->current_cycle + Z80_ACK_DELAY; new_busack = Z80_REQ_ACK; } busreq = 1; } else { + sync_z80(gen->z80, context->current_cycle * MCLKS_PER_68K); if (busreq) { dputs("releasing z80 bus"); #ifdef DO_DEBUG_PRINT -- cgit v1.2.3 From c4e2e0114cdc1871e959c67d0f15be99bef3459c Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Tue, 1 Oct 2013 23:51:16 -0700 Subject: Implement turbo/slow motion feature that overclocks or underclocks the entire system at the push of a button --- blastem.c | 30 +++++++++++++--- blastem.h | 5 ++- default.cfg | 22 ++++++++++++ io.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- psg.c | 20 +++++++---- psg.h | 8 +++-- ym2612.c | 55 ++++++++++++++++------------- ym2612.h | 8 +++-- 8 files changed, 213 insertions(+), 48 deletions(-) diff --git a/blastem.c b/blastem.c index 1b5e138..b57952e 100644 --- a/blastem.c +++ b/blastem.c @@ -33,6 +33,8 @@ #define LINES_NTSC 262 #define LINES_PAL 312 +#define MAX_SOUND_CYCLES 100000 + uint32_t mclks_per_frame = MCLKS_LINE*LINES_NTSC; uint16_t cart[CARTRIDGE_WORDS]; @@ -206,6 +208,13 @@ void sync_z80(z80_context * z_context, uint32_t mclks) void sync_sound(genesis_context * gen, uint32_t target) { //printf("YM | Cycle: %d, bpos: %d, PSG | Cycle: %d, bpos: %d\n", gen->ym->current_cycle, gen->ym->buffer_pos, gen->psg->cycles, gen->psg->buffer_pos * 2); + while (target > gen->psg->cycles && target - gen->psg->cycles > MAX_SOUND_CYCLES) { + uint32_t cur_target = gen->psg->cycles + MAX_SOUND_CYCLES; + //printf("Running PSG to cycle %d\n", cur_target); + psg_run(gen->psg, cur_target); + //printf("Running YM-2612 to cycle %d\n", cur_target); + ym_run(gen->ym, cur_target); + } psg_run(gen->psg, target); ym_run(gen->ym, target); @@ -1492,6 +1501,17 @@ m68k_context * debugger(m68k_context * context, uint32_t address) return context; } +void set_speed_percent(genesis_context * context, uint32_t percent) +{ + uint32_t old_clock = context->master_clock; + context->master_clock = ((uint64_t)context->normal_clock * (uint64_t)percent) / 100; + while (context->ym->current_cycle != context->psg->cycles) { + sync_sound(context, context->psg->cycles + MCLKS_PER_PSG); + } + ym_adjust_master_clock(context->ym, context->master_clock); + psg_adjust_master_clock(context->psg, context->master_clock); +} + #define ROM_END 0x1A4 #define RAM_ID 0x1B0 #define RAM_FLAGS 0x1B2 @@ -1868,23 +1888,23 @@ int main(int argc, char ** argv) render_init(width, height, title, fps, fullscreen); } vdp_context v_context; + genesis_context gen; + memset(&gen, 0, sizeof(gen)); + gen.master_clock = gen.normal_clock = fps == 60 ? MCLKS_NTSC : MCLKS_PAL; init_vdp_context(&v_context); ym2612_context y_context; - ym_init(&y_context, render_sample_rate(), fps == 60 ? MCLKS_NTSC : MCLKS_PAL, MCLKS_PER_YM, render_audio_buffer(), ym_log ? YM_OPT_WAVE_LOG : 0); + ym_init(&y_context, render_sample_rate(), gen.master_clock, MCLKS_PER_YM, render_audio_buffer(), ym_log ? YM_OPT_WAVE_LOG : 0); psg_context p_context; - psg_init(&p_context, render_sample_rate(), fps == 60 ? MCLKS_NTSC : MCLKS_PAL, MCLKS_PER_PSG, render_audio_buffer()); + psg_init(&p_context, render_sample_rate(), gen.master_clock, MCLKS_PER_PSG, render_audio_buffer()); z80_context z_context; x86_z80_options z_opts; init_x86_z80_opts(&z_opts); init_z80_context(&z_context, &z_opts); - genesis_context gen; - memset(&gen, 0, sizeof(gen)); - z_context.system = &gen; z_context.mem_pointers[0] = z80_ram; z_context.sync_cycle = z_context.target_cycle = mclks_per_frame/MCLKS_PER_Z80; diff --git a/blastem.h b/blastem.h index 68bccfd..0ec7c61 100644 --- a/blastem.h +++ b/blastem.h @@ -1,6 +1,6 @@ /* Copyright 2013 Michael Pavone - This file is part of BlastEm. + This file is part of BlastEm. BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. */ #ifndef BLASTEM_H_ @@ -30,6 +30,8 @@ typedef struct { uint8_t *save_ram; 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) uint8_t bank_regs[8]; io_port ports[3]; } genesis_context; @@ -43,6 +45,7 @@ extern uint8_t reset; uint16_t read_dma_value(uint32_t address); m68k_context * debugger(m68k_context * context, uint32_t address); +void set_speed_percent(genesis_context * context, uint32_t percent); #endif //BLASTEM_H_ diff --git a/default.cfg b/default.cfg index 1959d5c..ef8bace 100644 --- a/default.cfg +++ b/default.cfg @@ -19,6 +19,16 @@ bindings { u ui.enter_debugger esc ui.exit ` ui.save_state + 0 ui.set_speed.0 + 1 ui.set_speed.1 + 2 ui.set_speed.2 + 3 ui.set_speed.3 + 4 ui.set_speed.4 + 5 ui.set_speed.5 + 6 ui.set_speed.6 + 7 ui.set_speed.7 + = ui.next_speed + - ui.prev_speed } pads { 0 { @@ -53,5 +63,17 @@ audio { buffer 512 } +clocks { + speeds { + 1 150 + 2 200 + 3 300 + 4 400 + 5 25 + 6 50 + 7 75 + } +} + default_region U diff --git a/io.c b/io.c index b9e95c1..c83f756 100644 --- a/io.c +++ b/io.c @@ -1,6 +1,6 @@ /* Copyright 2013 Michael Pavone - This file is part of BlastEm. + This file is part of BlastEm. BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. */ #include "io.h" @@ -18,7 +18,10 @@ typedef enum { UI_DEBUG_MODE_INC, UI_DEBUG_PAL_INC, UI_ENTER_DEBUGGER, - UI_SAVE_STATE, + UI_SAVE_STATE, + UI_SET_SPEED, + UI_NEXT_SPEED, + UI_PREV_SPEED, UI_EXIT } ui_action; @@ -139,9 +142,9 @@ void bind_dpad_gamepad(int joystick, int dpad, uint8_t direction, int gamepadnum bind_dpad(joystick, dpad, direction, bind_type, button >> 12, button >> 8 & 0xF, button & 0xFF); } -void bind_ui(int keycode, ui_action action) +void bind_ui(int keycode, ui_action action, uint8_t param) { - bind_key(keycode, BIND_UI, action, 0, 0); + bind_key(keycode, BIND_UI, action, 0, param); } void handle_binding_down(keybinding * binding) @@ -183,6 +186,10 @@ void handle_joydown(int joystick, int button) uint8_t ui_debug_mode = 0; uint8_t ui_debug_pal = 0; +int current_speed = 0; +int num_speeds = 1; +uint32_t * speeds = NULL; + void handle_binding_up(keybinding * binding) { switch(binding->bind_type) @@ -219,6 +226,32 @@ void handle_binding_up(keybinding * binding) case UI_SAVE_STATE: save_state = 1; break; + case UI_NEXT_SPEED: + current_speed++; + if (current_speed >= num_speeds) { + current_speed = 0; + } + printf("Setting speed to %d: %d\n", current_speed, speeds[current_speed]); + set_speed_percent(genesis, speeds[current_speed]); + break; + case UI_PREV_SPEED: + current_speed--; + if (current_speed < 0) { + current_speed = num_speeds - 1; + } + printf("Setting speed to %d: %d\n", current_speed, speeds[current_speed]); + set_speed_percent(genesis, speeds[current_speed]); + break; + case UI_SET_SPEED: + if (binding->value < num_speeds) { + current_speed = binding->value; + printf("Setting speed to %d: %d\n", current_speed, speeds[current_speed]); + set_speed_percent(genesis, speeds[current_speed]); + } else { + printf("Setting speed to %d\n", speeds[current_speed]); + set_speed_percent(genesis, binding->value); + } + break; case UI_EXIT: exit(0); } @@ -286,6 +319,7 @@ int parse_binding_target(char * target, tern_node * padbuttons, int * ui_out, in fprintf(stderr, "Gamepad mapping string '%s' refers to an invalid gamepad number %c\n", target, target[gpadslen]); } } else if(!memcmp(target, "ui.", strlen("ui."))) { + *padbutton_out = 0; if (!strcmp(target + 3, "vdp_debug_mode")) { *ui_out = UI_DEBUG_MODE_INC; } else if(!strcmp(target + 3, "vdp_debug_pal")) { @@ -294,6 +328,13 @@ int parse_binding_target(char * target, tern_node * padbuttons, int * ui_out, in *ui_out = UI_ENTER_DEBUGGER; } else if(!strcmp(target + 3, "save_state")) { *ui_out = UI_SAVE_STATE; + } else if(!memcmp(target + 3, "set_speed.", strlen("set_speed."))) { + *ui_out = UI_SET_SPEED; + *padbutton_out = atoi(target + 3 + strlen("set_speed.")); + } else if(!strcmp(target + 3, "next_speed")) { + *ui_out = UI_NEXT_SPEED; + } else if(!strcmp(target + 3, "prev_speed")) { + *ui_out = UI_PREV_SPEED; } else if(!strcmp(target + 3, "exit")) { *ui_out = UI_EXIT; } else { @@ -309,7 +350,7 @@ int parse_binding_target(char * target, tern_node * padbuttons, int * ui_out, in void process_keys(tern_node * cur, tern_node * special, tern_node * padbuttons, char * prefix) { - char * curstr; + char * curstr = NULL; int len; if (!cur) { return; @@ -324,8 +365,8 @@ void process_keys(tern_node * cur, tern_node * special, tern_node * padbuttons, len = 0; } curstr[len] = cur->el; + curstr[len+1] = 0; if (cur->el) { - curstr[len+1] = 0; process_keys(cur->straight.next, special, padbuttons, curstr); } else { int keycode = tern_find_int(special, curstr, 0); @@ -341,11 +382,59 @@ void process_keys(tern_node * cur, tern_node * special, tern_node * padbuttons, if (bindtype == 1) { bind_gamepad(keycode, padnum, button); } else if(bindtype == 2) { - bind_ui(keycode, ui_func); + bind_ui(keycode, ui_func, button); } } process_keys(cur->left, special, padbuttons, prefix); process_keys(cur->right, special, padbuttons, prefix); + if (curstr && len) { + free(curstr); + } +} + +void process_speeds(tern_node * cur, char * prefix) +{ + char * curstr = NULL; + int len; + if (!cur) { + return; + } + char onec[2]; + if (prefix) { + len = strlen(prefix); + curstr = malloc(len + 2); + memcpy(curstr, prefix, len); + } else { + curstr = onec; + len = 0; + } + curstr[len] = cur->el; + curstr[len+1] = 0; + if (cur->el) { + process_speeds(cur->straight.next, curstr); + } else { + int speed_index = atoi(curstr); + if (speed_index < 1) { + if (!strcmp(curstr, "0")) { + fputs("Speed index 0 cannot be set to a custom value\n", stderr); + } else { + fprintf(stderr, "%s is not a valid speed index", curstr); + } + } else { + if (speed_index >= num_speeds) { + speeds = realloc(speeds, sizeof(uint32_t) * (speed_index+1)); + for(; num_speeds < speed_index + 1; num_speeds++) { + speeds[num_speeds] = 0; + } + } + speeds[speed_index] = atoi(cur->straight.value.ptrval); + } + } + process_speeds(cur->left, prefix); + process_speeds(cur->right, prefix); + if (curstr && len) { + free(curstr); + } } void set_keybindings() @@ -425,6 +514,16 @@ void set_keybindings() } } } + tern_node * speed_nodes = tern_find_prefix(config, "clocksspeeds"); + speeds = malloc(sizeof(uint32_t)); + speeds[0] = 100; + process_speeds(speed_nodes, NULL); + for (int i = 0; i < num_speeds; i++) { + if (!speeds[i]) { + fprintf(stderr, "Speed index %d was not set to a valid percentage!", i); + speeds[i] = 100; + } + } } #define TH 0x40 diff --git a/psg.c b/psg.c index 036a693..6ea47f9 100644 --- a/psg.c +++ b/psg.c @@ -1,6 +1,6 @@ /* Copyright 2013 Michael Pavone - This file is part of BlastEm. + This file is part of BlastEm. BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. */ #include "psg.h" @@ -13,15 +13,23 @@ void psg_init(psg_context * context, uint32_t sample_rate, uint32_t master_clock memset(context, 0, sizeof(*context)); context->audio_buffer = malloc(sizeof(*context->audio_buffer) * samples_frame); context->back_buffer = malloc(sizeof(*context->audio_buffer) * samples_frame); - double clock_rate = (double)master_clock / (double)clock_div; - context->buffer_inc = ((double)sample_rate / (double)master_clock) * clock_div; context->clock_inc = clock_div; + context->sample_rate = sample_rate; context->samples_frame = samples_frame; + psg_adjust_master_clock(context, master_clock); for (int i = 0; i < 4; i++) { context->volume[i] = 0xF; } } +#define BUFFER_INC_RES 1000000000UL + +void psg_adjust_master_clock(psg_context * context, uint32_t master_clock) +{ + uint64_t old_inc = context->buffer_inc; + context->buffer_inc = ((BUFFER_INC_RES * (uint64_t)context->sample_rate) / (uint64_t)master_clock) * (uint64_t)context->clock_inc; +} + void psg_write(psg_context * context, uint8_t value) { if (value & 0x80) { @@ -70,7 +78,7 @@ void psg_write(psg_context * context, uint8_t value) //table shamelessly swiped from PSG doc from smspower.org int16_t volume_table[16] = { 32767/PSG_VOL_DIV, 26028/PSG_VOL_DIV, 20675/PSG_VOL_DIV, 16422/PSG_VOL_DIV, 13045/PSG_VOL_DIV, 10362/PSG_VOL_DIV, - 8231/PSG_VOL_DIV, 6568/PSG_VOL_DIV, 5193/PSG_VOL_DIV, 4125/PSG_VOL_DIV, 3277/PSG_VOL_DIV, 2603/PSG_VOL_DIV, + 8231/PSG_VOL_DIV, 6568/PSG_VOL_DIV, 5193/PSG_VOL_DIV, 4125/PSG_VOL_DIV, 3277/PSG_VOL_DIV, 2603/PSG_VOL_DIV, 2067/PSG_VOL_DIV, 1642/PSG_VOL_DIV, 1304/PSG_VOL_DIV, 0 }; @@ -97,8 +105,8 @@ void psg_run(psg_context * context, uint32_t cycles) } } context->buffer_fraction += context->buffer_inc; - if (context->buffer_fraction >= 1.0) { - context->buffer_fraction -= 1.0; + if (context->buffer_fraction >= BUFFER_INC_RES) { + context->buffer_fraction -= BUFFER_INC_RES; int16_t acc = 0; for (int i = 0; i < 3; i++) { if (context->output_state[i]) { diff --git a/psg.h b/psg.h index 503bac2..4cd4552 100644 --- a/psg.h +++ b/psg.h @@ -1,6 +1,6 @@ /* Copyright 2013 Michael Pavone - This file is part of BlastEm. + This file is part of BlastEm. BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. */ #ifndef PSG_CONTEXT_H_ @@ -11,11 +11,12 @@ typedef struct { int16_t *audio_buffer; int16_t *back_buffer; - double buffer_fraction; - double buffer_inc; + uint64_t buffer_fraction; + uint64_t buffer_inc; uint32_t buffer_pos; uint32_t clock_inc; uint32_t cycles; + uint32_t sample_rate; uint32_t samples_frame; uint16_t lsfr; uint16_t counter_load[4]; @@ -30,6 +31,7 @@ typedef struct { void psg_init(psg_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t samples_frame); +void psg_adjust_master_clock(psg_context * context, uint32_t master_clock); void psg_write(psg_context * context, uint8_t value); void psg_run(psg_context * context, uint32_t cycles); diff --git a/ym2612.c b/ym2612.c index a1ab1a0..2f7862d 100644 --- a/ym2612.c +++ b/ym2612.c @@ -1,6 +1,6 @@ /* Copyright 2013 Michael Pavone - This file is part of BlastEm. + This file is part of BlastEm. BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. */ #include @@ -127,6 +127,13 @@ void ym_finalize_log() } } } +#define BUFFER_INC_RES 1000000000UL + +void ym_adjust_master_clock(ym2612_context * context, uint32_t master_clock) +{ + uint64_t old_inc = context->buffer_inc; + context->buffer_inc = ((BUFFER_INC_RES * (uint64_t)context->sample_rate) / (uint64_t)master_clock) * (uint64_t)context->clock_inc; +} void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t sample_limit, uint32_t options) { @@ -134,8 +141,10 @@ void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t master_clo memset(context, 0, sizeof(*context)); context->audio_buffer = malloc(sizeof(*context->audio_buffer) * sample_limit*2); context->back_buffer = malloc(sizeof(*context->audio_buffer) * sample_limit*2); - context->buffer_inc = ((double)sample_rate / (double)master_clock) * clock_div * 6; + context->sample_rate = sample_rate; context->clock_inc = clock_div * 6; + ym_adjust_master_clock(context, master_clock); + context->sample_limit = sample_limit*2; context->write_cycle = CYCLE_NEVER; for (int i = 0; i < NUM_OPERATORS; i++) { @@ -451,29 +460,29 @@ void ym_run(ym2612_context * context, uint32_t to_cycle) context->buffer_fraction += context->buffer_inc; if (context->current_op == NUM_OPERATORS) { context->current_op = 0; - if (context->buffer_fraction > 1.0) { - context->buffer_fraction -= 1.0; - context->audio_buffer[context->buffer_pos] = 0; - context->audio_buffer[context->buffer_pos + 1] = 0; - for (int i = 0; i < NUM_CHANNELS; i++) { - int16_t value = context->channels[i].output & 0x3FE0; - if (value & 0x2000) { - value |= 0xC000; - } - if (context->channels[i].logfile) { - fwrite(&value, sizeof(value), 1, context->channels[i].logfile); - } - if (context->channels[i].lr & 0x80) { - context->audio_buffer[context->buffer_pos] += value / YM_VOLUME_DIVIDER; - } - if (context->channels[i].lr & 0x40) { - context->audio_buffer[context->buffer_pos+1] += value / YM_VOLUME_DIVIDER; - } + } + if (context->buffer_fraction > BUFFER_INC_RES) { + context->buffer_fraction -= BUFFER_INC_RES; + context->audio_buffer[context->buffer_pos] = 0; + context->audio_buffer[context->buffer_pos + 1] = 0; + for (int i = 0; i < NUM_CHANNELS; i++) { + int16_t value = context->channels[i].output & 0x3FE0; + if (value & 0x2000) { + value |= 0xC000; } - context->buffer_pos += 2; - if (context->buffer_pos == context->sample_limit) { - render_wait_ym(context); + if (context->channels[i].logfile) { + fwrite(&value, sizeof(value), 1, context->channels[i].logfile); } + if (context->channels[i].lr & 0x80) { + context->audio_buffer[context->buffer_pos] += value / YM_VOLUME_DIVIDER; + } + if (context->channels[i].lr & 0x40) { + context->audio_buffer[context->buffer_pos+1] += value / YM_VOLUME_DIVIDER; + } + } + context->buffer_pos += 2; + if (context->buffer_pos == context->sample_limit) { + render_wait_ym(context); } } } diff --git a/ym2612.h b/ym2612.h index 8516492..16a8cdf 100644 --- a/ym2612.h +++ b/ym2612.h @@ -1,6 +1,6 @@ /* Copyright 2013 Michael Pavone - This file is part of BlastEm. + This file is part of BlastEm. BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. */ #ifndef YM2612_H_ @@ -59,10 +59,11 @@ typedef struct { typedef struct { int16_t *audio_buffer; int16_t *back_buffer; - double buffer_fraction; - double buffer_inc; + uint64_t buffer_fraction; + uint64_t buffer_inc; uint32_t clock_inc; uint32_t buffer_pos; + uint32_t sample_rate; uint32_t sample_limit; uint32_t current_cycle; uint32_t write_cycle; @@ -93,6 +94,7 @@ typedef struct { } ym2612_context; void ym_init(ym2612_context * context, uint32_t sample_rate, uint32_t master_clock, uint32_t clock_div, uint32_t sample_limit, uint32_t options); +void ym_adjust_master_clock(ym2612_context * context, uint32_t master_clock); void ym_run(ym2612_context * context, uint32_t to_cycle); void ym_address_write_part1(ym2612_context * context, uint8_t address); void ym_address_write_part2(ym2612_context * context, uint8_t address); -- cgit v1.2.3 From 68b4a4c04df8684eeee60e07be5ba80d4842bcd7 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Thu, 3 Oct 2013 21:20:29 -0700 Subject: Add support for test instruction to x86 generator library --- gen_x86.c | 32 +++++++++++++++++++++++++++++--- gen_x86.h | 7 ++++++- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/gen_x86.c b/gen_x86.c index 096059f..e7fa009 100644 --- a/gen_x86.c +++ b/gen_x86.c @@ -1,6 +1,6 @@ /* Copyright 2013 Michael Pavone - This file is part of BlastEm. + This file is part of BlastEm. BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. */ #include "gen_x86.h" @@ -30,6 +30,7 @@ #define PRE_SIZE 0x66 #define OP_JCC 0x70 #define OP_IMMED_ARITH 0x80 +#define OP_TEST 0x84 #define OP_XCHG 0x86 #define OP_MOV 0x88 #define OP_XCHG_AX 0x90 @@ -430,7 +431,7 @@ uint8_t * x86_rdisp8_size(uint8_t * out, uint8_t opcode, uint8_t opex, uint8_t d uint8_t * x86_ir(uint8_t * out, uint8_t opcode, uint8_t op_ex, uint8_t al_opcode, int32_t val, uint8_t dst, uint8_t size) { uint8_t sign_extend = 0; - if ((size == SZ_D || size == SZ_Q) && val <= 0x7F && val >= -0x80) { + if (opcode != OP_NOT_NEG && (size == SZ_D || size == SZ_Q) && val <= 0x7F && val >= -0x80) { sign_extend = 1; opcode |= BIT_DIR; } @@ -1023,6 +1024,31 @@ uint8_t * cmp_rdisp8r(uint8_t * out, uint8_t src_base, int8_t disp, uint8_t dst, return x86_rrdisp8_sizedir(out, OP_CMP, dst, src_base, disp, size, BIT_DIR); } +uint8_t * test_rr(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size) +{ + return x86_rr_sizedir(out, OP_TEST, src, dst, size); +} + +uint8_t * test_ir(uint8_t * out, int32_t val, uint8_t dst, uint8_t size) +{ + return x86_ir(out, OP_NOT_NEG, OP_EX_TEST_I, OP_TEST, val, dst, size); +} + +uint8_t * test_irdisp8(uint8_t * out, int32_t val, uint8_t dst_base, int8_t disp, uint8_t size) +{ + return x86_irdisp8(out, OP_NOT_NEG, OP_EX_TEST_I, val, dst_base, disp, size); +} + +uint8_t * test_rrdisp8(uint8_t * out, uint8_t src, uint8_t dst_base, int8_t disp, uint8_t size) +{ + return x86_rrdisp8_sizedir(out, OP_TEST, src, dst_base, disp, size, 0); +} + +uint8_t * test_rdisp8r(uint8_t * out, uint8_t src_base, int8_t disp, uint8_t dst, uint8_t size) +{ + return x86_rrdisp8_sizedir(out, OP_TEST, dst, src_base, disp, size, BIT_DIR); +} + uint8_t * imul_rr(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size) { return x86_rr_sizedir(out, OP2_IMUL | (PRE_2BYTE << 8), dst, src, size); @@ -1139,7 +1165,7 @@ uint8_t * mov_rindexr(uint8_t * out, uint8_t src_base, uint8_t src_index, uint8_ } uint8_t * mov_ir(uint8_t * out, int64_t val, uint8_t dst, uint8_t size) -{ +{ uint8_t sign_extend = 0; if (size == SZ_Q && val <= 0x7FFFFFFF && val >= -2147483648) { sign_extend = 1; diff --git a/gen_x86.h b/gen_x86.h index aa9d851..83983f6 100644 --- a/gen_x86.h +++ b/gen_x86.h @@ -1,6 +1,6 @@ /* Copyright 2013 Michael Pavone - This file is part of BlastEm. + This file is part of BlastEm. BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. */ #ifndef GEN_X86_H_ @@ -154,6 +154,11 @@ uint8_t * mul_rdisp8(uint8_t * out, uint8_t dst_base, int8_t disp, uint8_t size) uint8_t * imul_rdisp8(uint8_t * out, uint8_t dst_base, int8_t disp, uint8_t size); uint8_t * div_rdisp8(uint8_t * out, uint8_t dst_base, int8_t disp, uint8_t size); uint8_t * idiv_rdisp8(uint8_t * out, uint8_t dst_base, int8_t disp, uint8_t size); +uint8_t * test_rr(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size); +uint8_t * test_ir(uint8_t * out, int32_t val, uint8_t dst, uint8_t size); +uint8_t * test_irdisp8(uint8_t * out, int32_t val, uint8_t dst_base, int8_t disp, uint8_t size); +uint8_t * test_rrdisp8(uint8_t * out, uint8_t src, uint8_t dst_base, int8_t disp, uint8_t size); +uint8_t * test_rdisp8r(uint8_t * out, uint8_t src_base, int8_t disp, uint8_t dst, uint8_t size); uint8_t * mov_rr(uint8_t * out, uint8_t src, uint8_t dst, uint8_t size); uint8_t * mov_rrdisp8(uint8_t * out, uint8_t src, uint8_t dst_base, int8_t disp, uint8_t size); uint8_t * mov_rdisp8r(uint8_t * out, uint8_t src_base, int8_t disp, uint8_t dst, uint8_t size); -- cgit v1.2.3 From 4cf2c9baeabf8b64b36170d864ec44e7215a89df Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Thu, 3 Oct 2013 21:21:47 -0700 Subject: Follow amd64 ABI stack alignment requirements in places it matters so we can call sprintf with floating point arguments without crashing --- m68k_to_x86.c | 32 +++++++++++++++++++++++++++++++- runtime.S | 38 +++++++++++++++++++++++++++----------- 2 files changed, 58 insertions(+), 12 deletions(-) diff --git a/m68k_to_x86.c b/m68k_to_x86.c index a9e71e4..219d4c9 100644 --- a/m68k_to_x86.c +++ b/m68k_to_x86.c @@ -1,6 +1,6 @@ /* Copyright 2013 Michael Pavone - This file is part of BlastEm. + This file is part of BlastEm. BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. */ #include "gen_x86.h" @@ -4246,7 +4246,17 @@ uint8_t * gen_mem_fun(x86_68k_options * opts, memmap_chunk * memmap, uint32_t nu dst = push_r(dst, CONTEXT); dst = mov_rr(dst, SCRATCH1, RDI, SZ_D); } + dst = test_ir(dst, 8, RSP, SZ_D); + uint8_t *adjust_rsp = dst+1; + dst = jcc(dst, CC_NZ, dst+2); + dst = call(dst, cfun); + uint8_t *no_adjust = dst+1; + dst = jmp(dst, dst+2); + *adjust_rsp = dst - (adjust_rsp + 1); + dst = sub_ir(dst, 8, RSP, SZ_Q); dst = call(dst, cfun); + dst = add_ir(dst, 8, RSP, SZ_Q); + *no_adjust = dst - (no_adjust + 1); if (is_write) { dst = mov_rr(dst, RAX, CONTEXT, SZ_Q); } else { @@ -4339,7 +4349,17 @@ uint8_t * gen_mem_fun(x86_68k_options * opts, memmap_chunk * memmap, uint32_t nu dst = push_r(dst, CONTEXT); dst = mov_rr(dst, SCRATCH1, RDI, SZ_D); } + dst = test_ir(dst, 8, RSP, SZ_D); + uint8_t *adjust_rsp = dst+1; + dst = jcc(dst, CC_NZ, dst+2); dst = call(dst, cfun); + uint8_t *no_adjust = dst+1; + dst = jmp(dst, dst+2); + *adjust_rsp = dst - (adjust_rsp + 1); + dst = sub_ir(dst, 8, RSP, SZ_Q); + dst = call(dst, cfun); + dst = add_ir(dst, 8, RSP, SZ_Q); + *no_adjust = dst - (no_adjust+1); if (is_write) { dst = mov_rr(dst, RAX, CONTEXT, SZ_Q); } else { @@ -4444,7 +4464,17 @@ void init_x86_68k_opts(x86_68k_options * opts, memmap_chunk * memmap, uint32_t n dst = call(dst, (uint8_t *)m68k_save_context); dst = mov_rr(dst, CONTEXT, RDI, SZ_Q); dst = mov_rr(dst, SCRATCH1, RSI, SZ_D); + dst = test_ir(dst, 8, RSP, SZ_D); + uint8_t *adjust_rsp = dst+1; + dst = jcc(dst, CC_NZ, dst+2); + dst = call(dst, (uint8_t *)sync_components); + uint8_t *no_adjust = dst+1; + dst = jmp(dst, dst+2); + *adjust_rsp = dst - (adjust_rsp + 1); + dst = sub_ir(dst, 8, RSP, SZ_Q); dst = call(dst, (uint8_t *)sync_components); + dst = add_ir(dst, 8, RSP, SZ_Q); + *no_adjust = dst - (no_adjust+1); dst = mov_rr(dst, RAX, CONTEXT, SZ_Q); dst = jmp(dst, (uint8_t *)m68k_load_context); *skip_sync = dst - (skip_sync+1); diff --git a/runtime.S b/runtime.S index 357eabe..36ecfff 100644 --- a/runtime.S +++ b/runtime.S @@ -10,14 +10,22 @@ do_sync: call m68k_save_context mov %rsi, %rdi xor %esi, %esi + test $8, %esp + jnz adjust_rsp call sync_components + jmp done_adjust +adjust_rsp: + sub $8, %rsp + call sync_components + add $8, %rsp +done_adjust: mov %rax, %rsi call m68k_load_context pop %rdi pop %rcx skip_sync: ret - + sr_msg_int: .asciz "SR set to $%X due to interrupt\n" debug_print_sr_int: @@ -47,7 +55,7 @@ debug_print_sr: invalid_msg: .asciz "Invalid instruction at %X\n" - + .global m68k_invalid m68k_invalid: lea invalid_msg(%rip), %rdi @@ -60,7 +68,7 @@ m68k_invalid: .global bcd_add bcd_add: xchg %rax, %rdi - + mov %cl, %ch mov %al, %ah and $0xF, %ch @@ -82,14 +90,14 @@ def_adjust: add $0x60, %cl mov $1, %ch no_adjust_h: - + mov %rdi, %rax ret .global bcd_sub bcd_sub: xchg %rax, %rdi - + mov %cl, %ch mov %al, %ah and $0xF, %ch @@ -111,7 +119,7 @@ def_adjusts: sub $0x60, %cl mov $1, %ch no_adjust_hs: - + mov %rdi, %rax ret @@ -168,7 +176,7 @@ set_ccr: and $1, %cl mov %cl, (%rsi) ret - + .global m68k_modified_ret_addr m68k_modified_ret_addr: add $16, %rsp @@ -181,7 +189,15 @@ m68k_native_addr_and_sync: push %rcx mov %rsi, %rdi xor %esi, %esi + test $8, %rsp + jnz adjust_rsp_na + call sync_components + jmp no_adjust_rsp_na +adjust_rsp_na: + sub $8, %rsp call sync_components + add $8, %rsp +no_adjust_rsp_na: pop %rsi push %rax mov %rax, %rdi @@ -202,7 +218,7 @@ m68k_native_addr: pop %rsi call m68k_load_context ret - + .global m68k_retrans_stub m68k_retrans_stub: call m68k_save_context @@ -255,15 +271,15 @@ m68k_start_context: push %r13 push %r14 push %r15 - + call m68k_load_context call *%rdi call m68k_save_context - + pop %r15 pop %r14 pop %r13 pop %r12 pop %rbp - + ret -- cgit v1.2.3 From b8dc9d69563379341f127af571b4bec1312f4ca0 Mon Sep 17 00:00:00 2001 From: Mike Pavone Date: Thu, 3 Oct 2013 21:22:05 -0700 Subject: Add an FPS counter to the title bar --- render_sdl.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/render_sdl.c b/render_sdl.c index 8954deb..3cbc604 100644 --- a/render_sdl.c +++ b/render_sdl.c @@ -93,6 +93,8 @@ uint8_t render_depth() return screen->format->BytesPerPixel * 8; } +char * caption = NULL; + void render_init(int width, int height, char * title, uint32_t fps, uint8_t fullscreen) { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0) { @@ -118,6 +120,7 @@ void render_init(int width, int height, char * title, uint32_t fps, uint8_t full exit(1); } SDL_WM_SetCaption(title, title); + caption = title; min_delay = 0; for (int i = 0; i < 100; i++) { uint32_t start = SDL_GetTicks(); @@ -326,6 +329,8 @@ int32_t handle_event(SDL_Event *event) return 0; } +char * fps_caption = NULL; + uint32_t frame_counter = 0; uint32_t start = 0; int wait_render_frame(vdp_context * context, int frame_limit) @@ -349,18 +354,22 @@ int wait_render_frame(vdp_context * context, int frame_limit) } } render_context(context); - - + + //TODO: Figure out why this causes segfaults - /*frame_counter++; + frame_counter++; if ((last_frame - start) > 1000) { if (start && (last_frame-start)) { - printf("\r%f fps", ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0)); + if (!fps_caption) { + fps_caption = malloc(strlen(caption) + strlen(" - 1000.1 fps") + 1); + } + sprintf(fps_caption, "%s - %.1f fps", caption, ((float)frame_counter) / (((float)(last_frame-start)) / 1000.0)); + SDL_WM_SetCaption(fps_caption, caption); fflush(stdout); } start = last_frame; frame_counter = 0; - }*/ + } return ret; } -- cgit v1.2.3