Use submodules for apple2shader and cpu6502 (#202)

* Use submodules for apple2shader and cpu6502

* Update instructions
This commit is contained in:
Will Scullin 2023-11-22 16:28:40 -08:00 committed by GitHub
parent eab7de7db3
commit e7891114c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 3215 additions and 6086 deletions

View File

@ -12,6 +12,8 @@ jobs:
steps:
- uses: actions/checkout@v2
with:
submodules: "true"
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:

6
.gitmodules vendored Normal file
View File

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

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
sit submodule update
npm install
```

View File

@ -17,11 +17,12 @@ import {
} from './gl';
import ROM from './roms/rom';
import { Apple2IOState } from './apple2io';
import CPU6502, {
import {
CPU6502,
CpuState,
FLAVOR_6502,
FLAVOR_ROCKWELL_65C02,
} from './cpu6502';
} from '@whscullin/cpu6502';
import MMU, { MMUState } from './mmu';
import RAM, { RAMState } from './ram';

View File

@ -1,4 +1,4 @@
import CPU6502 from './cpu6502';
import { CPU6502 } from '@whscullin/cpu6502';
import { Card, Memory, MemoryPages, TapeData, byte, Restorable } from './types';
import { debug, garbage } from './util';
import { VideoModes } from './videomodes';

View File

@ -1,5 +1,5 @@
import { Card, byte, word, Restorable } from '../types';
import CPU6502, { CpuState } from '../cpu6502';
import { CPU6502, CpuState } from '@whscullin/cpu6502';
import { debug } from '../util';
import { rom } from '../roms/cards/mouse';

View File

@ -2,7 +2,7 @@ import { debug, toHex } from '../util';
import { rom as smartPortRom } from '../roms/cards/smartport';
import { Card, Restorable, byte, word, rom } from '../types';
import { MassStorage, BlockDisk, ENCODING_BLOCK, BlockFormat, MassStorageData, DiskFormat } from '../formats/types';
import CPU6502, { CpuState, flags } from '../cpu6502';
import { CPU6502, CpuState, flags } from '@whscullin/cpu6502';
import { create2MGFromBlockDisk, HeaderData, read2MGHeader } from '../formats/2mg';
import createBlockDisk from '../formats/block';
import { DriveNumber } from '../formats/types';

View File

@ -4,7 +4,7 @@ import Disk2, { Callbacks } from '../cards/disk2';
import Apple2IO from '../apple2io';
import { DiskII, DiskIIData } from './DiskII';
import SmartPort from 'js/cards/smartport';
import CPU6502 from 'js/cpu6502';
import { CPU6502 } from '@whscullin/cpu6502';
import { BlockDisk } from './BlockDisk';
import { ErrorModal } from './ErrorModal';
import { ProgressModal } from './ProgressModal';

View File

@ -1,4 +1,4 @@
import CPU6502 from 'js/cpu6502';
import { CPU6502 } from '@whscullin/cpu6502';
import { Memory } from 'js/types';
import { useEffect } from 'preact/hooks';
import Apple2IO, { slot } from '../apple2io';

View File

@ -2,7 +2,7 @@ import { RefObject } from 'preact';
import Apple2IO, { slot } from '../apple2io';
import { MouseUI } from '../ui/mouse';
import MouseCard from '../cards/mouse';
import CPU6502 from '../cpu6502';
import { CPU6502 } from '@whscullin/cpu6502';
import { useEffect } from 'preact/hooks';
/**

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
import { debug, toHex } from './util';
import { byte, word } from './types';
import CPU6502, { DebugInfo, flags, sizes } from './cpu6502';
import { CPU6502, DebugInfo, flags, sizes } from '@whscullin/cpu6502';
export interface DebuggerContainer {
run: () => void;

View File

@ -1,4 +1,4 @@
import CPU6502 from './cpu6502';
import { CPU6502 } from '@whscullin/cpu6502';
import RAM, { RAMState } from './ram';
import ROM, { ROMState } from './roms/rom';
import { debug } from './util';

View File

@ -27,7 +27,7 @@ import { TXTTAB } from 'js/applesoft/zeropage';
import { debug } from '../util';
import { Apple2, Stats, State as Apple2State } from '../apple2';
import DiskII from '../cards/disk2';
import CPU6502 from '../cpu6502';
import { CPU6502 } from '@whscullin/cpu6502';
import { VideoModes } from '../videomodes';
import Apple2IO from '../apple2io';
import Printer from './printer';

View File

@ -1,6 +1,6 @@
import { byte, DeepMemberOf, KnownKeys } from '../types';
import Apple2IO from '../apple2io';
import CPU6502 from '../cpu6502';
import { CPU6502 } from '@whscullin/cpu6502';
import { debug, toHex } from '../util';
// keycode: [plain, cntl, shift]

4516
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -72,7 +72,8 @@
"y18n": "^4.0.1"
},
"dependencies": {
"apple2shader": "0.0.4",
"@whscullin/cpu6502": "file:submodules/cpu6502",
"apple2shader": "file:submodules/apple2shader",
"classnames": "^2.3.1",
"micromodal": "^0.4.2",
"preact": "^10.7.1"

@ -0,0 +1 @@
Subproject commit c9a649db689c31e04d342021db5a39cd86337a24

1
submodules/cpu6502 Submodule

@ -0,0 +1 @@
Subproject commit 4e1031d1bfeaa930b95292269e02d5c31a3440a8

View File

@ -1,284 +0,0 @@
/**
* Tom Harte test data based test suite
*
* Uses test files from https://github.com/TomHarte/ProcessorTests
* To use, set TOM_HARTE_TEST_PATH to local copy of that repository
*/
import fs from 'fs';
import CPU6502, { FLAVOR_ROCKWELL_65C02, FLAVOR_WDC_65C02 } from 'js/cpu6502';
import { toHex } from 'js/util';
import type { byte, word } from 'js/types';
import { toReadableState } from './util/cpu';
import { TestMemory } from './util/memory';
// JEST_DETAIL=true converts decimal values to hex before testing
// expectations, for better readability at the expense of speed.
const detail = !!process.env.JEST_DETAIL;
/**
* Types for JSON tests
*/
/**
* Memory address and value
*/
type MemoryValue = [address: word, value: byte];
/**
* Represents initial and final CPU and memory states
*/
interface TestState {
/** Program counter */
pc: word;
/** Stack register */
s: byte;
/** Accumulator */
a: byte;
/** X index */
x: byte;
/** Y index */
y: byte;
/** Processor status register */
p: byte;
/** M */
ram: MemoryValue[];
}
/**
* CPU cycle memory operation
*/
type Cycle = [address: word, value: byte, type: 'read'|'write'];
/**
* One test record
*/
interface Test {
/** Test name */
name: string;
/** Initial CPU register and memory state */
initial: TestState;
/** Final CPU register and memory state */
final: TestState;
/** Detailed CPU cycles */
cycles: Cycle[];
}
/**
* Initialize cpu and memory before test
*
* @param cpu Target cpu instance
* @param state Initial test state
*/
function initState(cpu: CPU6502, state: TestState) {
const { pc, s, a, x, y, p, ram } = state;
cpu.setState({ cycles: 0, pc, sp: s, a, x, y, s: p });
for (let idx = 0; idx < ram.length; idx++) {
const [address, mem] = ram[idx];
cpu.write(address, mem);
}
}
/**
* Pretty print 'address: val' if detail is turned on,
* or passes through raw test data if not.
*
* @returns string or raw test data
*/
function toAddrValHex([address, val]: MemoryValue) {
if (detail) {
return `${toHex(address, 4)}: ${toHex(val)}`;
} else {
return [address, val];
}
}
/**
* Pretty print 'address: val (read|write)' if detail is turned on,
* or passes through raw test data if not.
*
* @returns string or raw test data
*/
function toAddrValHexType([address, val, type]: Cycle) {
if (detail) {
return `${toHex(address, 4)}: ${toHex(val)} ${type}`;
} else {
return [address, val, type];
}
}
/**
* Compare end state and read write behavior of test run
*
* @param cpu Test CPU
* @param memory Test memory
* @param test Test data to compare against
*/
function expectState(cpu: CPU6502, memory: TestMemory, test: Test) {
const { pc, s, a, x, y, p, ram } = test.final;
expect(
toReadableState(cpu.getState())
).toEqual(
toReadableState({cycles: test.cycles.length, pc, sp: s, a, x, y, s: p })
);
// Retrieve relevant memory locations and values
const result = [];
for (let idx = 0; idx < ram.length; idx++) {
const [address] = ram[idx];
result.push([address, cpu.read(address)]);
}
expect(
result.map(toAddrValHex)
).toEqual(
ram.map(toAddrValHex)
);
expect(
memory.getLog().map(toAddrValHexType)
).toEqual(
test.cycles.map(toAddrValHexType)
);
}
interface OpTest {
op: string;
name: string;
mode: string;
}
const testPath = process.env.TOM_HARTE_TEST_PATH;
// There are 10,0000 tests per test file, which would take several hours
// in jest. 16 is a manageable quantity that still gets good coverage.
const maxTests = 16;
if (testPath) {
const testPath6502 = `${testPath}/6502/v1/`;
const testPathWDC65C02 = `${testPath}/wdc65c02/v1/`;
const testPathRW65C02 = `${testPath}/rockwell65c02/v1/`;
const opAry6502: OpTest[] = [];
const opAryRW65C02: OpTest[] = [];
const opAryWDC65C02: OpTest[] = [];
const buildOpArrays = () => {
const cpu = new CPU6502();
for (const op in cpu.OPS_6502) {
const { name, mode } = cpu.OPS_6502[op];
const test = { op: toHex(+op), name, mode };
opAry6502.push(test);
opAryRW65C02.push(test);
opAryWDC65C02.push(test);
}
for (const op in cpu.OPS_NMOS_6502) {
const { name, mode } = cpu.OPS_NMOS_6502[op];
const test = { op: toHex(+op), name, mode };
opAry6502.push(test);
}
for (const op in cpu.OPS_65C02) {
const { name, mode } = cpu.OPS_65C02[op];
const test = { op: toHex(+op), name, mode };
opAryRW65C02.push(test);
opAryWDC65C02.push(test);
}
// WDC 65C02 NOPs
[
'03', '0b', '13', '1b', '23', '2b', '33', '3b',
'43', '4b', '53', '5b', '63', '6b', '73', '7b',
'83', '8b', '93', '9b', 'a3', 'ab', 'b3', 'bb',
'c3', 'd3', 'e3', 'eb', 'f3', 'fb'
].forEach((op) =>
opAryWDC65C02.push({ op, name: 'nop', mode: 'implied'})
);
// Rockwell 65C02 NOPs
[
'03', '0b', '13', '1b', '23', '2b', '33', '3b',
'43', '4b', '53', '5b', '63', '6b', '73', '7b',
'83', '8b', '93', '9b', 'a3', 'ab', 'b3', 'bb',
'c3', 'cb', 'd3', 'db', 'e3', 'eb', 'f3', 'fb'
].forEach((op) =>
opAryRW65C02.push({ op, name: 'nop', mode: 'implied'})
);
};
buildOpArrays();
describe('Tom Harte', function() {
let cpu: CPU6502;
let memory: TestMemory;
describe('NMOS 6502', function() {
beforeAll(function() {
cpu = new CPU6502();
memory = new TestMemory(256);
cpu.addPageHandler(memory);
});
describe.each(opAry6502)('Test op $op $name $mode', ({op}) => {
const data = fs.readFileSync(`${testPath6502}${op}.json`, 'utf-8');
const tests = JSON.parse(data) as Test[];
it.each(tests.slice(0, maxTests))('Test $name', (test) => {
initState(cpu, test.initial);
memory.logStart();
cpu.step();
memory.logStop();
expectState(cpu, memory, test);
});
});
});
describe('Rockwell 65C02', function() {
beforeAll(function() {
cpu = new CPU6502({ flavor: FLAVOR_ROCKWELL_65C02 });
memory = new TestMemory(256);
cpu.addPageHandler(memory);
});
describe.each(opAryRW65C02)('Test op $op $name $mode', ({op}) => {
const data = fs.readFileSync(`${testPathRW65C02}${op}.json`, 'utf-8');
const tests = JSON.parse(data) as Test[];
it.each(tests.slice(0, maxTests))('Test $name', (test) => {
initState(cpu, test.initial);
memory.logStart();
cpu.step();
memory.logStop();
expectState(cpu, memory, test);
});
});
});
describe('WDC 65C02', function() {
beforeAll(function() {
cpu = new CPU6502({ flavor: FLAVOR_WDC_65C02 });
memory = new TestMemory(256);
cpu.addPageHandler(memory);
});
describe.each(opAryWDC65C02)('Test op $op $name $mode', ({op}) => {
const data = fs.readFileSync(`${testPathWDC65C02}${op}.json`, 'utf-8');
const tests = JSON.parse(data) as Test[];
it.each(tests.slice(0, maxTests))('Test $name', (test) => {
initState(cpu, test.initial);
memory.logStart();
cpu.step();
memory.logStop();
expectState(cpu, memory, test);
});
});
});
});
} else {
test('Skipping Tom Harte tests', () => { expect(testPath).toBeFalsy(); });
}

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

View File

@ -3,7 +3,7 @@ import fs from 'fs';
import Apple2IO from 'js/apple2io';
import DiskII, { Callbacks } from 'js/cards/disk2';
import CPU6502 from 'js/cpu6502';
import { CPU6502 } from '@whscullin/cpu6502';
import { DriveNumber, NibbleDisk, WozDisk } from 'js/formats/types';
import { byte } from 'js/types';
import { toHex } from 'js/util';

View File

@ -1,4 +1,4 @@
import CPU6502 from '../../js/cpu6502';
import { CPU6502 } from '@whscullin/cpu6502';
import Debugger, { DebuggerContainer } from '../../js/debugger';
import { MemoryPages } from '../../js/types';
import { TestMemory } from '../util/memory';

View File

@ -1,6 +1,6 @@
import Apple2IO from 'js/apple2io';
import MMU from '../../js/mmu';
import CPU6502 from 'js/cpu6502';
import { CPU6502 } from '@whscullin/cpu6502';
import { HiresPage, LoresPage, VideoModes, VideoPage } from 'js/videomodes';
import Apple2eROM from '../../js/roms/system/apple2e';
import { MemoryPages } from 'js/types';
@ -48,7 +48,7 @@ describe('MMU', () => {
it('requires prewrite to write to bank1', () => {
const mmu = new MMU(fakeCPU, fakeVideoModes, fakeLoResPage1, fakeLoResPage2,
fakeHiResPage1, fakeHiResPage2, fakeApple2IO, new Apple2eROM());
// From https://github.com/whscullin/apple2js/issues/187
// Action descriptions from Sather, Table 5.5, p. 5-24, UtAIIe:
mmu._access(0x8b); // WRTCOUNT = WRTCOUNT + 1, READ ENABLE
@ -63,7 +63,7 @@ describe('MMU', () => {
it('prewrite is reset on write access before write', () => {
const mmu = new MMU(fakeCPU, fakeVideoModes, fakeLoResPage1, fakeLoResPage2,
fakeHiResPage1, fakeHiResPage2, fakeApple2IO, new Apple2eROM());
// Action descriptions from Sather, Table 5.5, p. 5-24, UtAIIe:
mmu._access(0x89, 0x00); // WRTCOUNT = 0, READ DISABLE
mmu._access(0x8b); // WRTCOUNT = WRTCOUNT + 1, READ ENABLE (write not enabled yet)
@ -77,7 +77,7 @@ describe('MMU', () => {
it('write stays active with overzealous switching', () => {
const mmu = new MMU(fakeCPU, fakeVideoModes, fakeLoResPage1, fakeLoResPage2,
fakeHiResPage1, fakeHiResPage2, fakeApple2IO, new Apple2eROM());
// Action descriptions from Sather, Table 5.5, p. 5-24, UtAIIe:
mmu._access(0x8b); // WRTCOUNT = WRTCOUNT + 1, READ ENABLE
mmu._access(0x8b); // WRTCOUNT = WRTCOUNT + 1, READ ENABLE (write enabled)
@ -87,4 +87,4 @@ describe('MMU', () => {
mmu._access(0x8b); // WRTCOUNT = WRTCOUNT + 1, READ ENABLE (write still enabled)
expect(mmu.read(0xd0, 0x00)).toBe(0xa1);
});
});
});

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,4 +1,4 @@
import type { CpuState } from 'js/cpu6502';
import type { CpuState } from '@whscullin/cpu6502';
import { toHex } from 'js/util';
import { dumpStatusRegister } from 'js/debugger';