True up with website.

* Drag and drop support.
* Re-enable audio in newer Chrome, Safari
* Minimal Printer
* Video dirty regions
This commit is contained in:
Will Scullin 2019-02-18 20:42:50 -08:00
parent 868923d550
commit b8261252e3
No known key found for this signature in database
GPG Key ID: 9092A5C0A673416B
17 changed files with 544 additions and 217 deletions

View File

@ -1,6 +1,6 @@
<!DOCTYPE html><!-- -*- mode: HTML; indent-tabs-mode: nil -*- --> <!DOCTYPE html>
<!-- <!--
Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com> Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
Permission to use, copy, modify, distribute, and sell this software and its Permission to use, copy, modify, distribute, and sell this software and its
documentation for any purpose is hereby granted without fee, provided that documentation for any purpose is hereby granted without fee, provided that
@ -15,7 +15,7 @@
<title>Apple ][js - An Apple 2 Emulator in JavaScript</title> <title>Apple ][js - An Apple 2 Emulator in JavaScript</title>
<meta name="viewport" content="width=device-width, user-scalable=no" /> <meta name="viewport" content="width=640, height=device-height, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-title" content="Apple ][js"> <meta name="apple-mobile-web-app-title" content="Apple ][js">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
@ -25,10 +25,11 @@
<link rel="apple-touch-icon" href="img/webapp-iphone.png" /> <link rel="apple-touch-icon" href="img/webapp-iphone.png" />
<link rel="apple-touch-icon" size="72x72" href="img/webapp-ipad.png" /> <link rel="apple-touch-icon" size="72x72" href="img/webapp-ipad.png" />
<link rel="shortcut icon" href="logoicon.png" /> <link rel="shortcut icon" href="img/logoicon.png" />
<link rel="stylesheet" type="text/css" href="css/apple2.css" /> <link rel="stylesheet" type="text/css" href="css/apple2.css" />
<link rel="stylesheet" type="text/css" <link rel="stylesheet" type="text/css"
href="http://code.jquery.com/ui/1.10.3/themes/mint-choc/jquery-ui.css" /> href="http://code.jquery.com/ui/1.10.3/themes/mint-choc/jquery-ui.css" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.2/css/all.css" />
<meta property="og:title" content="Apple ][js" /> <meta property="og:title" content="Apple ][js" />
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
@ -73,10 +74,16 @@
<script type="text/javascript" src="json/disks/index.js"></script> <script type="text/javascript" src="json/disks/index.js"></script>
</head> </head>
<body> <body class="apple2"
<div style="margin: auto; width: 604px"> ondragover="handleDragOver(0, event)"
ondrop="handleDrop(0, event)"
ondragend="handleDragEnd(0, event)">
<div id="fb-root"></div>
<div style="margin: auto; width: 614px">
<div id="header"> <div id="header">
<a href="http://www.w3.org/html/logo/"><img src="http://www.w3.org/html/logo/badge/html5-badge-h-solo.png" width="63" height="64" alt="HTML5 Powered" title="HTML5 Powered" style="float: right"></a> <a href="//www.w3.org/html/logo/" target="_blank">
<img src="//www.w3.org/html/logo/badge/html5-badge-h-solo.png" style="float: right" />
</a>
<a href="about.html" target="_blank"> <a href="about.html" target="_blank">
<img src="img/badge.png" id="badge" /> <img src="img/badge.png" id="badge" />
</a> </a>
@ -88,77 +95,94 @@
</div> </div>
</div> </div>
<div class="inset"> <div class="inset">
<div style="float: left; width: 50%"> <div style="float: left; width: 50%"
ondragover="handleDragOver(1, event)"
ondrop="handleDrop(1, event)"
ondragend="handleDragEnd(2, event)">
<button id="diskload1" class="diskload" title="Load Disk"
onclick="openLoad(1, event);">
<i class="fas fa-folder-open"></i>
</button>
<button id="disksave1" class="disksave" title="Save Disk"
onclick="openSave(1, event);">
<i class="fas fa-save"></i>
</button>
<div class="disk" id="disk1">&nbsp;</div> <div class="disk" id="disk1">&nbsp;</div>
<span id="disklabel1" class="disklabel">Disk 1</span> <span id="disklabel1" class="disklabel">Disk 1</span>
<button id="diskload1" class="diskload" value="Load"
onclick="openLoad(1, event);">
Load
</button>
<button id="disksave1" class="disksave" value="Save"
onclick="openSave(1, event);">
Save
</button>
</div> </div>
<div style="float: left; width: 50%"> <div style="float: left; width: 50%"
ondragover="handleDragOver(2, event)"
ondrop="handleDrop(2, event)"
ondragend="handleDragEnd(2, event)">
<button id="diskload2" class="diskload" title="Load Disk"
onclick="openLoad(2, event);">
<i class="fas fa-folder-open"></i>
</button>
<button id="disksave2" class="disksave" title="Save Disk"
onclick="openSave(2, event);">
<i class="fas fa-save"></i>
</button>
<div class="disk" id="disk2">&nbsp;</div> <div class="disk" id="disk2">&nbsp;</div>
<span id="disklabel2" class="disklabel">Disk 2</span> <span id="disklabel2" class="disklabel">Disk 2</span>
<button id="diskload2" class="diskload" value="Load"
onclick="openLoad(2, event);">
Load
</button>
<button id="disksave2" class="disksave" value="Save"
onclick="openSave(2, event);">
Save
</button>
</div> </div>
<div style="clear: both"></div> <div style="clear: both"></div>
</div> </div>
<div class="inset"> <div class="inset">
<div id="khz" onclick="showFPS = !showFPS">0KHz</div> <div id="khz" onclick="showFPS = !showFPS">0KHz</div>
<input type="button" value="Pause" onclick="pauseRun(this)" /> <button id="pause-run" onclick="pauseRun()" title="Pause/Run">
<i class="fas fa-pause"></i>
</button>
<button id="toggle-sound" onclick="toggleSound()" title="Toggle Sound">
<i class="fas fa-volume-off"></i>
</button>
<button id="toggle-printer" onclick="$('#printer').dialog('open')" title="Toggle Printer">
<i class="fas fa-print"></i>
</button>
<div style="float: right"> <div style="float: right">
<input type="button" onclick="window.open('about.html','_html')" <a class="button" href="about.html" target="_blank" title="About">
name="About" value="About"> <i class="fas fa-info"></i>
<input type="button" onclick="$('#options').dialog('open')" </a>
name="Options" value="Options"> <button onclick="$('#options').dialog('open')" title="Options">
<i class="fas fa-cog"></i>
</button>
</div> </div>
</div> </div>
<div class="inset"> <div class="inset">
<div id="keyboard"></div> <div style="margin: 0 10px">
<div id="textarea" style="display: none"> <div id="keyboard"></div>
<button onclick="io.keyDown(0x1b)"> <div id="textarea" style="display: none">
ESC <button onclick="io.keyDown(0x1b)">
</button> ESC
<button onclick="reset()" style="float: right; margin-left: 10px"> </button>
Reset <button onclick="reset()" style="float: right; margin-left: 10px">
</button> Reset
<button onclick="io.keyDown(0x15)" style="float: right"> </button>
&rarr; <button onclick="io.keyDown(0x15)" style="float: right">
</button> &rarr;
<button onclick="io.keyDown(0x08)" style="float: right"> </button>
&larr; <button onclick="io.keyDown(0x08)" style="float: right">
</button> &larr;
<label for="text_input">Text Input</label> </button>
<textarea rows="10" style="width: 99%" id="text_input"></textarea> <label for="text_input">Text Input</label>
<br /> <textarea rows="10" style="width: 99%" id="text_input"></textarea>
<button onclick="io.setKeyBuffer($('#text_input').val())"> <br />
Send <button onclick="io.setKeyBuffer($('#text_input').val())">
</button> Send
<input type="checkbox" id="buffering" /> </button>
<label for="buffering">Buffer</label> <input type="checkbox" id="buffering" />
<button style="float: right" <label for="buffering">Buffer</label>
onclick="$('#keyboard').show(); $('#textarea').hide()"> <button style="float: right"
Keyboard onclick="$('#keyboard').show(); $('#textarea').hide()">
</button> Keyboard
</button>
</div>
</div> </div>
</div> </div>
</div>
<div id="loading" title="Loading" style="display: none"> <div id="loading" title="Loading" style="display: none">
Loading... Loading...
</div> </div>
<div id="options" title="Options" style="display: none"> <div id="options" title="Options" style="display: none">
<h3>CPU</h3> <h3>Type</h3>
<ul> <ul>
<li> <li>
<select id="computer_type2" value="apple2plus" onchange="updateCPU()"> <select id="computer_type2" value="apple2plus" onchange="updateCPU()">
@ -166,10 +190,13 @@
<option value="apple2">Autostart Apple ][</option> <option value="apple2">Autostart Apple ][</option>
<option value="original">Apple ][</option> <option value="original">Apple ][</option>
</select> </select>
<label for="computer_type2">
Type
</label>
</li> </li>
<li>
<i>* Reload page to take effect</i>
</li>
</ul>
<h3>CPU</h3>
<ul>
<li> <li>
<input type="checkbox" id="accelerator_toggle" onclick="updateCPU()"/> <input type="checkbox" id="accelerator_toggle" onclick="updateCPU()"/>
<label for="accelerator_toggle"> <label for="accelerator_toggle">
@ -279,6 +306,10 @@
<input type="file" id="local_file" /> <input type="file" id="local_file" />
</form> </form>
</div> </div>
<script type="text/javascript" src="js/main2.js"></script> <div id="printer" title="Printer" style="display: none">
<div class="paper">
</div>
</div>
<script src="js/main2.js"></script>
</body> </body>
</html> </html>

View File

@ -1,4 +1,4 @@
<!DOCTYPE html><!-- -*- mode: HTML; indent-tabs-mode: nil -*- --> <!DOCTYPE html>
<!-- <!--
Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com> Copyright 2010-2016 Will Scullin <scullin@scullinsteel.com>
@ -15,7 +15,7 @@
<title>Apple //jse - An Apple //e Emulator in JavaScript</title> <title>Apple //jse - An Apple //e Emulator in JavaScript</title>
<meta name="viewport" content="width=device-width, user-scalable=no" /> <meta name="viewport" content="width=640 user-scalable=0" />
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-title" content="Apple //jse"> <meta name="apple-mobile-web-app-title" content="Apple //jse">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
@ -25,10 +25,11 @@
<link rel="apple-touch-icon" href="img/webapp-iphone.png" /> <link rel="apple-touch-icon" href="img/webapp-iphone.png" />
<link rel="apple-touch-icon" size="72x72" href="img/webapp-ipad.png" /> <link rel="apple-touch-icon" size="72x72" href="img/webapp-ipad.png" />
<link rel="shortcut icon" href="logoicon.png" /> <link rel="shortcut icon" href="img/logoicon.png" />
<link rel="stylesheet" type="text/css" href="css/apple2.css" /> <link rel="stylesheet" type="text/css" href="css/apple2.css" />
<link rel="stylesheet" type="text/css" <link rel="stylesheet" type="text/css"
href="http://code.jquery.com/ui/1.10.3/themes/mint-choc/jquery-ui.css" /> href="http://code.jquery.com/ui/1.10.3/themes/mint-choc/jquery-ui.css" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.2/css/all.css" />
<meta property="og:title" content="Apple //js" /> <meta property="og:title" content="Apple //js" />
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
@ -72,12 +73,17 @@
<script type="text/javascript" src="json/disks/index.js"></script> <script type="text/javascript" src="json/disks/index.js"></script>
</head> </head>
<body class="apple2e"> <body class="apple2e"
<div style="margin: auto; width: 604px"> ondragover="handleDragOver(0, event)"
ondrop="handleDrop(0, event)"
ondragend="handleDragEnd(0, event)">
<div class="outer">
<div id="header"> <div id="header">
<a href="http://www.w3.org/html/logo/"><img src="http://www.w3.org/html/logo/badge/html5-badge-h-solo.png" width="63" height="64" alt="HTML5 Powered" title="HTML5 Powered" style="float: right"></a> <a href="//www.w3.org/html/logo/" target="_blank">
<img src="//www.w3.org/html/logo/badge/html5-badge-h-solo.png" style="float: right" />
</a>
<a href="about.html" target="_blank"> <a href="about.html" target="_blank">
<img src="img/badge.png" id="badge" /> <img src="img/badge2e.png" id="badge" />
</a> </a>
<h1 id="subtitle">An Apple //e Emulator in JavaScript</h1> <h1 id="subtitle">An Apple //e Emulator in JavaScript</h1>
</div> </div>
@ -89,44 +95,62 @@
</div> </div>
</div> </div>
<div class="inset"> <div class="inset">
<div style="float: left; width: 50%"> <div style="float: left; width: 50%"
ondragover="handleDragOver(1, event)"
ondrop="handleDrop(1, event)"
ondragend="handleDragEnd(1, event)">
<button id="diskload1" class="diskload" title="Load Disk"
onclick="openLoad(1, event);">
<i class="fas fa-folder-open"></i>
</button>
<button id="disksave1" class="disksave" title="Save Disk"
onclick="openSave(1, event);">
<i class="fas fa-save"></i>
</button>
<div class="disk" id="disk1">&nbsp;</div> <div class="disk" id="disk1">&nbsp;</div>
<span id="disklabel1" class="disklabel">Disk 1</span> <span id="disklabel1" class="disklabel">Disk 1</span>
<button id="diskload1" class="diskload" value="Load"
onclick="openLoad(1, event);">
Load
</button>
<button id="disksave1" class="disksave" value="Save"
onclick="openSave(1, event);">
Save
</button>
</div> </div>
<div style="float: left; width: 50%"> <div style="float: left; width: 50%"
ondragover="handleDragOver(2, event)"
ondrop="handleDrop(2, event)"
ondragend="handleDragEnd(2, event)">
<button id="diskload2" class="diskload" title="Load Disk"
onclick="openLoad(2, event);">
<i class="fas fa-folder-open"></i>
</button>
<button id="disksave2" class="disksave" title="Save Disk"
onclick="openSave(2, event);">
<i class="fas fa-save"></i>
</button>
<div class="disk" id="disk2">&nbsp;</div> <div class="disk" id="disk2">&nbsp;</div>
<span id="disklabel2" class="disklabel">Disk 2</span> <span id="disklabel2" class="disklabel">Disk 2</span>
<button id="diskload2" class="diskload" value="Load"
onclick="openLoad(2, event);">
Load
</button>
<button id="disksave2" class="disksave" value="Save"
onclick="openSave(2, event);">
Save
</button>
</div> </div>
<div style="clear: both"></div> <div style="clear: both"></div>
</div> </div>
<div style="position: relative"> <div style="position: relative">
<div id="controls" class="inset"> <div id="controls" class="inset">
<div id="khz" onclick="showFPS = !showFPS">0KHz</div> <div id="khz" onclick="showFPS = !showFPS">0KHz</div>
<input type="button" value="Pause" onclick="pauseRun(this)" /> <button id="pause-run" onclick="pauseRun()">
<i class="fas fa-pause"></i>
</button>
<button id="toggle-sound" onclick="toggleSound()">
<i class="fas fa-volume-off"></i>
</button>
<button id="toggle-printer" onclick="$('#printer').dialog('open')" title="Toggle Printer">
<i class="fas fa-print"></i>
</button>
<div style="float: right"> <div style="float: right">
<input type="button" onclick="window.open('about.html','_html')" <a class="button" href="about.html" target="_blank" title="About">
name="About" value="About"> <i class="fas fa-info"></i>
<input type="button" onclick="$('#options').dialog('open')" </a>
name="Options" value="Options"> <button onclick="$('#options').dialog('open')">
<i class="fas fa-cog"></i>
</button>
</div> </div>
</div> </div>
<input id="reset" type="button" value="Reset" onclick="reset()" /> <input id="reset" type="button" value="Reset"
onclick="keyboard.reset(event)"
oncontextmenu="keyboard.reset(event)" />
</div> </div>
<div class="inset"> <div class="inset">
<div id="keyboard"></div> <div id="keyboard"></div>
@ -136,17 +160,20 @@
Loading... Loading...
</div> </div>
<div id="options" title="Options" style="display: none"> <div id="options" title="Options" style="display: none">
<h3>CPU</h3> <h3>Type</h3>
<ul> <ul>
<li> <li>
<select id="computer_type2e" value="apple2enh" onchange="updateCPU()"> <select id="computer_type2e" value="apple2enh" onchange="updateCPU()">
<option value="apple2enh">Enhanced Apple //e</option> <option value="apple2enh">Enhanced Apple //e</option>
<option value="apple2e">Apple //e</option> <option value="apple2e">Apple //e</option>
</select> </select>
<label for="computer_type2e">
Type
</label>
</li> </li>
<li>
<i>* Reload page to take effect</i>
</li>
</ul>
<h3>CPU</h3>
<ul>
<li> <li>
<input type="checkbox" id="accelerator_toggle" onclick="updateCPU()"/> <input type="checkbox" id="accelerator_toggle" onclick="updateCPU()"/>
<label for="accelerator_toggle"> <label for="accelerator_toggle">
@ -229,7 +256,7 @@
</div> </div>
<div id="manage" title="Manage Disks" style="display: none"> <div id="manage" title="Manage Disks" style="display: none">
</div> </div>
<div id="http_load" title="Load URL"> <div id="http_load" title="Load URL" style="display: none">
<form action="#"> <form action="#">
<input type="text" id="http_url" style="width: 500px"/> <input type="text" id="http_url" style="width: 500px"/>
</form> </form>
@ -256,6 +283,10 @@
<input type="file" id="local_file" /> <input type="file" id="local_file" />
</form> </form>
</div> </div>
<script type="text/javascript" src="js/main2e.js"></script> <div id="printer" title="Printer" style="display: none">
<div class="paper">
</div>
</div>
<script src="js/main2e.js"></script>
</body> </body>
</html> </html>

BIN
img/badge2e.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
img/logoicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 838 B

View File

@ -120,25 +120,25 @@ function Apple2IO(cpu, callbacks)
var delta = now - _trigger; var delta = now - _trigger;
switch (off) { switch (off) {
case LOC.CLR80VID: case LOC.CLR80VID:
if ('_80col' in callbacks && val !== undefined) { if (callbacks._80col && val !== undefined) {
_debug('80 Column Mode off'); _debug('80 Column Mode off');
callbacks._80col(false); callbacks._80col(false);
} }
break; break;
case LOC.SET80VID: case LOC.SET80VID:
if ('_80col' in callbacks && val !== undefined) { if (callbacks._80col && val !== undefined) {
_debug('80 Column Mode on'); _debug('80 Column Mode on');
callbacks._80col(true); callbacks._80col(true);
} }
break; break;
case LOC.CLRALTCH: case LOC.CLRALTCH:
if ('altchar' in callbacks && val !== undefined) { if (callbacks.altchar && val !== undefined) {
_debug('Alt Char off'); _debug('Alt Char off');
callbacks.altchar(false); callbacks.altchar(false);
} }
break; break;
case LOC.SETALTCH: case LOC.SETALTCH:
if ('altchar' in callbacks && val !== undefined) { if (callbacks.altchar && val !== undefined) {
_debug('Alt Char on'); _debug('Alt Char on');
callbacks.altchar(true); callbacks.altchar(true);
} }
@ -174,27 +174,27 @@ function Apple2IO(cpu, callbacks)
callbacks.page(2); callbacks.page(2);
break; break;
case LOC.RDTEXT: case LOC.RDTEXT:
if ('isText' in callbacks) if (callbacks.isText)
result = callbacks.isText() ? 0x80 : 0x0; result = callbacks.isText() ? 0x80 : 0x0;
break; break;
case LOC.RDMIXED: case LOC.RDMIXED:
if ('isMixed' in callbacks) if (callbacks.isText)
result = callbacks.isMixed() ? 0x80 : 0x0; result = callbacks.isText() ? 0x80 : 0x0;
break; break;
case LOC.RDPAGE2: case LOC.RDPAGE2:
if ('isPage2' in callbacks) if (callbacks.isPage2)
result = callbacks.isPage2() ? 0x80 : 0x0; result = callbacks.isPage2() ? 0x80 : 0x0;
break; break;
case LOC.RDHIRES: case LOC.RDHIRES:
if ('isHires' in callbacks) if (callbacks.isHires)
result = callbacks.isHires() ? 0x80 : 0x0; result = callbacks.isHires() ? 0x80 : 0x0;
break; break;
case LOC.RD80VID: case LOC.RD80VID:
if ('is80Col' in callbacks) if (callbacks.is80Col)
result = callbacks.is80Col() ? 0x80 : 0x0; result = callbacks.is80Col() ? 0x80 : 0x0;
break; break;
case LOC.RDALTCH: case LOC.RDALTCH:
if ('isAltChar' in callbacks) if (callbacks.isAltChar)
result = callbacks.isAltChar() ? 0x80 : 0x0; result = callbacks.isAltChar() ? 0x80 : 0x0;
break; break;
case LOC.SETAN0: case LOC.SETAN0:
@ -212,7 +212,7 @@ function Apple2IO(cpu, callbacks)
case LOC.SETAN3: case LOC.SETAN3:
_debug('Annunciator 3 on'); _debug('Annunciator 3 on');
_annunciators[3] = true; _annunciators[3] = true;
if ('doublehires' in callbacks) callbacks.doublehires(false); if (callbacks.doublehires) callbacks.doublehires(false);
break; break;
case LOC.CLRAN0: case LOC.CLRAN0:
_debug('Annunciator 0 off'); _debug('Annunciator 0 off');
@ -229,7 +229,7 @@ function Apple2IO(cpu, callbacks)
case LOC.CLRAN3: case LOC.CLRAN3:
_debug('Annunciator 3 off'); _debug('Annunciator 3 off');
_annunciators[3] = false; _annunciators[3] = false;
if ('doublehires' in callbacks) callbacks.doublehires(true); if (callbacks.doublehires) callbacks.doublehires(true);
break; break;
case LOC.SPEAKER: case LOC.SPEAKER:
_phase = -_phase; _phase = -_phase;
@ -275,12 +275,11 @@ function Apple2IO(cpu, callbacks)
_trigger = cpu.cycles(); _trigger = cpu.cycles();
break; break;
case LOC.RDDHIRES: case LOC.RDDHIRES:
if ('isDoubleHires' in callbacks) { if (callbacks.isDoubleHires) {
result = callbacks.isDoubleHires() ? 0x80 : 0x0; result = callbacks.isDoubleHires() ? 0x80 : 0x0;
} }
break; break;
case LOC.TAPEIN: case LOC.TAPEIN:
// var flipped = false;
if (_tapeOffset == -1) { if (_tapeOffset == -1) {
_tapeOffset = 0; _tapeOffset = 0;
_tapeNext = now; _tapeNext = now;
@ -339,6 +338,7 @@ function Apple2IO(cpu, callbacks)
card.reset(); card.reset();
} }
} }
callbacks.reset();
}, },
read: function apple2io_read(page, off) { read: function apple2io_read(page, off) {
@ -472,8 +472,13 @@ function Apple2IO(cpu, callbacks)
_cycles_per_sample = _hz / _rate; _cycles_per_sample = _hz / _rate;
}, },
sampleTick: function sampleTick() { tick: function tick() {
_tick(); _tick();
for (var idx = 0; idx < 8; idx++) {
if (_slot[idx] && _slot[idx].tick) {
_slot[idx].tick();
}
}
}, },
addSampleListener: function addSampleListener(cb) { addSampleListener: function addSampleListener(cb) {
@ -482,6 +487,10 @@ function Apple2IO(cpu, callbacks)
annunciator: function annunciator(idx) { annunciator: function annunciator(idx) {
return _annunciators[idx]; return _annunciators[idx];
},
cycles: function apple2io_cycles() {
return cpu.cycles();
} }
}; };
} }

