function _typeof( s )
{
    var r = typeof( s );
    if( r == "object" ){
        if( r instanceof Array ) return "array";
    }
    return r;
}

function _parseInt( v )
{
    var m, v;
    if( (m = /^\s*(\-|\+)?0x([\da-f]+)\s*$/i.exec( v ) ) != null ){
        v = parseInt( m[ 2 ], 16 );
        if( m[ 1 ] == "-" ) return -v; else return v;
    }
    if( (m = /^\s*(\-|\+)?(\d[\dA-Fa-f]*)\s*$/.exec( v ) ) != null ){
        v = parseInt( m[ 2 ], 16 );
        if( m[ 1 ] == "-" ) return -v; else return v;
    }
    if( (m = /^\s*(\-|\+)?(\d+)\s*$/.exec( v ) ) != null ){
        v = parseInt( m[ 2 ], 10 );
        if( m[ 1 ] == "-" ) return -v; else return v;
    }
    if( (m = /^\s*(\-|\+)?([\da-f]+)h\s*$/i.exec( v ) ) != null ){
        v = parseInt( m[ 2 ], 16 );
        if( m[ 1 ] == "-" ) return -v; else return v;
    }
    return null;
}

(function(){
    Number.prototype.toHex = function( digit ){
        var s = "";
        var r = this.toString( 16 );
        while( r.length < digit ){
            r = "0" + r;
        }
        return r;
    };
    Number.prototype.loByte = function(){
        return this & 0xff;
    };
    Number.prototype.hiByte = function(){
        return (this >> 8 ) & 0xff;
    };
})();

