Lint, other build fixes

This commit is contained in:
Will Scullin 2023-08-06 07:32:11 -07:00
parent 3f6cfd67b7
commit ca3f255a87
26 changed files with 5545 additions and 3297 deletions

View File

@ -8,7 +8,11 @@ insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
[*.js] [*.js]
indent_size = 4 indent_size = 2
[*.ts]
indent_size = 2
quote_type = single
[*.html] [*.html]
indent_size = 2 indent_size = 2

View File

@ -1,20 +1,60 @@
{ {
// Global
"root": true,
"parser": "@typescript-eslint/parser",
"extends": [
"eslint:recommended",
"plugin:jest/recommended"
],
"globals": {
"tapes": "writable"
},
"rules": { "rules": {
"indent": [ "indent": [
2, "error",
4 4,
{
"SwitchCase": 1
}
], ],
"quotes": [ "quotes": [
2, "error",
"single" "single",
{ "avoidEscape": true }
], ],
"linebreak-style": [ "linebreak-style": [
2, "error",
"unix" "unix"
], ],
"semi": [ "eqeqeq": [
2, "error",
"always" "smart"
],
"prefer-const": [
"error"
],
"no-var": "error",
"no-use-before-define": "off",
"no-console": [
"error",
{
"allow": [
"info",
"warn",
"error"
]
}
],
// Jest configuration
"jest/expect-expect": [
"error",
{
"assertFunctionNames": [
"expect*",
"checkImageData",
"testCode"
]
}
] ]
}, },
"env": { "env": {
@ -22,16 +62,109 @@
"browser": true, "browser": true,
"es6": true "es6": true
}, },
"globals": {
"tapes": true
},
"parserOptions": {
"sourceType": "module"
},
"extends": "eslint:recommended",
"overrides": [ "overrides": [
// All overrides matching a file are applied in-order, with the last
// taking precedence.
//
// TypeScript/TSX-specific configuration
{ {
"files": [ "bin/*", "babel.config.js", "webpack.config.js" ], "files": [
"*.ts",
"*.tsx"
],
"plugins": [
"@typescript-eslint/eslint-plugin"
],
"extends": [
"plugin:@typescript-eslint/recommended",
"plugin:@typescript-eslint/recommended-requiring-type-checking"
],
"rules": {
"indent": [
"error",
2,
{
"SwitchCase": 1
}
],
// recommended is just "warn"
"@typescript-eslint/no-explicit-any": "error",
// enforce semicolons at ends of statements
"semi": "off",
"@typescript-eslint/semi": [
"error",
"always"
],
// enforce semicolons to separate members
"@typescript-eslint/member-delimiter-style": [
"error",
{
"multiline": {
"delimiter": "semi",
"requireLast": true
},
"singleline": {
"delimiter": "semi",
"requireLast": false
}
}
],
// definitions must come before uses for variables
"@typescript-eslint/no-use-before-define": [
"error",
{
"functions": false,
"classes": false
}
],
// no used variables
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
"argsIgnorePattern": "^_"
}
],
// no redeclaration of classes, members or variables
"no-redeclare": "off",
"@typescript-eslint/no-redeclare": [
"error"
],
// allow empty interface definitions and empty extends
"@typescript-eslint/no-empty-interface": "off",
// allow explicit type declaration
"@typescript-eslint/no-inferrable-types": "off",
// allow some non-string types in templates
"@typescript-eslint/restrict-template-expressions": [
"error",
{
"allowNumber": true,
"allowBoolean": true
}
]
},
"parserOptions": {
"sourceType": "module",
"project": "./tsconfig.json"
}
},
// UI elements
{
"files": [
"js/ui/**.ts"
],
"rules": {
// allow non-null assertions since these classes reference the DOM
"@typescript-eslint/no-non-null-assertion": "off"
}
},
// JS Node configuration
{
"files": [
"bin/*",
"babel.config.js",
"webpack.config.js"
],
"rules": { "rules": {
"no-console": 0 "no-console": 0
}, },
@ -40,17 +173,49 @@
"jquery": false, "jquery": false,
"browser": false "browser": false
} }
}, { },
"files": [ "test/*"], // Test configuration
{
"files": [
"test/**/*"
],
"env": { "env": {
"node": true, "jest": true,
"jest": true "jasmine": true,
"node": true
},
"rules": {
"no-console": 0
} }
}, { },
"files": [ "js/entry1.js"], // Entry point configuration
{
"files": [
"js/entry2.ts",
"js/entry2e.ts",
"jest.config.js"
],
"env": { "env": {
"commonjs": true "commonjs": true
} }
},
// Worker configuration
{
"files": [
"workers/*"
],
"parserOptions": {
"project": "workers/tsconfig.json"
}
} }
] ],
"ignorePatterns": [
"coverage/**/*"
],
"settings": {
"react": {
"pragma": "h",
"version": "16"
}
}
} }

View File

