Skip to content

Instantly share code, notes, and snippets.

@mk12
Last active October 10, 2015 21:28
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mk12/3753349 to your computer and use it in GitHub Desktop.
Save mk12/3753349 to your computer and use it in GitHub Desktop.
Calculates the area and perimeter of rectangles.
; Copyright 2012 Mitchell Kember. Subject to the MIT license.
; ICS3U / 18-Sept-2012 / Daily Assignment
; Calculates the area and perimeter of rectangles.
; ----------------------------------------
; ------------ READONLY DATA -------------
; ----------------------------------------
section .rodata
; Prompts
pcalc: db "Calculate (a)rea or (p)erimeter?: "
pcalc_s: equ $-pcalc
plength: db "Enter the length: "
plength_s: equ $-plength
pwidth: db "Enter the width: "
pwidth_s: equ $-pwidth
; Error messages
errcalc db "Bad input. Please enter 'a' or 'p'.", 0x0a
errcalc_s equ $-errcalc
; ----------------------------------------
; ---------- UNINITIALIZED DATA ----------
; ----------------------------------------
section .bss
%define buf_s 0x40 ; Size of buffer
buf: resb buf_s ; 64 byte buffer
; ----------------------------------------
; ---------------- MACROS ----------------
; ----------------------------------------
; System call write
%macro write 2
mov rax, 0x2000004 ; System call write = 4
mov rdi, 1 ; Write to stdout = 1
mov rsi, %1 ; Address of the string
mov rdx, %2 ; Size to write
syscall ; Invoke the kernel
%endmacro
; System call read
%macro read 2
mov rax, 0x2000003 ; System call read = 3
mov rdi, 1 ; Read from stdin = 1
mov rsi, %1 ; Address to store input
mov rdx, %2 ; Size to read
syscall ; Invoke the kernel
%endmacro
; System call exit
%macro exit 1
mov rax, 0x2000001 ; System call exit = 1
mov rdi, %1 ; Exit status
syscall ; Invoke the kernel
%endmacro
; ----------------------------------------
; ----------------- MAIN -----------------
; ----------------------------------------
section .text
global start
start:
write pcalc, pcalc_s ; Write area/perimeter prompt
read buf, buf_s ; Read user input
cmp rax, -1 ; Check for read error
jne .continue ; If not, carry on
exit 1 ; Exit failure
.continue:
mov rax, buf ; Because buf is apparently a 32-bit absolute address
mov sil, [rax] ; Get the first character
cmp sil, 'a' ; Is the character 'a'?
je .area ; Calculate area
cmp sil, 'p' ; Is the character 'p'?
je .perimeter ; Calculate perimeter
write errcalc, errcalc_s ; Write error messsage
exit 1 ; Exit failure
.area:
call lw_input ; Get length and width
mul rbx ; Multiply rax (width) and rbx (length)
jmp .done ; Write result and exit
.perimeter:
call lw_input ; Get length and width
add rax, rbx ; Add rbx (length) to rax (length)
mov rdi, 2 ; Because mul cannot be used with immediate values
mul rdi ; Multiply rax by 2
.done:
mov rdi, buf ; First argument (buffer address)
mov rsi, buf_s ; Second argument (buffer size)
call ltoa ; Convert the number to a string
mov rbx, rax ; rax gets overwrited by the system write macro
write rbx, buf_s ; Write the string
exit 0 ; Exit success
; ----------------------------------------
; ------------- SUBPROGRAMS --------------
; ----------------------------------------
; Gets the length and width from user input
; Return: rbx - length
; rax - width
; Trash: rdi, rsi, rdx, rcx
lw_input:
write plength, plength_s ; Write length prompt
read buf, buf_s ; Read user input
cmp rax, -1 ; Check for read error
jne .continuel ; If OK, don't exit
exit 1 ; Exit failure
.continuel: ; Press onwards!
dec rax ; Change n. of bytes into index
mov rbx, buf ; Because buf is apparently a 32-bit abs address
add rax, rbx ; Get the last character read (newline)
mov byte [rax], 0 ; Null it
mov rdi, buf ; Argument to atol: address of the string
call atol ; Parse as integer
mov rbx, rax ; Save length in rbx
; Same deal
write pwidth, pwidth_s
read buf, buf_s
cmp rax, -1
jne .continuew
exit 1
.continuew:
dec rax
mov rcx, buf
add rax, rcx
mov byte [rax], 0
mov rdi, buf
call atol
ret
; Parses a string (containing only numerical characters) as a 64-bit integer
; Args: rdi - address of null-terminated string
; Return: rax - the string parsed as an integer
; Trash: rdi, rsi, rdx, rcx
atol:
xor rax, rax ; Zero rax; this will hold the number
xor rsi, rsi ; Zero rsi because we will be using both sil and rsi
mov rcx, 10 ; Because mul cannot be used with immediate values
; Loop through each character, convert it to a single digit (0-9), Multiply
; the accumulating result in rax by rcx (10) to make room for another digit
; and then add the digit -- repeat until null character is reached.
.loop:
mov sil, [rdi] ; Get the character (using lower byte of rsi)
test sil, sil ; Check if it's null (the end of the string)
jz .break ; If so, break out of the loop
sub sil, '0' ; Convert it to a number (single digit)
mul rcx ; Multiply RAX (result) by ten (destroys rdx)
add rax, rsi ; Add the digit
inc rdi ; Next character
jmp .loop ; Loop
.break:
ret
; Args: rax - number, rdi - address to store, rsi - size
; Return: rax - address
; Trash: rdx, rbx, rcx
ltoa:
mov rcx, rdi ; Save the start of the buffer
add rdi, rsi ; Start at the higher addresses
mov rbx, 10 ; Because div cannot be used with immediate values
; Start things off by ending with a null character and newline
dec rdi
mov byte [rdi], 0
dec rdi
mov byte [rdi], 0x0a
; Get the digits backwards by dividing by 10 and using the remainder.
.loop:
dec rdi ; Backstep in the buffer
cmp rdi, rcx ; Let's not overstep our bounds
je .break ; Just stop if there's no more room
xor rdx, rdx ; Zero rdx or else div will cause an error
div rbx ; Divide by 10
add rdx, '0' ; Convert digit to character
mov byte [rdi], dl ; Write it in the buffer
test rax, rax ; Check if the number is 0 yet
jnz .loop ; If not, do the next (previous) digit
.break:
mov rax, rdi ; Return the address in rax
ret
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment