# 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, 900 .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 jle 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 call convert_to_base64 # 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 #Actual function which does the converting. Takes in 3 bytes of input and changes them to 4 bytes according to the base64 lookup table. convert_to_base64: movq %rdx, %r8 #copy 6 bits into rsi salq $6, %rdx #delete 6 bits being processed from source andq $0b0000000000000000000000000111111, %r8 #keep only last six bits convert_loop: next_byte: end_convert_loop: movq %rdi, %rax # Store number of chars converted into RAX as return value popq %rbx movq %rbp, %rsp popq %rbp ret