Merge pull request #5674 from starfrost013/bugfixes

Video Refactor Part 2: Super CGA
This commit is contained in:
Miran Grča
2025-06-10 14:09:12 +02:00
committed by GitHub
42 changed files with 2917 additions and 2710 deletions

View File

@@ -23,7 +23,6 @@ add_library(mch OBJECT
m_xt_laserxt.c
m_xt_philips.c
m_xt_t1000.c
m_xt_t1000_vid.c
m_xt_xi8088.c
m_xt_zenith.c
m_pcjr.c
@@ -37,7 +36,6 @@ add_library(mch OBJECT
m_at_commodore.c
m_at_grid.c
m_at_t3100e.c
m_at_t3100e_vid.c
m_ps1.c
m_ps1_hdc.c
m_ps2_isa.c

View File

@@ -116,7 +116,7 @@ typedef struct amsvid_t {
int sc;
int vc;
int cgadispon;
int con;
int cursorvisible;
int cursoron;
int cgablink;
int vsynctime;
@@ -369,26 +369,26 @@ vid_poll_1512(void *priv)
for (c = 0; c < 8; c++) {
if ((vid->cgamode & 0x12) == 0x12) {
buffer32->line[vid->displine << 1][c] = buffer32->line[(vid->displine << 1) + 1][c] = (vid->border & 15) + 16;
if (vid->cgamode & 1) {
if (vid->cgamode & CGA_MODE_FLAG_HIGHRES) {
buffer32->line[vid->displine << 1][c + (vid->crtc[1] << 3) + 8] = buffer32->line[(vid->displine << 1) + 1][c + (vid->crtc[1] << 3) + 8] = 0;
} else {
buffer32->line[vid->displine << 1][c + (vid->crtc[1] << 4) + 8] = buffer32->line[(vid->displine << 1) + 1][c + (vid->crtc[1] << 4) + 8] = 0;
}
} else {
buffer32->line[vid->displine << 1][c] = buffer32->line[(vid->displine << 1) + 1][c] = (vid->cgacol & 15) + 16;
if (vid->cgamode & 1) {
if (vid->cgamode & CGA_MODE_FLAG_HIGHRES) {
buffer32->line[vid->displine << 1][c + (vid->crtc[1] << 3) + 8] = buffer32->line[(vid->displine << 1) + 1][c + (vid->crtc[1] << 3) + 8] = (vid->cgacol & 15) + 16;
} else {
buffer32->line[vid->displine << 1][c + (vid->crtc[1] << 4) + 8] = buffer32->line[(vid->displine << 1) + 1][c + (vid->crtc[1] << 4) + 8] = (vid->cgacol & 15) + 16;
}
}
}
if (vid->cgamode & 1) {
if (vid->cgamode & CGA_MODE_FLAG_HIGHRES) {
for (x = 0; x < 80; x++) {
chr = vid->vram[(vid->ma << 1) & 0x3fff];
attr = vid->vram[((vid->ma << 1) + 1) & 0x3fff];
drawcursor = ((vid->ma == ca) && vid->con && vid->cursoron);
if (vid->cgamode & 0x20) {
drawcursor = ((vid->ma == ca) && vid->cursorvisible && vid->cursoron);
if (vid->cgamode & CGA_MODE_FLAG_BLINK) {
cols[1] = (attr & 15) + 16;
cols[0] = ((attr >> 4) & 7) + 16;
if ((vid->blink & 16) && (attr & 0x80) && !drawcursor)
@@ -408,12 +408,12 @@ vid_poll_1512(void *priv)
}
vid->ma++;
}
} else if (!(vid->cgamode & 2)) {
} else if (!(vid->cgamode & CGA_MODE_FLAG_GRAPHICS)) {
for (x = 0; x < 40; x++) {
chr = vid->vram[(vid->ma << 1) & 0x3fff];
attr = vid->vram[((vid->ma << 1) + 1) & 0x3fff];
drawcursor = ((vid->ma == ca) && vid->con && vid->cursoron);
if (vid->cgamode & 0x20) {
drawcursor = ((vid->ma == ca) && vid->cursorvisible && vid->cursoron);
if (vid->cgamode & CGA_MODE_FLAG_BLINK) {
cols[1] = (attr & 15) + 16;
cols[0] = ((attr >> 4) & 7) + 16;
if ((vid->blink & 16) && (attr & 0x80))
@@ -433,10 +433,10 @@ vid_poll_1512(void *priv)
}
}
}
} else if (!(vid->cgamode & 16)) {
} else if (!(vid->cgamode & CGA_MODE_FLAG_HIGHRES_GRAPHICS)) {
cols[0] = (vid->cgacol & 15) | 16;
col = (vid->cgacol & 16) ? 24 : 16;
if (vid->cgamode & 4) {
if (vid->cgamode & CGA_MODE_FLAG_BW) {
cols[1] = col | 3;
cols[2] = col | 4;
cols[3] = col | 7;
@@ -477,7 +477,7 @@ vid_poll_1512(void *priv)
}
} else {
cols[0] = ((vid->cgamode & 0x12) == 0x12) ? 0 : (vid->cgacol & 15) + 16;
if (vid->cgamode & 1) {
if (vid->cgamode & CGA_MODE_FLAG_HIGHRES) {
hline(buffer32, 0, (vid->displine << 1), (vid->crtc[1] << 3) + 16, cols[0]);
hline(buffer32, 0, (vid->displine << 1) + 1, (vid->crtc[1] << 3) + 16, cols[0]);
} else {
@@ -486,7 +486,7 @@ vid_poll_1512(void *priv)
}
}
if (vid->cgamode & 1)
if (vid->cgamode & CGA_MODE_FLAG_HIGHRES)
x = (vid->crtc[1] << 3) + 16;
else
x = (vid->crtc[1] << 4) + 16;
@@ -513,7 +513,7 @@ vid_poll_1512(void *priv)
vid->stat &= ~8;
}
if (vid->sc == (vid->crtc[11] & 31)) {
vid->con = 0;
vid->cursorvisible = 0;
}
if (vid->vadj) {
vid->sc++;
@@ -545,7 +545,7 @@ vid_poll_1512(void *priv)
vid->displine = 0;
vid->vsynctime = 46;
if (vid->cgamode & 1)
if (vid->cgamode & CGA_MODE_FLAG_HIGHRES)
x = (vid->crtc[1] << 3) + 16;
else
x = (vid->crtc[1] << 4) + 16;
@@ -582,15 +582,15 @@ vid_poll_1512(void *priv)
video_res_x = xsize;
video_res_y = ysize;
if (vid->cgamode & 1) {
if (vid->cgamode & CGA_MODE_FLAG_HIGHRES) {
video_res_x /= 8;
video_res_y /= vid->crtc[9] + 1;
video_bpp = 0;
} else if (!(vid->cgamode & 2)) {
} else if (!(vid->cgamode & CGA_MODE_FLAG_GRAPHICS)) {
video_res_x /= 16;
video_res_y /= vid->crtc[9] + 1;
video_bpp = 0;
} else if (!(vid->cgamode & 16)) {
} else if (!(vid->cgamode & CGA_MODE_FLAG_HIGHRES_GRAPHICS)) {
video_res_x /= 2;
video_bpp = 2;
} else {
@@ -607,7 +607,7 @@ vid_poll_1512(void *priv)
vid->ma = vid->maback;
}
if (vid->sc == (vid->crtc[10] & 31))
vid->con = 1;
vid->cursorvisible = 1;
}
}
@@ -1292,7 +1292,7 @@ lcdm_poll(amsvid_t *vid)
for (x = 0; x < mda->crtc[1]; x++) {
chr = mda->vram[(mda->ma << 1) & 0xfff];
attr = mda->vram[((mda->ma << 1) + 1) & 0xfff];
drawcursor = ((mda->ma == ca) && mda->con && mda->cursoron);
drawcursor = ((mda->ma == ca) && mda->cursorvisible && mda->cursoron);
blink = ((mda->blink & 16) && (mda->ctrl & 0x20) && (attr & 0x80) && !drawcursor);
lcd_draw_char_80(vid, &(buffer32->line[mda->displine])[x * 8], chr, attr, drawcursor, blink, mda->sc, 0, mda->ctrl);
@@ -1316,7 +1316,7 @@ lcdm_poll(amsvid_t *vid)
mda->stat &= ~8;
}
if (mda->sc == (mda->crtc[11] & 31) || ((mda->crtc[8] & 3) == 3 && mda->sc == ((mda->crtc[11] & 31) >> 1))) {
mda->con = 0;
mda->cursorvisible = 0;
}
if (mda->vadj) {
mda->sc++;
@@ -1383,7 +1383,7 @@ lcdm_poll(amsvid_t *vid)
mda->ma = mda->maback;
}
if (mda->sc == (mda->crtc[10] & 31) || ((mda->crtc[8] & 3) == 3 && mda->sc == ((mda->crtc[10] & 31) >> 1)))
mda->con = 1;
mda->cursorvisible = 1;
}
}
@@ -1403,14 +1403,14 @@ lcdc_poll(amsvid_t *vid)
uint16_t ca;
int blink;
ca = (cga->crtc[15] | (cga->crtc[14] << 8)) & 0x3fff;
ca = (cga->crtc[CGA_CRTC_CURSOR_ADDR_LOW] | (cga->crtc[CGA_CRTC_CURSOR_ADDR_HIGH] << 8)) & 0x3fff;
if (!cga->linepos) {
timer_advance_u64(&vid->timer, cga->dispofftime);
cga->cgastat |= 1;
cga->linepos = 1;
oldsc = cga->sc;
if ((cga->crtc[8] & 3) == 3)
if ((cga->crtc[CGA_CRTC_INTERLACE] & 3) == 3)
cga->sc = ((cga->sc << 1) + cga->oddeven) & 7;
if (cga->cgadispon) {
if (cga->displine < cga->firstline) {
@@ -1419,28 +1419,28 @@ lcdc_poll(amsvid_t *vid)
}
cga->lastline = cga->displine;
if (cga->cgamode & 1) {
for (x = 0; x < cga->crtc[1]; x++) {
if (cga->cgamode & CGA_MODE_FLAG_HIGHRES) {
for (x = 0; x < cga->crtc[CGA_CRTC_HDISP]; x++) {
chr = cga->charbuffer[x << 1];
attr = cga->charbuffer[(x << 1) + 1];
drawcursor = ((cga->ma == ca) && cga->con && cga->cursoron);
blink = ((cga->cgablink & 16) && (cga->cgamode & 0x20) && (attr & 0x80) && !drawcursor);
drawcursor = ((cga->ma == ca) && cga->cursorvisible && cga->cursoron);
blink = ((cga->cgablink & 16) && (cga->cgamode & CGA_MODE_FLAG_BLINK) && (attr & 0x80) && !drawcursor);
lcd_draw_char_80(vid, &(buffer32->line[cga->displine << 1])[x * 8], chr, attr, drawcursor, blink, cga->sc, cga->cgamode & 0x40, cga->cgamode);
lcd_draw_char_80(vid, &(buffer32->line[(cga->displine << 1) + 1])[x * 8], chr, attr, drawcursor, blink, cga->sc, cga->cgamode & 0x40, cga->cgamode);
cga->ma++;
}
} else if (!(cga->cgamode & 2)) {
for (x = 0; x < cga->crtc[1]; x++) {
} else if (!(cga->cgamode & CGA_MODE_FLAG_GRAPHICS)) {
for (x = 0; x < cga->crtc[CGA_CRTC_HDISP]; x++) {
chr = cga->vram[(cga->ma << 1) & 0x3fff];
attr = cga->vram[((cga->ma << 1) + 1) & 0x3fff];
drawcursor = ((cga->ma == ca) && cga->con && cga->cursoron);
blink = ((cga->cgablink & 16) && (cga->cgamode & 0x20) && (attr & 0x80) && !drawcursor);
drawcursor = ((cga->ma == ca) && cga->cursorvisible && cga->cursoron);
blink = ((cga->cgablink & 16) && (cga->cgamode & CGA_MODE_FLAG_BLINK) && (attr & 0x80) && !drawcursor);
lcd_draw_char_40(vid, &(buffer32->line[cga->displine << 1])[x * 16], chr, attr, drawcursor, blink, cga->sc, cga->cgamode);
lcd_draw_char_40(vid, &(buffer32->line[(cga->displine << 1) + 1])[x * 16], chr, attr, drawcursor, blink, cga->sc, cga->cgamode);
cga->ma++;
}
} else { /* Graphics mode */
for (x = 0; x < cga->crtc[1]; x++) {
for (x = 0; x < cga->crtc[CGA_CRTC_HDISP]; x++) {
dat = (cga->vram[((cga->ma << 1) & 0x1fff) + ((cga->sc & 1) * 0x2000)] << 8) | cga->vram[((cga->ma << 1) & 0x1fff) + ((cga->sc & 1) * 0x2000) + 1];
cga->ma++;
for (uint8_t c = 0; c < 16; c++) {
@@ -1450,22 +1450,22 @@ lcdc_poll(amsvid_t *vid)
}
}
} else {
if (cga->cgamode & 1) {
hline(buffer32, 0, (cga->displine << 1), (cga->crtc[1] << 3), green);
hline(buffer32, 0, (cga->displine << 1) + 1, (cga->crtc[1] << 3), green);
if (cga->cgamode & CGA_MODE_FLAG_HIGHRES) {
hline(buffer32, 0, (cga->displine << 1), (cga->crtc[CGA_CRTC_HDISP] << 3), green);
hline(buffer32, 0, (cga->displine << 1) + 1, (cga->crtc[CGA_CRTC_HDISP] << 3), green);
} else {
hline(buffer32, 0, (cga->displine << 1), (cga->crtc[1] << 4), green);
hline(buffer32, 0, (cga->displine << 1) + 1, (cga->crtc[1] << 4), green);
hline(buffer32, 0, (cga->displine << 1), (cga->crtc[CGA_CRTC_HDISP] << 4), green);
hline(buffer32, 0, (cga->displine << 1) + 1, (cga->crtc[CGA_CRTC_HDISP] << 4), green);
}
}
if (cga->cgamode & 1)
x = (cga->crtc[1] << 3);
if (cga->cgamode & CGA_MODE_FLAG_HIGHRES)
x = (cga->crtc[CGA_CRTC_HDISP] << 3);
else
x = (cga->crtc[1] << 4);
x = (cga->crtc[CGA_CRTC_HDISP] << 4);
cga->sc = oldsc;
if (cga->vc == cga->crtc[7] && !cga->sc)
if (cga->vc == cga->crtc[CGA_CRTC_VSYNC] && !cga->sc)
cga->cgastat |= 8;
cga->displine++;
if (cga->displine >= 360)
@@ -1478,10 +1478,10 @@ lcdc_poll(amsvid_t *vid)
if (!cga->vsynctime)
cga->cgastat &= ~8;
}
if (cga->sc == (cga->crtc[11] & 31) || ((cga->crtc[8] & 3) == 3 && cga->sc == ((cga->crtc[11] & 31) >> 1))) {
cga->con = 0;
if (cga->sc == (cga->crtc[CGA_CRTC_CURSOR_END] & 31) || ((cga->crtc[CGA_CRTC_INTERLACE] & 3) == 3 && cga->sc == ((cga->crtc[CGA_CRTC_CURSOR_END] & 31) >> 1))) {
cga->cursorvisible = 0;
}
if ((cga->crtc[8] & 3) == 3 && cga->sc == (cga->crtc[9] >> 1))
if ((cga->crtc[CGA_CRTC_INTERLACE] & 3) == 3 && cga->sc == (cga->crtc[CGA_CRTC_MAX_SCANLINE_ADDR] >> 1))
cga->maback = cga->ma;
if (cga->vadj) {
cga->sc++;
@@ -1490,41 +1490,41 @@ lcdc_poll(amsvid_t *vid)
cga->vadj--;
if (!cga->vadj) {
cga->cgadispon = 1;
cga->ma = cga->maback = (cga->crtc[13] | (cga->crtc[12] << 8)) & 0x3fff;
cga->ma = cga->maback = (cga->crtc[CGA_CRTC_START_ADDR_LOW] | (cga->crtc[CGA_CRTC_START_ADDR_HIGH] << 8)) & 0x3fff;
cga->sc = 0;
}
} else if (cga->sc == cga->crtc[9]) {
} else if (cga->sc == cga->crtc[CGA_CRTC_MAX_SCANLINE_ADDR]) {
cga->maback = cga->ma;
cga->sc = 0;
oldvc = cga->vc;
cga->vc++;
cga->vc &= 127;
if (cga->vc == cga->crtc[6])
if (cga->vc == cga->crtc[CGA_CRTC_VDISP])
cga->cgadispon = 0;
if (oldvc == cga->crtc[4]) {
if (oldvc == cga->crtc[CGA_CRTC_VTOTAL]) {
cga->vc = 0;
cga->vadj = cga->crtc[5];
cga->vadj = cga->crtc[CGA_CRTC_VTOTAL_ADJUST];
if (!cga->vadj)
cga->cgadispon = 1;
if (!cga->vadj)
cga->ma = cga->maback = (cga->crtc[13] | (cga->crtc[12] << 8)) & 0x3fff;
if ((cga->crtc[10] & 0x60) == 0x20)
cga->ma = cga->maback = (cga->crtc[CGA_CRTC_START_ADDR_LOW] | (cga->crtc[CGA_CRTC_START_ADDR_HIGH] << 8)) & 0x3fff;
if ((cga->crtc[CGA_CRTC_CURSOR_START] & 0x60) == 0x20)
cga->cursoron = 0;
else
cga->cursoron = cga->cgablink & 8;
}
if (cga->vc == cga->crtc[7]) {
if (cga->vc == cga->crtc[CGA_CRTC_VSYNC]) {
cga->cgadispon = 0;
cga->displine = 0;
cga->vsynctime = 16;
if (cga->crtc[7]) {
if (cga->cgamode & 1)
x = (cga->crtc[1] << 3);
if (cga->crtc[CGA_CRTC_VSYNC]) {
if (cga->cgamode & CGA_MODE_FLAG_HIGHRES)
x = (cga->crtc[CGA_CRTC_HDISP] << 3);
else
x = (cga->crtc[1] << 4);
x = (cga->crtc[CGA_CRTC_HDISP] << 4);
cga->lastline++;
xs_temp = x;
@@ -1536,7 +1536,7 @@ lcdc_poll(amsvid_t *vid)
if (ys_temp < 32)
ys_temp = 400;
if ((cga->cgamode & 8) && ((xs_temp != xsize) || (ys_temp != ysize) || video_force_resize_get())) {
if ((cga->cgamode & CGA_MODE_FLAG_VIDEO_ENABLE) && ((xs_temp != xsize) || (ys_temp != ysize) || video_force_resize_get())) {
xsize = xs_temp;
ysize = ys_temp;
set_screen_size(xsize, ysize);
@@ -1553,15 +1553,15 @@ lcdc_poll(amsvid_t *vid)
video_res_x = xsize;
video_res_y = ysize;
if (cga->cgamode & 1) {
if (cga->cgamode & CGA_MODE_FLAG_HIGHRES) {
video_res_x /= 8;
video_res_y /= cga->crtc[9] + 1;
video_res_y /= cga->crtc[CGA_CRTC_MAX_SCANLINE_ADDR] + 1;
video_bpp = 0;
} else if (!(cga->cgamode & 2)) {
} else if (!(cga->cgamode & CGA_MODE_FLAG_GRAPHICS)) {
video_res_x /= 16;
video_res_y /= cga->crtc[9] + 1;
video_res_y /= cga->crtc[CGA_CRTC_MAX_SCANLINE_ADDR] + 1;
video_bpp = 0;
} else if (!(cga->cgamode & 16)) {
} else if (!(cga->cgamode & CGA_MODE_FLAG_HIGHRES_GRAPHICS)) {
video_res_x /= 2;
video_bpp = 2;
} else
@@ -1579,10 +1579,10 @@ lcdc_poll(amsvid_t *vid)
}
if (cga->cgadispon)
cga->cgastat &= ~1;
if (cga->sc == (cga->crtc[10] & 31) || ((cga->crtc[8] & 3) == 3 && cga->sc == ((cga->crtc[10] & 31) >> 1)))
cga->con = 1;
if (cga->cgadispon && (cga->cgamode & 1)) {
for (x = 0; x < (cga->crtc[1] << 1); x++)
if (cga->sc == (cga->crtc[CGA_CRTC_CURSOR_START] & 31) || ((cga->crtc[CGA_CRTC_INTERLACE] & 3) == 3 && cga->sc == ((cga->crtc[CGA_CRTC_CURSOR_START] & 31) >> 1)))
cga->cursorvisible = 1;
if (cga->cgadispon && (cga->cgamode & CGA_MODE_FLAG_HIGHRES)) {
for (x = 0; x < (cga->crtc[CGA_CRTC_HDISP] << 1); x++)
cga->charbuffer[x] = cga->vram[((cga->ma << 1) + x) & 0x3fff];
}
}

View File

@@ -44,9 +44,6 @@
#include <86box/vid_cga_comp.h>
#include <86box/plat_unused.h>
static video_timings_t timing_compaq_plasma = { .type = VIDEO_ISA, .write_b = 8, .write_w = 16, .write_l = 32, .read_b = 8, .read_w = 16, .read_l = 32 };
enum {
COMPAQ_PORTABLEII = 0,
COMPAQ_PORTABLEIII,
@@ -55,752 +52,11 @@ enum {
COMPAQ_DESKPRO386_05_1988
};
#define CGA_RGB 0
#define CGA_COMPOSITE 1
/*Very rough estimate*/
#define VID_CLOCK (double) (651 * 416 * 60)
/* Mapping of attributes to colours */
static uint32_t amber;
static uint32_t black;
static uint32_t blinkcols[256][2];
static uint32_t normcols[256][2];
/* Video options set by the motherboard; they will be picked up by the card
* on the next poll.
*
* Bit 3: Disable built-in video (for add-on card)
* Bit 2: Thin font
* Bits 0,1: Font set (not currently implemented)
*/
static int8_t cpq_st_display_internal = -1;
static uint8_t mdaattr[256][2][2];
static void
compaq_plasma_display_set(uint8_t internal)
{
cpq_st_display_internal = internal;
}
typedef struct compaq_plasma_t {
cga_t cga;
mem_mapping_t font_ram_mapping;
uint8_t *font_ram;
uint8_t port_13c6;
uint8_t port_23c6;
uint8_t port_27c6;
uint8_t internal_monitor;
} compaq_plasma_t;
static int compaq_machine_type = 0;
/* Compaq Deskpro 386 remaps RAM from 0xA0000-0xFFFFF to 0xFA0000-0xFFFFFF */
static mem_mapping_t ram_mapping;
static void compaq_plasma_recalcattrs(compaq_plasma_t *self);
static void
compaq_plasma_recalctimings(compaq_plasma_t *self)
{
double _dispontime;
double _dispofftime;
double disptime;
if (!self->internal_monitor && !(self->port_23c6 & 0x01)) {
cga_recalctimings(&self->cga);
return;
}
disptime = 651;
_dispontime = 640;
_dispofftime = disptime - _dispontime;
self->cga.dispontime = (uint64_t) (_dispontime * (cpuclock / VID_CLOCK) * (double) (1ULL << 32));
self->cga.dispofftime = (uint64_t) (_dispofftime * (cpuclock / VID_CLOCK) * (double) (1ULL << 32));
}
static void
compaq_plasma_waitstates(UNUSED(void *priv))
{
int ws_array[16] = { 3, 4, 5, 6, 7, 8, 4, 5, 6, 7, 8, 4, 5, 6, 7, 8 };
int ws;
ws = ws_array[cycles & 0xf];
sub_cycles(ws);
}
static void
compaq_plasma_write(uint32_t addr, uint8_t val, void *priv)
{
compaq_plasma_t *self = (compaq_plasma_t *) priv;
if (self->port_23c6 & 0x08)
self->font_ram[addr & 0x1fff] = val;
else
self->cga.vram[addr & 0x7fff] = val;
compaq_plasma_waitstates(&self->cga);
}
static uint8_t
compaq_plasma_read(uint32_t addr, void *priv)
{
compaq_plasma_t *self = (compaq_plasma_t *) priv;
uint8_t ret;
compaq_plasma_waitstates(&self->cga);
if (self->port_23c6 & 0x08)
ret = (self->font_ram[addr & 0x1fff]);
else
ret = (self->cga.vram[addr & 0x7fff]);
return ret;
}
static void
compaq_plasma_out(uint16_t addr, uint8_t val, void *priv)
{
compaq_plasma_t *self = (compaq_plasma_t *) priv;
switch (addr) {
/* Emulated CRTC, register select */
case 0x3d0:
case 0x3d2:
case 0x3d4:
case 0x3d6:
cga_out(addr, val, &self->cga);
break;
/* Emulated CRTC, value */
case 0x3d1:
case 0x3d3:
case 0x3d5:
case 0x3d7:
cga_out(addr, val, &self->cga);
compaq_plasma_recalctimings(self);
break;
case 0x3d8:
case 0x3d9:
cga_out(addr, val, &self->cga);
break;
case 0x13c6:
self->port_13c6 = val;
compaq_plasma_display_set((self->port_13c6 & 0x08) ? 1 : 0);
/*
For bits 2-0, John gives 0 = CGA, 1 = EGA, 3 = MDA;
Another source (Ralf Brown?) gives 4 = CGA, 5 = EGA, 7 = MDA;
This leads me to believe bit 2 is not relevant to the mode.
*/
if ((val & 0x03) == 0x03)
mem_mapping_set_addr(&self->cga.mapping, 0xb0000, 0x08000);
else
mem_mapping_set_addr(&self->cga.mapping, 0xb8000, 0x08000);
break;
case 0x23c6:
self->port_23c6 = val;
compaq_plasma_recalcattrs(self);
break;
case 0x27c6:
self->port_27c6 = val;
break;
default:
break;
}
}
static uint8_t
compaq_plasma_in(uint16_t addr, void *priv)
{
compaq_plasma_t *self = (compaq_plasma_t *) priv;
uint8_t ret = 0xff;
switch (addr) {
case 0x3d4:
case 0x3da:
case 0x3db:
case 0x3dc:
ret = cga_in(addr, &self->cga);
break;
case 0x3d1:
case 0x3d3:
case 0x3d5:
case 0x3d7:
ret = cga_in(addr, &self->cga);
break;
case 0x3d8:
ret = self->cga.cgamode;
break;
case 0x13c6:
ret = self->port_13c6;
#if 0
if ((self->cga.cgamode & 0x28) == 0x00)
ret |= 0x04;
#endif
break;
case 0x17c6:
ret = 0xe6;
break;
case 0x1bc6:
ret = 0x40;
break;
case 0x23c6:
ret = self->port_23c6;
break;
case 0x27c6:
ret = self->port_27c6 & 0x3f;
break;
default:
break;
}
return ret;
}
static void
compaq_plasma_poll(void *priv)
{
compaq_plasma_t *self = (compaq_plasma_t *) priv;
uint8_t chr;
uint8_t attr;
uint8_t sc;
uint16_t ma = (self->cga.crtc[13] | (self->cga.crtc[12] << 8)) & 0x7fff;
uint16_t ca = (self->cga.crtc[15] | (self->cga.crtc[14] << 8)) & 0x7fff;
uint16_t addr;
int drawcursor;
int cursorline;
int blink = 0;
int underline = 0;
int c;
int x;
uint32_t ink = 0;
uint32_t fg = (self->cga.cgacol & 0x0f) ? amber : black;
uint32_t bg = black;
uint32_t cols[2];
uint8_t dat;
uint8_t pattern;
uint32_t ink0 = 0;
uint32_t ink1 = 0;
/* Switch between internal plasma and external CRT display. */
if ((cpq_st_display_internal != -1) && (cpq_st_display_internal != self->internal_monitor)) {
self->internal_monitor = cpq_st_display_internal;
compaq_plasma_recalctimings(self);
}
/* graphic mode and not mode 40h */
if (!self->internal_monitor && !(self->port_23c6 & 0x01)) {
/* standard cga mode */
cga_poll(&self->cga);
return;
} else {
/* mode 40h or text mode */
if (!self->cga.linepos) {
timer_advance_u64(&self->cga.timer, self->cga.dispofftime);
self->cga.cgastat |= 1;
self->cga.linepos = 1;
if (self->cga.cgadispon) {
if (self->cga.displine == 0)
video_wait_for_buffer();
/* 80-col */
if (self->cga.cgamode & 0x01) {
sc = self->cga.displine & 0x0f;
addr = ((ma & ~1) + (self->cga.displine >> 4) * 80) << 1;
ma += (self->cga.displine >> 4) * 80;
if ((self->cga.crtc[0x0a] & 0x60) == 0x20)
cursorline = 0;
else
cursorline = (((self->cga.crtc[0x0a] & 0x0f) << 1) <= sc) && (((self->cga.crtc[0x0b] & 0x0f) << 1) >= sc);
/* for each text column */
for (x = 0; x < 80; x++) {
/* video output enabled */
if (self->cga.cgamode & 0x08) {
/* character */
chr = self->cga.vram[(addr + (x << 1)) & 0x7fff];
/* text attributes */
attr = self->cga.vram[(addr + ((x << 1) + 1)) & 0x7fff];
} else {
chr = 0x00;
attr = 0x00;
}
uint8_t hi_bit = attr & 0x08;
/* check if cursor has to be drawn */
drawcursor = ((ma == ca) && cursorline && (self->cga.cgamode & 0x08) && (self->cga.cgablink & 0x10));
/* check if character underline mode should be set */
underline = ((attr & 0x07) == 0x01);
underline = underline || (((self->port_23c6 >> 5) == 2) && hi_bit);
if (underline) {
/* set forecolor to white */
attr = attr | 0x7;
}
blink = 0;
/* set foreground */
cols[1] = blinkcols[attr][1];
/* blink active */
if (self->cga.cgamode & 0x20) {
cols[0] = blinkcols[attr][0];
/* attribute 7 active and not cursor */
if ((self->cga.cgablink & 0x08) && (attr & 0x80) && !drawcursor) {
/* set blinking */
cols[1] = cols[0];
blink = 1;
}
} else {
/* Set intensity bit */
cols[1] = normcols[attr][1];
cols[0] = normcols[attr][0];
}
/* character address */
uint16_t chr_addr = ((chr * 16) + sc) & 0x0fff;
if (((self->port_23c6 >> 5) == 3) && hi_bit)
chr_addr |= 0x1000;
/* character underline active and 7th row of pixels in character height being drawn */
if (underline && (sc == 7)) {
/* for each pixel in character width */
for (c = 0; c < 8; c++)
buffer32->line[self->cga.displine][(x << 3) + c] = mdaattr[attr][blink][1];
} else if (drawcursor) {
for (c = 0; c < 8; c++)
buffer32->line[self->cga.displine][(x << 3) + c] = cols[(self->font_ram[chr_addr] & (1 << (c ^ 7))) ? 1 : 0] ^ (amber ^ black);
} else {
for (c = 0; c < 8; c++)
buffer32->line[self->cga.displine][(x << 3) + c] = cols[(self->font_ram[chr_addr] & (1 << (c ^ 7))) ? 1 : 0];
}
if (hi_bit) {
if ((self->port_23c6 >> 5) == 1) {
for (c = 0; c < 8; c++)
buffer32->line[self->cga.displine][(x << 3) + c] ^= (amber ^ black);
} else if ((self->port_23c6 >> 5) == 4) {
for (c = 0; c < 8; c++) {
uint32_t b = ((buffer32->line[self->cga.displine][(x << 3) + c]) >> 1) & 0x7f;
uint32_t g = ((buffer32->line[self->cga.displine][(x << 3) + c]) >> 9) & 0x7f;
uint32_t r = ((buffer32->line[self->cga.displine][(x << 3) + c]) >> 17) & 0x7f;
buffer32->line[self->cga.displine][(x << 3) + c] = b | (g << 8) || (r << 16);
}
}
}
ma++;
}
}
/* 40-col */
else if (!(self->cga.cgamode & 0x02)) {
sc = self->cga.displine & 0x0f;
addr = ((ma & ~1) + (self->cga.displine >> 4) * 40) << 1;
ma += (self->cga.displine >> 4) * 40;
if ((self->cga.crtc[0x0a] & 0x60) == 0x20)
cursorline = 0;
else
cursorline = (((self->cga.crtc[0x0a] & 0x0f) << 1) <= sc) && (((self->cga.crtc[0x0b] & 0x0f) << 1) >= sc);
for (x = 0; x < 40; x++) {
/* video output enabled */
if (self->cga.cgamode & 0x08) {
/* character */
chr = self->cga.vram[(addr + (x << 1)) & 0x7fff];
/* text attributes */
attr = self->cga.vram[(addr + ((x << 1) + 1)) & 0x7fff];
} else {
chr = 0x00;
attr = 0x00;
}
uint8_t hi_bit = attr & 0x08;
/* check if cursor has to be drawn */
drawcursor = ((ma == ca) && cursorline && (self->cga.cgamode & 0x08) && (self->cga.cgablink & 0x10));
/* check if character underline mode should be set */
underline = ((attr & 0x07) == 0x01);
underline = underline || (((self->port_23c6 >> 5) == 2) && hi_bit);
if (underline) {
/* set forecolor to white */
attr = attr | 0x7;
}
blink = 0;
/* set foreground */
cols[1] = blinkcols[attr][1];
/* blink active */
if (self->cga.cgamode & 0x20) {
cols[0] = blinkcols[attr][0];
/* attribute 7 active and not cursor */
if ((self->cga.cgablink & 0x08) && (attr & 0x80) && !drawcursor) {
/* set blinking */
cols[1] = cols[0];
blink = 1;
}
} else {
/* Set intensity bit */
cols[1] = normcols[attr][1];
cols[0] = normcols[attr][0];
}
/* character address */
uint16_t chr_addr = ((chr * 16) + sc) & 0x0fff;
if (((self->port_23c6 >> 5) == 3) && hi_bit)
chr_addr |= 0x1000;
/* character underline active and 7th row of pixels in character height being drawn */
if (underline && (sc == 7)) {
/* for each pixel in character width */
for (c = 0; c < 8; c++)
buffer32->line[self->cga.displine][(x << 4) + (c << 1)] = buffer32->line[self->cga.displine][(x << 4) + (c << 1) + 1] = mdaattr[attr][blink][1];
} else if (drawcursor) {
for (c = 0; c < 8; c++)
buffer32->line[self->cga.displine][(x << 4) + (c << 1)] = buffer32->line[self->cga.displine][(x << 4) + (c << 1) + 1] = cols[(self->font_ram[chr_addr] & (1 << (c ^ 7))) ? 1 : 0] ^ (amber ^ black);
} else {
for (c = 0; c < 8; c++)
buffer32->line[self->cga.displine][(x << 4) + (c << 1)] = buffer32->line[self->cga.displine][(x << 4) + (c << 1) + 1] = cols[(self->font_ram[chr_addr] & (1 << (c ^ 7))) ? 1 : 0];
}
if (hi_bit) {
if ((self->port_23c6 >> 5) == 1)
for (c = 0; c < 8; c++) {
buffer32->line[self->cga.displine][(x << 4) + (c << 1)] ^= (amber ^ black);
buffer32->line[self->cga.displine][(x << 4) + (c << 1) + 1] ^= (amber ^ black);
}
else if ((self->port_23c6 >> 5) == 4)
for (c = 0; c < 8; c++) {
uint32_t b = ((buffer32->line[self->cga.displine][(x << 4) + (c << 1)]) >> 1) & 0x7f;
uint32_t g = ((buffer32->line[self->cga.displine][(x << 4) + (c << 1)]) >> 9) & 0x7f;
uint32_t r = ((buffer32->line[self->cga.displine][(x << 4) + (c << 1)]) >> 17) & 0x7f;
buffer32->line[self->cga.displine][(x << 4) + (c << 1)] = buffer32->line[self->cga.displine][(x << 4) + (c << 1) + 1] = b | (g << 8) || (r << 16);
}
}
ma++;
}
} else {
if (self->cga.cgamode & 0x10) {
/* 640x400 mode */
if (self->port_23c6 & 0x01) /* 640*400 */ {
addr = ((self->cga.displine) & 1) * 0x2000 + ((self->cga.displine >> 1) & 1) * 0x4000 + (self->cga.displine >> 2) * 80 + ((ma & ~1) << 1);
} else {
addr = ((self->cga.displine >> 1) & 1) * 0x2000 + (self->cga.displine >> 2) * 80 + ((ma & ~1) << 1);
}
for (uint8_t x = 0; x < 80; x++) {
dat = self->cga.vram[addr & 0x7fff];
addr++;
for (uint8_t c = 0; c < 8; c++) {
ink = (dat & 0x80) ? fg : bg;
if (!(self->cga.cgamode & 0x08))
ink = black;
buffer32->line[self->cga.displine][(x << 3) + c] = ink;
dat <<= 1;
}
}
} else {
addr = ((self->cga.displine >> 1) & 1) * 0x2000 + (self->cga.displine >> 2) * 80 + ((ma & ~1) << 1);
for (uint8_t x = 0; x < 80; x++) {
dat = self->cga.vram[addr & 0x7fff];
addr++;
for (uint8_t c = 0; c < 4; c++) {
pattern = (dat & 0xC0) >> 6;
if (!(self->cga.cgamode & 0x08))
pattern = 0;
switch (pattern & 3) {
case 0:
ink0 = ink1 = black;
break;
case 1:
if (self->cga.displine & 0x01) {
ink0 = black;
ink1 = black;
} else {
ink0 = amber;
ink1 = black;
}
break;
case 2:
if (self->cga.displine & 0x01) {
ink0 = black;
ink1 = amber;
} else {
ink0 = amber;
ink1 = black;
}
break;
case 3:
ink0 = ink1 = amber;
break;
default:
break;
}
buffer32->line[self->cga.displine][(x << 3) + (c << 1)] = ink0;
buffer32->line[self->cga.displine][(x << 3) + (c << 1) + 1] = ink1;
dat <<= 2;
}
}
}
}
}
self->cga.displine++;
/* Hardcode a fixed refresh rate and VSYNC timing */
if (self->cga.displine == 400) { /* Start of VSYNC */
self->cga.cgastat |= 8;
self->cga.cgadispon = 0;
}
if (self->cga.displine == 416) { /* End of VSYNC */
self->cga.displine = 0;
self->cga.cgastat &= ~8;
self->cga.cgadispon = 1;
}
} else {
timer_advance_u64(&self->cga.timer, self->cga.dispontime);
if (self->cga.cgadispon)
self->cga.cgastat &= ~1;
self->cga.linepos = 0;
if (self->cga.displine == 400) {
xsize = 640;
ysize = 400;
if ((self->cga.cgamode & 0x08) || video_force_resize_get()) {
set_screen_size(xsize, ysize);
if (video_force_resize_get())
video_force_resize_set(0);
}
/* Plasma specific */
video_blit_memtoscreen(0, 0, xsize, ysize);
frames++;
/* Fixed 640x400 resolution */
video_res_x = 640;
video_res_y = 400;
if (self->cga.cgamode & 0x02) {
if (self->cga.cgamode & 0x10)
video_bpp = 1;
else
video_bpp = 2;
} else
video_bpp = 0;
self->cga.cgablink++;
}
}
}
}
static void
compaq_plasma_mdaattr_rebuild(void)
{
for (uint16_t c = 0; c < 256; c++) {
mdaattr[c][0][0] = mdaattr[c][1][0] = mdaattr[c][1][1] = 16;
if (c & 8)
mdaattr[c][0][1] = 15 + 16;
else
mdaattr[c][0][1] = 7 + 16;
}
mdaattr[0x70][0][1] = 16;
mdaattr[0x70][0][0] = mdaattr[0x70][1][0] = mdaattr[0x70][1][1] = 16 + 15;
mdaattr[0xF0][0][1] = 16;
mdaattr[0xF0][0][0] = mdaattr[0xF0][1][0] = mdaattr[0xF0][1][1] = 16 + 15;
mdaattr[0x78][0][1] = 16 + 7;
mdaattr[0x78][0][0] = mdaattr[0x78][1][0] = mdaattr[0x78][1][1] = 16 + 15;
mdaattr[0xF8][0][1] = 16 + 7;
mdaattr[0xF8][0][0] = mdaattr[0xF8][1][0] = mdaattr[0xF8][1][1] = 16 + 15;
mdaattr[0x00][0][1] = mdaattr[0x00][1][1] = 16;
mdaattr[0x08][0][1] = mdaattr[0x08][1][1] = 16;
mdaattr[0x80][0][1] = mdaattr[0x80][1][1] = 16;
mdaattr[0x88][0][1] = mdaattr[0x88][1][1] = 16;
}
static void
compaq_plasma_recalcattrs(compaq_plasma_t *self)
{
int n;
/* val behaves as follows:
* Bit 0: Attributes 01-06, 08-0E are inverse video
* Bit 1: Attributes 01-06, 08-0E are bold
* Bit 2: Attributes 11-16, 18-1F, 21-26, 28-2F ... F1-F6, F8-FF
* are inverse video
* Bit 3: Attributes 11-16, 18-1F, 21-26, 28-2F ... F1-F6, F8-FF
* are bold */
/* Set up colours */
amber = makecol(0xff, 0x7d, 0x00);
black = makecol(0x64, 0x19, 0x00);
/* Initialize the attribute mapping. Start by defaulting everything
* to black on amber, and with bold set by bit 3 */
for (n = 0; n < 256; n++) {
blinkcols[n][0] = normcols[n][0] = amber;
blinkcols[n][1] = normcols[n][1] = black;
}
/* Colours 0x11-0xFF are controlled by bits 2 and 3 of the
* passed value. Exclude x0 and x8, which are always black on
* amber. */
for (n = 0x11; n <= 0xFF; n++) {
if ((n & 7) == 0)
continue;
if ((self->port_23c6 >> 5) == 1) { /* Inverse */
blinkcols[n][0] = normcols[n][0] = amber;
blinkcols[n][1] = normcols[n][1] = black;
} else { /* Normal */
blinkcols[n][0] = normcols[n][0] = black;
blinkcols[n][1] = normcols[n][1] = amber;
}
}
/* Set up the 01-0E range, controlled by bits 0 and 1 of the
* passed value. When blinking is enabled this also affects 81-8E. */
for (n = 0x01; n <= 0x0E; n++) {
if (n == 7)
continue;
if ((self->port_23c6 >> 5) == 1) {
blinkcols[n][0] = normcols[n][0] = amber;
blinkcols[n][1] = normcols[n][1] = black;
blinkcols[n + 128][0] = amber;
blinkcols[n + 128][1] = black;
} else {
blinkcols[n][0] = normcols[n][0] = black;
blinkcols[n][1] = normcols[n][1] = amber;
blinkcols[n + 128][0] = black;
blinkcols[n + 128][1] = amber;
}
}
/* Colours 07 and 0F are always amber on black. If blinking is
* enabled so are 87 and 8F. */
for (n = 0x07; n <= 0x0F; n += 8) {
blinkcols[n][0] = normcols[n][0] = black;
blinkcols[n][1] = normcols[n][1] = amber;
blinkcols[n + 128][0] = black;
blinkcols[n + 128][1] = amber;
}
/* When not blinking, colours 81-8F are always amber on black. */
for (n = 0x81; n <= 0x8F; n++) {
normcols[n][0] = black;
normcols[n][1] = amber;
}
/* Finally do the ones which are solid black. These differ between
* the normal and blinking mappings */
for (n = 0; n <= 0xFF; n += 0x11)
normcols[n][0] = normcols[n][1] = black;
/* In the blinking range, 00 11 22 .. 77 and 80 91 A2 .. F7 are black */
for (n = 0; n <= 0x77; n += 0x11) {
blinkcols[n][0] = blinkcols[n][1] = black;
blinkcols[n + 128][0] = blinkcols[n + 128][1] = black;
}
}
static void *
compaq_plasma_init(UNUSED(const device_t *info))
{
compaq_plasma_t *self = calloc(1, sizeof(compaq_plasma_t));
cga_init(&self->cga);
video_inform(VIDEO_FLAG_TYPE_CGA, &timing_compaq_plasma);
self->cga.composite = 0;
self->cga.revision = 0;
self->cga.vram = malloc(0x8000);
self->internal_monitor = 1;
self->font_ram = malloc(0x2000);
cga_comp_init(self->cga.revision);
timer_set_callback(&self->cga.timer, compaq_plasma_poll);
timer_set_p(&self->cga.timer, self);
mem_mapping_add(&self->cga.mapping, 0xb8000, 0x08000,
compaq_plasma_read, NULL, NULL,
compaq_plasma_write, NULL, NULL,
NULL, MEM_MAPPING_EXTERNAL, self);
for (int i = 1; i <= 2; i++) {
io_sethandler(0x03c6 + (i << 12), 0x0001, compaq_plasma_in, NULL, NULL, compaq_plasma_out, NULL, NULL, self);
io_sethandler(0x07c6 + (i << 12), 0x0001, compaq_plasma_in, NULL, NULL, compaq_plasma_out, NULL, NULL, self);
io_sethandler(0x0bc6 + (i << 12), 0x0001, compaq_plasma_in, NULL, NULL, compaq_plasma_out, NULL, NULL, self);
}
io_sethandler(0x03d0, 0x0010, compaq_plasma_in, NULL, NULL, compaq_plasma_out, NULL, NULL, self);
overscan_x = overscan_y = 16;
self->cga.rgb_type = device_get_config_int("rgb_type");
cga_palette = (self->cga.rgb_type << 1);
cgapal_rebuild();
compaq_plasma_mdaattr_rebuild();
return self;
}
static void
compaq_plasma_close(void *priv)
{
compaq_plasma_t *self = (compaq_plasma_t *) priv;
free(self->cga.vram);
free(self->font_ram);
free(self);
}
static void
compaq_plasma_speed_changed(void *priv)
{
compaq_plasma_t *self = (compaq_plasma_t *) priv;
compaq_plasma_recalctimings(self);
}
const device_config_t compaq_plasma_config[] = {
// clang-format off
{
.name = "rgb_type",
.description = "RGB type",
.type = CONFIG_SELECTION,
.default_string = "",
.default_int = 0,
.file_filter = "",
.spinner = { 0 },
.selection = {
{ .description = "Color", .value = 0 },
{ .description = "Green Monochrome", .value = 1 },
{ .description = "Amber Monochrome", .value = 2 },
{ .description = "Gray Monochrome", .value = 3 },
{ .description = "" }
}
},
{ .name = "", .description = "", .type = CONFIG_END }
// clang-format on
};
const device_t compaq_plasma_device = {
.name = "Compaq Plasma",
.internal_name = "compaq_plasma",
.flags = 0,
.local = 0,
.init = compaq_plasma_init,
.close = compaq_plasma_close,
.reset = NULL,
.available = NULL,
.speed_changed = compaq_plasma_speed_changed,
.force_redraw = NULL,
.config = compaq_plasma_config
};
static uint8_t
read_ram(uint32_t addr, UNUSED(void *priv))

View File

@@ -1,717 +0,0 @@
/*
* 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.
*
* This file is part of the 86Box distribution.
*
* Implementation of the Toshiba 3100e plasma display.
* This display has a fixed 640x400 resolution.
*
* T3100e CRTC regs (from the ROM):
*
* Selecting a character height of 3 seems to be sufficient to
* convert the 640x200 graphics mode to 640x400 (and, by
* analogy, 320x200 to 320x400).
*
* Horiz-----> Vert------> I ch
* 38 28 2D 0A 1F 06 19 1C 02 07 06 07 CO40
* 71 50 5A 0A 1F 06 19 1C 02 07 06 07 CO80
* 38 28 2D 0A 7F 06 64 70 02 01 06 07 Graphics
* 61 50 52 0F 19 06 19 19 02 0D 0B 0C MONO
* 2D 28 22 0A 67 00 64 67 02 03 06 07 640x400
*
*
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
* John Elliott, <jce@seasip.info>
*
* Copyright 2017-2019 Fred N. van Kempen.
* Copyright 2016-2019 Miran Grca.
* Copyright 2008-2019 John Elliott.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/io.h>
#include <86box/mem.h>
#include <86box/timer.h>
#include "cpu.h"
#include <86box/video.h>
#include <86box/vid_cga.h>
#include <86box/m_at_t3100e.h>
#include <86box/plat_unused.h>
#define T3100E_XSIZE 640
#define T3100E_YSIZE 400
/*Very rough estimate*/
#define VID_CLOCK (double) (651 * 416 * 60)
/* Mapping of attributes to colours */
static uint32_t amber;
static uint32_t black;
static uint8_t boldcols[256]; /* Which attributes use the bold font */
static uint32_t blinkcols[256][2];
static uint32_t normcols[256][2];
/* Video options set by the motherboard; they will be picked up by the card
* on the next poll.
*
* Bit 3: Disable built-in video (for add-on card)
* Bit 2: Thin font
* Bits 0,1: Font set (not currently implemented)
*/
static uint8_t st_video_options;
static int8_t st_display_internal = -1;
void
t3100e_video_options_set(uint8_t options)
{
st_video_options = options;
}
void
t3100e_display_set(uint8_t internal)
{
st_display_internal = internal;
}
uint8_t
t3100e_display_get(void)
{
return st_display_internal;
}
typedef struct t3100e_t {
mem_mapping_t mapping;
cga_t cga; /* The CGA is used for the external
* display; most of its registers are
* ignored by the plasma display. */
int font; /* Current font, 0-3 */
int enabled; /* Hardware enabled, 0 or 1 */
int internal; /* Using internal display? */
uint8_t attrmap; /* Attribute mapping register */
uint64_t dispontime, dispofftime;
int linepos, displine;
int vc;
int dispon;
int vsynctime;
uint8_t video_options;
uint8_t *vram;
} t3100e_t;
static video_timings_t timing_t3100e = { VIDEO_ISA, 8, 16, 32, 8, 16, 32 };
void t3100e_recalctimings(t3100e_t *t3100e);
void t3100e_write(uint32_t addr, uint8_t val, void *priv);
uint8_t t3100e_read(uint32_t addr, void *priv);
void t3100e_recalcattrs(t3100e_t *t3100e);
void
t3100e_out(uint16_t addr, uint8_t val, void *priv)
{
t3100e_t *t3100e = (t3100e_t *) priv;
switch (addr) {
/* Emulated CRTC, register select */
case 0x3d0:
case 0x3d2:
case 0x3d4:
case 0x3d6:
cga_out(addr, val, &t3100e->cga);
break;
/* Emulated CRTC, value */
case 0x3d1:
case 0x3d3:
case 0x3d5:
case 0x3d7:
/* Register 0x12 controls the attribute mappings for the
* plasma screen. */
if (t3100e->cga.crtcreg == 0x12) {
t3100e->attrmap = val;
t3100e_recalcattrs(t3100e);
return;
}
cga_out(addr, val, &t3100e->cga);
t3100e_recalctimings(t3100e);
return;
case 0x3D8: /* CGA control register */
case 0x3D9: /* CGA colour register */
cga_out(addr, val, &t3100e->cga);
return;
default:
break;
}
}
uint8_t
t3100e_in(uint16_t addr, void *priv)
{
t3100e_t *t3100e = (t3100e_t *) priv;
uint8_t val;
switch (addr) {
case 0x3d1:
case 0x3d3:
case 0x3d5:
case 0x3d7:
if (t3100e->cga.crtcreg == 0x12) {
val = t3100e->attrmap & 0x0F;
if (t3100e->internal)
val |= 0x30; /* Plasma / CRT */
return val;
}
break;
default:
break;
}
return cga_in(addr, &t3100e->cga);
}
void
t3100e_write(uint32_t addr, uint8_t val, void *priv)
{
t3100e_t *t3100e = (t3100e_t *) priv;
t3100e->vram[addr & 0x7fff] = val;
cycles -= 4;
}
uint8_t
t3100e_read(uint32_t addr, void *priv)
{
const t3100e_t *t3100e = (t3100e_t *) priv;
cycles -= 4;
return t3100e->vram[addr & 0x7fff];
}
void
t3100e_recalctimings(t3100e_t *t3100e)
{
double disptime;
double _dispontime;
double _dispofftime;
if (!t3100e->internal) {
cga_recalctimings(&t3100e->cga);
return;
}
disptime = 651;
_dispontime = 640;
_dispofftime = disptime - _dispontime;
t3100e->dispontime = (uint64_t) (_dispontime * (cpuclock / VID_CLOCK) * (double) (1ULL << 32));
t3100e->dispofftime = (uint64_t) (_dispofftime * (cpuclock / VID_CLOCK) * (double) (1ULL << 32));
}
/* Draw a row of text in 80-column mode */
void
t3100e_text_row80(t3100e_t *t3100e)
{
uint32_t cols[2];
uint8_t chr;
uint8_t attr;
int drawcursor;
int cursorline;
int bold;
int blink;
uint16_t addr;
uint8_t sc;
uint16_t ma = (t3100e->cga.crtc[13] | (t3100e->cga.crtc[12] << 8)) & 0x7fff;
uint16_t ca = (t3100e->cga.crtc[15] | (t3100e->cga.crtc[14] << 8)) & 0x7fff;
sc = (t3100e->displine) & 15;
addr = ((ma & ~1) + (t3100e->displine >> 4) * 80) * 2;
ma += (t3100e->displine >> 4) * 80;
if ((t3100e->cga.crtc[10] & 0x60) == 0x20) {
cursorline = 0;
} else {
cursorline = ((t3100e->cga.crtc[10] & 0x0F) * 2 <= sc) && ((t3100e->cga.crtc[11] & 0x0F) * 2 >= sc);
}
for (uint8_t x = 0; x < 80; x++) {
chr = t3100e->vram[(addr + 2 * x) & 0x7FFF];
attr = t3100e->vram[(addr + 2 * x + 1) & 0x7FFF];
drawcursor = ((ma == ca) && cursorline && (t3100e->cga.cgamode & 8) && (t3100e->cga.cgablink & 16));
blink = ((t3100e->cga.cgablink & 16) && (t3100e->cga.cgamode & 0x20) && (attr & 0x80) && !drawcursor);
if (t3100e->video_options & 4)
bold = boldcols[attr] ? chr + 256 : chr;
else
bold = boldcols[attr] ? chr : chr + 256;
bold += 512 * (t3100e->video_options & 3);
if (t3100e->cga.cgamode & 0x20) /* Blink */
{
cols[1] = blinkcols[attr][1];
cols[0] = blinkcols[attr][0];
if (blink)
cols[1] = cols[0];
} else {
cols[1] = normcols[attr][1];
cols[0] = normcols[attr][0];
}
if (drawcursor) {
for (uint8_t c = 0; c < 8; c++) {
(buffer32->line[t3100e->displine])[(x << 3) + c] = cols[(fontdatm[bold][sc] & (1 << (c ^ 7))) ? 1 : 0] ^ (amber ^ black);
}
} else {
for (uint8_t c = 0; c < 8; c++)
(buffer32->line[t3100e->displine])[(x << 3) + c] = cols[(fontdatm[bold][sc] & (1 << (c ^ 7))) ? 1 : 0];
}
++ma;
}
}
/* Draw a row of text in 40-column mode */
void
t3100e_text_row40(t3100e_t *t3100e)
{
uint32_t cols[2];
int c;
uint8_t chr;
uint8_t attr;
int drawcursor;
int cursorline;
int bold;
int blink;
uint16_t addr;
uint8_t sc;
uint16_t ma = (t3100e->cga.crtc[13] | (t3100e->cga.crtc[12] << 8)) & 0x7fff;
uint16_t ca = (t3100e->cga.crtc[15] | (t3100e->cga.crtc[14] << 8)) & 0x7fff;
sc = (t3100e->displine) & 15;
addr = ((ma & ~1) + (t3100e->displine >> 4) * 40) * 2;
ma += (t3100e->displine >> 4) * 40;
if ((t3100e->cga.crtc[10] & 0x60) == 0x20) {
cursorline = 0;
} else {
cursorline = ((t3100e->cga.crtc[10] & 0x0F) * 2 <= sc) && ((t3100e->cga.crtc[11] & 0x0F) * 2 >= sc);
}
for (uint8_t x = 0; x < 40; x++) {
chr = t3100e->vram[(addr + 2 * x) & 0x7FFF];
attr = t3100e->vram[(addr + 2 * x + 1) & 0x7FFF];
drawcursor = ((ma == ca) && cursorline && (t3100e->cga.cgamode & 8) && (t3100e->cga.cgablink & 16));
blink = ((t3100e->cga.cgablink & 16) && (t3100e->cga.cgamode & 0x20) && (attr & 0x80) && !drawcursor);
if (t3100e->video_options & 4)
bold = boldcols[attr] ? chr + 256 : chr;
else
bold = boldcols[attr] ? chr : chr + 256;
bold += 512 * (t3100e->video_options & 3);
if (t3100e->cga.cgamode & 0x20) /* Blink */
{
cols[1] = blinkcols[attr][1];
cols[0] = blinkcols[attr][0];
if (blink)
cols[1] = cols[0];
} else {
cols[1] = normcols[attr][1];
cols[0] = normcols[attr][0];
}
if (drawcursor) {
for (c = 0; c < 8; c++) {
(buffer32->line[t3100e->displine])[(x << 4) + c * 2] = (buffer32->line[t3100e->displine])[(x << 4) + c * 2 + 1] = cols[(fontdatm[bold][sc] & (1 << (c ^ 7))) ? 1 : 0] ^ (amber ^ black);
}
} else {
for (c = 0; c < 8; c++) {
(buffer32->line[t3100e->displine])[(x << 4) + c * 2] = (buffer32->line[t3100e->displine])[(x << 4) + c * 2 + 1] = cols[(fontdatm[bold][sc] & (1 << (c ^ 7))) ? 1 : 0];
}
}
++ma;
}
}
/* Draw a line in CGA 640x200 or T3100e 640x400 mode */
void
t3100e_cgaline6(t3100e_t *t3100e)
{
uint8_t dat;
uint32_t ink = 0;
uint16_t addr;
uint32_t fg = (t3100e->cga.cgacol & 0x0F) ? amber : black;
uint32_t bg = black;
uint16_t ma = (t3100e->cga.crtc[13] | (t3100e->cga.crtc[12] << 8)) & 0x7fff;
if (t3100e->cga.crtc[9] == 3) /* 640*400 */
{
addr = ((t3100e->displine) & 1) * 0x2000 + ((t3100e->displine >> 1) & 1) * 0x4000 + (t3100e->displine >> 2) * 80 + ((ma & ~1) << 1);
} else {
addr = ((t3100e->displine >> 1) & 1) * 0x2000 + (t3100e->displine >> 2) * 80 + ((ma & ~1) << 1);
}
for (uint8_t x = 0; x < 80; x++) {
dat = t3100e->vram[addr & 0x7FFF];
addr++;
for (uint8_t c = 0; c < 8; c++) {
ink = (dat & 0x80) ? fg : bg;
if (!(t3100e->cga.cgamode & 8))
ink = black;
(buffer32->line[t3100e->displine])[x * 8 + c] = ink;
dat = dat << 1;
}
}
}
/* Draw a line in CGA 320x200 mode. Here the CGA colours are converted to
* dither patterns: colour 1 to 25% grey, colour 2 to 50% grey */
void
t3100e_cgaline4(t3100e_t *t3100e)
{
uint8_t dat;
uint8_t pattern;
uint32_t ink0 = 0;
uint32_t ink1 = 0;
uint16_t addr;
uint16_t ma = (t3100e->cga.crtc[13] | (t3100e->cga.crtc[12] << 8)) & 0x7fff;
if (t3100e->cga.crtc[9] == 3) /* 320*400 undocumented */
{
addr = ((t3100e->displine) & 1) * 0x2000 + ((t3100e->displine >> 1) & 1) * 0x4000 + (t3100e->displine >> 2) * 80 + ((ma & ~1) << 1);
} else /* 320*200 */
{
addr = ((t3100e->displine >> 1) & 1) * 0x2000 + (t3100e->displine >> 2) * 80 + ((ma & ~1) << 1);
}
for (uint8_t x = 0; x < 80; x++) {
dat = t3100e->vram[addr & 0x7FFF];
addr++;
for (uint8_t c = 0; c < 4; c++) {
pattern = (dat & 0xC0) >> 6;
if (!(t3100e->cga.cgamode & 8))
pattern = 0;
switch (pattern & 3) {
case 0:
ink0 = ink1 = black;
break;
case 1:
if (t3100e->displine & 1) {
ink0 = black;
ink1 = black;
} else {
ink0 = amber;
ink1 = black;
}
break;
case 2:
if (t3100e->displine & 1) {
ink0 = black;
ink1 = amber;
} else {
ink0 = amber;
ink1 = black;
}
break;
case 3:
ink0 = ink1 = amber;
break;
default:
break;
}
(buffer32->line[t3100e->displine])[x * 8 + 2 * c] = ink0;
(buffer32->line[t3100e->displine])[x * 8 + 2 * c + 1] = ink1;
dat = dat << 2;
}
}
}
void
t3100e_poll(void *priv)
{
t3100e_t *t3100e = (t3100e_t *) priv;
if (t3100e->video_options != st_video_options) {
t3100e->video_options = st_video_options;
if (t3100e->video_options & 8) /* Disable internal CGA */
mem_mapping_disable(&t3100e->mapping);
else
mem_mapping_enable(&t3100e->mapping);
/* Set the font used for the external display */
t3100e->cga.fontbase = (512 * (t3100e->video_options & 3))
+ ((t3100e->video_options & 4) ? 256 : 0);
}
/* Switch between internal plasma and external CRT display. */
if (st_display_internal != -1 && st_display_internal != t3100e->internal) {
t3100e->internal = st_display_internal;
t3100e_recalctimings(t3100e);
}
if (!t3100e->internal) {
cga_poll(&t3100e->cga);
return;
}
if (!t3100e->linepos) {
timer_advance_u64(&t3100e->cga.timer, t3100e->dispofftime);
t3100e->cga.cgastat |= 1;
t3100e->linepos = 1;
if (t3100e->dispon) {
if (t3100e->displine == 0) {
video_wait_for_buffer();
}
/* Graphics */
if (t3100e->cga.cgamode & 0x02) {
if (t3100e->cga.cgamode & 0x10)
t3100e_cgaline6(t3100e);
else
t3100e_cgaline4(t3100e);
} else if (t3100e->cga.cgamode & 0x01) /* High-res text */
{
t3100e_text_row80(t3100e);
} else {
t3100e_text_row40(t3100e);
}
}
t3100e->displine++;
/* Hardcode a fixed refresh rate and VSYNC timing */
if (t3100e->displine == 400) /* Start of VSYNC */
{
t3100e->cga.cgastat |= 8;
t3100e->dispon = 0;
}
if (t3100e->displine == 416) /* End of VSYNC */
{
t3100e->displine = 0;
t3100e->cga.cgastat &= ~8;
t3100e->dispon = 1;
}
} else {
if (t3100e->dispon) {
t3100e->cga.cgastat &= ~1;
}
timer_advance_u64(&t3100e->cga.timer, t3100e->dispontime);
t3100e->linepos = 0;
if (t3100e->displine == 400) {
/* Hardcode 640x400 window size */
if ((T3100E_XSIZE != xsize) || (T3100E_YSIZE != ysize) || video_force_resize_get()) {
xsize = T3100E_XSIZE;
ysize = T3100E_YSIZE;
if (xsize < 64)
xsize = 656;
if (ysize < 32)
ysize = 200;
set_screen_size(xsize, ysize);
if (video_force_resize_get())
video_force_resize_set(0);
}
video_blit_memtoscreen(0, 0, xsize, ysize);
frames++;
/* Fixed 640x400 resolution */
video_res_x = T3100E_XSIZE;
video_res_y = T3100E_YSIZE;
if (t3100e->cga.cgamode & 0x02) {
if (t3100e->cga.cgamode & 0x10)
video_bpp = 1;
else
video_bpp = 2;
} else
video_bpp = 0;
t3100e->cga.cgablink++;
}
}
}
void
t3100e_recalcattrs(t3100e_t *t3100e)
{
int n;
/* val behaves as follows:
* Bit 0: Attributes 01-06, 08-0E are inverse video
* Bit 1: Attributes 01-06, 08-0E are bold
* Bit 2: Attributes 11-16, 18-1F, 21-26, 28-2F ... F1-F6, F8-FF
* are inverse video
* Bit 3: Attributes 11-16, 18-1F, 21-26, 28-2F ... F1-F6, F8-FF
* are bold */
/* Set up colours */
amber = makecol(0xf7, 0x7C, 0x34);
black = makecol(0x17, 0x0C, 0x00);
/* Initialise the attribute mapping. Start by defaulting everything
* to black on amber, and with bold set by bit 3 */
for (n = 0; n < 256; n++) {
boldcols[n] = (n & 8) != 0;
blinkcols[n][0] = normcols[n][0] = amber;
blinkcols[n][1] = normcols[n][1] = black;
}
/* Colours 0x11-0xFF are controlled by bits 2 and 3 of the
* passed value. Exclude x0 and x8, which are always black on
* amber. */
for (n = 0x11; n <= 0xFF; n++) {
if ((n & 7) == 0)
continue;
if (t3100e->attrmap & 4) /* Inverse */
{
blinkcols[n][0] = normcols[n][0] = amber;
blinkcols[n][1] = normcols[n][1] = black;
} else /* Normal */
{
blinkcols[n][0] = normcols[n][0] = black;
blinkcols[n][1] = normcols[n][1] = amber;
}
if (t3100e->attrmap & 8)
boldcols[n] = 1; /* Bold */
}
/* Set up the 01-0E range, controlled by bits 0 and 1 of the
* passed value. When blinking is enabled this also affects 81-8E. */
for (n = 0x01; n <= 0x0E; n++) {
if (n == 7)
continue;
if (t3100e->attrmap & 1) {
blinkcols[n][0] = normcols[n][0] = amber;
blinkcols[n][1] = normcols[n][1] = black;
blinkcols[n + 128][0] = amber;
blinkcols[n + 128][1] = black;
} else {
blinkcols[n][0] = normcols[n][0] = black;
blinkcols[n][1] = normcols[n][1] = amber;
blinkcols[n + 128][0] = black;
blinkcols[n + 128][1] = amber;
}
if (t3100e->attrmap & 2)
boldcols[n] = 1;
}
/* Colours 07 and 0F are always amber on black. If blinking is
* enabled so are 87 and 8F. */
for (n = 0x07; n <= 0x0F; n += 8) {
blinkcols[n][0] = normcols[n][0] = black;
blinkcols[n][1] = normcols[n][1] = amber;
blinkcols[n + 128][0] = black;
blinkcols[n + 128][1] = amber;
}
/* When not blinking, colours 81-8F are always amber on black. */
for (n = 0x81; n <= 0x8F; n++) {
normcols[n][0] = black;
normcols[n][1] = amber;
boldcols[n] = (n & 0x08) != 0;
}
/* Finally do the ones which are solid black. These differ between
* the normal and blinking mappings */
for (n = 0; n <= 0xFF; n += 0x11) {
normcols[n][0] = normcols[n][1] = black;
}
/* In the blinking range, 00 11 22 .. 77 and 80 91 A2 .. F7 are black */
for (n = 0; n <= 0x77; n += 0x11) {
blinkcols[n][0] = blinkcols[n][1] = black;
blinkcols[n + 128][0] = blinkcols[n + 128][1] = black;
}
}
void *
t3100e_init(UNUSED(const device_t *info))
{
t3100e_t *t3100e = calloc(1, sizeof(t3100e_t));
loadfont("roms/machines/t3100e/t3100e_font.bin", 5);
cga_init(&t3100e->cga);
video_inform(VIDEO_FLAG_TYPE_CGA, &timing_t3100e);
t3100e->internal = 1;
/* 32k video RAM */
t3100e->vram = malloc(0x8000);
timer_set_callback(&t3100e->cga.timer, t3100e_poll);
timer_set_p(&t3100e->cga.timer, t3100e);
/* Occupy memory between 0xB8000 and 0xBFFFF */
mem_mapping_add(&t3100e->mapping, 0xb8000, 0x8000, t3100e_read, NULL, NULL, t3100e_write, NULL, NULL, NULL, 0, t3100e);
/* Respond to CGA I/O ports */
io_sethandler(0x03d0, 0x000c, t3100e_in, NULL, NULL, t3100e_out, NULL, NULL, t3100e);
/* Default attribute mapping is 4 */
t3100e->attrmap = 4;
t3100e_recalcattrs(t3100e);
/* Start off in 80x25 text mode */
t3100e->cga.cgastat = 0xF4;
t3100e->cga.vram = t3100e->vram;
t3100e->enabled = 1;
t3100e->video_options = 0xFF;
return t3100e;
}
void
t3100e_close(void *priv)
{
t3100e_t *t3100e = (t3100e_t *) priv;
free(t3100e->vram);
free(t3100e);
}
void
t3100e_speed_changed(void *priv)
{
t3100e_t *t3100e = (t3100e_t *) priv;
t3100e_recalctimings(t3100e);
}
const device_t t3100e_device = {
.name = "Toshiba T3100e",
.internal_name = "t3100e",
.flags = 0,
.local = 0,
.init = t3100e_init,
.close = t3100e_close,
.reset = NULL,
.available = NULL,
.speed_changed = t3100e_speed_changed,
.force_redraw = NULL,
.config = NULL
};

View File

@@ -58,13 +58,13 @@ static void
elt_vid_off_poll(void *priv)
{
cga_t *cga = priv;
uint8_t hdisp = cga->crtc[1];
uint8_t hdisp = cga->crtc[CGA_CRTC_HDISP];
/* Don't display anything.
* TODO: Do something less stupid to emulate backlight off. */
cga->crtc[1] = 0;
cga->crtc[CGA_CRTC_HDISP] = 0;
cga_poll(cga);
cga->crtc[1] = hdisp;
cga->crtc[CGA_CRTC_HDISP] = hdisp;
}
static void

View File

@@ -48,11 +48,10 @@
#include <86box/snd_sn76489.h>
#include <86box/video.h>
#include <86box/vid_cga_comp.h>
#include <86box/m_pcjr.h>
#include <86box/machine.h>
#include <86box/plat_unused.h>
#define PCJR_RGB 0
#define PCJR_COMPOSITE 1
#define STAT_PARITY 0x80
#define STAT_RTIMEOUT 0x40
@@ -63,49 +62,10 @@
#define STAT_IFULL 0x02
#define STAT_OFULL 0x01
typedef struct pcjr_t {
/* Video Controller stuff. */
mem_mapping_t mapping;
uint8_t crtc[32];
int crtcreg;
int array_index;
uint8_t array[32];
int array_ff;
int memctrl;
uint8_t stat;
int addr_mode;
uint8_t *vram;
uint8_t *b8000;
int linepos;
int displine;
int sc;
int vc;
int dispon;
int con;
int cursoron;
int blink;
int vsynctime;
int fullchange;
int vadj;
uint16_t ma;
uint16_t maback;
uint64_t dispontime;
uint64_t dispofftime;
pc_timer_t timer;
int firstline;
int lastline;
int composite;
int apply_hd;
/* Keyboard Controller stuff. */
int latched;
int data;
int serial_data[44];
int serial_pos;
uint8_t pa;
uint8_t pb;
pc_timer_t send_delay_timer;
} pcjr_t;
static uint8_t key_queue[16];
static int key_queue_start = 0;
static int key_queue_end = 0;
/*PCjr keyboard has no escape scancodes, and no scancodes beyond 54
Map right alt to 54h (FN) */
@@ -626,643 +586,6 @@ const scancode scancode_pcjr[512] = {
// clang-format on
};
static video_timings_t timing_dram = { VIDEO_BUS, 0, 0, 0, 0, 0, 0 }; /*No additional waitstates*/
static uint8_t crtcmask[32] = {
0xff, 0xff, 0xff, 0xff, 0x7f, 0x1f, 0x7f, 0x7f,
0xf3, 0x1f, 0x7f, 0x1f, 0x3f, 0xff, 0x3f, 0xff,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static uint8_t key_queue[16];
static int key_queue_start = 0;
static int key_queue_end = 0;
static void
recalc_address(pcjr_t *pcjr)
{
uint8_t masked_memctrl = pcjr->memctrl;
/* According to the Technical Reference, bits 2 and 5 are
ignored if there is only 64k of RAM and there are only
4 pages. */
if (mem_size < 128)
masked_memctrl &= ~0x24;
if ((pcjr->memctrl & 0xc0) == 0xc0) {
pcjr->vram = &ram[(masked_memctrl & 0x06) << 14];
pcjr->b8000 = &ram[(masked_memctrl & 0x30) << 11];
} else {
pcjr->vram = &ram[(masked_memctrl & 0x07) << 14];
pcjr->b8000 = &ram[(masked_memctrl & 0x38) << 11];
}
}
static void
recalc_timings(pcjr_t *pcjr)
{
double _dispontime;
double _dispofftime;
double disptime;
if (pcjr->array[0] & 1) {
disptime = pcjr->crtc[0] + 1;
_dispontime = pcjr->crtc[1];
} else {
disptime = (pcjr->crtc[0] + 1) << 1;
_dispontime = pcjr->crtc[1] << 1;
}
_dispofftime = disptime - _dispontime;
_dispontime *= CGACONST;
_dispofftime *= CGACONST;
pcjr->dispontime = (uint64_t) (_dispontime);
pcjr->dispofftime = (uint64_t) (_dispofftime);
}
static int
vid_get_h_overscan_size(pcjr_t *pcjr)
{
int ret;
if (pcjr->array[0] & 1)
ret = 128;
else
ret = 256;
return ret;
}
static void
vid_out(uint16_t addr, uint8_t val, void *priv)
{
pcjr_t *pcjr = (pcjr_t *) priv;
uint8_t old;
switch (addr) {
case 0x3d0:
case 0x3d2:
case 0x3d4:
case 0x3d6:
pcjr->crtcreg = val & 0x1f;
return;
case 0x3d1:
case 0x3d3:
case 0x3d5:
case 0x3d7:
old = pcjr->crtc[pcjr->crtcreg];
pcjr->crtc[pcjr->crtcreg] = val & crtcmask[pcjr->crtcreg];
if (pcjr->crtcreg == 2)
overscan_x = vid_get_h_overscan_size(pcjr);
if (old != val) {
if (pcjr->crtcreg < 0xe || pcjr->crtcreg > 0x10) {
pcjr->fullchange = changeframecount;
recalc_timings(pcjr);
}
}
return;
case 0x3da:
if (!pcjr->array_ff)
pcjr->array_index = val & 0x1f;
else {
if (pcjr->array_index & 0x10)
val &= 0x0f;
pcjr->array[pcjr->array_index & 0x1f] = val;
if (!(pcjr->array_index & 0x1f))
update_cga16_color(val);
}
pcjr->array_ff = !pcjr->array_ff;
break;
case 0x3df:
pcjr->memctrl = val;
pcjr->pa = val; /* The PCjr BIOS expects the value written to 3DF to
then be readable from port 60, others it errors out
with only 64k RAM set (but somehow, still works with
128k or more RAM). */
pcjr->addr_mode = val >> 6;
recalc_address(pcjr);
break;
default:
break;
}
}
static uint8_t
vid_in(uint16_t addr, void *priv)
{
pcjr_t *pcjr = (pcjr_t *) priv;
uint8_t ret = 0xff;
switch (addr) {
case 0x3d0:
case 0x3d2:
case 0x3d4:
case 0x3d6:
ret = pcjr->crtcreg;
break;
case 0x3d1:
case 0x3d3:
case 0x3d5:
case 0x3d7:
ret = pcjr->crtc[pcjr->crtcreg];
break;
case 0x3da:
pcjr->array_ff = 0;
pcjr->stat ^= 0x10;
ret = pcjr->stat;
break;
default:
break;
}
return ret;
}
static void
vid_write(uint32_t addr, uint8_t val, void *priv)
{
pcjr_t *pcjr = (pcjr_t *) priv;
if (pcjr->memctrl == -1)
return;
pcjr->b8000[addr & 0x3fff] = val;
}
static uint8_t
vid_read(uint32_t addr, void *priv)
{
const pcjr_t *pcjr = (pcjr_t *) priv;
if (pcjr->memctrl == -1)
return 0xff;
return (pcjr->b8000[addr & 0x3fff]);
}
static int
vid_get_h_overscan_delta(pcjr_t *pcjr)
{
int def;
int coef;
int ret;
switch ((pcjr->array[0] & 0x13) | ((pcjr->array[3] & 0x08) << 5)) {
case 0x13: /*320x200x16*/
def = 0x56;
coef = 8;
break;
case 0x12: /*160x200x16*/
def = 0x2c; /* I'm going to assume a datasheet erratum here. */
coef = 16;
break;
case 0x03: /*640x200x4*/
def = 0x56;
coef = 8;
break;
case 0x01: /*80 column text*/
def = 0x5a;
coef = 8;
break;
case 0x00: /*40 column text*/
default:
def = 0x2c;
coef = 16;
break;
case 0x02: /*320x200x4*/
def = 0x2b;
coef = 16;
break;
case 0x102: /*640x200x2*/
def = 0x2b;
coef = 16;
break;
}
ret = pcjr->crtc[0x02] - def;
if (ret < -8)
ret = -8;
if (ret > 8)
ret = 8;
return ret * coef;
}
static void
vid_blit_h_overscan(pcjr_t *pcjr)
{
int cols = (pcjr->array[2] & 0xf) + 16;;
int y0 = pcjr->firstline << 1;
int y = (pcjr->lastline << 1) + 16;
int ho_s = vid_get_h_overscan_size(pcjr);
int i;
int x;
if (pcjr->array[0] & 1)
x = (pcjr->crtc[1] << 3) + ho_s;
else
x = (pcjr->crtc[1] << 4) + ho_s;
for (i = 0; i < 16; i++) {
hline(buffer32, 0, y0 + i, x, cols);
hline(buffer32, 0, y + i, x, cols);
if (pcjr->composite) {
Composite_Process(pcjr->array[0], 0, x >> 2, buffer32->line[y0 + i]);
Composite_Process(pcjr->array[0], 0, x >> 2, buffer32->line[y + i]);
} else {
video_process_8(x, y0 + i);
video_process_8(x, y + i);
}
}
}
static void
vid_poll(void *priv)
{
pcjr_t *pcjr = (pcjr_t *) priv;
uint16_t ca = (pcjr->crtc[15] | (pcjr->crtc[14] << 8)) & 0x3fff;
int drawcursor;
int x;
int xs_temp;
int ys_temp;
int oldvc;
uint8_t chr;
uint8_t attr;
uint16_t dat;
int cols[4];
int oldsc;
int l = (pcjr->displine << 1) + 16;
int ho_s = vid_get_h_overscan_size(pcjr);
int ho_d = vid_get_h_overscan_delta(pcjr) + (ho_s / 2);
if (!pcjr->linepos) {
timer_advance_u64(&pcjr->timer, pcjr->dispofftime);
pcjr->stat &= ~1;
pcjr->linepos = 1;
oldsc = pcjr->sc;
if ((pcjr->crtc[8] & 3) == 3)
pcjr->sc = (pcjr->sc << 1) & 7;
if (pcjr->dispon) {
uint16_t offset = 0;
uint16_t mask = 0x1fff;
if (pcjr->displine < pcjr->firstline) {
pcjr->firstline = pcjr->displine;
video_wait_for_buffer();
}
pcjr->lastline = pcjr->displine;
cols[0] = (pcjr->array[2] & 0xf) + 16;
if (pcjr->array[0] & 1) {
hline(buffer32, 0, l, (pcjr->crtc[1] << 3) + ho_s, cols[0]);
hline(buffer32, 0, l + 1, (pcjr->crtc[1] << 3) + ho_s, cols[0]);
} else {
hline(buffer32, 0, l, (pcjr->crtc[1] << 4) + ho_s, cols[0]);
hline(buffer32, 0, l + 1, (pcjr->crtc[1] << 4) + ho_s, cols[0]);
}
switch (pcjr->addr_mode) {
case 0: /*Alpha*/
offset = 0;
mask = 0x3fff;
break;
case 1: /*Low resolution graphics*/
offset = (pcjr->sc & 1) * 0x2000;
break;
case 3: /*High resolution graphics*/
offset = (pcjr->sc & 3) * 0x2000;
break;
default:
break;
}
switch ((pcjr->array[0] & 0x13) | ((pcjr->array[3] & 0x08) << 5)) {
case 0x13: /*320x200x16*/
for (x = 0; x < pcjr->crtc[1]; x++) {
int ef_x = (x << 3) + ho_d;
dat = (pcjr->vram[((pcjr->ma << 1) & mask) + offset] << 8) |
pcjr->vram[((pcjr->ma << 1) & mask) + offset + 1];
pcjr->ma++;
buffer32->line[l][ef_x] = buffer32->line[l][ef_x + 1] =
buffer32->line[l + 1][ef_x] = buffer32->line[l + 1][ef_x + 1] =
pcjr->array[((dat >> 12) & pcjr->array[1] & 0x0f) + 16] + 16;
buffer32->line[l][ef_x + 2] = buffer32->line[l][ef_x + 3] =
buffer32->line[l + 1][ef_x + 2] = buffer32->line[l + 1][ef_x + 3] =
pcjr->array[((dat >> 8) & pcjr->array[1] & 0x0f) + 16] + 16;
buffer32->line[l][ef_x + 4] = buffer32->line[l][ef_x + 5] =
buffer32->line[l + 1][ef_x + 4] = buffer32->line[l + 1][ef_x + 5] =
pcjr->array[((dat >> 4) & pcjr->array[1] & 0x0f) + 16] + 16;
buffer32->line[l][ef_x + 6] = buffer32->line[l][ef_x + 7] =
buffer32->line[l + 1][ef_x + 6] = buffer32->line[l + 1][ef_x + 7] =
pcjr->array[(dat & pcjr->array[1] & 0x0f) + 16] + 16;
}
break;
case 0x12: /*160x200x16*/
for (x = 0; x < pcjr->crtc[1]; x++) {
int ef_x = (x << 4) + ho_d;
dat = (pcjr->vram[((pcjr->ma << 1) & mask) + offset] << 8) |
pcjr->vram[((pcjr->ma << 1) & mask) + offset + 1];
pcjr->ma++;
buffer32->line[l][ef_x] = buffer32->line[l][ef_x + 1] =
buffer32->line[l][ef_x + 2] = buffer32->line[l][ef_x + 3] =
buffer32->line[l + 1][ef_x] = buffer32->line[l + 1][ef_x + 1] =
buffer32->line[l + 1][ef_x + 2] = buffer32->line[l + 1][ef_x + 3] =
pcjr->array[((dat >> 12) & pcjr->array[1] & 0x0f) + 16] + 16;
buffer32->line[l][ef_x + 4] = buffer32->line[l][ef_x + 5] =
buffer32->line[l][ef_x + 6] = buffer32->line[l][ef_x + 7] =
buffer32->line[l + 1][ef_x + 4] = buffer32->line[l + 1][ef_x + 5] =
buffer32->line[l + 1][ef_x + 6] = buffer32->line[l + 1][ef_x + 7] =
pcjr->array[((dat >> 8) & pcjr->array[1] & 0x0f) + 16] + 16;
buffer32->line[l][ef_x + 8] = buffer32->line[l][ef_x + 9] =
buffer32->line[l][ef_x + 10] = buffer32->line[l][ef_x + 11] =
buffer32->line[l + 1][ef_x + 8] = buffer32->line[l + 1][ef_x + 9] =
buffer32->line[l + 1][ef_x + 10] = buffer32->line[l + 1][ef_x + 11] =
pcjr->array[((dat >> 4) & pcjr->array[1] & 0x0f) + 16] + 16;
buffer32->line[l][ef_x + 12] = buffer32->line[l][ef_x + 13] =
buffer32->line[l][ef_x + 14] = buffer32->line[l][ef_x + 15] =
buffer32->line[l + 1][ef_x + 12] = buffer32->line[l + 1][ef_x + 13] =
buffer32->line[l + 1][ef_x + 14] = buffer32->line[l + 1][ef_x + 15] =
pcjr->array[(dat & pcjr->array[1] & 0x0f) + 16] + 16;
}
break;
case 0x03: /*640x200x4*/
for (x = 0; x < pcjr->crtc[1]; x++) {
int ef_x = (x << 3) + ho_d;
dat = (pcjr->vram[((pcjr->ma << 1) & mask) + offset + 1] << 8) |
pcjr->vram[((pcjr->ma << 1) & mask) + offset];
pcjr->ma++;
for (uint8_t c = 0; c < 8; c++) {
chr = (dat >> 7) & 1;
chr |= ((dat >> 14) & 2);
buffer32->line[l][ef_x + c] = buffer32->line[l + 1][ef_x + c] =
pcjr->array[(chr & pcjr->array[1] & 0x0f) + 16] + 16;
dat <<= 1;
}
}
break;
case 0x01: /*80 column text*/
for (x = 0; x < pcjr->crtc[1]; x++) {
int ef_x = (x << 3) + ho_d;
chr = pcjr->vram[((pcjr->ma << 1) & mask) + offset];
attr = pcjr->vram[((pcjr->ma << 1) & mask) + offset + 1];
drawcursor = ((pcjr->ma == ca) && pcjr->con && pcjr->cursoron);
if (pcjr->array[3] & 4) {
cols[1] = pcjr->array[((attr & 15) & pcjr->array[1] & 0x0f) + 16] + 16;
cols[0] = pcjr->array[(((attr >> 4) & 7) & pcjr->array[1] & 0x0f) + 16] + 16;
if ((pcjr->blink & 16) && (attr & 0x80) && !drawcursor)
cols[1] = cols[0];
} else {
cols[1] = pcjr->array[((attr & 15) & pcjr->array[1] & 0x0f) + 16] + 16;
cols[0] = pcjr->array[((attr >> 4) & pcjr->array[1] & 0x0f) + 16] + 16;
}
if (pcjr->sc & 8)
for (uint8_t c = 0; c < 8; c++)
buffer32->line[l][ef_x + c] =
buffer32->line[l + 1][ef_x + c] = cols[0];
else
for (uint8_t c = 0; c < 8; c++)
buffer32->line[l][ef_x + c] =
buffer32->line[l + 1][ef_x + c] =
cols[(fontdat[chr][pcjr->sc & 7] & (1 << (c ^ 7))) ? 1 : 0];
if (drawcursor)
for (uint8_t c = 0; c < 8; c++) {
buffer32->line[l][ef_x + c] ^= 15;
buffer32->line[l + 1][ef_x + c] ^= 15;
}
pcjr->ma++;
}
break;
case 0x00: /*40 column text*/
for (x = 0; x < pcjr->crtc[1]; x++) {
int ef_x = (x << 4) + ho_d;
chr = pcjr->vram[((pcjr->ma << 1) & mask) + offset];
attr = pcjr->vram[((pcjr->ma << 1) & mask) + offset + 1];
drawcursor = ((pcjr->ma == ca) && pcjr->con && pcjr->cursoron);
if (pcjr->array[3] & 4) {
cols[1] = pcjr->array[((attr & 15) & pcjr->array[1] & 0x0f) + 16] + 16;
cols[0] = pcjr->array[(((attr >> 4) & 7) & pcjr->array[1] & 0x0f) + 16] + 16;
if ((pcjr->blink & 16) && (attr & 0x80) && !drawcursor)
cols[1] = cols[0];
} else {
cols[1] = pcjr->array[((attr & 15) & pcjr->array[1] & 0x0f) + 16] + 16;
cols[0] = pcjr->array[((attr >> 4) & pcjr->array[1] & 0x0f) + 16] + 16;
}
pcjr->ma++;
if (pcjr->sc & 8)
for (uint8_t c = 0; c < 8; c++)
buffer32->line[l][ef_x + (c << 1)] =
buffer32->line[l][ef_x + (c << 1) + 1] =
buffer32->line[l + 1][ef_x + (c << 1)] =
buffer32->line[l + 1][ef_x + (c << 1) + 1] = cols[0];
else
for (uint8_t c = 0; c < 8; c++)
buffer32->line[l][ef_x + (c << 1)] =
buffer32->line[l][ef_x + (c << 1) + 1] =
buffer32->line[l + 1][ef_x + (c << 1)] =
buffer32->line[l + 1][ef_x + (c << 1) + 1] =
cols[(fontdat[chr][pcjr->sc & 7] & (1 << (c ^ 7))) ? 1 : 0];
if (drawcursor)
for (uint8_t c = 0; c < 16; c++) {
buffer32->line[l][ef_x + c] ^= 15;
buffer32->line[l + 1][ef_x + c] ^= 15;
}
}
break;
case 0x02: /*320x200x4*/
cols[0] = pcjr->array[0 + 16] + 16;
cols[1] = pcjr->array[1 + 16] + 16;
cols[2] = pcjr->array[2 + 16] + 16;
cols[3] = pcjr->array[3 + 16] + 16;
for (x = 0; x < pcjr->crtc[1]; x++) {
int ef_x = (x << 4) + ho_d;
dat = (pcjr->vram[((pcjr->ma << 1) & mask) + offset] << 8) |
pcjr->vram[((pcjr->ma << 1) & mask) + offset + 1];
pcjr->ma++;
for (uint8_t c = 0; c < 8; c++) {
buffer32->line[l][ef_x + (c << 1)] =
buffer32->line[l][ef_x + (c << 1) + 1] =
buffer32->line[l + 1][ef_x + (c << 1)] =
buffer32->line[l + 1][ef_x + (c << 1) + 1] = cols[dat >> 14];
dat <<= 2;
}
}
break;
case 0x102: /*640x200x2*/
cols[0] = pcjr->array[0 + 16] + 16;
cols[1] = pcjr->array[1 + 16] + 16;
for (x = 0; x < pcjr->crtc[1]; x++) {
int ef_x = (x << 4) + ho_d;
dat = (pcjr->vram[((pcjr->ma << 1) & mask) + offset] << 8) |
pcjr->vram[((pcjr->ma << 1) & mask) + offset + 1];
pcjr->ma++;
for (uint8_t c = 0; c < 16; c++) {
buffer32->line[l][ef_x + c] = buffer32->line[l + 1][ef_x + c] =
cols[dat >> 15];
dat <<= 1;
}
}
break;
default:
break;
}
} else {
if (pcjr->array[3] & 4) {
if (pcjr->array[0] & 1) {
hline(buffer32, 0, l, (pcjr->crtc[1] << 3) + ho_s, (pcjr->array[2] & 0xf) + 16);
hline(buffer32, 0, l + 1, (pcjr->crtc[1] << 3) + ho_s, (pcjr->array[2] & 0xf) + 16);
} else {
hline(buffer32, 0, l, (pcjr->crtc[1] << 4) + ho_s, (pcjr->array[2] & 0xf) + 16);
hline(buffer32, 0, l + 1, (pcjr->crtc[1] << 4) + ho_s, (pcjr->array[2] & 0xf) + 16);
}
} else {
cols[0] = pcjr->array[0 + 16] + 16;
if (pcjr->array[0] & 1) {
hline(buffer32, 0, l, (pcjr->crtc[1] << 3) + ho_s, cols[0]);
hline(buffer32, 0, l + 1, (pcjr->crtc[1] << 3) + ho_s, cols[0]);
} else {
hline(buffer32, 0, l, (pcjr->crtc[1] << 4) + ho_s, cols[0]);
hline(buffer32, 0, l + 1, (pcjr->crtc[1] << 4) + ho_s, cols[0]);
}
}
}
if (pcjr->array[0] & 1)
x = (pcjr->crtc[1] << 3) + ho_s;
else
x = (pcjr->crtc[1] << 4) + ho_s;
if (pcjr->composite) {
Composite_Process(pcjr->array[0], 0, x >> 2, buffer32->line[l]);
Composite_Process(pcjr->array[0], 0, x >> 2, buffer32->line[l + 1]);
} else {
video_process_8(x, l);
video_process_8(x, l + 1);
}
pcjr->sc = oldsc;
if (pcjr->vc == pcjr->crtc[7] && !pcjr->sc) {
pcjr->stat |= 8;
}
pcjr->displine++;
if (pcjr->displine >= 360)
pcjr->displine = 0;
} else {
timer_advance_u64(&pcjr->timer, pcjr->dispontime);
if (pcjr->dispon)
pcjr->stat |= 1;
pcjr->linepos = 0;
if (pcjr->vsynctime) {
pcjr->vsynctime--;
if (!pcjr->vsynctime) {
pcjr->stat &= ~8;
}
}
if (pcjr->sc == (pcjr->crtc[11] & 31) || ((pcjr->crtc[8] & 3) == 3 && pcjr->sc == ((pcjr->crtc[11] & 31) >> 1))) {
pcjr->con = 0;
}
if (pcjr->vadj) {
pcjr->sc++;
pcjr->sc &= 31;
pcjr->ma = pcjr->maback;
pcjr->vadj--;
if (!pcjr->vadj) {
pcjr->dispon = 1;
pcjr->ma = pcjr->maback = (pcjr->crtc[13] | (pcjr->crtc[12] << 8)) & 0x3fff;
pcjr->sc = 0;
}
} else if (pcjr->sc == pcjr->crtc[9] || ((pcjr->crtc[8] & 3) == 3 && pcjr->sc == (pcjr->crtc[9] >> 1))) {
pcjr->maback = pcjr->ma;
pcjr->sc = 0;
oldvc = pcjr->vc;
pcjr->vc++;
pcjr->vc &= 127;
if (pcjr->vc == pcjr->crtc[6])
pcjr->dispon = 0;
if (oldvc == pcjr->crtc[4]) {
pcjr->vc = 0;
pcjr->vadj = pcjr->crtc[5];
if (!pcjr->vadj)
pcjr->dispon = 1;
if (!pcjr->vadj)
pcjr->ma = pcjr->maback = (pcjr->crtc[13] | (pcjr->crtc[12] << 8)) & 0x3fff;
if ((pcjr->crtc[10] & 0x60) == 0x20)
pcjr->cursoron = 0;
else
pcjr->cursoron = pcjr->blink & 16;
}
if (pcjr->vc == pcjr->crtc[7]) {
pcjr->dispon = 0;
pcjr->displine = 0;
pcjr->vsynctime = 16;
picint(1 << 5);
if (pcjr->crtc[7]) {
if (pcjr->array[0] & 1)
x = (pcjr->crtc[1] << 3) + ho_s;
else
x = (pcjr->crtc[1] << 4) + ho_s;
pcjr->lastline++;
xs_temp = x;
ys_temp = (pcjr->lastline - pcjr->firstline) << 1;
if ((xs_temp > 0) && (ys_temp > 0)) {
int actual_ys = ys_temp;
if (xs_temp < 64)
xs_temp = 656;
if (ys_temp < 32)
ys_temp = 400;
if (!enable_overscan)
xs_temp -= ho_s;
if ((xs_temp != xsize) || (ys_temp != ysize) || video_force_resize_get()) {
xsize = xs_temp;
ysize = ys_temp;
set_screen_size(xsize, ysize + (enable_overscan ? 32 : 0));
if (video_force_resize_get())
video_force_resize_set(0);
}
vid_blit_h_overscan(pcjr);
if (enable_overscan) {
video_blit_memtoscreen(0, pcjr->firstline << 1,
xsize, actual_ys + 32);
} else if (pcjr->apply_hd) {
video_blit_memtoscreen(ho_s / 2, (pcjr->firstline << 1) + 16,
xsize, actual_ys);
} else {
video_blit_memtoscreen(ho_d, (pcjr->firstline << 1) + 16,
xsize, actual_ys);
}
}
frames++;
video_res_x = xsize;
video_res_y = ysize;
}
pcjr->firstline = 1000;
pcjr->lastline = 0;
pcjr->blink++;
}
} else {
pcjr->sc++;
pcjr->sc &= 31;
pcjr->ma = pcjr->maback;
}
if (pcjr->sc == (pcjr->crtc[10] & 31) || ((pcjr->crtc[8] & 3) == 3 && pcjr->sc == ((pcjr->crtc[10] & 31) >> 1)))
pcjr->con = 1;
}
}
static void
kbd_write(uint16_t port, uint8_t val, void *priv)
@@ -1433,35 +756,7 @@ speed_changed(void *priv)
{
pcjr_t *pcjr = (pcjr_t *) priv;
recalc_timings(pcjr);
}
static void
pcjr_vid_init(pcjr_t *pcjr)
{
int display_type;
video_inform(VIDEO_FLAG_TYPE_CGA, &timing_dram);
pcjr->memctrl = -1;
if (mem_size < 128)
pcjr->memctrl &= ~0x24;
display_type = device_get_config_int("display_type");
pcjr->composite = (display_type != PCJR_RGB);
pcjr->apply_hd = device_get_config_int("apply_hd");
overscan_x = 256;
overscan_y = 32;
mem_mapping_add(&pcjr->mapping, 0xb8000, 0x08000,
vid_read, NULL, NULL,
vid_write, NULL, NULL, NULL, 0, pcjr);
io_sethandler(0x03d0, 16,
vid_in, NULL, NULL, vid_out, NULL, NULL, pcjr);
timer_add(&pcjr->timer, vid_poll, pcjr, 1);
cga_palette = 0;
cgapal_rebuild();
pcjr_recalc_timings(pcjr);
}
void

View File

@@ -44,12 +44,9 @@
#include <86box/video.h>
#include <86box/vid_cga_comp.h>
#include <86box/machine.h>
#include <86box/m_tandy.h>
#include <86box/plat_unused.h>
enum {
TANDY_RGB = 0,
TANDY_COMPOSITE
};
enum {
TYPE_TANDY = 0,
@@ -65,79 +62,6 @@ enum {
EEPROM_WRITE
};
typedef struct t1kvid_t {
mem_mapping_t mapping;
mem_mapping_t vram_mapping;
uint8_t crtc[32];
int crtcreg;
int array_index;
uint8_t array[256];
int memctrl;
uint8_t mode;
uint8_t col;
uint8_t stat;
uint8_t *vram;
uint8_t *b8000;
uint32_t b8000_mask;
uint32_t b8000_limit;
uint8_t planar_ctrl;
uint8_t lp_strobe;
int linepos;
int displine;
int sc;
int vc;
int dispon;
int con;
int cursoron;
int blink;
int fullchange;
int vsynctime;
int vadj;
uint16_t ma;
uint16_t maback;
uint64_t dispontime;
uint64_t dispofftime;
pc_timer_t timer;
int firstline;
int lastline;
int composite;
} t1kvid_t;
typedef struct t1keep_t {
char *path;
int state;
int count;
int addr;
int clk;
uint16_t data;
uint16_t store[64];
} t1keep_t;
typedef struct tandy_t {
mem_mapping_t ram_mapping;
mem_mapping_t rom_mapping; /* SL2 */
uint8_t *rom; /* SL2 */
uint8_t ram_bank;
uint8_t rom_bank; /* SL2 */
int rom_offset; /* SL2 */
uint32_t base;
uint32_t mask;
int is_hx;
int is_sl2;
t1kvid_t *vid;
} tandy_t;
static video_timings_t timing_dram = { VIDEO_BUS, 0, 0, 0, 0, 0, 0 }; /*No additional waitstates*/
static const scancode scancode_tandy[512] = {
// clang-format off
@@ -655,22 +579,8 @@ static const scancode scancode_tandy[512] = {
{ .mk = { 0 }, .brk = { 0 } } /* 1ff */
// clang-format on
};
static uint8_t crtcmask[32] = {
0xff, 0xff, 0xff, 0xff, 0x7f, 0x1f, 0x7f, 0x7f,
0xf3, 0x1f, 0x7f, 0x1f, 0x3f, 0xff, 0x3f, 0xff,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static uint8_t crtcmask_sl[32] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0xff, 0xff,
0xf3, 0x1f, 0x7f, 0x1f, 0x3f, 0xff, 0x3f, 0xff,
0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static int eep_data_out;
static uint8_t vid_in(uint16_t addr, void *priv);
static void vid_out(uint16_t addr, uint8_t val, void *priv);
static int eep_data_out;
#ifdef ENABLE_TANDY_LOG
int tandy_do_log = ENABLE_TANDY_LOG;
@@ -690,762 +600,7 @@ tandy_log(const char *fmt, ...)
# define tandy_log(fmt, ...)
#endif
static void
recalc_mapping(tandy_t *dev)
{
t1kvid_t *vid = dev->vid;
mem_mapping_disable(&vid->mapping);
io_removehandler(0x03d0, 16,
vid_in, NULL, NULL, vid_out, NULL, NULL, dev);
if (vid->planar_ctrl & 4) {
mem_mapping_enable(&vid->mapping);
if (vid->array[5] & 1)
mem_mapping_set_addr(&vid->mapping, 0xa0000, 0x10000);
else
mem_mapping_set_addr(&vid->mapping, 0xb8000, 0x8000);
io_sethandler(0x03d0, 16, vid_in, NULL, NULL, vid_out, NULL, NULL, dev);
}
}
static void
recalc_timings(tandy_t *dev)
{
t1kvid_t *vid = dev->vid;
double _dispontime;
double _dispofftime;
double disptime;
if (vid->mode & 1) {
disptime = vid->crtc[0] + 1;
_dispontime = vid->crtc[1];
} else {
disptime = (vid->crtc[0] + 1) << 1;
_dispontime = vid->crtc[1] << 1;
}
_dispofftime = disptime - _dispontime;
_dispontime *= CGACONST;
_dispofftime *= CGACONST;
vid->dispontime = (uint64_t) (_dispontime);
vid->dispofftime = (uint64_t) (_dispofftime);
}
static void
recalc_address(tandy_t *dev)
{
t1kvid_t *vid = dev->vid;
if ((vid->memctrl & 0xc0) == 0xc0) {
vid->vram = &ram[((vid->memctrl & 0x06) << 14) + dev->base];
vid->b8000 = &ram[((vid->memctrl & 0x30) << 11) + dev->base];
vid->b8000_mask = 0x7fff;
} else {
vid->vram = &ram[((vid->memctrl & 0x07) << 14) + dev->base];
vid->b8000 = &ram[((vid->memctrl & 0x38) << 11) + dev->base];
vid->b8000_mask = 0x3fff;
}
}
static void
recalc_address_sl(tandy_t *dev)
{
t1kvid_t *vid = dev->vid;
vid->b8000_limit = 0x8000;
if (vid->array[5] & 1) {
vid->vram = &ram[((vid->memctrl & 0x04) << 14) + dev->base];
vid->b8000 = &ram[((vid->memctrl & 0x20) << 11) + dev->base];
} else if ((vid->memctrl & 0xc0) == 0xc0) {
vid->vram = &ram[((vid->memctrl & 0x06) << 14) + dev->base];
vid->b8000 = &ram[((vid->memctrl & 0x30) << 11) + dev->base];
} else {
vid->vram = &ram[((vid->memctrl & 0x07) << 14) + dev->base];
vid->b8000 = &ram[((vid->memctrl & 0x38) << 11) + dev->base];
if ((vid->memctrl & 0x38) == 0x38)
vid->b8000_limit = 0x4000;
}
}
static void
vid_update_latch(t1kvid_t *vid)
{
uint32_t lp_latch = vid->displine * vid->crtc[1];
vid->crtc[0x10] = (lp_latch >> 8) & 0x3f;
vid->crtc[0x11] = lp_latch & 0xff;
}
static void
vid_out(uint16_t addr, uint8_t val, void *priv)
{
tandy_t *dev = (tandy_t *) priv;
t1kvid_t *vid = dev->vid;
uint8_t old;
if ((addr >= 0x3d0) && (addr <= 0x3d7))
addr = (addr & 0xff9) | 0x004;
switch (addr) {
case 0x03d4:
vid->crtcreg = val & 0x1f;
break;
case 0x03d5:
old = vid->crtc[vid->crtcreg];
if (dev->is_sl2)
vid->crtc[vid->crtcreg] = val & crtcmask_sl[vid->crtcreg];
else
vid->crtc[vid->crtcreg] = val & crtcmask[vid->crtcreg];
if (old != val) {
if (vid->crtcreg < 0xe || vid->crtcreg > 0x10) {
vid->fullchange = changeframecount;
recalc_timings(dev);
}
}
break;
case 0x03d8:
old = vid->mode;
vid->mode = val;
if ((old ^ val) & 0x01)
recalc_timings(dev);
if (!dev->is_sl2)
update_cga16_color(vid->mode);
break;
case 0x03d9:
vid->col = val;
break;
case 0x03da:
vid->array_index = val & 0x1f;
break;
case 0x3db:
if (!dev->is_sl2 && (vid->lp_strobe == 1))
vid->lp_strobe = 0;
break;
case 0x3dc:
if (!dev->is_sl2 && (vid->lp_strobe == 0)) {
vid->lp_strobe = 1;
vid_update_latch(vid);
}
break;
case 0x03de:
if (vid->array_index & 16)
val &= 0xf;
vid->array[vid->array_index & 0x1f] = val;
if (dev->is_sl2) {
if ((vid->array_index & 0x1f) == 5) {
recalc_mapping(dev);
recalc_address_sl(dev);
}
}
break;
case 0x03df:
vid->memctrl = val;
if (dev->is_sl2)
recalc_address_sl(dev);
else
recalc_address(dev);
break;
case 0x0065:
if (val == 8)
return; /*Hack*/
vid->planar_ctrl = val;
recalc_mapping(dev);
break;
default:
break;
}
}
static uint8_t
vid_in(uint16_t addr, void *priv)
{
const tandy_t *dev = (tandy_t *) priv;
t1kvid_t *vid = dev->vid;
uint8_t ret = 0xff;
if ((addr >= 0x3d0) && (addr <= 0x3d7))
addr = (addr & 0xff9) | 0x004;
switch (addr) {
case 0x03d4:
ret = vid->crtcreg;
break;
case 0x03d5:
ret = vid->crtc[vid->crtcreg];
break;
case 0x03da:
ret = vid->stat;
break;
case 0x3db:
if (!dev->is_sl2 && (vid->lp_strobe == 1))
vid->lp_strobe = 0;
break;
case 0x3dc:
if (!dev->is_sl2 && (vid->lp_strobe == 0)) {
vid->lp_strobe = 1;
vid_update_latch(vid);
}
break;
default:
break;
}
return ret;
}
static void
vid_write(uint32_t addr, uint8_t val, void *priv)
{
tandy_t *dev = (tandy_t *) priv;
t1kvid_t *vid = dev->vid;
if (vid->memctrl == -1)
return;
if (dev->is_sl2) {
if (vid->array[5] & 1)
vid->b8000[addr & 0xffff] = val;
else {
if ((addr & 0x7fff) < vid->b8000_limit)
vid->b8000[addr & 0x7fff] = val;
}
} else {
vid->b8000[addr & vid->b8000_mask] = val;
}
}
static uint8_t
vid_read(uint32_t addr, void *priv)
{
const tandy_t *dev = (tandy_t *) priv;
const t1kvid_t *vid = dev->vid;
if (vid->memctrl == -1)
return 0xff;
if (dev->is_sl2) {
if (vid->array[5] & 1)
return (vid->b8000[addr & 0xffff]);
if ((addr & 0x7fff) < vid->b8000_limit)
return (vid->b8000[addr & 0x7fff]);
else
return 0xff;
} else {
return (vid->b8000[addr & vid->b8000_mask]);
}
}
static void
vid_poll(void *priv)
{
tandy_t *dev = (tandy_t *) priv;
t1kvid_t *vid = dev->vid;
uint16_t ca = (vid->crtc[15] | (vid->crtc[14] << 8)) & 0x3fff;
int drawcursor;
int x;
int c;
int xs_temp;
int ys_temp;
int oldvc;
uint8_t chr;
uint8_t attr;
uint16_t dat;
int cols[4];
int col;
int oldsc;
if (!vid->linepos) {
timer_advance_u64(&vid->timer, vid->dispofftime);
vid->stat |= 1;
vid->linepos = 1;
oldsc = vid->sc;
if ((vid->crtc[8] & 3) == 3)
vid->sc = (vid->sc << 1) & 7;
if (vid->dispon) {
if (vid->displine < vid->firstline) {
vid->firstline = vid->displine;
video_wait_for_buffer();
}
vid->lastline = vid->displine;
cols[0] = (vid->array[2] & 0xf) + 16;
for (c = 0; c < 8; c++) {
if (vid->array[3] & 4) {
buffer32->line[vid->displine << 1][c] = buffer32->line[(vid->displine << 1) + 1][c] = cols[0];
if (vid->mode & 1) {
buffer32->line[vid->displine << 1][c + (vid->crtc[1] << 3) + 8] = buffer32->line[(vid->displine << 1) + 1][c + (vid->crtc[1] << 3) + 8] = cols[0];
} else {
buffer32->line[vid->displine << 1][c + (vid->crtc[1] << 4) + 8] = buffer32->line[(vid->displine << 1) + 1][c + (vid->crtc[1] << 4) + 8] = cols[0];
}
} else if ((vid->mode & 0x12) == 0x12) {
buffer32->line[vid->displine << 1][c] = buffer32->line[(vid->displine << 1) + 1][c] = 0;
if (vid->mode & 1) {
buffer32->line[vid->displine << 1][c + (vid->crtc[1] << 3) + 8] = buffer32->line[(vid->displine << 1) + 1][c + (vid->crtc[1] << 3) + 8] = 0;
} else {
buffer32->line[vid->displine << 1][c + (vid->crtc[1] << 4) + 8] = buffer32->line[(vid->displine << 1) + 1][c + (vid->crtc[1] << 4) + 8] = 0;
}
} else {
buffer32->line[vid->displine << 1][c] = buffer32->line[(vid->displine << 1) + 1][c] = (vid->col & 15) + 16;
if (vid->mode & 1) {
buffer32->line[vid->displine << 1][c + (vid->crtc[1] << 3) + 8] = buffer32->line[(vid->displine << 1) + 1][c + (vid->crtc[1] << 3) + 8] = (vid->col & 15) + 16;
} else {
buffer32->line[vid->displine << 1][c + (vid->crtc[1] << 4) + 8] = buffer32->line[(vid->displine << 1) + 1][c + (vid->crtc[1] << 4) + 8] = (vid->col & 15) + 16;
}
}
}
if (dev->is_sl2 && (vid->array[5] & 1)) { /*640x200x16*/
for (x = 0; x < vid->crtc[1] * 2; x++) {
dat = (vid->vram[(vid->ma << 1) & 0xffff] << 8) | vid->vram[((vid->ma << 1) + 1) & 0xffff];
vid->ma++;
buffer32->line[vid->displine << 1][(x << 2) + 8] = buffer32->line[(vid->displine << 1) + 1][(x << 2) + 8] = vid->array[((dat >> 12) & 0xf) + 16] + 16;
buffer32->line[vid->displine << 1][(x << 2) + 9] = buffer32->line[(vid->displine << 1) + 1][(x << 2) + 9] = vid->array[((dat >> 8) & 0xf) + 16] + 16;
buffer32->line[vid->displine << 1][(x << 2) + 10] = buffer32->line[(vid->displine << 1) + 1][(x << 2) + 10] = vid->array[((dat >> 4) & 0xf) + 16] + 16;
buffer32->line[vid->displine << 1][(x << 2) + 11] = buffer32->line[(vid->displine << 1) + 1][(x << 2) + 11] = vid->array[(dat & 0xf) + 16] + 16;
}
} else if ((vid->array[3] & 0x10) && (vid->mode & 1)) { /*320x200x16*/
for (x = 0; x < vid->crtc[1]; x++) {
dat = (vid->vram[((vid->ma << 1) & 0x1fff) + ((vid->sc & 3) * 0x2000)] << 8) | vid->vram[((vid->ma << 1) & 0x1fff) + ((vid->sc & 3) * 0x2000) + 1];
vid->ma++;
buffer32->line[vid->displine << 1][(x << 3) + 8] = buffer32->line[(vid->displine << 1) + 1][(x << 3) + 8] = buffer32->line[vid->displine << 1][(x << 3) + 9] = buffer32->line[(vid->displine << 1) + 1][(x << 3) + 9] = vid->array[((dat >> 12) & vid->array[1] & 0x0f) + 16] + 16;
buffer32->line[vid->displine << 1][(x << 3) + 10] = buffer32->line[(vid->displine << 1) + 1][(x << 3) + 10] = buffer32->line[vid->displine << 1][(x << 3) + 11] = buffer32->line[(vid->displine << 1) + 1][(x << 3) + 11] = vid->array[((dat >> 8) & vid->array[1] & 0x0f) + 16] + 16;
buffer32->line[vid->displine << 1][(x << 3) + 12] = buffer32->line[(vid->displine << 1) + 1][(x << 3) + 12] = buffer32->line[vid->displine << 1][(x << 3) + 13] = buffer32->line[(vid->displine << 1) + 1][(x << 3) + 13] = vid->array[((dat >> 4) & vid->array[1] & 0x0f) + 16] + 16;
buffer32->line[vid->displine << 1][(x << 3) + 14] = buffer32->line[(vid->displine << 1) + 1][(x << 3) + 14] = buffer32->line[vid->displine << 1][(x << 3) + 15] = buffer32->line[(vid->displine << 1) + 1][(x << 3) + 15] = vid->array[(dat & vid->array[1] & 0x0f) + 16] + 16;
}
} else if (vid->array[3] & 0x10) { /*160x200x16*/
for (x = 0; x < vid->crtc[1]; x++) {
if (dev->is_sl2) {
dat = (vid->vram[((vid->ma << 1) & 0x1fff) + ((vid->sc & 1) * 0x2000)] << 8) | vid->vram[((vid->ma << 1) & 0x1fff) + ((vid->sc & 1) * 0x2000) + 1];
} else {
dat = (vid->vram[((vid->ma << 1) & 0x1fff) + ((vid->sc & 3) * 0x2000)] << 8) | vid->vram[((vid->ma << 1) & 0x1fff) + ((vid->sc & 3) * 0x2000) + 1];
}
vid->ma++;
buffer32->line[vid->displine << 1][(x << 4) + 8] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + 8] = buffer32->line[vid->displine << 1][(x << 4) + 9] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + 9] = buffer32->line[vid->displine << 1][(x << 4) + 10] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + 10] = buffer32->line[vid->displine << 1][(x << 4) + 11] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + 11] = vid->array[((dat >> 12) & vid->array[1] & 0x0f) + 16] + 16;
buffer32->line[vid->displine << 1][(x << 4) + 12] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + 12] = buffer32->line[vid->displine << 1][(x << 4) + 13] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + 13] = buffer32->line[vid->displine << 1][(x << 4) + 14] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + 14] = buffer32->line[vid->displine << 1][(x << 4) + 15] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + 15] = vid->array[((dat >> 8) & vid->array[1] & 0x0f) + 16] + 16;
buffer32->line[vid->displine << 1][(x << 4) + 16] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + 16] = buffer32->line[vid->displine << 1][(x << 4) + 17] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + 17] = buffer32->line[vid->displine << 1][(x << 4) + 18] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + 18] = buffer32->line[vid->displine << 1][(x << 4) + 19] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + 19] = vid->array[((dat >> 4) & vid->array[1] & 0x0f) + 16] + 16;
buffer32->line[vid->displine << 1][(x << 4) + 20] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + 20] = buffer32->line[vid->displine << 1][(x << 4) + 21] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + 21] = buffer32->line[vid->displine << 1][(x << 4) + 22] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + 22] = buffer32->line[vid->displine << 1][(x << 4) + 23] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + 23] = vid->array[(dat & vid->array[1] & 0x0f) + 16] + 16;
}
} else if (vid->array[3] & 0x08) { /*640x200x4 - this implementation is a complete guess!*/
for (x = 0; x < vid->crtc[1]; x++) {
dat = (vid->vram[((vid->ma << 1) & 0x1fff) + ((vid->sc & 3) * 0x2000)] << 8) | vid->vram[((vid->ma << 1) & 0x1fff) + ((vid->sc & 3) * 0x2000) + 1];
vid->ma++;
for (c = 0; c < 8; c++) {
chr = (dat >> 6) & 2;
chr |= ((dat >> 15) & 1);
buffer32->line[vid->displine << 1][(x << 3) + 8 + c] = buffer32->line[(vid->displine << 1) + 1][(x << 3) + 8 + c] = vid->array[(chr & vid->array[1]) + 16] + 16;
dat <<= 1;
}
}
} else if (vid->mode & 1) {
for (x = 0; x < vid->crtc[1]; x++) {
chr = vid->vram[(vid->ma << 1) & 0x3fff];
attr = vid->vram[((vid->ma << 1) + 1) & 0x3fff];
drawcursor = ((vid->ma == ca) && vid->con && vid->cursoron);
if (vid->mode & 0x20) {
cols[1] = vid->array[((attr & 15) & vid->array[1]) + 16] + 16;
cols[0] = vid->array[(((attr >> 4) & 7) & vid->array[1]) + 16] + 16;
if ((vid->blink & 16) && (attr & 0x80) && !drawcursor)
cols[1] = cols[0];
} else {
cols[1] = vid->array[((attr & 15) & vid->array[1]) + 16] + 16;
cols[0] = vid->array[((attr >> 4) & vid->array[1]) + 16] + 16;
}
if (vid->sc & 8) {
for (c = 0; c < 8; c++) {
buffer32->line[vid->displine << 1][(x << 3) + c + 8] = buffer32->line[(vid->displine << 1) + 1][(x << 3) + c + 8] = cols[0];
}
} else {
for (c = 0; c < 8; c++) {
if (vid->sc == 8) {
buffer32->line[vid->displine << 1][(x << 3) + c + 8] = buffer32->line[(vid->displine << 1) + 1][(x << 3) + c + 8] = cols[(fontdat[chr][7] & (1 << (c ^ 7))) ? 1 : 0];
} else {
buffer32->line[vid->displine << 1][(x << 3) + c + 8] = buffer32->line[(vid->displine << 1) + 1][(x << 3) + c + 8] = cols[(fontdat[chr][vid->sc & 7] & (1 << (c ^ 7))) ? 1 : 0];
}
}
}
if (drawcursor) {
for (c = 0; c < 8; c++) {
buffer32->line[vid->displine << 1][(x << 3) + c + 8] ^= 15;
buffer32->line[(vid->displine << 1) + 1][(x << 3) + c + 8] ^= 15;
}
}
vid->ma++;
}
} else if (!(vid->mode & 2)) {
for (x = 0; x < vid->crtc[1]; x++) {
chr = vid->vram[(vid->ma << 1) & 0x3fff];
attr = vid->vram[((vid->ma << 1) + 1) & 0x3fff];
drawcursor = ((vid->ma == ca) && vid->con && vid->cursoron);
if (vid->mode & 0x20) {
cols[1] = vid->array[((attr & 15) & vid->array[1]) + 16] + 16;
cols[0] = vid->array[(((attr >> 4) & 7) & vid->array[1]) + 16] + 16;
if ((vid->blink & 16) && (attr & 0x80) && !drawcursor)
cols[1] = cols[0];
} else {
cols[1] = vid->array[((attr & 15) & vid->array[1]) + 16] + 16;
cols[0] = vid->array[((attr >> 4) & vid->array[1]) + 16] + 16;
}
vid->ma++;
if (vid->sc & 8) {
for (c = 0; c < 8; c++)
buffer32->line[vid->displine << 1][(x << 4) + (c << 1) + 8] = buffer32->line[vid->displine << 1][(x << 4) + (c << 1) + 1 + 8] = cols[0];
} else {
for (c = 0; c < 8; c++) {
if (vid->sc == 8) {
buffer32->line[vid->displine << 1][(x << 4) + (c << 1) + 8] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + (c << 1) + 8] = buffer32->line[vid->displine << 1][(x << 4) + (c << 1) + 1 + 8] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + (c << 1) + 1 + 8] = cols[(fontdat[chr][7] & (1 << (c ^ 7))) ? 1 : 0];
} else {
buffer32->line[vid->displine << 1][(x << 4) + (c << 1) + 8] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + (c << 1) + 8] = buffer32->line[vid->displine << 1][(x << 4) + (c << 1) + 1 + 8] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + (c << 1) + 1 + 8] = cols[(fontdat[chr][vid->sc & 7] & (1 << (c ^ 7))) ? 1 : 0];
}
}
}
if (drawcursor) {
for (c = 0; c < 16; c++) {
buffer32->line[vid->displine << 1][(x << 4) + c + 8] ^= 15;
buffer32->line[(vid->displine << 1) + 1][(x << 4) + c + 8] ^= 15;
}
}
}
} else if (!(vid->mode & 16)) {
cols[0] = (vid->col & 15);
col = (vid->col & 16) ? 8 : 0;
if (vid->mode & 4) {
cols[1] = col | 3;
cols[2] = col | 4;
cols[3] = col | 7;
} else if (vid->col & 32) {
cols[1] = col | 3;
cols[2] = col | 5;
cols[3] = col | 7;
} else {
cols[1] = col | 2;
cols[2] = col | 4;
cols[3] = col | 6;
}
cols[0] = vid->array[(cols[0] & vid->array[1]) + 16] + 16;
cols[1] = vid->array[(cols[1] & vid->array[1]) + 16] + 16;
cols[2] = vid->array[(cols[2] & vid->array[1]) + 16] + 16;
cols[3] = vid->array[(cols[3] & vid->array[1]) + 16] + 16;
for (x = 0; x < vid->crtc[1]; x++) {
dat = (vid->vram[((vid->ma << 1) & 0x1fff) + ((vid->sc & 1) * 0x2000)] << 8) | vid->vram[((vid->ma << 1) & 0x1fff) + ((vid->sc & 1) * 0x2000) + 1];
vid->ma++;
for (c = 0; c < 8; c++) {
buffer32->line[vid->displine << 1][(x << 4) + (c << 1) + 8] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + (c << 1) + 8] = buffer32->line[vid->displine << 1][(x << 4) + (c << 1) + 1 + 8] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + (c << 1) + 1 + 8] = cols[dat >> 14];
dat <<= 2;
}
}
} else {
cols[0] = 0;
cols[1] = vid->array[(vid->col & vid->array[1]) + 16] + 16;
for (x = 0; x < vid->crtc[1]; x++) {
dat = (vid->vram[((vid->ma << 1) & 0x1fff) + ((vid->sc & 1) * 0x2000)] << 8) | vid->vram[((vid->ma << 1) & 0x1fff) + ((vid->sc & 1) * 0x2000) + 1];
vid->ma++;
for (c = 0; c < 16; c++) {
buffer32->line[vid->displine << 1][(x << 4) + c + 8] = buffer32->line[(vid->displine << 1) + 1][(x << 4) + c + 8] = cols[dat >> 15];
dat <<= 1;
}
}
}
} else {
if (vid->array[3] & 4) {
if (vid->mode & 1) {
hline(buffer32, 0, (vid->displine << 1), (vid->crtc[1] << 3) + 16, (vid->array[2] & 0xf) + 16);
hline(buffer32, 0, (vid->displine << 1) + 1, (vid->crtc[1] << 3) + 16, (vid->array[2] & 0xf) + 16);
} else {
hline(buffer32, 0, (vid->displine << 1), (vid->crtc[1] << 4) + 16, (vid->array[2] & 0xf) + 16);
hline(buffer32, 0, (vid->displine << 1) + 1, (vid->crtc[1] << 4) + 16, (vid->array[2] & 0xf) + 16);
}
} else {
cols[0] = ((vid->mode & 0x12) == 0x12) ? 0 : (vid->col & 0xf) + 16;
if (vid->mode & 1) {
hline(buffer32, 0, (vid->displine << 1), (vid->crtc[1] << 3) + 16, cols[0]);
hline(buffer32, 0, (vid->displine << 1) + 1, (vid->crtc[1] << 3) + 16, cols[0]);
} else {
hline(buffer32, 0, (vid->displine << 1), (vid->crtc[1] << 4) + 16, cols[0]);
hline(buffer32, 0, (vid->displine << 1) + 1, (vid->crtc[1] << 4) + 16, cols[0]);
}
}
}
if (vid->mode & 1)
x = (vid->crtc[1] << 3) + 16;
else
x = (vid->crtc[1] << 4) + 16;
if (!dev->is_sl2 && vid->composite) {
Composite_Process(vid->mode, 0, x >> 2, buffer32->line[vid->displine << 1]);
Composite_Process(vid->mode, 0, x >> 2, buffer32->line[(vid->displine << 1) + 1]);
} else {
video_process_8(x, vid->displine << 1);
video_process_8(x, (vid->displine << 1) + 1);
}
vid->sc = oldsc;
if (vid->vc == vid->crtc[7] && !vid->sc)
vid->stat |= 8;
vid->displine++;
if (vid->displine >= 360)
vid->displine = 0;
} else {
timer_advance_u64(&vid->timer, vid->dispontime);
if (vid->dispon)
vid->stat &= ~1;
vid->linepos = 0;
if (vid->vsynctime) {
vid->vsynctime--;
if (!vid->vsynctime)
vid->stat &= ~8;
}
if (vid->sc == (vid->crtc[11] & 31) || ((vid->crtc[8] & 3) == 3 && vid->sc == ((vid->crtc[11] & 31) >> 1))) {
vid->con = 0;
}
if (vid->vadj) {
vid->sc++;
vid->sc &= 31;
vid->ma = vid->maback;
vid->vadj--;
if (!vid->vadj) {
vid->dispon = 1;
if (dev->is_sl2 && (vid->array[5] & 1))
vid->ma = vid->maback = vid->crtc[13] | (vid->crtc[12] << 8);
else
vid->ma = vid->maback = (vid->crtc[13] | (vid->crtc[12] << 8)) & 0x3fff;
vid->sc = 0;
}
} else if (vid->sc == vid->crtc[9] || ((vid->crtc[8] & 3) == 3 && vid->sc == (vid->crtc[9] >> 1))) {
vid->maback = vid->ma;
vid->sc = 0;
oldvc = vid->vc;
vid->vc++;
if (dev->is_sl2)
vid->vc &= 255;
else
vid->vc &= 127;
if (vid->vc == vid->crtc[6])
vid->dispon = 0;
if (oldvc == vid->crtc[4]) {
vid->vc = 0;
vid->vadj = vid->crtc[5];
if (!vid->vadj)
vid->dispon = 1;
if (!vid->vadj) {
if (dev->is_sl2 && (vid->array[5] & 1))
vid->ma = vid->maback = vid->crtc[13] | (vid->crtc[12] << 8);
else
vid->ma = vid->maback = (vid->crtc[13] | (vid->crtc[12] << 8)) & 0x3fff;
}
if ((vid->crtc[10] & 0x60) == 0x20)
vid->cursoron = 0;
else
vid->cursoron = vid->blink & 16;
}
if (vid->vc == vid->crtc[7]) {
vid->dispon = 0;
vid->displine = 0;
vid->vsynctime = 16;
picint(1 << 5);
if (vid->crtc[7]) {
if (vid->mode & 1)
x = (vid->crtc[1] << 3) + 16;
else
x = (vid->crtc[1] << 4) + 16;
vid->lastline++;
xs_temp = x;
ys_temp = (vid->lastline - vid->firstline) << 1;
if ((xs_temp > 0) && (ys_temp > 0)) {
if (xs_temp < 64)
xs_temp = 656;
if (ys_temp < 32)
ys_temp = 400;
if (!enable_overscan)
xs_temp -= 16;
if ((xs_temp != xsize) || (ys_temp != ysize) || video_force_resize_get()) {
xsize = xs_temp;
ysize = ys_temp;
set_screen_size(xsize, ysize + (enable_overscan ? 16 : 0));
if (video_force_resize_get())
video_force_resize_set(0);
}
if (enable_overscan) {
video_blit_memtoscreen(0, (vid->firstline - 4) << 1,
xsize, ((vid->lastline - vid->firstline) + 8) << 1);
} else {
video_blit_memtoscreen(8, vid->firstline << 1,
xsize, (vid->lastline - vid->firstline) << 1);
}
}
frames++;
video_res_x = xsize;
video_res_y = ysize;
if ((vid->array[3] & 0x10) && (vid->mode & 1)) { /*320x200x16*/
video_res_x /= 2;
video_bpp = 4;
} else if (vid->array[3] & 0x10) { /*160x200x16*/
video_res_x /= 4;
video_bpp = 4;
} else if (vid->array[3] & 0x08) { /*640x200x4 - this implementation is a complete guess!*/
video_bpp = 2;
} else if (vid->mode & 1) {
video_res_x /= 8;
video_res_y /= vid->crtc[9] + 1;
video_bpp = 0;
} else if (!(vid->mode & 2)) {
video_res_x /= 16;
video_res_y /= vid->crtc[9] + 1;
video_bpp = 0;
} else if (!(vid->mode & 16)) {
video_res_x /= 2;
video_bpp = 2;
} else {
video_bpp = 1;
}
}
vid->firstline = 1000;
vid->lastline = 0;
vid->blink++;
}
} else {
vid->sc++;
vid->sc &= 31;
vid->ma = vid->maback;
}
if (vid->sc == (vid->crtc[10] & 31) || ((vid->crtc[8] & 3) == 3 && vid->sc == ((vid->crtc[10] & 31) >> 1)))
vid->con = 1;
}
}
static void
vid_speed_changed(void *priv)
{
tandy_t *dev = (tandy_t *) priv;
recalc_timings(dev);
}
static void
vid_close(void *priv)
{
tandy_t *dev = (tandy_t *) priv;
free(dev->vid);
dev->vid = NULL;
}
static void
vid_init(tandy_t *dev)
{
int display_type;
t1kvid_t *vid;
vid = calloc(1, sizeof(t1kvid_t));
vid->memctrl = -1;
video_inform(VIDEO_FLAG_TYPE_CGA, &timing_dram);
display_type = device_get_config_int("display_type");
vid->composite = (display_type != TANDY_RGB);
cga_comp_init(1);
if (dev->is_sl2) {
vid->b8000_limit = 0x8000;
vid->planar_ctrl = 4;
overscan_x = overscan_y = 16;
io_sethandler(0x0065, 1, vid_in, NULL, NULL, vid_out, NULL, NULL, dev);
} else
vid->b8000_mask = 0x3fff;
timer_add(&vid->timer, vid_poll, dev, 1);
mem_mapping_add(&vid->mapping, 0xb8000, 0x08000,
vid_read, NULL, NULL, vid_write, NULL, NULL, NULL, 0, dev);
io_sethandler(0x03d0, 16,
vid_in, NULL, NULL, vid_out, NULL, NULL, dev);
dev->vid = vid;
}
const device_config_t vid_config[] = {
// clang-format off
{
.name = "display_type",
.description = "Display type",
.type = CONFIG_SELECTION,
.default_string = "",
.default_int = TANDY_RGB,
.file_filter = "",
.spinner = { 0 },
.selection = {
{ .description = "RGB", .value = TANDY_RGB },
{ .description = "Composite", .value = TANDY_COMPOSITE },
{ .description = "" }
}
},
{ .name = "", .description = "", .type = CONFIG_END }
// clang-format on
};
const device_t vid_device = {
.name = "Tandy 1000",
.internal_name = "tandy1000_video",
.flags = 0,
.local = 0,
.init = NULL,
.close = vid_close,
.reset = NULL,
.available = NULL,
.speed_changed = vid_speed_changed,
.force_redraw = NULL,
.config = vid_config
};
const device_t vid_device_hx = {
.name = "Tandy 1000 HX",
.internal_name = "tandy1000_hx_video",
.flags = 0,
.local = 0,
.init = NULL,
.close = vid_close,
.reset = NULL,
.available = NULL,
.speed_changed = vid_speed_changed,
.force_redraw = NULL,
.config = vid_config
};
const device_t vid_device_sl = {
.name = "Tandy 1000SL2",
.internal_name = "tandy1000_sl_video",
.flags = 0,
.local = 1,
.init = NULL,
.close = vid_close,
.reset = NULL,
.available = NULL,
.speed_changed = vid_speed_changed,
.force_redraw = NULL,
.config = NULL
};
static void
eep_write(UNUSED(uint16_t addr), uint8_t val, void *priv)
@@ -1630,12 +785,12 @@ tandy_write(uint16_t addr, uint8_t val, void *priv)
}
if (dev->is_hx) {
io_removehandler(0x03d0, 16,
vid_in, NULL, NULL, vid_out, NULL, NULL, dev);
tandy_vid_in, NULL, NULL, tandy_vid_out, NULL, NULL, dev);
if (val & 0x01)
mem_mapping_disable(&dev->vid->mapping);
else {
io_sethandler(0x03d0, 16,
vid_in, NULL, NULL, vid_out, NULL, NULL, dev);
tandy_vid_in, NULL, NULL, tandy_vid_out, NULL, NULL, dev);
mem_mapping_set_addr(&dev->vid->mapping, 0xb8000, 0x8000);
}
} else {
@@ -1654,7 +809,7 @@ tandy_write(uint16_t addr, uint8_t val, void *priv)
mem_mapping_set_addr(&dev->ram_mapping,
((val >> 1) & 7) * 128 * 1024,
0x20000);
recalc_address_sl(dev);
tandy_recalc_address_sl(dev);
dev->ram_bank = val;
break;
@@ -1803,10 +958,10 @@ machine_tandy1k_init(const machine_t *model, int type)
keyboard_set_table(scancode_tandy);
io_sethandler(0x00a0, 1,
tandy_read, NULL, NULL, tandy_write, NULL, NULL, dev);
device_context(&vid_device);
vid_init(dev);
device_context(&tandy_1000_video_device);
tandy_vid_init(dev);
device_context_restore();
device_add_ex(&vid_device, dev);
device_add_ex(&tandy_1000_video_device, dev);
device_add((type == TYPE_TANDY1000SX) ? &ncr8496_device : &sn76489_device);
break;
@@ -1815,10 +970,10 @@ machine_tandy1k_init(const machine_t *model, int type)
keyboard_set_table(scancode_tandy);
io_sethandler(0x00a0, 1,
tandy_read, NULL, NULL, tandy_write, NULL, NULL, dev);
device_context(&vid_device_hx);
vid_init(dev);
device_context(&tandy_1000hx_video_device);
tandy_vid_init(dev);
device_context_restore();
device_add_ex(&vid_device_hx, dev);
device_add_ex(&tandy_1000hx_video_device, dev);
device_add(&ncr8496_device);
device_add(&eep_1000hx_device);
break;
@@ -1828,10 +983,10 @@ machine_tandy1k_init(const machine_t *model, int type)
init_rom(dev);
io_sethandler(0xffe8, 8,
tandy_read, NULL, NULL, tandy_write, NULL, NULL, dev);
device_context(&vid_device_sl);
vid_init(dev);
device_context(&tandy_1000sl_video_device);
tandy_vid_init(dev);
device_context_restore();
device_add_ex(&vid_device_sl, dev);
device_add_ex(&tandy_1000sl_video_device, dev);
device_add(&pssj_device);
device_add(&eep_1000sl2_device);
break;

View File

@@ -1918,7 +1918,7 @@ m19_vid_out(uint16_t addr, uint8_t val, void *priv)
/* activating plantronics mode */
if (addr == 0x3dd) {
/* already in graphics mode */
if ((val & 0x30) && (vid->ogc.cga.cgamode & 0x2))
if ((val & 0x30) && (vid->ogc.cga.cgamode & CGA_MODE_FLAG_GRAPHICS))
vid->mode = PLANTRONICS_MODE;
else
vid->mode = OLIVETTI_OGC_MODE;

View File

@@ -1,761 +0,0 @@
/*
* 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.
*
* This file is part of the 86Box distribution.
*
* Implementation of the Toshiba T1000 plasma display, which
* has a fixed resolution of 640x200 pixels.
*
*
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* Miran Grca, <mgrca8@gmail.com>
* John Elliott, <jce@seasip.info>
*
* Copyright 2018-2019 Fred N. van Kempen.
* Copyright 2018-2019 Miran Grca.
* Copyright 2018-2019 John Elliott.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the:
*
* Free Software Foundation, Inc.
* 59 Temple Place - Suite 330
* Boston, MA 02111-1307
* USA.
*/
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include <86box/86box.h>
#include <86box/device.h>
#include <86box/io.h>
#include <86box/mem.h>
#include <86box/timer.h>
#include "cpu.h"
#include <86box/video.h>
#include <86box/vid_cga.h>
#include <86box/m_xt_t1000.h>
#include <86box/plat_unused.h>
#define T1000_XSIZE 640
#define T1000_YSIZE 200
/* Mapping of attributes to colours */
static uint32_t blue;
static uint32_t grey;
static uint8_t boldcols[256]; /* Which attributes use the bold font */
static uint32_t blinkcols[256][2];
static uint32_t normcols[256][2];
static uint8_t language;
static video_timings_t timing_t1000 = { VIDEO_ISA, 8, 16, 32, 8, 16, 32 };
/* Video options set by the motherboard; they will be picked up by the card
* on the next poll.
*
* Bit 1: Danish
* Bit 0: Thin font
*/
static uint8_t st_video_options;
static uint8_t st_enabled = 1;
static int8_t st_display_internal = -1;
void
t1000_video_options_set(uint8_t options)
{
st_video_options = options & 1;
st_video_options |= language;
}
void
t1000_video_enable(uint8_t enabled)
{
st_enabled = enabled;
}
void
t1000_display_set(uint8_t internal)
{
st_display_internal = (int8_t) internal;
}
uint8_t
t1000_display_get(void)
{
return (uint8_t) st_display_internal;
}
typedef struct t1000_t {
mem_mapping_t mapping;
cga_t cga; /* The CGA is used for the external
* display; most of its registers are
* ignored by the plasma display. */
int font; /* Current font, 0-3 */
int enabled; /* Hardware enabled, 0 or 1 */
int internal; /* Using internal display? */
uint8_t attrmap; /* Attribute mapping register */
uint64_t dispontime, dispofftime;
int linepos, displine;
int vc;
int dispon;
int vsynctime;
uint8_t video_options;
uint8_t backlight, invert;
uint8_t *vram;
} t1000_t;
static void t1000_recalctimings(t1000_t *t1000);
static void t1000_write(uint32_t addr, uint8_t val, void *priv);
static uint8_t t1000_read(uint32_t addr, void *priv);
static void t1000_recalcattrs(t1000_t *t1000);
static void
t1000_out(uint16_t addr, uint8_t val, void *priv)
{
t1000_t *t1000 = (t1000_t *) priv;
switch (addr) {
/* Emulated CRTC, register select */
case 0x3d0:
case 0x3d2:
case 0x3d4:
case 0x3d6:
cga_out(addr, val, &t1000->cga);
break;
/* Emulated CRTC, value */
case 0x3d1:
case 0x3d3:
case 0x3d5:
case 0x3d7:
/* Register 0x12 controls the attribute mappings for the
* LCD screen. */
if (t1000->cga.crtcreg == 0x12) {
t1000->attrmap = val;
t1000_recalcattrs(t1000);
return;
}
cga_out(addr, val, &t1000->cga);
t1000_recalctimings(t1000);
return;
case 0x3D8: /* CGA control register */
case 0x3D9: /* CGA colour register */
cga_out(addr, val, &t1000->cga);
return;
default:
break;
}
}
static uint8_t
t1000_in(uint16_t addr, void *priv)
{
t1000_t *t1000 = (t1000_t *) priv;
uint8_t val;
switch (addr) {
case 0x3d1:
case 0x3d3:
case 0x3d5:
case 0x3d7:
if (t1000->cga.crtcreg == 0x12) {
val = t1000->attrmap & 0x0F;
if (t1000->internal)
val |= 0x20; /* LCD / CRT */
return val;
}
break;
default:
break;
}
return cga_in(addr, &t1000->cga);
}
static void
t1000_write(uint32_t addr, uint8_t val, void *priv)
{
t1000_t *t1000 = (t1000_t *) priv;
t1000->vram[addr & 0x3fff] = val;
cycles -= 4;
}
static uint8_t
t1000_read(uint32_t addr, void *priv)
{
const t1000_t *t1000 = (t1000_t *) priv;
cycles -= 4;
return t1000->vram[addr & 0x3fff];
}
static void
t1000_recalctimings(t1000_t *t1000)
{
double disptime;
double _dispontime;
double _dispofftime;
if (!t1000->internal) {
cga_recalctimings(&t1000->cga);
return;
}
disptime = 651;
_dispontime = 640;
_dispofftime = disptime - _dispontime;
t1000->dispontime = (uint64_t) (_dispontime * xt_cpu_multi);
t1000->dispofftime = (uint64_t) (_dispofftime * xt_cpu_multi);
}
/* Draw a row of text in 80-column mode */
static void
t1000_text_row80(t1000_t *t1000)
{
uint32_t cols[2];
uint8_t chr;
uint8_t attr;
int drawcursor;
int cursorline;
int bold;
int blink;
uint16_t addr;
uint8_t sc;
uint16_t ma = (t1000->cga.crtc[13] | (t1000->cga.crtc[12] << 8)) & 0x3fff;
uint16_t ca = (t1000->cga.crtc[15] | (t1000->cga.crtc[14] << 8)) & 0x3fff;
sc = (t1000->displine) & 7;
addr = ((ma & ~1) + (t1000->displine >> 3) * 80) * 2;
ma += (t1000->displine >> 3) * 80;
if ((t1000->cga.crtc[10] & 0x60) == 0x20) {
cursorline = 0;
} else {
cursorline = ((t1000->cga.crtc[10] & 0x0F) <= sc) && ((t1000->cga.crtc[11] & 0x0F) >= sc);
}
for (uint8_t x = 0; x < 80; x++) {
chr = t1000->vram[(addr + 2 * x) & 0x3FFF];
attr = t1000->vram[(addr + 2 * x + 1) & 0x3FFF];
drawcursor = ((ma == ca) && cursorline && (t1000->cga.cgamode & 8) && (t1000->cga.cgablink & 16));
blink = ((t1000->cga.cgablink & 16) && (t1000->cga.cgamode & 0x20) && (attr & 0x80) && !drawcursor);
if (t1000->video_options & 1)
bold = boldcols[attr] ? chr : chr + 256;
else
bold = boldcols[attr] ? chr + 256 : chr;
if (t1000->video_options & 2)
bold += 512;
if (t1000->cga.cgamode & 0x20) /* Blink */
{
cols[1] = blinkcols[attr][1];
cols[0] = blinkcols[attr][0];
if (blink)
cols[1] = cols[0];
} else {
cols[1] = normcols[attr][1];
cols[0] = normcols[attr][0];
}
if (drawcursor) {
for (uint8_t c = 0; c < 8; c++) {
(buffer32->line[t1000->displine])[(x << 3) + c] = cols[(fontdat[bold][sc] & (1 << (c ^ 7))) ? 1 : 0] ^ (blue ^ grey);
}
} else {
for (uint8_t c = 0; c < 8; c++)
(buffer32->line[t1000->displine])[(x << 3) + c] = cols[(fontdat[bold][sc] & (1 << (c ^ 7))) ? 1 : 0];
}
++ma;
}
}
/* Draw a row of text in 40-column mode */
static void
t1000_text_row40(t1000_t *t1000)
{
uint32_t cols[2];
uint8_t chr;
uint8_t attr;
int drawcursor;
int cursorline;
int bold;
int blink;
uint16_t addr;
uint8_t sc;
uint16_t ma = (t1000->cga.crtc[13] | (t1000->cga.crtc[12] << 8)) & 0x3fff;
uint16_t ca = (t1000->cga.crtc[15] | (t1000->cga.crtc[14] << 8)) & 0x3fff;
sc = (t1000->displine) & 7;
addr = ((ma & ~1) + (t1000->displine >> 3) * 40) * 2;
ma += (t1000->displine >> 3) * 40;
if ((t1000->cga.crtc[10] & 0x60) == 0x20) {
cursorline = 0;
} else {
cursorline = ((t1000->cga.crtc[10] & 0x0F) <= sc) && ((t1000->cga.crtc[11] & 0x0F) >= sc);
}
for (uint8_t x = 0; x < 40; x++) {
chr = t1000->vram[(addr + 2 * x) & 0x3FFF];
attr = t1000->vram[(addr + 2 * x + 1) & 0x3FFF];
drawcursor = ((ma == ca) && cursorline && (t1000->cga.cgamode & 8) && (t1000->cga.cgablink & 16));
blink = ((t1000->cga.cgablink & 16) && (t1000->cga.cgamode & 0x20) && (attr & 0x80) && !drawcursor);
if (t1000->video_options & 1)
bold = boldcols[attr] ? chr : chr + 256;
else
bold = boldcols[attr] ? chr + 256 : chr;
if (t1000->video_options & 2)
bold += 512;
if (t1000->cga.cgamode & 0x20) /* Blink */
{
cols[1] = blinkcols[attr][1];
cols[0] = blinkcols[attr][0];
if (blink)
cols[1] = cols[0];
} else {
cols[1] = normcols[attr][1];
cols[0] = normcols[attr][0];
}
if (drawcursor) {
for (uint8_t c = 0; c < 8; c++) {
(buffer32->line[t1000->displine])[(x << 4) + c * 2] = (buffer32->line[t1000->displine])[(x << 4) + c * 2 + 1] = cols[(fontdat[bold][sc] & (1 << (c ^ 7))) ? 1 : 0] ^ (blue ^ grey);
}
} else {
for (uint8_t c = 0; c < 8; c++) {
(buffer32->line[t1000->displine])[(x << 4) + c * 2] = (buffer32->line[t1000->displine])[(x << 4) + c * 2 + 1] = cols[(fontdat[bold][sc] & (1 << (c ^ 7))) ? 1 : 0];
}
}
++ma;
}
}
/* Draw a line in CGA 640x200 mode */
static void
t1000_cgaline6(t1000_t *t1000)
{
uint8_t dat;
uint32_t ink = 0;
uint16_t addr;
uint32_t fg = (t1000->cga.cgacol & 0x0F) ? blue : grey;
uint32_t bg = grey;
uint16_t ma = (t1000->cga.crtc[13] | (t1000->cga.crtc[12] << 8)) & 0x3fff;
addr = ((t1000->displine) & 1) * 0x2000 + (t1000->displine >> 1) * 80 + ((ma & ~1) << 1);
for (uint8_t x = 0; x < 80; x++) {
dat = t1000->vram[addr & 0x3FFF];
addr++;
for (uint8_t c = 0; c < 8; c++) {
ink = (dat & 0x80) ? fg : bg;
if (!(t1000->cga.cgamode & 8))
ink = grey;
(buffer32->line[t1000->displine])[x * 8 + c] = ink;
dat = dat << 1;
}
}
}
/* Draw a line in CGA 320x200 mode. Here the CGA colours are converted to
* dither patterns: colour 1 to 25% grey, colour 2 to 50% grey */
static void
t1000_cgaline4(t1000_t *t1000)
{
uint8_t dat;
uint8_t pattern;
uint32_t ink0;
uint32_t ink1;
uint16_t addr;
uint16_t ma = (t1000->cga.crtc[13] | (t1000->cga.crtc[12] << 8)) & 0x3fff;
addr = ((t1000->displine) & 1) * 0x2000 + (t1000->displine >> 1) * 80 + ((ma & ~1) << 1);
for (uint8_t x = 0; x < 80; x++) {
dat = t1000->vram[addr & 0x3FFF];
addr++;
for (uint8_t c = 0; c < 4; c++) {
pattern = (dat & 0xC0) >> 6;
if (!(t1000->cga.cgamode & 8))
pattern = 0;
switch (pattern & 3) {
default:
case 0:
ink0 = ink1 = grey;
break;
case 1:
if (t1000->displine & 1) {
ink0 = grey;
ink1 = grey;
} else {
ink0 = blue;
ink1 = grey;
}
break;
case 2:
if (t1000->displine & 1) {
ink0 = grey;
ink1 = blue;
} else {
ink0 = blue;
ink1 = grey;
}
break;
case 3:
ink0 = ink1 = blue;
break;
}
(buffer32->line[t1000->displine])[x * 8 + 2 * c] = ink0;
(buffer32->line[t1000->displine])[x * 8 + 2 * c + 1] = ink1;
dat = dat << 2;
}
}
}
static void
t1000_poll(void *priv)
{
t1000_t *t1000 = (t1000_t *) priv;
if (t1000->video_options != st_video_options || t1000->enabled != st_enabled) {
t1000->video_options = st_video_options;
t1000->enabled = st_enabled;
/* Set the font used for the external display */
t1000->cga.fontbase = ((t1000->video_options & 3) * 256);
if (t1000->enabled) /* Disable internal chipset */
mem_mapping_enable(&t1000->mapping);
else
mem_mapping_disable(&t1000->mapping);
}
/* Switch between internal plasma and external CRT display. */
if (st_display_internal != -1 && st_display_internal != t1000->internal) {
t1000->internal = st_display_internal;
t1000_recalctimings(t1000);
}
if (!t1000->internal) {
cga_poll(&t1000->cga);
return;
}
if (!t1000->linepos) {
timer_advance_u64(&t1000->cga.timer, t1000->dispofftime);
t1000->cga.cgastat |= 1;
t1000->linepos = 1;
if (t1000->dispon) {
if (t1000->displine == 0) {
video_wait_for_buffer();
}
/* Graphics */
if (t1000->cga.cgamode & 0x02) {
if (t1000->cga.cgamode & 0x10)
t1000_cgaline6(t1000);
else
t1000_cgaline4(t1000);
} else if (t1000->cga.cgamode & 0x01) /* High-res text */
{
t1000_text_row80(t1000);
} else {
t1000_text_row40(t1000);
}
}
t1000->displine++;
/* Hardcode a fixed refresh rate and VSYNC timing */
if (t1000->displine == 200) /* Start of VSYNC */
{
t1000->cga.cgastat |= 8;
t1000->dispon = 0;
}
if (t1000->displine == 216) /* End of VSYNC */
{
t1000->displine = 0;
t1000->cga.cgastat &= ~8;
t1000->dispon = 1;
}
} else {
if (t1000->dispon) {
t1000->cga.cgastat &= ~1;
}
timer_advance_u64(&t1000->cga.timer, t1000->dispontime);
t1000->linepos = 0;
if (t1000->displine == 200) {
/* Hardcode 640x200 window size */
if ((T1000_XSIZE != xsize) || (T1000_YSIZE != ysize) || video_force_resize_get()) {
xsize = T1000_XSIZE;
ysize = T1000_YSIZE;
if (xsize < 64)
xsize = 656;
if (ysize < 32)
ysize = 200;
set_screen_size(xsize, ysize);
if (video_force_resize_get())
video_force_resize_set(0);
}
video_blit_memtoscreen(0, 0, xsize, ysize);
frames++;
/* Fixed 640x200 resolution */
video_res_x = T1000_XSIZE;
video_res_y = T1000_YSIZE;
if (t1000->cga.cgamode & 0x02) {
if (t1000->cga.cgamode & 0x10)
video_bpp = 1;
else
video_bpp = 2;
} else
video_bpp = 0;
t1000->cga.cgablink++;
}
}
}
static void
t1000_recalcattrs(t1000_t *t1000)
{
int n;
/* val behaves as follows:
* Bit 0: Attributes 01-06, 08-0E are inverse video
* Bit 1: Attributes 01-06, 08-0E are bold
* Bit 2: Attributes 11-16, 18-1F, 21-26, 28-2F ... F1-F6, F8-FF
* are inverse video
* Bit 3: Attributes 11-16, 18-1F, 21-26, 28-2F ... F1-F6, F8-FF
* are bold */
/* Set up colours */
if (t1000->invert) {
if (t1000->backlight) {
grey = makecol(0x2D, 0x39, 0x5A);
blue = makecol(0x85, 0xa0, 0xD6);
} else {
grey = makecol(0x0f, 0x21, 0x3f);
blue = makecol(0x1C, 0x71, 0x31);
}
} else {
if (t1000->backlight) {
blue = makecol(0x2D, 0x39, 0x5A);
grey = makecol(0x85, 0xa0, 0xD6);
} else {
blue = makecol(0x0f, 0x21, 0x3f);
grey = makecol(0x1C, 0x71, 0x31);
}
}
/* Initialise the attribute mapping. Start by defaulting everything
* to grey on blue, and with bold set by bit 3 */
for (n = 0; n < 256; n++) {
boldcols[n] = (n & 8) != 0;
blinkcols[n][0] = normcols[n][0] = blue;
blinkcols[n][1] = normcols[n][1] = grey;
}
/* Colours 0x11-0xFF are controlled by bits 2 and 3 of the
* passed value. Exclude x0 and x8, which are always grey on
* blue. */
for (n = 0x11; n <= 0xFF; n++) {
if ((n & 7) == 0)
continue;
if (t1000->attrmap & 4) /* Inverse */
{
blinkcols[n][0] = normcols[n][0] = blue;
blinkcols[n][1] = normcols[n][1] = grey;
} else /* Normal */
{
blinkcols[n][0] = normcols[n][0] = grey;
blinkcols[n][1] = normcols[n][1] = blue;
}
if (t1000->attrmap & 8)
boldcols[n] = 1; /* Bold */
}
/* Set up the 01-0E range, controlled by bits 0 and 1 of the
* passed value. When blinking is enabled this also affects 81-8E. */
for (n = 0x01; n <= 0x0E; n++) {
if (n == 7)
continue;
if (t1000->attrmap & 1) {
blinkcols[n][0] = normcols[n][0] = blue;
blinkcols[n][1] = normcols[n][1] = grey;
blinkcols[n + 128][0] = blue;
blinkcols[n + 128][1] = grey;
} else {
blinkcols[n][0] = normcols[n][0] = grey;
blinkcols[n][1] = normcols[n][1] = blue;
blinkcols[n + 128][0] = grey;
blinkcols[n + 128][1] = blue;
}
if (t1000->attrmap & 2)
boldcols[n] = 1;
}
/* Colours 07 and 0F are always blue on grey. If blinking is
* enabled so are 87 and 8F. */
for (n = 0x07; n <= 0x0F; n += 8) {
blinkcols[n][0] = normcols[n][0] = grey;
blinkcols[n][1] = normcols[n][1] = blue;
blinkcols[n + 128][0] = grey;
blinkcols[n + 128][1] = blue;
}
/* When not blinking, colours 81-8F are always blue on grey. */
for (n = 0x81; n <= 0x8F; n++) {
normcols[n][0] = grey;
normcols[n][1] = blue;
boldcols[n] = (n & 0x08) != 0;
}
/* Finally do the ones which are solid grey. These differ between
* the normal and blinking mappings */
for (n = 0; n <= 0xFF; n += 0x11) {
normcols[n][0] = normcols[n][1] = grey;
}
/* In the blinking range, 00 11 22 .. 77 and 80 91 A2 .. F7 are grey */
for (n = 0; n <= 0x77; n += 0x11) {
blinkcols[n][0] = blinkcols[n][1] = grey;
blinkcols[n + 128][0] = blinkcols[n + 128][1] = grey;
}
}
static void *
t1000_init(UNUSED(const device_t *info))
{
t1000_t *t1000 = calloc(1, sizeof(t1000_t));
loadfont("roms/machines/t1000/t1000font.bin", 8);
cga_init(&t1000->cga);
video_inform(VIDEO_FLAG_TYPE_CGA, &timing_t1000);
t1000->internal = 1;
t1000->backlight = device_get_config_int("backlight");
t1000->invert = device_get_config_int("invert");
/* 16k video RAM */
t1000->vram = malloc(0x4000);
timer_set_callback(&t1000->cga.timer, t1000_poll);
timer_set_p(&t1000->cga.timer, t1000);
/* Occupy memory between 0xB8000 and 0xBFFFF */
mem_mapping_add(&t1000->mapping, 0xb8000, 0x8000, t1000_read, NULL, NULL, t1000_write, NULL, NULL, NULL, 0, t1000);
/* Respond to CGA I/O ports */
io_sethandler(0x03d0, 0x000c, t1000_in, NULL, NULL, t1000_out, NULL, NULL, t1000);
/* Default attribute mapping is 4 */
t1000->attrmap = 4;
t1000_recalcattrs(t1000);
/* Start off in 80x25 text mode */
t1000->cga.cgastat = 0xF4;
t1000->cga.vram = t1000->vram;
t1000->enabled = 1;
t1000->video_options = 0x01;
language = device_get_config_int("display_language") ? 2 : 0;
return t1000;
}
static void
t1000_close(void *priv)
{
t1000_t *t1000 = (t1000_t *) priv;
free(t1000->vram);
free(t1000);
}
static void
t1000_speed_changed(void *priv)
{
t1000_t *t1000 = (t1000_t *) priv;
t1000_recalctimings(t1000);
}
static const device_config_t t1000_config[] = {
// clang-format off
{
.name = "display_language",
.description = "Language",
.type = CONFIG_SELECTION,
.selection = {
{ .description = "USA", .value = 0 },
{ .description = "Danish", .value = 1 }
},
.default_int = 0
},
{
.name = "backlight",
.description = "Enable backlight",
.type = CONFIG_BINARY,
.default_string = "",
.default_int = 1
},
{
.name = "invert",
.description = "Invert colors",
.type = CONFIG_BINARY,
.default_string = "",
.default_int = 0
},
{ .name = "", .description = "", .type = CONFIG_END }
// clang-format on
};
const device_t t1000_video_device = {
.name = "Toshiba T1000 Video",
.internal_name = "t1000_video",
.flags = 0,
.local = 0,
.init = t1000_init,
.close = t1000_close,
.reset = NULL,
.available = NULL,
.speed_changed = t1000_speed_changed,
.force_redraw = NULL,
.config = t1000_config
};
const device_t t1200_video_device = {
.name = "Toshiba T1200 Video",
.internal_name = "t1200_video",
.flags = 0,
.local = 0,
.init = t1000_init,
.close = t1000_close,
.reset = NULL,
.available = NULL,
.speed_changed = t1000_speed_changed,
.force_redraw = NULL,
.config = t1000_config
};

View File

@@ -39,8 +39,10 @@
// Temporarily here till we move everything out into the right files
extern const device_t pcjr_device;
extern const device_t m19_vid_device;
extern const device_t vid_device;
extern const device_t vid_device_hx;
extern const device_t tandy_1000_video_device;
extern const device_t tandy_1000hx_video_device;
extern const device_t tandy_1000sl_video_device;
extern const device_t t1000_video_device;
extern const device_t xi8088_device;
extern const device_t cga_device;
@@ -50,7 +52,6 @@ extern const device_t vid_pc2086_device;
extern const device_t vid_pc3086_device;
extern const device_t vid_200_device;
extern const device_t vid_ppc512_device;
extern const device_t vid_device_sl;
extern const device_t t1200_video_device;
extern const device_t compaq_plasma_device;
extern const device_t ps1_2011_device;
@@ -1591,7 +1592,7 @@ const machine_t machines[] = {
.device = NULL,
.fdc_device = NULL,
.sio_device = NULL,
.vid_device = &vid_device,
.vid_device = &tandy_1000_video_device,
.snd_device = NULL,
.net_device = NULL
},
@@ -1630,7 +1631,7 @@ const machine_t machines[] = {
.device = NULL,
.fdc_device = NULL,
.sio_device = NULL,
.vid_device = &vid_device_hx,
.vid_device = &tandy_1000hx_video_device,
.snd_device = NULL,
.net_device = NULL
},
@@ -2531,7 +2532,7 @@ const machine_t machines[] = {
.device = NULL,
.fdc_device = NULL,
.sio_device = NULL,
.vid_device = &vid_device_sl,
.vid_device = &tandy_1000sl_video_device,
.snd_device = NULL,
.net_device = NULL
},