From d413238570390dc0d921f953f9353bc3740a52ba Mon Sep 17 00:00:00 2001 From: waltje Date: Sat, 2 Mar 2019 19:19:24 -0500 Subject: [PATCH] Re-worked the ST506 driver for XT, adding support for Seagate ST-11 and WD-1002 controllers, and RLL mode. Added John Elliott's implementation of the IBM PGC controller, and the ImageManager 1024 clone card. The PGC is not working properly yet, pending conversion to our video backend. --- src/devices/disk/hdc.c | 10 +- src/devices/disk/hdc.h | 8 +- src/devices/disk/hdc_st506_xt.c | 1802 +++++++++++++------ src/devices/system/dma.c | 1 - src/devices/video/vid_cga.c | 4 +- src/devices/video/vid_cga.h | 7 +- src/devices/video/vid_im1024.c | 913 ++++++++++ src/devices/video/vid_pgc.c | 2543 +++++++++++++++++++++++++++ src/devices/video/vid_pgc.h | 193 ++ src/devices/video/vid_pgc_palette.h | 1597 +++++++++++++++++ src/devices/video/video.h | 10 +- src/devices/video/video_dev.c | 6 +- src/win/mingw/Makefile.MinGW | 3 +- src/win/msvc/Makefile.VC | 3 +- 14 files changed, 6575 insertions(+), 525 deletions(-) create mode 100644 src/devices/video/vid_im1024.c create mode 100644 src/devices/video/vid_pgc.c create mode 100644 src/devices/video/vid_pgc.h create mode 100644 src/devices/video/vid_pgc_palette.h diff --git a/src/devices/disk/hdc.c b/src/devices/disk/hdc.c index 732396c..f31a234 100644 --- a/src/devices/disk/hdc.c +++ b/src/devices/disk/hdc.c @@ -8,12 +8,12 @@ * * Common code to handle all sorts of disk controllers. * - * Version: @(#)hdc.c 1.0.16 2018/10/20 + * Version: @(#)hdc.c 1.0.17 2019/02/24 * * Authors: Fred N. van Kempen, * Miran Grca, * - * Copyright 2017,2018 Fred N. van Kempen. + * Copyright 2017-2019 Fred N. van Kempen. * Copyright 2016-2018 Miran Grca. * * This program is free software; you can redistribute it and/or modify @@ -63,6 +63,10 @@ static const struct { { "st506_xt", &st506_xt_xebec_device }, { "st506_dtc5150x", &st506_xt_dtc5150x_device }, + { "st506_st11_m", &st506_xt_st11_m_device }, + { "st506_st11_r", &st506_xt_st11_r_device }, + { "st506_wd1002a_wx1", &st506_xt_wd1002a_wx1_device }, + { "st506_wd1002a_27x", &st506_xt_wd1002a_27x_device }, { "xta_wdxt150", &xta_wdxt150_device }, @@ -97,7 +101,7 @@ hdc_log(int level, const char *fmt, ...) # ifdef ENABLE_HDC_LOG va_list ap; - if (hdc_do_log >= level) { + if ((hdc_do_log + LOG_INFO) >= level) { va_start(ap, fmt); pclog_ex(fmt, ap); va_end(ap); diff --git a/src/devices/disk/hdc.h b/src/devices/disk/hdc.h index c7944c1..4938f26 100644 --- a/src/devices/disk/hdc.h +++ b/src/devices/disk/hdc.h @@ -8,12 +8,12 @@ * * Definitions for the common disk controller handler. * - * Version: @(#)hdc.h 1.0.14 2018/09/28 + * Version: @(#)hdc.h 1.0.15 2019/02/24 * * Authors: Fred N. van Kempen, * Miran Grca, * - * Copyright 2017,2018 Fred N. van Kempen. + * Copyright 2017-2019 Fred N. van Kempen. * Copyright 2016-2018 Miran Grca. * * This program is free software; you can redistribute it and/or modify @@ -57,6 +57,10 @@ extern "C" { #ifdef EMU_DEVICE_H extern const device_t st506_xt_xebec_device; /* st506_xt_xebec */ extern const device_t st506_xt_dtc5150x_device; /* st506_xt_dtc */ +extern const device_t st506_xt_st11_m_device; /* st506_xt_st11_m */ +extern const device_t st506_xt_st11_r_device; /* st506_xt_st11_r */ +extern const device_t st506_xt_wd1002a_wx1_device; /* st506_xt_wd1002a_wx1 */ +extern const device_t st506_xt_wd1002a_27x_device; /* st506_xt_wd1002a_27x */ extern const device_t st506_at_wd1003_device; /* st506_at_wd1003 */ diff --git a/src/devices/disk/hdc_st506_xt.c b/src/devices/disk/hdc_st506_xt.c index a95a454..de603b0 100644 --- a/src/devices/disk/hdc_st506_xt.c +++ b/src/devices/disk/hdc_st506_xt.c @@ -14,7 +14,7 @@ * #1 Original, single drive (ST412), 10MB, 2 heads. * #2 Update, single drive (ST412) but with option for a * switch block that can be used to 'set' the actual - * drive type. Four switches are define, where switches + * drive type. Four switches are defined, where switches * 1 and 2 define drive0, and switches 3 and 4 drive1. * * 0 ON ON 306 2 0 @@ -41,12 +41,12 @@ * Since all controllers (including the ones made by DTC) use * (mostly) the same API, we keep them all in this module. * - * Version: @(#)hdc_st506_xt.c 1.0.14 2018/10/15 + * Version: @(#)hdc_st506_xt.c 1.0.15 2019/02/24 * * Authors: Fred N. van Kempen, * Sarah Walker, * - * Copyright 2017,2018 Fred N. van Kempen. + * Copyright 2017-2019 Fred N. van Kempen. * Copyright 2008-2018 Sarah Walker. * * This program is free software; you can redistribute it and/or modify @@ -75,7 +75,7 @@ #include #include #include -#define dbglog hdc_log +//#define dbglog hdc_log #include "../../emu.h" #include "../../io.h" #include "../../mem.h" @@ -90,43 +90,112 @@ #include "hdd.h" -#define XEBEC_BIOS_FILE L"disk/st506/ibm_xebec_62x0822_1985.bin" -#define DTC_BIOS_FILE L"disk/st506/dtc_cxd21a.bin" +#define XEBEC_BIOS_FILE L"disk/st506/ibm_xebec_62x0822_1985.bin" +#define DTC_BIOS_FILE L"disk/st506/dtc_cxd21a.bin" +#define ST11_BIOS_FILE L"disk/st506/st11_bios_vers_1.7.bin" +#define WD1002A_WX1_BIOS_FILE L"disk/st506/wd1002a_wx1-62-000094-032.bin" +#define WD1002A_27X_BIOS_FILE L"disk/st506/wd1002a_27x-62-000215-060.bin" -#define ST506_TIME (50LL*TIMER_USEC) +#define ST506_TIME (50LL * TIMER_USEC) +#define ST506_TIME_MS (20LL * ST506_TIME) -#define STAT_IRQ 0x20 -#define STAT_DRQ 0x10 -#define STAT_BSY 0x08 -#define STAT_CD 0x04 -#define STAT_IO 0x02 -#define STAT_REQ 0x01 +/* MFM and RLL use different sectors/track. */ +#define SECTOR_SIZE 512 +#define MFM_SECTORS 17 +#define RLL_SECTORS 26 -#define IRQ_ENA 0x02 -#define DMA_ENA 0x01 -#define CMD_TEST_DRIVE_READY 0x00 -#define CMD_RECALIBRATE 0x01 -#define CMD_READ_STATUS 0x03 -#define CMD_VERIFY_SECTORS 0x05 -#define CMD_FORMAT_TRACK 0x06 -#define CMD_READ_SECTORS 0x08 -#define CMD_WRITE_SECTORS 0x0a -#define CMD_SEEK 0x0b -#define CMD_INIT_DRIVE_PARAMS 0x0c -#define CMD_WRITE_SECTOR_BUFFER 0x0f -#define CMD_BUFFER_DIAGNOSTIC 0xe0 -#define CMD_CONTROLLER_DIAGNOSTIC 0xe4 +/* Status register. */ +#define STAT_REQ 0x01 /* controller ready */ +#define STAT_IO 0x02 /* input, data to host */ +#define STAT_CD 0x04 /* command mode (else data) */ +#define STAT_BSY 0x08 /* controller is busy */ +#define STAT_DRQ 0x10 /* controller needs DMA */ +#define STAT_IRQ 0x20 /* interrupt, we have info */ -#define CMD_DTC_GET_DRIVE_PARAMS 0xfb -#define CMD_DTC_SET_STEP_RATE 0xfc -#define CMD_DTC_SET_GEOMETRY 0xfe -#define CMD_DTC_GET_GEOMETRY 0xff +/* DMA/IRQ enable register. */ +#define DMA_ENA 0x01 /* DMA operation enabled */ +#define IRQ_ENA 0x02 /* IRQ operation enabled */ -#define ERR_NOT_READY 0x04 -#define ERR_SEEK_ERROR 0x15 -#define ERR_ILLEGAL_SECTOR_ADDRESS 0x21 +/* Error codes in sense report. */ +#define ERR_BV 0x80 +#define ERR_TYPE_MASK 0x30 +#define ERR_TYPE_SHIFT 4 +# define ERR_TYPE_DRIVE 0x00 +# define ERR_TYPE_CONTROLLER 0x01 +# define ERR_TYPE_COMMAND 0x02 +# define ERR_TYPE_MISC 0x03 + +/* No, um, errors.. */ +#define ERR_NONE 0x00 + +/* Group 0: drive errors. */ +#define ERR_NO_SEEK 0x02 /* no seek_complete */ +#define ERR_WR_FAULT 0x03 /* write fault */ +#define ERR_NOT_READY 0x04 /* drive not ready */ +#define ERR_NO_TRACK0 0x06 /* track 0 not found */ +#define ERR_STILL_SEEKING 0x08 /* drive is still seeking */ +#define ERR_NOT_AVAILABLE 0x09 /* drive not available */ + +/* Group 1: controller errors. */ +#define ERR_ID_FAULT 0x10 /* could not read ID field */ +#define ERR_UNC_ERR 0x11 /* uncorrectable data */ +#define ERR_SECTOR_ADDR 0x12 /* sector address */ +#define ERR_DATA_ADDR 0x13 /* data mark not found */ +#define ERR_TARGET_SECTOR 0x14 /* target sector not found */ +#define ERR_SEEK_ERROR 0x15 /* seek error- cyl not found */ +#define ERR_CORR_ERR 0x18 /* correctable data */ +#define ERR_BAD_TRACK 0x19 /* track is flagged as bad */ +#define ERR_ALT_TRACK_FLAGGED 0x1c /* alt trk not flagged as alt */ +#define ERR_ALT_TRACK_ACCESS 0x1e /* illegal access to alt trk */ +#define ERR_NO_RECOVERY 0x1f /* recovery mode not avail */ + +/* Group 2: command errors. */ +#define ERR_BAD_COMMAND 0x20 /* invalid command */ +#define ERR_ILLEGAL_ADDR 0x21 /* address beyond disk size */ +#define ERR_BAD_PARAMETER 0x22 /* invalid command parameter */ + +/* Group 3: misc errors. */ +#define ERR_BAD_RAM 0x30 /* controller has bad RAM */ +#define ERR_BAD_ROM 0x31 /* ROM failed checksum test */ +#define ERR_CRC_FAIL 0x32 /* CRC circuit failed test */ + +/* Controller commands. */ +#define CMD_TEST_DRIVE_READY 0x00 +#define CMD_RECALIBRATE 0x01 +/* reserved 0x02 */ +#define CMD_STATUS 0x03 +#define CMD_FORMAT_DRIVE 0x04 +#define CMD_VERIFY 0x05 +#define CMD_FORMAT_TRACK 0x06 +#define CMD_FORMAT_BAD_TRACK 0x07 +#define CMD_READ 0x08 +#define CMD_REASSIGN 0x09 +#define CMD_WRITE 0x0a +#define CMD_SEEK 0x0b +#define CMD_SPECIFY 0x0c +#define CMD_READ_ECC_BURST_LEN 0x0d +#define CMD_READ_BUFFER 0x0e +#define CMD_WRITE_BUFFER 0x0f +#define CMD_ALT_TRACK 0x11 +#define CMD_INQUIRY_ST11 0x12 /* ST-11 BIOS */ +#define CMD_RAM_DIAGNOSTIC 0xe0 +/* reserved 0xe1 */ +/* reserved 0xe2 */ +#define CMD_DRIVE_DIAGNOSTIC 0xe3 +#define CMD_CTRLR_DIAGNOSTIC 0xe4 +#define CMD_READ_LONG 0xe5 +#define CMD_WRITE_LONG 0xe6 + +#define CMD_FORMAT_DRIVE_ST11 0xf6 /* ST-11 BIOS */ +#define CMD_GET_GEOMETRY_ST11 0xf8 /* ST-11 BIOS */ +#define CMD_SET_GEOMETRY_ST11 0xfa /* ST-11 BIOS */ + +#define CMD_GET_DRIVE_PARAMS_DTC 0xfb /* DTC */ +#define CMD_SET_STEP_RATE_DTC 0xfc /* DTC */ +#define CMD_SET_GEOMETRY_DTC 0xfe /* DTC */ +#define CMD_GET_GEOMETRY_DTC 0xff /* DTC */ enum { @@ -138,284 +207,252 @@ enum { STATE_SEND_DATA, STATE_SENT_DATA, STATE_COMPLETION_BYTE, - STATE_DUNNO + STATE_DONE }; typedef struct { - int spt, + int8_t present; + uint8_t hdd_num; + + uint8_t interleave; /* default interleave */ + char pad; + + uint16_t cylinder; /* current cylinder */ + + uint8_t spt, /* physical parameters */ hpc; - int tracks; - int cfg_spt; - int cfg_hpc; - int cfg_cyl; - int current_cylinder; - int present; - int hdd_num; + uint16_t tracks; + + uint8_t cfg_spt, /* configured parameters */ + cfg_hpc; + uint16_t cfg_cyl; } drive_t; + typedef struct { + uint8_t type; /* controller type */ + + uint8_t spt; /* sectors-per-track for controller */ + + uint16_t base; /* controller configuration */ + int8_t irq, + dma; + uint8_t switches; + uint8_t misc; + uint32_t bios_addr, + bios_size, + bios_ram; rom_t bios_rom; - int64_t callback; - int state; - uint8_t status; - uint8_t command[6]; - int command_pos; - uint8_t data[512]; - int data_pos, - data_len; - uint8_t sector_buf[512]; - uint8_t irq_dma_mask; - uint8_t completion_byte; + + int state; /* operational data */ + uint8_t irq_dma; uint8_t error; + uint8_t status; + int8_t cyl_off; /* for ST-11, cylinder0 offset */ + int64_t callback; + + uint8_t command[6]; /* current command request */ int drive_sel; - drive_t drives[ST506_NUM]; int sector, head, - cylinder; - int sector_count; - uint8_t switches; + cylinder, + count; + uint8_t compl; /* current request completion code */ + + int buff_pos, /* pointers to the RAM buffer */ + buff_cnt; + + drive_t drives[ST506_NUM]; /* the attached drives */ + uint8_t scratch[64]; /* ST-11 scratchpad RAM */ + uint8_t buff[SECTOR_SIZE + 4]; /* sector buffer RAM (+ ECC bytes) */ } hdc_t; +/* Supported drives table for the Xebec controller. */ static const struct { uint16_t tracks; uint8_t hpc; uint8_t spt; } hd_types[4] = { - { 306, 4, 17 }, /* Type 0 */ - { 612, 4, 17 }, /* Type 16 */ - { 615, 4, 17 }, /* Type 2 */ - { 306, 8, 17 } /* Type 13 */ + { 306, 4, MFM_SECTORS }, /* type 0 */ + { 612, 4, MFM_SECTORS }, /* type 16 */ + { 615, 4, MFM_SECTORS }, /* type 2 */ + { 306, 8, MFM_SECTORS } /* type 13 */ }; -static uint8_t -st506_read(uint16_t port, void *priv) -{ - hdc_t *dev = (hdc_t *)priv; - uint8_t temp = 0xff; - - switch (port) { - case 0x320: /*Read data*/ - dev->status &= ~STAT_IRQ; - switch (dev->state) { - case STATE_COMPLETION_BYTE: - if ((dev->status & 0xf) != (STAT_CD | STAT_IO | STAT_REQ | STAT_BSY)) - fatal("Read data STATE_COMPLETION_BYTE, status=%02x\n", dev->status); - - temp = dev->completion_byte; - dev->status = 0; - dev->state = STATE_IDLE; - break; - - case STATE_SEND_DATA: - if ((dev->status & 0xf) != (STAT_IO | STAT_REQ | STAT_BSY)) - fatal("Read data STATE_COMPLETION_BYTE, status=%02x\n", dev->status); - if (dev->data_pos >= dev->data_len) - fatal("Data write with full data!\n"); - temp = dev->data[dev->data_pos++]; - if (dev->data_pos == dev->data_len) { - dev->status = STAT_BSY; - dev->state = STATE_SENT_DATA; - dev->callback = ST506_TIME; - } - break; - - default: - fatal("Read data register - %i, %02x\n", dev->state, dev->status); - } - break; - - case 0x321: /*Read status*/ - temp = dev->status; - break; - - case 0x322: /*Read option jumpers*/ - temp = dev->switches; - break; - } - - return(temp); -} - - -static void -st506_write(uint16_t port, uint8_t val, void *priv) -{ - hdc_t *dev = (hdc_t *)priv; - - switch (port) { - case 0x320: /*Write data*/ - switch (dev->state) { - case STATE_RECEIVE_COMMAND: - if ((dev->status & 0xf) != (STAT_BSY | STAT_CD | STAT_REQ)) - fatal("Bad write data state - STATE_START_COMMAND, status=%02x\n", dev->status); - if (dev->command_pos >= 6) - fatal("Command write with full command!\n"); - /*Command data*/ - dev->command[dev->command_pos++] = val; - if (dev->command_pos == 6) { - dev->status = STAT_BSY; - dev->state = STATE_START_COMMAND; - dev->callback = ST506_TIME; - } - break; - - case STATE_RECEIVE_DATA: - if ((dev->status & 0xf) != (STAT_BSY | STAT_REQ)) - fatal("Bad write data state - STATE_RECEIVE_DATA, status=%02x\n", dev->status); - if (dev->data_pos >= dev->data_len) - fatal("Data write with full data!\n"); - /*Command data*/ - dev->data[dev->data_pos++] = val; - if (dev->data_pos == dev->data_len) { - dev->status = STAT_BSY; - dev->state = STATE_RECEIVED_DATA; - dev->callback = ST506_TIME; - } - break; - - default: - DEBUG("Write data unknown state - %i %02x\n", dev->state, dev->status); - } - break; - - case 0x321: /*Controller reset*/ - dev->status = 0; - break; - - case 0x322: /*Generate controller-select-pulse*/ - dev->status = STAT_BSY | STAT_CD | STAT_REQ; - dev->command_pos = 0; - dev->state = STATE_RECEIVE_COMMAND; - break; - - case 0x323: /*DMA/IRQ mask register*/ - dev->irq_dma_mask = val; - break; - } -} - - static void st506_complete(hdc_t *dev) { dev->status = STAT_REQ | STAT_CD | STAT_IO | STAT_BSY; dev->state = STATE_COMPLETION_BYTE; - if (dev->irq_dma_mask & IRQ_ENA) { + if (dev->irq_dma & IRQ_ENA) { dev->status |= STAT_IRQ; - picint(1 << 5); + picint(1 << dev->irq); } } static void -st506_error(hdc_t *dev, uint8_t error) +st506_error(hdc_t *dev, uint8_t err) { - dev->completion_byte |= 0x02; - dev->error = error; - - DEBUG("st506_error - %02x\n", dev->error); + dev->compl |= 0x02; + dev->error = err; } static int -get_sector(hdc_t *dev, off64_t *addr) +get_sector(hdc_t *dev, drive_t *drive, off64_t *addr) { - drive_t *drive = &dev->drives[dev->drive_sel]; - int heads = drive->cfg_hpc; - - if (drive->current_cylinder != dev->cylinder) { - DEBUG("st506_get_sector: wrong cylinder\n"); - dev->error = ERR_ILLEGAL_SECTOR_ADDRESS; - return(1); - } - if (dev->head > heads) { - DEBUG("st506_get_sector: past end of configured heads\n"); - dev->error = ERR_ILLEGAL_SECTOR_ADDRESS; - return(1); - } - if (dev->head > drive->hpc) { - DEBUG("st506_get_sector: past end of heads\n"); - dev->error = ERR_ILLEGAL_SECTOR_ADDRESS; - return(1); - } - if (dev->sector >= 17) { - DEBUG("st506_get_sector: past end of sectors\n"); - dev->error = ERR_ILLEGAL_SECTOR_ADDRESS; - return(1); + if (! drive->present) { + /* No need to log this. */ + dev->error = ERR_NOT_AVAILABLE; + return(0); } - *addr = ((((off64_t) dev->cylinder * heads) + dev->head) * - 17) + dev->sector; + if (drive->cylinder != dev->cylinder) { + DEBUG("ST506: get_sector: wrong cylinder\n"); + dev->error = ERR_ILLEGAL_ADDR; + return(0); + } + + if (dev->head >= drive->cfg_hpc) { + DEBUG("ST506: get_sector: past end of configured heads\n"); + dev->error = ERR_ILLEGAL_ADDR; + return(0); + } + if (dev->sector >= drive->cfg_spt) { + DEBUG("ST506: get_sector: past end of configured sectors\n"); + dev->error = ERR_ILLEGAL_ADDR; + return(0); + } + + *addr = ((((off64_t)dev->cylinder * drive->cfg_hpc) + dev->head) * drive->cfg_spt) + dev->sector; - return(0); + return(1); } static void -next_sector(hdc_t *dev) +next_sector(hdc_t *dev, drive_t *drive) { - drive_t *drive = &dev->drives[dev->drive_sel]; - - dev->sector++; - if (dev->sector >= 17) { + if (++dev->sector >= drive->cfg_spt) { dev->sector = 0; - dev->head++; - if (dev->head >= drive->cfg_hpc) { + if (++dev->head >= drive->cfg_hpc) { dev->head = 0; - dev->cylinder++; - drive->current_cylinder++; - if (drive->current_cylinder >= drive->cfg_cyl) - drive->current_cylinder = drive->cfg_cyl-1; + if (++drive->cylinder >= drive->cfg_cyl) { + /* + * This really is an error, we cannot move + * past the end of the drive, which should + * result in an ERR_ILLEGAL_ADDR. --FvK + */ + drive->cylinder = drive->cfg_cyl - 1; + } else + dev->cylinder++; } } } +/* Extract the CHS info from a command block. */ +static int +get_chs(hdc_t *dev, drive_t *drive) +{ + dev->cylinder = dev->command[3] | ((dev->command[2] & 0xc0) << 2); + dev->head = dev->command[1] & 0x1f; + dev->sector = dev->command[2] & 0x1f; /* 0x3f on some */ + dev->count = dev->command[4]; + dev->cylinder += dev->cyl_off; /* for ST-11 */ + + if (dev->cylinder >= drive->cfg_cyl) { + /* + * This really is an error, we cannot move + * past the end of the drive, which should + * result in an ERR_ILLEGAL_ADDR. --FvK + */ + drive->cylinder = drive->cfg_cyl - 1; + return(0); + } + + drive->cylinder = dev->cylinder; + + return(1); +} + + static void st506_callback(void *priv) { hdc_t *dev = (hdc_t *)priv; drive_t *drive; off64_t addr; + int val; + /* Cancel the timer for now. */ dev->callback = 0LL; - dev->drive_sel = (dev->command[1] & 0x20) ? 1 : 0; - dev->completion_byte = dev->drive_sel & 0x20; + /* Get the drive info. Note that the API supports up to 8 drives! */ + dev->drive_sel = (dev->command[1] >> 5) & 0x07; drive = &dev->drives[dev->drive_sel]; + /* Preset the completion byte to "No error" and the selected drive. */ + dev->compl = (dev->drive_sel << 5) | ERR_NONE; + switch (dev->command[0]) { case CMD_TEST_DRIVE_READY: - if (!drive->present) - st506_error(dev, ERR_NOT_READY); + DEBUG("ST506: TEST_READY(%i) = %i\n", + dev->drive_sel, drive->present); + if (! drive->present) + st506_error(dev, ERR_NOT_AVAILABLE); st506_complete(dev); break; case CMD_RECALIBRATE: - if (!drive->present) - st506_error(dev, ERR_NOT_READY); - else { - dev->cylinder = 0; - drive->current_cylinder = 0; - } - st506_complete(dev); - break; - - case CMD_READ_STATUS: switch (dev->state) { case STATE_START_COMMAND: - dev->state = STATE_SEND_DATA; - dev->data_pos = 0; - dev->data_len = 4; - dev->status = STAT_BSY | STAT_IO | STAT_REQ; - dev->data[0] = dev->error; - dev->data[1] = dev->drive_sel ? 0x20 : 0; - dev->data[2] = dev->data[3] = 0; + DEBUG("ST506: RECALIBRATE(%i) [%i]\n", + dev->drive_sel, drive->present); + if (! drive->present) { + st506_error(dev, ERR_NOT_AVAILABLE); + st506_complete(dev); + break; + } + + /* Wait 20msec. */ + dev->callback = ST506_TIME_MS * 20; + + dev->cylinder = dev->cyl_off; + drive->cylinder = dev->cylinder; + dev->state = STATE_DONE; + + break; + + case STATE_DONE: + st506_complete(dev); + break; + } + break; + + case CMD_STATUS: + switch (dev->state) { + case STATE_START_COMMAND: + DEBUG("ST506: STATUS\n"); + dev->buff_pos = 0; + dev->buff_cnt = 4; + dev->buff[0] = ERR_BV | dev->error; dev->error = 0; + + /* Give address of last operation. */ + dev->buff[1] = (dev->drive_sel ? 0x20 : 0) | + dev->head; + dev->buff[2] = ((dev->cylinder & 0x0300) >> 2) | + dev->sector; + dev->buff[3] = (dev->cylinder & 0xff); + + dev->status = STAT_BSY | STAT_IO | STAT_REQ; + dev->state = STATE_SEND_DATA; break; case STATE_SENT_DATA: @@ -424,184 +461,215 @@ st506_callback(void *priv) } break; - case CMD_VERIFY_SECTORS: + case CMD_FORMAT_DRIVE: + case CMD_FORMAT_DRIVE_ST11: switch (dev->state) { case STATE_START_COMMAND: - dev->cylinder = dev->command[3] | ((dev->command[2] & 0xc0) << 2); - drive->current_cylinder = (dev->cylinder >= drive->cfg_cyl) ? drive->cfg_cyl-1 : dev->cylinder; - dev->head = dev->command[1] & 0x1f; - dev->sector = dev->command[2] & 0x1f; - dev->sector_count = dev->command[4]; - do { - if (get_sector(dev, &addr)) { - DEBUG("get_sector failed\n"); - st506_error(dev, dev->error); - st506_complete(dev); - return; - } - - next_sector(dev); - - dev->sector_count = (dev->sector_count-1) & 0xff; - } while (dev->sector_count); - - st506_complete(dev); - + (void)get_chs(dev, drive); + DEBUG("ST506: FORMAT_DRIVE%s(%i) interleave=%i\n", + (dev->command[0] == CMD_FORMAT_DRIVE_ST11) ? "_ST11" : "", + dev->drive_sel, dev->command[4]); +DEBUG("ST506: start_cyl = %i\n", dev->cylinder); hdd_active(drive->hdd_num, 1); + dev->callback = ST506_TIME; + dev->state = STATE_SEND_DATA; break; - default: - fatal("CMD_VERIFY_SECTORS: bad state %i\n", dev->state); + case STATE_SEND_DATA: /* wrong, but works */ + if (! get_sector(dev, drive, &addr)) { + hdd_active(drive->hdd_num, 0); + st506_error(dev, dev->error); + st506_complete(dev); + return; + } + + /* FIXME: should be drive->capac, not ->spt */ + hdd_image_zero(drive->hdd_num, + addr, drive->cfg_spt); + + /* Wait 5msec per cylinder. */ + dev->callback = ST506_TIME_MS * 5 * drive->cfg_cyl; + + dev->state = STATE_SENT_DATA; + break; + + case STATE_SENT_DATA: + hdd_active(drive->hdd_num, 0); + st506_complete(dev); + break; + } + break; + + case CMD_VERIFY: + switch (dev->state) { + case STATE_START_COMMAND: + (void)get_chs(dev, drive); + DEBUG("ST506: VERIFY(%i, %i/%i/%i, %i)\n", + dev->drive_sel, dev->cylinder, + dev->head, dev->sector, dev->count); + hdd_active(drive->hdd_num, 1); + dev->callback = ST506_TIME; + dev->state = STATE_SEND_DATA; + break; + + case STATE_SEND_DATA: + if (dev->count-- == 0) { + hdd_active(drive->hdd_num, 0); + st506_complete(dev); + } + + if (! get_sector(dev, drive, &addr)) { + hdd_active(drive->hdd_num, 0); + st506_error(dev, dev->error); + st506_complete(dev); + return; + } + + next_sector(dev, drive); + + dev->callback = ST506_TIME; + break; } break; case CMD_FORMAT_TRACK: - dev->cylinder = dev->command[3] | ((dev->command[2] & 0xc0) << 2); - drive->current_cylinder = (dev->cylinder >= drive->cfg_cyl) ? drive->cfg_cyl-1 : dev->cylinder; - dev->head = dev->command[1] & 0x1f; - - if (get_sector(dev, &addr)) { - DEBUG("get_sector failed\n"); - st506_error(dev, dev->error); - st506_complete(dev); - return; - } - - hdd_image_zero(drive->hdd_num, addr, 17); - - st506_complete(dev); - break; - - case CMD_READ_SECTORS: + case CMD_FORMAT_BAD_TRACK: switch (dev->state) { case STATE_START_COMMAND: - dev->cylinder = dev->command[3] | ((dev->command[2] & 0xc0) << 2); - drive->current_cylinder = (dev->cylinder >= drive->cfg_cyl) ? drive->cfg_cyl-1 : dev->cylinder; - dev->head = dev->command[1] & 0x1f; - dev->sector = dev->command[2] & 0x1f; - dev->sector_count = dev->command[4]; + (void)get_chs(dev, drive); + DEBUG("ST506: FORMAT_%sTRACK(%i, %i/%i)\n", + (dev->command[0] == CMD_FORMAT_BAD_TRACK) ? "BAD_" : "", + dev->drive_sel, dev->cylinder, dev->head); + hdd_active(drive->hdd_num, 1); + dev->callback = ST506_TIME; dev->state = STATE_SEND_DATA; - dev->data_pos = 0; - dev->data_len = 512; + break; - if (get_sector(dev, &addr)) { + case STATE_SEND_DATA: /* wrong, but works */ + if (! get_sector(dev, drive, &addr)) { + hdd_active(drive->hdd_num, 0); st506_error(dev, dev->error); st506_complete(dev); return; } + hdd_image_zero(drive->hdd_num, + addr, drive->cfg_spt); + + /* Wait 5msec per cylinder. */ + dev->callback = ST506_TIME_MS * 5; + + dev->state = STATE_SENT_DATA; + break; + + case STATE_SENT_DATA: + hdd_active(drive->hdd_num, 0); + st506_complete(dev); + break; + } + break; + + case CMD_READ: +#if 0 + case CMD_READ_LONG: +#endif + switch (dev->state) { + case STATE_START_COMMAND: + (void)get_chs(dev, drive); + DEBUG("ST506: READ%s(%i, %i/%i/%i, %i)\n", + (dev->command[0] == CMD_READ_LONG) ? "_LONG" : "", + dev->drive_sel, dev->cylinder, + dev->head, dev->sector, dev->count); + + if (! get_sector(dev, drive, &addr)) { +INFO("ST506: get_sector error on %d/%d/%d\n", dev->cylinder,dev->head,dev->sector); + st506_error(dev, dev->error); + st506_complete(dev); + return; + } hdd_active(drive->hdd_num, 1); + /* Read data from the image. */ hdd_image_read(drive->hdd_num, addr, 1, - (uint8_t *) dev->sector_buf); + (uint8_t *)dev->buff); - if (dev->irq_dma_mask & DMA_ENA) + /* Set up the data transfer. */ + dev->buff_pos = 0; + dev->buff_cnt = SECTOR_SIZE; + if (dev->command[0] == CMD_READ_LONG) + dev->buff_cnt += 4; + dev->status = STAT_BSY | STAT_IO | STAT_REQ; + if (dev->irq_dma & DMA_ENA) { dev->callback = ST506_TIME; - else { - dev->status = STAT_BSY | STAT_IO | STAT_REQ; - memcpy(dev->data, dev->sector_buf, 512); + dev->status |= STAT_DRQ; } + dev->state = STATE_SEND_DATA; break; case STATE_SEND_DATA: - dev->status = STAT_BSY; - if (dev->irq_dma_mask & DMA_ENA) { - for (; dev->data_pos < 512; dev->data_pos++) { - int val = dma_channel_write(3, dev->sector_buf[dev->data_pos]); - - if (val == DMA_NODATA) { - ERRLOG("HDC: CMD_READ_SECTORS out of data!\n"); - dev->status = STAT_BSY | STAT_CD | STAT_IO | STAT_REQ; - dev->callback = ST506_TIME; - return; - } + dev->status &= ~STAT_DRQ; + for (; dev->buff_pos < dev->buff_cnt; dev->buff_pos++) { + val = dma_channel_write(dev->dma, dev->buff[dev->buff_pos]); + if (val == DMA_NODATA) { + ERRLOG("ST506: CMD_READ out of data!\n"); + st506_error(dev, ERR_NO_RECOVERY); + st506_complete(dev); + return; } - dev->state = STATE_SENT_DATA; dev->callback = ST506_TIME; + dev->state = STATE_SENT_DATA; } break; case STATE_SENT_DATA: - next_sector(dev); - - dev->data_pos = 0; - - dev->sector_count = (dev->sector_count-1) & 0xff; - - if (dev->sector_count) { - if (get_sector(dev, &addr)) { - st506_error(dev, dev->error); - st506_complete(dev); - return; - } - - hdd_active(drive->hdd_num, 1); - - hdd_image_read(drive->hdd_num, addr, 1, - (uint8_t *) dev->sector_buf); - - dev->state = STATE_SEND_DATA; - - if (dev->irq_dma_mask & DMA_ENA) - dev->callback = ST506_TIME; - else { - dev->status = STAT_BSY | STAT_IO | STAT_REQ; - memcpy(dev->data, dev->sector_buf, 512); - } - } else { - st506_complete(dev); +INFO("ST506: block read, count= %d\n", dev->count); + if (--dev->count == 0) { +INFO("ST506: block read DONE, status %d\n", dev->error); hdd_active(drive->hdd_num, 0); + st506_complete(dev); + break; } - break; - default: - fatal("CMD_READ_SECTORS: bad state %i\n", dev->state); + next_sector(dev, drive); + + if (! get_sector(dev, drive, &addr)) { +INFO("ST506: get_sector2 error on %d/%d/%d\n", dev->cylinder,dev->head,dev->sector); + hdd_active(drive->hdd_num, 0); + st506_error(dev, dev->error); + st506_complete(dev); + return; + } + + /* Read data from the image. */ + hdd_image_read(drive->hdd_num, addr, 1, + (uint8_t *)dev->buff); + + /* Set up the data transfer. */ + dev->buff_pos = 0; + dev->status = STAT_BSY | STAT_IO | STAT_REQ; + if (dev->irq_dma & DMA_ENA) { + dev->callback = ST506_TIME; + dev->status |= STAT_DRQ; + } + dev->state = STATE_SEND_DATA; + break; } break; - case CMD_WRITE_SECTORS: + case CMD_WRITE: +#if 0 + case CMD_WRITE_LONG: +#endif switch (dev->state) { case STATE_START_COMMAND: - dev->cylinder = dev->command[3] | ((dev->command[2] & 0xc0) << 2); - drive->current_cylinder = (dev->cylinder >= drive->cfg_cyl) ? drive->cfg_cyl-1 : dev->cylinder; - dev->head = dev->command[1] & 0x1f; - dev->sector = dev->command[2] & 0x1f; - dev->sector_count = dev->command[4]; - dev->state = STATE_RECEIVE_DATA; - dev->data_pos = 0; - dev->data_len = 512; - if (dev->irq_dma_mask & DMA_ENA) - dev->callback = ST506_TIME; - else - dev->status = STAT_BSY | STAT_REQ; - break; + (void)get_chs(dev, drive); + DEBUG("ST506: WRITE%s(%i, %i/%i/%i, %i)\n", + (dev->command[0] == CMD_WRITE_LONG) ? "_LONG" : "", + dev->drive_sel, dev->cylinder, + dev->head, dev->sector, dev->count); - case STATE_RECEIVE_DATA: - dev->status = STAT_BSY; - if (dev->irq_dma_mask & DMA_ENA) { - for (; dev->data_pos < 512; dev->data_pos++) { - int val = dma_channel_read(3); - - if (val == DMA_NODATA) { - ERRLOG("CMD_WRITE_SECTORS out of data!\n"); - dev->status = STAT_BSY | STAT_CD | STAT_IO | STAT_REQ; - dev->callback = ST506_TIME; - return; - } - - dev->sector_buf[dev->data_pos] = val & 0xff; - } - - dev->state = STATE_RECEIVED_DATA; - dev->callback = ST506_TIME; - } - break; - - case STATE_RECEIVED_DATA: - if (! (dev->irq_dma_mask & DMA_ENA)) - memcpy(dev->sector_buf, dev->data, 512); - - if (get_sector(dev, &addr)) { + if (! get_sector(dev, drive, &addr)) { +INFO("ST506: get_sector error on %d/%d/%d\n", dev->cylinder,dev->head,dev->sector); st506_error(dev, dev->error); st506_complete(dev); return; @@ -609,91 +677,183 @@ st506_callback(void *priv) hdd_active(drive->hdd_num, 1); - hdd_image_write(drive->hdd_num, addr, 1, - (uint8_t *) dev->sector_buf); - - next_sector(dev); - dev->data_pos = 0; - dev->sector_count = (dev->sector_count-1) & 0xff; - - if (dev->sector_count) { - dev->state = STATE_RECEIVE_DATA; - if (dev->irq_dma_mask & DMA_ENA) - dev->callback = ST506_TIME; - else - dev->status = STAT_BSY | STAT_REQ; - } else - st506_complete(dev); + /* Set up the data transfer. */ + dev->buff_pos = 0; + dev->buff_cnt = SECTOR_SIZE; + if (dev->command[0] == CMD_WRITE_LONG) + dev->buff_cnt += 4; + dev->status = STAT_BSY | STAT_REQ; + if (dev->irq_dma & DMA_ENA) { + dev->callback = ST506_TIME; + dev->status |= STAT_DRQ; + } + dev->state = STATE_RECEIVE_DATA; break; - default: - fatal("CMD_WRITE_SECTORS: bad state %i\n", dev->state); + case STATE_RECEIVE_DATA: + dev->status &= ~STAT_DRQ; + for (; dev->buff_pos < dev->buff_cnt; dev->buff_pos++) { + val = dma_channel_read(dev->dma); + if (val == DMA_NODATA) { + ERRLOG("ST506: CMD_WRITE out of data!\n"); + st506_error(dev, ERR_NO_RECOVERY); + st506_complete(dev); + return; + } + dev->buff[dev->buff_pos] = val & 0xff; + + dev->callback = ST506_TIME; + dev->state = STATE_RECEIVED_DATA; + } + break; + + case STATE_RECEIVED_DATA: + if (! get_sector(dev, drive, &addr)) { +INFO("ST506: get_sector2 error on %d/%d/%d\n", dev->cylinder,dev->head,dev->sector); + hdd_active(drive->hdd_num, 0); + st506_error(dev, dev->error); + st506_complete(dev); + return; + } + + /* Write data to image. */ + hdd_image_write(drive->hdd_num, addr, 1, + (uint8_t *)dev->buff); + +INFO("ST506: block written, count= %d\n", dev->count); + if (--dev->count == 0) { +INFO("ST506: block write DONE, status %d\n", dev->error); + hdd_active(drive->hdd_num, 0); + st506_complete(dev); + break; + } + + next_sector(dev, drive); + + /* Set up the data transfer. */ + dev->buff_pos = 0; + dev->status = STAT_BSY | STAT_REQ; + if (dev->irq_dma & DMA_ENA) { + dev->callback = ST506_TIME; + dev->status |= STAT_DRQ; + } + dev->state = STATE_RECEIVE_DATA; + break; } break; case CMD_SEEK: - if (! drive->present) - st506_error(dev, ERR_NOT_READY); - else { - int cylinder = dev->command[3] | ((dev->command[2] & 0xc0) << 2); - - drive->current_cylinder = (cylinder >= drive->cfg_cyl) ? drive->cfg_cyl-1 : cylinder; - - if (cylinder != drive->current_cylinder) + if (drive->present) { + val = get_chs(dev, drive); + DEBUG("ST506: SEEK(%i, %i) [%i]\n", + dev->drive_sel, drive->cylinder, val); + if (! val) st506_error(dev, ERR_SEEK_ERROR); - } + } else + st506_error(dev, ERR_NOT_AVAILABLE); st506_complete(dev); break; - case CMD_INIT_DRIVE_PARAMS: + case CMD_SPECIFY: switch (dev->state) { case STATE_START_COMMAND: - dev->state = STATE_RECEIVE_DATA; - dev->data_pos = 0; - dev->data_len = 8; + dev->buff_pos = 0; + dev->buff_cnt = 8; dev->status = STAT_BSY | STAT_REQ; + dev->state = STATE_RECEIVE_DATA; break; case STATE_RECEIVED_DATA: - drive->cfg_cyl = dev->data[1] | (dev->data[0] << 8); - drive->cfg_hpc = dev->data[2]; - DEBUG("Drive %i: cylinders=%i, heads=%i\n", dev->drive_sel, drive->cfg_cyl, drive->cfg_hpc); + drive->cfg_cyl = dev->buff[1] | (dev->buff[0] << 8); + drive->cfg_hpc = dev->buff[2]; + DEBUG("ST506: drive%i: cyls=%i, heads=%i\n", + dev->drive_sel, drive->cfg_cyl, drive->cfg_hpc); st506_complete(dev); break; - - default: - fatal("CMD_INIT_DRIVE_PARAMS bad state %i\n", dev->state); } break; - case CMD_WRITE_SECTOR_BUFFER: + case CMD_READ_ECC_BURST_LEN: switch (dev->state) { case STATE_START_COMMAND: - dev->state = STATE_RECEIVE_DATA; - dev->data_pos = 0; - dev->data_len = 512; - if (dev->irq_dma_mask & DMA_ENA) + DEBUG("ST506: READ_ECC_BURST_LEN\n"); + dev->buff_pos = 0; + dev->buff_cnt = 1; + dev->buff[0] = 0; /* 0 bits */ + dev->status = STAT_BSY | STAT_IO | STAT_REQ; + dev->state = STATE_SEND_DATA; + break; + + case STATE_SENT_DATA: + st506_complete(dev); + break; + } + break; + + case CMD_READ_BUFFER: + switch (dev->state) { + case STATE_START_COMMAND: + dev->buff_pos = 0; + dev->buff_cnt = SECTOR_SIZE; + DEBUG("ST506: READ_BUFFER (%i)\n", + dev->buff_cnt); + + dev->status = STAT_BSY | STAT_IO | STAT_REQ; + if (dev->irq_dma & DMA_ENA) { dev->callback = ST506_TIME; - else - dev->status = STAT_BSY | STAT_REQ; + dev->status |= STAT_DRQ; + } + dev->state = STATE_SEND_DATA; + break; + + case STATE_SEND_DATA: + dev->status &= ~STAT_DRQ; + for (; dev->buff_pos < dev->buff_cnt; dev->buff_pos++) { + val = dma_channel_write(dev->dma, dev->buff[dev->buff_pos]); + if (val == DMA_NODATA) { + ERRLOG("ST506: CMD_READ_BUFFER out of data!\n"); + st506_error(dev, ERR_NO_RECOVERY); + st506_complete(dev); + return; + } + dev->callback = ST506_TIME; + dev->state = STATE_SENT_DATA; + } + break; + + case STATE_SENT_DATA: + st506_complete(dev); + break; + } + break; + + case CMD_WRITE_BUFFER: + switch (dev->state) { + case STATE_START_COMMAND: + dev->buff_pos = 0; + dev->buff_cnt = SECTOR_SIZE; + DEBUG("ST506: WRITE_BUFFER (%i)\n", + dev->buff_cnt); + + dev->status = STAT_BSY | STAT_REQ; + if (dev->irq_dma & DMA_ENA) { + dev->callback = ST506_TIME; + dev->status |= STAT_DRQ; + } + dev->state = STATE_RECEIVE_DATA; break; case STATE_RECEIVE_DATA: - if (dev->irq_dma_mask & DMA_ENA) { - dev->status = STAT_BSY; - - for (; dev->data_pos < 512; dev->data_pos++) { - int val = dma_channel_read(3); - - if (val == DMA_NODATA) { - ERRLOG("CMD_WRITE_SECTOR_BUFFER out of data!\n"); - dev->status = STAT_BSY | STAT_CD | STAT_IO | STAT_REQ; - dev->callback = ST506_TIME; - return; - } - - dev->data[dev->data_pos] = val & 0xff; + dev->status &= ~STAT_DRQ; + for (; dev->buff_pos < dev->buff_cnt; dev->buff_pos++) { + val = dma_channel_read(dev->dma); + if (val == DMA_NODATA) { + ERRLOG("ST506: CMD_WRITE_BUFFER out of data!\n"); + st506_error(dev, ERR_NO_RECOVERY); + st506_complete(dev); + return; } + dev->buff[dev->buff_pos] = val & 0xff; dev->state = STATE_RECEIVED_DATA; dev->callback = ST506_TIME; @@ -701,122 +861,457 @@ st506_callback(void *priv) break; case STATE_RECEIVED_DATA: - memcpy(dev->sector_buf, dev->data, 512); st506_complete(dev); break; - - default: - fatal("CMD_WRITE_SECTOR_BUFFER bad state %i\n", dev->state); } break; - case CMD_BUFFER_DIAGNOSTIC: - case CMD_CONTROLLER_DIAGNOSTIC: - st506_complete(dev); - break; - - case 0xfa: - st506_complete(dev); - break; - - case CMD_DTC_SET_STEP_RATE: - st506_complete(dev); - break; - - case CMD_DTC_GET_DRIVE_PARAMS: - switch (dev->state) { + case CMD_INQUIRY_ST11: + if (dev->type == 11 || dev->type == 12) switch (dev->state) { case STATE_START_COMMAND: - dev->state = STATE_SEND_DATA; - dev->data_pos = 0; - dev->data_len = 4; + DEBUG("ST506: INQUIRY (type=%i)\n", dev->type); + dev->buff_pos = 0; + dev->buff_cnt = 2; + dev->buff[0] = 0x80; /* "ST-11" */ + dev->buff[1] = dev->misc; /* revision */ dev->status = STAT_BSY | STAT_IO | STAT_REQ; - memset(dev->data, 0, 4); - dev->data[0] = drive->tracks & 0xff; - dev->data[1] = 17 | ((drive->tracks >> 2) & 0xc0); - dev->data[2] = drive->hpc-1; - DEBUG("Get drive params %02x %02x %02x %i\n", dev->data[0], dev->data[1], dev->data[2], drive->tracks); + dev->state = STATE_SEND_DATA; break; case STATE_SENT_DATA: st506_complete(dev); break; - - default: - fatal("CMD_INIT_DRIVE_PARAMS bad state %i\n", dev->state); + } else { + st506_error(dev, ERR_BAD_COMMAND); + st506_complete(dev); } break; - case CMD_DTC_GET_GEOMETRY: - switch (dev->state) { + case CMD_RAM_DIAGNOSTIC: + DEBUG("ST506: RAM_DIAG\n"); + st506_complete(dev); + break; + + case CMD_CTRLR_DIAGNOSTIC: + DEBUG("ST506: CTRLR_DIAG\n"); + st506_complete(dev); + break; + + case CMD_GET_GEOMETRY_ST11: + if (dev->type == 11 || dev->type == 12) switch (dev->state) { + /* + * 42? bytes + * [0] = 0xda; // magic + * [1] = 0xbe; // magic + * [2] = cyl_hi + * [3] = cyl_lo + * [4] = heads + * [5] = sectors + * [6] = interleave + * [7] = 00 // ?? + * [8] = 01 // ?? + * [9] = 03 // ?? + * [10] = landing_hi + * [11] = landing_lo + * [12] = 'SEAGATESTxxxxxx' // magic + * [29] .. = 00 + * [41] = 02 // ?? + */ case STATE_START_COMMAND: + /* Send geometry data. */ + dev->buff_pos = 0; + dev->buff_cnt = SECTOR_SIZE; + memset(dev->buff, 0x00, dev->buff_cnt); + dev->buff[0] = 0xda; + dev->buff[1] = 0xbe; + dev->buff[2] = (drive->tracks >> 8) & 0xff; + dev->buff[3] = drive->tracks & 0xff; + dev->buff[4] = drive->hpc; + dev->buff[5] = drive->spt; + dev->buff[6] = drive->interleave; + dev->buff[7] = 0x00; + dev->buff[8] = 0x01; + dev->buff[9] = 0x03; + dev->buff[10] = (drive->tracks >> 8) & 0xff; + dev->buff[11] = drive->tracks & 0xff; + memcpy(&dev->buff[12], "SEAGATESTxxxxxx", 15); + dev->buff[41] = 0x02; dev->state = STATE_SEND_DATA; - dev->data_pos = 0; - dev->data_len = 16; dev->status = STAT_BSY | STAT_IO | STAT_REQ; - memset(dev->data, 0, 16); - dev->data[0x4] = drive->tracks & 0xff; - dev->data[0x5] = (drive->tracks >> 8) & 0xff; - dev->data[0xa] = drive->hpc; break; case STATE_SENT_DATA: st506_complete(dev); break; + } else { + st506_error(dev, ERR_BAD_COMMAND); + st506_complete(dev); } break; - case CMD_DTC_SET_GEOMETRY: - switch (dev->state) { + case CMD_SET_GEOMETRY_ST11: + if (dev->type == 11 || dev->type == 12) switch (dev->state) { case STATE_START_COMMAND: + dev->buff_pos = 0; + dev->buff_cnt = 512; // 42 + memset(dev->buff, 0x00, dev->buff_cnt); dev->state = STATE_RECEIVE_DATA; - dev->data_pos = 0; - dev->data_len = 16; dev->status = STAT_BSY | STAT_REQ; break; case STATE_RECEIVED_DATA: - /*Bit of a cheat here - we always report the actual geometry of the drive in use*/ +INFO("ST506: GEO data for drive %i:\n", dev->drive_sel); +INFO("ST506: [ %02x %02x %02x %02x %02x %02x %02x %02x ]\n", +dev->buff[0], dev->buff[1], dev->buff[2], dev->buff[3], +dev->buff[4], dev->buff[5], dev->buff[6], dev->buff[7]); + st506_complete(dev); + break; + } else if (dev->type == 1) { + /* DTC sends this.. */ + st506_complete(dev); + } else { + st506_error(dev, ERR_BAD_COMMAND); + st506_complete(dev); + } + break; + + case CMD_SET_STEP_RATE_DTC: + st506_complete(dev); + break; + + case CMD_GET_DRIVE_PARAMS_DTC: + switch (dev->state) { + case STATE_START_COMMAND: + dev->buff_pos = 0; + dev->buff_cnt = 4; + memset(dev->buff, 0x00, dev->buff_cnt); + dev->buff[0] = drive->tracks & 0xff; + dev->buff[1] = ((drive->tracks >> 2) & 0xc0) | + drive->spt; + dev->buff[2] = drive->hpc - 1; + dev->status = STAT_BSY | STAT_IO | STAT_REQ; + dev->state = STATE_SEND_DATA; + break; + + case STATE_SENT_DATA: + st506_complete(dev); + break; + } + break; + + case CMD_SET_GEOMETRY_DTC: + switch (dev->state) { + case STATE_START_COMMAND: + val = dev->command[1] & 0x01; + DEBUG("ST506: DTC_GET_GEOMETRY(%i) %i\n", + dev->drive_sel, val); + dev->buff_pos = 0; + dev->buff_cnt = 16; + dev->status = STAT_BSY | STAT_REQ; + dev->state = STATE_RECEIVE_DATA; + break; + + case STATE_RECEIVED_DATA: + /* FIXME: ignore the results. */ st506_complete(dev); break; } break; + case CMD_GET_GEOMETRY_DTC: + switch (dev->state) { + case STATE_START_COMMAND: + val = dev->command[1] & 0x01; + DEBUG("ST506: DTC_GET_GEOMETRY(%i) %i\n", + dev->drive_sel, val); + dev->buff_pos = 0; + dev->buff_cnt = 16; + memset(dev->buff, 0x00, dev->buff_cnt); + dev->buff[4] = drive->tracks & 0xff; + dev->buff[5] = (drive->tracks >> 8) & 0xff; + dev->buff[10] = drive->hpc; + dev->status = STAT_BSY | STAT_IO | STAT_REQ; + dev->state = STATE_SEND_DATA; + break; + + case STATE_SENT_DATA: + st506_complete(dev); + break; + } + break; + default: - fatal("Unknown Xebec command - %02x %02x %02x %02x %02x %02x\n", - dev->command[0], dev->command[1], - dev->command[2], dev->command[3], - dev->command[4], dev->command[5]); + ERRLOG("ST506: unknown command:\n"); + ERRLOG("ST506: %02x %02x %02x %02x %02x %02x\n", + dev->command[0], dev->command[1], dev->command[2], + dev->command[3], dev->command[4], dev->command[5]); + st506_error(dev, ERR_BAD_COMMAND); + st506_complete(dev); } } +/* Read from one of the registers. */ +static uint8_t +st506_read(uint16_t port, void *priv) +{ + hdc_t *dev = (hdc_t *)priv; + uint8_t ret = 0xff; + + switch (port & 3) { + case 0: /* read data */ + dev->status &= ~STAT_IRQ; + switch (dev->state) { + case STATE_COMPLETION_BYTE: + ret = dev->compl; + dev->status = 0x00; + dev->state = STATE_IDLE; + break; + + case STATE_SEND_DATA: + ret = dev->buff[dev->buff_pos++]; + if (dev->buff_pos == dev->buff_cnt) { + dev->buff_pos = 0; + dev->buff_cnt = 0; + dev->status = STAT_BSY; + dev->state = STATE_SENT_DATA; + dev->callback = ST506_TIME; + } + break; + } + break; + + case 1: /* read status */ + ret = dev->status; + break; + + case 2: /* read option jumpers */ + ret = dev->switches; + break; + } + DBGLOG(1, "ST506: read(%04x) = %02x\n", port, ret); + + return(ret); +} + + +/* Write to one of the registers. */ +static void +st506_write(uint16_t port, uint8_t val, void *priv) +{ + hdc_t *dev = (hdc_t *)priv; + + DBGLOG(1, "ST506: write(%04x, %02x)\n", port, val); + switch (port & 3) { + case 0: /* write data */ + switch (dev->state) { + case STATE_RECEIVE_COMMAND: /* command data */ + dev->buff[dev->buff_pos++] = val; + if (dev->buff_pos == dev->buff_cnt) { + /* We have a new command. */ + memcpy(dev->command, dev->buff, dev->buff_cnt); + dev->buff_pos = 0; + dev->buff_cnt = 0; + dev->status = STAT_BSY; + dev->state = STATE_START_COMMAND; + dev->callback = ST506_TIME; + } + break; + + case STATE_RECEIVE_DATA: /* data */ + dev->buff[dev->buff_pos++] = val; + if (dev->buff_pos == dev->buff_cnt) { + dev->buff_pos = 0; + dev->buff_cnt = 0; + dev->status = STAT_BSY; + dev->state = STATE_RECEIVED_DATA; + dev->callback = ST506_TIME; + } + break; + } + break; + + case 1: /* controller reset */ + dev->status = 0x00; + break; + + case 2: /* generate controller-select-pulse */ + dev->status = STAT_BSY | STAT_CD | STAT_REQ; + dev->buff_pos = 0; + dev->buff_cnt = sizeof(dev->command); + dev->state = STATE_RECEIVE_COMMAND; + break; + + case 3: /* DMA/IRQ enable register */ + dev->irq_dma = val; + break; + } +} + + +/* Write to ROM (or scratchpad RAM.) */ +static void +mem_write(uint32_t addr, uint8_t val, void *priv) +{ + hdc_t *dev = (hdc_t *)priv; + uint32_t ptr, mask = 0; + + addr &= dev->bios_rom.mask; + + switch(dev->type) { + case 11: /* ST-11M */ + case 12: /* ST-11R */ + mask = 0x1fff; /* ST-11 decodes RAM on each 8K block */ + break; + + default: + break; + } + + ptr = (dev->bios_rom.mask & mask) - dev->bios_ram; + if (mask && ((addr & mask) > ptr) && + ((addr & mask) < (ptr + dev->bios_ram))) { + dev->scratch[addr & (dev->bios_ram - 1)] = val; + } +} + + +static uint8_t +mem_read(uint32_t addr, void *priv) +{ + hdc_t *dev = (hdc_t *)priv; + uint32_t ptr, mask = 0; + uint8_t ret = 0xff; + + addr &= dev->bios_rom.mask; + + switch(dev->type) { + case 0: /* Xebec */ + if (addr >= 0x001000) + DEBUG("ST506: Xebec ROM access(0x%06lx)\n", addr); + break; + + case 1: /* DTC */ + if (addr >= 0x002000) + DEBUG("ST506: DTC-5150X ROM access(0x%06lx)\n", addr); + break; + + case 11: /* ST-11M */ + case 12: /* ST-11R */ + mask = 0x1fff; /* ST-11 decodes RAM on each 8K block */ + break; + + default: + break; + } + + ptr = (dev->bios_rom.mask & mask) - dev->bios_ram; + if (mask && ((addr & mask) > ptr) && + ((addr & mask) < (ptr + dev->bios_ram))) { + ret = dev->scratch[addr & (dev->bios_ram - 1)]; + } else + ret = dev->bios_rom.rom[addr]; + + return(ret); +} + + +/* + * Set up and load the ROM BIOS for this controller. + * + * This is straightforward for most, but some (like the ST-11x) + * map part of the area as scratchpad RAM, so we cannot use the + * standard 'rom_init' function here. + */ +static void +loadrom(hdc_t *dev, const wchar_t *fn) +{ + uint32_t size; + FILE *fp; + + if ((fp = plat_fopen(rom_path(fn), L"rb")) == NULL) { + ERRLOG("ST506: BIOS ROM '%ls' not found!\n", fn); + return; + } + + /* Initialize the ROM entry. */ + memset(&dev->bios_rom, 0x00, sizeof(rom_t)); + + /* Manually load and process the ROM image. */ + (void)fseek(fp, 0L, SEEK_END); + size = ftell(fp); + (void)fseek(fp, 0L, SEEK_SET); + + /* + * The Xebec and DTC-5150X ROMs seem to be probing for + * (other) ROMs at addresses between their own size and + * 16K, at 2K blocks. So, we must enable all of that.. + */ + if (dev->type < 2) + size = 16384; + + /* Load the ROM data. */ + dev->bios_rom.rom = (uint8_t *)mem_alloc(size); + memset(dev->bios_rom.rom, 0xff, size); + (void)fread(dev->bios_rom.rom, size, 1, fp); + (void)fclose(fp); + + /* Set up an address mask for this memory. */ + dev->bios_size = size; + dev->bios_rom.mask = (size - 1); + + /* Map this system into the memory map. */ + mem_map_add(&dev->bios_rom.mapping, dev->bios_addr, size, + mem_read,NULL,NULL, mem_write,NULL,NULL, + dev->bios_rom.rom, MEM_MAPPING_EXTERNAL, dev); +} + + static void loadhd(hdc_t *dev, int c, int d, const wchar_t *fn) { - drive_t *drive = &dev->drives[d]; + drive_t *drive = &dev->drives[c]; if (! hdd_image_load(d)) { drive->present = 0; return; } + /* Make sure we can do this. */ + if (hdd[d].spt != dev->spt) { + /* + * Uh-oh, MFM/RLL mismatch. + * + * Although this would be no issue in the code itself, + * most of the BIOSes were hardwired to whatever their + * native SPT setting was, so, do not allow this here. + */ + ERRLOG("ST506: drive%i: MFM/RLL mismatch (%i/%i)\n", + c, hdd[d].spt, dev->spt); + hdd_image_close(d); + drive->present = 0; + return; + } + drive->spt = (uint8_t)hdd[d].spt; drive->hpc = (uint8_t)hdd[d].hpc; drive->tracks = (uint16_t)hdd[d].tracks; + drive->hdd_num = d; drive->present = 1; } +/* Set the "drive type" switches for the IBM Xebec controller. */ static void set_switches(hdc_t *dev) { drive_t *drive; int c, d; - dev->switches = 0; + dev->switches = 0x00; - for (d = 0; d < 2; d++) { + for (d = 0; d < ST506_NUM; d++) { drive = &dev->drives[d]; if (! drive->present) continue; @@ -832,10 +1327,10 @@ set_switches(hdc_t *dev) INFO("ST506: "); if (c == 4) - INFO("*WARNING* drive%d unsupported", d); + INFO("*WARNING* drive%i unsupported", d); else - INFO("drive%d is type %d", d, c); - INFO(" (%d/%d/%d)\n", drive->tracks, drive->hpc, drive->spt); + INFO("drive%i is type %i", d, c); + INFO(" (%i/%i/%i)\n", drive->tracks, drive->hpc, drive->spt); } } @@ -849,44 +1344,105 @@ st506_init(const device_t *info) dev = (hdc_t *)mem_alloc(sizeof(hdc_t)); memset(dev, 0x00, sizeof(hdc_t)); + dev->type = info->local & 255; - DEBUG("ST506: looking for disks..\n"); + /* Set defaults for the controller. */ + dev->spt = MFM_SECTORS; + dev->base = 0x0320; + dev->irq = 5; + dev->dma = 3; + dev->bios_addr = 0xc8000; - c = 0; - for (i = 0; i < HDD_NUM; i++) { + switch(dev->type) { + case 0: /* Xebec (MFM) */ + fn = XEBEC_BIOS_FILE; + break; + + case 1: /* DTC5150 (MFM) */ + fn = DTC_BIOS_FILE; + dev->switches = 0xff; + break; + + case 12: /* Seagate ST-11R (RLL) */ + dev->spt = RLL_SECTORS; + /*FALLTHROUGH*/ + + case 11: /* Seagate ST-11M (MFM) */ + fn = ST11_BIOS_FILE; + dev->switches = 0x01; /* fixed */ + dev->misc = device_get_config_int("revision"); + dev->base = device_get_config_hex16("base"); + dev->irq = device_get_config_int("irq"); + dev->bios_addr = device_get_config_hex20("bios_addr"); + dev->bios_ram = 64; /* scratch RAM size */ + + /* + * Industrial Madness Alert. + * + * With the ST-11 controller, Seagate decided to act + * like they owned the industry, and reserved the + * first cylinder of a drive for the controller. So, + * when the host accessed cylinder 0, that would be + * the actual cylinder 1 on the drive, and so on. + */ + dev->cyl_off = 1; + break; + + case 21: /* Western Digital WD1002A-WX1 (MFM) */ + fn = WD1002A_WX1_BIOS_FILE; + dev->switches = 0x10; /* autobios */ + dev->base = device_get_config_hex16("base"); + dev->irq = device_get_config_int("irq"); + dev->bios_addr = device_get_config_hex20("bios_addr"); + break; + + case 22: /* Western Digital WD1002A-27X (RLL) */ + fn = WD1002A_27X_BIOS_FILE; + dev->switches = 0x10; /* autobios */ + dev->spt = RLL_SECTORS; + dev->base = device_get_config_hex16("base"); + dev->irq = device_get_config_int("irq"); + dev->bios_addr = device_get_config_hex20("bios_addr"); + break; + } + + /* Load the ROM BIOS. */ + loadrom(dev, fn); + + /* Set up the I/O region. */ + io_sethandler(dev->base, 4, + st506_read,NULL,NULL, st506_write,NULL,NULL, dev); + + /* Add the timer. */ + timer_add(st506_callback, &dev->callback, &dev->callback, dev); + + INFO("ST506: %s (I/O=%03X, IRQ=%i, DMA=%i, BIOS @0x%06lX, size %lu)\n", + info->name,dev->base,dev->irq,dev->dma, dev->bios_addr,dev->bios_size); + + /* Load any drives configured for us. */ + INFO("ST506: looking for disks..\n"); + for (c = 0, i = 0; i < HDD_NUM; i++) { if ((hdd[i].bus == HDD_BUS_ST506) && (hdd[i].bus_id.st506_channel < ST506_NUM)) { - DEBUG("Found ST506 hard disk on channel %i\n", hdd[i].bus_id.st506_channel); + INFO("ST506: disk '%ls' on channel %i\n", + hdd[i].fn, hdd[i].bus_id.st506_channel); loadhd(dev, hdd[i].bus_id.st506_channel, i, hdd[i].fn); if (++c > ST506_NUM) break; } } - DEBUG("ST506: %d disks loaded.\n", c); + INFO("ST506: %i disks loaded.\n", c); - switch(info->local & 255) { - case 0: /* Xebec */ - fn = XEBEC_BIOS_FILE; - set_switches(dev); - break; + /* For the Xebec, set the switches now. */ + if (dev->type == 0) + set_switches(dev); - case 1: /* DTC5150 */ - fn = DTC_BIOS_FILE; - dev->switches = 0xff; - dev->drives[0].cfg_cyl = dev->drives[0].tracks; - dev->drives[0].cfg_hpc = dev->drives[0].hpc; - dev->drives[1].cfg_cyl = dev->drives[1].tracks; - dev->drives[1].cfg_hpc = dev->drives[1].hpc; - break; + /* Initial "active" drive parameters. */ + for (c = 0; c < ST506_NUM; c++) { + dev->drives[c].cfg_cyl = dev->drives[c].tracks; + dev->drives[c].cfg_hpc = dev->drives[c].hpc; + dev->drives[c].cfg_spt = dev->drives[c].spt; } - rom_init(&dev->bios_rom, fn, - 0xc8000, 0x4000, 0x3fff, 0, MEM_MAPPING_EXTERNAL); - - io_sethandler(0x0320, 4, - st506_read,NULL,NULL, st506_write,NULL,NULL, dev); - - timer_add(st506_callback, &dev->callback, &dev->callback, dev); - return(dev); } @@ -895,14 +1451,20 @@ static void st506_close(void *priv) { hdc_t *dev = (hdc_t *)priv; + drive_t *drive; int d; for (d = 0; d < ST506_NUM; d++) { - drive_t *drive = &dev->drives[d]; + drive = &dev->drives[d]; hdd_image_close(drive->hdd_num); } + if (dev->bios_rom.rom != NULL) { + free(dev->bios_rom.rom); + dev->bios_rom.rom = NULL; + } + free(dev); } @@ -920,6 +1482,188 @@ dtc5150x_available(void) return(rom_present(DTC_BIOS_FILE)); } +static int +st11_m_available(void) +{ + return(rom_present(ST11_BIOS_FILE)); +} + +static int +st11_r_available(void) +{ + return(rom_present(ST11_BIOS_FILE)); +} + +static int +wd1002a_wx1_available(void) +{ + return(rom_present(WD1002A_WX1_BIOS_FILE)); +} + +static int +wd1002a_27x_available(void) +{ + return(rom_present(WD1002A_27X_BIOS_FILE)); +} + + +static const device_config_t dtc_config[] = { + { + "bios_addr", "BIOS address", CONFIG_HEX20, "", 0xc8000, + { + { + "Disabled", 0x00000 + }, + { + "C800H", 0xc8000 + }, + { + "CA00H", 0xca000 + }, + { + "D800H", 0xd8000 + }, + { + "F400H", 0xf4000 + }, + { + "" + } + } + }, + { + "", "", -1 + } +}; + +static const device_config_t st11_config[] = { + { + "base", "Address", CONFIG_HEX16, "", 0x0320, + { + { + "320H", 0x0320 + }, + { + "324H", 0x0324 + }, + { + "328H", 0x0328 + }, + { + "32CH", 0x032c + }, + { + "" + } + } + }, + { + "irq", "IRQ", CONFIG_SELECTION, "", 5, + { + { + "IRQ 2", 2 + }, + { + "IRQ 5", 5 + }, + { + "" + } + } + }, + { + "bios_addr", "BIOS address", CONFIG_HEX20, "", 0xc8000, + { + { + "Disabled", 0x00000 + }, + { + "C800H", 0xc8000 + }, + { + "D000H", 0xd0000 + }, + { + "D800H", 0xd8000 + }, + { + "E000H", 0xe0000 + }, + { + "" + } + } + }, + { + "revision", "Board Revision", CONFIG_SELECTION, "", 5, + { + { + "Rev. 00", 0 + }, + { + "Rev. 01", 1 + }, + { + "Rev. 05", 5 + }, + { + "" + } + } + }, + { + "", "", -1 + } +}; + +static const device_config_t wd_config[] = { + { + "bios_addr", "BIOS address", CONFIG_HEX20, "", 0xc8000, + { + { + "Disabled", 0x00000 + }, + { + "C800H", 0xc8000 + }, + { + "" + } + } + }, + { + "base", "Address", CONFIG_HEX16, "", 0x0320, + { + { + "320H", 0x0320 + }, + { + "324H", 0x0324 + }, + { + "" + } + } + }, + { + "irq", "IRQ", CONFIG_SELECTION, "", 5, + { + { + "IRQ 2", 2 + }, + { + "IRQ 5", 5 + }, + { + "" + } + } + }, + { + "", "", -1 + } +}; + const device_t st506_xt_xebec_device = { "IBM PC Fixed Disk Adapter", @@ -938,5 +1682,45 @@ const device_t st506_xt_dtc5150x_device = { st506_init, st506_close, NULL, dtc5150x_available, NULL, NULL, NULL, - NULL + dtc_config +}; + +const device_t st506_xt_st11_m_device = { + "ST-11M Fixed Disk Adapter", + DEVICE_ISA, + (HDD_BUS_ST506 << 8) | 11, + st506_init, st506_close, NULL, + st11_m_available, + NULL, NULL, NULL, + st11_config +}; + +const device_t st506_xt_st11_r_device = { + "ST-11R RLL Fixed Disk Adapter", + DEVICE_ISA, + (HDD_BUS_ST506 << 8) | 12, + st506_init, st506_close, NULL, + st11_r_available, + NULL, NULL, NULL, + st11_config +}; + +const device_t st506_xt_wd1002a_wx1_device = { + "WD1002A-WX1 Fixed Disk Adapter", + DEVICE_ISA, + (HDD_BUS_ST506 << 8) | 21, + st506_init, st506_close, NULL, + wd1002a_wx1_available, + NULL, NULL, NULL, + wd_config +}; + +const device_t st506_xt_wd1002a_27x_device = { + "WD1002A-27X RLL Fixed Disk Adapter", + DEVICE_ISA, + (HDD_BUS_ST506 << 8) | 22, + st506_init, st506_close, NULL, + wd1002a_27x_available, + NULL, NULL, NULL, + wd_config }; diff --git a/src/devices/system/dma.c b/src/devices/system/dma.c index 860e6df..6bdc3e6 100644 --- a/src/devices/system/dma.c +++ b/src/devices/system/dma.c @@ -427,7 +427,6 @@ dma_write(uint16_t addr, uint8_t val, UNUSED(void *priv)) dma_m |= (1 << (val & 3)); else dma_m &= ~(1 << (val & 3)); -if ((val & 3) == 3) INFO("DMA: mask=%02x\n", dma_m); return; case 0xb: /*Mode*/ diff --git a/src/devices/video/vid_cga.c b/src/devices/video/vid_cga.c index db70ffd..1762259 100644 --- a/src/devices/video/vid_cga.c +++ b/src/devices/video/vid_cga.c @@ -8,7 +8,7 @@ * * Emulation of the old and new IBM CGA graphics cards. * - * Version: @(#)vid_cga.c 1.0.10 2019/02/12 + * Version: @(#)vid_cga.c 1.0.11 2019/01/01 * * Authors: Fred N. van Kempen, * Miran Grca, @@ -55,8 +55,6 @@ #include "vid_cga_comp.h" -#define CGA_FONT_ROM_PATH L"video/ibm/cga/cga.rom" - #define CGA_RGB 0 #define CGA_COMPOSITE 1 diff --git a/src/devices/video/vid_cga.h b/src/devices/video/vid_cga.h index e47214e..a89257a 100644 --- a/src/devices/video/vid_cga.h +++ b/src/devices/video/vid_cga.h @@ -8,13 +8,13 @@ * * Definitions for the CGA driver. * - * Version: @(#)vid_cga.h 1.0.5 2018/10/05 + * Version: @(#)vid_cga.h 1.0.6 2019/03/01 * * Authors: Fred N. van Kempen, * Miran Grca, * Sarah Walker, * - * Copyright 2017,2018 Fred N. van Kempen. + * Copyright 2017-2019 Fred N. van Kempen. * Copyright 2016-2018 Miran Grca. * Copyright 2008-2018 Sarah Walker. * @@ -40,6 +40,9 @@ # define VIDEO_CGA_H +#define CGA_FONT_ROM_PATH L"video/ibm/cga/cga.rom" + + typedef struct { mem_map_t mapping; diff --git a/src/devices/video/vid_im1024.c b/src/devices/video/vid_im1024.c new file mode 100644 index 0000000..6fab2aa --- /dev/null +++ b/src/devices/video/vid_im1024.c @@ -0,0 +1,913 @@ +/* + * VARCem Virtual ARchaeological Computer EMulator. + * An emulator of (mostly) x86-based PC systems and devices, + * using the ISA,EISA,VLB,MCA and PCI system buses, roughly + * spanning the era between 1981 and 1995. + * + * This file is part of the VARCem Project. + * + * 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. + * + * Version: @(#)vid_im1024.c 1.0.1 2019/03/01 + * + * Authors: Fred N. van Kempen, + * John Elliott, + * + * Copyright 2019 Fred N. van Kempen. + * Copyright 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 +#include +#include +#include +#include +#include +#include "../../emu.h" +#include "../../io.h" +#include "../../mem.h" +#include "../../rom.h" +#include "../../timer.h" +#include "../../device.h" +#include "../system/pit.h" +#include "../../plat.h" +#include "video.h" +#include "vid_pgc.h" + + +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 void +fifo_write(im1024_t *dev, uint8_t val) +{ +#if 0 + DEBUG(("fifo_write: %02x [rd=%04x wr=%04x]\n", + val, dev->fifo_rdptr, dev->fifo_wrptr)); +#endif + + if (((dev->fifo_wrptr + 1) % dev->fifo_len) == dev->fifo_rdptr) { + /* FIFO is full. Double its size. */ + uint8_t *buf; + + DEBUG("fifo_resize: %d to %d\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; + + DBGLOG(1, "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 either from 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 ((dev->fifo_wrptr == dev->fifo_rdptr) && + (pgc->mapram[0x300] == pgc->mapram[0x301])) { + pgc->waiting_input_fifo = 1; + pgc_sleep(pgc); + } + + 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); + + DBGLOG(1, "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, h; + uint8_t a, 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; + + DEBUG("IMGSIZ %d,%d,%d,%d\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; + + DEBUG("IPREC %d\n", param); +} + + +/* 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, y; + + if (! pgc_param_word(pgc, &x)) return; + if (! pgc_param_word(pgc, &y)) return; + + DEBUG("PAN %d,%d\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], y[257]; + uint16_t linemask = pgc->line_pattern; + uint8_t count; + unsigned n; + + if (! pgc_param_byte(pgc, &count)) return; + + DEBUG("PLINE (%d) ", count); + for (n = 0; n < count; n++) { + if (! pgc_param_word(pgc, &x[n])) return; + if (! pgc_param_word(pgc, &y[n])) return; + DEBUG(" (%d,%d)\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, with no + * clipping. */ +static void +hndl_blkmov(pgc_t *pgc) +{ +#if 0 + im1024_t *dev = (im1024_t *)pgc; +#endif + int16_t x0, y0; + int16_t x1, y1; + int16_t x2, 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; + + DEBUG("BLKMOV %d,%d,%d,%d,%d,%d\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 +} + + +/* This overrides the PGC ELIPSE command to parse its parameters as words + * rather than coordinates */ +static void +hndl_ellipse(pgc_t *pgc) +{ + int16_t x, y; + + if (! pgc_param_word(pgc, &x)) return; + if (! pgc_param_word(pgc, &y)) return; + + DEBUG("ELLIPSE %d,%d @ %d,%d\n", x, y, pgc->x >> 16, pgc->y >> 16); + + pgc_draw_ellipse(pgc, x << 16, y << 16); +} + + +/* This overrides the PGC MOVE command to parse its parameters as words + * rather than coordinates */ +static void +hndl_move(pgc_t *pgc) +{ + int16_t x, y; + + if (! pgc_param_word(pgc, &x)) return; + if (! pgc_param_word(pgc, &y)) return; + + pgc->x = x << 16; + pgc->y = y << 16; + + DEBUG("MOVE %d,%d\n", x, y); +} + + +/* This overrides the PGC DRAW command to parse its parameters as words + * rather than coordinates */ +static void +hndl_draw(pgc_t *pgc) +{ + int16_t x, y; + + if (! pgc_param_word(pgc, &x)) return; + if (! pgc_param_word(pgc, &y)) return; + + DEBUG("DRAW %d,%d to %d,%d\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; +} + + +/* This overrides the PGC POLY command to parse its parameters as words + * rather than coordinates */ +static void +hndl_poly(pgc_t *pgc) +{ + int32_t *x, *y, *nx, *ny; + int16_t xw, yw, mask; + unsigned realcount = 0; + unsigned n, as = 256; + int parsing = 1; + uint8_t count; + + x = (int32_t *)mem_alloc(as * sizeof(int32_t)); + y = (int32_t *)mem_alloc(as * sizeof(int32_t)); + + if (!x || !y) { + DEBUG("hndl_poly: malloc failed\n"); + return; + } + + while (parsing) { + if (! pgc_param_byte(pgc, &count)) 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) { + DEBUG("hndl_poly: realloc failed\n"); + break; + } + x = nx; + y = ny; + as *= 2; + } + + for (n = 0; n < count; n++) { + if (! pgc_param_word(pgc, &xw)) return; + if (! pgc_param_word(pgc, &yw)) 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) { + DEBUG("hndl_poly: POLY continues!\n"); + parsing = 1; + + /* Swallow the POLY. */ + pgc->clcur->rdptr++; + } + }; + + DEBUG("POLY (%i) fill_mode=%d\n", realcount, pgc->fill_mode); + for (n = 0; n < realcount; n++) { + DEBUG(" (%i,%i)\n", x[n] >> 16, y[n] >> 16); + } + + 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, int c) +{ + uint8_t count; + + DEBUG("parse_poly\n"); + + if (! pgc_param_byte(pgc, &count)) return 0; + + DEBUG("parse_poly: count=%02x\n", count); + if (! pgc_commandlist_append(cl, count)) { + pgc_error(pgc, PGC_ERROR_OVERFLOW); + return 0; + } + + DEBUG("parse_poly: parse %d words\n", 2 * count); + + return pgc_parse_words(pgc, cl, count * 2); +} + + +/* This overrides the PGC RECT command to parse its parameters as words + * rather than coordinates */ +static void +hndl_rect(pgc_t *pgc) +{ + int16_t x0, y0, x1, y1, p, 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; } + DEBUG("RECT (%d,%d) -> (%d,%d)\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); + } +} + + +/* TODO: Text drawing should probably be implemented in vid_pgc.c rather + * than vid_im1024.c */ +static void +hndl_tdefin(pgc_t *pgc) +{ + im1024_t *dev = (im1024_t *)pgc; + uint8_t ch, bt; + uint8_t rows, cols; + unsigned len, n; + + if (! pgc_param_byte(pgc, &ch)) return; + if (! pgc_param_byte(pgc, &rows)) return; + if (! pgc_param_byte(pgc, &cols)) return; + + DEBUG("TDEFIN (%d,%d,%d) 0x%02x 0x%02x\n", + ch, rows, cols, pgc->mapram[0x300], pgc->mapram[0x301]); + + len = ((cols + 7) / 8) * rows; + for (n = 0; n < len; n++) { +// char buf[10]; + + if (! pgc_param_byte(pgc, &bt)) return; + +// buf[0] = 0; +// for (mask = 0x80; mask != 0; mask >>= 1) { +// if (bt & mask) strcat(buf, "#"); +// else strcat(buf, "-"); +// ++x; +// if (x == cols) { strcat(buf, "\n"); x = 0; } +// } +// DEBUG(buf); + + if (n < sizeof(dev->font[ch])) + dev->font[ch][n] = bt; + } + + dev->fontx[ch] = rows; + dev->fonty[ch] = cols; +} + + +static void +hndl_twrite(pgc_t *pgc) +{ + uint8_t buf[256], rbuf[256]; + im1024_t *dev = (im1024_t *)pgc; + uint8_t count, mask, *row; + int x, y, wb, 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; + for (n = 0; n <= count; n++) { + if (isprint(buf[n]) || 0 == buf[n]) + rbuf[n] = buf[n]; + else + rbuf[n] = '?'; + } + + pgc_sto_raster(pgc, &x0, &y0); + DEBUG("TWRITE (%d,%-*.*s) x0=%d y0=%d\n", + count, count, count, rbuf, x0, y0); + + for (n = 0; n < count; n++) { + wb = (dev->fontx[buf[n]] + 7) / 8; + DEBUG("ch=0x%02x w=%d h=%d wb=%d\n", + buf[n], dev->fontx[buf[n]], dev->fonty[buf[n]], wb); + + for (y = 0; y < dev->fonty[buf[n]]; y++) { + mask = 0x80; + row = &dev->font[buf[n]][y * wb]; + for (x = 0; x < dev->fontx[buf[n]]; x++) { + rbuf[x] = (row[0] & mask) ? '#' : '-'; + if (row[0] & mask) + pgc_plot(pgc, x + x0, y0 - y); + mask = mask >> 1; + if (mask == 0) { + mask = 0x80; + row++; + } + } + rbuf[x++] = '\n'; + rbuf[x++] = 0; +// DEBUG(rbuf); + } + + x0 += dev->fontx[buf[n]]; + } +} + + +static void +hndl_imagew(pgc_t *pgc) +{ + int16_t vp_x1, vp_y1, vp_x2, vp_y2; + int16_t row1, col1, col2; + uint8_t v1, v2; + + if (! pgc_param_word(pgc, &row1)) return; + if (! pgc_param_word(pgc, &col1)) return; + if (! pgc_param_word(pgc, &col2)) return; + + /* IMAGEW already uses raster coordinates so there is no need to + * convert it */ + DEBUG("IMAGEW (row=%d,col1=%d,col2=%d)\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++; + } + + return; + } + + /* 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, y = pgc->y >> 16; + + pgc_sto_raster(pgc, &x, &y); + + DEBUG("Dot @ %d,%d ink=%d mode=%d\n", x, y, pgc->colour, 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, x1, y0, y1; + int16_t p,q; + + 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; + + /* IMAGEX already uses raster coordinates so don't convert */ + DEBUG("IMAGEX (%d,%d,%d,%d)\n", x0,y0,x1,y1); + + for (p = y0; p <= y1; p++) { + for (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 }, + { "IW", 0xd9, hndl_imagew, NULL, 0 }, + { "IMAGEX", 0xda, hndl_imagex, NULL, 0 }, + { "TWRITE", 0x8b, hndl_twrite, NULL, 0 }, + { "TDEFIN", 0x84, hndl_tdefin, NULL, 0 }, + { "TD", 0x84, hndl_tdefin, NULL, 0 }, + { "IPREC", 0xe4, hndl_iprec, NULL, 0 }, + { "IMGSIZ", 0x4e, hndl_imgsiz, NULL, 0 }, + { "LUT8", 0xe6, pgc_hndl_lut8, NULL, 0 }, + { "L8", 0xe6, pgc_hndl_lut8, NULL, 0 }, + { "LUT8RD", 0x53, pgc_hndl_lut8rd,NULL, 0 }, + { "L8RD", 0x53, pgc_hndl_lut8rd,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(const device_t *info) +{ + im1024_t *dev; + + dev = (im1024_t *)mem_alloc(sizeof(im1024_t)); + memset(dev, 0x00, sizeof(im1024_t)); + + dev->fifo = (uint8_t *)mem_alloc(4096); + dev->fifo_len = 4096; + 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); + + dev->pgc.commands = im1024_commands; + + mem_map_set_handler(&dev->pgc.mapping, + im1024_read,NULL,NULL, im1024_write,NULL,NULL); + + video_inform(VID_TYPE_CGA, info->vid_timing); + + return(dev); +} + + +static void +im1024_close(void *priv) +{ + im1024_t *dev = (im1024_t *)priv; + + pgc_close(&dev->pgc); + + free(dev); +} + + +#if 0 +static int +im1024_available(void) +{ + return rom_present(BIOS_ROM_PATH); +} +#endif + + +static void +speed_changed(void *priv) +{ + im1024_t *dev = (im1024_t *)priv; + + pgc_speed_changed(&dev->pgc); +} + + +static const video_timings_t im1024_timings = { VID_ISA,8,16,32,8,16,32 }; + +static const device_config_t im1024_config[] = { + { + "", "", -1 + } +}; + + +const device_t im1024_device = { + "ImageManager 1024", + DEVICE_ISA, + 0, + im1024_init, im1024_close, NULL, + NULL, + speed_changed, + NULL, + &im1024_timings, + im1024_config +}; diff --git a/src/devices/video/vid_pgc.c b/src/devices/video/vid_pgc.c new file mode 100644 index 0000000..a54ba2a --- /dev/null +++ b/src/devices/video/vid_pgc.c @@ -0,0 +1,2543 @@ +/* + * VARCem Virtual ARchaeological Computer EMulator. + * An emulator of (mostly) x86-based PC systems and devices, + * using the ISA,EISA,VLB,MCA and PCI system buses, roughly + * spanning the era between 1981 and 1995. + * + * This file is part of the VARCem Project. + * + * This implements just enough of the Professional Graphics + * Controller to act as a basis for the Vermont Microsystems + * IM-1024. + * + * PGC features implemented include: + * > The CGA-compatible display modes + * > Switching to and from native mode + * > Communicating with the host PC + * + * Numerous features are implemented partially or not at all, + * such as: + * > 2D drawing + * > 3D drawing + * > Command lists + * Some of these are marked TODO. + * + * The PGC has two display modes: CGA (in which it appears in + * the normal CGA memory and I/O ranges) and native (in which + * all functions are accessed through reads and writes to 1K + * of memory at 0xC6000). + * + * The PGC's 8088 processor monitors this buffer and executes + * instructions left there for it. We simulate this behavior + * with a separate thread. + * + * **NOTE** This driver does not yet work, pending (proper) conversion + * to the video backend, which is different from PCem's. --FvK + * + * Version: @(#)vid_pgc.c 1.0.1 2019/03/01 + * + * Authors: Fred N. van Kempen, + * John Elliott, + * + * Copyright 2019 Fred N. van Kempen. + * Copyright 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 +#include +#include +#include +#include +#include +#include +#include "../../emu.h" +#include "../../io.h" +#include "../../mem.h" +#include "../../rom.h" +#include "../../timer.h" +#include "../../device.h" +#include "../system/pit.h" +#include "../../plat.h" +#include "video.h" +#include "vid_cga.h" +#include "vid_pgc.h" + + +#define PGC_CGA_WIDTH 640 +#define PGC_CGA_HEIGHT 400 + +#define HWORD(u) ((u) >> 16) +#define LWORD(u) ((u) & 0xffff) + + +int pgc_clist_byte(pgc_t *pgc, uint8_t *val); + +static int pgc_parse_command(pgc_t *, const pgc_cmd_t **); + + +static const char *pgc_err_msgs[] = { + "Range \r", + "Integer \r", + "Memory \r", + "Overflow\r", + "Digit \r", + "Opcode \r", + "Running \r", + "Stack \r", + "Too long\r", + "Area \r", + "Missing \r" +}; + + +/* Initial palettes */ +static const uint32_t init_palette[6][256] = { +#include "vid_pgc_palette.h" +}; + + +/* When idle, the PGC drawing thread sleeps. pgc_wake() awakens it - but + * not immediately. Like the Voodoo, it has a short delay so that writes + * can be batched */ +#define WAKE_DELAY (TIMER_USEC * 500) +void +pgc_wake(pgc_t *dev) +{ + if (! dev->wake_timer) { + timer_process(); + dev->wake_timer = WAKE_DELAY; + timer_update_outstanding(); + } +} + + +/* When the wake timer expires, that's when the drawing thread is actually + * woken */ +static void +wake_timer(void *priv) +{ + pgc_t *dev = (pgc_t *)priv; + + dev->wake_timer = 0; + DEBUG("pgc: Waking up\n"); + + thread_set_event(dev->pgc_wake_thread); +} + + +/* This is called by the drawing thread when it's waiting for the host + * to put more input in its input FIFO, or drain output from its output + * FIFO. */ +void +pgc_sleep(pgc_t *dev) +{ + DEBUG("pgc: Sleeping on %i %i %i 0x%02x 0x%02x\n", + dev->waiting_input_fifo, dev->waiting_output_fifo, + dev->waiting_error_fifo, dev->mapram[0x300], dev->mapram[0x301]); + + /* Race condition: If host wrote to the PGC during the pclog() that + * won't be noticed */ + if (dev->waiting_input_fifo && dev->mapram[0x300] != dev->mapram[0x301]) { + dev->waiting_input_fifo = 0; + return; + } + + /* Same if they read */ + if (dev->waiting_output_fifo && dev->mapram[0x302] != (uint8_t)(dev->mapram[0x303] - 1)) { + dev->waiting_output_fifo = 0; + return; + } + + thread_wait_event(dev->pgc_wake_thread, -1); + thread_reset_event(dev->pgc_wake_thread); +} + + +/* Switch between CGA mode (DISPLAY 1) and native mode (DISPLAY 0) */ +void +pgc_setdisplay(pgc_t *dev, int cga) +{ + if (dev->cga_selected != (dev->cga_enabled && cga)) { + dev->cga_selected = (dev->cga_enabled && cga); + + if (dev->cga_selected) { + mem_map_enable(&dev->cga_mapping); + dev->screenw = PGC_CGA_WIDTH; + dev->screenh = PGC_CGA_HEIGHT; + } else { + mem_map_disable(&dev->cga_mapping); + dev->screenw = dev->visw; + dev->screenh = dev->vish; + } + + pgc_recalctimings(dev); + } +} + + +/* Convert coordinates based on the current window / viewport to raster + * coordinates. */ +void +pgc_dto_raster(pgc_t *dev, double *x, double *y) +{ +#ifdef _LOGGING + double x0 = *x, y0 = *y; +#endif + + *x += (dev->vp_x1 - dev->win_x1); + *y += (dev->vp_y1 - dev->win_y1); + + DBGLOG(1, "Coords to raster: (%f, %f) -> (%f, %f)\n", x0, y0, *x, *y); +} + + +/* Overloads that take ints */ +void +pgc_sto_raster(pgc_t *dev, int16_t *x, int16_t *y) +{ + double xd = *x, yd = *y; + + pgc_dto_raster(dev, &xd, &yd); + *x = (int16_t)xd; + *y = (int16_t)yd; +} + + +void +pgc_ito_raster(pgc_t *dev, int32_t *x, int32_t *y) +{ + double xd = *x, yd = *y; + + pgc_dto_raster(dev, &xd, &yd); + *x = (int32_t)xd; + *y = (int32_t)yd; +} + + +/* + * Add a byte to a command list. + * + * We allow command lists to be arbitrarily large. + */ +int +pgc_commandlist_append(pgc_cl_t *list, uint8_t v) +{ + uint8_t *buf; + + if (list->listmax == 0 || list->list == NULL) { + list->list = (uint8_t *)mem_alloc(4096); + if (!list->list) { + DEBUG("Out of memory initializing command list\n"); + return 0; + } + list->listmax = 4096; + } + + while (list->wrptr >= list->listmax) { + buf = (uint8_t *)realloc(list->list, 2 * list->listmax); + if (!buf) { + DEBUG("Out of memory growing command list\n"); + return 0; + } + list->list = buf; + list->listmax *= 2; + } + + list->list[list->wrptr++] = v; + + return 1; +} + + +/* Beginning of a command list. Parse commands up to the next CLEND, + * storing them (in hex form) in the named command list. */ +static void +hndl_clbeg(pgc_t *dev) +{ + uint8_t param; + pgc_cl_t cl; + const pgc_cmd_t *cmd; + + if (! pgc_param_byte(dev, ¶m)) return; + DEBUG("CLBEG(%i)\n", param); + + memset(&cl, 0x00, sizeof(pgc_cl_t)); + + while (1) { + if (! pgc_parse_command(dev, &cmd)) { + /* PGC has been reset. */ + return; + } + if (!cmd) { + pgc_error(dev, PGC_ERROR_OPCODE); + return; + } else if (dev->hex_command == 0x71) { + /* CLEND */ + dev->clist[param] = cl; + return; + } else { + if (! pgc_commandlist_append(&cl, dev->hex_command)) { + pgc_error(dev, PGC_ERROR_OVERFLOW); + return; + } + + if (cmd->parser) { + if (!(*cmd->parser)(dev, &cl, cmd->p)) + return; + } + } + } +} + + +static void +hndl_clend(pgc_t *dev) +{ + /* Should not happen outside a CLBEG. */ +} + + +/* + * Execute a command list. + * + * If one was already executing, remember + * it so we can return to it afterwards. + */ +static void +hndl_clrun(pgc_t *dev) +{ + pgc_cl_t *clprev = dev->clcur; + uint8_t param; + + if (! pgc_param_byte(dev, ¶m)) return; + + dev->clcur = &dev->clist[param]; + dev->clcur->rdptr = 0; + dev->clcur->repeat = 1; + dev->clcur->chain = clprev; +} + + +/* Execute a command list multiple times. */ +static void +hndl_cloop(pgc_t *dev) +{ + pgc_cl_t *clprev = dev->clcur; + uint8_t param; + int16_t repeat; + + if (! pgc_param_byte(dev, ¶m)) return; + if (! pgc_param_word(dev, &repeat)) return; + + dev->clcur = &dev->clist[param]; + dev->clcur->rdptr = 0; + dev->clcur->repeat = repeat; + dev->clcur->chain = clprev; +} + + +/* Read back a command list. */ +static void +hndl_clread(pgc_t *dev) +{ + uint8_t param; + uint32_t n; + + if (! pgc_param_byte(dev, ¶m)) return; + + for (n = 0; n < dev->clist[param].wrptr; n++) { + if (! pgc_result_byte(dev, dev->clist[param].list[n])) + return; + } +} + + +/* Delete a command list. */ +static void +hndl_cldel(pgc_t *dev) +{ + uint8_t param; + + if (! pgc_param_byte(dev, ¶m)) return; + + memset(&dev->clist[param], 0, sizeof(pgc_cl_t)); +} + + +/* Clear the screen to a specified color. */ +static void +hndl_clears(pgc_t *dev) +{ + uint8_t param; + uint32_t y; + + if (! pgc_param_byte(dev, ¶m)) return; + + for (y = 0; y < dev->screenh; y++) + memset(dev->vram + y * dev->maxw, param, dev->screenw); +} + + +/* Select drawing color. */ +static void +hndl_color(pgc_t *dev) +{ + uint8_t param; + + if (! pgc_param_byte(dev, ¶m)) return; + + dev->colour = param; + DEBUG("COLOR(%i)\n", param); +} + + +/* + * Set drawing mode. + * + * 0 => Draw + * 1 => Invert + * 2 => XOR (IM-1024) + * 3 => AND (IM-1024) + */ +static void +hndl_linfun(pgc_t *dev) +{ + uint8_t param; + + if (! pgc_param_byte(dev, ¶m)) return; + + /* TODO: Not range-checked. Strictly speaking we should limit to 0-1 + * for the PGC and 0-3 for the IM-1024. */ + dev->draw_mode = param; + DEBUG("LINFUN(%i)\n", param); +} + + +/* Set the line drawing pattern. */ +static void +hndl_linpat(pgc_t *dev) +{ + uint16_t param; + + if (! pgc_param_word(dev, (int16_t *)¶m)) return; + + dev->line_pattern = param; + DEBUG("LINPAT(0x%04x)\n", param); +} + + +/* Set the polygon fill mode (0=hollow, 1=filled, 2=fast fill) */ +static void +hndl_prmfil(pgc_t *dev) +{ + uint8_t param; + + if (! pgc_param_byte(dev, ¶m)) return; + + DEBUG("PRMFIL(%i)\n", param); + if (param < 3) + dev->fill_mode = param; + else + pgc_error(dev, PGC_ERROR_RANGE); +} + + +/* Set the 2D drawing position. */ +static void +hndl_move(pgc_t *dev) +{ + int32_t x, y; + + if (! pgc_param_coord(dev, &x)) return; + if (! pgc_param_coord(dev, &y)) return; + + dev->x = x; + dev->y = y; + DEBUG("MOVE %x.%04x,%x.%04x\n", HWORD(x), LWORD(x), HWORD(y), LWORD(y)); +} + + +/* Set the 3D drawing position. */ +static void +hndl_move3(pgc_t *dev) +{ + int32_t x, y, z; + + if (! pgc_param_coord(dev, &x)) return; + if (! pgc_param_coord(dev, &y)) return; + if (! pgc_param_coord(dev, &z)) return; + + dev->x = x; + dev->y = y; + dev->z = z; +} + + +/* Relative move (2D) */ +static void +hndl_mover(pgc_t *dev) +{ + int32_t x, y; + + if (! pgc_param_coord(dev, &x)) return; + if (! pgc_param_coord(dev, &y)) return; + + dev->x += x; + dev->y += y; +} + + +/* Relative move (3D) */ +static void +hndl_mover3(pgc_t *dev) +{ + int32_t x, y, z; + + if (! pgc_param_coord(dev, &x)) return; + if (! pgc_param_coord(dev, &y)) return; + if (! pgc_param_coord(dev, &z)) return; + + dev->x += x; + dev->y += y; + dev->z += z; +} + + +/* Draw a line (using PGC fixed-point coordinates) */ +uint16_t +pgc_draw_line(pgc_t *dev, int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint16_t linemask) +{ + DEBUG("pgc_draw_line: (%i,%i) to (%i,%i)\n", + x0 >> 16, y0 >> 16, x1 >> 16, y1 >> 16); + + /* Convert from PGC fixed-point to device coordinates */ + x0 >>= 16; + x1 >>= 16; + y0 >>= 16; + y1 >>= 16; + + pgc_ito_raster(dev, &x0, &y0); + pgc_ito_raster(dev, &x1, &y1); + + return pgc_draw_line_r(dev, x0, y0, x1, y1, linemask); +} + + +/* Draw a line (using raster coordinates) + Bresenham's Algorithm from + * + * The line pattern mask to use is passed in. The return value is the line + * pattern mask rotated by the number of points drawn. + */ +uint16_t +pgc_draw_line_r(pgc_t *dev, int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint16_t linemask) +{ + int32_t dx, dy, sx, sy, err, e2; + + dx = abs(x1 - x0); + dy = abs(y1 - y0); + sx = (x0 < x1) ? 1 : -1; + sy = (y0 < y1) ? 1 : -1; + err = (dx > dy ? dx : -dy) / 2; + + for (;;) { + if (linemask & 0x8000) { + pgc_plot(dev, x0, y0); + linemask = (linemask << 1) | 1; + } else { + linemask = (linemask << 1); + } + + if (x0 == x1 && y0 == y1) break; + + e2 = err; + if (e2 > -dx) { + err -= dy; + x0 += sx; + } + if (e2 < dy) { + err += dx; + y0 += sy; + } + } + + return linemask; +} + + +/* Draw a horizontal line in the current fill pattern + * (using raster coordinates) */ +void +pgc_fill_line_r(pgc_t *dev, int32_t x0, int32_t x1, int32_t y0) +{ + int32_t mask = 0x8000 >> (x0 & 0x0f); + int32_t x; + + if (x0 > x1) { + x = x1; + x1 = x0; + x0 = x; + } + + for (x = x0; x <= x1; x++) { + if (dev->fill_pattern[y0 & 0x0F] & mask) + pgc_plot(dev, x, y0); + mask = mask >> 1; + if (mask == 0) mask = 0x8000; + } +} + + +/* For sorting polygon nodes. */ +static int +compare_double(const void *a, const void *b) +{ + const double *da = (const double *)a; + const double *db = (const double *)b; + + if (*da < *db) return 1; + if (*da > *db) return -1; + + return 0; +} + + +/* Draw a filled polygon (using PGC fixed-point coordinates) */ +void +pgc_fill_polygon(pgc_t *dev, unsigned corners, int32_t *x, int32_t *y) +{ + double *nodex; + double *dx; + double *dy; + unsigned n, nodes, i, j; + double ymin, ymax, ypos; + + DEBUG("pgc_fill_polygon(%d corners)\n", corners); + + if (corners < 2) return; /* Degenerate polygon */ + + nodex = (double *)mem_alloc(corners * sizeof(double)); + dx = (double *)mem_alloc(corners * sizeof(double)); + dy = (double *)mem_alloc(corners * sizeof(double)); + if (!nodex || !dx || !dy) return; + + ymin = ymax = y[0] / 65536.0; + for (n = 0; n < corners; n++) { + /* Convert from PGC fixed-point to native floating-point */ + dx[n] = x[n] / 65536.0; + dy[n] = y[n] / 65536.0; + + if (dy[n] < ymin) ymin = dy[n]; + if (dy[n] > ymax) ymax = dy[n]; + } + + /* Polygon fill. Based on */ + /* For each row, work out where the polygon lines intersect with + * that row. */ + for (ypos = ymin; ypos <= ymax; ypos++) { + nodes = 0; + j = corners - 1; + for (i = 0; i < corners; i++) { + if ((dy[i] < ypos && dy[j] >= ypos) || + (dy[j] < ypos && dy[i] >= ypos)) /* Line crosses */ { + nodex[nodes++] = dx[i] + (ypos-dy[i])/(dy[j]-dy[i]) * (dx[j] - dx[i]); + } + j = i; + } + + /* Sort the intersections */ + if (nodes) qsort(nodex, nodes, sizeof(double), compare_double); + + /* And fill between them */ + for (i = 0; i < nodes; i += 2) { + int16_t x1 = (int16_t)nodex[i], x2 = (int16_t)nodex[i + 1], + y1 = (int16_t)ypos, y2 = (int16_t)ypos; + pgc_sto_raster(dev, &x1, &y1); + pgc_sto_raster(dev, &x2, &y2); + pgc_fill_line_r(dev, x1, x2, y1); + } + } + + free(nodex); + free(dx); + free(dy); +} + + +/* Draw a filled ellipse (using PGC fixed-point coordinates) */ +void +pgc_draw_ellipse(pgc_t *dev, int32_t x, int32_t y) +{ + /* Convert from PGC fixed-point to native floating-point. */ + double h = y / 65536.0; + double w = x / 65536.0; + double y0 = dev->y / 65536.0; + double x0 = dev->x / 65536.0; + double ypos, xpos; + double x1; + double xlast = 0.0; + int16_t linemask = dev->line_pattern; + + DEBUG("Ellipse: Color=%i Drawmode=%i fill=%i\n", + dev->colour, dev->draw_mode, dev->fill_mode); + + pgc_dto_raster(dev, &x0, &y0); + + for (ypos = 0; ypos <= h; ypos++) { + if (ypos == 0) { + if (dev->fill_mode) + pgc_fill_line_r(dev, (uint16_t)(x0 - w), + (uint16_t)(x0 + w), (uint16_t)y0); + if (linemask & 0x8000) { + pgc_plot(dev, (uint16_t)(x0 + w), (uint16_t)y0); + pgc_plot(dev, (uint16_t)(x0 - w), (uint16_t)y0); + linemask = (linemask << 1) | 1; + } else + linemask = linemask << 1; + + xlast = w; + } else { + x1 = sqrt((h * h) - (ypos * ypos)) * w / h; + + if (dev->fill_mode) { + pgc_fill_line_r(dev, (uint16_t)(x0 - x1), + (uint16_t)(x0 + x1), + (uint16_t)(y0 + ypos)); + pgc_fill_line_r(dev, (uint16_t)(x0 - x1), + (uint16_t)(x0 + x1), + (uint16_t)(y0 - ypos)); + } + + /* Draw border. */ + for (xpos = xlast; xpos >= x1; xpos--) { + if (linemask & 0x8000) { + pgc_plot(dev, (uint16_t)(x0 + xpos), + (uint16_t)(y0 + ypos)); + pgc_plot(dev, (uint16_t)(x0 - xpos), + (uint16_t)(y0 + ypos)); + pgc_plot(dev, (uint16_t)(x0 + xpos), + (uint16_t)(y0 - ypos)); + pgc_plot(dev, (uint16_t)(x0 - xpos), + (uint16_t)(y0 - ypos)); + linemask = (linemask << 1) | 1; + } else + linemask = linemask << 1; + } + + xlast = x1; + } + } +} + + +/* Handle the ELIPSE (sic) command. */ +static void +hndl_ellipse(pgc_t *dev) +{ + int32_t x, y; + + if (! pgc_param_coord(dev, &x)) return; + if (! pgc_param_coord(dev, &y)) return; + + pgc_draw_ellipse(dev, x, y); +} + + +/* Handle the POLY command. */ +static void +hndl_poly(pgc_t *dev) +{ + uint8_t count; + int32_t x[256]; + int32_t y[256]; + int32_t n; + + if (! pgc_param_byte(dev, &count)) return; + + for (n = 0; n < count; n++) { + if (! pgc_param_coord(dev, &x[n])) return; + if (! pgc_param_coord(dev, &y[n])) return; + } + DEBUG("POLY (%i)\n", count); +} + + +/* Parse but don't execute a POLY command (for adding to a command list) */ +static int +parse_poly(pgc_t *dev, pgc_cl_t *cl, int c) +{ + uint8_t count; + + DEBUG("parse_poly\n"); + if (! pgc_param_byte(dev, &count)) return 0; + DEBUG("parse_poly: count=%02x\n", count); + + if (! pgc_commandlist_append(cl, count)) { + pgc_error(dev, PGC_ERROR_OVERFLOW); + return 0; + } + DEBUG("parse_poly: parse %i coords\n", 2 * count); + + return pgc_parse_coords(dev, cl, 2 * count); +} + + +/* Parse but don't execute a command with a fixed number of byte parameters */ +int +pgc_parse_bytes(pgc_t *dev, pgc_cl_t *cl, int count) +{ + uint8_t *param = (uint8_t *)mem_alloc(count); + int n; + + if (! param) { + pgc_error(dev, PGC_ERROR_OVERFLOW); + return 0; + } + + for (n = 0; n < count; n++) { + if (! pgc_param_byte(dev, ¶m[n])) { + free(param); + return 0; + } + + if (! pgc_commandlist_append(cl, param[n])) { + pgc_error(dev, PGC_ERROR_OVERFLOW); + free(param); + return 0; + } + } + + free(param); + + return 1; +} + + +/* Parse but don't execute a command with a fixed number of word parameters */ +int +pgc_parse_words(pgc_t *dev, pgc_cl_t *cl, int count) +{ + int16_t *param = (int16_t *)mem_alloc(count * sizeof(int16_t)); + int n; + + if (! param) { + pgc_error(dev, PGC_ERROR_OVERFLOW); + return 0; + } + + for (n = 0; n < count; n++) { + if (! pgc_param_word(dev, ¶m[n])) return 0; + + if (!pgc_commandlist_append(cl, param[n] & 0xff) || + !pgc_commandlist_append(cl, param[n] >> 8)) { + pgc_error(dev, PGC_ERROR_OVERFLOW); + free(param); + return 0; + } + } + + free(param); + + return 1; +} + + +/* Parse but don't execute a command with a fixed number of coord parameters */ +int +pgc_parse_coords(pgc_t *dev, pgc_cl_t *cl, int count) +{ + int32_t *param = (int32_t *)mem_alloc(count * sizeof(int32_t)); + int n; + + if (! param) { + pgc_error(dev, PGC_ERROR_OVERFLOW); + return 0; + } + + for (n = 0; n < count; n++) + if (! pgc_param_coord(dev, ¶m[n])) return 0; + + /* Here is how the real PGC serializes coords: + * + * 100.5 -> 64 00 00 80 ie 0064.8000 + * 100.3 -> 64 00 CD 4C ie 0064.4CCD + */ + for (n = 0; n < count; n++) { + /* Serialize integer part. */ + if (!pgc_commandlist_append(cl, (param[n] >> 16) & 0xff) || + !pgc_commandlist_append(cl, (param[n] >> 24) & 0xff) || + + /* Serialize fraction part. */ + !pgc_commandlist_append(cl, (param[n] ) & 0xff) || + !pgc_commandlist_append(cl, (param[n] >> 8) & 0xff)) { + pgc_error(dev, PGC_ERROR_OVERFLOW); + free(param); + return 0; + } + } + + free(param); + + return 1; +} + + +/* Handle the DISPLAY command */ +static void +hndl_display(pgc_t *dev) +{ + uint8_t param; + + if (! pgc_param_byte(dev, ¶m)) return; + + if (param > 1) + pgc_error(dev, PGC_ERROR_RANGE); + else + pgc_setdisplay(dev, param); +} + + +/* Handle the IMAGEW command (memory to screen blit) */ +static void +hndl_imagew(pgc_t *dev) +{ + int16_t row, col1, col2; + uint8_t v1, v2; + + if (! pgc_param_word(dev, &row)) return; + if (! pgc_param_word(dev, &col1)) return; + if (! pgc_param_word(dev, &col2)) return; + + if ((uint32_t)row >= dev->screenh || + (uint32_t)col1 >= dev->maxw || (uint32_t)col2 >= dev->maxw) { + pgc_error(dev, PGC_ERROR_RANGE); + return; + } + + /* In ASCII mode, what is written is a stream of bytes. */ + if (dev->ascii_mode) { + while (col1 <= col2) { + if (! pgc_param_byte(dev, &v1)) return; + pgc_write_pixel(dev, col1, row, v1); + col1++; + } + + return; + } + + /* In hex mode, it's RLE compressed */ + while (col1 <= col2) { + if (! pgc_param_byte(dev, &v1)) return; + + if (v1 & 0x80) { + /* Literal run */ + v1 -= 0x7f; + while (col1 <= col2 && v1 != 0) { + if (! pgc_param_byte(dev, &v2)) return; + pgc_write_pixel(dev, col1, row, v2); + col1++; + v1--; + } + } else { + /* Repeated run */ + if (! pgc_param_byte(dev, &v2)) return; + + v1++; + while (col1 <= col2 && v1 != 0) { + pgc_write_pixel(dev, col1, row, v2); + col1++; + v1--; + } + } + } +} + + +/* Select one of the built-in palettes */ +static void +init_lut(pgc_t *dev, int param) +{ + if (param >= 0 && param < 6) { + memcpy(dev->palette, init_palette[param], sizeof(dev->palette)); + } else if (param == 0xff) { + memcpy(dev->palette, dev->userpal, sizeof(dev->palette)); + } else { + pgc_error(dev, PGC_ERROR_RANGE); + } +} + + +/* Save the current palette */ +static void +hndl_lutsav(pgc_t *dev) +{ + memcpy(dev->userpal, dev->palette, sizeof(dev->palette)); +} + + +/* Handle LUTINT (select palette) */ +static void +hndl_lutint(pgc_t *dev) +{ + uint8_t param; + + if (! pgc_param_byte(dev, ¶m)) return; + + init_lut(dev, param); +} + + +/* Handle LUTRD (read palette register) */ +static void +hndl_lutrd(pgc_t *dev) +{ + uint8_t param; + uint32_t col; + + if (! pgc_param_byte(dev, ¶m)) return; + + col = dev->palette[param]; + + pgc_result_byte(dev, (col >> 20) & 0x0f); + pgc_result_byte(dev, (col >> 12) & 0x0f); + pgc_result_byte(dev, (col >> 4) & 0x0f); +} + + +/* Handle LUT (write palette register) */ +static void +hndl_lut(pgc_t *dev) +{ + uint8_t param[4]; + int n; + + for (n = 0; n < 4; n++) { + if (! pgc_param_byte(dev, ¶m[n])) return; + if (n > 0 && param[n] > 15) { + pgc_error(dev, PGC_ERROR_RANGE); + param[n] &= 0x0f; + } + } + + dev->palette[param[0]] = makecol((param[1] * 0x11), + (param[2] * 0x11), + (param[3] * 0x11)); +} + + +/* LUT8RD and LUT8 are extensions implemented by several PGC clones, so + * here are functions that implement them even though they aren't + * used by the PGC */ +void +pgc_hndl_lut8rd(pgc_t *dev) +{ + uint8_t param; + uint32_t col; + + if (! pgc_param_byte(dev, ¶m)) return; + + col = dev->palette[param]; + + pgc_result_byte(dev, (col >> 16) & 0xff); + pgc_result_byte(dev, (col >> 8) & 0xff); + pgc_result_byte(dev, col & 0xff); +} + + +void +pgc_hndl_lut8(pgc_t *dev) +{ + uint8_t param[4]; + int n; + + for (n = 0; n < 4; n++) + if (! pgc_param_byte(dev, ¶m[n])) return; + + dev->palette[param[0]] = makecol((param[1]), (param[2]), (param[3])); +} + + +/* Handle AREAPT (set 16x16 fill pattern) */ +static void +hndl_areapt(pgc_t *dev) +{ + int16_t pat[16]; + int n; + + for (n = 0; n < 16; n++) + if (! pgc_param_word(dev, &pat[n])) return; + + memcpy(dev->fill_pattern, pat, sizeof(dev->fill_pattern)); + DEBUG("AREAPT(%04x %04x %04x %04x...)\n", + pat[0] & 0xffff, pat[1] & 0xffff, pat[2] & 0xffff, pat[3] & 0xffff); +} + + +/* Handle CA (select ASCII mode) */ +static void +hndl_ca(pgc_t *dev) +{ + dev->ascii_mode = 1; +} + + +/* Handle CX (select hex mode) */ +static void +hndl_cx(pgc_t *dev) +{ + dev->ascii_mode = 0; +} + + +/* CA and CX remain valid in hex mode; they are handled as command 0x43 ('C') + * with a one-byte parameter */ +static void +hndl_c(pgc_t *dev) +{ + uint8_t param; + + if (! dev->inputbyte(dev, ¶m)) return; + + if (param == 'A') + dev->ascii_mode = 1; + + if (param == 'X') + dev->ascii_mode = 0; +} + + +/* RESETF resets the PGC. */ +static void +hndl_resetf(pgc_t *dev) +{ + pgc_reset(dev); +} + + +/* TJUST sets text justify settings. */ +static void +hndl_tjust(pgc_t *dev) +{ + uint8_t param[2]; + + if (! dev->inputbyte(dev, ¶m[0])) return; + if (! dev->inputbyte(dev, ¶m[1])) return; + + if (param[0] >= 1 && param[0] <= 3 && param[1] >= 1 && param[1] <= 3) { + dev->tjust_h = param[0]; + dev->tjust_v = param[1]; + } else + pgc_error(dev, PGC_ERROR_RANGE); +} + + +/* VWPORT sets up the viewport (roughly, the clip rectangle) in raster + * coordinates, measured from the bottom left of the screen */ +static void +hndl_vwport(pgc_t *dev) +{ + int16_t x1, x2, y1, y2; + + if (! pgc_param_word(dev, &x1)) return; + if (! pgc_param_word(dev, &x2)) return; + if (! pgc_param_word(dev, &y1)) return; + if (! pgc_param_word(dev, &y2)) return; + + DEBUG("VWPORT %i,%i,%i,%i\n", x1,x2,y1,y2); + dev->vp_x1 = x1; + dev->vp_x2 = x2; + dev->vp_y1 = y1; + dev->vp_y2 = y2; +} + + +/* WINDOW defines the coordinate system in use. */ +static void +hndl_window(pgc_t *dev) +{ + int16_t x1, x2, y1, y2; + + if (! pgc_param_word(dev, &x1)) return; + if (! pgc_param_word(dev, &x2)) return; + if (! pgc_param_word(dev, &y1)) return; + if (! pgc_param_word(dev, &y2)) return; + + DEBUG("WINDOW %i,%i,%i,%i\n", x1,x2,y1,y2); + dev->win_x1 = x1; + dev->win_x2 = x2; + dev->win_y1 = y1; + dev->win_y2 = y2; +} + + +/* + * The list of commands implemented by this mini-PGC. + * + * In order to support the original PGC and clones, we support two lists; + * core commands (listed below) and subclass commands (listed in the clone). + * + * Each row has five parameters: + * ASCII-mode command + * Hex-mode command + * Function that executes this command + * Function that parses this command when building a command list + * Parameter for the parse function + * + * TODO: This list omits numerous commands present in a genuine PGC + * (ARC, AREA, AREABC, BUFFER, CIRCLE etc etc). + * TODO: Some commands don't have a parse function (for example, IMAGEW) + * + * The following ASCII entries have special meaning: + * ~~~~~~ command is valid only in hex mode + * ****** end of subclass command list, now process core command list + * @@@@@@ end of core command list + * + */ +static const pgc_cmd_t pgc_commands[] = { + { "AREAPT", 0xe7, hndl_areapt, pgc_parse_words, 16 }, + { "AP", 0xe7, hndl_areapt, pgc_parse_words, 16 }, + { "~~~~~~", 0x43, hndl_c, NULL, 0 }, + { "CA", 0xd2, hndl_ca, NULL, 0 }, + { "CLBEG", 0x70, hndl_clbeg, NULL, 0 }, + { "CB", 0x70, hndl_clbeg, NULL, 0 }, + { "CLDEL", 0x74, hndl_cldel, pgc_parse_bytes, 1 }, + { "CD", 0x74, hndl_cldel, pgc_parse_bytes, 1 }, + { "CLEND", 0x71, hndl_clend, NULL, 0 }, + { "CLRUN", 0x72, hndl_clrun, pgc_parse_bytes, 1 }, + { "CR", 0x72, hndl_clrun, pgc_parse_bytes, 1 }, + { "CLRD", 0x75, hndl_clread, pgc_parse_bytes, 1 }, + { "CRD", 0x75, hndl_clread, pgc_parse_bytes, 1 }, + { "CLOOP", 0x73, hndl_cloop, NULL, 0 }, + { "CL", 0x73, hndl_cloop, NULL, 0 }, + { "CLEARS", 0x0f, hndl_clears, pgc_parse_bytes, 1 }, + { "CLS", 0x0f, hndl_clears, pgc_parse_bytes, 1 }, + { "COLOR", 0x06, hndl_color, pgc_parse_bytes, 1 }, + { "C", 0x06, hndl_color, pgc_parse_bytes, 1 }, + { "CX", 0xd1, hndl_cx, NULL, 0 }, + { "DISPLA", 0xd0, hndl_display, pgc_parse_bytes, 1 }, + { "DI", 0xd0, hndl_display, pgc_parse_bytes, 1 }, + { "ELIPSE", 0x39, hndl_ellipse, pgc_parse_coords, 2 }, + { "EL", 0x39, hndl_ellipse, pgc_parse_coords, 2 }, + { "IMAGEW", 0xd9, hndl_imagew, NULL, 0 }, + { "IW", 0xd9, hndl_imagew, NULL, 0 }, + { "LINFUN", 0xeb, hndl_linfun, pgc_parse_bytes, 1 }, + { "LF", 0xeb, hndl_linfun, pgc_parse_bytes, 1 }, + { "LINPAT", 0xea, hndl_linpat, pgc_parse_words, 1 }, + { "LP", 0xea, hndl_linpat, pgc_parse_words, 1 }, + { "LUTINT", 0xec, hndl_lutint, pgc_parse_bytes, 1 }, + { "LI", 0xec, hndl_lutint, pgc_parse_bytes, 1 }, + { "LUTRD", 0x50, hndl_lutrd, pgc_parse_bytes, 1 }, + { "LUTSAV", 0xed, hndl_lutsav, NULL, 0 }, + { "LUT", 0xee, hndl_lut, pgc_parse_bytes, 4 }, + { "MOVE", 0x10, hndl_move, pgc_parse_coords, 2 }, + { "M", 0x10, hndl_move, pgc_parse_coords, 2 }, + { "MOVE3", 0x12, hndl_move3, pgc_parse_coords, 3 }, + { "M3", 0x12, hndl_move3, pgc_parse_coords, 3 }, + { "MOVER", 0x11, hndl_mover, pgc_parse_coords, 2 }, + { "MR", 0x11, hndl_mover, pgc_parse_coords, 2 }, + { "MOVER3", 0x13, hndl_mover3, pgc_parse_coords, 3 }, + { "MR3", 0x13, hndl_mover3, pgc_parse_coords, 3 }, + { "PRMFIL", 0xe9, hndl_prmfil, pgc_parse_bytes, 1 }, + { "PF", 0xe9, hndl_prmfil, pgc_parse_bytes, 1 }, + { "POLY", 0x30, hndl_poly, parse_poly, 0 }, + { "P", 0x30, hndl_poly, parse_poly, 0 }, + { "RESETF", 0x04, hndl_resetf, NULL, 0 }, + { "RF", 0x04, hndl_resetf, NULL, 0 }, + { "TJUST", 0x85, hndl_tjust, pgc_parse_bytes, 2 }, + { "TJ", 0x85, hndl_tjust, pgc_parse_bytes, 2 }, + { "VWPORT", 0xb2, hndl_vwport, pgc_parse_words, 4 }, + { "VWP", 0xb2, hndl_vwport, pgc_parse_words, 4 }, + { "WINDOW", 0xb3, hndl_window, pgc_parse_words, 4 }, + { "WI", 0xb3, hndl_window, pgc_parse_words, 4 }, + + { "@@@@@@", 0x00, NULL, NULL, 0 } +}; + + +/* Writes to CGA registers are copied into the transfer memory buffer. */ +void +pgc_out(uint16_t addr, uint8_t val, void *priv) +{ + pgc_t *dev = (pgc_t *)priv; + + switch(addr) { + case 0x3d0: + case 0x3d2: + case 0x3d4: + case 0x3d6: + dev->mapram[0x3d0] = val; + break; + + case 0x3d1: + case 0x3d3: + case 0x3d5: + case 0x3d7: + if (dev->mapram[0x3d0] < 18) + dev->mapram[0x3e0 + dev->mapram[0x3d0]] = val; + break; + + case 0x3d8: + dev->mapram[0x3d8] = val; + break; + + case 0x3d9: + dev->mapram[0x3d9] = val; + break; + } +} + + +/* Read back the CGA registers. */ +uint8_t +pgc_in(uint16_t addr, void *priv) +{ + pgc_t *dev = (pgc_t *)priv; + + switch(addr) { + case 0x3d0: + case 0x3d2: + case 0x3d4: + case 0x3d6: + return dev->mapram[0x3d0]; + + case 0x3d1: + case 0x3d3: + case 0x3d5: + case 0x3d7: + if (dev->mapram[0x3d0] < 18) + return dev->mapram[0x3e0 + dev->mapram[0x3d0]]; + return 0xff; + + case 0x3d8: + return dev->mapram[0x3d8]; + + case 0x3d9: + return dev->mapram[0x3d9]; + + case 0x3da: + return dev->mapram[0x3da]; + } + + return 0xff; +} + + +/* Memory write to the transfer buffer. */ +void +pgc_write(uint32_t addr, uint8_t val, void *priv) +{ + pgc_t *dev = (pgc_t *)priv; + + /* + * It seems variable whether the PGC maps 1k or 2k at 0xC6000. + * + * Map 2K here in case a clone requires it. + */ + if (addr >= 0xc6000 && addr < 0xc6800) { + addr &= 0x7ff; + + /* If one of the FIFOs has been updated, this may cause + * the drawing thread to be woken */ + + if (dev->mapram[addr] != val) { + dev->mapram[addr] = val; + switch (addr) { + case 0x300: /* Input write pointer */ + if (dev->waiting_input_fifo && + dev->mapram[0x300] != dev->mapram[0x301]) { + dev->waiting_input_fifo = 0; + pgc_wake(dev); + } + break; + + case 0x303: /* Output read pointer */ + if (dev->waiting_output_fifo && + dev->mapram[0x302] != (uint8_t)(dev->mapram[0x303] - 1)) { + dev->waiting_output_fifo = 0; + pgc_wake(dev); + } + break; + + case 0x305: /* Error read pointer */ + if (dev->waiting_error_fifo && + dev->mapram[0x304] != (uint8_t)(dev->mapram[0x305] - 1)) { + dev->waiting_error_fifo = 0; + pgc_wake(dev); + } + break; + + case 0x306: /* Cold start flag */ + /* XXX This should be in IM-1024 specific code */ + dev->mapram[0x306] = 0; + break; + + case 0x30c: /* Display type */ + pgc_setdisplay(priv, dev->mapram[0x30c]); + dev->mapram[0x30d] = dev->mapram[0x30c]; + break; + + case 0x3ff: /* Reboot the PGC + (handled on core thread) */ + pgc_wake(dev); + break; + } + } + } + + if (addr >= 0xb8000 && addr < 0xbc000 && dev->cga_selected) { + addr &= 0x3fff; + dev->cga_vram[addr] = val; + } +} + + +uint8_t +pgc_read(uint32_t addr, void *priv) +{ + pgc_t *dev = (pgc_t *)priv; + + if (addr >= 0xc6000 && addr < 0xc6800) { + addr &= 0x7ff; + return dev->mapram[addr]; + } + + if (addr >= 0xb8000 && addr < 0xbc000 && dev->cga_selected) { + addr &= 0x3fff; + return dev->cga_vram[addr]; + } + + return 0xff; +} + + +/* Called by the drawing thread to read the next byte from the input + * buffer. If no byte available will sleep until one is. Returns 0 if + * a PGC reset has been triggered by a write to 0xC63FF */ +static int +input_byte(pgc_t *dev, uint8_t *result) +{ + /* If input buffer empty, wait for it to fill */ + while (dev->mapram[0x300] == dev->mapram[0x301]) { + dev->waiting_input_fifo = 1; + pgc_sleep(dev); + } + + if (dev->mapram[0x3ff]) { + /* Reset triggered */ + pgc_reset(dev); + return 0; + } + + *result = dev->mapram[dev->mapram[0x301]]; + ++dev->mapram[0x301]; + + return 1; +} + + +/* Called by the drawing thread to write a byte to the output buffer. + * If buffer is full will sleep until it is not. Returns 0 if + * a PGC reset has been triggered by a write to 0xC63FF */ +static int +output_byte(pgc_t *dev, uint8_t val) +{ + /* If output buffer full, wait for it to empty */ + while (dev->mapram[0x302] == (uint8_t)(dev->mapram[0x303] - 1)) { + DEBUG("Output buffer state: %02x %02x Sleeping\n", + dev->mapram[0x302], dev->mapram[0x303]); + dev->waiting_output_fifo = 1; + pgc_sleep(dev); + } + + if (dev->mapram[0x3ff]) { + /* Reset triggered */ + pgc_reset(dev); + return 0; + } + + dev->mapram[0x100 + dev->mapram[0x302]] = val; + ++dev->mapram[0x302]; + + DBGLOG(1, "Output %02x: new state: %02x %02x\n", val, + dev->mapram[0x302], dev->mapram[0x303]); + return 1; +} + + +/* Helper to write an entire string to the output buffer. */ +static int +output_string(pgc_t *dev, const char *s) +{ + while (*s) { + if (! output_byte(dev, *s)) return 0; + s++; + } + + return 1; +} + + +/* As pgc_output_byte, for the error buffer. */ +static int +error_byte(pgc_t *dev, uint8_t val) +{ + /* If error buffer full, wait for it to empty */ + while (dev->mapram[0x304] == dev->mapram[0x305] - 1) { + dev->waiting_error_fifo = 1; + pgc_sleep(dev); + } + + if (dev->mapram[0x3ff]) { + /* Reset triggered */ + pgc_reset(dev); + return 0; + } + + dev->mapram[0x200 + dev->mapram[0x304]] = val; + ++dev->mapram[0x304]; + + return 1; +} + + +/* As pgc_output_string, for the error buffer. */ +static int +error_string(pgc_t *dev, const char *s) +{ + while (*s) { + if (! error_byte(dev, *s)) return 0; + s++; + } + + return 1; +} + + +/* Report an error, either in ASCII or in hex. */ +int +pgc_error(pgc_t *dev, int err) +{ + if (dev->mapram[0x307]) { + /* Errors enabled? */ + if (dev->ascii_mode) { + if (err >= PGC_ERROR_RANGE && err <= PGC_ERROR_MISSING) + return error_string(dev, pgc_err_msgs[err]); + return error_string(dev, "Unknown error\r"); + } else { + return error_byte(dev, err); + } + } + + return 1; +} + + +static inline +int is_whitespace(char ch) +{ + return (ch != 0 && strchr(" \r\n\t,;()+-", ch) != NULL); +} + + +/* Read a byte and interpret as ASCII: ignore control characters other than + * CR, LF or tab. */ +static int +input_char(pgc_t *dev, char *result) +{ + uint8_t ch; + + while (1) { + if (! dev->inputbyte(dev, &ch)) return 0; + + ch &= 0x7f; + if (ch == '\r' || ch == '\n' || ch == '\t' || ch >= ' ') { + *result = toupper(ch); + return 1; + } + } +} + + +/* Parameter passed is not a number: abort */ +static int +err_digit(pgc_t *dev) +{ + uint8_t asc; + + do { + /* Swallow everything until the next separator */ + if (! dev->inputbyte(dev, &asc)) return 0; + } while (! is_whitespace(asc)); + + pgc_error(dev, PGC_ERROR_DIGIT); + + return 0; +} + + +typedef enum { + PS_MAIN, + PS_FRACTION, + PS_EXPONENT +} parse_state_t; + + +/* Read in a PGC coordinate, either as hex (4 bytes) or ASCII (xxxx.yyyyEeee) + * Returns 0 if PGC reset detected while the value is being read */ +int +pgc_param_coord(pgc_t *dev, int32_t *value) +{ + uint8_t asc; + int sign = 1; + int esign = 1; + int n; + uint16_t dp = 1; + uint16_t integer = 0; + uint16_t frac = 0; + uint16_t exponent = 0; + uint32_t res; + parse_state_t state = PS_MAIN; + uint8_t encoded[4]; + + /* If there is a command list running, pull the bytes out of that + * command list */ + if (dev->clcur) { + for (n = 0; n < 4; n++) + if (! pgc_clist_byte(dev, &encoded[n])) return 0; + integer = (((int16_t)encoded[1]) << 8) | encoded[0]; + frac = (((int16_t)encoded[3]) << 8) | encoded[2]; + + *value = (((int32_t)integer) << 16) | frac; + return 1; + } + + /* If in hex mode, read in the encoded integer and fraction parts + * from the hex stream */ + if (! dev->ascii_mode) { + for (n = 0; n < 4; n++) + if (! dev->inputbyte(dev, &encoded[n])) return 0; + integer = (((int16_t)encoded[1]) << 8) | encoded[0]; + frac = (((int16_t)encoded[3]) << 8) | encoded[2]; + + *value = (((int32_t)integer) << 16) | frac; + return 1; + } + + /* Parsing an ASCII value */ + /* Skip separators */ + do { + if (! dev->inputbyte(dev, &asc)) return 0; + if (asc == '-') sign = -1; + } while (is_whitespace(asc)); + + /* There had better be a digit next */ + if (! isdigit(asc)) { + pgc_error(dev, PGC_ERROR_MISSING); + return 0; + } + + do { + switch (asc) { + case '.': + /* Decimal point is acceptable in 'main' state (start + * of fraction) not otherwise */ + if (state == PS_MAIN) { + if (! dev->inputbyte(dev, &asc)) return 0; + state = PS_FRACTION; + continue; + } else { + pgc_error(dev, PGC_ERROR_MISSING); + return err_digit(dev); + } + break; + + /* Scientific notation */ + case 'd': + case 'D': + case 'e': + case 'E': + esign = 1; + if (! dev->inputbyte(dev, &asc)) return 0; + if (asc == '-') { + sign = -1; + if (! dev->inputbyte(dev, &asc)) return 0; + } + state = PS_EXPONENT; + continue; + + /* Should be a number or a separator */ + default: + if (is_whitespace(asc)) break; + if (! isdigit(asc)) { + pgc_error(dev, PGC_ERROR_MISSING); + return err_digit(dev); + } + asc -= '0'; /* asc is digit */ + + switch (state) { + case PS_MAIN: + integer = (integer * 10)+asc; + if (integer & 0x8000) { + /* Overflow */ + pgc_error(dev, PGC_ERROR_RANGE); + integer = 0x7fff; + } + break; + + case PS_FRACTION: + frac = (frac * 10) + asc; + dp *= 10; + break; + + case PS_EXPONENT: + exponent = (exponent * 10)+asc; + break; + } + + } + + if (! dev->inputbyte(dev, &asc)) return 0; + } while (! is_whitespace(asc)); + + res = (frac << 16) / dp; + DEBUG("integer=%u frac=%u exponent=%u dp=%i res=0x%08lx\n", + integer, frac, exponent, dp, res); + + res = (res & 0xffff) | (integer << 16); + if (exponent) { + for (n = 0; n < exponent; n++) { + if (esign > 0) + res *= 10; + else + res /= 10; + } + } + *value = sign*res; + + return 1; +} + + +/* Pull the next byte from the current command list. */ +int +pgc_clist_byte(pgc_t *dev, uint8_t *val) +{ + if (dev->clcur == NULL) return 0; + + *val = dev->clcur->list[dev->clcur->rdptr++]; + + /* If we've reached the end, reset to the beginning and + * (if repeating) run the repeat */ + if (dev->clcur->rdptr >= dev->clcur->wrptr) { + dev->clcur->rdptr = 0; + dev->clcur->repeat--; + if (dev->clcur->repeat == 0) + dev->clcur = dev->clcur->chain; + } + + return 1; +} + + +/* Read in a byte, either as hex (1 byte) or ASCII (decimal). + * Returns 0 if PGC reset detected while the value is being read */ +int +pgc_param_byte(pgc_t *dev, uint8_t *val) +{ + int32_t c; + + if (dev->clcur) + return pgc_clist_byte(dev, val); + + if (! dev->ascii_mode) + return dev->inputbyte(dev, val); + + if (! pgc_param_coord(dev, &c)) return 0; + + c = (c >> 16); /* Drop fractional part */ + if (c > 255) { + pgc_error(dev, PGC_ERROR_RANGE); + return 0; + } + *val = (uint8_t)c; + + return 1; +} + + +/* Read in a word, either as hex (2 bytes) or ASCII (decimal). + * Returns 0 if PGC reset detected while the value is being read */ +int +pgc_param_word(pgc_t *dev, int16_t *val) +{ + uint8_t lo, hi; + int32_t c; + + if (dev->clcur) { + if (! pgc_clist_byte(dev, &lo)) return 0; + if (! pgc_clist_byte(dev, &hi)) return 0; + *val = (((int16_t)hi) << 8) | lo; + + return 1; + } + + if (! dev->ascii_mode) { + if (! dev->inputbyte(dev, &lo)) return 0; + if (! dev->inputbyte(dev, &hi)) return 0; + *val = (((int16_t)hi) << 8) | lo; + + return 1; + } + + if (! pgc_param_coord(dev, &c)) return 0; + + c = (c >> 16); + if (c > 0x7fff || c < -0x7fff) { + pgc_error(dev, PGC_ERROR_RANGE); + return 0; + } + *val = (int16_t)c; + + return 1; +} + + +/* Output a byte, either as hex or ASCII depending on the mode. */ +int +pgc_result_byte(pgc_t *dev, uint8_t val) +{ + char buf[20]; + + if (! dev->ascii_mode) + return output_byte(dev, val); + + if (dev->result_count) { + if (! output_byte(dev, ',')) return 0; + } + sprintf(buf, "%i", val); + dev->result_count++; + + return output_string(dev, buf); +} + + +/* Output a word, either as hex or ASCII depending on the mode. */ +int +pgc_result_word(pgc_t *dev, int16_t val) +{ + char buf[20]; + + if (! dev->ascii_mode) { + if (! output_byte(dev, val & 0xFF)) return 0; + return output_byte(dev, val >> 8); + } + + if (dev->result_count) { + if (! output_byte(dev, ',')) return 0; + } + sprintf(buf, "%i", val); + dev->result_count++; + + return output_string(dev, buf); +} + + +/* Write a screen pixel (x and y are raster coordinates, ink is the + * value to write) */ +void +pgc_write_pixel(pgc_t *dev, uint16_t x, uint16_t y, uint8_t ink) +{ + uint8_t *vram; + + /* Suppress out-of-range writes; clip to viewport */ + if (x < dev->vp_x1 || x > dev->vp_x2 || x >= dev->maxw || + y < dev->vp_y1 || y > dev->vp_y2 || y >= dev->maxh) { + DEBUG("pgc_write_pixel clipped: (%i,%i) " + "vp_x1=%i vp_y1=%i vp_x2=%i vp_y2=%i " + "ink=0x%02x\n", + x, y, dev->vp_x1, dev->vp_y1, dev->vp_x2, dev->vp_y2, ink); + return; + } + + vram = pgc_vram_addr(dev, x, y); + if (vram) + *vram = ink; +} + + +/* Read a screen pixel (x and y are raster coordinates) */ +uint8_t +pgc_read_pixel(pgc_t *dev, uint16_t x, uint16_t y) +{ + uint8_t *vram; + + /* Suppress out-of-range reads */ + if (x >= dev->maxw || y >= dev->maxh) + return 0; + + vram = pgc_vram_addr(dev, x, y); + if (vram) + return *vram; + + return 0; +} + + +/* Plot a point in the current color and draw mode. Raster coordinates. */ +void +pgc_plot(pgc_t *dev, uint16_t x, uint16_t y) +{ + uint8_t *vram; + + /* Only allow plotting within the current viewport. */ + if (x < dev->vp_x1 || x > dev->vp_x2 || x >= dev->maxw || + y < dev->vp_y1 || y > dev->vp_y2 || y >= dev->maxh) { + DEBUG("pgc_plot clipped: (%i,%i) %i <= x <= %i; %i <= y <= %i; " + "mode=%i ink=0x%02x\n", x, y, + dev->vp_x1, dev->vp_x2, dev->vp_y1, dev->vp_y2, + dev->draw_mode, dev->colour); + return; + } + + vram = pgc_vram_addr(dev, x, y); + if (! vram) return; + + /* TODO: Does not implement the PGC plane mask (set by MASK) */ + switch (dev->draw_mode) { + default: + case 0: /* Write */ + *vram = dev->colour; + break; + + case 1: /* Invert */ + *vram ^= 0xFF; + break; + + case 2: /* XOR color */ + *vram ^= dev->colour; + break; + + case 3: /* AND */ + *vram &= dev->colour; + break; + } +} + + +/* Given raster coordinates, find the matching address in PGC video RAM. */ +uint8_t * +pgc_vram_addr(pgc_t *dev, int16_t x, int16_t y) +{ + int offset; + + /* We work from the bottom left-hand corner */ + if (y < 0 || (uint32_t)y >= dev->maxh || + x < 0 || (uint32_t)x >= dev->maxw) return NULL; + + offset = (dev->maxh - 1 - y) * (dev->maxw) + x; + DBGLOG(1, "pgc_vram_addr x=%i y=%i offset=%i\n", x, y, offset); + + if (offset < 0 || (uint32_t)offset >= (dev->maxw * dev->maxh)) + return NULL; + + return &dev->vram[offset]; +} + + +/* Read in the next command, either as hex (1 byte) or ASCII (up to 6 + * characters) */ +int +pgc_read_command(pgc_t *dev) +{ + if (dev->clcur) + return pgc_clist_byte(dev, &dev->hex_command); + + if (dev->ascii_mode) { + char ch; + int count = 0; + + while (count < 7) { + if (! input_char(dev, &ch)) return 0; + + if (is_whitespace(ch)) { + /* Pad to 6 characters */ + while (count < 6) + dev->asc_command[count++] = ' '; + dev->asc_command[6] = 0; + + return 1; + } + dev->asc_command[count++] = toupper(ch); + } + + return 1; + } + + return dev->inputbyte(dev, &dev->hex_command); +} + + +/* Read in the next command and parse it. */ +static int +pgc_parse_command(pgc_t *dev, const pgc_cmd_t **pcmd) +{ + const pgc_cmd_t *cmd; + char match[7]; + + *pcmd = NULL; + dev->hex_command = 0; + memset(dev->asc_command, ' ', 6); + dev->asc_command[7] = 0; + if (! pgc_read_command(dev)) { + /* PGC has been reset */ + return 0; + } + + /* Scan the list of valid commands. dev->commands may be a subclass + * list (terminated with '*') or the core list (terminated with '@') */ + for (cmd = dev->commands; cmd->ascii[0] != '@'; cmd++) { + /* End of subclass command list, chain to core */ + if (cmd->ascii[0] == '*') + cmd = pgc_commands; + + /* If in ASCII mode match on the ASCII command. */ + if (dev->ascii_mode && !dev->clcur) { + sprintf(match, "%-6.6s", cmd->ascii); + if (! strncmp(match, dev->asc_command, 6)) { + *pcmd = cmd; + dev->hex_command = cmd->hex; + break; + } + } else { + /* Otherwise match on the hex command */ + if (cmd->hex == dev->hex_command) { + sprintf(dev->asc_command, "%-6.6s", cmd->ascii); + *pcmd = cmd; + break; + } + } + } + + return 1; +} + + +/* The PGC drawing thread main loop. Read in commands and execute them ad + * infinitum */ +static void +pgc_thread(void *priv) +{ + pgc_t *dev = (pgc_t *)priv; + const pgc_cmd_t *cmd; + + DEBUG("pgc_thread begins"); + while (1) { + if (! pgc_parse_command(dev, &cmd)) { + /* PGC has been reset */ + continue; + } + + DEBUG("PGC command: [%02x] '%s' found=%i\n", + dev->hex_command, dev->asc_command, (cmd != NULL)); + + if (cmd) { + dev->result_count = 0; + (*cmd->handler)(dev); + } else + pgc_error(dev, PGC_ERROR_OPCODE); + } +} + + +void +pgc_recalctimings(pgc_t *dev) +{ + double disptime, _dispontime, _dispofftime; + + /* Use a fixed 640 columns, like the T3100e */ + disptime = dev->screenw + 11; + _dispontime = dev->screenw; + _dispofftime = disptime - _dispontime; + dev->dispontime = (int)(_dispontime * (1 << TIMER_SHIFT)); + dev->dispofftime = (int)(_dispofftime * (1 << TIMER_SHIFT)); +} + + +/* Draw the display in CGA (640x400) text mode. */ +void +pgc_cga_text(pgc_t *dev, int w) +{ + int x, c; + uint8_t chr, attr; + int drawcursor = 0; + uint32_t cols[2]; + int pitch = (dev->mapram[0x3e9] + 1) * 2; + uint16_t sc = (dev->displine & 0x0f) % pitch; + uint16_t ma = (dev->mapram[0x3ed] | (dev->mapram[0x3ec] << 8)) & 0x3fff; + uint16_t ca = (dev->mapram[0x3ef] | (dev->mapram[0x3ee] << 8)) & 0x3fff; + uint8_t *addr; + int cw = (w == 80) ? 8 : 16; + + addr = &dev->cga_vram[(((ma << 1) + ((dev->displine / pitch)*w)) * 2) & 0x3ffe]; + ma += (dev->displine / pitch) * w; + + for (x = 0; x < w; x++) { + chr = addr[0]; + attr = addr[1]; + addr += 2; + + /* Cursor enabled? */ + if (ma == ca && (dev->cgablink & 8) && + (dev->mapram[0x3ea] & 0x60) != 0x20) { + drawcursor = ((dev->mapram[0x3ea] & 0x1f) <= (sc >> 1)) && + ((dev->mapram[0x3eb] & 0x1f) >= (sc >> 1)); + } else + drawcursor = 0; + + if (dev->mapram[0x3d8] & 0x20) { + cols[1] = attr & 15; + cols[0] = (attr >> 4) & 7; + if ((dev->cgablink & 8) && (attr & 0x80) && !drawcursor) + cols[1] = cols[0]; + } else { + cols[1] = attr & 15; + cols[0] = attr >> 4; + } + + if (drawcursor) { + for (c = 0; c < cw; c++) { + if (w == 80) + buffer32->line[dev->displine][(x << 3) + c] = cols[(fontdatm[chr + dev->fontbase][sc] & (1 << (c ^ 7))) ? 1 : 0] ^ 0x0f; + else + buffer32->line[dev->displine][(x << 4) + c] = cols[(fontdatm[chr + dev->fontbase][sc] & (1 << (c ^ 7))) ? 1 : 0] ^ 0x0f; + } + } else { + for (c = 0; c < cw; c++) { + if (w == 80) + buffer32->line[dev->displine][(x << 3) + c] = cols[(fontdat[chr + dev->fontbase][sc] & (1 << (c ^ 7))) ? 1 : 0]; + else + buffer32->line[dev->displine][(x << 4) + c] = cols[(fontdat[chr + dev->fontbase][sc] & (1 << (c ^ 7))) ? 1 : 0]; + } + } + + ma++; + } +} + + +/* Draw the display in CGA (320x200) graphics mode. */ +void +pgc_cga_gfx40(pgc_t *dev) +{ + int x, c; + uint32_t cols[4]; + int col; + uint16_t ma = (dev->mapram[0x3ed] | (dev->mapram[0x3ec] << 8)) & 0x3fff; + uint8_t *addr; + uint16_t dat; + + cols[0] = dev->mapram[0x3d9] & 15; + col = (dev->mapram[0x3d9] & 16) ? 8 : 0; + + if (dev->mapram[0x3d8] & 4) { + cols[1] = col | 3; + cols[2] = col | 4; + cols[3] = col | 7; + } else if (dev->mapram[0x3d9] & 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; + } + + for (x = 0; x < 40; x++) { + addr = &dev->cga_vram[(ma + 2 * x + 80 * (dev->displine >> 2) + 0x2000 * ((dev->displine >> 1) & 1)) & 0x3fff]; + dat = (addr[0] << 8) | addr[1]; + dev->ma++; + for (c = 0; c < 8; c++) { + buffer->line[dev->displine][(x << 4) + (c << 1)] = + buffer->line[dev->displine][(x << 4) + (c << 1) + 1] = cols[dat >> 14]; + dat <<= 2; + } + } +} + + +/* Draw the display in CGA (640x200) graphics mode. */ +void +pgc_cga_gfx80(pgc_t *dev) +{ + int x, c; + uint32_t cols[2]; + uint16_t ma = (dev->mapram[0x3ed] | (dev->mapram[0x3ec] << 8)) & 0x3fff; + uint8_t *addr; + uint16_t dat; + + cols[0] = 0; + cols[1] = dev->mapram[0x3d9] & 15; + + for (x = 0; x < 40; x++) { + addr = &dev->cga_vram[(ma + 2 * x + 80 * (dev->displine >> 2) + 0x2000 * ((dev->displine >> 1) & 1)) & 0x3fff]; + dat = (addr[0] << 8) | addr[1]; + dev->ma++; + for (c = 0; c < 16; c++) { + buffer->line[dev->displine][(x << 4) + c] = cols[dat >> 15]; + dat <<= 1; + } + } +} + + +/* Draw the screen in CGA mode. Based on the simplified WY700 renderer + * rather than the original CGA, since the PGC doesn't have a real 6845 */ +void +pgc_cga_poll(pgc_t *dev) +{ + uint32_t cols[2]; + int c; + + if (! dev->linepos) { + dev->vidtime += dev->dispofftime; + dev->mapram[0x3da] |= 1; + dev->linepos = 1; + + if (dev->cgadispon) { + if (dev->displine == 0) + video_wait_for_buffer(); + + if ((dev->mapram[0x3d8] & 0x12) == 0x12) + pgc_cga_gfx80(dev); + else if (dev->mapram[0x3d8] & 0x02) + pgc_cga_gfx40(dev); + else if (dev->mapram[0x3d8] & 0x01) + pgc_cga_text(dev, 80); + else + pgc_cga_text(dev, 40); + } else { + cols[0] = ((dev->mapram[0x3d8] & 0x12) == 0x12) ? 0 : (dev->mapram[0x3d9] & 15); + cga_hline(buffer, 0, dev->displine, PGC_CGA_WIDTH, cols[0]); + } + +#if 0 + for (c = 0; c < PGC_CGA_WIDTH; c++) + buffer->line[dev->displine][c] = *((uint32_t *)&cgapal[buffer->line[dev->displine][c] & 0xf]); +#endif + + if (++dev->displine == PGC_CGA_HEIGHT) { + dev->mapram[0x3da] |= 8; + dev->cgadispon = 0; + } + if (dev->displine == PGC_CGA_HEIGHT + 32) { + dev->mapram[0x3da] &= ~8; + dev->cgadispon = 1; + dev->displine = 0; + } + } else { + if (dev->cgadispon) + dev->mapram[0x3da] &= ~1; + dev->vidtime += dev->dispontime; + dev->linepos = 0; + + if (dev->displine == PGC_CGA_HEIGHT) { + if (PGC_CGA_WIDTH != xsize || PGC_CGA_HEIGHT != ysize) { + xsize = PGC_CGA_WIDTH; + ysize = PGC_CGA_HEIGHT; + set_screen_size(xsize, ysize); + + if (video_force_resize_get()) + video_force_resize_set(0); + } + video_blit_memtoscreen(0, 0, 0, ysize, xsize, ysize); + frames++; + + /* We have a fixed 640x400 screen for CGA modes. */ + video_res_x = PGC_CGA_WIDTH; + video_res_y = PGC_CGA_HEIGHT; + switch (dev->mapram[0x3d8] & 0x12) { + case 0x12: + video_bpp = 1; + break; + + case 0x02: + video_bpp = 2; + break; + + default: + video_bpp = 0; + break; + } + dev->cgablink++; + } + } +} + + +/* Draw the screen in CGA or native mode. */ +void +pgc_poll(void *priv) +{ + pgc_t *dev = (pgc_t *)priv; + uint32_t x, y; + + if (dev->cga_selected) { + pgc_cga_poll(dev); + return; + } + + /* Not CGA, so must be native mode. */ + if (! dev->linepos) { + dev->vidtime += dev->dispofftime; + dev->mapram[0x3da] |= 1; + dev->linepos = 1; + if (dev->cgadispon && (uint32_t)dev->displine < dev->maxh) { + if (dev->displine == 0) + video_wait_for_buffer(); + + /* Don't know why pan needs to be multiplied by -2, but + * the IM1024 driver uses PAN -112 for an offset of + * 224. */ + y = dev->displine - 2 * dev->pan_y; + for (x = 0; x < dev->screenw; x++) { + if (x + dev->pan_x < dev->maxw) { + buffer->line[dev->displine][x] = dev->palette[dev->vram[y * dev->maxw + x]]; + } else { + buffer->line[dev->displine][x] = dev->palette[0]; + } + } + } else { + cga_hline(buffer, 0, dev->displine, dev->screenw, dev->palette[0]); + } + + if (++dev->displine == dev->screenh) { + dev->mapram[0x3da] |= 8; + dev->cgadispon = 0; + } + + if (dev->displine == dev->screenh + 32) { + dev->mapram[0x3da] &= ~8; + dev->cgadispon = 1; + dev->displine = 0; + } + } else { + if (dev->cgadispon) + dev->mapram[0x3da] &= ~1; + dev->vidtime += dev->dispontime; + dev->linepos = 0; + + if (dev->displine == dev->screenh) { + if (dev->screenw != xsize || dev->screenh != ysize) { + xsize = dev->screenw; + ysize = dev->screenh; + set_screen_size(xsize, ysize); + + if (video_force_resize_get()) + video_force_resize_set(0); + } + video_blit_memtoscreen(0, 0, 0, ysize, xsize, ysize); + frames++; + + video_res_x = dev->screenw; + video_res_y = dev->screenh; + video_bpp = 8; + dev->cgablink++; + } + } +} + + +/* Initialize RAM and registers to default values. */ +void +pgc_reset(pgc_t *dev) +{ + int n; + + memset(dev->mapram, 0x00, sizeof(dev->mapram)); + + /* There is no point in emulating the 'CGA disable' jumper as this is only + * appropriate for a dual-head system, not emulated by PCEM */ + dev->mapram[0x30b] = dev->cga_enabled = 1; + dev->mapram[0x3f8] = 0x03; /* Minor version */ + dev->mapram[0x3f9] = 0x01; /* Minor version */ + dev->mapram[0x3fb] = 0xa5; /* } */ + dev->mapram[0x3fc] = 0x5a; /* PGC self-test passed */ + dev->mapram[0x3fd] = 0x55; /* } */ + dev->mapram[0x3fe] = 0x5a; /* } */ + + dev->ascii_mode = 1; /* start off in ASCII mode */ + dev->line_pattern = 0xffff; + memset(dev->fill_pattern, 0xff, sizeof(dev->fill_pattern)); + dev->colour = 0xff; + dev->tjust_h = 1; + dev->tjust_v = 1; + + /* Reset panning. */ + dev->pan_x = 0; + dev->pan_y = 0; + + /* Reset clipping. */ + dev->vp_x1 = 0; + dev->vp_y1 = 0; + dev->vp_x2 = dev->visw - 1; + dev->vp_y2 = dev->vish - 1; + + /* Empty command lists. */ + for (n = 0; n < 256; n++) { + dev->clist[n].wrptr = 0; + dev->clist[n].rdptr = 0; + dev->clist[n].repeat = 0; + dev->clist[n].chain = 0; + } + dev->clcur = NULL; + + /* Select CGA display. */ + dev->cga_selected = -1; + pgc_setdisplay(dev, dev->cga_enabled); + + /* Default palette is 0. */ + init_lut(dev, 0); + hndl_lutsav(dev); +} + + +void +pgc_speed_changed(void *priv) +{ + pgc_t *dev = (pgc_t *)priv; + + pgc_recalctimings(dev); +} + + +void +pgc_close(void *priv) +{ + pgc_t *dev = (pgc_t *)priv; + + if (dev->cga_vram) + free(dev->cga_vram); + if (dev->vram) + free(dev->vram); + + free(dev); +} + + +/* + * Initialization code common to the PGC and its subclasses. + * + * Pass the 'input byte' function in since this is overridden in + * the IM-1024, and needs to be set before the drawing thread is + * launched. + */ +void +pgc_init(pgc_t *dev, int maxw, int maxh, int visw, int vish, + int (*inpbyte)(pgc_t *, uint8_t *)) +{ + int i; + + mem_map_add(&dev->mapping, 0xc6000, 2048, + pgc_read,NULL,NULL, pgc_write,NULL,NULL, + NULL, MEM_MAPPING_EXTERNAL, dev); + mem_map_add(&dev->cga_mapping, 0xb8000, 32768, + pgc_read,NULL,NULL, pgc_write,NULL,NULL, + NULL, MEM_MAPPING_EXTERNAL, dev); + + io_sethandler(0x03d0, 16, + pgc_in,NULL,NULL, pgc_out,NULL,NULL, dev); + + dev->maxw = maxw; + dev->maxh = maxh; + dev->visw = visw; + dev->vish = vish; + + dev->vram = (uint8_t *)mem_alloc(maxw * maxh); + memset(dev->vram, 0x00, maxw * maxh); + dev->cga_vram = (uint8_t *)mem_alloc(16384); + memset(dev->cga_vram, 0x00, 16384); + + /* Create and initialize command lists. */ + dev->clist = (pgc_cl_t *)mem_alloc(256 * sizeof(pgc_cl_t)); + memset(dev->clist, 0x00, 256 * sizeof(pgc_cl_t)); + for (i = 0; i < 256; i++) { + dev->clist[i].list = NULL; + dev->clist[i].listmax = 0; + dev->clist[i].wrptr = 0; + dev->clist[i].rdptr = 0; + dev->clist[i].repeat = 0; + dev->clist[i].chain = NULL; + } + dev->clcur = NULL; + + pgc_reset(dev); + + dev->inputbyte = inpbyte; + dev->commands = pgc_commands; + dev->pgc_wake_thread = thread_create_event(); + dev->pgc_thread = thread_create(pgc_thread, dev); + + video_load_font(CGA_FONT_ROM_PATH, 8); + + timer_add(pgc_poll, &dev->vidtime, TIMER_ALWAYS_ENABLED, dev); + + timer_add(wake_timer, &dev->wake_timer, &dev->wake_timer, (void *)dev); +} + + +/* InitialiSation code specific to the PGC. */ +static void * +pgc_standalone_init(const device_t *info) +{ + pgc_t *dev; + + dev = (pgc_t *)mem_alloc(sizeof(pgc_t)); + memset(dev, 0x00, sizeof(pgc_t)); + dev->type = info->local; + + /* Framebuffer and screen are both 640x480 */ + pgc_init(dev, 640, 480, 640, 480, input_byte); + + video_inform(VID_TYPE_CGA, info->vid_timing); + + return(dev); +} + + +static const video_timings_t pgc_timings = { VID_ISA,8,16,32,8,16,32 }; + +static const device_config_t pgc_config[] = { + { + "", "", -1 + } +}; + + +const device_t pgc_device = { + "IBM PGC", + DEVICE_ISA, + 0, + pgc_standalone_init, pgc_close, NULL, + pgc_speed_changed, + NULL, NULL, + &pgc_timings, + pgc_config +}; diff --git a/src/devices/video/vid_pgc.h b/src/devices/video/vid_pgc.h new file mode 100644 index 0000000..dfeeacf --- /dev/null +++ b/src/devices/video/vid_pgc.h @@ -0,0 +1,193 @@ +/* + * VARCem Virtual ARchaeological Computer EMulator. + * An emulator of (mostly) x86-based PC systems and devices, + * using the ISA,EISA,VLB,MCA and PCI system buses, roughly + * spanning the era between 1981 and 1995. + * + * This file is part of the VARCem Project. + * + * Definitions for the PGC driver. + * + * Version: @(#)vid_pgc.h 1.0.1 2019/03/01 + * + * Authors: Fred N. van Kempen, + * John Elliott, + * + * Copyright 2019 Fred N. van Kempen. + * Copyright 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. + */ +#ifndef VID_PGC_H +# define VID_PGC_H + + +#define PGC_ERROR_RANGE 0x01 +#define PGC_ERROR_INTEGER 0x02 +#define PGC_ERROR_MEMORY 0x03 +#define PGC_ERROR_OVERFLOW 0x04 +#define PGC_ERROR_DIGIT 0x05 +#define PGC_ERROR_OPCODE 0x06 +#define PGC_ERROR_RUNNING 0x07 +#define PGC_ERROR_STACK 0x08 +#define PGC_ERROR_TOOLONG 0x09 +#define PGC_ERROR_AREA 0x0A +#define PGC_ERROR_MISSING 0x0B + + +struct pgc; + +typedef struct pgc_cl { + uint8_t *list; + uint32_t listmax; + uint32_t wrptr; + uint32_t rdptr; + uint32_t repeat; + struct pgc_cl *chain; +} pgc_cl_t; + +typedef struct pgc_cmd { + char ascii[6]; + uint8_t hex; + void (*handler)(struct pgc *); + int (*parser) (struct pgc *, pgc_cl_t *, int); + int p; +} pgc_cmd_t; + +typedef struct pgc { + int type; /* board type */ + + mem_map_t mapping; + mem_map_t cga_mapping; + + pgc_cl_t *clist, + *clcur; + const pgc_cmd_t *commands; + + uint8_t mapram[2048]; /* Host <--> PGC communication buffer */ + uint8_t *cga_vram; + uint8_t *vram; + char asc_command[7]; + uint8_t hex_command; + uint32_t palette[256]; + uint32_t userpal[256]; + uint32_t maxw, maxh; /* Maximum framebuffer size */ + uint32_t visw, vish; /* Maximum screen size */ + uint32_t screenw, screenh; + int16_t pan_x, pan_y; + uint16_t win_x1, win_x2, win_y1, win_y2; + uint16_t vp_x1, vp_x2, vp_y1, vp_y2; + int16_t fill_pattern[16]; + int16_t line_pattern; + uint8_t draw_mode; + uint8_t fill_mode; + uint8_t colour; + uint8_t tjust_h; /* hor alignment 1=left 2=center 3=right*/ + uint8_t tjust_v; /* vert alignment 1=bottom 2=center 3=top*/ + + int32_t x, y, z; /* drawing position */ + + thread_t *pgc_thread; + event_t *pgc_wake_thread; + int64_t wake_timer; + + int waiting_input_fifo; + int waiting_output_fifo; + int waiting_error_fifo; + int cga_enabled; + int cga_selected; + int ascii_mode; + int result_count; + + int fontbase; + int linepos, displine; + int vc; + int cgadispon; + int con, coff, cursoron, cgablink; + int vsynctime, vadj; + uint16_t ma, maback; + int oddeven; + + int dispontime, dispofftime; + int64_t vidtime; + + int drawcursor; + + int (*inputbyte)(struct pgc *, uint8_t *result); +} pgc_t; + + +/* I/O functions and worker thread handlers. */ +extern void pgc_out(uint16_t addr, uint8_t val, void *priv); +extern uint8_t pgc_in(uint16_t addr, void *priv); +extern void pgc_write(uint32_t addr, uint8_t val, void *priv); +extern uint8_t pgc_read(uint32_t addr, void *priv); +extern void pgc_recalctimings(pgc_t *); +extern void pgc_poll(void *priv); +extern void pgc_reset(pgc_t *); +extern void pgc_wake(pgc_t *); +extern void pgc_sleep(pgc_t *); +extern void pgc_close(void *priv); +extern void pgc_speed_changed(void *priv); +extern void pgc_init(pgc_t *, + int maxw, int maxh, int visw, int vish, + int (*inpbyte)(pgc_t *, uint8_t *)); + +/* Misc support functions. */ +extern uint8_t *pgc_vram_addr(pgc_t *, int16_t x, int16_t y); +extern void pgc_sto_raster(pgc_t *, int16_t *x, int16_t *y); +extern void pgc_ito_raster(pgc_t *, int32_t *x, int32_t *y); +extern void pgc_dto_raster(pgc_t *, double *x, double *y); +//extern int pgc_input_byte(pgc_t *, uint8_t *val); +//extern int pgc_output_byte(pgc_t *, uint8_t val); +extern int pgc_output_string(pgc_t *, const char *val); +//extern int pgc_error_byte(pgc_t *, uint8_t val); +extern int pgc_error_string(pgc_t *, const char *val); +extern int pgc_error(pgc_t *, int err); + +/* Graphics functions. */ +extern void pgc_plot(pgc_t *, uint16_t x, uint16_t y); +extern void pgc_write_pixel(pgc_t *, uint16_t x, uint16_t y, uint8_t ink); +extern uint8_t pgc_read_pixel(pgc_t *, uint16_t x, uint16_t y); +extern uint16_t pgc_draw_line(pgc_t *, int32_t x1, int32_t y1, + int32_t x2, int32_t y2, uint16_t linemask); +extern uint16_t pgc_draw_line_r(pgc_t *, int32_t x1, int32_t y1, + int32_t x2, int32_t y2, uint16_t linemask); +extern void pgc_draw_ellipse(pgc_t *, int32_t x, int32_t y); +extern void pgc_fill_polygon(pgc_t *, + unsigned corners, int32_t *x, int32_t *y); +extern void pgc_fill_line_r(pgc_t *, int32_t x0, int32_t x1, int32_t y); + +/* Command and parameter handling functions. */ +extern int pgc_commandlist_append(pgc_cl_t *, uint8_t v); +extern int pgc_parse_bytes(pgc_t *, pgc_cl_t *, int p); +extern int pgc_parse_words(pgc_t *, pgc_cl_t *, int p); +extern int pgc_parse_coords(pgc_t *, pgc_cl_t *, int p); +extern int pgc_param_byte(pgc_t *, uint8_t *val); +extern int pgc_param_word(pgc_t *, int16_t *val); +extern int pgc_param_coord(pgc_t *, int32_t *val); +extern int pgc_result_byte(pgc_t *, uint8_t val); +extern int pgc_result_word(pgc_t *, int16_t val); +extern int pgc_result_coord(pgc_t *, int32_t val); + +extern void pgc_hndl_lut8(pgc_t *); +extern void pgc_hndl_lut8rd(pgc_t *); + + +#endif /*VID_PGC_H*/ diff --git a/src/devices/video/vid_pgc_palette.h b/src/devices/video/vid_pgc_palette.h new file mode 100644 index 0000000..9214cc9 --- /dev/null +++ b/src/devices/video/vid_pgc_palette.h @@ -0,0 +1,1597 @@ +/* + * VARCem Virtual ARchaeological Computer EMulator. + * An emulator of (mostly) x86-based PC systems and devices, + * using the ISA,EISA,VLB,MCA and PCI system buses, roughly + * spanning the era between 1981 and 1995. + * + * This file is part of the VARCem Project. + * + * Palette definitions for the PGC core. + * + * Version: @(#)vid_pgc_palette.h 1.0.1 2019/03/01 + * + * Authors: Fred N. van Kempen, + * John Elliott, + * + * Copyright 2019 Fred N. van Kempen. + * Copyright 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. + */ +#ifndef VID_PGC_PALETTE_H +# define VID_PGC_PALETTE_H + + +{ + /* Palette 0: Default */ + makecol(0x00, 0x00, 0x00), + makecol(0x11, 0x11, 0x11), + makecol(0x22, 0x22, 0x22), + makecol(0x33, 0x33, 0x33), + makecol(0x44, 0x44, 0x44), + makecol(0x55, 0x55, 0x55), + makecol(0x66, 0x66, 0x66), + makecol(0x77, 0x77, 0x77), + makecol(0x88, 0x88, 0x88), + makecol(0x99, 0x99, 0x99), + makecol(0xaa, 0xaa, 0xaa), + makecol(0xbb, 0xbb, 0xbb), + makecol(0xcc, 0xcc, 0xcc), + makecol(0xdd, 0xdd, 0xdd), + makecol(0xee, 0xee, 0xee), + makecol(0xff, 0xff, 0xff), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x22, 0x00), + makecol(0x00, 0x44, 0x00), + makecol(0x00, 0x66, 0x00), + makecol(0x00, 0x88, 0x00), + makecol(0x00, 0xaa, 0x00), + makecol(0x00, 0xcc, 0x00), + makecol(0x00, 0xee, 0x00), + makecol(0x00, 0xff, 0x00), + makecol(0x22, 0xff, 0x22), + makecol(0x44, 0xff, 0x44), + makecol(0x66, 0xff, 0x66), + makecol(0x88, 0xff, 0x88), + makecol(0xaa, 0xff, 0xaa), + makecol(0xcc, 0xff, 0xcc), + makecol(0xee, 0xff, 0xee), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x22, 0x11), + makecol(0x00, 0x44, 0x22), + makecol(0x00, 0x66, 0x33), + makecol(0x00, 0x88, 0x44), + makecol(0x00, 0xaa, 0x55), + makecol(0x00, 0xcc, 0x66), + makecol(0x00, 0xee, 0x77), + makecol(0x00, 0xff, 0x88), + makecol(0x22, 0xff, 0x99), + makecol(0x44, 0xff, 0xaa), + makecol(0x66, 0xff, 0xbb), + makecol(0x88, 0xff, 0xcc), + makecol(0xaa, 0xff, 0xdd), + makecol(0xcc, 0xff, 0xee), + makecol(0xee, 0xff, 0xff), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x22, 0x22), + makecol(0x00, 0x44, 0x44), + makecol(0x00, 0x66, 0x66), + makecol(0x00, 0x88, 0x88), + makecol(0x00, 0xaa, 0xaa), + makecol(0x00, 0xcc, 0xcc), + makecol(0x00, 0xee, 0xee), + makecol(0x00, 0xff, 0xff), + makecol(0x22, 0xff, 0xff), + makecol(0x44, 0xff, 0xff), + makecol(0x66, 0xff, 0xff), + makecol(0x88, 0xff, 0xff), + makecol(0xaa, 0xff, 0xff), + makecol(0xcc, 0xff, 0xff), + makecol(0xee, 0xff, 0xff), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x11, 0x22), + makecol(0x00, 0x22, 0x44), + makecol(0x00, 0x33, 0x66), + makecol(0x00, 0x44, 0x88), + makecol(0x00, 0x55, 0xaa), + makecol(0x00, 0x66, 0xcc), + makecol(0x00, 0x77, 0xee), + makecol(0x00, 0x88, 0xff), + makecol(0x22, 0x99, 0xff), + makecol(0x44, 0xaa, 0xff), + makecol(0x66, 0xbb, 0xff), + makecol(0x88, 0xcc, 0xff), + makecol(0xaa, 0xdd, 0xff), + makecol(0xcc, 0xee, 0xff), + makecol(0xee, 0xff, 0xff), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x22), + makecol(0x00, 0x00, 0x44), + makecol(0x00, 0x00, 0x66), + makecol(0x00, 0x00, 0x88), + makecol(0x00, 0x00, 0xaa), + makecol(0x00, 0x00, 0xcc), + makecol(0x00, 0x00, 0xee), + makecol(0x00, 0x00, 0xff), + makecol(0x22, 0x22, 0xff), + makecol(0x44, 0x44, 0xff), + makecol(0x66, 0x66, 0xff), + makecol(0x88, 0x88, 0xff), + makecol(0xaa, 0xaa, 0xff), + makecol(0xcc, 0xcc, 0xff), + makecol(0xee, 0xee, 0xff), + makecol(0x00, 0x00, 0x00), + makecol(0x11, 0x00, 0x22), + makecol(0x22, 0x00, 0x44), + makecol(0x33, 0x00, 0x66), + makecol(0x44, 0x00, 0x88), + makecol(0x55, 0x00, 0xaa), + makecol(0x66, 0x00, 0xcc), + makecol(0x77, 0x00, 0xee), + makecol(0x88, 0x00, 0xff), + makecol(0x99, 0x22, 0xff), + makecol(0xaa, 0x44, 0xff), + makecol(0xbb, 0x66, 0xff), + makecol(0xcc, 0x88, 0xff), + makecol(0xdd, 0xaa, 0xff), + makecol(0xee, 0xcc, 0xff), + makecol(0xff, 0xee, 0xff), + makecol(0x00, 0x00, 0x00), + makecol(0x22, 0x00, 0x22), + makecol(0x44, 0x00, 0x44), + makecol(0x66, 0x00, 0x66), + makecol(0x88, 0x00, 0x88), + makecol(0xaa, 0x00, 0xaa), + makecol(0xcc, 0x00, 0xcc), + makecol(0xee, 0x00, 0xee), + makecol(0xff, 0x00, 0xff), + makecol(0xff, 0x22, 0xff), + makecol(0xff, 0x44, 0xff), + makecol(0xff, 0x66, 0xff), + makecol(0xff, 0x88, 0xff), + makecol(0xff, 0xaa, 0xff), + makecol(0xff, 0xcc, 0xff), + makecol(0xff, 0xee, 0xff), + makecol(0x00, 0x00, 0x00), + makecol(0x22, 0x00, 0x11), + makecol(0x44, 0x00, 0x22), + makecol(0x66, 0x00, 0x33), + makecol(0x88, 0x00, 0x44), + makecol(0xaa, 0x00, 0x55), + makecol(0xcc, 0x00, 0x66), + makecol(0xee, 0x00, 0x77), + makecol(0xff, 0x00, 0x88), + makecol(0xff, 0x22, 0x99), + makecol(0xff, 0x44, 0xaa), + makecol(0xff, 0x66, 0xbb), + makecol(0xff, 0x88, 0xcc), + makecol(0xff, 0xaa, 0xdd), + makecol(0xff, 0xcc, 0xee), + makecol(0xff, 0xee, 0xff), + makecol(0x00, 0x00, 0x00), + makecol(0x22, 0x00, 0x00), + makecol(0x44, 0x00, 0x00), + makecol(0x66, 0x00, 0x00), + makecol(0x88, 0x00, 0x00), + makecol(0xaa, 0x00, 0x00), + makecol(0xcc, 0x00, 0x00), + makecol(0xee, 0x00, 0x00), + makecol(0xff, 0x00, 0x00), + makecol(0xff, 0x22, 0x22), + makecol(0xff, 0x44, 0x44), + makecol(0xff, 0x66, 0x66), + makecol(0xff, 0x88, 0x88), + makecol(0xff, 0xaa, 0xaa), + makecol(0xff, 0xcc, 0xcc), + makecol(0xff, 0xee, 0xee), + makecol(0x00, 0x00, 0x00), + makecol(0x22, 0x11, 0x00), + makecol(0x44, 0x22, 0x00), + makecol(0x66, 0x33, 0x00), + makecol(0x88, 0x44, 0x00), + makecol(0xaa, 0x55, 0x00), + makecol(0xcc, 0x66, 0x00), + makecol(0xee, 0x77, 0x00), + makecol(0xff, 0x88, 0x00), + makecol(0xff, 0x99, 0x22), + makecol(0xff, 0xaa, 0x44), + makecol(0xff, 0xbb, 0x66), + makecol(0xff, 0xcc, 0x88), + makecol(0xff, 0xdd, 0xaa), + makecol(0xff, 0xee, 0xcc), + makecol(0xff, 0xff, 0xee), + makecol(0x00, 0x00, 0x00), + makecol(0x22, 0x22, 0x00), + makecol(0x44, 0x44, 0x00), + makecol(0x66, 0x66, 0x00), + makecol(0x88, 0x88, 0x00), + makecol(0xaa, 0xaa, 0x00), + makecol(0xcc, 0xcc, 0x00), + makecol(0xee, 0xee, 0x00), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xff, 0x22), + makecol(0xff, 0xff, 0x44), + makecol(0xff, 0xff, 0x66), + makecol(0xff, 0xff, 0x88), + makecol(0xff, 0xff, 0xaa), + makecol(0xff, 0xff, 0xcc), + makecol(0xff, 0xff, 0xee), + makecol(0x00, 0x00, 0x00), + makecol(0x11, 0x22, 0x00), + makecol(0x22, 0x44, 0x00), + makecol(0x33, 0x66, 0x00), + makecol(0x44, 0x88, 0x00), + makecol(0x55, 0xaa, 0x00), + makecol(0x66, 0xcc, 0x00), + makecol(0x77, 0xee, 0x00), + makecol(0x88, 0xff, 0x00), + makecol(0x99, 0xff, 0x22), + makecol(0xaa, 0xff, 0x44), + makecol(0xbb, 0xff, 0x66), + makecol(0xcc, 0xff, 0x88), + makecol(0xdd, 0xff, 0xaa), + makecol(0xee, 0xff, 0xcc), + makecol(0xff, 0xff, 0xee), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x11, 0x00), + makecol(0x11, 0x33, 0x11), + makecol(0x11, 0x44, 0x11), + makecol(0x22, 0x66, 0x22), + makecol(0x22, 0x77, 0x22), + makecol(0x33, 0x99, 0x33), + makecol(0x33, 0xaa, 0x33), + makecol(0x44, 0xcc, 0x44), + makecol(0x55, 0xcc, 0x55), + makecol(0x77, 0xdd, 0x77), + makecol(0x88, 0xdd, 0x88), + makecol(0xaa, 0xee, 0xaa), + makecol(0xbb, 0xee, 0xbb), + makecol(0xdd, 0xff, 0xdd), + makecol(0xee, 0xff, 0xee), + makecol(0x00, 0x00, 0x00), + makecol(0x11, 0x00, 0x00), + makecol(0x33, 0x11, 0x11), + makecol(0x44, 0x11, 0x11), + makecol(0x66, 0x22, 0x22), + makecol(0x77, 0x22, 0x22), + makecol(0x99, 0x33, 0x33), + makecol(0xaa, 0x33, 0x33), + makecol(0xcc, 0x44, 0x44), + makecol(0xcc, 0x55, 0x55), + makecol(0xdd, 0x77, 0x77), + makecol(0xdd, 0x88, 0x88), + makecol(0xee, 0xaa, 0xaa), + makecol(0xee, 0xbb, 0xbb), + makecol(0xff, 0xdd, 0xdd), + makecol(0xff, 0xee, 0xee), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x11), + makecol(0x11, 0x11, 0x33), + makecol(0x11, 0x11, 0x44), + makecol(0x22, 0x22, 0x66), + makecol(0x22, 0x22, 0x77), + makecol(0x33, 0x33, 0x99), + makecol(0x33, 0x33, 0xaa), + makecol(0x44, 0x44, 0xcc), + makecol(0x55, 0x55, 0xcc), + makecol(0x77, 0x77, 0xdd), + makecol(0x88, 0x88, 0xdd), + makecol(0xaa, 0xaa, 0xee), + makecol(0xbb, 0xbb, 0xee), + makecol(0xdd, 0xdd, 0xff), + makecol(0xee, 0xee, 0xff) +}, +{ + /* Palette 1: 16-color palette. */ + makecol(0x88, 0x66, 0xdd), + makecol(0x00, 0x00, 0x00), + makecol(0x44, 0x77, 0x22), + makecol(0x77, 0xaa, 0x44), + makecol(0x00, 0x77, 0x00), + makecol(0x00, 0xff, 0x00), + makecol(0x77, 0xff, 0x00), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xaa, 0x00), + makecol(0xff, 0x00, 0x00), + makecol(0x77, 0x00, 0x00), + makecol(0x77, 0x00, 0x77), + makecol(0x00, 0x00, 0x77), + makecol(0x99, 0xee, 0x66), + makecol(0x77, 0x77, 0x77), + makecol(0xff, 0xff, 0xff), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x44, 0x77, 0x22), + makecol(0x44, 0x77, 0x22), + makecol(0x44, 0x77, 0x22), + makecol(0x44, 0x77, 0x22), + makecol(0x44, 0x77, 0x22), + makecol(0x44, 0x77, 0x22), + makecol(0x44, 0x77, 0x22), + makecol(0x44, 0x77, 0x22), + makecol(0x44, 0x77, 0x22), + makecol(0x44, 0x77, 0x22), + makecol(0x44, 0x77, 0x22), + makecol(0x44, 0x77, 0x22), + makecol(0x44, 0x77, 0x22), + makecol(0x44, 0x77, 0x22), + makecol(0x44, 0x77, 0x22), + makecol(0x44, 0x77, 0x22), + makecol(0x77, 0xaa, 0x44), + makecol(0x77, 0xaa, 0x44), + makecol(0x77, 0xaa, 0x44), + makecol(0x77, 0xaa, 0x44), + makecol(0x77, 0xaa, 0x44), + makecol(0x77, 0xaa, 0x44), + makecol(0x77, 0xaa, 0x44), + makecol(0x77, 0xaa, 0x44), + makecol(0x77, 0xaa, 0x44), + makecol(0x77, 0xaa, 0x44), + makecol(0x77, 0xaa, 0x44), + makecol(0x77, 0xaa, 0x44), + makecol(0x77, 0xaa, 0x44), + makecol(0x77, 0xaa, 0x44), + makecol(0x77, 0xaa, 0x44), + makecol(0x77, 0xaa, 0x44), + makecol(0x00, 0x77, 0x00), + makecol(0x00, 0x77, 0x00), + makecol(0x00, 0x77, 0x00), + makecol(0x00, 0x77, 0x00), + makecol(0x00, 0x77, 0x00), + makecol(0x00, 0x77, 0x00), + makecol(0x00, 0x77, 0x00), + makecol(0x00, 0x77, 0x00), + makecol(0x00, 0x77, 0x00), + makecol(0x00, 0x77, 0x00), + makecol(0x00, 0x77, 0x00), + makecol(0x00, 0x77, 0x00), + makecol(0x00, 0x77, 0x00), + makecol(0x00, 0x77, 0x00), + makecol(0x00, 0x77, 0x00), + makecol(0x00, 0x77, 0x00), + makecol(0x00, 0xff, 0x00), + makecol(0x00, 0xff, 0x00), + makecol(0x00, 0xff, 0x00), + makecol(0x00, 0xff, 0x00), + makecol(0x00, 0xff, 0x00), + makecol(0x00, 0xff, 0x00), + makecol(0x00, 0xff, 0x00), + makecol(0x00, 0xff, 0x00), + makecol(0x00, 0xff, 0x00), + makecol(0x00, 0xff, 0x00), + makecol(0x00, 0xff, 0x00), + makecol(0x00, 0xff, 0x00), + makecol(0x00, 0xff, 0x00), + makecol(0x00, 0xff, 0x00), + makecol(0x00, 0xff, 0x00), + makecol(0x00, 0xff, 0x00), + makecol(0x77, 0xff, 0x00), + makecol(0x77, 0xff, 0x00), + makecol(0x77, 0xff, 0x00), + makecol(0x77, 0xff, 0x00), + makecol(0x77, 0xff, 0x00), + makecol(0x77, 0xff, 0x00), + makecol(0x77, 0xff, 0x00), + makecol(0x77, 0xff, 0x00), + makecol(0x77, 0xff, 0x00), + makecol(0x77, 0xff, 0x00), + makecol(0x77, 0xff, 0x00), + makecol(0x77, 0xff, 0x00), + makecol(0x77, 0xff, 0x00), + makecol(0x77, 0xff, 0x00), + makecol(0x77, 0xff, 0x00), + makecol(0x77, 0xff, 0x00), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xaa, 0x00), + makecol(0xff, 0xaa, 0x00), + makecol(0xff, 0xaa, 0x00), + makecol(0xff, 0xaa, 0x00), + makecol(0xff, 0xaa, 0x00), + makecol(0xff, 0xaa, 0x00), + makecol(0xff, 0xaa, 0x00), + makecol(0xff, 0xaa, 0x00), + makecol(0xff, 0xaa, 0x00), + makecol(0xff, 0xaa, 0x00), + makecol(0xff, 0xaa, 0x00), + makecol(0xff, 0xaa, 0x00), + makecol(0xff, 0xaa, 0x00), + makecol(0xff, 0xaa, 0x00), + makecol(0xff, 0xaa, 0x00), + makecol(0xff, 0xaa, 0x00), + makecol(0xff, 0x00, 0x00), + makecol(0xff, 0x00, 0x00), + makecol(0xff, 0x00, 0x00), + makecol(0xff, 0x00, 0x00), + makecol(0xff, 0x00, 0x00), + makecol(0xff, 0x00, 0x00), + makecol(0xff, 0x00, 0x00), + makecol(0xff, 0x00, 0x00), + makecol(0xff, 0x00, 0x00), + makecol(0xff, 0x00, 0x00), + makecol(0xff, 0x00, 0x00), + makecol(0xff, 0x00, 0x00), + makecol(0xff, 0x00, 0x00), + makecol(0xff, 0x00, 0x00), + makecol(0xff, 0x00, 0x00), + makecol(0xff, 0x00, 0x00), + makecol(0x77, 0x00, 0x00), + makecol(0x77, 0x00, 0x00), + makecol(0x77, 0x00, 0x00), + makecol(0x77, 0x00, 0x00), + makecol(0x77, 0x00, 0x00), + makecol(0x77, 0x00, 0x00), + makecol(0x77, 0x00, 0x00), + makecol(0x77, 0x00, 0x00), + makecol(0x77, 0x00, 0x00), + makecol(0x77, 0x00, 0x00), + makecol(0x77, 0x00, 0x00), + makecol(0x77, 0x00, 0x00), + makecol(0x77, 0x00, 0x00), + makecol(0x77, 0x00, 0x00), + makecol(0x77, 0x00, 0x00), + makecol(0x77, 0x00, 0x00), + makecol(0x77, 0x00, 0x77), + makecol(0x77, 0x00, 0x77), + makecol(0x77, 0x00, 0x77), + makecol(0x77, 0x00, 0x77), + makecol(0x77, 0x00, 0x77), + makecol(0x77, 0x00, 0x77), + makecol(0x77, 0x00, 0x77), + makecol(0x77, 0x00, 0x77), + makecol(0x77, 0x00, 0x77), + makecol(0x77, 0x00, 0x77), + makecol(0x77, 0x00, 0x77), + makecol(0x77, 0x00, 0x77), + makecol(0x77, 0x00, 0x77), + makecol(0x77, 0x00, 0x77), + makecol(0x77, 0x00, 0x77), + makecol(0x77, 0x00, 0x77), + makecol(0x00, 0x00, 0x77), + makecol(0x00, 0x00, 0x77), + makecol(0x00, 0x00, 0x77), + makecol(0x00, 0x00, 0x77), + makecol(0x00, 0x00, 0x77), + makecol(0x00, 0x00, 0x77), + makecol(0x00, 0x00, 0x77), + makecol(0x00, 0x00, 0x77), + makecol(0x00, 0x00, 0x77), + makecol(0x00, 0x00, 0x77), + makecol(0x00, 0x00, 0x77), + makecol(0x00, 0x00, 0x77), + makecol(0x00, 0x00, 0x77), + makecol(0x00, 0x00, 0x77), + makecol(0x00, 0x00, 0x77), + makecol(0x00, 0x00, 0x77), + makecol(0x99, 0xee, 0x66), + makecol(0x99, 0xee, 0x66), + makecol(0x99, 0xee, 0x66), + makecol(0x99, 0xee, 0x66), + makecol(0x99, 0xee, 0x66), + makecol(0x99, 0xee, 0x66), + makecol(0x99, 0xee, 0x66), + makecol(0x99, 0xee, 0x66), + makecol(0x99, 0xee, 0x66), + makecol(0x99, 0xee, 0x66), + makecol(0x99, 0xee, 0x66), + makecol(0x99, 0xee, 0x66), + makecol(0x99, 0xee, 0x66), + makecol(0x99, 0xee, 0x66), + makecol(0x99, 0xee, 0x66), + makecol(0x99, 0xee, 0x66), + makecol(0x77, 0x77, 0x77), + makecol(0x77, 0x77, 0x77), + makecol(0x77, 0x77, 0x77), + makecol(0x77, 0x77, 0x77), + makecol(0x77, 0x77, 0x77), + makecol(0x77, 0x77, 0x77), + makecol(0x77, 0x77, 0x77), + makecol(0x77, 0x77, 0x77), + makecol(0x77, 0x77, 0x77), + makecol(0x77, 0x77, 0x77), + makecol(0x77, 0x77, 0x77), + makecol(0x77, 0x77, 0x77), + makecol(0x77, 0x77, 0x77), + makecol(0x77, 0x77, 0x77), + makecol(0x77, 0x77, 0x77), + makecol(0x77, 0x77, 0x77), + makecol(0xff, 0xff, 0xff), + makecol(0xff, 0xff, 0xff), + makecol(0xff, 0xff, 0xff), + makecol(0xff, 0xff, 0xff), + makecol(0xff, 0xff, 0xff), + makecol(0xff, 0xff, 0xff), + makecol(0xff, 0xff, 0xff), + makecol(0xff, 0xff, 0xff), + makecol(0xff, 0xff, 0xff), + makecol(0xff, 0xff, 0xff), + makecol(0xff, 0xff, 0xff), + makecol(0xff, 0xff, 0xff), + makecol(0xff, 0xff, 0xff), + makecol(0xff, 0xff, 0xff), + makecol(0xff, 0xff, 0xff), + makecol(0xff, 0xff, 0xff) +}, +{ + /* Palette 2: 2-3-3 TrueColor. */ + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x33), + makecol(0x00, 0x00, 0x55), + makecol(0x00, 0x00, 0x77), + makecol(0x00, 0x00, 0x99), + makecol(0x00, 0x00, 0xbb), + makecol(0x00, 0x00, 0xdd), + makecol(0x00, 0x00, 0xff), + makecol(0x33, 0x00, 0x00), + makecol(0x33, 0x00, 0x33), + makecol(0x33, 0x00, 0x55), + makecol(0x33, 0x00, 0x77), + makecol(0x33, 0x00, 0x99), + makecol(0x33, 0x00, 0xbb), + makecol(0x33, 0x00, 0xdd), + makecol(0x33, 0x00, 0xff), + makecol(0x55, 0x00, 0x00), + makecol(0x55, 0x00, 0x33), + makecol(0x55, 0x00, 0x55), + makecol(0x55, 0x00, 0x77), + makecol(0x55, 0x00, 0x99), + makecol(0x55, 0x00, 0xbb), + makecol(0x55, 0x00, 0xdd), + makecol(0x55, 0x00, 0xff), + makecol(0x77, 0x00, 0x00), + makecol(0x77, 0x00, 0x33), + makecol(0x77, 0x00, 0x55), + makecol(0x77, 0x00, 0x77), + makecol(0x77, 0x00, 0x99), + makecol(0x77, 0x00, 0xbb), + makecol(0x77, 0x00, 0xdd), + makecol(0x77, 0x00, 0xff), + makecol(0x99, 0x00, 0x00), + makecol(0x99, 0x00, 0x33), + makecol(0x99, 0x00, 0x55), + makecol(0x99, 0x00, 0x77), + makecol(0x99, 0x00, 0x99), + makecol(0x99, 0x00, 0xbb), + makecol(0x99, 0x00, 0xdd), + makecol(0x99, 0x00, 0xff), + makecol(0xbb, 0x00, 0x00), + makecol(0xbb, 0x00, 0x33), + makecol(0xbb, 0x00, 0x55), + makecol(0xbb, 0x00, 0x77), + makecol(0xbb, 0x00, 0x99), + makecol(0xbb, 0x00, 0xbb), + makecol(0xbb, 0x00, 0xdd), + makecol(0xbb, 0x00, 0xff), + makecol(0xdd, 0x00, 0x00), + makecol(0xdd, 0x00, 0x33), + makecol(0xdd, 0x00, 0x55), + makecol(0xdd, 0x00, 0x77), + makecol(0xdd, 0x00, 0x99), + makecol(0xdd, 0x00, 0xbb), + makecol(0xdd, 0x00, 0xdd), + makecol(0xdd, 0x00, 0xff), + makecol(0xff, 0x00, 0x00), + makecol(0xff, 0x00, 0x33), + makecol(0xff, 0x00, 0x55), + makecol(0xff, 0x00, 0x77), + makecol(0xff, 0x00, 0x99), + makecol(0xff, 0x00, 0xbb), + makecol(0xff, 0x00, 0xdd), + makecol(0xff, 0x00, 0xff), + makecol(0x00, 0x55, 0x00), + makecol(0x00, 0x55, 0x33), + makecol(0x00, 0x55, 0x55), + makecol(0x00, 0x55, 0x77), + makecol(0x00, 0x55, 0x99), + makecol(0x00, 0x55, 0xbb), + makecol(0x00, 0x55, 0xdd), + makecol(0x00, 0x55, 0xff), + makecol(0x33, 0x55, 0x00), + makecol(0x33, 0x55, 0x33), + makecol(0x33, 0x55, 0x55), + makecol(0x33, 0x55, 0x77), + makecol(0x33, 0x55, 0x99), + makecol(0x33, 0x55, 0xbb), + makecol(0x33, 0x55, 0xdd), + makecol(0x33, 0x55, 0xff), + makecol(0x55, 0x55, 0x00), + makecol(0x55, 0x55, 0x33), + makecol(0x55, 0x55, 0x55), + makecol(0x55, 0x55, 0x77), + makecol(0x55, 0x55, 0x99), + makecol(0x55, 0x55, 0xbb), + makecol(0x55, 0x55, 0xdd), + makecol(0x55, 0x55, 0xff), + makecol(0x77, 0x55, 0x00), + makecol(0x77, 0x55, 0x33), + makecol(0x77, 0x55, 0x55), + makecol(0x77, 0x55, 0x77), + makecol(0x77, 0x55, 0x99), + makecol(0x77, 0x55, 0xbb), + makecol(0x77, 0x55, 0xdd), + makecol(0x77, 0x55, 0xff), + makecol(0x99, 0x55, 0x00), + makecol(0x99, 0x55, 0x33), + makecol(0x99, 0x55, 0x55), + makecol(0x99, 0x55, 0x77), + makecol(0x99, 0x55, 0x99), + makecol(0x99, 0x55, 0xbb), + makecol(0x99, 0x55, 0xdd), + makecol(0x99, 0x55, 0xff), + makecol(0xbb, 0x55, 0x00), + makecol(0xbb, 0x55, 0x33), + makecol(0xbb, 0x55, 0x55), + makecol(0xbb, 0x55, 0x77), + makecol(0xbb, 0x55, 0x99), + makecol(0xbb, 0x55, 0xbb), + makecol(0xbb, 0x55, 0xdd), + makecol(0xbb, 0x55, 0xff), + makecol(0xdd, 0x55, 0x00), + makecol(0xdd, 0x55, 0x33), + makecol(0xdd, 0x55, 0x55), + makecol(0xdd, 0x55, 0x77), + makecol(0xdd, 0x55, 0x99), + makecol(0xdd, 0x55, 0xbb), + makecol(0xdd, 0x55, 0xdd), + makecol(0xdd, 0x55, 0xff), + makecol(0xff, 0x55, 0x00), + makecol(0xff, 0x55, 0x33), + makecol(0xff, 0x55, 0x55), + makecol(0xff, 0x55, 0x77), + makecol(0xff, 0x55, 0x99), + makecol(0xff, 0x55, 0xbb), + makecol(0xff, 0x55, 0xdd), + makecol(0xff, 0x55, 0xff), + makecol(0x00, 0xaa, 0x00), + makecol(0x00, 0xaa, 0x33), + makecol(0x00, 0xaa, 0x55), + makecol(0x00, 0xaa, 0x77), + makecol(0x00, 0xaa, 0x99), + makecol(0x00, 0xaa, 0xbb), + makecol(0x00, 0xaa, 0xdd), + makecol(0x00, 0xaa, 0xff), + makecol(0x33, 0xaa, 0x00), + makecol(0x33, 0xaa, 0x33), + makecol(0x33, 0xaa, 0x55), + makecol(0x33, 0xaa, 0x77), + makecol(0x33, 0xaa, 0x99), + makecol(0x33, 0xaa, 0xbb), + makecol(0x33, 0xaa, 0xdd), + makecol(0x33, 0xaa, 0xff), + makecol(0x55, 0xaa, 0x00), + makecol(0x55, 0xaa, 0x33), + makecol(0x55, 0xaa, 0x55), + makecol(0x55, 0xaa, 0x77), + makecol(0x55, 0xaa, 0x99), + makecol(0x55, 0xaa, 0xbb), + makecol(0x55, 0xaa, 0xdd), + makecol(0x55, 0xaa, 0xff), + makecol(0x77, 0xaa, 0x00), + makecol(0x77, 0xaa, 0x33), + makecol(0x77, 0xaa, 0x55), + makecol(0x77, 0xaa, 0x77), + makecol(0x77, 0xaa, 0x99), + makecol(0x77, 0xaa, 0xbb), + makecol(0x77, 0xaa, 0xdd), + makecol(0x77, 0xaa, 0xff), + makecol(0x99, 0xaa, 0x00), + makecol(0x99, 0xaa, 0x33), + makecol(0x99, 0xaa, 0x55), + makecol(0x99, 0xaa, 0x77), + makecol(0x99, 0xaa, 0x99), + makecol(0x99, 0xaa, 0xbb), + makecol(0x99, 0xaa, 0xdd), + makecol(0x99, 0xaa, 0xff), + makecol(0xbb, 0xaa, 0x00), + makecol(0xbb, 0xaa, 0x33), + makecol(0xbb, 0xaa, 0x55), + makecol(0xbb, 0xaa, 0x77), + makecol(0xbb, 0xaa, 0x99), + makecol(0xbb, 0xaa, 0xbb), + makecol(0xbb, 0xaa, 0xdd), + makecol(0xbb, 0xaa, 0xff), + makecol(0xdd, 0xaa, 0x00), + makecol(0xdd, 0xaa, 0x33), + makecol(0xdd, 0xaa, 0x55), + makecol(0xdd, 0xaa, 0x77), + makecol(0xdd, 0xaa, 0x99), + makecol(0xdd, 0xaa, 0xbb), + makecol(0xdd, 0xaa, 0xdd), + makecol(0xdd, 0xaa, 0xff), + makecol(0xff, 0xaa, 0x00), + makecol(0xff, 0xaa, 0x33), + makecol(0xff, 0xaa, 0x55), + makecol(0xff, 0xaa, 0x77), + makecol(0xff, 0xaa, 0x99), + makecol(0xff, 0xaa, 0xbb), + makecol(0xff, 0xaa, 0xdd), + makecol(0xff, 0xaa, 0xee), + makecol(0x00, 0xff, 0x00), + makecol(0x00, 0xff, 0x33), + makecol(0x00, 0xff, 0x55), + makecol(0x00, 0xff, 0x77), + makecol(0x00, 0xff, 0x99), + makecol(0x00, 0xff, 0xbb), + makecol(0x00, 0xff, 0xdd), + makecol(0x00, 0xff, 0xff), + makecol(0x33, 0xff, 0x00), + makecol(0x33, 0xff, 0x33), + makecol(0x33, 0xff, 0x55), + makecol(0x33, 0xff, 0x77), + makecol(0x33, 0xff, 0x99), + makecol(0x33, 0xff, 0xbb), + makecol(0x33, 0xff, 0xdd), + makecol(0x33, 0xff, 0xff), + makecol(0x55, 0xff, 0x00), + makecol(0x55, 0xff, 0x33), + makecol(0x55, 0xff, 0x55), + makecol(0x55, 0xff, 0x77), + makecol(0x55, 0xff, 0x99), + makecol(0x55, 0xff, 0xbb), + makecol(0x55, 0xff, 0xdd), + makecol(0x55, 0xff, 0xff), + makecol(0x77, 0xff, 0x00), + makecol(0x77, 0xff, 0x33), + makecol(0x77, 0xff, 0x55), + makecol(0x77, 0xff, 0x77), + makecol(0x77, 0xff, 0x99), + makecol(0x77, 0xff, 0xbb), + makecol(0x77, 0xff, 0xdd), + makecol(0x77, 0xff, 0xff), + makecol(0x99, 0xff, 0x00), + makecol(0x99, 0xff, 0x33), + makecol(0x99, 0xff, 0x55), + makecol(0x99, 0xff, 0x77), + makecol(0x99, 0xff, 0x99), + makecol(0x99, 0xff, 0xbb), + makecol(0x99, 0xff, 0xdd), + makecol(0x99, 0xff, 0xff), + makecol(0xbb, 0xff, 0x00), + makecol(0xbb, 0xff, 0x33), + makecol(0xbb, 0xff, 0x55), + makecol(0xbb, 0xff, 0x77), + makecol(0xbb, 0xff, 0x99), + makecol(0xbb, 0xff, 0xbb), + makecol(0xbb, 0xff, 0xdd), + makecol(0xbb, 0xff, 0xff), + makecol(0xdd, 0xff, 0x00), + makecol(0xdd, 0xff, 0x33), + makecol(0xdd, 0xff, 0x55), + makecol(0xdd, 0xff, 0x77), + makecol(0xdd, 0xff, 0x99), + makecol(0xdd, 0xff, 0xbb), + makecol(0xdd, 0xff, 0xdd), + makecol(0xdd, 0xff, 0xff), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xff, 0x33), + makecol(0xff, 0xff, 0x55), + makecol(0xff, 0xff, 0x77), + makecol(0xff, 0xff, 0x99), + makecol(0xff, 0xff, 0xbb), + makecol(0xff, 0xff, 0xdd), + makecol(0xff, 0xff, 0xff) +}, +{ + /* Palette 3: 3-2-3 TrueColor. */ + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x33), + makecol(0x00, 0x00, 0x55), + makecol(0x00, 0x00, 0x77), + makecol(0x00, 0x00, 0x99), + makecol(0x00, 0x00, 0xbb), + makecol(0x00, 0x00, 0xdd), + makecol(0x00, 0x00, 0xff), + makecol(0x55, 0x00, 0x00), + makecol(0x55, 0x00, 0x33), + makecol(0x55, 0x00, 0x55), + makecol(0x55, 0x00, 0x77), + makecol(0x55, 0x00, 0x99), + makecol(0x55, 0x00, 0xbb), + makecol(0x55, 0x00, 0xdd), + makecol(0x55, 0x00, 0xff), + makecol(0xaa, 0x00, 0x00), + makecol(0xaa, 0x00, 0x33), + makecol(0xaa, 0x00, 0x55), + makecol(0xaa, 0x00, 0x77), + makecol(0xaa, 0x00, 0x99), + makecol(0xaa, 0x00, 0xbb), + makecol(0xaa, 0x00, 0xdd), + makecol(0xaa, 0x00, 0xff), + makecol(0xff, 0x00, 0x00), + makecol(0xff, 0x00, 0x33), + makecol(0xff, 0x00, 0x55), + makecol(0xff, 0x00, 0x77), + makecol(0xff, 0x00, 0x99), + makecol(0xff, 0x00, 0xbb), + makecol(0xff, 0x00, 0xdd), + makecol(0xff, 0x00, 0xff), + makecol(0x00, 0x33, 0x00), + makecol(0x00, 0x33, 0x33), + makecol(0x00, 0x33, 0x55), + makecol(0x00, 0x33, 0x77), + makecol(0x00, 0x33, 0x99), + makecol(0x00, 0x33, 0xbb), + makecol(0x00, 0x33, 0xdd), + makecol(0x00, 0x33, 0xff), + makecol(0x55, 0x33, 0x00), + makecol(0x55, 0x33, 0x33), + makecol(0x55, 0x33, 0x55), + makecol(0x55, 0x33, 0x77), + makecol(0x55, 0x33, 0x99), + makecol(0x55, 0x33, 0xbb), + makecol(0x55, 0x33, 0xdd), + makecol(0x55, 0x33, 0xff), + makecol(0xaa, 0x33, 0x00), + makecol(0xaa, 0x33, 0x33), + makecol(0xaa, 0x33, 0x55), + makecol(0xaa, 0x33, 0x77), + makecol(0xaa, 0x33, 0x99), + makecol(0xaa, 0x33, 0xbb), + makecol(0xaa, 0x33, 0xdd), + makecol(0xaa, 0x33, 0xff), + makecol(0xff, 0x33, 0x00), + makecol(0xff, 0x33, 0x33), + makecol(0xff, 0x33, 0x55), + makecol(0xff, 0x33, 0x77), + makecol(0xff, 0x33, 0x99), + makecol(0xff, 0x33, 0xbb), + makecol(0xff, 0x33, 0xdd), + makecol(0xff, 0x33, 0xff), + makecol(0x00, 0x55, 0x00), + makecol(0x00, 0x55, 0x33), + makecol(0x00, 0x55, 0x55), + makecol(0x00, 0x55, 0x77), + makecol(0x00, 0x55, 0x99), + makecol(0x00, 0x55, 0xbb), + makecol(0x00, 0x55, 0xdd), + makecol(0x00, 0x55, 0xff), + makecol(0x55, 0x55, 0x00), + makecol(0x55, 0x55, 0x33), + makecol(0x55, 0x55, 0x55), + makecol(0x55, 0x55, 0x77), + makecol(0x55, 0x55, 0x99), + makecol(0x55, 0x55, 0xbb), + makecol(0x55, 0x55, 0xdd), + makecol(0x55, 0x55, 0xff), + makecol(0xaa, 0x55, 0x00), + makecol(0xaa, 0x55, 0x33), + makecol(0xaa, 0x55, 0x55), + makecol(0xaa, 0x55, 0x77), + makecol(0xaa, 0x55, 0x99), + makecol(0xaa, 0x55, 0xbb), + makecol(0xaa, 0x55, 0xdd), + makecol(0xaa, 0x55, 0xff), + makecol(0xff, 0x55, 0x00), + makecol(0xff, 0x55, 0x33), + makecol(0xff, 0x55, 0x55), + makecol(0xff, 0x55, 0x77), + makecol(0xff, 0x55, 0x99), + makecol(0xff, 0x55, 0xbb), + makecol(0xff, 0x55, 0xdd), + makecol(0xff, 0x55, 0xff), + makecol(0x00, 0x77, 0x00), + makecol(0x00, 0x77, 0x33), + makecol(0x00, 0x77, 0x55), + makecol(0x00, 0x77, 0x77), + makecol(0x00, 0x77, 0x99), + makecol(0x00, 0x77, 0xbb), + makecol(0x00, 0x77, 0xdd), + makecol(0x00, 0x77, 0xff), + makecol(0x55, 0x77, 0x00), + makecol(0x55, 0x77, 0x33), + makecol(0x55, 0x77, 0x55), + makecol(0x55, 0x77, 0x77), + makecol(0x55, 0x77, 0x99), + makecol(0x55, 0x77, 0xbb), + makecol(0x55, 0x77, 0xdd), + makecol(0x55, 0x77, 0xff), + makecol(0xaa, 0x77, 0x00), + makecol(0xaa, 0x77, 0x33), + makecol(0xaa, 0x77, 0x55), + makecol(0xaa, 0x77, 0x77), + makecol(0xaa, 0x77, 0x99), + makecol(0xaa, 0x77, 0xbb), + makecol(0xaa, 0x77, 0xdd), + makecol(0xaa, 0x77, 0xff), + makecol(0xff, 0x77, 0x00), + makecol(0xff, 0x77, 0x33), + makecol(0xff, 0x77, 0x55), + makecol(0xff, 0x77, 0x77), + makecol(0xff, 0x77, 0x99), + makecol(0xff, 0x77, 0xbb), + makecol(0xff, 0x77, 0xdd), + makecol(0xff, 0x77, 0xff), + makecol(0x00, 0x99, 0x00), + makecol(0x00, 0x99, 0x33), + makecol(0x00, 0x99, 0x55), + makecol(0x00, 0x99, 0x77), + makecol(0x00, 0x99, 0x99), + makecol(0x00, 0x99, 0xbb), + makecol(0x00, 0x99, 0xdd), + makecol(0x00, 0x99, 0xff), + makecol(0x55, 0x99, 0x00), + makecol(0x55, 0x99, 0x33), + makecol(0x55, 0x99, 0x55), + makecol(0x55, 0x99, 0x77), + makecol(0x55, 0x99, 0x99), + makecol(0x55, 0x99, 0xbb), + makecol(0x55, 0x99, 0xdd), + makecol(0x55, 0x99, 0xff), + makecol(0xaa, 0x99, 0x00), + makecol(0xaa, 0x99, 0x33), + makecol(0xaa, 0x99, 0x55), + makecol(0xaa, 0x99, 0x77), + makecol(0xaa, 0x99, 0x99), + makecol(0xaa, 0x99, 0xbb), + makecol(0xaa, 0x99, 0xdd), + makecol(0xaa, 0x99, 0xff), + makecol(0xff, 0x99, 0x00), + makecol(0xff, 0x99, 0x33), + makecol(0xff, 0x99, 0x55), + makecol(0xff, 0x99, 0x77), + makecol(0xff, 0x99, 0x99), + makecol(0xff, 0x99, 0xbb), + makecol(0xff, 0x99, 0xdd), + makecol(0xff, 0x99, 0xff), + makecol(0x00, 0xbb, 0x00), + makecol(0x00, 0xbb, 0x33), + makecol(0x00, 0xbb, 0x55), + makecol(0x00, 0xbb, 0x77), + makecol(0x00, 0xbb, 0x99), + makecol(0x00, 0xbb, 0xbb), + makecol(0x00, 0xbb, 0xdd), + makecol(0x00, 0xbb, 0xff), + makecol(0x55, 0xbb, 0x00), + makecol(0x55, 0xbb, 0x33), + makecol(0x55, 0xbb, 0x55), + makecol(0x55, 0xbb, 0x77), + makecol(0x55, 0xbb, 0x99), + makecol(0x55, 0xbb, 0xbb), + makecol(0x55, 0xbb, 0xdd), + makecol(0x55, 0xbb, 0xff), + makecol(0xaa, 0xbb, 0x00), + makecol(0xaa, 0xbb, 0x33), + makecol(0xaa, 0xbb, 0x55), + makecol(0xaa, 0xbb, 0x77), + makecol(0xaa, 0xbb, 0x99), + makecol(0xaa, 0xbb, 0xbb), + makecol(0xaa, 0xbb, 0xdd), + makecol(0xaa, 0xbb, 0xff), + makecol(0xff, 0xbb, 0x00), + makecol(0xff, 0xbb, 0x33), + makecol(0xff, 0xbb, 0x55), + makecol(0xff, 0xbb, 0x77), + makecol(0xff, 0xbb, 0x99), + makecol(0xff, 0xbb, 0xbb), + makecol(0xff, 0xbb, 0xdd), + makecol(0xff, 0xbb, 0xff), + makecol(0x00, 0xdd, 0x00), + makecol(0x00, 0xdd, 0x33), + makecol(0x00, 0xdd, 0x55), + makecol(0x00, 0xdd, 0x77), + makecol(0x00, 0xdd, 0x99), + makecol(0x00, 0xdd, 0xbb), + makecol(0x00, 0xdd, 0xdd), + makecol(0x00, 0xdd, 0xff), + makecol(0x55, 0xdd, 0x00), + makecol(0x55, 0xdd, 0x33), + makecol(0x55, 0xdd, 0x55), + makecol(0x55, 0xdd, 0x77), + makecol(0x55, 0xdd, 0x99), + makecol(0x55, 0xdd, 0xbb), + makecol(0x55, 0xdd, 0xdd), + makecol(0x55, 0xdd, 0xff), + makecol(0xaa, 0xdd, 0x00), + makecol(0xaa, 0xdd, 0x33), + makecol(0xaa, 0xdd, 0x55), + makecol(0xaa, 0xdd, 0x77), + makecol(0xaa, 0xdd, 0x99), + makecol(0xaa, 0xdd, 0xbb), + makecol(0xaa, 0xdd, 0xdd), + makecol(0xaa, 0xdd, 0xff), + makecol(0xff, 0xdd, 0x00), + makecol(0xff, 0xdd, 0x33), + makecol(0xff, 0xdd, 0x55), + makecol(0xff, 0xdd, 0x77), + makecol(0xff, 0xdd, 0x99), + makecol(0xff, 0xdd, 0xbb), + makecol(0xff, 0xdd, 0xdd), + makecol(0xff, 0xdd, 0xff), + makecol(0x00, 0xff, 0x00), + makecol(0x00, 0xff, 0x33), + makecol(0x00, 0xff, 0x55), + makecol(0x00, 0xff, 0x77), + makecol(0x00, 0xff, 0x99), + makecol(0x00, 0xff, 0xbb), + makecol(0x00, 0xff, 0xdd), + makecol(0x00, 0xff, 0xff), + makecol(0x55, 0xff, 0x00), + makecol(0x55, 0xff, 0x33), + makecol(0x55, 0xff, 0x55), + makecol(0x55, 0xff, 0x77), + makecol(0x55, 0xff, 0x99), + makecol(0x55, 0xff, 0xbb), + makecol(0x55, 0xff, 0xdd), + makecol(0x55, 0xff, 0xff), + makecol(0xaa, 0xff, 0x00), + makecol(0xaa, 0xff, 0x33), + makecol(0xaa, 0xff, 0x55), + makecol(0xaa, 0xff, 0x77), + makecol(0xaa, 0xff, 0x99), + makecol(0xaa, 0xff, 0xbb), + makecol(0xaa, 0xff, 0xdd), + makecol(0xaa, 0xff, 0xff), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xff, 0x33), + makecol(0xff, 0xff, 0x55), + makecol(0xff, 0xff, 0x77), + makecol(0xff, 0xff, 0x99), + makecol(0xff, 0xff, 0xbb), + makecol(0xff, 0xff, 0xdd), + makecol(0xff, 0xff, 0xff) +}, +{ + /* Palette 4: 3-3-2 TrueColor. */ + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x55), + makecol(0x00, 0x00, 0xaa), + makecol(0x00, 0x00, 0xff), + makecol(0x00, 0x33, 0x00), + makecol(0x00, 0x33, 0x55), + makecol(0x00, 0x33, 0xaa), + makecol(0x00, 0x33, 0xff), + makecol(0x00, 0x55, 0x00), + makecol(0x00, 0x55, 0x55), + makecol(0x00, 0x55, 0xaa), + makecol(0x00, 0x55, 0xff), + makecol(0x00, 0x77, 0x00), + makecol(0x00, 0x77, 0x55), + makecol(0x00, 0x77, 0xaa), + makecol(0x00, 0x77, 0xff), + makecol(0x00, 0x99, 0x00), + makecol(0x00, 0x99, 0x55), + makecol(0x00, 0x99, 0xaa), + makecol(0x00, 0x99, 0xff), + makecol(0x00, 0xbb, 0x00), + makecol(0x00, 0xbb, 0x55), + makecol(0x00, 0xbb, 0xaa), + makecol(0x00, 0xbb, 0xff), + makecol(0x00, 0xdd, 0x00), + makecol(0x00, 0xdd, 0x55), + makecol(0x00, 0xdd, 0xaa), + makecol(0x00, 0xdd, 0xff), + makecol(0x00, 0xff, 0x00), + makecol(0x00, 0xff, 0x55), + makecol(0x00, 0xff, 0xaa), + makecol(0x00, 0xff, 0xff), + makecol(0x33, 0x00, 0x00), + makecol(0x33, 0x00, 0x55), + makecol(0x33, 0x00, 0xaa), + makecol(0x33, 0x00, 0xff), + makecol(0x33, 0x33, 0x00), + makecol(0x33, 0x33, 0x55), + makecol(0x33, 0x33, 0xaa), + makecol(0x33, 0x33, 0xff), + makecol(0x33, 0x55, 0x00), + makecol(0x33, 0x55, 0x55), + makecol(0x33, 0x55, 0xaa), + makecol(0x33, 0x55, 0xff), + makecol(0x33, 0x77, 0x00), + makecol(0x33, 0x77, 0x55), + makecol(0x33, 0x77, 0xaa), + makecol(0x33, 0x77, 0xff), + makecol(0x33, 0x99, 0x00), + makecol(0x33, 0x99, 0x55), + makecol(0x33, 0x99, 0xaa), + makecol(0x33, 0x99, 0xff), + makecol(0x33, 0xbb, 0x00), + makecol(0x33, 0xbb, 0x55), + makecol(0x33, 0xbb, 0xaa), + makecol(0x33, 0xbb, 0xff), + makecol(0x33, 0xdd, 0x00), + makecol(0x33, 0xdd, 0x55), + makecol(0x33, 0xdd, 0xaa), + makecol(0x33, 0xdd, 0xff), + makecol(0x33, 0xff, 0x00), + makecol(0x33, 0xff, 0x55), + makecol(0x33, 0xff, 0xaa), + makecol(0x33, 0xff, 0xff), + makecol(0x55, 0x00, 0x00), + makecol(0x55, 0x00, 0x55), + makecol(0x55, 0x00, 0xaa), + makecol(0x55, 0x00, 0xff), + makecol(0x55, 0x33, 0x00), + makecol(0x55, 0x33, 0x55), + makecol(0x55, 0x33, 0xaa), + makecol(0x55, 0x33, 0xff), + makecol(0x55, 0x55, 0x00), + makecol(0x55, 0x55, 0x55), + makecol(0x55, 0x55, 0xaa), + makecol(0x55, 0x55, 0xff), + makecol(0x55, 0x77, 0x00), + makecol(0x55, 0x77, 0x55), + makecol(0x55, 0x77, 0xaa), + makecol(0x55, 0x77, 0xff), + makecol(0x55, 0x99, 0x00), + makecol(0x55, 0x99, 0x55), + makecol(0x55, 0x99, 0xaa), + makecol(0x55, 0x99, 0xff), + makecol(0x55, 0xbb, 0x00), + makecol(0x55, 0xbb, 0x55), + makecol(0x55, 0xbb, 0xaa), + makecol(0x55, 0xbb, 0xff), + makecol(0x55, 0xdd, 0x00), + makecol(0x55, 0xdd, 0x55), + makecol(0x55, 0xdd, 0xaa), + makecol(0x55, 0xdd, 0xff), + makecol(0x55, 0xff, 0x00), + makecol(0x55, 0xff, 0x55), + makecol(0x55, 0xff, 0xaa), + makecol(0x55, 0xff, 0xff), + makecol(0x77, 0x00, 0x00), + makecol(0x77, 0x00, 0x55), + makecol(0x77, 0x00, 0xaa), + makecol(0x77, 0x00, 0xff), + makecol(0x77, 0x33, 0x00), + makecol(0x77, 0x33, 0x55), + makecol(0x77, 0x33, 0xaa), + makecol(0x77, 0x33, 0xff), + makecol(0x77, 0x55, 0x00), + makecol(0x77, 0x55, 0x55), + makecol(0x77, 0x55, 0xaa), + makecol(0x77, 0x55, 0xff), + makecol(0x77, 0x77, 0x00), + makecol(0x77, 0x77, 0x55), + makecol(0x77, 0x77, 0xaa), + makecol(0x77, 0x77, 0xff), + makecol(0x77, 0x99, 0x00), + makecol(0x77, 0x99, 0x55), + makecol(0x77, 0x99, 0xaa), + makecol(0x77, 0x99, 0xff), + makecol(0x77, 0xbb, 0x00), + makecol(0x77, 0xbb, 0x55), + makecol(0x77, 0xbb, 0xaa), + makecol(0x77, 0xbb, 0xff), + makecol(0x77, 0xdd, 0x00), + makecol(0x77, 0xdd, 0x55), + makecol(0x77, 0xdd, 0xaa), + makecol(0x77, 0xdd, 0xff), + makecol(0x77, 0xff, 0x00), + makecol(0x77, 0xff, 0x55), + makecol(0x77, 0xff, 0xaa), + makecol(0x77, 0xff, 0xff), + makecol(0x99, 0x00, 0x00), + makecol(0x99, 0x00, 0x55), + makecol(0x99, 0x00, 0xaa), + makecol(0x99, 0x00, 0xff), + makecol(0x99, 0x33, 0x00), + makecol(0x99, 0x33, 0x55), + makecol(0x99, 0x33, 0xaa), + makecol(0x99, 0x33, 0xff), + makecol(0x99, 0x55, 0x00), + makecol(0x99, 0x55, 0x55), + makecol(0x99, 0x55, 0xaa), + makecol(0x99, 0x55, 0xff), + makecol(0x99, 0x77, 0x00), + makecol(0x99, 0x77, 0x55), + makecol(0x99, 0x77, 0xaa), + makecol(0x99, 0x77, 0xff), + makecol(0x99, 0x99, 0x00), + makecol(0x99, 0x99, 0x55), + makecol(0x99, 0x99, 0xaa), + makecol(0x99, 0x99, 0xff), + makecol(0x99, 0xbb, 0x00), + makecol(0x99, 0xbb, 0x55), + makecol(0x99, 0xbb, 0xaa), + makecol(0x99, 0xbb, 0xff), + makecol(0x99, 0xdd, 0x00), + makecol(0x99, 0xdd, 0x55), + makecol(0x99, 0xdd, 0xaa), + makecol(0x99, 0xdd, 0xff), + makecol(0x99, 0xff, 0x00), + makecol(0x99, 0xff, 0x55), + makecol(0x99, 0xff, 0xaa), + makecol(0x99, 0xff, 0xff), + makecol(0xbb, 0x00, 0x00), + makecol(0xbb, 0x00, 0x55), + makecol(0xbb, 0x00, 0xaa), + makecol(0xbb, 0x00, 0xff), + makecol(0xbb, 0x33, 0x00), + makecol(0xbb, 0x33, 0x55), + makecol(0xbb, 0x33, 0xaa), + makecol(0xbb, 0x33, 0xff), + makecol(0xbb, 0x55, 0x00), + makecol(0xbb, 0x55, 0x55), + makecol(0xbb, 0x55, 0xaa), + makecol(0xbb, 0x55, 0xff), + makecol(0xbb, 0x77, 0x00), + makecol(0xbb, 0x77, 0x55), + makecol(0xbb, 0x77, 0xaa), + makecol(0xbb, 0x77, 0xff), + makecol(0xbb, 0x99, 0x00), + makecol(0xbb, 0x99, 0x55), + makecol(0xbb, 0x99, 0xaa), + makecol(0xbb, 0x99, 0xff), + makecol(0xbb, 0xbb, 0x00), + makecol(0xbb, 0xbb, 0x55), + makecol(0xbb, 0xbb, 0xaa), + makecol(0xbb, 0xbb, 0xff), + makecol(0xbb, 0xdd, 0x00), + makecol(0xbb, 0xdd, 0x55), + makecol(0xbb, 0xdd, 0xaa), + makecol(0xbb, 0xdd, 0xff), + makecol(0xbb, 0xff, 0x00), + makecol(0xbb, 0xff, 0x55), + makecol(0xbb, 0xff, 0xaa), + makecol(0xbb, 0xff, 0xff), + makecol(0xdd, 0x00, 0x00), + makecol(0xdd, 0x00, 0x55), + makecol(0xdd, 0x00, 0xaa), + makecol(0xdd, 0x00, 0xff), + makecol(0xdd, 0x33, 0x00), + makecol(0xdd, 0x33, 0x55), + makecol(0xdd, 0x33, 0xaa), + makecol(0xdd, 0x33, 0xff), + makecol(0xdd, 0x55, 0x00), + makecol(0xdd, 0x55, 0x55), + makecol(0xdd, 0x55, 0xaa), + makecol(0xdd, 0x55, 0xff), + makecol(0xdd, 0x77, 0x00), + makecol(0xdd, 0x77, 0x55), + makecol(0xdd, 0x77, 0xaa), + makecol(0xdd, 0x77, 0xff), + makecol(0xdd, 0x99, 0x00), + makecol(0xdd, 0x99, 0x55), + makecol(0xdd, 0x99, 0xaa), + makecol(0xdd, 0x99, 0xff), + makecol(0xdd, 0xbb, 0x00), + makecol(0xdd, 0xbb, 0x55), + makecol(0xdd, 0xbb, 0xaa), + makecol(0xdd, 0xbb, 0xff), + makecol(0xdd, 0xdd, 0x00), + makecol(0xdd, 0xdd, 0x55), + makecol(0xdd, 0xdd, 0xaa), + makecol(0xdd, 0xdd, 0xff), + makecol(0xdd, 0xff, 0x00), + makecol(0xdd, 0xff, 0x55), + makecol(0xdd, 0xff, 0xaa), + makecol(0xdd, 0xff, 0xff), + makecol(0xff, 0x00, 0x00), + makecol(0xff, 0x00, 0x55), + makecol(0xff, 0x00, 0xaa), + makecol(0xff, 0x00, 0xff), + makecol(0xff, 0x33, 0x00), + makecol(0xff, 0x33, 0x55), + makecol(0xff, 0x33, 0xaa), + makecol(0xff, 0x33, 0xff), + makecol(0xff, 0x55, 0x00), + makecol(0xff, 0x55, 0x55), + makecol(0xff, 0x55, 0xaa), + makecol(0xff, 0x55, 0xff), + makecol(0xff, 0x77, 0x00), + makecol(0xff, 0x77, 0x55), + makecol(0xff, 0x77, 0xaa), + makecol(0xff, 0x77, 0xff), + makecol(0xff, 0x99, 0x00), + makecol(0xff, 0x99, 0x55), + makecol(0xff, 0x99, 0xaa), + makecol(0xff, 0x99, 0xff), + makecol(0xff, 0xbb, 0x00), + makecol(0xff, 0xbb, 0x55), + makecol(0xff, 0xbb, 0xaa), + makecol(0xff, 0xbb, 0xff), + makecol(0xff, 0xdd, 0x00), + makecol(0xff, 0xdd, 0x55), + makecol(0xff, 0xdd, 0xaa), + makecol(0xff, 0xdd, 0xff), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xff, 0x55), + makecol(0xff, 0xff, 0xaa), + makecol(0xff, 0xff, 0xff) +}, +{ + /* Palette 5: 6x6x6 color cube. */ + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x33), + makecol(0x00, 0x00, 0x66), + makecol(0x00, 0x00, 0x99), + makecol(0x00, 0x00, 0xcc), + makecol(0x00, 0x00, 0xff), + makecol(0x33, 0x00, 0x00), + makecol(0x33, 0x00, 0x33), + makecol(0x33, 0x00, 0x66), + makecol(0x33, 0x00, 0x99), + makecol(0x33, 0x00, 0xcc), + makecol(0x33, 0x00, 0xff), + makecol(0x66, 0x00, 0x00), + makecol(0x66, 0x00, 0x33), + makecol(0x66, 0x00, 0x66), + makecol(0x66, 0x00, 0x99), + makecol(0x66, 0x00, 0xcc), + makecol(0x66, 0x00, 0xff), + makecol(0x99, 0x00, 0x00), + makecol(0x99, 0x00, 0x33), + makecol(0x99, 0x00, 0x66), + makecol(0x99, 0x00, 0x99), + makecol(0x99, 0x00, 0xcc), + makecol(0x99, 0x00, 0xff), + makecol(0xcc, 0x00, 0x00), + makecol(0xcc, 0x00, 0x33), + makecol(0xcc, 0x00, 0x66), + makecol(0xcc, 0x00, 0x99), + makecol(0xcc, 0x00, 0xcc), + makecol(0xcc, 0x00, 0xff), + makecol(0xff, 0x00, 0x00), + makecol(0xff, 0x00, 0x33), + makecol(0xff, 0x00, 0x66), + makecol(0xff, 0x00, 0x99), + makecol(0xff, 0x00, 0xcc), + makecol(0xff, 0x00, 0xff), + makecol(0x00, 0x33, 0x00), + makecol(0x00, 0x33, 0x33), + makecol(0x00, 0x33, 0x66), + makecol(0x00, 0x33, 0x99), + makecol(0x00, 0x33, 0xcc), + makecol(0x00, 0x33, 0xff), + makecol(0x33, 0x33, 0x00), + makecol(0x33, 0x33, 0x33), + makecol(0x33, 0x33, 0x66), + makecol(0x33, 0x33, 0x99), + makecol(0x33, 0x33, 0xcc), + makecol(0x33, 0x33, 0xff), + makecol(0x66, 0x33, 0x00), + makecol(0x66, 0x33, 0x33), + makecol(0x66, 0x33, 0x66), + makecol(0x66, 0x33, 0x99), + makecol(0x66, 0x33, 0xcc), + makecol(0x66, 0x33, 0xff), + makecol(0x99, 0x33, 0x00), + makecol(0x99, 0x33, 0x33), + makecol(0x99, 0x33, 0x66), + makecol(0x99, 0x33, 0x99), + makecol(0x99, 0x33, 0xcc), + makecol(0x99, 0x33, 0xff), + makecol(0xcc, 0x33, 0x00), + makecol(0xcc, 0x33, 0x33), + makecol(0xcc, 0x33, 0x66), + makecol(0xcc, 0x33, 0x99), + makecol(0xcc, 0x33, 0xcc), + makecol(0xcc, 0x33, 0xff), + makecol(0xff, 0x33, 0x00), + makecol(0xff, 0x33, 0x33), + makecol(0xff, 0x33, 0x66), + makecol(0xff, 0x33, 0x99), + makecol(0xff, 0x33, 0xcc), + makecol(0xff, 0x33, 0xff), + makecol(0x00, 0x66, 0x00), + makecol(0x00, 0x66, 0x33), + makecol(0x00, 0x66, 0x66), + makecol(0x00, 0x66, 0x99), + makecol(0x00, 0x66, 0xcc), + makecol(0x00, 0x66, 0xff), + makecol(0x33, 0x66, 0x00), + makecol(0x33, 0x66, 0x33), + makecol(0x33, 0x66, 0x66), + makecol(0x33, 0x66, 0x99), + makecol(0x33, 0x66, 0xcc), + makecol(0x33, 0x66, 0xff), + makecol(0x66, 0x66, 0x00), + makecol(0x66, 0x66, 0x33), + makecol(0x66, 0x66, 0x66), + makecol(0x66, 0x66, 0x99), + makecol(0x66, 0x66, 0xcc), + makecol(0x66, 0x66, 0xff), + makecol(0x99, 0x66, 0x00), + makecol(0x99, 0x66, 0x33), + makecol(0x99, 0x66, 0x66), + makecol(0x99, 0x66, 0x99), + makecol(0x99, 0x66, 0xcc), + makecol(0x99, 0x66, 0xff), + makecol(0xcc, 0x66, 0x00), + makecol(0xcc, 0x66, 0x33), + makecol(0xcc, 0x66, 0x66), + makecol(0xcc, 0x66, 0x99), + makecol(0xcc, 0x66, 0xcc), + makecol(0xcc, 0x66, 0xff), + makecol(0xff, 0x66, 0x00), + makecol(0xff, 0x66, 0x33), + makecol(0xff, 0x66, 0x66), + makecol(0xff, 0x66, 0x99), + makecol(0xff, 0x66, 0xcc), + makecol(0xff, 0x66, 0xff), + makecol(0x00, 0x99, 0x00), + makecol(0x00, 0x99, 0x33), + makecol(0x00, 0x99, 0x66), + makecol(0x00, 0x99, 0x99), + makecol(0x00, 0x99, 0xcc), + makecol(0x00, 0x99, 0xff), + makecol(0x33, 0x99, 0x00), + makecol(0x33, 0x99, 0x33), + makecol(0x33, 0x99, 0x66), + makecol(0x33, 0x99, 0x99), + makecol(0x33, 0x99, 0xcc), + makecol(0x33, 0x99, 0xff), + makecol(0x66, 0x99, 0x00), + makecol(0x66, 0x99, 0x33), + makecol(0x66, 0x99, 0x66), + makecol(0x66, 0x99, 0x99), + makecol(0x66, 0x99, 0xcc), + makecol(0x66, 0x99, 0xff), + makecol(0x99, 0x99, 0x00), + makecol(0x99, 0x99, 0x33), + makecol(0x99, 0x99, 0x66), + makecol(0x99, 0x99, 0x99), + makecol(0x99, 0x99, 0xcc), + makecol(0x99, 0x99, 0xff), + makecol(0xcc, 0x99, 0x00), + makecol(0xcc, 0x99, 0x33), + makecol(0xcc, 0x99, 0x66), + makecol(0xcc, 0x99, 0x99), + makecol(0xcc, 0x99, 0xcc), + makecol(0xcc, 0x99, 0xff), + makecol(0xff, 0x99, 0x00), + makecol(0xff, 0x99, 0x33), + makecol(0xff, 0x99, 0x66), + makecol(0xff, 0x99, 0x99), + makecol(0xff, 0x99, 0xcc), + makecol(0xff, 0x99, 0xff), + makecol(0x00, 0xcc, 0x00), + makecol(0x00, 0xcc, 0x33), + makecol(0x00, 0xcc, 0x66), + makecol(0x00, 0xcc, 0x99), + makecol(0x00, 0xcc, 0xcc), + makecol(0x00, 0xcc, 0xff), + makecol(0x33, 0xcc, 0x00), + makecol(0x33, 0xcc, 0x33), + makecol(0x33, 0xcc, 0x66), + makecol(0x33, 0xcc, 0x99), + makecol(0x33, 0xcc, 0xcc), + makecol(0x33, 0xcc, 0xff), + makecol(0x66, 0xcc, 0x00), + makecol(0x66, 0xcc, 0x33), + makecol(0x66, 0xcc, 0x66), + makecol(0x66, 0xcc, 0x99), + makecol(0x66, 0xcc, 0xcc), + makecol(0x66, 0xcc, 0xff), + makecol(0x99, 0xcc, 0x00), + makecol(0x99, 0xcc, 0x33), + makecol(0x99, 0xcc, 0x66), + makecol(0x99, 0xcc, 0x99), + makecol(0x99, 0xcc, 0xcc), + makecol(0x99, 0xcc, 0xff), + makecol(0xcc, 0xcc, 0x00), + makecol(0xcc, 0xcc, 0x33), + makecol(0xcc, 0xcc, 0x66), + makecol(0xcc, 0xcc, 0x99), + makecol(0xcc, 0xcc, 0xcc), + makecol(0xcc, 0xcc, 0xff), + makecol(0xff, 0xcc, 0x00), + makecol(0xff, 0xcc, 0x33), + makecol(0xff, 0xcc, 0x66), + makecol(0xff, 0xcc, 0x99), + makecol(0xff, 0xcc, 0xcc), + makecol(0xff, 0xcc, 0xff), + makecol(0x00, 0xff, 0x00), + makecol(0x00, 0xff, 0x33), + makecol(0x00, 0xff, 0x66), + makecol(0x00, 0xff, 0x99), + makecol(0x00, 0xff, 0xcc), + makecol(0x00, 0xff, 0xff), + makecol(0x33, 0xff, 0x00), + makecol(0x33, 0xff, 0x33), + makecol(0x33, 0xff, 0x66), + makecol(0x33, 0xff, 0x99), + makecol(0x33, 0xff, 0xcc), + makecol(0x33, 0xff, 0xff), + makecol(0x66, 0xff, 0x00), + makecol(0x66, 0xff, 0x33), + makecol(0x66, 0xff, 0x66), + makecol(0x66, 0xff, 0x99), + makecol(0x66, 0xff, 0xcc), + makecol(0x66, 0xff, 0xff), + makecol(0x99, 0xff, 0x00), + makecol(0x99, 0xff, 0x33), + makecol(0x99, 0xff, 0x66), + makecol(0x99, 0xff, 0x99), + makecol(0x99, 0xff, 0xcc), + makecol(0x99, 0xff, 0xff), + makecol(0xcc, 0xff, 0x00), + makecol(0xcc, 0xff, 0x33), + makecol(0xcc, 0xff, 0x66), + makecol(0xcc, 0xff, 0x99), + makecol(0xcc, 0xff, 0xcc), + makecol(0xcc, 0xff, 0xff), + makecol(0xff, 0xff, 0x00), + makecol(0xff, 0xff, 0x33), + makecol(0xff, 0xff, 0x66), + makecol(0xff, 0xff, 0x99), + makecol(0xff, 0xff, 0xcc), + makecol(0xff, 0xff, 0xff), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00), + makecol(0x00, 0x00, 0x00) +} + + +#endif /*VID_PGC_PALETTE_H*/ diff --git a/src/devices/video/video.h b/src/devices/video/video.h index ec4d798..b069762 100644 --- a/src/devices/video/video.h +++ b/src/devices/video/video.h @@ -8,13 +8,13 @@ * * Definitions for the video controller module. * - * Version: @(#)video.h 1.0.25 2019/02/10 + * Version: @(#)video.h 1.0.26 2019/03/01 * * Authors: Fred N. van Kempen, * Miran Grca, * Sarah Walker, * - * Copyright 2017,2018 Fred N. van Kempen. + * Copyright 2017-2019 Fred N. van Kempen. * Copyright 2016-2018 Miran Grca. * Copyright 2008-2018 Sarah Walker. * @@ -169,6 +169,9 @@ extern const device_t mach64vt2_device; /* IBM CGA and compatibles. */ extern const device_t cga_device; +/* IBM PGC. */ +extern const device_t pgc_device; + /* Cirrus Logic GD-series cards. */ #if defined(DEV_BRANCH) extern const device_t gd5402_isa_device; @@ -217,6 +220,9 @@ extern const device_t et4000w32p_cardex_pci_device; /* MDSI Genius VHR card. */ extern const device_t genius_device; +/* ImageManager 1024 card. */ +extern const device_t im1024_device; + /* Hercules series cards and compatibles. */ extern const device_t hercules_device; diff --git a/src/devices/video/video_dev.c b/src/devices/video/video_dev.c index 004915c..204aed9 100644 --- a/src/devices/video/video_dev.c +++ b/src/devices/video/video_dev.c @@ -12,7 +12,7 @@ * "extern" reference to its device into the video.h file, * and add an entry for it into the table here. * - * Version: @(#)video_dev.c 1.0.30 2019/02/10 + * Version: @(#)video_dev.c 1.0.31 2019/03/01 * * Authors: Fred N. van Kempen, * Miran Grca, @@ -102,6 +102,10 @@ static const struct { { "hercules_plus", &herculesplus_device }, { "incolor", &incolor_device }, { "genius", &genius_device }, +#if 0 + { "pgc", &pgc_device }, +#endif + { "im1024", &im1024_device }, { "oti037c", &oti037c_device }, { "oti067", &oti067_device }, { "oti077", &oti077_device }, diff --git a/src/win/mingw/Makefile.MinGW b/src/win/mingw/Makefile.MinGW index d9d808e..65b0cfa 100644 --- a/src/win/mingw/Makefile.MinGW +++ b/src/win/mingw/Makefile.MinGW @@ -8,7 +8,7 @@ # # Makefile for Windows systems using the MinGW32 environment. # -# Version: @(#)Makefile.mingw 1.0.76 2019/02/13 +# Version: @(#)Makefile.mingw 1.0.77 2019/03/01 # # Author: Fred N. van Kempen, # @@ -717,6 +717,7 @@ VIDOBJ := video.o \ vid_hercules.o vid_herculesplus.o vid_incolor.o \ vid_colorplus.o \ vid_genius.o \ + vid_pgc.o vid_im1024.o \ vid_sigma.o \ vid_wy700.o \ vid_ega.o vid_ega_render.o \ diff --git a/src/win/msvc/Makefile.VC b/src/win/msvc/Makefile.VC index 7f84be8..af3f575 100644 --- a/src/win/msvc/Makefile.VC +++ b/src/win/msvc/Makefile.VC @@ -8,7 +8,7 @@ # # Makefile for Windows using Visual Studio 2015. # -# Version: @(#)Makefile.VC 1.0.62 2019/02/13 +# Version: @(#)Makefile.VC 1.0.63 2019/03/01 # # Author: Fred N. van Kempen, # @@ -693,6 +693,7 @@ VIDOBJ := video.obj \ vid_hercules.obj vid_herculesplus.obj vid_incolor.obj \ vid_colorplus.obj \ vid_genius.obj \ + vid_pgc.obj vid_im1024.obj \ vid_sigma.obj \ vid_wy700.obj \ vid_ega.obj vid_ega_render.obj \