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';
export default class EnhancedBasic {
rom = [

View File

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

View File

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

View File

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

View File

@ -9,13 +9,13 @@
* implied warranty.
*/
import { byte, word } from "./types";
import { byte, word } from './types';
var hex_digits = "0123456789ABCDEF";
var bin_digits = "01";
const hex_digits = '0123456789ABCDEF';
const bin_digits = '01';
export function allocMem(size: word) {
var result;
let result;
if (window.Uint8Array) {
result = new Uint8Array(size);
} else {
@ -37,8 +37,8 @@ export function toHex(v: byte, n?: 2 | 4) {
if (!n) {
n = v < 256 ? 2 : 4;
}
var result = "";
for (var idx = 0; idx < n; idx++) {
let result = '';
for (let idx = 0; idx < n; idx++) {
result = hex_digits[v & 0x0f] + result;
v >>= 4;
}
@ -46,8 +46,8 @@ export function toHex(v: byte, n?: 2 | 4) {
}
export function toBinary(v: byte) {
var result = "";
for (var idx = 0; idx < 8; idx++) {
let result = '';
for (let idx = 0; idx < 8; idx++) {
result = bin_digits[v & 0x01] + result;
v >>= 1;
}
@ -60,8 +60,8 @@ export function gup(name: string) {
}
export function hup() {
var regex = new RegExp("#(.*)");
var results = regex.exec(window.location.hash);
if (!results) return "";
const regex = new RegExp('#(.*)');
const results = regex.exec(window.location.hash);
if (!results) return '';
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/preset-env": "^7.4.2",
"@types/micromodal": "^0.3.3",
"@typescript-eslint/eslint-plugin": "^6.2.1",
"ajv": "^6.9.2",
"babel-jest": "^29.5.0",
"eslint": "^8.3.0",
"eslint-plugin-jest": "^27.2.3",
"jest": "^29.5.0",
"node-forge": "^1.3.0",
"ts-jest": "^29.1.1",

View File

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

View File

@ -1,63 +1,56 @@
import { MemoryPages, byte } from '../../js/types';
import { assertByte } from './asserts';
import { MemoryPages, byte } from "../../js/types";
import { assertByte } from "./asserts";
export class Program implements MemoryPages {
private data: Buffer;
private data: Buffer;
constructor(private page: byte, code: byte[]) {
this.data = Buffer.from(code);
}
constructor(private page: byte, code: byte[]) {
this.data = Buffer.from(code);
}
start() {
return this.page;
}
start() {
return this.page;
}
end() {
return this.page;
}
end() {
return this.page;
}
read(page: byte, off: byte) {
assertByte(page);
assertByte(off);
return this.data[off];
}
read(page: byte, off: byte) {
assertByte(page);
assertByte(off);
return this.data[off];
}
write(_page: byte, _off: byte, _val: byte) {
// do nothing
}
write(_page: byte, _off: byte, _val: byte) {
// do nothing
}
}
export const bios = new Program(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,
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, 0xff, 0x00, 0x04, 0x00, 0xff
]);
export const bios = new Program(
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, 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, 0xff,
0x00, 0x04, 0x00, 0xff,
]
);

View File

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

View File

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