Skip to content

Instantly share code, notes, and snippets.

@donaldguy
Created March 22, 2014 22:59
Show Gist options
  • Save donaldguy/9715784 to your computer and use it in GitHub Desktop.
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
;; 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