function Assembler(){
    var _err = "";
    var _labels = {};
    var _unknowns = [];
    var _pc = 0x100;
    var _mem = [];
    var checkOperandSize = function( operand ){
        var r1 = operand.operand1, r2 = operand.operand2;
		console.log( operand.toSource() );

        if( r1 !== undefined && r2 !== undefined ){
			if( r1.w !== undefined && r2.w != undefined && r1.w != r2.w ) throw new Error( "invalid operand" );
        	if( r1.w !== undefined ){
				r2.w = r1.w;
        	}else if( r2.w != undefined ){
				r1.w = r2.w;
			}
		}
    };
    var _opecode = {
        "mov_reg,imm"   : function( opecode, operand ){
                checkOperandSize( operand );
                var c = 0xb0;
                if( operand.operand1.w ) c |= 0x8;
                c |= operand.operand1.reg;
                _mem[ _pc++ ] = c;
                addExp( _pc, operand.operand2.value, operand.operand2.w );
                _mem[ _pc++ ] = undefined;
                if( operand.operand2.w ){
                    _mem[ _pc + 1 ] = undefined;
                    _pc++;
                }
            },
        "mov_acc,imm"   : function( opecode, operand ){
                return _opecode[ "mov_reg,imm" ]( opecode, operand );
            },
        "mov_reg,seg"   : function( opecode, operand ){
                checkOperandSize( operand );
                var c = 0x8c;
                _mem[ _pc++ ] = c;
                c = operand.operand1.mdXXXrm;
                c |= ( operand.operand2.reg << 3 );
                _mem[ _pc++ ] = c;
            },
        "mov_acc,seg"   : function( opecode, operand ){
                checkOperandSize( operand );
                return _opecode[ "mov_reg,seg" ]( opecode, operand );
            },
        "mov_seg,reg"   : function( opecode, operand ){
                checkOperandSize( operand );
                var c = 0x8e;
                _mem[ _pc++ ] = c;
                c = operand.operand2.mdXXXrm;
                c |= ( operand.operand1.reg << 3 );
                _mem[ _pc++ ] = c;
            },
        "mov_seg,acc"   : function( opecode, operand ){
                return _opecode[ "mov_seg,reg" ]( opecode, operand );
            },
        "mov_mem,reg"   : function( opecode, operand ){
                checkOperandSize( operand );
                var c = 0x88;
                if( operand.operand1.w || operand.operand2.w ) c |= 0x1;
                _mem[ _pc++ ] = c;
                c = operand.operand1.mdXXXrm;
                c |= ( operand.operand2.reg << 3 );
                _mem[ _pc++ ] = c;
                if( operand.operand1.disp !== undefined && operand.operand1.disp.value !== undefined ){
                    addExp( _pc++, operand.operand1.disp.value, operand.operand1.disp.w );
                    if( operand.operand1.disp.w ) _pc++;
                }
            },
        "mov_mem,acc"   : function( opecode, operand ){
                return _opecode[ "mov_mem,reg" ]( opecode, operand );
            },
        "mov_mem,imm"   : function( opecode, operand ){
                checkOperandSize( operand );
                var c = 0xc6;
                if( operand.operand1.w || operand.operand2.w ) c |= 0x1;
                if( operand.operand1.w === undefined && operand.operand2.w === undefined ){
                    throw new Error( "operand size unknown" );
                }
                _mem[ _pc++ ] = c;
                c = operand.operand1.mdXXXrm;
                _mem[ _pc++ ] = c;
                if( operand.operand1.disp !== undefined && operand.operand1.disp.value !== undefined ){
                    addExp( _pc++, operand.operand1.disp.value, operand.operand1.disp.w );
                    if( operand.operand1.disp.w ) _pc++;
                }
                addExp( _pc++, operand.operand2.value, operand.operand2.w );
                if( operand.operand2.w ) _pc++;
            },
        "mov_reg,reg"   : function( opecode, operand ){
                checkOperandSize( operand );
                var c = 0x88;
                if( operand.operand1.w || operand.operand2.w ) c |= 0x1;
                _mem[ _pc++ ] = c;
                c = operand.operand1.mdXXXrm;
                c |= ( operand.operand2.reg << 3 );
                _mem[ _pc++ ] = c;
            },
        "mov_reg,acc"   : function( opecode, operand ){
                checkOperandSize( operand );
                return _opecode[ "mov_reg,reg" ]( opecode, operand );
            },
        "mov_acc,reg"   : function( opecode, operand ){
                return _opecode[ "mov_reg,reg" ]( opecode, operand );
            },
        "mov_reg,mem"   : function( opecode, operand ){
                checkOperandSize( operand );
                var c = 0x8a;
                if( operand.operand1.w || operand.operand2.w ) c |= 0x1;
                _mem[ _pc++ ] = c;
                c = operand.operand2.mdXXXrm;
                c |= ( operand.operand1.reg << 3 );
                _mem[ _pc++ ] = c;
                if( operand.operand1.disp !== undefined && operand.operand1.disp.value !== undefined ){
                    addExp( _pc++, operand.operand1.disp.value, operand.operand1.disp.w );
                    if( operand.operand1.disp.w ) _pc++;
                }
            },
        "mov_acc,mem"   : function( opecode, operand ){
                // todo: optimization
                return _opecode[ "mov_reg,mem" ]( opecode, operand );
            },
        "mov_mem,seg"   : function( opecode, operand ){
                // todo
                throw new Error( opecode + ":not implemented" );
            },
        "mov_seg,mem"   : function( opecode, operand ){
                throw new Error( opecode + ":not implemented" );
            },
        "xchg_reg,reg"  : function( opecode, operand ){
                checkOperandSize( operand );
                var c = 0x86;
                if( operand.operand1.w || operand.operand2.w ) c |= 0x1;
                _mem[ _pc++ ] = c;
                c = operand.operand1.mdXXXrm;
                c |= ( operand.operand2.reg << 3 );
                _mem[ _pc++ ] = c;
            },
        "xchg_reg,mem"  : function( opecode, operand, d ){
                checkOperandSize( operand );
                var c = 0x86, o;
                if( operand.operand1.w || operand.operand2.w ) c |= 0x1;
                if( d ){
                    // swap
                    o = operand.operand1;
                    operand.operand1 = operand.operand2;
                    operand.operand2 = o;
                }
                _mem[ _pc++ ] = c;
                c = operand.operand2.mdXXXrm;
                c |= ( operand.operand1.reg << 3 );
                _mem[ _pc++ ] = c;
                if( operand.operand2.disp !== undefined && operand.operand2.disp.value !== undefined ){
                    addExp( _pc++, operand.operand2.disp.value, operand.operand2.disp.w );
                    if( operand.operand2.disp.w ) _pc++;
                }
            },
        "xchg_acc,acc"  : function( opecode, operand ){
                return _opecode[ "xchg_reg,reg" ]( opecode, operand );
            },
        "xchg_acc,mem"  : function( opecode, operand ){
                return _opecode[ "xchg_reg,mem" ]( opecode, operand );
            },
        "xchg_mem,reg"  : function( opecode, operand ){
                return _opecode[ "xchg_reg,mem" ]( opecode, operand, true );
            },
        "xchg_mem,acc"  : function( opecode, operand ){
                return _opecode[ "xchg_reg,mem" ]( opecode, operand, true );
            },
        "xchg_reg,acc"  : function( opecode, operand, d ){
                checkOperandSize( operand );
                if( operand.operand1.w ){
                    var c = 0x90;
                    if( d ) c |= operand.operand2.reg;
                    else c |= operand.operand1.reg;
                    _mem[ _pc++ ] = c;
                }else{
                    return _opecode[ "xchg_reg,reg" ]( opecode, operand, d );
                }
            },
        "xchg_acc,reg"  : function( opecode, operand, d ){
                return _opecode[ "xchg_reg,acc" ]( opecode, operand, true );
            },
        "push_mem"  : function( opecode, operand ){
                var c = 0xff;
                if( operand.operand1.w === 0 ) throw new Error( "syntax error" );
                _mem[ _pc++ ] = c;

                c = operand.operand1.mdXXXrm;
                c |= 0x30;
                _mem[ _pc++ ] = c;
                if( operand.operand1.disp !== undefined && operand.operand1.disp.value !== undefined ){
                    addExp( _pc++, operand.operand1.disp.value, operand.operand1.disp.w );
                    if( operand.operand1.disp.w ) _pc++;
                }
            },
        "push_reg"  : function( opecode, operand ){
                var c = 0x50;
                if( operand.operand1.w === 0 ) throw new Error( "syntax error" );
                c |= operand.operand1.reg;
                _mem[ _pc++ ] = c;
            },
        "push_acc"  : function( opecode, operand ){
                return _opecode[ "push_reg" ]( opecode, operand, true );
            },
        "push_seg"  : function( opecode, operand ){
                var c = 0x6;
                c |= ( operand.operand1.reg << 3 );
                _mem[ _pc++ ] = c;
            },
        "push_imm"  : function( opecode, operand ){
                throw new Error( "not implemented" );
            },
        "pusha"     : 0x60,
        "pop_mem"   : function( opecode, operand ){
                var c = 0x8f;
                if( operand.operand1.w === 0 ) throw new Error( "syntax error" );
                _mem[ _pc++ ] = c;

                c = operand.operand1.mdXXXrm;
                c |= 0x00;
                _mem[ _pc++ ] = c;
                if( operand.operand1.disp !== undefined && operand.operand1.disp.value !== undefined ){
                    addExp( _pc++, operand.operand1.disp.value, operand.operand1.disp.w );
                    if( operand.operand1.disp.w ) _pc++;
                }
            },
        "pop_reg"   : function( opecode, operand ){
                var c = 0x58;
                if( operand.operand1.w === 0 ) throw new Error( "syntax error" );
                c |= operand.operand1.reg;
                _mem[ _pc++ ] = c;
            },
        "push_acc"  : function( opecode, operand ){
                return _opecode[ "push_reg" ]( opecode, operand, true );
            },
        "push_seg"  : function( opecode, operand ){
                var c = 0x7;
                c |= ( operand.operand1.reg << 3 );
                _mem[ _pc++ ] = c;
            },
        "popa"      : 0x61,
        "in_acc,imm"    : function( opecode, operand ){
                var c = 0xe4;
                if( operand.operand1.w ) c |= 0x1;
                _mem[ _pc++ ] = c;
                if( operand.operand2.w ) throw new Error( "syntax error" );
                addExp( _pc++, operand.operand2.value, 0 );
            },
        "in_acc,reg"    : function( opecode, operand ){
                var c = 0xec;
                if( operand.operand2.str != "dx" ) throw new Error( "syntax error" );
                if( operand.operand1.w ) c |= 0x1;
                _mem[ _pc++ ] = c;
            },
        "out_imm,acc"   : function( opecode, operand ){
                var c = 0xe6;
                if( operand.operand2.w ) c |= 0x1;
                _mem[ _pc++ ] = c;
                if( operand.operand1.w ) throw new Error( "syntax error" );
                addExp( _pc++, operand.operand1.value, 0 );
            },
        "out_reg,acc"   : function( opecode, operand ){
                var c = 0xee;
                if( operand.operand1.str != "dx" ) throw new Error( "syntax error" );
                if( operand.operand2.w ) c |= 0x1;
                _mem[ _pc++ ] = c;
            },
        "xlat"  :   0xd7,
        /* lea, lds, les */
        "lahf"  :   0x9f,
        "sahf"  :   0x9e,
        "pushf" :   0x9c,
        "popf"  :   0x9d,
        "int"           : function( opecode, operand )
            {
                var label = operand.operand1.value;
                var v = getLabel( label );
                if( v === null ){
                    _mem[ _pc++ ] = 0xcd;
                    addExp( _pc++, label, 0 );
                }else if( v == 3 ){
                    _mem[ _pc++ ] = 0xcc;
                }else{
                    limitValue( v, 0, 0xff, label );
                    _mem[ _pc++ ] = 0xcd;
                    _mem[ _pc++ ] = v;
                }
            },
        "db"            : function( opecode, operand ) {
                var text = operand.all.split( "," ), s, m, i, t, b = false, v;
                for( var i = 0; i < text.length; i++ ){
                    s = text[ i ];
                    if( !b ){
                        m = /^\s*"([^"]*)("\s*)?$/.exec( s );
                        if( m != null ){
                            t = m[ 1 ];
                            if( !m[ 2 ] ){
                                b = true;
                            }else{
                                for( j = 0; j < t.length; j++ ){
                                    _mem[ _pc++ ] = t.charCodeAt( j );
                                }
                            }
                        }else{
                            v = _parseInt( s );
                            if( v === null ){
                                // error
                                throw new Error( "syntax error" );
                            }
                            _mem[ _pc++ ] = v;
                        }
                    }else{
                        m = /^([^"]*)("\s*)?$/.exec( s );
                        if( m != null ){
                            t += "," + m[ 1 ];
                            if( m[ 2 ] ){
                                for( j = 0; j < t.length; j++ ){
                                    _mem[ _pc++ ] = t.charCodeAt( j );
                                }
                                b = false;
                            }else t += ",";
                        }else{
                            // error
                            throw new Error( "syntax error" );
                        }
                    }
                }
            },
        "nop" : 0x90

    };

    var getLabel = function( label ){
        var v;
        v = _parseInt( label );
        if( v !== null ) return v;
        v = _labels[ label ];
        if( v === undefined ) v = null;
        return v;
    }

    var pushLabel = function( label ){
        var m;
        if( !label ) return;

        if( (m = /^(\d[0-9a-fA-F]*):?$/.exec( label ) ) != null ){
            _pc = parseInt( m[ 1 ], 16 );
            return;
        }else if( ( m = /^(\w+):?$/.exec( label ) ) != null ){
            _labels[ m[ 1 ] ] = _pc;
        }else{
            throw new Error( "invalid label name [" + label + "]" );
        }
    };

    var addExp = function( addr, exp, isWord ){
        _unknowns.push( { "addr" : addr, "w" : isWord, "exp" : exp } );
    }

    var resolveExp = function(){
        var i, addr, exp, w, m, re = /(\w+)/g, v;
        for( var i = 0; i < _unknowns.length; i++ ){
            addr = _unknowns[ i ].addr;
            exp  = _unknowns[ i ].exp;
            w    = _unknowns[ i ].w;
            if( typeof( exp ) == "string" )
                exp = exp.replace( /\w+/g, 
                    function( str ){ 
                        var v = getLabel( str );
                        if( v === null ) throw new Error( "unknown symbol [" + str + "]" );
                        return v;
                    } );
            v = eval( exp );
            if( w ){
                _mem[ addr     ] = ( v.loByte() );
                _mem[ addr + 1 ] = ( v.hiByte() );
            }else{
                _mem[ addr ] = ( v.loByte() );
            }
        }
    }


    var limitValue = function( v, min, max, msg ){
        if( v < min )
            throw new Error( "value too small" + ( msg !== undefined ? ":" + msg : "" ) );
        if( v > max )
            throw new Error( "value too large" + ( msg !== undefined ? ":" + msg : "" ) );
        return true;
    };

    var Regs = {
        "al" : 0,   // 000
        "ax" : 0,   // 000
        "cl" : 1,   // 001
        "cx" : 1,   // 001
        "dl" : 2,   // 010
        "dx" : 2,   // 010
        "bl" : 3,   // 011
        "bx" : 3,   // 011
        "ah" : 4,   // 100
        "sp" : 4,   // 100
        "ch" : 5,   // 101
        "bp" : 5,   // 101
        "dh" : 6,   // 110
        "si" : 6,   // 110
        "bh" : 7,   // 111
        "di" : 7,   // 111
        "es" : 0,   // 00
        "cs" : 1,   // 01
        "ss" : 2,   // 10
        "ds" : 3    // 11
    };

    var parseOperand = function( operand ){
        var parseAnOperand = function( s ){
            /*
               type : "acc" / "reg" / "seg" / "mem" / "imm"
               reg  : 000 ... 111
               str  : original argument 's'
               mdXXXrm : mm000r/m
               value : valueOf( s )
               w : 0 if 8bit, 1 if 16bit, undefined
               disp : { value, w } or {}
             */
            var type, reg, str, mdXXXrm, value, w, disp = {}, v;
            if( s === null || s === undefined ) return undefined;
            str = s;
            if( ( m = /^(byteptr|wordptr)?(ds|ss)?\[([^\]]*)\]$/.exec( s ) ) != null ){
                var segprefix = m[ 2 ];
                var addr = m[ 3 ];
                if( m[ 3 ] === undefined || m[ 3 ] == "" ) throw new Error( "syntax error" );
                if( m[ 1 ] == "wordptr" ) w = 1; else
                if( m[ 1 ] == "byteptr" ) w = 0;
                type = "mem";
                m = /^(bx|si|di|bp)((\+(bx|si|di|bp))?(.+)?)?$/.exec( addr );
                if( m === null ){
                    disp.value = addr;
                    disp.w = 1;
                    mdXXXrm = 0x06;
                }else{
                    var regs = { 
                        "bxsi" : 0, "sibx" : 0, "bxdi" : 1, "dibx" : 1, "bpsi" : 2, "sibp" : 2, 
                        "bpdi" : 3, "dibp" : 3, "si" : 4, "di" : 5, "bp" : 6, "bx" : 7 }

                    var c = m[ 1 ] + ( m[4] === undefined ? "" : m[4] );
                    c = regs[ c ];

                    if( m[ 5 ] !== undefined && m[ 4 ] !== undefined ){
                        v = getLabel( m[ 5 ] );
                        if( v !== null && 0 <= v && v <= 0xff ){
                            disp.w = 0;
                            disp.value = v;
                            if( c !== undefined ) mdXXXrm = c | 0x40;
                        }else{
                            disp.w = 1;
                            disp.value = m[ 5 ];
                            if( c !== undefined ) mdXXXrm = c | 0x80;
                        }
                    }else if( m[ 5 ] === undefined && m[ 4 ] !== undefined ){
                        if( c != undefined ) mdXXXrm = c;
                    }else if( m[ 5 ] !== undefined && m[ 4 ] === undefined ){
                        v = getLabel( m[ 5 ] );
                        if( v !== null && 0 <= v && v <= 0xff ){
                            disp.w = 0;
                            disp.value = v;
                            if( c != undefined ) mdXXXrm = c | 0x40;
                        }else{
                            disp.w = 1;     
                            disp.value = m[ 5 ];
                            if( c != undefined ) mdXXXrm = c | 0x80;
                        }
                    }else{
                        if( c !== undefined ) mdXXXrm = c;
                    }
                }
            }else if( ( m = Regs[ s ] ) !== undefined ){
                var c;
                reg = m;
                c = s.charAt( 1 );

                if( c == 's' ) type = "seg"; else
                if( /^(ax|al)$/.test( s ) ) type = "acc"; else 
                type = "reg";
                if( c != 's' ) mdXXXrm = 0xC0 | reg;
                if( c == 'l' || c == 'h' ) w = 0; else w = 1;
            }else{
                type = "imm";
                value = s;
            }
            return ( { "type" : type, "reg" : reg, "str" : str, "mdXXXrm" : mdXXXrm, "value" : value, "w" : w, "disp" : disp } );
        };
        var r1, r2;
        r1 = parseAnOperand( operand.operand1 );
        r2 = parseAnOperand( operand.operand2 );

		/*
        if( r1 !== undefined && r2 !== undefined ){
            if( r1.w != r2.w ) throw new Error( "invalid operand" );
        	if( r1.w !== undefined ){
				r2.w = r1.w;
        	}else if( r2.w != undefined ){
				r1.w = r2.w;
			}
		}
		*/

        return ({ operand1 : r1, operand2 : r2, all : operand.all } );
    }

    var errorMsg = function( e ){
        var s = undefined;
        if( typeof( e ) === "object" ){
            if( e.message !== undefined ){
                s = e.message;
                if( window.opera ){
                    s = s.replace( /\r\nstacktrace:[\s\S].*$/, "" );
                }
            }
        }
        if( s === undefined ) return e;
        else return s ;
    };

    this.asm = function ( text, errorCallback ){
        _err = "";
        _labels = {};
        _pc = 0x100;
        _mem = [];
        _unknowns = [];
        console.clear();
        text = text.split( /\r?\n/ );
        var i, s, m;
        var re = /^(\w+:)?(\s+(\w+)?(\s+(([^,]+)(\s*,\s*(.*))?)?)?)?$/;
        var f, ftype;
        var label, opecode, operand1, operand2;
        var line;
        var r;
        for( line = 0; line < text.length; line++ ){
            try{
                s = text[ line ].replace( /\;.*$/,"" );
                if( s.length == 0 ) continue;
                m = re.exec( s );
                if( m ){ 
                    label = m[ 1 ];
                    if( label ) pushLabel( label );
                    opecode  = m[ 3 ] ? m[ 3 ].replace( /\s/g, "" ).toLowerCase() : "";
                    if( opecode.length == 0 ) continue;
                    operand1 = m[ 6 ] ? m[ 6 ].replace( /\s/g, "" ).toLowerCase() : undefined;
                    operand2 = m[ 8 ] ? m[ 8 ].replace( /\s/g, "" ).toLowerCase() : undefined;
                    r = parseOperand( { "operand1" : operand1, "operand2" : operand2, "all" : m[ 5 ] } );

                    f = _opecode[ opecode ];
                    if( f === undefined && r.operand1 !== undefined ){
                        if( r.operand2 === undefined || r.operand2.type === undefined ){
                            f = _opecode[ opecode + "_" + r.operand1.type ];
                        }else{
                            f = _opecode[ opecode + "_" + r.operand1.type +"," + r.operand2.type ];
                        }
                    }
                    if( f === undefined )
                        f = _opecode[ opecode + "_" ];
                    ftype = _typeof( f );
                    if( ftype == "function" ){
                        f( opecode, r );
                    }else if( ftype == "array" ){
                        for( var i = 0; i < f.length; i++ ){
                            _mem[ _pc++ ] = f;
                        }
                    }else if( ftype == "number" ){
                        _mem[ _pc++ ] = f;
                    }else{
                        // error
                        //console.log( "not implemented:", opecode, " ", r.operand1.type,",",r.operand2.type );
                        throw Error( "not implemented:'" + opecode + "'" );
                    }
                }else if( !s.match( /^\s*$/ ) ){
                    throw Error( "syntax error" );
                }
            }catch( e ){
                _err += errorMsg( e ) + " at line " + ( line + 1 ) + ".\r";
                if( typeof errorCallback == "function" )
                    errorCallback( e + " at line " + ( line + 1 ) + "." );
            }
        }
        try{
            resolveExp();
        }catch( e ){
            _err += errorMsg( e ) + "\r"; 
            if( typeof errorCallback == "function" )
                errorCallback( e );
        }
        /*
        (function(){
            var i;
            var s = "";
            for( i = 0x100; i < _mem.length; i++ ){
                s += typeof( _mem[ i ] ) == "number" ? _mem[ i ].toHex(2) + " " : _mem[ i ] + " ";
            }
            console.log( s );
        })();*/

    };
    this.getMem = function(){
        var i;
        var s = "";
        for( i = 0x100; i < _mem.length; i++ ){
            s += typeof( _mem[ i ] ) == "number" ? _mem[ i ].toHex(2) + " " : "?? ";
        }
        return s;
    }
    this.getError = function(){
        return _err;
    };
    this.getSym = function(){
        var t, c;
        t = "%@\"%\"@,~,%,!`_______-;>`_______%\"!,^,:`_______-@{-`{-?:`_______-" +
            "``-``-@@`_______-`~-``-@$`_______-``-``-@@`_______-`~-``-@#`____" +
            "___-+~-/~-?;`_______%!~-;-,;`_______-\"$-@~-@``_______-{[-);-@:`_" +
            "______-/*,%`_______`_______`_______`_______%@$-@;-?;`_______-/~-" +
            "`&,#`_______-`~-`{,*`_______-@@-$!`_______-:$,[,<`_______-!|-.)," +
            "!`_______-@{-@`-/(`_______`_______`_______`_______-{!-{.,.`_____" +
            "__-~/-/``_______%\"\"-}@$\"`_______%@@-!/,!`_______-:*-=%`[[[[[[[[`" +
            "^^^^^-%+)@@^^^!;";
        var len = _mem.length - 0x100;
        var symbol = function( c ){
            if( c == 0 ) return String.fromCharCode( 0x40 );
            else if( c == 0xf ) return String.fromCharCode( 0x5f );
            else return String.fromCharCode(0x20 | c);
        };

        t += symbol( ( len >> 8 ) & 0xf );
        t += symbol( ( len >> 12 ) & 0xf );
        t += symbol( ( len      ) & 0xf );
        t += symbol( ( len >> 4 ) & 0xf );
        
        for( i = 0x100; i < _mem.length; i++ ){
            c = typeof( _mem[ i ] ) == "number" ? _mem[ i ] : 0xcc; // 0xcc ?

            t += symbol( c & 0xf );
            t += symbol( ( c >> 4 ) & 0xf );
        };
        return t;
    };
    return this;
}

var asm = new Assembler();

