ISM Assembler

For a Computer Architecture course, a teammate and I created a working CPU in Verilog. It is a Harvard architecture processor with an ALU and registers. It implements a MIPS-like instruction set including load word, store word, add, subtract, set less than, xor immediate, no-op, jump, jump register, and branch not equal.

Writing assembly by hand is not bad for simple tests, but when I started to write more complicated programs, I got tired of remembering what I was using the $s0 register for and so on.

So, just for fun, I wrote a little Python script to translate assembly-like code into machine code for my cpu. In retrospect, I guess it's what you could call a macro assembler. The syntax is a bit like a higher level language, assigning values with "a = b" or adding with "c = a + b". It's not a compiler, but doesn't really need to be, and with too much abstraction you'd be better off writing in C anyway.

// ISM example, by Ben Fisher
// find prime numbers from 2 to 100.
// place the largest found in result.
registers i, tmp, base, limit, result

// clear memory
for i,300,0
  dataWordIndex[i] = zero
endfor

limit = 100
for base,2,100
  // check if we've already seen this number come up
  tmp = dataWordIndex[base]
  (tmp != zero) goto CONTINUELOOP
  
  // check off multiples of this number.
  i=base
  INNERLOOP:
    i = i + base
    dataWordIndex[i] = one
    (i < limit) goto INNERLOOP
  
  CONTINUELOOP:
endfor

// put largest prime found into result
for i,100,2
  tmp = dataWordIndex[i]
  if (tmp==zero)
    result = i
    goto DONE
  endif
endfor

DONE:

It is kind of thrilling to see the thousands of AND, OR, NOT gates you typed into Verilog do real computation like searching for prime numbers.


The earlier version I wrote yesterday still uses instruction names:

// add numbers from 1 to 25 and put result in sum.

registers i, const_one,sum,highest
seti const_one=1
seti sum=0
seti highest=25
seti i=0
LOOP:
 add i=i+const_one
 add sum=sum+i
 bne (i!=highest) goto LOOP


Then I started to add more features. I added syntax shortcuts like a++, and simulated more types of branching even though the cpu only supported bne. I also added inference, where each line is checked to infer which operation you are trying to do. For example, if the line is "c=a+b" it is clear that you are doing an add instruction, so you don't have to type "add". Also, it now understands simple loops and if structures, translating them to assembly. It looks like Python if you squint.

It was fun to come up with syntax for this. Branches are done with the different, yet readable syntax "(a==b) goto Label". dataWordIndex means that the index is automatically multiplied by four, so that it can be used as an array of 32 bit integers.

When the script runs, it also prints out Verilog $display statements, which can be placed in the Verilog code to see the values of the registers.

Here is the code. ism_simple.py is the first version. I realize that it won't be useful unless you have a mips assembler and cpu.