# 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