The XamOS — Let’s Build an OS!!!

Nipuni Perera
7 min readAug 5, 2021

The 03rd step of operating system development — “integrate_outputs”

Welcome back!

As I promised in the last article today we will design Drivers for our operating system, which function as a layer between the Kernel and the Hardware.

If you haven’t already, go here to read the previous article in the series on how the operating system implements with C.

Ubuntu Guest Virtual Machine Vs Windows Host Machine

01. Interaction between HARDWARE

The operating system learns how to communicate with each piece of hardware through drivers. Drivers are required for graphics cards, sound cards, networking cards, USB devices, and everything else you attach to your computer. The operating system then makes use of these drivers to guarantee that each device works properly.

Memory mappings I/O and I/O ports are two ways to interact with hardware.

Memory-mapped I/O

Memory-mapped Both memory and I/O devices are addressed using the same address space in I/O. The I/O devices’ memory and registers are mapped to (related with) address values. As a result, a memory address can correspond to either a part of physical RAM or the I/O device’s memory. As a result, the same CPU commands that are used to access memory may also be used to access devices.

You can write a specific memory location and the hardware will be updated with new data if the hardware uses the memory map’s I/O. The frame buffer is an example that will be covered in greater depth later. When the value 0x410f is sent to the address 0x000b8000, for example, the white letter A appears on a black background.

I/O ports

An I/O port is a computer socket into which a cable is connected. A hardware interface connects the CPU to a peripheral device, and a network interface connects the CPU to the network.
If the hardware has an I/O port, you must communicate with it using the OUT and IN assembly code instructions. The OUT command has two parameters: the I/O port address and the data to send. The in order takes only one parameter, the I/O port’s address, and returns data from the hardware. The I/O port can be considered to connect with the hardware in the same way as the server uses socket communication. The cursor (flicker rectangle) frame buffer is an example of hardware controlled through the I / O port on the PC.

02. THE FRAMEBUFFER

Sun_sbus_cgsix_framebuffer

A framebuffer (also known as a frame buffer or frame store) is a section of random-access memory (RAM) that contains a bitmap that is used to drive a visual display. It’s a memory buffer that stores data that represents all of the pixels in a single video frame. The framebuffer has 80 columns and 25 rows, with row and column indices beginning at 0 (thus rows 0–24)...

>>> 2.1 Write text

The frame buffer performs the text write console, which is finished by the memory I / O map. 0x000b8000 is the beginning address of the frame buffer’s memory mapping I/O. The memory is split into 16-bit units, with the first 16 bits determining the colors of the characters, foreground, and backdrop. Bit 7–4 is the backdrop, bit 3–0 is in the foreground and bit 7–4 is the ASCII value of the character. As illustrated in the diagram below:

The first cell on the console corresponds to the first line, the zero columns. The ASCII table shows that A corresponds to 65 or 0x41. As a result, use the following assembly code instructions to create character A with a green foreground (2) and a dark gray background (8) at location (0, 0):

The second cell then corresponds to the first column of the ziping line, resulting in the following address:

0x000B8010 = 0x000B8000 + 16

The writing to the frame buffer may be finished in the C language by interpreting address 0x000B8000 as a CHAR pointer char * fb = (char *) 0x000b8000. Then, with a green foreground and a dark gray backdrop, write A to the location (0, 0) as follows:

The following shows how to apply it to a function:

After that, you can use it:

>>> 2.2 Moving Cursor

The mobile frame buffer’s cursor is controlled by two separate I/O ports. The cursor’s location is defined by 16-bit integers: 0 represents line 0, column 0; 1 represents row 0, column 1; 80 represents a row, zero columns, and so on. Because the location is 16 digits long, the assembly code instructions are 8 bits long, thus it must be delivered in two parts: first 8 bits, then 8 bits more. The frame buffer contains two I/O ports: one for receiving data and the other for summarizing the data that has been received.
The port 0x3d4 is used to describe data, whereas the port 0x3d5 is used to store the data.
The cursor should be placed in the first row column (position 80 = 0x0050). following assembly code instructions will be used:

