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).
1098 lines
27 KiB
C
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, ¶m))
|
|
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, ¶m))
|
|
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
|
|
};
|