This fork mostly contains the config file for my CPU architecture MCPU.
I want to be able to run a project I initially created for the 8bitworkshop IDE on the command-line.
I also might try to embed this into other (web) projects in the future.
YMMV.
A tiny configurable assembler for Verilog projects, as described in the book "Designing Video Game Hardware in Verilog" and integrated into the 8bitworkshop online Verilog IDE.
npm i
node src/asmmain.js <config.json> <file.asm>
Output binary is hex format, one line per word, compatible with Verilog's
$readmemh
command.
cd examples
make
NANOASM can translate custom assembly language for Verilog CPUs. The CPU's language is defined in a a JSON configuration file.
Our assembler's configuration format has a number of rules.
Each rule has a format that matches a line of assembly code, and a bit pattern that is emitted when matched.
For example, this is the rule for the swapab
instruction:
{"fmt":"swapab", "bits":["10000001"]},
The "fmt" attribute defines the pattern to be matched, which in this case is just a simple instruction without any operands.
If the rule is matched, the "bits" attribute defines the machine code to be emitted.
This can be a combination of binary constants and variables.
Here we just emit the bits 10000001
-- i.e., the byte $81
.
Let's say we want to match the following format, sta <n>
where <n>
is a variable 4-bit operand:
sta [0-15] ; 4-bit constant
We can specify different types of variables in the \textbf{vars} section of the configuration file.
For example, this defines a 4-bit variable named const4
:
"const4":{"bits":4}
The assembler rules are big-endian by default (most significant bits first) so if you need constants larger than a single machine word, set the "endian" property:
"abs16":{"bits":16,"endian":"little"}
To include a variable in a rule, prefix the variable's name with a tilde (\textasciitilde).
For example, our sta
rule takes one ~const4
variable:
{"fmt":"sta ~const4", "bits":["1001",0]},
We also have to include the value of the variable in the instruction encoding. To do this, we put an integer into the "bits" array -- 0 for the first variable, 1 for the second, etc.
An example: The assembler is given the instruction sta 15
.
It matches the rule sta ~const4
, and assigns 15 to the first variable slot.
It then outputs the the bits 1001
and then the 4-bit value 15, or 1111
.
The final opcode is 10011111
or $9f
.
Instead of a single integer index, you can emit a slice of bits. This comes in handy for instruction sets where values are not contiguous, for example RISC-V:
// for argument 1, take 7 bits starting at index 5, then 5 bits starting
// at index 0
// {a = argument index, b = bit index, n = number of bits}
{"fmt":"sb ~reg,~imm12(~reg)", "bits":[{"a":1,"b":5,"n":7},2,0,"000",{"a":1,"b":0,"n":5},"0100011"]},
Variables can also be defined by tokens.
For example, the following rule defines a variable reg
with four possible values -- a
, b
, ip
, or none
, encoded as two bits:
"reg":{"bits":2, "toks":["a", "b", "ip", "none"]},
Here's an example of a rule that uses it:
{"fmt":"mov ~reg,[b]", "bits":["11",0,"1011"]},
When decoding mov a,[b]
, the assembler sees that a
is the first token in the variable, and substitutes the bit pattern 00
.
The final bit pattern is 11
00
1011
which makes a full byte.
More complex instructions are possible, by using multiple variables in a single rule:
{"fmt":"~binop ~reg,#~imm8", "bits":["01",1,"1",0,2]},
In this rule, binop
, reg
, and imm8
(2) are variables, identified by the integers 0, 1, and 2.
add b,#123
is an example of a matching instruction.
This rule emits an opcode 16 bits (two bytes) long.
NANOASM supports these directives:
.arch <arch>
-- Required. Loads the file <arch>.json
and configures the assembler.
.org <address>
-- The start address of the ROM, as seen by the CPU.
.len <length>
-- The length of the ROM file output by the assembler.
.width <value>
-- Specify the size in bits of an machine word. Default = 8.
.define <label> <value>
-- Define a label with a given numeric value.
.data $aa $bb ...
-- Includes raw data in the output.
.string .....
-- Converts a string to machine words, then includes it in the output.
.align <value>
-- Align the current IP to a multiple of <value>
.
MIT