diff --git a/_config.yml b/_config.yml index b0b3a7f1f..e20d5469e 100644 --- a/_config.yml +++ b/_config.yml @@ -6,7 +6,8 @@ description: > PCjs: The Original IBM PC in a Browser. Classic computer simulations in JavaScript. Includes an archive of historical PC software and publications. - Additional computer simulations in JavaScript include the 6502-based Ohio Scientific Challenger 1P. + Additional computer simulations in JavaScript include the 6502-based Ohio Scientific Challenger 1P and 8080-based + machines, including arcade machines such as Space Invaders. baseurl: "" # "/pcjs" when using http://jeffpar.github.io or "" when using http://www.pcjs.org url: "http://www.pcjs.org" # "http://jeffpar.github.io" or "http://www.pcjs.org" twitter_username: jeffpar diff --git a/devices/pc8080/machine/invaders/machine.xml b/devices/pc8080/machine/invaders/machine.xml index 246adab95..1cdd8a49b 100644 --- a/devices/pc8080/machine/invaders/machine.xml +++ b/devices/pc8080/machine/invaders/machine.xml @@ -18,6 +18,7 @@ + diff --git a/docs/pcjs/examples/pc-dbg.js b/docs/pcjs/examples/pc-dbg.js index ee3b4194e..a4564278d 100644 --- a/docs/pcjs/examples/pc-dbg.js +++ b/docs/pcjs/examples/pc-dbg.js @@ -94,7 +94,7 @@ function kd(a,b,c){var d=a.F,e=a.Lb,f=d.Sa.Z,g=d.Sa.Aa;if(!c){if(!(d.Sa.type&512 d.oa(h+g)));c&&d.jb(h+0,f);d.Za|=8;return!0}m.save=function(){return[this.Z,this.Aa,this.Ta,this.wb,this.id,this.lc,this.Lb,this.Gc,this.Xb,this.Oc,this.Ba,this.ba,this.R,this.type,this.Zb]};m.restore=function(a){"number"==typeof a?this.load(a):(this.Z=a[0],this.Aa=a[1],this.Ta=a[2],this.wb=a[3],this.id=a[4],this.lc=a[5],this.Lb=a[6],this.Gc=a[7],this.Xb=a[8],this.Oc=a[9]||2,this.Ba=a[10]||65535,this.ba=a[11]||2,this.R=a[12]||65535,this.type=a[13]||this.wb&7936,this.Zb=a[14]||(this.Ta>>>0)+1)}; function ed(a,b,c,d){void 0===c&&(c=!!(a.F.Za&1));a.cd=!1;if(c)if(a.load=a.tg,a.Wi=a.fm,a.nc=a.Ci,a.oc=a.Ei,void 0===d&&(d=!!(a.F.ca&131072)),d)a.load=a.V,a.nc=a.ea,a.oc=a.qa,a.Lb=a.Gc=3,a.ba=a.Oc=2,a.R=a.Ba=65535,a.Ta=65535,a.Zb=a.Ta+1,a.Oc=a.ba,a.Xb=-1,a.cf=!1;else{if(!(a.Z&-4))a.nc=a.og,a.oc=a.pg;else if(a.type&4096){6144==(a.type&6656)&&(a.nc=a.og);if(a.type&2048||!(a.type&512))a.oc=a.pg;1024==(a.type&3072)&&(a.nc==a.Ci&&(a.nc=a.Ik),a.oc==a.Ei&&(a.oc=a.Jk),a.cd=!0);b&&6>a.id&&a.Z&-4&&-1!==a.Xb&& (c=a.Xb+5,d=a.F.Qa(c),d&1||a.F.qc(c,d|1))}b&&(a.Lb=a.Z&3,a.Gc=(a.wb&24576)>>13,80386>a.F.ja||!(a.ext&64)?(a.ba=2,a.R=65535):(a.ba=4,a.R=-1),a.Oc=a.ba,a.Ba=a.R)}else a.load=a.lh,a.Wi=a.gm,a.nc=a.Di,a.oc=a.Fi,a.Lb=a.Gc=0,a.Xb=-1,a.cf=!1} -function Ad(a){this.ja=a.model||8088;var b=a.stepping;this.Uc=this.ja+(b?ca(b,16):0);b=0;switch(this.ja){default:b=4772727;break;case 80286:b=6E6;break;case 80386:b=16E6}Lc.call(this,a,b);this.Yf=61442;this.Cc=1792;this.Wf=28672;this.Dd=4;this.ra=255;this.B=80286<=this.ja?xb:wb;this.X=Bd;this.bg=Cd;this.gg=Dd;this.hg=Ed;if(80186<=this.ja&&(this.X=Bd.slice(),this.bg=Cd.slice(),this.gg=Dd.slice(),this.ra=31,this.X[15]=Fd,this.X[96]=Gd,this.X[97]=Hd,this.X[98]=Id,this.X[99]=Fd,this.X[100]=Fd,this.X[101]= +function Ad(a){this.ja=+a.model||8088;var b=a.stepping;this.Uc=this.ja+(b?ca(b,16):0);b=0;switch(this.ja){default:b=4772727;break;case 80286:b=6E6;break;case 80386:b=16E6}Lc.call(this,a,b);this.Yf=61442;this.Cc=1792;this.Wf=28672;this.Dd=4;this.ra=255;this.B=80286<=this.ja?xb:wb;this.X=Bd;this.bg=Cd;this.gg=Dd;this.hg=Ed;if(80186<=this.ja&&(this.X=Bd.slice(),this.bg=Cd.slice(),this.gg=Dd.slice(),this.ra=31,this.X[15]=Fd,this.X[96]=Gd,this.X[97]=Hd,this.X[98]=Id,this.X[99]=Fd,this.X[100]=Fd,this.X[101]= Fd,this.X[102]=Fd,this.X[103]=Fd,this.X[104]=Jd,this.X[105]=Kd,this.X[106]=Ld,this.X[107]=Md,this.X[108]=Nd,this.X[109]=Od,this.X[110]=Pd,this.X[111]=Qd,this.X[192]=Rd,this.X[193]=Sd,this.X[200]=Td,this.X[201]=Ud,this.X[241]=Yd,this.bg[7]=Zd,this.gg[7]=Zd,80286<=this.ja)){this.Yf=2;this.Cc|=28672;this.Dd=0;this.X[15]=$d;this.tc=ae.slice();for(a=0;a=this.Uc&&(this.tc[166]=ne,this.tc[167]=oe)}}this.Id=[];this.le=[];this.ne=0;Oc(this);this.la.Wd=this.la.Mi=!1;this.Pg=0;this.ld=this.sa=[];this.La=this.Sb=this.Fa=this.Gd=this.jd=this.Hb=this.Db=0;pe(this)}db(Ad,Lc);function qe(a,b,c,d){Gc((d?a.ld:a.sa)[b>>>a.La],c);d&&Ub(a)} function re(a){var b;if(a.sa===a.ld){a.sa=Array(a.Gd);a.me=new B(null,0,0,5,null,a);Mb(a.me,a.ha);for(b=0;ba.oe&&(a.ag[a.oe++]=d);a.sa[b]=a.me}a.Hd=[]}function Ub(a){a.Za&-2147483648&&re(a)} @@ -461,7 +461,7 @@ function vm(a,b,c){var d=!0,e=!1,f=!1,g=b.keyCode,h=Em(a,g,!0);a.bh&&h==X["`"]&& function Dm(a,b,c){var d=!1;xm(a,b,!0,c);var e=Y[b]||Y[b+1E3];if(void 0!==e){14==e&&40==(a.xc&40)&&(e=83);var d=[],f=e&255;if(83=b||97<=b&&122>=b;e>>>=8;){var g=0,h=e&255;224==f||225==f?d.push(f|(c?0:128)):(42==h?a.C&3||a.C&512&&b||(g=h):29==h?a.C&12||(g=h):56==h?a.C&48||(g=h):d.push(f|(c?0:128)),g&&(c?d.unshift(g):d.push(g|128)))}for(c=0;ce.A.length?(A(e)&&y(e,"scan code "+t(f)+" buffered"),e.A.push(f),1== e.A.length&&e.N&&Wl(e.N,f)):(20==e.A.length&&e.A.push(255),y(e,"scan code buffer overflow")));d=!0}return d}Qa(function(){for(var a=nb(document,"pcjs","keyboard"),b=0;b>>0)+1)}; function zc(a,b,c,d){void 0===c&&(c=!!(a.A.Pa&1));a.zd=!1;if(c)if(a.load=a.Cf,a.Wh=a.Tk,a.Qb=a.Ih,a.Rb=a.Kh,void 0===d&&(d=!!(a.A.X&131072)),d)a.load=a.L,a.Qb=a.I,a.Rb=a.K,a.sb=a.nc=3,a.U=a.Nc=2,a.N=a.sa=65535,a.Za=65535,a.C=a.Za+1,a.Nc=a.U,a.hc=-1,a.pe=!1;else{if(!(a.ja&-4))a.Qb=a.wf,a.Rb=a.xf;else if(a.type&4096){6144==(a.type&6656)&&(a.Qb=a.wf);if(a.type&2048||!(a.type&512))a.Rb=a.xf;1024==(a.type&3072)&&(a.Qb==a.Ih&&(a.Qb=a.zj),a.Rb==a.Kh&&(a.Rb=a.Aj),a.zd=!0);b&&6>a.id&&a.ja&-4&&-1!==a.hc&&(c= a.hc+5,d=a.A.Kb(c),d&1||a.A.Mc(c,d|1))}b&&(a.sb=a.ja&3,a.nc=(a.lb&24576)>>13,80386>a.A.ca||!(a.ext&64)?(a.U=2,a.N=65535):(a.U=4,a.N=-1),a.Nc=a.U,a.sa=a.N)}else a.load=a.Xh,a.Wh=a.Uk,a.Qb=a.Jh,a.Rb=a.Lh,a.sb=a.nc=0,a.hc=-1,a.pe=!1} -function Qc(a){this.ca=a.model||8088;var b=a.stepping;this.pd=this.ca+(b?ca(b,16):0);b=0;switch(this.ca){default:b=4772727;break;case 80286:b=6E6;break;case 80386:b=16E6}hc.call(this,a,b);this.rh=61442;this.od=1792;this.qh=28672;this.ae=4;this.Da=255;this.B=80286<=this.ca?fb:eb;this.ma=Rc;this.uh=Sc;this.vh=Tc;this.wh=Uc;if(80186<=this.ca&&(this.ma=Rc.slice(),this.uh=Sc.slice(),this.vh=Tc.slice(),this.Da=31,this.ma[15]=Vc,this.ma[96]=Wc,this.ma[97]=Xc,this.ma[98]=Yc,this.ma[99]=Vc,this.ma[100]=Vc, +function Qc(a){this.ca=+a.model||8088;var b=a.stepping;this.pd=this.ca+(b?ca(b,16):0);b=0;switch(this.ca){default:b=4772727;break;case 80286:b=6E6;break;case 80386:b=16E6}hc.call(this,a,b);this.rh=61442;this.od=1792;this.qh=28672;this.ae=4;this.Da=255;this.B=80286<=this.ca?fb:eb;this.ma=Rc;this.uh=Sc;this.vh=Tc;this.wh=Uc;if(80186<=this.ca&&(this.ma=Rc.slice(),this.uh=Sc.slice(),this.vh=Tc.slice(),this.Da=31,this.ma[15]=Vc,this.ma[96]=Wc,this.ma[97]=Xc,this.ma[98]=Yc,this.ma[99]=Vc,this.ma[100]=Vc, this.ma[101]=Vc,this.ma[102]=Vc,this.ma[103]=Vc,this.ma[104]=Zc,this.ma[105]=$c,this.ma[106]=ad,this.ma[107]=bd,this.ma[108]=cd,this.ma[109]=dd,this.ma[110]=ed,this.ma[111]=fd,this.ma[192]=gd,this.ma[193]=hd,this.ma[200]=id,this.ma[201]=jd,this.ma[241]=kd,this.uh[7]=ld,this.vh[7]=ld,80286<=this.ca)){this.rh=2;this.od|=28672;this.ae=0;this.ma[15]=md;this.Zc=nd.slice();for(a=0;a=this.pd&&(this.Zc[166]=wd,this.Zc[167]=xd)}}this.of=[];this.th=[];this.Vf=0;kc(this);this.da.Le=this.da.Hj=!1;this.yh=0;this.vc=this.S=[];this.ea=this.xb=this.pb=this.mf=this.Yd=this.Fa=this.oc=0;yd(this)}Qa(Qc,hc); function Bb(a){var b;if(a.S===a.vc){a.S=Array(a.mf);a.pf=new q(null,0,0,5,null,a);sb(a.pf,a.Ia);for(b=0;ba.qf&&(a.sh[a.qf++]=d);a.S[b]=a.pf}a.nf=[]} @@ -433,10 +433,10 @@ function vk(a,b,c){var d=!0,e=!1,f=!1,g=b.keyCode,h=Ek(a,g,!0);a.ig&&h==U["`"]&& function Dk(a,b,c){var d=!1;xk(a,b,!0,c);var e=X[b]||X[b+1E3];if(void 0!==e){14==e&&40==(a.bc&40)&&(e=83);var d=[],f=e&255;if(83=b||97<=b&&122>=b;e>>>=8;){var g=0,h=e&255;224==f||225==f?d.push(f|(c?0:128)):(42==h?a.C&3||a.C&512&&b||(g=h):29==h?a.C&12||(g=h):56==h?a.C&48||(g=h):d.push(f|(c?0:128)),g&&(c?d.unshift(g):d.push(g|128)))}for(c=0;ce.Pb.length?(e.Pb.push(f),1==e.Pb.length&&e.R&&Yj(e.R,f)):20==e.Pb.length&& e.Pb.push(255));d=!0}return d}Ea(function(){for(var a=Za(document,"pcjs","keyboard"),b=0;b 39 Keyboard.STUPID_KEYCODES[Keyboard.KEYCODE.FF_DASH] = Keyboard.ASCII['-']; +/** + * Maps SOFTCODE (string) to KEYCODE (number). + * + * @enum {number} + */ +Keyboard.SOFTCODES = { + '1p': Keyboard.KEYCODE.ONE, + '2p': Keyboard.KEYCODE.TWO, + 'coin': Keyboard.KEYCODE.FIVE, + 'left': Keyboard.KEYCODE.LEFT, + 'right': Keyboard.KEYCODE.RIGHT, + 'fire': Keyboard.KEYCODE.SPACE +}; + +/** + * Alternate keyCode mappings (to support the popular WASD directional mappings) + * + * TODO: ES6 computed property name support may now be in all mainstream browsers, allowing us to use + * a simple object literal for this and all other object initializations. + */ +Keyboard.ALTCODES = {}; +Keyboard.ALTCODES[Keyboard.ASCII.A] = Keyboard.KEYCODE.LEFT; +Keyboard.ALTCODES[Keyboard.ASCII.D] = Keyboard.KEYCODE.RIGHT; +Keyboard.ALTCODES[Keyboard.ASCII.L] = Keyboard.KEYCODE.SPACE; + +/** + * getSoftCode(keyCode) + * + * @this {Keyboard} + * @return {string|null} + */ +Keyboard.prototype.getSoftCode = function(keyCode) +{ + keyCode = Keyboard.ALTCODES[keyCode] || keyCode; + for (var sSoftCode in Keyboard.SOFTCODES) { + if (Keyboard.SOFTCODES[sSoftCode] === keyCode) { + return sSoftCode; + } + } + return null; +}; + +/** + * reset() + * + * @this {Keyboard} + */ +Keyboard.prototype.reset = function() +{ + /* + * As SOFTCODE keyDown events are encountered, a corresponding property is set to true in + * keysPressed, and as SOFTCODE keyUp events are encountered, the property is set to false. + */ + this.keysPressed = {}; +}; + /** * setBinding(sHTMLType, sBinding, control, sValue) * @@ -257,68 +324,24 @@ Keyboard.prototype.setBinding = function(sHTMLType, sBinding, control, sValue) control.onkeydown = function onKeyDown(event) { return kbd.onKeyDown(event, true); }; - control.onkeypress = function onKeyPressKbd(event) { - return kbd.onKeyPress(event); - }; control.onkeyup = function onKeyUp(event) { return kbd.onKeyDown(event, false); }; return true; - case "caps-lock": - this.bindings[id] = control; - control.onclick = function onClickCapsLock(event) { - if (kbd.cmp) kbd.cmp.updateFocus(); - return kbd.toggleCapsLock(); - }; - return true; - - case "num-lock": - this.bindings[id] = control; - control.onclick = function onClickNumLock(event) { - if (kbd.cmp) kbd.cmp.updateFocus(); - return kbd.toggleNumLock(); - }; - return true; - - case "scroll-lock": - this.bindings[id] = control; - control.onclick = function onClickScrollLock(event) { - if (kbd.cmp) kbd.cmp.updateFocus(); - return kbd.toggleScrollLock(); - }; - return true; - default: - /* - * Maintain support for older button codes; eg, map button code "ctrl-c" to CLICKCODE "CTRL_C" - */ - var sCode = sBinding.toUpperCase().replace(/-/g, '_'); - if (Keyboard.CLICKCODES[sCode] !== undefined && sHTMLType == "button") { + if (Keyboard.SOFTCODES[sBinding] !== undefined) { this.bindings[id] = control; - control.onclick = function(kbd, sKey, simCode) { - return function onClickKeyboard(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 - kbd.addActiveKey(simCode, true); - }; - }(this, sCode, Keyboard.CLICKCODES[sCode]); - return true; - } - else if (Keyboard.SOFTCODES[sBinding] !== undefined) { - this.cSoftCodes++; - this.bindings[id] = control; - var fnDown = function(kbd, sKey, simCode) { + var fnDown = function(kbd, sSoftCode) { return function onMouseOrTouchDownKeyboard(event) { - kbd.addActiveKey(simCode); + kbd.onSoftKeyDown(sSoftCode, true); }; - }(this, sBinding, Keyboard.SOFTCODES[sBinding]); - var fnUp = function (kbd, sKey, simCode) { + }(this, sBinding); + var fnUp = function (kbd, sSoftCode) { return function onMouseOrTouchUpKeyboard(event) { - kbd.removeActiveKey(simCode); + kbd.onSoftKeyDown(sSoftCode, false); }; - }(this, sBinding, Keyboard.SOFTCODES[sBinding]); + }(this, sBinding); if ('ontouchstart' in window) { control.ontouchstart = fnDown; control.ontouchend = fnUp; @@ -328,19 +351,92 @@ Keyboard.prototype.setBinding = function(sHTMLType, sBinding, control, sValue) } return true; } - 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). - */ - this.bindings[id] = control; - control.onclick = function onClickTest(event) { - if (kbd.cmp) kbd.cmp.updateFocus(); - return kbd.injectKeys(sValue); - }; - return true; - } + break; + } + } + return false; +}; + +/** + * initBus(cmp, bus, cpu, dbg) + * + * @this {Keyboard} + * @param {Computer} cmp + * @param {Bus} bus + * @param {CPUState} cpu + * @param {Debugger} dbg + */ +Keyboard.prototype.initBus = function(cmp, bus, cpu, dbg) +{ + this.dbg = dbg; // NOTE: The "dbg" property must be set for the message functions to work + this.chipset = cmp.getMachineComponent("ChipSet"); +}; + +/** + * onKeyDown(event, fDown) + * + * @this {Keyboard} + * @param {Object} event + * @param {boolean} fDown is true for a keyDown event, false for a keyUp event + * @return {boolean} true to pass the event along, false to consume it + */ +Keyboard.prototype.onKeyDown = function(event, fDown) +{ + var fPass = true; + var keyCode = event.keyCode; + var sSoftCode = this.getSoftCode(keyCode); + + if (sSoftCode) { + fPass = this.onSoftKeyDown(sSoftCode, fDown); + } + + if (!fPass) { + event.preventDefault(); + } + + if (!COMPILED && this.messageEnabled(Messages.KEYS)) { + this.printMessage("onKey" + (fDown? "Down" : "Up") + "(" + keyCode + "): " + (fPass? "true" : "false"), true); + } + + return fPass; +}; + +/** + * onSoftKeyDown(sSoftCode, fDown) + * + * @this {Keyboard} + * @param {string} sSoftCode + * @param {boolean} fDown is true for a down event, false for an up event + * @return {boolean} true to pass the event along, false to consume it + */ +Keyboard.prototype.onSoftKeyDown = function(sSoftCode, fDown) +{ + this.keysPressed[sSoftCode] = fDown; + + if (this.chipset) { + switch(sSoftCode) { + case '1p': + this.chipset.updateStatus1(ChipSet.SI_1978.STATUS1.P1, fDown); + break; + + case '2p': + this.chipset.updateStatus1(ChipSet.SI_1978.STATUS1.P2, fDown); + break; + + case 'coin': + this.chipset.updateStatus1(ChipSet.SI_1978.STATUS1.CREDIT, fDown); + break; + + case 'left': + this.chipset.updateStatus1(ChipSet.SI_1978.STATUS1.P1_LEFT, fDown); + break; + + case 'right': + this.chipset.updateStatus1(ChipSet.SI_1978.STATUS1.P1_RIGHT, fDown); + break; + + case 'fire': + this.chipset.updateStatus1(ChipSet.SI_1978.STATUS1.P1_FIRE, fDown); break; } } diff --git a/modules/pc8080/lib/video.js b/modules/pc8080/lib/video.js index 621c9fb13..84c700b75 100644 --- a/modules/pc8080/lib/video.js +++ b/modules/pc8080/lib/video.js @@ -245,6 +245,16 @@ Video.prototype.initBus = function(cmp, bus, cpu, dbg) this.nPixelsPerCell = (16 / this.nBitsPerPixel)|0; this.initCache(); } + + /* + * 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"); + if (this.kbd && this.canvasScreen) { + this.kbd.setBinding(this.textareaScreen? "textarea" : "canvas", "kbd", this.inputScreen); + } + this.setReady(); }; diff --git a/modules/pcjs/lib/keyboard.js b/modules/pcjs/lib/keyboard.js index 71cca2663..db3a85286 100644 --- a/modules/pcjs/lib/keyboard.js +++ b/modules/pcjs/lib/keyboard.js @@ -566,6 +566,11 @@ Keyboard.KEYSTATES[Keyboard.SIMCODE.SCROLL_LOCK] = Keyboard.STATE.SCROLL_LOCK; /** * Maps CLICKCODE (string) to SIMCODE (number). * + * NOTE: Unlike SOFTCODES, CLICKCODES are upper-case and use underscores instead of dashes, so that this + * and other components can reference them using "dot" property syntax; using upper-case merely adheres to + * our convention for constants. setBinding() will automatically convert any incoming CLICKCODE bindings + * that use lower-case and dashes to upper-case and underscores before performing property lookup. + * * @enum {number} */ Keyboard.CLICKCODES = { diff --git a/modules/pcjs/lib/video.js b/modules/pcjs/lib/video.js index 5838ee74b..1f2b36d9a 100644 --- a/modules/pcjs/lib/video.js +++ b/modules/pcjs/lib/video.js @@ -165,7 +165,7 @@ function Video(parmsVideo, canvas, context, textarea, container) */ var fSmoothing = parmsVideo['smoothing']; var sSmoothing = Component.parmsURL['smoothing']; - if (sSmoothing) fSmoothing = (sSmoothing == "true")? true : false; + if (sSmoothing) fSmoothing = (sSmoothing == "true"); if (fSmoothing != null) { for (i = 0; i < asWebPrefixes.length; i++) { sEvent = asWebPrefixes[i]; @@ -4560,8 +4560,8 @@ Video.prototype.removeCursor = function() /* * If we're using an off-screen buffer in text mode, then we need to keep it in sync with "reality". */ - if (this.contextScreenBuffer) { - this.updateChar(col, row, data, this.contextScreenBuffer); + if (this.contextBuffer) { + this.updateChar(col, row, data, this.contextBuffer); } /* * While updating the on-screen canvas directly could open us up to potential subpixel artifacts again, @@ -4856,11 +4856,11 @@ Video.prototype.setDimensions = function() } /* - * In text modes, we have the option of setting all the *ScreenBuffer variables to null instead of + * In text modes, we have the option of setting all the *Buffer variables to null instead of * allocating them, because updateChar(), as currently written, is capable of writing characters to * either an off-screen or on-screen context. * - * this.imageScreenBuffer = this.canvasScreenBuffer = this.contextScreenBuffer = null; + * this.imageBuffer = this.canvasBuffer = this.contextBuffer = null; */ this.cxBuffer = this.cyBuffer = 0; if (font) { @@ -4888,11 +4888,11 @@ Video.prototype.setDimensions = function() /* * Allocate the off-screen buffers */ - this.imageScreenBuffer = this.contextScreen.createImageData(this.cxBuffer, this.cyBuffer); - this.canvasScreenBuffer = document.createElement("canvas"); - this.canvasScreenBuffer.width = this.cxBuffer; - this.canvasScreenBuffer.height = this.cyBuffer; - this.contextScreenBuffer = this.canvasScreenBuffer.getContext("2d"); + this.imageBuffer = this.contextScreen.createImageData(this.cxBuffer, this.cyBuffer); + this.canvasBuffer = document.createElement("canvas"); + this.canvasBuffer.width = this.cxBuffer; + this.canvasBuffer.height = this.cyBuffer; + this.contextBuffer = this.canvasBuffer.getContext("2d"); /* * Since cxCell and cyCell were originally defined in terms of cxScreen/nCols and cyScreen/nRows, you might think @@ -5676,7 +5676,7 @@ Video.prototype.updateScreenText = function(addrScreen, addrScreenLimit, iCell, if (!this.fCellCacheValid || data !== this.aCellCache[iCell]) { var col = iCell % this.nCols; var row = (iCell / this.nCols)|0; - this.updateChar(col, row, data, this.contextScreenBuffer); + this.updateChar(col, row, data, this.contextBuffer); this.aCellCache[iCell] = data; cUpdated++; } @@ -5686,8 +5686,8 @@ Video.prototype.updateScreenText = function(addrScreen, addrScreenLimit, iCell, this.fCellCacheValid = true; - if (cUpdated && this.contextScreenBuffer) { - this.contextScreen.drawImage(this.canvasScreenBuffer, 0, 0, this.cxBuffer, this.cyBuffer, this.xScreenOffset, this.yScreenOffset, this.cxScreenOffset, this.cyScreenOffset); + if (cUpdated && this.contextBuffer) { + this.contextScreen.drawImage(this.canvasBuffer, 0, 0, this.cxBuffer, this.cyBuffer, this.xScreenOffset, this.yScreenOffset, this.cxScreenOffset, this.cyScreenOffset); } }; @@ -5725,7 +5725,7 @@ Video.prototype.updateScreenGraphicsCGA = function(addrScreen, addrScreenLimit) if (x < xDirty) xDirty = x; for (var iPixel = 0; iPixel < nPixelsPerCell; iPixel++) { var bPixel = (wPixels & (wMask >>= nPixelShift)) >> (nShift -= nPixelShift); - this.setPixel(this.imageScreenBuffer, x++, y, aPixelColors[bPixel]); + this.setPixel(this.imageBuffer, x++, y, aPixelColors[bPixel]); } if (x > xMaxDirty) xMaxDirty = x; if (y < yDirty) yDirty = y; @@ -5748,30 +5748,30 @@ Video.prototype.updateScreenGraphicsCGA = function(addrScreen, addrScreenLimit) this.fCellCacheValid = true; /* - * Instead of blasting the ENTIRE imageScreenBuffer into contextScreenBuffer, and then blasting the ENTIRE - * canvasScreenBuffer onto contextScreen, even for the smallest change, let's try to be a bit smarter about + * Instead of blasting the ENTIRE imageBuffer into contextBuffer, and then blasting the ENTIRE + * canvasBuffer onto contextScreen, even for the smallest change, let's try to be a bit smarter about * the update (well, to the extent that the canvas APIs permit). */ if (xDirty < this.nCols) { var cxDirty = xMaxDirty - xDirty; var cyDirty = yMaxDirty - yDirty; - // this.contextScreenBuffer.putImageData(this.imageScreenBuffer, 0, 0); - this.contextScreenBuffer.putImageData(this.imageScreenBuffer, 0, 0, xDirty, yDirty, cxDirty, cyDirty); + // this.contextBuffer.putImageData(this.imageBuffer, 0, 0); + this.contextBuffer.putImageData(this.imageBuffer, 0, 0, xDirty, yDirty, cxDirty, cyDirty); /* - * While ideally I would draw only the dirty portion of canvasScreenBuffer, there usually isn't a 1-1 pixel mapping - * between canvasScreenBuffer and contextScreen. In fact, the WHOLE POINT of the canvasScreenBuffer is to leverage + * While ideally I would draw only the dirty portion of canvasBuffer, there usually isn't a 1-1 pixel mapping + * between canvasBuffer and contextScreen. In fact, the WHOLE POINT of the canvasBuffer is to leverage * drawImage()'s scaling ability; for example, a CGA graphics mode might be 640x200, whereas the canvas representing * the screen might be 960x400. In those situations, if we draw interior rectangles, we often end up with subpixel - * artifacts along the edges of those rectangles. So it appears I must continue to redraw the entire canvasScreenBuffer + * artifacts along the edges of those rectangles. So it appears I must continue to redraw the entire canvasBuffer * on every change. * var xScreen = (((xDirty * this.cxScreen) / this.nCols) | 0); var yScreen = (((yDirty * this.cyScreen) / this.nRows) | 0); var cxScreen = (((cxDirty * this.cxScreen) / this.nCols) | 0); var cyScreen = (((cyDirty * this.cyScreen) / this.nRows) | 0); - this.contextScreen.drawImage(this.canvasScreenBuffer, xDirty, yDirty, cxDirty, cyDirty, xScreen, yScreen, cxScreen, cyScreen); + this.contextScreen.drawImage(this.canvasBuffer, xDirty, yDirty, cxDirty, cyDirty, xScreen, yScreen, cxScreen, cyScreen); */ - this.contextScreen.drawImage(this.canvasScreenBuffer, 0, 0, this.nCols, this.nRows, 0, 0, this.cxScreen, this.cyScreen); + this.contextScreen.drawImage(this.canvasBuffer, 0, 0, this.nCols, this.nRows, 0, 0, this.cxScreen, this.cyScreen); } }; @@ -5863,7 +5863,7 @@ Video.prototype.updateScreenGraphicsEGA = function(addrBuffer, addrScreen, addrS * index, which is what this code requires. */ var bPixel = Video.aEGADWToByte[dwPixel] || 0; - this.setPixel(this.imageScreenBuffer, x++, y, aPixelColors[bPixel]); + this.setPixel(this.imageBuffer, x++, y, aPixelColors[bPixel]); data <<= 1; } if (x > xMaxDirty) xMaxDirty = x; @@ -5888,8 +5888,8 @@ Video.prototype.updateScreenGraphicsEGA = function(addrBuffer, addrScreen, addrS if (xDirty < this.nCols) { var cxDirty = xMaxDirty - xDirty; var cyDirty = yMaxDirty - yDirty; - this.contextScreenBuffer.putImageData(this.imageScreenBuffer, 0, 0, xDirty, yDirty, cxDirty, cyDirty); - this.contextScreen.drawImage(this.canvasScreenBuffer, 0, 0, this.nCols, this.nRows, 0, 0, this.cxScreen, this.cyScreen); + this.contextBuffer.putImageData(this.imageBuffer, 0, 0, xDirty, yDirty, cxDirty, cyDirty); + this.contextScreen.drawImage(this.canvasBuffer, 0, 0, this.nCols, this.nRows, 0, 0, this.cxScreen, this.cyScreen); } }; @@ -5957,7 +5957,7 @@ Video.prototype.updateScreenGraphicsVGA = function(addrBuffer, addrScreen, addrS if (nPixels) { if (x < xDirty) xDirty = x; for (iPixel = 0; iPixel < nPixels; iPixel++) { - this.setPixel(this.imageScreenBuffer, x++, y, aPixelColors[data & 0xff]); + this.setPixel(this.imageBuffer, x++, y, aPixelColors[data & 0xff]); data >>= 8; } if (x > xMaxDirty) xMaxDirty = x; @@ -5984,8 +5984,8 @@ Video.prototype.updateScreenGraphicsVGA = function(addrBuffer, addrScreen, addrS if (xDirty < this.nCols) { var cxDirty = xMaxDirty - xDirty; var cyDirty = yMaxDirty - yDirty; - this.contextScreenBuffer.putImageData(this.imageScreenBuffer, 0, 0, xDirty, yDirty, cxDirty, cyDirty); - this.contextScreen.drawImage(this.canvasScreenBuffer, 0, 0, this.nCols, this.nRows, 0, 0, this.cxScreen, this.cyScreen); + this.contextBuffer.putImageData(this.imageBuffer, 0, 0, xDirty, yDirty, cxDirty, cyDirty); + this.contextScreen.drawImage(this.canvasBuffer, 0, 0, this.nCols, this.nRows, 0, 0, this.cxScreen, this.cyScreen); } };