mirror of
https://github.com/whscullin/apple1js.git
synced 2025-02-19 05:30:57 +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
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
[*.js]
|
[*.js]
|
||||||
indent_size = 4
|
indent_size = 2
|
||||||
|
|
||||||
|
[*.ts]
|
||||||
|
indent_size = 2
|
||||||
|
quote_type = single
|
||||||
|
|
||||||
[*.html]
|
[*.html]
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
207
.eslintrc.json
207
.eslintrc.json
@ -1,20 +1,60 @@
|
|||||||
{
|
{
|
||||||
|
// Global
|
||||||
|
"root": true,
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:jest/recommended"
|
||||||
|
],
|
||||||
|
"globals": {
|
||||||
|
"tapes": "writable"
|
||||||
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"indent": [
|
"indent": [
|
||||||
2,
|
"error",
|
||||||
4
|
4,
|
||||||
|
{
|
||||||
|
"SwitchCase": 1
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"quotes": [
|
"quotes": [
|
||||||
2,
|
"error",
|
||||||
"single"
|
"single",
|
||||||
|
{ "avoidEscape": true }
|
||||||
],
|
],
|
||||||
"linebreak-style": [
|
"linebreak-style": [
|
||||||
2,
|
"error",
|
||||||
"unix"
|
"unix"
|
||||||
],
|
],
|
||||||
"semi": [
|
"eqeqeq": [
|
||||||
2,
|
"error",
|
||||||
"always"
|
"smart"
|
||||||
|
],
|
||||||
|
"prefer-const": [
|
||||||
|
"error"
|
||||||
|
],
|
||||||
|
"no-var": "error",
|
||||||
|
"no-use-before-define": "off",
|
||||||
|
"no-console": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"allow": [
|
||||||
|
"info",
|
||||||
|
"warn",
|
||||||
|
"error"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
// Jest configuration
|
||||||
|
"jest/expect-expect": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"assertFunctionNames": [
|
||||||
|
"expect*",
|
||||||
|
"checkImageData",
|
||||||
|
"testCode"
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"env": {
|
"env": {
|
||||||
@ -22,16 +62,109 @@
|
|||||||
"browser": true,
|
"browser": true,
|
||||||
"es6": true
|
"es6": true
|
||||||
},
|
},
|
||||||
"globals": {
|
"overrides": [
|
||||||
"tapes": true
|
// All overrides matching a file are applied in-order, with the last
|
||||||
|
// taking precedence.
|
||||||
|
//
|
||||||
|
// TypeScript/TSX-specific configuration
|
||||||
|
{
|
||||||
|
"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": {
|
"parserOptions": {
|
||||||
"sourceType": "module"
|
"sourceType": "module",
|
||||||
|
"project": "./tsconfig.json"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"extends": "eslint:recommended",
|
// UI elements
|
||||||
"overrides": [
|
|
||||||
{
|
{
|
||||||
"files": [ "bin/*", "babel.config.js", "webpack.config.js" ],
|
"files": [
|
||||||
|
"js/ui/**.ts"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
// allow non-null assertions since these classes reference the DOM
|
||||||
|
"@typescript-eslint/no-non-null-assertion": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// JS Node configuration
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"bin/*",
|
||||||
|
"babel.config.js",
|
||||||
|
"webpack.config.js"
|
||||||
|
],
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-console": 0
|
"no-console": 0
|
||||||
},
|
},
|
||||||
@ -40,17 +173,49 @@
|
|||||||
"jquery": false,
|
"jquery": false,
|
||||||
"browser": false
|
"browser": false
|
||||||
}
|
}
|
||||||
}, {
|
},
|
||||||
"files": [ "test/*"],
|
// Test configuration
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"test/**/*"
|
||||||
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"node": true,
|
"jest": true,
|
||||||
"jest": true
|
"jasmine": true,
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"no-console": 0
|
||||||
}
|
}
|
||||||
}, {
|
},
|
||||||
"files": [ "js/entry1.js"],
|
// Entry point configuration
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"js/entry2.ts",
|
||||||
|
"js/entry2e.ts",
|
||||||
|
"jest.config.js"
|
||||||
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"commonjs": true
|
"commonjs": true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
// Worker configuration
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"workers/*"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"project": "workers/tsconfig.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ignorePatterns": [
|
||||||
|
"coverage/**/*"
|
||||||
|
],
|
||||||
|
"settings": {
|
||||||
|
"react": {
|
||||||
|
"pragma": "h",
|
||||||
|
"version": "16"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
284
js/apple1.ts
284
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 MicroModal from 'micromodal';
|
||||||
import CPU6502 from "./cpu6502";
|
|
||||||
import Prefs from "./prefs";
|
|
||||||
import RAM from "./ram";
|
|
||||||
import { TextPage } from "./canvas1";
|
|
||||||
import { debug, hup } from "./util";
|
|
||||||
|
|
||||||
import Basic from "./roms/basic";
|
import Apple1IO from './apple1io';
|
||||||
import Bios from "./roms/bios";
|
import CPU6502 from './cpu6502';
|
||||||
import Krusader from "./roms/krusader";
|
import Prefs from './prefs';
|
||||||
|
import RAM from './ram';
|
||||||
|
import { TextPage } from './canvas1';
|
||||||
|
import { debug, hup } from './util';
|
||||||
|
|
||||||
import ACI from "./cards/aci";
|
import Basic from './roms/basic';
|
||||||
|
import Bios from './roms/bios';
|
||||||
|
import Krusader from './roms/krusader';
|
||||||
|
|
||||||
import { mapKeyEvent, KeyBoard } from "./ui/keyboard";
|
import ACI from './cards/aci';
|
||||||
import { address, byte } from "./types";
|
|
||||||
|
|
||||||
|
import { mapKeyEvent, KeyBoard } from './ui/keyboard';
|
||||||
|
import { address, byte } from './types';
|
||||||
|
|
||||||
|
// eslint-disable-next-line prefer-const
|
||||||
let DEBUG = false;
|
let DEBUG = false;
|
||||||
|
// eslint-disable-next-line prefer-const
|
||||||
let TRACE = true;
|
let TRACE = true;
|
||||||
const skidmarks: string[] = [];
|
const skidmarks: string[] = [];
|
||||||
|
|
||||||
@ -32,22 +45,22 @@ const prefs = new Prefs();
|
|||||||
let runTimer: ReturnType<typeof setInterval> | null = null;
|
let runTimer: ReturnType<typeof setInterval> | null = null;
|
||||||
const cpu = new CPU6502();
|
const cpu = new CPU6502();
|
||||||
|
|
||||||
var krusader = window.location.hash == "#krusader";
|
const krusader = window.location.hash === '#krusader';
|
||||||
|
|
||||||
var raml, ramh, rom, aci: ACI, io: Apple1IO, text: TextPage, keyboard: KeyBoard;
|
let ramh, rom;
|
||||||
|
|
||||||
// 32K base memory. Should be 0x0f for 4K, 0x1f for 8K, 0x3f for 16K
|
// 32K base memory. Should be 0x0f for 4K, 0x1f for 8K, 0x3f for 16K
|
||||||
raml = new RAM(0x00, 0x7f);
|
const raml = new RAM(0x00, 0x7f);
|
||||||
text = new TextPage();
|
const text = new TextPage();
|
||||||
text.init();
|
text.init();
|
||||||
|
|
||||||
aci = new ACI(cpu, {
|
const aci = new ACI(cpu, {
|
||||||
progress: function (val) {
|
progress: function (val) {
|
||||||
document.querySelector<HTMLElement>("#tape")!.style.width =
|
document.querySelector<HTMLElement>('#tape')!.style.width =
|
||||||
val * 100 + "px";
|
val * 100 + 'px';
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
io = new Apple1IO(text);
|
const io = new Apple1IO(text);
|
||||||
|
|
||||||
if (krusader) {
|
if (krusader) {
|
||||||
ramh = null;
|
ramh = null;
|
||||||
@ -57,7 +70,7 @@ if (krusader) {
|
|||||||
ramh = new Basic();
|
ramh = new Basic();
|
||||||
rom = new Bios();
|
rom = new Bios();
|
||||||
}
|
}
|
||||||
keyboard = new KeyBoard("#keyboard", cpu, io, text);
|
const keyboard = new KeyBoard('#keyboard', cpu, io, text);
|
||||||
|
|
||||||
cpu.addPageHandler(raml);
|
cpu.addPageHandler(raml);
|
||||||
if (ramh) {
|
if (ramh) {
|
||||||
@ -68,7 +81,7 @@ cpu.addPageHandler(rom);
|
|||||||
cpu.addPageHandler(aci);
|
cpu.addPageHandler(aci);
|
||||||
cpu.addPageHandler(io);
|
cpu.addPageHandler(io);
|
||||||
|
|
||||||
var showFPS = false;
|
let showFPS = false;
|
||||||
|
|
||||||
interface Tape {
|
interface Tape {
|
||||||
script: string;
|
script: string;
|
||||||
@ -86,7 +99,7 @@ declare global {
|
|||||||
//aci.setData([0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef])
|
//aci.setData([0x01,0x23,0x45,0x67,0x89,0xab,0xcd,0xef])
|
||||||
|
|
||||||
//aci.setData(tapes['BASIC']);
|
//aci.setData(tapes['BASIC']);
|
||||||
aci.setData(window.tapes["Microchess"].tracks);
|
aci.setData(window.tapes['Microchess'].tracks);
|
||||||
|
|
||||||
// Audio Buffer Source
|
// Audio Buffer Source
|
||||||
declare global {
|
declare global {
|
||||||
@ -99,55 +112,61 @@ const AudioContext = window.AudioContext || window.webkitAudioContext;
|
|||||||
const context = new AudioContext();
|
const context = new AudioContext();
|
||||||
|
|
||||||
export function doLoadLocal(files: FileList) {
|
export function doLoadLocal(files: FileList) {
|
||||||
context.resume();
|
context
|
||||||
|
.resume()
|
||||||
|
.then(() => {
|
||||||
files =
|
files =
|
||||||
files || document.querySelector<HTMLInputElement>("#local_file")!.files;
|
files || document.querySelector<HTMLInputElement>('#local_file')!.files;
|
||||||
if (files.length == 1) {
|
if (files.length === 1) {
|
||||||
var file = files[0];
|
const file = files[0];
|
||||||
var fileReader = new FileReader();
|
const fileReader = new FileReader();
|
||||||
fileReader.onload = function (ev) {
|
fileReader.onload = function (ev) {
|
||||||
context.decodeAudioData(
|
context
|
||||||
|
.decodeAudioData(
|
||||||
ev.target!.result as ArrayBuffer,
|
ev.target!.result as ArrayBuffer,
|
||||||
function (buffer) {
|
function (buffer) {
|
||||||
var buf = [];
|
const buf = [];
|
||||||
var data = buffer.getChannelData(0);
|
const data = buffer.getChannelData(0);
|
||||||
var old = data[0] > 0.25;
|
let old = data[0] > 0.25;
|
||||||
var last = 0;
|
let last = 0;
|
||||||
for (var idx = 1; idx < data.length; idx++) {
|
for (let idx = 1; idx < data.length; idx++) {
|
||||||
var current = data[idx] > 0.25;
|
const current = data[idx] > 0.25;
|
||||||
if (current != old) {
|
if (current !== old) {
|
||||||
var delta = idx - last;
|
const delta = idx - last;
|
||||||
buf.push(Math.floor((delta / buffer.sampleRate) * 1023000));
|
buf.push(Math.floor((delta / buffer.sampleRate) * 1023000));
|
||||||
old = current;
|
old = current;
|
||||||
last = idx;
|
last = idx;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
aci.buffer = buf;
|
aci.buffer = buf;
|
||||||
MicroModal.close("local-modal");
|
MicroModal.close('local-modal');
|
||||||
},
|
},
|
||||||
function () {
|
function () {
|
||||||
window.alert("Unable to read tape file: " + file.name);
|
window.alert('Unable to read tape file: ' + file.name);
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
|
.catch(console.error);
|
||||||
};
|
};
|
||||||
fileReader.readAsArrayBuffer(file);
|
fileReader.readAsArrayBuffer(file);
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateKHz() {
|
function updateKHz() {
|
||||||
let now = Date.now();
|
const now = Date.now();
|
||||||
let ms = now - startTime;
|
const ms = now - startTime;
|
||||||
let cycles = cpu.getCycles();
|
const cycles = cpu.getCycles();
|
||||||
let delta: number;
|
let delta: number;
|
||||||
|
|
||||||
if (showFPS) {
|
if (showFPS) {
|
||||||
delta = renderedFrames - lastFrames;
|
delta = renderedFrames - lastFrames;
|
||||||
var fps = Math.floor(delta / (ms / 1000));
|
const fps = Math.floor(delta / (ms / 1000));
|
||||||
document.querySelector("#khz")!.innerHTML = fps + "fps";
|
document.querySelector('#khz')!.innerHTML = fps + 'fps';
|
||||||
} else {
|
} else {
|
||||||
delta = cycles - lastCycles;
|
delta = cycles - lastCycles;
|
||||||
var khz = Math.floor(delta / ms);
|
const khz = Math.floor(delta / ms);
|
||||||
document.querySelector("#khz")!.innerHTML = khz + "KHz";
|
document.querySelector('#khz')!.innerHTML = khz + 'KHz';
|
||||||
}
|
}
|
||||||
|
|
||||||
startTime = now;
|
startTime = now;
|
||||||
@ -155,9 +174,8 @@ function updateKHz() {
|
|||||||
lastFrames = renderedFrames;
|
lastFrames = renderedFrames;
|
||||||
}
|
}
|
||||||
|
|
||||||
var loading = false;
|
let throttling = true;
|
||||||
var throttling = true;
|
let turbotape = false;
|
||||||
var turbotape = false;
|
|
||||||
|
|
||||||
export function toggleFPS() {
|
export function toggleFPS() {
|
||||||
showFPS = !showFPS;
|
showFPS = !showFPS;
|
||||||
@ -165,7 +183,7 @@ export function toggleFPS() {
|
|||||||
|
|
||||||
export function toggleSpeed() {
|
export function toggleSpeed() {
|
||||||
throttling =
|
throttling =
|
||||||
document.querySelector<HTMLInputElement>("#speed_toggle")!.checked;
|
document.querySelector<HTMLInputElement>('#speed_toggle')!.checked;
|
||||||
if (runTimer) {
|
if (runTimer) {
|
||||||
run();
|
run();
|
||||||
}
|
}
|
||||||
@ -188,17 +206,17 @@ function run(pc?: address) {
|
|||||||
cpu.setPC(pc);
|
cpu.setPC(pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
var ival = 30,
|
let ival = 30;
|
||||||
step = 1023 * ival,
|
let step = 1023 * ival;
|
||||||
stepMax = step;
|
const stepMax = step;
|
||||||
|
|
||||||
if (!throttling) {
|
if (!throttling) {
|
||||||
ival = 1;
|
ival = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
var now,
|
let now;
|
||||||
last = Date.now();
|
let last = Date.now();
|
||||||
var runFn = function () {
|
const runFn = function () {
|
||||||
now = Date.now();
|
now = Date.now();
|
||||||
renderedFrames++;
|
renderedFrames++;
|
||||||
step = (now - last) * 1023;
|
step = (now - last) * 1023;
|
||||||
@ -206,13 +224,12 @@ function run(pc?: address) {
|
|||||||
if (step > stepMax) {
|
if (step > stepMax) {
|
||||||
step = stepMax;
|
step = stepMax;
|
||||||
}
|
}
|
||||||
if (document.location.hash != hashtag) {
|
if (document.location.hash !== hashtag) {
|
||||||
hashtag = document.location.hash;
|
hashtag = document.location.hash;
|
||||||
}
|
}
|
||||||
if (!loading) {
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
cpu.stepCyclesDebug(TRACE ? 1 : step, function () {
|
cpu.stepCyclesDebug(TRACE ? 1 : step, function () {
|
||||||
var line = JSON.stringify(cpu.getState());
|
const line = JSON.stringify(cpu.getState());
|
||||||
if (TRACE) {
|
if (TRACE) {
|
||||||
debug(line);
|
debug(line);
|
||||||
} else {
|
} else {
|
||||||
@ -226,7 +243,6 @@ function run(pc?: address) {
|
|||||||
cpu.stepCycles(step);
|
cpu.stepCycles(step);
|
||||||
}
|
}
|
||||||
text.blit();
|
text.blit();
|
||||||
}
|
|
||||||
if (!paused) {
|
if (!paused) {
|
||||||
requestAnimationFrame(runFn);
|
requestAnimationFrame(runFn);
|
||||||
}
|
}
|
||||||
@ -255,7 +271,7 @@ declare global {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _key: byte;
|
let _key: byte;
|
||||||
function _keydown(evt: KeyboardEvent) {
|
function _keydown(evt: KeyboardEvent) {
|
||||||
if (evt.keyCode === 112) {
|
if (evt.keyCode === 112) {
|
||||||
cpu.reset();
|
cpu.reset();
|
||||||
@ -263,19 +279,19 @@ function _keydown(evt: KeyboardEvent) {
|
|||||||
if (document.webkitIsFullScreen) {
|
if (document.webkitIsFullScreen) {
|
||||||
document.webkitCancelFullScreen();
|
document.webkitCancelFullScreen();
|
||||||
} else {
|
} else {
|
||||||
var elem = document.getElementById("display");
|
const elem = document.getElementById('display');
|
||||||
elem!.webkitRequestFullScreen();
|
elem!.webkitRequestFullScreen();
|
||||||
}
|
}
|
||||||
} else if (evt.key === "Shift") {
|
} else if (evt.key === 'Shift') {
|
||||||
keyboard.shiftKey(true);
|
keyboard.shiftKey(true);
|
||||||
} else if (evt.key == "Control") {
|
} else if (evt.key === 'Control') {
|
||||||
keyboard.controlKey(true);
|
keyboard.controlKey(true);
|
||||||
} else if (!focused && (!evt.metaKey || evt.ctrlKey)) {
|
} else if (!focused && (!evt.metaKey || evt.ctrlKey)) {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
|
|
||||||
var key = mapKeyEvent(evt);
|
const key = mapKeyEvent(evt);
|
||||||
if (key != 0xff) {
|
if (key !== 0xff) {
|
||||||
if (_key != 0xff) io.keyUp();
|
if (_key !== 0xff) io.keyUp();
|
||||||
io.keyDown(key);
|
io.keyDown(key);
|
||||||
_key = key;
|
_key = key;
|
||||||
}
|
}
|
||||||
@ -285,9 +301,9 @@ function _keydown(evt: KeyboardEvent) {
|
|||||||
function _keyup(evt: KeyboardEvent) {
|
function _keyup(evt: KeyboardEvent) {
|
||||||
_key = 0xff;
|
_key = 0xff;
|
||||||
|
|
||||||
if (evt.key === "Shift") {
|
if (evt.key === 'Shift') {
|
||||||
keyboard.shiftKey(false);
|
keyboard.shiftKey(false);
|
||||||
} else if (evt.key === "Control") {
|
} else if (evt.key === 'Control') {
|
||||||
keyboard.controlKey(false);
|
keyboard.controlKey(false);
|
||||||
} else {
|
} else {
|
||||||
if (!focused) {
|
if (!focused) {
|
||||||
@ -300,9 +316,9 @@ let _updateScreenTimer: ReturnType<typeof setInterval> | null = null;
|
|||||||
|
|
||||||
export function updateScreen() {
|
export function updateScreen() {
|
||||||
const green =
|
const green =
|
||||||
document.querySelector<HTMLInputElement>("#green_screen")!.checked;
|
document.querySelector<HTMLInputElement>('#green_screen')!.checked;
|
||||||
const scanlines =
|
const scanlines =
|
||||||
document.querySelector<HTMLInputElement>("#show_scanlines")!.checked;
|
document.querySelector<HTMLInputElement>('#show_scanlines')!.checked;
|
||||||
|
|
||||||
text.green(green);
|
text.green(green);
|
||||||
text.scanlines(scanlines);
|
text.scanlines(scanlines);
|
||||||
@ -321,39 +337,39 @@ paused = false;
|
|||||||
export function pauseRun(b: HTMLButtonElement) {
|
export function pauseRun(b: HTMLButtonElement) {
|
||||||
if (paused) {
|
if (paused) {
|
||||||
run();
|
run();
|
||||||
b.value = "Pause";
|
b.value = 'Pause';
|
||||||
} else {
|
} else {
|
||||||
stop();
|
stop();
|
||||||
b.value = "Run";
|
b.value = 'Run';
|
||||||
}
|
}
|
||||||
paused = !paused;
|
paused = !paused;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function openOptions() {
|
export function openOptions() {
|
||||||
MicroModal.show("options-modal");
|
MicroModal.show('options-modal');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function openLoadText(event?: MouseEvent) {
|
export function openLoadText(event?: MouseEvent) {
|
||||||
if (event && event.altKey) {
|
if (event && event.altKey) {
|
||||||
MicroModal.show("local-modal");
|
MicroModal.show('local-modal');
|
||||||
} else {
|
} else {
|
||||||
MicroModal.show("input-modal");
|
MicroModal.show('input-modal');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doLoadText() {
|
export function doLoadText() {
|
||||||
var text = document.querySelector<HTMLInputElement>("#text_input")!.value;
|
const text = document.querySelector<HTMLInputElement>('#text_input')!.value;
|
||||||
if (!text.indexOf("//Binary")) {
|
if (!text.indexOf('//Binary')) {
|
||||||
var lines = text.split("\n");
|
const lines = text.split('\n');
|
||||||
lines.forEach(function (line) {
|
lines.forEach(function (line) {
|
||||||
var parts = line.split(": ");
|
const parts = line.split(': ');
|
||||||
if (parts.length == 2) {
|
if (parts.length === 2) {
|
||||||
let addr: address = 0;
|
let addr: address = 0;
|
||||||
if (parts[0].length > 0) {
|
if (parts[0].length > 0) {
|
||||||
addr = parseInt(parts[0], 16);
|
addr = parseInt(parts[0], 16);
|
||||||
}
|
}
|
||||||
var data = parts[1].split(" ");
|
const data = parts[1].split(' ');
|
||||||
for (var idx = 0; idx < data.length; idx++) {
|
for (let idx = 0; idx < data.length; idx++) {
|
||||||
cpu.write(addr >> 8, addr & 0xff, parseInt(data[idx], 16));
|
cpu.write(addr >> 8, addr & 0xff, parseInt(data[idx], 16));
|
||||||
addr++;
|
addr++;
|
||||||
}
|
}
|
||||||
@ -362,28 +378,28 @@ export function doLoadText() {
|
|||||||
} else {
|
} else {
|
||||||
io.paste(text);
|
io.paste(text);
|
||||||
}
|
}
|
||||||
MicroModal.close("input-modal");
|
MicroModal.close('input-modal');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleDragOver(event: DragEvent) {
|
export function handleDragOver(event: DragEvent) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.dataTransfer!.dropEffect = "copy";
|
event.dataTransfer!.dropEffect = 'copy';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleDrop(event: DragEvent) {
|
export function handleDrop(event: DragEvent) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
var dt = event.dataTransfer;
|
const dt = event.dataTransfer;
|
||||||
if (dt?.files && dt.files.length > 0) {
|
if (dt?.files && dt.files.length > 0) {
|
||||||
doLoadLocal(dt.files);
|
doLoadLocal(dt.files);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function handleDragEnd(event: DragEvent) {
|
export function handleDragEnd(event: DragEvent) {
|
||||||
var dt = event.dataTransfer;
|
const dt = event.dataTransfer;
|
||||||
if (dt?.items) {
|
if (dt?.items) {
|
||||||
for (var i = 0; i < dt.items.length; i++) {
|
for (let i = 0; i < dt.items.length; i++) {
|
||||||
dt.items.remove(i);
|
dt.items.remove(i);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -393,39 +409,39 @@ export function handleDragEnd(event: DragEvent) {
|
|||||||
|
|
||||||
MicroModal.init();
|
MicroModal.init();
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", function () {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
hashtag = document.location.hash;
|
hashtag = document.location.hash;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Input Handling
|
* Input Handling
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const canvas = document.querySelector<HTMLCanvasElement>("#text")!;
|
const canvas = document.querySelector<HTMLCanvasElement>('#text')!;
|
||||||
const context = canvas.getContext("2d")!;
|
const context = canvas.getContext('2d')!;
|
||||||
|
|
||||||
text.setContext(context);
|
text.setContext(context);
|
||||||
|
|
||||||
window.addEventListener("keydown", _keydown);
|
window.addEventListener('keydown', _keydown);
|
||||||
window.addEventListener("keyup", _keyup);
|
window.addEventListener('keyup', _keyup);
|
||||||
|
|
||||||
window.addEventListener("paste", (event) => {
|
window.addEventListener('paste', (event) => {
|
||||||
var paste = event.clipboardData!.getData("text/plain");
|
const paste = event.clipboardData!.getData('text/plain');
|
||||||
setKeyBuffer(paste);
|
setKeyBuffer(paste);
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener("copy", (event) => {
|
window.addEventListener('copy', (event) => {
|
||||||
event.clipboardData?.setData("text/plain", text.getText());
|
event.clipboardData?.setData('text/plain', text.getText());
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
document.querySelectorAll("input,textarea").forEach(function (el) {
|
document.querySelectorAll('input,textarea').forEach(function (el) {
|
||||||
el.addEventListener("focus", function () {
|
el.addEventListener('focus', function () {
|
||||||
focused = true;
|
focused = true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
document.querySelectorAll("input,textarea").forEach(function (el) {
|
document.querySelectorAll('input,textarea').forEach(function (el) {
|
||||||
el.addEventListener("blur", function () {
|
el.addEventListener('blur', function () {
|
||||||
focused = false;
|
focused = false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -433,58 +449,58 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
|
|
||||||
if (prefs.havePrefs()) {
|
if (prefs.havePrefs()) {
|
||||||
document
|
document
|
||||||
.querySelectorAll<HTMLInputElement>("input[type=checkbox]")
|
.querySelectorAll<HTMLInputElement>('input[type=checkbox]')
|
||||||
.forEach(function (el) {
|
.forEach(function (el) {
|
||||||
var val = prefs.readPref(el.id);
|
const val = prefs.readPref(el.id);
|
||||||
if (val != null) el.checked = JSON.parse(val);
|
if (val != null) el.checked = !!JSON.parse(val);
|
||||||
});
|
});
|
||||||
document
|
document
|
||||||
.querySelectorAll<HTMLInputElement>("input[type=checkbox]")
|
.querySelectorAll<HTMLInputElement>('input[type=checkbox]')
|
||||||
.forEach(function (el) {
|
.forEach(function (el) {
|
||||||
el.addEventListener("change", function () {
|
el.addEventListener('change', function () {
|
||||||
prefs.writePref(el.id, JSON.stringify(el.checked));
|
prefs.writePref(el.id, JSON.stringify(el.checked));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
turbotape = document.querySelector<HTMLInputElement>("#turbo_tape")!.checked;
|
turbotape = document.querySelector<HTMLInputElement>('#turbo_tape')!.checked;
|
||||||
|
|
||||||
Object.keys(window.tapes)
|
Object.keys(window.tapes)
|
||||||
.sort()
|
.sort()
|
||||||
.forEach(function (key) {
|
.forEach(function (key) {
|
||||||
var option = document.createElement("option");
|
const option = document.createElement('option');
|
||||||
option.value = key;
|
option.value = key;
|
||||||
option.text = key;
|
option.text = key;
|
||||||
document.querySelector("#tape_select")!.append(option);
|
document.querySelector('#tape_select')!.append(option);
|
||||||
});
|
});
|
||||||
|
|
||||||
function doTapeSelect() {
|
function doTapeSelect() {
|
||||||
var tapeId =
|
const tapeId =
|
||||||
document.querySelector<HTMLInputElement>("#tape_select")!.value;
|
document.querySelector<HTMLInputElement>('#tape_select')!.value;
|
||||||
var tape = window.tapes[tapeId];
|
const tape = window.tapes[tapeId];
|
||||||
if (!tape) {
|
if (!tape) {
|
||||||
document.querySelector<HTMLInputElement>("#text_input")!.value = "";
|
document.querySelector<HTMLInputElement>('#text_input')!.value = '';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
debug("Loading", tapeId);
|
debug('Loading', tapeId);
|
||||||
|
|
||||||
window.location.hash = tapeId;
|
window.location.hash = tapeId;
|
||||||
reset();
|
reset();
|
||||||
if (turbotape) {
|
if (turbotape) {
|
||||||
var trackIdx = 0,
|
let trackIdx = 0,
|
||||||
script = "";
|
script = '';
|
||||||
var parts = tape.script.split("\n");
|
const parts = tape.script.split('\n');
|
||||||
// Ignore part 0 (C100R)
|
// Ignore part 0 (C100R)
|
||||||
// Split part 1 into ranges
|
// Split part 1 into ranges
|
||||||
var ranges = parts[1].split(" ");
|
const ranges = parts[1].split(' ');
|
||||||
var idx;
|
let idx;
|
||||||
for (idx = 0; idx < ranges.length; idx++) {
|
for (idx = 0; idx < ranges.length; idx++) {
|
||||||
var range = ranges[idx].split(".");
|
const range = ranges[idx].split('.');
|
||||||
var start = parseInt(range[0], 16);
|
const start = parseInt(range[0], 16);
|
||||||
var end = parseInt(range[1], 16);
|
const end = parseInt(range[1], 16);
|
||||||
var track = tape.tracks[trackIdx];
|
const track = tape.tracks[trackIdx];
|
||||||
var kdx = 0;
|
let kdx = 0;
|
||||||
for (var jdx = start; jdx <= end; jdx++) {
|
for (let jdx = start; jdx <= end; jdx++) {
|
||||||
cpu.write(jdx >> 8, jdx & 0xff, track[kdx++]);
|
cpu.write(jdx >> 8, jdx & 0xff, track[kdx++]);
|
||||||
}
|
}
|
||||||
trackIdx++;
|
trackIdx++;
|
||||||
@ -492,29 +508,29 @@ document.addEventListener("DOMContentLoaded", function () {
|
|||||||
// Execute parts 2-n
|
// Execute parts 2-n
|
||||||
for (idx = 2; idx < parts.length; idx++) {
|
for (idx = 2; idx < parts.length; idx++) {
|
||||||
if (parts[idx]) {
|
if (parts[idx]) {
|
||||||
script += parts[idx] + "\n";
|
script += parts[idx] + '\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
document.querySelector<HTMLInputElement>("#text_input")!.value = script;
|
document.querySelector<HTMLInputElement>('#text_input')!.value = script;
|
||||||
} else {
|
} else {
|
||||||
aci.setData(tape.tracks);
|
aci.setData(tape.tracks);
|
||||||
document.querySelector<HTMLInputElement>("#text_input")!.value =
|
document.querySelector<HTMLInputElement>('#text_input')!.value =
|
||||||
tape.script;
|
tape.script;
|
||||||
}
|
}
|
||||||
doLoadText();
|
doLoadText();
|
||||||
}
|
}
|
||||||
document
|
document
|
||||||
.querySelector("#tape_select")!
|
.querySelector('#tape_select')!
|
||||||
.addEventListener("change", doTapeSelect);
|
.addEventListener('change', doTapeSelect);
|
||||||
|
|
||||||
run();
|
run();
|
||||||
setInterval(updateKHz, 1000);
|
setInterval(updateKHz, 1000);
|
||||||
updateScreen();
|
updateScreen();
|
||||||
|
|
||||||
var tape = hup();
|
const tape = hup();
|
||||||
if (tape) {
|
if (tape) {
|
||||||
openLoadText();
|
openLoadText();
|
||||||
document.querySelector<HTMLInputElement>("#tape_select")!.value = tape;
|
document.querySelector<HTMLInputElement>('#tape_select')!.value = tape;
|
||||||
doTapeSelect();
|
doTapeSelect();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
* implied warranty.
|
* implied warranty.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { TextPage } from "./canvas1";
|
import { TextPage } from './canvas1';
|
||||||
import type { byte } from "./types";
|
import type { byte } from './types';
|
||||||
|
|
||||||
const LOC = {
|
const LOC = {
|
||||||
KBD: 0x10,
|
KBD: 0x10,
|
||||||
@ -36,10 +36,11 @@ export default class Apple1IO {
|
|||||||
return 0xd0;
|
return 0xd0;
|
||||||
}
|
}
|
||||||
read(_page: byte, off: byte): byte {
|
read(_page: byte, off: byte): byte {
|
||||||
var result = 0;
|
let result = 0;
|
||||||
off &= 0x13;
|
off &= 0x13;
|
||||||
switch (off) {
|
switch (off) {
|
||||||
case LOC.KBD:
|
case LOC.KBD:
|
||||||
|
{
|
||||||
// Keyboard
|
// Keyboard
|
||||||
const key = this._buffer.shift();
|
const key = this._buffer.shift();
|
||||||
if (key != null) {
|
if (key != null) {
|
||||||
@ -50,6 +51,7 @@ export default class Apple1IO {
|
|||||||
this._keyReady = false;
|
this._keyReady = false;
|
||||||
}
|
}
|
||||||
result |= 0x80;
|
result |= 0x80;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case LOC.KBDRDY:
|
case LOC.KBDRDY:
|
||||||
result = this._keyReady ? 0x80 : 0x00;
|
result = this._keyReady ? 0x80 : 0x00;
|
||||||
@ -96,9 +98,9 @@ export default class Apple1IO {
|
|||||||
this._keyReady = true;
|
this._keyReady = true;
|
||||||
}
|
}
|
||||||
paste(buffer: string) {
|
paste(buffer: string) {
|
||||||
buffer = buffer.replace(/\/\/.*\n/g, "");
|
buffer = buffer.replace(/\/\/.*\n/g, '');
|
||||||
buffer = buffer.replace(/\n/g, "\r");
|
buffer = buffer.replace(/\n/g, '\r');
|
||||||
this._buffer = buffer.split("");
|
this._buffer = buffer.split('');
|
||||||
this._keyReady = true;
|
this._keyReady = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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. */
|
/** Encode an array of bytes in base64. */
|
||||||
export function base64_encode(data: null | undefined): undefined;
|
export function base64_encode(data: null | undefined): undefined;
|
||||||
@ -37,7 +37,7 @@ export function base64_encode(
|
|||||||
bits,
|
bits,
|
||||||
i = 0,
|
i = 0,
|
||||||
ac = 0,
|
ac = 0,
|
||||||
enc = "";
|
enc = '';
|
||||||
const tmp_arr = [];
|
const tmp_arr = [];
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
@ -62,14 +62,14 @@ export function base64_encode(
|
|||||||
B64.charAt(h1) + B64.charAt(h2) + B64.charAt(h3) + B64.charAt(h4);
|
B64.charAt(h1) + B64.charAt(h2) + B64.charAt(h3) + B64.charAt(h4);
|
||||||
} while (i < data.length);
|
} while (i < data.length);
|
||||||
|
|
||||||
enc = tmp_arr.join("");
|
enc = tmp_arr.join('');
|
||||||
|
|
||||||
switch (data.length % 3) {
|
switch (data.length % 3) {
|
||||||
case 1:
|
case 1:
|
||||||
enc = enc.slice(0, -2) + "==";
|
enc = enc.slice(0, -2) + '==';
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
enc = enc.slice(0, -1) + "=";
|
enc = enc.slice(0, -1) + '=';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,11 +147,11 @@ export function base64_decode(
|
|||||||
return new Uint8Array(tmp_arr);
|
return new Uint8Array(tmp_arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
const DATA_URL_PREFIX = "data:application/octet-stream;base64,";
|
const DATA_URL_PREFIX = 'data:application/octet-stream;base64,';
|
||||||
|
|
||||||
export function base64_json_parse(json: string): unknown {
|
export function base64_json_parse(json: string): unknown {
|
||||||
const reviver = (_key: string, value: unknown) => {
|
const reviver = (_key: string, value: unknown) => {
|
||||||
if (typeof value === "string" && value.startsWith(DATA_URL_PREFIX)) {
|
if (typeof value === 'string' && value.startsWith(DATA_URL_PREFIX)) {
|
||||||
return base64_decode(value.slice(DATA_URL_PREFIX.length));
|
return base64_decode(value.slice(DATA_URL_PREFIX.length));
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
|
@ -9,8 +9,8 @@
|
|||||||
* implied warranty.
|
* implied warranty.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { charset } from "./roms/apple1char";
|
import { charset } from './roms/apple1char';
|
||||||
import type { byte } from "./types";
|
import type { byte } from './types';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
0: A9 9 AA 20 EF FF E8 8A 4C 2 0
|
0: A9 9 AA 20 EF FF E8 8A 4C 2 0
|
||||||
@ -40,9 +40,9 @@ export class TextPage {
|
|||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this._buffer = [];
|
this._buffer = [];
|
||||||
for (var row = 0; row < 24; row++) {
|
for (let row = 0; row < 24; row++) {
|
||||||
this._buffer[row] = [];
|
this._buffer[row] = [];
|
||||||
for (var col = 0; col < 40; col++) {
|
for (let col = 0; col < 40; col++) {
|
||||||
this._buffer[row][col] = col % 2 ? 0x00 : 0xff;
|
this._buffer[row][col] = col % 2 ? 0x00 : 0xff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -58,12 +58,12 @@ export class TextPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
write(val: byte): void {
|
write(val: byte): void {
|
||||||
var col;
|
let col;
|
||||||
val &= 0x7f;
|
val &= 0x7f;
|
||||||
|
|
||||||
if (this.transcript) {
|
if (this.transcript) {
|
||||||
if (val == 0xd) {
|
if (val === 0xd) {
|
||||||
this.transcript += "\n";
|
this.transcript += '\n';
|
||||||
} else if (val >= 0x20) {
|
} else if (val >= 0x20) {
|
||||||
if (val >= 0x60) {
|
if (val >= 0x60) {
|
||||||
val &= 0x5f;
|
val &= 0x5f;
|
||||||
@ -72,7 +72,7 @@ export class TextPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (val == 0x0d) {
|
if (val === 0x0d) {
|
||||||
for (col = this._col; col < 40; col++) {
|
for (col = this._col; col < 40; col++) {
|
||||||
this._buffer[this._row][col] = 0x20;
|
this._buffer[this._row][col] = 0x20;
|
||||||
}
|
}
|
||||||
@ -99,15 +99,13 @@ export class TextPage {
|
|||||||
}
|
}
|
||||||
writeAt(row: byte, col: byte, val: byte): void {
|
writeAt(row: byte, col: byte, val: byte): void {
|
||||||
this._buffer[row][col] = val;
|
this._buffer[row][col] = val;
|
||||||
var data = this._page.data,
|
const data = this._page.data;
|
||||||
fore,
|
let color;
|
||||||
back,
|
let off = (col * 14 + row * 560 * 8 * 2) * 4;
|
||||||
color;
|
|
||||||
var off = (col * 14 + row * 560 * 8 * 2) * 4;
|
|
||||||
|
|
||||||
fore = this._greenMode ? this._green : this._white;
|
let fore = this._greenMode ? this._green : this._white;
|
||||||
back = this._black;
|
const back = this._black;
|
||||||
var char = 0;
|
let char = 0;
|
||||||
|
|
||||||
if (!val) {
|
if (!val) {
|
||||||
if (this._blinking) {
|
if (this._blinking) {
|
||||||
@ -118,12 +116,12 @@ export class TextPage {
|
|||||||
char |= val & 0x40 ? 0 : 0x20;
|
char |= val & 0x40 ? 0 : 0x20;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var jdx = 0; jdx < 8; jdx++) {
|
for (let jdx = 0; jdx < 8; jdx++) {
|
||||||
var b = charset[char * 8 + jdx];
|
let b = charset[char * 8 + jdx];
|
||||||
for (var idx = 0; idx < 7; idx += 1) {
|
for (let idx = 0; idx < 7; idx += 1) {
|
||||||
b <<= 1;
|
b <<= 1;
|
||||||
color = b & 0x80 ? fore : back;
|
color = b & 0x80 ? fore : back;
|
||||||
var c0 = color[0],
|
const c0 = color[0],
|
||||||
c1 = color[1],
|
c1 = color[1],
|
||||||
c2 = color[2];
|
c2 = color[2];
|
||||||
data[off + 0] = data[off + 4] = c0;
|
data[off + 0] = data[off + 4] = c0;
|
||||||
@ -144,9 +142,9 @@ export class TextPage {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
_blink() {
|
_blink() {
|
||||||
for (var row = 0; row < 24; row++) {
|
for (let row = 0; row < 24; row++) {
|
||||||
for (var col = 0; col < 40; col++) {
|
for (let col = 0; col < 40; col++) {
|
||||||
var val = this._buffer[row][col];
|
const val = this._buffer[row][col];
|
||||||
if (!val) {
|
if (!val) {
|
||||||
this.writeAt(row, col, val);
|
this.writeAt(row, col, val);
|
||||||
}
|
}
|
||||||
@ -155,8 +153,8 @@ export class TextPage {
|
|||||||
this._dirty = true;
|
this._dirty = true;
|
||||||
}
|
}
|
||||||
refresh() {
|
refresh() {
|
||||||
for (var row = 0; row < 24; row++) {
|
for (let row = 0; row < 24; row++) {
|
||||||
for (var col = 0; col < 40; col++) {
|
for (let col = 0; col < 40; col++) {
|
||||||
this.writeAt(row, col, this._buffer[row][col]);
|
this.writeAt(row, col, this._buffer[row][col]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -186,7 +184,7 @@ export class TextPage {
|
|||||||
setContext(context: CanvasRenderingContext2D) {
|
setContext(context: CanvasRenderingContext2D) {
|
||||||
this._context = context;
|
this._context = context;
|
||||||
this._page = context.createImageData(560, 384);
|
this._page = context.createImageData(560, 384);
|
||||||
for (var idx = 0; idx < 560 * 384 * 4; idx++) {
|
for (let idx = 0; idx < 560 * 384 * 4; idx++) {
|
||||||
this._page.data[idx] = 0xff;
|
this._page.data[idx] = 0xff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -202,24 +200,24 @@ export class TextPage {
|
|||||||
return charCode;
|
return charCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
var buffer = "",
|
let buffer = '',
|
||||||
line,
|
line,
|
||||||
charCode;
|
charCode;
|
||||||
var row, col;
|
let row, col;
|
||||||
for (row = 0; row < 24; row++) {
|
for (row = 0; row < 24; row++) {
|
||||||
line = "";
|
line = '';
|
||||||
for (col = 0; col < 40; col++) {
|
for (col = 0; col < 40; col++) {
|
||||||
charCode = mapCharCode(this._buffer[row][col]);
|
charCode = mapCharCode(this._buffer[row][col]);
|
||||||
line += String.fromCharCode(charCode);
|
line += String.fromCharCode(charCode);
|
||||||
}
|
}
|
||||||
line = line.trimRight();
|
line = line.trimRight();
|
||||||
buffer += line + "\n";
|
buffer += line + '\n';
|
||||||
}
|
}
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
clear() {
|
clear() {
|
||||||
for (var row = 0; row < 24; row++) {
|
for (let row = 0; row < 24; row++) {
|
||||||
for (var col = 0; col < 40; col++) {
|
for (let col = 0; col < 40; col++) {
|
||||||
this._buffer[row][col] = 0x20;
|
this._buffer[row][col] = 0x20;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -228,5 +226,5 @@ export class TextPage {
|
|||||||
this.refresh();
|
this.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
transcript = "";
|
transcript = '';
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import CPU6502 from "../cpu6502";
|
import CPU6502 from '../cpu6502';
|
||||||
import { debug } from "../util";
|
import { debug } from '../util';
|
||||||
import { byte } from "../types";
|
import { byte } from '../types';
|
||||||
|
|
||||||
const rom = [
|
const rom = [
|
||||||
0xa9, 0xaa, 0x20, 0xef, 0xff, 0xa9, 0x8d, 0x20, 0xef, 0xff, 0xa0, 0xff, 0xc8,
|
0xa9, 0xaa, 0x20, 0xef, 0xff, 0xa9, 0x8d, 0x20, 0xef, 0xff, 0xa0, 0xff, 0xc8,
|
||||||
@ -50,19 +50,18 @@ export default class ACI {
|
|||||||
return 0xc1;
|
return 0xc1;
|
||||||
}
|
}
|
||||||
read(page: byte, off: byte) {
|
read(page: byte, off: byte) {
|
||||||
var now = this.cpu.getCycles();
|
const now = this.cpu.getCycles();
|
||||||
var result = rom[off];
|
let result = rom[off];
|
||||||
if (page == 0xc0) {
|
if (page === 0xc0) {
|
||||||
if (this._recording) {
|
if (this._recording) {
|
||||||
var delta = now - this._last;
|
const delta = now - this._last;
|
||||||
this.buffer.push(delta);
|
this.buffer.push(delta);
|
||||||
this._last = now;
|
this._last = now;
|
||||||
} else {
|
} else {
|
||||||
var progress;
|
|
||||||
if (this._readOffset < this.buffer.length) {
|
if (this._readOffset < this.buffer.length) {
|
||||||
if (now > this._next) {
|
if (now > this._next) {
|
||||||
if (this._readOffset % 1000 == 0) {
|
if (this._readOffset % 1000 === 0) {
|
||||||
debug("Read " + this._readOffset / 1000);
|
debug('Read ' + this._readOffset / 1000);
|
||||||
}
|
}
|
||||||
this._flip = !this._flip;
|
this._flip = !this._flip;
|
||||||
this._next = now + this.buffer[this._readOffset++];
|
this._next = now + this.buffer[this._readOffset++];
|
||||||
@ -70,9 +69,9 @@ export default class ACI {
|
|||||||
}
|
}
|
||||||
result = this._flip ? rom[off | 0x01] : rom[off & 0xfe];
|
result = this._flip ? rom[off | 0x01] : rom[off & 0xfe];
|
||||||
|
|
||||||
progress =
|
const progress =
|
||||||
Math.round((this._readOffset / this.buffer.length) * 100) / 100;
|
Math.round((this._readOffset / this.buffer.length) * 100) / 100;
|
||||||
if (this._progress != progress) {
|
if (this._progress !== progress) {
|
||||||
this._progress = progress;
|
this._progress = progress;
|
||||||
this.cb.progress(this._progress);
|
this.cb.progress(this._progress);
|
||||||
}
|
}
|
||||||
@ -83,14 +82,14 @@ export default class ACI {
|
|||||||
case 0x00:
|
case 0x00:
|
||||||
this._recording = false;
|
this._recording = false;
|
||||||
this._beKind = true;
|
this._beKind = true;
|
||||||
debug("Entering ACI CLI");
|
debug('Entering ACI CLI');
|
||||||
break;
|
break;
|
||||||
case 0x63:
|
case 0x63:
|
||||||
if (this._recording) {
|
if (this._recording) {
|
||||||
this.buffer.push(5000000);
|
this.buffer.push(5000000);
|
||||||
this._recording = false;
|
this._recording = false;
|
||||||
}
|
}
|
||||||
debug("Exiting ACI CLI");
|
debug('Exiting ACI CLI');
|
||||||
break;
|
break;
|
||||||
case 0x70: // WRITE
|
case 0x70: // WRITE
|
||||||
this._recording = true;
|
this._recording = true;
|
||||||
@ -98,7 +97,7 @@ export default class ACI {
|
|||||||
this._beKind = false;
|
this._beKind = false;
|
||||||
this.buffer = [];
|
this.buffer = [];
|
||||||
}
|
}
|
||||||
debug("Start write");
|
debug('Start write');
|
||||||
this._last = now;
|
this._last = now;
|
||||||
break;
|
break;
|
||||||
//case 0x7c: // WBITLOOP:
|
//case 0x7c: // WBITLOOP:
|
||||||
@ -107,7 +106,7 @@ export default class ACI {
|
|||||||
// break;
|
// break;
|
||||||
case 0x8d: // READ
|
case 0x8d: // READ
|
||||||
this._recording = false;
|
this._recording = false;
|
||||||
debug("Start read");
|
debug('Start read');
|
||||||
if (this._beKind) {
|
if (this._beKind) {
|
||||||
this._readOffset = 0;
|
this._readOffset = 0;
|
||||||
this._next = now + 5000000;
|
this._next = now + 5000000;
|
||||||
@ -131,7 +130,7 @@ export default class ACI {
|
|||||||
setState() {}
|
setState() {}
|
||||||
|
|
||||||
setData(data: number[][]) {
|
setData(data: number[][]) {
|
||||||
var seg, idx, jdx, d, b;
|
let seg, idx, jdx, d, b;
|
||||||
this.buffer = [];
|
this.buffer = [];
|
||||||
for (seg = 0; seg < data.length; seg++) {
|
for (seg = 0; seg < data.length; seg++) {
|
||||||
for (idx = 0; idx < 16384; idx++) {
|
for (idx = 0; idx < 16384; idx++) {
|
||||||
|
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 {
|
export default class Prefs {
|
||||||
havePrefs() {
|
havePrefs() {
|
||||||
return typeof localStorage !== "undefined";
|
return typeof localStorage !== 'undefined';
|
||||||
}
|
}
|
||||||
readPref(name: string) {
|
readPref(name: string) {
|
||||||
if (localStorage) return localStorage.getItem(name);
|
if (localStorage) return localStorage.getItem(name);
|
||||||
|
10
js/ram.ts
10
js/ram.ts
@ -9,9 +9,9 @@
|
|||||||
* implied warranty.
|
* implied warranty.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { base64_decode, base64_encode } from "./base64";
|
import { base64_decode, base64_encode } from './base64';
|
||||||
import { allocMemPages } from "./util";
|
import { allocMemPages } from './util';
|
||||||
import type { byte } from "./types";
|
import type { byte } from './types';
|
||||||
|
|
||||||
export interface RAMState {
|
export interface RAMState {
|
||||||
start: byte;
|
start: byte;
|
||||||
@ -25,8 +25,8 @@ export default class RAM {
|
|||||||
constructor(private start_page: byte, private end_page: byte) {
|
constructor(private start_page: byte, private end_page: byte) {
|
||||||
this.mem = allocMemPages(end_page - start_page + 1);
|
this.mem = allocMemPages(end_page - start_page + 1);
|
||||||
|
|
||||||
for (var page = 0; page <= end_page; page++) {
|
for (let page = 0; page <= end_page; page++) {
|
||||||
for (var off = 0; off < 0x100; off++) {
|
for (let off = 0; off < 0x100; off++) {
|
||||||
this.mem[page * 0x100 + off] = 0; // Math.floor(Math.random()*256);
|
this.mem[page * 0x100 + off] = 0; // Math.floor(Math.random()*256);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { byte } from "js/types";
|
import { byte } from 'js/types';
|
||||||
|
|
||||||
export default class Basic {
|
export default class Basic {
|
||||||
ram = [
|
ram = [
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { byte } from "js/types";
|
import { byte } from 'js/types';
|
||||||
|
|
||||||
export default class Bios {
|
export default class Bios {
|
||||||
rom = [
|
rom = [
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { byte } from "js/types";
|
import { byte } from 'js/types';
|
||||||
|
|
||||||
export default class EnhancedBasic {
|
export default class EnhancedBasic {
|
||||||
rom = [
|
rom = [
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { byte } from "js/types";
|
import { byte } from 'js/types';
|
||||||
|
|
||||||
export default class Krusader {
|
export default class Krusader {
|
||||||
rom = [
|
rom = [
|
||||||
|
@ -9,16 +9,16 @@
|
|||||||
* implied warranty.
|
* implied warranty.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { address } from "./types";
|
import type { address } from './types';
|
||||||
|
|
||||||
export const SYMBOLS: Record<address, string> = {
|
export const SYMBOLS: Record<address, string> = {
|
||||||
0xd010: "KBD",
|
0xd010: 'KBD',
|
||||||
0xd011: "KBDCR",
|
0xd011: 'KBDCR',
|
||||||
0xd012: "DSP",
|
0xd012: 'DSP',
|
||||||
0xd013: "DSPCR",
|
0xd013: 'DSPCR',
|
||||||
|
|
||||||
0xff1f: "GETLINE",
|
0xff1f: 'GETLINE',
|
||||||
0xffef: "ECHO",
|
0xffef: 'ECHO',
|
||||||
0xffdc: "PRBYTE",
|
0xffdc: 'PRBYTE',
|
||||||
0xffe5: "PRHEX",
|
0xffe5: 'PRHEX',
|
||||||
};
|
};
|
||||||
|
@ -9,11 +9,11 @@
|
|||||||
* implied warranty.
|
* implied warranty.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import CPU6502 from "js/cpu6502";
|
import CPU6502 from 'js/cpu6502';
|
||||||
import { debug, toHex } from "../util";
|
import { debug, toHex } from '../util';
|
||||||
import Apple1IO from "js/apple1io";
|
import Apple1IO from 'js/apple1io';
|
||||||
import { TextPage } from "js/canvas1";
|
import { TextPage } from 'js/canvas1';
|
||||||
import { byte } from "js/types";
|
import { byte } from 'js/types';
|
||||||
|
|
||||||
// keycode: [plain, cntl, shift]
|
// keycode: [plain, cntl, shift]
|
||||||
|
|
||||||
@ -140,29 +140,29 @@ const keymap: Record<byte, readonly byte[]> = {
|
|||||||
|
|
||||||
const keys = [
|
const keys = [
|
||||||
[
|
[
|
||||||
["1", "2", "3", "4", "5", "6", "7", "8", "9", "0", ":", "-", "RESET"],
|
['1', '2', '3', '4', '5', '6', '7', '8', '9', '0', ':', '-', 'RESET'],
|
||||||
["ESC", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "FEED", "RETURN"],
|
['ESC', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', 'FEED', 'RETURN'],
|
||||||
["CTRL", "A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "OUT", "CLS"],
|
['CTRL', 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', 'OUT', 'CLS'],
|
||||||
["SHIFT", "Z", "X", "C", "V", "B", "N", "M", ",", ".", "/", "SHIFT"],
|
['SHIFT', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', ',', '.', '/', 'SHIFT'],
|
||||||
[" "],
|
[' '],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
["!", '"', "#", "$", "%", "&", "'", "(", ")", "0", "*", "=", "RESET"],
|
['!', '"', '#', '$', '%', '&', "'", '(', ')', '0', '*', '=', 'RESET'],
|
||||||
["ESC", "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "@", "LINE", "RETURN"],
|
['ESC', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', '@', 'LINE', 'RETURN'],
|
||||||
["CTRL", "A", "S", "D", "F", "BELL", "H", "J", "K", "L", "+", "RUB", "CLS"],
|
['CTRL', 'A', 'S', 'D', 'F', 'BELL', 'H', 'J', 'K', 'L', '+', 'RUB', 'CLS'],
|
||||||
["SHIFT", "Z", "X", "C", "V", "B", "^", "M", "<", ">", "?", "SHIFT"],
|
['SHIFT', 'Z', 'X', 'C', 'V', 'B', '^', 'M', '<', '>', '?', 'SHIFT'],
|
||||||
[" "],
|
[' '],
|
||||||
],
|
],
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export function mapKeyEvent(evt: KeyboardEvent) {
|
export function mapKeyEvent(evt: KeyboardEvent) {
|
||||||
var code = evt.keyCode;
|
const code = evt.keyCode;
|
||||||
|
|
||||||
if (code in keymap) {
|
if (code in keymap) {
|
||||||
return keymap[code][evt.shiftKey ? 2 : evt.ctrlKey ? 1 : 0];
|
return keymap[code][evt.shiftKey ? 2 : evt.ctrlKey ? 1 : 0];
|
||||||
}
|
}
|
||||||
|
|
||||||
debug("Unhandled key = " + toHex(code));
|
debug('Unhandled key = ' + toHex(code));
|
||||||
return 0xff;
|
return 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,34 +182,34 @@ export class KeyBoard {
|
|||||||
|
|
||||||
shiftKey(down: boolean) {
|
shiftKey(down: boolean) {
|
||||||
this.shifted = down;
|
this.shifted = down;
|
||||||
this.kb.querySelectorAll(".key-SHIFT").forEach(function (el) {
|
this.kb.querySelectorAll('.key-SHIFT').forEach(function (el) {
|
||||||
if (down) {
|
if (down) {
|
||||||
el.classList.add("active");
|
el.classList.add('active');
|
||||||
} else {
|
} else {
|
||||||
el.classList.remove("active");
|
el.classList.remove('active');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
controlKey(down: boolean) {
|
controlKey(down: boolean) {
|
||||||
this.controlled = down;
|
this.controlled = down;
|
||||||
this.kb.querySelectorAll(".key-CTRL").forEach(function (el) {
|
this.kb.querySelectorAll('.key-CTRL').forEach(function (el) {
|
||||||
if (down) {
|
if (down) {
|
||||||
el.classList.add("active");
|
el.classList.add('active');
|
||||||
} else {
|
} else {
|
||||||
el.classList.remove("active");
|
el.classList.remove('active');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
create() {
|
create() {
|
||||||
var x, y, row, key, key1, key2, label, label1, label2;
|
let x, y, row, key, key1, key2, label, label1, label2;
|
||||||
|
|
||||||
function buildLabel(k: string) {
|
function buildLabel(k: string) {
|
||||||
var span = document.createElement("span");
|
const span = document.createElement('span');
|
||||||
span.innerHTML = k;
|
span.innerHTML = k;
|
||||||
if (k.length > 1 && !k.startsWith("&")) {
|
if (k.length > 1 && !k.startsWith('&')) {
|
||||||
span.classList.add("small");
|
span.classList.add('small');
|
||||||
}
|
}
|
||||||
return span;
|
return span;
|
||||||
}
|
}
|
||||||
@ -219,7 +219,7 @@ export class KeyBoard {
|
|||||||
currentTarget: HTMLElement;
|
currentTarget: HTMLElement;
|
||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
event.currentTarget.classList.remove("pressed");
|
event.currentTarget.classList.remove('pressed');
|
||||||
};
|
};
|
||||||
|
|
||||||
const _mousedown = (
|
const _mousedown = (
|
||||||
@ -227,31 +227,31 @@ export class KeyBoard {
|
|||||||
currentTarget: HTMLElement;
|
currentTarget: HTMLElement;
|
||||||
}
|
}
|
||||||
) => {
|
) => {
|
||||||
event.currentTarget.classList.add("pressed");
|
event.currentTarget.classList.add('pressed');
|
||||||
var key = event.currentTarget.dataset[this.shifted ? "key2" : "key1"];
|
let key = event.currentTarget.dataset[this.shifted ? 'key2' : 'key1'];
|
||||||
if (!key) {
|
if (!key) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case "BELL":
|
case 'BELL':
|
||||||
key = "G";
|
key = 'G';
|
||||||
break;
|
break;
|
||||||
case "RETURN":
|
case 'RETURN':
|
||||||
key = "\r";
|
key = '\r';
|
||||||
break;
|
break;
|
||||||
case "LINE":
|
case 'LINE':
|
||||||
case "FEED":
|
case 'FEED':
|
||||||
key = "\n";
|
key = '\n';
|
||||||
break;
|
break;
|
||||||
case "RUB":
|
case 'RUB':
|
||||||
case "OUT":
|
case 'OUT':
|
||||||
key = "_"; // 0x5f
|
key = '_'; // 0x5f
|
||||||
break;
|
break;
|
||||||
case " ":
|
case ' ':
|
||||||
key = " ";
|
key = ' ';
|
||||||
break;
|
break;
|
||||||
case "ESC":
|
case 'ESC':
|
||||||
key = "\0x1b";
|
key = '\0x1b';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -259,31 +259,31 @@ export class KeyBoard {
|
|||||||
|
|
||||||
if (key.length > 1) {
|
if (key.length > 1) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case "SHIFT":
|
case 'SHIFT':
|
||||||
this.shifted = !this.shifted;
|
this.shifted = !this.shifted;
|
||||||
this.kb
|
this.kb
|
||||||
.querySelectorAll(".key-SHIFT")
|
.querySelectorAll('.key-SHIFT')
|
||||||
.forEach(function (el: HTMLElement) {
|
.forEach(function (el: HTMLElement) {
|
||||||
el.classList.toggle("active");
|
el.classList.toggle('active');
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "CTRL":
|
case 'CTRL':
|
||||||
this.controlled = !this.controlled;
|
this.controlled = !this.controlled;
|
||||||
this.kb.querySelectorAll(".key-CTRL").forEach(function (el) {
|
this.kb.querySelectorAll('.key-CTRL').forEach(function (el) {
|
||||||
el.classList.toggle("active");
|
el.classList.toggle('active');
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case "RESET":
|
case 'RESET':
|
||||||
this.cpu.reset();
|
this.cpu.reset();
|
||||||
break;
|
break;
|
||||||
case "CLS":
|
case 'CLS':
|
||||||
this.text.clear();
|
this.text.clear();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (this.controlled && key >= "@" && key <= "_") {
|
if (this.controlled && key >= '@' && key <= '_') {
|
||||||
this.io.keyDown(key.charCodeAt(0) - 0x40);
|
this.io.keyDown(key.charCodeAt(0) - 0x40);
|
||||||
} else {
|
} else {
|
||||||
this.io.keyDown(key.charCodeAt(0));
|
this.io.keyDown(key.charCodeAt(0));
|
||||||
@ -292,46 +292,46 @@ export class KeyBoard {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (y = 0; y < 5; y++) {
|
for (y = 0; y < 5; y++) {
|
||||||
row = document.createElement("div");
|
row = document.createElement('div');
|
||||||
row.classList.add("row", "row" + y);
|
row.classList.add('row', 'row' + y);
|
||||||
this.kb.append(row);
|
this.kb.append(row);
|
||||||
for (x = 0; x < keys[0][y].length; x++) {
|
for (x = 0; x < keys[0][y].length; x++) {
|
||||||
key1 = keys[0][y][x];
|
key1 = keys[0][y][x];
|
||||||
key2 = keys[1][y][x];
|
key2 = keys[1][y][x];
|
||||||
|
|
||||||
label = document.createElement("div");
|
label = document.createElement('div');
|
||||||
label1 = buildLabel(key1);
|
label1 = buildLabel(key1);
|
||||||
label2 = buildLabel(key2);
|
label2 = buildLabel(key2);
|
||||||
|
|
||||||
key = document.createElement("div");
|
key = document.createElement('div');
|
||||||
key.classList.add("key", "key-" + key1.replace(/[&;]/g, ""));
|
key.classList.add('key', 'key-' + key1.replace(/[&;]/g, ''));
|
||||||
|
|
||||||
if (key1.length > 1) {
|
if (key1.length > 1) {
|
||||||
if (key1 != key2) {
|
if (key1 !== key2) {
|
||||||
key.classList.add("vcenter2");
|
key.classList.add('vcenter2');
|
||||||
} else {
|
} else {
|
||||||
key.classList.add("vcenter");
|
key.classList.add('vcenter');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key1 != key2) {
|
if (key1 !== key2) {
|
||||||
key.classList.add("key-" + key2.replace(/[&;]/g, ""));
|
key.classList.add('key-' + key2.replace(/[&;]/g, ''));
|
||||||
label.append(label2);
|
label.append(label2);
|
||||||
label.append(document.createElement("br"));
|
label.append(document.createElement('br'));
|
||||||
}
|
}
|
||||||
label.append(label1);
|
label.append(label1);
|
||||||
key.append(label);
|
key.append(label);
|
||||||
key.dataset["key1"] = key1;
|
key.dataset['key1'] = key1;
|
||||||
key.dataset["key2"] = key2;
|
key.dataset['key2'] = key2;
|
||||||
|
|
||||||
if (window.ontouchstart === undefined) {
|
if (window.ontouchstart === undefined) {
|
||||||
key.addEventListener("mousedown", _mousedown);
|
key.addEventListener('mousedown', _mousedown);
|
||||||
key.addEventListener("mouseup", _mouseup);
|
key.addEventListener('mouseup', _mouseup);
|
||||||
key.addEventListener("mouseout", _mouseup);
|
key.addEventListener('mouseout', _mouseup);
|
||||||
} else {
|
} else {
|
||||||
key.addEventListener("touchstart", _mousedown);
|
key.addEventListener('touchstart', _mousedown);
|
||||||
key.addEventListener("touchend", _mouseup);
|
key.addEventListener('touchend', _mouseup);
|
||||||
key.addEventListener("touchleave", _mouseup);
|
key.addEventListener('touchleave', _mouseup);
|
||||||
}
|
}
|
||||||
|
|
||||||
row.append(key);
|
row.append(key);
|
||||||
|
22
js/util.ts
22
js/util.ts
@ -9,13 +9,13 @@
|
|||||||
* implied warranty.
|
* implied warranty.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { byte, word } from "./types";
|
import { byte, word } from './types';
|
||||||
|
|
||||||
var hex_digits = "0123456789ABCDEF";
|
const hex_digits = '0123456789ABCDEF';
|
||||||
var bin_digits = "01";
|
const bin_digits = '01';
|
||||||
|
|
||||||
export function allocMem(size: word) {
|
export function allocMem(size: word) {
|
||||||
var result;
|
let result;
|
||||||
if (window.Uint8Array) {
|
if (window.Uint8Array) {
|
||||||
result = new Uint8Array(size);
|
result = new Uint8Array(size);
|
||||||
} else {
|
} else {
|
||||||
@ -37,8 +37,8 @@ export function toHex(v: byte, n?: 2 | 4) {
|
|||||||
if (!n) {
|
if (!n) {
|
||||||
n = v < 256 ? 2 : 4;
|
n = v < 256 ? 2 : 4;
|
||||||
}
|
}
|
||||||
var result = "";
|
let result = '';
|
||||||
for (var idx = 0; idx < n; idx++) {
|
for (let idx = 0; idx < n; idx++) {
|
||||||
result = hex_digits[v & 0x0f] + result;
|
result = hex_digits[v & 0x0f] + result;
|
||||||
v >>= 4;
|
v >>= 4;
|
||||||
}
|
}
|
||||||
@ -46,8 +46,8 @@ export function toHex(v: byte, n?: 2 | 4) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function toBinary(v: byte) {
|
export function toBinary(v: byte) {
|
||||||
var result = "";
|
let result = '';
|
||||||
for (var idx = 0; idx < 8; idx++) {
|
for (let idx = 0; idx < 8; idx++) {
|
||||||
result = bin_digits[v & 0x01] + result;
|
result = bin_digits[v & 0x01] + result;
|
||||||
v >>= 1;
|
v >>= 1;
|
||||||
}
|
}
|
||||||
@ -60,8 +60,8 @@ export function gup(name: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function hup() {
|
export function hup() {
|
||||||
var regex = new RegExp("#(.*)");
|
const regex = new RegExp('#(.*)');
|
||||||
var results = regex.exec(window.location.hash);
|
const results = regex.exec(window.location.hash);
|
||||||
if (!results) return "";
|
if (!results) return '';
|
||||||
else return decodeURIComponent(results[1]);
|
else return decodeURIComponent(results[1]);
|
||||||
}
|
}
|
||||||
|
1153
package-lock.json
generated
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/core": "^7.4.0",
|
||||||
"@babel/preset-env": "^7.4.2",
|
"@babel/preset-env": "^7.4.2",
|
||||||
"@types/micromodal": "^0.3.3",
|
"@types/micromodal": "^0.3.3",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.2.1",
|
||||||
"ajv": "^6.9.2",
|
"ajv": "^6.9.2",
|
||||||
"babel-jest": "^29.5.0",
|
"babel-jest": "^29.5.0",
|
||||||
"eslint": "^8.3.0",
|
"eslint": "^8.3.0",
|
||||||
|
"eslint-plugin-jest": "^27.2.3",
|
||||||
"jest": "^29.5.0",
|
"jest": "^29.5.0",
|
||||||
"node-forge": "^1.3.0",
|
"node-forge": "^1.3.0",
|
||||||
"ts-jest": "^29.1.1",
|
"ts-jest": "^29.1.1",
|
||||||
|
3138
test/cpu6502.spec.ts
3138
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) => {
|
export const assertByte = (b: byte) => {
|
||||||
expect(b <= 0xFF).toEqual(true);
|
expect(b <= 0xff).toEqual(true);
|
||||||
expect(b >= 0x00).toEqual(true);
|
expect(b >= 0x00).toEqual(true);
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { MemoryPages, byte } from '../../js/types';
|
import { MemoryPages, byte } from "../../js/types";
|
||||||
import { assertByte } from './asserts';
|
import { assertByte } from "./asserts";
|
||||||
|
|
||||||
export class Program implements MemoryPages {
|
export class Program implements MemoryPages {
|
||||||
private data: Buffer;
|
private data: Buffer;
|
||||||
@ -27,37 +27,30 @@ export class Program implements MemoryPages {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const bios = new Program(0xff, [
|
export const bios = new Program(
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0xff,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
[
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x45, 0x4c, 0x4c,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x4f, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x48, 0x45, 0x4C, 0x4C, 0x4F, 0x0D, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x04, 0x00, 0xff,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
]
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
);
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0xff, 0x00, 0x04, 0x00, 0xff
|
|
||||||
]);
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { MemoryPages, byte, word } from 'js/types';
|
import { MemoryPages, byte, word } from "js/types";
|
||||||
import { assertByte } from './asserts';
|
import { assertByte } from "./asserts";
|
||||||
|
|
||||||
export type Log = [address: word, value: byte, types: 'read'|'write'];
|
export type Log = [address: word, value: byte, types: "read" | "write"];
|
||||||
export class TestMemory implements MemoryPages {
|
export class TestMemory implements MemoryPages {
|
||||||
private data: Buffer;
|
private data: Buffer;
|
||||||
private logging: boolean = false;
|
private logging: boolean = false;
|
||||||
@ -25,7 +25,7 @@ export class TestMemory implements MemoryPages {
|
|||||||
|
|
||||||
const val = this.data[(page << 8) | off];
|
const val = this.data[(page << 8) | off];
|
||||||
if (this.logging) {
|
if (this.logging) {
|
||||||
this.log.push([page << 8 | off, val, 'read']);
|
this.log.push([(page << 8) | off, val, "read"]);
|
||||||
}
|
}
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
@ -36,7 +36,7 @@ export class TestMemory implements MemoryPages {
|
|||||||
assertByte(val);
|
assertByte(val);
|
||||||
|
|
||||||
if (this.logging) {
|
if (this.logging) {
|
||||||
this.log.push([page << 8 | off, val, 'write']);
|
this.log.push([(page << 8) | off, val, "write"]);
|
||||||
}
|
}
|
||||||
this.data[(page << 8) | off] = val;
|
this.data[(page << 8) | off] = val;
|
||||||
}
|
}
|
||||||
@ -58,4 +58,3 @@ export class TestMemory implements MemoryPages {
|
|||||||
return this.log;
|
return this.log;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,9 +9,6 @@ module.exports =
|
|||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
path: path.resolve('dist/'),
|
path: path.resolve('dist/'),
|
||||||
library: 'Apple1',
|
|
||||||
libraryExport: 'Apple1',
|
|
||||||
libraryTarget: 'var'
|
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
|
Loading…
x
Reference in New Issue
Block a user