debugging refactoring; fixed up embed.html; started DASM macro parse; Z80 stack view; replay wraps buffer > 120 secs; verilog edits

This commit is contained in:
Steven Hugg 2018-08-24 22:55:16 -04:00
parent cb22adeb62
commit 5b3c415c6f
17 changed files with 158 additions and 119 deletions

View File

@ -34,7 +34,6 @@ TODO:
- facade/kbd shortcuts for emulators, focus - facade/kbd shortcuts for emulators, focus
- update Javatari version? (and others?) - update Javatari version? (and others?)
- unify versioning - unify versioning
- more UI tests
- disassembler for uploaded ROMs - disassembler for uploaded ROMs
- show tool-specific (readonly) include files - show tool-specific (readonly) include files
- verilog debugging/reloading makes it slow - verilog debugging/reloading makes it slow
@ -45,14 +44,13 @@ TODO:
- Verilog compile spins forever? - Verilog compile spins forever?
- go to error in include files - go to error in include files
- BOM in upload/download? - BOM in upload/download?
- stack view for Z80 platforms using memory map
- online tools for music etc - online tools for music etc
- tools (memory, disasm) use debugging state
- text log debugging script - text log debugging script
- NES crt should mark raster pos when debugging - NES crt should mark raster pos when debugging
- intro/help text for each platform - intro/help text for each platform
- vscode/atom extension? - vscode/atom extension?
- navigator.getGamepads - navigator.getGamepads
- VCS library
FYI: Image links for the books on http://8bitworkshop.com/ are broken FYI: Image links for the books on http://8bitworkshop.com/ are broken
On the website the additional grey spacing next to the program line numbers is not dynamically resized when the web browser window size is changed. Intentional? On the website the additional grey spacing next to the program line numbers is not dynamically resized when the web browser window size is changed. Intentional?

View File

