From 71c9a4fb3664483fa3ac04a23b06db20fe8c3c6e Mon Sep 17 00:00:00 2001 From: lexzach Date: Sun, 13 Sep 2026 21:30:17 -0400 Subject: [PATCH 1/3] Initial show ends --- src/file.S | 131 +++++++++++++++++++++++++++++++++++++++++++++-------- src/main.S | 22 ++++++--- 2 files changed, 127 insertions(+), 26 deletions(-) diff --git a/src/file.S b/src/file.S index 97ffae8..eabfa18 100644 --- a/src/file.S +++ b/src/file.S @@ -1,5 +1,6 @@ .data -cat_buf: .space 4096 +cat_buf: .space 4096 +endings_buf: .space 4097 .section .text .globl cat_file, open_fd, cat_stdin @@ -8,6 +9,11 @@ cat_buf: .space 4096 # @param[in] a0: ptr to null-terminated file str. # @param[out] a0: result code. cat_file: + addi sp, sp, -24 + sd s0, 0(sp) + sd ra, 8(sp) + sd s1, 16(sp) + mv a1, a0 # since the function takes in a0, move it to a1 for syscall addi sp, sp, -8 @@ -16,21 +22,30 @@ cat_file: ld ra, 0(sp) addi sp, sp, 8 - mv t0, a1 # put fd in t0 + mv s0, a1 # put fd in s0 + la t1, show_ends + lb s1, 0(t1) # store if we show ends into s1 + beqz a0, .LReadFileBlock # if we don't have an error, jump to reading li a0, 1 j .LCatFileRet .LReadFileBlock: - mv a0, t0 + mv a0, s0 la a1, cat_buf li a2, 4096 li a7, 63 ecall blez a0, .LCloseFD # if we haven't read any bytes, EOF, close FD - mv a2, a0 # move bytes read into bytes needed to write + beqz s1, .LNoEndingsWrite + mv a1, a0 + la a0, cat_buf + call _stdout_add_endings + j .LReadFileBlock + .LNoEndingsWrite: + mv a2, a0 # load the amount of bytes read in previous step into bytes needed to write li a0, 1 la a1, cat_buf li a7, 64 @@ -38,11 +53,15 @@ cat_file: j .LReadFileBlock .LCloseFD: - mv a0, t0 + mv a0, s0 li a7, 57 # close fd ecall .LCatFileRet: + ld s0, 0(sp) + ld ra, 8(sp) + ld s1, 16(sp) + addi sp, sp, 24 ret # Cat out stdin to stdout @@ -89,19 +108,91 @@ open_fd: 2: ret -# # Check if the given file exists without opening fd -# # @param[in] a0: ptr to null-terminated file str. -# # @param[out] a0: result code (0-exists 1-doesn't exist). -# file_exists: -# mv a1, a0 -# li a0, -100 # relative/absolute path -# li a2, 0 -# li a7, 48 -# ecall -# beqz a0, 1f -# li a0, 1 -# ret +# Print to stdout while adding "$" characters. +# @param[in] a0: ptr to null-terminated str. +# @param[in] a1: length of str. +# @param[out] a0: result code. +_stdout_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) -# 1: -# li a0, 0 -# ret + + 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 + + la s5, endings_buf + .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 + + li a0, 1 + la a1, endings_buf + mv a2, s3 + li a7, 64 + ecall + call _zero_endings_buf + li s3, 0 # reset endings_buf char counter + 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 + beqz s3, .LSkipEndingsPrint + li a0, 1 + la a1, endings_buf + mv a2, s3 + li a7, 64 + ecall + .LSkipEndingsPrint: + + li a0, 0 + + 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 + +_zero_endings_buf: + li t0, 0 + li t1, 4097 + + .LZeroEndingsBufByte: + la t2, endings_buf + add t2, t2, t0 + sb x0, 0(t2) + addi t0, t0, 1 + blt t0, t1, .LZeroEndingsBufByte + + ret diff --git a/src/main.S b/src/main.S index 3bb30b4..d0d05d3 100644 --- a/src/main.S +++ b/src/main.S @@ -1,5 +1,6 @@ .data argc: .space 8 +show_ends: .space 1 cat_prefix: .ascii "cat: " .equ CAT_PREFIX_LEN, . - cat_prefix @@ -11,13 +12,14 @@ help: .ascii "Usage: cat [OPTION]... [FILE]...\n" .ascii "Concatenate FILE(S) to standard output.\n\n" .ascii "With no FILE, or when FILE is -, read standard input.\n\n" - .ascii " --help display this help and exit\n" - .ascii " --version output version information and exit\n\n" + .ascii " -E, --show-ends display $ at end of each line\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.0.0\n" + .ascii "cat 1.1.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" @@ -26,10 +28,12 @@ version: .equ VERSION_LEN, . - version flag_help: .asciz "--help" -flag_version: .asciz "--version" +flag_version: .asciz "--version" +flag_show_ends: .asciz "--show-ends" +flag_E: .asciz "-E" .section .text -.globl _start +.globl _start, show_ends _start: .option push @@ -113,7 +117,7 @@ _check_flags: .LSkipDispHelp: - la a0, flag_version # help flag + la a0, flag_version # version flag li a1, 0 # no second flag call has_switch bnez a0, .LMainReturnErr @@ -126,6 +130,12 @@ _check_flags: j .LMainReturnOk .LSkipDispVersion: + la a0, flag_show_ends # show ends flag + la a1, flag_E # show ends -E flag + call has_switch + 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 + ld ra, 0(sp) addi sp, sp, 8 ret From cf66f92aae8675f9fe5045a0f50748b4c85bb526 Mon Sep 17 00:00:00 2001 From: lexzach Date: Sun, 13 Sep 2026 21:49:53 -0400 Subject: [PATCH 2/3] Add _is_known_flag func --- README.md | 4 +++- src/args.S | 8 ++++---- src/main.S | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 7211fc4..108deb2 100644 --- a/README.md +++ b/README.md @@ -16,4 +16,6 @@ Run the `make` command in the root of the repository. Options: -`-h, --help` - Show a help page. +`-E, --show-ends` - display $ at end of each line\ +`--help` - Show the help page.\ +`--version` - Show the version page. diff --git a/src/args.S b/src/args.S index 29d9527..94a12cd 100644 --- a/src/args.S +++ b/src/args.S @@ -2,7 +2,7 @@ sp_start: .space 8 .section .text -.globl save_sp, fetch_argc, fetch_argv, has_switch +.globl save_sp, fetch_argc, fetch_argv, has_switch, str_cmp save_sp: sd sp, sp_start, t0 # save the stack pointer @@ -125,13 +125,13 @@ has_switch: mv a0, s3 # load argv buff addr into a0 (string 0) mv a1, s0 # move switch ptr 1 addr into a1 (string 1) - call _str_cmp + call str_cmp bnez a1, .LSwitchFound # if the strings match, exit loop beqz s1, .LSkipSecondSwitch # if we have no second switch, skip the check mv a0, s3 # move argv buff addr into a0 (string 0) mv a1, s1 # move switch ptr 2 addr into a1 (string 1) - call _str_cmp + call str_cmp bnez a1, .LSwitchFound # if the strings match, exit loop .LSkipSecondSwitch: @@ -170,7 +170,7 @@ has_switch: # @param[in] a1: str buffer 2 # @param[out] a0: result code. # @param[out] a1: is equal (0-no, 1-yes). -_str_cmp: +str_cmp: li t2, 0 .LByteCmp: diff --git a/src/main.S b/src/main.S index d0d05d3..39d1b23 100644 --- a/src/main.S +++ b/src/main.S @@ -35,6 +35,7 @@ flag_E: .asciz "-E" .section .text .globl _start, show_ends +# main. _start: .option push .option norelax @@ -56,8 +57,15 @@ _start: call fetch_argv bnez a0, .LMainReturnErr # error if failed mv a0, a1 + addi sp, sp, -8 + sd a0, 0(sp) + call _is_known_flag + bnez a1, .LSkipArg # check if arg is flag + ld a0, 0(sp) + addi sp, sp, 8 call cat_file bnez a0, .LReturnFileErr # error if failed + .LSkipArg: addi s1, s1, 1 blt s1, s0, .LProcessFile j .LMainReturnOk @@ -98,7 +106,9 @@ _start: ecall # return 0 - +# Check for flags, branch or set buffers as needed. +# @param[in] a0: None. +# @param[out] a0: None. _check_flags: addi sp, sp, -8 sd ra, 0(sp) @@ -139,3 +149,41 @@ _check_flags: ld ra, 0(sp) addi sp, sp, 8 ret + +# Check if given argv is a flag to determine if cat_file should be used +# @param[in] a0: ptr to null-terminated argv string. +# @param[out] a0: result code. +# @param[out] a1: found flag (0- not a flag, 1- flag). +_is_known_flag: + addi sp, sp, -16 + sd ra, 8(sp) + sd s0, 0(sp) + + mv s0, a0 + + la a0, flag_help + mv a1, s0 + call str_cmp + bnez a1, .LFlagFound + + la a0, flag_version + mv a1, s0 + call str_cmp + bnez a1, .LFlagFound + + la a0, flag_show_ends + mv a1, s0 + call str_cmp + bnez a1, .LFlagFound + + la a0, flag_E + mv a1, s0 + call str_cmp + bnez a1, .LFlagFound + + li a1, 0 + .LFlagFound: + ld s0, 0(sp) + ld ra, 8(sp) + addi sp, sp, 16 + ret From 43635ab659a73eb2e77db1b999721e064afca378 Mon Sep 17 00:00:00 2001 From: lexzach Date: Sun, 13 Sep 2026 22:04:44 -0400 Subject: [PATCH 3/3] Finish -E --- src/file.S | 21 +++++++++++++++++++-- src/main.S | 5 ++++- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/file.S b/src/file.S index eabfa18..b8da07d 100644 --- a/src/file.S +++ b/src/file.S @@ -66,8 +66,15 @@ cat_file: # Cat out stdin to stdout # @param[in] a0: Not used. -# @param[out] a0: result code. +# @param[out] a0: Not used. cat_stdin: + addi sp, sp, -16 + sd ra, 0(sp) + sd s0, 8(sp) + + la t1, show_ends + lb s0, 0(t1) # store if we show ends into s1 + .LReadStdinBlock: li a0, 0 la a1, cat_buf @@ -75,8 +82,15 @@ cat_stdin: li a7, 63 ecall blez a0, .LCatStdinRet # if we haven't read any bytes, EOF, close FD - mv a2, a0 # move bytes read into bytes needed to write + beqz s0, .LNoEndingsStdinWrite + mv a1, a0 + la a0, cat_buf + call _stdout_add_endings + j .LReadStdinBlock + + .LNoEndingsStdinWrite: + mv a2, a0 # move bytes read into bytes needed to write li a0, 1 la a1, cat_buf li a7, 64 @@ -84,6 +98,9 @@ cat_stdin: j .LReadStdinBlock .LCatStdinRet: + ld ra, 0(sp) + ld s0, 8(sp) + addi sp, sp, 16 ret # Open fd diff --git a/src/main.S b/src/main.S index 39d1b23..3540ebb 100644 --- a/src/main.S +++ b/src/main.S @@ -46,11 +46,12 @@ _start: call fetch_argc mv s0, a1 # store argc li s1, 1 + li s2, 0 # processed files call _check_flags li t0, 1 - ble s0, t0, .LProcessStdin # use stdin if no commands + ble s0, t0, .LProcessStdin # use stdin if no commands, although this is checked later, if no flags are used, we can go ahead and jump .LProcessFile: mv a0, s1 @@ -64,10 +65,12 @@ _start: ld a0, 0(sp) addi sp, sp, 8 call cat_file + addi s2, s2, 1 bnez a0, .LReturnFileErr # error if failed .LSkipArg: addi s1, s1, 1 blt s1, s0, .LProcessFile + beqz s2, .LProcessStdin # if we reach the end of the file queue, and no files have actually been processed, that means they were all flags, switch to stdin j .LMainReturnOk .LProcessStdin: