diff --git a/_posts/2016-02-17-saving-disks-and-machines.md b/_posts/2016-02-17-saving-disks-and-machines.md new file mode 100644 index 000000000..96d622298 --- /dev/null +++ b/_posts/2016-02-17-saving-disks-and-machines.md @@ -0,0 +1,121 @@ +--- +layout: post +title: "Saving Disks and Machines" +date: 2016-02-17 14:00:00 +permalink: /blog/2016/02/17/ +--- + +PCjs (v1.20.9) now offers some new, *much* easier ways to save disks and machines, thanks to the new +[Save Disk](#saving-disks) and [Save Machine](#saving-machines) features. With one click, PCjs can now +generate a single download containing everything you need to embed any of our IBM PC demos on your own +web page. + +Saving Disks +--- + +Floppy disk images can now be saved to your desktop computer by simply clicking the **Save** button next +to the floppy disk controls. Select the drive first, and then whatever diskette is shown as being "loaded" +in that drive will be saved in your local machine's Downloads folder when you click **Save**. + +If you made any changes to that disk after it was loaded, those changes will be included, so if you want a pristine +copy of the disk, click the **Load** button first. PCjs will ask you to confirm that you really want to reload the +disk and discard any changes. + +Note that the disk *should* be downloaded as an **.img** file, which is nothing more than a sector-by-sector binary +dump of the disk. For example, a 360Kb double-sided double-density (DSDD) disk contains 9 512-byte sectors on each +of the 40 tracks on each of its 2 sides, so when you **Save** a 360Kb disk, the downloaded file should be exactly +368,640 bytes large. + +The Mac OS X operating system can automatically mount most **.img** files (from disks created by DOS 2.0 or later). +Older 1.x DOS disks, along with most non-DOS disks, do not have a BIOS Parameter Block (BPB) in the boot sector, so +most modern operating systems won't recognize the disk format. Other operating systems, like Windows, may require +third-party software in order to mount an **.img** file, and some third-party software may prefer a different extension, +such as **.ima** or **.bin**. + +It's also recommended that make your **.img** files *read-only*, so that if you do mount them on your desktop +computer, neither you nor the operating system will inadvertently modify the contents of the disk. On OS X, this is +easily done with the **chmod** utility. + +For example, if you saved the disk named "PC-DOS 2.00 (Disk 1)", it should have been downloaded as "PCDOS200-DISK2.img" +in your Downloads folder, so the OS X Terminal command `chmod -w PCDOS200-DISK2.img` will make it read-only, and +`chmod +w PCDOS200-DISK2.img` will make it writable again. + +Saving Machines +--- + +Saving the entire state of any existing IBM PC machine is also possible with a single click. You can choose to save +a machine in its initial state, or make changes to any of the machine's disks and then save it. All your changes should +be preserved. + +Under the bottom-left corner of any IBM PC on the PCjs [website](http://www.pcjs.org/), you should now see a +[**Save Machine**] link. When you click that link, PCjs will generate a large "blob" of JavaScript containing +everything that machine needs to run, including: + + * The machine XML configuration file (eg, "machine.xml") + * The machine XSL transformation file (eg, "components.xsl") + * The machine CSS stylesheet file (eg, "components.css") + * The machine state file (eg, "state.json") + * The PCjs machine emulation script (eg, "pc.js") + * Copies of all the disk images mounted by the machine + +Let's say you want to save the IBM PC on the PCjs [home page](http://www.pcjs.org/). When you click +**Save Machine**, two things should happen: + + * A file will be downloaded ("pc.json") + * A popup will appear with some markup to copy-and-paste + +The popup should provide the following information: + + Check your Downloads folder for "pc.json", copy it to your web server as "pc.js", + and then add the following to your web page: + +
+ ... + + + + The machine should appear where the
is located. + +Rename the downloaded file from **pc.json** to **pc.js**, copy it to your own web server, then create or edit +a web page and insert the above text. If **pc.js** and your web page are in different folders, then you'll also +need to change *src* to indicate the location of **pc.js**. + +Some notes: + + * PCjs asks the browser to name the downloaded file **pc.json** instead of **pc.js** because a file with a ".js" + extension may cause the Chrome web browser to block the download. And not all browsers support named downloads; + PCjs will attempt to open a new window/tab if that seems to be the case. + + * Your version of Chrome may also impose size limitations on the download. If nothing downloads, the machine may + be too large for your browser, so try a different browser (eg, Firefox or Safari). + + * If you have modified any floppy disks mounted by the machine *or* any of the machine's hard disks, those + modifications should be saved along with the machine. + + * Any floppy disks mounted during the lifetime of the machine will be added to the machine's state. So, for example, + if you want your copy of the machine to include all the Windows 1.01 SDK disks, make sure you have loaded each disk + once before clicking **Save Machine**. + + * Any floppy disks *not* mounted during the lifetime of the machine will be *removed* from the machine's list of + available disks; we don't want machines running on other websites to be consuming our bandwidth. + + * The machine's current state, including memory and screen contents, are included as part of the saved + machine state. So, even if the original machine always powers on from scratch, the *copied* machine will always + resume at the point it was saved. This behavior, however, can be disabled by passing a *parms* object as the + 4th parameter to the *embedPC()* call, overriding the 'state' property: + + + +While the [PCjs Documentation](/docs/pcjs/) explains how to create a *new* machine, by writing your own machine +XML file and manually copying all the other pieces, the new **Save Machine** feature is the best way to save +any *existing* IBM PC and embed it on any other website. + +**WARNING**: While this feature is still "hot of the press," there will probably be some kinks to work out. Every +browser seems to have its own idiosyncrasies in terms of what can be downloaded and/or how large it can be. It's +also quite possible that certain machine features or modifications may not be properly preserved. + +If you're having trouble with a particular browser or a particular machine, be sure to +[let me know](mailto:Jeff@pcjs.org), and then try another browser and/or machine. + +*[@jeffpar](http://twitter.com/jeffpar)* +*February 17, 2016* diff --git a/disks/pc/compiled/samples.xml b/disks/pc/compiled/samples.xml index 12511ea8b..b15bd74da 100644 --- a/disks/pc/compiled/samples.xml +++ b/disks/pc/compiled/samples.xml @@ -10,7 +10,8 @@ Microsoft Adventure VisiCalc - Load + Load + Save diff --git a/docs/pcjs/demos/pc-dbg.js b/docs/pcjs/demos/pc-dbg.js index f19fa8dc2..3844e8da9 100644 --- a/docs/pcjs/demos/pc-dbg.js +++ b/docs/pcjs/demos/pc-dbg.js @@ -1206,7 +1206,7 @@ ef.prototype={constructor:ef,value:function(){return this[this.id]},data:functio this.key)){try{window.localStorage.removeItem(d)}catch(e){}b.splice(c,1);c=0}}};function kr(a,b){a[a.id]={};b&&ff(a,"parms",b);a.A=!1}function lr(a){var b=!0;if(Ga()){var c=JSON.stringify(a[a.id]);Ia(a.key,c)||(Ca("Unable to store "+c.length+" bytes in browser local storage"),b=!1)}return b}function mr(a){var b=!0;try{a[a.id]=JSON.parse(a[a.id])}catch(c){Ca(c.message||c),b=!1}return b}function nr(a,b){return a[a.id][b]||null}function ff(a,b,c){try{a[a.id][b]=c}catch(d){}} function or(a,b,c){Ya.call(this,"Computer",a,or,67108864);this.ra.hc=!1;pr(this,b);this.da=Qo(this,"autoPower",a);this.R=0;this.wa=a.busWidth||a.buswidth;this.A=qr;this.W=null;this.P=this.ta=!1;this.za=Qo(this,"url")||"";this.Ba=(Math.random()+.1).toString(36).substr(2,12);this.C=rr(this);if(this.M=mb("CPU",this.id)){this.ma=mb("Debugger",this.id);this.ja=new Rb({id:this.re+".bus",buswidth:this.wa},this.M,this.ma);var d,e=kb(this.id);if((this.Wd=mb("Panel",this.id))&&this.Wd.Af)for(b=0;bqr){if(d.load(this.W)){this.O=new ef(this,sr,"failsafe");this.O.load()&&(vr(this,d),a=2,kr(this.O));ff(this.O,"timestamp",ta());lr(this.O);var e=this.A&&!this.P;if(1==a||Ea("Click OK to restore the previous PCjs machine state, or CANCEL to reset the machine.")){if(c=mr(d)){var f=nr(d,"code"),g=nr(d,"data");f&&("ok"==f?d.load(g):("error"==f&&"no machine state"!= diff --git a/docs/pcjs/demos/pc.js b/docs/pcjs/demos/pc.js index 09b96ce14..4fc1ac6a9 100644 --- a/docs/pcjs/demos/pc.js +++ b/docs/pcjs/demos/pc.js @@ -1025,7 +1025,7 @@ Td.prototype={constructor:Td,value:function(){return this[this.id]},data:functio this.key)){try{window.localStorage.removeItem(d)}catch(e){}b.splice(c,1);c=0}}};function Qm(a,b){a[a.id]={};b&&D(a,"parms",b);a.A=!1}function Rm(a){var b=!0;if(ta()){var c=JSON.stringify(a[a.id]);va(a.key,c)||(g("Unable to store "+c.length+" bytes in browser local storage"),b=!1)}return b}function Sm(a){var b=!0;try{a[a.id]=JSON.parse(a[a.id])}catch(c){g(c.message||c),b=!1}return b}function Tm(a,b){return a[a.id][b]||null}function D(a,b,c){try{a[a.id][b]=c}catch(d){}} function Um(a,b,c){Ha.call(this,"Computer",a,Um);this.ja.Qb=!1;Vm(this,b);this.S=Zl(this,"autoPower",a);this.F=0;this.ta=a.busWidth||a.buswidth;this.B=Wm;this.T=null;this.J=this.ha=!1;this.ka=Zl(this,"url")||"";this.na=(Math.random()+.1).toString(36).substr(2,12);this.D=Xm(this);if(this.A=Ua("CPU",this.id)){this.Ta=Ua("Debugger",this.id);this.oa=new qb({id:this.mf+".bus",buswidth:this.ta},this.A,this.Ta);var d,e=Sa(this.id);if((this.md=Ua("Panel",this.id))&&this.md.hg)for(b=0;bWm){if(d.load(this.T)){this.H=new Td(this,Ym,"failsafe");this.H.load()&&(an(this,d),a=2,Qm(this.H));D(this.H,"timestamp",la());Rm(this.H);var e=this.B&&!this.J;if(1==a||ra("Click OK to restore the previous PCjs machine state, or CANCEL to reset the machine.")){if(c=Sm(d)){var k=Tm(d,"code"),l=Tm(d,"data");k&&("ok"==k?d.load(l):("error"==k&&"no machine state"!= diff --git a/modules/pcjs/lib/computer.js b/modules/pcjs/lib/computer.js index 9e7d43f48..47d9149cc 100644 --- a/modules/pcjs/lib/computer.js +++ b/modules/pcjs/lib/computer.js @@ -372,18 +372,18 @@ Computer.prototype.setMachineParms = function(parmsMachine) * * @param {string} sParm * @param {Object} [parmsComponent] - * @return {string|null} + * @return {string|undefined} */ Computer.prototype.getMachineParm = function(sParm, parmsComponent) { - var value = null; + var value; if (this.parmsMachine) { value = this.parmsMachine[sParm]; } - if (value == null && parmsComponent) { + if (value === undefined && parmsComponent) { value = parmsComponent[sParm]; } - if (value == null && typeof resources == 'object' && resources[sParm]) { + if (value === undefined && typeof resources == 'object' && resources[sParm]) { value = sParm; } return value; diff --git a/modules/shared/lib/save.js b/modules/shared/lib/save.js index 4e010e0be..c007e1d02 100644 --- a/modules/shared/lib/save.js +++ b/modules/shared/lib/save.js @@ -55,7 +55,7 @@ function savePC(idMachine, sPCJSFile) var sState = cmp.powerOff(true); var sParms = cmp.saveMachineParms(); if (!sPCJSFile) { - if (MAXDEBUG) { + if (DEBUG) { sPCJSFile = "/tmp/pcjs/" + (XMLVERSION || APPVERSION) + "/pc.js" } else { sPCJSFile = "/versions/pcjs/" + (XMLVERSION || APPVERSION) + "/pc" + (dbg? "-dbg" : "") + ".js"; @@ -209,7 +209,7 @@ function downloadPC(sURL, sCSS, nErrorCode, aMachineInfo) } var link = document.createElement('a'); - if (typeof link.download == 'string') { + if (!DEBUG && typeof link.download == 'string') { link.href = uri; link.download = sScript + ".json"; document.body.appendChild(link); // Firefox requires the link to be in the body (?) diff --git a/versions/pcjs/1.20.9/pc-dbg.js b/versions/pcjs/1.20.9/pc-dbg.js index f19fa8dc2..3844e8da9 100644 --- a/versions/pcjs/1.20.9/pc-dbg.js +++ b/versions/pcjs/1.20.9/pc-dbg.js @@ -1206,7 +1206,7 @@ ef.prototype={constructor:ef,value:function(){return this[this.id]},data:functio this.key)){try{window.localStorage.removeItem(d)}catch(e){}b.splice(c,1);c=0}}};function kr(a,b){a[a.id]={};b&&ff(a,"parms",b);a.A=!1}function lr(a){var b=!0;if(Ga()){var c=JSON.stringify(a[a.id]);Ia(a.key,c)||(Ca("Unable to store "+c.length+" bytes in browser local storage"),b=!1)}return b}function mr(a){var b=!0;try{a[a.id]=JSON.parse(a[a.id])}catch(c){Ca(c.message||c),b=!1}return b}function nr(a,b){return a[a.id][b]||null}function ff(a,b,c){try{a[a.id][b]=c}catch(d){}} function or(a,b,c){Ya.call(this,"Computer",a,or,67108864);this.ra.hc=!1;pr(this,b);this.da=Qo(this,"autoPower",a);this.R=0;this.wa=a.busWidth||a.buswidth;this.A=qr;this.W=null;this.P=this.ta=!1;this.za=Qo(this,"url")||"";this.Ba=(Math.random()+.1).toString(36).substr(2,12);this.C=rr(this);if(this.M=mb("CPU",this.id)){this.ma=mb("Debugger",this.id);this.ja=new Rb({id:this.re+".bus",buswidth:this.wa},this.M,this.ma);var d,e=kb(this.id);if((this.Wd=mb("Panel",this.id))&&this.Wd.Af)for(b=0;bqr){if(d.load(this.W)){this.O=new ef(this,sr,"failsafe");this.O.load()&&(vr(this,d),a=2,kr(this.O));ff(this.O,"timestamp",ta());lr(this.O);var e=this.A&&!this.P;if(1==a||Ea("Click OK to restore the previous PCjs machine state, or CANCEL to reset the machine.")){if(c=mr(d)){var f=nr(d,"code"),g=nr(d,"data");f&&("ok"==f?d.load(g):("error"==f&&"no machine state"!=