[HN Gopher] Show HN: Torque - A lightweight meta-assembler for a...
       ___________________________________________________________________
        
       Show HN: Torque - A lightweight meta-assembler for any processor
        
       Hello everyone, I've been working on this project for the past few
       months.  Torque is a meta-assembler: instead of having an
       instruction set built into the assembler, you use macros to build
       up a small language that decribes an instruction set and then you
       use that to write your program. It's designed to work for any
       microcontroller/processor architecture, you build from the bit
       level upwards so there aren't any assumptions around word widths,
       instruction formats, or endianness.  I created Torque initially to
       write programs for a PIC microcontroller, after running into
       difficulties with the official assembler. I've also used it to
       write programs for the Z80 processor inside an old TRS-80 computer.
       Let me know if you try it out or have any questions!
        
       Author : benbridle
       Score  : 63 points
       Date   : 2025-04-15 21:46 UTC (19 hours ago)
        
 (HTM) web link (benbridle.com)
 (TXT) w3m dump (benbridle.com)
        
       | sitkack wrote:
       | Super fun site!
       | 
       | Did you get inspiration from other assemblers or macro
       | processors?
       | 
       | You have it running on a TRS-80, how does that work? I had no
       | idea Rust could target a TRS-80.
       | 
       | I am getting hints of Forth, Lisp and TCL.
       | 
       | How would you go about laying out structs in memory?
       | 
       | I am sure you considered an internal DSL, what caused you go with
       | something stand alone?
       | 
       | Any thoughts on adding a constraint solver, like Z3 and allowing
       | end users to set constraints on things like the size of a jump.
       | 
       | I could see taking this an growing it into a compiler by making
       | macro(macro(macros txt)))
       | 
       | Is there an internal IR?
       | 
       | Projects for inspiration
       | 
       | https://github.com/mattbierner/Template-Assembly
       | 
       | Specifying representations of machine instructions
       | https://dl.acm.org/doi/pdf/10.1145/256167.256225
       | 
       | https://www.semanticscholar.org/paper/Specifying-representat...
       | 
       | Typed Assembly Language (TAL) https://www.cs.cornell.edu/talc/
       | 
       | And you haven't come across it, you are in for a treat
       | https://en.wikipedia.org/wiki/META_II has spawned a whole trove
       | of clones
       | 
       | https://en.wikipedia.org/wiki/OMeta
       | 
       | https://github.com/DalekBaldwin/clometa
        
         | benbridle wrote:
         | Thank you! My main inspiration was the Uxn assembly language
         | [0], which is itself heavily inspired by Forth. I loved how
         | easy it was to build something that looks like a high-level
         | language by just stacking up macros, and I wanted to have that
         | with embedded development too.
         | 
         | Rust isn't involved past implementing the Torque executable;
         | you write your program with the Torque language and then run
         | the assembler on it to convert it to machine code. You can see
         | the whole process of running code on the TRS-80 from start to
         | finish here [1].
         | 
         | For laying out structs, I'd build a macro that expands to the
         | memory representation of the struct. If I wanted a struct
         | representing, say, a 2D point with signed 16-bit little-endian
         | integers for the x and y coords, I would build it from scratch
         | like this (this is a valid program, you can assemble it with
         | Torque):                 %BYTE:n     #nnnn_nnnn          ;
         | %LOW:n      BYTE:[n 0xff <and>] ;       %HIGH:n     BYTE:[n
         | 8 <shr>] ;       %16LE:n     LOW:n HIGH:n        ;
         | %POINT:x:y  16LE:x 16LE:y       ;                 @point-1
         | POINT:50:-7        @point-2 POINT:20:-45       @point-3
         | POINT:0:0
         | 
         | If I want the address of a field, I can add an offset to the
         | struct address, using macros to name the offset values:
         | %POINT.X  0 ;       %POINT.Y  2 ;       SET:[point-3 POINT.X
         | +]:15       SET:[point-3 POINT.Y +]:32
         | 
         | Creating a DSL for an existing language wasn't something I'd
         | ever considered. By being a standalone executable it's really
         | easy to use and share, people don't have to install a whole
         | language toolchain in order to use it.
         | 
         | Regarding constraints solving and jumping, Torque already
         | throws an error if you try to pack too large a value into too
         | small a field. This works really well for things like relative
         | jumps, because jumping too far will create a value that can't
         | fit in the instruction. I'm planning on adding an error-
         | throwing token to the language that could be used alongside
         | expressions and conditions to further constrain the values
         | accepted by a macro, but I'm really happy with the simplicity
         | of the language so far.
         | 
         | The actual internal representation isn't what I'd call an 'IR'
         | per se, nothing like with a C compiler. It's all very
         | pedestrian; the syntax tree is baked down across multiple
         | passes, with macros acting as a glorified copy-paste system.
         | 
         | Thanks for the interest and the links, every one of those
         | linked projects is new to me.
         | 
         | [0] https://wiki.xxiivv.com/site/uxn.html
         | 
         | [1] https://benbridle.com/articles/torque-programming-the-
         | trs-80...
        
       | zadkey wrote:
       | Excellent work man! I know some low level guys who would really
       | appreciate this.
        
         | benbridle wrote:
         | Thanks, that's great to hear! What platforms do they develop
         | for?
        
       | anonzzzies wrote:
       | Well done! I write quite a bit of z80 for msx and this seems a
       | nice addition.
        
       | 2ton_jeff wrote:
       | Very cool and I like the idea of a "meta-assembler." The most-
       | recent version of flatassembler (fasm 2) is built with fasmg
       | which is also a "meta-assembler" of sorts, in that it also
       | doesn't directly support a specific instruction set and instead
       | is a very powerful macro assembler. I'm keen to check out
       | functionality overlaps between the two implementations.
       | 
       | https://board.flatassembler.net/topic.php?t=19389
       | 
       | https://flatassembler.net/download.php
        
         | benbridle wrote:
         | Oh neat! Thanks for the link, I hadn't heard of fasmg before.
         | 
         | It looks like fasmg builds up from the byte level, so it would
         | only work for architectures that use 8-bit words. Torque builds
         | up from the bit level, so it can assemble code for
         | architectures like in PIC microcontrollers, using word sizes of
         | 12 or 14 bits.
         | 
         | However, fasmg does allow a lot more control over the syntax of
         | the language. The documentation shows some pretty powerful
         | string manipulation that's used to parse real x86 assembler
         | code, which makes sense given the purpose of it. Torque doesn't
         | allow overriding the syntax to that degree, the macro
         | invocation syntax is baked into the assembler.
         | 
         | One thing that intrigues me about fasmg is how it handles
         | circular dependencies in expressions [0] (search for
         | 'circular'). Currently in Torque it isn't possible to use a
         | label reference inside a predicate, because the predicate
         | evaluating one way could insert additional code, moving the
         | label and causing the predicate to evaluate the other way [1].
         | But in fasmg it's possible to use the result of an expression
         | as part of its own calculation.
         | 
         | [0] https://flatassembler.net/docs.php?article=fasmg
         | 
         | [1] https://benbridle.com/projects/torque/roadmap.html
        
           | 2ton_jeff wrote:
           | I reached out to the author of fasmg WRT your post and
           | circular dependency interest and he pointed me toward two
           | posts that he wrote very specifically to explain what he
           | believes is unique to fasm/fasmg and allows to handle
           | circular dependencies of many kinds. [0] Types of multi-pass
           | assembly, and [1] related pitfalls.
           | 
           | [0] https://board.flatassembler.net/topic.php?t=20249
           | 
           | [1] https://board.flatassembler.net/topic.php?t=21060
        
             | benbridle wrote:
             | Thank you so much! It looks like the issue I described is
             | what he calls the 'oscillator problem' [0]. This is an
             | absolute goldmine, I'm going to be reading for days.
             | 
             | [0]
             | https://board.flatassembler.net/topic.php?p=178828#178828
        
           | 2ton_jeff wrote:
           | Tomasz (of fasmg) added "one more quick thought" re: 8-bit
           | word not being a limitation, he said that since fasm1 had
           | macros for LZW compression [0] (from the assembler alone) and
           | that this is even easier with fasmg the word length isn't a
           | restricting factor with his design either. Cheers and thanks
           | for sharing.
           | 
           | [0] https://board.flatassembler.net/topic.php?t=14612
        
       | angelmm wrote:
       | Any time I read about Z80, my mind wants to try to run something
       | on a GameBoy emulator.
       | 
       | Pretty cool project!
        
       | eterps wrote:
       | Nice!
       | 
       | Would be interesting to target the RISC CPU of
       | https://www.projectoberon.net with it.
        
       | userbinator wrote:
       | Reminds me of TDASM from the turn of the century:
       | https://web.archive.org/web/20230906054935/http://www.pengui...
       | 
       | I remember there were a few other meta-assemblers I came across
       | in the 80s-90s, so this is definitely not "unchartered
       | territory", but it's good to see another one show up.
       | 
       | Of course, in the other direction there are meta-disassemblers
       | used for analysis in tools like Ghidra.
        
       | kunley wrote:
       | Need to address this point:
       | 
       | "Assemblers tend to be poorly documented"
       | 
       | I wish everything in programming was as good documented as
       | assemblers and ISAs.
        
         | benbridle wrote:
         | I should clarify, I was thinking of microcontroller toolchains
         | when I wrote this. The documentation I've seen for Intel
         | processors is solid.
        
           | kunley wrote:
           | Not to mention ARM64...
           | 
           | And 6502 ;-) (and probably most of the ancient ones)
        
       | bjackman wrote:
       | At first I thought it was useless: "but each ISA will still end
       | up having different effective syntax because the underlying macro
       | systems will not be designed the same".
       | 
       | But then I reread it and realised I was not paying attention to
       | the usecase. It's about making it easy to write assemblers. So
       | this isn't for your Arms and RISC-Vs it's for your random niche
       | microcontrollers where the vendor-provided toolchain kinda sucks.
       | 
       | Seems cool!
       | 
       | I've experienced a couple of under-documented assemblers in my
       | time. In neither case did this turn out to be that much of a
       | problem in practice, but I guess I just don't write that much
       | assembly.
        
       | MathMonkeyMan wrote:
       | I'm reminded of a 2016 [talk][1] where Rob Pike describes the
       | common-denominator assembly language that the Go compiler
       | generates. Then that assembly is translated into machine-specific
       | code via table lookups. See the 11 minute mark.
       | 
       | [1]: https://www.youtube.com/watch?v=KINIAgRpkDA
        
       | roetlich wrote:
       | This looks very cool, maybe I'll try this for risc-v if I have
       | some time. I guess you could also use this to write RAW images,
       | or other binary data.
        
       ___________________________________________________________________
       (page generated 2025-04-16 17:03 UTC)