# This Source Code Form is subject to the terms of the Mozilla Public License, # v. 2.0. If a copy of the MPL was not distributed with this file, You can # obtain one at https://mozilla.org/MPL/2.0/. .global _is_alpha, _is_digit, _is_alnum, _is_upper, _is_lower .global _write_out, _read_file, _write_error, _put_char, _printi .global _get, _memcmp, _memchr, _memmem, _memcpy .global _divide_by_zero_error, _exit, _mmap .global _strings_index, _string_equal .section .rodata .equ SYS_READ, 63 .equ SYS_WRITE, 64 .equ SYS_EXIT, 93 .equ SYS_MMAP2, 222 .equ STDIN, 0 .equ STDOUT, 1 .equ STDERR, 2 .equ PROT_READ, 0x1 .equ PROT_WRITE, 0x2 .equ MAP_PRIVATE, 0x02 .equ MAP_ANONYMOUS, 0x20 new_line: .ascii "\n" .section .text # Write the current token to stderr. Ends the output with a newline. # # a0 - String pointer. # a1 - String length. .type _write_error, @function _write_error: mv t0, a0 mv t1, a1 li a0, STDERR mv a1, t0 mv a2, t1 li a7, SYS_WRITE ecall li a0, STDERR la a1, new_line li a2, 1 li a7, SYS_WRITE ecall ret # a0 - First pointer. # a1 - Second pointer. # a2 - The length to compare. # # Returns 0 in a0 if memory regions are equal. .type _memcmp, @function _memcmp: mv t0, a0 li a0, 0 .Lmemcmp_loop: beqz a2, .Lmemcmp_end lbu t1, (t0) lbu t2, (a1) sub a0, t1, t2 bnez a0, .Lmemcmp_end addi t0, t0, 1 addi a1, a1, 1 addi a2, a2, -1 j .Lmemcmp_loop .Lmemcmp_end: ret # Detects if a0 is an uppercase character. Sets a0 to 1 if so, otherwise to 0. .type _is_upper, @function _is_upper: li t0, 'A' - 1 sltu t1, t0, a0 # t1 = a0 >= 'A' sltiu t2, a0, 'Z' + 1 # t2 = a0 <= 'Z' and a0, t1, t2 # t1 = a0 >= 'A' & a0 <= 'Z' ret # Detects if a0 is an lowercase character. Sets a0 to 1 if so, otherwise to 0. .type _is_lower, @function _is_lower: li t0, 'a' - 1 sltu t2, t0, a0 # t2 = a0 >= 'a' sltiu t3, a0, 'z' + 1 # t3 = a0 <= 'z' and a0, t2, t3 # t2 = a0 >= 'a' & a0 <= 'z' ret # Detects if the passed character is a 7-bit alpha character or an underscore. # The character is passed in a0. # Sets a0 to 1 if the character is an alpha character or underscore, sets it to 0 otherwise. .type _is_alpha, @function _is_alpha: # Prologue. addi sp, sp, -16 sw ra, 12(sp) sw s0, 8(sp) addi s0, sp, 16 sw a0, 4(sp) call _is_upper sw a0, 0(sp) lw a0, 4(sp) call _is_lower lw t0, 4(sp) xori t1, t0, '_' seqz t1, t1 lw t0, 0(sp) or a0, a0, t0 or a0, a0, t1 # Epilogue. lw ra, 12(sp) lw s0, 8(sp) addi sp, sp, 16 ret # Detects whether the passed character is a digit # (a value between 0 and 9). # # Parameters: # a0 - Exemined value. # # Sets a0 to 1 if it is a digit, to 0 otherwise. .type _is_digit, @function _is_digit: li t0, '0' - 1 sltu t1, t0, a0 # t1 = a0 >= '0' sltiu t2, a0, '9' + 1 # t2 = a0 <= '9' and a0, t1, t2 ret .type _is_alnum, @function _is_alnum: # Prologue. addi sp, sp, -16 sw ra, 12(sp) sw s0, 8(sp) addi s0, sp, 16 sw a0, 4(sp) call _is_alpha sw a0, 0(sp) lw a0, 4(sp) call _is_digit lw a1, 0(sp) or a0, a0, a1 # Epilogue. lw ra, 12(sp) lw s0, 8(sp) addi sp, sp, 16 ret .type _write_out, @function _write_out: # Prologue. addi sp, sp, -8 sw ra, 4(sp) sw s0, 0(sp) addi s0, sp, 8 mv a2, a1 mv a1, a0 li a0, STDOUT li a7, SYS_WRITE ecall # Epilogue. lw ra, 4(sp) lw s0, 0(sp) addi sp, sp, 8 ret # Reads standard input into a buffer. # a0 - Buffer pointer. # a1 - Buffer size. # # Sets s1 to the buffer passed in a0. # # Returns the amount of bytes written in a0. .type _read_file, @function _read_file: # Prologue. addi sp, sp, -8 sw ra, 4(sp) sw s0, 0(sp) addi s0, sp, 8 mv s1, a0 li a0, STDIN mv a2, a1 mv a1, s1 li a7, SYS_READ ecall # Epilogue. lw ra, 4(sp) lw s0, 0(sp) addi sp, sp, 8 ret # Terminates the program. a0 contains the return code. # # Parameters: # a0 - Status code. .type _exit, @function _exit: li a7, SYS_EXIT ecall # ret .type _divide_by_zero_error, @function _divide_by_zero_error: addi a7, zero, 172 # getpid ecall addi a1, zero, 8 # SIGFPE addi a7, zero, 129 # kill ecall ret # a0 - Whole number. # t1 - Constant 10. # a1 - Local buffer. # t2 - Current character. # t3 - Whether the number is negative. .type printi, @function _printi: addi sp, sp, -16 sw s0, 0(sp) sw ra, 4(sp) addi s0, sp, 16 addi t1, zero, 10 addi a1, s0, -1 addi t3, zero, 0 bge a0, zero, .digit10 addi t3, zero, 1 sub a0, zero, a0 .digit10: rem t2, a0, t1 addi t2, t2, '0' sb t2, 0(a1) div a0, a0, t1 addi a1, a1, -1 bne zero, a0, .digit10 beq zero, t3, .write_call addi t2, zero, '-' sb t2, 0(a1) addi a1, a1, -1 .write_call: addi a0, zero, 1 addi a1, a1, 1 sub a2, s0, a1 addi a7, zero, 64 # write ecall lw s0, 0(sp) lw ra, 4(sp) addi sp, sp, 16 ret # Writes a character from a0 into the standard output. .type _put_char, @function _put_char: # Prologue addi sp, sp, -16 sw ra, 12(sp) sw s0, 8(sp) addi s0, sp, 16 sb a0, 4(sp) li a0, STDOUT addi a1, sp, 4 li a2, 1 li a7, SYS_WRITE ecall # Epilogue. lw ra, 12(sp) lw s0, 8(sp) add sp, sp, 16 ret # a0 - Pointer to an array to get the first element. # # Dereferences a pointer and returns what is on the address in a0. .type _get, @function _get: lw a0, (a0) ret # Searches for the occurences of a character in the given memory block. # # Parameters: # a0 - Memory block. # a1 - Needle. # a2 - Memory size. # # Sets a0 to the pointer to the found character or to null if the character # doesn't occur in the memory block. .type _memchr, @function _memchr: .Lmemchr_loop: beqz a2, .Lmemchr_nil # Exit if the length is 0. lbu t0, (a0) # Load the character from the memory block. beq t0, a1, .Lmemchr_end # Exit if the character was found. # Otherwise, continue with the next character. addi a0, a0, 1 addi a2, a2, -1 j .Lmemchr_loop .Lmemchr_nil: li a0, 0 .Lmemchr_end: ret # Locates a substring. # # Parameters: # a0 - Haystack. # a1 - Haystack size. # a2 - Needle. # a3 - Needle size. # # Sets a0 to the pointer to the beginning of the substring in memory or to 0 # if the substring doesn't occur in the block. .type _memmem, @function _memmem: # Prologue. addi sp, sp, -24 sw ra, 20(sp) sw s0, 16(sp) addi s0, sp, 24 # Save preserved registers. They are used to keep arguments. sw s1, 12(sp) sw s2, 8(sp) sw s3, 4(sp) sw s4, 0(sp) mv s1, a0 mv s2, a1 mv s3, a2 mv s4, a3 .Lmemmem_loop: blt s2, s3, .Lmemmem_nil # Exit if the needle length is greater than memory. mv a0, s1 mv a1, s3 mv a2, s4 call _memcmp mv t0, a0 # memcmp result. mv a0, s1 # Memory pointer for the case the substring was found. beqz t0, .Lmemmem_end addi s1, s1, 1 add s2, s2, -1 j .Lmemmem_loop .Lmemmem_nil: li a0, 0 .Lmemmem_end: # Restore the preserved registers. lw s1, 12(sp) lw s2, 8(sp) lw s3, 4(sp) lw s4, 0(sp) # Epilogue. lw ra, 20(sp) lw s0, 16(sp) add sp, sp, 24 ret # Copies memory. # # Parameters: # a0 - Destination. # a1 - Source. # a2 - Size. # # Preserves a0. .type _memcpy, @function _memcpy: mv t0, a0 .Lmemcpy_loop: beqz a2, .Lmemcpy_end lbu t1, (a1) sb t1, (a0) addi a0, a0, 1 addi a1, a1, 1 addi a2, a2, -1 j .Lmemcpy_loop .Lmemcpy_end: mv a0, t0 ret # Searches for a string in a string array. # # Parameters: # a0 - Number of elements in the string array. # a1 - String array. # a2 - Needle length. # a3 - Needle. # # Sets a0 to the 1-based index of the needle in the haystack or to 0 if the # element could not be found. .type _strings_index, @function _strings_index: # Prologue. addi sp, sp, -32 sw ra, 28(sp) sw s0, 24(sp) addi s0, sp, 32 sw s1, 20(sp) mv s1, a0 sw s2, 16(sp) mv s2, a1 sw s3, 12(sp) mv s3, a2 sw s4, 8(sp) mv s4, a3 sw s5, 4(sp) li s5, 0 # Index counter. .Lstrings_index_loop: addi s5, s5, 1 beqz s1, .Lstrings_index_missing lw a2, (s2) # Read the length of the current element in the haystack. bne a2, s3, .Lstrings_index_next # Lengths don't match, skip the iteration. addi a0, s2, 4 mv a1, s4 call _memcmp beqz a0, .Lstrings_index_end .Lstrings_index_next: # Advance the pointer, reduce the length. lw a2, (s2) addi s2, s2, 4 add s2, s2, a2 addi s1, s1, -1 j .Lstrings_index_loop .Lstrings_index_missing: li s5, 0 .Lstrings_index_end: mv a0, s5 lw s1, 20(sp) lw s2, 16(sp) lw s3, 12(sp) lw s4, 8(sp) lw s5, 4(sp) # Epilogue. lw ra, 28(sp) lw s0, 24(sp) add sp, sp, 32 ret # Compares two strings for equality. # # Parameters: # a0 - Length of the first string. # a1 - Pointer to the first string. # a2 - Length of the second string. # a3 - Pointer to the second string. # # Sets a0 to 1 if the string are equal, to 0 if not. .type _string_equal, @function _string_equal: # Prologue. addi sp, sp, -32 sw ra, 28(sp) sw s0, 24(sp) addi s0, sp, 32 # Compare string lengths. bne a0, a2, .Lstring_equal_not_found # If lengths match, compare the content. mv a0, a1 mv a1, a3 # a2 is already set to the length. call _memcmp bnez a0, .Lstring_equal_not_found li a0, 1 j .Lstring_equal_end .Lstring_equal_not_found: mv a0, zero .Lstring_equal_end: # Epilogue. lw ra, 28(sp) lw s0, 24(sp) addi sp, sp, 32 ret # Sets a0 to the mapping address. .type _mmap, @function _mmap: li a0, 0 # Address at which to create the mapping. li a1, 4096 # The length of the mapping. li a2, PROT_READ | PROT_WRITE # Protection flags. li a3, MAP_ANONYMOUS | MAP_PRIVATE # The mapping is not backed by a file. li a4, -1 # File descriptor. li a5, 0 # Page offset. li a7, SYS_MMAP2 ecall ret