# 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