Use submodule for cpu6502

This commit is contained in:
Will Scullin 2023-11-22 13:52:40 -08:00
parent 6e49066162
commit b5dd61b88e
No known key found for this signature in database
GPG Key ID: 26DCD1042C6638CD
30 changed files with 766 additions and 7410 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "submodules/cpu6502"]
path = submodules/cpu6502
url = https://github.com/whscullin/cpu6502

View File

@ -9,6 +9,8 @@ Things are still a little rough around the edges right now, hopefully I will hav
First
```sh
git submodule init
git submodule update
npm install
```

View File

@ -12,7 +12,7 @@
import MicroModal from 'micromodal';
import Apple1IO from './apple1io';
import CPU6502 from './cpu6502';
import { CPU6502, word, byte } from '@whscullin/cpu6502';
import Prefs from './prefs';
import RAM from './ram';
import { TextPage } from './canvas1';
@ -25,7 +25,6 @@ import Krusader from './roms/krusader';
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;
@ -197,7 +196,7 @@ export function setTurboTape(val: boolean) {
turbotape = val;
}
function run(pc?: address) {
function run(pc?: word) {
if (runTimer) {
clearInterval(runTimer);
}
@ -364,7 +363,7 @@ export function doLoadText() {
lines.forEach(function (line) {
const parts = line.split(': ');
if (parts.length === 2) {
let addr: address = 0;
let addr: word = 0;
if (parts[0].length > 0) {
addr = parseInt(parts[0], 16);
}

View File

@ -10,7 +10,7 @@
*/
import { TextPage } from './canvas1';
import type { byte } from './types';
import type { byte } from '@whscullin/cpu6502';
const LOC = {
KBD: 0x10,

View File

@ -1,4 +1,4 @@
import { memory } from './types';
import { memory } from '@whscullin/cpu6502';
const B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';

View File

@ -10,7 +10,7 @@
*/
import { charset } from './roms/apple1char';
import type { byte } from './types';
import type { byte } from '@whscullin/cpu6502';
/*
0: A9 9 AA 20 EF FF E8 8A 4C 2 0

View File

@ -1,28 +1,40 @@
import CPU6502 from '../cpu6502';
import { CPU6502, byte } from '@whscullin/cpu6502';
import { debug } from '../util';
import { byte } from '../types';
// prettier-ignore
const rom = [
0xa9, 0xaa, 0x20, 0xef, 0xff, 0xa9, 0x8d, 0x20, 0xef, 0xff, 0xa0, 0xff, 0xc8,
0xad, 0x11, 0xd0, 0x10, 0xfb, 0xad, 0x10, 0xd0, 0x99, 0x00, 0x02, 0x20, 0xef,
0xff, 0xc9, 0x9b, 0xf0, 0xe1, 0xc9, 0x8d, 0xd0, 0xe9, 0xa2, 0xff, 0xa9, 0x00,
0x85, 0x24, 0x85, 0x25, 0x85, 0x26, 0x85, 0x27, 0xe8, 0xbd, 0x00, 0x02, 0xc9,
0xd2, 0xf0, 0x56, 0xc9, 0xd7, 0xf0, 0x35, 0xc9, 0xae, 0xf0, 0x27, 0xc9, 0x8d,
0xf0, 0x20, 0xc9, 0xa0, 0xf0, 0xe8, 0x49, 0xb0, 0xc9, 0x0a, 0x90, 0x06, 0x69,
0x88, 0xc9, 0xfa, 0x90, 0xad, 0x0a, 0x0a, 0x0a, 0x0a, 0xa0, 0x04, 0x0a, 0x26,
0x24, 0x26, 0x25, 0x88, 0xd0, 0xf8, 0xf0, 0xcc, 0x4c, 0x1a, 0xff, 0xa5, 0x24,
0x85, 0x26, 0xa5, 0x25, 0x85, 0x27, 0xb0, 0xbf, 0xa9, 0x40, 0x20, 0xcc, 0xc1,
0x88, 0xa2, 0x00, 0xa1, 0x26, 0xa2, 0x10, 0x0a, 0x20, 0xdb, 0xc1, 0xd0, 0xfa,
0x20, 0xf1, 0xc1, 0xa0, 0x1e, 0x90, 0xec, 0xa6, 0x28, 0xb0, 0x98, 0x20, 0xbc,
0xc1, 0xa9, 0x16, 0x20, 0xcc, 0xc1, 0x20, 0xbc, 0xc1, 0xa0, 0x1f, 0x20, 0xbf,
0xc1, 0xb0, 0xf9, 0x20, 0xbf, 0xc1, 0xa0, 0x3a, 0xa2, 0x08, 0x48, 0x20, 0xbc,
0xc1, 0x68, 0x2a, 0xa0, 0x39, 0xca, 0xd0, 0xf5, 0x81, 0x26, 0x20, 0xf1, 0xc1,
0xa0, 0x35, 0x90, 0xea, 0xb0, 0xcd, 0x20, 0xbf, 0xc1, 0x88, 0xad, 0x81, 0xc0,
0xc5, 0x29, 0xf0, 0xf8, 0x85, 0x29, 0xc0, 0x80, 0x60, 0x86, 0x28, 0xa0, 0x42,
0x20, 0xe0, 0xc1, 0xd0, 0xf9, 0x69, 0xfe, 0xb0, 0xf5, 0xa0, 0x1e, 0x20, 0xe0,
0xc1, 0xa0, 0x2c, 0x88, 0xd0, 0xfd, 0x90, 0x05, 0xa0, 0x2f, 0x88, 0xd0, 0xfd,
0xbc, 0x00, 0xc0, 0xa0, 0x29, 0xca, 0x60, 0xa5, 0x26, 0xc5, 0x24, 0xa5, 0x27,
0xe5, 0x25, 0xe6, 0x26, 0xd0, 0x02, 0xe6, 0x27, 0x60,
0xA9,0xAA,0x20,0xEF,0xFF,0xA9,0x8D,0x20,
0xEF,0xFF,0xA0,0xFF,0xC8,0xAD,0x11,0xD0,
0x10,0xFB,0xAD,0x10,0xD0,0x99,0x00,0x02,
0x20,0xEF,0xFF,0xC9,0x9B,0xF0,0xE1,0xC9,
0x8D,0xD0,0xE9,0xA2,0xFF,0xA9,0x00,0x85,
0x24,0x85,0x25,0x85,0x26,0x85,0x27,0xE8,
0xBD,0x00,0x02,0xC9,0xD2,0xF0,0x56,0xC9,
0xD7,0xF0,0x35,0xC9,0xAE,0xF0,0x27,0xC9,
0x8D,0xF0,0x20,0xC9,0xA0,0xF0,0xE8,0x49,
0xB0,0xC9,0x0A,0x90,0x06,0x69,0x88,0xC9,
0xFA,0x90,0xAD,0x0A,0x0A,0x0A,0x0A,0xA0,
0x04,0x0A,0x26,0x24,0x26,0x25,0x88,0xD0,
0xF8,0xF0,0xCC,0x4C,0x1A,0xFF,0xA5,0x24,
0x85,0x26,0xA5,0x25,0x85,0x27,0xB0,0xBF,
0xA9,0x40,0x20,0xCC,0xC1,0x88,0xA2,0x00,
0xA1,0x26,0xA2,0x10,0x0A,0x20,0xDB,0xC1,
0xD0,0xFA,0x20,0xF1,0xC1,0xA0,0x1E,0x90,
0xEC,0xA6,0x28,0xB0,0x98,0x20,0xBC,0xC1,
0xA9,0x16,0x20,0xCC,0xC1,0x20,0xBC,0xC1,
0xA0,0x1F,0x20,0xBF,0xC1,0xB0,0xF9,0x20,
0xBF,0xC1,0xA0,0x3A,0xA2,0x08,0x48,0x20,
0xBC,0xC1,0x68,0x2A,0xA0,0x39,0xCA,0xD0,
0xF5,0x81,0x26,0x20,0xF1,0xC1,0xA0,0x35,
0x90,0xEA,0xB0,0xCD,0x20,0xBF,0xC1,0x88,
0xAD,0x81,0xC0,0xC5,0x29,0xF0,0xF8,0x85,
0x29,0xC0,0x80,0x60,0x86,0x28,0xA0,0x42,
0x20,0xE0,0xC1,0xD0,0xF9,0x69,0xFE,0xB0,
0xF5,0xA0,0x1E,0x20,0xE0,0xC1,0xA0,0x2C,
0x88,0xD0,0xFD,0x90,0x05,0xA0,0x2F,0x88,
0xD0,0xFD,0xBC,0x00,0xC0,0xA0,0x29,0xCA,
0x60,0xA5,0x26,0xC5,0x24,0xA5,0x27,0xE5,
0x25,0xE6,0x26,0xD0,0x02,0xE6,0x27,0x60
] as const;
export interface ACICallback {

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@
import { base64_decode, base64_encode } from './base64';
import { allocMemPages } from './util';
import type { byte } from './types';
import type { byte, memory } from '@whscullin/cpu6502';
export interface RAMState {
start: byte;
@ -20,7 +20,7 @@ export interface RAMState {
}
export default class RAM {
mem: Uint8Array | byte[];
mem: memory;
constructor(
private start_page: byte,

View File

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

View File

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

View File

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

View File

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

View File

@ -9,9 +9,9 @@
* implied warranty.
*/
import type { address } from './types';
import type { word } from '@whscullin/cpu6502';
export const SYMBOLS: Record<address, string> = {
export const SYMBOLS: Record<word, string> = {
0xd010: 'KBD',
0xd011: 'KBDCR',
0xd012: 'DSP',

View File

@ -1,43 +0,0 @@
/* Copyright 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.
*/
export type byte = number;
export type word = number;
export type address = word;
export type memory = Uint8Array | byte[];
export interface Memory {
/** Read a byte. */
read(page: byte, offset: byte): byte;
/** Write a byte. */
write(page: byte, offset: byte, value: byte): void;
}
/** A mapped region of memory. */
export interface MemoryPages extends Memory {
/** Start page. */
start(): byte;
/** End page, inclusive. */
end(): byte;
}
/**
* Extracts the members of a constant array as a type. Used as:
*
* @example
* const SOME_VALUES = ['a', 'b', 1, 2] as const;
* type SomeValues = MemberOf<typeof SOME_VALUES>; // 'a' | 'b' | 1 | 2
*/
export type MemberOf<T extends ReadonlyArray<unknown>> =
T extends ReadonlyArray<infer E> ? E : never;

View File

@ -9,11 +9,10 @@
* implied warranty.
*/
import CPU6502 from 'js/cpu6502';
import { CPU6502, byte } from '@whscullin/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]

View File

@ -9,12 +9,12 @@
* implied warranty.
*/
import { byte, word } from './types';
import { byte, word } from '@whscullin/cpu6502';
const hex_digits = '0123456789ABCDEF';
const bin_digits = '01';
export function allocMem(size: word) {
export function allocMem(size: word): Uint8Array | byte[] {
let result;
if (window.Uint8Array) {
result = new Uint8Array(size);

1253
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -47,6 +47,7 @@
"webpack-dev-server": "^4.6.0"
},
"dependencies": {
"@whscullin/cpu6502": "file:submodules/cpu6502",
"micromodal": "^0.4.9"
}
}

1
submodules/cpu6502 Submodule

@ -0,0 +1 @@
Subproject commit 208e761911a0088a5bafbdbc2c82de8110a92992

View File

@ -1,48 +0,0 @@
import CPU6502, { FLAVOR_ROCKWELL_65C02 } from '../js/cpu6502';
// From https://github.com/Klaus2m5/6502_65C02_functional_tests
import Test6502 from './roms/6502test';
import Test65C02 from './roms/65C02test';
import { toHex } from '../js/util';
describe('CPU', function () {
let cpu: CPU6502;
let lastPC = 0;
let done = false;
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);
do {
cpu.stepCyclesDebug(1000, traceCB);
} while (!done);
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);
do {
cpu.stepCyclesDebug(1000, traceCB);
} while (!done);
expect(toHex(lastPC)).toEqual(toHex(0x24f1));
});
});
});

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,29 +0,0 @@
// From https://github.com/Klaus2m5/6502_65C02_functional_tests
import fs from 'fs';
import path from 'path';
import { MemoryPages, byte } from '../../js/types';
export default class Test6502 implements MemoryPages {
private data: Buffer;
constructor() {
this.data = fs.readFileSync(path.join(__dirname, '6502_functional_test.bin'));
}
start = () => {
return 0x00;
};
end = () => {
return 0xff;
};
read = (page: byte, off: byte) => {
return this.data[page << 8 | off];
};
write = (page: byte, off: byte, val: byte) => {
this.data[page << 8 | off] = val;
};
}

View File

@ -1,29 +0,0 @@
// From https://github.com/Klaus2m5/6502_65C02_functional_tests
import fs from 'fs';
import path from 'path';
import { MemoryPages, byte } from '../../js/types';
export default class Test65C02 implements MemoryPages {
private data: Buffer;
constructor() {
this.data = fs.readFileSync(path.join(__dirname, '65C02_extended_opcodes_test.bin'));
}
start = () => {
return 0x00;
};
end = () => {
return 0xff;
};
read = (page: byte, off: byte) => {
return this.data[page << 8 | off];
};
write = (page: byte, off: byte, val: byte) => {
this.data[page << 8 | off] = val;
};
}

View File

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

View File

@ -1,56 +0,0 @@
import { MemoryPages, byte } from "../../js/types";
import { assertByte } from "./asserts";
export class Program implements MemoryPages {
private data: Buffer;
constructor(private page: byte, code: byte[]) {
this.data = Buffer.from(code);
}
start() {
return this.page;
}
end() {
return this.page;
}
read(page: byte, off: byte) {
assertByte(page);
assertByte(off);
return this.data[off];
}
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,
]
);

View File

@ -1,34 +0,0 @@
import { flags, CpuState } from "js/cpu6502";
import { byte } from "js/types";
import { toHex } from "js/util";
export const dumpStatusRegister = (sr: byte) =>
[
sr & flags.N ? "N" : "-",
sr & flags.V ? "V" : "-",
sr & flags.X ? "X" : "-",
sr & flags.B ? "B" : "-",
sr & flags.D ? "D" : "-",
sr & flags.I ? "I" : "-",
sr & flags.Z ? "Z" : "-",
sr & flags.C ? "C" : "-",
].join("");
const detail = !!process.env.JEST_DETAIL;
export function toReadableState(state: CpuState) {
if (detail) {
const { pc, sp, a, x, y, s } = state;
return {
pc: toHex(pc, 4),
sp: toHex(sp),
a: toHex(a),
x: toHex(x),
y: toHex(y),
s: dumpStatusRegister(s),
};
} else {
return state;
}
}

View File

@ -1,60 +0,0 @@
import { MemoryPages, byte, word } from "js/types";
import { assertByte } from "./asserts";
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[] = [];
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;
}
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;
}
}