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

View File

@ -9,8 +9,8 @@
* implied warranty.
*/
import { TextPage } from "./canvas1";
import type { byte } from "./types";
import { TextPage } from './canvas1';
import type { byte } from './types';
const LOC = {
KBD: 0x10,
@ -36,20 +36,22 @@ export default class Apple1IO {
return 0xd0;
}
read(_page: byte, off: byte): byte {
var result = 0;
let result = 0;
off &= 0x13;
switch (off) {
case LOC.KBD:
// Keyboard
const key = this._buffer.shift();
if (key != null) {
result = key.toUpperCase().charCodeAt(0) & 0x7f;
this._keyReady = this._buffer.length > 0;
} else {
result = this._key;
this._keyReady = false;
{
// Keyboard
const key = this._buffer.shift();
if (key != null) {
result = key.toUpperCase().charCodeAt(0) & 0x7f;
this._keyReady = this._buffer.length > 0;
} else {
result = this._key;
this._keyReady = false;
}
result |= 0x80;
}
result |= 0x80;
break;
case LOC.KBDRDY:
result = this._keyReady ? 0x80 : 0x00;
@ -96,9 +98,9 @@ export default class Apple1IO {
this._keyReady = true;
}
paste(buffer: string) {
buffer = buffer.replace(/\/\/.*\n/g, "");
buffer = buffer.replace(/\n/g, "\r");
this._buffer = buffer.split("");
buffer = buffer.replace(/\/\/.*\n/g, '');
buffer = buffer.replace(/\n/g, '\r');
this._buffer = buffer.split('');
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. */
export function base64_encode(data: null | undefined): undefined;
@ -37,7 +37,7 @@ export function base64_encode(
bits,
i = 0,
ac = 0,
enc = "";
enc = '';
const tmp_arr = [];
if (!data) {
@ -62,14 +62,14 @@ export function base64_encode(
B64.charAt(h1) + B64.charAt(h2) + B64.charAt(h3) + B64.charAt(h4);
} while (i < data.length);
enc = tmp_arr.join("");
enc = tmp_arr.join('');
switch (data.length % 3) {
case 1:
enc = enc.slice(0, -2) + "==";
enc = enc.slice(0, -2) + '==';
break;
case 2:
enc = enc.slice(0, -1) + "=";
enc = enc.slice(0, -1) + '=';
break;
}
@ -147,11 +147,11 @@ export function base64_decode(
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 {
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 value;

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
import { byte } from "js/types";
import { byte } from 'js/types';