The VT100 is now smarter about detecting the host machine's physical CAPS LOCK state (and is also capable of displaying that state now)

This commit is contained in:
Jeff Parsons 2016-11-25 16:06:39 -08:00 committed by Jeff Parsons
commit 57d287f161
7 changed files with 239 additions and 30 deletions

View file

@ -3,7 +3,7 @@
<machine id="vt100" class="pc8080" border="1" pos="center" background="#FAEBD7">
<name pos="center">VT100 Terminal</name>
<computer id="computer" busWidth="16"/>
<cpu id="cpu8080" model="8080" cycles="2764800"/>
<cpu id="cpu8080" model="8080" cycles="2764800" autoStart="true"/>
<rom id="rom" addr="0x0000" size="0x2000" file="/devices/pc8080/rom/vt100/VT100.json"/>
<ram id="ram" addr="0x2000" size="0x0C00"/>
<video id="video" screenWidth="1600" screenHeight="960" smoothing="false" interruptRate="60"
@ -19,6 +19,7 @@
<control type="rled" label="L2" binding="l2" padLeft="8px"/>
<control type="rled" label="L3" binding="l3" padLeft="8px"/>
<control type="rled" label="L4" binding="l4" padLeft="8px"/>
<control type="led" label="CAPS" binding="caps-lock" padLeft="8px"/>
<control type="button" binding="fullScreen" padLeft="8px;line-height:1em">Full Screen</control>
</control>
</menu>

View file

@ -19,6 +19,7 @@
<control type="rled" label="L2" binding="l2" padLeft="8px"/>
<control type="rled" label="L3" binding="l3" padLeft="8px"/>
<control type="rled" label="L4" binding="l4" padLeft="8px"/>
<control type="led" label="CAPS" binding="caps-lock" padLeft="8px"/>
<control type="button" binding="fullScreen" padLeft="8px;line-height:1em">Full Screen</control>
</control>
</menu>

View file

@ -10,7 +10,8 @@ machines:
connection: dl11->vt100.serialPort
- id: vt100
type: pc8080
config: /devices/pc8080/machine/vt100/machine.xml
debugger: true
config: /devices/pc8080/machine/vt100/debugger/machine.xml
connection: serialPort->test1170.dl11
---

View file

