Anyways, here's the example. "kernel.s" Excuse the tabbing of it.
Code: Select all
/* Setup */
// Specify the bits of the program
#pragma bits 32 // 32-bit program
#pragma org 0x100000 // go to 1st megabyte of memory
/* Defines */
// Multiboot information
#define MBOOT_MAGIC 0x1BADB002
#define MBOOT_PAGE_ALIGN 1<<0
#define MBOOT_MEM_INFO 1<<1
#define MBOOT_FLAGS MBOOT_PAGE_ALIGN|MBOOT_MEM_INFO
#define MBOOT_CHECKSUM -(MBOOT_MAGIC+MBOOT_FLAGS)
/* Macros */
// Bit Management
#define Bits8To16(a,b) ((u16)(((u8)(a))|(((u16)((u8)(b)))<<8)))
#define Bits16To32(a,b) ((u32)(((u16)(a))|(((u32)((u16)(b)))<<16)))
#define Bits8To32(a,b,c,d) (Bits16To32(Bits8To16(a,b),Bits8To16(c,d)))
#define Bits32To16L(a) ((u16)((u32)(a)))
#define Bits32To16H(a) ((u16)(((u32)(a)>>16)&0xFFFF))
#define Bits16To8L(a) ((u8)(a))
#define Bits16To8H(a) ((u8)(((u16)(a)>>8)&0xFF))
#define Bits32To8LL(a) (Bits16To8L(Bits32To16L(a)))
#define Bits32To8LH(a) (Bits16To8L(Bits32To16H(a)))
#define Bits32To8HL(a) (Bits16To8H(Bits32To16L(a)))
#define Bits32To8HH(a) (Bits16To8H(Bits32To16H(a)))
// Bit Management (Windows Equivalents)
#define LOWORD(a) (Bits32To16L(a))
#define HIWORD(a) (Bits32To16H(a))
#define LOBYTE(a) (Bits16To8L(a))
#define HIBYTE(a) (Bits16To8H(a))
// Flags & Bits
#define MakeFlag(id) (((u32)1)<<(id))
#define ClearBit(value,bit) (value &= ~(1<<bit))
#define SetBit(value,bit) (value |= (1<<bit))
#define GetBit(value,bit) (value & (1<<bit))
/* Packed Types */
// GDT Entry
type GDT_ENTRY
{
u16 limitLow;
u16 baseLow;
u8 baseMiddle;
u8 access;
u8 granularity;
u8 baseHigh;
} packed(1);
// GDT Pointer
type GDT_PTR
{
u16 limit;
u32 base;
} packed(1);
// Task State Segment
type TSS_ENTRY
{
u32 prevTss;
u32 esp0;
u32 ss0;
u32 esp1;
u32 ss1;
u32 esp2;
u32 ss2;
u32 cr3;
u32 eip;
u32 eflags;
u32 eax;
u32 ecx;
u32 edx;
u32 ebx;
u32 esp;
u32 ebp;
u32 esi;
u32 edi;
u32 es;
u32 cs;
u32 ss;
u32 ds;
u32 fs;
u32 gs;
u32 ldt;
u16 trap;
u16 ioMapBase;
} packed(1);
// Screen character
type TEXTMODE_CHAR
{
u8 c; // Character
u8 a; // Attribute
} packed(1); // pack down to the byte
/* Variables */
// From the linker script
extern u32 code;
extern u32 bss;
extern u32 end;
// Reserved data. (This will go in the .bss section)
reserve u8 _kernelStack[8192]; // Allocate space for the kernel's stack
/* Program Entries */
// Data for the very start of the program
__head
{
/*
This information will appear at the very beginning of the file's .text
section. The dd() function simply writes a dword to the file as data.
It is recommended that you do NOT call this function
*/
mboot:
// Required values of the multiboot header
dd(MBOOT_MAGIC);
dd(MBOOT_FLAGS);
dd(MBOOT_CHECKSUM);
// Information
dd($mboot); // mboot is a label, but we're treating it as a variable
dd($code);
dd($bss);
dd($end);
dd($start); // start is a function, but we're treating it as a variable
// Video
dd(0); //mode
dd(0); //width
dd(0); //height
dd(0); //depth
}
/* Functions */
// Start of the program
nakedfunction start()
{
// Handle the inline assembly
asm
{
// Setup the stack
mov esp, $_kernelStack
// Push the data to the header
push ebx // boot params
push eax // 0x2BADB002
// Stop interrupts
cli
// Call the main function
call $kmain
// If we reach this point... STOP
cli
hlt
}
}
// Flush the TSS
fastfunction tssFlush()
{
asm
{
mov ax, 0x2B
ltr ax
}
}
// Set a GDT gate
fastfunction gdtSetGate(u32 index, u32 base, u32 limit, u8 access, u8 granularity)
{
// Set the base address
$g_gdtEnt[$index].baseLow = (u16)($base&0xFFFF);
$g_gdtEnt[$index].baseMiddle = (u8)(($base>>16)&0xFF);
$g_gdtEnt[$index].baseHigh = (u8)(($base>>24)&0xFF);
// Set the access flag
$g_gdtEnt[$index].access = $access;
// Set the limit and granularity
$g_gdtEnt[$index].limitLow = (u16)($limit&0xFFFF);
$g_gdtEnt[$index].granularity = (u8)((($limit>>16)&0x0F)|($granularity&0xF0));
}
// Write the TSS
fastfunction gdtSetTss(u32 index, u16 ss0, u32 esp0)
{
// Preliminary
u32 base, limit;
// Calculate the base and limit
$base = (u32)@g_tssEnt;
$limit = $base+sizeof(TSS_ENTRY);
// Add the TSS to the GDT
gdtSetGate($index, $base, $limit, 0xE9, 0x00);
// Zero out the TSS
kMemSet((u8 *)@g_tssEnt, 0, sizeof(TSS_ENTRY));
// Setup the stack segment/pointer
$g_tssEnt.ss0 = $ss0;
$g_tssEnt.esp0 = $esp0;
// Set all the segments
$g_tssEnt.cs = 0x0B;
$g_tssEnt.ss = 0x13;
$g_tssEnt.ds = 0x13;
$g_tssEnt.es = 0x13;
$g_tssEnt.fs = 0x13;
$g_tssEnt.gs = 0x13;
}
// Initialize the GDT
function gdtInit()
{
// Setup the pointer
$g_gdtPtr.limit = (sizeof(GDT_ENTRY)*5)-1;
$g_gdtPtr.base = (u32)@g_gdtEnt[0];
// Setup the segments
gdtSetGate(0, 0x00000000, 0x00000000, 0x00, 0x00); // Null segment
gdtSetGate(1, 0x00000000, 0xFFFFFFFF, 0x9A, 0xCF); // Kernel code segment
gdtSetGate(2, 0x00000000, 0xFFFFFFFF, 0x92, 0xCF); // Kernel data segment
gdtSetGate(3, 0x00000000, 0xFFFFFFFF, 0xFA, 0xCF); // User code segment
gdtSetGate(4, 0x00000000, 0xFFFFFFFF, 0xF2, 0xCF); // User data segment
//gdtSetTss(5, 0x0010, 0x00000000); // TSS
// Flush the GDT (and TSS)
gdtFlush();
tssFlush();
}
// Modify the kernel stack
function gdtSetKernelStack(u32 esp)
{
$g_tssEnt.esp0 = $esp;
}
// The actual main entry point for the logic of the program
function kmain(u32 magic, MULTIBOOT_INFO* pMBoot) // function keyword uses __stdcall convention. Use cdeclfunction for C styled functions.
{
// Unused parameters
#pragma unused($magic) // avoid "unused parameter" warnings
#pragma unused($pMBoot) // avoid "unused parameter" warnings
// Initialize
gdtInit();
ktmInit();
// Write something to the screen
ktmWrite("Hello, World!\n\tThis is a test.");
}
-naota