JOSH — EXTENDED

Pasindu Chinthana
7 min readJul 26, 2020

Simple Real Mode Operating System

This is my first attempt to learn and understand operating systems fundamentals and concepts. I chose JOSH, to learn its structure, how it is operating and I tried to extend some features.

JOSH

JOSH is an interrupt-driven real-mode operating system. This initial idea and implementation were done by Dr. Mohan Raj Dhanagopal. His tutorial was very helpful to understand its process. You can find Dr. Dhanagopal’s tutorial from here.

The boot loader which used here(‘ultimate boot loader’) is created by Mattew Vea. You can find his article from here.

I would like to thank them for their commitment and for sharing these wonderful resources. I would try to explain what I’ve learned from this amazing OS.

I followed Tutorialspoint assembly articles to learn assembly language basics. I used Netwide Assembler(NASM) to compile these assembly files and I worked on a Linux environment. And I used Oracle VirtualBox to run and test this OS. You can visit my GitHub repository to get the source code.

To install NASM on Linux,

sudo apt-get update -ysudo apt-get install -y nasm

Operating System

Basically, the Operating system is a software that runs on a computer. It manages computer resources, allocates them to user applications, and handle communication.

Boot Process and Boot Loader

When the computer is starting, its memory, registers are blanked. Then it loads the Basic Input Output System(BIOS) and executes it. Then BIOS looks for the operating system. In BIOS, it searches for the OS from floppy disks, hard disks, USB drives. When it finds the OS it only loads the code fragment present in the first sector called boot sector.

Here I have used a floppy disk image since JOSH is designed to boot from FAT12 floppy disk. So this boot sector code will be 512 bytes long. The last 2 bytes of this code fragment is called the boot signature. BIOS identifies a bootable device from this boot signature. If it is absent that device will not be considered as bootable. After BIOS loads up the bootstrap loader to the memory, it is up to boot loader to load up the OS and do necessary changes.

To create a floppy image and mount:

mkfs.msdos -C floppyname.img 1440
sudo mount -o loop floppyname.img /media/floppy/

After mounting, I compiled and copied the boot loader to the created floppy.

nasm boot.asm -o boot.bin -f bindd if=boot.bin bs=512 count=1 of=/dev/loop5

Here /dev/loop5 is the mount point of the floppy I created.

Now the floppy is a bootable device, then I compiled Dr. Dhanagopal’s JOSH kernel to the mounted floppy image.

Kernel

The kernel is the core of the OS, it facilitates interactions between hardware and software components.

In JOSH, the initial kernel supports limited functions like displaying a message, wait for keypress, reboot the computer. It uses BIOS interrupts for display words on the screen.

You can find Dr. Dhanagopal’s JOSH kernel from here.

Display text on the screen

[SEGMENT .text]
_disp_str:
lodsb ; load next character
or al, al ; test for NUL character
jz .DONE
mov ah, 0x0E ; BIOS teletype
mov bh, 0x00 ; display page 0
mov bl, 0x07 ; text attribute
int 0x10 ; invoke BIOS
jmp _disp_str
.DONE:
ret

To display text on the screen, BIOS teletype display service is used here. ‘loadsb’ loads a character from si register to al register to check it is a null character(0x00). If it is a null jump to .DONE and end the function(ret). Else it invokes the BIOS function by using an interrupt and displays the character on the screen and jumps back to the starting point of this function.

jmp _disp_str

This loop continues until it finds the null character. So when we need to initialize text value in the beginning, we add a trailing null character to the text so this function will print the character until it reaches the null character.

strWelcomeMsg   db  "Welcome to JOSH!", 0x00

To print the value, first, have to move the text value to si register and call the _disp_str function we created.

mov si, strWelcomeMsg   ;load message
call _disp_str

Interrupts

An interrupt is a process that interrupts the current execution of the program and gains the attention of the system. There are two types of interrupts software interrupts and hardware interrupts. When we press a key on the keyboard it will generate interrupt and tell the system to handle that interrupt. Likewise, this system runs because of interrupts. we use interrupts to use this system.

push dx
push es
xor ax, ax
mov es, ax
cli
mov word [es:0x21*4], _int0x21 ; setup interrupt service
mov [es:0x21*4+2], cs
sti
pop es
pop dx

Shell or Command Interpreter

In JOSH OS, the shell is the routine that communicates with the user. Dr. Dhanagopal has created a basic shell that can read user inputs and iterate through its initialized data and identify the user input and run the respective function.

I tried to extend its shell command to display hardware information, display help menu, and clear screen. In clear screen function, I still couldn’t complete it I am still working on that issue. I tried int 0x10h with ah = 0x00.

Display help menu

In shell,

; display help menu
_cmd_help:
mov si, strCmd0
mov di, cmdHelp
mov cx, 5
repe cmpsb
jne _cmd_clear ;next command

display help menu function:

_help_menu:
call _display_endl
mov si, h_heading
mov al, 0x01
int 0x21
call _display_endl
mov si, h_ver
mov al, 0x01
int 0x21
call _display_endl
mov si, h_info
mov al, 0x01
int 0x21
call _display_endl
mov si, h_help
mov al, 0x01
int 0x21
call _display_endl
mov si, h_exit
mov al, 0x01
int 0x21
call _display_endl
call _display_endl
ret

