Operating System in C++

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Operating System in C++

Post by pcmattman »

Hello all, I'm new to this forum but I hope that I can finally get some results!

I've written a bootloader that runs a COM file which is in the root directory of the disk.

The problem is, the kernel (ie. the COM file) is all written in assembly and I would like to be able to write it in C++. Most of the time the bootloader starts, displaying it's loading text, then nothing happens (this is where the COM is meant to run)... This is really frustrating me as I do not want to write my operating system in assembly, chiefly because it is much more difficult to write in.

The bootloader source:

Code: Select all

; Mattise Boot Sector
; Assemble with NASM

            bits 16
            org 0x7C00

start:      jmp short begin
            nop
bsOEM       db "Mattise1"               ; OEM String
bsSectSize  dw 512                      ; Bytes per sector
bsClustSize db 1                        ; Sectors per cluster
bsRessect   dw 1                        ; # of reserved sectors
bsFatCnt    db 2                        ; # of fat copies
bsRootSize  dw 224                      ; size of root directory
bsTotalSect dw 2880                     ; total # of sectors if < 32 meg
bsMedia     db 0xF0                     ; Media Descriptor
bsFatSize   dw 9                        ; Size of each FAT
bsTrackSect dw 18                       ; Sectors per track
bsHeadCnt   dw 2                        ; number of read-write heads
bsHidenSect dd 0                        ; number of hidden sectors
bsHugeSect  dd 0                        ; if bsTotalSect is 0 this value is
                                        ; the number of sectors
bsBootDrv   db 0                        ; holds drive that the bs came from
bsReserv    db 0                        ; not used for anything
bsBootSign  db 29h                      ; boot signature 29h
bsVolID     dd 0                        ; Disk volume ID also used for temp
                                        ; sector # / # sectors to load
bsVoLabel   db "MATTISEOS  "            ; Volume Label
bsFSType    db "FAT12   "               ; File System type

begin:      cli                         ; disable interrupts
            mov [bsBootDrv],dl          ; save drive number
            mov ax,0x9000               ; put stack at 0x98000
            mov ss,ax
            mov sp,0x8000

            mov cx,[bsTrackSect]        ; update int 1E FDC param table
            mov bx,0x0078
            lds si,[ds:bx]
            mov byte [si+4], cl
            mov byte [si+9], 0x0F

            sti                         ; enable interrupts
            push ds
            mov dl,[bsBootDrv]          ; reset controller
            xor ax,ax
            int 0x13
            pop ds
            jc bootfail2                ; display error message
            jmp _l1
bootfail2:  jmp bootfail
_l1:
            mov ax,0x0000
            mov es,ax
            mov ds,ax

            mov si,MsgLoad              ; display load message
            call putstr

            ; find the root directory

            xor ax,ax
            mov al,[bsFatCnt]
            mov bx,[bsFatSize]
            mul bx
            add ax,word [bsHidenSect]
            adc ax,word [bsHidenSect+2]
            add ax,word [bsRessect]     ; ax holds root directory location
            mov word [BootSig],ax

            call checkroot

            xor ax,ax
            add ax,word [start]
            add ax,word [bsVolID]       ; sector number
            add ax,word [BootSig]
            sub ax,2                    ; correction for a mis-calc
            mov cx,word [bsVolID+2]     ; number of sectors

            mov bx,0x8000
            mov es,bx


nextsector: push ax                     ; save registers
            push cx
            push dx
            push es

            xor bx,bx                   ; set zero offset
            call readsect               ; read a sector

            mov si,MsgDot               ; display a dot
            call putstr

            pop es                      ; restore registers
            pop dx
            pop cx
            pop ax
            mov bx,es
            add bx,20h                  ; increment address 512 bytes
            mov es,bx
            inc ax                      ; read next sector
            loopnz nextsector

            mov ax,0x8000               ; set segment registers and jump
            mov es,ax
            mov ds,ax
            push ax
            mov ax,0
            push ax
            retf

checkroot:
            push ax                     ; save registers
            push bx
            push cx
            push dx
            push si
            push di
            
            mov ax,0x8000               ; put root directory at 0x80000
            mov es,ax
            mov ax,32                   ; AX = ((32*RootSize)/512) + 2
            mul word [bsRootSize]
            div word [bsSectSize]
            mov cx,ax                   ; cx holds # of sectors in root
            mov word [start],ax
            mov ax,word [BootSig]       ; get prev. saved loc. for root dir

