Refactor tape drive.
This commit is contained in:
parent
8ee8b01d0a
commit
131db0340a
|
@ -67,6 +67,7 @@
|
|||
<script type="text/javascript" src="js/ui/keyboard.js"></script>
|
||||
<script type="text/javascript" src="js/ui/gamepad.js"></script>
|
||||
<script type="text/javascript" src="js/ui/printer.js"></script>
|
||||
<script type="text/javascript" src="js/ui/tape.js"></script>
|
||||
|
||||
<!-- Disk Index -->
|
||||
<script type="text/javascript" src="json/disks/index.js"></script>
|
||||
|
@ -160,12 +161,12 @@
|
|||
<h3>CPU</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<select id="computer_type" value="apple2plus" onchange="updateCPU()">
|
||||
<select id="computer_type2" value="apple2plus" onchange="updateCPU()">
|
||||
<option value="apple2plus">Apple ][+</option>
|
||||
<option value="apple2">Autostart Apple ][</option>
|
||||
<option value="original">Apple ][</option>
|
||||
</select>
|
||||
<label for="computer_type">
|
||||
<label for="computer_type2">
|
||||
Type
|
||||
</label>
|
||||
</li>
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
<script type="text/javascript" src="js/ui/keyboard.js"></script>
|
||||
<script type="text/javascript" src="js/ui/gamepad.js"></script>
|
||||
<script type="text/javascript" src="js/ui/printer.js"></script>
|
||||
<script type="text/javascript" src="js/ui/tape.js"></script>
|
||||
|
||||
<!-- Disk Index -->
|
||||
<script type="text/javascript" src="json/disks/index.js"></script>
|
||||
|
@ -138,11 +139,11 @@
|
|||
<h3>CPU</h3>
|
||||
<ul>
|
||||
<li>
|
||||
<select id="computer_type" value="apple2enh" onchange="updateCPU()">
|
||||
<select id="computer_type2e" value="apple2enh" onchange="updateCPU()">
|
||||
<option value="apple2enh">Enhanced Apple //e</option>
|
||||
<option value="apple2e">Apple //e</option>
|
||||
</select>
|
||||
<label for="computer_type">
|
||||
<label for="computer_type2e">
|
||||
Type
|
||||
</label>
|
||||
</li>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
JSLINT = jshint
|
||||
JSLINT = eslint
|
||||
|
||||
JSFILES = copyright.js util.js prefs.js ram.js langcard.js fpbasic.js apple2char.js canvas2.js apple2io.js parallel.js ramfactor.js disk2.js cpu6502.js base64.js
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ function Apple2IO(cpu, callbacks)
|
|||
var _tape = [];
|
||||
var _tapeOffset = 0;
|
||||
var _tapeNext = 0;
|
||||
var _tapeFlip = false;
|
||||
var _tapeCurrent = false;
|
||||
|
||||
var LOC = {
|
||||
KEYBOARD: 0x00, // keyboard data (latched) (Read),
|
||||
|
@ -285,32 +285,20 @@ function Apple2IO(cpu, callbacks)
|
|||
_tapeOffset = 0;
|
||||
_tapeNext = now;
|
||||
}
|
||||
|
||||
if (_tapeOffset < _tape.length) {
|
||||
_tapeCurrent = _tape[_tapeOffset][1];
|
||||
while (now >= _tapeNext) {
|
||||
if ((_tapeOffset % 1000) === 0) {
|
||||
debug('Read ' + (_tapeOffset / 1000));
|
||||
}
|
||||
_tapeFlip = !_tapeFlip;
|
||||
// flipped = true;
|
||||
_tapeNext += _tape[_tapeOffset++];
|
||||
_tapeCurrent = _tape[_tapeOffset][1];
|
||||
_tapeNext += _tape[_tapeOffset++][0];
|
||||
}
|
||||
result = _tapeFlip ? 0x80 : 0x00;
|
||||
}
|
||||
/*
|
||||
if (flipped) {
|
||||
debug('now=' + now + ' next=' + _tapeNext + ' (' + (_tapeNext - now) + ')');
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
var progress =
|
||||
Math.round(_tapeOffset / _tapeBuffer.length * 100) / 100;
|
||||
|
||||
if (_progress != progress) {
|
||||
_progress = progress;
|
||||
cb.progress(_progress);
|
||||
}
|
||||
*/
|
||||
|
||||
result = _tapeCurrent ? 0x80 : 0x00;
|
||||
}
|
||||
|
||||
if (val !== undefined) {
|
||||
|
@ -461,6 +449,10 @@ function Apple2IO(cpu, callbacks)
|
|||
_cycles_per_sample = _hz / _rate;
|
||||
},
|
||||
|
||||
getHz: function apple2io_updateHz() {
|
||||
return _hz;
|
||||
},
|
||||
|
||||
setKeyBuffer: function apple2io_setKeyBuffer(buffer) {
|
||||
_buffer = buffer.split('');
|
||||
if (_buffer.length > 0) {
|
||||
|
|
95
js/main2.js
95
js/main2.js
|
@ -19,6 +19,7 @@
|
|||
disk_index: false,
|
||||
Audio: false,
|
||||
initGamepad: false, processGamepad: false, gamepad: false,
|
||||
Tape: false,
|
||||
ApplesoftDump: false, SYMBOLS: false,
|
||||
multiScreen: true
|
||||
*/
|
||||
|
@ -127,7 +128,7 @@ function DriveLights()
|
|||
}
|
||||
|
||||
var DISK_TYPES = ['dsk','d13','do','po','raw','nib','2mg'];
|
||||
var TAPE_TYPES = ['wav','aiff','aif','mp3'];
|
||||
var TAPE_TYPES = ['wav','aiff','aif','mp3','m4a'];
|
||||
|
||||
var _currentDrive = 1;
|
||||
|
||||
|
@ -204,7 +205,7 @@ function doLoad() {
|
|||
|
||||
var files = $('#local_file').prop('files');
|
||||
if (files.length == 1) {
|
||||
doLoadLocal(_currentDrive);
|
||||
doLoadLocal(_currentDrive, files[0]);
|
||||
} else if (url) {
|
||||
var filename;
|
||||
$('#load').dialog('close');
|
||||
|
@ -242,20 +243,18 @@ function doDelete(name) {
|
|||
}
|
||||
}
|
||||
|
||||
function doLoadLocal(drive) {
|
||||
var files = $('#local_file').prop('files');
|
||||
if (files.length == 1) {
|
||||
var file = files[0];
|
||||
var parts = file.name.split('.');
|
||||
var ext = parts[parts.length - 1].toLowerCase();
|
||||
if ($.inArray(ext, DISK_TYPES) >= 0) {
|
||||
doLoadLocalDisk(drive, file);
|
||||
} else if ($.inArray(ext, TAPE_TYPES) >= 0) {
|
||||
doLoadLocalTape(file);
|
||||
} else {
|
||||
window.alert('Unknown file type: ' + ext);
|
||||
function doLoadLocal(drive, file) {
|
||||
var parts = file.name.split('.');
|
||||
var ext = parts[parts.length - 1].toLowerCase();
|
||||
if ($.inArray(ext, DISK_TYPES) >= 0) {
|
||||
doLoadLocalDisk(drive, file);
|
||||
} else if ($.inArray(ext, TAPE_TYPES) >= 0) {
|
||||
tape.doLoadLocalTape(file, function() {
|
||||
$('#load').dialog('close');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
window.alert('Unknown file type: ' + ext);
|
||||
$('#load').dialog('close');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -263,7 +262,8 @@ function doLoadLocalDisk(drive, file) {
|
|||
var fileReader = new FileReader();
|
||||
fileReader.onload = function() {
|
||||
var parts = file.name.split('.');
|
||||
var name = parts[0], ext = parts[parts.length - 1].toLowerCase();
|
||||
var ext = parts.pop().toLowerCase();
|
||||
var name = parts.join('.');
|
||||
if (disk2.setBinary(drive, name, ext, this.result)) {
|
||||
drivelights.label(drive, name);
|
||||
$('#load').dialog('close');
|
||||
|
@ -273,60 +273,6 @@ function doLoadLocalDisk(drive, file) {
|
|||
fileReader.readAsArrayBuffer(file);
|
||||
}
|
||||
|
||||
function doLoadLocalTape(file) {
|
||||
// Audio Buffer Source
|
||||
var context;
|
||||
if (typeof window.AudioContext != 'undefined') {
|
||||
context = new window.AudioContext();
|
||||
} else {
|
||||
window.alert('Not supported by your browser');
|
||||
$('#load').dialog('close');
|
||||
return;
|
||||
}
|
||||
|
||||
var fileReader = new FileReader();
|
||||
fileReader.onload = function(ev) {
|
||||
context.decodeAudioData(ev.target.result, function(buffer) {
|
||||
var buf = [];
|
||||
var data = buffer.getChannelData(0), datum = data[0];
|
||||
var old = (datum > 0.0), current;
|
||||
var last = 0, delta, ival;
|
||||
debug('Sample Count: ' + data.length);
|
||||
debug('Sample rate: ' + buffer.sampleRate);
|
||||
for (var idx = 1; idx < data.length; idx++) {
|
||||
datum = data[idx];
|
||||
if ((datum > 0.1) || (datum < -0.1)) {
|
||||
current = (datum > 0.0);
|
||||
if (current != old) {
|
||||
delta = idx - last;
|
||||
if (delta > 2000000) {
|
||||
delta = 2000000;
|
||||
}
|
||||
ival = delta / buffer.sampleRate * 1000;
|
||||
if (ival >= 0.550 && ival < 0.750) {
|
||||
ival = 0.650; // Header
|
||||
} else if (ival >= 0.175 && ival < 0.225) {
|
||||
ival = 0.200; // sync 1
|
||||
} else if (ival >= 0.225 && ival < 0.275) {
|
||||
ival = 0.250; // 0 / sync 2
|
||||
} else if (ival >= 0.450 && ival < 0.550) {
|
||||
ival = 0.500; // 1
|
||||
} else {
|
||||
// debug(idx + ' ' + buf.length + ' ' + ival);
|
||||
}
|
||||
buf.push(parseInt(ival * kHz));
|
||||
old = current;
|
||||
last = idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
io.setTape(buf);
|
||||
$('#load').dialog('close');
|
||||
});
|
||||
};
|
||||
fileReader.readAsArrayBuffer(file);
|
||||
}
|
||||
|
||||
function doLoadHTTP(drive, _url) {
|
||||
var url = _url || $('#http_url').val();
|
||||
if (url) {
|
||||
|
@ -335,9 +281,11 @@ function doLoadHTTP(drive, _url) {
|
|||
req.responseType = 'arraybuffer';
|
||||
|
||||
req.onload = function() {
|
||||
var parts = url.split(/[/.]/);
|
||||
var name = decodeURIComponent(parts[parts.length - 2]);
|
||||
var ext = parts[parts.length - 1].toLowerCase();
|
||||
var urlParts = url.split('/');
|
||||
var file = urlParts.pop();
|
||||
var fileParts = file.split('.');
|
||||
var ext = fileParts.pop().toLowerCase();
|
||||
var name = decodeURIComponent(fileParts.join('.'));
|
||||
if (disk2.setBinary(drive, name, ext, req.response)) {
|
||||
drivelights.label(drive, name);
|
||||
$('#http_load').dialog('close');
|
||||
|
@ -429,6 +377,7 @@ var drivelights = new DriveLights();
|
|||
var io = new Apple2IO(cpu, vm);
|
||||
var keyboard = new KeyBoard(io);
|
||||
var audio = new Audio(io);
|
||||
var tape = new Tape(io);
|
||||
var lc = new LanguageCard(io, 0, rom);
|
||||
var parallel = new Parallel(io, 1, new Printer());
|
||||
var slinky = new RAMFactor(io, 2, 1024 * 1024);
|
||||
|
|
107
js/main2e.js
107
js/main2e.js
|
@ -16,6 +16,7 @@
|
|||
disk_index: false,
|
||||
Audio: false,
|
||||
initGamepad: false, processGamepad: false, gamepad: false,
|
||||
Tape: false,
|
||||
ApplesoftDump: false, SYMBOLS: false,
|
||||
multiScreen: true
|
||||
*/
|
||||
|
@ -121,7 +122,7 @@ function DriveLights()
|
|||
}
|
||||
|
||||
var DISK_TYPES = ['dsk','d13','do','po','raw','nib','2mg'];
|
||||
var TAPE_TYPES = ['wav','aiff','aif','mp3'];
|
||||
var TAPE_TYPES = ['wav','aiff','aif','mp3','m4a'];
|
||||
|
||||
var _currentDrive = 1;
|
||||
|
||||
|
@ -198,7 +199,7 @@ function doLoad() {
|
|||
|
||||
var files = $('#local_file').prop('files');
|
||||
if (files.length == 1) {
|
||||
doLoadLocal(_currentDrive);
|
||||
doLoadLocal(_currentDrive, files[0]);
|
||||
} else if (url) {
|
||||
var filename;
|
||||
$('#load').dialog('close');
|
||||
|
@ -236,20 +237,18 @@ function doDelete(name) {
|
|||
}
|
||||
}
|
||||
|
||||
function doLoadLocal(drive) {
|
||||
var files = $('#local_file').prop('files');
|
||||
if (files.length == 1) {
|
||||
var file = files[0];
|
||||
var parts = file.name.split('.');
|
||||
var ext = parts[parts.length - 1].toLowerCase();
|
||||
if ($.inArray(ext, DISK_TYPES) >= 0) {
|
||||
doLoadLocalDisk(drive, file);
|
||||
} else if ($.inArray(ext, TAPE_TYPES) >= 0) {
|
||||
doLoadLocalTape(file);
|
||||
} else {
|
||||
window.alert('Unknown file type: ' + ext);
|
||||
function doLoadLocal(drive, file) {
|
||||
var parts = file.name.split('.');
|
||||
var ext = parts[parts.length - 1].toLowerCase();
|
||||
if ($.inArray(ext, DISK_TYPES) >= 0) {
|
||||
doLoadLocalDisk(drive, file);
|
||||
} else if ($.inArray(ext, TAPE_TYPES) >= 0) {
|
||||
tape.doLoadLocalTape(file, function() {
|
||||
$('#load').dialog('close');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
window.alert('Unknown file type: ' + ext);
|
||||
$('#load').dialog('close');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -257,7 +256,8 @@ function doLoadLocalDisk(drive, file) {
|
|||
var fileReader = new FileReader();
|
||||
fileReader.onload = function() {
|
||||
var parts = file.name.split('.');
|
||||
var name = parts[0], ext = parts[parts.length - 1].toLowerCase();
|
||||
var ext = parts.pop().toLowerCase();
|
||||
var name = parts.join('.');
|
||||
if (disk2.setBinary(drive, name, ext, this.result)) {
|
||||
drivelights.label(drive, name);
|
||||
$('#load').dialog('close');
|
||||
|
@ -267,60 +267,6 @@ function doLoadLocalDisk(drive, file) {
|
|||
fileReader.readAsArrayBuffer(file);
|
||||
}
|
||||
|
||||
function doLoadLocalTape(file) {
|
||||
// Audio Buffer Source
|
||||
var context;
|
||||
if (typeof window.AudioContext != 'undefined') {
|
||||
context = new window.AudioContext();
|
||||
} else {
|
||||
window.alert('Not supported by your browser');
|
||||
$('#load').dialog('close');
|
||||
return;
|
||||
}
|
||||
|
||||
var fileReader = new FileReader();
|
||||
fileReader.onload = function(ev) {
|
||||
context.decodeAudioData(ev.target.result, function(buffer) {
|
||||
var buf = [];
|
||||
var data = buffer.getChannelData(0), datum = data[0];
|
||||
var old = (datum > 0.0), current;
|
||||
var last = 0, delta, ival;
|
||||
debug('Sample Count: ' + data.length);
|
||||
debug('Sample rate: ' + buffer.sampleRate);
|
||||
for (var idx = 1; idx < data.length; idx++) {
|
||||
datum = data[idx];
|
||||
if ((datum > 0.1) || (datum < -0.1)) {
|
||||
current = (datum > 0.0);
|
||||
if (current != old) {
|
||||
delta = idx - last;
|
||||
if (delta > 2000000) {
|
||||
delta = 2000000;
|
||||
}
|
||||
ival = delta / buffer.sampleRate * 1000;
|
||||
if (ival >= 0.550 && ival < 0.750) {
|
||||
ival = 0.650; // Header
|
||||
} else if (ival >= 0.175 && ival < 0.225) {
|
||||
ival = 0.200; // sync 1
|
||||
} else if (ival >= 0.225 && ival < 0.275) {
|
||||
ival = 0.250; // 0 / sync 2
|
||||
} else if (ival >= 0.450 && ival < 0.550) {
|
||||
ival = 0.500; // 1
|
||||
} else {
|
||||
// debug(idx + ' ' + buf.length + ' ' + ival);
|
||||
}
|
||||
buf.push(parseInt(ival * kHz));
|
||||
old = current;
|
||||
last = idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
io.setTape(buf);
|
||||
$('#load').dialog('close');
|
||||
});
|
||||
};
|
||||
fileReader.readAsArrayBuffer(file);
|
||||
}
|
||||
|
||||
function doLoadHTTP(drive, _url) {
|
||||
var url = _url || $('#http_url').val();
|
||||
if (url) {
|
||||
|
@ -329,17 +275,15 @@ function doLoadHTTP(drive, _url) {
|
|||
req.responseType = 'arraybuffer';
|
||||
|
||||
req.onload = function() {
|
||||
var parts = url.split(/[/.]/);
|
||||
var name = decodeURIComponent(parts[parts.length - 2]);
|
||||
var ext = parts[parts.length - 1].toLowerCase();
|
||||
if (req.response.byteLength >= 400 * 1024) {
|
||||
// smartport.setBinary(drive, req.response);
|
||||
} else {
|
||||
if (disk2.setBinary(drive, name, ext, req.response)) {
|
||||
drivelights.label(drive, name);
|
||||
$('#http_load').dialog('close');
|
||||
initGamepad();
|
||||
}
|
||||
var urlParts = url.split('/');
|
||||
var file = urlParts.pop();
|
||||
var fileParts = file.split('.');
|
||||
var ext = fileParts.pop().toLowerCase();
|
||||
var name = decodeURIComponent(fileParts.join('.'));
|
||||
if (disk2.setBinary(drive, name, ext, req.response)) {
|
||||
drivelights.label(drive, name);
|
||||
$('#http_load').dialog('close');
|
||||
initGamepad();
|
||||
}
|
||||
};
|
||||
req.send(null);
|
||||
|
@ -415,6 +359,7 @@ var drivelights = new DriveLights();
|
|||
var io = new Apple2IO(cpu, vm);
|
||||
var keyboard = new KeyBoard(io, true);
|
||||
var audio = new Audio(io);
|
||||
var tape = new Tape(io);
|
||||
|
||||
var mmu = new MMU(cpu, vm, gr, gr2, hgr, hgr2, io, rom);
|
||||
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
/*globals debug */
|
||||
/*exported Tape */
|
||||
|
||||
function Tape(io) {
|
||||
var AudioContext = window.AudioContext || window.webkitAudioContext;
|
||||
|
||||
return {
|
||||
doLoadLocalTape: function(file, done) {
|
||||
var kHz = io.getHz() / 1000;
|
||||
|
||||
// Audio Buffer Source
|
||||
var context;
|
||||
if (AudioContext) {
|
||||
context = new AudioContext();
|
||||
} else {
|
||||
window.alert('Not supported by your browser');
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
var fileReader = new FileReader();
|
||||
fileReader.onload = function(ev) {
|
||||
context.decodeAudioData(ev.target.result, function(buffer) {
|
||||
var buf = [];
|
||||
var data = buffer.getChannelData(0), datum = data[0];
|
||||
var old = (datum > 0.0), current;
|
||||
var last = 0, delta, ival;
|
||||
debug('Sample Count: ' + data.length);
|
||||
debug('Sample rate: ' + buffer.sampleRate);
|
||||
for (var idx = 1; idx < data.length; idx++) {
|
||||
datum = data[idx];
|
||||
if ((datum > 0.1) || (datum < -0.1)) {
|
||||
current = (datum > 0.0);
|
||||
if (current != old) {
|
||||
delta = idx - last;
|
||||
if (delta > 2000000) {
|
||||
delta = 2000000;
|
||||
}
|
||||
ival = delta / buffer.sampleRate * 1000;
|
||||
if (ival >= 0.550 && ival < 0.750) {
|
||||
ival = 0.650; // Header
|
||||
} else if (ival >= 0.175 && ival < 0.225) {
|
||||
ival = 0.200; // sync 1
|
||||
} else if (ival >= 0.225 && ival < 0.275) {
|
||||
ival = 0.250; // 0 / sync 2
|
||||
} else if (ival >= 0.450 && ival < 0.550) {
|
||||
ival = 0.500; // 1
|
||||
} else {
|
||||
// debug(idx + ' ' + buf.length + ' ' + ival);
|
||||
}
|
||||
buf.push([parseInt(ival * kHz), current]);
|
||||
old = current;
|
||||
last = idx;
|
||||
}
|
||||
}
|
||||
}
|
||||
io.setTape(buf);
|
||||
done();
|
||||
});
|
||||
};
|
||||
fileReader.readAsArrayBuffer(file);
|
||||
}
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue