This article is a hands-on introduction to the Torque assembler, showing how to use it to write a program for the PIC10F200 microcontroller.
The language
Torque is a lightweight meta-assembler that gives you the tools to take an instruction set specification from a datasheet and turn it into an expressive and ergonomic programming language. The Torque language is bare-bones, providing only integers, bit sequences, labels, and macro expansion, but it’s general enough to be able to lever the right bits into the right order to write a program for any processor.
The main project page has downloads, source code, and the full user manual.
The microcontroller
The PIC10F200 is the least powerful microcontroller offered by Microchip, priced around $1 USD each. It uses a 12-bit fixed-width instruction format, with 256 words of program memory, 16 bytes of general-purpose memory, and 4 input/output pins. It runs at 1 million instructions per second and draws around 350 μW while doing so.
The datasheet can be found here.
The program
The program we’ll be writing will be that enduring classic, the blinking LED. We’ll turn a pin on, wait half a second, turn the pin off, wait another half a second, and then loop.
This is enough of a task to show off the various language features without getting bogged down by algorithms and circuit design. If you want to jump to the completed program listing, click here.
Getting started
The PIC10F200 has an instruction set with 33 instructions, each encoded as a 12-bit word.
The first step will be to assemble a program containing just a single instruction in order to demonstrate the basic features of a Torque program. We’ll use the GOTO
instruction, using it to jump back to the beginning of the program.
The GOTO
instruction
The datasheet lists the full instruction set in a table on page 46. The column titled ‘12-Bit Opcode’ shows the sequence of bits used for each instruction. The GOTO
instruction uses the 12-bit sequence 101k_kkkk_kkkk
, where the bits marked k
contain the address to jump to.

