Sunday, January 23, 2011

[Adobe Alchemy Hacks] Compile *.as Source Files Assembly to SWF and SWC

With alchemy, we can compile AS3 source files assembly, which means we can use some AVM2 inline assembly language in our AS3 source file. Here is the code snippet for using inline asm and accessing memory in AS3:

/*
[Adobe Alchemy Hacks]
AlchemyAD_hack.as
{Simple example for using inline asm and 
accessing memory in AS3}
By Bruce Jawn (January/23/2011)
[http://bruce-lab.blogspot.com]

To compile this source file with Alchemy: 
cd /cygdrive/f/alchemy/
java -Xms16M -Xmx196M -jar F:/alchemy/bin/asc.jar -AS3 -strict -import F:/alchemy/flashlibs/global.abc -import F:/alchemy/flashlibs/playerglobal.abc -config Alchemy::Debugger=false -config Alchemy::NoDebugger=true -config Alchemy::Shell=false -config Alchemy::NoShell=true -config Alchemy::LogLevel=0 -config Alchemy::Vector=true -config Alchemy::NoVector=false -config Alchemy::SetjmpAbuse=false -swf AlchemyAD_hack,800,600,60 AlchemyAD_hack.as
*/
package
{    
 import flash.display.Sprite;
 import flash.text.TextField;
 import flash.utils.ByteArray;
 import flash.utils.Endian;
 import flash.system.ApplicationDomain;
 public class AlchemyAD_hack extends Sprite{

public function AlchemyAD_hack () 
{ 
  /*Create the print shell*/
  var MyShell:TextField=new TextField();
  MyShell.height=600;
  addChild(MyShell);
  function print(output:*):void
  {
   MyShell.appendText(String(output));
   MyShell.appendText("\n");
  }
  
  /*Test Memory Write*/
  //ByteArray for the test
  var testData:ByteArray = new ByteArray();
  testData.endian = Endian.LITTLE_ENDIAN;
  testData.length=0xffff*4;
  //select testdata in memory
  ApplicationDomain.currentDomain.domainMemory=testData;
  var AdrInt:int=0;
  //the test value we will write into testData via memory
  var testValue:int=123;
  //write the testValue into testData
  ApplicationDomain.currentDomain.domainMemory[0] = testValue;
  //Check if testValue has been written into testData
  print(testData[0]);//should print 123
  
  /*Test Memory Read*/
  var readedValue:int=ApplicationDomain.currentDomain.domainMemory[0];
  print(readedValue);//should print 123 
  
  /*Test Inline ASM*/
  //label and jump
  __asm(jump, target('myLable'));
  print("not jumped!");//this line will be skipped
  __asm(label, lbl('myLable'));
  print("jumped!"); 
  //switch jump
  var myState:int=1;
  __asm(push(myState), switchjump('state0','state1','state2'));
  __asm(lbl('state0'));
  print("This is state0.");
  __asm(lbl('state1'));
  print("This is state1.");
  __asm(lbl('state2'));
  print("This is state2.");
  //iftrue jump
  var temp:int=1;
  __asm(push(temp!=0), iftrue, target('turejump'));
  print("iftrue not jumped!");//this line will be skipped
  __asm(label, lbl('turejump'));
  print("iftrue jumped!"); 
  
  /*Test Alchemy Memory Instructions*/
  //All memory opcodes listed here:
  /*
  Get a 32 bit value at the location addr and return as an int:
  _mr32(addr:int):int{ return __xasm(push(addr), op(0x37)); }

  Get a 16 bit unsigned value at the location addr and return as an int:. 
  _mru16(addr:int):int { return __xasm(push(addr), op(0x36)); }

  Get a 16 bit signed value at the location addr and return as an int: 
  _mrs16(addr:int):int { return __xasm(push(addr), op(0x36)); } // li16

  Get a 8 bit value at the location addr and return as an int:
  _mru8(addr:int):int { return __xasm(push(addr), op(0x35)); }
  
  Get a 8 bit value at the location addr and return as an int: 
  _mrs8(addr:int):int { return __xasm(push(addr), op(0x35)); }

  Get a float value at the location addr and return as an Number:
  _mrf(addr:int):Number { return __xasm(push(addr), op(0x38)); }

  Get a double value at the location addr and return as an Number:
  _mrd(addr:int):Number { return __xasm(push(addr), op(0x39)); }

  Write an int as a 32 bit value at the location addr: 
  _mw32(addr:int, val:int):void { __asm(push(val), push(addr), op(0x3c)); }

  Write an int as a 16 bit value at the location addr: 
  _mw16(addr:int, val:int):void { __asm(push(val), push(addr), op(0x3b)); }

  Write an int as a 8 bit value at the location addr: 
  _mw8(addr:int, val:int):void { __asm(push(val), push(addr), op(0x3a)); }

  Write a Number as a float at the location addr: 
  _mwf(addr:int, val:Number):void { __asm(push(val), push(addr), op(0x3d)); }

  Write a Number as a double at the location addr:
  _mwd(addr:int, val:Number):void { __asm(push(val), push(addr), op(0x3e)); }
  */
  
  //Write an int 654321 as a 32 bit value at the location 1000
  __asm(push(654321),push(1000),op(0x3c));
  //Trace the memory
  ApplicationDomain.currentDomain.domainMemory.position=1000;
  print(ApplicationDomain.currentDomain.domainMemory.readInt());//should print 654321
  //Get a 32 bit value at the location 1000 and return as an int
  var temp:int=__xasm(push(1000), op(0x37));
  print(temp);//should print 654321

  /*Test some AVM2 Instructions*/
  //More AVM2 Instructions can be found at:
  //http://www.anotherbigidea.com/javaswf/avm2/AVM2Instructions.html
  
  //test add: 0xA0 
  var var1:int=123;
  var var2:int=321;
  //write (var1+var2)=444 to testData[0] via memory
  //ApplicationDomain.currentDomain.domainMemory.position=AdrInt;
  //ApplicationDomain.currentDomain.domainMemory.writeInt(var1+var2);
  __asm(push(var1), push(var2), op(0xA0), push(AdrInt), op(0x3c));
  testData.position=0;
  print(testData.readInt());//should print 444
  
  //test subtract: 0xA1
  var result:int=__xasm(push(var1), push(var2), op(0xA1));//var result=var1-var2;
  print(result);//should print -198
  //write (var1-var2)=-198 to testData[1] via memory in a different way
  __asm(push(result),push(AdrInt+4),op(0x3c));
  testData.position=4;
  print(testData.readInt());//should print -198
  
}//end of function AlchemyAD_hack

}//end of class
}//end of pacakge
/*
References:
http://labs.adobe.com/wiki/index.php/Alchemy:Documentation:Developing_with_Alchemy:AS3_API
http://unitzeroone.com/blog/2009/05/22/another-scream-on-flash-alchemy-memory-and-compilers/
http://blog.frankula.com/?p=211
http://forums.adobe.com/message/2616985
http://forums.adobe.com/message/3001861
Special thanks to Bernd Paradies (http://forums.adobe.com/people/Bernd%20Paradies)
*/
======
Update: 20, Feb., 2011
All AVM2 Opcode names for Alchemy can be found here:(ASC source code)
http://opensource.adobe.com/svn/opensource/flex/sdk/trunk/modules/asc/src/java/macromedia/abc/Opcodes.java
so for example, the following code
var result:int=__xasm(push(var1), push(var2), op(0xA1));
can be also writen as
var result:int=__xasm(push(var1), push(var2), subtract);
======
The output:
123
123
jumped!
This is state2.
iftrue jumped!
654321
654321
444
-198
-198

