Relearning MSX #28: Keyboard inputPosted by Javi Lavandeira in How-to, MSX, Retro | October 06, 2015
In the previous two posts we learnt how to operate with values stored inside variables in our program.
This time we’re going to see how to read characters and text strings from the keyboard in MSX-C.
Reading a single key press: getch()
MSX-C comes with a few functions to read a single character from the keyboard, but probably the easiest to understand is getch(). Let’s see an example:
If you compile and run this program you’ll see that the computer displays a cursor and nothing happens. This is because the program is waiting for you to press a key. Go ahead and press any key on the keyboard. The program immediately prints the message The key you pressed is [x], with x being the corresponding character:
Please note that getch() by itself doesn’t print on the screen the character it read from the keyboard. We say that this function doesn’t echo.
getch() reads a single character from the keyboard. Unlike the other C functions we’ve seen so far, getch()‘s return value has meaning. For example, we can assign it to a char variable to use it later, as we did in the program above:
c = getch();
In BASIC we used to call functions those keywords that return a value, and statements those that don’t. In C there’s no such distinction: all of them are functions and all functions return a value. However, often we don’t care about the value returned. For example, the putchar() and puts() functions we’ve seen already return do return a value (0) when they succeed in printing to the screen. It is very rare that these two functions will fail to output a character (the only case I can think of is if the output is redirected to a file on disk, and the disk happens to be full or write-protected), so we usually just ignore the return value. However, we can still assign the return value to a variable in order to test for errors if we want:
error = putchar(c);
As we saw in the previous post, a char variable contains the ASCII code of a character. Since this is just a number we can perform operations directly with the value returned by getch(). For example, we can write a program to print the next character to the one entered on the keyboard:
This program does the same thing as the previous one, but it adds 1 to the character code returned by getch(), so it prints the next character. For example, if we run GETCH2.COM three times and enter the letters M, S and X we get this result:
Another function to read a single character from the keyboard is getche(). It works exactly like getch(), but it echoes back to the screen the character entered:
In lesson #17 we saw that MSX-DOS (1) doesn’t support redirection or chaining commands with the pipe character. Because of this, getch() and getche() behave differently in MSX-C Ver.1.1 and MSX-C Ver.1.2:
- The MSX-C Ver.1.1 version of getch()/getche() always check the keyboard, regardless of whether the input is redirected or not. Because of this we can’t chain the input of an MSX-C Ver.1.1 program to read commands from a file.
- The MSX-C Ver.1.2 versions of getch()/getche() do not have this restriction. They will happily read characters from a file or device if the input is redirected.
The issue with I/O buffering
When programming under MSX-C Ver.1.2 there’s an issue we’ll have to be aware of: I/O buffering. This doesn’t affect MSX-C Ver.1.1, but if that’s the version you’re using you still want to read this section, because it’s something that pops up often when programming in C.
To illustrate the problem let’s look at the following program:
Looking at the source code we expect that the program will first print the string “INPUT> ” on the screen and then wait for us to press a key on the keyboard. That’s exactly what would happen under MSX-C Ver.1.1.
However, under MSX-C Ver.1.2 this is what we get:
As you saw in the video, the program waits for us to press a key, and then it prints the input prompt, followed by the output message. It’s as if the first puts() and the getch() where in the opposite order.
The reason why this happens is that MSX-C Ver.1.2 uses output buffering.
The output buffering mechanism consists on storing in a temporary memory area (the buffer) the characters before sending them to the screen or disk. The data is sent all together to the device whenever the buffer becomes full or it receives a newline character. This diagram illustrates the buffering process:
Note that for simplicity, the diagram above the buffer holds only four characters, while the actual MSX-C buffer holds 1024 bytes.
Buffering helps increase the performance of our programs because it helps group many characters in a single input/output operation, instead of writing characters one by one (each requiring its own I/O call). However, there are times when we need to control exactly when the characters will be sent to the device, as in the example program above.
There are two ways of dealing with this situation in MSX-C Ver.1.2: disabling I/O buffering and forcing the program to empty the output buffer.
The first way uses the setbuf() library function. We will see it with more detail in a future post, but for now remember that we can disable buffering by calling it like this at the beginning of our program:
stdout is the standard output, which is usually the screen unless the output of the program is redirected somewhere else. NULL is a null pointer. We will talk more about this when we discuss pointers. For now just remember that this will completely disable output buffering.
Let’s modify the previous program and see how this works:
Compile and run this program to see that now the output behaves as expected: the INPUT> prompt appears on the screen and then the program waits for us to press a key:
This works, but it’s not always a solution because without the buffer, printing to the screen will in many cases be slower.
The other way to handle printing to the screen while buffering is enabled is to use the fflush() library function. Calling this function will force the program to send the contents of the output buffer to the screen the moment we call this function. Like before, we have to tell fflush() that we want to flush the standard output:
Note that fflush() isn’t available in MSX-C Ver.1.1 because that version doesn’t use I/O buffering.
Unlike setbuf(), which we only called once at the beginning of our program, we need to call fflush() every time we want to flush the output buffer, as in this example:
Also, the I/O buffers are automatically flushed then the program ends, so there’s no need to add another fflush() at the end.
String input: gets()
We’ve seen how to read text from the keyboard one character at a time using the getch() and getche() functions. To read strings of text we use the function gets(). The following program is a simple example using gets(): it shows a cursor and waits until we enter some text, then prints it on the screen:
Try it: compile and run the program, then type some text and press ENTER:
One very important thing to remember is that C doesn’t actually have a text string type. Text strings are just arrays of single characters. This is why at the beginning of the program we prepare an array of char values that can hold up to 200 characters:
The gets() function takes two parameters: the first one indicates where the program should store the characters read from the keyboard, and the second one indicates the maximum number of characters to read. For example, the following line from the program above:
means “read a string of at most 200 characters and store them in array s“. This ensures that the data read from the keyboard fits inside the array. If we enter a string longer than allowed then the extra characters will be discarded. This example illustrates this:
If you’ve been paying attention you probably noticed that in the example above we tell gets() to store 10 characters, but the string printed on the screen, this_is_a, contains only 9. The reason for this is that text strings in C always end in a special character to mark the end of the string. This terminator counts as the last character of the string, and i’s not printed on the scrren. Without it, puts() wouldn’t know where the string ends and it would print garbage on the screen. We’ll learn more about this when we look at character arrays in more detail.
We’ve covered some ground already. We’ve seen how to read characters from the keyboard using getch() and getche(). We have learned what I/O buffering is and we’ve learnt to control it with the setbuf() and fflush() library functions. Finally, we’ve learnt how to use the gets() function to input character arrays.
In the next post…
This is where things start getting interesting. Next we’re going to see how to use conditionals, loops and other control structures. Stay tuned!
This series of articles is supported by your donations. If you’re willing and able to donate, please visit the link below to register a small pledge. Every little amount helps.
Javi Lavandeira’s Patreon page
Nice article again. Is there a particular reason you record videos with a screengrabber, instead of the built-in video recorder of openMSX? (Which produces AVI’s which YouTube happily accepts.)
I did record some of my early videos with openMSX’s built-in video recorder (for example, https://www.youtube.com/watch?v=1TuF-puqcq4). However, I’m not using it anymore because the video output doesn’t match what’s actually displayed on the screen:
– I have set my emulator screen to be zoomed at 4x, but the video recorder only supports 1x, 2x or 3x
– No scanlines. My emulator is set up to resemble a CRT display, but the built-in video recorder seems to save emulator frames before effects such as scalines are applied. This makes the resulting video look rough.