├── Makefile ├── include ├── kernel.h └── string.h └── src ├── boot └── bootloader.asm └── kernel ├── entry.asm ├── kernel.c └── string.c /Makefile: -------------------------------------------------------------------------------- 1 | all: disk.iso 2 | 3 | C_SOURCES = $(wildcard src/kernel/*.c) 4 | C_OBJECTS = ${C_SOURCES:.c=.o} 5 | 6 | CFLAGS = -m32 -ffreestanding -nostdlib -Iinclude -std=c11 \ 7 | -Wall \ 8 | -Wextra \ 9 | -pedantic \ 10 | -Wcast-align \ 11 | -Wcast-qual \ 12 | -Wdisabled-optimization \ 13 | -Wformat=2 \ 14 | -Winit-self \ 15 | -Wjump-misses-init \ 16 | -Wlogical-op \ 17 | -Wmissing-declarations \ 18 | -Wmissing-include-dirs \ 19 | -Wredundant-decls \ 20 | -Wshadow \ 21 | -Wsign-conversion \ 22 | -Wstrict-overflow=5 \ 23 | -Wswitch-default \ 24 | -Wundef \ 25 | -fno-pie \ 26 | -fno-stack-protector 27 | 28 | disk.iso: bootloader.bin kernel.bin 29 | cat $^ > bootsector.bin.tmp 30 | dd if=/dev/zero of=$@ bs=512 count=2 31 | dd if=bootsector.bin.tmp of=$@ conv=notrunc 32 | rm bootsector.bin.tmp 33 | 34 | kernel.bin: entry.o ${C_OBJECTS} 35 | ld -Ttext 0x1000 --oformat binary -m elf_i386 -o $@ $^ 36 | 37 | bootloader.bin: src/boot/bootloader.asm 38 | nasm $^ -f bin -o $@ 39 | 40 | entry.o: src/kernel/entry.asm 41 | nasm $^ -f elf -o $@ 42 | 43 | %.o: %.c 44 | gcc -c $^ -o $@ $(CFLAGS) 45 | 46 | clean: 47 | rm *.bin *.o src/kernel/*.o 48 | -------------------------------------------------------------------------------- /include/kernel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | static const char scancode2ascii[256] = { 6 | [0x1C] = 'A', 7 | [0x32] = 'B', 8 | [0x21] = 'C', 9 | [0x23] = 'D', 10 | [0x24] = 'E', 11 | [0x2B] = 'F', 12 | [0x34] = 'G', 13 | [0x33] = 'H', 14 | [0x43] = 'I', 15 | [0x3B] = 'J', 16 | [0x42] = 'K', 17 | [0x4B] = 'L', 18 | [0x3A] = 'M', 19 | [0x31] = 'N', 20 | [0x44] = 'O', 21 | [0x4D] = 'P', 22 | [0x15] = 'Q', 23 | [0x2D] = 'R', 24 | [0x1B] = 'S', 25 | [0x2C] = 'T', 26 | [0x3C] = 'U', 27 | [0x2A] = 'V', 28 | [0x1D] = 'W', 29 | [0x22] = 'X', 30 | [0x35] = 'Y', 31 | [0x1A] = 'Z', 32 | [0x5A] = '\n', 33 | }; 34 | 35 | struct { 36 | unsigned char x; 37 | unsigned char y; 38 | } cursor = {0, 0}; 39 | 40 | #define MAX_COMMANDS 100 41 | struct Command { 42 | char *command; 43 | char *description; 44 | int (*function)(); 45 | } commands[MAX_COMMANDS]; 46 | uint8_t commands_no; 47 | #define ADD_COMMAND(cmd, desc, func) \ 48 | commands[commands_no].command = cmd; \ 49 | commands[commands_no].description = desc; \ 50 | commands[commands_no].function = func; \ 51 | ++commands_no 52 | 53 | struct Command *findCommand(char *command); 54 | 55 | #define GETCHAR_WAITING -1337 56 | #define GETCHAR_NOTWAITING -1234 57 | int getchar_char = GETCHAR_NOTWAITING; 58 | int kernel_getchar(); 59 | 60 | void printCharAt(char chr, uint8_t color, uint8_t x, uint8_t y); 61 | void printChar(char chr); 62 | void printString(char *str); 63 | void printHex(unsigned int hex); 64 | void handleAsciiCode(char asciicode); 65 | void handleScanCode(unsigned char scancode); 66 | -------------------------------------------------------------------------------- /include/string.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | size_t strlen(char *str); 6 | int strcmp(const char *s1, const char *s2); 7 | 8 | void *memcpy(void * restrict dest, const void * restrict src, size_t n); 9 | 10 | void *memmove(void *dest, const void *src, size_t n); 11 | -------------------------------------------------------------------------------- /src/boot/bootloader.asm: -------------------------------------------------------------------------------- 1 | ; BIOS looks for the magic number 0x55 0xAA at the end of the 1st sector (which is 512 bytes big) of each drive, 2 | ; and if found, loads that sector into address 0x7c00 3 | ORG 0x7c00 ; Telling the assembler that we know this code will get loaded into address 0x7c00 4 | ; Generate 16 bit code please 5 | BITS 16 6 | ; CPU instruction set is 386 7 | CPU 386 8 | 9 | ; Define constant of the RAM location that we're gonna load the kernel into. 10 | KERNEL_OFFSET equ 0x1000 11 | 12 | ; The only thing guaranteed at this point is that BIOS gives us, in the DL register, 13 | ; the "drive number" that the current code was found on, 14 | ; so that we can ask BIOS to load more data from the same drive later. 15 | mov [BOOT_DRIVE], dl 16 | 17 | ;;; Clear screen 18 | ;;; BIOS 10h interrupt (Set Video Mode) 19 | ;;; AH = 00 20 | ;;; AL = 02 --> 80x25 16 shades of grey text 21 | mov ax, 2 22 | int 0x10 23 | 24 | mov si, STR_HELLO 25 | call print_string 26 | 27 | call load_kernel 28 | 29 | ; Go into protected mode 30 | cli 31 | lgdt [gdt_descriptor] 32 | mov eax, cr0 33 | or eax, 0x1 34 | mov cr0, eax 35 | jmp CODE_SEG:init_protected_mode 36 | 37 | load_kernel: 38 | mov si, STR_BOOTING 39 | call print_string 40 | 41 | ; Let's load the kernel stuff into RAM 42 | mov bx, KERNEL_OFFSET ; Data read from drive goes into address pointed to by ES:BX 43 | mov dh, 10 ; How many sectors we want to read 44 | mov dl, [BOOT_DRIVE] ; Which drive to read from 45 | call disk_load 46 | 47 | ret 48 | 49 | disk_load: 50 | push dx 51 | 52 | ;;; BIOS 13h interrupt (Disk Services) 53 | ;;; AH = 2 (Read disk sectors) 54 | ;;; AL = number of sectors to read 55 | ;;; CH = track/cylinder number 56 | ;;; CL = sector number 57 | ;;; DH = head number 58 | ;;; DL = drive number 59 | ;;; ES:[BX] = where to store data 60 | ;;; returning: 61 | ;;; AH = status code 62 | ;;; AL = number of sectors read 63 | ;;; CF = 0 on success, 1 on error 64 | mov ah, 0x02 65 | mov al, dh 66 | mov ch, 0x00 67 | mov cl, 0x02 68 | mov dh, 0x00 69 | int 0x13 70 | 71 | jc .error ; carry flag set on error 72 | 73 | pop dx 74 | cmp dh, al ; AL gets number of sectors actually read 75 | jne .error_sectors_not_same 76 | ret 77 | 78 | .error: 79 | mov si, STR_DISK_ERROR 80 | call print_string 81 | mov si, ax ; AH contains the status code 82 | call print_hex 83 | jmp $ 84 | 85 | .error_sectors_not_same: 86 | mov si, STR_DISK_ERROR_SECTORS_NOT_SAME 87 | call print_string 88 | jmp $ 89 | 90 | print_string: 91 | push ax 92 | 93 | .loop: 94 | cmp [si], byte 0 95 | je .end 96 | mov ah, 0x0e 97 | mov al, [si] 98 | int 0x10 99 | inc si 100 | jmp .loop 101 | 102 | .end: 103 | pop ax 104 | ret 105 | 106 | print_hex: 107 | ; Assuming the number we want to print is in SI 108 | ; We're going to loop 4 times and process 4 bits each iteration. 109 | ; By right shifting and ANDing we get the 4 bit integer, which 110 | ; we can do simple arithmetics on to get the proper ASCII value. 111 | xor dx, dx ; Loop counter 112 | .loop: 113 | mov ax, si 114 | mov cx, dx 115 | shl cx, 2 ; Multiply by 4 (we want to shift multiples of 4 bits) 116 | shr ax, cl ; Shift the number so the 4 bits are least significant 117 | and ax, 0xf 118 | cmp al, 9 119 | jle .loopend 120 | add al, 7 ; There's an offset between numbers and chars in ASCII 121 | .loopend: 122 | add al, 48 ; ASCII 0 is at 48 123 | mov bx, 3 124 | sub bx, dx ; Calculate the offset into the string 125 | ; (0th number (from right) is 3 into string) 126 | mov [STR_HEX + bx + 2], al ; Put the ASCII char into the string 127 | inc dx 128 | cmp dx, 3 129 | jle .loop 130 | .print: 131 | 132 | mov si, STR_HEX 133 | call print_string 134 | 135 | ret 136 | 137 | ; 32 bit protected mode! 138 | BITS 32 139 | init_protected_mode: 140 | mov ax, DATA_SEG 141 | mov ds, ax 142 | mov ss, ax 143 | mov es, ax 144 | mov fs, ax 145 | mov gs, ax 146 | 147 | mov ebp, 0x90000 148 | mov esp, ebp 149 | 150 | jmp start_protected_mode 151 | 152 | start_protected_mode: 153 | mov si, STR_HELLO_PROTECTED 154 | call print_string_protected 155 | 156 | call KERNEL_OFFSET 157 | 158 | jmp $ 159 | 160 | print_string_protected: 161 | pusha 162 | 163 | mov edx, 0xb8000 164 | .loop: 165 | cmp [si], byte 0 166 | je .end 167 | mov ah, 0x0f 168 | mov al, [si] 169 | mov [edx], ax 170 | inc si 171 | add edx, 2 172 | jmp .loop 173 | 174 | .end: 175 | popa 176 | ret 177 | 178 | ; Data 179 | 180 | ; Global Descriptor Table 181 | gdt_start: 182 | gdt_null: 183 | dd 0x0 184 | dd 0x0 185 | gdt_code: 186 | dw 0xffff 187 | dw 0x0 188 | db 0x0 189 | db 10011010b 190 | db 11001111b 191 | db 0x0 192 | gdt_data: 193 | dw 0xffff 194 | dw 0x0 195 | db 0x0 196 | db 10010010b 197 | db 11001111b 198 | db 0x0 199 | gdt_end: 200 | gdt_descriptor: 201 | dw gdt_end - gdt_start - 1 202 | dd gdt_start 203 | 204 | CODE_SEG equ gdt_code - gdt_start 205 | DATA_SEG equ gdt_data - gdt_start 206 | 207 | BOOT_DRIVE: db 0 208 | 209 | STR_HELLO: 210 | db 'Hello World!', 10,13,0 211 | STR_HELLO_PROTECTED: 212 | db "Hello World from Protected Mode!", 0 213 | STR_BOOTING: 214 | db 'Booting Operating System...', 10,13,0 215 | STR_HEX: 216 | db '0x0000', 13,10,0 217 | STR_DISK_ERROR: 218 | db "Could not read from disk!", 13,10,0 219 | STR_DISK_ERROR_SECTORS_NOT_SAME: 220 | db "Could not read the specified amount of sectors from disk!", 13,10,0 221 | 222 | times 510-($-$$) db 0 223 | dw 0xaa55 224 | 225 | -------------------------------------------------------------------------------- /src/kernel/entry.asm: -------------------------------------------------------------------------------- 1 | BITS 32 2 | CPU 386 3 | 4 | VIDEOMEM equ 0xb8000 5 | 6 | EXTERN main 7 | EXTERN printString 8 | EXTERN printHex 9 | EXTERN handleScanCode 10 | 11 | cli 12 | 13 | ; Set up the PIC to send IRQs to the right offset 14 | mov al, 0x11 15 | out 0x20, al 16 | out 0xA0, al 17 | 18 | mov al, 0x70 ; IRQ 0-7 goes on this offset 19 | out 0x21, al 20 | mov al, 0x78 ; and IRQ 8-15 goes here 21 | out 0xA1, al 22 | 23 | mov al, 0x04 24 | out 0x21, al 25 | mov al, 0x02 26 | out 0xA1, al 27 | 28 | mov al, 0x01 29 | out 0x21, al 30 | out 0xA1, al 31 | 32 | ; Load interrupt table! 33 | lidt [idt] 34 | sti 35 | 36 | ; Set up keyboard 37 | setupkeyboard: 38 | ; Disable 39 | mov al, 0xAD 40 | out 64h, al 41 | mov al, 0xA7 42 | out 64h, al 43 | ; Flush buffer 44 | .wait1: 45 | in al, 64h 46 | bt ax, 0 47 | jnc .endwait1 48 | in al, 60h 49 | jmp .wait1 50 | .endwait1: 51 | ; Config byte 52 | mov al, 0x20 53 | out 64h, al 54 | .wait3: 55 | in al, 64h 56 | bt ax, 0 57 | jnc .wait3 58 | in al, 60h 59 | mov bl, al 60 | and bl, 00111100b 61 | mov al, 0x60 62 | out 64h, al 63 | .wait4: 64 | in al, 64h 65 | bt ax, 1 66 | jc .wait4 67 | mov al, bl 68 | out 60h, al 69 | ; Self test 70 | mov al, 0xAA 71 | out 64h, al 72 | .wait5: 73 | in al, 64h 74 | bt ax, 0 75 | jnc .wait5 76 | in al, 60h 77 | cmp al, 0x55 78 | je .cont1 79 | push STR_PS2FAIL 80 | call printString 81 | jmp $ 82 | .cont1: 83 | ; Enable 84 | mov al, 0xAE 85 | out 64h, al 86 | mov al, 0xA8 87 | out 64h, al 88 | 89 | ; Set scan code set 90 | mov al, 0xF0 91 | out 60h, al 92 | ; Read ACK byte 93 | .wait8: 94 | in al, 64h 95 | bt ax, 0 96 | jnc .wait8 97 | in al, 60h ; al == 0xFA on success 98 | ; We'd like scan code set 2 99 | mov al, 2 100 | out 60h, al 101 | ; Read next ACK byte 102 | .wait9: 103 | in al, 64h 104 | bt ax, 0 105 | jnc .wait9 106 | in al, 60h ; al == 0xFA on success 107 | 108 | ; Enable interrupt 109 | mov al, 0x20 110 | out 64h, al 111 | .wait6: 112 | in al, 64h 113 | bt ax, 0 114 | jnc .wait6 115 | in al, 60h 116 | mov bl, al 117 | or bl, 11b 118 | mov al, 0x60 119 | out 64h, al 120 | .wait7: 121 | in al, 64h 122 | bt ax, 1 123 | jc .wait7 124 | mov al, bl 125 | out 60h, al 126 | 127 | call main 128 | 129 | push STR_GOODBYE 130 | call printString 131 | 132 | jmp $ 133 | 134 | print_char_at: 135 | ; Character in DI, x pos in EAX, y pos in EBX 136 | 137 | ; = 2 * (80*y + x) 138 | imul ebx, ebx, 80 139 | add ebx, eax 140 | add ebx, ebx 141 | 142 | mov edx, ebx 143 | inc edx 144 | mov [VIDEOMEM + ebx], di 145 | mov [VIDEOMEM + edx], byte 0x07 146 | 147 | ret 148 | 149 | print_char: 150 | ; Character in DI 151 | movzx ax, byte [CURSOR] 152 | movzx bx, byte [CURSOR+1] 153 | call print_char_at 154 | inc ax 155 | mov byte [CURSOR], al 156 | ret 157 | 158 | print_string: 159 | ; String in SI 160 | .loop: 161 | cmp [si], byte 0 162 | je .end 163 | mov di, [si] 164 | call print_char 165 | inc si 166 | jmp .loop 167 | 168 | .end: 169 | ret 170 | 171 | handle_keyboard: 172 | pushad 173 | mov ebp, esp 174 | 175 | xor eax,eax 176 | in al, 60h 177 | push eax 178 | call handleScanCode 179 | pop eax 180 | 181 | mov al, 20h 182 | out 20h, al 183 | 184 | popad 185 | iret 186 | 187 | exception0: 188 | pushad 189 | mov ebp, esp 190 | sub esp, 8 191 | push 0x0 192 | jmp exception_common 193 | exception1: 194 | pushad 195 | mov ebp, esp 196 | sub esp, 8 197 | push 0x1 198 | jmp exception_common 199 | exception2: 200 | pushad 201 | mov ebp, esp 202 | sub esp, 8 203 | push 0x2 204 | jmp exception_common 205 | exception3: 206 | pushad 207 | mov ebp, esp 208 | sub esp, 8 209 | push 0x3 210 | jmp exception_common 211 | exception4: 212 | pushad 213 | mov ebp, esp 214 | sub esp, 8 215 | push 0x4 216 | jmp exception_common 217 | exception5: 218 | pushad 219 | mov ebp, esp 220 | sub esp, 8 221 | push 0x5 222 | jmp exception_common 223 | exception6: 224 | pushad 225 | mov ebp, esp 226 | sub esp, 8 227 | push 0x6 228 | jmp exception_common 229 | exception7: 230 | pushad 231 | mov ebp, esp 232 | sub esp, 8 233 | push 0x7 234 | jmp exception_common 235 | int_doublefault: 236 | pushad 237 | mov ebp,esp 238 | sub esp, 8 239 | push STR_DOUBLEFAULT 240 | call printString 241 | mov esp,ebp 242 | mov al, 20h 243 | out 20h, al 244 | popad 245 | iret 246 | exception9: 247 | pushad 248 | mov ebp, esp 249 | sub esp, 8 250 | push 0x9 251 | jmp exception_common 252 | exceptionA: 253 | pushad 254 | mov ebp, esp 255 | sub esp, 8 256 | push 0xA 257 | jmp exception_common 258 | exceptionB: 259 | pushad 260 | mov ebp, esp 261 | sub esp, 8 262 | push 0xB 263 | jmp exception_common 264 | exceptionC: 265 | pushad 266 | mov ebp, esp 267 | sub esp, 8 268 | push 0xC 269 | jmp exception_common 270 | exceptionD: 271 | pushad 272 | mov ebp, esp 273 | sub esp, 8 274 | push 0xD 275 | jmp exception_common 276 | exceptionE: 277 | pushad 278 | mov ebp, esp 279 | sub esp, 8 280 | push 0xE 281 | jmp exception_common 282 | exceptionF: 283 | pushad 284 | mov ebp, esp 285 | sub esp, 8 286 | push 0xF 287 | jmp exception_common 288 | exception10: 289 | pushad 290 | mov ebp, esp 291 | sub esp, 8 292 | push 0x10 293 | jmp exception_common 294 | exception11: 295 | pushad 296 | mov ebp, esp 297 | sub esp, 8 298 | push 0x11 299 | jmp exception_common 300 | exception12: 301 | pushad 302 | mov ebp, esp 303 | sub esp, 8 304 | push 0x12 305 | jmp exception_common 306 | exception13: 307 | pushad 308 | mov ebp, esp 309 | sub esp, 8 310 | push 0x13 311 | jmp exception_common 312 | exception_common: 313 | call printHex 314 | push STR_EXCEPTION 315 | call printString 316 | mov esp, ebp 317 | mov al, 20h 318 | out 20h, al 319 | popad 320 | iret 321 | 322 | irq0: 323 | pushad 324 | mov al, 20h 325 | out 20h, al 326 | popad 327 | iret 328 | irq1: 329 | pushad 330 | mov ebp, esp 331 | sub esp, 8 332 | push 1 333 | jmp irq_common 334 | irq2: 335 | pushad 336 | mov ebp, esp 337 | sub esp, 8 338 | push 2 339 | jmp irq_common 340 | irq3: 341 | pushad 342 | mov ebp, esp 343 | sub esp, 8 344 | push 3 345 | jmp irq_common 346 | irq4: 347 | pushad 348 | mov ebp, esp 349 | sub esp, 8 350 | push 4 351 | jmp irq_common 352 | irq5: 353 | pushad 354 | mov ebp, esp 355 | sub esp, 8 356 | push 5 357 | jmp irq_common 358 | irq6: 359 | pushad 360 | mov ebp, esp 361 | sub esp, 8 362 | push 6 363 | jmp irq_common 364 | irq7: 365 | pushad 366 | mov ebp, esp 367 | sub esp, 8 368 | push 7 369 | jmp irq_common 370 | irq8: 371 | pushad 372 | mov ebp, esp 373 | sub esp, 8 374 | push 8 375 | jmp irq_common 376 | irq9: 377 | pushad 378 | mov ebp, esp 379 | sub esp, 8 380 | push 9 381 | jmp irq_common 382 | irq10: 383 | pushad 384 | mov ebp, esp 385 | sub esp, 8 386 | push 10 387 | jmp irq_common 388 | irq11: 389 | pushad 390 | mov ebp, esp 391 | sub esp, 8 392 | push 11 393 | jmp irq_common 394 | irq12: 395 | pushad 396 | mov ebp, esp 397 | sub esp, 8 398 | push 12 399 | jmp irq_common 400 | irq13: 401 | pushad 402 | mov ebp, esp 403 | sub esp, 8 404 | push 13 405 | jmp irq_common 406 | irq14: 407 | pushad 408 | mov ebp, esp 409 | sub esp, 8 410 | push 14 411 | jmp irq_common 412 | irq15: 413 | pushad 414 | mov ebp, esp 415 | sub esp, 8 416 | push 15 417 | jmp irq_common 418 | irq_common: 419 | call printHex 420 | push STR_IRQ 421 | call printString 422 | mov esp, ebp 423 | mov al, 20h 424 | out 20h, al 425 | popad 426 | iret 427 | 428 | ; Interrupt Descriptor Table 429 | idt_start: 430 | dw exception0 431 | dw 0x8 432 | db 0 433 | db 10001110b 434 | dw 0 435 | 436 | dw exception1 437 | dw 0x8 438 | db 0 439 | db 10001110b 440 | dw 0 441 | 442 | dw exception2 443 | dw 0x8 444 | db 0 445 | db 10001110b 446 | dw 0 447 | 448 | dw exception3 449 | dw 0x8 450 | db 0 451 | db 10001110b 452 | dw 0 453 | 454 | dw exception4 455 | dw 0x8 456 | db 0 457 | db 10001110b 458 | dw 0 459 | 460 | dw exception5 461 | dw 0x8 462 | db 0 463 | db 10001110b 464 | dw 0 465 | 466 | dw exception6 467 | dw 0x8 468 | db 0 469 | db 10001110b 470 | dw 0 471 | 472 | dw exception7 473 | dw 0x8 474 | db 0 475 | db 10001110b 476 | dw 0 477 | 478 | dw int_doublefault 479 | dw 0x8 480 | db 0 481 | db 10001110b 482 | dw 0 483 | 484 | dw exception9 485 | dw 0x8 486 | db 0 487 | db 10001110b 488 | dw 0 489 | 490 | dw exceptionA 491 | dw 0x8 492 | db 0 493 | db 10001110b 494 | dw 0 495 | 496 | dw exceptionB 497 | dw 0x8 498 | db 0 499 | db 10001110b 500 | dw 0 501 | 502 | dw exceptionC 503 | dw 0x8 504 | db 0 505 | db 10001110b 506 | dw 0 507 | 508 | dw exceptionD 509 | dw 0x8 510 | db 0 511 | db 10001110b 512 | dw 0 513 | 514 | dw exceptionE 515 | dw 0x8 516 | db 0 517 | db 10001110b 518 | dw 0 519 | 520 | dw exceptionF 521 | dw 0x8 522 | db 0 523 | db 10001110b 524 | dw 0 525 | 526 | dw exception10 527 | dw 0x8 528 | db 0 529 | db 10001110b 530 | dw 0 531 | 532 | dw exception11 533 | dw 0x8 534 | db 0 535 | db 10001110b 536 | dw 0 537 | 538 | dw exception12 539 | dw 0x8 540 | db 0 541 | db 10001110b 542 | dw 0 543 | 544 | dw exception13 545 | dw 0x8 546 | db 0 547 | db 10001110b 548 | dw 0 549 | 550 | ; Skip interrupts we don't care about for now 551 | times 8*(6Fh-13h) db 0x00 552 | 553 | dw irq0 554 | dw 0x8 555 | db 0 556 | db 10001110b 557 | dw 0 558 | 559 | ; 71h = IRQ1 = keyboard 560 | dw handle_keyboard 561 | dw 0x8 562 | db 0 563 | db 10001110b 564 | dw 0 565 | 566 | dw irq2 567 | dw 0x8 568 | db 0 569 | db 10001110b 570 | dw 0 571 | 572 | dw irq3 573 | dw 0x8 574 | db 0 575 | db 10001110b 576 | dw 0 577 | 578 | dw irq4 579 | dw 0x8 580 | db 0 581 | db 10001110b 582 | dw 0 583 | 584 | dw irq5 585 | dw 0x8 586 | db 0 587 | db 10001110b 588 | dw 0 589 | 590 | dw irq6 591 | dw 0x8 592 | db 0 593 | db 10001110b 594 | dw 0 595 | 596 | dw irq7 597 | dw 0x8 598 | db 0 599 | db 10001110b 600 | dw 0 601 | 602 | dw irq8 603 | dw 0x8 604 | db 0 605 | db 10001110b 606 | dw 0 607 | 608 | dw irq9 609 | dw 0x8 610 | db 0 611 | db 10001110b 612 | dw 0 613 | 614 | dw irq10 615 | dw 0x8 616 | db 0 617 | db 10001110b 618 | dw 0 619 | 620 | dw irq11 621 | dw 0x8 622 | db 0 623 | db 10001110b 624 | dw 0 625 | 626 | dw irq12 627 | dw 0x8 628 | db 0 629 | db 10001110b 630 | dw 0 631 | 632 | dw irq13 633 | dw 0x8 634 | db 0 635 | db 10001110b 636 | dw 0 637 | 638 | dw irq14 639 | dw 0x8 640 | db 0 641 | db 10001110b 642 | dw 0 643 | 644 | dw irq15 645 | dw 0x8 646 | db 0 647 | db 10001110b 648 | dw 0 649 | 650 | idt_end: 651 | idt: 652 | dw idt_end - idt_start 653 | dd idt_start 654 | 655 | CURSOR: dw 0 656 | STR_GOODBYE: db "Goodbye, shutting down!", 0 657 | STR_DOUBLEFAULT: db "DOUBLE FAULT!!!", 0 658 | STR_EXCEPTION: db "EXCEPTION", 0 659 | STR_IRQ: db "IRQ", 0 660 | STR_PS2FAIL: db "PS/2 SELF TEST FAILED!", 0 661 | -------------------------------------------------------------------------------- /src/kernel/kernel.c: -------------------------------------------------------------------------------- 1 | #include "kernel.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | void cmd_hello() 10 | { 11 | printString("HELLO WORLD!\n"); 12 | } 13 | 14 | void main() 15 | { 16 | ADD_COMMAND("HELLO", "Prints greeting.", cmd_hello); 17 | 18 | printString("Hello World from the booted C kernel!\n"); 19 | printString("And here's another line!\n"); 20 | 21 | printHex(0xDEADBEEF); 22 | printString("\n"); 23 | 24 | while (true) { 25 | #define CMD_BUF_LEN 256 26 | char buffer[CMD_BUF_LEN] = {0}; 27 | uint16_t buffer_len = 0; 28 | 29 | printString("$ "); 30 | 31 | for (uint16_t i = 0; i < CMD_BUF_LEN - 1; ++i) { 32 | int chr = kernel_getchar(); 33 | printChar(chr); 34 | 35 | if ('\n' == chr) { 36 | break; 37 | } 38 | 39 | buffer[buffer_len] = chr; 40 | ++buffer_len; 41 | } 42 | buffer[buffer_len] = 0; 43 | 44 | struct Command *command = findCommand(buffer); 45 | if (NULL != command) { 46 | command->function(); 47 | } else { 48 | printString("Unknown command \""); 49 | printString(buffer); 50 | printString("\"\n"); 51 | } 52 | } 53 | } 54 | 55 | struct Command *findCommand(char *command) 56 | { 57 | for (uint8_t i = 0; i < commands_no; ++i) { 58 | if (strcmp(command, commands[i].command) == 0) { 59 | return &commands[i]; 60 | } 61 | } 62 | 63 | return NULL; 64 | } 65 | 66 | int kernel_getchar() 67 | { 68 | getchar_char = GETCHAR_WAITING; 69 | while (GETCHAR_WAITING == getchar_char) { 70 | __asm__("hlt"); 71 | } 72 | 73 | int chr = getchar_char; 74 | getchar_char = GETCHAR_NOTWAITING; 75 | return chr; 76 | } 77 | 78 | void printCharAt(char chr, uint8_t color, uint8_t x, uint8_t y) 79 | { 80 | unsigned char *videomem_start = (unsigned char *) 0xb8000; 81 | uint16_t offset = 160*y + 2*x; 82 | 83 | videomem_start[offset] = (unsigned char) chr; 84 | videomem_start[offset + 1] = color; 85 | } 86 | 87 | void printChar(char chr) 88 | { 89 | switch (chr) { 90 | case '\n': 91 | ++cursor.y; 92 | cursor.x = 0; 93 | break; 94 | default: 95 | printCharAt(chr, 0x07, cursor.x, cursor.y); 96 | ++cursor.x; 97 | } 98 | } 99 | 100 | void printString(char *str) 101 | { 102 | unsigned char *videomem_start = (unsigned char *) 0xb8000; 103 | 104 | if (cursor.y >= 24) { 105 | memmove(videomem_start, videomem_start+160, 160*24); 106 | } 107 | 108 | size_t len = strlen(str); 109 | for (size_t i = 0; i < len; i++) { 110 | printChar(str[i]); 111 | } 112 | } 113 | 114 | void printHex(unsigned int hex) 115 | { 116 | static int iters = 0; 117 | char hex_str[] = "0x00000000"; 118 | 119 | for (int i = 9; i >= 2; --i) { 120 | unsigned char nibble = hex & 0xF; 121 | char hex_chr = '0' + nibble; 122 | if (nibble > 9) { 123 | hex_chr += 7; 124 | } 125 | hex_str[i] = hex_chr; 126 | hex /= 16; 127 | } 128 | printString(hex_str); 129 | 130 | ++iters; 131 | } 132 | 133 | void handleAsciiCode(char asciicode) 134 | { 135 | if (GETCHAR_WAITING == getchar_char) { 136 | getchar_char = asciicode; 137 | } 138 | } 139 | void handleScanCode(unsigned char scancode) 140 | { 141 | static bool is_break = false; 142 | if (0xF0 == scancode) { 143 | is_break = true; 144 | return; 145 | } 146 | if (0xE0 == scancode) { 147 | return; 148 | } 149 | 150 | if ( ! is_break) { 151 | char asciicode = scancode2ascii[scancode]; 152 | if (0 != asciicode) { 153 | handleAsciiCode(asciicode); 154 | } 155 | } 156 | is_break = false; 157 | } 158 | -------------------------------------------------------------------------------- /src/kernel/string.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | size_t strlen(char *str) 4 | { 5 | size_t len = 0; 6 | while (*str++) 7 | len++; 8 | return len; 9 | } 10 | 11 | int strcmp(const char *s1, const char *s2) 12 | { 13 | while (*s1 != 0 && *s1 == *s2) 14 | ++s1, ++s2; 15 | return *s1 - *s2; 16 | } 17 | 18 | void *memcpy(void * restrict dest, const void * restrict src, size_t n) 19 | { 20 | char * restrict dest_c = dest; 21 | const char * restrict src_c = src; 22 | 23 | for (size_t i = 0; i < n; ++i) { 24 | dest_c[i] = src_c[i]; 25 | } 26 | 27 | return dest; 28 | } 29 | 30 | void *memmove(void *dest, const void *src, size_t n) 31 | { 32 | // Possibly undefined behaviour 33 | if (src > dest) { 34 | return memcpy(dest, src, n); 35 | } else { 36 | char *dest_c = dest; 37 | const char *src_c = src; 38 | 39 | // We can't do >= 0 because unsigned 40 | for (size_t i = n; i > 0; --i) { 41 | dest_c[i-1] = src_c[i-1]; 42 | } 43 | 44 | return dest; 45 | } 46 | } 47 | --------------------------------------------------------------------------------