And when we compile C/C++ code to swf or swc, we can manuly modify the *.as file generated during the compilation process, for optimization and then use Alchemy asc to compile the modified *.as to swf or swc. By default the temp *.as file will be deleted, to get that file, we can simply copy and paste that file before it deleted during the compilation, or modify the "gcc" file in "alchemy\achacks" folder, remove/commentize the last two lines:
# remove junk TODO failure leaves stuff around!
if(!$ENV{ACHACKS_TMPS})
{ sys("rm", "-f", <$$.achacks.*>) }
Now we have the generated *.as files from the compiler, something like "19048.achacks.as".
It's easy to compile the *.as to a swf, use the command in the code snippet above.

To compile the *.as to swc, there are several ways.
First way, you can compile the *.as to swf, unzip the swc compiled before and replace the "library.swf" with the new swf. I've tried this but there are some problems I haven't solved.
Second way, modify the gcc file, you can follow this post: http://blog.frankula.com/?p=211.
Third way, use the makefile from this project:
http://alchemy-hacks.googlecode.com/svn/trunk/tricks/
Fourth way, follow this post
http://unitzeroone.com/blog/2009/05/22/another-scream-on-flash-alchemy-memory-and-compilers/
Fifth way, mentioned by Bernd Paradies, can be found here: http://forums.adobe.com/message/3001861
Final way, this is what I recommend, use the wrapper by Ed McManus.
Ed McManus posted the script here: http://forums.adobe.com/message/2616985,
but there are some errors caused by the forum formatting. I fixed the errors,
one obvious error is extra spaces in "nbsp;", another big problem is when you try to use the compiled swc, flashdevelop will throw the error "Target Matching “[xX][mM][lL]” is Not Allowed", thanks to this post http://www.anujgakhar.com/2009/02/17/the-processing-instruction-target-matching-xxmmll-is-not-allowed/, I figure out this problem is caused by the spaces before
catalog.xml's header. I made changes to the script and now it works properly.

