; Version : 1.00 ; Updated : 20/8/2021 ; Licence : GPLv3 ; Set the case of ASCII text to upper or lower case. default rel ; we always want this. extern printf %define argv0 r12 %define upper r13 ;make r13 the boolean %define scratch r14 %define file r15 section .rodata Usage db `%s OPTION [filename]\n Option Description\n-u Make characters uppercase.\n-l Make characters lowercase.\n`,0 OpenError: db `Could not open: %s\n`,0 section .text global main usage: push rbx ;align the stack if not already done mov rsi, [argv0] ;get argv[0] lea rdi, [rel Usage] ;copy the usage to rdi xor eax,eax ;al == 0 means no FP args in XMM registers call printf wrt ..plt ;call printf with `wrt ..plt` so dynamic linking works pop rbx ;pop rbx off the stack mov eax, 1 ;exit with value 1 ret stdin: xor edi, edi ; file descriptor = stdin = 0 lea rsi, [scratch] ; buffer = address to store the bytes read mov edx, 1 ; number of bytes to read xor eax, eax ; syscall for read == 0 syscall test eax, eax ; If syscall returns 0 in rax, the character is EOF je exit ; if char is EOF: exit call process_char jmp stdin process_char: test upper,upper jz lower ;jump to lower if upper is false ;fall through if uppercase cmp byte[scratch],'a' ;check if read char is below 'a' jl skip_modification ;if below 'a', it's not a lowercase char cmp byte[scratch],'z' ;check if read char is equal to or below z ja skip_modification ;if not equal or above, it's not a lowercase char sub byte[scratch], 32 ;subtract 32 from ASCII char to capitalise it jmp skip_modification ;skip lowercase lower: cmp byte[scratch],'A' ;check if read char is below 'A' jl skip_modification ;if below 'A', it's not a uppercase char cmp byte[scratch],'Z' ;check if read char is equal to or below 'Z' ja skip_modification ;if not equal or above, it's not a lowercase char add byte[scratch], 32 ;add 32 to ASCII char to make it lowercase skip_modification: mov edi, 1 ; file descriptor = stdout lea rsi, [scratch] ; buffer = address to write to console mov edx, 1 ; number of bytes to write mov eax, 1 ; SYSCALL number for writing to STDOUT syscall ; make the syscall ret ; read the next char file_error: push rbx ;align the stack if not already done mov rsi, scratch ;get argv[2] lea rdi, [rel OpenError];copy the filename to rdi xor eax,eax ;al == 0 means no FP args in XMM registers call printf wrt ..plt ;call printf with `wrt ..plt` so dynamic linking works pop rbx ;pop rbx off the stack mov eax, 1 ;exit with value 1 ret open_file: add rsi, 8 ;get pointer-to-pointer to argv[2] mov scratch,[rsi] ;get pointer to argv[2] and copy it to scratch mov eax, 2 ;syscall sys_open mov rdi, scratch ;place pointer to filename string in rdi xor esi, esi ;O_RDONLY xor edx, edx ;don't need a mode syscall test eax, eax js file_error ;if nonzero, sys_open failed mov file, rax ;store fp in file read_file: mov rdi, file ;file descriptor = rax from sys_open above lea rsi, [scratch] ;read into scratch mov edx, 1 ;number of bytes to read xor eax, eax ;syscall for read == 0 syscall test eax, eax ;If syscall returns 0 in rax, the character is EOF je close_file ;if char is EOF, close the file and exit call process_char jmp read_file main: mov argv0, rsi ;save argv[0] for later cmp edi, 2 jl usage ;show usage if not enough args are provided check_args: add rsi, 8 ;get pointer-to-pointer to argv[1] mov scratch, [rsi] ;get pointer to argv[1] and copy it to scratch cmp byte[scratch], '-' jne usage ;show usage if there is no argument (missing -) xor upper, upper ;assume lowercase by default cmp byte[scratch+1], 'u' jne false ;fall through if argv[1][1] == 'u' and thus set upper boolean to true mov upper,1 false: cmp edi, 3 jl stdin ;go to stdin mode if no filename is provided jg usage ;go to usage if more that 3 args are provided jmp open_file ;jump to file opening close_file: mov rdi, file ;fp to close mov eax, 3 ;sys_close syscall exit: xor eax, eax ret