How to make an operating system?
This is one of the most asked questions on the internet  asked by new comers to computer programming world.
Operating system design is one of the most hard topic if not the hardest topic in the field of computer science. One needs to know about hardware, extreme level of computer programming including low level assembly language and mid level language like C/C++.

Even after such complexity many coders wants to know and make their own operating system as a hobby project. There exists a number of options when thinking about it.
  1.  From scratch
  2.  Developing on top of an existing kernel like "Linux"
  3. Customizing a linux based distro and making a new distro.
Option 2 and 3 are comparatively easier than option 1. But today we will write some boilerplate code that boots up a computer and prints something on the screen. This should not be considered a fully functional OS anyway. This is just for fun and can be considered as an entry point to developing your OS. Let's start...


We will create 3 stage OS. First is to just display message on the screen with colors, second is to take input from user, and third for drawing.
The bootloaders are generally written in 16-bit assembly(also called Real mode), then the bits can be extended to 32-bit(Protected mode).
So the bootloaders must be written in 16-bit assembly.
Before you move to next, you must have some knowledge about 16-bit assembly language.

Requirements


You need an assember that can convert your assembly instructions into raw binary format and an simulator to view.
I am using Nasm assember and Qemu simulator.

For Linux, type following commands to install nasm & qemu(Quick Emulator)


sudo apt-get install nasm
sudo apt-get install qemu qemu-system-x86_64

For Windows,download them from following sites and install them.

Coding

Starting of coding always comes with printing Hello World, isn't it ?
Here's the code that prints Hello World! on screen.
(file = hello_world.asm)



hello_world.asm

[bits 16] ; tell assembler that working in real mode(16 bit mode) [org 0x7c00] ; organize from 0x7C00 memory location where BIOS will load us start: ; start label from where our code starts xor ax,ax ; set ax register to 0 mov ds,ax ; set data segment(ds) to 0 mov es,ax ; set extra segment(es) to 0 mov bx,0x8000 mov si, hello_world ; point hello_world to source index call print_string ; call print different color string function hello_world db 'Hello World!',13,0 print_string: mov ah, 0x0E ; value to tell interrupt handler that take value from al & print it .repeat_next_char: lodsb ; get character from string cmp al, 0 ; cmp al with end of string je .done_print ; if char is zero, end of string int 0x10 ; otherwise, print it jmp .repeat_next_char ; jmp to .repeat_next_char if not 0 .done_print: ret ;return times (510 - ($ - $$)) db 0x00 ;set 512 bytes for boot sector which are necessary dw 0xAA55 ; boot signature 0xAA & 0x55

OK now, what the hell is this ?

[bits 16] : This line tells the assember you are working in 16-bit real mode.
it will convert assembly data to 16-bit binary form.

[org 0x7c00] : This is assember directive. 0x7c00 is the memory location where BIOS will load us. xor ax,ax mov ds,ax mov es,ax mov bx,0x8000 First setting the registers to zero such as ax,ds,es which we will used further . Then we will copy memory location 0x8000 to bx register
because we want to perform operations/instructions because we are loaded at 0x7c00 memory location.
we need memory location above it. hello_world db 'Hello World!',13,0 : this line defines the string with label hello_world,
where 13 is New line and 0 is end of string. mov si, hello_world call print_string Pointing first character of hello_world string to source index(si) register and then call print_string function. Copying 0x0E to ah register.
this will tell to interrupt handler that take value/ASCII character from al & print it using int 0x10. AH = 0x0E AL = character BH = page BL = color (graphics mode) int 0x10 .repeat_next_char : Label for continue to loop until end of string occurs. lodsb : This instruction loads the first character from si to al register using ASCII code. Then we will compare whether al contains 0 or not,if not then print it and jump to loop,
otherwise jump to .done_print. int 0x10 : This is BIOS video interrupt which takes char value from al register & print it. times (510 - ($ - $$)) db 0x00 : A boot sector always be a 512 byte. starting with address 0x00.
because on hard drive,there are only 512 bytes of sectors.
dw 0xAA55 : This is the magic number of bootable device.
This line is boot signature that makes our code to bootable code.
it defines word 0xAA & 0x55.
These are last two bytes of our first sector. because of this number,BIOS loads us at 0x7c00 location when computer starts. For Linux,Type following command to compile file nasm -f bin hello_world.asm -o myos.bin Once file is compiled successfully and myos.bin file is created, then run it in qemu. qemu-system-x86_64 myos.bin For windows, open nasm application, it will prompt a command at location where nasm is installed. Perform same commands as performed for linux juts giving full file name path. Consider i have file in C:\Users\Pritam\Documents\temp folder. nasm.exe -f bin "C:\Users\Pritam\Documents\temp\hello_world.asm"
-o "C:\Users\Pritam\Documents\temp\myos.bin" and to run in qemu, "C:\Program Files\qemu\qemu-system-x86_64.exe" "C:\Users\Pritam\Documents\temp\myos.bin" where i have installed qemu. Here i have created .bin file, but you can also create .iso file. Once it successfully prints Hello World! then attach Secondary device/USB drive and boot .bin/.iso in it. You can use dd command on linux or can use rufus software on windows.
Output of above program is






















