# PURPOSE: This program converts an input text file to an output text file with the base64 encoding.
#
# PROCESSING: 1) Open the input file
# 2) Open the output file
# 4) While we're not at the end of the input file
# a) read part of the file into our piece of memory
# b) go through each byte of memory
# convert to base64
# c) write the piece of memory to the output file
.section .data #Data storage
base64: #Lookup table
.ascii "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\0"
#######CONSTANTS########
# System call numbers
.equ SYS_OPEN, 2
.equ SYS_READ, 0
.equ SYS_WRITE, 1
.equ SYS_CLOSE, 3
.equ SYS_EXIT, 60
# Options for open (look at /usr/include/asm/fcntl.h for
# various values. You can combine them
# by adding them)
.equ O_RDONLY, 0 # Open file options - read-only
.equ O_CREAT_WRONLY_TRUNC, 03101 # Open file options - these options are:
# CREAT - create file if it doesn't exist
# WRONLY - we will only write to this file
# TRUNC - destroy current file contents, if any exist
.equ O_PERMS, 0666 # Read & Write permissions for everyone
# End-of-file result status
.equ END_OF_FILE, 0 # This is the return value of read() which
# means we've hit the end of the file
#######BUFFERS#########
.section .bss
# This is where the data is loaded into from
# the data file and written from into the output file.
# It should never exceed 16,000 for various reasons.
.equ BUFFER_SIZE_INPUT, 600
.lcomm BUFFER_DATA_INPUT, BUFFER_SIZE_INPUT
.equ BUFFER_SIZE_OUTPUT, 1000
.lcomm BUFFER_DATA_OUTPUT, BUFFER_SIZE_OUTPUT
#######PROGRAM CODE###
.section .text
# STACK POSITIONS
.equ ST_SIZE_RESERVE, 16 # Space for local variables
# Note: Offsets are RBP-based, which is set immediately at program start
.equ ST_FD_IN, -16 # Local variable for input file descriptor
.equ ST_FD_OUT, -8 # Local variable for output file descriptor
.equ ST_ARGC, 0 # Number of arguments
.equ ST_ARGV_0, 8 # Name of program
.equ ST_ARGV_1, 16 # Input file name
.equ ST_ARGV_2, 24 # Output file name
.globl _start
_start:
###INITIALIZE PROGRAM###
movq %rsp, %rbp
subq $ST_SIZE_RESERVE, %rsp # Allocate space for our file descriptors on the stack
###CHECK PARAMETER COUNT###
cmpq $3, ST_ARGC(%rbp)
je open_files
movq $-1, %rdi # Our return value for parameter problems
movq $SYS_EXIT, %rax
syscall
open_files:
open_fd_in:
###OPEN INPUT FILE###
movq ST_ARGV_1(%rbp), %rdi # Input filename into %rdi
movq $O_RDONLY, %rsi # Read-only flag
movq $O_PERMS, %rdx # This doesn't really matter for reading
movq $SYS_OPEN, %rax # Specify "open"
syscall # Call Linux
cmpq $0, %rax # Check success
jl exit # In case of error simply terminate
store_fd_in:
movq %rax, ST_FD_IN(%rbp) # Save the returned file descriptor
open_fd_out:
###OPEN OUTPUT FILE###
movq ST_ARGV_2(%rbp), %rdi # Output filename into %rdi
movq $O_CREAT_WRONLY_TRUNC, %rsi # Flags for writing to the file
movq $O_PERMS, %rdx # Permission set for new file (if it's created)
movq $SYS_OPEN, %rax # Open the file
syscall # Call Linux
cmpq $0, %rax # Check success
jl close_input # In case of error close input file (already open!)
store_fd_out:
movq %rax, ST_FD_OUT(%rbp) # Store the file descriptor
###BEGIN MAIN LOOP###
read_loop_begin:
###READ IN A BLOCK FROM THE INPUT FILE###
movq ST_FD_IN(%rbp), %rdi # Get the input file descriptor
movq $BUFFER_DATA_INPUT, %rsi # The location to read into
movq $BUFFER_SIZE_INPUT, %rdx # The size of the buffer
movq $SYS_READ, %rax
syscall # Size of buffer read is returned in %eax
###EXIT IF WE'VE REACHED THE END###
cmpq $END_OF_FILE, %rax # Check for end of file marker
jle end_loop # If found (or error), go to the end
movq $0, %rcx # Registry for counting data
continue_read_loop:
###CONVERT TO BASE64###
addq $3, %rcx #adds 3 to rcx to check data bounds
cmpq %rcx, %rax #compares counter to actual data to check bounds
jge end_loop #jumps to end if counter exceeds data
subq $3, %rcx #subtracts rcx to keep counter accurate
movq $BUFFER_DATA_INPUT, %rdx # moves input data address to rdx
#move byte 1
movb 0(%rdx,%rcx,1), %dil # moves actual data to dil
incq %rcx # increment pointer
shlq $8, %rdi # moves rdi by one pos so data doesn't get overwritten
#move byte 2
movb 0(%rdx,%rcx,1), %dil # moves actual data to dil
incq %rcx # increment pointer
shlq $8, %rdi # moves rdi by one pos so data doesn't get overwritten
#move byte 3
movb 0(%rdx,%rcx,1), %dil # moves actual data to dil
incq %rcx # increment pointer
pushq $-1 # Dummy value for stack alignment
pushq %rax # Store number of bytes read for write check
call convert_to_upper # Note: RAX may (will) be destroyed (caller-safe!),
# but will be returned identically as return value
###WRITE THE BLOCK OUT TO THE OUTPUT FILE###
movq ST_FD_OUT(%rbp), %rdi # File to use
movq $BUFFER_DATA_OUTPUT, %rsi # Location of buffer
movq %rax, %rdx # Size of buffer (=number of bytes actually read/converted)
movq $SYS_WRITE, %rax
syscall
###CHECK WRITE SUCCESS###
popq %rbx # Retrieve number of bytes read
addq $8, %rsp # Remove stack alignment space
cmpq %rax, %rbx # Compare number read to written
jne end_loop # If not the same, terminate program
###CONTINUE THE LOOP###
jmp read_loop_begin
end_loop:
###CLOSE THE FILES###
# NOTE - we don't need to do error checking on these, because
# error conditions don't signify anything special here
# and there is nothing for us to do anyway
movq ST_FD_OUT(%rbp), %rdi
movq $SYS_CLOSE, %rax
syscall
close_input:
movq ST_FD_IN(%rbp), %rdi
movq $SYS_CLOSE, %rax
syscall
exit:
###EXIT###
movq $0, %rdi # Standard return value for all cases
movq $SYS_EXIT, %rax
syscall
#####FUNCTION convert_to_upper
#
#PURPOSE: This function actually does the conversion to upper case for a block
#
#INPUT: The first parameter (rdi) is the location of the block of memory to convert
# The second parameter (rsi) is the length of that buffer
#
#OUTPUT: This function overwrites the current buffer with the upper-casified
# version.
# Return value: The number of bytes converted (=content of RBX), as
# nothing in here can happen to stop/interrupt this.
#
#VARIABLES:
# %rax - beginning of buffer
# %rbx - length of buffer (old value must be saved!)
# %rdi - current buffer offset
# %r10b - current byte being examined (%r10b is the first byte of %r10)
# Note: This variable assignment is for exemplary purposes only and very suboptimal!
#
###CONSTANTS##
.equ LOWERCASE_A, 'a' # The lower boundary of our search
.equ LOWERCASE_Z, 'z' # The upper boundary of our search
.equ UPPER_CONVERSION, 'A' - 'a' # Conversion between upper and lower case
convert_to_upper:
pushq %rbp # Prepare stack
movq %rsp, %rbp
pushq %rbx # Save RBX
###SET UP VARIABLES###
movq %rdi, %rax
movq %rsi, %rbx
movq $0, %rdi
# If a buffer with zero length was given us, just leave
cmpq $0, %rbx
je end_convert_loop
convert_loop:
# Get the current byte
movb (%rax,%rdi,1), %r10b
# Go to the next byte unless it is between 'a' and 'z'
cmpb $LOWERCASE_A, %r10b
jl next_byte
cmpb $LOWERCASE_Z, %r10b
jg next_byte
# Otherwise convert the byte to uppercase
addb $UPPER_CONVERSION, %r10b
# And store it back
movb %r10b, (%rax,%rdi,1)
next_byte:
incq %rdi # Next byte
cmpq %rdi, %rbx # Continue unless we've reached the end
jne convert_loop
end_convert_loop:
movq %rdi, %rax # Store number of chars converted into RAX as return value
popq %rbx
movq %rbp, %rsp
popq %rbp
ret
Comments
0 B
|👍
/👎
0 B
|0 👍
/0 👎