Display hardware information

push ax
push bx
push cx
push dx
push es
push si
call _display_endl
mov si, strInfo ; Prints the command description
mov al, 0x01
int 0x21
call _display_endl
call _display_endl

mov si, strmemory ; Prints base memory string
mov al, 0x01
int 0x21
; Reading Base Memory -----------------------------------------------
push ax
push dx

int 0x12 ; call interrupt 12 to get base mem size
mov dx,ax
mov [basemem] , ax
call _print_dec ; display the number in decimal
mov al, 0x6b
mov ah, 0x0E ; BIOS teletype acts on 'K'
mov bh, 0x00
mov bl, 0x07
int 0x10

pop dx
pop ax
; Reading extended Memory
call _display_endl
mov si, strsmallextended
mov al, 0x01
int 0x21
xor cx, cx ; Clear CX
xor dx, dx ; clear DX
mov ax, 0xE801
int 0x15 ; call interrupt 15h
mov dx, ax ; save memory value in DX as the procedure argument
mov [extmem1], ax
call _print_dec ; print the decimal value in DX
mov al, 0x6b
mov ah, 0x0E ; BIOS teletype acts on 'K'
mov bh, 0x00
mov bl, 0x07
int 0x10
xor cx, cx ; clear CX
xor dx, dx ; clear DX
mov ax, 0xE801
int 0x15 ; call interrupt 15h
mov ax, dx ; save memory value in AX for division
xor dx, dx
mov si , 16
div si ; divide AX value to get the number of MB
mov dx, ax
mov [extmem2], ax
push dx ; save dx value
call _display_endl
mov si, strbigextended
mov al, 0x01
int 0x21

pop dx ; retrieve DX for printing
call _print_dec
mov al, 0x4D
mov ah, 0x0E ; BIOS teletype acts on 'M'
mov bh, 0x00
mov bl, 0x07
int 0x10
call _display_endl
mov si, strtotalmemory
mov al, 0x01
int 0x21
; total memory = basemem + extmem1 + extmem2
mov ax, [basemem]
add ax, [extmem1] ; ax = ax + extmem1
shr ax, 10
add ax, [extmem2] ; ax = ax + extmem2
mov dx, ax
call _print_dec
mov al, 0x4D
mov ah, 0x0E ; BIOS teletype acts on 'M'
mov bh, 0x00
mov bl, 0x07
int 0x10
;CPU Information --------------------------------------------------------------------------
call _display_endl
mov si, strCPUVendor
mov al, 0x01
int 0x21
mov eax, 0x00000000 ; set eax register to get the vendor
cpuid
mov eax, ebx ; prepare for string saving
mov ebx, edx
mov edx, 0x00
mov si, strVendorID
call _save_string
mov si, strVendorID ;print string
mov al, 0x01
int 0x21
call _display_endl
mov si, strCPUdescription
mov al, 0x01
int 0x21
mov eax, 0x80000000 ; First check if CPU support this
cpuid
cmp eax, 0x80000004
jb _cpu_not_supported ; if not supported jump to function end
mov eax, 0x80000002 ; get first part of the brand
mov si, strBrand
cpuid
call _save_string
add si, 16
mov eax, 0x80000003 ; get second part of the brand
cpuid
call _save_string
add si, 16
mov eax, 0x80000004 ; get third part of the brand
cpuid
call _save_string
mov si, strBrand ; print the saved Brand string
mov al, 0x01
int 0x21
jmp _hard_info
_cpu_not_supported:
mov si, strNotSupported
mov al, 0x01
int 0x21
;End of processor info
; Number of Harddrives -------------------------------------------------------------
_hard_info:
call _display_endl
mov si, strhdnumber
mov al, 0x01
int 0x21
mov ax,0040h ; look at 0040:0075 for a number
mov es,ax ;
mov dl,[es:0075h] ; move the number into DL register
add dl,30h ; add 48 to get ASCII value
mov al, dl
mov ah, 0x0E ; BIOS teletype acts on character
mov bh, 0x00
mov bl, 0x07
int 0x10
_serial_ports:
call _display_endl
mov si, strserialportnumber
mov al, 0x01
int 0x21
mov ax, [es:0x10]
shr ax, 9
and ax, 0x0007
add al, 30h
mov ah, 0x0E ; BIOS teletype acts on character
mov bh, 0x00
mov bl, 0x07
int 0x10
; Reading base I/O addresses
;Base I/O address for serial port 1 (communications port 1 - COM 1)
mov ax, [es:0000h] ; Read address for serial port 1
cmp ax, 0
je _end
call _display_endl
mov si, strserialport1
mov al, 0x01
int 0x21
mov dx, ax
call _print_dec
_end:
;Base I/O address for serial port 1(communications port 1 - COM 1)

call _display_endl
pop si
pop es
pop dx
pop cx
pop bx
pop ax
ret

I am planning to add borders and change colors with BIOS teletype service functions. This is a pretty curious subject, there will be more hidden things to discover.

--

--