Last active
April 25, 2016 09:41
-
-
Save Sunjammer/48147b558c1a2f2c6208b337841f45b4 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
package; | |
import haxe.ds.Vector; | |
using StringTools; | |
using Std; | |
enum Value | |
{ | |
Literal(v:Int); | |
Location(v:Int); | |
Relative(v:Int); | |
AReg; | |
XReg; | |
YReg; | |
} | |
/** | |
* Syntax: | |
* INSTRUCTION ARG0 ... ARGN | |
* ;Comment | |
* alias word value | |
* label: | |
* int values only | |
* int value prefix with # is a memory address | |
* int value prefix with @ is a relative program counter offset | |
* A, X and Y reference registers | |
* A is accumulator, used for results | |
*/ | |
enum Instruction | |
{ | |
LDA(v:Value); //Load value into A | |
LDX(v:Value); //Load value into X | |
LDY(v:Value); //Load value into Y | |
STA(v:Value); //Store A at memory | |
STX(v:Value); //Store X at memory | |
STY(v:Value); //Store Y at memory | |
PHA; //Push A on stack | |
PLA; //Pull A from stack | |
TAX; // Copy A to X | |
TXA; // Copy X to A | |
TAY; // Copy A to Y | |
TYA; // Copy Y to A | |
TXY; // Copy X to Y | |
TYX; // Copy Y to X | |
SUB(v:Value); //Subtract value from A | |
ADD(v:Value); //Add value to A | |
BNE(v:Value, pos:Value); //Branch to pos if v != A | |
BEQ(v:Value, pos:Value); //Branch to pos if v == A | |
BLT(v:Value, pos:Value); //Branch to pos if v < A | |
BGT(v:Value, pos:Value); //Branch to pos if v > A | |
JMP(v:Value); //Set program counter for next instruction | |
JSR(v:Value); //Begin subroutine | |
RTS; //Return from subroutine | |
RTI; //Return from interrupt | |
AND(v:Value); //& v with A and put the result in A | |
IOR(v:Value); //| v with A and put the result in A | |
XOR(v:Value); //^ v with A and put the result in A | |
LSH(v:Value); //Leftshift A by v and put the result in A | |
RSH(v:Value); //Rightshift A by v and put the result in A | |
BRK; //End program | |
NOP; //No operation | |
/* Debug stuff */ | |
TRC(v:Value); //Print value | |
MEM(a:Value, b:Value); //Print memory from A to B | |
} | |
typedef Program = Vector<Instruction>; | |
class Machine | |
{ | |
static inline var MEMSIZE = 512; | |
static inline var STACK_OFFSET = MEMSIZE-128; | |
public var name(default, null):String; | |
public var cycleCount(default, null):Int; | |
public var program(default, null):Program; | |
var memory = new Vector<Int>(MEMSIZE); | |
var A = 0; | |
var X = 0; | |
var Y = 0; | |
var pc = -1; | |
var stack = 0; | |
public function new(name:String) | |
{ | |
this.name = name; | |
reset(); | |
} | |
public function setMemory(mem:Vector<Int>) | |
{ | |
memory = mem; | |
} | |
public function load(prog:Program) | |
{ | |
program = prog; | |
} | |
public function isRunning():Bool { | |
return pc != -1; | |
} | |
public inline function getValue(v:Value):Int { | |
return switch(v) { | |
case Relative(v): | |
pc + (v-1); | |
case Literal(v): | |
v; | |
case Location(l): | |
memory[l]; | |
case AReg: | |
A; | |
case XReg: | |
X; | |
case YReg: | |
Y; | |
} | |
} | |
public function reset() | |
{ | |
for (i in 0...MEMSIZE) { | |
memory[i] = 0; | |
} | |
A = X = Y = stack = cycleCount = pc = 0; | |
} | |
function printMem(a:Int = 0, b:Int = MEMSIZE) | |
{ | |
trace("Memory:"); | |
for (i in a...b) | |
{ | |
trace("\t"+ i + "\t0x" + StringTools.hex(memory[i], 2)); | |
} | |
} | |
function pushStack(v:Int) | |
{ | |
if (STACK_OFFSET + stack + 1 > MEMSIZE) throw "Stack overflow"; | |
memory[STACK_OFFSET + stack] = v; | |
stack++; | |
} | |
function popStack():Int | |
{ | |
if (stack == 0) throw "Attempted to pop empty stack"; | |
var v = memory[STACK_OFFSET + stack-1]; | |
stack--; | |
return v; | |
} | |
public function step(instruction:Instruction):Int | |
{ | |
pc++; | |
cycleCount++; | |
switch(instruction) | |
{ | |
case LDA(v): | |
A = getValue(v); | |
case LDX(v): | |
X = getValue(v); | |
case LDY(v): | |
Y = getValue(v); | |
case STA(v): | |
memory[getValue(v)] = A; | |
case STX(v): | |
memory[getValue(v)] = X; | |
case STY(v): | |
memory[getValue(v)] = Y; | |
case TAX: | |
X = A; | |
case TXA: | |
A = X; | |
case TAY: | |
Y = A; | |
case TYA: | |
A = Y; | |
case TXY: | |
Y = X; | |
case TYX: | |
X = Y; | |
case SUB(v): | |
A -= getValue(v); | |
case ADD(v): | |
A += getValue(v); | |
case JMP(v): | |
pc = getValue(v); | |
case TRC(v): | |
trace(getValue(v)); | |
case BNE(v, pos): | |
if(getValue(v) != A) pc = getValue(pos); | |
case BEQ(v, pos): | |
if(getValue(v) == A) pc = getValue(pos); | |
case BLT(v, pos): | |
if(getValue(v) < A) pc = getValue(pos); | |
case BGT(v, pos): | |
if(getValue(v) > A) pc = getValue(pos); | |
case AND(a): | |
A = A & getValue(a); | |
case IOR(a): | |
A = A | getValue(a); | |
case XOR(a): | |
A = A ^ getValue(a); | |
case LSH(a): | |
A = A << getValue(a); | |
case RSH(a): | |
A = A >> getValue(a); | |
case JSR(v): | |
pushStack(pc); | |
pc = getValue(v); | |
case RTS|RTI: | |
pc = popStack(); | |
case BRK: | |
pc = -1; | |
case MEM(a, b): | |
printMem(getValue(a), getValue(b)); | |
case NOP: | |
//Do nothing | |
case _: | |
throw "Unsupported instruction " + instruction; | |
} | |
return pc; | |
} | |
public function next(#if neko interactive:Bool = false #end) | |
{ | |
var instruction:Instruction; | |
#if neko | |
if (program == null || program.length == 0) | |
{ | |
Sys.stdout().writeString("? "); | |
instruction = Assembler.parseLine(Sys.stdin().readLine(), pc); | |
}else{ | |
#end | |
instruction = program[pc]; | |
#if neko | |
} | |
if (interactive) { | |
trace("-> " + instruction); | |
Sys.stdin().readLine(); | |
} | |
#end | |
pc = step(instruction); | |
} | |
public function run(program:Program = null #if neko, interactive:Bool = false #end) { | |
this.program = program; | |
reset(); | |
try { | |
while (isRunning()) { | |
next(interactive); | |
} | |
}catch (e:Dynamic) { | |
trace("Program crashed at " + pc + ": "+e); | |
} | |
} | |
} | |
class Assembler | |
{ | |
public static function assemble(source:String):Program | |
{ | |
var rawlines = source.trim().split("\n"); | |
//label, comment and trim prepass | |
var lines:Array<String> = []; | |
var aliases = new Map<String, String>(); | |
var labels = new Map<String,String>(); | |
var count = 0; | |
for (i in 0...rawlines.length) { | |
var line = rawlines[i].trim(); | |
var str = ""; | |
for (i in 0...line.length) | |
{ | |
var char = line.charAt(i); | |
if (char == ";") break; | |
str += char; | |
} | |
line = str.rtrim(); | |
if (line.length == 0) continue; | |
if (line.indexOf(":") > -1) { | |
labels[line.substr(0, line.length - 1)] = count+""; | |
continue; | |
} | |
if (line.indexOf("alias") >-1) { | |
var components = line.split(" "); | |
components.shift(); | |
aliases[components[0]] = components[1]; | |
continue; | |
} | |
count++; | |
lines.push(line); | |
} | |
for (i in 0...lines.length) { | |
var line = lines[i]; | |
for (label in labels.keys()) { | |
var idx = line.indexOf(label); | |
if (idx >-1) { | |
lines[i] = line.replace(label, labels[label]); | |
} | |
} | |
for (alias in aliases.keys()) { | |
var idx = line.indexOf(alias); | |
if (idx >-1) { | |
lines[i] = line.replace(alias, aliases[alias]); | |
} | |
} | |
} | |
//Generate instructions | |
var out = new Program(lines.length); | |
for (i in 0...lines.length) { | |
out[i] = parseLine(lines[i], i); | |
} | |
return out; | |
} | |
public static function parseLine(line:String, lineNo:Int):Instruction { | |
var tokens = line.split(" "); | |
var operator = tokens.shift(); | |
try { | |
return Instruction.createByName(operator, [for(t in tokens) toValue(t)] ); | |
}catch (e:Dynamic){ | |
throw "Syntax error " + operator+" at line "+lineNo; | |
} | |
} | |
static inline function toValue(str:String):Value { | |
var firstChar:String = str.charAt(0).toLowerCase(); | |
return switch(firstChar) { | |
case "@": | |
Relative(str.substr(1).parseInt()); | |
case "#": | |
Location(str.substr(1).parseInt()); | |
case "a": | |
AReg; | |
case "x": | |
XReg; | |
case "y": | |
YReg; | |
case _: | |
Literal(str.parseInt()); | |
} | |
} | |
} | |
class Main | |
{ | |
static function main() | |
{ | |
var src = " | |
start: | |
LDA 0 | |
LDX 5 | |
LDY iteration1 | |
JSR whileloop | |
LDA 0 | |
LDX 10 | |
LDY iteration2 | |
JSR whileloop | |
JMP end | |
whileloop: | |
ADD 1 | |
BLT X @3 | |
JSR Y | |
JMP @-3 | |
TAX | |
RTS | |
iteration1: | |
TRC 1 | |
RTS | |
iteration2: | |
TRC 2 | |
RTS | |
end: | |
TRC -1 | |
BRK | |
"; | |
//run(null); | |
var a = new Machine("Alpha"); | |
var b = new Machine("Beta"); | |
var machines = [a, b]; | |
var memory = new Vector<Int>(512); | |
var prog = Assembler.assemble(src); | |
for (m in machines) { | |
m.setMemory(memory); | |
m.load(prog); | |
} | |
while (a.isRunning() || b.isRunning()) { | |
a.next(); | |
b.next(); | |
} | |
//printMem(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment