diff --git a/Makefile b/Makefile index 730358b..20aba3d 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,7 @@ all: true false cat env hexdump echo prtest: prtest.o lib/printregs.o lib/hex.o sorttest: lib/sort.o +flagtest: lib/flags.o .PHONY: test test: diff --git a/flagtest.s b/flagtest.s new file mode 100644 index 0000000..f2a147a --- /dev/null +++ b/flagtest.s @@ -0,0 +1,35 @@ + +.global _start +.extern init_flags +.extern parse_flags + +.data + +flags: +.dword 0 # value / default +.byte 1 # type: , bool, string, int +.byte 5 # flag length +#.short 16 # help length +.ascii "-help" +#.ascii "prints help text" + +# last flag +.dword 0 +.dword 0 + +.text + +_start: + .option push + .option norelax + la gp, __global_pointer$ + .option pop + + mv a0, sp + call init_flags + la a0, flags + call parse_flags + + li a7, 93 # sys_exit + li a0, 0 + ecall diff --git a/lib/flags.s b/lib/flags.s index 4e1c66c..aab0c17 100644 --- a/lib/flags.s +++ b/lib/flags.s @@ -1,4 +1,12 @@ +.option norelax +.global init_flags +.global parse_flags + +.macro subi dst, src, imm + addi \dst, \src, -\imm +.endm + .data flags: @@ -19,6 +27,15 @@ 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. # @@ -56,11 +73,13 @@ parse_flags: 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: @@ -68,27 +87,53 @@ parse_flags_args: # -flag value (only non-boolean flags) # -flag=value - # main loop + # 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 - mv s2, a2 + + # 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 '-' - ld t0, (s1) - beqz t0, .Lno # empty string - addi t0, t0, -'-' - bnez t0, .Lno # not a dash + 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 t2, 9(s0) # flag length - beqz t2, .Lunknown_flag + 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 @@ -97,42 +142,53 @@ parse_flags_args: bnez t0, .Lfound .L_next_flag: - addi s0, s0, 8 - add s0, s0, t2 + addi s0, s0, 10 + add s0, s0, s3 j .L_flag_loop .Lfound: - add t0, s1, t2 # arg+len - lbu t1, (t0) - beqz t1, 1f # is it a \0 or an =? - 0:# move s1 past the = - addi t0, t0, 1 - mv s1, t0 - j 2f - 1:# is this a boolean flag? - li t0, 1 - beq t0, t3, 2f + 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 - 2: + # fallthrough + # s1 now points to the value of the flag + +handle_flag: # ok now parse the flag -.Lparse_flag: - lbu t3, 8(s0) # load type - li t0, 1 - beq t0, t3, boolflag - li t0, 2 + #lbu t3, 8(s0) # load type + beqz t3, boolflag + li t0, 2-1 beq t0, t3, strflag - li t0, 3 + 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' @@ -140,63 +196,56 @@ boolflag: li t1, 't' beq t0, t1, .Lboolcmptrue # anything else is an error - j .Lboolbad + j .Lbadbool .Lboolcmpfalse: lbu t0, 1(s1) - beqz t0, .Lboolbad + beqz t0, .Lbadbool li t1, 'a' - bne t1, t0, .Lboolbad + bne t1, t0, .Lbadbool lbu t0, 2(s1) - beqz t0, .Lboolbad + beqz t0, .Lbadbool li t1, 'l' - bne t1, t0, .Lboolbad + bne t1, t0, .Lbadbool lbu t0, 3(s1) - beqz t0, .Lboolbad + beqz t0, .Lbadbool li t1, 's' - bne t1, t0, .Lboolbad + bne t1, t0, .Lbadbool lbu t0, 4(s1) - beqz t0, .Lboolbad + beqz t0, .Lbadbool li t1, 's' - bne t1, t0, .Lboolbad + bne t1, t0, .Lbadbool lbu t0, 5(s1) - bnez t0, .Lboolbad - j 0b + bnez t0, .Lbadbool - sd x0, 0(s0) + 0:sd x0, 0(s0) j .L_next_arg .Lboolcmptrue: lbu t0, 1(s1) - beqz t0, .Lboolbad + beqz t0, .Lbadbool li t1, 'r' - bne t1, t0, .Lboolbad + bne t1, t0, .Lbadbool lbu t0, 2(s1) - beqz t0, .Lboolbad + beqz t0, .Lbadbool li t1, 'u' - bne t1, t0, .Lboolbad + bne t1, t0, .Lbadbool lbu t0, 3(s1) - beqz t0, .Lboolbad + beqz t0, .Lbadbool li t1, 'e' - bne t1, t0, .Lboolbad + bne t1, t0, .Lbadbool lbu t0, 4(s1) - bnez t0, .Lboolbad + bnez t0, .Lbadbool - 1:li t1, 1 - sd t1, 0(s0) - j .L_next_arg - -strflag: - # easy, just store the string pointer - # TODO: strlen - sd s1, 0(s0) + 1:li t0, 1 + sd t0, 0(s0) j .L_next_arg intflag: @@ -208,7 +257,7 @@ intflag: li t2, 10 lbu t1, 0(s1) beqz t1, .Lbadint - 0:addi t1, t1, -0x30 # '0' + 0:subi t1, t1, 0x30 # '0' bltz t1, .Lbadint bge t1, t2, .Lbadint mul t0, t0, t2 @@ -219,25 +268,50 @@ intflag: 1:sd t0, 0(s0) j .L_next_arg - -.L_next_arg: - - - ret - +.Lbad_flag_def: .Lbadint: -.Lboolbad: -.Lunknown_flag: +.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 -# t4 - flag -# t5 - -# t6 length -# t0 - return +# 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: - mv t0, zero + 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 -handle_flag: -