I'm going to preface this by saying that I'm far from good at C or C++, but nonetheless I've decided to try and write my kernel in it... my problem lies with writting a string (char array), I can write a character but as soon as I give it an array of chars it doesn't work.
I've also done some testing and I found out that it apperently sees the char array length as being 0.
Here's my kernel...
#include "kernel.h"
void clear_screen(enum vga_color back, enum vga_color fore);
void print(char* msg, unsigned int line, enum vga_color back, enum vga_color fore);
int hex_combine(unsigned int x, unsigned int y);
int get_length(char* str);
void main(void)
{
clear_screen(BLUE, WHITE);
print("Unga Bunga", 0, BLUE, WHITE);
for(;;);
};
void clear_screen(enum vga_color back, enum vga_color fore)
{
unsigned char *vidmem = (unsigned char *) VGA_ADDR;
unsigned int i = 0;
while (i < (80*25*2))
{
vidmem[i] = ' ';
vidmem[i+1] = hex_combine(back, fore);
i+=2;
};
};
void print(char* msg, unsigned int line, enum vga_color back, enum vga_color fore)
{
unsigned char* vidmem = (unsigned char*) VGA_ADDR;
unsigned int i = (line*80*2);
for (char* chr = msg; *chr != '\0'; chr++)
{
vidmem[i] = *chr;
vidmem[i+1] = hex_combine(back, fore);
chr++;
i+=2;
};
};
int hex_combine(unsigned int x, unsigned int y) { return (x<<4)|(y); }
int get_length(char* str)
{
char* c;
for (c = str; *c != '\0'; c++);
return c - str;
}
And here's the header file for the kernel...
#ifndef KERNEL_H
#define KERNEL_H
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int uint32;
#define VGA_ADDR 0xB8000
#define BUFSIZE 2200
uint16* vga_buffer;
#define NULL 0
enum vga_color {
BLACK,
BLUE,
GREEN,
CYAN,
RED,
MAGENTA,
BROWN,
GREY,
DARK_GREY,
BRIGHT_BLUE,
BRIGHT_GREEN,
BRIGHT_CYAN,
BRIGHT_RED,
BRIGHT_MAGENTA,
YELLOW,
WHITE,
};
#endif
I don't know if you need it, but here's my bootloader...
[org 0x7C00]
KERNEL_ADDRESS equ 0x100000
cli
lgdt [gdt_descriptor]
; Switch to PM
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp 0x8:init_pm
[bits 32]
init_pm:
mov ax, 0x10
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
call build_page_tables
; Enable PAE
mov eax, cr4
or eax, 1 << 5
mov cr4, eax
mov eax, cr4
or eax, 1 << 7
mov cr4, eax
mov eax, 0x1000
mov cr3, eax
; Set LME bit in EFER register
mov ecx, 0xC0000080 ; Operand of 'rdmsr' and 'wrmsr'
rdmsr ; Read before so we don't overwrite the content
or eax, 1 << 8 ; eax : operand of wrmsr
wrmsr
; Enable paging by setting CR0.PG bit to 1
mov eax, cr0
or eax, (1 << 31)
mov cr0, eax
; Load 64-bit GDT
lgdt [gdt64_descriptor]
; Jump to code segment in 64-bit GDT
jmp 0x8:init_lm
[bits 64]
init_lm:
mov ax, 0x10
mov fs, ax
mov gs, ax
mov rbp, 0x90000
mov rsp, rbp
; Load kernel from disk
xor ebx, ebx ; Upper 2 bytes above bh in ebx is for cylinder = 0x0
mov bl, 0x2 ; Read from 2nd sector
mov bh, 0x0 ; Head
mov ch, 1 ; Read 1 sector
mov rdi, KERNEL_ADDRESS
call ata_chs_read
jmp KERNEL_ADDRESS
jmp $
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;[bits 16]
;; http://wiki.osdev.org/ATA_in_x86_RealMode_%28BIOS%29
;load_loader:
;mov bx, LOADER_OFFSET
;mov dh, 1 ; Load 1 sector (max allowed by BIOS is 128)
;mov dl, 0x80 ; Drive number
;mov ah, 0x02 ; Read function
;mov al, dh
;mov ch, 0x00 ; Cylinder
;mov dh, 0x00 ; Head
;; !! Sector is 1-based, and not 0-based
;mov cl, 0x02 ; 1st sector to read
;int 0x13
;ret
[bits 32]
build_page_tables:
; PML4 starts at 0x1000
mov eax, 0x2000 ; PDP base address
or eax, 0b11 ; P and R/W bites
mov ebx, 0x1000 ; MPL4 base address
mov [ebx], eax
mov eax, 0x3000 ; PD base address
mov ebx, 0x2000 ; PDP physical address
mov ecx, 64 ; 64 PDP
build_PDP:
or eax, 0b11
mov [ebx], eax
add ebx, 0x8
add eax, 0x1000 ; Next PD page base address
loop build_PDP
mov eax, 0x0 ; 1st page physical base address
mov ebx, 0x3000 ; PD physical base address
mov ecx, 512
build_PD:
or eax, 0b10000011 ;P + R/W + PS (bit for 2MB page)
mov [ebx], eax
add ebx, 0x8
add eax, 0x200000 ; Next 2MB physical page
loop build_PD
;(tables end at 0x4000 => fits before Bios boot sector at 0x7c00)
ret
;=============================================================================
; ATA read sectors (CHS mode)
; Max head index is 15, giving 16 possible heads
; Max cylinder index can be a very large number (up to 65535)
; Sector is usually always 1-63, sector 0 reserved, max 255 sectors/track
; If using 63 sectors/track, max disk size = 31.5GB
; If using 255 sectors/track, max disk size = 127.5GB
; See OSDev forum links in bottom of [http://wiki.osdev.org/ATA]
;
; @param EBX The CHS values; 2 bytes, 1 byte (BH), 1 byte (BL) accordingly
; @param CH The number of sectors to read
; @param RDI The address of buffer to put data obtained from disk
;
; @return None
;=============================================================================
[bits 64]
ata_chs_read: pushfq
push rax
push rbx
push rcx
push rdx
push rdi
mov rdx, 0x1f6 ; Port to send drive & head numbers
mov al, bh ; Head index in BH
and al, 00001111b ; Head is only 4 bits long
or al, 10100000b ; Default 1010b in high nibble
out dx, al
mov rdx, 0x1f2 ; Sector count port
mov al, ch ; Read CH sectors
out dx, al
mov rdx, 0x1f3 ; Sector number port
mov al, bl ; BL is sector index
out dx, al
mov rdx, 0x1f4 ; Cylinder low port
mov eax, ebx ; Byte 2 in ebx, just above BH
mov cl, 16
shr eax, cl ; Shift down to AL
out dx, al
mov rdx, 0x1f5 ; Cylinder high port
mov eax, ebx ; Byte 3 in ebx, just above byte 2
mov cl, 24
shr eax, cl ; Shift down to AL
out dx, al
mov rdx, 0x1f7 ; Command port
mov al, 0x20 ; Read with retry
out dx, al
.still_going: in al, dx
test al, 8 ; The sector buffer requires servicing
jz .still_going ; Until the sector buffer is ready
mov rax, 512/2 ; To read 265 words = 1 sector
xor bx, bx
mov bl, ch ; Read CH sectors
mul bx
mov rcx, rax ; RCX is counter for INSW
mov rdx, 0x1f0 ; Data port, in and out
rep insw ; In to [RDI]
pop rdi
pop rdx
pop rcx
pop rbx
pop rax
popfq
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[bits 16]
GDT:
; Null :
dd 0x0
dd 0x0
; Code :
dw 0xffff ; Limit
dw 0x0 ; Base
db 0x0 ; Base
db 10011010b ; 1st flag, Type flag
db 11001111b ; 2nd flag, Limit
db 0x0 ; Base
; Data :
dw 0xffff
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
gdt_descriptor:
dw $ - GDT - 1 ; 16-bit size
dd GDT ; 32-bit start address
[bits 32]
GDT64:
; Null :
dq 0x0
; Code :
dd 0x0
db 0x0
db 0b10011000
db 0b00100000
db 0x0
; Data :
dd 0x0
db 0x0
db 0b10010000
db 0b00000000
db 0x0
gdt64_descriptor:
dw $ - GDT64 - 1 ; 16-bit size
dd GDT64 ; 32-bit start address
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
[bits 16]
times 510 - ($-$$) db 0
dw 0xaa55
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
I compile the whole thing using the bat file...
@echo off
wsl nasm -f bin boot.asm -o boot.bin -l dump/boot.list
wsl nasm -f elf64 loader.asm -o loader.o -l dump/loader.list
wsl cc -m64 -masm=intel -c kernel.c
wsl ld -Ttext 0x100000 -o kernel.elf loader.o kernel.o
wsl objcopy -R .note -R .comment -S -O binary kernel.elf kernel.bin
wsl dd if=/dev/zero of=images/image.bin bs=512 count=2880
wsl dd if=boot.bin of=images/image.bin conv=notrunc
wsl dd if=kernel.bin of=images/image.bin conv=notrunc bs=512 seek=1
del boot.bin kernel.bin kernel.o loader.o kernel.elf
qemu-system-x86_64 images/image.bin
pause > nul
cls