CFFA (#20)
* CFFA * CFFA multi-disk and write functionaliity. * Clean up multi-devices/partition behavior. * ProDOS WIP * Update against refactored codebase. * WIP * Wait until disks load, show progress. * Don't wait so long to boot floppies. * Forgot to save :|. * Credit.
This commit is contained in:
parent
8ea7cf2abe
commit
b647b3c2bb
|
@ -13,12 +13,16 @@
|
|||
"unix"
|
||||
],
|
||||
"semi": [
|
||||
2,
|
||||
1,
|
||||
"always"
|
||||
],
|
||||
"no-use-before-define": [
|
||||
2,
|
||||
{ "functions": false }
|
||||
],
|
||||
"no-console": [
|
||||
2,
|
||||
{ "allow": ["info", "warn", "error"] }
|
||||
]
|
||||
},
|
||||
"env": {
|
||||
|
|
|
@ -149,6 +149,7 @@ then
|
|||
* [jQuery](https://jquery.com) and [jQuery UI](https://jqueryui.com)
|
||||
* Base64 Utilities via [KvZ](http://kevin.vanzonneveld.net/)
|
||||
* LED graphics from [Modern Life](http://modernl.com/).
|
||||
* [CFFA2 Firmware](http://dreher.net/?s=projects/CFforAppleII&c=projects/CFforAppleII/downloads1.php) by Chris Schumann, Rich Dreher and Dave Lyons
|
||||
|
||||
* I heavily referenced:
|
||||
|
||||
|
|
|
@ -139,10 +139,15 @@
|
|||
<div class="modal__overlay" tabindex="-1" data-micromodal-close>
|
||||
<div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="Loading" >
|
||||
<header class="modal__header">
|
||||
<span class="modal__title" id="loading-modal-title">
|
||||
<div class="modal__title" id="loading-modal-title">
|
||||
Loading...
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
<main class="modal__content" id="loading-modal-content">
|
||||
<div class="meter">
|
||||
<div class="progress"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -378,6 +383,28 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="alert-modal" aria-hidden="true">
|
||||
<div class="modal__overlay" tabindex="-1" data-micromodal-close>
|
||||
<div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="Printer">
|
||||
<header class="modal__header">
|
||||
<span class="modal__title" id="printer-modal-title">
|
||||
Alert
|
||||
</span>
|
||||
<button class="modal__close" aria-label="Close modal" data-micromodal-close>
|
||||
</button>
|
||||
</header>
|
||||
<main class="modal__content" id="alert-modal-content">
|
||||
<div class=".message">
|
||||
</div>
|
||||
</main>
|
||||
<footer class="modal__footer">
|
||||
<button class="modal__btn" data-micromodal-close aria-label="Close this dialog window">Close</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="dist/main2.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -120,10 +120,15 @@
|
|||
<div class="modal__overlay" tabindex="-1" data-micromodal-close>
|
||||
<div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="Loading" >
|
||||
<header class="modal__header">
|
||||
<span class="modal__title" id="loading-modal-title">
|
||||
<div class="modal__title" id="loading-modal-title">
|
||||
Loading...
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
<main class="modal__content" id="loading-modal-content">
|
||||
<div class="meter">
|
||||
<div class="progress"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -354,6 +359,28 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="alert-modal" aria-hidden="true">
|
||||
<div class="modal__overlay" tabindex="-1" data-micromodal-close>
|
||||
<div class="modal__container" role="dialog" aria-modal="true" aria-labelledby="Printer">
|
||||
<header class="modal__header">
|
||||
<span class="modal__title" id="printer-modal-title">
|
||||
Alert
|
||||
</span>
|
||||
<button class="modal__close" aria-label="Close modal" data-micromodal-close>
|
||||
</button>
|
||||
</header>
|
||||
<main class="modal__content" id="alert-modal-content">
|
||||
<div class="message">
|
||||
</div>
|
||||
</main>
|
||||
<footer class="modal__footer">
|
||||
<button class="modal__btn" data-micromodal-close aria-label="Close this dialog window">Close</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="dist/main2e.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -230,6 +230,19 @@ canvas {
|
|||
line-height: 14px;
|
||||
}
|
||||
|
||||
#loading-modal .meter {
|
||||
width: 200px;
|
||||
height: 15px;
|
||||
background-color: black;
|
||||
border: 1px inset #888;
|
||||
}
|
||||
|
||||
#loading-modal .progress {
|
||||
position: absolute;
|
||||
height: 15px;
|
||||
background-color: #0f0;
|
||||
}
|
||||
|
||||
.modal {
|
||||
display: none;
|
||||
}
|
||||
|
@ -609,3 +622,8 @@ button:focus {
|
|||
font-family: monospace;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
#alert-modal-content {
|
||||
min-width: 200px;
|
||||
min-height: 50px;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,411 @@
|
|||
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and its
|
||||
* documentation for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation. No representations are made about the suitability of this
|
||||
* software for any purpose. It is provided "as is" without express or
|
||||
* implied warranty.
|
||||
*/
|
||||
|
||||
import { debug, toHex } from '../util';
|
||||
import { rom } from '../roms/cards/cffa';
|
||||
import _2MG from '../formats/2mg';
|
||||
import { ProDOSVolume } from '../formats/prodos';
|
||||
import BlockVolume from '../formats/block';
|
||||
import { dump } from '../formats/prodos/utils';
|
||||
|
||||
export default function CFFA() {
|
||||
var COMMANDS = {
|
||||
ATACRead: 0x20,
|
||||
ATACWrite: 0x30,
|
||||
ATAIdentify: 0xEC
|
||||
};
|
||||
|
||||
// CFFA Card Settings
|
||||
|
||||
var SETTINGS = {
|
||||
Max32MBPartitionsDev0: 0x800,
|
||||
Max32MBPartitionsDev1: 0x801,
|
||||
DefaultBootDevice: 0x802,
|
||||
DefaultBootPartition: 0x803,
|
||||
Reserved: 0x804, // 4 bytes
|
||||
WriteProtectBits: 0x808,
|
||||
MenuSnagMask: 0x809,
|
||||
MenuSnagKey: 0x80A,
|
||||
BootTimeDelayTenths: 0x80B,
|
||||
BusResetSeconds: 0x80C,
|
||||
CheckDeviceTenths: 0x80D,
|
||||
ConfigOptionBits: 0x80E,
|
||||
BlockOffsetDev0: 0x80F, // 3 bytes
|
||||
BlockOffsetDev1: 0x812, // 3 bytes
|
||||
Unused: 0x815
|
||||
};
|
||||
|
||||
// CFFA ATA Register Locations
|
||||
|
||||
var LOC = {
|
||||
ATADataHigh: 0x80,
|
||||
SetCSMask: 0x81,
|
||||
ClearCSMask: 0x82,
|
||||
WriteEEPROM: 0x83,
|
||||
NoWriteEEPROM: 0x84,
|
||||
ATADevCtrl: 0x86,
|
||||
ATAAltStatus: 0x86,
|
||||
ATADataLow: 0x88,
|
||||
AError: 0x89,
|
||||
ASectorCnt: 0x8a,
|
||||
ASector: 0x8b,
|
||||
ATACylinder: 0x8c,
|
||||
ATACylinderH: 0x8d,
|
||||
ATAHead: 0x8e,
|
||||
ATACommand: 0x8f,
|
||||
ATAStatus: 0x8f
|
||||
};
|
||||
|
||||
// ATA Status Bits
|
||||
|
||||
var STATUS = {
|
||||
BSY: 0x80, // Busy
|
||||
DRDY: 0x40, // Drive ready. 1 when ready
|
||||
DWF: 0x20, // Drive write fault. 1 when fault
|
||||
DSC: 0x10, // Disk seek complete. 1 when not seeking
|
||||
DRQ: 0x08, // Data request. 1 when ready to write
|
||||
CORR: 0x04, // Correct data. 1 on correctable error
|
||||
IDX: 0x02, // 1 once per revolution
|
||||
ERR: 0x01 // Error. 1 on error
|
||||
};
|
||||
|
||||
// ATA Identity Block Locations
|
||||
|
||||
var IDENTITY = {
|
||||
SectorCountLow: 58,
|
||||
SectorCountHigh: 57
|
||||
};
|
||||
|
||||
// CFFA internal Flags
|
||||
|
||||
var _disableSignalling = false;
|
||||
var _writeEEPROM = true;
|
||||
|
||||
var _lba = true;
|
||||
|
||||
// LBA/CHS registers
|
||||
|
||||
var _sectorCnt = 1;
|
||||
var _sector = 0;
|
||||
var _cylinder = 0;
|
||||
var _cylinderH = 0;
|
||||
var _head = 0;
|
||||
var _drive = 0;
|
||||
|
||||
// CFFA Data High register
|
||||
|
||||
var _dataHigh = 0;
|
||||
|
||||
// Current Sector
|
||||
|
||||
var _curSector = [];
|
||||
var _curWord = 0;
|
||||
|
||||
// ATA Status registers
|
||||
|
||||
var _interruptsEnabled = false;
|
||||
var _altStatus = 0;
|
||||
var _error = 0;
|
||||
|
||||
var _identity = [[], []];
|
||||
|
||||
// Disk data
|
||||
|
||||
var _partitions = [
|
||||
// Drive 1
|
||||
[],
|
||||
// Drive 2
|
||||
[]
|
||||
];
|
||||
|
||||
var _sectors = [
|
||||
// Drive 1
|
||||
[],
|
||||
// Drive 2
|
||||
[]
|
||||
];
|
||||
|
||||
function _init() {
|
||||
debug('CFFA');
|
||||
|
||||
for (var idx = 0; idx < 0x100; idx++) {
|
||||
_identity[0][idx] = 0;
|
||||
_identity[1][idx] = 0;
|
||||
}
|
||||
|
||||
rom[SETTINGS.Max32MBPartitionsDev0] = 0x1;
|
||||
rom[SETTINGS.Max32MBPartitionsDev1] = 0x0;
|
||||
rom[SETTINGS.BootTimeDelayTenths] = 0x14; // 2 seconds
|
||||
rom[SETTINGS.CheckDeviceTenths] = 0x14; // 2 seconds
|
||||
}
|
||||
|
||||
// Verbose debug method
|
||||
|
||||
function _debug() {
|
||||
// debug.apply(this, arguments);
|
||||
}
|
||||
|
||||
function _reset() {
|
||||
_debug('reset');
|
||||
|
||||
_sectorCnt = 1;
|
||||
_sector = 0;
|
||||
_cylinder = 0;
|
||||
_cylinderH = 0;
|
||||
_head = 0;
|
||||
_drive = 0;
|
||||
|
||||
_dataHigh = 0;
|
||||
}
|
||||
|
||||
// Convert status register into readable string
|
||||
|
||||
function _statusString(status) {
|
||||
var statusArray = [];
|
||||
for (var flag in STATUS) {
|
||||
if(status & STATUS[flag]) {
|
||||
statusArray.push(flag);
|
||||
}
|
||||
}
|
||||
return statusArray.join('|');
|
||||
}
|
||||
|
||||
// Dump sector as hex and ascii
|
||||
|
||||
function _dumpSector(sector) {
|
||||
if (sector >= _sectors[_drive].length) {
|
||||
_debug('dump sector out of range', sector);
|
||||
return;
|
||||
}
|
||||
for (var idx = 0; idx < 16; idx++) {
|
||||
var row = [];
|
||||
var charRow = [];
|
||||
for (var jdx = 0; jdx < 16; jdx++) {
|
||||
var val = _sectors[_drive][sector][idx * 16 + jdx];
|
||||
row.push(toHex(val, 4));
|
||||
var low = val & 0x7f;
|
||||
var hi = val >> 8 & 0x7f;
|
||||
charRow.push(low > 0x1f ? String.fromCharCode(low) : '.');
|
||||
charRow.push(hi > 0x1f ? String.fromCharCode(hi) : '.');
|
||||
}
|
||||
_debug(row.join(' '), ' ', charRow.join(''));
|
||||
}
|
||||
}
|
||||
|
||||
// Card I/O access
|
||||
|
||||
function _access(off, val) {
|
||||
var readMode = val === undefined;
|
||||
var retVal = readMode ? 0 : undefined;
|
||||
var sector;
|
||||
|
||||
if (readMode) {
|
||||
switch (off & 0x8f) {
|
||||
case LOC.ATADataHigh: // 0x00
|
||||
retVal = _dataHigh;
|
||||
break;
|
||||
case LOC.SetCSMask: // 0x01
|
||||
_disableSignalling = true;
|
||||
break;
|
||||
case LOC.ClearCSMask: // 0x02
|
||||
_disableSignalling = false;
|
||||
break;
|
||||
case LOC.WriteEEPROM: // 0x03
|
||||
_writeEEPROM = true;
|
||||
break;
|
||||
case LOC.NoWriteEEPROM: // 0x04
|
||||
_writeEEPROM = false;
|
||||
break;
|
||||
case LOC.ATAAltStatus: // 0x06
|
||||
retVal = _altStatus;
|
||||
break;
|
||||
case LOC.ATADataLow: // 0x08
|
||||
_dataHigh = _curSector[_curWord] >> 8;
|
||||
retVal = _curSector[_curWord] & 0xff;
|
||||
if (!_disableSignalling) {
|
||||
_curWord++;
|
||||
}
|
||||
break;
|
||||
case LOC.AError: // 0x09
|
||||
retVal = _error;
|
||||
break;
|
||||
case LOC.ASectorCnt: // 0x0A
|
||||
retVal = _sectorCnt;
|
||||
break;
|
||||
case LOC.ASector: // 0x0B
|
||||
retVal = _sector;
|
||||
break;
|
||||
case LOC.ATACylinder: // 0x0C
|
||||
retVal = _cylinder;
|
||||
break;
|
||||
case LOC.ATACylinderH: // 0x0D
|
||||
retVal = _cylinderH;
|
||||
break;
|
||||
case LOC.ATAHead: // 0x0E
|
||||
retVal = _head | (_lba ? 0x40 : 0) | (_drive ? 0x10 : 0) | 0xA0;
|
||||
break;
|
||||
case LOC.ATAStatus: // 0x0F
|
||||
retVal = _sectors[_drive].length > 0 ? STATUS.DRDY | STATUS.DSC : 0;
|
||||
_debug('returning status', _statusString(retVal));
|
||||
break;
|
||||
default:
|
||||
debug('read unknown soft switch', toHex(off));
|
||||
}
|
||||
|
||||
if (off & 0x7) { // Anything but data high/low
|
||||
_debug('read soft switch', toHex(off), toHex(retVal));
|
||||
}
|
||||
} else {
|
||||
if (off & 0x7) { // Anything but data high/low
|
||||
_debug('write soft switch', toHex(off), toHex(val));
|
||||
}
|
||||
|
||||
switch (off & 0x8f) {
|
||||
case LOC.ATADataHigh: // 0x00
|
||||
_dataHigh = val;
|
||||
break;
|
||||
case LOC.SetCSMask: // 0x01
|
||||
_disableSignalling = true;
|
||||
break;
|
||||
case LOC.ClearCSMask: // 0x02
|
||||
_disableSignalling = false;
|
||||
break;
|
||||
case LOC.WriteEEPROM: // 0x03
|
||||
_writeEEPROM = true;
|
||||
break;
|
||||
case LOC.NoWriteEEPROM: // 0x04
|
||||
_writeEEPROM = false;
|
||||
break;
|
||||
case LOC.ATADevCtrl: // 0x06
|
||||
_debug('devCtrl:', toHex(val));
|
||||
_interruptsEnabled = (val & 0x04) ? true : false;
|
||||
_debug('Interrupts', _interruptsEnabled ? 'enabled' : 'disabled');
|
||||
if (val & 0x02) {
|
||||
_reset();
|
||||
}
|
||||
break;
|
||||
case LOC.ATADataLow: // 0x08
|
||||
_curSector[_curWord] = _dataHigh << 8 | val;
|
||||
_curWord++;
|
||||
break;
|
||||
case LOC.ASectorCnt: // 0x0a
|
||||
_debug('setting sector count', val);
|
||||
_sectorCnt = val;
|
||||
break;
|
||||
case LOC.ASector: // 0x0b
|
||||
_debug('setting sector', toHex(val));
|
||||
_sector = val;
|
||||
break;
|
||||
case LOC.ATACylinder: // 0x0c
|
||||
_debug('setting cylinder', toHex(val));
|
||||
_cylinder = val;
|
||||
break;
|
||||
case LOC.ATACylinderH: // 0x0d
|
||||
_debug('setting cylinder high', toHex(val));
|
||||
_cylinderH = val;
|
||||
break;
|
||||
case LOC.ATAHead:
|
||||
_head = val & 0xf;
|
||||
_lba = val & 0x40 ? true : false;
|
||||
_drive = val & 0x10 ? 1 : 0;
|
||||
_debug('setting head', toHex(val & 0xf), 'drive', _drive);
|
||||
if (!_lba) {
|
||||
console.error('CHS mode not supported');
|
||||
}
|
||||
break;
|
||||
case LOC.ATACommand: // 0x0f
|
||||
_debug('command:', toHex(val));
|
||||
sector = _head << 24 | _cylinderH << 16 | _cylinder << 8 | _sector;
|
||||
_dumpSector(sector);
|
||||
|
||||
switch (val) {
|
||||
case COMMANDS.ATAIdentify:
|
||||
_debug('ATA identify');
|
||||
_curSector = _identity[_drive];
|
||||
_curWord = 0;
|
||||
break;
|
||||
case COMMANDS.ATACRead:
|
||||
_debug('ATA read sector', toHex(_cylinderH), toHex(_cylinder), toHex(_sector), sector);
|
||||
_curSector = _sectors[_drive][sector];
|
||||
_curWord = 0;
|
||||
break;
|
||||
case COMMANDS.ATACWrite:
|
||||
_debug('ATA write sector', toHex(_cylinderH), toHex(_cylinder), toHex(_sector), sector);
|
||||
_curSector = _sectors[_drive][sector];
|
||||
_curWord = 0;
|
||||
break;
|
||||
default:
|
||||
debug('unknown command', toHex(val));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
debug('write unknown soft switch', toHex(off), toHex(val));
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
_init();
|
||||
|
||||
return {
|
||||
ioSwitch: function (off, val) {
|
||||
return _access(off, val);
|
||||
},
|
||||
|
||||
read: function(page, off) {
|
||||
return rom[(page - 0xc0) << 8 | off];
|
||||
},
|
||||
|
||||
write: function(page, off, val) {
|
||||
if (_writeEEPROM) {
|
||||
_debug('writing', toHex(page << 8 | off), toHex(val));
|
||||
rom[(page - 0xc0) << 8 | off] - val;
|
||||
}
|
||||
},
|
||||
|
||||
// Assign a raw disk image to a drive. Must be 2mg or raw PO image.
|
||||
|
||||
setBinary: function(drive, name, ext, rawData) {
|
||||
drive = drive - 1;
|
||||
var disk;
|
||||
var options = {
|
||||
rawData,
|
||||
name
|
||||
};
|
||||
|
||||
if (ext === '2mg') {
|
||||
disk = new _2MG(options);
|
||||
} else {
|
||||
disk = new BlockVolume(options);
|
||||
}
|
||||
|
||||
// Convert 512 byte blocks into 256 word sectors
|
||||
_sectors[drive] = disk.blocks.map(function(block) {
|
||||
return new Uint16Array(block.buffer);
|
||||
});
|
||||
|
||||
_identity[drive][IDENTITY.SectorCountHigh] = _sectors[0].length & 0xffff;
|
||||
_identity[drive][IDENTITY.SectorCountLow] = _sectors[0].length >> 16;
|
||||
|
||||
var prodos = new ProDOSVolume(disk);
|
||||
dump(prodos);
|
||||
|
||||
_partitions[drive] = prodos;
|
||||
|
||||
if (drive) {
|
||||
rom[SETTINGS.Max32MBPartitionsDev1] = 0x1;
|
||||
} else {
|
||||
rom[SETTINGS.Max32MBPartitionsDev0] = 0x1;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -9,45 +9,74 @@
|
|||
* implied warranty.
|
||||
*/
|
||||
|
||||
import BlockVolume from './block';
|
||||
import DOS from './do';
|
||||
import Nibble from './nib';
|
||||
import ProDOS from './po';
|
||||
|
||||
import { numToString, debug } from '../util';
|
||||
|
||||
export default function _2MG(options) {
|
||||
var { rawData } = options;
|
||||
var OFFSETS = {
|
||||
CREATOR: 0x04,
|
||||
FLAGS: 0x0A,
|
||||
FORMAT: 0x0C,
|
||||
BLOCKS: 0x14,
|
||||
DATA_OFFSET: 0x18,
|
||||
BYTES: 0x1C,
|
||||
};
|
||||
|
||||
var FLAGS = {
|
||||
READ_ONLY: 0x80000000,
|
||||
VOLUME_VALID: 0x00000100,
|
||||
VOLUME_MASK: 0x000000FF
|
||||
};
|
||||
|
||||
var { rawData, arrayConstructor } = options;
|
||||
var disk;
|
||||
var volume = 254;
|
||||
|
||||
// Standard header size is 64 bytes. Make assumptions.
|
||||
var prefix = new Uint8Array(rawData.slice(0, 64));
|
||||
rawData = rawData.slice(64);
|
||||
var flags =
|
||||
prefix[0x10] |
|
||||
(prefix[0x11] << 8) |
|
||||
(prefix[0x12] << 16) |
|
||||
(prefix[0x13] << 24);
|
||||
var readOnly = (flags & 0x80000000) !== 0;
|
||||
if ((flags & 0x10) !== 0) {
|
||||
volume = flags & 0xff;
|
||||
var prefix = new DataView(rawData);
|
||||
var signature = numToString(prefix.getInt32(0x0, true));
|
||||
if (signature !== '2IMG') {
|
||||
throw new Error('Unrecognized 2mg signature: ' + signature);
|
||||
}
|
||||
var creator = numToString(prefix.getInt32(OFFSETS.CREATOR, true));
|
||||
var format = prefix.getInt32(OFFSETS.FORMAT, true);
|
||||
var bytes = prefix.getInt32(OFFSETS.BYTES, true);
|
||||
var offset = prefix.getInt32(OFFSETS.DATA_OFFSET, true);
|
||||
var flags = prefix.getInt32(OFFSETS.FLAGS, true);
|
||||
var readOnly = (flags & FLAGS.READ_ONLY) !== 0;
|
||||
if (flags & FLAGS.VOLUME_VALID) {
|
||||
volume = flags & FLAGS.VOLUME_MASK;
|
||||
}
|
||||
|
||||
options = { rawData, readOnly, volume };
|
||||
debug('created by', creator);
|
||||
rawData = rawData.slice(offset, offset + bytes);
|
||||
|
||||
// Check image format.
|
||||
// Sure, it's really 64 bits. But only 2 are actually used.
|
||||
switch (prefix[0xc]) {
|
||||
case 1: // PO
|
||||
disk = new ProDOS(options);
|
||||
break;
|
||||
case 2: // NIB
|
||||
disk = new Nibble(options);
|
||||
break;
|
||||
case 0: // dsk
|
||||
default: // Something hinky, assume 'dsk'
|
||||
disk = new DOS(options);
|
||||
break;
|
||||
var blockVolume = options.blockVolume || rawData.byteLength >= (800 * 1024);
|
||||
|
||||
options = { rawData, readOnly, volume, arrayConstructor };
|
||||
|
||||
if (blockVolume) {
|
||||
disk = new BlockVolume(options);
|
||||
} else {
|
||||
// Check image format.
|
||||
// Sure, it's really 64 bits. But only 2 are actually used.
|
||||
switch (format) {
|
||||
case 1: // PO
|
||||
disk = new ProDOS(options);
|
||||
break;
|
||||
case 2: // NIB
|
||||
disk = new Nibble(options);
|
||||
break;
|
||||
case 0: // dsk
|
||||
default: // Something hinky, assume 'dsk'
|
||||
disk = new DOS(options);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return disk;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and its
|
||||
* documentation for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation. No representations are made about the suitability of this
|
||||
* software for any purpose. It is provided "as is" without express or
|
||||
* implied warranty.
|
||||
*/
|
||||
|
||||
export default function BlockVolume(options) {
|
||||
var { rawData, readOnly, name } = options;
|
||||
var disk;
|
||||
|
||||
var blocks = [];
|
||||
blocks = [];
|
||||
var offset = 0;
|
||||
while (offset < rawData.byteLength) {
|
||||
blocks.push(new Uint8Array(rawData.slice(offset, offset + 0x200)));
|
||||
offset += 0x200;
|
||||
}
|
||||
|
||||
disk = {
|
||||
blocks,
|
||||
name,
|
||||
readOnly,
|
||||
};
|
||||
|
||||
return disk;
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and its
|
||||
* documentation for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation. No representations are made about the suitability of this
|
||||
* software for any purpose. It is provided "as is" without express or
|
||||
* implied warranty.
|
||||
*/
|
||||
|
||||
export function BitMap(volume) {
|
||||
var vdh = volume.vdh();
|
||||
var blocks = volume.blocks();
|
||||
|
||||
var BLOCK_ENTRIES = 4096;
|
||||
|
||||
function _init() {
|
||||
}
|
||||
|
||||
_init();
|
||||
|
||||
return {
|
||||
allocBlock: function () {
|
||||
for (var idx = 0; idx < vdh.totalBlocks; idx++) {
|
||||
var blockOffset = vdh.bitMapPointer + Math.floor(idx / BLOCK_ENTRIES);
|
||||
var bitMapBlock = blocks[blockOffset];
|
||||
var byteOffset = (idx - blockOffset * BLOCK_ENTRIES) >> 8;
|
||||
var bits = bitMapBlock[byteOffset];
|
||||
if (bits !== 0xff) {
|
||||
var mask = 0x01;
|
||||
for (var bitOffset = 0; bitOffset < 8; bitOffset++) {
|
||||
if (!(bits & mask)) {
|
||||
bitMapBlock[byteOffset] |= mask;
|
||||
return idx;
|
||||
}
|
||||
mask <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new Error('Disk full');
|
||||
},
|
||||
|
||||
freeBlock: function (block) {
|
||||
if (block >= vdh.totalBlocks) {
|
||||
throw new Error('Block out of range');
|
||||
}
|
||||
var blockOffset = vdh.bitMapPointer + Math.floor(block / BLOCK_ENTRIES);
|
||||
var byteOffset = (block - blockOffset * BLOCK_ENTRIES) >> 8;
|
||||
var bitOffset = block & 0x7;
|
||||
|
||||
var bitMapBlock = blocks[blockOffset];
|
||||
|
||||
bitMapBlock[byteOffset] &= 0xff ^ (0x01 << bitOffset);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and its
|
||||
* documentation for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation. No representations are made about the suitability of this
|
||||
* software for any purpose. It is provided "as is" without express or
|
||||
* implied warranty.
|
||||
*/
|
||||
|
||||
export var BLOCK_SIZE = 512;
|
||||
|
||||
export var STORAGE_TYPES = {
|
||||
DELETED: 0x0,
|
||||
SEEDLING: 0x1,
|
||||
SAPLING: 0x2,
|
||||
TREE: 0x3,
|
||||
DIRECTORY: 0xD,
|
||||
SUBDIRECTORY_HEADER: 0xE,
|
||||
VDH_HEADER: 0xF
|
||||
};
|
||||
|
||||
export var ACCESS_TYPES = {
|
||||
DELETE: 0x80,
|
||||
RENAME: 0x40,
|
||||
BACKUP: 0x20,
|
||||
WRITE: 0x02,
|
||||
READ: 0x01,
|
||||
ALL: 0xE3
|
||||
};
|
||||
|
||||
export var FILE_TYPES = {
|
||||
0x00: 'UNK', // Typeless file (SOS and ProDOS)
|
||||
0x01: 'BAD', // Bad block file
|
||||
0x02: 'PDC', // Pascal code file
|
||||
0x03: 'PTX', // Pascal text file
|
||||
0x04: 'TXT', // ASCII text file (SOS and ProDOS)
|
||||
0x05: 'PDA', // Pascal data file
|
||||
0x06: 'BIN', // General binary file (SOS and ProDOS)
|
||||
0x07: 'FNT', // Font file
|
||||
0x08: 'FOT', // Graphics screen file
|
||||
0x09: 'BA3', // Business BASIC program file
|
||||
0x0A: 'DA3', // Business BASIC data file
|
||||
0x0B: 'WPF', // Word Processor file
|
||||
0x0C: 'SOS', // SOS system file
|
||||
0x0F: 'DIR', // Directory file (SOS and ProDOS)
|
||||
0x10: 'RPD', // RPS data file
|
||||
0x11: 'RPI', // RPS index file
|
||||
0x12: 'AFD', // AppleFile discard file
|
||||
0x13: 'AFM', // AppleFile model file
|
||||
0x14: 'ARF', // AppleFile report format file
|
||||
0x15: 'SCL', // Screen Library file
|
||||
0x19: 'ADB', // AppleWorks Data Base file
|
||||
0x1A: 'AWP', // AppleWorks Word Processor file
|
||||
0x1B: 'ASP', // AppleWorks Spreadsheet file
|
||||
0xEF: 'PAR', // Pascal area
|
||||
0xF0: 'CMD', // ProDOS CI added command file
|
||||
0xFA: 'INT', // Integer BASIC program file
|
||||
0xFB: 'IVR', // Integer BASIC variable file
|
||||
0xFC: 'BAS', // Applesoft program file
|
||||
0xFD: 'VAR', // Applesoft variables file
|
||||
0xFE: 'REL', // Relocatable code file (EDASM)
|
||||
0xFF: 'SYS' // ProDOS system file
|
||||
};
|
|
@ -0,0 +1,98 @@
|
|||
|
||||
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and its
|
||||
* documentation for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation. No representations are made about the suitability of this
|
||||
* software for any purpose. It is provided "as is" without express or
|
||||
* implied warranty.
|
||||
*/
|
||||
|
||||
import { dateToUint32, readFileName, writeFileName, uint32ToDate } from './utils';
|
||||
import { readEntries, writeEntries } from './file_entry';
|
||||
import { STORAGE_TYPES, ACCESS_TYPES } from './constants';
|
||||
|
||||
export function Directory(volume, fileEntry) {
|
||||
var _fileEntry = fileEntry;
|
||||
|
||||
var DIRECTORY_OFFSETS = {
|
||||
PREV: 0x00,
|
||||
NEXT: 0x02,
|
||||
STORAGE_TYPE: 0x04,
|
||||
NAME_LENGTH: 0x04,
|
||||
VOLUME_NAME: 0x05,
|
||||
RESERVED_1: 0x14,
|
||||
CREATION: 0x1C,
|
||||
VERSION: 0x20,
|
||||
MIN_VERSION: 0x21,
|
||||
ACCESS: 0x22,
|
||||
ENTRY_LENGTH: 0x23,
|
||||
ENTRIES_PER_BLOCK: 0x24,
|
||||
FILE_COUNT: 0x25,
|
||||
PARENT: 0x27,
|
||||
PARENT_ENTRY_NUMBER: 0x29,
|
||||
PARENT_ENTRY_LENGTH: 0x2A
|
||||
};
|
||||
|
||||
var blocks = volume.blocks();
|
||||
|
||||
return {
|
||||
prev: 0,
|
||||
next: 0,
|
||||
storageType: STORAGE_TYPES.DIRECTORY,
|
||||
name: 'Untitled',
|
||||
creation: new Date(),
|
||||
access: ACCESS_TYPES.ALL,
|
||||
entryLength: 0x27,
|
||||
entriesPerBlock: 23,
|
||||
fileCount: 0,
|
||||
parent: 0,
|
||||
parentEntryLength: 0,
|
||||
parentEntryNumber: 0,
|
||||
entries: [],
|
||||
|
||||
read: function(fileEntry) {
|
||||
fileEntry = fileEntry || _fileEntry;
|
||||
|
||||
var block = new DataView(blocks[fileEntry.keyPointer].buffer);
|
||||
|
||||
this.prev = block.getUint16(DIRECTORY_OFFSETS.PREV, true);
|
||||
this.next = block.getUint16(DIRECTORY_OFFSETS.NEXT, true);
|
||||
this.storageType = block.getUint8(DIRECTORY_OFFSETS.STORAGE_TYPE) >> 4;
|
||||
var nameLength = block.getUint8(DIRECTORY_OFFSETS.NAME_LENGTH) & 0xF;
|
||||
var caseBits = block.getUint8(DIRECTORY_OFFSETS.CASE_BITS);
|
||||
this.name = readFileName(block, DIRECTORY_OFFSETS.VOLUME_NAME, nameLength, caseBits);
|
||||
this.creation = uint32ToDate(block.getUint32(DIRECTORY_OFFSETS.CREATION, true));
|
||||
this.access = block.getUint8(DIRECTORY_OFFSETS.ACCESS);
|
||||
this.entryLength = block.getUint8(DIRECTORY_OFFSETS.ENTRY_LENGTH);
|
||||
this.entriesPerBlock = block.getUint8(DIRECTORY_OFFSETS.ENTRIES_PER_BLOCK);
|
||||
this.fileCount = block.getUint16(DIRECTORY_OFFSETS.FILE_COUNT, true);
|
||||
this.parent = block.getUint16(DIRECTORY_OFFSETS.PARENT, true);
|
||||
this.parentEntryNumber = block.getUint8(DIRECTORY_OFFSETS.PARENT_ENTRY_NUMBER);
|
||||
this.parentEntryLength = block.getUint8(DIRECTORY_OFFSETS.PARENT_ENTRY_LENGTH);
|
||||
|
||||
this.entries = readEntries(volume, block, this);
|
||||
},
|
||||
|
||||
write: function() {
|
||||
var block = new DataView(blocks[fileEntry.keyPointer].buffer);
|
||||
|
||||
var nameLength = name.length & 0x0f;
|
||||
block.setUint8(DIRECTORY_OFFSETS.STORAGE_TYPE, this.storageType << 4 & nameLength);
|
||||
var caseBits = writeFileName(block, DIRECTORY_OFFSETS.FILE_NAME, this.name);
|
||||
block.setUint32(DIRECTORY_OFFSETS.CREATION, dateToUint32(this.creation), true);
|
||||
block.setUint16(DIRECTORY_OFFSETS.CASE_BITS, caseBits);
|
||||
block.setUint8(DIRECTORY_OFFSETS.ACCESS, this.access);
|
||||
block.setUint8(DIRECTORY_OFFSETS.ENTRY_LENGTH, this.entryLength);
|
||||
block.setUint8(DIRECTORY_OFFSETS.ENTRIES_PER_BLOCK, this.entriesPerBlock);
|
||||
block.setUint16(DIRECTORY_OFFSETS.FILE_COUNT, this.fileCount, true);
|
||||
block.setUint16(DIRECTORY_OFFSETS.PARENT, this.parent, true);
|
||||
block.setUint8(DIRECTORY_OFFSETS.PARENT_ENTRY_NUMBER, this.parentEntryNumber);
|
||||
block.setUint8(DIRECTORY_OFFSETS.PARENT_ENTRY_LENGTH, this.parentEntryLength);
|
||||
|
||||
writeEntries(volume, block, this);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and its
|
||||
* documentation for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation. No representations are made about the suitability of this
|
||||
* software for any purpose. It is provided "as is" without express or
|
||||
* implied warranty.
|
||||
*/
|
||||
|
||||
import { dateToUint32, readFileName, writeFileName, uint32ToDate } from './utils';
|
||||
import { STORAGE_TYPES, ACCESS_TYPES } from './constants';
|
||||
|
||||
export function FileEntry () {
|
||||
var ENTRY_OFFSETS = {
|
||||
STORAGE_TYPE: 0x00,
|
||||
NAME_LENGTH: 0x00,
|
||||
FILE_NAME: 0x01,
|
||||
FILE_TYPE: 0x10,
|
||||
KEY_POINTER: 0x11,
|
||||
BLOCKS_USED: 0x13,
|
||||
EOF: 0x15,
|
||||
CREATION: 0x18,
|
||||
CASE_BITS: 0x1C,
|
||||
VERSION: 0x1C,
|
||||
MIN_VERSION: 0x1D,
|
||||
ACCESS: 0x1E,
|
||||
AUX_TYPE: 0x1F,
|
||||
LAST_MOD: 0x21,
|
||||
HEADER_POINTER: 0x25
|
||||
};
|
||||
|
||||
var _block;
|
||||
var _offset;
|
||||
|
||||
return {
|
||||
storageType: STORAGE_TYPES.SEEDLING,
|
||||
name: 'Untitled',
|
||||
fileType: 0,
|
||||
auxType: 0,
|
||||
blocksUsed: 0,
|
||||
eof: 0,
|
||||
access: ACCESS_TYPES.ALL,
|
||||
creation: new Date(),
|
||||
lastMod: new Date(),
|
||||
keyPointer: 0,
|
||||
headerPointer: 0,
|
||||
|
||||
read: function(block, offset) {
|
||||
_block = block;
|
||||
_offset = offset;
|
||||
|
||||
this.storageType = block.getUint8(offset + ENTRY_OFFSETS.STORAGE_TYPE) >> 4;
|
||||
var nameLength = block.getUint8(offset + ENTRY_OFFSETS.NAME_LENGTH) & 0xF;
|
||||
var caseBits = block.getUint16(offset + ENTRY_OFFSETS.CASE_BITS, true);
|
||||
this.name = readFileName(block, offset + ENTRY_OFFSETS.FILE_NAME, nameLength, caseBits);
|
||||
this.fileType = block.getUint8(offset + ENTRY_OFFSETS.FILE_TYPE);
|
||||
this.keyPointer = block.getUint16(offset + ENTRY_OFFSETS.KEY_POINTER, true);
|
||||
this.blocksUsed = block.getUint16(offset + ENTRY_OFFSETS.BLOCKS_USED, true);
|
||||
this.eof =
|
||||
block.getUint8(offset + ENTRY_OFFSETS.EOF) |
|
||||
block.getUint8(offset + ENTRY_OFFSETS.EOF + 1) << 8 |
|
||||
block.getUint8(offset + ENTRY_OFFSETS.EOF + 2) << 16;
|
||||
this.creation = uint32ToDate(block.getUint32(offset + ENTRY_OFFSETS.CREATION, true));
|
||||
this.access = block.getUint8(offset + ENTRY_OFFSETS.ACCESS);
|
||||
this.auxType = block.getUint16(offset + ENTRY_OFFSETS.AUX_TYPE, true);
|
||||
this.lastMod = uint32ToDate(block.getUint32(offset + ENTRY_OFFSETS.LAST_MOD, true));
|
||||
this.headerPointer = block.getUint16(offset + ENTRY_OFFSETS.HEADER_POINTER, true);
|
||||
},
|
||||
|
||||
write: function(block, offset) {
|
||||
block = block || _block;
|
||||
offset = offset || _offset;
|
||||
|
||||
var nameLength = name.length & 0x0f;
|
||||
block.setUint8(offset + ENTRY_OFFSETS.STORAGE_TYPE, this.storageType << 4 & nameLength);
|
||||
var caseBits = writeFileName(block, offset + ENTRY_OFFSETS.FILE_NAME, this.name);
|
||||
block.setUint16(offset + ENTRY_OFFSETS.CASE_BITS, caseBits);
|
||||
block.setUint8(offset + ENTRY_OFFSETS.FILE_TYPE, this.fileType);
|
||||
block.setUint16(offset + ENTRY_OFFSETS.KEY_POINTER, this.keyPointer, true);
|
||||
block.setUint16(offset + ENTRY_OFFSETS.BLOCKS_USED, this.blocksUsed, true);
|
||||
block.setUint8(offset + ENTRY_OFFSETS.EOF, this.eof & 0xff);
|
||||
block.setUint8(offset + ENTRY_OFFSETS.EOF + 1, (this.eof && 0xff00) >> 8);
|
||||
block.setUint8(offset + ENTRY_OFFSETS.EOF + 2, this.eof >> 16);
|
||||
block.setUint32(offset + ENTRY_OFFSETS.CREATION, dateToUint32(this.creation), true);
|
||||
block.setUint8(offset + ENTRY_OFFSETS.ACCESS, this.access);
|
||||
block.setUint16(offset + ENTRY_OFFSETS.AUX_TYPE, this.auxType, true);
|
||||
block.setUint32(offset + ENTRY_OFFSETS.LAST_MOD, dateToUint32(this.lastMod), true);
|
||||
block.setUint16(offset + ENTRY_OFFSETS.HEADER_POINTER, this.headerPointer, true);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function readEntries(volume, block, header) {
|
||||
var blocks = volume.blocks();
|
||||
var entries = [];
|
||||
var offset = header.entryLength + 0x4;
|
||||
var count = 2;
|
||||
var next = header.next;
|
||||
|
||||
for (var idx = 0; idx < header.fileCount; idx++) {
|
||||
var fileEntry = new FileEntry();
|
||||
fileEntry.read(block, offset);
|
||||
entries.push(fileEntry);
|
||||
offset += header.entryLength;
|
||||
count++;
|
||||
if (count >= header.entriesPerBlock) {
|
||||
block = new DataView(blocks[next].buffer);
|
||||
next = block.getUint16(0x02, true);
|
||||
offset = 0x4;
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
export function writeEntries(volume, block, header) {
|
||||
var blocks = volume.blocks();
|
||||
var bitMap = volume.bitmap();
|
||||
var offset = header.entryLength + 0x4;
|
||||
var count = 2;
|
||||
var next = header.next;
|
||||
|
||||
for (var idx = 0; idx < header.fileCount; idx++) {
|
||||
var fileEntry = new header.entries[idx];
|
||||
fileEntry.write(block, offset);
|
||||
offset += header.entryLength;
|
||||
count++;
|
||||
if (count >= header.entriesPerBlock) {
|
||||
var prev = next;
|
||||
if (!next) {
|
||||
next = bitMap.allocBlock();
|
||||
}
|
||||
block = new DataView(blocks[next].buffer);
|
||||
block.setUint16(0x00, prev, true);
|
||||
next = block.getUint16(0x02, true);
|
||||
offset = 0x4;
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
next = block.getUint16(0x02, true);
|
||||
block.setUint16(0x02, 0, true);
|
||||
while (next) {
|
||||
block = new DataView(blocks[next].buffer);
|
||||
bitMap.freeLock(next);
|
||||
next = block.getUint16(0x02, true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and its
|
||||
* documentation for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation. No representations are made about the suitability of this
|
||||
* software for any purpose. It is provided "as is" without express or
|
||||
* implied warranty.
|
||||
*/
|
||||
|
||||
import { VDH } from './vdh';
|
||||
import { BitMap } from './bit_map';
|
||||
|
||||
export function ProDOSVolume(disk) {
|
||||
var _disk = disk;
|
||||
var _vdh;
|
||||
var _bitMap;
|
||||
|
||||
return {
|
||||
blocks() {
|
||||
return _disk.blocks;
|
||||
},
|
||||
|
||||
vdh() {
|
||||
if (!_vdh) {
|
||||
_vdh = new VDH(this);
|
||||
_vdh.read();
|
||||
}
|
||||
return _vdh;
|
||||
},
|
||||
|
||||
bitMap() {
|
||||
if (!_bitMap) {
|
||||
_bitMap = new BitMap(this);
|
||||
}
|
||||
return _bitMap;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and its
|
||||
* documentation for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation. No representations are made about the suitability of this
|
||||
* software for any purpose. It is provided "as is" without express or
|
||||
* implied warranty.
|
||||
*/
|
||||
|
||||
import { BLOCK_SIZE, STORAGE_TYPES } from './constants';
|
||||
|
||||
export function SaplingFile (volume, fileEntry) {
|
||||
var blocks = volume.blocks();
|
||||
var bitMap = volume.bitMap();
|
||||
|
||||
return {
|
||||
getBlockPointers() {
|
||||
var saplingBlock = blocks[fileEntry.keyPointer];
|
||||
var seedlingPointers = new DataView(saplingBlock);
|
||||
|
||||
var pointers = [fileEntry.keyPointer];
|
||||
for (var idx = 0; idx < 256; idx++) {
|
||||
var seedlingPointer = seedlingPointers.getUint16(idx * 2);
|
||||
if (seedlingPointer) {
|
||||
pointers.push(seedlingPointer);
|
||||
}
|
||||
}
|
||||
return pointers;
|
||||
},
|
||||
|
||||
read: function() {
|
||||
var saplingBlock = blocks[fileEntry.keyPointer];
|
||||
var seedlingPointers = new DataView(saplingBlock);
|
||||
|
||||
var remainingLength = fileEntry.oef;
|
||||
var data = new Uint8Array(remainingLength);
|
||||
var offset = 0;
|
||||
var idx = 0;
|
||||
while (remainingLength > 0) {
|
||||
var seedlingPointer = seedlingPointers.getUint16(idx * 2);
|
||||
if (seedlingPointer) {
|
||||
var seedlingBlock = blocks[seedlingPointer];
|
||||
var bytes = seedlingBlock.slice(0, Math.min(BLOCK_SIZE, remainingLength));
|
||||
|
||||
data.set(bytes, offset);
|
||||
}
|
||||
idx++;
|
||||
offset += BLOCK_SIZE;
|
||||
remainingLength -= BLOCK_SIZE;
|
||||
}
|
||||
return data;
|
||||
},
|
||||
|
||||
write: function(data) {
|
||||
fileEntry.storageType = STORAGE_TYPES.SAPLING;
|
||||
fileEntry.keyPointer = bitMap.allocBlock();
|
||||
fileEntry.eof = data.byteLength;
|
||||
var saplingBlock = blocks[fileEntry.keyPointer];
|
||||
var seedlingPointers = new DataView(saplingBlock);
|
||||
|
||||
var remainingLength = data.byteLength;
|
||||
var offset = 0;
|
||||
var idx = 0;
|
||||
|
||||
while (remainingLength > 0) {
|
||||
var seedlingPointer = bitMap.allocBlock();
|
||||
seedlingPointers.setUint16(idx * 2, seedlingPointer, true);
|
||||
var seedlingBlock = blocks[seedlingPointer];
|
||||
seedlingBlock.set(data.slice(offset, Math.min(BLOCK_SIZE, remainingLength)));
|
||||
idx++;
|
||||
offset += BLOCK_SIZE;
|
||||
remainingLength -= BLOCK_SIZE;
|
||||
}
|
||||
fileEntry.write();
|
||||
},
|
||||
|
||||
delete: function() {
|
||||
var pointers = this.getBlockPointers();
|
||||
for (var idx; idx < pointers.length; idx++) {
|
||||
bitMap.freeBlock(pointers[idx]);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and its
|
||||
* documentation for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation. No representations are made about the suitability of this
|
||||
* software for any purpose. It is provided "as is" without express or
|
||||
* implied warranty.
|
||||
*/
|
||||
import { STORAGE_TYPES } from './constants';
|
||||
|
||||
export function SeedlingFile (volume, fileEntry) {
|
||||
var blocks = volume.blocks();
|
||||
var bitMap = volume.bitMap();
|
||||
|
||||
return {
|
||||
getBlockPointers() {
|
||||
var pointers = [fileEntry.keyPointer];
|
||||
return pointers;
|
||||
},
|
||||
|
||||
read: function () {
|
||||
var seedlingBlock = blocks[fileEntry.keyPointer];
|
||||
var data = new Uint8Array(fileEntry.eof);
|
||||
data.set(seedlingBlock.slice(0, fileEntry.eof));
|
||||
return data;
|
||||
},
|
||||
|
||||
write: function(data) {
|
||||
if (fileEntry.keyPointer) {
|
||||
this.delete();
|
||||
}
|
||||
fileEntry.storageType = STORAGE_TYPES.SEEDLING;
|
||||
fileEntry.keyPointer = bitMap.allocBlock();
|
||||
fileEntry.eof = data.byteLength;
|
||||
var seedlingBlock = blocks[fileEntry.keyPointer];
|
||||
seedlingBlock.set(data);
|
||||
fileEntry.write();
|
||||
},
|
||||
|
||||
delete: function() {
|
||||
var pointers = this.getBlockPointers();
|
||||
for (var idx; idx < pointers.length; idx++) {
|
||||
bitMap.freeBlock(pointers[idx]);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and its
|
||||
* documentation for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation. No representations are made about the suitability of this
|
||||
* software for any purpose. It is provided "as is" without express or
|
||||
* implied warranty.
|
||||
*/
|
||||
|
||||
import { BLOCK_SIZE, STORAGE_TYPES } from './constants';
|
||||
|
||||
export function TreeFile (volume, fileEntry) {
|
||||
var blocks = volume.blocks();
|
||||
var bitMap = volume.bitMap();
|
||||
|
||||
return {
|
||||
getBlockPointers() {
|
||||
var treeBlock = blocks[fileEntry.keyPointer];
|
||||
var saplingPointers = new DataView(treeBlock);
|
||||
var pointers = [];
|
||||
for (var idx = 0; idx < 256; idx++) {
|
||||
var saplingPointer = saplingPointers.getUint16(idx * 2);
|
||||
if (saplingPointer) {
|
||||
pointers.push(saplingPointer);
|
||||
var seedlingPointers = new DataView(blocks[saplingPointer]);
|
||||
for (var jdx = 0; jdx < 256; jdx++) {
|
||||
var seedlingPointer = seedlingPointers.getUint16(idx * 2);
|
||||
if (seedlingPointer) {
|
||||
pointers.push(seedlingPointer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return pointers;
|
||||
},
|
||||
|
||||
read: function() {
|
||||
var treeBlock = blocks[fileEntry.keyPointer];
|
||||
var saplingPointers = new DataView(treeBlock);
|
||||
var remainingLength = fileEntry.eof;
|
||||
var data = new Uint8Array(remainingLength);
|
||||
var offset = 0;
|
||||
var idx = 0;
|
||||
|
||||
while (remainingLength > 0) {
|
||||
var saplingPointer = saplingPointers.getUint16(idx * 2, true);
|
||||
var jdx = 0;
|
||||
if (saplingPointer) {
|
||||
var saplingBlock = blocks[saplingPointer];
|
||||
var seedlingPointers = new DataView(saplingBlock);
|
||||
|
||||
while (jdx < 256 && remainingLength > 0) {
|
||||
var seedlingPointer = seedlingPointers.getUint16(idx * 2, true);
|
||||
if (seedlingPointer) {
|
||||
var seedlingBlock = blocks[seedlingPointer];
|
||||
var bytes = seedlingBlock.slice(Math.min(BLOCK_SIZE, remainingLength));
|
||||
|
||||
data.set(bytes, offset);
|
||||
}
|
||||
jdx++;
|
||||
offset += BLOCK_SIZE;
|
||||
remainingLength -= BLOCK_SIZE;
|
||||
}
|
||||
} else {
|
||||
offset += BLOCK_SIZE * 256;
|
||||
remainingLength -= BLOCK_SIZE * 256;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
return data;
|
||||
},
|
||||
|
||||
write: function(data) {
|
||||
fileEntry.storageType = STORAGE_TYPES.TREE;
|
||||
fileEntry.keyPointer = bitMap.allocBlock();
|
||||
fileEntry.eof = data.byteLength;
|
||||
|
||||
var treeBlock = blocks[fileEntry.keyPointer];
|
||||
var saplingPointers = new DataView(treeBlock);
|
||||
|
||||
var remainingLength = fileEntry.eof;
|
||||
var offset = 0;
|
||||
var idx = 0;
|
||||
|
||||
while (remainingLength > 0) {
|
||||
var saplingPointer = bitMap.allocBlock();
|
||||
var saplingBlock = blocks[saplingPointer];
|
||||
saplingPointers.setUint16(idx * 2, saplingPointer, true);
|
||||
var seedlingPointers = new DataView(saplingBlock);
|
||||
|
||||
var jdx = 0;
|
||||
|
||||
while (jdx < 256 && remainingLength > 0) {
|
||||
var seedlingPointer = bitMap.allocBlock();
|
||||
seedlingPointers.setUint16(idx * 2, seedlingPointer, true);
|
||||
var seedlingBlock = blocks[seedlingPointer];
|
||||
seedlingBlock.set(data.slice(offset, Math.min(BLOCK_SIZE, remainingLength)));
|
||||
jdx++;
|
||||
offset += BLOCK_SIZE;
|
||||
remainingLength -= BLOCK_SIZE;
|
||||
}
|
||||
idx++;
|
||||
}
|
||||
fileEntry.write();
|
||||
},
|
||||
|
||||
delete: function() {
|
||||
var pointers = this.getBlockPointers();
|
||||
for (var idx; idx < pointers.length; idx++) {
|
||||
bitMap.freeBlock(pointers[idx]);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and its
|
||||
* documentation for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation. No representations are made about the suitability of this
|
||||
* software for any purpose. It is provided "as is" without express or
|
||||
* implied warranty.
|
||||
*/
|
||||
|
||||
import { debug } from '../../util';
|
||||
import { STORAGE_TYPES } from './constants';
|
||||
import { Directory } from './directory';
|
||||
|
||||
export function uint32ToDate(val) {
|
||||
// yyyyyyy m|mmmm ddddd|000hhhhh|00mmmmmm
|
||||
|
||||
if (val) {
|
||||
var yearMonthDay = val & 0xffff;
|
||||
var hourMinute = val >> 16;
|
||||
|
||||
var year = yearMonthDay >> 9;
|
||||
var month = (yearMonthDay & 0x01E0) >> 5;
|
||||
var day = yearMonthDay & 0x001F;
|
||||
|
||||
var hour = hourMinute >> 8;
|
||||
var min = hourMinute & 0xff;
|
||||
|
||||
return new Date(1900 + year, month - 1, day, hour, min);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function dateToUint32(date) {
|
||||
// yyyyyyy m|mmmm ddddd|000hhhhh|00mmmmmm
|
||||
|
||||
var val = 0;
|
||||
|
||||
if (date) {
|
||||
var year = date.getYear() - 1900;
|
||||
var month = date.getMonth() + 1;
|
||||
var day = date.getDate();
|
||||
|
||||
var hour = date.getHour();
|
||||
var min = date.getMinute();
|
||||
|
||||
var yearMonthDay = year << 9 | month << 5 | day;
|
||||
var hourMinute = hour << 8 | min;
|
||||
val = hourMinute << 16 | yearMonthDay;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
export function readFileName(block, offset, nameLength, caseBits) {
|
||||
var name = '';
|
||||
if (!(caseBits & 0x8000)) {
|
||||
caseBits = 0;
|
||||
}
|
||||
for (var idx = 0; idx < nameLength; idx++) {
|
||||
caseBits <<= 1;
|
||||
var char = String.fromCharCode(block.getUint8(offset + idx));
|
||||
name += caseBits & 0x8000 ? char.toLowerCase() : char;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
export function writeFileName(block, offset, name) {
|
||||
var caseBits = 0;
|
||||
for (var idx = 0; idx < name.length; idx++) {
|
||||
caseBits <<= 1;
|
||||
var charCode = name.charCodeAt(idx);
|
||||
if (charCode > 0x60 && charCode < 0x7B) {
|
||||
caseBits |= 0x1;
|
||||
charCode -= 0x20;
|
||||
}
|
||||
block.setUint8(offset + idx, charCode);
|
||||
}
|
||||
return caseBits;
|
||||
}
|
||||
|
||||
export function dumpDirectory(volume, dirEntry, depth) {
|
||||
var dir = new Directory(volume, dirEntry);
|
||||
dir.read();
|
||||
|
||||
for (var idx = 0; idx < dir.entries.length; idx++) {
|
||||
var fileEntry = dir.entries[idx];
|
||||
if (fileEntry.storageType !== STORAGE_TYPES.DELETED) {
|
||||
debug(depth, fileEntry.name);
|
||||
if (fileEntry.storageType === STORAGE_TYPES.DIRECTORY) {
|
||||
dumpDirectory(volume, fileEntry, depth + ' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function dump(volume) {
|
||||
var vdh = volume.vdh();
|
||||
debug(vdh.name);
|
||||
for (var idx = 0; idx < vdh.entries.length; idx++) {
|
||||
var fileEntry = vdh.entries[idx];
|
||||
debug(fileEntry.name);
|
||||
if (fileEntry.storageType === STORAGE_TYPES.DIRECTORY) {
|
||||
dumpDirectory(volume, fileEntry, ' ');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
/* Copyright 2010-2019 Will Scullin <scullin@scullinsteel.com>
|
||||
*
|
||||
* Permission to use, copy, modify, distribute, and sell this software and its
|
||||
* documentation for any purpose is hereby granted without fee, provided that
|
||||
* the above copyright notice appear in all copies and that both that
|
||||
* copyright notice and this permission notice appear in supporting
|
||||
* documentation. No representations are made about the suitability of this
|
||||
* software for any purpose. It is provided "as is" without express or
|
||||
* implied warranty.
|
||||
*/
|
||||
|
||||
import { dateToUint32, readFileName, writeFileName, uint32ToDate } from './utils';
|
||||
import { readEntries, writeEntries } from './file_entry';
|
||||
import { STORAGE_TYPES, ACCESS_TYPES } from './constants';
|
||||
|
||||
export function VDH(volume) {
|
||||
var blocks = volume.blocks();
|
||||
|
||||
var VDH_BLOCK = 2;
|
||||
var VDH_OFFSETS = {
|
||||
PREV: 0x00,
|
||||
NEXT: 0x02,
|
||||
STORAGE_TYPE: 0x04,
|
||||
NAME_LENGTH: 0x04,
|
||||
VOLUME_NAME: 0x05,
|
||||
RESERVED_1: 0x14,
|
||||
CASE_BITS: 0x1A,
|
||||
CREATION: 0x1C,
|
||||
VERSION: 0x20,
|
||||
MIN_VERSION: 0x21,
|
||||
ACCESS: 0x22,
|
||||
ENTRY_LENGTH: 0x23,
|
||||
ENTRIES_PER_BLOCK: 0x24,
|
||||
FILE_COUNT: 0x25,
|
||||
BIT_MAP_POINTER: 0x27,
|
||||
TOTAL_BLOCKS: 0x29,
|
||||
};
|
||||
|
||||
return {
|
||||
prev: 0,
|
||||
next: 0,
|
||||
storageType: STORAGE_TYPES.VDH_HEADER,
|
||||
name: '',
|
||||
creation: new Date(),
|
||||
access: ACCESS_TYPES.ALL,
|
||||
entryLength: 0x27,
|
||||
entriesPerBlock: 23,
|
||||
fileCount: 0,
|
||||
bitMapPointer: 0,
|
||||
totalBlocks: 0,
|
||||
entries: [],
|
||||
|
||||
read: function () {
|
||||
var block = new DataView(blocks[VDH_BLOCK].buffer);
|
||||
|
||||
this.next = block.getUint16(VDH_OFFSETS.NEXT, true);
|
||||
this.storageType = block.getUint8(VDH_OFFSETS.STORAGE_TYPE) >> 4;
|
||||
var nameLength = block.getUint8(VDH_OFFSETS.NAME_LENGTH) & 0xF;
|
||||
var caseBits = block.getUint8(VDH_OFFSETS.CASE_BITS);
|
||||
this.name = readFileName(block, VDH_OFFSETS.VOLUME_NAME, nameLength, caseBits);
|
||||
this.creation = uint32ToDate(block.getUint32(VDH_OFFSETS.CREATION, true));
|
||||
this.access = block.getUint8(VDH_OFFSETS.ACCESS);
|
||||
this.entryLength = block.getUint8(VDH_OFFSETS.ENTRY_LENGTH);
|
||||
this.entriesPerBlock = block.getUint8(VDH_OFFSETS.ENTRIES_PER_BLOCK);
|
||||
this.fileCount = block.getUint16(VDH_OFFSETS.FILE_COUNT, true);
|
||||
this.bitMapPointer = block.getUint16(VDH_OFFSETS.BIT_MAP_POINTER, true);
|
||||
this.totalBlocks = block.getUint16(VDH_OFFSETS.TOTAL_BLOCKS, true);
|
||||
|
||||
this.entries = readEntries(volume, block, this);
|
||||
},
|
||||
|
||||
write: function() {
|
||||
var block = new DataView(blocks[VDH_BLOCK].buffer);
|
||||
|
||||
var nameLength = name.length & 0x0f;
|
||||
block.setUint8(VDH_OFFSETS.STORAGE_TYPE, this.storageType << 4 & nameLength);
|
||||
var caseBits = writeFileName(block, VDH_OFFSETS.FILE_NAME, this.name);
|
||||
block.setUint32(VDH_OFFSETS.CREATION, dateToUint32(this.creation), true);
|
||||
block.setUint16(VDH_OFFSETS.CASE_BITS, caseBits);
|
||||
block.setUint8(VDH_OFFSETS.ACCESS, this.access);
|
||||
block.setUint8(VDH_OFFSETS.ENTRY_LENGTH, this.entryLength);
|
||||
block.setUint8(VDH_OFFSETS.ENTRIES_PER_BLOCK, this.entriesPerBlock);
|
||||
block.setUint16(VDH_OFFSETS.FILE_COUNT, this.fileCount, true);
|
||||
block.setUint16(VDH_OFFSETS.BIT_MAP_POINTER, this.bitMapPointer, true);
|
||||
block.setUint16(VDH_OFFSETS.TOTAL_BLOCKS, this.totalBlocks, true);
|
||||
|
||||
writeEntries(blocks, block, this);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
|
@ -4,6 +4,7 @@ import { driveLights, initUI, updateUI } from './ui/apple2';
|
|||
import Printer from './ui/printer';
|
||||
|
||||
import DiskII from './cards/disk2';
|
||||
import CFFA from './cards/cffa';
|
||||
import LanguageCard from './cards/langcard';
|
||||
import Parallel from './cards/parallel';
|
||||
import RAMFactor from './cards/ramfactor';
|
||||
|
@ -97,8 +98,9 @@ var videoTerm = new VideoTerm(io, options.screen[0]);
|
|||
var slinky = new RAMFactor(io, 1024 * 1024);
|
||||
var disk2 = new DiskII(io, driveLights);
|
||||
var clock = new Thunderclock(io);
|
||||
var cffa = new CFFA(io);
|
||||
|
||||
initUI(apple2, disk2, false);
|
||||
initUI(apple2, disk2, cffa, false);
|
||||
|
||||
io.setSlot(0, lc);
|
||||
io.setSlot(1, parallel);
|
||||
|
|
|
@ -3,6 +3,7 @@ import Prefs from './prefs';
|
|||
import { driveLights, initUI, updateUI } from './ui/apple2';
|
||||
import Printer from './ui/printer';
|
||||
|
||||
import CFFA from './cards/cffa';
|
||||
import DiskII from './cards/disk2';
|
||||
import Parallel from './cards/parallel';
|
||||
import RAMFactor from './cards/ramfactor';
|
||||
|
@ -82,12 +83,14 @@ var parallel = new Parallel(io, printer);
|
|||
var slinky = new RAMFactor(io, 1024 * 1024);
|
||||
var disk2 = new DiskII(io, driveLights);
|
||||
var clock = new Thunderclock(io);
|
||||
var cffa = new CFFA(io);
|
||||
|
||||
initUI(apple2, disk2, options.e);
|
||||
initUI(apple2, disk2, cffa, options.e);
|
||||
|
||||
io.setSlot(1, parallel);
|
||||
io.setSlot(2, slinky);
|
||||
io.setSlot(4, clock);
|
||||
io.setSlot(5, clock);
|
||||
io.setSlot(6, disk2);
|
||||
io.setSlot(7, cffa);
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,519 @@
|
|||
|
||||
/*
|
||||
* http://dreher.net/?s=projects/CFforAppleII&c=projects/CFforAppleII/downloads1.php
|
||||
*/
|
||||
|
||||
export var rom = [
|
||||
0x43, 0x46, 0x46, 0x41, 0x20, 0x46, 0x69, 0x72,
|
||||
0x6d, 0x77, 0x61, 0x72, 0x65, 0x0d, 0x0d, 0x0d,
|
||||
0x53, 0x65, 0x65, 0x20, 0x3c, 0x68, 0x74, 0x74,
|
||||
0x70, 0x3a, 0x2f, 0x2f, 0x64, 0x72, 0x65, 0x68,
|
||||
0x65, 0x72, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x43,
|
||||
0x46, 0x66, 0x6f, 0x72, 0x41, 0x70, 0x70, 0x6c,
|
||||
0x65, 0x49, 0x49, 0x2f, 0x3e, 0x2e, 0x0d, 0x0d,
|
||||
0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d, 0x0d,
|
||||
0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x20,
|
||||
0x32, 0x2e, 0x30, 0x20, 0x66, 0x6f, 0x72, 0x20,
|
||||
0x36, 0x35, 0x30, 0x32, 0x20, 0x6f, 0x72, 0x20,
|
||||
0x6c, 0x61, 0x74, 0x65, 0x72, 0x2e, 0x0d, 0x0d,
|
||||
0x52, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x73,
|
||||
0x20, 0x43, 0x46, 0x46, 0x41, 0x20, 0x77, 0x69,
|
||||
0x74, 0x68, 0x20, 0x45, 0x45, 0x50, 0x52, 0x4f,
|
||||
0x4d, 0x2c, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x45,
|
||||
0x50, 0x52, 0x4f, 0x4d, 0x2e, 0x0d, 0x0d, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xa9, 0x20, 0xa2, 0x00, 0xa9, 0x03, 0xa9, 0x3c,
|
||||
0x10, 0x0a, 0x18, 0x90, 0x01, 0x38, 0x20, 0xa1,
|
||||
0xc1, 0x4c, 0x28, 0xc8, 0x20, 0xa1, 0xc1, 0x86,
|
||||
0x43, 0xac, 0x0b, 0xc8, 0xa9, 0xc5, 0x20, 0xa8,
|
||||
0xfc, 0x88, 0xd0, 0xf8, 0xad, 0x00, 0xc0, 0x2d,
|
||||
0x09, 0xc8, 0xcd, 0x0a, 0xc8, 0xd0, 0x07, 0xea,
|
||||
0x20, 0xa1, 0xc1, 0x20, 0x2b, 0xc8, 0xa0, 0x01,
|
||||
0x84, 0x42, 0x88, 0x84, 0x46, 0x84, 0x47, 0xa9,
|
||||
0x08, 0x85, 0x45, 0x84, 0x44, 0x20, 0xa1, 0xc1,
|
||||
0x20, 0x37, 0xc8, 0x85, 0xff, 0xb0, 0x10, 0xac,
|
||||
0x00, 0x08, 0x88, 0xd0, 0x0a, 0xad, 0x01, 0x08,
|
||||
0xf0, 0x05, 0xa6, 0x43, 0x4c, 0x01, 0x08, 0xa5,
|
||||
0x00, 0xd0, 0x0a, 0xa5, 0x01, 0xcd, 0xf8, 0x07,
|
||||
0xd0, 0x03, 0x4c, 0xba, 0xfa, 0x20, 0x2e, 0xc8,
|
||||
0xa0, 0x00, 0xa6, 0xff, 0xd0, 0x02, 0xa0, 0x0e,
|
||||
0x20, 0x95, 0xc1, 0x8a, 0xf0, 0x0c, 0xad, 0x79,
|
||||
0x07, 0x0a, 0x0a, 0xa9, 0xb0, 0x69, 0x00, 0x20,
|
||||
0xed, 0xfd, 0xa0, 0x1c, 0x20, 0x95, 0xc1, 0x20,
|
||||
0xa1, 0xc1, 0x4c, 0x34, 0xc8, 0xb9, 0xbc, 0xc1,
|
||||
0xf0, 0x06, 0x20, 0x31, 0xc8, 0xc8, 0xd0, 0xf5,
|
||||
0x60, 0x8d, 0x92, 0xc0, 0xa9, 0xc1, 0x8d, 0xf8,
|
||||
0x07, 0x2c, 0xff, 0xcf, 0xad, 0x20, 0xc8, 0x49,
|
||||
0xcf, 0xd0, 0x05, 0xa0, 0x01, 0xa2, 0x10, 0x60,
|
||||
0x20, 0x93, 0xfe, 0x00, 0x43, 0x68, 0x65, 0x63,
|
||||
0x6b, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,
|
||||
0x20, 0x00, 0x4e, 0x6f, 0x20, 0x62, 0x6f, 0x6f,
|
||||
0x74, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x00,
|
||||
0x2e, 0x0d, 0x0d, 0x43, 0x6f, 0x75, 0x6c, 0x64,
|
||||
0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x6f, 0x6f,
|
||||
0x74, 0x20, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x20, 0x00, 0xff, 0x43, 0x46,
|
||||
0x46, 0x41, 0xff, 0x00, 0x00, 0x00, 0x17, 0x0a,
|
||||
0xa9, 0x20, 0xa2, 0x00, 0xa9, 0x03, 0xa9, 0x3c,
|
||||
0x10, 0x0a, 0x18, 0x90, 0x01, 0x38, 0x20, 0xa1,
|
||||
0xc2, 0x4c, 0x28, 0xc8, 0x20, 0xa1, 0xc2, 0x86,
|
||||
0x43, 0xac, 0x0b, 0xc8, 0xa9, 0xc5, 0x20, 0xa8,
|
||||
0xfc, 0x88, 0xd0, 0xf8, 0xad, 0x00, 0xc0, 0x2d,
|
||||
0x09, 0xc8, 0xcd, 0x0a, 0xc8, 0xd0, 0x07, 0xea,
|
||||
0x20, 0xa1, 0xc2, 0x20, 0x2b, 0xc8, 0xa0, 0x01,
|
||||
0x84, 0x42, 0x88, 0x84, 0x46, 0x84, 0x47, 0xa9,
|
||||
0x08, 0x85, 0x45, 0x84, 0x44, 0x20, 0xa1, 0xc2,
|
||||
0x20, 0x37, 0xc8, 0x85, 0xff, 0xb0, 0x10, 0xac,
|
||||
0x00, 0x08, 0x88, 0xd0, 0x0a, 0xad, 0x01, 0x08,
|
||||
0xf0, 0x05, 0xa6, 0x43, 0x4c, 0x01, 0x08, 0xa5,
|
||||
0x00, 0xd0, 0x0a, 0xa5, 0x01, 0xcd, 0xf8, 0x07,
|
||||
0xd0, 0x03, 0x4c, 0xba, 0xfa, 0x20, 0x2e, 0xc8,
|
||||
0xa0, 0x00, 0xa6, 0xff, 0xd0, 0x02, 0xa0, 0x0e,
|
||||
0x20, 0x95, 0xc2, 0x8a, 0xf0, 0x0c, 0xad, 0x7a,
|
||||
0x07, 0x0a, 0x0a, 0xa9, 0xb0, 0x69, 0x00, 0x20,
|
||||
0xed, 0xfd, 0xa0, 0x1c, 0x20, 0x95, 0xc2, 0x20,
|
||||
0xa1, 0xc2, 0x4c, 0x34, 0xc8, 0xb9, 0xbc, 0xc2,
|
||||
0xf0, 0x06, 0x20, 0x31, 0xc8, 0xc8, 0xd0, 0xf5,
|
||||
0x60, 0x8d, 0xa2, 0xc0, 0xa9, 0xc2, 0x8d, 0xf8,
|
||||
0x07, 0x2c, 0xff, 0xcf, 0xad, 0x20, 0xc8, 0x49,
|
||||
0xcf, 0xd0, 0x05, 0xa0, 0x02, 0xa2, 0x20, 0x60,
|
||||
0x20, 0x93, 0xfe, 0x00, 0x43, 0x68, 0x65, 0x63,
|
||||
0x6b, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,
|
||||
0x20, 0x00, 0x4e, 0x6f, 0x20, 0x62, 0x6f, 0x6f,
|
||||
0x74, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x00,
|
||||
0x2e, 0x0d, 0x0d, 0x43, 0x6f, 0x75, 0x6c, 0x64,
|
||||
0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x6f, 0x6f,
|
||||
0x74, 0x20, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x20, 0x00, 0xff, 0x43, 0x46,
|
||||
0x46, 0x41, 0xff, 0x00, 0x00, 0x00, 0x17, 0x0a,
|
||||
0xa9, 0x20, 0xa2, 0x00, 0xa9, 0x03, 0xa9, 0x3c,
|
||||
0x10, 0x0a, 0x18, 0x90, 0x01, 0x38, 0x20, 0xa1,
|
||||
0xc3, 0x4c, 0x28, 0xc8, 0x20, 0xa1, 0xc3, 0x86,
|
||||
0x43, 0xac, 0x0b, 0xc8, 0xa9, 0xc5, 0x20, 0xa8,
|
||||
0xfc, 0x88, 0xd0, 0xf8, 0xad, 0x00, 0xc0, 0x2d,
|
||||
0x09, 0xc8, 0xcd, 0x0a, 0xc8, 0xd0, 0x07, 0xea,
|
||||
0x20, 0xa1, 0xc3, 0x20, 0x2b, 0xc8, 0xa0, 0x01,
|
||||
0x84, 0x42, 0x88, 0x84, 0x46, 0x84, 0x47, 0xa9,
|
||||
0x08, 0x85, 0x45, 0x84, 0x44, 0x20, 0xa1, 0xc3,
|
||||
0x20, 0x37, 0xc8, 0x85, 0xff, 0xb0, 0x10, 0xac,
|
||||
0x00, 0x08, 0x88, 0xd0, 0x0a, 0xad, 0x01, 0x08,
|
||||
0xf0, 0x05, 0xa6, 0x43, 0x4c, 0x01, 0x08, 0xa5,
|
||||
0x00, 0xd0, 0x0a, 0xa5, 0x01, 0xcd, 0xf8, 0x07,
|
||||
0xd0, 0x03, 0x4c, 0xba, 0xfa, 0x20, 0x2e, 0xc8,
|
||||
0xa0, 0x00, 0xa6, 0xff, 0xd0, 0x02, 0xa0, 0x0e,
|
||||
0x20, 0x95, 0xc3, 0x8a, 0xf0, 0x0c, 0xad, 0x7b,
|
||||
0x07, 0x0a, 0x0a, 0xa9, 0xb0, 0x69, 0x00, 0x20,
|
||||
0xed, 0xfd, 0xa0, 0x1c, 0x20, 0x95, 0xc3, 0x20,
|
||||
0xa1, 0xc3, 0x4c, 0x34, 0xc8, 0xb9, 0xbc, 0xc3,
|
||||
0xf0, 0x06, 0x20, 0x31, 0xc8, 0xc8, 0xd0, 0xf5,
|
||||
0x60, 0x8d, 0xb2, 0xc0, 0xa9, 0xc3, 0x8d, 0xf8,
|
||||
0x07, 0x2c, 0xff, 0xcf, 0xad, 0x20, 0xc8, 0x49,
|
||||
0xcf, 0xd0, 0x05, 0xa0, 0x03, 0xa2, 0x30, 0x60,
|
||||
0x20, 0x93, 0xfe, 0x00, 0x43, 0x68, 0x65, 0x63,
|
||||
0x6b, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,
|
||||
0x20, 0x00, 0x4e, 0x6f, 0x20, 0x62, 0x6f, 0x6f,
|
||||
0x74, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x00,
|
||||
0x2e, 0x0d, 0x0d, 0x43, 0x6f, 0x75, 0x6c, 0x64,
|
||||
0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x6f, 0x6f,
|
||||
0x74, 0x20, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x20, 0x00, 0xff, 0x43, 0x46,
|
||||
0x46, 0x41, 0xff, 0x00, 0x00, 0x00, 0x17, 0x0a,
|
||||
0xa9, 0x20, 0xa2, 0x00, 0xa9, 0x03, 0xa9, 0x3c,
|
||||
0x10, 0x0a, 0x18, 0x90, 0x01, 0x38, 0x20, 0xa1,
|
||||
0xc4, 0x4c, 0x28, 0xc8, 0x20, 0xa1, 0xc4, 0x86,
|
||||
0x43, 0xac, 0x0b, 0xc8, 0xa9, 0xc5, 0x20, 0xa8,
|
||||
0xfc, 0x88, 0xd0, 0xf8, 0xad, 0x00, 0xc0, 0x2d,
|
||||
0x09, 0xc8, 0xcd, 0x0a, 0xc8, 0xd0, 0x07, 0xea,
|
||||
0x20, 0xa1, 0xc4, 0x20, 0x2b, 0xc8, 0xa0, 0x01,
|
||||
0x84, 0x42, 0x88, 0x84, 0x46, 0x84, 0x47, 0xa9,
|
||||
0x08, 0x85, 0x45, 0x84, 0x44, 0x20, 0xa1, 0xc4,
|
||||
0x20, 0x37, 0xc8, 0x85, 0xff, 0xb0, 0x10, 0xac,
|
||||
0x00, 0x08, 0x88, 0xd0, 0x0a, 0xad, 0x01, 0x08,
|
||||
0xf0, 0x05, 0xa6, 0x43, 0x4c, 0x01, 0x08, 0xa5,
|
||||
0x00, 0xd0, 0x0a, 0xa5, 0x01, 0xcd, 0xf8, 0x07,
|
||||
0xd0, 0x03, 0x4c, 0xba, 0xfa, 0x20, 0x2e, 0xc8,
|
||||
0xa0, 0x00, 0xa6, 0xff, 0xd0, 0x02, 0xa0, 0x0e,
|
||||
0x20, 0x95, 0xc4, 0x8a, 0xf0, 0x0c, 0xad, 0x7c,
|
||||
0x07, 0x0a, 0x0a, 0xa9, 0xb0, 0x69, 0x00, 0x20,
|
||||
0xed, 0xfd, 0xa0, 0x1c, 0x20, 0x95, 0xc4, 0x20,
|
||||
0xa1, 0xc4, 0x4c, 0x34, 0xc8, 0xb9, 0xbc, 0xc4,
|
||||
0xf0, 0x06, 0x20, 0x31, 0xc8, 0xc8, 0xd0, 0xf5,
|
||||
0x60, 0x8d, 0xc2, 0xc0, 0xa9, 0xc4, 0x8d, 0xf8,
|
||||
0x07, 0x2c, 0xff, 0xcf, 0xad, 0x20, 0xc8, 0x49,
|
||||
0xcf, 0xd0, 0x05, 0xa0, 0x04, 0xa2, 0x40, 0x60,
|
||||
0x20, 0x93, 0xfe, 0x00, 0x43, 0x68, 0x65, 0x63,
|
||||
0x6b, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,
|
||||
0x20, 0x00, 0x4e, 0x6f, 0x20, 0x62, 0x6f, 0x6f,
|
||||
0x74, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x00,
|
||||
0x2e, 0x0d, 0x0d, 0x43, 0x6f, 0x75, 0x6c, 0x64,
|
||||
0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x6f, 0x6f,
|
||||
0x74, 0x20, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x20, 0x00, 0xff, 0x43, 0x46,
|
||||
0x46, 0x41, 0xff, 0x00, 0x00, 0x00, 0x17, 0x0a,
|
||||
0xa9, 0x20, 0xa2, 0x00, 0xa9, 0x03, 0xa9, 0x00,
|
||||
0x10, 0x0a, 0x18, 0x90, 0x01, 0x38, 0x20, 0xa1,
|
||||
0xc5, 0x4c, 0x28, 0xc8, 0x20, 0xa1, 0xc5, 0x86,
|
||||
0x43, 0xac, 0x0b, 0xc8, 0xa9, 0xc5, 0x20, 0xa8,
|
||||
0xfc, 0x88, 0xd0, 0xf8, 0xad, 0x00, 0xc0, 0x2d,
|
||||
0x09, 0xc8, 0xcd, 0x0a, 0xc8, 0xd0, 0x07, 0xea,
|
||||
0x20, 0xa1, 0xc5, 0x20, 0x2b, 0xc8, 0xa0, 0x01,
|
||||
0x84, 0x42, 0x88, 0x84, 0x46, 0x84, 0x47, 0xa9,
|
||||
0x08, 0x85, 0x45, 0x84, 0x44, 0x20, 0xa1, 0xc5,
|
||||
0x20, 0x37, 0xc8, 0x85, 0xff, 0xb0, 0x10, 0xac,
|
||||
0x00, 0x08, 0x88, 0xd0, 0x0a, 0xad, 0x01, 0x08,
|
||||
0xf0, 0x05, 0xa6, 0x43, 0x4c, 0x01, 0x08, 0xa5,
|
||||
0x00, 0xd0, 0x0a, 0xa5, 0x01, 0xcd, 0xf8, 0x07,
|
||||
0xd0, 0x03, 0x4c, 0xba, 0xfa, 0x20, 0x2e, 0xc8,
|
||||
0xa0, 0x00, 0xa6, 0xff, 0xd0, 0x02, 0xa0, 0x0e,
|
||||
0x20, 0x95, 0xc5, 0x8a, 0xf0, 0x0c, 0xad, 0x7d,
|
||||
0x07, 0x0a, 0x0a, 0xa9, 0xb0, 0x69, 0x00, 0x20,
|
||||
0xed, 0xfd, 0xa0, 0x1c, 0x20, 0x95, 0xc5, 0x20,
|
||||
0xa1, 0xc5, 0x4c, 0x34, 0xc8, 0xb9, 0xbc, 0xc5,
|
||||
0xf0, 0x06, 0x20, 0x31, 0xc8, 0xc8, 0xd0, 0xf5,
|
||||
0x60, 0x8d, 0xd2, 0xc0, 0xa9, 0xc5, 0x8d, 0xf8,
|
||||
0x07, 0x2c, 0xff, 0xcf, 0xad, 0x20, 0xc8, 0x49,
|
||||
0xcf, 0xd0, 0x05, 0xa0, 0x05, 0xa2, 0x50, 0x60,
|
||||
0x20, 0x93, 0xfe, 0x00, 0x43, 0x68, 0x65, 0x63,
|
||||
0x6b, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,
|
||||
0x20, 0x00, 0x4e, 0x6f, 0x20, 0x62, 0x6f, 0x6f,
|
||||
0x74, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x00,
|
||||
0x2e, 0x0d, 0x0d, 0x43, 0x6f, 0x75, 0x6c, 0x64,
|
||||
0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x6f, 0x6f,
|
||||
0x74, 0x20, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x20, 0x00, 0xff, 0x43, 0x46,
|
||||
0x46, 0x41, 0xff, 0x00, 0x00, 0x00, 0x17, 0x0a,
|
||||
0xa9, 0x20, 0xa2, 0x00, 0xa9, 0x03, 0xa9, 0x3c,
|
||||
0x10, 0x0a, 0x18, 0x90, 0x01, 0x38, 0x20, 0xa1,
|
||||
0xc6, 0x4c, 0x28, 0xc8, 0x20, 0xa1, 0xc6, 0x86,
|
||||
0x43, 0xac, 0x0b, 0xc8, 0xa9, 0xc5, 0x20, 0xa8,
|
||||
0xfc, 0x88, 0xd0, 0xf8, 0xad, 0x00, 0xc0, 0x2d,
|
||||
0x09, 0xc8, 0xcd, 0x0a, 0xc8, 0xd0, 0x07, 0xea,
|
||||
0x20, 0xa1, 0xc6, 0x20, 0x2b, 0xc8, 0xa0, 0x01,
|
||||
0x84, 0x42, 0x88, 0x84, 0x46, 0x84, 0x47, 0xa9,
|
||||
0x08, 0x85, 0x45, 0x84, 0x44, 0x20, 0xa1, 0xc6,
|
||||
0x20, 0x37, 0xc8, 0x85, 0xff, 0xb0, 0x10, 0xac,
|
||||
0x00, 0x08, 0x88, 0xd0, 0x0a, 0xad, 0x01, 0x08,
|
||||
0xf0, 0x05, 0xa6, 0x43, 0x4c, 0x01, 0x08, 0xa5,
|
||||
0x00, 0xd0, 0x0a, 0xa5, 0x01, 0xcd, 0xf8, 0x07,
|
||||
0xd0, 0x03, 0x4c, 0xba, 0xfa, 0x20, 0x2e, 0xc8,
|
||||
0xa0, 0x00, 0xa6, 0xff, 0xd0, 0x02, 0xa0, 0x0e,
|
||||
0x20, 0x95, 0xc6, 0x8a, 0xf0, 0x0c, 0xad, 0x7e,
|
||||
0x07, 0x0a, 0x0a, 0xa9, 0xb0, 0x69, 0x00, 0x20,
|
||||
0xed, 0xfd, 0xa0, 0x1c, 0x20, 0x95, 0xc6, 0x20,
|
||||
0xa1, 0xc6, 0x4c, 0x34, 0xc8, 0xb9, 0xbc, 0xc6,
|
||||
0xf0, 0x06, 0x20, 0x31, 0xc8, 0xc8, 0xd0, 0xf5,
|
||||
0x60, 0x8d, 0xe2, 0xc0, 0xa9, 0xc6, 0x8d, 0xf8,
|
||||
0x07, 0x2c, 0xff, 0xcf, 0xad, 0x20, 0xc8, 0x49,
|
||||
0xcf, 0xd0, 0x05, 0xa0, 0x06, 0xa2, 0x60, 0x60,
|
||||
0x20, 0x93, 0xfe, 0x00, 0x43, 0x68, 0x65, 0x63,
|
||||
0x6b, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,
|
||||
0x20, 0x00, 0x4e, 0x6f, 0x20, 0x62, 0x6f, 0x6f,
|
||||
0x74, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x00,
|
||||
0x2e, 0x0d, 0x0d, 0x43, 0x6f, 0x75, 0x6c, 0x64,
|
||||
0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x6f, 0x6f,
|
||||
0x74, 0x20, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x20, 0x00, 0xff, 0x43, 0x46,
|
||||
0x46, 0x41, 0xff, 0x00, 0x00, 0x00, 0x17, 0x0a,
|
||||
0xa9, 0x20, 0xa2, 0x00, 0xa9, 0x03, 0xa9, 0x3c,
|
||||
0x10, 0x0a, 0x18, 0x90, 0x01, 0x38, 0x20, 0xa1,
|
||||
0xc7, 0x4c, 0x28, 0xc8, 0x20, 0xa1, 0xc7, 0x86,
|
||||
0x43, 0xac, 0x0b, 0xc8, 0xa9, 0xc5, 0x20, 0xa8,
|
||||
0xfc, 0x88, 0xd0, 0xf8, 0xad, 0x00, 0xc0, 0x2d,
|
||||
0x09, 0xc8, 0xcd, 0x0a, 0xc8, 0xd0, 0x07, 0xea,
|
||||
0x20, 0xa1, 0xc7, 0x20, 0x2b, 0xc8, 0xa0, 0x01,
|
||||
0x84, 0x42, 0x88, 0x84, 0x46, 0x84, 0x47, 0xa9,
|
||||
0x08, 0x85, 0x45, 0x84, 0x44, 0x20, 0xa1, 0xc7,
|
||||
0x20, 0x37, 0xc8, 0x85, 0xff, 0xb0, 0x10, 0xac,
|
||||
0x00, 0x08, 0x88, 0xd0, 0x0a, 0xad, 0x01, 0x08,
|
||||
0xf0, 0x05, 0xa6, 0x43, 0x4c, 0x01, 0x08, 0xa5,
|
||||
0x00, 0xd0, 0x0a, 0xa5, 0x01, 0xcd, 0xf8, 0x07,
|
||||
0xd0, 0x03, 0x4c, 0xba, 0xfa, 0x20, 0x2e, 0xc8,
|
||||
0xa0, 0x00, 0xa6, 0xff, 0xd0, 0x02, 0xa0, 0x0e,
|
||||
0x20, 0x95, 0xc7, 0x8a, 0xf0, 0x0c, 0xad, 0x7f,
|
||||
0x07, 0x0a, 0x0a, 0xa9, 0xb0, 0x69, 0x00, 0x20,
|
||||
0xed, 0xfd, 0xa0, 0x1c, 0x20, 0x95, 0xc7, 0x20,
|
||||
0xa1, 0xc7, 0x4c, 0x34, 0xc8, 0xb9, 0xbc, 0xc7,
|
||||
0xf0, 0x06, 0x20, 0x31, 0xc8, 0xc8, 0xd0, 0xf5,
|
||||
0x60, 0x8d, 0xf2, 0xc0, 0xa9, 0xc7, 0x8d, 0xf8,
|
||||
0x07, 0x2c, 0xff, 0xcf, 0xad, 0x20, 0xc8, 0x49,
|
||||
0xcf, 0xd0, 0x05, 0xa0, 0x07, 0xa2, 0x70, 0x60,
|
||||
0x20, 0x93, 0xfe, 0x00, 0x43, 0x68, 0x65, 0x63,
|
||||
0x6b, 0x20, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65,
|
||||
0x20, 0x00, 0x4e, 0x6f, 0x20, 0x62, 0x6f, 0x6f,
|
||||
0x74, 0x20, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x00,
|
||||
0x2e, 0x0d, 0x0d, 0x43, 0x6f, 0x75, 0x6c, 0x64,
|
||||
0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x6f, 0x6f,
|
||||
0x74, 0x20, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74,
|
||||
0x69, 0x6f, 0x6e, 0x20, 0x00, 0xff, 0x43, 0x46,
|
||||
0x46, 0x41, 0xff, 0x00, 0x00, 0x00, 0x17, 0x0a,
|
||||
0x04, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0xdf, 0xcd, 0x05, 0x1f, 0x64, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
|
||||
0xcf, 0xfa, 0x20, 0x11, 0xc0, 0x00, 0x00, 0x07,
|
||||
0x4c, 0xb9, 0xca, 0x4c, 0x1e, 0xce, 0x4c, 0x59,
|
||||
0xc8, 0x4c, 0x84, 0xcf, 0x4c, 0x3a, 0xc8, 0x4c,
|
||||
0xbe, 0xca, 0xb9, 0xf8, 0x07, 0x18, 0x69, 0x01,
|
||||
0x20, 0x6d, 0xcf, 0x20, 0x8e, 0xfd, 0x20, 0xdd,
|
||||
0xfb, 0xa6, 0xff, 0xf0, 0x09, 0xa0, 0x15, 0x20,
|
||||
0x61, 0xcf, 0x8a, 0x20, 0xda, 0xfd, 0x4c, 0x00,
|
||||
0xe0, 0x20, 0x2f, 0xfb, 0x20, 0x84, 0xfe, 0x20,
|
||||
0x58, 0xfc, 0x20, 0x93, 0xfe, 0x20, 0x89, 0xfe,
|
||||
0x20, 0x81, 0xc8, 0xa0, 0x00, 0x20, 0x61, 0xcf,
|
||||
0xa9, 0x22, 0x85, 0x24, 0xa0, 0x0e, 0x20, 0x61,
|
||||
0xcf, 0xad, 0xf8, 0x07, 0x49, 0x70, 0x20, 0xed,
|
||||
0xfd, 0xa0, 0x28, 0xa9, 0xad, 0x20, 0xed, 0xfd,
|
||||
0x88, 0xd0, 0xf8, 0x60, 0xa0, 0x0c, 0xb9, 0x40,
|
||||
0x00, 0x48, 0x88, 0x10, 0xf9, 0x86, 0x41, 0xba,
|
||||
0xbd, 0x0e, 0x01, 0x85, 0x48, 0x18, 0x69, 0x03,
|
||||
0x9d, 0x0e, 0x01, 0xbd, 0x0f, 0x01, 0x85, 0x49,
|
||||
0x69, 0x00, 0x9d, 0x0f, 0x01, 0xad, 0xf8, 0x07,
|
||||
0x29, 0x0f, 0x85, 0x40, 0xa8, 0xa6, 0x41, 0x20,
|
||||
0xfb, 0xcb, 0xa0, 0x01, 0xb1, 0x48, 0x85, 0x42,
|
||||
0xc8, 0xb1, 0x48, 0xaa, 0xc8, 0xb1, 0x48, 0x85,
|
||||
0x49, 0x86, 0x48, 0xa9, 0x01, 0xa6, 0x42, 0xe0,
|
||||
0x0a, 0xb0, 0x1c, 0xa0, 0x00, 0xb1, 0x48, 0xdd,
|
||||
0x10, 0xc9, 0xd0, 0x27, 0xa0, 0x01, 0xb1, 0x48,
|
||||
0xa4, 0x40, 0x99, 0xf8, 0x04, 0x8a, 0x0a, 0xaa,
|
||||
0x20, 0x07, 0xc9, 0xb0, 0x02, 0xa9, 0x00, 0xaa,
|
||||
0xa0, 0x00, 0x68, 0x99, 0x40, 0x00, 0xc8, 0xc0,
|
||||
0x0d, 0x90, 0xf7, 0x8a, 0xa0, 0x02, 0xa2, 0x00,
|
||||
0xc9, 0x01, 0x60, 0xa9, 0x04, 0xd0, 0xe8, 0xbd,
|
||||
0x1b, 0xc9, 0x48, 0xbd, 0x1a, 0xc9, 0x48, 0x60,
|
||||
0x03, 0x03, 0x03, 0x01, 0x03, 0x01, 0x01, 0x01,
|
||||
0x04, 0x04, 0x2d, 0xc9, 0x34, 0xcb, 0x2c, 0xcb,
|
||||
0xa7, 0xca, 0x2e, 0xca, 0x50, 0xca, 0x5a, 0xca,
|
||||
0x5a, 0xca, 0xef, 0xc9, 0xef, 0xc9, 0x20, 0x5f,
|
||||
0xca, 0xb0, 0x37, 0xa4, 0x40, 0xb9, 0xf8, 0x04,
|
||||
0xd0, 0x38, 0xa5, 0x4c, 0xf0, 0x14, 0xc9, 0x49,
|
||||
0xf0, 0x09, 0xc9, 0x48, 0xd0, 0x08, 0xa9, 0x40,
|
||||
0x99, 0x78, 0x07, 0x4c, 0xdb, 0xc9, 0xa9, 0x21,
|
||||
0x38, 0x60, 0xa4, 0x40, 0x18, 0xb9, 0x78, 0x06,
|
||||
0x79, 0xf8, 0x06, 0xa0, 0x00, 0x91, 0x4a, 0xa0,
|
||||
0x07, 0xb9, 0x6a, 0xc9, 0x91, 0x4a, 0x88, 0xd0,
|
||||
0xf8, 0x18, 0x60, 0x40, 0x00, 0xcc, 0x00, 0x20,
|
||||
0x00, 0x00, 0x38, 0xe9, 0x01, 0x20, 0xb3, 0xcd,
|
||||
0xb0, 0x0e, 0xa6, 0x4c, 0xf0, 0x18, 0xca, 0xf0,
|
||||
0x08, 0xca, 0xca, 0xf0, 0x11, 0xa9, 0x21, 0x38,
|
||||
0x60, 0xa9, 0x01, 0xa0, 0x00, 0x91, 0x4a, 0xa8,
|
||||
0xa9, 0x00, 0x91, 0x4a, 0x18, 0x60, 0xa9, 0xf8,
|
||||
0xa0, 0x00, 0x91, 0x4a, 0xa6, 0x41, 0xa4, 0x40,
|
||||
0x20, 0xe6, 0xca, 0xb0, 0xc5, 0x98, 0xa0, 0x02,
|
||||
0x91, 0x4a, 0x88, 0x8a, 0x91, 0x4a, 0xa0, 0x03,
|
||||
0xa9, 0x00, 0x91, 0x4a, 0xa5, 0x4c, 0xf0, 0x0c,
|
||||
0xa0, 0x04, 0xb9, 0xc2, 0xc9, 0x91, 0x4a, 0xc8,
|
||||
0xc0, 0x19, 0x90, 0xf6, 0x18, 0x60, 0x10, 0x43,
|
||||
0x4f, 0x4d, 0x50, 0x41, 0x43, 0x54, 0x20, 0x46,
|
||||
0x4c, 0x41, 0x53, 0x48, 0x20, 0x20, 0x20, 0x02,
|
||||
0x20, 0x00, 0x20, 0xa4, 0x40, 0xa6, 0x41, 0x20,
|
||||
0x99, 0xcd, 0xd0, 0x04, 0xa9, 0x28, 0x38, 0x60,
|
||||
0x20, 0x2b, 0xcd, 0x20, 0x10, 0xcd, 0xd0, 0x04,
|
||||
0xa9, 0x27, 0x38, 0x60, 0xa0, 0x01, 0x84, 0x42,
|
||||
0x88, 0xbd, 0x88, 0xc0, 0x91, 0x4a, 0xc8, 0xbd,
|
||||
0x80, 0xc0, 0x91, 0x4a, 0xc8, 0xd0, 0xf2, 0xe6,
|
||||
0x4b, 0xc6, 0x42, 0x10, 0xec, 0xc6, 0x4b, 0xc6,
|
||||
0x4b, 0xa0, 0x2e, 0xa2, 0x18, 0x20, 0x1c, 0xca,
|
||||
0xa0, 0x14, 0xa2, 0x0a, 0xb1, 0x4a, 0x48, 0xc8,
|
||||
0xb1, 0x4a, 0x88, 0x91, 0x4a, 0xc8, 0x68, 0x91,
|
||||
0x4a, 0xc8, 0xca, 0xd0, 0xef, 0x18, 0x60, 0x20,
|
||||
0x5f, 0xca, 0xb0, 0x18, 0x20, 0xa8, 0xca, 0xb0,
|
||||
0x13, 0xa6, 0x4c, 0xf0, 0x0f, 0xca, 0xf0, 0x0c,
|
||||
0xca, 0xf0, 0x06, 0xca, 0xf0, 0x07, 0xca, 0xf0,
|
||||
0x03, 0xa9, 0x21, 0x38, 0x60, 0xa9, 0x1f, 0x38,
|
||||
0x60, 0xb9, 0xf8, 0x04, 0x18, 0xf0, 0x03, 0xa9,
|
||||
0x11, 0x38, 0x60, 0xa9, 0x01, 0x38, 0x60, 0xa0,
|
||||
0x02, 0xb1, 0x48, 0x85, 0x4a, 0xc8, 0xb1, 0x48,
|
||||
0x85, 0x4b, 0xc8, 0xb1, 0x48, 0x85, 0x4c, 0x18,
|
||||
0x60, 0xa0, 0x02, 0xb1, 0x48, 0x85, 0x44, 0xc8,
|
||||
0xb1, 0x48, 0x85, 0x45, 0xc8, 0xb1, 0x48, 0x85,
|
||||
0x46, 0xc8, 0xb1, 0x48, 0x85, 0x47, 0xa4, 0x40,
|
||||
0xb9, 0xf8, 0x04, 0x20, 0xa8, 0xca, 0xb0, 0x17,
|
||||
0xa0, 0x06, 0xb1, 0x48, 0xd0, 0x0e, 0xa5, 0x47,
|
||||
0x25, 0x46, 0xc9, 0xff, 0xf0, 0x06, 0xa4, 0x40,
|
||||
0xa6, 0x41, 0x18, 0x60, 0xa9, 0x2d, 0x38, 0x60,
|
||||
0xa4, 0x40, 0xb9, 0xf8, 0x04, 0x38, 0xf0, 0x06,
|
||||
0x38, 0xe9, 0x01, 0x20, 0xb3, 0xcd, 0xa9, 0x11,
|
||||
0x60, 0x90, 0x03, 0x4c, 0x8c, 0xc8, 0x20, 0xfb,
|
||||
0xcb, 0x8a, 0x45, 0x43, 0x29, 0x70, 0xf0, 0x02,
|
||||
0xa9, 0x02, 0x24, 0x43, 0x10, 0x03, 0x18, 0x69,
|
||||
0x01, 0x20, 0xb3, 0xcd, 0xb0, 0x0e, 0xa5, 0x42,
|
||||
0xf0, 0x0c, 0xc9, 0x01, 0xf0, 0x5c, 0xc9, 0x02,
|
||||
0xf0, 0x50, 0xa9, 0x27, 0x38, 0x60, 0xb9, 0x78,
|
||||
0x07, 0x0a, 0x30, 0x0d, 0xb9, 0xf8, 0x04, 0x18,
|
||||
0x69, 0x01, 0xd9, 0x78, 0x06, 0xf0, 0x15, 0xd0,
|
||||
0x0b, 0xb9, 0xf8, 0x04, 0x18, 0x69, 0x01, 0xd9,
|
||||
0xf8, 0x06, 0xf0, 0x08, 0xa2, 0xff, 0xa0, 0xff,
|
||||
0xa9, 0x00, 0x18, 0x60, 0xa5, 0x08, 0x48, 0xa5,
|
||||
0x07, 0x48, 0xa5, 0x06, 0x48, 0x20, 0x3b, 0xcd,
|
||||
0xa4, 0x07, 0xa6, 0x06, 0x68, 0x85, 0x06, 0x68,
|
||||
0x85, 0x07, 0x68, 0x85, 0x08, 0xb0, 0x03, 0xa9,
|
||||
0x00, 0x60, 0xa9, 0x27, 0x60, 0x20, 0x71, 0xca,
|
||||
0xb0, 0xfa, 0x38, 0xb0, 0x06, 0x20, 0x71, 0xca,
|
||||
0xb0, 0xf2, 0x18, 0xa5, 0x47, 0x48, 0xa5, 0x46,
|
||||
0x48, 0xa5, 0x45, 0x48, 0xa5, 0xef, 0x48, 0x08,
|
||||
0x20, 0xbd, 0xcb, 0x28, 0xa9, 0x20, 0x90, 0x02,
|
||||
0xa9, 0x30, 0x08, 0x20, 0x12, 0xcd, 0xf0, 0x60,
|
||||
0x28, 0xa0, 0x01, 0x84, 0xef, 0x88, 0xb0, 0x17,
|
||||
0xbd, 0x88, 0xc0, 0x91, 0x44, 0xc8, 0xbd, 0x80,
|
||||
0xc0, 0x91, 0x44, 0xc8, 0xd0, 0xf2, 0xe6, 0x45,
|
||||
0xc6, 0xef, 0x10, 0xec, 0x4c, 0xa0, 0xcb, 0xa5,
|
||||
0x44, 0x18, 0x69, 0x01, 0x85, 0x46, 0xa5, 0x45,
|
||||
0x69, 0x00, 0x85, 0x47, 0xbd, 0x81, 0xc0, 0xb1,
|
||||
0x46, 0x9d, 0x80, 0xc0, 0xb1, 0x44, 0x9d, 0x88,
|
||||
0xc0, 0xc8, 0xc8, 0xd0, 0xf2, 0xe6, 0x45, 0xe6,
|
||||
0x47, 0xc6, 0xef, 0x10, 0xea, 0xbd, 0x82, 0xc0,
|
||||
0x20, 0x21, 0xcd, 0xf0, 0x14, 0xa9, 0x00, 0xc9,
|
||||
0x01, 0xaa, 0x68, 0x85, 0xef, 0x68, 0x85, 0x45,
|
||||
0x68, 0x85, 0x46, 0x68, 0x85, 0x47, 0x8a, 0x60,
|
||||
0x28, 0xa9, 0x27, 0xd0, 0xea, 0x20, 0x2b, 0xcd,
|
||||
0xa9, 0x01, 0x9d, 0x8a, 0xc0, 0xb9, 0x78, 0x07,
|
||||
0x0a, 0x30, 0x1a, 0xa5, 0x46, 0x6d, 0x0f, 0xc8,
|
||||
0x9d, 0x8b, 0xc0, 0xa5, 0x47, 0x6d, 0x10, 0xc8,
|
||||
0x9d, 0x8c, 0xc0, 0xad, 0x11, 0xc8, 0x79, 0xf8,
|
||||
0x04, 0x9d, 0x8d, 0xc0, 0x60, 0xa5, 0x46, 0x6d,
|
||||
0x12, 0xc8, 0x9d, 0x8b, 0xc0, 0xa5, 0x47, 0x6d,
|
||||
0x13, 0xc8, 0x9d, 0x8c, 0xc0, 0xad, 0x14, 0xc8,
|
||||
0x4c, 0xde, 0xcb, 0xb9, 0x78, 0x04, 0xc9, 0xa5,
|
||||
0xf0, 0x49, 0xad, 0x03, 0xc8, 0x38, 0xe9, 0x01,
|
||||
0x99, 0xf8, 0x07, 0xad, 0x02, 0xc8, 0x99, 0x78,
|
||||
0x05, 0xad, 0x00, 0xc8, 0x99, 0x78, 0x06, 0xad,
|
||||
0x01, 0xc8, 0x99, 0xf8, 0x06, 0x19, 0x78, 0x06,
|
||||
0xf0, 0x24, 0xbd, 0x82, 0xc0, 0xa9, 0x00, 0x9d,
|
||||
0x80, 0xc0, 0xad, 0x0e, 0xc8, 0x30, 0x14, 0xa9,
|
||||
0x06, 0x9d, 0x86, 0xc0, 0xa9, 0x04, 0x20, 0xa7,
|
||||
0xcd, 0xa9, 0x02, 0x9d, 0x86, 0xc0, 0xa9, 0x7c,
|
||||
0x20, 0xa7, 0xcd, 0x20, 0x51, 0xcc, 0xa9, 0xa5,
|
||||
0x99, 0x78, 0x04, 0xa9, 0x00, 0x99, 0x78, 0x07,
|
||||
0x60, 0xbd, 0x82, 0xc0, 0x20, 0x87, 0xcc, 0x90,
|
||||
0x09, 0xa9, 0x00, 0x99, 0x78, 0x06, 0x99, 0xf8,
|
||||
0x06, 0x60, 0xb9, 0x78, 0x06, 0xf0, 0x0d, 0xa9,
|
||||
0xe0, 0x20, 0xa9, 0xcc, 0xd9, 0x78, 0x06, 0xb0,
|
||||
0x03, 0x99, 0x78, 0x06, 0xb9, 0xf8, 0x06, 0xf0,
|
||||
0x0d, 0xa9, 0xf0, 0x20, 0xa9, 0xcc, 0xd9, 0xf8,
|
||||
0x06, 0xb0, 0x03, 0x99, 0xf8, 0x06, 0x60, 0x98,
|
||||
0x48, 0xac, 0x0c, 0xc8, 0xbd, 0x8f, 0xc0, 0x10,
|
||||
0x14, 0xa9, 0x0a, 0x48, 0xa9, 0xc5, 0x20, 0xa7,
|
||||
0xcd, 0x68, 0x38, 0xe9, 0x01, 0xd0, 0xf4, 0x88,
|
||||
0xd0, 0xea, 0x38, 0xb0, 0x01, 0x18, 0x68, 0xa8,
|
||||
0x60, 0x9d, 0x8e, 0xc0, 0x99, 0xf8, 0x05, 0x98,
|
||||
0x48, 0xac, 0x0d, 0xc8, 0xbd, 0x8f, 0xc0, 0x29,
|
||||
0xd0, 0xc9, 0x50, 0xf0, 0x15, 0xa9, 0xc5, 0x20,
|
||||
0xa7, 0xcd, 0x88, 0xd0, 0xef, 0x68, 0xa8, 0xb9,
|
||||
0xf8, 0x05, 0x49, 0x10, 0x9d, 0x8e, 0xc0, 0xa9,
|
||||
0x00, 0x60, 0x68, 0xa8, 0xbd, 0x8e, 0xc0, 0x29,
|
||||
0x10, 0x0a, 0x0a, 0x99, 0x78, 0x07, 0xa5, 0x08,
|
||||
0x48, 0xa5, 0x07, 0x48, 0xa5, 0x06, 0x48, 0x20,
|
||||
0x3b, 0xcd, 0xa9, 0x00, 0xb0, 0x0a, 0xa5, 0x06,
|
||||
0x05, 0x07, 0xc9, 0x01, 0xa5, 0x08, 0x69, 0x00,
|
||||
0x99, 0xf8, 0x05, 0x68, 0x85, 0x06, 0x68, 0x85,
|
||||
0x07, 0x68, 0x85, 0x08, 0xb9, 0xf8, 0x05, 0x60,
|
||||
0xbd, 0x8f, 0xc0, 0x30, 0xfb, 0x29, 0x08, 0x60,
|
||||
0xa9, 0xec, 0x48, 0xbd, 0x8f, 0xc0, 0x30, 0xfb,
|
||||
0xa9, 0x00, 0x9d, 0x80, 0xc0, 0x68, 0x9d, 0x8f,
|
||||
0xc0, 0xbd, 0x8f, 0xc0, 0x30, 0xfb, 0x29, 0x09,
|
||||
0xc9, 0x01, 0x60, 0x20, 0x08, 0xcd, 0xb9, 0x78,
|
||||
0x07, 0x4a, 0x4a, 0x29, 0x10, 0x09, 0xe0, 0x9d,
|
||||
0x8e, 0xc0, 0x60, 0x20, 0x2b, 0xcd, 0x20, 0x10,
|
||||
0xcd, 0xd0, 0x04, 0xa9, 0x27, 0x38, 0x60, 0x98,
|
||||
0x48, 0xa0, 0x00, 0xbd, 0x88, 0xc0, 0xc8, 0xc0,
|
||||
0x39, 0xd0, 0xf8, 0x68, 0xa8, 0xb9, 0x78, 0x07,
|
||||
0x0a, 0x0a, 0x98, 0x48, 0xa0, 0x00, 0x90, 0x02,
|
||||
0xa0, 0x03, 0x38, 0xbd, 0x88, 0xc0, 0xf9, 0x0f,
|
||||
0xc8, 0x85, 0x06, 0xbd, 0x80, 0xc0, 0xf9, 0x10,
|
||||
0xc8, 0x85, 0x07, 0xbd, 0x88, 0xc0, 0xf9, 0x11,
|
||||
0xc8, 0x85, 0x08, 0xbd, 0x80, 0xc0, 0xe9, 0x00,
|
||||
0xf0, 0x08, 0xa9, 0xff, 0x85, 0x06, 0x85, 0x07,
|
||||
0x85, 0x08, 0x68, 0xa8, 0x20, 0x08, 0xcd, 0xf0,
|
||||
0x06, 0xbd, 0x88, 0xc0, 0x4c, 0x8c, 0xcd, 0x18,
|
||||
0x60, 0xb9, 0x78, 0x07, 0x0a, 0x30, 0x04, 0xb9,
|
||||
0x78, 0x06, 0x60, 0xb9, 0xf8, 0x06, 0x60, 0x38,
|
||||
0x48, 0xe9, 0x01, 0xd0, 0xfc, 0x68, 0xe9, 0x01,
|
||||
0xd0, 0xf6, 0x60, 0x48, 0xb9, 0x78, 0x05, 0xd0,
|
||||
0x22, 0x68, 0xf0, 0x09, 0xd9, 0xf8, 0x07, 0xd0,
|
||||
0x07, 0xa9, 0x00, 0xf0, 0x03, 0xb9, 0xf8, 0x07,
|
||||
0x99, 0xf8, 0x04, 0x38, 0xf9, 0x78, 0x06, 0x90,
|
||||
0x32, 0x99, 0xf8, 0x04, 0xd9, 0xf8, 0x06, 0xb0,
|
||||
0x22, 0x90, 0x23, 0x68, 0xf0, 0x09, 0xd9, 0xf8,
|
||||
0x07, 0xd0, 0x07, 0xa9, 0x00, 0xf0, 0x03, 0xb9,
|
||||
0xf8, 0x07, 0x99, 0xf8, 0x04, 0x38, 0xf9, 0xf8,
|
||||
0x06, 0x90, 0x0b, 0x99, 0xf8, 0x04, 0xd9, 0x78,
|
||||
0x06, 0x90, 0x08, 0xa9, 0x28, 0x60, 0xa9, 0x40,
|
||||
0x99, 0x78, 0x07, 0xb9, 0xf8, 0x04, 0x60, 0x0a,
|
||||
0x0b, 0x0d, 0x0e, 0x15, 0x03, 0x0e, 0x0a, 0x09,
|
||||
0x08, 0x1c, 0x2b, 0x2f, 0x37, 0x40, 0x00, 0x00,
|
||||
0x00, 0x01, 0x0e, 0x0e, 0x02, 0x0e, 0x86, 0x41,
|
||||
0x84, 0x40, 0x84, 0x01, 0xa9, 0x00, 0x8d, 0xf2,
|
||||
0x03, 0xa9, 0xe0, 0x8d, 0xf3, 0x03, 0x20, 0x6f,
|
||||
0xfb, 0x20, 0x59, 0xc8, 0xa6, 0x41, 0xa4, 0x40,
|
||||
0x20, 0x02, 0xcc, 0x8d, 0x10, 0xc0, 0xa4, 0x40,
|
||||
0xb9, 0x78, 0x05, 0x85, 0x87, 0xb9, 0xf8, 0x07,
|
||||
0x18, 0x69, 0x01, 0x85, 0x88, 0xa2, 0x01, 0xd0,
|
||||
0x02, 0xa2, 0x03, 0xbd, 0x00, 0xc8, 0x95, 0x85,
|
||||
0xca, 0x10, 0xf8, 0xa9, 0x03, 0x85, 0x82, 0xa9,
|
||||
0x00, 0x85, 0x83, 0xa2, 0x04, 0xbd, 0x07, 0xce,
|
||||
0x20, 0x5b, 0xfb, 0xbd, 0x0c, 0xce, 0x85, 0x24,
|
||||
0xbd, 0x11, 0xce, 0xa8, 0x20, 0x61, 0xcf, 0xe0,
|
||||
0x04, 0xf0, 0x22, 0xa0, 0x13, 0x20, 0x61, 0xcf,
|
||||
0xb5, 0x85, 0xdd, 0x00, 0xc8, 0xf0, 0x0a, 0xe0,
|
||||
0x02, 0xb0, 0x02, 0xe6, 0x83, 0xa0, 0x3f, 0x84,
|
||||
0x32, 0x20, 0x6d, 0xcf, 0xa0, 0xff, 0x84, 0x32,
|
||||
0xa9, 0xa0, 0x20, 0xed, 0xfd, 0xca, 0x10, 0xc5,
|
||||
0xa4, 0x40, 0xa5, 0x87, 0x99, 0x78, 0x05, 0xa5,
|
||||
0x88, 0x38, 0xe9, 0x01, 0x99, 0xf8, 0x07, 0xa6,
|
||||
0x82, 0xbd, 0x07, 0xce, 0x20, 0x5b, 0xfb, 0xa9,
|
||||
0x16, 0x85, 0x24, 0xd0, 0x03, 0x20, 0xdd, 0xfb,
|
||||
0x20, 0x0c, 0xfd, 0xc9, 0x9b, 0xf0, 0x8a, 0xc9,
|
||||
0x88, 0xd0, 0x12, 0xa9, 0xfe, 0x75, 0x85, 0xdd,
|
||||
0x16, 0xce, 0x90, 0xe9, 0xdd, 0x1a, 0xce, 0xb0,
|
||||
0xe4, 0x95, 0x85, 0x90, 0x82, 0xc9, 0x95, 0xd0,
|
||||
0x04, 0xa9, 0x00, 0xf0, 0xe8, 0xc9, 0x8d, 0xf0,
|
||||
0x04, 0xc9, 0x8a, 0xd0, 0x0f, 0x8a, 0x18, 0x69,
|
||||
0x01, 0xc9, 0x04, 0x90, 0x02, 0xa9, 0x00, 0x85,
|
||||
0x82, 0x4c, 0xaf, 0xce, 0xc9, 0x8b, 0xd0, 0x08,
|
||||
0xc6, 0x82, 0x10, 0xf5, 0xa9, 0x03, 0xd0, 0xef,
|
||||
0x29, 0xdf, 0xc9, 0xc2, 0xd0, 0x0f, 0xa5, 0x83,
|
||||
0xd0, 0xab, 0x20, 0x58, 0xfc, 0xad, 0xf8, 0x07,
|
||||
0x85, 0x84, 0x6c, 0x83, 0x00, 0xc9, 0xd3, 0xf0,
|
||||
0x0a, 0xc9, 0xd1, 0xd0, 0xd4, 0x20, 0x58, 0xfc,
|
||||
0x4c, 0x00, 0xe0, 0xa2, 0x0e, 0xbd, 0xf0, 0xcf,
|
||||
0x95, 0x90, 0xca, 0x10, 0xf8, 0xa6, 0x41, 0x9d,
|
||||
0x83, 0xc0, 0xa2, 0x03, 0xb5, 0x85, 0xdd, 0x00,
|
||||
0xc8, 0xf0, 0x0e, 0xe0, 0x02, 0xb0, 0x05, 0xa4,
|
||||
0x40, 0x99, 0x78, 0x04, 0x20, 0x90, 0x00, 0xf0,
|
||||
0x0a, 0xca, 0x10, 0xe8, 0xa6, 0x41, 0x9d, 0x84,
|
||||
0xc0, 0xd0, 0x03, 0x20, 0xdd, 0xfb, 0x4c, 0x5f,
|
||||
0xce, 0xb9, 0x9a, 0xcf, 0x48, 0x20, 0x31, 0xc8,
|
||||
0xc8, 0x68, 0x10, 0xf5, 0x60, 0xc9, 0x0a, 0x90,
|
||||
0x11, 0xc9, 0x14, 0x90, 0x04, 0xa9, 0x3f, 0xd0,
|
||||
0x0b, 0xe9, 0x09, 0x48, 0xa9, 0xb1, 0x20, 0xed,
|
||||
0xfd, 0x68, 0x09, 0xb0, 0x09, 0x80, 0xc9, 0xe1,
|
||||
0x90, 0x0d, 0x48, 0xad, 0xb3, 0xfb, 0x49, 0x06,
|
||||
0xc9, 0x01, 0x68, 0x90, 0x02, 0x29, 0xdf, 0x4c,
|
||||
0xed, 0xfd, 0x43, 0x46, 0x46, 0x41, 0x20, 0x36,
|
||||
0x35, 0x30, 0x32, 0x20, 0x76, 0x32, 0x2e, 0xb0,
|
||||
0x53, 0x6c, 0x6f, 0x74, 0xa0, 0x3a, 0xa0, 0x0d,
|
||||
0x0d, 0x45, 0x72, 0x72, 0x20, 0xa4, 0x50, 0x61,
|
||||
0x72, 0x74, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73,
|
||||
0x20, 0x44, 0x65, 0x76, 0xb0, 0x44, 0x65, 0x76,
|
||||
0xb1, 0x42, 0x6f, 0x6f, 0x74, 0x20, 0x44, 0x65,
|
||||
0xf6, 0x50, 0x61, 0x72, 0x74, 0x69, 0x74, 0x69,
|
||||
0x6f, 0xee, 0x42, 0x29, 0x4f, 0x4f, 0x54, 0x20,
|
||||
0x53, 0x29, 0x41, 0x56, 0x45, 0x20, 0x51, 0x29,
|
||||
0x55, 0x49, 0xd4, 0x77, 0x77, 0x77, 0x77, 0xff,
|
||||
0x9d, 0x00, 0xc8, 0xa0, 0x00, 0x88, 0xf0, 0x05,
|
||||
0xdd, 0x00, 0xc8, 0xd0, 0xf8, 0x98, 0x60, 0xff
|
||||
];
|
137
js/ui/apple2.js
137
js/ui/apple2.js
|
@ -13,13 +13,15 @@ import ApplesoftCompiler from '../applesoft/compiler';
|
|||
import { debug, gup, hup } from '../util';
|
||||
import Prefs from '../prefs';
|
||||
|
||||
var paused = false;
|
||||
|
||||
var focused = false;
|
||||
var startTime = Date.now();
|
||||
var lastCycles = 0;
|
||||
var lastFrames = 0;
|
||||
var lastRenderedFrames = 0;
|
||||
|
||||
var hashtag;
|
||||
var hashtag = document.location.hash;
|
||||
|
||||
var disk_categories = {'Local Saves': []};
|
||||
var disk_sets = {};
|
||||
|
@ -32,6 +34,7 @@ var stats;
|
|||
var vm;
|
||||
var tape;
|
||||
var _disk2;
|
||||
var _cffa;
|
||||
var audio;
|
||||
var keyboard;
|
||||
var io;
|
||||
|
@ -81,6 +84,17 @@ export function openSave(drive, event) {
|
|||
}
|
||||
}
|
||||
|
||||
export function openAlert(msg) {
|
||||
var el = document.querySelector('#alert-modal .message');
|
||||
el.innerText = msg;
|
||||
MicroModal.show('alert-modal');
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
*
|
||||
* Drag and Drop
|
||||
*/
|
||||
|
||||
export function handleDragOver(drive, event) {
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = 'copy';
|
||||
|
@ -130,8 +144,31 @@ export function handleDrop(drive, event) {
|
|||
}
|
||||
}
|
||||
|
||||
export function loadAjax(drive, url) {
|
||||
function loadingStart () {
|
||||
var meter = document.querySelector('#loading-modal .meter');
|
||||
meter.style.display = 'none';
|
||||
MicroModal.show('loading-modal');
|
||||
}
|
||||
|
||||
function loadingProgress (current, total) {
|
||||
if (total) {
|
||||
var meter = document.querySelector('#loading-modal .meter');
|
||||
var progress = document.querySelector('#loading-modal .progress');
|
||||
meter.style.display = 'block';
|
||||
progress.style.width = current / total * meter.clientWidth + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
function loadingStop () {
|
||||
MicroModal.close('loading-modal');
|
||||
|
||||
if (!paused) {
|
||||
_apple2.run();
|
||||
}
|
||||
}
|
||||
|
||||
export function loadAjax(drive, url) {
|
||||
loadingStart();
|
||||
|
||||
fetch(url).then(function(response) {
|
||||
if (response.ok) {
|
||||
|
@ -146,10 +183,10 @@ export function loadAjax(drive, url) {
|
|||
loadDisk(drive, data);
|
||||
}
|
||||
initGamepad(data.gamepad);
|
||||
MicroModal.close('loading-modal');
|
||||
loadingStop();
|
||||
}).catch(function(error) {
|
||||
MicroModal.close('loading-modal');
|
||||
window.alert(error.message);
|
||||
loadingStop();
|
||||
openAlert(error.message);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -211,32 +248,67 @@ function doLoadLocal(drive, file) {
|
|||
} else if (TAPE_TYPES.indexOf(ext) > -1) {
|
||||
tape.doLoadLocalTape(file);
|
||||
} else {
|
||||
window.alert('Unknown file type: ' + ext);
|
||||
openAlert('Unknown file type: ' + ext);
|
||||
}
|
||||
}
|
||||
|
||||
function doLoadLocalDisk(drive, file) {
|
||||
MicroModal.show('loading-modal');
|
||||
loadingStart();
|
||||
var fileReader = new FileReader();
|
||||
fileReader.onload = function() {
|
||||
var parts = file.name.split('.');
|
||||
var ext = parts.pop().toLowerCase();
|
||||
var name = parts.join('.');
|
||||
if (_disk2.setBinary(drive, name, ext, this.result)) {
|
||||
driveLights.label(drive, name);
|
||||
MicroModal.close('loading-modal');
|
||||
initGamepad();
|
||||
if (this.result.byteLength >= 800 * 1024) {
|
||||
if (_cffa.setBinary(drive, name, ext, this.result)) {
|
||||
driveLights.label(drive, name);
|
||||
initGamepad();
|
||||
}
|
||||
} else {
|
||||
if (_disk2.setBinary(drive, name, ext, this.result)) {
|
||||
driveLights.label(drive, name);
|
||||
initGamepad();
|
||||
}
|
||||
}
|
||||
loadingStop();
|
||||
};
|
||||
fileReader.readAsArrayBuffer(file);
|
||||
}
|
||||
|
||||
export function doLoadHTTP(drive, _url) {
|
||||
if (!_url) {
|
||||
MicroModal.close('http-modal');
|
||||
}
|
||||
|
||||
loadingStart();
|
||||
var url = _url || document.querySelector('#http_url').value;
|
||||
if (url) {
|
||||
fetch(url).then(function(response) {
|
||||
if (response.ok) {
|
||||
return response.arrayBuffer();
|
||||
var reader = response.body.getReader();
|
||||
var received = 0;
|
||||
var chunks = [];
|
||||
var contentLength = parseInt(response.headers.get('content-length'), 10);
|
||||
|
||||
return reader.read().then(function readChunk(result) {
|
||||
if (result.done) {
|
||||
var data = new Uint8Array(received);
|
||||
var offset = 0;
|
||||
for (var idx = 0; idx < chunks.length; idx++) {
|
||||
data.set(chunks[idx], offset);
|
||||
offset += chunks[idx].length;
|
||||
}
|
||||
return data.buffer;
|
||||
}
|
||||
|
||||
received += result.value.length;
|
||||
if (contentLength) {
|
||||
loadingProgress(received, contentLength);
|
||||
}
|
||||
chunks.push(result.value);
|
||||
|
||||
return reader.read().then(readChunk);
|
||||
});
|
||||
} else {
|
||||
throw new Error('Error loading: ' + response.statusText);
|
||||
}
|
||||
|
@ -246,14 +318,21 @@ export function doLoadHTTP(drive, _url) {
|
|||
var fileParts = file.split('.');
|
||||
var ext = fileParts.pop().toLowerCase();
|
||||
var name = decodeURIComponent(fileParts.join('.'));
|
||||
if (_disk2.setBinary(drive, name, ext, data)) {
|
||||
driveLights.label(drive, name);
|
||||
initGamepad();
|
||||
if (data.byteLength >= 800 * 1024) {
|
||||
if (_cffa.setBinary(drive, name, ext, data)) {
|
||||
driveLights.label(drive, name);
|
||||
initGamepad();
|
||||
}
|
||||
} else {
|
||||
if (_disk2.setBinary(drive, name, ext, data)) {
|
||||
driveLights.label(drive, name);
|
||||
initGamepad();
|
||||
}
|
||||
}
|
||||
if (!_url) { MicroModal.close('http-modal'); }
|
||||
loadingStop();
|
||||
}).catch(function(error) {
|
||||
window.alert(error.message);
|
||||
if (!_url) { MicroModal.close('http-modal'); }
|
||||
loadingStop();
|
||||
openAlert(error.message);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -432,7 +511,7 @@ function saveLocalStorage(drive, name) {
|
|||
|
||||
window.localStorage.diskIndex = JSON.stringify(diskIndex);
|
||||
|
||||
window.alert('Saved');
|
||||
openAlert('Saved');
|
||||
|
||||
driveLights.label(drive, name);
|
||||
driveLights.dirty(drive, false);
|
||||
|
@ -443,7 +522,7 @@ function deleteLocalStorage(name) {
|
|||
var diskIndex = JSON.parse(window.localStorage.diskIndex || '{}');
|
||||
if (diskIndex[name]) {
|
||||
delete diskIndex[name];
|
||||
window.alert('Deleted');
|
||||
openAlert('Deleted');
|
||||
}
|
||||
window.localStorage.diskIndex = JSON.stringify(diskIndex);
|
||||
updateLocalStorage();
|
||||
|
@ -652,8 +731,6 @@ function _mousemove(evt) {
|
|||
io.paddle(1, flipY ? 1 - y : y);
|
||||
}
|
||||
|
||||
var paused = false;
|
||||
|
||||
export function pauseRun() {
|
||||
var label = document.querySelector('#pause-run i');
|
||||
if (paused) {
|
||||
|
@ -682,7 +759,7 @@ export function openPrinterModal() {
|
|||
MicroModal.show('printer-modal');
|
||||
}
|
||||
|
||||
export function initUI(apple2, disk2, e) {
|
||||
export function initUI(apple2, disk2, cffa, e) {
|
||||
_apple2 = apple2;
|
||||
cpu = _apple2.getCPU();
|
||||
io = _apple2.getIO();
|
||||
|
@ -690,6 +767,7 @@ export function initUI(apple2, disk2, e) {
|
|||
vm = apple2.getVideoModes();
|
||||
tape = new Tape(io);
|
||||
_disk2 = disk2;
|
||||
_cffa = cffa;
|
||||
|
||||
keyboard = new KeyBoard(cpu, io, e);
|
||||
keyboard.create('#keyboard');
|
||||
|
@ -747,6 +825,10 @@ export function initUI(apple2, disk2, e) {
|
|||
});
|
||||
}
|
||||
|
||||
if (navigator.standalone) {
|
||||
document.body.classList.add('standalone');
|
||||
}
|
||||
|
||||
cpu.reset();
|
||||
setInterval(updateKHz, 1000);
|
||||
updateSound();
|
||||
|
@ -758,12 +840,9 @@ export function initUI(apple2, disk2, e) {
|
|||
|
||||
var hash = gup('disk') || hup();
|
||||
if (hash) {
|
||||
_apple2.stop();
|
||||
processHash(hash);
|
||||
} else {
|
||||
_apple2.run();
|
||||
}
|
||||
|
||||
if (navigator.standalone) {
|
||||
document.body.classList.add('standalone');
|
||||
}
|
||||
|
||||
_apple2.run();
|
||||
}
|
||||
|
|
13
js/util.js
13
js/util.js
|
@ -51,9 +51,7 @@ export function bytify(ary) {
|
|||
}
|
||||
|
||||
export function debug() {
|
||||
if (typeof console != 'undefined' && 'log' in console) {
|
||||
console.log.apply(console, arguments);
|
||||
}
|
||||
console.log.apply(console, arguments);
|
||||
}
|
||||
|
||||
export function toHex(v, n) {
|
||||
|
@ -112,3 +110,12 @@ export function keys(obj) {
|
|||
export function each(obj, fn) {
|
||||
keys(obj).forEach(fn);
|
||||
}
|
||||
|
||||
export function numToString(num) {
|
||||
let result = '';
|
||||
for (let idx = 0; idx < 4; idx++) {
|
||||
result += String.fromCharCode(num & 0xff);
|
||||
num >>= 8;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -4115,6 +4115,46 @@
|
|||
"flat-cache": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"file-loader": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-5.0.2.tgz",
|
||||
"integrity": "sha512-QMiQ+WBkGLejKe81HU8SZ9PovsU/5uaLo0JdTCEXOYv7i7jfAjHZi1tcwp9tSASJPOmmHZtbdCervFmXMH/Dcg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loader-utils": "^1.2.3",
|
||||
"schema-utils": "^2.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.10.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
|
||||
"integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^2.0.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"ajv-keywords": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz",
|
||||
"integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==",
|
||||
"dev": true
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.5.0.tgz",
|
||||
"integrity": "sha512-32ISrwW2scPXHUSusP8qMg5dLUawKkyV+/qIEV9JdXKx+rsM6mi8vZY8khg2M69Qom16rtroWXD3Ybtiws38gQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "^6.10.2",
|
||||
"ajv-keywords": "^3.4.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"file-uri-to-path": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
|
||||
|
@ -8940,6 +8980,46 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"raw-loader": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.0.tgz",
|
||||
"integrity": "sha512-iINUOYvl1cGEmfoaLjnZXt4bKfT2LJnZZib5N/LLyAphC+Dd11vNP9CNVb38j+SAJpFI1uo8j9frmih53ASy7Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loader-utils": "^1.2.3",
|
||||
"schema-utils": "^2.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": {
|
||||
"version": "6.10.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
|
||||
"integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^2.0.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
"json-schema-traverse": "^0.4.1",
|
||||
"uri-js": "^4.2.2"
|
||||
}
|
||||
},
|
||||
"ajv-keywords": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz",
|
||||
"integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==",
|
||||
"dev": true
|
||||
},
|
||||
"schema-utils": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.5.0.tgz",
|
||||
"integrity": "sha512-32ISrwW2scPXHUSusP8qMg5dLUawKkyV+/qIEV9JdXKx+rsM6mi8vZY8khg2M69Qom16rtroWXD3Ybtiws38gQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "^6.10.2",
|
||||
"ajv-keywords": "^3.4.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-is": {
|
||||
"version": "16.8.6",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz",
|
||||
|
|
|
@ -29,8 +29,10 @@
|
|||
"ajv": "^6.9.2",
|
||||
"babel-jest": "^24.5.0",
|
||||
"eslint": "^5.16.0",
|
||||
"file-loader": "^5.0.2",
|
||||
"handlebars": "^4.5.3",
|
||||
"jest": "^24.8.0",
|
||||
"raw-loader": "^4.0.0",
|
||||
"webpack": "^4.29.6",
|
||||
"webpack-cli": "^3.2.3",
|
||||
"webpack-dev-server": "^3.2.1"
|
||||
|
|
|
@ -17,5 +17,25 @@ module.exports =
|
|||
compress: true,
|
||||
publicPath: '/dist/',
|
||||
watchContentBase: true
|
||||
}
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.2mg$/i,
|
||||
use: [
|
||||
{
|
||||
loader: 'file-loader',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.rom$/i,
|
||||
use: [
|
||||
{
|
||||
loader: 'raw-loader',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue