Lazy load ROMs (#81)

* Switch modules to `esnext` to allow `webpack` to see import statements
* Pass rom names into Apple2 class
* Move ROMs into `system` and `character` directories to allow webpack bundle appropriate ROMs.
* Wait for ROMs to load before completing initialization.
This commit is contained in:
Will Scullin 2021-06-13 17:06:16 -07:00 committed by GitHub
parent af57378852
commit 8087294456
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 199 additions and 162 deletions

View File

@ -25,6 +25,7 @@
"always" "always"
], ],
"no-use-before-define": "off", "no-use-before-define": "off",
"@typescript-eslint/no-floating-promises": "error",
"@typescript-eslint/no-use-before-define": [ "@typescript-eslint/no-use-before-define": [
"error", "error",
{ {
@ -59,7 +60,8 @@
"es6": true "es6": true
}, },
"parserOptions": { "parserOptions": {
"sourceType": "module" "sourceType": "module",
"project": "./tsconfig.json"
}, },
"extends": "eslint:recommended", "extends": "eslint:recommended",
"overrides": [ "overrides": [
@ -109,5 +111,6 @@
"commonjs": true "commonjs": true
} }
} }
] ],
"ignorePatterns": ["coverage/**/*"]
} }

View File

@ -309,6 +309,6 @@
</filter> </filter>
</svg> </svg>
<script src="dist/main2.js"></script> <script src="dist/main2.bundle.js"></script>
</body> </body>
</html> </html>

View File

@ -29,10 +29,6 @@
<link rel="stylesheet" type="text/css" href="css/apple2.css" /> <link rel="stylesheet" type="text/css" href="css/apple2.css" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.2/css/all.css" /> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.2/css/all.css" />
<script>
window.e = true
</script>
<!-- Disk Index --> <!-- Disk Index -->
<script type="text/javascript" src="json/disks/index.js"></script> <script type="text/javascript" src="json/disks/index.js"></script>
@ -318,6 +314,6 @@
</filter> </filter>
</svg> </svg>
<script src="dist/main2e.js"></script> <script src="dist/main2e.bundle.js"></script>
</body> </body>
</html> </html>

View File

