/* * $Id: type340.c,v 1.5 2004/01/24 20:52:16 phil Exp $ * Simulator Independent DEC Type 340 Graphic Display Processor Simulation * Phil Budne * September 20, 2003 * from vt11.c * * Information from DECUS 7-13 * http://www.spies.com/~aek/pdf/dec/pdp7/7-13_340displayProgMan.pdf */ /* * Copyright (c) 2003-2004, Philip L. Budne * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the name of the author shall * not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization * from the authors. */ #include "display.h" /* XY plot interface */ /* * The Type 340 was used on the PDP-{4,6,7,9,10} * and used 18-bit words, with bits numbered 0 thru 17 * (most significant to least) */ #define BITMASK(N) (1<<(17-(N))) /* mask for a field */ #define FIELDMASK(START,END) ((1<<((END)-(START)+1))-1) /* extract a field */ #define GETFIELD(W,START,END) (((W)>>(17-(END)))&FIELDMASK(START,END)) /* extract a 1-bit field */ #define TESTBIT(W,B) (((W) & BITMASK(B)) != 0) #ifdef DEBUG_TY340 #define DEBUGF(X) printf X #else #define DEBUGF(X) #endif typedef long ty340word; static ty340word DAC; /* Display Address Counter */ static unsigned char shift; /* 1 bit */ static enum mode mode; /* 3 bits */ static int scale; /* 2 bits */ enum mode { PARAM=0, POINT, SLAVE, CHAR, VECTOR, VCONT, INCR, SUBR }; enum jump_type { DJP=2, DJS=3, DDS=1 }; static ty340word ASR; /* Address Save Register */ static unsigned char save_ff; /* "save" flip-flop */ static unsigned char intensity; /* 3 bits */ static unsigned char lp_ena; /* 1 bit */ /* kept signed for raster violation checking */ static short xpos, ypos; /* 10 bits, signed */ static unsigned char sequence; /* 2 bits */ /* XXX make defines public for 340_cycle return */ #define STOPPED 01 #define LPHIT 02 #define VEDGE 04 #define HEDGE 010 static unsigned char status = STOPPED; /* * callbacks into PDP-6/10 simulator */ extern ty340word ty340_fetch(ty340word); extern void ty340_store(ty340word, ty340word); extern void ty340_stop_int(void); extern void ty340_lp_int(void); void ty340_set_dac(ty340word addr) { DAC = addr; mode = 0; DEBUGF(("set DAC %06\r\n", DAC)); status = 0; /* XXX just clear stopped? */ /* XXX clear other stuff? save_ff? */ } void ty340_reset(void) { /* XXX call display layer? destroy window? */ xpos = ypos = 0; status = STOPPED; } static int point(int x, int y, int seq) { int i; /* XXX apply scale? */ i = DISPLAY_INT_MAX-7+intensity; if (i <= 0) i = 1; if (x < 0 || x > 1023) { status |= VEDGE; return 0; } if (y < 0 || y > 1023) { status |= HEDGE; return 0; } if (display_point(x, y, i, 0)) { if (lp_ena) { /* XXX save location? */ status |= LPHIT; sequence = seq; } } } /* * two-step algorithm, developed by Xiaolin Wu * from http://graphics.lcs.mit.edu/~mcmillan/comp136/Lecture6/Lines.html */ /* * The two-step algorithm takes the interesting approach of treating * line drawing as a automaton, or finite state machine. If one looks * at the possible configurations for the next two pixels of a line, * it is easy to see that only a finite set of possibilities exist. * The two-step algorithm shown here also exploits the symmetry of * line-drawing by simultaneously drawn from both ends towards the * midpoint. */ static void lineTwoStep(int x0, int y0, int x1, int y1) { int dy = y1 - y0; int dx = x1 - x0; int stepx, stepy; if (dy < 0) { dy = -dy; stepy = -1; } else { stepy = 1; } if (dx < 0) { dx = -dx; stepx = -1; } else { stepx = 1; } lpoint(x0,y0); if (dx == 0 && dy == 0) /* following algorithm won't work */ return; /* just the one dot */ lpoint(x1, y1); if (dx > dy) { int length = (dx - 1) >> 2; int extras = (dx - 1) & 3; int incr2 = (dy << 2) - (dx << 1); if (incr2 < 0) { int c = dy << 1; int incr1 = c << 1; int d = incr1 - dx; int i; for (i = 0; i < length; i++) { x0 += stepx; x1 -= stepx; if (d < 0) { /* Pattern: */ lpoint(x0, y0); lpoint(x0 += stepx, y0); /* x o o */ lpoint(x1, y1); lpoint(x1 -= stepx, y1); d += incr1; } else { if (d < c) { /* Pattern: */ lpoint(x0, y0); /* o */ lpoint(x0 += stepx, y0 += stepy); /* x o */ lpoint(x1, y1); lpoint(x1 -= stepx, y1 -= stepy); } else { lpoint(x0, y0 += stepy); /* Pattern: */ lpoint(x0 += stepx, y0); /* o o */ lpoint(x1, y1 -= stepy); /* x */ lpoint(x1 -= stepx, y1); } d += incr2; } } if (extras > 0) { if (d < 0) { lpoint(x0 += stepx, y0); if (extras > 1) lpoint(x0 += stepx, y0); if (extras > 2) lpoint(x1 -= stepx, y1); } else if (d < c) { lpoint(x0 += stepx, y0); if (extras > 1) lpoint(x0 += stepx, y0 += stepy); if (extras > 2) lpoint(x1 -= stepx, y1); } else { lpoint(x0 += stepx, y0 += stepy); if (extras > 1) lpoint(x0 += stepx, y0); if (extras > 2) lpoint(x1 -= stepx, y1 -= stepy); } } } else { int c = (dy - dx) << 1; int incr1 = c << 1; int d = incr1 + dx; int i; for (i = 0; i < length; i++) { x0 += stepx; x1 -= stepx; if (d > 0) { lpoint(x0, y0 += stepy); /* Pattern: */ lpoint(x0 += stepx, y0 += stepy); /* o */ lpoint(x1, y1 -= stepy); /* o */ lpoint(x1 -= stepx, y1 -= stepy); /* x */ d += incr1; } else { if (d < c) { lpoint(x0, y0); /* Pattern: */ lpoint(x0 += stepx, y0 += stepy); /* o */ lpoint(x1, y1); /* x o */ lpoint(x1 -= stepx, y1 -= stepy); } else { lpoint(x0, y0 += stepy); /* Pattern: */ lpoint(x0 += stepx, y0); /* o o */ lpoint(x1, y1 -= stepy); /* x */ lpoint(x1 -= stepx, y1); } d += incr2; } } if (extras > 0) { if (d > 0) { lpoint(x0 += stepx, y0 += stepy); if (extras > 1) lpoint(x0 += stepx, y0 += stepy); if (extras > 2) lpoint(x1 -= stepx, y1 -= stepy); } else if (d < c) { lpoint(x0 += stepx, y0); if (extras > 1) lpoint(x0 += stepx, y0 += stepy); if (extras > 2) lpoint(x1 -= stepx, y1); } else { lpoint(x0 += stepx, y0 += stepy); if (extras > 1) lpoint(x0 += stepx, y0); if (extras > 2) { if (d > c) lpoint(x1 -= stepx, y1 -= stepy); else lpoint(x1 -= stepx, y1); } } } } } else { int length = (dy - 1) >> 2; int extras = (dy - 1) & 3; int incr2 = (dx << 2) - (dy << 1); if (incr2 < 0) { int c = dx << 1; int incr1 = c << 1; int d = incr1 - dy; int i; for (i = 0; i < length; i++) { y0 += stepy; y1 -= stepy; if (d < 0) { lpoint(x0, y0); lpoint(x0, y0 += stepy); lpoint(x1, y1); lpoint(x1, y1 -= stepy); d += incr1; } else { if (d < c) { lpoint(x0, y0); lpoint(x0 += stepx, y0 += stepy); lpoint(x1, y1); lpoint(x1 -= stepx, y1 -= stepy); } else { lpoint(x0 += stepx, y0); lpoint(x0, y0 += stepy); lpoint(x1 -= stepx, y1); lpoint(x1, y1 -= stepy); } d += incr2; } } if (extras > 0) { if (d < 0) { lpoint(x0, y0 += stepy); if (extras > 1) lpoint(x0, y0 += stepy); if (extras > 2) lpoint(x1, y1 -= stepy); } else if (d < c) { lpoint(x0, y0 += stepy); if (extras > 1) lpoint(x0 += stepx, y0 += stepy); if (extras > 2) lpoint(x1, y1 -= stepy); } else { lpoint(x0 += stepx, y0 += stepy); if (extras > 1) lpoint(x0, y0 += stepy); if (extras > 2) lpoint(x1 -= stepx, y1 -= stepy); } } } else { int c = (dx - dy) << 1; int incr1 = c << 1; int d = incr1 + dy; int i; for (i = 0; i < length; i++) { y0 += stepy; y1 -= stepy; if (d > 0) { lpoint(x0 += stepx, y0); lpoint(x0 += stepx, y0 += stepy); lpoint(x1 -= stepy, y1); lpoint(x1 -= stepx, y1 -= stepy); d += incr1; } else { if (d < c) { lpoint(x0, y0); lpoint(x0 += stepx, y0 += stepy); lpoint(x1, y1); lpoint(x1 -= stepx, y1 -= stepy); } else { lpoint(x0 += stepx, y0); lpoint(x0, y0 += stepy); lpoint(x1 -= stepx, y1); lpoint(x1, y1 -= stepy); } d += incr2; } } if (extras > 0) { if (d > 0) { lpoint(x0 += stepx, y0 += stepy); if (extras > 1) lpoint(x0 += stepx, y0 += stepy); if (extras > 2) lpoint(x1 -= stepx, y1 -= stepy); } else if (d < c) { lpoint(x0, y0 += stepy); if (extras > 1) lpoint(x0 += stepx, y0 += stepy); if (extras > 2) lpoint(x1, y1 -= stepy); } else { lpoint(x0 += stepx, y0 += stepy); if (extras > 1) lpoint(x0, y0 += stepy); if (extras > 2) { if (d > c) lpoint(x1 -= stepx, y1 -= stepy); else lpoint(x1, y1 -= stepy); } } } } } } /* lineTwoStep */ static int vector(int i, int sx, int dx, int sy, int dy) { int x0, y0, x1, y1; x0 = xpos; y0 = ypos; if (sx) { x1 = x0 - dx; if (x1 < 0) /* XXX TEMP? */ x1 = 0; } else { x1 = x0 + dx; if (x1 > 1023) /* XXX TEMP? */ x1 = 1023; } if (sy) { y1 = y0 - dy; if (y1 < 0) /* XXX TEMP? */ y1 = 0; } else { y1 = y0 + dy; /* XXX TEMP? */ if (y1 > 1023) y1 = 1023; } DEBUGF(("vector i%d (%d,%d) to (%d,%d)\r\n", i, x0, y0, x1, y1)); if (i) lineTwoStep(x0, y0, x1, y1); xpos = x1; ypos = y1; return 0; } /* return true on raster violation */ int ipoint(int i, int n, unsigned char byte) { if (byte & 010) { /* left/right */ if (byte & 04) { if (xpos == 0) { status |= VEDGE; return 1; } xpos--; } else { if (xpos == 1023) { status |= VEDGE; return 1; } xpos++; } } if (byte & 02) { /* up/down */ if (byte & 04) { if (ypos == 0) { status |= HEDGE; return 1; } ypos--; } else { if (ypos == 1023) { status |= HEDGE; return 1; } ypos++; } } if (i) point(xpos, ypos, n); return 0; } /* * 342 character generator - first 64 characters (from manual) */ static const unsigned char chars[64][5] = { { 0070, 0124, 0154, 0124, 0070 }, /* 00 */ { 0174, 0240, 0240, 0240, 0174 }, /* 01 A */ { 0376, 0222, 0222, 0222, 0154 }, /* 02 B */ { 0174, 0202, 0202, 0202, 0104 }, /* 03 C */ { 0376, 0202, 0202, 0202, 0174 }, /* 04 D */ { 0376, 0222, 0222, 0222, 0222 }, /* 05 E */ { 0376, 0220, 0220, 0220, 0220 }, /* 06 F */ { 0174, 0202, 0222, 0222, 0134 }, /* 07 G */ { 0376, 0020, 0020, 0020, 0376 }, /* 10 H */ { 0000, 0202, 0376, 0202, 0000 }, /* 11 I */ { 0004, 0002, 0002, 0002, 0374 }, /* 12 J */ { 0376, 0020, 0050, 0104, 0202 }, /* 13 K */ { 0376, 0002, 0002, 0002, 0002 }, /* 14 K */ { 0374, 0100, 0040, 0100, 0374 }, /* 15 M */ { 0376, 0100, 0040, 0020, 0376 }, /* 16 N */ { 0174, 0202, 0202, 0202, 0174 }, /* 17 O */ { 0376, 0220, 0220, 0220, 0140 }, /* 20 P */ { 0174, 0202, 0212, 0206, 0176 }, /* 21 Q */ { 0376, 0220, 0230, 0224, 0142 }, /* 22 R */ { 0144, 0222, 0222, 0222, 0114 }, /* 23 S */ { 0200, 0200, 0376, 0200, 0200 }, /* 24 T */ { 0374, 0002, 0002, 0002, 0374 }, /* 25 U */ { 0370, 0004, 0002, 0004, 0370 }, /* 26 V */ { 0376, 0004, 0010, 0004, 0376 }, /* 27 W */ { 0202, 0104, 0070, 0104, 0202 }, /* 30 X */ { 0200, 0100, 0076, 0100, 0200 }, /* 31 Y */ { 0226, 0232, 0222, 0262, 0322 }, /* 32 Z */ { 0000, 0000, 0000, 0000, 0000 }, /* 33 LF */ { 0000, 0000, 0000, 0000, 0000 }, /* 34 CR */ { 0000, 0000, 0000, 0000, 0000 }, /* 35 HORIZ */ { 0000, 0000, 0000, 0000, 0000 }, /* 36 VERT */ { 0000, 0000, 0000, 0000, 0000 }, /* 37 ESC */ { 0000, 0000, 0000, 0000, 0000 }, /* 40 space */ { 0000, 0000, 0372, 0000, 0000 }, /* 41 ! */ { 0000, 0340, 0000, 0340, 0000 }, /* 42 " */ { 0050, 0376, 0050, 0376, 0050 }, /* 43 # */ { 0144, 0222, 0376, 0222, 0114 }, /* 44 $ */ { 0306, 0310, 0220, 0246, 0306 }, /* 45 % */ { 0154, 0222, 0156, 0004, 0012 }, /* 46 & */ { 0000, 0000, 0300, 0340, 0000 }, /* 47 ' */ { 0070, 0104, 0202, 0000, 0000 }, /* 50 ( */ { 0000, 0000, 0202, 0104, 0070 }, /* 51 ) */ { 0124, 0070, 0174, 0070, 0124 }, /* 52 * */ { 0020, 0020, 0174, 0020, 0020 }, /* 53 + */ { 0000, 0014, 0016, 0000, 0000 }, /* 54 , */ { 0020, 0020, 0020, 0020, 0020 }, /* 55 - */ { 0000, 0006, 0006, 0000, 0000 }, /* 56 . */ { 0004, 0010, 0020, 0040, 0100 }, /* 57 / */ { 0174, 0212, 0222, 0242, 0174 }, /* 60 0 */ { 0000, 0102, 0376, 0002, 0000 }, /* 61 1 */ { 0116, 0222, 0222, 0222, 0142 }, /* 62 2 */ { 0104, 0202, 0222, 0222, 0154 }, /* 63 3 */ { 0020, 0060, 0120, 0376, 0020 }, /* 64 4 */ { 0344, 0222, 0222, 0222, 0214 }, /* 65 5 */ { 0174, 0222, 0222, 0222, 0114 }, /* 66 6 */ { 0306, 0210, 0220, 0240, 0300 }, /* 67 7 */ { 0154, 0222, 0222, 0222, 0154 }, /* 70 8 */ { 0144, 0222, 0222, 0222, 0174 }, /* 71 9 */ { 0000, 0066, 0066, 0000, 0000 }, /* 72 : */ { 0000, 0154, 0156, 0000, 0000 }, /* 73 ; */ { 0020, 0050, 0104, 0202, 0000 }, /* 74 < */ { 0050, 0050, 0050, 0050, 0050 }, /* 75 = */ { 0000, 0202, 0104, 0050, 0020 }, /* 76 > */ { 0100, 0200, 0236, 0220, 0140 } /* 77 ? */ }; /* * type 342 Character/Symbol generator for type 340 display * return true if ESCaped */ static int character(int n, char c) { int x, y; switch (c) { case 033: /* LF */ if (ypos < 12) { status |= HEDGE; ypos = 0; } else ypos -= 12; /* XXX scale? */ return 0; case 034: /* CR */ xpos = 0; return 0; case 035: /* shift in */ shift = 1; return 0; case 036: /* shift out */ shift = 0; return 0; case 037: /* escape */ sequence = n; return 1; } /* XXX plot character from character set selected by "shift" * (offset index by 64?) */ for (x = 0; x < 5; x++) { for (y = 0; y < 7; y++) { if (chars[c][x] & (1< 1023) { xpos = 1023; status |= VEDGE; } return 0; } int ty340_cycle(int us, int slowdown) { ty340word inst, addr; int i, escape, stopped; if (status & STOPPED) return 0; /* XXX age display? */ inst = ty340_fetch(DAC); DEBUGF(("%06o: %06o\r\n", DAC, inst)); DAC++; escape = 0; switch (mode) { case PARAM: mode = GETFIELD(inst, 2, 4); if (TESTBIT(inst, 5)) { /* load l.p. enable */ lp_ena = TESTBIT(inst,6); DEBUGF(("lp_ena %d\r\n", lp_ena)); } if (TESTBIT(inst, 7)) { status |= STOPPED; if (TESTBIT(inst, 8)) ty340_stop_int(); /* set stop_int_end? */ } if (TESTBIT(inst, 11)) scale = GETFIELD(inst, 12, 13); if (TESTBIT(inst, 14)) intensity = GETFIELD(inst, 15, 17); break; case POINT: mode = GETFIELD(inst, 2, 4); if (TESTBIT(inst, 5)) /* load l.p. enable */ lp_ena = TESTBIT(inst,6); if (TESTBIT(inst, 1)) ypos = GETFIELD(inst, 8, 17); else xpos = GETFIELD(inst, 8, 17); if (TESTBIT(inst, 7)) point(xpos, ypos, 0); break; case SLAVE: mode = GETFIELD(inst, 2, 4); break; case CHAR: escape = (character(0, GETFIELD(inst, 0, 5)) || character(1, GETFIELD(inst, 6, 11)) || character(2, GETFIELD(inst, 12, 17))); break; case VECTOR: escape = TESTBIT(inst, 0); if (vector(TESTBIT(inst, 1), TESTBIT(inst, 2), GETFIELD(inst, 3, 9), TESTBIT(inst, 10), GETFIELD(inst, 11, 17))) { /* XXX interrupt? */ } break; case VCONT: escape = TESTBIT(inst, 0); if (vector(TESTBIT(inst, 1), TESTBIT(inst, 2), GETFIELD(inst, 3, 9), TESTBIT(inst, 10), GETFIELD(inst, 11, 17))) { /* XXX set escape? */ mode = PARAM; /* raster violation */ } break; case INCR: escape = TESTBIT(inst, 0); /* escape bit */ i = TESTBIT(inst, 1); if (ipoint(i, 0, GETFIELD(inst, 2, 5)) || ipoint(i, 1, GETFIELD(inst, 6, 9)) || ipoint(i, 2, GETFIELD(inst, 10, 13)) || ipoint(i, 3, GETFIELD(inst, 14, 17))) /* XXX set escape? */ mode = PARAM; /* raster violation */ break; case SUBR: /* type 347 Display Subroutine Option? */ mode = GETFIELD(inst, 2, 4); /* XXX take high bits of current DAC? */ addr = GETFIELD(inst, 5, 17); switch (GETFIELD(inst, 0, 1)) { case DJS: /* display jump and save */ ASR = DAC; save_ff = 1; /* set "save" flip-flop */ /* FALL */ case DJP: /* display jump */ DAC = addr; break; case DDS: /* display deposit save register */ ty340_deposit(addr, (DJP<<16) | ASR); save_ff = 0; /* ?? */ break; default: /* XXX ??? */ break; } break; } if (escape) { mode = PARAM; if (save_ff) { /* return from subroutine */ DAC = ASR; save_ff = 0; } } return status; } /* ty340_cycle */