Processor Architecture, Web Assembler and Graphic Accelerator
Posted: Fri Oct 19, 2018 9:55 am
Today is a nice day, I finished the assembler for the third iteration of my processor architecture, I can finaly program it!
(8-Bit, 26 instructions in the instruction set, 256 instructions in the program memory, 1 accumulator, 15 registers, 16 input and 16 output ports, each instruction takes only 1 clock cycle)
Look at that, I wrote a program to use my Bresenham Graphic Accelerator ^^
(you will find this circuit on a previous post on the forum)
Here's the program on my assembler, I can tell you that it is way fancier than the second iteration tool I made, it event allows comments!
(it is not case sensitive)
If you want to make your own assembler inside a webpage like I did, I let you my code below, so you can get some cool ideas (like the saveData function, which I found on stack overflow)
/!\ WARNING /!\ it only works on Google Chrome, you may get strange results if you try to load the page on another web browser.
"assembler.html":
"assembler.css":
(8-Bit, 26 instructions in the instruction set, 256 instructions in the program memory, 1 accumulator, 15 registers, 16 input and 16 output ports, each instruction takes only 1 clock cycle)
Look at that, I wrote a program to use my Bresenham Graphic Accelerator ^^
(you will find this circuit on a previous post on the forum)
Here's the program on my assembler, I can tell you that it is way fancier than the second iteration tool I made, it event allows comments!
(it is not case sensitive)
If you want to make your own assembler inside a webpage like I did, I let you my code below, so you can get some cool ideas (like the saveData function, which I found on stack overflow)
/!\ WARNING /!\ it only works on Google Chrome, you may get strange results if you try to load the page on another web browser.
"assembler.html":
Code: Select all
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="./assembler.css">
<title>Online Assembler</title>
</head>
<body onload="TextPass()">
<div id=TextArea>
<div id="TextBG"></div>
<div id="TextLines"></div>
<textarea rows="32" cols="50" spellcheck="false" id="TextInput" oninput="TextPass()"></textarea>
<div id="TextOutput"></div>
<button id="asm_mode" type="button" onclick="SwitchA()">Disable Auto Assembly</button>
<button id="asm_run" type="button" onclick="byteprog.fill(0);asm_enabled=1;TextPass();asm_enabled=0;">Assemble</button>
<input id="filename" type="text" value="program_name">
<button id="download" type="button" onclick="saveData(document.getElementById('filename').value)">Download Program</button>
<div id="ErrorDisplay"></div>
</div>
<script type="text/javascript">
var colors = {
inst:"#FFBBBB",
reg:"#F0E68C",
valneg:"#A5D6E1",
valsml:"#A5B8E1",
valbig:"#B0A5E1",
comm: "#90EE90"
}
var color_enabled = 1;
var asm_enabled = 1;
var byteprog = [];
byteprog[511] = 0;
byteprog.fill(0);
var saveData = (function () {//saveData(document.getElementById('filename').value)
var a = document.createElement("a");
document.body.appendChild(a);
a.style = "display: none";
return function (fileName) {
var blob = new Blob([new Uint8Array(byteprog)]),
url = window.URL.createObjectURL(blob);
a.href = url;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(url);
};
}());
function op_nbr(p_op) {
switch (p_op) {
case "NOP": return 0;
case "RET": return 8;
case "CALL": return 9;
case "JMP": return 10;
case "JRO": return 11;
case "MOV": return 4;
case "JEZ": return 12;
case "JNZ": return 13;
case "JGZ": return 14;
case "JLZ": return 15;
case "NOT": return 16;
case "AND": return 17;
case "XOR": return 18;
case "OR": return 19;
case "NOR": return 20;
case "XNOR": return 21;
case "NAND": return 22;
case "NEG": return 23;
case "ADD": return 24;
case "SUB": return 25;
case "DIV": return 26;
case "MOD": return 27;
case "MUL": return 28;
case "ISUB": return 29;
case "IDIV": return 30;
case "IMOD": return 31;
default:
return 0;
}
}
function val_nbr(p_val) {
if (p_val < 0) p_val += 256;
return p_val;
}
function reg_nbr(p_reg) {
p_reg = p_reg.toUpperCase();
if (p_reg == 'ACC') return 0;
if (p_reg[0] == 'R') {
p_reg = p_reg.substr(1);
return p_reg;
} else if (p_reg[0] == 'P') {
return p_reg.charCodeAt(1) - 49;
} else if (p_reg[0] == '$') {
p_reg = p_reg.substr(1);
return p_reg;
}
return 0;
}
function writeBin(inst, prm1, prm2, isval, line) {
//alert(inst + ':' + prm1 + ':' + prm2 + ':' + isval + ':' + line);
if (!asm_enabled) return;
var tmp = 0;
var chunk = [0, 0];
chunk[1] += op_nbr(inst)<<3;//instruction
if (inst == 'MOV') {
if (isval) {
tmp = val_nbr(prm1);
chunk[1] += tmp>>3;
chunk[0] += (tmp<<5)%256;
chunk[0] += (reg_nbr(prm2)<<3)%32;
chunk[0] += reg_nbr(prm2)>>2;
} else {
chunk[1] += reg_nbr(prm1);
chunk[0] += reg_nbr(prm2)<<3;
chunk[0] += 7;
}
} else {
if (isval) {
tmp = val_nbr(prm1);
chunk[1] += tmp>>5;
chunk[0] += (tmp<<3)%256;
chunk[0] += (reg_nbr(prm2)%4)<<1;
} else {
tmp = reg_nbr(prm1);
chunk[1] += tmp>>2;
chunk[0] += (tmp<<2)%256;
chunk[0] += reg_nbr(prm2)<<1;
chunk[0] += 1;
}
}
byteprog[line*2] = chunk[0];
byteprog[line*2+1] = chunk[1];
//alert(chunk);
}
function getCode(str) {//removes the comments
if (str == '') return '';
var temp = '';
var cnt = 0;
while (cnt < str.length & str[cnt] != '*') {
temp += str[cnt];
cnt ++;
}
return temp;
}
function getComm(str) {//removes the code
if (str == '') return '';
var temp = '';
var cnt = 0;
while (cnt < str.length & str[cnt] != '*') {
cnt ++;
}
while (cnt < str.length) {
temp += str[cnt];
cnt ++;
}
return temp;
}
function RefineTab(tab) {//removes useless '' inside arrays
var temp = [];
var cnt = 0;
var cnt2 = 0;
while (cnt < tab.length) {
if (tab[cnt] != '') {
temp[cnt2] = tab[cnt];
cnt2 ++;
}
cnt ++;
}
return temp;
}
function isinst(str) {//tells if it is an instruction and how many words are expected in the line
if (str.length < 3) return 0;
if (str.length > 4) return 0;
str = str.toUpperCase();
switch (str) {
case "NOP":
case "RET":
return 1
case "CALL":
case "JMP":
case "JRO":
return 2
case "MOV":
case "JEZ":
case "JNZ":
case "JGZ":
case "JLZ":
case "NOT":
case "AND":
case "XOR":
case "OR":
case "NOR":
case "XNOR":
case "NAND":
case "NEG":
case "ADD":
case "SUB":
case "DIV":
case "MOD":
case "MUL":
case "ISUB":
case "IDIV":
case "IMOD":
return 3;
default:
return 0;
}
}
function isreg(str) {//tells if it is a register name (don't put a '.' in str)
str = str.toUpperCase();
if (str == 'ACC') return 1;
if (str.length < 2 | str.length > 3) return 0;
if (str[0] == 'R') {
str = str.substr(1);
if (isNaN(str)) return 0;
if (str > 0 & str < 16) return 1;
}
else if (str[0] == 'P') {
if (str.length != 2) return 0;
if (str[1].search(/[A-P]/) != -1) return 1;
}
else if (str[0] == '$') {
str = str.substr(1);
if (isNaN(str)) return 0;
if (str >= 0 & str < 32) return 1;
}
return 0;
}
function isvalue(str) {//tells if it is a decimal integer that can fit into 8 bits (don't put a '.' in str)
if (isNaN(str)) return 0;
if (str >= -128 & str < 0) return 1;//negative signed int
if (str >= 0 & str < 128) return 2;//positive signed int or small unsigned int
if (str >= 128 & str < 256) return 3;//big unsigned int
return 0;
}
function addColor(str, pos, color) {//return str with the word selected by pos (0 = first word and so on) colored
var cnt = 0;
var output = '';
if (pos == -1) {
output += "<span.style=\"color:" + color + "\">" + str + "</span>";
}
while (str[cnt] == ' ') {
output += ' ';
cnt ++;
if (cnt == str.length) return output;
}
while (pos > 0) {
while (str[cnt] != ' ') {
output += str[cnt];
cnt ++;
if (cnt == str.length) return output;
}
while (str[cnt] == ' ') {
output += ' ';
cnt ++;
if (cnt == str.length) return output;
}
pos --;
}
output += "<span.style=\"color:" + color + "\">";
while (str[cnt] != ' ' & cnt < str.length) {
output += str[cnt];
cnt ++;
}
output += "</span>";
while (cnt < str.length) {
output += str[cnt];
cnt ++;
}
return output;
}
// ^ functions for Color();
function Color(str) {//puts the colors in the text
str = str.split('\n');
var lines = str.length;
var colored_str = '';
var cnt = 0;
var code = '';
var comm = '';
var prmcnt = 0;
var valueType = 0;
var tempTxt = '';
var errmsg = '';
while (cnt < lines) {
code = RefineTab(getCode(str[cnt]).split(' '));//array of code words in the line
tempTxt = getCode(str[cnt]);
if (code.length != 0) {
prmcnt = isinst(code[0]);
if (prmcnt == 0) {
errmsg += 'line ' + cnt + ': unknown instruction<br>';
} else {
tempTxt = addColor(tempTxt, 0, colors.inst);
if (code.length != prmcnt & code.length != prmcnt-1) {
errmsg += 'line ' + cnt + ': wrong number of argument<br>';
} else {
switch (prmcnt) {
case 1:
writeBin(code[0].toUpperCase(), "ACC", "ACC", 0, cnt);
break;
case 2:
if (code.length == 2) {//code[1] exists
if (isreg(code[1])) {
tempTxt = addColor(tempTxt, 1, colors.reg);//--- 1 register argument on a 1 argument instruction ---o
writeBin(code[0].toUpperCase(), code[1].toUpperCase(), "ACC", 0, cnt);
} else {
valueType = isvalue(code[1]);
if (valueType == 0) errmsg += 'line ' + cnt + ': wrong argument 1<br>';
else {
switch (valueType) {//--- 1 value argument on a 1 argument instruction ---o
case 1:
tempTxt = addColor(tempTxt, 1, colors.valneg);
break;
case 2:
tempTxt = addColor(tempTxt, 1, colors.valsml);
break;
case 3:
tempTxt = addColor(tempTxt, 1, colors.valbig);
break;
default: break;
}
writeBin(code[0].toUpperCase(), code[1], "ACC", 1, cnt);
}
}
} else writeBin(code[0].toUpperCase(), "ACC", "ACC", 0, cnt);
break;
case 3:
if (isreg(code[1])) {//- reg -o
tempTxt = addColor(tempTxt, 1, colors.reg);
if (code.length == 3) {//code[2] exists
if (isreg(code[2])) {
tempTxt = addColor(tempTxt, 2, colors.reg);//--- reg reg ---o
writeBin(code[0].toUpperCase(), code[1].toUpperCase(), code[2].toUpperCase(), 0, cnt);
} else {
valueType = isvalue(code[2]);
if (valueType == 0) {
errmsg += 'line ' + cnt + ': wrong argument 2<br>';
}
else {//--- reg val ---o
errmsg += 'line ' + cnt + ': wrong argument 2, it has to be a register<br>';
}
}
} else {
tempTxt = addColor(tempTxt, 1, colors.reg);
writeBin(code[0].toUpperCase(), code[1].toUpperCase(), "ACC", 0, cnt);
}
} else {
valueType = isvalue(code[1]);
if (valueType == 0) errmsg += 'line ' + cnt + ': wrong argument 1<br>';
else {
switch (valueType) {//- val -o
case 1:
tempTxt = addColor(tempTxt, 1, colors.valneg);
break;
case 2:
tempTxt = addColor(tempTxt, 1, colors.valsml);
break;
case 3:
tempTxt = addColor(tempTxt, 1, colors.valbig);
break;
default: break;
}
if (code.length == 3) {//code[2] exists
if (isreg(code[2])) {//--- val reg ---o
if (code[0].toUpperCase() == 'MOV') {
switch (code[2].toUpperCase()) {
case 'PM':
case 'PN':
case 'PO':
case 'PP':
case '$28':
case '$29':
case '$30':
case '$31':
errmsg += 'line ' + cnt + ': wrong argument 2, you can\'t move a value to the 4 last ports<br>'
break;
default:
tempTxt = addColor(tempTxt, 2, colors.reg);
writeBin(code[0].toUpperCase(), code[1], code[2].toUpperCase(), 1, cnt);
break;
}
} else {
switch (code[2].toUpperCase()) {
case 'ACC':
case 'R1':
case 'R2':
case 'R3':
case '$0':
case '$1':
case '$2':
case '$3':
tempTxt = addColor(tempTxt, 2, colors.reg);
writeBin(code[0].toUpperCase(), code[1], code[2].toUpperCase(), 1, cnt);
break;
default:
errmsg += 'line ' + cnt + ': wrong argument 2, only the 4 first registers can receive a value<br>'
break;
}
}
} else {
valueType = isvalue(code[2]);
if (valueType == 0) errmsg += 'line ' + cnt + ': wrong argument 2<br>';
else errmsg += 'line ' + cnt + ': wrong argument 2, it has to be a register<br>';//--- val val ---o
}
} else writeBin(code[0].toUpperCase(), code[1], "ACC", 1, cnt);
}
}
break;
default: break;
}
}
}
}
colored_str += tempTxt.replace(/ /g, ' ').replace(/\./g, ' ');
comm = getComm(str[cnt]);//comments
if (comm != '') colored_str += "<span style=\"color:" + colors.comm + "\">" + comm.replace(/ /g, ' ') + "</span>";
colored_str += '\n';
cnt ++;
}
if (lines > 256) errmsg += "WARNING: the program must be at most 256 lines long (it is " + lines + " lines long)";
document.getElementById("ErrorDisplay").innerHTML = errmsg;
return colored_str;
}
function FillNbr(lines) {//fills the column with the line numbers
var str = "0";
var cnt = 1;
while (cnt < lines) {
str = str + "<br>" + cnt.toString();
cnt ++;
}
document.getElementById("TextLines").innerHTML = str;
var wid = (cnt-1).toString().length*8;
document.getElementById("TextLines").style.width = wid.toString() + "px";
document.getElementById("TextBG").style.width = (393+wid).toString() + "px";
document.getElementById("TextOutput").style.left = (19+wid).toString() + "px";
document.getElementById("TextInput").style.left = (17+wid).toString() + "px";
document.getElementById("ErrorDisplay").style.left = (440+wid).toString() + "px";
document.getElementById("asm_mode").style.left = (440+wid).toString() + "px";
document.getElementById("asm_run").style.left = (620+wid).toString() + "px";
document.getElementById("filename").style.left = (440+wid).toString() + "px";
document.getElementById("download").style.left = (620+wid).toString() + "px";
}
function TextPass() {//main function of the custom TextArea
if (asm_enabled) byteprog.fill(0);
var str_ = "";
var str = "";
var cnt = 0;
var cnt2 = 0;
var lines = 1;
var SelStart = 0;
var SelEnd = 0;
str_ = document.getElementById("TextInput").value.replace(/\t/g, " ").replace(/[^A-Za-z0-9\-\* \n\r$]/g, "?");//if forbiden character, replace by '?' (at least '<>&.')
while (cnt < str_.length) {
cnt ++;
cnt2 ++;
if (str_[cnt-1] == "\n") {
cnt2 = 0;
lines ++;
}
if (cnt2 < 50) {
str = str + str_[cnt-1];
}
}
lines = Math.max(32, lines);
FillNbr(lines);
document.getElementById("TextInput").rows = lines;
document.getElementById("TextBG").style.height = (lines*17+2).toString() + "px";
document.getElementById("TextLines").style.height = (lines*17+1).toString() + "px";
SelStart = document.getElementById("TextInput").selectionStart;
SelEnd = document.getElementById("TextInput").selectionEnd;
document.getElementById("TextInput").value = str;//Updating the input TextArea
document.getElementById("TextInput").setSelectionRange(SelStart, SelEnd);
if (color_enabled) str = Color(str);//puting colors
else Color(str);//the function is still mandatory for the assembly
str = str.replace(/\n/g, '<br>');//replacing '\n' by '<br>'
document.getElementById("TextOutput").innerHTML = str;
}
function SwitchC() {//disable colors
color_enabled = 1 - color_enabled;
TextPass();
}
function SwitchA() {//disable assembly
asm_enabled = 1-asm_enabled;
if (asm_enabled) {
document.getElementById("asm_mode").innerHTML = "Disable Auto Assembly";
document.getElementById("asm_run").style = "visibility: hidden";
} else {
document.getElementById("asm_mode").innerHTML = "Enable Auto Assembly";
document.getElementById("asm_run").style = "visibility: visible";
byteprog.fill(0);
if (asm_enabled) TextPass();
}
}
</script>
</body>
</html>
Code: Select all
#TextArea { /* whole text editor */
position: absolute;
left: 1em;
top: 1em;
}
#TextBG { /* background + border */
position: absolute;
left: 10px;
width: 409px; /*JS*/
top: 10px;
height: 546px; /*JS*/
z-index: 1;
background-color: #272822; /* bg color */
border: 1px solid #00AAFF; /* border color */
}
#TextLines { /* line nbr */
/*-webkit-user-select: none;*/
font-family: Consolas;
font-size: 14px;
position: absolute;
left: 11px;
width: 16px; /*JS*/
top: 11px;
height: 545px; /*JS*/
z-index: 2;
padding: 1px 2px 0px;
text-align: right;
color: lightgrey; /* line nbr color */
background-color: grey; /* line nbr bg color */
}
#TextOutput { /* text shown and customisable */
/*-webkit-user-select: none;*/
font-family: Consolas;
font-size: 14px;
position: absolute;
left: 35px; /*JS*/
top: 12px;
z-index: 2;
color: white; /* text color */
}
#TextInput { /* invisible textarea input */
font-family: Consolas;
font-size: 14px;
position: absolute;
left: 33px; /*JS*/
top: 10px;
z-index: 3;
resize: none;
border: none;
outline: none;
background: none;
overflow: hidden;
color: #FF000000;
caret-color: white; /* caret color */
}
#TextInput::selection {
color: white; /* text color while being selected */
background: #E09050 /* bg color while being selected */
}
#asm_mode {
position: fixed;
left: 460px;
top: 40px;
}
#asm_run {
position: fixed;
left: 640px;
top: 40px;
visibility: hidden
}
#filename {
position: fixed;
left: 460px;
top: 80px;
}
#download {
position: fixed;
left: 640px;
top: 80px;
}
#ErrorDisplay {
position: fixed;
left: 460px;
top: 120px;
}