2017-01-03 15:43:40 +00:00
|
|
|
"use strict";
|
2017-01-03 01:42:15 +00:00
|
|
|
|
2017-01-03 15:43:40 +00:00
|
|
|
var ATARIVEC_PRESETS = [
|
2017-01-03 01:42:15 +00:00
|
|
|
]
|
|
|
|
|
2017-01-03 15:43:40 +00:00
|
|
|
var AtariVectorPlatform = function(mainElement) {
|
2017-01-03 01:42:15 +00:00
|
|
|
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;
|
|
|
|
|
2017-01-03 15:43:40 +00:00
|
|
|
this.getPresets = function() {
|
|
|
|
return ATARIVEC_PRESETS;
|
|
|
|
}
|
|
|
|
|
2017-01-03 01:42:15 +00:00
|
|
|
this.start = function() {
|
|
|
|
cpu = new jt.M6502();
|
|
|
|
cpuram = new RAM(0x400);
|
|
|
|
dvgram = new RAM(0x2000);
|
|
|
|
//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();
|
2017-01-03 15:43:40 +00:00
|
|
|
//cpu.executeInstruction();
|
2017-01-03 01:42:15 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
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) {
|
2017-01-03 15:43:40 +00:00
|
|
|
if(data.length != 0x2000) {
|
|
|
|
throw "ROM length must be == 0x2000";
|
2017-01-03 01:42:15 +00:00
|
|
|
}
|
2017-01-03 15:43:40 +00:00
|
|
|
rom = data.slice(0,0x1800);
|
|
|
|
vecrom = data.slice(0x1800,0x2000);
|
2017-01-03 01:42:15 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-03 15:43:40 +00:00
|
|
|
var DVGStateMachine = function(bus, video) {
|
2017-01-03 01:42:15 +00:00
|
|
|
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) {
|
2017-01-03 15:43:40 +00:00
|
|
|
var s = w & (1<<o2);
|
2017-01-03 01:42:15 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2017-01-03 15:43:40 +00:00
|
|
|
var GSCALES = [7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8];
|
2017-01-03 01:42:15 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|