Skip to content

Instantly share code, notes, and snippets.

@mk12
Created January 29, 2015 18:07
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/21f0921ab56f2af1e37a to your computer and use it in GitHub Desktop.
Save mk12/21f0921ab56f2af1e37a to your computer and use it in GitHub Desktop.
Prints a diamond pattern of N rows with alternating $ and *.
; diamond.s
; Usage:
; diamond <size>
; size is a number from 0 to 300
; If size is greater than 300, 300 will be used
; If size is even, size + 1 (odd) will be used
; If size contains non-numerical characters,
; actual diamond size will be unexpected
; ----------------------------------------
; ----------------- MAIN -----------------
; ----------------------------------------
section .text
global start
start:
; ===== Get Number of Lines =====
; Get number of arguments
pop rax ; argc - the number of command line arguments
dec rax ; Don't count argv[0] (the program's name)
add rsp, 8 ; Skip argv[0]
; Error checking
test rax, rax ; Make sure there is at least one argument
jz .quit ; If not, quit
; Convert string to number (the number of lines)
pop rdi ; Get the first argument
call atol ; Convert it to a number and store it in rax
; Special cases
test rax, rax ; Check if the number is 0
jz .quit ; If so, quit
cmp rax, 300 ; Check if the number is > 300
jna .endif1 ; If not, skip the next instruction
mov rax, 300 ; Limit the number to 300
.endif1:
cmp rax, 1 ; Check if the number is 1 (special case)
jne .endif2 ; If not, skip the next instructions
mov rax, 2 ; 2 bytes of memory required ($\n)
sub rsp, rax ; Make room on the stack
mov rbp, rsp ; Set up the base pointer
jmp .last$ ; Jump to the last "$" character
.endif2:
; ===== Reserve Memory on the Stack =====
; We divide the number of lines by 2 because there is a relation between
; this and the number of bytes of memory required (see next comment)
; This is why all diamonds have an odd number of lines, and an even input
; number N produces the same output as N+1 (odd)
mov rcx, 2 ; Because div cannot be used with immediate values
xor rdx, rdx ; Zero rdx to prevent error when dividing
div rcx ; Divide the number (rax) in half (we don't need the remainder)
; Calculate y for y = 3x^2 + 5x + 2, where x is equal to the input number
; divided by 2 (truncated) -- this is the number of bytes of memory
; required to store the charactes to be printed (" ", "$", "*" and \n)
mov rbx, rax ; Save rax (x) in rbx (hereafter referred to as x)
mul rax ; Square rax (x^2)
mov rcx, 3 ; Immediate values cannot be used with mul
mul rcx ; Multiply by 3 (3x^2)
mov rdi, rax ; Save 3x^2 to rdi
mov rcx, 5 ; Immediate values cannot be used with mul
mov rax, rbx ; Reload x into rax
mul rcx ; Multiply by 5 (5x)
add rax, rdi ; Add together (5x + 3x^2)
add rax, 2 ; Add 2 (3x^2 + 5x + 2)
sub rsp, rax ; Make room on the stack
; To keep track of location in the stack as characters are written to it
; note: rbp is not being used in the usual manner (the stack base pointer)
; although by the end of the program it will be pointing there
mov rbp, rsp
; ===== Write Characters to Memory =====
; These could be optimized away (i.e. put the immediate values in the
; subprograms that use them -- memset and oememset) but this way those
; subprograms are more generalized and the code is more clear
mov r8b, " " ; For writing spaces with memset
mov dl, "$" ; For writing "$" characters every second byte (odd)
mov dil, "*" ; And "*" characters every other byte (even) with oememset
; == First Half of Lines ==
; Counter initialization
xor rsi, rsi ; Zero rsi to be used as the counter
; Start of loop
.loop1: ; First half of lines (0 to x)
cmp rsi, rbx ; Check if the counter (rsi) has reached x
je .break ; If so, break out of the loop
; Spaces
mov rcx, rbx ; Copy x to rcx
sub rcx, rsi ; Subtract the counter from it to get the number of spaces
call memset ; Write them to the stack
; Diamond symbols
mov rcx, rsi ; Copy the counter to rcx
shl rcx, 1 ; Multiply it by 2
add rcx, 1 ; And add 1 to get the number of symbols
call oememset ; Write them to the stack
; Newline
mov [rbp], byte 0x0a
inc rbp
; End of loop
inc rsi ; Next line
jmp .loop1 ; Loop
.break:
; == Second Half of Lines ==
; Counter initialization
mov rsi, rbx ; Set up the counter once more
; Start of loop
.loop2: ; Second half of lines (x to 0)
test rsi, rsi ; Check if the counter (rsi) has reached 0
jz .finish ; If so, break out of the loop
; Spaces
mov rcx, rbx ; Copy x to rcx
sub rcx, rsi ; Subtract the counter from it to get the number of spaces
test rcx, rcx ; Check if there are no spaces (first iteration)
; If there are none, skip the next instruction -- this is required
; because memset uses the loop instruction, and will write one space
; even if rcx is 0, causing a segfault
jz .skip
call memset ; Write them to the stack
.skip:
; Diamond symbols
mov rcx, rsi ; Copy the counter to rcx
shl rcx, 1 ; Multiply it by 2
add rcx, 1 ; Add 1
call oememset ; Write them to the stack
; Newline
mov [rbp], byte 0x0a
inc rbp
; End of loop
dec rsi ; Next line
jmp .loop2 ; Loop
.finish:
; == Last Line ==
; Spaces
mov rcx, rbx ; Copy x to rcx
sub rcx, rsi ; Subtract the counter from it to get the number of spaces
call memset ; Write them to the stack
.last$:
; Diamond symbol (last "$" character)
mov [rbp], byte "$"
inc rbp
; Newline
mov [rbp], byte 0x0a
inc rbp
; ===== Print Diamond and Finish =====
mov rdi, 1 ; Write to stdout = 1
mov rsi, rsp ; Starting address of data
mov rdx, rax ; Number of bytes to write
; System call write = 4 -- set last to avoid overwriting rax before
; setting rdx, because it still contains the number of bytes required
mov rax, 0x2000004
syscall ; Invoke the kernel
mov rsp, rbp ; Clean up
; ===== Quit =====
.quit:
mov rax, 0x2000001 ; System call exit = 1
mov rdi, 0 ; Exit success = 0 (successful)
syscall ; Invoke the kernel
; ----------------------------------------
; ------------- SUBPROGRAMS --------------
; ----------------------------------------
; Args: r8b - the value to write
; rbp - the address to begin writing
; rcx - the number of bytes to set
; Trash: rcx (becomes zero)
; Note: rbp becomes rbp+rcx
; rcx must be nonzero
memset:
.loop:
mov [rbp], r8b ; Write the byte
inc rbp ; Increase the address
loop .loop ; Repeat using rcx (number of bytes to write)
ret ; Return
; Args: dl - the value to write every second byte (odd)
; dil - the value to write every other byte (even)
; rbp - the address to begin writing
; rcx - the number of bytes to set
; Trash: rcx (becomes zero)
; Note: rbp becomes rbp+rcx
; rcx must be nonzero
oememset:
.loop:
test rcx, 1 ; Check if rcx is odd
jz .else ; If not, jump to else
mov [rbp], dl ; Write the odd byte
jmp .endif ; Skip the else block
.else:
mov [rbp], dil ; Write the even byte
.endif:
inc rbp ; Increase the address
loop .loop ; Repeat using rcx (number of bytes to write)
ret ; Return
; Args: rdi - address of null-terminated string containing only numerical chars
; Return: rax - the numerical representation of the string
; 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
; Note: assumes numerical input
.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 ; Nothing more to do
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment