aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authormithe24 <mithe24@student.sdu.dk>2025-10-08 13:35:41 +0200
committermithe24 <mithe24@student.sdu.dk>2025-10-29 13:49:57 +0100
commitfa03afa46984fbf6eacbd5540e4e318d161cbbe0 (patch)
treecd5e2f567a407ee459d94fcfd8ed3088fce3bc96 /src
parent07816c4c9c257c327bb93a72ed3a9b680816136c (diff)
downloadsorter-fa03afa46984fbf6eacbd5540e4e318d161cbbe0.tar.gz
sorter-fa03afa46984fbf6eacbd5540e4e318d161cbbe0.zip
chore(snippets): move snippetss to src/
Diffstat (limited to 'src')
-rw-r--r--src/lib/allocate.s21
-rw-r--r--src/lib/fileHandling.s16
-rw-r--r--src/lib/parsing.s76
-rw-r--r--src/lib/utils.s214
4 files changed, 327 insertions, 0 deletions
diff --git a/src/lib/allocate.s b/src/lib/allocate.s
new file mode 100644
index 0000000..fdd9bf8
--- /dev/null
+++ b/src/lib/allocate.s
@@ -0,0 +1,21 @@
+# void *allocate(int n)
+#
+# A naive memory allocator that simply retrieves some new space from the OS.
+# It is not possible to deallocate the memory again.
+.globl allocate
+.type allocate, @function
+allocate:
+ push %rdi
+ # 1. Find the current end of the data segment.
+ movq $12, %rax # brk
+ xorq %rdi, %rdi # 0 means we retrieve the current end.
+ syscall
+ # 2. Add the amount of memory we want to allocate.
+ pop %rdi # the argument
+ push %rax # current end, which is where the allocated memory will start
+ addq %rax, %rdi # compute the new end
+ movq $12, %rax # brk
+ syscall
+ pop %rax # the old end, which is the address of our allocated memory
+ ret
+
diff --git a/src/lib/fileHandling.s b/src/lib/fileHandling.s
new file mode 100644
index 0000000..17a9428
--- /dev/null
+++ b/src/lib/fileHandling.s
@@ -0,0 +1,16 @@
+# int getFileSize(int fd)
+#
+# Returns the size (in bytes) of the file indicated by the file descriptor.
+.section .data
+.Lstat: .space 144 # size of the fstat struct
+.section .text
+.globl getFileSize
+.type getFileSize, @function
+getFileSize:
+ movq $5, %rax # fstat
+ # rdi already contains the fd
+ movq $.Lstat, %rsi # buffer to write fstat data into
+ syscall
+ movq $.Lstat, %rax
+ movq 48(%rax), %rax # position of size in the struct
+ ret
diff --git a/src/lib/parsing.s b/src/lib/parsing.s
new file mode 100644
index 0000000..e039be6
--- /dev/null
+++ b/src/lib/parsing.s
@@ -0,0 +1,76 @@
+# int getLineCount(const char *data, int size)
+#
+# Returns the number of '\n' characters in the memory pointed to.
+# 'data': the address of the first character to look at.
+# 'size': the length of the memory area to scan through.
+.globl getLineCount
+.type getLinecount, @function
+getLineCount:
+ # rdi: 'data'
+ # rsi: 'size'
+ addq %rdi, %rsi # make rsi the past-the-end pointer
+ xorq %rax, %rax # count = 0
+.LgetLineCount_loop:
+ cmpq %rdi, %rsi
+ je .LgetLineCount_end # if rdi == rsi: we are done
+ movb (%rdi), %dl # load the next byte
+ addq $1, %rdi
+ cmpb $0xA, %dl # is it a newline char?
+ jne .LgetLineCount_loop # if not, continue in the buffer
+ addq $1, %rax # completed a number
+ jmp .LgetLineCount_loop
+.LgetLineCount_end:
+ ret
+
+
+# void parseData(const char *data, int size, int *result)
+#
+# Converts the ASCII representation of the coordinates into pairs of numbers.
+# 'data': the address of the first character in the ASCII representation.
+# 'size': the length of the ASCII representation.
+# 'result': the address of a piece of memory big enough to hold the
+# coordinates. If there are n coordinates in the input, the 'result'
+# memory will be an array of 2n 8-byte integers, with alternating x and y
+# coordinates.
+#
+# Note, this functions only expects unsigned ints in the input and does not
+# perform any validity checks at all.
+.globl parseData
+.type parseData, @function
+parseData:
+ addq %rdi, %rsi # make rsi the past-the-end pointer
+ push %rsi # and store it as the top element on the stack
+.LparseData_coordinateLoop:
+ cmpq (%rsp), %rdi
+ je .LparseData_coordinateLoop_end
+ movq $9, %rsi # '\t'
+ call parseNumber # increases rdi to point past-the-end of the number
+ movq %rax, (%rdx) # store the number
+ addq $8, %rdx # point to the next place for a number
+ movq $10, %rsi # '\n'
+ call parseNumber # increases rdi to point past-the-end of the number
+ movq %rax, (%rdx) # store the number
+ addq $8, %rdx # point to the next place for a number
+ jmp .LparseData_coordinateLoop
+.LparseData_coordinateLoop_end:
+ addq $8, %rsp
+ ret
+
+# int parseNumber(const char *&data, const char *end)
+parseNumber:
+ xorq %rax, %rax # result
+.LparseNumber_loop:
+ xorq %r10, %r10 # the next digit
+ movb (%rdi), %r10b # read character
+ addq $1, %rdi # ++data
+ cmpq %rsi, %r10 # done with this number?
+ je .LparseNumber_loop_end
+ # here we assume that the character is actually a digit
+ # add this digit to the current number
+ subq $48, %r10 # convert the ASCII code to the digit it represents
+ imul $10, %rax # 'make room' for the new digit
+ addq %r10, %rax # and add the new digit
+ jmp .LparseNumber_loop
+.LparseNumber_loop_end:
+ # we now have a number in rax
+ ret
diff --git a/src/lib/utils.s b/src/lib/utils.s
new file mode 100644
index 0000000..f63d3f9
--- /dev/null
+++ b/src/lib/utils.s
@@ -0,0 +1,214 @@
+# These registers are not neccesarily perserved after a function call:
+# RAX RCX RDX RSI RDI R8 R9 R10 R11
+# Which means that these must be perserved:
+# RBX RBP R12 R13 R14 R15
+# The order of arguments passed to functions is as follows:
+# RDI RSI RDX RCX R8 R9
+# The order of the return registers is as follows:
+# RAX RDX
+
+# Buffer for file data
+.section .data
+myBuffer:
+ .space 1
+
+.section .text
+
+
+# Print RDI as an unsigned integer following by a newline.
+# Note: the function does not follow the ordinary calling convention,
+# but restores all registers.
+.type printNum, @function
+.globl printNum
+printNum:
+ push %rbp
+ movq %rsp, %rbp
+
+ # save
+ push %rax
+ push %rdi
+ push %rsi
+ push %rdx
+ push %rcx
+ push %r8
+ push %r9
+
+ movq %rdi, %rax # arg
+
+ movq $1, %r9 # we always print "\n"
+ push $10 # '\n'
+.LprintNum_convertLoop:
+ movq $0, %rdx
+ movq $10, %rcx
+ idivq %rcx
+ addq $48, %rdx # '0' is 48
+ push %rdx
+ addq $1, %r9
+ cmpq $0, %rax
+ jne .LprintNum_convertLoop
+.LprintNum_printLoop:
+ movq $1, %rax # sys_write
+ movq $1, %rdi # stdout
+ movq %rsp, %rsi # buf
+ movq $1, %rdx # len
+ syscall
+ addq $8, %rsp
+ addq $-1, %r9
+ jne .LprintNum_printLoop
+
+ # restore
+ pop %r9
+ pop %r8
+ pop %rcx
+ pop %rdx
+ pop %rsi
+ pop %rdi
+ pop %rax
+
+ movq %rbp, %rsp
+ pop %rbp
+ ret
+
+.globl intFromString # int intFromString(char *str)
+# Pre: str != 0
+# Pre: all characters in the string are one of 0123456789.
+.type intFromString, @function
+intFromString:
+ xorq %rax, %rax
+.LintFromString_loop:
+ movzx (%rdi), %rsi # Move a single character/byte %rbx and zero-extend it.
+ cmpq $0, %rsi # A string ends with a 0-byte.
+ je .LintFromString_done
+ movq $10, %rcx # Shift the number 1 decimal place to the left.
+ mulq %rcx
+ subq $48, %rsi # Convert from ASCII character to number. ASCII '0' has value 48. '1' is 49, etc.
+ addq %rsi, %rax # Add the number.
+ addq $1, %rdi
+ jmp .LintFromString_loop
+.LintFromString_done:
+ ret
+
+
+# The pointer to the string is argument RDI
+# It is required that the string ends with the zero byte
+.type printString, @function
+.globl printString
+printString:
+ push %r12 # Save R12
+
+ movq %rdi, %r12 # Save copy of string address
+ call stringLength # Get length of string in RAX
+
+ movq $1, %rdi # Select to write to stdout
+ movq %r12, %rsi # Use pointer at start of string
+ movq %rax, %rdx # Put string size in rdx
+ movq $1, %rax # Select sys_write
+ syscall # Call the function
+
+ pop %r12 # Restore R12
+ ret
+
+
+# Requires that the file is already open, and file descriptor is passed as first
+# argument.
+.type printFile, @function
+.globl printFile
+printFile:
+ # The callee-saved registers
+ push %rbx
+ push %rbp
+ push %r12
+ push %r13
+ push %r14
+ push %r15
+
+ movq %rdi, %r10 # Save file descriptor in r10
+
+loop:
+ movq %r10, %rdi # Select the file discriptor
+ movq $0, %rax # Select read syscall
+ movq $1, %rdx # Size to read from file
+ movq $myBuffer, %rsi # Select the buffer to store input
+ syscall # Read from the file
+
+ cmp $0, %rax # If rax is zero, end of file
+ je endPrintFile # Jump to end of program
+
+ movq $1, %rdx # Put string size in RDX
+ movq $myBuffer, %rsi # Use pointer at start of string
+ movq $1, %rdi # Select to write to stdout
+ movq $1, %rax # Select sys_write
+ syscall # Call the function
+
+ jmp loop
+
+endPrintFile:
+ # The callee-saved registers
+ pop %r15
+ pop %r14
+ pop %r13
+ pop %r12
+ pop %rbp
+ pop %rbx
+
+ ret
+
+
+.type printStdin, @function
+.globl printStdin
+printStdin:
+ # The callee-saved registers
+ push %rbx
+ push %rbp
+ push %r12
+ push %r13
+ push %r14
+ push %r15
+
+printStdinLoop:
+ movq $0, %rax # Select read syscall
+ movq $1, %rdx # Size to read
+ movq $0, %rdi # Select stdin
+ movq $myBuffer, %rsi # Select the buffer to store input
+ syscall
+
+ cmp $0, %rax # Check if there was something to be read.
+ je endPrintStdin # If not, jump to end
+
+ movq $1, %rdx # Put string size in rdx
+ movq $myBuffer, %rsi # Use pointer at start of string
+ movq $1, %rdi # Select to write to stdout
+ movq $1, %rax # Select sys_write
+ syscall # Call the function
+
+ jmp printStdinLoop
+
+
+endPrintStdin:
+ # The callee-saved registers
+ pop %r15
+ pop %r14
+ pop %r13
+ pop %r12
+ pop %rbp
+ pop %rbx
+
+ ret
+
+.type stringLength, @function
+.globl stringLength
+stringLength:
+ movq $0, %rcx # CL will keep the current byte
+ movq $0, %rax # The length counter
+
+loopStringLength:
+ movb (%rdi), %cl # Move the lower byte of the string into CL
+ cmp $0, %cl # Compare 0 with the lower byte of register C
+ je endStringLength # Go the end
+ addq $1, %rax # Add one to length
+ addq $1, %rdi # Go one forward in address
+ jmp loopStringLength# Go to start of loop
+
+endStringLength:
+ ret
+