NECPU-Assembler
Assembler
How-To
- Command line usage:
NEASM.py [-v/-b] [source_dir] <[targer_dir]>- -v: full Verilog module output
- -b: bin output (not yet implemented)
- Empty lines are allowed
- immediate must be a valid Python integer literal or expression. Note that immediate must not contain any white space characters even if you put an expression!
- immediate will be ealuated with
eval()without safety checking of any sort, so please don't write anything weird here :>- One trick is to write
LINE_NUMBERat immediate field to get the current line number~
- One trick is to write
- Comments (starting with '
//') can be put on an empty line or after a valid instruction - Don't use register 31 because it is reserved for the assembler for pseudo-instructions!
Architecture Description
The NECPU is a 32-bit general purpose register architecture processor with the specifications below. Registers $rs, $rt, and $rd are placeholders for actual general purpose registers $0, $1, $2, ..., $31, each holding a 32-bit value. immediate refers to an immediate value (constant) and label refers to label in the instruction corresponding to a specific line in the code. All immediates are given as 16-bit unsigned values. Whenever an immediate is used as an operand with a register as the other operand, the immediate is zero-extended to 32-bit before computation. All labels will be converted into actual addresses using direct addressing mode.
NECPU can accomodate up to 4294967296 (2^32) addressable memory where each memory location holds 4 bytes (32 bits). NECPU is word (4 bytes) addressable. (It is not byte addressable!)
Every instruction of NECPU takes exactly one clock cycle to complete. Hence, the time taken of a code snippet can easily be calculated.
Instruction Set
We will use the following convension to simplify our description:
- Given a register
$r, the content of the register$ris given asR[$r]. - Given a memory location
addr, the content at the memory locationaddrisM[addr]. PCis the program counter which is a special register that holds the address of the instruction currently being executed.
| Instruction Format | Name | Operation |
|---|---|---|
| NOP | No-Op | Do nothing |
LW $rd, $rs, immediate |
Load Word | R[$rd] = M[R[$rs] + immediate] |
SW $rd, $rs, immediate |
Store Word | M[R[$rs] + immediate] = R[$rd] |
LLI $rd, immediate |
Load Lower Immediate | R[$rd][15:0] = immediate |
LUI $rd, immediate |
Load Upper Immediate | R[$rd][31:16] = immediate |
SLT $rd, $rs, $rt |
Set Less Than | R[$rd] = R[$rs] < R[$rt] |
SEQ $rd, $rs, $rt |
Set Equal | R[$rd] = R[$rs] == R[$rt] |
BEQ $rd, immediate |
Branch On Equal | PC = PC + (R[$rd] == immediate ? 2 : 1) |
BNE $rd, immediate |
Branch On Not Equal | PC = PC + (R[$rd] != immediate ? 2 : 1) |
ADD $rd, $rs, $rt |
Add | R[$rd] = R[$rs] + R[$rt] |
ADDi $rd, $rs, immediate |
Add immediate | R[$rd] = R[$rs] + immediate |
SUB $rd, $rs, $rt |
Subtract | R[$rd] = R[$rs] - R[$rt] |
SUBi $rd, $rs, immediate |
Subtract Immediate | R[$rd] = R[$rs] - immediate |
SLL $rd, $rs, $rt |
Shift Left Logical | R[$rd] = R[$rs] << R[$rt] |
SRL $rd, $rs, $rt |
Shift Right Logical | R[$rd] = R[$rs] >> R[$rt] |
AND $rd, $rs, $rt |
And | R[$rd] = R[$rs] & R[$rt] |
ANDi $rd, $rs, immediate |
And Immediate | R[$rd] = R[$rs] & immediate |
OR $rd, $rs, $rt |
Or | R[$rd] = R[$rs] |
ORi $rd, $rs, immediate |
Or Immediate | R[$rd] = R[$rs] |
INV $rd, $rs |
Invert | R[$rd] = ~ R[$rs] |
XOR $rd, $rs, $rt |
Xor | R[$rd] = R[$rs] ^ R[$rt] |
XORi $rd, $rs, immediate |
Xor Immediate | R[$rd] = R[$rs] ^ immediate |
JMP $rd |
Unconditional Jump | PC = R[$rd] |
Instruction Encoding
- Type A
- SLT
- SEQ
- ADD
- SUB
- SLL
- SRL
- AND
- OR
- INV (
$rtignored) - XOR
- Type B
- LW
- SW
- LLI (
$rsignored) - LUI (
$rsignored) - BEQ (
$rsignored) - BNE (
$rsignored) - ADDi
- SUBi
- ANDi
- ORi
- XORi
- JMP (can also put under type A since only
$rdis used)
Note that the BEQ and BNE instructions of NECPU differ from those of MIPS:
- NECPU's
BEQandBNEinstructions compare a register's value with animmediate, while MIPS compares the values of two registers. - NECPU's
BEQandBNEinstructions can only skip the next one instruction if the condition is satisfied.
Pseudo-Instructions
- JUMP (unstable)
JUMP [label]JUMPinstruction complements the nativeJMPinstruction by accepting a label instead of a raw memory address. It simplifies the programming experience.- To create a label, put
[label]:before the instruction you want to jump to. The label can be any valid string. - JUMP pseudo-instruction now supports jumping forward and backward!
- A
JUMPpseudo-instruction is translated to the following 3 instructions:LUI $31 [Upper half of label's address]LLI $31 [Lower half of label's address]JMP $31
- Note that when a
JUMPpseudo-instruction is placed right after a branch instruction, the first two LI instructions will be put before the branch instruction so that the branch instruction can choose whether to skip theJMPinstruction or not.
- LWI - Load Word Immediate
LWI $rd, immediateLWIbasically combinesLLIandLUIimmediatehere is a 32-bit constant value (zero-extended)
To-Do
- Implement
.datadirective to support preloading data into the ROM.- Support loading external files into ROM (in the format of bin or bmp)
- LA (Load Address) pseudo-instruction to load the address of variable defined in the
.datasection
Disassembler
The disassembler is for the purpose of debugging only.
How-To
- Command line usage:
NEDISASM.py [verilog_instruction_rom_file_dir]- The disassembled file
disasmout.asmwill be created in the current directory

Formed in 2009, the Archive Team (not to be confused with the archive.org Archive-It Team) is a rogue archivist collective dedicated to saving copies of rapidly dying or deleted websites for the sake of history and digital heritage. The group is 100% composed of volunteers and interested parties, and has expanded into a large amount of related projects for saving online and digital history.

