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 \