Avt3k

Building your own operating system

                        

                                                             PCK Home

Introduction

If you know how an operating system works, it will help you a lot in programming, especially for system programs like device drivers; even for non-system-programming, it can help a lot. And also, I'm sure every one would like to have their own operating system.

You would also learn from this article how to read and write raw sectors from a disk.

Background

In this article, I would explain the first part of building an operating system.

Here is what happens when you start your computer:

1. The BIOS (Basic Input Output System – this is a program that comes with any mother board and it is placed in a chip on the mother board) checks all the computer components to make sure that they are all working.
2. If all the components are working, the BIOS starts searching for a drive that might have an operating system. (The BIOS can look in hard drives, floppy drives, CD-ROM drives etc. The order the BIOS checks can be set it in the BIOS setup program. To get to the BIOS setup, right when the computer turns on, press the DELETE key until you see the BIOS setup program; in some computers, it can be a different button than DELETE, so look in your mother board specification to find how to get to the BIOS setup program. And also look up (if you don’t now how to do it) how to change the search search of BIOS while looking for an operating system.)
3. The BIOS checks the first drive to see if he has a valid BOOT sector. (A disk is divided into little regions that are named sectors. The BOOT sector size is 512 bytes in most drives.) If the drive has a valid BOOT sector, the BIOS loads that sector in to the memory at address 0:7c00 (=31,744) and gives control to that area of the memory.
4. Now this little program that was loaded from the BOOT sector continues to load the operating system, and after the operating system is loaded, it initializes the operating system and gives the control to the operating system.

Making a bootable disk

The steps would be like this:

1. Take a floppy diskette that you don't need.
2. Use the program that comes with this article, BOOTSectorUtility.exe, to copy the file BOOT.bin to the floppy diskette BOOT sector.
3. Make sure that your BIOS is set to BOOT from the floppy drive first, so that our operating system would be loaded.
4. Restart your computer (make sure that the floppy diskette is in the drive), and watch what our BOOT sector does.

With BOOTSectorUtility.exe, you can also search your regular operating system, by saving the boot sector of the drive of your operating system to a file.

And to read that file, start the command line and type Debug <file path> (if you have a Microsoft operating system).

The Debug command starts a 16 bit debugger (any boot sector is in 16 bit code, because when the computer starts, it is in 16 bit mode, and only after the boot sector is run, can it change the CPU to 32 bit mode or 64 bit mode). Press u <Enter> (u = unassemble) to unassemble the file. Figure 1 shows an example.

Figure 1 Unassembeling 16 bit code.



Reading raw bytes from a drive

You can read raw sectors from a drive like this:
Collapse

  1.  
  2. //
  3.  
  4. // Reading/writing raw sectors.
  5.  
  6. //
  7.  
  8.  
  9. //pBuffer has to be at least 512 bytes wide.
  10.  
  11. BOOL ReadSector(char chDriveName,char *pBuffer,DWORD nSector)
  12. {
  13.  
  14.     char Buffer[256];
  15.     HANDLE hDevice;
  16.     DWORD dwBytesReaden;
  17.  
  18.     //Init the drive name (as a Driver name).
  19.  
  20.     sprintf(Buffer,"\\\\.\\%c:",chDriveName);
  21.  
  22.     hDevice =
  23.       CreateFile(Buffer,                  // drive to open.
  24.  
  25.                 GENERIC_READ,
  26.                 FILE_SHARE_READ | // share mode.
  27.  
  28.                 FILE_SHARE_WRITE,
  29.                 NULL,             // default security attributes.
  30.  
  31.                 OPEN_EXISTING,    // disposition.
  32.  
  33.                 0,                // file attributes.
  34.  
  35.                 NULL);            //
  36.  
  37.  
  38.     if(hDrive==INVALID_HANDLE_VALUE)//if Error Openning a drive.
  39.  
  40.     {
  41.            return FALSE;
  42.     }
  43.  
  44.     //Move the read pointer to the right sector.
  45.  
  46.     if(SetFilePointer(hDevice,
  47.            nSector*512,
  48.            NULL,
  49.            FILE_BEGIN)==0xFFFFFFFF)
  50.            return FALSE;
  51.  
  52.     //Read the Sector.
  53.  
  54.     ReadFile(hDevice,
  55.            pBuffer,
  56.            512,
  57.            &dwBytesReaden,
  58.            0);
  59.  
  60.     //if Error reading the sector.
  61.  
  62.     if(dwBytesReaden!=512)
  63.            return FALSE;
  64.  
  65.     return TRUE;
  66. }
  67.  


