Close

SPAM-1 Assembler

A project log for SPAM-1 - 8 Bit CPU

8 Bit CPU in 7400 with full Verilog simulator and toolchain

john-lonerganJohn Lonergan 11/14/2020 at 19:540 Comments

Decided to get on with writing an assembler for SPAM-1 and use this as an opprtunity to learn about writing grammars.

My previous effort at writing an Assembler was way back when I was experimenting with using Google sheets for the assembler for my Logisim model. At that time I was shy of speaking on camera so I recorded a silent video of using that software. See "Prequel 1" below. Attempting to use Google sheets for the toolchain is quite limiting obviously. I turned to using Icarus Verilog to te idea of using Google sheets for anything more was bust. So I've reverted to proper dev tools and Linux.

My first attempt recently was using Antlr4 to write a grammar and parser and I spent a couple of days on this. But I became quickly frustrated with the approach. I found it pretty difficult to get the software to do what I wanted and while there's a lot of documentation but it didn't help me much when I had problems. Also I didn't particularly like the approach of writing a text file grammar and then having to compile it into code. I spent quite a few years programming in Scala and I was aware of better alternatives.

So, I switched to using scala and scala's "parser combinators" to build an assembler. Parser combinators are just a library built into the SDK that provide some fancy Scala operator syntax to allow the building of Lexer/Parsers really easily, and without all the hassle of Antlr and the entire thing is done in the scala language itself and not two different modes like Antlr. This aproach shortened the dev cycle and the strong type system in scala helped to direct the work and avoid subtle type related bugs. I also found a bunch of useful parser combinator stuff to use for inspiration such as a 6502 parser that I could learn from.

Every operation in SPAM-1 is an assignment like REGA=REGB+REGC and the assembly language looks like this ...

; define a constant called SOMEVAR using a constant expression
; forward references to label addresses are permitted

SOMEVAR:    EQU ($0100 + %1010 + $f + 1+2+(:LABEL2+33)) ; some arbitrarily complicated constant expression

; grab the top and bottom bytes of the constant SOMEVAR into two constants

TOPBYTE:    EQU <:SOMEVAR                        ; top 8 bits into TOPBYTE
BOTBYTE:    EQU >:SOMEVAR                        ; bottom 8 bits into BOTBYTE

; demo setting registers to constants

            REGA = $ff                          ; set A to the constant hex ff but do not set processor status flags
            REGB = $ff    _S                    ; set A to the constant hex ff and set the processor status flags

; registers can be set to results of ALU operations

LABEL1:     REGA = REGB   _C_S                  ; if Carry is set then update A to value of B and set the flags
            REGA = REGB
            REGA = REGA A_PLUS_B REGC           ; set A = A + B but do not set flags
            REGA = REGA + REGC                  ; set A = A + B but do not set flags            REGA = REGB A_MINUS_B [:SOMEVAR]    ; set B to the data at RAM location :SOMEVAR
LABEL2:
            REGA = :TOPBYTE                     ; set A to the constant

; unconditional jump to whatever SOMEVAR was pointing to

            PCHITMP = :TOPBYTE                  ; prepare the top PC register for a jump
            PC      = :BOTBYTE                  ; execute the jump to the location defined by {TOPBYTE:PCHITMP}

END

You can see the code for the assembler here .. https://github.com/Johnlon/spam-1/tree/master/verilog/assembler 

The grammar is pulled together by the Parser class https://github.com/Johnlon/spam-1/blob/master/verilog/assembler/src/main/scala/Parser.scala

Next revision will be to have it write the ROM files for the Verilog simulation so I can double check the solution. I've written some automated scalatest tests so I'm hopeful.

Have fun.

===

Below is my eariest effort at a YT video :)

Discussions