Without BIOS :  

Without BIOSIn above program we used BIOS interrupt. 
To display HelloWorld on the screen without BIOS see following link :
 
 HelloWorld_without_BIOS



Watch video here for how to implement above procedure for both Linux & Windows :



To print a string on screen at specific location or to set cursor at specific location use following actions. AH = 0x02 BH = page DH = row DL = column e.g: mov ah,0x02 ; set value for change to cursor position mov bh,0x00 ; page mov dh,0x06 ; y cordinate/row mov dl,0x05 ; x cordinate/col int 0x10 mov si, hello_world call print_string To get input first set ax to 0x00 and call int 0x16. To display character which has been input,mov ah to 0x0E and call int 0x10. It will store char value to al register & key code to ah register. e.g: inputLoop: mov ax,0x00 int 0x16 cmp ah,0x1C ; compare input is enter(1C) or not je .inputLoop cmp al,0x61 ; compare input is character 'a' or not je exitLoop mov ah,0x0E ;display input char int 0x10 exitLoop: ret As described above that every sector has size only 512 bytes,
so if you write code which is taking more than 512 bytes it will not work or assembler will give you an error. So to use more memory, you need to load/read next sector into main memory. To load/read sectors in main memory, AH = sector number(1,2,3 etc)[1 is already taken by our bootloader] AL = number of sectors to read DL = type of memory from where to read(0x80 is for hard drive/USB drive) CH = cylinder number DH = head number CL = sector number BX = memory location where to jump after loaded int 0x13 = Disk I/O interrupt Then jump to your memory location(label in assembly). e.g: ; load second sector from memory mov ah, 0x02 ; load second stage to memory mov al, 1 ; numbers of sectors to read into memory mov dl, 0x80 ; sector read from fixed/usb disk mov ch, 0 ; cylinder number mov dh, 0 ; head number mov cl, 2 ; sector number mov bx, _OS_Stage_2 ; load into es:bx segment :offset of buffer int 0x13 ; disk I/O interrupt jmp _OS_Stage_2 ; jump to second stage For clearing the screen,copy 0x13 to ax & call video interrupt. mov ax,0x13 int 0x10 For graphics, we need to access video memory segments. This can be done by pushing 0x0A000 into stack,and setting di,ax,es to specific values. AX = color DI = x & y cordinates(y=320 for next line(320*200 display)) [ES:DI] = value of x,y cordinates & color(AX)[segment :offset]
Here's the complete 3 stages OS code
Extended Operating System
[bits 16] ; tell assembler that working in real mode(16 bit mode) [org 0x7c00] ; organize from 0x7C00 memory location where BIOS will load us start: ; start label from where our code starts xor ax,ax ; set ax register to 0 mov ds,ax ; set data segment(ds) to 0 mov es,ax ; set extra segment(es) to 0 mov bx,0x8000 mov ax,0x13 ;clears the screen int 0x10 ;call bios video interrupt mov ah,02 ;clear the screen with big font int 0x10 ;interrupt display ;set cursor to specific position on screen mov ah,0x02 ; set value for change to cursor position mov bh,0x00 ; page mov dh,0x06 ; y cordinate/row mov dl,0x09 ; x cordinate/col int 0x10 mov si, start_os_intro ; point start_os_intro string to source index call _print_DiffColor_String ; call print different color string function ;set cursor to specific position on screen mov ah,0x02 mov bh,0x00 mov dh,0x10 mov dl,0x06 int 0x10 mov si,press_key ; point press_key string to source index call _print_GreenColor_String ; call print green color string function mov ax,0x00 ; get keyboard input int 0x16 ; interrupt for hold & read input ;///////////////////////////////////////////////////////////// ; load second sector into memory mov ah, 0x02 ; load second stage to memory mov al, 1 ; numbers of sectors to read into memory mov dl, 0x80 ; sector read from fixed/usb disk mov ch, 0 ; cylinder number mov dh, 0 ; head number mov cl, 2 ; sector number mov bx, _OS_Stage_2 ; load into es:bx segment :offset of buffer int 0x13 ; disk I/O interrupt jmp _OS_Stage_2 ; jump to second stage ;///////////////////////////////////////////////////////////// ; declaring string datas here start_os_intro db 'Welcome to My OS!',0 press_key db '>>>> Press any key <<<<',0 login_username db 'Username : ',0 login_password db 'Password : ',0 display_text db '! Welcome to my Operating System !', 0 os_info db 10, 'My Operating System, 16-Bit, version=1.0.0',13,0 press_key_2 db 10,'Press any key to go to graphics view',0 window_text db 10,'Graphics in OS......', 0 hello_world_text db 10,10, ' Hello World!',0 login_label db '#] Login please....(ESC to skip login)', 0 ;///////////////////////////////////////////////////////////// ; defining printing string functions here ;****** print string without color print_string: mov ah, 0x0E ; value to tell interrupt handler that take value from al & print it .repeat_next_char: lodsb ; get character from string cmp al, 0 ; cmp al with end of string je .done_print ; if char is zero, end of string int 0x10 ; otherwise, print it jmp .repeat_next_char ; jmp to .repeat_next_char if not 0 .done_print: ret ;return ;****** print string with different colors _print_DiffColor_String: mov bl,1 ;color value mov ah, 0x0E .repeat_next_char: lodsb cmp al, 0 je .done_print add bl,6 ;increase color value by 6 int 0x10 jmp .repeat_next_char .done_print: ret ;****** print string with green color _print_GreenColor_String: mov bl,10 mov ah, 0x0E .repeat_next_char: lodsb cmp al, 0 je .done_print int 0x10 jmp .repeat_next_char .done_print: ret ;****** print string with white color _print_WhiteColor_String: mov bl,15 mov ah, 0x0E .repeat_next_char: lodsb cmp al, 0 je .done_print int 0x10 jmp .repeat_next_char .done_print: ret ;****** print string with yellow color _print_YellowColor_String: mov bl,14 mov ah, 0x0E .repeat_next_char: lodsb cmp al, 0 je .done_print int 0x10 jmp .repeat_next_char .done_print: ret ;/////////////////////////////////////////// ; boot loader magic number times ((0x200 - 2) - ($ - $$)) db 0x00 ;set 512 bytes for boot sector which are necessary dw 0xAA55 ; boot signature 0xAA & 0x55 ;//////////////////////////////////////////////////////////////////////////////////////// _OS_Stage_2 : mov al,2 ; set font to normal mode mov ah,0 ; clear the screen int 0x10 ; call video interrupt mov cx,0 ; initialize counter(cx) to get input ;***** print login_label on screen ;set cursor to specific position on screen mov ah,0x02 mov bh,0x00 mov dh,0x00 mov dl,0x00 int 0x10 mov si,login_label ; point si to login_username call print_string ; display it on screen ;****** read username ;set cursor to specific position on screen mov ah,0x02 mov bh,0x00 mov dh,0x02 mov dl,0x00 int 0x10 mov si,login_username ; point si to login_username call print_string ; display it on screen _getUsernameinput: mov ax,0x00 ; get keyboard input int 0x16 ; hold for input cmp ah,0x1C ; compare input is enter(1C) or not je .exitinput ; if enter then jump to exitinput cmp ah,0x01 ; compare input is escape(01) or not je _skipLogin ; jump to _skipLogin mov ah,0x0E ;display input char int 0x10 inc cx ; increase counter cmp cx,5 ; compare counter reached to 5 jbe _getUsernameinput ; yes jump to _getUsernameinput jmp .inputdone ; else jump to inputdone .inputdone: mov cx,0 ; set counter to 0 jmp _getUsernameinput ; jump to _getUsernameinput ret ; return .exitinput: hlt ;****** read password ;set x y position to text mov ah,0x02 mov bh,0x00 mov dh,0x03 mov dl,0x00 int 0x10 mov si,login_password ; point si to login_username call print_string ; display it on screen _getPasswordinput: mov ax,0x00 int 0x16 cmp ah,0x1C je .exitinput cmp ah,0x01 je _skipLogin inc cx cmp cx,5 jbe _getPasswordinput jmp .inputdone .inputdone: mov cx,0 jmp _getPasswordinput ret .exitinput: hlt ;****** display display_text on screen ;set x y position to text mov ah,0x02 mov bh,0x00 mov dh,0x08 mov dl,0x12 int 0x10 mov si, display_text ;display display_text on screen call print_string ;set x y position to text mov ah,0x02 mov bh,0x00 mov dh,0x9 mov dl,0x10 int 0x10 mov si, os_info ;display os_info on screen call print_string ;set x y position to text mov ah,0x02 mov bh,0x00 mov dh,0x11 mov dl,0x11 int 0x10 mov si, press_key_2 ;display press_key_2 on screen call print_string mov ah,0x00 int 0x16 ;////////////////////////////////////////////////////////////////// _skipLogin: ;///////////////////////////////////////////////////////////// ; load third sector into memory mov ah, 0x03 ; load third stage to memory mov al, 1 mov dl, 0x80 mov ch, 0 mov dh, 0 mov cl, 3 ; sector number 3 mov bx, _OS_Stage_3 int 0x13 jmp _OS_Stage_3 ;//////////////////////////////////////////////////////////////////////////////////////// _OS_Stage_3: mov ax,0x13 ; clears the screen int 0x10 ;////////////////////////////////////////////////////////// ; drawing window with lines push 0x0A000 ; video memory graphics segment pop es ; pop any extar segments from stack xor di,di ; set destination index to 0 xor ax,ax ; set color register to zero ;////////////////////////////////////////////// ;******drawing top line of our window mov ax,0x02 ; set color to green mov dx,0 ; initialize counter(dx) to 0 add di,320 ; add di to 320(next line) imul di,10 ;multiply by 10 to di to set y cordinate from where we need to start drawing add di,10 ;set x cordinate of line from where to be drawn _topLine_perPixel_Loop: mov [es:di],ax ; move value ax to memory location es:di inc di ; increment di for next pixel inc dx ; increment our counter cmp dx,300 ; comprae counter value with 300 jbe _topLine_perPixel_Loop ; if <= 300 jump to _topLine_perPixel_Loop hlt ; halt process after drawing ;////////////////////////////////////////////// ;******drawing bottm line of our window xor dx,dx xor di,di add di,320 imul di,190 ; set y cordinate for line to be drawn add di,10 ;set x cordinate of line to be drawn mov ax,0x01 ; blue color _bottmLine_perPixel_Loop: mov [es:di],ax inc di inc dx cmp dx,300 jbe _bottmLine_perPixel_Loop hlt ;////////////////////////////////////////////// ;******drawing left line of our window xor dx,dx xor di,di add di,320 imul di,10 ; set y cordinate for line to be drawn add di,10 ; set x cordinate for line to be drawn mov ax,0x03 ; cyan color _leftLine_perPixel_Loop: mov [es:di],ax inc dx add di,320 cmp dx,180 jbe _leftLine_perPixel_Loop hlt ;////////////////////////////////////////////// ;******drawing right line of our window xor dx,dx xor di,di add di,320 imul di,10 ; set y cordinate for line to be drawn add di,310 ; set x cordinate for line to be drawn mov ax,0x06 ; orange color _rightLine_perPixel_Loop: mov [es:di],ax inc dx add di,320 cmp dx,180 jbe _rightLine_perPixel_Loop hlt ;////////////////////////////////////////////// ;******drawing line below top line of our window xor dx,dx xor di,di add di,320 imul di,27 ; set y cordinate for line to be drawn add di,11 ; set x cordinate for line to be drawn mov ax,0x05 ; pink color _belowLineTopLine_perPixel_Loop: mov [es:di],ax inc di inc dx cmp dx,298 jbe _belowLineTopLine_perPixel_Loop hlt ;***** print window_text & X char ;set cursor to specific position mov ah,0x02 mov bh,0x00 mov dh,0x01 ; y cordinate mov dl,0x02 ; x cordinate int 0x10 mov si,window_text ; point si to window_text call _print_YellowColor_String hlt ;set cursor to specific position mov ah,0x02 mov bh,0x00 mov dh,0x02 ; y cordinate mov dl,0x25 ; x cordinate int 0x10 mov ah,0x0E mov al,0x58 ; 0x58=X mov bh,0x00 mov bl,4 ; red color int 0x10 hlt ;set cursor to specific position mov ah,0x02 mov bh,0x00 mov dh,0x02 ; y cordinate mov dl,0x23 ; x cordinate int 0x10 mov ah,0x0E mov al,0x5F ; 0x58=X mov bh,0x00 mov bl,9 ; red color int 0x10 hlt ;set cursor to specific position mov ah,0x02 mov bh,0x00 mov dh,0x05 ; y cordinate mov dl,0x09 ; x cordinate int 0x10 mov si,hello_world_text call _print_DiffColor_String hlt ;set cursor to specific position mov ah,0x02 mov bh,0x00 mov dh,0x12 ; y cordinate mov dl,0x03 ; x cordinate int 0x10 mov si,display_text call _print_WhiteColor_String hlt ; add how much memory we need times (1024 - ($-$$)) db 0x00





Post a Comment

Your feedback is welcome. Be it positive or negative. Please do not post any irrelevant comment or abuse anyway.

Previous Post Next Post