Files
86Box/src/video/vid_ega_render.c

252 lines
8.4 KiB
C
Raw Normal View History

/*
2023-01-06 15:36:05 -05:00
* 86Box A hypervisor and IBM PC system emulator that specializes in
* running old operating systems and software designed for IBM
* PC systems and compatibles from 1981 through fairly recent
* system designs based on the PCI bus.
*
2023-01-06 15:36:05 -05:00
* This file is part of the 86Box distribution.
*
2023-01-06 15:36:05 -05:00
* EGA renderers.
*
2020-03-25 00:46:02 +02:00
*
*
2023-01-06 15:36:29 -05:00
* Authors: Sarah Walker, <https://pcem-emulator.co.uk/>
2023-01-06 15:36:05 -05:00
* Miran Grca, <mgrca8@gmail.com>
*
2023-01-06 15:36:05 -05:00
* Copyright 2008-2019 Sarah Walker.
* Copyright 2016-2019 Miran Grca.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <wchar.h>
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/timer.h>
#include <86box/mem.h>
#include <86box/rom.h>
#include <86box/video.h>
#include <86box/vid_ega.h>
#include <86box/vid_ega_render_remap.h>
int
ega_display_line(ega_t *ega)
{
2023-06-01 18:32:25 -04:00
int y_add = enable_overscan ? (overscan_y >> 1) : 0;
2022-08-31 19:19:29 -04:00
unsigned int dl = ega->displine;
if (ega->crtc[9] & 0x1f)
2022-08-31 19:19:29 -04:00
dl -= (ega->crtc[8] & 0x1f);
dl += y_add;
dl &= 0x7ff;
return dl;
}
void
ega_render_blank(ega_t *ega)
{
if ((ega->displine + ega->y_add) < 0)
2022-08-31 19:19:29 -04:00
return;
2023-06-01 18:32:25 -04:00
for (int x = 0; x < (ega->hdisp + ega->scrollcache); x++) {
2022-08-31 19:19:29 -04:00
switch (ega->seqregs[1] & 9) {
case 0:
2023-06-09 23:46:54 -04:00
for (uint8_t xx = 0; xx < 9; xx++)
2022-08-31 19:19:29 -04:00
buffer32->line[ega->displine + ega->y_add][ega->x_add + (x * 9) + xx] = 0;
break;
case 1:
2023-06-09 23:46:54 -04:00
for (uint8_t xx = 0; xx < 8; xx++)
2022-08-31 19:19:29 -04:00
buffer32->line[ega->displine + ega->y_add][ega->x_add + (x * 8) + xx] = 0;
break;
case 8:
2023-06-09 23:46:54 -04:00
for (uint8_t xx = 0; xx < 18; xx++)
2022-08-31 19:19:29 -04:00
buffer32->line[ega->displine + ega->y_add][ega->x_add + (x * 18) + xx] = 0;
break;
case 9:
2023-06-09 23:46:54 -04:00
for (uint8_t xx = 0; xx < 16; xx++)
2022-08-31 19:19:29 -04:00
buffer32->line[ega->displine + ega->y_add][ega->x_add + (x * 16) + xx] = 0;
break;
2023-06-09 23:46:54 -04:00
default:
break;
2022-08-31 19:19:29 -04:00
}
}
}
void
ega_render_overscan_left(ega_t *ega)
{
if ((ega->displine + ega->y_add) < 0)
2022-08-31 19:19:29 -04:00
return;
if (ega->scrblank || (ega->hdisp == 0))
2022-08-31 19:19:29 -04:00
return;
2023-06-01 18:32:25 -04:00
for (int i = 0; i < ega->x_add; i++)
2022-08-31 19:19:29 -04:00
buffer32->line[ega->displine + ega->y_add][i] = ega->overscan_color;
}
void
ega_render_overscan_right(ega_t *ega)
{
2023-06-01 18:32:25 -04:00
int right;
if ((ega->displine + ega->y_add) < 0)
2022-08-31 19:19:29 -04:00
return;
if (ega->scrblank || (ega->hdisp == 0))
2022-08-31 19:19:29 -04:00
return;
right = (overscan_x >> 1) + ega->scrollcache;
2023-06-01 18:32:25 -04:00
for (int i = 0; i < right; i++)
2022-08-31 19:19:29 -04:00
buffer32->line[ega->displine + ega->y_add][ega->x_add + ega->hdisp + i] = ega->overscan_color;
}
void
2023-03-23 17:06:12 +13:00
ega_render_text(ega_t *ega)
2022-02-20 02:26:27 -05:00
{
if ((ega->displine + ega->y_add) < 0)
2022-08-31 19:19:29 -04:00
return;
2022-02-20 02:26:27 -05:00
if (ega->firstline_draw == 2000)
2022-08-31 19:19:29 -04:00
ega->firstline_draw = ega->displine;
ega->lastline_draw = ega->displine;
if (ega->fullchange) {
2023-07-31 14:45:22 -04:00
const bool doublewidth = ((ega->seqregs[1] & 8) != 0);
const bool attrblink = ((ega->attrregs[0x10] & 8) != 0);
const bool attrlinechars = (ega->attrregs[0x10] & 4);
2023-07-31 14:45:22 -04:00
const bool crtcreset = ((ega->crtc[0x17] & 0x80) == 0);
const bool seq9dot = ((ega->seqregs[1] & 1) == 0);
const int dwshift = doublewidth ? 1 : 0;
const int dotwidth = 1 << dwshift;
const int charwidth = dotwidth * (seq9dot ? 9 : 8);
const bool blinked = ega->blink & 0x10;
uint32_t *p = &buffer32->line[ega->displine + ega->y_add][ega->x_add];
2022-08-31 19:19:29 -04:00
2023-03-23 17:06:12 +13:00
for (int x = 0; x < (ega->hdisp + ega->scrollcache); x += charwidth) {
uint32_t addr = ega->remap_func(ega, ega->ma) & ega->vrammask;
2022-08-31 19:19:29 -04:00
2023-03-23 17:06:12 +13:00
int drawcursor = ((ega->ma == ega->ca) && ega->con && ega->cursoron);
2022-08-31 19:19:29 -04:00
2023-06-01 18:32:25 -04:00
uint32_t chr;
uint32_t attr;
if (!crtcreset) {
2022-08-31 19:19:29 -04:00
chr = ega->vram[addr];
attr = ega->vram[addr + 1];
} else
chr = attr = 0;
2023-03-23 17:06:12 +13:00
uint32_t charaddr;
2022-08-31 19:19:29 -04:00
if (attr & 8)
2023-06-01 18:32:25 -04:00
charaddr = ega->charsetb + (chr * 0x80);
2022-08-31 19:19:29 -04:00
else
2023-06-01 18:32:25 -04:00
charaddr = ega->charseta + (chr * 0x80);
2022-08-31 19:19:29 -04:00
2023-06-01 18:32:25 -04:00
int fg;
int bg;
2022-08-31 19:19:29 -04:00
if (drawcursor) {
bg = ega->pallook[ega->egapal[attr & 0x0f]];
fg = ega->pallook[ega->egapal[attr >> 4]];
} else {
fg = ega->pallook[ega->egapal[attr & 0x0f]];
bg = ega->pallook[ega->egapal[attr >> 4]];
if ((attr & 0x80) && attrblink) {
2022-08-31 19:19:29 -04:00
bg = ega->pallook[ega->egapal[(attr >> 4) & 7]];
if (blinked)
2022-08-31 19:19:29 -04:00
fg = bg;
}
}
2023-03-23 17:06:12 +13:00
uint32_t dat = ega->vram[charaddr + (ega->sc << 2)];
dat <<= 1;
if ((chr & ~0x1F) == 0xC0 && attrlinechars)
2023-03-23 17:06:12 +13:00
dat |= (dat >> 1) & 1;
2023-03-23 17:06:12 +13:00
for (int xx = 0; xx < charwidth; xx++)
2023-07-31 14:45:22 -04:00
p[xx] = (dat & (0x100 >> (xx >> dwshift))) ? fg : bg;
ega->ma += 4;
2023-03-23 17:06:12 +13:00
p += charwidth;
}
ega->ma &= 0x3ffff;
}
}
void
ega_render_graphics(ega_t *ega)
{
if ((ega->displine + ega->y_add) < 0)
2022-08-31 19:19:29 -04:00
return;
2022-06-26 23:16:44 +02:00
if (ega->firstline_draw == 2000)
ega->firstline_draw = ega->displine;
ega->lastline_draw = ega->displine;
2023-07-31 14:45:22 -04:00
const bool doublewidth = ((ega->seqregs[1] & 8) != 0);
const bool cga2bpp = ((ega->gdcreg[5] & 0x20) != 0);
const bool attrblink = ((ega->attrregs[0x10] & 8) != 0);
const bool blinked = ega->blink & 0x10;
const bool crtcreset = ((ega->crtc[0x17] & 0x80) == 0);
const bool seqoddeven = ((ega->seqregs[1] & 4) != 0);
const uint8_t blinkmask = (attrblink && blinked ? 0x8 : 0x0);
uint32_t *p = &buffer32->line[ega->displine + ega->y_add][ega->x_add];
const int dwshift = doublewidth ? 1 : 0;
const int dotwidth = 1 << dwshift;
const int charwidth = dotwidth * 8;
int secondcclk = 0;
for (int x = 0; x <= (ega->hdisp + ega->scrollcache); x += charwidth) {
uint32_t addr = ega->remap_func(ega, ega->ma) & ega->vrammask;
uint8_t edat[4];
if (seqoddeven) {
// FIXME: Verify the behaviour of planes 1,3 on actual hardware
2023-07-31 14:45:22 -04:00
edat[0] = ega->vram[(addr | 0) ^ secondcclk];
edat[1] = ega->vram[(addr | 1) ^ secondcclk];
edat[2] = ega->vram[(addr | 2) ^ secondcclk];
edat[3] = ega->vram[(addr | 3) ^ secondcclk];
secondcclk = (secondcclk + 1) & 1;
if (secondcclk == 0)
ega->ma += 4;
2022-06-26 23:16:44 +02:00
} else {
2022-08-31 19:19:29 -04:00
*(uint32_t *) (&edat[0]) = *(uint32_t *) (&ega->vram[addr]);
2022-06-26 23:16:44 +02:00
ega->ma += 4;
}
ega->ma &= 0x3ffff;
2022-06-26 23:16:44 +02:00
if (cga2bpp) {
// Remap CGA 2bpp-chunky data into fully planar data
2023-07-31 14:45:22 -04:00
uint8_t dat0 = egaremap2bpp[edat[1]] | (egaremap2bpp[edat[0]] << 4);
uint8_t dat1 = egaremap2bpp[edat[1] >> 1] | (egaremap2bpp[edat[0] >> 1] << 4);
uint8_t dat2 = egaremap2bpp[edat[3]] | (egaremap2bpp[edat[2]] << 4);
uint8_t dat3 = egaremap2bpp[edat[3] >> 1] | (egaremap2bpp[edat[2] >> 1] << 4);
edat[0] = dat0;
edat[1] = dat1;
edat[2] = dat2;
edat[3] = dat3;
}
if (!crtcreset) {
for (int i = 0; i < 8; i += 2) {
const int outoffs = i << dwshift;
const int inshift = 6 - i;
2023-07-31 14:45:22 -04:00
uint8_t dat = (edatlookup[(edat[0] >> inshift) & 3][(edat[1] >> inshift) & 3])
| (edatlookup[(edat[2] >> inshift) & 3][(edat[3] >> inshift) & 3] << 2);
// FIXME: Confirm blink behaviour is actually XOR on real hardware
uint32_t p0 = ega->pallook[ega->egapal[((dat >> 4) & ega->plane_mask) ^ blinkmask]];
2023-07-31 14:45:22 -04:00
uint32_t p1 = ega->pallook[ega->egapal[(dat & ega->plane_mask) ^ blinkmask]];
for (int subx = 0; subx < dotwidth; subx++)
p[outoffs + subx] = p0;
for (int subx = 0; subx < dotwidth; subx++)
p[outoffs + subx + dotwidth] = p1;
}
2022-06-26 23:16:44 +02:00
} else
memset(p, 0x00, charwidth * sizeof(uint32_t));
2022-06-26 23:16:44 +02:00
p += charwidth;
}
}