bk-emulator/scr.c
2019-11-04 14:16:42 +01:00

499 lines
13 KiB
C

#include "defines.h"
#include "SDL/SDL.h"
#include "scr.h"
#include <libintl.h>
#define _(String) gettext (String)
SDL_Surface * screen, *rl_screen;
flag_t fullscreen = 0;
int cur_shift = 0;
int cur_width = 0; /* 0 = narrow, !0 = wide */
int horsize = 512, vertsize = 512;
/* Scan lines */
// The scan lines must be in 8-bit palettized form, as they have
// to be re-blitted to the screen with their new meaning if the
// palette changes.
unsigned char dirty[1024];
// There are 256 scan lines in each of 2 buffers
SDL_Surface * lines[512];
/*
* The colors are ordered in the HW order, not in the "Basic" order.
* To convert, switch colors 1 and 3.
*/
SDL_Color palettes[16][5] = {
{ Black, Blue, Green, Red, White }, /* BK0010 Colors */
{ Black, Yellow, Magenta, Red, White },
{ Black, Cyan, Blue, Magenta, White },
{ Black, Green, Cyan, Yellow, White },
{ Black, Magenta, Cyan, White, White },
{ Black, White, White, White, White },
{ Black, DarkRed, Brown, Red, White },
{ Black, Green, Cyan, Yellow, White },
{ Black, Violet, LightBlue, Magenta, White },
{ Black, Yellow, LightBlue, DarkRed, White }, /* Almost the same as the next one */
{ Black, Yellow, Violet, Red, White },
{ Black, Cyan, Yellow, Red, White }, /* CSI-DOS colors */
{ Black, Red, Green, Cyan, White },
{ Black, Cyan, Yellow, White, White },
{ Black, Yellow, Green, White, White },
{ Black, Cyan, Green, White, White },
};
unsigned scr_dirty = 0;
unsigned char req_page[512], req_palette[512];
unsigned char active_palette, active_page;
unsigned char half_frame = 0;
unsigned char param_change_line;
unsigned char change_req;
int upper_porch = 0; /* Default */
int lower_porch = 3; /* Default */
#define LINES_TOTAL (256+upper_porch+lower_porch)
// It is unlikely that we get a hardware surface, but one can hope
#define SCREEN_FLAGS \
(SDL_HWSURFACE|SDL_HWACCEL|SDL_ASYNCBLIT|SDL_ANYFORMAT|SDL_HWPALETTE|(fullscreen ? SDL_FULLSCREEN : SDL_RESIZABLE))
Uint16 horbase[513], vertbase[257];
/*
* Set the pixel at (x, y) to the given value
* NOTE: We never put pixels to the actual screen surface, no locking
* is necessary.
*/
static inline void putpixels(SDL_Surface * s, int x, Uint32 pixel)
{
Uint8 *p = (Uint8 *)s->pixels + horbase[x];
int n = horbase[x+1] - horbase[x];
do *p++ = pixel; while (--n);
}
static void Refresh_screen(void)
{
SDL_Surface* tmp;
tmp = SDL_DisplayFormat(screen);
SDL_SoftStretch(tmp, NULL, rl_screen, NULL);
SDL_FreeSurface(tmp);
SDL_Flip(rl_screen);
}
void Quit_SDL(void)
{
if (screen) SDL_FreeSurface(screen);
if (rl_screen) SDL_FreeSurface(rl_screen);
SDL_Quit();
}
/*
* Flushes a word into a statically defined scan line;
* scrolling and line doubling are done during blitting. bufno is 0 or 1,
* address is relative to the video buffer.
*/
int scr_write(int bufno, c_addr addr, d_word wrd)
{
int offset, dest_x, dest_y;
SDL_Surface * s;
if (!cflag) {
int i;
offset = addr * 8;
dest_y = offset / 512;
dest_x = offset & 511;
i = 256*bufno+dest_y;
s = lines[i];
if (!dirty[i]) {
dirty[i] = 1;
scr_dirty++;
}
for (i = 16; i; i--, dest_x++, wrd >>= 1) {
putpixels(s, dest_x, (wrd & 1) << 2);
}
} else {
int i;
offset = addr * 4;
dest_y = offset / 256;
dest_x = offset & 255;
i = 256*bufno+dest_y;
s = lines[i];
if (!dirty[i]) {
dirty[i] = 1;
scr_dirty++;
}
for(i = 8; i; i--, dest_x++, wrd >>= 2) {
putpixels(s, dest_x, wrd & 3);
}
}
return OK;
}
/* In the bk_icon array, each word is in RGBA format;
* A = 0 is transparent, A = 255 is opaque
*/
extern Uint32 bk_icon_width, bk_icon_height, bk_icon[];
Uint8 *
compute_icon_mask() {
int i;
static Uint8 * mask = 0;
if (mask) return mask;
mask = calloc(1, bk_icon_width*bk_icon_height/8);
if (!mask) return NULL;
for ( i=0; i<bk_icon_width*bk_icon_height; ) {
/* More than half opaque is opaque */
if ( (bk_icon[i] & 0xFF) >= 128)
mask[i/8] |= 0x01;
++i;
if ( (i%8) != 0 )
mask[i/8] <<= 1;
}
return mask;
}
/* BK-0010 screen refresh - no palettes */
extern void scr_refresh_bk0010(unsigned shift, unsigned full);
/* BK-0011 screen refresh - single size, lossy interlacing */
extern void scr_refresh_bk0011_1(unsigned shift, unsigned full);
/* BK-0011 screen refresh - double size, exact interlacing */
extern void scr_refresh_bk0011_2(unsigned shift, unsigned full);
void (*scr_refresh)(unsigned, unsigned);
void
setup_bases() {
int i;
int horpix = (cflag ? 256 : 512);
for (i = 0; i <= horpix; i++) {
horbase[i] = i * horsize / horpix;
}
for (i = 0; i <= 256; i++) {
vertbase[i] = i * vertsize / 256;
}
}
void scr_init() {
extern unsigned bk_icon[];
extern unsigned char * compute_icon_mask();
static char init_done = 0;
int i;
Uint32 bpp, rmask, gmask, bmask;
if (init_done) return;
init_done = 1;
SDL_WM_SetIcon(SDL_CreateRGBSurfaceFrom(bk_icon, bk_icon_width,
bk_icon_height, 32, bk_icon_width*4,
0xff000000, 0xff0000, 0xff00, 0xff),
compute_icon_mask());
rl_screen = SDL_SetVideoMode(480, 272, 16, SCREEN_FLAGS);
if (rl_screen == NULL) {
fprintf(stderr, _("Couldn't set up video: %s\n"),
SDL_GetError());
exit(1);
}
screen = SDL_CreateRGBSurface(SDL_SWSURFACE, horsize, vertsize, 32, 0, 0, 0, 0);
/* Translation disabled because of an SDL bug */
if (bkmodel == 0) {
SDL_WM_SetCaption("BK-0010", "BK-0010"); }
else {
SDL_WM_SetCaption("BK-0011M", "BK-0011M");
}
active_palette = bkmodel ? 15 : 0;
if (screen->format->BitsPerPixel == 8) {
SDL_SetColors(screen, palettes[active_palette], 0, 5);
}
/* Create palettized surfaces for scan lines for the highest possible
* resolution (so far width 1024).
*/
for (i = 0; i < 512; i++) {
lines[i] = SDL_CreateRGBSurface(SDL_SWSURFACE, 1024, 1,
8, 0, 0, 0, 0);
if (!lines[i]) {
fprintf(stderr, "Couldn't set up video: %s\n",
SDL_GetError());
exit(1);
}
SDL_SetPalette(lines[i], SDL_LOGPAL,
palettes[active_palette], 0, 5);
dirty[i] = 0;
}
SDL_ShowCursor(SDL_DISABLE);
scan_line_duration = TICK_RATE/(LINES_TOTAL*50);
scr_refresh = bkmodel ?
vertsize != 256 ? scr_refresh_bk0011_2 :
scr_refresh_bk0011_1 :
scr_refresh_bk0010;
if (horsize < 512) {
cflag = 1;
}
setup_bases();
}
/*
* Switches to the new screen size, or, if called with (0, 0),
* switches between color and B/W.
*/
scr_switch(int hsize, int vsize) {
int i;
int old_hor = horsize, old_vert = vertsize;
// It is ok to go beyond 1024x1024, but a smaller screen is
// returned to no less than 512x256
char restore = 0;
if (hsize | vsize) {
horsize = hsize;
vertsize = vsize;
/* Allow horizontal size down to 256 in color mode */
if (horsize < 512) {
if (cflag && horsize < 256) {
restore = 1;
horsize = 256;
} else if (!cflag) {
restore = 1;
horsize = 512;
}
}
if (vertsize < 256) { restore = 1; vertsize = 256; }
if (horsize > 1024) horsize = 1024;
if (vertsize > 1024) vertsize = 1024;
} else {
cflag = !cflag;
if (!cflag && horsize < 512) {
restore = 1;
horsize = 512;
}
}
if (restore || old_hor != horsize || old_vert != vertsize) {
screen = SDL_SetVideoMode(horsize, vertsize, 0, SCREEN_FLAGS);
}
setup_bases();
/* Re-flush both video pages */
for (i = 0; i < 040000; i+=2) {
scr_write(0, i, ram[1][i >> 1]);
scr_write(1, i, ram[7][i >> 1]);
}
if (bkmodel) scr_refresh = vertsize != 256 ? scr_refresh_bk0011_2 : scr_refresh_bk0011_1;
}
/* Returns the scan line number that is supposedly being displayed "now".
* Each half frame is TICK_RATE/50 long and consists of 128 lines.
*/
unsigned current_scan_line() {
unsigned nframes = ticks/half_frame_delay;
unsigned frame_ticks = ticks - half_frame_delay * nframes;
unsigned line = frame_ticks / scan_line_duration;
if (line < upper_porch) return 0;
line -= upper_porch;
if (line < 256) return line; else return 256;
}
void scr_param_change(int pal, int buf) {
int cur = current_scan_line();
uint i;
for (i = param_change_line; i < cur; i++) {
req_palette[2 * i + half_frame] = active_palette;
req_page[2 * i + half_frame] = active_page;
}
active_palette = pal;
active_page = buf;
param_change_line = cur;
change_req = 3; /* For 2 half-frames */
fprintf(stderr, "l=%d\n", cur);
}
/*
* Just before a half frame ends, fill up the buffer and palette
* requests to the end with the current values.
*/
void scr_sync() {
uint i;
for (i = param_change_line; i < 256; i++) {
req_palette[2 * i + half_frame] = active_palette;
req_page[2 * i + half_frame] = active_page;
}
half_frame ^= 1;
param_change_line = 0;
}
/*
* Screen refresh for BK-0010 does not need to know about buffers or
* palettes.
*/
void
scr_refresh_bk0010(unsigned shift, unsigned full)
{
static SDL_Surface* tmp;
int blit_all = shift != cur_shift || cur_width != full;
int i;
/* If more than a few lines changed, no point
* doing separate UpdateRect's for each line.
*/
int update_all = blit_all || scr_dirty >= 4;
int width = horsize;
int nlines = full ? 256 : 64;
static SDL_Rect srcrect = {0, 0, 0, 1};
static SDL_Rect dstrect = {0, 0, 0, 0};
srcrect.w = width;
for (i = 0; i < nlines; i++) {
int line = (i + shift) & 0xFF;
SDL_Surface * l = lines[line];
dstrect.y = vertbase[i];
int vertstretch = vertbase[i+1]-vertbase[i];
if (dirty[line] | blit_all) {
int n = vertstretch-1;
do {
SDL_BlitSurface(l, &srcrect, screen, &dstrect);
dstrect.y++;
} while (n--);
if (!update_all) {
Refresh_screen();
//SDL_UpdateRect(screen, 0, vertbase[i], width, vertstretch);
}
}
}
// Only the first 256 lines are used
memset(dirty, 0, 256);
if (!full && cur_width) {
/* Black out the low 3/4 of the screen */
dstrect.x = 0; dstrect.y = vertbase[64];
dstrect.w = width;
dstrect.h = vertbase[256]-vertbase[64];
SDL_FillRect(screen, &dstrect, 0);
}
cur_width = full;
cur_shift = shift;
if (update_all)
{
Refresh_screen();
}
scr_dirty = 0;
}
/*
* For single-sized BK-0011 screen we do crude interlacing:
* even lines are taken from the first half-frame,
* odd lines - from the second.
*/
void
scr_refresh_bk0011_1(unsigned shift, unsigned full) {
int blit_all = change_req || shift != cur_shift || cur_width != full;
int i;
/* If more than a few lines changed, no point
* doing separate UpdateRect's for each line.
*/
int update_all = blit_all || scr_dirty >= 4;
int width = horsize;
int do_palette = change_req || shift != cur_shift;
int nlines = full ? 256 : 64;
static SDL_Rect srcrect = {0, 0, 0, 1};
static SDL_Rect dstrect = {0, 0, 0, 0};
srcrect.w = width;
for (i = 0; i < nlines; i++) {
int line = (i + shift) & 0xFF;
unsigned physline = 256*req_page[2*i+(i&1)] + line;
SDL_Surface * l = lines[physline];
dstrect.y = i;
if (dirty[physline] | blit_all) {
if (do_palette) {
SDL_SetPalette(l, SDL_LOGPAL,
palettes[req_palette[2*i+(i&1)]], 0, 5);
}
SDL_BlitSurface(l, &srcrect, screen, &dstrect);
if (!update_all) {
//SDL_UpdateRect(screen, 0, dstrect.y, width, 1);
Refresh_screen();
}
}
}
memset(dirty, 0, 1024);
if (!full && cur_width) {
/* Black out the low 3/4 of the screen */
dstrect.x = 0; dstrect.y = 64;
dstrect.w = width;
dstrect.h = 192;
SDL_FillRect(screen, &dstrect, 0);
}
cur_width = full;
cur_shift = shift;
if (update_all) {
Refresh_screen();
}
scr_dirty = 0;
change_req >>= 1;
}
void
scr_refresh_bk0011_2(unsigned shift, unsigned full) {
int blit_all = change_req || shift != cur_shift || cur_width != full;
int i;
/* If more than a few lines changed, no point
* doing separate UpdateRect's for each line.
*/
int update_all = blit_all || scr_dirty >= 4;
int width = horsize;
int do_palette = change_req || shift != cur_shift;
int nlines = full ? vertsize : vertbase[64];
static SDL_Rect srcrect = {0, 0, 512, 1};
srcrect.w = width;
static SDL_Rect dstrect = {0, 0, 0, 0};
int j;
for (i = 0, j = 0; i < nlines; i++) {
// The next line is the reverse mapping of vertbase
if (vertbase[j+1] == i) j++;
int line = (j + shift) & 0xFF;
unsigned physline = 256*req_page[2*j+(i&1)]+line;
SDL_Surface * l = lines[physline];
dstrect.y = i;
if (dirty[physline] | blit_all) {
if (do_palette) {
SDL_SetPalette(l, SDL_LOGPAL,
palettes[req_palette[2*j+(i&1)]], 0, 5);
}
SDL_BlitSurface(l, &srcrect, screen, &dstrect);
if (!update_all) {
//SDL_UpdateRect(screen, 0, dstrect.y, width, 1);
Refresh_screen();
}
}
}
memset(dirty, 0, vertsize);
if (!full && cur_width) {
/* Black out the low 3/4 of the screen */
dstrect.x = 0; dstrect.y = vertbase[64];
dstrect.w = width;
dstrect.h = vertsize - dstrect.y;
SDL_FillRect(screen, &dstrect, 0);
}
cur_width = full;
cur_shift = shift;
if (update_all) {
Refresh_screen();
}
scr_dirty = 0;
change_req >>= 1;
}
void scr_flush() {
if (scr_dirty || change_req)
scr_refresh((tty_scroll - 0330) & 0377, tty_scroll & 01000);
}