Created
March 22, 2014 22:59
-
-
Save donaldguy/9715784 to your computer and use it in GitHub Desktop.
The single calculation infix calculator I wrote as a capstone project for my high school "Computer Architecture" class in 2006
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; DONALD GUY'S AMAZING UNSIGNED 32-bit INTEGER CALCULATOR!! | |
;; | |
;; written, after much research and testing, on December 30-31, 2006 | |
;; (if Y2K hits seven years late, we will still have this ^_^) | |
;; | |
;; Limitations: can't work with negative numbers (though it has subtraction) | |
;; only works with a MAX sum, product, minuend, or dividend | |
;; of 4,294,967,295 | |
;; | |
;; Target Archetectuire: IA32 system running a POSIX operating system in | |
;; 32-bit protected mode | |
;; | |
;; build commands: | |
;; nasm -f <format: elf on most systems, macho for OS X) -o calc.o calc.asm | |
;; ld -e _start -o calc calc.o | |
;; | |
;; Kopyleft (K) 2006-07 Donald Guy. All Rites Reversed. Do as you like. | |
;; | |
[section .bss] | |
buff resb 11 | |
[section .text] | |
global _start | |
_start: | |
;; our first objective is to get some input | |
xor ebx,ebx ;clean ebx so we can throw some numbers in it | |
_getInput: | |
;; we will call a SYS_read to get input a byte at a time | |
push 1 ;we are reading 1 byte | |
push buff ;we will store into our buffer (no read to regs) | |
push 0 ;this is coming from STDIN | |
mov eax, 3 ;SYS_read is call 3 | |
push eax ;this is junk so that the INT works alone | |
; (otherwise we'd need to use a call/ret pair) | |
int 0x80 ;signal the kernel to get our input | |
add esp, 16 ;clean the stack | |
xor ecx, ecx ;clean edx | |
mov cl, byte [buff] ;get that byte into cl | |
;; now we need to see what type of character it is | |
cmp cl, ' ' ;if its a space: | |
je _getInput ; next byte! | |
cmp cl, '0' ;now we need to see if its a digit | |
jb .notDigit ; if its less than 0x30 .. not a digit | |
cmp cl, '9' | |
ja .notDigit ;if its more than 0x39 .. also not a digit | |
;; if execution gets here, we are looking at a digit in dl | |
mov eax, ebx ;we are using ebx as our accumulator because | |
; we have to use eax for system calls, but the | |
; IA32 is written for EAX to be the accumulator | |
; so we need to move the data there | |
mov edx, 10 ; move 10 to edx so we can multiply eax*edx | |
mul edx ;essentially think of this as decimal SHL | |
xor cl, '0' ;convert ascii to number .. for example: | |
; '5' = 0b00110101 | |
; '0' = 0b00110000 | |
; 0b00110000 xor 0b00110101 = 0b00000101 or 5d | |
add eax, ecx ;add the new digit | |
mov ebx, eax ;and move it back to ebx where we need it | |
jmp _getInput ;we got our digit .. jump back for another byte | |
.notDigit: ;by now, we know its not a digit | |
; we hope that its +, -, *, /, or newline | |
cmp cl, 0xa ;check if its a newline | |
jne .op ;if its not a newline it SHOULD be an operator | |
;; if it IS a newline | |
push ebx ;put op1 on the stack | |
jmp .done ; and input is done! | |
.op: | |
push ebx ;put arg1 on the stack | |
push ecx ;put our operator on the stack | |
mov ebx, 0 ;set ebx back to 0 to prep for arg2 | |
jmp _getInput ;got our operator .. jump back for next byte | |
.done: | |
_computation: | |
;; its now time to do the actual computation | |
pop ebx ;this is arg2 | |
pop ecx ;get the op in dl | |
pop eax ;this is arg1 | |
;; time to determine what operation we want | |
cmp cl, '+' ;is it a + ? | |
je .add ; then add | |
cmp cl, '-' ;is it a - ? | |
je .sub ; then subtract | |
cmp cl, '*' ;is it a * ? | |
je .multiply ; then multiply | |
cmp cl, '/' ;is it a / ? | |
je .divide ; then divide | |
.add: | |
add eax, ebx ; do the addition | |
jmp .done ; time to move on to output | |
.sub: | |
sub eax, ebx ;do the subtraction | |
jmp .done ; move on to output | |
.multiply: | |
mul ebx ;multiply it | |
;; if I get adventorous I will implement 64bit output .. since MUL | |
;; stores to EDX::EAX | |
jmp .done ;move on to output | |
.divide: | |
xor edx, edx ;clear EDX .. cause DIV works on EDX:EAX | |
div ebx ;division time | |
jmp .done | |
.done: | |
_output: | |
mov cx, 10 ;set max iterations (10 digits.. for MAX_INT) | |
.loop: | |
mov edi, ecx ;move to edi so we can use as offset | |
dec edi ;0 based addressing needs dec | |
xor edx, edx ;since idiv works on edx:eax, edx must be 0'd | |
mov ebx, 10 ;idiv only works on reg/mem, no imm | |
idiv ebx ;get rightmost digit in EDX, rest in EAX | |
or edx, 0x30 ;make ascii rather than just BCD | |
mov [buff+edi], dl ;save that digit to the buffer | |
and eax,eax ;set flags based on contents of eax | |
jz .done ;no more number, we are done | |
loop .loop ;else loop | |
.done: | |
dec ecx ;LOOP never reached, so CX needs down 1 more | |
mov edi, 10 ;we are determining num length, 10 is max | |
sub edi, ecx ;subtract num iterations not done.. find length | |
mov [buff+10], byte 0xa ;throw a newline on the end | |
inc edi ;add one to the length for the newline | |
;; time to make our SYS_write call | |
push edi ;this is the length in bytes to be printed | |
;; we need to find the adress of the string | |
mov esi, buff ;we start with the buffer adress | |
add esi, ecx ;we add ecx ... bringing us back to the start | |
push esi ;we push this adress onto the stack.. think of | |
; it as our string pointer, *buf in the manpage | |
push 1 ;output goes to STDOUT | |
mov eax, 4 ;SYS_write is call 4 | |
push eax ;this is the junk .. see call to SYS_read | |
int 0x80 ;send software interrupt to kernel | |
add esp, 16 ;clean the stack | |
;; our output is done .. time to exit .. if only that was easy | |
push 0 ;retuen status of 0 | |
mov eax, 1 ;SYS_exit is call 1 | |
push eax ;this is our junk again .. see call to SYS_read | |
int 0x80 ;send software interrupt to kernel |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment