Last active
July 8, 2021 21:27
-
-
Save Alikberov/cff9df3bc1901d33f5c055682721240a to your computer and use it in GitHub Desktop.
Koyaanisqatsi Machine Emulator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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*)(\.)?/i) | |
if(tmp) { | |
do_run(tmp[1] || 1, tmp[2]); | |
clearTimeout(hRefresh); | |
} | |
cycle(); | |
} | |
setTimeout(main, 100); | |
</script> | |
<style> | |
body | |
{ | |
visibility :hidden; | |
background-color:silver; | |
padding-bottom :0px; | |
margin-bottom :0px; | |
padding-right :0px; | |
margin-right :0px; | |
padding-left :0px; | |
margin-left :0px; | |
padding-top :0px; | |
margin-top :0px; | |
} | |
hr | |
{ | |
padding-bottom :0px; | |
margin-bottom :0px; | |
padding-right :0px; | |
margin-right :0px; | |
padding-left :0px; | |
margin-left :0px; | |
padding-top :0px; | |
margin-top :0px; | |
} | |
input#UserPad | |
{ | |
width :100%; | |
} | |
textarea#UserLines { | |
border-bottom :thin ButtonFace inset; | |
border-left :thin ButtonFace inset; | |
border-top :thin ButtonFace inset; | |
border-right :thin green dashed; | |
display :inline-block; | |
font-family :Courier New; | |
font-size :12px; | |
white-space :pre; | |
font-weight :normal; | |
margin :0px 0px 0px 0px; | |
padding-top :0px; | |
padding-right :0px; | |
color :green; | |
background-color:lightgreen; | |
overflow :hidden; | |
resize :none; | |
text-align :right; | |
} | |
pre#EmuLog | |
{ | |
background-color:Menu; | |
color :MenuText; | |
border :thin ButtonFace inset; | |
display :inline-block; | |
font-family :Courier New; | |
font-size :12px; | |
margin :0px 0px 0px 0px; | |
resize :none; | |
padding :0px 0px 0px 0px; | |
display:inline-block; | |
position:fixed; | |
right:0%; | |
text-align:right; | |
} | |
textarea#UserDump | |
{ | |
background-color:lightgreen; | |
color :green; | |
border-left :none; | |
border-top :thin ButtonFace inset; | |
border-right :thin ButtonFace inset; | |
border-bottom :thin ButtonFace inset; | |
display :inline-block; | |
font-family :Courier New; | |
font-size :12px; | |
margin-left :0px; | |
resize :none; | |
padding :0px 0px 0px 0px; | |
} | |
textarea#UserDump:focus, | |
textarea#UserLines:focus | |
{ | |
outline :none; | |
} | |
pre#EmuDis, pre#EmuDump, | |
pre#EmuCtx | |
{ | |
border :thin ButtonFace inset; | |
display :block; | |
font-family :Courier New; | |
font-size :12px; | |
} | |
span.ActiveByte | |
{ | |
color :white; | |
font-weight :bolder; | |
} | |
pre#EmuDis { | |
background-color:darkgreen; | |
color :lightgreen; | |
font-family :Courier New; | |
font-size :12px; | |
} | |
pre#EmuDis.line0 > span:nth-child(1), | |
pre#EmuDis.line1 > span:nth-child(2), | |
pre#EmuDis.line2 > span:nth-child(3), | |
pre#EmuDis.line3 > span:nth-child(4), | |
pre#EmuDis.line4 > span:nth-child(5), | |
pre#EmuDis.line5 > span:nth-child(6), | |
pre#EmuDis.line6 > span:nth-child(7), | |
pre#EmuDis.line7 > span:nth-child(8), | |
pre#EmuDis.line8 > span:nth-child(9), | |
pre#EmuDis.line9 > span:nth-child(10), | |
pre#EmuDis.line10 > span:nth-child(11), | |
pre#EmuDis.line11 > span:nth-child(12), | |
pre#EmuDis.line12 > span:nth-child(13), | |
pre#EmuDis.line13 > span:nth-child(14), | |
pre#EmuDis.line14 > span:nth-child(15), | |
pre#EmuDis.line15 > span:nth-child(16) { | |
background-color:green; | |
} | |
tr | |
{ | |
border-top :none; | |
padding-top :0px; | |
margin-top :0px; | |
} | |
table#Caption | |
{ | |
border :none; | |
z-index :1000; | |
} | |
caption-bar | |
{ | |
background-color:red;/*Menu;*/ | |
color :MenuText; | |
display :block; | |
} | |
label | |
{ | |
background-color:Menu; | |
color :MenuText; | |
display :block; | |
} | |
label input#CaptionMenu | |
{ | |
display :none; | |
} | |
label input#CaptionMenu ~ menu li menu ul::before | |
{ | |
color :HighlightText; | |
left :-1em; | |
position :absolute; | |
content :'►' | |
} | |
label input#CaptionMenu ~ menu | |
{ | |
background-color:inherit; | |
color :inherit; | |
border :none; | |
padding :1px 9px 1px 9px; | |
margin :0 0 0 0; | |
display :inline-block; | |
} | |
label input#CaptionMenu ~ menu:hover | |
{ | |
border :1px outset; | |
padding :0px 8px 0px 8px; | |
} | |
label input#CaptionMenu:checked ~ menu:hover | |
{ | |
border :1px inset; | |
padding :0px 8px 0px 8px; | |
} | |
label input#CaptionMenu:checked ~ menu:active | |
{ | |
border :1px inset; | |
} | |
label input#CaptionMenu ~ menu ol, | |
label input#CaptionMenu ~ menu ul | |
{ | |
background-color:inherit; | |
color :inherit; | |
position :absolute; | |
border :1px outset; | |
display :none; | |
list-style-type :none; | |
list-style-type :none; | |
list-style-position :outside; | |
} | |
label input#CaptionMenu ~ li | |
{ | |
background-color:inherit; | |
color :inherit; | |
} | |
label input#CaptionMenu:checked ~ menu:hover ol, | |
label input#CaptionMenu:checked ~ menu:hover ul | |
{ | |
background-color:inherit; | |
color :inherit; | |
display :block; | |
padding :0px 0px 0px 0px; | |
margin :0px 0px 0px 0px; | |
} | |
label input#CaptionMenu:checked ~ menu:hover li:hover | |
{ | |
background-color:Highlight; | |
color :HighlightText; | |
width :100%; | |
} | |
label input#CaptionMenu:checked ~ menu:hover li menu | |
{ | |
padding :0px 0px 0px 0px; | |
margin :0px 0px 0px 0px; | |
width :100%; | |
} | |
label input#CaptionMenu:checked ~ menu:hover li menu ol, | |
label input#CaptionMenu:checked ~ menu:hover li menu ul | |
{ | |
left :100%; | |
background-color:Menu; | |
color :MenuText; | |
display :none; | |
width :auto; | |
} | |
label input#CaptionMenu:checked ~ menu:hover li:hover menu ol, | |
label input#CaptionMenu:checked ~ menu:hover li:hover menu ul | |
{ | |
background-color:Menu; | |
color :MenuText; | |
display :inline-block; | |
} | |
label input#CaptionMenu:checked ~ menu:hover li:hover menu ol li:hover, | |
label input#CaptionMenu:checked ~ menu:hover li:hover menu ul li:hover | |
{ | |
background-color:Highlight; | |
color :HighlightText; | |
display :inline-block; | |
} | |
li.File | |
{ | |
white-space :nowrap; | |
} | |
table#Caption | |
{ | |
width :100%; | |
position :fixed; | |
left :0px; | |
top :0px; | |
background-color:ActiveCaption; | |
color :CaptionText; | |
} | |
table#Caption tr td:nth-child(2) | |
{ | |
font-family :Courier New; | |
font-weight :bold; | |
width :100%; | |
} | |
table#Caption tr td:nth-child(3) | |
{ | |
white-space :nowrap; | |
} | |
table#Caption tr td:nth-child(3) button | |
{ | |
font-family :Marlett; | |
} | |
input#ImageFiles | |
{ | |
display :none; | |
} | |
canvas#Display | |
{ | |
position :absolute; | |
} | |
span.CPU_Group_A { | |
background-color: #772; <!-- ALU - AND/XOR/CMP 772--> | |
} | |
span.CPU_Group_B { | |
background-color: #764; <!-- Branching - Ccnd/Jcnd 752--> | |
} | |
span.CPU_Group_C { | |
background-color: #888; <!-- Controls - HLT/NOP 888--> | |
} | |
span.CPU_Group_D { | |
background-color: #663; <!-- Dubbed/Pair - INC/DEC 552--> | |
} | |
span.CPU_Group_E { | |
background-color: #363; <!-- Xchg/Stack - POP/PUSH 262--> | |
} | |
span.CPU_Group_F { | |
background-color: #367; <!-- Flip-Flop - MOV 257--> | |
} | |
span.CPU_Group_X { | |
background-color: #437; <!-- X-Code - --- 437--> | |
} | |
span.CPU_Group_Z { | |
background-color: #444; <!-- X-Code - --- 437--> | |
} | |
div.About | |
{ | |
display :none; | |
background-color:ButtonFace; | |
opacity :90%; | |
position :fixed; | |
bottom :10%; | |
right :10%; | |
left :10%; | |
top :10%; | |
} | |
div.About h1 | |
{ | |
background-color:ActiveCaption; | |
border :thick inset InActiveCaption; | |
margin :0px 0px 0px 0px; | |
text-align :center; | |
} | |
</style> | |
</head> | |
<body> | |
<div id=Info class=About onclick='this.style.display="none"'><h1>Koyaanisqatsi::Info</h1><hr /> | |
KISC - is Koyaanisqatsi Instruction Set Computing<hr /> | |
CPS - Cycles Per Second: Programm cycle until HALT<br /> | |
Classic Mode: Native Koyaanisqatsi operating<br /> | |
DF - Duplex Flag: Alias for ZF, when compare identical operands<br /> | |
Do n CPS: Run until end of terminal line</div> | |
<div id=Help class=About onclick='this.style.display="none"'><h1>Koyaanisqatsi::Help</h1><hr /> | |
<ul> | |
<li><a href='https://gistpreview.github.io/?cff9df3bc1901d33f5c055682721240a#gist=https://gist.githubusercontent.com/Alikberov/0a9ed1f8bca71b6e0bc957485497311b/raw/koy-mac-slalom.asm'>Autoload listing</a></li> | |
<li><a href='https://gistpreview.github.io/?cff9df3bc1901d33f5c055682721240a#autorun-gist=https://gist.githubusercontent.com/Alikberov/0a9ed1f8bca71b6e0bc957485497311b/raw/koy-mac-slalom.asm'>Autorun listing</a></li> | |
<li><a href='https://gistpreview.github.io/?cff9df3bc1901d33f5c055682721240a#classic-gist=https://gist.githubusercontent.com/Alikberov/0a9ed1f8bca71b6e0bc957485497311b/raw/koy-mac-slalom.asm'>Classic mnemonic</a></li> | |
<li><a href='https://gistpreview.github.io/?cff9df3bc1901d33f5c055682721240a#autorun-classic-gist=https://gist.githubusercontent.com/Alikberov/0a9ed1f8bca71b6e0bc957485497311b/raw/koy-mac-slalom.asm'>Autorun listing with classic</a></li> | |
</ul> | |
Context - Set | |
<button onclick='hUserPad.value = "B5:3F"; do_console()'>B5=3F</button> | |
<button onclick='hUserPad.value = "B5:CD"; do_console()'>B5=CD</button> | |
<button onclick='hUserPad.value = "C6:5B"; do_console()'>C6=5B</button> | |
<button onclick='hUserPad.value = "A0:01"; do_console()'>A0=01</button> | |
<button onclick='hUserPad.value = "A0:02"; do_console()'>A0=02</button> | |
<button onclick='hUserPad.value = "A0:08"; do_console()'>A0=08</button><br /> | |
Device#CB:Part#00 - Set | |
CB:00.1=<button onclick='hUserPad.value = "CB001#3F"; do_console()'>3F</button> | |
<button onclick='hUserPad.value = "CB001#06"; do_console()'>06</button> | |
<button onclick='hUserPad.value = "CB001#5B"; do_console()'>5B</button> | |
<button onclick='hUserPad.value = "CB001#4F"; do_console()'>4F</button> | |
<button onclick='hUserPad.value = "CB001#66"; do_console()'>66</button> | |
<button onclick='hUserPad.value = "CB001#6D"; do_console()'>6D</button> | |
<button onclick='hUserPad.value = "CB001#7D"; do_console()'>7D</button> | |
<button onclick='hUserPad.value = "CB001#07"; do_console()'>07</button> | |
<button onclick='hUserPad.value = "CB001#7F"; do_console()'>7F</button> | |
<button onclick='hUserPad.value = "CB001#6F"; do_console()'>6F</button><br /> | |
Device#CD:Part#00 - Set | |
CD:12.3=<button onclick='hUserPad.value = "CD123#00"; do_console()'>00</button> | |
<button onclick='hUserPad.value = "CD123#55"; do_console()'>55</button> | |
<button onclick='hUserPad.value = "CD123#FF"; do_console()'>FF</button> | |
CD:12.5=<button onclick='hUserPad.value = "CD125#AA"; do_console()'>AA</button> | |
<button onclick='hUserPad.value = "CD125#55"; do_console()'>55</button> | |
<button onclick='hUserPad.value = "CD125#FF"; do_console()'>FF</button> | |
CD:39.1=<button onclick='hUserPad.value = "CD391#AA"; do_console()'>AA</button> | |
<button onclick='hUserPad.value = "CD391#55"; do_console()'>55</button> | |
<button onclick='hUserPad.value = "CD391#FF"; do_console()'>FF</button><br /> | |
</div> | |
<div id=About class=About onclick='this.style.display="none"'><h1>Koyaanisqatsi::About</h1><hr /> | |
KISC - is Koyaanisqatsi Instruction Set Computing</div> | |
<table id=Caption cellPadding=0 cellSpacing=0 border=0> | |
<tr> | |
<td><blink><b style=color:red>K</b></blink></td> | |
<td>oy-Machine Emulator v1.01</td> | |
<td><button>0</button><button>2</button><button>r</button></td> | |
</tr> | |
<tr> | |
<td colSpan=3> | |
<label id=MenuBar> | |
<input id=CaptionMenu type=checkbox /> | |
<menu>File<ul> | |
<li onclick='window.localStorage.clear(); window.location=""'>New</li> | |
<li onclick='return false'><menu>Load<ol id=Files> | |
</ol></menu></li> | |
<li onclick='hImageFiles.click()'>Load local</li> | |
<hr /></li> | |
<li>… … …</li> | |
</ul> | |
</menu><menu>Edit<ul> | |
<li onclick='hUserDump.focus(); document.execCommand("cut", false)'>Cut</li> | |
<li onclick='hUserDump.focus(); document.execCommand("copy", false)'>Copy</li> | |
<li onclick='hUserDump.focus(); document.execCommand("selectAll", false)'>Select all</li> | |
</ul> | |
</menu><menu>Debug<ul> | |
<li onclick='cpu.step(); showState(); return false'>Step</li> | |
<li onclick='while(cpu.step()) {} showState(); return false'>Do</li> | |
</ul> | |
</menu><menu>CPU<ul> | |
<li onclick='cpu.reset(); showState()'>Reset</li> | |
<li onclick='cpu.classic = !cpu.classic; showState(); return false'>Classic</li> | |
<li onclick='do_run(1)'>Run 1 CPS</li> | |
<li onclick='do_run(10)'>Run 10 CPS</li> | |
<li onclick='do_run(100)'>Run 100 CPS</li> | |
<li onclick='do_run(1, true)'>Do 1 CPS</li> | |
<li onclick='do_run(10, true)'>Do 10 CPS</li> | |
<li onclick='do_run(100, true)'>Do 100 CPS</li> | |
</ul> | |
</menu><menu>View<ul> | |
<li onclick='return false'><menu>Render<ul> | |
<li onclick='cpu.render.noise = 0; return false'>Clean</li> | |
<li onclick='cpu.render.noise = 25; return false'>Noised</li> | |
<li onclick='cpu.render.noise = 50; return false'>Dirty</li> | |
</ul></menu></li> | |
<li onclick='hEmuCtx.style.display = hEmuCtx.style.display != "none" ? "none" : "block"; return false'>Context</li> | |
<li onclick='hEmuDis.style.display = hEmuDis.style.display != "none" ? "none" : "block"; return false'>Disassembly</li> | |
<li onclick='hUserLines.style.display = hUserDump.style.display = hUserDump.style.display != "none" ? "none" : "block"; return false'>Listing</li> | |
<li onclick='hEmuDump.style.display = hEmuDump.style.display != "none" ? "none" : "block"; return false'>Memory</li> | |
</ul></menu><menu>Help<ul> | |
<li onclick='document.getElementById("Info").style.display="block"'>Info</li> | |
<li onclick='document.getElementById("Help").style.display="block"'>Help</li> | |
<li onclick='document.getElementById("About").style.display="block"'>About</li> | |
<li><a href='https://gistpreview.github.io/?9a3cb1276bf066814f9a551a74052ff7' target=_blank>LCD-Editor</a></li> | |
<li><a href='https://github.com/Alikberov/Koyaanisqatsi'>GitHub</a></li> | |
</ul></menu><pre id=EmuLog>Status</pre></label> | |
</td> | |
</tr> | |
</table> | |
<span id=State></span> | |
<input id=UserPad type=text placeholder='User input Terminal' value='DF00 EOR A1,[BC98765+43210+C1] ;$run 12421242124211322311322644656446757675737322' /> | |
<hr /> | |
<table id=Debugger cellspacing=0 cellpadding=0> | |
<tr> | |
<td> | |
<pre id=EmuCtx> | |
</pre> | |
</td> | |
<td> | |
<pre id=EmuDis> | |
</pre> | |
</td> | |
<td><textarea id=UserLines cols=4 disabled></textarea></td> | |
<td><textarea id=UserDump rows=16 cols=56> | |
.assm ; revision 20210709 | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; (C) Alikberov - 2021.07.09 ; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
.IMGUR OysxF9E.png ; Load LCD-Sprites | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
CALL .DEFW 0xFE ; CALL LABEL | |
RET .DEF 0xE9 ; RETURN | |
JC .DEFW 0xEC ; JUMP IF CARRY | |
JNC .DEFW 0xED ; JUMP IF NO CARRY | |
JZ .DEFW 0xEE ; JUMP IF ZERO | |
JNZ .DEFW 0xEF ; JUMP IF NO ZERO | |
SHR .DEF 0xF9 ; SHIFT RIGHT | |
LDA .DEFB 0xFA ; LOAD A | |
LDB .DEFB 0xFB ; LOAD B | |
LDC .DEFB 0xFC ; LOAD C | |
LOAD .DEFW 0xFD ; LOAD BC | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
STACK .EQU 0x00A0 ; STACK BASE | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; CALL ADDRESS | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
.DEF CALL | |
OR A9,A9 ; CLC | |
ADD BC4,BC9 ; ADC SP,-1 | |
CMC ; CMC | |
ADD BC4,BC9 ; ADC SP,-1 | |
MOV C8,[BC0+1] | |
MOV B8,[BC0+2] | |
ADD BC0,BC1 | |
ADD BC0,BC1 | |
MOV [BC4+0],C0 ; MOV [SP],C0 | |
MOV [BC4+1],B0 ; MOV [SP+1],B0 | |
JCF BC8+0 | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; RET | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
.DEF RET | |
MOV B0,[BC4+1] | |
MOV C0,[BC4+0] | |
OR C0,C0 | |
ADD BC4,BC1 | |
ADD BC4,BC1 | |
CMC | |
JCE | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; JC ADDRESS | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
.DEF JC | |
JCF BC0+3 ; Is Carry - False? | |
MOV A0,[BC0+1] | |
MOV B0,[BC0+2] | |
MOV C0,A0 | |
JCE | |
CMC | |
JCE | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; JNC ADDRESS | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
.DEF JNC | |
JCE BC0+3 ; Is Carry - Exist? | |
MOV A0,[BC0+1] | |
MOV B0,[BC0+2] | |
MOV C0,A0 | |
JCE | |
CMC | |
JCE | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; JZ ADDRESS | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
.DEF JZ | |
JDF BC0+3 ; Is Duplex - False? | |
MOV A0,[BC0+1] | |
MOV B0,[BC0+2] | |
MOV C0,A0 | |
JDE | |
CMD | |
JDE | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; JNZ ADDRESS | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
.DEF JNZ | |
JDE BC0+3 ; Is Duplex - Exist? | |
MOV A0,[BC0+1] | |
MOV B0,[BC0+2] | |
MOV C0,A0 | |
JDE | |
CMD | |
JDE | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; SHR | |
; SHIFT RIGHT A9 | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
.DEF SHR | |
ADD A9,A9 | |
ADD A9 | |
ADD A9 | |
ADD A9 | |
ADD A9 | |
ADD A9 | |
ADD A9 | |
ADD A9 | |
ADD A9 | |
ADD A9 | |
ADD A9 | |
ADD A9 | |
ADD A9 | |
ADD A9 | |
ADD A9 | |
ADD A9 | |
ADD A9 | |
JAE BC0+1 | |
JAF BC0+1 | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; LOAD A,DATA | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
.DEF LDA | |
MOV A,[BC0+1] | |
JAE BC0+2 | |
JAF BC0+2 | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; LOAD B,DATA | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
.DEF LDB | |
MOV B,[BC0+1] | |
JAE BC0+2 | |
JAF BC0+2 | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; LOAD C,DATA | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
.DEF LDC | |
MOV C,[BC0+1] | |
JAE BC0+2 | |
JAF BC0+2 | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; LOAD BC,ADDRESS | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
.DEF LOAD | |
MOV C,[BC0+1] | |
MOV B,[BC0+2] | |
JAE BC0+3 | |
JAF BC0+3 | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
; BC1 = +1 | |
; BC9 = -1 | |
; BC6 = GLASS DATA | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
STACK .EQU 0x1000 | |
.ORG 0 | |
TETRIS | |
OR C0,B0 | |
JZ TETRINI | |
CALL SCORED | |
CALL GLASSED | |
CALL CHECK | |
HALT | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
TETRINI | |
LOAD BC1,1 ; BC1 = +1 | |
LOAD BC9,65535 ; BC9 = -1 | |
LOAD BC6,GLASS | |
LOAD BC5,NUMBERS | |
LOAD BC4,STACK | |
LDA A9,0xCB | |
MOV D9,A9 | |
HALT | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
SCORED | |
LOAD BC8,0 | |
LDA A5,10 | |
MOV A9,[BC6+65535] | |
.DIV10 ARG A,A | |
MOV A8,A9 | |
OR A9,A9 | |
SUB A5 | |
JC .DIV1 | |
ADD BC8,BC1 | |
JNZ .DIV10 | |
.DIV1 MOV D0,[BC5+0+A8] | |
MOV D1,[BC5+0+C8] | |
MOV D2,[BC6+0] | |
MOV D3,[BC6+1] | |
MOV D4,[BC6+2] | |
MOV D5,[BC6+3] | |
MOV D6,[BC6+4] | |
MOV D7,[BC6+5] | |
RET | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
CHECK | |
LDA A9,5 | |
MOV C7,C6 | |
MOV B7,B6 | |
.LOOP | |
MOV A8,[BC7+0] | |
OR A8 | |
SUB C9 | |
JZ .ERASE | |
CMC | |
ADD BC7,BC1 | |
SUB A9,C1 | |
JNC .LOOP | |
RET | |
.ERASE | |
MOV A8,[BC7+1] | |
MOV [BC7+0],A8 | |
OR A9,A9 | |
ADD BC7,BC1 | |
SUB C1 | |
JNZ .ERASE | |
HALT | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
GLASSED | |
RET | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
SCORE .DB 25 | |
GLASS .DB 0xFD | |
.DB 0xFF | |
.DB 0xCF | |
.DB 0xE1 | |
.DB 0x00 | |
.DB 0x00 | |
NUMBERS .DB 0x7E ; 0 | |
.DB 0x0C ; 1 | |
.DB 0xB6 ; 2 | |
.DB 0x9E ; 3 | |
.DB 0xCC ; 4 | |
.DB 0xDA ; 5 | |
.DB 0xFA ; 6 | |
.DB 0x0E ; 7 | |
.DB 0xFE ; 8 | |
.DB 0xDE ; 9 | |
</textarea> | |
<!-- | |
____ .0 .1 .2 .3 .4 .5 .6 .7 .8 .9 .A .B .C .D .E .F | |
0000 B4 C4 EE A0 00 B1 C1 EE 01 00 B9 C9 EE FF FF B5 | |
0010 C5 EE C0 C0 D9 DC 5F EC 'H 'E 'L 'L 'O 20 'W 'O | |
0020 'R 'L 'D 21 00 | |
EC00 BB B5 0F CC C5 0F 10 EC 51 CE CF 51 CE | |
EC10 AA A9 9E 09 DD 9E 51 DD 50 9D 02 DE D0 DA 9F DF | |
EC20 DE | |
EE00 CC 01 8F BB 02 8F 03 CE 03 CF | |
.assm--> | |
</td> | |
<td><pre id=EmuDump> | |
</pre> | |
</td> | |
</tr> | |
</table> | |
<hr /> | |
<canvas id=Display width=128 height=128 style=zoom:3></canvas> | |
<canvas id=Screen width=128 height=128></canvas> | |
<img id=Sprites style=display:none crossorigin='' src5='https://i.imgur.com/OysxF9E.png' src4='https://i.imgur.com/kUeYpDl.png' src3='https://i.imgur.com/AKvx7ER.png' src2='https://i.imgur.com/xPIE14m.png' src1='https://i.imgur.com/MQ1x2Bf.png' /> | |
<input id=ImageFiles type=file accesskey=f accept='image/png' /> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment