From 8cf83c1b1233e6c46b713f2214d439c0e8f4f594 Mon Sep 17 00:00:00 2001 From: Phil Budne Date: Sun, 20 Mar 2016 23:10:34 -0400 Subject: [PATCH] PDP7: GRAPHICS2 works, mostly --- PDP18B/pdp18b_defs.h | 10 +- PDP18B/pdp18b_g2tty.c | 211 ++++++++++++++++++++++++++++++++---------- 2 files changed, 165 insertions(+), 56 deletions(-) diff --git a/PDP18B/pdp18b_defs.h b/PDP18B/pdp18b_defs.h index 3b15a5ef..b5561c55 100644 --- a/PDP18B/pdp18b_defs.h +++ b/PDP18B/pdp18b_defs.h @@ -463,6 +463,7 @@ typedef struct { #define INT_V_TTI 0 /* console keyboard */ #define INT_V_TTO 1 /* console output */ #define INT_V_PTP 2 /* paper tape punch */ +#define INT_V_G2 3 /* BTL GRAPHICS-2 */ #define INT_TTI (1 << INT_V_TTI) #define INT_TTO (1 << INT_V_TTO) @@ -474,12 +475,11 @@ typedef struct { #ifdef GRAPHICS2 /* - * a PDP-9 version existed, and may have used API, - * but we're only interested in the PDP-7 + * A PDP-9 version existed, + * but we're only interested simulating a PDP-7 without API */ -#define INT_V_G2 2 /* GRAPHICS-2 */ -#define INT_G2 (1 << INT_V_G2) -#define API_G2 4 +#define INT_G2 (1 << INT_V_G2) +#define API_G2 4 #endif /* Interrupt macros */ diff --git a/PDP18B/pdp18b_g2tty.c b/PDP18B/pdp18b_g2tty.c index 8378a79a..217c4b25 100644 --- a/PDP18B/pdp18b_g2tty.c +++ b/PDP18B/pdp18b_g2tty.c @@ -1,4 +1,4 @@ -/* pdp18b_g2tty.c: PDP-7/9 Bell Labs "GRAPHIC-2" subsystem (as a TTY!!) +/* pdp18b_g2tty.c: PDP-7/9 Bell Labs "GRAPHIC-2" subsystem as a TTY via TELNET from 13-Sep-15 version of pdp18b_tt1.c Copyright (c) 1993-2015, Robert M Supnik @@ -46,25 +46,57 @@ G2KB 043 GRAPHICS-2 keyboard G2BB 044 GRAPHICS-2 button box (lighted bush buttons) + 20-Mar-16 PLB Works, mostly 19-Mar-16 PLB Working (up to a screen full) 17-Mar-16 PLB Cloned from 13-Sep-15 version of pdp18b_tt1.c */ +/* + + * GRAPHICS-2 was vector graphics hardware, UNIX-7 uses it as a + * "Glass TTY" for a "second seat". + * + * This simulation ONLY handles text display; the one program + * found that uses the "capt" (capture?) call to set a user + * supplied display list will not work here. + * + * When the display buffer or screen is filled, the UNIX "display" + * driver lights "push button 7" (PB7), and waits for the user to + * press the button. UNIX then clears the screen, and output + * continues. If the program outputs a "Form Feed" character the + * display is also cleared. + * + * This simulation automatically presses PB7 when lit, without + * bothering the user (a more accurate simulation might prompt + * the user to press any key)! + */ + #include "pdp18b_defs.h" #ifdef GRAPHICS2 #include "sim_tmxr.h" -#include +int debug = 0; + +/* hardware registers */ uint8 g2kb_done = 0; /* keyboard flag */ uint32 g2kb_buf = 0; /* keyboard buffer */ - uint8 g2bb_flag = 0; /* button flag */ uint32 g2bb_bbuf = 0; /* button buffer */ uint32 g2bb_lbuf = 0; /* button lights buffer */ - uint32 g2out_addr = 0; /* display address */ -int32 g2out_count = 0; /* character count (not a hw reg) */ + +/* not hardware registers: */ +uint32 g2out_count = 0; uint8 g2out_stuffcr = 0; /* need to stuff a CR */ +uint g2out_which = 0; +#define OLD g2out_which +#define NEW (g2out_which ^ 1) + +#define MAXBUFCHARS 700 /* larger than kernel display list */ +static struct dspbuf { + uint16 count; + char buffer[MAXBUFCHARS]; /* 7-bit ASCII */ +} g2out_dspbufs[2]; /* terminal mux data */ TMLN g2_ldsc = { 0 }; /* line descriptor */ @@ -98,6 +130,9 @@ t_stat g2in_svc (UNIT *uptr); /* SIMH G2OUT DEVICE */ int32 g2d1_iot (int32 dev, int32 pulse, int32 dat); /* device 05 */ +static void g2out_clear (); +static void g2out_process_display_list (); +static void g2out_send_new (); /* both G2IN/G2OUT: */ t_stat g2_attach (UNIT *uptr, char *cptr); @@ -228,15 +263,22 @@ return dat; int32 g2bb_iot (int32 dev, int32 pulse, int32 dat) { if (pulse == 001) { /* "spb" -- skip on push button flag */ - if (g2bb_flag) + if (g2bb_flag) { dat = dat | IOT_SKP; + printf("bb: SKIP\r\n"); + } } else if (pulse == 002) /* "lpb"/"opb" -- or push buttons */ dat = dat | g2bb_bbuf; /* return buttons */ -else if (pulse == 004) /* "cpb" -- clear push button flag */ +else if (pulse == 004) { /* "cpb" -- clear push button flag */ g2bb_clr_flag (); /* clear flag */ -else if (pulse == 024) /* "wbl" -- write buttons lights */ + printf("bb: CLEAR\r\n"); + g2out_clear(); + } +else if (pulse == 024) { /* "wbl" -- write buttons lights */ g2bb_lbuf = dat; + printf("G2: wbl %#o\r\n", dat); /* TEMP */ + } return dat; } @@ -247,7 +289,8 @@ int32 ln, c; if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ return SCPE_OK; -if (g2bb_lbuf & 02000) { /* button 7 lit? */ +if (g2bb_lbuf & 02000) { /* button 7 lit? */ + if (g2bb_bbuf == 0) { printf("pressing pb7\r\n"); debug = 1; } g2bb_bbuf |= 02000; /* press it to clear screen! */ g2bb_set_flag (); } @@ -265,6 +308,8 @@ if (g2_ldsc.conn) { /* connected? */ c &= 0177; if (c == '\r') /* translate CR but not ESC! */ c = '\n'; + else if ((c & 0155) == 055) /* kernel swaps around -?/= */ + c ^= 020; /* pre-swap!! */ } g2kb_buf = c; g2kb_set_done (); @@ -272,7 +317,6 @@ if (g2_ldsc.conn) { /* connected? */ } /* connected */ else { /* not connected; next connection sees entire "screen" */ - g2out_count = 0; g2out_stuffcr = 0; } return SCPE_OK; @@ -322,30 +366,35 @@ return; * SIMH G2OUT (Display Output) DEVICE routines */ -/* helper to put 7-bit display character: - * characters are only consumed if TELNET user connected & output ready/enabled - */ -static void g2out_putchar(char c) -{ -if (g2_ldsc.conn && g2_ldsc.xmte) { /* connected, tx enabled? */ - - if (g2out_stuffcr) { /* need to stuff a CR? */ - tmxr_putc_ln (&g2_ldsc, '\r'); - g2out_stuffcr = 0; - if (!g2_ldsc.xmte) /* full? */ - return; /* yes: wait until next time */ - } - +/* helper to put 7-bit display character */ +static void g2pc(char c) { + //if (debug) putchar(c); tmxr_putc_ln (&g2_ldsc, c); - g2out_count++; /* consumed */ - - if (c == '\n') { /* was it a NL? */ - if (g2_ldsc.xmte) /* transmitter enabled? */ - tmxr_putc_ln (&g2_ldsc, '\r'); /* send CR now */ - else - g2out_stuffcr = 1; /* wait until next time */ - } } + +/* send a character from the display. adds CR after LF */ +/* returns 1 if "c" was sent; 0 means try again later */ +static int g2out_putchar(char c) +{ +if (!g2_ldsc.conn || !g2_ldsc.xmte) /* connected, tx enabled? */ + return 0; + +if (g2out_stuffcr) { /* need to stuff a CR? */ + g2pc ('\r'); + g2out_stuffcr = 0; + if (!g2_ldsc.xmte) /* full? */ + return 0; /* yes: wait until next time */ + } + +g2pc (c); + +if (c == '\n') { /* was it a NL? */ + if (g2_ldsc.xmte) /* transmitter enabled? */ + g2pc ('\r'); /* send CR now */ + else + g2out_stuffcr = 1; /* wait until next time */ + } +return 1; } /* Device 05 IOT routine */ @@ -356,38 +405,99 @@ int32 g2d1_iot (int32 dev, int32 pulse, int32 dat) * and display output is restarted periodicly in timer PI service code */ if (g2_ldsc.conn && g2_ldsc.xmte && pulse == 047) { /* conn&ready, "beg" */ - int32 n = g2out_count, i; g2out_addr = dat & 017777; + g2out_process_display_list(); + g2out_send_new(); + g2out_which ^= 1; /* swap buffers */ + } /* beg IOT */ +return dat; +} + +/**************** + * display buffer management/process + * we're informed when UNIX wants to clear the screen (PB7 lit) + * we then press the button + * UNIX does a "cpb" to ACK/clear the interrupt. + * + * *BUT* UNIX clears the screen when a FF (014) char is output, + * which just resets the buffer (and not issuing any IOTs) + */ + +static void g2out_clear() { + g2out_stuffcr = 0; + g2out_which = 0; + g2out_count = 0; + g2out_dspbufs[0].count = g2out_dspbufs[1].count = 0; +} + +/* process display list into "other" buffer */ +static void g2out_process_display_list() { + uint32 i; + struct dspbuf *dp = g2out_dspbufs + NEW; + + dp->count = 0; for (i = g2out_addr; i < 020000; i++) { uint32 w = M[i] & 0777777; int offset = i - g2out_addr; - if (w & 0400000) /* TRAP (stops display engine)? */ - break; + char c; + + if (w & 0400000) /* TRAP (always last word in buf) */ + return; + /* check first three words for expected setup commands */ if (offset < sizeof(g2_expect)/sizeof(g2_expect[0])) { if (w != g2_expect[offset]) { - /* TEMP: */ + /* XXX TEMP? send out on TELNET connection?? */ printf("G2: unexpected command at %#o: %#o expected %#o\r\n", i, w, g2_expect[offset]); - break; + return; } continue; } - if (w & 0300000) { /* not characters? */ - printf("G2: unexpected command at %#o: %#o\r\n", i, w); /* TEMP */ - break; + if (w & 0300000) { /* not characters? */ + /* XXX TEMP? send out on TELNET connection?? */ + printf("G2: unexpected command at %#o: %#o\r\n", i, w); + return; } - if (--n < 0) /* new? */ - g2out_putchar( (w>>7) & 0177 ); + c = (w >> 7) & 0177; + if (c) + dp->buffer[dp->count++] = c; + c = w & 0177; + if (c) + dp->buffer[dp->count++] = c; + } +} - if ((w & 0177) && --n < 0) /* char2 & new? */ - g2out_putchar( w & 0177 ); +/* figure out what to send + * truncates new->count to the number sent + */ +static void g2out_send_new() { + struct dspbuf *old = g2out_dspbufs + OLD; + struct dspbuf *new = g2out_dspbufs + NEW; + char *cp = new->buffer; + int cur = 0; - } /* for loop */ - if (n > 0) - g2out_count = 0; /* didn't see as much as last time? */ - } /* beg IOT */ -return dat; + /* nothing in newest refresh? + * COULD have had undisplayed stuff on last screen before it was cleared?? + * would need to have a transmit queue?? + */ + if (new->count == 0) + return; + + if (old->count && /* have old chars */ + memcmp(old->buffer, new->buffer, old->count) == 0) { /* and a prefix */ + cur = old->count; + cp += cur; + } + + /* loop for chars while connected & tx enabled */ + while (cur < new->count && g2_ldsc.conn && g2_ldsc.xmte) { + if (g2out_putchar(*cp)) { + cp++; + cur++; + } + } + new->count = cur; /* only remember number sent */ } /**************************************************************** @@ -417,8 +527,7 @@ g2bb_lbuf = 0; /* clear lights */ g2bb_clr_flag (); g2out_addr = 0; -g2out_count = 0; -g2out_stuffcr = 0; +g2out_clear(); sim_cancel (&g2out_unit); /* stop poll */ return SCPE_OK; }