View File

@ -66,6 +66,12 @@ function LoresPage(page, charset, e, context)
var _refreshing = false; var _refreshing = false;
var _greenMode = false; var _greenMode = false;
var _blink = false; var _blink = false;
var _dirty = {
top: 385,
bottom: -1,
left: 561,
right: -1
};
var _green = [0x00,0xff,0x80]; var _green = [0x00,0xff,0x80];
@ -204,6 +210,15 @@ function LoresPage(page, charset, e, context)
var data = _imageData.data; var data = _imageData.data;
if ((row < 24) && (col < 40)) { if ((row < 24) && (col < 40)) {
var y = row * 16;
if (y < _dirty.top) { _dirty.top = y; }
y += 16;
if (y > _dirty.bottom) { _dirty.bottom = y; }
var x = col * 14;
if (x < _dirty.left) { _dirty.left = x; }
x += 14;
if (x > _dirty.right) { _dirty.right = x; }
var color; var color;
if (textMode || hiresMode || (mixedMode && row > 19)) { if (textMode || hiresMode || (mixedMode && row > 19)) {
var inverse; var inverse;
@ -400,11 +415,26 @@ function LoresPage(page, charset, e, context)
this.refresh(); this.refresh();
}, },
blit: function(mixed) { blit: function(mixed) {
if (_dirty.top === 385) { return false; }
var top = _dirty.top;
var bottom = _dirty.bottom;
var left = _dirty.left;
var right = _dirty.right;
if (mixed) { if (mixed) {
context.putImageData(_imageData, 0, 0, 0, 320, 560, 64); if (bottom < 320) { return false; }
} else { if (top < 320) { top = 320; }
context.putImageData(_imageData, 0, 0, 0, 0, 560, 384);
} }
context.putImageData(
_imageData, 0, 0, left, top, right - left, bottom - top
);
_dirty = {
top: 385,
bottom: -1,
left: 561,
right: -1
};
return true;
}, },
start: function() { start: function() {
var self = this; var self = this;
@ -455,6 +485,12 @@ function HiresPage(page, context)
var _page = page; var _page = page;
var _imageData; var _imageData;
var _dirty = {
top: 385,
bottom: -1,
left: 561,
right: -1
};
var r4 = [ var r4 = [
0, // Black 0, // Black
@ -627,6 +663,15 @@ function HiresPage(page, context)
return; return;
} }
var y = rowa * 16 + rowb * 2;
if (y < _dirty.top) { _dirty.top = y; }
y += 2;
if (y > _dirty.bottom) { _dirty.bottom = y; }
var x = col * 14;
if (x < _dirty.left) { _dirty.left = x; }
x += 14;
if (x > _dirty.right) { _dirty.right = x; }
dy = rowa * 16 + rowb * 2; dy = rowa * 16 + rowb * 2;
var bz, b0, b1, b2, b3, b4, c, hb; var bz, b0, b1, b2, b3, b4, c, hb;
if (doubleHiresMode) { if (doubleHiresMode) {
@ -813,11 +858,26 @@ function HiresPage(page, context)
this.refresh(); this.refresh();
}, },
blit: function(mixed) { blit: function(mixed) {
if (_dirty.top === 385) { return false; }
var top = _dirty.top;
var bottom = _dirty.bottom;
var left = _dirty.left;
var right = _dirty.right;
if (mixed) { if (mixed) {
context.putImageData(_imageData, 0, 0, 0, 0, 560, 320); if (top > 320) { return false; }
} else { if (bottom > 320) { bottom = 320; }
context.putImageData(_imageData, 0, 0, 0, 0, 560, 384);
} }
context.putImageData(
_imageData, 0, 0, left, top, right - left, bottom - top
);
_dirty = {
top: 385,
bottom: -1,
left: 561,
right: -1
};
return true;
}, },
start: function() { start: function() {
return this._start(); return this._start();
@ -986,7 +1046,11 @@ function VideoModes(gr, hgr, gr2, hgr2, e) {
} }
}, },
page: function(pageNo) { page: function(pageNo) {
var old = pageMode;
pageMode = pageNo; pageMode = pageNo;
if (old != pageNo) {
_refresh();
}
}, },
isText: function() { isText: function() {
return textMode; return textMode;
@ -1007,23 +1071,25 @@ function VideoModes(gr, hgr, gr2, hgr2, e) {
return altCharMode; return altCharMode;
}, },
blit: function() { blit: function() {
var blitted = false;
if (multiScreen) { if (multiScreen) {
_grs[0].blit(); blitted = _grs[0].blit() || blitted;
_grs[1].blit(); blitted = _grs[1].blit() || blitted;
_hgrs[0].blit(); blitted = _hgrs[0].blit() || blitted;
_hgrs[1].blit(); blitted = _hgrs[1].blit() || blitted;
} else { } else {
if (hiresMode && !textMode) { if (hiresMode && !textMode) {
if (mixedMode) { if (mixedMode) {
_grs[pageMode - 1].blit(true); blitted = _grs[pageMode - 1].blit(true) || blitted;
_hgrs[pageMode - 1].blit(true); blitted = _hgrs[pageMode - 1].blit(true) || blitted;
} else { } else {
_hgrs[pageMode - 1].blit(); blitted = _hgrs[pageMode - 1].blit();
} }
} else { } else {
_grs[pageMode - 1].blit(); blitted = _grs[pageMode - 1].blit();
} }
} }
return blitted;
}, },
getState: function() { getState: function() {
return { return {

View File

@ -59,12 +59,14 @@ function Parallel(io, slot, cbs) {
]; ];
function _access(off, val) { function _access(off, val) {
switch (off) { switch (off & 0x8f) {
case LOC.IOREG: case LOC.IOREG:
if (val && 'putChar' in cbs) { if (cbs.putChar && val) {
cbs.putChar(val); cbs.putChar(val);
} }
break; break;
default:
debug('Parallel card unknown softswitch', off);
} }
} }

View File

@ -463,6 +463,7 @@ function Videoterm(io, slot, context) {
var _bank = 0; var _bank = 0;
var _buffer = allocMemPages(8); var _buffer = allocMemPages(8);
var _imageData; var _imageData;
var _dirty = false;
var _black = [0x00, 0x00, 0x00]; var _black = [0x00, 0x00, 0x00];
var _white = [0xff, 0xff, 0xff]; var _white = [0xff, 0xff, 0xff];
@ -500,6 +501,7 @@ function Videoterm(io, slot, context) {
var color; var color;
if (row < 25) { if (row < 25) {
_dirty = true;
for (var idx = 0; idx < 8; idx++) { for (var idx = 0; idx < 8; idx++) {
var cdata = VIDEO_ROM[c + idx]; var cdata = VIDEO_ROM[c + idx];
for (var jdx = 0; jdx < 7; jdx++) { for (var jdx = 0; jdx < 7; jdx++) {
@ -543,6 +545,7 @@ function Videoterm(io, slot, context) {
return; return;
} }
if (_blink || (blinkmode === CURSOR_MODES.SOLID)) { if (_blink || (blinkmode === CURSOR_MODES.SOLID)) {
_dirty = true;
for (var idx = 0; idx < 8; idx++) { for (var idx = 0; idx < 8; idx++) {
var color = _white; var color = _white;
if (idx >= (_regs[REGS.CURSOR_UPPER] & 0x1f) && if (idx >= (_regs[REGS.CURSOR_UPPER] & 0x1f) &&
@ -641,7 +644,12 @@ function Videoterm(io, slot, context) {
_refresh(); _refresh();
_shouldRefresh = false; _shouldRefresh = false;
} }
context.putImageData(_imageData, 0, 0); if (_dirty) {
context.putImageData(_imageData, 0, 0);
_dirty = false;
return true;
}
return false;
} }
}; };
} }

View File

@ -1,5 +1,5 @@
/*! /*!
* Copyright 2010-2017 Will Scullin <scullin@scullinsteel.com> * Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
* *
* Permission to use, copy, modify, distribute, and sell this software and its * Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that * documentation for any purpose is hereby granted without fee, provided that

View File

@ -23,7 +23,7 @@
ApplesoftDump: false, SYMBOLS: false, ApplesoftDump: false, SYMBOLS: false,
multiScreen: true multiScreen: true
*/ */
/* exported openLoad, openSave, doDelete, /* exported openLoad, openSave, doDelete, handleDragOver, handleDragEnd, handleDrop,
selectCategory, selectDisk, clickDisk, selectCategory, selectDisk, clickDisk,
multiScreen, multiScreen,
updateJoystick, updateJoystick,
@ -94,12 +94,9 @@ function DriveLights()
{ {
return { return {
driveLight: function(drive, on) { driveLight: function(drive, on) {
$('#disk' + drive).css( $('#disk' + drive).css('background-image',
'background-image', on ? 'url(css/red-on-16.png)' :
on ? 'url(css/red-off-16.png)');
'url(css/red-on-16.png)' :
'url(css/red-off-16.png)'
);
}, },
dirty: function() { dirty: function() {
// $('#disksave' + drive).button('option', 'disabled', !dirty); // $('#disksave' + drive).button('option', 'disabled', !dirty);
@ -165,6 +162,54 @@ function openSave(drive, event)
} }
} }
function handleDragOver(drive, event) {
event.preventDefault();
event.dataTransfer.dropEffect = 'copy';
}
function handleDragEnd(drive, event) {
var dt = event.dataTransfer;
if (dt.items) {
for (var i = 0; i < dt.items.length; i++) {
dt.items.remove(i);
}
} else {
event.dataTransfer.clearData();
}
}
function handleDrop(drive, event) {
event.preventDefault();
event.stopPropagation();
if (drive < 1) {
if (!disk2.getMetadata(1)) {
drive = 1;
} else if (!disk2.getMetadata(2)) {
drive = 2;
} else {
drive = 1;
}
}
var dt = event.dataTransfer;
if (dt.files.length == 1) {
doLoadLocal(drive, dt.files[0]);
} else if (dt.files.length == 2) {
doLoadLocal(1, dt.files[0]);
doLoadLocal(2, dt.files[1]);
} else {
for (var idx = 0; idx < dt.items.length; idx++) {
if (dt.items[idx].type === 'text/uri-list') {
dt.items[idx].getAsString(function(url) {
var parts = document.location.hash.split('|');
parts[drive - 1] = url;
document.location.hash = parts.join('|');
});
}
}
}
}
var loading = false; var loading = false;
function loadAjax(drive, url) { function loadAjax(drive, url) {
@ -378,8 +423,10 @@ var io = new Apple2IO(cpu, vm);
var keyboard = new KeyBoard(io); var keyboard = new KeyBoard(io);
var audio = new Audio(io); var audio = new Audio(io);
var tape = new Tape(io); var tape = new Tape(io);
var printer = new Printer($('#printer .paper'));
var lc = new LanguageCard(io, 0, rom); var lc = new LanguageCard(io, 0, rom);
var parallel = new Parallel(io, 1, new Printer()); var parallel = new Parallel(io, 1, printer);
var slinky = new RAMFactor(io, 2, 1024 * 1024); var slinky = new RAMFactor(io, 2, 1024 * 1024);
var videoterm = new Videoterm(io, 3, context1); var videoterm = new Videoterm(io, 3, context1);
var disk2 = new DiskII(io, 6, drivelights); var disk2 = new DiskII(io, 6, drivelights);
@ -426,7 +473,14 @@ function updateKHz() {
} }
function updateSound() { function updateSound() {
audio.enable($('#enable_sound').attr('checked')); var on = $('#enable_sound').prop('checked');
var label = $('#toggle-sound i');
audio.enable(on);
if (on) {
label.removeClass('fa-volume-off').addClass('fa-volume-up');
} else {
label.removeClass('fa-volume-up').addClass('fa-volume-off');
}
} }
function dumpDisk(drive) { function dumpDisk(drive) {
@ -492,7 +546,6 @@ function run(pc) {
var now, last = Date.now(); var now, last = Date.now();
var runFn = function() { var runFn = function() {
now = Date.now(); now = Date.now();
renderedFrames++;
var step = (now - last) * kHz, stepMax = kHz * ival; var step = (now - last) * kHz, stepMax = kHz * ival;
last = now; last = now;
@ -528,11 +581,15 @@ function run(pc) {
if (multiScreen) { if (multiScreen) {
vm.blit(); vm.blit();
} }
videoterm.blit(); if (videoterm.blit()) {
renderedFrames++;
}
} else { } else {
vm.blit(); if (vm.blit()) {
renderedFrames++;
}
} }
io.sampleTick(); io.tick();
} }
processGamepad(io); processGamepad(io);
@ -691,8 +748,8 @@ function updateLocalStorage() {
cat.push({ cat.push({
'category': 'Local Saves', 'category': 'Local Saves',
'name': name, 'name': name,
'filename': 'local:' + name} 'filename': 'local:' + name
); });
$('#manage').append( $('#manage').append(
'<span class="local_save">' + '<span class="local_save">' +
name + name +
@ -864,17 +921,24 @@ function _mousemove(evt) {
io.paddle(1, flipY ? 1 - y : y); io.paddle(1, flipY ? 1 - y : y);
} }
function pauseRun(b) { function pauseRun() {
var label = $('#pause-run i');
if (paused) { if (paused) {
run(); run();
b.value = 'Pause'; label.removeClass('fa-play').addClass('fa-pause');
} else { } else {
stop(); stop();
b.value = 'Run'; label.removeClass('fa-pause').addClass('fa-play');
} }
paused = !paused; paused = !paused;
} }
function toggleSound() {
var enableSound = $('#enable_sound');
enableSound.prop('checked', !enableSound.prop('checked'));
updateSound();
}
$(function() { $(function() {
hashtag = document.location.hash; hashtag = document.location.hash;
@ -890,15 +954,18 @@ $(function() {
* Input Handling * Input Handling
*/ */
$(window).keydown(_keydown); $(window)
$(window).keyup(_keyup); .keydown(_keydown)
.keyup(_keyup)
.mousedown(function() { audio.autoStart(); });
$('canvas').mousedown(function(evt) { $('canvas')
if (!gamepad) { .mousedown(function(evt) {
io.buttonDown(evt.which == 1 ? 0 : 1); if (!gamepad) {
} io.buttonDown(evt.which == 1 ? 0 : 1);
evt.preventDefault(); }
}) evt.preventDefault();
})
.mouseup(function(evt) { .mouseup(function(evt) {
if (!gamepad) { if (!gamepad) {
io.buttonUp(evt.which == 1 ? 0 : 1); io.buttonUp(evt.which == 1 ? 0 : 1);
@ -908,10 +975,11 @@ $(function() {
$('body').mousemove(_mousemove); $('body').mousemove(_mousemove);
$('input,textarea').focus(function() { focused = true; }) $('input,textarea')
.focus(function() { focused = true; })
.blur(function() { focused = false; }); .blur(function() { focused = false; });
keyboard.create($('#keyboard')); keyboard.create('#keyboard');
if (prefs.havePrefs()) { if (prefs.havePrefs()) {
$('#options input[type=checkbox]').each(function() { $('#options input[type=checkbox]').each(function() {
@ -931,7 +999,6 @@ $(function() {
} }
reset(); reset();
run();
setInterval(updateKHz, 1000); setInterval(updateKHz, 1000);
updateSound(); updateSound();
updateScreen(); updateScreen();
@ -963,6 +1030,16 @@ $(function() {
width: 320, width: 320,
buttons: {'Close': cancel } buttons: {'Close': cancel }
}); });
$('#printer').dialog({
autoOpen: false,
modal: true,
resizeable: false,
width: 570,
buttons: {
'Clear': printer.clear,
'Close': cancel
}
});
$('#http_load').dialog({ $('#http_load').dialog({
autoOpen: false, autoOpen: false,
modal: true, modal: true,
@ -1025,4 +1102,6 @@ $(function() {
}).keyup(function() { }).keyup(function() {
focused = $('#buffering').prop('checked'); focused = $('#buffering').prop('checked');
}); });
run();
}); });

View File

@ -20,7 +20,7 @@
ApplesoftDump: false, SYMBOLS: false, ApplesoftDump: false, SYMBOLS: false,
multiScreen: true multiScreen: true
*/ */
/* exported openLoad, openSave, doDelete, /* exported openLoad, openSave, doDelete, handleDragOver, handleDragEnd, handleDrop,
selectCategory, selectDisk, clickDisk, selectCategory, selectDisk, clickDisk,
multiScreen, multiScreen,
updateJoystick, updateJoystick,
@ -159,6 +159,44 @@ function openSave(drive, event)
} }
} }
function handleDragOver(drive, event) {
event.preventDefault();
event.dataTransfer.dropEffect = 'copy';
}
function handleDragEnd(drive, event) {
var dt = event.dataTransfer;
if (dt.items) {
for (var i = 0; i < dt.items.length; i++) {
dt.items.remove(i);
}
} else {
event.dataTransfer.clearData();
}
}
function handleDrop(drive, event) {
event.preventDefault();
event.stopPropagation();
if (drive < 1) {
if (!disk2.getMetadata(1)) {
drive = 1;
} else if (!disk2.getMetadata(2)) {
drive = 2;
} else {
drive = 1;
}
}
var dt = event.dataTransfer;
if (dt.files.length == 1) {
doLoadLocal(drive, dt.files[0]);
} else if (dt.files.length == 2) {
doLoadLocal(1, dt.files[0]);
doLoadLocal(2, dt.files[1]);
}
}
var loading = false; var loading = false;
function loadAjax(drive, url) { function loadAjax(drive, url) {
@ -320,7 +358,6 @@ default:
} }
var runTimer = null; var runTimer = null;
var cpu = new CPU6502({'65C02': enhanced}); var cpu = new CPU6502({'65C02': enhanced});
var context1, context2, context3, context4; var context1, context2, context3, context4;
@ -360,12 +397,13 @@ var io = new Apple2IO(cpu, vm);
var keyboard = new KeyBoard(io, true); var keyboard = new KeyBoard(io, true);
var audio = new Audio(io); var audio = new Audio(io);
var tape = new Tape(io); var tape = new Tape(io);
var printer = new Printer($('#printer .paper'));
var mmu = new MMU(cpu, vm, gr, gr2, hgr, hgr2, io, rom); var mmu = new MMU(cpu, vm, gr, gr2, hgr, hgr2, io, rom);
cpu.addPageHandler(mmu); cpu.addPageHandler(mmu);
var parallel = new Parallel(io, 1, new Printer()); var parallel = new Parallel(io, 1, printer);
var slinky = new RAMFactor(io, 2, 1024 * 1024); var slinky = new RAMFactor(io, 2, 1024 * 1024);
var disk2 = new DiskII(io, 6, drivelights); var disk2 = new DiskII(io, 6, drivelights);
var clock = new Thunderclock(io, 7); var clock = new Thunderclock(io, 7);
@ -399,7 +437,14 @@ function updateKHz() {
} }
function updateSound() { function updateSound() {
audio.enable($('#enable_sound').attr('checked')); var on = $('#enable_sound').prop('checked');
var label = $('#toggle-sound i');
audio.enable(on);
if (on) {
label.removeClass('fa-volume-off').addClass('fa-volume-up');
} else {
label.removeClass('fa-volume-up').addClass('fa-volume-off');
}
} }
function dumpDisk(drive) { function dumpDisk(drive) {
@ -499,7 +544,7 @@ function run(pc) {
cpu.stepCycles(step); cpu.stepCycles(step);
} }
vm.blit(); vm.blit();
io.sampleTick(); io.tick();
} }
processGamepad(io); processGamepad(io);
@ -726,6 +771,8 @@ function processHash(hash) {
*/ */
function _keydown(evt) { function _keydown(evt) {
audio.autoStart();
if (!focused) { if (!focused) {
evt.preventDefault(); evt.preventDefault();
@ -842,17 +889,24 @@ function _mousemove(evt) {
io.paddle(1, flipY ? 1 - y : y); io.paddle(1, flipY ? 1 - y : y);
} }
function pauseRun(b) { function pauseRun() {
var label = $('#pause-run i');
if (paused) { if (paused) {
run(); run();
b.value = 'Pause'; label.removeClass('fa-play').addClass('fa-pause');
} else { } else {
stop(); stop();
b.value = 'Run'; label.removeClass('fa-pause').addClass('fa-play');
} }
paused = !paused; paused = !paused;
} }
function toggleSound() {
var enableSound = $('#enable_sound');
enableSound.prop('checked', !enableSound.prop('checked'));
updateSound();
}
$(function() { $(function() {
hashtag = document.location.hash; hashtag = document.location.hash;
@ -868,15 +922,18 @@ $(function() {
* Input Handling * Input Handling
*/ */
$(window).keydown(_keydown); $(window)
$(window).keyup(_keyup); .keydown(_keydown)
.keyup(_keyup)
.mousedown(function() { audio.autoStart(); });
$('canvas').mousedown(function(evt) { $('canvas')
if (!gamepad) { .mousedown(function(evt) {
io.buttonDown(evt.which == 1 ? 0 : 1); if (!gamepad) {
} io.buttonDown(evt.which == 1 ? 0 : 1);
evt.preventDefault(); }
}) evt.preventDefault();
})
.mouseup(function(evt) { .mouseup(function(evt) {
if (!gamepad) { if (!gamepad) {
io.buttonUp(evt.which == 1 ? 0 : 1); io.buttonUp(evt.which == 1 ? 0 : 1);
@ -886,13 +943,11 @@ $(function() {
$('body').mousemove(_mousemove); $('body').mousemove(_mousemove);
$('body > div').hover(function() { focused = false; }, $('input,textarea')
function() { focused = true; }); .focus(function() { focused = true; })
$('input,textarea').focus(function() { focused = true; })
.blur(function() { focused = false; }); .blur(function() { focused = false; });
keyboard.create($('#keyboard')); keyboard.create('#keyboard');
if (prefs.havePrefs()) { if (prefs.havePrefs()) {
$('#options input[type=checkbox]').each(function() { $('#options input[type=checkbox]').each(function() {
@ -912,7 +967,6 @@ $(function() {
} }
reset(); reset();
run();
setInterval(updateKHz, 1000); setInterval(updateKHz, 1000);
updateSound(); updateSound();
updateScreen(); updateScreen();
@ -944,6 +998,16 @@ $(function() {
width: 320, width: 320,
buttons: {'Close': cancel } buttons: {'Close': cancel }
}); });
$('#printer').dialog({
autoOpen: false,
modal: true,
resizeable: false,
width: 570,
buttons: {
'Clear': printer.clear,
'Close': cancel
}
});
$('#http_load').dialog({ $('#http_load').dialog({
autoOpen: false, autoOpen: false,
modal: true, modal: true,
@ -993,4 +1057,5 @@ $(function() {
$('body').addClass('standalone'); $('body').addClass('standalone');
} }
run();
}); });

View File

@ -494,7 +494,7 @@ function MMU(cpu, vm, lores1, lores2, hires1, hires2, io, rom)
} }
break; break;
// Graphics Switches // Graphics Switches
case LOC.PAGE1: case LOC.PAGE1:
_page2 = false; _page2 = false;
@ -523,7 +523,7 @@ function MMU(cpu, vm, lores1, lores2, hires1, hires2, io, rom)
_debug('Hires on'); _debug('Hires on');
break; break;
// Language Card Switches // Language Card Switches
case LOC.READBSR2: // 0xC080 case LOC.READBSR2: // 0xC080
case LOC._READBSR2: // 0xC084 case LOC._READBSR2: // 0xC084
@ -590,7 +590,7 @@ function MMU(cpu, vm, lores1, lores2, hires1, hires2, io, rom)
//_debug('Bank 1 Read/Write'); //_debug('Bank 1 Read/Write');
break; break;
// Status registers // Status registers
case LOC.BSRBANK2: case LOC.BSRBANK2:
_debug('Bank 2 Read ' + !_bank1); _debug('Bank 2 Read ' + !_bank1);

View File

@ -23,6 +23,7 @@ function Audio(io) {
var audioContext; var audioContext;
var AudioContext = window.AudioContext || window.webkitAudioContext; var AudioContext = window.AudioContext || window.webkitAudioContext;
var audioNode; var audioNode;
var started = false;
if (AudioContext) { if (AudioContext) {
audioContext = new AudioContext(); audioContext = new AudioContext();
@ -67,6 +68,14 @@ function Audio(io) {
_initAudio(io); _initAudio(io);
return { return {
autoStart: function () {
if (audioContext && !started) {
_samples = [];
audioContext.resume();
started = true;
}
},
start: function () { start: function () {
if (audioContext) { if (audioContext) {
_samples = []; _samples = [];

View File

@ -1,5 +1,5 @@
/*! /*!
* Copyright 2010-2017 Will Scullin <scullin@scullinsteel.com> * Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
* *
* Permission to use, copy, modify, distribute, and sell this software and its * Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that * documentation for any purpose is hereby granted without fee, provided that

View File

@ -278,7 +278,14 @@ function KeyBoard(io, e) {
}, },
create: function keyboard_create(kb) { reset: function keyboard_reset(event) {
event.preventDefault();
event.stopPropagation();
reset();
},
create: function keyboard_create(el) {
var kb = $(el);
var x, y, row, key, key1, key2, label, label1, label2, self = this; var x, y, row, key, key1, key2, label, label1, label2, self = this;
kb.disableSelection(); kb.disableSelection();
@ -295,6 +302,7 @@ function KeyBoard(io, e) {
} }
function _mousedown(ev) { function _mousedown(ev) {
ev.preventDefault();
$(this).addClass('pressed'); $(this).addClass('pressed');
var key = $(ev.currentTarget).data(shifted ? 'key2' : 'key1'); var key = $(ev.currentTarget).data(shifted ? 'key2' : 'key1');
switch (key) { switch (key) {

View File

@ -1,38 +1,46 @@
/*globals debug: false */
/*exported Printer */ /*exported Printer */
function Printer() { function Printer(paper) {
var _printer = null; var _lineBuffer;
var _linebuffer = ''; var _line;
function newLine() {
_line = $('<div>').addClass('line').text(_lineBuffer);
paper.append(_line);
_lineBuffer = '';
}
newLine();
return { return {
putChar: function(val) { putChar: function(val) {
if (!_printer || _printer.closed) { var ascii = val & 0x7f;
_printer = window.open('', '_blank','toolbar=0,location=0'); var visible = val >= 0x20;
if (_printer) { var c = String.fromCharCode(ascii);
_printer.document.title = 'Printer';
_printer.document.write('<div style="font: 12px courier">'); if (c == '\r') {
_printer.document.write('<span>'); newLine();
window.focus(); _lineBuffer = '';
} } else if (c == '\t') {
} _lineBuffer += ' ';
var c = String.fromCharCode(val & 0x7f); } else if (c == '\010') {
if (_printer) { _lineBuffer = _lineBuffer.slice(0, -1);
if (c == '\r') {
_printer.document.write('<br /></span>');
} else if (c == ' ') {
_printer.document.write('&nbsp;');
} else {
_printer.document.write(c);
}
} else { } else {
if (c == '\r') { if (visible) {
debug(_linebuffer); _lineBuffer += c;
_linebuffer = '';
} else if (c == ' ') {
_linebuffer += c;
} }
} }
_line.text(_lineBuffer);
},
clear: function() {
_lineBuffer = '';
paper.empty();
newLine();
},
hasPrintout: function() {
return paper.text().length();
} }
}; };
} }

View File

@ -24,15 +24,26 @@ var hex_digits = '0123456789ABCDEF';
var bin_digits = '01'; var bin_digits = '01';
function allocMem(size) { function allocMem(size) {
function garbage() {
return (Math.random() * 0x100) & 0xff;
}
var result; var result;
if (window.Uint8Array) { if (window.Uint8Array) {
result = new Uint8Array(size); result = new Uint8Array(size);
} else { } else {
result = new Array(size); result = new Array(size);
} }
for (var idx = 0; idx < size; idx++) { var idx;
for (idx = 0; idx < size; idx++) {
result[idx] = (idx & 0x02) ? 0x00 : 0xff; result[idx] = (idx & 0x02) ? 0x00 : 0xff;
} }
// Borrowed from AppleWin (https://github.com/AppleWin/AppleWin)
for(idx = 0; idx < size; idx += 0x200 ) {
result[idx + 0x28] = garbage();
result[idx + 0x29] = garbage();
result[idx + 0x68] = garbage();
result[idx + 0x69] = garbage();
}
return result; return result;
} }