@ -79,7 +79,7 @@ window.Javatari.AUTO_START = false;
var PLATFORMS = exports.PLATFORMS; var PLATFORMS = exports.PLATFORMS;
var platform, platform_id; var platform, platform_id;
var qs = (function (a) { var _qs = (function (a) {
if (!a || a.length == 0) if (!a || a.length == 0)
return {}; return {};
var b = {}; var b = {};
@ -135,7 +135,7 @@ function addPageFocusHandlers() {
}); });
} }
function startPlatform() { function startPlatform(qs) {
if (!PLATFORMS[platform_id]) throw Error("Invalid platform '" + platform_id + "'."); if (!PLATFORMS[platform_id]) throw Error("Invalid platform '" + platform_id + "'.");
platform = new PLATFORMS[platform_id]($("#emulator")[0]); platform = new PLATFORMS[platform_id]($("#emulator")[0]);
platform.start(); platform.start();
@ -148,6 +148,17 @@ function startPlatform() {
return true; return true;
} }
function loadPlatform(qs) {
if (qs.data) qs = qs.data;
platform_id = qs['p'];
if (!platform_id) throw('No platform variable!');
var scriptfn = 'gen/platform/' + platform_id.split(/[.-]/)[0] + '.js';
loadScript(scriptfn, () => {
console.log("loaded platform", platform_id);
startPlatform(qs);
});
}
function loadScript(scriptfn, onload) { function loadScript(scriptfn, onload) {
var script = document.createElement('script'); var script = document.createElement('script');
script.onload = onload; script.onload = onload;
@ -158,14 +169,8 @@ function loadScript(scriptfn, onload) {
// start // start
function startEmbed() { function startEmbed() {
installErrorHandler(); installErrorHandler();
// add default platform? window.addEventListener("message", loadPlatform, false);
platform_id = qs['p']; if (_qs['p']) loadPlatform(_qs);
if (!platform_id) throw('No platform variable!');
var scriptfn = 'gen/platform/' + platform_id.split(/[.-]/)[0] + '.js';
loadScript(scriptfn, () => {
console.log("loaded platform", platform_id);
startPlatform();
});
} }
startEmbed(); startEmbed();

View File

@ -234,6 +234,8 @@ if (window.location.host.endsWith('8bitworkshop.com')) {
<p>You can also embed it into an IFRAME:</p> <p>You can also embed it into an IFRAME:</p>
<textarea rows="4" cols="80" id="embedIframeTextarea" class="cliptext"></textarea> <textarea rows="4" cols="80" id="embedIframeTextarea" class="cliptext"></textarea>
<button type="button" class="btn btn-primary" data-clipboard-target="#embedIframeTextarea">Copy IFRAME Tag</button> <button type="button" class="btn btn-primary" data-clipboard-target="#embedIframeTextarea">Copy IFRAME Tag</button>
<p id="embedAdviceWarnIE">Note: These links may be too long for IE/Edge browsers.</p>
<p id="embedAdviceWarnAll">Note: These links may be too long for some browsers.</p>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
Choose one (or none) then Choose one (or none) then

View File

@ -13,7 +13,7 @@ module RAM_sync(clk, addr, din, dout, we);
output [D-1:0] dout; // data output output [D-1:0] dout; // data output
input we; // write enable input we; // write enable
reg [D-1:0] mem [0:(1<<A)-1]; // (1<<A)xD bit memory reg [D-1:0] mem [1<<A]; // (1<<A)xD bit memory
always @(posedge clk) begin always @(posedge clk) begin
if (we) // if write enabled if (we) // if write enabled
@ -34,7 +34,7 @@ module RAM_async(clk, addr, din, dout, we);
output [D-1:0] dout; // data output output [D-1:0] dout; // data output
input we; // write enable input we; // write enable
reg [D-1:0] mem [0:(1<<A)-1]; // (1<<A)xD bit memory reg [D-1:0] mem [1<<A]; // (1<<A)xD bit memory
always @(posedge clk) begin always @(posedge clk) begin
if (we) // if write enabled if (we) // if write enabled
@ -55,7 +55,7 @@ module RAM_async_tristate(clk, addr, data, we);
inout [D-1:0] data; // data in/out inout [D-1:0] data; // data in/out
input we; // write enable input we; // write enable
reg [D-1:0] mem [0:(1<<A)-1]; // (1<<A)xD bit memory reg [D-1:0] mem [1<<A]; // (1<<A)xD bit memory
always @(posedge clk) begin always @(posedge clk) begin
if (we) // if write enabled if (we) // if write enabled

View File

@ -1,5 +1,5 @@
import { RAM, RasterVideo, dumpRAM, dumpStackToString } from "./emu"; import { RAM, RasterVideo, dumpRAM, lookupSymbol } from "./emu";
import { hex } from "./util"; import { hex } from "./util";
import { CodeAnalyzer } from "./analysis"; import { CodeAnalyzer } from "./analysis";
@ -291,7 +291,7 @@ export abstract class Base6502Platform extends BaseFrameBasedPlatform {
switch (category) { switch (category) {
case 'CPU': return cpuStateToLongString_6502(state.c); case 'CPU': return cpuStateToLongString_6502(state.c);
case 'ZPRAM': return dumpRAM(state.b, 0x0, 0x100); case 'ZPRAM': return dumpRAM(state.b, 0x0, 0x100);
case 'Stack': return dumpStackToString(state.b, 0x100, 0x1ff, 0x100+state.c.SP); case 'Stack': return dumpStackToString(<Platform><any>this, state.b, 0x100, 0x1ff, 0x100+state.c.SP, 0x20);
} }
} }
} }
@ -502,11 +502,18 @@ export abstract class BaseZ80Platform extends BaseDebugPlatform {
//this.getOpcodeMetadata = function() { } //this.getOpcodeMetadata = function() { }
getDebugCategories() { getDebugCategories() {
return ['CPU']; return ['CPU','Stack'];
} }
getDebugInfo(category:string, state:EmuState) : string { getDebugInfo(category:string, state:EmuState) : string {
switch (category) { switch (category) {
case 'CPU': return cpuStateToLongString_Z80(state.c); case 'CPU': return cpuStateToLongString_Z80(state.c);
case 'Stack': {
var sp = state.c.SP;
var start = ((sp-1) & 0xff00);
var end = start + 0xff;
if (sp == 0) sp = 0x10000;
return dumpStackToString(<Platform><any>this, [], start, end, sp, 0xcd);
}
} }
} }
} }
@ -852,3 +859,32 @@ export abstract class BaseMAMEPlatform {
} }
} }
export function dumpStackToString(platform:Platform, mem:number[], start:number, end:number, sp:number, jsrop:number) : string {
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;
var jsrofs = (jsrop == 0xcd) ? -3 : -2;
var opcode = read(addr + jsrofs); // might be out of bounds
if (opcode == jsrop) { // JSR
s += "\n$" + hex(sp) + ": ";
s += hex(addr,4) + " " + lookupSymbol(addr);
sp++;
nraw = 0;
} else {
if (nraw == 0)
s += "\n$" + hex(sp) + ": ";
s += hex(read(sp+1)) + " ";
if (++nraw == 8) nraw = 0;
}
}
return s+"\n";
}

