mirror of
https://github.com/whscullin/apple1js.git
synced 2024-11-21 13:31:05 +00:00
Lint, other build fixes
This commit is contained in:
parent
3f6cfd67b7
commit
ca3f255a87
@ -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
|
||||
|
211
.eslintrc.json
211
.eslintrc.json
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
342
js/apple1.ts
342
js/apple1.ts
@ -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();
|
||||
}
|
||||
});
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
16
js/base64.ts
16
js/base64.ts
@ -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;
|
||||
|
@ -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 = '';
|
||||
}
|
||||
|
@ -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++) {
|
||||
|
1222
js/cpu6502.ts
1222
js/cpu6502.ts
File diff suppressed because it is too large
Load Diff
@ -1,3 +1,3 @@
|
||||
var Apple1 = require('./apple1');
|
||||
import * as Apple1 from './apple1';
|
||||
|
||||
module.exports = { Apple1: Apple1 };
|
||||
window.Apple1 = Apple1;
|
||||
|
@ -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);
|
||||
|
10
js/ram.ts
10
js/ram.ts
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { byte } from "js/types";
|
||||
import { byte } from 'js/types';
|
||||
|
||||
export default class Basic {
|
||||
ram = [
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { byte } from "js/types";
|
||||
import { byte } from 'js/types';
|
||||
|
||||
export default class Bios {
|
||||
rom = [
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { byte } from "js/types";
|
||||
import { byte } from 'js/types';
|
||||
|
||||
export default class EnhancedBasic {
|
||||
rom = [
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { byte } from "js/types";
|
||||
import { byte } from 'js/types';
|
||||
|
||||
export default class Krusader {
|
||||
rom = [
|
||||
|
@ -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',
|
||||
};
|
||||
|
@ -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"],
|
||||
[" "],
|
||||
['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'],
|
||||
[' '],
|
||||
],
|
||||
[
|
||||
["!", '"', "#", "$", "%", "&", "'", "(", ")", "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"],
|
||||
[" "],
|
||||
['!', '"', '#', '$', '%', '&', "'", '(', ')', '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'],
|
||||
[' '],
|
||||
],
|
||||
] 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 " ":
|
||||
key = " ";
|
||||
case ' ':
|
||||
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);
|
||||
|
22
js/util.ts
22
js/util.ts
@ -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
1153
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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",
|
||||
|
@ -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));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
5284
test/cpu6502.spec.ts
5284
test/cpu6502.spec.ts
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
};
|
||||
|
@ -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,
|
||||
]
|
||||
);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,9 +9,6 @@ module.exports =
|
||||
},
|
||||
output: {
|
||||
path: path.resolve('dist/'),
|
||||
library: 'Apple1',
|
||||
libraryExport: 'Apple1',
|
||||
libraryTarget: 'var'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
|
Loading…
Reference in New Issue
Block a user