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

No comments:

Post a Comment

Sponsors