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:
parent
cb22adeb62
commit
5b3c415c6f
|
@ -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?
|
||||||
|
|
25
embed.html
25
embed.html
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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";
|
||||||
|
}
|
||||||
|
|
30
src/emu.ts
30
src/emu.ts
|
@ -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";
|
|
||||||
}
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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'},
|
||||||
|
|
|
@ -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} {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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.
Loading…
Reference in New Issue