This can be translated directly into the Torque language using a ‘word template’, a fixed-width sequence of bits into which other values can be packed. The syntax for a word template that represents the GOTO
instruction jumping to address zero is:
#1010_0000_0000
This is an entire valid Torque program, assembling to a single GOTO
instruction. Assemble it with the following command, where inhx32
is the Intel Hex format used by Microchip’s programming tools:
tq program.tq output.hex --format=inhx32
A better way with macros
In the previous example, the instruction is an unreadable binary sequence and the address is hard-coded to be zero. We can do a lot better.
Macros provide a way to associate a name with a fragment of code, allowing us to reuse that fragment by invoking the macro name later on in the program. We can define a macro for the GOTO
instruction from before, and then invoke it to insert the instruction into the program:
%GOTO #1010_0000_0000 ; GOTO GOTO
The first line defines the macro, and the following lines invoke it twice, assembling to two 12-bit words. But we’re still not quite where we want to be, we want to be able to pass any address to the instruction instead of having it hard-coded.
Passing in values
Macros don’t just have to expand to a static code fragment, they can also receive values as arguments. We can make a small change to our macro definition from before:
%GOTO:k #101k_kkkk_kkkk ; GOTO:1 GOTO:0
This new GOTO
macro takes a single integer value as an argument, which is given the name k
, and that value is packed into the k
field of the word template each time the macro is invoked. Integers can be given in decimal, hexadecimal, or binary, as 29
, 0x1D
, or 0b11101
.
Named fields in word templates work by searching for a macro with that name that expands to an integer, and then that integer is packed into the bits of the field. Arguments inside a macro definition are treated as regular macros, expanding to the value that was passed.
This program also assembles to two 12-bit words. It will jump to address 1, which is the address of the second GOTO
, and then will jump to address 0, which is the address of the first GOTO
.
Addressing with labels
Finally, instead of using an integer literal as an address, we can have Torque automatically calculate an address value from a label.
%GOTO:k #101k_kkkk_kkkk ; @one GOTO:two @two GOTO:one
Labels are treated as regular macros that expand to the address of the next word in the program.
Writing the program
We now know most of the Torque language, and can build up to the full program.
Quick architecture overview
The PIC10F200 contains 24 8-bit ‘file registers’, and one 8-bit ‘working register’ (the accumulator). 16 of the file registers are general-purpose, to be used however we wish.
Most instructions will operate on any given file register (called f
) and the accumulator (called W
), with the result of an operation being stored back into either one depending on the state of the bit marked d
. For example, the ADDWF
instruction #0001_11df_ffff
will add the values of the accumulator and the register f
, writing the result back to f
if d
is set.
Configuring an output pin
We need to configure the first input-output pin (called GP0
) to be an output pin, as all pins are set to be inputs by default.
We can do this with the TRIS
instruction in the datasheet. This will write the contents of the accumulator to the hidden TRISGPIO
register controlling the mode of each pin.
%SET-W:k #1100_kkkk_kkkk ; ( Write value k to W ) %SET-GPIO #0000_0000_0110 ; ( Write W to TRISGPIO ) SET-W:0b1110 SET-GPIO
Here we set pin GP0
to be an output pin by clearing the lowest-order bit of the TRISGPIO
register.
Toggling the pin
To turn the GP0
pin on and off, we set and clear the lowest-order bit of the GPIO
register (register 0x06
).
%GOTO:k #101k_kkkk_kkkk ; ( Jump to address k ) %SET-BIT:f:b #0101_bbbf_ffff ; ( Set bit b of register f ) %CLR-BIT:f:b #0100_bbbf_ffff ; ( Clear bit b of register f ) %GPIO 0x06 ; %GP0 0 ; @start SET-BIT:GPIO:GP0 CLR-BIT:GPIO:GP0 GOTO:start
Here we turn the pin on, then off, and then loop from the top.
Slowing down the program
Lastly, we need to slow the loop down so that the flashes are visible, otherwise the LED will flash 250,000 times a second. We can create a delay by using a long loop, using registers to store our loop counters.
The general-purpose registers start from register address 0x10
. We can create macros to associate names with each of the first three general-purpose registers:
%LOOP1 0x10 ; %LOOP2 0x11 ; %LOOP3 0x12 ;
Conditional logic on the PIC10F200 works by skipping the next instruction if a condition is true. We can combine the DEC-SKIP
instruction (called DECFSZ
in the datasheet) with the GOTO
instruction to decrement our loop counter and jump to the top of the loop. When the loop counter reaches zero, the DEC-SKIP
instruction will skip over the GOTO
, breaking us out of the loop.
%DEC-SKIP:f #0010_111f_ffff ; ( Decrement register f, skip next instruction if zero ) %NEXT:f:k DEC-SKIP:f GOTO:k ; ( Decrement register f, jump to address k if not zero )
We need to calculate the correct value for each loop counter in order to delay for exactly half a second, or 500,000μs. Each instruction takes 1μs to execute, or 2μs if a jump occurs. Our NEXT
macro takes 3μs when looping back to the top, and 2μs when breaking.
The following example will set LOOP1
to zero and then repeatedly decrement it 256 times until it reaches zero again, taking a total duration of 769μs:
%SET-W:k #1100_kkkk_kkkk ; ( Write value k to W ) %SET-f:reg #0000_001f_ffff ; ( Write W to register f ) %SET:f:k SET-W:k SET-f:f ; ( Write value k to register f ) SET:LOOP1:0 @loop NEXT:LOOP1:loop
We can extend the duration further if we nest multiple loops within one other, using maths to determine the correct value for each loop counter in order to take a total duration of exactly 500,000μs:
@delay SET:LOOP1:127 @loop1 SET:LOOP2:207 @loop2 SET:LOOP3:5 @loop3 NEXT:LOOP3:loop3 NEXT:LOOP2:loop2 NEXT:LOOP1:loop1
Bringing it all together
The completed Torque program is below. The CALL
and RETURN
instructions have been introduced to allow us to call the delay code from two places.
%SET-GPIO #0000_0000_0110 ; ( Write W to TRISGPIO register ) %SET-W:k #1100_kkkk_kkkk ; ( Write value k to W ) %SET-f:f #0000_001f_ffff ; ( Write W to register f ) %SET:f:k SET-W:k SET-f:f ; ( Write value k to register f ) %GOTO:k #101k_kkkk_kkkk ; ( Jump to address k ) %CALL:k #1001_kkkk_kkkk ; ( Call the subroutine at address k ) %RETURN #1000_0000_0000 ; ( Return from the current subroutine ) %SET-BIT:f:b #0101_bbbf_ffff ; ( Set bit b of register f ) %CLR-BIT:f:b #0100_bbbf_ffff ; ( Clear bit b of register f ) %DEC-SKIP:f #0010_111f_ffff ; ( Decrement f, skip forward if zero ) %NEXT:f:k DEC-SKIP:f GOTO:k ; ( Decrement f, jump to k if not zero ) %GPIO 0x06 ; %GP0 0 ; %LOOP1 0x10 ; %LOOP2 0x11 ; %LOOP3 0x12 ; ( Configure GP0 to be an output pin. ) SET-W:0b1110 SET-GPIO ( Blink the GP0 pin. ) @start SET-BIT:GPIO:GP0 CALL:delay CLR-BIT:GPIO:GP0 CALL:delay GOTO:start ( Delay for half a second. ) @delay SET:LOOP1:127 @loop1 SET:LOOP2:207 @loop2 SET:LOOP3:5 @loop3 NEXT:LOOP3:loop3 NEXT:LOOP2:loop2 NEXT:LOOP1:loop1 RETURN
This can be assembled with Torque using the following command, which will output a .hex
file in the Intel Hex format used by Microchip’s programming tools:
tq program.tq output.hex --format=inhx32
Further reading
- The main Torque project page
- Torque: Programming the TRS-80