r1:         xor bx,bx
            push cx                     ; save count
            push ax                     ; save sector number
            push es
            push dx
            call readsect
            xor bx,bx
l_1:        mov di,bx                   ; set address to check from
            mov cx,11                   ; check 11 bytes
            mov si,FileName             ; address of string to check with
            repz cmpsb
            je foundit
            add bx,32                   ; check next entry
            cmp bx,[bsSectSize]         ; end of sector?
            je l_2
            jmp l_1
l_2:        pop dx                      ; restore registers
            pop es
            pop ax
            pop cx
            inc ax                      ; read next sector
            loopnz r1
            jmp bootfail
foundit:    pop dx                      ; get these off the stack
            pop es
            pop ax
            pop cx

            mov di,0x1A                 ; get clustor #
            add di,bx
            push bx                     ; save bx for finding # of sectors
            mov ax,[es:di]
            xor bx,bx                   ; calculate sector #
            mov bl,[bsClustSize]
            mul bx                      ; ax holds sector #
            mov word [bsVolID],ax

            pop bx                      ; get location of directory entry
            mov di,0x1C
            add di,bx
            mov ax,[es:di]              ; put number of bytes in ax
            xor dx,dx
            mov bx,[bsClustSize]        ; # of bytes / 512
            div bx
            inc ax
            mov word [bsVolID+2],ax     ; save number of sectors to load

            pop di                      ; restore registers
            pop si
            pop dx
            pop cx
            pop bx
            pop ax
            
            ret                         ; return to caller
            
putstr:     ; SI = address of string to display
            lodsb
            or al,al
            jz short putstrd
            mov ah,0x0E
            mov bx,0x0007
            int 0x10
            jmp putstr
putstrd:    retn                        ; return to caller

bootfail:   ; display failure message
            mov si,MsgBad               ; display error message
            call putstr
            xor ax,ax                   ; wait for keypress
            int 0x16
            int 0x19                    ; reboot

readsect:   ; ES:BX = Location ; AX = Sector
            mov si,[bsTrackSect]
            div si                      ; divide logical sect by track size
            inc dl                      ; sector # begins at 1
            mov [bsReserv],dl           ; sector to read
            xor dx,dx                   ; logical track left in ax
            div word [bsHeadCnt]        ; leaves head in dl, cyl in ax
            mov dh, [bsBootDrv]         ;
            xchg dl,dh                  ; head to dh, drive to dl
            mov cx,ax                   ; cyl to cx
            xchg cl,ch                  ; low 8 bits of cyl to ch, hi 2 bits
            shl cl,6                    ; shifted to bits 6 and 7
            or cl, byte [bsReserv]      ; or with sector number
            mov al,1                    ; number of sectors
            mov ah,2                    ; use read function of int 0x13
            int 0x13                    ; read sector
            jc bootfail                 ; display error message
            ret                         ; return to caller

FileName    db "OSLOADERCOM"
MsgBad      db "Could not boot!",13,10,0
MsgDot      db ".",0
Newline	    db 13,10,0
MsgLoad     db "Mattise Loading...",13,10,"Reading files",0
padding     times 22 db 0
BootSig     db 0x55, 0xAA
Any help would be greatly appreciated - I'm going bald with frustration!
User avatar
Alboin
Member
Member
Posts: 1466
Joined: Thu Jan 04, 2007 3:29 pm
Location: Noricum and Pannonia

Post by Alboin »

Writing a kernel in C++ is nearly identical (Or somewhat close, that is....) to writing one in C. As long as your bootloader can read a file and jump to it your set. I suggest you look at FritzOS, here. Moreover, there is an article in the wiki. 8) I've written a small C++ kernel once, couldn't do much, so I don't know how much help I'll be. :D

PS: If your having troubles with the bootloader, you might want to try GRUB for the time being, so you can focus on the actually kernel.
C8H10N4O2 | #446691 | Trust the nodes.
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Post by Solar »

BareBonesC++ might give you a hint at how to jump from Assembler to C++.
Every good solution is obvious once you've found it.
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Post by pcmattman »

