Support for VT100 dot stretching

This commit is contained in:
Jeff Parsons 2016-08-08 11:19:06 -07:00
commit f7464dd182
7 changed files with 4890 additions and 4701 deletions

View file

@ -100,6 +100,12 @@ function ChipSet(parmsChipSet)
Component.subclass(ChipSet);
/*
* NOTE: The STATUS1 port could have been handled entirely by the Keyboard component, but it was just as easy
* to create a simple ChipSet interface, updateStatus1(), that the Keyboard calls whenever it wants to simulate a
* button press or release. It's a six-of-one, half-a-dozen-of-another choice, since technically, Space Invaders
* doesn't have a keyboard.
*/
ChipSet.SI1978 = {
MODEL: 1978.1,
STATUS0: { // NOTE: STATUS0 not used by the SI1978 ROMs; refer to STATUS1 instead
@ -168,8 +174,33 @@ ChipSet.SI1978 = {
*
* <cpu id="cpu8080" model="8080" cycles="2764800"/>
*
* where 2764800 = 24883200 / 9. Beyond that, we don't really care about that particular 8224. I only mention it
* because knowing the CPU frequency is helpful for simulating some of the other circuits below that we DO care about.
* where 2764800 = 24883200 / 9. You need to know this because we rely on the CPU frequency for simulating some
* of the other VT100 circuits.
*
* For reference, here is a list of all the VT100 I/O ports, from /devices/pc8080/machine/vt100/debugger/README.md,
* which in turn comes from p. 4-17 of the VT100 Technical Manual (July 1982):
*
* READ OR WRITE
* 00H PUSART data bus
* 01H PUSART command port
*
* WRITE ONLY (Decoded with I/O WR L)
* 02H Baud rate generator
* 42H Brightness D/A latch
* 62H NVR latch
* 82H Keyboard UART data input [used to update the Keyboard Status Byte -JP]
* A2H Video processor DC012
* C2H Video processor DC011
* E2H Graphics port
*
* READ ONLY (Decoded with I/O RD L)
* 22H Modem buffer
* 42H Flags buffer
* 82H Keyboard UART data output
*
* Most of these are handled by the ChipSet component, since it exists as sort of a "catch-all" component,
* but some are more appropriately handled by other components; eg, port 0x82 is handled by the Keyboard component,
* so it's defined there instead of here.
*/
ChipSet.VT100 = {
MODEL: 100.0,
@ -202,13 +233,65 @@ ChipSet.VT100 = {
STANDBY: 0x7
},
WORDMASK: 0x3fff // NVR words are 14-bit
/*
* The Technical Manual, p. 4-18, also notes that "Early VT100s can disable the receiver interrupt by
* programming D4 in the NVR latch. However, this is never used by the VT100."
*/
},
/*
* DC012 is referred to as a Control Chip.
*
* As p. 4-67 (117) of the VT100 Technical Manual (July 1982) explains:
*
* The DCO12 performs three main functions.
*
* 1. Scan count generation. This involves two counters, a multiplexer to switch between the counters,
* double-height logic, scroll and line attribute latches, and various logic controlling switching between
* the two counters. This is the biggest part of the chip. It includes all scrolling, double-height logic,
* and feeds into the underline and hold request circuits.
*
* 2. Generation of HOLD REQUEST. This uses information from the scan counters and the scrolling logic to
* decide when to generate HOLD REQUEST.
*
* 3. Video modifications: dot stretching, blanking, addition of attributes to video outputs, and multiple
* intensity levels.
*
* The input decoder accepts a 4-bit command from the microprocessor when VID WR 2 L is asserted. Table 4-6-2
* lists the commands.
*
* D3 D2 D1 D0 Function
* -- -- -- -- --------
* 0 0 0 0 Load low order scroll latch = 00
* 0 0 0 1 Load low order scroll latch = 01
* 0 0 1 0 Load low order scroll latch = 10
* 0 0 1 1 Load low order scroll latch = 11
*
* 0 1 0 0 Load high order scroll latch = 00
* 0 1 0 1 Load high order scroll latch = 01
* 0 1 1 0 Load high order scroll latch = 10
* 0 1 1 1 Load high order scroll latch = 11 (not used)
*
* 1 0 0 0 Toggle blink flip-flop
* 1 0 0 1 Clear vertical frequency interrupt
*
* 1 0 1 0 Set reverse field on
* 1 0 1 1 Set reverse field off
*
* 1 1 0 0 Set basic attribute to underline*
* 1 1 0 1 Set basic attribute to reverse video*
* 1 1 1 0 Reserved for future specification*
* 1 1 1 1 Reserved for future specification*
*
* *These functions also clear blink flip-flop.
*/
DC012: { // generates scan counts for the Video Processor
PORT: 0xA2, // write-only
INIT: 0x00 // for lack of a better guess
},
/*
* As p. 4-55 (105) of the July 1982 Technical Manual explains:
* DC011 is referred to as a Timing Chip.
*
* As p. 4-55 (105) of the VT100 Technical Manual (July 1982) explains:
*
* The DCO11 is a custom designed bipolar circuit that provides most of the timing signals required by the
* video processor. Internal counters divide the output of a 24.0734 MHz oscillator (located elsewhere on the

View file

@ -102,8 +102,7 @@ Keyboard.ASCII = {
* keyCodes for most common ASCII keys can simply use the appropriate ASCII code above.
*
* Most of these represent non-ASCII keys (eg, the LEFT arrow key), yet for some reason, browsers defined
* them using ASCII codes (eg, the LEFT arrow key uses the ASCII code for '%' or 37). This conflict is
* discussed further in the definition of CLICKCODE below.
* them using ASCII codes (eg, the LEFT arrow key uses the ASCII code for '%' or 37).
*
* @enum {number}
*/
@ -286,7 +285,18 @@ Keyboard.VT100 = {
LED1: 0x08,
LOCKED: 0x10,
ONLINE: 0x20,
LEDS: 0x3F, // all LEDs
START: 0x40, // set to initiate a scan
/*
* From p. 4-38 of the VT100 Technical Manual (July 1982):
*
* A bit (CLICK) in the keyboard status word controls the bell.... When a single status word contains
* the bell bit, flip-flop E3 toggles and turns on E1, generating a click. If the bell bit is set for
* many words in succession, the UART latch holds the data output constant..., allowing the circuit to
* produce an 800 hertz tone. Bell is generated by setting the bell bit for 0.25 seconds. Each cycle of
* the tone is at a reduced amplitude compared with the single keyclick.... The overall effect of the
* tone burst on the ear is that of a beep.
*/
CLICK: 0x80,
INIT: 0x00
}
@ -573,7 +583,7 @@ Keyboard.prototype.save = function()
case Keyboard.SI1978.MODEL:
break;
case Keyboard.VT100.MODEL:
state.set(0, [this.bLEDs]);
state.set(0, [this.bVT100Status]);
break;
}
return state.data();
@ -597,8 +607,8 @@ Keyboard.prototype.restore = function(data)
return true;
case Keyboard.VT100.MODEL:
this.bLEDs = a[0];
this.updateLEDs();
this.bVT100Status = a[0];
this.updateLEDs(this.bVT100Status & Keyboard.VT100.STATUS.LEDS);
return true;
}
}
@ -621,20 +631,22 @@ Keyboard.prototype.setLED = function(control, f)
};
/**
* updateLEDs()
* updateLEDs(bLEDs)
*
* @this {Keyboard}
* @param {number} bLEDs
*/
Keyboard.prototype.updateLEDs = function()
Keyboard.prototype.updateLEDs = function(bLEDs)
{
this.bLEDs = bLEDs;
for (var sBinding in this.config.LEDCODES) {
var id = "led-" + sBinding;
var control = this.bindings[id];
if (control) {
var bitLED = this.config.LEDCODES[sBinding];
var fOn = !!(this.bLEDs & bitLED);
var fOn = !!(bLEDs & bitLED);
if (bitLED & (bitLED-1)) {
fOn = !(this.bLEDs & ~bitLED);
fOn = !(bLEDs & ~bitLED);
}
this.setLED(control, fOn);
}
@ -783,8 +795,13 @@ Keyboard.prototype.checkSoftKeysToRelease = function()
Keyboard.prototype.outVT100UARTStatus = function(port, b, addrFrom)
{
this.printMessageIO(port, b, addrFrom, "KBDUART.STATUS", null, true);
this.bLEDs = b;
this.updateLEDs();
this.bVT100Status = b;
this.updateLEDs(b & Keyboard.VT100.STATUS.LEDS);
/*
if (b & Keyboard.VT100.STATUS.START) {
console.log("keyboard scan initiated");
}
*/
};
/*

View file

@ -117,6 +117,7 @@ function Video(parmsVideo, canvas, context, textarea, container)
this.cxCellDefault = this.cxCell = parmsVideo['cellWidth'] || 1;
this.cyCellDefault = this.cyCell = parmsVideo['cellHeight'] || 1;
this.abFontData = null;
this.fDotStretcher = false;
this.nBitsPerPixel = parmsVideo['bufferBits'] || 1;
this.iBitFirstPixel = parmsVideo['bufferLeft'] || 0;
@ -495,6 +496,7 @@ Video.prototype.createFonts = function(abFontData)
* We retain abFontData in case we have to rebuild the fonts (eg, when we switch from 80 to 132 columns)
*/
this.abFontData = abFontData;
this.fDotStretcher = (this.nFormat == Video.FORMAT.VT100);
this.aFonts[Video.VT100.FONT.NORML] = [
this.createFontVariation(this.cxCell, this.cyCell),
this.createFontVariation(this.cxCell, this.cyCell, this.fUnderline)
@ -561,6 +563,7 @@ Video.prototype.createFontVariation = function(cxCell, cyCell, fUnderline)
var offFontData = iChar * nFontBytesPerChar + ((nFontByteOffset + y) & (nFontBytesPerChar - 1));
var bits = (fUnderline && y == 8? 0xff : this.abFontData[offFontData]);
for (var nRows = 0; nRows < (cyCell / this.cyCell); nRows++) {
var bitPrev = 0;
for (var x = 0, xDst = x; x < this.cxCell; x++) {
/*
* While x goes from 0 to cxCell-1, obviously we will run out of bits after x is 7;
@ -568,12 +571,14 @@ Video.prototype.createFontVariation = function(cxCell, cyCell, fUnderline)
* (so that line-drawing characters seamlessly connect), we ensure that the effective
* shift count remains stuck at 7 once it reaches 7.
*/
var bit = bits & (0x80 >> (x > 7? 7 : x));
var bitReal = bits & (0x80 >> (x > 7? 7 : x));
var bit = (this.fDotStretcher && !bitReal && bitPrev)? bitPrev : bitReal;
for (var nCols = 0; nCols < (cxCell / this.cxCell); nCols++) {
if (fReverse) bit = !bit;
this.setPixel(imageChar, xDst, yDst, bit? 1 : 0);
xDst++;
}
bitPrev = bitReal;
}
yDst++;
}