mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-05-28 08:41:30 +00:00
Z80 Space Invaders starting to work; deferred worker module load
This commit is contained in:
parent
bd551f7311
commit
7fce763f64
17
index.html
17
index.html
|
@ -165,6 +165,14 @@ a.dropdown-toggle {
|
||||||
-moz-border-radius:6px 0 6px 6px;
|
-moz-border-radius:6px 0 6px 6px;
|
||||||
border-radius:6px 0 6px 6px;
|
border-radius:6px 0 6px 6px;
|
||||||
}
|
}
|
||||||
|
canvas {
|
||||||
|
image-rendering: optimizeSpeed; /* Older versions of FF */
|
||||||
|
image-rendering: -moz-crisp-edges; /* FF 6.0+ */
|
||||||
|
image-rendering: -webkit-optimize-contrast; /* Safari */
|
||||||
|
image-rendering: -o-crisp-edges; /* OS X & Windows Opera (12.02+) */
|
||||||
|
image-rendering: pixelated; /* Awesome future-browsers */
|
||||||
|
-ms-interpolation-mode: nearest-neighbor; /* IE */
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -177,14 +185,14 @@ a.dropdown-toggle {
|
||||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton">
|
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton">
|
||||||
<li><a class="dropdown-item" href="#" id="item_new_file">New File...</a></li>
|
<li><a class="dropdown-item" href="#" id="item_new_file">New File...</a></li>
|
||||||
<li><a class="dropdown-item" href="#" id="item_share_file">Share File...</a></li>
|
<li><a class="dropdown-item" href="#" id="item_share_file">Share File...</a></li>
|
||||||
<li><a class="dropdown-item" href="#" id="item_reset_file">Reset to Original...</a></li>
|
<li><a class="dropdown-item" href="#" id="item_reset_file">Revert to Original...</a></li>
|
||||||
<li class="dropdown dropdown-submenu">
|
<li class="dropdown dropdown-submenu">
|
||||||
<a tabindex="-1" href="#">Platform</a>
|
<a tabindex="-1" href="#">Platform</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a class="dropdown-item" href="?platform=vcs" id="item_platform_vcs">Atari VCS</a></li>
|
<li><a class="dropdown-item" href="?platform=vcs" id="item_platform_vcs">Atari VCS</a></li>
|
||||||
<li><a class="dropdown-item" href="?platform=apple2" id="item_platform_apple2">Apple ][</a></li>
|
<li><a class="dropdown-item" href="?platform=apple2" id="item_platform_apple2">Apple ][</a></li>
|
||||||
<li><a class="dropdown-item" href="?platform=atarivec" id="item_platform_atarivec">Asteroids</a></li>
|
<li><a class="dropdown-item" href="?platform=atarivec" id="item_platform_atarivec">Asteroids</a></li>
|
||||||
<li><a class="dropdown-item" href="?platform=exidy" id="item_platform_exidy">Exidy</a></li>
|
<li><a class="dropdown-item" href="?platform=spaceinv" id="item_platform_spaceinv">Space Invaders</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li class="dropdown dropdown-submenu">
|
<li class="dropdown dropdown-submenu">
|
||||||
|
@ -202,9 +210,9 @@ a.dropdown-toggle {
|
||||||
<button id="dbg_go" type="button" title="Run"><img src="images/play.png"></button>
|
<button id="dbg_go" type="button" title="Run"><img src="images/play.png"></button>
|
||||||
<button id="dbg_step" type="submit" title="Step"><img src="images/singlestep.png"></button>
|
<button id="dbg_step" type="submit" title="Step"><img src="images/singlestep.png"></button>
|
||||||
<button id="dbg_toline" type="submit" title="Run To Line"><img src="images/runtoline.png"></button>
|
<button id="dbg_toline" type="submit" title="Run To Line"><img src="images/runtoline.png"></button>
|
||||||
<button id="dbg_reset" type="submit" title="Reset and Run To Line"><img src="images/resetandrun.png"></button>
|
|
||||||
<button id="dbg_stepout" type="submit" title="Step Out of Subroutine">RTS</button>
|
<button id="dbg_stepout" type="submit" title="Step Out of Subroutine">RTS</button>
|
||||||
<button id="dbg_stepback" type="submit" title="Step Backwards"><<</button>
|
<button id="dbg_stepback" type="submit" title="Step Backwards"><<</button>
|
||||||
|
<button id="dbg_reset" type="submit" title="Reset and Break"><img src="images/resetandrun.png"></button>
|
||||||
<button id="dbg_timing" type="submit" title="See Timing" style="display:none"><img src="images/timing.png"></button>
|
<button id="dbg_timing" type="submit" title="See Timing" style="display:none"><img src="images/timing.png"></button>
|
||||||
<button id="dbg_disasm" type="submit" title="Toggle Disassembly">#</button>
|
<button id="dbg_disasm" type="submit" title="Toggle Disassembly">#</button>
|
||||||
</span>
|
</span>
|
||||||
|
@ -261,13 +269,14 @@ a.dropdown-toggle {
|
||||||
<link rel="stylesheet" href="codemirror/addon/dialog/dialog.css">
|
<link rel="stylesheet" href="codemirror/addon/dialog/dialog.css">
|
||||||
|
|
||||||
<script src="javatari.js/release/javatari/javatari.js"></script>
|
<script src="javatari.js/release/javatari/javatari.js"></script>
|
||||||
|
<script src="src/cpu/z80.js"></script>
|
||||||
<script src="src/emu.js"></script>
|
<script src="src/emu.js"></script>
|
||||||
<script src="src/util.js"></script>
|
<script src="src/util.js"></script>
|
||||||
<script src="src/disasm.js"></script>
|
<script src="src/disasm.js"></script>
|
||||||
<script src="src/platform/vcs.js"></script>
|
<script src="src/platform/vcs.js"></script>
|
||||||
<script src="src/platform/apple2.js"></script>
|
<script src="src/platform/apple2.js"></script>
|
||||||
<script src="src/platform/atarivec.js"></script>
|
<script src="src/platform/atarivec.js"></script>
|
||||||
<script src="src/platform/exidy.js"></script>
|
<script src="src/platform/spaceinv.js"></script>
|
||||||
<script src="src/ui.js"></script>
|
<script src="src/ui.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
2661
src/cpu/z80.coffee
Normal file
2661
src/cpu/z80.coffee
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
195
src/emu.js
195
src/emu.js
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
// Emulator classes
|
// Emulator classes
|
||||||
|
|
||||||
|
function noise() {
|
||||||
|
return (Math.random() * 256) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
function _metakeyflags(e) {
|
function _metakeyflags(e) {
|
||||||
return (e.shiftKey?2:0) | (e.ctrlKey?4:0) | (e.altKey?8:0) | (e.metaKey?16:0);
|
return (e.shiftKey?2:0) | (e.ctrlKey?4:0) | (e.altKey?8:0) | (e.metaKey?16:0);
|
||||||
}
|
}
|
||||||
|
@ -11,6 +15,8 @@ function __createCanvas(mainElement, width, height) {
|
||||||
var fsElement = document.createElement('div');
|
var fsElement = document.createElement('div');
|
||||||
fsElement.style.position = "relative";
|
fsElement.style.position = "relative";
|
||||||
fsElement.style.padding = "20px";
|
fsElement.style.padding = "20px";
|
||||||
|
if (height > width)
|
||||||
|
fsElement.style.margin = "20%"; // TODO
|
||||||
fsElement.style.overflow = "hidden";
|
fsElement.style.overflow = "hidden";
|
||||||
fsElement.style.background = "black";
|
fsElement.style.background = "black";
|
||||||
|
|
||||||
|
@ -31,8 +37,11 @@ var RasterVideo = function(mainElement, width, height, options) {
|
||||||
var canvas, ctx;
|
var canvas, ctx;
|
||||||
var imageData, buf8, datau32;
|
var imageData, buf8, datau32;
|
||||||
|
|
||||||
this.start = function() {
|
this.create = function() {
|
||||||
canvas = __createCanvas(mainElement, width, height);
|
canvas = __createCanvas(mainElement, width, height);
|
||||||
|
if (options.rotate) {
|
||||||
|
canvas.style.transform = "rotate("+options.rotate+"deg)";
|
||||||
|
}
|
||||||
ctx = canvas.getContext('2d');
|
ctx = canvas.getContext('2d');
|
||||||
imageData = ctx.createImageData(width, height);
|
imageData = ctx.createImageData(width, height);
|
||||||
var buf = new ArrayBuffer(imageData.data.length);
|
var buf = new ArrayBuffer(imageData.data.length);
|
||||||
|
@ -303,6 +312,23 @@ var SampleAudio = function(clockfreq) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function cpuStateToLongString_6502(c) {
|
||||||
|
function decodeFlags(c, flags) {
|
||||||
|
var s = "";
|
||||||
|
s += c.N ? " N" : " -";
|
||||||
|
s += c.V ? " V" : " -";
|
||||||
|
s += c.D ? " D" : " -";
|
||||||
|
s += c.Z ? " Z" : " -";
|
||||||
|
s += c.C ? " C" : " -";
|
||||||
|
// s += c.I ? " I" : " -";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
return "PC " + hex(c.PC,4) + " " + decodeFlags(c) + " " + getTIAPosString() + "\n"
|
||||||
|
+ " A " + hex(c.A) + " " + (c.R ? "" : "BUSY") + "\n"
|
||||||
|
+ " X " + hex(c.X) + "\n"
|
||||||
|
+ " Y " + hex(c.Y) + " " + "SP " + hex(c.SP) + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
var Base6502Platform = function() {
|
var Base6502Platform = function() {
|
||||||
|
|
||||||
this.getOpcodeMetadata = function(opcode, offset) {
|
this.getOpcodeMetadata = function(opcode, offset) {
|
||||||
|
@ -345,6 +371,7 @@ var Base6502Platform = function() {
|
||||||
}
|
}
|
||||||
this.clearDebug = function() {
|
this.clearDebug = function() {
|
||||||
debugSavedState = null;
|
debugSavedState = null;
|
||||||
|
debugBreakState = null;
|
||||||
debugTargetClock = 0;
|
debugTargetClock = 0;
|
||||||
debugClock = 0;
|
debugClock = 0;
|
||||||
onBreakpointHit = null;
|
onBreakpointHit = null;
|
||||||
|
@ -416,4 +443,170 @@ var Base6502Platform = function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
this.runUntilReturn = function() {
|
||||||
|
var depth = 1;
|
||||||
|
self.runEval(function(c) {
|
||||||
|
if (depth <= 0 && c.T == 0)
|
||||||
|
return true;
|
||||||
|
if (c.o == 0x20)
|
||||||
|
depth++;
|
||||||
|
else if (c.o == 0x60 || c.o == 0x40)
|
||||||
|
--depth;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.disassemble = function(mem, start, end, pcvisits) {
|
||||||
|
return new Disassembler6502().disassemble(mem, start, end, pcvisits);
|
||||||
|
}
|
||||||
|
this.cpuStateToLongString = function(c) {
|
||||||
|
return cpuStateToLongString_6502(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dumpRAM(ram, ramofs, ramlen) {
|
||||||
|
var s = "";
|
||||||
|
// TODO: show scrollable RAM for other platforms
|
||||||
|
for (var ofs=0; ofs<ramlen; ofs+=0x10) {
|
||||||
|
s += '$' + hex(ofs+ramofs) + ':';
|
||||||
|
for (var i=0; i<0x10; i++) {
|
||||||
|
if (ofs+i < ram.length) {
|
||||||
|
if (i == 8) s += " ";
|
||||||
|
s += " " + hex(ram[ofs+i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s += "\n";
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cpuStateToLongString_Z80(c) {
|
||||||
|
function decodeFlags(flags) {
|
||||||
|
var flagspec = "SZ-H-VNC";
|
||||||
|
var s = "";
|
||||||
|
for (var i=0; i<8; i++)
|
||||||
|
s += (flags & (128>>i)) ? flagspec.slice(i,i+1) : "-";
|
||||||
|
return s; // TODO
|
||||||
|
}
|
||||||
|
return "PC " + hex(c.PC,4) + " " + decodeFlags(c.AF) + " " + getTIAPosString() + "\n"
|
||||||
|
+ "SP " + hex(c.SP,4) + " IR " + hex(c.IR,4) + "\n"
|
||||||
|
+ "IX " + hex(c.IX,4) + " IY " + hex(c.IY,4) + "\n"
|
||||||
|
+ "AF " + hex(c.AF,4) + " BC " + hex(c.BC,4) + "\n"
|
||||||
|
+ "DE " + hex(c.DE,4) + " HL " + hex(c.HL,4) + "\n"
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
var BaseZ80Platform = function() {
|
||||||
|
|
||||||
|
var onBreakpointHit;
|
||||||
|
var debugCondition;
|
||||||
|
var debugSavedState = null;
|
||||||
|
var debugBreakState = null;
|
||||||
|
var debugTargetClock = 0;
|
||||||
|
|
||||||
|
this.setDebugCondition = function(debugCond) {
|
||||||
|
if (debugSavedState) {
|
||||||
|
this.loadState(debugSavedState);
|
||||||
|
} else {
|
||||||
|
debugSavedState = this.saveState();
|
||||||
|
}
|
||||||
|
debugCondition = debugCond;
|
||||||
|
this.resume();
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
this.restartDebugState = function() {
|
||||||
|
if (debugCondition && !debugBreakState) {
|
||||||
|
debugTargetClock -= cpu.getTstates();
|
||||||
|
cpu.setTstates(0);
|
||||||
|
debugSavedState = this.saveState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
this.getDebugCallback = function() {
|
||||||
|
return debugCondition;
|
||||||
|
}
|
||||||
|
this.setupDebug = function(callback) {
|
||||||
|
onBreakpointHit = callback;
|
||||||
|
}
|
||||||
|
this.clearDebug = function() {
|
||||||
|
debugSavedState = null;
|
||||||
|
debugBreakState = null;
|
||||||
|
debugTargetClock = 0;
|
||||||
|
onBreakpointHit = null;
|
||||||
|
debugCondition = null;
|
||||||
|
}
|
||||||
|
this.breakpointHit = function() {
|
||||||
|
debugBreakState = this.saveState();
|
||||||
|
//debugBreakState.c.PC = (debugBreakState.c.PC-1) & 0xffff;
|
||||||
|
console.log("Breakpoint at clk", debugBreakState.c.tstates, "PC", debugBreakState.c.PC.toString(16));
|
||||||
|
this.pause();
|
||||||
|
if (onBreakpointHit) {
|
||||||
|
onBreakpointHit(debugBreakState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO: lower bound of clock value
|
||||||
|
this.step = function() {
|
||||||
|
var self = this;
|
||||||
|
this.setDebugCondition(function() {
|
||||||
|
var cpuState = self.getCPUState();
|
||||||
|
if (cpuState.tstates > debugTargetClock) {
|
||||||
|
debugTargetClock = cpuState.tstates;
|
||||||
|
self.breakpointHit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.stepBack = function() {
|
||||||
|
var self = this;
|
||||||
|
var prevState;
|
||||||
|
var prevClock;
|
||||||
|
this.setDebugCondition(function() {
|
||||||
|
var cpuState = self.getCPUState();
|
||||||
|
var debugClock = cpuState.tstates;
|
||||||
|
if (debugClock >= debugTargetClock && prevState) {
|
||||||
|
self.loadState(prevState);
|
||||||
|
debugTargetClock = prevClock;
|
||||||
|
self.breakpointHit();
|
||||||
|
return true;
|
||||||
|
} else if (debugClock > debugTargetClock-20 && debugClock < debugTargetClock) {
|
||||||
|
prevState = self.saveState();
|
||||||
|
prevClock = debugClock;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.runEval = function(evalfunc) {
|
||||||
|
var self = this;
|
||||||
|
this.setDebugCondition(function() {
|
||||||
|
var cpuState = self.getCPUState();
|
||||||
|
if (cpuState.tstates > debugTargetClock) {
|
||||||
|
if (evalfunc(cpuState)) {
|
||||||
|
debugTargetClock = cpuState.tstates;
|
||||||
|
self.breakpointHit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.runUntilReturn = function() {
|
||||||
|
var self = this;
|
||||||
|
var depth = 1;
|
||||||
|
self.runEval(function(c) {
|
||||||
|
if (depth <= 0)
|
||||||
|
return true;
|
||||||
|
var op = self.readAddress(c.PC);
|
||||||
|
if (op == 0xcd) // CALL
|
||||||
|
depth++;
|
||||||
|
else if (op == 0xc0 || op == 0xc8 || op == 0xc9 || op == 0xd0) // RET (TODO?)
|
||||||
|
--depth;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.disassemble = function(mem, start, end, pcvisits) {
|
||||||
|
return new Disassembler6502().disassemble(mem, start, end, pcvisits);
|
||||||
|
}
|
||||||
|
this.cpuStateToLongString = function(c) {
|
||||||
|
return cpuStateToLongString_Z80(c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,10 +35,6 @@ var Apple2Platform = function(mainElement) {
|
||||||
return APPLE2_PRESETS;
|
return APPLE2_PRESETS;
|
||||||
}
|
}
|
||||||
|
|
||||||
function noise() {
|
|
||||||
return (Math.random() * 256) & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.start = function() {
|
this.start = function() {
|
||||||
cpu = new jt.M6502();
|
cpu = new jt.M6502();
|
||||||
ram = new RAM(0x13000); // 64K + 16K LC RAM - 4K hardware
|
ram = new RAM(0x13000); // 64K + 16K LC RAM - 4K hardware
|
||||||
|
@ -138,8 +134,7 @@ var Apple2Platform = function(mainElement) {
|
||||||
// create video/audio
|
// create video/audio
|
||||||
video = new RasterVideo(mainElement,280,192);
|
video = new RasterVideo(mainElement,280,192);
|
||||||
audio = new SampleAudio(cpuFrequency);
|
audio = new SampleAudio(cpuFrequency);
|
||||||
video.start();
|
video.create();
|
||||||
audio.start();
|
|
||||||
video.setKeyboardEvents(function(key,code,flags) {
|
video.setKeyboardEvents(function(key,code,flags) {
|
||||||
// since we're an Apple II+, we don't do lowercase
|
// since we're an Apple II+, we don't do lowercase
|
||||||
if (flags & 1) {
|
if (flags & 1) {
|
||||||
|
@ -158,8 +153,6 @@ var Apple2Platform = function(mainElement) {
|
||||||
var colors = [0xffff0000, 0xff00ff00];
|
var colors = [0xffff0000, 0xff00ff00];
|
||||||
timer = new AnimationTimer(60, function() {
|
timer = new AnimationTimer(60, function() {
|
||||||
// 262.5 scanlines per frame
|
// 262.5 scanlines per frame
|
||||||
var iaddr = 0x2000;
|
|
||||||
var iofs = 0;
|
|
||||||
var clock = 0;
|
var clock = 0;
|
||||||
var debugCond = self.getDebugCallback();
|
var debugCond = self.getDebugCallback();
|
||||||
for (var sl=0; sl<262; sl++) {
|
for (var sl=0; sl<262; sl++) {
|
||||||
|
@ -254,10 +247,6 @@ var Apple2Platform = function(mainElement) {
|
||||||
this.reset();
|
this.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getRasterPosition = function() {
|
|
||||||
return {x:0, y:0};
|
|
||||||
}
|
|
||||||
|
|
||||||
this.isRunning = function() {
|
this.isRunning = function() {
|
||||||
return timer.isRunning();
|
return timer.isRunning();
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ var AtariVectorPlatform = function(mainElement) {
|
||||||
video = new VectorVideo(mainElement,1024,1024);
|
video = new VectorVideo(mainElement,1024,1024);
|
||||||
dvg = new DVGStateMachine(bus, video);
|
dvg = new DVGStateMachine(bus, video);
|
||||||
audio = new SampleAudio(cpuFrequency);
|
audio = new SampleAudio(cpuFrequency);
|
||||||
video.start();
|
video.create();
|
||||||
timer = new AnimationTimer(60, function() {
|
timer = new AnimationTimer(60, function() {
|
||||||
video.clear();
|
video.clear();
|
||||||
// 262.5 scanlines per frame
|
// 262.5 scanlines per frame
|
||||||
|
|
|
@ -47,9 +47,8 @@ var ExidyPlatform = function(mainElement) {
|
||||||
cpu.connectBus(bus);
|
cpu.connectBus(bus);
|
||||||
// create video/audio
|
// create video/audio
|
||||||
video = new RasterVideo(mainElement,256,256);
|
video = new RasterVideo(mainElement,256,256);
|
||||||
video.start();
|
video.create();
|
||||||
audio = new SampleAudio(cpuFrequency); // TODO
|
audio = new SampleAudio(cpuFrequency); // TODO
|
||||||
audio.start();
|
|
||||||
var idata = video.getFrameData();
|
var idata = video.getFrameData();
|
||||||
timer = new AnimationTimer(60, function() {
|
timer = new AnimationTimer(60, function() {
|
||||||
var clock = 0;
|
var clock = 0;
|
||||||
|
|
224
src/platform/spaceinv.js
Normal file
224
src/platform/spaceinv.js
Normal file
|
@ -0,0 +1,224 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// http://www.computerarcheology.com/Arcade/SpaceInvaders/Hardware.html
|
||||||
|
|
||||||
|
var SPACEINV_PRESETS = [
|
||||||
|
];
|
||||||
|
|
||||||
|
// TODO: global???
|
||||||
|
window.buildZ80({
|
||||||
|
applyContention: false
|
||||||
|
});
|
||||||
|
|
||||||
|
var SpaceInvadersPlatform = function(mainElement) {
|
||||||
|
var self = this;
|
||||||
|
this.__proto__ = new BaseZ80Platform();
|
||||||
|
|
||||||
|
var cpu, ram, membus, iobus, rom;
|
||||||
|
var video, audio, timer, pixels;
|
||||||
|
var inputs = [0xe,0x8,0x0];
|
||||||
|
var bitshift_offset = 0;
|
||||||
|
var bitshift_register = 0;
|
||||||
|
var watchdog_counter;
|
||||||
|
var cpuFrequency = 1996800;
|
||||||
|
var cpuCyclesPerLine = Math.round(cpuFrequency/(60*224)); // TODO
|
||||||
|
var INITIAL_WATCHDOG = 256;
|
||||||
|
var PIXEL_ON = 0xffeeeeee;
|
||||||
|
var PIXEL_OFF = 0xff000000;
|
||||||
|
|
||||||
|
var KEYCODE_MAP = {
|
||||||
|
32:{i:1,b:4}, // space bar (P1)
|
||||||
|
37:{i:1,b:5}, // left arrow (P1)
|
||||||
|
39:{i:1,b:6}, // right arrow (P1)
|
||||||
|
0x53:{i:2,b:4}, // S (P2)
|
||||||
|
0x41:{i:2,b:5}, // A (P2)
|
||||||
|
0x44:{i:2,b:6}, // D (P2)
|
||||||
|
53:{i:1,b:0}, // 5
|
||||||
|
49:{i:1,b:2}, // 1
|
||||||
|
50:{i:1,b:1}, // 2
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getOpcodeMetadata = function() {
|
||||||
|
return {
|
||||||
|
opcode:0,
|
||||||
|
mnenomic:'?',
|
||||||
|
minCycles:0,
|
||||||
|
maxCycles:0,
|
||||||
|
insnlength:1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getPresets = function() {
|
||||||
|
return SPACEINV_PRESETS;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.start = function() {
|
||||||
|
ram = new RAM(0x2000);
|
||||||
|
membus = {
|
||||||
|
read: function(address) {
|
||||||
|
if (address < 0x2000) {
|
||||||
|
return rom ? rom[address] : 0;
|
||||||
|
} else {
|
||||||
|
address &= 0x1fff;
|
||||||
|
return ram.mem[address];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
write: function(address, value) {
|
||||||
|
//console.log("write", hex(address,4), hex(value,2));
|
||||||
|
if (address >= 0x2000) {
|
||||||
|
address &= 0x1fff;
|
||||||
|
value &= 0xff;
|
||||||
|
ram.mem[address] = value;
|
||||||
|
if (address >= 0x400) {
|
||||||
|
// TODO: dirty flags
|
||||||
|
var ofs = (address - 0x400)*8;
|
||||||
|
for (var i=0; i<8; i++)
|
||||||
|
pixels[ofs+i] = (value & (1<<i)) ? PIXEL_ON : PIXEL_OFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isContended: function() { return false; },
|
||||||
|
};
|
||||||
|
iobus = {
|
||||||
|
read: function(addr) {
|
||||||
|
addr &= 0x3;
|
||||||
|
//console.log('IO read', hex(addr,4));
|
||||||
|
switch (addr) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
return inputs[addr];
|
||||||
|
case 3:
|
||||||
|
return (bitshift_register << bitshift_offset) & 0xff;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
write: function(addr, val) {
|
||||||
|
addr &= 0x7;
|
||||||
|
val &= 0xff;
|
||||||
|
//console.log('IO write', hex(addr,4), hex(val,2));
|
||||||
|
switch (addr) {
|
||||||
|
case 2:
|
||||||
|
bitshift_offset = val & 0x7;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
case 5:
|
||||||
|
// TODO: sound
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
bitshift_register = (bitshift_register >> 8) | (val << 8);
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
watchdog_counter = INITIAL_WATCHDOG;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
cpu = window.Z80({
|
||||||
|
display: {},
|
||||||
|
memory: membus,
|
||||||
|
ioBus: iobus
|
||||||
|
});
|
||||||
|
video = new RasterVideo(mainElement,256,224,{rotate:-90});
|
||||||
|
audio = new SampleAudio(cpuFrequency);
|
||||||
|
video.create();
|
||||||
|
var idata = video.getFrameData();
|
||||||
|
video.setKeyboardEvents(function(key,code,flags) {
|
||||||
|
var o = KEYCODE_MAP[key];
|
||||||
|
if (o) {
|
||||||
|
if (flags & 1) {
|
||||||
|
inputs[o.i] |= (1<<o.b);
|
||||||
|
} else {
|
||||||
|
inputs[o.i] &= ~(1<<o.b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
pixels = video.getFrameData();
|
||||||
|
timer = new AnimationTimer(60, function() {
|
||||||
|
if (!self.isRunning())
|
||||||
|
return;
|
||||||
|
cpu.setTstates(0);
|
||||||
|
var debugCond = self.getDebugCallback();
|
||||||
|
for (var sl=0; sl<224; sl++) {
|
||||||
|
var targetTstates = cpu.getTstates() + cpuCyclesPerLine;
|
||||||
|
if (debugCond) {
|
||||||
|
while (cpu.getTstates() < targetTstates) {
|
||||||
|
if (debugCond && debugCond()) { debugCond = null; }
|
||||||
|
cpu.runFrame(cpu.getTstates() + 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cpu.runFrame(targetTstates);
|
||||||
|
}
|
||||||
|
if (sl == 95)
|
||||||
|
cpu.requestInterrupt(0x8); // RST $8
|
||||||
|
else if (sl == 223)
|
||||||
|
cpu.requestInterrupt(0x10); // RST $10
|
||||||
|
}
|
||||||
|
video.updateFrame();
|
||||||
|
if (watchdog_counter-- <= 0) {
|
||||||
|
console.log("WATCHDOG FIRED"); // TODO: alert on video
|
||||||
|
self.reset();
|
||||||
|
}
|
||||||
|
//self.restartDebugState();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loadROM = function(title, data) {
|
||||||
|
rom = data;
|
||||||
|
self.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loadState = function(state) {
|
||||||
|
cpu.loadState(state.c);
|
||||||
|
ram.mem.set(state.b);
|
||||||
|
bitshift_register = state.bsr;
|
||||||
|
bitshift_offset = state.bso;
|
||||||
|
watchdog_counter = state.wdc;
|
||||||
|
inputs[0] = state.in0;
|
||||||
|
inputs[1] = state.in1;
|
||||||
|
inputs[2] = state.in2;
|
||||||
|
}
|
||||||
|
this.saveState = function() {
|
||||||
|
return {
|
||||||
|
c:self.getCPUState(),
|
||||||
|
b:ram.mem.slice(0),
|
||||||
|
bsr:bitshift_register,
|
||||||
|
bso:bitshift_offset,
|
||||||
|
wdc:watchdog_counter,
|
||||||
|
in0:inputs[0],
|
||||||
|
in1:inputs[1],
|
||||||
|
in2:inputs[2],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
this.getRAMForState = function(state) {
|
||||||
|
return ram.mem;
|
||||||
|
}
|
||||||
|
this.getCPUState = function() {
|
||||||
|
return cpu.saveState();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isRunning = function() {
|
||||||
|
return timer.isRunning();
|
||||||
|
}
|
||||||
|
this.pause = function() {
|
||||||
|
timer.stop();
|
||||||
|
audio.stop();
|
||||||
|
}
|
||||||
|
this.resume = function() {
|
||||||
|
timer.start();
|
||||||
|
audio.start();
|
||||||
|
}
|
||||||
|
this.reset = function() {
|
||||||
|
cpu.reset();
|
||||||
|
cpu.setTstates(0);
|
||||||
|
watchdog_counter = INITIAL_WATCHDOG;
|
||||||
|
}
|
||||||
|
this.readAddress = function(addr) {
|
||||||
|
return membus.read(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ramStateToLongString = function(state) {
|
||||||
|
var stack = state.b.slice(state.c.SP & 0x1fff, 0x400);
|
||||||
|
return "\n" + dumpRAM(stack, state.c.SP, stack.length);
|
||||||
|
}
|
||||||
|
}
|
|
@ -88,9 +88,29 @@ var VCSPlatform = function() {
|
||||||
return (this.readAddress(0xfffc) | (this.readAddress(0xfffd) << 8)) & 0xffff;
|
return (this.readAddress(0xfffc) | (this.readAddress(0xfffd) << 8)) & 0xffff;
|
||||||
}
|
}
|
||||||
this.readAddress = function(addr) {
|
this.readAddress = function(addr) {
|
||||||
return current_output[addr - 0xf000]; // TODO: use bus to read
|
return current_output[addr & 0xfff]; // TODO: use bus to read
|
||||||
|
}
|
||||||
|
this.runUntilReturn = function() {
|
||||||
|
var depth = 1;
|
||||||
|
self.runEval(function(c) {
|
||||||
|
if (depth <= 0 && c.T == 0)
|
||||||
|
return true;
|
||||||
|
if (c.o == 0x20)
|
||||||
|
depth++;
|
||||||
|
else if (c.o == 0x60 || c.o == 0x40)
|
||||||
|
--depth;
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.cpuStateToLongString = function(c) {
|
||||||
|
return cpuStateToLongString_6502(c);
|
||||||
}
|
}
|
||||||
this.getRAMForState = function(state) {
|
this.getRAMForState = function(state) {
|
||||||
return jt.Util.byteStringToUInt8Array(atob(state.r.b));
|
return jt.Util.byteStringToUInt8Array(atob(state.r.b));
|
||||||
}
|
}
|
||||||
|
this.ramStateToLongString = function(state) {
|
||||||
|
// TODO: customize RAM dump per-platform
|
||||||
|
var ram = self.getRAMForState(state);
|
||||||
|
return "\n" + dumpRAM(ram, 0x80, 0x80);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
208
src/ui.js
208
src/ui.js
|
@ -1,5 +1,21 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
// catch errors
|
||||||
|
if (typeof window.onerror == "object") {
|
||||||
|
window.onerror = function (msgevent, url, line, col, error) {
|
||||||
|
console.log(msgevent, url, line, col);
|
||||||
|
console.log(error);
|
||||||
|
//$("#editor").hide();
|
||||||
|
if (window.location.host.endsWith('8bitworkshop.com')) {
|
||||||
|
ga('send', 'exception', {
|
||||||
|
'exDescription': msgevent + " " + url + " " + " " + line + ":" + col + ", " + error,
|
||||||
|
'exFatal': true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
alert(msgevent+"");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// 8bitworkshop IDE user interface
|
// 8bitworkshop IDE user interface
|
||||||
|
|
||||||
var PRESETS; // presets array
|
var PRESETS; // presets array
|
||||||
|
@ -260,6 +276,7 @@ function getToolForFilename(fn) {
|
||||||
if (fn.endsWith(".pla")) return "plasm";
|
if (fn.endsWith(".pla")) return "plasm";
|
||||||
if (fn.endsWith(".c")) return "cc65";
|
if (fn.endsWith(".c")) return "cc65";
|
||||||
if (fn.endsWith(".s")) return "ca65";
|
if (fn.endsWith(".s")) return "ca65";
|
||||||
|
if (fn.endsWith(".asm")) return "z80asm";
|
||||||
return "dasm";
|
return "dasm";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,8 +340,8 @@ worker.onmessage = function(e) {
|
||||||
offset2line = {};
|
offset2line = {};
|
||||||
line2offset = {};
|
line2offset = {};
|
||||||
for (var info of e.data.listing.lines) {
|
for (var info of e.data.listing.lines) {
|
||||||
if (info.offset) {
|
if (info.offset >= 0) {
|
||||||
var textel = document.createTextNode(info.offset.toString(16));
|
var textel = document.createTextNode(hex(info.offset,4));
|
||||||
editor.setGutterMarker(info.line-1, "gutter-offset", textel);
|
editor.setGutterMarker(info.line-1, "gutter-offset", textel);
|
||||||
offset2line[info.offset] = info.line;
|
offset2line[info.offset] = info.line;
|
||||||
line2offset[info.line] = info.offset;
|
line2offset[info.line] = info.offset;
|
||||||
|
@ -354,7 +371,7 @@ function findLineForOffset(PC) {
|
||||||
if (offset2line) {
|
if (offset2line) {
|
||||||
for (var i=0; i<256; i++) {
|
for (var i=0; i<256; i++) {
|
||||||
var line = offset2line[PC];
|
var line = offset2line[PC];
|
||||||
if (line) {
|
if (line >= 0) {
|
||||||
return line;
|
return line;
|
||||||
}
|
}
|
||||||
PC--;
|
PC--;
|
||||||
|
@ -367,36 +384,11 @@ function setCurrentLine(line) {
|
||||||
editor.setSelection({line:line,ch:0}, {line:line-1,ch:0}, {scroll:true});
|
editor.setSelection({line:line,ch:0}, {line:line-1,ch:0}, {scroll:true});
|
||||||
}
|
}
|
||||||
|
|
||||||
function hex(v, nd) {
|
|
||||||
try {
|
|
||||||
if (!nd) nd = 2;
|
|
||||||
var s = v.toString(16).toUpperCase();
|
|
||||||
while (s.length < nd)
|
|
||||||
s = "0" + s;
|
|
||||||
return s;
|
|
||||||
} catch (e) {
|
|
||||||
return v+"";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function decodeFlags(c, flags) {
|
|
||||||
var s = "";
|
|
||||||
s += c.N ? " N" : " -";
|
|
||||||
s += c.V ? " V" : " -";
|
|
||||||
s += c.D ? " D" : " -";
|
|
||||||
s += c.Z ? " Z" : " -";
|
|
||||||
s += c.C ? " C" : " -";
|
|
||||||
// s += c.I ? " I" : " -";
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
function cpuStateToLongString(c) {
|
|
||||||
return "PC " + hex(c.PC,4) + " " + decodeFlags(c) + " " + getTIAPosString() + "\n"
|
|
||||||
+ " A " + hex(c.A) + " " + (c.R ? "" : "BUSY") + "\n"
|
|
||||||
+ " X " + hex(c.X) + "\n"
|
|
||||||
+ " Y " + hex(c.Y) + " " + "SP " + hex(c.SP) + "\n";
|
|
||||||
}
|
|
||||||
function getTIAPosString() {
|
function getTIAPosString() {
|
||||||
var pos = platform.getRasterPosition();
|
if (platform.getRasterPosition) {
|
||||||
return "V" + pos.y + " H" + pos.x;
|
var pos = platform.getRasterPosition();
|
||||||
|
return "V" + pos.y + " H" + pos.x;
|
||||||
|
} else return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastDebugInfo;
|
var lastDebugInfo;
|
||||||
|
@ -431,19 +423,9 @@ function highlightDifferences(s1, s2) {
|
||||||
function showMemory(state) {
|
function showMemory(state) {
|
||||||
var s = "";
|
var s = "";
|
||||||
if (state) {
|
if (state) {
|
||||||
s = cpuStateToLongString(state.c);
|
s = platform.cpuStateToLongString(state.c);
|
||||||
s += "\n";
|
if (platform.ramStateToLongString) {
|
||||||
var ram = platform.getRAMForState(state);
|
s += platform.ramStateToLongString(state);
|
||||||
var ramlen = ram.length <= 128 ? 128 : 256; // TODO
|
|
||||||
var ramofs = ram.length == 128 ? 0x80 : 0;
|
|
||||||
// TODO: show scrollable RAM for other platforms
|
|
||||||
for (var ofs=0; ofs<ramlen; ofs+=0x10) {
|
|
||||||
s += '$' + hex(ofs+ramofs) + ':';
|
|
||||||
for (var i=0; i<0x10; i++) {
|
|
||||||
if (i == 8) s += " ";
|
|
||||||
s += " " + hex(ram[ofs+i]);
|
|
||||||
}
|
|
||||||
s += "\n";
|
|
||||||
}
|
}
|
||||||
var hs = lastDebugInfo ? highlightDifferences(lastDebugInfo, s) : s;
|
var hs = lastDebugInfo ? highlightDifferences(lastDebugInfo, s) : s;
|
||||||
$("#mem_info").show().html(hs);
|
$("#mem_info").show().html(hs);
|
||||||
|
@ -460,9 +442,12 @@ function setupBreakpoint() {
|
||||||
lastDebugState = state;
|
lastDebugState = state;
|
||||||
var PC = state.c.PC;
|
var PC = state.c.PC;
|
||||||
var line = findLineForOffset(PC);
|
var line = findLineForOffset(PC);
|
||||||
if (line) {
|
if (line >= 0) {
|
||||||
console.log("BREAKPOINT", hex(PC), line);
|
console.log("BREAKPOINT", hex(PC), line);
|
||||||
setCurrentLine(line);
|
setCurrentLine(line);
|
||||||
|
} else {
|
||||||
|
console.log("BREAKPOINT", hex(PC));
|
||||||
|
// TODO: switch to disasm
|
||||||
}
|
}
|
||||||
pcvisits[PC] = pcvisits[PC] ? pcvisits[PC]+1 : 1;
|
pcvisits[PC] = pcvisits[PC] ? pcvisits[PC]+1 : 1;
|
||||||
showMemory(state);
|
showMemory(state);
|
||||||
|
@ -498,7 +483,7 @@ function runToCursor() {
|
||||||
setupBreakpoint();
|
setupBreakpoint();
|
||||||
var line = getCurrentLine();
|
var line = getCurrentLine();
|
||||||
var pc = line2offset[line];
|
var pc = line2offset[line];
|
||||||
if (pc) {
|
if (pc >= 0) {
|
||||||
console.log("Run to", line, pc.toString(16));
|
console.log("Run to", line, pc.toString(16));
|
||||||
platform.runEval(function(c) {
|
platform.runEval(function(c) {
|
||||||
return c.PC == pc;
|
return c.PC == pc;
|
||||||
|
@ -508,16 +493,7 @@ function runToCursor() {
|
||||||
|
|
||||||
function runUntilReturn() {
|
function runUntilReturn() {
|
||||||
setupBreakpoint();
|
setupBreakpoint();
|
||||||
var depth = 1;
|
platform.runUntilReturn();
|
||||||
platform.runEval(function(c) {
|
|
||||||
if (depth <= 0 && c.T == 0)
|
|
||||||
return true;
|
|
||||||
if (c.o == 0x20)
|
|
||||||
depth++;
|
|
||||||
else if (c.o == 0x60 || c.o == 0x40)
|
|
||||||
--depth;
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function runStepBackwards() {
|
function runStepBackwards() {
|
||||||
|
@ -735,7 +711,8 @@ function updateDisassembly() {
|
||||||
function disassemble(start, end) {
|
function disassemble(start, end) {
|
||||||
if (start < 0) start = 0;
|
if (start < 0) start = 0;
|
||||||
if (end > mem.length) end = mem.length;
|
if (end > mem.length) end = mem.length;
|
||||||
var disasm = new Disassembler6502().disassemble(mem, start, end, pcvisits);
|
// TODO: use platform.readMemory()
|
||||||
|
var disasm = platform.disassemble(mem, start, end, pcvisits);
|
||||||
var s = "";
|
var s = "";
|
||||||
for (a in disasm) {
|
for (a in disasm) {
|
||||||
var srclinenum = offset2line[a];
|
var srclinenum = offset2line[a];
|
||||||
|
@ -777,11 +754,11 @@ function toggleDisassembly() {
|
||||||
function resetAndDebug() {
|
function resetAndDebug() {
|
||||||
clearBreakpoint();
|
clearBreakpoint();
|
||||||
platform.reset();
|
platform.reset();
|
||||||
runToCursor();
|
platform.breakpointHit();
|
||||||
}
|
}
|
||||||
|
|
||||||
function _breakExpression() {
|
function _breakExpression() {
|
||||||
var exprs = window.prompt("Enter break expression", "c.PC == 0x6000");
|
var exprs = window.prompt("Enter break expression", "c.PC == 0x6000"); // TODO
|
||||||
if (exprs) {
|
if (exprs) {
|
||||||
var fn = new Function('c', 'return (' + exprs + ');');
|
var fn = new Function('c', 'return (' + exprs + ');');
|
||||||
setupBreakpoint();
|
setupBreakpoint();
|
||||||
|
@ -798,7 +775,10 @@ function setupDebugControls(){
|
||||||
$("#dbg_stepout").click(runUntilReturn);
|
$("#dbg_stepout").click(runUntilReturn);
|
||||||
$("#dbg_stepback").click(runStepBackwards);
|
$("#dbg_stepback").click(runStepBackwards);
|
||||||
$("#dbg_timing").click(traceTiming);
|
$("#dbg_timing").click(traceTiming);
|
||||||
$("#dbg_disasm").click(toggleDisassembly);
|
if (platform.disassemble)
|
||||||
|
$("#dbg_disasm").click(toggleDisassembly);
|
||||||
|
else
|
||||||
|
$("#dbg_disasm").hide();
|
||||||
$("#disassembly").hide();
|
$("#disassembly").hide();
|
||||||
$(".dropdown-menu").collapse({toggle: false});
|
$(".dropdown-menu").collapse({toggle: false});
|
||||||
$("#item_new_file").click(_createNewFile);
|
$("#item_new_file").click(_createNewFile);
|
||||||
|
@ -865,61 +845,59 @@ var qs = (function (a) {
|
||||||
})(window.location.search.substr(1).split('&'));
|
})(window.location.search.substr(1).split('&'));
|
||||||
|
|
||||||
// start
|
// start
|
||||||
setupDebugControls();
|
|
||||||
showWelcomeMessage();
|
showWelcomeMessage();
|
||||||
// parse query string
|
// parse query string
|
||||||
try {
|
// is this a share URL?
|
||||||
// is this a share URL?
|
if (qs['sharekey']) {
|
||||||
if (qs['sharekey']) {
|
var sharekey = qs['sharekey'];
|
||||||
var sharekey = qs['sharekey'];
|
console.log("Loading shared file ", sharekey);
|
||||||
console.log("Loading shared file ", sharekey);
|
$.getJSON( ".storage/" + sharekey, function( result ) {
|
||||||
$.getJSON( ".storage/" + sharekey, function( result ) {
|
console.log(result);
|
||||||
console.log(result);
|
var newid = 'shared/' + result['filename'];
|
||||||
var newid = 'shared/' + result['filename'];
|
updatePreset(newid, result['text']);
|
||||||
updatePreset(newid, result['text']);
|
qs['file'] = newid;
|
||||||
qs['file'] = newid;
|
delete qs['sharekey'];
|
||||||
delete qs['sharekey'];
|
window.location = "?" + $.param(qs);
|
||||||
window.location = "?" + $.param(qs);
|
}, 'text');
|
||||||
}, 'text');
|
} else {
|
||||||
} else {
|
// add default platform?
|
||||||
// add default platform?
|
platform_id = qs['platform'] || localStorage.getItem("__lastplatform");
|
||||||
platform_id = qs['platform'] || localStorage.getItem("__lastplatform");
|
if (!platform_id) {
|
||||||
if (!platform_id) {
|
platform_id = qs['platform'] = "vcs";
|
||||||
platform_id = qs['platform'] = "vcs";
|
}
|
||||||
}
|
// load and start platform object
|
||||||
// load and start platform object
|
// TODO: self-register platforms
|
||||||
// TODO: self-register platforms
|
if (platform_id == 'vcs') {
|
||||||
if (platform_id == 'vcs') {
|
platform = new VCSPlatform();
|
||||||
platform = new VCSPlatform();
|
$("#booklink_vcs").show();
|
||||||
$("#booklink_vcs").show();
|
} else if (platform_id == 'apple2') {
|
||||||
} else if (platform_id == 'apple2') {
|
platform = new Apple2Platform($("#emulator")[0]);
|
||||||
platform = new Apple2Platform($("#emulator")[0]);
|
} else if (platform_id == 'atarivec') {
|
||||||
} else if (platform_id == 'atarivec') {
|
platform = new AtariVectorPlatform($("#emulator")[0]);
|
||||||
platform = new AtariVectorPlatform($("#emulator")[0]);
|
} else if (platform_id == 'exidy') {
|
||||||
} else if (platform_id == 'exidy') {
|
platform = new ExidyPlatform($("#emulator")[0]);
|
||||||
platform = new ExidyPlatform($("#emulator")[0]);
|
} else if (platform_id == 'spaceinv') {
|
||||||
} else {
|
platform = new SpaceInvadersPlatform($("#emulator")[0]);
|
||||||
alert("Platform " + platform_id + " not recognized");
|
} else {
|
||||||
}
|
alert("Platform " + platform_id + " not recognized");
|
||||||
store = new FileStore(localStorage, platform_id + '/');
|
}
|
||||||
PRESETS = platform.getPresets();
|
store = new FileStore(localStorage, platform_id + '/');
|
||||||
platform.start();
|
PRESETS = platform.getPresets();
|
||||||
// reset file?
|
setupDebugControls();
|
||||||
if (qs['file'] && qs['reset']) {
|
platform.start();
|
||||||
store.deleteFile(qs['file']);
|
// reset file?
|
||||||
qs['reset'] = '';
|
if (qs['file'] && qs['reset']) {
|
||||||
window.location = "?" + $.param(qs);
|
store.deleteFile(qs['file']);
|
||||||
} else if (qs['file']) {
|
qs['reset'] = '';
|
||||||
// load file
|
window.location = "?" + $.param(qs);
|
||||||
loadPreset(qs['file']);
|
} else if (qs['file']) {
|
||||||
updateSelector();
|
// load file
|
||||||
} else {
|
loadPreset(qs['file']);
|
||||||
// try to load last file
|
updateSelector();
|
||||||
var lastid = localStorage.getItem("__lastid_"+platform_id) || localStorage.getItem("__lastid");
|
} else {
|
||||||
localStorage.removeItem("__lastid");
|
// try to load last file
|
||||||
gotoPresetNamed(lastid || PRESETS[0].id);
|
var lastid = localStorage.getItem("__lastid_"+platform_id) || localStorage.getItem("__lastid");
|
||||||
}
|
localStorage.removeItem("__lastid");
|
||||||
|
gotoPresetNamed(lastid || PRESETS[0].id);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
alert(e+""); // TODO?
|
|
||||||
}
|
}
|
||||||
|
|
12
src/util.js
12
src/util.js
|
@ -1,4 +1,16 @@
|
||||||
|
|
||||||
|
function hex(v, nd) {
|
||||||
|
try {
|
||||||
|
if (!nd) nd = 2;
|
||||||
|
var s = v.toString(16).toUpperCase();
|
||||||
|
while (s.length < nd)
|
||||||
|
s = "0" + s;
|
||||||
|
return s;
|
||||||
|
} catch (e) {
|
||||||
|
return v+"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function lzgmini() {
|
function lzgmini() {
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// set up require.js for worker
|
var loaded = {}
|
||||||
importScripts("dasm.js");
|
function load(modulename) {
|
||||||
importScripts("acme.js");
|
if (!loaded[modulename]) {
|
||||||
importScripts("plasm.js");
|
importScripts(modulename+".js");
|
||||||
importScripts("cc65.js");
|
loaded[modulename] = 1;
|
||||||
importScripts("ca65.js");
|
}
|
||||||
importScripts("ld65.js");
|
}
|
||||||
importScripts("z80asm.js");
|
|
||||||
importScripts("sdcc.js");
|
|
||||||
|
|
||||||
// shim out window and document objects for security
|
// shim out window and document objects for security
|
||||||
// https://github.com/mbostock/d3/issues/1053
|
// https://github.com/mbostock/d3/issues/1053
|
||||||
var noop = function() { return new Function(); };
|
var noop = function() { return new Function(); };
|
||||||
|
@ -49,6 +46,20 @@ function setupFS(FS) {
|
||||||
}, '/share');
|
}, '/share');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function extractErrors(strings, regex) {
|
||||||
|
var errors = [];
|
||||||
|
for (var i=0; i<strings.length; i++) {
|
||||||
|
var m = regex.exec(strings[i]);
|
||||||
|
if (m) {
|
||||||
|
errors.push({
|
||||||
|
line: m[1],
|
||||||
|
msg: m[2]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
|
|
||||||
// main worker start
|
// main worker start
|
||||||
|
|
||||||
var DASM_MAIN_FILENAME = "main.a";
|
var DASM_MAIN_FILENAME = "main.a";
|
||||||
|
@ -69,13 +80,36 @@ function match_msvc(s) {
|
||||||
console.log(s, matches);
|
console.log(s, matches);
|
||||||
if (matches) {
|
if (matches) {
|
||||||
errline = parseInt(matches[1]);
|
errline = parseInt(matches[1]);
|
||||||
errors.push({
|
msvc_errors.push({
|
||||||
line:errline,
|
line:errline,
|
||||||
msg:matches[2]
|
msg:matches[2]
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseListing(code, lineMatch) {
|
||||||
|
var lines = [];
|
||||||
|
for (var line of code.split(/\r?\n/)) {
|
||||||
|
var linem = lineMatch.exec(line);
|
||||||
|
if (linem && linem[1]) {
|
||||||
|
var linenum = parseInt(linem[1]);
|
||||||
|
var filename = linem[2];
|
||||||
|
var offset = parseInt(linem[3], 16);
|
||||||
|
var insns = linem[4];
|
||||||
|
var restline = linem[5];
|
||||||
|
if (insns) {
|
||||||
|
lines.push({
|
||||||
|
line:linenum,
|
||||||
|
offset:offset,
|
||||||
|
insns:insns,
|
||||||
|
iscode:restline[0] != '.'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
function parseDASMListing(code, unresolved) {
|
function parseDASMListing(code, unresolved) {
|
||||||
var errorMatch = /main.a [(](\d+)[)]: error: (.+)/;
|
var errorMatch = /main.a [(](\d+)[)]: error: (.+)/;
|
||||||
// 4 08ee a9 00 start lda #01workermain.js:23:5
|
// 4 08ee a9 00 start lda #01workermain.js:23:5
|
||||||
|
@ -137,6 +171,7 @@ function parseDASMListing(code, unresolved) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function assembleDASM(code) {
|
function assembleDASM(code) {
|
||||||
|
load("dasm");
|
||||||
var re_usl = /(\w+)\s+0000\s+[?][?][?][?]/;
|
var re_usl = /(\w+)\s+0000\s+[?][?][?][?]/;
|
||||||
var unresolved = {};
|
var unresolved = {};
|
||||||
function match_fn(s) {
|
function match_fn(s) {
|
||||||
|
@ -165,6 +200,7 @@ function assembleDASM(code) {
|
||||||
|
|
||||||
// TODO: not quite done
|
// TODO: not quite done
|
||||||
function assembleACME(code) {
|
function assembleACME(code) {
|
||||||
|
load("acme");
|
||||||
// stderr
|
// stderr
|
||||||
var re_err2 = /(Error|Warning) - File (.+?), line (\d+) ([^:]+) (.*)/;
|
var re_err2 = /(Error|Warning) - File (.+?), line (\d+) ([^:]+) (.*)/;
|
||||||
var errors = [];
|
var errors = [];
|
||||||
|
@ -210,6 +246,7 @@ function setupStdin(fs, code) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function compilePLASMA(code) {
|
function compilePLASMA(code) {
|
||||||
|
load("plasm");
|
||||||
// stdout
|
// stdout
|
||||||
var outstr = "";
|
var outstr = "";
|
||||||
function out_fn(s) { outstr += s; outstr += "\n"; }
|
function out_fn(s) { outstr += s; outstr += "\n"; }
|
||||||
|
@ -278,6 +315,8 @@ function parseCA65Listing(code, mapfile) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function assemblelinkCA65(code, platform, warnings) {
|
function assemblelinkCA65(code, platform, warnings) {
|
||||||
|
load("ca65");
|
||||||
|
load("ld65");
|
||||||
if (!platform)
|
if (!platform)
|
||||||
platform = 'apple2'; // TODO
|
platform = 'apple2'; // TODO
|
||||||
var objout, lstout;
|
var objout, lstout;
|
||||||
|
@ -321,6 +360,7 @@ function assemblelinkCA65(code, platform, warnings) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function compileCC65(code, platform) {
|
function compileCC65(code, platform) {
|
||||||
|
load("cc65");
|
||||||
if (!platform)
|
if (!platform)
|
||||||
platform = 'apple2'; // TODO
|
platform = 'apple2'; // TODO
|
||||||
// stderr
|
// stderr
|
||||||
|
@ -357,6 +397,7 @@ function compileCC65(code, platform) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function assembleZ80ASM(code, platform) {
|
function assembleZ80ASM(code, platform) {
|
||||||
|
load("z80asm");
|
||||||
if (!platform)
|
if (!platform)
|
||||||
platform = 'apple2'; // TODO
|
platform = 'apple2'; // TODO
|
||||||
var Module = z80asm({
|
var Module = z80asm({
|
||||||
|
@ -364,6 +405,7 @@ function assembleZ80ASM(code, platform) {
|
||||||
//logReadFiles:true,
|
//logReadFiles:true,
|
||||||
print:print_fn,
|
print:print_fn,
|
||||||
printErr:print_fn,
|
printErr:print_fn,
|
||||||
|
TOTAL_MEMORY:64*1024*1024,
|
||||||
//locateFile: function(s) { return "" + s; },
|
//locateFile: function(s) { return "" + s; },
|
||||||
});
|
});
|
||||||
var FS = Module['FS'];
|
var FS = Module['FS'];
|
||||||
|
@ -377,7 +419,9 @@ function assembleZ80ASM(code, platform) {
|
||||||
Module.callMain(["-b", "-s", "-l", "-m", "main.asm"]);
|
Module.callMain(["-b", "-s", "-l", "-m", "main.asm"]);
|
||||||
try {
|
try {
|
||||||
var aerr = FS.readFile("main.err", {'encoding':'utf8'}); // TODO
|
var aerr = FS.readFile("main.err", {'encoding':'utf8'}); // TODO
|
||||||
console.log("ERRORS", aerr); // TODO
|
if (aerr.length) {
|
||||||
|
return {listing:{errors:extractErrors(aerr.split("\n"), /.+? line (\d+): (.+)/)}};
|
||||||
|
}
|
||||||
// Warning at file 'test.asm' line 9: 'XREF' is deprecated, use 'EXTERN' instead
|
// Warning at file 'test.asm' line 9: 'XREF' is deprecated, use 'EXTERN' instead
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
}
|
}
|
||||||
|
@ -396,15 +440,19 @@ l_main00101 = 0003, L: test
|
||||||
return {
|
return {
|
||||||
exitstatus:Module.EXITSTATUS,
|
exitstatus:Module.EXITSTATUS,
|
||||||
output:aout,
|
output:aout,
|
||||||
listing:listing,
|
listing:{
|
||||||
|
errors:[],
|
||||||
|
lines:parseListing(alst, /(\d+)(\s+)([0-9A-F]+)\s+([0-9A-F][0-9A-F ]*[0-9A-F])\s+([A-Z_.].+)/i)
|
||||||
|
},
|
||||||
intermediate:{listing:alst, mapfile:amap},
|
intermediate:{listing:alst, mapfile:amap},
|
||||||
};
|
};
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw Error(e);
|
throw (e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function compileSDCC(code, platform) {
|
function compileSDCC(code, platform) {
|
||||||
|
load("sdcc");
|
||||||
var SDCC = sdcc({
|
var SDCC = sdcc({
|
||||||
noInitialRun:true,
|
noInitialRun:true,
|
||||||
noFSInit:true,
|
noFSInit:true,
|
||||||
|
|
|
@ -59,12 +59,16 @@ global.postMessage = null;
|
||||||
|
|
||||||
includeInThisContext("src/worker/workermain.js");
|
includeInThisContext("src/worker/workermain.js");
|
||||||
|
|
||||||
function compile(tool, code, callback, outlen) {
|
function compile(tool, code, callback, outlen, nlines, nerrors) {
|
||||||
global.postMessage = function(msg) {
|
global.postMessage = function(msg) {
|
||||||
if (msg.listing.errors.length) console.log(msg.listing.errors);
|
if (msg.listing.errors && msg.listing.errors.length) {
|
||||||
assert.ok(msg.listing.lines);
|
console.log(msg.listing.errors);
|
||||||
assert.equal(msg.listing.errors.length, msg.listing.errors);
|
assert.equal(nerrors, msg.listing.errors.length, "errors");
|
||||||
assert.equal(msg.output.length, outlen);
|
} else {
|
||||||
|
assert.equal(nerrors||0, 0, "errors");
|
||||||
|
assert.equal(msg.output.length, outlen, "output binary");
|
||||||
|
assert.equal(msg.listing.lines.length, nlines, "listing lines");
|
||||||
|
}
|
||||||
callback(null, msg);
|
callback(null, msg);
|
||||||
};
|
};
|
||||||
global.onmessage({
|
global.onmessage({
|
||||||
|
@ -74,18 +78,21 @@ function compile(tool, code, callback, outlen) {
|
||||||
|
|
||||||
describe('Worker', function() {
|
describe('Worker', function() {
|
||||||
it('should assemble DASM', function(done) {
|
it('should assemble DASM', function(done) {
|
||||||
compile('dasm', '\tprocessor 6502\n\torg $f000\nfoo lda #0\n', done, 2);
|
compile('dasm', '\tprocessor 6502\n\torg $f000\nfoo lda #0\n', done, 2, 1);
|
||||||
});
|
});
|
||||||
it('should compile PLASMA', function(done) {
|
it('should compile PLASMA', function(done) {
|
||||||
compile('plasm', 'word x = 0', done, 5);
|
compile('plasm', 'word x = 0', done, 5, 0);
|
||||||
});
|
});
|
||||||
it('should compile CC65', function(done) {
|
it('should compile CC65', function(done) {
|
||||||
compile('cc65', '#include <stdio.h>\nint main() {\nint x=1;\nprintf("%d",x);\nreturn x+2;\n}', done, 2947);
|
compile('cc65', '#include <stdio.h>\nint main() {\nint x=1;\nprintf("%d",x);\nreturn x+2;\n}', done, 2947, 4);
|
||||||
|
});
|
||||||
|
it('should NOT assemble Z80ASM', function(done) {
|
||||||
|
compile('z80asm', 'ddwiuweq', done, 0, 0, 1);
|
||||||
});
|
});
|
||||||
it('should assemble Z80ASM', function(done) {
|
it('should assemble Z80ASM', function(done) {
|
||||||
compile('z80asm', '\tMODULE test\n\tXREF _puts\n\tld hl,$0000\n\tret\n', done, 4);
|
compile('z80asm', '\tMODULE test\n\tXREF _puts\n\tld hl,$0000\n\tret\n', done, 4, 2, 0);
|
||||||
});
|
});
|
||||||
it('should compile SDCC', function(done) {
|
it('should compile SDCC', function(done) {
|
||||||
compile('sdcc', 'int main(int argc) {\nint x=1; int y=2;\nreturn x+y+argc;\n}', done, 16);
|
compile('sdcc', 'int main(int argc) {\nint x=1; int y=2;\nreturn x+y+argc;\n}', done, 16, 8, 0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,9 @@
|
||||||
|
|
||||||
require('../../src/cpu/z80.js');
|
require('../../src/cpu/z80.js');
|
||||||
|
|
||||||
global.buildZ80({
|
var _global = window;
|
||||||
|
|
||||||
|
_global.buildZ80({
|
||||||
applyContention: true
|
applyContention: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -92,7 +94,7 @@ function runTest(input, expected) {
|
||||||
|
|
||||||
var memory = Memory(dump);
|
var memory = Memory(dump);
|
||||||
var ioBus = IOBus();
|
var ioBus = IOBus();
|
||||||
var z80 = global.Z80({
|
var z80 = _global.Z80({
|
||||||
display: {},
|
display: {},
|
||||||
memory: memory,
|
memory: memory,
|
||||||
ioBus: ioBus
|
ioBus: ioBus
|
||||||
|
|
Loading…
Reference in New Issue
Block a user