Files
86Box/src/video/vid_im1024.c
TC1995 6802c0593b Video, Storage and MCA changes/fixes.
1. Cirrus Logic GD54xx, Paradise/WD VGA now reset the interlace once a text mode is issued if not done automatically.
2. Paradise/WD's 15/16bpp modes using the 800x600 resolution now have the correct ma_latch, should fix most operating systems drivers using this combo.
3. More fixes (hopefully) to the accelerated pitch and rowoffset of the Trident TGUI cards (9440AGi and 96x0XGi), should fix issues with delayed displays mode changes under various operating systems (e.g.: Win3.1x).
4. Preliminary implementation of the Area Fill command of XGA, which is issued while using various painting and/or calc utilities on Win3.1x (IBM XGA updated drivers, e.g.: 2.12).
5. Preliminary (and incomplete) 4bpp XGA mode.
6. The XGA memory test for the 0xa5 using writes (used by various operating systems) no longer conflicts with DOS' XGAKIT's memory detection.
7. Small ROP fixes to both XGA and 8514/A.
8. Re-organized the mapping of the Mach32 chipset, especially when to enable the ATI mode or switching back to IBM mode, should fix LFB conflicts with various operating systems.
9. According to The OS/2 Museum, the Adaptec AHA-154xB series of SCSI cards fail the ASPI4DOS.SYS 3.36 signature check, so now make the changes accordingly.
10. Remove useless and crashy bios-less option of the Trantor T128.
11. The Image Manager 1024 card can also be used on a XT (although only if it has a V20/V30).
12. Re-organized the IBM PS/2 model 60 initialization as well as its right POS machine ID (though an update to sc.exe is still required for the POST memory amount to work normally).
2023-09-30 22:08:08 +02:00

1098 lines
27 KiB
C

