This page contains code examples demonstrating programming techniques for the Torque meta-assembler.
Integer encodings
Endian encoding
This example implements macros for encoding a 16-bit value as little-endian or big-endian.
%BYTE:n #nnnnnnnn; %LBYTE:n BYTE:[n 0xFF <and>]; ( extract the low byte of a value ) %HBYTE:n BYTE:[n 8 <shr>]; ( extract the high byte of a value ) %16LE:n LBYTE:n HBYTE:n; ( encode a 16-bit little-endian value ) %16BE:n HBYTE:n LBYTE:n; ( encode a 16-bit big-endian value ) 16LE:9000 16BE:9000
This macro encodes an integer in four bytes as a 32-bit big-endian value. A more general approach is taken to breaking the value into bytes.
%BYTE:n #nnnnnnnn; %XBYTE:n:i BYTE:[n [8 i *] <shr> 0xFF <and>]; %32BE:n XBYTE:n:3 XBYTE:n:2 XBYTE:n:1 XBYTE:n:0; 32BE:96000
LEB128
This macro encodes an integer using the variable-length LEB128 integer encoding. The macro recurses into itself to handle integers of arbitrary magnitude.
%LEB128:n ?[n <len> 7 <gth>] { BYTE:[n 0x7f <and> 0x80 <or>] LEB128:[n 7 >>] } ?[n <len> 7 <leq>] { BYTE:[n 0x7f <and>] }; LEB128:51966
The encoding works by breaking an integer down into one or more 7-bit sections, then packing each section into a byte ala 7-bit ASCII, using the highest bit to indicate that there are still more bytes to come, and finally flipping the bytes to little-endian order. The process looks like the following, encoding the value 51966:
51966 ( Decimal )
11001010 11111110 ( Binary )
0000011 0010101 1111110 ( 7-bit groups )
00000011 10010101 11111110 ( 8-bit groups )
11111110 10010101 00000011 ( Little-endian )
0xfe 0x95 0x03 ( Hexadecimal )
Text encodings
ASCII
This macro encodes a string using the 7-bit ASCII encoding. An error is raised if a non-ASCII character is included.
%ASCII:c #0ccccccc; ASCII:"This is a string."
UTF-8
This macro encodes a string using the variable-width UTF-8 text encoding. Conditional blocks are used to select the correct byte packing for each character value.
%UTF8-B1:c #0ccccccc; %UTF8-B2:c #10cccccc; %UTF8-B3:c #110ccccc; %UTF8-B4:c #1110cccc; %UTF8-B5:c #11110ccc; %UTF8:c ?[c 0x7f <=] { UTF8-B1:c } ?[c 0x80 >= c 0x07ff <= <and>] { UTF8-B3:[c 6 >>] UTF8-B2:[c 0x3f <and>] } ?[c 0x0800 >= c 0xffff <= <and>] { UTF8-B4:[c 12 >>] UTF8-B2:[c 6 >> 0x3f <and>] UTF8-B2:[c 0x3f <and>] } ?[c 0x010000 >= c 0x10ffff <= <and>] { UTF8-B5:[c 18 >>] UTF8-B2:[c 12 >> 0x3f <and>] UTF8-B2:[c 6 >> 0x3f <and>] UTF8-B2:[c 0x3f <and>] }; UTF8:"tohutÅ"
Null-terminated
This macro adds a null byte to the end of an ASCII-encoded string.
%BYTE:n #nnnnnnnn; %ASCII:c #0ccccccc; %STRING:[chars] ASCII:chars BYTE:0; STRING:"Null-terminated string"
Length-prefixed
This macro adds a one-byte length prefix to the start of an ASCII-encoded string.
%BYTE:n #nnnnnnnn; %ASCII:c #0ccccccc; %STRING:[chars] BYTE:[~end ~start -] &start ASCII:chars &end; STRING:"Length-prefixed string"
Structure
Padding
This macro expands to n null bytes, using recursion.
%PAD:n ?[n 0 >] { #00000000 PAD:[n 1 -] }; PAD:16
Alignment
This macro pads the following token to the next n-word boundary.
%ALIGN:n &here PAD:[ n ~here - <abs> n <mod> ]; ALIGN:4
Flow control
if-else
This macro implements an if-else conditional statement for a target instruction set.
%IF-ELSE:cond:{true-block}:{false-block} JMP-IF:cond:~true false-block JMP:~end &true true-block &end; IF-ELSE:[n 0 >]:{ DO-THING }:{ OTHER-THING }
if-else (static)
This macro implements an if-else conditional statement as a macro, with evaluation performed at assemble-time. Only one of the two blocks will be included in the assembled program.
%IF-ELSE:cond:{true}:{false} ?[cond 0 !=] true ?[cond 0 ==] false; IF-ELSE:[n 0 >]:{ DO-THING }:{ OTHER-THING }
for-loop
This macro implements a for-loop for a target instruction set.
%FOR:n:{body} SET:counter:n &loop body DEC:counter JMP-IF:counter:~loop; FOR:3:{ DO-THING }
for-loop (static)
This macro implements an unrolled for-loop as a macro, with looping performed at assemble-time. The body of the loop will be duplicated n times.
%FOR:n:{body} ?[n 0 >] { body FOR:[n 1 -]:body }; FOR:3:{ DO-THING }
Objects
2D point
This example shows how to create and modify an object representing a 2D point, using labels and named offsets for field access.
%BYTE:n #nnnnnnnn; %16BE:n BYTE:[n 8 <shr>] BYTE:[n 0xff <and>]; %POINT:x:y 16BE:x 16BE:y; %POINT.X 0; %POINT.Y 2; @point-1 POINT:50:-7 @point-2 POINT:20:-45 @point-3 POINT:0:0 SET:[point-3 POINT.X +]:15 SET:[point-3 POINT.Y +]:32