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
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.