Merge branch 'next-release'

This commit is contained in:
Jeff Parsons 2016-08-15 15:00:48 -07:00
commit c349a56218
13 changed files with 962 additions and 559 deletions

View file

@ -235,24 +235,52 @@ ChipSet.VT100 = {
PORT: 0x42, // write-only
INIT: 0x00 // for lack of a better guess
},
NVR: {
LATCH: {
PORT: 0x62 // write-only
},
CMD: {
ACCEPT_DATA: 0x0,
ACCEPT_ADDR: 0x1,
SHIFT_OUT: 0x2,
WRITE: 0x4,
ERASE: 0x5,
READ: 0x6,
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."
*/
/*
* 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
* terminal controller module) into the lower frequencies that define dot, character, scan, and frame timing.
* The counters are programmable through various input pins to control the number of characters per line,
* the frequency at which the screen is refreshed, and whether the display is interlaced or noninterlaced.
* These parameters can be controlled through SET-UP mode or by the host.
*
* Table 4-6-1: Video Mode Selection (Write Address 0xC2)
*
* D5 D4 Configuration
* -- -- -------------
* 0 0 80-column mode, interlaced
* 0 1 132-column mode, interlaced
* 1 0 60Hz, non-interlaced
* 1 1 50Hz, non-interlaced
*
* On p. 4-56, the DC011 Block Diagram shows 8 outputs labeled LBA0 through LBA7. From p. 4-61:
*
* Several of the LBAs are used as general purpose clocks in the VT100. LBA 3 and LBA 4 are used to generate
* timing for the keyboard. These signals satisfy the keyboard's requirement of two square-waves, one twice the
* frequency of the other, even though every 16th transition is delayed (the second stage of the horizontal
* counter divides by 17, not 16). LBA 7 is used by the nonvolatile RAM.
*
* And on p. 4-62, timings are provided for the LBA0 through LBA7 when the VT100 is in 80-column mode; in particular:
*
* LBA6: 16.82353us (when LBA6 is low, for a period of 33.64706us)
* LBA7: 31.77778us (when LBA7 is high, for a period of 63.55556us)
*
* If we assume that the CPU cycle count increments once every 361.69ns, it will increment roughly 88 times every
* time LBA7 toggles. So we can divide the CPU cycle count by 88 and set LBA to the low bit of that truncated
* result. An even faster (but less accurate) solution would be to mask bit 6 of the CPU cycle count, which will
* doesn't change until the count has been incremented 64 times. See getVT100LBA() for the chosen implementation.
*/
DC011: { // generates Line Buffer Addresses (LBAs) for the Video Processor
PORT: 0xC2, // write-only
COLS80: 0x00,
COLS132: 0x10,
RATE60: 0x20,
RATE50: 0x30,
INITCOLS: 0x00, // ie, COLS80
INITRATE: 0x20 // ie, RATE60
},
/*
* DC012 is referred to as a Control Chip.
@ -302,40 +330,33 @@ ChipSet.VT100 = {
*/
DC012: { // generates scan counts for the Video Processor
PORT: 0xA2, // write-only
INIT: 0x00 // for lack of a better guess
SCROLL_LO: 0x00,
INITSCROLL: 0x00,
INITBLINK: 0x00,
INITREVERSE:0x00,
INITATTR: 0x00
},
/*
* 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
* terminal controller module) into the lower frequencies that define dot, character, scan, and frame timing.
* The counters are programmable through various input pins to control the number of characters per line,
* the frequency at which the screen is refreshed, and whether the display is interlaced or noninterlaced.
* These parameters can be controlled through SET-UP mode or by the host.
*
* On p. 4-56, the DC011 Block Diagram shows 8 outputs labeled LBA0 through LBA7. From p. 4-61:
*
* Several of the LBAs are used as general purpose clocks in the VT100. LBA 3 and LBA 4 are used to generate
* timing for the keyboard. These signals satisfy the keyboard's requirement of two square-waves, one twice the
* frequency of the other, even though every 16th transition is delayed (the second stage of the horizontal
* counter divides by 17, not 16). LBA 7 is used by the nonvolatile RAM.
*
* And on p. 4-62, timings are provided for the LBA0 through LBA7 when the VT100 is in 80-column mode; in particular:
*
* LBA6: 16.82353us (when LBA6 is low, for a period of 33.64706us)
* LBA7: 31.77778us (when LBA7 is high, for a period of 63.55556us)
*
* If we assume that the CPU cycle count increments once every 361.69ns, it will increment roughly 88 times every
* time LBA7 toggles. So we can divide the CPU cycle count by 88 and set LBA to the low bit of that truncated
* result. An even faster (but less accurate) solution would be to mask bit 6 of the CPU cycle count, which will
* doesn't change until the count has been incremented 64 times. See getVT100LBA() for the chosen implementation.
* ER1400 Non-Volatile RAM (NVR) Chip Definitions
*/
DC011: { // generates Line Buffer Addresses (LBAs) for the Video Processor
PORT: 0xC2, // write-only
INIT: 0x00 // for lack of a better guess
NVR: {
LATCH: {
PORT: 0x62 // write-only
},
CMD: {
ACCEPT_DATA: 0x0,
ACCEPT_ADDR: 0x1,
SHIFT_OUT: 0x2,
WRITE: 0x4,
ERASE: 0x5,
READ: 0x6,
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."
*/
}
};
@ -402,7 +423,8 @@ ChipSet.prototype.initBus = function(cmp, bus, cpu, dbg)
this.cpu = cpu;
this.dbg = dbg;
this.cmp = cmp;
this.kbd = cmp.getMachineComponent("Keyboard");
this.kbd = /** @type {Keyboard} */ (cmp.getMachineComponent("Keyboard"));
this.video = /** @type {Video} */ (cmp.getMachineComponent("Video"));
bus.addPortInputTable(this, this.config.portsInput);
bus.addPortOutputTable(this, this.config.portsOutput);
};
@ -452,9 +474,17 @@ ChipSet.SI1978.INIT = [
ChipSet.VT100.INIT = [
[
ChipSet.VT100.BRIGHTNESS.INIT,
ChipSet.VT100.FLAGS_BUFFER.NO_AVO | ChipSet.VT100.FLAGS_BUFFER.NO_GFX,
ChipSet.VT100.DC012.INIT,
ChipSet.VT100.DC011.INIT
ChipSet.VT100.FLAGS_BUFFER.NO_AVO | ChipSet.VT100.FLAGS_BUFFER.NO_GFX
],
[
ChipSet.VT100.DC011.INITCOLS,
ChipSet.VT100.DC011.INITRATE
],
[
ChipSet.VT100.DC012.INITSCROLL,
ChipSet.VT100.DC012.INITBLINK,
ChipSet.VT100.DC012.INITREVERSE,
ChipSet.VT100.DC012.INITATTR
],
[
0, 0, 0, 0,
@ -501,8 +531,10 @@ ChipSet.prototype.save = function()
state.set(0, [this.bStatus0, this.bStatus1, this.bStatus2, this.wShiftData, this.bShiftCount, this.bSound1, this.bSound2]);
break;
case ChipSet.VT100.MODEL:
state.set(0, [this.bBrightness, this.bFlagsBuffer, this.bDC012, this.bDC011]);
state.set(1, [this.dNVRAddr, this.wNVRData, this.bNVRLatch, this.bNVROut, this.aNVRWords]);
state.set(0, [this.bBrightness, this.bFlagsBuffer]);
state.set(1, [this.bDC011Cols, this.bDC011Rate]);
state.set(2, [this.bDC012Scroll, this.bDC012Blink, this.bDC012Reverse, this.bDC012Attr]);
state.set(3, [this.dNVRAddr, this.wNVRData, this.bNVRLatch, this.bNVROut, this.aNVRWords]);
break;
}
return state.data();
@ -534,9 +566,15 @@ ChipSet.prototype.restore = function(data)
case ChipSet.VT100.MODEL:
this.bBrightness = a[0];
this.bFlagsBuffer = a[1];
this.bDC012 = a[2];
this.bDC011 = a[3];
a = data[1];
this.bDC011Cols = a[0];
this.bDC011Rate = a[1];
a = data[2];
this.bDC012Scroll = a[0];
this.bDC012Blink = a[1];
this.bDC012Reverse = a[2];
this.bDC012Attr = a[3];
a = data[3];
this.dNVRAddr = a[0]; // 20-bit address
this.wNVRData = a[1]; // 14-bit word
this.bNVRLatch = a[2]; // 1 byte
@ -909,9 +947,8 @@ ChipSet.prototype.outVT100NVRLatch = function(port, b, addrFrom)
/**
* outVT100DC012(port, b, addrFrom)
*
* TODO: Consider whether we should disable any interrupts (eg, vertical retrace) until the
* this port is initialized at runtime. We initialize it ourselves at start-up, but our initial
* value is just a guess.
* TODO: Consider whether we should disable any interrupts (eg, vertical retrace) until
* this port is initialized at runtime.
*
* @this {ChipSet}
* @param {number} port (0xA2)
@ -921,7 +958,34 @@ ChipSet.prototype.outVT100NVRLatch = function(port, b, addrFrom)
ChipSet.prototype.outVT100DC012 = function(port, b, addrFrom)
{
this.printMessageIO(port, b, addrFrom, "DC012");
this.bDC012 = b;
var bOpt = b & 0x3;
var bCmd = (b >> 2) & 0x3;
switch(bCmd) {
case 0x0:
this.bDC012Scroll = (this.bDC012Scroll & ~0x3) | bOpt;
break;
case 0x1:
this.bDC012Scroll = (this.bDC012Scroll & ~0xC) | (bOpt << 2);
break;
case 0x2:
switch(bOpt) {
case 0x0:
this.bDC012Blink = ~this.bDC012Blink;
break;
case 0x1:
// TODO: Clear vertical frequency interrupt
break;
case 0x2:
case 0x3:
this.bDC012Reverse = 0x3 - bOpt;
break;
}
break;
case 0x3:
this.bDC012Attr = bOpt;
break;
}
};
/**
@ -935,7 +999,25 @@ ChipSet.prototype.outVT100DC012 = function(port, b, addrFrom)
ChipSet.prototype.outVT100DC011 = function(port, b, addrFrom)
{
this.printMessageIO(port, b, addrFrom, "DC011");
this.bDC011 = b;
if (b & ChipSet.VT100.DC011.RATE60) {
b &= ChipSet.VT100.DC011.RATE50;
if (this.bDC011Rate != b) {
this.bDC011Rate = b;
if (this.video) {
this.video.updateRate(this.bDC011Rate == ChipSet.VT100.DC011.RATE50? 50 : 60);
}
}
} else {
b &= ChipSet.VT100.DC011.COLS132;
if (this.bDC011Cols != b) {
this.bDC011Cols = b;
if (this.video) {
var nCols = (this.bDC011Cols == ChipSet.VT100.DC011.COLS132? 132 : 80);
var nRows = (nCols > 80 && (this.bFlagsBuffer & ChipSet.VT100.FLAGS_BUFFER.NO_AVO)? 14 : 24);
this.video.updateDimensions(nCols, nRows);
}
}
}
};
/*

View file

@ -176,13 +176,13 @@ CPU.prototype.initBus = function(cmp, bus, cpu, dbg)
/*
* We need to know the refresh rate (and corresponding interrupt rate, if any) of the Video component.
*/
var video = cmp.getMachineComponent("Video");
var video = /** @type {Video} */ (cmp.getMachineComponent("Video"));
this.refreshRate = video && video.getRefreshRate() || CPU.VIDEO_UPDATES_PER_SECOND;
/*
* Attach the ChipSet component to the CPU so that it can be notified whenever the CPU stops and starts.
*/
this.chipset = cmp.getMachineComponent("ChipSet");
this.chipset = /** @type {ChipSet} */ (cmp.getMachineComponent("ChipSet"));
/*
* We've already saved the parmsCPU 'autoStart' setting, but there may be a machine (or URL) override.

View file

@ -72,9 +72,9 @@ Component.subclass(Keyboard);
/**
* Alphanumeric and other common (printable) ASCII codes.
*
* TODO: Determine what we can do to get ALL constants like these inlined (enum doesn't seem to
* get the job done); the problem seems to be limited to property references that use quotes, which
* is why I've 'unquoted' as many of them as possible.
* TODO: Determine what we can do to get ALL constants like these inlined by the Closure Compiler
* (enum doesn't seem to get the job done); the problem seems to be limited to property references
* that use quotes, which is why I've 'unquoted' as many of them as possible.
*
* @enum {number}
*/
@ -101,8 +101,8 @@ 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).
* Most of these represent non-ASCII characters (eg, the LEFT arrow key), yet for some reason, browsers
* defined them using ASCII codes (eg, the LEFT arrow key uses 37, which is the ASCII code for '%').
*
* @enum {number}
*/
@ -282,13 +282,15 @@ Keyboard.VT100 = {
KEYMAP: {},
ALTCODES: {},
LEDCODES: {},
SOFTCODES: {},
SOFTCODES: {
'setup': Keyboard.KEYCODE.F9
},
/*
* Reading port 0x82 returns a key address from the VT100 keyboard's UART data output.
*
* Every time a keyboard scan is initiated (by setting the START bit of the status byte),
* an internal address index is reset to zero, and an interrupt is generated for each entry
* in the aKeysActive array, along with a final interrupt for KEYLAST.
* our internal address index (iKeyNext) is set to zero, and an interrupt is generated for
* each entry in the aKeysActive array, along with a final interrupt for KEYLAST.
*/
ADDRESS: {
PORT: 0x82,
@ -320,9 +322,12 @@ Keyboard.VT100 = {
CLICK: 0x80,
INIT: 0x00
},
KEYLAST: 0x7F
KEYLAST: 0x7F // special end-of-scan key address (all valid key addresses are < KEYLAST)
};
/*
* Table to map host key codes to VT100 key addresses (ie, unique 7-bit values representing key positions on the VT100)
*/
Keyboard.VT100.KEYMAP[Keyboard.KEYCODE.DEL] = 0x03;
Keyboard.VT100.KEYMAP[Keyboard.ASCII.P] = 0x05;
Keyboard.VT100.KEYMAP[Keyboard.ASCII.O] = 0x06;
@ -486,16 +491,25 @@ Keyboard.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
default:
if (this.config.SOFTCODES && this.config.SOFTCODES[sBinding] !== undefined) {
this.bindings[id] = control;
var fnDown = function(kbd, sSoftCode) {
return function onMouseOrTouchDownKeyboard(event) {
kbd.onSoftKeyDown(sSoftCode, true);
var fnDown = function(kbd, softCode) {
return function onKeyboardBindingDown(event) {
kbd.onSoftKeyDown(softCode, true);
/*
* I'm assuming we only need to give focus back on the "up" event...
*
* if (kbd.cmp) kbd.cmp.updateFocus();
*/
};
}(this, sBinding);
var fnUp = function (kbd, sSoftCode) {
return function onMouseOrTouchUpKeyboard(event) {
kbd.onSoftKeyDown(sSoftCode, false);
}(this, this.config.SOFTCODES[sBinding]);
var fnUp = function (kbd, softCode) {
return function onKeyboardBindingUp(event) {
kbd.onSoftKeyDown(softCode, false);
/*
* Give focus back to the machine (since clicking the button takes focus away).
*/
if (kbd.cmp) kbd.cmp.updateFocus();
};
}(this, sBinding);
}(this, this.config.SOFTCODES[sBinding]);
if ('ontouchstart' in window) {
control.ontouchstart = fnDown;
control.ontouchend = fnUp;
@ -522,9 +536,10 @@ Keyboard.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
*/
Keyboard.prototype.initBus = function(cmp, bus, cpu, dbg)
{
this.cmp = cmp;
this.cpu = cpu;
this.dbg = dbg; // NOTE: The "dbg" property must be set for the message functions to work
this.chipset = cmp.getMachineComponent("ChipSet");
this.chipset = /** @type {ChipSet} */ (cmp.getMachineComponent("ChipSet"));
bus.addPortInputTable(this, this.config.portsInput);
bus.addPortOutputTable(this, this.config.portsOutput);
};
@ -873,8 +888,8 @@ Keyboard.prototype.checkBusy = function()
*
* We take our cue from iKeyNext. If it's -1 (default), we simply return the last value latched
* in bVT100Address. Otherwise, if iKeyNext is a valid index into aKeysActive, we look up the key
* in the VT100.KEYMAP, latch it, and increment iKeyNext, else we latch Keyboard.VT100.KEYLAST
* and set iKeyNext to -1 again.
* in the VT100.KEYMAP, latch it, and increment iKeyNext. Failing that, we latch Keyboard.VT100.KEYLAST
* and reset iKeyNext to -1.
*
* @this {Keyboard}
* @param {number} port (0x82)
@ -893,8 +908,8 @@ Keyboard.prototype.inVT100UARTAddress = function(port, addrFrom)
/*
* In MAXDEBUG builds, this code removes the key as soon as it's been reported, because
* when debugging, it's easy for the window to lose focus and never receive the keyUp event,
* thereby leaving us with a stuck key. However, this causes more problems than it solves,
* and seems to illustrate that key presses need to be persist for more than a single poll.
* thereby leaving us with a stuck key. However, this may cause more problems than it solves,
* because the VT100's ROM seems to require that key presses persist for more than a single poll.
*/
this.aKeysActive.splice(this.iKeyNext, 1);
}

View file

@ -95,7 +95,7 @@ Panel.prototype.initBus = function(cmp, bus, cpu, dbg)
this.bus = bus;
this.cpu = cpu;
this.dbg = dbg;
this.kbd = cmp.getMachineComponent("Keyboard");
this.kbd = /** @type {Keyboard} */ (cmp.getMachineComponent("Keyboard"));
};
/**

View file

@ -136,6 +136,90 @@ function SerialPort(parmsSerial) {
Component.subclass(SerialPort);
SerialPort.UART8251 = {
/*
* Format of MODE byte written to CONTROL port 0x1
*/
MODE: {
BAUD_FACTOR: 0x03, // 00=SYNC, 01=1x, 10=16x, 11=64x
DATA_BITS: 0x0C, // 00=5, 01=6, 10=7, 11=8
PARITY_ENABLE: 0x10,
EVEN_PARITY: 0x20,
STOP_BITS: 0xC0, // 00=invalid, 01=1, 10=1.5, 11=2
INIT: 0x8E // 16x baudrate, 8 data bits, no parity, 1.5 stop bits
},
/*
* Format of COMMAND byte written to CONTROL port 0x1
*/
COMMAND: {
XMIT_ENABLE: 0x01,
DTR: 0x02, // Data Terminal Ready
RECV_ENABLE: 0x04,
SEND_BREAK: 0x08,
ERROR_RESET: 0x10,
RTS: 0x20, // Request To Send
INTERNAL_RESET: 0x40,
HUNT_MODE: 0x80,
INIT: 0x27 // XMIT_ENABLE | DTR | RECV_ENABLE | RTS
},
/*
* Format of STATUS byte read from CONTROL port 0x1
*/
STATUS: {
XMIT_READY: 0x01,
RECV_FULL: 0x02,
XMIT_EMPTY: 0x04,
PARITY_ERROR: 0x08,
OVERRUN_ERROR: 0x10,
FRAMING_ERROR: 0x20,
BREAK_DETECT: 0x40,
DSR: 0x80, // Data Set Ready
INIT: 0x85 // XMIT_READY | XMIT_EMPTY | DSR
},
/*
* Format of BAUDRATE byte written to port 0x2
*
* Each nibble is an index (0x0-0xF) into a set internal CPU clock divisors that yield the following baud rates:
*
* Index Divisor Baud Rate
* ----- ------- ---------
* 0x0 3456 50
* 0x1 2304 75
* 0x2 1571 110
* 0x3 1285 134.5
* 0x4 1152 150
* 0x5 864 200
* 0x6 576 300
* 0x7 288 600
* 0x8 144 1200
* 0x9 96 1800
* 0xA 86 2000
* 0xB 72 2400
* 0xC 48 3600
* 0xD 36 4800
* 0xE 18 9600 (default)
* 0xF 9 19200
*
* TODO: I'm not really sure at this point which nibble is for the XMIT rate and which is for the RECV
* rate; I just made a random guess.
*/
BAUDRATE: {
XMIT_RATE: 0x0F,
RECV_RATE: 0xF0,
INIT: 0xEE // default to 9600 (0xE) for both XMIT and RECV
}
};
SerialPort.UART8251.INIT = [
false,
0,
0,
SerialPort.UART8251.STATUS.INIT,
SerialPort.UART8251.MODE.INIT,
SerialPort.UART8251.COMMAND.INIT,
SerialPort.UART8251.BAUDRATE.INIT
];
/*
* Internal name used for the I/O buffer control, if any, that we bind to the SerialPort.
*
@ -188,7 +272,7 @@ SerialPort.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
if (keyCode === 0x08 || event.ctrlKey && keyCode >= 0x41 && keyCode <= 0x5A) {
if (event.preventDefault) event.preventDefault();
if (keyCode > 0x40) keyCode -= 0x40;
// serial.sendRBR([keyCode]);
serial.sendByteIn(keyCode);
}
return true;
};
@ -218,10 +302,31 @@ SerialPort.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
* so that the soft keyboard will activate, but it shouldn't hurt to remove the attribute for all browsers.
*/
control.removeAttribute("readonly");
return true;
default:
if (sValue) {
/*
* Instead of just having a dedicated "test" control, we now treat any unrecognized control with
* a "value" attribute as a test control. The only caveat is that such controls must have binding IDs
* that do not conflict with predefined controls (which, of course, is the only way you can get here).
*/
this.bindings[sBinding] = control;
/*
* Convert any "backslashed" sequences into the appropriate control characters.
*/
sValue = sValue.replace(/\\n/g, "\n").replace(/\\r/g, "\r");
control.onclick = function onClickTest(event) {
serial.sDataIn = sValue;
serial.sendDataIn();
/*
* Give focus back to the machine (since clicking the button takes focus away).
*/
if (serial.cmp) serial.cmp.updateFocus();
return true;
};
return true;
}
break;
}
return false;
@ -238,10 +343,11 @@ SerialPort.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
*/
SerialPort.prototype.initBus = function(cmp, bus, cpu, dbg)
{
this.cmp = cmp;
this.bus = bus;
this.cpu = cpu;
this.dbg = dbg;
this.chipset = cmp.getMachineComponent("ChipSet");
this.chipset = /** @type {ChipSet} */ (cmp.getMachineComponent("ChipSet"));
bus.addPortInputTable(this, SerialPort.aPortInput, this.portBase);
bus.addPortOutputTable(this, SerialPort.aPortOutput, this.portBase);
this.setReady();
@ -330,11 +436,15 @@ SerialPort.prototype.initState = function(data)
{
var i = 0;
if (data === undefined) {
data = [0, 0, 0];
data = SerialPort.UART8251.INIT;
}
this.bData = data[i++];
this.bCommand = data[i++];
this.bBaudRate = data[i++];
this.fReady = data[i++];
this.bDataIn = data[i++];
this.bDataOut = data[i++];
this.bStatus = data[i++];
this.bMode = data[i++];
this.bCommand = data[i++];
this.bBaudRate = data[i];
return true;
};
@ -348,82 +458,46 @@ SerialPort.prototype.saveRegisters = function()
{
var i = 0;
var data = [];
data[i++] = this.bData;
data[i++] = this.fReady;
data[i++] = this.bDataIn;
data[i++] = this.bDataOut;
data[i++] = this.bStatus;
data[i++] = this.bMode;
data[i++] = this.bCommand;
data[i++] = this.bBaudRate;
data[i] = this.bBaudRate;
return data;
};
/**
* inData(port, addrFrom)
* sendByteIn(b)
*
* @this {SerialPort}
* @param {number} port (0x0)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
* @param {number} b
* @return {boolean}
*/
SerialPort.prototype.inData = function(port, addrFrom)
SerialPort.prototype.sendByteIn = function(b)
{
var b = this.bData;
this.printMessageIO(port, null, addrFrom, "DATA", b);
return b;
if (!(this.bStatus & SerialPort.UART8251.STATUS.RECV_FULL)) {
this.bDataIn = b;
this.bStatus |= SerialPort.UART8251.STATUS.RECV_FULL;
this.cpu.requestINTR(this.nIRQ);
return true;
}
return false;
};
/**
* inCommand(port, addrFrom)
* sendDataIn()
*
* @this {SerialPort}
* @param {number} port (0x1)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
SerialPort.prototype.inCommand = function(port, addrFrom)
SerialPort.prototype.sendDataIn = function()
{
var b = this.bCommand;
this.printMessageIO(port, null, addrFrom, "COMMAND", b);
return b;
};
/**
* outData(port, bOut, addrFrom)
*
* @this {SerialPort}
* @param {number} port (0x0)
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
SerialPort.prototype.outData = function(port, bOut, addrFrom)
{
this.printMessageIO(port, bOut, addrFrom, "DATA");
this.bData = bOut;
};
/**
* outCommand(port, bOut, addrFrom)
*
* @this {SerialPort}
* @param {number} port (0x1)
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
SerialPort.prototype.outCommand = function(port, bOut, addrFrom)
{
this.printMessageIO(port, bOut, addrFrom, "COMMAND");
this.bCommand = bOut;
};
/**
* outBaudRate(port, bOut, addrFrom)
*
* @this {SerialPort}
* @param {number} port (0x2)
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
SerialPort.prototype.outBaudRate = function(port, bOut, addrFrom)
{
this.printMessageIO(port, bOut, addrFrom, "BAUDRATE");
this.bBaudRate = bOut;
if (this.sDataIn) {
if (this.sendByteIn(this.sDataIn.charCodeAt(0))) {
this.sDataIn = this.sDataIn.substr(1);
}
}
};
/**
@ -474,12 +548,99 @@ SerialPort.prototype.echoByte = function(b)
return false;
};
/**
* inData(port, addrFrom)
*
* @this {SerialPort}
* @param {number} port (0x0)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
SerialPort.prototype.inData = function(port, addrFrom)
{
var b = this.bDataIn;
this.printMessageIO(port, null, addrFrom, "DATA", b);
this.bStatus &= ~SerialPort.UART8251.STATUS.RECV_FULL;
this.sendDataIn(); // if there is still queued incoming data, send another byte
return b;
};
/**
* inControl(port, addrFrom)
*
* @this {SerialPort}
* @param {number} port (0x1)
* @param {number} [addrFrom] (not defined whenever the Debugger tries to read the specified port)
* @return {number} simulated port value
*/
SerialPort.prototype.inControl = function(port, addrFrom)
{
var b = this.bStatus;
this.printMessageIO(port, null, addrFrom, "STATUS", b);
return b;
};
/**
* outData(port, bOut, addrFrom)
*
* @this {SerialPort}
* @param {number} port (0x0)
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
SerialPort.prototype.outData = function(port, bOut, addrFrom)
{
this.printMessageIO(port, bOut, addrFrom, "DATA");
this.bDataOut = bOut;
};
/**
* outControl(port, bOut, addrFrom)
*
* Writes to the CONTROL port (0x1) are either MODE or COMMAND bytes. If the device has just
* been powered or reset, it is in a "not ready" state and is waiting for a MODE byte. Once it
* has received that initial byte, the device is marked "ready", and all further bytes are
* interpreted as COMMAND bytes (until/unless a COMMAND byte with the INTERNAL_RESET bit is set).
*
* @this {SerialPort}
* @param {number} port (0x1)
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
SerialPort.prototype.outControl = function(port, bOut, addrFrom)
{
this.printMessageIO(port, bOut, addrFrom, "CONTROL");
if (!this.fReady) {
this.bMode = bOut;
this.fReady = true;
} else {
this.bCommand = bOut;
if (this.bCommand & SerialPort.UART8251.COMMAND.INTERNAL_RESET) {
this.fReady = false;
}
}
};
/**
* outBaudRate(port, bOut, addrFrom)
*
* @this {SerialPort}
* @param {number} port (0x2)
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
SerialPort.prototype.outBaudRate = function(port, bOut, addrFrom)
{
this.printMessageIO(port, bOut, addrFrom, "BAUDRATE");
this.bBaudRate = bOut;
};
/*
* Port input notification table
*/
SerialPort.aPortInput = {
0x0: SerialPort.prototype.inData,
0x1: SerialPort.prototype.inCommand
0x1: SerialPort.prototype.inControl
};
@ -488,7 +649,7 @@ SerialPort.aPortInput = {
*/
SerialPort.aPortOutput = {
0x0: SerialPort.prototype.outData,
0x1: SerialPort.prototype.outCommand,
0x1: SerialPort.prototype.outControl,
0x2: SerialPort.prototype.outBaudRate
};

View file

@ -152,7 +152,7 @@ function Video(parmsVideo, canvas, context, textarea, container)
/*
* Now that we've finished using nRowsBuffer to help define the screen size, we add one more
* row for text modes, to simplify smooth-scrolling down the road.
* row for text modes, to account for the VT100's scroll line buffer (used for smooth scrolling).
*/
if (this.cyCell > 1) this.nRowsBuffer++;
@ -292,6 +292,9 @@ Video.VT100 = {
/**
* initBuffers()
*
* @this {Video}
* @return {boolean}
*/
Video.prototype.initBuffers = function()
{
@ -308,10 +311,11 @@ Video.prototype.initBuffers = function()
cyBuffer = this.cxBuffer;
}
this.sizeBuffer = ((this.cxBuffer * this.nBitsPerPixel) >> 3) * this.cyBuffer;
this.sizeBuffer = 0;
if (!this.fUseRAM) {
this.sizeBuffer = ((this.cxBuffer * this.nBitsPerPixel) >> 3) * this.cyBuffer;
if (!this.bus.addMemory(this.addrBuffer, this.sizeBuffer, Memory.TYPE.VIDEO)) {
return;
return false;
}
}
@ -319,15 +323,15 @@ Video.prototype.initBuffers = function()
* imageBuffer is only used for graphics modes. For text modes, we create a canvas
* for each font and draw characters by drawing from the font canvas to the target canvas.
*/
if (this.cxCell > 1) {
if (this.sizeBuffer) {
this.imageBuffer = this.contextScreen.createImageData(cxBuffer, cyBuffer);
this.nPixelsPerCell = (16 / this.nBitsPerPixel)|0;
this.initCellCache(this.sizeBuffer >> 1);
} else {
/*
* We add an extra column per row to store the visible line length at the start of every row.
*/
this.initCellCache((this.nColsBuffer + 1) * this.nRowsBuffer);
} else {
this.imageBuffer = this.contextScreen.createImageData(cxBuffer, cyBuffer);
this.nPixelsPerCell = (16 / this.nBitsPerPixel)|0;
this.initCellCache(this.sizeBuffer >> 1);
}
this.canvasBuffer = document.createElement("canvas");
@ -383,6 +387,7 @@ Video.prototype.initBuffers = function()
this.abLineBuffer = new Array(this.nColsBuffer);
}
return true;
};
/**
@ -410,7 +415,7 @@ Video.prototype.initBus = function(cmp, bus, cpu, dbg)
* If we have an associated keyboard, then ensure that the keyboard will be notified
* whenever the canvas gets focus and receives input.
*/
this.kbd = cmp.getMachineComponent("Keyboard");
this.kbd = /** @type {Keyboard} */ (cmp.getMachineComponent("Keyboard"));
if (this.kbd) {
for (var s in this.ledBindings) {
this.kbd.setBinding("led", s, this.ledBindings[s]);
@ -461,7 +466,8 @@ Video.prototype.doneLoad = function(sURL, sFontData, nErrorCode)
* Minimal font data validation, just to make sure we're not getting garbage from the server.
*/
if (abFontData.length == 2048) {
this.createFonts(abFontData);
this.abFontData = abFontData;
this.createFonts();
}
else {
this.notice("Unrecognized font data length (" + abFontData.length + ")");
@ -484,30 +490,33 @@ Video.prototype.doneLoad = function(sURL, sFontData, nErrorCode)
};
/**
* createFonts(abFontData)
* createFonts()
*
* @this {Video}
* @param {Array.<number>} abFontData
* @return {boolean}
*/
Video.prototype.createFonts = function(abFontData)
Video.prototype.createFonts = function()
{
/*
* 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)
];
this.aFonts[Video.VT100.FONT.DWIDE] = [
this.createFontVariation(this.cxCell*2, this.cyCell),
this.createFontVariation(this.cxCell*2, this.cyCell, this.fUnderline)
];
this.aFonts[Video.VT100.FONT.DHIGH] = this.aFonts[Video.VT100.FONT.DHIGH_BOT] = [
this.createFontVariation(this.cxCell*2, this.cyCell*2),
this.createFontVariation(this.cxCell*2, this.cyCell*2, this.fUnderline)
];
if (this.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)
];
this.aFonts[Video.VT100.FONT.DWIDE] = [
this.createFontVariation(this.cxCell*2, this.cyCell),
this.createFontVariation(this.cxCell*2, this.cyCell, this.fUnderline)
];
this.aFonts[Video.VT100.FONT.DHIGH] = this.aFonts[Video.VT100.FONT.DHIGH_BOT] = [
this.createFontVariation(this.cxCell*2, this.cyCell*2),
this.createFontVariation(this.cxCell*2, this.cyCell*2, this.fUnderline)
];
return true;
}
return false;
};
/**
@ -712,6 +721,47 @@ Video.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
return false;
};
/**
* updateDimensions(nCols, nRows)
*
* Called from the ChipSet component whenever the screen dimensions have been dynamically altered.
*
* @this {Video}
* @param {number} nCols (should be either 80 or 132; 80 is the default)
* @param {number} nRows (should be either 24 or 14; 24 is the default)
*/
Video.prototype.updateDimensions = function(nCols, nRows)
{
this.printMessage("updateDimensions(" + nCols + "," + nRows + ")");
this.nColsBuffer = nCols;
/*
* Even when the number of effective rows is 14 (or 15 counting the scroll line buffer), we want
* to leave the number of rows at 24 (or 25 counting the scroll line buffer), because the VT100 doesn't
* actually change character height (only character width).
*
* this.nRowsBuffer = nRows+1; // +1 for scroll line buffer
*/
this.cxCell = this.cxCellDefault;
if (nCols > 80) this.cxCell--; // VT100 font cells are 9x10 instead of 10x10 in 132-column mode
if (this.initBuffers()) {
this.createFonts();
}
};
/**
* updateRate(nRate)
*
* Called from the ChipSet component whenever the monitor refresh rate has been dynamically altered.
*
* @this {Video}
* @param {number} nRate (should be either 50 or 60; 60 is the default)
*/
Video.prototype.updateRate = function(nRate)
{
this.printMessage("updateRate(" + nRate + ")");
this.rateMonitor = nRate;
};
/**
* doFullScreen()
*
@ -796,7 +846,7 @@ Video.prototype.notifyFullScreen = function(fFullScreen)
this.canvasScreen.style.width = this.canvasScreen.style.height = "";
}
}
this.printMessage("notifyFullScreen(" + fFullScreen + ")", true);
this.printMessage("notifyFullScreen(" + fFullScreen + ")");
};
/**
@ -1057,8 +1107,8 @@ Video.prototype.updateVT100 = function(fForced)
if (cUpdated && this.contextBuffer) {
/*
* NOTE: We must subtract cyCell from cyBuffer to avoid displaying the extra row that we normally buffer
* in support of smooth-scrolling.
* NOTE: We must subtract cyCell from cyBuffer to avoid displaying the extra "scroll line" that we normally
* buffer to support smooth-scrolling.
*/
this.contextScreen.drawImage(this.canvasBuffer, 0, 0, this.cxBuffer, this.cyBuffer - this.cyCell, this.xScreenOffset, this.yScreenOffset, this.cxScreenOffset, this.cyScreenOffset);
}
@ -1111,9 +1161,10 @@ Video.prototype.updateScreen = function(n)
}
/*
* Since this is not a forced update, if our cell cache is valid AND the buffer is clean, then do nothing.
* Since this is not a forced update, if our cell cache is valid AND we allocated our own buffer AND the buffer
* is clean, then there's nothing to do.
*/
if (fUpdate && this.fCellCacheValid) {
if (fUpdate && this.fCellCacheValid && this.sizeBuffer) {
if ((fClean = this.bus.cleanMemory(this.addrBuffer, this.sizeBuffer))) {
fUpdate = false;
}

View file

@ -1220,7 +1220,7 @@ Keyboard.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
if (Keyboard.CLICKCODES[sCode] !== undefined && sHTMLType == "button") {
this.bindings[id] = control;
control.onclick = function(kbd, sKey, simCode) {
return function onClickKeyboard(event) {
return function onKeyboardBindingClick(event) {
if (!COMPILED && kbd.messageEnabled()) kbd.printMessage(sKey + " clicked", Messages.KEYS);
if (kbd.cmp) kbd.cmp.updateFocus();
kbd.updateShiftState(simCode, true); // future-proofing if/when any LOCK keys are added to CLICKCODES
@ -1233,12 +1233,12 @@ Keyboard.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
this.cSoftCodes++;
this.bindings[id] = control;
var fnDown = function(kbd, sKey, simCode) {
return function onMouseOrTouchDownKeyboard(event) {
return function onKeyboardBindingDown(event) {
kbd.addActiveKey(simCode);
};
}(this, sBinding, Keyboard.SOFTCODES[sBinding]);
var fnUp = function (kbd, sKey, simCode) {
return function onMouseOrTouchUpKeyboard(event) {
return function onKeyboardBindingUp(event) {
kbd.removeActiveKey(simCode);
};
}(this, sBinding, Keyboard.SOFTCODES[sBinding]);
@ -1254,8 +1254,8 @@ Keyboard.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
else if (sValue) {
/*
* Instead of just having a dedicated "test" control, we now treat any unrecognized control with
* a data value as a test control. The only caveat is that such controls must have binding IDs that
* do not conflict with predefined controls (which, of course, is the only way you can get here).
* a "value" attribute as a test control. The only caveat is that such controls must have binding IDs
* that do not conflict with predefined controls (which, of course, is the only way you can get here).
*/
this.bindings[id] = control;
control.onclick = function onClickTest(event) {