Last active
October 10, 2015 21:28
-
-
Save mk12/3753349 to your computer and use it in GitHub Desktop.
Calculates the area and perimeter of rectangles.
This file contains 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
; 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