diff --git a/src/file.S b/src/cat.S similarity index 61% rename from src/file.S rename to src/cat.S index 76ccae6..99a25d3 100644 --- a/src/file.S +++ b/src/cat.S @@ -57,11 +57,31 @@ cat_file: la a2, mod_buf_0 # load mod_buf_0 as write buf 2: - call _add_endings + call add_endings xori s1, s1, 1 # flip the mod buf to write to mv s2, a1 # update the current byte count .LNoEndings: + la t1, number + lb t1, 0(t1) + beqz t1, .LNoNumbers + + mv a1, s2 # load mod buf size to a1 + + beqz s1, 1f + la a0, mod_buf_0 # load mod_buf_0 as read buf + la a2, mod_buf_1 # load mod_buf_1 as write buf + j 2f + 1: + la a0, mod_buf_1 # load mod_buf_1 as read buf + la a2, mod_buf_0 # load mod_buf_0 as write buf + 2: + + call add_line_numbers + xori s1, s1, 1 # flip the mod buf to write to + mv s2, a1 # update the current byte count + .LNoNumbers: + beqz s1, 1f la a1, mod_buf_0 j 2f @@ -109,67 +129,3 @@ open_fd: 2: ret - -# Print to stdout while adding "$" characters. -# @param[in] a0: ptr to null-terminated str. -# @param[in] a1: length of str. -# @param[in] a2: mod buf. -# @param[out] a0: result code. -# @param[out] a1: bytes written to mod buf. -_add_endings: - addi sp, sp, -72 - sd s7, 64(sp) - sd s6, 56(sp) - sd s5, 48(sp) - sd s4, 40(sp) - sd ra, 32(sp) - sd s3, 24(sp) - sd s2, 16(sp) - sd s1, 8(sp) - sd s0, 0(sp) - - - mv s0, a0 # put the cat_buf ptr in s0 - mv s1, a1 - li s2, 0 # set the cat_buf char counter to 0 - li s3, 0 # set the endings_buf char counter to 0 - - mv s5, a2 - .LStdoutEndingsProcessChunk: - add s4, s0, s2 # Calculate the offset address - lb t6, 0(s4) - li t4, 0xA # newline - bne t6, t4, 1f # if the current byte is not a newline, skip to writing - - add s4, s5, s3 # s4 = endings_buf[s2] - li t5, '$' - sb t5, 0(s4) # endings_buf[s2] = $ - li t5, '\n' - addi s4, s4, 1 - sb t5, 0(s4) # endings_buf[s2] = \n - addi s3, s3, 2 - j 2f - - 1: - add s4, s5, s3 # s4 = endings_buf[s2] - sb t6, 0(s4) # endings_buf[s2] = t6 - - addi s3, s3, 1 # inc endings_buf pointer - 2: - addi s2, s2, 1 # inc cat_buf pointer - blt s2, s1, .LStdoutEndingsProcessChunk - - li a0, 0 - mv a1, s3 - - ld s0, 0(sp) - ld s1, 8(sp) - ld s2, 16(sp) - ld s3, 24(sp) - ld ra, 32(sp) - ld s4, 40(sp) - ld s5, 48(sp) - ld s6, 56(sp) - ld s7, 64(sp) - addi sp, sp, 72 - ret diff --git a/src/main.S b/src/main.S index def4c09..13323f0 100644 --- a/src/main.S +++ b/src/main.S @@ -1,6 +1,7 @@ .data argc: .space 8 show_ends: .space 1 +number: .space 1 cat_prefix: .ascii "cat: " .equ CAT_PREFIX_LEN, . - cat_prefix @@ -13,13 +14,14 @@ help: .ascii "Concatenate FILE(S) to standard output.\n\n" .ascii "With no FILE, or when FILE is -, read standard input.\n\n" .ascii " -E, --show-ends display $ at end of each line\n" + .ascii " -n, --number number all output lines\n" .ascii " --help display this help and exit\n" .ascii " --version output version information and exit\n\n" .asciz "Git repository: \n" .equ HELP_LEN, . - help version: - .ascii "cat 1.2.0\n" + .ascii "cat 1.3.0\n" .ascii "Copyright (C) 2026 lexzach\n" .ascii "License MIT\n" .ascii "This is free software: you are free to change and redistribute it.\n" @@ -31,9 +33,11 @@ flag_help: .asciz "--help" flag_version: .asciz "--version" flag_show_ends: .asciz "--show-ends" flag_E: .asciz "-E" +flag_number: .asciz "--number" +flag_n: .asciz "-n" .section .text -.globl _start, show_ends +.globl _start, show_ends, number # main. _start: @@ -48,6 +52,8 @@ _start: li s1, 1 li s2, 0 # processed files + call zero_line_count + bnez a0, .LMainReturnErr call _check_flags li t0, 1 @@ -150,6 +156,12 @@ _check_flags: bnez a0, .LMainReturnErr sb a1, show_ends, t1 # since the return for has switch is either 0 for not found or 1 for found, we can just write it directly into the buf + la a0, flag_number + la a1, flag_n + call has_switch + bnez a0, .LMainReturnErr + sb a1, number, t1 + ld ra, 0(sp) addi sp, sp, 8 ret @@ -185,6 +197,16 @@ _is_known_flag: call str_cmp bnez a1, .LFlagFound + la a0, flag_number + mv a1, s0 + call str_cmp + bnez a1, .LFlagFound + + la a0, flag_n + mv a1, s0 + call str_cmp + bnez a1, .LFlagFound + li a1, 0 .LFlagFound: ld s0, 0(sp) diff --git a/src/mod.S b/src/mod.S new file mode 100644 index 0000000..19fa675 --- /dev/null +++ b/src/mod.S @@ -0,0 +1,208 @@ +.data +line_count: .space 6 + +.section .text +.globl add_endings, add_line_numbers, zero_line_count + +# Print to stdout while adding "$" characters. +# @param[in] a0: ptr to null-terminated str. +# @param[in] a1: length of str. +# @param[in] a2: mod buf addr. +# @param[out] a0: result code. +# @param[out] a1: bytes written to mod buf. +add_endings: + addi sp, sp, -72 + sd s7, 64(sp) + sd s6, 56(sp) + sd s5, 48(sp) + sd s4, 40(sp) + sd ra, 32(sp) + sd s3, 24(sp) + sd s2, 16(sp) + sd s1, 8(sp) + sd s0, 0(sp) + + + mv s0, a0 # put the str buf ptr in s0 + mv s1, a1 + li s2, 0 # set the str buf char counter to 0 + li s3, 0 # set the mod buf char counter to 0 + + mv s5, a2 + .LEndingsProcessChunk: + add s4, s0, s2 # Calculate the offset address + lb t6, 0(s4) + li t4, 0xA # newline + bne t6, t4, 1f # if the current byte is not a newline, skip to writing + + add s4, s5, s3 # s4 = mod buf[s2] + li t5, '$' + sb t5, 0(s4) # mod buf[s2] = $ + li t5, '\n' + addi s4, s4, 1 + sb t5, 0(s4) # mod buf[s2] = \n + addi s3, s3, 2 + j 2f + + 1: + add s4, s5, s3 # s4 = mod buf[s2] + sb t6, 0(s4) # mod buf[s2] = t6 + + addi s3, s3, 1 # inc mod buf pointer + 2: + addi s2, s2, 1 # inc str buf pointer + blt s2, s1, .LEndingsProcessChunk + + li a0, 0 + mv a1, s3 + + ld s0, 0(sp) + ld s1, 8(sp) + ld s2, 16(sp) + ld s3, 24(sp) + ld ra, 32(sp) + ld s4, 40(sp) + ld s5, 48(sp) + ld s6, 56(sp) + ld s7, 64(sp) + addi sp, sp, 72 + ret + +# Prefix newlines with ######+TAB line count +# @param[in] a0: ptr to null-terminated str. +# @param[in] a1: length of str. +# @param[in] a2: mod buf addr. +# @param[in] a3: skip empty. +# @param[out] a0: result code. +# @param[out] a1: bytes written to mod buf. +add_line_numbers: + addi sp, sp, -72 + sd s7, 64(sp) #unused + sd s6, 56(sp) # mod buf ptr + sd s5, 48(sp) # mod buf bp + sd s4, 40(sp) # str buf ptr + sd ra, 32(sp) + sd s3, 24(sp) # mod buf offset + sd s2, 16(sp) # str buf offset + sd s1, 8(sp) # len of str + sd s0, 0(sp) # str buf addr + + + mv s0, a0 # put the str ptr in s0 + mv s1, a1 # move length of str into s1 + li s2, 0 # set the str buf char offset to 0 + li s3, 0 # set the mod buf char offset to 0 + mv s5, a2 # move mod buf addr into s5 + + .LLineNumbersProcessChunk: + add s4, s0, s2 # Calculate the offset address + lb t6, 0(s4) + li t4, 0xA # newline + bne t6, t4, 1f # if the current byte is not a newline, skip to writing + + call _inc_line_count + + addi s3, s3, 1 # we want the ptr at the spot right after the newline + add s6, s5, s3 # update mod buf + offset addr + + li t2, 0 # lc char buf offset + li t3, 6 # lc char buf max offset + la t1, line_count # load the line count buffer addr + li t5, 0xA # newline character + sb t5, 0(s6) + addi s3, s3, 1 + add s6, s5, s3 # update mod buf + offset addr + 3: + add t4, t1, t2 + lb t5, 0(t4) + sb t5, 0(s6) # mod buf[s4] = t4 + addi t2, t2, 1 + addi s3, s3, 1 + add s6, s5, s3 # update mod buf + offset addr + blt t2, t3, 3b + + li t5, 0x7 # tab character + sb t5, 0(s6) + + addi s3, s3, 1 + + j 2f + + 1: + add s6, s5, s3 # s4 = mod buf[s2] + sb t6, 0(s6) # mod buf[s2] = t6 + + addi s3, s3, 1 # inc mod buf pointer + 2: + addi s2, s2, 1 # inc str buf pointer + blt s2, s1, .LLineNumbersProcessChunk + + li a0, 0 + mv a1, s3 + + ld s0, 0(sp) + ld s1, 8(sp) + ld s2, 16(sp) + ld s3, 24(sp) + ld ra, 32(sp) + ld s4, 40(sp) + ld s5, 48(sp) + ld s6, 56(sp) + ld s7, 64(sp) + addi sp, sp, 72 + ret + + +# Increment the line count string +# @param[in] a0: not used +# @param[out] a0: result code +_inc_line_count: + la t0, line_count # load the line count buffer address + li t1, 5 # current offset, we move from right to left first + + .LIncLine: + bltz t1, .LLineCountDoneInc + add t2, t0, t1 # load the correct offset address into t2 + lb t3, 0(t2) # load the current ascii character into t3 + + li t4, 32 # load space character + bne t3, t4, .LLineCountNotSpaceChar # if the character is not a space, skip setting to zero + li t4, 48 # ascii '0' + sb t4, 0(t2) # store this at the current pointer + + .LLineCountNotSpaceChar: + add t2, t0, t1 # load the correct offset address into t2 + lb t3, 0(t2) # load the current ascii character into t3 + li t4, 58 # ascii ':', right after ascii '9', overflow + + addi t3, t3, 1 + beq t3, t4, .LLineCountOverflow + sb t3, 0(t2) + j .LLineCountDoneInc + .LLineCountOverflow: + li t4, 48 # ascii '0' + sb t4, 0(t2) # store that ascii '0' + li t4, 1 + sub t1, t1, t4 # move to the next digit to the left + j .LIncLine + + .LLineCountDoneInc: + ret + +# Set the line count to all space characters +# @param[in] a0: not used +# @param[out] a0: result code +zero_line_count: + la t0, line_count # load the line count buffer address + li t1, 0 # char ptr + li t2, 6 # max char + li t3, 32 # space character + + 1: + add t4, t0, t1 # load offset addr + sb t3, 0(t4) # store the space char in offset addr + addi t1, t1, 1 + blt t1, t2, 1b + + li a0, 0 + ret