In C, the OUT assembly code instruction cannot be directly executed. As a result, it’s better to put assembly code in a function that can be called from C via CDECL calls.

After creating a c file named io.h , you may simply access the OUT assembly code instruction from C by putting this function in a file named io.s.

application:(kmain.c file)

>>> 2.3 The Driver

Other code in the OS should be able to communicate with Framebuffer through the driver.

The interface should not include any pairs of features, however, it is suggested that you utilize the WRITE function with the following declaration:

The contents of the buffer BUF’s length are written to the screen via Write Francis. The writing feature should automatically advance the pointer and scroll the screen after writing characters.

03. THE SERIAL PORT

Although the serial port is included on virtually all motherboards, it is currently seldom accessible to the user in the form of a DE-9 connection. The serial port is simple to use, and it may also be used for logging in to Bochs.
If your computer has a serial port, it is likely to have many ports, but we will only utilize one of them.
This is because the serial port will only be used for logging. We also solely utilize the serial port for output rather than input.

The I/O port has complete control over the serial port.

>>> 3.1 Setting up the serial port

Configuration data is the first thing that has to be transmitted to the serial port. Two hardware devices must agree on several things before they may interact with one another. These are some of them: data transmission speed (bit or baud rate) (bit or baud rate) If any error check (parity bit, stop bit) should be used to indicate the number of bits (data bits)

>>> 3.2 Configure the line

Configuring the line entails determining how data is transmitted across it. The serial port has an I/O port, which is a configuration line command port. First, the data transmission speed will be configured. The serial port features an internal clock with a 115200 Hz frequency. Setting speed to a serial port implies sending divisions, such as transmission 2, at a rate of 115200/2 = 57600 Hz. We can only send 8 digits at a time because the divisor is 16 digits. As a result, we must send a command to the serial port. The highest 8 bits must be expected first, followed by the lowest 8 bits. Sending 0x80 to the line command port does this. Consider the following scenario:

The manner you deliver data must be configured. That’s also possible to accomplish it by sending one byte over the command line port. The following is the 8-bit layout:

We’ll use the conventional value 0x03 for the most part, which implies an 8-bit length, one parity bit, one stop bit, and no interrupt control. It is transmitted to the line command port, as demonstrated in the following example:

>>> 3.3 Configuring Buffers

Whether receiving or transferring data, data is deposited in the buffer when sent over a serial connection. As a result, sending data to the serial port will cause it to be buffered before being sent over the wired network. If you send too much data too quickly, though, the buffer will fill up and the data will be lost. In other words, the buffer is a first-in, first-out (FIFO) queue. The following are the FIFO queue configuration bytes:

We use 0xc7 = 11000111:
> Enable FIFO
> Clear reception and send FIFO queue
> Use 14 bytes as the queue size

>>> 3.4 Configuring Modem

We don’t need to initiate Interrupts because we don’t handle received input at the moment.

As a result, we utilize the value 0x03 = 00000011 (RTS = 1 and DTS = 1).

>>> 3.5 Write data to the serial port

Through the data I/O port, write data to the serial port. However, the FIFO queue must be empty before writing (you must complete all previous writes).
The FIFO queue is empty if bit 5 of the line status I/O port is set to 1. The IN assembly code instruction is used to read the contents of the I/O port.

In C, the in assembly code instruction isn’t available, therefore you’ll have to pack it (just like the OUT assembly code instruction):.

>>> 3.6 Configuring Bochs

You must edit the Bochs profile bochsrc.txt to store the output of the first serial port.
The COM1 setting specifies how the first serial port is handled by Bochs:

Serial port 1’s output is now saved in the file com1.out.
If you try to start OS by making a run after finishing this, you will get an error since we have yet to create the main function to execute our functions.
In the next article, we will deconstruct this.

Thank you very much for reading!

I’ll hope to get back to you with the chapter four, “integrate_segmentation” as soon as possible.Till then,

Stay Safe!!!

-Nipuni Perera-

--

--

Nipuni Perera

As a Software Engineering undergrad at the University of Kelaniya SL , I share insights on coding, dev methodologies & emerging tech. Join me on my journey!