You can download the fixed version(alc-asc) here: http://flaswf.googlecode.com/svn/trunk/QuickAlchemy/Hack/SWC/
To use it, put it into your "alchemy/achacks" folder, go to cygwin use command
such as "alc-asc modifiedAlchemy.as outputLib.swc".

Links:
http://forums.adobe.com/message/2616985
http://forums.adobe.com/message/3001861
http://blog.frankula.com/?p=211
http://www.anujgakhar.com/2009/02/17/the-processing-instruction-target-matching-xxmmll-is-not-allowed/
http://unitzeroone.com/blog/2009/05/22/another-scream-on-flash-alchemy-memory-and-compilers/
And Ed McManus wrote some very good documents on Alchemy -
General Porting Tips:
https://github.com/emcmanus/flashsnes/blob/master/docs/General_Porting_Tips
Alchemy VM Architecture:
https://github.com/emcmanus/flashsnes/blob/master/docs/Alchemy_VM_Architecture.txt
Performance:
https://github.com/emcmanus/flashsnes/blob/master/docs/Performance

Thursday, January 13, 2011

Adobe Alchemy Hacks - access memory and use inline asm in C

It's not a short time since Alchemy emerged in 2008.
To my disapointment, scarcely any more great C projects ported to Flash after doom and quake.
And there is no updates for this great tool.I like alchemy and use it a lot because it allows you to do flash in C. Alchemy is much more than the new fast memory opcodes, it is a C virtual machine, which wraps the standard C lib for ActionScript. Do flash in C, makes porting C programs easy and also makes your flash program portable. Moreover, the gcc and llvm compilers can optimaize your code a lot. That's why although there are both powerful tools like apparat, yogda and handy tools like azoth which let you to use those alchemy memory opcodes in pure AS3, I'm still using alchemy. Actually, if there is no alchemy, I was already a Haxer in 2008.

