# HG changeset patch # User Mike Pavone # Date 1383030191 25200 # Node ID 27345a67225d0009dc46accb4fb22dc20fdc1609 # Parent 51bf87f76d15c139e8f04deb36d4e4a377ecabab# Parent 8ac0eb05642c8db1c078a0a8a38559571016e948 Merge diff -r 8ac0eb05642c -r 27345a67225d Makefile --- a/Makefile Mon Oct 07 10:02:08 2013 -0700 +++ b/Makefile Tue Oct 29 00:03:11 2013 -0700 @@ -1,4 +1,8 @@ +ifdef NOGL LIBS=sdl +else +LIBS=sdl glew gl +endif LDFLAGS=-lm `pkg-config --libs $(LIBS)` ifdef DEBUG CFLAGS=-ggdb -std=gnu99 `pkg-config --cflags-only-I $(LIBS)` -Wreturn-type -Werror=return-type @@ -10,16 +14,20 @@ CFLAGS+= -pg LDFLAGS+= -pg endif +ifdef NOGL +CFLAGS+= -DDISABLE_OPENGL +endif TRANSOBJS=gen_x86.o x86_backend.o mem.o 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 +CONFIGOBJS=config.o tern.o util.o 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) +blastem : blastem.o vdp.o render_sdl.o io.o $(CONFIGOBJS) gst.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS) + $(CC) -ggdb -o blastem blastem.o vdp.o render_sdl.o io.o $(CONFIGOBJS) gst.o $(M68KOBJS) $(Z80OBJS) $(TRANSOBJS) $(AUDIOOBJS) $(LDFLAGS) dis : dis.o 68kinst.o $(CC) -o dis dis.o 68kinst.o @@ -42,11 +50,11 @@ ztestgen : ztestgen.o z80inst.o $(CC) -o 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)` +stateview : stateview.o vdp.o render_sdl.o $(CONFIGOBJS) gst.o + $(CC) -o stateview stateview.o vdp.o render_sdl.o $(CONFIGOBJS) gst.o $(LDFLAGS) -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) +vgmplay : vgmplay.o render_sdl.o $(CONFIGOBJS) $(AUDIOOBJS) + $(CC) -o vgmplay vgmplay.o render_sdl.o $(CONFIGOBJS) $(AUDIOOBJS) $(LDFLAGS) testgst : testgst.o gst.o $(CC) -o testgst testgst.o gst.o diff -r 8ac0eb05642c -r 27345a67225d blastem.c --- a/blastem.c Mon Oct 07 10:02:08 2013 -0700 +++ b/blastem.c Tue Oct 29 00:03:11 2013 -0700 @@ -11,6 +11,7 @@ #include "render.h" #include "blastem.h" #include "gst.h" +#include "util.h" #include #include #include @@ -278,9 +279,9 @@ adjust_int_cycle(context, v_context); if (address) { if (break_on_sync) { - break_on_sync = 0; - debugger(context, address); - } + break_on_sync = 0; + debugger(context, address); + } if (save_state) { save_state = 0; while (!z_context->pc) @@ -1507,7 +1508,7 @@ 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); } @@ -1762,7 +1763,8 @@ fputs("Usage: blastem [OPTIONS] ROMFILE [WIDTH] [HEIGHT]\n", stderr); return 1; } - config = load_config(argv[0]); + set_exe_str(argv[0]); + config = load_config(); detect_region(); int width = -1; int height = -1; @@ -1773,7 +1775,7 @@ char * romfname = NULL; FILE *address_log = NULL; char * statefile = NULL; - uint8_t fullscreen = 0; + uint8_t fullscreen = 0, use_gl = 0; for (int i = 1; i < argc; i++) { if (argv[i][0] == '-') { switch(argv[i][1]) { @@ -1783,6 +1785,9 @@ case 'f': fullscreen = 1; break; + case 'g': + use_gl = 1; + break; case 'l': address_log = fopen("address.log", "w"); break; @@ -1885,7 +1890,7 @@ fps = 50; } if (!headless) { - render_init(width, height, title, fps, fullscreen); + render_init(width, height, title, fps, fullscreen, use_gl); } vdp_context v_context; genesis_context gen; diff -r 8ac0eb05642c -r 27345a67225d config.c --- a/config.c Mon Oct 07 10:02:08 2013 -0700 +++ b/config.c Tue Oct 29 00:03:11 2013 -0700 @@ -1,79 +1,13 @@ /* 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 "tern.h" +#include "util.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 @@ -164,32 +98,9 @@ return ret; } -char * readlink_alloc(char * path) +tern_node * load_config() { - 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 * exe_dir; char * home = getenv("HOME"); if (!home) { goto load_in_app_dir; @@ -201,33 +112,18 @@ } 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; - } + exe_dir = get_exe_dir(); + if (!exe_dir) { + goto no_config; } - if (cur == linktext) { - goto link_prob; - } - path = alloc_concat(linktext, "/default.cfg"); + path = alloc_concat(exe_dir, "/default.cfg"); ret = parse_config_file(path); + free(path); success: - return ret; -link_prob: - if (linktext) { - free(linktext); + if (ret) { + return ret; } -no_proc: - //TODO: Fall back to using expath if /proc is not available +no_config: fputs("Failed to find a config file in ~/.config/blastem/blastem.cfg or in the blastem executable directory\n", stderr); exit(1); } diff -r 8ac0eb05642c -r 27345a67225d config.h --- a/config.h Mon Oct 07 10:02:08 2013 -0700 +++ b/config.h Tue Oct 29 00:03:11 2013 -0700 @@ -1,13 +1,13 @@ /* 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 CONFIG_H_ #define CONFIG_H_ #include "tern.h" -tern_node * load_config(char * expath); +tern_node * load_config(); #endif //CONFIG_H_ diff -r 8ac0eb05642c -r 27345a67225d default.cfg --- a/default.cfg Mon Oct 07 10:02:08 2013 -0700 +++ b/default.cfg Tue Oct 29 00:03:11 2013 -0700 @@ -56,6 +56,8 @@ video { width 640 + vertex_shader default.v.glsl + fragment_shader default.f.glsl } audio { diff -r 8ac0eb05642c -r 27345a67225d render.h --- a/render.h Mon Oct 07 10:02:08 2013 -0700 +++ b/render.h Tue Oct 29 00:03:11 2013 -0700 @@ -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 RENDER_H_ @@ -9,9 +9,17 @@ #include "vdp.h" #include "psg.h" #include "ym2612.h" + +typedef struct { + void *oddbuf; + void *evenbuf; + int stride; +} surface_info; + uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b); +void render_alloc_surfaces(vdp_context * context); uint8_t render_depth(); -void render_init(int width, int height, char * title, uint32_t fps, uint8_t fullscreen); +void render_init(int width, int height, char * title, uint32_t fps, uint8_t fullscreen, uint8_t use_gl); void render_context(vdp_context * context); void render_wait_quit(vdp_context * context); void render_wait_psg(psg_context * context); diff -r 8ac0eb05642c -r 27345a67225d render_sdl.c --- a/render_sdl.c Mon Oct 07 10:02:08 2013 -0700 +++ b/render_sdl.c Tue Oct 29 00:03:11 2013 -0700 @@ -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 @@ -8,10 +8,16 @@ #include "render.h" #include "blastem.h" #include "io.h" +#include "util.h" + +#ifndef DISABLE_OPENGL +#include +#endif SDL_Surface *screen; uint8_t render_dbg = 0; uint8_t debug_pal = 0; +uint8_t render_gl; uint32_t last_frame = 0; @@ -85,7 +91,118 @@ uint32_t render_map_color(uint8_t r, uint8_t g, uint8_t b) { - return SDL_MapRGB(screen->format, r, g, b); + if (render_gl) { + return 255 << 24 | r << 16 | g << 8 | b; + } else { + return SDL_MapRGB(screen->format, r, g, b); + } +} + +#ifndef DISABLE_OPENGL +GLuint textures[3], buffers[2], vshader, fshader, program, un_textures[2], at_pos; + +const GLfloat vertex_data[] = { + -1.0f, -1.0f, + 1.0f, -1.0f, + -1.0f, 1.0f, + 1.0f, 1.0f +}; + +const GLushort element_data[] = {0, 1, 2, 3}; + +GLuint load_shader(char * fname, GLenum shader_type) +{ + char * parts[] = {getenv("HOME"), "/.config/blastem/shaders/", fname}; + char * shader_path = alloc_concat_m(3, parts); + FILE * f = fopen(shader_path, "r"); + free(shader_path); + if (!f) { + parts[0] = get_exe_dir(); + parts[1] = "/shaders/"; + shader_path = alloc_concat_m(3, parts); + f = fopen(shader_path, "r"); + free(shader_path); + if (!f) { + fprintf(stderr, "Failed to open shader file %s for reading\n", fname); + return 0; + } + } + long fsize = file_size(f); + GLchar * text = malloc(fsize); + if (fread(text, 1, fsize, f) != fsize) { + fprintf(stderr, "Error reading from shader file %s\n", fname); + free(text); + return 0; + } + GLuint ret = glCreateShader(shader_type); + glShaderSource(ret, 1, (const GLchar **)&text, (const GLint *)&fsize); + free(text); + glCompileShader(ret); + GLint compile_status, loglen; + glGetShaderiv(ret, GL_COMPILE_STATUS, &compile_status); + if (!compile_status) { + fprintf(stderr, "Shader %s failed to compile\n", fname); + glGetShaderiv(ret, GL_INFO_LOG_LENGTH, &loglen); + text = malloc(loglen); + glGetShaderInfoLog(ret, loglen, NULL, text); + fputs(text, stderr); + free(text); + glDeleteShader(ret); + return 0; + } + return ret; +} +#endif + +void render_alloc_surfaces(vdp_context * context) +{ +#ifndef DISABLE_OPENGL + if (render_gl) { + context->oddbuf = context->framebuf = malloc(320 * 240 * 4 * 2); + memset(context->oddbuf, 0, 320 * 240 * 4 * 2); + context->evenbuf = ((char *)context->oddbuf) + 320 * 240 * 4; + glGenTextures(3, textures); + for (int i = 0; i < 3; i++) + { + glBindTexture(GL_TEXTURE_2D, textures[i]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (i < 2) { + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 512, 256, 0, GL_BGRA, GL_UNSIGNED_BYTE, i ? context->evenbuf : context->oddbuf); + } else { + uint32_t blank = 255 << 24; + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_BGRA, GL_UNSIGNED_BYTE, &blank); + } + } + glGenBuffers(2, buffers); + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_data), vertex_data, GL_STATIC_DRAW); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(element_data), element_data, GL_STATIC_DRAW); + vshader = load_shader(tern_find_ptr_default(config, "videovertex_shader", "default.v.glsl"), GL_VERTEX_SHADER); + fshader = load_shader(tern_find_ptr_default(config, "videofragment_shader", "default.f.glsl"), GL_FRAGMENT_SHADER); + program = glCreateProgram(); + glAttachShader(program, vshader); + glAttachShader(program, fshader); + glLinkProgram(program); + GLint link_status; + glGetProgramiv(program, GL_LINK_STATUS, &link_status); + if (!link_status) { + fputs("Failed to link shader program\n", stderr); + exit(1); + } + un_textures[0] = glGetUniformLocation(program, "textures[0]"); + un_textures[1] = glGetUniformLocation(program, "textures[1]"); + at_pos = glGetAttribLocation(program, "pos"); + } else { +#endif + context->oddbuf = context->framebuf = malloc(320 * 240 * screen->format->BytesPerPixel * 2); + context->evenbuf = ((char *)context->oddbuf) + 320 * 240 * screen->format->BytesPerPixel; +#ifndef DISABLE_OPENGL + } +#endif } uint8_t render_depth() @@ -95,62 +212,98 @@ char * caption = NULL; -void render_init(int width, int height, char * title, uint32_t fps, uint8_t fullscreen) +void render_init(int width, int height, char * title, uint32_t fps, uint8_t fullscreen, uint8_t use_gl) { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK) < 0) { - fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError()); - exit(1); - } - atexit(SDL_Quit); - atexit(render_close_audio); - printf("width: %d, height: %d\n", width, height); - 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); - } - if (screen->format->BytesPerPixel != 2 && screen->format->BytesPerPixel != 4) { - fprintf(stderr, "BlastEm requires a 16-bit or 32-bit surface, SDL returned a %d-bit surface\n", screen->format->BytesPerPixel * 8); - exit(1); - } - SDL_WM_SetCaption(title, title); + fprintf(stderr, "Unable to init SDL: %s\n", SDL_GetError()); + exit(1); + } + atexit(SDL_Quit); + atexit(render_close_audio); + printf("width: %d, height: %d\n", width, height); + uint32_t flags = SDL_ANYFORMAT; + +#ifndef DISABLE_OPENGL + if (use_gl) + { + SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5); + SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5); + SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5); + SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 0); + SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); + flags = SDL_OPENGL; + if (fullscreen) { + flags |= SDL_FULLSCREEN; + } + } else { +#else + { +#endif + 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); + } + if (!use_gl && screen->format->BytesPerPixel != 2 && screen->format->BytesPerPixel != 4) { + fprintf(stderr, "BlastEm requires a 16-bit or 32-bit surface, SDL returned a %d-bit surface\n", screen->format->BytesPerPixel * 8); + exit(1); + } +#ifndef DISABLE_OPENGL + //TODO: fallback on standard rendering if OpenGL 2.0 is unavailable or if init fails + if (use_gl) + { + GLenum res = glewInit(); + if (res != GLEW_OK) + { + fprintf(stderr, "Initialization of GLEW failed with code %d\n", res); + exit(1); + } + if (!GLEW_VERSION_2_0) + { + fputs("OpenGL 2.0 is unable, falling back to standard SDL rendering\n", stderr); + exit(1); + } + } + render_gl = use_gl; +#endif + SDL_WM_SetCaption(title, title); caption = title; - min_delay = 0; - for (int i = 0; i < 100; i++) { - uint32_t start = SDL_GetTicks(); - SDL_Delay(1); - uint32_t delay = SDL_GetTicks()-start; - if (delay > min_delay) { - min_delay = delay; - } - } - if (!min_delay) { - min_delay = 1; - } - printf("minimum delay: %d\n", min_delay); - - frame_delay = 1000/fps; - - audio_mutex = SDL_CreateMutex(); - psg_cond = SDL_CreateCond(); - ym_cond = SDL_CreateCond(); - audio_ready = SDL_CreateCond(); - - SDL_AudioSpec desired, actual; + min_delay = 0; + for (int i = 0; i < 100; i++) { + uint32_t start = SDL_GetTicks(); + SDL_Delay(1); + uint32_t delay = SDL_GetTicks()-start; + if (delay > min_delay) { + min_delay = delay; + } + } + if (!min_delay) { + min_delay = 1; + } + printf("minimum delay: %d\n", min_delay); + + frame_delay = 1000/fps; + + audio_mutex = SDL_CreateMutex(); + psg_cond = SDL_CreateCond(); + ym_cond = SDL_CreateCond(); + audio_ready = SDL_CreateCond(); + + SDL_AudioSpec desired, actual; 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.format = AUDIO_S16SYS; + desired.channels = 2; char * samples_str = tern_find_ptr(config, "audiobuffer"); int samples = samples_str ? atoi(samples_str) : 0; if (!samples) { @@ -158,95 +311,136 @@ } printf("config says: %d\n", samples); desired.samples = samples*2; - desired.callback = audio_callback; - desired.userdata = NULL; - - if (SDL_OpenAudio(&desired, &actual) < 0) { - fprintf(stderr, "Unable to open SDL audio: %s\n", SDL_GetError()); - exit(1); - } - buffer_samples = actual.samples; - sample_rate = actual.freq; - printf("Initialized audio at frequency %d with a %d sample buffer\n", actual.freq, actual.samples); - SDL_PauseAudio(0); - num_joysticks = SDL_NumJoysticks(); - if (num_joysticks > MAX_JOYSTICKS) { - num_joysticks = MAX_JOYSTICKS; - } - for (int i = 0; i < num_joysticks; i++) { - printf("Joystick %d: %s\n", i, SDL_JoystickName(i)); - SDL_Joystick * joy = joysticks[i] = SDL_JoystickOpen(i); - if (joy) { - printf("\tNum Axes: %d\n\tNum Buttons: %d\n\tNum Hats: %d\n", SDL_JoystickNumAxes(joy), SDL_JoystickNumButtons(joy), SDL_JoystickNumHats(joy)); - } - } - SDL_JoystickEventState(SDL_ENABLE); + desired.callback = audio_callback; + desired.userdata = NULL; + + if (SDL_OpenAudio(&desired, &actual) < 0) { + fprintf(stderr, "Unable to open SDL audio: %s\n", SDL_GetError()); + exit(1); + } + buffer_samples = actual.samples; + sample_rate = actual.freq; + printf("Initialized audio at frequency %d with a %d sample buffer\n", actual.freq, actual.samples); + SDL_PauseAudio(0); + num_joysticks = SDL_NumJoysticks(); + if (num_joysticks > MAX_JOYSTICKS) { + num_joysticks = MAX_JOYSTICKS; + } + for (int i = 0; i < num_joysticks; i++) { + printf("Joystick %d: %s\n", i, SDL_JoystickName(i)); + SDL_Joystick * joy = joysticks[i] = SDL_JoystickOpen(i); + if (joy) { + printf("\tNum Axes: %d\n\tNum Buttons: %d\n\tNum Hats: %d\n", SDL_JoystickNumAxes(joy), SDL_JoystickNumButtons(joy), SDL_JoystickNumHats(joy)); + } + } + SDL_JoystickEventState(SDL_ENABLE); } +#ifndef DISABLE_OPENGL +void render_context_gl(vdp_context * context) +{ + glBindTexture(GL_TEXTURE_2D, textures[context->framebuf == context->oddbuf ? 0 : 1]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 320, 240, GL_BGRA, GL_UNSIGNED_BYTE, context->framebuf);; + + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + glUseProgram(program); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, textures[0]); + glUniform1i(un_textures[0], 0); + + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, (context->regs[REG_MODE_4] & BIT_INTERLACE) ? textures[1] : textures[2]); + glUniform1i(un_textures[1], 1); + + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + glVertexAttribPointer(at_pos, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat[2]), (void *)0); + glEnableVertexAttribArray(at_pos); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); + glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_SHORT, (void *)0); + + glDisableVertexAttribArray(at_pos); + + SDL_GL_SwapBuffers(); + if (context->regs[REG_MODE_4] & BIT_INTERLACE) + { + context->framebuf = context->framebuf == context->oddbuf ? context->evenbuf : context->oddbuf; + } +} +#endif uint32_t blankbuf[320*240]; void render_context(vdp_context * context) { uint16_t *buf_16; - uint32_t *buf_32; + uint32_t *buf_32; uint8_t b,g,r; last_frame = SDL_GetTicks(); +#ifndef DISABLE_OPENGL + if (render_gl) + { + render_context_gl(context); + return; + } +#endif if (SDL_MUSTLOCK(screen)) { if (SDL_LockSurface(screen) < 0) { return; } - } - uint16_t repeat_x = screen->clip_rect.w / 320; - uint16_t repeat_y = screen->clip_rect.h / 240; - if (repeat_x > repeat_y) { - repeat_x = repeat_y; - } else { - repeat_y = repeat_x; - } - int othermask = repeat_y >> 1; - - if (screen->format->BytesPerPixel == 2) { - uint16_t *otherbuf = (context->regs[REG_MODE_4] & BIT_INTERLACE) ? context->evenbuf : (uint16_t *)blankbuf; - uint16_t * oddbuf = context->oddbuf; - buf_16 = (uint16_t *)screen->pixels; - for (int y = 0; y < 240; y++) { - for (int i = 0; i < repeat_y; i++,buf_16 += screen->pitch/2) { - uint16_t *line = buf_16; - uint16_t *src_line = (i & othermask ? otherbuf : oddbuf) + y * 320; - for (int x = 0; x < 320; x++) { - uint16_t color = *(src_line++); - for (int j = 0; j < repeat_x; j++) { - *(line++) = color; - } - } - } - } - } else { - uint32_t *otherbuf = (context->regs[REG_MODE_4] & BIT_INTERLACE) ? context->evenbuf : (uint32_t *)blankbuf; - uint32_t * oddbuf = context->oddbuf; - buf_32 = (uint32_t *)screen->pixels; - for (int y = 0; y < 240; y++) { - for (int i = 0; i < repeat_y; i++,buf_32 += screen->pitch/4) { - uint32_t *line = buf_32; - uint32_t *src_line = (i & othermask ? otherbuf : oddbuf) + y * 320; - for (int x = 0; x < 320; x++) { - uint32_t color = *(src_line++); - for (int j = 0; j < repeat_x; j++) { - *(line++) = color; - } - } - } - } - } - if ( SDL_MUSTLOCK(screen) ) { - SDL_UnlockSurface(screen); - } + } + uint16_t repeat_x = screen->clip_rect.w / 320; + uint16_t repeat_y = screen->clip_rect.h / 240; + if (repeat_x > repeat_y) { + repeat_x = repeat_y; + } else { + repeat_y = repeat_x; + } + int othermask = repeat_y >> 1; + + if (screen->format->BytesPerPixel == 2) { + uint16_t *otherbuf = (context->regs[REG_MODE_4] & BIT_INTERLACE) ? context->evenbuf : (uint16_t *)blankbuf; + uint16_t * oddbuf = context->oddbuf; + buf_16 = (uint16_t *)screen->pixels; + for (int y = 0; y < 240; y++) { + for (int i = 0; i < repeat_y; i++,buf_16 += screen->pitch/2) { + uint16_t *line = buf_16; + uint16_t *src_line = (i & othermask ? otherbuf : oddbuf) + y * 320; + for (int x = 0; x < 320; x++) { + uint16_t color = *(src_line++); + for (int j = 0; j < repeat_x; j++) { + *(line++) = color; + } + } + } + } + } else { + uint32_t *otherbuf = (context->regs[REG_MODE_4] & BIT_INTERLACE) ? context->evenbuf : (uint32_t *)blankbuf; + uint32_t * oddbuf = context->oddbuf; + buf_32 = (uint32_t *)screen->pixels; + for (int y = 0; y < 240; y++) { + for (int i = 0; i < repeat_y; i++,buf_32 += screen->pitch/4) { + uint32_t *line = buf_32; + uint32_t *src_line = (i & othermask ? otherbuf : oddbuf) + y * 320; + for (int x = 0; x < 320; x++) { + uint32_t color = *(src_line++); + for (int j = 0; j < repeat_x; j++) { + *(line++) = color; + } + } + } + } + } + if ( SDL_MUSTLOCK(screen) ) { + SDL_UnlockSurface(screen); + } //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; - } + if (context->regs[REG_MODE_4] & BIT_INTERLACE) + { + context->framebuf = context->framebuf == context->oddbuf ? context->evenbuf : context->oddbuf; + } } int render_joystick_num_buttons(int joystick) diff -r 8ac0eb05642c -r 27345a67225d shaders/default.f.glsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/shaders/default.f.glsl Tue Oct 29 00:03:11 2013 -0700 @@ -0,0 +1,14 @@ +#version 110 + +uniform sampler2D textures[2]; + +varying vec2 texcoord; + +void main() +{ + gl_FragColor = mix( + texture2D(textures[0], texcoord), + texture2D(textures[1], vec2(texcoord.x, texcoord.y - 1.0/512.0)), + sin((texcoord.y * 512.0 - 0.75) * 3.14159265359) / 2.0 + 0.5 + ); +} diff -r 8ac0eb05642c -r 27345a67225d shaders/default.v.glsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/shaders/default.v.glsl Tue Oct 29 00:03:11 2013 -0700 @@ -0,0 +1,10 @@ +#version 110 + +attribute vec2 pos; +varying vec2 texcoord; + +void main() +{ + gl_Position = vec4(pos, 0.0, 1.0); + texcoord = pos * vec2(320.0/1024.0, 240.0/-512.0) + vec2(320.0/1024.0, 240.0/512.0); +} diff -r 8ac0eb05642c -r 27345a67225d tern.c --- a/tern.c Mon Oct 07 10:02:08 2013 -0700 +++ b/tern.c Tue Oct 29 00:03:11 2013 -0700 @@ -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 "tern.h" @@ -101,13 +101,18 @@ return tern_insert(head, key, val); } -void * tern_find_ptr(tern_node * head, char * key) +void * tern_find_ptr_default(tern_node * head, char * key, void * def) { tern_val ret; if (tern_find(head, key, &ret)) { return ret.ptrval; } - return NULL; + return def; +} + +void * tern_find_ptr(tern_node * head, char * key) +{ + return tern_find_ptr_default(head, key, NULL); } tern_node * tern_insert_ptr(tern_node * head, char * key, void * value) diff -r 8ac0eb05642c -r 27345a67225d tern.h --- a/tern.h Mon Oct 07 10:02:08 2013 -0700 +++ b/tern.h Tue Oct 29 00:03:11 2013 -0700 @@ -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 TERN_H_ @@ -28,6 +28,7 @@ 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_default(tern_node * head, char * key, void * def); void * tern_find_ptr(tern_node * head, char * key); tern_node * tern_insert_ptr(tern_node * head, char * key, void * value); diff -r 8ac0eb05642c -r 27345a67225d util.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util.c Tue Oct 29 00:03:11 2013 -0700 @@ -0,0 +1,139 @@ +#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; +} + +static char * exe_str; + +void set_exe_str(char * str) +{ + exe_str = str; +} + +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; +} + +char * get_exe_dir() +{ + static char * exe_dir; + if (!exe_dir) { + char * linktext = readlink_alloc("/proc/self/exe"); + if (!linktext) { + goto fallback; + } + char * cur; + int linksize = strlen(linktext); + for(cur = linktext + linksize - 1; cur != linktext; cur--) + { + if (*cur == '/') { + *cur = 0; + break; + } + } + if (cur == linktext) { + free(linktext); +fallback: + if (!exe_str) { + fputs("/proc/self/exe is not available and set_exe_str was not called!", stderr); + } + int pathsize = strlen(exe_str); + for(cur = exe_str + pathsize - 1; cur != exe_str; cur--) + { + if (*cur == '/') { + exe_dir = malloc(cur-exe_str+1); + memcpy(exe_dir, exe_str, cur-exe_str); + exe_dir[cur-exe_str] = 0; + break; + } + } + } else { + exe_dir = linktext; + } + } + return exe_dir; +} diff -r 8ac0eb05642c -r 27345a67225d util.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util.h Tue Oct 29 00:03:11 2013 -0700 @@ -0,0 +1,25 @@ +#ifndef UTIL_H_ +#define UTIL_H_ + +#include + +//Utility functions + +//Allocates a new string containing the concatenation of first and second +char * alloc_concat(char * first, char * second); +//Allocates a new string containing the concatenation of the strings pointed to by parts +char * alloc_concat_m(int num_parts, char ** parts); +//Returns the size of a file using fseek and ftell +long file_size(FILE * f); +//Strips whitespace and non-printable characters from the beginning and end of a string +char * strip_ws(char * text); +//Inserts a null after the first word, returns a pointer to the second word +char * split_keyval(char * text); +//Should be called by main with the value of argv[0] for use by get_exe_dir +void set_exe_str(char * str); +//Returns the directory the executable is in +char * get_exe_dir(); +//Returns the contents of a symlink in a newly allocated string +char * readlink_alloc(char * path); + +#endif //UTIL_H_ diff -r 8ac0eb05642c -r 27345a67225d vdp.c --- a/vdp.c Mon Oct 07 10:02:08 2013 -0700 +++ b/vdp.c Tue Oct 29 00:03:11 2013 -0700 @@ -50,10 +50,13 @@ memset(context, 0, sizeof(*context)); context->vdpmem = malloc(VRAM_SIZE); memset(context->vdpmem, 0, VRAM_SIZE); - context->oddbuf = context->framebuf = malloc(FRAMEBUF_ENTRIES * (render_depth() / 8)); + /*context->oddbuf = context->framebuf = malloc(FRAMEBUF_ENTRIES * (render_depth() / 8)); memset(context->framebuf, 0, FRAMEBUF_ENTRIES * (render_depth() / 8)); context->evenbuf = malloc(FRAMEBUF_ENTRIES * (render_depth() / 8)); memset(context->evenbuf, 0, FRAMEBUF_ENTRIES * (render_depth() / 8)); + */ + render_alloc_surfaces(context); + context->framebuf = context->oddbuf; context->linebuf = malloc(LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2); memset(context->linebuf, 0, LINEBUF_SIZE + SCROLL_BUFFER_SIZE*2); context->tmp_buf_a = context->linebuf + LINEBUF_SIZE;