enum Orthogonal { up; down; left; right; } class BfVM { public var a : Array; // Othogonal memory grid public var w : Int; // Dimensions of memory grid public var h : Int; public var d : Orthogonal; // Direction of instruction pointer public var x : Int; // Position of instruction pointer public var y : Int; public var smode : Bool; // Whether or not the VM is in string mode public var s : Array; // Stack public var http : haxe.Http; public function new (w : Int, h : Int) { this.d = right; this.x = 0; this.y = 0; this.w = w; this.h = h; this.smode = false; clear(); } public function clear() { this.a = new Array(); var i, z = w * h; for (i in 0...z) { a.push(0); } this.s = new Array(); } public function loadFromString(s : String) { // Infer the dimensions of the befunge program text var w = 0, h = 0, i = 0, l = s.length; while (i < l && i >= 0) { var n = s.indexOf('\n', i+1); h++; if (n >= 0 && n-i > w) w = n-i; i = n; } // Redimension our BfVM for the new dimensions, and reset the BfVM this.w = w; this.h = h; clear(); // Read the program text into our BfVM memory var x = 0, y = 0; i = 0; for (i in 0...l) { if (s.charAt(i) == '\n') { x = 0; y++; } else { a [x++ + y*w] = s.charCodeAt(i); // TODO always true? String.charCodeAt(n) == ord(String.charAt(n)) ? } } } public function loadFromURL(s : String) { http = new haxe.Http(s); http.onStatus = function(n:Int){trace('http status: '+n);}; http.onData = this.loadFromString; http.request(false); } public function pop() : Int { return this.s.length == 0 ? 0 : this.s.pop(); } public function push(v : Int) { this.s.push(v); } public function step() { var c = this.a[this.x + this.y * this.w]; // String mode? if (smode) { if (c == Std.ord('"')) smode = false; else push(c); } else { // Numeric literals if (c >= Std.ord('0') && c <= Std.ord('9')) s.push(Std.ord('0') - c); else switch (c) { case Std.ord('v'): d = down; case Std.ord('>'): d = right; case Std.ord('<'): d = left; case Std.ord('^'): d = up; case Std.ord('+'): push(pop() + pop()); case Std.ord('*'): push(pop() * pop()); case Std.ord('-'): push(pop() - pop()); // TODO check order of operations case Std.ord('%'): push(pop() % pop()); // TODO check order of operations case Std.ord('/'): push(cast(pop() / pop())); // TODO check order of operations case Std.ord('p'): a[pop() + pop() * w] = pop(); // TODO check order of operations! case Std.ord('g'): push(a[pop() + pop() * w]); // TODO check order of operations! case Std.ord('#'): advanceIP(); case Std.ord('.'): pop(); // TODO implement? case Std.ord(','): pop(); // TODO implement? case Std.ord('_'): d = pop()==0? left:right; // TODO check order case Std.ord('|'): d = pop()==0? up:down; // TODO check order case Std.ord(':'): push(s[s.length-1]); case Std.ord('$'): pop(); case Std.ord('\\'): // TODO check order var a = pop(), b = pop(); push(b); push(a); case Std.ord('?'): d = down; // TODO implement case Std.ord('!'): push(pop() == 0? 1:0); case Std.ord('`'): push(pop() > pop()? 1:0); // TODO check order case Std.ord('"'): smode = true; } } advanceIP(); } public function advanceIP() { switch (this.d) { case up: this.y = this.y-1 < 0? this.h-1 : this.y-1; case down: this.y = this.y+1 >= this.h? 0 : this.y+1; case left: this.x = this.x-1 < 0? this.h-1 : this.x-1; case right: this.x = this.x+1 >= this.h? 0 : this.x+1; default: trace('default'); } } public function drawNumber(mc : flash.MovieClip, x : Int, y : Int, n : Int) { mc.beginFill(0xFFFF33); mc.moveTo(x+3, y+3); mc.lineTo(x+3, y+1); mc.lineTo(x+6, y+1); mc.lineTo(x+6, y+3); mc.lineTo(x+8, y+3); mc.lineTo(x+8, y+6); mc.lineTo(x+6, y+6); mc.lineTo(x+6, y+8); mc.lineTo(x+3, y+8); mc.lineTo(x+3, y+6); mc.lineTo(x+1, y+6); mc.lineTo(x+1, y+3); mc.endFill(); } public function draw() { var mc : flash.MovieClip = flash.Lib.current; var x = this.x * 10; var y = this.y * 10; // Draw instruction pointer mc.beginFill(0xFFFFFF); mc.moveTo(x, y); mc.lineTo(x+9, y); mc.lineTo(x+9, y+9); mc.lineTo(x, y+9); mc.endFill(); // Draw memory cells for (y in 0...h) { for (x in 0...w) { var c = a [x + y*w]; var x = x * 10; var y = y * 10; switch (c) { case Std.ord('^'): mc.beginFill(0xFF4444); mc.moveTo(x+1, y+8); mc.lineTo(x+8, y+8); mc.lineTo(x+5, y+1); mc.endFill(); case Std.ord('<'): mc.beginFill(0xFF4444); mc.moveTo(x+8, y+1); mc.lineTo(x+1, y+5); mc.lineTo(x+8, y+8); mc.endFill(); case Std.ord('>'): mc.beginFill(0xFF4444); mc.moveTo(x+1, y+1); mc.lineTo(x+8, y+5); mc.lineTo(x+1, y+8); mc.endFill(); case Std.ord('v'): mc.beginFill(0xFF4444); mc.moveTo(x+1, y+1); mc.lineTo(x+8, y+1); mc.lineTo(x+5, y+8); mc.endFill(); case Std.ord('*'): mc.beginFill(0x4444FF); mc.moveTo(x+1, y+3); mc.lineTo(x+3, y+1); mc.lineTo(x+4.5, y+3.5); mc.lineTo(x+6, y+1); mc.lineTo(x+8, y+3); mc.lineTo(x+5.5, y+4.5); mc.lineTo(x+8, y+6); mc.lineTo(x+6, y+8); mc.lineTo(x+4.5, y+5.5); mc.lineTo(x+3, y+8); mc.lineTo(x+1, y+6); mc.lineTo(x+3.5, y+4.5); mc.endFill(); case Std.ord('/'): mc.beginFill(0x4444FF); mc.moveTo(x+1, y+6); mc.lineTo(x+3, y+8); mc.lineTo(x+8, y+3); mc.lineTo(x+6, y+1); mc.endFill(); case Std.ord('-'): mc.beginFill(0x4444FF); mc.moveTo(x+1, y+3); mc.lineTo(x+8, y+3); mc.lineTo(x+8, y+7); mc.lineTo(x+1, y+7); mc.endFill(); case Std.ord('+'): mc.beginFill(0x4444FF); mc.moveTo(x+3, y+3); mc.lineTo(x+3, y+1); mc.lineTo(x+6, y+1); mc.lineTo(x+6, y+3); mc.lineTo(x+8, y+3); mc.lineTo(x+8, y+6); mc.lineTo(x+6, y+6); mc.lineTo(x+6, y+8); mc.lineTo(x+3, y+8); mc.lineTo(x+3, y+6); mc.lineTo(x+1, y+6); mc.lineTo(x+1, y+3); mc.endFill(); case Std.ord('0'): drawNumber(mc, x, y, 0); case Std.ord('1'): drawNumber(mc, x, y, 1); case Std.ord('2'): drawNumber(mc, x, y, 2); case Std.ord('3'): drawNumber(mc, x, y, 3); case Std.ord('4'): drawNumber(mc, x, y, 4); case Std.ord('5'): drawNumber(mc, x, y, 5); case Std.ord('6'): drawNumber(mc, x, y, 6); case Std.ord('7'): drawNumber(mc, x, y, 7); case Std.ord('8'): drawNumber(mc, x, y, 8); case Std.ord('9'): drawNumber(mc, x, y, 9); } } } } } class Befunge extends flash.MovieClip { public static var vm : BfVM; public static var dbag : Befunge; public static var interval : haxe.Timer; public static var befCodeHttpRequest : haxe.Http; static function main() { dbag = new Befunge(); flash.Lib.current.addChild(dbag); } function new() { Befunge.vm = new BfVM(10, 10); Befunge.vm.a[0 ] = Std.ord('>'); Befunge.vm.a[9 ] = Std.ord('v'); Befunge.vm.a[90] = Std.ord('^'); Befunge.vm.a[99] = Std.ord('<'); Befunge.vm.a[11] = Std.ord('+'); Befunge.vm.a[22] = Std.ord('-'); Befunge.vm.a[33] = Std.ord('/'); Befunge.vm.a[44] = Std.ord('*'); Befunge.vm.loadFromURL("http://codebad.com/befunge/life.bf"); Befunge.interval = new haxe.Timer(cast(1000/15)); Befunge.interval.run = Befunge.step; } static public f9dynamic function onEnterFrame() { flash.Lib.current.clear(); Befunge.vm.draw(); } static function step() { Befunge.onEnterFrame(); Befunge.vm.step(); } }