- [FIX] Fixed flag visualization on debugger - [NEW] Comencem a treballar en el mòdul gbscreen
375 lines
11 KiB
C++
375 lines
11 KiB
C++
#include "gbscreen.h"
|
|
#include "sm83.h"
|
|
#include "mem.h"
|
|
//#include "zx_ula.h"
|
|
#include <SDL2/SDL.h>
|
|
#include "ui_window.h"
|
|
#include "debug.h"
|
|
#include "ui.h"
|
|
|
|
namespace zxscreen
|
|
{
|
|
uint32_t palette[4] = {
|
|
0xFFFFFF, 0xAAAAAA, 0x555555, 0x000000
|
|
};
|
|
SDL_Window *win = nullptr;
|
|
SDL_Renderer *ren = nullptr;
|
|
SDL_Texture *tex = nullptr;
|
|
SDL_Texture *uitex = nullptr;
|
|
|
|
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<t_states_per_scanline*vsync_lines;++i) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_SYNC; } // En el 128K 16 passa a ser 15 i 224 passa a ser 228
|
|
|
|
// Upper border
|
|
for (int i=0; i<48;++i) {
|
|
// hsync
|
|
for (int j=0;j<t_states_per_scanline-176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_SYNC; } // En el 128K 48 passsa a ser 52
|
|
//border
|
|
for (int j=0;j<176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_BORDER; count+=2; }
|
|
}
|
|
|
|
// scanlines
|
|
for (uint8_t y=0; y<192; ++y)
|
|
{
|
|
// hsync
|
|
for (int j=0;j<t_states_per_scanline-176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_SYNC; } // En el 128K 48 passsa a ser 52
|
|
|
|
// Left border
|
|
for (int j=0;j<24;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_BORDER; count+=2; }
|
|
|
|
// Actual screen
|
|
for (uint8_t x=0;x<32;++x)
|
|
{
|
|
uint16_t color = /*0x5800 +*/ x + (y>>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;j<t_states_per_scanline-176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_SYNC; } // En el 128K 48 passsa a ser 52
|
|
//border
|
|
for (int j=0;j<176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = SCREEN_BORDER; count+=2; }
|
|
}
|
|
//printf("COUNT: %i\n", count);
|
|
}
|
|
|
|
bool eventHandler(SDL_Event *e)
|
|
{
|
|
if (e->type==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;i<dt;++i)
|
|
{
|
|
if (color_addr[t_screen] != SCREEN_SYNC)
|
|
{
|
|
if (color_addr[t_screen] == SCREEN_BORDER) {
|
|
*(ptr_pixel++) = border_color;
|
|
*(ptr_pixel++) = border_color;
|
|
} else {
|
|
uint8_t color = *(color_mem + color_addr[t_screen]); // z80mem::get()->readMem(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;
|
|
}
|
|
}
|