diff --git a/Makefile b/Makefile index 3b5665b..36c89a4 100644 --- a/Makefile +++ b/Makefile @@ -260,8 +260,8 @@ endif # SDL support #### ifeq ($(ENABLE_SDL),yes) - LIBLNK += $(shell sdl-config --libs) - CFLAGS += $(shell sdl-config --cflags) + LIBLNK += $(shell sdl2-config --libs) + CFLAGS += $(shell sdl2-config --cflags) endif diff --git a/src/keyboard.c b/src/keyboard.c index 5813d38..a80d9da 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -10,24 +10,24 @@ * Key map -- a mapping from SDLK_xxx constants to scancodes and vice versa. */ struct { - SDLKey key; ///< SDLK_xxx key code constant + SDL_Keycode key; ///< SDLK_xxx key code constant int extended; ///< 1 if this is an extended keycode unsigned char scancode; ///< Keyboard scan code } keymap[] = { { SDLK_UP, 0, 0x01 }, // ROLL/Up [UpArrow] - { SDLK_KP2, 0, 0x01 }, // ROLL/Up [Keypad 2] + { SDLK_KP_2, 0, 0x01 }, // ROLL/Up [Keypad 2] // { SDLK_, 1, 0x02 }, // Clear Line // { SDLK_, 1, 0x03 }, // Rstrt / Ref { SDLK_ESCAPE, 1, 0x04 }, // Exit - { SDLK_KP1, 0, 0x05 }, // PREV [Keypad 1] + { SDLK_KP_1, 0, 0x05 }, // PREV [Keypad 1] // { SDLK_, 1, 0x06 }, // Msg { SDLK_BACKSPACE, 1, 0x07 }, // Cancl { SDLK_BACKSPACE, 0, 0x08 }, // Backspace { SDLK_TAB, 0, 0x09 }, // Tab { SDLK_RETURN, 1, 0x0a }, // ENTER { SDLK_DOWN, 0, 0x0b }, // ROLL/Down [DownArrow] - { SDLK_KP0, 0, 0x0b }, // ROLL/Down [Keypad 0] - { SDLK_KP3, 0, 0x0c }, // NEXT [Keypad 3] + { SDLK_KP_0, 0, 0x0b }, // ROLL/Down [Keypad 0] + { SDLK_KP_3, 0, 0x0c }, // NEXT [Keypad 3] { SDLK_RETURN, 0, 0x0d }, // RETURN [Return] { SDLK_LEFT, 0, 0x0e }, // <-- [LeftArrow] { SDLK_KP_MINUS, 0, 0x0e }, // <-- [Keypad -] @@ -54,7 +54,7 @@ struct { // { SDLK_, 1, 0x22 }, // Redo // { SDLK_, 1, 0x23 }, // FIND // { SDLK_, 1, 0x24 }, // RPLAC - { SDLK_BREAK, 0, 0x25 }, // RESET/BREAK [Pause/Break] + { SDLK_PAUSE, 0, 0x25 }, // RESET/BREAK [Pause/Break] // { SDLK_, 1, 0x26 }, // DleteChar { SDLK_QUOTE, 0, 0x27 }, // ' (single-quote) // { SDLK_, 1, 0x28 }, // SLCT/MARK @@ -82,17 +82,17 @@ struct { // Keycodes 3E, 3F, 40 not used // { SDLK_, 1, 0x41 }, // CMD // { SDLK_, 1, 0x42 }, // CLOSE/OPEN - { SDLK_KP7, 0, 0x43 }, // PRINT - { SDLK_KP8, 0, 0x44 }, // CLEAR/RFRSH + { SDLK_KP_7, 0, 0x43 }, // PRINT + { SDLK_KP_8, 0, 0x44 }, // CLEAR/RFRSH { SDLK_CAPSLOCK, 0, 0x45 }, // Caps Lock - { SDLK_KP9, 0, 0x46 }, // PAGE - { SDLK_KP4, 0, 0x47 }, // BEG + { SDLK_KP_9, 0, 0x46 }, // PAGE + { SDLK_KP_4, 0, 0x47 }, // BEG { SDLK_LSHIFT, 0, 0x48 }, // Left Shift { SDLK_RSHIFT, 0, 0x49 }, // Right Shift { SDLK_HOME, 0, 0x4a }, // Home - { SDLK_KP5, 0, 0x4a }, // Home [Keypad 5] + { SDLK_KP_5, 0, 0x4a }, // Home [Keypad 5] { SDLK_END, 0, 0x4b }, // End - { SDLK_KP6, 0, 0x4b }, // End [Keypad 6] + { SDLK_KP_6, 0, 0x4b }, // End [Keypad 6] { SDLK_LCTRL, 0, 0x4c }, // Left Ctrl? \___ not sure which is left and which is right... { SDLK_RCTRL, 0, 0x4d }, // Right Ctrl? / // Keycodes 4E thru 5A not used @@ -128,7 +128,7 @@ struct { { SDLK_y, 0, 0x79 }, // Y { SDLK_z, 0, 0x7a }, // Z // Keycodes 7B, 7C, 7D not used - { SDLK_NUMLOCK, 0, 0x7e }, // Numlock + { SDLK_NUMLOCKCLEAR, 0, 0x7e }, // Numlock { SDLK_DELETE, 0, 0x7f } // Dlete }; diff --git a/src/lightbar.c b/src/lightbar.c new file mode 100644 index 0000000..641c93b --- /dev/null +++ b/src/lightbar.c @@ -0,0 +1,58 @@ +/* GIMP RGBA C-Source image dump (lightbar.c) */ + +static const struct { + unsigned int width; + unsigned int height; + unsigned int bytes_per_pixel; /* 2:RGB16, 3:RGB, 4:RGBA */ + unsigned char pixel_data[32 * 8 * 4 + 1]; +} lightbar = { + 32, 8, 4, + "\000\000\000\000\000\000\000\000\310\305\304\377\366\313\307\377\366\313\307\377\310\305" + "\304\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\304\306\304\377\307\332\307\377" + "\307\332\307\377\304\306\304\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\307\306" + "\304\377\352\332\303\377\352\332\303\377\307\306\304\377\000\000\000\000\000\000\000\000" + "\000\000\000\000\000\000\000\000\262\262\262\377\326\326\326\377\326\326\326\377\262\262" + "\262\377\000\000\000\000\000\000\000\000\000\000\000\000\351\312\307\377\371\216p\377\377\243\201" + "\377\377\243\201\377\371\216p\377\351\312\307\377\000\000\000\000\000\000\000\000\307\326" + "\307\377q\317q\377\203\351\203\377\203\351\203\377q\317q\377\307\326\307" + "\377\000\000\000\000\000\000\000\000\341\327\305\377\354\331\223\377\375\371\313\377\375" + "\371\313\377\354\331\223\377\341\327\305\377\000\000\000\000\000\000\000\000\323\323\323" + "\377\277\277\277\377\330\330\330\377\330\330\330\377\277\277\277\377\323" + "\323\323\377\000\000\000\000\307\305\304\377\366\203f\377\377\233y\377\377\233y\377" + "\377\233y\377\377\233y\377\366\203f\377\307\305\304\377\304\306\304\377j" + "\307j\377\177\345\177\377\177\345\177\377\177\345\177\377\177\345\177\377" + "j\307j\377\304\306\304\377\306\306\304\377\351\321~\377\377\366\265\377\377" + "\366\265\377\377\366\265\377\377\366\265\377\351\321~\377\306\306\304\377" + "\262\262\262\377\267\267\267\377\324\324\324\377\324\324\324\377\324\324" + "\324\377\324\324\324\377\267\267\267\377\262\262\262\377\356\307\305\377" + "\371tS\377\374~\\\377\376\206d\377\376\206d\377\374~\\\377\371tS\377\356" + "\307\305\377\305\324\305\377_\304_\377i\316i\377q\327q\377q\327q\377i\316" + "i\377_\304_\377\305\324\305\377\342\324\303\377\362\324e\377\370\337v\377" + "\376\350\203\377\376\350\203\377\370\337v\377\362\324e\377\342\324\303\377" + "\321\321\321\377\270\270\270\377\300\300\300\377\307\307\307\377\307\307" + "\307\377\300\300\300\377\270\270\270\377\321\321\321\377\353\306\304\377" + "\365Y\067\377\360H&\377\360H&\377\360H&\377\360H&\377\365Y\067\377\353\306" + "\304\377\305\322\305\377J\260J\377\067\235\067\377\067\235\067\377\067\235\067" + "\377\067\235\067\377J\260J\377\305\322\305\377\340\322\303\377\352\300(\377" + "\340\256\006\377\340\256\006\377\340\256\006\377\340\256\006\377\352\300(\377\340" + "\322\303\377\317\317\317\377\247\247\247\377\234\234\234\377\234\234\234" + "\377\234\234\234\377\234\234\234\377\247\247\247\377\317\317\317\377\307" + "\304\304\377\351W>\377\370V\064\377\367T\062\377\367T\062\377\370V\064\377\351" + "W>\377\307\304\304\377\304\305\304\377N\247N\377L\262L\377I\257I\377I\257" + "I\377L\262L\377N\247N\377\304\305\304\377\306\305\304\377\333\261\062\377" + "\361\303\025\377\356\300\023\377\356\300\023\377\361\303\025\377\333\261\062\377" + "\306\305\304\377\262\262\262\377\231\231\231\377\250\250\250\377\246\246" + "\246\377\246\246\246\377\250\250\250\377\231\231\231\377\262\262\262\377" + "\000\000\000\000\340\305\304\377\350P\071\377\373T\062\377\373T\062\377\350P\071\377" + "\340\305\304\377\000\000\000\000\000\000\000\000\305\317\305\377K\243K\377Q\266Q\377Q\266" + "Q\377K\243K\377\305\317\305\377\000\000\000\000\000\000\000\000\331\317\304\377\333\255," + "\377\370\306\006\377\370\306\006\377\333\255,\377\331\317\304\377\000\000\000\000\000\000" + "\000\000\314\314\314\377\224\224\224\377\246\246\246\377\246\246\246\377\224" + "\224\224\377\314\314\314\377\000\000\000\000\000\000\000\000\000\000\000\000\306\304\304\377\344" + "\304\303\377\344\304\303\377\306\304\304\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000" + "\000\000\304\305\304\377\304\316\304\377\304\316\304\377\304\305\304\377\000\000" + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\306\305\304\377\332\316\303\377\332\316\303" + "\377\306\305\304\377\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\262\262\262\377\313" + "\313\313\377\313\313\313\377\262\262\262\377\000\000\000\000\000\000\000\000", +}; + diff --git a/src/main.c b/src/main.c index f2bb108..6d853cd 100644 --- a/src/main.c +++ b/src/main.c @@ -11,6 +11,8 @@ #include "state.h" #include "memory.h" +#include "lightbar.c" + extern int cpu_log_enabled; void FAIL(char *err) @@ -105,8 +107,10 @@ void putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel) /** * @brief Refresh the screen. * @param surface SDL surface upon which to draw. + * @param renderer SDL renderer. + * @param texture SDL texture to copy surface to. */ -void refreshScreen(SDL_Surface *s) +void refreshScreen(SDL_Surface *s, SDL_Renderer *r, SDL_Texture *t) { // Lock the screen surface (if necessary) if (SDL_MUSTLOCK(s)) { @@ -140,22 +144,41 @@ void refreshScreen(SDL_Surface *s) } } - // TODO: blit LEDs and status info - // Unlock the screen surface if (SDL_MUSTLOCK(s)) { SDL_UnlockSurface(s); } - // Trigger a refresh -- TODO: partial refresh depending on whether we - // refreshed the screen area, status area, both, or none. Use SDL_UpdateRect() for this. - SDL_Flip(s); + // Update framebuffer texture + SDL_UpdateTexture(t, NULL, s->pixels, s->pitch); + SDL_RenderCopy(r, t, NULL, NULL); +} + +#define LED_SIZE 8 + +void refreshStatusBar(SDL_Renderer *r, SDL_Texture *lightbar_tex) +{ + SDL_Rect red_led = { 0, 0, LED_SIZE, LED_SIZE }; + SDL_Rect green_led = { LED_SIZE, 0, LED_SIZE, LED_SIZE }; + SDL_Rect yellow_led = { LED_SIZE*2, 0, LED_SIZE, LED_SIZE }; + SDL_Rect inactive_led = { LED_SIZE*3, 0, LED_SIZE, LED_SIZE }; + SDL_Rect dstrect = { 720-LED_SIZE*4, 348-LED_SIZE, LED_SIZE, LED_SIZE }; + + // LED bit values are inverse of documentation (leftmost LED is LSB) + // Red user LED (leftmost LED) can be turned on using "syslocal(SYSL_LED, 1)" from sys/syslocal.h + SDL_RenderCopy(r, lightbar_tex, (state.leds & 1) ? &red_led : &inactive_led, &dstrect); + dstrect.x += LED_SIZE; + SDL_RenderCopy(r, lightbar_tex, (state.leds & 2) ? &green_led : &inactive_led, &dstrect); + dstrect.x += LED_SIZE; + SDL_RenderCopy(r, lightbar_tex, (state.leds & 4) ? &yellow_led : &inactive_led, &dstrect); + dstrect.x += LED_SIZE; + SDL_RenderCopy(r, lightbar_tex, (state.leds & 8) ? &red_led : &inactive_led, &dstrect); } /** * @brief Handle events posted by SDL. */ -bool HandleSDLEvents(SDL_Surface *screen) +bool HandleSDLEvents(SDL_Window *window) { SDL_Event event; static int mouse_grabbed = 0, mouse_buttons = 0; @@ -175,12 +198,10 @@ bool HandleSDLEvents(SDL_Surface *screen) switch (event.key.keysym.sym) { case SDLK_F10: if (mouse_grabbed){ - SDL_ShowCursor(1); - SDL_WM_GrabInput(SDL_GRAB_OFF); + SDL_SetRelativeMouseMode(SDL_FALSE); mouse_grabbed = 0; }else{ - SDL_ShowCursor(0); - SDL_WM_GrabInput(SDL_GRAB_ON); + SDL_SetRelativeMouseMode(SDL_TRUE); mouse_grabbed = 1; } break; @@ -274,13 +295,43 @@ int main(int argc, char *argv[]) atexit(SDL_Quit); // Set up the video display - SDL_Surface *screen = NULL; - if ((screen = SDL_SetVideoMode(720, 348, 8, SDL_SWSURFACE | SDL_ANYFORMAT)) == NULL) { - fprintf(stderr, "Could not find a suitable video mode: %s.\n", SDL_GetError()); + SDL_Window *window; + if ((window = SDL_CreateWindow("FreeBee 3B1 Emulator", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, + 720, 348, 0)) == NULL) { + fprintf(stderr, "Error creating SDL window: %s.\n", SDL_GetError()); + exit(EXIT_FAILURE); + } + SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, 0); + if (!renderer){ + fprintf(stderr, "Error creating SDL renderer: %s.\n", SDL_GetError()); exit(EXIT_FAILURE); } + SDL_Texture *fbTexture = SDL_CreateTexture(renderer, + SDL_PIXELFORMAT_RGB888, + SDL_TEXTUREACCESS_STREAMING, + 720, 348); + if (!fbTexture){ + fprintf(stderr, "Error creating SDL FB texture: %s.\n", SDL_GetError()); + exit(EXIT_FAILURE); + } + SDL_Surface *screen = SDL_CreateRGBSurface(0, 720, 348, 32, 0, 0, 0, 0); + if (!screen){ + fprintf(stderr, "Error creating SDL FB surface: %s.\n", SDL_GetError()); + exit(EXIT_FAILURE); + } + // Load in status LED sprites + SDL_Surface *surf = SDL_CreateRGBSurfaceFrom((void*)lightbar.pixel_data, lightbar.width, lightbar.height, + lightbar.bytes_per_pixel*8, lightbar.bytes_per_pixel*lightbar.width, +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF +#else + 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000 +#endif + ); + SDL_Texture *lightbarTexture = SDL_CreateTextureFromSurface(renderer, surf); + SDL_FreeSurface(surf); + printf("Set %dx%d at %d bits-per-pixel mode\n\n", screen->w, screen->h, screen->format->BitsPerPixel); - SDL_WM_SetCaption("FreeBee 3B1 emulator", "FreeBee"); // Load a disc image load_fd(); @@ -299,6 +350,7 @@ int main(int argc, char *argv[]) uint32_t next_timeslot = SDL_GetTicks() + MILLISECS_PER_TIMESLOT; uint32_t clock_cycles = 0, tmp; bool exitEmu = false; + uint8_t last_leds = 255; /*bool lastirq_fdc = false;*/ for (;;) { @@ -418,8 +470,17 @@ int main(int argc, char *argv[]) } // Is it time to run the 60Hz periodic interrupt yet? if (clock_cycles > CLOCKS_PER_60HZ) { - // Refresh the screen - refreshScreen(screen); + // Refresh the screen if VRAM has been changed + if (state.vram_updated){ + refreshScreen(screen, renderer, fbTexture); + } + if (state.vram_updated || last_leds != state.leds){ + refreshStatusBar(renderer, lightbarTexture); + last_leds = state.leds; + } + state.vram_updated = false; + SDL_RenderPresent(renderer); + if (state.timer_enabled){ m68k_set_irq(6); state.timer_asserted = true; @@ -431,7 +492,7 @@ int main(int argc, char *argv[]) } // handle SDL events -- returns true if we need to exit - if (HandleSDLEvents(screen)) + if (HandleSDLEvents(window)) exitEmu = true; // make sure frame rate is equal to real time @@ -459,5 +520,12 @@ int main(int argc, char *argv[]) fclose(state.fdc_disc); } + // Clean up SDL + SDL_DestroyTexture(lightbarTexture); + SDL_FreeSurface(screen); + SDL_DestroyTexture(fbTexture); + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + return 0; } diff --git a/src/memory.c b/src/memory.c index 3e62dca..da2dfc3 100644 --- a/src/memory.c +++ b/src/memory.c @@ -1004,6 +1004,7 @@ void m68k_write_memory_32(uint32_t address, uint32_t value)/*{{{*/ case 0x020000: // Video RAM if (address > 0x427FFF) fprintf(stderr, "NOTE: WR32 to VideoRAM mirror, addr=0x%08X\n", address); WR32(state.vram, address, 0x7FFF, value); + state.vram_updated = true; break; default: IoWrite(address, value, 32); @@ -1052,6 +1053,7 @@ void m68k_write_memory_16(uint32_t address, uint32_t value)/*{{{*/ case 0x020000: // Video RAM if (address > 0x427FFF) fprintf(stderr, "NOTE: WR16 to VideoRAM mirror, addr=0x%08X, data=0x%04X\n", address, value); WR16(state.vram, address, 0x7FFF, value); + state.vram_updated = true; break; default: IoWrite(address, value, 16); @@ -1099,6 +1101,7 @@ void m68k_write_memory_8(uint32_t address, uint32_t value)/*{{{*/ case 0x020000: // Video RAM if (address > 0x427FFF) fprintf(stderr, "NOTE: WR8 to VideoRAM mirror, addr=0x%08X, data=0x%04X\n", address, value); WR8(state.vram, address, 0x7FFF, value); + state.vram_updated = true; break; default: IoWrite(address, value, 8); diff --git a/src/state.h b/src/state.h index f5b195d..74eb75e 100644 --- a/src/state.h +++ b/src/state.h @@ -104,6 +104,9 @@ typedef struct { /// VIDPAL mod (allows user writing to VRAM) bool vidpal; + + /// Update screen only when VRAM has been changed + bool vram_updated; } S_state; // Global emulator state. Yes, I know global variables are evil, please don't diff --git a/src/wd2010.c b/src/wd2010.c index 39398c6..fa681a1 100644 --- a/src/wd2010.c +++ b/src/wd2010.c @@ -320,7 +320,7 @@ void wd2010_write_reg(WD2010_CTX *ctx, uint8_t addr, uint8_t val) case CMD_RESTORE: // Restore. Set track to 0 and throw an IRQ. ctx->track = 0; - SDL_AddTimer(WD2010_SEEK_DELAY, (SDL_NewTimerCallback)seek_complete, ctx); + SDL_AddTimer(WD2010_SEEK_DELAY, (SDL_TimerCallback)seek_complete, ctx); break; case CMD_SCAN_ID: ctx->cylinder_high_reg = (ctx->track >> 8) & CYLH_MASK; @@ -351,7 +351,7 @@ void wd2010_write_reg(WD2010_CTX *ctx, uint8_t addr, uint8_t val) ctx->formatting = cmd == CMD_WRITE_FORMAT; switch (cmd){ case CMD_SEEK: - SDL_AddTimer(WD2010_SEEK_DELAY, (SDL_NewTimerCallback)seek_complete, ctx); + SDL_AddTimer(WD2010_SEEK_DELAY, (SDL_TimerCallback)seek_complete, ctx); break; case CMD_READ_SECTOR: /*XXX: does a separate function to set the head have to be added?*/ @@ -401,7 +401,7 @@ void wd2010_write_reg(WD2010_CTX *ctx, uint8_t addr, uint8_t val) ctx->status = 0; ctx->status |= (ctx->data_pos < ctx->data_len) ? SR_DRQ | SR_COMMAND_IN_PROGRESS | SR_BUSY : 0x00; - /*SDL_AddTimer(WD2010_SEEK_DELAY, (SDL_NewTimerCallback)transfer_seek_complete, ctx);*/ + /*SDL_AddTimer(WD2010_SEEK_DELAY, (SDL_TimerCallback)transfer_seek_complete, ctx);*/ ctx->drq = true; break; @@ -444,7 +444,7 @@ void wd2010_write_reg(WD2010_CTX *ctx, uint8_t addr, uint8_t val) ctx->status = 0; ctx->status |= (ctx->data_pos < ctx->data_len) ? SR_DRQ | SR_COMMAND_IN_PROGRESS | SR_BUSY : 0x00; - /*SDL_AddTimer(WD2010_SEEK_DELAY, (SDL_NewTimerCallback)transfer_seek_complete, ctx);*/ + /*SDL_AddTimer(WD2010_SEEK_DELAY, (SDL_TimerCallback)transfer_seek_complete, ctx);*/ ctx->drq = true; break;