I'm currently trying to make my own OS (and pretty new to assembly and C). Here is the code (I know it's not finished at all)
bootloader.asm
:
[bits 16]
0x7C00
start:
cli
call EnableA20
lgdt [gdt_descriptor]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp 0x08:Protected
gdt_start:
dq 0
dq 0x00CF9A000000FFFF
dq 0x00CF92000000FFFF
gdt_end:
gdt_descriptor:
dw gdt_end - gdt_start - 1
dd gdt_start
EnableA20:
in al, 0x64
.wait1:
test al, 2
jnz .wait1
mov al, 0xD1
out 0x64, al
.wait2:
in al, 0x64
test al, 2
jnz .wait2
mov al, 0xDF
out 0x60, al
ret
[bits 32]
Protected:
mov ax, 0x10
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov esp, 0x90000
jmp 0x1000:0x00000000
jmp $
times 510 - ($ - $$) db 0
dw 0xAA55
kernel.c
:
/*
INCLUDE
*/
#include "stdint.h"
#include "stdbool.h"
#include <stddef.h>
/*
DEFINE
*/
#define PIT_COMMAND 0x43
#define PIT_CHANNEL0 0x40
#define PIT_FREQUENCY 1193182
#define KEYBOARD_DATA_PORT 0x60
#define PIC_EOI 0x20
#define MAX_COMMAND_LENGTH 256
/*
VARIABLE
*/
bool isPressed = false;
int table_length;
char CurrentCommand[MAX_COMMAND_LENGTH] = {0};
int CommandLength = 0;
unsigned char scancode;
volatile uint8_t timer_expired = 0;
int color = 0x0f;
int row = 0;
/*
IO FUNCTION
*/
static inline uint16_t inw(uint16_t port) {
uint16_t value;
__asm__ volatile ("inw %1, %0" : "=a"(value) : "d"(port));
return value;
}
static inline void outw(uint16_t port, uint16_t value) {
__asm__ volatile ("outw %0, %1" : : "a"(value), "d"(port));
}
unsigned char inb(unsigned short port) {
unsigned char result;
__asm__("inb %1, %0" : "=a"(result) : "dN"(port));
return result;
}
void outb(unsigned short port, unsigned char value) {
__asm__ volatile ("outb %0, %1" : : "a"(value), "dN"(port));
}
/*
IMPORTANT FUNCTION
*/
long strtol(const char *str, char **endptr, int base) {
const char *s = str;
long result = 0;
int sign = 1;
while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r' || *s == '\f' || *s == '\v') {
s++;
}
if (*s == '-') {
sign = -1;
s++;
} else if (*s == '+') {
s++;
}
if (base == 0) {
if (*s == '0') {
if (*(s + 1) == 'x' || *(s + 1) == 'X') {
base = 16;
s += 2;
} else {
base = 8;
s++;
}
} else {
base = 10;
}
}
if (base < 2 || base > 36) {
if (endptr) *endptr = (char *)str;
return 0;
}
while (*s) {
int digit = 0;
if (*s >= '0' && *s <= '9') {
digit = *s - '0';
} else if (*s >= 'a' && *s <= 'z') {
digit = *s - 'a' + 10;
} else if (*s >= 'A' && *s <= 'Z') {
digit = *s - 'A' + 10;
} else {
break;
}
if (digit >= base) {
break;
}
result = result * base + digit;
s++;
}
result *= sign;
if (endptr) {
*endptr = (char *)s;
}
return result;
}
int strcmp(const char *s1, const char *s2) {
while (*s1 && *s2 && *s1 == *s2) {
s1++;
s2++;
}
return *s1 - *s2;
}
int strlen(const char *str) {
int len = 0;
while (str[len]) len++;
return len;
}
void memset(char *buffer, char value, int size) {
for (int i = 0; i < size; i++) {
buffer[i] = value;
}
}
/*
VIDEO RELATED
*/
void RenderTextAt(const char* text, int x, int y, int color) {
char* screen = (char*) 0xB8000;
int index = (y * 80 + x) * 2;
for (int i = 0; text[i] != '\0' && index < 80 * 25 * 2; i++) {
screen[index] = text[i];
screen[index + 1] = color;
index += 2;
}
}
void RenderText(const char* text, int color) {
RenderTextAt(text,0,0,color);
}
void clear_screen() {
char *video_memory = (char *)0xB8000;
for (int i = 0; i < 80 * 25 * 2; i += 2) {
video_memory[i] = ' ';
video_memory[i + 1] = 0x07;
}
}
void ClearRegion(int x, int y, int width, int height) {
char* video_memory = (char*) 0xB8000;
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
int index = ((y + row) * 80 + (x + col)) * 2;
video_memory[index] = ' ';
video_memory[index + 1] = 0x07;
}
}
}
/*
TIME REALTED
*/
void timer_isr() {
timer_expired = 1;
outb(0x20, PIC_EOI);
}
void sleep(uint32_t milliseconds) {
uint32_t ticks = milliseconds * (PIT_FREQUENCY / 1000);
outb(PIT_COMMAND, 0x36);
outb(PIT_CHANNEL0, ticks & 0xFF);
outb(PIT_CHANNEL0, (ticks >> 8) & 0xFF);
__asm__ volatile ("sti");
timer_expired = 0;
while (!timer_expired) {
__asm__ volatile ("hlt");
}
__asm__ volatile ("cli");
}
/*
INPUT RELATED
*/
typedef struct {
const char cle;
const char* value;
} ScancodeTable;
ScancodeTable FR[] = {
{0x0E, "BS"}, {0x1C, "Enter"}, {0x10, "a"}, {0x30, "b"}, {0x2E, "c"}, {0x20, "d"},
{0x12, "e"}, {0x21, "f"}, {0x22, "g"}, {0x23, "h"}, {0x17, "i"}, {0x24, "j"},
{0x25, "k"}, {0x26, "l"}, {0x27, "m"}, {0x31, "n"}, {0x18, "o"}, {0x19, "p"},
{0x1E, "q"}, {0x13, "r"}, {0x1F, "s"}, {0x14, "t"}, {0x16, "u"}, {0x2F, "v"},
{0x2C, "w"}, {0x2D, "x"}, {0x15, "y"}, {0x11, "z"}
};
const char* getKey(ScancodeTable table[], unsigned char code) {
for (int i = 0; i < table_length; i++) {
if (table[i].cle == code) {
return table[i].value;
}
}
return NULL;
}
void keyboard_interrupt_handler() {
scancode = inb(KEYBOARD_DATA_PORT);
if (!(scancode & 0x80)) {
isPressed = true;
}
outb(0x20, PIC_EOI);
}
/*
ACPI AND SHUTDOWN RELATED
*/
struct RSDPDescriptor {
char Signature[8];
uint8_t Checksum;
char OEMID[6];
uint8_t Revision;
uint32_t RsdtAddress;
} __attribute__((packed));
struct FADT {
char Signature[4];
uint32_t Length;
uint8_t Unused[76];
uint32_t PM1aControlBlock;
uint32_t PM1bControlBlock;
} __attribute__((packed));
void acpi_shutdown() {
uint16_t pm1a_control = 0xB004;
outw(pm1a_control, 0x2000);
}
/*
ROOT COMMAND
*/
void setcolor(){
RenderTextAt("Code ? Ignore 0x", 0, row, color);
row += 1;
while (!isPressed);
isPressed = 0;
const char* key = getKey(FR, scancode);
if (!key || strlen(key) != 1) {
RenderTextAt("Invalid argument", 0, row, 0x04);
return;
}
char key1 = key[0];
RenderTextAt(&key1, 0, row, color);
while (!isPressed);
isPressed = 0;
const char* key2 = getKey(FR, scancode);
if (!key2 || strlen(key2) != 1) {
RenderTextAt("Invalid argument", 0, row, 0x04);
return;
}
char key2_char = key2[0];
RenderTextAt(&key2_char, 1, row, color);
char result[3] = {key1, key2_char, '\0'};
color = (int)strtol(result, NULL, 16);
row += 1;
}
/*
COMMAND HANDLING
*/
void RunCommand(const char* command, int length) {
if (length == 0) return;
if (strcmp(command, "clear") == 0) {
clear_screen();
row = 0;
} else if (strcmp(command, "setcolor") == 0) {
setcolor();
} else if(strcmp(command,"shutdown") == 0) {
acpi_shutdown();
}else{
RenderTextAt("Unknown command", 0, row, 0x04);
}
CommandLength = 0;
memset(CurrentCommand, 0, MAX_COMMAND_LENGTH);
}
/*
MAIN
*/
void kernel_main() {
table_length = sizeof(FR) / sizeof(ScancodeTable);
RenderText("myOS", 0x0F);
sleep(2000);
clear_screen();
while (1) {
if (isPressed) {
isPressed = 0;
const char* LastChar = getKey(FR, scancode);
if (LastChar != NULL) {
if (strcmp(LastChar, "BS") == 0) {
if (CommandLength > 0) {
CommandLength--;
CurrentCommand[CommandLength] = '\0';
}
} else if (strcmp(LastChar, "Enter") == 0) {
RunCommand(CurrentCommand, CommandLength);
} else {
if (CommandLength < MAX_COMMAND_LENGTH - 1) {
CurrentCommand[CommandLength] = LastChar[0];
CommandLength++;
CurrentCommand[CommandLength] = '\0';
}
}
}
}
}
}
void _start(void) {
kernel_main();
}
linker.ld
:
ENTRY(_start)
SECTIONS
{
. = 0x1000;
.text : {
*(.text)
}
.data : {
*(.data)
}
.bss : {
*(.bss COMMON)
}
}
I use these commands for compilation:
nasm -f bin -o bootloader.bin bootloader.asm
i686-elf-gcc -ffreestanding -m32 -c kernel.c -o kernel.o
i686-elf-ld -T linker.ld -o kernel.bin -m elf_i386 kernel.o
dd if=bootloader.bin of=floppy.img bs=512 seek=0
dd if=kernel.bin of=floppy.img bs=512 seek=4
qemu-system-i386 -drive format=raw,file=floppy.img
I expect it to run without problems, but it gets stuck in a loop where a blinking Booting from Hard Drive...
is displayed.