@ -104,7 +104,7 @@ on [bitsavers.org](http://bitsavers.trailing-edge.com/pdf/dec/pdp11/microfiche/f
DATE CREATED: MAY, 1980
MAINTAINER: DIAGNOSTIC ENGINEERING
The source code listing for TEST 40 appears to match the code above:
The source code listing for TEST 40 matches the code above, and begins at the same address (020402):
;;***************************************************************
;*TEST 40 RED ZONE TRAP
@ -176,3 +176,86 @@ PDPjs was failing to trigger a RED stack violation on this instruction:
so execution fell into `ERROR 241`. This was resolved by updating the *checkStackLimit1145()* function
in [cpustate.js](/modules/pdp11/lib/cpustate.js) to include stack addresses >= 177776 in the RED stack
overflow range test.
The next test that PDPjs failed was TEST 63, which also matches the source code listing in the above microfiche,
and begins at the same address (031532):
;;***************************************************************
;*TEST 63 WAIT
;*
;* THIS TEST ENSURES THAT THE WAIT INSTRUCTION WORKS PROPERLY.
;* IT FIRST EXECUTES WITH A LEVEL 4 INTERRUPT.
;* THEN THE T BIT IS ENABLED TO ENSURE THAT THE INTERRUPT
;* OCCURS AND NOT THE T BIT TRAP.
;;***************************************************************
TST63: SCOPE
MOV #TST64,NEXTTST ;SAVE ADDRESS OF NEXT TEST
MOV #^D1990,$ICNT ;ADJUST ITTERATION COUNT
MOV #10$,$LPADR ;SETUP LOOP ADR
10$: MOV #2$,@#TPVEC ;SETUP TELEPRINTER VECTOR
MOV #1$,$LPERR ;SETUP ERROR LOOP
1$: MOV #STACK,SP ;INITIALIZE THE SP
SPL 3
MOV #BIT6,@$TPS ;SET PRINTER INTERRUPT FLAG
MOV #15,@$TPB ;SEND CHARACTER
WAIT ;EXECUTE INSTRUCTION UNDER TEST
;FAILURE
CLR @$TPS ;CLEAR INTERR ENABLE BIT
ERROR 377 ;WAIT INSTRUCTION DID NOT
BR 5$ ;WAIT FOR INTERRUPT
After setting and hitting a breakpoint at 031532, we can step through the instructions in this test:
bp 031532 hit
stopped (37666352 instructions, 181205851 cycles, 27294 ms, 6639036 hz)
R0=000000 R1=000102 R2=000000 R3=000000 R4=000000 R5=026642
SP=001074 PC=031532 PS=010340 IR=000000 SL=000377 T0 N0 Z0 V0 C0
031532: 012767 032024 147530 MOV #32024,001270
>> tr
R0=000000 R1=000102 R2=000000 R3=000000 R4=000000 R5=026642
SP=001074 PC=031540 PS=010340 IR=000000 SL=000377 T0 N0 Z0 V0 C0
031540: 012767 003706 147336 MOV #3706,001104 ;cycles=16
>> tr
R0=000000 R1=000102 R2=000000 R3=000000 R4=000000 R5=026642
SP=001074 PC=031546 PS=010340 IR=000000 SL=000377 T0 N0 Z0 V0 C0
031546: 012767 031554 147332 MOV #31554,001106 ;cycles=16
>> tr
R0=000000 R1=000102 R2=000000 R3=000000 R4=000000 R5=026642
SP=001074 PC=031554 PS=010340 IR=000000 SL=000377 T0 N0 Z0 V0 C0
031554: 012737 031624 000064 MOV #31624,@#64 ;cycles=16
>> tr
R0=000000 R1=000102 R2=000000 R3=000000 R4=000000 R5=026642
SP=001074 PC=031562 PS=010340 IR=000000 SL=000377 T0 N0 Z0 V0 C0
031562: 012767 031570 147320 MOV #31570,001110 ;cycles=16
>> tr
R0=000000 R1=000102 R2=000000 R3=000000 R4=000000 R5=026642
SP=001074 PC=031570 PS=010340 IR=000000 SL=000377 T0 N0 Z0 V0 C0
031570: 012706 001100 MOV #1100,SP ;cycles=16
>> tr
R0=000000 R1=000102 R2=000000 R3=000000 R4=000000 R5=026642
SP=001100 PC=031574 PS=010340 IR=000000 SL=000377 T0 N0 Z0 V0 C0
031574: 000233 SPL 003 ;cycles=7
>> tr
R0=000000 R1=000102 R2=000000 R3=000000 R4=000000 R5=026642
SP=001100 PC=031576 PS=010140 IR=000000 SL=000377 T0 N0 Z0 V0 C0
031576: 012777 000100 147336 MOV #100,@001142 ;cycles=5
>> tr
R0=000000 R1=000102 R2=000000 R3=000000 R4=000000 R5=026642
SP=001100 PC=031604 PS=010140 IR=000000 SL=000377 T0 N0 Z0 V0 C0
031604: 012777 000015 147332 MOV #15,@001144 ;cycles=20
>> tr
trapped to 100 (INTERRUPT)
R0=000000 R1=000102 R2=000000 R3=000000 R4=000000 R5=026642
SP=001074 PC=026020 PS=000340 IR=000000 SL=000377 T0 N0 Z0 V0 C0
026020: 005077 153212 CLR @001236 ;cycles=0
>> tr
R0=000000 R1=000102 R2=000000 R3=000000 R4=000000 R5=026642
SP=001074 PC=026024 PS=000344 IR=000000 SL=000377 T0 N0 Z1 V0 C0
026024: 005037 177772 CLR @#177772 ;cycles=19
>> tr
R0=000000 R1=000102 R2=000000 R3=000000 R4=000000 R5=026642
SP=001074 PC=026030 PS=000344 IR=000000 SL=000377 T0 N0 Z1 V0 C0
026030: 104324 EMT 324 ;cycles=16
Vector 100 is for the KW11 Line Clock -- an interrupt that the test was apparently not expecting. It
was instead looking for an interrupt from the DL11 "teleprinter" device.

View file

@ -68,6 +68,6 @@ the PDF (p. 199 of the source code listing):
;;***************************************************************
As noted in the *mapVirtualToPhysical()* function in [cpustate.js](/modules/pdp11/lib/cpustate.js), this test
begins at PC 076060.
begins at PC 076060, which is exactly 0100 bytes lower than the address shown in the source code listing (076160).
This diagnostic should run in any [PDP-11/70](/devices/pdp11/machine/1170/) ([Demo](/devices/pdp11/machine/1170/panel/debugger/xxdp/)).

View file

@ -69,7 +69,34 @@ function Keyboard8080(parmsKbd)
Component.subclass(Keyboard8080);
Keyboard8080.MINPRESSTIME = 50; // minimum milliseconds to wait before auto-releasing keys
/*
* Now that we want to keep track of the physical (and simulated) state of modifier keys, I've
* grabbed a copy of the same bit definitions used by /modules/pcx86/lib/keyboard.js, since it's
* only important that we have a set of unique values; what the values are isn't critical.
*/
Keyboard8080.STATE = {
RSHIFT: 0x0001,
SHIFT: 0x0002,
SHIFTS: 0x0003,
RCTRL: 0x0004, // 101-key keyboard only
CTRL: 0x0008,
CTRLS: 0x000C,
RALT: 0x0010, // 101-key keyboard only
ALT: 0x0020,
ALTS: 0x0030,
RCMD: 0x0040, // 101-key keyboard only
CMD: 0x0080, // 101-key keyboard only
CMDS: 0x00C0,
ALL_RIGHT: 0x0055, // RSHIFT | RCTRL | RALT | RCMD
ALL_SHIFT: 0x00FF, // SHIFT | RSHIFT | CTRL | RCTRL | ALT | RALT | CMD | RCMD
INSERT: 0x0100, // TODO: Placeholder (we currently have no notion of any "insert" states)
CAPS_LOCK: 0x0200,
NUM_LOCK: 0x0400,
SCROLL_LOCK: 0x0800,
ALL_LOCKS: 0x0E00 // CAPS_LOCK | NUM_LOCK | SCROLL_LOCK
};
Keyboard8080.MINPRESSTIME = 50; // minimum milliseconds to wait before auto-releasing keys
/**
* Alternate keyCode mappings to support popular "WASD"-style directional-key mappings.
@ -255,7 +282,8 @@ Keyboard8080.VT100.LEDCODES = {
'l1': Keyboard8080.VT100.STATUS.LED1,
'locked': Keyboard8080.VT100.STATUS.LOCKED,
'local': Keyboard8080.VT100.STATUS.LOCAL,
'online': ~Keyboard8080.VT100.STATUS.LOCAL
'online': ~Keyboard8080.VT100.STATUS.LOCAL,
'caps-lock':Keyboard8080.STATE.CAPS_LOCK
};
/*
@ -322,6 +350,9 @@ Keyboard8080.prototype.setBinding = function(sHTMLType, sBinding, control, sValu
control.onkeyup = function onKeyUp(event) {
return kbd.onKeyDown(event, false);
};
control.onkeypress = function onKeyPress(event) {
return kbd.onKeyPress(event);
};
return true;
default:
@ -458,6 +489,13 @@ Keyboard8080.prototype.reset = function()
*/
this.aKeysActive = [];
/*
* The current (assumed) physical (and simulated) states of the various shift/lock keys.
*
* TODO: Determine how (or whether) we can query the browser's initial shift/lock key states.
*/
this.bitsState = 0;
if (this.config.INIT && !this.restore(this.config.INIT)) {
this.notice("reset error");
}
@ -515,39 +553,80 @@ Keyboard8080.prototype.restore = function(data)
};
/**
* setLED(control, f)
* setLED(control, f, color)
*
* TODO: Add support for user-definable LED colors
*
* @this {Keyboard8080}
* @param {Object} control is an HTML control DOM object
* @param {boolean} f is true if the LED represented by control should be "on", false if "off"
* @param {boolean|number} f is true if the LED represented by control should be "on", false if "off"
* @param {number} color (ie, 0xff0000 for RED, or 0x00ff00 for GREEN)
*/
Keyboard8080.prototype.setLED = function(control, f)
Keyboard8080.prototype.setLED = function(control, f, color)
{
/*
* TODO: Add support for user-definable LED colors
*/
control.style.backgroundColor = (f? "#ff0000" : "#000000");
control.style.backgroundColor = (f? ('#' + str.toHex(color, 6)) : "#000000");
};
/**
* updateLEDs(bLEDs)
*
* @this {Keyboard8080}
* @param {number} bLEDs
* @param {number} [bLEDs]
*/
Keyboard8080.prototype.updateLEDs = function(bLEDs)
{
this.bLEDs = bLEDs;
var id, control;
if (bLEDs != null) {
this.bLEDs = bLEDs;
} else {
bLEDs = this.bLEDs;
}
for (var sBinding in this.config.LEDCODES) {
var id = "led-" + sBinding;
var control = this.bindings[id];
id = "led-" + sBinding;
control = this.bindings[id];
if (control) {
var bitLED = this.config.LEDCODES[sBinding];
var fOn = !!(bLEDs & bitLED);
if (bitLED & (bitLED-1)) {
fOn = !(bLEDs & ~bitLED);
}
this.setLED(control, fOn);
this.setLED(control, fOn, 0xff0000);
}
}
id = "led-caps-lock";
control = this.bindings[id];
if (control) {
this.setLED(control, (this.bitsState & Keyboard8080.STATE.CAPS_LOCK), 0x00ff00);
}
};
/**
* checkModifierKeys(softCode, fDown, fRight)
*
* @this {Keyboard8080}
* @param {number|string} softCode (ie, either a keycode or string ID)
* @param {boolean} fDown (true if key going down, false if key going up)
* @param {boolean} fRight (true if key is on the right, false if not or unknown or n/a)
*/
Keyboard8080.prototype.checkModifierKeys = function(softCode, fDown, fRight)
{
var bit = 0;
switch(softCode) {
case Keys.KEYCODE.CTRL:
bit = fRight? Keyboard8080.STATE.RCTRL : Keyboard8080.STATE.CTRL;
break;
case Keys.KEYCODE.SHIFT:
bit = fRight? Keyboard8080.STATE.RSHIFT : Keyboard8080.STATE.SHIFT;
break;
case Keys.KEYCODE.CAPS_LOCK:
bit = Keyboard8080.STATE.CAPS_LOCK;
break;
}
if (bit) {
if (fDown) {
this.bitsState |= bit;
} else {
this.bitsState &= ~bit;
}
}
};
@ -555,7 +634,7 @@ Keyboard8080.prototype.updateLEDs = function(bLEDs)
/**
* getSoftCode(keyCode)
*
* Returns a number if the keyCode exists in the KEYMAP, or a string if the keyCode has a soft-code string.
* Returns a number if the keyCode exists in the KEYMAP, or a string if the keyCode has a string ID.
*
* @this {Keyboard8080}
* @return {string|number|null}
@ -589,8 +668,21 @@ Keyboard8080.prototype.onKeyDown = function(event, fDown)
var softCode = this.getSoftCode(keyCode);
if (softCode) {
/*
* We now keep track of any physical keyboard modifier keys that also exist on the simulated
* keyboard; in the case of the VT100, that means CTRL and SHIFT. This will make it possible
* for a new pair of services to eventually be implemented: simulateKeysDown() and simulateKeysUp().
*/
this.checkModifierKeys(softCode, fDown, event.location == Keys.LOCATION.RIGHT);
fPass = this.onSoftKeyDown(softCode, fDown);
event.preventDefault();
/*
* As onKeyPress() explains, the only key presses we're interested in are letters, which provide
* an important clue regarding the CAPS-LOCK state. For all other keys, we call preventDefault(),
* which "suppresses" the keyPress event.
*/
if (softCode < Keys.ASCII.A || softCode > Keys.ASCII.Z) {
event.preventDefault();
}
}
if (!COMPILED && this.messageEnabled(Messages8080.KEYS)) {
@ -600,6 +692,35 @@ Keyboard8080.prototype.onKeyDown = function(event, fDown)
return fPass;
};
/**
* onKeyPress(event)
*
* For now, our only interest in keyPress events is letters, as a means of detecting the CAPS-LOCK state.
*
* @this {Keyboard8080}
* @param {Object} event
* @return {boolean} true to pass the event along, false to consume it
*/
Keyboard8080.prototype.onKeyPress = function(event)
{
var keyCode = event.keyCode;
if (keyCode >= Keys.ASCII.A && keyCode <= Keys.ASCII.Z) {
if (!(this.bitsState & (Keyboard8080.STATE.SHIFTS | Keyboard8080.STATE.CAPS_LOCK))) {
this.bitsState |= Keyboard8080.STATE.CAPS_LOCK;
this.onSoftKeyDown(Keys.KEYCODE.CAPS_LOCK, true);
this.updateLEDs();
}
}
else if (keyCode >= Keys.ASCII.a && keyCode <= Keys.ASCII.z) {
if (this.bitsState & Keyboard8080.STATE.CAPS_LOCK) {
this.bitsState &= ~Keyboard8080.STATE.CAPS_LOCK;
this.onSoftKeyDown(Keys.KEYCODE.CAPS_LOCK, false);
this.updateLEDs();
}
}
return true;
};
/**
* indexOfSoftKey(softCode)
*
@ -609,35 +730,36 @@ Keyboard8080.prototype.onKeyDown = function(event, fDown)
*/
Keyboard8080.prototype.indexOfSoftKey = function(softCode)
{
var i;
for (i = 0; i < this.aKeysActive.length; i++) {
for (var i = 0; i < this.aKeysActive.length; i++) {
if (this.aKeysActive[i].softCode == softCode) return i;
}
return -1;
};
/**
* onSoftKeyDown(softCode, fDown)
* onSoftKeyDown(softCode, fDown, fAutoRelease)
*
* @this {Keyboard8080}
* @param {number|string} softCode
* @param {boolean} fDown is true for a down event, false for up
* @param {boolean} [fAutoRelease] is true only if we know we want the key to auto-release
* @return {boolean} true to pass the event along, false to consume it
*/
Keyboard8080.prototype.onSoftKeyDown = function(softCode, fDown)
Keyboard8080.prototype.onSoftKeyDown = function(softCode, fDown, fAutoRelease)
{
var i = this.indexOfSoftKey(softCode);
if (fDown) {
// this.println(softCode + " down");
fAutoRelease = fAutoRelease || false;
if (i < 0) {
this.aKeysActive.push({
softCode: softCode,
msDown: Date.now(),
fAutoRelease: false
fAutoRelease: fAutoRelease
});
} else {
this.aKeysActive[i].msDown = Date.now();
this.aKeysActive[i].fAutoRelease = false;
this.aKeysActive[i].fAutoRelease = fAutoRelease;
}
} else if (i >= 0) {
// this.println(softCode + " up");

View file

@ -331,22 +331,23 @@ Keyboard.SCANCODE = {
Keyboard.STATE = {
RSHIFT: 0x0001,
SHIFT: 0x0002,
SHIFTS: 0x0003,
RCTRL: 0x0004, // 101-key keyboard only
CTRL: 0x0008,
CTRLS: 0x000c,
CTRLS: 0x000C,
RALT: 0x0010, // 101-key keyboard only
ALT: 0x0020,
ALTS: 0x0030,
RCMD: 0x0040, // 101-key keyboard only
CMD: 0x0080, // 101-key keyboard only
CMDS: 0x00c0,
CMDS: 0x00C0,
ALL_RIGHT: 0x0055, // RSHIFT | RCTRL | RALT | RCMD
ALL_SHIFT: 0x00ff, // SHIFT | RSHIFT | CTRL | RCTRL | ALT | RALT | CMD | RCMD
ALL_SHIFT: 0x00FF, // SHIFT | RSHIFT | CTRL | RCTRL | ALT | RALT | CMD | RCMD
INSERT: 0x0100, // TODO: Placeholder (we currently have no notion of any "insert" states)
CAPS_LOCK: 0x0200,
NUM_LOCK: 0x0400,
SCROLL_LOCK: 0x0800,
ALL_LOCKS: 0x0e00 // CAPS_LOCK | NUM_LOCK | SCROLL_LOCK
ALL_LOCKS: 0x0E00 // CAPS_LOCK | NUM_LOCK | SCROLL_LOCK
};
/**