haXe

hxasm : haXe Flash9 Assembler Library

hxASM enables you to program directly in Flash9 assembler and it will compile for your a SWF compatible with Flash Player 9 that can be either directly loaded from memory or saved into a file.

The library can dynamicaly generate a ByteArray representing a SWF file that contains the compiled assembler methods. The code can then be executed by using the flash.display.Loader.loadBytes method.

Installation

In order to install the library, you need to :

  • Download and install haXe using the haXe Installer
  • On Windows, download and install FlashDevelop 2.0
  • Install the haXe FlashDevelop Plugin FlashDevelop Plugin
  • Install the hxASM library that is available through haxelib, by running the following command :

    haxelib install hxasm

Usage

First, create a new haXe Project and add the -lib hxasm to the Extra Parameters. Then you can start writing in Flash9 assembler. Here's a small example :

import hxasm.Bytecode;

class Test {

    static var loader : flash.display.Loader;

    static function main() {
    // create a new bytecode Context
    var ctx = new hxasm.Context();
    // defines a class called Main
    ctx.beginClass("Main");
    // the type 'int' in Flash9
    var tint = ctx.type("int");
    // create a member method called 'test'
        // with 0 arguments and return type 'int'
    var m = ctx.beginMethod("test",[],tint);
    // the maximum size of the stack in this method
    m.maxStack = 1;
    // write bytecode into the current method
    ctx.ops([
        OInt(666),
        ORet,
    ]);
    // we are done with all the bytecode writing
    ctx.finalize();

    // create an output and write the bytecode to it
    var o = new hxasm.Output();
    hxasm.Writer.write(o,ctx);
    var swf = o.getBytes();

    // load the SWF bytes
    loader = new flash.display.Loader();
    loader.contentLoaderInfo.addEventListener(flash.events.Event.COMPLETE,onLoaded);
    loader.loadBytes(swf);
    }

    // the data has been succesfully loaded
    public static function onLoaded(e) {
    // get the Main class
    var m = loader.contentLoaderInfo.applicationDomain.getDefinition("Main");
    // create an instance of it
    var inst : Dynamic = Type.createInstance(m,[]);
    // call the 'test' method
    trace(inst.test());
    // this should display '666'
    }

}

Another example that shows good performances is the Fibonnacci recursive calculus. It's defined in haXe as the following method :

static function fib( n : Int ) : Int {
   if( n <= 1 ) return 1;
   return fib(n - 1) + fib(n - 2);
}

Here's the corresponding Flash9 bytecode :

// fib takes an integer argument and returns an integer
var m = ctx.beginMethod("fib",[tint],tint);
// we will have up to 3 values on the stack
m.maxStack = 3;
ctx.ops([
    OReg(1),      // register 1 = first argument
    OSmallInt(1), // the integer 1
    OJump(JGt,3), // jump 3 bytes if reg1 > 1
    OInt(1),
    ORet,         //   return 1
    ODecrIReg(1), // decrement register 1
    OThis,
    OReg(1),
        // call this.fib(reg1) with 1 argument
    OCallProperty(ctx.property("fib"),1), 
    ODecrIReg(1), // decrement register 1
    OThis,
    OReg(1),
        // call this.fib(reg1) with 1 argument
    OCallProperty(ctx.property("fib"),1),
    OOp(OpIAdd),  // add the two values
    ORet,         // returns
]);

When timed, fib(35) shows a +30% speedup in assembler versus the AS3/haXe version.

Performing jumps

It's not always easy to count bytes when writing a OJump opcode. There is an API to make it more easy to works with jumps. Here's the modified fib version that uses this API :

    var m = ctx.beginMethod("fib",[tint],tint);
    m.maxStack = 3;
    ctx.ops([
        OReg(1),
        OSmallInt(1),
    ]);
    var j = ctx.jump(JGt); // prepare a jump
    ctx.ops([
        OInt(1),
        ORet,
    ]);
    j(); // patch the jump with current position
    ctx.ops([
        ODecrIReg(1),
        OThis,
        OReg(1),
        OCallProperty(ctx.property("fib"),1),
        ODecrIReg(1),
        OThis,
        OReg(1),
        OCallProperty(ctx.property("fib"),1),
        OOp(OpIAdd),
        ORet,
    ]);

The ctx.jump method writes a OJump opcode, then returns a function. This function can be called when you reach the place of the jump target.

There's also a ctx.backwardJump that works the following :

    var j = ctx.backwardJump();
    // ....
    j(JAlways); // jump to saved position

