Script started on Thu 04 Apr 2024 09:44:50 PM EDT [mweeks@gsuad.gsu.edu@snowball code]$ cat third_word.asm ; Assemble: nasm -f elf64 third_word.asm ; Link: gcc third_word.o -o third_word ; echo "1 two 3 four" | ./third_word ; ; Example of reading input. ; This program reads the input (stdin) and outputs the 3rd word. ; For example, if the input is ; one two three four five ; then this program will output ; three ; ; ASSUMPTION: A space is either the space character, a TAB, or a newline. ; ASSUMPTION: If the first character is a space, then we consider ; the first word has 0 characters. ; E.g., " one two" will be interpreted as a null first word,, ; followed by "one" then a space, ; and "two" would be third word. ; ASSUMPTION: only 1 space character will be between words. ; E.g., "one two" will be interpreted as "one" (space) (space) "two" ; and "two" would be third word. ; We could instead have a flag that stores whether or not the last ; char was a space, and not advance the count if so. ; ; ; -MCW, Spring 2024 MAXLEN equ 1000 ; Maximum allowed string size (bytes) TWO_SPACES equ 2 ; Number of spaces to find in initial scan. ; Have this defined here in case we need to ; find the 4th word in the future: if so, ; simply change this. extern printf ; We will use this external function extern puts ; We will use this external function ; see https://codereview.stackexchange.com/questions/248569/simple- ; input-and-output-in-assembly-x86-64 ; Start of "codereview" example SYS_READ equ 0 ; read text from stdin SYS_WRITE equ 1 ; write text to stdout STDIN equ 0 ; standard input STDOUT equ 1 ; standard output ; End of "codereview" example TAB equ 9 NL equ 10 section .bss ; Uninitialized data section third: resb MAXLEN ; place to store the third word. mybuffer: resb 2 ; place to store the input section .data ; Data section, initialized variables third_len: dd 0 ; length of "third" buffer num_read: dd 0 ; length of the input read in last syscall space_count: dw 0 ; Number of spaces found third_test: db "Third", 0 ; Used for testing read_a_char_str: db "Read a char", 0 read_a_char_str1: db " Read char %c", 0 read_a_char_str2: db "(%d)", 10, 0 space_count_str: db " %d Spaces so far", 10, 0 found_space_str: db "Found a space", 10, 0 start_pt1_str: db "Start part 1", 0 start_pt2_str: db "Start part 2", 0 section .text global main main: push rbp ; set up stack frame mov rbp,rsp ; print "Start part 1" mov rdi, start_pt1_str call puts xor rax, rax ; set A = 0 ; ******************************************************** ; Part 1 - read input until the start of the 3rd word ; ******************************************************** get_next: ;mov rdi, read_a_char_str ; string to print ;call puts ; Read 1 char from stdin mov rdx, 1 ; characters to read mov rsi, mybuffer ; where to store chars read mov rdi, STDIN ; fd of input mov rax, SYS_READ ; function to call syscall ; The char read will be in "mybuffer". ; Register A will hold the number of chars read. ; Since we just asked to read 1 char, A should be 1, ; but it might be 0 if there is no more input. mov [num_read], eax ; number of chars read cmp eax, 0 ; Did we read 0 chars? je exit_code ; if we read 0 chars, we got EOF, so exit ; It's good to see that it is reading the input correctly. ; Since the number of chars read and the char(s) themselves ; are stored in memory, we can print them here. call print_char_as_int ; Is it a space? If so, advance the space_count. ; Count a TAB or NL as a space, too. xor rbx, rbx ; B = 0 mov bl, byte [mybuffer] cmp bl, ' ' ; Is it a space? je advance_space_count cmp bl, TAB ; Is it a TAB? je advance_space_count cmp bl, NL ; Is it a Newline? je advance_space_count jmp get_next advance_space_count: ; We found a space, so remember this. mov rdi, found_space_str ; string to print call puts ; Also, see if we have already seen 2 spaces mov ax, [space_count] ; get current value inc ax ; add 1 mov [space_count], ax ; store it back ;mov rdi, space_count_str ; Format of the string to print ;xor rax, rax ;mov ax, [space_count] ; A = count of spaces ;mov rsi, rax ; Value to print ;xor rax, rax ; A = 0 ;call printf mov ax, [space_count] ; get current value cmp ax, TWO_SPACES ; Is it == TWO_SPACES? je start_part_2 jmp get_next ; ******************************************************** ; Part 2 - scan the word, store it, look for next space ; ******************************************************** start_part_2: ; print "Start part 2" mov rdi, start_pt2_str call puts get_next_pt2: ; Read 1 char from stdin mov rdx, 1 ; characters to read mov rsi, mybuffer ; where to store chars read mov rdi, STDIN ; fd of input mov rax, SYS_READ ; function to call syscall ; The char read will be in "mybuffer". ; Register A will hold the number of chars read. ; Since we just asked to read 1 char, A should be 1, ; but it might be 0 if there is no more input. mov [num_read], eax ; number of chars read cmp eax, 0 ; Did we read 0 chars? je exit_code ; if we read 0 chars, we got EOF, so exit ; It's good to see that it is reading the input correctly. ; Since the number of chars read and the char(s) themselves ; are stored in memory, we can print them here. call print_char_as_int ; Put the char that we read into the string ; i.e. copy from mybuffer to third. ; mybuffer should only have the 1 char. ; But we will need to index into the third array. mov bl, [mybuffer] mov eax, [third_len] mov [third + eax], bl ; Increment the length of third ;mov eax, [third_len] inc eax mov [third_len], eax ; Is the char a space? cmp bl, ' ' ; Is it a space? je found_end_of_word cmp bl, TAB ; Is it a TAB? je found_end_of_word cmp bl, NL ; Is it a Newline? je found_end_of_word ; Is the length too much? cmp eax, MAXLEN je found_end_of_word jmp get_next_pt2 found_end_of_word: ; third_len has the length of the string. ; third will have a space in it, i.e. third[third_len-1] == space ; Replace the space with a 0 mov eax, [third_len] dec eax mov [third_len], eax ; store it back in case we add code in future. mov [third + eax], byte 0 ;mov rdi, third_test ; string to print mov rdi, third ; string to print call puts exit_code: mov rsp, rbp ; takedown stack frame pop rbp ; same as "leave" op mov rax, 0 ret ; Print the character in mybuffer as char, and as decimal. ; Thus, we print it twice. print_char_as_int: mov rdi, read_a_char_str1 ; Format of the string to print xor rax, rax mov al, byte [mybuffer] ; A = char read from earlier mov rsi, rax ; Value to print xor rax, rax ; A = 0 call printf mov rdi, read_a_char_str2 ; Format of the string to print xor rax, rax mov al, byte [mybuffer] ; A = char read from earlier mov rsi, rax ; Value to print xor rax, rax ; A = 0 call printf ret [mweeks@gsuad.gsu.edu@snowball code]$ nasm -f elf64 third_word.asm [mweeks@gsuad.gsu.edu@snowball code]$ gcc third_word.o -o third_word [mweeks@gsuad.gsu.edu@snowball code]$ cat third_test1.txt one two three four five [mweeks@gsuad.gsu.edu@snowball code]$ cat third_test2.txt the quick brown fox jumps over the lazy dog [mweeks@gsuad.gsu.edu@snowball code]$ ./third_word < third_test1.txt Start part 1 Read char o(111) Read char n(110) Read char e(101) Read char (32) Found a space Read char t(116) Read char w(119) Read char o(111) Read char (32) Found a space Start part 2 Read char t(116) Read char h(104) Read char r(114) Read char e(101) Read char e(101) Read char (32) three [mweeks@gsuad.gsu.edu@snowball code]$ ./third_word < third_test2.txt Start part 1 Read char t(116) Read char h(104) Read char e(101) Read char (10) Found a space Read char q(113) Read char u(117) Read char i(105) Read char c(99) Read char k(107) Read char (10) Found a space Start part 2 Read char b(98) Read char r(114) Read char o(111) Read char w(119) Read char n(110) Read char (10) brown [mweeks@gsuad.gsu.edu@snowball code]$ echo "1 two 3 four" | ./third_word Start part 1 Read char 1(49) Read char (32) Found a space Read char t(116) Read char w(119) Read char o(111) Read char (32) Found a space Start part 2 Read char 3(51) Read char (32) 3 [mweeks@gsuad.gsu.edu@snowball code]$ exit exit Script done on Thu 04 Apr 2024 09:47:28 PM EDT