The obvious disadvantage to use alchemy is this tool has lots of bugs and it is very hard to debug. As far as I know most bugs are with C++(broken string.h, can't use cin/cout, the class initialization function will never initialize), so don't try to port a C++ program with alchemy now unless you're ready to port the C++ program to C first. What's more, it lacks documentations and developing resources. Although the official forum is a good place to discuss alchemy, it' not easy to find some advanced and detailed things. There is no instructions for inline asm and memory manipulation which many people may be intereted in.
After some search, finally I got some useful things.
So I wrote this little code snippet, hope to help those who want to know more about how to use alchemy. I hope Adobe can give more emphasis on alchemy, update it, remove the bugs, make it stable and make the developing process easier. It is a great thing and should not only be an experimental project, which be played with for some moment, then thrown into some dark corner of the lab and let it decay. I even wish Adobe could make C/C++ an official alternative for ActionScript to develop flash.

/*
[Adobe Alchemy Hacks]
AlchemyVM_hack.c
{Simple example for using inline asm and 
accessing memory of Alchemy Virtual Machine in C}
By Bruce Jawn (January/14/2011)
[http://bruce-lab.blogspot.com]

To compile this source file with Alchemy: 
cd /cygdrive/f/alchemy/
source /cygdrive/f/alchemy/alchemy-setup
alc-on
gcc AlchemyVM_hack.c -O3 -Wall -swf -o AlchemyVM_hack.swf
*/
#include 
#include 
//get the Alchemy C Virtual Machine memory
//use asm to embed AS3 code
asm("var ALCVM_Memory:ByteArray = gstate.ds;");
int main () 
{ 
  /*Test Memory Write*/
  //array of int for the test
  int* testData = malloc(0xff * sizeof(int));
  //get the address of testdata in memory
  AS3_Val Adr=AS3_Ptr(testData);
  int AdrInt=AS3_IntValue(Adr);
  //the test value we will write into testData via memory
  int testValue=123;
  //write the testValue into testData
  //gcc AT&T inline assembly used here
  asm("ALCVM_Memory[%0] = %1;" : : "r"(AdrInt), "r"(testValue)); //ALCVM_Memory[AdrInt] = testValue;
  //Check if testValue has been written into testData
  printf("%d\n",testData[0]);//should print 123
  
  /*Test Memory Read*/
  int readedValue;
  asm("%0 ALCVM_Memory[%1];" : "=r"(readedValue) : "r"(AdrInt));
  printf("%d\n",readedValue);//should print 123
  
  /*Test Inline ASM*/
  //label and jump
  asm("__asm(jump, target('myLable'))");
  printf("%s\n","not jumped!");//this line will be skipped
  asm("__asm(label, lbl('myLable'))");
  printf("%s\n","jumped!"); 
  //switch jump
  asm("var myState:int=1;");
  asm("__asm(push(myState), switchjump('state0','state1','state2'));");
  asm("__asm(lbl('state0'))");
  printf("%s\n","This is state0.");
  asm("__asm(lbl('state1'))");
  printf("%s\n","This is state1.");
  asm("__asm(lbl('state2'))");
  printf("%s\n","This is state2.");
  //iftrue jump
  asm("var temp:int=1;");
  asm("__asm(push(temp!=0), iftrue, target('turejump'));");
  printf("%s\n","iftrue not jumped!");//this line will be skipped
  asm("__asm(label, lbl('turejump'))");
  printf("%s\n","iftrue jumped!"); 
  
  /*Test Alchemy Memory Instructions*/
  //All memory opcodes listed here:
  /*
  Get a 32 bit value at the location addr and return as an int:
  _mr32(addr:int):int{ return __xasm(push(addr), op(0x37)); }

  Get a 16 bit unsigned value at the location addr and return as an int:. 
  _mru16(addr:int):int { return __xasm(push(addr), op(0x36)); }

  Get a 16 bit signed value at the location addr and return as an int: 
  _mrs16(addr:int):int { return __xasm(push(addr), op(0x36)); } // li16

  Get a 8 bit value at the location addr and return as an int:
  _mru8(addr:int):int { return __xasm(push(addr), op(0x35)); }
  
  Get a 8 bit value at the location addr and return as an int: 
  _mrs8(addr:int):int { return __xasm(push(addr), op(0x35)); }

  Get a float value at the location addr and return as an Number:
  _mrf(addr:int):Number { return __xasm(push(addr), op(0x38)); }

  Get a double value at the location addr and return as an Number:
  _mrd(addr:int):Number { return __xasm(push(addr), op(0x39)); }

  Write an int as a 32 bit value at the location addr: 
  _mw32(addr:int, val:int):void { __asm(push(val), push(addr), op(0x3c)); }

  Write an int as a 16 bit value at the location addr: 
  _mw16(addr:int, val:int):void { __asm(push(val), push(addr), op(0x3b)); }

  Write an int as a 8 bit value at the location addr: 
  _mw8(addr:int, val:int):void { __asm(push(val), push(addr), op(0x3a)); }

  Write a Number as a float at the location addr: 
  _mwf(addr:int, val:Number):void { __asm(push(val), push(addr), op(0x3d)); }

  Write a Number as a double at the location addr:
  _mwd(addr:int, val:Number):void { __asm(push(val), push(addr), op(0x3e)); }
  */

  //Write an int 654321 as a 32 bit value at the location 1000
  asm("__asm(push(654321),push(1000),op(0x3c));");
  //Trace the memory
  asm("ALCVM_Memory.position=1000");
  asm("trace(ALCVM_Memory.readInt());");//should trace 654321 in flashlog.txt
  //Get a 32 bit value at the location 1000 and return as an int
  asm("var temp:int=__xasm(push(1000), op(0x37));");
  asm("trace(temp)");//should trace 654321 in flashlog.txt

  /*Test some AVM2 Instructions*/
  //More AVM2 Instructions can be found at:
  //http://www.anotherbigidea.com/javaswf/avm2/AVM2Instructions.html
  
  //test add: 0xA0 
  int var1=123;
  int var2=321;
  //write (var1+var2)=444 to testData[0] via memory
  //ALCVM_Memory.position=AdrInt;
  //ALCVM_Memory.writeInt(var1+var2);
  asm("__asm(push(%0), push(%1), op(0xA0), push(%2), op(0x3c))" : : "r"(var1), "r"(var2), "r"(AdrInt));
  printf("%d\n",testData[0]);//should print 444

  //test subtract: 0xA1  
  asm("var result:int=__xasm(push(%0), push(%1), op(0xA1));" : : "r"(var1), "r"(var2));//var result=var1-var2;
  //write (var1-var2)=-198 to testData[1] via memory in a different way
  asm("__asm(push(result),push(%0),op(0x3c));":: "r"(AdrInt+4));
  printf("%d\n",testData[1]);//should print -198

  return 0;
} 
/*
References:
http://labs.adobe.com/wiki/index.php/Alchemy:Documentation:Developing_with_Alchemy:AS3_API
http://blog.frankula.com/?p=211
http://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html
http://gcc.gnu.org/onlinedocs/gcc/Simple-Constraints.html
http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html
Special thanks to zazzo9 (http://forums.adobe.com/people/zazzo9)
http://forums.adobe.com/thread/660099
http://forums.adobe.com/message/2101303
http://forums.adobe.com/message/1059161
http://forums.adobe.com/message/1915605
http://forums.adobe.com/message/2101405
http://forums.adobe.com/message/1914780
*/
The compiled swf will display:
123
123
jumped!
This is state2.
iftrue jumped!
444
-198
The flashlog(C:\Documents and Settings\Administrator\Application Data\Macromedia\Flash Player\Logs\flashlog.txt)
654321
654321
[object AlchemyExit]
at global/shellExit()
at cmodule.AlchemyVM_hack::CSystemLocal/exit()
at cmodule.AlchemyVM_hack::CRunner/work()
at ()
at flash.utils::Timer/_timerDispatch()
at flash.utils::Timer/tick

[Update 2012/Jan/06]
Alchemy GOODIES: The C and AS3 mixed syntax using ASM.
It is not a good habit to mix C code with AS3, but this can be handy sometimes:
//AS3 values to C
int myINT = 0;
asm("var ASvar1 = 1; var ASvar2 = 2;");//Embed AS3 code in C
asm("%0 ASvar1+ASvar2" : "=r"(myINT) : );//myINT=ASvar1+ASvar2;
AS3_Trace(AS3_String("myINT="));
AS3_Trace(AS3_Int(myINT));//will print 3

//C values to AS3
int myINT0 = 123456;
asm("var ASvar0:int;");
asm("ASvar0 = %0" :: "r" (myINT0) );
asm("trace('ASvar0=');");
asm("trace(ASvar0);");//will print 123456

Tuesday, January 4, 2011

Ken Silverman's GROUFST2 Terrain Raycaster Ported to AS3


Simple nice heightmap terrain raycaster, a good start for your own voxel engine.
Fork it here:
http://wonderfl.net/c/7d41
or
http://flaswf.googlecode.com/svn/trunk/GROUFST2/
==========
Update: April, 4,2011
GROUFST2 Ported to HaXe(Supporting targeting swf and cpp both):
Source Code: https://flaswf.googlecode.com/svn/trunk/GROUFST2/Groufst2HXNME
==========
Update: 11, 11,2011
Add HTML5 support:
Source Code: https://flaswf.googlecode.com/svn/trunk/GROUFST2/Groufst2HXNME/Groufst2NME_HTML5/
DEMO:  https://flaswf.googlecode.com/svn/trunk/GROUFST2/Groufst2HXNME/Groufst2NME_HTML5/Export/html5/bin/index.html (Very, very slow.)
==========

Original source code can be found at Ken's website:
Qbasic: http://www.advsys.net/ken/voxlap.htm (GROUFST2.BAS)
EVALDRAW: http://www.advsys.net/ken/download.htm (evaldraw.zip\demos\groufst2b.kc)

And

Happy Coding 2011!

Sponsors