diff --git a/src/baseplatform.ts b/src/baseplatform.ts index 8a9a748a..7b92550a 100644 --- a/src/baseplatform.ts +++ b/src/baseplatform.ts @@ -27,6 +27,7 @@ export interface CpuState { export interface EmuState { c?:CpuState, // CPU state b?:Uint8Array|number[], // RAM (TODO: not for vcs, support Uint8Array) + ram?:Uint8Array, o?:{}, // verilog T?:number, // verilog }; @@ -95,6 +96,7 @@ export interface Platform { getOpcodeMetadata?(opcode:number, offset:number) : OpcodeMetadata; //TODO getSP?() : number; + getPC?() : number; getOriginPC?() : number; newCodeAnalyzer?() : CodeAnalyzer; @@ -413,8 +415,8 @@ export abstract class Base6502Platform extends BaseDebugPlatform { getDebugInfo(category:string, state:EmuState) : string { switch (category) { case 'CPU': return cpuStateToLongString_6502(state.c); - case 'ZPRAM': return dumpRAM(state.b, 0x0, 0x100); - case 'Stack': return dumpStackToString(this, state.b, 0x100, 0x1ff, 0x100+state.c.SP, 0x20); + case 'ZPRAM': return dumpRAM(state.b||state.ram, 0x0, 0x100); + case 'Stack': return dumpStackToString(this, state.b||state.ram, 0x100, 0x1ff, 0x100+state.c.SP, 0x20); } } } @@ -1185,3 +1187,174 @@ export abstract class BasicZ80ScanlinePlatform extends BaseZ80Platform { this.cpu.setTstates(0); } } + +/// new style + +import { Bus, Resettable, FrameBased, VideoSource, SampledAudioSource, AcceptsROM, AcceptsInput, SavesState, HasCPU } from "./devices"; +import { SampledAudio } from "./audio"; + +interface Machine extends Bus, Resettable, FrameBased, AcceptsROM, HasCPU, SavesState { +} + +function hasVideo(arg:any): arg is VideoSource { + return typeof arg.connectVideo === 'function'; +} +function hasAudio(arg:any): arg is SampledAudioSource { + return typeof arg.connectAudio === 'function'; +} +function hasInput(arg:any): arg is AcceptsInput { + return typeof arg.setInput === 'function'; +} + +export abstract class BaseMachinePlatform extends BaseDebugPlatform implements Platform { + machine : T; + mainElement : HTMLElement; + timer : AnimationTimer; + video : RasterVideo; + audio : SampledAudio; + + abstract newMachine() : T; + abstract getToolForFilename(s:string) : string; + abstract getDefaultExtension() : string; + abstract getPresets() : Preset[]; + + constructor(mainElement : HTMLElement) { + super(); + this.mainElement = mainElement; + this.machine = this.newMachine(); + } + + reset() { this.machine.reset(); } + loadState(s) { this.machine.loadState(s); } + saveState() { return this.machine.saveState(); } + getSP() { return this.machine.cpu.getSP(); } + getPC() { return this.machine.cpu.getPC(); } + getCPUState() { return this.machine.cpu.saveState(); } + loadControlsState(s) { if (hasInput(this.machine)) this.machine.loadControlsState(s); } + saveControlsState() { return hasInput(this.machine) && this.machine.saveControlsState(); } + + start() { + var m = this.machine; + this.timer = new AnimationTimer(60, this.nextFrame.bind(this)); + if (hasVideo(m)) { + var vp = m.getVideoParams(); + this.video = new RasterVideo(this.mainElement, vp.width, vp.height); + this.video.create(); + m.connectVideo(this.video.getFrameData()); + } + if (hasAudio(m)) { + var ap = m.getAudioParams(); + this.audio = new SampledAudio(ap.sampleRate); + this.audio.start(); + m.connectAudio(this.audio); + } + if (hasInput(m)) { + this.video.setKeyboardEvents(m.setInput.bind(m)); + // TODO: ControllerPoller + } + } + + loadROM(title, data) { + this.machine.loadROM(data); + this.reset(); + } + + advance(novideo:boolean) { + this.machine.advanceFrame(999999, this.getDebugCallback()); + if (!novideo) this.video.updateFrame(); + } + + isRunning() { + return this.timer.isRunning(); + } + + resume() { + this.timer.start(); + this.audio && this.audio.start(); + } + + pause() { + this.timer.stop(); + this.audio && this.audio.stop(); + } + +// TODO + breakpointHit(targetClock : number) { + console.log(this.debugTargetClock, targetClock, this.debugClock, this.machine.cpu.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); + } + } + runEval(evalfunc : DebugEvalCondition) { + this.setDebugCondition( () => { + if (++this.debugClock >= this.debugTargetClock && this.machine.cpu.isStable()) { + var cpuState = this.getCPUState(); + if (evalfunc(cpuState)) { + this.breakpointHit(this.debugClock); + return true; + } else { + return false; + } + } + }); + } + runUntilReturn() { + var SP0 = this.machine.cpu.getSP(); + this.runEval( (c:CpuState) : boolean => { + return c.SP > SP0; + }); + } + runToFrameClock(clock : number) : void { + this.restartDebugging(); + this.debugTargetClock = clock; + this.runEval(() : boolean => { 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:CpuState) : boolean => { + if (this.debugClock < clock0) { + prevState = this.saveState(); + prevClock = this.debugClock; + return false; + } else { + if (prevState) { + this.loadState(prevState); + this.debugClock = prevClock; + } + return true; + } + }); + } +} + +export abstract class Base6502MachinePlatform extends BaseMachinePlatform { + + getOpcodeMetadata = getOpcodeMetadata_6502; + getToolForFilename = getToolForFilename_6502; + + disassemble(pc:number, read:(addr:number)=>number) : DisasmLine { + return disassemble6502(pc, read(pc), read(pc+1), read(pc+2)); + } + getDebugCategories() { + return ['CPU','ZPRAM','Stack']; + } + getDebugInfo(category:string, state:EmuState) : string { + switch (category) { + case 'CPU': return cpuStateToLongString_6502(state.c); + case 'ZPRAM': return dumpRAM(state.b||state.ram, 0x0, 0x100); + case 'Stack': return dumpStackToString(this, state.b||state.ram, 0x100, 0x1ff, 0x100+state.c.SP, 0x20); + } + } + +} diff --git a/src/cpu/MOS6502.ts b/src/cpu/MOS6502.ts index 158de886..ee058a56 100644 --- a/src/cpu/MOS6502.ts +++ b/src/cpu/MOS6502.ts @@ -1755,7 +1755,8 @@ var _MOS6502 = function() { this.saveState = function():MOS6502State { return { - PC: PC, A: A, X: X, Y: Y, SP: SP, + PC: (PC-1) & 0xffff, + A: A, X: X, Y: Y, SP: SP, N: N, V: V, D: D, I: I, Z: Z, C: C, T: T, o: opcode, R: RDY?1:0, d: data, AD: AD, BA: BA, BC: BALCrossed?1:0, IA: IA, @@ -1764,7 +1765,8 @@ var _MOS6502 = function() { }; this.loadState = function(state:MOS6502State) { - PC = state.PC; A = state.A; X = state.X; Y = state.Y; SP = state.SP; + PC = (state.PC+1) & 0xffff; + A = state.A; X = state.X; Y = state.Y; SP = state.SP; N = state.N; V = state.V; D = state.D; I = state.I; Z = state.Z; C = state.C; T = state.T; opcode = state.o; RDY = !!state.R; data = state.d; AD = state.AD; BA = state.BA; BALCrossed = !!state.BC; IA = state.IA; @@ -1887,12 +1889,10 @@ var _MOS6502 = function() { } this.getSP = function() { return SP; } - this.getPC = function() { return PC; } + this.getPC = function() { return (PC-1) & 0xffff; } this.getT = function() { return T; } this.isPCStable = function() { - // TODO: gotta fix base class first - //return T < 0 || T == instruction.length-1; return T == 0; } }; @@ -1936,7 +1936,7 @@ export class MOS6502 implements CPU, ClockBased, SavesState, Inter interrupt(itype:number) { this.interruptType = itype; } - getSP() { + getSP() { return this.cpu.getSP(); } getPC() { diff --git a/src/cpu/ZilogZ80.ts b/src/cpu/ZilogZ80.ts index ba41e46e..4b1eff6e 100644 --- a/src/cpu/ZilogZ80.ts +++ b/src/cpu/ZilogZ80.ts @@ -1742,6 +1742,7 @@ export class Z80 implements CPU, InstructionBased, IOBusConnected, SavesState { - saveState() : T; - loadState(state:T) : void; +export interface SavesState { + saveState() : S; + loadState(state:S) : void; } export interface Bus { @@ -72,20 +72,25 @@ export interface IOBusConnected { connectIOBus(bus:Bus) : void; } -export interface CPU extends MemoryBusConnected, Resettable { +export interface CPU extends MemoryBusConnected, Resettable, SavesState { getPC() : number; getSP() : number; + isStable() : boolean; } -export interface Interruptable { - interrupt(type:T) : void; +export interface HasCPU { + cpu : CPU; +} + +export interface Interruptable { + interrupt(type:IT) : void; } // TODO -export interface AcceptsInput { +export interface AcceptsInput { setInput(key:number, code:number, flags:number) : void; - //loadControlState(); - //saveControlState(); + loadControlsState(cs:CS); + saveControlsState() : CS; } // TODO? diff --git a/src/machine/apple2.ts b/src/machine/apple2.ts index 99b4aba6..bac5cda8 100644 --- a/src/machine/apple2.ts +++ b/src/machine/apple2.ts @@ -1,7 +1,7 @@ "use strict"; import { MOS6502, MOS6502State } from "../cpu/MOS6502"; -import { Bus, RasterFrameBased, SavesState, AcceptsROM, noise, Resettable, SampledAudioSource, SampledAudioSink } from "../devices"; +import { Bus, RasterFrameBased, SavesState, AcceptsROM, AcceptsInput, noise, Resettable, SampledAudioSource, SampledAudioSink, HasCPU } from "../devices"; import { KeyFlags } from "../emu"; // TODO import { lzgmini } from "../util"; @@ -16,18 +16,22 @@ const HDR_SIZE = PGM_BASE - LOAD_BASE; interface AppleIIStateBase { ram : Uint8Array; - rnd,kbdlatch,soundstate : number; + rnd,soundstate : number; auxRAMselected,writeinhibit : boolean; - auxRAMbank,bank2rdoffset,bank2wroffset : number; - grdirty : boolean[]; + auxRAMbank : number; } -interface AppleIIState extends AppleIIStateBase { +interface AppleIIControlsState { + kbdlatch : number; +} + +interface AppleIIState extends AppleIIStateBase, AppleIIControlsState { c : MOS6502State; + grswitch : number; } -export class AppleII implements Bus, Resettable, RasterFrameBased, SampledAudioSource, AcceptsROM, - AppleIIStateBase, SavesState { +export class AppleII implements HasCPU, Bus, Resettable, RasterFrameBased, SampledAudioSource, AcceptsROM, + AppleIIStateBase, SavesState, AcceptsInput { ram = new Uint8Array(0x13000); // 64K + 16K LC RAM - 4K hardware + 12K ROM rom : Uint8Array; @@ -58,7 +62,7 @@ export class AppleII implements Bus, Resettable, RasterFrameBased, SampledAudioS this.ram[0xbf6f] = 0x01; // fake DOS detect for C this.cpu.connectMemoryBus(this); } - saveState() { + saveState() : AppleIIState { // TODO: automagic return { c: this.cpu.saveState(), @@ -66,27 +70,45 @@ export class AppleII implements Bus, Resettable, RasterFrameBased, SampledAudioS rnd: this.rnd, kbdlatch: this.kbdlatch, soundstate: this.soundstate, + grswitch: this.grparams.grswitch, auxRAMselected: this.auxRAMselected, - writeinhibit: this.writeinhibit, auxRAMbank: this.auxRAMbank, - bank2rdoffset: this.bank2rdoffset, - bank2wroffset: this.bank2wroffset, - grdirty: this.grdirty.slice(), + writeinhibit: this.writeinhibit, }; } - loadState(s) { + loadState(s:AppleIIState) { this.cpu.loadState(s.c); this.ram.set(s.ram); - // TODO + this.rnd = s.rnd; + this.kbdlatch = s.kbdlatch; + this.soundstate = s.soundstate; + this.grparams.grswitch = s.grswitch; + this.auxRAMselected = s.auxRAMselected; + this.auxRAMbank = s.auxRAMbank; + this.writeinhibit = s.writeinhibit; + this.setupLanguageCardConstants(); + this.ap2disp.invalidate(); // repaint entire screen + } + saveControlsState() : AppleIIControlsState { + return {kbdlatch:this.kbdlatch}; + } + loadControlsState(s:AppleIIControlsState) { + this.kbdlatch = s.kbdlatch; } reset() { this.cpu.reset(); + // execute until $c600 boot + for (var i=0; i<2000000; i++) { + this.cpu.advanceClock(); + if (this.cpu.getPC() == 0xc602) { + break; + } + } } noise() : number { return (this.rnd = noise(this.rnd)) & 0xff; } - read(address:number) : number { - address &= 0xffff; + readConst(address:number) : number { if (address < 0xc000) { return this.ram[address]; } else if (address >= 0xd000) { @@ -96,6 +118,13 @@ export class AppleII implements Bus, Resettable, RasterFrameBased, SampledAudioS return this.ram[address]; else return this.ram[address + this.bank2rdoffset]; + } else + return 0; + } + read(address:number) : number { + address &= 0xffff; + if (address < 0xc000 || address >= 0xd000) { + return this.readConst(address); } else if (address < 0xc100) { var slot = (address >> 4) & 0x0f; switch (slot) @@ -191,7 +220,7 @@ export class AppleII implements Bus, Resettable, RasterFrameBased, SampledAudioS advanceFrame(maxCycles, trap) : number { maxCycles = Math.min(maxCycles, cpuCyclesPerFrame); for (var i=0; i> 7); - var grswitch = GR_TXMODE; - var kbdlatch = 0; - var soundstate = 0; - var pgmbin; - // language card switches - var auxRAMselected = false; - var auxRAMbank = 1; - var writeinhibit = true; - // value to add when reading & writing each of these banks - // bank 1 is E000-FFFF, bank 2 is D000-DFFF - var bank2rdoffset=0, bank2wroffset=0; - var grparams : AppleGRParams; - var scanline : number; - - class Apple2Platform extends Base6502Platform implements Platform { - - getPresets() { - return APPLE2_PRESETS; - } - start() { - cpu = new jt.M6502(); - ram = new RAM(0x13000); // 64K + 16K LC RAM - 4K hardware - // ROM - var rom = new lzgmini().decode(APPLEIIGO_LZG); - ram.mem.set(rom, 0xd000); - ram.mem[0xbf00] = 0x4c; // fake DOS detect for C - ram.mem[0xbf6f] = 0x01; // fake DOS detect for C - // bus - bus = { - read: function(address) { - address &= 0xffff; - if (address < 0xc000) { - return ram.mem[address]; - } else if (address >= 0xd000) { - //return rom[address - 0xd000] & 0xff; - //console.log(hex(address), rom[address-0xd000], ram.mem[address]); - if (!auxRAMselected) - return rom[address - 0xd000]; - else if (address >= 0xe000) - return ram.mem[address]; - else - return ram.mem[address + bank2rdoffset]; - } else if (address < 0xc100) { - var slot = (address >> 4) & 0x0f; - switch (slot) - { - case 0: - return kbdlatch; - case 1: - kbdlatch &= 0x7f; - break; - case 3: - soundstate = soundstate ^ 1; - break; - case 5: - if ((address & 0x0f) < 8) { - // graphics - if ((address & 1) != 0) - grswitch |= 1 << ((address >> 1) & 0x07); - else - grswitch &= ~(1 << ((address >> 1) & 0x07)); - } - break; - case 6: - // tapein, joystick, buttons - switch (address & 7) { - // buttons (off) - case 1: - case 2: - case 3: - return noise() & 0x7f; - // joystick - case 4: - case 5: - return noise() | 0x80; - default: - return noise(); - } - case 7: - // joy reset - if (address == 0xc070) - return noise() | 0x80; - case 8: - return doLanguageCardIO(address); - case 9: case 10: case 11: case 12: case 13: case 14: case 15: - return noise(); // return slots[slot-8].doIO(address, value); - } - } else { - switch (address) { - // JMP VM_BASE - case 0xc600: { - // load program into RAM - if (pgmbin) - ram.mem.set(pgmbin.slice(HDR_SIZE), PGM_BASE); - return 0x4c; - } - case 0xc601: return VM_BASE&0xff; - case 0xc602: return (VM_BASE>>8)&0xff; - default: return noise(); - } - } - return noise(); - }, - write: function(address, val) { - address &= 0xffff; - val &= 0xff; - if (address < 0xc000) { - ram.mem[address] = val; - grdirty[address>>7] = 1; - } else if (address < 0xc100) { - this.read(address); // strobe address, discard result - } else if (address >= 0xd000 && !writeinhibit) { - if (address >= 0xe000) - ram.mem[address] = val; - else - ram.mem[address + bank2wroffset] = val; - } - } - }; - cpu.connectBus(bus); - // create video/audio - video = new RasterVideo(mainElement,280,192); - audio = new SampleAudio(cpuFrequency); - video.create(); - video.setKeyboardEvents((key,code,flags) => { - if (flags & KeyFlags.KeyPress) { - // convert to uppercase for Apple ][ - if (code >= 0x61 && code <= 0x7a) - code -= 32; - if (code >= 32) { - if (code >= 65 && code < 65+26) { - if (flags & KeyFlags.Ctrl) - code -= 64; // ctrl - } - kbdlatch = (code | 0x80) & 0xff; - } - } else if (flags & KeyFlags.KeyDown) { - code = 0; - switch (key) { - case 13: code=13; break; // return - case 37: code=8; break; // left - case 39: code=21; break; // right - case 38: code=11; break; // up - case 40: code=10; break; // down - } - if (code) - kbdlatch = (code | 0x80) & 0xff; - } - }); - var idata = video.getFrameData(); - grparams = {dirty:grdirty, grswitch:grswitch, mem:ram.mem}; - ap2disp = new Apple2Display(idata, grparams); - timer = new AnimationTimer(60, this.nextFrame.bind(this)); - } - - advance(novideo : boolean) { - // 262.5 scanlines per frame - var clock = 0; - var debugCond = this.getDebugCallback(); - for (var sl=0; sl<262; sl++) { - scanline = sl; - for (var i=0; i 0xc000-0xcfff - else - bank2rdoffset = 0x3000; // map 0xd000-0xdfff -> 0x10000-0x10fff - if (auxRAMbank == 2) - bank2wroffset = -0x1000; // map 0xd000-0xdfff -> 0xc000-0xcfff - else - bank2wroffset = 0x3000; // map 0xd000-0xdfff -> 0x10000-0x10fff - } - - return new Apple2Platform(); // return inner class from constructor -}; - -var Apple2Display = function(pixels : number[], apple : AppleGRParams) { - var XSIZE = 280; - var YSIZE = 192; - var PIXELON = 0xffffffff; - var PIXELOFF = 0xff000000; - - var oldgrmode = -1; - var textbuf = new Array(40*24); - - const flashInterval = 500; - - const loresColor = [ - (0xff000000), (0xffff00ff), (0xff00007f), (0xff7f007f), - (0xff007f00), (0xff7f7f7f), (0xff0000bf), (0xff0000ff), - (0xffbf7f00), (0xffffbf00), (0xffbfbfbf), (0xffff7f7f), - (0xff00ff00), (0xffffff00), (0xff00bf7f), (0xffffffff), - ]; - - const text_lut = [ - 0x000, 0x080, 0x100, 0x180, 0x200, 0x280, 0x300, 0x380, - 0x028, 0x0a8, 0x128, 0x1a8, 0x228, 0x2a8, 0x328, 0x3a8, - 0x050, 0x0d0, 0x150, 0x1d0, 0x250, 0x2d0, 0x350, 0x3d0 - ]; - - const hires_lut = [ - 0x0000, 0x0400, 0x0800, 0x0c00, 0x1000, 0x1400, 0x1800, 0x1c00, - 0x0080, 0x0480, 0x0880, 0x0c80, 0x1080, 0x1480, 0x1880, 0x1c80, - 0x0100, 0x0500, 0x0900, 0x0d00, 0x1100, 0x1500, 0x1900, 0x1d00, - 0x0180, 0x0580, 0x0980, 0x0d80, 0x1180, 0x1580, 0x1980, 0x1d80, - 0x0200, 0x0600, 0x0a00, 0x0e00, 0x1200, 0x1600, 0x1a00, 0x1e00, - 0x0280, 0x0680, 0x0a80, 0x0e80, 0x1280, 0x1680, 0x1a80, 0x1e80, - 0x0300, 0x0700, 0x0b00, 0x0f00, 0x1300, 0x1700, 0x1b00, 0x1f00, - 0x0380, 0x0780, 0x0b80, 0x0f80, 0x1380, 0x1780, 0x1b80, 0x1f80, - 0x0028, 0x0428, 0x0828, 0x0c28, 0x1028, 0x1428, 0x1828, 0x1c28, - 0x00a8, 0x04a8, 0x08a8, 0x0ca8, 0x10a8, 0x14a8, 0x18a8, 0x1ca8, - 0x0128, 0x0528, 0x0928, 0x0d28, 0x1128, 0x1528, 0x1928, 0x1d28, - 0x01a8, 0x05a8, 0x09a8, 0x0da8, 0x11a8, 0x15a8, 0x19a8, 0x1da8, - 0x0228, 0x0628, 0x0a28, 0x0e28, 0x1228, 0x1628, 0x1a28, 0x1e28, - 0x02a8, 0x06a8, 0x0aa8, 0x0ea8, 0x12a8, 0x16a8, 0x1aa8, 0x1ea8, - 0x0328, 0x0728, 0x0b28, 0x0f28, 0x1328, 0x1728, 0x1b28, 0x1f28, - 0x03a8, 0x07a8, 0x0ba8, 0x0fa8, 0x13a8, 0x17a8, 0x1ba8, 0x1fa8, - 0x0050, 0x0450, 0x0850, 0x0c50, 0x1050, 0x1450, 0x1850, 0x1c50, - 0x00d0, 0x04d0, 0x08d0, 0x0cd0, 0x10d0, 0x14d0, 0x18d0, 0x1cd0, - 0x0150, 0x0550, 0x0950, 0x0d50, 0x1150, 0x1550, 0x1950, 0x1d50, - 0x01d0, 0x05d0, 0x09d0, 0x0dd0, 0x11d0, 0x15d0, 0x19d0, 0x1dd0, - 0x0250, 0x0650, 0x0a50, 0x0e50, 0x1250, 0x1650, 0x1a50, 0x1e50, - 0x02d0, 0x06d0, 0x0ad0, 0x0ed0, 0x12d0, 0x16d0, 0x1ad0, 0x1ed0, - 0x0350, 0x0750, 0x0b50, 0x0f50, 0x1350, 0x1750, 0x1b50, 0x1f50, - 0x03d0, 0x07d0, 0x0bd0, 0x0fd0, 0x13d0, 0x17d0, 0x1bd0, 0x1fd0 - ]; - - var colors_lut; - - /** - * This function makes the color lookup table for hires mode. - * We make a table of 1024 * 2 * 7 entries. - * Why? Because we assume each color byte has 10 bits - * (8 real bits + 1 on each side) and we need different colors - * for odd and even addresses (2) and each byte displays 7 pixels. - */ - { - colors_lut = new Array(256*4*2*7); - var i,j; - var c1,c2,c3 = 15; - var base = 0; - - // go thru odd and even - for (j=0; j<2; j++) - { - // go thru 1024 values - for (var b1=0; b1<1024; b1++) - { - // see if the hi bit is set - if ((b1 & 0x80) == 0) - { - c1 = 1; c2 = 12; // red & green - } else - { - c1 = 7; c2 = 9; // blue & orange - } - // make a value consisting of: - // the 8th bit, then bits 0-7, then the 9th bit - var b = ((b1 & 0x100) >> 8) | ((b1 & 0x7f) << 1) | - ((b1 & 0x200) >> 1); - // go through each pixel - for (i=0; i<7; i++) - { - var c; - // is this pixel lit? - if (((2<> 4]; - for (i=0; i<4; i++) - { - pixels[base] = - pixels[base+1] = - pixels[base+2] = - pixels[base+3] = - pixels[base+4] = - pixels[base+5] = - pixels[base+6] = c; - base += XSIZE; - } - } - - function drawTextChar(x, y, b, invert) - { - var base = (y<<3)*XSIZE + x*7; // (x<<2) + (x<<1) + x - var on,off; - if (invert) - { - on = PIXELOFF; - off = PIXELON; - } else - { - on = PIXELON; - off = PIXELOFF; - } - - for (var yy=0; yy<8; yy++) - { - var chr = apple2_charset[(b<<3)+yy]; - pixels[base] = ((chr & 64) > 0)?on:off; - pixels[base+1] = ((chr & 32) > 0)?on:off; - pixels[base+2] = ((chr & 16) > 0)?on:off; - pixels[base+3] = ((chr & 8) > 0)?on:off; - pixels[base+4] = ((chr & 4) > 0)?on:off; - pixels[base+5] = ((chr & 2) > 0)?on:off; - pixels[base+6] = ((chr & 1) > 0)?on:off; - base += XSIZE; - } - } - - function drawHiresLines(y, maxy) - { - var yb = y*XSIZE; - for (; y < maxy; y++) - { - var base = hires_lut[y] + (((apple.grswitch & GR_PAGE1) != 0) ? 0x4000 : 0x2000); - if (!apple.dirty[base >> 7]) - { - yb += XSIZE; - continue; - } - var c1, c2; - var b = 0; - var b1 = apple.mem[base] & 0xff; - for (var x1=0; x1<20; x1++) - { - var b2 = apple.mem[base+1] & 0xff; - var b3 = apple.mem[base+2] & 0xff; - var d1 = (((b&0x40)<<2) | b1 | b2<<9) & 0x3ff; - for (var i=0; i<7; i++) - pixels[yb+i] = colors_lut[d1*7+i]; - var d2 = (((b1&0x40)<<2) | b2 | b3<<9) & 0x3ff; - for (var i=0; i<7; i++) - pixels[yb+7+i] = colors_lut[d2*7+7168+i]; - yb += 14; - base += 2; - b = b2; - b1 = b3; - } - } - } - - function drawLoresLine(y) - { - // get the base address of this line - var base = text_lut[y] + - (((apple.grswitch & GR_PAGE1) != 0) ? 0x800 : 0x400); - // if (!dirty[base >> 7]) - // return; - for (var x=0; x<40; x++) - { - var b = apple.mem[base+x] & 0xff; - // if the char. changed, draw it - if (b != textbuf[y*40+x]) - { - drawLoresChar(x, y, b); - textbuf[y*40+x] = b; - } - } - } - - function drawTextLine(y, flash) - { - // get the base address of this line - var base = text_lut[y] + - (((apple.grswitch & GR_PAGE1) != 0) ? 0x800 : 0x400); - // if (!dirty[base >> 7]) - // return; - for (var x=0; x<40; x++) - { - var b = apple.mem[base+x] & 0xff; - var invert; - // invert flash characters 1/2 of the time - if (b >= 0x80) - { - invert = false; - } else if (b >= 0x40) - { - invert = flash; - if (flash) - b -= 0x40; - else - b += 0x40; - } else - invert = true; - // if the char. changed, draw it - if (b != textbuf[y*40+x]) - { - drawTextChar(x, y, b & 0x7f, invert); - textbuf[y*40+x] = b; - } - } - } - - this.updateScreen = function(totalrepaint) - { - var y; - var flash = (new Date().getTime() % (flashInterval<<1)) > flashInterval; - - // if graphics mode changed, repaint whole screen - if (apple.grswitch != oldgrmode) - { - oldgrmode = apple.grswitch; - totalrepaint = true; - } - if (totalrepaint) - { - // clear textbuf if in text mode - if ((apple.grswitch & GR_TXMODE) != 0 || (apple.grswitch & GR_MIXMODE) != 0) - { - for (y=0; y<24; y++) - for (var x=0; x<40; x++) - textbuf[y*40+x] = -1; - } - for (var i=0; i implements Platform { - mainElement : HTMLElement; - machine : AppleII; - timer : AnimationTimer; - video : RasterVideo; - audio : SampledAudio; + newMachine() { return new AppleII(); } + getPresets() { return APPLE2_PRESETS; } + getDefaultExtension() { return ".c"; }; + readAddress(a) { return this.machine.readConst(a); } + // TODO loadBios(bios) { this.machine.loadBIOS(a); } - constructor(mainElement : HTMLElement) { - super(); - this.mainElement = mainElement; - } - getOpcodeMetadata = getOpcodeMetadata_6502; - getDefaultExtension () { return ".c"; }; - getToolForFilename = getToolForFilename_6502; - getPresets () { return APPLE2_PRESETS; } - getCPUState() { return this.machine.cpu.saveState(); } - saveState() { return this.machine.saveState(); } - loadState(s) { this.machine.loadState(s); } - readAddress(a) { return this.machine.read(a); } - - start() { - this.machine = new AppleII(); - this.timer = new AnimationTimer(60, this.nextFrame.bind(this)); - var vp = this.machine.getVideoParams(); - this.video = new RasterVideo(this.mainElement, vp.width, vp.height); - this.video.create(); - this.video.setKeyboardEvents(this.machine.setInput.bind(this.machine)); - this.machine.connectVideo(this.video.getFrameData()); - var ap = this.machine.getAudioParams(); - this.audio = new SampledAudio(ap.sampleRate); - this.audio.start(); - this.machine.connectAudio(this.audio); - } - reset() { - this.machine.reset(); - } - loadROM(title, data) { - this.machine.loadROM(data); - this.reset(); - } - advance(novideo:boolean) { - this.machine.advanceFrame(999999, this.getDebugCallback()); - if (!novideo) this.video.updateFrame(); - } - isRunning() { - return this.timer.isRunning(); - } - resume() { - this.timer.start(); - this.audio.start(); - } - pause() { - this.timer.stop(); - this.audio.stop(); - } - // debugging - restartDebugging() { - super.restartDebugging(); - // TODO - var clock = 0; - var cpuhook = new CPUClockHook(this.machine.cpu, { - logExecute: (a:number) => { - clock++; - }, - logInterrupt: (a:number) => { - } - }); - } } -PLATFORMS['apple2'] = _Apple2Platform; PLATFORMS['apple2.mame'] = Apple2MAMEPlatform; -PLATFORMS['apple2.new'] = NewApple2Platform; +PLATFORMS['apple2'] = NewApple2Platform; diff --git a/test/cli/6502/test6502sim.js b/test/cli/6502/test6502sim.js index fbb02c2d..99744bf1 100644 --- a/test/cli/6502/test6502sim.js +++ b/test/cli/6502/test6502sim.js @@ -21,6 +21,7 @@ describe('MOS6502', function() { s0.PC = 0x400; cpu.loadState(s0); for (var i=0; i<100000000; i++) { + //console.log(cpu.isStable(), cpu.saveState().o); cpu.advanceClock(); var pc = cpu.getPC(); if (pc == 0x3469) break; // success! diff --git a/test/cli/testplatforms.js b/test/cli/testplatforms.js index 6ba68308..bf213dbe 100644 --- a/test/cli/testplatforms.js +++ b/test/cli/testplatforms.js @@ -27,11 +27,14 @@ includeInThisContext('tss/js/tss/AudioLooper.js'); //includeInThisContext("jsnes/dist/jsnes.min.js"); global.jsnes = require("jsnes/dist/jsnes.min.js"); +//var devices = require('gen/devices.js'); var emu = require('gen/emu.js'); var Keys = emu.Keys; var audio = require('gen/audio.js'); var recorder = require('gen/recorder.js'); +//var _6502 = require('gen/cpu/MOS6502.js'); var _apple2 = require('gen/platform/apple2.js'); +//var m_apple2 = require('gen/machine/apple2.js'); var _vcs = require('gen/platform/vcs.js'); var _nes = require('gen/platform/nes.js'); var _vicdual = require('gen/platform/vicdual.js'); @@ -171,7 +174,7 @@ describe('Platform Replay', () => { keycallback(32, 32, 128); // space bar } }); - assert.equal(platform.saveState().kbd, 0x20); // strobe cleared + assert.equal(platform.saveState().kbdlatch, 0x20); // strobe cleared }); it('Should run > 120 secs', () => {