aboutsummaryrefslogtreecommitdiff
path: root/src/utils.s
blob: f63d3f9fac5a56db1c17a5bdc4787e674ad30878 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
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