Examples

This page contains code examples that demonstrate programming techniques for the Torque meta-assembler.

Integer encodings

Little-endian

This macro encodes an integer in two bytes as a 16-bit little-endian value.

%BYTE:n    #nnnn_nnnn ;
%BYTE-L:n  BYTE:[n 0xFF <and>] ;
%BYTE-H:n  BYTE:[n    8 <shr>] ;
%16LE:n    BYTE-L:n BYTE-H:n ;

16LE:9000

Big-endian

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      #nnnn_nnnn ;
%BYTE-I:n:i  [n [8 i *] <shr> 0xFF <and>] ;
%32BE:n      BYTE-I:n:3 BYTE-I:n:2 BYTE-I:n:1 BYTE-I:n:0 ;

32BE:96000

LEB128

This macro encodes an integer using the variable-length LEB128 integer encoding. The macro recurses into itself in order 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  #0ccc_cccc ;

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   #nnnn_nnnn ;
%ASCII:c  #0ccc_cccc ;
%STRING:"string"
  ASCII:string 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   #nnnn_nnnn ;
%ASCII:c  #0ccc_cccc ;
%STRING:"string"
  BYTE:[~end ~start -]
  &start ASCII:string &end ;

STRING:"Length-prefixed string"

Structure

Padding

This macro expands to n null bytes, using recursion.

%PAD:n
  ?[n 0 >] {
    #0000_0000
    PAD:[n 1 -]
  } ;

PAD:16

Alignment

This macro aligns the following value to an arbitrary address boundary.

%ALIGN:n
  &here
  |[~here [n 1 -] + n / n *] ;

ALIGN:4

It works by pinning to the next address that is a multiple of the passed integer.

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-block}:{false-block}
  ?[cond 0 !=] true-block
  ?[cond 0 ==] false-block

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     #nnnn_nnnn          ;
%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