-
Notifications
You must be signed in to change notification settings - Fork 0
/
calculator.asm
executable file
·422 lines (263 loc) · 7.03 KB
/
calculator.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
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
; Data
section .data
extern getText
extern printText
extern printChar
extern printEndl
extern stringify
welcomeMsg db "Welcome to Nik's assembly calculator! Enter an expression to evaluate. Ctrl + C to quit. Supported operations are + - / * % ^."
welcomeLen db 126
operatorMsg db "Invalid operator. You can use + - / * % ^. Please try again."
operatorLen db 58
rightMsg db "Please enter a right-hand number."
rightLen db 33
div0Msg db "Error: cannot divide by zero."
div0Len db 29
powNegMsg db "Error: negative powers are not supported."
powNegLen db 41
pow00Msg db "--> Undefined, or 1"
pow00Len db 19
resultMsg db "--> "
resultLen db 4
lastResult dq 0
; BSS
section .bss
input resb 0x80
output resb 0x80
left resq 1
right resq 1
op resb 1 ; 0 = error, otherwise ASCII value of + - * / %
location resb 1
; Code
section .text
global _start
_start:
mov rdi, welcomeMsg
movzx rsi, byte [welcomeLen]
call printText
calcNext:
call printEndl
call printEndl
mov rdi, input
mov rsi, 0x80
call getText ; get expression from user
mov byte [location], 0
call getNum
cmp dil, 0 ; success return val from getNum
jne leftSuccess
mov rax, qword [lastResult] ; use last result (or default 0) as number if not successful
leftSuccess:
mov qword [left], rax ; store left hand number
call skipWhitespace ; skip over whitespace
call getOperator
cmp rax, 0xA
jb calcNext ; if invalid operator input (0), retry input
je skipRight ; if operator = \n, skip right hand collection
call skipWhitespace
call getNum
cmp dil, 0
jne rightSuccess
mov rdi, rightMsg ; ask user to input right-hand number
movzx rsi, byte [rightLen]
call printText
jmp calcNext ; retry expression
rightSuccess:
mov qword [right], rax ; store right hand number
skipRight:
call validateInput
cmp rax, 0
jne calcNext
call calculate ; calculate with left/right/op
mov qword [lastResult], rax
mov rdi, resultMsg
movzx rsi, byte [resultLen]
call printText
mov rdi, output
mov rsi, qword [lastResult]
call stringify ; stringify result
mov rdi, output
mov rsi, rax ; return value from stringify; length of string
call printText
jmp calcNext
getNum: ; custom calling convention! num returned in rax, success (bool) returned in dil
mov r10b, 0 ; "is negative" flag
mov r11b, byte [location] ; store location in case
getNumStart:
movzx rcx, byte [location] ; get current location --> rcx
movzx r9, byte [input + rcx] ; get byte at location
cmp r9b, '-' ; check if negative number
jne notNegative
inc byte [location]
cmp r10b, 0 ; check if set to negative
je makeNegative
mov r10b, 0 ; if set to negative, make not set
jmp getNumStart
makeNegative: ; if not set to negative, set to negative
mov r10b, 1
jmp getNumStart
notNegative:
cmp r9b, '0' ; check byte is '0' ≤ x ≤ '9'
jb noNum
cmp r9b, '9'
ja noNum
sub r9, '0' ; convert ASCII -> int
mov r8, r9
loopNum:
inc rcx
movzx r9, byte [input + rcx] ; get next byte
cmp r9b, '0' ; check character is a number; finish if not
jb doneNum
cmp r9b, '9'
ja doneNum
mov rax, 10 ; multiply current value by 10 to account for next part
mul r8
mov r8, rax ; move product back into r8
sub r9, '0' ; convert ASCII -> int
add r8, r9 ; add old to new
jmp loopNum ; restart loop
noNum:
mov byte [location], r11b ; reset location to what it was when entering function
mov dil, 0 ; success = false
jmp endNum
doneNum:
mov rax, r8
mov byte [location], cl ; store updated location
mov dil, 1 ; success = true
cmp r10b, 1 ; check if negative number
jne endNum
not rax ; take two's complement if negative
inc rax
endNum:
ret
skipWhitespace:
movzx rcx, byte [location] ; get current location
dec rcx
whitespaceLoop:
inc rcx
cmp rcx, 0x80
jae finishWhitespace
mov al, byte [input + rcx] ; get byte at current location
cmp al, ' '
je whitespaceLoop
finishWhitespace:
mov byte [location], cl ; store new location
ret
getOperator: ; return val of 0 = invalid; 1 = \n
movzx rcx, byte [location]
mov al, byte [input + rcx]
inc byte [location]
cmp al, '+'
je validOperator
cmp al, '-'
je validOperator
cmp al, '*'
je validOperator
cmp al, '/'
je validOperator
cmp al, '%'
je validOperator
cmp al, '^'
je validOperator
cmp al, 0xA
je validOperator
mov rdi, operatorMsg
movzx rsi, byte [operatorLen]
call printText
mov rax, 0
jmp endOperator
validOperator:
mov byte [op], al
movzx rax, al ; fill out top of return value with 0's
endOperator:
ret
validateInput:
mov rax, 0 ; assume OK, set to 1 if not
mov r8, qword [left]
mov r9, qword [right]
cmp byte [op], '/' ; check for div 0
jne endDivVal
cmp qword [right], 0
jne endValidate
mov rdi, div0Msg
movzx rsi, byte [div0Len]
call printText
mov rax, 1
jmp endValidate
endDivVal:
cmp byte [op], '^' ; check for 0^0
jne endPowVal
cmp qword [right], 0
jge checkPow00
mov rdi, powNegMsg
movzx rsi, byte [powNegLen]
call printText
jmp endValidate
checkPow00:
jne endValidate ; based off of cmp right to 0 from above
cmp qword [left], 0
jne endValidate
mov rdi, pow00Msg
movzx rsi, byte [pow00Len]
call printText
mov rax, 1
jmp endValidate
endPowVal:
endValidate:
ret
calculate:
cmp byte [op], '+'
jne endAdd
mov rax, qword [left]
add rax, qword [right]
jmp endCalc
endAdd:
cmp byte [op], '-'
jne endSub
mov rax, qword [left]
sub rax, qword [right]
jmp endCalc
endSub:
cmp byte [op], '*'
jne endMul
mov rax, qword [left]
imul qword [right]
jmp endCalc
endMul:
cmp byte [op], '/'
jne endDiv
mov rax, qword [left]
cqo
idiv qword [right]
jmp endCalc
endDiv:
cmp byte [op], '%'
jne endMod
mov rax, qword [left]
cqo
idiv qword [right]
mov rax, rdx
jmp endCalc
endMod:
cmp byte [op], 0xA ; endl
jne endEndl
mov rax, qword [left]
jmp endCalc
endEndl:
cmp byte [op], '^'
jne endPow
mov r8b, 0 ; not negative
mov rax, qword [left]
mov rcx, qword [right]
cmp rcx, 0 ; check if pow = 0
jne powLoop
mov rax, 1
jmp endCalc
powLoop:
dec rcx
cmp rcx, 0
je endCalc
imul qword [left]
jmp powLoop
endPow:
endCalc:
ret