mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-11-29 14:51:17 +00:00
started on asteroids vector driver
This commit is contained in:
parent
f0cb1602e4
commit
14f3667e7e
@ -22,3 +22,4 @@ TODO:
|
|||||||
- break on BRK/illegal opcode?
|
- break on BRK/illegal opcode?
|
||||||
- start analysis from vector address
|
- start analysis from vector address
|
||||||
|
|
||||||
|
cat ~/Downloads/appleiigo.rom | hexdump -v -e '"\n" 32/1 "%u,"' ; echo
|
||||||
|
22
index.html
22
index.html
@ -25,12 +25,12 @@ body {
|
|||||||
.gutter-info {
|
.gutter-info {
|
||||||
width: 1em;
|
width: 1em;
|
||||||
}
|
}
|
||||||
.tooltip {
|
.tooltipbox {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
border-bottom: 1px dotted black;
|
border-bottom: 1px dotted black;
|
||||||
}
|
}
|
||||||
.tooltip .tooltiptext {
|
.tooltipbox .tooltiptext {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
width: 120px;
|
width: 120px;
|
||||||
background-color: black;
|
background-color: black;
|
||||||
@ -43,7 +43,7 @@ body {
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
}
|
}
|
||||||
.tooltip:hover .tooltiptext {
|
.tooltipbox:hover .tooltiptext {
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
}
|
}
|
||||||
#notebook {
|
#notebook {
|
||||||
@ -57,7 +57,7 @@ div.editor {
|
|||||||
bottom:0;
|
bottom:0;
|
||||||
right:0;
|
right:0;
|
||||||
width:50%;
|
width:50%;
|
||||||
height:100vh;
|
height:120vh;
|
||||||
background-color:#999;
|
background-color:#999;
|
||||||
line-height:1.25;
|
line-height:1.25;
|
||||||
font-size:12pt;
|
font-size:12pt;
|
||||||
@ -67,16 +67,17 @@ div.emulator {
|
|||||||
left:50%;
|
left:50%;
|
||||||
top:0;
|
top:0;
|
||||||
width:50%;
|
width:50%;
|
||||||
height:100vh;
|
height:120vh;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
background-color: #666;
|
background-color: #666;
|
||||||
|
margin-top: 20px auto 0;
|
||||||
}
|
}
|
||||||
div.mem_info {
|
div.mem_info {
|
||||||
position: absolute;
|
position: fixed;
|
||||||
|
left: 51%;
|
||||||
bottom: 10px;
|
bottom: 10px;
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
color: #66ff66;
|
color: #66ff66;
|
||||||
left: 20px;
|
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
@ -144,7 +145,7 @@ a.dropdown-toggle {
|
|||||||
<span class="dbg_info" id="dbg_info"></span>
|
<span class="dbg_info" id="dbg_info"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top: 30px auto 0; min-height: 594px;" class="emulator" id="emulator">
|
<div class="emulator" id="emulator">
|
||||||
<div id="javatari-screen" style="margin: 0 auto; box-shadow: 2px 2px 10px rgb(60, 60, 60);"></div>
|
<div id="javatari-screen" style="margin: 0 auto; box-shadow: 2px 2px 10px rgb(60, 60, 60);"></div>
|
||||||
<div id="javatari-console-panel" style="margin: 0 auto; box-shadow: 2px 2px 10px rgb(60, 60, 60);"></div>
|
<div id="javatari-console-panel" style="margin: 0 auto; box-shadow: 2px 2px 10px rgb(60, 60, 60);"></div>
|
||||||
<div id="mem_info" class="mem_info" style="display:none">
|
<div id="mem_info" class="mem_info" style="display:none">
|
||||||
@ -186,8 +187,11 @@ 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/emu.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/atarivec.js"></script>
|
||||||
|
<script src="local/vecrom.js"></script>
|
||||||
<script src="src/ui.js"></script>
|
<script src="src/ui.js"></script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
198
src/emu.js
Normal file
198
src/emu.js
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
|
||||||
|
// Emulator classes
|
||||||
|
|
||||||
|
RasterVideo = function(mainElement, width, height, options) {
|
||||||
|
var self = this;
|
||||||
|
var canvas, ctx;
|
||||||
|
var imageData, buf8, datau32;
|
||||||
|
|
||||||
|
this.start = function() {
|
||||||
|
// TODO
|
||||||
|
fsElement = document.createElement('div');
|
||||||
|
fsElement.style.position = "relative";
|
||||||
|
fsElement.style.padding = "50px";
|
||||||
|
//fsElement.style.width = "100%";
|
||||||
|
//fsElement.style.height = "100%";
|
||||||
|
fsElement.style.overflow = "hidden";
|
||||||
|
fsElement.style.background = "black";
|
||||||
|
|
||||||
|
canvas = document.createElement('canvas');
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
|
||||||
|
fsElement.appendChild(canvas);
|
||||||
|
mainElement.appendChild(fsElement);
|
||||||
|
|
||||||
|
ctx = canvas.getContext('2d');
|
||||||
|
imageData = ctx.createImageData(width, height);
|
||||||
|
var buf = new ArrayBuffer(imageData.data.length);
|
||||||
|
buf8 = new Uint8ClampedArray(buf);
|
||||||
|
datau32 = new Uint32Array(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getFrameData = function() {
|
||||||
|
return datau32;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.updateFrame = function() {
|
||||||
|
imageData.data.set(buf8);
|
||||||
|
ctx.putImageData(imageData, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
mainElement.style.position = "relative";
|
||||||
|
mainElement.style.overflow = "hidden";
|
||||||
|
mainElement.style.outline = "none";
|
||||||
|
mainElement.tabIndex = "-1"; // Make it focusable
|
||||||
|
|
||||||
|
borderElement = document.createElement('div');
|
||||||
|
borderElement.style.position = "relative";
|
||||||
|
borderElement.style.overflow = "hidden";
|
||||||
|
borderElement.style.background = "black";
|
||||||
|
borderElement.style.border = "0 solid black";
|
||||||
|
borderElement.style.borderWidth = "" + borderTop + "px " + borderLateral + "px " + borderBottom + "px";
|
||||||
|
if (Javatari.SCREEN_CONTROL_BAR === 2) {
|
||||||
|
borderElement.style.borderImage = "url(" + IMAGE_PATH + "screenborder.png) " +
|
||||||
|
borderTop + " " + borderLateral + " " + borderBottom + " repeat stretch";
|
||||||
|
}
|
||||||
|
|
||||||
|
fsElement = document.createElement('div');
|
||||||
|
fsElement.style.position = "relative";
|
||||||
|
fsElement.style.width = "100%";
|
||||||
|
fsElement.style.height = "100%";
|
||||||
|
fsElement.style.overflow = "hidden";
|
||||||
|
fsElement.style.background = "black";
|
||||||
|
|
||||||
|
document.addEventListener("fullscreenchange", fullScreenChanged);
|
||||||
|
document.addEventListener("webkitfullscreenchange", fullScreenChanged);
|
||||||
|
document.addEventListener("mozfullscreenchange", fullScreenChanged);
|
||||||
|
document.addEventListener("msfullscreenchange", fullScreenChanged);
|
||||||
|
|
||||||
|
borderElement.appendChild(fsElement);
|
||||||
|
|
||||||
|
canvas.style.position = "absolute";
|
||||||
|
canvas.style.display = "block";
|
||||||
|
canvas.style.left = canvas.style.right = 0;
|
||||||
|
canvas.style.top = canvas.style.bottom = 0;
|
||||||
|
canvas.style.margin = "auto";
|
||||||
|
canvas.tabIndex = "-1"; // Make it focusable
|
||||||
|
canvas.style.outline = "none";
|
||||||
|
fsElement.appendChild(canvas);
|
||||||
|
|
||||||
|
setElementsSizes(jt.CanvasDisplay.DEFAULT_STARTING_WIDTH, jt.CanvasDisplay.DEFAULT_STARTING_HEIGHT);
|
||||||
|
|
||||||
|
mainElement.appendChild(borderElement);
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
VectorVideo = function(mainElement, width, height) {
|
||||||
|
var self = this;
|
||||||
|
var canvas, ctx;
|
||||||
|
var persistenceAlpha = 0.5;
|
||||||
|
var jitter = 1.0;
|
||||||
|
|
||||||
|
this.start = function() {
|
||||||
|
// TODO
|
||||||
|
var fsElement = document.createElement('div');
|
||||||
|
fsElement.style.position = "relative";
|
||||||
|
fsElement.style.padding = "20px";
|
||||||
|
fsElement.style.overflow = "hidden";
|
||||||
|
fsElement.style.background = "black";
|
||||||
|
|
||||||
|
canvas = document.createElement('canvas');
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
canvas.style.width = "100%";
|
||||||
|
canvas.style.height = "100%";
|
||||||
|
canvas.tabIndex = "-1"; // Make it focusable
|
||||||
|
|
||||||
|
fsElement.appendChild(canvas);
|
||||||
|
mainElement.appendChild(fsElement);
|
||||||
|
|
||||||
|
ctx = canvas.getContext('2d');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setKeyboardEvents = function(callback) {
|
||||||
|
canvas.onkeydown = function(e) {
|
||||||
|
callback(e.key, 1);
|
||||||
|
};
|
||||||
|
canvas.onkeyup = function(e) {
|
||||||
|
callback(e.key, 0);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.clear = function() {
|
||||||
|
ctx.globalCompositeOperation = 'source-over';
|
||||||
|
ctx.globalAlpha = persistenceAlpha;
|
||||||
|
ctx.fillStyle = '#000000';
|
||||||
|
ctx.fillRect(0, 0, width, height);
|
||||||
|
ctx.globalAlpha = 1.0;
|
||||||
|
ctx.globalCompositeOperation = 'lighter';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.drawLine = function(x1, y1, x2, y2, intensity) {
|
||||||
|
//console.log(x1, y1, x2, y2, intensity);
|
||||||
|
if (intensity > 0) {
|
||||||
|
// TODO: landscape vs portrait
|
||||||
|
ctx.beginPath();
|
||||||
|
// TODO: dots
|
||||||
|
var jx = jitter * (Math.random() - 0.5);
|
||||||
|
var jy = jitter * (Math.random() - 0.5);
|
||||||
|
x1 += jx;
|
||||||
|
x2 += jx;
|
||||||
|
y1 += jy;
|
||||||
|
y2 += jy;
|
||||||
|
ctx.moveTo(x1, height-y1);
|
||||||
|
ctx.lineTo(x2+1, height-y2);
|
||||||
|
ctx.strokeStyle = '#ffffff';
|
||||||
|
ctx.lineWidth = intensity*0.1;
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SampleAudio = function(clockfreq) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
RAM = function(size) {
|
||||||
|
var memArray = new ArrayBuffer(size);
|
||||||
|
this.mem = new Uint8Array(memArray);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
AnimationTimer = function(frequencyHz, callback) {
|
||||||
|
var intervalMsec = 1000.0 / frequencyHz;
|
||||||
|
var curTime = 0;
|
||||||
|
var running;
|
||||||
|
var useReqAnimFrame = false; // TODO: disable on OS X
|
||||||
|
|
||||||
|
function scheduleFrame() {
|
||||||
|
if (useReqAnimFrame)
|
||||||
|
window.requestAnimationFrame(nextFrame);
|
||||||
|
else
|
||||||
|
setTimeout(nextFrame, intervalMsec);
|
||||||
|
}
|
||||||
|
|
||||||
|
var nextFrame = function(timestamp) {
|
||||||
|
// TODO: calculate framerate
|
||||||
|
callback();
|
||||||
|
if (running) {
|
||||||
|
scheduleFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.isRunning = function() {
|
||||||
|
return running;
|
||||||
|
}
|
||||||
|
this.start = function() {
|
||||||
|
if (!running) {
|
||||||
|
running = true;
|
||||||
|
scheduleFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.stop = function() {
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
235
src/platform/apple2.js
Normal file
235
src/platform/apple2.js
Normal file
@ -0,0 +1,235 @@
|
|||||||
|
|
||||||
|
var PRESETS = [
|
||||||
|
]
|
||||||
|
|
||||||
|
Apple2Platform = function(mainElement) {
|
||||||
|
var self = this;
|
||||||
|
var cpuFrequency = 1.023;
|
||||||
|
var cpuCyclesPerLine = 65;
|
||||||
|
var cpu, ram, rom, bus;
|
||||||
|
var video, audio, timer;
|
||||||
|
|
||||||
|
this.start = function() {
|
||||||
|
cpu = new jt.M6502();
|
||||||
|
ram = new RAM(0xc000);
|
||||||
|
//rom = new lzgmini().decode(APPLEIIGO_LZG).slice(0,12288);
|
||||||
|
// bus
|
||||||
|
bus = {
|
||||||
|
read: function(address) {
|
||||||
|
if (address >= 0xd000 && address <= 0xffff) {
|
||||||
|
return rom[address - 0xd000];
|
||||||
|
} else if (address < 0xc000) {
|
||||||
|
return ram.mem[address];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
write: function(address, val) {
|
||||||
|
if (address < 0xc000) {
|
||||||
|
ram.mem[address] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
cpu.connectBus(bus);
|
||||||
|
// create video/audio
|
||||||
|
video = new RasterVideo(mainElement,280,192);
|
||||||
|
audio = new SampleAudio(cpuFrequency);
|
||||||
|
video.start();
|
||||||
|
var idata = video.getFrameData();
|
||||||
|
var colors = [0xffff0000, 0xff00ff00];
|
||||||
|
timer = new AnimationTimer(60, function() {
|
||||||
|
// 262.5 scanlines per frame
|
||||||
|
var iaddr = 0x2000;
|
||||||
|
var iofs = 0;
|
||||||
|
breakClock = -1;
|
||||||
|
clock = 0;
|
||||||
|
for (var sl=0; sl<262; sl++) {
|
||||||
|
for (var i=0; i<cpuCyclesPerLine; i++) {
|
||||||
|
if (debugCondition && breakClock < 0 && debugCondition()) { breakClock = clock; }
|
||||||
|
clock++;
|
||||||
|
cpu.clockPulse();
|
||||||
|
// TODO: audio
|
||||||
|
}
|
||||||
|
if (sl < 192) {
|
||||||
|
for (var xo=0; xo<35; xo++) {
|
||||||
|
var b = ram.mem[iaddr++];
|
||||||
|
idata[iofs++] = colors[(b>>0)&1];
|
||||||
|
idata[iofs++] = colors[(b>>1)&1];
|
||||||
|
idata[iofs++] = colors[(b>>2)&1];
|
||||||
|
idata[iofs++] = colors[(b>>3)&1];
|
||||||
|
idata[iofs++] = colors[(b>>4)&1];
|
||||||
|
idata[iofs++] = colors[(b>>5)&1];
|
||||||
|
idata[iofs++] = colors[(b>>6)&1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
video.updateFrame();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getOpcodeMetadata = function(opcode, offset) {
|
||||||
|
return Javatari.getOpcodeMetadata(opcode, offset); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loadROM = function(title, data) {
|
||||||
|
if(data.length != 0x3000) {
|
||||||
|
throw "ROM length must be == 0x3000";
|
||||||
|
}
|
||||||
|
rom = data;
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getRasterPosition = function() {
|
||||||
|
return {x:xpos, y:ypos};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isRunning = function() {
|
||||||
|
return timer.isRunning();
|
||||||
|
}
|
||||||
|
this.pause = function() {
|
||||||
|
timer.stop();
|
||||||
|
}
|
||||||
|
this.resume = function() {
|
||||||
|
timer.start();
|
||||||
|
}
|
||||||
|
this.reset = function() {
|
||||||
|
cpu.reset();
|
||||||
|
}
|
||||||
|
this.getOriginPC = function() {
|
||||||
|
return (this.readAddress(0xfffc) | (this.readAddress(0xfffd) << 8)) & 0xffff;
|
||||||
|
}
|
||||||
|
this.readAddress = function(addr) {
|
||||||
|
return bus.read(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
var onBreakpointHit;
|
||||||
|
var debugCondition;
|
||||||
|
var debugSavedState = null;
|
||||||
|
var debugBreakState = null;
|
||||||
|
var debugTargetClock = 0;
|
||||||
|
var debugClock = 0;
|
||||||
|
var debugFrameStartClock = 0;
|
||||||
|
var breakClock;
|
||||||
|
|
||||||
|
this.setDebugCondition = function(debugCond) {
|
||||||
|
if (debugSavedState) {
|
||||||
|
self.loadState(debugSavedState);
|
||||||
|
} else {
|
||||||
|
debugSavedState = self.saveState();
|
||||||
|
}
|
||||||
|
debugClock = 0;
|
||||||
|
debugCondition = debugCond;
|
||||||
|
self.resume();
|
||||||
|
}
|
||||||
|
this.setupDebug = function(callback) {
|
||||||
|
onBreakpointHit = callback;
|
||||||
|
}
|
||||||
|
this.clearDebug = function() {
|
||||||
|
debugSavedState = null;
|
||||||
|
debugTargetClock = 0;
|
||||||
|
debugClock = 0;
|
||||||
|
debugFrameStartClock = 0;
|
||||||
|
onBreakpointHit = null;
|
||||||
|
debugCondition = null;
|
||||||
|
}
|
||||||
|
this.breakpointHit = function() {
|
||||||
|
debugBreakState = self.saveState();
|
||||||
|
console.log("Breakpoint at clk", debugClock, "PC", debugBreakState.c.PC.toString(16));
|
||||||
|
this.pause();
|
||||||
|
if (onBreakpointHit) {
|
||||||
|
onBreakpointHit(debugBreakState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.step = function() {
|
||||||
|
var previousPC = -1;
|
||||||
|
self.setDebugCondition(function() {
|
||||||
|
if (debugClock++ >= debugTargetClock) {
|
||||||
|
var thisState = cpu.saveState();
|
||||||
|
if (previousPC < 0) {
|
||||||
|
previousPC = thisState.PC;
|
||||||
|
} else {
|
||||||
|
if (thisState.PC != previousPC && thisState.T == 0) {
|
||||||
|
//console.log(previousPC.toString(16), thisPC.toString(16));
|
||||||
|
debugTargetClock = debugClock-1;
|
||||||
|
self.breakpointHit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.runToPC = function(targetPC) {
|
||||||
|
var self = this;
|
||||||
|
self.setDebugCondition(function() {
|
||||||
|
if (debugClock++ >= debugTargetClock) {
|
||||||
|
var thisPC = cpu.saveState().PC;
|
||||||
|
if (thisPC == targetPC) {
|
||||||
|
self.breakpointHit();
|
||||||
|
debugTargetClock = debugClock;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loadState = function(state) {
|
||||||
|
cpu.loadState(state.c);
|
||||||
|
ram.mem.set(state.b);
|
||||||
|
}
|
||||||
|
this.saveState = function() {
|
||||||
|
return {
|
||||||
|
c:cpu.saveState(),
|
||||||
|
b:ram.mem.slice(0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
APPLEIIGO_LZG = [
|
||||||
|
76,90,71,0,0,48,0,0,0,5,159,47,60,46,159,1,21,25,30,52,65,80,80,76,69,73,73,71,79,32,82,79,
|
||||||
|
77,49,46,48,0,52,31,52,31,52,31,52,31,52,31,52,31,52,31,52,31,52,28,52,6,32,0,224,25,30,67,52,
|
||||||
|
30,52,28,25,31,174,52,31,52,30,52,28,52,6,25,63,107,52,31,52,30,52,28,25,63,101,52,19,25,31,139,52,
|
||||||
|
31,52,31,52,31,52,31,52,31,52,31,52,31,52,31,52,31,52,31,52,31,52,31,52,25,160,32,52,28,32,32,160,
|
||||||
|
25,14,40,1,16,16,12,5,19,15,6,20,32,14,15,20,32,1,22,1,9,12,1,2,12,5,25,16,40,198,207,210,
|
||||||
|
160,205,207,210,197,160,201,206,30,3,205,193,212,201,207,206,160,208,204,197,193,211,197,160,195,204,201,195,203,160,30,8,
|
||||||
|
160,25,8,40,212,200,197,160,193,208,30,25,201,201,199,207,160,204,207,52,129,194,197,204,207,215,30,28,52,10,25,29,
|
||||||
|
224,52,22,76,3,224,32,88,252,162,39,189,0,223,157,128,4,202,16,247,30,3,48,223,157,0,5,30,195,30,78,25,
|
||||||
|
5,3,96,30,3,6,30,195,144,30,25,7,30,3,76,64,224,52,65,25,30,131,52,31,52,31,52,31,52,31,52,31,
|
||||||
|
52,31,52,31,52,31,52,31,52,31,52,31,52,31,52,31,52,31,52,31,52,31,52,31,52,31,52,31,52,31,52,31,
|
||||||
|
52,31,52,24,201,206,176,238,201,201,144,234,201,204,240,230,208,232,234,52,11,72,74,41,3,9,4,133,41,104,41,24,
|
||||||
|
144,2,105,127,133,40,10,10,5,40,133,40,96,25,30,116,0,165,37,32,193,251,101,32,25,29,75,52,21,165,34,72,
|
||||||
|
32,36,252,165,40,133,42,165,41,133,43,164,33,136,104,105,1,197,35,176,13,30,78,177,40,145,42,136,16,249,48,225,
|
||||||
|
160,0,32,158,252,176,134,164,36,169,160,145,40,200,196,33,144,249,25,30,199,52,27,164,36,177,40,72,41,63,9,64,
|
||||||
|
145,40,104,108,56,25,19,27,32,12,253,32,165,251,52,161,201,155,240,243,25,28,141,52,6,32,142,253,165,51,32,237,
|
||||||
|
253,162,1,138,240,243,202,32,53,253,201,149,208,2,177,40,201,224,144,2,41,223,157,0,2,201,141,208,178,32,156,252,
|
||||||
|
169,141,208,91,164,61,166,60,30,39,32,64,249,160,0,169,173,76,237,253,25,28,87,52,31,52,31,52,31,52,31,52,
|
||||||
|
31,52,31,52,31,52,31,52,31,52,31,52,31,52,31,52,28,52,7,169,0,133,28,165,230,133,27,160,0,132,26,165,
|
||||||
|
28,145,26,32,126,244,200,208,246,230,27,165,27,41,31,208,238,96,133,226,134,224,132,225,72,41,192,133,38,74,74,5,
|
||||||
|
38,133,38,104,133,39,10,10,10,38,39,52,66,102,38,165,39,41,31,5,230,133,39,138,192,0,240,5,160,35,105,4,
|
||||||
|
200,233,7,176,251,132,229,170,189,185,244,133,48,152,74,165,228,133,28,176,21,28,0,35,52,4,10,201,192,16,6,165,
|
||||||
|
28,73,127,133,28,25,254,218,52,31,52,31,52,31,52,31,52,31,52,31,52,28,52,10,74,8,32,71,248,40,169,15,
|
||||||
|
144,2,105,224,133,46,177,38,69,48,37,46,81,38,145,38,96,32,0,248,196,44,176,17,200,32,14,248,144,246,105,1,
|
||||||
|
72,30,8,104,197,45,144,245,96,160,47,208,2,160,39,132,45,160,39,169,0,133,48,32,40,248,136,16,246,96,21,5,
|
||||||
|
4,126,39,21,6,4,126,38,10,10,25,130,52,96,165,48,24,105,3,41,15,133,48,10,52,1,5,48,133,48,25,103,
|
||||||
|
223,144,4,74,52,1,41,15,25,107,240,168,74,144,9,106,176,16,201,162,240,12,41,135,74,170,189,98,249,32,121,248,
|
||||||
|
208,4,160,128,169,0,170,189,166,249,133,46,41,3,133,47,152,41,143,170,152,160,3,224,138,240,11,74,144,8,74,74,
|
||||||
|
9,32,136,208,250,200,136,208,242,25,159,59,52,31,52,31,52,20,216,32,132,254,32,47,251,32,147,254,32,137,254,173,
|
||||||
|
88,192,173,90,192,173,93,192,173,95,192,173,255,207,44,16,192,216,32,58,255,32,96,251,169,0,133,0,169,198,133,1,
|
||||||
|
108,25,30,111,52,29,21,3,19,108,221,219,199,207,25,10,13,173,112,192,160,0,234,234,189,100,192,16,4,200,208,248,
|
||||||
|
136,96,169,0,133,72,173,86,192,173,84,192,173,81,192,169,0,240,11,173,80,192,173,83,192,32,54,248,169,20,133,34,
|
||||||
|
30,22,32,169,40,133,33,169,24,133,35,169,23,133,37,76,34,252,32,88,252,160,9,185,8,251,153,14,4,136,208,247,
|
||||||
|
96,173,243,3,73,165,141,244,3,96,201,141,208,24,172,0,192,16,19,192,147,208,15,44,16,192,30,68,251,192,131,240,
|
||||||
|
3,30,4,76,253,251,25,12,148,21,29,7,248,40,133,40,96,201,135,208,18,169,64,32,168,252,160,192,169,12,52,193,
|
||||||
|
173,48,192,136,208,245,96,164,36,145,40,230,36,165,36,197,33,176,102,96,201,160,176,239,168,16,236,201,141,240,90,201,
|
||||||
|
138,52,97,136,208,201,198,36,16,232,165,33,133,36,198,36,165,34,197,37,176,11,198,37,21,28,7,248,0,72,32,36,
|
||||||
|
252,32,158,252,160,0,104,105,0,197,35,144,240,176,202,165,34,133,37,160,0,132,36,240,228,169,0,133,36,230,30,62,
|
||||||
|
30,16,182,198,37,21,29,7,248,21,6,7,248,56,72,233,1,208,252,104,52,129,246,96,230,66,208,2,230,67,165,60,
|
||||||
|
197,62,165,61,229,63,230,60,30,6,61,25,125,244,52,18,21,13,7,248,230,78,208,2,230,79,44,0,192,16,245,145,
|
||||||
|
40,173,0,192,44,16,192,96,21,10,7,248,254,96,165,50,72,169,255,133,50,189,0,2,32,237,253,104,30,129,201,136,
|
||||||
|
240,29,201,152,240,10,224,248,144,3,32,58,255,232,208,19,169,220,30,21,21,10,7,248,254,21,30,7,248,52,27,0,
|
||||||
|
72,25,162,88,32,229,253,104,41,15,9,176,201,186,144,2,105,6,108,54,0,201,160,144,2,37,50,132,53,72,32,120,
|
||||||
|
251,104,164,53,25,49,47,64,25,10,5,25,11,24,177,60,145,66,32,180,252,144,247,25,190,97,52,1,160,63,208,2,
|
||||||
|
160,255,132,50,25,98,82,62,162,56,160,27,208,8,30,130,54,160,240,165,62,41,15,240,6,9,192,160,0,240,2,169,
|
||||||
|
253,148,0,149,1,96,234,234,76,21,31,30,67,52,7,169,135,76,237,253,165,72,72,165,69,166,70,164,71,25,110,22,
|
||||||
|
25,62,27,52,30,52,14,245,3,251,3,98,250,98,250
|
||||||
|
];
|
334
src/platform/atarivec.js
Normal file
334
src/platform/atarivec.js
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
|
||||||
|
var PRESETS = [
|
||||||
|
]
|
||||||
|
|
||||||
|
AtariVectorPlatform = function(mainElement) {
|
||||||
|
var self = this;
|
||||||
|
var cpuFrequency = 1500000.0;
|
||||||
|
var cpuCyclesPerNMI = 6000;
|
||||||
|
var cpuCyclesPerFrame = Math.round(cpuFrequency/60);
|
||||||
|
var cpu, cpuram, dvgram, rom, vecrom, bus, dvg;
|
||||||
|
var video, audio, timer;
|
||||||
|
var clock;
|
||||||
|
var switches = new RAM(16).mem;
|
||||||
|
var nmicount = cpuCyclesPerNMI;
|
||||||
|
|
||||||
|
this.start = function() {
|
||||||
|
cpu = new jt.M6502();
|
||||||
|
cpuram = new RAM(0x400);
|
||||||
|
dvgram = new RAM(0x2000);
|
||||||
|
vecrom = VECROM;
|
||||||
|
//switches[5] = 0xff;
|
||||||
|
//switches[7] = 0xff;
|
||||||
|
// bus
|
||||||
|
bus = {
|
||||||
|
read: function(address) {
|
||||||
|
address &= 0x7fff;
|
||||||
|
if (address >= 0x6800 && address <= 0x7fff) {
|
||||||
|
return rom[address - 0x6800];
|
||||||
|
} else if (address <= 0x3ff) {
|
||||||
|
return cpuram.mem[address];
|
||||||
|
} else if (address >= 0x5000 && address <= 0x5fff) {
|
||||||
|
return vecrom[address - 0x5000];
|
||||||
|
} else if (address >= 0x4000 && address <= 0x5fff) {
|
||||||
|
return dvgram.mem[address - 0x4000];
|
||||||
|
} else if (address >= 0x2000 && address <= 0x3fff) {
|
||||||
|
if (address == 0x2001)
|
||||||
|
return ((clock/500) & 1) ? 0xff : 0x00;
|
||||||
|
else if (address >= 0x2000 && address <= 0x2007)
|
||||||
|
return switches[address - 0x2000];
|
||||||
|
else if (address >= 0x2400 && address <= 0x2407)
|
||||||
|
return switches[address - 0x2400 + 8];
|
||||||
|
}
|
||||||
|
return 0xff;
|
||||||
|
},
|
||||||
|
write: function(address, val) {
|
||||||
|
address &= 0x7fff;
|
||||||
|
if (address < 0x3ff) {
|
||||||
|
cpuram.mem[address] = val;
|
||||||
|
} else if (address >= 0x4000 && address <= 0x5fff) {
|
||||||
|
dvgram.mem[address - 0x4000] = val;
|
||||||
|
} else if (address >= 0x3000 && address <= 0x3fff) {
|
||||||
|
//console.log(address.toString(16), val);
|
||||||
|
if (address == 0x3000) dvg.runUntilHalt();
|
||||||
|
// TODO: draw asynchronous or allow poll of HALT ($2002)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
cpu.connectBus(bus);
|
||||||
|
// create video/audio
|
||||||
|
video = new VectorVideo(mainElement,1024,1024);
|
||||||
|
dvg = new DVGStateMachine(bus, video);
|
||||||
|
audio = new SampleAudio(cpuFrequency);
|
||||||
|
video.start();
|
||||||
|
timer = new AnimationTimer(60, function() {
|
||||||
|
video.clear();
|
||||||
|
// 262.5 scanlines per frame
|
||||||
|
var iaddr = 0x4000;
|
||||||
|
var iofs = 0;
|
||||||
|
breakClock = -1;
|
||||||
|
clock = 0;
|
||||||
|
for (var i=0; i<cpuCyclesPerFrame; i++) {
|
||||||
|
if (debugCondition && breakClock < 0 && debugCondition()) { breakClock = clock; }
|
||||||
|
clock++;
|
||||||
|
if (--nmicount == 0) {
|
||||||
|
//console.log("NMI", cpu.saveState());
|
||||||
|
var n = cpu.setNMIAndWait();
|
||||||
|
clock += n;
|
||||||
|
nmicount = cpuCyclesPerNMI - n;
|
||||||
|
//console.log(n, clock, nmicount);
|
||||||
|
}
|
||||||
|
cpu.clockPulse();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
video.setKeyboardEvents(function(key,flags) {
|
||||||
|
var KEY2ADDR = {
|
||||||
|
"Shift": 3,
|
||||||
|
" ": 4,
|
||||||
|
"5": 8+0,
|
||||||
|
"6": 8+1,
|
||||||
|
"7": 8+2,
|
||||||
|
"1": 8+3,
|
||||||
|
"2": 8+4,
|
||||||
|
ArrowUp: 8+5,
|
||||||
|
ArrowRight: 8+6,
|
||||||
|
ArrowLeft: 8+7,
|
||||||
|
};
|
||||||
|
var addr = KEY2ADDR[key];
|
||||||
|
console.log(key,flags,addr);
|
||||||
|
if (addr >= 0) {
|
||||||
|
switches[addr] = (flags&1) ? 0xff : 0x00;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getOpcodeMetadata = function(opcode, offset) {
|
||||||
|
return Javatari.getOpcodeMetadata(opcode, offset); // TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loadROM = function(title, data) {
|
||||||
|
if(data.length != 0x1800) {
|
||||||
|
throw "ROM length must be == 0x1800";
|
||||||
|
}
|
||||||
|
rom = data;
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getRasterPosition = function() {
|
||||||
|
return {x:0, y:0};
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isRunning = function() {
|
||||||
|
return timer.isRunning();
|
||||||
|
}
|
||||||
|
this.pause = function() {
|
||||||
|
timer.stop();
|
||||||
|
}
|
||||||
|
this.resume = function() {
|
||||||
|
timer.start();
|
||||||
|
}
|
||||||
|
this.reset = function() {
|
||||||
|
this.clearDebug();
|
||||||
|
cpu.reset();
|
||||||
|
}
|
||||||
|
this.getOriginPC = function() {
|
||||||
|
return (this.readAddress(0xfffc) | (this.readAddress(0xfffd) << 8)) & 0xffff;
|
||||||
|
}
|
||||||
|
this.readAddress = function(addr) {
|
||||||
|
return bus.read(addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
var onBreakpointHit;
|
||||||
|
var debugCondition;
|
||||||
|
var debugSavedState = null;
|
||||||
|
var debugBreakState = null;
|
||||||
|
var debugTargetClock = 0;
|
||||||
|
var debugClock = 0;
|
||||||
|
var debugFrameStartClock = 0;
|
||||||
|
var breakClock;
|
||||||
|
|
||||||
|
this.setDebugCondition = function(debugCond) {
|
||||||
|
if (debugSavedState) {
|
||||||
|
self.loadState(debugSavedState);
|
||||||
|
} else {
|
||||||
|
debugSavedState = self.saveState();
|
||||||
|
}
|
||||||
|
debugClock = 0;
|
||||||
|
debugCondition = debugCond;
|
||||||
|
self.resume();
|
||||||
|
}
|
||||||
|
this.setupDebug = function(callback) {
|
||||||
|
onBreakpointHit = callback;
|
||||||
|
}
|
||||||
|
this.clearDebug = function() {
|
||||||
|
debugSavedState = null;
|
||||||
|
debugTargetClock = 0;
|
||||||
|
debugClock = 0;
|
||||||
|
debugFrameStartClock = 0;
|
||||||
|
onBreakpointHit = null;
|
||||||
|
debugCondition = null;
|
||||||
|
}
|
||||||
|
this.breakpointHit = function() {
|
||||||
|
debugBreakState = self.saveState();
|
||||||
|
console.log("Breakpoint at clk", debugClock, "PC", debugBreakState.c.PC.toString(16));
|
||||||
|
this.pause();
|
||||||
|
if (onBreakpointHit) {
|
||||||
|
onBreakpointHit(debugBreakState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.step = function() {
|
||||||
|
var previousPC = -1;
|
||||||
|
self.setDebugCondition(function() {
|
||||||
|
if (debugClock++ >= debugTargetClock) {
|
||||||
|
var thisState = cpu.saveState();
|
||||||
|
if (previousPC < 0) {
|
||||||
|
previousPC = thisState.PC;
|
||||||
|
} else {
|
||||||
|
if (thisState.PC != previousPC && thisState.T == 0) {
|
||||||
|
//console.log(previousPC.toString(16), thisPC.toString(16));
|
||||||
|
debugTargetClock = debugClock-1;
|
||||||
|
self.breakpointHit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.runToPC = function(targetPC) {
|
||||||
|
var self = this;
|
||||||
|
self.setDebugCondition(function() {
|
||||||
|
if (debugClock++ >= debugTargetClock) {
|
||||||
|
var thisPC = cpu.saveState().PC;
|
||||||
|
if (thisPC == targetPC) {
|
||||||
|
self.breakpointHit();
|
||||||
|
debugTargetClock = debugClock;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loadState = function(state) {
|
||||||
|
cpu.loadState(state.c);
|
||||||
|
cpuram.mem.set(state.cb);
|
||||||
|
dvgram.mem.set(state.db);
|
||||||
|
nmicount = state.nmic;
|
||||||
|
}
|
||||||
|
this.saveState = function() {
|
||||||
|
return {
|
||||||
|
c:cpu.saveState(),
|
||||||
|
cb:cpuram.mem.slice(0),
|
||||||
|
db:dvgram.mem.slice(0),
|
||||||
|
nmic:nmicount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.getRAMForState = function(state) {
|
||||||
|
return state.cb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DVGStateMachine = function(bus, video) {
|
||||||
|
var self = this;
|
||||||
|
var pc = 0;
|
||||||
|
var x = 0;
|
||||||
|
var y = 0;
|
||||||
|
var gsc = 0;
|
||||||
|
var bofs = 0x4000;
|
||||||
|
var pcstack = [];
|
||||||
|
var running = false;
|
||||||
|
|
||||||
|
function readWord(a) {
|
||||||
|
a &= 0xfff;
|
||||||
|
return bus.read(a*2+bofs) + (bus.read(a*2+bofs+1) << 8);
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeSigned(w, o2) {
|
||||||
|
s = w & (1<<o2);
|
||||||
|
w = w & ((1<<o2)-1);
|
||||||
|
if (s)
|
||||||
|
return -w;
|
||||||
|
else
|
||||||
|
return w;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.go = function() {
|
||||||
|
pc = 0;
|
||||||
|
gsc = 7;
|
||||||
|
running = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.runUntilHalt = function() {
|
||||||
|
this.go();
|
||||||
|
for (var i=0; i<10000; i++) { // TODO
|
||||||
|
if (!running) break;
|
||||||
|
this.nextInstruction();
|
||||||
|
}
|
||||||
|
//console.log('DVG',i);
|
||||||
|
}
|
||||||
|
|
||||||
|
GSCALES = [7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8];
|
||||||
|
|
||||||
|
this.nextInstruction = function() {
|
||||||
|
if (!running) return;
|
||||||
|
var w = readWord(pc);
|
||||||
|
var op = w >> 12;
|
||||||
|
//console.log(hex(pc), hex(w));
|
||||||
|
pc++;
|
||||||
|
switch (op) {
|
||||||
|
// VEC
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
case 4:
|
||||||
|
case 5:
|
||||||
|
case 6:
|
||||||
|
case 7:
|
||||||
|
case 8:
|
||||||
|
case 9: { // VCTR
|
||||||
|
var sc = gsc + 9 - op;
|
||||||
|
var w2 = readWord(pc++);
|
||||||
|
var z = w2 >> 12;
|
||||||
|
var x2 = x + ((decodeSigned(w2, 10) << 7) >> sc);
|
||||||
|
var y2 = y + ((decodeSigned(w, 10) << 7) >> sc);
|
||||||
|
video.drawLine(x, y, x2, y2, z);
|
||||||
|
//console.log(pc.toString(16), w.toString(16), w2.toString(16), gsc, sc, x, y, x2, y2);
|
||||||
|
x = x2;
|
||||||
|
y = y2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 10: { // LABS
|
||||||
|
var w2 = readWord(pc++);
|
||||||
|
gsc = GSCALES[w2 >> 12];
|
||||||
|
x = w2 & 0x3ff;
|
||||||
|
y = w & 0x3ff;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 11: // HALT
|
||||||
|
running = false;
|
||||||
|
break;
|
||||||
|
case 13: // RTSL
|
||||||
|
pc = pcstack.pop();
|
||||||
|
break;
|
||||||
|
case 12: // JSRL
|
||||||
|
pcstack.push(pc);
|
||||||
|
case 14: // JMPL
|
||||||
|
pc = w & 0xfff;
|
||||||
|
break;
|
||||||
|
case 15: { // SVEC
|
||||||
|
var sc = ((w>>11)&1) + ((w>>2)&2);
|
||||||
|
sc = gsc - sc - 1;
|
||||||
|
var x2 = x + ((decodeSigned(w, 2) << 7) >> sc);
|
||||||
|
var y2 = y + ((decodeSigned(w>>8, 2) << 7) >> sc);
|
||||||
|
var z = (w >> 4) & 0xf;
|
||||||
|
video.drawLine(x, y, x2, y2, z);
|
||||||
|
x = x2;
|
||||||
|
y = y2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -29,6 +29,7 @@ var PRESETS = [
|
|||||||
// {id:'examples/fullgame', name:'Thru Hike: The Game', title:'Thru Hike'},
|
// {id:'examples/fullgame', name:'Thru Hike: The Game', title:'Thru Hike'},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
Javatari.AUTO_START = false;
|
||||||
Javatari.SHOW_ERRORS = false;
|
Javatari.SHOW_ERRORS = false;
|
||||||
Javatari.CARTRIDGE_CHANGE_DISABLED = true;
|
Javatari.CARTRIDGE_CHANGE_DISABLED = true;
|
||||||
Javatari.DEBUG_SCANLINE_OVERFLOW = false; // TODO: make a switch
|
Javatari.DEBUG_SCANLINE_OVERFLOW = false; // TODO: make a switch
|
||||||
@ -37,6 +38,10 @@ Javatari.AUDIO_BUFFER_SIZE = 256;
|
|||||||
VCSPlatform = function() {
|
VCSPlatform = function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
this.start = function() {
|
||||||
|
Javatari.start();
|
||||||
|
}
|
||||||
|
|
||||||
this.loadROM = function(title, data) {
|
this.loadROM = function(title, data) {
|
||||||
Javatari.loadROM(title, data);
|
Javatari.loadROM(title, data);
|
||||||
this.current_output = data; // TODO: use bus
|
this.current_output = data; // TODO: use bus
|
||||||
@ -74,11 +79,12 @@ VCSPlatform = function() {
|
|||||||
Javatari.room.console.powerOn();
|
Javatari.room.console.powerOn();
|
||||||
}
|
}
|
||||||
this.getOriginPC = function() {
|
this.getOriginPC = function() {
|
||||||
return 0xf000; // TODO: read from vector
|
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 - 0xf000]; // TODO: use bus to read
|
||||||
}
|
}
|
||||||
|
this.getRAMForState = function(state) {
|
||||||
|
return jt.Util.byteStringToUInt8Array(atob(state.r.b));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
platform = new VCSPlatform();
|
|
||||||
|
40
src/ui.js
40
src/ui.js
@ -1,4 +1,6 @@
|
|||||||
|
|
||||||
|
// 8bitworkshop IDE user interface
|
||||||
|
|
||||||
var worker = new Worker("./src/worker/workermain.js");
|
var worker = new Worker("./src/worker/workermain.js");
|
||||||
var current_output = null;
|
var current_output = null;
|
||||||
var current_preset_idx = -1; // TODO: use URL
|
var current_preset_idx = -1; // TODO: use URL
|
||||||
@ -125,10 +127,6 @@ function _shareFile(e) {
|
|||||||
alert("Please fix errors before sharing.");
|
alert("Please fix errors before sharing.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (current_preset_idx < 0) {
|
|
||||||
alert("Can only reset built-in file examples.")
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
var text = editor.getValue();
|
var text = editor.getValue();
|
||||||
console.log("POST",text.length,'bytes');
|
console.log("POST",text.length,'bytes');
|
||||||
$.post({
|
$.post({
|
||||||
@ -152,8 +150,9 @@ function _shareFile(e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _resetPreset(e) {
|
function _resetPreset(e) {
|
||||||
if (current_preset_idx >= 0
|
if (current_preset_idx < 0) {
|
||||||
&& confirm("Reset '" + PRESETS[current_preset_idx].name + "' to default?")) {
|
alert("Can only reset built-in file examples.")
|
||||||
|
} else if (confirm("Reset '" + PRESETS[current_preset_idx].name + "' to default?")) {
|
||||||
qs['reset'] = '1';
|
qs['reset'] = '1';
|
||||||
window.location = "?" + $.param(qs);
|
window.location = "?" + $.param(qs);
|
||||||
}
|
}
|
||||||
@ -232,7 +231,7 @@ worker.onmessage = function(e) {
|
|||||||
editor.clearGutter("gutter-info");
|
editor.clearGutter("gutter-info");
|
||||||
for (info of e.data.listing.errors) {
|
for (info of e.data.listing.errors) {
|
||||||
var div = document.createElement("div");
|
var div = document.createElement("div");
|
||||||
div.setAttribute("class", "tooltip tooltiperror");
|
div.setAttribute("class", "tooltipbox tooltiperror");
|
||||||
div.style.color = '#ff3333'; // TODO
|
div.style.color = '#ff3333'; // TODO
|
||||||
div.appendChild(document.createTextNode("\u24cd"));
|
div.appendChild(document.createTextNode("\u24cd"));
|
||||||
var tooltip = document.createElement("span");
|
var tooltip = document.createElement("span");
|
||||||
@ -377,8 +376,8 @@ function showMemory(state) {
|
|||||||
if (state) {
|
if (state) {
|
||||||
s = cpuStateToLongString(state.c);
|
s = cpuStateToLongString(state.c);
|
||||||
s += "\n";
|
s += "\n";
|
||||||
var ram = jt.Util.byteStringToUInt8Array(atob(state.r.b));
|
var ram = platform.getRAMForState(state);
|
||||||
// TODO: show entire RAM for other platforms
|
// TODO: show scrollable RAM for other platforms
|
||||||
for (var ofs=0; ofs<0x80; ofs+=0x10) {
|
for (var ofs=0; ofs<0x80; ofs+=0x10) {
|
||||||
s += '$' + hex(ofs+0x80) + ':';
|
s += '$' + hex(ofs+0x80) + ':';
|
||||||
for (var i=0; i<0x10; i++) {
|
for (var i=0; i<0x10; i++) {
|
||||||
@ -636,15 +635,11 @@ function showLoopTimingForCurrentLine() {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function reset() {
|
|
||||||
platform.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetAndDebug() {
|
function resetAndDebug() {
|
||||||
reset();
|
platform.reset();
|
||||||
runToCursor();
|
runToCursor();
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
function setupDebugControls(){
|
function setupDebugControls(){
|
||||||
$("#dbg_reset").click(resetAndDebug);
|
$("#dbg_reset").click(resetAndDebug);
|
||||||
$("#dbg_pause").click(pause);
|
$("#dbg_pause").click(pause);
|
||||||
@ -718,8 +713,10 @@ var qs = (function (a) {
|
|||||||
return b;
|
return b;
|
||||||
})(window.location.search.substr(1).split('&'));
|
})(window.location.search.substr(1).split('&'));
|
||||||
|
|
||||||
|
// start
|
||||||
setupDebugControls();
|
setupDebugControls();
|
||||||
showWelcomeMessage();
|
showWelcomeMessage();
|
||||||
|
// parse query string
|
||||||
try {
|
try {
|
||||||
// is this a share URL?
|
// is this a share URL?
|
||||||
if (qs['sharekey']) {
|
if (qs['sharekey']) {
|
||||||
@ -753,6 +750,19 @@ try {
|
|||||||
var lastid = localStorage.getItem("__lastid");
|
var lastid = localStorage.getItem("__lastid");
|
||||||
gotoPresetNamed(lastid || PRESETS[0].id);
|
gotoPresetNamed(lastid || PRESETS[0].id);
|
||||||
}
|
}
|
||||||
|
// load and start platform object
|
||||||
|
if (qs['platform'] == 'vcs') {
|
||||||
|
platform = new VCSPlatform();
|
||||||
|
platform.start();
|
||||||
|
} else if (qs['platform'] == 'apple2') {
|
||||||
|
platform = new Apple2Platform($("#emulator")[0]);
|
||||||
|
platform.start();
|
||||||
|
} else if (qs['platform'] == 'atarivec') {
|
||||||
|
platform = new AtariVectorPlatform($("#emulator")[0]);
|
||||||
|
platform.start();
|
||||||
|
} else {
|
||||||
|
alert("Platform " + qs['platform'] + " not recognized");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
alert(e+""); // TODO?
|
alert(e+""); // TODO?
|
||||||
|
207
src/util.js
Normal file
207
src/util.js
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
|
||||||
|
function lzgmini() {
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
var LZG_HEADER_SIZE = 16;
|
||||||
|
var LZG_METHOD_COPY = 0;
|
||||||
|
var LZG_METHOD_LZG1 = 1;
|
||||||
|
|
||||||
|
// LUT for decoding the copy length parameter
|
||||||
|
var LZG_LENGTH_DECODE_LUT = [2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,
|
||||||
|
20,21,22,23,24,25,26,27,28,29,35,48,72,128];
|
||||||
|
|
||||||
|
// Decoded data (produced by the decode() method)
|
||||||
|
var outdata = null;
|
||||||
|
|
||||||
|
// Calculate the checksum
|
||||||
|
var calcChecksum = function(data) {
|
||||||
|
var a = 1;
|
||||||
|
var b = 0;
|
||||||
|
var i = LZG_HEADER_SIZE;
|
||||||
|
while (i < data.length)
|
||||||
|
{
|
||||||
|
a = (a + (data[i] & 0xff)) & 0xffff;
|
||||||
|
b = (b + a) & 0xffff;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return (b << 16) | a;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode LZG coded data. The function returns the size of the decoded data.
|
||||||
|
// Use any of the get* methods to retrieve the decoded data.
|
||||||
|
this.decode = function(data) {
|
||||||
|
// Start by clearing the decompressed array in this object
|
||||||
|
outdata = null;
|
||||||
|
|
||||||
|
// Check magic ID
|
||||||
|
if ((data.length < LZG_HEADER_SIZE) || (data[0] != 76) ||
|
||||||
|
(data[1] != 90) || (data[2] != 71))
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate & check the checksum
|
||||||
|
var checksum = ((data[11] & 0xff) << 24) |
|
||||||
|
((data[12] & 0xff) << 16) |
|
||||||
|
((data[13] & 0xff) << 8) |
|
||||||
|
(data[14] & 0xff);
|
||||||
|
if (calcChecksum(data) != checksum)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check which method to use
|
||||||
|
var method = data[15] & 0xff;
|
||||||
|
if (method == LZG_METHOD_LZG1)
|
||||||
|
{
|
||||||
|
// Get marker symbols
|
||||||
|
var m1 = data[16] & 0xff;
|
||||||
|
var m2 = data[17] & 0xff;
|
||||||
|
var m3 = data[18] & 0xff;
|
||||||
|
var m4 = data[19] & 0xff;
|
||||||
|
|
||||||
|
// Main decompression loop
|
||||||
|
var symbol, b, b2, b3, len, offset;
|
||||||
|
var dst = new Array();
|
||||||
|
var dstlen = 0;
|
||||||
|
var k = LZG_HEADER_SIZE + 4;
|
||||||
|
var datalen = data.length;
|
||||||
|
while (k <= datalen)
|
||||||
|
{
|
||||||
|
symbol = data[k++] & 0xff;
|
||||||
|
if ((symbol != m1) && (symbol != m2) && (symbol != m3) && (symbol != m4))
|
||||||
|
{
|
||||||
|
// Literal copy
|
||||||
|
dst[dstlen++] = symbol;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
b = data[k++] & 0xff;
|
||||||
|
if (b != 0)
|
||||||
|
{
|
||||||
|
// Decode offset / length parameters
|
||||||
|
if (symbol == m1)
|
||||||
|
{
|
||||||
|
// marker1 - "Distant copy"
|
||||||
|
len = LZG_LENGTH_DECODE_LUT[b & 0x1f];
|
||||||
|
b2 = data[k++] & 0xff;
|
||||||
|
b3 = data[k++] & 0xff;
|
||||||
|
offset = (((b & 0xe0) << 11) | (b2 << 8) | b3) + 2056;
|
||||||
|
}
|
||||||
|
else if (symbol == m2)
|
||||||
|
{
|
||||||
|
// marker2 - "Medium copy"
|
||||||
|
len = LZG_LENGTH_DECODE_LUT[b & 0x1f];
|
||||||
|
b2 = data[k++] & 0xff;
|
||||||
|
offset = (((b & 0xe0) << 3) | b2) + 8;
|
||||||
|
}
|
||||||
|
else if (symbol == m3)
|
||||||
|
{
|
||||||
|
// marker3 - "Short copy"
|
||||||
|
len = (b >> 6) + 3;
|
||||||
|
offset = (b & 63) + 8;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// marker4 - "Near copy (incl. RLE)"
|
||||||
|
len = LZG_LENGTH_DECODE_LUT[b & 0x1f];
|
||||||
|
offset = (b >> 5) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the corresponding data from the history window
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
dst[dstlen] = dst[dstlen-offset];
|
||||||
|
dstlen++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Literal copy (single occurance of a marker symbol)
|
||||||
|
dst[dstlen++] = symbol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the decompressed data in the lzgmini object for later retrieval
|
||||||
|
outdata = dst;
|
||||||
|
return outdata;
|
||||||
|
}
|
||||||
|
else if (method == LZG_METHOD_COPY)
|
||||||
|
{
|
||||||
|
// Plain copy
|
||||||
|
var dst = new Array();
|
||||||
|
var dstlen = 0;
|
||||||
|
var datalen = data.length;
|
||||||
|
for (var i = LZG_HEADER_SIZE; i < datalen; i++)
|
||||||
|
{
|
||||||
|
dst[dstlen++] = data[i] & 0xff;
|
||||||
|
}
|
||||||
|
outdata = dst;
|
||||||
|
return outdata;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Unknown method
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the decoded byte array
|
||||||
|
this.getByteArray = function()
|
||||||
|
{
|
||||||
|
return outdata;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the decoded string from a Latin 1 (or ASCII) encoded array
|
||||||
|
this.getStringLatin1 = function()
|
||||||
|
{
|
||||||
|
var str = "";
|
||||||
|
if (outdata != null)
|
||||||
|
{
|
||||||
|
var charLUT = new Array();
|
||||||
|
for (var i = 0; i < 256; ++i)
|
||||||
|
charLUT[i] = String.fromCharCode(i);
|
||||||
|
var outlen = outdata.length;
|
||||||
|
for (var i = 0; i < outlen; i++)
|
||||||
|
str += charLUT[outdata[i]];
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the decoded string from an UTF-8 encoded array
|
||||||
|
this.getStringUTF8 = function()
|
||||||
|
{
|
||||||
|
var str = "";
|
||||||
|
if (outdata != null)
|
||||||
|
{
|
||||||
|
var charLUT = new Array();
|
||||||
|
for (var i = 0; i < 128; ++i)
|
||||||
|
charLUT[i] = String.fromCharCode(i);
|
||||||
|
var c;
|
||||||
|
var outlen = outdata.length;
|
||||||
|
for (var i = 0; i < outlen;)
|
||||||
|
{
|
||||||
|
c = outdata[i++];
|
||||||
|
if (c < 128)
|
||||||
|
{
|
||||||
|
str += charLUT[c];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ((c > 191) && (c < 224))
|
||||||
|
{
|
||||||
|
c = ((c & 31) << 6) | (outdata[i++] & 63);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
c = ((c & 15) << 12) | ((outdata[i] & 63) << 6) | (outdata[i+1] & 63);
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
str += String.fromCharCode(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user