LSO Assembler
From libopenmetaverse - libomv - Developer Wiki
A very crude LSO assembler has been created based on the information available on the LSO reverse engineering page. It is created using Ville Helin's WLA assemblers. His macro support is good enough that it is a language of its own and thus allowed the LSO assembler to be built on top of his existing assembler using just macros and constant definitions (I told you it was crude! For those curious, it is actually running on the assembler designed for the Super Nintendo sound processor. heh :) ).
Feel free to download the LSO assembler package and try it out.
Instructions
The package given contains the win32 version of the WLA pieces I needed. WLA is cross platform, and thus you can compile it yourself if you do not use win32. The source is available at: [1].
WLA is more complicated that is necessary for this project and creates object files which need to be linked as defined by a project file. I have included a batch file to allow you to effectively ignore and skip these steps.
So, in short, the instructions are:
- download and extract LSO assembler package
- at a command prompt run "asm test" ... this will compile test.asm to test.lso
- read through the example code along with the LSO documentation and you should be on your way to making your own assembled LSO files!
Known limitations
Because the underlying assembler wasn't designed for this task there are some limitations which should be noted.
- In some cases, when asking the assembler to do arithmetic with two integers, such as LONG $12345678 + $87654321 you will get the error: "STACK_CALCULATE: Out of 16bit range." In these cases, just do the math for the assembler (in this case replace the command with LONG $99999999).
- I have not seen this affect any label arithmetic, and the branch command output has been verified for both forward and backwards jumps.
- The assembler doesn't support outputting IEEE-754 floating point values. Therefore you must specify all floating point values (including those for vectors and rotations) as hex values. Someone could write a quick "pre"-preprocessor to do this for us if people actually intend to use this for writing code instead of reverse-engineering purposes. That wasn't the intent of this assembler though.
- Many hex editors have a tool to show you what the IEEE-754 hex values of a number are, or if you don't have one, you can use one of the numerous online converters like this [2]. It adds a bit of time to do the conversion, but with comments the code readibility shouldn't be affected much.
- It appears that WLA stopped supporting address labels in calculations for .DEFINE statements.
"Hello, Avatar!" source
For those curious what the language looks like, here's some example source:
/***************************************************************
As a test this is just the assembly equivalent of some simple
LSL code for which the binary LSO is known.
default
{
state_entry()
{
llSay(0, "Hello, Avatar!");
}
}
000000 00 00 40 00 00 00 00 00 00 00 02 00 00 00 3f ff ..@...........?ÿ
000010 00 00 3f ff 00 00 00 af 00 00 00 b6 00 00 00 00 ..?ÿ...¯...¶....
000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
000030 00 00 00 00 00 00 00 00 00 00 00 64 00 00 00 64 ...........d...d
000040 00 00 00 00 00 00 00 00 00 00 00 64 00 00 00 00 ...........d....
000050 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 ................
000060 00 00 00 01 00 00 00 01 00 00 00 10 00 00 00 00 ................
000070 00 00 00 01 00 00 00 05 00 00 00 00 08 00 00 00 ................
000080 00 00 00 00 05 00 63 5b 5e 00 00 00 00 60 48 65 ......c[^....`He
000090 6c 6c 6f 2c 20 41 76 61 74 61 72 21 00 66 00 00 llo, Avatar!.f..
0000a0 00 00 5c 5e 00 00 00 08 70 11 08 d1 00 17 95 00 ..\^....p..Ñ....
0000b0 00 40 00 00 .@..
... remaining bytes are 00 ...
***************************************************************/
.INCLUDE "LSO.inc"
HeaderBlock:
LONG $00004000 ; Machine memory
LONG $00000000 ; Program Counter (0 means currently not running)
LONG $00000200 ; version
LONG LocalFrame
LONG StackTop
LONG HeapTop
LONG HeapBottom
LONG $00000000
LONG $00000000 ; Current State
LONG $00000000
LONG $00000000
LONG $00000000
LONG $00000000
LONG $00000000
LONG StaticBlock
LONG FunctionBlock
LONG $00000000
LONG $00000000
LONG StateBlock
LONG $00000000
LONG $00000001 ; Bit-Encoded Events to run bit31-0
LONG $00000000
LONG $00000000
LONG $00000000 ; Bit-Encoded Event Mask bit63-32
LONG $00000001 ; Bit-Encoded Event Mask bit31-0 ... bit0=state_entry
StaticBlock:
FunctionBlock:
StateBlock:
LONG 1 ; Numer of States in script
;StateEntry
LONG State0-StateBlock ; Location of State Relative to State Block
LONG $00000000 ; Bit-Encoded Event Mask bit63-32
LONG $00000001 ; Bit-Encoded Event Mask bit31-0 ... bit0=state_entry
State0:
;State Header
LONG 5 ;Size of Header
.db 0 ;null terminated string
State0_header_end:
; Pointer to Code Chunk (relative to end of State Block header)
LONG State0_state_entry-State0_header_end
LONG 0 ; Call Frame Size
State0_state_entry:
;Code Chunk Header
LONG 5 ;Size of Code Chunk Header
.db 0 ;Null Terminated String
PUSH_VOID4
PUSH_ARGS_START
PUSH_INTEGER $00000000
PUSH_STRING "Hello, Avatar!"
PUSH_LOCAL_ALLOC $00000000
PUSH_ARGS_END
PUSH_INTEGER $00000008
MATH_ADD Integer, Integer
BUILD_CALL_FRAME
CALL_BUILTIN llSay
RETURN
State0End:
HeapTop:
;HeapEntry DataSize(long), Type(byte), Reference count(word)
;marks the end of the heap?
HeapEntry $4000,Void,0
HeapBottom:
.org $3FFF
StackTop:
LocalFrame: