show raster x/y crosshair when stopped (getRasterCanvasPosition)

This commit is contained in:
Steven Hugg 2023-11-10 10:38:30 -06:00
parent c6345ec728
commit de6250b0cd
11 changed files with 254 additions and 160 deletions

@ -1 +1 @@
Subproject commit 9d124f087e1f0c7f74f9244b9679cc62e71fa524 Subproject commit 113cd5741e5c414bbbe47ef8be7a896652d48f64

View File

@ -1,5 +1,5 @@
import { RasterVideo, dumpRAM, AnimationTimer, ControllerPoller } from "./emu"; import { RasterVideo, dumpRAM, AnimationTimer, ControllerPoller, drawCrosshair } from "./emu";
import { hex, printFlags, invertMap, byteToASCII } from "./util"; import { hex, printFlags, invertMap, byteToASCII } from "./util";
import { CodeAnalyzer } from "./analysis"; import { CodeAnalyzer } from "./analysis";
import { Segment, FileData } from "./workertypes"; import { Segment, FileData } from "./workertypes";
@ -850,7 +850,7 @@ export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPl
} }
} }
} }
loadROM(title, data) { loadROM(title, data) {
this.machine.loadROM(data, title); this.machine.loadROM(data, title);
this.reset(); this.reset();
@ -873,11 +873,26 @@ export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPl
advance(novideo:boolean) { advance(novideo:boolean) {
let trap = this.getDebugCallback(); let trap = this.getDebugCallback();
var steps = this.machine.advanceFrame(trap); var steps = this.machine.advanceFrame(trap);
if (!novideo && this.video) this.video.updateFrame(); if (!novideo && this.video) {
if (!novideo && this.serialVisualizer) this.serialVisualizer.refresh(); this.video.updateFrame();
this.updateVideoDebugger();
}
if (!novideo && this.serialVisualizer) {
this.serialVisualizer.refresh();
}
return steps; return steps;
} }
updateVideoDebugger() {
if (!this.isRunning() && isRaster(this.machine) && this.machine.getRasterCanvasPosition) {
const {x,y} = this.machine.getRasterCanvasPosition();
if (x >= 0 || y >= 0) {
const ctx = this.video.getContext();
drawCrosshair(ctx, x, y, 1);
}
}
}
advanceFrameClock(trap, step) { advanceFrameClock(trap, step) {
if (!(step > 0)) return; if (!(step > 0)) return;
if (this.machine instanceof BaseWASMMachine) { if (this.machine instanceof BaseWASMMachine) {
@ -915,7 +930,10 @@ export abstract class BaseMachinePlatform<T extends Machine> extends BaseDebugPl
// TODO: reset target clock counter // TODO: reset target clock counter
getRasterScanline() { getRasterScanline() {
return isRaster(this.machine) && this.machine.getRasterY(); 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 : number) : number { readAddress(addr : number) : number {

View File

@ -1,115 +1,116 @@
export interface SavesState<S> { export interface SavesState<S> {
saveState() : S; saveState(): S;
loadState(state:S) : void; loadState(state: S): void;
} }
export interface Bus { export interface Bus {
read(a:number) : number; read(a: number): number;
write(a:number, v:number) : void; write(a: number, v: number): void;
readConst?(a:number) : number; readConst?(a: number): number;
} }
export interface ClockBased { export interface ClockBased {
advanceClock() : void; advanceClock(): void;
} }
export interface InstructionBased { export interface InstructionBased {
advanceInsn() : number; advanceInsn(): number;
} }
export type TrapCondition = () => boolean; export type TrapCondition = () => boolean;
export interface FrameBased { export interface FrameBased {
advanceFrame(trap:TrapCondition) : number; advanceFrame(trap: TrapCondition): number;
} }
export interface VideoSource { export interface VideoSource {
getVideoParams() : VideoParams; getVideoParams(): VideoParams;
connectVideo(pixels:Uint32Array) : void; connectVideo(pixels: Uint32Array): void;
} }
export interface RasterFrameBased extends FrameBased, VideoSource { export interface RasterFrameBased extends FrameBased, VideoSource {
getRasterY() : number; getRasterY(): number;
getRasterX() : number; getRasterX(): number;
getRasterCanvasPosition?(): { x: number, y: number };
} }
export interface VideoParams { export interface VideoParams {
width : number; width: number;
height : number; height: number;
overscan? : boolean; overscan?: boolean;
rotate? : number; rotate?: number;
videoFrequency? : number; // default = 60 videoFrequency?: number; // default = 60
aspect? : number; aspect?: number;
} }
// TODO: frame buffer optimization (apple2, etc) // TODO: frame buffer optimization (apple2, etc)
export interface SampledAudioParams { export interface SampledAudioParams {
sampleRate : number; sampleRate: number;
stereo : boolean; stereo: boolean;
} }
export interface SampledAudioSink { export interface SampledAudioSink {
feedSample(value:number, count:number) : void; feedSample(value: number, count: number): void;
//sendAudioFrame(samples:Uint16Array) : void; //sendAudioFrame(samples:Uint16Array) : void;
} }
export interface SampledAudioSource { export interface SampledAudioSource {
getAudioParams() : SampledAudioParams; getAudioParams(): SampledAudioParams;
connectAudio(audio : SampledAudioSink) : void; connectAudio(audio: SampledAudioSink): void;
} }
export interface AcceptsROM { export interface AcceptsROM {
loadROM(data:Uint8Array, title?:string) : void; loadROM(data: Uint8Array, title?: string): void;
} }
export interface AcceptsBIOS { export interface AcceptsBIOS {
loadBIOS(data:Uint8Array, title?:string) : void; loadBIOS(data: Uint8Array, title?: string): void;
} }
export interface Resettable { export interface Resettable {
reset() : void; reset(): void;
} }
export interface MemoryBusConnected { export interface MemoryBusConnected {
connectMemoryBus(bus:Bus) : void; connectMemoryBus(bus: Bus): void;
} }
export interface IOBusConnected { export interface IOBusConnected {
connectIOBus(bus:Bus) : void; connectIOBus(bus: Bus): void;
} }
export interface CPU extends MemoryBusConnected, Resettable, SavesState<any> { export interface CPU extends MemoryBusConnected, Resettable, SavesState<any> {
getPC() : number; getPC(): number;
getSP() : number; getSP(): number;
isStable() : boolean; isStable(): boolean;
} }
export interface HasCPU extends Resettable { export interface HasCPU extends Resettable {
cpu : CPU; cpu: CPU;
} }
export interface Interruptable<IT> { export interface Interruptable<IT> {
interrupt(type:IT) : void; interrupt(type: IT): void;
} }
export interface SavesInputState<CS> { export interface SavesInputState<CS> {
loadControlsState(cs:CS) : void; loadControlsState(cs: CS): void;
saveControlsState() : CS; saveControlsState(): CS;
} }
export interface AcceptsKeyInput { export interface AcceptsKeyInput {
setKeyInput(key:number, code:number, flags:number) : void; setKeyInput(key: number, code: number, flags: number): void;
} }
export interface AcceptsPaddleInput { export interface AcceptsPaddleInput {
setPaddleInput(controller:number, value:number) : void; setPaddleInput(controller: number, value: number): void;
} }
// TODO: interface not yet used (setKeyInput() handles joystick) // TODO: interface not yet used (setKeyInput() handles joystick)
export interface AcceptsJoyInput { export interface AcceptsJoyInput {
setJoyInput(joy:number, bitmask:number) : void; setJoyInput(joy: number, bitmask: number): void;
} }
// SERIAL I/O // SERIAL I/O
@ -123,15 +124,15 @@ export interface SerialEvent {
// TODO: all these needed? // TODO: all these needed?
export interface SerialIOInterface { export interface SerialIOInterface {
// from machine to platform // from machine to platform
clearToSend() : boolean; clearToSend(): boolean;
sendByte(b : number); sendByte(b: number);
// from platform to machine // from platform to machine
byteAvailable() : boolean; byteAvailable(): boolean;
recvByte() : number; recvByte(): number;
// implement these too // implement these too
reset() : void; reset(): void;
advance(clocks: number) : void; advance(clocks: number): void;
// refresh() : void; // refresh() : void;
} }
export interface HasSerialIO { export interface HasSerialIO {
@ -143,62 +144,62 @@ export interface HasSerialIO {
/// PROFILER /// PROFILER
export interface Probeable { export interface Probeable {
connectProbe(probe: ProbeAll) : void; connectProbe(probe: ProbeAll): void;
} }
export interface ProbeTime { export interface ProbeTime {
logClocks(clocks:number); logClocks(clocks: number);
logNewScanline(); logNewScanline();
logNewFrame(); logNewFrame();
} }
export interface ProbeCPU { export interface ProbeCPU {
logExecute(address:number, SP:number); logExecute(address: number, SP: number);
logInterrupt(type:number); logInterrupt(type: number);
logIllegal(address:number); logIllegal(address: number);
logWait(address:number); logWait(address: number);
} }
export interface ProbeBus { export interface ProbeBus {
logRead(address:number, value:number); logRead(address: number, value: number);
logWrite(address:number, value:number); logWrite(address: number, value: number);
logDMARead(address:number, value:number); logDMARead(address: number, value: number);
logDMAWrite(address:number, value:number); logDMAWrite(address: number, value: number);
} }
export interface ProbeIO { export interface ProbeIO {
logIORead(address:number, value:number); logIORead(address: number, value: number);
logIOWrite(address:number, value:number); logIOWrite(address: number, value: number);
} }
export interface ProbeVRAM { export interface ProbeVRAM {
logVRAMRead(address:number, value:number); logVRAMRead(address: number, value: number);
logVRAMWrite(address:number, value:number); logVRAMWrite(address: number, value: number);
} }
export interface ProbeAll extends ProbeTime, ProbeCPU, ProbeBus, ProbeIO, ProbeVRAM { export interface ProbeAll extends ProbeTime, ProbeCPU, ProbeBus, ProbeIO, ProbeVRAM {
logData(data:number); // entire 32 bits logData(data: number); // entire 32 bits
addLogBuffer(src: Uint32Array); addLogBuffer(src: Uint32Array);
} }
export class NullProbe implements ProbeAll { export class NullProbe implements ProbeAll {
logClocks() {} logClocks() { }
logNewScanline() {} logNewScanline() { }
logNewFrame() {} logNewFrame() { }
logExecute() {} logExecute() { }
logInterrupt() {} logInterrupt() { }
logRead() {} logRead() { }
logWrite() {} logWrite() { }
logIORead() {} logIORead() { }
logIOWrite() {} logIOWrite() { }
logVRAMRead() {} logVRAMRead() { }
logVRAMWrite() {} logVRAMWrite() { }
logIllegal() {} logIllegal() { }
logWait() {} logWait() { }
logDMARead() {} logDMARead() { }
logDMAWrite() {} logDMAWrite() { }
logData() {} logData() { }
addLogBuffer(src: Uint32Array) {} addLogBuffer(src: Uint32Array) { }
} }
/// CONVENIENCE /// CONVENIENCE
@ -215,32 +216,32 @@ export interface BasicMachineState extends BasicMachineControlsState {
export abstract class BasicHeadlessMachine implements HasCPU, Bus, AcceptsROM, Probeable, export abstract class BasicHeadlessMachine implements HasCPU, Bus, AcceptsROM, Probeable,
SavesState<BasicMachineState>, SavesInputState<BasicMachineControlsState> { SavesState<BasicMachineState>, SavesInputState<BasicMachineControlsState> {
abstract cpuFrequency : number; abstract cpuFrequency: number;
abstract defaultROMSize : number; abstract defaultROMSize: number;
abstract cpu : CPU; abstract cpu: CPU;
abstract ram : Uint8Array; abstract ram: Uint8Array;
rom : Uint8Array; rom: Uint8Array;
inputs : Uint8Array = new Uint8Array(32); inputs: Uint8Array = new Uint8Array(32);
handler : (key,code,flags) => void; // keyboard handler handler: (key, code, flags) => void; // keyboard handler
nullProbe = new NullProbe(); nullProbe = new NullProbe();
probe : ProbeAll = this.nullProbe; probe: ProbeAll = this.nullProbe;
abstract read(a:number) : number;
abstract write(a:number, v:number) : void;
setKeyInput(key:number, code:number, flags:number) : void { abstract read(a: number): number;
this.handler && this.handler(key,code,flags); abstract write(a: number, v: number): void;
setKeyInput(key: number, code: number, flags: number): void {
this.handler && this.handler(key, code, flags);
} }
connectProbe(probe: ProbeAll) : void { connectProbe(probe: ProbeAll): void {
this.probe = probe || this.nullProbe; this.probe = probe || this.nullProbe;
} }
reset() { reset() {
this.cpu.reset(); this.cpu.reset();
} }
loadROM(data:Uint8Array, title?:string) : void { loadROM(data: Uint8Array, title?: string): void {
if (!this.rom) this.rom = new Uint8Array(this.defaultROMSize); if (!this.rom) this.rom = new Uint8Array(this.defaultROMSize);
this.rom.set(data); this.rom.set(data);
} }
@ -251,9 +252,9 @@ export abstract class BasicHeadlessMachine implements HasCPU, Bus, AcceptsROM, P
} }
saveState() { saveState() {
return { return {
c:this.cpu.saveState(), c: this.cpu.saveState(),
ram:this.ram.slice(0), ram: this.ram.slice(0),
inputs:this.inputs.slice(0), inputs: this.inputs.slice(0),
}; };
} }
loadControlsState(state) { loadControlsState(state) {
@ -261,7 +262,7 @@ export abstract class BasicHeadlessMachine implements HasCPU, Bus, AcceptsROM, P
} }
saveControlsState() { saveControlsState() {
return { return {
inputs:this.inputs.slice(0) inputs: this.inputs.slice(0)
}; };
} }
advanceCPU() { advanceCPU() {
@ -273,102 +274,104 @@ export abstract class BasicHeadlessMachine implements HasCPU, Bus, AcceptsROM, P
this.probe.logClocks(n); this.probe.logClocks(n);
return n; return n;
} }
probeMemoryBus(membus:Bus) : Bus { probeMemoryBus(membus: Bus): Bus {
return { return {
read: (a) => { read: (a) => {
let val = membus.read(a); let val = membus.read(a);
this.probe.logRead(a,val); this.probe.logRead(a, val);
return val; return val;
}, },
write: (a,v) => { write: (a, v) => {
this.probe.logWrite(a,v); this.probe.logWrite(a, v);
membus.write(a,v); membus.write(a, v);
} }
}; };
} }
connectCPUMemoryBus(membus:Bus) : void { connectCPUMemoryBus(membus: Bus): void {
this.cpu.connectMemoryBus(this.probeMemoryBus(membus)); this.cpu.connectMemoryBus(this.probeMemoryBus(membus));
} }
probeIOBus(iobus:Bus) : Bus { probeIOBus(iobus: Bus): Bus {
return { return {
read: (a) => { read: (a) => {
let val = iobus.read(a); let val = iobus.read(a);
this.probe.logIORead(a,val); this.probe.logIORead(a, val);
return val; return val;
}, },
write: (a,v) => { write: (a, v) => {
this.probe.logIOWrite(a,v); this.probe.logIOWrite(a, v);
iobus.write(a,v); iobus.write(a, v);
} }
}; };
} }
probeDMABus(iobus:Bus) : Bus { probeDMABus(iobus: Bus): Bus {
return { return {
read: (a) => { read: (a) => {
let val = iobus.read(a); let val = iobus.read(a);
this.probe.logDMARead(a,val); this.probe.logDMARead(a, val);
return val; return val;
}, },
write: (a,v) => { write: (a, v) => {
this.probe.logDMAWrite(a,v); this.probe.logDMAWrite(a, v);
iobus.write(a,v); iobus.write(a, v);
} }
}; };
} }
connectCPUIOBus(iobus:Bus) : void { connectCPUIOBus(iobus: Bus): void {
this.cpu['connectIOBus'](this.probeIOBus(iobus)); this.cpu['connectIOBus'](this.probeIOBus(iobus));
} }
} }
export abstract class BasicMachine extends BasicHeadlessMachine implements SampledAudioSource { export abstract class BasicMachine extends BasicHeadlessMachine implements SampledAudioSource {
abstract canvasWidth : number; abstract canvasWidth: number;
abstract numVisibleScanlines : number; abstract numVisibleScanlines: number;
abstract sampleRate : number; abstract sampleRate: number;
overscan : boolean = false; overscan: boolean = false;
rotate : number = 0; rotate: number = 0;
aspectRatio : number; aspectRatio: number;
pixels : Uint32Array;
audio : SampledAudioSink;
scanline : number; pixels: Uint32Array;
audio: SampledAudioSink;
getAudioParams() : SampledAudioParams {
return {sampleRate:this.sampleRate, stereo:false}; scanline: number;
getAudioParams(): SampledAudioParams {
return { sampleRate: this.sampleRate, stereo: false };
} }
connectAudio(audio : SampledAudioSink) : void { connectAudio(audio: SampledAudioSink): void {
this.audio = audio; this.audio = audio;
} }
getVideoParams() : VideoParams { getVideoParams(): VideoParams {
return {width:this.canvasWidth, return {
height:this.numVisibleScanlines, width: this.canvasWidth,
aspect:this.aspectRatio, height: this.numVisibleScanlines,
overscan:this.overscan, aspect: this.aspectRatio,
rotate:this.rotate}; overscan: this.overscan,
rotate: this.rotate
};
} }
connectVideo(pixels:Uint32Array) : void { connectVideo(pixels: Uint32Array): void {
this.pixels = pixels; this.pixels = pixels;
} }
} }
export abstract class BasicScanlineMachine extends BasicMachine implements RasterFrameBased { export abstract class BasicScanlineMachine extends BasicMachine implements RasterFrameBased {
abstract numTotalScanlines : number; abstract numTotalScanlines: number;
abstract cpuCyclesPerLine : number; abstract cpuCyclesPerLine: number;
abstract startScanline() : void; abstract startScanline(): void;
abstract drawScanline() : void; abstract drawScanline(): void;
frameCycles : number; frameCycles: number;
advanceFrame(trap: TrapCondition) : number { advanceFrame(trap: TrapCondition): number {
this.preFrame(); this.preFrame();
var endLineClock = 0; var endLineClock = 0;
var steps = 0; var steps = 0;
this.probe.logNewFrame(); this.probe.logNewFrame();
this.frameCycles = 0; this.frameCycles = 0;
for (var sl=0; sl<this.numTotalScanlines; sl++) { for (var sl = 0; sl < this.numTotalScanlines; sl++) {
endLineClock += this.cpuCyclesPerLine; // could be fractional endLineClock += this.cpuCyclesPerLine; // could be fractional
this.scanline = sl; this.scanline = sl;
this.startScanline(); this.startScanline();

View File

@ -215,6 +215,21 @@ export class VectorVideo extends RasterVideo {
} }
} }
export function drawCrosshair(ctx:CanvasRenderingContext2D, x:number, y:number, width:number) {
ctx.fillStyle = 'rgba(0,0,0,0.25)';
ctx.fillRect(x-2, 0, 5, 32767);
ctx.fillRect(0, y-2, 32767, 5);
ctx.lineWidth = width;
ctx.strokeStyle = 'rgba(255,255,255,0.75)';
ctx.setLineDash([width*2,width*2]);
ctx.beginPath();
ctx.moveTo(x, 0);
ctx.lineTo(x, 32767);
ctx.moveTo(0, y);
ctx.lineTo(32767, y);
ctx.stroke();
}
export class RAM { export class RAM {
mem : Uint8Array; mem : Uint8Array;
constructor(size:number) { constructor(size:number) {

View File

@ -556,6 +556,7 @@ export class BallyAstrocade extends BasicScanlineMachine implements AcceptsPaddl
case 'Astro': return this.m.toLongString(state); case 'Astro': return this.m.toLongString(state);
} }
} }
getRasterCanvasPosition() { return { x: this.getRasterX(), y: this.getRasterY() }; }
} }

View File

@ -516,6 +516,7 @@ export class Atari7800 extends BasicMachine implements RasterFrameBased {
var mc = 0; var mc = 0;
var fc = 0; var fc = 0;
var steps = 0; var steps = 0;
this.lastFrameCycles = -1;
this.probe.logNewFrame(); this.probe.logNewFrame();
//console.log(hex(this.cpu.getPC()), hex(this.maria.dll)); //console.log(hex(this.cpu.getPC()), hex(this.maria.dll));
// visible lines // visible lines
@ -530,6 +531,7 @@ export class Atari7800 extends BasicMachine implements RasterFrameBased {
if (trap && trap()) { if (trap && trap()) {
trap = null; trap = null;
sl = 999; sl = 999;
this.lastFrameCycles = mc;
break; // TODO? break; // TODO?
} }
mc += this.advanceCPU() << 2; mc += this.advanceCPU() << 2;
@ -567,6 +569,7 @@ export class Atari7800 extends BasicMachine implements RasterFrameBased {
if (trap && trap()) { if (trap && trap()) {
trap = null; trap = null;
sl = 999; sl = 999;
this.lastFrameCycles = mc;
break; break;
} }
mc += this.advanceCPU() << 2; mc += this.advanceCPU() << 2;
@ -583,13 +586,18 @@ export class Atari7800 extends BasicMachine implements RasterFrameBased {
// TODO let bkcol = this.maria.regs[0x0]; // TODO let bkcol = this.maria.regs[0x0];
// TODO $(this.video.canvas).css('background-color', COLORS_WEB[bkcol]); // TODO $(this.video.canvas).css('background-color', COLORS_WEB[bkcol]);
*/ */
this.lastFrameCycles = fc;
return steps; return steps;
} }
getRasterX() { return this.lastFrameCycles % colorClocksPerLine; } // TODO: doesn't work when breakpoint
getRasterX() { return (this.lastFrameCycles + colorClocksPerLine) % colorClocksPerLine; }
getRasterY() { return this.scanline; } getRasterY() { return this.scanline; }
getRasterCanvasPosition() {
return { x: this.getRasterX(), y: this.getRasterY() };
}
loadROM(data) { loadROM(data) {
if (data.length == 0xc080) data = data.slice(0x80); // strip header if (data.length == 0xc080) data = data.slice(0x80); // strip header
this.rom = padBytes(data, this.defaultROMSize, true); this.rom = padBytes(data, this.defaultROMSize, true);

View File

@ -267,12 +267,18 @@ export class Atari800 extends BasicScanlineMachine implements AcceptsPaddleInput
keycode: this.keycode, keycode: this.keycode,
}; };
} }
getRasterScanline() { getRasterY() {
return this.antic.v; return this.antic.v;
} }
getRasterLineClock() { getRasterX() {
return this.antic.h; return this.antic.h;
} }
getRasterCanvasPosition() {
return {
x: this.antic.h * 4 - this.firstVisibleClock,
y: this.antic.v - this.firstVisibleScanline,
}
}
getDebugCategories() { getDebugCategories() {
return ['CPU', 'Stack', 'ANTIC', 'GTIA', 'POKEY']; return ['CPU', 'Stack', 'ANTIC', 'GTIA', 'POKEY'];
} }

View File

@ -200,9 +200,18 @@ export class C64_WASMMachine extends BaseWASMMachine
} }
this.exports.c64_joystick(this.sys, this.joymask0, this.joymask1); this.exports.c64_joystick(this.sys, this.joymask0, this.joymask1);
} }
getRasterX() {
return this.statearr[0xf4];
}
getRasterY() { getRasterY() {
return this.exports.machine_get_raster_line(this.sys); return this.exports.machine_get_raster_line(this.sys);
} }
getRasterCanvasPosition() {
return {
x: this.getRasterX() * 392/63,
y: this.getRasterY() - 14,
}
}
getDebugStateOffset(index: number) { getDebugStateOffset(index: number) {
var p = this.exports.machine_get_debug_pointer(this.sys, index); var p = this.exports.machine_get_debug_pointer(this.sys, index);
return p - this.sys; return p - this.sys;

View File

@ -69,6 +69,12 @@ export class CPC_WASMMachine extends BaseWASMMachine implements Machine {
getRasterY() { getRasterY() {
return this.exports.machine_get_raster_line(this.sys); return this.exports.machine_get_raster_line(this.sys);
} }
getRasterCanvasPosition() {
return {
x: -1, // TODO?
y: this.getRasterY() - 14,
}
}
/* /*
z80_tick_t tick_cb; // 0 z80_tick_t tick_cb; // 0
uint64_t bc_de_hl_fa; // 8 uint64_t bc_de_hl_fa; // 8

View File

@ -93,6 +93,12 @@ export class VIC20_WASMMachine extends BaseWASMMachine implements Machine, Probe
getRasterY() { getRasterY() {
return this.exports.machine_get_raster_line(this.sys); return this.exports.machine_get_raster_line(this.sys);
} }
getRasterCanvasPosition() {
return {
x: -1, // TODO?
y: this.getRasterY() - 14,
}
}
getCPUState() { getCPUState() {
this.exports.machine_save_cpu_state(this.sys, this.cpustateptr); this.exports.machine_save_cpu_state(this.sys, this.cpustateptr);
var s = this.cpustatearr; var s = this.cpustatearr;

View File

@ -1,6 +1,6 @@
import { Platform, BasePlatform, cpuStateToLongString_6502, dumpStackToString, DisasmLine, CpuState, getToolForFilename_6502 } from "../common/baseplatform"; import { Platform, BasePlatform, cpuStateToLongString_6502, dumpStackToString, DisasmLine, CpuState, getToolForFilename_6502 } from "../common/baseplatform";
import { PLATFORMS, dumpRAM, EmuHalt, RasterVideo, __createCanvas } from "../common/emu"; import { PLATFORMS, dumpRAM, EmuHalt, RasterVideo, __createCanvas, drawCrosshair } from "../common/emu";
import { hex, loadScript, lpad, tobin } from "../common/util"; import { hex, loadScript, lpad, tobin } from "../common/util";
import { CodeAnalyzer_vcs } from "../common/analysis"; import { CodeAnalyzer_vcs } from "../common/analysis";
import { disassemble6502 } from "../common/cpu/disasm6502"; import { disassemble6502 } from "../common/cpu/disasm6502";
@ -72,6 +72,7 @@ function getToolForFilename_vcs(fn: string) {
class VCSPlatform extends BasePlatform { class VCSPlatform extends BasePlatform {
lastBreakState; // last breakpoint state lastBreakState; // last breakpoint state
canvas : HTMLCanvasElement;
// TODO: super hack for ProbeBitmap view // TODO: super hack for ProbeBitmap view
machine = { machine = {
@ -92,10 +93,10 @@ class VCSPlatform extends BasePlatform {
// show console div and start // show console div and start
$("#javatari-div").show(); $("#javatari-div").show();
Javatari.start(); Javatari.start();
var console = Javatari.room.console; var jaconsole = Javatari.room.console;
// intercept clockPulse function // intercept clockPulse function
console.oldClockPulse = console.clockPulse; jaconsole.oldClockPulse = jaconsole.clockPulse;
console.clockPulse = function() { jaconsole.clockPulse = function() {
self.updateRecorder(); self.updateRecorder();
self.probe.logNewFrame(); self.probe.logNewFrame();
this.oldClockPulse(); this.oldClockPulse();
@ -106,18 +107,19 @@ class VCSPlatform extends BasePlatform {
} }
} }
// intercept TIA end of line // intercept TIA end of line
var videoSignal = console.tia.getVideoOutput(); var videoSignal = jaconsole.tia.getVideoOutput();
videoSignal.oldNextLine = videoSignal.nextLine; videoSignal.oldNextLine = videoSignal.nextLine;
videoSignal.nextLine = function(pixels, vsync) { videoSignal.nextLine = function(pixels, vsync) {
self.probe.logNewScanline(); self.probe.logNewScanline();
return this.oldNextLine(pixels, vsync); return this.oldNextLine(pixels, vsync);
} }
// resize after added to dom tree // resize after added to dom tree
var jacanvas = $("#javatari-screen").find("canvas"); var jacanvas = $("#javatari-screen").find("canvas")[0];
const resizeObserver = new ResizeObserver(entries => { const resizeObserver = new ResizeObserver(entries => {
this.resize(); this.resize();
}); });
resizeObserver.observe(jacanvas[0]); resizeObserver.observe(jacanvas);
this.canvas = jacanvas;
} }
loadROM(title, data) { loadROM(title, data) {
@ -148,6 +150,16 @@ class VCSPlatform extends BasePlatform {
getRasterLineClock() : number { getRasterLineClock() : number {
return this.getRasterPosition().x; return this.getRasterPosition().x;
} }
getRasterCanvasPosition() : {x:number,y:number} {
let p = Javatari.room.console.tia.getVideoOutput().monitor.getDisplayParameters();
let {x,y} = this.getRasterPosition();
let canvasPos = {
x: (x - p.displayOriginX) * p.displayWidth * p.displayScaleX / (p.signalWidth - p.displayOriginX),
y: (y - p.displayOriginY) * p.displayHeight * p.displayScaleY / p.displayHeight
};
console.log(x,y,canvasPos,p);
return canvasPos;
}
// TODO: Clock changes this on event, so it may not be current // TODO: Clock changes this on event, so it may not be current
isRunning() { isRunning() {
@ -194,6 +206,8 @@ class VCSPlatform extends BasePlatform {
Javatari.room.speaker.mute(); Javatari.room.speaker.mute();
this.lastBreakState = state; this.lastBreakState = state;
callback(state); callback(state);
// TODO: we have to delay because javatari timer is still running
setTimeout(() => this.updateVideoDebugger(), 100);
} }
Javatari.room.speaker.mute(); Javatari.room.speaker.mute();
} }
@ -248,6 +262,7 @@ class VCSPlatform extends BasePlatform {
} }
readAddress(addr) { readAddress(addr) {
// TODO: shouldn't have to do this when debugging // TODO: shouldn't have to do this when debugging
// TODO: don't read bank switch addresses
if (this.lastBreakState && addr >= 0x80 && addr < 0x100) if (this.lastBreakState && addr >= 0x80 && addr < 0x100)
return this.getRAMForState(this.lastBreakState)[addr & 0x7f]; return this.getRAMForState(this.lastBreakState)[addr & 0x7f];
else if ((addr & 0x1280) === 0x280) else if ((addr & 0x1280) === 0x280)
@ -419,6 +434,13 @@ class VCSPlatform extends BasePlatform {
var xt = (1 - scale) * 50; var xt = (1 - scale) * 50;
$('#javatari-div').css('transform', `translateX(-${xt}%) translateY(-${xt}%) scale(${scale})`); $('#javatari-div').css('transform', `translateX(-${xt}%) translateY(-${xt}%) scale(${scale})`);
} }
updateVideoDebugger() {
const {x,y} = this.getRasterCanvasPosition();
if (x >= 0 || y >= 0) {
const ctx = this.canvas.getContext('2d');
drawCrosshair(ctx, x, y, 2);
}
}
}; };
// TODO: mixin for Base6502Platform? // TODO: mixin for Base6502Platform?