Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Koyaanisqatsi Machine Emulator
<html>
<head>
<meta http-equiv=refresh content='600'>
<title>It's Koyaanisqatsi, baby!</title>
<script>
var linkTo
= {
gist :{
host :"https://gist.githubusercontent.com/Alikberov",
list :"/66bb9698f58765031522c600dda55260/raw/koy-mac-catalogue.txt"
}
};
</script>
<script>
Number.prototype.toHex = function(n) {
return (n < 0 ? "0x" : "") + ("0000000" + this.toString(16)).substr(-Math.abs(n)).toUpperCase();
}
</script>
<script>
const hRefresh = setTimeout
(function() {
window.location = "";
}, 30000);
window.addEventListener("mouseover",
function() {
clearTimeout(hRefresh);
}
);
if(window.location.protocol != "file:")
clearTimeout(hRefresh);
</script>
<script>
const getters = {
right :function() { return 0x0F & this; },
left :function() { return 0x0F & (this >> 4); },
low :function() { return 0xFF & this; },
high :function() { return 0xFF & (this >> 8); },
isNum :function() { return (this & 15) <= 9; },
isReg :function() { return (this & 15) >= 0xA && (this & 15) <= 0xD; },
isDec :function() { return (this & 15) <= 9 && ((this >> 4) & 15) <= 9 && this > 0; }
};
for(var fn in getters)
Number.prototype.__defineGetter__(fn, getters[fn]);
/*Number.prototype.__defineGetter__("right",
function() {
return this & 15;
}
);
Number.prototype.__defineGetter__("left",
function() {
return (this >> 4) & 15;
}
);
Number.prototype.__defineGetter__("low",
function() {
return this & 255;
}
);
Number.prototype.__defineGetter__("high",
function() {
return (this >> 8) & 255;
}
);
Number.prototype.__defineGetter__("isNum",
function() {
return this <= 9;
}
);
Number.prototype.__defineGetter__('isReg",
function() {
return this >= 0xA && this <= 0xD;
}
);*/
</script>
<script>
var i;
</script>
<script>
var KOY
= function() {
this.cps = 1; // Cycles Per Second
this.is_peek = false;
this.cycles = 0;
this.ticks = 0;
this.classic = false;
this.address = 0;
this.prefix = false;
this.prefixes = 0;
this.offset = 0;
this.id = 0;
this.dis_pointer = 0;
this.dis_address = 0x0000;
this.dis_lines = 16;
this.dis_lines_points = [];
this.dis_points_lines = [];
this.dis_lines_tables = [];
//////////////////////////////////////////////////////////////////////
this.devices
= {
i8255 :{
init :
function() {
this.state = [];
for(var i = 0; i < 256; ++ i)
this.state[i] = [0x00, 0x00, 0x00, 0x00];
}
,
clock :
function() {
}
,
read :
function(index, port) {
return this.state[index][port & 3];
}
,
write :
function(index, port, data) {
this.state[index][port & 3] = data & 0xFF;
}
},
i8279 :{
init :
function() {
this.state = [];
this.mode = [];
this.freq = [];
for(var i = 0; i < 256; ++ i) {
this.state[i] = 0;
this.mode[i] = 0;
this.freq[i] = 0;
}
}
,
clock :
function() {
}
,
read :
function(index, port) {
switch(port) {
case 0x00:
if(!this._this.is_peek) {
if(hUserPad.selectionEnd == hUserPad.selectionStart + 1) {
hUserPad.selectionStart ++;
hUserPad.selectionEnd ++;
if(hUserPad.selectionStart >= hUserPad.value.length) {
tmp = hUserPad.value.indexOf("$run ");
if(tmp >= 0) {
hUserPad.selectionStart = tmp + 5;
hUserPad.selectionEnd = hUserPad.selectionStart + 1;
}
}
}
}
return hUserPad.value.charCodeAt(hUserPad.selectionStart) & 0xFF;
}
return 0xFF;
}
,
write :
function(index, port, data) {
switch(port) {
case 0x01:
if((port & 0xE0) == 0x00)
// 0_0_0_D_D_K_K_K
// 0 0 -------> 8×8 character display - left entry
// 0 1 -------> 16×8 character display - left entry
// 1 0 -------> 8×8 character display - right entry
// 1 1 -------> 16×8 character display - right entry
// 0 0 0 -> Encoded Scan Keyboard - 2-Key Lock-out
// 0 0 1 -> Decoded Scan Keyboard - 2-Key Lock-out
// 0 1 0 -> Encoded Scan Keyboard - N-Key Roll-over
// 0 1 1 -> Decoded Scan Keyboard - N-Key Roll-over
// 1 0 0 -> Encoded Scan Sensor Matrix
// 1 0 1 -> Decoded Scan Sensor Matrix
// 1 1 0 -> Strobed Input. Encoded Display Scan
// 1 1 1 -> Strobed Input. Decoded Display Scan
this.mode[index] = data & 0x1F;
if((port & 0xE0) == 0x20)
this.freq[index] = data & 0x1F;
break;
}
}
},
i82C0 :{
init :
function() {
}
,
clock :
function() {
}
,
read :
function(index, port) {
return this.state[index][port & 3];
}
,
write :
function(index, port, data) {
if(data == 0x00)
hEmuLog.textContent = "";
else
hEmuLog.textContent += String.fromCharCode(data);
}
},
i82CB :{
init :
function() {
this.buffer = new Uint8Array(256 * 8);
}
,
clock :
function() {
}
,
read :
function(index, port) {
return 0;
}
,
write :
function(index, port, data) {
var addr = (index & 0xFF) << 3;
var x = (index & 0x0F) << 3;
var y = (index & 0xF0) >> 1;
var pixels;
var alfa = 0;
this.buffer[addr + (port & 7)] = data & 0xFF;
}
},
i82CD :{
init :
function() {
this.buffer = new Uint8Array(256 * 8);
}
,
clock :
function() {
}
,
read :
function(index, port) {
return 0;
}
,
write :
function(index, port, data) {
var addr = (index & 0xFF) << 3;
var x = (index & 0x0F) << 3;
var y = (index & 0xF0) >> 1;
var pixels;
var alfa = 0;
this.buffer[addr + (port & 7)] = data & 0xFF;
if((this.buffer[addr] | this.buffer[addr + 1]
| this.buffer[addr + 2] | this.buffer[addr + 3]
| this.buffer[addr + 4] | this.buffer[addr + 5]
| this.buffer[addr + 6] | this.buffer[addr + 7]) != 0)
alfa = 255;
for(var h = 0; h < 8; ++ h) {
pixels = hDisplay.getImageData(x + h, y, 1, 8);
data = this.buffer[addr + h];
for(var i = 0; i < 8; ++ i) {
pixels.data[i * 4 + 0] = data & 1 ? 0xFF : 0x00;
pixels.data[i * 4 + 1] = data & 1 ? 0xFF : 0x00;
pixels.data[i * 4 + 2] = data & 1 ? 0xFF : 0x00;
pixels.data[i * 4 + 3] = alfa;
data >>= 1;
}
hDisplay.putImageData(pixels, x + h, y);
}
}
}
};
//////////////////////////////////////////////////////////////////////
this.ram = new Array(65536);
//////////////////////////////////////////////////////////////////////
this.rams
= new Proxy
(new Array(65536),
{
get: function(target, name) {
if(0x00D0 <= name && name <= 0x00D9) {
if(name <= 0x00D7) {
if(("i82" + target[0x00D9].toHex(2)) in target.devices) {
return target.devices["i82" + target[0x00D9].toHex(2)].read(target[0x00D8], (+name & 0x7));
}
}
}
return target[name];
},
set: function(target, name, data) {
if(0x00D0 <= name && name <= 0x00D9) {
if(name <= 0x00D7) {
target[0xD000 + +target[0x00D9] * 16 + (+name & 0x000F)] = data;
if(("i82" + target[0x00D9].toHex(2)) in target.devices) {
target.devices["i82" + target[0x00D9].toHex(2)].write(target[0x00D8], (+name & 0x7), +data);
}
} else
if(name == 0x00D8)
target[0xD000 + +target[0x00D9] * 16 + (+name & 0x000F)] = data;
else {
if(data == 0xCB)
hScreen.canvas.style.zoom = 1;
target[0x00D9] = data;
}
/* switch(target[0x00D9]) {
case 0xC0:
switch(+name) {
case 0x00D0:
if(data == 0x00)
hEmuLog.textContent = "";
else
hEmuLog.textContent += String.fromCharCode(data);
}
break;
default:
}*/
}
if(name >= 0x7000 && 0x9FFF >= name) {
var addr = +name - 0x7000;
var x = addr % 120;
var y = (addr - x) / 120;
hGraphic.data[addr * 16 + 0] = +data;
hGraphic.data[addr * 16 + 1] = +data;
hGraphic.data[addr * 16 + 2] = +data;
hGraphic.data[addr * 16 + 3] = 0xFF;
hGraphic.data[addr * 16 + 4] = +data;
hGraphic.data[addr * 16 + 5] = +data;
hGraphic.data[addr * 16 + 6] = +data;
hGraphic.data[addr * 16 + 7] = 0xFF;
hGraphic.data[addr * 16 + 8] = +data;
hGraphic.data[addr * 16 + 9] = +data;
hGraphic.data[addr * 16 + 10] = +data;
hGraphic.data[addr * 16 + 11] = 0xFF;
hGraphic.data[addr * 16 + 12] = +data;
hGraphic.data[addr * 16 + 13] = +data;
hGraphic.data[addr * 16 + 14] = +data;
hGraphic.data[addr * 16 + 15] = 0xFF;
hScreen.putImageData(hGraphic, 0, 0);
//hScreen.fillStyle = "#" + (+data * 0x010101).HEX(6);
//hScreen.fillRect(x * 2, y * 2, 2, 2);
}
return target[name] = data;
}
}
);
//////////////////////////////////////////////////////////////////////
this.ram.base = 0x0000;
this.ctx = new Array(0xFFF + 1);
for(i = 0; i < this.ctx.length; ++ i)
this.ctx[i] = 0;
this.ctx.devices = this.devices;
for(i = 0; i < 8; ++ i) {
Object.defineProperty(this.ctx, 0xD0 + i,
{
set: (
function(data) {
var id = this._this.ctx[0xD9];
var name = `i82${id.toHex(2)}`;
if(name in this._this.devices)
this._this.devices[name].write(this._this.ctx[0xD8], this.index, data);
else
this._this.ctx[0xE0 + id] = data;
}
).bind(
{
_this :this,
index :i
}
),
get: (
function() {
var id = this._this.ctx[0xD9];
var name = `i82${id.toHex(2)}`;
if(name in this._this.devices)
return this._this.devices[name].read(this._this.ctx[0xD8], this.index);
return this._this.ctx[0xE0 + id];
}
).bind(
{
_this :this,
index :i
}
)
}
);
}
//////////////////////////////////////////////////////////////////////
this.ctx.receiver
= function(data) {
var vector = this[this[0x00AE] | 0x0F];
if(isFinite(data))
this[vector] = data.low;
else
if(typeof data == "string")
return vector.toHex(2).toLowerCase() + data;
return this[vector];
}
//////////////////////////////////////////////////////////////////////
this.ctx.translator
= function(data) {
var vector = this[(this[0x00AE].right << 4) | 0x000F];
if(isFinite(data))
this[vector] = data.low;
else
if(typeof data == "string")
return vector.toHex(2).toLowerCase() + data;
return this[vector];
}
//////////////////////////////////////////////////////////////////////
this.ctx.state
= function(data) {
if(isFinite(data))
this[0x00A0] = (data & 0xFF ? 0 : 1) | (data & 0x100 ? 2 : 0) | (data & 0x80 ? 8 : 0);
return this[0x00A0];
}
//////////////////////////////////////////////////////////////////////
this.ctx.pointer
= function(index, address) {
if(typeof address == "string")
return (0x0BC0 | index).toHex(3) + address;
else
if(isFinite(address)) {
this[this.base | index] = address.high;
this[(this.base | index) + 0x10] = address.low;
}
return (this[this.base | index] << 8) | this[(this.base | index) + 0x10];
}
//////////////////////////////////////////////////////////////////////
this.ram.toDump
= function(address, width) {
var i;
var n = address.low;
var row, data;
var dump = [];
address &= 0xFF00;
for(i = 0; i < 256; ++ i) {
if(i.right == 0)
row = [(address + i).toHex(4) + ":"];
data = this[address + i];
if(isFinite(data))
data = data.toHex(2);
else
data = "--";
row.push(
(i == n ? "<span class=ActiveByte>" + data + "</span>":
(i > n && i <= n + width - 1 ? "<span class=ActiveByte>" + data + "</span>" : data)
));
if(i.right == 15)
dump.push(row.join(" "));
}
return dump;
}
//////////////////////////////////////////////////////////////////////
this.ram.set
= function(data) {
var address;
var ram = this;
//
if("string" == typeof data) {
data
.split(/\r?\n/)
.forEach
(function(row) {
row
.split(/\s+|\t+/)
.forEach
(function(chars) {
data = chars.match(/[0-9A-F]{2,4}/);
if(data)
switch(data[0].length) {
case 4:
address = parseInt(data[0], 16);
break;
case 2:
ram[address ++] = parseInt(data[0], 16);
break;
}
else
if(chars.match(/'./))
ram[address ++] = chars.charCodeAt(1) & 0xFF;
});
});
} else
address = this.length;
while(address -- > 0)
this[address] = isFinite(data) ? data : 0;
return this;
}
//////////////////////////////////////////////////////////////////////
this.context
= function() {
var i;
var text = [];
var flags = this.ctx[0xA0];
text.push(`TICKS: ${this.ticks}`);
text.push(`PC:${this.ctx.pointer(0x00BE).toHex(4)}`);
text.push(`AF:${flags & 0x08 ? "AF(Is Among)" : "NA(No Among)"}`);
text.push(`BF:${flags & 0x04 ? "BF(Is ???)" : "NB(No ???)"}`);
text.push(`CF:${flags & 0x02 ? "CF(Is Carry)" : "NC(No Carry)"}`);
text.push(`DF:${flags & 0x01 ? "DF(Is Duplex)" : "ND(No Duplex)"}`);
this.is_peek = true;
for(i = 0; i <= 9; ++ i) {
text.push(`A${i}:${this.ctx[0xA0 + i].toHex(2)} BC${i}:${this.ctx.pointer(0xB0 | i).toHex(4)} D${i}:${this.ctx[0xD0 + i].toHex(2)}`);
}
this.is_peek = false;
return text.join("\r\n");
}
//////////////////////////////////////////////////////////////////////
this.prepareImage
= function(im) {
var pScr = [];
var hSrc = document.createElement("canvas").getContext("2d");
hSrc.canvas.width = im.width;
hSrc.canvas.height = im.height;
hSrc.canvas.setAttribute("crossOrigin", "");
hSrc.imageSmoothingEnabled = false;
hSrc.drawImage(im, 0, 0);
var imdt = hSrc.getImageData(0, 0, hSrc.canvas.width, hSrc.canvas.height);
var data = imdt.data;
var len = data.length;
var i, bytes, code = 0;
var bc, gc, rc;
var sprite = false;
var flag, index;
var paper, sprite, indexes, output;
var rrggbb;
//
for(i = 0; i < len; i += 4) {
paper = {
b: (data[i] & 0x30) << 2,
g: (data[i + 1] & 0x30) << 2,
r: (data[i + 2] & 0x30) << 2
};
sprite = {
b: (data[i] & 0xC0),
g: (data[i + 1] & 0xC0),
r: (data[i + 2] & 0xC0)
};
indexes = {
b: (data[i] & 0x0C) << 4,
g: (data[i + 1] & 0x0C) << 4,
r: (data[i + 2] & 0x0C) << 4
};
flag = paper.r == sprite.r && paper.g == sprite.g && paper.b == sprite.b;
if(flag) {
output = {
r: paper.r & 0xC0,
g: paper.g & 0xC0,
b: paper.b & 0xC0
};
} else {
output = {
r: sprite.r & 0xC0,
g: sprite.g & 0xC0,
b: sprite.b & 0xC0
};
}
data[i] = output.b;
data[i + 1] = output.g;
data[i + 2] = output.r;
code = ((indexes.r & 0xC0) >> 2) | ((indexes.g & 0xC0) >> 4) | ((indexes.b & 0xC0) >> 6);
output_rrggbb = (output.r >> 2) | (output.g >> 4) | (output.b >> 6);
paper_rrggbb = (paper.r >> 2) | (paper.g >> 4) | (paper.b >> 6);
sprite_rrggbb = (sprite.r >> 2) | (sprite.g >> 4) | (sprite.b >> 6);
if(index != code) {
index = code;
pScr.push(index);
pScr.push(128 + paper_rrggbb);
} else {
pScr.push(128 + 64 + sprite_rrggbb);
pScr.push(128 + 0 + paper_rrggbb);
}
}
//
pScr.width = hSrc.canvas.width;
pScr.height = hSrc.canvas.height;
return pScr;
}
//////////////////////////////////////////////////////////////////////
this.render
= function(hCnv, pScr) {
if(hCnv.canvas.width != pScr.width || hCnv.canvas.height != pScr.height) {
hCnv.canvas.width = pScr.width * 2;
hCnv.canvas.height = pScr.height * 2;
}
var imdt = hCnv.getImageData(0, 0, hCnv.canvas.width, hCnv.canvas.height);
var data = imdt.data;
var r = 0, g = 0, b = 0;
var len = data.length / 3;
var dup = hCnv.canvas.width * 4;
var i = 0, bytes, index = 0, shift = 0;
var d, x, y;
var s = 0;
var mask = this.devices.i82CB.buffer.slice(0x0, 0x08);
var rn, gn, bn, noisel = 0;
//
for(y = 0; y < hCnv.canvas.height; ++ y) {
for(x = 0; x < hCnv.canvas.width; ++ x) {
if(-- noisel < 0) {
noisel = Math.floor(Math.random() * 64 * this.render.noise / 100) + 8;
rn = Math.floor(Math.random() * 256 * this.render.noise / 100);
gn = Math.floor(Math.random() * 256 * this.render.noise / 100);
bn = Math.floor(Math.random() * 256 * this.render.noise / 100);
}
d = pScr[s ++];
if(d & 128) {
if(((d >> 6) & 1) == ((mask[index] >> shift) & 1)) {
b = ((d & 3) << 6) ^ bn;
g = ((d & 12) << 4) ^ gn;
r = ((d & 48) << 2) ^ rn;
}
} else {
index = (d >> 3) & 15;
shift = d & 7;
}
data[i] = b;
data[i + 1] = g;
data[i + 2] = r;
data[i + 3] = 255;
data[i + dup] = b;
data[i + dup + 1] = g;
data[i + dup + 2] = r;
data[i + dup + 3] = 255;
i += 4;
if(x == hCnv.canvas.width - 1)
i += dup;
}
}
hCnv.putImageData(imdt, 0, 0);
}
//////////////////////////////////////////////////////////////////////
this.assembly
= function(text, head) {
var _this = this;
var rows = text.split(/\r?\n/);
var i;
var ri, ti;
var part, text;
var curLabel = "";
var address = 0x0000;
var command, code;
var codes;
var rcv = "X", trs = "X";
var regs = {A:0xA0, B:0xB0, C:0xC0, D:0xD0};
var alu = {ADD:10, ADC:10, SUB:11, SBB:11, AND:12, CON:12, OR:13, DIS:13, XOR:14, EOR:14, MOV:15, FOR:15};
var defs = {};
var labels = [];
var label;
var review = 0;
var usr;
var vector, offset;
do {
label = [];
for(i = 0; i < rows.length; ++ i) {
codes = [];
this.dis_lines_points[i + head] = address;
this.dis_points_lines[address] = i + head;
part = /([^:;"'`\s]*(?::| ?))?(?:\s)*([^\s;]*)(?:\s*)([^\s,;"'`]*)(?:[,\s]*)?([^\s,;"'`]*)(?:[,\s]*)?(?:(?:("(?:\\.|.)*?")|('(?:\\.|.)*?')|(`(?:\\.|.)*?`)|[^;"'`]*)*)*(\.*)/.exec(rows[i]);
if(part[1]) {
switch(part[2].toUpperCase()) {
case ".DEF":
defs[part[1].toUpperCase()] = { bytes: 0, code: parseInt(part[3])};
this.dis_lines_points[i + head] = parseInt(part[3]) << 8;
break;
case ".DEFB":
defs[part[1].toUpperCase()] = { bytes: 1, code: parseInt(part[3])};
this.dis_lines_points[i + head] = parseInt(part[3]) << 8;
break;
case ".DEFW":
defs[part[1].toUpperCase()] = { bytes: 2, code: parseInt(part[3])};
this.dis_lines_points[i + head] = parseInt(part[3]) << 8;
break;
default:
part[1] = part[1].replace(/[ :]$/, "");
tmp = part[1].replace(/:/g, "..").match(/(\.*)(.*)/);
label = label.slice(0, tmp[1].length).concat(tmp[2].split("."));
part[1] = label.join(".");
if(labels[part[1]] != address)
delete labels[labels[part[1].toUpperCase()]];
labels[address] = part[1];
labels[part[1].toUpperCase()] = address;
break;
}
}
if(part[2].toUpperCase() == ".DEF") {
rcv = "X", trs = "X";
regs.A = 0xFA;
regs.B = 0xFB;
regs.C = 0xFC;
regs.D = 0xFD;
if(part[3].toUpperCase() in defs)
address = defs[part[3].toUpperCase()].code << 8;
this.dis_lines_points[i + head] = address;
this.dis_points_lines[address] = i + head;
} else
if(part[2].charAt(0) == ".") {
switch(part[2].toUpperCase()) {
case ".ORG":
rcv = "X", trs = "X";
regs.A = 0xFA;
regs.B = 0xFB;
regs.C = 0xFC;
regs.D = 0xFD;
if(part[3].toUpperCase() in defs)
address = defs[part[3].toUpperCase()].code << 8;
else
address = parseInt(part[3]);
this.dis_lines_points[i + head] = address;
this.dis_points_lines[address] = i + head;
break;
case ".EQU":
labels[part[1].toUpperCase()] = parseInt(part[3]);
this.dis_lines_points[i + head] = parseInt(part[3]);
break;
case ".DB":
if(part[5] && (part[5].charAt(0) == '"')) {
this.dis_lines_points[i + head] = address;
part[5].substr(1, part[5].length - 2)
.replace(/\\0/g, "\0")
.replace(/\\n/g, "\n")
.replace(/\\r/g, "\r")
.replace(/\\t/g, "\t")
.split("")
.forEach
(function(ascii) {
_this.dis_lines_tables[address] = true;
_this.dis_points_lines[address] = i + head;
_this.ram[address ++] = ascii.charCodeAt(0);
return 0;
});
} else {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(part[3]);
}
break;
case ".DW":
code = parseInt(part[3]);
this.dis_lines_points[i + head] = address;
this.dis_points_lines[address] = i + head;
this.dis_lines_tables[address] = true;
this.ram[address ++] = code.low;
this.dis_points_lines[address] = i + head;
this.dis_lines_tables[address] = true;
this.ram[address ++] = code.high;
break;
case ".CLR":
rcv = "X", trs = "X";
break;
case ".DEF":
if(!part[1])
this.dis_lines_points[i + head] = parseInt(part[4]) << 8,
defs[part[3].toUpperCase()] = { bytes: 0, code: parseInt(part[4])};
break;
case ".DEFB":
if(!part[1])
this.dis_lines_points[i + head] = parseInt(part[4]) << 8,
defs[part[3].toUpperCase()] = { bytes: 1, code: parseInt(part[4])};
break;
case ".DEFW":
if(!part[1])
this.dis_lines_points[i + head] = parseInt(part[4]) << 8,
defs[part[3].toUpperCase()] = { bytes: 2, code: parseInt(part[4])};
break;
case ".IMGUR":
hSprites.src = "https://i.imgur.com/" + part[3];
break;
}
}
part[3] = part[3].replace(/(\.*)([A-Z_a-z][A-Z_a-z.0-9]*)/g, function(str, nest, tag) {
var tmp;
if(label && labels.length) {
tmp = labels[label.slice(0, nest.length).concat(tag.split(".")).join(".").toUpperCase()];
if(isFinite(tmp))
return Number(tmp);
return nest + tag;
}
return str;
});
part[4] = part[4].replace(/(\.*)([A-Z_a-z][A-Z_a-z.0-9]*)/g, function(str, nest, tag) {
var tmp;
if(label && labels.length) {
tmp = labels[label.slice(0, nest.length).concat(tag.split(".")).join(".").toUpperCase()];
if(isFinite(tmp))
return Number(tmp);
return nest + tag;
}
return str;
});
command = (part[2] + " " + part[3] + (part[4] ? "," + part[4] : "")).toUpperCase();
if(part[2].toUpperCase() in defs) {
usr = defs[part[2].toUpperCase()];
switch(usr.bytes) {
case 0:
this.dis_points_lines[address] = i + head;
this.ram[address ++] = usr.code;
break;
case 1:
if(part[3].match(/[ABC]\d/i)) {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(part[3], 16);
this.dis_points_lines[address] = i + head;
this.ram[address ++] = usr.code;
this.dis_lines_tables[address] = true;
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(part[4]);
} else {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = usr.code;
this.dis_points_lines[address] = i + head;
this.dis_lines_tables[address] = true;
this.ram[address ++] = parseInt(part[3]);
}
rcv = "X", trs = "X";
regs.A = 0xFA;
regs.B = 0xFB;
regs.C = 0xFC;
regs.D = 0xFD;
break;
case 2:
if(part[3].match(/BC\d/i)) {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(part[3].charAt(0) + part[3].charAt(2), 16);
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(part[3].substr(1), 16);
this.dis_points_lines[address] = i + head;
this.ram[address ++] = usr.code;
this.dis_points_lines[address] = i + head;
this.dis_lines_tables[address] = true;
this.ram[address ++] = parseInt(part[4]).low;
this.dis_points_lines[address] = i + head;
this.dis_lines_tables[address] = true;
this.ram[address ++] = parseInt(part[4]).high;
} else {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = usr.code;
this.dis_points_lines[address] = i + head;
this.dis_lines_tables[address] = true;
this.ram[address ++] = parseInt(part[3]).low;
this.dis_points_lines[address] = i + head;
this.dis_lines_tables[address] = true;
this.ram[address ++] = parseInt(part[3]).high;
}
rcv = "X", trs = "X";
regs.A = 0xFA;
regs.B = 0xFB;
regs.C = 0xFC;
regs.D = 0xFD;
break;
}
}
if(command.match(/REG BC\d/)) {
code = parseInt(part[3].charAt(2), 16);
this.dis_points_lines[address] = i + head;
this.ram[address ++] = 0xB0 + code;
regs.B = 0xB0 + code;
this.dis_points_lines[address] = i + head;
this.ram[address ++] = 0xC0 + code;
regs.C = 0xC0 + code;
} else
if(command.match(/REG [ABCD]\d/)) {
code = parseInt(part[3], 16);
this.dis_points_lines[address] = i + head;
this.ram[address ++] = code;
regs[part[3].charAt(0).toUpperCase()] = code;
}
if(command.match(/ARG [ABCD]\d?,[ABCD]\d?/)) {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(part[3].charAt(0) + part[4].charAt(0), 16);
if(part[3].length == 2) {
code = parseInt(part[3], 16);
regs[part[3].charAt(0).toUpperCase()] = code;
this.dis_points_lines[address] = i + head;
this.ram[address ++] = code;
rcv = part[3].charAt(0).toUpperCase();
}
if(part[4].length == 2) {
code = parseInt(part[4], 16);
regs[part[4].charAt(0).toUpperCase()] = code;
this.dis_points_lines[address] = i + head;
this.ram[address ++] = code;
trs = part[4].charAt(0).toUpperCase();
}
}
if(tmp = command.match(/MOV \[BC(\d+)\+(\d+)\],([ABCD]\d)/)) {
vector = tmp[1].padEnd(tmp[2].length, tmp[1].charAt(0));
offset = parseInt(tmp[2]);
while(vector != "") {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(vector.charAt(0)) * 16 + Math.floor(offset / Math.pow(10, vector.length - 1)) % 10;
vector = vector.substr(1);
}
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(tmp[3], 16);
} else
if(tmp = command.match(/LEA BC(\d),\[BC(\d+)\+(\d+)\]/)) {
vector = tmp[2].padEnd(tmp[3].length, tmp[2].charAt(0));
offset = parseInt(tmp[3]);
while(vector != "") {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(vector.charAt(0)) * 16 + Math.floor(offset / Math.pow(10, vector.length - 1)) % 10;
vector = vector.substr(1);
}
this.dis_points_lines[address] = i + head;
this.ram[address ++] = 0xDD;
} else
if(command.match(/ADD BC\d\,BC\d/)) {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(part[3].charAt(2) + part[4].charAt(2), 16);
this.dis_points_lines[address] = i + head;
this.ram[address ++] = 0xDD;
} else
if(command.match(/SWP [ABC]\d,[ABC]\d/)) {
ri = parseInt(part[3].charAt(1));
ti = parseInt(part[4].charAt(1));
if(ri < ti) {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(part[3].charAt(1) + part[4].charAt(1), 16);
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(part[3].charAt(0) + part[4].charAt(0), 16);
} else {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(part[4].charAt(1) + part[3].charAt(1), 16);
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(part[4].charAt(0) + part[3].charAt(0), 16);
}
} else
if(command.match(/DIV BC\d/)) {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(part[3].charAt(2), 16) * 0x11;
this.dis_points_lines[address] = i + head;
this.ram[address ++] = 0xAA;
} else
if(command.match(/MIL BC\d/)) {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(part[3].charAt(2), 16) * 0x11;
this.dis_points_lines[address] = i + head;
this.ram[address ++] = 0xBB;
} else
if(command.match(/MUL BC\d/)) {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(part[3].charAt(2), 16) * 0x11;
this.dis_points_lines[address] = i + head;
this.ram[address ++] = 0xCC;
} else
if(tmp = command.match(/(ADD|ADC|SUB|SBB|AND|CON|OR|DIS|XOR|EOR|MOV|FOR) ([ABCD]\d?,)?\[BC(\d+)(\+\d+)?(\+[ABCD]\d)?\]/)) {
var cmd = tmp[1];
var dst = tmp[2];
vector = tmp[3].padEnd((tmp[4] || "+").length - 1, tmp[3].charAt(0));
offset = parseInt(tmp[4]) || 0;
var idx = parseInt(tmp[5], 16);
if(dst) {
if(rcv != dst.charAt(0) || (tmp[5] ? trs != tmp[5].charAt(1) : false)) {
rcv = dst.charAt(0);
if(idx)
trs = tmp[5].charAt(1);
if(trs == "X")
trs = rcv;
codes.unshift(parseInt(rcv + trs, 16));
}
code = parseInt(dst, 16);
if(code != regs[rcv] && code >= 0xA0) {
this.dis_points_lines[address] = i + head;
codes.unshift(code);
regs[rcv] = code;
}
while(codes.length > 0) {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = codes.shift();
}
}
while(vector != "") {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(vector.charAt(0)) * 16 + Math.floor(offset / Math.pow(10, vector.length - 1)) % 10;
vector = vector.substr(1);
}
this.dis_points_lines[address] = i + head;
this.ram[address ++] = +(tmp[5] ? +tmp[5].charAt(2) : 0) * 16 + alu[cmd];
} else
if(command.match(/(ADD|ADC|SUB|SBB|AND|CON|OR|DIS|XOR|EOR|MOV|FOR) ([ABCD]\d?,)?[ABCD]?\d/)) {
if(!part[4]) {
if(isFinite(part[3])) {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = +part[3] * 16 + alu[part[2]];
} else {
if(trs != part[3].charAt(0).toUpperCase()) {
trs = part[3].charAt(0).toUpperCase();
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(rcv + trs, 16);
}
this.dis_points_lines[address] = i + head;
this.ram[address ++] = +(part[3].charAt(1)) * 16 + alu[part[2].toUpperCase()];
}
} else {
if(rcv != part[3].charAt(0).toUpperCase() || trs != part[4].charAt(0).toUpperCase()) {
if(part[4].match(/^[ABCD]/)) {
rcv = part[3].charAt(0).toUpperCase();
trs = part[4].charAt(0).toUpperCase();
codes.unshift(parseInt(rcv + trs, 16));
}
}
if(parseInt(part[3], 16) != regs[part[3].charAt(0).toUpperCase()]) {
code = parseInt(part[3], 16);
if(code >= 0xA0) {
codes.unshift(code);
regs[part[3].charAt(0).toUpperCase()] = parseInt(part[3], 16);
}
}
while(codes.length > 0) {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = codes.shift();
}
this.dis_points_lines[address] = i + head;
this.ram[address ++] = (+(part[4].charAt(1)) || 0) * 16 + alu[part[2].toUpperCase()];
}
}
if(command.match(/^(CMA|CMB|CMC|CMD|AF|BF|CF|DF)/)) {
if(part[2].length == 2) {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(part[2], 16);
} else {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(part[2].charAt(2) + "F", 16);
}
}
if(command.match(/(JAE|JBE|JCE|JDE) BC\d\+\d/)) {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(part[3].charAt(2) + part[3].charAt(4), 16);
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(part[2].charAt(1) + "E", 16);
} else
if(command.match(/(JAF|JBF|JCF|JND|JDF) BC\d\+\d/)) {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(part[3].charAt(2) + part[3].charAt(4), 16);
if(part[2].charAt(1) == "N") {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(part[2].charAt(2) + "F", 16);
} else {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(part[2].charAt(1) + "F", 16);
}
} else
if(command.match(/(JAE|JBE|JCE|JDE)/)) {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = parseInt(part[2].charAt(1) + "E", 16);
}
if(command.match(/(INT) ([0-9A-F]{2}){1,2}/)) {
code = parseInt(part[3], 16);
if(part[3].length > 2) {
if(code.low) {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = code.low;
}
this.dis_points_lines[address] = i + head;
this.ram[address ++] = code.high;
} else {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = code.low;
}
rcv = "X", trs = "X";
regs.A = 0xFA;
regs.B = 0xFB;
regs.C = 0xFC;
regs.D = 0xFD;
}
if(command.match(/HLT|HALT/)) {
this.dis_points_lines[address] = i + head;
this.ram[address ++] = 0x00;
}
}
} while(review ++ < 2);
}
//////////////////////////////////////////////////////////////////////
i = (function() {
var arr = new Array(256, 0x00);
var i;
arr._this = this.ram;
for(i = 0xD0; i <= 0xD8; ++ i) {
Object.defineProperty(arr, i,
{
set() {
this._this.devices[this[0xD9]](this._this.ram[0xD900], i & 15, data);
}
}
);
}
return arr;
})();
//////////////////////////////////////////////////////////////////////
var dis
= {
addr :0,
read
:function(address) {
var ic; // Instruction Code
var id; // Instruction Data (Prefix)
var is_id;
//
do {
ic = this.ram[address ++];
is_id = (ic.left.isNum && ic.right.isNum && ic > 0);
if(is_id && this.classic)
return true;
id = (id << 8) | ic;
} while(is_id);
}
};
//////////////////////////////////////////////////////////////////////
this.disassm
= function(pc) {
var i;
var address, offset;
var ic; // Instruction Code
var id = 0; // Instruction Data / Prefix
var ip = isFinite(pc) ? pc : this.ctx.pointer(0x00BE);
var ctx = this.ctx;//this.ram.slice(this.base, 256);
var pointer;
var pointers;
var bytes;
var rows;
var text;
var color;
var active = 0;
var prefix, receiver, args;
var vector, source;
var arg = ctx[0x00AE];
var reg = {
0x0F: "??",
0xAF: ctx[0x00AF] ? ctx[0x00AF].toHex(2) : "A?",
0xBF: ctx[0x00BF] ? ctx[0x00BF].toHex(2) : "B?",
0xCF: ctx[0x00CF] ? ctx[0x00CF].toHex(2) : "C?",
0xDF: ctx[0x00DF] ? ctx[0x00DF].toHex(2) : "D?"
};
//
if(this.dis_address > ip)
this.dis_address = ip;
do {
pointer = this.dis_address;
this.dis_pointer = pointer;
rows = [];
pointers = [];
i = this.dis_lines;
id = 0;
do {
bytes = [];
text = "";
if(!this.classic)
id = 0;
pointers.push(pointer);
if(!id) {
vector = "";
offset = 0;
address = pointer;
}
while(this.dis_lines_tables[pointer]) {
bytes.push(this.ram[pointer].toHex(2));
pointer ++;
text = "...DATA...";
color = "Data";
}
if(text == "")
do {
if(pointer == ip)
active = this.dis_lines - i;
ic = this.ram[pointer ++];
if(!isFinite(ic))
ic = 0;
bytes.push(ic.toHex(2));
if(this.classic && ic.left.isNum && ic.right.isNum && ic != 0x00) {
vector = "PTR";
offset = 0;
id = ic;
text = "PTR " + "BC" + ic.left + "," + ic.right;
color = "CPU_Group_C";
bytes.push("++");
} else
if(this.classic) {
if(id > 0)
bytes.unshift("++");
} else
while(ic.left.isNum && ic.right.isNum && ic != 0x00) {
if(id == 0) {
id = ic;
vector = this.ctx.pointer(id.left, "");
} else
if(id.left != ic.left)
vector += "" + ic.left;
offset = offset * 10 + ic.right;
if(pointer == ip)
active = this.dis_lines - i;
ic = this.ram[pointer ++];
if(!isFinite(ic))
ic = 0;
bytes.push(ic.toHex(2));
}
if(id > 0 && !this.classic)
vector += "+" + offset;
if(ic == 0x00) {
if(id == 0)
text = "HLT",
color = "CPU_Group_C";
else
text = "RET " + vector,
color = "CPU_Group_C";
}
if(ic.left.isNum && ic.right >= 0xA) {
if(ctx[0x00F9].left == 0xD) {
if(arg > 0)
source = ((arg.right << 4) | ic.left).toHex(2);
else
source = "?" + ic.left.toHex(1);
if(id > 0)
text = "MOV " + reg[arg | 0x0F] + ",[" + vector + "+" + source + "]",
color = "CPU_Group_F";
else
if(ic.right == 0xF)
text = "MOV " + reg[arg | 0x0F] + "," + source,
color = "CPU_Group_F";
else
text = "??? " + reg[arg | 0x0F] + "," + source,
color = "CPU_Group_X";
} else
if(ctx[0x00F9].right == 0xD && id > 0) {
} else {
text = "ADD SUB AND OR EOR MOV".split(/\s+/)[ic.right - 0xA];
text += " " + reg[arg | 0x0F] + ",";
if(arg > 0)
source = ((arg.right << 4) | ic.left).toHex(2);
else
source = "?" + ic.left.toHex(1);
if(id > 0) {
if(ic.left > 0)
text += "[" + vector + "+" + source + "]";
else
text += "[" + vector + "]";
} else
text += source;
color = "CPU_Group_A";
}
}
if(ic.left.isReg && ic.right.isNum) {
if(id > 0)
text = "MOV [" + vector + "]," + ic.toHex(2),
color = "CPU_Group_F";
else {
if(this.classic)
text = "REG " + ic.toHex(2),
color = "CPU_Group_E",
bytes.push("--", "--", "--");
reg[ic | 0x0F] = ic.toHex(2);
}
}
if(ic.left.isReg && ic.right.isReg) {
if(id > 0) {
if(ic == 0xDD) {
text = "ADD " + this.ctx.pointer(id.left, ",") + this.ctx.pointer(id.right, ""),
color = "CPU_Group_D";
} else {
if(ic.left == ic.right && id.left == id.right) {
switch(ic) {
case 0xAA:
text = "DIV " + this.ctx.pointer(id.left, `,A${id.right}`);
break;
case 0xBB:
text = "MIL " + this.ctx.pointer(id.left, `,B${id.right},C${id.right}`);
break;
case 0xCC:
text = "MUL " + this.ctx.pointer(id.left, `,B${id.right},C${id.right}`);
break;
}
} else {
if(id.left < id.right)
text = "SWP " + ((ic.left << 4) | id.left).toHex(2) +"," + ((ic.right << 4) | id.right).toHex(2);
else
text = "??? " + ((ic.left << 4) | id.left).toHex(2) +"," + ((ic.right << 4) | id.right).toHex(2),
color = "CPU_Group_X";
}
}
} else {
if(ic == 0xDD)
text = "DBG";
else {
if(this.classic)
text = "ARG " + ic.left.toHex(1) + "," + ic.right.toHex(1),
color = "CPU_Group_E",
bytes.push("--", "--");
arg = ic;
}
}
}
if(ic.left.isReg && ic.right == 0xE) {
if(id > 0) {
text = "JCE JDE JAE JBE".split(/\s+/)[ic.left & 3] + " " + vector;
} else
text = "JCE JDE JAE JBE".split(/\s+/)[ic.left & 3];
color = "CPU_Group_B";
}
if(ic.left.isReg && ic.right == 0xF) {
if(id > 0) {
text = "JCF JDF JAF JBF".split(/\s+/)[ic.left & 3] + " " + vector;
} else
text = "CMC CMD CMA CMB".split(/\s+/)[ic.left & 3];
color = "CPU_Group_B";
}
if(ic >= 0xE0) {
text = "INT " + ((ic << 8) | id).toHex(-4);
color = "CPU_Group_C";
}
} while(text == "");
if(this.classic && !text.match(/^PTR/))
id = 0;
while(bytes.length < 4)
bytes.unshift("--");
if(bytes.length > 4) {
do {
tmp = address.toHex(4) + " " + bytes.slice(0, 4).join(" ") + "|" + text;
if(bytes.length <= 4 && tmp.length <= 34) {
rows.push(address.toHex(4) + " " + bytes.slice(0, 4).join(" ") + "|" + `<span class=${color}>` + text.padEnd(20, " ") + "</span>");
text = "";
} else
if(bytes.length == 5)
rows.push((address.toHex(4) + " " + bytes.join(" ")).padEnd(20, " ")),
tmp = "";
else
rows.push((address.toHex(4) + " " + bytes.slice(0, 4).join(" ") + (bytes.length > 4 ? "+" : "")).padEnd(20, " ")),
tmp = "";
address += 4;
bytes.splice(0, 4);
} while(bytes.length > 1 && -- i > 0);
if(text != "" && -- i > 0)
rows.push(`${address.toHex(4)}${tmp}|<span class=${color}>${text.padEnd(20, " ").substr(0, 28)}` + "</span>");
} else
rows.push(address.toHex(4) + " " + bytes.join(" ") + `|<span class=${color}>` + text.padEnd(20, " ") + "</span>");
} while(-- i > 0);
if(pointer - 1 < ip) {
this.dis_address = ip;
if(pointers.length >= this.dis_lines)
this.dis_address = pointers[pointers.length - (this.dis_lines - 1)];
continue;
}
} while(false);
return {
active :active,
content :rows.join("\r\n").replace(/^(.+)$/gm, "<span>$1</span>")
}
}
//////////////////////////////////////////////////////////////////////
this.init
= function() {
this.render.noise = 25;
}
//////////////////////////////////////////////////////////////////////
this.reset
= function() {
this.ctx.pointer(0x00B0, 0x0000);
this.ctx.pointer(0x00BE, 0x0000);
this.ticks = 0;
for(var dev in this.devices) {
try {
this.devices[dev]._this = this;
this.devices[dev].init();
} catch(e) {
console.log(e);
}
}
}
//////////////////////////////////////////////////////////////////////
this.step
= function() {
const apr = this.ram.base | 0x00F0;
var address;
var x, y, z;
var flags;
var hollow; // Hollow command
var ic; // Instruction Code
var ip = this.ctx.pointer(0x00BE);
//
do {
if(!this.classic || !this.prefix)
this.id = 0,
this.offset = 0,
this.prefixes = 0,
this.address = this.ctx.pointer(0x00B0);
hollow = false;
this.ticks ++;
ic = this.ram[ip];
while(ic.left.isNum && ic.right.isNum && ic != 0x00) {
// 01..99
this.offset += this.offset << 2;
this.offset <<= 1;
this.offset += ic.right;
if(ic.left != this.id.left || this.prefixes == 0)
this.address = (this.prefixes > 0 ? this.address : 0) + this.ctx.pointer(0x00B0 | ic.left);
if(this.prefixes == 0)
this.id = ic;
ic = this.ram[++ ip];
++ this.prefixes;
this.prefix = true;
if(this.classic) {
this.ctx.pointer(0x00BE, ip);
return true;
}
}
this.prefix = false;
this.address += this.offset;
this.address &= 0xFFFF;
if(ic == 0x00) {
if(this.prefixes == 0) {
// HLT
this.ctx.pointer(0x00B0, ip);
this.ctx.pointer(0x00BE, 0x0000);
} else {
// HLT vector
this.ctx.pointer(0x00B0, ip);
this.ctx.pointer(0x00BE, this.address);
}
return false;
}
if(ic.left.isNum && ic.right >= 0xA) {
// ALU Rn,Ti
//y = this.ram.translator();
if(this.prefixes > 0 && ic.left == 0)
y = 0;
else
y = this.ctx[(this.ctx[0xAE].right << 4) | ic.left];
x = this.ctx.receiver();
if(this.prefixes > 0) {
// ALU Rn,[BCi+k+Tm]
y = this.ram[this.address + y];
}
switch(ic.right) {
case 0xA: // ADD / ADC
x += y + ((this.ctx.state() >> 1) & 1);
this.ctx.state(x);
break;
case 0xB: // SUB / SBB
x -= y + ((this.ctx.state() >> 1) & 1);
this.ctx.state(x);
break;
case 0xC: // CON / AND
x &= y;
this.ctx.state(x);
break;
case 0xD: // DIS / OR
x |= y;
this.ctx.state(x);
break;
case 0xE: // EOR / XOR
x ^= y;
this.ctx.state(x);
break;
case 0xF: // FOR / MOV
x = y;
break;
}
this.ctx.receiver(x);
ip ++;
}
if(ic.left.isReg && ic.right.isNum) {
if(this.prefixes > 0) {
// MOV [BCi+k],Rn
this.ram[this.address] = this.ctx[ic];
} else {
// REG Rn
this.ctx[ic | 0x0F] = ic;
hollow = !this.classic;
}
ip ++;
}
if(ic.left.isReg && ic.right.isReg) {
if(this.prefixes > 1) {
// Has few prefixes
return false;
} else
if(this.prefixes == 1) {
// Has one prefix
if(ic == 0xDD) {
// ADD BCi,BCk
x = this.ctx.pointer(0x00B0 | this.id.left);
y = this.ctx.pointer(0x00B0 | this.id.right);
this.address = x + y + ((this.ctx[0x00A0] >> 1) & 1);
this.ctx.pointer(0x00B0 | this.id.left, this.address);
this.ctx[0xA0] = (this.ctx[0xA0] & 0xFD) | ((this.address >> 15) & 2);
} else {
if(ic.left == ic.right && this.id.left == this.id.right) {
switch(ic) {
case 0xAA:
// DIV BCi,Ak
x = this.ctx.pointer(0x00B0 | this.id.left);
y = this.ctx[0x0A0 | this.id.left];
if(y == 0)
y = 256;
this.ctx.pointer(0x00B0 | this.id.left, Math.floor(x / y));
this.ctx[0x0A0 | this.id.left] = (x % y).low;
break;
case 0xBB:
x = this.ctx.pointer(0x00B0 | this.id.left);
y = x.high;
x = x.low;
y -= (y & 128) << 1;
x -= (x & 128) << 1;
x *= y;
this.ctx.pointer(0x00B0 | this.id.left, x >= 0 ? x : x + 65536);
break;
case 0xCC:
x = this.ctx.pointer(0x00B0 | this.id.left);
y = x.high;
x = x.low;
this.ctx.pointer(0x00B0 | this.id.left, x * y);
break;
}
} else {
if(this.id.left < this.id.right) {
// SWP Ri,Tk
x = this.ctx[(ic & 0xF0) | this.id.left];
y = this.ctx[(ic.right << 4) | this.id.right];
this.ctx[(ic & 0xF0) | this.id.left] = y;
this.ctx[(ic.right << 4) | this.id.right] = x;
} else {
// ??? Ri,Tk
return false
}
}
}
ip ++;
} else {
if(ic == 0xDD) {
// DBG
this.ctx.pointer(0x00B0, ip);
ip = (ic << 8);
this.dis_address = ip;
} else {
// ARG R,T
this.ctx[0xAE] = ic;
ip ++;
hollow = !this.classic;
}
}
}
if(ic.left.isReg && ic.right == 0xE) {
if(this.ctx.state() & [2, 1, 8, 4][ic.left & 3]) {
// AE / BE / CE / DE / JA / JB / JC / JD
this.ctx.pointer(0x00B0, ip);
ip = this.address;
this.dis_address = ip;
} else
ip ++;
}
if(ic.left.isReg && ic.right == 0xF) {
if(this.prefixes > 0) {
if(!(this.ctx.state() & [2, 1, 8, 4][ic.left & 3])) {
// JAF / JBF / JCF / JDF / JNA / JNB / JNC / JND
this.ctx.pointer(0x00B0, ip);
ip = this.address;
this.dis_address = ip;
} else
ip ++;
} else {
// AF / BF / CF / DF | CMA / CMB / CMC / CMD
this.ctx[0xA0] ^= [2, 1, 8, 4][ic.left & 3];
ip ++;
}
}
if(ic >= 0xE0) {
// E000..FF99 | CALL 0xE000..0xFF99
this.ctx.pointer(this.process | 0x00B0, ip);
ip = (ic << 8) | this.id;
this.dis_address = ip;
}
this.ctx.pointer(0x00BE, ip);
} while(hollow);
return true;
}
}
var cpu = new KOY();
var hDebugger;
var hDisplay;
var hScreen;
var hClock;
var hGraphic;
var hUserPad;
var hEmuDump;
var hEmuDis;
var hEmuCtx;
var hEmuLog;
var hUserLines;
var hUserDump;
var hFiles;
var hImageFiles;
function showState(addr, is_edit) {
var ip = isFinite(addr) ? addr : cpu.ctx.pointer(0x00BE);
var ic;
i = 0;
do {
ic = cpu.ram[ip + i ++];
} while(ic > 0 && ic.left.isNum && ic.right.isNum);
hEmuDump.innerHTML = cpu.ram.toDump(ip, i).join("\r\n");
tmp = cpu.disassm(ip + 1);//.join("\r\n");
tmp = cpu.disassm(ip);//.join("\r\n");
if(!isFinite(addr) && isFinite(cpu.dis_points_lines[cpu.dis_pointer]) && !is_edit) {
hUserDump.scrollTop = cpu.dis_points_lines[cpu.dis_pointer] * (hUserDump.scrollHeight / (hUserDump.value.split(/\r?\n/).length));
var line = cpu.dis_points_lines[ip];
var lines = hUserDump.value.split(/\r?\n/);
hUserDump.focus();
hUserDump.selectionStart = lines.slice(0, line).join().length + 1;
hUserDump.selectionEnd = lines.slice(0, line + 1).join().length;
}
hEmuDis.innerHTML = tmp.content;
hEmuDis.className = "line" + tmp.active;
hEmuCtx.textContent = cpu.context();
}
HTMLTextAreaElement.prototype.insertAtCursor
= function(szChar) {
//IE support
if(document.selection) {
this.focus();
sel = document.selection.createRange();
sel.text = szChar;
} else
//MOZILLA and others
if(this.selectionStart || this.selectionStart == '0') {
var startPos = this.selectionStart;
var endPos = this.selectionEnd;
this.value = this.value.substring(0, startPos)
+ szChar
+ this.value.substring(endPos, this.value.length);
this.selectionStart = startPos + szChar.length;
this.selectionEnd = this.selectionStart;
} else
this.value += szChar;
}
function onUserDump_RefreshState(e) {
window.localStorage.listingSelectAt = e.srcElement.selectionStart;
window.localStorage.listingSelectTo = e.srcElement.selectionEnd;
showState(cpu.dis_lines_points[e.srcElement.value.substr(0, e.srcElement.selectionStart).split(/\r?\n/).length - 1], true);
var row = e.srcElement.value.substr(0, e.srcElement.selectionStart).split(/\r?\n/).length;
var col = e.srcElement.value.substr(0, e.srcElement.selectionEnd).split(/\r?\n/).pop().length + 1;
hUserLines.scrollTop = e.srcElement.scrollTop;
hEmuLog.textContent = `${row}:${col}`;
return true;
}
function onUserDump_Input(e) {
var text = ((e && e.srcElement) || hUserDump).value;
var dump = text.split(".assm")[0];
var assm = text.split(".assm")[1];
var n = text.match(/\r?\n/g).length;
var rows = [];
if(e)
window.localStorage.listing = ((e && e.srcElement) || hUserDump).srcElement.value;
clearTimeout(hRefresh);
cpu.assembly(assm, dump.split(/\r?\n/).length - 1);
if(n != hUserLines.value.split(/\r?\n/).length) {
for(var i = 1; i <= n; ++ i)
rows.push(i);
hUserLines.textContent = rows.join("\r\n");
hUserLines.rows = hUserDump.rows;
}
hUserLines.scrollTop = ((e && e.srcElement) || hUserDump).scrollTop;
}
var pVideo;
function check_status(resp) {
if(!resp.ok) {
throw new Error(`HTTP ${resp.status} - ${resp.statusText}`);
}
return resp;
}
//
function load_gist(el, id) {
var f;
if(id)
f = fetch(linkTo.gist.host + id, {redirect: "follow"});
else
f = fetch(linkTo.gist.host + linkTo.gist.list, {redirect: "follow"});
console.log(`Load Gist...`);
f
.then(response =>
check_status(response)
)
.then(response =>
{
console.log(response);
return response.arrayBuffer()
}
)
.then((function(buffer)
{
var text = String.fromCharCode.apply(null, new Uint8Array(buffer));
var index = 1;
if(this.is_list) {
text.split(/\r?\n/)
.forEach(
function(items) {
var item = items.split(/\t+/);
var li = document.createElement("li");
li.addEventListener("click",
(function(e) {
load_gist(null, this.url);
}).bind({
url :item[0]
})
);
li.textContent = index ++ + ". " + item[1];
li.className = "File";
hFiles.appendChild(li);
}
);
} else {
hUserDump.value = text;
onUserDump_Input();
cpu.ram.set(0).set(hUserDump.value.split(".assm")[0]);
cpu.assembly(hUserDump.value.split(".assm")[1], hUserDump.value.split(".assm")[0].split(/\r?\n/).length - 1);
cpu.reset();
showState();
}
})
.bind({
el :el,
is_list :id ? false : true
})
)
.catch(error =>
console.log(error)
);
}
//
function load_assembly(url) {
//url = "https://pastebin.com/raw/AqHSN1B9";
fetch(url, {
redirect: "follow"
})
.then(response =>
check_status(response)
)
.then(response =>
{
console.log(response);
return response.arrayBuffer()
}
)
.then(buffer =>
{
hUserDump.value = String.fromCharCode.apply(null, new Uint8Array( buffer));
onUserDump_Input();
cpu.ram.set(0).set(hUserDump.value.split(".assm")[0]);
cpu.assembly(hUserDump.value.split(".assm")[1], hUserDump.value.split(".assm")[0].split(/\r?\n/).length - 1);
cpu.reset();
showState();
}
)
.catch(error =>
console.log(error)
);
}
//////////////////////////////////////////////////
function cycle() {
setTimeout(cycle, 1000.0 / cpu.cps);
if((hUserPad.value.indexOf("$run ") > 0 || hUserPad.value.indexOf("$go") > 0)
&& (hUserPad.selectionEnd == hUserPad.selectionStart + 1)
&& (hUserPad.selectionEnd > hUserPad.value.replace(";$go ", ";$run ").indexOf("$run "))) {
while(cpu.step()) {}
showState(cpu.ctx.pointer(0x00B0));
showState(cpu.ctx.pointer(0x00B0));
if(hUserPad.value.indexOf(";$run") < 0)
hDebugger.style.display = "block";
} else
hDebugger.style.display = "block";
}
//////////////////////////////////////////////////////////
function do_run(speed, let_go) {
var tmp = hUserPad.value.replace(";$go ", ";$run ");
if(tmp.indexOf(";$run ") < 0) {
if(tmp.indexOf(";") < 0) {
tmp += ";$run Test for keyboard stroke...";
} else {
tmp = tmp.replace(";", ";$run ");
}
hUserPad.value = tmp;
}
if(let_go)
hDebugger.style.display = "block",
hUserPad.value = hUserPad.value.replace(";$run ", ";$go ");
else
hDebugger.style.display = "none",
hUserPad.value = hUserPad.value.replace(";$go ", ";$run ");
if(hUserPad.selectionEnd < tmp.indexOf("$run ")
|| hUserPad.selectionEnd >= hUserPad.value.length) {
hUserPad.selectionStart = tmp.indexOf("$run ") + 5;
}
hUserPad.selectionEnd = hUserPad.selectionStart + 1;
hUserPad.focus();
cpu.cps = speed;
}
//////////////////////////////////////////////////////////
function do_console(e) {
var text = hUserPad.value;
var pos = text.indexOf(";");
var assm;
if(pos > 0 && hUserPad.selectionStart < pos) {
assm = hUserPad.value.split(" ")[0];
if(assm.length == 4) {
cpu.assembly([
"\t.ORG\t0x" + assm,
hUserPad.value.substr(assm.length),
".DW\t0x0000",
".DW\t0x0000",
".DW\t0x0000"
].join("\r\n\t"));
showState(parseInt(assm, 16));
showState(parseInt(assm, 16));
}
} else {
if(tmp = text.match(/([0-9A-F]{2}):([0-9A-F]{2})/i)) {
cpu.ctx[parseInt(tmp[1], 16)] = parseInt(tmp[2], 16);
showState();
showState();
} else
if(tmp = text.match(/([0-9A-F]{2})([0-9A-F]{2})([0-9A-F])#([0-9A-F]{2})/i)) {
cpu.ctx[0xD9] = parseInt(tmp[1], 16);
cpu.ctx[0xD8] = parseInt(tmp[2], 16);
cpu.ctx[0xD0 + parseInt(tmp[3], 16)] = parseInt(tmp[4], 16);
showState();
showState();
}
}
}
//////////////////////////////////////////////////////////
function main() {
hFiles = document.getElementById("Files");
hDebugger = document.getElementById("Debugger");
hUserPad = document.getElementById("UserPad");
hSprites = document.getElementById("Sprites");
hDisplay = document.getElementById("Display").getContext("2d");
hScreen = document.getElementById("Screen").getContext("2d");
hGraphic = hScreen.getImageData(0, 0, hScreen.canvas.width, hScreen.canvas.height);
hUserLines = document.getElementById("UserLines");
hImageFiles = document.getElementById("ImageFiles");
hEmuDump = document.getElementById("EmuDump");
hEmuDis = document.getElementById("EmuDis");
hEmuCtx = document.getElementById("EmuCtx");
hUserDump = document.getElementById("UserDump");
hEmuLog = document.getElementById("EmuLog");
hCaption = document.getElementById("Caption");
document.body.style.paddingTop = hCaption.offsetHeight;
document.body.style.visibility = "visible";
//
hImageFiles.addEventListener("change", function(e) {
if(e.srcElement.files[0]) {
var reader = new FileReader();
reader.onload = function() {
var dataUrl = reader.result;
var base64 = dataUrl.split(',')[1];
hSprites.style.height = "";
hSprites.style.width = "";
hSprites.src = dataUrl;
};
reader.readAsDataURL(e.srcElement.files[0]);
e.srcElement.style.display = "none";
}
}
);
hUserPad.addEventListener("focus", function(e) {
/* if(hClock)
clearInterval(hClock);
hClock = setInterval("while(cpu.step()) {} showState();", 1000 / cpu.cps);
hDebugger.style.display = "none";*/
}
);
hUserPad.addEventListener("blur", function(e) {
/* if(hClock)
clearInterval(hClock);
hClock = null;
hDebugger.style.display = "block";*/
}
);
hSprites.src = hSprites.src;
hSprites.addEventListener("load",
function(e) {
e.srcElement.style.display = "inline";
pVideo = cpu.prepareImage(e.srcElement);
setInterval("cpu.render(hScreen, pVideo)" , 40);
}
);
hUserDump.addEventListener("input", onUserDump_Input);
hUserDump.addEventListener("scroll",
function(e) {
var el = e.srcElement;
hUserLines.scrollTop = el.scrollTop;
}
);
hUserDump.addEventListener("mousedown", onUserDump_RefreshState);
hUserDump.addEventListener("keyup", onUserDump_RefreshState);
hUserDump.addEventListener("keydown",
function(e) {
var keyCode = e.keyCode || e.which;
if(keyCode === 0x09) {
document.execCommand('insertText', false, '\t'.repeat(1));
e.preventDefault();
}
onUserDump_RefreshState(e);
}
);
hUserPad.addEventListener("keydown",
function(e) {
var keyCode = e.keyCode || e.which;
if(keyCode === 0x09) {
document.execCommand('insertText', false, '\t'.repeat(1));
e.preventDefault();
} else
if(keyCode == 0x0D) {
cpu.step();
showState(undefined, true);
}
}
);
hUserPad.addEventListener("input", do_console);
cpu.init();
if(window.location.href.match(/classic/i))
cpu.classic = true;
if(tmp = window.location.href.match(/gist=(http.*\.asm)/))
load_assembly(tmp[1]);
else
if(window.localStorage.listing && window.localStorage.listing.indexOf("20210709") > 0) {
var listing = window.localStorage.listing;
if(listing.indexOf("20210709") > 0) {
onUserDump_Input();
hUserDump.value = listing;
hUserDump.selectionStart = window.localStorage.listingSelectAt;
hUserDump.selectionEnd = window.localStorage.listingSelectTo;
hUserDump.focus();
hUserLines.scrollTop = hUserDump.scrollTop;
}
} else
if(!window.location.href.match(/debug/))
load_assembly("https://gist.githubusercontent.com/Alikberov/0a9ed1f8bca71b6e0bc957485497311b/raw/koy-mac-slalom.asm");
load_gist(hFiles);
cpu.ram.set(0).set(hUserDump.value.split(".assm")[0]);
cpu.assembly(hUserDump.value.split(".assm")[1], hUserDump.value.split(".assm")[0].split(/\r?\n/).length - 1);
cpu.reset();
showState();
onUserDump_Input();
tmp = window.location.href.match(/autorun-?(\d*)(\.)?/</