Page 1 of 1

Processor Architecture, Web Assembler and Graphic Accelerator

Posted: Fri Oct 19, 2018 9:55 am
by Xaetral
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)

Image

Image

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)

Image

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, '&nbsp;').replace(/\./g, ' ');

				comm = getComm(str[cnt]);//comments
				if (comm != '') colored_str += "<span style=\"color:" + colors.comm + "\">" + comm.replace(/ /g, '&nbsp;') + "</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>
"assembler.css":

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;
}