OasisOS

driver

keyboard (ps/2)

file: src/kernel/drivers/keyboard.c

inisialisasi

void keyboard_init(void) {
 (void)inb(KEYBOARD_DATA); // buang byte pending
 ctrl_pressed = 0;
 extended_key = 0;
}

handler interrupt (irq1)

void keyboard_interrupt_handler(void) {
 uint8_t scancode = inb(KEYBOARD_DATA); // baca dari port 0x60

 if (scancode == 0xE0) { extended_key = 1; return; }
 if (extended_key) {
 // arrow keys, delete, home, end, pgup, pgdn
 extended_key = 0;
 if (scancode & 0x80) return; // release
 uint8_t key = ...; // konversi
 keyboard_buffer[write_pos] = key;
 write_pos = (write_pos + 1) % KEYBOARD_BUFFER_SIZE;
 return;
 }

 // shift
 if (scancode == 0x2A) { shift_pressed = 1; return; }
 if (scancode == 0xAA) { shift_pressed = 0; return; }
 // ctrl
 if (scancode == 0x1D) { ctrl_pressed = 1; return; }
 if (scancode == 0x9D) { ctrl_pressed = 0; return; }

 if (scancode & 0x80) return; // tombol release, cuekin

 const char *map = shift_pressed ? keymap_shift : keymap_us;
 char c = map[scancode];
 if (c != 0) {
 keyboard_buffer[write_pos] = c;
 write_pos = (write_pos + 1) % KEYBOARD_BUFFER_SIZE;
 }
}

circular buffer

static uint8_t keyboard_buffer[KEYBOARD_BUFFER_SIZE]; // 
static volatile int read_pos = 0;
static volatile int write_pos = 0;

keyboard_getchar

char keyboard_getchar(void) {
 while (read_pos == write_pos) {
 asm volatile("sti; hlt"); // tunggu interrupt
 }
 uint8_t key = keyboard_buffer[read_pos];
 read_pos = (read_pos + 1) % KEYBOARD_BUFFER_SIZE;
 return (char)key;
}

sti; hlt adalah atomik di x86: interrupt di-enable saat cpu hlt, jadi kalau ada keyboard irq, cpu akan bangun.

keymap

static const char keymap_us[128] = {
 0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
 '\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', 0,
 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0, '\\',
 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*', 0, ' ',
};

timer (pit)

file: src/kernel/drivers/timer.c

inisialisasi

void timer_init(uint32_t frequency) {
 uint32_t divisor = PIT_FREQUENCY / frequency; // 1193182 / 100 = 11931

 outb(PIT_CONTROL, 0x36); // channel 0, mode 2 (rate generator)
 outb(PIT_CHANNEL_0, divisor & 0xFF); // low byte
 outb(PIT_CHANNEL_0, (divisor >> 8) & 0xFF); // high byte

 pic_enable_irq(0); // enable timer irq di pic
}

handler

void timer_interrupt_handler(void) {
 ticks++;
 task_switch();
}

100hz -> tiap 10ms trigger sekali. ticks counter bisa dipakai untuk uptime.

timer_get_ticks / timer_sleep

uint32_t timer_get_ticks(void) { return ticks; }

void timer_sleep(uint32_t ms) {
 uint32_t target = ticks + (ms / 10);
 while (ticks < target);
}

vga text mode

file: src/kernel/core/vga.c

memory

#define VGA_MEMORY 0xB8000
static uint16_t* vga_buffer = (uint16_t*)VGA_MEMORY;

tiap karakter : char (low) + attribute (high). attribute: 4-bit foreground + 4-bit background.

fungsi

void vga_putc(char c); // tulis karakter, handle \n, \b, scroll
void vga_print(const char* s); // print string
void vga_clear(void); // bersihin layar
void vga_set_color(uint8_t fg, uint8_t bg);
void vga_set_cursor(uint8_t x, uint8_t y);
void vga_write_char(uint8_t x, uint8_t y, char c, uint8_t color);
void vga_fill_rect(uint8_t x, uint8_t y, uint8_t w, uint8_t h, char c, uint8_t color);

vga_putc

void vga_putc(char c) {
 if (c == '\n') { cursor_x = 0; cursor_y++; if (cursor_y >= 25) scroll(); return; }
 if (c == '\b') { if (cursor_x > 0) cursor_x--; buffer[...] = ' '; return; }

 vga_buffer[cursor_y * 80 + cursor_x] = c | (color << 8);
 cursor_x++;
 if (cursor_x >= 80) { cursor_x = 0; cursor_y++; if (cursor_y >= 25) scroll(); }
}

scroll

geser layar ke atas banyak baris:

for (y = 1; y < 25; y++)
 for (x = 0; x < 80; x++)
 buffer[(y-1)*80 + x] = buffer[y*80 + x];
// clear baris terakhir
for (x = 0; x < 80; x++)
 buffer[24*80 + x] = ' ' | (color << 8);