Making a BOOT program

Now, I will explain the basics of a boot program (to understand this, you need to be familiar with Assembler and Interrupts and Interrupts vector table).

[Interrupts vector table = From Address 0 -> 1,024 holds 256 structures (4 bytes in size) that holds an address in the form: CS:IP = xxxx:xxxx. So you have addresses from INT 1 -> INT 256. Each interrupt has an index into that table. This table is used like this: if you, for example, use the instruction INT 10h, the CPU checks in index 10h in the interrupt table were the address of the routine is that handles INT 10h, and the CPU jumps to that address to execute it].

Now, I will explain how to print a string, read sectors, and wait for a key press using only the BIOS.

To print a string, I use the INT 10h function 0Ah (AH = 0Ah). To move the cursor, I use the INT 10h function 2h (AH = 2h). To read sectors, I use the INT 13h function 2h (AH = 2h). To wait for a key stroke, use the INT 16h function 0h (AH = 0h).

I used TASM v3.1 and TLINK to make my boot sector, but you can use any x86 16 bit assembler compiler.

(If you can't get a copy of TASM and TLINK, you can send me an e-mail and if it is legal, I would send you a copy of them).

(TASM and TLINK v3.1 were released in 1992 by Borland International).

Now I would explain the steps the BOOT program does.

1. Make a stack frame (if not you don't have any stack).
2. Set the DS (data segment) so you can access the data.
3. In my boot sector, I added this: (Display a message to the user, Wait for a key stroke, Continue).
4. Set up the Disk Parameter Block (the Disk Parameter Block is a structure that holds information about the drive, like how much sectors it has etc., the drive controller uses it for knowing how to read the disk in the drive).
5. To set the Disk Parameter Block, get its address (its address is pointed by INT 1Eh (in the memory, this is 1E x 4 bytes = 78h = 30 x 4 bytes = 120).

This is an example of how to initialize the Disk Parameter Block for a 3.5 inch (1.44 MB) floppy Disk.

  1.  
  2.  
  3.       StepRateAndHeadUnloadTime                     db      0DFh
  4.       HeadLoadTimeAndDMAModeFlag                    db      2h
  5.       DelayForMotorTurnOff                          db      25h
  6.       BytesPerSector                                db      2h              
  7.       SectorsPerTrack                               db      12h            
  8.       IntersectorGapLength                          db      1bh
  9.       DataLength                                    db      0FFh
  10.       IntersectorGapLengthDuringFormat              db      54h
  11.       FormatByteValue                               db      0F6h
  12.       HeadSettlingTime                              db      0Fh
  13.       DelayUntilMotorAtNormalSpeed                  db      8h
  14.        
  15.       DisketteSectorAddress(as LBA)OfTheDataArea    db      0
  16.       CylinderNumberToReadFrom                      db      0
  17.       SectorNumberToReadFrom                        db      0
  18.       DisketteSectorAddress (as LBA) OfTheRootDirectory    db      0
  19.  



6. And set the address that INT 1E points at to the address of the Disk Parameter Block you set up.
7. Reset the drive (by using the INT 13h function 0).
8. Start reading the diskette by using the INT 13h function 2h.
9. Give control to the loaded operating system (this is done by inserting in the code the op code of a jump to were you loaded the operating system. Let's say you loaded the operating system 512 bytes from were the BIOS loaded the BOOT sector (0:7c00h).

  1.  
  2.       db 0E9h  ; FAR JMP op code.
  3.  
  4.       db 512   ; 512 bytes
  5.  


or you can use another way: call some address, and there, change the return address in the stack to were you want the jump to, like this:

  1.  
  2.       call GiveControlToOS
  3.        
  4.       GiveControlToOS:
  5.               Pop ax
  6.               Pop ax
  7.               Mov ax,CodeSegement            ;Push the new CS to return.
  8.  
  9.               Push ax
  10.               mov ax,InstructionPointer      ;Push the new IP to return.
  11.  
  12.               Push ax
  13.        
  14.               ret                            ;Return to the modified address.
  15.  


10. In the end of the boot sector the bytes would be 0x55h 0x0AAh. If the boot sector doesn’t have this value, the BIOS would not load that boot sector.

After this, the BOOT sector finishes its job and the operating system starts running.

You can download the files I used for my boot sector here, the files are BOOT.asm, BOOT.bin (this is the sector itself).
The BOOT sector program
Collapse

  1.  
  2. .MODEL SMALL
  3.  
  4. .CODE          
  5.  
  6. ORG 7c00h      ;Because BIOS loades the OS at
  7.  
  8.                        ; address 0:7C00h so ORG 7C00h
  9.  
  10.                        ; makes that the refrence to date
  11.  
  12.                        ; are with the right offset (7c00h).
  13.  
  14.  
  15. ProgramStart:
  16.  
  17.                        
  18.    ; CS = 0 / IP = 7C00h // SS = ? / SP = ?
  19.  
  20.    ; You are now at address 7c00.
  21.  
  22. jmp start   ;Here we start the, BIOS gave us now the control.
  23.  
  24.  
  25.  
  26.  
  27. ;///////////////////////////////////////////
  28.  
  29. ;//Here goes all the data of the program.
  30.  
  31. ;///////////////////////////////////////////
  32.  
  33.  
  34. xCursor db 0
  35. yCursor db 0
  36.  
  37.  
  38. nSector db 0
  39. nTrack  db 0
  40. nSide   db 0
  41. nDrive  db 0
  42.  
  43. nTrays  db 0
  44.  
  45. 'Are You Ready to start Loading the OS...',0
  46. szReady                db
  47. 'Error Reading Drive, Press any Key to reboot...',0
  48. szErrorReadingDrive    db
  49. ;//Done Reading a track.
  50.  
  51. szPlaceMarker          db  '~~~~',0
  52. szDone                 db  'Done',0
  53.  
  54. pOS                    dw   7E00h
  55. ;//Points to were to download the Operating System.
  56.  
  57.  
  58. ;//Disk Paremeter Table.
  59.  
  60. StepRateAndHeadUnloadTime                     db      0DFh
  61. HeadLoadTimeAndDMAModeFlag                    db      2h
  62. DelayForMotorTurnOff                          db      25h
  63. ;// (1 = 256) //(2 = 512 bytes)
  64.  
  65. BytesPerSector                                db      2h
  66. ;// 18 sectors in a track.
  67.  
  68. SectorsPerTrack                               db      18
  69. IntersectorGapLength                          db      1Bh
  70. DataLength                                    db      0FFh
  71. IntersectorGapLengthDuringFormat              db      54h
  72. FormatByteValue                               db      0F6h
  1. HeadSettlingTime                              db      0Fh
  2. DelayUntilMotorAtNormalSpeed                  db      8h
  3.  
  4. DisketteSectorAddress_as_LBA_OfTheDataArea    db      0
  5. CylinderNumberToReadFrom                      db      0
  6. SectorNumberToReadFrom                        db      0
  7. DisketteSectorAddress_as_LBA_OfTheRootDirectory      db      0
  8.  
  9. ;/////////////////////////////////
  10.  
  11. ;//Here the program starts.
  12.  
  13. ;/////////////////////////////////
  14.  
  15.  
  16.  
  17. Start:
  18.  
  19. CLI     ;Clear Interupt Flag so while setting
  20.  
  21.         ;up the stack any intrupt would not be fired.
  22.  
  23.  
  24.         mov AX,7B0h    ;lets have the stack start at 7c00h-256 = 7B00h
  25.  
  26.         mov SS,ax      ;SS:SP = 7B0h:256 = 7B00h:256
  27.  
  28.         mov SP,256     ;Lets make the stack 256 bytes.
  29.  
  30.  
  31.         Mov ax,CS      ;Set the data segment = CS = 0
  32.  
  33.         mov DS,ax
  34.        
  35.         XOR AX,AX      ;Makes AX=0.
  36.  
  37.         MOV ES,AX      ;Make ES=0
  38.  
  39.  
  40.  
  41. STI     ;Set Back the Interupt Flag after
  42.  
  43.         ;we finished setting a stack fram.
  44.  
  45.        
  46.         Call ClearScreen       ;ClearScreen()
  47.  
  48.         LEA AX,szReady         ;Get Address of szReady.
  49.  
  50.         CALL PrintMessage      ;Call PrintfMessage()
  51.  
  52.         CALL GetKey                    ;Call GetKey()
  53.  
  54.        
  55.         CALL SetNewDisketteParameterTable
  56.         ;SetNewDisketteParameterTable()
  57.  
  58.        
  59.         CALL DownloadOS
  60.         CALL GetKey                    ;Call GetKey()
  61.  
  62.         CALL FAR PTR  GiveControlToOS  ;Give Control To OS.
  63.  
  64.  
  65. ret
  66.  
  67. ;/////////////////////////////////////
  68.  
  69. ;//Prints a message to the screen.
  70.  
  71. ;/////////////////////////////////////
  72.  
  73. PrintMessage PROC
  74.         mov DI,AX      ;AX holds the address of the string to Display.
  75.  
  76.         Mov xCursor,1  ;Column.
  77.  
  78.        
  79. ContinuPrinting:
  80.  
  81.         cmp byte ptr [DI],0    ;Did we get to the End of String.
  82.  
  83.         JE EndPrintingMessage  ;if you gat to the end of the string return.
  84.  
  85.        
  86.         mov AH,2               ;Move Cursor
  87.  
  88.         mov DH,yCursor         ;row.
  89.  
  90.         mov DL,xCursor         ;column.
  91.  
  92.         mov BH,0               ;page number.
  93.  
  94.         INT 10h
  95.         INC xCursor
  96.        
  97.         mov AH,0Ah             ;Display Character Function.
  98.  
  99.         mov AL,[DI]            ;character to display.
  100.  
  101.         mov BH,0               ;page number.
  102.  
  103.         mov CX,1               ;number of times to write character
  104.  
  105.         INT 10h
  106.        
  107.        
  108.                
  109.         INC DI                 ;Go to next character.
  110.  
  111.        
  112.         JMP ContinuPrinting    ;go to Print Next Character.
  113.  
  114.                
  115. EndPrintingMessage:
  116.        
  117.         Inc yCursor            ;So Next time the message would
  118.                                ;be printed in the second line.
  119.  
  120.        
  121.         cmp yCursor,25
  122.         JNE dontMoveCorsurToBegin
  123.         Mov yCursor,0
  124.        
  125. dontMoveCorsurToBegin:
  126.         ret
  127.        
  128.                
  129. PrintMessage EndP
  130. ;//////////////////////////////////////
  131.  
  132. ;//Watis for the user to press a key.
  133.  
  134. ;//////////////////////////////////////
  135.  
  136. GetKey PROC
  137.  
  138.         mov ah,0
  139.         int 16h ;Wait for a key press.
  140.  
  141.         Ret
  142.        
  143. GetKey EndP
  144. ;///////////////////////////////////////////
  145.  
  146. ;//Gives Control To Second Part Loader.
  147.  
  148. ;///////////////////////////////////////////
  149.  
  150. GiveControlToOS PROC
  151.  
  152.         LEA AX,szDone
  153.         Call PrintMessage
  154.         CALL GetKey
  155.        
  156.         db 0e9h        ;Far JMP op code.
  157.  
  158.         dw 512         ;JMP 512 bytes ahead.
  159.  
  160.        
  161. ;       POP AX         ;//Another why to make
  162.  
  163.                        ;the CPU jump to a new place.
  164.  
  165. ;       POP AX
  166.  
  167. ;       Push 7E0h      ;Push New CS address.
  168.  
  169. ;       Push 0          ;Push New IP address.
  170.  
  171.                ;The address that comes out is 7E00:0000.
  172.  
  173.                ;(512 bytes Higher from were BIOS Put us.)
  174.  
  175. ;       ret
  176.  
  177.        
  178.        
  179. GiveControlToOS EndP
  180. ;///////////////////////////////////
  181.  
  182. ;//Clear Screen.
  183.  
  184. ;///////////////////////////////////
  185.  
  186. ClearScreen PROC
  187.  
  188.         mov ax,0600h   ;//Scroll All Screen UP to Clear Screen.
  189.  
  190.         mov bh,07
  191.         mov cx,0
  192.         mov dx,184fh  
  193.         int 10h
  194.        
  195.         Mov xCursor,0  ;//Set Corsur Position So next
  196.  
  197.                         //write would start in
  198.                         //the beginning of screen.
  199.         Mov yCursor,0
  200.  
  201.         Ret
  202.        
  203. ClearScreen EndP
  204. ;/////////////////////////////////
  205.  
  206. ;//PrintPlaceMarker.
  207.  
  208. ;/////////////////////////////////
  209.  
  210. PrintPlaceMarker PROC
  211.  
  212.  
  213.         LEA AX,szPlaceMarker
  214.         CALL PrintMessage  ;Call PrintfMessage()
  215.  
  216.         CALL GetKey        ;Call GetKey()
  217.  
  218.         ret
  219.        
  220. PrintPlaceMarker EndP
  221. ;/////////////////////////////////////////
  222.  
  223. ;//Set New Disk Parameter Table
  224.  
  225. ;/////////////////////////////////////////
  226.  
  227. SetNewDisketteParameterTable PROC
  228.  
  229.         LEA DX,StepRateAndHeadUnloadTime
  230.         ;//Get the address of the Disk Parameters Block.
  231.  
  232.        
  233.                ;//Int 1E (that is in address 0:78h)
  234.  
  235.                ;//holds the address of the disk parametrs
  236.  
  237.                ;//block, so now change it to
  238.  
  239.                ;//our parametr black.
  240.  
  241.         ;//DX holds the address of our Parameters block.
  242.  
  243.         MOV WORD PTR CS:[0078h],DX
  244.         MOV WORD PTR CS:[007Ah],0000
  245.        
  246.         ;Reset Drive To Update the DisketteParameterTable.
  247.  
  248.         MOV AH,0
  249.         INT 13H
  250.        
  251.         ret
  252.        
  253. SetNewDisketteParameterTable EndP
  254. ;///////////////////////////////////
  255.  
  256. ;//DownloadOS
  257.  
  258. ;///////////////////////////////////
  259.  
  260. DownloadOS PROC
  261.         mov nDrive,0
  262.         mov nSide,0
  263.         mov nTrack,0
  264.         mov nSector,1
  265.        
  266. ContinueDownload:
  267.        
  268.         INC nSector            ;Read Next Sector.
  269.  
  270.         cmp nSector,19         ;Did we get to end of track.
  271.  
  272.         JNE StayInTrack
  273.         CALL PrintPlaceMarker  ;Print now '~~~~' so the user would
  274.  
  275.                                ;now that we finished reding a track
  276.  
  277.         INC nTrack             ;If we gat to end of track Move to next track.
  278.  
  279.         mov nSector,1          ;And Read Next Sector.
  280.  
  281.         CMP nTrack,5           ;Read 5 Tracks (Modify this value
  282.  
  283.                                ;to how much Tracks you want to read).
  284.  
  285.         JE      EndDownloadingOS
  286.        
  287. StayInTrack:
  288.        
  289.         ;ReadSector();
  290.  
  291.         Call ReadSector
  292.        
  293.        
  294.         JMP     ContinueDownload
  295.         ;If diden't yet finish Loading OS.
  296.  
  297.        
  298. EndDownloadingOS:
  299.  
  300.        ret
  301.        
  302. DownloadOS EndP
  303. ;////////////////////////////////////////
  304.  
  305. ;//Read Sector.
  306.  
  307. ;////////////////////////////////////////
  308.  
  309. ReadSector PROC
  310.  
  311.        mov nTrays,0
  312.        
  313. TryAgain:
  314.  
  315.        mov AH,2               ;//Read Function.
  316.  
  317.        mov AL,1               ;//1 Sector.
  318.  
  319.        mov CH,nTrack
  320.        mov CL,nSector         ;//Remember: Sectors start with 1, not 0.
  321.  
  322.        mov DH,nSide
  323.        mov DL,nDrive
  324.        Mov BX,pOS             ;//ES:BX points to the address
  325.  
  326.                               ;to were to store the sector.
  327.  
  328.        INT 13h
  329.        
  330.  
  331.        CMP AH,0               ;Int 13 return Code is in AH.
  332.  
  333.        JE EndReadSector       ;if 'Sucsess' (AH = 0) End function.
  334.  
  335.  
  336.        mov AH,0               ;Else Reset Drive . And Try Again...
  337.  
  338.        INT 13h
  339.        cmp nTrays,3           ;Chack if you tryed reading
  340.  
  341.                               ;more then 3 times.
  342.  
  343.        
  344.        JE DisplayError        ; if tryed 3 Times Display Error.
  345.  
  346.        
  347.        INC nTrays
  348.        
  349.        jmp TryAgain       ;Try Reading again.
  350.  
  351.        
  352. DisplayError:
  353.        LEA AX,szErrorReadingDrive
  354.        Call PrintMessage
  355.        Call GetKey
  356.        mov AH,0                       ;Reboot Computer.
  357.  
  358.        INT 19h
  359.        
  360.  
  361. EndReadSector:
  362.        ;ADD WORD PTR pOS,512  ;//Move the pointer
  363.  
  364.                               ;(ES:BX = ES:pOS = 0:pOS) 512 bytes.
  365.  
  366.                               ;//Here you set the varible
  367.  
  368.                               ;pOS (pOS points to were BIOS
  369.  
  370.                               ;//Would load the Next Sector).
  371.  
  372.        Ret
  373.        
  374. ReadSector EndP
  375. ;////////////////////////////////////
  376.  
  377. ;//
  378.  
  379. ;////////////////////////////////////
  380.  
  381. END ProgramStart
  382.  

  383. Points of interest

    It took some time until I got a running boot sector. I would list a couple of bugs I had in the beginning while writing my boot sector.

    1. I didn’t set up a right stack frame.
    2. I didn’t modify the Disk Parameter Block.
    3. I loaded the operating system to areas that are used by BIOS routines (and even to the Interpret table).
    4. In the end of the boot sector, it must have the bytes 0x55h 0x0AAh (this is a signature that it is a valid bootable sector).

    (For using BOOTSectorUtility.exe, you would need the .NET 2.0 Framework installed on your computer. To download the .NET 2.0 Framework, click here.) The building files are located in the download section.

    Courtesy of
    S Keller from codeproject
    Occupation: Systems Engineer
    Location: United States United States
    http://www.codeproject.com/KB/system/MakingOS.aspx