2019-08-22 02:55:32 +00:00
|
|
|
|
2019-08-23 14:37:30 +00:00
|
|
|
export interface SavesState<S> {
|
|
|
|
saveState() : S;
|
|
|
|
loadState(state:S) : void;
|
2019-08-22 02:55:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface Bus {
|
|
|
|
read(a:number) : number;
|
|
|
|
write(a:number, v:number) : void;
|
|
|
|
// TODO: readConst?(a:number) : number;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface ClockBased {
|
|
|
|
advanceClock() : void;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface InstructionBased {
|
|
|
|
advanceInsn() : number;
|
|
|
|
}
|
|
|
|
|
2019-08-22 20:10:15 +00:00
|
|
|
export type TrapCondition = () => boolean;
|
|
|
|
|
2019-08-22 02:55:32 +00:00
|
|
|
export interface FrameBased {
|
2019-08-22 20:10:15 +00:00
|
|
|
advanceFrame(maxClocks:number, trap:TrapCondition) : number;
|
2019-08-22 02:55:32 +00:00
|
|
|
}
|
|
|
|
|
2019-08-22 20:10:15 +00:00
|
|
|
export interface VideoSource {
|
2019-08-22 02:55:32 +00:00
|
|
|
getVideoParams() : VideoParams;
|
|
|
|
connectVideo(pixels:Uint32Array) : void;
|
|
|
|
}
|
|
|
|
|
2019-08-22 20:10:15 +00:00
|
|
|
export interface RasterFrameBased extends FrameBased, VideoSource {
|
2019-08-22 02:55:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface VideoParams {
|
|
|
|
width : number;
|
|
|
|
height : number;
|
|
|
|
overscan? : boolean;
|
|
|
|
rotate? : number;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: frame buffer optimization (apple2, etc)
|
|
|
|
|
|
|
|
export interface SampledAudioParams {
|
|
|
|
sampleRate : number;
|
|
|
|
stereo : boolean;
|
|
|
|
}
|
|
|
|
|
2019-08-22 20:10:15 +00:00
|
|
|
export interface SampledAudioSink {
|
|
|
|
feedSample(value:number, count:number) : void;
|
|
|
|
//sendAudioFrame(samples:Uint16Array) : void;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface SampledAudioSource {
|
|
|
|
getAudioParams() : SampledAudioParams;
|
|
|
|
connectAudio(audio : SampledAudioSink) : void;
|
2019-08-22 02:55:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface AcceptsROM {
|
|
|
|
loadROM(data:Uint8Array, title?:string) : void;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface Resettable {
|
|
|
|
reset() : void;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface MemoryBusConnected {
|
|
|
|
connectMemoryBus(bus:Bus) : void;
|
|
|
|
}
|
|
|
|
|
2019-08-23 02:01:05 +00:00
|
|
|
export interface IOBusConnected {
|
|
|
|
connectIOBus(bus:Bus) : void;
|
|
|
|
}
|
|
|
|
|
2019-08-23 14:37:30 +00:00
|
|
|
export interface CPU extends MemoryBusConnected, Resettable, SavesState<any> {
|
2019-08-22 02:55:32 +00:00
|
|
|
getPC() : number;
|
|
|
|
getSP() : number;
|
2019-08-23 14:37:30 +00:00
|
|
|
isStable() : boolean;
|
2019-08-22 02:55:32 +00:00
|
|
|
}
|
|
|
|
|
2019-08-23 14:37:30 +00:00
|
|
|
export interface HasCPU {
|
|
|
|
cpu : CPU;
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface Interruptable<IT> {
|
|
|
|
interrupt(type:IT) : void;
|
2019-08-22 02:55:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO
|
2019-08-23 14:37:30 +00:00
|
|
|
export interface AcceptsInput<CS> {
|
2019-08-22 02:55:32 +00:00
|
|
|
setInput(key:number, code:number, flags:number) : void;
|
2019-08-23 14:37:30 +00:00
|
|
|
loadControlsState(cs:CS);
|
|
|
|
saveControlsState() : CS;
|
2019-08-22 02:55:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO?
|
|
|
|
export function noise(x : number) : number {
|
|
|
|
x ^= x << 13;
|
|
|
|
x ^= x >> 17;
|
|
|
|
x ^= x << 5;
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// HOOKS
|
|
|
|
|
|
|
|
export interface Hook<T> {
|
|
|
|
//target : T;
|
|
|
|
unhook();
|
|
|
|
}
|
|
|
|
|
|
|
|
export class BusHook implements Hook<Bus> {
|
|
|
|
//target : Bus;
|
2019-08-22 20:10:15 +00:00
|
|
|
constructor(bus : Bus, profiler : LogBus) {
|
2019-08-22 02:55:32 +00:00
|
|
|
//this.target = bus;
|
|
|
|
var oldread = bus.read.bind(bus);
|
|
|
|
var oldwrite = bus.write.bind(bus);
|
|
|
|
bus.read = (a:number):number => {
|
|
|
|
profiler.logRead(a);
|
|
|
|
var val = oldread(a);
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
bus.write = (a:number,v:number) => {
|
|
|
|
profiler.logWrite(a);
|
|
|
|
oldwrite(a,v);
|
|
|
|
}
|
|
|
|
this.unhook = () => {
|
|
|
|
bus.read = oldread;
|
|
|
|
bus.write = oldwrite;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
unhook : () => void;
|
|
|
|
}
|
|
|
|
|
|
|
|
export class CPUClockHook implements Hook<CPU&ClockBased> {
|
|
|
|
//target : CPU&ClockBased;
|
2019-08-22 20:10:15 +00:00
|
|
|
constructor(cpu : CPU&ClockBased, profiler : LogCPU) {
|
2019-08-22 02:55:32 +00:00
|
|
|
//this.target = cpu;
|
|
|
|
var oldclock = cpu.advanceClock.bind(cpu);
|
|
|
|
cpu.advanceClock = () => {
|
|
|
|
profiler.logExecute(cpu.getPC());
|
|
|
|
return oldclock();
|
|
|
|
}
|
|
|
|
this.unhook = () => {
|
|
|
|
cpu.advanceClock = oldclock;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
unhook : () => void;
|
|
|
|
}
|
|
|
|
|
|
|
|
export class CPUInsnHook implements Hook<CPU&InstructionBased> {
|
|
|
|
//target : CPU&InstructionBased;
|
2019-08-22 20:10:15 +00:00
|
|
|
constructor(cpu : CPU&InstructionBased, profiler : LogCPU) {
|
2019-08-22 02:55:32 +00:00
|
|
|
//this.target = cpu;
|
|
|
|
var oldinsn = cpu.advanceInsn.bind(cpu);
|
|
|
|
cpu.advanceInsn = () => {
|
|
|
|
profiler.logExecute(cpu.getPC());
|
|
|
|
return oldinsn();
|
|
|
|
}
|
|
|
|
this.unhook = () => {
|
|
|
|
cpu.advanceInsn = oldinsn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
unhook : () => void;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// PROFILER
|
|
|
|
|
2019-08-22 20:10:15 +00:00
|
|
|
export interface LogCPU {
|
2019-08-22 02:55:32 +00:00
|
|
|
logExecute(address:number);
|
2019-08-22 20:10:15 +00:00
|
|
|
logInterrupt(type:number);
|
|
|
|
}
|
|
|
|
|
|
|
|
export interface LogBus {
|
2019-08-22 02:55:32 +00:00
|
|
|
logRead(address:number);
|
|
|
|
logWrite(address:number);
|
2019-08-22 20:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface LogIO {
|
2019-08-22 02:55:32 +00:00
|
|
|
logIORead(address:number);
|
|
|
|
logIOWrite(address:number);
|
2019-08-22 20:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface LogAll extends LogCPU, LogBus, LogIO {
|
2019-08-22 02:55:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// DEBUGGING
|
|
|
|
|
|
|
|
class EmuBreakpoint extends Error {
|
|
|
|
}
|
|
|
|
|