forked from kanaka/mal
-
Notifications
You must be signed in to change notification settings - Fork 0
/
step0_repl.asm
326 lines (257 loc) · 9.11 KB
/
step0_repl.asm
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
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
;; nasm -felf64 mal.asm && ld mal.o && ./a.out
;; Calling convention: Address of input is in RSI
;; Address of return value is in RAX
;;
;; Data structures
;; Memory management is done by having two fixed-size datatypes,
;; Cons and Array.
;;
;; Both Cons and Array have the following in common:
;; a type field at the start, a reference count, followed by data
;; [ type (8) | (8) | refs (16) | data ]
;;
STRUC Cons
.typecar: RESB 1 ; Type information for car (8 bit)
.typecdr: RESB 1 ; Type information for cdr (8 bits)
.refcount: RESW 1 ; Number of references to this Cons (16 bit)
.car: RESQ 1 ; First value (64 bit)
.cdr: RESQ 1 ; Second value (64 bit)
.size: ; Total size of struc
ENDSTRUC
%define array_chunk_len 32 ; Number of 64-bit values which can be stored in a single chunk
STRUC Array
.type: RESB 1 ; Type information (8 bits)
.control: RESB 1 ; Control data (8 bits)
.refcount: RESW 1 ; Number of references to this Array (16 bit)
.length: RESD 1 ; Number of elements in array (32 bit)
.next RESQ 1 ; Pointer to the next chunk (64 bit)
.data: RESQ array_chunk_len ; Data storage
.size: ; Total size of struc
ENDSTRUC
;; Type information
%define type_char 1 ; Character type
%define type_integer 2 ; Integer type
%define type_float 3 ; Floating point number
%define type_array 128 ; Last bit tests if array or cons
global _start
section .data
str: ISTRUC Array
AT Array.type, db type_char + type_array
AT Array.length, dd 6
AT Array.data, db 'hello',10
IEND
prompt_string: db 10,"user> " ; The string to print at the prompt
.len: equ $ - prompt_string
error_msg_print_string: db "Error in print string",10
.len: equ $ - error_msg_print_string
section .bss
section .text
;; ------------------------------------------
;; Array alloc_array()
;;
;; Returns the address of an Array object in RAX
alloc_array:
mov rax, str
ret
;; -------------------------------------------
;; Prints a raw string to stdout
;; String address in rsi, string length in rdx
print_rawstring:
push rax
push rdi
; write(1, string, length)
mov rax, 1 ; system call 1 is write
mov rdi, 1 ; file handle 1 is stdout
syscall
pop rdi
pop rax
ret
;; ------------------------------------------
;; void print_string(char array)
;; Address of the char Array should be in RSI
print_string:
; Push registers we're going to use
push rax
push rdi
push rdx
push rsi
; Check that we have a char array
mov al, [rsi]
cmp al, type_char + type_array
jne .error
; write(1, string, length)
mov edx, [rsi + Array.length] ; number of bytes
add rsi, Array.data ; address of raw string to output
call print_rawstring
; Restore registers
pop rsi
pop rdx
pop rdi
pop rax
ret
.error:
; An error occurred
mov rdx, error_msg_print_string.len ; number of bytes
mov rsi, error_msg_print_string ; address of raw string to output
call print_rawstring
; exit
jmp quit_error
;; ------------------------------------------
;; String itostring(Integer number)
;;
;; Converts an integer to a string (array of chars)
;;
;; Input in RAX
;; Return string address in RAX
itostring:
; Save registers to restore afterwards
push rcx
push rdx
push rsi
push rdi
mov rcx, 0 ; counter of how many bytes we need to print in the end
.divideLoop:
inc rcx ; count each byte to print - number of characters
xor rdx, rdx
mov rsi, 10
idiv rsi ; divide rax by rsi
add rdx, 48 ; convert rdx to it's ascii representation - rdx holds the remainder after a divide instruction
; Character is now in DL
dec rsp
mov BYTE [rsp], dl ; Put onto stack
cmp rax, 0 ; can the integer be divided anymore?
jnz .divideLoop ; jump if not zero to the label divideLoop
; Get an Array object to put the string into
call alloc_array ; Address in RAX
; put length into string
mov [rax + Array.length], ecx
; copy data from stack into string
; Note: Currently this does not handle long strings
mov rdi, rax
add rdi, Array.data ; Address where raw string will go
.copyLoop:
mov BYTE dl, [rsp] ; Copy one byte at a time. Could be more efficient
mov [rdi], BYTE dl
inc rsp
inc rdi
dec rcx
cmp rcx, 0
jnz .copyLoop
; Restore registers
pop rdi
pop rsi
pop rdx
pop rcx
ret
;; ----------------------------
;; int stringtoi(String)
;;
;; Convert a string (char array) to an integer
;;
;; Address of input string is in RSI
;; Output integer in RAX
stringtoi:
ret
;------------------------------------------
; void exit()
; Exit program and restore resources
quit:
mov eax, 60 ; system call 60 is exit
xor rdi, rdi ; exit code 0
syscall ; invoke operating system to exit
quit_error:
mov eax, 60 ; system call 60 is exit
mov rdi, 1 ; exit code 1
syscall
;; Takes a string as input and processes it into a form
read:
mov rax, rsi ; Return the input
ret
;; Evaluates a form
eval:
mov rax, rsi ; Return the input
ret
;; Prints the result
print:
mov rax, rsi ; Return the input
ret
;; Read-Eval-Print in sequence
rep_seq:
call read
mov rsi, rax ; Output of read into input of eval
call eval
mov rsi, rax ; Output of eval into input of print
call print
mov rsi, rax ; Return value
ret
;; Read a line from stdin
;; Gets a new string array, fills it until a newline or EOF is reached
;; Returns pointer to string in RAX
read_line:
; Get an array to put the string into
; Address in rax
call alloc_array
; Mark it as a character array (string)
mov BYTE [rax + Array.type], type_char + type_array
push rax ; Save pointer to string
; Read character by character until either newline or end of input
mov ebx, 0 ; Count how many characters read
mov rsi, rax
add rsi, Array.data ; Point to the data
.readLoop:
mov rax, 0 ; sys_read
mov rdi, 0 ; stdin
mov rdx, 1 ; count
syscall
; Characters read in RAX
cmp rax, 0 ; end loop if read <= 0
jle .readLoopEnd
mov cl, BYTE [rsi]
cmp cl, 10 ; End if we read a newline
je .readLoopEnd
cmp cl, 8 ; Backspace?
je .handleBackspace
cmp cl, 31 ; Below space
jle .readLoop ; Ignore, keep going
cmp cl, 127 ; DEL or above
jge .readLoop ; Ignore, keep going
inc ebx
inc rsi ; Move to next point in the array
jmp .readLoop ; Get another character
.handleBackspace:
; Check if we've read any characters
cmp ebx, 0
je .readLoop ; If not, carry on the loop
; Characters have been read. Remove one
dec ebx
dec rsi
jmp .readLoop
.readLoopEnd:
pop rax ; Restore pointer to string
mov DWORD [rax + Array.length], ebx ; Set string length
ret
_start:
; -----------------------------
; Main loop
.mainLoop:
; print the prompt
mov rdx, prompt_string.len ; number of bytes
mov rsi, prompt_string ; address of raw string to output
call print_rawstring
call read_line
; Check if we have a zero-length string
cmp DWORD [rax+Array.length], 0
je .mainLoopEnd
mov rsi, rax ; Put into input of print_string
call print_string
jmp .mainLoop
.mainLoopEnd:
;mov rdx, 1
;mov rsi,
;call print_rawstring
;inc rsp
;mov rax, 1223
;call itostring
;mov rsi, rax
;call print_string
jmp quit