3843 lines
156 KiB
C
3843 lines
156 KiB
C
/*
|
||
* $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 <phil@ultimate.com>
|
||
* 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 <stdio.h>
|
||
#endif
|
||
#include <string.h> /* memset */
|
||
#ifndef NO_CONIC_OPT
|
||
#include <math.h> /* 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 <stdio.h>
|
||
|
||
#if defined(__cplusplus)
|
||
extern "C" {
|
||
#endif
|
||
#define DEVICE void
|
||
|
||
#define DBG_CALL 1
|
||
int vt11_debug;
|
||
|
||
#if defined(VM_PDP11)
|
||
extern void _sim_debug_device (unsigned int dbits, DEVICE* dptr, const char* fmt, ...);
|
||
|
||
#define DEBUGF(...) _sim_debug_device (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 int32 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
|
||
|
||
int32 vt11_csp_w = VT11_CSP_W; /* horizontal character spacing */
|
||
int32 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}(<saved params>) 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
|
||
*/
|
||
|
||
/*
|
||
The following code is commented out here and written more
|
||
compactly below since tPEn, tPEd, tPLn and tPLd are constants.
|
||
|
||
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;
|
||
}
|
||
}
|
||
*/
|
||
|
||
if (rdx < 0) {
|
||
if (x0 <= 0 && x0 >= rdx) /* x0 not positive but less negative than rdx? */
|
||
tPEn = x0, tPEd = rdx;
|
||
} else {
|
||
if ((x0 >= 0 && x0 <= rdx) && /* x0 not negative but less than or equal to rdx */
|
||
(rdx != 0)) /* and rdx not zero */
|
||
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);
|
||
/* Fallthrough */
|
||
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 = 0;
|
||
int32 adx = ABS(dx), ady = ABS(dy);
|
||
if (adx >= ady) {
|
||
if (dx)
|
||
tangent = 010000L * dy / dx; /* signed */
|
||
lp_ypos = y0 + tangent * (lp_xpos - x0) / 010000L;
|
||
if (dx)
|
||
tangent = 010000L * dz / dx;
|
||
lp_zpos = z0 + tangent * (lp_xpos - x0) / 010000L;
|
||
} else {
|
||
if (dy)
|
||
tangent = 010000L * dx / dy; /* signed */
|
||
lp_xpos = x0 + tangent * (lp_ypos - y0) / 010000L;
|
||
if (dy)
|
||
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 = 0;
|
||
int compress = vt11_csp_w <= 12 && x == 2;
|
||
int dot = col & (1<<y), nxtdot = 0;
|
||
|
||
if (dot) {
|
||
illum2(xbase + xllc, ybase + yllc);
|
||
if (!compress || (nxtdot = nxtcol & (1<<y)) == 0)
|
||
illum2(xbase + xllc + 1, ybase + yllc);
|
||
}
|
||
if (italics) {
|
||
delay_skew = 0;
|
||
if ((y % 3) != 0
|
||
&& !(delay_skew = ((prvcol & (3<<y))>>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 */
|