riscv-utils/lib/flags.s

318 lines
6.0 KiB
ArmAsm

.option norelax
.global init_flags
.global parse_flags
.macro subi dst, src, imm
addi \dst, \src, -\imm
.endm
.data
flags:
.dword 0 # value / default
.byte 1 # type: <end>, bool, string, int
.byte 5 # flag length
#.short 16 # help length
.ascii "-help"
#.ascii "prints help text"
# last flag
.dword 0
.dword 0
.bss
entry_sp: .dword 0
saved_argc: .dword 0
saved_argv: .dword 0
.section .rodata
flag_failure_msg: .ascii "flag parsing failed\n\0"
.equ flag_failure_msg_len, (.) - flag_failure_msg
unknown_flag_msg: .ascii "unknown flag\n\0"
.equ unknown_flag_msg_len, (.) - unknown_flag_msg
.text
# when a program starts, it should stash the initial value of the sp register somewhere
# e.g.
#
# .data
# entry_sp:
# .dword 0
# _start:
# la t0, entry_sp
# sd sp, (t0)
#
# then at some opportune moment, it can call init_flags.
# a0 should be used to pass in the initial sp value.
#
# la t0, entry_sp
# ld a0, (t0)
# call init_flags
# in order to store argc and argv
# a0 - initial stack pointer
init_flags:
la t0, saved_argc
ld t1, (a0)
sd t1, (t0)
la t0, saved_argv
add t2, a0, 8
sd t2, (t0)
ret
# a0 - flag definition struct
parse_flags:
la a1, saved_argc
la a2, saved_argv
ld a1, (a1)
ld a2, (a2)
tail parse_flags_args
# n.b. tail clobbers t1
# a0 - flag definition struct
# a1 - argc
# a2 - argv
parse_flags_args:
mv a7, ra
# TODO: positional arguments
# syntax:
# -flag (only boolean flags)
# -flag value (only non-boolean flags)
# -flag=value
# if argc <= 1, return
li t0, 1
bleu a1, t0, .Ldone
# main loop:
# for each argument, if it starts with a '-' then see if it matches a flag
# skip arg 0
addi s2, a2, 8
subi a1, a1, 1
.L_arg_loop:
ld s1, (s2) # load string pointer
beqz s1, .L_next_arg # skip null pointer
# check if first char is a '-'
lbu t0, 0(s1)
li t1, '-'
beq t0, t1, find_flag
.L_next_arg:
addi s2, s2, 8
subi a1, a1, 1
beqz a1, .Ldone
j .L_arg_loop
.Ldone:
mv ra, a7
ret
.Lunknown_flag:
mv ra, a7
li a7, 64 # sys_write
li a0, 2
la a1, unknown_flag_msg
li a2, unknown_flag_msg_len
ecall
li a0, -1
ret
# TODO: allow --flag
# TODO: -- should end flag parsing
find_flag:
mv s0, a0
.L_flag_loop:
lbu t3, 8(s0) # type
lbu s3, 9(s0) # flag length
beqz s3, .Lunknown_flag # len=0 signifies the end of the flag definitions
call flagcmp
# return value:
# 0: no match
# 1: match
# 2: match w/ equal sign
bnez t0, .Lfound
.L_next_flag:
addi s0, s0, 10
add s0, s0, s3
j .L_flag_loop
.Lfound:
subi t3, t3, 1
add s1, s1, s3 # arg+len
lbu t0, (s1)
beqz t0, 1f # is it a \0 or an =?
# =
# move s1 past the =
addi s1, s1, 1
j handle_flag
1:# \0
# is this a boolean flag? then there's no value
beqz t3, handle_flag
# if not, set s1 to the next arg and advance s2
ld s1, 8(s2)
addi s2, s2, 8
subi a1, a1, 1
# TODO: if it's null, that's an error
# fallthrough
# s1 now points to the value of the flag
handle_flag:
# ok now parse the flag
#lbu t3, 8(s0) # load type
beqz t3, boolflag
li t0, 2-1
beq t0, t3, strflag
li t0, 3-1
beq t0, t3, intflag
j .Lbad_flag_def
strflag:
# easy, just store the string pointer
# TODO: strlen
sd s1, 0(s0)
j .L_next_arg
boolflag:
lbu t0, 0(s1)
# if arg is empty, set value to true
# TODO: should -bool and -bool= be treated differently?
beqz t0, 0f
# check for "true" and "false"
li t1, 'f'
beq t0, t1, .Lboolcmpfalse
li t1, 't'
beq t0, t1, .Lboolcmptrue
# anything else is an error
j .Lbadbool
.Lboolcmpfalse:
lbu t0, 1(s1)
beqz t0, .Lbadbool
li t1, 'a'
bne t1, t0, .Lbadbool
lbu t0, 2(s1)
beqz t0, .Lbadbool
li t1, 'l'
bne t1, t0, .Lbadbool
lbu t0, 3(s1)
beqz t0, .Lbadbool
li t1, 's'
bne t1, t0, .Lbadbool
lbu t0, 4(s1)
beqz t0, .Lbadbool
li t1, 's'
bne t1, t0, .Lbadbool
lbu t0, 5(s1)
bnez t0, .Lbadbool
0:sd x0, 0(s0)
j .L_next_arg
.Lboolcmptrue:
lbu t0, 1(s1)
beqz t0, .Lbadbool
li t1, 'r'
bne t1, t0, .Lbadbool
lbu t0, 2(s1)
beqz t0, .Lbadbool
li t1, 'u'
bne t1, t0, .Lbadbool
lbu t0, 3(s1)
beqz t0, .Lbadbool
li t1, 'e'
bne t1, t0, .Lbadbool
lbu t0, 4(s1)
bnez t0, .Lbadbool
1:li t0, 1
sd t0, 0(s0)
j .L_next_arg
intflag:
# gotta convert string to int
# TODO: negative numbers
# TODO: detect overflow
# TODO: hex
mv t0, x0
li t2, 10
lbu t1, 0(s1)
beqz t1, .Lbadint
0:subi t1, t1, 0x30 # '0'
bltz t1, .Lbadint
bge t1, t2, .Lbadint
mul t0, t0, t2
add t0, t0, t1
addi s1, s1, 1
lbu t1, 0(s1)
bnez t1, 0b
1:sd t0, 0(s0)
j .L_next_arg
.Lbad_flag_def:
.Lbadint:
.Lbadbool:
mv ra, a7
li a7, 64
li a0, 2
la a1, flag_failure_msg
li a2, flag_failure_msg_len
ecall
li a0, -1
ret
# in: s1 - string to compare, length unknown (null terminated)
# in: s0 - flag definition
# in: s3 - flag length
# out: t0 - match=1 no match=0
# clobbers t0, t1, t4, t5, t6, ra
flagcmp:
add t1, s0, 10 # flag
add t0, t1, s3 # end of flag
mv t4, s1 # str
3:lbu t5, (t1)
lbu t6, (t4)
bne t5, t6, 0f
# we don't _need_ to specifically check if t6==\0
# since that should be caught by the comparison above
# unless the flag contains an embedded \0 (it shouldn't)
# but do the check anyway just to be extra safe
beqz t6, 0f
addi t1, t1, 1
addi t4, t4, 1
# once we reach the end of the flag, we just need to check
# that the argument string ends in the same place
bge t1, t0, .Ltail
j 3b # loop
.Ltail:
lbu t1, (t4)
li t0, '='
beq t0, t1, 2f
beqz t1, 1f
0:mv t0, zero
ret
2:li t0, 2
ret
1:li t0, 1
ret