diff options
Diffstat (limited to 'PGU')
46 files changed, 2571 insertions, 0 deletions
diff --git a/PGU/CHAP10/convprog.s b/PGU/CHAP10/convprog.s new file mode 100644 index 0000000..49ac6fb --- /dev/null +++ b/PGU/CHAP10/convprog.s @@ -0,0 +1,35 @@ +.include "linux.s" + +.section .data + +tmp_buffer: + .ascii "\0\0\0\0\0\0\0\0\0\0\0" + +.section .text + +.globl _start + +_start: + movq %rsp, %rbp + + pushq $tmp_buffer + pushq $824 + call integer2string + addq $16, %rsp + + pushq $tmp_buffer + call count_chars + addq $8, %rsp + + movq %rax, %rdx + movq $STDOUT, %rdi + movq $tmp_buffer, %rsi + movq $SYS_WRITE, %rax + syscall + + pushq $STDOUT + call write_newline + + movq $SYS_EXIT, %rax + movq $0, %rdi + syscall diff --git a/PGU/CHAP10/count-chars.s b/PGU/CHAP10/count-chars.s new file mode 100644 index 0000000..7e12791 --- /dev/null +++ b/PGU/CHAP10/count-chars.s @@ -0,0 +1,47 @@ +# Count the characters in a string (until a null byte is reached) +# It's supposed to behave similarly with strlen() +# +# Returns the count in %rax +# +# - %rcx: Char count +# - %rdx: current Char address +# - %al: current char +# + +.type count_chars, @function +.globl count_chars + +# We receive the string addr in the stack. +# Remember %rsp + 8 contains the return value +.equ ST_STRING_ADDRESS, 16 + +count_chars: + pushq %rbp + movq %rsp, %rbp + + #initialize counter + movq $0, %rcx + + # Start address of string: + movq ST_STRING_ADDRESS(%rbp), %rdx + +count_loop_begin: + + # Grab char + movb (%rdx), %al + + cmpb $0, %al + je count_loop_end + + # We are not done yet... + incq %rcx + incq %rdx + jmp count_loop_begin + +count_loop_end: + + movq %rcx, %rax + + movq %rbp, %rsp + popq %rbp + ret diff --git a/PGU/CHAP10/int2str.s b/PGU/CHAP10/int2str.s new file mode 100644 index 0000000..6ea5178 --- /dev/null +++ b/PGU/CHAP10/int2str.s @@ -0,0 +1,86 @@ +# integer2string +# +# Convert an int number to a decima string for display +# +# Receives a buffer large enough to hold the largest possible numberr +# and an integer to convert +# +# %rcx hold the count of characters processed +# %rax holds the current value +# %rdi hold the base (10) + +.equ ST_VALUE, 16 +.equ ST_BUFFER, 24 + +.globl integer2string +.type integer2string, @function + +integer2string: + + # Default function setup + pushq %rbp + movq %rsp, %rbp + + movq $0, %rcx # Init char count + movq ST_VALUE(%rbp), %rax # Retrieve value from stack + + # The divisor must be in a register or memory location + movq $10, %rdi + + conversion_loop: + # Division is actually performed on the combined + # %rdx:%rax register, so first, clear out %rdx + movq $0, %rdx + + # Divide %rdx:%rax (which are implied) by 10 + # Store the quotient in %rax and the remainder in + # %rdx (both which are implied) + divq %rdi + + # Quotient is in the right place. %rdx has the remainder + # which now needs to be converted into a number. So, %rdx has a + # number that is 0 through 9. We'll use it as an 'index' on the + # ASCII table starting from char '0'. + addq $'0', %rdx # Get our number's index from ASCII + # table, starting from '0'. + + # Push this value into the stack. When we are done, we can just + # pop them off one-by-one, and they will be in the right order. + # NOTE: We are pushing the whole register, but we only need the + # byte in %dl (The last byte of the %rdx register) for the + # character. + pushq %rdx + + incq %rcx # Increment digit count + + cmpq $0, %rax # Have we reached the end of the calculation? + je end_conversion_loop + + # %rax has its new value + jmp conversion_loop + + end_conversion_loop: + # The string is now on the stack, if we pop it off a char at a + # time, we can copy it into the buffer and we are done. + + movq ST_BUFFER(%rbp), %rdx # Move buffer address to %rdx + + copy_reversing_loop: + popq %rax + movb %al, (%rdx) + decq %rcx # Once we reach 0 we are finished + incq %rdx # Point to the next byte + + cmpq $0, %rcx # Check if we are finished + je end_copy_reversing_loop + + jmp copy_reversing_loop + + end_copy_reversing_loop: + movb $0, (%rdx) + + movq %rbp, %rsp + popq %rbp + ret + +# end of integer2string diff --git a/PGU/CHAP10/linux.s b/PGU/CHAP10/linux.s new file mode 100644 index 0000000..9ab8243 --- /dev/null +++ b/PGU/CHAP10/linux.s @@ -0,0 +1,16 @@ +# Syscall numbers (x86_64) + +.equ SYS_EXIT, 60 +.equ SYS_READ, 0 +.equ SYS_WRITE, 1 +.equ SYS_OPEN, 2 +.equ SYS_CLOSE, 3 +.equ SYS_BRK, 12 + +# Default File Descriptors +.equ STDIN, 0 +.equ STDOUT, 1 +.equ STDERR, 2 + +# Common Status Codes +.equ END_OF_FILE, 0 diff --git a/PGU/CHAP10/write-newline.s b/PGU/CHAP10/write-newline.s new file mode 100644 index 0000000..e916379 --- /dev/null +++ b/PGU/CHAP10/write-newline.s @@ -0,0 +1,29 @@ +# Just write a newline (\n) to STDOUT + +.include "linux.s" +.type write_newline, @function +.globl write_newline + +.section .data + +newline: + .ascii "\n" + +.section .text + .equ ST_FILEDES, 16 + +write_newline: + pushq %rbp + movq %rsp, %rbp + + movq $SYS_WRITE, %rax + movq ST_FILEDES(%rbp), %rdi + movq $newline, %rsi + movq $1, %rdx + syscall + + movq %rbp, %rsp + popq %rbp + ret + + diff --git a/PGU/CHAP3/exit.s b/PGU/CHAP3/exit.s new file mode 100644 index 0000000..df2addb --- /dev/null +++ b/PGU/CHAP3/exit.s @@ -0,0 +1,23 @@ +# Program does not but exit() call with a status code +# returned to kernel + +# No inputs or outputs + +# Variables +# +# %rax - holds syscall number +# %rdi - holds return status + +.section .data +#No data + +.section .text +.globl _start + +_start: +movq $60, %rax # %rax used to hold syscall numbers + # 60 is exit() syscall + +movq $245, %rdi # Exit status (BYTE MAX) + +syscall diff --git a/PGU/CHAP3/max_value.s b/PGU/CHAP3/max_value.s new file mode 100644 index 0000000..7025b05 --- /dev/null +++ b/PGU/CHAP3/max_value.s @@ -0,0 +1,65 @@ +# Find the maximum value inside a static list + +# Variables used: +# +# - %r11 - Holds index of the data item being examined +# - %rdi - Largest data item found so far +# - %rax - Current data item +# +# +# Constant memory locations: +# +# data_items - contains the item data. (Marks the beginning actualy) +# So far, 0 is used as terminator +# +# This program is written for x86_64 processors, it won't assemble in x86 + +.section .data # Specify data section + + +# Array of data items. This use 8bytes (quad-word) for each location +data_items: +.quad 133,55,50,12,25,77,30,93,222,55,62,250,66,34,69,0 + + +.section .text + +# Mark program's starting point so Linux can 'see' it +.globl _start + +_start: +movq $0, %r11 # Move 0 into %rdi to indicate + # the start of the index + +movq data_items(,%r11,8), %rax # Load the first data item (8bytes long) + # This actually uses indexed access + # mode, where %rdi is the 'offset' into + # the data_items array, and 8 is the + # multiplier. + +movq %rax, %rbx # This is the first item, so, %rax is + # current the biggest anyway + + +# Loop over all items +start_loop: + cmpq $0, %rax # compare current item with the + je loop_exit # terminator 0, and jump to exit + # if it is equal. + + incq %r11 # Increment index so we can grab next + # item + + movq data_items(,%r11,8), %rax # Grab next item once we incremented the + # index above + + cmpq %rbx, %rax # If our current item in %rax is smaller + jle start_loop # we ignore it and move back to the loop + + movq %rax, %rdi # Otherwise we store it as our new largest + jmp start_loop # item and jump back to the loop unconditionally + +loop_exit: + movq $60, %rax # Call exit() syscall, our argument is + # already in %rdi, just setup syscall + syscall # number and call kernel diff --git a/PGU/CHAP4/factorial.s b/PGU/CHAP4/factorial.s new file mode 100644 index 0000000..d4907b7 --- /dev/null +++ b/PGU/CHAP4/factorial.s @@ -0,0 +1,67 @@ +# Recursion example - calculates the factorial of a number + +# Everything goes in the stack, no data section needed +.section .data + +.section .text + +.globl _start + +# This is pointless unless we are exporting this function to other programs or +# files. It's just here for completeness. +.global factorial + +_start: + pushq $4 # Push argument + call factorial # call command pushes the next instruction's address on + # the stack, when we return to it, it will pop the + # address off of the stack and point the Instruction + # pointer to it + + addq $8, %rsp # Cleanup stack - Remove the argument from it + # Remember by cleanup, we just move the stackpointer + # back a word (8 bytes in x86_64) times the number of + # arguments pushed in the stack. + + movq %rax, %rdi # Move result into exit() argument register %rdi + # Return values are always stored in %rax... + + movq $60, %rax # Setup %rax to exit() syscall number + syscall # Invoke kernel's syscall + + +# Tell linker this is a function +# Export factorial as a function +.type factorial, @function + +factorial: + # Create the stack frame for this function... + pushq %rbp # Save %rbp in the stack + movq %rsp, %rbp # And current stack pointer in %rbp + + movq 16(%rbp), %rax # Retrieve argument from stack, remember, we're + # dealing with quad words, %rbp is at the + # beginning of the function's stack frame, + # 8(%rbp) is the function's return address setup + # by call command and + # 16(%rbp) is our argument pushed in the stack + # by the function's caller + + cmpq $1, %rax # If we are at 1, nothing to be done... + je end_factorial + + decq %rax # Otherwise we decrease our current operand + pushq %rax # and send it again to factorial function + call factorial # here... + + movq 16(%rbp), %r8 # Retrieve our current argument again + imulq %r8, %rax # and multiply it by the result of the factorial + # below (num factorial = num * factorial of + # previous num, ex. 4! == 4 * 3!) + +end_factorial: + + ## Clean up function's stack frame ## + movq %rbp, %rsp # We are done with this factorial, restore + popq %rbp # %rsp and %rbp and + ret # return to the caller diff --git a/PGU/CHAP4/power.s b/PGU/CHAP4/power.s new file mode 100644 index 0000000..124586e --- /dev/null +++ b/PGU/CHAP4/power.s @@ -0,0 +1,94 @@ +# Power Program +# +# Computes the value of a number raised to a power +# using functions. +# + +# Everything in the main program is stored in registers +# no need for data section +.section .data + +.section .text + +.globl _start + +_start: + + pushq $3 # Second argument (power) + pushq $2 # First argument (base) + call power # Call the function power + + # Cleanup stack removing the arguments from it + addq $16, %rsp + + # Save the answer in the stack before calling power again + push %rax + + pushq $2 # Second argument (Power) + pushq $5 # First argument (Base) + call power + + addq $16, %rsp # Cleanup stack + + # Second answer is already in %rax, get the first + # back from stack to %rdi + popq %rdi + + # Just add them + addq %rax, %rdi + + #Sum is already in %rdi, just call exit() with %rdi as argument + movq $60, %rax + syscall + + + +################## +# Power Function # +################## + +# - First Arg - Base power +# - Second Arg - The power to raise it to +# +# NOTE: So far, power must be 1 or greater +# +# Variables +# +# - %rbx - holds the base number +# - %rcx - holds the power (also acts as a counter) +# - -8(%rbp) holds the current result + +.type power, @function + +power: + pushq %rbp # Save old Base Pointer + movq %rsp, %rbp # make stack pointer the base pointer + subq $8, %rsp # Add space in stack for local storage + + movq 16(%rbp), %r8 # Retrieve Base number from stack to %rbx + movq 24(%rbp), %r9 # Retrieve second argument from stack to %rcx + + movq %r8, -8(%rbp) # Store current result (the operand by itself) + +power_loop_start: + cmpq $1, %r9 # If power is 1, we are done + je end_power + + movq -8(%rbp), %rax # Move current into %rax + imulq %r8, %rax # Multiply current result by base number + + movq %rax, -8(%rbp) # Save current result + + decq %r9 # Decrease the power (using as a counter) + jmp power_loop_start + +end_power: + movq -8(%rbp), %rax # Retrieve end result into %rax + # Return value goes into %rax + movq %rbp, %rsp # Restore stack pointer + popq %rbp # Restore base pointer + ret + + + + diff --git a/PGU/CHAP5/argtest.s b/PGU/CHAP5/argtest.s new file mode 100644 index 0000000..aded606 --- /dev/null +++ b/PGU/CHAP5/argtest.s @@ -0,0 +1,20 @@ +.section .data + +.section .text + +.globl _start + +_start: + movq %rsp, %rbp + movq (%rbp), %r12 + movq 16(%rbp), %r13 + + movq $2, %rax #SYS_OPEN + movq %r13, %rdi + movq $0, %rsi + syscall + + movq %r12, %rdi + movq $60, %rax + syscall + diff --git a/PGU/CHAP5/toupper.s b/PGU/CHAP5/toupper.s new file mode 100644 index 0000000..b3f4f13 --- /dev/null +++ b/PGU/CHAP5/toupper.s @@ -0,0 +1,206 @@ +# Convert an input file to an output file with all +# letters converted to uppercase + +.section .data + +# CONSTANTS +.equ SYS_OPEN, 2 +.equ SYS_WRITE, 1 +.equ SYS_READ, 0 +.equ SYS_CLOSE, 3 +.equ SYS_EXIT, 60 + +# We have no access to headers, so, let's define our flags +.equ O_RDONLY, 0 +.equ O_CREAT_WRONLY_TRUNC, 03101 + +# Add some other naming to make my life easier +.equ STDIN, 0 +.equ STDOUT, 1 +.equ STDERR, 2 + +.equ END_OF_FILE, 0 + +.equ NUMBER_ARGUMENTS, 2 + +################################################################ + +.section .bss + +.equ BUFFER_SIZE, 500 +.lcomm BUFFER_DATA, BUFFER_SIZE + +################################################################ + +.section .text + +# STACK POSITIONS + +.equ ST_SIZE_RESERVE, 16 +.equ ST_FD_IN, -8 +.equ ST_FD_OUT, -16 +.equ ST_ARGC, 0 # Number of arguments +.equ ST_ARGV_0, 8 # Program's name +.equ ST_ARGV_1, 16 # Input file name +.equ ST_ARGV_2, 24 # Output file name + +.globl _start +.global convert_to_upper # Declaration only + +### START OF PROGRAM ### +_start: + + # Save stack pointer + movq %rsp, %rbp + + # Alloc space for FDs on the stack + subq $ST_SIZE_RESERVE, %rsp + +# Labels are here just to make my life easier in splitting the sections +# I won't be jumping around... + +# int open(const char *pathname, int flags, mode_t mode) +# Argument's order: %rdi, %rsi, %rdx + +open_files: +open_fd_in: + movq $SYS_OPEN, %rax # Setup syscall number + movq ST_ARGV_1(%rbp), %rdi # input filename into %rdi + movq $O_RDONLY, %rsi # Open in O_RDONLY + movq $0666, %rdx # regular posix perms + syscall # Invoke kernel to call sys_open() + +store_fd_in: + movq %rax, ST_FD_IN(%rbp) # Save file's FD + +open_fd_out: + movq $SYS_OPEN, %rax # Setup syscall number + movq ST_ARGV_2(%rbp), %rdi # Output file name location to %ebx + movq $O_CREAT_WRONLY_TRUNC, %rsi# Setup writing flags + movq $0666, %rdx # POSIX mode + syscall # Invoke kernel (sys_open()) + +store_fd_out: + movq %rax, ST_FD_OUT(%rbp) # Save output FD in the stack + + +################################################################ + +# ssize_t read(int fd, void *buf, size_t count); +# ssize_t write(int fd, const void *buf, size_t count); + +read_loop_begin: + movq $SYS_READ, %rax + movq ST_FD_IN(%rbp), %rdi # Read from this FD + movq $BUFFER_DATA, %rsi # Copy buffer's address to %rsi, so + # read() knows where to put data read + + movq $BUFFER_SIZE, %rdx # Set buffer size for read() + syscall # Invoke kernel + + # Number of bytes read should be in %eax + + # Leave loop if we are at EOF + cmpq $END_OF_FILE, %rax + jle end_loop + +continue_read_loop: + # Call convert function for the current buffer + pushq $BUFFER_DATA # Buffer location - 2nd argument + pushq %rax # Buffer size - 1st argument + # (returned from read() syscall) + + call convert_to_upper + popq %rax # get size back + addq $8, %rsp # Cleanup stack used for buffer_data location + +write_file: + movq %rax, %rdx # Use number of bytes read to write to + # output file + movq $SYS_WRITE, %rax + movq ST_FD_OUT(%rbp), %rdi # Write to this FD + movq $BUFFER_DATA, %rsi # This buffer + syscall # Invoke kernel + + jmp read_loop_begin # Keep reading the file + +end_loop: + movq $SYS_CLOSE, %rax + movq ST_FD_OUT(%rbp), %rdi + syscall + + movq $SYS_CLOSE, %rax + movq ST_FD_IN(%rbp), %rdi + syscall + + movq $SYS_EXIT, %rax + movq $0, %rdi + syscall + + +# convert_to_upper: +# +# Convert all ascii characters in $BUFFER +# to uppercase +# +# - 1st argument: Buffer size +# - 2nd argument: Buffer address +# +# REMEMBER: Arguments must be pushed in the reverse order as they are documented +# +# - %eax - beginning of buffer +# - %rdi - buffer length +# - %rsi - current buffer offset +# - %cl - byte being examined (lower byte of %rcx) + +## CONSTANTS +.equ LOWERCASE_A, 'a' # lower bundary +.equ LOWERCASE_Z, 'z' # upper boundary + +# CONVERSION +.equ UPPER_CONVERSION, 'A' - 'a' + +## STACK STUFF +.equ ST_BUFFER_LEN, 16 # Buffer length +.equ ST_BUFFER, 24 # Actual Buffer + + +.type convert_to_upper, @function + +convert_to_upper: + pushq %rbp # Setup function's stack frame. Save current %ebp + movq %rsp, %rbp # And set it to current stack pointer + + movq ST_BUFFER(%rbp), %rax + movq ST_BUFFER_LEN(%rbp), %rdi + movq $0, %rsi + + # If a zero length buffer was given, just leave + cmpq $0, %rax + je end_convert_loop + +convert_loop: + movb (%rax, %rsi, 1), %cl # Get current byte + + # Check if current byte is already in low case + cmpb $LOWERCASE_A, %cl + jl next_byte + cmpb $LOWERCASE_Z, %cl + jg next_byte + + # Convert if we are still here + addb $UPPER_CONVERSION, %cl + movb %cl, (%rax, %rsi, 1) # Replace old value with the new one + +next_byte: + incq %rsi + cmpq %rsi, %rdi # Continue conversion unless we reached + jne convert_loop # the end of the buffer + +end_convert_loop: + # no return value, just restore stack pointer + # and base pointer and return. + movq %rbp, %rsp + popq %rbp + ret + diff --git a/PGU/CHAP5/toupper.up b/PGU/CHAP5/toupper.up new file mode 100644 index 0000000..d4c2367 --- /dev/null +++ b/PGU/CHAP5/toupper.up @@ -0,0 +1,206 @@ +# CONVERT AN INPUT FILE TO AN OUTPUT FILE WITH ALL +# LETTERS CONVERTED TO UPPERCASE + +.SECTION .DATA + +# CONSTANTS +.EQU SYS_OPEN, 2 +.EQU SYS_WRITE, 1 +.EQU SYS_READ, 0 +.EQU SYS_CLOSE, 3 +.EQU SYS_EXIT, 60 + +# WE HAVE NO ACCESS TO HEADERS, SO, LET'S DEFINE OUR FLAGS +.EQU O_RDONLY, 0 +.EQU O_CREAT_WRONLY_TRUNC, 03101 + +# ADD SOME OTHER NAMING TO MAKE MY LIFE EASIER +.EQU STDIN, 0 +.EQU STDOUT, 1 +.EQU STDERR, 2 + +.EQU END_OF_FILE, 0 + +.EQU NUMBER_ARGUMENTS, 2 + +################################################################ + +.SECTION .BSS + +.EQU BUFFER_SIZE, 500 +.LCOMM BUFFER_DATA, BUFFER_SIZE + +################################################################ + +.SECTION .TEXT + +# STACK POSITIONS + +.EQU ST_SIZE_RESERVE, 16 +.EQU ST_FD_IN, -8 +.EQU ST_FD_OUT, -16 +.EQU ST_ARGC, 0 # NUMBER OF ARGUMENTS +.EQU ST_ARGV_0, 8 # PROGRAM'S NAME +.EQU ST_ARGV_1, 16 # INPUT FILE NAME +.EQU ST_ARGV_2, 24 # OUTPUT FILE NAME + +.GLOBL _START +.GLOBAL CONVERT_TO_UPPER # DECLARATION ONLY + +### START OF PROGRAM ### +_START: + + # SAVE STACK POINTER + MOVQ %RSP, %RBP + + # ALLOC SPACE FOR FDS ON THE STACK + SUBQ $ST_SIZE_RESERVE, %RSP + +# LABELS ARE HERE JUST TO MAKE MY LIFE EASIER IN SPLITTING THE SECTIONS +# I WON'T BE JUMPING AROUND... + +# INT OPEN(CONST CHAR *PATHNAME, INT FLAGS, MODE_T MODE) +# ARGUMENT'S ORDER: %RDI, %RSI, %RDX + +OPEN_FILES: +OPEN_FD_IN: + MOVQ $SYS_OPEN, %RAX # SETUP SYSCALL NUMBER + MOVQ ST_ARGV_1(%RBP), %RDI # INPUT FILENAME INTO %RDI + MOVQ $O_RDONLY, %RSI # OPEN IN O_RDONLY + MOVQ $0666, %RDX # REGULAR POSIX PERMS + SYSCALL # INVOKE KERNEL TO CALL SYS_OPEN() + +STORE_FD_IN: + MOVQ %RAX, ST_FD_IN(%RBP) # SAVE FILE'S FD + +OPEN_FD_OUT: + MOVQ $SYS_OPEN, %RAX # SETUP SYSCALL NUMBER + MOVQ ST_ARGV_2(%RBP), %RDI # OUTPUT FILE NAME LOCATION TO %EBX + MOVQ $O_CREAT_WRONLY_TRUNC, %RSI# SETUP WRITING FLAGS + MOVQ $0666, %RDX # POSIX MODE + SYSCALL # INVOKE KERNEL (SYS_OPEN()) + +STORE_FD_OUT: + MOVQ %RAX, ST_FD_OUT(%RBP) # SAVE OUTPUT FD IN THE STACK + + +################################################################ + +# SSIZE_T READ(INT FD, VOID *BUF, SIZE_T COUNT); +# SSIZE_T WRITE(INT FD, CONST VOID *BUF, SIZE_T COUNT); + +READ_LOOP_BEGIN: + MOVQ $SYS_READ, %RAX + MOVQ ST_FD_IN(%RBP), %RDI # READ FROM THIS FD + MOVQ $BUFFER_DATA, %RSI # COPY BUFFER'S ADDRESS TO %RSI, SO + # READ() KNOWS WHERE TO PUT DATA READ + + MOVQ $BUFFER_SIZE, %RDX # SET BUFFER SIZE FOR READ() + SYSCALL # INVOKE KERNEL + + # NUMBER OF BYTES READ SHOULD BE IN %EAX + + # LEAVE LOOP IF WE ARE AT EOF + CMPQ $END_OF_FILE, %RAX + JLE END_LOOP + +CONTINUE_READ_LOOP: + # CALL CONVERT FUNCTION FOR THE CURRENT BUFFER + PUSHQ $BUFFER_DATA # BUFFER LOCATION - 2ND ARGUMENT + PUSHQ %RAX # BUFFER SIZE - 1ST ARGUMENT + # (RETURNED FROM READ() SYSCALL) + + CALL CONVERT_TO_UPPER + POPQ %RAX # GET SIZE BACK + ADDQ $8, %RSP # CLEANUP STACK USED FOR BUFFER_DATA LOCATION + +WRITE_FILE: + MOVQ %RAX, %RDX # USE NUMBER OF BYTES READ TO WRITE TO + # OUTPUT FILE + MOVQ $SYS_WRITE, %RAX + MOVQ ST_FD_OUT(%RBP), %RDI # WRITE TO THIS FD + MOVQ $BUFFER_DATA, %RSI # THIS BUFFER + SYSCALL # INVOKE KERNEL + + JMP READ_LOOP_BEGIN # KEEP READING THE FILE + +END_LOOP: + MOVQ $SYS_CLOSE, %RAX + MOVQ ST_FD_OUT(%RBP), %RDI + SYSCALL + + MOVQ $SYS_CLOSE, %RAX + MOVQ ST_FD_IN(%RBP), %RDI + SYSCALL + + MOVQ $SYS_EXIT, %RAX + MOVQ $0, %RDI + SYSCALL + + +# CONVERT_TO_UPPER: +# +# CONVERT ALL ASCII CHARACTERS IN $BUFFER +# TO UPPERCASE +# +# - 1ST ARGUMENT: BUFFER SIZE +# - 2ND ARGUMENT: BUFFER ADDRESS +# +# REMEMBER: ARGUMENTS MUST BE PUSHED IN THE REVERSE ORDER AS THEY ARE DOCUMENTED +# +# - %EAX - BEGINNING OF BUFFER +# - %RDI - BUFFER LENGTH +# - %RSI - CURRENT BUFFER OFFSET +# - %CL - BYTE BEING EXAMINED (LOWER BYTE OF %RCX) + +## CONSTANTS +.EQU LOWERCASE_A, 'A' # LOWER BUNDARY +.EQU LOWERCASE_Z, 'Z' # UPPER BOUNDARY + +# CONVERSION +.EQU UPPER_CONVERSION, 'A' - 'A' + +## STACK STUFF +.EQU ST_BUFFER_LEN, 16 # BUFFER LENGTH +.EQU ST_BUFFER, 24 # ACTUAL BUFFER + + +.TYPE CONVERT_TO_UPPER, @FUNCTION + +CONVERT_TO_UPPER: + PUSHQ %RBP # SETUP FUNCTION'S STACK FRAME. SAVE CURRENT %EBP + MOVQ %RSP, %RBP # AND SET IT TO CURRENT STACK POINTER + + MOVQ ST_BUFFER(%RBP), %RAX + MOVQ ST_BUFFER_LEN(%RBP), %RDI + MOVQ $0, %RSI + + # IF A ZERO LENGTH BUFFER WAS GIVEN, JUST LEAVE + CMPQ $0, %RAX + JE END_CONVERT_LOOP + +CONVERT_LOOP: + MOVB (%RAX, %RSI, 1), %CL # GET CURRENT BYTE + + # CHECK IF CURRENT BYTE IS ALREADY IN LOW CASE + CMPB $LOWERCASE_A, %CL + JL NEXT_BYTE + CMPB $LOWERCASE_Z, %CL + JG NEXT_BYTE + + # CONVERT IF WE ARE STILL HERE + ADDB $UPPER_CONVERSION, %CL + MOVB %CL, (%RAX, %RSI, 1) # REPLACE OLD VALUE WITH THE NEW ONE + +NEXT_BYTE: + INCQ %RSI + CMPQ %RSI, %RDI # CONTINUE CONVERSION UNLESS WE REACHED + JNE CONVERT_LOOP # THE END OF THE BUFFER + +END_CONVERT_LOOP: + # NO RETURN VALUE, JUST RESTORE STACK POINTER + # AND BASE POINTER AND RETURN. + MOVQ %RBP, %RSP + POPQ %RBP + RET + diff --git a/PGU/CHAP6_7/count-chars.s b/PGU/CHAP6_7/count-chars.s new file mode 100644 index 0000000..7e12791 --- /dev/null +++ b/PGU/CHAP6_7/count-chars.s @@ -0,0 +1,47 @@ +# Count the characters in a string (until a null byte is reached) +# It's supposed to behave similarly with strlen() +# +# Returns the count in %rax +# +# - %rcx: Char count +# - %rdx: current Char address +# - %al: current char +# + +.type count_chars, @function +.globl count_chars + +# We receive the string addr in the stack. +# Remember %rsp + 8 contains the return value +.equ ST_STRING_ADDRESS, 16 + +count_chars: + pushq %rbp + movq %rsp, %rbp + + #initialize counter + movq $0, %rcx + + # Start address of string: + movq ST_STRING_ADDRESS(%rbp), %rdx + +count_loop_begin: + + # Grab char + movb (%rdx), %al + + cmpb $0, %al + je count_loop_end + + # We are not done yet... + incq %rcx + incq %rdx + jmp count_loop_begin + +count_loop_end: + + movq %rcx, %rax + + movq %rbp, %rsp + popq %rbp + ret diff --git a/PGU/CHAP6_7/error.s b/PGU/CHAP6_7/error.s new file mode 100644 index 0000000..6e933ed --- /dev/null +++ b/PGU/CHAP6_7/error.s @@ -0,0 +1,46 @@ +# Error function +# +# Print error messages and exit program. + +.include "linux.s" +.equ ST_ERROR_CODE, 16 +.equ ST_ERROR_MSG, 24 + +.globl error_exit +.type error_exit, @function + +error_exit: + pushq %rbp + movq %rsp, %rbp + + # Write error Code + movq ST_ERROR_CODE(%rbp), %rcx + pushq %rcx + call count_chars + popq %rcx + + movq %rax, %rdx + movq %rcx, %rsi + movq $STDERR, %rdi + movq $SYS_WRITE, %rax + syscall + + # Write error message + movq ST_ERROR_MSG(%rbp), %rcx + pushq %rcx + call count_chars + popq %rcx + + movq %rax, %rdx # How many bytes to write + movq %rcx, %rsi # Buffer to write from + movq $STDERR, %rdi # FD to write to + movq $SYS_WRITE, %rax # self explanatory + syscall + + pushq $STDERR + call write_newline + + # Exit program + movq $SYS_EXIT, %rax + movq $1, %rdi + syscall diff --git a/PGU/CHAP6_7/linux.s b/PGU/CHAP6_7/linux.s new file mode 100644 index 0000000..9ab8243 --- /dev/null +++ b/PGU/CHAP6_7/linux.s @@ -0,0 +1,16 @@ +# Syscall numbers (x86_64) + +.equ SYS_EXIT, 60 +.equ SYS_READ, 0 +.equ SYS_WRITE, 1 +.equ SYS_OPEN, 2 +.equ SYS_CLOSE, 3 +.equ SYS_BRK, 12 + +# Default File Descriptors +.equ STDIN, 0 +.equ STDOUT, 1 +.equ STDERR, 2 + +# Common Status Codes +.equ END_OF_FILE, 0 diff --git a/PGU/CHAP6_7/modify-records.s b/PGU/CHAP6_7/modify-records.s new file mode 100644 index 0000000..4cf2d92 --- /dev/null +++ b/PGU/CHAP6_7/modify-records.s @@ -0,0 +1,86 @@ +# Modify records in a file and write them to the output file +.include "linux.s" +.include "record-def.s" + +.section .data + input_filename: + .ascii "test.dat\0" + output_filename: + .ascii "modified.dat\0" + +.section .bss + .lcomm record_buffer, RECORD_SIZE + + +.section .text +.globl _start + +_start: + # Stack local vars + .equ ST_INPUT_DESCRIPTOR, -8 + .equ ST_OUTPUT_DESCRIPTOR, -16 + + # Copy stack pointer and make room for local variables + movq %rsp, %rbp + subq $16, %rsp + + # Open file for reading + movq $SYS_OPEN, %rax + movq $input_filename, %rdi + movq $0, %rsi + movq $0666, %rdx + syscall + + cmpq $0, %rax + jl continue_processing + + # We've got an error... +continue_processing: + movq %rax, ST_INPUT_DESCRIPTOR(%rbp) + + .section .data + no_open_file_code: + .ascii "0001: \0" + no_open_file_msg: + .ascii "Can't open input file\0" + .section .text + pushq $no_open_file_msg + pushq $no_open_file_code + call error_exit + + # Open file for writing + movq $SYS_OPEN, %rax + movq $output_filename, %rdi + movq $0101, %rsi + movq $0666, %rdx + syscall + + movq %rax, ST_OUTPUT_DESCRIPTOR(%rbp) + +loop_begin: + pushq ST_INPUT_DESCRIPTOR(%rbp) + pushq $record_buffer + call read_record + addq $16, %rsp + + # Return number of bytes read. If it's not the same as + # $RECORD_SIZE, then we are either at EOF or we hit an error. + cmpq $RECORD_SIZE, %rax + jne loop_end + + # Increment age... + incq record_buffer + RECORD_AGE + + # Write record to the output file + pushq ST_OUTPUT_DESCRIPTOR(%rbp) + pushq $record_buffer + call write_record + addq $16, %rsp + + jmp loop_begin + +loop_end: + + movq $SYS_EXIT, %rax + movq $0, %rbx + syscall diff --git a/PGU/CHAP6_7/read-records.s b/PGU/CHAP6_7/read-records.s new file mode 100644 index 0000000..89b1e91 --- /dev/null +++ b/PGU/CHAP6_7/read-records.s @@ -0,0 +1,74 @@ +# Read records previously written in file.dat, by write-records software + +.include "linux.s" +.include "record-def.s" + +.section .data + filename: + .ascii "test.dat\0" + +.section .bss + .lcomm record_buffer, RECORD_SIZE + +.section .text + +.globl _start + +_start: + # Stack locations for INPUT and OUTPUT FDs + .equ ST_INPUT_DESCRIPTOR, -8 + .equ ST_OUTPUT_DESCRIPTOR, -16 + + movq %rsp, %rbp + subq $16, %rsp # Save space in the stack for FDs + + # Open data file + movq $SYS_OPEN, %rax + movq $filename, %rdi + movq $0, %rsi # Open for read only + movq $0666, %rdx + syscall + + # Save FD + movq %rax, ST_INPUT_DESCRIPTOR(%rbp) + + # Yes, STDOUT is always 1, but if I want to change the output location + # later, I don't need to change everywhere... + movq $STDOUT, ST_OUTPUT_DESCRIPTOR(%rbp) + +record_read_loop: + pushq ST_INPUT_DESCRIPTOR(%rbp) + pushq $record_buffer + call read_record + addq $16, %rsp # Cleanup stack + + # All records are RECORD_SIZE size, if we didn't get this amount of + # bytes from read function, we either are at EOF or we hit an error. + cmpq $RECORD_SIZE, %rax + jne finished_reading + + # We are ok, so print the first name in the record + pushq $RECORD_FIRSTNAME + record_buffer # Location of firstname + # record in the buffer + # we just read + call count_chars + addq $8, %rsp # Cleanup stack + + # Write name to OUTPUT + movq %rax, %rdx # Returned record size, used as argument to + # write() + movq $RECORD_FIRSTNAME + record_buffer, %rsi + movq ST_OUTPUT_DESCRIPTOR(%rbp), %rdi + movq $SYS_WRITE, %rax + syscall + + pushq ST_OUTPUT_DESCRIPTOR(%rbp) + call write_newline + addq $8, %rsp + + jmp record_read_loop + +finished_reading: + movq $SYS_EXIT, %rax + movq $0, %rdi + syscall diff --git a/PGU/CHAP6_7/read_write.s b/PGU/CHAP6_7/read_write.s new file mode 100644 index 0000000..d574013 --- /dev/null +++ b/PGU/CHAP6_7/read_write.s @@ -0,0 +1,62 @@ +.include "record-def.s" +.include "linux.s" + +# Read function +# Reads a record from the file descriptor +# and writes it into the buffer passed + +# STACK VARS - Used for both read and write functions. They don't share the +# location, but the arguments are passed in the same position +# for both, so, no need to create different location vars. + +.equ ST_BUFFER, 16 # Ret address is at %rsp+8 +.equ ST_FILEDES, 24 + +.section .text + +.globl read_record +.type read_record, @function + +read_record: + pushq %rbp + movq %rsp, %rbp + + # READ A RECORD + pushq %rdi + movq ST_FILEDES(%rbp), %rdi + movq ST_BUFFER(%rbp), %rsi + movq $RECORD_SIZE, %rdx + movq $SYS_READ, %rax + syscall + + # NOTE: %rax has the return value, which we will give back + # to our caller + + popq %rdi + + movq %rbp, %rsp + popq %rbp + ret + +.globl write_record +.type write_record, @function + +write_record: + pushq %rbp + movq %rsp, %rbp + + # WRITE A RECORD + pushq %rdi + movq ST_FILEDES(%rbp), %rdi + movq ST_BUFFER(%rbp), %rsi + movq $RECORD_SIZE, %rdx + movq $SYS_WRITE, %rax + syscall + + # NOTE: %rax has the return value, which we will give back + # to our caller + popq %rdi + + movq %rbp, %rsp + popq %rbp + ret diff --git a/PGU/CHAP6_7/record-def.s b/PGU/CHAP6_7/record-def.s new file mode 100644 index 0000000..9e5274d --- /dev/null +++ b/PGU/CHAP6_7/record-def.s @@ -0,0 +1,7 @@ +# Define offsets within a record + +.equ RECORD_FIRSTNAME, 0 +.equ RECORD_LASTNAME, 40 +.equ RECORD_ADDRESS, 80 +.equ RECORD_AGE, 320 # Use 8 bytes for age, because it's +.equ RECORD_SIZE, 328 # simpler to deal with whole words diff --git a/PGU/CHAP6_7/write-newline.s b/PGU/CHAP6_7/write-newline.s new file mode 100644 index 0000000..e916379 --- /dev/null +++ b/PGU/CHAP6_7/write-newline.s @@ -0,0 +1,29 @@ +# Just write a newline (\n) to STDOUT + +.include "linux.s" +.type write_newline, @function +.globl write_newline + +.section .data + +newline: + .ascii "\n" + +.section .text + .equ ST_FILEDES, 16 + +write_newline: + pushq %rbp + movq %rsp, %rbp + + movq $SYS_WRITE, %rax + movq ST_FILEDES(%rbp), %rdi + movq $newline, %rsi + movq $1, %rdx + syscall + + movq %rbp, %rsp + popq %rbp + ret + + diff --git a/PGU/CHAP6_7/write_records.s b/PGU/CHAP6_7/write_records.s new file mode 100644 index 0000000..e52d7e0 --- /dev/null +++ b/PGU/CHAP6_7/write_records.s @@ -0,0 +1,135 @@ +.include "linux.s" +.include "record-def.s" + +.section .data + +# Constant data of the records we want to write +# Each data item is padded to the proper length +# With null (i.e 0) bytes + +# .rept is used to pad each item. .rept tells the assembler to repeat the +# section between .rept and .endr the number of times specified. +# This is used in this program to add extra null chars at the end of each field +# to fill the record up to the record size. + +# START OF RECORDS +record1: + # First name + .ascii "Fredrick\0" + .rept 31 # Padding to 40 bytes + .byte 0 + .endr + + # Last name + .ascii "Bardlet\0" + .rept 31 # Padding to 40 bytes + .byte 0 + .endr + + # Address + .ascii "4242 S Prairie\nTulsa, OK 55555\0" + .rept 209 # Padding to 240 bytes + .byte 0 + .endr + + # Age + .long 45 # Field is 8 bytes long (1 word) + +record2: + # First name + .ascii "Marilyn\0" + .rept 32 # Padding to 40 bytes + .byte 0 + .endr + + # Last name + .ascii "Taylor\0" + .rept 33 # Padding to 40 bytes + .byte 0 + .endr + + # Address + .ascii "2224 S Johannan St\nChicago, IL 12345\0" + .rept 203 # Padding to 240 bytes + .byte 0 + .endr + + # Age + .long 29 # Field is 8 bytes long (1 word) + +record3: + # First name + .ascii "Derrick\0" + .rept 32 # Padding to 40 bytes + .byte 0 + .endr + + # Last name + .ascii "McIntire\0" + .rept 31 # Padding to 40 bytes + .byte 0 + .endr + + # Address + .ascii "500 W Oakland\nSan Diego, CA 54321\0" + .rept 206 # Padding to 240 bytes + .byte 0 + .endr + + # Age + .long 36 # Field is 8 bytes long (1 word) + +# END OF RECORDS + + +.section .text +# File we'll be writing to +file_name: + .ascii "test.dat\0" + +.equ ST_FILE_DESCRIPTOR, -8 + +.globl _start +.global write_record + +_start: + movq %rsp, %rbp + subq $8, %rsp # Allocate space in stack for FD + + # Open file + movq $SYS_OPEN, %rax + movq $file_name, %rdi + movq $0101, %rsi # Open for writing, create if it doesn't exist + movq $0666, %rdx + syscall + + # Store FD: + movq %rax, ST_FILE_DESCRIPTOR(%rbp) + + # Write first record + pushq ST_FILE_DESCRIPTOR(%rbp) + pushq $record1 + call write_record + addq $16, %rsp #cleanup stack + + # Write second record + pushq ST_FILE_DESCRIPTOR(%rbp) + pushq $record2 + call write_record + addq $16, %rsp + + # Write third record + pushq ST_FILE_DESCRIPTOR(%rbp) + pushq $record3 + call write_record + addq $16, %rsp + + # Close FD + movq $SYS_CLOSE, %rax + movq ST_FILE_DESCRIPTOR(%rbp), %rdi + syscall + + # We are done, just exit + movq $SYS_EXIT, %rax + movq $0, %rdi + syscall diff --git a/PGU/CHAP8/helloworld-lib.s b/PGU/CHAP8/helloworld-lib.s new file mode 100644 index 0000000..6e04968 --- /dev/null +++ b/PGU/CHAP8/helloworld-lib.s @@ -0,0 +1,20 @@ +# Hello world using DSOs + +.section .data + helloworld: + .ascii "hello world\n\0" + +.section .text + +.globl _start + +_start: + + # This differs from the book. In x86_64, arguments are passed through + # registers most of the time, in contrast with i386, where we can push + # them in the stack. + movq $helloworld, %rdi + call printf + + movq $50, %rdi + call exit diff --git a/PGU/CHAP8/helloworld-nolib.s b/PGU/CHAP8/helloworld-nolib.s new file mode 100644 index 0000000..83d52ce --- /dev/null +++ b/PGU/CHAP8/helloworld-nolib.s @@ -0,0 +1,25 @@ +# Write "hello world" and exit + +.include "linux.s" + +.section .data + helloworld: + .ascii "hello world\n" + helloworld_end: + + .equ helloworld_len, helloworld_end - helloworld + +.section .text + +.globl _start + +_start: + movq $STDOUT, %rdi + movq $helloworld, %rsi + movq $helloworld_len, %rdx + movq $SYS_WRITE, %rax + syscall + + movq $0, %rdi + movq $SYS_EXIT, %rax + syscall diff --git a/PGU/CHAP8/linux.s b/PGU/CHAP8/linux.s new file mode 100644 index 0000000..9ab8243 --- /dev/null +++ b/PGU/CHAP8/linux.s @@ -0,0 +1,16 @@ +# Syscall numbers (x86_64) + +.equ SYS_EXIT, 60 +.equ SYS_READ, 0 +.equ SYS_WRITE, 1 +.equ SYS_OPEN, 2 +.equ SYS_CLOSE, 3 +.equ SYS_BRK, 12 + +# Default File Descriptors +.equ STDIN, 0 +.equ STDOUT, 1 +.equ STDERR, 2 + +# Common Status Codes +.equ END_OF_FILE, 0 diff --git a/PGU/CHAP8/printcall.s b/PGU/CHAP8/printcall.s new file mode 100644 index 0000000..2d50e73 --- /dev/null +++ b/PGU/CHAP8/printcall.s @@ -0,0 +1,34 @@ +# Just an example of calling printf() using ASM + +.section .data + firststring: + .ascii "Hello! %s is a %s who lives the number %d\n\0" + name: + .ascii "Maiolino\0" + personstring: + .ascii "person\0" + + numberloved: + .long 123 + +.section .text +.globl _start + + # Recall we are using x86_64 ABI here, so we pass arguments through + # registers instead of on the stack. + +_start: + movq $firststring, %rdi + movq $name, %rsi + movq $personstring, %rdx + + # We don't use $numberloved here because $ sign means 'immediate' + # addressing. Here we want the 'CONTENT' pointed by label 'numberloved' + # so, we use 'direct' addressing, by ommiting the $ sign, so we get the + # information "pointed by" the label numberloved itself. + movq numberloved, %rcx + call printf + + movq $0, %rdi + call exit + diff --git a/PGU/CHAP9/count-chars.s b/PGU/CHAP9/count-chars.s new file mode 100644 index 0000000..7e12791 --- /dev/null +++ b/PGU/CHAP9/count-chars.s @@ -0,0 +1,47 @@ +# Count the characters in a string (until a null byte is reached) +# It's supposed to behave similarly with strlen() +# +# Returns the count in %rax +# +# - %rcx: Char count +# - %rdx: current Char address +# - %al: current char +# + +.type count_chars, @function +.globl count_chars + +# We receive the string addr in the stack. +# Remember %rsp + 8 contains the return value +.equ ST_STRING_ADDRESS, 16 + +count_chars: + pushq %rbp + movq %rsp, %rbp + + #initialize counter + movq $0, %rcx + + # Start address of string: + movq ST_STRING_ADDRESS(%rbp), %rdx + +count_loop_begin: + + # Grab char + movb (%rdx), %al + + cmpb $0, %al + je count_loop_end + + # We are not done yet... + incq %rcx + incq %rdx + jmp count_loop_begin + +count_loop_end: + + movq %rcx, %rax + + movq %rbp, %rsp + popq %rbp + ret diff --git a/PGU/CHAP9/error.s b/PGU/CHAP9/error.s new file mode 100644 index 0000000..6e933ed --- /dev/null +++ b/PGU/CHAP9/error.s @@ -0,0 +1,46 @@ +# Error function +# +# Print error messages and exit program. + +.include "linux.s" +.equ ST_ERROR_CODE, 16 +.equ ST_ERROR_MSG, 24 + +.globl error_exit +.type error_exit, @function + +error_exit: + pushq %rbp + movq %rsp, %rbp + + # Write error Code + movq ST_ERROR_CODE(%rbp), %rcx + pushq %rcx + call count_chars + popq %rcx + + movq %rax, %rdx + movq %rcx, %rsi + movq $STDERR, %rdi + movq $SYS_WRITE, %rax + syscall + + # Write error message + movq ST_ERROR_MSG(%rbp), %rcx + pushq %rcx + call count_chars + popq %rcx + + movq %rax, %rdx # How many bytes to write + movq %rcx, %rsi # Buffer to write from + movq $STDERR, %rdi # FD to write to + movq $SYS_WRITE, %rax # self explanatory + syscall + + pushq $STDERR + call write_newline + + # Exit program + movq $SYS_EXIT, %rax + movq $1, %rdi + syscall diff --git a/PGU/CHAP9/linux.s b/PGU/CHAP9/linux.s new file mode 100644 index 0000000..9ab8243 --- /dev/null +++ b/PGU/CHAP9/linux.s @@ -0,0 +1,16 @@ +# Syscall numbers (x86_64) + +.equ SYS_EXIT, 60 +.equ SYS_READ, 0 +.equ SYS_WRITE, 1 +.equ SYS_OPEN, 2 +.equ SYS_CLOSE, 3 +.equ SYS_BRK, 12 + +# Default File Descriptors +.equ STDIN, 0 +.equ STDOUT, 1 +.equ STDERR, 2 + +# Common Status Codes +.equ END_OF_FILE, 0 diff --git a/PGU/CHAP9/memalloc.s b/PGU/CHAP9/memalloc.s new file mode 100644 index 0000000..6f354de --- /dev/null +++ b/PGU/CHAP9/memalloc.s @@ -0,0 +1,303 @@ + +# Alloc and Dealloc memory as requested +# +# Programs using these routines will ask +# for a certain size of memory. We actually +# use more than that size for metadata, +# but we put it at the beginning, before the +# pointer we hand back. We add a size field and +# and AVAILABLE/UNAVAILABLE marker. +# +# The whole memory slot looks like this: +# +#################################################### +# Available marker | Size | Actual memory location # +#################################################### +# ^-- returned pointer points +# here +# +# The calling program won't see our metadata. + +### GLOBAL VARS ### +.section .data + +############################### DEBUG INFO ################################ + +# Originally, these labels heap_begin and current_break are .long long... +# This implicitly means 4 bytes for each variable. This was causing the instructions +# in allocate_init to overlap the variables when writing to them: + +# The following instructions were the problem: +# movq %rax, current_break +# allocated at 0x402011 +# movq %rax, heap_begin +# allocated at 0x40200d +# +# Static allocation grows up in memory... But, when writing %rax to heap_begin, +# due the variable sizes being 4bytes, the instruction was corrupting +# current_break variable, causing the program to crash later in the allocation +# loop, because the heap_begin allocation, consequently caused current_break to be +# zero causing a NULL ptr dereference (or simply a SEGFAULT): + +# => 0x000000000040114e <+5>: mov 0x8(%rax),%rdx +# Program received signal SIGSEGV, Segmentation fault. + +# allocate_init() disassemble: +# 0x0000000000401105 <+0>: push %rbp +# 0x0000000000401106 <+1>: mov %rsp,%rbp +# 0x0000000000401109 <+4>: mov $0xc,%rax +# 0x0000000000401110 <+11>: mov $0x0,%rdi +# 0x0000000000401117 <+18>: syscall +# 0x0000000000401119 <+20>: inc %rax +# 0x000000000040111c <+23>: mov %rax,0x402011 +# 0x0000000000401124 <+31>: mov %rax,0x40200d +#=> 0x000000000040112c <+39>: mov %rbp,%rsp +# 0x000000000040112f <+42>: pop %rbp +# 0x0000000000401130 <+43>: ret +# +# (gdb) info register rax +# rax 0x403001 4206593 +# (gdb) p /x *0x40200d +# $5 = 0x403001 +# (gdb) p /x *0x402011 +# $6 = 0x0 <-- Here, the address of current_break got zeroed after +# heap_being has been changed + +############################# END OF DEBUG INFO ############################# + +# Points to the beginning of the memory we are managing +heap_begin: + .quad 0 + +# Points to one locaiton past the memory we are managing +current_break: + .quad 0 + +### HEADER STRUCTURE INFORMATION ### + +# To make things simpler, we use one word for +# each field in the header + +.equ HEADER_SIZE, 16 # size of space for memory region header +.equ HDR_AVAIL_OFFSET, 0 # Location of the 'available' flag in the header +.equ HDR_SIZE_OFFSET, 8 # Location of the size field in the header + + +### CONSTANTS ### +.equ UNAVAILABLE, 0 +.equ AVAILABLE, 1 + +.equ SYS_BRK, 12 # syscall number for brk() syscall in x86_64 + + +.section .text + +### FUNCTIONS ### + +## allocate_init ## +# +# Call this function to initialize the functions by setting heap_begin and +# current_break. No parameters and no return value + +.globl allocate_init +.type allocate_init, @function + +allocate_init: + pushq %rbp # standard function stuff + movq %rsp, %rbp + + # If brk() syscall is called with a 0 in %rdi, it returns the last valid + # usable address + movq $SYS_BRK, %rax + movq $0, %rdi + syscall + + incq %rax # brk(0) returns the current break in %rax, we want the + # value after that + + movq %rax, current_break # Store the current break (actually the address + # after that) + + movq %rax, heap_begin # Our heap starts where the break is now. First + # address of uninitialized memory. This will + # cause the allocate function to get more memory + # from Linux the first time it is run. + + movq %rbp, %rsp # Exit the function + popq %rbp + ret + +## END of allocate_init ## + + +## allocate ## +# +# Grab a section of memory. +# - Checks to see if there are any free blocks +# - If not, asks Linux for more memory for the +# heap through brk() syscall +# +# - Receives on parameter, the size of the memory block +# we want to allocate +# +# - Returns the address of the allocated memory in %rax, or 0 +# if there is no memory available on the system +# +# ### Process +# +# %rcx - holds the size of requested memory (first/only parameter) +# %rax - current memory region being examined +# %rbx - Memory address past the end of the heap +# %rdx - size of the current memory region +# +# We scan through each memory region starting with heap_begin. We look at the +# size of each one and if it has been allocated. If it's big enough for the +# requested size, and it's available, we grab that one. If we do not find a +# region large enough, we ask Linux for more memory, which in case, moves the +# current_break up. + +.globl allocate +.type allocate, @function +.equ ST_MEM_SIZE, 16 # Stack position of the memory size to allocate + +allocate: + pushq %rbp + movq %rsp, %rbp + + movq ST_MEM_SIZE(%rbp), %rcx # %rcx now holds the memory size we are + # looking for (first/only parameter) + + movq heap_begin, %rax # %rax will hold the current search location + movq current_break, %rbx + + alloc_loop_begin: # Scan memory regions + + cmpq %rbx, %rax # heap needs more memory if these are + # equal. %rax will hold the end of the + # next memory region at each iteraction + # until it hits the current_break. + + je move_break + + # Retrieve the size of this mem slot + movq HDR_SIZE_OFFSET(%rax), %rdx + + cmpq $UNAVAILABLE, HDR_AVAIL_OFFSET(%rax) # If the space is + # unavailable, i.e. + # already in use.. Go + je next_location # to the next one. + + cmpq %rdx, %rcx # If the slot is available, check if + jle allocate_here # it's big enough. + + next_location: + addq $HEADER_SIZE, %rax # Total size of the memory region is the + # sum of the size requested (currently + # at %rdx), plus the 16 bytes for the + addq %rdx, %rax # header. + + jmp alloc_loop_begin # Go look at the next location + + allocate_here: # If we've made it here, that means that + # the region header of the region to + # allocate is in %rax + + # Mark space as unavailable + movq $UNAVAILABLE, HDR_AVAIL_OFFSET(%rax) + addq $HEADER_SIZE, %rax # move %rax past the header, so it + # points to the usable memory. Such + # address is returned to the user. + + # Normal function return + movq %rbp, %rsp + popq %rbp + ret + + move_break: # We have exhausted all addressable memory, we need to + # get more from Linux + # %rbx holds the current endpoint of the data, and + # %rcx its size. + + addq $HEADER_SIZE, %rbx # We need to increase %rbx to where we _want_ + # memory to end. So we account for the header + addq %rcx, %rbx # and the user's requested size + + # Ask Linux for more memory + + # We'll need the values in these registers, so save them... + pushq %rax + pushq %rbx + pushq %rcx + + movq $SYS_BRK, %rax # Set new break by calling brk() + movq %rbx, %rdi # x86_64 uses %rdi for first parameter + syscall + + # brk() returns 0 for error or the address we asked for (or larger if it + # needs to be rounded up). We don't really care here where it ends up + # setting the break as long as it isn't 0. + + cmpq $0, %rax # Check for errors + je error + + # Restore our registers + popq %rcx + popq %rbx + popq %rax + + # Set this memory as unavailable since we are about to give it away + movq $UNAVAILABLE, HDR_AVAIL_OFFSET(%rax) + movq %rcx, HDR_SIZE_OFFSET(%rax) # Set memory size + + addq $HEADER_SIZE, %rax # Set %rax to the address being + # returned to the user (user + # doesn't know anything about the headers + + movq %rbx, current_break # Save the current break. In + # reality it may be larger due + # rounding, but we don't care + # about memory footprint here + + movq %rbp, %rsp + popq %rbp + ret + + error: + movq $0, %rax # Return 0 on error + movq %rbp, %rsp + popq %rbp + ret +##### END OF allocate ###### + +## deallocate ## +# +# Give back a region of memory to the pool after user is done +# with it +# +# Parameter: Address of the memory to be freed +# No return value +# +# The memory address returned to the user starts at 2 words beyond its header, +# all we need to do here is mark the memory slot as free (available). +# By now we don't care about moving the break back. +.globl deallocate +.type deallocate, @function + +# Stack position of the memory region to be free (function's parameter) +.equ ST_MEMORY_SEG, 8 # We are not saving %rbp here so we just need to skip ret + # address + +deallocate: + # Function is too simple, no need for fancy function stuff + + # Get the address of the memory to free (Normally this is 16(%rbp), but + # since we didn't push %rbp or move %rsp to %rbp, we can just do 8(%rsp) + movq ST_MEMORY_SEG(%rsp), %rax + + # Get the pointer to the beginning of the region (i.e. to the header) + subq $HEADER_SIZE, %rax + + # Mark it as available + movq $AVAILABLE, HDR_AVAIL_OFFSET(%rax) + ret +##### END OF deallocate ##### diff --git a/PGU/CHAP9/read-records.s b/PGU/CHAP9/read-records.s new file mode 100644 index 0000000..01384a0 --- /dev/null +++ b/PGU/CHAP9/read-records.s @@ -0,0 +1,86 @@ +# Read records previously written in file.dat, by write-records software + +.include "linux.s" +.include "record-def.s" + +.section .data + filename: + .ascii "test.dat\0" + record_buffer_ptr: + .long 0 + +.section .text + +.globl _start + +_start: + # Stack locations for INPUT and OUTPUT FDs + .equ ST_INPUT_DESCRIPTOR, -8 + .equ ST_OUTPUT_DESCRIPTOR, -16 + + movq %rsp, %rbp + subq $16, %rsp # Save space in the stack for FDs + + call allocate_init # Initialize our memory + + # allocate buffer + pushq $RECORD_SIZE + call allocate + movq %rax, record_buffer_ptr + + # Open data file + movq $SYS_OPEN, %rax + movq $filename, %rdi + movq $0, %rsi # Open for read only + movq $0666, %rdx + syscall + + # Save FD + movq %rax, ST_INPUT_DESCRIPTOR(%rbp) + + # Yes, STDOUT is always 1, but if I want to change the output location + # later, I don't need to change everywhere... + movq $STDOUT, ST_OUTPUT_DESCRIPTOR(%rbp) + +record_read_loop: + pushq ST_INPUT_DESCRIPTOR(%rbp) + pushq record_buffer_ptr + call read_record + addq $16, %rsp # Cleanup stack + + # All records are RECORD_SIZE size, if we didn't get this amount of + # bytes from read function, we either are at EOF or we hit an error. + cmpq $RECORD_SIZE, %rax + jne finished_reading + + # We are ok, so print the first name in the record + movq record_buffer_ptr, %rax + addq $RECORD_FIRSTNAME, %rax # Seek offset of the firstname in the + # buffer + pushq %rax + call count_chars + addq $8, %rsp # Cleanup stack + + # Write name to OUTPUT + movq %rax, %rdx # Returned record size, used as argument to + # write() + + movq record_buffer_ptr, %rsi + addq $RECORD_FIRSTNAME, %rsi + movq ST_OUTPUT_DESCRIPTOR(%rbp), %rdi + movq $SYS_WRITE, %rax + syscall + + pushq ST_OUTPUT_DESCRIPTOR(%rbp) + call write_newline + addq $8, %rsp + + jmp record_read_loop + + # Free buffer + pushq record_buffer_ptr + call deallocate +finished_reading: + movq $SYS_EXIT, %rax + movq $0, %rdi + syscall diff --git a/PGU/CHAP9/read_write.s b/PGU/CHAP9/read_write.s new file mode 100644 index 0000000..d574013 --- /dev/null +++ b/PGU/CHAP9/read_write.s @@ -0,0 +1,62 @@ +.include "record-def.s" +.include "linux.s" + +# Read function +# Reads a record from the file descriptor +# and writes it into the buffer passed + +# STACK VARS - Used for both read and write functions. They don't share the +# location, but the arguments are passed in the same position +# for both, so, no need to create different location vars. + +.equ ST_BUFFER, 16 # Ret address is at %rsp+8 +.equ ST_FILEDES, 24 + +.section .text + +.globl read_record +.type read_record, @function + +read_record: + pushq %rbp + movq %rsp, %rbp + + # READ A RECORD + pushq %rdi + movq ST_FILEDES(%rbp), %rdi + movq ST_BUFFER(%rbp), %rsi + movq $RECORD_SIZE, %rdx + movq $SYS_READ, %rax + syscall + + # NOTE: %rax has the return value, which we will give back + # to our caller + + popq %rdi + + movq %rbp, %rsp + popq %rbp + ret + +.globl write_record +.type write_record, @function + +write_record: + pushq %rbp + movq %rsp, %rbp + + # WRITE A RECORD + pushq %rdi + movq ST_FILEDES(%rbp), %rdi + movq ST_BUFFER(%rbp), %rsi + movq $RECORD_SIZE, %rdx + movq $SYS_WRITE, %rax + syscall + + # NOTE: %rax has the return value, which we will give back + # to our caller + popq %rdi + + movq %rbp, %rsp + popq %rbp + ret diff --git a/PGU/CHAP9/record-def.s b/PGU/CHAP9/record-def.s new file mode 100644 index 0000000..9e5274d --- /dev/null +++ b/PGU/CHAP9/record-def.s @@ -0,0 +1,7 @@ +# Define offsets within a record + +.equ RECORD_FIRSTNAME, 0 +.equ RECORD_LASTNAME, 40 +.equ RECORD_ADDRESS, 80 +.equ RECORD_AGE, 320 # Use 8 bytes for age, because it's +.equ RECORD_SIZE, 328 # simpler to deal with whole words diff --git a/PGU/CHAP9/write-newline.s b/PGU/CHAP9/write-newline.s new file mode 100644 index 0000000..e916379 --- /dev/null +++ b/PGU/CHAP9/write-newline.s @@ -0,0 +1,29 @@ +# Just write a newline (\n) to STDOUT + +.include "linux.s" +.type write_newline, @function +.globl write_newline + +.section .data + +newline: + .ascii "\n" + +.section .text + .equ ST_FILEDES, 16 + +write_newline: + pushq %rbp + movq %rsp, %rbp + + movq $SYS_WRITE, %rax + movq ST_FILEDES(%rbp), %rdi + movq $newline, %rsi + movq $1, %rdx + syscall + + movq %rbp, %rsp + popq %rbp + ret + + diff --git a/PGU/OLD/asm/exit.s b/PGU/OLD/asm/exit.s new file mode 100644 index 0000000..b1ef45b --- /dev/null +++ b/PGU/OLD/asm/exit.s @@ -0,0 +1,17 @@ +.section .data + +.section .text + +.globl _start + +_start: + + # %eax should contain syscall number + # to be executed (exit syscall num: 1) + movl $1, %eax + + # %ebx contains syscall argument + movl $5, %ebx + + #Call syscall interrupt + int $0x80 diff --git a/PGU/OLD/asm/max.c b/PGU/OLD/asm/max.c new file mode 100644 index 0000000..5b4c37a --- /dev/null +++ b/PGU/OLD/asm/max.c @@ -0,0 +1,27 @@ +int main(void) +{ + + static int list[] = {10,20,30,40,5,50,60,70,80,90,0}; + int i, ret; + + ret = list[0]; + i = 0; + + /* + for (i = 0; list[i] != 0; i++) { + if (list[i] < ret) + ret = list[i]; + } + */ + while (1) { + i++; + + if (list[i] == 0) + return ret; + + if (ret > list[i]) + ret = list[i]; + } + + return ret; +} diff --git a/PGU/OLD/asm/max.s b/PGU/OLD/asm/max.s new file mode 100644 index 0000000..21f750a --- /dev/null +++ b/PGU/OLD/asm/max.s @@ -0,0 +1,39 @@ + .file "max.c" + .text + .globl main + .type main, @function +main: +.LFB0: + .cfi_startproc + movl $list.1406+8, %ecx + movl $20, %edx + movl $10, %eax +.L2: + cmpl %edx, %eax + cmovg %edx, %eax + addl $4, %ecx + movl -4(%ecx), %edx + testl %edx, %edx + jne .L2 + rep ret + .cfi_endproc +.LFE0: + .size main, .-main + .section .rodata + .align 32 + .type list.1406, @object + .size list.1406, 44 +list.1406: + .long 10 + .long 20 + .long 30 + .long 40 + .long 5 + .long 50 + .long 60 + .long 70 + .long 80 + .long 90 + .long 0 + .ident "GCC: (GNU) 6.2.1 20160916 (Red Hat 6.2.1-2)" + .section .note.GNU-stack,"",@progbits diff --git a/PGU/OLD/asm/maximum b/PGU/OLD/asm/maximum Binary files differnew file mode 100644 index 0000000..4c7b855 --- /dev/null +++ b/PGU/OLD/asm/maximum diff --git a/PGU/OLD/asm/maximum.s b/PGU/OLD/asm/maximum.s new file mode 100644 index 0000000..cb6394c --- /dev/null +++ b/PGU/OLD/asm/maximum.s @@ -0,0 +1,14 @@ +#%edi holds the current position +#%ebx hold the current highest value +#%eax hold the current element being examined + +.section.data data_items: +.long 3, 4, 5, 65, 44, 36, 99, 6, 7, 8, 9, 0 + data_end:.section.text.globl _start + _start:movl $ (data_items), %edi +movl (%edi), %eax +movl % eax, %ebx +start_loop:cmpl $ (data_end), %edi +je loop_exit +addl $4, %edi +movl (%edi), %eax cmpl % ebx, %eax jle start_loop movl % eax, %ebx jmp start_loop loop_exit:movl $1, %eax int $0x80 diff --git a/PGU/OLD/chapter3/exit b/PGU/OLD/chapter3/exit Binary files differnew file mode 100644 index 0000000..a70d0fd --- /dev/null +++ b/PGU/OLD/chapter3/exit diff --git a/PGU/OLD/chapter3/exit.s b/PGU/OLD/chapter3/exit.s new file mode 100644 index 0000000..632ebed --- /dev/null +++ b/PGU/OLD/chapter3/exit.s @@ -0,0 +1,70 @@ +# A useless program, which the only thing this shit does is call exit syscall +# The exit status code can be read using + +# `echo $?` on shell, which will show the exit status of the last program run. + + +# %eax holds the system call number +# %ebx holds the return status + + +# Assembler directives (or pseudo-operations). +# +# Everything starting with a period, is an instruction to the assembler itself. +# They are handled by the assembler program, not actually executed by the +# computer. + +# .section command breaks the program down into sections. + +# .data section lists any memory storage which will be needed for data +.section .data + +# .text section lists the program instructions +.section .text + +# Instructs the assembler that _start is "important to remember". +# _start is a symbol, which is going to be replaced by something else either +# during assembly or linking + +# Symbols are generally used to mark locations of programs or data, so you can +# refer to them by name instead of by location number + +# .globl means that the assembler should not discard this symbol after assembly, +# because the linker will need it. + +# _start is a special symbol that always needs to be marked with .globl, because +# it marks the location of the start of the program. + +# If the program is not marked this way, when the computer loads the program, it +# won't know where to begin running the program. + +.globl _start + +# Defines the value of _start label. Label == a symbol followed by a colon. +# Labels defines a symbol's value. + +# Labels tell the assembler to make the symbol's value be wherever the next +# instruction or data element will be. + +_start: + +# different from the book, I'm using x86_64 instructions and its registers, so, +# instead of using %eax and %ebx, I'm using %rax and %rbx directly. The movl +# instruction needed to be changed to movq (move quad) to adapt to the 64-bit +# registers +# mov: (move) 16-bit +# movl: (move long) 32-bit +# movq: (move quad) 64-bit + +#ins $(val), (dest) == immediate mode +movq $1, %rax # Move value 1 into %eax register + # %eax will hold the syscall number + # by the linux's calling convention + +movq $100, %rbx # Move value 0 into %ebx register + # %ebx will hold the exit() syscall + # argument. + +int $0x80 # Interrupt 0x80 will wake up the kernel + # execute the syscall loaded into %eax + diff --git a/PGU/OLD/chapter3/max.s b/PGU/OLD/chapter3/max.s new file mode 100644 index 0000000..6ebc906 --- /dev/null +++ b/PGU/OLD/chapter3/max.s @@ -0,0 +1,42 @@ +# Given a list of X numbers, find the maximum number of the list and use it as +# the argument of exit() syscall + +# %rbx holds the maximum number through the whole scanning process +# %rax holds the current element being examined +# %rdi holds the currend position in the list +# zero marks the end of the list + +# Data section, now containing some statically created data +.section .data + +# Just a label to refer to the first item in the list +data_items: + # "Type" of memory location to be reserved. In quotes because it just + # says how many bytes should be reserved to each item, not the 'type' + # itself + # Reserves 10 '8byte' (quad) slots consecutive in memory, + .quad 3,10,9,230,66,77,23,66,12,69,0 + +.section .text + +.globl _start + +_start: + movq $0, %rdi + movq data_items(, %rdi,8), %rax + movq %rax, %rbx + + start_loop: + cmpq $0, %rax + je loop_exit + incq %rdi + movq data_items(,%rdi,8), %rax + cmpq %rbx, %rax + jle start_loop + movq %rax, %rbx + jmp start_loop + + + loop_exit: + movq $1, %rax + int $0x80 diff --git a/PGU/OLD/chapter3/min.s b/PGU/OLD/chapter3/min.s new file mode 100644 index 0000000..9179be7 --- /dev/null +++ b/PGU/OLD/chapter3/min.s @@ -0,0 +1,52 @@ +# This code has been originally copied from max.s + +# It has been modified to: +# - Find the smallest value, instead of the largest +# - Use Start/End addresses to delimit the boundaries of the number list +# +# %rax - holds the current element being examined +# %rbx - Holds the smallest number + +.section .data + +# This version has 2 labels, one to point to the start of the list, and another +# to point to the address right after it, so we can use it to search the list +# boundary. + +data_items: + .quad 234,10,9,230,66,77,23,66,101,69,100 +data_end: + +.section .text + +.globl _start + +_start: + +# Using immediate mode with a label, will give you the ADDRESS of the +# instruction/data it points to. Once the label itself contains an ADDRESS, the +# immediate mode will give you the ADDRESS itself. + +# Using direct mode with a label, will give you the DATA into the address the +# label points to. Once the label itself contains an address, the direct mode, +# as expected will give you the DATA into the address pointed by the label. + movq $data_items, %rax + cmpq $data_end, %rax # We need this check here in case + je exit_loop_empty # the items list is empty + movq (%rax), %rbx + + loop_start: + addq $8, %rax # We are using QuadWords, so 8 bytes + cmpq $data_end, %rax + je exit_loop + + cmpq (%rax), %rbx + jle loop_start + movq (%rax), %rbx + jmp loop_start + + exit_loop_empty: + movq $255, %rbx + exit_loop: + movq $1, %rax + int $0x80 diff --git a/PGU/OLD/chapter4/call_power.c b/PGU/OLD/chapter4/call_power.c new file mode 100644 index 0000000..b6bb7e0 --- /dev/null +++ b/PGU/OLD/chapter4/call_power.c @@ -0,0 +1,12 @@ +#include <stdio.h> + +int power(int a, int b); + +int main(void) { + int a = 2; + int b = 6; + + printf("Power == %d\n", power(2, 6)); + + return 0; +} diff --git a/PGU/OLD/chapter4/factorial.s b/PGU/OLD/chapter4/factorial.s new file mode 100644 index 0000000..749f57e --- /dev/null +++ b/PGU/OLD/chapter4/factorial.s @@ -0,0 +1,73 @@ +################################################################## +# Calculate the factorial of a number, using recursive functions # +################################################################## + + +# +# %rbx holds the number to factor +# +.section .data + +.section .text + +.globl _start +_start: + pushq $5 + call factorial2 + addq $8, %rsp # Turn back the stack pointer + # Not really needed here, but good practice + movq %rax, %rbx + movq $1, %rax + int $0x80 + +.type factorial, @function + +# This function is garbage, can be much more clean than this shit. + +factorial: + pushq %rbp # Regular initial function stuff + movq %rsp, %rbp # Save %rbp and reset it to %rsp + + movq 16(%rbp), %rbx # Get the argument passed into %rbx + movq %rbx, %rcx # Copy it to %rcx and decrement it + decq %rcx # here. %rcx will be the next element in the + # factorial. + # + cmpq $1, %rcx # Compare it to 1 and exit if true, we are at + je exit_factorial # the end of the factorial + + pushq %rbx # If not one, save the original argument in the + pushq %rcx # stack and push the next value as argument to + call factorial # the recursive call + popq %rcx # pop value to restore stack pointer + popq %rbx # restore %rbx and multiply it + imul %rax, %rbx # by the result + +exit_factorial: + movq %rbx, %rax # Move the result to %rax + movq %rbp, %rsp # And do standard clean up before + popq %rbp # returning. + ret + +# This is a clean version after extra thought on this + +.type factorial2, @function +factorial2: + pushq %rbp # Basic function setup, save %rbp and + movq %rsp, %rbp # setup it to current %rsp + + movq 16(%rbp), %rax # Get the argument passed into + cmpq $1, %rax # + je factorial2_end # We are at the last element in the factorial + # Just return it + decq %rax + pushq %rax + call factorial2 + movq 16(%rbp), %rbx + imulq %rbx, %rax + +factorial2_end: + movq %rbp, %rsp + popq %rbp + ret + diff --git a/PGU/OLD/chapter4/libpower.s b/PGU/OLD/chapter4/libpower.s new file mode 100644 index 0000000..5172cda --- /dev/null +++ b/PGU/OLD/chapter4/libpower.s @@ -0,0 +1,48 @@ +# Simple example of functions in ASM, following System V ABI +# and C calling convention + +# Everything is stored in registers, so, we have nothing in data section + +#################### +# # +# Function power() # +# # +#################### + +# Remember the return address (where the program should keep executing after +# function return), is also pushed into the stack by the 'call' command. + +# Remember, in x86_64 architecture, the Stack grows 'downwards', so, every time +# we want to look back in the stack, we need to add to the current stack +# pointer, other than subtract. + +# Ex: +# 56 <- stack 'bottom' +# 48 pushq +# 40 pushq +# 32 <- %rsp (top of the stack) + +.type power, @function +power: + pushq %rbp + movq %rsp, %rbp + movq 16(%rbp), %rcx # Holds the Base number. 16 because: + # Current %rsp (pushq %rbp) plus the + # implicit return address pushed by + # the call command + + movq 24(%rbp), %rbx # Holds the power (pushed first before + # function call + movq %rcx, %rax + +loop: + cmpq $1, %rbx + je return + imul %rcx, %rax + decq %rbx + jmp loop + +return: + movq %rbp, %rsp + popq %rbp + ret diff --git a/PGU/OLD/chapter4/power.s b/PGU/OLD/chapter4/power.s new file mode 100644 index 0000000..fe89eae --- /dev/null +++ b/PGU/OLD/chapter4/power.s @@ -0,0 +1,90 @@ +# Simple example of functions in ASM, following System V ABI +# and C calling convention + +# Everything is stored in registers, so, we have nothing in data section +.section .data + +.section .text + +.globl _start + +_start: + pushq $3 # Second argument + pushq $2 # First argument + call power # call function power() + + addq $16, %rsp # Remove arguments from the stack + # Could have popped'em, but we don't + # need them anymore, so, just move + # the stack pointer back + + pushq %rax # Save return value of power() + +# Call power () again + pushq $2 # Push second argument + pushq $5 # Push first argument + call power + + addq $16, %rsp # Move stack pointer back + + popq %rbx # Second answer is already in %eax + # (returned from power), first answer + # was pushed into the stack before, so, + # just pop it into %ebx. + + addq %rax, %rbx # Sum up both results (and %ebx is already the + # exit()'s return value + +# Call exit() + + movq $1, %rax + int $0x80 + + + + +#################### +# # +# Function power() # +# # +#################### + +# Remember the return address (where the program should keep executing after +# function return), is also pushed into the stack by the 'call' command. + +# Remember, in x86_64 architecture, the Stack grows 'downwards', so, every time +# we want to look back in the stack, we need to add to the current stack +# pointer, other than subtract. + +# Ex: +# 56 <- stack 'bottom' +# 48 pushq +# 40 pushq +# 32 <- %rsp (top of the stack) + +#Tread symbol 'power' as a function +.type power,@function + +power: + pushq %rbp + movq %rsp, %rbp + movq 16(%rbp), %rcx # Holds the Base number. 16 because: + # Current %rsp (pushq %rbp) plus the + # implicit return address pushed by + # the call command + + movq 24(%rbp), %rbx # Holds the power (pushed first before + # function call + movq %rcx, %rax + +loop: + cmpq $1, %rbx + je return + imul %rcx, %rax # The result is already stored in %rax + decq %rbx + jmp loop + +return: + movq %rbp, %rsp + popq %rbp + ret |
