cmdline support
This commit is contained in:
@@ -5,7 +5,7 @@ EMULATOR = ./amach
|
|||||||
ASFLAGS = -march=rv32imac -mabi=ilp32 -nostdlib -s -static -fno-pic -fno-pie -mno-relax
|
ASFLAGS = -march=rv32imac -mabi=ilp32 -nostdlib -s -static -fno-pic -fno-pie -mno-relax
|
||||||
LDFLAGS = -Wl,-Ttext=0x100b0 -Wl,--build-id=none
|
LDFLAGS = -Wl,-Ttext=0x100b0 -Wl,--build-id=none
|
||||||
|
|
||||||
all: rv32imac_tests.bin rv32imac_syscall_tests.bin rv32imac_tests.elf rv32imac_syscall_tests.elf
|
all: rv32imac_tests.bin rv32imac_syscall_tests.bin rv32imac_args_tests.bin rv32imac_tests.elf rv32imac_syscall_tests.elf rv32imac_args_tests.elf
|
||||||
|
|
||||||
rv32imac_tests.elf: rv32imac_tests.s
|
rv32imac_tests.elf: rv32imac_tests.s
|
||||||
$(CC) $(ASFLAGS) $(LDFLAGS) rv32imac_tests.s -o rv32imac_tests.elf
|
$(CC) $(ASFLAGS) $(LDFLAGS) rv32imac_tests.s -o rv32imac_tests.elf
|
||||||
@@ -19,7 +19,13 @@ rv32imac_syscall_tests.elf: rv32imac_syscall_tests.s
|
|||||||
rv32imac_syscall_tests.bin: rv32imac_syscall_tests.elf
|
rv32imac_syscall_tests.bin: rv32imac_syscall_tests.elf
|
||||||
$(OBJCOPY) -O binary rv32imac_syscall_tests.elf rv32imac_syscall_tests.bin
|
$(OBJCOPY) -O binary rv32imac_syscall_tests.elf rv32imac_syscall_tests.bin
|
||||||
|
|
||||||
run: rv32imac_tests.bin rv32imac_syscall_tests.bin rv32imac_tests.elf rv32imac_syscall_tests.elf
|
rv32imac_args_tests.elf: rv32imac_args_tests.s
|
||||||
|
$(CC) $(ASFLAGS) $(LDFLAGS) rv32imac_args_tests.s -o rv32imac_args_tests.elf
|
||||||
|
|
||||||
|
rv32imac_args_tests.bin: rv32imac_args_tests.elf
|
||||||
|
$(OBJCOPY) -O binary rv32imac_args_tests.elf rv32imac_args_tests.bin
|
||||||
|
|
||||||
|
run: rv32imac_tests.bin rv32imac_syscall_tests.bin rv32imac_args_tests.bin rv32imac_tests.elf rv32imac_syscall_tests.elf rv32imac_args_tests.elf
|
||||||
@echo "Running CPU instruction tests (binary)..."
|
@echo "Running CPU instruction tests (binary)..."
|
||||||
$(EMULATOR) rv32imac_tests.bin
|
$(EMULATOR) rv32imac_tests.bin
|
||||||
@echo "Running CPU instruction tests (ELF)..."
|
@echo "Running CPU instruction tests (ELF)..."
|
||||||
@@ -32,6 +38,10 @@ run: rv32imac_tests.bin rv32imac_syscall_tests.bin rv32imac_tests.elf rv32imac_s
|
|||||||
rm -rf test_dir test_link.txt test_output.txt test_output_new.txt
|
rm -rf test_dir test_link.txt test_output.txt test_output_new.txt
|
||||||
ln -sf test_output_new.txt test_link.txt
|
ln -sf test_output_new.txt test_link.txt
|
||||||
$(EMULATOR) rv32imac_syscall_tests.elf
|
$(EMULATOR) rv32imac_syscall_tests.elf
|
||||||
|
@echo "Running command line arguments tests (binary)..."
|
||||||
|
$(EMULATOR) rv32imac_args_tests.bin arg1 arg2
|
||||||
|
@echo "Running command line arguments tests (ELF)..."
|
||||||
|
$(EMULATOR) rv32imac_args_tests.elf arg1 arg2
|
||||||
@echo "Testing standard ebreak..."
|
@echo "Testing standard ebreak..."
|
||||||
@echo "115 0 16 0" | awk -f amach.awk 2>&1 | grep -q "Fatal: EBREAK"
|
@echo "115 0 16 0" | awk -f amach.awk 2>&1 | grep -q "Fatal: EBREAK"
|
||||||
@echo "Testing compressed c.ebreak..."
|
@echo "Testing compressed c.ebreak..."
|
||||||
@@ -39,6 +49,6 @@ run: rv32imac_tests.bin rv32imac_syscall_tests.bin rv32imac_tests.elf rv32imac_s
|
|||||||
@echo "All tests PASSED!"
|
@echo "All tests PASSED!"
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf rv32imac_tests.elf rv32imac_tests.bin rv32imac_syscall_tests.elf rv32imac_syscall_tests.bin test_dir test_link.txt test_output.txt test_output_new.txt
|
rm -rf rv32imac_tests.elf rv32imac_tests.bin rv32imac_syscall_tests.elf rv32imac_syscall_tests.bin rv32imac_args_tests.elf rv32imac_args_tests.bin test_dir test_link.txt test_output.txt test_output_new.txt
|
||||||
|
|
||||||
.PHONY: all run clean
|
.PHONY: all run clean
|
||||||
|
|||||||
@@ -19,17 +19,17 @@ First, decide where your program needs to be loaded in memory. The default LVA (
|
|||||||
|
|
||||||
Then, most probably, you'll want to use the provided wrapper:
|
Then, most probably, you'll want to use the provided wrapper:
|
||||||
```sh
|
```sh
|
||||||
[LVA=...] ./amach path/to/program.bin
|
[LVA=...] ./amach path/to/program.bin [...arguments]
|
||||||
```
|
```
|
||||||
|
|
||||||
Or you can run the following:
|
Or you can run the following:
|
||||||
```sh
|
```sh
|
||||||
od -An -v -tu1 path/to/program.bin | POSIXLY_CORRECT=1 awk -f amach.awk [-v LVA=...]
|
od -An -v -tu1 path/to/program.bin | POSIXLY_CORRECT=1 awk -f amach.awk [-v LVA=...] [-v CMD_OPTS=...]
|
||||||
```
|
```
|
||||||
|
|
||||||
But in case you already have a program in the `.dec` format, you can pass it directly to AWK:
|
But in case you already have a program in the `.dec` format, you can pass it directly to AWK:
|
||||||
```sh
|
```sh
|
||||||
POSIXLY_CORRECT=1 awk -f amach.awk [-v LVA=...] -- path/to/program.dec
|
POSIXLY_CORRECT=1 awk -f amach.awk [-v LVA=...] [-v CMD_OPTS=...] -- path/to/program.dec
|
||||||
```
|
```
|
||||||
|
|
||||||
### ELF Executables
|
### ELF Executables
|
||||||
@@ -41,7 +41,7 @@ POSIXLY_CORRECT=1 awk -f amach.awk [-v LVA=...] -- path/to/program.dec
|
|||||||
|
|
||||||
You can launch ELF files directly using the wrapper or via the same pipe pipeline:
|
You can launch ELF files directly using the wrapper or via the same pipe pipeline:
|
||||||
```sh
|
```sh
|
||||||
./amach path/to/program.elf
|
./amach path/to/program.elf [...arguments]
|
||||||
```
|
```
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|||||||
@@ -1,4 +1,16 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# A-Machine .bin launcher wrapper
|
# A-Machine .bin/.elf launcher wrapper
|
||||||
[ -z "$LVA" ] && LVA=65712 # 0x100b0
|
[ -z "$LVA" ] && LVA=65712 # 0x100b0
|
||||||
od -An -v -tu1 "$1" | POSIXLY_CORRECT=1 awk -f amach.awk -v LVA="$LVA"
|
SEP=$(printf '\001')
|
||||||
|
CMD_OPTS=""
|
||||||
|
first=1
|
||||||
|
for arg in "$@"; do
|
||||||
|
if [ "$first" = 1 ]; then
|
||||||
|
CMD_OPTS="$arg"
|
||||||
|
first=0
|
||||||
|
else
|
||||||
|
CMD_OPTS="$CMD_OPTS$SEP$arg"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
[ -z "$AWK" ] && AWK=awk
|
||||||
|
od -An -v -tu1 "$1" | POSIXLY_CORRECT=1 $AWK -f amach.awk -v LVA="$LVA" -v CMD_OPTS="$CMD_OPTS"
|
||||||
|
|||||||
@@ -781,6 +781,35 @@ END {
|
|||||||
LVA = e_entry
|
LVA = e_entry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Setup command-line arguments on the stack
|
||||||
|
n = 0
|
||||||
|
if(length(CMD_OPTS) > 0) n = split(CMD_OPTS, args, "\001")
|
||||||
|
else {
|
||||||
|
n = 1
|
||||||
|
args[1] = "amach"
|
||||||
|
}
|
||||||
|
|
||||||
|
sp = getreg(2)
|
||||||
|
for(i = n; i >= 1; i--) {
|
||||||
|
len = length(args[i])
|
||||||
|
sp = sp - (len + 1)
|
||||||
|
for(j = 1; j <= len; j++)
|
||||||
|
MEM[uint32(sp + j - 1)] = ord(substr(args[i], j, 1))
|
||||||
|
MEM[uint32(sp + len)] = 0
|
||||||
|
argv_ptr[i] = sp
|
||||||
|
}
|
||||||
|
|
||||||
|
sp = sp - (sp % 16)
|
||||||
|
sp = sp - 4 * (n + 3)
|
||||||
|
sp = sp - (sp % 16)
|
||||||
|
|
||||||
|
write_word(sp, n)
|
||||||
|
for(i = 1; i <= n; i++) write_word(sp + 4 * i, argv_ptr[i])
|
||||||
|
write_word(sp + 4 * (n + 1), 0)
|
||||||
|
write_word(sp + 4 * (n + 2), 0)
|
||||||
|
|
||||||
|
setreg(2, sp)
|
||||||
|
|
||||||
memsize = pc - LVA
|
memsize = pc - LVA
|
||||||
pc = LVA
|
pc = LVA
|
||||||
while(pc > -1) {
|
while(pc > -1) {
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
.section .text
|
||||||
|
.option norvc
|
||||||
|
.global _start
|
||||||
|
|
||||||
|
# Helper macro to exit with code if condition fails
|
||||||
|
.macro ASSERT_EQ reg, val, error_code
|
||||||
|
li t4, \val
|
||||||
|
beq \reg, t4, .L_ok_\@
|
||||||
|
li a0, \error_code
|
||||||
|
li a7, 93
|
||||||
|
ecall
|
||||||
|
.L_ok_\@:
|
||||||
|
.endm
|
||||||
|
|
||||||
|
# Compare string at register \str_reg with data label \label, exit with \error_code if not equal
|
||||||
|
.macro ASSERT_STR_EQ str_reg, label, error_code
|
||||||
|
la t0, \label
|
||||||
|
mv t1, \str_reg
|
||||||
|
.L_loop_\@:
|
||||||
|
lbu t2, 0(t0)
|
||||||
|
lbu t3, 0(t1)
|
||||||
|
bne t2, t3, .L_fail_\@
|
||||||
|
beqz t2, .L_ok_\@
|
||||||
|
addi t0, t0, 1
|
||||||
|
addi t1, t1, 1
|
||||||
|
j .L_loop_\@
|
||||||
|
.L_fail_\@:
|
||||||
|
li a0, \error_code
|
||||||
|
li a7, 93
|
||||||
|
ecall
|
||||||
|
.L_ok_\@:
|
||||||
|
.endm
|
||||||
|
|
||||||
|
_start:
|
||||||
|
# sp + 0: argc
|
||||||
|
# sp + 4: argv[0]
|
||||||
|
# sp + 8: argv[1]
|
||||||
|
# sp + 12: argv[2]
|
||||||
|
|
||||||
|
# 1. Print verification start message
|
||||||
|
li a7, 64 # sys_write
|
||||||
|
li a0, 1 # stdout
|
||||||
|
la a1, msg_start
|
||||||
|
li a2, 32 # message length
|
||||||
|
ecall
|
||||||
|
|
||||||
|
# 2. Check argc == 3
|
||||||
|
lw t0, 0(sp)
|
||||||
|
ASSERT_EQ t0, 3, 10
|
||||||
|
|
||||||
|
# 3. Check argv[0] is not null and not empty
|
||||||
|
lw t0, 4(sp)
|
||||||
|
beqz t0, .L_argv0_bad
|
||||||
|
lbu t1, 0(t0)
|
||||||
|
beqz t1, .L_argv0_bad
|
||||||
|
j .L_argv0_ok
|
||||||
|
.L_argv0_bad:
|
||||||
|
li a0, 11
|
||||||
|
li a7, 93
|
||||||
|
ecall
|
||||||
|
.L_argv0_ok:
|
||||||
|
|
||||||
|
# 4. Check argv[1] == "arg1"
|
||||||
|
lw t0, 8(sp)
|
||||||
|
ASSERT_STR_EQ t0, str_arg1, 12
|
||||||
|
|
||||||
|
# 5. Check argv[2] == "arg2"
|
||||||
|
lw t0, 12(sp)
|
||||||
|
ASSERT_STR_EQ t0, str_arg2, 13
|
||||||
|
|
||||||
|
# 6. Print success message
|
||||||
|
li a7, 64 # sys_write
|
||||||
|
li a0, 1 # stdout
|
||||||
|
la a1, msg_success
|
||||||
|
li a2, 32 # message length
|
||||||
|
ecall
|
||||||
|
|
||||||
|
# 7. Exit with code 0 on success
|
||||||
|
li a0, 0
|
||||||
|
li a7, 93
|
||||||
|
ecall
|
||||||
|
|
||||||
|
.section .rodata
|
||||||
|
msg_start:
|
||||||
|
.ascii "Running Command Line Args Tests\n"
|
||||||
|
msg_success:
|
||||||
|
.ascii "Command Line Args Tests PASSED!\n"
|
||||||
|
str_arg1:
|
||||||
|
.asciz "arg1"
|
||||||
|
str_arg2:
|
||||||
|
.asciz "arg2"
|
||||||
Reference in New Issue
Block a user