mirror of
https://github.com/whscullin/apple2js.git
synced 2024-01-12 14:14:38 +00:00
Merge fad720ddf6
into af57378852
This commit is contained in:
commit
4f4f1427a0
91
bin/bin2js
Executable file
91
bin/bin2js
Executable file
|
@ -0,0 +1,91 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const { exit } = require('process');
|
||||
|
||||
const readFile = require('fs').promises.readFile;
|
||||
const argv = require('yargs').argv;
|
||||
|
||||
const fileName = argv._[0];
|
||||
const name = argv.n || argv.name;
|
||||
const start = argv.s !== undefined ? argv.s : argv.start;
|
||||
|
||||
function toHex(v, n) {
|
||||
if (!n) {
|
||||
n = 2
|
||||
}
|
||||
let r = v.toString(16);
|
||||
r = r.padStart(n, '0');
|
||||
return r;
|
||||
}
|
||||
|
||||
function usage(message) {
|
||||
if (message) {
|
||||
console.error(message);
|
||||
}
|
||||
console.error("bin2js -n name -s start binfile");
|
||||
}
|
||||
|
||||
if (argv.h || argv.help) {
|
||||
usage();
|
||||
process.exit(0);
|
||||
}
|
||||
if (!name || !fileName || (typeof name != "string")) {
|
||||
usage("must specify a file name for binfile");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (start === "" || start === undefined) {
|
||||
usage("must specify a start page");
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
if (typeof start === "string") {
|
||||
if (start.startsWith("0x")) {
|
||||
start = parseInt(start.slice(2), 16);
|
||||
} else {
|
||||
start = parseInt(start, 10);
|
||||
}
|
||||
}
|
||||
|
||||
readFile(fileName, { flag: 'r' }).then((fileData => {
|
||||
if (fileData.length % 256 != 0) {
|
||||
console.error(`${filename} length is not a multiple of 256`);
|
||||
process.exit(2);
|
||||
}
|
||||
let end = start + fileData.length / 256 - 1;
|
||||
console.log('const MEMORY = [');
|
||||
const step = 0x08;
|
||||
for (let i = 0; i < fileData.length; i += step) {
|
||||
let line = ' ';
|
||||
for (let j = i; j < fileData.length && j < i + step; j++) {
|
||||
line += '0x' + toHex(fileData[j]) + ', ';
|
||||
}
|
||||
line += '// ' + toHex(i, 4);
|
||||
console.log(line);
|
||||
}
|
||||
console.log('];');
|
||||
console.log();
|
||||
console.log(`export default function ${name}() {`);
|
||||
console.log(' let mem = [...MEMORY];');
|
||||
console.log(' return {');
|
||||
console.log(' start: function() {');
|
||||
console.log(` return 0x${toHex(start)};`);
|
||||
console.log(' },');
|
||||
console.log(' end: function() {');
|
||||
console.log(` return 0x${toHex(end)};`);
|
||||
console.log(' },');
|
||||
console.log(' read: function(page, off) {');
|
||||
console.log(` return mem[(page - 0x${toHex(start)}) << 8 | off];`);
|
||||
console.log(' },');
|
||||
console.log(' write: function(page, off, val) {');
|
||||
console.log(` mem[(page - 0x${toHex(start)}) << 8 | off] = val;`);
|
||||
console.log(' },');
|
||||
console.log(' reset: function() {');
|
||||
console.log(' mem = [...MEMORY];');
|
||||
console.log(' },');
|
||||
console.log(' };');
|
||||
console.log('}');
|
||||
})).catch((reason) => {
|
||||
console.error('Unable to read binary file:', reason);
|
||||
process.exit(1);
|
||||
});
|
80
test/perf/cpu_benchmark.html
Normal file
80
test/perf/cpu_benchmark.html
Normal file
|
@ -0,0 +1,80 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Benchmark for 6502</title>
|
||||
<script async="false" type="module" src="/dist/cpu_benchmark.js"></script>
|
||||
<script>
|
||||
window.addEventListener('load', async (e) => {
|
||||
await window.benchmark();
|
||||
console.log('done');
|
||||
});
|
||||
</script>
|
||||
<style>
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
empty-cells: hide;
|
||||
}
|
||||
|
||||
tr.bottom-header {
|
||||
border-bottom: 2px solid black;
|
||||
}
|
||||
|
||||
th.right-header {
|
||||
border-right: 2px solid black;
|
||||
border-top: 1px solid gray;
|
||||
}
|
||||
|
||||
td {
|
||||
text-align: right;
|
||||
border-top: 1px solid gray;
|
||||
padding: 4px 10px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Benchmark for 6502</h1>
|
||||
|
||||
<table id="benchmarks">
|
||||
<thead>
|
||||
<tr id="impl">
|
||||
<th></th>
|
||||
<th colspan="2">cpu6502.js</th>
|
||||
</tr>
|
||||
<tr class="bottom-header" id="emul">
|
||||
<th></th>
|
||||
<th>6502</th>
|
||||
<th>65C02</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr id="min">
|
||||
<th class="right-header">min</th>
|
||||
<td id="min_js_6502"></td>
|
||||
<td id="min_js_65C02"></td>
|
||||
</tr>
|
||||
<tr id="max">
|
||||
<th class="right-header">max</th>
|
||||
<td id="max_js_6502"></td>
|
||||
<td id="max_js_65C02"></td>
|
||||
</tr>
|
||||
<tr id="mean">
|
||||
<th class="right-header">mean</th>
|
||||
<td id="mean_js_6502"></td>
|
||||
<td id="mean_js_65C02"></td>
|
||||
</tr>
|
||||
<tr id="median">
|
||||
<th class="right-header">median</th>
|
||||
<td id="median_js_6502"></td>
|
||||
<td id="median_js_65C02"></td>
|
||||
</tr>
|
||||
<tr id="runs">
|
||||
<th class="right-header">runs</th>
|
||||
<td id="runs_js_6502"></td>
|
||||
<td id="runs_js_65C02"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
|
||||
</html>
|
239
test/perf/cpu_benchmark.js
Normal file
239
test/perf/cpu_benchmark.js
Normal file
|
@ -0,0 +1,239 @@
|
|||
import JSCPU6502 from './impl/jscpu6502';
|
||||
import TSCPU6502 from './impl/tscpu6502';
|
||||
import TSCPU6502v2 from './impl/tscpu6502v2';
|
||||
import TSCPU6502v5 from './impl/tscpu6502v5';
|
||||
import TSCPU6502v6 from './impl/tscpu6502v6';
|
||||
import Test6502 from './test6502rom';
|
||||
import Test65C02 from './test65c02rom';
|
||||
|
||||
let cpu;
|
||||
let memory;
|
||||
let done;
|
||||
let lastPC;
|
||||
let callbacks = 0;
|
||||
|
||||
function traceCB() {
|
||||
let pc = cpu.getPC();
|
||||
done = (lastPC == pc) || (pc < 0x100);
|
||||
lastPC = pc;
|
||||
callbacks++;
|
||||
}
|
||||
|
||||
function setup6502(cpuType) {
|
||||
cpu = new cpuType();
|
||||
memory = new Test6502();
|
||||
cpu.addPageHandler(memory);
|
||||
}
|
||||
|
||||
function setup65C02(cpuType) {
|
||||
cpu = new cpuType({ '65C02': true });
|
||||
memory = new Test65C02();
|
||||
cpu.addPageHandler(memory);
|
||||
}
|
||||
|
||||
function runCPU(stopPC, maxCallbacks) {
|
||||
done = false;
|
||||
lastPC = 0x0000;
|
||||
callbacks = 0;
|
||||
cpu.reset();
|
||||
memory.reset();
|
||||
cpu.setPC(0x400);
|
||||
|
||||
do {
|
||||
cpu.stepCyclesDebug(1000, traceCB);
|
||||
} while (!done && callbacks <= maxCallbacks);
|
||||
if (cpu.getPC() < 0x100) {
|
||||
console.log('PC in zero page');
|
||||
}
|
||||
if (cpu.getPC() != stopPC) {
|
||||
console.log(`stop PC incorrect: ${cpu.getPC().toString(16)} != ${stopPC.toString(16)}`);
|
||||
}
|
||||
if (callbacks > maxCallbacks) {
|
||||
console.log('too many callbacks');
|
||||
}
|
||||
}
|
||||
|
||||
const tests = [
|
||||
{
|
||||
impl: 'cpu6502.js',
|
||||
emul: '6502',
|
||||
setup: () => setup6502(JSCPU6502),
|
||||
test: () => runCPU(0x3469, 30648245),
|
||||
},
|
||||
{
|
||||
impl: 'cpu6502.js',
|
||||
emul: '65C02',
|
||||
setup: () => setup65C02(JSCPU6502),
|
||||
test: () => runCPU(0x24f1, 21987280),
|
||||
},
|
||||
{
|
||||
impl: 'cpu6502.ts',
|
||||
emul: '6502',
|
||||
setup: () => setup6502(TSCPU6502),
|
||||
test: () => runCPU(0x3469, 30648245),
|
||||
},
|
||||
{
|
||||
impl: 'cpu6502.ts',
|
||||
emul: '65C02',
|
||||
setup: () => setup65C02(TSCPU6502),
|
||||
test: () => runCPU(0x24f1, 21987280),
|
||||
},
|
||||
{
|
||||
impl: 'cpu6502v2.ts',
|
||||
emul: '6502',
|
||||
setup: () => setup6502(TSCPU6502v2),
|
||||
test: () => runCPU(0x3469, 30648245),
|
||||
},
|
||||
{
|
||||
impl: 'cpu6502v2.ts',
|
||||
emul: '65C02',
|
||||
setup: () => setup65C02(TSCPU6502v2),
|
||||
test: () => runCPU(0x24f1, 21987280),
|
||||
},
|
||||
{
|
||||
impl: 'cpu6502v5.ts',
|
||||
emul: '6502',
|
||||
setup: () => setup6502(TSCPU6502v5),
|
||||
test: () => runCPU(0x3469, 30648245),
|
||||
},
|
||||
// {
|
||||
// impl: 'cpu6502v5.ts',
|
||||
// emul: '6502',
|
||||
// setup: () => setup65C02(TSCPU6502v5),
|
||||
// test: () => runCPU(0x24f1, 21987280),
|
||||
// },
|
||||
{
|
||||
impl: 'cpu6502v6.ts',
|
||||
emul: '6502',
|
||||
setup: () => setup6502(TSCPU6502v6),
|
||||
test: () => runCPU(0x3469, 30648245),
|
||||
},
|
||||
{
|
||||
impl: 'cpu6502v6.ts',
|
||||
emul: '65C02',
|
||||
setup: () => setup65C02(TSCPU6502v6),
|
||||
test: () => runCPU(0x24f1, 21987280),
|
||||
},
|
||||
];
|
||||
|
||||
const IMPLS = [...new Set(tests.map((e) => e.impl))];
|
||||
|
||||
|
||||
const RUNS = 50;
|
||||
const WARMUP = 10;
|
||||
|
||||
function pause() {
|
||||
return new Promise(resolve => setTimeout(resolve, 0));
|
||||
}
|
||||
|
||||
function round(x, n) {
|
||||
return Math.round(x * Math.pow(10, n)) / Math.pow(10, n);
|
||||
}
|
||||
|
||||
function shuffle(/** @type Array<*> */ a) {
|
||||
for (let i = a.length - 1; i > 1; i--) {
|
||||
let j = Math.floor(Math.random() * (i + 1));
|
||||
if (j !== i) {
|
||||
let t = a[i];
|
||||
a[i] = a[j];
|
||||
a[j] = t;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ensureSibling(
|
||||
/** @type Element */ e,
|
||||
/** @type function(): Element */ f) {
|
||||
if (!e.nextElementSibling) {
|
||||
e.parentElement.appendChild(f());
|
||||
}
|
||||
return e.nextElementSibling;
|
||||
}
|
||||
|
||||
function clearSiblings(/** @type Element */ e) {
|
||||
while (e.nextElementSibling) {
|
||||
e.parentElement.removeChild(e.nextElementSibling);
|
||||
}
|
||||
}
|
||||
|
||||
function expandTable(
|
||||
/** @type Element */ e,
|
||||
/** @type string */ name,
|
||||
/** @type number */ i) {
|
||||
e = ensureSibling(e, () => document.createElement("td"));
|
||||
e.setAttribute("id", name + "_" + i + "_6502");
|
||||
e = ensureSibling(e, () => document.createElement("td"));
|
||||
e.setAttribute("id", name + "_" + i + "_65C02");
|
||||
return e;
|
||||
}
|
||||
|
||||
function buildTable() {
|
||||
let table = document.getElementById("benchmarks");
|
||||
let implElement = document.getElementById("impl").firstElementChild;
|
||||
let emulElement = document.getElementById("emul").firstElementChild;
|
||||
let minElement = document.getElementById("min").firstElementChild;
|
||||
let maxElement = document.getElementById("max").firstElementChild;
|
||||
let meanElement = document.getElementById("mean").firstElementChild;
|
||||
let medianElement = document.getElementById("median").firstElementChild;
|
||||
let runsElement = document.getElementById("runs").firstElementChild;
|
||||
|
||||
for (let i = 0; i < IMPLS.length; i++) {
|
||||
let impl = IMPLS[i];
|
||||
implElement = ensureSibling(implElement, () => document.createElement("th"));
|
||||
implElement.setAttribute("colspan", "2");
|
||||
implElement.innerText = impl;
|
||||
|
||||
emulElement = ensureSibling(emulElement, () => document.createElement("th"));
|
||||
emulElement.innerText = "6502";
|
||||
emulElement = ensureSibling(emulElement, () => document.createElement("th"));
|
||||
emulElement.innerText = "65C02";
|
||||
|
||||
minElement = expandTable(minElement, "min", i);
|
||||
maxElement = expandTable(maxElement, "max", i);
|
||||
meanElement = expandTable(meanElement, "mean", i);
|
||||
medianElement = expandTable(medianElement, "median", i);
|
||||
runsElement = expandTable(runsElement, "runs", i);
|
||||
}
|
||||
clearSiblings(implElement);
|
||||
}
|
||||
|
||||
function name(test) {
|
||||
return test.impl + " " + test.emul;
|
||||
}
|
||||
|
||||
export async function benchmark() {
|
||||
console.log('benchmark');
|
||||
buildTable();
|
||||
console.log('table built');
|
||||
await pause();
|
||||
let stats = {};
|
||||
for (let i = 0; i < tests.length; i++) {
|
||||
console.log('setting up', name(tests[i]));
|
||||
tests[i].setup();
|
||||
console.log('starting warmup of', name(tests[i]));
|
||||
for (let w = 0; w < WARMUP; w++) {
|
||||
tests[i].test();
|
||||
await pause();
|
||||
}
|
||||
console.log('running ', name(tests[i]));
|
||||
let runs = [];
|
||||
for (let r = 0; r < RUNS; r++) {
|
||||
let start = performance.now();
|
||||
tests[i].test();
|
||||
let end = performance.now();
|
||||
let delta = round(end - start, 2);
|
||||
runs.push(delta);
|
||||
runs.sort((a, b) => a - b);
|
||||
let id = IMPLS.indexOf(tests[i].impl) + "_" + tests[i].emul;
|
||||
document.getElementById('min_' + id).innerText = runs[0];
|
||||
document.getElementById('max_' + id).innerText = runs[runs.length - 1];
|
||||
document.getElementById('median_' + id).innerText = runs[Math.floor(runs.length / 2)];
|
||||
document.getElementById('mean_' + id).innerText = round(runs.reduce((a, b) => (a + b)) / runs.length, 2);
|
||||
document.getElementById('runs_' + id).innerText = runs.length;
|
||||
await pause();
|
||||
}
|
||||
console.log(`${name(tests[i])} runs = `, runs);
|
||||
}
|
||||
}
|
||||
|
||||
window.benchmark = benchmark;
|
1633
test/perf/impl/jscpu6502.js
Normal file
1633
test/perf/impl/jscpu6502.js
Normal file
File diff suppressed because it is too large
Load Diff
1759
test/perf/impl/tscpu6502.ts
Normal file
1759
test/perf/impl/tscpu6502.ts
Normal file
File diff suppressed because it is too large
Load Diff
1772
test/perf/impl/tscpu6502v2.ts
Normal file
1772
test/perf/impl/tscpu6502v2.ts
Normal file
File diff suppressed because it is too large
Load Diff
1979
test/perf/impl/tscpu6502v5.ts
Normal file
1979
test/perf/impl/tscpu6502v5.ts
Normal file
File diff suppressed because it is too large
Load Diff
1718
test/perf/impl/tscpu6502v6.ts
Normal file
1718
test/perf/impl/tscpu6502v6.ts
Normal file
File diff suppressed because it is too large
Load Diff
8215
test/perf/test6502rom.js
Normal file
8215
test/perf/test6502rom.js
Normal file
File diff suppressed because it is too large
Load Diff
8215
test/perf/test65c02rom.js
Normal file
8215
test/perf/test65c02rom.js
Normal file
File diff suppressed because it is too large
Load Diff
2240
test/tscpu6502.spec.js
Normal file
2240
test/tscpu6502.spec.js
Normal file
File diff suppressed because it is too large
Load Diff
2240
test/tscpu6502v2.spec.js
Normal file
2240
test/tscpu6502v2.spec.js
Normal file
File diff suppressed because it is too large
Load Diff
2240
test/tscpu6502v6.spec.js
Normal file
2240
test/tscpu6502v6.spec.js
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -6,7 +6,8 @@ module.exports =
|
|||
mode: 'development',
|
||||
entry: {
|
||||
main2: path.resolve('js/entry2.js'),
|
||||
main2e: path.resolve('js/entry2e.js')
|
||||
main2e: path.resolve('js/entry2e.js'),
|
||||
cpu_benchmark: path.resolve('test/perf/cpu_benchmark.js')
|
||||
},
|
||||
output: {
|
||||
path: path.resolve('dist/'),
|
||||
|
|
Loading…
Reference in New Issue
Block a user