8bitworkshop/gen/common/baseplatform.js

943 lines
35 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Base6809MachinePlatform = exports.BaseZ80MachinePlatform = exports.Base6502MachinePlatform = exports.BaseMachinePlatform = exports.hasSerialIO = exports.hasBIOS = exports.hasProbe = exports.isRaster = exports.hasPaddleInput = exports.hasJoyInput = exports.hasKeyInput = exports.hasAudio = exports.hasVideo = exports.lookupSymbol = exports.dumpStackToString = exports.Base6809Platform = exports.getToolForFilename_6809 = exports.cpuStateToLongString_6809 = exports.getToolForFilename_z80 = exports.BaseZ80Platform = exports.cpuStateToLongString_Z80 = exports.getOpcodeMetadata_6502 = exports.cpuStateToLongString_6502 = exports.Base6502Platform = exports.getToolForFilename_6502 = exports.inspectSymbol = exports.BaseDebugPlatform = exports.BasePlatform = exports.BreakpointList = exports.isDebuggable = exports.DebugSymbols = void 0;
const emu_1 = require("./emu");
const util_1 = require("./util");
const disasm6502_1 = require("./cpu/disasm6502");
const disasmz80_1 = require("./cpu/disasmz80");
const ZilogZ80_1 = require("./cpu/ZilogZ80");
const audio_1 = require("./audio");
const probe_1 = require("./probe");
const wasmplatform_1 = require("./wasmplatform");
const _6809_1 = require("./cpu/6809");
const MOS6502_1 = require("./cpu/MOS6502");
;
;
class DebugSymbols {
constructor(symbolmap, debuginfo) {
this.symbolmap = symbolmap;
this.debuginfo = debuginfo;
this.addr2symbol = (0, util_1.invertMap)(symbolmap);
//// TODO: shouldn't be necc.
if (!this.addr2symbol[0x0])
this.addr2symbol[0x0] = '$00'; // needed for ...
this.addr2symbol[0x10000] = '__END__'; // ... dump memory to work
}
}
exports.DebugSymbols = DebugSymbols;
function isDebuggable(arg) {
return arg && typeof arg.getDebugCategories === 'function';
}
exports.isDebuggable = isDebuggable;
// for composite breakpoints w/ single debug function
class BreakpointList {
constructor() {
this.id2bp = {};
}
getDebugCondition() {
if (Object.keys(this.id2bp).length == 0) {
return null; // no breakpoints
}
else {
// evaluate all breakpoints
return () => {
var result = false;
for (var id in this.id2bp)
if (this.id2bp[id].cond())
result = true;
return result;
};
}
}
}
exports.BreakpointList = BreakpointList;
;
/////
class BasePlatform {
constructor() {
this.recorder = null;
this.internalFiles = {};
}
setRecorder(recorder) {
this.recorder = recorder;
}
updateRecorder() {
// are we recording and do we need to save a frame?
if (this.recorder && this.isRunning() && this.recorder.frameRequested()) {
this.recorder.recordFrame(this.saveState());
}
}
inspect(sym) {
return inspectSymbol(this, sym);
}
getDebugTree() {
var _a;
var o = {};
o.state = this.saveState();
if ((_a = this.debugSymbols) === null || _a === void 0 ? void 0 : _a.debuginfo)
o.debuginfo = this.debugSymbols.debuginfo;
return o;
}
readFile(path) {
return this.internalFiles[path];
}
writeFile(path, data) {
this.internalFiles[path] = data;
return true;
}
}
exports.BasePlatform = BasePlatform;
class BaseDebugPlatform extends BasePlatform {
constructor() {
super(...arguments);
this.debugSavedState = null;
this.debugBreakState = null;
this.debugTargetClock = 0;
this.debugClock = 0;
this.breakpoints = new BreakpointList();
this.frameCount = 0;
}
setBreakpoint(id, cond) {
if (cond) {
this.breakpoints.id2bp[id] = { cond: cond };
this.restartDebugging();
}
else {
this.clearBreakpoint(id);
}
}
clearBreakpoint(id) {
delete this.breakpoints.id2bp[id];
}
hasBreakpoint(id) {
return this.breakpoints.id2bp[id] != null;
}
getDebugCallback() {
return this.breakpoints.getDebugCondition();
}
setupDebug(callback) {
this.onBreakpointHit = callback;
}
clearDebug() {
if (this.debugBreakState != null && this.debugSavedState != null) {
this.loadState(this.debugSavedState);
}
this.debugSavedState = null;
this.debugBreakState = null;
this.debugTargetClock = -1;
this.debugClock = 0;
this.onBreakpointHit = null;
this.clearBreakpoint('debug');
this.frameCount = 0;
}
setDebugCondition(debugCond) {
this.setBreakpoint('debug', debugCond);
}
resetDebugging() {
if (this.debugSavedState) {
this.loadState(this.debugSavedState);
}
else {
this.debugSavedState = this.saveState();
}
this.debugClock = 0;
this.debugCallback = this.getDebugCallback();
this.debugBreakState = null;
}
restartDebugging() {
this.resetDebugging();
this.resume();
}
preFrame() {
// save state before frame, to record any inputs that happened pre-frame
if (this.debugCallback && !this.debugBreakState) {
// save state every frame and rewind debug clocks
this.debugSavedState = this.saveState();
this.debugTargetClock -= this.debugClock;
this.debugClock = 0;
}
}
postFrame() {
// reload debug state at end of frame after breakpoint
if (this.debugCallback && this.debugBreakState) {
this.loadState(this.debugBreakState);
}
this.frameCount++;
}
pollControls() {
}
nextFrame(novideo) {
this.pollControls();
this.updateRecorder();
this.preFrame();
var steps = this.advance(novideo);
this.postFrame();
return steps;
}
evalDebugCondition() {
if (this.debugCallback && !this.debugBreakState) {
this.debugCallback();
}
}
wasBreakpointHit() {
return this.debugBreakState != null;
}
breakpointHit(targetClock, reason) {
console.log(this.debugTargetClock, targetClock, this.debugClock, this.isStable());
this.debugTargetClock = targetClock;
this.debugBreakState = this.saveState();
console.log("Breakpoint at clk", this.debugClock, "PC", this.debugBreakState.c.PC.toString(16));
this.pause();
if (this.onBreakpointHit) {
this.onBreakpointHit(this.debugBreakState, reason);
}
}
haltAndCatchFire(reason) {
this.breakpointHit(this.debugClock, reason);
}
runEval(evalfunc) {
this.setDebugCondition(() => {
if (++this.debugClock >= this.debugTargetClock && this.isStable()) {
var cpuState = this.getCPUState();
if (evalfunc(cpuState)) {
this.breakpointHit(this.debugClock);
return true;
}
else {
return false;
}
}
});
}
runToPC(pc) {
this.debugTargetClock++;
this.runEval((c) => {
return c.PC == pc;
});
}
runUntilReturn() {
var SP0 = this.getSP();
this.runEval((c) => {
return c.SP > SP0; // TODO: check for RTS/RET opcode
});
}
runToFrameClock(clock) {
this.restartDebugging();
this.debugTargetClock = clock;
this.runEval(() => { return true; });
}
step() {
this.runToFrameClock(this.debugClock + 1);
}
stepBack() {
var prevState;
var prevClock;
var clock0 = this.debugTargetClock;
this.restartDebugging();
this.debugTargetClock = clock0 - 25; // TODO: depends on CPU
this.runEval((c) => {
if (this.debugClock < clock0) {
prevState = this.saveState();
prevClock = this.debugClock;
return false;
}
else {
if (prevState) {
this.loadState(prevState);
this.debugClock = prevClock;
}
return true;
}
});
}
runToVsync() {
this.restartDebugging();
var frame0 = this.frameCount;
this.runEval(() => {
return this.frameCount > frame0;
});
}
}
exports.BaseDebugPlatform = BaseDebugPlatform;
function inspectSymbol(platform, sym) {
if (!platform.debugSymbols)
return;
var symmap = platform.debugSymbols.symbolmap;
var addr2sym = platform.debugSymbols.addr2symbol;
if (!symmap || !platform.readAddress)
return null;
var addr = symmap["_" + sym] || symmap[sym]; // look for C or asm symbol
if (!(typeof addr == 'number'))
return null;
var b = platform.readAddress(addr);
// don't show 2 bytes if there's a symbol at the next address
if (addr2sym && addr2sym[addr + 1] != null) {
return "$" + (0, util_1.hex)(addr, 4) + " = $" + (0, util_1.hex)(b, 2) + " (" + b + " decimal)"; // unsigned
}
else {
let b2 = platform.readAddress(addr + 1);
let w = b | (b2 << 8);
return "$" + (0, util_1.hex)(addr, 4) + " = $" + (0, util_1.hex)(b, 2) + " $" + (0, util_1.hex)(b2, 2) + " (" + ((w << 16) >> 16) + " decimal)"; // signed
}
}
exports.inspectSymbol = inspectSymbol;
////// 6502
function getToolForFilename_6502(fn) {
if (fn.endsWith("-llvm.c"))
return "remote:llvm-mos";
if (fn.endsWith(".c"))
return "cc65";
if (fn.endsWith(".h"))
return "cc65";
if (fn.endsWith(".s"))
return "ca65";
if (fn.endsWith(".ca65"))
return "ca65";
if (fn.endsWith(".dasm"))
return "dasm";
if (fn.endsWith(".acme"))
return "acme";
if (fn.endsWith(".wiz"))
return "wiz";
if (fn.endsWith(".ecs"))
return "ecs";
return "dasm"; // .a
}
exports.getToolForFilename_6502 = getToolForFilename_6502;
// TODO: can merge w/ Z80?
class Base6502Platform extends BaseDebugPlatform {
constructor() {
super(...arguments);
// some platforms store their PC one byte before or after the first opcode
// so we correct when saving and loading from state
this.debugPCDelta = -1;
this.getToolForFilename = getToolForFilename_6502;
}
fixPC(c) { c.PC = (c.PC + this.debugPCDelta) & 0xffff; return c; }
unfixPC(c) { c.PC = (c.PC - this.debugPCDelta) & 0xffff; return c; }
getSP() { return this.getCPUState().SP; }
;
getPC() { return this.getCPUState().PC; }
;
isStable() { return !this.getCPUState()['T']; }
newCPU(membus) {
var cpu = new MOS6502_1._MOS6502();
cpu.connectBus(membus);
return cpu;
}
getOpcodeMetadata(opcode, offset) {
return getOpcodeMetadata_6502(opcode, offset);
}
getOriginPC() {
return (this.readAddress(0xfffc) | (this.readAddress(0xfffd) << 8)) & 0xffff;
}
disassemble(pc, read) {
return (0, disasm6502_1.disassemble6502)(pc, read(pc), read(pc + 1), read(pc + 2));
}
getDefaultExtension() { return ".a"; }
;
getDebugCategories() {
return ['CPU', 'ZPRAM', 'Stack'];
}
getDebugInfo(category, state) {
switch (category) {
case 'CPU': return cpuStateToLongString_6502(state.c);
case 'ZPRAM': return (0, emu_1.dumpRAM)(state.b || state.ram, 0x0, 0x100);
case 'Stack': return dumpStackToString(this, state.b || state.ram, 0x100, 0x1ff, 0x100 + state.c.SP, 0x20);
}
}
}
exports.Base6502Platform = Base6502Platform;
function cpuStateToLongString_6502(c) {
function decodeFlags(c) {
var s = "";
s += c.N ? " N" : " -";
s += c.V ? " V" : " -";
s += c.D ? " D" : " -";
s += c.Z ? " Z" : " -";
s += c.C ? " C" : " -";
s += c.I ? " I" : " -";
return s;
}
return "PC " + (0, util_1.hex)(c.PC, 4) + " " + decodeFlags(c) + "\n"
+ " A " + (0, util_1.hex)(c.A) + " " + (c.R ? "" : "BUSY") + "\n"
+ " X " + (0, util_1.hex)(c.X) + "\n"
+ " Y " + (0, util_1.hex)(c.Y) + " " + "SP " + (0, util_1.hex)(c.SP) + "\n";
}
exports.cpuStateToLongString_6502 = cpuStateToLongString_6502;
var OPMETA_6502 = {
cycletime: [
7, 6, 0, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6, 2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 0, 7, 4, 4, 7, 7, 6, 6, 0, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6, 2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 0, 7, 4, 4, 7, 7, 6, 6, 0, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6, 2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 0, 7, 4, 4, 7, 7, 6, 6, 0, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6, 2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 0, 7, 4, 4, 7, 7, 0, 6, 0, 6, 3, 3, 3, 3, 2, 0, 2, 0, 4, 4, 4, 4, 2, 6, 0, 0, 4, 4, 4, 4, 2, 5, 2, 0, 0, 5, 0, 0, 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 0, 4, 4, 4, 4, 2, 5, 0, 5, 4, 4, 4, 4, 2, 4, 2, 0, 4, 4, 4, 4, 2, 6, 0, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 3, 6, 2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 0, 7, 4, 4, 7, 7, 2, 6, 0, 8, 3, 3, 5, 5, 2, 2, 2, 0, 4, 4, 6, 6, 2, 5, 0, 8, 4, 4, 6, 6, 2, 4, 0, 7, 4, 4, 7, 7
],
extracycles: [
0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1
],
insnlengths: [
1, 2, 0, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3, 2, 2, 0, 2, 2, 2, 2, 2, 1, 3, 0, 3, 3, 3, 3, 3, 3, 2, 0, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3, 2, 2, 0, 2, 2, 2, 2, 2, 1, 3, 0, 3, 3, 3, 3, 3, 1, 2, 0, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3, 2, 2, 0, 2, 2, 2, 2, 2, 1, 3, 0, 3, 3, 3, 3, 3, 1, 2, 0, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3, 2, 2, 0, 2, 2, 2, 2, 2, 1, 3, 0, 3, 3, 3, 3, 3, 0, 2, 0, 2, 2, 2, 2, 2, 1, 0, 1, 0, 3, 3, 3, 3, 2, 2, 0, 0, 2, 2, 2, 3, 1, 3, 1, 0, 0, 3, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1, 0, 3, 3, 3, 3, 2, 2, 0, 2, 2, 2, 2, 2, 1, 3, 1, 0, 3, 3, 3, 3, 2, 2, 0, 2, 2, 2, 2, 2, 1, 2, 1, 2, 3, 3, 3, 3, 2, 2, 0, 2, 2, 2, 2, 2, 1, 3, 0, 3, 3, 3, 3, 3, 2, 2, 0, 2, 2, 2, 2, 2, 1, 2, 1, 0, 3, 3, 3, 3, 2, 2, 0, 2, 2, 2, 2, 2, 1, 3, 0, 3, 3, 3, 3, 3
],
validinsns: [
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 0, 3, 3, 0, 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 0, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 0, 3, 0, 0, 2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0
],
};
function getOpcodeMetadata_6502(opcode, address) {
// TODO: more intelligent maximum cycles
// TODO: must always be new object, b/c we might modify it
return {
opcode: opcode,
minCycles: OPMETA_6502.cycletime[opcode],
maxCycles: OPMETA_6502.cycletime[opcode] + OPMETA_6502.extracycles[opcode],
insnlength: OPMETA_6502.insnlengths[opcode]
};
}
exports.getOpcodeMetadata_6502 = getOpcodeMetadata_6502;
////// Z80
function cpuStateToLongString_Z80(c) {
function decodeFlags(flags) {
return (0, util_1.printFlags)(flags, ["S", "Z", , "H", , "V", "N", "C"], true);
}
return "PC " + (0, util_1.hex)(c.PC, 4) + " " + decodeFlags(c.AF) + " " + (c.iff1 ? "I" : "-") + (c.iff2 ? "I" : "-") + "\n"
+ "SP " + (0, util_1.hex)(c.SP, 4) + " IR " + (0, util_1.hex)(c.IR, 4) + "\n"
+ "IX " + (0, util_1.hex)(c.IX, 4) + " IY " + (0, util_1.hex)(c.IY, 4) + "\n"
+ "AF " + (0, util_1.hex)(c.AF, 4) + " BC " + (0, util_1.hex)(c.BC, 4) + "\n"
+ "DE " + (0, util_1.hex)(c.DE, 4) + " HL " + (0, util_1.hex)(c.HL, 4) + "\n";
}
exports.cpuStateToLongString_Z80 = cpuStateToLongString_Z80;
class BaseZ80Platform extends BaseDebugPlatform {
constructor() {
super(...arguments);
this.waitCycles = 0;
this.getToolForFilename = getToolForFilename_z80;
}
newCPU(membus, iobus) {
this._cpu = new ZilogZ80_1.Z80();
this._cpu.connectMemoryBus(membus);
this._cpu.connectIOBus(iobus);
return this._cpu;
}
getPC() { return this._cpu.getPC(); }
getSP() { return this._cpu.getSP(); }
isStable() { return true; }
// TODO: refactor other parts into here
runCPU(cpu, cycles) {
this._cpu = cpu; // TODO?
this.waitCycles = 0; // TODO: needs to spill over betwenn calls
if (this.wasBreakpointHit())
return 0;
var debugCond = this.getDebugCallback();
var n = 0;
this.waitCycles += cycles;
while (this.waitCycles > 0) {
if (debugCond && debugCond()) {
debugCond = null;
break;
}
var cyc = cpu.advanceInsn();
n += cyc;
this.waitCycles -= cyc;
}
return n;
}
getDefaultExtension() { return ".c"; }
;
// TODO: Z80 opcode metadata
//this.getOpcodeMetadata = function() { }
getDebugCategories() {
return ['CPU', 'Stack'];
}
getDebugInfo(category, state) {
switch (category) {
case 'CPU': return cpuStateToLongString_Z80(state.c);
case 'Stack': {
var sp = (state.c.SP - 1) & 0xffff;
var start = sp & 0xff00;
var end = start + 0xff;
if (sp == 0)
sp = 0x10000;
console.log(sp, start, end);
return dumpStackToString(this, [], start, end, sp, 0xcd);
}
}
}
disassemble(pc, read) {
return (0, disasmz80_1.disassembleZ80)(pc, read(pc), read(pc + 1), read(pc + 2), read(pc + 3));
}
}
exports.BaseZ80Platform = BaseZ80Platform;
function getToolForFilename_z80(fn) {
if (fn.endsWith(".c"))
return "sdcc";
if (fn.endsWith(".h"))
return "sdcc";
if (fn.endsWith(".s"))
return "sdasz80";
if (fn.endsWith(".ns"))
return "naken";
if (fn.endsWith(".scc"))
return "sccz80";
if (fn.endsWith(".z"))
return "zmac";
if (fn.endsWith(".wiz"))
return "wiz";
return "zmac";
}
exports.getToolForFilename_z80 = getToolForFilename_z80;
////// 6809
function cpuStateToLongString_6809(c) {
function decodeFlags(flags) {
return (0, util_1.printFlags)(flags, ["E", "F", "H", "I", "N", "Z", "V", "C"], true);
}
return "PC " + (0, util_1.hex)(c.PC, 4) + " " + decodeFlags(c.CC) + "\n"
+ "SP " + (0, util_1.hex)(c.SP, 4) + "\n"
+ "DP " + (0, util_1.hex)(c.DP, 2) + "\n"
+ " A " + (0, util_1.hex)(c.A, 2) + "\n"
+ " B " + (0, util_1.hex)(c.B, 2) + "\n"
+ " X " + (0, util_1.hex)(c.X, 4) + "\n"
+ " Y " + (0, util_1.hex)(c.Y, 4) + "\n"
+ " U " + (0, util_1.hex)(c.U, 4) + "\n";
}
exports.cpuStateToLongString_6809 = cpuStateToLongString_6809;
function getToolForFilename_6809(fn) {
if (fn.endsWith(".c"))
return "cmoc";
if (fn.endsWith(".h"))
return "cmoc";
if (fn.endsWith(".xasm"))
return "xasm6809";
if (fn.endsWith(".lwasm"))
return "lwasm";
return "cmoc";
}
exports.getToolForFilename_6809 = getToolForFilename_6809;
class Base6809Platform extends BaseZ80Platform {
constructor() {
super(...arguments);
//this.getOpcodeMetadata = function() { }
this.getToolForFilename = getToolForFilename_6809;
}
newCPU(membus) {
var cpu = Object.create((0, _6809_1.CPU6809)());
cpu.init(membus.write, membus.read, 0);
return cpu;
}
cpuStateToLongString(c) {
return cpuStateToLongString_6809(c);
}
disassemble(pc, read) {
// TODO: don't create new CPU
return Object.create((0, _6809_1.CPU6809)()).disasm(read(pc), read(pc + 1), read(pc + 2), read(pc + 3), read(pc + 4), pc);
}
getDefaultExtension() { return ".asm"; }
;
getDebugCategories() {
return ['CPU', 'Stack'];
}
getDebugInfo(category, state) {
switch (category) {
case 'CPU': return cpuStateToLongString_6809(state.c);
default: return super.getDebugInfo(category, state);
}
}
}
exports.Base6809Platform = Base6809Platform;
//TODO: how to get stack_end?
function dumpStackToString(platform, mem, start, end, sp, jsrop, bigendian) {
var s = "";
var nraw = 0;
//s = dumpRAM(mem.slice(start,start+end+1), start, end-start+1);
function read(addr) {
if (addr < mem.length)
return mem[addr];
else
return platform.readAddress(addr);
}
while (sp < end) {
sp++;
// see if there's a JSR on the stack here
// TODO: make work with roms and memory maps
var addr = read(sp) + read(sp + 1) * 256;
if (bigendian) {
addr = ((addr & 0xff) << 8) | ((addr & 0xff00) >> 8);
}
var jsrofs = jsrop == 0x20 ? -2 : -3; // 6502 vs Z80
var opcode = read(addr + jsrofs); // might be out of bounds
if (opcode == jsrop) { // JSR
s += "\n$" + (0, util_1.hex)(sp) + ": ";
s += (0, util_1.hex)(addr, 4) + " " + lookupSymbol(platform, addr, true);
sp++;
nraw = 0;
}
else {
if (nraw == 0)
s += "\n$" + (0, util_1.hex)(sp) + ": ";
s += (0, util_1.hex)(read(sp)) + " ";
if (++nraw == 8)
nraw = 0;
}
}
return s + "\n";
}
exports.dumpStackToString = dumpStackToString;
// TODO: slow, funky, uses global
function lookupSymbol(platform, addr, extra) {
var start = addr;
var addr2symbol = platform.debugSymbols && platform.debugSymbols.addr2symbol;
while (addr2symbol && addr >= 0) {
var sym = addr2symbol[addr];
if (sym) { // return first symbol we find
var sym = addr2symbol[addr];
return extra ? (sym + " + $" + (0, util_1.hex)(start - addr)) : sym;
}
if (!extra)
break;
addr--;
}
return "";
}
exports.lookupSymbol = lookupSymbol;
function hasVideo(arg) {
return typeof arg.connectVideo === 'function';
}
exports.hasVideo = hasVideo;
function hasAudio(arg) {
return typeof arg.connectAudio === 'function';
}
exports.hasAudio = hasAudio;
function hasKeyInput(arg) {
return typeof arg.setKeyInput === 'function';
}
exports.hasKeyInput = hasKeyInput;
function hasJoyInput(arg) {
return typeof arg.setJoyInput === 'function';
}
exports.hasJoyInput = hasJoyInput;
function hasPaddleInput(arg) {
return typeof arg.setPaddleInput === 'function';
}
exports.hasPaddleInput = hasPaddleInput;
function isRaster(arg) {
return typeof arg.getRasterY === 'function';
}
exports.isRaster = isRaster;
function hasProbe(arg) {
return typeof arg.connectProbe == 'function';
}
exports.hasProbe = hasProbe;
function hasBIOS(arg) {
return typeof arg.loadBIOS == 'function';
}
exports.hasBIOS = hasBIOS;
function hasSerialIO(arg) {
return typeof arg.connectSerialIO === 'function';
}
exports.hasSerialIO = hasSerialIO;
class BaseMachinePlatform extends BaseDebugPlatform {
constructor(mainElement) {
super();
this.mainElement = mainElement;
}
reset() {
this.machine.reset();
if (this.serialVisualizer != null)
this.serialVisualizer.reset();
}
loadState(s) { this.machine.loadState(s); }
saveState() { return this.machine.saveState(); }
getSP() { return this.machine.cpu.getSP(); }
getPC() { return this.machine.cpu.getPC(); }
isStable() { return this.machine.cpu.isStable(); }
getCPUState() { return this.machine.cpu.saveState(); }
loadControlsState(s) { this.machine.loadControlsState(s); }
saveControlsState() { return this.machine.saveControlsState(); }
async start() {
this.machine = this.newMachine();
const m = this.machine;
// block on WASM loading
if (m instanceof wasmplatform_1.BaseWASMMachine) {
await m.loadWASM();
}
var videoFrequency;
if (hasVideo(m)) {
var vp = m.getVideoParams();
this.video = new emu_1.RasterVideo(this.mainElement, vp.width, vp.height, { overscan: !!vp.overscan,
rotate: vp.rotate | 0,
aspect: vp.aspect });
this.video.create();
m.connectVideo(this.video.getFrameData());
// TODO: support keyboard w/o video?
if (hasKeyInput(m)) {
this.video.setKeyboardEvents(m.setKeyInput.bind(m));
this.poller = new emu_1.ControllerPoller(m.setKeyInput.bind(m));
}
videoFrequency = vp.videoFrequency;
}
this.timer = new emu_1.AnimationTimer(videoFrequency || 60, this.nextFrame.bind(this));
if (hasAudio(m)) {
var ap = m.getAudioParams();
this.audio = new audio_1.SampledAudio(ap.sampleRate);
this.audio.start();
m.connectAudio(this.audio);
}
if (hasPaddleInput(m)) {
this.video.setupMouseEvents();
}
if (hasProbe(m)) {
this.probeRecorder = new probe_1.ProbeRecorder(m);
this.startProbing = () => {
m.connectProbe(this.probeRecorder);
return this.probeRecorder;
};
this.stopProbing = () => {
m.connectProbe(null);
};
}
if (hasBIOS(m)) {
this.loadBIOS = (title, data) => {
m.loadBIOS(data, title);
};
}
if (hasSerialIO(m)) {
if (this.serialIOInterface == null) {
this.serialVisualizer = new SerialIOVisualizer(this.mainElement, m);
}
else {
m.connectSerialIO(this.serialIOInterface);
}
}
}
loadROM(title, data) {
this.machine.loadROM(data, title);
this.reset();
}
pollControls() {
this.poller && this.poller.poll();
if (hasPaddleInput(this.machine)) {
this.machine.setPaddleInput(0, this.video.paddle_x);
this.machine.setPaddleInput(1, this.video.paddle_y);
}
// TODO: put into interface
if (this.machine['pollControls']) {
this.machine['pollControls']();
}
}
advance(novideo) {
let trap = this.getDebugCallback();
try {
var steps = this.machine.advanceFrame(trap);
return steps;
}
finally {
// in case EmuHalt is thrown...
if (!novideo && this.video) {
this.video.updateFrame();
this.updateVideoDebugger();
}
if (!novideo && this.serialVisualizer) {
this.serialVisualizer.refresh();
}
}
}
updateVideoDebugger() {
var _a;
if (!this.isRunning() && isRaster(this.machine) && this.machine.getRasterCanvasPosition) {
const { x, y } = this.machine.getRasterCanvasPosition();
if (x >= 0 || y >= 0) {
const ctx = (_a = this.video) === null || _a === void 0 ? void 0 : _a.getContext();
if (ctx) {
(0, emu_1.drawCrosshair)(ctx, x, y, 1);
}
}
}
}
advanceFrameClock(trap, step) {
if (!(step > 0))
return;
if (this.machine instanceof wasmplatform_1.BaseWASMMachine) {
return this.machine.advanceFrameClock(trap, step);
}
else {
return this.machine.advanceFrame(() => {
return --step <= 0;
});
}
}
isRunning() {
return this.timer && this.timer.isRunning();
}
resume() {
this.timer.start();
this.audio && this.audio.start();
}
pause() {
this.timer.stop();
this.audio && this.audio.stop();
}
// so probe views stick around TODO: must be a better way?
runToVsync() {
this.restartDebugging();
var flag = false;
this.runEval(() => {
if (this.getRasterScanline() > 0)
flag = true;
else
return flag;
});
}
// TODO: reset target clock counter
getRasterScanline() {
return isRaster(this.machine) && this.machine.getRasterY ? this.machine.getRasterY() : -1;
}
getRasterLineClock() {
return isRaster(this.machine) && this.machine.getRasterX ? this.machine.getRasterX() : -1;
}
readAddress(addr) {
return this.machine.read(addr);
}
getDebugCategories() {
if (isDebuggable(this.machine))
return this.machine.getDebugCategories();
}
getDebugInfo(category, state) {
return isDebuggable(this.machine) && this.machine.getDebugInfo(category, state);
}
}
exports.BaseMachinePlatform = BaseMachinePlatform;
// TODO: move debug info into CPU?
class Base6502MachinePlatform extends BaseMachinePlatform {
constructor() {
super(...arguments);
this.getOpcodeMetadata = getOpcodeMetadata_6502;
}
getToolForFilename(fn) { return getToolForFilename_6502(fn); }
disassemble(pc, read) {
return (0, disasm6502_1.disassemble6502)(pc, read(pc), read(pc + 1), read(pc + 2));
}
getDebugCategories() {
if (isDebuggable(this.machine))
return this.machine.getDebugCategories();
else
return ['CPU', 'ZPRAM', 'Stack'];
}
getDebugInfo(category, state) {
switch (category) {
case 'CPU': return cpuStateToLongString_6502(state.c);
case 'ZPRAM': return (0, emu_1.dumpRAM)(state.b || state.ram, 0x0, 0x100);
case 'Stack': return dumpStackToString(this, state.b || state.ram, 0x100, 0x1ff, 0x100 + state.c.SP, 0x20);
default: return isDebuggable(this.machine) && this.machine.getDebugInfo(category, state);
}
}
}
exports.Base6502MachinePlatform = Base6502MachinePlatform;
class BaseZ80MachinePlatform extends BaseMachinePlatform {
constructor() {
super(...arguments);
//getOpcodeMetadata = getOpcodeMetadata_z80;
this.getToolForFilename = getToolForFilename_z80;
}
getDebugCategories() {
if (isDebuggable(this.machine))
return this.machine.getDebugCategories();
else
return ['CPU', 'Stack'];
}
getDebugInfo(category, state) {
switch (category) {
case 'CPU': return cpuStateToLongString_Z80(state.c);
case 'Stack': {
var sp = (state.c.SP - 1) & 0xffff;
var start = sp & 0xff00;
var end = start + 0xff;
if (sp == 0)
sp = 0x10000;
return dumpStackToString(this, [], start, end, sp, 0xcd);
}
default: return isDebuggable(this.machine) && this.machine.getDebugInfo(category, state);
}
}
disassemble(pc, read) {
return (0, disasmz80_1.disassembleZ80)(pc, read(pc), read(pc + 1), read(pc + 2), read(pc + 3));
}
}
exports.BaseZ80MachinePlatform = BaseZ80MachinePlatform;
class Base6809MachinePlatform extends BaseMachinePlatform {
constructor() {
super(...arguments);
this.getToolForFilename = getToolForFilename_6809;
}
getDebugCategories() {
if (isDebuggable(this.machine))
return this.machine.getDebugCategories();
else
return ['CPU', 'Stack'];
}
getDebugInfo(category, state) {
switch (category) {
case 'CPU': return cpuStateToLongString_6809(state.c);
case 'Stack': {
var sp = (state.c.SP - 1) & 0xffff;
var start = sp & 0xff00;
var end = start + 0xff;
if (sp == 0)
sp = 0x10000;
return dumpStackToString(this, [], start, end, sp, 0x17, true);
}
default: return super.getDebugInfo(category, state);
}
}
disassemble(pc, read) {
// TODO: don't create new CPU
return Object.create((0, _6809_1.CPU6809)()).disasm(read(pc), read(pc + 1), read(pc + 2), read(pc + 3), read(pc + 4), pc);
}
}
exports.Base6809MachinePlatform = Base6809MachinePlatform;
///
class SerialIOVisualizer {
constructor(parentElement, device) {
this.lastOutCount = -1;
this.lastInCount = -1;
this.device = device;
this.textarea = document.createElement("textarea");
this.textarea.classList.add('transcript');
this.textarea.classList.add('transcript-style-2');
this.textarea.style.display = 'none';
parentElement.appendChild(this.textarea);
/*
this.vlist = new VirtualTextScroller(parentElement);
this.vlist.create(parentElement, 1024, this.getMemoryLineAt.bind(this));
this.vlist.maindiv.style.height = '8em';
this.vlist.maindiv.style.overflow = 'clip';
*/
}
reset() {
this.lastOutCount = 0;
this.lastInCount = 0;
this.textarea.style.display = 'none';
}
refresh() {
var lastop = '';
if (this.device.serialOut.length != this.lastOutCount) {
var s = '';
for (var ev of this.device.serialOut) {
if (lastop != ev.op) {
if (s != '')
s += '\n';
if (ev.op === 'read')
s += '<< ';
else if (ev.op === 'write')
s += '>> ';
lastop = ev.op;
}
if (ev.value == 10) {
s += '\u21b5';
lastop = '';
}
else {
s += (0, util_1.byteToASCII)(ev.value);
}
}
this.textarea.value = s;
this.lastOutCount = this.device.serialOut.length;
this.textarea.style.display = 'block';
}
}
}
//# sourceMappingURL=baseplatform.js.map