#include "zx_screen.h" #include "z80.h" #include "z80mem.h" #include "zx_ula.h" #include #include "zx_tape.h" #include "ui_window.h" #include "z80debug.h" #include "ui.h" namespace zxscreen { uint32_t palette[16] = { 0x000000, 0x000080, 0x800000, 0x800080, 0x008000, 0x008080, 0x808000, 0x808080, 0x000000, 0x0000FF, 0xFF0000, 0xFF00FF, 0x00FF00, 0x00FFFF, 0xFFFF00, 0xFFFFFF }; SDL_Window *win = nullptr; SDL_Renderer *ren = nullptr; SDL_Texture *tex = nullptr; SDL_Texture *uitex = nullptr; int mode = SCREEN_MODE_48K; uint32_t t_states_total = 69888; uint32_t t_states_per_scanline = 224; uint32_t vsync_lines = 16; uint8_t zoom = 1; bool fullscreen = false; bool full_refresh = true; int fullscreen_scale = 1; SDL_Rect dest_rect; uint32_t time=0; uint32_t t_screen = 0; uint8_t t_flash = 0; bool flash = false; int pixels_draw = 0; uint16_t pixel_base_addr = 0x4000; uint16_t color_base_addr = 0x5800; uint16_t *pixel_addr = nullptr; //[69888]; uint16_t *color_addr = nullptr; //[69888]; uint8_t zx_pixels[352*296]; uint8_t *ptr_pixel = zx_pixels; #define SCREEN_SYNC 0xFFFF #define SCREEN_BORDER 0xFFFE void create_tables() { uint32_t count = 0; if (pixel_addr) free(pixel_addr); if (color_addr) free(color_addr); pixel_addr = (uint16_t*)malloc(t_states_total*sizeof(uint16_t)); color_addr = (uint16_t*)malloc(t_states_total*sizeof(uint16_t)); uint16_t *ptr_pixel = pixel_addr; uint16_t *ptr_color = color_addr; // vsync for (int i=0; i>3)*32; uint16_t address = /*0x4000 |*/ (x&0x1f) | ((y&0x7)<<8) | ((y&0x38)<<2) | ((y&0xc0)<<5); for (int i=7;i>0;i-=2) { *(ptr_pixel++) = (address & 0x1FFF) | (i << 13); *(ptr_color++) = color; count+=2; } } // Right border for (int j=0;j<24;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_BORDER; count+=2; } } // Lower border for (int i=0; i<56;++i) { // hsync for (int j=0;jtype==SDL_WINDOWEVENT) { if (e->window.event==SDL_WINDOWEVENT_CLOSE) { return false; } else if ((e->window.event==SDL_WINDOWEVENT_SHOWN) || (e->window.event==SDL_WINDOWEVENT_EXPOSED)) { redraw(); } } if (!z80debug::debugging()) { if (z80debug::paused()) { if (e->type == SDL_KEYDOWN) { if (e->key.keysym.scancode==SDL_SCANCODE_ESCAPE) { const uint8_t dt = z80::step(); z80debug::cont(); zxscreen::refresh(dt); } } } else { if (e->type == SDL_KEYDOWN) { if (e->key.keysym.scancode==SDL_SCANCODE_ESCAPE) { z80debug::pause(); zxscreen::redraw(); } else if (e->key.keysym.scancode==SDL_SCANCODE_F1) { zxscreen::decZoom(); } else if (e->key.keysym.scancode==SDL_SCANCODE_F2) { zxscreen::incZoom(); } else if (e->key.keysym.scancode==SDL_SCANCODE_F3) { zxscreen::toggleFullscreen(); } else if (e->key.keysym.scancode==SDL_SCANCODE_F6) { zx_tape::play(); } else if (e->key.keysym.scancode==SDL_SCANCODE_F7) { zx_tape::rewind(); } } } } if (e->type == SDL_MOUSEMOTION) { SDL_ShowCursor(true); } return true; } void reinit() { if (win) ui::window::unregisterWindow(SDL_GetWindowID(win)); if (tex) SDL_DestroyTexture(tex); if (uitex) SDL_DestroyTexture(uitex); if (ren) SDL_DestroyRenderer(ren); if (win) SDL_DestroyWindow(win); const int z = fullscreen ? 1 : zoom; win = SDL_CreateWindow("ZX Spectrum Screen", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 352*z, 296*z, fullscreen?SDL_WINDOW_FULLSCREEN_DESKTOP:SDL_WINDOW_SHOWN); ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED); tex = SDL_CreateTexture(ren, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, 352, 296); uitex = ui::createtexture(ren); ui::window::registerWindow(SDL_GetWindowID(win), eventHandler); if (fullscreen) { int w, h; SDL_GetWindowSize(win, &w, &h); fullscreen_scale = h/296; dest_rect.w = 352 * fullscreen_scale; dest_rect.h = 296 * fullscreen_scale; dest_rect.x = (w - dest_rect.w)/2; dest_rect.y = (h - dest_rect.h)/2; } else { dest_rect.x = dest_rect.y = 0; dest_rect.w = 352 * zoom; dest_rect.h = 296 * zoom; } focus(); } void setBaseAddresses(const uint32_t pixeladdr, const uint32_t coloraddr) { pixel_base_addr = pixeladdr; color_base_addr = coloraddr; } void init(int mode) { zxscreen::mode = mode; if (mode==SCREEN_MODE_48K) { setBaseAddresses(0x4000, 0x5800); t_states_total = 69888; t_states_per_scanline = 224; vsync_lines = 16; } else if (mode==SCREEN_MODE_128K) { setBaseAddresses(0x14000, 0x15800); t_states_total = 70908; t_states_per_scanline = 228; vsync_lines = 15; } create_tables(); reinit(); } void focus() { if (win) { SDL_RaiseWindow(win); redraw(); } } void refresh(const uint32_t dt, const bool full) { const uint8_t* pixel_mem = z80mem::get()->rawPtr(pixel_base_addr); const uint8_t* color_mem = z80mem::get()->rawPtr(color_base_addr); const uint8_t border_color = zx_ula::get_border_color(); for (int i=0;ireadMem(color_base_addr + color_addr[t_screen]); uint8_t c1 = color&0x7, c2 = (color>>3)&0x7; if ((color&0x80) && flash) { c1=c2; c2=color&0x7; } if ((color&0x40)) { c1 |= 0x8; c2 |= 0x8; } uint16_t address = /*(0x4000) |*/ (pixel_addr[t_screen]&0x1FFF); uint8_t mask = 1 << (pixel_addr[t_screen]>>13); uint8_t block = *(pixel_mem + address); // z80mem::get()->readMem(pixel_base_addr + address); *(ptr_pixel++)=(block&mask) ? c1 : c2; mask>>=1; *(ptr_pixel++)=(block&mask) ? c1 : c2; } pixels_draw+=2; } t_screen++; if (t_screen>=t_states_total) { pixels_draw=0; t_flash++; if (t_flash==16) { t_flash=0; flash = !flash; } t_screen=0; ptr_pixel = zx_pixels; redraw(); if (!full) z80::interrupt(); } } } void fullrefresh() { uint32_t tmp = t_screen; t_screen = 0; uint8_t * tmp_ptr = ptr_pixel; ptr_pixel = zx_pixels; refresh(t_states_total, true); ptr_pixel = tmp_ptr; t_screen = tmp; } void debugrefresh(const uint32_t dt) { if (full_refresh) fullrefresh(); else refresh(dt); } void redraw(const bool present) { if (zx_tape::getplaying() && zx_tape::getOption(ZXTAPE_OPTION_FAST_LOAD)) return; ui::setrenderer(ren, uitex); Uint32* pixels; int pitch; SDL_LockTexture(tex, NULL, (void**)&pixels, &pitch); for (int i=0; i<352*296;++i) *(pixels++) = palette[zx_pixels[i]]; SDL_UnlockTexture(tex); if (fullscreen) { SDL_SetRenderDrawColor(ren, 0, 0, 0, 255); SDL_RenderClear(ren); } // Pintem la textura a pantalla SDL_RenderCopy(ren, tex, NULL, &dest_rect); if (present) SDL_RenderPresent(ren); else { SDL_SetRenderDrawBlendMode(ren, SDL_BLENDMODE_BLEND); SDL_SetRenderDrawColor(ren, 0, 0, 0, 128); SDL_Rect rect {0,0,352*zoom,296*zoom}; if (fullscreen) SDL_GetWindowSize(win, &rect.w, &rect.h); SDL_RenderFillRect(ren, &rect); } } void present() { SDL_RenderPresent(ren); } void setTitle(const char* title) { char tmp[256]; strcpy(tmp, "ZX Spectrum Screen"); strcat(tmp, title); SDL_SetWindowTitle(win, tmp); } void setZoom(const int value) { if (value < 1) return; SDL_DisplayMode dm; SDL_GetCurrentDisplayMode(0, &dm); if (352*value > dm.w) return; if (296*value > dm.h) return; zoom = value; reinit(); } void incZoom() { setZoom(zoom+1); } void decZoom() { setZoom(zoom-1); } void toggleFullscreen() { fullscreen = !fullscreen; reinit(); } const bool getFullscreen() { return fullscreen; } void toggleFullRefresh() { full_refresh = !full_refresh; } const bool getFullRefresh() { return full_refresh; } SDL_Renderer *getrenderer() { return ren; } }