/*
* 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.
*
* Emulation of the ImageManager 1024 video controller.
*
* Just enough of the Vermont Microsystems IM-1024 is implemented
* to support the Windows 1.03 driver. Functions are partially
* implemented or hardwired to the behavior expected by the
* Windows driver.
*
* One major difference seems to be that in hex mode, coordinates
* are passed as 2-byte integer words rather than 4-byte
* fixed-point fractions.
*
* It is unknown what triggers this, so for now it's always on.
*
* As well as the usual PGC ring buffer at 0xC6000, the IM1024
* appears to have an alternate method of passing commands. This
* is enabled by setting 0xC6330 to 1, and then:
*
* CX = count to write
* SI -> bytes to write
*
* Set pending bytes to 0
* Read [C6331]. This gives number of bytes that can be written:
* 0xFF => 0, 0xFE => 1, 0xFD => 2 etc.
* Write that number of bytes to C6000.
* If there are more to come, go back to reading [C6331].
*
* As far as can be determined, at least one byte is always
* written; there is no provision to pause if the queue is full.
*
* This is implemented by holding a FIFO of unlimited depth in
* the IM1024 to receive the data.
*
*
*
* Authors: Fred N. van Kempen, <decwiz@yahoo.com>
* John Elliott, <jce@seasip.info>
*
* Copyright 2019 Fred N. van Kempen.
* Copyright 2019 John Elliott.
*/
#include <stdarg.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include <ctype.h>
#define HAVE_STDARG_H
#include <86box/86box.h>
#include <86box/io.h>
#include <86box/mem.h>
#include <86box/rom.h>
#include <86box/timer.h>
#include <86box/device.h>
#include <86box/pit.h>
#include <86box/plat.h>
#include <86box/thread.h>
#include <86box/video.h>
#include <86box/vid_pgc.h>
#define BIOS_ROM_PATH "roms/video/im1024/im1024font.bin"
typedef struct {
pgc_t pgc;
uint8_t fontx[256];
uint8_t fonty[256];
uint8_t font[256][128];
uint8_t *fifo;
unsigned fifo_len,
fifo_wrptr,
fifo_rdptr;
} im1024_t;
static video_timings_t timing_im1024 = { .type = VIDEO_ISA, .write_b = 8, .write_w = 16, .write_l = 32, .read_b = 8, .read_w = 16, .read_l = 32 };
#ifdef ENABLE_IM1024_LOG
int im1024_do_log = ENABLE_IM1024_LOG;
static void
im1024_log(const char *fmt, ...)
{
va_list ap;
if (im1024_do_log) {
va_start(ap, fmt);
pclog_ex(fmt, ap);
va_end(ap);
}
}
#else
# define im1024_log(fmt, ...)
#endif
static void
fifo_write(im1024_t *dev, uint8_t val)
{
im1024_log("IM1024: fifo_write: %02x [rd=%04x wr=%04x]\n",
val, dev->fifo_rdptr, dev->fifo_wrptr);
if (((dev->fifo_wrptr + 1) % dev->fifo_len) == dev->fifo_rdptr) {
/* FIFO is full. Double its size. */
uint8_t *buf;
im1024_log("IM1024: fifo_resize: %i to %i\n",
dev->fifo_len, 2 * dev->fifo_len);
buf = realloc(dev->fifo, 2 * dev->fifo_len);
if (buf == NULL)
return;
/* Move the [0..wrptr] range to the newly-allocated area [len..len+wrptr] */
memmove(buf + dev->fifo_len, buf, dev->fifo_wrptr);
dev->fifo = buf;
dev->fifo_wrptr += dev->fifo_len;
dev->fifo_len *= 2;
}
/* Append to the queue. */
dev->fifo[dev->fifo_wrptr++] = val;
/* Wrap if end of buffer reached. */
if (dev->fifo_wrptr >= dev->fifo_len)
dev->fifo_wrptr = 0;
}
static int
fifo_read(im1024_t *dev)
{
uint8_t ret;
if (dev->fifo_wrptr == dev->fifo_rdptr)
return -1; /* FIFO empty */
ret = dev->fifo[dev->fifo_rdptr++];
if (dev->fifo_rdptr >= dev->fifo_len)
dev->fifo_rdptr = 0;
im1024_log("IM1024: fifo_read: %02x\n", ret);
return ret;
}
/*
* Where a normal PGC would just read from the ring buffer at 0xC6300,
* the IM-1024 can read from either this or from its internal FIFO.
*
* The internal FIFO has priority.
*/
static int
input_byte(pgc_t *pgc, uint8_t *result)
{
im1024_t *dev = (im1024_t *) pgc;
/* If input buffer empty, wait for it to fill. */
while (!pgc->stopped && (dev->fifo_wrptr == dev->fifo_rdptr) && (pgc->mapram[0x300] == pgc->mapram[0x301])) {
pgc->waiting_input_fifo = 1;
pgc_sleep(pgc);
}
if (pgc->stopped)
return 0;
if (pgc->mapram[0x3ff]) {
/* Reset triggered. */
pgc_reset(pgc);
return 0;
}
if (dev->fifo_wrptr == dev->fifo_rdptr) {
*result = pgc->mapram[pgc->mapram[0x301]];
pgc->mapram[0x301]++;
} else
*result = fifo_read(dev);
return 1;
}
/* Macros to disable clipping and save clip state. */
#define PUSHCLIP \
{ \
uint16_t vp_x1, vp_x2, vp_y1, vp_y2; \
vp_x1 = pgc->vp_x1; \
vp_y1 = pgc->vp_y1; \
vp_x2 = pgc->vp_x2; \
vp_y2 = pgc->vp_y2; \
pgc->vp_x1 = 0; \
pgc->vp_y1 = 0; \
pgc->vp_x2 = pgc->maxw - 1; \
pgc->vp_y2 = pgc->maxh - 1;
/* And to restore clip state */
#define POPCLIP \
pgc->vp_x1 = vp_x1; \
pgc->vp_y1 = vp_y1; \
pgc->vp_x2 = vp_x2; \
pgc->vp_y2 = vp_y2; \
}
/* Override memory read to return FIFO space. */
static uint8_t
im1024_read(uint32_t addr, void *priv)
{
im1024_t *dev = (im1024_t *) priv;
if (addr == 0xc6331 && dev->pgc.mapram[0x330] == 1) {
/* Hardcode that there are 128 bytes free. */
return 0x80;
}
return (pgc_read(addr, &dev->pgc));
}
/* Override memory write to handle writes to the FIFO. */
static void
im1024_write(uint32_t addr, uint8_t val, void *priv)
{
im1024_t *dev = (im1024_t *) priv;
/*
* If we are in 'fast' input mode, send all
* writes to the internal FIFO.
*/
if (addr >= 0xc6000 && addr < 0xc6100 && dev->pgc.mapram[0x330] == 1) {
fifo_write(dev, val);
im1024_log("IM1024: write(%02x)\n", val);
if (dev->pgc.waiting_input_fifo) {
dev->pgc.waiting_input_fifo = 0;
pgc_wake(&dev->pgc);
}
return;
}
pgc_write(addr, val, &dev->pgc);
}
/*
* I don't know what the IMGSIZ command does, only that the
* Windows driver issues it. So just parse and ignore it.
*/
static void
hndl_imgsiz(pgc_t *pgc)
{
#if 0
im1024_t *dev = (im1024_t *)pgc;
#endif
int16_t w;
int16_t h;
uint8_t a;
uint8_t b;
if (!pgc_param_word(pgc, &w))
return;
if (!pgc_param_word(pgc, &h))
return;
if (!pgc_param_byte(pgc, &a))
return;
if (!pgc_param_byte(pgc, &b))
return;
im1024_log("IM1024: IMGSIZ %i,%i,%i,%i\n", w, h, a, b);
}
/*
* I don't know what the IPREC command does, only that the
* Windows driver issues it. So just parse and ignore it.
*/
static void
hndl_iprec(pgc_t *pgc)
{
#if 0
im1024_t *dev = (im1024_t *)pgc;
#endif
uint8_t param;
if (!pgc_param_byte(pgc, &param))
return;
im1024_log("IM1024: IPREC %i\n", param);
}
/*
* Set drawing mode.
*
* 0 => Draw
* 1 => Invert
* 2 => XOR (IM-1024)
* 3 => AND (IM-1024)
*/
static void
hndl_linfun(pgc_t *pgc)
{
uint8_t param;
if (!pgc_param_byte(pgc, &param))
return;
if (param < 4) {
pgc->draw_mode = param;
im1024_log("IM1024: LINFUN(%i)\n", param);
} else
pgc_error(pgc, PGC_ERROR_RANGE);
}
/*
* I think PAN controls which part of the 1024x1024 framebuffer
* is displayed in the 1024x800 visible screen.
*/
static void
hndl_pan(pgc_t *pgc)
{
int16_t x;
int16_t y;
if (!pgc_param_word(pgc, &x))
return;
if (!pgc_param_word(pgc, &y))
return;
im1024_log("IM1024: PAN %i,%i\n", x, y);
pgc->pan_x = x;
pgc->pan_y = y;
}
/* PLINE draws a non-filled polyline at a fixed position. */
static void
hndl_pline(pgc_t *pgc)
{
int16_t x[257];
int16_t y[257];
uint16_t linemask = pgc->line_pattern;
uint8_t count;
unsigned n;
if (!pgc_param_byte(pgc, &count))
return;
im1024_log("IM1024: PLINE (%i) ", count);
for (n = 0; n < count; n++) {
if (!pgc_param_word(pgc, &x[n]))
return;
if (!pgc_param_word(pgc, &y[n]))
return;
im1024_log(" (%i,%i)\n", x[n], y[n]);
}
for (n = 1; n < count; n++) {
linemask = pgc_draw_line(pgc, x[n - 1] << 16, y[n - 1] << 16,
x[n] << 16, y[n] << 16, linemask);
}
}
/*
* Blit a single row of pixels from one location to another.
*
* To avoid difficulties if the two overlap, read both rows
* into memory, process them there, and write the result back.
*/
static void
blkmov_row(pgc_t *pgc, int16_t x0, int16_t x1, int16_t x2, int16_t sy, int16_t ty)
{
uint8_t src[1024];
uint8_t dst[1024];
int16_t x;
for (x = x0; x <= x1; x++) {
src[x - x0] = pgc_read_pixel(pgc, x, sy);
dst[x - x0] = pgc_read_pixel(pgc, x - x0 + x2, ty);
}
for (x = x0; x <= x1; x++)
switch (pgc->draw_mode) {
default:
case 0:
pgc_write_pixel(pgc, (x - x0 + x2), ty, src[x - x0]);
break;
case 1:
pgc_write_pixel(pgc, (x - x0 + x2), ty, dst[x - x0] ^ 0xff);
break;
case 2:
pgc_write_pixel(pgc, (x - x0 + x2), ty, src[x - x0] ^ dst[x - x0]);
break;
case 3:
pgc_write_pixel(pgc, (x - x0 + x2), ty, src[x - x0] & dst[x - x0]);
break;
}
}
/*
* BLKMOV blits a rectangular area from one location to another.
*
* Clipping is disabled.
*/
static void
hndl_blkmov(pgc_t *pgc)
{
int16_t x0;
int16_t y0;
int16_t x1;
int16_t y1;
int16_t x2;
int16_t y2;
int16_t y;
if (!pgc_param_word(pgc, &x0))
return;
if (!pgc_param_word(pgc, &y0))
return;
if (!pgc_param_word(pgc, &x1))
return;
if (!pgc_param_word(pgc, &y1))
return;
if (!pgc_param_word(pgc, &x2))
return;
if (!pgc_param_word(pgc, &y2))
return;
im1024_log("IM1024: BLKMOV %i,%i,%i,%i,%i,%i\n", x0, y0, x1, y1, x2, y2);
/* Disable clipping. */
PUSHCLIP
/*
* Either go down from the top, or up from the bottom,
* depending whether areas might overlap.
*/
if (y2 <= y0) {
for (y = y0; y <= y1; y++)
blkmov_row(pgc, x0, x1, x2, y, y - y0 + y2);
} else {
for (y = y1; y >= y0; y--)
blkmov_row(pgc, x0, x1, x2, y, y - y0 + y2);
}
/* Restore clipping. */
POPCLIP
}
/*
* Override the PGC ELIPSE command to parse its
* parameters as words rather than coordinates.
*/
static void
hndl_ellipse(pgc_t *pgc)
{
int16_t x;
int16_t y;
if (!pgc_param_word(pgc, &x))
return;
if (!pgc_param_word(pgc, &y))
return;
im1024_log("IM1024: ELLIPSE %i,%i @ %i,%i\n",
x, y, pgc->x >> 16, pgc->y >> 16);
pgc_draw_ellipse(pgc, x << 16, y << 16);
}
/*
* Override the PGC MOVE command to parse its
* parameters as words rather than coordinates.
*/
static void
hndl_move(pgc_t *pgc)
{
int16_t x;
int16_t y;
if (!pgc_param_word(pgc, &x))
return;
if (!pgc_param_word(pgc, &y))
return;
im1024_log("IM1024: MOVE %i,%i\n", x, y);
pgc->x = x << 16;
pgc->y = y << 16;
}
/*
* Override the PGC DRAW command to parse its
* parameters as words rather than coordinates.
*/
static void
hndl_draw(pgc_t *pgc)
{
int16_t x;
int16_t y;
if (!pgc_param_word(pgc, &x))
return;
if (!pgc_param_word(pgc, &y))
return;
im1024_log("IM1024: DRAW %i,%i to %i,%i\n", pgc->x >> 16, pgc->y >> 16, x, y);
pgc_draw_line(pgc, pgc->x, pgc->y, x << 16, y << 16, pgc->line_pattern);
pgc->x = x << 16;
pgc->y = y << 16;
}
/*
* Override the PGC POLY command to parse its
* parameters as words rather than coordinates.
*/
static void
hndl_poly(pgc_t *pgc)
{
int32_t *x;
int32_t *y;
int32_t *nx;
int32_t *ny;
int16_t xw;
int16_t yw;
int16_t mask;
unsigned realcount = 0;
unsigned n;
unsigned as = 256;
int parsing = 1;
uint8_t count;
x = (int32_t *) malloc(as * sizeof(int32_t));
y = (int32_t *) malloc(as * sizeof(int32_t));
if (!x || !y) {
#ifdef ENABLE_IM1024_LOG
im1024_log("IM1024: POLY: out of memory\n");
#endif
if (x)
free(x);
if (y)
free(y);
return;
}
while (parsing) {
if (!pgc_param_byte(pgc, &count)) {
if (x)
free(x);
if (y)
free(y);
return;
}
if (count + realcount >= as) {
nx = (int32_t *) realloc(x, 2 * as * sizeof(int32_t));
ny = (int32_t *) realloc(y, 2 * as * sizeof(int32_t));
if (!x || !y) {
#ifdef ENABLE_IM1024_LOG
im1024_log("IM1024: poly: realloc failed\n");
#endif
break;
}
x = nx;
y = ny;
as *= 2;
}
for (n = 0; n < count; n++) {
if (!pgc_param_word(pgc, &xw)) {
if (x)
free(x);
if (y)
free(y);
return;
}
if (!pgc_param_word(pgc, &yw)) {
if (x)
free(x);
if (y)
free(y);
return;
}
/* Skip degenerate line segments. */
if (realcount > 0 && (xw << 16) == x[realcount - 1] && (yw << 16) == y[realcount - 1])
continue;
x[realcount] = xw << 16;
y[realcount] = yw << 16;
realcount++;
}
/*
* If we are in a command list, peek ahead to see if the next
* command is also POLY. If so, that's a continuation of this
* polygon!
*/
parsing = 0;
if (pgc->clcur && (pgc->clcur->rdptr + 1) < pgc->clcur->wrptr && pgc->clcur->list[pgc->clcur->rdptr] == 0x30) {
#ifdef ENABLE_IM1024_LOG
im1024_log("IM1024: POLY continues!\n");
#endif
parsing = 1;
/* Swallow the POLY. */
pgc->clcur->rdptr++;
}
}
im1024_log("IM1024: POLY (%i) fill_mode=%i\n", realcount, pgc->fill_mode);
#ifdef ENABLE_IM1024_LOG
for (n = 0; n < realcount; n++) {
im1024_log(" (%i,%i)\n", x[n] >> 16, y[n] >> 16);
}
#endif
if (pgc->fill_mode)
pgc_fill_polygon(pgc, realcount, x, y);
/* Now draw borders. */
mask = pgc->line_pattern;
for (n = 1; n < realcount; n++)
mask = pgc_draw_line(pgc, x[n - 1], y[n - 1], x[n], y[n], mask);
pgc_draw_line(pgc, x[realcount - 1], y[realcount - 1], x[0], y[0], mask);
free(y);
free(x);
}
static int
parse_poly(pgc_t *pgc, pgc_cl_t *cl, UNUSED(int c))
{
uint8_t count;
#ifdef ENABLE_IM1024_LOG
im1024_log("IM1024: parse_poly\n");
#endif
if (!pgc_param_byte(pgc, &count))
return 0;
im1024_log("IM1024: parse_poly: count=%02x\n", count);
if (!pgc_cl_append(cl, count)) {
pgc_error(pgc, PGC_ERROR_OVERFLOW);
return 0;
}
im1024_log("IM1024: parse_poly: parse %i words\n", 2 * count);
return pgc_parse_words(pgc, cl, count * 2);
}
/*
* Override the PGC RECT command to parse its
* parameters as words rather than coordinates.
*/
static void
hndl_rect(pgc_t *pgc)
{
int16_t x0;
int16_t y0;
int16_t x1;
int16_t y1;
int16_t p;
int16_t q;
x0 = pgc->x >> 16;
y0 = pgc->y >> 16;
if (!pgc_param_word(pgc, &x1))
return;
if (!pgc_param_word(pgc, &y1))
return;
/* Convert to raster coords. */
pgc_sto_raster(pgc, &x0, &y0);
pgc_sto_raster(pgc, &x1, &y1);
if (x0 > x1) {
p = x0;
x0 = x1;
x1 = p;
}
if (y0 > y1) {
q = y0;
y0 = y1;
y1 = q;
}
im1024_log("IM1024: RECT (%i,%i) -> (%i,%i)\n", x0, y0, x1, y1);
if (pgc->fill_mode) {
for (p = y0; p <= y1; p++)
pgc_fill_line_r(pgc, x0, x1, p);
} else {
/* Outline: 4 lines. */
p = pgc->line_pattern;
p = pgc_draw_line_r(pgc, x0, y0, x1, y0, p);
p = pgc_draw_line_r(pgc, x1, y0, x1, y1, p);
p = pgc_draw_line_r(pgc, x1, y1, x0, y1, p);
p = pgc_draw_line_r(pgc, x0, y1, x0, y0, p);
}
}
/*
* FIXME:
* Define a font character.
*
* Text drawing should probably be implemented in
* vid_pgc.c rather than here..
*/
static void
hndl_tdefin(pgc_t *pgc)
{
im1024_t *dev = (im1024_t *) pgc;
uint8_t ch;
uint8_t bt;
uint8_t rows;
uint8_t cols;
unsigned len;
if (!pgc_param_byte(pgc, &ch))
return;
if (!pgc_param_byte(pgc, &cols))
return;
if (!pgc_param_byte(pgc, &rows))
return;
im1024_log("IM1024: TDEFIN (%i,%i,%i) 0x%02x 0x%02x\n",
ch, rows, cols, pgc->mapram[0x300], pgc->mapram[0x301]);
len = ((cols + 7) / 8) * rows;
for (unsigned int n = 0; n < len; n++) {
if (!pgc_param_byte(pgc, &bt))
return;
if (n < sizeof(dev->font[ch]))
dev->font[ch][n] = bt;
}
dev->fontx[ch] = cols;
dev->fonty[ch] = rows;
}
static void
hndl_tsize(pgc_t *pgc)
{
int16_t size;
if (!pgc_param_word(pgc, &size))
return;
im1024_log("IM1024: TSIZE(%i)\n", size);
pgc->tsize = size << 16;
}
static void
hndl_twrite(pgc_t *pgc)
{
uint8_t buf[256];
const im1024_t *dev = (im1024_t *) pgc;
uint8_t count;
uint8_t mask;
const uint8_t *row;
int wb;
int n;
int16_t x0 = pgc->x >> 16;
int16_t y0 = pgc->y >> 16;
if (!pgc_param_byte(pgc, &count))
return;
for (n = 0; n < count; n++)
if (!pgc_param_byte(pgc, &buf[n]))
return;
buf[count] = 0;
pgc_sto_raster(pgc, &x0, &y0);
im1024_log("IM1024: TWRITE (%i) x0=%i y0=%i\n", count, x0, y0);
for (n = 0; n < count; n++) {
wb = (dev->fontx[buf[n]] + 7) / 8;
im1024_log("IM1024: ch=0x%02x w=%i h=%i wb=%i\n",
buf[n], dev->fontx[buf[n]], dev->fonty[buf[n]], wb);
for (uint8_t y = 0; y < dev->fonty[buf[n]]; y++) {
mask = 0x80;
row = &dev->font[buf[n]][y * wb];
for (uint8_t x = 0; x < dev->fontx[buf[n]]; x++) {
if (row[0] & mask)
pgc_plot(pgc, x + x0, y0 - y);
mask = mask >> 1;
if (mask == 0) {
mask = 0x80;
row++;
}
}
}
x0 += dev->fontx[buf[n]];
}
}
static void
hndl_txt88(pgc_t *pgc)
{
uint8_t buf[256];
uint8_t count;
uint8_t mask;
const uint8_t *row;
int16_t x0 = pgc->x >> 16;
int16_t y0 = pgc->y >> 16;
unsigned int n;
if (!pgc_param_byte(pgc, &count))
return;
for (n = 0; n < count; n++)
if (!pgc_param_byte(pgc, &buf[n]))
return;
buf[count] = 0;
pgc_sto_raster(pgc, &x0, &y0);
im1024_log("IM204: TXT88 (%i) x0=%i y0=%i\n", count, x0, y0);
for (n = 0; n < count; n++) {
im1024_log("ch=0x%02x w=12 h=18\n", buf[n]);
for (uint8_t y = 0; y < 18; y++) {
mask = 0x80;
row = &fontdat12x18[buf[n]][y * 2];
for (uint8_t x = 0; x < 12; x++) {
if (row[0] & mask)
pgc_plot(pgc, x + x0, y0 - y);
mask = mask >> 1;
if (mask == 0) {
mask = 0x80;
row++;
}
}
}
x0 += 12;
}
}
static void
hndl_imagew(pgc_t *pgc)
{
int16_t vp_x1;
int16_t vp_y1;
int16_t vp_x2;
int16_t vp_y2;
int16_t row1;
int16_t col1;
int16_t col2;
uint8_t v1;
uint8_t v2;
if (!pgc_param_word(pgc, &row1))
return;
if (!pgc_param_word(pgc, &col1))
return;
if (!pgc_param_word(pgc, &col2))
return;
/* Already using raster coordinates, no need to convert. */
im1024_log("IM1024: IMAGEW (row=%i,col1=%i,col2=%i)\n", row1, col1, col2);
vp_x1 = pgc->vp_x1;
vp_y1 = pgc->vp_y1;
vp_x2 = pgc->vp_x2;
vp_y2 = pgc->vp_y2;
/* Disable clipping. */
pgc->vp_x1 = 0;
pgc->vp_y1 = 0;
pgc->vp_x2 = pgc->maxw - 1;
pgc->vp_y2 = pgc->maxh - 1;
/* In ASCII mode, what is written is a stream of bytes. */
if (pgc->ascii_mode) {
while (col1 <= col2) {
if (!pgc_param_byte(pgc, &v1))
return;
pgc_write_pixel(pgc, col1, row1, v1);
col1++;
}
} else {
/* In hex mode, it's RLE compressed. */
while (col1 <= col2) {
if (!pgc_param_byte(pgc, &v1))
return;
if (v1 & 0x80) {
/* Literal run. */
v1 -= 0x7f;
while (col1 <= col2 && v1 != 0) {
if (!pgc_param_byte(pgc, &v2))
return;
pgc_write_pixel(pgc, col1, row1, v2);
col1++;
v1--;
}
} else {
/* Repeated run. */
if (!pgc_param_byte(pgc, &v2))
return;
v1++;
while (col1 <= col2 && v1 != 0) {
pgc_write_pixel(pgc, col1, row1, v2);
col1++;
v1--;
}
}
}
}
/* Restore clipping. */
pgc->vp_x1 = vp_x1;
pgc->vp_y1 = vp_y1;
pgc->vp_x2 = vp_x2;
pgc->vp_y2 = vp_y2;
}
/*
* I have called this command DOT - I don't know its proper name.
*
* Draws a single pixel at the current location.
*/
static void
hndl_dot(pgc_t *pgc)
{
int16_t x = pgc->x >> 16;
int16_t y = pgc->y >> 16;
pgc_sto_raster(pgc, &x, &y);
im1024_log("IM1024: DOT @ %i,%i ink=%i mode=%i\n",
x, y, pgc->color, pgc->draw_mode);
pgc_plot(pgc, x, y);
}
/*
* This command (which I have called IMAGEX, since I don't know its real
* name) is a screen-to-memory blit. It reads a rectangle of bytes, rather
* than the single row read by IMAGER, and does not attempt to compress
* the result.
*/
static void
hndl_imagex(pgc_t *pgc)
{
int16_t x0;
int16_t x1;
int16_t y0;
int16_t y1;
if (!pgc_param_word(pgc, &x0))
return;
if (!pgc_param_word(pgc, &y0))
return;
if (!pgc_param_word(pgc, &x1))
return;
if (!pgc_param_word(pgc, &y1))
return;
/* Already using raster coordinates, no need to convert. */
im1024_log("IM1024: IMAGEX (%i,%i,%i,%i)\n", x0, y0, x1, y1);
for (int16_t p = y0; p <= y1; p++) {
for (int16_t q = x0; q <= x1; q++) {
if (!pgc_result_byte(pgc, pgc_read_pixel(pgc, q, p)))
return;
}
}
}
/*
* Commands implemented by the IM-1024.
*
* TODO: A lot of commands need commandlist parsers.
* TODO: The IM-1024 has a lot more commands that are not included here
* (BLINK, BUTRD, COPROC, RBAND etc) because the Windows 1.03 driver
* does not use them.
*/
static const pgc_cmd_t im1024_commands[] = {
{"BLKMOV", 0xdf, hndl_blkmov, pgc_parse_words, 6},
{ "DRAW", 0x28, hndl_draw, pgc_parse_words, 2},
{ "D", 0x28, hndl_draw, pgc_parse_words, 2},
{ "DOT", 0x08, hndl_dot, NULL, 0},
{ "ELIPSE", 0x39, hndl_ellipse, pgc_parse_words, 2},
{ "EL", 0x39, hndl_ellipse, pgc_parse_words, 2},
{ "IMAGEW", 0xd9, hndl_imagew, NULL, 0},
{ "IMAGEX", 0xda, hndl_imagex, NULL, 0},
{ "IMGSIZ", 0x4e, hndl_imgsiz, NULL, 0},
{ "IPREC", 0xe4, hndl_iprec, NULL, 0},
{ "IW", 0xd9, hndl_imagew, NULL, 0},
{ "L8", 0xe6, pgc_hndl_lut8, NULL, 0},
{ "LF", 0xeb, hndl_linfun, pgc_parse_bytes, 1},
{ "LINFUN", 0xeb, hndl_linfun, pgc_parse_bytes, 1},
{ "LUT8", 0xe6, pgc_hndl_lut8, NULL, 0},
{ "LUT8RD", 0x53, pgc_hndl_lut8rd, NULL, 0},
{ "L8RD", 0x53, pgc_hndl_lut8rd, NULL, 0},
{ "TDEFIN", 0x84, hndl_tdefin, NULL, 0},
{ "TD", 0x84, hndl_tdefin, NULL, 0},
{ "TSIZE", 0x81, hndl_tsize, NULL, 0},
{ "TS", 0x81, hndl_tsize, NULL, 0},
{ "TWRITE", 0x8b, hndl_twrite, NULL, 0},
{ "TXT88", 0x88, hndl_txt88, NULL, 0},
{ "PAN", 0xb7, hndl_pan, NULL, 0},
{ "POLY", 0x30, hndl_poly, parse_poly, 0},
{ "P", 0x30, hndl_poly, parse_poly, 0},
{ "PLINE", 0x36, hndl_pline, NULL, 0},
{ "PL", 0x37, hndl_pline, NULL, 0},
{ "MOVE", 0x10, hndl_move, pgc_parse_words, 2},
{ "M", 0x10, hndl_move, pgc_parse_words, 2},
{ "RECT", 0x34, hndl_rect, NULL, 0},
{ "R", 0x34, hndl_rect, NULL, 0},
{ "******", 0x00, NULL, NULL, 0}
};
static void *
im1024_init(UNUSED(const device_t *info))
{
im1024_t *dev;
dev = (im1024_t *) malloc(sizeof(im1024_t));
memset(dev, 0x00, sizeof(im1024_t));
loadfont(BIOS_ROM_PATH, 9);
dev->fifo_len = 4096;
dev->fifo = (uint8_t *) malloc(dev->fifo_len);
dev->fifo_wrptr = 0;
dev->fifo_rdptr = 0;
/* Create a 1024x1024 framebuffer with 1024x800 visible. */
pgc_init(&dev->pgc, 1024, 1024, 1024, 800, input_byte, 65000000.0);
dev->pgc.commands = im1024_commands;
mem_mapping_set_handler(&dev->pgc.mapping,
im1024_read, NULL, NULL, im1024_write, NULL, NULL);
video_inform(VIDEO_FLAG_TYPE_CGA, &timing_im1024);
return dev;
}
static void
im1024_close(void *priv)
{
im1024_t *dev = (im1024_t *) priv;
pgc_close_common(&dev->pgc);
free(dev);
}
static int
im1024_available(void)
{
return rom_present(BIOS_ROM_PATH);
}
static void
im1024_speed_changed(void *priv)
{
im1024_t *dev = (im1024_t *) priv;
pgc_speed_changed(&dev->pgc);
}
const device_t im1024_device = {
.name = "ImageManager 1024",
.internal_name = "im1024",
.flags = DEVICE_ISA,
.local = 0,
.init = im1024_init,
.close = im1024_close,
.reset = NULL,
{ .available = im1024_available },
.speed_changed = im1024_speed_changed,
.force_redraw = NULL,
.config = NULL
};