init upload
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
CC = riscv64-linux-gnu-gcc
|
||||
OBJCOPY = riscv64-linux-gnu-objcopy
|
||||
EMULATOR = ./amach
|
||||
|
||||
ASFLAGS = -march=rv32ima -mabi=ilp32 -nostdlib -static -fno-pic -fno-pie -mno-relax
|
||||
LDFLAGS = -Wl,-Ttext=0x100b0 -Wl,--build-id=none
|
||||
|
||||
all: rv32ima_tests.bin
|
||||
|
||||
rv32ima_tests.elf: rv32ima_tests.s
|
||||
$(CC) $(ASFLAGS) $(LDFLAGS) rv32ima_tests.s -o rv32ima_tests.elf
|
||||
|
||||
rv32ima_tests.bin: rv32ima_tests.elf
|
||||
$(OBJCOPY) -O binary rv32ima_tests.elf rv32ima_tests.bin
|
||||
|
||||
run: rv32ima_tests.bin
|
||||
$(EMULATOR) rv32ima_tests.bin
|
||||
|
||||
clean:
|
||||
rm -f rv32ima_tests.elf rv32ima_tests.bin
|
||||
|
||||
.PHONY: all run clean
|
||||
@@ -0,0 +1,66 @@
|
||||
# A-machine: a minimal RISC-V (RV32IMA) emulator in POSIX AWK
|
||||
|
||||
## About
|
||||
|
||||
A-machine is a simple and straightforward implementation of the RV32I specification with M (multiplication/division) and A (atomic operations) extenstions. It consists of two files:
|
||||
|
||||
- [amach.awk](amach.awk), the emulator core itself that supports .dec files,
|
||||
- and [amach](amach) shell script which is a two-liner that uses POSIX `od` to convert .bin files into .dec files on the fly and then passes them to the emulator core.
|
||||
|
||||
Additionally, A-machine is shipped with a reference test suite that assumes that you have a full RISC-V toolchain installed under the `riscv64-linux-gnu-` prefix (adjustable in the `Makefile`). Just run `make run` to build and run all tests. In case you don't have any toolchain available, a file called `rv32ima_tests.bin` is also included into the repo.
|
||||
|
||||
The project is to be considered highly experimental and not production-ready in any way.
|
||||
|
||||
## Usage
|
||||
|
||||
First, decide where your program needs to be loaded in memory. The default LVA (loading virtual address) is 0x100b0. In most AWK implementations, you need to convert this value to decimal (the default is 65712).
|
||||
|
||||
Then, most probably, you'll want to use the provided wrapper:
|
||||
```sh
|
||||
[LVA=...] ./amach path/to/program.bin
|
||||
```
|
||||
|
||||
Or you can run the following:
|
||||
```sh
|
||||
od -An -v -tu1 path/to/program.bin | POSIXLY_CORRECT=1 awk -f amach.awk [-v LVA=...]
|
||||
```
|
||||
|
||||
But in case you already have a program in the `.dec` format, you can pass it directly to AWK:
|
||||
```sh
|
||||
POSIXLY_CORRECT=1 awk -f amach.awk [-v LVA=...] -- path/to/program.dec
|
||||
```
|
||||
|
||||
## Support
|
||||
|
||||
Currently, A-machine only supports the **unprivileged** RV32IMA instruction set with a few additional `ecall` syscalls: 63 (sys_read, for fd 0 only), 64 (sys_write, for fd 1 and 2 only) and 93 (sys_exit). For completeness, `fence` instruction is also supported but yields a no-op.
|
||||
|
||||
## Plans (from higher to lower priority)
|
||||
|
||||
- Implement the C-extension (compressed instruction set).
|
||||
- Implement a simple RV32IMAC disassembler in POSIX AWK.
|
||||
- Introduce more popular Linux-compatible syscalls (that make sense to implement in AWK but don't increase the overall project complexity).
|
||||
- Test for RV32EMC compatibility (that the code built for the "embedded" variant runs here).
|
||||
- Implement a privileged instruction set minimum (Zicsr), interrupts and traps.
|
||||
- If time permits, implement a simple RV32IMAC assembler in POSIX AWK, with full pseudo-instruction support.
|
||||
|
||||
## FAQ
|
||||
|
||||
### What was the rationale of creating a platform emulator in the least suitable language for this purpose?
|
||||
|
||||
The main reason is maximum portability. While AWK is clearly tailored for text processing and, in its standard variant, doesn't even has bitwise operations and hexadecimal literals support, it still remains the single most common scripting language present on every POSIX-compatible system, including embedded ones that run [BusyBox](https://busybox.net/) on top of the kernel and might not have anything else from available programming tools at all. In such conditions, an emulation layer written in AWK is far from efficient but still better than nothing.
|
||||
|
||||
Apart from that, the A-machine serves more of an educational purpose for the author to learn about the RISC-V ISA and the details of its inner workings.
|
||||
|
||||
### Will I be able to run a Linux kernel on A-machine?
|
||||
|
||||
While A-Machine creation was partially inspired by the wonderful [mini-rv32ima project](https://github.com/cnlohr/mini-rv32ima) (although not a single line of code had been taken from there), running a (no-MMU) Linux kernel here is not currently possible due to several reasons. First, A-machine needs to support Zicsr and other privileged RV32 extensions to even theoretically be capable of running the kernel. Second, in order to run the same kernel build as mini-rv32ima does, A-machine will need to support the exact same MMIO model and non-standard CSR calls as mini-rv32ima, which already is non-trivial and will indeed require some logic copy-pasting, which the author is trying to avoid.
|
||||
|
||||
So, the answer is: no, not anytime soon.
|
||||
|
||||
### 64 bits, floats, doubles?
|
||||
|
||||
Not in this project. As already mentioned, A-machine, as well as all of its supplementary artifacts, is more of educational value. Stay tuned for the next RISC-V-related emulation projects for something more practical, fast and powerful.
|
||||
|
||||
## Credits
|
||||
|
||||
Created by Luxferre in 2026, released into the public domain with no warranties.
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
# A-Machine .bin launcher wrapper
|
||||
[ -z "$LVA" ] && LVA=65712 # 0x100b0
|
||||
od -An -v -tu1 "$1" | POSIXLY_CORRECT=1 awk -f amach.awk -v LVA="$LVA"
|
||||
@@ -0,0 +1,357 @@
|
||||
#!/usr/bin/env awk -f
|
||||
# A-Machine: an experimental RISC-V (RV32IMA) emulator in POSIX AWK
|
||||
# with a very small subset of supported ECALLs
|
||||
# Accepts a headerless binary previously converted to .dec format
|
||||
# e.g. with POSIX od: od -An -v -tu1 program.bin > program.dec
|
||||
# Usage: awk -f amach.awk [-v LVA=...] -- prog.dec
|
||||
# Or run a .bin directly with:
|
||||
# od -An -v -tu1 program.bin | awk -f amach.awk [-v LVA=...]
|
||||
# Created by Luxferre in 2026, released into the public domain
|
||||
|
||||
# fatal error reporting function
|
||||
function trapout(msg) {
|
||||
cmd = "cat 1>&2"
|
||||
printf("Fatal: %s\n", msg) | cmd
|
||||
close(cmd)
|
||||
exit(1)
|
||||
}
|
||||
|
||||
# helper functions
|
||||
|
||||
function setreg(idx, val) {
|
||||
if(idx > 0) {
|
||||
val = int(val) % 4294967296
|
||||
if(val < 0) val += 4294967296
|
||||
REG[idx] = (val >= 2147483648) ? (val - 4294967296) : val
|
||||
}
|
||||
}
|
||||
|
||||
function getreg(idx) {return (idx == 0) ? 0 : int(REG[idx])}
|
||||
|
||||
function imm_sign_ex(val) {val = int(val); return (val >= 2048) ? (val - 4096) : val}
|
||||
function imm_sign_ex_b(val) {val = int(val); return (val >= 4096) ? (val - 8192) : val}
|
||||
|
||||
function floor(x, i) {
|
||||
i = int(x)
|
||||
return (x >= 0 || x == i) ? i : (i - 1)
|
||||
}
|
||||
|
||||
function ord(c, b) {
|
||||
if(!TGL_ORD["#"]) for(b=0;b<256;b++) TGL_ORD[sprintf("%c", b)] = b
|
||||
return int(TGL_ORD[c])
|
||||
}
|
||||
|
||||
function bw_and(a, b, v, r) {
|
||||
v = 1; r = 0
|
||||
a = int(a)
|
||||
b = int(b)
|
||||
if(a < 0) a += 4294967296
|
||||
if(b < 0) b += 4294967296
|
||||
while(a > 0 || b > 0) {
|
||||
if((a%2) == 1 && (b%2) == 1) r += v
|
||||
a = int(a/2)
|
||||
b = int(b/2)
|
||||
v *= 2
|
||||
}
|
||||
return int(r)
|
||||
}
|
||||
|
||||
function bw_or(a, b, v, r) {
|
||||
v = 1; r = 0
|
||||
a = int(a)
|
||||
b = int(b)
|
||||
if(a < 0) a += 4294967296
|
||||
if(b < 0) b += 4294967296
|
||||
while(a > 0 || b > 0) {
|
||||
if((a%2) == 1 || (b%2) == 1) r += v
|
||||
a = int(a/2)
|
||||
b = int(b/2)
|
||||
v *= 2
|
||||
}
|
||||
return int(r)
|
||||
}
|
||||
|
||||
function bw_xor(a, b, v, r) {
|
||||
v = 1; r = 0
|
||||
a = int(a)
|
||||
b = int(b)
|
||||
if(a < 0) a += 4294967296
|
||||
if(b < 0) b += 4294967296
|
||||
while(a > 0 || b > 0) {
|
||||
if((a%2) != (b%2)) r += v
|
||||
a = int(a/2)
|
||||
b = int(b/2)
|
||||
v *= 2
|
||||
}
|
||||
return int(r)
|
||||
}
|
||||
|
||||
function read_word(addr, val) {
|
||||
addr = int(addr) % 4294967296
|
||||
if(addr < 0) addr += 4294967296
|
||||
val = MEM[addr] + MEM[(addr+1)%4294967296]*256 + MEM[(addr+2)%4294967296]*65536 + MEM[(addr+3)%4294967296]*16777216
|
||||
return (val >= 2147483648) ? (val - 4294967296) : val
|
||||
}
|
||||
|
||||
function write_word(addr, val) {
|
||||
addr = int(addr) % 4294967296
|
||||
if(addr < 0) addr += 4294967296
|
||||
val = int(val) % 4294967296
|
||||
if(val < 0) val += 4294967296
|
||||
MEM[addr] = val % 256
|
||||
MEM[(addr+1)%4294967296] = int(val/256) % 256
|
||||
MEM[(addr+2)%4294967296] = int(val/65536) % 256
|
||||
MEM[(addr+3)%4294967296] = int(val/16777216) % 256
|
||||
}
|
||||
|
||||
# syscall emulation
|
||||
|
||||
function handle_ecall(callnum, a0, a1, a2, a3, a4, a5, i, l, buf, addr) {
|
||||
if(callnum == 63) { # sys_read
|
||||
if(a0 == 0) {
|
||||
getline buf < "/dev/tty"
|
||||
l = length(buf)
|
||||
if(l > a2) l = a2
|
||||
for(i=0;i<l;i++) {
|
||||
addr = (a1 + i) % 4294967296
|
||||
MEM[addr] = ord(substr(buf, i+1, 1))
|
||||
}
|
||||
setreg(10, i)
|
||||
}
|
||||
} else if(callnum == 64) { # sys_write
|
||||
if(a0 == 1 || a0 == 2) {
|
||||
for(i=0;i<a2;i++) {
|
||||
addr = (a1 + i) % 4294967296
|
||||
buf = int(MEM[addr])
|
||||
printf("%c", buf)
|
||||
}
|
||||
setreg(10, i)
|
||||
}
|
||||
} else if(callnum == 93) exit(a0) # sys_exit
|
||||
else trapout(sprintf("Unimplemented environment call %d at 0x%X", callnum, pc-4))
|
||||
}
|
||||
|
||||
# instruction type executors
|
||||
|
||||
function amach_reg_arith(f3, f7, rd, rs1, rs2, r1, r2, ur1, ur2, shamt) {
|
||||
r1 = getreg(rs1)
|
||||
r2 = getreg(rs2)
|
||||
ur1 = (r1 >= 0) ? r1 : (r1 + 4294967296)
|
||||
ur2 = (r2 >= 0) ? r2 : (r2 + 4294967296)
|
||||
shamt = ur2 % 32
|
||||
|
||||
if(f3 == 0 && f7 == 0) setreg(rd, r1 + r2)
|
||||
else if(f3 == 0 && f7 == 32) setreg(rd, r1 - r2)
|
||||
else if(f3 == 4 && f7 == 0) setreg(rd, bw_xor(r1, r2))
|
||||
else if(f3 == 6 && f7 == 0) setreg(rd, bw_or(r1, r2))
|
||||
else if(f3 == 7 && f7 == 0) setreg(rd, bw_and(r1, r2))
|
||||
else if(f3 == 1 && f7 == 0) setreg(rd, (r1 * (2^shamt)) % 4294967296)
|
||||
else if(f3 == 5 && f7 == 0) setreg(rd, int(ur1 / (2^shamt)))
|
||||
else if(f3 == 5 && f7 == 32) setreg(rd, floor(r1 / (2^shamt)))
|
||||
else if(f3 == 2 && f7 == 0) setreg(rd, (r1 < r2) ? 1 : 0)
|
||||
else if(f3 == 3 && f7 == 0) setreg(rd, (ur1 < ur2) ? 1 : 0)
|
||||
else if(f3 == 0 && f7 == 1) setreg(rd, (r1 * r2) % 4294967296)
|
||||
else if(f3 == 1 && f7 == 1) setreg(rd, floor((r1 * r2) / 4294967296))
|
||||
else if(f3 == 2 && f7 == 1) setreg(rd, floor((r1 * ur2) / 4294967296))
|
||||
else if(f3 == 3 && f7 == 1) setreg(rd, floor((ur1 * ur2) / 4294967296))
|
||||
else if(f3 == 4 && f7 == 1) setreg(rd, (r2 == 0) ? -1 : int(r1 / r2))
|
||||
else if(f3 == 5 && f7 == 1) setreg(rd, (r2 == 0) ? 4294967295 : int(ur1 / ur2))
|
||||
else if(f3 == 6 && f7 == 1) setreg(rd, (r2 == 0) ? r1 : (r1 % r2))
|
||||
else if(f3 == 7 && f7 == 1) setreg(rd, (r2 == 0) ? r1 : (ur1 % ur2))
|
||||
else trapout(sprintf("Illegal instruction at 0x%X", pc-4))
|
||||
}
|
||||
|
||||
function amach_store(f3, rs1, rs2, immval, r1, r2, base) {
|
||||
r1 = getreg(rs1)
|
||||
r2 = getreg(rs2)
|
||||
if(r2 < 0) r2 += 4294967296
|
||||
base = (r1 + immval) % 4294967296
|
||||
if(base < 0) base += 4294967296
|
||||
if(f3 == 0) MEM[base] = r2 % 256
|
||||
else if(f3 == 1) {
|
||||
MEM[base] = r2 % 256
|
||||
MEM[(base + 1)%4294967296] = int(r2/256) % 256
|
||||
}
|
||||
else if(f3 == 2) {
|
||||
MEM[base] = r2 % 256
|
||||
MEM[(base + 1)%4294967296] = int(r2/256) % 256
|
||||
MEM[(base + 2)%4294967296] = int(r2/65536) % 256
|
||||
MEM[(base + 3)%4294967296] = int(r2/16777216) % 256
|
||||
}
|
||||
else trapout(sprintf("Illegal instruction at 0x%X", pc-4))
|
||||
}
|
||||
|
||||
function amach_branch(f3, rs1, rs2, immval, r1, r2, ur1, ur2, taken) {
|
||||
r1 = getreg(rs1)
|
||||
r2 = getreg(rs2)
|
||||
ur1 = (r1 >= 0) ? r1 : (r1 + 4294967296)
|
||||
ur2 = (r2 >= 0) ? r2 : (r2 + 4294967296)
|
||||
taken = 0
|
||||
if(f3 == 0) taken = (r1 == r2)
|
||||
else if(f3 == 1) taken = (r1 != r2)
|
||||
else if(f3 == 4) taken = (r1 < r2)
|
||||
else if(f3 == 5) taken = (r1 >= r2)
|
||||
else if(f3 == 6) taken = (ur1 < ur2)
|
||||
else if(f3 == 7) taken = (ur1 >= ur2)
|
||||
else trapout(sprintf("Illegal instruction at 0x%X", pc-4))
|
||||
|
||||
if(taken) pc += immval
|
||||
else pc += 4
|
||||
}
|
||||
|
||||
function amach_imm(opcode, f3, rd, rs1, immval, r1, ur1, base, shamt, val) {
|
||||
r1 = getreg(rs1)
|
||||
ur1 = (r1 >= 0) ? r1 : (r1 + 4294967296)
|
||||
if(opcode == 3) { # load
|
||||
base = (r1 + immval) % 4294967296
|
||||
if(base < 0) base += 4294967296
|
||||
if(f3 == 0) { # lb
|
||||
val = MEM[base]
|
||||
setreg(rd, (val >= 128) ? (val - 256) : val)
|
||||
}
|
||||
else if(f3 == 1) { # lh
|
||||
val = MEM[base] + MEM[(base+1)%4294967296]*256
|
||||
setreg(rd, (val >= 32768) ? (val - 65536) : val)
|
||||
}
|
||||
else if(f3 == 2) {
|
||||
val = MEM[base] + MEM[(base+1)%4294967296]*256 + MEM[(base+2)%4294967296]*65536 + MEM[(base+3)%4294967296]*16777216
|
||||
setreg(rd, val)
|
||||
}
|
||||
else if(f3 == 4) setreg(rd, MEM[base]) # lbu
|
||||
else if(f3 == 5) setreg(rd, MEM[base] + MEM[(base+1)%4294967296]*256) # lhu
|
||||
else trapout(sprintf("Illegal instruction at 0x%X", pc-4))
|
||||
} else if(opcode == 19) { # 0x13, immediate arithmetic
|
||||
shamt = bw_and(immval, 31)
|
||||
if(f3 == 0) setreg(rd, r1 + immval)
|
||||
else if(f3 == 4) setreg(rd, bw_xor(r1, immval))
|
||||
else if(f3 == 6) setreg(rd, bw_or(r1, immval))
|
||||
else if(f3 == 7) setreg(rd, bw_and(r1, immval))
|
||||
else if(f3 == 1) setreg(rd, (r1 * (2^shamt)) % 4294967296) # slli
|
||||
else if(f3 == 5 && immval < 1024) setreg(rd, int(ur1 / (2^shamt))) # srli
|
||||
else if(f3 == 5 && immval >= 1024) setreg(rd, floor(r1 / (2^shamt))) # srai
|
||||
else if(f3 == 2) setreg(rd, (r1 < immval) ? 1 : 0)
|
||||
else if(f3 == 3) setreg(rd, (ur1 < immval) ? 1 : 0)
|
||||
else trapout(sprintf("Illegal instruction at 0x%X", pc-4))
|
||||
} else if(opcode == 103) { # 0x67, JALR
|
||||
setreg(rd, pc)
|
||||
pc = r1 + immval
|
||||
} else if(opcode == 115) { # 0x73, system call / csr
|
||||
if(immval == 0) handle_ecall(getreg(17), getreg(10), getreg(11), getreg(12), getreg(13), getreg(14), getreg(15))
|
||||
else trapout(sprintf("Unimplemented external system call at 0x%X", pc-4))
|
||||
} else trapout(sprintf("Illegal instruction at 0x%X", pc-4))
|
||||
}
|
||||
|
||||
# A-extension
|
||||
function amach_atomic(f3, f5, aq, rl, rd, rs1, rs2, r1, r2, val, uval, ur2) {
|
||||
r1 = getreg(rs1)
|
||||
r2 = getreg(rs2)
|
||||
ur2 = (r2 >= 0) ? r2 : (r2 + 4294967296)
|
||||
if(f3 == 2) {
|
||||
val = read_word(r1)
|
||||
uval = (val >= 0) ? val : (val + 4294967296)
|
||||
if(f5 == 0) { # amoadd.w
|
||||
setreg(rd, val)
|
||||
write_word(r1, val + r2)
|
||||
} else if(f5 == 1) { # amoswap.w
|
||||
setreg(rd, val)
|
||||
write_word(r1, r2)
|
||||
} else if(f5 == 2) { # lr.w
|
||||
setreg(rd, val)
|
||||
RES_ADDR = r1
|
||||
} else if(f5 == 3) { # sc.w
|
||||
if(RES_ADDR == r1) {
|
||||
write_word(r1, r2)
|
||||
setreg(rd, 0)
|
||||
RES_ADDR = -1
|
||||
} else {
|
||||
setreg(rd, 1)
|
||||
}
|
||||
} else if(f5 == 4) { # amoxor.w
|
||||
setreg(rd, val)
|
||||
write_word(r1, bw_xor(val, r2))
|
||||
} else if(f5 == 8) { # amoor.w
|
||||
setreg(rd, val)
|
||||
write_word(r1, bw_or(val, r2))
|
||||
} else if(f5 == 12) { # amoand.w
|
||||
setreg(rd, val)
|
||||
write_word(r1, bw_and(val, r2))
|
||||
} else if(f5 == 16) { # amomin.w
|
||||
setreg(rd, val)
|
||||
write_word(r1, val <= r2 ? val : r2)
|
||||
} else if(f5 == 20) { # amomax.w
|
||||
setreg(rd, val)
|
||||
write_word(r1, val >= r2 ? val : r2)
|
||||
} else if(f5 == 24) { # amominu.w
|
||||
setreg(rd, val)
|
||||
write_word(r1, uval <= ur2 ? val : r2)
|
||||
} else if(f5 == 28) { # amomaxu.w
|
||||
setreg(rd, val)
|
||||
write_word(r1, uval >= ur2 ? val : r2)
|
||||
} else trapout(sprintf("Unimplemented atomic instruction at 0x%X", pc-4))
|
||||
} else trapout(sprintf("Illegal atomic instruction at 0x%X", pc-4))
|
||||
}
|
||||
|
||||
# main instruction decoding and execution routine
|
||||
function amach_exec(instr, opcode, rd, rs1, rs2, imm, funct3, funct7) {
|
||||
opcode = instr % 128
|
||||
instr = int(instr / 128)
|
||||
rd = instr % 32
|
||||
imm = int(instr / 32)
|
||||
funct3 = imm % 8
|
||||
rs1 = int(imm / 8) % 32
|
||||
rs2 = int(imm / 256) % 32
|
||||
funct7 = int(imm / 8192)
|
||||
|
||||
if(opcode == 51) { # 0x33, register arithmetic, type R
|
||||
amach_reg_arith(funct3, funct7, rd, rs1, rs2)
|
||||
} else if(opcode == 47) { # 0x2f, atomic operations, type R
|
||||
amach_atomic(funct3, int(funct7 / 4), int(funct7 / 2) % 2, funct7 % 2, rd, rs1, rs2)
|
||||
} else if(opcode == 3 || opcode == 19 || opcode == 103 || opcode == 115) { # type I
|
||||
amach_imm(opcode, funct3, rd, rs1, imm_sign_ex(rs2 + funct7 * 32))
|
||||
} else if(opcode == 35) { # 0x23, store, type S
|
||||
amach_store(funct3, rs1, rs2, imm_sign_ex(rd + funct7 * 32))
|
||||
} else if(opcode == 99) { # 0x63, branch, type B
|
||||
imm = rd + (funct7 % 64) * 32 + (rd % 2) * 2047 + int(funct7 / 64) * 4096
|
||||
pc -= 4
|
||||
amach_branch(funct3, rs1, rs2, imm_sign_ex_b(imm))
|
||||
} else if(opcode == 111) { # 0x6F, JAL, type J
|
||||
setreg(rd, pc)
|
||||
imm_10_1 = int(rs2 / 2) + (funct7 % 64) * 16
|
||||
imm_19_12 = funct3 + rs1 * 8
|
||||
imm = imm_10_1 * 2 + (rs2 % 2) * 2048 + imm_19_12 * 4096 + int(funct7 / 64) * 1048576
|
||||
if(imm >= 1048576) imm -= 2097152
|
||||
pc += imm - 4
|
||||
} else if(opcode == 55) { # 0x37, LUI, type U
|
||||
setreg(rd, imm * 4096)
|
||||
} else if(opcode == 23) { # 0x17, AUIPC, type U
|
||||
setreg(rd, imm * 4096 + pc - 4)
|
||||
} else if(opcode == 15) { # 0x0F, FENCE, type I / no-op
|
||||
# no-op
|
||||
} else trapout(sprintf("Illegal instruction at 0x%X", pc-4))
|
||||
}
|
||||
|
||||
# Initialization section
|
||||
BEGIN {
|
||||
split("", MEM) # init the memory array
|
||||
split("", REG) # init the (integer) registers
|
||||
REG[0] = 0 # zero register
|
||||
REG[2] = 2^31 # stack pointer top
|
||||
RES_ADDR = -1 # reserved address for atomic ops
|
||||
LVA = int(LVA)
|
||||
pc = LVA
|
||||
}
|
||||
|
||||
# Decimal memory collection section
|
||||
/^[[:space:]]*[[:digit:][:space:]]+/ {for(i=1; i<=NF; i++) MEM[pc++] = int($i)}
|
||||
|
||||
# Execution section
|
||||
END {
|
||||
memsize = pc - LVA
|
||||
pc = LVA
|
||||
while(pc > -1) {
|
||||
instr = MEM[pc++] + 256 * MEM[pc++]
|
||||
if((instr % 4) == 3) # full instruction
|
||||
instr += 65536 * MEM[pc++] + 16777216 * MEM[pc++]
|
||||
amach_exec(instr) # decode and execute
|
||||
}
|
||||
}
|
||||
Executable
BIN
Binary file not shown.
+741
@@ -0,0 +1,741 @@
|
||||
.section .text
|
||||
.global _start
|
||||
|
||||
.macro INC_TESTS
|
||||
la s10, test_count
|
||||
lw s11, 0(s10)
|
||||
addi s11, s11, 1
|
||||
sw s11, 0(s10)
|
||||
.endm
|
||||
|
||||
.macro ASSERT_EQ reg, expected, msg
|
||||
li s10, \expected
|
||||
mv s11, \reg
|
||||
beq s10, s11, .L_success_\@
|
||||
# Failure
|
||||
la a0, .L_msg_\@
|
||||
mv a1, s11
|
||||
li a2, \expected
|
||||
jal report_failure
|
||||
j .L_end_\@
|
||||
.L_success_\@:
|
||||
# Success
|
||||
la s10, success_count
|
||||
lw s11, 0(s10)
|
||||
addi s11, s11, 1
|
||||
sw s11, 0(s10)
|
||||
.L_end_\@:
|
||||
.section .rodata
|
||||
.L_msg_\@: .string "\msg"
|
||||
.section .text
|
||||
.endm
|
||||
|
||||
.macro ASSERT_EQ_REG reg, expected_reg, msg
|
||||
mv s10, \expected_reg
|
||||
mv s11, \reg
|
||||
beq s10, s11, .L_success_reg_\@
|
||||
# Failure
|
||||
la a0, .L_msg_reg_\@
|
||||
mv a1, s11
|
||||
mv a2, s10
|
||||
jal report_failure
|
||||
j .L_end_reg_\@
|
||||
.L_success_reg_\@:
|
||||
# Success
|
||||
la s10, success_count
|
||||
lw s11, 0(s10)
|
||||
addi s11, s11, 1
|
||||
sw s11, 0(s10)
|
||||
.L_end_reg_\@:
|
||||
.section .rodata
|
||||
.L_msg_reg_\@: .string "\msg"
|
||||
.section .text
|
||||
.endm
|
||||
|
||||
.macro TEST_VAL_RI inst, val1, imm, expected, name
|
||||
INC_TESTS
|
||||
li t1, \val1
|
||||
\inst t0, t1, \imm
|
||||
ASSERT_EQ t0, \expected, \name
|
||||
.endm
|
||||
|
||||
.macro TEST_VAL_RR inst, val1, val2, expected, name
|
||||
INC_TESTS
|
||||
li t1, \val1
|
||||
li t2, \val2
|
||||
\inst t0, t1, t2
|
||||
ASSERT_EQ t0, \expected, \name
|
||||
.endm
|
||||
|
||||
_start:
|
||||
# ==========================================
|
||||
# RV32I Immediate Arithmetic
|
||||
# ==========================================
|
||||
TEST_VAL_RI addi, 10, 20, 30, addi_pos
|
||||
TEST_VAL_RI addi, 10, -20, -10, addi_neg
|
||||
TEST_VAL_RI addi, 0x7fffffff, 1, 0x80000000, addi_overflow
|
||||
|
||||
TEST_VAL_RI slti, 10, 20, 1, slti_lt
|
||||
TEST_VAL_RI slti, 20, 10, 0, slti_gt
|
||||
TEST_VAL_RI slti, -10, 10, 1, slti_neg_lt
|
||||
|
||||
TEST_VAL_RI sltiu, 10, 20, 1, sltiu_lt
|
||||
TEST_VAL_RI sltiu, -10, 10, 0, sltiu_neg_gt
|
||||
|
||||
TEST_VAL_RI xori, 0xf0f0f0f0, 0x0ff, 0xf0f0f00f, xori_val
|
||||
TEST_VAL_RI ori, 0xf0f0f0f0, 0x0ff, 0xf0f0f0ff, ori_val
|
||||
TEST_VAL_RI andi, 0xf0f0f0f0, 0x0ff, 0x000000f0, andi_val
|
||||
|
||||
TEST_VAL_RI slli, 1, 5, 32, slli_val
|
||||
TEST_VAL_RI slli, 0x80000000, 1, 0, slli_overflow
|
||||
|
||||
TEST_VAL_RI srli, 32, 5, 1, srli_val
|
||||
TEST_VAL_RI srli, 0x80000000, 1, 0x40000000, srli_sign
|
||||
|
||||
TEST_VAL_RI srai, 32, 5, 1, srai_pos
|
||||
TEST_VAL_RI srai, 0x80000000, 1, 0xc0000000, srai_neg
|
||||
|
||||
# ==========================================
|
||||
# RV32I Register-Register Arithmetic
|
||||
# ==========================================
|
||||
TEST_VAL_RR add, 10, 20, 30, add_pos
|
||||
TEST_VAL_RR add, 0x7fffffff, 1, 0x80000000, add_overflow
|
||||
|
||||
TEST_VAL_RR sub, 20, 10, 10, sub_pos
|
||||
TEST_VAL_RR sub, 10, 20, -10, sub_neg
|
||||
|
||||
TEST_VAL_RR sll, 1, 5, 32, sll_val
|
||||
TEST_VAL_RR sll, 1, 37, 32, sll_wrap
|
||||
|
||||
TEST_VAL_RR slt, 10, 20, 1, slt_lt
|
||||
TEST_VAL_RR slt, -10, 10, 1, slt_neg
|
||||
|
||||
TEST_VAL_RR sltu, 10, 20, 1, sltu_lt
|
||||
TEST_VAL_RR sltu, -10, 10, 0, sltu_neg
|
||||
|
||||
TEST_VAL_RR xor, 0xf0f0f0f0, 0x0f0f0f0f, 0xffffffff, xor_val
|
||||
|
||||
TEST_VAL_RR srl, 32, 5, 1, srl_val
|
||||
TEST_VAL_RR srl, 32, 37, 1, srl_wrap
|
||||
|
||||
TEST_VAL_RR sra, 0x80000000, 1, 0xc0000000, sra_neg
|
||||
TEST_VAL_RR sra, 0x80000000, 33, 0xc0000000, sra_wrap
|
||||
|
||||
TEST_VAL_RR or, 0xf0f0f0f0, 0x0f0f0f0f, 0xffffffff, or_val
|
||||
TEST_VAL_RR and, 0xf0f0f0f0, 0x0f0f0f0f, 0, and_val
|
||||
|
||||
# ==========================================
|
||||
# RV32I LUI and AUIPC
|
||||
# ==========================================
|
||||
INC_TESTS
|
||||
lui t0, 0x12345
|
||||
ASSERT_EQ t0, 0x12345000, lui_val
|
||||
|
||||
INC_TESTS
|
||||
1: auipc t0, 0x10
|
||||
la t1, 1b
|
||||
sub t0, t0, t1
|
||||
ASSERT_EQ t0, 0x10000, auipc_val
|
||||
|
||||
# ==========================================
|
||||
# RV32I Jumps
|
||||
# ==========================================
|
||||
INC_TESTS
|
||||
jal t0, 2f
|
||||
li t1, 0
|
||||
j 3f
|
||||
2:
|
||||
li t1, 1
|
||||
3:
|
||||
ASSERT_EQ t1, 1, jal_branch
|
||||
INC_TESTS
|
||||
la t2, 2b - 8
|
||||
ASSERT_EQ_REG t0, t2, jal_link
|
||||
|
||||
INC_TESTS
|
||||
la t1, 4f
|
||||
jalr t0, t1, 0
|
||||
li t2, 0
|
||||
j 5f
|
||||
4:
|
||||
li t2, 1
|
||||
5:
|
||||
ASSERT_EQ t2, 1, jalr_branch
|
||||
INC_TESTS
|
||||
la t3, 4b - 8
|
||||
ASSERT_EQ_REG t0, t3, jalr_link
|
||||
|
||||
INC_TESTS
|
||||
la t1, 6f - 4
|
||||
jalr t0, t1, 4
|
||||
li t2, 0
|
||||
j 7f
|
||||
6:
|
||||
li t2, 1
|
||||
7:
|
||||
ASSERT_EQ t2, 1, jalr_offset
|
||||
|
||||
# ==========================================
|
||||
# RV32I Branches
|
||||
# ==========================================
|
||||
# BEQ
|
||||
INC_TESTS
|
||||
li t1, 10
|
||||
li t2, 10
|
||||
beq t1, t2, 1f
|
||||
li t0, 0
|
||||
j 2f
|
||||
1:
|
||||
li t0, 1
|
||||
2:
|
||||
ASSERT_EQ t0, 1, beq_taken
|
||||
|
||||
INC_TESTS
|
||||
li t1, 10
|
||||
li t2, 20
|
||||
beq t1, t2, 1f
|
||||
li t0, 1
|
||||
j 2f
|
||||
1:
|
||||
li t0, 0
|
||||
2:
|
||||
ASSERT_EQ t0, 1, beq_not_taken
|
||||
|
||||
# BNE
|
||||
INC_TESTS
|
||||
li t1, 10
|
||||
li t2, 20
|
||||
bne t1, t2, 1f
|
||||
li t0, 0
|
||||
j 2f
|
||||
1:
|
||||
li t0, 1
|
||||
2:
|
||||
ASSERT_EQ t0, 1, bne_taken
|
||||
|
||||
INC_TESTS
|
||||
li t1, 10
|
||||
li t2, 10
|
||||
bne t1, t2, 1f
|
||||
li t0, 1
|
||||
j 2f
|
||||
1:
|
||||
li t0, 0
|
||||
2:
|
||||
ASSERT_EQ t0, 1, bne_not_taken
|
||||
|
||||
# BLT
|
||||
INC_TESTS
|
||||
li t1, -10
|
||||
li t2, 10
|
||||
blt t1, t2, 1f
|
||||
li t0, 0
|
||||
j 2f
|
||||
1:
|
||||
li t0, 1
|
||||
2:
|
||||
ASSERT_EQ t0, 1, blt_taken
|
||||
|
||||
INC_TESTS
|
||||
li t1, 10
|
||||
li t2, -10
|
||||
blt t1, t2, 1f
|
||||
li t0, 1
|
||||
j 2f
|
||||
1:
|
||||
li t0, 0
|
||||
2:
|
||||
ASSERT_EQ t0, 1, blt_not_taken
|
||||
|
||||
# BGE
|
||||
INC_TESTS
|
||||
li t1, 10
|
||||
li t2, -10
|
||||
bge t1, t2, 1f
|
||||
li t0, 0
|
||||
j 2f
|
||||
1:
|
||||
li t0, 1
|
||||
2:
|
||||
ASSERT_EQ t0, 1, bge_taken
|
||||
|
||||
INC_TESTS
|
||||
li t1, -10
|
||||
li t2, 10
|
||||
bge t1, t2, 1f
|
||||
li t0, 1
|
||||
j 2f
|
||||
1:
|
||||
li t0, 0
|
||||
2:
|
||||
ASSERT_EQ t0, 1, bge_not_taken
|
||||
|
||||
# BLTU
|
||||
INC_TESTS
|
||||
li t1, 10
|
||||
li t2, -10
|
||||
bltu t1, t2, 1f
|
||||
li t0, 0
|
||||
j 2f
|
||||
1:
|
||||
li t0, 1
|
||||
2:
|
||||
ASSERT_EQ t0, 1, bltu_taken
|
||||
|
||||
INC_TESTS
|
||||
li t1, -10
|
||||
li t2, 10
|
||||
bltu t1, t2, 1f
|
||||
li t0, 1
|
||||
j 2f
|
||||
1:
|
||||
li t0, 0
|
||||
2:
|
||||
ASSERT_EQ t0, 1, bltu_not_taken
|
||||
|
||||
# BGEU
|
||||
INC_TESTS
|
||||
li t1, -10
|
||||
li t2, 10
|
||||
bgeu t1, t2, 1f
|
||||
li t0, 0
|
||||
j 2f
|
||||
1:
|
||||
li t0, 1
|
||||
2:
|
||||
ASSERT_EQ t0, 1, bgeu_taken
|
||||
|
||||
INC_TESTS
|
||||
li t1, 10
|
||||
li t2, -10
|
||||
bgeu t1, t2, 1f
|
||||
li t0, 1
|
||||
j 2f
|
||||
1:
|
||||
li t0, 0
|
||||
2:
|
||||
ASSERT_EQ t0, 1, bgeu_not_taken
|
||||
|
||||
# ==========================================
|
||||
# RV32I Loads and Stores
|
||||
# ==========================================
|
||||
# Store word and load word/half/byte
|
||||
la t1, data_buffer
|
||||
li t2, 0x12345678
|
||||
sw t2, 0(t1)
|
||||
|
||||
INC_TESTS
|
||||
lw t0, 0(t1)
|
||||
ASSERT_EQ t0, 0x12345678, load_word
|
||||
|
||||
INC_TESTS
|
||||
lh t0, 0(t1)
|
||||
ASSERT_EQ t0, 0x5678, load_half
|
||||
|
||||
INC_TESTS
|
||||
lhu t0, 0(t1)
|
||||
ASSERT_EQ t0, 0x5678, load_half_u
|
||||
|
||||
INC_TESTS
|
||||
lb t0, 0(t1)
|
||||
ASSERT_EQ t0, 0x78, load_byte
|
||||
|
||||
INC_TESTS
|
||||
lbu t0, 0(t1)
|
||||
ASSERT_EQ t0, 0x78, load_byte_u
|
||||
|
||||
# Store half and byte with sign extension verification
|
||||
li t2, 0x8000
|
||||
sh t2, 0(t1)
|
||||
INC_TESTS
|
||||
lh t0, 0(t1)
|
||||
ASSERT_EQ t0, -32768, load_half_neg
|
||||
|
||||
INC_TESTS
|
||||
lhu t0, 0(t1)
|
||||
ASSERT_EQ t0, 32768, load_half_neg_u
|
||||
|
||||
li t2, 0x80
|
||||
sb t2, 0(t1)
|
||||
INC_TESTS
|
||||
lb t0, 0(t1)
|
||||
ASSERT_EQ t0, -128, load_byte_neg
|
||||
|
||||
INC_TESTS
|
||||
lbu t0, 0(t1)
|
||||
ASSERT_EQ t0, 128, load_byte_neg_u
|
||||
|
||||
# ==========================================
|
||||
# RV32M Extensions
|
||||
# ==========================================
|
||||
TEST_VAL_RR mul, 10, 20, 200, mul_pos
|
||||
TEST_VAL_RR mul, -10, 20, -200, mul_neg
|
||||
TEST_VAL_RR mul, 0x10000, 0x10000, 0, mul_overflow
|
||||
|
||||
TEST_VAL_RR mulh, 0x10000, 0x10000, 1, mulh_pos
|
||||
TEST_VAL_RR mulh, -10, 20, -1, mulh_neg
|
||||
|
||||
TEST_VAL_RR mulhsu, -10, 20, -1, mulhsu_neg
|
||||
TEST_VAL_RR mulhu, 0x80000000, 2, 1, mulhu_val
|
||||
|
||||
TEST_VAL_RR div, 20, 6, 3, div_pos
|
||||
TEST_VAL_RR div, -20, 6, -3, div_neg
|
||||
TEST_VAL_RR div, 20, 0, -1, div_by_zero
|
||||
TEST_VAL_RR div, -2147483648, -1, -2147483648, div_overflow
|
||||
|
||||
TEST_VAL_RR divu, 20, 6, 3, divu_pos
|
||||
TEST_VAL_RR divu, -20, 6, 715827879, divu_neg
|
||||
TEST_VAL_RR divu, 20, 0, 4294967295, divu_by_zero
|
||||
|
||||
TEST_VAL_RR rem, 20, 6, 2, rem_pos
|
||||
TEST_VAL_RR rem, -20, 6, -2, rem_neg
|
||||
TEST_VAL_RR rem, 20, 0, 20, rem_by_zero
|
||||
TEST_VAL_RR rem, -2147483648, -1, 0, rem_overflow
|
||||
|
||||
TEST_VAL_RR remu, 20, 6, 2, remu_pos
|
||||
TEST_VAL_RR remu, -20, 6, 2, remu_neg
|
||||
TEST_VAL_RR remu, 20, 0, 20, remu_by_zero
|
||||
|
||||
# ==========================================
|
||||
# RV32A Extensions
|
||||
# ==========================================
|
||||
# Load-Reserved / Store-Conditional
|
||||
la t1, atomic_var
|
||||
li t2, 42
|
||||
lr.w t0, (t1)
|
||||
sc.w t3, t2, (t1)
|
||||
INC_TESTS
|
||||
ASSERT_EQ t3, 0, sc_success
|
||||
INC_TESTS
|
||||
lw t4, 0(t1)
|
||||
ASSERT_EQ t4, 42, sc_val
|
||||
|
||||
# SC without LR (should fail)
|
||||
li t2, 99
|
||||
sc.w t3, t2, (t1)
|
||||
INC_TESTS
|
||||
ASSERT_EQ t3, 1, sc_fail_no_lr
|
||||
|
||||
# AMOSWAP.W
|
||||
li t2, 10
|
||||
sw t2, 0(t1)
|
||||
li t3, 20
|
||||
amoswap.w t0, t3, (t1)
|
||||
INC_TESTS
|
||||
ASSERT_EQ t0, 10, amoswap_old
|
||||
INC_TESTS
|
||||
lw t4, 0(t1)
|
||||
ASSERT_EQ t4, 20, amoswap_new
|
||||
|
||||
# AMOADD.W
|
||||
li t2, 10
|
||||
sw t2, 0(t1)
|
||||
li t3, 20
|
||||
amoadd.w t0, t3, (t1)
|
||||
INC_TESTS
|
||||
ASSERT_EQ t0, 10, amoadd_old
|
||||
INC_TESTS
|
||||
lw t4, 0(t1)
|
||||
ASSERT_EQ t4, 30, amoadd_new
|
||||
|
||||
# AMOXOR.W
|
||||
li t2, 0x12345678
|
||||
sw t2, 0(t1)
|
||||
li t3, 0x0f0f0f0f
|
||||
amoxor.w t0, t3, (t1)
|
||||
INC_TESTS
|
||||
ASSERT_EQ t0, 0x12345678, amoxor_old
|
||||
INC_TESTS
|
||||
lw t4, 0(t1)
|
||||
ASSERT_EQ t4, 0x1d3b5977, amoxor_new
|
||||
|
||||
# AMOAND.W
|
||||
li t2, 0x12345678
|
||||
sw t2, 0(t1)
|
||||
li t3, 0x0f0f0f0f
|
||||
amoand.w t0, t3, (t1)
|
||||
INC_TESTS
|
||||
ASSERT_EQ t0, 0x12345678, amoand_old
|
||||
INC_TESTS
|
||||
lw t4, 0(t1)
|
||||
ASSERT_EQ t4, 0x02040608, amoand_new
|
||||
|
||||
# AMOOR.W
|
||||
li t2, 0x12345678
|
||||
sw t2, 0(t1)
|
||||
li t3, 0x0f0f0f0f
|
||||
amoor.w t0, t3, (t1)
|
||||
INC_TESTS
|
||||
ASSERT_EQ t0, 0x12345678, amoor_old
|
||||
INC_TESTS
|
||||
lw t4, 0(t1)
|
||||
ASSERT_EQ t4, 0x1f3f5f7f, amoor_new
|
||||
|
||||
# AMOMIN.W
|
||||
li t2, -10
|
||||
sw t2, 0(t1)
|
||||
li t3, 10
|
||||
amomin.w t0, t3, (t1)
|
||||
INC_TESTS
|
||||
ASSERT_EQ t0, -10, amomin_old1
|
||||
INC_TESTS
|
||||
lw t4, 0(t1)
|
||||
ASSERT_EQ t4, -10, amomin_new1
|
||||
|
||||
li t2, 10
|
||||
sw t2, 0(t1)
|
||||
li t3, -10
|
||||
amomin.w t0, t3, (t1)
|
||||
INC_TESTS
|
||||
ASSERT_EQ t0, 10, amomin_old2
|
||||
INC_TESTS
|
||||
lw t4, 0(t1)
|
||||
ASSERT_EQ t4, -10, amomin_new2
|
||||
|
||||
# AMOMAX.W
|
||||
li t2, -10
|
||||
sw t2, 0(t1)
|
||||
li t3, 10
|
||||
amomax.w t0, t3, (t1)
|
||||
INC_TESTS
|
||||
ASSERT_EQ t0, -10, amomax_old
|
||||
INC_TESTS
|
||||
lw t4, 0(t1)
|
||||
ASSERT_EQ t4, 10, amomax_new
|
||||
|
||||
# AMOMINU.W
|
||||
li t2, -10 # 0xfffffff6
|
||||
sw t2, 0(t1)
|
||||
li t3, 10
|
||||
amominu.w t0, t3, (t1)
|
||||
INC_TESTS
|
||||
ASSERT_EQ t0, -10, amominu_old
|
||||
INC_TESTS
|
||||
lw t4, 0(t1)
|
||||
ASSERT_EQ t4, 10, amominu_new
|
||||
|
||||
# AMOMAXU.W
|
||||
li t2, -10 # 0xfffffff6
|
||||
sw t2, 0(t1)
|
||||
li t3, 10
|
||||
amomaxu.w t0, t3, (t1)
|
||||
INC_TESTS
|
||||
ASSERT_EQ t0, -10, amomaxu_old
|
||||
INC_TESTS
|
||||
lw t4, 0(t1)
|
||||
ASSERT_EQ t4, -10, amomaxu_new
|
||||
|
||||
# ==========================================
|
||||
# RV32I Fence
|
||||
# ==========================================
|
||||
fence
|
||||
|
||||
# ==========================================
|
||||
# Report Results
|
||||
# ==========================================
|
||||
# Print Summary
|
||||
la a0, str_summary_prefix
|
||||
jal print_string
|
||||
|
||||
# Print total test count
|
||||
la t0, test_count
|
||||
lw a0, 0(t0)
|
||||
jal print_decimal
|
||||
|
||||
la a0, str_summary_tests
|
||||
jal print_string
|
||||
|
||||
# Print passed count
|
||||
la t0, success_count
|
||||
lw a0, 0(t0)
|
||||
jal print_decimal
|
||||
|
||||
la a0, str_summary_passed
|
||||
jal print_string
|
||||
|
||||
# Print failed count
|
||||
la t0, failure_count
|
||||
lw a0, 0(t0)
|
||||
jal print_decimal
|
||||
|
||||
la a0, str_summary_failed
|
||||
jal print_string
|
||||
|
||||
la t0, failure_count
|
||||
lw t1, 0(t0)
|
||||
bnez t1, report_error
|
||||
|
||||
la a0, str_all_passed
|
||||
jal print_string
|
||||
li a0, 0
|
||||
li a7, 93
|
||||
ecall
|
||||
|
||||
report_error:
|
||||
la a0, str_some_failed
|
||||
jal print_string
|
||||
li a0, 1
|
||||
li a7, 93
|
||||
ecall
|
||||
|
||||
report_failure:
|
||||
addi sp, sp, -32
|
||||
sw ra, 28(sp)
|
||||
sw s0, 24(sp)
|
||||
sw s1, 20(sp)
|
||||
sw s2, 16(sp)
|
||||
|
||||
mv s0, a0
|
||||
mv s1, a1
|
||||
mv s2, a2
|
||||
|
||||
la a0, str_fail_prefix
|
||||
jal print_string
|
||||
|
||||
mv a0, s0
|
||||
jal print_string
|
||||
|
||||
la a0, str_expected
|
||||
jal print_string
|
||||
|
||||
mv a0, s2
|
||||
jal print_hex
|
||||
|
||||
la a0, str_got
|
||||
jal print_string
|
||||
|
||||
mv a0, s1
|
||||
jal print_hex
|
||||
|
||||
li a0, 10
|
||||
jal print_char
|
||||
|
||||
la t0, failure_count
|
||||
lw t1, 0(t0)
|
||||
addi t1, t1, 1
|
||||
sw t1, 0(t0)
|
||||
|
||||
lw ra, 28(sp)
|
||||
lw s0, 24(sp)
|
||||
lw s1, 20(sp)
|
||||
lw s2, 16(sp)
|
||||
addi sp, sp, 32
|
||||
ret
|
||||
|
||||
print_string:
|
||||
addi sp, sp, -16
|
||||
sw ra, 12(sp)
|
||||
sw s0, 8(sp)
|
||||
mv s0, a0
|
||||
print_string_loop:
|
||||
lbu a0, 0(s0)
|
||||
beqz a0, print_string_done
|
||||
jal print_char
|
||||
addi s0, s0, 1
|
||||
j print_string_loop
|
||||
print_string_done:
|
||||
lw ra, 12(sp)
|
||||
lw s0, 8(sp)
|
||||
addi sp, sp, 16
|
||||
ret
|
||||
|
||||
print_char:
|
||||
addi sp, sp, -16
|
||||
sb a0, 0(sp)
|
||||
li a7, 64
|
||||
li a0, 1
|
||||
mv a1, sp
|
||||
li a2, 1
|
||||
ecall
|
||||
addi sp, sp, 16
|
||||
ret
|
||||
|
||||
print_hex:
|
||||
addi sp, sp, -32
|
||||
sw ra, 28(sp)
|
||||
sw s0, 24(sp)
|
||||
sw s1, 20(sp)
|
||||
sw s2, 16(sp)
|
||||
|
||||
mv s0, a0
|
||||
li s1, 8
|
||||
la s2, hex_chars
|
||||
|
||||
print_hex_loop:
|
||||
srli t0, s0, 28
|
||||
andi t0, t0, 0xF
|
||||
add t1, s2, t0
|
||||
lbu a0, 0(t1)
|
||||
jal print_char
|
||||
slli s0, s0, 4
|
||||
addi s1, s1, -1
|
||||
bnez s1, print_hex_loop
|
||||
|
||||
lw ra, 28(sp)
|
||||
lw s0, 24(sp)
|
||||
lw s1, 20(sp)
|
||||
lw s2, 16(sp)
|
||||
addi sp, sp, 32
|
||||
ret
|
||||
|
||||
print_decimal:
|
||||
addi sp, sp, -32
|
||||
sw ra, 28(sp)
|
||||
sw s0, 24(sp)
|
||||
sw s1, 20(sp)
|
||||
|
||||
mv s0, a0
|
||||
|
||||
# Handle 0 case
|
||||
bnez s0, 1f
|
||||
li a0, 48 # '0'
|
||||
jal print_char
|
||||
j print_decimal_done
|
||||
1:
|
||||
addi s1, sp, 8
|
||||
li t2, 10
|
||||
2:
|
||||
divu t0, s0, t2
|
||||
remu t1, s0, t2
|
||||
addi t1, t1, 48 # convert to ASCII
|
||||
sb t1, 0(s1)
|
||||
addi s1, s1, 1
|
||||
mv s0, t0
|
||||
bnez s0, 2b
|
||||
|
||||
# Now print digits in reverse order
|
||||
3:
|
||||
addi s1, s1, -1
|
||||
lbu a0, 0(s1)
|
||||
jal print_char
|
||||
addi t0, sp, 8
|
||||
bne s1, t0, 3b
|
||||
|
||||
print_decimal_done:
|
||||
lw ra, 28(sp)
|
||||
lw s0, 24(sp)
|
||||
lw s1, 20(sp)
|
||||
addi sp, sp, 32
|
||||
ret
|
||||
|
||||
.section .data
|
||||
.align 2
|
||||
test_count: .word 0
|
||||
success_count: .word 0
|
||||
failure_count: .word 0
|
||||
|
||||
.align 4
|
||||
data_buffer: .zero 16
|
||||
atomic_var: .word 0
|
||||
|
||||
.section .rodata
|
||||
hex_chars: .string "0123456789abcdef"
|
||||
str_fail_prefix: .string " FAIL: "
|
||||
str_expected: .string ", expected 0x"
|
||||
str_got: .string ", got 0x"
|
||||
str_summary_prefix: .string "Executed "
|
||||
str_summary_tests: .string " tests: "
|
||||
str_summary_passed: .string " passed, "
|
||||
str_summary_failed: .string " failed.\n"
|
||||
str_all_passed: .string "All tests PASSED!\n"
|
||||
str_some_failed: .string "Some tests FAILED.\n"
|
||||
Reference in New Issue
Block a user