From a26e0094d06ba708e6df9af4f9561384342c4b30 Mon Sep 17 00:00:00 2001 From: waltje Date: Sun, 3 Mar 2019 03:52:09 -0500 Subject: [PATCH] Updates to clean up and fix the IM-1024 driver. --- src/devices/video/vid_im1024.c | 124 +- src/devices/video/vid_pgc.c | 2291 ++++++++++++++++---------------- src/devices/video/vid_pgc.h | 39 +- src/devices/video/video.c | 35 +- src/devices/video/video_dev.c | 10 +- 5 files changed, 1287 insertions(+), 1212 deletions(-) diff --git a/src/devices/video/vid_im1024.c b/src/devices/video/vid_im1024.c index 6fab2aa..1513167 100644 --- a/src/devices/video/vid_im1024.c +++ b/src/devices/video/vid_im1024.c @@ -38,7 +38,7 @@ * 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 + * Version: @(#)vid_im1024.c 1.0.2 2019/03/02 * * Authors: Fred N. van Kempen, * John Elliott, @@ -100,7 +100,7 @@ static void fifo_write(im1024_t *dev, uint8_t val) { #if 0 - DEBUG(("fifo_write: %02x [rd=%04x wr=%04x]\n", + DEBUG(("IM1024: fifo_write: %02x [rd=%04x wr=%04x]\n", val, dev->fifo_rdptr, dev->fifo_wrptr)); #endif @@ -108,7 +108,8 @@ fifo_write(im1024_t *dev, uint8_t val) /* FIFO is full. Double its size. */ uint8_t *buf; - DEBUG("fifo_resize: %d to %d\n", dev->fifo_len, 2 * dev->fifo_len); + DEBUG("IM1024: fifo_resize: %i to %i\n", + dev->fifo_len, 2 * dev->fifo_len); buf = realloc(dev->fifo, 2 * dev->fifo_len); if (buf == NULL) return; @@ -141,7 +142,7 @@ fifo_read(im1024_t *dev) if (dev->fifo_rdptr >= dev->fifo_len) dev->fifo_rdptr = 0; - DBGLOG(1, "fifo_read: %02x\n", ret); + DBGLOG(1, "IM1024: fifo_read: %02x\n", ret); return(ret); } @@ -162,17 +163,17 @@ input_byte(pgc_t *pgc, uint8_t *result) pgc_sleep(pgc); } - if (pgc->mapram[0x3ff]) { /* Reset triggered */ + 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 { + pgc->mapram[0x301]++; + } else *result = fifo_read(dev); - } return(1); } @@ -225,7 +226,7 @@ im1024_write(uint32_t addr, uint8_t val, void *priv) if (addr >= 0xC6000 && addr < 0xC6100 && dev->pgc.mapram[0x330] == 1) { fifo_write(dev, val); - DBGLOG(1, "im1024_write(%02x)\n", val); + DBGLOG(1, "IM1024: write(%02x)\n", val); if (dev->pgc.waiting_input_fifo) { dev->pgc.waiting_input_fifo = 0; @@ -254,7 +255,7 @@ hndl_imgsiz(pgc_t *pgc) 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); + DEBUG("IM1024: IMGSIZ %i,%i,%i,%i\n", w, h, a, b); } @@ -270,7 +271,30 @@ hndl_iprec(pgc_t *pgc) if (! pgc_param_byte(pgc, ¶m)) return; - DEBUG("IPREC %d\n", param); + DEBUG("IM1024: IPREC %i\n", param); +} + + +/* + * Set drawing mode. + * + * 0 => Draw + * 1 => Invert + * 2 => XOR (IM-1024) + * 3 => AND (IM-1024) + */ +static void +hndl_linfun(pgc_t *pgc) +{ + uint8_t param; + + if (! pgc_param_byte(pgc, ¶m)) return; + + if (param < 4) { + pgc->draw_mode = param; + DEBUG("IM1024: LINFUN(%i)\n", param); + } else + pgc_error(pgc, PGC_ERROR_RANGE); } @@ -284,7 +308,7 @@ hndl_pan(pgc_t *pgc) if (! pgc_param_word(pgc, &x)) return; if (! pgc_param_word(pgc, &y)) return; - DEBUG("PAN %d,%d\n", x, y); + DEBUG("IM1024: PAN %i,%i\n", x, y); pgc->pan_x = x; pgc->pan_y = y; @@ -302,11 +326,11 @@ hndl_pline(pgc_t *pgc) if (! pgc_param_byte(pgc, &count)) return; - DEBUG("PLINE (%d) ", count); + DEBUG("IM1024: PLINE (%i) ", count); for (n = 0; n < count; n++) { if (! pgc_param_word(pgc, &x[n])) return; if (! pgc_param_word(pgc, &y[n])) return; - DEBUG(" (%d,%d)\n", x[n], y[n]); + DEBUG(" (%i,%i)\n", x[n], y[n]); } for (n = 1; n < count; n++) { @@ -374,7 +398,7 @@ hndl_blkmov(pgc_t *pgc) 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); + DEBUG("IM1024: BLKMOV %i,%i,%i,%i,%i,%i\n", x0, y0, x1, y1, x2, y2); /* Disable clipping. */ PUSHCLIP @@ -404,7 +428,8 @@ hndl_ellipse(pgc_t *pgc) 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); + DEBUG("IM1024: ELLIPSE %i,%i @ %i,%i\n", + x, y, pgc->x >> 16, pgc->y >> 16); pgc_draw_ellipse(pgc, x << 16, y << 16); } @@ -423,7 +448,7 @@ hndl_move(pgc_t *pgc) pgc->x = x << 16; pgc->y = y << 16; - DEBUG("MOVE %d,%d\n", x, y); + DEBUG("IM1024: MOVE %i,%i\n", x, y); } @@ -437,7 +462,7 @@ hndl_draw(pgc_t *pgc) 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); + DEBUG("IM1024: DRAW %i,%i to %i,%i\n", pgc->x >> 16, pgc->y >> 16, x, y); pgc_draw_line(pgc, pgc->x, pgc->y, x << 16, y << 16, pgc->line_pattern); pgc->x = x << 16; @@ -461,7 +486,7 @@ hndl_poly(pgc_t *pgc) y = (int32_t *)mem_alloc(as * sizeof(int32_t)); if (!x || !y) { - DEBUG("hndl_poly: malloc failed\n"); + DEBUG("IM1024: POLY: out of memory\n"); return; } @@ -472,7 +497,7 @@ hndl_poly(pgc_t *pgc) 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"); + DEBUG("IM1024: poly: realloc failed\n"); break; } x = nx; @@ -500,7 +525,7 @@ hndl_poly(pgc_t *pgc) 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"); + DEBUG("IM1024: POLY continues!\n"); parsing = 1; /* Swallow the POLY. */ @@ -508,7 +533,7 @@ hndl_poly(pgc_t *pgc) } }; - DEBUG("POLY (%i) fill_mode=%d\n", realcount, pgc->fill_mode); + DEBUG("IM1024: POLY (%i) fill_mode=%i\n", realcount, pgc->fill_mode); for (n = 0; n < realcount; n++) { DEBUG(" (%i,%i)\n", x[n] >> 16, y[n] >> 16); } @@ -532,17 +557,17 @@ parse_poly(pgc_t *pgc, pgc_cl_t *cl, int c) { uint8_t count; - DEBUG("parse_poly\n"); + DEBUG("IM1024: parse_poly\n"); if (! pgc_param_byte(pgc, &count)) return 0; - DEBUG("parse_poly: count=%02x\n", count); - if (! pgc_commandlist_append(cl, count)) { + DEBUG("IM1024: parse_poly: count=%02x\n", count); + if (! pgc_cl_append(cl, count)) { pgc_error(pgc, PGC_ERROR_OVERFLOW); return 0; } - DEBUG("parse_poly: parse %d words\n", 2 * count); + DEBUG("IM1024: parse_poly: parse %i words\n", 2 * count); return pgc_parse_words(pgc, cl, count * 2); } @@ -567,13 +592,13 @@ hndl_rect(pgc_t *pgc) 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); + DEBUG("IM1024: RECT (%i,%i) -> (%i,%i)\n", x0, y0, x1, y1); if (pgc->fill_mode) { for (p = y0; p <= y1; p++) pgc_fill_line_r(pgc, x0, x1, p); } else { - /* Outline: 4 lines */ + /* 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); @@ -597,24 +622,13 @@ hndl_tdefin(pgc_t *pgc) if (! pgc_param_byte(pgc, &rows)) return; if (! pgc_param_byte(pgc, &cols)) return; - DEBUG("TDEFIN (%d,%d,%d) 0x%02x 0x%02x\n", + DEBUG("IM1024: TDEFIN (%i,%i,%i) 0x%02x 0x%02x\n", ch, rows, cols, pgc->mapram[0x300], pgc->mapram[0x301]); len = ((cols + 7) / 8) * rows; for (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; } @@ -648,12 +662,13 @@ hndl_twrite(pgc_t *pgc) } pgc_sto_raster(pgc, &x0, &y0); - DEBUG("TWRITE (%d,%-*.*s) x0=%d y0=%d\n", + + DEBUG("IM1024: TWRITE (%i,%-*.*s) x0=%i y0=%i\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", + DEBUG("IM1024: ch=0x%02x w=%d h=%i wb=%i\n", buf[n], dev->fontx[buf[n]], dev->fonty[buf[n]], wb); for (y = 0; y < dev->fonty[buf[n]]; y++) { @@ -671,7 +686,6 @@ hndl_twrite(pgc_t *pgc) } rbuf[x++] = '\n'; rbuf[x++] = 0; -// DEBUG(rbuf); } x0 += dev->fontx[buf[n]]; @@ -692,19 +706,20 @@ hndl_imagew(pgc_t *pgc) /* 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); + DEBUG("IM1024: IMAGEW (row=%i,col1=%i,col2=%i)\n", row1, col1, col2); vp_x1 = pgc->vp_x1; vp_y1 = pgc->vp_y1; vp_x2 = pgc->vp_x2; vp_y2 = pgc->vp_y2; - /* Disable clipping */ + + /* 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 */ + /* In ASCII mode, what is written is a stream of bytes. */ if (pgc->ascii_mode) { while (col1 <= col2) { if (! pgc_param_byte(pgc, &v1)) @@ -717,12 +732,12 @@ hndl_imagew(pgc_t *pgc) return; } - /* In hex mode, it's RLE compressed */ + /* In hex mode, it's RLE compressed. */ while (col1 <= col2) { if (! pgc_param_byte(pgc, &v1)) return; if (v1 & 0x80) { - /* Literal run */ + /* Literal run. */ v1 -= 0x7f; while (col1 <= col2 && v1 != 0) { if (! pgc_param_byte(pgc, &v2)) return; @@ -731,7 +746,7 @@ hndl_imagew(pgc_t *pgc) v1--; } } else { - /* Repeated run */ + /* Repeated run. */ if (! pgc_param_byte(pgc, &v2)) return; v1++; @@ -743,7 +758,7 @@ hndl_imagew(pgc_t *pgc) } } - /* Restore clipping */ + /* Restore clipping. */ pgc->vp_x1 = vp_x1; pgc->vp_y1 = vp_y1; pgc->vp_x2 = vp_x2; @@ -763,7 +778,8 @@ hndl_dot(pgc_t *pgc) pgc_sto_raster(pgc, &x, &y); - DEBUG("Dot @ %d,%d ink=%d mode=%d\n", x, y, pgc->colour, pgc->draw_mode); + DEBUG("IM1024: DOT @ %i,%i ink=%i mode=%i\n", + x, y, pgc->color, pgc->draw_mode); pgc_plot(pgc, x, y); } @@ -785,7 +801,7 @@ hndl_imagex(pgc_t *pgc) 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); + DEBUG("IM1024: IMAGEX (%i,%i,%i,%i)\n", x0,y0,x1,y1); for (p = y0; p <= y1; p++) { for (q = x0; q <= x1; q++) { @@ -820,6 +836,8 @@ static const pgc_cmd_t im1024_commands[] = { { "IMGSIZ", 0x4e, hndl_imgsiz, NULL, 0 }, { "LUT8", 0xe6, pgc_hndl_lut8, NULL, 0 }, { "L8", 0xe6, pgc_hndl_lut8, NULL, 0 }, + { "LINFUN", 0xeb, hndl_linfun, pgc_parse_bytes, 1 }, + { "LF", 0xeb, hndl_linfun, pgc_parse_bytes, 1 }, { "LUT8RD", 0x53, pgc_hndl_lut8rd,NULL, 0 }, { "L8RD", 0x53, pgc_hndl_lut8rd,NULL, 0 }, { "PAN", 0xb7, hndl_pan, NULL, 0 }, @@ -886,7 +904,7 @@ static void speed_changed(void *priv) { im1024_t *dev = (im1024_t *)priv; - + pgc_speed_changed(&dev->pgc); } diff --git a/src/devices/video/vid_pgc.c b/src/devices/video/vid_pgc.c index a54ba2a..9bc0a88 100644 --- a/src/devices/video/vid_pgc.c +++ b/src/devices/video/vid_pgc.c @@ -32,9 +32,21 @@ * 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 + * to the video backend, which is different from PCem's: * - * Version: @(#)vid_pgc.c 1.0.1 2019/03/01 + * - cursor will blink at very high speed if used on a machine + * with clock greater than 4.77MHz. We should "scale down" + * this speed, to become relative to a 4.77MHz-based system. + * + * - pgc_plot() should be overloaded by clones if they support + * modes other than WRITE and INVERT, like the IM-1024. + * + * - test it with the Windows 1.x driver? + * + * Until these are fixed, both the PGC as well as the IM1024 + * will remain in DevBranch mode. + * + * Version: @(#)vid_pgc.c 1.0.2 2019/03/02 * * Authors: Fred N. van Kempen, * John Elliott, @@ -86,10 +98,7 @@ #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 **); +#define WAKE_DELAY (TIMER_USEC * 500) static const char *pgc_err_msgs[] = { @@ -113,175 +122,251 @@ static const uint32_t init_palette[6][256] = { }; -/* 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) +static inline int +is_whitespace(char ch) { - 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; + return (ch != 0 && strchr(" \r\n\t,;()+-", ch) != NULL); } /* - * Add a byte to a command list. + * Write a byte to the output buffer. * - * We allow command lists to be arbitrarily large. + * If buffer is full will sleep until it is not. Returns 0 if + * a PGC reset has been triggered by a write to 0xC63FF. */ -int -pgc_commandlist_append(pgc_cl_t *list, uint8_t v) +static int +output_byte(pgc_t *dev, uint8_t val) { - 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; + /* If output buffer full, wait for it to empty. */ + while (dev->mapram[0x302] == (uint8_t)(dev->mapram[0x303] - 1)) { + DEBUG("PGC: output buffer state: %02x %02x Sleeping\n", + dev->mapram[0x302], dev->mapram[0x303]); + dev->waiting_output_fifo = 1; + pgc_sleep(dev); } - 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; + if (dev->mapram[0x3ff]) { + /* Reset triggered. */ + pgc_reset(dev); + return 0; } - list->list[list->wrptr++] = v; + dev->mapram[0x100 + dev->mapram[0x302]] = val; + dev->mapram[0x302]++; + + DBGLOG(1, "PGC: output %02x: new state: %02x %02x\n", val, + dev->mapram[0x302], dev->mapram[0x303]); return 1; } -/* Beginning of a command list. Parse commands up to the next CLEND, - * storing them (in hex form) in the named command list. */ +/* 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 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 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; +} + + +/* + * Read 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; +} + + +/* + * 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; + } + } +} + + +/* + * Read in the next command. + * + * This can be either as hex (1 byte) or ASCII (up to 6 characters). + */ +static int +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 +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 (! 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 = dev->master; + + /* 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; +} + + +/* + * 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) { + const pgc_cmd_t *cmd; uint8_t param; pgc_cl_t cl; - const pgc_cmd_t *cmd; if (! pgc_param_byte(dev, ¶m)) return; - DEBUG("CLBEG(%i)\n", param); + DEBUG("PGC: CLBEG(%i)\n", param); memset(&cl, 0x00, sizeof(pgc_cl_t)); while (1) { - if (! pgc_parse_command(dev, &cmd)) { + if (! parse_command(dev, &cmd)) { /* PGC has been reset. */ return; } @@ -293,13 +378,13 @@ hndl_clbeg(pgc_t *dev) dev->clist[param] = cl; return; } else { - if (! pgc_commandlist_append(&cl, dev->hex_command)) { + if (! pgc_cl_append(&cl, dev->hex_command)) { pgc_error(dev, PGC_ERROR_OVERFLOW); return; } if (cmd->parser) { - if (!(*cmd->parser)(dev, &cl, cmd->p)) + if (! (*cmd->parser)(dev, &cl, cmd->p)) return; } } @@ -403,8 +488,8 @@ hndl_color(pgc_t *dev) if (! pgc_param_byte(dev, ¶m)) return; - dev->colour = param; - DEBUG("COLOR(%i)\n", param); + DEBUG("PGC: COLOR(%i)\n", param); + dev->color = param; } @@ -413,8 +498,6 @@ hndl_color(pgc_t *dev) * * 0 => Draw * 1 => Invert - * 2 => XOR (IM-1024) - * 3 => AND (IM-1024) */ static void hndl_linfun(pgc_t *dev) @@ -423,10 +506,11 @@ hndl_linfun(pgc_t *dev) 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); + DEBUG("PGC: LINFUN(%i)\n", param); + if (param < 2) + dev->draw_mode = param; + else + pgc_error(dev, PGC_ERROR_RANGE); } @@ -438,12 +522,12 @@ hndl_linpat(pgc_t *dev) if (! pgc_param_word(dev, (int16_t *)¶m)) return; + DEBUG("PGC: LINPAT(0x%04x)\n", param); dev->line_pattern = param; - DEBUG("LINPAT(0x%04x)\n", param); } -/* Set the polygon fill mode (0=hollow, 1=filled, 2=fast fill) */ +/* Set the polygon fill mode (0=hollow, 1=filled, 2=fast fill). */ static void hndl_prmfil(pgc_t *dev) { @@ -451,7 +535,7 @@ hndl_prmfil(pgc_t *dev) if (! pgc_param_byte(dev, ¶m)) return; - DEBUG("PRMFIL(%i)\n", param); + DEBUG("PGC: PRMFIL(%i)\n", param); if (param < 3) dev->fill_mode = param; else @@ -468,9 +552,10 @@ hndl_move(pgc_t *dev) if (! pgc_param_coord(dev, &x)) return; if (! pgc_param_coord(dev, &y)) return; + DEBUG("PCG: MOVE %x.%04x,%x.%04x\n", + HWORD(x), LWORD(x), HWORD(y), LWORD(y)); dev->x = x; dev->y = y; - DEBUG("MOVE %x.%04x,%x.%04x\n", HWORD(x), LWORD(x), HWORD(y), LWORD(y)); } @@ -490,7 +575,7 @@ hndl_move3(pgc_t *dev) } -/* Relative move (2D) */ +/* Relative move (2D). */ static void hndl_mover(pgc_t *dev) { @@ -504,7 +589,7 @@ hndl_mover(pgc_t *dev) } -/* Relative move (3D) */ +/* Relative move (3D). */ static void hndl_mover3(pgc_t *dev) { @@ -520,31 +605,125 @@ hndl_mover3(pgc_t *dev) } -/* 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) +/* 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) { - DEBUG("pgc_draw_line: (%i,%i) to (%i,%i)\n", - x0 >> 16, y0 >> 16, x1 >> 16, y1 >> 16); + int offset; - /* Convert from PGC fixed-point to device coordinates */ - x0 >>= 16; - x1 >>= 16; - y0 >>= 16; - y1 >>= 16; + /* 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; - pgc_ito_raster(dev, &x0, &y0); - pgc_ito_raster(dev, &x1, &y1); + offset = (dev->maxh - 1 - y) * (dev->maxw) + x; + DBGLOG(1, "PGC: vram_addr(x=%i,y=%i) = %i\n", x, y, offset); - return pgc_draw_line_r(dev, x0, y0, x1, y1, linemask); + if (offset < 0 || (uint32_t)offset >= (dev->maxw * dev->maxh)) + return NULL; + + return &dev->vram[offset]; } -/* Draw a line (using raster coordinates) - Bresenham's Algorithm from +/* + * 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. * - * The line pattern mask to use is passed in. The return value is the line - * pattern mask rotated by the number of points drawn. + * FIXME: this should be overloaded by clones if they support + * modes other than WRITE and INVERT, like the IM-1024. + */ +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->color); + 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->color; + break; + + case 1: /* INVERT */ + *vram ^= 0xff; + break; + + case 2: /* XOR color */ + //FIXME: see notes + *vram ^= dev->color; + break; + + case 3: /* AND color */ + //FIXME: see notes + *vram &= dev->color; + break; + } +} + + +/* + * Draw a line (using raster coordinates). + * + * Bresenham's Algorithm from: + * + * + * The line pattern mask to use is passed in. 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) @@ -561,9 +740,8 @@ pgc_draw_line_r(pgc_t *dev, int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint if (linemask & 0x8000) { pgc_plot(dev, x0, y0); linemask = (linemask << 1) | 1; - } else { + } else linemask = (linemask << 1); - } if (x0 == x1 && y0 == y1) break; @@ -582,8 +760,30 @@ pgc_draw_line_r(pgc_t *dev, int32_t x0, int32_t y0, int32_t x1, int32_t y1, uint } -/* Draw a horizontal line in the current fill pattern - * (using raster coordinates) */ +/* 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; + y0 >>= 16; + pgc_ito_raster(dev, &x0, &y0); + + x1 >>= 16; + y1 >>= 16; + pgc_ito_raster(dev, &x1, &y1); + + return pgc_draw_line_r(dev, x0, y0, x1, y1, 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) { @@ -619,7 +819,7 @@ compare_double(const void *a, const void *b) } -/* Draw a filled polygon (using PGC fixed-point coordinates) */ +/* Draw a filled polygon (using PGC fixed-point coordinates). */ void pgc_fill_polygon(pgc_t *dev, unsigned corners, int32_t *x, int32_t *y) { @@ -629,7 +829,7 @@ pgc_fill_polygon(pgc_t *dev, unsigned corners, int32_t *x, int32_t *y) unsigned n, nodes, i, j; double ymin, ymax, ypos; - DEBUG("pgc_fill_polygon(%d corners)\n", corners); + DEBUG("PGC: fill_polygon(%i corners)\n", corners); if (corners < 2) return; /* Degenerate polygon */ @@ -640,7 +840,7 @@ pgc_fill_polygon(pgc_t *dev, unsigned corners, int32_t *x, int32_t *y) ymin = ymax = y[0] / 65536.0; for (n = 0; n < corners; n++) { - /* Convert from PGC fixed-point to native floating-point */ + /* Convert from PGC fixed-point to native floating-point. */ dx[n] = x[n] / 65536.0; dy[n] = y[n] / 65536.0; @@ -662,10 +862,11 @@ pgc_fill_polygon(pgc_t *dev, unsigned corners, int32_t *x, int32_t *y) j = i; } - /* Sort the intersections */ - if (nodes) qsort(nodex, nodes, sizeof(double), compare_double); + /* Sort the intersections. */ + if (nodes) + qsort(nodex, nodes, sizeof(double), compare_double); - /* And fill between them */ + /* 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; @@ -681,7 +882,7 @@ pgc_fill_polygon(pgc_t *dev, unsigned corners, int32_t *x, int32_t *y) } -/* Draw a filled ellipse (using PGC fixed-point coordinates) */ +/* Draw a filled ellipse (using PGC fixed-point coordinates). */ void pgc_draw_ellipse(pgc_t *dev, int32_t x, int32_t y) { @@ -695,8 +896,8 @@ pgc_draw_ellipse(pgc_t *dev, int32_t x, int32_t y) 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); + DEBUG("PGC: ellipse(color=%i drawmode=%i fill=%i)\n", + dev->color, dev->draw_mode, dev->fill_mode); pgc_dto_raster(dev, &x0, &y0); @@ -771,11 +972,11 @@ hndl_poly(pgc_t *dev) if (! pgc_param_byte(dev, &count)) return; + DEBUG("PGC: POLY (%i)\n", count); 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); } @@ -785,121 +986,21 @@ parse_poly(pgc_t *dev, pgc_cl_t *cl, int c) { uint8_t count; - DEBUG("parse_poly\n"); + DBGLOG(1, "PCG: parse_poly\n"); if (! pgc_param_byte(dev, &count)) return 0; - DEBUG("parse_poly: count=%02x\n", count); + DBGLOG(1, "PCG: parse_poly: count=%02x\n", count); - if (! pgc_commandlist_append(cl, count)) { + if (! pgc_cl_append(cl, count)) { pgc_error(dev, PGC_ERROR_OVERFLOW); return 0; } - DEBUG("parse_poly: parse %i coords\n", 2 * count); + DEBUG("PCG: 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 */ +/* Handle the DISPLAY command. */ static void hndl_display(pgc_t *dev) { @@ -914,7 +1015,7 @@ hndl_display(pgc_t *dev) } -/* Handle the IMAGEW command (memory to screen blit) */ +/* Handle the IMAGEW command (memory to screen blit). */ static void hndl_imagew(pgc_t *dev) { @@ -942,12 +1043,12 @@ hndl_imagew(pgc_t *dev) return; } - /* In hex mode, it's RLE compressed */ + /* In hex mode, it's RLE compressed. */ while (col1 <= col2) { if (! pgc_param_byte(dev, &v1)) return; if (v1 & 0x80) { - /* Literal run */ + /* Literal run. */ v1 -= 0x7f; while (col1 <= col2 && v1 != 0) { if (! pgc_param_byte(dev, &v2)) return; @@ -956,7 +1057,7 @@ hndl_imagew(pgc_t *dev) v1--; } } else { - /* Repeated run */ + /* Repeated run. */ if (! pgc_param_byte(dev, &v2)) return; v1++; @@ -970,21 +1071,20 @@ hndl_imagew(pgc_t *dev) } -/* Select one of the built-in palettes */ +/* Select one of the built-in palettes. */ static void init_lut(pgc_t *dev, int param) { - if (param >= 0 && param < 6) { + if (param >= 0 && param < 6) memcpy(dev->palette, init_palette[param], sizeof(dev->palette)); - } else if (param == 0xff) { + else if (param == 0xff) memcpy(dev->palette, dev->userpal, sizeof(dev->palette)); - } else { + else pgc_error(dev, PGC_ERROR_RANGE); - } } -/* Save the current palette */ +/* Save the current palette. */ static void hndl_lutsav(pgc_t *dev) { @@ -992,7 +1092,7 @@ hndl_lutsav(pgc_t *dev) } -/* Handle LUTINT (select palette) */ +/* Handle LUTINT (select palette). */ static void hndl_lutint(pgc_t *dev) { @@ -1004,7 +1104,7 @@ hndl_lutint(pgc_t *dev) } -/* Handle LUTRD (read palette register) */ +/* Handle LUTRD (read palette register). */ static void hndl_lutrd(pgc_t *dev) { @@ -1021,7 +1121,7 @@ hndl_lutrd(pgc_t *dev) } -/* Handle LUT (write palette register) */ +/* Handle LUT (write palette register). */ static void hndl_lut(pgc_t *dev) { @@ -1042,9 +1142,11 @@ hndl_lut(pgc_t *dev) } -/* 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 */ +/* + * 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) { @@ -1074,7 +1176,7 @@ pgc_hndl_lut8(pgc_t *dev) } -/* Handle AREAPT (set 16x16 fill pattern) */ +/* Handle AREAPT (set 16x16 fill pattern). */ static void hndl_areapt(pgc_t *dev) { @@ -1084,13 +1186,14 @@ hndl_areapt(pgc_t *dev) 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", + DEBUG("PGC: AREAPT(%04x %04x %04x %04x...)\n", pat[0] & 0xffff, pat[1] & 0xffff, pat[2] & 0xffff, pat[3] & 0xffff); + + memcpy(dev->fill_pattern, pat, sizeof(dev->fill_pattern)); } -/* Handle CA (select ASCII mode) */ +/* Handle CA (select ASCII mode). */ static void hndl_ca(pgc_t *dev) { @@ -1098,7 +1201,7 @@ hndl_ca(pgc_t *dev) } -/* Handle CX (select hex mode) */ +/* Handle CX (select hex mode). */ static void hndl_cx(pgc_t *dev) { @@ -1106,8 +1209,10 @@ hndl_cx(pgc_t *dev) } -/* CA and CX remain valid in hex mode; they are handled as command 0x43 ('C') - * with a one-byte parameter */ +/* + * 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) { @@ -1148,8 +1253,10 @@ hndl_tjust(pgc_t *dev) } -/* VWPORT sets up the viewport (roughly, the clip rectangle) in raster - * coordinates, measured from the bottom left of the screen */ +/* + * 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) { @@ -1160,7 +1267,7 @@ hndl_vwport(pgc_t *dev) 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); + DEBUG("PGC: VWPORT %i,%i,%i,%i\n", x1,x2,y1,y2); dev->vp_x1 = x1; dev->vp_x2 = x2; dev->vp_y1 = y1; @@ -1179,7 +1286,7 @@ hndl_window(pgc_t *dev) 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); + DEBUG("PGC: WINDOW %i,%i,%i,%i\n", x1,x2,y1,y2); dev->win_x1 = x1; dev->win_x2 = x2; dev->win_y1 = y1; @@ -1271,311 +1378,51 @@ static const pgc_cmd_t pgc_commands[] = { }; -/* Writes to CGA registers are copied into the transfer memory buffer. */ -void -pgc_out(uint16_t addr, uint8_t val, void *priv) +/* Timer expired, actually wake up the processing thread. */ +static void +wake_timer(void *priv) { pgc_t *dev = (pgc_t *)priv; - switch(addr) { - case 0x3d0: - case 0x3d2: - case 0x3d4: - case 0x3d6: - dev->mapram[0x3d0] = val; - break; + dev->wake_timer = 0LL; + DEBUG("PGC: woke up\n"); - 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; - } + thread_set_event(dev->pgc_wake_thread); } -/* Read back the CGA registers. */ -uint8_t -pgc_in(uint16_t addr, void *priv) +/* + * 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; - 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; + DEBUG("PGC: thread begins\n"); while (1) { - if (! dev->inputbyte(dev, &ch)) return 0; + if (! parse_command(dev, &cmd)) { + /* PGC has been reset. */ + continue; + } - ch &= 0x7f; - if (ch == '\r' || ch == '\n' || ch == '\t' || ch >= ' ') { - *result = toupper(ch); - return 1; - } + 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); } } -/* Parameter passed is not a number: abort */ +/* Parameter passed is not a number: abort. */ static int err_digit(pgc_t *dev) { @@ -1592,233 +1439,6 @@ err_digit(pgc_t *dev) } -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) @@ -1859,179 +1479,18 @@ pgc_result_word(pgc_t *dev, int16_t val) } -/* 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) */ +/* Report an error, either in ASCII or in hex. */ int -pgc_read_command(pgc_t *dev) +pgc_error(pgc_t *dev, int err) { - 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; - } + 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 { - /* Otherwise match on the hex command */ - if (cmd->hex == dev->hex_command) { - sprintf(dev->asc_command, "%-6.6s", cmd->ascii); - *pcmd = cmd; - break; - } + return error_byte(dev, err); } } @@ -2039,30 +1498,534 @@ pgc_parse_command(pgc_t *dev, const pgc_cmd_t **pcmd) } -/* The PGC drawing thread main loop. Read in commands and execute them ad - * infinitum */ -static void -pgc_thread(void *priv) +/* Initialize RAM and registers to default values. */ +void +pgc_reset(pgc_t *dev) { - pgc_t *dev = (pgc_t *)priv; - const pgc_cmd_t *cmd; + int n; - DEBUG("pgc_thread begins"); - while (1) { - if (! pgc_parse_command(dev, &cmd)) { - /* PGC has been reset */ - continue; - } + memset(dev->mapram, 0x00, sizeof(dev->mapram)); - DEBUG("PGC command: [%02x] '%s' found=%i\n", - dev->hex_command, dev->asc_command, (cmd != NULL)); + /* The 'CGA disable' jumper is not currently implemented. */ + dev->mapram[0x30b] = dev->cga_enabled = 1; - if (cmd) { - dev->result_count = 0; - (*cmd->handler)(dev); - } else - pgc_error(dev, PGC_ERROR_OPCODE); + 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->color = 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); +} + + +/* 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); + } +} + + +/* + * 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. + */ +void +pgc_wake(pgc_t *dev) +{ + if (! dev->wake_timer) { + timer_process(); + + dev->wake_timer = WAKE_DELAY; + + timer_update_outstanding(); + } +} + + +/* Wait for more input data, or for output to drain. */ +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 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); +} + + +/* 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; +} + + +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) { + /* Decimal point is acceptable in 'main' state + * (start of fraction) not otherwise */ + case '.': + 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; + DBGLOG(1, "PGC: 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; +} + + +/* + * Add a byte to a command list. + * + * We allow command lists to be arbitrarily large. + */ +int +pgc_cl_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("PGC: 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("PGC: out of memory growing command list\n"); + return 0; + } + list->list = buf; + list->listmax *= 2; + } + + list->list[list->wrptr++] = v; + + return 1; +} + + +/* 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_cl_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_cl_append(cl, param[n] & 0xff) || + !pgc_cl_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_cl_append(cl, (param[n] >> 16) & 0xff) || + !pgc_cl_append(cl, (param[n] >> 24) & 0xff) || + + /* Serialize fraction part. */ + !pgc_cl_append(cl, (param[n] ) & 0xff) || + !pgc_cl_append(cl, (param[n] >> 8) & 0xff)) { + pgc_error(dev, PGC_ERROR_OVERFLOW); + free(param); + return 0; + } + } + + free(param); + + return 1; +} + + +/* 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, "PGC: 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; } @@ -2071,7 +2034,7 @@ pgc_recalctimings(pgc_t *dev) { double disptime, _dispontime, _dispofftime; - /* Use a fixed 640 columns, like the T3100e */ + /* Use a fixed 640x400 display. */ disptime = dev->screenw + 11; _dispontime = dev->screenw; _dispofftime = disptime - _dispontime; @@ -2080,6 +2043,169 @@ pgc_recalctimings(pgc_t *dev) } +/* Write 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; + + DEBUG("PGC: out(%04x, %02x)\n", addr, val); + + switch(addr) { + case 0x03d0: + case 0x03d2: + case 0x03d4: + case 0x03d6: + dev->mapram[0x03d0] = val; + break; + + case 0x03d1: + case 0x03d3: + case 0x03d5: + case 0x03d7: + if (dev->mapram[0x03d0] < 18) + dev->mapram[0x03e0 + dev->mapram[0x03d0]] = val; + break; + + case 0x03d8: + dev->mapram[0x03d8] = val; + break; + + case 0x03d9: + dev->mapram[0x03d9] = val; + break; + } +} + + +/* Read back the CGA registers. */ +uint8_t +pgc_in(uint16_t addr, void *priv) +{ + pgc_t *dev = (pgc_t *)priv; + uint8_t ret = 0xff; + + switch(addr) { + case 0x03d0: + case 0x03d2: + case 0x03d4: + case 0x03d6: + ret = dev->mapram[0x03d0]; + break; + + case 0x03d1: + case 0x03d3: + case 0x03d5: + case 0x03d7: + if (dev->mapram[0x03d0] < 18) + ret = dev->mapram[0x03e0 + dev->mapram[0x03d0]]; + break; + + case 0x03d8: + ret = dev->mapram[0x03d8]; + break; + + case 0x03d9: + ret = dev->mapram[0x03d9]; + break; + + case 0x03da: + ret = dev->mapram[0x03da]; + break; + } + + DEBUG("PGC: in(%04x) = %02x\n", addr, ret); + + return ret; +} + + +/* 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 */ + 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; + uint8_t ret = 0xff; + + if (addr >= 0xc6000 && addr < 0xc6800) { + addr &= 0x7ff; + ret = dev->mapram[addr]; + } else if (addr >= 0xb8000 && addr < 0xbc000 && dev->cga_selected) { + addr &= 0x3fff; + ret = dev->cga_vram[addr]; + } + + return ret; +} + + /* Draw the display in CGA (640x400) text mode. */ void pgc_cga_text(pgc_t *dev, int w) @@ -2093,6 +2219,7 @@ pgc_cga_text(pgc_t *dev, int w) 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; + uint32_t val; int cw = (w == 80) ? 8 : 16; addr = &dev->cga_vram[(((ma << 1) + ((dev->displine / pitch)*w)) * 2) & 0x3ffe]; @@ -2121,20 +2248,12 @@ pgc_cga_text(pgc_t *dev, int w) 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]; - } + for (c = 0; c < cw; c++) { + if (drawcursor) + val = cols[(fontdatm[chr + dev->fontbase][sc] & (1 << (c ^ 7))) ? 1 : 0] ^ 0x0f; + else + val = cols[(fontdatm[chr + dev->fontbase][sc] & (1 << (c ^ 7))) ? 1 : 0]; + buffer->line[dev->displine][(x * cw) + c] = val; } ma++; @@ -2208,8 +2327,7 @@ pgc_cga_gfx80(pgc_t *dev) } -/* 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 */ +/* Draw the screen in CGA mode. */ void pgc_cga_poll(pgc_t *dev) { @@ -2218,30 +2336,28 @@ pgc_cga_poll(pgc_t *dev) if (! dev->linepos) { dev->vidtime += dev->dispofftime; - dev->mapram[0x3da] |= 1; + dev->mapram[0x03da] |= 1; dev->linepos = 1; if (dev->cgadispon) { if (dev->displine == 0) video_wait_for_buffer(); - if ((dev->mapram[0x3d8] & 0x12) == 0x12) + if ((dev->mapram[0x03d8] & 0x12) == 0x12) pgc_cga_gfx80(dev); - else if (dev->mapram[0x3d8] & 0x02) + else if (dev->mapram[0x03d8] & 0x02) pgc_cga_gfx40(dev); - else if (dev->mapram[0x3d8] & 0x01) + else if (dev->mapram[0x03d8] & 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); + cols[0] = ((dev->mapram[0x03d8] & 0x12) == 0x12) ? 0 : (dev->mapram[0x03d9] & 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 + ((uint32_t *)buffer32->line[dev->displine])[c] = *((uint32_t *)&cgapal[buffer->line[dev->displine][c] & 0x0f]); if (++dev->displine == PGC_CGA_HEIGHT) { dev->mapram[0x3da] |= 8; @@ -2365,60 +2481,6 @@ pgc_poll(void *priv) } -/* 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) { @@ -2491,19 +2553,17 @@ pgc_init(pgc_t *dev, int maxw, int maxh, int visw, int vish, pgc_reset(dev); dev->inputbyte = inpbyte; - dev->commands = pgc_commands; + dev->master = 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); - +pclog_repeat(0); 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) { @@ -2532,12 +2592,13 @@ static const device_config_t pgc_config[] = { const device_t pgc_device = { - "IBM PGC", + "PGC", DEVICE_ISA, 0, pgc_standalone_init, pgc_close, NULL, + NULL, pgc_speed_changed, - NULL, NULL, + NULL, &pgc_timings, pgc_config }; diff --git a/src/devices/video/vid_pgc.h b/src/devices/video/vid_pgc.h index dfeeacf..e99a5ea 100644 --- a/src/devices/video/vid_pgc.h +++ b/src/devices/video/vid_pgc.h @@ -8,7 +8,7 @@ * * Definitions for the PGC driver. * - * Version: @(#)vid_pgc.h 1.0.1 2019/03/01 + * Version: @(#)vid_pgc.h 1.0.2 2019/03/02 * * Authors: Fred N. van Kempen, * John Elliott, @@ -78,17 +78,18 @@ typedef struct pgc { pgc_cl_t *clist, *clcur; - const pgc_cmd_t *commands; + const pgc_cmd_t *master, + *commands; - uint8_t mapram[2048]; /* Host <--> PGC communication buffer */ + 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 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; @@ -97,11 +98,11 @@ typedef struct pgc { 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*/ + uint8_t color; + uint8_t tjust_h; /* hor alignment 1=left 2=ctr 3=right*/ + uint8_t tjust_v; /* vert alignment 1=bottom 2=ctr 3=top*/ - int32_t x, y, z; /* drawing position */ + int32_t x, y, z; /* drawing position */ thread_t *pgc_thread; event_t *pgc_wake_thread; @@ -116,7 +117,8 @@ typedef struct pgc { int result_count; int fontbase; - int linepos, displine; + int linepos, + displine; int vc; int cgadispon; int con, coff, cursoron, cgablink; @@ -143,14 +145,14 @@ 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_setdisplay(pgc_t *, int cga); extern void pgc_speed_changed(void *priv); +extern void pgc_close(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); @@ -162,20 +164,22 @@ 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 uint8_t *pgc_vram_addr(pgc_t *, int16_t x, int16_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 void pgc_plot(pgc_t *, uint16_t x, uint16_t y); 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_fill_line_r(pgc_t *, int32_t x0, int32_t x1, int32_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 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_clist_byte(pgc_t *, uint8_t *val); +extern int pgc_cl_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); @@ -186,6 +190,7 @@ 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); +/* Special overload functions for non-IBM implementations. */ extern void pgc_hndl_lut8(pgc_t *); extern void pgc_hndl_lut8rd(pgc_t *); diff --git a/src/devices/video/video.c b/src/devices/video/video.c index 9fa49ed..0483645 100644 --- a/src/devices/video/video.c +++ b/src/devices/video/video.c @@ -40,7 +40,7 @@ * W = 3 bus clocks * L = 4 bus clocks * - * Version: @(#)video.c 1.0.24 2019/02/11 + * Version: @(#)video.c 1.0.25 2019/03/02 * * Authors: Fred N. van Kempen, * Miran Grca, @@ -912,31 +912,21 @@ video_load_font(const wchar_t *s, int format) switch (format) { case 0: /* MDA */ for (c = 0; c < 256; c++) - for (d = 0; d < 8; d++) - fontdatm[c][d] = fgetc(fp); + (void)fread(&fontdatm[c][0], 1, 8, fp); for (c = 0; c < 256; c++) - for (d = 0; d < 8; d++) - fontdatm[c][d + 8] = fgetc(fp); -#if 0 - (void)fseek(f, 4096+2048, SEEK_SET); - for (c = 0; c < 256; c++) - for (d = 0; d < 8; d++) - fontdat[c][d] = fgetc(fp); -#endif + (void)fread(&fontdatm[c][8], 1, 8, fp); break; case 1: /* PC200 */ for (c = 0; c < 256; c++) - for (d = 0; d < 8; d++) - fontdatm[c][d] = fgetc(fp); + (void)fread(&fontdatm[c][0], 1, 8, fp); for (c = 0; c < 256; c++) - for (d = 0; d < 8; d++) - fontdatm[c][d + 8] = fgetc(fp); + (void)fread(&fontdatm[c][8], 1, 8, fp); (void)fseek(fp, 4096, SEEK_SET); for (c = 0; c < 256; c++) { + (void)fread(&fontdat[c][0], 1, 8, fp); for (d = 0; d < 8; d++) - fontdat[c][d] = fgetc(fp); - for (d = 0; d < 8; d++) (void)fgetc(fp); + (void)fgetc(fp); } break; @@ -947,20 +937,17 @@ video_load_font(const wchar_t *s, int format) (void)fseek(fp, 2048, SEEK_SET); } for (c = 0; c < 256; c++) - for (d = 0; d < 8; d++) - fontdat[c][d] = fgetc(fp); + (void)fread(&fontdat[c][0], 1, 8, fp); break; case 3: /* Wyse 700 */ for (c = 0; c < 512; c++) - for (d = 0; d < 32; d++) - fontdatw[c][d] = fgetc(fp); + (void)fread(&fontdatw[c][0], 1, 32, fp); break; case 4: /* MDSI Genius */ for (c = 0; c < 256; c++) - for (d = 0; d < 16; d++) - fontdat8x12[c][d] = fgetc(fp); + (void)fread(&fontdat8x12[c][0], 1, 16, fp); break; case 5: /* Toshiba 3100e */ @@ -1007,6 +994,8 @@ video_load_font(const wchar_t *s, int format) break; } + DEBUG("VIDEO: font #%i loaded\n", format); + (void)fclose(fp); } diff --git a/src/devices/video/video_dev.c b/src/devices/video/video_dev.c index 204aed9..6b2f4df 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.31 2019/03/01 + * Version: @(#)video_dev.c 1.0.32 2019/03/02 * * Authors: Fred N. van Kempen, * Miran Grca, @@ -68,6 +68,9 @@ static const struct { /* Standard video controllers. */ { "mda", &mda_device }, { "cga", &cga_device }, +#if defined(DEV_BRANCH) + { "pgc", &pgc_device }, +#endif { "ega", &ega_device }, { "vga", &vga_device }, { "hercules", &hercules_device }, @@ -102,10 +105,9 @@ static const struct { { "hercules_plus", &herculesplus_device }, { "incolor", &incolor_device }, { "genius", &genius_device }, -#if 0 - { "pgc", &pgc_device }, -#endif +#if defined(DEV_BRANCH) { "im1024", &im1024_device }, +#endif { "oti037c", &oti037c_device }, { "oti067", &oti067_device }, { "oti077", &oti077_device },