Part 20 - Debugging Input
Today we will debug our input function. Let's review our code.
Review input.c as follows.
#include <stdio.h> #include <string.h> #include "pico/stdlib.h" #define ZERO 0x30 #define NINE 0x39 #define PERIOD 0x2e #define CAPITAL_A 0x41 #define LOWER_CASE_Z 0x7a #define BACKSPACE 0x08 #define DEL 0x7f void input_proc(char type, char* p_usb_char, char* p_usb_string, const int* p_USB_STRING_SIZE) { *p_usb_char = '\0'; *p_usb_char = getchar_timeout_us(0); if(*p_usb_char == BACKSPACE || *p_usb_char == DEL) { if(p_usb_string[0] != '\0') { printf("\b"); printf(" "); printf("\b"); p_usb_string[strlen(p_usb_string)-1] = '\0'; } } if(type == 'f') { char* period; while((*p_usb_char >= ZERO && *p_usb_char <= NINE) || *p_usb_char == PERIOD) { if(*p_usb_char == PERIOD) period = strchr(p_usb_string, '.'); if(period == NULL) { if(strlen(p_usb_string) < *p_USB_STRING_SIZE) { putchar(*p_usb_char); strncat(p_usb_string, p_usb_char, 1); } *p_usb_char = '\0'; } else break; } } else if(type == 'd') { while(*p_usb_char >= ZERO && *p_usb_char <= NINE) { if(strlen(p_usb_string) < *p_USB_STRING_SIZE) { putchar(*p_usb_char); strncat(p_usb_string, p_usb_char, 1); } *p_usb_char = '\0'; } } else if(type == 's') { while(*p_usb_char >= CAPITAL_A && *p_usb_char <= LOWER_CASE_Z) { if(strlen(p_usb_string) < *p_USB_STRING_SIZE) { putchar(*p_usb_char); strncat(p_usb_string, p_usb_char, 1); } *p_usb_char = '\0'; } } } void flush_input(char* p_usb_string) { p_usb_string[0] = '\0'; }
Review our print.c as follows.
#include <stdio.h> #include "pico/stdlib.h" #include "input.h" #define RETURN 0x0d void print_proc(char* p_usb_char, char* p_usb_string) { if(*p_usb_char == RETURN) { if(p_usb_string[0] == '\0') printf("\n"); else printf("\n%s\n", p_usb_string); flush_input(p_usb_string); } }
Review our main.c as follows.
#include <stdio.h> #include "pico/stdlib.h" #include "print.h" #include "input.h" int main() { stdio_init_all(); const int USB_STRING_SIZE = 100; char usb_char; usb_char = '\0'; char usb_string[USB_STRING_SIZE]; usb_string[0] = '\0'; while(1) { input_proc('f', &usb_char, usb_string, &USB_STRING_SIZE); print_proc(&usb_char, usb_string); } return 0; }
Let's fire up in our debugger.
radare2 -w arm -b 16 main.elf
Let's auto analyze.
aaaa
Let's seek to main.
s main
Let's go into visual mode by typing V and then p twice to get to a good debugger view.
We first review main.
We see our stdio_init_all call which sets up IO and we see a 0x64 into r3 which is our move of 100 decimal to set USB_STRING_SIZE _and we set up our _usb_char value and init to 0 and finally usb_string and init to 0.
Let's look at our print_proc function.
We first check to see if our pointer to usb_char or p_usb_char is equal to the RETURN key or 0xd and if so branch.
We then iterate over p_usb_string until we hit the null terminator and then call our _printf _function which as we can see here is a wrapper to the c printf function.
We finally flush_input.
Our input_proc function is a bit more complex.
Here we use the getchar_timeout_us function and handle the BACKSPACE and DELETE keys.
We then call our putchar _wrapper against 0 and _9 and check the strlen and properly build our string with strncat.
We then properly handle our PERIOD logic to ensure only one _PERIOD _is entered as a floating point number can NOT handle 2 periods.
We then then properly handle our loop.
Finally, we have our flush_input function.
Here we simply flush the input buffer by setting p_usb_string to a null char.
This was a larger debug session so please take your time and compare the assembly against the source so you can really grasp each paragraph as I cover it here.
This brings us to the end of our initial learning journey. In this journey we took 197 steps together through several different architectures. It is your turn to take this training into practice and do great things!
This book will be your reference guide as you encounter challenges however there is nothing you can't accomplish!