Hmmmm. I think I've changed my mind, I'll now work with C... One thing though, I'm using DJGPP to compile and build the BareBones loader code but it just isn't working!

The outcome from the assemble and link:

Code: Select all

mcloader.s: Assembler messages:
mcloader.s:0: Warning: end of file in comment; newline inserted
mcloader.s:19: Error: junk at end of line, first unrecognized character is `,'
c:/djgpp/bin/ld.exe: warning: cannot find entry symbol _loader; defaulting to 00100000
I'm running all this on my Windows box, I don't have a Linux installation (my install disks don't like me) and it would be nice if anyone could help me.
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Post by Solar »

The problem comes from this line:

Code: Select all

.comm stack, STACKSIZE, 32      # reserve 16k stack on a quadword boundary
A third parameter to .comm is supported only for ELF targets. I don't know if DJGPP supports ELF (never used DJGPP myself), but the "canon" solution is to build a GCC Cross-Compiler for your OS work (which will also work around several other issues, like unresolved references to __alloca, warnings about "PE operations on non-PE files", or yourself including system headers "by accident".
Every good solution is obvious once you've found it.
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Post by pcmattman »

Would I be able to strip the headers from an EXE file that has no includes and only uses BIOS interrupts and use the resulting file as my kernel?

I assume that the instructions in the wiki for creating a GCC Cross-Compiler are for a UNIX system, and I need to make changes to the instructions for a Windows system?

If I need any help, I'll just reply/edit here.

Note: I'd like to keep the bootloader, and I'd prefer not to use GRUB.
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Post by Solar »

pcmattman wrote:Would I be able to strip the headers from an EXE file that has no includes and only uses BIOS interrupts and use the resulting file as my kernel?
I haven't really checked your bootloader, and never used anything but GRUB myself, so I can't really tell.
I assume that the instructions in the wiki for creating a GCC Cross-Compiler are for a UNIX system, and I need to make changes to the instructions for a Windows system?
No. The instructions are for a Windows system; actually, they were written for Cygwin, but it should work for DJGPP too. (Unix systems don't really need a cross-compiler to "speak ELF".)
Every good solution is obvious once you've found it.
User avatar
bubach
Member
Member
Posts: 1223
Joined: Sat Oct 23, 2004 11:00 pm
Location: Sweden
Contact:

Post by bubach »

If you want to use the 16-bit EXE format and BIOS interrupts, I suggest that you download: http://alexfru.chat.ru/epm.html#bootprog
And test it with a free C compiler capable of producing 16-bit EXE's, like:
http://www.openwatcom.org/
"Simplicity is the ultimate sophistication."
http://bos.asmhackers.net/ - GitHub
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Post by pcmattman »

Thankyou, I'll try that and see if I can get it to work.
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Post by pcmattman »

Ok, I have a new problem. I got the Watcom compiler and the bootloader program, which works when loading the demos that come with it. However, I cannot figure out how to create a 16-bit exe with watcom, let alone one for my OS.

I tried using GCC, but the resulting file does not work at all. I'm really confused and this is really frustrating so some help would be really nice.
User avatar
JackScott
Member
Member
Posts: 1036
Joined: Thu Dec 21, 2006 3:03 am
Location: Hobart, Australia
Mastodon: https://aus.social/@jackscottau
Matrix: @JackScottAU:matrix.org
GitHub: https://github.com/JackScottAU
Contact:

Post by JackScott »

I'm assuming you're using OpenWatcom compilers.

I would try first reading the User's Guide, available from http://www.openwatcom.org/index.php/Manuals. I'm suspecting (without having ever used the compiler myself) that you aren't setting the right instruction set. Section 2.1 of that guide might help.

GCC (afaik) can't produce 16bit executables for x86, so that might be why it isn't working for you. I think.
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Post by pcmattman »

All I get are GPF's when I try to compile... All I need is something that can compile and link some files into a 16-bit COM file with origin at 0x0000!

Every time I run my assembly code, everything works - command line:

Code: Select all

nasmw -f bin -o kernel.com kernel.asm
This works, the OS runs, and everyone seems happy. Would it work better if I used this working code as a second-stage bootloader, which then loads whatever.bin into memory and jumps to it?

If so, how do I do this?
Post Reply