hardware cursor

program cursor position via crtc registers:

uint16_t pos = y * 80 + x;
outb(VGA_CTRL_REG, 0x0F); outb(VGA_DATA_REG, pos & 0xFF);
outb(VGA_CTRL_REG, 0x0E); outb(VGA_DATA_REG, (pos >> 8) & 0xFF);

ata (ide) – pio mode

file: src/kernel/drivers/ata.c

port

0x1F0: data port (16-bit)
0x1F2: sector count
0x1F3: lba low
0x1F4: lba mid
0x1F5: lba high
0x1F6: drive/head (0xE0 = master, 0xF0 = slave)
0x1F7: command/status

read sector (pio, lba28)

ata_read_sector(uint32_t lba, void *buffer) {
 // pilih drive
 outb(0x1F6, 0xE0 | ((lba >> 24) & 0x0F));
 // set lba
 outb(0x1F2, 1); // sector count
 outb(0x1F3, lba & 0xFF);
 outb(0x1F4, (lba >> 8) & 0xFF);
 outb(0x1F5, (lba >> 16) & 0xFF);
 // command
 outb(0x1F7, 0x20); // read sectors with retry

 // polling status
 while (inb(0x1F7) & 0x80); // wait not busy
 while (!(inb(0x1F7) & 0x08)); // wait drq

 // read data (256 word = )
 for (int i = 0; i < 256; i++)
 ((uint16_t*)buffer)[i] = inw(0x1F0);
}

block cache

file: src/kernel/drivers/block.c

entry cache untuk mengurangi akses disk langsung.

typedef struct {
 uint32_t block_num;
 uint8_t data[BLOCK_SIZE]; // 
 int valid;
 int dirty;
} cache_entry_t;

static cache_entry_t cache[64];

block_read

int block_read(uint32_t abs_block, uint8_t *buffer) {
 // cek cache
 for (int i = 0; i < 64; i++) {
 if (cache[i].valid && cache[i].block_num == abs_block) {
 memcpy(buffer, cache[i].data, BLOCK_SIZE);
 return 0;
 }
 }
 // cache miss -> baca dari disk via ata
 ata_read_sector(abs_block, buffer);
 // simpen ke cache (evict kalo penuh)
 int slot = find_free_cache();
 cache[slot].block_num = abs_block;
 memcpy(cache[slot].data, buffer, BLOCK_SIZE);
 cache[slot].valid = 1;
 cache[slot].dirty = 0;
 return 0;
}

block_write

int block_write(uint32_t abs_block, const uint8_t *buffer) {
 // cari slot cache
 int slot = find_free_cache();
 cache[slot].block_num = abs_block;
 memcpy(cache[slot].data, buffer, BLOCK_SIZE);
 cache[slot].valid = 1;
 cache[slot].dirty = 1; // marked dirty, bakal di-flush nanti
 // langsung tulis ke disk juga
 ata_write_sector(abs_block, buffer);
 cache[slot].dirty = 0;
 return 0;
}

block_flush

tulis semua dirty cache ke disk:

for (int i = 0; i < 64; i++)
 if (cache[i].valid && cache[i].dirty)
 ata_write_sector(cache[i].block_num, cache[i].data);

pic

file: src/kernel/drivers/pic.c

init

remap irq 0-7 ke interrupt 32-39, irq 8-15 ke 40-47:

outb(PIC_MASTER_CMD, ICW1_INIT | ICW1_ICW4); // 0x11
outb(PIC_SLAVE_CMD, ICW1_INIT | ICW1_ICW4);
outb(PIC_MASTER_DATA, 32); // icw2: master base 32
outb(PIC_SLAVE_DATA, 40); // icw2: slave base 40
outb(PIC_MASTER_DATA, 0x04); // icw3: slave on irq2
outb(PIC_SLAVE_DATA, 0x02); // icw3: slave id 2
outb(PIC_MASTER_DATA, ICW4_8086); // 0x01
outb(PIC_SLAVE_DATA, ICW4_8086);
outb(PIC_MASTER_DATA, 0xFF); // mask semua irq dulu
outb(PIC_SLAVE_DATA, 0xFF);

enable/disable irq

void pic_enable_irq(int irq) {
 uint16_t port = (irq < 8) ? PIC_MASTER_DATA : PIC_SLAVE_DATA;
 if (irq >= 8) irq -= 8;
 uint8_t mask = inb(port);
 mask &= ~(1 << irq); // clear bit = enable
 outb(port, mask);
}

io ports

file: src/kernel/drivers/io.c

void outb(uint16_t port, uint8_t val) {
 asm volatile("outb %0, %1" : : "a"(val), "Nd"(port));
}
uint8_t inb(uint16_t port) {
 uint8_t ret;
 asm volatile("inb %1, %0" : "=a"(ret) : "Nd"(port));
 return ret;
}
On this page