In this blog I share my observations, thoughts and experience about computers, linguistics, philosophy and many other things that interest me.

Wednesday, January 14, 2026

asm359: A Macro Preprocessor is Born

Today marks a significant milestone for the GMS/359 project: we have started the asm359 project, a standalone assembler with a fully functional macro preprocessor.

What is asm359?

It's an assembler for GMS/359 — my FPGA-based computing system inspired by IBM System/360. The key word is inspired: GMS/359 deliberately breaks compatibility with the original architecture to create something cleaner and more modern.

The Preprocessor

The heart of asm359 is its NASM-style macro preprocessor. Over ~4500 lines of carefully modularized C code, it supports:

  • Single-line macros: %define, %xdefine, %idefine
  • Multi-line macros: %macro/%endmacro with parameters
  • Conditional assembly: %if, %ifdef, %ifndef, %elif, %else
  • Repeat blocks: %rep/%endrep
  • String operations: %strlen, %strcat, %substr
  • Context stack: %push, %pop with local macros
  • File inclusion: %include with search paths

The code is organized into clean, focused modules:

pp_token.c      — Tokenizer
pp_macro.c      — Macro storage and lookup  
pp_expand.c     — Macro expansion engine
pp_directive.c  — Directive dispatcher
pp_context.c    — Context and state management
pp_main.c       — Public interface

Design Philosophy: Not Your Father's S/360

While implementing the assembler syntax, I crystallized the GMS/359 design principles:

IBM System/360 GMS/359
Big-endian Little-endian
Opcode last in instruction Opcode first
Source, Destination in ST Destination first (always)
Base+Displacement addressing PC-relative / Direct
BALR R12,0 + USING *,R12 Not needed!

That last point deserves explanation. In S/360, there was no way to read the program counter directly. Programs had to use BALR Rx,R0 to capture the current address into a register, then tell the assembler about it with USING. This was necessary because all memory references were encoded as base register + 12-bit displacement.

GMS/359 doesn't have this limitation. With PC-relative or direct 24-bit addressing, the assembler handles address resolution automatically. No more base register juggling!

Assembly Syntax

The syntax follows modern conventions:

; Destination first, like x86/ARM/RISC-V
LR    R5,R6            ; R5 ← R6
ST    [BUFFER],R3      ; memory ← R3

; Square brackets for memory references
LA    R2,[MSG]         ; Load address of MSG

; Hex suffix, no leading zero required
%define CONSOLE 10h

; NASM-style data directives  
DB    01h              ; Define Byte
DW    1234h            ; Define Word (2 bytes)
DD    value            ; Define Doubleword (4 bytes)
DQ    value            ; Define Quadword (8 bytes)

What's Next?

The preprocessor is complete. Next steps:

  1. Instruction encoding — RR, RX, RS, SI, SS formats
  2. Symbol table — Labels and forward references
  3. Output generation — Raw binary for now, maybe RDOFF later
  4. Integration — Feed directly to GMS/359 via IPL

The Bigger Picture

asm359 is part of a larger vision: a complete, self-consistent computing system that takes the best ideas from 1960s mainframe architecture and implements them with modern sensibilities. Channel I/O? Yes. Big-endian byte swapping headaches? No thank you.

The GMS 2050 CPU, GMS 2870 Multiplexor Channel, and GMS 2291 Video Controller are already running on my Cologne Chip GateMate FPGA. Soon they'll be executing code written in asm359.

No comments: