diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/OS_Project.iml b/.idea/OS_Project.iml
new file mode 100644
index 0000000..bc2cd87
--- /dev/null
+++ b/.idea/OS_Project.iml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/editor.xml b/.idea/editor.xml
new file mode 100644
index 0000000..25eca97
--- /dev/null
+++ b/.idea/editor.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..9071f6f
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Part3/09_memory/kernel/kernel.c b/Part3/09_memory/kernel/kernel.c
index 2c47aee..70309d9 100644
--- a/Part3/09_memory/kernel/kernel.c
+++ b/Part3/09_memory/kernel/kernel.c
@@ -153,3 +153,8 @@
}
return result;
}
+
+
+void print_holes(node* umem_head) {
+
+}
\ No newline at end of file
diff --git a/Part4/10_scheduler/Makefile b/Part4/10_scheduler/Makefile
new file mode 100644
index 0000000..504d70f
--- /dev/null
+++ b/Part4/10_scheduler/Makefile
@@ -0,0 +1,51 @@
+C_SOURCES = $(wildcard kernel/*.c drivers/*.c cpu/*.c libc/*.c)
+HEADERS = $(wildcard kernel/*.h drivers/*.h cpu/*.h libc/*.h)
+# Nice syntax for file extension replacement
+OBJ = ${C_SOURCES:.c=.o cpu/interrupt.o}
+
+# Change this if your cross-compiler is somewhere else
+CC = /usr/bin/gcc
+GDB = /usr/bin/gdb
+# -g: Use debugging symbols in gcc
+CFLAGS = -g -m32 -fno-pie -ffreestanding -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs -Wall -Wextra -Werror
+
+# First rule is run by default
+os-image.bin: boot/bootsect.bin kernel.bin
+ cat $^ > os-image.bin
+
+# '--oformat binary' deletes all symbols as a collateral, so we don't need
+# to 'strip' them manually on this case
+kernel.bin: boot/kernel_entry.o ${OBJ}
+ /usr/bin/ld -m elf_i386 -o $@ -Ttext 0x1000 $^ --oformat binary
+
+# Used for debugging purposes
+kernel.elf: boot/kernel_entry.o ${OBJ}
+ /usr/bin/ld -m elf_i386 -o $@ -Ttext 0x1000 $^
+
+run: os-image.bin
+ qemu-system-i386 -vga std -drive file=$<,index=0,if=floppy,format=raw
+
+curses: os-image.bin
+ qemu-system-i386 -curses -drive file=$<,index=0,if=floppy,format=raw
+
+# Open the connection to qemu and load our kernel-object file with symbols
+debug: os-image.bin kernel.elf
+ qemu-system-i386 -curses -s -drive file=os-image.bin,index=0,if=floppy,format=raw -d guest_errors,int
+
+gdb:
+ ${GDB} -ex "target remote localhost:1234" -ex "symbol-file kernel.elf"
+
+# Generic rules for wildcards
+# To make an object, always compile from its .c
+%.o: %.c ${HEADERS}
+ ${CC} ${CFLAGS} -c $< -o $@
+
+%.o: %.asm
+ nasm $< -f elf -o $@
+
+%.bin: %.asm
+ nasm $< -f bin -o $@
+
+clean:
+ rm -rf *.bin *.dis *.o *.s os-image.bin *.elf
+ rm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o cpu/*.o libc/*.o
diff --git a/Part4/10_scheduler/boot/32bit_print.asm b/Part4/10_scheduler/boot/32bit_print.asm
new file mode 100644
index 0000000..4169a17
--- /dev/null
+++ b/Part4/10_scheduler/boot/32bit_print.asm
@@ -0,0 +1,26 @@
+[bits 32] ; using 32-bit protected mode
+
+; this is how constants are defined
+VIDEO_MEMORY equ 0xb8000
+WHITE_OB_BLACK equ 0x0f ; the color byte for each character
+
+print_string_pm:
+ pusha
+ mov edx, VIDEO_MEMORY
+
+print_string_pm_loop:
+ mov al, [ebx] ; [ebx] is the address of our character
+ mov ah, WHITE_OB_BLACK
+
+ cmp al, 0 ; check if end of string
+ je print_string_pm_done
+
+ mov [edx], ax ; store character + attribute in video memory
+ add ebx, 1 ; next char
+ add edx, 2 ; next video memory position
+
+ jmp print_string_pm_loop
+
+print_string_pm_done:
+ popa
+ ret
diff --git a/Part4/10_scheduler/boot/32bit_print.bin_ b/Part4/10_scheduler/boot/32bit_print.bin_
new file mode 100644
index 0000000..674afb1
Binary files /dev/null and b/Part4/10_scheduler/boot/32bit_print.bin_ differ
diff --git a/Part4/10_scheduler/boot/bootsect.asm b/Part4/10_scheduler/boot/bootsect.asm
new file mode 100644
index 0000000..8df0ed1
--- /dev/null
+++ b/Part4/10_scheduler/boot/bootsect.asm
@@ -0,0 +1,51 @@
+; Identical to lesson 13's boot sector, but the %included files have new paths
+[org 0x7c00]
+KERNEL_OFFSET equ 0x1000 ; The same one we used when linking the kernel
+
+ mov [BOOT_DRIVE], dl ; Remember that the BIOS sets us the boot drive in 'dl' on boot
+ mov bp, 0x9000
+ mov sp, bp
+
+ mov bx, MSG_REAL_MODE
+ call print
+ call print_nl
+
+ call load_kernel ; read the kernel from disk
+ call switch_to_pm ; disable interrupts, load GDT, etc. Finally jumps to 'BEGIN_PM'
+ jmp $ ; Never executed
+
+%include "boot/print.asm"
+%include "boot/print_hex.asm"
+%include "boot/disk.asm"
+%include "boot/gdt.asm"
+%include "boot/32bit_print.asm"
+%include "boot/switch_pm.asm"
+
+[bits 16]
+load_kernel:
+ mov bx, MSG_LOAD_KERNEL
+ call print
+ call print_nl
+
+ mov bx, KERNEL_OFFSET ; Read from disk and store in 0x1000
+ mov dh, 48 ; Our future kernel will be larger, make this big (16 loads up to 12K kernel, make larger like 32)
+ mov dl, [BOOT_DRIVE]
+ call disk_load
+ ret
+
+[bits 32]
+BEGIN_PM:
+ mov ebx, MSG_PROT_MODE
+ call print_string_pm
+ call KERNEL_OFFSET ; Give control to the kernel
+ jmp $ ; Stay here when the kernel returns control to us (if ever)
+
+
+BOOT_DRIVE db 0 ; It is a good idea to store it in memory because 'dl' may get overwritten
+MSG_REAL_MODE db "Started in 16-bit Real Mode", 0
+MSG_PROT_MODE db "Landed in 32-bit Protected Mode", 0
+MSG_LOAD_KERNEL db "Loading kernel into memory", 0
+
+; padding
+times 510 - ($-$$) db 0
+dw 0xaa55
diff --git a/Part4/10_scheduler/boot/disk.asm b/Part4/10_scheduler/boot/disk.asm
new file mode 100644
index 0000000..be3d0a9
--- /dev/null
+++ b/Part4/10_scheduler/boot/disk.asm
@@ -0,0 +1,46 @@
+; load 'dh' sectors from drive 'dl' into ES:BX
+disk_load:
+ pusha
+ ; reading from disk requires setting specific values in all registers
+ ; so we will overwrite our input parameters from 'dx'. Let's save it
+ ; to the stack for later use.
+ push dx
+
+ mov ah, 0x02 ; ah <- int 0x13 function. 0x02 = 'read'
+ mov al, dh ; al <- number of sectors to read (0x01 .. 0x80)
+ mov cl, 0x02 ; cl <- sector (0x01 .. 0x11)
+ ; 0x01 is our boot sector, 0x02 is the first 'available' sector
+ mov ch, 0x00 ; ch <- cylinder (0x0 .. 0x3FF, upper 2 bits in 'cl')
+ ; dl <- drive number. Our caller sets it as a parameter and gets it from BIOS
+ ; (0 = floppy, 1 = floppy2, 0x80 = hdd, 0x81 = hdd2)
+ mov dh, 0x00 ; dh <- head number (0x0 .. 0xF)
+
+ ; [es:bx] <- pointer to buffer where the data will be stored
+ ; caller sets it up for us, and it is actually the standard location for int 13h
+ int 0x13 ; BIOS interrupt
+ jc disk_error ; if error (stored in the carry bit)
+
+ pop dx
+ cmp al, dh ; BIOS also sets 'al' to the # of sectors read. Compare it.
+ jne sectors_error
+ popa
+ ret
+
+
+disk_error:
+ mov bx, DISK_ERROR
+ call print
+ call print_nl
+ mov dh, ah ; ah = error code, dl = disk drive that dropped the error
+ call print_hex ; check out the code at http://stanislavs.org/helppc/int_13-1.html
+ jmp disk_loop
+
+sectors_error:
+ mov bx, SECTORS_ERROR
+ call print
+
+disk_loop:
+ jmp $
+
+DISK_ERROR: db "Disk read error", 0
+SECTORS_ERROR: db "Incorrect number of sectors read", 0
diff --git a/Part4/10_scheduler/boot/gdt.asm b/Part4/10_scheduler/boot/gdt.asm
new file mode 100644
index 0000000..e5db388
--- /dev/null
+++ b/Part4/10_scheduler/boot/gdt.asm
@@ -0,0 +1,35 @@
+gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps
+ ; the GDT starts with a null 8-byte
+ dd 0x0 ; 4 byte
+ dd 0x0 ; 4 byte
+
+; GDT for code segment. base = 0x00000000, length = 0xfffff
+; for flags, refer to os-dev.pdf document, page 36
+gdt_code:
+ dw 0xffff ; segment length, bits 0-15
+ dw 0x0 ; segment base, bits 0-15
+ db 0x0 ; segment base, bits 16-23
+ db 10011010b ; flags (8 bits)
+ db 11001111b ; flags (4 bits) + segment length, bits 16-19
+ db 0x0 ; segment base, bits 24-31
+
+; GDT for data segment. base and length identical to code segment
+; some flags changed, again, refer to os-dev.pdf
+gdt_data:
+ dw 0xffff
+ dw 0x0
+ db 0x0
+ db 10010010b
+ db 11001111b
+ db 0x0
+
+gdt_end:
+
+; GDT descriptor
+gdt_descriptor:
+ dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size
+ dd gdt_start ; address (32 bit)
+
+; define some constants for later use
+CODE_SEG equ gdt_code - gdt_start
+DATA_SEG equ gdt_data - gdt_start
diff --git a/Part4/10_scheduler/boot/kernel_entry.asm b/Part4/10_scheduler/boot/kernel_entry.asm
new file mode 100644
index 0000000..86f8fdf
--- /dev/null
+++ b/Part4/10_scheduler/boot/kernel_entry.asm
@@ -0,0 +1,4 @@
+[bits 32]
+[extern _start] ; Define calling point. Must have same name as kernel.c '_start' function
+call _start ; Calls the C function. The linker will know where it is placed in memory
+jmp $
diff --git a/Part4/10_scheduler/boot/print.asm b/Part4/10_scheduler/boot/print.asm
new file mode 100644
index 0000000..b066e0d
--- /dev/null
+++ b/Part4/10_scheduler/boot/print.asm
@@ -0,0 +1,37 @@
+print:
+ pusha
+
+; keep this in mind:
+; while (string[i] != 0) { print string[i]; i++ }
+
+; the comparison for string end (null byte)
+start:
+ mov al, [bx] ; 'bx' is the base address for the string
+ cmp al, 0
+ je done
+
+ ; the part where we print with the BIOS help
+ mov ah, 0x0e
+ int 0x10 ; 'al' already contains the char
+
+ ; increment pointer and do next loop
+ add bx, 1
+ jmp start
+
+done:
+ popa
+ ret
+
+
+
+print_nl:
+ pusha
+
+ mov ah, 0x0e
+ mov al, 0x0a ; newline char
+ int 0x10
+ mov al, 0x0d ; carriage return
+ int 0x10
+
+ popa
+ ret
diff --git a/Part4/10_scheduler/boot/print_hex.asm b/Part4/10_scheduler/boot/print_hex.asm
new file mode 100644
index 0000000..b537ef8
--- /dev/null
+++ b/Part4/10_scheduler/boot/print_hex.asm
@@ -0,0 +1,46 @@
+; receiving the data in 'dx'
+; For the examples we'll assume that we're called with dx=0x1234
+print_hex:
+ pusha
+
+ mov cx, 0 ; our index variable
+
+; Strategy: get the last char of 'dx', then convert to ASCII
+; Numeric ASCII values: '0' (ASCII 0x30) to '9' (0x39), so just add 0x30 to byte N.
+; For alphabetic characters A-F: 'A' (ASCII 0x41) to 'F' (0x46) we'll add 0x40
+; Then, move the ASCII byte to the correct position on the resulting string
+hex_loop:
+ cmp cx, 4 ; loop 4 times
+ je end
+
+ ; 1. convert last char of 'dx' to ascii
+ mov ax, dx ; we will use 'ax' as our working register
+ and ax, 0x000f ; 0x1234 -> 0x0004 by masking first three to zeros
+ add al, 0x30 ; add 0x30 to N to convert it to ASCII "N"
+ cmp al, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F'
+ jle step2
+ add al, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7
+
+step2:
+ ; 2. get the correct position of the string to place our ASCII char
+ ; bx <- base address + string length - index of char
+ mov bx, HEX_OUT + 5 ; base + length
+ sub bx, cx ; our index variable
+ mov [bx], al ; copy the ASCII char on 'al' to the position pointed by 'bx'
+ ror dx, 4 ; 0x1234 -> 0x4123 -> 0x3412 -> 0x2341 -> 0x1234
+
+ ; increment index and loop
+ add cx, 1
+ jmp hex_loop
+
+end:
+ ; prepare the parameter and call the function
+ ; remember that print receives parameters in 'bx'
+ mov bx, HEX_OUT
+ call print
+
+ popa
+ ret
+
+HEX_OUT:
+ db '0x0000',0 ; reserve memory for our new string
diff --git a/Part4/10_scheduler/boot/switch_pm.asm b/Part4/10_scheduler/boot/switch_pm.asm
new file mode 100644
index 0000000..9394da3
--- /dev/null
+++ b/Part4/10_scheduler/boot/switch_pm.asm
@@ -0,0 +1,22 @@
+[bits 16]
+switch_to_pm:
+ cli ; 1. disable interrupts
+ lgdt [gdt_descriptor] ; 2. load the GDT descriptor
+ mov eax, cr0
+ or eax, 0x1 ; 3. set 32-bit mode bit in cr0
+ mov cr0, eax
+ jmp CODE_SEG:init_pm ; 4. far jump by using a different segment
+
+[bits 32]
+init_pm: ; we are now using 32-bit instructions
+ mov ax, DATA_SEG ; 5. update the segment registers
+ mov ds, ax
+ mov ss, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ mov ebp, 0x90000 ; 6. update the stack right at the top of the free space
+ mov esp, ebp
+
+ call BEGIN_PM ; 7. Call a well-known label with useful code
diff --git a/Part4/10_scheduler/cpu/idt.c b/Part4/10_scheduler/cpu/idt.c
new file mode 100644
index 0000000..f904a00
--- /dev/null
+++ b/Part4/10_scheduler/cpu/idt.c
@@ -0,0 +1,16 @@
+#include "idt.h"
+
+void set_idt_gate(int n, u32 handler) {
+ idt[n].low_offset = low_16(handler);
+ idt[n].sel = KERNEL_CS;
+ idt[n].always0 = 0;
+ idt[n].flags = 0x8E;
+ idt[n].high_offset = high_16(handler);
+}
+
+void set_idt() {
+ idt_reg.base = (u32) &idt;
+ idt_reg.limit = IDT_ENTRIES * sizeof(idt_gate_t) - 1;
+ /* Don't make the mistake of loading &idt -- always load &idt_reg */
+ __asm__ __volatile__("lidtl (%0)" : : "r" (&idt_reg));
+}
diff --git a/Part4/10_scheduler/cpu/idt.h b/Part4/10_scheduler/cpu/idt.h
new file mode 100644
index 0000000..27bfac5
--- /dev/null
+++ b/Part4/10_scheduler/cpu/idt.h
@@ -0,0 +1,39 @@
+#ifndef IDT_H
+#define IDT_H
+
+#include "types.h"
+
+/* Segment selectors */
+#define KERNEL_CS 0x08
+
+/* How every interrupt gate (handler) is defined */
+typedef struct {
+ u16 low_offset; /* Lower 16 bits of handler function address */
+ u16 sel; /* Kernel segment selector */
+ u8 always0;
+ /* First byte
+ * Bit 7: "Interrupt is present"
+ * Bits 6-5: Privilege level of caller (0=kernel..3=user)
+ * Bit 4: Set to 0 for interrupt gates
+ * Bits 3-0: bits 1110 = decimal 14 = "32 bit interrupt gate" */
+ u8 flags;
+ u16 high_offset; /* Higher 16 bits of handler function address */
+} __attribute__((packed)) idt_gate_t ;
+
+/* A pointer to the array of interrupt handlers.
+ * Assembly instruction 'lidt' will read it */
+typedef struct {
+ u16 limit;
+ u32 base;
+} __attribute__((packed)) idt_register_t;
+
+#define IDT_ENTRIES 256
+idt_gate_t idt[IDT_ENTRIES];
+idt_register_t idt_reg;
+
+
+/* Functions implemented in idt.c */
+void set_idt_gate(int n, u32 handler);
+void set_idt();
+
+#endif
diff --git a/Part4/10_scheduler/cpu/interrupt.asm b/Part4/10_scheduler/cpu/interrupt.asm
new file mode 100644
index 0000000..bf83b7a
--- /dev/null
+++ b/Part4/10_scheduler/cpu/interrupt.asm
@@ -0,0 +1,425 @@
+; Defined in isr.c
+[extern isr_handler]
+[extern irq_handler]
+
+; Common ISR code
+isr_common_stub:
+ ; 1. Save CPU state
+ pusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax
+ mov ax, ds ; Lower 16-bits of eax = ds.
+ push eax ; save the data segment descriptor
+ mov ax, 0x10 ; kernel data segment descriptor
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ ; 2. Call C handler
+ call isr_handler
+
+ ; 3. Restore state
+ pop eax
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ popa
+ add esp, 8 ; Cleans up the pushed error code and pushed ISR number
+ sti
+ iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP
+
+; Common IRQ code. Identical to ISR code except for the 'call'
+; and the 'pop ebx'
+irq_common_stub:
+ pusha
+ mov ax, ds
+ push eax
+ mov ax, 0x10
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ call irq_handler ; Different than the ISR code
+ pop ebx ; Different than the ISR code
+ mov ds, bx
+ mov es, bx
+ mov fs, bx
+ mov gs, bx
+ popa
+ add esp, 8
+ sti
+ iret
+
+; We don't get information about which interrupt was caller
+; when the handler is run, so we will need to have a different handler
+; for every interrupt.
+; Furthermore, some interrupts push an error code onto the stack but others
+; don't, so we will push a dummy error code for those which don't, so that
+; we have a consistent stack for all of them.
+
+; First make the ISRs global
+global isr0
+global isr1
+global isr2
+global isr3
+global isr4
+global isr5
+global isr6
+global isr7
+global isr8
+global isr9
+global isr10
+global isr11
+global isr12
+global isr13
+global isr14
+global isr15
+global isr16
+global isr17
+global isr18
+global isr19
+global isr20
+global isr21
+global isr22
+global isr23
+global isr24
+global isr25
+global isr26
+global isr27
+global isr28
+global isr29
+global isr30
+global isr31
+; IRQs
+global irq0
+global irq1
+global irq2
+global irq3
+global irq4
+global irq5
+global irq6
+global irq7
+global irq8
+global irq9
+global irq10
+global irq11
+global irq12
+global irq13
+global irq14
+global irq15
+
+; 0: Divide By Zero Exception
+isr0:
+ cli
+ push byte 0
+ push byte 0
+ jmp isr_common_stub
+
+; 1: Debug Exception
+isr1:
+ cli
+ push byte 0
+ push byte 1
+ jmp isr_common_stub
+
+; 2: Non Maskable Interrupt Exception
+isr2:
+ cli
+ push byte 0
+ push byte 2
+ jmp isr_common_stub
+
+; 3: Int 3 Exception
+isr3:
+ cli
+ push byte 0
+ push byte 3
+ jmp isr_common_stub
+
+; 4: INTO Exception
+isr4:
+ cli
+ push byte 0
+ push byte 4
+ jmp isr_common_stub
+
+; 5: Out of Bounds Exception
+isr5:
+ cli
+ push byte 0
+ push byte 5
+ jmp isr_common_stub
+
+; 6: Invalid Opcode Exception
+isr6:
+ cli
+ push byte 0
+ push byte 6
+ jmp isr_common_stub
+
+; 7: Coprocessor Not Available Exception
+isr7:
+ cli
+ push byte 0
+ push byte 7
+ jmp isr_common_stub
+
+; 8: Double Fault Exception (With Error Code!)
+isr8:
+ cli
+ push byte 8
+ jmp isr_common_stub
+
+; 9: Coprocessor Segment Overrun Exception
+isr9:
+ cli
+ push byte 0
+ push byte 9
+ jmp isr_common_stub
+
+; 10: Bad TSS Exception (With Error Code!)
+isr10:
+ cli
+ push byte 10
+ jmp isr_common_stub
+
+; 11: Segment Not Present Exception (With Error Code!)
+isr11:
+ cli
+ push byte 11
+ jmp isr_common_stub
+
+; 12: Stack Fault Exception (With Error Code!)
+isr12:
+ cli
+ push byte 12
+ jmp isr_common_stub
+
+; 13: General Protection Fault Exception (With Error Code!)
+isr13:
+ cli
+ push byte 13
+ jmp isr_common_stub
+
+; 14: Page Fault Exception (With Error Code!)
+isr14:
+ cli
+ push byte 14
+ jmp isr_common_stub
+
+; 15: Reserved Exception
+isr15:
+ cli
+ push byte 0
+ push byte 15
+ jmp isr_common_stub
+
+; 16: Floating Point Exception
+isr16:
+ cli
+ push byte 0
+ push byte 16
+ jmp isr_common_stub
+
+; 17: Alignment Check Exception
+isr17:
+ cli
+ push byte 0
+ push byte 17
+ jmp isr_common_stub
+
+; 18: Machine Check Exception
+isr18:
+ cli
+ push byte 0
+ push byte 18
+ jmp isr_common_stub
+
+; 19: Reserved
+isr19:
+ cli
+ push byte 0
+ push byte 19
+ jmp isr_common_stub
+
+; 20: Reserved
+isr20:
+ cli
+ push byte 0
+ push byte 20
+ jmp isr_common_stub
+
+; 21: Reserved
+isr21:
+ cli
+ push byte 0
+ push byte 21
+ jmp isr_common_stub
+
+; 22: Reserved
+isr22:
+ cli
+ push byte 0
+ push byte 22
+ jmp isr_common_stub
+
+; 23: Reserved
+isr23:
+ cli
+ push byte 0
+ push byte 23
+ jmp isr_common_stub
+
+; 24: Reserved
+isr24:
+ cli
+ push byte 0
+ push byte 24
+ jmp isr_common_stub
+
+; 25: Reserved
+isr25:
+ cli
+ push byte 0
+ push byte 25
+ jmp isr_common_stub
+
+; 26: Reserved
+isr26:
+ cli
+ push byte 0
+ push byte 26
+ jmp isr_common_stub
+
+; 27: Reserved
+isr27:
+ cli
+ push byte 0
+ push byte 27
+ jmp isr_common_stub
+
+; 28: Reserved
+isr28:
+ cli
+ push byte 0
+ push byte 28
+ jmp isr_common_stub
+
+; 29: Reserved
+isr29:
+ cli
+ push byte 0
+ push byte 29
+ jmp isr_common_stub
+
+; 30: Reserved
+isr30:
+ cli
+ push byte 0
+ push byte 30
+ jmp isr_common_stub
+
+; 31: Reserved
+isr31:
+ cli
+ push byte 0
+ push byte 31
+ jmp isr_common_stub
+
+; IRQ handlers
+irq0:
+ cli
+ push byte 0
+ push byte 32
+ jmp irq_common_stub
+
+irq1:
+ cli
+ push byte 1
+ push byte 33
+ jmp irq_common_stub
+
+irq2:
+ cli
+ push byte 2
+ push byte 34
+ jmp irq_common_stub
+
+irq3:
+ cli
+ push byte 3
+ push byte 35
+ jmp irq_common_stub
+
+irq4:
+ cli
+ push byte 4
+ push byte 36
+ jmp irq_common_stub
+
+irq5:
+ cli
+ push byte 5
+ push byte 37
+ jmp irq_common_stub
+
+irq6:
+ cli
+ push byte 6
+ push byte 38
+ jmp irq_common_stub
+
+irq7:
+ cli
+ push byte 7
+ push byte 39
+ jmp irq_common_stub
+
+irq8:
+ cli
+ push byte 8
+ push byte 40
+ jmp irq_common_stub
+
+irq9:
+ cli
+ push byte 9
+ push byte 41
+ jmp irq_common_stub
+
+irq10:
+ cli
+ push byte 10
+ push byte 42
+ jmp irq_common_stub
+
+irq11:
+ cli
+ push byte 11
+ push byte 43
+ jmp irq_common_stub
+
+irq12:
+ cli
+ push byte 12
+ push byte 44
+ jmp irq_common_stub
+
+irq13:
+ cli
+ push byte 13
+ push byte 45
+ jmp irq_common_stub
+
+irq14:
+ cli
+ push byte 14
+ push byte 46
+ jmp irq_common_stub
+
+irq15:
+ cli
+ push byte 15
+ push byte 47
+ jmp irq_common_stub
+
diff --git a/Part4/10_scheduler/cpu/isr.c b/Part4/10_scheduler/cpu/isr.c
new file mode 100644
index 0000000..c46a1d8
--- /dev/null
+++ b/Part4/10_scheduler/cpu/isr.c
@@ -0,0 +1,153 @@
+#include "isr.h"
+#include "idt.h"
+#include "../drivers/screen.h"
+#include "../drivers/keyboard.h"
+#include "../libc/string.h"
+#include "timer.h"
+#include "ports.h"
+
+isr_t interrupt_handlers[256];
+
+/* Can't do this with a loop because we need the address
+ * of the function names */
+void isr_install() {
+ set_idt_gate(0, (u32)isr0);
+ set_idt_gate(1, (u32)isr1);
+ set_idt_gate(2, (u32)isr2);
+ set_idt_gate(3, (u32)isr3);
+ set_idt_gate(4, (u32)isr4);
+ set_idt_gate(5, (u32)isr5);
+ set_idt_gate(6, (u32)isr6);
+ set_idt_gate(7, (u32)isr7);
+ set_idt_gate(8, (u32)isr8);
+ set_idt_gate(9, (u32)isr9);
+ set_idt_gate(10, (u32)isr10);
+ set_idt_gate(11, (u32)isr11);
+ set_idt_gate(12, (u32)isr12);
+ set_idt_gate(13, (u32)isr13);
+ set_idt_gate(14, (u32)isr14);
+ set_idt_gate(15, (u32)isr15);
+ set_idt_gate(16, (u32)isr16);
+ set_idt_gate(17, (u32)isr17);
+ set_idt_gate(18, (u32)isr18);
+ set_idt_gate(19, (u32)isr19);
+ set_idt_gate(20, (u32)isr20);
+ set_idt_gate(21, (u32)isr21);
+ set_idt_gate(22, (u32)isr22);
+ set_idt_gate(23, (u32)isr23);
+ set_idt_gate(24, (u32)isr24);
+ set_idt_gate(25, (u32)isr25);
+ set_idt_gate(26, (u32)isr26);
+ set_idt_gate(27, (u32)isr27);
+ set_idt_gate(28, (u32)isr28);
+ set_idt_gate(29, (u32)isr29);
+ set_idt_gate(30, (u32)isr30);
+ set_idt_gate(31, (u32)isr31);
+
+ // Remap the PIC
+ port_byte_out(0x20, 0x11);
+ port_byte_out(0xA0, 0x11);
+ port_byte_out(0x21, 0x20);
+ port_byte_out(0xA1, 0x28);
+ port_byte_out(0x21, 0x04);
+ port_byte_out(0xA1, 0x02);
+ port_byte_out(0x21, 0x01);
+ port_byte_out(0xA1, 0x01);
+ port_byte_out(0x21, 0x0);
+ port_byte_out(0xA1, 0x0);
+
+ // Install the IRQs
+ set_idt_gate(32, (u32)irq0);
+ set_idt_gate(33, (u32)irq1);
+ set_idt_gate(34, (u32)irq2);
+ set_idt_gate(35, (u32)irq3);
+ set_idt_gate(36, (u32)irq4);
+ set_idt_gate(37, (u32)irq5);
+ set_idt_gate(38, (u32)irq6);
+ set_idt_gate(39, (u32)irq7);
+ set_idt_gate(40, (u32)irq8);
+ set_idt_gate(41, (u32)irq9);
+ set_idt_gate(42, (u32)irq10);
+ set_idt_gate(43, (u32)irq11);
+ set_idt_gate(44, (u32)irq12);
+ set_idt_gate(45, (u32)irq13);
+ set_idt_gate(46, (u32)irq14);
+ set_idt_gate(47, (u32)irq15);
+
+ set_idt(); // Load with ASM
+}
+
+/* To print the message which defines every exception */
+char *exception_messages[] = {
+ "Division By Zero",
+ "Debug",
+ "Non Maskable Interrupt",
+ "Breakpoint",
+ "Into Detected Overflow",
+ "Out of Bounds",
+ "Invalid Opcode",
+ "No Coprocessor",
+
+ "Double Fault",
+ "Coprocessor Segment Overrun",
+ "Bad TSS",
+ "Segment Not Present",
+ "Stack Fault",
+ "General Protection Fault",
+ "Page Fault",
+ "Unknown Interrupt",
+
+ "Coprocessor Fault",
+ "Alignment Check",
+ "Machine Check",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved",
+ "Reserved"
+};
+
+void isr_handler(registers_t r) {
+ kprint("received interrupt: ");
+ char s[3];
+ int_to_ascii(r.int_no, s);
+ kprint(s);
+ kprint("\n");
+ kprint(exception_messages[r.int_no]);
+ kprint("\n");
+}
+
+void register_interrupt_handler(u8 n, isr_t handler) {
+ interrupt_handlers[n] = handler;
+}
+
+void irq_handler(registers_t r) {
+ /* After every interrupt we need to send an EOI to the PICs
+ * or they will not send another interrupt again */
+ if (r.int_no >= 40) port_byte_out(0xA0, 0x20); /* slave */
+ port_byte_out(0x20, 0x20); /* master */
+
+ /* Handle the interrupt in a more modular way */
+ if (interrupt_handlers[r.int_no] != 0) {
+ isr_t handler = interrupt_handlers[r.int_no];
+ handler(r);
+ }
+}
+
+void irq_install() {
+ /* Enable interruptions */
+ asm volatile("sti");
+ /* IRQ0: timer */
+ init_timer(50);
+ /* IRQ1: keyboard */
+ init_keyboard();
+}
diff --git a/Part4/10_scheduler/cpu/isr.h b/Part4/10_scheduler/cpu/isr.h
new file mode 100644
index 0000000..337fbf3
--- /dev/null
+++ b/Part4/10_scheduler/cpu/isr.h
@@ -0,0 +1,89 @@
+#ifndef ISR_H
+#define ISR_H
+
+#include "types.h"
+
+/* ISRs reserved for CPU exceptions */
+extern void isr0();
+extern void isr1();
+extern void isr2();
+extern void isr3();
+extern void isr4();
+extern void isr5();
+extern void isr6();
+extern void isr7();
+extern void isr8();
+extern void isr9();
+extern void isr10();
+extern void isr11();
+extern void isr12();
+extern void isr13();
+extern void isr14();
+extern void isr15();
+extern void isr16();
+extern void isr17();
+extern void isr18();
+extern void isr19();
+extern void isr20();
+extern void isr21();
+extern void isr22();
+extern void isr23();
+extern void isr24();
+extern void isr25();
+extern void isr26();
+extern void isr27();
+extern void isr28();
+extern void isr29();
+extern void isr30();
+extern void isr31();
+/* IRQ definitions */
+extern void irq0();
+extern void irq1();
+extern void irq2();
+extern void irq3();
+extern void irq4();
+extern void irq5();
+extern void irq6();
+extern void irq7();
+extern void irq8();
+extern void irq9();
+extern void irq10();
+extern void irq11();
+extern void irq12();
+extern void irq13();
+extern void irq14();
+extern void irq15();
+
+#define IRQ0 32
+#define IRQ1 33
+#define IRQ2 34
+#define IRQ3 35
+#define IRQ4 36
+#define IRQ5 37
+#define IRQ6 38
+#define IRQ7 39
+#define IRQ8 40
+#define IRQ9 41
+#define IRQ10 42
+#define IRQ11 43
+#define IRQ12 44
+#define IRQ13 45
+#define IRQ14 46
+#define IRQ15 47
+
+/* Struct which aggregates many registers */
+typedef struct {
+ u32 ds; /* Data segment selector */
+ u32 edi, esi, ebp, esp, ebx, edx, ecx, eax; /* Pushed by pusha. */
+ u32 int_no, err_code; /* Interrupt number and error code (if applicable) */
+ u32 eip, cs, eflags, useresp, ss; /* Pushed by the processor automatically */
+} registers_t;
+
+void isr_install();
+void isr_handler(registers_t r);
+void irq_install();
+
+typedef void (*isr_t)(registers_t);
+void register_interrupt_handler(u8 n, isr_t handler);
+
+#endif
diff --git a/Part4/10_scheduler/cpu/ports.c b/Part4/10_scheduler/cpu/ports.c
new file mode 100644
index 0000000..f015689
--- /dev/null
+++ b/Part4/10_scheduler/cpu/ports.c
@@ -0,0 +1,37 @@
+#include "ports.h"
+
+/**
+ * Read a byte from the specified port
+ */
+u8 port_byte_in (u16 port) {
+ u8 result;
+ /* Inline assembler syntax
+ * !! Notice how the source and destination registers are switched from NASM !!
+ *
+ * '"=a" (result)'; set '=' the C variable '(result)' to the value of register e'a'x
+ * '"d" (port)': map the C variable '(port)' into e'd'x register
+ *
+ * Inputs and outputs are separated by colons
+ */
+ __asm__("in %%dx, %%al" : "=a" (result) : "d" (port));
+ return result;
+}
+
+void port_byte_out (u16 port, u8 data) {
+ /* Notice how here both registers are mapped to C variables and
+ * nothing is returned, thus, no equals '=' in the asm syntax
+ * However we see a comma since there are two variables in the input area
+ * and none in the 'return' area
+ */
+ __asm__ __volatile__("out %%al, %%dx" : : "a" (data), "d" (port));
+}
+
+u16 port_word_in (u16 port) {
+ u16 result;
+ __asm__("in %%dx, %%ax" : "=a" (result) : "d" (port));
+ return result;
+}
+
+void port_word_out (u16 port, u16 data) {
+ __asm__ __volatile__("out %%ax, %%dx" : : "a" (data), "d" (port));
+}
diff --git a/Part4/10_scheduler/cpu/ports.h b/Part4/10_scheduler/cpu/ports.h
new file mode 100644
index 0000000..0b7fa2c
--- /dev/null
+++ b/Part4/10_scheduler/cpu/ports.h
@@ -0,0 +1,11 @@
+#ifndef PORTS_H
+#define PORTS_H
+
+#include "../cpu/types.h"
+
+unsigned char port_byte_in (u16 port);
+void port_byte_out (u16 port, u8 data);
+unsigned short port_word_in (u16 port);
+void port_word_out (u16 port, u16 data);
+
+#endif
diff --git a/Part4/10_scheduler/cpu/timer.c b/Part4/10_scheduler/cpu/timer.c
new file mode 100644
index 0000000..c0f4357
--- /dev/null
+++ b/Part4/10_scheduler/cpu/timer.c
@@ -0,0 +1,25 @@
+#include "timer.h"
+#include "isr.h"
+#include "ports.h"
+#include "../libc/function.h"
+
+u32 tick = 0;
+
+static void timer_callback(registers_t regs) {
+ tick++;
+ UNUSED(regs);
+}
+
+void init_timer(u32 freq) {
+ /* Install the function we just wrote */
+ register_interrupt_handler(IRQ0, timer_callback);
+
+ /* Get the PIT value: hardware clock at 1193180 Hz */
+ u32 divisor = 1193180 / freq;
+ u8 low = (u8)(divisor & 0xFF);
+ u8 high = (u8)( (divisor >> 8) & 0xFF);
+ /* Send the command */
+ port_byte_out(0x43, 0x36); /* Command port */
+ port_byte_out(0x40, low);
+ port_byte_out(0x40, high);
+}
diff --git a/Part4/10_scheduler/cpu/timer.h b/Part4/10_scheduler/cpu/timer.h
new file mode 100644
index 0000000..5d0195b
--- /dev/null
+++ b/Part4/10_scheduler/cpu/timer.h
@@ -0,0 +1,8 @@
+#ifndef TIMER_H
+#define TIMER_H
+
+#include "types.h"
+
+void init_timer(u32 freq);
+
+#endif
diff --git a/Part4/10_scheduler/cpu/types.h b/Part4/10_scheduler/cpu/types.h
new file mode 100644
index 0000000..48f450f
--- /dev/null
+++ b/Part4/10_scheduler/cpu/types.h
@@ -0,0 +1,18 @@
+#ifndef TYPES_H
+#define TYPES_H
+
+/* Instead of using 'chars' to allocate non-character bytes,
+ * we will use these new type with no semantic meaning */
+typedef unsigned int u32;
+typedef int s32;
+typedef unsigned short u16;
+typedef short s16;
+typedef unsigned char u8;
+typedef char s8;
+
+#define NULL ((void *) 0)
+
+#define low_16(address) (u16)((address) & 0xFFFF)
+#define high_16(address) (u16)(((address) >> 16) & 0xFFFF)
+
+#endif
diff --git a/Part4/10_scheduler/drivers/keyboard.c b/Part4/10_scheduler/drivers/keyboard.c
new file mode 100644
index 0000000..6f40ac6
--- /dev/null
+++ b/Part4/10_scheduler/drivers/keyboard.c
@@ -0,0 +1,51 @@
+#include "keyboard.h"
+#include "../cpu/ports.h"
+#include "../cpu/isr.h"
+#include "screen.h"
+#include "../libc/string.h"
+#include "../libc/function.h"
+#include "../kernel/kernel.h"
+
+#define BACKSPACE 0x0E
+#define ENTER 0x1C
+
+static char key_buffer[256];
+
+#define SC_MAX 57
+const char *sc_name[] = { "ERROR", "Esc", "1", "2", "3", "4", "5", "6",
+ "7", "8", "9", "0", "-", "=", "Backspace", "Tab", "Q", "W", "E",
+ "R", "T", "Y", "U", "I", "O", "P", "[", "]", "Enter", "Lctrl",
+ "A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "'", "`",
+ "LShift", "\\", "Z", "X", "C", "V", "B", "N", "M", ",", ".",
+ "/", "RShift", "Keypad *", "LAlt", "Spacebar"};
+const char sc_ascii[] = { '?', '?', '1', '2', '3', '4', '5', '6',
+ '7', '8', '9', '0', '-', '=', '?', '?', 'Q', 'W', 'E', 'R', 'T', 'Y',
+ 'U', 'I', 'O', 'P', '[', ']', '?', '?', 'A', 'S', 'D', 'F', 'G',
+ 'H', 'J', 'K', 'L', ';', '\'', '`', '?', '\\', 'Z', 'X', 'C', 'V',
+ 'B', 'N', 'M', ',', '.', '/', '?', '?', '?', ' '};
+
+static void keyboard_callback(registers_t regs) {
+ /* The PIC leaves us the scancode in port 0x60 */
+ u8 scancode = port_byte_in(0x60);
+
+ if (scancode > SC_MAX) return;
+ if (scancode == BACKSPACE) {
+ backspace(key_buffer);
+ kprint_backspace();
+ } else if (scancode == ENTER) {
+ kprint("\n");
+ user_input(key_buffer); /* kernel-controlled function */
+ key_buffer[0] = '\0';
+ } else {
+ char letter = sc_ascii[(int)scancode];
+ /* Remember that kprint only accepts char[] */
+ char str[2] = {letter, '\0'};
+ append(key_buffer, letter);
+ kprint(str);
+ }
+ UNUSED(regs);
+}
+
+void init_keyboard() {
+ register_interrupt_handler(IRQ1, keyboard_callback);
+}
diff --git a/Part4/10_scheduler/drivers/keyboard.h b/Part4/10_scheduler/drivers/keyboard.h
new file mode 100644
index 0000000..ff317a5
--- /dev/null
+++ b/Part4/10_scheduler/drivers/keyboard.h
@@ -0,0 +1,7 @@
+#ifndef KEYBOARD_H
+#define KEYBOARD_H
+#include "../cpu/types.h"
+
+void init_keyboard();
+
+#endif
diff --git a/Part4/10_scheduler/drivers/screen.c b/Part4/10_scheduler/drivers/screen.c
new file mode 100644
index 0000000..e955688
--- /dev/null
+++ b/Part4/10_scheduler/drivers/screen.c
@@ -0,0 +1,149 @@
+#include "screen.h"
+#include "../cpu/ports.h"
+#include "../libc/mem.h"
+
+/* Declaration of private functions */
+int get_cursor_offset();
+void set_cursor_offset(int offset);
+int print_char(char c, int col, int row, char attr);
+int get_offset(int col, int row);
+int get_offset_row(int offset);
+int get_offset_col(int offset);
+
+/**********************************************************
+ * Public Kernel API functions *
+ **********************************************************/
+
+/**
+ * Print a message on the specified location
+ * If col, row, are negative, we will use the current offset
+ */
+void kprint_at(char *message, int col, int row) {
+ /* Set cursor if col/row are negative */
+ int offset;
+ if (col >= 0 && row >= 0)
+ offset = get_offset(col, row);
+ else {
+ offset = get_cursor_offset();
+ row = get_offset_row(offset);
+ col = get_offset_col(offset);
+ }
+
+ /* Loop through message and print it */
+ int i = 0;
+ while (message[i] != 0) {
+ offset = print_char(message[i++], col, row, WHITE_ON_BLACK);
+ /* Compute row/col for next iteration */
+ row = get_offset_row(offset);
+ col = get_offset_col(offset);
+ }
+}
+
+void kprint(char *message) {
+ kprint_at(message, -1, -1);
+}
+
+void kprint_backspace() {
+ int offset = get_cursor_offset()-2;
+ int row = get_offset_row(offset);
+ int col = get_offset_col(offset);
+ print_char(0x08, col, row, WHITE_ON_BLACK);
+}
+
+
+/**********************************************************
+ * Private kernel functions *
+ **********************************************************/
+
+
+/**
+ * Innermost print function for our kernel, directly accesses the video memory
+ *
+ * If 'col' and 'row' are negative, we will print at current cursor location
+ * If 'attr' is zero it will use 'white on black' as default
+ * Returns the offset of the next character
+ * Sets the video cursor to the returned offset
+ */
+int print_char(char c, int col, int row, char attr) {
+ u8 *vidmem = (u8*) VIDEO_ADDRESS;
+ if (!attr) attr = WHITE_ON_BLACK;
+
+ /* Error control: print a red 'E' if the coords aren't right */
+ if (col >= MAX_COLS || row >= MAX_ROWS) {
+ vidmem[2*(MAX_COLS)*(MAX_ROWS)-2] = 'E';
+ vidmem[2*(MAX_COLS)*(MAX_ROWS)-1] = RED_ON_WHITE;
+ return get_offset(col, row);
+ }
+
+ int offset;
+ if (col >= 0 && row >= 0) offset = get_offset(col, row);
+ else offset = get_cursor_offset();
+
+ if (c == '\n') {
+ row = get_offset_row(offset);
+ offset = get_offset(0, row+1);
+ } else if (c == 0x08) { /* Backspace */
+ vidmem[offset] = ' ';
+ vidmem[offset+1] = attr;
+ } else {
+ vidmem[offset] = c;
+ vidmem[offset+1] = attr;
+ offset += 2;
+ }
+
+ /* Check if the offset is over screen size and scroll */
+ if (offset >= MAX_ROWS * MAX_COLS * 2) {
+ int i;
+ for (i = 1; i < MAX_ROWS; i++)
+ memory_copy((u8*)(get_offset(0, i) + VIDEO_ADDRESS),
+ (u8*)(get_offset(0, i-1) + VIDEO_ADDRESS),
+ MAX_COLS * 2);
+
+ /* Blank last line */
+ char *last_line = (char*) (get_offset(0, MAX_ROWS-1) + (u8*) VIDEO_ADDRESS);
+ for (i = 0; i < MAX_COLS * 2; i++) last_line[i] = 0;
+
+ offset -= 2 * MAX_COLS;
+ }
+
+ set_cursor_offset(offset);
+ return offset;
+}
+
+int get_cursor_offset() {
+ /* Use the VGA ports to get the current cursor position
+ * 1. Ask for high byte of the cursor offset (data 14)
+ * 2. Ask for low byte (data 15)
+ */
+ port_byte_out(REG_SCREEN_CTRL, 14);
+ int offset = port_byte_in(REG_SCREEN_DATA) << 8; /* High byte: << 8 */
+ port_byte_out(REG_SCREEN_CTRL, 15);
+ offset += port_byte_in(REG_SCREEN_DATA);
+ return offset * 2; /* Position * size of character cell */
+}
+
+void set_cursor_offset(int offset) {
+ /* Similar to get_cursor_offset, but instead of reading we write data */
+ offset /= 2;
+ port_byte_out(REG_SCREEN_CTRL, 14);
+ port_byte_out(REG_SCREEN_DATA, (u8)(offset >> 8));
+ port_byte_out(REG_SCREEN_CTRL, 15);
+ port_byte_out(REG_SCREEN_DATA, (u8)(offset & 0xff));
+}
+
+void clear_screen() {
+ int screen_size = MAX_COLS * MAX_ROWS;
+ int i;
+ u8 *screen = (u8*) VIDEO_ADDRESS;
+
+ for (i = 0; i < screen_size; i++) {
+ screen[i*2] = ' ';
+ screen[i*2+1] = WHITE_ON_BLACK;
+ }
+ set_cursor_offset(get_offset(0, 0));
+}
+
+
+int get_offset(int col, int row) { return 2 * (row * MAX_COLS + col); }
+int get_offset_row(int offset) { return offset / (2 * MAX_COLS); }
+int get_offset_col(int offset) { return (offset - (get_offset_row(offset)*2*MAX_COLS))/2; }
diff --git a/Part4/10_scheduler/drivers/screen.h b/Part4/10_scheduler/drivers/screen.h
new file mode 100644
index 0000000..24ec3e4
--- /dev/null
+++ b/Part4/10_scheduler/drivers/screen.h
@@ -0,0 +1,22 @@
+#ifndef SCREEN_H
+#define SCREEN_H
+
+#include "../cpu/types.h"
+
+#define VIDEO_ADDRESS 0xb8000
+#define MAX_ROWS 25
+#define MAX_COLS 80
+#define WHITE_ON_BLACK 0x0f
+#define RED_ON_WHITE 0xf4
+
+/* Screen i/o ports */
+#define REG_SCREEN_CTRL 0x3d4
+#define REG_SCREEN_DATA 0x3d5
+
+/* Public kernel API */
+void clear_screen();
+void kprint_at(char *message, int col, int row);
+void kprint(char *message);
+void kprint_backspace();
+
+#endif
diff --git a/Part4/10_scheduler/e_compile.sh b/Part4/10_scheduler/e_compile.sh
new file mode 100644
index 0000000..f6f8b9c
--- /dev/null
+++ b/Part4/10_scheduler/e_compile.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+### interrupt.asm has no .c counterpart so it needs to be built first
+nasm ./cpu/interrupt.asm -f elf -o interrupt.o
+
+### Alternatively to see assembly output -S
+### Generate .s the assembly language output prior to generating .o machine files -S
+### gcc -g -m32 -fno-pie -ffreestanding -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs -Wall -Wextra -Werror ./libc/mem.c ./libc/string.c ./drivers/keyboard.c ./drivers/screen.c ./cpu/isr.c ./cpu/ports.c ./cpu/idt.c ./cpu/timer.c ./libc/globals.c -S hello.c
+
+
+### Generate .o machine files but do not link -c
+gcc -g -m32 -fno-pie -ffreestanding -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs -Wall -Wextra -Werror ./libc/mem.c ./libc/string.c ./drivers/keyboard.c ./drivers/screen.c ./cpu/isr.c ./cpu/ports.c ./cpu/idt.c ./cpu/timer.c ./libc/globals.c -c --entry start_ hello.c
+
+### Do the final link to create executable "final" and set entry to 0x10000
+ld -m elf_i386 -o hey_there -Ttext 0x10000 *.o --oformat binary --entry start_
diff --git a/Part4/10_scheduler/hello.c b/Part4/10_scheduler/hello.c
new file mode 100644
index 0000000..29fafdf
--- /dev/null
+++ b/Part4/10_scheduler/hello.c
@@ -0,0 +1,55 @@
+#include "./cpu/types.h"
+#include "./libc/globals.h"
+
+#include "./cpu/isr.h"
+#include "./cpu/idt.h"
+#include "./cpu/ports.h"
+#include "./cpu/timer.h"
+
+#include "./drivers/keyboard.h"
+#include "./drivers/screen.h"
+
+#include "./libc/mem.h"
+#include "./libc/string.h"
+#include "./libc/function.h"
+
+
+void start_( void)
+{
+ int pid = 0;
+ char c[16];
+ kprint("Process ");
+ hex_to_ascii( (u32 ) pid, c);
+ kprint( c);
+ kprint("has been entered. Hey there.\n");
+ return;
+}
+
+void user_input(char *input) {
+ if (strcmp(input, "END") == 0) {
+ kprint("Stopping the CPU. Bye!\n");
+ asm volatile("hlt");
+ } else if (strcmp(input, "ADD") == 0) {
+ u32 *n0; n0 = (u32 *) 16; n0++;
+ node *n1; n1 = NULL;
+ kmalloc( 20, 0, (u32 *) &n1);
+ n1->id = global_id++;
+ n1->base = global_id*100;
+ n1->size = global_id*10;
+ n1->next = NULL;
+ n1->previous = NULL;
+ kprint("ADDED: ");
+ char c[16];
+ hex_to_ascii( (u32 ) n1, c);
+ kprint( c);
+ kprint( " id: ");
+ hex_to_ascii( n1->id, c);
+ kprint( c);
+ } else if (strcmp(input, "ZOO") == 0) {
+ kprint("ZOO.\n");
+ }
+
+ kprint("You said: ");
+ kprint(input);
+ kprint("\n> ");
+}
diff --git a/Part4/10_scheduler/hey_there b/Part4/10_scheduler/hey_there
new file mode 100644
index 0000000..a706842
Binary files /dev/null and b/Part4/10_scheduler/hey_there differ
diff --git a/Part4/10_scheduler/kernel/kernel.c b/Part4/10_scheduler/kernel/kernel.c
new file mode 100644
index 0000000..f495291
--- /dev/null
+++ b/Part4/10_scheduler/kernel/kernel.c
@@ -0,0 +1,41 @@
+#include "kernel.h"
+
+void _start() {
+ free_mem_addr = 0x20000;
+ global_id = 0;
+ memory_limit = 0x40000;
+ isr_install();
+ irq_install();
+
+ kprint("Type something, it will go through the kernel\n"
+ "Type END to halt the CPU\n> ");
+}
+
+void user_input(char *input) {
+ if (strcmp(input, "END") == 0) {
+ kprint("Stopping the CPU. Bye!\n");
+ asm volatile("hlt");
+ } else if (strcmp(input, "ADD") == 0) {
+ u32 *n0; n0 = (u32 *) 16; n0++;
+ node *n1; n1 = NULL;
+ kmalloc( 20, 0, (u32 *) &n1);
+ n1->id = global_id++;
+ n1->base = global_id*100;
+ n1->size = global_id*10;
+ n1->next = NULL;
+ n1->previous = NULL;
+ kprint("ADDED: ");
+ char c[16];
+ hex_to_ascii( (u32 ) n1, c);
+ kprint( c);
+ kprint( " id: ");
+ hex_to_ascii( n1->id, c);
+ kprint( c);
+ } else if (strcmp(input, "ZOO") == 0) {
+ kprint("ZOO.\n");
+ }
+
+ kprint("You said: ");
+ kprint(input);
+ kprint("\n> ");
+}
diff --git a/Part4/10_scheduler/kernel/kernel.h b/Part4/10_scheduler/kernel/kernel.h
new file mode 100644
index 0000000..c31ef35
--- /dev/null
+++ b/Part4/10_scheduler/kernel/kernel.h
@@ -0,0 +1,12 @@
+#ifndef KERNEL_H
+#define KERNEL_H
+
+void user_input(char *input);
+
+#include "../cpu/isr.h"
+#include "../drivers/screen.h"
+#include "../libc/string.h"
+#include "../libc/mem.h"
+#include "../libc/globals.h"
+
+#endif
diff --git a/Part4/10_scheduler/libc/function.h b/Part4/10_scheduler/libc/function.h
new file mode 100644
index 0000000..bf656ed
--- /dev/null
+++ b/Part4/10_scheduler/libc/function.h
@@ -0,0 +1,8 @@
+#ifndef FUNCTION_H
+#define FUNCTION_H
+
+/* Sometimes we want to keep parameters to a function for later use
+ * and this is a solution to avoid the 'unused parameter' compiler warning */
+#define UNUSED(x) (void)(x)
+
+#endif
diff --git a/Part4/10_scheduler/libc/globals.c b/Part4/10_scheduler/libc/globals.c
new file mode 100644
index 0000000..d89540b
--- /dev/null
+++ b/Part4/10_scheduler/libc/globals.c
@@ -0,0 +1,5 @@
+#include "../cpu/types.h"
+#include "globals.h"
+u32 free_mem_addr;
+u32 global_id;
+u32 memory_limit;
diff --git a/Part4/10_scheduler/libc/globals.h b/Part4/10_scheduler/libc/globals.h
new file mode 100644
index 0000000..8599466
--- /dev/null
+++ b/Part4/10_scheduler/libc/globals.h
@@ -0,0 +1,9 @@
+#ifndef GLOBAL_H
+#define GLOBAL_H
+
+#include "../cpu/types.h"
+extern u32 free_mem_addr;
+extern u32 global_id;
+extern u32 memory_limit;
+
+#endif // GLOBAL_H
diff --git a/Part4/10_scheduler/libc/mem.c b/Part4/10_scheduler/libc/mem.c
new file mode 100644
index 0000000..68ffb59
--- /dev/null
+++ b/Part4/10_scheduler/libc/mem.c
@@ -0,0 +1,43 @@
+#include "mem.h"
+
+void memory_copy(u8 *source, u8 *dest, int nbytes) {
+ int i;
+ for (i = 0; i < nbytes; i++) {
+ *(dest + i) = *(source + i);
+ }
+}
+
+void memory_set(u8 *dest, u8 val, u32 len) {
+ u8 *temp = (u8 *)dest;
+ for ( ; len != 0; len--) *temp++ = val;
+}
+
+// See mem.h for free_mem_addr global declaration
+/* Implementation is just a pointer to some free memory which
+ * keeps growing */
+u32 kmalloc(u32 size, int align, u32 *phys_addr) {
+ /* Pages are aligned to 4K, or 0x1000 */
+ if (align == 1 && (free_mem_addr & 0xFFFFF000)) {
+ free_mem_addr &= 0xFFFFF000;
+ free_mem_addr += 0x1000;
+ }
+
+
+ char c[16];
+ hex_to_ascii( (u32) *phys_addr, c);
+ kprint("Inside kmalloc phys_addr = ");
+ kprint(c);
+ kprint("\n");
+
+ /* Save also the physical address */
+ *phys_addr = free_mem_addr;
+
+ hex_to_ascii( (u32) *phys_addr, c);
+ kprint("Inside kmalloc phys_addr = ");
+ kprint(c);
+ kprint("\n");
+
+ u32 ret = free_mem_addr;
+ free_mem_addr += size; /* Remember to increment the pointer */
+ return ret;
+}
diff --git a/Part4/10_scheduler/libc/mem.h b/Part4/10_scheduler/libc/mem.h
new file mode 100644
index 0000000..1d198d1
--- /dev/null
+++ b/Part4/10_scheduler/libc/mem.h
@@ -0,0 +1,22 @@
+#ifndef MEM_H
+#define MEM_H
+
+#include "../cpu/types.h"
+#include "../libc/string.h"
+#include "../drivers/screen.h"
+#include "globals.h"
+
+typedef struct _node {
+ u32 id;
+ u32 base;
+ u32 size;
+ struct _node *next;
+ struct _node *previous;
+} node;
+
+void memory_copy(u8 *source, u8 *dest, int nbytes);
+void memory_set(u8 *dest, u8 val, u32 len);
+
+u32 kmalloc(u32 size, int align, u32 *phys_addr);
+
+#endif
diff --git a/Part4/10_scheduler/libc/string.c b/Part4/10_scheduler/libc/string.c
new file mode 100644
index 0000000..0d3de2d
--- /dev/null
+++ b/Part4/10_scheduler/libc/string.c
@@ -0,0 +1,79 @@
+#include "string.h"
+
+/**
+ * K&R implementation
+ */
+void int_to_ascii(int n, char str[]) {
+ int i, sign;
+ if ((sign = n) < 0) n = -n;
+ i = 0;
+ do {
+ str[i++] = n % 10 + '0';
+ } while ((n /= 10) > 0);
+
+ if (sign < 0) str[i++] = '-';
+ str[i] = '\0';
+
+ reverse(str);
+}
+
+void hex_to_ascii(int n, char str[]) {
+ str[0] = 0;
+ append(str, '0');
+ append(str, 'x');
+ char zeros = 0;
+
+ s32 tmp;
+ int i;
+ for (i = 28; i > 0; i -= 4) {
+ tmp = (n >> i) & 0xF;
+ if (tmp == 0 && zeros == 0) continue;
+ zeros = 1;
+ if (tmp > 0x0A) append(str, tmp - 0x0a + 'a');
+ else append(str, tmp + '0');
+ }
+
+ tmp = n & 0xF;
+ if (tmp >= 0x0A) append(str, tmp - 0x0a + 'a');
+ else append(str, tmp + '0');
+
+ return;
+}
+
+/* K&R */
+void reverse(char s[]) {
+ int c, i, j;
+ for (i = 0, j = strlen(s)-1; i < j; i++, j--) {
+ c = s[i];
+ s[i] = s[j];
+ s[j] = c;
+ }
+}
+
+/* K&R */
+int strlen(char s[]) {
+ int i = 0;
+ while (s[i] != '\0') ++i;
+ return i;
+}
+
+void append(char s[], char n) {
+ int len = strlen(s);
+ s[len] = n;
+ s[len+1] = '\0';
+}
+
+void backspace(char s[]) {
+ int len = strlen(s);
+ s[len-1] = '\0';
+}
+
+/* K&R
+ * Returns <0 if s10 if s1>s2 */
+int strcmp(char s1[], char s2[]) {
+ int i;
+ for (i = 0; s1[i] == s2[i]; i++) {
+ if (s1[i] == '\0') return 0;
+ }
+ return s1[i] - s2[i];
+}
diff --git a/Part4/10_scheduler/libc/string.h b/Part4/10_scheduler/libc/string.h
new file mode 100644
index 0000000..db4200c
--- /dev/null
+++ b/Part4/10_scheduler/libc/string.h
@@ -0,0 +1,14 @@
+#ifndef STRINGS_H
+#define STRINGS_H
+
+#include "../cpu/types.h"
+
+void int_to_ascii(int n, char str[]);
+void hex_to_ascii(int n, char str[]);
+void reverse(char s[]);
+int strlen(char s[]);
+void backspace(char s[]);
+void append(char s[], char n);
+int strcmp(char s1[], char s2[]);
+
+#endif
diff --git a/Part4/10_scheduler/s_compile.sh b/Part4/10_scheduler/s_compile.sh
new file mode 100644
index 0000000..20ddf8b
--- /dev/null
+++ b/Part4/10_scheduler/s_compile.sh
@@ -0,0 +1,14 @@
+#!/bin/bash
+### interrupt.asm has no .c counterpart so it needs to be built first
+nasm ./cpu/interrupt.asm -f elf -o interrupt.o
+
+### Alternatively to see assembly output -S
+### Generate .s the assembly language output prior to generating .o machine files -S
+gcc -g -m32 -fno-pie -ffreestanding -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs -Wall -Wextra -Werror ./libc/mem.c ./libc/string.c ./drivers/keyboard.c ./drivers/screen.c ./cpu/isr.c ./cpu/ports.c ./cpu/idt.c ./cpu/timer.c ./libc/globals.c -S hello.c
+
+
+### Generate .o machine files but do not link -c
+### gcc -g -m32 -fno-pie -ffreestanding -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs -Wall -Wextra -Werror ./libc/mem.c ./libc/string.c ./drivers/keyboard.c ./drivers/screen.c ./cpu/isr.c ./cpu/ports.c ./cpu/idt.c ./cpu/timer.c ./libc/globals.c -c --entry start_ hello.c
+
+### Do the final link to create executable "final" and set entry to 0x10000
+### ld -m elf_i386 -o hey_there -Ttext 0x10000 *.o --oformat binary --entry start_
diff --git a/Part4/CS3502S01_Project_IV_Scheduler_Stub(1).docx b/Part4/CS3502S01_Project_IV_Scheduler_Stub(1).docx
new file mode 100644
index 0000000..7b327ef
Binary files /dev/null and b/Part4/CS3502S01_Project_IV_Scheduler_Stub(1).docx differ