Files
z80/zx_screen.cpp

258 lines
7.1 KiB
C++

#include "zx_screen.h"
#include "z80.h"
#include "zx_ula.h"
#include <SDL2/SDL.h>
#include "zx_tape.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;
uint8_t zoom = 1;
bool fullscreen = false;
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_addr[69888];
uint16_t color_addr[69888];
uint8_t zx_pixels[352*296];
uint8_t *ptr_pixel = zx_pixels;
void create_tables()
{
uint32_t count = 0;
uint16_t *ptr_pixel = pixel_addr;
uint16_t *ptr_color = color_addr;
// vsync
for (int i=0; i<224*16;++i) { *(ptr_pixel++) = 0; *(ptr_color++) = 0; }
// Upper border
for (int i=0; i<48;++i) {
// hsync
for (int j=0;j<48;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 0; }
//border
for (int j=0;j<176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 1; count+=2; }
}
// scanlines
for (uint8_t y=0; y<192; ++y)
{
// hsync
for (int j=0;j<48;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 0; }
// Left border
for (int j=0;j<24;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 1; 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++) = 1; count+=2; }
}
// Lower border
for (int i=0; i<56;++i) {
// hsync
for (int j=0;j<48;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 0; }
//border
for (int j=0;j<176;++j) { *(ptr_pixel++) = 0; *(ptr_color++) = 1; count+=2; }
}
//printf("COUNT: %i\n", count);
}
void reinit()
{
if (tex) SDL_DestroyTexture(tex);
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);
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;
}
redraw();
}
void init()
{
create_tables();
reinit();
}
void refresh(const uint32_t dt)
{
const uint8_t* memory = z80::getMem();
const uint8_t border_color = zx_ula::get_border_color();
for (int i=0;i<dt;++i)
{
if (color_addr[t_screen] != 0)
{
if (color_addr[t_screen] == 1)
{
*(ptr_pixel++) = border_color;
*(ptr_pixel++) = border_color;
//pixels_draw+=2;
}
else
{
uint8_t color = memory[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 = memory[address];
*(ptr_pixel++)=(block&mask) ? c1 : c2;
mask>>=1;
*(ptr_pixel++)=(block&mask) ? c1 : c2;
}
pixels_draw+=2;
}
t_screen++;
/*if (pixels_draw>352*296)
{
printf("PIXELS OVERFLOW: %i\n", pixels_draw);
}*/
if (t_screen>=69888) {
//printf("PIXELS DRAWN: %i\n", pixels_draw);
pixels_draw=0;
t_flash++;
if (t_flash==16) { t_flash=0; flash = !flash; }
t_screen=0;
ptr_pixel = zx_pixels;
redraw();
//while (SDL_GetTicks()-time < 20) {}
//time = SDL_GetTicks();
z80::interrupt();
}
}
}
void fullrefresh()
{
t_screen = 0;
refresh(69888);
}
void redraw(const bool present)
{
if (zx_tape::getplaying() && zx_tape::getberserk()) return;
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 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;
}
SDL_Renderer *getrenderer()
{
return ren;
}
}