Due: April 14th, 11:59PM
TA: Youngjoon Cheon (yjcheon@csl.korea.ac.kr)
In this project, you will implement a two-pass assembler for a subset of
the RISC-V (RV32I) instruction set. The assembler reads a .s assembly file
and produces a binary .o file that the simulator can execute.
assembly (.s) → [assembler] → binary (.o) → [simulator]
This is a simplified assembler which does not support linking, so there are no
symbol or relocation tables. A single .s file is the entire program.
Your assembler must handle the following instructions.
| Format | Instructions |
|---|---|
| R-type | add, sub, and, or, sltu |
| I-type | addi, andi, xori, ori, sltiu, slli, srli, lw, jalr |
| S-type | sw |
| B-type | beq, bne, blt, bge |
| J-type | jal |
| U-type | lui, auipc |
| Pseudo | la (expands to auipc + addi) |
Additional constraints:
- Signed operations (
add,sub) and unsigned operations (sltiu,sltu,slli,srli) must both be supported. - Only word-sized (4-byte) loads and stores (
lw,sw) are required. - Immediates may be decimal (e.g.,
1024) or hexadecimal (e.g.,0x400). - Register names are written as
x0throughx31.
la loads the 32-bit address of a data-section label into a register.
It expands into auipc + addi:
la x<n>, VAR1
→ auipc x<n>, D[31:12] # hi20 = (D + 0x800) >> 12, D = VAR1 − PC
→ addi x<n>, x<n>, D[11:0] # lo12 = D − (hi20 << 12)
Adding 0x800 before the right-shift ensures that the signed 12-bit addi
immediate (lo12) always stays within [−2048, +2047].
Example — D = 0x0FCFFFFC:
auipc x2, 0x0FD00 # hi20 = (0x0FCFFFFC + 0x800) >> 12
addi x2, x2, 0xFFC # lo12 = D − (hi20 << 12)
Example — D = 0x0FC00000 (lo12 = 0, addi can be omitted):
auipc x2, 0x0FC00
| Directive | Description | Start address |
|---|---|---|
.data |
Static data segment | 0x10000000 |
.text |
Instruction segment | 0x00400000 |
.word |
32-bit data word | — |
Rules:
- The
.datasection must appear before.text. - Each section appears exactly once.
- All words in
.datahave an initial value.
- Fields (opcode, operands) are separated by tabs.
- Labels in
.textappear on their own line followed by a colon (e.g.,loop:). - Labels in
.dataappear on the same line as.word(e.g.,array: .word 9).
Example input file (fields are tab-separated in actual files):
.data
array: .word 3
.word 123
.word 4346
array2: .word 0x11111111
.text
main:
addi x2, x0, 1024
add x3, x2, x2
la x4, array2
sw x3, 0(x2)
lw x5, 0(x2)
bne x3, x5, main0x7FFFFFFC ← sp (stack top)
Stack (grows ↓)
Dynamic Data (grows ↑)
0x10008000 ← gp
0x10000000 Static Data (.data starts here)
0x00400000 ← pc Text (.text starts here)
0x00000000 Reserved
The assembler writes a binary .o file with the following layout:
<text section size> (32-bit word: number of bytes in .text)
<data section size> (32-bit word: number of bytes in .data)
<instruction 1> (32-bit words, one per instruction)
...
<instruction n>
<initial value of data 1> (32-bit words, one per .word)
...
The output file is placed in the same directory as the input .s file.
The provided skeleton (assembler.c) implements file I/O and a two-pass
framework. Your task is to write code in the four functions marked /* blank */:
| Function | What to implement |
|---|---|
make_symbol_table(FILE *input) |
1st pass: build label-to-address map; separate .data and .text segments |
make_binary_file(FILE *output) |
2nd pass: call record_text_section() and record_data_section() |
record_text_section(FILE *output) |
Translate each .text line to a 32-bit binary instruction |
record_data_section(FILE *output) |
Translate each .data word to its 32-bit binary value |
Utility functions in util.c are available for common operations (see the
file for details).
You may want to write the assembler from scratch, but the input/output format
must match exactly.
Please note that only assembler.c can be modified.
In assembler.h, set #define DEBUG 1 to enable verbose trace output that
shows the code flow. Set it back to 0 before submitting.
| File | Status | Description |
|---|---|---|
main.c |
Do not modify | Entry point and file handling |
assembler.h |
Do not modify | Struct definitions and prototypes |
util.c / util.h |
Do not modify | Utility functions |
assembler.c |
You modify this | Two-pass assembler implementation |
make # build the assembler
make test # run all test cases
make clean # remove build artifactsRun a single test manually:
./ku-rv32i-as sample_input/example0.s # produces sample_input/example0.oCompare with the reference output:
diff -Naur sample_input/example0.o sample_output/example0.oNo output from diff means your result is correct.
The make test target runs diff on all 9 examples automatically.
Your output must be bit-for-bit identical to the reference files in
sample_output/. A single wrong bit gives 0 points for that test case.
There are 9 graded test cases total (the last 3 cases are hidden on the grader). Grades are determined automatically by the grader.
Submit only assembler.c to the course submission system. Thus, you
are not allowed to change the file name or include any other files.
In addition, submit a 1-page PDF report (no cover page required) that includes:
- How you understand the structure and flow of the skeleton code
- How you implemented
make_symbol_table() - What you learned from this assignment
- (Optional) Suggestions to improve the assignment
Include your name and student ID at the top of the report. Submit your report via LMS 'Assignments' tab. Do not include code or screenshots.
| Function | Description |
|---|---|
FILE *tmpfile(void) |
Create a temporary file opened in wb+ mode |
void rewind(FILE *fp) |
Reset file stream position to the beginning |
When computing D = label_address − PC:
int32_t hi20 = (D + 0x800) >> 12; // upper 20 bits (rounded)
int32_t lo12 = D - (hi20 << 12); // lower 12 bits, fits in [-2048, 2047]If lo12 == 0, the addi instruction may be omitted and only auipc is
emitted.
We will accept your late submissions, but your score will lose up to 50%. Please do not give up the project.
Be aware of plagiarism! Although it is encouraged to discuss with others and refer to extra materials, copying other students or opening code publicly is strictly banned. The TAs will compare your source code with other team’s code. If you are caught, you will receive a penalty for plagiarism.
Last semester, we found a couple of plagiarism cases through an automated tool. Please do not try to cheat TAs. If you have any requests or questions regarding administrative issues (such as late submission due to an unfortunate accident, gradescope is not working), please send an e-mail to the TAs.