180 lines
5.3 KiB
JavaScript
180 lines
5.3 KiB
JavaScript
/*
|
|
* 6502 Based Virtual Computer
|
|
* Copyright (C) 2011 Scott C. Duensing <scott@jaegertech.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
// Memory
|
|
function ram(newSize) {
|
|
|
|
// --- Private Types.
|
|
|
|
function T_EVENT() {
|
|
this.callback = null;
|
|
this.startAddress = 0;
|
|
this.endAddress = 0;
|
|
this.anyData = null;
|
|
};
|
|
|
|
|
|
// --- Private variables.
|
|
|
|
var MEMORY = new Array(newSize);
|
|
var WRITE_EVENTS = new Array();
|
|
var READ_EVENTS = new Array();
|
|
var EVENTS_ENABLED = true;
|
|
var EVENTS_ENABLED_COUNTER = 0;
|
|
var CANVAS_HEIGHT = 0;
|
|
var CANVAS_WIDTH = 0;
|
|
var CANVAS_DATA = null;
|
|
var CANVAS_CONTEXT = null;
|
|
|
|
|
|
// --- Private methods.
|
|
|
|
var drawByte = function(location, value) {
|
|
var index = (MEMORY.length - location) * 4;
|
|
CANVAS_DATA.data[index] = value;
|
|
CANVAS_DATA.data[index + 1] = value;
|
|
CANVAS_DATA.data[index + 2] = value;
|
|
CANVAS_DATA.data[index + 3] = 255;
|
|
}
|
|
|
|
var updateCanvas = function() {
|
|
CANVAS_CONTEXT.putImageData(CANVAS_DATA, 0, 0);
|
|
}
|
|
|
|
|
|
// --- Public methods.
|
|
|
|
// Add callback for memory read event.
|
|
this.addReadCallback = function(newStart, newEnd, newData, newCallback) {
|
|
var event = new T_EVENT();
|
|
event.callback = newCallback;
|
|
event.startAddress = newStart;
|
|
event.endAddress = newEnd;
|
|
event.anyData = newData;
|
|
READ_EVENTS.push(event);
|
|
console.log("Read event " + READ_EVENTS.length + " added from 0x" + newStart.toString(16) + " to 0x" + newEnd.toString(16));
|
|
};
|
|
|
|
// Add callback for memory write event.
|
|
this.addWriteCallback = function(newStart, newEnd, newData, newCallback) {
|
|
var event = new T_EVENT();
|
|
event.callback = newCallback;
|
|
event.startAddress = newStart;
|
|
event.endAddress = newEnd;
|
|
event.anyData = newData;
|
|
WRITE_EVENTS.push(event);
|
|
console.log("Write event " + WRITE_EVENTS.length + " added from 0x" + newStart.toString(16) + " to 0x" + newEnd.toString(16));
|
|
};
|
|
|
|
// Optionally attach to a div for visual display of memory contents.
|
|
this.attach = function(divId) {
|
|
//***TODO*** Assumes 64k
|
|
CANVAS_HEIGHT = 256;
|
|
CANVAS_WIDTH = 256;
|
|
var canvas = document.createElement('canvas');
|
|
canvas.id = "MemoryDisplay";
|
|
canvas.width = CANVAS_WIDTH;
|
|
canvas.height = CANVAS_HEIGHT;
|
|
CANVAS_CONTEXT = canvas.getContext("2d");
|
|
document.getElementById(divId).appendChild(canvas);
|
|
CANVAS_DATA = CANVAS_CONTEXT.getImageData(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
|
|
window.setInterval(updateCanvas.bind(this), 500);
|
|
};
|
|
|
|
// Are we performing callbacks?
|
|
this.callbacksEnabled = function(trueFalse) {
|
|
EVENTS_ENABLED_COUNTER += (trueFalse ? -1 : 1);
|
|
EVENTS_ENABLED = (EVENTS_ENABLED_COUNTER == 0);
|
|
};
|
|
|
|
// Deserialize RAM state.
|
|
this.deserialize = function(state) {
|
|
for (var x=0; x<MEMORY.length; x++) {
|
|
MEMORY[x] = state.RAM[x];
|
|
if (CANVAS_DATA != null) {
|
|
drawByte(x, MEMORY[x] & 0xff);
|
|
}
|
|
}
|
|
};
|
|
|
|
// See how much RAM we have.
|
|
this.getSize = function() {
|
|
return MEMORY.length;
|
|
};
|
|
|
|
// Read a byte and return it.
|
|
this.readByte = function(address) {
|
|
if ((address < MEMORY.length) && (address >= 0)) {
|
|
if ((READ_EVENTS.length > 0) && (EVENTS_ENABLED)) {
|
|
for (var x=0; x<READ_EVENTS.length; x++) {
|
|
var event = READ_EVENTS[x];
|
|
if ((address >= event.startAddress) && (address <= event.endAddress)) {
|
|
event.callback(address, MEMORY[address] & 0xff, event.anyData);
|
|
}
|
|
}
|
|
}
|
|
return MEMORY[address] & 0xff;
|
|
}
|
|
};
|
|
|
|
// Read a word and return it.
|
|
this.readWord = function(address) {
|
|
return (this.readByte(address) + (this.readByte(address + 1) << 8));
|
|
};
|
|
|
|
// Remove registered events.
|
|
this.removeEvents = function() {
|
|
READ_EVENTS = new Array();
|
|
WRITE_EVENTS = new Array();
|
|
};
|
|
|
|
// Set all the memory to zero. Does NOT fire events.
|
|
this.reset = function() {
|
|
for (var x=0; x<MEMORY.length; x++) {
|
|
MEMORY[x] = 0;
|
|
drawByte(x, 0, 0, 0);
|
|
}
|
|
};
|
|
|
|
// Serialize RAM state.
|
|
this.serialize = function() {
|
|
var state = {
|
|
RAM: MEMORY
|
|
};
|
|
return state;
|
|
};
|
|
|
|
// Write a byte.
|
|
this.writeByte = function(address, value) {
|
|
if ((address < MEMORY.length) && (address >= 0)) {
|
|
MEMORY[address] = value & 0xff;
|
|
if (CANVAS_DATA != null) {
|
|
drawByte(address, value & 0xff);
|
|
}
|
|
if ((WRITE_EVENTS.length > 0) && (EVENTS_ENABLED)) {
|
|
for (var x=0; x<WRITE_EVENTS.length; x++) {
|
|
var event = WRITE_EVENTS[x];
|
|
if ((address >= event.startAddress) && (address <= event.endAddress))
|
|
event.callback(address, MEMORY[address] & 0xff, event.anyData);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
}
|