The FASM Advantage: Stripping Squirrel to the Bone
Why is the FASM source shorter than the C source? The official C SDK relies on a giant wrapper structure called
SquirrelAPI. Every time C code calls
sq->pushstring, the compiler generates code to look up an offset, dereference a pointer, and then execute.
In FASM, we skip the middleman. We define the offsets (like
SQ_PUSHSTRING = 0x7C) as direct constants. We aren't asking a struct where the function is; we are telling the CPU exactly where to jump. We also skip the
sprintf overhead used in the C
RegisterSquirrelFunc for parameter checking.
The Linux Portability Factor One of the biggest strengths of writing in FASM is how close we are to the binary level. While the current source is a PE DLL for Windows, a
Linux version is entirely possible with minimal effort.
The cross-platform logic: On Windows, the server uses LoadLibrary() to bring the module into memory. On Linux, it uses dlopen().
To port this to a Linux-based server, we would simply:
- Change the format PE header to format ELF.
- Adjust the SQLoad export to match the Linux shared object symbols (.so).
- Swap Win32-specific setup (like AllocConsole) for standard ioctl or write syscalls.
The "Direct Include" Revolution Normally, to use an external library in Squirrel, you have to write a C++ bridge and register every single function. By writing a custom
Loader Module in FASM, we can hook
sq_compile.
Instead of registering functions one by one, the FASM module acts as a dynamic linker—using
GetProcAddress (Win) or
dlsym (Linux) to map exports directly into the Squirrel Root Table at runtime. This allows a script to
include() a system library as if it were a native part of the VM.
The Full FASM Sourceformat PE DLL at 0x10000000
entry DllMain
include 'win32ax.inc'
; =============================================================================
; SQUIRREL API OFFSETS
; =============================================================================
SQ_GETTOP = 0x50
SQ_PUSHSTRING = 0x7C
SQ_PUSHINTEGER = 0x84
SQ_PUSHBOOL = 0x88
SQ_GETINTEGER = 0xB0
SQ_SETNATIVECLOSURENAME = 0xDC
SQ_PUSHROOTTABLE = 0x108
SQ_NEWSLOT = 0x11C
SQ_POP = 0x44
SQ_NEWCLOSURE = 0x70
section '.data' data readable writeable
sq_v dd 0
sq_ptr dd 0
szHelloName db 'Hello', 0
szHelloMsg db 'FASM: UserData retrieved successfully!', 10, 0
szSumName db 'Sum', 0
szInitMsg db 10,'--- FASM Squirrel Module Initialized Synchronously ---', 10, 0
section '.code' code readable executable
DllMain:
mov eax, 1
ret 12
; -----------------------------------------------------------------------------
; SQLoad(v, api) - Synchronous Entry Point
; -----------------------------------------------------------------------------
SQLoad:
push ebp
mov ebp, esp
push esi
push ebx
; 1. Store the VM and API pointers immediately
mov eax, [ebp+8]
mov [sq_v], eax
mov esi, eax ; ESI = HSQUIRRELVM
mov eax, [ebp+12]
mov [sq_ptr], eax
mov ebx, eax ; EBX = API Pointer
; 2. Initialize Console for debugging
invoke AllocConsole
cinvoke printf, szInitMsg
; 3. Register "Hello"
push szHelloName
push Hello
push esi
call RegisterFunc
add esp, 12
; 4. Register "Sum"
push szSumName
push Sum
push esi
call RegisterFunc
add esp, 12
; 5. Return 0 (or 1 depending on LU requirements, usually 0 for success in SQ modules)
xor eax, eax
pop ebx
pop esi
pop ebp
ret
; -----------------------------------------------------------------------------
; RegisterFunc(v, func_ptr, name)
; -----------------------------------------------------------------------------
RegisterFunc:
push ebp
mov ebp, esp
push ebx
push esi
mov esi, [ebp+8] ; v
mov ebx, [sq_ptr] ; api
; sq_pushroottable(v)
push esi
call dword [ebx + SQ_PUSHROOTTABLE]
add esp, 4
; sq_pushstring(v, name, -1)
push -1
push dword [ebp+16]
push esi
call dword [ebx + SQ_PUSHSTRING]
add esp, 12
; sq_newclosure(v, func_ptr, 0)
push 0
push dword [ebp+12]
push esi
call dword [ebx + SQ_NEWCLOSURE]
add esp, 12
; sq_setnativeclosurename(v, -1, name)
push dword [ebp+16]
push -1
push esi
call dword [ebx + SQ_SETNATIVECLOSURENAME]
add esp, 12
; sq_newslot(v, -3, SQFalse)
push 0
push -3
push esi
call dword [ebx + SQ_NEWSLOT]
add esp, 12
; sq_pop(v, 1)
push 1
push esi
call dword [ebx + SQ_POP]
add esp, 8
pop esi
pop ebx
pop ebp
ret
; -----------------------------------------------------------------------------
; Sum(v) - Squirrel Function
; -----------------------------------------------------------------------------
Sum:
push ebp
mov ebp, esp
sub esp, 8 ; Local vars for integers
push ebx
push esi
mov esi, [ebp+8] ; v
mov ebx, [sq_ptr] ; api
; Check arg count (Stack top)
push esi
call dword [ebx + SQ_GETTOP]
add esp, 4
cmp eax, 3 ; Root + 2 args
jne .wrong_args
; Get Arg 1 (Slot 2)
lea eax, [ebp-4]
push eax
push 2
push esi
call dword [ebx + SQ_GETINTEGER]
add esp, 12
; Get Arg 2 (Slot 3)
lea eax, [ebp-8]
push eax
push 3
push esi
call dword [ebx + SQ_GETINTEGER]
add esp, 12
; Add
mov eax, [ebp-4]
add eax, [ebp-8]
; Push Result
push eax
push esi
call dword [ebx + SQ_PUSHINTEGER]
add esp, 8
jmp .finish
.wrong_args:
push 0
push esi
call dword [ebx + SQ_PUSHBOOL]
add esp, 8
.finish:
mov eax, 1
pop esi
pop ebx
mov esp, ebp
pop ebp
ret
; -----------------------------------------------------------------------------
; Hello(v) - Squirrel Function
; -----------------------------------------------------------------------------
Hello:
push ebp
mov ebp, esp
push ebx
mov ebx, [sq_ptr]
push -1
push szHelloMsg
push dword [ebp+8]
call dword [ebx + SQ_PUSHSTRING]
add esp, 12
mov eax, 1
pop ebx
pop ebp
ret
SQUnload: xor eax, eax
ret
SQCallback: xor eax, eax
ret
SQPulse: xor eax, eax
ret
section '.idata' import data readable
library kernel32,'KERNEL32.DLL', msvcrt,'MSVCRT.DLL'
import kernel32, AllocConsole,'AllocConsole'
import msvcrt, printf,'printf'
section '.edata' export data readable
export 'LU_Mod.dll', \
SQLoad, 'SQLoad', \
SQUnload, 'SQUnload', \
SQCallback, 'SQCallback', \
SQPulse, 'SQPulse'
section '.reloc' fixups data discardable
High Performance. Zero Bloat. Bare Metal.
If anyone is interested in being able to directly load a dll inside of Squirrel and skip the standard way of importing exported functions, I can work on that very soon