diff --git a/modules/pc8080/lib/keyboard.js b/modules/pc8080/lib/keyboard.js index ab67b208a..25b584ad0 100644 --- a/modules/pc8080/lib/keyboard.js +++ b/modules/pc8080/lib/keyboard.js @@ -93,7 +93,8 @@ Keyboard8080.STATE = { 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 + REMAPPED: 0x1000 }; Keyboard8080.MINPRESSTIME = 50; // minimum milliseconds to wait before auto-releasing keys @@ -187,7 +188,7 @@ Keyboard8080.VT100 = { [Keys.KEYCODE.F4]: 0x41, // aka PF4 [Keys.KEYCODE.F2]: 0x42, // aka PF2 [Keys.KEYCODE.NUM_0]: 0x43, - [Keys.KEYCODE.F7]: 0x44, // aka LINE FEED + [Keys.KEYCODE.F7]: 0x44, // aka LINE-FEED [Keys.KEYCODE.BSLASH]: 0x45, [Keys.ASCII.L]: 0x46, [Keys.ASCII.K]: 0x47, @@ -214,7 +215,7 @@ Keyboard8080.VT100 = { [Keys.ASCII.N]: 0x67, [Keys.ASCII.B]: 0x68, [Keys.ASCII.X]: 0x69, - [Keys.KEYCODE.F8]: 0x6A, // aka NO SCROLL + [Keys.KEYCODE.F8]: 0x6A, // aka NO-SCROLL [Keys.KEYCODE.NUM_9]: 0x70, [Keys.KEYCODE.NUM_3]: 0x71, [Keys.KEYCODE.NUM_6]: 0x72, @@ -233,7 +234,11 @@ Keyboard8080.VT100 = { ALTCODES: {}, LEDCODES: {}, SOFTCODES: { - 'setup': Keys.KEYCODE.F9 + 'num-comma': Keys.KEYCODE.F5, // since modern keypads don't typically have a comma... + 'break': Keys.KEYCODE.F6, + 'line-feed': Keys.KEYCODE.F7, + 'no-scroll': Keys.KEYCODE.F8, + 'setup': Keys.KEYCODE.F9 }, /* * Reading port 0x82 returns a key address from the VT100 keyboard's UART data output. @@ -606,17 +611,17 @@ Keyboard8080.prototype.updateLEDs = function(bLEDs) }; /** - * checkModifierKeys(softCode, fDown, fRight) + * checkModifierKeys(keyCode, fDown, fRight) * * @this {Keyboard8080} - * @param {number|string} softCode (ie, either a keycode or string ID) + * @param {number} keyCode (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) +Keyboard8080.prototype.checkModifierKeys = function(keyCode, fDown, fRight) { var bit = 0; - switch(softCode) { + switch(keyCode) { case Keys.KEYCODE.SHIFT: bit = fRight? Keyboard8080.STATE.RSHIFT : Keyboard8080.STATE.SHIFT; break; @@ -676,22 +681,63 @@ Keyboard8080.prototype.onKeyDown = function(event, fDown) { var fPass = true; var keyCode = event.keyCode; - var softCode = this.getSoftCode(keyCode); + /* + * We now keep track of physical keyboard modifier keys. This makes it possible for new services + * to eventually be implemented (simulateKeysDown() and simulateKeysUp()), to map special ALT-key + * combinations to VT100 keys, etc. + */ + this.checkModifierKeys(keyCode, fDown, event.location == Keys.LOCATION.RIGHT); + + 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(). + * Key combinations involving the "meta" key (ie, the Windows or Command key) are meaningless to + * the VT100, so we ignore them. The "meta" key itself is already effectively ignored, because it's + * not acknowledged by getSoftCode(), but we also don't want any of the keys combined with "meta" + * slipping through either. */ - this.checkModifierKeys(softCode, fDown, event.location == Keys.LOCATION.RIGHT); - if (!event.metaKey) { + /* + * The LINE-FEED key is an important key on the VT100, and while we DO map a host function key + * to it (F7), I like the idea of making ALT-ENTER an alias for LINE-FEED as well. Ditto for + * making ALT-DELETE an alias for BACKSPACE (and no, I don't mean ALT-BACKSPACE as an alias for + * DELETE; see my earlier discussion involving BACKSPACE and DELETE). + * + * Of course, as experienced VT100 users know, it's always possible to type CTRL-J for LINE-FEED + * and CTRL-H for BACKSPACE, too. But not all our users are that experienced. + * + * I was also tempted to use CTRL-ENTER or SHIFT-ENTER, but those are composable VT100 key + * sequences, so it's best not to muck with those. + * + * Finally, this hack is complicated by the fact that if the ALT key is released first, we run + * the risk of the remapped key being stuck "down". Hence the new REMAPPED bit, which should + * remain set (as a "proxy" for the ALT bit) as long as a remapped key is down. + */ + var fRemapped = false; + if (this.bitsState & (Keyboard8080.STATE.ALTS | Keyboard8080.STATE.REMAPPED)) { + if (softCode == Keys.KEYCODE.CR) { + softCode = Keys.KEYCODE.F7; + fRemapped = true; + } + else if (softCode == Keys.KEYCODE.BS) { + softCode = Keys.KEYCODE.DEL; + fRemapped = true; + } + if (fRemapped) { + if (fDown) { + this.bitsState |= Keyboard8080.STATE.REMAPPED; + } else { + this.bitsState &= ~Keyboard8080.STATE.REMAPPED; + } + } + } fPass = this.onSoftKeyDown(softCode, fDown); /* * 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. + * which normally "suppresses" the keyPress event, as well as other unwanted browser behaviors + * (eg, the SPACE key, which browsers interpret as a desire to scroll the entire web page down). */ if (!(softCode >= Keys.ASCII.A && softCode <= Keys.ASCII.Z)) { event.preventDefault(); diff --git a/versions/pc8080/1.30.5/pc8080-dbg.js b/versions/pc8080/1.30.5/pc8080-dbg.js index 390f5997d..6154d40a0 100644 --- a/versions/pc8080/1.30.5/pc8080-dbg.js +++ b/versions/pc8080/1.30.5/pc8080-dbg.js @@ -146,14 +146,14 @@ function Zd(a){!a.u&&a.B&&yb(a.w,a.F,a.B,1)&&(a.u=!0);if(!mb(a)){if(!a.u)w("No R m+=String.fromCharCode(q)}ae(a,m);break;default:c=!1}c?Od.call(d):e&&(a.g("\nCP/M vector "+u(b)),I(d,b),e.pa());b=!0}else b=!1;return b}}(a))}Tc(a.b,a.Sa)}delete a.j}F(a)}}Yd.prototype.reset=function(){};function ae(a,b){b=b.replace(/\r/g,"");a.Ha&&(a.Ha.value+=b,a.Ha.scrollTop=a.Ha.scrollHeight)}Oa(function(){for(var a=D(document,"pc8080","ram"),b=0;b=n.Eb&&a<=n.Pb?d.u&515||(d.u|=512,ge(d,20,!0),he(d)):a>=n.Jd&&a<=n.z&&d.u&512&&(d.u&=-513,ge(d,20,!1),he(d));return!0},c.onpaste=function(a){a:{if(d.J&&d.J.fb&&(a.stopPropagation&&a.stopPropagation(),a.preventDefault&&a.preventDefault(), a=a.clipboardData||window.clipboardData)){ie(d.J,a.getData("Text"));a=!1;break a}a=!0}return a},!0;default:if(this.w.Ya&&void 0!==this.w.Ya[b])return this.M[e]=c,a=function(a,b){return function(c){c.preventDefault();ge(a,b,!0)}}(this,this.w.Ya[b]),b=function(a,b){return function(){ge(a,b,!1)}}(this,this.w.Ya[b]),"ontouchstart"in window?(c.ontouchstart=a,c.ontouchend=b):(c.onmousedown=a,c.onmouseup=c.onmouseout=b),!0}}return!1}; be.prototype.Qa=function(a,b,c,d){this.A=a;this.b=c;this.H=d;var e=this;this.O=Kc(this.b,function(){je(e)});this.F=ub(a,"ChipSet");this.J=ub(a,"SerialPort");Gb(b,this,this.w.Kb);Ub(b,this,this.w.Lb)};be.prototype.Fa=function(a,b){if(!b)if(!a)this.reset();else if(!this.restore(a))return!1;return!0};be.prototype.Ja=function(a){return a?this.save():!0};W.INIT=[[W.Na.INIT,W.Lc.INIT,!1,0,-1]];l=be.prototype;l.reset=function(){this.j=[];this.u=0;this.w.INIT&&!this.restore(this.w.INIT)&&this.ia("reset error")}; l.save=function(){var a=new Vc(this);switch(this.w.Ba){case W.Ba:a.set(0,[this.K,this.I,this.D,this.L,-1])}return a.data()};l.restore=function(a){var b;if(a&&(b=a[0])&&b.length)switch(this.w.Ba){case ee.Ba:return!0;case W.Ba:return this.K=b[0],he(this,this.K&W.Na.ec),this.I=b[1],this.D=b[2],this.L=b[3],this.B=b[4],!0}return!1}; function he(a,b){var c;null!=b?a.N=b:b=a.N;for(var d in a.w.tb)if(c="led-"+d,c=a.M[c]){var e=a.w.tb[d],f=!!(b&e);e&e-1&&(f=!(b&~e));c.style.backgroundColor=f?"#"+t(16711680,6):"#000000"}if(c=a.M["led-caps-lock"])c.style.backgroundColor=a.u&512?"#"+t(65280,6):"#000000"} -function fe(a,b,c){var d=!0,e;a:if(e=b.keyCode,e=a.w.$b[e]||e,!a.w.Nb[e]){for(var f in a.w.Ya)if(a.w.Ya[f]===e){e=f;break a}e=null}if(e){f=2==b.location;var g=0;switch(e){case 16:g=f?1:2;break;case 17:g=f?4:8;break;case 18:g=f?16:32;break;case 91:g=f?64:128;break;case 20:g=512}g&&(a.u=c?a.u|g:a.u&~g);b.metaKey||(d=ge(a,e,c),e>=n.Eb&&e<=n.Pb||b.preventDefault())}return d} +function fe(a,b,c){var d=!0,e=b.keyCode,f=2==b.location,g=0;switch(e){case 16:g=f?1:2;break;case 17:g=f?4:8;break;case 18:g=f?16:32;break;case 91:g=f?64:128;break;case 20:g=512}g&&(a.u=c?a.u|g:a.u&~g);var h;a:if(e=a.w.$b[e]||e,a.w.Nb[e])h=e;else{for(h in a.w.Ya)if(a.w.Ya[h]===e)break a;h=null}h&&!b.metaKey&&(d=!1,a.u&4144&&(13==h?(h=118,d=!0):8==h&&(h=46,d=!0),d&&(a.u=c?a.u|4096:a.u&-4097)),d=ge(a,h,c),h>=n.Eb&&h<=n.Pb||b.preventDefault());return d} function ge(a,b,c){var d,e;a:{for(e=0;ee?a.j.push({Xb:b,Ub:Date.now(),Hb:d}):(a.j[e].Ub=Date.now(),a.j[e].Hb=d);else if(0<=e){if(!a.j[e].Hb&&(d=a.j[e].Ub)&&50>Date.now()-d)return a.j[e].Hb=!0,je(a),!0;a.j.splice(e,1)}if(a.F){d=0;switch(b){case "1p":d=S.Za.od;break;case "2p":d=S.Za.sd;break;case "coin":d=S.Za.Pc;break;case "left":d=S.Za.qd;break;case "right":d=S.Za.rd;break;case "fire":d=S.Za.pd}d&&(a=a.F,b=d,a.D&=~b,c&&(a.D|=b))}return!0} function je(a){for(var b=0,c=-1;bc||c>e)c=e}else{ge(a,d,!1);b=0;continue}}b++}0<=c&&Lc(a.b,a.O,c)}l.Zd=function(a,b){var c=this.I;0<=this.B&&(this.B=m.Wa&&a<=m.kb?d.c&515||(d.c|=512,Dc(d,20,!0),Ec(d)):a>=m.Pc&&a<=m.z&&d.c&512&&(d.c&=-513,Dc(d,20,!1),Ec(d));return!0},c.onpaste=function(a){a:{if(d.h&&d.h.Ea&&(a.stopPropagation&&a.stopPropagation(),a.preventDefault&&a.preventDefault(), a=a.clipboardData||window.clipboardData)){Fc(d.h,a.getData("Text"));a=!1;break a}a=!0}return a},!0;default:if(this.b.Ba&&void 0!==this.b.Ba[b])return this.v[e]=c,a=function(a,b){return function(c){c.preventDefault();Dc(a,b,!0)}}(this,this.b.Ba[b]),b=function(a,b){return function(){Dc(a,b,!1)}}(this,this.b.Ba[b]),"ontouchstart"in window?(c.ontouchstart=a,c.ontouchend=b):(c.onmousedown=a,c.onmouseup=c.onmouseout=b),!0}}return!1}; xc.prototype.qa=function(a,b,c,d){this.w=a;this.a=c;this.M=d;var e=this;this.u=Jb(this.a,function(){Gc(e)});this.L=C(a,"ChipSet");this.h=C(a,"SerialPort");hb(b,this,this.b.fb);jb(b,this,this.b.gb)};xc.prototype.fa=function(a,b){if(!b)if(!a)this.reset();else if(!this.restore(a))return!1;return!0};xc.prototype.ha=function(a){return a?this.save():!0};Z.INIT=[[Z.ga.INIT,Z.Rb.INIT,!1,0,-1]];l=xc.prototype;l.reset=function(){this.i=[];this.c=0;this.b.INIT&&!this.restore(this.b.INIT)&&this.K("reset error")}; l.save=function(){var a=new G(this);switch(this.b.ba){case Z.ba:a.set(0,[this.m,this.l,this.g,this.o,-1])}return a.data()};l.restore=function(a){var b;if(a&&(b=a[0])&&b.length)switch(this.b.ba){case Ac.ba:return!0;case Z.ba:return this.m=b[0],Ec(this,this.m&Z.ga.vb),this.l=b[1],this.g=b[2],this.o=b[3],this.f=b[4],!0}return!1}; function Ec(a,b){var c;null!=b?a.s=b:b=a.s;for(var d in a.b.La)if(c="led-"+d,c=a.v[c]){var e=a.b.La[d],f=!!(b&e);e&e-1&&(f=!(b&~e));c.style.backgroundColor=f?"#"+p(16711680,6):"#000000"}if(c=a.v["led-caps-lock"])c.style.backgroundColor=a.c&512?"#"+p(65280,6):"#000000"} -function Cc(a,b,c){var d=!0,e;a:if(e=b.keyCode,e=a.b.qb[e]||e,!a.b.jb[e]){for(var f in a.b.Ba)if(a.b.Ba[f]===e){e=f;break a}e=null}if(e){f=2==b.location;var g=0;switch(e){case 16:g=f?1:2;break;case 17:g=f?4:8;break;case 18:g=f?16:32;break;case 91:g=f?64:128;break;case 20:g=512}g&&(a.c=c?a.c|g:a.c&~g);b.metaKey||(d=Dc(a,e,c),e>=m.Wa&&e<=m.kb||b.preventDefault())}return d} +function Cc(a,b,c){var d=!0,e=b.keyCode,f=2==b.location,g=0;switch(e){case 16:g=f?1:2;break;case 17:g=f?4:8;break;case 18:g=f?16:32;break;case 91:g=f?64:128;break;case 20:g=512}g&&(a.c=c?a.c|g:a.c&~g);var h;a:if(e=a.b.qb[e]||e,a.b.jb[e])h=e;else{for(h in a.b.Ba)if(a.b.Ba[h]===e)break a;h=null}h&&!b.metaKey&&(d=!1,a.c&4144&&(13==h?(h=118,d=!0):8==h&&(h=46,d=!0),d&&(a.c=c?a.c|4096:a.c&-4097)),d=Dc(a,h,c),h>=m.Wa&&h<=m.kb||b.preventDefault());return d} function Dc(a,b,c){var d,e;a:{for(e=0;ee?a.i.push({pb:b,mb:Date.now(),cb:d}):(a.i[e].mb=Date.now(),a.i[e].cb=d);else if(0<=e){if(!a.i[e].cb&&(d=a.i[e].mb)&&50>Date.now()-d)return a.i[e].cb=!0,Gc(a),!0;a.i.splice(e,1)}if(a.L){d=0;switch(b){case "1p":d=V.Ca.uc;break;case "2p":d=V.Ca.yc;break;case "coin":d=V.Ca.Vb;break;case "left":d=V.Ca.wc;break;case "right":d=V.Ca.xc;break;case "fire":d=V.Ca.vc}d&&(a=a.L,b=d,a.g&=~b,c&&(a.g|=b))}return!0} function Gc(a){for(var b=0,c=-1;bc||c>e)c=e}else{Dc(a,d,!1);b=0;continue}}b++}0<=c&&Kb(a.a,a.u,c)}l.bd=function(){var a=this.l;0<=this.f&&(this.f