In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

Spacewar! (DEC PDP-1 emulator)

Get Adobe Flash player
by toyoshim 26 Sep 2009
/**
 * Copyright toyoshim ( http://wonderfl.net/user/toyoshim )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/orza
 */

package {
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.BlendMode;
    import flash.display.DisplayObject;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.geom.ColorTransform;
    import flash.geom.Matrix;
    import flash.geom.Rectangle;
    import flash.net.URLLoader;
    import flash.net.URLLoaderDataFormat;
    import flash.net.URLRequest;
    import com.flashdynamix.utils.SWFProfiler;

    public class Main extends Sprite {
        private const rect   : Rectangle = new Rectangle(0, 0, 465, 465);

        private var   canvas : BitmapData;
        private var   trans  : ColorTransform;
        private var   mat    : Matrix;
        private var   loader : URLLoader;
        
        private var   rim    : Rim;
        private var   pdp1   : PDP1;
        private var   disp   : Display;
        private var   pad    : Gamepad;
		
        [SWF(width="465", height="465", frameRate="15")]
        public function Main() : void
        {
            Wonderfl.disable_capture();
            SWFProfiler.init(this);

            // initialize graphic resource
            canvas = new BitmapData(465, 465, false, 0x000000);
            trans  = new ColorTransform(1, 0.7, 1);
            mat    = new Matrix();
            mat.scale(465 / 1024, 465 / 1024);
            addChild(new Bitmap(canvas));

            // kick rim data loader
            var url : String = "http://wonderfl.toyoshima-house.net/spacewar.rim";
            loader = new URLLoader();
            loader.dataFormat = URLLoaderDataFormat.BINARY;
            loader.load(new URLRequest(url));
            loader.addEventListener(Event.COMPLETE, RimLoad);
        }

        private function RimLoad (event : Event) : void {
            // load rim data
            var rim_file : Array;
            var rim_size : int;
            rim_size = loader.bytesTotal;
            rim_file = new Array(rim_size);
            var i : int;
            for (i = 0; i < rim_size; i++) {
                rim_file[i] = loader.data.readUnsignedByte();
            }

            // initialize PDP-1 memory
            var mem : Array;
            mem = new Array(4096);
            for (i = 0; i < 4096; i++) {
                mem[i] = 0;
            }

            // boot from rim
            rim = new Rim();
            rim.Load(rim_file, rim_size);
            var pc : int = rim.Boot(mem);
            
            // initialize display
            disp = new Display();

            // start keyboard emulation
            pad = new Gamepad();
            stage.addEventListener(KeyboardEvent.KEY_DOWN, KeyDown);
            stage.addEventListener(KeyboardEvent.KEY_UP  , KeyUp  );

            // reset PDP-1 CPU core
            pdp1 = new PDP1();
            pdp1.Reset(pc, mem, rim, disp, pad);
            
            // start screen emulation
            addEventListener(Event.ENTER_FRAME, Update);
        }
        
        private function KeyDown (e : KeyboardEvent) : void {
            switch (e.charCode) {
                case 0x41: // A
                case 0x61: // a
                    pad.Set(2); // 1: rotate L
                    break;
                case 0x53: // S
                case 0x73: // s
                    pad.Set(3); // 1: rotate R
                    break;
                case 0x44: // D
                case 0x64: // d
                    pad.Set(1); // 1: engines
                    break;
                case 0x46: // F
                case 0x66: // f
                    pad.Set(0); // 1: torpedos
                    break;
                case 0x48: // H
                case 0x68: // h
                    pad.Set(16); // 2: rotate L
                    break;
                case 0x4a: // J
                case 0x6a: // j
                    pad.Set(17); // 2: rotate R
                    break;
                case 0x4b: // k
                case 0x6b: // K
                    pad.Set(15); // 2: engines
                    break;
                case 0x4c: // l
                case 0x6c: // L
                    pad.Set(14); // 2: torpedos
                    break;
            }
        }
        
        private function KeyUp (e : KeyboardEvent) : void {
            switch (e.charCode) {
                case 0x41: // A
                case 0x61: // a
                    pad.Reset(2); // 1: rotate R
                    break;
                case 0x53: // S
                case 0x73: // s
                    pad.Reset(3); // 1: rotate L
                    break;
                case 0x44: // D
                case 0x64: // d
                    pad.Reset(1); // 1: engines
                    break;
                case 0x46: // F
                case 0x66: // f
                    pad.Reset(0); // 1: torpedos
                    break;
                case 0x48: // H
                case 0x68: // h
                    pad.Reset(16); // 2: rotate R
                    break;
                case 0x4a: // J
                case 0x6a: // j
                    pad.Reset(17); // 2: rotate L
                    break;
                case 0x4b: // k
                case 0x6b: // K
                    pad.Reset(15); // 2: engines
                    break;
                case 0x4c: // l
                case 0x6c: // L
                    pad.Reset(14); // 2: torpedos
                    break;
            }
        }
        
        private function Update (e : Event) : void
        {
            disp.Lock();
            
            for (var i : int = 0; i < 10000; i++) {
                pdp1.Step();
            }
            disp.Unlock();
            
            canvas.lock();
            canvas.colorTransform(rect, trans);
            canvas.draw(disp.Bitmap(), mat, null, BlendMode.ADD);
            canvas.unlock();
        }
    }	
}

    import flash.display.BitmapData;
    import flash.geom.Rectangle;
    import flash.geom.Point;
    import flash.filters.BlurFilter;


    internal class Rim {
        private var rim : Array;
        private var rim_size : int;
        private var rim_offset : int;
    
        public function Load (_rim : Array, size : int) : void {
            rim = _rim;
            rim_size = size;
            rim_offset = 0;
        }
        
        public function Boot (mem : Array) : int {
            for (;; ) {
                var cmd : int = Get();
                var ins : int = cmd >> 12;
                var adr : int = cmd & 0xfff;
                if (cmd < 0) return -1;
                switch(ins) {
                    case 0x1A: /* 032 */
                        cmd = Get();
                        trace("rim: mem[" + TraceUtil.to06o(adr) + "] := " + TraceUtil.to06o(cmd));
                        mem[adr] = cmd;
                        break;
                    case 0x30: /* 60 */
                        trace("rim: org := " + TraceUtil.to06o(adr));
                        return adr;
                    default:
                        trace("rim: unexpected instruction " + ins + " " + ((cmd >> 6) & 0x3f) + " " + (cmd & 0x3f));
                        return -2;
                }
            }
            return 0;
        }
        
        public function Get () : int {
            var result : int;
            
            for (;; ) {
                if ((rim_offset + 1) >= rim_size) return -1;
                if (0 != (rim[rim_offset] & 0x80)) break;
                rim_offset++;
            }
            if ((rim_offset + 3) >= rim_size) return -1;
            if ((0 == (0x80 & (rim[rim_offset + 0] &
                               rim[rim_offset + 1] &
                               rim[rim_offset + 2]))) ||
                (0 != (0x40 & (rim[rim_offset + 0] |
                               rim[rim_offset + 1] |
                               rim[rim_offset + 2])))) {
                trace("rim error: " + rim[rim_offset + 0].toString(8) + " " +
                                      rim[rim_offset + 1].toString(8) + " " +
                                      rim[rim_offset + 2].toString(8));
                return -2;
            }
            result = ((rim[rim_offset + 0] & 0x3f) << 12) |
                     ((rim[rim_offset + 1] & 0x3f) <<  6) |
                     ((rim[rim_offset + 2] & 0x3f) <<  0);
            rim_offset += 3;
            return result;
        }
    }

    internal class PDP1 {
        private var pc   : int;
        private var ir   : int;
        private var y    : int;
        private var inst : int;
        private var ind  : int;
        private var io   : int;
        private var xct  : int;
        private var ac   : int;
        private var ov   : int;
        private var dump : int;
        private var test : int;
        private var pf   : int;
        private var cm   : Array;
        private var rim  : Rim;
        private var disp : Display;
        private var pad  : Gamepad;
        
        public function Reset(_pc : int, _cm : Array, _rim : Rim, _disp : Display, _pad : Gamepad) : void {
            pc   = _pc;
            ir   = 0;
            y    = 0;
            inst = 0;
            ind  = 0;
            io   = 0;
            xct  = 0;
            ac   = 0;
            ov   = 0;
            dump = 0;
            test = 0;
            pf   = 0;
            cm   = _cm;
            rim  = _rim;
            disp = _disp;
            pad  = _pad;
        }
        
        public function Step() : int {
            var tmp : int;
            
            if (xct == 0) {
                //Trace();
                ir = cm[pc];
                pc = pc + 1;
            } else {
                ir = cm[y];
                xct = 0;
            }
            inst = (ir >> 12) & 0x3e;
            ind  = (ir >> 12) & 1;
            y    = ir & 0x0fff;
            
            switch (inst) {
                case 0x02: // 002 and
                    Op();
                    ac &= cm[y];
                    break;
                case 0x04: // 004 ior
                    Op();
                    ac |= cm[y];
                    break;
                case 0x06: // 006 xor
                    Op();
                    ac ^= cm[y];
                    break;
                case 0x08: // 010 xct
                    Op();
                    xct = 1;
                    break;
                case 0x0e: // 016
                    if (0 == ind) {
                        // cal
                        cm[0x040] = ac;
                        ac = (ov << 17) + pc;
                        pc = 0x41;
                    } else {
                        // jda
                        cm[y] = ac;
                        ac = (ov << 17) + pc;
                        pc = y + 1;
                    }
                    break;
                case 0x10: // 020 lac
                    Op();
                    ac = cm[y];
                    break;
                case 0x12: // 022 lio
                    Op();
                    io = cm[y];
                    break;
                case 0x14: // 024 dac
                    Op();
                    cm[y] = ac;
                    break;
                case 0x16: // 026 dap
                    Op();
                    cm[y] = (cm[y] & 0x3f000) | (ac & 0x00fff);
                    break;
                case 0x1a: // 032 dio
                    Op();
                    cm[y] = io;
                    break;
                case 0x1c: // 034 dzm
                    Op();
                    cm[y] = 0;
                    break;
                case 0x20: // 040 add
                    Op();
                    tmp = ac;
                    ac += cm[y];
                    if (ac > 0x3ffff) ac = (ac + 1) & 0x3ffff;
                    if (0 != ((cm[y] ^ ~tmp) & (ac ^ tmp) & 0x20000)) ov |= 1;
                    if (0x3ffff == ac) ac = 0;
                    break;
                case 0x22: // 042 sub
                    Op();
                    tmp = ac ^ 0x3ffff;
                    ac = tmp + cm[y];
                    if (ac > 0x3ffff) ac = (ac + 1) & 0x3ffff;
                    if (0 != ((cm[y] ^ ~tmp) & (ac ^ tmp) & 0x20000)) ov |= 1;
                    ac ^= 0x3ffff;
                    break;
                case 0x24: // 044 idx
                    Op();
                    ac = cm[y] + 1;
                    if (0x3ffff <= ac) ac = (ac + 1) & 0x3ffff;
                    cm[y] = ac;
                    break;
                case 0x26: // 046 isp
                    Op();
                    ac = cm[y] + 1;
                    if (0x3ffff <= ac) ac = (ac + 1) & 0x3ffff;
                    cm[y] = ac;
                    if (0 == (ac & 0x20000)) pc++;
                    break;
                case 0x28: // 050 sad
                    Op();
                    if (cm[y] != ac) pc++;
                    break;
                case 0x2a: // 052 sas
                    Op();
                    if (cm[y] == ac) pc++;
                    break;
                case 0x2c: // 054 mus
                    Op();
                    if (0 != (io & 1)) {
                        ac += cm[y];
                        ac += (ac >> 18);
                        ac &= 0x3ffff;
                        if (0x3ffff == ac) ac = 0;
                    }
                    io = ((io >> 1) | (ac << 17)) & 0x3ffff;
                    ac >>= 1;
                    break;
                case 0x2e: // 056 div
                    Op();
                    tmp = ac >> 17;
                    ac = ((ac << 1) | (io >> 17)) & 0x3ffff;
                    io = ((io << 1) | (tmp ^ 1)) & 0x3ffff;
                    if (0 != (io & 1)) ac = ac + (cm[y] ^ 0x3ffff);
                    else ac = ac + cm[y] + 1;
                    if (ac > 0x3ffff) ac = (ac + 1) & 0x3ffff;
                    if (0x3ffff == ac) ac = 0;
                    break;
                case 0x30: // 060 jmp
                    Op();
                    pc = y;
                    break;
                case 0x32: // 062 jsp
                    Op();
                    ac = (ov << 17) + pc;
                    pc = y;
                    break;
                case 0x34: // 064 skp
                    return SKP();
                case 0x36: // 066 sft
                    return SFT();
                case 0x38: // 070 law
                    ac = (0 == ind)? y: (y ^ 0x3ffff);
                    break;
                case 0x3a: // 072 iot
                    tmp = IOT();
                    //pc--;
                    //Trace();
                    //pc++;
                    return tmp;
                case 0x3e: // 076 opr
                    return OPR();
                default:
                    trace("!!! error !!! : unknown instruction " + TraceUtil.to02o(inst + ind));
                    return -1;
            }
            return 0;
        }
        
        private function Op () : void {
            while (0 != ind) {
                ir  = cm[y];
                ind = (ir >> 12) & 1;
                y   = ir & 0xfff;
            }
        }
        
        private function SKP () : int {
            var skip : int = 0;
            if (((0 != (y & 0x040)) && (0 ==  ac           )) ||
                ((0 != (y & 0x080)) && (0 == (ac & 0x20000))) ||
                ((0 != (y & 0x100)) && (0 != (ac & 0x20000))) ||
                ((0 != (y & 0x200)) && (0 ==  ov           )) ||
                ((0 != (y & 0x400)) && (0 == (io & 0x20000)))) {
                skip = 1;
            }
            if (0 != (y & 0x038)) {
                //trace("skip on switch");
                skip = 1;
            }
            if (0 != (y & 0x007)) {
                var flags : Array = [ 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x3f ];
                if (0 == (pf & flags[y & 007])) skip = 1;
            }
            if (0 != (y & 0x200)) {
                ov = 0;
            }
            if (0 != (skip ^ ind)) {
                pc++;
            }
            if (0 != (y & 0x800)) {
                trace("!!! error !!! : skip on unknown flags");
                return -1;
            }
            return 0;
        }
        
        private function SFT () : int {
            var mode : int = (ind << 3) | (y >> 9);
            var n    : int = PopCount(y & 0x1ff);
            var tmp  : int;
            switch (mode) {
                case 0x01: // 001 ral
                    ac  = ((ac << n) | (ac >> (18 - n))) & 0x3ffff;
                    break;
                case 0x02: // 002 ril
                    io  = ((io << n) | (io >> (18 - n))) & 0x3ffff;
                    break;
                case 0x03: // 003 rcl
                    tmp = ((ac << n) | (io >> (18 - n))) & 0x3ffff;
                    io  = ((io << n) | (ac >> (18 - n))) & 0x3ffff;
                    ac  = tmp;
                    break;
                case 0x05: // 005 sal
                    tmp = (0 != (ac & 0x20000))? 0x3ffff: 0;
                    ac  = (ac & 0x20000) | ((ac << n) & 0x1ffff) | (tmp >> (18 - n));
                    break;
                case 0x06: // 006 sil
                    tmp = (0 != (io & 0x20000))? 0x3ffff: 0;
                    io  = (io & 0x20000) | ((io << n) & 0x1ffff) | (tmp >> (18 - n));
                    break;
                case 0x07: // 007 scl
                    tmp = (0 != (ac & 0x20000))? 0x3ffff: 0;
                    ac  = (ac & 0x20000) | ((ac << n) & 0x1ffff) | (io  >> (18 - n));
                    io  =                  ((io << n) & 0x3ffff) | (tmp >> (18 - n));
                    break;
                case 0x09: // 011 rar
                    ac  = ((ac >> n) | (ac << (18 - n))) & 0x3ffff;
                    break;
                case 0x0a: // 012 rir
                    io  = ((io >> n) | (io << (18 - n))) & 0x3ffff;
                    break;
                case 0x0b: // 013 rcr
                    tmp = ((ac >> n) | (io << (18 - n))) & 0x3ffff;
                    io  = ((io >> n) | (ac << (18 - n))) & 0x3ffff;
                    ac  = tmp;
                    break;
                case 0x0d: // 015 sar
                    tmp = (0 != (ac & 0x20000))? 0x3ffff: 0;
                    ac  = ((ac >> n) | (tmp << (18 - n))) & 0x3ffff;
                    break;
                case 0x0e: // 016 sar
                    tmp = (0 != (io & 0x20000))? 0x3ffff: 0;
                    io  = ((io >> n) | (tmp << (18 - n))) & 0x3ffff;
                    break;
                case 0x0f: // 017 scr
                    tmp = (0 != (ac & 0x20000))? 0x3ffff: 0;
                    io  = ((io >> n) | (ac  << (18 - n))) & 0x3ffff;
                    ac  = ((ac >> n) | (tmp << (18 - n))) & 0x3ffff;
                    break;
                default:
                    trace("!!! error !!! : unknown shift operation: " + TraceUtil.to02o(mode));
                    return -1;
            }
            return 0;
        }
        
        private function OPR () : int {
            var cmd : int = y;
            if (0 != (cmd & 0x080)) {
                // cla
                ac = 0;
                cmd &= ~0x080;
            }
            if (0 != (cmd & 0x100)) {
                // hlt
                trace("hlt");
                cmd &= ~0x100;
            }
            if (0 != (cmd & 0x200)) {
                // cma
                ac = ac ^ 0x3ffff;
                cmd &= ~0x200;
            }
            if (0 != (cmd & 0x400)) {
                // lat
                ac |= test;
                cmd &= ~0x400;
            }
            if (0 != (cmd & 0x800)) {
                // cli
                io = 0;
                cmd &= ~0x800;
            }
            if (0 != (cmd & 0x007)) {
                var flags : Array = [ 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, 0x3f ];
                if (0 != (cmd & 0x008)) {
                    pf |= flags[cmd & 0x007];
                } else {
                    pf &= ~flags[cmd & 0x007];
                }
                cmd &= ~0x00f;
            }
            if (0 != cmd) {
                trace("!!! error !!! : unknown operation: " + TraceUtil.to04o(cmd));
                return -1;
            }
            return 0;
        }
        
        private function IOT () : int {
            switch (y & 0x3f) {
                case 0x00: // 000
                    break;
                case 0x02: // 002
                    io = rim.Get();
                    //trace("rpb -> " + TraceUtil.to06o(io));
                    if (io < 0) return -1;
                    break;
                case 0x07: // 007
                    disp.SetPoint(((ac >> 8) + 0x200) & 0x3ff, ((io >> 8) + 0x200) & 0x3ff);
                    //trace("display (" + TraceUtil.tod(ac) + ", " + TraceUtil.tod(io) + ")");
                    break;
                case 0x09: // 011
                    io = pad.Get();
                    //trace("spacewar -> " + TraceUtil.to04o(io));
                    break;
                default:
                    trace("!!! error !!! : unknown io device : " + TraceUtil.to04o(y));
                    return -1;
            }
            return 0;
        }
        
        private function PopCount (n : int) : int {
            var c : int = 0;
            var i : int;
            for (i = 0; i < 9; i++) {
                if (0 != (n & (1 << i))) {
                    c++;
                }
            }
            return c;
        }
        
        private function Trace () : void {
            trace("PC: "   + TraceUtil.to06o(pc)                 + ", " +
                  "IO: "   + TraceUtil.to06o(io)                 + ", " +
                  "AC: "   + TraceUtil.to06o(ac)                 + ", " +
                  "OV: "   + TraceUtil.tod(ov)                   + ", " +
                  "I: "    + TraceUtil.tod((cm[pc] >> 12) & 1)   + ", " +
                  "M[Y]: " + TraceUtil.to06o(cm[y])              + ", " +
                  "PF: "   + TraceUtil.to06o(pf));
        }
    }
    
    internal class Display {
        private const rect : Rectangle = new Rectangle(0, 0, 1024, 1024);
        private const zero : Point     = new Point(0, 0);
        private var   bmp  : BitmapData;
        private var   blur : BlurFilter;

        public function Display () : void {
            bmp  = new BitmapData(1024, 1024, false, 0x000000);
            blur = new BlurFilter(2, 2, 1);
        }

        public function Lock () : void {
            bmp.lock();
            bmp.fillRect(rect, 0x000000);
        }
        
        public function Unlock () : void {
            //bmp.applyFilter(bmp, rect, zero, blur);
            bmp.unlock();
        }
        
        public function Bitmap () : BitmapData {
            return bmp;
        }
        
        public function SetPoint (x : int, y : int) : void {
            bmp.setPixel(x, y, 0x00ff00);
        }
    }
    
    internal class Gamepad {
        private var sw : int = 0;
        
        public function Set (n : int) : void {
            sw |= (1 << n);
        }
        
        public function Reset (n : int) : void {
            sw &= ~(1 << n);    
        }
        
        public function Get () : int {
            return sw;
        }
    }
    
    internal class TraceUtil {
        static public function to02o (n : int) : String {
            return to0o(2, n);
        }
        
        static public function to04o (n : int) : String {
            return to0o(4, n);
        }
        
        static public function to06o (n : int) : String {
            return to0o(6, n);
        }
        
        static public function to0o (width : int, n : int) : String {
            var str : String = "00000000" + n.toString(8);
            return str.substr(str.length - width, width);
        }
        
        static public function tod (n : int) : String {
            return n.toString(10);
        }
    }