View File

@ -430,12 +430,12 @@ export function newAddressDecoder(table : AddressDecoderEntry[], options?:Addres
// STACK DUMP // STACK DUMP
declare var addr2symbol; // address to symbol name map (TODO: import) var addr2symbol = {}; // address to symbol name map (TODO: import)
function lookupSymbol(addr) { export function lookupSymbol(addr) {
var start = addr; var start = addr;
var foundsym; var foundsym;
while (addr >= 0) { while (addr2symbol && addr >= 0) {
var sym = addr2symbol[addr]; var sym = addr2symbol[addr];
if (sym && sym.startsWith('_')) { // return first C symbol we find if (sym && sym.startsWith('_')) { // return first C symbol we find
return addr2symbol[addr] + " + " + (start-addr); return addr2symbol[addr] + " + " + (start-addr);
@ -447,27 +447,3 @@ function lookupSymbol(addr) {
return foundsym || ""; return foundsym || "";
} }
export function dumpStackToString(mem:number[], start:number, end:number, sp:number) : string {
var s = "";
var nraw = 0;
//s = dumpRAM(mem.slice(start,start+end+1), start, end-start+1);
while (sp < end) {
sp++;
// see if there's a JSR on the stack here
// TODO: make work with roms and memory maps
var addr = mem[sp] + mem[sp+1]*256;
var opcode = mem[addr-2]; // might be out of bounds
if (opcode == 0x20) { // JSR
s += "\n$" + hex(sp) + ": ";
s += hex(addr,4) + " " + lookupSymbol(addr);
sp++;
nraw = 0;
} else {
if (nraw == 0)
s += "\n$" + hex(sp) + ": ";
s += hex(mem[sp+1]) + " ";
if (++nraw == 8) nraw = 0;
}
}
return s+"\n";
}

View File

@ -196,15 +196,12 @@ const _Apple2Platform = function(mainElement) {
// 262.5 scanlines per frame // 262.5 scanlines per frame
var clock = 0; var clock = 0;
var debugCond = this.getDebugCallback(); var debugCond = this.getDebugCallback();
var rendered = false;
for (var sl=0; sl<262; sl++) { for (var sl=0; sl<262; sl++) {
for (var i=0; i<cpuCyclesPerLine; i++) { for (var i=0; i<cpuCyclesPerLine; i++) {
if (debugCond && debugCond()) { if (debugCond && debugCond()) {
grparams.dirty = grdirty;
grparams.grswitch = grswitch;
ap2disp.updateScreen();
debugCond = null; debugCond = null;
rendered = true; sl = 999;
break;
} }
clock++; clock++;
cpu.clockPulse(); cpu.clockPulse();
@ -212,11 +209,9 @@ const _Apple2Platform = function(mainElement) {
} }
} }
if (!novideo) { if (!novideo) {
if (!rendered) { grparams.dirty = grdirty;
grparams.dirty = grdirty; grparams.grswitch = grswitch;
grparams.grswitch = grswitch; ap2disp.updateScreen();
ap2disp.updateScreen();
}
video.updateFrame(); video.updateFrame();
} }
} }

View File

@ -304,21 +304,11 @@ const _GalaxianPlatform = function(mainElement, options) {
} }
advance(novideo : boolean) { advance(novideo : boolean) {
var debugCond = this.getDebugCallback();
var targetTstates = cpu.getTstates();
for (var sl=0; sl<scanlinesPerFrame; sl++) { for (var sl=0; sl<scanlinesPerFrame; sl++) {
if (!novideo) { if (!novideo) {
drawScanline(pixels, sl); drawScanline(pixels, sl);
} }
targetTstates += cpuCyclesPerLine; this.runCPU(cpu, cpuCyclesPerLine);
if (debugCond) {
while (cpu.getTstates() < targetTstates) {
if (debugCond && debugCond()) { debugCond = null; }
cpu.runFrame(cpu.getTstates() + 1);
}
} else {
cpu.runFrame(targetTstates);
}
} }
// visible area is 256x224 (before rotation) // visible area is 256x224 (before rotation)
if (!novideo) { if (!novideo) {

View File

@ -120,23 +120,8 @@ const _Midway8080BWPlatform = function(mainElement) {
} }
advance(novideo : boolean) { advance(novideo : boolean) {
var debugCond = this.getDebugCallback();
var targetTstates = cpu.getTstates();
for (var sl=0; sl<224; sl++) { for (var sl=0; sl<224; sl++) {
targetTstates += cpuCyclesPerLine; this.runCPU(cpu, cpuCyclesPerLine);
if (debugCond) {
while (cpu.getTstates() < targetTstates) {
if (debugCond && debugCond()) {
debugCond = null;
break;
}
cpu.runFrame(cpu.getTstates() + 1);
}
if (!debugCond)
break;
} else {
cpu.runFrame(targetTstates);
}
if (sl == 95) if (sl == 95)
cpu.requestInterrupt(0x8); // RST $8 cpu.requestInterrupt(0x8); // RST $8
else if (sl == 223) else if (sl == 223)

View File

@ -1,7 +1,7 @@
"use strict"; "use strict";
import { Platform, Base6502Platform, BaseMAMEPlatform, getOpcodeMetadata_6502, cpuStateToLongString_6502, getToolForFilename_6502 } from "../baseplatform"; import { Platform, Base6502Platform, BaseMAMEPlatform, getOpcodeMetadata_6502, cpuStateToLongString_6502, getToolForFilename_6502, dumpStackToString } from "../baseplatform";
import { PLATFORMS, RAM, newAddressDecoder, padBytes, noise, setKeyboardFromMap, AnimationTimer, RasterVideo, Keys, makeKeycodeMap, dumpRAM, dumpStackToString } from "../emu"; import { PLATFORMS, RAM, newAddressDecoder, padBytes, noise, setKeyboardFromMap, AnimationTimer, RasterVideo, Keys, makeKeycodeMap, dumpRAM } from "../emu";
import { hex, lpad, lzgmini } from "../util"; import { hex, lpad, lzgmini } from "../util";
import { CodeAnalyzer_nes } from "../analysis"; import { CodeAnalyzer_nes } from "../analysis";
import { SampleAudio } from "../audio"; import { SampleAudio } from "../audio";
@ -114,6 +114,7 @@ const _JSNESPlatform = function(mainElement) {
var cycles = nes.cpu._emulate(); var cycles = nes.cpu._emulate();
//if (self.debugCondition && !self.debugBreakState && self.debugClock < 100) console.log(self.debugClock, nes.cpu.REG_PC); //if (self.debugCondition && !self.debugBreakState && self.debugClock < 100) console.log(self.debugClock, nes.cpu.REG_PC);
self.evalDebugCondition(); self.evalDebugCondition();
// TODO: doesn't stop on breakpoint
return cycles; return cycles;
} }
timer = new AnimationTimer(60, this.nextFrame.bind(this)); timer = new AnimationTimer(60, this.nextFrame.bind(this));
@ -230,7 +231,7 @@ const _JSNESPlatform = function(mainElement) {
switch (category) { switch (category) {
case 'CPU': return cpuStateToLongString_6502(state.c); case 'CPU': return cpuStateToLongString_6502(state.c);
case 'ZPRAM': return dumpRAM(state.b, 0x0, 0x100); case 'ZPRAM': return dumpRAM(state.b, 0x0, 0x100);
case 'Stack': return dumpStackToString(state.b, 0x100, 0x1ff, 0x100+state.c.SP); case 'Stack': return dumpStackToString(this, state.b, 0x100, 0x1ff, 0x100+state.c.SP, 0x20);
case 'PPU': return this.ppuStateToLongString(state.ppu, state.b); case 'PPU': return this.ppuStateToLongString(state.ppu, state.b);
} }
} }

View File

@ -115,7 +115,10 @@ var AtariVectorPlatform = function(mainElement) {
var debugCond = self.getDebugCallback(); var debugCond = self.getDebugCallback();
clock = 0; clock = 0;
for (var i=0; i<cpuCyclesPerFrame; i++) { for (var i=0; i<cpuCyclesPerFrame; i++) {
if (debugCond && debugCond()) { debugCond = null; } if (debugCond && debugCond()) {
debugCond = null;
break;
}
clock++; clock++;
if (--nmicount == 0) { if (--nmicount == 0) {
//console.log("NMI", cpu.saveState()); //console.log("NMI", cpu.saveState());
@ -267,7 +270,10 @@ var AtariColorVectorPlatform = function(mainElement) {
var debugCond = self.getDebugCallback(); var debugCond = self.getDebugCallback();
clock = 0; clock = 0;
for (var i=0; i<cpuCyclesPerFrame; i++) { for (var i=0; i<cpuCyclesPerFrame; i++) {
if (debugCond && debugCond()) { debugCond = null; } if (debugCond && debugCond()) {
debugCond = null;
break;
}
clock++; clock++;
if (--nmicount == 0) { if (--nmicount == 0) {
//console.log("NMI", cpu.saveState()); //console.log("NMI", cpu.saveState());

View File

@ -13,9 +13,10 @@ var VERILOG_PRESETS = [
{id:'scoreboard.v', name:'Scoreboard'}, {id:'scoreboard.v', name:'Scoreboard'},
{id:'ball_absolute.v', name:'Ball Motion (absolute position)'}, {id:'ball_absolute.v', name:'Ball Motion (absolute position)'},
{id:'ball_slip_counter.v', name:'Ball Motion (slipping counter)'}, {id:'ball_slip_counter.v', name:'Ball Motion (slipping counter)'},
{id:'paddles.v', name:'Paddle Inputs'},
{id:'ball_paddle.v', name:'Brick Smash Game'}, {id:'ball_paddle.v', name:'Brick Smash Game'},
{id:'ram1.v', name:'RAM Text Display'}, {id:'ram1.v', name:'RAM Text Display'},
{id:'switches.v', name:'Switch Inputs'},
{id:'paddles.v', name:'Paddle Inputs'},
{id:'sprite_bitmap.v', name:'Sprite Bitmaps'}, {id:'sprite_bitmap.v', name:'Sprite Bitmaps'},
{id:'sprite_renderer.v', name:'Sprite Rendering'}, {id:'sprite_renderer.v', name:'Sprite Rendering'},
{id:'racing_game.v', name:'Racing Game'}, {id:'racing_game.v', name:'Racing Game'},

View File

@ -13,7 +13,7 @@ export class StateRecorderImpl implements EmuRecorder {
checkpoints : EmuState[]; checkpoints : EmuState[];
framerecs : FrameRec[]; framerecs : FrameRec[];
frameCount : number; frameCount : number;
lastSeekFrame : number = -1; lastSeekFrame : number;
constructor(platform : Platform) { constructor(platform : Platform) {
this.reset(); this.reset();
@ -29,10 +29,6 @@ export class StateRecorderImpl implements EmuRecorder {
} }
frameRequested() : boolean { frameRequested() : boolean {
// checkpoints full?
if (this.checkpoints.length >= this.maxCheckpoints) {
return false;
}
var controls = { var controls = {
controls:this.platform.saveControlsState(), controls:this.platform.saveControlsState(),
seed:getNoiseSeed() seed:getNoiseSeed()
@ -64,6 +60,14 @@ export class StateRecorderImpl implements EmuRecorder {
recordFrame(state : EmuState) { recordFrame(state : EmuState) {
this.checkpoints.push(state); this.checkpoints.push(state);
// checkpoints full?
if (this.checkpoints.length > this.maxCheckpoints) {
this.checkpoints.shift(); // remove 1st checkpoint
this.framerecs = this.framerecs.slice(this.checkpointInterval);
this.lastSeekFrame -= this.checkpointInterval;
this.frameCount -= this.checkpointInterval;
if (this.callbackStateChanged) this.callbackStateChanged();
}
} }
getStateAtOrBefore(frame : number) : {frame : number, state : EmuState} { getStateAtOrBefore(frame : number) : {frame : number, state : EmuState} {

View File

@ -57,7 +57,7 @@ var current_output; // current ROM
var current_preset_entry : Preset; // current preset object (if selected) var current_preset_entry : Preset; // current preset object (if selected)
var main_file_id : string; // main file ID var main_file_id : string; // main file ID
var symbolmap; // symbol map var symbolmap; // symbol map
var addr2symbol; // address to symbol name map declare var addr2symbol; // address to symbol name map
var compparams; // received build params from worker var compparams; // received build params from worker
var store; // persistent store var store; // persistent store
@ -344,7 +344,10 @@ function _shareEmbedLink(e) {
$("#embedLinkTextarea").text(fulllink); $("#embedLinkTextarea").text(fulllink);
$("#embedIframeTextarea").text(iframelink); $("#embedIframeTextarea").text(iframelink);
$("#embedLinkModal").modal('show'); $("#embedLinkModal").modal('show');
//document.execCommand("copy"); $("#embedAdviceWarnAll").hide();
$("#embedAdviceWarnIE").hide();
if (fulllink.length >= 65536) $("#embedAdviceWarnAll").show();
else if (fulllink.length >= 5120) $("#embedAdviceWarnIE").show();
}); });
return true; return true;
} }

View File

@ -491,11 +491,14 @@ function parseSourceLines(code, lineMatch, offsetMatch, origin) {
function parseDASMListing(code, unresolved, mainFilename) { function parseDASMListing(code, unresolved, mainFilename) {
// 4 08ee a9 00 start lda #01workermain.js:23:5 // 4 08ee a9 00 start lda #01workermain.js:23:5
var lineMatch = /\s*(\d+)\s+(\S+)\s+([0-9a-f]+)\s+([0-9a-f][0-9a-f ]+)?\s+(.+)?/; var lineMatch = /\s*(\d+)\s+(\S+)\s+([0-9a-f]+)\s+([?0-9a-f][?0-9a-f ]+)?\s+(.+)?/i;
var equMatch = /\bequ\b/; var equMatch = /\bequ\b/i;
var macroMatch = /\bMAC\s+(.+)?/i;
var errors = []; var errors = [];
var lines = []; var lines = [];
var macrolines = [];
var lastline = 0; var lastline = 0;
var macros = {};
for (var line of code.split(/\r?\n/)) { for (var line of code.split(/\r?\n/)) {
var linem = lineMatch.exec(line); var linem = lineMatch.exec(line);
if (linem && linem[1]) { if (linem && linem[1]) {
@ -504,9 +507,15 @@ function parseDASMListing(code, unresolved, mainFilename) {
var offset = parseInt(linem[3], 16); var offset = parseInt(linem[3], 16);
var insns = linem[4]; var insns = linem[4];
var restline = linem[5]; var restline = linem[5];
if (insns && insns.startsWith('?')) insns = null;
// inside of main file? // inside of main file?
if (filename == mainFilename) { if (filename == mainFilename) {
if (insns && !restline.match(equMatch)) { // look for MAC statement
var macmatch = macroMatch.exec(restline);
if (macmatch) {
macros[macmatch[1]] = {line:parseInt(linem[1]), file:linem[2].toLowerCase()};
}
else if (insns && !restline.match(equMatch)) {
lines.push({ lines.push({
line:linenum, line:linenum,
offset:offset, offset:offset,
@ -524,6 +533,16 @@ function parseDASMListing(code, unresolved, mainFilename) {
insns:null insns:null
}); });
} }
// inside of macro?
var mac = macros[filename.toLowerCase()];
if (insns && mac) {
macrolines.push({
filename:mac.file,
line:mac.line+linenum,
offset:offset,
insns:insns
});
}
} }
// TODO: check filename too // TODO: check filename too
// TODO: better symbol test (word boundaries) // TODO: better symbol test (word boundaries)
@ -546,7 +565,8 @@ function parseDASMListing(code, unresolved, mainFilename) {
}) })
} }
} }
return {lines:lines, errors:errors}; // TODO: use macrolines
return {lines:lines, macrolines:macrolines, errors:errors};
} }
function assembleDASM(step) { function assembleDASM(step) {
@ -582,7 +602,7 @@ function assembleDASM(step) {
return {errors:errors}; return {errors:errors};
} }
var listings = {}; var listings = {};
listings[lstpath] = {lines:listing.lines}; listings[lstpath] = listing;
// parse include files // parse include files
// TODO: kinda wasted effort // TODO: kinda wasted effort
for (var fn of step.files) { for (var fn of step.files) {

View File

@ -99,33 +99,50 @@ function testPlatform(platid, romname, maxframes, callback) {
platform.nextFrame(); platform.nextFrame();
if (i==10) { if (i==10) {
for (var j=0; j<0x10000; j++) for (var j=0; j<0x10000; j++)
platform.readAddress(j); platform.readAddress(j); // make sure readAddress() doesn't leave side effects
} }
} }
// test replay feature
platform.pause(); platform.pause();
if (maxframes > 120*60)
maxframes = 120*60;
assert.equal(maxframes, rec.numFrames()); assert.equal(maxframes, rec.numFrames());
var state1 = platform.saveState(); var state1 = platform.saveState();
assert.equal(1, rec.loadFrame(1)); assert.equal(1, rec.loadFrame(1));
assert.equal(maxframes, rec.loadFrame(maxframes)); assert.equal(maxframes, rec.loadFrame(maxframes));
var state2 = platform.saveState(); var state2 = platform.saveState();
assert.deepEqual(state1, state2); assert.deepEqual(state1, state2);
// test debug info
var debugs = platform.getDebugCategories();
for (var dcat of debugs) {
var dinfo = platform.getDebugInfo(dcat, state2);
console.log(dcat, dinfo);
assert.ok(dinfo && dinfo.length > 0, dcat + " empty");
assert.ok(dinfo.length < 80*24, dcat + " too long");
assert.ok(dinfo.indexOf('undefined') < 0, dcat + " undefined");
}
return platform; return platform;
} }
describe('Platform Replay', () => { describe('Platform Replay', () => {
it('Should run apple2', () => { it('Should run apple2', () => {
var platform = testPlatform('apple2', 'cosmic.c.rom', 70, (platform, frameno) => { var platform = testPlatform('apple2', 'cosmic.c.rom', 72, (platform, frameno) => {
if (frameno == 60) { if (frameno == 62) {
keycallback(32, 32, 1); // space bar keycallback(32, 32, 1); // space bar
} }
}); });
assert.equal(platform.saveState().kbd, 0x20); // strobe cleared assert.equal(platform.saveState().kbd, 0x20); // strobe cleared
}); });
it('Should run > 120 secs', () => {
var platform = testPlatform('apple2', 'mandel.c.rom', 122*60, (platform, frameno) => {
});
});
it('Should run vcs', () => { it('Should run vcs', () => {
var platform = testPlatform('vcs', 'brickgame.rom', 70, (platform, frameno) => { var platform = testPlatform('vcs', 'brickgame.rom', 72, (platform, frameno) => {
if (frameno == 60) { if (frameno == 62) {
var cstate = platform.saveControlsState(); var cstate = platform.saveControlsState();
cstate.SA = 0xff ^ 0x40; // stick left cstate.SA = 0xff ^ 0x40; // stick left
platform.loadControlsState(cstate); platform.loadControlsState(cstate);
@ -136,8 +153,8 @@ describe('Platform Replay', () => {
}); });
it('Should run nes', () => { it('Should run nes', () => {
var platform = testPlatform('nes', 'shoot2.c.rom', 70, (platform, frameno) => { var platform = testPlatform('nes', 'shoot2.c.rom', 72, (platform, frameno) => {
if (frameno == 60) { if (frameno == 62) {
keycallback(Keys.VK_LEFT.c, Keys.VK_LEFT.c, 1); keycallback(Keys.VK_LEFT.c, Keys.VK_LEFT.c, 1);
} }
}); });
@ -145,16 +162,16 @@ describe('Platform Replay', () => {
}); });
it('Should run vicdual', () => { it('Should run vicdual', () => {
var platform = testPlatform('vicdual', 'snake1.c.rom', 70, (platform, frameno) => { var platform = testPlatform('vicdual', 'snake1.c.rom', 72, (platform, frameno) => {
if (frameno == 60) { if (frameno == 62) {
keycallback(Keys.VK_DOWN.c, Keys.VK_DOWN.c, 1); keycallback(Keys.VK_DOWN.c, Keys.VK_DOWN.c, 1);
} }
}); });
}); });
it('Should run mw8080bw', () => { it('Should run mw8080bw', () => {
var platform = testPlatform('mw8080bw', 'game2.c.rom', 70, (platform, frameno) => { var platform = testPlatform('mw8080bw', 'game2.c.rom', 72, (platform, frameno) => {
if (frameno == 60) { if (frameno == 62) {
keycallback(Keys.VK_LEFT.c, Keys.VK_LEFT.c, 1); keycallback(Keys.VK_LEFT.c, Keys.VK_LEFT.c, 1);
} }
}); });
@ -162,8 +179,8 @@ describe('Platform Replay', () => {
}); });
it('Should run galaxian', () => { it('Should run galaxian', () => {
var platform = testPlatform('galaxian-scramble', 'shoot2.c.rom', 70, (platform, frameno) => { var platform = testPlatform('galaxian-scramble', 'shoot2.c.rom', 72, (platform, frameno) => {
if (frameno == 60) { if (frameno == 62) {
keycallback(Keys.VK_LEFT.c, Keys.VK_LEFT.c, 1); keycallback(Keys.VK_LEFT.c, Keys.VK_LEFT.c, 1);
} }
}); });
@ -171,23 +188,23 @@ describe('Platform Replay', () => {
}); });
it('Should run vector', () => { it('Should run vector', () => {
var platform = testPlatform('vector-z80color', 'game.c.rom', 70, (platform, frameno) => { var platform = testPlatform('vector-z80color', 'game.c.rom', 72, (platform, frameno) => {
if (frameno == 60) { if (frameno == 62) {
keycallback(Keys.VK_UP.c, Keys.VK_UP.c, 1); keycallback(Keys.VK_UP.c, Keys.VK_UP.c, 1);
} }
}); });
}); });
it('Should run williams', () => { it('Should run williams', () => {
var platform = testPlatform('williams-z80', 'game1.c.rom', 70, (platform, frameno) => { var platform = testPlatform('williams-z80', 'game1.c.rom', 72, (platform, frameno) => {
if (frameno == 60) { if (frameno == 62) {
keycallback(Keys.VK_LEFT.c, Keys.VK_LEFT.c, 1); keycallback(Keys.VK_LEFT.c, Keys.VK_LEFT.c, 1);
} }
}); });
}); });
/* /*
it('Should run sound_williams', () => { it('Should run sound_williams', () => {
var platform = testPlatform('sound_williams-z80', 'swave.c.rom', 70, (platform, frameno) => { var platform = testPlatform('sound_williams-z80', 'swave.c.rom', 72, (platform, frameno) => {
if (frameno == 60) { if (frameno == 60) {
keycallback(Keys.VK_2.c, Keys.VK_2.c, 1); keycallback(Keys.VK_2.c, Keys.VK_2.c, 1);
} }

Binary file not shown.