@ -1,22 +1,35 @@
import MicroModal from "micromodal"; /* Copyright 2010-2023 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 Apple1IO from "./apple1io"; import MicroModal from 'micromodal';
import CPU6502 from "./cpu6502";
import Prefs from "./prefs";
import RAM from "./ram";
import { TextPage } from "./canvas1";
import { debug, hup } from "./util";
import Basic from "./roms/basic"; import Apple1IO from './apple1io';
import Bios from "./roms/bios"; import CPU6502 from './cpu6502';
import Krusader from "./roms/krusader"; import Prefs from './prefs';
import RAM from './ram';
import { TextPage } from './canvas1';
import { debug, hup } from './util';
import ACI from "./cards/aci"; import Basic from './roms/basic';
import Bios from './roms/bios';
import Krusader from './roms/krusader';
import { mapKeyEvent, KeyBoard } from "./ui/keyboard"; import ACI from './cards/aci';
import { address, byte } from "./types";
import { mapKeyEvent, KeyBoard } from './ui/keyboard';
import { address, byte } from './types';
// eslint-disable-next-line prefer-const
let DEBUG = false; let DEBUG = false;
// eslint-disable-next-line prefer-const
let TRACE = true; let TRACE = true;
const skidmarks: string[] = []; const skidmarks: string[] = [];
@ -32,22 +45,22 @@ const prefs = new Prefs();
let runTimer: ReturnType<typeof setInterval> | null = null; let runTimer: ReturnType<typeof setInterval> | null = null;
const cpu = new CPU6502(); const cpu = new CPU6502();
var krusader = window.location.hash == "#krusader"; const krusader = window.location.hash === '#krusader';
var raml, ramh, rom, aci: ACI, io: Apple1IO, text: TextPage, keyboard: KeyBoard; let ramh, rom;
// 32K base memory. Should be 0x0f for 4K, 0x1f for 8K, 0x3f for 16K // 32K base memory. Should be 0x0f for 4K, 0x1f for 8K, 0x3f for 16K
raml = new RAM(0x00, 0x7f); const raml = new RAM(0x00, 0x7f);
text = new TextPage(); const text = new TextPage();
text.init(); text.init();
aci = new ACI(cpu, { const aci = new ACI(cpu, {
progress: function (val) { progress: function (val) {
document.querySelector<HTMLElement>("#tape")!.style.width = document.querySelector<HTMLElement>('#tape')!.style.width =
val * 100 + "px"; val * 100 + 'px';
}, },
}); });
io = new Apple1IO(text); const io = new Apple1IO(text);
if (krusader) { if (krusader) {
ramh = null; ramh = null;
@ -57,7 +70,7 @@ if (krusader) {
ramh = new Basic(); ramh = new Basic();
rom = new Bios(); rom = new Bios();
} }
keyboard = new KeyBoard("#keyboard", cpu, io, text); const keyboard = new KeyBoard('#keyboard', cpu, io, text);
cpu.addPageHandler(raml); cpu.addPageHandler(raml);
if (ramh) { if (ramh) {
@ -68,7 +81,7 @@ cpu.addPageHandler(rom);
cpu.addPageHandler(aci); cpu.addPageHandler(aci);
cpu.addPageHandler(io); cpu.addPageHandler(io);
var showFPS = false; let showFPS = false;
interface Tape { interface Tape {
script: string; script: string;
@ -86,7 +99,7 @@ declare global {
//aci.setData([0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef]) //aci.setData([0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef])
//aci.setData(tapes['BASIC']); //aci.setData(tapes['BASIC']);
aci.setData(window.tapes["Microchess"].tracks); aci.setData(window.tapes['Microchess'].tracks);
// Audio Buffer Source // Audio Buffer Source
declare global { declare global {
@ -99,55 +112,61 @@ const AudioContext = window.AudioContext || window.webkitAudioContext;
const context = new AudioContext(); const context = new AudioContext();
export function doLoadLocal(files: FileList) { export function doLoadLocal(files: FileList) {
context.resume(); context
files = .resume()
files || document.querySelector<HTMLInputElement>("#local_file")!.files; .then(() => {
if (files.length == 1) { files =
var file = files[0]; files || document.querySelector<HTMLInputElement>('#local_file')!.files;
var fileReader = new FileReader(); if (files.length === 1) {
fileReader.onload = function (ev) { const file = files[0];
context.decodeAudioData( const fileReader = new FileReader();
ev.target!.result as ArrayBuffer, fileReader.onload = function (ev) {
function (buffer) { context
var buf = []; .decodeAudioData(
var data = buffer.getChannelData(0); ev.target!.result as ArrayBuffer,
var old = data[0] > 0.25; function (buffer) {
var last = 0; const buf = [];
for (var idx = 1; idx < data.length; idx++) { const data = buffer.getChannelData(0);
var current = data[idx] > 0.25; let old = data[0] > 0.25;
if (current != old) { let last = 0;
var delta = idx - last; for (let idx = 1; idx < data.length; idx++) {
buf.push(Math.floor((delta / buffer.sampleRate) * 1023000)); const current = data[idx] > 0.25;
old = current; if (current !== old) {
last = idx; const delta = idx - last;
} buf.push(Math.floor((delta / buffer.sampleRate) * 1023000));
} old = current;
aci.buffer = buf; last = idx;
MicroModal.close("local-modal"); }
}, }
function () { aci.buffer = buf;
window.alert("Unable to read tape file: " + file.name); MicroModal.close('local-modal');
} },
); function () {
}; window.alert('Unable to read tape file: ' + file.name);
fileReader.readAsArrayBuffer(file); }
} )
.catch(console.error);
};
fileReader.readAsArrayBuffer(file);
}
})
.catch(console.error);
} }
function updateKHz() { function updateKHz() {
let now = Date.now(); const now = Date.now();
let ms = now - startTime; const ms = now - startTime;
let cycles = cpu.getCycles(); const cycles = cpu.getCycles();
let delta: number; let delta: number;
if (showFPS) { if (showFPS) {
delta = renderedFrames - lastFrames; delta = renderedFrames - lastFrames;
var fps = Math.floor(delta / (ms / 1000)); const fps = Math.floor(delta / (ms / 1000));
document.querySelector("#khz")!.innerHTML = fps + "fps"; document.querySelector('#khz')!.innerHTML = fps + 'fps';
} else { } else {
delta = cycles - lastCycles; delta = cycles - lastCycles;
var khz = Math.floor(delta / ms); const khz = Math.floor(delta / ms);
document.querySelector("#khz")!.innerHTML = khz + "KHz"; document.querySelector('#khz')!.innerHTML = khz + 'KHz';
} }
startTime = now; startTime = now;
@ -155,9 +174,8 @@ function updateKHz() {
lastFrames = renderedFrames; lastFrames = renderedFrames;
} }
var loading = false; let throttling = true;
var throttling = true; let turbotape = false;
var turbotape = false;
export function toggleFPS() { export function toggleFPS() {
showFPS = !showFPS; showFPS = !showFPS;
@ -165,7 +183,7 @@ export function toggleFPS() {
export function toggleSpeed() { export function toggleSpeed() {
throttling = throttling =
document.querySelector<HTMLInputElement>("#speed_toggle")!.checked; document.querySelector<HTMLInputElement>('#speed_toggle')!.checked;
if (runTimer) { if (runTimer) {
run(); run();
} }
@ -188,17 +206,17 @@ function run(pc?: address) {
cpu.setPC(pc); cpu.setPC(pc);
} }
var ival = 30, let ival = 30;
step = 1023 * ival, let step = 1023 * ival;
stepMax = step; const stepMax = step;
if (!throttling) { if (!throttling) {
ival = 1; ival = 1;
} }
var now, let now;
last = Date.now(); let last = Date.now();
var runFn = function () { const runFn = function () {
now = Date.now(); now = Date.now();
renderedFrames++; renderedFrames++;
step = (now - last) * 1023; step = (now - last) * 1023;
@ -206,27 +224,25 @@ function run(pc?: address) {
if (step > stepMax) { if (step > stepMax) {
step = stepMax; step = stepMax;
} }
if (document.location.hash != hashtag) { if (document.location.hash !== hashtag) {
hashtag = document.location.hash; hashtag = document.location.hash;
} }
if (!loading) { if (DEBUG) {
if (DEBUG) { cpu.stepCyclesDebug(TRACE ? 1 : step, function () {
cpu.stepCyclesDebug(TRACE ? 1 : step, function () { const line = JSON.stringify(cpu.getState());
var line = JSON.stringify(cpu.getState()); if (TRACE) {
if (TRACE) { debug(line);
debug(line); } else {
} else { skidmarks.push(line);
skidmarks.push(line); if (skidmarks.length > 256) {
if (skidmarks.length > 256) { skidmarks.shift();
skidmarks.shift();
}
} }
}); }
} else { });
cpu.stepCycles(step); } else {
} cpu.stepCycles(step);
text.blit();
} }
text.blit();
if (!paused) { if (!paused) {
requestAnimationFrame(runFn); requestAnimationFrame(runFn);
} }
@ -255,7 +271,7 @@ declare global {
} }
} }
var _key: byte; let _key: byte;
function _keydown(evt: KeyboardEvent) { function _keydown(evt: KeyboardEvent) {
if (evt.keyCode === 112) { if (evt.keyCode === 112) {
cpu.reset(); cpu.reset();
@ -263,19 +279,19 @@ function _keydown(evt: KeyboardEvent) {
if (document.webkitIsFullScreen) { if (document.webkitIsFullScreen) {
document.webkitCancelFullScreen(); document.webkitCancelFullScreen();
} else { } else {
var elem = document.getElementById("display"); const elem = document.getElementById('display');
elem!.webkitRequestFullScreen(); elem!.webkitRequestFullScreen();
} }
} else if (evt.key === "Shift") { } else if (evt.key === 'Shift') {
keyboard.shiftKey(true); keyboard.shiftKey(true);
} else if (evt.key == "Control") { } else if (evt.key === 'Control') {
keyboard.controlKey(true); keyboard.controlKey(true);
} else if (!focused && (!evt.metaKey || evt.ctrlKey)) { } else if (!focused && (!evt.metaKey || evt.ctrlKey)) {
evt.preventDefault(); evt.preventDefault();
var key = mapKeyEvent(evt); const key = mapKeyEvent(evt);
if (key != 0xff) { if (key !== 0xff) {
if (_key != 0xff) io.keyUp(); if (_key !== 0xff) io.keyUp();
io.keyDown(key); io.keyDown(key);
_key = key; _key = key;
} }
@ -285,9 +301,9 @@ function _keydown(evt: KeyboardEvent) {
function _keyup(evt: KeyboardEvent) { function _keyup(evt: KeyboardEvent) {
_key = 0xff; _key = 0xff;
if (evt.key === "Shift") { if (evt.key === 'Shift') {
keyboard.shiftKey(false); keyboard.shiftKey(false);
} else if (evt.key === "Control") { } else if (evt.key === 'Control') {
keyboard.controlKey(false); keyboard.controlKey(false);
} else { } else {
if (!focused) { if (!focused) {
@ -300,9 +316,9 @@ let _updateScreenTimer: ReturnType<typeof setInterval> | null = null;
export function updateScreen() { export function updateScreen() {
const green = const green =
document.querySelector<HTMLInputElement>("#green_screen")!.checked; document.querySelector<HTMLInputElement>('#green_screen')!.checked;
const scanlines = const scanlines =
document.querySelector<HTMLInputElement>("#show_scanlines")!.checked; document.querySelector<HTMLInputElement>('#show_scanlines')!.checked;
text.green(green); text.green(green);
text.scanlines(scanlines); text.scanlines(scanlines);
@ -321,39 +337,39 @@ paused = false;
export function pauseRun(b: HTMLButtonElement) { export function pauseRun(b: HTMLButtonElement) {
if (paused) { if (paused) {
run(); run();
b.value = "Pause"; b.value = 'Pause';
} else { } else {
stop(); stop();
b.value = "Run"; b.value = 'Run';
} }
paused = !paused; paused = !paused;
} }
export function openOptions() { export function openOptions() {
MicroModal.show("options-modal"); MicroModal.show('options-modal');
} }
export function openLoadText(event?: MouseEvent) { export function openLoadText(event?: MouseEvent) {
if (event && event.altKey) { if (event && event.altKey) {
MicroModal.show("local-modal"); MicroModal.show('local-modal');
} else { } else {
MicroModal.show("input-modal"); MicroModal.show('input-modal');
} }
} }
export function doLoadText() { export function doLoadText() {
var text = document.querySelector<HTMLInputElement>("#text_input")!.value; const text = document.querySelector<HTMLInputElement>('#text_input')!.value;
if (!text.indexOf("//Binary")) { if (!text.indexOf('//Binary')) {
var lines = text.split("\n"); const lines = text.split('\n');
lines.forEach(function (line) { lines.forEach(function (line) {
var parts = line.split(": "); const parts = line.split(': ');
if (parts.length == 2) { if (parts.length === 2) {
let addr: address = 0; let addr: address = 0;
if (parts[0].length > 0) { if (parts[0].length > 0) {
addr = parseInt(parts[0], 16); addr = parseInt(parts[0], 16);
} }
var data = parts[1].split(" "); const data = parts[1].split(' ');
for (var idx = 0; idx < data.length; idx++) { for (let idx = 0; idx < data.length; idx++) {
cpu.write(addr >> 8, addr & 0xff, parseInt(data[idx], 16)); cpu.write(addr >> 8, addr & 0xff, parseInt(data[idx], 16));
addr++; addr++;
} }
@ -362,28 +378,28 @@ export function doLoadText() {
} else { } else {
io.paste(text); io.paste(text);
} }
MicroModal.close("input-modal"); MicroModal.close('input-modal');
} }
export function handleDragOver(event: DragEvent) { export function handleDragOver(event: DragEvent) {
event.preventDefault(); event.preventDefault();
event.dataTransfer!.dropEffect = "copy"; event.dataTransfer!.dropEffect = 'copy';
} }
export function handleDrop(event: DragEvent) { export function handleDrop(event: DragEvent) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
var dt = event.dataTransfer; const dt = event.dataTransfer;
if (dt?.files && dt.files.length > 0) { if (dt?.files && dt.files.length > 0) {
doLoadLocal(dt.files); doLoadLocal(dt.files);
} }
} }
export function handleDragEnd(event: DragEvent) { export function handleDragEnd(event: DragEvent) {
var dt = event.dataTransfer; const dt = event.dataTransfer;
if (dt?.items) { if (dt?.items) {
for (var i = 0; i < dt.items.length; i++) { for (let i = 0; i < dt.items.length; i++) {
dt.items.remove(i); dt.items.remove(i);
} }
} else { } else {
@ -393,39 +409,39 @@ export function handleDragEnd(event: DragEvent) {
MicroModal.init(); MicroModal.init();
document.addEventListener("DOMContentLoaded", function () { document.addEventListener('DOMContentLoaded', function () {
hashtag = document.location.hash; hashtag = document.location.hash;
/* /*
* Input Handling * Input Handling
*/ */
const canvas = document.querySelector<HTMLCanvasElement>("#text")!; const canvas = document.querySelector<HTMLCanvasElement>('#text')!;
const context = canvas.getContext("2d")!; const context = canvas.getContext('2d')!;
text.setContext(context); text.setContext(context);
window.addEventListener("keydown", _keydown); window.addEventListener('keydown', _keydown);
window.addEventListener("keyup", _keyup); window.addEventListener('keyup', _keyup);
window.addEventListener("paste", (event) => { window.addEventListener('paste', (event) => {
var paste = event.clipboardData!.getData("text/plain"); const paste = event.clipboardData!.getData('text/plain');
setKeyBuffer(paste); setKeyBuffer(paste);
event.preventDefault(); event.preventDefault();
}); });
window.addEventListener("copy", (event) => { window.addEventListener('copy', (event) => {
event.clipboardData?.setData("text/plain", text.getText()); event.clipboardData?.setData('text/plain', text.getText());
event.preventDefault(); event.preventDefault();
}); });
document.querySelectorAll("input,textarea").forEach(function (el) { document.querySelectorAll('input,textarea').forEach(function (el) {
el.addEventListener("focus", function () { el.addEventListener('focus', function () {
focused = true; focused = true;
}); });
}); });
document.querySelectorAll("input,textarea").forEach(function (el) { document.querySelectorAll('input,textarea').forEach(function (el) {
el.addEventListener("blur", function () { el.addEventListener('blur', function () {
focused = false; focused = false;
}); });
}); });
@ -433,58 +449,58 @@ document.addEventListener("DOMContentLoaded", function () {
if (prefs.havePrefs()) { if (prefs.havePrefs()) {
document document
.querySelectorAll<HTMLInputElement>("input[type=checkbox]") .querySelectorAll<HTMLInputElement>('input[type=checkbox]')
.forEach(function (el) { .forEach(function (el) {
var val = prefs.readPref(el.id); const val = prefs.readPref(el.id);
if (val != null) el.checked = JSON.parse(val); if (val != null) el.checked = !!JSON.parse(val);
}); });
document document
.querySelectorAll<HTMLInputElement>("input[type=checkbox]") .querySelectorAll<HTMLInputElement>('input[type=checkbox]')
.forEach(function (el) { .forEach(function (el) {
el.addEventListener("change", function () { el.addEventListener('change', function () {
prefs.writePref(el.id, JSON.stringify(el.checked)); prefs.writePref(el.id, JSON.stringify(el.checked));
}); });
}); });
} }
turbotape = document.querySelector<HTMLInputElement>("#turbo_tape")!.checked; turbotape = document.querySelector<HTMLInputElement>('#turbo_tape')!.checked;
Object.keys(window.tapes) Object.keys(window.tapes)
.sort() .sort()
.forEach(function (key) { .forEach(function (key) {
var option = document.createElement("option"); const option = document.createElement('option');
option.value = key; option.value = key;
option.text = key; option.text = key;
document.querySelector("#tape_select")!.append(option); document.querySelector('#tape_select')!.append(option);
}); });
function doTapeSelect() { function doTapeSelect() {
var tapeId = const tapeId =
document.querySelector<HTMLInputElement>("#tape_select")!.value; document.querySelector<HTMLInputElement>('#tape_select')!.value;
var tape = window.tapes[tapeId]; const tape = window.tapes[tapeId];
if (!tape) { if (!tape) {
document.querySelector<HTMLInputElement>("#text_input")!.value = ""; document.querySelector<HTMLInputElement>('#text_input')!.value = '';
return; return;
} }
debug("Loading", tapeId); debug('Loading', tapeId);
window.location.hash = tapeId; window.location.hash = tapeId;
reset(); reset();
if (turbotape) { if (turbotape) {
var trackIdx = 0, let trackIdx = 0,
script = ""; script = '';
var parts = tape.script.split("\n"); const parts = tape.script.split('\n');
// Ignore part 0 (C100R) // Ignore part 0 (C100R)
// Split part 1 into ranges // Split part 1 into ranges
var ranges = parts[1].split(" "); const ranges = parts[1].split(' ');
var idx; let idx;
for (idx = 0; idx < ranges.length; idx++) { for (idx = 0; idx < ranges.length; idx++) {
var range = ranges[idx].split("."); const range = ranges[idx].split('.');
var start = parseInt(range[0], 16); const start = parseInt(range[0], 16);
var end = parseInt(range[1], 16); const end = parseInt(range[1], 16);
var track = tape.tracks[trackIdx]; const track = tape.tracks[trackIdx];
var kdx = 0; let kdx = 0;
for (var jdx = start; jdx <= end; jdx++) { for (let jdx = start; jdx <= end; jdx++) {
cpu.write(jdx >> 8, jdx & 0xff, track[kdx++]); cpu.write(jdx >> 8, jdx & 0xff, track[kdx++]);
} }
trackIdx++; trackIdx++;
@ -492,29 +508,29 @@ document.addEventListener("DOMContentLoaded", function () {
// Execute parts 2-n // Execute parts 2-n
for (idx = 2; idx < parts.length; idx++) { for (idx = 2; idx < parts.length; idx++) {
if (parts[idx]) { if (parts[idx]) {
script += parts[idx] + "\n"; script += parts[idx] + '\n';
} }
} }
document.querySelector<HTMLInputElement>("#text_input")!.value = script; document.querySelector<HTMLInputElement>('#text_input')!.value = script;
} else { } else {
aci.setData(tape.tracks); aci.setData(tape.tracks);
document.querySelector<HTMLInputElement>("#text_input")!.value = document.querySelector<HTMLInputElement>('#text_input')!.value =
tape.script; tape.script;
} }
doLoadText(); doLoadText();
} }
document document
.querySelector("#tape_select")! .querySelector('#tape_select')!
.addEventListener("change", doTapeSelect); .addEventListener('change', doTapeSelect);
run(); run();
setInterval(updateKHz, 1000); setInterval(updateKHz, 1000);
updateScreen(); updateScreen();
var tape = hup(); const tape = hup();
if (tape) { if (tape) {
openLoadText(); openLoadText();
document.querySelector<HTMLInputElement>("#tape_select")!.value = tape; document.querySelector<HTMLInputElement>('#tape_select')!.value = tape;
doTapeSelect(); doTapeSelect();
} }
}); });

View File

@ -9,8 +9,8 @@
* implied warranty. * implied warranty.
*/ */
import { TextPage } from "./canvas1"; import { TextPage } from './canvas1';
import type { byte } from "./types"; import type { byte } from './types';
const LOC = { const LOC = {
KBD: 0x10, KBD: 0x10,
@ -36,20 +36,22 @@ export default class Apple1IO {
return 0xd0; return 0xd0;
} }
read(_page: byte, off: byte): byte { read(_page: byte, off: byte): byte {
var result = 0; let result = 0;
off &= 0x13; off &= 0x13;
switch (off) { switch (off) {
case LOC.KBD: case LOC.KBD:
// Keyboard {
const key = this._buffer.shift(); // Keyboard
if (key != null) { const key = this._buffer.shift();
result = key.toUpperCase().charCodeAt(0) & 0x7f; if (key != null) {
this._keyReady = this._buffer.length > 0; result = key.toUpperCase().charCodeAt(0) & 0x7f;
} else { this._keyReady = this._buffer.length > 0;
result = this._key; } else {
this._keyReady = false; result = this._key;
this._keyReady = false;
}
result |= 0x80;
} }
result |= 0x80;
break; break;
case LOC.KBDRDY: case LOC.KBDRDY:
result = this._keyReady ? 0x80 : 0x00; result = this._keyReady ? 0x80 : 0x00;
@ -96,9 +98,9 @@ export default class Apple1IO {
this._keyReady = true; this._keyReady = true;
} }
paste(buffer: string) { paste(buffer: string) {
buffer = buffer.replace(/\/\/.*\n/g, ""); buffer = buffer.replace(/\/\/.*\n/g, '');
buffer = buffer.replace(/\n/g, "\r"); buffer = buffer.replace(/\n/g, '\r');
this._buffer = buffer.split(""); this._buffer = buffer.split('');
this._keyReady = true; this._keyReady = true;
} }
} }

View File

@ -1,6 +1,6 @@
import { memory } from "./types"; import { memory } from './types';
const B64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; const B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
/** Encode an array of bytes in base64. */ /** Encode an array of bytes in base64. */
export function base64_encode(data: null | undefined): undefined; export function base64_encode(data: null | undefined): undefined;
@ -37,7 +37,7 @@ export function base64_encode(
bits, bits,
i = 0, i = 0,
ac = 0, ac = 0,
enc = ""; enc = '';
const tmp_arr = []; const tmp_arr = [];
if (!data) { if (!data) {
@ -62,14 +62,14 @@ export function base64_encode(
B64.charAt(h1) + B64.charAt(h2) + B64.charAt(h3) + B64.charAt(h4); B64.charAt(h1) + B64.charAt(h2) + B64.charAt(h3) + B64.charAt(h4);
} while (i < data.length); } while (i < data.length);
enc = tmp_arr.join(""); enc = tmp_arr.join('');
switch (data.length % 3) { switch (data.length % 3) {
case 1: case 1:
enc = enc.slice(0, -2) + "=="; enc = enc.slice(0, -2) + '==';
break; break;
case 2: case 2:
enc = enc.slice(0, -1) + "="; enc = enc.slice(0, -1) + '=';
break; break;
} }
@ -147,11 +147,11 @@ export function base64_decode(
return new Uint8Array(tmp_arr); return new Uint8Array(tmp_arr);
} }
const DATA_URL_PREFIX = "data:application/octet-stream;base64,"; const DATA_URL_PREFIX = 'data:application/octet-stream;base64,';
export function base64_json_parse(json: string): unknown { export function base64_json_parse(json: string): unknown {
const reviver = (_key: string, value: unknown) => { const reviver = (_key: string, value: unknown) => {
if (typeof value === "string" && value.startsWith(DATA_URL_PREFIX)) { if (typeof value === 'string' && value.startsWith(DATA_URL_PREFIX)) {
return base64_decode(value.slice(DATA_URL_PREFIX.length)); return base64_decode(value.slice(DATA_URL_PREFIX.length));
} }
return value; return value;

View File

@ -9,8 +9,8 @@
* implied warranty. * implied warranty.
*/ */
import { charset } from "./roms/apple1char"; import { charset } from './roms/apple1char';
import type { byte } from "./types"; import type { byte } from './types';
/* /*
0: A9 9 AA 20 EF FF E8 8A 4C 2 0 0: A9 9 AA 20 EF FF E8 8A 4C 2 0
@ -40,9 +40,9 @@ export class TextPage {
constructor() { constructor() {
this._buffer = []; this._buffer = [];
for (var row = 0; row < 24; row++) { for (let row = 0; row < 24; row++) {
this._buffer[row] = []; this._buffer[row] = [];
for (var col = 0; col < 40; col++) { for (let col = 0; col < 40; col++) {
this._buffer[row][col] = col % 2 ? 0x00 : 0xff; this._buffer[row][col] = col % 2 ? 0x00 : 0xff;
} }
} }
@ -58,12 +58,12 @@ export class TextPage {
} }
write(val: byte): void { write(val: byte): void {
var col; let col;
val &= 0x7f; val &= 0x7f;
if (this.transcript) { if (this.transcript) {
if (val == 0xd) { if (val === 0xd) {
this.transcript += "\n"; this.transcript += '\n';
} else if (val >= 0x20) { } else if (val >= 0x20) {
if (val >= 0x60) { if (val >= 0x60) {
val &= 0x5f; val &= 0x5f;
@ -72,7 +72,7 @@ export class TextPage {
} }
} }
if (val == 0x0d) { if (val === 0x0d) {
for (col = this._col; col < 40; col++) { for (col = this._col; col < 40; col++) {
this._buffer[this._row][col] = 0x20; this._buffer[this._row][col] = 0x20;
} }
@ -99,15 +99,13 @@ export class TextPage {
} }
writeAt(row: byte, col: byte, val: byte): void { writeAt(row: byte, col: byte, val: byte): void {
this._buffer[row][col] = val; this._buffer[row][col] = val;
var data = this._page.data, const data = this._page.data;
fore, let color;
back, let off = (col * 14 + row * 560 * 8 * 2) * 4;
color;
var off = (col * 14 + row * 560 * 8 * 2) * 4;
fore = this._greenMode ? this._green : this._white; let fore = this._greenMode ? this._green : this._white;
back = this._black; const back = this._black;
var char = 0; let char = 0;
if (!val) { if (!val) {
if (this._blinking) { if (this._blinking) {
@ -118,12 +116,12 @@ export class TextPage {
char |= val & 0x40 ? 0 : 0x20; char |= val & 0x40 ? 0 : 0x20;
} }
for (var jdx = 0; jdx < 8; jdx++) { for (let jdx = 0; jdx < 8; jdx++) {
var b = charset[char * 8 + jdx]; let b = charset[char * 8 + jdx];
for (var idx = 0; idx < 7; idx += 1) { for (let idx = 0; idx < 7; idx += 1) {
b <<= 1; b <<= 1;
color = b & 0x80 ? fore : back; color = b & 0x80 ? fore : back;
var c0 = color[0], const c0 = color[0],
c1 = color[1], c1 = color[1],
c2 = color[2]; c2 = color[2];
data[off + 0] = data[off + 4] = c0; data[off + 0] = data[off + 4] = c0;
@ -144,9 +142,9 @@ export class TextPage {
} }
} }
_blink() { _blink() {
for (var row = 0; row < 24; row++) { for (let row = 0; row < 24; row++) {
for (var col = 0; col < 40; col++) { for (let col = 0; col < 40; col++) {
var val = this._buffer[row][col]; const val = this._buffer[row][col];
if (!val) { if (!val) {
this.writeAt(row, col, val); this.writeAt(row, col, val);
} }
@ -155,8 +153,8 @@ export class TextPage {
this._dirty = true; this._dirty = true;
} }
refresh() { refresh() {
for (var row = 0; row < 24; row++) { for (let row = 0; row < 24; row++) {
for (var col = 0; col < 40; col++) { for (let col = 0; col < 40; col++) {
this.writeAt(row, col, this._buffer[row][col]); this.writeAt(row, col, this._buffer[row][col]);
} }
} }
@ -186,7 +184,7 @@ export class TextPage {
setContext(context: CanvasRenderingContext2D) { setContext(context: CanvasRenderingContext2D) {
this._context = context; this._context = context;
this._page = context.createImageData(560, 384); this._page = context.createImageData(560, 384);
for (var idx = 0; idx < 560 * 384 * 4; idx++) { for (let idx = 0; idx < 560 * 384 * 4; idx++) {
this._page.data[idx] = 0xff; this._page.data[idx] = 0xff;
} }
} }
@ -202,24 +200,24 @@ export class TextPage {
return charCode; return charCode;
} }
var buffer = "", let buffer = '',
line, line,
charCode; charCode;
var row, col; let row, col;
for (row = 0; row < 24; row++) { for (row = 0; row < 24; row++) {
line = ""; line = '';
for (col = 0; col < 40; col++) { for (col = 0; col < 40; col++) {
charCode = mapCharCode(this._buffer[row][col]); charCode = mapCharCode(this._buffer[row][col]);
line += String.fromCharCode(charCode); line += String.fromCharCode(charCode);
} }
line = line.trimRight(); line = line.trimRight();
buffer += line + "\n"; buffer += line + '\n';
} }
return buffer; return buffer;
} }
clear() { clear() {
for (var row = 0; row < 24; row++) { for (let row = 0; row < 24; row++) {
for (var col = 0; col < 40; col++) { for (let col = 0; col < 40; col++) {
this._buffer[row][col] = 0x20; this._buffer[row][col] = 0x20;
} }
} }
@ -228,5 +226,5 @@ export class TextPage {
this.refresh(); this.refresh();
} }
transcript = ""; transcript = '';
} }

View File

@ -1,6 +1,6 @@
import CPU6502 from "../cpu6502"; import CPU6502 from '../cpu6502';
import { debug } from "../util"; import { debug } from '../util';
import { byte } from "../types"; import { byte } from '../types';
const rom = [ const rom = [
0xa9, 0xaa, 0x20, 0xef, 0xff, 0xa9, 0x8d, 0x20, 0xef, 0xff, 0xa0, 0xff, 0xc8, 0xa9, 0xaa, 0x20, 0xef, 0xff, 0xa9, 0x8d, 0x20, 0xef, 0xff, 0xa0, 0xff, 0xc8,
@ -50,19 +50,18 @@ export default class ACI {
return 0xc1; return 0xc1;
} }
read(page: byte, off: byte) { read(page: byte, off: byte) {
var now = this.cpu.getCycles(); const now = this.cpu.getCycles();
var result = rom[off]; let result = rom[off];
if (page == 0xc0) { if (page === 0xc0) {
if (this._recording) { if (this._recording) {
var delta = now - this._last; const delta = now - this._last;
this.buffer.push(delta); this.buffer.push(delta);
this._last = now; this._last = now;
} else { } else {
var progress;
if (this._readOffset < this.buffer.length) { if (this._readOffset < this.buffer.length) {
if (now > this._next) { if (now > this._next) {
if (this._readOffset % 1000 == 0) { if (this._readOffset % 1000 === 0) {
debug("Read " + this._readOffset / 1000); debug('Read ' + this._readOffset / 1000);
} }
this._flip = !this._flip; this._flip = !this._flip;
this._next = now + this.buffer[this._readOffset++]; this._next = now + this.buffer[this._readOffset++];
@ -70,9 +69,9 @@ export default class ACI {
} }
result = this._flip ? rom[off | 0x01] : rom[off & 0xfe]; result = this._flip ? rom[off | 0x01] : rom[off & 0xfe];
progress = const progress =
Math.round((this._readOffset / this.buffer.length) * 100) / 100; Math.round((this._readOffset / this.buffer.length) * 100) / 100;
if (this._progress != progress) { if (this._progress !== progress) {
this._progress = progress; this._progress = progress;
this.cb.progress(this._progress); this.cb.progress(this._progress);
} }
@ -83,14 +82,14 @@ export default class ACI {
case 0x00: case 0x00:
this._recording = false; this._recording = false;
this._beKind = true; this._beKind = true;
debug("Entering ACI CLI"); debug('Entering ACI CLI');
break; break;
case 0x63: case 0x63:
if (this._recording) { if (this._recording) {
this.buffer.push(5000000); this.buffer.push(5000000);
this._recording = false; this._recording = false;
} }
debug("Exiting ACI CLI"); debug('Exiting ACI CLI');
break; break;
case 0x70: // WRITE case 0x70: // WRITE
this._recording = true; this._recording = true;
@ -98,7 +97,7 @@ export default class ACI {
this._beKind = false; this._beKind = false;
this.buffer = []; this.buffer = [];
} }
debug("Start write"); debug('Start write');
this._last = now; this._last = now;
break; break;
//case 0x7c: // WBITLOOP: //case 0x7c: // WBITLOOP:
@ -107,7 +106,7 @@ export default class ACI {
// break; // break;
case 0x8d: // READ case 0x8d: // READ
this._recording = false; this._recording = false;
debug("Start read"); debug('Start read');
if (this._beKind) { if (this._beKind) {
this._readOffset = 0; this._readOffset = 0;
this._next = now + 5000000; this._next = now + 5000000;
@ -131,7 +130,7 @@ export default class ACI {
setState() {} setState() {}
setData(data: number[][]) { setData(data: number[][]) {
var seg, idx, jdx, d, b; let seg, idx, jdx, d, b;
this.buffer = []; this.buffer = [];
for (seg = 0; seg < data.length; seg++) { for (seg = 0; seg < data.length; seg++) {
for (idx = 0; idx < 16384; idx++) { for (idx = 0; idx < 16384; idx++) {

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,3 @@
var Apple1 = require('./apple1'); import * as Apple1 from './apple1';
module.exports = { Apple1: Apple1 }; window.Apple1 = Apple1;

View File

@ -11,7 +11,7 @@
export default class Prefs { export default class Prefs {
havePrefs() { havePrefs() {
return typeof localStorage !== "undefined"; return typeof localStorage !== 'undefined';
} }
readPref(name: string) { readPref(name: string) {
if (localStorage) return localStorage.getItem(name); if (localStorage) return localStorage.getItem(name);

View File

@ -9,9 +9,9 @@
* implied warranty. * implied warranty.
*/ */
import { base64_decode, base64_encode } from "./base64"; import { base64_decode, base64_encode } from './base64';
import { allocMemPages } from "./util"; import { allocMemPages } from './util';
import type { byte } from "./types"; import type { byte } from './types';
export interface RAMState { export interface RAMState {
start: byte; start: byte;
@ -25,8 +25,8 @@ export default class RAM {
constructor(private start_page: byte, private end_page: byte) { constructor(private start_page: byte, private end_page: byte) {
this.mem = allocMemPages(end_page - start_page + 1); this.mem = allocMemPages(end_page - start_page + 1);
for (var page = 0; page <= end_page; page++) { for (let page = 0; page <= end_page; page++) {
for (var off = 0; off < 0x100; off++) { for (let off = 0; off < 0x100; off++) {
this.mem[page * 0x100 + off] = 0; // Math.floor(Math.random()*256); this.mem[page * 0x100 + off] = 0; // Math.floor(Math.random()*256);
} }
} }

View File

@ -1,4 +1,4 @@
import { byte } from "js/types"; import { byte } from 'js/types';
export default class Basic { export default class Basic {
ram = [ ram = [

View File

@ -1,4 +1,4 @@
import { byte } from "js/types"; import { byte } from 'js/types';
export default class Bios { export default class Bios {
rom = [ rom = [

View File

@ -1,4 +1,4 @@
import { byte } from "js/types"; import { byte } from 'js/types';
export default class EnhancedBasic { export default class EnhancedBasic {
rom = [ rom = [

View File

@ -1,4 +1,4 @@
import { byte } from "js/types"; import { byte } from 'js/types';
export default class Krusader { export default class Krusader {
rom = [ rom = [

View File

@ -9,16 +9,16 @@
* implied warranty. * implied warranty.
*/ */
import type { address } from "./types"; import type { address } from './types';
export const SYMBOLS: Record<address, string> = { export const SYMBOLS: Record<address, string> = {
0xd010: "KBD", 0xd010: 'KBD',
0xd011: "KBDCR", 0xd011: 'KBDCR',
0xd012: "DSP", 0xd012: 'DSP',
0xd013: "DSPCR", 0xd013: 'DSPCR',
0xff1f: "GETLINE", 0xff1f: 'GETLINE',
0xffef: "ECHO", 0xffef: 'ECHO',
0xffdc: "PRBYTE", 0xffdc: 'PRBYTE',
0xffe5: "PRHEX", 0xffe5: 'PRHEX',
}; };

View File

@ -9,11 +9,11 @@
* implied warranty. * implied warranty.
*/ */
import CPU6502 from "js/cpu6502"; import CPU6502 from 'js/cpu6502';
import { debug, toHex } from "../util"; import { debug, toHex } from '../util';
import Apple1IO from "js/apple1io"; import Apple1IO from 'js/apple1io';
import { TextPage } from "js/canvas1"; import { TextPage } from 'js/canvas1';
import { byte } from "js/types"; import { byte } from 'js/types';
// keycode: [plain, cntl, shift] // keycode: [plain, cntl, shift]
@ -140,29 +140,29 @@ const keymap: Record<byte, readonly byte[]> = {
const keys = [ const keys = [
[ [
["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", ":", "-", "RESET"], ['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', ':', '-', 'RESET'],
["ESC", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "FEED", "RETURN"], ['ESC', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', 'FEED', 'RETURN'],
["CTRL", "A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "OUT", "CLS"], ['CTRL', 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', 'OUT', 'CLS'],
["SHIFT", "Z", "X", "C", "V", "B", "N", "M", ",", ".", "/", "SHIFT"], ['SHIFT', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', ',', '.', '/', 'SHIFT'],
["&nbsp;"], ['&nbsp;'],
], ],
[ [
["!", '"', "#", "$", "%", "&", "'", "(", ")", "0", "*", "=", "RESET"], ['!', '"', '#', '$', '%', '&', "'", '(', ')', '0', '*', '=', 'RESET'],
["ESC", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "@", "LINE", "RETURN"], ['ESC', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', '@', 'LINE', 'RETURN'],
["CTRL", "A", "S", "D", "F", "BELL", "H", "J", "K", "L", "+", "RUB", "CLS"], ['CTRL', 'A', 'S', 'D', 'F', 'BELL', 'H', 'J', 'K', 'L', '+', 'RUB', 'CLS'],
["SHIFT", "Z", "X", "C", "V", "B", "^", "M", "<", ">", "?", "SHIFT"], ['SHIFT', 'Z', 'X', 'C', 'V', 'B', '^', 'M', '<', '>', '?', 'SHIFT'],
["&nbsp;"], ['&nbsp;'],
], ],
] as const; ] as const;
export function mapKeyEvent(evt: KeyboardEvent) { export function mapKeyEvent(evt: KeyboardEvent) {
var code = evt.keyCode; const code = evt.keyCode;
if (code in keymap) { if (code in keymap) {
return keymap[code][evt.shiftKey ? 2 : evt.ctrlKey ? 1 : 0]; return keymap[code][evt.shiftKey ? 2 : evt.ctrlKey ? 1 : 0];
} }
debug("Unhandled key = " + toHex(code)); debug('Unhandled key = ' + toHex(code));
return 0xff; return 0xff;
} }
@ -182,34 +182,34 @@ export class KeyBoard {
shiftKey(down: boolean) { shiftKey(down: boolean) {
this.shifted = down; this.shifted = down;
this.kb.querySelectorAll(".key-SHIFT").forEach(function (el) { this.kb.querySelectorAll('.key-SHIFT').forEach(function (el) {
if (down) { if (down) {
el.classList.add("active"); el.classList.add('active');
} else { } else {
el.classList.remove("active"); el.classList.remove('active');
} }
}); });
} }
controlKey(down: boolean) { controlKey(down: boolean) {
this.controlled = down; this.controlled = down;
this.kb.querySelectorAll(".key-CTRL").forEach(function (el) { this.kb.querySelectorAll('.key-CTRL').forEach(function (el) {
if (down) { if (down) {
el.classList.add("active"); el.classList.add('active');
} else { } else {
el.classList.remove("active"); el.classList.remove('active');
} }
}); });
} }
create() { create() {
var x, y, row, key, key1, key2, label, label1, label2; let x, y, row, key, key1, key2, label, label1, label2;
function buildLabel(k: string) { function buildLabel(k: string) {
var span = document.createElement("span"); const span = document.createElement('span');
span.innerHTML = k; span.innerHTML = k;
if (k.length > 1 && !k.startsWith("&")) { if (k.length > 1 && !k.startsWith('&')) {
span.classList.add("small"); span.classList.add('small');
} }
return span; return span;
} }
@ -219,7 +219,7 @@ export class KeyBoard {
currentTarget: HTMLElement; currentTarget: HTMLElement;
} }
) => { ) => {
event.currentTarget.classList.remove("pressed"); event.currentTarget.classList.remove('pressed');
}; };
const _mousedown = ( const _mousedown = (
@ -227,31 +227,31 @@ export class KeyBoard {
currentTarget: HTMLElement; currentTarget: HTMLElement;
} }
) => { ) => {
event.currentTarget.classList.add("pressed"); event.currentTarget.classList.add('pressed');
var key = event.currentTarget.dataset[this.shifted ? "key2" : "key1"]; let key = event.currentTarget.dataset[this.shifted ? 'key2' : 'key1'];
if (!key) { if (!key) {
return; return;
} }
switch (key) { switch (key) {
case "BELL": case 'BELL':
key = "G"; key = 'G';
break; break;
case "RETURN": case 'RETURN':
key = "\r"; key = '\r';
break; break;
case "LINE": case 'LINE':
case "FEED": case 'FEED':
key = "\n"; key = '\n';
break; break;
case "RUB": case 'RUB':
case "OUT": case 'OUT':
key = "_"; // 0x5f key = '_'; // 0x5f
break; break;
case "&nbsp;": case '&nbsp;':
key = " "; key = ' ';
break; break;
case "ESC": case 'ESC':
key = "\0x1b"; key = '\0x1b';
break; break;
default: default:
break; break;
@ -259,31 +259,31 @@ export class KeyBoard {
if (key.length > 1) { if (key.length > 1) {
switch (key) { switch (key) {
case "SHIFT": case 'SHIFT':
this.shifted = !this.shifted; this.shifted = !this.shifted;
this.kb this.kb
.querySelectorAll(".key-SHIFT") .querySelectorAll('.key-SHIFT')
.forEach(function (el: HTMLElement) { .forEach(function (el: HTMLElement) {
el.classList.toggle("active"); el.classList.toggle('active');
}); });
break; break;
case "CTRL": case 'CTRL':
this.controlled = !this.controlled; this.controlled = !this.controlled;
this.kb.querySelectorAll(".key-CTRL").forEach(function (el) { this.kb.querySelectorAll('.key-CTRL').forEach(function (el) {
el.classList.toggle("active"); el.classList.toggle('active');
}); });
break; break;
case "RESET": case 'RESET':
this.cpu.reset(); this.cpu.reset();
break; break;
case "CLS": case 'CLS':
this.text.clear(); this.text.clear();
break; break;
default: default:
break; break;
} }
} else { } else {
if (this.controlled && key >= "@" && key <= "_") { if (this.controlled && key >= '@' && key <= '_') {
this.io.keyDown(key.charCodeAt(0) - 0x40); this.io.keyDown(key.charCodeAt(0) - 0x40);
} else { } else {
this.io.keyDown(key.charCodeAt(0)); this.io.keyDown(key.charCodeAt(0));
@ -292,46 +292,46 @@ export class KeyBoard {
}; };
for (y = 0; y < 5; y++) { for (y = 0; y < 5; y++) {
row = document.createElement("div"); row = document.createElement('div');
row.classList.add("row", "row" + y); row.classList.add('row', 'row' + y);
this.kb.append(row); this.kb.append(row);
for (x = 0; x < keys[0][y].length; x++) { for (x = 0; x < keys[0][y].length; x++) {
key1 = keys[0][y][x]; key1 = keys[0][y][x];
key2 = keys[1][y][x]; key2 = keys[1][y][x];
label = document.createElement("div"); label = document.createElement('div');
label1 = buildLabel(key1); label1 = buildLabel(key1);
label2 = buildLabel(key2); label2 = buildLabel(key2);
key = document.createElement("div"); key = document.createElement('div');
key.classList.add("key", "key-" + key1.replace(/[&;]/g, "")); key.classList.add('key', 'key-' + key1.replace(/[&;]/g, ''));
if (key1.length > 1) { if (key1.length > 1) {
if (key1 != key2) { if (key1 !== key2) {
key.classList.add("vcenter2"); key.classList.add('vcenter2');
} else { } else {
key.classList.add("vcenter"); key.classList.add('vcenter');
} }
} }
if (key1 != key2) { if (key1 !== key2) {
key.classList.add("key-" + key2.replace(/[&;]/g, "")); key.classList.add('key-' + key2.replace(/[&;]/g, ''));
label.append(label2); label.append(label2);
label.append(document.createElement("br")); label.append(document.createElement('br'));
} }
label.append(label1); label.append(label1);
key.append(label); key.append(label);
key.dataset["key1"] = key1; key.dataset['key1'] = key1;
key.dataset["key2"] = key2; key.dataset['key2'] = key2;
if (window.ontouchstart === undefined) { if (window.ontouchstart === undefined) {
key.addEventListener("mousedown", _mousedown); key.addEventListener('mousedown', _mousedown);
key.addEventListener("mouseup", _mouseup); key.addEventListener('mouseup', _mouseup);
key.addEventListener("mouseout", _mouseup); key.addEventListener('mouseout', _mouseup);
} else { } else {
key.addEventListener("touchstart", _mousedown); key.addEventListener('touchstart', _mousedown);
key.addEventListener("touchend", _mouseup); key.addEventListener('touchend', _mouseup);
key.addEventListener("touchleave", _mouseup); key.addEventListener('touchleave', _mouseup);
} }
row.append(key); row.append(key);

View File

@ -9,13 +9,13 @@
* implied warranty. * implied warranty.
*/ */
import { byte, word } from "./types"; import { byte, word } from './types';
var hex_digits = "0123456789ABCDEF"; const hex_digits = '0123456789ABCDEF';
var bin_digits = "01"; const bin_digits = '01';
export function allocMem(size: word) { export function allocMem(size: word) {
var result; let result;
if (window.Uint8Array) { if (window.Uint8Array) {
result = new Uint8Array(size); result = new Uint8Array(size);
} else { } else {
@ -37,8 +37,8 @@ export function toHex(v: byte, n?: 2 | 4) {
if (!n) { if (!n) {
n = v < 256 ? 2 : 4; n = v < 256 ? 2 : 4;
} }
var result = ""; let result = '';
for (var idx = 0; idx < n; idx++) { for (let idx = 0; idx < n; idx++) {
result = hex_digits[v & 0x0f] + result; result = hex_digits[v & 0x0f] + result;
v >>= 4; v >>= 4;
} }
@ -46,8 +46,8 @@ export function toHex(v: byte, n?: 2 | 4) {
} }
export function toBinary(v: byte) { export function toBinary(v: byte) {
var result = ""; let result = '';
for (var idx = 0; idx < 8; idx++) { for (let idx = 0; idx < 8; idx++) {
result = bin_digits[v & 0x01] + result; result = bin_digits[v & 0x01] + result;
v >>= 1; v >>= 1;
} }
@ -60,8 +60,8 @@ export function gup(name: string) {
} }
export function hup() { export function hup() {
var regex = new RegExp("#(.*)"); const regex = new RegExp('#(.*)');
var results = regex.exec(window.location.hash); const results = regex.exec(window.location.hash);
if (!results) return ""; if (!results) return '';
else return decodeURIComponent(results[1]); else return decodeURIComponent(results[1]);
} }

1153
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -29,9 +29,11 @@
"@babel/core": "^7.4.0", "@babel/core": "^7.4.0",
"@babel/preset-env": "^7.4.2", "@babel/preset-env": "^7.4.2",
"@types/micromodal": "^0.3.3", "@types/micromodal": "^0.3.3",
"@typescript-eslint/eslint-plugin": "^6.2.1",
"ajv": "^6.9.2", "ajv": "^6.9.2",
"babel-jest": "^29.5.0", "babel-jest": "^29.5.0",
"eslint": "^8.3.0", "eslint": "^8.3.0",
"eslint-plugin-jest": "^27.2.3",
"jest": "^29.5.0", "jest": "^29.5.0",
"node-forge": "^1.3.0", "node-forge": "^1.3.0",
"ts-jest": "^29.1.1", "ts-jest": "^29.1.1",

View File

@ -6,43 +6,43 @@ import Test65C02 from './roms/65C02test';
import { toHex } from '../js/util'; import { toHex } from '../js/util';
describe('CPU', function () { describe('CPU', function () {
let cpu: CPU6502; let cpu: CPU6502;
let lastPC = 0; let lastPC = 0;
let done = false; let done = false;
function traceCB() { function traceCB() {
const pc = cpu.getPC(); const pc = cpu.getPC();
done = lastPC === pc; done = lastPC === pc;
lastPC = pc; lastPC = pc;
} }
describe('6502', function () { describe('6502', function () {
it('completes the test ROM', function () { it('completes the test ROM', function () {
cpu = new CPU6502(); cpu = new CPU6502();
const test = new Test6502(); const test = new Test6502();
cpu.addPageHandler(test); cpu.addPageHandler(test);
cpu.setPC(0x400); cpu.setPC(0x400);
do { do {
cpu.stepCyclesDebug(1000, traceCB); cpu.stepCyclesDebug(1000, traceCB);
} while (!done); } while (!done);
expect(toHex(lastPC)).toEqual(toHex(0x3469)); expect(toHex(lastPC)).toEqual(toHex(0x3469));
});
}); });
});
describe('65C02', function () { describe('65C02', function () {
it('completes the test ROM', function () { it('completes the test ROM', function () {
cpu = new CPU6502({ flavor: FLAVOR_ROCKWELL_65C02 }); cpu = new CPU6502({ flavor: FLAVOR_ROCKWELL_65C02 });
const test = new Test65C02(); const test = new Test65C02();
cpu.addPageHandler(test); cpu.addPageHandler(test);
cpu.setPC(0x400); cpu.setPC(0x400);
do { do {
cpu.stepCyclesDebug(1000, traceCB); cpu.stepCyclesDebug(1000, traceCB);
} while (!done); } while (!done);
expect(toHex(lastPC)).toEqual(toHex(0x24f1)); expect(toHex(lastPC)).toEqual(toHex(0x24f1));
});
}); });
});
}); });

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
import { byte } from '../../js/types'; import { byte } from "../../js/types";
export const assertByte = (b: byte) => { export const assertByte = (b: byte) => {
expect(b <= 0xFF).toEqual(true); expect(b <= 0xff).toEqual(true);
expect(b >= 0x00).toEqual(true); expect(b >= 0x00).toEqual(true);
}; };

View File

@ -1,63 +1,56 @@
import { MemoryPages, byte } from '../../js/types'; import { MemoryPages, byte } from "../../js/types";
import { assertByte } from './asserts'; import { assertByte } from "./asserts";
export class Program implements MemoryPages { export class Program implements MemoryPages {
private data: Buffer; private data: Buffer;
constructor(private page: byte, code: byte[]) { constructor(private page: byte, code: byte[]) {
this.data = Buffer.from(code); this.data = Buffer.from(code);
} }
start() { start() {
return this.page; return this.page;
} }
end() { end() {
return this.page; return this.page;
} }
read(page: byte, off: byte) { read(page: byte, off: byte) {
assertByte(page); assertByte(page);
assertByte(off); assertByte(off);
return this.data[off]; return this.data[off];
} }
write(_page: byte, _off: byte, _val: byte) { write(_page: byte, _off: byte, _val: byte) {
// do nothing // do nothing
} }
} }
export const bios = new Program(0xff, [ export const bios = new Program(
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, [
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x45, 0x4c, 0x4c,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x48, 0x45, 0x4C, 0x4C, 0x4F, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0xff,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, );
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xff, 0x00, 0x04, 0x00, 0xff
]);

View File

@ -1,61 +1,60 @@
import { MemoryPages, byte, word } from 'js/types'; import { MemoryPages, byte, word } from "js/types";
import { assertByte } from './asserts'; import { assertByte } from "./asserts";
export type Log = [address: word, value: byte, types: 'read'|'write']; export type Log = [address: word, value: byte, types: "read" | "write"];
export class TestMemory implements MemoryPages { export class TestMemory implements MemoryPages {
private data: Buffer; private data: Buffer;
private logging: boolean = false; private logging: boolean = false;
private log: Log[] = []; private log: Log[] = [];
constructor(private size: number) { constructor(private size: number) {
this.data = Buffer.alloc(size << 8); this.data = Buffer.alloc(size << 8);
}
start() {
return 0;
}
end() {
return this.size - 1;
}
read(page: byte, off: byte) {
assertByte(page);
assertByte(off);
const val = this.data[(page << 8) | off];
if (this.logging) {
this.log.push([(page << 8) | off, val, "read"]);
} }
return val;
}
start() { write(page: byte, off: byte, val: byte) {
return 0; assertByte(page);
assertByte(off);
assertByte(val);
if (this.logging) {
this.log.push([(page << 8) | off, val, "write"]);
} }
this.data[(page << 8) | off] = val;
}
end() { reset() {
return this.size - 1; this.log = [];
} }
read(page: byte, off: byte) { logStart() {
assertByte(page); this.log = [];
assertByte(off); this.logging = true;
}
const val = this.data[(page << 8) | off]; logStop() {
if (this.logging) { this.logging = false;
this.log.push([page << 8 | off, val, 'read']); }
}
return val;
}
write(page: byte, off: byte, val: byte) { getLog() {
assertByte(page); return this.log;
assertByte(off); }
assertByte(val);
if (this.logging) {
this.log.push([page << 8 | off, val, 'write']);
}
this.data[(page << 8) | off] = val;
}
reset() {
this.log = [];
}
logStart() {
this.log = [];
this.logging = true;
}
logStop() {
this.logging = false;
}
getLog() {
return this.log;
}
} }

View File

@ -9,9 +9,6 @@ module.exports =
}, },
output: { output: {
path: path.resolve('dist/'), path: path.resolve('dist/'),
library: 'Apple1',
libraryExport: 'Apple1',
libraryTarget: 'var'
}, },
module: { module: {
rules: [ rules: [