2023-12-18 20:04:20 +00:00
|
|
|
import {CPU6502, byte} from '@whscullin/cpu6502';
|
|
|
|
import {debug} from '../util';
|
2023-08-06 02:43:26 +00:00
|
|
|
|
2023-11-22 21:52:40 +00:00
|
|
|
// prettier-ignore
|
2023-08-06 02:43:26 +00:00
|
|
|
const rom = [
|
2023-11-22 21:52:40 +00:00
|
|
|
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
|
2023-08-06 02:43:26 +00:00
|
|
|
] as const;
|
|
|
|
|
|
|
|
export interface ACICallback {
|
|
|
|
progress: (value: number) => void;
|
|
|
|
}
|
|
|
|
|
|
|
|
export default class ACI {
|
|
|
|
_last: number;
|
|
|
|
_next: number;
|
|
|
|
_recording = false;
|
|
|
|
_readOffset = 0;
|
|
|
|
_flip = false;
|
|
|
|
_beKind = false;
|
|
|
|
_progress = 0;
|
|
|
|
|
2023-08-19 22:07:00 +00:00
|
|
|
constructor(
|
|
|
|
private cpu: CPU6502,
|
|
|
|
private cb: ACICallback,
|
|
|
|
) {
|
2023-08-06 02:43:26 +00:00
|
|
|
this._last = cpu.getCycles();
|
|
|
|
this._next = this._last;
|
|
|
|
}
|
|
|
|
|
|
|
|
start() {
|
|
|
|
return 0xc0;
|
|
|
|
}
|
|
|
|
end() {
|
|
|
|
return 0xc1;
|
|
|
|
}
|
|
|
|
read(page: byte, off: byte) {
|
2023-08-06 14:32:11 +00:00
|
|
|
const now = this.cpu.getCycles();
|
|
|
|
let result = rom[off];
|
|
|
|
if (page === 0xc0) {
|
2023-08-06 02:43:26 +00:00
|
|
|
if (this._recording) {
|
2023-08-06 14:32:11 +00:00
|
|
|
const delta = now - this._last;
|
2023-08-06 02:43:26 +00:00
|
|
|
this.buffer.push(delta);
|
|
|
|
this._last = now;
|
|
|
|
} else {
|
|
|
|
if (this._readOffset < this.buffer.length) {
|
|
|
|
if (now > this._next) {
|
2023-08-06 14:32:11 +00:00
|
|
|
if (this._readOffset % 1000 === 0) {
|
|
|
|
debug('Read ' + this._readOffset / 1000);
|
2023-08-06 02:43:26 +00:00
|
|
|
}
|
|
|
|
this._flip = !this._flip;
|
|
|
|
this._next = now + this.buffer[this._readOffset++];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
result = this._flip ? rom[off | 0x01] : rom[off & 0xfe];
|
|
|
|
|
2023-08-06 14:32:11 +00:00
|
|
|
const progress =
|
2023-08-06 02:43:26 +00:00
|
|
|
Math.round((this._readOffset / this.buffer.length) * 100) / 100;
|
2023-08-06 14:32:11 +00:00
|
|
|
if (this._progress !== progress) {
|
2023-08-06 02:43:26 +00:00
|
|
|
this._progress = progress;
|
|
|
|
this.cb.progress(this._progress);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (this.cpu.getSync()) {
|
|
|
|
switch (off) {
|
|
|
|
case 0x00:
|
|
|
|
this._recording = false;
|
|
|
|
this._beKind = true;
|
2023-08-06 14:32:11 +00:00
|
|
|
debug('Entering ACI CLI');
|
2023-08-06 02:43:26 +00:00
|
|
|
break;
|
|
|
|
case 0x63:
|
|
|
|
if (this._recording) {
|
|
|
|
this.buffer.push(5000000);
|
|
|
|
this._recording = false;
|
|
|
|
}
|
2023-08-06 14:32:11 +00:00
|
|
|
debug('Exiting ACI CLI');
|
2023-08-06 02:43:26 +00:00
|
|
|
break;
|
|
|
|
case 0x70: // WRITE
|
|
|
|
this._recording = true;
|
|
|
|
if (this._beKind) {
|
|
|
|
this._beKind = false;
|
|
|
|
this.buffer = [];
|
|
|
|
}
|
2023-08-06 14:32:11 +00:00
|
|
|
debug('Start write');
|
2023-08-06 02:43:26 +00:00
|
|
|
this._last = now;
|
|
|
|
break;
|
|
|
|
//case 0x7c: // WBITLOOP:
|
|
|
|
// _debug = true;
|
|
|
|
// debug("Write bit loop");
|
|
|
|
// break;
|
|
|
|
case 0x8d: // READ
|
|
|
|
this._recording = false;
|
2023-08-06 14:32:11 +00:00
|
|
|
debug('Start read');
|
2023-08-06 02:43:26 +00:00
|
|
|
if (this._beKind) {
|
|
|
|
this._readOffset = 0;
|
|
|
|
this._next = now + 5000000;
|
|
|
|
this._beKind = false;
|
|
|
|
|
|
|
|
this.cb.progress(0);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
write() {}
|
|
|
|
|
|
|
|
getState() {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
setState() {}
|
|
|
|
|
|
|
|
setData(data: number[][]) {
|
2023-08-06 14:32:11 +00:00
|
|
|
let seg, idx, jdx, d, b;
|
2023-08-06 02:43:26 +00:00
|
|
|
this.buffer = [];
|
|
|
|
for (seg = 0; seg < data.length; seg++) {
|
|
|
|
for (idx = 0; idx < 16384; idx++) {
|
|
|
|
this.buffer.push(592);
|
|
|
|
}
|
|
|
|
this.buffer.push(180);
|
|
|
|
this.buffer.push(238);
|
|
|
|
d = data[seg];
|
|
|
|
for (idx = 0; idx < d.length; idx++) {
|
|
|
|
b = d[idx];
|
|
|
|
for (jdx = 0; jdx < 8; jdx++) {
|
|
|
|
if (b & 0x80) {
|
|
|
|
this.buffer.push(473);
|
|
|
|
this.buffer.push(473);
|
|
|
|
} else {
|
|
|
|
this.buffer.push(238);
|
|
|
|
this.buffer.push(238);
|
|
|
|
}
|
|
|
|
b <<= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.buffer.push(5000000);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
buffer: byte[] = [];
|
|
|
|
}
|