FAQ

  • In order to read an array, first push on the stack the array and the index, then use OGetProp(ctx.arrayProp)
  • In order to write into an array, first push on the stack the array, the index and the value, then use OSetProp(ctx.arrayProp)
  • I'm getting VerifyError #1023 : this is a stack overflow error. Try increasing the maxStack property of your method.
  • I'm getting VerifyError #1024 : this is a stack underflow error. It means that you are using an operation (such as ORet) while there is not enough values on the stack.
  • I'm getting VerifyError #1025 : this is a invalid register error. Try increasing the nRegs property of your method.
  • I'm getting VerifyError #1030 : this is a stack unbalanced error. It means that two branches of a Jump results in different stack sizes when they join back. All jumps or code leading to a given position should result in the same stack size.
  • I'm getting VerifyError #1021 : this is an invalid jump address error. This shouldn't occur if you use the Jump API that is presented before.

ASM Reference

A good reference of the Flash9 AVM2 instructions can be found here. The names are not always the same in this reference and in hxASM, but they should be similar.
If you edit hxasm/OpWriter.hx you'll see for each opcode the hex code AVM2 is using.

Here's the list of opcodes defined in hxASM :

OBreakPoint;
ONop;
OThrow;
OGetSuper( v : IName );
OSetSuper( v : IName );
ORegKill( r : Register );
OLabel;
OJump( j : JumpStyle, delta : Int );
OSwitch( def : Int, deltas : Array<Int> );
OPushWith;
OPopScope;
OForIn;
OHasNext;
ONull;
OUndefined;
OForEach;
OSmallInt( v : Int );
OInt( v : Int );
OTrue;
OFalse;
ONaN;
OPop;
ODup;
OSwap;
OString( v : Index<String> );
OIntRef( v : Index<Int> );
OFloat( v : Index<Float> );
OScope;
ONamespace( v : Index<Namespace> );
ONext( r1 : Register, r2 : Register );
OFunction( f : Index<MethodType> );
OCallStack( nargs : Int );
OConstruct( nargs : Int );
OCallMethod( slot : Slot, nargs : Int );
OCallStatic( meth : Index<MethodType>, nargs : Int );
OCallSuper( name : IName, nargs : Int );
OCallProperty( name : IName, nargs : Int );
ORetVoid;
ORet;
OConstructSuper( nargs : Int );
OConstructProperty( name : IName, nargs : Int );
OCallPropLex( name : IName, nargs : Int );
OCallSuperVoid( name : IName, nargs : Int );
OCallPropVoid( name : IName, nargs : Int );
OObject( nfields : Int );
OArray( nvalues : Int );
ONewBlock;
OClassDef( c : Index<ClassDef> );
OCatch( c : Int );
OFindPropStrict( p : IName );
OFindProp( p : IName );
OFindDefinition( d : IName );
OGetLex( p : IName );
OSetProp( p : IName );
OReg( r : Register );
OSetReg( r : Register );
OGetGlobalScope;
OGetScope( n : Int );
OGetProp( p : IName );
OInitProp( p : IName );
ODeleteProp( p : IName );
OGetSlot( s : Slot );
OSetSlot( s : Slot );
OToString;
OToXml;
OToXmlAttr;
OToInt;
OToUInt;
OToNumber;
OToBool;
OToObject;
OCheckIsXml;
OCast( t : IName );
OAsAny;
OAsString;
OAsType( t : IName );
OAsObject;
OIncrReg( r : Register );
ODecrReg( r : Register );
OTypeof;
OInstanceOf;
OIsType( t : IName );
OIncrIReg( r : Register );
ODecrIReg( r : Register );
OThis;
OSetThis;
ODebugReg( name : Index<String>, r : Register, line : Int );
ODebugLine( line : Int );
ODebugFile( file : Index<String> );
OBreakPointLine( n : Int );
OTimestamp;
OOp( op : Operation );

The different possible jumps are :

JNotLt;
JNotLte;
JNotGt;
JNotGte;
JAlways;
JTrue;
JFalse;
JEq;
JNeq;
JLt;
JLte;
JGt;
JGte;
JPhysEq;
JPhysNeq;

And the different operations are the following :

OpAs;
OpNeg;
OpIncr;
OpDecr;
OpNot;
OpBitNot;
OpAdd;
OpSub;
OpMul;
OpDiv;
OpMod;
OpShl;
OpShr;
OpUShr;
OpAnd;
OpOr;
OpXor;
OpEq;
OpPhysEq;
OpLt;
OpLte;
OpGt;
OpGte;
OpIs;
OpIn;
OpIIncr;
OpIDecr;
OpINeg;
OpIAdd;
OpISub;
OpIMul;

Don't hesitate to ask on the haXe mailing list if you have any question about hxASM usage.

Source Code

hxasm is released under BSD license and the source code repository is available on http://code.google.com/p/hxasm.

version #83, modified 2008-05-01 23:30:02 by ncannasse