Implementing Operating System #3

Tharushi Chamalsha
5 min readAug 6, 2021

From this chapter, we are going to display some text on the console and write data to the serial port. First, we will create a driver for the frame buffer to be able to display text on the console.

What is a frame buffer?

The frame buffer is a hardware device that is capable of displaying a buffer of memory on the screen. The frame buffer has 80 columns and 25 rows, and the row and column indices start at 0 (so rows are labeled 0–24).

Displaying the text

Writing text to the console via the frame buffer is done with memory-mapped I/O. Writing to the frame buffer can also be done in C by treating the address 0x000B8000 as a char pointer. The following code shows the function to write the text in the console. You can create a separate file called writer.h and store the function inside the file.

The above function should call in the kmain.c file. You have to include writer.h file to kmain.c file. Otherwise, you will get an error.

fb_write_cell(0, 'A', FB_GREEN, FB_DARK_GREY);

Now you can run the os by executing make run code. Then you can see the character shown below in the console.

Now you can try to display a retire sentence. In order to do that you can use a function with a for loop as below. Simply copy the function and paste it inside the writer.h file.

Then you can call the function in kmain.c file using the following code segment. You can use any text inside the pointer array.

 char ptr1[]="Hello! Welcome to MoonOS";
fb_write(24,ptr1);

Then run the os and you will see the text like below. And boom !! you got the text in your console.

Move the cursor

Moving the cursor of the frame buffer is done via two different I/O ports. The cursor’s position is determined with a 16 bits integer.To set the cursor to row one you can use the following function. Store it inside a file called io.s.

global outb             ; make the label outb visible outside this file

; outb - send a byte to an I/O port
; stack: [esp + 8] the data byte
; [esp + 4] the I/O port
; [esp ] return address
outb:
mov al, [esp + 8] ; move the data to be sent into the al register
mov dx, [esp + 4] ; move the address of the I/O port into the dx register
out dx, al ; send the data to the I/O port
ret ; return to the calling function

Then paste the below function inside io.h file.

#ifndef INCLUDE_IO_H
#define INCLUDE_IO_H

/** outb:
* Sends the given data to the given I/O port. Defined in io.s
*
* @param port The I/O port to send the data to
* @param data The data to send to the I/O port
*/
void outb(unsigned short port, unsigned char data);

#endif /* INCLUDE_IO_H */

Here we created the io.h and io.s. But how we will link it to link.ls file? You just need to add one word in the Makefile and you are all set. Your make file should look like below.

Then you can use the below function and paste it inside the writer.h.

Then simply call the function in kmain.c file.

fb_move_cursor(00);

Then you can run the os and there you can see the cursor moved to the starting point of the console.

Writing data to the serial port.

What is a serial port?

The serial port is an interface for communicating between hardware devices and although it is available on almost all motherboards. The serial port is easy to use, and, more importantly, it can be used as a logging utility in Bochs. If a computer has support for a serial port, then it usually has support for multiple serial ports, but we will only make use of one of the ports. This is because we will only use the serial ports for logging. Furthermore, we will only use the serial ports for output, not input. The serial ports are completely controlled via I/O ports.

We have to create a separate header file for serial port configurations. You should store the following code inside that header file.

Writing data to the serial port is done via the data I/O port. However, before writing, the transmit FIFO queue has to be empty (all previous writes must have finished). The transmit FIFO queue is empty if bit 5 of the line status I/O port is equal to one.

Paste the following code segment inside the io.s file.

global inb

; inb - returns a byte from the given I/O port
; stack: [esp + 4] The address of the I/O port
; [esp ] The return address
inb:
mov dx, [esp + 4] ; move the address of the I/O port to the dx register
in al, dx ; read a byte from the I/O port and store it in the al register
ret ; return the read byte

And this part should be stored inside io.h file.

/* in file io.h */

/** inb:
* Read a byte from an I/O port.
*
* @param port The address of the I/O port
* @return The read byte
*/
unsigned char inb(unsigned short port);

To save the output from the first serial port the Bochs configuration file bochsrc.txt must be updated. paste following inside the bochsrc.txt. Then you will be able to get output file called com1.out.

com1: enabled=1, mode=file, dev=com1.out

You have to call the function inside the kmain.c file. After doing today's activities kmain.c file should look like this.

kmain.c

Now you can run the os. And using terminal take the serial port output as follows.

Hope to see you in next article!!

--

--