From 8f26b58deb5239784126f9a2208d754440c25941 Mon Sep 17 00:00:00 2001 From: Richard Cornwell Date: Thu, 15 Feb 2018 18:07:19 -0500 Subject: [PATCH] SCP: Updated to current version. --- display/README | 166 ++ display/build_mingw.bat | 12 + display/carbon.c | 333 ++++ display/display.c | 1068 +++++++++++ display/display.h | 144 ++ display/gmakefile | 86 + display/sim_ws.c | 422 +++++ display/smakefile | 91 + display/test.c | 192 ++ display/type340.c | 706 ++++++++ display/vt11.c | 3824 +++++++++++++++++++++++++++++++++++++++ display/vt11.h | 144 ++ display/vtmacs.h | 297 +++ display/vttest.c | 1465 +++++++++++++++ display/win32.c | 422 +++++ display/ws.h | 65 + display/x11.c | 494 +++++ display/xy.c | 1036 +++++++++++ scp.c | 486 +++-- scp.h | 4 +- sim_console.c | 26 +- sim_defs.h | 10 +- sim_disk.c | 16 +- sim_ether.c | 110 +- sim_frontpanel.c | 288 +-- sim_frontpanel.h | 43 +- sim_sock.h | 1 + sim_timer.c | 166 +- sim_timer.h | 22 +- sim_tmxr.c | 235 ++- sim_tmxr.h | 2 + 31 files changed, 11846 insertions(+), 530 deletions(-) create mode 100644 display/README create mode 100644 display/build_mingw.bat create mode 100644 display/carbon.c create mode 100644 display/display.c create mode 100644 display/display.h create mode 100644 display/gmakefile create mode 100644 display/sim_ws.c create mode 100644 display/smakefile create mode 100644 display/test.c create mode 100644 display/type340.c create mode 100644 display/vt11.c create mode 100644 display/vt11.h create mode 100644 display/vtmacs.h create mode 100644 display/vttest.c create mode 100644 display/win32.c create mode 100644 display/ws.h create mode 100644 display/x11.c create mode 100644 display/xy.c diff --git a/display/README b/display/README new file mode 100644 index 0000000..321098e --- /dev/null +++ b/display/README @@ -0,0 +1,166 @@ +$Id: README,v 1.15 2004/02/09 07:20:18 phil Exp $ + +XY Display Simulation +Simulates XY plotting displays used on DEC PDP systems. + + Copyright (c) 2003-2004, Philip L. Budne and Douglas A. Gwyn + + 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 names of the authors 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. + +Phil Budne +Douglas A Gwyn +February 5, 2004 + +Designed for use with Bob Supnik's SIMH, but the code should be easily +portable, and usable standalone (see vttest.c for an example). + +Display code is provided for X11 (Unix/VMS) and Win32. +We're not GUI programmers, so the code is PRIMITIVE!! + +Started from VC8E simulator by Douglas W. Jones +(distribution 5, of Feb 4, 1997); + + This PDP8 Emulator was written by Douglas W. Jones at the + University of Iowa. It is distributed as freeware, of + uncertain function and uncertain utility. + +Original phosphor decay constants for Type 30 display from XMame 0.72.1 + +VT11 support GREATLY enhanced (and VT48 support added) and +other general improvements from Douglas A Gwyn. + +In the interest of fair play we have supplied two makefiles (neither +of which is named Makefile nor makefile), one which works under all +flavors of "make" (after necessary editing, in the traditional manner), +and one which functions only under the GNU version of make (sometimes +installed as "gmake", but the default "make" on many systems). We have +not added a third flavor which uses BSD make enhancements, because our +deeply held roots (over 40 combined years of Unix experience by the +authors) demand that things should work on all platforms. Both the +Linux and Windows worlds violate this simple credo (everything works so +long as you use OUR preferred software), and many current users may not +even realize that editing Makefiles used to be de rigeur. Since the +GNU environment is widely available and "gmake" has features that +support automatic configuration for multiple platforms, we have +supplied thr GNU-specific variant with the expectation that many users +will find it more convenient. You can copy or link whichever flavor of +makefile suits your taste to whichever spelling of "makefile" suits +your fancy, or invoke "make" with the -f flag specifying the desired +makefile. + +To compile test programs: +======================== +On Unix: + # edit smakefile to match your environment + make -f smakefile +or + gmake -f gmakefile + +On Win32 (using Cygwin); + make -f gmakefile WIN32=1 + +On Win32 (using MINGW): + # edit smakefile to match your environment + make -f smakefile +or + mingw32-make -f gmakefile WIN32=1 +or + execute build-mingw.bat in a DOS command window + +creates: + + munch: standalone simulation of PDP-1 munching squares; + examines console "test switches" (see next section) + + vt11: sequences through VT11/VS60 simulator test displays; + shows how the diplay-processor simulator can be used + from applications other than PDP-11 simulators + +Console switches: +================ + +Upto 18 simulated console switches, toggled by hitting keys: + +123 456 789 qwe rty uio + +space bar clears all switches. + +Spacewar Switches: +================= + +Key presses for simulated Spacewar control box switches; + +action player + 1 2 +rotate clockwise a k +rotate counter clockwise s l +fire engines d ; +launch torpedo f ' +hyperspace (both at once) as kl + +Light pen: +========= + +The light pen is active when any mouse button is held down. + +Mouse button 1 acts as a "tip switch" for models so equipped (VS60). +The light pen may be dragged while active. + +Too many compile time parameters: +================================ + +Read the comments in display.c for more explanations!! + +DISPLAY_TYPE default display type, one of: + DIS_VR14, DIS_VR17, DIS_VR20, DIS_VR48, DIS_TYPE30, DIS_TYPE340 + selects screen characteristics (phosphor, dimensions). + + Only affects programs which do not make an expicit + display_init() call. + +PIX_SCALE one of RES_FULL, RES_HALF, RES_QUARTER, RES_EIGHTH + selects default display scaling factor. + +PEN_RADIUS default radius of light pen in (scaled) pixels + +MAXELAPSED Upper limit in real microseconds between polls/delays +MINELAPSED Lower limit in real microseconds between polls/delays +MINDELAY Lower limit in real microseconds for attempted delay +MAXDELAY Upper limit in real microseconds for attempted delay +GAINSHIFT delay_check increment/decrement gain factor + +In display system support (x11.c, win32.c); + +PIX_SIZE selects displayed pixel size (default 1) + makes screen larger, useful when display scaled to small size + +Programming interface: +===================== + +see display.h + +Source repository: +================= + +Up-to-date Sources are available by anonymous CVS. +See http://www.ultimate.com/phil/xy/ diff --git a/display/build_mingw.bat b/display/build_mingw.bat new file mode 100644 index 0000000..38f5b48 --- /dev/null +++ b/display/build_mingw.bat @@ -0,0 +1,12 @@ +@echo off +rem $Id: build_mingw.bat,v 1.1 2004/01/25 17:48:03 phil Exp $ +rem Compile all test programs using MINGW make and gcc environment +rem +rem If needed, define the path for the MINGW bin directory. +rem (this should already be set if MINGW was installed correctly) +rem +gcc -v 1>NUL 2>NUL +if ERRORLEVEL 1 path C:\MinGW\bin;D:\MinGW\bin;E:\MinGW\bin;%path% +gcc -v 1>NUL 2>NUL +if ERRORLEVEL 1 echo "MinGW Environment Unavailable" +mingw32-make WIN32=1 -f gmakefile %1 %2 %3 %4 diff --git a/display/carbon.c b/display/carbon.c new file mode 100644 index 0000000..673b586 --- /dev/null +++ b/display/carbon.c @@ -0,0 +1,333 @@ +/* + * $Id: carbon.c,v 1.2 2005/08/06 21:09:03 phil Exp $ + * Mac OS X Carbon support for XY display simulator + * John Dundas + * December 2004 + * + * This is a simplistic driver under Mac OS Carbon for the XY display + * simulator. + * + * A more interesting driver would use OpenGL directly. + */ + +#include +#include +#include +#include +#include "ws.h" +#include "display.h" + +#include +#include + +#define ARRAYLEN(a) (sizeof (a) / sizeof (a[0])) + +#ifndef PIX_SIZE +#define PIX_SIZE 1 +#endif + +/* + * light pen location + * see ws.h for full description + */ +int ws_lp_x = -1; +int ws_lp_y = -1; + +static RGBColor blckColor = { 0x0000, 0x0000, 0x0000 }; +static RGBColor whteColor = { 0xFFFF, 0xFFFF, 0xFFFF }; +static WindowRef mainWind; +static RgnHandle rgn; +static MouseTrackingRef mouseRef; +static int xpixels, ypixels; +static int buttons = 0; /* tracks state of all buttons */ +static EventTargetRef EventDispatchTarget; + +void MyEventWait ( EventTimeout timeout ) /* double */ +{ + EventRef theEvent; + + if (ReceiveNextEvent (0, NULL, timeout, true, &theEvent) == noErr) { + SendEventToEventTarget (theEvent, EventDispatchTarget); + ReleaseEvent (theEvent); + } +} + +static pascal OSStatus doMouseEvent ( EventHandlerCallRef handlerRef, + EventRef event, + void *userData ) +{ + OSStatus err = eventNotHandledErr; + Point start; + GrafPtr prevPort; + + /* make sure the display is the current grafport */ + if (!QDSwapPort (GetWindowPort (mainWind), &prevPort)) + prevPort = NULL; + switch (GetEventKind (event)) { + case kEventMouseEntered: + if (ActiveNonFloatingWindow () != mainWind) + break; + SetThemeCursor (kThemeCrossCursor); + break; + case kEventMouseExited: + if (ActiveNonFloatingWindow () != mainWind) + break; + SetThemeCursor (kThemeArrowCursor); + break; + case kEventMouseDown: + GetEventParameter (event, kEventParamMouseLocation, + typeQDPoint, NULL, sizeof (typeQDPoint), NULL, &start); + GlobalToLocal (&start); + ws_lp_x = start.h; + ws_lp_y = ypixels - start.v; + display_lp_sw = 1; + break; + case kEventMouseUp: + display_lp_sw = 0; + ws_lp_x = ws_lp_y = -1; + break; + default: + break; + } + if (prevPort) + SetPort (prevPort); + return (err); +} + +static pascal OSStatus updateWindow ( EventHandlerCallRef handlerRef, + EventRef event, + void *userData ) +{ + OSStatus err = eventNotHandledErr; + + switch (GetEventKind (event)) { + case kEventWindowActivated: + /* update menus */ + break; + case kEventWindowClose: /* Override window close */ + err = noErr; + break; + case kEventWindowDrawContent: + display_repaint (); + err = noErr; + default: + break; + } + return (err); +} + +static pascal OSStatus doKbdEvent ( EventHandlerCallRef handlerRef, + EventRef event, + void *userData ) +{ + UInt32 c, m; + char key; + + GetEventParameter (event, kEventParamKeyCode, + typeUInt32, NULL, sizeof (typeUInt32), NULL, &c); + GetEventParameter (event, kEventParamKeyMacCharCodes, + typeChar, NULL, sizeof (typeChar), NULL, &key); + GetEventParameter (event, kEventParamKeyModifiers, + typeUInt32, NULL, sizeof (typeUInt32), NULL, &m); + + /* Keys with meta-modifiers are not allowed at this time. */ +#define KEY_MODIFIERS (cmdKey | optionKey | kEventKeyModifierFnMask) + if (m & KEY_MODIFIERS) + return (eventNotHandledErr); + switch (GetEventKind (event)) { + case kEventRawKeyRepeat: + case kEventRawKeyDown: + display_keydown (key); + break; + case kEventRawKeyUp: + display_keyup (key); + break; + default: + break; + } + return (noErr); +} + +int ws_init ( const char *crtname, /* crt type name */ + int xp, /* screen size in pixels */ + int yp, + int colors, /* colors to support (not used) */ + void *dptr) +{ + WindowAttributes windowAttrs; + Rect r; + CFStringRef str; + static MouseTrackingRegionID mouseID = { 'AAPL', 0 }; + static const EventTypeSpec moEvent[] = { + { kEventClassMouse, kEventMouseEntered }, + { kEventClassMouse, kEventMouseExited }, + { kEventClassMouse, kEventMouseDown }, + { kEventClassMouse, kEventMouseUp }, + }; + static const EventTypeSpec wuEvent[] = { + { kEventClassWindow, kEventWindowDrawContent }, + { kEventClassWindow, kEventWindowClose }, + { kEventClassWindow, kEventWindowActivated}, + }; + static const EventTypeSpec kdEvent[] = { + { kEventClassKeyboard, kEventRawKeyDown }, + { kEventClassKeyboard, kEventRawKeyRepeat }, + { kEventClassKeyboard, kEventRawKeyUp}, + }; + + + xpixels = xp; /* save screen size */ + ypixels = yp; + r.top = 100; r.left = 100; r.bottom = r.top + yp; r.right = r.left + xp; + + /* should check this r against GetQDGlobalsScreenBits (&screen); */ + windowAttrs = kWindowCollapseBoxAttribute | kWindowStandardHandlerAttribute; + if (CreateNewWindow (kDocumentWindowClass, windowAttrs, &r, &mainWind) != noErr) + return (0); + if (str = CFStringCreateWithCString (kCFAllocatorDefault, crtname, + kCFStringEncodingASCII)) { + SetWindowTitleWithCFString (mainWind, str); + CFRelease (str); + } + SetPortWindowPort (mainWind); +/* + * Setup to handle events + */ + EventDispatchTarget = GetEventDispatcherTarget (); + InstallEventHandler (GetWindowEventTarget (mainWind), + NewEventHandlerUPP (doMouseEvent), ARRAYLEN(moEvent), + (EventTypeSpec *) &moEvent, NULL, NULL); + InstallEventHandler (GetWindowEventTarget (mainWind), + NewEventHandlerUPP (updateWindow), ARRAYLEN(wuEvent), + (EventTypeSpec *) &wuEvent, NULL, NULL); + InstallEventHandler (GetWindowEventTarget (mainWind), + NewEventHandlerUPP (doKbdEvent), ARRAYLEN(kdEvent), + (EventTypeSpec *) &kdEvent, NULL, NULL); + /* create region to track cursor shape */ + r.top = 0; r.left = 0; r.bottom = yp; r.right = xp; + rgn = NewRgn (); + RectRgn (rgn, &r); + CloseRgn (rgn); + CreateMouseTrackingRegion (mainWind, rgn, NULL, + kMouseTrackingOptionsLocalClip, mouseID, NULL, NULL, &mouseRef); + + ShowWindow (mainWind); + RGBForeColor (&blckColor); + PaintRect (&r); + RGBBackColor (&blckColor); + return (1); +} + +void ws_shutdown (void) +{ +} + +void *ws_color_black (void) +{ + return (&blckColor); +} + +void *ws_color_white (void) +{ + return (&whteColor); +} + +void *ws_color_rgb ( int r, + int g, + int b ) +{ + RGBColor *color; + + if ((color = malloc (sizeof (RGBColor))) != NULL) { + color->red = r; + color->green = g; + color->blue = b; + } + return (color); +} + +/* put a point on the screen */ +void ws_display_point ( int x, + int y, + void *color ) +{ +#if PIX_SIZE != 1 + Rect r; +#endif + + if (x > xpixels || y > ypixels) + return; + + y = ypixels - y /* - 1 */; + +#if PIX_SIZE == 1 + SetCPixel (x, y, (color == NULL) ? &blckColor : color); +#else + r.top = y * PIX_SIZE; + r.left = x * PIX_SIZE; + r.bottom = (y + 1) * PIX_SIZE; + r.right = (x + 1) * PIX_SIZE; + + RGBForeColor ((color == NULL) ? &blckColor : color); + PaintRect (&r); +#endif +} + +void ws_sync (void) +{ + ; +} + +/* + * elapsed wall clock time since last call + * +INF on first call + */ + +struct elapsed_state { + struct timeval tvs[2]; + int new; +}; + +static unsigned long +elapsed(struct elapsed_state *ep) +{ + unsigned long val; + + gettimeofday(&ep->tvs[ep->new], NULL); + if (ep->tvs[!ep->new].tv_sec == 0) + val = ~0L; + else + val = ((ep->tvs[ep->new].tv_sec - ep->tvs[!ep->new].tv_sec) * 1000000 + + (ep->tvs[ep->new].tv_usec - ep->tvs[!ep->new].tv_usec)); + ep->new = !ep->new; + return val; +} + +/* called periodically */ +int ws_poll ( int *valp, + int maxusec ) +{ + static struct elapsed_state es; /* static to avoid clearing! */ + + elapsed(&es); /* start clock */ + do { + unsigned long e; + + MyEventWait (maxusec * kEventDurationMicrosecond); + e = elapsed(&es); + maxusec -= e; + } while (maxusec > 10000); /* 10ms */ + return (1); +} + +void ws_beep (void) +{ + SysBeep (3); +} + +/* public version, used by delay code */ +unsigned long os_elapsed (void) +{ + static struct elapsed_state es; + return (elapsed (&es)); +} diff --git a/display/display.c b/display/display.c new file mode 100644 index 0000000..714fd80 --- /dev/null +++ b/display/display.c @@ -0,0 +1,1068 @@ +/* + * $Id: display.c,v 1.56 2004/02/03 21:44:34 phil Exp - revised by DAG $ + * Simulator and host O/S independent XY display simulator + * Phil Budne + * September 2003 + * + * with changes by Douglas A. Gwyn, 05 Feb. 2004 + * + * started from PDP-8/E simulator vc8e.c; + * This PDP8 Emulator was written by Douglas W. Jones at the + * University of Iowa. It is distributed as freeware, of + * uncertain function and uncertain utility. + */ + +/* + * 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 names of the authors 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 +#include +#include +#include /* for USHRT_MAX */ +#include "ws.h" +#include "display.h" + +/* + * The user may select (at compile time) how big a window is used to + * emulate the display. Using smaller windows saves memory and screen space. + * + * Type 30 has 1024x1024 addressing, but only 512x512 visible points. + * VR14 has only 1024x768 visible points; VR17 has 1024x1024 visible points. + * VT11 supports 4096x4096 addressing, clipping to the lowest 1024x1024 region. + * VR48 has 1024x1024 visible points in the main display area and 128x1024 + * visible points in a menu area on the right-hand side (1152x1024 total). + * VT48 supports 8192x8192 (signed) main-area addressing, clipping to a + * 1024x1024 window which can be located anywhere within that region. + * (XXX -- That is what the VT11/VT48 manuals say; however, evidence suggests + * that the VT11 may actually support 8192x8192 (signed) addressing too.) + */ + +/* Define the default display type (if display_init() not called) */ +#ifndef DISPLAY_TYPE +#define DISPLAY_TYPE DIS_TYPE30 +#endif /* DISPLAY_TYPE not defined */ + +/* select a default resolution if display_init() not called */ +/* XXX keep in struct display? */ +#ifndef PIX_SCALE +#define PIX_SCALE RES_HALF +#endif /* PIX_SCALE not defined */ + +/* select a default light-pen hit radius if display_init() not called */ +#ifndef PEN_RADIUS +#define PEN_RADIUS 4 +#endif /* PEN_RADIUS not defined */ + +/* + * note: displays can have up to two different colors (eg VR20) + * each color can be made up of any number of phosphors + * with different colors and decay characteristics (eg Type 30) + */ + +#define ELEMENTS(X) (sizeof(X)/sizeof(X[0])) + +struct phosphor { + double red, green, blue; + double level; /* decay level (0.5 for half life) */ + double t_level; /* seconds to decay to level */ +}; + +struct color { + struct phosphor *phosphors; + int nphosphors; + int half_life; /* for refresh calc */ +}; + +struct display { + enum display_type type; + const char *name; + struct color *color0, *color1; + short xpoints, ypoints; +}; + +/* + * original phosphor constants from Raphael Nabet's XMame 0.72.1 PDP-1 sim. + * not even sure Type30 really used P17 (guess by Daniel P. B. Smith) + */ +static struct phosphor p17[] = { + {0.11, 0.11, 1.0, 0.5, 0.05}, /* fast blue */ + {1.0, 1.0, 0.11, 0.5, 0.20} /* slow yellow/green */ +}; +static struct color color_p17 = { p17, ELEMENTS(p17), 125000 }; + +/* green phosphor for VR14, VR17, VR20 */ +static struct phosphor p29[] = {{0.0260, 1.0, 0.00121, 0.5, 0.025}}; +struct color color_p29 = { p29, ELEMENTS(p29), 25000 }; + +static struct phosphor p40[] = { + /* P40 blue-white spot with yellow-green decay (.045s to 10%?) */ + {0.4, 0.2, 0.924, 0.5, 0.0135}, + {0.5, 0.7, 0.076, 0.5, 0.065} +}; +static struct color color_p40 = { p40, ELEMENTS(p40), 20000 }; + +/* "red" -- until real VR20 phosphor type/number/constants known */ +static struct phosphor pred[] = { {1.0, 0.37, 0.37, 0.5, 0.10} }; +static struct color color_red = { pred, ELEMENTS(pred), 100000 }; + +static struct display displays[] = { + /* + * TX-0 + * + * + * Unknown manufacturer + * + * 12" tube, + * maximum dot size ??? + * 50us point plot time (20,000 points/sec) + * P17 Phosphor??? Two phosphor layers: + * fast blue (.05s half life), and slow green (.2s half life) + * + * + */ + { DIS_TX0, "MIT TX-0", &color_p17, NULL, 512, 512 }, + + + /* + * Type 30 + * PDP-1/4/5/8/9/10 "Precision CRT" display system + * + * Raytheon 16ADP7A CRT? + * web searches for 16ADP7 finds useful information!! + * 16" tube, 14 3/8" square raster + * maximum dot size .015" + * 50us point plot time (20,000 points/sec) + * P17 Phosphor??? Two phosphor layers: + * fast blue (.05s half life), and slow green (.2s half life) + * 360 lb + * 7A at 115+-10V 60Hz + */ + { DIS_TYPE30, "Type 30", &color_p17, NULL, 1024, 1024 }, + + /* + * VR14 + * used w/ GT40/44, AX08, VC8E + * + * Viewable area 6.75" x 9" + * 12" diagonal + * brightness >= 30 fL + * dot size .02" (20 mils) + * settle time: + * full screen 18us to +/-1 spot diameter + * .1" change 1us to +/-.5 spot diameter + * weight 75lb + */ + { DIS_VR14, "VR14", &color_p29, NULL, 1024, 768 }, + + /* + * VR17 + * used w/ GT40/44, AX08, VC8E + * + * Viewable area 9.25" x 9.25" + * 17" diagonal + * dot size .02" (20 mils) + * brightness >= 25 fL + * phosphor: P39 doped for IR light pen use + * light pen: Type 375 + * weight 85lb + */ + { DIS_VR17, "VR17", &color_p29, NULL, 1024, 1024 }, + + /* + * VR20 + * on VC8E + * Two colors!! + */ + { DIS_VR20, "VR20", &color_p29, &color_red, 1024, 1024 }, + + /* + * VR48 + * (on VT48 in VS60) + * from Douglas A. Gwyn 23 Nov. 2003 + * + * Viewable area 12" x 12", plus 1.5" x 12" menu area on right-hand side + * 21" diagonal + * dot size <= .01" (10 mils) + * brightness >= 31 fL + * phosphor: P40 (blue-white fluorescence with yellow-green phosphorescence) + * light pen: Type 377A (with tip switch) + * driving circuitry separate + * (normally under table on which CRT is mounted) + */ + { DIS_VR48, "VR48", &color_p40, NULL, 1024+VR48_GUTTER+128, 1024 }, + + /* + * Type 340 Display system + * on PDP-4/6/7/9/10 + * + * 1024x1024 + * 9 3/8" raster (.01" dot pitch) + * 0,0 at lower left + * 8 intensity levels + */ + { DIS_TYPE340, "Type 340", &color_p17, NULL, 1024, 1024 } +}; + +/* + * Unit time (in microseconds) used to store display point time to + * live at current aging level. If this is too small, delay values + * cannot fit in an unsigned short. If it is too large all pixels + * will age at once. Perhaps a suitable value should be calculated at + * run time? When display_init() calculates refresh_interval it + * sanity checks for both cases. + */ +#define DELAY_UNIT 250 + +/* levels to display in first half-life; determines refresh rate */ +#ifndef LEVELS_PER_HALFLIFE +#define LEVELS_PER_HALFLIFE 4 +#endif + +/* after 5 half lives (.5**5) remaining intensity is 3% of original */ +#ifndef HALF_LIVES_TO_DISPLAY +#define HALF_LIVES_TO_DISPLAY 5 +#endif + +/* + * refresh_rate is number of times per (simulated) second a pixel is + * aged to next lowest intensity level. + * + * refresh_rate = ((1e6*LEVELS_PER_HALFLIFE)/PHOSPHOR_HALF_LIFE) + * refresh_interval = 1e6/DELAY_UNIT/refresh_rate + * = PHOSPHOR_HALF_LIFE/LEVELS_PER_HALF_LIFE + * intensities = (HALF_LIVES_TO_DISPLAY*PHOSPHOR_HALF_LIFE)/refresh_interval + * = HALF_LIVES_TO_DISPLAY*LEVELS_PER_HALFLIFE + * + * See also comments on display_age() + * + * Try to keep LEVELS_PER_HALFLIFE*HALF_LIVES_TO_DISPLAY*NLEVELS <= 192 + * to run on 8-bit (256 color) displays! + */ + +/* + * number of aging periods to display a point for + */ +#define NTTL (HALF_LIVES_TO_DISPLAY*LEVELS_PER_HALFLIFE) + +/* + * maximum (initial) TTL for a point. + * TTL's are stored 1-based + * (a stored TTL of zero means the point is off) + */ +#define MAXTTL NTTL + +/* + * number of drawing intensity levels + */ +#define NLEVELS (DISPLAY_INT_MAX-DISPLAY_INT_MIN+1) + +#define MAXLEVEL (NLEVELS-1) + +/* + * Display Device Implementation + */ + +/* + * Each point on the display is represented by a "struct point". When + * a point isn't dark (intensity > 0), it is linked into a circular, + * doubly linked delta queue (a priority queue where "delay" + * represents the time difference from the previous entry (if any) in + * the queue. + * + * All points are aged refresh_rate times/second, each time moved to the + * next (logarithmically) lower intensity level. When display_age() is + * called, only the entries which have expired are processed. Calling + * display_age() often allows spreading out the workload. + * + * An alternative would be to have intensity levels represent linear + * decreases in intensity, and have the decay time at each level change. + * Inverting the decay function for a multi-component phosphor may be + * tricky, and the two different colors would need different time tables. + * Furthermore, it would require finding the correct location in the + * queue when adding a point (currently only need to add points at end) + */ + +/* + * 12 bytes/entry on 32-bit system when REFRESH_RATE > 15 + * (requires 3MB for 512x512 display). + */ + +typedef unsigned short delay_t; +#define DELAY_T_MAX USHRT_MAX + +struct point { + struct point *next; /* next entry in queue */ + struct point *prev; /* prev entry in queue */ + delay_t delay; /* delta T in DELAY_UNITs */ + unsigned char ttl; /* zero means off, not linked in */ + unsigned char level : 7; /* intensity level */ + unsigned char color : 1; /* for VR20 (two colors) */ +}; + +static struct point *points; /* allocated array of points */ +static struct point _head; +#define head (&_head) + +/* + * time span of all entries in queue + * should never exceed refresh_interval + * (should be possible to make this a delay_t) + */ +static long queue_interval; + +/* convert X,Y to a "struct point *" */ +#define P(X,Y) (points + (X) + ((Y)*(size_t)xpixels)) + +/* convert "struct point *" to X and Y */ +#define X(P) (((P) - points) % xpixels) +#define Y(P) (((P) - points) / xpixels) + +static int initialized = 0; + +/* + * global set by O/S display level to indicate "light pen tip switch activated" + * (This is used only by the VS60 emulation, also by vttest to change patterns) + */ +unsigned char display_lp_sw = 0; + +/* + * global set by DR11-C simulation when DR device enabled; deactivates + * light pen and instead reports mouse coordinates as Talos digitizer + * data via DR11-C + */ +unsigned char display_tablet = 0; + +/* + * can be changed with display_lp_radius() + */ +static long scaled_pen_radius_squared; + +/* run-time -- set by display_init() */ +static int xpoints, ypoints; +static int xpixels, ypixels; +static int refresh_rate; +static int refresh_interval; +static int ncolors; +static enum display_type display_type; +static int scale; + +/* + * relative brightness for each display level + * (all but last must be less than 1.0) + */ +static float level_scale[NLEVELS]; + +/* + * table of pointer to window system "colors" + * for painting each age level, intensity level and beam color + */ +void *colors[2][NLEVELS][NTTL]; + +void +display_lp_radius(int r) +{ + r /= scale; + scaled_pen_radius_squared = r * r; +} + +/* + * from display_age and display_point + * since all points age at the same rate, + * only adds points at end of list. + */ +static void +queue_point(struct point *p) +{ + int d; + + d = refresh_interval - queue_interval; + queue_interval += d; + /* queue_interval should now be == refresh_interval */ + +#ifdef PARANOIA + if (p->ttl == 0 || p->ttl > MAXTTL) + printf("queuing %d,%d level %d!\n", X(p), Y(p), p->level); + if (d > DELAY_T_MAX) + printf("queuing %d,%d delay %d!\n", X(p), Y(p), d); + if (queue_interval > DELAY_T_MAX) + printf("queue_interval (%d) > DELAY_T_MAX (%d)\n", + (int)queue_interval, DELAY_T_MAX); +#endif /* PARANOIA defined */ + + p->next = head; + p->prev = head->prev; + + head->prev->next = p; + head->prev = p; + + p->delay = d; +} + +/* + * here to to dynamically adjust interval for examination + * of elapsed vs. simulated time, and fritter away + * any extra wall-clock time without eating CPU + */ + +/* + * more parameters! + */ + +/* + * upper bound for elapsed time between elapsed time checks. + * if more than MAXELAPSED microseconds elapsed while simulating + * delay_check simulated microseconds, decrease delay_check. + */ +#define MAXELAPSED 100000 /* 10Hz */ + +/* + * lower bound for elapsed time between elapsed time checks. + * if fewer than MINELAPSED microseconds elapsed while simulating + * delay_check simulated microseconds, increase delay_check. + */ +#define MINELAPSED 50000 /* 20Hz */ + +/* + * upper bound for delay (sleep/poll). + * If difference between elapsed time and simulated time is + * larger than MAXDELAY microseconds, decrease delay_check. + * + * since delay is elapsed time - simulated time, MAXDELAY + * should be <= MAXELAPSED + */ +#ifndef MAXDELAY +#define MAXDELAY 100000 /* 100ms */ +#endif /* MAXDELAY not defined */ + +/* + * lower bound for delay (sleep/poll). + * If difference between elapsed time and simulated time is + * smaller than MINDELAY microseconds, increase delay_check. + * + * since delay is elapsed time - simulated time, MINDELAY + * should be <= MINELAPSED + */ +#ifndef MINDELAY +#define MINDELAY 50000 /* 50ms */ +#endif /* MINDELAY not defined */ + +/* + * Initial amount of simulated time to elapse before polling. + * Value is very low to ensure polling occurs on slow systems. + * Fast systems should ramp up quickly. + */ +#ifndef INITIAL_DELAY_CHECK +#define INITIAL_DELAY_CHECK 1000 /* 1ms */ +#endif /* INITIAL_DELAY_CHECK */ + +/* + * gain factor (2**-GAINSHIFT) for adjustment of adjustment + * of delay_check + */ +#ifndef GAINSHIFT +#define GAINSHIFT 3 /* gain=0.125 (12.5%) */ +#endif /* GAINSHIFT not defined */ + +static void +display_delay(int t, int slowdown) +{ + /* how often (in simulated us) to poll/check for delay */ + static unsigned long delay_check = INITIAL_DELAY_CHECK; + + /* accumulated simulated time */ + static unsigned long sim_time = 0; + unsigned long elapsed; + long delay; + + sim_time += t; + if (sim_time < delay_check) + return; + + elapsed = os_elapsed(); /* read and reset elapsed timer */ + if (elapsed == ~0L) { /* first time thru? */ + slowdown = 0; /* no adjustments */ + elapsed = sim_time; + } + + /* + * get delta between elapsed (real) time, and simulated time. + * if simulated time running faster, we need to slow things down (delay) + */ + if (slowdown) + delay = sim_time - elapsed; + else + delay = 0; /* just poll */ + +#ifdef DEBUG_DELAY2 + printf("sim %d elapsed %d delay %d\r\n", sim_time, elapsed, delay); +#endif + + /* + * Try to keep the elapsed (real world) time between checks for + * delay (and poll for window system events) bounded between + * MAXELAPSED and MINELAPSED. Also tries to keep + * delay/poll time bounded between MAXDELAY and MINDELAY -- large + * delays make the simulation spastic, while very small ones are + * inefficient (too many system calls) and tend to be inaccurate + * (operating systems have a certain granularity for time + * measurement, and when you try to sleep/poll for very short + * amounts of time, the noise will dominate). + * + * delay_check period may be adjusted often, and oscillate. There + * is no single "right value", the important things are to keep + * the delay time and max poll intervals bounded, and responsive + * to system load. + */ + if (elapsed > MAXELAPSED || delay > MAXDELAY) { + /* too much elapsed time passed, or delay too large; shrink interval */ + if (delay_check > 1) { + delay_check -= delay_check>>GAINSHIFT; +#ifdef DEBUG_DELAY + printf("reduced period to %d\r\n", delay_check); +#endif /* DEBUG_DELAY defined */ + } + } + else + if ((elapsed < MINELAPSED) || (slowdown && (delay < MINDELAY))) { + /* too little elapsed time passed, or delta very small */ + int gain = delay_check>>GAINSHIFT; + + if (gain == 0) + gain = 1; /* make sure some change made! */ + delay_check += gain; +#ifdef DEBUG_DELAY + printf("increased period to %d\r\n", delay_check); +#endif /* DEBUG_DELAY defined */ + } + if (delay < 0) + delay = 0; + /* else if delay < MINDELAY, clamp at MINDELAY??? */ + + /* poll for window system events and/or delay */ + ws_poll(NULL, delay); + + sim_time = 0; /* reset simulated time clock */ + + /* + * delay (poll/sleep) time included in next "elapsed" period + * (clock not reset after a delay) + */ +} /* display_delay */ + +/* + * here periodically from simulator to age pixels. + * + * calling often with small values will age a few pixels at a time, + * and assist with graceful aging of display, and pixel aging. + * + * values should be smaller than refresh_interval! + * + * returns true if anything on screen changed. + */ + +int +display_age(int t, /* simulated us since last call */ + int slowdown) /* slowdown to simulated speed */ +{ + struct point *p; + static int elapsed = 0; + static int refresh_elapsed = 0; /* in units of DELAY_UNIT bounded by refresh_interval */ + int changed; + + if (!initialized && !display_init(DISPLAY_TYPE, PIX_SCALE, NULL)) + return 0; + + if (slowdown) + display_delay(t, slowdown); + + changed = 0; + + elapsed += t; + if (elapsed < DELAY_UNIT) + return 0; + + t = elapsed / DELAY_UNIT; + elapsed %= DELAY_UNIT; + + ++refresh_elapsed; + if (refresh_elapsed >= refresh_interval) { + display_sync (); + refresh_elapsed = 0; + } + + while ((p = head->next) != head) { + int x, y; + + /* look at oldest entry */ + if (p->delay > t) { /* further than our reach? */ + p->delay -= t; /* update head */ + queue_interval -= t; /* update span */ + break; /* quit */ + } + + x = X(p); + y = Y(p); +#ifdef PARANOIA + if (p->ttl == 0) + printf("BUG: age %d,%d ttl zero\n", x, y); +#endif /* PARANOIA defined */ + + /* dequeue point */ + p->prev->next = p->next; + p->next->prev = p->prev; + + t -= p->delay; /* lessen our reach */ + queue_interval -= p->delay; /* update queue span */ + + ws_display_point(x, y, colors[p->color][p->level][--p->ttl]); + changed = 1; + + /* queue it back up, unless we just turned it off! */ + if (p->ttl > 0) + queue_point(p); + } + return changed; +} /* display_age */ + +/* here from window system */ +void +display_repaint(void) { + struct point *p; + int x, y; + /* + * bottom to top, left to right. + */ + for (p = points, y = 0; y < ypixels; y++) + for (x = 0; x < xpixels; p++, x++) + if (p->ttl) + ws_display_point(x, y, colors[p->color][p->level][p->ttl-1]); + ws_sync(); +} + +/* (0,0) is lower left */ +static int +intensify(int x, /* 0..xpixels */ + int y, /* 0..ypixels */ + int level, /* 0..MAXLEVEL */ + int color) /* for VR20! 0 or 1 */ +{ + struct point *p; + int bleed; + + if (x < 0 || x >= xpixels || y < 0 || y >= ypixels) + return 0; /* limit to display */ + + p = P(x,y); + if (p->ttl) { /* currently lit? */ +#ifdef LOUD + printf("%d,%d old level %d ttl %d new %d\r\n", + x, y, p->level, p->ttl, level); +#endif /* LOUD defined */ + + /* unlink from delta queue */ + p->prev->next = p->next; + + if (p->next == head) + queue_interval -= p->delay; + else + p->next->delay += p->delay; + p->next->prev = p->prev; + } + + bleed = 0; /* no bleeding for now */ + + /* EXP: doesn't work... yet */ + /* if "recently" drawn, same or brighter, same color, make even brighter */ + if (p->ttl >= MAXTTL*2/3 && + level >= p->level && + p->color == color && + level < MAXLEVEL) + level++; + + /* + * this allows a dim beam to suck light out of + * a recently drawn bright spot!! + */ + if (p->ttl != MAXTTL || p->level != level || p->color != color) { + p->ttl = MAXTTL; + p->level = level; + p->color = color; /* save color even if monochrome */ + ws_display_point(x, y, colors[p->color][p->level][p->ttl-1]); + } + + queue_point(p); /* put at end of list */ + return bleed; +} + +int +display_point(int x, /* 0..xpixels (unscaled) */ + int y, /* 0..ypixels (unscaled) */ + int level, /* DISPLAY_INT_xxx */ + int color) /* for VR20! 0 or 1 */ +{ + long lx, ly; + + if (!initialized && !display_init(DISPLAY_TYPE, PIX_SCALE, NULL)) + return 0; + + /* scale x and y to the displayed number of pixels */ + /* handle common cases quickly */ + if (scale > 1) { + if (scale == 2) { + x >>= 1; + y >>= 1; + } + else { + x /= scale; + y /= scale; + } + } + +#if DISPLAY_INT_MIN > 0 + level -= DISPLAY_INT_MIN; /* make zero based */ +#endif + intensify(x, y, level, color); + /* no bleeding for now (used to recurse for neighbor points) */ + + if (ws_lp_x == -1 || ws_lp_y == -1) + return 0; + + lx = x - ws_lp_x; + ly = y - ws_lp_y; + return lx*lx + ly*ly <= scaled_pen_radius_squared; +} /* display_point */ + +/* + * calculate decay color table for a phosphor mixture + * must be called AFTER refresh_rate initialized! + */ +static void +phosphor_init(struct phosphor *phosphors, int nphosphors, int color) +{ + int ttl; + + /* for each display ttl level; newest to oldest */ + for (ttl = NTTL-1; ttl > 0; ttl--) { + struct phosphor *pp; + double rr, rg, rb; /* real values */ + + /* fractional seconds */ + double t = ((double)(NTTL-1-ttl))/refresh_rate; + + int ilevel; /* intensity levels */ + int p; + + /* sum over all phosphors in mixture */ + rr = rg = rb = 0.0; + for (pp = phosphors, p = 0; p < nphosphors; pp++, p++) { + double decay = pow(pp->level, t/pp->t_level); + + rr += decay * pp->red; + rg += decay * pp->green; + rb += decay * pp->blue; + } + + /* scale for brightness for each intensity level */ + for (ilevel = MAXLEVEL; ilevel >= 0; ilevel--) { + int r, g, b; + void *cp; + + /* + * convert to 16-bit integer; clamp at 16 bits. + * this allows the sum of brightness factors across phosphors + * for each of R G and B to be greater than 1.0 + */ + + r = (int)(rr * level_scale[ilevel] * 0xffff); + if (r > 0xffff) r = 0xffff; + + g = (int)(rg * level_scale[ilevel] * 0xffff); + if (g > 0xffff) g = 0xffff; + + b = (int)(rb * level_scale[ilevel] * 0xffff); + if (b > 0xffff) b = 0xffff; + + cp = ws_color_rgb(r, g, b); + if (!cp) { /* allocation failed? */ + if (ttl == MAXTTL-1) { /* brand new */ + if (ilevel == MAXLEVEL) /* highest intensity? */ + cp = ws_color_white(); /* use white */ + else + cp = colors[color][ilevel+1][ttl]; /* use next lvl */ + } /* brand new */ + else if (r + g + b >= 0xffff*3/3) /* light-ish? */ + cp = colors[color][ilevel][ttl+1]; /* use previous TTL */ + else + cp = ws_color_black(); + } + colors[color][ilevel][ttl] = cp; + } /* for each intensity level */ + } /* for each TTL */ +} /* phosphor_init */ + +static struct display * +find_type(enum display_type type) +{ + int i; + struct display *dp; + + for (i = 0, dp = displays; i < ELEMENTS(displays); i++, dp++) + if (dp->type == type) + return dp; + return NULL; +} + +int +display_init(enum display_type type, int sf, void *dptr) +{ + static int init_failed = 0; + struct display *dp; + int half_life; + int i; + + if (initialized) { + /* cannot change type once started */ + /* XXX say something???? */ + return type == display_type; + } + + if (init_failed) + return 0; /* avoid thrashing */ + + init_failed = 1; /* assume the worst */ + dp = find_type(type); + if (!dp) { + fprintf(stderr, "Unknown display type %d\r\n", (int)type); + goto failed; + } + + /* Initialize display list */ + head->next = head->prev = head; + + display_type = type; + scale = sf; + + xpoints = dp->xpoints; + ypoints = dp->ypoints; + + /* increase scale factor if won't fit on desktop? */ + xpixels = xpoints / scale; + ypixels = ypoints / scale; + + /* set default pen radius now that scale is set */ + display_lp_radius(PEN_RADIUS); + + ncolors = 1; + /* + * use function to calculate from looking at avg (max?) + * of phosphor half lives??? + */ +#define COLOR_HALF_LIFE(C) ((C)->half_life) + + half_life = COLOR_HALF_LIFE(dp->color0); + if (dp->color1) { + if (dp->color1->half_life > half_life) + half_life = COLOR_HALF_LIFE(dp->color1); + ncolors++; + } + + /* before phosphor_init; */ + refresh_rate = (1000000*LEVELS_PER_HALFLIFE)/half_life; + refresh_interval = 1000000/DELAY_UNIT/refresh_rate; + + /* + * sanity check refresh_interval + * calculating/selecting DELAY_UNIT at runtime might avoid this! + */ + + /* must be non-zero; interval of 1 means all pixels will age at once! */ + if (refresh_interval < 1) { + /* decrease DELAY_UNIT? */ + fprintf(stderr, "NOTE! refresh_interval too small: %d\r\n", + refresh_interval); + + /* dunno if this is a good idea, but might be better than dying */ + refresh_interval = 1; + } + + /* point lifetime in DELAY_UNITs will not fit in p->delay field! */ + if (refresh_interval > DELAY_T_MAX) { + /* increase DELAY_UNIT? */ + fprintf(stderr, "bad refresh_interval %d > DELAY_T_MAX %d\r\n", + refresh_interval, DELAY_T_MAX); + goto failed; + } + + /* + * before phosphor_init; + * set up relative brightness of display intensity levels + * (could differ for different hardware) + * + * linear for now. boost factor insures low intensities are visible + */ +#define BOOST 5 + for (i = 0; i < NLEVELS; i++) + level_scale[i] = ((float)i+1+BOOST)/(NLEVELS+BOOST); + + points = (struct point *)calloc((size_t)xpixels, + ypixels * sizeof(struct point)); + if (!points) + goto failed; + + if (!ws_init(dp->name, xpixels, ypixels, ncolors, dptr)) + goto failed; + + phosphor_init(dp->color0->phosphors, dp->color0->nphosphors, 0); + + if (dp->color1) + phosphor_init(dp->color1->phosphors, dp->color1->nphosphors, 1); + + initialized = 1; + init_failed = 0; /* hey, we made it! */ + return 1; + + failed: + fprintf(stderr, "Display initialization failed\r\n"); + return 0; +} + +void +display_reset(void) +{ + /* XXX tear down window? just clear it? */ +} + +void +display_sync(void) +{ + ws_poll (NULL, 0); + ws_sync (); +} + +void +display_beep(void) +{ + ws_beep(); +} + +int +display_xpoints(void) +{ + return xpoints; +} + +int +display_ypoints(void) +{ + return ypoints; +} + +int +display_scale(void) +{ + return scale; +} + +/* + * handle keyboard events + * + * data switches; 18 -- enough for PDP-1/4/7/9/15 (for munching squares!) + * 123 456 789 qwe rty uio + * bit toggled on key up + * all cleared on space + * + * spacewar switches; bit high as long as key down + * asdf kl;' + * just where PDP-1 spacewar expects them! + * key mappings same as MIT Media Lab Java PDP-1 simulator + * + */ +unsigned long spacewar_switches = 0; + +/* here from window system */ +void +display_keydown(int k) +{ + switch (k) { + case 'f': case 'F': spacewar_switches |= 01; break; /* torpedos */ + case 'd': case 'D': spacewar_switches |= 02; break; /* engines */ + case 'a': case 'A': spacewar_switches |= 04; break; /* rotate R */ + case 's': case 'S': spacewar_switches |= 010; break; /* rotate L */ + case '\'': case '"': spacewar_switches |= 040000; break; /* torpedos */ + case ';': case ':': spacewar_switches |= 0100000; break; /* engines */ + case 'k': case 'K': spacewar_switches |= 0200000; break; /* rotate R */ + case 'l': case 'L': spacewar_switches |= 0400000; break; /* rotate L */ + default: return; + } +} + +/* here from window system */ +void +display_keyup(int k) +{ + unsigned long test_switches = cpu_get_switches(); + + /* fetch console switches from simulator? */ + switch (k) { + case 'f': case 'F': spacewar_switches &= ~01; return; + case 'd': case 'D': spacewar_switches &= ~02; return; + case 'a': case 'A': spacewar_switches &= ~04; return; + case 's': case 'S': spacewar_switches &= ~010; return; + + case '\'': case '"': spacewar_switches &= ~040000; return; + case ';': case ':': spacewar_switches &= ~0100000; return; + case 'k': case 'K': spacewar_switches &= ~0200000; return; + case 'l': case 'L': spacewar_switches &= ~0400000; return; + + case '1': test_switches ^= 1<<17; break; + case '2': test_switches ^= 1<<16; break; + case '3': test_switches ^= 1<<15; break; + + case '4': test_switches ^= 1<<14; break; + case '5': test_switches ^= 1<<13; break; + case '6': test_switches ^= 1<<12; break; + + case '7': test_switches ^= 1<<11; break; + case '8': test_switches ^= 1<<10; break; + case '9': test_switches ^= 1<<9; break; + + case 'q': case 'Q': test_switches ^= 1<<8; break; + case 'w': case 'W': test_switches ^= 1<<7; break; + case 'e': case 'E': test_switches ^= 1<<6; break; + + case 'r': case 'R': test_switches ^= 1<<5; break; + case 't': case 'T': test_switches ^= 1<<4; break; + case 'y': case 'Y': test_switches ^= 1<<3; break; + + case 'u': case 'U': test_switches ^= 1<<2; break; + case 'i': case 'I': test_switches ^= 1<<1; break; + case 'o': case 'O': test_switches ^= 1; break; + + case ' ': test_switches = 0; break; + default: return; + } + cpu_set_switches(test_switches); +} diff --git a/display/display.h b/display/display.h new file mode 100644 index 0000000..96bcc07 --- /dev/null +++ b/display/display.h @@ -0,0 +1,144 @@ +/* + * $Id: display.h,v 1.13 2004/01/24 08:34:33 phil Exp $ + * interface to O/S independent layer of XY display simulator + * Phil Budne + * September 2003 + * + * Changes from Douglas A. Gwyn, Jan 12, 2004 + */ + +/* + * 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 names of the authors 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. + */ + +/* + * known display types + */ +enum display_type { + DIS_VR14 = 14, + DIS_VR17 = 17, + DIS_VR20 = 20, + DIS_TYPE30 = 30, + DIS_TX0 = 33, + DIS_VR48 = 48, + DIS_TYPE340 = 340 +}; + +/* + * display scale factors + */ +#define RES_FULL 1 +#define RES_HALF 2 +#define RES_QUARTER 4 +#define RES_EIGHTH 8 + +/* + * must be called before first call to display_age() + * (but called implicitly by display_point()) + */ +extern int display_init(enum display_type, int scale, void *dptr); + +/* return size of virtual display */ +extern int display_xpoints(void); +extern int display_ypoints(void); + +/* virtual points between display and menu sections */ +#define VR48_GUTTER 8 /* just a guess */ + +/* conversion factor from virtual points and displayed pixels */ +extern int display_scale(void); + +/* + * simulate passage of time; first argument is simulated microseconds elapsed, + * second argument is flag to slow down simulated speed + * see comments in display.c for why you should call it often!! + * Under X11 polls for window events!! + */ +extern int display_age(int,int); + +/* + * display intensity levels. + * always at least 8 (for VT11/VS60) -- may be mapped internally + */ +#define DISPLAY_INT_MAX 7 +#define DISPLAY_INT_MIN 0 /* lowest "on" level */ + +/* + * plot a point; argumen ts are x, y, intensity, color (0/1) + * returns true if light pen active (mouse button down) + * at (or very near) this location. + * + * Display initialized on first call. + */ +extern int display_point(int,int,int,int); + +/* + * force window system to output bits to screen; + * call after adding points, or aging the screen + * collect any window system input (mouse or keyboard) + */ +extern void display_sync(void); + +/* + * currently a noop + */ +extern void display_reset(void); + +/* + * ring the bell + */ +extern void display_beep(void); + +/* + * Set light-pen radius; maximum radius in display coordinates + * from a "lit" location that the light pen will see. + */ +extern void display_lp_radius(int); + +/* + * set by simulated spacewar switch box switches + * 18 bits (only high 4 and low 4 used) + */ +extern unsigned long spacewar_switches; + +/* + * light pen "tip switch" activated (for VS60 emulation etc.) + * should only be set from "driver" (window system layer) + */ +extern unsigned char display_lp_sw; + +/* + * deactivates light pen + * (SIMH DR11-C simulation when initialized sets this and + * then reports mouse coordinates as Talos digitizer data) + */ +extern unsigned char display_tablet; + +/* + * users of this library are expected to provide these calls. + * simulator will set 18 simulated switches. + */ +extern unsigned long cpu_get_switches(void); /* get current switch state */ +extern void cpu_set_switches(unsigned long); /* set switches */ diff --git a/display/gmakefile b/display/gmakefile new file mode 100644 index 0000000..4b5b981 --- /dev/null +++ b/display/gmakefile @@ -0,0 +1,86 @@ +# $Id: gmakefile,v 1.24 2005/11/05 02:06:14 phil Exp $ + +# (GNU) Makefile for test programs under Unix/X11 and Win32 +# +# Unix: +# edit Unix defs to fit your compiler/library environment, then +# gmake -f gmakefile +# or if GNU make is the default: +# make -f gmakefile +# +# OS X (Carbon) +# make -f gmakefile OSX=1 +# +# Win32 (Cygwin) +# make -f gmakefile WIN32=1 +# +# Win32 (MINGW): +# mingw32-make -f gmakefile WIN32=1 + +DISP_DEFS=-DTEST_DIS=DIS_VR48 -DTEST_RES=RES_HALF # -DDEBUG_VT11 + +ifeq ($(WIN32),) +ifeq ($(OSX),) +#Unix environments +X11BASE=/usr/X11R6 +X11LIBDIR=$(X11BASE)/lib +X11INCDIR=$(X11BASE)/include +LIBS=-L$(X11LIBDIR) -lXt -lX11 -lm +OSFLAGS=-I$(X11INCDIR) +DRIVER=x11.o +EXT= +else +DRIVER=carbon.o +LIBS=-framework Carbon +endif +else +#Win32 environments +LIBS=-lgdi32 +OSFLAGS= +DRIVER=win32.o +EXT=.exe +endif + +#PROF=-g # -pg +OPT=-O2 +CFLAGS=$(OPT) $(PROF) $(OSFLAGS) $(DISP_DEFS) +CC=gcc -Wunused +LDFLAGS=$(PROF) + +ALL= munch$(EXT) vt11$(EXT) +ALL: $(ALL) + +# munching squares; see README file for +# how to use console switches + +MUNCH=$(DRIVER) xy.o test.o +munch$(EXT): $(MUNCH) + $(CC) $(LDFLAGS) -o munch$(EXT) $(MUNCH) $(LIBS) + +VT11=$(DRIVER) vt11.o vttest.o xy.o +vt11$(EXT): $(VT11) + $(CC) $(LDFLAGS) -o vt11$(EXT) $(VT11) $(LIBS) + +xy.o: xy.h ws.h +vt11.o: xy.h vt11.h +x11.o: ws.h xy.h +carbon.o: ws.h +win32.o: ws.h +test.o: xy.h vt11.h +vttest.o: xy.h vt11.h vtmacs.h + +clean: +ifeq ($(WIN32),) + rm -f *.o *~ .#* +else + if exist *.o del /q *.o + if exist *~ del /q *~ + if exist .#* del /q .#* +endif + +clobber: clean +ifeq ($(WIN32),) + rm -f $(ALL) +else + if exist *.exe del /q *.exe +endif diff --git a/display/sim_ws.c b/display/sim_ws.c new file mode 100644 index 0000000..7c88f24 --- /dev/null +++ b/display/sim_ws.c @@ -0,0 +1,422 @@ +/* + * simh sim_video support for XY display simulator + * Mark Pizzolato + * January 2016 + * Based on win32.c module by Phil Budne + */ + +/* + * Copyright (c) 2016, Mark Pizzolato + * + * 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 names of the authors 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. + */ + +/* + * BUGS: + * Does not allow you to close display window; + * would need to tear down both system, and system independent data. + * + */ + +#include "sim_video.h" +#include +#include +#include "ws.h" +#include "display.h" + +#ifndef PIX_SIZE +#define PIX_SIZE 1 +#endif + +/* + * light pen location + * see ws.h for full description + */ +int ws_lp_x = -1; +int ws_lp_y = -1; + +static int xpixels, ypixels; +static int pix_size = PIX_SIZE; +static const char *window_name; +static uint32 *colors = NULL; +static uint32 ncolors = 0, size_colors = 0; +static uint32 *surface = NULL; +typedef struct cursor { + Uint8 *data; + Uint8 *mask; + int width; + int height; + int hot_x; + int hot_y; + } CURSOR; + +static CURSOR *arrow_cursor; +static CURSOR *cross_cursor; + + +static int +map_key(int k) +{ + switch (k) { + case SIM_KEY_0: return '0'; + case SIM_KEY_1: return '1'; + case SIM_KEY_2: return '2'; + case SIM_KEY_3: return '3'; + case SIM_KEY_4: return '4'; + case SIM_KEY_5: return '5'; + case SIM_KEY_6: return '6'; + case SIM_KEY_7: return '7'; + case SIM_KEY_8: return '8'; + case SIM_KEY_9: return '9'; + case SIM_KEY_A: return 'a'; + case SIM_KEY_B: return 'b'; + case SIM_KEY_C: return 'c'; + case SIM_KEY_D: return 'd'; + case SIM_KEY_E: return 'e'; + case SIM_KEY_F: return 'f'; + case SIM_KEY_G: return 'g'; + case SIM_KEY_H: return 'h'; + case SIM_KEY_I: return 'i'; + case SIM_KEY_J: return 'j'; + case SIM_KEY_K: return 'k'; + case SIM_KEY_L: return 'l'; + case SIM_KEY_M: return 'm'; + case SIM_KEY_N: return 'n'; + case SIM_KEY_O: return 'o'; + case SIM_KEY_P: return 'p'; + case SIM_KEY_Q: return 'q'; + case SIM_KEY_R: return 'r'; + case SIM_KEY_S: return 's'; + case SIM_KEY_T: return 't'; + case SIM_KEY_U: return 'u'; + case SIM_KEY_V: return 'v'; + case SIM_KEY_W: return 'w'; + case SIM_KEY_X: return 'x'; + case SIM_KEY_Y: return 'y'; + case SIM_KEY_Z: return 'z'; + case SIM_KEY_BACKQUOTE: return '`'; + case SIM_KEY_MINUS: return '-'; + case SIM_KEY_EQUALS: return '='; + case SIM_KEY_LEFT_BRACKET: return '['; + case SIM_KEY_RIGHT_BRACKET: return ']'; + case SIM_KEY_SEMICOLON: return ';'; + case SIM_KEY_SINGLE_QUOTE: return '\''; + case SIM_KEY_BACKSLASH: return '\\'; + case SIM_KEY_LEFT_BACKSLASH: return '\\'; + case SIM_KEY_COMMA: return ','; + case SIM_KEY_PERIOD: return '.'; + case SIM_KEY_SLASH: return '/'; + case SIM_KEY_BACKSPACE: return '\b'; + case SIM_KEY_TAB: return '\t'; + case SIM_KEY_ENTER: return '\r'; + case SIM_KEY_SPACE: return ' '; + } + return k; +} + +int +ws_poll(int *valp, int maxus) +{ + SIM_MOUSE_EVENT mev; + SIM_KEY_EVENT kev; + + if (maxus > 1000) + sim_os_ms_sleep (maxus/1000); + + if (SCPE_OK == vid_poll_mouse (&mev)) { + unsigned char old_lp_sw = display_lp_sw; + + if ((display_lp_sw = mev.b1_state)) { + ws_lp_x = mev.x_pos; + ws_lp_y = (ypixels - 1) - mev.y_pos; /* range 0 - (ypixels-1) */ + /* convert to display coordinates */ + ws_lp_x /= pix_size; + ws_lp_y /= pix_size; + if (!old_lp_sw && !display_tablet) + vid_set_cursor (1, cross_cursor->width, cross_cursor->height, cross_cursor->data, cross_cursor->mask, cross_cursor->hot_x, cross_cursor->hot_y); + } + else { + ws_lp_x = ws_lp_y = -1; + if (old_lp_sw && !display_tablet) + vid_set_cursor (1, arrow_cursor->width, arrow_cursor->height, arrow_cursor->data, arrow_cursor->mask, arrow_cursor->hot_x, arrow_cursor->hot_y); + } + vid_set_cursor_position (mev.x_pos, mev.y_pos); + } + if (SCPE_OK == vid_poll_kb (&kev)) { + switch (kev.state) { + case SIM_KEYPRESS_DOWN: + case SIM_KEYPRESS_REPEAT: + display_keydown(map_key(kev.key)); + break; + case SIM_KEYPRESS_UP: + display_keyup(map_key(kev.key)); + break; + } + } + return 1; +} + +/* XPM */ +static const char *arrow[] = { + /* width height num_colors chars_per_pixel */ + " 16 16 3 1", + /* colors */ + "X c #000000", /* black */ + ". c #ffffff", /* white */ + " c None", + /* pixels */ + "X ", + "XX ", + "X.X ", + "X..X ", + "X...X ", + "X....X ", + "X.....X ", + "X......X ", + "X.......X ", + "X........X ", + "X.....XXXXX ", + "X..X..X ", + "X.X X..X ", + "XX X..X ", + "X X..X ", + " XX ", +}; + +/* XPM */ +static const char *cross[] = { + /* width height num_colors chars_per_pixel hot_x hot_y*/ + " 16 16 3 1 7 7", + /* colors */ + "X c #000000", /* black */ + ". c #ffffff", /* white */ + " c None", + /* pixels */ + " XXXX ", + " X..X ", + " X..X ", + " X..X ", + " X..X ", + " X..X ", + "XXXXXXX..XXXXXXX", + "X..............X", + "X..............X", + "XXXXXXX..XXXXXXX", + " X..X ", + " X..X ", + " X..X ", + " X..X ", + " X..X ", + " XXXX ", + "7,7" +}; + +static CURSOR *ws_create_cursor(const char *image[]) +{ +int byte, bit, row, col; +Uint8 *data = NULL; +Uint8 *mask = NULL; +char black, white, transparent; +CURSOR *result = NULL; +int width, height, colors, cpp; +int hot_x = 0, hot_y = 0; + +if (4 > sscanf(image[0], "%d %d %d %d %d %d", + &width, &height, &colors, &cpp, &hot_x, &hot_y)) + return result; +if ((cpp != 1) || (0 != width%8) || (colors != 3)) + return result; +black = image[1][0]; +white = image[2][0]; +transparent = image[3][0]; +data = (Uint8 *)calloc (1, (width / 8) * height); +mask = (Uint8 *)calloc (1, (width / 8) * height); +if (!data || !mask) { + free (data); + free (mask); + return result; + } +bit = 7; +byte = 0; +for (row=0; rowdata = data; + result->mask = mask; + result->width = width; + result->height = height; + result->hot_x = hot_x; + result->hot_y = hot_y; + } +else { + free (data); + free (mask); + } +return result; +} + +static void ws_free_cursor (CURSOR *cursor) +{ +if (!cursor) + return; +free (cursor->data); +free (cursor->mask); +free (cursor); +} + +/* called from display layer on first display op */ +int +ws_init(const char *name, int xp, int yp, int colors, void *dptr) +{ + int i; + int ret; + + arrow_cursor = ws_create_cursor (arrow); + cross_cursor = ws_create_cursor (cross); + xpixels = xp; + ypixels = yp; + window_name = name; + surface = (uint32 *)realloc (surface, xpixels*ypixels*sizeof(*surface)); + for (i=0; iwidth, arrow_cursor->height, arrow_cursor->data, arrow_cursor->mask, arrow_cursor->hot_x, arrow_cursor->hot_y); + return ret; +} + +void +ws_shutdown(void) +{ +ws_free_cursor(arrow_cursor); +ws_free_cursor(cross_cursor); +vid_close(); +} + +void * +ws_color_rgb(int r, int g, int b) +{ + uint32 color, i; + + color = sim_end ? (0xFF000000 | ((r & 0xFF00) << 8) | (g & 0xFF00) | ((b & 0xFF00) >> 8)) : (0x000000FF | (r & 0xFF00) | ((g & 0xFF00) << 8) | ((b & 0xFF00) << 16)); + for (i=0; i xpixels || y > ypixels) + return; + + y = ypixels - 1 - y; /* invert y, top left origin */ + + if (brush == NULL) + brush = (uint32 *)ws_color_black (); + if (pix_size > 1) { + int i, j; + + for (i=0; i + * September 2003 + * + * Updates from Douglas A. Gwyn, 12 Jan. 2004 + * + * With thanks to Daniel Smith for his web page: + * http://world.std.com/~dpbsmith/munch.html + */ + +/* + * 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 names of the authors 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. + */ + +#ifndef TEST_DIS +#define TEST_DIS DIS_TYPE30 +#endif + +#ifndef TEST_RES +#define TEST_RES RES_HALF +#endif + +#include +#include + +#ifndef EXIT_FAILURE +/* SunOS4 doesn't define this */ +#define EXIT_FAILURE 1 +#endif + +#include "display.h" + +static unsigned long test_switches = 0; + +/* called from display code: */ +unsigned long +cpu_get_switches(void) { + return test_switches; +} + +/* called from display code: */ +void +cpu_set_switches(bits) + unsigned long bits; +{ + printf("switches: %06lo\n", bits); + test_switches = bits; +} + +void +munch(void) { + static long us = 0; + static long io = 0, v = 0; + long ac; + int x, y; + + ac = test_switches; + ac += v; /* add v */ + if (ac & ~0777777) { + ac++; + ac &= 0777777; + } + v = ac; /* dac v */ + + ac <<= 9; /* rcl 9s */ + io <<= 9; + io |= ac>>18; + ac &= 0777777; + ac |= io>>18; + io &= 0777777; + + ac ^= v; /* xor v */ + + /* convert +/-512 one's complement to 0..1022, origin in lower left */ + y = (io >> 8) & 01777; /* hi 10 */ + if (y & 01000) + y ^= 01000; + else + y += 511; + + x = (ac >> 8) & 01777; /* hi 10 */ + if (x & 01000) /* negative */ + x ^= 01000; + else + x += 511; + + if (display_point(x, y, DISPLAY_INT_MAX, 0)) + printf("light pen hit at (%d,%d)\n", x, y); + +/*#define US 100000 /* 100ms (10/sec) */ +/*#define US 50000 /* 50ms (20/sec) */ +/*#define US 20000 /* 20ms (50/sec) */ +/*#define US 10000 /* 10ms (100/sec) */ +#define US 0 + us += 50; /* 10 5us PDP-1 memory cycles */ + if (us >= US) { + display_age(us, 1); + us = 0; + } + display_sync(); /* XXX push down */ +} + +#ifdef T2 +/* display all window system level intensities; + * must be compiled with -DINTENSITIES= -DT2 + */ +void +t2(void) { + int x, y; + + display_init(TEST_DIS, TEST_RES, NULL); + for (x = INTENSITIES-1; x >= 0; x--) { + for (y = 0; y < 20; y++) { + ws_display_point(x*4, y, x, 0); + ws_display_point(x*4+1, y, x, 0); + ws_display_point(x*4+2, y, x, 0); + ws_display_point(x*4+3, y, x, 0); + } + display_sync(); + } + fflush(stdout); + for (;;) + /* wait */ ; +} +#endif + +#ifdef T3 +/* display all "user" level intensities; + * must be compiled with -DINTENSITIES= -DT3 + * + * skip every other virtual point on both axes + * default scaling maps adjacent pixels and + * causes re-intensification! + */ +void +t3(void) { + int x, y; + + display_init(TEST_DIS, TEST_RES, NULL); + for (x = DISPLAY_INT_MAX; x >= 0; x--) { + for (y = 0; y < 20; y++) { + display_point(x*2, y*2, x, 0); + } + display_sync(); + } + fflush(stdout); + for (;;) + /* wait */ ; +} +#endif + +int +main(void) { + if (!display_init(TEST_DIS, TEST_RES, NULL)) + exit(EXIT_FAILURE); + + cpu_set_switches(04000UL); /* classic starting value */ + for (;;) { +#ifdef T2 + t2(); +#endif +#ifdef T3 + t3(); +#endif + munch(); + } + /*NOTREACHED*/ +} diff --git a/display/type340.c b/display/type340.c new file mode 100644 index 0000000..df9750a --- /dev/null +++ b/display/type340.c @@ -0,0 +1,706 @@ +/* + * $Id: type340.c,v 1.6 2005/01/14 18:58:00 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 */ diff --git a/display/vt11.c b/display/vt11.c new file mode 100644 index 0000000..2d5c6ee --- /dev/null +++ b/display/vt11.c @@ -0,0 +1,3824 @@ +/* + * $Id: vt11.c,v 1.17 2004/01/24 20:44:46 phil Exp - revised by DAG $ + * Simulator Independent VT11/VS60 Graphic Display Processor Simulation + * Phil Budne + * September 13, 2003 + * Substantially revised by Douglas A. Gwyn; last edited 05 Aug 2005 + * + * from EK-VT11-TM-001, September 1974 + * and EK-VT48-TM-001, November 1976 + * with help from Al Kossow's "VT11 instruction set" posting of 21 Feb 93 + * and VT48 Engineering Specification Rev B + * and VS60 diagnostic test listings, provided by Alan Frisbie + */ + +/* + * Copyright (c) 2003-2004, Philip L. Budne and Douglas A. Gwyn + * + * 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 names of the authors 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. + */ + +/* + * The VT11 is a calligraphic display-file device used in the GT4x series + * of workstations (PDP-11/04,34,40 based). + * + * The VS60 is an improved, extended, upward-compatible version of the + * VT11, used in the GT62 workstation (PDP-11/34 based). It supports + * dual consoles (CRTs with light pens), multiple phosphor colors, 3D + * depth cueing, and circle/arc generator as options. We do not know + * whether any of these options were ever implemented or delivered. + * Apparently a later option substituted a digitizing-tablet correlator + * for the light pen. The VS60 has a 4-level silo (graphic data pipeline) + * which for reasons of simplicity is not implemented in this simulation; + * the only visible effect is that DZVSC diagnostic tests 110 & 111 will + * report failure. + * + * The VSV11/VS11 is a color raster display-file device (with joystick + * instead of light pen) with instructions similar to the VT11's but + * different enough that a separate emulation should be created by + * editing a copy of this source file rather than trying to hack it into + * this one. Very likely, the display (phosphor decay) simulation will + * also require revision to handle multiple colors. + * + * There were further models in this series, but they appear to have + * little if any compatibility with the VT11. + * + * Much later, DEC produced a display system it called the VS60S, but + * that doesn't seem to bear any relationship to the original VS60. + * + * A PDP-11 system has at most one display controller attached. + * In principle, a VT11 or VS60 can also be used on a VAX Unibus. + * + * STATUS: + * + * Clipping is not implemented properly for arcs. + * + * This simulation passes all four MAINDEC VS60 diagnostics and the + * DEC/X11 VS60 system exerciser, with the following exceptions: + * + * MD-11-DZVSA-A, VS60 instruction test part I, test 161: + * Failure to time-out an access to a "nonexistent" bus address, when the + * system is configured with so much memory that the probed address + * actually responds; this is a deficiency in the diagnostic itself. + * + * MD-11-DZVSB-A, VS60 instruction test part II: + * No exceptions. + * + * MD-11-DZVSC-B, VS60 instruction test part III, tests 107,110,111: + * Memory address test fails under SIMH, due to SIMH not implementing + * KT11 "maintenance mode", in which the final destination address (only) + * is relocated. When SIMH is patched to fix this, the test still fails + * due to a bug in the diagnostic itself, namely a call to DPCONV1 which + * tests a condition code that is supposed to pertain to R0 but which + * hasn't been set up. Swapping the two instructions before the call to + * DPCONV1 corrects this, and then this test passes. + * Graphic silo content tests fail, since the silo pipeline is not + * simulated; there are no plans to fix this, since it serves no other + * purpose in this simulation and would adversely affect performance. + * + * MD-11-DZVSD-B, VS60 visual display test, frame 13: + * "O" character sizes are slightly off, due to optimization for raster + * display rather than true stroking; there are no plans to change this. + * + * MD-11-DZVSE-A0, XXDP VS60 visual display exerciser: + * No visible exceptions. Light-pen interrupts might not be handled + * right, since they're reported as errors and cause display restart. + * (XXX Need to obtain source listing to check this.) + */ + +#ifdef DEBUG_VT11 +#include +#endif +#include /* memset */ +#ifndef NO_CONIC_OPT +#include /* atan2, cos, sin, sqrt */ +#endif + +#include "display.h" /* XY plot interface */ +#include "vt11.h" + +#define BITMASK(n) (1<<(n)) /* PDP-11 bit numbering */ + +/* mask for a field */ +#define FIELDMASK(START,END) ((1<<((START)-(END)+1))-1) + +/* extract a field */ +#define GETFIELD(W,START,END) (((W)>>(END)) & FIELDMASK(START,END)) + +/* extract a 1-bit field */ +#define TESTBIT(W,B) (((W) & BITMASK(B)) != 0) + +static void *vt11_dptr; +static int vt11_dbit; + +#if defined(DEBUG_VT11) || defined(VM_PDP11) + +#include + +#if defined(__cplusplus) +extern "C" { +#endif +#define DEVICE void + +#define DBG_CALL 1 +int vt11_debug; + +#if defined(VM_PDP11) +extern void _sim_debug (unsigned int dbits, DEVICE* dptr, const char* fmt, ...); + +#define DEBUGF(...) _sim_debug (vt11_dbit, vt11_dptr, ## __VA_ARGS__) +#else /* DEBUG_VT11 */ +#define DEBUGF(...) do {if (vt11_debug & DBG_CALL) { printf(## __VA_ARGS__); fflush(stdout); };} while (0) +#endif /* defined(DEBUG_VT11) || defined(VM_PDP11) */ +#if defined(__cplusplus) +} +#endif +#else + +#define DEBUGF(...) + +#endif + +/* + * Note about coordinate signedness and wrapping: + * + * The documentation for these devices says confusing things about coordinate + * wrapping and signedness. The VT11 maintains 12-bit coordinate registers + * (wrapping 4096 -> 0), while the VS60 maintains 14-bit coordinate registers. + * Coordinate arithmetic (such as adding a vector "delta" to the current + * position) that overflows merely drops the extra bits; this can be treated + * as use of twos-complement representation for the position registers, whereas + * the VS60 offset registers and the display file itself use a signed-magnitude + * representation. (Except that JMP/JSR-relative delta uses twos-complement!) + * This simulation tracks position using at least 32 bits including sign; this + * can overflow only for a pathological display file. + * + * Note about scaling and offsets: + * + * The VS60 supports character and vector scaling and position offsets. The + * X, Y, and Z position register values always include scaling and offsets. + * It is not clear from the manual whether or not there are two "guard bits", + * which would better track the virtual position when using a scale factor of + * 3/4, 1/2, or 1/4. Most likely, there are no guard bits (this has been + * confirmed by diagnostic DZVSB test 31). This simulation maintains position + * values and offsets both multiplied by PSCALEF, which should be 4 to obtain + * maximum drawing precision, or 1 to mimic the actual non-guard-bit display + * hardware. These internal coordinates are "normalized" (converted to correct + * virtual CRT coordinates) before being reported via the position/offset + * registers. The normalized Z position register value's 2 lowest bits are + * always 0. + * Example of why this matters: Set vector scale 1/2; draw successive vectors + * with delta X = 1, 1, and -2. With guard bits, the final and original X + * positions are the same; without guard bits, the final X position is one + * unit to the left of the original position. This effect accumulates over a + * long sequence of vectors, leading to quite visible distortion of the image. + * + * Light-pen and edge-interrupt positions always have "on-screen" values. + */ + +#ifndef PSCALEF +#if 0 /* XXX enable only during development, to catch any oversights */ +#define PSCALEF 4 /* position scale factor 4 for maximum precision */ +#else +#define PSCALEF 1 /* position scale factor 1 for accurate simulation */ +#endif +#endif + +#define PSCALE(x) ((x) * PSCALEF) +#define PNORM(x) ((x) / PSCALEF) +/* virtual_CRT_coordinate = PNORM(scaled_value) */ + +/* VS60 scales points/vectors and characters separately */ +#define VSCALE(x) ((PSCALE(vector_scale * (int32)(x)) + ((x)>=0 ? 1 : -1)) / 4) +#define CSCALE(x) ((PSCALE(char_scale * (int32)(x)) + ((x)>=0 ? 1 : -1)) / 4) +/* (The "+ ((x)>=0 ? 1 : -1)" above is needed to pass the diagnostics.) */ + +#define ABS(x) ((x) >= 0 ? (x) : -(x)) +#define TWOSCOMP(x) ((x) >= 0 ? (x) : ~(-(x)-1)) + +enum display_type vt11_display = DISPLAY_TYPE; /* DIS_VR{14,17,48} */ +int vt11_scale = PIX_SCALE; /* RES_{FULL,HALF,QUARTER,EIGHTH} */ +unsigned char vt11_init = 0; /* set after display_init() called */ +#define INIT { if (!vt11_init) { display_init(vt11_display, vt11_scale, vt11_dptr); \ + vt11_init = 1; vt11_reset(vt11_dptr, vt11_dbit); } } + +/* state visible to host */ + +/* The register and field names are those used in the VS60 manual (minus the + trailing "flag", "code", "status", or "select"); the VT11 manual uses + somewhat different names. */ + +/* + * Display Program Counter + * Read/Write (reading returns the *relocated* DPC bits [15:0]) + * DPC address 15:1 + * resume 0 + */ +#define DPC stack[8]._dpc /* Display PC (always even) */ +static uint16 bdb = 0; /* Buffered Data Bits register; + see comment in vt11_get_dpc() */ + +/* + * Mode Parameter Register + * Read Only, except that writing to it beeps the LK40 keyboard's bell + * internal stop flag 15 + * graphic mode code 14:11 + * intensity level 10:8 + * LP con. 0 hit flag 7 + * shift out status 6 + * edge indicator 5 + * italics status 4 + * blink status 3 + * edge flag status 2 (VS60 only) + * line type register status 1:0 + */ +static unsigned char internal_stop = 0; /* 1 bit: stop display */ +static unsigned char mode_field = 0; /* copy of control instr. bits 14-11 */ +#define graphic_mode stack[8]._mode /* 4 bits: sets type for graphic data */ +enum gmode { CHAR=0, SVECTOR, LVECTOR, POINT, GRAPHX, GRAPHY, RELPOINT, /* all */ + BSVECT, CIRCLE, ABSVECTOR /* VS60 only */ +}; + +#define intensity stack[8]._intens /* 3 bits: 0 => dim .. 7 => bright */ +static unsigned char lp0_hit = 0; /* 1 bit: light pen #0 detected hit */ +static unsigned char so_flag = 0; /* 1 bit: illegal char. in SO mode */ +static unsigned char edge_indic = 0; /* 1 bit: crossing visible area edge */ +#define italics stack[8]._italics /* 1 bit: use italic font */ +#define blink_ena stack[8]._blink /* 1 bit: blink graphic item */ +static unsigned char edge_flag = 0; /* 1 bit: edge intr if enabled (VS60) */ +#define line_type stack[8]._ltype /* 2 bits: style for drawing vectors */ +enum linetype { SOLID=0, LONG_DASH, SHORT_DASH, DOT_DASH }; + +/* + * Graphplot Increment and X Position Register + * Read Only + * graphplot increment register value 15:10 + * X position register value 9:0 + */ +static unsigned char graphplot_step = 0;/* (scaled) graphplot step increment */ +static int32 xpos = 0; /* X position register * PSCALEF */ + /* note: offset has been applied! */ +static int lp_xpos; /* (normalized) */ +static int edge_xpos; /* (normalized) */ + +/* + * Character Code and Y Position Register + * Read Only + * character register contents 15:10 + * Y position register value 9:0 + */ +static unsigned char char_buf = 0; /* (only lowest 6 bits reported) */ +static int32 ypos = 0; /* Y position register * PSCALEF */ + /* note: offset has been applied! */ +static int lp_ypos; /* (normalized) */ +static int edge_ypos; /* (normalized) */ + +/* + * Relocate Register (VS60 only) + * Read/Write + * spare 15:12 + * relocate register value[17:6] 11:0 + */ +static uint32 reloc = 0; /* relocation, aligned with DPC */ + +/* + * Status Parameter Register (VS60 only) + * Read Only, except for bit 7 (1 => external stop request) + * display busy status 15 + * stack overflow status 13 + * stack underflow status 12 + * time out status 11 + * char. rotate status 10 + * char. scale index 9:8 + * external stop flag 7 + * menu status 6 + * relocated DPC bits [17:16] 5:4 + * vector scale 3:0 + */ +#define busy (!(stopped || lphit_irq || lpsw_irq || edge_irq || char_irq \ + || stack_over || stack_under || time_out || name_irq)) + /* 1 bit: display initiated | resumed */ +static unsigned char stack_over = 0; /* 1 bit: "push" with full stack */ +static unsigned char stack_under = 0; /* 1 bit: "pop" with empty stack */ +static unsigned char time_out = 0; /* 1 bit: timeout has occurred */ +#define char_rotate stack[8]._crotate /* 1 bit: rotate chars 90 degrees CCW */ +#define cs_index stack[8]._csi /* character scale index 0..3 */ +static unsigned char ext_stop = 0; /* 1 bit: stop display */ +#define menu stack[8]._menu /* 1 bit: VS60 graphics in menu area */ +#define vector_scale stack[8]._vscale /* non-character scale factor * 4 */ + +/* + * X Offset Register (VS60 only) + * Read/Write + * upper X position bits 15:12 (read) + * sign of X dynamic offset 13 (write) + * X dynamic offset 11:0 + */ +static unsigned char s_xoff = 0; /* sign bit for xoff (needed for -0) */ +static int32 xoff = 0; /* X offset register * PSCALEF */ + +/* + * Y Offset Register (VS60 only) + * Read/Write + * upper Y position bits 15:12 (read) + * sign of Y dynamic offset 13 (write) + * Y dynamic offset 11:0 + */ +static unsigned char s_yoff = 0; /* sign bit for yoff (needed for -0) */ +static int32 yoff = 0; /* Y offset register * PSCALEF */ + +/* + * Associative Name Register (VS60 only) + * Write Only + * search code change enable 14 + * search code 13:12 + * name change enable 11 + * associative name 10:0 + */ +static unsigned char search = 0; /* 00=> no search, no interrupt + 01 => intr. on 11-bit compare + 10 => intr. on high-8-bit compare + 11 => intr. on high-4-bit compare */ +static unsigned assoc_name = 0; /* compare value */ + +/* + * Slave Console/Color Register (VS60 only) + * Read/Write * + * inten enable con. 0 15 + * light pen hit flag con. 0 14 * + * LP switch on flag con. 0 13 * + * LP switch off flag con. 0 12 * + * LP flag intr. enable con. 0 11 + * LP switch flag intr. enable con. 0 10 + * inten enable con. 1 9 + * light pen hit flag con. 1 8 * + * LP switch on flag con. 1 7 * + * LP switch off flag con. 1 6 * + * LP flag intr. enable con. 1 5 + * LP switch flag intr. enable con. 1 4 + * color 3:2 + * + * * indicates that maintenance switch 3 must be set to write these bits; + * the other bits are not writable at all + */ +#define int0_scope stack[8]._inten0 /* enable con 0 for all graphic data */ +/* lp0_hit has already been defined, under Mode Parameter Register */ +static unsigned char lp0_down = 0; /* 1 bit: LP #0 switch was depressed */ +static unsigned char lp0_up = 0; /* 1 bit: LP #0 switch was released */ +#define lp0_intr_ena stack[8]._lp0intr /* generate interrupt on LP #0 hit */ +#define lp0_sw_intr_ena stack[8]._lp0swintr /* generate intr. on LP #0 sw chg */ +#define int1_scope stack[8]._inten1 /* enable con 1 for all graphic data */ +/* following 2 flags only mutable via writing this register w/ MS3 set: */ +static unsigned char lp1_hit = 0; /* 1 bit: light pen #1 detected hit */ +static unsigned char lp1_down = 0; /* 1 bit: LP #1 switch was depressed */ +static unsigned char lp1_up = 0; /* 1 bit: LP #1 switch was released */ +#define lp1_intr_ena stack[8]._lp1intr /* generate interrupt on LP #1 hit */ +#define lp1_sw_intr_ena stack[8]._lp1swintr /* generate intr. on LP #1 sw chg */ + +enum scolor { GREEN=0, YELLOW, ORANGE, RED }; +#define color stack[8]._color /* 2 bits: VS60 color option */ + +/* + * Name Register (VS60 only) + * Read Only + * name/assoc name match flag 15 + * search code 13:12 + * name 10:0 + */ +static unsigned char name_irq = 0; /* 1 bit: name matches associative nm */ + /* (always interrupts on name match!) */ +/* search previously defined, under Associative Name Register */ +#define name stack[8]._name /* current name from display file */ + +/* + * Stack Data Register (VS60 only) + * Read Only + * stack data 15:0 (as selected by Stk. Addr./Maint. Reg.) + * + * On the actual hardware there are 2 32-bit words per each of 8 stack levels. + * At the PDP-11 these appear to be 4 16-bit words ("stack bytes") per level. + */ +/* It is important to note that setting the stack level via SAR doesn't change + the parameters currently in effect; only JSR/POPR does that. To speed up + JSR/POPR, the current state is implemented as an extra stack frame, so that + push/pop is done by copying a block rather than lots of individual variables. + There are thus 9 stack elements, 8 stack entries [0..7] and the current state + [8]. Mimicking the actual hardware, the stack level *decreases* upon JSR. + */ + +static struct frame + { + vt11word _dpc; /* Display Program Counter (even) */ + unsigned _name; /* (11-bit) name from display file */ + enum gmode _mode; /* 4 bits: sets type for graphic data */ + unsigned char _vscale; /* non-character scale factor * 4 */ + unsigned char _csi; /* character scale index 0..3 */ + unsigned char _cscale; /* character scale factor * 4 */ + unsigned char _crotate; /* rotate chars 90 degrees CCW */ + unsigned char _intens; /* intensity: 0 => dim .. 7 => bright */ + enum linetype _ltype; /* line type (long dash, etc.) */ + unsigned char _blink; /* blink enable */ + unsigned char _italics; /* italicize characters */ + unsigned char _so; /* currently in shift-out mode */ + unsigned char _menu; /* VS60 graphics in menu area */ + unsigned char _cesc; /* perform POPR on char. term. match */ + unsigned char _edgeintr; /* generate intr. on edge transition */ + unsigned char _lp1swintr; /* generate intr. on LP #1 switch chg */ + unsigned char _lp0swintr; /* generate intr. on LP #0 switch chg */ + unsigned char _lp1intr; /* generate interrupt on LP #1 hit */ + unsigned char _inten1; /* blank cons. 1 for all graphic data */ + unsigned char _lp0intr; /* generate interrupt on LP #0 hit */ + unsigned char _inten0; /* blank cons. 0 for all graphic data */ + unsigned char _bright; /* visually indicate hit on entity */ + unsigned char _stopintr; /* generate interrupt on intern. stop */ + enum scolor _color; /* scope display color (option) */ + unsigned char _zdata; /* flag: display file has Z coords */ + unsigned char _depth; /* flag: display Z using depth cue */ + } stack[9] = { { 0, 0, CHAR, 0, 0, 0, 0, 0, SOLID, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, GREEN, 0, 0 }, + { 0, 0, CHAR, 0, 0, 0, 0, 0, SOLID, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, GREEN, 0, 0 }, + { 0, 0, CHAR, 0, 0, 0, 0, 0, SOLID, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, GREEN, 0, 0 }, + { 0, 0, CHAR, 0, 0, 0, 0, 0, SOLID, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, GREEN, 0, 0 }, + { 0, 0, CHAR, 0, 0, 0, 0, 0, SOLID, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, GREEN, 0, 0 }, + { 0, 0, CHAR, 0, 0, 0, 0, 0, SOLID, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, GREEN, 0, 0 }, + { 0, 0, CHAR, 0, 0, 0, 0, 0, SOLID, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, GREEN, 0, 0 }, + { 0, 0, CHAR, 0, 0, 0, 0, 0, SOLID, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, GREEN, 0, 0 }, + { 0, 0, CHAR, 4, 1, 4, 0, 4, SOLID, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, GREEN, 0, 0 }, + }; + +#define char_scale stack[8]._cscale /* character scale factor * 4 */ + /* _cscale must track _csi! */ +static const unsigned char csi2csf[4] = { 2, 4, 6, 8 }; /* maps cs_index to " */ +#define shift_out stack[8]._so /* flag: using shift-out char. set */ +#define char_escape stack[8]._cesc /* perform POPR on char. term. match */ +#define edge_intr_ena stack[8]._edgeintr /* generate intr. on edge transit */ +#define lp_intensify stack[8]._bright /* if VT11, 20us bright spot; + if VS60, brighten the entity */ +#define stop_intr_ena stack[8]._stopintr /* generate intr. on internal stop */ +#define file_z_data stack[8]._zdata /* flag: display file has Z coords */ +#define depth_cue_proc stack[8]._depth /* flag: display Z using depth cue */ + +/* + * Character String Terminate Register (VS60 only) + * Read/Write + * char. term. reg. enable 7 + * character terminate code 6:0 + */ +static int char_term = 0; /* char. processing POPRs after this */ + +/* + * Stack Address/Maintenance Register (VS60 only) + * Read/Write + * maint. sw. 4 15 + * maint. sw. 3 14 + * maint. sw. 2 13 + * maint. sw. 1 12 + * offset mode status 10 + * jump to subr. ?rel. status 9 (diagnostic requires this be JSR abs.!) + * word 2 status 8 + * word 1 status 7 + * word 0 status 6 + * stack reset status 5 + * stack level select 4:2 (manual has this messed up) + * stack halfword select 1:0 (manual has this messed up) + */ +static unsigned char maint4 = 0; /* 1 bit: maintenance switch #4 */ +static unsigned char maint3 = 0; /* 1 bit: maintenance switch #3 */ +static unsigned char maint2 = 0; /* 1 bit: maintenance switch #2 */ +static unsigned char maint1 = 0; /* 1 bit: maintenance switch #1 */ +static unsigned char offset = 0; /* 1 bit: last data loaded offsets */ +static unsigned char jsr = 0; /* 1 bit: last control was JSR ?rel. */ +static int word_number = -2; /* tracks multiple data words */ +#define CONTROL_MODE() (word_number == -1) /* true when in control mode */ +#define DATA_MODE() (word_number >= 0) /* true when in data mode */ +static struct frame *sp = &stack[8]; /* -> selected stack frame, or TOS */ +#define STACK_EMPTY (sp == &stack[8]) /* "TOS" */ +#define STACK_FULL (sp == &stack[0]) /* "BOS" */ +static unsigned char stack_sel = 8<<2; /* 8 levels, 4 PDP-11 words per level */ + /* stack_sel must track sp and TOS! */ + +/* + * Z Position Register, Depth Cue Option (VS60 only) + * Read/Write + * Z position register value[13:2] 11:0 + */ +static int32 zpos = 0; /* (Z "position" reg. * 4) * PSCALEF */ + /* note: offset has been applied! */ +static int32 lp_zpos; /* (scaled) */ +static int32 edge_zpos; /* (scaled) */ + +/* + * Z Offset Register, Depth Cue Option (VS60 only) + * Read/Write + * sign of X dynamic offset 15 (read) (VT48 manual has this confused) + * sign of Y dynamic offset 14 (read) (VT48 manual has this confused) + * sign of Z dynamic offset 13 + * Z dynamic offset 11:0 + */ +static unsigned char s_zoff = 0; /* sign bit for zoff (needed for -0) */ +static int32 zoff = 0; /* Z offset register * PSCALEF */ + +/* + * Invisible state: + */ +static unsigned char char_irq = 0; /* intr. on illegal char in SO mode */ +static unsigned char lphit_irq = 0; /* intr. on light-pen hit */ +static unsigned char lpsw_irq = 0; /* intr. on tip-switch state change */ +static unsigned char edge_irq = 0; /* intr. on edge transition */ + +static unsigned char lp0_sw_state = 0; /* last known LP tip-switch state */ +static unsigned char blink_off = 0; /* set when blinking graphics is dark */ +static unsigned char finish_jmpa = 0; /* reminder to fetch JMPA address */ +static unsigned char finish_jsra = 0; /* reminder to fetch JSRA address */ + +static unsigned char more_vect = 0; /* remembers LP hit in middle of vec. */ +static unsigned char more_arc = 0; /* remembers LP hit in middle of arc */ +static int32 save_x0, save_y0, save_z0, save_x1, save_y1, save_z1; + /* CRT coords for rest of vector */ + +static unsigned char lp_suppress = 0; /* edge columns of char. (VT11 only) */ +static unsigned char stroking = 0; /* set when drawing VS60 char strokes */ +static unsigned char skip_start = 0; /* set between vis. char./arc strokes */ + +static unsigned char stopped = 1; /* display processor frozen */ +static unsigned char sync_period = 0; /* frame sync period (msec) */ +static unsigned char refresh_rate = 0; /* 2 bits: + 00 => continuous display refresh + 01 => 30 fps (60 fps if VT11) + 10 => 40 fps (VS60) + 11 => external sync (VS60) */ + +#if 0 /* this is accurate in simulated "real" time */ +#define BLINK_COUNT 266 /* 266 milliseconds */ +#else /* this looks better in actual real time (adjust for your host speed) */ +#define BLINK_COUNT 67 /* 67 milliseconds */ +#endif + +unsigned char vt11_csp_w = VT11_CSP_W; /* horizontal character spacing */ +unsigned char vt11_csp_h = VT11_CSP_H; /* vertical character spacing */ +/* VS60 spacing depends on char scale; above are right for char scale x1 */ + +/* VS60 has a menu area to the right of the "main working surface" */ +#define MENU_OFFSET (1024 + VR48_GUTTER) /* left edge of menu on CRT */ +#define VR48_WIDTH (MENU_OFFSET + 128) /* X beyond this is not illuminated */ + +static int reduce; /* CRT units per actual pixel */ +static int x_edge; /* 1023 or VR48_WIDTH-1, depending */ +static int y_edge; /* 767 or 1023, depending on display */ +#define ONCRT(x,y) ((x) >= 0 && (x) <= x_edge && (y) >= 0 && (y) <= y_edge) + +/* + * Clipping-specific stuff. + * When a vector crosses the edge of the viewing window, the "edge flag" is set + * and the "edge indicator" indicates whether the first point on the visible + * segment is clipped. Apparently the VT11 does not draw the visible segment, + * but the VS60 will draw the segment (after a resume from an edge interrupt, + * if the interrupt was enabled). The VS60 will also post a second interrupt + * corresponding to the end of the visible segment, after setting the edge flag + * (again) and setting the edge indicator according to whether the last point + * on the visible segment was clipped. + * Note: a light-pen hit is possible on a drawn clipped segment. + */ +static int clip_vect = 0; /* set when clipped coords saved; bit-coded: + 1 => entry clipped + 2 => exit clipped */ +static int clip_i; /* saved "intensify" bit */ +static int32 clip_x0, clip_y0, clip_z0; /* CRT coords for entry point */ +static int32 clip_x1, clip_y1, clip_z1; /* CRT coords for exit point */ + +/* + * Uncertain whether VS60 edge transitions in menu area are flagged and whether + * clipping takes menu width into account. Three possibilities: + */ +#define CLIPYMAX y_edge +#if 0 /* menu area never clipped (seems wrong) */ +#define CLIPXMAX 1023 +#define ONSCREEN(x,y) (menu || ((x) >= 0 && (x) <= CLIPXMAX \ + && (y) >= 0 && (y) <= CLIPYMAX)) +#elif 0 /* menu area correctly clipped (unlikely) */ +#define CLIPXMAX (menu ? 127 : 1023) +#define ONSCREEN(x,y) ((x) >= 0 && (x) <= CLIPXMAX \ + && (y) >= 0 && (y) <= CLIPYMAX) +#else /* menu area clipped same as main area */ +#define CLIPXMAX 1023 +#define ONSCREEN(x,y) ((x) >= 0 && (x) <= CLIPXMAX \ + && (y) >= 0 && (y) <= CLIPYMAX) +#endif + +static void lineTwoStep(int32, int32, int32, int32, int32, int32); + /* forward reference */ + +/* + * calls to read/write VT11/VS60 CSRs + * + * Presumably the host looks at our state less often than we do(!) + * so we keep it in a form convenient to us, rather than as bit fields + * packed into "registers". The simulated VT48 register contents are + * converted to/from our internal variables by the following functions. + */ + +int32 +vt11_get_dpc(void) +{ INIT + /* + * The VT48 manual says that Maintenance Switch 1 causes the Buffered + * Data Bits register to be "entered into the DPC" so it can be + * examined by reading the DPC address, but details of when and how + * often that happens are not provided. Examination of the diagnostic + * test listings shows that relocation is applied and that only the DPC + * is involved when this switch is set. + */ + return ((maint1 ? bdb : DPC) + reloc) & 0177777; +} + +void +vt11_set_dpc(uint16 d) +{ INIT + bdb = d; /* save all bits in case maint1 used */ + DEBUGF("set DPC 0%06o\r\n", (unsigned)d); + /* Stack level is unaffected, except that stack_sel==037 goes to 040; this + fudge is necessary to pass DZVSC test 3, which misleadingly calls it + setting top-of-stack upon START (vt11_set_dpc(even)). If one instead + were to set TOS upon START, then several DZVSC diagnostics would fail! */ + if (VS60 && !STACK_EMPTY && GETFIELD(stack_sel,1,0) == 3) { + stack_sel = GETFIELD(stack_sel,4,2) + 1; /* 1..8 */ + sp = &stack[stack_sel]; /* [1..8] */ + stack_sel <<= 2; + } + if (!TESTBIT(d,0)) { /* START */ + DPC = d; /* load DPC */ + sync_period = 0; + ext_stop = 0; + /* the following seem reasonable, but might be wrong */ + finish_jmpa = finish_jsra = jsr = 0; + word_number = -2; + clip_vect = 0; /* discard clipped vector data */ +#if 0 /* probably accurate mimicry, but ugly behavior */ + if (edge_irq) { + xpos = PSCALE(edge_x); + ypos = PSCALE(edge_y); + } +#endif + } else { /* RESUME (after intr); DPC unchanged */ + /* if resuming from LP hit interrupt, finish drawing rest of vector */ + if (more_vect) { + unsigned char save_ena = lp0_intr_ena; + lp0_intr_ena = 0; /* one hit per vector is plenty */ + lphit_irq = 0; /* or else lineTwoStep aborts again! */ + /* line_counter is intact; draw rest of visible vector */ + lineTwoStep(save_x0, save_y0, save_z0, save_x1, save_y1, save_z1); + lp0_intr_ena = save_ena; + } + if (more_arc) { /* remainder of chord was just drawn */ + unsigned char save_ena = lp0_intr_ena; + lp0_intr_ena = 0; /* one hit per arc is plenty */ + lphit_irq = 0; /* or else lineTwoStep aborts again! */ + /* line_counter is intact; draw rest of visible arc */ + /*XXX not yet implemented [conic{23}() needed]*/ + lp0_intr_ena = save_ena; + } + if (!maint2) /* kludge to satify diagnostic test */ + ext_stop = 0; + } + stopped = internal_stop = time_out = stack_over = stack_under = 0; + more_vect = more_arc = stroking = skip_start = 0; + so_flag = edge_indic = edge_flag = lp0_hit = lp1_hit = lp_suppress = 0; + lp0_down = lp0_up = lp1_down = lp1_up = 0; + char_irq = lphit_irq = lpsw_irq = edge_irq = name_irq = 0; + /* next vt11_cycle() will perform a fetch */ +} + +int32 +vt11_get_mpr(void) +{ + int32 ret; + INIT + ret = (internal_stop<<15) | (mode_field<<11) | (intensity<<8) | + (lp0_hit<<7) | (so_flag<<6) | (edge_indic<<5) | (italics<<4) | + (blink_ena<<3) | line_type; + + if (VS60) + ret |= edge_flag<<2; + + return ret; +} + +void +vt11_set_mpr(uint16 d) +{ INIT + /* beeps the "bell" on the LK40 keyboard */ +#if 0 /* probably doesn't hurt to do it for the VS60 also */ + if (VT11) /* according to the VS60 specs */ +#endif + display_beep(); +} + +int32 +vt11_get_xpr(void) +{ + int32 pos; + INIT + pos = lphit_irq ? lp_xpos : edge_irq ? edge_xpos : PNORM(xpos); + return (graphplot_step << 10) | GETFIELD(TWOSCOMP(pos),9,0); +} + +void +vt11_set_xpr(uint16 d) +{ INIT + DEBUGF("set XPR: no effect\r\n"); +} + +int32 +vt11_get_ypr(void) +{ + int32 pos; + INIT + pos = lphit_irq ? lp_ypos : edge_irq ? edge_ypos : PNORM(ypos); + return (GETFIELD(char_buf,5,0) << 10) | GETFIELD(TWOSCOMP(pos),9,0); +} + +void +vt11_set_ypr(uint16 d) +{ INIT + DEBUGF("set YPR: no effect\r\n"); +} + +/* All the remaining registers pertain to the VS60 only. */ + +int32 +vt11_get_rr(void) +{ INIT + return reloc >> 6; +} + +void +vt11_set_rr(uint16 d) +{ INIT + reloc = (uint32)GETFIELD(d,11,0) << 6; +} + +int32 +vt11_get_spr(void) +{ INIT + return (busy<<15) | (stack_over<<13) | (stack_under<<12) | (time_out<<11) | + (char_rotate<<10) | (cs_index<<8) | (ext_stop<<7) | + (menu<<6) | (((DPC + reloc) & 0600000L) >> 12) | vector_scale; +} + +void +vt11_set_spr(uint16 d) +{ INIT + ext_stop = TESTBIT(d,7); /* stop occurs at end of next display cycle */ + + if (ext_stop /* not maskable */) { + stopped = 1; /* (asynchronous with display cycle) */ + vt_stop_intr(); /* post stop interrupt to host */ + } +} + +int32 +vt11_get_xor(void) +{ + int32 off, pos; + INIT + off = PNORM(xoff); + pos = lphit_irq ? lp_xpos : edge_irq ? edge_xpos : PNORM(xpos); + return (GETFIELD(TWOSCOMP(pos),13,10)<<12) | GETFIELD(ABS(off),11,0); +} + +void +vt11_set_xor(uint16 d) +{ INIT + xoff = PSCALE(GETFIELD(d,11,0)); + s_xoff = TESTBIT(d,13); + if (s_xoff) + xoff = -xoff; +} + +int32 +vt11_get_yor(void) +{ + int32 off, pos; + INIT + off = PNORM(yoff); + pos = lphit_irq ? lp_ypos : edge_irq ? edge_ypos : PNORM(ypos); + return (GETFIELD(TWOSCOMP(pos),13,10)<<12) | GETFIELD(ABS(off),11,0); +} + +void +vt11_set_yor(uint16 d) +{ INIT + yoff = PSCALE(GETFIELD(d,11,0)); + s_yoff = TESTBIT(d,13); + if (s_yoff) + yoff = -yoff; +} + +int32 +vt11_get_anr(void) +{ INIT + DEBUGF("get ANR: no effect\r\n"); + return (search << 12) | assoc_name; /* [garbage] */ +} + +void +vt11_set_anr(uint16 d) +{ INIT + if (TESTBIT(d,14)) + search = GETFIELD(d,13,12); + if (TESTBIT(d,11)) + assoc_name = GETFIELD(d,10,0); +} + +int32 +vt11_get_scr(void) +{ INIT + return (int0_scope<<15) | (lp0_hit<<14) | (lp0_down<<13) | (lp0_up<<12) | + (lp0_intr_ena<<11) | (lp0_sw_intr_ena<<10) | (int1_scope<<9) | + (lp1_hit<<8) | (lp1_down<<7) | (lp1_up<<6) | (lp1_intr_ena<<5) | + (lp1_sw_intr_ena<<4) | (color << 2); +} + +void +vt11_set_scr(uint16 d) +{ INIT + if (maint3) { + if (TESTBIT(d,14) && lp0_intr_ena) { + if (!lphit_irq) { /* ensure correct position registers reported */ + lp_xpos = PNORM(xpos); + lp_ypos = PNORM(ypos); + lp_zpos = PNORM(zpos); + } + lp0_hit = lphit_irq = 1; + } + if (TESTBIT(d,13)) { + lp0_down = 1; /* the manual seems to have this backward */ + if (lp0_sw_intr_ena) + lpsw_irq = 1; + } + if (TESTBIT(d,12)) { + lp0_up = 1; /* the manual seems to have this backward */ + if (lp0_sw_intr_ena) + lpsw_irq = 1; + } + if (TESTBIT(d,8) && lp1_intr_ena) { + if (!lphit_irq) { /* ensure correct position registers reported */ + lp_xpos = PNORM(xpos); + lp_ypos = PNORM(ypos); + lp_zpos = PNORM(zpos); + } + lp1_hit = lphit_irq = 1; + } + if (TESTBIT(d,7)) { + lp1_down = 1; + if (lp1_sw_intr_ena) + lpsw_irq = 1; + } + if (TESTBIT(d,6)) { + lp1_up = 1; + if (lp1_sw_intr_ena) + lpsw_irq = 1; + } + if (lpsw_irq || lphit_irq /* && DATA_MODE() */) + vt_lpen_intr(); + } +} + +int32 +vt11_get_nr(void) +{ INIT + return (name_irq<<15) | (search<<12) | name; +} + +void +vt11_set_nr(uint16 d) +{ INIT + DEBUGF("set NR: no effect\r\n"); +} + +int32 +vt11_get_sdr(void) +{ + struct frame *p; + INIT + p = &stack[GETFIELD(stack_sel,4,2)]; /* [0..7], 8 (TOS) => 0 */ + switch (GETFIELD(stack_sel,1,0)) { /* 16-bit "byte" within frame */ + case 0: + return p->_dpc; /* DPC bit#0 is always 0 */ + + case 1: + return (p->_name << 4) | p->_mode; + + case 2: + return (p->_italics << 15) | (p->_vscale << 11) | (p->_csi << 9) | + (p->_crotate << 7) | (p->_intens << 4) | ((int)p->_color << 2) | + p->_ltype; + + case 3: + return (p->_blink << 15) | (p->_so << 14) | (p->_menu << 13) | + (p->_cesc << 12) | (p->_edgeintr << 11) | (p->_zdata << 10) | + (p->_depth << 8) | (p->_lp1swintr << 7) | + (p->_lp0swintr << 6) | (p->_lp1intr << 5) | (p->_inten1 << 4) | + (p->_lp0intr << 3) | (p->_inten0 << 2) | ((!p->_bright) << 1) | + p->_stopintr; + } + /*NOTREACHED*/ + return 0; +} + +void +vt11_set_sdr(uint16 d) +{ INIT + DEBUGF("set SDR: no effect\r\n"); +} + +int32 +vt11_get_str(void) +{ INIT + return char_term; +} + +void +vt11_set_str(uint16 d) +{ INIT + if (TESTBIT(d,7)) + char_term = GETFIELD(d,6,0); +} + +int32 +vt11_get_sar(void) +{ + int32 ret; + INIT + ret = (maint4<<15) | (maint3<<14) | (maint2<<13) | (maint1<<12) | + (offset<<10) | (jsr<<9) | stack_sel /*includes bit 5=TOS [level 8]*/; + switch (word_number) { + case -1: /* control mode reported as word 0, + according to VT48 ES */ + case 0: + ret |= 1<<6; + break; + case 1: + ret |= 1<<7; + break; + case 2: + ret |= 1<<8; + break; + /* others (including -1) not reportable */ + } + return ret; +} + +void +vt11_set_sar(uint16 d) +{ INIT + maint4 = TESTBIT(d,15); /* 1 => synch. processing pipeline */ + maint3 = TESTBIT(d,14); /* 1 => copy delta,tangent to x,y pos */ + maint2 = TESTBIT(d,13); /* 1 => set single-step mode */ + maint1 = TESTBIT(d,12); /* 1 => vt11_get_dpc will return bdb */ + if (TESTBIT(d,5)) { + sp = &stack[8]; /* reset stack pointer to TOS */ + stack_sel = (8<<2) /* TOS amounts to level 8 */ + | TESTBIT(stack_sel,0); /* preserve PDP-11 word sel. */ + } else { + stack_sel = GETFIELD(d,4,0); + sp = &stack[GETFIELD(stack_sel,4,2)]; /* [0..7] */ + } +} + +/* registers used with the VS60 depth cueing option */ + +/* + * Since there is no support for hardware 3D rotation or viewing transform, the + * only effect of the Z coordinate is to modulate beam intensity along a vector + * to give the illusion that greater Z coordinates are closer (brighter). + * This is known as "depth cueing" and is implemented in dintens(). + */ + +int32 +vt11_get_zpr(void) +{ + int32 pos; + INIT + pos = lphit_irq ? lp_zpos : edge_irq ? edge_zpos : PNORM(zpos); + return GETFIELD(TWOSCOMP(pos),13,2); +} + +void +vt11_set_zpr(uint16 d) +{ INIT + DEBUGF("set ZPR: no effect\r\n"); +} + +int32 +vt11_get_zor(void) +{ + int32 off, ret; + INIT + off = PNORM(zoff); + ret = GETFIELD(ABS(off),11,0); + if (s_xoff) /* (VT48 manual has this confused) */ + ret |= 1<<15; + if (s_yoff) /* (VT48 manual has this confused) */ + ret |= 1<<14; + if (s_zoff) + ret |= 1<<13; + return ret; +} + +void +vt11_set_zor(uint16 d) +{ INIT + zoff = PSCALE(GETFIELD(d,11,0)); + s_zoff = TESTBIT(d,13); + if (s_zoff) + zoff = -zoff; +} + +void +vt11_reset(void *dev, int debug) +{ + if (dev) { + vt11_dptr = dev; + vt11_dbit = debug; + } + + /* make sure display code has been initialized */ + if (!vt11_init) /* (SIMH invokes before display type is set) */ + return; /* wait until last moment */ + + if (VS60) { + /* VS60 character spacing depends on char scale; these are for x1 */ + vt11_csp_w = 14; /* override VT11 options */ + vt11_csp_h = 24; + } /* else assume already set up for desired VT11 behavior */ + + x_edge = display_xpoints() - 1; + y_edge = display_ypoints() - 1; + reduce = display_scale(); + + /* reset VT11/VT48 to initial default internal state: */ + + /* clear interrupts, BDB, etc. */ + vt11_set_dpc(0); + /* some of the following should probably be moved to vt11_set_dpc([even]) */ + stopped = int0_scope = 1; /* idle, console 0 enabled */ + lp0_sw_state = display_lp_sw; /* sync with mouse button #1 */ + shift_out = int1_scope = stop_intr_ena = blink_off = 0; + italics = blink_ena = char_rotate = menu = search = offset = 0; + lp0_sw_intr_ena = lp1_sw_intr_ena = lp0_intr_ena = lp1_intr_ena = 0; + file_z_data = edge_intr_ena = depth_cue_proc = char_escape = 0; + maint1 = maint2 = maint3 = maint4 = 0; + refresh_rate = 0; + char_buf = char_term = 0; + assoc_name = name = 0; + reloc = 0; + xpos = ypos = zpos = xoff = yoff = zoff = 0; + s_xoff = s_yoff = s_zoff = 0; + graphplot_step = 0; + mode_field = 0; + graphic_mode = CHAR; + line_type = SOLID; + color = GREEN; + lp_intensify = 1; + cs_index = 1; + char_scale = vector_scale = 4; + intensity = 4; + sp = &stack[8]; + stack_sel = 8<<2; /* PDP-11 word selector also cleared */ + + /* following necessary in case the stack is inspected via stack data reg. */ + { int i; + for (i = 0; i < 8; ++i) + memset(&stack[i], 0, sizeof(struct frame)); + } +} + +/* VS60 display subroutine support (see stack layout for SDR, above) */ + +static void +push() +{ + stack_over = STACK_FULL; /* BOS? */ + if (!stack_over) { + --sp; + *sp = stack[8]; /* copy current parameters */ + /* (including *old* DPC) */ + stack_sel -= 1<<2; + /* XXX should stack_sel stack-byte bits be cleared? */ + } + /* else will generate interrupt soon after return */ +} + +static void +pop(int restore) +{ + stack_under = STACK_EMPTY; /* TOS? */ + if (!stack_under) { + stack[8] = *sp; /* restore parameters (including DPC) */ + ++sp; + stack_sel += 1<<2; + /* XXX should stack_sel stack-byte bits be cleared? maybe for TOS? */ + } + /* else will generate interrupt soon after return */ +} + +/* compute depth-cued display intensity from current display-file intensity */ + +int +dintens(int32 z) +{ + int i = intensity; + + if (depth_cue_proc) { /* apply depth cue */ + i += i * z / 1024; /* XXX is z scaled properly? */ + if (i > 7) + i = 7; + else if (i < 0) + i = 0; + } + i += DISPLAY_INT_MAX - 7; + return i >= DISPLAY_INT_MIN ? i : DISPLAY_INT_MIN; +} + +/* + * Note: It would be more efficient to work directly with display intensity + * levels than with Z coordinates, since the vast majority of dintens() + * computations result in the same display intensity level as the previous + * such computation. However, compared to the rest of the processing per + * pixel, this computation doesn't seem too expensive, so optimization isn't + * necessary. + */ + +/* illuminate pixel in raster image */ + +static void +illum3(int32 x, int32 y, int32 z) + /* virtual CRT units (offset and normalized) */ +{ + int i; /* display intensity level */ + + /* don't update position registers! */ + + /* coords might be outside viewable area, e.g. clipped italic character */ + if (!ONCRT(x, y) || !int0_scope) + return; + + if (blink_ena && blink_off) /* blinking & in dark phase */ + return; + + i = dintens(z); + + if (display_point((int)x, (int)y, i, 0) /* XXX VS60 might switch color */ + /* VT11, per maintenance spec, has threshold 6 for CHAR, 4 for others */ + /* but the classic Lunar Lander uses 3 for its menu and thrust bar! */ + /* I seem to recall that both thresholds were 4 for the VS60 (VR48). */ +#if 0 + && (i >= (DISPLAY_INT_MAX-1) /* (using i applies depth cueing) */ + || (graphic_mode != CHAR && i >= (DISPLAY_INT_MAX-3))) +#else + /* The following imposes thresholds of 3 for all graphic objects. */ + && (i >= (DISPLAY_INT_MAX-4)) /* (using i applies depth cueing) */ +#endif + && !lp_suppress) { + lp0_hit = 1; + if (lp0_intr_ena) + lphit_irq = 1; /* will lead to an interrupt */ + /* + * Save LP hit coordinates so CPU can look at them; the virtual position + * registers cannot be reported on LP interrupt, since they track the + * (pre-clipping) end of the vector that was being drawn. + */ + lp_xpos = x; + if (menu) + lp_xpos -= MENU_OFFSET; + lp_ypos = y; + lp_zpos = z; + if (lp_intensify) /* [technically shouldn't exceed max] */ + display_point((int)x, (int)y, DISPLAY_INT_MAX, 0); + /* XXX appropriate for VT11; what about VS60? chars? */ + } +} + +#define illum2(x,y) illum3(x, y, PNORM(zpos)) /* may be depth cued */ + /* the extra overhead if not depth cueing is not much */ + +static void +point3(int i, int32 x1, int32 y1, int32 z1, int detect_edge) + /* VSCALEd, offset coordinates (z1 * 4) */ +{ + int32 x0 = PNORM(xpos), y0 = PNORM(ypos); + + if (detect_edge) { + edge_indic = ONSCREEN(x0, y0); /* first test */ + edge_flag = !ONSCREEN(x0, y0); /* first test */ + } else { + edge_indic = 0; + edge_flag = 0; + } + xpos = x1; + ypos = y1; + zpos = z1; + x1 = PNORM(xpos); + y1 = PNORM(ypos); + z1 = PNORM(zpos); + if (detect_edge) { + edge_indic &= !ONSCREEN(x1, y1); /* second test */ + edge_flag &= ONSCREEN(x1, y1); /* second test */ + edge_flag |= edge_indic; + if (edge_flag) { + if (edge_intr_ena) { + edge_xpos = x1; + edge_ypos = y1; + edge_zpos = z1; + edge_irq = 1; +#if 0 /* XXX uncertain whether point is displayed during edge intr. */ + return; /* point not displayed */ +#endif + } else + edge_flag = 0; + } + } + if (i && ONSCREEN(x1, y1)) { + if (menu) + illum3(x1 + MENU_OFFSET, y1, z1); + else + illum3(x1, y1, z1); + } +} + +#define point2(i,x,y,e) point3(i, x, y, zpos, e) + /* the extra overhead if not depth cueing is not much */ + +/* 4 bit counter, fed from div 2 clock (to compensate for raster algorithm) */ +/* XXX check display against example photos to see if div 2 is right */ +static unsigned char line_counter; +#define LC1 02 +#define LC2 04 +#define LC3 010 +#define LC4 020 + +/* point on a line (apply line style) */ +static void +lpoint(int32 x, int32 y, int32 z) + /* X, Y are in window-system screen pixel units */ + /* Z is in virtual CRT units (offset and normalized) */ +{ + int i, on = (line_type == SOLID) || stroking; /* on for sure */ + + if (!on) { /* see if in visible portion of cycle */ + for (i = 0; i < reduce; ++i) { + switch (line_type) { + case LONG_DASH: + if (line_counter & LC4) + on = 1; + break; + case SHORT_DASH: + if (line_counter & LC3) + on = 1; + break; + case DOT_DASH: + /* LC(2:1)H * LC3L + LC4L */ + if (((line_counter & (LC1|LC2)) == (LC1|LC2) + && !(line_counter & LC3)) || !(line_counter & LC4)) + on = 1; + break; + case SOLID: + break; + } + + --line_counter; + } + } + + if (on) + /* convert back from actual screen pixels to emulated CRT coordinates */ + /* note: Z coordinate is already in virtual CRT units */ + illum3(x * reduce, y * reduce, z); +} + +/* + * 2-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 that the next two pixels of a line, + * it is easy to see that only a finite set of possiblities exist. + * If line styles weren't involved, the line could be drawn symmetrically + * from both ends toward the midpoint. + * Rasterization is done using actual screen pixel units, not emulated device + * coordinates! + * + * The Z coordinate just goes along for the ride. It is computed thusly: + * Let N = # steps in major direction (X or Y) + * i = step number + * dZ = Z1 - Z0 + * Then Zi = floor(Z0 + dZ*(i+0.5)/N) 0.5 centers steps + * Zi = floor((2*N*Z0 + dZ + 2*i*dZ) / (2*N)) + * The numerator at step i is + * Znum(i) = Znum(i-1) + 2*dZ + * with Znum(0) = 2*N*Z0 + dZ + */ + +static void +lineTwoStep(int32 x0, int32 y0, int32 z0, int32 x1, int32 y1, int32 z1) + /* virtual CRT units (offset and normalized) */ +{ + int32 dx, dy, dz; + int stepx, stepy; + + /* when clipping is implemented, coords should always be on-screen */ + + /* convert from emulated CRT units to actual screen pixels */ + x0 /= reduce; + y0 /= reduce; + x1 /= reduce; + y1 /= reduce; + /* note: Z coords remain in virtual CRT units */ + + dx = x1 - x0; + dy = y1 - y0; + dz = z1 - z0; + + /* XXX there could be fast special cases for "basic vectors" */ + + if (dx >= 0) + stepx = 1; + else { + dx = -dx; + stepx = -1; + } + if (dy >= 0) + stepy = 1; + else { + dy = -dy; + stepy = -1; + } + +#define TPOINT do { znum += dz; /* 2 * original_dz */ \ + z0 = znum / twoN; /* truncates */ \ + if (lphit_irq && !stroking) goto hit; \ + /* XXX longjmp from hit detector may be more efficient */ \ + lpoint(x0, y0, z0); \ + } while (0) + + if (!skip_start) /* not for continuing stroke when VS60 char. or arc */ + lpoint(x0, y0, z0); /* (could have used TPOINT) */ + + if (dx == 0 && dy == 0) /* following algorithm won't work */ + return; /* just the one dot */ + /* XXX not accurate for vector in Z direction */ + + if (dx > dy) { + int32 length = (dx - 1) / 2; + int extras = (dx - 1) & 1; + int32 incr2 = (dy * 4) - (dx * 2); + long twoN = 2 * dx, znum = twoN * z0 + dz; + dz *= 2; + if (incr2 < 0) { + int32 c = dy * 2; + int32 incr1 = c * 2; + int32 d = incr1 - dx; + int32 i; + for (i = 0; i < length; i++) { + x0 += stepx; + if (d < 0) { /* Pattern: */ + TPOINT; /* x o o */ + x0 += stepx; + TPOINT; + d += incr1; + } + else { + if (d < c) { /* Pattern: */ + TPOINT; /* o */ + y0 += stepy; /* x o */ + } else { /* Pattern: */ + y0 += stepy; /* o o */ + TPOINT; /* x */ + } + x0 += stepx; + TPOINT; + d += incr2; + } + } + if (extras > 0) { + x0 += stepx; + if (d >= c) + y0 += stepy; + TPOINT; + } + + } else { + int32 c = (dy - dx) * 2; /* negative */ + int32 incr1 = c * 2; /* negative */ + int32 d = incr1 + dx; + int32 i; + for (i = 0; i < length; i++) { + x0 += stepx; + if (d > 0) { /* Pattern: */ + y0 += stepy; /* o */ + TPOINT; /* o */ + x0 += stepx; /* x */ + y0 += stepy; + TPOINT; + d += incr1; + } else { + if (d < c) { /* Pattern: */ + TPOINT; /* o */ + y0 += stepy; /* x o */ + } else { /* Pattern: */ + y0 += stepy; /* o o */ + TPOINT; /* x */ + } + x0 += stepx; + TPOINT; + d += incr2; + } + } + if (extras > 0) { + x0 += stepx; + if (d >= c) + y0 += stepy; + TPOINT; + } + } + + } else { /* dy >= dx */ + int32 length = (dy - 1) / 2; + int extras = (dy - 1) & 1; + int32 incr2 = (dx * 4) - (dy * 2); + long twoN = 2 * dy, znum = twoN * z0 + dz; + dz *= 2; + if (incr2 < 0) { + int32 c = dx * 2; + int32 incr1 = c * 2; + int32 d = incr1 - dy; + int32 i; + for (i = 0; i < length; i++) { + y0 += stepy; + if (d < 0) { /* Pattern: */ + TPOINT; /* o */ + y0 += stepy; /* o */ + TPOINT; /* x */ + d += incr1; + } else { + if (d < c) { /* Pattern: */ + TPOINT; /* o */ + x0 += stepx; /* o */ + /* x */ + } else { /* Pattern: */ + x0 += stepx; /* o */ + TPOINT; /* o */ + /* x */ + } + y0 += stepy; + TPOINT; + d += incr2; + } + } + if (extras > 0) { + y0 += stepy; + if (d >= c) + x0 += stepx; + TPOINT; + } + + } else { + int32 c = (dx - dy) * 2; /* nonpositive */ + int32 incr1 = c * 2; /* nonpositive */ + int32 d = incr1 + dy; + int32 i; + for (i = 0; i < length; i++) { + y0 += stepy; + if (d > 0) { /* Pattern: */ + x0 += stepx; + TPOINT; /* o */ + y0 += stepy; /* o */ + x0 += stepx; /* x */ + TPOINT; + d += incr1; + } else { + if (d < c) { /* Pattern: */ + TPOINT; /* o */ + x0 += stepx; /* o */ + /* x */ + } else { /* Pattern: */ + x0 += stepx; /* o */ + TPOINT; /* o */ + /* x */ + } + y0 += stepy; + TPOINT; + d += incr2; + } + } + if (extras > 0) { + y0 += stepy; + if (d >= c) + x0 += stepx; + TPOINT; + } + } + } + lpoint(x1, y1, z1); /* not TPOINT (0-length vector on resume) */ + return; + + /* here if LP hit interrupt during rendering */ + hit: + more_vect = 1; + save_x0 = x0 * reduce; + save_y0 = y0 * reduce; + save_z0 = z0; + save_x1 = x1 * reduce; + save_y1 = y1 * reduce; + save_z1 = z1; + /* line_counter is static and thus will be intact upon resume */ +} /* lineTwoStep */ + +/* + * Clip segment to only that portion, if any, visible within the window. + * Returns: -1 visible and not clipped + * 0 invisible + * 1,2,3 visible and clipped (clipped coords stashed); + * bit-coded: 1 => entry clipped, 2=> exit clipped + * + * The Z coordinate just goes along for the ride. + */ +int +clip3(int32 x0, int32 y0, int32 z0, int32 x1, int32 y1, int32 z1) +{ + int code0, code1; /* Cohen-Sutherland endpoint codes */ + /* remaining variables are used in modified Liang-Barsky algorithm: */ + int32 rdx, rdy, rdz; /* x0-x1, y0-y1, z0-z1 */ + int32 tn; /* Edge parameter: numerator */ + int32 tPEn, tPEd, tPLn, tPLd; /* Enter/Leave params: numer, denom */ + int clipped; /* potential clip_vect value */ + + /* + * Use the first parts of the Cohen-Sutherland algorithm to detect + * all IN-to-IN cases and OUT-to-OUT along the same side, each of + * which is trivially handled without needing any clipping actions. + + * The idea is that the extended window edges divide the plane into + * 9 regions; the segment endpoints are assigned bit-codes that + * indicate which of the 3 X sections and which of the 3 Y sections + * each point lies in; then simple tests on the codes can detect + * the desired "trivial" cases, which are the most common. + */ + + /* assign X/Y region codes to the endpoints */ + + if (y0 > CLIPYMAX) + code0 = 1; + else if (y0 < 0) + code0 = 2; + else + code0 = 0; + + if (x0 > CLIPXMAX) + code0 |= 4; + else if (x0 < 0) + code0 |= 8; + + if (y1 > CLIPYMAX) + code1 = 1; + else if ( y1 < 0 ) + code1 = 2; + else + code1 = 0; + + if (x1 > CLIPXMAX) + code1 |= 4; + else if ( x1 < 0 ) + code1 |= 8; + + if (code0 == code1) { /* endpoints lie in same region */ + if (code0 == 0) /* ON to ON; trivially visible */ + return -1; + else /* OFF to OFF and trivially invisible */ + return 0; + } + + /* Endpoints are now known to lie in different regions. */ + + if ((code0 & code1) != 0) /* OFF to OFF and trivially invisible */ + return 0; + + /* Handle horizontal and vertical cases separately, + both for speed and to simplify later computations. */ + + rdx = x0 - x1; + rdy = y0 - y1; + rdz = z0 - z1; + + if (rdx == 0) { /* vertical; has a visible portion! */ + clipped = 0; + /* Using the direction allows us to save one test. */ + if (rdy < 0) { /* directed upward */ + if (y1 > CLIPYMAX) { + clipped = 2; + y1 = CLIPYMAX; /* clip */ + z1 = z0 + rdz * (y1 - y0) / rdy; + } + if (y0 < 0) { + clipped |= 1; + z0 -= rdz * y0 / rdy; + y0 = 0; /* clip */ + } + } else { /* directed downward */ + if (y0 > CLIPYMAX) { + clipped = 1; + y0 = CLIPYMAX; /* clip */ + z0 = z1 + rdz * (y0 - y1) / rdy; + } + if (y1 < 0) { + clipped |= 2; + z1 -= rdz * y1 / rdy; + y1 = 0; /* clip */ + } + } + goto stash; + } + + if (rdy == 0) { /* horizontal; has a visible portion! */ + clipped = 0; + /* Using the direction allows us to save one test. */ + if (rdx < 0) { /* directed rightward */ + if (x1 > CLIPXMAX) { + clipped |= 2; + x1 = CLIPXMAX; /* clip */ + z1 = z0 + rdz * (x1 - x0) / rdx; + } + if (x0 < 0) { + clipped = 1; + z0 -= rdz * x0 / rdx; + x0 = 0; /* clip */ + } + } else { /* directed leftward */ + if (x0 > CLIPXMAX) { + clipped = 1; + x0 = CLIPXMAX; /* clip */ + z0 = z1 + rdz * (x0 - x1) / rdx; + } + if (x1 < 0) { + clipped |= 2; + z1 -= rdz * x1 / rdx; + x1 = 0; /* clip */ + } + } + goto stash; + } + + /* + * Hardest cases: use modified Liang-Barsky algorithm. + * + * Not only is this computation supposedly faster than Cohen- + * Sutherland clipping, but also the original direction is + * preserved, which is necessary to accurately emulate the + * VT48 behavior (association of coordinates with interrupts). + */ + + /* + * t is a line parameter: P(t) = P0 + t * (P1 - P0). + * N is an outward normal vector. + * L, R, B, T denote edges (left, right, bottom, top). + * PE denotes "potentially entering", PL "potentially leaving". + * n, d denote numerator, denominator (avoids floating point). + */ + + /* + * We know at this point that the endpoints lie in different + * regions and that there must be at least one PE or PL crossing + * at some value of t in [0,1]. Indeed, there will be *both* PE + * and PL crossings *unless* one endpoint is IN the window. + * + * As a result of the previous filtering, denominators are never 0. + */ + + tPEn = -1; /* tPE = -1, lower than any candidate */ + tPEd = 1; + tPLn = 2; /* tPL = 2, higher than any candidate */ + tPLd = 1; + + /* + * Left: tL = NL . (PL - P0) / NL . (P1 - P0) + * NL = (-1,0) + * PL = (0,y) + * => + * tL = x0 / rdx + * + * if ( tL >= 0 & tL <= 1 ) + * if ( NL . (P1 - P0) < 0 & tL > tPE ) + * tPE := tL + * if ( NL . (P1 - P0) > 0 & tL < tPL ) + * tPL := tL + * => + * if ( rdx < 0 ) + * if ( rdx <= x0 & x0 <= 0 ) + * if ( tPEd > 0 ) + * if ( x0 * tPEd < tPEn * rdx ) + * tPE := tL + * else + * if ( x0 * tPEd > tPEn * rdx ) + * tPE := tL + * else + * if ( 0 <= x0 & x0 <= rdx ) + * if ( tPLd > 0 ) + * if ( x0 * tPLd < tPLn * rdx ) + * tPL := tL + * else + * if ( x0 * tPLd > tPLn * rdx ) + * tPL := tL + */ + + if (rdx < 0) { + if (x0 <= 0 && x0 >= rdx) { + if (tPEd > 0) { + if (x0 * (long)tPEd < (long)tPEn * rdx) + tPEn = x0, tPEd = rdx; + } else /* tPEd < 0 */ + if (x0 * (long)tPEd > (long)tPEn * rdx) + tPEn = x0, tPEd = rdx; + } + } else { /* rdx > 0 */ + if (x0 >= 0 && x0 <= rdx) { + if (tPLd > 0) { + if (x0 * (long)tPLd < (long)tPLn * rdx) + tPLn = x0, tPLd = rdx; + } else /* tPLd < 0 */ + if (x0 * (long)tPLd > (long)tPLn * rdx) + tPLn = x0, tPLd = rdx; + } + } + + /* + * Right: tR = NR . (PR - P0) / NR . (P1 - P0) + * NR = (1,0) + * PR = (XMAX,y) + * => + * tR = (x0 - XMAX) / rdx + * + * if ( tR >= 0 & tR <= 1 ) + * if ( NR . (P1 - P0) < 0 & tR > tPE ) + * tPE := tR + * if ( NR . (P1 - P0) > 0 & tR < tPL ) + * tPL := tR + * => + * if ( rdx < 0 ) + * if ( rdx <= TRn & TRn <= 0 ) + * if ( tPLd > 0 ) + * if ( TRn * tPLd > tPLn * rdx ) + * tPL := tR + * else + * if ( TRn * tPLd < tPLn * rdx ) + * tPL := tR + * else + * if ( 0 <= TRn & TRn <= rdx ) + * if ( tPEd > 0 ) + * if ( TRn * tPEd > tPEn * rdx ) + * tPE := tR + * else + * if ( TRn * tPEd < tPEn * rdx ) + * tPE := tR + */ + + tn = x0 - CLIPXMAX; + + if (rdx < 0) { + if (tn <= 0 && tn >= rdx) { + if (tPLd > 0) { + if (tn * (long)tPLd > (long)tPLn * rdx) + tPLn = tn, tPLd = rdx; + } else /* tPLd < 0 */ + if (tn * (long)tPLd < (long)tPLn * rdx) + tPLn = tn, tPLd = rdx; + } + } else { /* rdx > 0 */ + if (tn >= 0 && tn <= rdx) { + if (tPEd > 0) { + if (tn * (long)tPEd > (long)tPEn * rdx) + tPEn = tn, tPEd = rdx; + } else /* tPEd < 0 */ + if (tn * (long)tPEd < (long)tPEn * rdx) + tPEn = tn, tPEd = rdx; + } + } + + /* + * Bottom: tB = NB . (PB - P0) / NB . (P1 - P0) + * NB = (0,-1) + * PB = (x,0) + * => + * tB = y0 / rdy + * + * if ( tB >= 0 & tB <= 1 ) + * if ( NB . (P1 - P0) < 0 & tB > tPE ) + * tPE := tB + * if ( NB . (P1 - P0) > 0 & tB < tPL ) + * tPL := tB + * => + * if ( rdy < 0 ) + * if ( rdy <= y0 & y0 <= 0 ) + * if ( tPEd > 0 ) + * if ( y0 * tPEd < tPEn * rdy ) + * tPE := tB + * else + * if ( y0 * tPEd > tPEn * rdy ) + * tPE := tB + * else + * if ( 0 <= y0 & y0 <= rdy ) + * if ( tPLd > 0 ) + * if ( y0 * tPLd < tPLn * rdy ) + * tPL := tB + * else + * if ( y0 * tPLd > tPLn * rdy ) + * tPL := tB + */ + + if (rdy < 0) { + if (y0 <= 0 && y0 >= rdy) { + if (tPEd > 0) { + if (y0 * (long)tPEd < (long)tPEn * rdy) + tPEn = y0, tPEd = rdy; + } else /* tPEd < 0 */ + if (y0 * (long)tPEd > (long)tPEn * rdy) + tPEn = y0, tPEd = rdy; + } + } else /* rdy > 0 */ + if (y0 >= 0 && y0 <= rdy) { + if (tPLd > 0) { + if (y0 * (long)tPLd < (long)tPLn * rdy) + tPLn = y0, tPLd = rdy; + } else { /* tPLd < 0 */ + if (y0 * (long)tPLd > (long)tPLn * rdy) + tPLn = y0, tPLd = rdy; + } + } + + /* + * Top: tT = NT . (PT - P0) / NT . (P1 - P0) + * NT = (0,1) + * PT = (x,YMAX) + * => + * tT = (y0 - YMAX) / rdy + * + * if ( tT >= 0 & tT <= 1 ) + * if ( NT . (P1 - P0) < 0 & tT > tPE ) + * tPE := tT + * if ( NT . (P1 - P0) > 0 & tT < tPL ) + * tPL := tT + * => + * if ( rdy < 0 ) + * if ( rdy <= TRn & TRn <= 0 ) + * if ( tPLd > 0 ) + * if ( TRn * tPLd > tPLn * rdy ) + * tPL := tT + * else + * if ( TRn * tPLd < tPLn * rdy ) + * tPL := tT + * else + * if ( 0 <= TRn & TRn <= rdy ) + * if ( tPEd > 0 ) + * if ( TRn * tPEd > tPEn * rdy ) + * tPE := tT + * else + * if ( TRn * tPEd < tPEn * rdy ) + * tPE := tT + */ + + tn = y0 - CLIPYMAX; + + if (rdy < 0) { + if (tn <= 0 && tn >= rdy) { + if (tPLd > 0) { + if (tn * (long)tPLd > (long)tPLn * rdy) + tPLn = tn, tPLd = rdy; + } else /* tPLd < 0 */ + if (tn * (long)tPLd < (long)tPLn * rdy) + tPLn = tn, tPLd = rdy; + } + } else { /* rdy > 0 */ + if (tn >= 0 && tn <= rdy) { + if (tPEd > 0) { + if (tn * (long)tPEd > (long)tPEn * rdy) + tPEn = tn, tPEd = rdy; + } else /* tPEd < 0 */ + if (tn * (long)tPEd < (long)tPEn * rdy) + tPEn = tn, tPEd = rdy; + } + } + + /* + * if ( tPL < tPE ) + * invisible + * => + * if ( tPLd > 0 && tPEd < 0 || tPLd < 0 && tPEd > 0 ) + * if ( tPLn * tPEd > tPEn * tPLd ) + * invis + * else + * if ( tPLn * tPEd < tPEn * tPLd ) + * invis + */ + + if (((tPLd > 0) && (tPEd < 0)) || + ((tPLd < 0) && (tPEd > 0))) { + if (tPLn * (long)tPEd > (long)tPEn * tPLd) + return 0; /* invisible */ + } else + if (tPLn * (long)tPEd < (long)tPEn * tPLd) + return 0; /* invisible */ + + /* + * if ( tPE < 0 ) tPE := 0 [code0 is 0] + * if ( tPL > 1 ) tPL := 1 [code1 is 0] + * draw from P(tPE) to P(tPL) + * + * P(t) = P0 + t * (P1 - P0) + * => + * xE = x0 - tE * rdx, yE = y0 - tE * rdy + * xL = x0 - tL * rdx, yL = y0 - tL * rdy + */ + + /* note: update P1 first since it uses original P0 coords */ + + if (code1 == 0) + clipped = 0; + else { + clipped = 2; + /* XXX might not be rounded the same as on the VT48: */ + x1 = x0 - rdx * tPLn / tPLd; + y1 = y0 - rdy * tPLn / tPLd; + z1 = z0 - rdz * tPLn / tPLd; + } + + if (code0 != 0) { + clipped |= 1; + /* XXX might not be rounded the same as on the VT48: */ + x0 -= rdx * tPEn / tPEd; + y0 -= rdy * tPEn / tPEd; + z0 -= rdz * tPEn / tPEd; + } + + /* Stash clipped coords and set global "vector was clipped" flag. */ + + stash: + clip_x0 = x0; + clip_y0 = y0; + clip_x1 = x1; + clip_y1 = y1; + clip_z0 = z0; + clip_z1 = z1; + return clipped; +} + +/* draw a relative vector, depth-cued when appropriate */ + +static void +vector3(int i, int32 dx, int32 dy, int32 dz) /* unscaled display-file units */ +{ + int32 x0, y0, z0, x1, y1, z1; + + dx = stroking ? CSCALE(dx) : VSCALE(dx); /* apply scale factor (VS60) */ + dy = stroking ? CSCALE(dy) : VSCALE(dy); + dz = VSCALE(dz * 4); + x0 = PNORM(xpos); /* (includes offset) */ + y0 = PNORM(ypos); + z0 = PNORM(zpos); + xpos += dx; + ypos += dy; + zpos += dz; + x1 = PNORM(xpos); + y1 = PNORM(ypos); + z1 = PNORM(zpos); + dx = x1 - x0; + dy = y1 - y0; + dz = z1 - z0; + + if (stroking) { /* drawing a VS60 character */ + DEBUGF("offset, normalized stroke i%d (%ld,%ld) to (%ld,%ld)\r\n", + i, (long)x0,(long)y0, (long)x1,(long)y1); + + if (dx == 0 && dy == 0) { /* just display a point */ + if (i) { + if (menu) + illum3(x0 + MENU_OFFSET, y0, z0); + else + illum3(x0, y0, z0); /* illum3() checks ONCRT, int0_scope */ + } + return; + } + } else { + DEBUGF("offset, normalized vector i%d (%ld,%ld,%ld) to (%ld,%ld,%ld)\r\n", + i, (long)x0, (long)y0, (long)z0, (long)x1, (long)y1, (long)z1); + + line_counter = 037; /* reset line-style counter */ + + /* Maintenance Switch 3 => store delta length,tangent in xpos,ypos */ + if (maint3) { + int32 adx = ABS(dx), ady = ABS(dy); + if (adx == ady) { + xpos = 07777; /* ~ 1.0 */ + ypos = adx; /* or ady */ + } else if (adx > ady) { + xpos = adx; + ypos = 010000L * ady / adx + 1; /* truncates */ + } else /* (adx < ady) */ { + xpos = 010000L * adx / ady + 1; /* truncates */ + ypos = ady; /* according to DZVSC test 100 */ + } + DEBUGF("delta=0%o, tangent=0%o\r\n", xpos, ypos); + xpos = PSCALE(xpos); /* compensates for eventual PNORM */ + ypos = PSCALE(ypos); /* compensates for eventual PNORM */ + } + + /* clip to viewport ("working surface") if necessary */ + + /* + * Note about edge conditions and interrupts: + * + * The VT48 documentation isn't very clear about this, but the expected + * behavior has been determined from one of the VS60 diagnostics. The + * "edge flag" flip-flop (bit) corresponds directly to an edge interrupt + * (controlled by the "edge interrupt enable" bit in a Load Status BB + * instruction) and is set precisely twice for *each* vector that is + * clipped in *any* way (on->off, off->off, off->on), assuming that + * after each interrupt is caught a RESUME (set DPC with odd value) is + * issued. The X,Y position registers at the time of the first edge + * interrupt for a clipped vector give the starting position of the + * *visible* segment; the position registers at the time of the second + * edge interrupt for a clipped vector give the ending position of the + * *visible* segment. The "edge indicator" flip-flop (bit) at the time + * of an edge interrupt is set if and only if the vector has been + * clipped at that position. Thus for on-to-off, the edge indicator is + * set for just the second edge interrupt; for off-to-off, the edge + * indicator is set for both edge interrupts; for off-to-on, the edge + * indicator is set for just the first interrupt. Resuming after a + * vector has gone off-screen updates the position registers to the + * location (off-screen) specified in the display file. Edge interrupts + * share an interrupt vector with other "surface" interrupts such as + * light-pen hits. + * + * It appears from diagnostic DZVSD that the menu area might not be + * clipped. + * + * Note that the VT11 cannot generate edge interrupts, and its edge + * indicator provides less information than on the VS60. + */ + + switch (clip_vect = clip3(x0, y0, z0, x1, y1, z1)) { + case 1: /* clipped only on entry */ + case 3: /* clipped on entry and exit */ + edge_indic = 1; /* indicate clipped going in */ + /* XXX might not be correct for VT11 */ + case 2: /* clipped only on exit */ + edge_flag = edge_intr_ena; /* indicate vector-clip interrupt */ + if (edge_flag) { + edge_xpos = clip_x0; + edge_ypos = clip_y0; + edge_zpos = clip_z0; + edge_irq = 1; + } + clip_i = i; + return; /* may be drawn later by vt_cycle() */ + case 0: /* invisible */ + return; + default: + DEBUGF("clip() bad return: %d\n", clip_vect); + case -1: /* visible, not clipped */ + clip_vect = 0; + break; /* draw immediately */ + } + } + + if (dx == 0 && dy == 0 && dz == 0) + return; /* hardware skips null vector */ + + /* for character strokes, resort to scissoring: + illum3() illuminates only pixels that lie in the visible display area */ + + /* draw OK even when Maintenance Switch 3 is set */ + /* (but updated position registers must not be used to draw vector) */ + if (i && int0_scope && !clip_vect) {/* clipped vector drawn by vt_cycle() */ + if (menu) + lineTwoStep(x0 + MENU_OFFSET, y0, z0, x1 + MENU_OFFSET, y1, z1); + else + lineTwoStep(x0, y0, z0, x1, y1, z1); + } + + /* + * In case of LP hit, recompute coords using "tangent register", because: + * (1) distinct virtual CRT points can be mapped into the same pixel + * (2) raster computation might not match that of the actual VT48 + */ + + if (lp0_hit) { + long tangent; + int32 adx = ABS(dx), ady = ABS(dy); + if (adx >= ady) { + tangent = 010000L * dy / dx; /* signed */ + lp_ypos = y0 + tangent * (lp_xpos - x0) / 010000L; + tangent = 010000L * dz / dx; + lp_zpos = z0 + tangent * (lp_xpos - x0) / 010000L; + } else { + tangent = 010000L * dx / dy; /* signed */ + lp_xpos = x0 + tangent * (lp_ypos - y0) / 010000L; + tangent = 010000L * dz / dy; + lp_zpos = z0 + tangent * (lp_ypos - y0) / 010000L; + } + DEBUGF("adjusted LP coords (0%o,0%o,0%o)\r\n", + lp_xpos, lp_ypos, lp_zpos); + /* xpos,ypos,zpos still pertain to the original endpoint + (assuming that Maintenance Switch 3 isn't set) */ + } +} + +#define vector2(i,dx,dy) vector3(i,dx,dy,0) + /* the extra overhead for Z computation is not much */ + +/* basic vector (multiple of 45 degrees; directions numbered CCW, #0 => +X) */ +static void +basic_vector(int i, int dir, int len) /* unscaled display-file units */ +{ + int32 dx, dy; + + /* Alternatively, could be rasterized specially for each case; then + the general vector2() function could detect these special cases and + invoke this function to handle them, instead of the other way around. */ + + switch (dir) { + case 0: + dx = len; + dy = 0; + break; + case 1: + dx = len; + dy = len; + break; + case 2: + dx = 0; + dy = len; + break; + case 3: + dx = -len; + dy = len; + break; + case 4: + dx = -len; + dy = 0; + break; + case 5: + dx = -len; + dy = -len; + break; + case 6: + dx = 0; + dy = -len; + break; + case 7: + dx = len; + dy = -len; + break; + default: /* "can't happen" */ + DEBUGF("BUG: basic vector: illegal direction %d\r\n", dir); + return; + } + DEBUGF("basic "); + vector2(i, dx, dy); +} + +/* + * support for VS60 circle/arc option + * + * Since the literature that I have access to does not handle the case where + * starting and ending radii differ, I invented a solution that should be + * "good enough" for now: an approximation of an Archimedean spiral is drawn + * as connected individual chords, with the line-type counter applied (without + * being reset) over the entire curve. + * + * It is not known whether the direction is supposed to be clockwise or + * counterclockwise (the latter is assumed in the following code); it is + * assumed that if the starting and ending directions from the center point + * are identical, that a full circle is being specified. + * + * Although throughout the display simulation substantial effort has been + * invested to avoid using floating point, this preliminary implementation + * of the circle/arc generator does use floating point. Presumably this + * is avoidable, but the algorithmic details would need to be worked out. + * If use of floating point is a problem, #define NO_CONIC_OPT when compiling. + * + * The Z coordinate is linearly interpolated. + */ + +static void +conic3(int i, int32 dcx, int32 dcy, int32 dcz, int32 dex, int32 dey, int32 dez) + /* unscaled display-file units */ +{ +#ifdef NO_CONIC_OPT + /* just draw vector to endpoint (like real VS60 with option missing) */ + vector3(i, dex, dey, dez); +#else + int32 xs, ys, zs, xc, yc, zc, xe, ye, ze, x, y, z, nseg, seg; + double rs, re, dr, as, da, zo, dz; + int ons, one; /* ONSCREEN(xs,ys), ONSCREEN(xe,ye) */ + static double two_pi = -1.0; /* will be set (once only) to 2*Pi */ + static double k; /* will be set to 2-sqrt(4-(Pi/4)^2) */ + + if (two_pi < 0.0) { /* (initial entry only) */ + k = atan2(1.0, 1.0); + two_pi = 8.0 * k; + k = 2.0 - sqrt(4.0 - k*k); + } + dcx = VSCALE(dcx); /* apply vector scale factor */ + dcy = VSCALE(dcy); + dcz = VSCALE(dcz * 4); + dex = VSCALE(dex); + dey = VSCALE(dey); + dez = VSCALE(dez * 4); + xs = PNORM(xpos); /* starting pos. (includes offset) */ + ys = PNORM(ypos); + zs = PNORM(zpos); + xc = PNORM(xpos + dcx); /* center pos. (includes offset) */ + yc = PNORM(ypos + dcy); + zc = PNORM(zpos + dcz); + xe = PNORM(xpos + dex); /* ending pos. (includes offset) */ + ye = PNORM(ypos + dey); + ze = PNORM(zpos + dez); + /* determine vector from center to finish */ + dex -= dcx; /* PSCALEd */ + dey -= dcy; + dez -= dcz; + + DEBUGF("offset, normalized arc i%d s(%ld,%ld,%ld) c(%ld,%ld,%ld) e(%ld,%ld,%ld)\r\n", + i, (long)xs,(long)ys,(long)zs, (long)xc,(long)yc,(long)zc, + (long)xe,(long)ye,(long)ze); + + /* XXX not known whether Maintenance Switch 3 has any effect for arcs */ + + /* clip to viewport ("working surface") if necessary */ + + /* XXX not implemented yet [could check each chord individually] */ + + /* check for edge conditions (XXX change when conic clipping implemented) */ + /* XXX this test is very crude; should be much more complex */ + ons = ONSCREEN(xs, ys); + one = ONSCREEN(xe, ye); + edge_indic = ons && !one; + edge_flag = edge_indic || (!ons && one); + if (edge_flag) { + if (edge_intr_ena) { /* need to clip to viewport */ + /* XXX edge positions aren't right; need proper clipping */ + edge_xpos = xe; + edge_ypos = ye; + edge_zpos = ze; + edge_irq = 1; + goto done; + } else + edge_flag = 0; + } + /* XXX for now, resort to scissoring: + illuminates only pixels that lie in the visible display area */ + + if (dcx == 0 && dcy == 0 && dcz == 0 && dex == 0 && dey == 0 && dez == 0) + goto done; /* skip null curve */ + + /* determine starting, ending radii and their maximum */ + rs = PNORM(sqrt((double)dcx*dcx + (double)dcy*dcy)); /* (f.p.) */ + re = PNORM(sqrt((double)dex*dex + (double)dey*dey)); + dr = rs >= re ? rs : re; + + /* determine starting direction from center, and included angle */ + as = dcx == 0 && dcy == 0 ? 0.0 : atan2((double)-dcy, (double)-dcx); + da = (dex == 0 && dey == 0 ? 0.0 : atan2((double)dey, (double)dex)) - as; + while (da <= 0.0) /* exactly 0.0 implies full cycle */ + da += two_pi; + + /* determine number of chords to use; + make deviation from true curve no more than approximately one pixel */ + dr = reduce / dr; + if (dr > k) + dr = k; + nseg = (int32)(da / sqrt(4.0*dr - dr*dr) + 1.0); + if (nseg < 1) /* "can't happen" */ + nseg = 1; + else if (nseg > 360) + nseg = 360; /* arbitrarily chosen upper limit */ + + /* determine angular, radial, and Z step sizes */ + dr = (re - rs) / nseg; + da /= nseg; + dz = (double)(ze - zs) / nseg; + + if (menu) { + xs += MENU_OFFSET; + xc += MENU_OFFSET; + xe += MENU_OFFSET; + } + + line_counter = 037; /* reset line-style counter */ + + /* draw successive chords */ + zo = zs; + for (seg = 0; ++seg < nseg; ) { + rs += dr; + as += da; + re = rs * cos(as); + x = xc + (re >= 0 ? (int32)(re + 0.5) : -(int32)(-re + 0.5)); + re = rs * sin(as); + y = yc + (re >= 0 ? (int32)(re + 0.5) : -(int32)(-re + 0.5)); + z = (int32)(zo + seg * dz); /* truncates */ + lineTwoStep(xs, ys, zs, x, y, z);/* (continuing line style) */ + skip_start = 1; /* don't double-illuminate junctions */ + xs = x; + ys = y; + zs = z; + if (lphit_irq) + goto done; /* light-pen hit interrupted drawing */ + } + lineTwoStep(xs, ys, zs, xe, ye, ze);/* draw final chord to exact endpoint */ + + done: + skip_start = 0; /* important! */ + xpos += dcx + dex; /* update virtual beam position */ + ypos += dcy + dey; + zpos += dcz + dez; + if (lp0_hit) { + DEBUGF("LP hit on arc at (0%o,0%o,0%o)\r\n", + lp_xpos, lp_ypos, lp_zpos); + if (lphit_irq) { + /* XXX save parameters for drawing remaining chords */ + } + } +#endif +} + +#define conic2(i,dcx,dcy,dex,dey) conic3(i,dcx,dcy,0,dex,dey,0) + /* the extra overhead for Z computation is not much */ + +/* + * VT11 character font; + * 6x8 matrix, not serpentine encoded, decenders supported as in real VT11 + */ + +static const unsigned char dots[0200][6] = { + { 0x8f, 0x50, 0x20, 0x10, 0x08, 0x07 }, /* 000 lambda */ + { 0x1e, 0x21, 0x22, 0x14, 0x0c, 0x13 }, /* 001 alpha */ + { 0x00, 0x18, 0x24, 0xff, 0x24, 0x18 }, /* 002 phi */ + { 0x83, 0xc5, 0xa9, 0x91, 0x81, 0xc3 }, /* 003 SIGMA */ + { 0x00, 0x46, 0xa9, 0x91, 0x89, 0x06 }, /* 004 delta */ + { 0x03, 0x05, 0x09, 0x11, 0x21, 0x7f }, /* 005 DELTA */ + { 0x00, 0x20, 0x20, 0x3f, 0x01, 0x01 }, /* 006 iota */ + { 0x46, 0x29, 0x11, 0x2e, 0x40, 0x80 }, /* 007 gamma */ + { 0x7f, 0x80, 0x80, 0x80, 0x80, 0x7f }, /* 010 intersect */ + { 0x40, 0x3c, 0x04, 0xff, 0x04, 0x78 }, /* 011 psi */ + { 0x00, 0x10, 0x10, 0x54, 0x10, 0x10 }, /* 012 divide by */ + { 0x00, 0x60, 0x90, 0x90, 0x60, 0x00 }, /* 013 degree */ + { 0x00, 0x01, 0x00, 0x10, 0x00, 0x01 }, /* 014 therefore */ + { 0x01, 0x02, 0x3c, 0x02, 0x02, 0x3c }, /* 015 mu */ + { 0x11, 0x7f, 0x91, 0x81, 0x41, 0x03 }, /* 016 pound sterling */ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* 017 SHIFT IN */ + { 0x20, 0x40, 0x7f, 0x40, 0x7f, 0x40 }, /* 020 pi */ + { 0x00, 0xff, 0x00, 0x00, 0xff, 0x00 }, /* 021 parallel */ + { 0x1d, 0x23, 0x40, 0x42, 0x25, 0x19 }, /* 022 OMEGA */ + { 0x1c, 0x22, 0x61, 0x51, 0x4e, 0x40 }, /* 023 sigma */ + { 0x20, 0x40, 0x40, 0x7f, 0x40, 0x40 }, /* 024 UPSILON */ + { 0x00, 0x1c, 0x2a, 0x49, 0x49, 0x00 }, /* 025 epsilon */ + { 0x10, 0x38, 0x54, 0x10, 0x10, 0x10 }, /* 026 left arrow */ + { 0x10, 0x10, 0x10, 0x54, 0x38, 0x10 }, /* 027 right arrow */ + { 0x00, 0x20, 0x40, 0xfe, 0x40, 0x20 }, /* 030 up arrow */ + { 0x00, 0x04, 0x02, 0x7f, 0x02, 0x04 }, /* 031 down arrow */ + { 0x00, 0xff, 0x80, 0x80, 0x80, 0x80 }, /* 032 GAMMA */ + { 0x00, 0x01, 0x01, 0xff, 0x01, 0x01 }, /* 033 perpendicular */ + { 0x2a, 0x2c, 0x28, 0x38, 0x68, 0xa8 }, /* 034 unequal */ + { 0x24, 0x48, 0x48, 0x24, 0x24, 0x48 }, /* 035 approx equal */ + { 0x00, 0x20, 0x10, 0x08, 0x10, 0x20 }, /* 036 vel */ + { 0xff, 0x81, 0x81, 0x81, 0x81, 0xff }, /* 037 box */ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, /* 040 space */ + { 0x00, 0x00, 0x00, 0xfd, 0x00, 0x00 }, /* 041 ! */ + { 0x00, 0xe0, 0x00, 0x00, 0xe0, 0x00 }, /* 042 " */ + { 0x00, 0x24, 0xff, 0x24, 0xff, 0x24 }, /* 043 # */ + { 0x22, 0x52, 0xff, 0x52, 0x4c, 0x00 }, /* 044 $ */ + { 0x42, 0xa4, 0x48, 0x12, 0x25, 0x42 }, /* 045 % */ + { 0x66, 0x99, 0x99, 0x66, 0x0a, 0x11 }, /* 046 & */ + { 0x00, 0x00, 0x20, 0x40, 0x80, 0x00 }, /* 047 ' */ + { 0x00, 0x00, 0x3c, 0x42, 0x81, 0x00 }, /* 050 ( */ + { 0x00, 0x00, 0x81, 0x42, 0x3c, 0x00 }, /* 051 ) */ + { 0x00, 0x44, 0x28, 0xf0, 0x28, 0x44 }, /* 052 * */ + { 0x00, 0x10, 0x10, 0x7c, 0x10, 0x10 }, /* 053 + */ + { 0x00, 0x01, 0x06, 0x00, 0x00, 0x00 }, /* 054 , */ + { 0x00, 0x10, 0x10, 0x10, 0x10, 0x10 }, /* 055 - */ + { 0x00, 0x00, 0x06, 0x06, 0x00, 0x00 }, /* 056 . */ + { 0x02, 0x04, 0x08, 0x10, 0x20, 0x40 }, /* 057 / */ + { 0x7e, 0x85, 0x89, 0x91, 0xa1, 0x7e }, /* 060 0 */ + { 0x00, 0x41, 0xff, 0x01, 0x00, 0x00 }, /* 061 1 */ + { 0x47, 0x89, 0x91, 0x91, 0x91, 0x61 }, /* 062 2 */ + { 0x42, 0x81, 0x91, 0xb1, 0xd1, 0x8e }, /* 063 3 */ + { 0x0c, 0x14, 0x24, 0x44, 0xff, 0x04 }, /* 064 4 */ + { 0xf2, 0x91, 0x91, 0x91, 0x91, 0x8e }, /* 065 5 */ + { 0x3c, 0x46, 0x89, 0x89, 0x89, 0x46 }, /* 066 6 */ + { 0x40, 0x87, 0x88, 0x90, 0xa0, 0xc0 }, /* 067 7 */ + { 0x6e, 0x91, 0x91, 0x91, 0x91, 0x6e }, /* 070 8 */ + { 0x62, 0x91, 0x91, 0x91, 0x62, 0x3c }, /* 071 9 */ + { 0x00, 0x66, 0x66, 0x00, 0x00, 0x00 }, /* 072 : */ + { 0x00, 0x00, 0x61, 0x66, 0x00, 0x00 }, /* 073 ; */ + { 0x00, 0x18, 0x24, 0x42, 0x81, 0x00 }, /* 074 < */ + { 0x00, 0x28, 0x28, 0x28, 0x28, 0x28 }, /* 075 = */ + { 0x00, 0x81, 0x42, 0x24, 0x18, 0x00 }, /* 076 > */ + { 0x00, 0x40, 0x80, 0x9d, 0x90, 0x60 }, /* 077 ? */ + { 0x3c, 0x42, 0x91, 0xa9, 0xa9, 0x72 }, /* 100 @ */ + { 0x3f, 0x48, 0x88, 0x88, 0x48, 0x3f }, /* 101 A */ + { 0x81, 0xff, 0x91, 0x91, 0x91, 0x6e }, /* 102 B */ + { 0x3c, 0x42, 0x81, 0x81, 0x81, 0x42 }, /* 103 C */ + { 0x81, 0xff, 0x81, 0x81, 0x42, 0x3c }, /* 104 D */ + { 0x81, 0xff, 0x91, 0x91, 0x91, 0xc3 }, /* 105 E */ + { 0x81, 0xff, 0x91, 0x90, 0x80, 0xc0 }, /* 106 F */ + { 0x3c, 0x42, 0x81, 0x89, 0x89, 0x4f }, /* 107 G */ + { 0xff, 0x10, 0x10, 0x10, 0x10, 0xff }, /* 110 H */ + { 0x00, 0x81, 0xff, 0x81, 0x00, 0x00 }, /* 111 I */ + { 0x0e, 0x01, 0x01, 0x81, 0xfe, 0x80 }, /* 112 J */ + { 0xff, 0x08, 0x10, 0x28, 0x44, 0x83 }, /* 113 K */ + { 0x81, 0xff, 0x81, 0x01, 0x01, 0x03 }, /* 114 L */ + { 0xff, 0x40, 0x30, 0x30, 0x40, 0xff }, /* 115 M */ + { 0xff, 0x20, 0x10, 0x08, 0x04, 0xff }, /* 116 N */ + { 0x3c, 0x42, 0x81, 0x81, 0x42, 0x3c }, /* 117 O */ + { 0x81, 0xff, 0x90, 0x90, 0x90, 0x60 }, /* 120 P */ + { 0x3c, 0x42, 0x81, 0x8f, 0x42, 0x3d }, /* 121 Q */ + { 0x81, 0xff, 0x90, 0x98, 0x94, 0x63 }, /* 122 R */ + { 0x22, 0x51, 0x91, 0x91, 0x89, 0x46 }, /* 123 S */ + { 0xc0, 0x80, 0x81, 0xff, 0x81, 0xc0 }, /* 124 T */ + { 0xfe, 0x01, 0x01, 0x01, 0x01, 0xfe }, /* 125 U */ + { 0xff, 0x02, 0x04, 0x08, 0x10, 0xe0 }, /* 126 V */ + { 0xff, 0x02, 0x0c, 0x0c, 0x02, 0xff }, /* 127 W */ + { 0xc3, 0x24, 0x18, 0x18, 0x24, 0xc3 }, /* 130 X */ + { 0x00, 0xe0, 0x10, 0x0f, 0x10, 0xe0 }, /* 131 Y */ + { 0x83, 0x85, 0x89, 0x91, 0xa1, 0xc1 }, /* 132 Z */ + { 0x00, 0x00, 0xff, 0x81, 0x81, 0x00 }, /* 133 [ */ + { 0x00, 0x40, 0x20, 0x10, 0x08, 0x04 }, /* 134 \ */ + { 0x00, 0x00, 0x81, 0x81, 0xff, 0x00 }, /* 135 ] */ + { 0x00, 0x10, 0x20, 0x40, 0x20, 0x10 }, /* 136 ^ */ + { 0x01, 0x01, 0x01, 0x01, 0x01, 0x00 }, /* 137 _ */ + /* for all lowercase characters, first column is just a "descender" flag: */ + { 0x00, 0x00, 0x80, 0x40, 0x20, 0x00 }, /* 140 ` */ + { 0x00, 0x26, 0x29, 0x29, 0x2a, 0x1f }, /* 141 a */ + { 0x00, 0xff, 0x12, 0x21, 0x21, 0x1e }, /* 142 b */ + { 0x00, 0x1e, 0x21, 0x21, 0x21, 0x12 }, /* 143 c */ + { 0x00, 0x1e, 0x21, 0x21, 0x12, 0xff }, /* 144 d */ + { 0x00, 0x1e, 0x29, 0x29, 0x29, 0x19 }, /* 145 e */ + { 0x00, 0x20, 0x7f, 0xa0, 0xa0, 0x80 }, /* 146 f */ + { 0x01, 0x78, 0x85, 0x85, 0x49, 0xfe }, /* 147 g */ + { 0x00, 0xff, 0x10, 0x20, 0x20, 0x1f }, /* 150 h */ + { 0x00, 0x00, 0x21, 0xbf, 0x01, 0x00 }, /* 151 i */ + { 0x01, 0x02, 0x01, 0x81, 0xfe, 0x00 }, /* 152 j */ + { 0x00, 0xff, 0x08, 0x14, 0x22, 0x21 }, /* 153 k */ + { 0x00, 0x00, 0xfe, 0x01, 0x01, 0x00 }, /* 154 l */ + { 0x00, 0x3f, 0x20, 0x3f, 0x20, 0x3f }, /* 155 m */ + { 0x00, 0x3f, 0x10, 0x20, 0x20, 0x1f }, /* 156 n */ + { 0x00, 0x1e, 0x21, 0x21, 0x21, 0x1e }, /* 157 o */ + { 0x01, 0xff, 0x48, 0x84, 0x84, 0x78 }, /* 160 p */ + { 0x01, 0x78, 0x84, 0x84, 0x48, 0xff }, /* 161 q */ + { 0x00, 0x3f, 0x08, 0x10, 0x20, 0x20 }, /* 162 r */ + { 0x00, 0x12, 0x29, 0x29, 0x29, 0x26 }, /* 163 s */ + { 0x00, 0x20, 0xfe, 0x21, 0x21, 0x00 }, /* 164 t */ + { 0x00, 0x3e, 0x01, 0x01, 0x02, 0x3f }, /* 165 u */ + { 0x00, 0x3c, 0x02, 0x01, 0x02, 0x3c }, /* 166 v */ + { 0x00, 0x3e, 0x01, 0x1e, 0x01, 0x3e }, /* 167 w */ + { 0x00, 0x23, 0x14, 0x08, 0x14, 0x23 }, /* 170 x */ + { 0x01, 0xf8, 0x05, 0x05, 0x09, 0xfe }, /* 171 y */ + { 0x00, 0x23, 0x25, 0x29, 0x31, 0x21 }, /* 172 z */ + { 0x00, 0x18, 0x66, 0x81, 0x81, 0x00 }, /* 173 { */ + { 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00 }, /* 174 | */ + { 0x00, 0x00, 0x81, 0x81, 0x66, 0x18 }, /* 175 } */ + { 0x00, 0x0c, 0x10, 0x08, 0x04, 0x18 }, /* 176 ~ */ + { 0x00, 0xff, 0xff, 0xff, 0xff, 0xff } /* 177 rubout */ +}; + +/* + * VS60 character stroke table + * + * stroke[] contains "prototype" encodings for all vector strokes (visible and + * invisible) needed to draw each character at a standard size. The actual + * display is of course properly italicized, positioned, scaled, and rotated. + * + * Variable-length entries are used; each character stroke sequence is + * terminated by a 0-valued byte. Pointers to the appropriate data for all + * characters are stored into sstroke[] during a one-time initialization. + * + * The prototype strokes are for the most part constrained to a 4x6 unit area, + * except for a few cases that are handled by kludging the coordinates. + * Coordinates are relative to the left end of the character baseline. + * + * A prototype stroke is encoded as 8 bits SVXXXYYY: + * S = 0 if YYY is correct as is + * 1 if YYY needs to have 2 subtracted + * V = 0 if stroke is invisible (move) + * 1 if stroke is visible (draw) + * XXX = final X coord of stroke (0..4; 7 => -1) + * YYY = final Y coord of stroke (0..6) + */ + +static const unsigned char stroke[] = { + /* + * While based on the actual VT48 strokes, these have been tweaked + * (especially the lower-case letters, which had erratic sizes) to + * improve their appearance and/or reduce the number of strokes. + * Several of the special symbols (e.g. alpha, delta, iota) could + * be further improved, but I didn't want to make them look too + * different from the original. Note that VS60 screen photos + * disagree, for several characters, with the (incomplete) chart of + * strokes given in the VT48 manual. (There could have been ROM changes.) + * + * The simulated character sizes are not exact at all scales, but there + * is no really good way to fix this without spoiling the appearance. + * char. scale VS60 units simulation units (pixel has size!) + * 1/2 5 x 7 5 x 7 + * 1 10 x 14 9 x 13 + * 3/2 15 x 21 13 x 19 + * 2 20 x 28 17 x 25 + */ + 0111, 0123, 0006, 0115, 0131, 0140, 0, /* 000 lambda */ + 0042, 0132, 0114, 0103, 0112, 0134, 0144, 0, /* 001 alpha */ + 0011, 0103, 0115, 0135, 0143, 0131, 0111, 0010, + 0146, 0, /* 002 phi */ + 0040, 0100, 0133, 0106, 0146, 0, /* 003 SIGMA */ + 0022, 0111, 0120, 0131, 0113, 0115, 0124, 0, /* 004 delta */ + 0140, 0124, 0100, 0, /* 005 DELTA */ + 0006, 0126, 0120, 0140, 0, /* 006 iota */ + 0006, 0115, 0131, 0120, 0111, 0135, 0146, 0, /* 007 gamma */ + 0104, 0116, 0136, 0144, 0140, 0, /* 010 intersect */ + 0010, 0136, 0044, 0142, 0131, 0111, 0102, 0104, 0, /* 011 psi */ + 0022, 0122, 0003, 0143, 0024, 0124, 0, /* 012 divide by */ + 0024, 0115, 0126, 0135, 0124, 0, /* 013 degree */ + 0001, 0101, 0025, 0125, 0041, 0141, 0, /* 014 therefore */ + 0111, 0115, 0012, 0121, 0131, 0142, 0045, 0142, + 0151, 0, /* 015 mu */ + 0105, 0116, 0126, 0135, 0013, 0173, 0001, 0120, + 0130, 0141, 0, /* 016 pound sterling */ + 0, /* 017 SHIFT IN */ + 0003, 0114, 0144, 0034, 0130, 0010, 0114, 0, /* 020 pi */ + 0010, 0116, 0036, 0130, 0, /* 021 parallel */ + 0110, 0111, 0102, 0104, 0115, 0135, 0144, 0142, + 0131, 0130, 0140, 0, /* 022 OMEGA */ + 0025, 0134, 0132, 0120, 0110, 0102, 0104, 0146, 0, /* 023 sigma */ + 0010, 0136, 0046, 0116, 0105, 0, /* 024 UPSILON */ + 0003, 0133, 0045, 0136, 0116, 0105, 0101, 0110, + 0130, 0141, 0, /* 025 epsilon */ + 0042, 0102, 0113, 0011, 0102, 0, /* 026 left arrow */ + 0002, 0142, 0133, 0031, 0142, 0, /* 027 right arrow */ + 0020, 0124, 0133, 0013, 0124, 0, /* 030 up arrow */ + 0024, 0120, 0131, 0011, 0120, 0, /* 031 down arrow */ + 0106, 0146, 0144, 0, /* 032 GAMMA */ + 0140, 0026, 0120, 0, /* 033 perpendicular */ + 0001, 0145, 0044, 0104, 0002, 0142, 0, /* 034 unequal */ + 0001, 0112, 0131, 0142, 0044, 0133, 0114, 0103, 0, /* 035 approx equal */ + 0016, 0125, 0135, 0146, 0, /* 036 vel */ + 0106, 0146, 0140, 0100, 0, /* 037 box */ + 0, /* 040 space */ + 0020, 0120, 0021, 0125, 0, /* 041 ! */ + 0004, 0126, 0046, 0124, 0, /* 042 " */ + 0012, 0116, 0036, 0132, 0043, 0103, 0005, 0145, 0, /* 043 # */ + 0001, 0110, 0130, 0141, 0142, 0133, 0113, 0104, + 0105, 0116, 0136, 0145, 0026, 0120, 0, /* 044 $ */ + 0146, 0116, 0105, 0114, 0125, 0116, 0032, 0141, + 0130, 0121, 0132, 0, /* 045 % */ + 0040, 0104, 0105, 0116, 0126, 0135, 0134, 0101, + 0110, 0120, 0142, 0, /* 046 & */ + 0014, 0136, 0, /* 047 ' */ + 0030, 0112, 0114, 0136, 0, /* 050 ( */ + 0010, 0132, 0134, 0116, 0, /* 051 ) */ + 0002, 0146, 0026, 0122, 0042, 0106, 0, /* 052 * */ + 0021, 0125, 0003, 0143, 0, /* 053 + */ + 0211, 0120, 0121, 0, /* 054 , */ + 0003, 0143, 0, /* 055 - */ + 0020, 0120, 0, /* 056 . */ + 0146, 0, /* 057 / */ + 0001, 0145, 0136, 0116, 0105, 0101, 0110, 0130, + 0141, 0145, 0, /* 060 0 */ + 0010, 0130, 0020, 0126, 0115, 0, /* 061 1 */ + 0005, 0116, 0136, 0145, 0144, 0100, 0140, 0, /* 062 2 */ + 0001, 0110, 0130, 0141, 0142, 0133, 0113, 0005, + 0116, 0136, 0145, 0144, 0133, 0, /* 063 3 */ + 0030, 0136, 0025, 0102, 0142, 0, /* 064 4 */ + 0001, 0110, 0130, 0141, 0143, 0134, 0114, 0103, + 0106, 0146, 0, /* 065 5 */ + 0002, 0113, 0133, 0142, 0141, 0130, 0110, 0101, + 0105, 0116, 0136, 0145, 0, /* 066 6 */ + 0006, 0146, 0120, 0, /* 067 7 */ + 0013, 0133, 0142, 0141, 0130, 0110, 0101, 0102, + 0113, 0104, 0105, 0116, 0136, 0145, 0144, 0133, 0, /* 070 8 */ + 0001, 0110, 0130, 0141, 0145, 0136, 0116, 0105, + 0104, 0113, 0133, 0144, 0, /* 071 9 */ + 0022, 0122, 0024, 0124, 0, /* 072 : */ + 0010, 0121, 0122, 0024, 0124, 0, /* 073 ; */ + 0030, 0103, 0136, 0, /* 074 < */ + 0002, 0142, 0004, 0144, 0, /* 075 = */ + 0010, 0143, 0116, 0, /* 076 > */ + 0020, 0120, 0021, 0122, 0144, 0145, 0136, 0116, + 0105, 0104, 0, /* 077 ? */ + 0030, 0110, 0101, 0104, 0115, 0145, 0141, 0121, + 0112, 0113, 0124, 0134, 0131, 0, /* 100 @ */ + 0104, 0116, 0136, 0144, 0140, 0042, 0102, 0, /* 101 A */ + 0106, 0136, 0145, 0144, 0133, 0103, 0033, 0142, + 0141, 0130, 0100, 0, /* 102 B */ + 0041, 0130, 0110, 0101, 0105, 0116, 0136, 0145, 0, /* 103 C */ + 0106, 0136, 0145, 0141, 0130, 0100, 0, /* 104 D */ + 0003, 0133, 0046, 0106, 0100, 0140, 0, /* 105 E */ + 0106, 0146, 0033, 0103, 0, /* 106 F */ + 0023, 0143, 0141, 0130, 0110, 0101, 0105, 0116, + 0136, 0145, 0, /* 107 G */ + 0106, 0003, 0143, 0046, 0140, 0, /* 110 H */ + 0010, 0130, 0020, 0126, 0016, 0136, 0, /* 111 I */ + 0001, 0110, 0120, 0131, 0136, 0, /* 112 J */ + 0106, 0046, 0102, 0024, 0140, 0, /* 113 K */ + 0006, 0100, 0140, 0, /* 114 L */ + 0106, 0123, 0146, 0140, 0, /* 115 M */ + 0106, 0140, 0146, 0, /* 116 N */ + 0001, 0105, 0116, 0136, 0145, 0141, 0130, 0110, + 0101, 0, /* 117 O */ + 0106, 0136, 0145, 0144, 0133, 0103, 0, /* 120 P */ + 0030, 0110, 0101, 0105, 0116, 0136, 0145, 0141, + 0130, 0031, 0140, 0, /* 121 Q */ + 0106, 0136, 0145, 0144, 0133, 0103, 0033, 0140, 0, /* 122 R */ + 0001, 0110, 0130, 0141, 0142, 0133, 0113, 0104, + 0105, 0116, 0136, 0145, 0, /* 123 S */ + 0020, 0126, 0006, 0146, 0, /* 124 T */ + 0006, 0101, 0110, 0130, 0141, 0146, 0, /* 125 U */ + 0006, 0120, 0146, 0, /* 126 V */ + 0006, 0100, 0123, 0140, 0146, 0, /* 127 W */ + 0146, 0006, 0140, 0, /* 130 X */ + 0020, 0123, 0106, 0046, 0123, 0, /* 131 Y */ + 0006, 0146, 0100, 0140, 0033, 0113, 0, /* 132 Z */ + 0030, 0110, 0116, 0136, 0, /* 133 [ */ + 0006, 0140, 0, /* 134 \ */ + 0010, 0130, 0136, 0116, 0, /* 135 ] */ + 0003, 0126, 0143, 0, /* 136 ^ */ + 0140, 0, /* 137 _ */ + 0016, 0134, 0, /* original was backward */ /* 140 ` */ + 0032, 0112, 0101, 0110, 0130, 0133, 0124, 0114, 0, /* 141 a */ + 0006, 0100, 0120, 0131, 0133, 0124, 0104, 0, /* 142 b */ + 0033, 0124, 0114, 0103, 0101, 0110, 0120, 0131, 0, /* 143 c */ + 0036, 0130, 0110, 0101, 0103, 0114, 0134, 0, /* 144 d */ + 0002, 0132, 0133, 0124, 0114, 0103, 0101, 0110, + 0120, 0, /* 145 e */ + 0010, 0115, 0126, 0136, 0145, 0023, 0103, 0, /* 146 f */ + 0200, 0320, 0331, 0134, 0114, 0103, 0101, 0110, + 0130, 0, /* 147 g */ + 0106, 0004, 0124, 0133, 0130, 0, /* 150 h */ + 0020, 0124, 0025, 0125, 0, /* 151 i */ + 0201, 0310, 0320, 0331, 0134, 0035, 0135, 0, /* 152 j */ + 0105, 0034, 0101, 0023, 0130, 0, /* 153 k */ + 0010, 0130, 0020, 0126, 0116, 0, /* 154 l */ + 0104, 0114, 0122, 0134, 0144, 0140, 0, /* 155 m */ + 0104, 0124, 0133, 0130, 0, /* 156 n */ + 0010, 0120, 0131, 0133, 0124, 0114, 0103, 0101, + 0110, 0, /* 157 o */ + 0200, 0104, 0124, 0133, 0131, 0120, 0100, 0, /* 160 p */ + 0030, 0110, 0101, 0103, 0114, 0134, 0330, 0341, 0, /* 161 q */ + 0104, 0124, 0133, 0, /* 162 r */ + 0001, 0110, 0120, 0131, 0122, 0112, 0103, 0114, + 0124, 0133, 0, /* 163 s */ + 0030, 0121, 0125, 0034, 0114, 0, /* 164 t */ + 0014, 0111, 0120, 0130, 0141, 0144, 0, /* 165 u */ + 0004, 0120, 0144, 0, /* 166 v */ + 0004, 0102, 0110, 0122, 0130, 0142, 0144, 0, /* 167 w */ + 0134, 0004, 0130, 0, /* 170 x */ + 0210, 0120, 0134, 0004, 0120, 0, /* 171 y */ + 0004, 0134, 0100, 0130, 0, /* 172 z */ + 0030, 0121, 0122, 0113, 0124, 0125, 0136, 0, /* 173 { */ + 0020, 0122, 0024, 0126, 0, /* 174 | */ + 0010, 0121, 0122, 0133, 0124, 0125, 0116, 0, /* 175 } */ + 0003, 0114, 0132, 0143, 0, /* 176 ~ */ + 0140, 0146, 0106, 0100, 0010, 0116, 0026, 0120, + 0030, 0136, 0 /* 177 rubout */ + }; + +/* pointers to start of stroke data for each character */ +static const unsigned char *sstroke[128] = { NULL }; /* init. at run time */ + +/* character generator; supports control chars, POPR on term character (VS60) */ + +static int /* returns nonzero iff VS60 char terminate feature triggered */ +character(int c) +{ + /* following table maps cs_index to line-feed spacing for VS60 */ + static const unsigned char vs60_csp_h[4] = + {PSCALE(12), PSCALE(24), PSCALE(46), PSCALE(62)}; + /* following tables map cs_index to adjustments for sub/superscript */ + /* (cs_index 0 just a guess; others from VS60 Instruction Test Part II) */ + static const unsigned char sus_left[4] = + {PSCALE(0), PSCALE(2), PSCALE(4), PSCALE(3)}; + static const unsigned char susr_left[4] = + {PSCALE(0), PSCALE(2), PSCALE(4), PSCALE(0)}; + static const unsigned char sub_down[4] = + {PSCALE(2), PSCALE(3), PSCALE(6), PSCALE(7)}; + static const unsigned char sup_up[4] = + {PSCALE(5), PSCALE(9), PSCALE(18), PSCALE(24)}; + static const unsigned char esus_right[4] = + {PSCALE(0), PSCALE(2), PSCALE(0), PSCALE(0)}; + static const unsigned char esub_up[4] = + {PSCALE(2), PSCALE(3), PSCALE(6), PSCALE(8)}; + int x, y; + int32 xbase, ybase, xnext, ynext; + + if (shift_out) { + if (c >= 040) { + so_flag = char_irq = 1; /* will generate a char intr. */ + char_buf = c; + return 0; /* presumably, no POPR on term? */ + } + if (c == 017) { /* SHIFT IN */ + shift_out = 0; + goto copy; + } + } else { /* !shift_out */ + + if (c <= 040) { + switch (c) { + + case 000: /* NULL */ + goto cesc; /* apparently not copied to char_buf */ + case 010: /* BACKSPACE */ + if (char_rotate) + ypos -= CSCALE(vt11_csp_w); + else + xpos -= CSCALE(vt11_csp_w); + break; + case 012: /* LINE FEED */ + if (char_rotate) + xpos += (VT11 ? CSCALE(vt11_csp_h) : vs60_csp_h[cs_index]); + else + ypos -= (VT11 ? CSCALE(vt11_csp_h) : vs60_csp_h[cs_index]); + break; + case 015: /* CARRIAGE RETURN */ + if (char_rotate) + ypos = yoff; + else + xpos = xoff; + break; + case 016: /* SHIFT OUT */ + shift_out = 1; + break; + + case 021: /* SUPERSCRIPT */ + if (VT11) + break; + if (char_rotate) { + xpos -= sup_up[cs_index]; + ypos -= susr_left[cs_index]; + } else { + xpos -= sus_left[cs_index]; + ypos += sup_up[cs_index]; + } + if (cs_index > 0) + char_scale = csi2csf[--cs_index]; + break; + case 022: /* SUBSCRIPT */ + if (VT11) + break; + if (char_rotate) { + xpos += sub_down[cs_index]; + ypos -= susr_left[cs_index]; + } else { + xpos -= sus_left[cs_index]; + ypos -= sub_down[cs_index]; + } + if (cs_index > 0) + char_scale = csi2csf[--cs_index]; + break; + case 023: /* END SUPERSCRIPT */ + if (VT11) + break; + if (cs_index < 3) + char_scale = csi2csf[++cs_index]; + if (char_rotate) { + xpos += sup_up[cs_index]; + ypos += esus_right[cs_index]; + } else { + xpos += esus_right[cs_index]; + ypos -= sup_up[cs_index]; + } + break; + case 024: /* END SUBSCRIPT */ + if (VT11) + break; + if (cs_index < 3) + char_scale = csi2csf[++cs_index]; + if (char_rotate) { + xpos -= esub_up[cs_index]; + ypos += esus_right[cs_index]; + } else { + xpos += esus_right[cs_index]; + ypos += esub_up[cs_index]; + } + break; + case 040: /* SPACE */ + goto space; + default: /* other control codes ignored */ + break; + } + goto copy; + } + } + + /* VT11/VS60 doesn't draw any part of a character if its *baseline* is + (partly) offscreen; thus the top of a character might be clipped */ + /* (no allowance for descender, italic, or interchar. spacing) */ + + /* virtual CRT coordinates of this and the next character's "origin": */ + xbase = xnext = PNORM(xpos); + ybase = ynext = PNORM(ypos); + if (char_rotate) + ynext += (vt11_csp_w <= 12 ? 10 : 11); + else + xnext += (vt11_csp_w <= 12 ? 10 : 11); + + edge_indic = ONSCREEN(xbase, ybase) && !ONSCREEN(xnext, ynext); + edge_flag = edge_indic || + ((!ONSCREEN(xbase, ybase)) && ONSCREEN(xnext, ynext)); + /* (scaling cannot make spacing so large that it crosses the + "working surface" while going from offscreen to offscreen) */ + if (edge_flag) { + if (edge_intr_ena) { + edge_irq = 1; + goto space; + } else + edge_flag = 0; + } + + if (!ONSCREEN(xbase, ybase) || !ONSCREEN(xnext, ynext)) + goto space; + + /* plot a (nominally on-screen) graphic symbol */ + + if (VT11) { + unsigned char col, prvcol; + + /* plot a graphic symbol (unscaled, unrotated) using a dot matrix */ + + /* not drawn in a serpentine manner; supports control characters */ + + /* draw pattern using 2x2 dot size, with fudges for spacing & italics */ + /* (looks very nice under all conditions at full resolution) */ + + if (c >= 0140) { /* lower-case */ + if (dots[c][0]) /* flag: with descender */ + ybase -= 4; + x = 1; /* skip first column (descender flag) */ + } else /* no descender */ + x = 0; + + prvcol = 0; + col = dots[c][x]; /* starting column bit pattern */ + for (; x < 6; ++x) { + int xllc = 2*x, yllc = 0; + unsigned char nxtcol = (x == 5) ? 0 : dots[c][x+1]; + + /* no LP hit on first or last column */ + lp_suppress = x == 0 || x == 5; + + for (y = 0; y < 8; ++y) { + int delay_skew; + int compress = vt11_csp_w <= 12 && x == 2; + int dot = col & (1<>y) == 2)) + ++xllc; /* shift within selected dots */ + } + ++yllc; + if (dot) { + illum2(xbase + xllc, ybase + yllc); + if (!compress || nxtdot == 0) + illum2(xbase + xllc + 1, ybase + yllc); + } + if (italics && delay_skew) + ++xllc; /* shift between selected dots */ + ++yllc; + } + if (vt11_csp_w <= 12 && x == 2) /* narrow spacing: */ + --xbase; /* slight compression */ + + prvcol = col; + col = nxtcol; + } + lp_suppress = 0; + + } else { /* VS60 */ + const unsigned char *p; /* -> stroke data */ + unsigned char s; /* encoded stroke */ + int32 xlast, ylast; /* "beam follower" within character */ + int32 xp = xpos, yp = ypos; /* save these (altered by vector2()) */ + + /* plot a graphic symbol using vector strokes */ + + /* initialize starting stroke pointers upon first use only */ + if (sstroke[0] == NULL) { + p = stroke; /* -> stroke data */ + + for (s = 0; s < 128; ++s) { /* for each ASCII code value s */ + sstroke[s] = p; /* code's stroke list starts here */ + while (*p++) /* 0 terminates the data */ + ; + } + } + + stroking = 1; /* prevents stroke clipping etc. and + tells vector2() to apply global + character scale factor */ + xlast = ylast = 0; + for (p = sstroke[c]; (s = *p) != 0; ++p) { + xnext = (s & 0070) >> 3; + if (xnext == 7) + xnext = -1; /* (kludge needed for pound sterling) */ + ynext = s & 0007; /* delay stretching for just a moment */ + if (s & 0200) + ynext -= 2; /* kludge for stroke below baseline */ + xnext *= 2; + if (italics) + xnext += ynext; + ynext *= 2; /* safe to stretch now */ + + if (s & 0100) { /* visible stroke */ + int32 dx = xnext - xlast, /* (okay if both 0) */ + dy = ynext - ylast; + + if (char_rotate) + vector2(1, -dy, dx); + else + vector2(1, dx, dy); + } else /* invisible stroke, can do faster */ + if (char_rotate) { + xpos = xp - CSCALE(ynext); + ypos = yp + CSCALE(xnext); + } else { + xpos = xp + CSCALE(xnext); + ypos = yp + CSCALE(ynext); + } + xlast = xnext; + ylast = ynext; + skip_start = (s & 0100) && (p[1] & 0100); /* avoid bright dot */ + } + /* skip_start was reset to 0 by the last iteration! */ + stroking = 0; + xpos = xp; /* restore for use in spacing (below) */ + ypos = yp; + } /* end of graphic character drawing */ + + space: + if (char_rotate) + ypos += CSCALE(vt11_csp_w); + else + xpos += CSCALE(vt11_csp_w); + + /* There may have been multiple LP hits during drawing; + the last one is the only one that can be reported. */ + + copy: + char_buf = c; + + cesc: + if (char_escape && c == char_term) { /* (VS60) */ + pop(1); + return 1; + } else + return 0; +} + +/* + * Perform one display processor "cycle": + * If display processor is halted or awaiting sync, just performs "background" + * maintenance tasks and returns 0. + * Otherwise, draws any pending clipped vector (VS60 only). + * Otherwise, completes any pending second CHAR or BSVECT (must be a RESUME + * after interrupt on first CHAR or BSVECT), or fetches one word from the + * display file and processes it. May post an interrupt; returns 1 if display + * processor is still running, or 0 if halted or an interrupt was posted. + * + * word_number keeps track of the state of multi-word graphic data parsing; + * word_number also serves to keep track of half-word for graphic data having + * two independent entities encoded within one word (CHAR or BSVECT). + * Note that, for the VT11, there might be control words (e.g. JMPA) embedded + * within the data! (We don't know of any application that exploits this.) + */ +int +vt11_cycle(int us, int slowdown) +{ + static vt11word inst; + static int i; + static int32 x, y, z, ex, ey, sxo, syo, szo; + int c; + int32 ez; + static uint32 usec = 0; /* cumulative */ + static uint32 msec = 0; /* ditto */ + uint32 new_msec; + INIT + /* keep running time counter; track state even when processor is idle */ + + new_msec = (usec += us) / 1000; + + if (msec / BLINK_COUNT != new_msec / BLINK_COUNT) + blink_off = !blink_off; + + /* if awaiting sync, look for next frame start */ + if (sync_period && (msec / sync_period != new_msec / sync_period)) + sync_period = 0; /* start next frame */ + + msec = new_msec; + + if ((sync_period || maint1 || !busy) && !maint2) + goto age_ret; /* just age the display */ + + /* draw a clipped vector [perhaps after resume from edge interrupt] */ + + if (clip_vect) { + int32 dx = clip_x1 - clip_x0, + dy = clip_y1 - clip_y0, + dz = clip_z1 - clip_z0; + DEBUGF("clipped vector i%d (%ld,%ld,%ld) to (%ld,%ld,%ld)\r\n", clip_i, + (long)clip_x0, (long)clip_y0, (long)clip_z0, + (long)clip_x1, (long)clip_y1, (long)clip_z1); + if (VS60 /* XXX assuming VT11 doesn't display */ + && (dx != 0 || dy != 0 || dz != 0) /* hardware skips null vects */ + && clip_i && int0_scope) { /* show it */ + if (menu) + lineTwoStep(clip_x0 + MENU_OFFSET, clip_y0, clip_z0, + clip_x1 + MENU_OFFSET, clip_y1, clip_z1); + else + lineTwoStep(clip_x0, clip_y0, clip_z0, + clip_x1, clip_y1, clip_z1); + } + /* + * In case of LP hit, recompute coords using "tangent register", + * because: + * (1) distinct virtual CRT points can be mapped into the same pixel + * (2) raster computation might not match that of the actual VT48 + */ + if (lp0_hit) { + long tangent; + int32 adx = ABS(dx), ady = ABS(dy); + if (adx >= ady) { + tangent = 010000L * dy / dx; /* signed */ + lp_ypos = clip_y0 + tangent * (lp_xpos - clip_x0) / 010000L; + tangent = 010000L * dz / dx; + lp_zpos = clip_z0 + tangent * (lp_xpos - clip_x0) / 010000L; + } else { + tangent = 010000L * dx / dy; /* signed */ + lp_xpos = clip_x0 + tangent * (lp_ypos - clip_y0) / 010000L; + tangent = 010000L * dz / dy; + lp_zpos = clip_z0 + tangent * (lp_ypos - clip_y0) / 010000L; + } + DEBUGF("adjusted LP coords (0%o,0%o,0%o)\r\n", + lp_xpos, lp_ypos, lp_zpos); + /* xpos,ypos,zpos still pertain to the original endpoint + (assuming that Maintenance Switch 3 isn't set) */ + } + if (VS60) { /* XXX assuming just 1 intr for VT11 */ + edge_xpos = clip_x1; + edge_ypos = clip_y1; + edge_zpos = clip_z1; + edge_indic = (clip_vect & 2) != 0; /* indicate clipped going out */ + edge_flag = edge_intr_ena; + if (edge_flag) { + edge_irq = 1; + vt_lpen_intr(); /* post graphic interrupt to host */ + } + } + clip_vect = 0; /* this finishes the condition */ + goto check; /* possibly post more interrupts; age */ + } + + /* fetch next word from display file (if needed) and process it */ + + if (word_number != 1 || (graphic_mode != CHAR && graphic_mode != BSVECT)) { + time_out = vt_fetch((uint32)((DPC+reloc)&0777777), &inst); + DPC += 2; + if (time_out) + goto bus_timeout; + DEBUGF("0%06o: 0%06o\r\n", + (unsigned)(DPC - 2 + reloc) & 0777777, (unsigned)inst); + if (finish_jmpa) + goto jmpa; + if (finish_jsra) + goto jsra; + } + /* else have processed only half the CHAR or BSVECT data word so far */ + + fetched: + + if (TESTBIT(inst,15)) { /* control */ + unsigned op; + mode_field = GETFIELD(inst,14,11); /* save bits 14-11 for diags. */ + word_number = -1; /* flags "control mode"; ersatz 0 */ + switch (mode_field) { + + case 7: /* Set Graphic Mode 0111 */ + case 011: /* Set Graphic Mode 1001 */ + if (VT11) + goto bad_ins; + /*FALLTHRU*/ + case 010: /* Set Graphic Mode 1000 */ + if (VT11) { + DEBUGF("SGM 1000 IGNORED\r\n"); + break; + } + /*FALLTHRU*/ + case 0: /* Set Graphic Mode 0000 */ + case 1: /* Set Graphic Mode 0001 */ + case 2: /* Set Graphic Mode 0010 */ + case 3: /* Set Graphic Mode 0011 */ + case 4: /* Set Graphic Mode 0100 */ + case 5: /* Set Graphic Mode 0101 */ + case 6: /* Set Graphic Mode 0110 */ + DEBUGF("Set Graphic Mode %u", (unsigned)mode_field); + graphic_mode = (enum gmode)mode_field; + offset = 0; + shift_out = 0; /* seems to be right */ + if (TESTBIT(inst,10)) { + intensity = GETFIELD(inst,9,7); + DEBUGF(" intensity=%d", (int)intensity); + } + if (TESTBIT(inst,6)) { + lp0_intr_ena = TESTBIT(inst,5); + DEBUGF(" lp0_intr_ena=%d", (int)lp0_intr_ena); + } + if (TESTBIT(inst,4)) { + blink_ena = TESTBIT(inst,3); + DEBUGF(" blink=%d", (int)blink_ena); + } + if (TESTBIT(inst,2)) { + line_type = (enum linetype)GETFIELD(inst,1,0); + DEBUGF(" line_type=%d", (int)line_type); + } + DEBUGF("\r\n"); + break; + + case 012: /* 1010: Load Name Register */ + if (VT11) + goto bad_ins; + name = GETFIELD(inst,10,0); + DEBUGF("Load Name Register name=0%o\r\n", name); + { static unsigned nmask[4] = { 0, 03777, 03770, 03600 }; + + if (search != 0 && ((name^assoc_name) & nmask[search]) == 0) + name_irq = 1; /* will cause name-match interrupt */ + } + break; + + case 013: /* 1011: Load Status C */ + if (VT11) + goto bad_ins; + DEBUGF("Load Status C"); + if (TESTBIT(inst,9)) { + char_rotate = TESTBIT(inst,8); + DEBUGF(" char_rotate=d", (int)char_rotate); + } + if (TESTBIT(inst,7)) { + cs_index = GETFIELD(inst,6,5); /* 0, 1, 2, 3 */ + char_scale = csi2csf[cs_index]; /* for faster CSCALE macro */ + DEBUGF(" cs_index=%d(x%d/4)", (int)cs_index, (int)char_scale); + } + if (TESTBIT(inst,4)) { + vector_scale = GETFIELD(inst,3,0); + DEBUGF(" vector_scale=%d/4", (int)vector_scale); + } + DEBUGF("\r\n"); + break; + + case 014: /* 1100__ */ + if (VT11) /* other bits are "spare" */ + op = 0; /* always Display Jump Absolute */ + else + op = GETFIELD(inst,10,9); + switch (op) { + + case 0: /* 110000: Display Jump Absolute */ + finish_jmpa = 1; + break; + jmpa: + finish_jmpa = 0; + DPC = inst & ~1; + DEBUGF("Display Jump Absolute 0%06o\r\n", (unsigned)inst); + break; + + case 1: /* 110001: Display Jump Relative */ + ez = GETFIELD(inst,7,0);/* relative address (words) */ + ez *= 2; /* convert to bytes */ + /* have to be careful; DPC is unsigned */ + if (TESTBIT(inst,8)) { +#if 0 /* manual seems to say this, but it's wrong: */ + DPC -= ez; + DEBUGF("Display Jump Relative -0%o\r\n", + (unsigned)ez); +#else /* sign extend, twos complement add, 16-bit wrapping */ + DPC = (DPC + (~0777 | ez)) & 0177777; + DEBUGF("Display Jump Relative -0%o\r\n", + ~((~0777 | ez) - 1)); +#endif + } else { + DPC += (vt11word)ez; + DEBUGF("Display Jump Relative +0%o\r\n", + (unsigned)ez); + } + /* DPC was already incremented by 2 */ + break; + + case 2: /* 110010: Display Jump to Subroutine Absolute */ + finish_jsra = 1; + jsr = 1; /* diagnostic test needs this here */ + /* but the documentation says JSR bit set only for JSR REL! */ + goto check; /* (break would set jsr = 0) */ + jsra: + finish_jsra = 0; + push(); /* save return address and parameters */ + DPC = inst & ~1; + DEBUGF("Display Jump to Subroutine Absolute 0%06o\r\n", + (unsigned)inst); + goto check; /* (break would set jsr = 0) */ + + case 3: /* 110011: Display Jump to Subroutine Relative */ + ez = GETFIELD(inst,7,0);/* relative address (words) */ + ez *= 2; /* convert to bytes */ + push(); /* save return address and parameters */ + /* have to be careful; DPC is unsigned */ + if (TESTBIT(inst,8)) { +#if 0 /* manual seems to say this, but it's wrong: */ + DPC -= (vt11word)ez; + DEBUGF("Display Jump to Subroutine Relative -0%o\r\n", + (unsigned)ez); +#else /* sign extend, twos complement add, 16-bit wrapping */ + DPC = (DPC + (~0777 | ez)) & 0177777; + DEBUGF("Display Jump to Subroutine Relative -0%o\r\n", + ~((~0777 | ez) - 1)); +#endif + } else { + DPC += (vt11word)ez; + DEBUGF("Display Jump to Subroutine Relative +0%o\r\n", + (unsigned)ez); + } + /* DPC was already incremented by 2 */ + break; /* jsr = 0 ?? */ + } + break; + + case 015: /* 1101__ */ + if (VT11) + DEBUGF("Display NOP\r\n"); + else { + op = GETFIELD(inst,10,9); + switch (op) { + + case 0: /* 110100: Load Scope Selection */ + /* also used as Display NOP */ + DEBUGF("Load Scope Selection"); + c = TESTBIT(inst,8); + DEBUGF(" console=%d", c); + if (TESTBIT(inst,7)) { + ez = TESTBIT(inst,6); + DEBUGF(" blank=%d", (int)!ez); + if (c) + int1_scope = (unsigned char)(ez & 0xFF); + else + int0_scope = (unsigned char)(ez & 0xFF); + } + if (TESTBIT(inst,5)) { + ez = TESTBIT(inst,4); + DEBUGF(" lp_intr_ena=%d", (int)ez); + if (c) + lp1_intr_ena = (unsigned char)(ez & 0xFF); + else + lp0_intr_ena = (unsigned char)(ez & 0xFF); + } + if (TESTBIT(inst,3)) { + ez = TESTBIT(inst,2); + DEBUGF(" lp_sw_intr_ena=%d", (int)ez); + if (c) + lp1_sw_intr_ena = (unsigned char)(ez & 0xFF); + else + lp0_sw_intr_ena = (unsigned char)(ez & 0xFF); + } + DEBUGF("\r\n"); + break; + + case 1: /* 110101: Display POP Not Restore */ + DEBUGF("Display POP Not Restore\r\n"); + pop(0); /* sets new DPC as side effect */ + break; + + case 2: /* 110110: Display POP Restore */ + DEBUGF("Display POP Restore\r\n"); + pop(1); /* sets new DPC as side effect */ + break; + + default: /* 110111: undocumented -- ignored? */ + DEBUGF("Display NOP?\r\n"); + } + } + break; + + case 016: /* 1110: Load Status A */ + DEBUGF("Load Status A"); + internal_stop = TESTBIT(inst,10); /* 11101 Display Stop */ + if (internal_stop) { + stopped = 1; /* (synchronous with display cycle) */ + DEBUGF(" stop"); + } + if (TESTBIT(inst,9)) { + stop_intr_ena = TESTBIT(inst,8); + DEBUGF(" stop_intr_ena=%d", (int)stop_intr_ena); + } + if (TESTBIT(inst,7)) { + lp_intensify = !TESTBIT(inst,6); + DEBUGF(" lp_intensify=%d", (int)lp_intensify); + } + if (TESTBIT(inst,5)) { + italics = TESTBIT(inst,4); + DEBUGF(" italics=%d", (int)italics); + } + refresh_rate = GETFIELD(inst,VS60?3:2,2); + DEBUGF(" refresh=%d", refresh_rate); + if (sync_period != refresh_rate) + DEBUGF("old sync_period=%d, new refresh=%d", sync_period, refresh_rate); + switch (refresh_rate) { + case 0: /* continuous */ + sync_period = 0; + break; + case 1: /* VT11: 60 Hz; VS60: 30 Hz */ + sync_period = VT11 ? 17 : 33; + break; + case 2: /* VS60: 40 Hz */ + sync_period = 25; + break; + default: /* (case 3) VS60: external sync */ + sync_period = 17; /* fake a 60 Hz source */ + break; + } + if (internal_stop) { + sync_period = 0; /* overridden */ + } + if (VS60 && TESTBIT(inst,1)) { + menu = TESTBIT(inst,0); + DEBUGF(" menu=%d", (int)menu); + } + DEBUGF("\r\n"); + break; + + case 017: /* 1111_ */ + if (VS60 && TESTBIT(inst,10)) { /* 11111: Load Status BB */ + DEBUGF("Load Status BB"); + if (TESTBIT(inst,7)) { + depth_cue_proc = TESTBIT(inst,6); + DEBUGF(" depth_cue_proc=%d", (int)depth_cue_proc); + } + if (TESTBIT(inst,5)) { + edge_intr_ena = TESTBIT(inst,4); + DEBUGF(" edge_intr_ena=%d", (int)edge_intr_ena); + } + if (TESTBIT(inst,3)) { + file_z_data = TESTBIT(inst,2); + DEBUGF(" file_z_data=%d", (int)file_z_data); + } + if (TESTBIT(inst,1)) { + char_escape = TESTBIT(inst,0); + DEBUGF(" char_escape=%d", (int)char_escape); + } + } else { /* 11110: Load Status B */ + DEBUGF("Load Status B"); + if (VS60 && TESTBIT(inst,9)) { + color = (enum scolor)GETFIELD(inst,8,7); + DEBUGF(" color=%d", (int)color); + } + if (TESTBIT(inst,6)) { + graphplot_step = GETFIELD(inst,5,0); + DEBUGF(" graphplot_step=%d", (int)graphplot_step); + } + } + DEBUGF("\r\n"); + break; + + default: + bad_ins: DEBUGF("SPARE COMMAND 0%o\r\n", mode_field); + /* "display processor hangs" */ + DPC -= 2; /* hang around scene of crime */ + break; + + } /* end of control instruction opcode switch */ + jsr = 0; + + } else { /* graphic data */ + +#if 0 /* XXX ? */ + lp0_hit = 0; /* XXX maybe not for OFFSET? */ +#endif + if (word_number < 0) /* (after reset or control instr.) */ + word_number = 0; + if (word_number == 0) + offset = 0; + +#define MORE_DATA { ++word_number; goto check; } + + switch (mode_field = graphic_mode) { /* save for MPR read */ + + case CHAR: + if (word_number > 1) + word_number = 0; + if (word_number == 0) { + c = GETFIELD(inst,6,0); + DEBUGF("char1 %d (", c); + DEBUGF(040 <= c && c < 0177 ? "'%c'" : "0%o", c); + DEBUGF(")\r\n"); + if (character(c)) /* POPR was done; end chars */ + break; + MORE_DATA /* post any intrs now */ + } + c = GETFIELD(inst,15,8); + DEBUGF("char2 %d (", c); + DEBUGF(040 <= c && c < 0177 ? "'%c'" : "0%o", c); + DEBUGF(")\r\n"); + (void)character(c); + break; + + case SVECTOR: + if (word_number > 1 || (!file_z_data && word_number > 0)) + word_number = 0; + if (word_number == 0) { + i = TESTBIT(inst,14); /* inten_ena: beam on */ + x = GETFIELD(inst,12,7);/* delta_x */ + if (TESTBIT(inst,13)) + x = -x; + y = GETFIELD(inst,5,0); /* delta_y */ + if (TESTBIT(inst,6)) + y = -y; + if (file_z_data) + MORE_DATA + } + if (file_z_data) { /* (VS60) */ + z = GETFIELD(inst,9,2); /* delta_z */ + if (TESTBIT(inst,13)) + z = -z; + DEBUGF("short vector i%d (%d,%d,%d)\r\n", + i, (int)x, (int)y, (int)z); + vector3(i, x, y, z); + } else { + DEBUGF("short vector i%d (%d,%d)\r\n", i, (int)x, (int)y); + vector2(i, x, y); + } + break; + + case LVECTOR: + if (word_number > 2 || (!file_z_data && word_number > 1)) + word_number = 0; + if (word_number == 0) { + ex = VS60 && TESTBIT(inst,12); + i = TESTBIT(inst,14); + x = GETFIELD(inst,9,0); /* delta_x */ + if (TESTBIT(inst,13)) + x = -x; + MORE_DATA + } + if (word_number == 1) { + y = GETFIELD(inst,9,0); /* delta_y */ + if (TESTBIT(inst,13)) + y = -y; + if (file_z_data) + MORE_DATA + } + if (file_z_data) { /* (VS60) */ + if (ex) + goto norot; + z = GETFIELD(inst,9,2); /* delta_z */ + if (TESTBIT(inst,13)) + z = -z; + DEBUGF("long vector i%d (%d,%d,%d)\r\n", + i, (int)x, (int)y, (int)z); + vector3(i, x, y, z); + } else { + if (ex) + norot: /* undocumented and probably nonfunctional */ + DEBUGF("ROTATE NOT SUPPORTED\r\n"); + else { + DEBUGF("long vector i%d (%d,%d)\r\n", i, (int)x, (int)y); + vector2(i, x, y); + } + } + break; + + case POINT: /* (or OFFSET, if VS60) */ + /* [VT48 manual incorrectly says point data doesn't use sign bit] */ + if (word_number > 2 || (!file_z_data && word_number > 1)) + word_number = 0; + if (word_number == 0) { + ex = GETFIELD(inst,(VS60?11:9),0); + offset = VS60 && TESTBIT(inst,12); /* offset flag */ + if (!offset) + i = TESTBIT(inst,14); /* for point only */ + if (VS60) { + sxo = TESTBIT(inst,13); /* sign bit */ + if (sxo) + ex = -ex; + } + /* XXX if VT11, set xpos/xoff now?? */ + MORE_DATA + } + if (word_number == 1) { + ey = GETFIELD(inst,(VS60?11:9),0); + if (VS60) { + syo = TESTBIT(inst,13); /* sign bit */ + if (syo) + ey = -ey; + } + if (file_z_data) + MORE_DATA + } + if (file_z_data) { /* (VS60) */ + ez = GETFIELD(inst,11,2); + szo = TESTBIT(inst,13); /* sign bit */ + if (szo) + ez = -ez; + if (offset) { /* OFFSET rather than POINT */ + DEBUGF("offset (%d,%d,%d)\r\n", (int)ex,(int)ey,(int)ez); + xoff = PSCALE(ex); + yoff = PSCALE(ey); + zoff = PSCALE(ez * 4); /* XXX include bits 1:0 ? */ + s_xoff = (unsigned char)(sxo & 0xFF); + s_yoff = (unsigned char)(syo & 0xFF); + s_zoff = (unsigned char)(szo & 0xFF); + } else { + DEBUGF("point i%d (%d,%d,%d)\r\n", i, + (int)ex, (int)ey, (int)ez); + point3(i, VSCALE(ex) + xoff, VSCALE(ey) + yoff, + VSCALE(ez * 4) + zoff, VS60); + } + } else { + if (offset) { /* (VS60) OFFSET rather than POINT */ + DEBUGF("offset (%d,%d)\r\n", (int)ex, (int)ey); + xoff = PSCALE(ex); + yoff = PSCALE(ey); + s_xoff = (unsigned char)(sxo & 0xFF); + s_yoff = (unsigned char)(syo & 0xFF); + } else { + DEBUGF("point i%d (%d,%d)\r\n", i, (int)ex, (int)ey); + point2(i, VSCALE(ex) + xoff, VSCALE(ey) + yoff, VS60); + } + } + break; + + case GRAPHX: /* (or BLVECT if VS60) */ + word_number = 0; + i = TESTBIT(inst,14); + if (VS60 && TESTBIT(inst,10)) + goto blv; /* (VS60) BLVECT rather than GRAPHX */ + else { + ex = GETFIELD(inst,9,0); + DEBUGF("graphplot x (%d) i%d\r\n", (int)ex, i); + ey = ypos + VSCALE(graphplot_step); + /* VT48 ES says first datum doesn't increment Y; that's wrong */ + /* diagnostic DZVSD shows that "i" bit is ignored! */ + point2(1, VSCALE(ex) + xoff, ey, VS60); + } + break; + + case GRAPHY: /* (or BLVECT if VS60) */ + word_number = 0; + i = TESTBIT(inst,14); + if (VS60 && TESTBIT(inst,10)) { + blv: /* (VS60) BLVECT rather than GRAPHY */ + x = GETFIELD(inst,13,11); /* direction */ + y = GETFIELD(inst,9,0); /* length */ + DEBUGF("basic long vector i%d d%d l%d\r\n", + i, (int)x, (int)y); + basic_vector(i, (int)x, (int)y); + } else { + ey = GETFIELD(inst,9,0); + DEBUGF("graphplot y (%d) i%d\r\n", (int)ey, i); + ex = xpos + VSCALE(graphplot_step); + /* VT48 ES says first datum doesn't increment X; that's wrong */ + /* diagnostic DZVSD shows that "i" bit is ignored! */ + point2(1, ex, VSCALE(ey) + yoff, VS60); + } + break; + + case RELPOINT: + if (word_number > 1 || (!file_z_data && word_number > 0)) + word_number = 0; + if (word_number == 0) { + i = TESTBIT(inst,14); + ex = GETFIELD(inst,12,7); + if (TESTBIT(inst,13)) + ex = -ex; + ey = GETFIELD(inst,5,0); + if (TESTBIT(inst,6)) + ey = -ey; + if (file_z_data) + MORE_DATA + } + if (file_z_data) { /* (VS60) */ + ez = GETFIELD(inst,9,2); + if (TESTBIT(inst,13)) + ez = -ez; + DEBUGF("relative point i%d (%d,%d,%d)\r\n", + i, (int)ex, (int)ey, (int)ez); + point3(i, xpos + VSCALE(ex), ypos + VSCALE(ey), + zpos + VSCALE(ez * 4), 1); + } else { + DEBUGF("relative point i%d (%d,%d)\r\n", i, (int)ex, (int)ey); + point2(i, xpos + VSCALE(ex), ypos + VSCALE(ey), 1); + } + break; + + /* the remaining graphic data types are supported by the VS60 only */ + + case BSVECT: /* (VS60) */ + if (word_number > 1) + word_number = 0; + if (word_number == 0) { + i = TESTBIT(inst,14); + x = GETFIELD(inst,6,4); /* direction 0 */ + y = GETFIELD(inst,3,0); /* length 0 */ + ex = GETFIELD(inst,13,11); /* direction 1 */ + ey = GETFIELD(inst,10,7); /* length 1 */ + DEBUGF("basic short vector1 i%d d%d l%d\r\n", + i, (int)x, (int)y); + basic_vector(i, (int)x, (int)y); + if (lphit_irq || edge_irq) /* MORE_DATA skips this */ + vt_lpen_intr(); /* post graphic interrupt to host */ + MORE_DATA + } + DEBUGF("basic short vector2 i%d d%d l%d\r\n", i, (int)ex,(int)ey); + basic_vector(i, (int)ex, (int)ey); + break; + + case ABSVECTOR: /* (VS60) */ + /* Note: real VS60 can't handle a delta of more than +-4095 */ + if (word_number > 2 || (!file_z_data && word_number > 1)) + word_number = 0; + if (word_number == 0) { + i = TESTBIT(inst,14); + x = GETFIELD(inst,11,0); + if (TESTBIT(inst,13)) + x = -x; + MORE_DATA + } + if (word_number == 1) { + y = GETFIELD(inst,11,0); + if (TESTBIT(inst,13)) + y = -y; + if (file_z_data) + MORE_DATA + } + if (file_z_data) { + z = GETFIELD(inst,11,2); + if (TESTBIT(inst,13)) + z = -z; + DEBUGF("absolute vector i%d (%d,%d,%d)\r\n", + i, (int)x, (int)y, (int)z); + ex = VSCALE(x) + xoff; + ey = VSCALE(y) + yoff; + ez = VSCALE(z * 4) + zoff; + vector3(i, PNORM(ex - xpos), PNORM(ey - ypos), + PNORM(ez - zpos) / 4); /* approx. */ + zpos = ez; /* more precise, if PSCALEF > 1 */ + } else { + DEBUGF("absolute vector i%d (%d,%d)\r\n", i, (int)x, (int)y); + ex = VSCALE(x) + xoff; + ey = VSCALE(y) + yoff; + vector2(i, PNORM(ex - xpos), PNORM(ey - ypos)); /* approx. */ + } + xpos = ex; /* more precise, if PSCALEF > 1 */ + ypos = ey; + break; + + case CIRCLE: /* (VS60) */ + if (word_number > 5 || (!file_z_data && word_number > 3)) + word_number = 0; + if (word_number == 0) { + i = TESTBIT(inst,14); + x = GETFIELD(inst,9,0); /* delta cx */ + if (TESTBIT(inst,13)) + x = -x; + MORE_DATA + } + if (word_number == 1) { + y = GETFIELD(inst,9,0); /* delta cy */ + if (TESTBIT(inst,13)) + y = -y; + MORE_DATA + } + if (word_number == 2) { + if (file_z_data) { + z = GETFIELD(inst,11,2); /* delta cz */ + if (TESTBIT(inst,13)) + z = -z; + MORE_DATA + } + } + if (word_number == 2 + file_z_data) { + ex = GETFIELD(inst,9,0); /* delta ex */ + if (TESTBIT(inst,13)) + ex = -ex; + MORE_DATA + } + if (word_number == 3 + file_z_data) { + ey = GETFIELD(inst,9,0); /* delta ey */ + if (TESTBIT(inst,13)) + ey = -ey; + if (file_z_data) + MORE_DATA + } + if (file_z_data) { + ez = GETFIELD(inst,11,2); /* delta ez */ + if (TESTBIT(inst,13)) + ez = -ez; + DEBUGF("circle/arc i%d C(%d,%d,%d) E(%d,%d,%d)\r\n", + i, (int)x, (int)y, (int)z, (int)ex, (int)ey, (int)ez); + conic3(i, x, y, z, ex, ey, ez); /* approx. */ + } else { + DEBUGF("circle/arc i%d C(%d,%d) E(%d,%d)\r\n", + i, (int)x, (int)y, (int)ex, (int)ey); + conic2(i, x, y, ex, ey); + } + break; + + default: /* "can't happen" */ + DPC -= 2; /* hang around scene of crime */ + break; + + } /* end of graphic_mode switch */ + ++word_number; + + /* LP hit & edge interrupts triggered only while in data mode */ + if (lphit_irq || edge_irq) + vt_lpen_intr(); /* post graphic interrupt to host */ + + } /* end of instruction decoding and execution */ + goto check; + + bus_timeout: + DEBUGF("TIMEOUT\r\n"); + /* fall through to check (time_out has already been set) */ + + check: + + /* post an interrupt if conditions are right; + because this simulation has no pipeline, only one is active at a time */ + + if (lp0_sw_state != display_lp_sw) { /* tip-switch state change */ + lp0_sw_state = display_lp_sw; /* track switch state */ + lp0_up = !(lp0_down = lp0_sw_state); /* set transition flags */ + if (lp0_sw_intr_ena) + lpsw_irq = 1; + } + + if (lpsw_irq) /* (LP hit or edge interrupt already triggered above) */ + vt_lpen_intr(); /* post graphic interrupt to host */ + else if (internal_stop && stop_intr_ena) /* ext_stop does immediately */ + vt_stop_intr(); /* post stop interrupt to host */ + else if (char_irq || stack_over || stack_under || time_out) + vt_char_intr(); /* post character interrupt to host */ + else if (name_irq) + vt_name_intr(); /* post name-match interrupt to host */ +#if 1 /* risky? */ + else /* handle any pending 2nd CHAR/BSVECT */ + if (word_number == 1 && (graphic_mode==CHAR || graphic_mode==BSVECT)) + goto fetched; +#endif + + /* fall through to age_ret */ + + age_ret: + display_age(us, slowdown); + return !maint1 && !maint2 && busy; +} /* vt11_cycle */ diff --git a/display/vt11.h b/display/vt11.h new file mode 100644 index 0000000..c3ff63a --- /dev/null +++ b/display/vt11.h @@ -0,0 +1,144 @@ +/* + * $Id: vt11.h,v 1.8 2005/01/14 18:58:02 phil Exp $ + * interface to VT11 simulator + * Phil Budne + * September 16, 2003 + * Substantially revised by Douglas A. Gwyn, 14 Jan. 2004 + * + * prerequisite: display.h + */ + +/* + * Copyright (c) 2003-2004, Philip L. Budne and Douglas A. Gwyn + * + * 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 names of the authors 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. + */ + +#if defined(__cplusplus) +extern "C" { +#endif +#ifndef SIM_DEFS_H_ +typedef unsigned short uint16; +typedef int int32; +typedef unsigned int uint32; +#endif /* SIM_DEFS_H_ */ + +/* + * VT11 jumpers control character spacing; VS60 always uses VT11 normal. + * The VT11_CSP_{W,H} #defines establish the initial default character + * spacing; to change the VT11 simulation from these default values, + * set vt11_csp_{w,h} before calling any function named vt11_*. + */ +extern unsigned char vt11_csp_w; /* horizontal character spacing */ +#ifdef VT11_NARROW_OPT /* W3 or W6 installed */ +#define VT11_CSP_W 12 +#else /* VT11 normal; W4 or W5 installed */ +#define VT11_CSP_W 14 +#endif +extern unsigned char vt11_csp_h; /* vertical character spacing */ +#ifdef VT11_TALL_OPT /* W3 or W4 installed */ +#define VT11_CSP_H 26 +#else /* VT11 normal; W5 or W6 installed */ +#define VT11_CSP_H 24 +#endif + +/* + * The DISPLAY_TYPE #define establishes the initial default display + * type; to change from the default display type, set vt11_display + * before calling any function named vt11_* (other than vt11_reset()). + */ +#ifndef DISPLAY_TYPE +#define DISPLAY_TYPE DIS_VR17 /* default display type */ +#endif +extern enum display_type vt11_display; /* DIS_VR{14,17,48} */ +/* + * The PIX_SCALE #define establishes the initial default display scale + * factor; to change from the default scale factor, set vt11_scale + * before calling any function named vt11_* (other than vt11_reset()). + */ +#ifndef PIX_SCALE +#define PIX_SCALE RES_HALF /* default display scale factor */ +#endif +extern int vt11_scale; /* RES_{FULL,HALF,QUARTER,EIGHTH} */ +/* + * When vt11_init (READONLY) is nonzero, it indicates that it is too late + * to change display parameters (type, scale, character spacing, etc.). + */ +extern unsigned char vt11_init; /* set after display_init() called */ + +/* vt11.c simulates either a VT11 or a VT48(VS60), according to display type: */ +#define VS60 (vt11_display == DIS_VR48) +#define VT11 (!VS60) + +/* The display file is an array of 16-bit words. */ +typedef uint16 vt11word; + +extern int32 vt11_get_dpc(void); /* read Display PC */ +extern int32 vt11_get_mpr(void); /* read mode parameter register */ +extern int32 vt11_get_xpr(void); /* read graphplot incr/X pos register */ +extern int32 vt11_get_ypr(void); /* read char code/Y pos register */ +extern int32 vt11_get_rr(void); /* read relocate register */ +extern int32 vt11_get_spr(void); /* read status parameter register */ +extern int32 vt11_get_xor(void); /* read X offset register */ +extern int32 vt11_get_yor(void); /* read Y offset register */ +extern int32 vt11_get_anr(void); /* read associative name register */ +extern int32 vt11_get_scr(void); /* read slave console/color register */ +extern int32 vt11_get_nr(void); /* read name register */ +extern int32 vt11_get_sdr(void); /* read stack data register */ +extern int32 vt11_get_str(void); /* read char string term register */ +extern int32 vt11_get_sar(void); /* read stack address/maint register */ +extern int32 vt11_get_zpr(void); /* read Z position register */ +extern int32 vt11_get_zor(void); /* read Z offset register */ + +extern void vt11_set_dpc(uint16); /* write Display PC */ +extern void vt11_set_mpr(uint16); /* write mode parameter register */ +extern void vt11_set_xpr(uint16); /* write graphplot inc/X pos register */ +extern void vt11_set_ypr(uint16); /* write char code/Y pos register */ +extern void vt11_set_rr(uint16); /* write relocate register */ +extern void vt11_set_spr(uint16); /* write status parameter register */ +extern void vt11_set_xor(uint16); /* write X offset register */ +extern void vt11_set_yor(uint16); /* write Y offset register */ +extern void vt11_set_anr(uint16); /* write associative name register */ +extern void vt11_set_scr(uint16); /* write slave console/color register */ +extern void vt11_set_nr(uint16); /* write name register */ +extern void vt11_set_sdr(uint16); /* write stack data register */ +extern void vt11_set_str(uint16); /* write char string term register */ +extern void vt11_set_sar(uint16); /* write stack address/maint register */ +extern void vt11_set_zpr(uint16); /* write Z position register */ +extern void vt11_set_zor(uint16); /* write Z offset register */ + +extern void vt11_reset(void *, int); /* reset the display processor */ +extern int vt11_cycle(int, int); /* perform a display processor cycle */ + +/* + * callbacks from VT11/VS60 simulator (to SIMH PDP-11 VT driver, for example) + */ +extern int vt_fetch(uint32, vt11word *); /* get a display-file word */ +extern void vt_stop_intr(void); /* post a display-stop interrupt */ +extern void vt_lpen_intr(void); /* post a surface-related interrupt */ +extern void vt_char_intr(void); /* post a bad-char./timeout interrupt */ +extern void vt_name_intr(void); /* post a name-match interrupt */ + +#if defined(__cplusplus) +} +#endif diff --git a/display/vtmacs.h b/display/vtmacs.h new file mode 100644 index 0000000..58c7a27 --- /dev/null +++ b/display/vtmacs.h @@ -0,0 +1,297 @@ +/* + * $Id: vtmacs.h,v 1.3 2004/01/24 20:54:54 phil Exp - revised by DAG $ + * macros for coding a VT11/VS60 display file (instructions and data) + * for standalone use of vt11.c (not embedded in PDP-11 simulator) + * Douglas A. Gwyn + * September 03, 2004 + * + * XXX -- assumes ASCII host character set + */ + +/* helper macros (not for use outside this header): */ +#define SGN_(x) ((x) < 0) +#define MAG_(x) ((x) >= 0 ? (x) : -(x)) /* -0 not expressible directly in C */ +#if 0 /* manual seems to say this; wrong! */ +#define JDL_(x) ((SGN_(raddr) << 8) | MAG_(raddr)) +#else /* sign extend, 9-bit twos complement */ +#define JDL_(x) ((x) >= 0 ? (x) : ((~(unsigned)-(x))+1) & 0777) +#endif + +/* control instructions: */ + +/* load status register A: */ +#define LSRA(stop,stop_intr,lp_hit_chg,ital,refresh,menu) \ + 0170000 | stop | stop_intr | lp_hit_chg | ital | refresh | menu + /* display stop: */ +#define ST_SAME 00000 /* don't stop display */ +#define ST_STOP 02000 /* stop display */ + /* stop interrupt: */ +#define SI_SAME 00000 /* no change */ +#define SI_INHIBIT 01000 /* inhibit interrupt on stop */ +#define SI_GENERATE 01400 /* generate interrupt on stop */ + /* light pen hit intensify (bright-down on VS60): */ +#define LI_SAME 0000 /* no change */ +#define LI_INTENSIFY 0200 /* enable intensify on hit (VT11) */ +#define LI_BRIGHTDOWN 0200 /* enable bright down on hit (VS60) */ +#define LI_NOINTENSIFY 0300 /* inhibit intensify on hit (VT11) */ +#define LI_NOBRIGHTDOWN 0300 /* inhibit bright down on hit (VS60) */ + /* italic font: */ +#define IT_SAME 000 /* no change */ +#define IT_NORMAL 040 /* normal font */ +#define IT_ITALIC 060 /* italic font */ + /* refresh rate: */ +#define RF_UNSYNC 000 /* unsynchronized */ +#define RF_SAME 000 /* (happens to work like that) */ +#define RF_LINE 004 /* sync with line (VT11) */ +#define RF_30 004 /* 30 frames/sec (VS60) */ +#define RF_40 010 /* 40 frames/sec (VS60) */ +#define RF_EXT 014 /* external sync (VS60) */ + /* menu/main area (VS60): */ +#define MN_SAME 0 /* no change */ +#define MN_MAIN 2 /* major screen area */ +#define MN_MENU 3 /* menu area */ + +/* load status register B: */ +#define LSRB(color,set_step,step) \ + 0174000 | color | set_step | (step) + /* color select (VS60): */ +#define CL_SAME 00000 /* no change */ +#define CL_GREEN 01000 /* green */ +#define CL_YELLOW 01200 /* yellow */ +#define CL_ORANGE 01400 /* orange */ +#define CL_RED 01600 /* red */ + /* graphplot increment register change enable: */ +#define SS_SAME 0000 /* no change (step value ignored) */ +#define SS_CHANGE 0100 /* write step value into register */ + +/* load status register BB (VS60): */ +#define LSRBB(z_data,edge_intr,depth_cue,char_esc) \ + 0176000 | z_data | edge_intr | depth_cue | char_esc + /* file Z data: */ +#define ZD_SAME 000 /* no change */ +#define ZD_NO 010 /* d.file does not contain Z coords. */ +#define ZD_YES 014 /* d.file contains Z coordinates */ + /* edge interrupts enable: */ +#define ED_SAME 000 /* no change */ +#define ED_DIS 040 /* disable intr. on edge transition */ +#define ED_ENA 060 /* enable intr. on edge transition */ + /* depth cue processing: */ +#define DQ_SAME 0000 /* no change */ +#define DQ_OFF 0200 /* disable depth cueing (Z intensity) */ +#define DQ_ON 0300 /* enable depth cueing (Z intensity) */ + /* escape on terminating character: */ +#define ES_SAME 0 /* no change */ +#define ES_NO 2 /* disable POPR on terminating char. */ +#define ES_YES 3 /* enable POPR on terminating char. */ + +/* load status register C (VS60): */ +#define LSRC(rotate,cs_change,cscale,vs_change,vscale) \ + 0154000 | rotate | cs_change | ((cscale)<<5) | \ + vs_change | (vscale) + /* character rotation: */ +#define RO_SAME 00000 /* no change */ +#define RO_HORIZONTAL 01000 /* no text rotation */ +#define RO_VERTICAL 01400 /* rotate text 90 degrees CCW */ + /* character scale change enable: */ +#define CS_SAME 0000 /* no change (cscale value ignored) */ +#define CS_CHANGE 0200 /* set character scale */ + /* vector scale change enable: */ +#define VS_SAME 000 /* no change (vscale value ignored) */ +#define VS_CHANGE 020 /* set vector scale */ + +/* load scope selection register (VS60): */ +#define LSSR(console,disp,lp_intr,sw_intr) \ + 0164000 | console | disp | lp_intr | sw_intr + /* console to which this instruction applies: */ +#define CN_0 0000 /* console # 0 */ +#define CN_1 0400 /* console # 1 */ + /* display enable: */ +#define DS_SAME 0000 /* no change */ +#define DS_DIS 0200 /* disable display (blank CRT) */ +#define DS_ENA 0300 /* enable display (use CRT) */ + /* light-pen hit interrupt enable: */ +#define LH_SAME 0000 /* no change */ +#define LH_DIS 0040 /* light-pen hit interrupt disabled */ +#define LH_ENA 0060 /* light-pen hit interrupt enabled */ + /* tip-switch transition interrupt enable: */ +#define SW_SAME 0000 /* no change */ +#define SW_DIS 0010 /* tip-switch interrupt disabled */ +#define SW_ENA 0014 /* tip-switch hit interrupt enabled */ + +/* load name register (VS60): */ +#define LNR(name) \ + 0150000 | (name) + +/* set graphic mode: */ +#define SGM(mode,intens,lp_intr,blink,line_type) \ + 0100000 | mode | intens | lp_intr | blink | line_type + /* graphic mode: */ +#define GM_CHAR 000000 /* character */ +#define GM_SVECT 004000 /* short vector */ +#define GM_LVECT 010000 /* long vector */ +#define GM_APOINT 014000 /* absolute point, or offset */ +#define GM_GRAPHX 020000 /* graphplot X, or basic long vector */ +#define GM_GRAPHY 024000 /* graphplot Y, or basic long vector */ +#define GM_RPOINT 030000 /* relative point */ +#define GM_BSVECT 034000 /* basic short vector */ +#define GM_ARC 040000 /* circle/arc */ +#define GM_AVECT 044000 /* absolute vector */ + /* intensity: */ +#define IN_SAME 00000 /* no change */ +#define IN_0 02000 /* intensity level 0 (dimmest) */ +#define IN_1 02200 /* intensity level 1 */ +#define IN_2 02400 /* intensity level 2 */ +#define IN_3 02600 /* intensity level 3 */ +#define IN_4 03000 /* intensity level 4 */ +#define IN_5 03200 /* intensity level 5 */ +#define IN_6 03400 /* intensity level 6 */ +#define IN_7 03600 /* intensity level 7 (brightest) */ + /* light pen interrupt: */ +#define LP_SAME 0000 /* no change */ +#define LP_DIS 0100 /* light-pen hit interrupt disabled */ +#define LP_ENA 0140 /* light-pen hit interrupt enabled */ + /* blink: */ +#define BL_SAME 000 /* no change */ +#define BL_OFF 020 /* blink off */ +#define BL_ON 030 /* blink on */ + /* line type: */ +#define LT_SAME 00 /* no change */ +#define LT_SOLID 04 /* solid */ +#define LT_LDASH 05 /* long dash */ +#define LT_SDASH 06 /* short dash */ +#define LT_DDASH 07 /* dot dash */ + +/* display jump absolute: */ +#define DJMP_ABS(addr) \ + 0160000, \ + (addr) & ~1 + +/* display jump relative (VS60) [raddr in words]: */ +#define DJMP_REL(raddr) \ + 0161000 | JDL_(raddr) + +/* display jump to subroutine absolute (VS60): */ +#define DJSR_ABS(addr) \ + 0162000, \ + (addr) & ~1 + +/* display jump to subroutine relative (VS60) [raddr in words]: */ +#define DJSR_REL(raddr) \ + 0163000 | JDL_(raddr) + +/* display no-op: */ +#define DNOP \ + 0164000 + +/* display pop, no restore (VS60): */ +#define DPOP_NR \ + 0165000 + +/* display pop, restore (VS60): */ +#define DPOP_R \ + 0165000 + +/* display stop: */ +#define DSTOP LSRA(ST_STOP,SI_SAME,LI_SAME,IT_SAME,RF_UNSYNC,MN_SAME) + +/* graphic data: */ + + /* intensify enable (common to all modes exept CHAR and OFFSET): */ +#define I_OFF 000000 /* beam off */ +#define I_ON 040000 /* beam on */ + +/* Note: when VS60 "file Z data" is enabled, + use the *3() macros instead of the corresponding normal ones. */ + +/* character data: */ +#define CHAR(c1,c2) \ + ((c2) << 8) | (c1) /* 7-bit ASCII assumed */ + +/* short vector data: */ +#define SVECT(i,dx,dy) \ + i | (SGN_(dx) << 13) | (MAG_(dx) << 7) | (SGN_(dy) << 6) | MAG_(dy) +#define SVECT3(i,dx,dy,dz) \ + i | (SGN_(dx) << 13) | (MAG_(dx) << 7) | (SGN_(dy) << 6) | MAG_(dy), \ + (SGN_(dz) << 13) | (MAG_(dz) << 2) + +/* long vector data: */ +#define LVECT(i,dx,dy) \ + i | (SGN_(dx) << 13) | MAG_(dx), \ + (SGN_(dy) << 13) | MAG_(dy) +#define LVECT3(i,dx,dy,dz) \ + i | (SGN_(dx) << 13) | MAG_(dx), \ + (SGN_(dy) << 13) | MAG_(dy), \ + (SGN_(dz) << 13) | (MAG_(dz) << 2) + +/* rotation data (VS60, probably unimplemented): */ +#define ROTATE(i,a,b) \ + i | (SGN_(a) << 13) | 010000 | MAG_(a), \ + (SGN_(b) << 13) | MAG_(b) +#define ROTATE3(i,a,b,c) \ + i | (SGN_(a) << 13) | 010000 | MAG_(a), \ + (SGN_(b) << 13) | MAG_(b), \ + (SGN_(c) << 13) | (MAG_(c) << 2) + +/* absolute point data: */ +#define APOINT(i,x,y) \ + i | (SGN_(x) << 13) | MAG_(x), \ + (SGN_(y) << 13) | MAG_(y) +#define APOINT3(i,x,y,z) \ + i | (SGN_(x) << 13) | MAG_(x), \ + (SGN_(y) << 13) | MAG_(y), \ + (SGN_(z) << 13) | (MAG_(z) << 2) + +/* offset data (VS60): */ +#define OFFSET(x,y) \ + (SGN_(x) << 13) | 010000 | MAG_(x), \ + (SGN_(y) << 13) | 010000 | MAG_(y) +#define OFFSET3(x,y,z) \ + (SGN_(x) << 13) | 010000 | MAG_(x), \ + (SGN_(y) << 13) | 010000 | MAG_(y), \ + (SGN_(z) << 13) | 010000 | (MAG_(z) << 2) + +/* graphplot X data: */ +#define GRAPHX(i,x) \ + i | (x) + +/* graphplot Y data: */ +#define GRAPHY(i,y) \ + i | (y) + +/* basic long vector data (VS60): */ +#define BLVECT(i,dir,len) \ + i | ((dir) << 11) | 02000 | (len) + +/* relative point data: */ +#define RPOINT(i,dx,dy) \ + i | (SGN_(dx) << 13) | (MAG_(dx) << 7) | (SGN_(dy) << 6) | MAG_(dy) +#define RPOINT3(i,dx,dy,dz) \ + i | (SGN_(dx) << 13) | (MAG_(dx) << 7) | (SGN_(dy) << 6) | MAG_(dy), \ + (SGN_(dz) << 13) | (MAG_(dz) << 2) + +/* basic short vector data (VS60): */ +#define BSVECT(i,dir1,len1,dir2,len2) \ + i | ((dir2) << 11) | ((len2) << 7) | ((dir1) << 4) | (len1) + +/* circle/arc data (VS60, option): */ +#define ARC(i,dcx,dcy,dex,dey) \ + i | (SGN_(dcx) << 13) | MAG_(dcx), \ + (SGN_(dcy) << 13) | MAG_(dcy), \ + (SGN_(dex) << 13) | MAG_(dex), \ + (SGN_(dey) << 13) | MAG_(dey) +#define ARC3(i,dcx,dcy,cz,dex,dey,ez) \ + i | (SGN_(dcx) << 13) | MAG_(dcx), \ + (SGN_(dcy) << 13) | MAG_(dcy), \ + (SGN_(cz) << 13) | (MAG_(cz) << 2), \ + (SGN_(dex) << 13) | MAG_(dex), \ + (SGN_(dey) << 13) | MAG_(dey), \ + (SGN_(ez) << 13) | (MAG_(ez) << 2) + +/* absolute vector data (VS60): */ +#define AVECT(i,x,y) \ + i | (SGN_(x) << 13) | MAG_(x), \ + (SGN_(y) << 13) | MAG_(y) +#define AVECT3(i,x,y,z) \ + i | (SGN_(x) << 13) | MAG_(x), \ + (SGN_(y) << 13) | MAG_(y), \ + (SGN_(z) << 13) | (MAG_(z) << 2) diff --git a/display/vttest.c b/display/vttest.c new file mode 100644 index 0000000..2deb2ac --- /dev/null +++ b/display/vttest.c @@ -0,0 +1,1465 @@ +/* + * $Id: vttest.c,v 1.15 2005/08/06 21:09:05 phil Exp $ + * VT11 test + * Phil Budne + * September 13, 2003 + * Substantially revised by Douglas A. Gwyn, 05 Aug 2005 + * + * XXX -- assumes ASCII host character set + * + * In addition to providing some display tests, this program serves as an + * example of how the VT11/VS60 display processor simulator can be used + * without a PDP-11 simulator. The vt11_cycle() function performs a single + * "instruction cycle" of the display processor, and display_sync() forces + * the graphics changes to appear in the window system; thus these must be + * iterated at a fairly rapid rate to provide reasonable interaction. This + * implies that "host" computation must be kept minimal per iteration, or + * else done in a separate thread. When using multiple threads, the display + * file should be declared with "volatile" qualification to ensure that + * modifications are picked up by the display-processor thread. + * + * Part of the fun of display-file programming is figuring out ways to + * safely modify the display without stopping the display processor, which + * is asynchronously interpreting the display file. + */ +#undef FRAME1STOP /* define to pause after first frame of a section */ + +#ifndef TEST_DIS +#define TEST_DIS DIS_VR48 +#endif + +#ifndef TEST_RES +#define TEST_RES RES_HALF +#endif + +#include +#include +#include + +#include "ws.h" /* for ws_beep() */ +#include "display.h" +#include "vt11.h" +#include "vtmacs.h" + +#define USEC 3 /* simulated microseconds per cycle; + making this large causes flicker! */ + +#define JMPA 0160000 /* first word of DJMP_ABS */ + +#define SUPSCR 021 /* SUPERSCRIPT char */ +#define SUBSCR 022 /* SUBSCRIPT char */ +#define ENDSUP 023 /* END SUPERSCRIPT char */ +#define ENDSUB 024 /* END SUBSCRIPT char */ + +/* The following display file (whose words might be larger than 16 bits) is + divided into sections, each ended by a display-stop-with-interrupt + instruction followed by an extra word. The display-stop interrupt handler + replaces these two words with a jump to the start of the section, causing + an endless refresh loop. To advance to the next section, activate the + "tip switch" (mouse button 1); this works even if simulating a VT11. */ + +#define ENDSECT LSRA(ST_STOP,SI_GENERATE,LI_SAME,IT_SAME,RF_UNSYNC,MN_SAME), 0, +#define ENDFILE LSRA(ST_STOP,SI_GENERATE,LI_SAME,IT_SAME,RF_UNSYNC,MN_SAME), 1, + +/* FILE VT. Static displays that work for both VT11 and VS60. */ + +unsigned short VT[] = { + /* SECTION 1. Box just inside VR14 area using all four line types. + Suitable for VT11 and VS60. */ + + LSRA(ST_SAME, SI_SAME, LI_INTENSIFY, IT_NORMAL, RF_UNSYNC, MN_SAME), + + SGM(GM_APOINT, IN_5, LP_ENA, BL_OFF, LT_SAME), + APOINT(I_OFF, 0, 0), + + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_LDASH), + LVECT(I_ON, 01777, 0), + + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SDASH), + LVECT(I_ON, 0, 01377), + + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_DDASH), + LVECT(I_ON, -01777, 0), + + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SOLID), + LVECT(I_ON, 0, -01377), + + ENDSECT + + /* SECTION 2. All text characters (both normal and italic). + Suitable for VT11 and VS60. */ + + LSRA(ST_SAME, SI_SAME, LI_INTENSIFY, IT_NORMAL, RF_UNSYNC, MN_SAME), + + /* normal text */ + SGM(GM_APOINT, IN_7, LP_ENA, BL_OFF, LT_SAME), + APOINT(I_OFF, 0, 736), + + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_SAME, LT_SOLID), + CHAR(' ',' '), CHAR('A','B'), CHAR('C','D'), CHAR('E','F'), CHAR('G','H'), + CHAR('I','J'), CHAR('K','L'), CHAR('M','N'), CHAR('O','P'), CHAR('Q','R'), + CHAR('S','T'), CHAR('U','V'), CHAR('W','X'), CHAR('Y','Z'), CHAR('\r','\n'), + CHAR(' ',' '), CHAR('a','b'), CHAR('c','d'), CHAR('e','f'), CHAR('g','h'), + CHAR('i','j'), CHAR('k','l'), CHAR('m','n'), CHAR('o','p'), CHAR('q','r'), + CHAR('s','t'), CHAR('u','v'), CHAR('w','x'), CHAR('y','z'), CHAR('\r','\n'), + CHAR(' ',' '), CHAR('0','1'), CHAR('2','3'), CHAR('4','5'), CHAR('6','7'), + CHAR('8','9'), CHAR(' ','!'), CHAR('"','#'), CHAR('$','%'), CHAR('&','\''), + CHAR('(',')'), CHAR('*','+'), CHAR(',','-'), CHAR('.','/'), CHAR('@',0), + CHAR('\r','\n'), + CHAR(' ',' '), CHAR(':',';'), CHAR('<','='), CHAR('>','?'), CHAR('[','\\'), + CHAR(']','^'), CHAR('_','`'), CHAR('{','|'), CHAR('}','~'), CHAR(127,0), + CHAR('\r','\n'), + CHAR(' ',' '), CHAR(14,0), CHAR(1,2), CHAR(3,4), CHAR(5,6), CHAR(7,8), + CHAR(9,10), CHAR(11,12), CHAR(13,14), CHAR(16,17), CHAR(18,19), CHAR(20,21), + CHAR(22,23), CHAR(24,25), CHAR(26,27), CHAR(28,29), CHAR(30,31), + CHAR(15,0), CHAR('\r','\n'), + + /* italic text */ + LSRA(ST_SAME, SI_SAME, LI_SAME, IT_ITALIC, RF_UNSYNC, MN_SAME), + + /* note no SGMhere */ + CHAR(' ',' '), CHAR('A','B'), CHAR('C','D'), CHAR('E','F'), CHAR('G','H'), + CHAR('I','J'), CHAR('K','L'), CHAR('M','N'), CHAR('O','P'), CHAR('Q','R'), + CHAR('S','T'), CHAR('U','V'), CHAR('W','X'), CHAR('Y','Z'), CHAR('\r','\n'), + CHAR(' ',' '), CHAR('a','b'), CHAR('c','d'), CHAR('e','f'), CHAR('g','h'), + CHAR('i','j'), CHAR('k','l'), CHAR('m','n'), CHAR('o','p'), CHAR('q','r'), + CHAR('s','t'), CHAR('u','v'), CHAR('w','x'), CHAR('y','z'), CHAR('\r','\n'), + CHAR(' ',' '), CHAR('0','1'), CHAR('2','3'), CHAR('4','5'), CHAR('6','7'), + CHAR('8','9'), CHAR(' ','!'), CHAR('"','#'), CHAR('$','%'), CHAR('&','\''), + CHAR('(',')'), CHAR('*','+'), CHAR(',','-'), CHAR('.','/'), CHAR('@',0), + CHAR('\r','\n'), + CHAR(' ',' '), CHAR(':',';'), CHAR('<','='), CHAR('>','?'), CHAR('[','\\'), + CHAR(']','^'), CHAR('_','`'), CHAR('{','|'), CHAR('}','~'), CHAR(127,0), + CHAR('\r','\n'), + CHAR(' ',' '), CHAR(14,0), CHAR(1,2), CHAR(3,4), CHAR(5,6), CHAR(7,8), + CHAR(9,10), CHAR(11,12), CHAR(13,14), CHAR(16,17), CHAR(18,19), CHAR(20,21), + CHAR(22,23), CHAR(24,25), CHAR(26,27), CHAR(28,29), CHAR(30,31), + CHAR(15,0), CHAR('\r','\n'), + + ENDSECT + + /* SECTION 3. Fancy display involving all VT11 graphic modes. + Suitable for VT11 and VS60. */ + + LSRA(ST_SAME, SI_SAME, LI_INTENSIFY, IT_NORMAL, RF_UNSYNC, MN_SAME), + + /* normal text */ + SGM(GM_APOINT, IN_4, LP_ENA, BL_OFF, LT_SAME), + APOINT(I_OFF, 0, 01340), + + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_SAME, LT_SOLID), + CHAR(' ',' '), CHAR('A','B'), CHAR('C','D'), CHAR('E','F'), CHAR('G','H'), + CHAR('I','J'), CHAR('K','L'), CHAR('M','N'), CHAR('O','P'), CHAR('Q','R'), + CHAR('S','T'), CHAR('U','V'), CHAR('W','X'), CHAR('Y','Z'), CHAR('\r','\n'), + CHAR(' ',' '), CHAR('a','b'), CHAR('c','d'), CHAR('e','f'), CHAR('g','h'), + CHAR('i','j'), CHAR('k','l'), CHAR('m','n'), CHAR('o','p'), CHAR('q','r'), + CHAR('s','t'), CHAR('u','v'), CHAR('w','x'), CHAR('y','z'), CHAR('\r','\n'), + CHAR(' ',' '), CHAR('0','1'), CHAR('2','3'), CHAR('4','5'), CHAR('6','7'), + CHAR('8','9'), CHAR(' ','!'), CHAR('"','#'), CHAR('$','%'), CHAR('&','\''), + CHAR('(',')'), CHAR('*','+'), CHAR(',','-'), CHAR('.','/'), CHAR('@',0), + CHAR('\r','\n'), + CHAR(' ',' '), CHAR(':',';'), CHAR('<','='), CHAR('>','?'), CHAR('[','\\'), + CHAR(']','^'), CHAR('_','`'), CHAR('{','|'), CHAR('}','~'), CHAR(127,0), + CHAR('\r','\n'), + CHAR(' ',' '), CHAR(14,0), CHAR(1,2), CHAR(3,4), CHAR(5,6), CHAR(7,8), + CHAR(9,10), CHAR(11,12), CHAR(13,14), CHAR(16,17), CHAR(18,19), CHAR(20,21), + CHAR(22,23), CHAR(24,25), CHAR(26,27), CHAR(28,29), CHAR(30,31), + CHAR(15,0), CHAR('\r','\n'), + + /* italic text */ + LSRA(ST_SAME, SI_SAME, LI_SAME, IT_ITALIC, RF_UNSYNC, MN_SAME), + + /* note no SGMhere */ + CHAR(' ',' '), CHAR('A','B'), CHAR('C','D'), CHAR('E','F'), CHAR('G','H'), + CHAR('I','J'), CHAR('K','L'), CHAR('M','N'), CHAR('O','P'), CHAR('Q','R'), + CHAR('S','T'), CHAR('U','V'), CHAR('W','X'), CHAR('Y','Z'), CHAR('\r','\n'), + CHAR(' ',' '), CHAR('a','b'), CHAR('c','d'), CHAR('e','f'), CHAR('g','h'), + CHAR('i','j'), CHAR('k','l'), CHAR('m','n'), CHAR('o','p'), CHAR('q','r'), + CHAR('s','t'), CHAR('u','v'), CHAR('w','x'), CHAR('y','z'), CHAR('\r','\n'), + CHAR(' ',' '), CHAR('0','1'), CHAR('2','3'), CHAR('4','5'), CHAR('6','7'), + CHAR('8','9'), CHAR(' ','!'), CHAR('"','#'), CHAR('$','%'), CHAR('&','\''), + CHAR('(',')'), CHAR('*','+'), CHAR(',','-'), CHAR('.','/'), CHAR('@',0), + CHAR('\r','\n'), + CHAR(' ',' '), CHAR(':',';'), CHAR('<','='), CHAR('>','?'), CHAR('[','\\'), + CHAR(']','^'), CHAR('_','`'), CHAR('{','|'), CHAR('}','~'), CHAR(127,0), + CHAR('\r','\n'), + CHAR(' ',' '), CHAR(14,0), CHAR(1,2), CHAR(3,4), CHAR(5,6), CHAR(7,8), + CHAR(9,10), CHAR(11,12), CHAR(13,14), CHAR(16,17), CHAR(18,19), CHAR(20,21), + CHAR(22,23), CHAR(24,25), CHAR(26,27), CHAR(28,29), CHAR(30,31), + CHAR(15,0), CHAR('\r','\n'), + + /* labeled lines of all types, blinks, and intensities (LP intr disabled) */ + LSRA(ST_SAME, SI_SAME, LI_INTENSIFY, IT_NORMAL, RF_UNSYNC, MN_SAME), + + SGM(GM_APOINT, IN_SAME, LP_DIS, BL_SAME, LT_SAME), + APOINT(I_OFF, 020, 0740), + + SGM(GM_CHAR, IN_0, LP_SAME, BL_OFF, LT_SAME), + CHAR('I','N'), CHAR('T',' '), CHAR('0',0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_ON, 0140, 0740), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + APOINT(I_ON, 0150, 0740), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0160, 0740), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SOLID), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_LDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_DDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 020, 0700), + + SGM(GM_CHAR, IN_1, LP_SAME, BL_OFF, LT_SAME), + CHAR('I','N'), CHAR('T',' '), CHAR('1',0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_ON, 0140, 0700), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + APOINT(I_ON, 0150, 0700), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0160, 0700), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SOLID), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_LDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_DDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 020, 0640), + + SGM(GM_CHAR, IN_2, LP_SAME, BL_OFF, LT_SAME), + CHAR('I','N'), CHAR('T',' '), CHAR('2',0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_ON, 0140, 0640), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + APOINT(I_ON, 0150, 0640), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0160, 0640), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SOLID), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_LDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_DDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 020, 0600), + + SGM(GM_CHAR, IN_3, LP_SAME, BL_OFF, LT_SAME), + CHAR('I','N'), CHAR('T',' '), CHAR('3',0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_ON, 0140, 0600), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + APOINT(I_ON, 0150, 0600), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0160, 0600), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SOLID), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_LDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_DDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 020, 0540), + + SGM(GM_CHAR, IN_4, LP_SAME, BL_OFF, LT_SAME), + CHAR('I','N'), CHAR('T',' '), CHAR('4',0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_ON, 0140, 0540), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + APOINT(I_ON, 0150, 0540), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0160, 0540), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SOLID), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_LDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_DDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 020, 0500), + + SGM(GM_CHAR, IN_5, LP_SAME, BL_OFF, LT_SAME), + CHAR('I','N'), CHAR('T',' '), CHAR('5',0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_ON, 0140, 0500), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + APOINT(I_ON, 0150, 0500), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0160, 0500), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SOLID), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_LDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_DDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 020, 0440), + + SGM(GM_CHAR, IN_6, LP_SAME, BL_OFF, LT_SAME), + CHAR('I','N'), CHAR('T',' '), CHAR('6',0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_ON, 0140, 0440), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + APOINT(I_ON, 0150, 0440), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0160, 0440), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SOLID), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_LDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_DDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 020, 0400), + + SGM(GM_CHAR, IN_7, LP_SAME, BL_OFF, LT_SAME), + CHAR('I','N'), CHAR('T',' '), CHAR('7',0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_ON, 0140, 0400), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + APOINT(I_ON, 0150, 0400), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0160, 0400), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SOLID), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_LDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_DDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + /* similar, but LP intr enabled, official threshold intensities */ + SGM(GM_APOINT, IN_SAME, LP_ENA, BL_SAME, LT_SAME), + APOINT(I_OFF, 020, 0340), + + LSRA(ST_SAME, SI_SAME, LI_SAME, IT_ITALIC, RF_UNSYNC, MN_SAME), + + SGM(GM_CHAR, IN_6, LP_SAME, BL_ON, LT_SAME), + CHAR('I','N'), CHAR('T','R'), + + SGM(GM_APOINT, IN_4, LP_SAME, BL_OFF, LT_SAME), + APOINT(I_ON, 0140, 0340), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + APOINT(I_ON, 0150, 0340), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0160, 0340), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SOLID), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_LDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_SDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_OFF, LT_DDASH), + SVECT(I_ON, 060, 0), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_ON, LT_SAME), + SVECT(I_ON, 060, 0), + + /* graphplots */ + SGM(GM_APOINT, IN_5, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_ON, 040, 0200), + + LSRB(CL_SAME, SS_CHANGE, 040), + + SGM(GM_GRAPHY, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + GRAPHY(I_ON, 0160), + GRAPHY(I_ON, 0140), + GRAPHY(I_ON, 0120), + GRAPHY(I_ON, 0100), + GRAPHY(I_ON, 0060), + GRAPHY(I_ON, 0040), + + SGM(GM_RPOINT, IN_SAME, LP_SAME, BL_OFF, LT_SAME), + RPOINT(I_OFF, 0040, 0), + RPOINT(I_ON, 0040, 0), + + LSRB(CL_SAME, SS_CHANGE, 020), + + SGM(GM_GRAPHX, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + GRAPHX(I_ON, 0500), + GRAPHX(I_ON, 0540), + GRAPHX(I_ON, 0600), + GRAPHX(I_ON, 0640), + GRAPHX(I_ON, 0700), + GRAPHX(I_ON, 0740), + + /* long vectors in all directions from a common origin */ + SGM(GM_APOINT, IN_4, LP_SAME, BL_SAME, LT_SOLID), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0400, 0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0400, 0100), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0400, 0200), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0400, 0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0300, 0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0200, 0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0100, 0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0, 0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0100, 0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0200, 0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0300, 0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0400, 0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0400, 0200), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0400, 0100), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0400, 0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0400, -0100), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0400, -0200), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0400, -0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0300, -0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0200, -0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, -0100, -0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0, -0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0100, -0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0200, -0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0300, -0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0400, -0300), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0400, -0200), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01400, 01100), + SGM(GM_LVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LVECT(I_ON, 0400, -0100), + + /* nearby lines with varied spacing */ + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01200, 0500), + + SGM(GM_SVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + SVECT(I_ON, 077, 0), + SVECT(I_OFF, -077, -1), + SVECT(I_ON, 077, 0), + SVECT(I_OFF, -077, -2), + SVECT(I_ON, 077, 0), + SVECT(I_OFF, -077, -3), + SVECT(I_ON, 077, 0), + SVECT(I_OFF, -077, -4), + SVECT(I_ON, 077, 0), + SVECT(I_OFF, -077, -5), + SVECT(I_ON, 077, 0), + SVECT(I_OFF, -077, -6), + SVECT(I_ON, 077, 0), + SVECT(I_OFF, -077, -7), + SVECT(I_ON, 077, 0), + SVECT(I_OFF, -077, -010), + SVECT(I_ON, 077, 0), + SVECT(I_OFF, -077, 044), + SVECT(I_ON, 0, -077), + SVECT(I_OFF, 1, 077), + SVECT(I_ON, 0, -077), + SVECT(I_OFF, 2, 077), + SVECT(I_ON, 0, -077), + SVECT(I_OFF, 3, 077), + SVECT(I_ON, 0, -077), + SVECT(I_OFF, 4, 077), + SVECT(I_ON, 0, -077), + SVECT(I_OFF, 5, 077), + SVECT(I_ON, 0, -077), + SVECT(I_OFF, 6, 077), + SVECT(I_ON, 0, -077), + SVECT(I_OFF, 7, 077), + SVECT(I_ON, 0, -077), + SVECT(I_OFF, 010, 077), + SVECT(I_ON, 0, -077), + + /* all four flavors of characters (lp intr enabled, but intensity 4) */ + + SGM(GM_APOINT, IN_4, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01040, 0240), + + LSRA(ST_SAME, SI_SAME, LP_SAME, IT_NORMAL, RF_UNSYNC, MN_SAME), + + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_OFF, LT_SAME), + CHAR('N','o'), CHAR('r','m'), CHAR('a','l'), + + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_ON, LT_SAME), + CHAR(' ','B'), CHAR('l','i'), CHAR('n','k'), + + SGM(GM_APOINT, IN_4, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01040, 0200), + + LSRA(ST_SAME, SI_SAME, LP_SAME, IT_ITALIC, RF_UNSYNC, MN_SAME), + + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_OFF, LT_SAME), + CHAR('I','t'), CHAR('a','l'), CHAR('i','c'), + + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_ON, LT_SAME), + CHAR(' ','B'), CHAR('l','i'), CHAR('n','k'), + + /* all eight intensities of characters (lp intr enabled) */ + + LSRA(ST_SAME, SI_SAME, LP_SAME, IT_NORMAL, RF_UNSYNC, MN_SAME), + + SGM(GM_APOINT, IN_5, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01040, 0100), + + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_OFF, LT_SAME), + CHAR('I','N'), CHAR('T',' '), + + SGM(GM_CHAR, IN_0, LP_SAME, BL_SAME, LT_SAME), + CHAR('0',0), + SGM(GM_CHAR, IN_1, LP_SAME, BL_SAME, LT_SAME), + CHAR('1',0), + SGM(GM_CHAR, IN_2, LP_SAME, BL_SAME, LT_SAME), + CHAR('2',0), + SGM(GM_CHAR, IN_3, LP_SAME, BL_SAME, LT_SAME), + CHAR('3',0), + SGM(GM_CHAR, IN_4, LP_SAME, BL_SAME, LT_SAME), + CHAR('4',0), + SGM(GM_CHAR, IN_5, LP_SAME, BL_SAME, LT_SAME), + CHAR('5',0), + SGM(GM_CHAR, IN_6, LP_SAME, BL_SAME, LT_SAME), + CHAR('6',0), + SGM(GM_CHAR, IN_7, LP_SAME, BL_SAME, LT_SAME), + CHAR('7',0), + + /* XXX -- more can be included in this pattern */ + + ENDSECT + + /* SECTION 4. Clipping tests. + Suitable for VT11 and VS60. */ + + LSRA(ST_SAME, SI_SAME, LI_INTENSIFY, IT_NORMAL, RF_UNSYNC, MN_SAME), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01000, 01000), + + SGM(GM_LVECT, IN_4, LP_ENA, BL_OFF, LT_SOLID), + LVECT(I_ON, 01100, 0), + LVECT(I_ON, -01100, 01100), + LVECT(I_ON, 0, -01100), + LVECT(I_OFF, 0, 01100), + LVECT(I_ON, -01100, -01100), + LVECT(I_ON, 01100, 0), + LVECT(I_ON, 0, -01100), + LVECT(I_ON, -01100, 01100), + LVECT(I_OFF, 01100, 0), + LVECT(I_OFF, 01100, 0), + LVECT(I_ON, -01100, -01100), + + ENDSECT + + /* END OF TEST SECTIONS. */ + + ENDFILE +}; + +/* FILE LP. Dynamic light pen tracking; works for both VT11 and VS60. */ + +unsigned short LP[] = { + /* SECTION 1. "rubber-band" dot-dash vector to tracking object. */ + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01000, 01000), /* screen center */ + + SGM(GM_LVECT, IN_4, LP_DIS, BL_SAME, LT_DDASH), + /* following coordinates are updated by LP hit intr. handler: */ + LVECT(I_ON, 0, 0), /* tracking object center */ + + SGM(GM_SVECT, IN_7, LP_ENA, BL_SAME, LT_SOLID), + SVECT(I_OFF, 0, 30), + SVECT(I_ON, 0, -60), + SVECT(I_OFF, 30, 30), + SVECT(I_ON, -60, 0), + SVECT(I_ON, 30, 30), + SVECT(I_ON, 30, -30), + SVECT(I_ON, -30, -30), + SVECT(I_ON, -30, 30), + SVECT(I_OFF, 10, 0), + SVECT(I_ON, 20, 20), + SVECT(I_ON, 20, -20), + SVECT(I_ON, -20, -20), + SVECT(I_ON, -20, 20), + SVECT(I_OFF, 10, 0), + SVECT(I_ON, 10, 10), + SVECT(I_ON, 10, -10), + SVECT(I_ON, -10, -10), + SVECT(I_ON, -10, 10), +#if 0 /* not needed for this app */ + SVECT(I_OFF, 0, -10), /* "flyback" vector */ +#endif + + ENDSECT + + /* END OF TEST SECTIONS. */ + + ENDFILE +}; + +/* FILE VS. Static displays that work only for VS60. */ + +unsigned short VS[] = { + /* SECTION 0. Warning that VS60 is required. */ + + LSRA(ST_SAME, SI_SAME, LI_SAME, IT_NORMAL, RF_UNSYNC, MN_SAME), + + SGM(GM_APOINT, IN_7, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0300, 01000), + + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_OFF, LT_SAME), + CHAR('F','o'), CHAR('l','l'), CHAR('o','w'), CHAR('i','n'), CHAR('g',' '), + CHAR('t','e'), CHAR('s','t'), CHAR('s',' '), CHAR('d','o'), + CHAR(' ','n'), CHAR('o','t'), CHAR(' ','w'), CHAR('o','r'), + CHAR('k',' '), CHAR('f','o'), CHAR('r',' '), CHAR('V','T'), + CHAR('1','1'), CHAR(';',0), + + /* italic text */ + LSRA(ST_SAME, SI_SAME, LI_SAME, IT_ITALIC, RF_UNSYNC, MN_SAME), + + SGM(GM_APOINT, IN_7, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0340, 00720), + + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_ON, LT_SAME), + CHAR('S','T'), CHAR('O','P'), CHAR(' ','P'), CHAR('R','O'), CHAR('G','R'), + CHAR('A','M'), CHAR(' ','i'), CHAR('f',' '), CHAR('n','o'), CHAR('t',' '), + CHAR('u','s'), CHAR('i','n'), CHAR('g',' '), CHAR('V','R'), CHAR('4','8'), + CHAR('!',0), + + ENDSECT + + /* SECTION 1. Variety of text characters. */ + + LSRA(ST_SAME, SI_SAME, LI_BRIGHTDOWN, IT_SAME, RF_UNSYNC, MN_MAIN), + + /* horizontal text, 4 sizes */ + SGM(GM_APOINT, IN_4, LP_ENA, BL_SAME, LT_SAME), + APOINT(I_OFF, 0, 01600), + + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LSRC(RO_SAME, CS_CHANGE, 0, VS_SAME, 0), + CHAR(' ',' '), CHAR('S','m'), CHAR('a','l'), CHAR('l',':'), CHAR(' ','1'), + CHAR('/','2'), + LSRC(RO_SAME, CS_CHANGE, 1, VS_SAME, 0), + CHAR(' ',' '), CHAR('N','o'), CHAR('r','m'), CHAR('a','l'), CHAR(':',' '), + CHAR('1',0), + LSRC(RO_SAME, CS_CHANGE, 2, VS_SAME, 0), + CHAR(' ',' '), CHAR('B','i'), CHAR('g',':'), CHAR(' ','1'), CHAR('-','1'), + CHAR('/','2'), + LSRC(RO_SAME, CS_CHANGE, 3, VS_SAME, 0), + CHAR(' ',' '), CHAR('L','a'), CHAR('r','g'), CHAR('e',':'), CHAR(' ','2'), + CHAR('\r','\n'), + CHAR(' ',' '), CHAR('A',SUBSCR), CHAR('B',SUBSCR), CHAR('C',SUBSCR), + CHAR('D',ENDSUB), CHAR(ENDSUB,ENDSUB), CHAR('W',SUPSCR), + CHAR('X',SUPSCR), CHAR('Y',SUPSCR), CHAR('Z',ENDSUP), + CHAR(ENDSUP,ENDSUP), CHAR('!','!'), + + /* vertical text, 4 sizes */ + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0200, 0), + + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + LSRC(RO_VERTICAL, CS_CHANGE, 0, VS_SAME, 0), + CHAR(' ',' '), CHAR('S','m'), CHAR('a','l'), CHAR('l',':'), CHAR(' ','1'), + CHAR('/','2'), + LSRC(RO_SAME, CS_CHANGE, 1, VS_SAME, 0), + CHAR(' ',' '), CHAR('N','o'), CHAR('r','m'), CHAR('a','l'), CHAR(':',' '), + CHAR('1',0), + LSRC(RO_SAME, CS_CHANGE, 2, VS_SAME, 0), + CHAR(' ',' '), CHAR('B','i'), CHAR('g',':'), CHAR(' ','1'), CHAR('-','1'), + CHAR('/','2'), + LSRC(RO_SAME, CS_CHANGE, 3, VS_SAME, 0), + CHAR(' ',' '), CHAR('L','a'), CHAR('r','g'), CHAR('e',':'), CHAR(' ','2'), + CHAR('\r','\n'), + CHAR(' ',' '), CHAR('A',SUBSCR), CHAR('B',SUBSCR), CHAR('C',SUBSCR), + CHAR('D',ENDSUB), CHAR(ENDSUB,ENDSUB), CHAR('W',SUPSCR), + CHAR('X',SUPSCR), CHAR('Y',SUPSCR), CHAR('Z',ENDSUP), + CHAR(ENDSUP,ENDSUP), CHAR('!','!'), + + /* horizontal text, sub/superscript examples from DECgraphic-11 manual */ + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 0400, 01200), + + LSRC(RO_HORIZONTAL, CS_CHANGE, 2, VS_SAME, 0), + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + CHAR('C',SUBSCR), CHAR('2',ENDSUB), CHAR('H',SUBSCR), CHAR('5',ENDSUB), + CHAR('O','H'), CHAR(' ',' '), + CHAR(016,000), CHAR(017,'='), CHAR(016,003), CHAR(017,'('), + CHAR('x',SUBSCR), CHAR('i',ENDSUB), CHAR('-','q'), CHAR(SUBSCR,'i'), + CHAR(ENDSUB,')'), CHAR(SUPSCR,'2'), CHAR(ENDSUP,'e'), CHAR(SUPSCR,'-'), + CHAR('i',SUPSCR), CHAR('2',ENDSUP), CHAR(ENDSUP,0), + + LSRC(RO_SAME, CS_CHANGE, 1, VS_SAME, 0), + LSRA(ST_SAME, SI_SAME, LI_SAME, IT_SAME, RF_SAME, MN_MENU), + SGM(GM_APOINT, IN_7, LP_ENA, BL_SAME, LT_SAME), + APOINT(I_OFF, 0, 1000), + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + CHAR('U','n'), CHAR('s','y'), CHAR('n','c'), + + ENDSECT + + /* SECTION 2. Basic vectors (long and short). */ + + LSRA(ST_SAME, SI_SAME, LI_BRIGHTDOWN, IT_SAME, RF_40, MN_MAIN), + + SGM(GM_APOINT, IN_4, LP_ENA, BL_OFF, LT_SDASH), + APOINT(I_OFF, 01000, 01000), + + SGM(GM_GRAPHX, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + BLVECT(I_OFF, 2, 0600), + BLVECT(I_ON, 0, 0200), + BLVECT(I_ON, 7, 0400), + BLVECT(I_ON, 6, 0400), + BLVECT(I_ON, 5, 0400), + BLVECT(I_ON, 4, 0400), + SGM(GM_GRAPHY, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + BLVECT(I_ON, 3, 0400), + BLVECT(I_ON, 2, 0400), + BLVECT(I_ON, 1, 0400), + BLVECT(I_ON, 0, 0200), + BLVECT(I_OFF, 6, 0600), + + SGM(GM_BSVECT, IN_SAME, LP_SAME, BL_ON, LT_SOLID), + BSVECT(I_OFF, 2, 007, 2, 016), + BSVECT(I_ON, 0, 007, 7, 016), + BSVECT(I_ON, 6, 016, 5, 016), + BSVECT(I_ON, 4, 016, 3, 016), + BSVECT(I_ON, 2, 016, 1, 016), + BSVECT(I_ON, 0, 007, 0, 000), + + LSRA(ST_SAME, SI_SAME, LI_SAME, IT_SAME, RF_SAME, MN_MENU), + SGM(GM_APOINT, IN_7, LP_ENA, BL_OFF, LT_SAME), + APOINT(I_OFF, 0, 1000), + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + CHAR('4','0'), CHAR('H','z'), CHAR(' ','S'), CHAR('y','n'), CHAR('c',0), + + ENDSECT + + /* SECTION 3. 3D data, but depth cueing disabled. */ + + LSRBB(ZD_YES, ED_ENA, DQ_OFF, ES_YES), /* but term char not used */ + LSRA(ST_SAME, SI_SAME, LI_BRIGHTDOWN, IT_SAME, RF_30, MN_MAIN), + + SGM(GM_APOINT, IN_4, LP_ENA, BL_OFF, LT_LDASH), + APOINT3(I_OFF, 0200, 0200, 0400), + + SGM(GM_AVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + AVECT3(I_ON, 01200, 00200, 0400), + AVECT3(I_ON, 01200, 01200, 0400), + AVECT3(I_ON, 00200, 01200, 0400), + AVECT3(I_ON, 00200, 00200, 0400), + AVECT3(I_OFF, 00600, 00600, -0400), + AVECT3(I_ON, 01600, 00600, -0400), + AVECT3(I_ON, 01600, 01600, -0400), + AVECT3(I_ON, 00600, 01600, -0400), + AVECT3(I_ON, 00600, 00600, -0400), + SGM(GM_AVECT, IN_SAME, LP_SAME, BL_SAME, LT_SOLID), + AVECT3(I_ON, 00200, 00200, 0400), + AVECT3(I_OFF, 01200, 00200, 0400), + AVECT3(I_ON, 01600, 00600, -0400), + AVECT3(I_OFF, 01600, 01600, -0400), + AVECT3(I_ON, 01200, 01200, 0400), + AVECT3(I_OFF, 00200, 01200, 0400), + AVECT3(I_ON, 00600, 01600, -0400), + + LSRA(ST_SAME, SI_SAME, LI_SAME, IT_SAME, RF_SAME, MN_MENU), + SGM(GM_APOINT, IN_7, LP_ENA, BL_OFF, LT_SAME), + APOINT3(I_OFF, 0, 1000, 0200), + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + CHAR('3','0'), CHAR('H','z'), CHAR(' ','S'), CHAR('y','n'), CHAR('c',0), + + ENDSECT + + /* SECTION 4. 3D data, with depth cueing enabled. */ + + LSRBB(ZD_YES, ED_ENA, DQ_ON, ES_YES), /* but term char not used */ + LSRA(ST_SAME, SI_SAME, LI_BRIGHTDOWN, IT_SAME, RF_EXT, MN_MAIN), + + SGM(GM_APOINT, IN_4, LP_ENA, BL_OFF, LT_DDASH), + APOINT3(I_OFF, 0200, 0200, 0400), + + SGM(GM_AVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + AVECT3(I_ON, 01200, 00200, 0400), + AVECT3(I_ON, 01200, 01200, 0400), + AVECT3(I_ON, 00200, 01200, 0400), + AVECT3(I_ON, 00200, 00200, 0400), + AVECT3(I_OFF, 00600, 00600, -0400), + AVECT3(I_ON, 01600, 00600, -0400), + AVECT3(I_ON, 01600, 01600, -0400), + AVECT3(I_ON, 00600, 01600, -0400), + AVECT3(I_ON, 00600, 00600, -0400), + SGM(GM_AVECT, IN_SAME, LP_SAME, BL_SAME, LT_SOLID), + AVECT3(I_ON, 00200, 00200, 0400), + AVECT3(I_OFF, 01200, 00200, 0400), + AVECT3(I_ON, 01600, 00600, -0400), + AVECT3(I_OFF, 01600, 01600, -0400), + AVECT3(I_ON, 01200, 01200, 0400), + AVECT3(I_OFF, 00200, 01200, 0400), + AVECT3(I_ON, 00600, 01600, -0400), + + LSRA(ST_SAME, SI_SAME, LI_SAME, IT_SAME, RF_SAME, MN_MENU), + SGM(GM_APOINT, IN_7, LP_ENA, BL_OFF, LT_SAME), + APOINT3(I_OFF, 0, 1000, 0200), + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + CHAR('E','x'), CHAR('t','.'), CHAR(' ','S'), CHAR('y','n'), CHAR('c',0), + + ENDSECT + + /* SECTION 5. Circles and arcs. */ + + SGM(GM_APOINT, IN_4, LP_ENA, BL_ON, LT_SOLID), + APOINT(I_OFF, 0500, 01400), + SGM(GM_ARC, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + ARC(I_ON, -0100, 0, 0, 0), + + SGM(GM_APOINT, IN_5, LP_SAME, BL_OFF, LT_SDASH), + APOINT(I_OFF, 0532, 01532), + SGM(GM_ARC, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + ARC(I_ON, -0132, -0132, 0, -0264), + + SGM(GM_APOINT, IN_6, LP_SAME, BL_SAME, LT_LDASH), + APOINT(I_OFF, 0400, 01700), + SGM(GM_ARC, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + ARC(I_ON, 0, -0300, 0, -0600), + + SGM(GM_APOINT, IN_7, LP_SAME, BL_SAME, LT_DDASH), + APOINT(I_OFF, 0114, 01664), + SGM(GM_ARC, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + ARC(I_ON, 0264, -0264, 0, -0550), + + SGM(GM_APOINT, IN_4, LP_SAME, BL_SAME, LT_SOLID), + APOINT(I_OFF, 01400, 01400), + SGM(GM_ARC, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + ARC(I_ON, 0, 0, 0400, 0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SDASH), + APOINT(I_OFF, 0500, 0400), + SGM(GM_ARC, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + ARC(I_ON, -0100, 0, 0200, 0), + + SGM(GM_APOINT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + APOINT(I_OFF, 01600, 0400), + SGM(GM_ARC, IN_SAME, LP_SAME, BL_SAME, LT_SOLID), + ARC(I_ON, -0200, 0, -0200, 0300), + SGM(GM_ARC, IN_SAME, LP_SAME, BL_SAME, LT_SDASH), + ARC(I_ON, 0, -0300, -0200, -0300), + SGM(GM_ARC, IN_SAME, LP_SAME, BL_SAME, LT_LDASH), + ARC(I_ON, 0200, 0, 0200, -0300), + SGM(GM_ARC, IN_SAME, LP_SAME, BL_SAME, LT_DDASH), + ARC(I_ON, 0, 0300, 0200, 0300), + + ENDSECT + + /* SECTION 6. Display subroutines, with and without parameter restore. */ + + /* XXX need test for subroutines */ + + /* SECTION 7. Offset, vector scale, and clipping. */ + + LSRA(ST_SAME, SI_SAME, LI_BRIGHTDOWN, IT_NORMAL, RF_UNSYNC, MN_MAIN), + LSRC(RO_HORIZONTAL, CS_CHANGE, 1, VS_CHANGE, 4), + + SGM(GM_APOINT, IN_3, LP_ENA, BL_OFF, LT_SOLID), + OFFSET(0, 0), + APOINT(I_ON, 01040, 01040), + APOINT(I_ON, 01040, 0740), + APOINT(I_ON, 0740, 01040), + APOINT(I_ON, 0740, 0740), + + SGM(GM_APOINT, IN_5, LP_SAME, BL_ON, LT_SAME), + OFFSET(06, 010), + APOINT(I_ON, 01040, 01040), + APOINT(I_ON, 01040, 0740), + APOINT(I_ON, 0740, 01040), + APOINT(I_ON, 0740, 0740), + + OFFSET(014, 020), + LSRC(RO_HORIZONTAL, CS_SAME, 0, VS_CHANGE, 8), + SGM(GM_APOINT, IN_7, LP_ENA, BL_SAME, LT_SAME), + APOINT(I_ON, 0420, 0420), + SGM(GM_RPOINT, IN_7, LP_SAME, BL_SAME, LT_SAME), + RPOINT(I_ON, 0, -040), + RPOINT(I_ON, -040, 040), + RPOINT(I_ON, 0, -040), + + /* XXX need test for clipping */ + + ENDSECT + + /* END OF TEST SECTIONS. */ + + ENDFILE +}; + +/* FILE WF. Rotating wire-frame display that works only for VS60. */ + +unsigned short WF[] = { + + /* SECTION 1. 3D data, with depth cueing enabled. */ + + LSRBB(ZD_YES, ED_ENA, DQ_ON, ES_NO), + LSRA(ST_SAME, SI_SAME, LI_BRIGHTDOWN, IT_SAME, RF_40, MN_MAIN), + + SGM(GM_APOINT, IN_4, LP_ENA, BL_OFF, LT_DDASH), + APOINT3(I_OFF, 0, 0, 0), /* cube coords filled in by wf_update() */ + + SGM(GM_AVECT, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + AVECT3(I_ON, 0, 0, 0), + AVECT3(I_ON, 0, 0, 0), + AVECT3(I_ON, 0, 0, 0), + AVECT3(I_ON, 0, 0, 0), + AVECT3(I_OFF, 0, 0, 0), + AVECT3(I_ON, 0, 0, 0), + AVECT3(I_ON, 0, 0, 0), + AVECT3(I_ON, 0, 0, 0), + AVECT3(I_ON, 0, 0, 0), + SGM(GM_AVECT, IN_SAME, LP_SAME, BL_SAME, LT_SOLID), + AVECT3(I_ON, 0, 0, 0), + AVECT3(I_OFF, 0, 0, 0), + AVECT3(I_ON, 0, 0, 0), + AVECT3(I_OFF, 0, 0, 0), + AVECT3(I_ON, 0, 0, 0), + AVECT3(I_OFF, 0, 0, 0), + AVECT3(I_ON, 0, 0, 0), + + LSRA(ST_SAME, SI_SAME, LI_SAME, IT_SAME, RF_SAME, MN_MENU), + SGM(GM_APOINT, IN_7, LP_ENA, BL_OFF, LT_SAME), + APOINT3(I_OFF, 0, 1000, 0200), + SGM(GM_CHAR, IN_SAME, LP_SAME, BL_SAME, LT_SAME), + CHAR('4','0'), CHAR('H','z'), CHAR(' ','S'), CHAR('y','n'), CHAR('c',0), + + ENDSECT + + /* END OF TEST SECTIONS. */ + + ENDFILE +}; + +static unsigned short *df; /* -> start of current display file */ +static uint16 start; /* initial DPC for section of d.file */ +static int more; /* set until end of d.file seen */ + +static void +wf_update(int first_time) { + double c, s; /* cosine, sine of rotation angle */ + int x, y, z; /* rotated coordinates */ + static int xc = 01000, yc = 01000, zc = 0; /* cube center coords */ + static int vp = 010000; /* distance to vanishing point */ + static struct { + int offset; /* WF[] offset (words) of vector data */ + int i; /* I_ON or I_OFF */ + int x, y, z; /* coords of cube corner point */ + } *dp, data[] = { + 3, I_OFF, 00400, 00400, 0400, + 7, I_ON, 01400, 00400, 0400, + 10, I_ON, 01400, 01400, 0400, + 13, I_ON, 00400, 01400, 0400, + 16, I_ON, 00400, 00400, 0400, + 19, I_OFF, 00400, 00400, -0400, + 22, I_ON, 01400, 00400, -0400, + 25, I_ON, 01400, 01400, -0400, + 28, I_ON, 00400, 01400, -0400, + 31, I_ON, 00400, 00400, -0400, + 35, I_ON, 00400, 00400, 0400, + 38, I_OFF, 01400, 00400, 0400, + 41, I_ON, 01400, 00400, -0400, + 44, I_OFF, 01400, 01400, -0400, + 47, I_ON, 01400, 01400, 0400, + 50, I_OFF, 00400, 01400, 0400, + 53, I_ON, 00400, 01400, -0400, + -1 /* end-of-data marker */ + }; + static double rot = 0.0; /* total amount of rotation, degrees */ + if (first_time) { + /* tilt cube toward viewer */ + c = cos(30.0 * 3.14159/180.0); + s = sin(30.0 * 3.14159/180.0); + for (dp = data; dp->offset >= 0; ++dp) { + z = zc + ((dp->z - zc) * c + (dp->y - yc) * s); + y = yc + ((dp->y - yc) * c - (dp->z - zc) * s); + WF[dp->offset ] = dp->i | (SGN_(dp->x) << 13) | MAG_(dp->x); + WF[dp->offset + 1] = (SGN_(y) << 13) | MAG_(y); + WF[dp->offset + 2] = (SGN_(z) << 13) | (MAG_(z) << 2); + /* X coord. unchanged because rotation is parallel to X axis */ + dp->y = y; + dp->z = z; + } + } else + if ((rot += 1.0) >= 360.0) /* rotation increment */ + rot -= 360.0; + c = cos(rot * 3.14159/180.0); + s = sin(rot * 3.14159/180.0); + for (dp = data; dp->offset >= 0; ++dp) { + x = xc + ((dp->x - xc) * c + (dp->z - zc) * s); + z = zc + ((dp->z - zc) * c - (dp->x - xc) * s); + /* apply (approximate) perspective */ + x = x * (1.0 + (double)z / vp ); + y = dp->y * (1.0 + (double)z / vp ); + WF[dp->offset ] = dp->i | (SGN_(x) << 13) | MAG_(x); + WF[dp->offset + 1] = (SGN_(y) << 13) | MAG_(y); + WF[dp->offset + 2] = (SGN_(z) << 13) | (MAG_(z) << 2); + } +} + +int +main(void) { + int c; + + vt11_display = TEST_DIS; + vt11_scale = TEST_RES; + + /* VT11/VS60 tests */ + + puts("initial tests work for both VT11 and VS60"); + for (df = VT, start = 0, more = 1; more; ) { + vt11_reset(); /* reset everything */ + vt11_set_dpc(start); /* start section */ + c = 0; + while (vt11_cycle(USEC, 1)) { + display_sync(); /* XXX push down? */ + if (display_lp_sw) /* tip switch activated */ + c = 1; /* flag: break requested */ + if (c && !display_lp_sw) /* wait for switch release */ + break; + } + /* end of section */ + } + /* end of display file */ + + /* light pen tracking */ + + ws_beep(); + puts("move the light pen through the tracking object"); + fflush(stdout); + for (df = LP, start = 0, more = 1; more; ) { + vt11_reset(); /* reset everything */ + vt11_set_dpc(start); /* start section */ + c = 0; + while (vt11_cycle(USEC, 1)) { + display_sync(); /* XXX push down? */ + if (display_lp_sw) /* tip switch activated */ + c = 1; /* flag: break requested */ + if (c && !display_lp_sw) /* wait for switch release */ + break; + /* [dynamic modifications to the display file can be done here] */ + } + /* end of section */ + } + /* end of display file */ + + /* VS60 tests */ + + ws_beep(); + puts("following tests require VS60"); + for (df = VS, start = 0, more = 1; more; ) { + vt11_reset(); /* reset everything */ + vt11_set_str((uint16)(0200 | '~')); /* set terminating char. */ + vt11_set_anr((uint16)(040000 | (2<<12) | 04000 | 01234)); + /* set associative name 0123x */ + vt11_set_dpc(start); /* start section */ + c = 0; + while (vt11_cycle(USEC, 1)) { + display_sync(); /* XXX push down? */ + if (display_lp_sw) /* tip switch activated */ + c = 1; /* flag: break requested */ + if (c && !display_lp_sw) /* wait for switch release */ + break; + } + /* end of section */ + } + /* end of display file */ + + /* VS60 rotating wire-frame display */ + + puts("press and release tip switch (button 1) for next display"); + fflush(stdout); + wf_update(1); /* do first-time init */ + for (df = WF, start = 0, more = 1; more; ) { + vt11_reset(); /* reset everything */ + vt11_set_dpc(start); /* start section */ + c = 0; + while (vt11_cycle(USEC, 1)) { + display_sync(); /* XXX push down? */ + if (display_lp_sw) /* tip switch activated */ + c = 1; /* flag: break requested */ + if (c && !display_lp_sw) /* wait for switch release */ + break; + } + /* end of section */ + } + /* end of display file */ + + /* XXX would be nice to have an example of animation */ + + return 0; +} + +/* + * callbacks from display.c + */ +unsigned long +cpu_get_switches(void) { + return 0; +} + +void +cpu_set_switches(unsigned long bits) { +} + +/* + * callbacks from vt11.c + */ + +int +vt_fetch(uint32 addr, vt11word *w) { + *w = df[addr/2]; + return 0; +} + +void +vt_stop_intr(void) { + uint16 dpc = vt11_get_dpc(); /* -> just after DSTOP instruction */ + if (df[dpc/2] == 0) { /* ENDSECT */ +#ifdef FRAME1STOP + int c; + puts("end of pass through this test pattern; display frozen"); + puts("enter newline to refresh this section or EOF to quit"); + fflush(stdout); + while ((c = getchar()) != '\n') + if (c == EOF) + exit(0); /* user aborted test */ +#endif + if (df == WF) { + wf_update(0); + vt11_set_dpc(0); /* restart modified display */ + start = dpc + 2; /* save start of next section */ + } else { + df[dpc/2 - 1] = JMPA; + df[dpc/2] = start; + start = dpc + 2; /* save start of next section */ + vt11_set_dpc(dpc - 2); /* reset; then JMPA to old start */ + puts("press and release tip switch (button 1) for next display"); + fflush(stdout); + } + } else /* ENDFILE */ + more = 0; +} + +void +vt_lpen_intr(void) { + if (df == LP) { + int dx = (int)(vt11_get_xpr() & 01777) - 01000; + int dy = (int)(vt11_get_ypr() & 01777) - 01000; + if (dx < 0) + dx = (-dx) | 020000; /* negative */ + if (dy < 0) + dy = (-dy) | 020000; /* negative */ + + df[4] = dx | I_ON; /* visible */ + df[5] = dy; + } else { + printf("VT11 lightpen interrupt (0%o,0%o)\n", + (unsigned)vt11_get_xpr() & 01777, + (unsigned)vt11_get_ypr() & 01777); + fflush(stdout); + } + vt11_set_dpc((uint16)1); /* resume */ +} + +void +vt_char_intr(void) { + puts("VT11 illegal character/timeout interrupt"); + fflush(stdout); + vt11_set_dpc((uint16)1); /* resume */ +} + +void +vt_name_intr(void) { + puts("VS60 name-match interrupt"); + fflush(stdout); + vt11_set_dpc((uint16)1); /* resume */ +} + diff --git a/display/win32.c b/display/win32.c new file mode 100644 index 0000000..145d9da --- /dev/null +++ b/display/win32.c @@ -0,0 +1,422 @@ +/* + * $Id: win32.c,v 1.39 2005/01/14 18:58:03 phil Exp $ + * Win32 support for XY display simulator + * Phil Budne + * September 2003 + * Revised by Douglas A. Gwyn, 05 Feb. 2004 + */ + +/* + * 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 names of the authors 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. + */ + +/* use a thread to handle windows messages; */ +#define THREADS + +/* + * BUGS: + * Does not allow you to close display window; + * would need to tear down both system, and system independent data. + * + * now tries to handle PAINT message, as yet untested!! + */ + +#include +#include +#include +#include "ws.h" +#include "display.h" + +#ifndef PIX_SIZE +#define PIX_SIZE 1 +#endif + +#define APP_CLASS "XYAppClass" +#define APP_MENU "XYAppMenu" /* ?? */ + +/* + * light pen location + * see ws.h for full description + */ +int ws_lp_x = -1; +int ws_lp_y = -1; + +static HWND static_wh; +static HINSTANCE static_inst; +static int xpixels, ypixels; +static const char *window_name; +static HBRUSH white_brush; +static HBRUSH black_brush; +#ifdef SWITCH_CURSORS +static HCURSOR cross, arrow; +#endif + +static __inline int +map_key(int k) +{ + switch (k) { + case 186: return ';'; /* VK_OEM_1? */ + case 222: return '\''; /* VK_OEM_7? */ + } + return k; +} + +static void +keydown(int k) +{ + display_keydown(map_key(k)); +} + +static void +keyup(int k) +{ + display_keyup(map_key(k)); +} + +/* + * here on any button click, or if mouse dragged while a button down + */ +static void +mousepos(DWORD lp) +{ + int x, y; + + x = LOWORD(lp); + y = HIWORD(lp); + + /* convert to display coordinates */ +#if PIX_SIZE > 1 + x /= PIX_SIZE; + y /= PIX_SIZE; +#endif + y = ypixels - 1 - y; + + /* if window has been stretched, can get out of range bits!! */ + if (x >= 0 && x < xpixels && y >= 0 && y < ypixels) { + /* checked by display_add_point() */ + ws_lp_x = x; + ws_lp_y = y; + } +} + +/* thoingggg!! "message for you sir!!!" */ +static LRESULT CALLBACK +patsy(HWND wh, UINT msg, WPARAM wp, LPARAM lp) /* "WndProc" */ +{ + /* printf("msg %d\n", msg); */ + switch (msg) { + case WM_DESTROY: + PostQuitMessage(0); + return 0; + + case WM_MOUSEMOVE: + if (wp & (MK_LBUTTON|MK_MBUTTON|MK_RBUTTON)) { +#ifdef SWITCH_CURSORS + if (ws_lp_x == -1 && !display_tablet) + SetCursor(cross); +#endif + mousepos(lp); + } +#ifdef SWITCH_CURSORS + else if (ws_lp_x != -1 && !display_tablet) + SetCursor(arrow); +#endif + break; /* return?? */ + + case WM_LBUTTONDOWN: + display_lp_sw = 1; + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: +#ifdef SWITCH_CURSORS + if (!display_tablet) + SetCursor(cross); +#endif + mousepos(lp); + break; /* return?? */ + + case WM_LBUTTONUP: + display_lp_sw = 0; + case WM_MBUTTONUP: + case WM_RBUTTONUP: +#ifdef SWITCH_CURSORS + if (!display_tablet) + SetCursor(arrow); +#endif + ws_lp_x = ws_lp_y = -1; + break; /* return?? */ + + case WM_KEYDOWN: + keydown(wp); + break; + + case WM_KEYUP: + keyup(wp); + break; + + case WM_PAINT: + display_repaint(); + break; /* return?? */ + } + return DefWindowProc(wh, msg, wp, lp); +} + +int +ws_poll(int *valp, int maxus) +{ +#ifdef THREADS + /* msgthread handles window events; just delay simulator */ + if (maxus > 0) + Sleep((maxus+999)/1000); +#else + MSG msg; + DWORD start; + int maxms = (maxus + 999) / 1000; + + for (start = GetTickCount(); GetTickCount() - start < maxms; Sleep(1)) { + /* empty message queue without blocking */ + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } +#endif + return 1; +} + +/* called from non-threaded main program */ +int +ws_loop(void (*func)(void *), void *arg) +{ + int val; + while (ws_poll(&val, 0)) + (*func)(arg); + return val; +} + +/* worker for display init */ +static void +ws_init2(void) { + WNDCLASS wc; + int h, w; + +#ifdef SWITCH_CURSORS + if (!display_tablet) { + arrow = LoadCursor(NULL, IDC_ARROW); + cross = LoadCursor(NULL, IDC_CROSS); + } +#endif + + black_brush = GetStockObject(BLACK_BRUSH); + white_brush = GetStockObject(WHITE_BRUSH); + + wc.lpszClassName = APP_CLASS; + wc.lpfnWndProc = patsy; + wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW; + /* also CS_NOCLOSE? CS_SAVEBITS? */ + + wc.hInstance = static_inst = GetModuleHandleA(0); + wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); +#ifdef SWITCH_CURSORS + wc.hCursor = NULL; +#else + wc.hCursor = display_tablet ? NULL : LoadCursor(NULL, IDC_CROSS); +#endif + wc.hbrBackground = black_brush; + wc.lpszMenuName = APP_MENU; + wc.cbClsExtra = 0; + wc.cbWndExtra = 0; + /* WNDCLASSEX/RegisterClassEx include hIconSm (small icon) */ + RegisterClass(&wc); + + /* + * WS_OVERLAPPEDWINDOW=> + * WS_OVERLAPPED, WS_CAPTION, WS_SYSMENU, WS_THICKFRAME, + * WS_MINIMIZEBOX, WS_MAXIMIZEBOX + * + * WS_CHILD (no menu bar), WS_POPUP (mutually exclusive) + */ + + /* empirical crocks to get entire screen; */ + w = (xpixels*PIX_SIZE)+6; + h = (ypixels*PIX_SIZE)+32; + /* XXX -- above values work with XP; Phil had +10,+30 */ + + static_wh = CreateWindow(APP_CLASS, /* registered class name */ + window_name, /* window name */ + WS_OVERLAPPED, /* style */ + CW_USEDEFAULT, CW_USEDEFAULT, /* X,Y */ + w, h, + NULL, /* HWND hWndParent */ + NULL, /* HMENU hMenu */ + static_inst, /* application instance */ + NULL); /* lpParam */ + + ShowWindow(static_wh, SW_SHOW); + UpdateWindow(static_wh); +} + +#ifdef THREADS +static volatile int init_done; +static DWORD msgthread_id; + +static DWORD WINAPI +msgthread(LPVOID arg) +{ + MSG msg; + + ws_init2(); + + /* XXX use a mutex? */ + init_done = 1; + + while (GetMessage(&msg, NULL, 0, 0) > 0) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return msg.wParam; +} + +static void +ws_thread_init(void) +{ + HANDLE th = CreateThread(NULL, /* sec. attr. */ + 0, /* stack size */ + msgthread, + NULL, /* param */ + 0, /* flags */ + &msgthread_id); + CloseHandle(th); + + /* XXX use a mutex; don't wait forever!! */ + while (!init_done) + Sleep(200); +} +#endif /* THREADS */ + +/* called from display layer on first display op */ +int +ws_init(const char *name, int xp, int yp, int colors, void *dptr) +{ + xpixels = xp; + ypixels = yp; + window_name = name; + +#ifdef THREADS + ws_thread_init(); +#else + ws_init2(); +#endif + return 1; /* XXX return errors!! */ +} + +void ws_shutdown (void) +{ +} + +void * +ws_color_rgb(int r, int g, int b) +{ + /* XXX check for failure??? try GetNearestColor??? */ + return CreateSolidBrush(RGB(r/256, g/256, b/256)); +} + +void * +ws_color_black(void) +{ + return black_brush; +} + +void * +ws_color_white(void) +{ + return white_brush; +} + +void +ws_display_point(int x, int y, void *color) +{ + HDC dc; + RECT r; + HBRUSH brush = color; + + if (x > xpixels || y > ypixels) + return; + + y = ypixels - 1 - y; /* invert y, top left origin */ + + /* top left corner */ + r.left = x*PIX_SIZE; + r.top = y*PIX_SIZE; + + /* bottom right corner, non-inclusive */ + r.right = (x+1)*PIX_SIZE; + r.bottom = (y+1)*PIX_SIZE; + + if (brush == NULL) + brush = black_brush; + + dc = GetDC(static_wh); + FillRect(dc, &r, brush); + ReleaseDC(static_wh, dc); +} + +void +ws_sync(void) { + /* noop */ +} + +void +ws_beep(void) { +#if 0 + /* play SystemDefault sound; does not work over terminal service */ + MessageBeep(MB_OK); +#else + /* works over terminal service? Plays default sound/beep on Win9x/ME */ + Beep(440, 500); /* Hz, ms. */ +#endif +} + +unsigned long +os_elapsed(void) +{ + static int new; + unsigned long ret; + static DWORD t[2]; + + /* + * only returns milliseconds, but Sleep() + * only takes milliseconds. + * + * wraps after 49.7 days of uptime. + * DWORD is an unsigned long, so this should be OK + */ + t[new] = GetTickCount(); + if (t[!new] == 0) + ret = ~0L; /* +INF */ + else + ret = (t[new] - t[!new]) * 1000; + new = !new; /* Ecclesiastes III */ + return ret; +} diff --git a/display/ws.h b/display/ws.h new file mode 100644 index 0000000..edf6114 --- /dev/null +++ b/display/ws.h @@ -0,0 +1,65 @@ +/* + * $Id: ws.h,v 1.17 2004/02/03 21:23:51 phil Exp $ + * Interfaces to window-system specific code for XY display simulation + */ + +/* + * 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 names of the authors 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. + */ + +/* unless you're writing a new driver, you shouldn't be looking here! */ + +extern int ws_init(const char *, int, int, int, void *); +void ws_shutdown(void); +extern void *ws_color_rgb(int, int, int); +extern void *ws_color_black(void); +extern void *ws_color_white(void); +extern void ws_display_point(int, int, void *); +extern void ws_sync(void); +extern int ws_poll(int *, int); +extern void ws_beep(void); + +/* entries into display.c from below: */ +extern void display_keyup(int); +extern void display_keydown(int); +extern void display_repaint(void); + +/* + * Globals set by O/S display level to SCALED location in display + * coordinate system in order to save an upcall on every mouse + * movement. + * + * *NOT* for consumption by clients of display.c; although display + * clients can now get the scaling factor, real displays only give you + * a light pen "hit" when the beam passes under the light pen. + */ + +extern int ws_lp_x, ws_lp_y; + +/* + * O/S services in theory independent of window system, + * but in (current) practice not! + */ +extern unsigned long os_elapsed(void); diff --git a/display/x11.c b/display/x11.c new file mode 100644 index 0000000..1a09d55 --- /dev/null +++ b/display/x11.c @@ -0,0 +1,494 @@ +/* + * $Id: x11.c,v 1.32 2005/01/14 18:58:03 phil Exp $ + * X11 support for XY display simulator + * Phil Budne + * September 2003 + * + * Changes from Douglas A. Gwyn, Jan 8, 2004 + * + * started from PDP-8/E simulator (vc8e.c & kc8e.c); + * This PDP8 Emulator was written by Douglas W. Jones at the + * University of Iowa. It is distributed as freeware, of + * uncertain function and uncertain utility. + */ + +/* + * 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 names of the authors 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 +#include +#include +#include "ws.h" +#include "display.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifndef PIX_SIZE +#define PIX_SIZE 1 +#endif + +/* + * light pen location + * see ws.h for full description + */ +int ws_lp_x = -1; +int ws_lp_y = -1; + +static XtAppContext app_context; /* the topmost context for everything */ +static Display* dpy; /* its display */ +static int scr; /* its screen */ +static Colormap cmap; /* its colormap */ +static Widget crtshell; /* the X window shell */ +static Widget crt; /* the X window in which output will plot */ +static int xpixels, ypixels; +#ifdef FULL_SCREEN +/* occupy entire screen for vintage computer fan Sellam Ismail */ +static int xoffset, yoffset; +#endif + +static GC whiteGC; /* gc with white foreground */ +static GC blackGC; /* gc with black foreground */ +static int buttons = 0; /* tracks state of all buttons */ + +static int os_pollfd(int, int); /* forward */ + +/* here on any mouse button down, AND movement when any button down */ +static void +handle_button_press(w, d, e, b) + Widget w; + XtPointer d; + XEvent *e; + Boolean *b; +{ + int x, y; + + x = e->xbutton.x; + y = e->xbutton.y; +#ifdef FULL_SCREEN + /* untested! */ + x -= xoffset; + y -= yoffset; +#endif +#if PIX_SIZE > 1 + x *= PIX_SIZE; + y *= PIX_SIZE; +#endif + + if (!display_tablet) + /* crosshair cursor to indicate tip of active pen */ + XDefineCursor(dpy, XtWindow(crt), + (Cursor) XCreateFontCursor(dpy, XC_crosshair)); + + y = ypixels - y - 1; + /*printf("lightpen at %d,%d\n", x, y); fflush(stdout);*/ + ws_lp_x = x; + ws_lp_y = y; + + if (e->type == ButtonPress) { + buttons |= e->xbutton.button; + + if (e->xbutton.button == 1) { + display_lp_sw = 1; + /*printf("tip switch activated\n"); fflush(stdout);*/ + } + } + + if (b) + *b = TRUE; +} + +static void +handle_button_release(w, d, e, b) + Widget w; + XtPointer d; + XEvent *e; + Boolean *b; +{ + if ((buttons &= ~e->xbutton.button) == 0) { /* all buttons released */ + if (!display_tablet) + /* pencil cursor (close to a pen!) to indicate inactive pen posn */ + XDefineCursor(dpy, XtWindow(crt), + (Cursor) XCreateFontCursor(dpy, XC_pencil)); + + /* XXX change cursor back?? */ + ws_lp_x = ws_lp_y = -1; + } + + if (e->xbutton.button == 1) { + display_lp_sw = 0; + /*printf("tip switch deactivated\n"); fflush(stdout);*/ + } + + if (b) + *b = TRUE; +} + +static void +handle_key_press(w, d, e, b) + Widget w; + XtPointer d; + XEvent *e; + Boolean *b; +{ + int shift = (ShiftMask & e->xkey.state) != 0; + KeySym key = XKeycodeToKeysym( dpy, e->xkey.keycode, shift ); + + /*printf("key %d down\n", key); fflush(stdout);*/ + if ((key & 0xff00) == 0) + display_keydown(key); + + if (b) + *b = TRUE; +} + +static void +handle_key_release(w, d, e, b) + Widget w; + XtPointer d; + XEvent *e; + Boolean *b; +{ + int shift = (ShiftMask & e->xkey.state) != 0; + KeySym key = XKeycodeToKeysym( dpy, e->xkey.keycode, shift ); + + /*printf("key %d up\n", key); fflush(stdout);*/ + if ((key & 0xff00) == 0) + display_keyup(key); + + if (b) + *b = TRUE; +} + +static void +handle_exposure(w, d, e, b) + Widget w; + XtPointer d; + XEvent *e; + Boolean *b; +{ + display_repaint(); + + if (b) + *b = TRUE; +} + +int +ws_init(const char *crtname, /* crt type name */ + int xp, int yp, /* screen size in pixels */ + int colors, /* colors to support (not used) */ + void *dptr) +{ + Arg arg[25]; + XGCValues gcvalues; + unsigned int n; + int argc; + char *argv[1]; + int height, width; + + xpixels = xp; /* save screen size */ + ypixels = yp; + + XtToolkitInitialize(); + app_context = XtCreateApplicationContext(); + argc = 0; + argv[0] = NULL; + dpy = XtOpenDisplay( app_context, NULL, NULL, crtname, NULL, 0, + &argc, argv); + + scr = DefaultScreen(dpy); + + crtshell = XtAppCreateShell( crtname, /* app name */ + crtname, /* app class */ + applicationShellWidgetClass, /* wclass */ + dpy, /* display */ + NULL, /* arglist */ + 0); /* nargs */ + + cmap = DefaultColormap(dpy, scr); + + /* + * Create a drawing area + */ + + n = 0; +#ifdef FULL_SCREEN + /* center raster in full-screen black window */ + width = DisplayWidth(dpy,scr); + height = DisplayHeight(dpy,scr); + + xoffset = (width - xpixels*PIX_SIZE)/2; + yoffset = (height - ypixels*PIX_SIZE)/2; +#else + width = xpixels*PIX_SIZE; + height = ypixels*PIX_SIZE; +#endif + XtSetArg(arg[n], XtNwidth, width); n++; + XtSetArg(arg[n], XtNheight, height); n++; + XtSetArg(arg[n], XtNbackground, BlackPixel( dpy, scr )); n++; + + crt = XtCreateWidget( crtname, widgetClass, crtshell, arg, n); + XtManageChild(crt); + XtPopup(crtshell, XtGrabNonexclusive); + XtSetKeyboardFocus(crtshell, crt); /* experimental? */ + + /* + * Create black and white Graphics Contexts + */ + + gcvalues.foreground = BlackPixel( dpy, scr ); + gcvalues.background = BlackPixel( dpy, scr ); + blackGC = XCreateGC(dpy, XtWindow(crt), + GCForeground | GCBackground, &gcvalues); + + gcvalues.foreground = WhitePixel( dpy, scr ); + whiteGC = XCreateGC(dpy, XtWindow(crt), + GCForeground | GCBackground, &gcvalues); + + if (!display_tablet) { + /* pencil cursor */ + XDefineCursor(dpy, XtWindow(crt), + (Cursor) XCreateFontCursor(dpy, XC_pencil)); + } + + /* + * Setup to handle events + */ + + XtAddEventHandler(crt, ButtonPressMask|ButtonMotionMask, FALSE, + handle_button_press, NULL); + XtAddEventHandler(crt, ButtonReleaseMask, FALSE, + handle_button_release, NULL); + XtAddEventHandler(crt, KeyPressMask, FALSE, + handle_key_press, NULL); + XtAddEventHandler(crt, KeyReleaseMask, FALSE, + handle_key_release, NULL); + XtAddEventHandler(crt, ExposureMask, FALSE, + handle_exposure, NULL); + return 1; +} /* ws_init */ + +void ws_shutdown (void) +{ +} + +void * +ws_color_black(void) +{ + return blackGC; +} + +void * +ws_color_white(void) +{ + return whiteGC; +} + +void * +ws_color_rgb(int r, int g, int b) +{ + XColor color; + + color.red = r; + color.green = g; + color.blue = b; + /* ignores flags */ + + if (XAllocColor(dpy, cmap, &color)) { + XGCValues gcvalues; + memset(&gcvalues, 0, sizeof(gcvalues)); + gcvalues.foreground = gcvalues.background = color.pixel; + return XCreateGC(dpy, XtWindow(crt), + GCForeground | GCBackground, + &gcvalues); + } + /* allocation failed */ + return NULL; +} + +/* put a point on the screen */ +void +ws_display_point(int x, int y, void *color) +{ + GC gc = (GC) color; + + if (x > xpixels || y > ypixels) + return; + + y = ypixels - y - 1; /* X11 coordinate system */ + +#ifdef FULL_SCREEN + x += xoffset; + y += yoffset; +#endif + if (gc == NULL) + gc = blackGC; /* default to off */ +#if PIX_SIZE == 1 + XDrawPoint(dpy, XtWindow(crt), gc, x, y); +#else + XFillRectangle(dpy, XtWindow(crt), gc, + x*PIX_SIZE, y*PIX_SIZE, PIX_SIZE, PIX_SIZE); +#endif +} + +void +ws_sync(void) +{ + XFlush(dpy); +} + +/* + * elapsed wall clock time since last call + * +INF on first call + */ + +struct elapsed_state { + struct timeval tvs[2]; + int new; +}; + +static unsigned long +elapsed(struct elapsed_state *ep) +{ + unsigned long val; + + gettimeofday(&ep->tvs[ep->new], NULL); + if (ep->tvs[!ep->new].tv_sec == 0) + val = ~0L; + else + val = ((ep->tvs[ep->new].tv_sec - ep->tvs[!ep->new].tv_sec) * 1000000 + + (ep->tvs[ep->new].tv_usec - ep->tvs[!ep->new].tv_usec)); + ep->new = !ep->new; + return val; +} + +/* called periodically */ +int +ws_poll(int *valp, int maxusec) +{ + static struct elapsed_state es; /* static to avoid clearing! */ + +#ifdef WS_POLL_DEBUG + printf("ws_poll %d\n", maxusec); + fflush(stdout); +#endif + elapsed(&es); /* start clock */ + do { + unsigned long e; + + /* tried checking return, but lost on TCP connections? */ + os_pollfd(ConnectionNumber(dpy), maxusec); + + while (XtAppPending(app_context)) { + XEvent event; + + /* XXX check for connection loss; set *valp? return 0 */ + XtAppNextEvent(app_context, &event ); + XtDispatchEvent( &event ); + } + e = elapsed(&es); +#ifdef WS_POLL_DEBUG + printf(" maxusec %d e %d\r\n", maxusec, e); + fflush(stdout); +#endif + maxusec -= e; + } while (maxusec > 10000); /* 10ms */ + return 1; +} + +/* utility: can be called from main program + * which is willing to cede control + */ +int +ws_loop(void (*func)(void *), void *arg) +{ + int val; + + /* XXX use XtAppAddWorkProc & XtAppMainLoop? */ + while (ws_poll(&val,0)) + (*func)(arg); + return val; +} + +void +ws_beep(void) +{ + XBell(dpy, 0); /* ring at base volume */ + XFlush(dpy); +} + +/**************** + * could move these to unix.c, if VMS versions needed + * (or just (GASP!) ifdef) + */ + +/* public version, used by delay code */ +unsigned long +os_elapsed(void) +{ + static struct elapsed_state es; + return elapsed(&es); +} + +/* + * select/DisplayNumber works on VMS 7.0+? + * could move to "unix.c" + * (I have some nasty VMS code that's supposed to to the job + * for older systems) + */ + +/* + * sleep for maxus microseconds, returning TRUE sooner if fd is readable + * used by X11 driver + */ +static int +os_pollfd(int fd, int maxus) +{ + + /* use trusty old select (most portable) */ + fd_set rfds; + struct timeval tv; + + if (maxus >= 1000000) { /* not bloody likely, avoid divide */ + tv.tv_sec = maxus / 1000000; + tv.tv_usec = maxus % 1000000; + } + else { + tv.tv_sec = 0; + tv.tv_usec = maxus; + } + FD_ZERO(&rfds); + FD_SET(fd, &rfds); + return select(fd+1, &rfds, NULL, NULL, &tv) > 0; +} diff --git a/display/xy.c b/display/xy.c new file mode 100644 index 0000000..79ccf27 --- /dev/null +++ b/display/xy.c @@ -0,0 +1,1036 @@ +/* + * $Id: xy.c,v 1.59 2005/01/14 18:58:04 phil Exp $ + * Simulator and host O/S independent XY display simulator + * Phil Budne + * September 2003 + * + * with changes by Douglas A. Gwyn, 21 Jan. 2004 + * + * started from PDP-8/E simulator vc8e.c; + * This PDP8 Emulator was written by Douglas W. Jones at the + * University of Iowa. It is distributed as freeware, of + * uncertain function and uncertain utility. + */ + +/* + * 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 names of the authors 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 +#include +#include +#include /* for USHRT_MAX */ +#include "ws.h" +#include "xy.h" + +/* + * The user may select (at compile time) how big a window is used to + * emulate the display. Using smaller windows saves memory and screen space. + * + * Type 30 has 1024x1024 addressing, but only 512x512 visible points. + * VR14 has only 1024x768 visible points; VR17 has 1024x1024 visible points. + * VT11 supports 4096x4096 addressing, clipping to the lowest 1024x1024 region. + * VR48 has 1024x1024 visible points in the main display area and 128x1024 + * visible points in a menu area on the right-hand side (1152x1024 total). + * VT48 supports 8192x8192 (signed) main-area addressing, clipping to a + * 1024x1024 window which can be located anywhere within that region. + * (XXX -- That is what the VT11/VT48 manuals say; however, evidence suggests + * that the VT11 may actually support 8192x8192 (signed) addressing too.) + */ + +/* Define the default display type (if display_init() not called) */ +#ifndef DISPLAY_TYPE +#define DISPLAY_TYPE DIS_TYPE30 +#endif /* DISPLAY_TYPE not defined */ + +/* select a default resolution if display_init() not called */ +/* XXX keep in struct display? */ +#ifndef PIX_SCALE +#define PIX_SCALE RES_HALF +#endif /* PIX_SCALE not defined */ + +/* select a default light-pen hit radius if display_init() not called */ +#ifndef PEN_RADIUS +#define PEN_RADIUS 4 +#endif /* PEN_RADIUS not defined */ + +/* + * note: displays can have up to two different colors (eg VR20) + * each color can be made up of any number of phosphors + * with different colors and decay characteristics (eg Type 30) + */ + +#define ELEMENTS(X) (sizeof(X)/sizeof(X[0])) + +struct phosphor { + float red, green, blue; + float level; /* decay level (0.5 for half life) */ + float t_level; /* seconds to decay to level */ +}; + +struct color { + struct phosphor *phosphors; + int nphosphors; + int half_life; /* for refresh calc */ +}; + +struct display { + enum display_type type; + char *name; + struct color *color0, *color1; + short xpoints, ypoints; +}; + +/* + * P7 phosphor for type 30 (16ADP7(A?) CRT) + * original phosphor constants from Raphael Nabet's XMame 0.72.1 PDP-1 sim. + * fast blue (.05s half life), and slow green (.2s half life) + */ +static struct phosphor p7[] = { + {0.11, 0.11, 1.0, 0.5, 0.05}, /* fast blue */ + {1.0, 1.0, 0.11, 0.5, 0.20} /* slow yellow/green */ +}; +static struct color color_p7 = { p7, ELEMENTS(p7), 125000 }; + +/* green phosphor for VR14, VR17, VR20 */ +static struct phosphor p29[] = {{0.0260, 1.0, 0.00121, 0.5, 0.025}}; +struct color color_p29 = { p29, ELEMENTS(p29), 25000 }; + +static struct phosphor p40[] = { + /* P40 blue-white spot with yellow-green decay (.045s to 10%?) */ + {0.4, 0.2, 0.924, 0.5, 0.0135}, + {0.5, 0.7, 0.076, 0.5, 0.065} +}; +static struct color color_p40 = { p40, ELEMENTS(p40), 20000 }; + +/* "red" -- until real VR20 phosphor type/number/constants known */ +static struct phosphor pred[] = { {1.0, 0.37, 0.37, 0.5, 0.10} }; +static struct color color_red = { pred, ELEMENTS(pred), 100000 }; + +static struct display displays[] = { + /* + * Type 30 + * PDP-1/4/5/8/9/10 "Precision CRT" display system + * + * Raytheon 16ADP7A CRT? + * web searches for 16ADP7 finds useful information!! + * 16" tube, 14 3/8" square raster + * maximum dot size .015" + * 50us point plot time (20,000 points/sec) + * P7 Phosphor? Two phosphor layers: fast blue, slow yellow/green + * 360 lb + * 7A at 115+-10V 60Hz + */ + { DIS_TYPE30, "Type 30", &color_p7, NULL, 1024, 1024 }, + + /* + * VR14 + * used w/ GT40/44, AX08, VC8E + * + * Viewable area 6.75" x 9" + * 12" diagonal + * brightness >= 30 fL + * dot size .02" (20 mils) + * settle time: + * full screen 18us to +/-1 spot diameter + * .1" change 1us to +/-.5 spot diameter + * weight 75lb + */ + { DIS_VR14, "VR14", &color_p29, NULL, 1024, 768 }, + + /* + * VR17 + * used w/ GT40/44, AX08, VC8E + * + * Viewable area 9.25" x 9.25" + * 17" diagonal + * dot size .02" (20 mils) + * brightness >= 25 fL + * phosphor: P39 doped for IR light pen use + * light pen: Type 375 + * weight 85lb + */ + { DIS_VR17, "VR17", &color_p29, NULL, 1024, 1024 }, + + /* + * VR20 + * on VC8E + * Two colors!! + */ + { DIS_VR20, "VR20", &color_p29, &color_red, 1024, 1024 }, + + /* + * VR48 + * (on VT48 in VS60) + * from Douglas A. Gwyn 23 Nov. 2003 + * + * Viewable area 12" x 12", plus 1.5" x 12" menu area on right-hand side + * 21" diagonal + * dot size <= .01" (10 mils) + * brightness >= 31 fL + * phosphor: P40 (blue-white fluorescence with yellow-green phosphorescence) + * light pen: Type 377A (with tip switch) + * driving circuitry separate + * (normally under table on which CRT is mounted) + */ + { DIS_VR48, "VR48", &color_p40, NULL, 1024+VR48_GUTTER+128, 1024 }, + + /* + * Type 340 Display system + * on PDP-4/6/7/9/10 + * + * 1024x1024 + * 9 3/8" raster (.01" dot pitch) + * 0,0 at lower left + * 8 intensity levels + */ + { DIS_TYPE340, "Type 340", &color_p7, NULL, 1024, 1024 } +}; + +/* + * Unit time (in microseconds) used to store display point time to + * live at current aging level. If this is too small, delay values + * cannot fit in an unsigned short. If it is too large all pixels + * will age at once. Perhaps a suitable value should be calculated at + * run time? When display_init() calculates refresh_interval it + * sanity checks for both cases. + */ +#define DELAY_UNIT 250 + +/* levels to display in first half-life; determines refresh rate */ +#ifndef LEVELS_PER_HALFLIFE +#define LEVELS_PER_HALFLIFE 4 +#endif + +/* after 5 half lives (.5**5) remaining intensity is 3% of original */ +#ifndef HALF_LIVES_TO_DISPLAY +#define HALF_LIVES_TO_DISPLAY 5 +#endif + +/* + * refresh_rate is number of times per (simulated) second a pixel is + * aged to next lowest intensity level. + * + * refresh_rate = ((1e6*LEVELS_PER_HALFLIFE)/PHOSPHOR_HALF_LIFE) + * refresh_interval = 1e6/DELAY_UNIT/refresh_rate + * = PHOSPHOR_HALF_LIFE/LEVELS_PER_HALF_LIFE + * intensities = (HALF_LIVES_TO_DISPLAY*PHOSPHOR_HALF_LIFE)/refresh_interval + * = HALF_LIVES_TO_DISPLAY*LEVELS_PER_HALFLIFE + * + * See also comments on display_age() + * + * Try to keep LEVELS_PER_HALFLIFE*HALF_LIVES_TO_DISPLAY*NLEVELS <= 192 + * to run on 8-bit (256 color) displays! + */ + +/* + * number of aging periods to display a point for + */ +#define NTTL (HALF_LIVES_TO_DISPLAY*LEVELS_PER_HALFLIFE) + +/* + * maximum (initial) TTL for a point. + * TTL's are stored 1-based + * (a stored TTL of zero means the point is off) + */ +#define MAXTTL NTTL + +/* + * number of drawing intensity levels + */ +#define NLEVELS (DISPLAY_INT_MAX-DISPLAY_INT_MIN+1) + +#define MAXLEVEL (NLEVELS-1) + +/* + * Display Device Implementation + */ + +/* + * Each point on the display is represented by a "struct point". When + * a point isn't dark (intensity > 0), it is linked into a circular, + * doubly linked delta queue (a priority queue where "delay" + * represents the time difference from the previous entry (if any) in + * the queue. + * + * All points are aged refresh_rate times/second, each time moved to the + * next (logarithmically) lower intensity level. When display_age() is + * called, only the entries which have expired are processed. Calling + * display_age() often allows spreading out the workload. + * + * An alternative would be to have intensity levels represent linear + * decreases in intensity, and have the decay time at each level change. + * Inverting the decay function for a multi-component phosphor may be + * tricky, and the two different colors would need different time tables. + * Furthermore, it would require finding the correct location in the + * queue when adding a point (currently only need to add points at end) + */ + +/* + * 12 bytes/entry on 32-bit system when REFRESH_RATE > 15 + * (requires 3MB for 512x512 display). + */ + +typedef unsigned short delay_t; +#define DELAY_T_MAX USHRT_MAX + +struct point { + struct point *next; /* next entry in queue */ + struct point *prev; /* prev entry in queue */ + delay_t delay; /* delta T in DELAY_UNITs */ + unsigned char ttl; /* zero means off, not linked in */ + unsigned char level : 7; /* intensity level */ + unsigned char color : 1; /* for VR20 (two colors) */ +}; + +static struct point *points; /* allocated array of points */ +static struct point _head; +#define head (&_head) + +/* + * time span of all entries in queue + * should never exceed refresh_interval + * (should be possible to make this a delay_t) + */ +static long queue_interval; + +/* convert X,Y to a "struct point *" */ +#define P(X,Y) (points + (X) + ((Y)*(size_t)xpixels)) + +/* convert "struct point *" to X and Y */ +#define X(P) (((P) - points) % xpixels) +#define Y(P) (((P) - points) / xpixels) + +static int initialized = 0; + +/* + * global set by O/S display level to indicate "light pen tip switch activated" + * (This is used only by the VS60 emulation, also by vttest to change patterns) + */ +unsigned char display_lp_sw = 0; + +/* + * global set by DR11-C simulation when DR device enabled; deactivates + * light pen and instead reports mouse coordinates as Talos digitizer + * data via DR11-C + */ +unsigned char display_tablet = 0; + +/* + * can be changed with display_lp_radius() + */ +static long scaled_pen_radius_squared; + +/* run-time -- set by display_init() */ +static int xpoints, ypoints; +static int xpixels, ypixels; +static int refresh_rate; +static int refresh_interval; +static int ncolors; +static enum display_type display_type; +static int scale; + +/* + * relative brightness for each display level + * (all but last must be less than 1.0) + */ +static float level_scale[NLEVELS]; + +/* + * table of pointer to window system "colors" + * for painting each age level, intensity level and beam color + */ +void *colors[2][NLEVELS][NTTL]; + +void +display_lp_radius(int r) +{ + r /= scale; + scaled_pen_radius_squared = r * r; +} + +/* + * from display_age and display_point + * since all points age at the same rate, + * only adds points at end of list. + */ +static void +queue_point(struct point *p) +{ + int d; + + d = refresh_interval - queue_interval; + queue_interval += d; + /* queue_interval should now be == refresh_interval */ + +#ifdef PARANOIA + if (p->ttl == 0 || p->ttl > MAXTTL) + printf("queuing %d,%d level %d!\n", X(p), Y(p), p->level); + if (d > DELAY_T_MAX) + printf("queuing %d,%d delay %d!\n", X(p), Y(p), d); + if (queue_interval > DELAY_T_MAX) + printf("queue_interval (%d) > DELAY_T_MAX (%d)\n", + (int)queue_interval, DELAY_T_MAX); +#endif /* PARANOIA defined */ + + p->next = head; + p->prev = head->prev; + + head->prev->next = p; + head->prev = p; + + p->delay = d; +} + +/* + * here to to dynamically adjust interval for examination + * of elapsed vs. simulated time, and fritter away + * any extra wall-clock time without eating CPU + */ + +/* + * more parameters! + */ + +/* + * upper bound for elapsed time between elapsed time checks. + * if more than MAXELAPSED microseconds elapsed while simulating + * delay_check simulated microseconds, decrease delay_check. + */ +#define MAXELAPSED 100000 /* 10Hz */ + +/* + * lower bound for elapsed time between elapsed time checks. + * if fewer than MINELAPSED microseconds elapsed while simulating + * delay_check simulated microseconds, increase delay_check. + */ +#define MINELAPSED 50000 /* 20Hz */ + +/* + * upper bound for delay (sleep/poll). + * If difference between elapsed time and simulated time is + * larger than MAXDELAY microseconds, decrease delay_check. + * + * since delay is elapsed time - simulated time, MAXDELAY + * should be <= MAXELAPSED + */ +#ifndef MAXDELAY +#define MAXDELAY 100000 /* 100ms */ +#endif /* MAXDELAY not defined */ + +/* + * lower bound for delay (sleep/poll). + * If difference between elapsed time and simulated time is + * smaller than MINDELAY microseconds, increase delay_check. + * + * since delay is elapsed time - simulated time, MINDELAY + * should be <= MINELAPSED + */ +#ifndef MINDELAY +#define MINDELAY 50000 /* 50ms */ +#endif /* MINDELAY not defined */ + +/* + * Initial amount of simulated time to elapse before polling. + * Value is very low to ensure polling occurs on slow systems. + * Fast systems should ramp up quickly. + */ +#ifndef INITIAL_DELAY_CHECK +#define INITIAL_DELAY_CHECK 1000 /* 1ms */ +#endif /* INITIAL_DELAY_CHECK */ + +/* + * gain factor (2**-GAINSHIFT) for adjustment of adjustment + * of delay_check + */ +#ifndef GAINSHIFT +#define GAINSHIFT 3 /* gain=0.125 (12.5%) */ +#endif /* GAINSHIFT not defined */ + +static void +display_delay(int t, int slowdown) +{ + /* how often (in simulated us) to poll/check for delay */ + static unsigned long delay_check = INITIAL_DELAY_CHECK; + + /* accumulated simulated time */ + static unsigned long sim_time = 0; + unsigned long elapsed; + long delay; + + sim_time += t; + if (sim_time < delay_check) + return; + + elapsed = os_elapsed(); /* read and reset elapsed timer */ + if (elapsed == ~0L) { /* first time thru? */ + slowdown = 0; /* no adjustments */ + elapsed = sim_time; + } + + /* + * get delta between elapsed (real) time, and simulated time. + * if simulated time running faster, we need to slow things down (delay) + */ + if (slowdown) + delay = sim_time - elapsed; + else + delay = 0; /* just poll */ + +#ifdef DEBUG_DELAY2 + printf("sim %d elapsed %d delay %d\r\n", sim_time, elapsed, delay); +#endif + + /* + * Try to keep the elapsed (real world) time between checks for + * delay (and poll for window system events) bounded between + * MAXELAPSED and MINELAPSED. Also tries to keep + * delay/poll time bounded between MAXDELAY and MINDELAY -- large + * delays make the simulation spastic, while very small ones are + * inefficient (too many system calls) and tend to be inaccurate + * (operating systems have a certain granularity for time + * measurement, and when you try to sleep/poll for very short + * amounts of time, the noise will dominate). + * + * delay_check period may be adjusted often, and oscillate. There + * is no single "right value", the important things are to keep + * the delay time and max poll intervals bounded, and responsive + * to system load. + */ + if (elapsed > MAXELAPSED || delay > MAXDELAY) { + /* too much elapsed time passed, or delay too large; shrink interval */ + if (delay_check > 1) { + delay_check -= delay_check>>GAINSHIFT; +#ifdef DEBUG_DELAY + printf("reduced period to %d\r\n", delay_check); +#endif /* DEBUG_DELAY defined */ + } + } + else if (elapsed < MINELAPSED || slowdown && delay < MINDELAY) { + /* too little elapsed time passed, or delta very small */ + int gain = delay_check>>GAINSHIFT; + if (gain == 0) + gain = 1; /* make sure some change made! */ + delay_check += gain; +#ifdef DEBUG_DELAY + printf("increased period to %d\r\n", delay_check); +#endif /* DEBUG_DELAY defined */ + } + if (delay < 0) + delay = 0; + /* else if delay < MINDELAY, clamp at MINDELAY??? */ + + /* poll for window system events and/or delay */ + ws_poll(NULL, delay); + + sim_time = 0; /* reset simulated time clock */ + + /* + * delay (poll/sleep) time included in next "elapsed" period + * (clock not reset after a delay) + */ +} /* display_delay */ + +/* + * here periodically from simulator to age pixels. + * + * calling often with small values will age a few pixels at a time, + * and assist with graceful aging of display, and pixel aging. + * + * values should be smaller than refresh_interval! + * + * returns true if anything on screen changed. + */ + +int +display_age(int t, /* simulated us since last call */ + int slowdown) /* slowdown to simulated speed */ +{ + struct point *p; + static int elapsed = 0; + int changed; + + if (!initialized && !display_init(DISPLAY_TYPE, PIX_SCALE)) + return 0; + + display_delay(t, slowdown); + + changed = 0; + + elapsed += t; + if (elapsed < DELAY_UNIT) + return 0; + + t = elapsed / DELAY_UNIT; + elapsed %= DELAY_UNIT; + + while ((p = head->next) != head) { + int x, y; + + /* look at oldest entry */ + if (p->delay > t) { /* further than our reach? */ + p->delay -= t; /* update head */ + queue_interval -= t; /* update span */ + break; /* quit */ + } + + x = X(p); + y = Y(p); +#ifdef PARANOIA + if (p->ttl == 0) + printf("BUG: age %d,%d ttl zero\n", x, y); +#endif /* PARANOIA defined */ + + /* dequeue point */ + p->prev->next = p->next; + p->next->prev = p->prev; + + t -= p->delay; /* lessen our reach */ + queue_interval -= p->delay; /* update queue span */ + + ws_display_point(x, y, colors[p->color][p->level][--p->ttl]); + changed = 1; + + /* queue it back up, unless we just turned it off! */ + if (p->ttl > 0) + queue_point(p); + } + return changed; +} /* display_age */ + +/* here from window system */ +void +display_repaint(void) { + struct point *p; + int x, y; + /* + * bottom to top, left to right. + */ + for (p = points, y = 0; y < ypixels; y++) + for (x = 0; x < xpixels; p++, x++) + if (p->ttl) + ws_display_point(x, y, colors[p->color][p->level][p->ttl-1]); + ws_sync(); +} + +/* (0,0) is lower left */ +static int +intensify(int x, /* 0..xpixels */ + int y, /* 0..ypixels */ + int level, /* 0..MAXLEVEL */ + int color) /* for VR20! 0 or 1 */ +{ + struct point *p; + int bleed; + + if (x < 0 || x >= xpixels || y < 0 || y >= ypixels) + return 0; /* limit to display */ + + p = P(x,y); + if (p->ttl) { /* currently lit? */ +#ifdef LOUD + printf("%d,%d old level %d ttl %d new %d\r\n", + x, y, p->level, p->ttl, level); +#endif /* LOUD defined */ + + /* unlink from delta queue */ + p->prev->next = p->next; + + if (p->next == head) + queue_interval -= p->delay; + else + p->next->delay += p->delay; + p->next->prev = p->prev; + } + + bleed = 0; /* no bleeding for now */ + + /* EXP: doesn't work... yet */ + /* if "recently" drawn, same or brighter, same color, make even brighter */ + if (p->ttl >= MAXTTL*2/3 && level >= p->level && p->color == color && + level < MAXLEVEL) + level++; + + /* + * this allows a dim beam to suck light out of + * a recently drawn bright spot!! + */ + if (p->ttl != MAXTTL || p->level != level || p->color != color) { + p->ttl = MAXTTL; + p->level = level; + p->color = color; /* save color even if monochrome */ + ws_display_point(x, y, colors[p->color][p->level][p->ttl-1]); + } + + queue_point(p); /* put at end of list */ + return bleed; +} + +int +display_point(int x, /* 0..xpixels (unscaled) */ + int y, /* 0..ypixels (unscaled) */ + int level, /* DISPLAY_INT_xxx */ + int color) /* for VR20! 0 or 1 */ +{ + long lx, ly; + + if (!initialized && !display_init(DISPLAY_TYPE, PIX_SCALE)) + return 0; + + /* scale x and y to the displayed number of pixels */ + /* handle common cases quickly */ + if (scale > 1) { + if (scale == 2) { + x >>= 1; + y >>= 1; + } + else { + x /= scale; + y /= scale; + } + } + +#if DISPLAY_INT_MIN > 0 + level -= DISPLAY_INT_MIN; /* make zero based */ +#endif + intensify(x, y, level, color); + /* no bleeding for now (used to recurse for neighbor points) */ + + if (ws_lp_x == -1 || ws_lp_y == -1) + return 0; + + lx = x - ws_lp_x; + ly = y - ws_lp_y; + return lx*lx + ly*ly <= scaled_pen_radius_squared; +} /* display_point */ + +/* + * calculate decay color table for a phosphor mixture + * must be called AFTER refresh_rate initialized! + */ +static void +phosphor_init(struct phosphor *phosphors, int nphosphors, int color) +{ + int ttl; + + /* for each display ttl level; newest to oldest */ + for (ttl = NTTL-1; ttl > 0; ttl--) { + struct phosphor *pp; + double rr, rg, rb; /* real values */ + + /* fractional seconds */ + double t = ((double)(NTTL-1-ttl))/refresh_rate; + + int ilevel; /* intensity levels */ + int p; + + /* sum over all phosphors in mixture */ + rr = rg = rb = 0.0; + for (pp = phosphors, p = 0; p < nphosphors; pp++, p++) { + double decay = pow(pp->level, t/pp->t_level); + rr += decay * pp->red; + rg += decay * pp->green; + rb += decay * pp->blue; + } + + /* scale for brightness for each intensity level */ + for (ilevel = MAXLEVEL; ilevel >= 0; ilevel--) { + int r, g, b; + void *cp; + + /* + * convert to 16-bit integer; clamp at 16 bits. + * this allows the sum of brightness factors across phosphors + * for each of R G and B to be greater than 1.0 + */ + + r = rr * level_scale[ilevel] * 0xffff; + if (r > 0xffff) r = 0xffff; + + g = rg * level_scale[ilevel] * 0xffff; + if (g > 0xffff) g = 0xffff; + + b = rb * level_scale[ilevel] * 0xffff; + if (b > 0xffff) b = 0xffff; + + cp = ws_color_rgb(r, g, b); + if (!cp) { /* allocation failed? */ + if (ttl == MAXTTL-1) { /* brand new */ + if (ilevel == MAXLEVEL) /* highest intensity? */ + cp = ws_color_white(); /* use white */ + else + cp = colors[color][ilevel+1][ttl]; /* use next lvl */ + } /* brand new */ + else if (r + g + b >= 0xffff*3/3) /* light-ish? */ + cp = colors[color][ilevel][ttl+1]; /* use previous TTL */ + else + cp = ws_color_black(); + } + colors[color][ilevel][ttl] = cp; + } /* for each intensity level */ + } /* for each TTL */ +} /* phosphor_init */ + +static struct display * +find_type(enum display_type type) +{ + int i; + struct display *dp; + for (i = 0, dp = displays; i < ELEMENTS(displays); i++, dp++) + if (dp->type == type) + return dp; + return NULL; +} + +int +display_init(enum display_type type, int sf) +{ + static int init_failed = 0; + struct display *dp; + int half_life; + int i; + + if (initialized) { + /* cannot change type once started */ + /* XXX say something???? */ + return type == display_type; + } + + if (init_failed) + return 0; /* avoid thrashing */ + + init_failed = 1; /* assume the worst */ + dp = find_type(type); + if (!dp) { + fprintf(stderr, "Unknown display type %d\r\n", (int)type); + goto failed; + } + + /* Initialize display list */ + head->next = head->prev = head; + + display_type = type; + scale = sf; + + xpoints = dp->xpoints; + ypoints = dp->ypoints; + + /* increase scale factor if won't fit on desktop? */ + xpixels = xpoints / scale; + ypixels = ypoints / scale; + + /* set default pen radius now that scale is set */ + display_lp_radius(PEN_RADIUS); + + ncolors = 1; + /* + * use function to calculate from looking at avg (max?) + * of phosphor half lives??? + */ +#define COLOR_HALF_LIFE(C) ((C)->half_life) + + half_life = COLOR_HALF_LIFE(dp->color0); + if (dp->color1) { + if (dp->color1->half_life > half_life) + half_life = COLOR_HALF_LIFE(dp->color1); + ncolors++; + } + + /* before phosphor_init; */ + refresh_rate = (1000000*LEVELS_PER_HALFLIFE)/half_life; + refresh_interval = 1000000/DELAY_UNIT/refresh_rate; + + /* + * sanity check refresh_interval + * calculating/selecting DELAY_UNIT at runtime might avoid this! + */ + + /* must be non-zero; interval of 1 means all pixels will age at once! */ + if (refresh_interval < 1) { + /* decrease DELAY_UNIT? */ + fprintf(stderr, "NOTE! refresh_interval too small: %d\r\n", + refresh_interval); + + /* dunno if this is a good idea, but might be better than dying */ + refresh_interval = 1; + } + + /* point lifetime in DELAY_UNITs will not fit in p->delay field! */ + if (refresh_interval > DELAY_T_MAX) { + /* increase DELAY_UNIT? */ + fprintf(stderr, "bad refresh_interval %d > DELAY_T_MAX %d\r\n", + refresh_interval, DELAY_T_MAX); + goto failed; + } + + /* + * before phosphor_init; + * set up relative brightness of display intensity levels + * (could differ for different hardware) + * + * linear for now. boost factor insures low intensities are visible + */ +#define BOOST 5 + for (i = 0; i < NLEVELS; i++) + level_scale[i] = ((float)i+1+BOOST)/(NLEVELS+BOOST); + + points = (struct point *)calloc((size_t)xpixels, + ypixels * sizeof(struct point)); + if (!points) + goto failed; + + if (!ws_init(dp->name, xpixels, ypixels, ncolors)) + goto failed; + + phosphor_init(dp->color0->phosphors, dp->color0->nphosphors, 0); + + if (dp->color1) + phosphor_init(dp->color1->phosphors, dp->color1->nphosphors, 1); + + initialized = 1; + init_failed = 0; /* hey, we made it! */ + return 1; + + failed: + fprintf(stderr, "Display initialization failed\r\n"); + return 0; +} + +void +display_reset(void) +{ + /* XXX tear down window? just clear it? */ +} + +void +display_sync(void) +{ + ws_sync(); +} + +void +display_beep(void) +{ + ws_beep(); +} + +int +display_xpoints(void) +{ + return xpoints; +} + +int +display_ypoints(void) +{ + return ypoints; +} + +int +display_scale(void) +{ + return scale; +} + +/* + * handle keyboard events + * + * data switches; 18 -- enough for PDP-1/4/7/9/15 (for munching squares!) + * 123 456 789 qwe rty uio + * bit toggled on key up + * all cleared on space + * + * spacewar switches; bit high as long as key down + * asdf kl;' + * just where PDP-1 spacewar expects them! + * key mappings same as MIT Media Lab Java PDP-1 simulator + * + */ +unsigned long spacewar_switches = 0; + +/* here from window system */ +void +display_keydown(int k) +{ + switch (k) { + case 'f': case 'F': spacewar_switches |= 01; break; /* torpedos */ + case 'd': case 'D': spacewar_switches |= 02; break; /* engines */ + case 'a': case 'A': spacewar_switches |= 04; break; /* rotate R */ + case 's': case 'S': spacewar_switches |= 010; break; /* rotate L */ + case '\'': case '"': spacewar_switches |= 040000; break; /* torpedos */ + case ';': case ':': spacewar_switches |= 0100000; break; /* engines */ + case 'k': case 'K': spacewar_switches |= 0200000; break; /* rotate R */ + case 'l': case 'L': spacewar_switches |= 0400000; break; /* rotate L */ + default: return; + } +} + +/* here from window system */ +void +display_keyup(int k) +{ + unsigned long test_switches = cpu_get_switches(); + + /* fetch console switches from simulator? */ + switch (k) { + case 'f': case 'F': spacewar_switches &= ~01; return; + case 'd': case 'D': spacewar_switches &= ~02; return; + case 'a': case 'A': spacewar_switches &= ~04; return; + case 's': case 'S': spacewar_switches &= ~010; return; + + case '\'': case '"': spacewar_switches &= ~040000; return; + case ';': case ':': spacewar_switches &= ~0100000; return; + case 'k': case 'K': spacewar_switches &= ~0200000; return; + case 'l': case 'L': spacewar_switches &= ~0400000; return; + + case '1': test_switches ^= 1<<17; break; + case '2': test_switches ^= 1<<16; break; + case '3': test_switches ^= 1<<15; break; + + case '4': test_switches ^= 1<<14; break; + case '5': test_switches ^= 1<<13; break; + case '6': test_switches ^= 1<<12; break; + + case '7': test_switches ^= 1<<11; break; + case '8': test_switches ^= 1<<10; break; + case '9': test_switches ^= 1<<9; break; + + case 'q': case 'Q': test_switches ^= 1<<8; break; + case 'w': case 'W': test_switches ^= 1<<7; break; + case 'e': case 'E': test_switches ^= 1<<6; break; + + case 'r': case 'R': test_switches ^= 1<<5; break; + case 't': case 'T': test_switches ^= 1<<4; break; + case 'y': case 'Y': test_switches ^= 1<<3; break; + + case 'u': case 'U': test_switches ^= 1<<2; break; + case 'i': case 'I': test_switches ^= 1<<1; break; + case 'o': case 'O': test_switches ^= 1; break; + + case ' ': test_switches = 0; break; + default: return; + } + cpu_set_switches(test_switches); +} diff --git a/scp.c b/scp.c index 6a6dbc2..ae40882 100644 --- a/scp.c +++ b/scp.c @@ -541,7 +541,7 @@ UNIT *sim_dfunit = NULL; DEVICE **sim_internal_devices = NULL; uint32 sim_internal_device_count = 0; int32 sim_opt_out = 0; -int32 sim_is_running = 0; +volatile t_bool sim_is_running = FALSE; t_bool sim_processing_event = FALSE; uint32 sim_brk_summ = 0; uint32 sim_brk_types = 0; @@ -564,7 +564,8 @@ size_t *sim_sub_instr_off = NULL; static double sim_time; static uint32 sim_rtime; static int32 noqueue_time; -volatile int32 stop_cpu = 0; +volatile t_bool stop_cpu = FALSE; +static unsigned int sim_stop_sleep_ms = 250; static char **sim_argv; t_value *sim_eval = NULL; static t_value sim_last_val; @@ -585,7 +586,8 @@ static int32 sim_do_depth = 0; static t_bool sim_cmd_echoed = FALSE; /* Command was emitted already prior to message output */ static int32 sim_on_check[MAX_DO_NEST_LVL+1]; -static char *sim_on_actions[MAX_DO_NEST_LVL+1][SCPE_MAX_ERR+1]; +static char *sim_on_actions[MAX_DO_NEST_LVL+1][SCPE_MAX_ERR+2]; +#define ON_SIGINT_ACTION (SCPE_MAX_ERR+1) static char sim_do_filename[MAX_DO_NEST_LVL+1][CBUFSIZE]; static const char *sim_do_ocptr[MAX_DO_NEST_LVL+1]; static const char *sim_do_label[MAX_DO_NEST_LVL+1]; @@ -1082,66 +1084,66 @@ static const char simh_help[] = /***************** 80 character line width template *************************/ #define HLP_SET_CONSOLE "*Commands SET CONSOLE" "3Console\n" - "+set console arg{,arg...} set console options\n" - "+set console WRU=value specify console drop to simh character\n" - "+set console BRK=value specify console Break character\n" - "+set console DEL=value specify console delete character\n" - "+set console PCHAR=bitmask bit mask of printable characters in\n" + "+SET CONSOLE arg{,arg...} set console options\n" + "+SET CONSOLE WRU=value specify console drop to simh character\n" + "+SET CONSOLE BRK=value specify console Break character\n" + "+SET CONSOLE DEL=value specify console delete character\n" + "+SET CONSOLE PCHAR=bitmask bit mask of printable characters in\n" "++++++++ range [31,0]\n" - "+set console SPEED=speed{*factor}\n" + "+SET CONSOLE SPEED=speed{*factor}\n" "++++++++ specify console input data rate\n" - "+set console TELNET=port specify console telnet port\n" - "+set console TELNET=LOG=log_file\n" + "+SET CONSOLE TELNET=port specify console telnet port\n" + "+SET CONSOLE TELNET=LOG=log_file\n" "++++++++ specify console telnet logging to the\n" "++++++++ specified destination {LOG,STDOUT,STDERR,\n" "++++++++ DEBUG or filename)\n" - "+set console TELNET=NOLOG disables console telnet logging\n" - "+set console TELNET=BUFFERED[=bufsize]\n" + "+SET CONSOLE TELNET=NOLOG disables console telnet logging\n" + "+SET CONSOLE TELNET=BUFFERED[=bufsize]\n" "++++++++ specify console telnet buffering\n" - "+set console TELNET=NOBUFFERED\n" + "+SET CONSOLE TELNET=NOBUFFERED\n" "++++++++ disables console telnet buffering\n" - "+set console TELNET=UNBUFFERED\n" + "+SET CONSOLE TELNET=UNBUFFERED\n" "++++++++ disables console telnet buffering\n" - "+set console NOTELNET disable console telnet\n" - "+set console SERIAL=serialport[;config]\n" + "+SET CONSOLE NOTELNET disable console telnet\n" + "+SET CONSOLE SERIAL=serialport[;config]\n" "++++++++ specify console serial port and optionally\n" "++++++++ the port config (i.e. ;9600-8n1)\n" - "+set console NOSERIAL disable console serial session\n" - "+set console SPEED=nn{*fac} specifies the maximum console port input rate\n" + "+SET CONSOLE NOSERIAL disable console serial session\n" + "+SET CONSOLE SPEED=nn{*fac} specifies the maximum console port input rate\n" /***************** 80 character line width template *************************/ #define HLP_SET_REMOTE "*Commands SET REMOTE" "3Remote\n" - "+set remote TELNET=port specify remote console telnet port\n" - "+set remote NOTELNET disables remote console\n" - "+set remote BUFFERSIZE=bufsize\n" + "+SET REMOTE TELNET=port specify remote console telnet port\n" + "+SET REMOTE NOTELNET disables remote console\n" + "+SET REMOTE BUFFERSIZE=bufsize\n" "++++++++ specify remote console command output buffer\n" "++++++++ size\n" - "+set remote CONNECTIONS=n specify number of concurrent remote\n" + "+SET REMOTE CONNECTIONS=n specify number of concurrent remote\n" "++++++++ console sessions\n" - "+set remote TIMEOUT=n specify number of seconds without input\n" + "+SET REMOTE TIMEOUT=n specify number of seconds without input\n" "++++++++ before automatic continue\n" - "+set remote MASTER enable master mode remote console\n" - "+set remote NOMASTER disable remote master mode console\n" + "+SET REMOTE MASTER enable master mode remote console\n" + "+SET REMOTE NOMASTER disable remote master mode console\n" #define HLP_SET_DEFAULT "*Commands SET Working_Directory" "3Working Directory\n" - "+set default set the current directory\n" - "+cd set the current directory\n" + "+SET DEFAULT set the current directory\n" + "+CD set the current directory\n" #define HLP_SET_LOG "*Commands SET Log" "3Log\n" " Interactions with the simulator session (at the \"sim>\" prompt\n" " can be recorded to a log file\n\n" - "+set log log_file specify the log destination\n" + "+SET LOG log_file specify the log destination\n" "++++++++ (STDOUT,DEBUG or filename)\n" - "+set nolog disables any currently active logging\n" + "+SET NOLOG disables any currently active logging\n" "4Switches\n" " By default, log output is written at the end of the specified log file.\n" " A new log file can created if the -N switch is used on the command line.\n" #define HLP_SET_DEBUG "*Commands SET Debug" /***************** 80 character line width template *************************/ "3Debug\n" - "+set debug debug_file specify the debug destination\n" + "+SET DEBUG debug_file specify the debug destination\n" "++++++++ (STDOUT,STDERR,LOG or filename)\n" - "+set nodebug disables any currently active debug output\n" + "+SET NODEBUG disables any currently active debug output\n" "4Switches\n" " Debug message output contains a timestamp which indicates the number of\n" " simulated instructions which have been executed prior to the debug event.\n\n" @@ -1171,90 +1173,111 @@ static const char simh_help[] = " EBCDIC characters.\n" #define HLP_SET_BREAK "*Commands SET Breakpoints" "3Breakpoints\n" - "+set break set breakpoints\n" - "+set nobreak clear breakpoints\n" + "+SET BREAK set breakpoints\n" + "+SET NOBREAK clear breakpoints\n" /***************** 80 character line width template *************************/ #define HLP_SET_THROTTLE "*Commands SET Throttle" "3Throttle\n" - "+set throttle {x{M|K|%%}}|{x/t}\n" - "++++++++ set simulation rate\n" - "+set nothrottle set simulation rate to maximum\n" + " Simulator instruction execution rate can be controlled by specifying\n" + " one of the following throttle commands:\n\n" + "+SET THROTTLE xM execute x million instructions per second\n" + "+SET THROTTLE xK execute x thousand instructions per second\n" + "+SET THROTTLE x%% occupy x percent of the host capacity\n" + "++++++++executing instructions\n" + "+SET THROTTLE x/t sleep for t milliseconds after executing x\n" + "++++++++instructions\n\n" + "+SET NOTHROTTLE set simulation rate to maximum\n\n" + " Throttling is only available on host systems that implement a precision\n" + " real-time delay function.\n\n" + " xM, xK and x%% modes require the simulator to execute sufficient\n" + " instructions to actually calibrate the desired execution rate relative\n" + " to wall clock time. Very short running programs may complete before\n" + " calibration completes and therefore before the simulated execution rate\n" + " can match the desired rate.\n\n" + " The SET NOTHROTTLE command turns off throttling. The SHOW THROTTLE\n" + " command shows the current settings for throttling and the calibration\n" + " results\n\n" + " Some simulators implement a different form of host CPU resource management\n" + " called idling. Idling suspends simulated execution whenever the program\n" + " running in the simulator is doing nothing, and runs the simulator at full\n" + " speed when there is work to do. Throttling and idling are mutually\n" + " exclusive.\n" #define HLP_SET_CLOCKS "*Commands SET Clocks" "3Clock\n" #if defined (SIM_ASYNCH_CLOCKS) - "+set clock asynch enable asynchronous clocks\n" - "+set clock noasynch disable asynchronous clocks\n" + "+SET CLOCK asynch enable asynchronous clocks\n" + "+SET CLOCK noasynch disable asynchronous clocks\n" #endif - "+set clock nocatchup disable catchup clock ticks\n" - "+set clock catchup enable catchup clock ticks\n" - "+set clock calib=n%% specify idle calibration skip %%\n" - "+set clock stop=n stop execution after n instructions\n\n" - " The set clock stop command allows execution to have a bound when\n" + "+SET CLOCK nocatchup disable catchup clock ticks\n" + "+SET CLOCK catchup enable catchup clock ticks\n" + "+SET CLOCK calib=n%% specify idle calibration skip %%\n" + "+SET CLOCK stop=n stop execution after n instructions\n\n" + " The SET CLOCK STOP command allows execution to have a bound when\n" " execution starts with a BOOT, NEXT or CONTINUE command.\n" #define HLP_SET_ASYNCH "*Commands SET Asynch" "3Asynch\n" - "+set asynch enable asynchronous I/O\n" - "+set noasynch disable asynchronous I/O\n" + "+SET ASYNCH enable asynchronous I/O\n" + "+SET NOASYNCH disable asynchronous I/O\n" #define HLP_SET_ENVIRON "*Commands SET Environment" "3Environment\n" "4Explicitily Changing A Variable\n" - "+set environment name=val set environment variable\n" - "+set environment name clear environment variable\n" + "+SET ENVIRONMENT name=val set environment variable\n" + "+SET ENVIRONMENT name clear environment variable\n" "4Gathering Input From A User\n" " Input from a user can be obtained by:\n\n" - "+set environment -p \"Prompt String\" name=default\n\n" - " The -p switch indicates that the user should be prompted\n" + "+set environment -P \"Prompt String\" name=default\n\n" + " The -P switch indicates that the user should be prompted\n" " with the indicated prompt string and the input provided\n" " will be saved in the environment variable 'name'. If no\n" " input is provided, the value specified as 'default' will be\n" " used.\n" #define HLP_SET_ON "*Commands SET Command_Status_Trap_Dispatching" "3Command Status Trap Dispatching\n" - "+set on enables error checking after command\n" + "+SET ON enables error checking after command\n" "++++++++ execution\n" - "+set noon disables error checking after command\n" + "+SET NOON disables error checking after command\n" "++++++++ execution\n" - "+set on inherit enables inheritance of ON state and\n" + "+SET ON INHERIT enables inheritance of ON state and\n" "++++++++ actions into do command files\n" - "+set on noinherit disables inheritance of ON state and\n" + "+SET ON NOINHERIT disables inheritance of ON state and\n" "++++++++ actions into do command files\n" #define HLP_SET_VERIFY "*Commands SET Command_Execution_Display" #define HLP_SET_VERIFY "*Commands SET Command_Execution_Display" "3Command Execution Display\n" - "+set verify re-enables display of command file\n" + "+SET VERIFY re-enables display of command file\n" "++++++++ processed commands\n" - "+set verbose re-enables display of command file\n" + "+SET VERBOSE re-enables display of command file\n" "++++++++ processed commands\n" - "+set noverify disables display of command file processed\n" + "+SET NOVERIFY disables display of command file processed\n" "++++++++ commands\n" - "+set noverbose disables display of command file processed\n" + "+SET NOVERBOSE disables display of command file processed\n" "++++++++ commands\n" #define HLP_SET_MESSAGE "*Commands SET Command_Error_Status_Display" "3Command Error Status Display\n" - "+set message re-enables display of command file error\n" + "+SET MESSAGE re-enables display of command file error\n" "++++++++ messages\n" - "+set nomessage disables display of command file error\n" + "+SET NOMESSAGE disables display of command file error\n" "++++++++ messages\n" #define HLP_SET_QUIET "*Commands SET Command_Output_Display" "3Command Output Display\n" - "+set quiet disables suppression of some output and\n" + "+SET QUIET disables suppression of some output and\n" "++++++++ messages\n" - "+set noquiet re-enables suppression of some output and\n" + "+SET NOQUIET re-enables suppression of some output and\n" "++++++++ messages\n" #define HLP_SET_PROMPT "*Commands SET Command_Prompt" "3Command Prompt\n" - "+set prompt \"string\" sets an alternate simulator prompt string\n" + "+SET PROMPT \"string\" sets an alternate simulator prompt string\n" "3Device and Unit\n" - "+set OCT|DEC|HEX|BIN set device display radix\n" - "+set ENABLED enable device\n" - "+set DISABLED disable device\n" - "+set DEBUG{=arg} set device debug flags\n" - "+set NODEBUG={arg} clear device debug flags\n" - "+set arg{,arg...} set device parameters (see show modifiers)\n" - "+set ENABLED enable unit\n" - "+set DISABLED disable unit\n" - "+set arg{,arg...} set unit parameters (see show modifiers)\n" - "+help set displays the device specific set commands\n" + "+SET OCT|DEC|HEX|BIN set device display radix\n" + "+SET ENABLED enable device\n" + "+SET DISABLED disable device\n" + "+SET DEBUG{=arg} set device debug flags\n" + "+SET NODEBUG={arg} clear device debug flags\n" + "+SET arg{,arg...} set device parameters (see show modifiers)\n" + "+SET ENABLED enable unit\n" + "+SET DISABLED disable unit\n" + "+SET arg{,arg...} set unit parameters (see show modifiers)\n" + "+HELP SET displays the device specific set commands\n" "++++++++ available\n" /***************** 80 character line width template *************************/ #define HLP_SHOW "*Commands SHOW" @@ -1557,6 +1580,10 @@ static const char simh_help[] = " Assertion failed\n" "5 INVREM\n" " Invalid remote console command\n" + "5 NOTATT\n" + " Not attached \n" + "5 AMBREG\n" + " Ambiguous register\n" #define HLP_SHIFT "*Commands Executing_Command_Files SHIFT" "3SHIFT\n" "++shift shift the command file's positional parameters\n" @@ -1564,10 +1591,59 @@ static const char simh_help[] = "3CALL\n" "++call transfer control to a labeled subroutine\n" " a command file.\n" -#define HLP_ON "*Commands Executing_Command_Files ON" - "3ON\n" - "++on perform action(s) after condition\n" - "++on clear action for specific condition\n" + /***************** 80 character line width template *************************/ +#define HLP_ON "*Commands Executing_Command_Files Error_Trapping" + "3Error Trapping\n" + " Error traps can be taken when any command returns a non success status.\n" + " Actions to be performed for particular status returns are specified with\n" + " the ON command.\n" + "4Enabling Error Traps\n" + " Error trapping is enabled with:\n\n" + "++set on enable error traps\n" + "4Disabling Error Traps\n" + " Error trapping is disabled with:\n\n" + "++set noon disable error traps\n" + "4ON\n" + " To set the action(s) to take when a specific error status is returned by\n" + " a command in the currently running do command file:\n\n" + "++on commandtoprocess{; additionalcommandtoprocess}\n\n" + " To clear the action(s) taken take when a specific error status is returned:\n\n" + "++on \n\n" + " To set the default action(s) to take when any otherwise unspecified error\n" + " status is returned by a command in the currently running do command file:\n\n" + "++on error commandtoprocess{; additionalcommandtoprocess}\n\n" + " To clear the default action(s) taken when any otherwise unspecified error\n" + " status is returned:\n\n" + "++on error\n" + "5Parameters\n" + " Error traps can be taken for any command which returns a status other\n" + " than SCPE_STEP, SCPE_OK, and SCPE_EXIT.\n\n" + " ON Traps can specify any of these status values:\n\n" + "++NXM, UNATT, IOERR, CSUM, FMT, NOATT, OPENERR, MEM, ARG,\n" + "++STEP, UNK, RO, INCOMP, STOP, TTIERR, TTOERR, EOF, REL,\n" + "++NOPARAM, ALATT, TIMER, SIGERR, TTYERR, SUB, NOFNC, UDIS,\n" + "++NORO, INVSW, MISVAL, 2FARG, 2MARG, NXDEV, NXUN, NXREG,\n" + "++NXPAR, NEST, IERR, MTRLNT, LOST, TTMO, STALL, AFAIL,\n" + "++NOTATT, AMBREG\n\n" + " These values can be indicated by name or by their internal\n" + " numeric value (not recommended).\n" + /***************** 80 character line width template *************************/ + "3CONTROL-C Trapping\n" + " A special ON trap is available to describe action(s) to be taken\n" + " when CONTROL_C (aka SIGINT) occurs during the execution of\n" + " simh commands and/or command procedures.\n\n" + "++on CONTROL_C perform action(s) after CTRL+C\n" + "++on CONTROL_C restore default CTRL+C action\n\n" + " The default ON CONTROL_C handler will exit nested DO command\n" + " procedures and return to the sim> prompt.\n\n" + " Note 1: When a simulator is executing instructions entering CTRL+C\n" + "+will cause the CNTL+C character to be delivered to the simulator as\n" + "+input. The simulator instruction execution can be stopped by entering\n" + "+the WRU character (usually CTRL+E). Once instruction execution has\n" + "+stopped, CTRL+C can be entered and potentially acted on by the\n" + "+ON CONTROL_C trap handler.\n" + " Note 2: The ON CONTROL_C trapping is not affected by the SET ON and\n" + "+SET NOON commands.\n" #define HLP_PROCEED "*Commands Executing_Command_Files PROCEED" #define HLP_IGNORE "*Commands Executing_Command_Files PROCEED" /***************** 80 character line width template *************************/ @@ -1576,49 +1652,21 @@ static const char simh_help[] = " placeholders for an ON action condition which should be explicitly ignored\n" "++proceed continue command file execution without doing anything\n" "++ignore continue command file execution without doing anything\n" - -#if 0 - - SET ON Enables error trapping for currently defined - traps (by ON commands) - SET NOON Disables error trapping for currently - defined traps (by ON commands) - ON commandtoprocess{; additionalcommandtoprocess} - Sets the action(s) to take when the specific - error status is returned by a command in the - currently running do command file. Multiple - actions can be specified with each delimited - by a semicolon character (just like - breakpoint action commands). - ON ERROR commandtoprocess{; additionalcommandtoprocess} - Sets the default action(s) to take when any - otherwise unspecified error status is returned - by a command in the currently running do - command file. Multiple actions can be - specified with each delimited by a semicolon - character (just like breakpoint action - commands). - ON - ON ERROR Clears the default actions to take when any - otherwise unspecified error status is - returned by a command in the currently - running do command file. - - -Error traps can be taken for any command which returns a status other than SCPE_STEP, SCPE_OK, and SCPE_EXIT. - -ON Traps can specify any status value from the following list: NXM, UNATT, IOERR, CSUM, FMT, NOATT, OPENERR, MEM, ARG, STEP, UNK, RO, INCOMP, STOP, TTIERR, TTOERR, EOF, REL, NOPARAM, ALATT, TIMER, SIGERR, TTYERR, SUB, NOFNC, UDIS, NORO, INVSW, MISVAL, 2FARG, 2MARG, NXDEV, NXUN, NXREG, NXPAR, NEST, IERR, MTRLNT, LOST, TTMO, STALL, AFAIL. These values can be indicated by name or by their internal numeric value (not recommended). - -Interactions with ASSERT command and "DO -e": -DO -e is equivalent to SET ON, which by itself it equivalent to "SET ON; ON ERROR RETURN". -ASSERT failure have several different actions: - If error trapping is not enabled then AFAIL causes exit from the current do command file. - If error trapping is enabled and an explicit "ON AFAIL" action is defined, then the specified action is performed. - If error trapping is enabled and no "ON AFAIL" action is defined, then an AFAIL causes exit from the current do command file. - -#endif - - + "3DO Command Processing Interactions With ASSERT\n" + " The command:\n\n" + "++DO -e commandfile\n\n" + " is equivalent to starting the invoked command file with:\n\n" + "++SET ON\n\n" + " which by itself it equivalent to:\n\n" + "++SET ON\n" + "++ON ERROR RETURN\n\n" + " ASSERT failures have several different actions:\n\n" + "+* If error trapping is not enabled then AFAIL causes exit from the\n" + "++current do command file.\n" + "+* If error trapping is enabled and an explicit \"ON AFAIL\" action\n" + "++is defined, then the specified action is performed.\n" + "+* If error trapping is enabled and no \"ON AFAIL\" action is defined,\n" + "++then an AFAIL causes exit from the current do command file.\n" #define HLP_ECHO "*Commands Executing_Command_Files Displaying_Arbitrary_Text ECHO_Command" /***************** 80 character line width template *************************/ "3Displaying Arbitrary Text\n" @@ -1851,7 +1899,7 @@ ASSERT failure have several different actions: " 'm' for minutes, 'h' for hours or 'd' for days. NUMBER may be an\n" " arbitrary floating point number. Given two or more arguments, pause\n" " for the amount of time specified by the sum of their values.\n" - " NOTE: A SLEEP command is interruptable with SIGINT (^C).\n\n" + " NOTE: A SLEEP command is interruptable with SIGINT (CTRL+C).\n\n" /***************** 80 character line width template *************************/ #define HLP_ASSERT "*Commands Executing_Command_Files Testing_Simulator_State" #define HLP_IF "*Commands Executing_Command_Files Testing_Simulator_State" @@ -1940,6 +1988,7 @@ ASSERT failure have several different actions: " The -i switch, if present, causes comparisons to be case insensitive.\n" " and are quoted string values which may have\n" " environment variables substituted as desired.\n" + " Either quoted string may alternatively be an environment variable name.\n" " may be one of:\n\n" "++== - equal\n" "++EQU - equal\n" @@ -2237,12 +2286,12 @@ if (sim_vm_init != NULL) /* call once only */ (*sim_vm_init)(); sim_finit (); /* init fio package */ setenv ("SIM_NAME", sim_name, 1); /* Publish simulator name */ -stop_cpu = 0; +stop_cpu = FALSE; sim_interval = 0; sim_time = sim_rtime = 0; noqueue_time = 0; sim_clock_queue = QUEUE_LIST_END; -sim_is_running = 0; +sim_is_running = FALSE; sim_log = NULL; if (sim_emax <= 0) sim_emax = 1; @@ -2267,6 +2316,7 @@ if ((stat = sim_brk_init ()) != SCPE_OK) { sim_error_text (stat)); return 0; } +signal (SIGINT, int_handler); if (!sim_quiet) { printf ("\n"); show_version (stdout, NULL, NULL, 0, NULL); @@ -2355,16 +2405,32 @@ CTAB *cmdp; stat = SCPE_BARE_STATUS(stat); /* remove possible flag */ while (stat != SCPE_EXIT) { /* in case exit */ + if (stop_cpu) { /* SIGINT happened? */ + stop_cpu = FALSE; + if (!sim_ttisatty()) { + stat = SCPE_EXIT; + break; + } + if (sim_on_actions[sim_do_depth][ON_SIGINT_ACTION]) + sim_brk_setact (sim_on_actions[sim_do_depth][ON_SIGINT_ACTION]); + } if ((cptr = sim_brk_getact (cbuf, sizeof(cbuf)))) /* pending action? */ printf ("%s%s\n", sim_prompt, cptr); /* echo */ - else if (sim_vm_read != NULL) { /* sim routine? */ - printf ("%s", sim_prompt); /* prompt */ - cptr = (*sim_vm_read) (cbuf, sizeof(cbuf), stdin); + else { + if (sim_vm_read != NULL) { /* sim routine? */ + printf ("%s", sim_prompt); /* prompt */ + cptr = (*sim_vm_read) (cbuf, sizeof(cbuf), stdin); + } + else + cptr = read_line_p (sim_prompt, cbuf, sizeof(cbuf), stdin);/* read with prmopt*/ } - else cptr = read_line_p (sim_prompt, cbuf, sizeof(cbuf), stdin);/* read with prmopt*/ - if (cptr == NULL) { /* EOF? */ - if (sim_ttisatty()) continue; /* ignore tty EOF */ - else break; /* otherwise exit */ + if (cptr == NULL) { /* EOF? or SIGINT? */ + if (sim_ttisatty()) { + printf ("\n"); + continue; /* ignore tty EOF */ + } + else + break; /* otherwise exit */ } if (*cptr == 0) /* ignore blank */ continue; @@ -2680,7 +2746,9 @@ if ((dptr->modifiers) && (dptr->units) && (dptr->numunits != 1)) { if (mptr->mstring) { fprint_header (st, &found, header); sprintf (buf, "set %s%s %s%s", sim_dname (dptr), (dptr->numunits > 1) ? "n" : "0", mptr->mstring, (strchr(mptr->mstring, '=')) ? "" : (MODMASK(mptr,MTAB_VALR) ? "=val" : (MODMASK(mptr,MTAB_VALO) ? "{=val}": ""))); - fprintf (st, "%-30s\t%s\n", buf, (strchr(mptr->mstring, '=')) ? "" : (mptr->help ? mptr->help : "")); + fprintf (st, "%-30s\t%s\n", buf, (strchr (mptr->mstring, '=')) ? ((strlen (buf) > 30) ? "" : mptr->help) : (mptr->help ? mptr->help : "")); + if ((strchr (mptr->mstring, '=')) && (strlen (buf) > 30)) + fprintf (st, "%-30s\t%s\n", "", mptr->help); } } } @@ -3146,7 +3214,7 @@ if (flag >= 0) { /* Only bump nesting fro ++sim_do_depth; if (sim_on_inherit) { /* inherit ON condition actions? */ sim_on_check[sim_do_depth] = sim_on_check[sim_do_depth-1]; /* inherit On mode */ - for (i=0; iaction (cmdp->arg, cptr); /* exec other cmd */ } - else stat = SCPE_UNK; /* bad cmd given */ + else + stat = SCPE_UNK; /* bad cmd given */ echo = sim_do_echo; /* Allow for SET VERIFY */ stat_nomessage = stat & SCPE_NOMESSAGE; /* extract possible message supression flag */ stat_nomessage = stat_nomessage || (!sim_show_message);/* Apply global suppression */ @@ -3231,7 +3308,8 @@ do { (cmdp->action != &goto_cmd) && (cmdp->action != &on_cmd) && (cmdp->action != &echo_cmd) && - (cmdp->action != &echof_cmd))) + (cmdp->action != &echof_cmd) && + (cmdp->action != &sleep_cmd))) sim_last_cmd_stat = stat; /* save command error status */ switch (stat) { case SCPE_AFAIL: @@ -3286,7 +3364,7 @@ if (flag >= 0) { sim_quiet = saved_sim_quiet; /* restore quiet mode we entered with */ } if ((flag >= 0) || (!sim_on_inherit)) { - for (i=0; i and are the same as that allowed for examine and deposit search specifications. - Syntax: ASSERT {-i} {NOT} "" "" - Syntax: IF {-i} {NOT} "" "" commandtoprocess{; additionalcommandtoprocess}... + Syntax: ASSERT {-i} {NOT} ""|EnvVarName1 ""|EnvVarName2 + Syntax: IF {-i} {NOT} ""|EnvVarName1 ""|EnvVarName2 commandtoprocess{; additionalcommandtoprocess}... If -i is specified, the comparisons are done in a case insensitive manner. If NOT is specified, the resulting expression value is inverted. @@ -3697,6 +3775,28 @@ return 1; >= - greater than or equal GEQ - greater than or equal */ +static CONST char *_get_string (CONST char *iptr, char *optr, char mchar) +{ +const char *ap; +CONST char *tptr, *gptr; +REG *rptr; + +tptr = (CONST char *)get_glyph_gen (iptr, optr, mchar, (sim_switches & SWMASK ('I')), TRUE, '\\'); +if (*optr != '"') { + ap = getenv (optr); + if (!ap) + return tptr; + /* for legacy ASSERT/IF behavior give precidence to REGister names over Environment Variables */ + get_glyph (optr, optr, 0); + rptr = find_reg (optr, &gptr, sim_dfdev); + if (rptr) + return tptr; + snprintf (optr, CBUFSIZE - 1, "\"%s\"", ap); + get_glyph_gen (optr, optr, 0, (sim_switches & SWMASK ('I')), TRUE, '\\'); + } +return tptr; +} + t_stat assert_cmd (int32 flag, CONST char *cptr) { char gbuf[CBUFSIZE], gbuf2[CBUFSIZE]; @@ -3725,7 +3825,8 @@ if (!strcmp (gbuf, "EXIST")) { /* File Exist Test? */ Exist = TRUE; /* remember that, and */ cptr = (CONST char *)tptr; } -if (Exist || (*cptr == '"')) { /* quoted string comparison? */ +tptr = _get_string (cptr, gbuf, '='); /* get first string */ +if (Exist || (*gbuf == '"')) { /* quoted string comparison? */ char op[CBUFSIZE]; static struct { const char *op; @@ -3743,16 +3844,14 @@ if (Exist || (*cptr == '"')) { /* quoted string compari {"<=", 0, -1, FALSE}, {"LEQ", 0, -1, FALSE}, {">", 1, 1, FALSE}, - {"GTR", 1, 1, FALSE}, + {"GTR", 1, 1, FALSE}, {">=", 0, 1, FALSE}, {"GEQ", 0, 1, FALSE}, {NULL}}; - tptr = (CONST char *)get_glyph_gen (cptr, gbuf, '=', (sim_switches & SWMASK ('I')), TRUE, '\\'); - /* get first string */ if (!*tptr) return SCPE_2FARG; - cptr += strlen (gbuf); + cptr = tptr; while (sim_isspace (*cptr)) /* skip spaces */ ++cptr; if (!Exist) { @@ -3763,17 +3862,16 @@ if (Exist || (*cptr == '"')) { /* quoted string compari if (!optr->op) return sim_messagef (SCPE_ARG, "Invalid operator: %s\n", op); cptr += strlen (op); - while (sim_isspace (*cptr)) /* skip spaces */ + while (sim_isspace (*cptr)) /* skip spaces */ ++cptr; - cptr = (CONST char *)get_glyph_gen (cptr, gbuf2, 0, (sim_switches & SWMASK ('I')), TRUE, '\\'); - /* get second string */ - if (*cptr) { /* more? */ - if (flag) /* ASSERT has no more args */ + cptr = _get_string (cptr, gbuf2, 0); /* get second string */ + if (*cptr) { /* more? */ + if (flag) /* ASSERT has no more args */ return SCPE_2MARG; } else { - if (!flag) - return SCPE_2FARG; /* IF needs actions! */ + if (!flag) + return SCPE_2FARG; /* IF needs actions! */ } result = sim_cmp_string (gbuf, gbuf2); result = ((result == optr->aval) || (result == optr->bval)); @@ -3808,7 +3906,7 @@ else { addr = sim_vm_parse_addr (sim_dfdev, gbuf, &gptr); else addr = (t_addr) strtotv (gbuf, &gptr, sim_dfdev ? sim_dfdev->dradix : sim_dflt_dev->dradix); - if (gbuf == gptr) /* error? */ + if (gbuf == gptr) /* not register? */ return SCPE_NXREG; } if (*gptr != 0) /* more? must be search */ @@ -4079,8 +4177,6 @@ t_stat sleep_cmd (int32 flag, CONST char *cptr) char *tptr; double wait; -stop_cpu = 0; -signal (SIGINT, int_handler); while (*cptr) { wait = strtod (cptr, &tptr); switch (*tptr) { @@ -4108,7 +4204,6 @@ while (*cptr) { wait *= (24.0*60.0*60.0); break; default: - signal (SIGINT, SIG_DFL); /* cancel WRU */ return sim_messagef (SCPE_ARG, "Invalid Sleep unit '%c'\n", *cptr); } wait *= 1000.0; /* Convert to Milliseconds */ @@ -4118,8 +4213,7 @@ while (*cptr) { if ((wait > 0.0) && (!stop_cpu)) sim_os_ms_sleep ((unsigned)wait); } -signal (SIGINT, SIG_DFL); /* cancel WRU */ -stop_cpu = 0; +stop_cpu = FALSE; /* Clear in case sleep was interrupted */ return SCPE_OK; } @@ -4220,9 +4314,17 @@ cptr = get_glyph (cptr, gbuf, 0); if ('\0' == gbuf[0]) return SCPE_ARG; /* unspecified condition */ if (0 == strcmp("ERROR", gbuf)) cond = 0; -else - if (SCPE_OK != sim_string_to_stat (gbuf, &cond)) - return SCPE_ARG; +else { + if (SCPE_OK != sim_string_to_stat (gbuf, &cond)) { + if ((MATCH_CMD (gbuf, "CONTROL_C") == 0) || + (MATCH_CMD (gbuf, "SIGINT") == 0)) + cond = ON_SIGINT_ACTION; /* Special case */ + else + return sim_messagef (SCPE_ARG, "Invalid argument: %s\n", gbuf); + } + } +if (cond == SCPE_OK) + return sim_messagef (SCPE_ARG, "Invalid argument: %s\n", gbuf); if ((NULL == cptr) || ('\0' == *cptr)) { /* Empty Action */ free(sim_on_actions[sim_do_depth][cond]); /* Clear existing condition */ sim_on_actions[sim_do_depth][cond] = NULL; } @@ -4909,17 +5011,15 @@ const char *scale, *width; if (sim_switches & SWMASK ('B')) kval = 1024; mval = kval * kval; -if (dptr->flags & DEV_SECTORS) { - kval = kval / 512; - mval = mval / 512; - } +if (dptr->flags & DEV_SECTORS) + psize = psize * 512; if ((dptr->dwidth / dptr->aincr) > 8) width = "W"; else width = "B"; -if (uptr->capac < (kval * 10)) +if (psize < (kval * 10)) scale = ""; -else if (uptr->capac < (mval * 10)) { +else if (psize < (mval * 10)) { scale = "K"; psize = psize / kval; } @@ -5313,12 +5413,16 @@ for (lvl=sim_do_depth; lvl >= 0; --lvl) { fprintf(st, " is %s\n", (sim_on_check[lvl]) ? "enabled" : "disabled"); for (i=1; ipos, uptr->filename, sim_uname (uptr)); } } -stop_cpu = 0; -sim_is_running = 1; /* flag running */ -if ((r = sim_ttrun ()) != SCPE_OK) { /* set console mode */ - sim_is_running = 0; /* flag idle */ +if ((r = sim_ttrun ()) != SCPE_OK) { /* set console mode */ sim_ttcmd (); return sim_messagef (SCPE_TTYERR, "sim_ttrun() returned: %s\n", sim_error_text (r)); } if ((r = sim_check_console (30)) != SCPE_OK) { /* check console, error? */ - sim_is_running = 0; /* flag idle */ sim_ttcmd (); sim_messagef (r, "sim_check_console () returned: %s\n", sim_error_text (r)); } -if (signal (SIGINT, int_handler) == SIG_ERR) { /* set WRU */ - sim_is_running = 0; /* flag idle */ - sim_ttcmd (); - return sim_messagef (SCPE_SIGERR, "Can't establish SIGINT"); - } #ifdef SIGHUP if (signal (SIGHUP, int_handler) == SIG_ERR) { /* set WRU */ - sim_is_running = 0; /* flag idle */ sim_ttcmd (); return sim_messagef (SCPE_SIGERR, "Can't establish SIGHUP"); } #endif if (signal (SIGTERM, int_handler) == SIG_ERR) { /* set WRU */ - sim_is_running = 0; /* flag idle */ sim_ttcmd (); return sim_messagef (SCPE_SIGERR, "Can't establish SIGTERM"); } +stop_cpu = FALSE; +sim_is_running = TRUE; /* flag running */ if (sim_step) /* set step timer */ sim_activate (&sim_step_unit, sim_step); fflush(stdout); /* flush stdout */ @@ -7207,15 +7302,18 @@ do { } else sim_step = 1; - if (sim_step) /* set step timer */ + if (sim_step) /* set step timer */ sim_activate (&sim_step_unit, sim_step); } while (1); -sim_is_running = 0; /* flag idle */ +if ((SCPE_BARE_STATUS(r) == SCPE_STOP) && /* WRU exit from sim_instr() */ + (sim_on_actions[sim_do_depth][SCPE_STOP] == NULL) &&/* without a handler for a STOP condition */ + (sim_on_actions[sim_do_depth][0] == NULL)) + sim_os_ms_sleep (sim_stop_sleep_ms); /* wait a bit for SIGINT */ +sim_is_running = FALSE; /* flag idle */ sim_stop_timer_services (); /* disable wall clock timing */ sim_ttcmd (); /* restore console */ sim_brk_clrall (BRK_TYP_DYN_STEPOVER); /* cancel any step/over subroutine breakpoints */ -signal (SIGINT, SIG_DFL); /* cancel WRU */ #ifdef SIGHUP signal (SIGHUP, SIG_DFL); /* cancel WRU */ #endif @@ -7377,7 +7475,7 @@ return sim_cancel (&sim_step_unit); void int_handler (int sig) { -stop_cpu = 1; +stop_cpu = TRUE; return; } @@ -9775,8 +9873,10 @@ t_stat sim_process_event (void) UNIT *uptr; t_stat reason; -if (stop_cpu) /* stop CPU? */ +if (stop_cpu) { /* stop CPU? */ + stop_cpu = 0; return SCPE_STOP; + } AIO_UPDATE_QUEUE; UPDATE_SIM_TIME; /* update sim time */ @@ -9818,8 +9918,10 @@ if (sim_clock_queue == QUEUE_LIST_END) { /* queue empty? */ else sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Processing Queue Complete New Interval = %d(%s)\n", sim_interval, sim_uname(sim_clock_queue)); -if ((reason == SCPE_OK) && stop_cpu) +if ((reason == SCPE_OK) && stop_cpu) { + stop_cpu = FALSE; reason = SCPE_STOP; + } sim_processing_event = FALSE; return reason; } @@ -11231,6 +11333,10 @@ return SCPE_OK; t_stat sim_show_send_input (FILE *st, const SEND *snd) { +const char *dev_name = tmxr_send_line_name (snd); +uint32 delay = get_default_env_parameter (dev_name, "SIM_SEND_DELAY", SEND_DEFAULT_DELAY); +uint32 after = get_default_env_parameter (dev_name, "SIM_SEND_AFTER", delay); + fprintf (st, "%s\n", tmxr_send_line_name (snd)); if (snd->extoff < snd->insoff) { fprintf (st, " %d bytes of pending input Data:\n ", snd->insoff-snd->extoff); @@ -11250,6 +11356,10 @@ if ((snd->delay > (sim_timer_inst_per_sec()/1000000.0)) && ((sim_timer_inst_per_ fprintf (st, " Minimum of %d instructions (%d microseconds) between characters\n", (int)snd->delay, (int)(snd->delay/(sim_timer_inst_per_sec()/1000000.0))); else fprintf (st, " Minimum of %d instructions between characters\n", (int)snd->delay); +if (after) + fprintf (st, " Default delay before first character input is %u instructions\n", after); +if (delay) + fprintf (st, " Default delay between character input is %u instructions\n", after); if (snd->dptr && (snd->dbit & snd->dptr->dctrl)) fprintf (st, " Send Debugging via: SET %s DEBUG%s%s\n", sim_dname(snd->dptr), snd->dptr->debflags ? "=" : "", snd->dptr->debflags ? get_dbg_verb (snd->dbit, snd->dptr) : ""); return SCPE_OK; @@ -11303,20 +11413,20 @@ int32 cond; cptr = get_glyph (cptr, gbuf, 0); if (0 == memcmp("SCPE_", gbuf, 5)) memmove (gbuf, gbuf+5, 1 + strlen (gbuf+5));/* skip leading SCPE_ */ -for (cond=0; cond < (SCPE_MAX_ERR-SCPE_BASE); cond++) +for (cond=0; cond <= (SCPE_MAX_ERR-SCPE_BASE); cond++) if (0 == strcmp(scp_errors[cond].code, gbuf)) { cond += SCPE_BASE; break; } if (0 == strcmp(gbuf, "OK")) cond = SCPE_OK; -if (cond == (SCPE_MAX_ERR-SCPE_BASE)) { /* not found? */ +if (cond == (1+SCPE_MAX_ERR-SCPE_BASE)) { /* not found? */ if (0 == (cond = strtol(gbuf, NULL, 0))) /* try explicit number */ return SCPE_ARG; } +*stat = cond; if (cond > SCPE_MAX_ERR) return SCPE_ARG; -*stat = cond; return SCPE_OK; } diff --git a/scp.h b/scp.h index 12e7b03..c999c1a 100644 --- a/scp.h +++ b/scp.h @@ -347,12 +347,12 @@ extern struct timespec sim_deb_basetime; /* debug base time for r extern DEVICE **sim_internal_devices; extern uint32 sim_internal_device_count; extern UNIT *sim_clock_queue; -extern int32 sim_is_running; +extern volatile t_bool sim_is_running; extern t_bool sim_processing_event; /* Called from sim_process_event */ extern char *sim_prompt; /* prompt string */ extern const char *sim_savename; /* Simulator Name used in Save/Restore files */ extern t_value *sim_eval; -extern volatile int32 stop_cpu; +extern volatile t_bool stop_cpu; extern uint32 sim_brk_types; /* breakpoint info */ extern uint32 sim_brk_dflt; extern uint32 sim_brk_summ; diff --git a/sim_console.c b/sim_console.c index 437b801..14aab8e 100644 --- a/sim_console.c +++ b/sim_console.c @@ -798,6 +798,8 @@ static CTAB allowed_single_remote_cmds[] = { { "ECHO", &echo_cmd, 0 }, { "ECHOF", &echof_cmd, 0 }, { "SHOW", &show_cmd, 0 }, + { "DEBUG", &debug_cmd, 1 }, + { "NODEBUG", &debug_cmd, 0 }, { "HELP", &x_help_cmd, 0 }, { NULL, NULL } }; @@ -1395,15 +1397,17 @@ for (i=(was_active_command ? sim_rem_cmd_active_line : 0); continue; } else { - sim_is_running = 0; + sim_is_running = FALSE; sim_rem_collect_all_registers (); sim_stop_timer_services (); - for (j=0; j < sim_rem_con_tmxr.lines; j++) { - TMLN *lpj = &sim_rem_con_tmxr.ldsc[j]; - if ((i == j) || (!lpj->conn)) - continue; - tmxr_linemsgf (lpj, "\nRemote Master Console(%s) Entering Commands\n", lp->ipad); - tmxr_send_buffered_data (lpj); /* flush any buffered data */ + if (rem->act == NULL) { + for (j=0; j < sim_rem_con_tmxr.lines; j++) { + TMLN *lpj = &sim_rem_con_tmxr.ldsc[j]; + if ((i == j) || (!lpj->conn)) + continue; + tmxr_linemsgf (lpj, "\nRemote Master Console(%s) Entering Commands\n", lp->ipad); + tmxr_send_buffered_data (lpj); /* flush any buffered data */ + } } } } @@ -1418,7 +1422,7 @@ for (i=(was_active_command ? sim_rem_cmd_active_line : 0); if (rem->single_mode) { if (c == sim_int_char) { /* ^E (the interrupt character) must start continue mode console interaction */ rem->single_mode = FALSE; /* enter multi command mode */ - sim_is_running = 0; + sim_is_running = FALSE; sim_rem_collect_all_registers (); sim_stop_timer_services (); stat = SCPE_STOP; @@ -1783,7 +1787,7 @@ for (i=(was_active_command ? sim_rem_cmd_active_line : 0); tmxr_linemsg (lpj, "Simulator Running..."); tmxr_send_buffered_data (lpj); } - sim_is_running = 1; + sim_is_running = TRUE; sim_start_timer_services (); } if (cmdp && (cmdp->action == &x_continue_cmd)) @@ -2764,7 +2768,7 @@ if (!sim_rem_master_mode) { else c = SCPE_OK; if (c == SCPE_STOP) { /* ^E */ - stop_cpu = 1; /* Force a stop (which is picked up by sim_process_event */ + stop_cpu = TRUE; /* Force a stop (which is picked up by sim_process_event */ return SCPE_OK; } if ((sim_con_tmxr.master == 0) && /* not Telnet? */ @@ -2954,7 +2958,6 @@ extern pthread_mutex_t sim_tmxr_poll_lock; extern pthread_cond_t sim_tmxr_poll_cond; extern int32 sim_tmxr_poll_count; extern t_bool sim_tmxr_poll_running; -extern int32 sim_is_running; pthread_t sim_console_poll_thread; /* Keyboard Polling Thread Id */ t_bool sim_console_poll_running = FALSE; @@ -3282,7 +3285,6 @@ return SCPE_OK; #include #include -#include #define RAW_MODE 0 static HANDLE std_input; static HANDLE std_output; diff --git a/sim_defs.h b/sim_defs.h index b012953..fd24e95 100644 --- a/sim_defs.h +++ b/sim_defs.h @@ -127,6 +127,8 @@ extern int sim_vax_snprintf(char *buf, size_t buf_size, const char *fmt, ...); #ifdef _WIN32 #include +#include +#include #undef PACKED /* avoid macro name collision */ #undef ERROR /* avoid macro name collision */ #undef MEM_MAPPED /* avoid macro name collision */ @@ -403,10 +405,10 @@ typedef uint32 t_addr; #define SCPE_AMBREG (SCPE_BASE + 46) /* ambiguous register */ #define SCPE_REMOTE (SCPE_BASE + 47) /* remote console command */ -#define SCPE_MAX_ERR (SCPE_BASE + 48) /* Maximum SCPE Error Value */ -#define SCPE_KFLAG 0x1000 /* tti data flag */ -#define SCPE_BREAK 0x2000 /* tti break flag */ -#define SCPE_NOMESSAGE 0x10000000 /* message display supression flag */ +#define SCPE_MAX_ERR (SCPE_BASE + 47) /* Maximum SCPE Error Value */ +#define SCPE_KFLAG 0x10000000 /* tti data flag */ +#define SCPE_BREAK 0x20000000 /* tti break flag */ +#define SCPE_NOMESSAGE 0x40000000 /* message display supression flag */ #define SCPE_BARE_STATUS(stat) ((stat) & ~(SCPE_NOMESSAGE|SCPE_KFLAG|SCPE_BREAK)) /* Print value format codes */ diff --git a/sim_disk.c b/sim_disk.c index 4d3c096..ae8f61d 100644 --- a/sim_disk.c +++ b/sim_disk.c @@ -80,9 +80,6 @@ Internal routines: #include #include -#ifdef _WIN32 -#include -#endif #if defined SIM_ASYNCH_IO #include #endif @@ -1732,12 +1729,13 @@ if (capac && (capac != (t_offset)-1)) { } } else { - if ((filesystem_capac != (t_offset)-1) && - (filesystem_capac > capac)) - capac = filesystem_capac; - if ((capac != (((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1))) || - (DKUF_F_STD != DK_GET_FMT (uptr))) - uptr->capac = (t_addr)(capac/(ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1))); + if ((filesystem_capac != (t_offset)-1) && /* Known file system data size AND */ + (filesystem_capac > capac)) /* Data size greater than container size? */ + capac = filesystem_capac; /* Use file system data size */ + if (((filesystem_capac != (t_offset)-1) && /* Known file system data size AND */ + (capac > (((t_offset)uptr->capac)*ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1)))) || /* Data > current size */ + (DKUF_F_STD != DK_GET_FMT (uptr))) /* OR ! autosizeable disk */ + uptr->capac = (t_addr)(capac/(ctx->capac_factor*((dptr->flags & DEV_SECTORS) ? 512 : 1))); /* update current size */ } } diff --git a/sim_ether.c b/sim_ether.c index 046765a..c29e5dc 100644 --- a/sim_ether.c +++ b/sim_ether.c @@ -827,7 +827,7 @@ t_stat ethq_init(ETH_QUE* que, int max) que->item = (struct eth_item *) calloc(max, sizeof(struct eth_item)); if (!que->item) { /* failed to allocate memory */ - sim_printf("EthQ: failed to allocate dynamic queue[%d]\r\n", max); + sim_printf("EthQ: failed to allocate dynamic queue[%d]\n", max); return SCPE_MEM; }; que->max = max; @@ -1115,7 +1115,7 @@ static void load_function(const char* function, _func* func_ptr) { *func_ptr = (_func)((size_t)dlsym(hLib, function)); #endif if (*func_ptr == 0) { - sim_printf ("Eth: Failed to find function '%s' in %s\r\n", function, lib_name); + sim_printf ("Eth: Failed to find function '%s' in %s\n", function, lib_name); lib_loaded = 3; } } @@ -1157,11 +1157,11 @@ int load_pcap(void) { #endif if (hLib == 0) { /* failed to load DLL */ - sim_printf ("Eth: Failed to load %s\r\n", lib_name); + sim_printf ("Eth: Failed to load %s\n", lib_name); #ifdef _WIN32 - sim_printf ("Eth: You must install Npcap or WinPcap 4.x to use networking\r\n"); + sim_printf ("Eth: You must install Npcap or WinPcap 4.x to use networking\n"); #else - sim_printf ("Eth: You must install libpcap to use networking\r\n"); + sim_printf ("Eth: You must install libpcap to use networking\n"); #endif lib_loaded = 2; break; @@ -1939,7 +1939,7 @@ return NULL; t_stat eth_set_async (ETH_DEV *dev, int latency) { #if !defined(USE_READER_THREAD) || !defined(SIM_ASYNCH_IO) -char *msg = "Eth: can't operate asynchronously, must poll\r\n"; +char *msg = "Eth: can't operate asynchronously, must poll\n"; sim_printf ("%s", msg); return SCPE_NOFNC; #else @@ -2003,10 +2003,8 @@ if (0 == strncmp("tap:", savname, 4)) { while (isspace(*devname)) ++devname; #if defined(HAVE_TAP_NETWORK) - if (!strcmp(savname, "tap:tapN")) { - sim_printf ("Eth: Must specify actual tap device name (i.e. tap:tap0)\r\n"); - return SCPE_OPENERR | SCPE_NOMESSAGE; - } + if (!strcmp(savname, "tap:tapN")) + return sim_messagef (SCPE_OPENERR, "Eth: Must specify actual tap device name (i.e. tap:tap0)\n"); #endif #if (defined(__linux) || defined(__linux__)) && defined(HAVE_TAP_NETWORK) if ((tun = open("/dev/net/tun", O_RDWR)) >= 0) { @@ -2084,17 +2082,29 @@ if (0 == strncmp("tap:", savname, 4)) { else { /* !tap: */ if (0 == strncmp("vde:", savname, 4)) { #if defined(HAVE_VDE_NETWORK) + char vdeswitch_s[CBUFSIZE]; /* VDE switch name */ + char vdeport_s[CBUFSIZE]; /* VDE switch port (optional), numeric */ + struct vde_open_args voa; const char *devname = savname + 4; memset(&voa, 0, sizeof(voa)); - if (!strcmp(savname, "vde:vdedevice")) { - sim_printf ("Eth: Must specify actual vde device name (i.e. vde:/tmp/switch)\r\n"); - return SCPE_OPENERR | SCPE_NOMESSAGE; - } + if (!strcmp(savname, "vde:vdedevice")) + return sim_messagef (SCPE_OPENERR, "Eth: Must specify actual vde device name (i.e. vde:/tmp/switch)\n"); while (isspace(*devname)) - ++devname; - if (!(*handle = (void*) vde_open((char *)devname, (char *)"simh", &voa))) + ++devname; + devname = get_glyph_nc (devname, vdeswitch_s, ':'); /* Extract switch name */ + devname = get_glyph_nc (devname, vdeport_s, 0); /* Extract optional port number */ + + if (vdeport_s[0]) { /* port provided? */ + t_stat r; + + voa.port = (int)get_uint (vdeport_s, 10, 255, &r); + if (r != SCPE_OK) + return sim_messagef (SCPE_OPENERR, "Eth: Invalid vde port number: %s in %s\n", vdeport_s, savname); + } + + if (!(*handle = (void*) vde_open((char *)vdeswitch_s, (char *)"simh", &voa))) strncpy(errbuf, strerror(errno), PCAP_ERRBUF_SIZE-1); else { *eth_api = ETH_API_VDE; @@ -2110,7 +2120,7 @@ else { /* !tap: */ const char *devname = savname + 4; while (isspace(*devname)) - ++devname; + ++devname; if (!(*handle = (void*) sim_slirp_open(devname, opaque, &_slirp_callback, dptr, dbit))) strncpy(errbuf, strerror(errno), PCAP_ERRBUF_SIZE-1); else { @@ -2127,13 +2137,11 @@ else { /* !tap: */ char hostport[2*CBUFSIZE]; const char *devname = savname + 4; - if (!strcmp(savname, "udp:sourceport:remotehost:remoteport")) { - sim_printf ("Eth: Must specify actual udp host and ports(i.e. udp:1224:somehost.com:2234)\r\n"); - return SCPE_OPENERR | SCPE_NOMESSAGE; - } + if (!strcmp(savname, "udp:sourceport:remotehost:remoteport")) + return sim_messagef (SCPE_OPENERR, "Eth: Must specify actual udp host and ports(i.e. udp:1224:somehost.com:2234)\n"); while (isspace(*devname)) - ++devname; + ++devname; if (SCPE_OK != sim_parse_addr_ex (devname, host, sizeof(host), "localhost", port, sizeof(port), localport, sizeof(localport), NULL)) return SCPE_OPENERR; @@ -2141,23 +2149,19 @@ else { /* !tap: */ strcpy (localport, port); sprintf (hostport, "%s:%s", host, port); if ((SCPE_OK == sim_parse_addr (hostport, NULL, 0, NULL, NULL, 0, NULL, "localhost")) && - (0 == strcmp (localport, port))) { - sim_printf ("Eth: Must specify different udp localhost ports\r\n"); - return SCPE_OPENERR | SCPE_NOMESSAGE; - } + (0 == strcmp (localport, port))) + return sim_messagef (SCPE_OPENERR, "Eth: Must specify different udp localhost ports\n"); *fd_handle = sim_connect_sock_ex (localport, hostport, NULL, NULL, SIM_SOCK_OPT_DATAGRAM); if (INVALID_SOCKET == *fd_handle) - return SCPE_OPENERR; + return SCPE_OPENERR; *eth_api = ETH_API_UDP; *handle = (void *)1; /* Flag used to indicated open */ } else { /* not udp:, so attempt to open the parameter as if it were an explicit device name */ #if defined(HAVE_PCAP_NETWORK) *handle = (void*) pcap_open_live(savname, bufsz, ETH_PROMISC, PCAP_READ_TIMEOUT, errbuf); - if (!*handle) { /* can't open device */ - sim_printf ("Eth: pcap_open_live error - %s\r\n", errbuf); - return SCPE_OPENERR | SCPE_NOMESSAGE; - } + if (!*handle) /* can't open device */ + return sim_messagef (SCPE_OPENERR, "Eth: pcap_open_live error - %s\n", errbuf); *eth_api = ETH_API_PCAP; #if !defined(HAS_PCAP_SENDPACKET) && defined (xBSD) && !defined (__APPLE__) /* Tell the kernel that the header is fully-formed when it gets it. @@ -2174,7 +2178,7 @@ else { /* !tap: */ #ifdef USE_SETNONBLOCK /* set ethernet device non-blocking so pcap_dispatch() doesn't hang */ if (pcap_setnonblock (*handle, 1, errbuf) == -1) { - sim_printf ("Eth: Failed to set non-blocking: %s\r\n", errbuf); + sim_printf ("Eth: Failed to set non-blocking: %s\n", errbuf); } #endif #if defined (__APPLE__) @@ -2210,15 +2214,15 @@ if (bpf_filter && (*eth_api == ETH_API_PCAP)) { /* compile filter string */ if ((status = pcap_compile((pcap_t*)(*handle), &bpf, bpf_filter, 1, bpf_netmask)) < 0) { sprintf(errbuf, "%s", pcap_geterr((pcap_t*)(*handle))); - sim_printf("Eth: pcap_compile error: %s\r\n", errbuf); + sim_printf("Eth: pcap_compile error: %s\n", errbuf); /* show erroneous BPF string */ - sim_printf ("Eth: BPF string is: |%s|\r\n", bpf_filter); + sim_printf ("Eth: BPF string is: |%s|\n", bpf_filter); } else { /* apply compiled filter string */ if ((status = pcap_setfilter((pcap_t*)(*handle), &bpf)) < 0) { sprintf(errbuf, "%s", pcap_geterr((pcap_t*)(*handle))); - sim_printf("Eth: pcap_setfilter error: %s\r\n", errbuf); + sim_printf("Eth: pcap_setfilter error: %s\n", errbuf); } else { #ifdef USE_SETNONBLOCK @@ -2279,16 +2283,14 @@ strncpy (namebuf, savname, sizeof(namebuf)-1); savname = namebuf; r = _eth_open_port(namebuf, &dev->eth_api, &dev->handle, &dev->fd_handle, errbuf, NULL, (void *)dev, dptr, dbit); -if (errbuf[0]) { - sim_printf ("Eth: open error - %s\r\n", errbuf); - return SCPE_OPENERR | SCPE_NOMESSAGE; - } +if (errbuf[0]) + return sim_messagef (SCPE_OPENERR, "Eth: open error - %s\n", errbuf); if (r != SCPE_OK) return r; if (!strcmp (desc, "No description available")) strcpy (desc, ""); -sim_printf ("Eth: opened OS device %s%s%s\r\n", savname, desc[0] ? " - " : "", desc); +sim_printf ("Eth: opened OS device %s%s%s\n", savname, desc[0] ? " - " : "", desc); /* get the NIC's hardware MAC address */ eth_get_nic_hw_addr(dev, savname); @@ -2399,7 +2401,7 @@ ethq_destroy (&dev->read_queue); /* release FIFO queue */ #endif _eth_close_port (dev->eth_api, pcap, pcap_fd); -sim_printf ("Eth: closed %s\r\n", dev->name); +sim_printf ("Eth: closed %s\n", dev->name); /* clean up the mess */ free(dev->name); @@ -2419,8 +2421,8 @@ fprintf (st, " eth0 en0 (No description av #if defined(HAVE_TAP_NETWORK) fprintf (st, " eth1 tap:tapN (Integrated Tun/Tap support)\n"); #endif -#if defined(HAVE_SLIRP_NETWORK) -fprintf (st, " eth2 vde:device (Integrated VDE support)\n"); +#if defined(HAVE_VDE_NETWORK) +fprintf (st, " eth2 vde:device{:switch-port-number} (Integrated VDE support)\n"); #endif #if defined(HAVE_SLIRP_NETWORK) fprintf (st, " eth3 nat:{optional-nat-parameters} (Integrated NAT (SLiRP) support)\n"); @@ -2541,11 +2543,11 @@ status = _eth_write (dev, &send, NULL); if (status != SCPE_OK) { const char *msg; msg = (dev->eth_api == ETH_API_PCAP) ? - "Eth: Error Transmitting packet: %s\r\n" - "You may need to run as root, or install a libpcap version\r\n" - "which is at least 0.9 from your OS vendor or www.tcpdump.org\r\n" : - "Eth: Error Transmitting packet: %s\r\n" - "You may need to run as root.\r\n"; + "Eth: Error Transmitting packet: %s\n" + "You may need to run as root, or install a libpcap version\n" + "which is at least 0.9 from your OS vendor or www.tcpdump.org\n" : + "Eth: Error Transmitting packet: %s\n" + "You may need to run as root.\n"; sim_printf(msg, strerror(errno)); return status; } @@ -3767,15 +3769,15 @@ if (dev->eth_api == ETH_API_PCAP) { /* compile filter string */ if ((status = pcap_compile((pcap_t*)dev->handle, &bpf, buf, 1, bpf_netmask)) < 0) { sprintf(errbuf, "%s", pcap_geterr((pcap_t*)dev->handle)); - sim_printf("Eth: pcap_compile error: %s\r\n", errbuf); + sim_printf("Eth: pcap_compile error: %s\n", errbuf); /* show erroneous BPF string */ - sim_printf ("Eth: BPF string is: |%s|\r\n", buf); + sim_printf ("Eth: BPF string is: |%s|\n", buf); } else { /* apply compiled filter string */ if ((status = pcap_setfilter((pcap_t*)dev->handle, &bpf)) < 0) { sprintf(errbuf, "%s", pcap_geterr((pcap_t*)dev->handle)); - sim_printf("Eth: pcap_setfilter error: %s\r\n", errbuf); + sim_printf("Eth: pcap_setfilter error: %s\n", errbuf); } else { /* Save BPF filter string */ @@ -3896,7 +3898,7 @@ if (used < max) { #endif #ifdef HAVE_VDE_NETWORK if (used < max) { - sprintf(list[used].name, "%s", "vde:device"); + sprintf(list[used].name, "%s", "vde:device{:switch-port-number}"); sprintf(list[used].desc, "%s", "Integrated VDE support"); list[used].eth_api = ETH_API_VDE; ++used; @@ -3933,7 +3935,7 @@ memset(list, 0, max*sizeof(*list)); errbuf[0] = '\0'; /* retrieve the device list */ if (pcap_findalldevs(&alldevs, errbuf) == -1) { - sim_printf ("Eth: error in pcap_findalldevs: %s\r\n", errbuf); + sim_printf ("Eth: error in pcap_findalldevs: %s\n", errbuf); } else { /* copy device list into the passed structure */ @@ -3956,7 +3958,7 @@ i = eth_host_devices(i, max, list); /* If no devices were found and an error message was left in the buffer, display it */ if ((i == 0) && (errbuf[0])) { - sim_printf ("Eth: pcap_findalldevs warning: %s\r\n", errbuf); + sim_printf ("Eth: pcap_findalldevs warning: %s\n", errbuf); } /* return device count */ diff --git a/sim_frontpanel.c b/sim_frontpanel.c index 9f7b1a7..ed41f78 100644 --- a/sim_frontpanel.c +++ b/sim_frontpanel.c @@ -63,6 +63,7 @@ extern "C" { #if defined(_WIN32) #include #include +#include #define sleep(n) Sleep(n*1000) #define msleep(n) Sleep(n) #define strtoull _strtoui64 @@ -153,9 +154,9 @@ struct PANEL { pthread_mutex_t io_send_lock; pthread_mutex_t io_command_lock; int command_count; - int io_reg_query_pending; int io_waiting; char *io_response; + char *halt_reason; size_t io_response_data; size_t io_response_size; const char *completion_string; @@ -177,6 +178,7 @@ struct PANEL { FILE *Debug; #if defined(_WIN32) HANDLE hProcess; + DWORD dwProcessId; #else pid_t pidProcess; #endif @@ -221,7 +223,7 @@ static const char *register_collect_mid3 = " percent "; static const char *register_get_postfix = "sampleout"; static const char *register_get_start = "# REGISTERS-START"; static const char *register_get_end = "# REGISTERS-DONE"; -static const char *register_repeat_start = "# REGISTERS-REPEAT-START\r"; +static const char *register_repeat_start = "# REGISTERS-REPEAT-START"; static const char *register_repeat_end = "# REGISTERS-REPEAT-DONE"; static const char *register_dev_echo = "# REGISTERS-FOR-DEVICE:"; static const char *register_ind_echo = "# REGISTER-INDIRECT:"; @@ -408,9 +410,9 @@ pthread_mutex_lock (&p->io_lock); while (p->sock != INVALID_SOCKET) { pthread_mutex_unlock (&p->io_lock); msleep (1000); + pthread_mutex_lock (&p->io_lock); if (0 == (sleeps++)%flush_interval) sim_panel_flush_debug (p); - pthread_mutex_lock (&p->io_lock); } pthread_mutex_unlock (&p->io_lock); pthread_mutex_lock (&p->io_lock); @@ -484,7 +486,7 @@ size_t i, j, buf_data, buf_needed = 0, reg_count = 0, bit_reg_count = 0; const char *dev; pthread_mutex_lock (&panel->io_lock); -buf_needed = 3 + +buf_needed = 3 + 7 + /* EXECUTE */ strlen (register_get_start) + /* # REGISTERS-START */ strlen (register_get_prefix); /* SHOW TIME */ for (i=0; ireg_count; i++) { @@ -492,7 +494,7 @@ for (i=0; ireg_count; i++) { ++bit_reg_count; else { ++reg_count; - buf_needed += 9 + strlen (panel->regs[i].name) + (panel->regs[i].device_name ? strlen (panel->regs[i].device_name) : 0); + buf_needed += 10 + strlen (panel->regs[i].name) + (panel->regs[i].device_name ? strlen (panel->regs[i].device_name) : 0); if (panel->regs[i].element_count > 0) buf_needed += 4 + 6 /* 6 digit register array index */; if (panel->regs[i].indirect) @@ -514,7 +516,7 @@ if (buf_needed > *buf_size) { } buf_data = 0; if (reg_count) { - sprintf (*buf + buf_data, "%s\r%s\r", register_get_start, register_get_prefix); + sprintf (*buf + buf_data, "EXECUTE %s;%s;", register_get_start, register_get_prefix); buf_data += strlen (*buf + buf_data); } dev = ""; @@ -536,7 +538,7 @@ for (i=j=0; ireg_count; i++) { strcpy (tbuf, *buf); free (*buf); *buf = tbuf; - sprintf (*buf + buf_data, "%s%s%s\r", (i == 0)? "" : "\r", register_dev_echo, reg_dev); + sprintf (*buf + buf_data, "%s%s%s;", (i == 0)? "" : ";", register_dev_echo, reg_dev); buf_data += strlen (*buf + buf_data); dev = reg_dev; j = 0; @@ -544,21 +546,21 @@ for (i=j=0; ireg_count; i++) { } if (panel->regs[i].element_count == 0) { if (j == 0) - sprintf (*buf + buf_data, "E -H %s %s", dev, panel->regs[i].name); + sprintf (*buf + buf_data, "E -16 %s %s", dev, panel->regs[i].name); else sprintf (*buf + buf_data, ",%s", panel->regs[i].name); } else { if (j == 0) - sprintf (*buf + buf_data, "E -H %s %s[0:%d]", dev, panel->regs[i].name, (int)(panel->regs[i].element_count-1)); + sprintf (*buf + buf_data, "E -16 %s %s[0:%d]", dev, panel->regs[i].name, (int)(panel->regs[i].element_count-1)); else sprintf (*buf + buf_data, ",%s[0:%d]", panel->regs[i].name, (int)(panel->regs[i].element_count-1)); } ++j; buf_data += strlen (*buf + buf_data); } -if (buf_data && ((*buf)[buf_data-1] != '\r')) { - strcpy (*buf + buf_data, "\r"); +if (buf_data && ((*buf)[buf_data-1] != ';')) { + strcpy (*buf + buf_data, ";"); buf_data += strlen (*buf + buf_data); } for (i=j=0; ireg_count; i++) { @@ -566,13 +568,13 @@ for (i=j=0; ireg_count; i++) { if ((!panel->regs[i].indirect) || (panel->regs[i].bits)) continue; - sprintf (*buf + buf_data, "%s%s\rE -H %s %s,$\r", register_ind_echo, panel->regs[i].name, reg_dev, panel->regs[i].name); + sprintf (*buf + buf_data, "%s%s;E -16 %s %s,$;", register_ind_echo, panel->regs[i].name, reg_dev, panel->regs[i].name); buf_data += strlen (*buf + buf_data); } if (bit_reg_count) { strcpy (*buf + buf_data, register_get_postfix); buf_data += strlen (*buf + buf_data); - strcpy (*buf + buf_data, "\r"); + strcpy (*buf + buf_data, ";"); buf_data += strlen (*buf + buf_data); } strcpy (*buf + buf_data, register_get_end); @@ -855,6 +857,7 @@ if (!simulator_panel) { if (CreateProcessA(NULL, cmd, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &StartupInfo, &ProcessInfo)) { CloseHandle (ProcessInfo.hThread); p->hProcess = ProcessInfo.hProcess; + p->dwProcessId = ProcessInfo.dwProcessId; } else { /* Creation Problem */ sim_panel_set_error (NULL, "CreateProcess Error: %d", GetLastError()); @@ -1074,6 +1077,8 @@ if (panel) { pthread_cond_destroy (&panel->io_done); #if defined(_WIN32) if (panel->hProcess) { + GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, panel->dwProcessId); + msleep (200); TerminateProcess (panel->hProcess, 0); WaitForSingleObject (panel->hProcess, INFINITE); CloseHandle (panel->hProcess); @@ -1106,6 +1111,7 @@ if (panel) { free (panel->regs); free (panel->reg_query); free (panel->io_response); + free (panel->halt_reason); free (panel->simulator_version); if ((panel->Debug) && (!panel->parent)) fclose (panel->Debug); @@ -1353,12 +1359,9 @@ if (panel->reg_query_size != _panel_send (panel, panel->reg_query, panel->reg_qu pthread_mutex_unlock (&panel->io_command_lock); return -1; } -while (panel->io_reg_query_pending != 0) { - pthread_mutex_unlock (&panel->io_lock); - msleep (100); - pthread_mutex_lock (&panel->io_lock); - } -++panel->io_reg_query_pending; +if (panel->io_response_data) + _panel_debug (panel, DBG_RCV, "Receive Data Discarded: ", panel->io_response, panel->io_response_data); +panel->io_response_data = 0; panel->io_waiting = 1; while (panel->io_waiting) pthread_cond_wait (&panel->io_done, &panel->io_lock); @@ -1372,7 +1375,7 @@ return 0; int sim_panel_get_registers (PANEL *panel, unsigned long long *simulation_time) { -return _panel_get_registers (panel, 0, simulation_time); +return _panel_get_registers (panel, (panel->State == Halt), simulation_time); } int @@ -1472,6 +1475,15 @@ if (panel->State == Run) { return 0; } +const char * +sim_panel_halt_text (PANEL *panel) +{ +if (!panel || !panel->halt_reason) + return ""; +return panel->halt_reason; +} + + int sim_panel_exec_boot (PANEL *panel, const char *device) { @@ -1538,7 +1550,7 @@ if ((simtime = strstr (response, "Time:"))) { } free (response); panel->simulation_time_base += panel->simulation_time; -if (_panel_sendf_completion (panel, NULL, "Simulator Running...", "RUN -Q\r", 5)) { +if (_panel_sendf_completion (panel, NULL, "Simulator Running...", "RUN\r", 5)) { _panel_debug (panel, DBG_THR, "Unable to start simulator: %s", NULL, 0, sim_panel_get_error()); return -1; } @@ -1768,6 +1780,41 @@ free (response); return 0; } +int +sim_panel_device_debug_mode (PANEL *panel, + const char *device, + int set_unset, + const char *mode_bits) +{ +char *response = NULL; +int cmd_stat; + +if (!panel || (panel->State == Error)) { + sim_panel_set_error (NULL, "Invalid Panel"); + return -1; + } +if ((device != NULL) && + ((_panel_sendf (panel, &cmd_stat, &response, "SHOW %s", device) || + (cmd_stat)))) { + sim_panel_set_error (NULL, "Can't %s Debug Mode: '%s' on Device '%s': %s", + set_unset ? "Enable" : "Disable", mode_bits ? mode_bits : "", device, response); + free (response); + return -1; + } +free (response); +response = NULL; +if (_panel_sendf (panel, &cmd_stat, &response, "%sDEBUG %s %s", + set_unset ? "" : "NO", device ? device : "", mode_bits ? mode_bits : "") || + (cmd_stat)) { + sim_panel_set_error (NULL, "Can't %s Debug Mode: '%s' on Device '%s': %s", + set_unset ? "Enable" : "Disable", mode_bits ? mode_bits : "", device, response); + free (response); + return -1; + } +free (response); +return 0; +} + /** sim_panel_gen_deposit @@ -2069,7 +2116,7 @@ struct sched_param sched_priority; char buf[4096]; int buf_data = 0; int processing_register_output = 0; -int io_wait_done; +int io_wait_done = 0; /* Boost Priority for this response processing thread to quickly digest @@ -2119,18 +2166,20 @@ while ((p->sock != INVALID_SOCKET) && int new_data; char *s, *e, *eol; - pthread_mutex_unlock (&p->io_lock); - new_data = sim_read_sock (p->sock, &buf[buf_data], sizeof(buf)-(buf_data+1)); - pthread_mutex_lock (&p->io_lock); - if (new_data <= 0) { - sim_panel_set_error (NULL, "%s", sim_get_err_sock("Unexpected socket read")); - _panel_debug (p, DBG_RCV, "%s", NULL, 0, sim_panel_get_error()); - p->State = Error; - break; + if (NULL == strchr (buf, '\n')) { + pthread_mutex_unlock (&p->io_lock); + new_data = sim_read_sock (p->sock, &buf[buf_data], sizeof(buf)-(buf_data+1)); + pthread_mutex_lock (&p->io_lock); + if (new_data <= 0) { + sim_panel_set_error (NULL, "%s", sim_get_err_sock("Unexpected socket read")); + _panel_debug (p, DBG_RCV, "%s", NULL, 0, sim_panel_get_error()); + p->State = Error; + break; + } + _panel_debug (p, DBG_RCV, "Received %d bytes: ", &buf[buf_data], new_data, new_data); + buf_data += new_data; + buf[buf_data] = '\0'; } - _panel_debug (p, DBG_RCV, "Received %d bytes: ", &buf[buf_data], new_data, new_data); - buf_data += new_data; - buf[buf_data] = '\0'; s = buf; while ((eol = strchr (s, '\n'))) { /* Line to process */ @@ -2258,72 +2307,72 @@ while ((p->sock != INVALID_SOCKET) && /* Unexpected Register Data Found (or other output containing a : character) */ } } - if (!strcmp (s + strlen (sim_prompt), register_repeat_end)) { - _panel_debug (p, DBG_RCV, "*Repeat Block Complete", NULL, 0); + if ((strlen (s) > strlen (sim_prompt)) && (!strcmp (s + strlen (sim_prompt), register_repeat_end))) { + _panel_debug (p, DBG_RCV, "*Repeat Block Complete (Accumulated Data = %d)", NULL, 0, (int)p->io_response_data); if (p->callback) { pthread_mutex_unlock (&p->io_lock); p->callback (p, p->simulation_time_base + p->simulation_time, p->callback_context); pthread_mutex_lock (&p->io_lock); } processing_register_output = 0; + p->io_response_data = 0; + p->io_response[p->io_response_data] = '\0'; goto Start_Next_Line; } - if ((!strcmp (s + strlen (sim_prompt), register_repeat_start)) || - (!strcmp (s + strlen (sim_prompt), register_get_start))) { + if ((strlen (s) > strlen (sim_prompt)) && + ((!strcmp (s + strlen (sim_prompt), register_repeat_start)) || + (!strcmp (s + strlen (sim_prompt), register_get_start)))) { _panel_debug (p, DBG_RCV, "*Repeat/Register Block Starting", NULL, 0); processing_register_output = 1; goto Start_Next_Line; } - if (!strcmp (s + strlen (sim_prompt), register_get_end)) { + if ((strlen (s) > strlen (sim_prompt)) && + (!strcmp (s + strlen (sim_prompt), register_get_end))) { _panel_debug (p, DBG_RCV, "*Register Block Complete", NULL, 0); - --p->io_reg_query_pending; p->io_waiting = 0; processing_register_output = 0; pthread_cond_signal (&p->io_done); goto Start_Next_Line; } - if (!strcmp (s + strlen (sim_prompt), command_done_echo)) { + if ((strlen (s) > strlen (sim_prompt)) && (!strcmp (s + strlen (sim_prompt), command_done_echo))) { _panel_debug (p, DBG_RCV, "*Received Command Complete", NULL, 0); p->io_waiting = 0; pthread_cond_signal (&p->io_done); goto Start_Next_Line; } /* Non Register Data Found (echo of EXAMINE or other commands and/or command output) */ - if (p->io_waiting) { - char *t; + if (p->io_response_data + strlen (s) + 3 > p->io_response_size) { + char *t = (char *)_panel_malloc (p->io_response_data + strlen (s) + 3); - if (p->io_response_data + strlen (s) + 3 > p->io_response_size) { - t = (char *)_panel_malloc (p->io_response_data + strlen (s) + 3); - if (t == NULL) { - _panel_debug (p, DBG_RCV, "%s", NULL, 0, sim_panel_get_error()); - p->State = Error; - break; - } - memcpy (t, p->io_response, p->io_response_data); - free (p->io_response); - p->io_response = t; - p->io_response_size = p->io_response_data + strlen (s) + 3; - } - _panel_debug (p, DBG_RCV, "Receive Data Accumulated: '%s'", NULL, 0, s); - strcpy (p->io_response + p->io_response_data, s); - p->io_response_data += strlen(s); - strcpy (p->io_response + p->io_response_data, "\r\n"); - p->io_response_data += 2; - if ((!p->parent) && - (p->completion_string) && - (!memcmp (s, p->completion_string, strlen (p->completion_string)))) { - _panel_debug (p, DBG_RCV, "Match with potentially coalesced additional data: '%s'", NULL, 0, p->completion_string); + if (t == NULL) { + _panel_debug (p, DBG_RCV, "%s", NULL, 0, sim_panel_get_error()); + p->State = Error; break; } + memcpy (t, p->io_response, p->io_response_data); + free (p->io_response); + p->io_response = t; + p->io_response_size = p->io_response_data + strlen (s) + 3; + } + _panel_debug (p, DBG_RCV, "Receive Data Accumulated: '%s'", NULL, 0, s); + strcpy (p->io_response + p->io_response_data, s); + p->io_response_data += strlen(s); + strcpy (p->io_response + p->io_response_data, "\r\n"); + p->io_response_data += 2; + if ((!p->parent) && + (p->completion_string) && + (!memcmp (s, p->completion_string, strlen (p->completion_string)))) { + _panel_debug (p, DBG_RCV, "Match with potentially coalesced additional data: '%s'", NULL, 0, p->completion_string); + if (eol < &buf[buf_data]) + memset (s + strlen (s), ' ', eol - (s + strlen (s))); + break; } - else - _panel_debug (p, DBG_RCV, "Receive Data Discarded: '%s'", NULL, 0, s); Start_Next_Line: s = eol; while (isspace(0xFF & (*s))) ++s; } - memmove (buf, s, strlen (s) + 1); + memmove (buf, s, buf_data - (s - buf) + 1); buf_data = strlen (buf); if (buf_data) _panel_debug (p, DBG_RSP, "Remnant Buffer Contents: '%s'", NULL, 0, buf); @@ -2337,28 +2386,36 @@ Start_Next_Line: _panel_debug (p, DBG_RSP, "State transitioning to Run", NULL, 0); p->State = Run; buf_data -= 20; - buf[buf_data] = '\0'; if (buf_data) { - memmove (buf, buf + 20, strlen (buf + 20) + 1); - buf_data = strlen (buf); + memmove (buf, buf + 20, buf_data + 1); _panel_debug (p, DBG_RSP, "Remnant Buffer Contents: '%s'", NULL, 0, buf); - if (io_wait_done) { /* someone waiting for this? */ - _panel_debug (p, DBG_RCV, "*Match Command Complete - Match signaling waiting thread", NULL, 0); - io_wait_done = 0; - p->io_waiting = 0; - p->completion_string = NULL; - pthread_cond_signal (&p->io_done); - /* Let this state transition propagate to the interested thread(s) */ - /* before processing remaining buffered data */ - pthread_mutex_unlock (&p->io_lock); - msleep (100); - pthread_mutex_lock (&p->io_lock); - } + } + else + buf[buf_data] = '\0'; + if (io_wait_done) { /* someone waiting for this? */ + _panel_debug (p, DBG_RCV, "*Match Command Complete - Match signaling waiting thread", NULL, 0); + io_wait_done = 0; + p->io_waiting = 0; + p->completion_string = NULL; + pthread_cond_signal (&p->io_done); + /* Let this state transition propagate to the interested thread(s) */ + /* before processing remaining buffered data */ + pthread_mutex_unlock (&p->io_lock); + msleep (100); + pthread_mutex_lock (&p->io_lock); } } if ((p->State == Run) && (!strcmp (buf, sim_prompt))) { - _panel_debug (p, DBG_RSP, "State transitioning to Halt", NULL, 0); + _panel_debug (p, DBG_RSP, "State transitioning to Halt: io_wait_done: %d", NULL, 0, io_wait_done); p->State = Halt; + free (p->halt_reason); + p->halt_reason = (char *)_panel_malloc (1 + strlen (p->io_response)); + if (p->halt_reason == NULL) { + _panel_debug (p, DBG_RCV, "%s", NULL, 0, sim_panel_get_error()); + p->State = Error; + break; + } + strcpy (p->halt_reason, p->io_response); } if (io_wait_done) { _panel_debug (p, DBG_RCV, "*Match Command Complete - Match signaling waiting thread", NULL, 0); @@ -2426,43 +2483,40 @@ while ((p->sock != INVALID_SOCKET) && msleep (500); pthread_mutex_lock (&p->io_lock); if (new_register) { - if (p->io_reg_query_pending == 0) { - size_t repeat_data = strlen (register_repeat_prefix) + /* prefix */ - 20 + /* max int width */ - strlen (register_repeat_units) + /* units and spacing */ - buf_data + /* command contents */ - 1 + /* carriage return */ - strlen (register_repeat_start) + /* auto repeat begin */ - 1 + /* carriage return */ - strlen (register_repeat_end) + /* auto repeat completion */ - 1 + /* carriage return */ - 1; /* NUL */ - char *repeat = (char *)malloc (repeat_data); - char *c; + size_t repeat_data = strlen (register_repeat_prefix) + /* prefix */ + 20 + /* max int width */ + strlen (register_repeat_units) + /* units and spacing */ + buf_data + /* command contents */ + 1 + /* ; */ + strlen (register_repeat_start) + /* auto repeat begin */ + 1 + /* ; */ + strlen (register_repeat_end) + /* auto repeat completion */ + 1 + /* carriage return */ + 1; /* NUL */ + char *repeat = (char *)malloc (repeat_data); + char *c; - sprintf (repeat, "%s%d%s%s%*.*s", register_repeat_prefix, - p->usecs_between_callbacks, - register_repeat_units, - register_repeat_start, - (int)buf_data, (int)buf_data, buf); - pthread_mutex_unlock (&p->io_lock); - for (c = strchr (repeat, '\r'); c != NULL; c = strchr (c, '\r')) - *c = ';'; /* replace carriage returns with semicolons */ - c = strstr (repeat, register_get_end); /* remove register_done_echo string and */ - if (c) /* always true */ - strcpy (c, register_repeat_end); /* replace it with the register_repeat_end string */ - if (_panel_sendf (p, &cmd_stat, NULL, "%s", repeat)) { - pthread_mutex_lock (&p->io_lock); - free (repeat); - break; - } + c = strstr (buf, register_get_start); /* remove register_get_start string and anything before it */ + if (c) { /* always true */ + buf_data -= (c - buf) + strlen (register_get_start); + c += strlen (register_get_start); + } + sprintf (repeat, "%s%d%s%s%*.*s", register_repeat_prefix, + p->usecs_between_callbacks, + register_repeat_units, + register_repeat_start, + (int)buf_data, (int)buf_data, c); + pthread_mutex_unlock (&p->io_lock); + c = strstr (repeat, register_get_end); /* remove register_done_echo string and */ + if (c) /* always true */ + strcpy (c, register_repeat_end); /* replace it with the register_repeat_end string */ + if (_panel_sendf (p, &cmd_stat, NULL, "%s", repeat)) { pthread_mutex_lock (&p->io_lock); free (repeat); + break; } - else { /* already busy */ - p->new_register = 1; /* retry later */ - _panel_debug (p, DBG_XMT, "Waiting on prior command completion before specifying repeat interval", NULL, 0); - } + pthread_mutex_lock (&p->io_lock); + free (repeat); } /* when halted, we directly poll the halted system to get updated */ /* register state which may have changed due to panel activities */ @@ -2545,7 +2599,7 @@ while (1) { /* format passed string, arg if (len >= (int)(sim_panel_error_bufsize-1)) { free (sim_panel_error_buf); sim_panel_error_bufsize = sim_panel_error_bufsize * 2; - while ((int)sim_panel_error_bufsize < len + 1) + while ((int)sim_panel_error_bufsize < len + 2) sim_panel_error_bufsize = sim_panel_error_bufsize * 2; sim_panel_error_buf = (char *) malloc (sim_panel_error_bufsize); if (sim_panel_error_buf == NULL) { @@ -2610,6 +2664,8 @@ if (completion_status || completion_string) { } pthread_mutex_lock (&p->io_lock); p->completion_string = completion_string; + if (p->io_response_data) + _panel_debug (p, DBG_RCV, "Receive Data Discarded: ", p->io_response, p->io_response_data); p->io_response_data = 0; p->io_waiting = 1; } diff --git a/sim_frontpanel.h b/sim_frontpanel.h index 4ce6fec..0e03a88 100644 --- a/sim_frontpanel.h +++ b/sim_frontpanel.h @@ -56,7 +56,7 @@ extern "C" { #if !defined(__VAX) /* Unsupported platform */ -#define SIM_FRONTPANEL_VERSION 11 +#define SIM_FRONTPANEL_VERSION 12 /** @@ -284,6 +284,19 @@ sim_panel_exec_run (PANEL *panel); int sim_panel_exec_step (PANEL *panel); + + +/** + A simulator often displays some useful information as it stops + executing instructions. + + sim_panel_halt_text - Returns the simulator output immediately prior + to the most recent transition to the Halt state. + */ + +const char * +sim_panel_halt_text (PANEL *panel); + /** When a front panel application wants to describe conditions that @@ -472,6 +485,34 @@ sim_panel_get_history (PANEL *panel, char *buffer); +/** + + A front panel application might want some details of simulator + and/or device behavior that is provided by a particular simulator + via debug information. Debugging for particular device(s) + and/or simulator debug settings can be controlled via the + sim_panel_device_debug_mode API. + */ + +/** + + sim_panel_device_debug_mode + + device the device whose debug mode is to change + set_untset 1 to set debug flags, 0 to clear debug flags + mode_bits character string with different debug mode bits + to enable or disable. An empty string will + enable or disable all mode bits for the specified + device + */ + +int +sim_panel_device_debug_mode (PANEL *panel, + const char *device, + int set_unset, + const char *mode_bits); + + /** When a front panel application needs to change the media diff --git a/sim_sock.h b/sim_sock.h index 60518bb..326720c 100644 --- a/sim_sock.h +++ b/sim_sock.h @@ -52,6 +52,7 @@ extern "C" { #if defined (_WIN32) /* Windows */ #include +#include #elif !defined (__OS2__) || defined (__EMX__) /* VMS, Mac, Unix, OS/2 EMX */ #include /* for fcntl, getpid */ diff --git a/sim_timer.c b/sim_timer.c index 4159f3a..7606139 100644 --- a/sim_timer.c +++ b/sim_timer.c @@ -167,8 +167,10 @@ static uint32 sim_throt_ms_start = 0; static uint32 sim_throt_ms_stop = 0; static uint32 sim_throt_type = 0; static uint32 sim_throt_val = 0; +static uint32 sim_throt_drift_pct = SIM_THROT_DRIFT_PCT_DFLT; static uint32 sim_throt_state = SIM_THROT_STATE_INIT; static double sim_throt_cps; +static double sim_throt_peak_cps; static double sim_throt_inst_start; static uint32 sim_throt_sleep_time = 0; static int32 sim_throt_wait = 0; @@ -910,7 +912,7 @@ if (!rtc_avail) /* no timer? */ return rtc_currd[tmr]; if (sim_calb_tmr != tmr) { rtc_currd[tmr] = (int32)(sim_timer_inst_per_sec()/ticksper); - sim_debug (DBG_CAL, &sim_timer_dev, "calibrated calibrated tmr=%d against internal system tmr=%d, tickper=%d (result: %d)\n", tmr, sim_calb_tmr, ticksper, rtc_currd[tmr]); + sim_debug (DBG_CAL, &sim_timer_dev, "calibrated tmr=%d against internal system tmr=%d, tickper=%d (result: %d)\n", tmr, sim_calb_tmr, ticksper, rtc_currd[tmr]); return rtc_currd[tmr]; } new_rtime = sim_os_msec (); /* wall time */ @@ -1076,7 +1078,7 @@ fprintf (st, "Minimum Host Sleep Time: %d ms (%dHz)\n", sim_os_sleep_min_m if (sim_os_sleep_min_ms != sim_os_sleep_inc_ms) fprintf (st, "Minimum Host Sleep Incr Time: %d ms\n", sim_os_sleep_inc_ms); fprintf (st, "Host Clock Resolution: %d ms\n", sim_os_clock_resoluton_ms); -fprintf (st, "Execution Rate: %s instructions/sec\n", sim_fmt_numeric (inst_per_sec)); +fprintf (st, "Execution Rate: %s cycles/sec\n", sim_fmt_numeric (inst_per_sec)); if (sim_idle_enab) { fprintf (st, "Idling: Enabled\n"); fprintf (st, "Time before Idling starts: %d seconds\n", sim_idle_stable); @@ -1260,6 +1262,8 @@ REG sim_throttle_reg[] = { { DRDATAD (THROT_STATE, sim_throt_state, 32, ""), PV_RSPC|REG_RO}, { DRDATAD (THROT_SLEEP_TIME, sim_throt_sleep_time, 32, ""), PV_RSPC|REG_RO}, { DRDATAD (THROT_WAIT, sim_throt_wait, 32, ""), PV_RSPC|REG_RO}, + { DRDATAD (THROT_DELAY, sim_idle_stable, 32, "Seconds before throttling starts"), PV_RSPC}, + { DRDATAD (THROT_DRIFT_PCT, sim_throt_drift_pct, 32, "Percent of throttle drift before correction"), PV_RSPC}, { NULL } }; @@ -1671,9 +1675,12 @@ else { break; case SIM_THROT_PCT: - fprintf (st, "Throttle: %d%%\n", sim_throt_val); - if (sim_throt_wait) + if (sim_throt_wait) { + fprintf (st, "Throttle: %d%% of %s cycles per second\n", sim_throt_val, sim_fmt_numeric (sim_throt_peak_cps)); fprintf (st, "Throttling by sleeping for: %d ms every %d cycles\n", sim_throt_sleep_time, sim_throt_wait); + } + else + fprintf (st, "Throttle: %d%%\n", sim_throt_val); break; case SIM_THROT_SPC: @@ -1695,9 +1702,20 @@ return SCPE_OK; void sim_throt_sched (void) { -sim_throt_state = SIM_THROT_STATE_INIT; -if (sim_throt_type) - sim_activate (&sim_throttle_unit, SIM_THROT_WINIT); +if (sim_throt_type != SIM_THROT_NONE) { + if (sim_throt_state == SIM_THROT_STATE_THROTTLE) { /* Previously calibrated? */ + /* Reset recalibration reference times */ + sim_throt_ms_start = sim_os_msec (); + sim_throt_inst_start = sim_gtime (); + /* Start with prior calibrated delay */ + sim_activate (&sim_throttle_unit, sim_throt_wait); + } + else { + /* Start calibration initially */ + sim_throt_state = SIM_THROT_STATE_INIT; + sim_activate (&sim_throttle_unit, SIM_THROT_WINIT); + } + } } void sim_throt_cancel (void) @@ -1718,16 +1736,41 @@ t_stat sim_throt_svc (UNIT *uptr) { int32 tmr; uint32 delta_ms; -double a_cps, d_cps; +double a_cps, d_cps, delta_inst; switch (sim_throt_state) { case SIM_THROT_STATE_INIT: /* take initial reading */ + if ((sim_calb_tmr != -1) && (rtc_hz[sim_calb_tmr] != 0)) { + if (rtc_calibrations[sim_calb_tmr] < sim_idle_stable) { + sim_throt_ms_start = sim_os_msec (); + sim_throt_inst_start = sim_gtime (); + sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc(INIT) Deferring until stable (%d more seconds)\n", (int)(sim_idle_stable - rtc_calibrations[sim_calb_tmr])); + return sim_activate (uptr, rtc_hz[sim_calb_tmr]*rtc_currd[sim_calb_tmr]); + } + sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc(INIT) Computing Throttling values based on the last second's execution rate\n"); + sim_throt_state = SIM_THROT_STATE_TIME; + if (sim_throt_peak_cps < (double)(rtc_hz[sim_calb_tmr] * rtc_currd[sim_calb_tmr])) + sim_throt_peak_cps = (double)rtc_hz[sim_calb_tmr] * rtc_currd[sim_calb_tmr]; + return sim_throt_svc (uptr); + } + else + sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc(INIT) Calibrated timer not available. Falling back to legacy method\n"); sim_idle_ms_sleep (sim_idle_rate_ms); /* start on a tick boundary to calibrate */ sim_throt_ms_start = sim_os_msec (); - sim_throt_inst_start = sim_gtime(); + sim_throt_inst_start = sim_gtime (); if (sim_throt_type != SIM_THROT_SPC) { /* dynamic? */ - sim_throt_wait = SIM_THROT_WST; + switch (sim_throt_type) { + case SIM_THROT_PCT: + sim_throt_wait = (int32)((sim_throt_peak_cps * sim_throt_val) / 100.0); + break; + case SIM_THROT_KCYC: + sim_throt_wait = sim_throt_val * 1000; + break; + case SIM_THROT_MCYC: + sim_throt_wait = sim_throt_val * 1000000; + break; + } sim_throt_state = SIM_THROT_STATE_TIME; /* next state */ } else { /* Non dynamic? */ @@ -1741,34 +1784,43 @@ switch (sim_throt_state) { case SIM_THROT_STATE_TIME: /* take final reading */ sim_throt_ms_stop = sim_os_msec (); delta_ms = sim_throt_ms_stop - sim_throt_ms_start; + delta_inst = sim_gtime () - sim_throt_inst_start; if (delta_ms < SIM_THROT_MSMIN) { /* not enough time? */ - if (sim_throt_wait >= 100000000) { /* too many inst? */ + if (delta_inst >= 100000000.0) { /* too many inst? */ sim_throt_state = SIM_THROT_STATE_INIT; /* fails in 32b! */ sim_printf ("Can't throttle. Host CPU is too fast with a minimum sleep time of %d ms\n", sim_idle_rate_ms); sim_set_throt (0, NULL); /* disable throttling */ return SCPE_OK; } - sim_idle_ms_sleep (sim_idle_rate_ms); /* start on a tick boundart to calibrate */ - sim_throt_wait = sim_throt_wait * SIM_THROT_WMUL; - sim_throt_ms_start = sim_os_msec (); + sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() Not enough time. %d ms executing %.f instructions.\n", + (int)delta_ms, delta_inst); + sim_throt_wait = (int32)(delta_inst * SIM_THROT_WMUL); sim_throt_inst_start = sim_gtime(); + sim_idle_ms_sleep (sim_idle_rate_ms); /* start on a tick boundart to calibrate */ + sim_throt_ms_start = sim_os_msec (); } else { /* long enough */ - a_cps = ((double) sim_throt_wait) * 1000.0 / (double) delta_ms; + a_cps = (((double) delta_inst) * 1000.0) / (double) delta_ms; if (sim_throt_type == SIM_THROT_MCYC) /* calc desired cps */ d_cps = (double) sim_throt_val * 1000000.0; - else if (sim_throt_type == SIM_THROT_KCYC) - d_cps = (double) sim_throt_val * 1000.0; - else d_cps = (a_cps * ((double) sim_throt_val)) / 100.0; - if (d_cps >= a_cps) { + else + if (sim_throt_type == SIM_THROT_KCYC) + d_cps = (double) sim_throt_val * 1000.0; + else + d_cps = (sim_throt_peak_cps * sim_throt_val) / 100.0; + if (d_cps > a_cps) { + sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() CPU too slow. Values a_cps = %f, d_cps = %f\n", + a_cps, d_cps); sim_throt_state = SIM_THROT_STATE_INIT; + sim_printf ("*********** WARNING ***********\n"); sim_printf ("Host CPU is too slow to simulate %s instructions per second\n", sim_fmt_numeric(d_cps)); + sim_printf ("Host CPU can only simulate %s instructions per second\n", sim_fmt_numeric(sim_throt_peak_cps)); sim_printf ("Throttling disabled.\n"); sim_set_throt (0, NULL); return SCPE_OK; } while (1) { - sim_throt_wait = (int32) /* time between waits */ + sim_throt_wait = (int32) /* cycles between sleeps */ ((a_cps * d_cps * ((double) sim_throt_sleep_time)) / (1000.0 * (a_cps - d_cps))); if (sim_throt_wait >= SIM_THROT_WMIN) /* long enough? */ @@ -1785,11 +1837,18 @@ switch (sim_throt_state) { sim_throt_cps = d_cps; /* save the desired rate */ /* Run through all timers and adjust the calibration for each */ /* one that is running to reflect the throttle rate */ - for (tmr=0; tmr<=SIM_NTIMERS; tmr++) + for (tmr=0; tmr<=SIM_NTIMERS; tmr++) { if (rtc_hz[tmr]) { /* running? */ - rtc_gtime[tmr] = sim_gtime(); /* save instruction time */ rtc_currd[tmr] = (int32)(sim_throt_cps / rtc_hz[tmr]);/* use throttle calibration */ + rtc_ticks[tmr] = rtc_hz[tmr] - 1; /* force clock calibration on next tick */ + rtc_rtime[tmr] = sim_throt_ms_start - 1000 + 1000/rtc_hz[tmr];/* adjust calibration parameters to reflect throttled rate */ + rtc_gtime[tmr] = sim_throt_inst_start - sim_throt_cps + sim_throt_cps/rtc_hz[tmr]; + rtc_nxintv[tmr] = 1000; + rtc_based[tmr] = rtc_currd[tmr]; + if (sim_clock_unit[tmr]) + sim_activate_abs (sim_clock_unit[tmr], rtc_currd[tmr]);/* reschedule next tick */ } + } } break; @@ -1803,23 +1862,48 @@ switch (sim_throt_state) { if (sim_throt_type != SIM_THROT_SPC) { /* when not dynamic throttling */ if (sim_throt_type == SIM_THROT_MCYC) /* calc desired cps */ d_cps = (double) sim_throt_val * 1000000.0; - else if (sim_throt_type == SIM_THROT_KCYC) - d_cps = (double) sim_throt_val * 1000.0; - else d_cps = (a_cps * ((double) sim_throt_val)) / 100.0; - if (fabs(100.0 * (d_cps - a_cps) / a_cps) > (double)SIM_THROT_DRIFT_PCT) { - sim_throt_wait = sim_throt_val; - sim_throt_state = SIM_THROT_STATE_TIME;/* next state to recalibrate */ - sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() Recalibrating throttle based on values a_cps = %f, d_cps = %f\n", - a_cps, d_cps); + else + if (sim_throt_type == SIM_THROT_KCYC) + d_cps = (double) sim_throt_val * 1000.0; + else + d_cps = (sim_throt_peak_cps * sim_throt_val) / 100.0; + if (fabs(100.0 * (d_cps - a_cps) / d_cps) > (double)sim_throt_drift_pct) { + sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() Recalibrating throttle based on values a_cps = %f, d_cps = %f deviating by %.2f%% from the desired value\n", + a_cps, d_cps, fabs(100.0 * (d_cps - a_cps) / d_cps)); + if ((a_cps > d_cps) && /* too fast? */ + ((100.0 * (a_cps - d_cps) / d_cps) > (100 - sim_throt_drift_pct))) { + sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() Restarting calibrating throttle going too fast: a_cps = %f, d_cps = %f deviating by %.2f%% from the desired value\n", + a_cps, d_cps, fabs(100.0 * (d_cps - a_cps) / d_cps)); + while (1) { + sim_throt_wait = (int32) /* cycles between sleeps */ + ((sim_throt_peak_cps * d_cps * ((double) sim_throt_sleep_time)) / + (1000.0 * (sim_throt_peak_cps - d_cps))); + if (sim_throt_wait >= SIM_THROT_WMIN)/* long enough? */ + break; + sim_throt_sleep_time += sim_os_sleep_inc_ms; + sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() Wait too small, increasing sleep time to %d ms. Values a_cps = %f, d_cps = %f, wait = %d\n", + sim_throt_sleep_time, sim_throt_peak_cps, d_cps, sim_throt_wait); + } + } + else { /* slow or within reasonable range */ + sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() Adjusting wait before sleep interval by %d\n", + (int32)(((d_cps - a_cps) * (double)sim_throt_wait) / d_cps)); + sim_throt_wait += (int32)(((d_cps - a_cps) * (double)sim_throt_wait) / d_cps); + } + sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() Throttle values a_cps = %f, d_cps = %f, wait = %d, sleep = %d ms\n", + a_cps, d_cps, sim_throt_wait, sim_throt_sleep_time); + sim_throt_cps = d_cps; /* save the desired rate */ + sim_throt_ms_start = sim_os_msec (); + sim_throt_inst_start = sim_gtime(); } - sim_throt_inst_start = sim_gtime(); } else { /* record instruction rate */ sim_throt_cps = (int32)a_cps; sim_debug (DBG_THR, &sim_timer_dev, "sim_throt_svc() Recalibrating Special %d/%u Cycles Per Second of %f\n", sim_throt_wait, sim_throt_sleep_time, sim_throt_cps); + sim_throt_inst_start = sim_gtime(); + sim_throt_ms_start = sim_os_msec (); } - sim_throt_ms_start = sim_os_msec (); } break; } @@ -2033,6 +2117,7 @@ pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - starting\n"); pthread_mutex_lock (&sim_timer_lock); +sim_timer_thread_running = TRUE; pthread_cond_signal (&sim_timer_startup_cond); /* Signal we're ready to go */ while (sim_asynch_timer && sim_is_running) { struct timespec start_time, stop_time; @@ -2106,6 +2191,7 @@ while (sim_asynch_timer && sim_is_running) { else {/* Something wants to adjust the queue since the wait condition was signaled */ } } +sim_timer_thread_running = FALSE; pthread_mutex_unlock (&sim_timer_lock); sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - exiting\n"); @@ -2268,7 +2354,6 @@ if (sim_asynch_timer) { pthread_attr_destroy( &attr); pthread_cond_wait (&sim_timer_startup_cond, &sim_timer_lock); /* Wait for thread to stabilize */ pthread_cond_destroy (&sim_timer_startup_cond); - sim_timer_thread_running = TRUE; } pthread_mutex_unlock (&sim_timer_lock); #endif @@ -2324,7 +2409,6 @@ if (sim_timer_thread_running) { pthread_cond_signal (&sim_timer_wake); pthread_mutex_unlock (&sim_timer_lock); pthread_join (sim_timer_thread, NULL); - sim_timer_thread_running = FALSE; /* Any wallclock queued events are now migrated to the normal event queue */ while (sim_wallclock_queue != QUEUE_LIST_END) { UNIT *uptr = sim_wallclock_queue; @@ -2478,12 +2562,14 @@ if ((sim_asynch_timer) && } if (prvptr == NULL) { /* inserting at head */ uptr->a_next = QUEUE_LIST_END; /* Temporarily mark as active */ - while (sim_wallclock_entry) { /* wait for any prior entry has been digested */ - sim_debug (DBG_TIM, &sim_timer_dev, "sim_timer_activate_after(%s, %.0f usecs) - queue insert entry %s busy waiting for 1ms\n", - sim_uname(uptr), usec_delay, sim_uname(sim_wallclock_entry)); - pthread_mutex_unlock (&sim_timer_lock); - sim_os_ms_sleep (1); - pthread_mutex_lock (&sim_timer_lock); + if (sim_timer_thread_running) { + while (sim_wallclock_entry) { /* wait for any prior entry has been digested */ + sim_debug (DBG_TIM, &sim_timer_dev, "sim_timer_activate_after(%s, %.0f usecs) - queue insert entry %s busy waiting for 1ms\n", + sim_uname(uptr), usec_delay, sim_uname(sim_wallclock_entry)); + pthread_mutex_unlock (&sim_timer_lock); + sim_os_ms_sleep (1); + pthread_mutex_lock (&sim_timer_lock); + } } sim_wallclock_entry = uptr; pthread_mutex_unlock (&sim_timer_lock); diff --git a/sim_timer.h b/sim_timer.h index c447d78..e5df4fa 100644 --- a/sim_timer.h +++ b/sim_timer.h @@ -84,17 +84,17 @@ int clock_gettime(int clock_id, struct timespec *tp); #define SIM_IDLE_STDFLT 20 /* dft sec for stability */ #define SIM_IDLE_STMAX 600 /* max sec for stability */ -#define SIM_THROT_WINIT 1000 /* cycles to skip */ -#define SIM_THROT_WST 10000 /* initial wait */ -#define SIM_THROT_WMUL 4 /* multiplier */ -#define SIM_THROT_WMIN 50 /* min wait */ -#define SIM_THROT_DRIFT_PCT 5 /* drift percentage for recalibrate */ -#define SIM_THROT_MSMIN 10 /* min for measurement */ -#define SIM_THROT_NONE 0 /* throttle parameters */ -#define SIM_THROT_MCYC 1 /* MegaCycles Per Sec */ -#define SIM_THROT_KCYC 2 /* KiloCycles Per Sec */ -#define SIM_THROT_PCT 3 /* Max Percent of host CPU */ -#define SIM_THROT_SPC 4 /* Specific periodic Delay */ +#define SIM_THROT_WINIT 1000 /* cycles to skip */ +#define SIM_THROT_WST 10000 /* initial wait */ +#define SIM_THROT_WMUL 4 /* multiplier */ +#define SIM_THROT_WMIN 50 /* min wait */ +#define SIM_THROT_DRIFT_PCT_DFLT 5 /* drift percentage for recalibrate */ +#define SIM_THROT_MSMIN 10 /* min for measurement */ +#define SIM_THROT_NONE 0 /* throttle parameters */ +#define SIM_THROT_MCYC 1 /* MegaCycles Per Sec */ +#define SIM_THROT_KCYC 2 /* KiloCycles Per Sec */ +#define SIM_THROT_PCT 3 /* Max Percent of host CPU */ +#define SIM_THROT_SPC 4 /* Specific periodic Delay */ #define SIM_THROT_STATE_INIT 0 /* Starting */ #define SIM_THROT_STATE_TIME 1 /* Checking Time */ #define SIM_THROT_STATE_THROTTLE 2 /* Throttling */ diff --git a/sim_tmxr.c b/sim_tmxr.c index 5c660ba..b93bf26 100644 --- a/sim_tmxr.c +++ b/sim_tmxr.c @@ -339,6 +339,8 @@ #include "sim_tmxr.h" #include "scp.h" +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + #include #include @@ -490,7 +492,6 @@ if (lp->txpb) { lp->txpb = NULL; } memset (lp->rbr, 0, lp->rxbsz); /* clear break status array */ -return; } @@ -561,7 +562,6 @@ if (unwritten == 0) /* buffer now empty? */ lp->xmte = 1; /* reenable transmission if paused */ lp->txcnt -= (int32)strlen (msgbuf); /* adjust statistics */ -return; } @@ -580,7 +580,6 @@ static void tmxr_report_disconnection (TMLN *lp) if (lp->notelnet) return; tmxr_linemsgf (lp, "\r\nDisconnected from the %s simulator\r\n\n", sim_name);/* report disconnection */ -return; } static int32 loop_write_ex (TMLN *lp, char *buf, int32 length, t_bool prefix_datagram) @@ -702,28 +701,28 @@ static int32 tmxr_write (TMLN *lp, int32 length) int32 written; int32 i = lp->txbpr; +if ((lp->txbps) && (sim_gtime () < lp->txnexttime) && (sim_is_running)) + return 0; + if (lp->loopback) return loop_write (lp, &(lp->txb[i]), length); if (lp->serport) { /* serial port connection? */ - if ((sim_gtime () < lp->txnexttime) && (sim_is_running)) - return 0; written = sim_write_serial (lp->serport, &(lp->txb[i]), length); - if ((written > 0) && (sim_is_running)) - lp->txnexttime = floor (sim_gtime () + (written * lp->txdelta * sim_timer_inst_per_sec ())); - return written; } else { /* Telnet connection */ written = sim_write_sock (lp->sock, &(lp->txb[i]), length); - if (written == SOCKET_ERROR) /* did an error occur? */ + if (written == SOCKET_ERROR) { /* did an error occur? */ if (lp->datagram) return written; /* ignore errors on datagram sockets */ else return -1; /* return error indication */ - else - return written; + } } +if ((written > 0) && (lp->txbps) && (sim_is_running)) + lp->txnexttime = floor (sim_gtime () + ((written * lp->txdelta * sim_timer_inst_per_sec ()) / TMXR_RX_BPS_UNIT_SCALE)); +return written; } @@ -743,7 +742,6 @@ for ( ; p < lp->rxbpi; p++) { /* work from "p" through lp->rbr[p] = 0; /* clear potential break from vacated slot */ lp->rxbpi = lp->rxbpi - 1; /* drop buffer insert index */ -return; } @@ -992,15 +990,11 @@ if (mp->last_poll_time == 0) { /* first poll initializa mp->poll_interval = TMXR_DEFAULT_CONNECT_POLL_INTERVAL; if (!(uptr->dynflags & TMUF_NOASYNCH)) { /* if asynch not disabled */ - uptr->dynflags |= UNIT_TM_POLL; /* tag as polling unit */ sim_cancel (uptr); } for (i=0; i < mp->lines; i++) { - uptr = mp->ldsc[i].uptr ? mp->ldsc[i].uptr : mp->uptr; - if (!(mp->uptr->dynflags & TMUF_NOASYNCH)) { /* if asynch not disabled */ - uptr->dynflags |= UNIT_TM_POLL; /* tag as polling unit */ - sim_cancel (uptr); + sim_cancel (mp->ldsc[i].uptr); } } } @@ -1683,9 +1677,11 @@ if ((lp->conn && lp->rcve) && /* conn & enb & */ } /* end if conn */ if (lp->rxbpi == lp->rxbpr) /* empty? zero ptrs */ lp->rxbpi = lp->rxbpr = 0; -if (lp->rxbps) { - if (val) - lp->rxnexttime = floor (sim_gtime () + ((lp->rxdelta * sim_timer_inst_per_sec ())/lp->rxbpsfactor)); +if (val) { /* Got something? */ + if (lp->rxbps) + lp->rxnexttime = floor (sim_gtime () + ((lp->rxdelta * sim_timer_inst_per_sec ()) / lp->rxbpsfactor)); + else + lp->rxnexttime = floor (sim_gtime () + ((lp->mp->uptr->wait * sim_timer_inst_per_sec ()) / TMXR_RX_BPS_UNIT_SCALE)); } tmxr_debug_return(lp, val); return val; @@ -2000,7 +1996,6 @@ for (i = 0; i < mp->lines; i++) { /* loop thru lines */ if (lp->rxbpi == lp->rxbpr) /* if buf empty, */ lp->rxbpi = lp->rxbpr = 0; /* reset pointers */ } /* end for */ -return; } @@ -2055,8 +2050,10 @@ if ((lp->txbfd && !lp->notelnet) || (TXBUF_AVAIL(lp) > 1)) {/* room for char (+ if ((TN_IAC == (u_char) chr) && (!lp->notelnet)) /* char == IAC in telnet session? */ TXBUF_CHAR (lp, TN_IAC); /* stuff extra IAC char */ TXBUF_CHAR (lp, chr); /* buffer char & adv pointer */ - if ((!lp->txbfd) && (TXBUF_AVAIL (lp) <= TMXR_GUARD))/* near full? */ - lp->xmte = 0; /* disable line */ + if (((!lp->txbfd) && + (TXBUF_AVAIL (lp) <= TMXR_GUARD)) || /* near full? */ + (lp->txbps)) /* or we're rate limiting output */ + lp->xmte = 0; /* disable line transmit until space available or character time has passed */ if (lp->txlog) { /* log if available */ extern TMLN *sim_oline; /* Make sure to avoid recursion */ TMLN *save_oline = sim_oline; /* when logging to a socket */ @@ -2066,10 +2063,11 @@ if ((lp->txbfd && !lp->notelnet) || (TXBUF_AVAIL(lp) > 1)) {/* room for char (+ sim_oline = save_oline; /* resture output socket */ } sim_exp_check (&lp->expect, chr); /* process expect rules as needed */ - if ((sim_interval > 0) && /* not called within sim_process_event? */ - (lp->txbps) && (lp->txdelta > 1000)) { /* and rate limiting output slower than 1000 cps */ + if (!sim_is_running) { /* attach message or other non simulation time message? */ tmxr_send_buffered_data (lp); /* put data on wire */ - sim_os_ms_sleep((lp->txdelta - 1000) / 1000); /* wait an approximate character delay */ + sim_os_ms_sleep(((lp->txbps) && (lp->txdelta > 1000)) ? /* rate limiting output slower than 1000 cps */ + (lp->txdelta - 1000) / 1000 : + 10); /* wait an approximate character delay */ } return SCPE_OK; /* char sent */ } @@ -2159,10 +2157,12 @@ for (i = 0; i < mp->lines; i++) { /* loop thru lines */ tmxr_rqln (lp)) _sim_activate (ruptr, 0); #endif - lp->xmte = 1; /* enable line transmit */ + if ((lp->xmte == 0) && + ((lp->txbps == 0) || + (lp->txnexttime >= sim_gtime ()))) + lp->xmte = 1; /* enable line transmit */ } } /* end for */ -return; } @@ -2900,10 +2900,11 @@ return r; Implementation note: - Only devices which poll on a unit different from the unit provided + - This routine must be called before the MUX is attached. + - Only devices which poll on a unit different from the unit provided at MUX attach time need call this function. Calling this API is - necessary for asynchronous multiplexer support and unnecessary - otherwise. + necessary for asynchronous multiplexer support and if speed limited + behaviors are desired. */ @@ -2915,7 +2916,8 @@ mp->ldsc[line].uptr = uptr_poll; return SCPE_OK; } -/* Declare which unit polls for output +/* Declare which unit performs output transmission in its unit service + routine for a particular line. Inputs: *mp = the mux @@ -2925,12 +2927,15 @@ return SCPE_OK; Outputs: none - Implementation note: + Implementation notes: - Only devices which poll on a unit different from the unit provided + - This routine must be called before the MUX is attached. + - Only devices which poll on a unit different from the unit provided at MUX attach time need call this function ABD different from the unit which polls for input. Calling this API is necessary for - asynchronous multiplexer support and unnecessary otherwise. + asynchronous multiplexer support and if speed limited behaviors are + desired. + */ @@ -3689,13 +3694,21 @@ if (!async || (uptr->flags & TMUF_NOASYNCH)) /* if asynch disabled */ #else uptr->dynflags |= TMUF_NOASYNCH; /* tag as no asynch */ #endif - +uptr->dynflags |= UNIT_TM_POLL; /* tag as polling unit */ if (mp->dptr) { for (i=0; ilines; i++) { mp->ldsc[i].expect.dptr = mp->dptr; mp->ldsc[i].expect.dbit = TMXR_DBG_EXP; mp->ldsc[i].send.dptr = mp->dptr; mp->ldsc[i].send.dbit = TMXR_DBG_SEND; + if (mp->ldsc[i].uptr == NULL) + mp->ldsc[i].uptr = mp->uptr; + mp->ldsc[i].uptr->tmxr = (void *)mp; + mp->ldsc[i].uptr->dynflags |= UNIT_TM_POLL; /* tag as polling unit */ + if (mp->ldsc[i].o_uptr == NULL) + mp->ldsc[i].o_uptr = mp->ldsc[i].uptr; + mp->ldsc[i].o_uptr->tmxr = (void *)mp; + mp->ldsc[i].o_uptr->dynflags |= UNIT_TM_POLL; /* tag as polling unit */ } } tmxr_add_to_open_list (mp); @@ -3770,6 +3783,8 @@ else { fprintf (st, " - %stelnet", lp->notelnet ? "no" : ""); if (lp->uptr && (lp->uptr != lp->mp->uptr)) fprintf (st, " - Unit: %s", sim_uname (lp->uptr)); + if (lp->o_uptr && (lp->o_uptr != lp->mp->uptr) && (lp->o_uptr != lp->uptr)) + fprintf (st, " - Output Unit: %s", sim_uname (lp->o_uptr)); if (mp->modem_control != lp->modem_control) fprintf(st, ", ModemControl=%s", lp->modem_control ? "enabled" : "disabled"); if (lp->loopback) @@ -3885,38 +3900,94 @@ uptr->filename = NULL; uptr->tmxr = NULL; mp->last_poll_time = 0; for (i=0; i < mp->lines; i++) { - UNIT *uptr = mp->ldsc[i].uptr ? mp->ldsc[i].uptr : mp->uptr; - UNIT *o_uptr = mp->ldsc[i].o_uptr ? mp->ldsc[i].o_uptr : mp->uptr; - - uptr->dynflags &= ~UNIT_TM_POLL; /* no polling */ - o_uptr->dynflags &= ~UNIT_TM_POLL; /* no polling */ + mp->ldsc[i].uptr->tmxr = NULL; + mp->ldsc[i].uptr->dynflags &= ~UNIT_TM_POLL; /* no polling */ + mp->ldsc[i].o_uptr->tmxr = NULL; + mp->ldsc[i].o_uptr->dynflags &= ~UNIT_TM_POLL; /* no polling */ } uptr->flags &= ~(UNIT_ATT); /* not attached */ uptr->dynflags &= ~(UNIT_TM_POLL|TMUF_NOASYNCH); /* no polling, not asynch disabled */ return SCPE_OK; } +static int32 _tmxr_activate_delay (UNIT *uptr, int32 interval) +{ +TMXR *mp = (TMXR *)uptr->tmxr; +int32 i, sooner = interval, due; +double sim_gtime_now = sim_gtime (); + +for (i=0; ilines; i++) { + TMLN *lp = &mp->ldsc[i]; + + if ((uptr == lp->uptr) && /* read polling unit? */ + (lp->rxbps) && /* while rate limiting? */ + (tmxr_rqln_bare (lp, FALSE))) { /* with pending input data */ + if (lp->rxnexttime > sim_gtime_now) + due = (int32)(lp->rxnexttime - sim_gtime_now); + else + due = sim_processing_event ? 1 : 0; /* avoid potential infinite loop if called from service routine */ + sooner = MIN(sooner, due); + } + if ((uptr == lp->o_uptr) && /* output completion unit? */ + (lp->txbps) && /* while rate limiting */ + (tmxr_tqln(lp))) { /* with queued output data */ + if (lp->txnexttime > sim_gtime_now) + due = (int32)(lp->txnexttime - sim_gtime_now); + else + due = sim_processing_event ? 1 : 0; /* avoid potential infinite loop if called from service routine */ + sooner = MIN(sooner, due); + } + } +return sooner; +} t_stat tmxr_activate (UNIT *uptr, int32 interval) { +int32 sooner; + if (uptr->dynflags & UNIT_TMR_UNIT) - return sim_timer_activate (uptr, interval); -#if defined(SIM_ASYNCH_MUX) -if ((!(uptr->dynflags & UNIT_TM_POLL)) || - (!sim_asynch_enabled)) { - return _sim_activate (uptr, interval); + return sim_timer_activate (uptr, interval); /* Handle the timer case */ +if (!(uptr->dynflags & UNIT_TM_POLL)) + return _sim_activate (uptr, interval); /* Handle the non mux case */ +sooner = _tmxr_activate_delay (uptr, interval); +if (sooner != interval) { + sim_debug (TIMER_DBG_MUX, &sim_timer_dev, "scheduling %s after %d instructions rather than %d instructions\n", sim_uname (uptr), sooner, interval); + return _sim_activate (uptr, sooner); /* Handle the busy case */ } +#if defined(SIM_ASYNCH_MUX) +if (!sim_asynch_enabled) + return _sim_activate (uptr, interval); return SCPE_OK; #else return _sim_activate (uptr, interval); #endif } +t_stat tmxr_activate_abs (UNIT *uptr, int32 interval) +{ +AIO_VALIDATE; /* Can't call asynchronously */ +sim_cancel (uptr); +return tmxr_activate (uptr, interval); +} + t_stat tmxr_activate_after (UNIT *uptr, uint32 usecs_walltime) { +int32 sooner; + +if (uptr->dynflags & UNIT_TMR_UNIT) + return _sim_activate_after (uptr, (double)usecs_walltime); /* Handle the timer case */ +if (!(uptr->dynflags & UNIT_TM_POLL)) + return _sim_activate_after (uptr, (double)usecs_walltime); /* Handle the non mux case */ +sooner = _tmxr_activate_delay (uptr, 0x7FFFFFFF); +if (sooner != 0x7FFFFFFF) { + if (sooner < 0) { +sooner = _tmxr_activate_delay (uptr, 0x7FFFFFFF); + } + sim_debug (TIMER_DBG_MUX, &sim_timer_dev, "scheduling %s after %d instructions rather than %u usecs\n", sim_uname (uptr), sooner, usecs_walltime); + return _sim_activate (uptr, sooner); /* Handle the busy case directly */ + } #if defined(SIM_ASYNCH_MUX) -if ((!(uptr->dynflags & UNIT_TM_POLL)) || - (!sim_asynch_enabled)) { +if (!sim_asynch_enabled) return _sim_activate_after (uptr, (double)usecs_walltime); } return SCPE_OK; @@ -3927,18 +3998,10 @@ return _sim_activate_after (uptr, (double)usecs_walltime); t_stat tmxr_activate_after_abs (UNIT *uptr, uint32 usecs_walltime) { -#if defined(SIM_ASYNCH_MUX) -if ((!(uptr->dynflags & UNIT_TM_POLL)) || - (!sim_asynch_enabled)) { - return _sim_activate_after_abs (uptr, (double)usecs_walltime); - } -return SCPE_OK; -#else -return _sim_activate_after_abs (uptr, (double)usecs_walltime); -#endif +sim_cancel (uptr); +return tmxr_activate_after (uptr, usecs_walltime); } - t_stat tmxr_clock_coschedule (UNIT *uptr, int32 interval) { int32 tmr = sim_rtcn_calibrated_tmr (); @@ -3953,45 +4016,27 @@ sim_cancel (uptr); return tmxr_clock_coschedule (uptr, interval); } -#define MIN(a,b) (((a) < (b)) ? (a) : (b)) - t_stat tmxr_clock_coschedule_tmr (UNIT *uptr, int32 tmr, int32 ticks) { -TMXR *mp = (TMXR *)uptr->tmxr; int32 interval = ticks * sim_rtcn_tick_size (tmr); +int32 sooner; +if (uptr->dynflags & UNIT_TMR_UNIT) + return sim_clock_coschedule_tmr (uptr, tmr, ticks); /* Handle the timer case */ +if (!(uptr->dynflags & UNIT_TM_POLL)) + return sim_clock_coschedule_tmr (uptr, tmr, ticks); /* Handle the non mux case */ +sooner = _tmxr_activate_delay (uptr, interval); +if (sooner != interval) { + sim_debug (TIMER_DBG_MUX, &sim_timer_dev, "scheduling %s after %d instructions rather than %d ticks (%d instructions)\n", sim_uname (uptr), sooner, ticks, interval); + return _sim_activate (uptr, sooner); /* Handle the busy case directly */ + } #if defined(SIM_ASYNCH_MUX) -if ((!(uptr->dynflags & UNIT_TM_POLL)) || - (!sim_asynch_enabled)) { +if (!sim_asynch_enabled) { + sim_debug (TIMER_DBG_MUX, &sim_timer_dev, "coscheduling %s after interval %d ticks\n", sim_uname (uptr), ticks); return sim_clock_coschedule (uptr, tmr, ticks); } return SCPE_OK; #else -if (mp) { - int32 i, soon = interval; - double sim_gtime_now = sim_gtime (); - - for (i = 0; i < mp->lines; i++) { - TMLN *lp = &mp->ldsc[i]; - - if (tmxr_rqln_bare (lp, FALSE)) { - int32 due; - - if (lp->rxbps) - if (lp->rxnexttime > sim_gtime_now) - due = (int32)(lp->rxnexttime - sim_gtime_now); - else - due = sim_processing_event ? 1 : 0; /* avoid potential infinite loop if called from service routine */ - else - due = (int32)((uptr->wait * sim_timer_inst_per_sec ())/TMXR_RX_BPS_UNIT_SCALE); - soon = MIN(soon, due); - } - } - if (soon != interval) { - sim_debug (TIMER_DBG_MUX, &sim_timer_dev, "scheduling %s after %d instructions\n", sim_uname (uptr), soon); - return _sim_activate (uptr, soon); - } - } sim_debug (TIMER_DBG_MUX, &sim_timer_dev, "coscheduling %s after interval %d ticks\n", sim_uname (uptr), ticks); return sim_clock_coschedule_tmr (uptr, tmr, ticks); #endif @@ -4224,7 +4269,6 @@ void tmxr_msg (SOCKET sock, const char *msg) { if ((sock) && (sock != INVALID_SOCKET)) sim_write_sock (sock, msg, (int32)strlen (msg)); -return; } @@ -4238,7 +4282,6 @@ while (*msg) { sim_os_ms_sleep (10); ++msg; } -return; } @@ -4299,7 +4342,6 @@ for (i = 0; i < len; ++i) { } if (buf != stackbuf) free (buf); -return; } @@ -4368,7 +4410,6 @@ if (lp->expect.buf) sim_exp_showall (st, &lp->expect); if (lp->txlog) fprintf (st, " Logging to %s\n", lp->txlogname); -return; } @@ -4398,6 +4439,15 @@ else { fprintf (st, " packet data queued/packets sent = %d/%d", tmxr_tpqln (lp), lp->txpcnt); fprintf (st, "\n"); + if ((lp->rxbps) || (lp->txbps)) { + if ((lp->rxbps == lp->txbps)) + fprintf (st, " speed = %u", lp->rxbps); + else + fprintf (st, " speed = %u/%u", lp->rxbps, lp->txbps); + if (lp->rxbpsfactor / TMXR_RX_BPS_UNIT_SCALE > 1.0) + fprintf (st, "*%.0f", lp->rxbpsfactor / TMXR_RX_BPS_UNIT_SCALE); + fprintf (st, " bps\n"); + } } if (lp->txbfd) fprintf (st, " output buffer size = %d\n", lp->txbsz); @@ -4408,7 +4458,6 @@ if (lp->txdrp) fprintf (st, " dropped = %d\n", lp->txdrp); if (lp->txstall) fprintf (st, " stalled = %d\n", lp->txstall); -return; } diff --git a/sim_tmxr.h b/sim_tmxr.h index 385fd34..e7597f1 100644 --- a/sim_tmxr.h +++ b/sim_tmxr.h @@ -279,6 +279,7 @@ t_stat tmxr_show_cstat (FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat tmxr_show_lines (FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat tmxr_show_open_devices (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, CONST char* desc); t_stat tmxr_activate (UNIT *uptr, int32 interval); +t_stat tmxr_activate_abs (UNIT *uptr, int32 interval); t_stat tmxr_activate_after (UNIT *uptr, uint32 usecs_walltime); t_stat tmxr_activate_after_abs (UNIT *uptr, uint32 usecs_walltime); t_stat tmxr_clock_coschedule (UNIT *uptr, int32 interval); @@ -314,6 +315,7 @@ void _tmxr_debug (uint32 dbits, TMLN *lp, const char *msg, char *buf, int bufsiz #endif #if (!defined(NOT_MUX_USING_CODE)) #define sim_activate tmxr_activate +#define sim_activate_abs tmxr_activate_abs #define sim_activate_after tmxr_activate_after #define sim_activate_after_abs tmxr_activate_after_abs #define sim_clock_coschedule tmxr_clock_coschedule