@ -28,11 +28,11 @@ import { Restorable, rom } from './types';
import { processGamepad } from './ui/gamepad'; import { processGamepad } from './ui/gamepad';
export interface Apple2Options { export interface Apple2Options {
characterRom: rom, characterRom: string;
enhanced: boolean, enhanced: boolean,
e: boolean, e: boolean,
gl: boolean, gl: boolean,
rom: ROM, rom: string,
canvas: HTMLCanvasElement, canvas: HTMLCanvasElement,
tick: () => void, tick: () => void,
} }
@ -68,6 +68,8 @@ export class Apple2 implements Restorable<State>, DebuggerContainer {
private io: Apple2IO; private io: Apple2IO;
private mmu: MMU | undefined; private mmu: MMU | undefined;
private ram: [RAM, RAM, RAM] | undefined; private ram: [RAM, RAM, RAM] | undefined;
private characterRom: rom;
private rom: ROM;
private tick: () => void; private tick: () => void;
@ -76,22 +78,41 @@ export class Apple2 implements Restorable<State>, DebuggerContainer {
renderedFrames: 0 renderedFrames: 0
}; };
public ready: Promise<void>
constructor(options: Apple2Options) { constructor(options: Apple2Options) {
this.ready = this.init(options);
}
async init(options: Apple2Options) {
const romImportPromise = import(`./roms/system/${options.rom}`);
const characterRomImportPromise = import(`./roms/character/${options.characterRom}`);
const LoresPage = options.gl ? LoresPageGL : LoresPage2D; const LoresPage = options.gl ? LoresPageGL : LoresPage2D;
const HiresPage = options.gl ? HiresPageGL : HiresPage2D; const HiresPage = options.gl ? HiresPageGL : HiresPage2D;
const VideoModes = options.gl ? VideoModesGL : VideoModes2D; const VideoModes = options.gl ? VideoModesGL : VideoModes2D;
this.cpu = new CPU6502({ '65C02': options.enhanced }); this.cpu = new CPU6502({ '65C02': options.enhanced });
this.vm = new VideoModes(options.canvas, options.e); this.vm = new VideoModes(options.canvas, options.e);
this.gr = new LoresPage(this.vm, 1, options.characterRom, options.e);
this.gr2 = new LoresPage(this.vm, 2, options.characterRom, options.e); const [{ default: Apple2ROM }, { default: characterRom }] = await Promise.all([
romImportPromise,
characterRomImportPromise,
this.vm.ready,
]);
this.rom = new Apple2ROM();
this.characterRom = characterRom;
this.gr = new LoresPage(this.vm, 1, this.characterRom, options.e);
this.gr2 = new LoresPage(this.vm, 2, this.characterRom, options.e);
this.hgr = new HiresPage(this.vm, 1); this.hgr = new HiresPage(this.vm, 1);
this.hgr2 = new HiresPage(this.vm, 2); this.hgr2 = new HiresPage(this.vm, 2);
this.io = new Apple2IO(this.cpu, this.vm); this.io = new Apple2IO(this.cpu, this.vm);
this.tick = options.tick; this.tick = options.tick;
if (options.e) { if (options.e) {
this.mmu = new MMU(this.cpu, this.vm, this.gr, this.gr2, this.hgr, this.hgr2, this.io, options.rom); this.mmu = new MMU(this.cpu, this.vm, this.gr, this.gr2, this.hgr, this.hgr2, this.io, this.rom);
this.cpu.addPageHandler(this.mmu); this.cpu.addPageHandler(this.mmu);
} else { } else {
this.ram = [ this.ram = [
@ -108,7 +129,7 @@ export class Apple2 implements Restorable<State>, DebuggerContainer {
this.cpu.addPageHandler(this.hgr2); this.cpu.addPageHandler(this.hgr2);
this.cpu.addPageHandler(this.ram[2]); this.cpu.addPageHandler(this.ram[2]);
this.cpu.addPageHandler(this.io); this.cpu.addPageHandler(this.io);
this.cpu.addPageHandler(options.rom); this.cpu.addPageHandler(this.rom);
} }
} }
@ -235,6 +256,9 @@ export class Apple2 implements Restorable<State>, DebuggerContainer {
return this.mmu; return this.mmu;
} }
getROM() {
return this.rom;
}
getVideoModes() { getVideoModes() {
return this.vm; return this.vm;

View File

@ -11,49 +11,43 @@ import SmartPort from './cards/smartport';
import Thunderclock from './cards/thunderclock'; import Thunderclock from './cards/thunderclock';
import VideoTerm from './cards/videoterm'; import VideoTerm from './cards/videoterm';
import apple2_charset from './roms/apple2_char';
import apple2j_charset from './roms/apple2j_char';
import apple2lc_charset from './roms/apple2lc_char';
import pigfont_charset from './roms/pigfont_char';
import Apple2ROM from './roms/fpbasic';
import Apple2jROM from './roms/apple2j';
import IntBASIC from './roms/intbasic';
import OriginalROM from './roms/original';
import { Apple2 } from './apple2'; import { Apple2 } from './apple2';
const prefs = new Prefs(); const prefs = new Prefs();
const romVersion = prefs.readPref('computer_type2'); const romVersion = prefs.readPref('computer_type2');
let rom; let rom: string;
let characterRom = apple2_charset; let characterRom: string;
let sectors = 16; let sectors = 16;
switch (romVersion) { switch (romVersion) {
case 'apple2': case 'apple2':
rom = new IntBASIC(); rom = 'intbasic';
characterRom = 'apple2_char';
break; break;
case 'apple213': case 'apple213':
rom = new IntBASIC(); rom = 'intbasic';
characterRom = 'apple2_char';
sectors = 13; sectors = 13;
break; break;
case 'original': case 'original':
rom = new OriginalROM(); rom = 'original';
characterRom = 'apple2_char';
break; break;
case 'apple2jplus': case 'apple2jplus':
rom = new Apple2jROM(); rom = 'apple2j';
characterRom = apple2j_charset; characterRom = 'apple2j_char';
break; break;
case 'apple2pig': case 'apple2pig':
rom = new Apple2ROM(); rom = 'fpbasic';
characterRom = pigfont_charset; characterRom = 'pigfont_char';
break; break;
case 'apple2lc': case 'apple2lc':
rom = new Apple2ROM(); rom = 'fpbasic';
characterRom = apple2lc_charset; characterRom = 'apple2lc_char';
break; break;
default: default:
rom = new Apple2ROM(); rom = 'fpbasic';
characterRom = 'apple2_char';
} }
const options = { const options = {
@ -67,27 +61,29 @@ const options = {
}; };
export const apple2 = new Apple2(options); export const apple2 = new Apple2(options);
const cpu = apple2.getCPU(); apple2.ready.then(() => {
const io = apple2.getIO(); const cpu = apple2.getCPU();
const io = apple2.getIO();
const printer = new Printer('#printer-modal .paper'); const printer = new Printer('#printer-modal .paper');
const lc = new LanguageCard(rom); const lc = new LanguageCard(apple2.getROM());
const parallel = new Parallel(printer); const parallel = new Parallel(printer);
const videoTerm = new VideoTerm(); const videoTerm = new VideoTerm();
const slinky = new RAMFactor(1024 * 1024); const slinky = new RAMFactor(1024 * 1024);
const disk2 = new DiskII(io, driveLights, sectors); const disk2 = new DiskII(io, driveLights, sectors);
const clock = new Thunderclock(); const clock = new Thunderclock();
const smartport = new SmartPort(cpu, { block: true }); const smartport = new SmartPort(cpu, { block: true });
io.setSlot(0, lc); io.setSlot(0, lc);
io.setSlot(1, parallel); io.setSlot(1, parallel);
io.setSlot(2, slinky); io.setSlot(2, slinky);
io.setSlot(4, clock); io.setSlot(4, clock);
io.setSlot(3, videoTerm); io.setSlot(3, videoTerm);
io.setSlot(6, disk2); io.setSlot(6, disk2);
io.setSlot(7, smartport); io.setSlot(7, smartport);
cpu.addPageHandler(lc); cpu.addPageHandler(lc);
initUI(apple2, disk2, smartport, printer, false); initUI(apple2, disk2, smartport, printer, false);
}).catch(console.error);

View File

@ -9,33 +9,27 @@ import RAMFactor from './cards/ramfactor';
import SmartPort from './cards/smartport'; import SmartPort from './cards/smartport';
import Thunderclock from './cards/thunderclock'; import Thunderclock from './cards/thunderclock';
import apple2e_charset from './roms/apple2e_char';
import apple2enh_charset from './roms/apple2enh_char';
import rmfont_charset from './roms/rmfont_char';
import Apple2eROM from './roms/apple2e';
import Apple2eEnhancedROM from './roms/apple2enh';
import { Apple2 } from './apple2'; import { Apple2 } from './apple2';
const prefs = new Prefs(); const prefs = new Prefs();
const romVersion = prefs.readPref('computer_type2e'); const romVersion = prefs.readPref('computer_type2e');
let enhanced = false; let enhanced = false;
let rom; let rom: string;
let characterRom = apple2e_charset; let characterRom: string;
switch (romVersion) { switch (romVersion) {
case 'apple2e': case 'apple2e':
rom = new Apple2eROM(); rom = 'apple2e';
characterRom = 'apple2e_char';
break; break;
case 'apple2rm': case 'apple2rm':
rom = new Apple2eEnhancedROM(); rom = 'apple2e';
characterRom = rmfont_charset; characterRom = 'rmfont_char';
enhanced = true; enhanced = true;
break; break;
default: default:
rom = new Apple2eEnhancedROM(); rom = 'apple2enh';
characterRom = apple2enh_charset; characterRom = 'apple2enh_char';
enhanced = true; enhanced = true;
} }
@ -50,21 +44,23 @@ const options = {
}; };
export const apple2 = new Apple2(options); export const apple2 = new Apple2(options);
const io = apple2.getIO(); apple2.ready.then(() => {
const cpu = apple2.getCPU(); const io = apple2.getIO();
const cpu = apple2.getCPU();
const printer = new Printer('#printer-modal .paper'); const printer = new Printer('#printer-modal .paper');
const parallel = new Parallel(printer); const parallel = new Parallel(printer);
const slinky = new RAMFactor(1024 * 1024); const slinky = new RAMFactor(1024 * 1024);
const disk2 = new DiskII(io, driveLights); const disk2 = new DiskII(io, driveLights);
const clock = new Thunderclock(); const clock = new Thunderclock();
const smartport = new SmartPort(cpu, { block: !enhanced }); const smartport = new SmartPort(cpu, { block: !enhanced });
io.setSlot(1, parallel); io.setSlot(1, parallel);
io.setSlot(2, slinky); io.setSlot(2, slinky);
io.setSlot(5, clock); io.setSlot(5, clock);
io.setSlot(6, disk2); io.setSlot(6, disk2);
io.setSlot(7, smartport); io.setSlot(7, smartport);
initUI(apple2, disk2, smartport, printer, options.e); initUI(apple2, disk2, smartport, printer, options.e);
}).catch(console.error);

View File

@ -1,4 +1,4 @@
import { ReadonlyUint8Array } from '../types'; import { ReadonlyUint8Array } from '../../types';
const apple2_charset = new Uint8Array([ const apple2_charset = new Uint8Array([
0x00,0x1c,0x22,0x2a,0x2e,0x2c,0x20,0x1e, 0x00,0x1c,0x22,0x2a,0x2e,0x2c,0x20,0x1e,

View File

@ -1,4 +1,4 @@
import { ReadonlyUint8Array } from '../types'; import { ReadonlyUint8Array } from '../../types';
/* exported apple2e_charset */ /* exported apple2e_charset */

View File

@ -1,4 +1,4 @@
import { ReadonlyUint8Array } from '../types'; import { ReadonlyUint8Array } from '../../types';
const apple2enh_charset = new Uint8Array([ const apple2enh_charset = new Uint8Array([
0x1c,0x22,0x2a,0x3a,0x1a,0x02,0x3c,0x00, 0x1c,0x22,0x2a,0x3a,0x1a,0x02,0x3c,0x00,

View File

@ -1,4 +1,4 @@
import { ReadonlyUint8Array } from '../types'; import { ReadonlyUint8Array } from '../../types';
const apple2j_charset = new Uint8Array([ const apple2j_charset = new Uint8Array([
0xff,0xef,0xe1,0xed,0xd5,0xfb,0xf7,0xef, 0xff,0xef,0xe1,0xed,0xd5,0xfb,0xf7,0xef,

View File

@ -1,4 +1,4 @@
import { ReadonlyUint8Array } from '../types'; import { ReadonlyUint8Array } from '../../types';
const apple2lc_charset = new Uint8Array([ const apple2lc_charset = new Uint8Array([
0x1c,0x22,0x2a,0x2a,0x2c,0x20,0x1e,0x00, 0x1c,0x22,0x2a,0x2a,0x2c,0x20,0x1e,0x00,

View File

@ -1,4 +1,4 @@
import { ReadonlyUint8Array } from '../types'; import { ReadonlyUint8Array } from '../../types';
const pigfont_charset = new Uint8Array([ const pigfont_charset = new Uint8Array([
0x00,0x1c,0x22,0x2a,0x2e,0x20,0x1e,0x00, 0x00,0x1c,0x22,0x2a,0x2e,0x20,0x1e,0x00,

View File

@ -1,4 +1,4 @@
import { ReadonlyUint8Array } from '../types'; import { ReadonlyUint8Array } from '../../types';
const rmfont_charset = new Uint8Array([ const rmfont_charset = new Uint8Array([
0x3c,0x42,0x59,0x55,0x55,0x39,0x02,0x3c, 0x3c,0x42,0x59,0x55,0x55,0x39,0x02,0x3c,

View File

@ -1,5 +1,5 @@
import { ReadonlyUint8Array } from '../types'; import { ReadonlyUint8Array } from '../../types';
import ROM from './rom'; import ROM from '../rom';
const rom = new Uint8Array([ const rom = new Uint8Array([
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

View File

@ -1,5 +1,5 @@
import { ReadonlyUint8Array } from '../types'; import { ReadonlyUint8Array } from '../../types';
import ROM from './rom'; import ROM from '../rom';
const rom = new Uint8Array([ const rom = new Uint8Array([
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

View File

@ -1,5 +1,5 @@
import { ReadonlyUint8Array } from '../types'; import { ReadonlyUint8Array } from '../../types';
import ROM from './rom'; import ROM from '../rom';
const rom = new Uint8Array([ const rom = new Uint8Array([
0x6f,0xd8,0x65,0xd7,0xf8,0xdc,0x94,0xd9, 0x6f,0xd8,0x65,0xd7,0xf8,0xdc,0x94,0xd9,

View File

@ -1,5 +1,5 @@
import { ReadonlyUint8Array } from '../types'; import { ReadonlyUint8Array } from '../../types';
import ROM from './rom'; import ROM from '../rom';
const rom = new Uint8Array([ const rom = new Uint8Array([
0x6f, 0xd8, 0x65, 0xd7, 0xf8, 0xdc, 0x94, 0xd9, 0x6f, 0xd8, 0x65, 0xd7, 0xf8, 0xdc, 0x94, 0xd9,

View File

@ -1,5 +1,5 @@
import { ReadonlyUint8Array } from '../types'; import { ReadonlyUint8Array } from '../../types';
import ROM from './rom'; import ROM from '../rom';
const rom = new Uint8Array([ const rom = new Uint8Array([
0xa9, 0x20, 0x8d, 0x26, 0x03, 0xad, 0x57, 0xc0, 0xa9, 0x20, 0x8d, 0x26, 0x03, 0xad, 0x57, 0xc0,

View File

@ -1,5 +1,5 @@
import { ReadonlyUint8Array } from '../types'; import { ReadonlyUint8Array } from '../../types';
import ROM from './rom'; import ROM from '../rom';
const rom = new Uint8Array([ const rom = new Uint8Array([
0xa9, 0x20, 0x8d, 0x26, 0x03, 0xad, 0x57, 0xc0, 0xa9, 0x20, 0x8d, 0x26, 0x03, 0xad, 0x57, 0xc0,

View File

@ -49,7 +49,14 @@ type DiskCollection = {
[name: string]: DiskDescriptor[] [name: string]: DiskDescriptor[]
}; };
const KNOWN_FILE_TYPES = [...DISK_FORMATS, ...TAPE_TYPES] as readonly string[]; const CIDERPRESS_EXTENSION = /#([0-9a-f]{2})([0-9a-f]{4})$/i;
const BIN_TYPES = ['bin'];
const KNOWN_FILE_TYPES = [
...DISK_FORMATS,
...TAPE_TYPES,
...BIN_TYPES,
] as readonly string[];
const disk_categories: DiskCollection = { 'Local Saves': [] }; const disk_categories: DiskCollection = { 'Local Saves': [] };
const disk_sets: DiskCollection = {}; const disk_sets: DiskCollection = {};
@ -73,6 +80,7 @@ let system: System;
let keyboard: KeyBoard; let keyboard: KeyBoard;
let io: Apple2IO; let io: Apple2IO;
let _currentDrive: DriveNumber = 1; let _currentDrive: DriveNumber = 1;
let _e: boolean;
export const driveLights = new DriveLights(); export const driveLights = new DriveLights();
@ -84,6 +92,7 @@ export function dumpAppleSoftProgram() {
export function compileAppleSoftProgram(program: string) { export function compileAppleSoftProgram(program: string) {
const compiler = new ApplesoftCompiler(cpu); const compiler = new ApplesoftCompiler(cpu);
compiler.compile(program); compiler.compile(program);
dumpAppleSoftProgram();
} }
export function openLoad(driveString: string, event: MouseEvent) { export function openLoad(driveString: string, event: MouseEvent) {
@ -204,9 +213,9 @@ function loadingStop() {
MicroModal.close('loading-modal'); MicroModal.close('loading-modal');
if (!paused) { if (!paused) {
vm.ready.then(() => { _apple2.ready.then(() => {
_apple2.run(); _apple2.run();
}); }).catch(console.error);
} }
} }
@ -297,11 +306,8 @@ export function doDelete(name: string) {
} }
} }
const CIDERPRESS_EXTENSION = /#([0-9a-f]{2})([0-9a-f]{4})$/i;
const BIN_TYPES = ['bin'];
interface LoadOptions { interface LoadOptions {
address: word, address?: word,
runOnLoad?: boolean, runOnLoad?: boolean,
} }
@ -318,7 +324,8 @@ function doLoadLocal(drive: DriveNumber, file: File, options: Partial<LoadOption
} else if (includes(TAPE_TYPES, ext)) { } else if (includes(TAPE_TYPES, ext)) {
tape.doLoadLocalTape(file); tape.doLoadLocalTape(file);
} else if (BIN_TYPES.includes(ext) || type === '06' || options.address) { } else if (BIN_TYPES.includes(ext) || type === '06' || options.address) {
doLoadBinary(file, { address: parseInt(aux || '2000', 16), ...options }); const address = aux !== undefined ? parseInt(aux, 16) : undefined;
doLoadBinary(file, { address, ...options });
} else { } else {
const addressInput = document.querySelector<HTMLInputElement>('#local_file_address'); const addressInput = document.querySelector<HTMLInputElement>('#local_file_address');
const addressStr = addressInput?.value; const addressStr = addressInput?.value;
@ -342,6 +349,7 @@ function doLoadBinary(file: File, options: LoadOptions) {
fileReader.onload = function () { fileReader.onload = function () {
const result = this.result as ArrayBuffer; const result = this.result as ArrayBuffer;
let { address } = options; let { address } = options;
address = address ?? 0x2000;
const bytes = new Uint8Array(result); const bytes = new Uint8Array(result);
for (let idx = 0; idx < result.byteLength; idx++) { for (let idx = 0; idx < result.byteLength; idx++) {
cpu.write(address >> 8, address & 0xff, bytes[idx]); cpu.write(address >> 8, address & 0xff, bytes[idx]);
@ -349,7 +357,7 @@ function doLoadBinary(file: File, options: LoadOptions) {
} }
if (options.runOnLoad) { if (options.runOnLoad) {
cpu.reset(); cpu.reset();
cpu.setPC(options.address); cpu.setPC(address);
} }
loadingStop(); loadingStop();
}; };
@ -437,9 +445,10 @@ export function doLoadHTTP(drive: DriveNumber, url?: string) {
initGamepad(); initGamepad();
} }
} else { } else {
if (includes(DISK_FORMATS, ext) if (includes(DISK_FORMATS, ext)) {
&& _disk2.setBinary(drive, name, ext, data)) { if (_disk2.setBinary(drive, name, ext, data)) {
initGamepad(); initGamepad();
}
} else { } else {
throw new Error(`Extension ${ext} not recognized.`); throw new Error(`Extension ${ext} not recognized.`);
} }
@ -678,38 +687,40 @@ declare global {
} }
} }
let oldCat = ''; function buildDiskIndex() {
let option; let oldCat = '';
for (let idx = 0; idx < window.disk_index.length; idx++) { let option;
const file = window.disk_index[idx]; for (let idx = 0; idx < window.disk_index.length; idx++) {
const cat = file.category; const file = window.disk_index[idx];
const name = file.name; const cat = file.category;
const disk = file.disk; const name = file.name;
if (file.e && !window.e) { const disk = file.disk;
continue; if (file.e && !_e) {
} continue;
if (cat != oldCat) {
option = document.createElement('option');
option.value = cat;
option.innerText = cat;
categorySelect.append(option);
disk_categories[cat] = [];
oldCat = cat;
}
disk_categories[cat].push(file);
if (disk) {
if (!disk_sets[name]) {
disk_sets[name] = [];
} }
disk_sets[name].push(file); if (cat != oldCat) {
} option = document.createElement('option');
} option.value = cat;
option = document.createElement('option'); option.innerText = cat;
option.innerText = 'Local Saves'; categorySelect.append(option);
categorySelect.append(option);
updateLocalStorage(); disk_categories[cat] = [];
oldCat = cat;
}
disk_categories[cat].push(file);
if (disk) {
if (!disk_sets[name]) {
disk_sets[name] = [];
}
disk_sets[name].push(file);
}
}
option = document.createElement('option');
option.innerText = 'Local Saves';
categorySelect.append(option);
updateLocalStorage();
}
/** /**
* Processes the URL fragment. It is expected to be of the form: * Processes the URL fragment. It is expected to be of the form:
@ -751,9 +762,9 @@ export function updateUI() {
export function pauseRun() { export function pauseRun() {
const label = document.querySelector<HTMLElement>('#pause-run i')!; const label = document.querySelector<HTMLElement>('#pause-run i')!;
if (paused) { if (paused) {
vm.ready.then(() => { _apple2.ready.then(() => {
_apple2.run(); _apple2.run();
}); }).catch(console.error);
label.classList.remove('fa-play'); label.classList.remove('fa-play');
label.classList.add('fa-pause'); label.classList.add('fa-pause');
} else { } else {
@ -805,6 +816,7 @@ function onLoaded(apple2: Apple2, disk2: DiskII, smartPort: SmartPort, printer:
_disk2 = disk2; _disk2 = disk2;
_smartPort = smartPort; _smartPort = smartPort;
_printer = printer; _printer = printer;
_e = e;
system = new System(io, e); system = new System(io, e);
optionsModal.addOptions(system); optionsModal.addOptions(system);
@ -835,6 +847,8 @@ function onLoaded(apple2: Apple2, disk2: DiskII, smartPort: SmartPort, printer:
} }
}); });
buildDiskIndex();
/* /*
* Input Handling * Input Handling
*/ */
@ -865,9 +879,9 @@ function onLoaded(apple2: Apple2, disk2: DiskII, smartPort: SmartPort, printer:
_apple2.stop(); _apple2.stop();
processHash(hash); processHash(hash);
} else { } else {
vm.ready.then(() => { _apple2.ready.then(() => {
_apple2.run(); _apple2.run();
}); }).catch(console.error);
} }
document.querySelector<HTMLInputElement>('#local_file')?.addEventListener( document.querySelector<HTMLInputElement>('#local_file')?.addEventListener(

View File

@ -89,15 +89,20 @@ export class Audio implements OptionHandler {
autoStart = () => { autoStart = () => {
if (this.audioContext && !this.started) { if (this.audioContext && !this.started) {
this.samples = []; this.samples = [];
this.audioContext.resume(); this.audioContext.resume().then(() => {
this.started = true; this.started = true;
}).catch((error) => {
console.warn('audio not started', error);
});
} }
} }
start = () => { start = () => {
if (this.audioContext) { if (this.audioContext) {
this.samples = []; this.samples = [];
this.audioContext.resume(); this.audioContext.resume().catch((error) => {
console.warn('audio not resumed', error);
});
} }
} }

View File

@ -36,7 +36,7 @@ export default class Tape {
fileReader.onload = (ev: ProgressEvent) => { fileReader.onload = (ev: ProgressEvent) => {
const target: FileReader = ev.target as FileReader; const target: FileReader = ev.target as FileReader;
const result: ArrayBuffer = target.result as ArrayBuffer; const result: ArrayBuffer = target.result as ArrayBuffer;
context.decodeAudioData(result, (buffer) => { context.decodeAudioData(result).then((buffer) => {
const buf: TapeData = []; const buf: TapeData = [];
const data = buffer.getChannelData(0); const data = buffer.getChannelData(0);
let datum = data[0]; let datum = data[0];

View File

@ -2,7 +2,7 @@
import { VideoPage } from 'js/videomodes'; import { VideoPage } from 'js/videomodes';
import { LoresPage2D, HiresPage2D, VideoModes2D } from 'js/canvas'; import { LoresPage2D, HiresPage2D, VideoModes2D } from 'js/canvas';
import apple2enh_char from 'js/roms/apple2enh_char'; import apple2enh_char from 'js/roms/character/apple2enh_char';
import { createImageFromImageData } from 'test/util/image'; import { createImageFromImageData } from 'test/util/image';
function checkImageData(page: VideoPage) { function checkImageData(page: VideoPage) {

View File

@ -2,7 +2,7 @@
import { VideoPage } from 'js/videomodes'; import { VideoPage } from 'js/videomodes';
import { LoresPageGL, HiresPageGL, VideoModesGL } from 'js/gl'; import { LoresPageGL, HiresPageGL, VideoModesGL } from 'js/gl';
import apple2enh_char from 'js/roms/apple2enh_char'; import apple2enh_char from 'js/roms/character/apple2enh_char';
import { createImageFromImageData } from 'test/util/image'; import { createImageFromImageData } from 'test/util/image';
function checkImageData(page: VideoPage) { function checkImageData(page: VideoPage) {

View File

@ -1,10 +1,10 @@
import type ROM from '../../js/roms/rom'; import type ROM from '../../js/roms/rom';
import OriginalROM from '../../js/roms/original'; import OriginalROM from '../../js/roms/system/original';
import IntegerROM from '../../js/roms/intbasic'; import IntegerROM from '../../js/roms/system/intbasic';
import FPBasicROM from '../../js/roms/fpbasic'; import FPBasicROM from '../../js/roms/system/fpbasic';
import Apple2eROM from '../../js/roms/apple2e'; import Apple2eROM from '../../js/roms/system/apple2e';
import Apple2enhROM from '../../js/roms/apple2enh'; import Apple2enhROM from '../../js/roms/system/apple2enh';
import Apple2jROM from '../../js/roms/apple2j'; import Apple2jROM from '../../js/roms/system/apple2j';
const roms: { [name: string]: { new(): ROM } } = { const roms: { [name: string]: { new(): ROM } } = {
'original': OriginalROM, 'original': OriginalROM,

View File

@ -1,6 +1,6 @@
{ {
"compilerOptions": { "compilerOptions": {
"module": "commonjs", "module": "esnext",
"esModuleInterop": true, "esModuleInterop": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"target": "es6", "target": "es6",
@ -30,5 +30,6 @@
"include": [ "include": [
"js/**/*", "js/**/*",
"test/**/*", "test/**/*",
"*.config.js"
] ]
} }

View File

@ -10,6 +10,8 @@ module.exports =
}, },
output: { output: {
path: path.resolve('dist/'), path: path.resolve('dist/'),
filename: '[name].bundle.js',
chunkFilename: '[name].bundle.js',
library: { library: {
name: 'Apple2', name: 'Apple2',
type: 'umd', type: 'umd',
@ -54,7 +56,7 @@ module.exports =
}, },
], ],
exclude: /node_modules/, exclude: /node_modules/,
} },
], ],
}, },
resolve: { resolve: {