1 /*
2 	Copyright (c) 2018 Clipsey
3 
4 	Permission is hereby granted, free of charge, to any person or organization
5 	obtaining a copy of the software and accompanying documentation covered by
6 	this license (the "Software") to use, reproduce, display, distribute,
7 	execute, and transmit the Software, and to prepare derivative works of the
8 	Software, and to permit third-parties to whom the Software is furnished to
9 	do so, all subject to the following:
10 
11 	The copyright notices in the Software and this entire statement, including
12 	the above license grant, this restriction and the following disclaimer,
13 	must be included in all copies of the Software, in whole or in part, and
14 	all derivative works of the Software, unless such copies or derivative
15 	works are solely in the form of machine-executable object code generated by
16 	a source language processor.
17 
18 	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 	FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
21 	SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
22 	FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
23 	ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 	DEALINGS IN THE SOFTWARE.
25 */
26 module crisc;
27 import std.stdio;
28 import std..string;
29 import std.array;
30 import std.conv;
31 import std.file;
32 import std.algorithm;
33 
34 public enum DBGOpCode : uint {
35 	// PRINT a register
36 	PRT_REG = 0,
37 	
38 	// PRINT the program counter
39 	PRT_CTR = 1,
40 	
41 	// PRINT the program cycles
42 	PRT_CYC = 2,
43 	
44 	// PRINT the writable memory
45 	PRT_WMEM = 3,
46 
47 	// PRINT the full memory.
48 	PRT_FMEM = 4,
49 
50 	// PRINT memory range
51 	PRT_MEMR = 5,
52 
53 	// PRINT the callstack.
54 	PRT_CSTK = 6,
55 
56 	// PRINT the datastack.
57 	PRT_DSTK = 7,
58 
59 	// SET verbose logging output
60 	SET_VEB = 8,
61 
62 	// SET verbose logging for data stack operations.
63 	SET_VSTK = 9,
64 }
65 
66 public enum OpCode : ubyte {
67 	// Kill program
68 	HALT = 0,
69 	
70 	// Move REG A to REG B
71 	MOV = 1,
72 	
73 	// Move CONST A to REG B
74 	MOVC = 2,
75 	
76 	// Move REG (Referenced by CONST) A to REG B
77 	MOVR = 3,
78 	
79 	ADD = 4,
80 	
81 	// Add CONST A to REG B
82 	ADDC = 5,
83 	
84 	// Add REG (Referenced by CONST) A to REG B
85 	ADDR = 6,
86 	
87 	// Subtract REG A to REG B
88 	SUB = 7,
89 	
90 	// Subtract CONST A to REG B
91 	SUBC = 8,
92 	
93 	// Subtract REG (Referenced by CONST) A to REG B
94 	SUBR = 9,
95 		
96 	// Multiply REG A to REG B
97 	MUL = 10,
98 	
99 	// Multiply CONST A to REG B
100 	MULC = 11,
101 	
102 	// Multiply REG (Referenced by CONST) A to REG B
103 	MULR = 12,
104 	
105 	// Divide REG A to REG B
106 	DIV = 13,
107 	
108 	// Divide CONST A to REG B
109 	DIVC = 14,
110 	
111 	// Divide REG (Referenced by CONST) A to REG B
112 	DIVR = 15,
113 
114 	// JUMP TO ADDRESS A
115 	JMP = 16,
116 
117 	// JUMP TO CONST A
118 	JMPC = 17,
119 	
120 	// JUMP TO ADDRESS A IF STATUS register is NOT EQUAL to CONST
121 	JMPNEQ = 18,
122 	
123 	// JUMP TO ADDRESS A IF STATUS register is EQUAL to CONST
124 	JMPEQ = 19,
125 	
126 	// JUMP TO ADDRESS A IF STATUS register is EQUAL or LARGER than CONST B
127 	JMPLEQ = 20,
128 	
129 	// JUMP TO ADDRESS A IF STATUS register is EQUAL or SMALLER than CONST B
130 	JMPSEQ = 21,
131 	
132 	// JUMP TO CONST A IF STATUS register is NOT EQUAL to CONST
133 	JMPNEQC = 22,
134 	
135 	// JUMP TO CONST A IF STATUS register is EQUAL to CONST
136 	JMPEQC = 23,
137 	
138 	// JUMP TO CONST A IF STATUS register is EQUAL or LARGER than CONST B
139 	JMPLEQC = 24,
140 	
141 	// JUMP TO CONST A IF STATUS register is EQUAL or SMALLER than CONST B
142 	JMPSEQC = 25,
143 	
144 	// LOAD VALUE to REG A from (Referenced by CONST) MEMORY ADDRESS B
145 	LDR = 26,
146 
147 	// LOAD VALUE TO REG A from (Referenced by CONST) MEMORY ADDRESS B
148 	LDRC = 27,
149 
150 	// STORE VALUE from REG A to (Referenced by CONST) MEMORY ADDRESS B
151 	STR = 28,
152 
153 	// STORE VALUE OF REG A to (Referenced by CONST) MEMORY ADDRESS B
154 	STRC = 29,
155 	
156 	// CALL jump to address referenced by CONST A and set stack return pointer
157 	CALL = 30,
158 
159 	// CALL system level function.
160 	SCALL = 31,
161 	
162 	// PUSH value to stack
163 	PUSH = 32,
164 
165 	// PUSH value to stack
166 	PUSHC = 33,
167 	
168 	// POP value from stack
169 	POP = 34,
170 	
171 	// RETURN returns to the last stack pointer with values
172 	RET = 35,
173 	
174 	// DBG debugging functionality
175 	DBG = 36
176 }
177 
178 struct Instruction {
179 	OpCode opCode;
180 	size_t[2] data;
181 }
182 
183 struct CPUStack {
184 	private {
185 		size_t size;
186 		ubyte* stackptr;
187 		size_t stackoffset;
188 	}
189 
190 	public {
191 		void clearStack() {
192 			stackoffset = 0;
193 			while (stackoffset < size) {
194 				stackpointer_t[stackoffset] = 0;
195 				stackoffset++;
196 			}
197 			stackoffset = 0;
198 		}
199 
200 		ubyte* stackpointer() {
201 			return stackptr+stackoffset;
202 		}
203 
204 		size_t* stackpointer_t() {
205 			return (cast(size_t*)stackptr)+stackoffset;
206 		}
207 
208 		void push(size_t item) {
209 			checkBounds(1);
210 			*((cast(size_t*)stackpointer_t)) = item;
211 			stackoffset += 1;
212 		}
213 
214 		size_t pop() {
215 			checkBounds(-1);
216 			stackoffset -= 1;
217 			size_t item = *((cast(size_t*)stackpointer_t));
218 			return item;
219 		}
220 
221 		void checkBounds(long offset) {
222 			if (offset < 0) {
223 				if (stackoffset == 0 || cast(long)(stackoffset)-offset < 0) throw new Exception("Stack underflow");
224 			} else {
225 				if (stackoffset+offset > size)
226 					throw new Exception("Stack overflow!\nStack:\n"~stackStr);
227 			}
228 		}
229 
230 		string stackStr() {
231 			return to!string((stackpointer_t-stackoffset)[0..size]) ~ "; OFFSET=" ~ to!string(stackoffset);
232 		}
233 	}
234 }
235 
236 class SysCall {
237 	public string name;
238 
239 	this(string name) {
240 		this.name = name;
241 	}
242 
243 	this(string name, CPU owner) {
244 		this.name = name;
245 		this.valueptr = &owner.datastack;
246 	}
247 
248 	this(string name, CPUStack* stack) {
249 		this.name = name;
250 		this.valueptr = stack;
251 	}
252 
253 	protected CPUStack* valueptr;
254 	public abstract void execute();
255 }
256 
257 class SysCallGetSafeMem : SysCall {
258 	CPU cpu;
259 
260 	this() {
261 		super("gsfm");
262 	}
263 
264 	this(CPU cpu) {
265 		super("gsfm", cpu);
266 		this.cpu = cpu;
267 	}
268 
269 	public override void execute() {
270 		valueptr.push(cast(size_t)cpu.safeMemOffset);
271 	}
272 }
273 
274 class CPU {
275 	// Program counter/program pointer.
276 	Instruction* progptr;
277 
278 	// Pointer to start of program
279 	Instruction* progstart;
280 	
281 	size_t progctr() {
282 		return progptr-progstart;
283 	}
284 
285 	// List of system calls (NOTE NEEDS TO BE IN SAME ORDER AS ASSEMBLED)
286 	SysCall[] syscalls;
287 
288 	// Registers
289 	ulong[256] REGISTERS;
290 
291 	// Pointer to status register (0xFF)
292 	ulong* STATUS_REG;
293 
294 	// Call stack
295 	CPUStack callstack;
296 	
297 	// Data stack
298 	CPUStack datastack;
299 
300 	// The memory in which the program resides.
301 	ubyte[] memory;
302 
303 	// safe memory space offset
304 	size_t safeMemOffset;
305 
306 	// Amount of operations/instructions program has run
307 	ulong cOps = 0;
308 
309 	// Sizes for stacks
310 	ulong callstackSize;
311 	ulong stackStoreSize;
312 	
313 	// Verbose and Stack Verbose
314 	bool VEB = false;
315 	bool SVEB = false;
316 	
317 	bool running() {
318 		return (progptr !is null);
319 	}
320    
321 
322 	this(ubyte[] program, size_t stackSize, size_t memorySize) {
323 		// Set up status register pointer.
324 		STATUS_REG = &REGISTERS[255];
325 		
326 		// Prepare memory.
327 		memory = program;
328 		memory.length += stackSize;
329 		memory.length += memorySize;
330 
331 		// Set stack pointer.
332 		CPUStack cstack = { size:stackSize, stackptr:memory.ptr+program.length, stackoffset:0 };
333 		callstack = cstack;
334 
335 		// Set stack pointer for data.
336 		CPUStack dstack = { size:stackSize, stackptr:(memory.ptr+program.length)+(size_t.sizeof*stackSize), stackoffset:0 };
337 		datastack = dstack;
338 
339 		// Set program counter and program start pointer
340 		progstart = cast(Instruction*)memory.ptr;
341 		progptr = progstart;
342 		
343 		// Clear stacks
344 		callstack.clearStack();
345 		datastack.clearStack();
346 
347 		safeMemOffset = (program.length)+((size_t.sizeof*stackSize)*2);
348 
349 		// Add ptrc and rdc syscalls.
350 		this.syscalls = cast(SysCall[])[new SysCallGetSafeMem(this)];
351 	}
352 	
353 	public void PushSyscalls(SysCall[] syscalls) {
354 		this.syscalls ~= syscalls;
355 	}
356 
357 	void runCycle() {
358 		if (progptr is null) return;
359 		REGISTERS[254] = cast(size_t)callstack.stackpointer;
360 		debugInstr(*progptr);
361 		switch (progptr.opCode) {
362 
363 			case(OpCode.MOV):	REGISTERS[progptr.data[1]] = REGISTERS[progptr.data[0]]; 	break;
364 			case(OpCode.MOVC):	REGISTERS[progptr.data[1]] = progptr.data[0]; 				break;
365 			case(OpCode.MOVR):	REGISTERS[REGISTERS[progptr.data[1]]] = progptr.data[0]; 	break;
366 
367 			case(OpCode.ADD):	REGISTERS[progptr.data[1]] += REGISTERS[progptr.data[0]]; 	break;
368 			case(OpCode.ADDC):	REGISTERS[progptr.data[1]] += progptr.data[0]; 				break;
369 			case(OpCode.ADDR):	REGISTERS[REGISTERS[progptr.data[1]]] += progptr.data[0]; 	break;
370 
371 			case(OpCode.SUB):	REGISTERS[progptr.data[1]] -= REGISTERS[progptr.data[0]]; 	break;
372 			case(OpCode.SUBC):	REGISTERS[progptr.data[1]] -= progptr.data[0]; 				break;
373 			case(OpCode.SUBR):	REGISTERS[REGISTERS[progptr.data[1]]] -= progptr.data[0]; 	break;
374 
375 			case(OpCode.MUL): 	REGISTERS[progptr.data[1]] *= REGISTERS[progptr.data[0]]; 	break;
376 			case(OpCode.MULC): 	REGISTERS[progptr.data[1]] *= progptr.data[0]; 				break;
377 			case(OpCode.MULR): 	REGISTERS[REGISTERS[progptr.data[1]]] *= progptr.data[0]; 	break;
378 
379 			case(OpCode.DIV): 	REGISTERS[progptr.data[1]] /= REGISTERS[progptr.data[0]]; 	break;
380 			case(OpCode.DIVC): 	REGISTERS[progptr.data[1]] /= progptr.data[0]; 				break;
381 			case(OpCode.DIVR): 	REGISTERS[REGISTERS[progptr.data[1]]] /= progptr.data[0]; 	break;
382 
383 			case(OpCode.JMP):	progptr = progstart+(REGISTERS[progptr.data[0]])-1;			break;
384 
385 			case(OpCode.JMPEQ):
386 				if (*STATUS_REG == REGISTERS[progptr.data[1]]) progptr = progstart+(progptr.data[0])-1;
387 				break;
388 
389 			case(OpCode.JMPNEQ):
390  				if (*STATUS_REG != REGISTERS[progptr.data[1]]) progptr = progstart+(progptr.data[0])-1;
391 				break;
392 
393 			case(OpCode.JMPLEQ):
394  				if (*STATUS_REG >= REGISTERS[progptr.data[1]]) progptr = progstart+(progptr.data[0])-1;
395 				break;
396 
397 			case(OpCode.JMPSEQ):
398  				if (*STATUS_REG <= REGISTERS[progptr.data[1]]) progptr = progstart+(progptr.data[0])-1;
399 				break;
400 
401 			case(OpCode.JMPC):	progptr = progstart+(progptr.data[0])-1;					break;
402 
403 			case(OpCode.JMPEQC):
404  				if (*STATUS_REG == progptr.data[1]) progptr = progstart+(progptr.data[0])-1;
405 				break;
406 
407 			case(OpCode.JMPNEQC):
408  				if (*STATUS_REG != progptr.data[1]) progptr = progstart+(progptr.data[0])-1;
409 				break;
410 
411 			case(OpCode.JMPLEQC):
412  				if (*STATUS_REG >= progptr.data[1]) progptr = progstart+(progptr.data[0])-1;
413 				break;
414 
415 			case(OpCode.JMPSEQC):
416  				if (*STATUS_REG <= progptr.data[1]) progptr = progstart+(progptr.data[0])-1;
417 				break;
418 
419 			case(OpCode.DBG):
420 				if (progptr.data[0] == DBGOpCode.PRT_REG) writeln("REG_", progptr.data[1], "=", REGISTERS[progptr.data[1]]);
421 				if (progptr.data[0] == DBGOpCode.PRT_CTR) writeln("PROG_CTR=", progptr);
422 				if (progptr.data[0] == DBGOpCode.PRT_CYC) writeln("CYCLES=", cOps);
423 				if (progptr.data[0] == DBGOpCode.PRT_WMEM) writeln("MEMORY MAP=", to!string(memory));
424 				if (progptr.data[0] == DBGOpCode.PRT_DSTK) writeln("DATASTACK=", datastack.stackStr);
425 				if (progptr.data[0] == DBGOpCode.PRT_CSTK) writeln("CALLSTACK=", callstack.stackStr);
426 				if (progptr.data[0] == DBGOpCode.PRT_MEMR) writeln(to!string(readMemRange(REGISTERS[253], progptr.data[1])));
427 
428 
429 				if (progptr.data[0] == DBGOpCode.SET_VEB) VEB = cast(bool)progptr.data[1];
430 				if (progptr.data[0] == DBGOpCode.SET_VSTK) SVEB = cast(bool)progptr.data[1];
431 				break;
432 
433 			case(OpCode.CALL):
434 				if (progptr.data[0] < 0) throw new Exception("CPU HALT; ACCESS OUT OF BOUNDS");
435 				callstack.push(progctr+1);
436 				if (SVEB) writeln("DATASTACK=", datastack.stackStr);
437 				progptr = progstart+(progptr.data[0])-1;
438 				break;
439 
440 			case(OpCode.SCALL):	syscalls[progptr.data[0]].execute();	break;
441 
442 			case(OpCode.RET):
443 				size_t tptr = callstack.pop();
444 				progptr = progstart+tptr-1;
445 				if (SVEB) writeln("DATASTACK=", datastack.stackStr);
446 				break;
447 
448 			case(OpCode.PUSHC):
449 		   		datastack.push(progptr.data[0]);
450 				if (SVEB) writeln("DATASTACK=", datastack.stackStr);
451 				break;
452 
453 			case(OpCode.PUSH):
454 		   		datastack.push(REGISTERS[progptr.data[0]]);
455 				if (SVEB) writeln("DATASTACK=", datastack.stackStr);
456 				break;
457 
458 			case(OpCode.POP):
459 		   		REGISTERS[progptr.data[0]] = datastack.pop();
460 				if (SVEB) writeln("DATASTACK=", datastack.stackStr);
461 				break;
462 
463 			case(OpCode.LDR):	REGISTERS[progptr.data[1]] = readMem(REGISTERS[progptr.data[0]]);			break;
464 			case(OpCode.LDRC):	REGISTERS[progptr.data[1]] = readMem(progptr.data[0]);	break;
465 
466 			case(OpCode.STR):	writeMem(REGISTERS[progptr.data[1]], REGISTERS[progptr.data[0]]);				break;
467 			case(OpCode.STRC):	writeMem(REGISTERS[progptr.data[1]], progptr.data[0]);							break;
468 
469 			case(OpCode.HALT):
470 		   		writeln("PROGRAM HALTED.");
471 		 		progptr = null;
472 				break;
473 
474 			default:
475 				writeln("INVALID OPERATION @", progptr-progstart);
476 				break;
477 		}
478 		if (progptr is null) return;
479 		progptr++;
480 		cOps++;
481 	}
482 
483 	private size_t readMem(size_t address) {
484 		size_t value = *cast(size_t*)(cast(ubyte*)memory+address);
485 		//writeln("Reading ", value, " fom ", address);
486 		return value;
487 	}
488 
489 	private size_t[] readMemRange(size_t address, size_t length) {
490 		//writeln("Reading address ", address, " to ", address+length);
491 		return (cast(size_t*)((cast(ubyte*)memory+address)[0..length]))[0..length/size_t.sizeof];
492 	}
493 
494 	private void writeMem(size_t address, size_t value) {
495 		//writeln("Writing ", value, " to ", address);
496 		*cast(size_t*)(cast(ubyte*)memory+address) = value;
497 	}
498 
499 	private void debugInstr(Instruction instr) {
500 		if (VEB) writeln(to!string((cast(OpCode)instr.opCode)), " ", instr.data[0], " ", instr.data[1]);
501 	}
502 }
503 
504 struct Label {
505 	string name;
506 	size_t offset;
507 }
508 
509 struct LabelRef {
510 	string name;
511 	size_t doffset;
512 	size_t offset;
513 }
514 
515 class Compiler {
516 	private LabelRef[] labelRefs;
517 	private Instruction[] code;
518 	private Label[] labels;
519 	private bool doInfer;
520 
521 	private SysCall[] syscalls;
522 
523 	public this(bool infer, SysCall[] syscalls) {
524 		this.doInfer = infer;
525 
526 		// Add ptrc and rdc syscalls.
527 		this.syscalls = cast(SysCall[])[new SysCallGetSafeMem()] ~ syscalls;
528 	}
529 
530 	public ubyte[] compile(string asmCode) {
531 		// Process lines in program, removing/ignoring comments.
532 		string[] lines = asmCode.split('\n');
533 		string[] keywords;
534 		string[string] definitions;
535 
536 		bool skip = false;
537 		foreach(string line; lines) {
538 			string lo = "";
539 			bool isComment = false;
540 			bool isPreprocessorDirective = false;
541 			foreach (int i, char c; line) {
542 				if (c == ';') isComment = true;
543 				if (i == 0 && c == '#') isPreprocessorDirective = true;
544 				if (!isComment && !isPreprocessorDirective && !skip) lo ~= c;
545 			}
546 			if (isPreprocessorDirective) {
547 				string[] directive = line[1..$].split(' ');
548 				if (directive.length > 0) {
549 					if (directive[0] == "define") {
550 						definitions[directive[1]] = "";
551 						if (directive.length == 3) {
552 							definitions[directive[1]] = directive[2];
553 						}
554 					}
555 
556 					if (directive[0] == "ifdef") {
557 						skip = true;
558 						if (directive[1] in definitions) {
559 							skip = false;
560 						}
561 					}
562 
563 					if (directive[0] == "ifndef") {
564 						skip = true;
565 						if (directive[1] !in definitions) {
566 							skip = false;
567 						}
568 					}
569 
570 					if (directive[0] == "undef") {
571 						if (directive[1] in definitions) definitions.remove(directive[1]);
572 					}
573 
574 					if (directive[0] == "endif") {
575 						skip = false;
576 					}
577 				} else {
578 					throw new Exception("Invalid preprocessor directive \""~directive[0]~"\"! Directive must contain 1 or more tags/datapairs.");
579 				}
580 			}
581 
582 			foreach (string keyword; lo.split) {
583 				if (keyword in definitions) keywords ~= definitions[keyword];
584 				else if (keyword != "") keywords ~= keyword;
585 			}
586 		}
587 		
588 
589 		// Convert text/asm to instructions.
590 		bool parsingCompleted = false;
591 		int i = 0;
592 		uint instr_pos = 0;
593 		while (!parsingCompleted) {
594 			try {
595 				// Generate labels.
596 				if (i >= keywords.length) break;
597 				if (keywords[i].endsWith(":")) {
598 					Label l = { keywords[i][0..$-1], instr_pos };
599 					labels ~= l;
600 				} else {
601 					// Preprocessing
602 					OpCode opCode = getOp(keywords[i]);
603 					string kw = keywords[i];
604 
605 					// Specifies whether the assembler should infer which instruction to use based on how the arguments are structured.
606 					if (doInfer) {
607 						if (opCode != OpCode.HALT && opCode != OpCode.RET && opCode != OpCode.DBG && opCode != OpCode.CALL && opCode != OpCode.SCALL) {
608 							int r = 1;
609 							if (kw.toUpper.startsWith("JMP")) {
610 								r = 2;
611 							}
612 							if (!keywords[i+r].startsWith("@")) {
613 								if (opCode == OpCode.POP) throw new Exception("Invalid operation, \""~kw~"\" [arg a] takes an register, not a constant!");
614 								// Handle references.
615 								kw ~= "C";
616 							}
617 							if (!kw.toUpper.startsWith("JMP")) {
618 								if (opCode != OpCode.POP && opCode != OpCode.JMP && opCode != OpCode.PUSH) {
619 									if (!keywords[i+2].startsWith("@")) throw new Exception("Invalid operation, \""~kw~"\" [arg b] takes an register, not a constant!");
620 								}
621 							}
622 						}
623 					}
624 
625 					// Generate instructions.
626 					opCode = getOp(kw);
627 
628 					string argAStr = "";
629 					if (i+1 < keywords.length) argAStr = keywords[i+1];
630 					uint argA = 0;
631 
632 					string argBStr = "";
633 					if (i+2 < keywords.length) argBStr = keywords[i+2];
634 					uint argB = 0;
635 					if (opCode != OpCode.HALT && opCode != OpCode.RET) {
636 						argAStr = getValMacro(definitions, argAStr);
637 						if (opCode == OpCode.DBG) {
638 							argA = getDBGOp(argAStr);
639 						} else if (opCode == OpCode.SCALL) {
640 							argA = getSyscall(argAStr);
641 						} else {
642 							argA = getVal(labels, 0, argAStr);
643 						}
644 
645 						// Iterate
646 						i++;
647 						argBStr = getValMacro(definitions, argBStr);
648 						
649 						if (opCode != OpCode.CALL && opCode != OpCode.SCALL && opCode != OpCode.PUSH && opCode != OpCode.PUSHC && opCode != OpCode.POP && opCode != OpCode.JMP && opCode != OpCode.JMPC) {
650 							argB = getVal(labels, 1, argBStr);
651 
652 							// Iterate
653 							i++;
654 						}
655 						
656 					}
657 					Instruction instr = { opCode, [argA, argB] };
658 					code ~= instr;
659 					instr_pos++;
660 				}
661 				// Iterate
662 				i++;
663 				if (i >= keywords.length) parsingCompleted = true;
664 			} catch (Exception ex) {
665 				writeln("Error @ index ", i, ", token=", keywords[i], " message=", ex.message);
666 				return null;
667 			}
668 		}
669 
670 		// Post processing (label conversion)
671 		foreach(Label l; labels) {
672 			size_t x = 0;
673 			while (true) {
674 				if (x >= labelRefs.length) break;
675 				LabelRef lref = labelRefs[x];
676 				Instruction* instr = &code[lref.offset];
677 				if (lref.name == l.name) {
678 					instr.data[lref.doffset] = cast(uint)l.offset;
679 					labelRefs = labelRefs.remove(x);
680 					continue;
681 
682 					//throw new Exception("Invalid reference to label for OPCode "~to!string(cast(OpCode)(instr.opCode)));
683 				}
684 				x++;
685 			}
686 		}
687 
688 		return cast(ubyte[])code;
689 	}
690 
691 	string getValMacro(string[string] macros, string name, bool throwExceptionOnFailure = false) {
692 		if (name in macros) {
693 			return macros[name];
694 		}
695 		if (throwExceptionOnFailure) throw new Exception("Macro defintion not found in macro definitions!");
696 		return name;
697 	}
698 
699 	uint getValLabels(Label[] labels, string label) {
700 		foreach(Label l; labels) {
701 			if (l.name == label) return cast(uint)l.offset;
702 		}
703 		return -1;
704 	}
705 
706 	// # - Label
707 	// @ - Reference/Register
708 	// TODO: * - Address
709 
710 	uint getSyscall(string t) {
711 		if (t.startsWith("#")) {
712 			foreach(i; 0 .. syscalls.length) {
713 				if (syscalls[i].name.toLower == t[1..$].toLower) 
714 					return cast(uint)i;
715 			}
716 			throw new Exception("syscall "~t~" not found!");
717 		}
718 		if (t.startsWith("@")) {
719 			return to!uint(t[1..$]);
720 		}
721 		return to!uint(t);
722 	}
723 
724 	uint getVal(Label[] labels, size_t owner_offset, string t) {
725 		if (t.startsWith("#")) {
726 			LabelRef r = {t[1..$], owner_offset, code.length};
727 			labelRefs ~= r;
728 			return 0;
729 		}
730 		if (t.startsWith("@")) {
731 			t = t[1..$];
732 		}
733 		if (t.startsWith("0x")) {
734 			return to!uint(t[2..$], 16);
735 		}
736 		return to!uint(t);
737 	}
738 
739 	OpCode getOp(string name) {
740 		return to!OpCode(name.toUpper);
741 	}
742 
743 	DBGOpCode getDBGOp(string name) {
744 		return to!DBGOpCode(name.toUpper);
745 	}
746 
747 	void printLabels() {
748 		writeln("Labels:");
749 		foreach(Label l; labels) {
750 			writeln("\t", l.name, "@", l.offset);
751 		}
752 	}
753 }
754 
755 public string binToASMDESC(ubyte[] data) {
756 	Instruction[] instructions = (cast(Instruction*)data)[0..data.length/Instruction.sizeof];
757 	string oi = "";
758 	int line = 0;
759 	foreach(Instruction i; instructions) {
760 		oi ~= "\t" ~ to!string(line) ~ "\t" ~ to!string(i.opCode) ~ " " ~ to!string(i.data[0]) ~ " " ~ to!string(i.data[1]) ~ "\n";
761 		line++;
762 	}
763 	return oi;
764 }