├── LICENSE ├── README.md ├── kernelBabyStep ├── README.md ├── function.bin ├── function.c ├── function.o ├── kernel.c ├── pointers.bin ├── pointers.c └── pointers.o ├── life_of_a_C_Program ├── README.md ├── function.bin ├── function.c ├── kernel.c ├── pointers.bin └── pointers.c ├── pointers.c ├── pointers.o └── src ├── PresentationMaterial ├── WriteyourOwnOS.pdf ├── firstBootSector.asm ├── firstBootSector.bin └── test.c ├── README.md ├── backupJames ├── PresentationMaterial │ ├── firstBootSector.asm │ ├── firstBootSector.bin │ └── test.c ├── TextEditor │ ├── Makefile │ ├── README.md │ ├── boot │ │ ├── boot_sect.asm │ │ ├── boot_sect.bin │ │ ├── bootsect.bin │ │ ├── disk_load.asm │ │ ├── gdt.asm │ │ ├── kernel_entry.asm │ │ ├── kernel_entry.o │ │ ├── print_string.asm │ │ ├── print_string_pm.asm │ │ └── switch_to_pm.asm │ ├── cpu │ │ ├── idt.c │ │ ├── idt.h │ │ ├── idt.o │ │ ├── interrupt.asm │ │ ├── interrupt.o │ │ ├── isr.c │ │ ├── isr.h │ │ ├── isr.o │ │ ├── ports.c │ │ ├── ports.h │ │ ├── ports.o │ │ ├── timer.c │ │ ├── timer.h │ │ ├── timer.o │ │ └── types.h │ ├── drivers │ │ ├── keyboard.c │ │ ├── keyboard.h │ │ ├── keyboard.o │ │ ├── screen.c │ │ ├── screen.h │ │ └── screen.o │ ├── kernel.bin │ ├── kernel │ │ ├── kernel.c │ │ ├── kernel.h │ │ └── kernel.o │ ├── libc │ │ ├── function.h │ │ ├── mem.c │ │ ├── mem.h │ │ ├── mem.o │ │ ├── string.c │ │ ├── string.h │ │ └── string.o │ └── os-image.bin ├── boot_sector │ ├── boot_sect.asm │ ├── disk_load.asm │ ├── gdt.asm │ ├── print_string.asm │ ├── print_string_pm.asm │ └── switch_to_pm.asm ├── kernelVanilla │ ├── .README.md.swo │ ├── README.md │ ├── boot_sect.asm │ ├── boot_sect.bin │ ├── disk_load.asm │ ├── gdt.asm │ ├── kernel.bin │ ├── kernel.c │ ├── kernel.o │ ├── kernel_entry.asm │ ├── kernel_entry.o │ ├── os-image │ ├── print_string.asm │ ├── print_string_pm.asm │ └── switch_to_pm.asm ├── kernelVideoDrivers │ ├── Makefile │ ├── README.md │ ├── boot │ │ ├── 32bit_print.asm │ │ ├── bootsect.asm │ │ ├── bootsect.bin │ │ ├── disk.asm │ │ ├── gdt.asm │ │ ├── kernel_entry.asm │ │ ├── kernel_entry.o │ │ ├── print.asm │ │ ├── print_hex.asm │ │ └── switch_pm.asm │ ├── drivers │ │ ├── ports.c │ │ ├── ports.h │ │ ├── ports.o │ │ ├── screen.c │ │ ├── screen.h │ │ └── screen.o │ ├── kernel.bin │ ├── kernel │ │ ├── kernel.c │ │ └── kernel.o │ └── os-image.bin └── kernelVideoPorts │ ├── Makefile │ ├── boot │ ├── 32bit_print.asm │ ├── bootsect.asm │ ├── bootsect.bin │ ├── disk.asm │ ├── gdt.asm │ ├── kernel_entry.asm │ ├── kernel_entry.o │ ├── print.asm │ ├── print_hex.asm │ └── switch_pm.asm │ ├── drivers │ ├── ports.c │ ├── ports.h │ └── ports.o │ ├── kernel.bin │ ├── kernel │ ├── kernel.c │ └── kernel.o │ └── os-image.bin ├── bochsrc ├── boot_sect.asm ├── boot_sect.bin ├── boot_sector ├── .disk_load.asm.swp ├── boot_sect.asm ├── disk_load.asm ├── gdt.asm ├── print_string.asm ├── print_string_pm.asm └── switch_to_pm.asm ├── disk_load.asm ├── gdt.asm ├── kernel ├── .README.md.swo ├── README.md ├── boot_sect.asm ├── boot_sect.bin ├── disk_load.asm ├── gdt.asm ├── kernel.bin ├── kernel.c ├── kernel.o ├── kernel_entry.asm ├── kernel_entry.o ├── os-image ├── print_string.asm ├── print_string_pm.asm └── switch_to_pm.asm ├── kernelVanilla ├── .README.md.swo ├── README.md ├── boot_sect.asm ├── boot_sect.bin ├── disk_load.asm ├── gdt.asm ├── kernel.bin ├── kernel.c ├── kernel.o ├── kernel_entry.asm ├── kernel_entry.o ├── os-image ├── print_string.asm ├── print_string_pm.asm └── switch_to_pm.asm ├── kernelVideoDrivers ├── Makefile ├── README.md ├── boot │ ├── 32bit_print.asm │ ├── bootsect.asm │ ├── bootsect.bin │ ├── disk.asm │ ├── gdt.asm │ ├── kernel_entry.asm │ ├── kernel_entry.o │ ├── print.asm │ ├── print_hex.asm │ └── switch_pm.asm ├── drivers │ ├── ports.c │ ├── ports.h │ ├── ports.o │ ├── screen.c │ ├── screen.h │ └── screen.o ├── kernel.bin ├── kernel │ ├── kernel.c │ └── kernel.o └── os-image.bin ├── kernelVideoPorts ├── Makefile ├── boot │ ├── 32bit_print.asm │ ├── bootsect.asm │ ├── bootsect.bin │ ├── disk.asm │ ├── gdt.asm │ ├── kernel_entry.asm │ ├── kernel_entry.o │ ├── print.asm │ ├── print_hex.asm │ └── switch_pm.asm ├── drivers │ ├── ports.c │ ├── ports.h │ └── ports.o ├── kernel.bin ├── kernel │ ├── kernel.c │ └── kernel.o └── os-image.bin ├── print_string.asm ├── print_string_pm.asm ├── screenDrivers ├── README.md ├── boot_sect.asm ├── boot_sect.bin ├── disk_load.asm ├── gdt.asm ├── kernel.bin ├── kernel.c ├── kernel.o ├── kernel_entry.asm ├── kernel_entry.o ├── os-image.bin ├── ports.c ├── ports.h ├── ports.o ├── print_string.asm ├── print_string_pm.asm ├── screen.c ├── screen.h ├── screen.o └── switch_to_pm.asm ├── switch_to_pm.asm └── videoPorts ├── README.md ├── boot_sect.asm ├── boot_sect.bin ├── disk_load.asm ├── gdt.asm ├── kernel.bin ├── kernel.c ├── kernel.o ├── kernel_entry.asm ├── kernel_entry.o ├── os-image.bin ├── print_string.asm ├── print_string_pm.asm └── switch_to_pm.asm /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 GEEKSKOOL 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OS_from_Scratch 2 | --- 3 | The main objective of the project is to implement a bootable OS with the help of ["How to Write an Operating System from Scratch?"](http://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf) by Nick Blundell. 4 | 5 | The README is incomplete!!!! 6 | Please run the following from command line to get a demo of what it does : 7 | 8 | ```sh 9 | $ nasm boot_sect.asm -f bin -o boot_sect.bin 10 | $ sudo-apt get install qemu 11 | $ qemu-system-x86_64 boot_sect.bin 12 | ``` 13 | 14 | (Optional) If you wish to see the binary contents of the boot_sector: 15 | ```sh 16 | $ od -t x1 -A n boot_sect.bin 17 | ``` 18 | 19 | 20 | The boot_sector code(mostly in assembly language) is as follows : 21 | - boot_sect.asm : 22 | * The entry point to seize control of the Computer 23 | * The boot sector is implemented in protected mode. 24 | * The last two bytes of the 512-byte sector are set to the magic number 0xaa55, to indicate itself to BIOS 25 | 26 | - disk_load.asm : 27 | * Reading more data from the disk will be essential for boot-strapping our operating system, 28 | * Reading the first N sectors following the boot sector from a given disk 29 | * Reading from dh Sector of ch cylinder. 30 | 31 | - gdt.asm 32 | * The Global Descriptor Table 33 | 34 | - switch_to_pm.asm : 35 | * Switch off interrupts(CLI) 36 | * Load Global Descriptor Table(gdt) 37 | * Jump to Code Segment 38 | * Update Stack Pointer 39 | * Begin Protected Mode. 40 | 41 | - print_string.asm : printing a string in real mode 42 | 43 | - print_string_pm.asm : printing a string in protected mode 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /kernelBabyStep/README.md: -------------------------------------------------------------------------------- 1 | Writing a simple C program which contains a function to observe the kind of assembly code generated 2 | 3 | To compile system-independent code, we need the flag -ffreestanding, so compile function.c in this fashion; this produces an object file, function.o 4 | 5 | ```sh 6 | $ gcc -ffreestanding -c function.c -o function.o 7 | ``` 8 | 9 | 10 | To examine the machine code generated by the compiler: 11 | 12 | ```sh 13 | $ objdump -d function.o 14 | ``` 15 | We will use the linker inorder to produce the binary file, function.bin. An important part of this step is to learn how high level languages call function labels. Which is the offset where our function will be placed in memory? 16 | 17 | ```sh 18 | $ ld -o function.bin -Ttext 0x0 --oformat binary function.o 19 | ``` 20 | 21 | Finally, we use the diassembler to see what machine code has been generated : 22 | 23 | ```sh 24 | $ ndisasm -b 32 function.bin 25 | ``` 26 | 27 | -------------------------------------------------------------------------------- /kernelBabyStep/function.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/kernelBabyStep/function.bin -------------------------------------------------------------------------------- /kernelBabyStep/function.c: -------------------------------------------------------------------------------- 1 | int myfunction() 2 | { 3 | return 0xbaba; 4 | } 5 | 6 | -------------------------------------------------------------------------------- /kernelBabyStep/function.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/kernelBabyStep/function.o -------------------------------------------------------------------------------- /kernelBabyStep/kernel.c: -------------------------------------------------------------------------------- 1 | /* This will force us to create a kernel entry function instead of jumping to kernel.c:0x00 */ 2 | void dummy_test_entrypoint() { 3 | } 4 | 5 | void main() { 6 | char* video_memory = (char*) 0xb8000; 7 | *video_memory = 'X'; 8 | } 9 | -------------------------------------------------------------------------------- /kernelBabyStep/pointers.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/kernelBabyStep/pointers.bin -------------------------------------------------------------------------------- /kernelBabyStep/pointers.c: -------------------------------------------------------------------------------- 1 | void func() 2 | { 3 | char* string = "Hello"; 4 | } 5 | -------------------------------------------------------------------------------- /kernelBabyStep/pointers.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/kernelBabyStep/pointers.o -------------------------------------------------------------------------------- /life_of_a_C_Program/README.md: -------------------------------------------------------------------------------- 1 | Writing a simple C program which contains a function to observe the kind of assembly code generated 2 | 3 | To compile system-independent code, we need the flag -ffreestanding, so compile function.c in this fashion; this produces an object file, function.o 4 | 5 | ```sh 6 | $ gcc -ffreestanding -c function.c -o function.o 7 | ``` 8 | 9 | 10 | To examine the machine code generated by the compiler: 11 | 12 | ```sh 13 | $ objdump -d function.o 14 | ``` 15 | We will use the linker inorder to produce the binary file, function.bin. An important part of this step is to learn how high level languages call function labels. Which is the offset where our function will be placed in memory? 16 | 17 | ```sh 18 | $ ld -o function.bin -Ttext 0x0 --oformat binary function.o 19 | ``` 20 | 21 | Finally, we use the diassembler to see what machine code has been generated : 22 | 23 | ```sh 24 | $ ndisasm -b 32 function.bin 25 | ``` 26 | 27 | -------------------------------------------------------------------------------- /life_of_a_C_Program/function.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/life_of_a_C_Program/function.bin -------------------------------------------------------------------------------- /life_of_a_C_Program/function.c: -------------------------------------------------------------------------------- 1 | int myfunction() 2 | { 3 | return 0xbaba; 4 | } 5 | 6 | -------------------------------------------------------------------------------- /life_of_a_C_Program/kernel.c: -------------------------------------------------------------------------------- 1 | /* This will force us to create a kernel entry function instead of jumping to kernel.c:0x00 */ 2 | void dummy_test_entrypoint() { 3 | } 4 | 5 | void main() { 6 | char* video_memory = (char*) 0xb8000; 7 | *video_memory = 'X'; 8 | } 9 | -------------------------------------------------------------------------------- /life_of_a_C_Program/pointers.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/life_of_a_C_Program/pointers.bin -------------------------------------------------------------------------------- /life_of_a_C_Program/pointers.c: -------------------------------------------------------------------------------- 1 | void func() 2 | { 3 | char* string = "Hello"; 4 | } 5 | -------------------------------------------------------------------------------- /pointers.c: -------------------------------------------------------------------------------- 1 | void func() 2 | { 3 | char* string = "Hello"; 4 | } 5 | -------------------------------------------------------------------------------- /pointers.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/pointers.o -------------------------------------------------------------------------------- /src/PresentationMaterial/WriteyourOwnOS.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/PresentationMaterial/WriteyourOwnOS.pdf -------------------------------------------------------------------------------- /src/PresentationMaterial/firstBootSector.asm: -------------------------------------------------------------------------------- 1 | loop: 2 | jmp loop 3 | 4 | times 510-($-$$) db 0 5 | dw 0xaa55 6 | -------------------------------------------------------------------------------- /src/PresentationMaterial/firstBootSector.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/PresentationMaterial/firstBootSector.bin -------------------------------------------------------------------------------- /src/PresentationMaterial/test.c: -------------------------------------------------------------------------------- 1 | int my_function () 2 | { 3 | return 0 xbaba ; 4 | } 5 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | Continuing from where we had left, the boot_sector binary is created by the NASM Assembler(Note the -f bin format). 2 | 3 | ```sh 4 | $ nasm boot_sect.asm -f bin -o boot_sect.bin 5 | ``` 6 | In contrast, the option -f elf tells the assembler to output an object file of the particular format Executable and Linking Format (ELF), which is the default format output by out C compiler. 7 | 8 | ```sh 9 | $ nasm kernel_entry.asm -f elf -o kernel_entry.o 10 | ``` 11 | The kernel.c program can be compiled this to raw binary as follows: 12 | ```sh 13 | $ gcc -m32 -ffreestanding -c kernel.c -o kernel.o 14 | ``` 15 | ```sh 16 | $ i386-elf-ld -o kernel.bin -Ttext 0x0500 kernel_entry.o kernel.o --oformat binary 17 | ``` 18 | 19 | ```sh 20 | $ cat boot_sect.bin kernel.bin > os-image 21 | ``` 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/backupJames/PresentationMaterial/firstBootSector.asm: -------------------------------------------------------------------------------- 1 | loop: 2 | jmp loop 3 | 4 | times 510-($-$$) db 0 5 | dw 0xaa55 6 | -------------------------------------------------------------------------------- /src/backupJames/PresentationMaterial/firstBootSector.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/PresentationMaterial/firstBootSector.bin -------------------------------------------------------------------------------- /src/backupJames/PresentationMaterial/test.c: -------------------------------------------------------------------------------- 1 | int my_function () 2 | { 3 | return 0 xbaba ; 4 | } 5 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/Makefile: -------------------------------------------------------------------------------- 1 | C_SOURCES = $(wildcard kernel/*.c drivers/*.c cpu/*.c libc/*.c) 2 | HEADERS = $(wildcard kernel/*.h drivers/*.h cpu/*.h libc/*.h) 3 | # Nice syntax for file extension replacement 4 | OBJ = ${C_SOURCES:.c=.o cpu/interrupt.o} 5 | 6 | # Change this if your cross-compiler is somewhere else 7 | CC = gcc -m32 8 | GDB = /usr/local/i386elfgcc/bin/i386-elf-gdb 9 | # -g: Use debugging symbols in gcc 10 | CFLAGS = -g -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs \ 11 | -Wall -Wextra -Werror 12 | 13 | # First rule is run by default 14 | os-image.bin: boot/bootsect.bin kernel.bin 15 | cat $^ > os-image.bin 16 | 17 | # '--oformat binary' deletes all symbols as a collateral, so we don't need 18 | # to 'strip' them manually on this case 19 | kernel.bin: boot/kernel_entry.o ${OBJ} 20 | i386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary 21 | 22 | # Used for debugging purposes 23 | kernel.elf: boot/kernel_entry.o ${OBJ} 24 | i386-elf-ld -o $@ -Ttext 0x1000 $^ 25 | 26 | run: os-image.bin 27 | qemu-system-i386 -fda os-image.bin 28 | 29 | # Open the connection to qemu and load our kernel-object file with symbols 30 | debug: os-image.bin kernel.elf 31 | qemu-system-i386 -s -fda os-image.bin -d guest_errors,int & 32 | ${GDB} -ex "target remote localhost:1234" -ex "symbol-file kernel.elf" 33 | 34 | # Generic rules for wildcards 35 | # To make an object, always compile from its .c 36 | %.o: %.c ${HEADERS} 37 | ${CC} ${CFLAGS} -ffreestanding -c $< -o $@ 38 | 39 | %.o: %.asm 40 | nasm $< -f elf -o $@ 41 | 42 | %.bin: %.asm 43 | nasm $< -f bin -o $@ 44 | 45 | clean: 46 | rm -rf *.bin *.dis *.o os-image.bin *.elf 47 | rm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o cpu/*.o libc/*.o 48 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/README.md: -------------------------------------------------------------------------------- 1 | 2 | **Goal: Clean the code a bit and parse user input** 3 | 4 | In this lesson we will do tho things. First, we will clean up the code a bit, so it is ready 5 | for further lessons. During the previous ones I tried to put things in the most predictable places, 6 | but it is also a good exercise to know when the code base is growing and adapt it to current 7 | and further needs. 8 | 9 | 10 | Code cleaning 11 | ------------- 12 | 13 | First of all, we will quickly start to need more utility functions 14 | for handling strings and so on. In a regular OS, this is called the C library, 15 | or libc for short. 16 | 17 | Right now we have a `utils.c` which we will split into `mem.c` and `string.c`, with their respective headers. 18 | 19 | Second, we will create a new function `irq_install()` so that the kernel 20 | only needs to perform one call to initialize all the IRQs. That function 21 | is akin to `isr_install()` and placed on the same `irq.c`. 22 | While we're here, we will disable the `kprint()` on `timer_callback()` 23 | to avoid filling the screen with junk, now that we know that it works 24 | properly. 25 | 26 | There is not a clear distinction between `cpu/` and `drivers/`. 27 | Keep in mind that I'm 28 | creating this tutorial while following many others, and each of them 29 | has a distinct folder structure. The only change we will do for now is to 30 | move `drivers/ports.*` into `cpu/` since it is clearly cpu-dependent code. 31 | `boot/` is also CPU-dependent code, but we will not mess with it until 32 | we implement the boot sequence for a different machine. 33 | 34 | There are more switches for the `CFLAGS` on the `Makefile`, since we will now 35 | start creating higher-level functions for our C library and we don't want 36 | the compiler to include any external code if we make a mistake with a declaration. 37 | We also added some flags to turn warnings into errors, since an apparantly minor mistake 38 | converting pointers can blow up later on. This also forced us to modify some misc pointer 39 | declarations in our code. 40 | 41 | Finally, we'll add a macro to avoid warning-errors on unused parameters on `libc/function.h` 42 | 43 | Keyboard characters 44 | ------------------- 45 | 46 | How to access the typed characters, then? 47 | 48 | - When a key is pressed, the callback gets the ASCII code via a new 49 | arrays which are defined at the beginning of `keyboard.c` 50 | - The callback then appends that character to a buffer, `key_buffer` 51 | - It is also printed on the screen 52 | - When the OS wants to read user input, it calls `libc/io.c:readline()` 53 | 54 | `keyboard.c` also parses backspace, by removing the last element 55 | of the key buffer, and deleting it from the screen, by calling 56 | `screen.c:kprint_backspace()`. For this we needed to modify a bit 57 | `print_char()` to not advance the offset when printing a backspace 58 | 59 | 60 | Responding to user input 61 | ------------------------ 62 | 63 | The keyboard callback checks for a newline, and then calls the kernel, 64 | telling it that the user has input something. Out final libc function 65 | is `strcmp()`, which compares two strings and returns 0 if they 66 | are equal. If the user inputs "END", we halt the CPU. 67 | 68 | This is the most basic shell ever, but you should be proud, because 69 | we implemented it from scratch. Do you realize how cool this is? 70 | 71 | If you want to, expand `kernel.c` to parse more stuff. In the future, 72 | when we have a filesystem, we will allow the user to run some basic commands. 73 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/boot/boot_sect.asm: -------------------------------------------------------------------------------- 1 | ; boot sector that enters 32 - bit protected mode. 2 | [org 0x7c00] 3 | KERNEL_OFFSET equ 0x1000 4 | 5 | mov [BOOT_DRIVE], dl ; BIOS stores our boot drive in DL , so it 's 6 | ; best to remember this for later. 7 | 8 | mov bp, 0x9000 9 | mov sp, bp 10 | 11 | mov bx, MSG_REAL_MODE ;to print a message on screen, store message in BX Register 12 | call print_string 13 | 14 | call load_kernel 15 | 16 | call switch_to_pm 17 | jmp $ ; jump forever(jump to address of current instruction) 18 | 19 | %include "print_string.asm" 20 | %include "disk_load.asm" 21 | %include "print_string_pm.asm" 22 | %include "gdt.asm" 23 | %include "switch_to_pm.asm" 24 | 25 | [ bits 16] 26 | load_kernel: 27 | mov bx, MSG_LOAD_KERNEL ; using BX as a parameter of our function 28 | call print_string ; by specifying the address of a string 29 | 30 | mov bx, KERNEL_OFFSET 31 | mov dh, 30 32 | mov dl, [BOOT_DRIVE] 33 | ; with dx having been loaded with number of sectors to read, we will call disk_load 34 | call disk_load 35 | ret 36 | 37 | [bits 32] 38 | BEGIN_PM: 39 | mov ebx, MSG_PROT_MODE 40 | call print_string_pm 41 | 42 | call KERNEL_OFFSET 43 | 44 | jmp $ 45 | 46 | BOOT_DRIVE: 47 | db 0 48 | 49 | MSG_REAL_MODE: 50 | db " Hello GeekSkool,Started in 16- bit Real Mode ", 0 51 | 52 | MSG_PROT_MODE: 53 | db " Successfully landed in 32- bit Protected Mode ", 0 54 | 55 | MSG_LOAD_KERNEL: 56 | db " Loading kernel into memory. ", 0 57 | 58 | times 510-($-$$) db 0 ; Pad the boot sector out with zeros 59 | ; When compiled , our program must fit into 512 bytes , 60 | ; with the last two bytes being the magic number , 61 | ; so here , tell our assembly compiler to pad out our 62 | ; program with enough zero bytes (db 0) to bring us to the 63 | ; 510 th byte. 64 | dw 0xaa55 ; Last two bytes form the magic number 65 | ; BIOS is informed that we are a boot sector 66 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/boot/boot_sect.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/TextEditor/boot/boot_sect.bin -------------------------------------------------------------------------------- /src/backupJames/TextEditor/boot/bootsect.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/TextEditor/boot/bootsect.bin -------------------------------------------------------------------------------- /src/backupJames/TextEditor/boot/disk_load.asm: -------------------------------------------------------------------------------- 1 | disk_load: 2 | push dx ; Store DX on stack so later we can recall 3 | ; how many sectors were request to be read! 4 | ;SECTOR 5 | mov ah, 0x02 ; BIOS read sector function 0x02 = READ 6 | mov al, dh ; READ number sectors ; number specified by dh 7 | ;CYLINDER 8 | mov ch, 0x00 ; select cylinder 0 9 | ;HEAD 10 | mov dh, 0x00 ; select head 0 11 | 12 | mov cl, 0x02 ; start reading from 2nd sector (after boot sector) 13 | 14 | int 0x13 ; BIOS interrupt 15 | jc disk_error 16 | 17 | pop dx ; pop dx from the stack 18 | cmp dh, al 19 | jne disk_error ; generate disk error if AL(sectors read) != DH(Expected number of sectors) 20 | 21 | ret 22 | disk_error: ; disk_error label 23 | mov bx, [DISK_ERROR_MSG] ; push the error message 24 | call print_string ; print_string is the helper to print string stored in [BX] 25 | jmp $ ; jump forever 26 | 27 | DISK_ERROR_MSG: 28 | db 'Disk read Error!', 0 29 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/boot/gdt.asm: -------------------------------------------------------------------------------- 1 | ; GDT tabel 2 | gdt_start: 3 | 4 | ;NULL segment 5 | gdt_null: 6 | dd 0x0 7 | dd 0x0 8 | ;define two segments, one for code and another for data 9 | ;there is no memory protection as these segments can overlap 10 | 11 | gdt_code: 12 | ; base =0x0 , limit =0 xfffff , 13 | ; 1st flags : ( present )1 ( privilege )00 ( descriptor type )1 -> 1001 b 14 | ; type flags : ( code )1 ( conforming )0 ( readable )1 ( accessed )0 -> 1010 b 15 | ; 2nd flags : ( granularity )1 (32 - bit default )1 (64 - bit seg )0 ( AVL )0 -> 1100 b 16 | dw 0xffff ; Limit ( bits 0 -15) 17 | dw 0x0 ; Base ( bits 0 -15) 18 | db 0x0 ; Base ( bits 16 -23) 19 | db 10011010b ; 1st flags , type flags 20 | db 11001111b ; 2nd flags , Limit ( bits 16 -19) 21 | db 0x0 ; Base ( bits 24 -31) 22 | 23 | gdt_data: 24 | ; Same as code segment except for the type flags : 25 | ; type flags : ( code )0 ( expand down )0 ( writable )1 ( accessed )0 -> 0010 b 26 | dw 0xffff ; Limit ( bits 0 -15) 27 | dw 0x0 ; Base ( bits 0 -15) 28 | db 0x0 ; Base ( bits 16 -23) 29 | db 10010010b ; 1st flags , type flags 30 | db 11001111b ; 2nd flags , Limit ( bits 16 -19) 31 | db 0x0 ; Base ( bits 24 -31) 32 | 33 | gdt_end: ; The reason for putting a label at the end of the 34 | ; GDT is so we can have the assembler calculate 35 | ; the size of the GDT for the GDT decriptor ( below ) 36 | 37 | gdt_descriptor: 38 | dw gdt_end - gdt_start - 1 ; Size of our GDT , always less one 39 | ; of the true size 40 | dd gdt_start ; Start address of our GDT 41 | 42 | ; Define some handy constants for the GDT segment descriptor offsets , which 43 | ; are what segment registers must contain when in protected mode. For example , 44 | ; when we set DS = 0 x10 in PM , the CPU knows that we mean it to use the 45 | ; segment described at offset 0 x10 ( i.e. 16 bytes ) in our GDT , which in our 46 | ; case is the DATA segment (0 x0 -> NULL ; 0x08 -> CODE ; 0 x10 -> DATA ) 47 | CODE_SEG equ gdt_code - gdt_start 48 | DATA_SEG equ gdt_data - gdt_start 49 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/boot/kernel_entry.asm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | [extern main] ; Define calling point. Must have same name as kernel.c 'main' function 3 | call main ; Calls the C function. The linker will know where it is placed in memory 4 | jmp $ 5 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/boot/kernel_entry.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/TextEditor/boot/kernel_entry.o -------------------------------------------------------------------------------- /src/backupJames/TextEditor/boot/print_string.asm: -------------------------------------------------------------------------------- 1 | print_string: 2 | mov ah, 0x0e ;indicating scrolling teletype mode of the OS 3 | 4 | loop: 5 | mov al, [bx] ;Remember [bx] stored the message. 6 | cmp al, 0 7 | je out 8 | int 0x10 ;invoking the screen-related Interrupt Service Routine 9 | ;An ISR is simply a sequence of machine instructions, much like our boot sector code, that deals with a 10 | ;specific interrupt (e.g. perhaps to read new data from a disk drive or from a network card). 11 | add bx, 0x01 12 | jmp loop 13 | 14 | out: 15 | mov al, ' ' 16 | int 0x10 17 | ret 18 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/boot/print_string_pm.asm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | 3 | VIDEO_MEMORY equ 0xb8000 4 | WHITE_ON_BLACK equ 0x0f 5 | 6 | ; data in ebx register 7 | print_string_pm: 8 | pusha 9 | mov edx, VIDEO_MEMORY 10 | 11 | print_string_pm_loop: 12 | mov al, [ebx] 13 | mov ah, WHITE_ON_BLACK 14 | 15 | cmp al, 0 16 | je done 17 | 18 | mov [edx], ax 19 | 20 | add edx, 2 21 | add ebx, 1 22 | jmp print_string_pm_loop 23 | 24 | done: 25 | popa 26 | ret 27 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/boot/switch_to_pm.asm: -------------------------------------------------------------------------------- 1 | [bits 16] 2 | 3 | switch_to_pm: 4 | cli ; We must switch off interrupts until we have 5 | ; set -up the protected mode interrupt vector 6 | ; otherwise interrupts will run riot. 7 | 8 | lgdt [gdt_descriptor] 9 | 10 | mov eax, cr0 ; see we are using 32 register (eax) here in 16 bit, we can do that 11 | or eax, 0x1 ; set the first bit of CR0, a control register 12 | mov cr0, eax 13 | 14 | jmp CODE_SEG:init_pm ;Make a far jump ( i.e. to a new segment ) to our 32 - bit 15 | ;code. 16 | 17 | [bits 32] 18 | init_pm: 19 | mov ax, DATA_SEG ; Now we are in protected mode , our old segments are meaningless , 20 | mov ds, ax ; so we point our segment registers to the 21 | mov ss, ax ; data selector we defined in our GDT 22 | mov es, ax 23 | mov fs, ax 24 | mov gs, ax 25 | 26 | mov ebp, 0x90000 ; Updating stack position so that it is right at the top of free space. 27 | mov esp, ebp ; create stack frame(MOV DST, SRC : Stackpointer <----- Basepointer) 28 | 29 | call BEGIN_PM 30 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/cpu/idt.c: -------------------------------------------------------------------------------- 1 | #include "idt.h" 2 | 3 | void set_idt_gate(int n, u32 handler) { 4 | idt[n].low_offset = low_16(handler); 5 | idt[n].sel = KERNEL_CS; 6 | idt[n].always0 = 0; 7 | idt[n].flags = 0x8E; 8 | idt[n].high_offset = high_16(handler); 9 | } 10 | 11 | void set_idt() { 12 | idt_reg.base = (u32) &idt; 13 | idt_reg.limit = IDT_ENTRIES * sizeof(idt_gate_t) - 1; 14 | /* Don't make the mistake of loading &idt -- always load &idt_reg */ 15 | __asm__ __volatile__("lidtl (%0)" : : "r" (&idt_reg)); 16 | } 17 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/cpu/idt.h: -------------------------------------------------------------------------------- 1 | #ifndef IDT_H 2 | #define IDT_H 3 | 4 | #include "types.h" 5 | 6 | /* Segment selectors */ 7 | #define KERNEL_CS 0x08 8 | 9 | /* How every interrupt gate (handler) is defined */ 10 | typedef struct { 11 | u16 low_offset; /* Lower 16 bits of handler function address */ 12 | u16 sel; /* Kernel segment selector */ 13 | u8 always0; 14 | /* First byte 15 | * Bit 7: "Interrupt is present" 16 | * Bits 6-5: Privilege level of caller (0=kernel..3=user) 17 | * Bit 4: Set to 0 for interrupt gates 18 | * Bits 3-0: bits 1110 = decimal 14 = "32 bit interrupt gate" */ 19 | u8 flags; 20 | u16 high_offset; /* Higher 16 bits of handler function address */ 21 | } __attribute__((packed)) idt_gate_t ; 22 | 23 | /* A pointer to the array of interrupt handlers. 24 | * Assembly instruction 'lidt' will read it */ 25 | typedef struct { 26 | u16 limit; 27 | u32 base; 28 | } __attribute__((packed)) idt_register_t; 29 | 30 | #define IDT_ENTRIES 256 31 | idt_gate_t idt[IDT_ENTRIES]; 32 | idt_register_t idt_reg; 33 | 34 | 35 | /* Functions implemented in idt.c */ 36 | void set_idt_gate(int n, u32 handler); 37 | void set_idt(); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/cpu/idt.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/TextEditor/cpu/idt.o -------------------------------------------------------------------------------- /src/backupJames/TextEditor/cpu/interrupt.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/TextEditor/cpu/interrupt.o -------------------------------------------------------------------------------- /src/backupJames/TextEditor/cpu/isr.c: -------------------------------------------------------------------------------- 1 | #include "isr.h" 2 | #include "idt.h" 3 | #include "../drivers/screen.h" 4 | #include "../drivers/keyboard.h" 5 | #include "../libc/string.h" 6 | #include "timer.h" 7 | #include "ports.h" 8 | 9 | isr_t interrupt_handlers[256]; 10 | 11 | /* Can't do this with a loop because we need the address 12 | * of the function names */ 13 | void isr_install() { 14 | set_idt_gate(0, (u32)isr0); 15 | set_idt_gate(1, (u32)isr1); 16 | set_idt_gate(2, (u32)isr2); 17 | set_idt_gate(3, (u32)isr3); 18 | set_idt_gate(4, (u32)isr4); 19 | set_idt_gate(5, (u32)isr5); 20 | set_idt_gate(6, (u32)isr6); 21 | set_idt_gate(7, (u32)isr7); 22 | set_idt_gate(8, (u32)isr8); 23 | set_idt_gate(9, (u32)isr9); 24 | set_idt_gate(10, (u32)isr10); 25 | set_idt_gate(11, (u32)isr11); 26 | set_idt_gate(12, (u32)isr12); 27 | set_idt_gate(13, (u32)isr13); 28 | set_idt_gate(14, (u32)isr14); 29 | set_idt_gate(15, (u32)isr15); 30 | set_idt_gate(16, (u32)isr16); 31 | set_idt_gate(17, (u32)isr17); 32 | set_idt_gate(18, (u32)isr18); 33 | set_idt_gate(19, (u32)isr19); 34 | set_idt_gate(20, (u32)isr20); 35 | set_idt_gate(21, (u32)isr21); 36 | set_idt_gate(22, (u32)isr22); 37 | set_idt_gate(23, (u32)isr23); 38 | set_idt_gate(24, (u32)isr24); 39 | set_idt_gate(25, (u32)isr25); 40 | set_idt_gate(26, (u32)isr26); 41 | set_idt_gate(27, (u32)isr27); 42 | set_idt_gate(28, (u32)isr28); 43 | set_idt_gate(29, (u32)isr29); 44 | set_idt_gate(30, (u32)isr30); 45 | set_idt_gate(31, (u32)isr31); 46 | 47 | // Remap the PIC 48 | port_byte_out(0x20, 0x11); 49 | port_byte_out(0xA0, 0x11); 50 | port_byte_out(0x21, 0x20); 51 | port_byte_out(0xA1, 0x28); 52 | port_byte_out(0x21, 0x04); 53 | port_byte_out(0xA1, 0x02); 54 | port_byte_out(0x21, 0x01); 55 | port_byte_out(0xA1, 0x01); 56 | port_byte_out(0x21, 0x0); 57 | port_byte_out(0xA1, 0x0); 58 | 59 | // Install the IRQs 60 | set_idt_gate(32, (u32)irq0); 61 | set_idt_gate(33, (u32)irq1); 62 | set_idt_gate(34, (u32)irq2); 63 | set_idt_gate(35, (u32)irq3); 64 | set_idt_gate(36, (u32)irq4); 65 | set_idt_gate(37, (u32)irq5); 66 | set_idt_gate(38, (u32)irq6); 67 | set_idt_gate(39, (u32)irq7); 68 | set_idt_gate(40, (u32)irq8); 69 | set_idt_gate(41, (u32)irq9); 70 | set_idt_gate(42, (u32)irq10); 71 | set_idt_gate(43, (u32)irq11); 72 | set_idt_gate(44, (u32)irq12); 73 | set_idt_gate(45, (u32)irq13); 74 | set_idt_gate(46, (u32)irq14); 75 | set_idt_gate(47, (u32)irq15); 76 | 77 | set_idt(); // Load with ASM 78 | } 79 | 80 | /* To print the message which defines every exception */ 81 | char *exception_messages[] = { 82 | "Division By Zero", 83 | "Debug", 84 | "Non Maskable Interrupt", 85 | "Breakpoint", 86 | "Into Detected Overflow", 87 | "Out of Bounds", 88 | "Invalid Opcode", 89 | "No Coprocessor", 90 | 91 | "Double Fault", 92 | "Coprocessor Segment Overrun", 93 | "Bad TSS", 94 | "Segment Not Present", 95 | "Stack Fault", 96 | "General Protection Fault", 97 | "Page Fault", 98 | "Unknown Interrupt", 99 | 100 | "Coprocessor Fault", 101 | "Alignment Check", 102 | "Machine Check", 103 | "Reserved", 104 | "Reserved", 105 | "Reserved", 106 | "Reserved", 107 | "Reserved", 108 | 109 | "Reserved", 110 | "Reserved", 111 | "Reserved", 112 | "Reserved", 113 | "Reserved", 114 | "Reserved", 115 | "Reserved", 116 | "Reserved" 117 | }; 118 | 119 | void isr_handler(registers_t r) { 120 | display("received interrupt: "); 121 | char s[3]; 122 | int_to_ascii(r.int_no, s); 123 | display(s); 124 | display("\n"); 125 | display(exception_messages[r.int_no]); 126 | display("\n"); 127 | } 128 | 129 | void register_interrupt_handler(u8 n, isr_t handler) { 130 | interrupt_handlers[n] = handler; 131 | } 132 | 133 | void irq_handler(registers_t r) { 134 | /* After every interrupt we need to send an EOI to the PICs 135 | * or they will not send another interrupt again */ 136 | if (r.int_no >= 40) port_byte_out(0xA0, 0x20); /* slave */ 137 | port_byte_out(0x20, 0x20); /* master */ 138 | 139 | /* Handle the interrupt in a more modular way */ 140 | if (interrupt_handlers[r.int_no] != 0) { 141 | isr_t handler = interrupt_handlers[r.int_no]; 142 | handler(r); 143 | } 144 | } 145 | 146 | void irq_install() { 147 | /* Enable interruptions */ 148 | asm volatile("sti"); 149 | /* IRQ0: timer */ 150 | init_timer(50); 151 | /* IRQ1: keyboard */ 152 | init_keyboard(); 153 | } 154 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/cpu/isr.h: -------------------------------------------------------------------------------- 1 | #ifndef ISR_H 2 | #define ISR_H 3 | 4 | #include "types.h" 5 | 6 | /* ISRs reserved for CPU exceptions */ 7 | extern void isr0(); 8 | extern void isr1(); 9 | extern void isr2(); 10 | extern void isr3(); 11 | extern void isr4(); 12 | extern void isr5(); 13 | extern void isr6(); 14 | extern void isr7(); 15 | extern void isr8(); 16 | extern void isr9(); 17 | extern void isr10(); 18 | extern void isr11(); 19 | extern void isr12(); 20 | extern void isr13(); 21 | extern void isr14(); 22 | extern void isr15(); 23 | extern void isr16(); 24 | extern void isr17(); 25 | extern void isr18(); 26 | extern void isr19(); 27 | extern void isr20(); 28 | extern void isr21(); 29 | extern void isr22(); 30 | extern void isr23(); 31 | extern void isr24(); 32 | extern void isr25(); 33 | extern void isr26(); 34 | extern void isr27(); 35 | extern void isr28(); 36 | extern void isr29(); 37 | extern void isr30(); 38 | extern void isr31(); 39 | /* IRQ definitions */ 40 | extern void irq0(); 41 | extern void irq1(); 42 | extern void irq2(); 43 | extern void irq3(); 44 | extern void irq4(); 45 | extern void irq5(); 46 | extern void irq6(); 47 | extern void irq7(); 48 | extern void irq8(); 49 | extern void irq9(); 50 | extern void irq10(); 51 | extern void irq11(); 52 | extern void irq12(); 53 | extern void irq13(); 54 | extern void irq14(); 55 | extern void irq15(); 56 | 57 | #define IRQ0 32 58 | #define IRQ1 33 59 | #define IRQ2 34 60 | #define IRQ3 35 61 | #define IRQ4 36 62 | #define IRQ5 37 63 | #define IRQ6 38 64 | #define IRQ7 39 65 | #define IRQ8 40 66 | #define IRQ9 41 67 | #define IRQ10 42 68 | #define IRQ11 43 69 | #define IRQ12 44 70 | #define IRQ13 45 71 | #define IRQ14 46 72 | #define IRQ15 47 73 | 74 | /* Struct which aggregates many registers */ 75 | typedef struct { 76 | u32 ds; /* Data segment selector */ 77 | u32 edi, esi, ebp, esp, ebx, edx, ecx, eax; /* Pushed by pusha. */ 78 | u32 int_no, err_code; /* Interrupt number and error code (if applicable) */ 79 | u32 eip, cs, eflags, useresp, ss; /* Pushed by the processor automatically */ 80 | } registers_t; 81 | 82 | void isr_install(); 83 | void isr_handler(registers_t r); 84 | void irq_install(); 85 | 86 | typedef void (*isr_t)(registers_t); 87 | void register_interrupt_handler(u8 n, isr_t handler); 88 | 89 | #endif 90 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/cpu/isr.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/TextEditor/cpu/isr.o -------------------------------------------------------------------------------- /src/backupJames/TextEditor/cpu/ports.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Read a byte from the specified port 3 | */ 4 | unsigned char port_byte_in (unsigned short port) { 5 | unsigned char result; 6 | /* Inline assembler syntax 7 | * !! Notice how the source and destination registers are switched from NASM !! 8 | * 9 | * '"=a" (result)'; set '=' the C variable '(result)' to the value of register e'a'x 10 | * '"d" (port)': map the C variable '(port)' into e'd'x register 11 | * 12 | * Inputs and outputs are separated by colons 13 | */ 14 | __asm__("in %%dx, %%al" : "=a" (result) : "d" (port)); 15 | return result; 16 | } 17 | 18 | void port_byte_out (unsigned short port, unsigned char data) { 19 | /* Notice how here both registers are mapped to C variables and 20 | * nothing is returned, thus, no equals '=' in the asm syntax 21 | * However we see a comma since there are two variables in the input area 22 | * and none in the 'return' area 23 | */ 24 | __asm__("out %%al, %%dx" : : "a" (data), "d" (port)); 25 | } 26 | 27 | unsigned short port_word_in (unsigned short port) { 28 | unsigned short result; 29 | __asm__("in %%dx, %%ax" : "=a" (result) : "d" (port)); 30 | return result; 31 | } 32 | 33 | void port_word_out (unsigned short port, unsigned short data) { 34 | __asm__("out %%ax, %%dx" : : "a" (data), "d" (port)); 35 | } 36 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/cpu/ports.h: -------------------------------------------------------------------------------- 1 | unsigned char port_byte_in (unsigned short port); 2 | void port_byte_out (unsigned short port, unsigned char data); 3 | unsigned short port_word_in (unsigned short port); 4 | void port_word_out (unsigned short port, unsigned short data); 5 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/cpu/ports.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/TextEditor/cpu/ports.o -------------------------------------------------------------------------------- /src/backupJames/TextEditor/cpu/timer.c: -------------------------------------------------------------------------------- 1 | #include "timer.h" 2 | #include "isr.h" 3 | #include "ports.h" 4 | #include "../libc/function.h" 5 | 6 | u32 tick = 0; 7 | 8 | static void timer_callback(registers_t regs) { 9 | tick++; 10 | UNUSED(regs); 11 | } 12 | 13 | void init_timer(u32 freq) { 14 | /* Install the function we just wrote */ 15 | register_interrupt_handler(IRQ0, timer_callback); 16 | 17 | /* Get the PIT value: hardware clock at 1193180 Hz */ 18 | u32 divisor = 1193180 / freq; 19 | u8 low = (u8)(divisor & 0xFF); 20 | u8 high = (u8)( (divisor >> 8) & 0xFF); 21 | /* Send the command */ 22 | port_byte_out(0x43, 0x36); /* Command port */ 23 | port_byte_out(0x40, low); 24 | port_byte_out(0x40, high); 25 | } 26 | 27 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/cpu/timer.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMER_H 2 | #define TIMER_H 3 | 4 | #include "types.h" 5 | 6 | void init_timer(u32 freq); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/cpu/timer.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/TextEditor/cpu/timer.o -------------------------------------------------------------------------------- /src/backupJames/TextEditor/cpu/types.h: -------------------------------------------------------------------------------- 1 | #ifndef TYPES_H 2 | #define TYPES_H 3 | 4 | /* Instead of using 'chars' to allocate non-character bytes, 5 | * we will use these new type with no semantic meaning */ 6 | typedef unsigned int u32; 7 | typedef int s32; 8 | typedef unsigned short u16; 9 | typedef short s16; 10 | typedef unsigned char u8; 11 | typedef char s8; 12 | 13 | #define low_16(address) (u16)((address) & 0xFFFF) 14 | #define high_16(address) (u16)(((address) >> 16) & 0xFFFF) 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/drivers/keyboard.c: -------------------------------------------------------------------------------- 1 | #include "keyboard.h" 2 | #include "../cpu/ports.h" 3 | #include "../cpu/isr.h" 4 | #include "screen.h" 5 | #include "../libc/string.h" 6 | #include "../libc/function.h" 7 | #include "../kernel/kernel.h" 8 | 9 | #define BACKSPACE 0x0E 10 | #define ENTER 0x1C 11 | 12 | static char key_buffer[256]; 13 | 14 | #define SC_MAX 57 15 | const char *sc_name[] = { "ERROR", "Esc", "1", "2", "3", "4", "5", "6", 16 | "7", "8", "9", "0", "-", "=", "Backspace", "Tab", "Q", "W", "E", 17 | "R", "T", "Y", "U", "I", "O", "P", "[", "]", "Enter", "Lctrl", 18 | "A", "S", "D", "F", "G", "H", "J", "K", "L", ";", "'", "`", 19 | "LShift", "\\", "Z", "X", "C", "V", "B", "N", "M", ",", ".", 20 | "/", "RShift", "Keypad *", "LAlt", "Spacebar"}; 21 | const char sc_ascii[] = { '?', '?', '1', '2', '3', '4', '5', '6', 22 | '7', '8', '9', '0', '-', '=', '?', '?', 'Q', 'W', 'E', 'R', 'T', 'Y', 23 | 'U', 'I', 'O', 'P', '[', ']', '?', '?', 'A', 'S', 'D', 'F', 'G', 24 | 'H', 'J', 'K', 'L', ';', '\'', '`', '?', '\\', 'Z', 'X', 'C', 'V', 25 | 'B', 'N', 'M', ',', '.', '/', '?', '?', '?', ' '}; 26 | 27 | static void keyboard_callback(registers_t regs) { 28 | /* The PIC leaves us the scancode in port 0x60 */ 29 | u8 scancode = port_byte_in(0x60); 30 | 31 | if (scancode > SC_MAX) return; 32 | if (scancode == BACKSPACE) { 33 | backspace(key_buffer); 34 | display_backspace(); 35 | } else if (scancode == ENTER) { 36 | display("\n"); 37 | user_input(key_buffer); /* kernel-controlled function */ 38 | key_buffer[0] = '\0'; 39 | } else { 40 | char letter = sc_ascii[(int)scancode]; 41 | /* Remember that display only accepts char[] */ 42 | char str[2] = {letter, '\0'}; 43 | append(key_buffer, letter); 44 | display(str); 45 | } 46 | UNUSED(regs); 47 | } 48 | 49 | void init_keyboard() { 50 | register_interrupt_handler(IRQ1, keyboard_callback); 51 | } 52 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/drivers/keyboard.h: -------------------------------------------------------------------------------- 1 | #include "../cpu/types.h" 2 | 3 | void init_keyboard(); 4 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/drivers/keyboard.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/TextEditor/drivers/keyboard.o -------------------------------------------------------------------------------- /src/backupJames/TextEditor/drivers/screen.c: -------------------------------------------------------------------------------- 1 | #include "screen.h" 2 | #include "../cpu/ports.h" 3 | #include "../libc/mem.h" 4 | 5 | /* Declaration of private functions */ 6 | int get_cursor_offset(); 7 | void set_cursor_offset(int offset); 8 | int print_char(char c, int col, int row, char attr); 9 | int get_offset(int col, int row); 10 | int get_offset_row(int offset); 11 | int get_offset_col(int offset); 12 | 13 | /********************************************************** 14 | * Public Kernel API functions * 15 | **********************************************************/ 16 | 17 | /** 18 | * Print a message on the specified location 19 | * If col, row, are negative, we will use the current offset 20 | */ 21 | void display_at(char *message, int col, int row) { 22 | /* Set cursor if col/row are negative */ 23 | int offset; 24 | if (col >= 0 && row >= 0) 25 | offset = get_offset(col, row); 26 | else { 27 | offset = get_cursor_offset(); 28 | row = get_offset_row(offset); 29 | col = get_offset_col(offset); 30 | } 31 | 32 | /* Loop through message and print it */ 33 | int i = 0; 34 | while (message[i] != 0) { 35 | offset = print_char(message[i++], col, row, WHITE_ON_BLACK); 36 | /* Compute row/col for next iteration */ 37 | row = get_offset_row(offset); 38 | col = get_offset_col(offset); 39 | } 40 | } 41 | 42 | void display(char *message) { 43 | display_at(message, -1, -1); 44 | } 45 | 46 | void display_backspace() { 47 | int offset = get_cursor_offset()-2; 48 | int row = get_offset_row(offset); 49 | int col = get_offset_col(offset); 50 | print_char(0x08, col, row, WHITE_ON_BLACK); 51 | } 52 | 53 | 54 | /********************************************************** 55 | * Private kernel functions * 56 | **********************************************************/ 57 | 58 | 59 | /** 60 | * Innermost print function for our kernel, directly accesses the video memory 61 | * 62 | * If 'col' and 'row' are negative, we will print at current cursor location 63 | * If 'attr' is zero it will use 'white on black' as default 64 | * Returns the offset of the next character 65 | * Sets the video cursor to the returned offset 66 | */ 67 | int print_char(char c, int col, int row, char attr) { 68 | u8 *vidmem = (u8*) VIDEO_ADDRESS; 69 | if (!attr) attr = WHITE_ON_BLACK; 70 | 71 | /* Error control: print a red 'E' if the coords aren't right */ 72 | if (col >= MAX_COLS || row >= MAX_ROWS) { 73 | vidmem[2*(MAX_COLS)*(MAX_ROWS)-2] = 'E'; 74 | vidmem[2*(MAX_COLS)*(MAX_ROWS)-1] = RED_ON_WHITE; 75 | return get_offset(col, row); 76 | } 77 | 78 | int offset; 79 | if (col >= 0 && row >= 0) offset = get_offset(col, row); 80 | else offset = get_cursor_offset(); 81 | 82 | if (c == '\n') { 83 | row = get_offset_row(offset); 84 | offset = get_offset(0, row+1); 85 | } else if (c == 0x08) { /* Backspace */ 86 | vidmem[offset] = ' '; 87 | vidmem[offset+1] = attr; 88 | } else { 89 | vidmem[offset] = c; 90 | vidmem[offset+1] = attr; 91 | offset += 2; 92 | } 93 | 94 | /* Check if the offset is over screen size and scroll */ 95 | if (offset >= MAX_ROWS * MAX_COLS * 2) { 96 | int i; 97 | for (i = 1; i < MAX_ROWS; i++) 98 | memory_copy((u8*)(get_offset(0, i) + VIDEO_ADDRESS), 99 | (u8*)(get_offset(0, i-1) + VIDEO_ADDRESS), 100 | MAX_COLS * 2); 101 | 102 | /* Blank last line */ 103 | char *last_line = (char*) (get_offset(0, MAX_ROWS-1) + (u8*) VIDEO_ADDRESS); 104 | for (i = 0; i < MAX_COLS * 2; i++) last_line[i] = 0; 105 | 106 | offset -= 2 * MAX_COLS; 107 | } 108 | 109 | set_cursor_offset(offset); 110 | return offset; 111 | } 112 | 113 | int get_cursor_offset() { 114 | /* Use the VGA ports to get the current cursor position 115 | * 1. Ask for high byte of the cursor offset (data 14) 116 | * 2. Ask for low byte (data 15) 117 | */ 118 | port_byte_out(REG_SCREEN_CTRL, 14); 119 | int offset = port_byte_in(REG_SCREEN_DATA) << 8; /* High byte: << 8 */ 120 | port_byte_out(REG_SCREEN_CTRL, 15); 121 | offset += port_byte_in(REG_SCREEN_DATA); 122 | return offset * 2; /* Position * size of character cell */ 123 | } 124 | 125 | void set_cursor_offset(int offset) { 126 | /* Similar to get_cursor_offset, but instead of reading we write data */ 127 | offset /= 2; 128 | port_byte_out(REG_SCREEN_CTRL, 14); 129 | port_byte_out(REG_SCREEN_DATA, (u8)(offset >> 8)); 130 | port_byte_out(REG_SCREEN_CTRL, 15); 131 | port_byte_out(REG_SCREEN_DATA, (u8)(offset & 0xff)); 132 | } 133 | 134 | void clear_screen() { 135 | int screen_size = MAX_COLS * MAX_ROWS; 136 | int i; 137 | u8 *screen = (u8*) VIDEO_ADDRESS; 138 | 139 | for (i = 0; i < screen_size; i++) { 140 | screen[i*2] = ' '; 141 | screen[i*2+1] = WHITE_ON_BLACK; 142 | } 143 | set_cursor_offset(get_offset(0, 0)); 144 | } 145 | 146 | 147 | int get_offset(int col, int row) { return 2 * (row * MAX_COLS + col); } 148 | int get_offset_row(int offset) { return offset / (2 * MAX_COLS); } 149 | int get_offset_col(int offset) { return (offset - (get_offset_row(offset)*2*MAX_COLS))/2; } 150 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/drivers/screen.h: -------------------------------------------------------------------------------- 1 | #ifndef SCREEN_H 2 | #define SCREEN_H 3 | 4 | #include "../cpu/types.h" 5 | 6 | #define VIDEO_ADDRESS 0xb8000 7 | #define MAX_ROWS 25 8 | #define MAX_COLS 80 9 | #define WHITE_ON_BLACK 0x0f 10 | #define RED_ON_WHITE 0xf4 11 | 12 | /* Screen i/o ports */ 13 | #define REG_SCREEN_CTRL 0x3d4 14 | #define REG_SCREEN_DATA 0x3d5 15 | 16 | /* Public kernel API */ 17 | void clear_screen(); 18 | void display_at(char *message, int col, int row); 19 | void display(char *message); 20 | void display_backspace(); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/drivers/screen.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/TextEditor/drivers/screen.o -------------------------------------------------------------------------------- /src/backupJames/TextEditor/kernel.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/TextEditor/kernel.bin -------------------------------------------------------------------------------- /src/backupJames/TextEditor/kernel/kernel.c: -------------------------------------------------------------------------------- 1 | #include "../cpu/isr.h" 2 | #include "../drivers/screen.h" 3 | #include "kernel.h" 4 | #include "../libc/string.h" 5 | 6 | void main() { 7 | isr_install(); 8 | irq_install(); 9 | 10 | display("Text Editor. Type END to halt the CPU\n "); 11 | } 12 | 13 | void user_input(char *input) { 14 | if (strcmp(input, "END") == 0) { 15 | display("Stopping the CPU. Bye!\n"); 16 | asm volatile("hlt"); 17 | } 18 | display("You said: "); 19 | display(input); 20 | display("\n> "); 21 | } 22 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/kernel/kernel.h: -------------------------------------------------------------------------------- 1 | #ifndef KERNEL_H 2 | #define KERNEL_H 3 | 4 | void user_input(char *input); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/kernel/kernel.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/TextEditor/kernel/kernel.o -------------------------------------------------------------------------------- /src/backupJames/TextEditor/libc/function.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTION_H 2 | #define FUNCTION_H 3 | 4 | /* Sometimes we want to keep parameters to a function for later use 5 | * and this is a solution to avoid the 'unused parameter' compiler warning */ 6 | #define UNUSED(x) (void)(x) 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/libc/mem.c: -------------------------------------------------------------------------------- 1 | #include "mem.h" 2 | 3 | void memory_copy(u8 *source, u8 *dest, int nbytes) { 4 | int i; 5 | for (i = 0; i < nbytes; i++) { 6 | *(dest + i) = *(source + i); 7 | } 8 | } 9 | 10 | void memory_set(u8 *dest, u8 val, u32 len) { 11 | u8 *temp = (u8 *)dest; 12 | for ( ; len != 0; len--) *temp++ = val; 13 | } 14 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/libc/mem.h: -------------------------------------------------------------------------------- 1 | #ifndef MEM_H 2 | #define MEM_H 3 | 4 | #include "../cpu/types.h" 5 | 6 | void memory_copy(u8 *source, u8 *dest, int nbytes); 7 | void memory_set(u8 *dest, u8 val, u32 len); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/libc/mem.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/TextEditor/libc/mem.o -------------------------------------------------------------------------------- /src/backupJames/TextEditor/libc/string.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | 3 | /** 4 | * K&R implementation 5 | */ 6 | void int_to_ascii(int n, char str[]) { 7 | int i, sign; 8 | if ((sign = n) < 0) n = -n; 9 | i = 0; 10 | do { 11 | str[i++] = n % 10 + '0'; 12 | } while ((n /= 10) > 0); 13 | 14 | if (sign < 0) str[i++] = '-'; 15 | str[i] = '\0'; 16 | 17 | reverse(str); 18 | } 19 | 20 | /* K&R */ 21 | void reverse(char s[]) { 22 | int c, i, j; 23 | for (i = 0, j = strlen(s)-1; i < j; i++, j--) { 24 | c = s[i]; 25 | s[i] = s[j]; 26 | s[j] = c; 27 | } 28 | } 29 | 30 | /* K&R */ 31 | int strlen(char s[]) { 32 | int i = 0; 33 | while (s[i] != '\0') ++i; 34 | return i; 35 | } 36 | 37 | void append(char s[], char n) { 38 | int len = strlen(s); 39 | s[len] = n; 40 | s[len+1] = '\0'; 41 | } 42 | 43 | void backspace(char s[]) { 44 | int len = strlen(s); 45 | s[len-1] = '\0'; 46 | } 47 | 48 | /* K&R 49 | * Returns <0 if s10 if s1>s2 */ 50 | int strcmp(char s1[], char s2[]) { 51 | int i; 52 | for (i = 0; s1[i] == s2[i]; i++) { 53 | if (s1[i] == '\0') return 0; 54 | } 55 | return s1[i] - s2[i]; 56 | } 57 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/libc/string.h: -------------------------------------------------------------------------------- 1 | #ifndef STRINGS_H 2 | #define STRINGS_H 3 | 4 | void int_to_ascii(int n, char str[]); 5 | void reverse(char s[]); 6 | int strlen(char s[]); 7 | void backspace(char s[]); 8 | void append(char s[], char n); 9 | int strcmp(char s1[], char s2[]); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /src/backupJames/TextEditor/libc/string.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/TextEditor/libc/string.o -------------------------------------------------------------------------------- /src/backupJames/TextEditor/os-image.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/TextEditor/os-image.bin -------------------------------------------------------------------------------- /src/backupJames/boot_sector/boot_sect.asm: -------------------------------------------------------------------------------- 1 | ; boot sector that enters 32 - bit protected mode. 2 | [org 0x7c00] 3 | KERNEL_OFFSET equ 0x1000 4 | 5 | mov [BOOT_DRIVE], dl ; BIOS stores our boot drive in DL , so it 's 6 | ; best to remember this for later. 7 | 8 | mov bp, 0x9000 9 | mov sp, bp 10 | 11 | mov bx, MSG_REAL_MODE ;to print a message on screen, store message in BX Register 12 | call print_string 13 | 14 | call load_kernel 15 | 16 | call switch_to_pm 17 | jmp $ ; jump forever(jump to address of current instruction) 18 | 19 | %include "print_string.asm" 20 | %include "disk_load.asm" 21 | %include "print_string_pm.asm" 22 | %include "gdt.asm" 23 | %include "switch_to_pm.asm" 24 | 25 | [ bits 16] 26 | load_kernel: 27 | mov bx, MSG_LOAD_KERNEL ; using BX as a parameter of our function 28 | call print_string ; by specifying the address of a string 29 | 30 | mov bx, KERNEL_OFFSET 31 | mov dh, 30 32 | mov dl, [BOOT_DRIVE] 33 | ; with dx having been loaded with number of sectors to read, we will call disk_load 34 | call disk_load 35 | ret 36 | 37 | [bits 32] 38 | BEGIN_PM: 39 | mov ebx, MSG_PROT_MODE 40 | call print_string_pm 41 | 42 | call KERNEL_OFFSET 43 | 44 | jmp $ 45 | 46 | BOOT_DRIVE: 47 | db 0 48 | 49 | MSG_REAL_MODE: 50 | db " Hello GeekSkool,Started in 16- bit Real Mode ", 0 51 | 52 | MSG_PROT_MODE: 53 | db " Successfully landed in 32- bit Protected Mode ", 0 54 | 55 | MSG_LOAD_KERNEL: 56 | db " Loading kernel into memory. ", 0 57 | 58 | times 510-($-$$) db 0 ; Pad the boot sector out with zeros 59 | ; When compiled , our program must fit into 512 bytes , 60 | ; with the last two bytes being the magic number , 61 | ; so here , tell our assembly compiler to pad out our 62 | ; program with enough zero bytes (db 0) to bring us to the 63 | ; 510 th byte. 64 | dw 0xaa55 ; Last two bytes form the magic number 65 | ; BIOS is informed that we are a boot sector 66 | -------------------------------------------------------------------------------- /src/backupJames/boot_sector/disk_load.asm: -------------------------------------------------------------------------------- 1 | disk_load: 2 | push dx ; Store DX on stack so later we can recall 3 | ; how many sectors were request to be read! 4 | ;SECTOR 5 | mov ah, 0x02 ; BIOS read sector function 0x02 = READ 6 | mov al, dh ; READ number sectors ; number specified by dh 7 | ;CYLINDER 8 | mov ch, 0x00 ; select cylinder 0 9 | ;HEAD 10 | mov dh, 0x00 ; select head 0 11 | 12 | mov cl, 0x02 ; start reading from 2nd sector (after boot sector) 13 | 14 | int 0x13 ; BIOS interrupt 15 | jc disk_error 16 | 17 | pop dx ; pop dx from the stack 18 | cmp dh, al 19 | jne disk_error ; generate disk error if AL(sectors read) != DH(Expected number of sectors) 20 | 21 | ret 22 | disk_error: ; disk_error label 23 | mov bx, [DISK_ERROR_MSG] ; push the error message 24 | call print_string ; print_string is the helper to print string stored in [BX] 25 | jmp $ ; jump forever 26 | 27 | DISK_ERROR_MSG: 28 | db 'Disk read Error!', 0 29 | -------------------------------------------------------------------------------- /src/backupJames/boot_sector/gdt.asm: -------------------------------------------------------------------------------- 1 | ; GDT tabel 2 | gdt_start: 3 | 4 | ;NULL segment 5 | gdt_null: 6 | dd 0x0 7 | dd 0x0 8 | ;define two segments, one for code and another for data 9 | ;there is no memory protection as these segments can overlap 10 | 11 | gdt_code: 12 | ; base =0x0 , limit =0 xfffff , 13 | ; 1st flags : ( present )1 ( privilege )00 ( descriptor type )1 -> 1001 b 14 | ; type flags : ( code )1 ( conforming )0 ( readable )1 ( accessed )0 -> 1010 b 15 | ; 2nd flags : ( granularity )1 (32 - bit default )1 (64 - bit seg )0 ( AVL )0 -> 1100 b 16 | dw 0xffff ; Limit ( bits 0 -15) 17 | dw 0x0 ; Base ( bits 0 -15) 18 | db 0x0 ; Base ( bits 16 -23) 19 | db 10011010b ; 1st flags , type flags 20 | db 11001111b ; 2nd flags , Limit ( bits 16 -19) 21 | db 0x0 ; Base ( bits 24 -31) 22 | 23 | gdt_data: 24 | ; Same as code segment except for the type flags : 25 | ; type flags : ( code )0 ( expand down )0 ( writable )1 ( accessed )0 -> 0010 b 26 | dw 0xffff ; Limit ( bits 0 -15) 27 | dw 0x0 ; Base ( bits 0 -15) 28 | db 0x0 ; Base ( bits 16 -23) 29 | db 10010010b ; 1st flags , type flags 30 | db 11001111b ; 2nd flags , Limit ( bits 16 -19) 31 | db 0x0 ; Base ( bits 24 -31) 32 | 33 | gdt_end: ; The reason for putting a label at the end of the 34 | ; GDT is so we can have the assembler calculate 35 | ; the size of the GDT for the GDT decriptor ( below ) 36 | 37 | gdt_descriptor: 38 | dw gdt_end - gdt_start - 1 ; Size of our GDT , always less one 39 | ; of the true size 40 | dd gdt_start ; Start address of our GDT 41 | 42 | ; Define some handy constants for the GDT segment descriptor offsets , which 43 | ; are what segment registers must contain when in protected mode. For example , 44 | ; when we set DS = 0 x10 in PM , the CPU knows that we mean it to use the 45 | ; segment described at offset 0 x10 ( i.e. 16 bytes ) in our GDT , which in our 46 | ; case is the DATA segment (0 x0 -> NULL ; 0x08 -> CODE ; 0 x10 -> DATA ) 47 | CODE_SEG equ gdt_code - gdt_start 48 | DATA_SEG equ gdt_data - gdt_start 49 | -------------------------------------------------------------------------------- /src/backupJames/boot_sector/print_string.asm: -------------------------------------------------------------------------------- 1 | print_string: 2 | mov ah, 0x0e ;indicating scrolling teletype mode of the OS 3 | 4 | loop: 5 | mov al, [bx] ;Remember [bx] stored the message. 6 | cmp al, 0 7 | je out 8 | int 0x10 ;invoking the screen-related Interrupt Service Routine 9 | ;An ISR is simply a sequence of machine instructions, much like our boot sector code, that deals with a 10 | ;specific interrupt (e.g. perhaps to read new data from a disk drive or from a network card). 11 | add bx, 0x01 12 | jmp loop 13 | 14 | out: 15 | mov al, ' ' 16 | int 0x10 17 | ret 18 | -------------------------------------------------------------------------------- /src/backupJames/boot_sector/print_string_pm.asm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | 3 | VIDEO_MEMORY equ 0xb8000 4 | WHITE_ON_BLACK equ 0x0f 5 | 6 | ; data in ebx register 7 | print_string_pm: 8 | pusha 9 | mov edx, VIDEO_MEMORY 10 | 11 | print_string_pm_loop: 12 | mov al, [ebx] 13 | mov ah, WHITE_ON_BLACK 14 | 15 | cmp al, 0 16 | je done 17 | 18 | mov [edx], ax 19 | 20 | add edx, 2 21 | add ebx, 1 22 | jmp print_string_pm_loop 23 | 24 | done: 25 | popa 26 | ret 27 | -------------------------------------------------------------------------------- /src/backupJames/boot_sector/switch_to_pm.asm: -------------------------------------------------------------------------------- 1 | [bits 16] 2 | 3 | switch_to_pm: 4 | cli ; We must switch off interrupts until we have 5 | ; set -up the protected mode interrupt vector 6 | ; otherwise interrupts will run riot. 7 | 8 | lgdt [gdt_descriptor] 9 | 10 | mov eax, cr0 ; see we are using 32 register (eax) here in 16 bit, we can do that 11 | or eax, 0x1 ; set the first bit of CR0, a control register 12 | mov cr0, eax 13 | 14 | jmp CODE_SEG:init_pm ;Make a far jump ( i.e. to a new segment ) to our 32 - bit 15 | ;code. 16 | 17 | [bits 32] 18 | init_pm: 19 | mov ax, DATA_SEG ; Now we are in protected mode , our old segments are meaningless , 20 | mov ds, ax ; so we point our segment registers to the 21 | mov ss, ax ; data selector we defined in our GDT 22 | mov es, ax 23 | mov fs, ax 24 | mov gs, ax 25 | 26 | mov ebp, 0x90000 ; Updating stack position so that it is right at the top of free space. 27 | mov esp, ebp ; create stack frame(MOV DST, SRC : Stackpointer <----- Basepointer) 28 | 29 | call BEGIN_PM 30 | -------------------------------------------------------------------------------- /src/backupJames/kernelVanilla/.README.md.swo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/kernelVanilla/.README.md.swo -------------------------------------------------------------------------------- /src/backupJames/kernelVanilla/README.md: -------------------------------------------------------------------------------- 1 | ```sh 2 | $ nasm boot_sect.asm -f bin -o boot_sect.bin 3 | ``` 4 | 5 | ```sh 6 | $ nasm kernel_entry.asm -f elf -o kernel_entry.o 7 | ``` 8 | 9 | ```sh 10 | $ gcc -m32 -ffreestanding -c kernel.c -o kernel.o 11 | ``` 12 | 13 | ```sh 14 | $ i386-elf-ld -o kernel.bin -Ttext 0x0500 kernel_entry.o kernel.o --oformat binary 15 | ``` 16 | 17 | ```sh 18 | $ cat boot_sect.bin kernel.bin > os-image 19 | ``` 20 | 21 | ```sh 22 | $ qemu-system-i386 -fda os-image 23 | -------------------------------------------------------------------------------- /src/backupJames/kernelVanilla/boot_sect.asm: -------------------------------------------------------------------------------- 1 | ; boot sector that enters 32 - bit protected mode. 2 | [org 0x7c00] 3 | KERNEL_OFFSET equ 0x1000 4 | 5 | mov [BOOT_DRIVE], dl ; BIOS stores our boot drive in DL , so it 's 6 | ; best to remember this for later. 7 | 8 | mov bp, 0x9000 9 | mov sp, bp 10 | 11 | mov bx, MSG_REAL_MODE 12 | call print_string 13 | 14 | call load_kernel 15 | 16 | call switch_to_pm 17 | jmp $ ; jump forever(jump to address of current instruction) 18 | 19 | %include "print_string.asm" 20 | %include "disk_load.asm" 21 | %include "print_string_pm.asm" 22 | %include "gdt.asm" 23 | %include "switch_to_pm.asm" 24 | 25 | [ bits 16] 26 | load_kernel: 27 | mov bx, MSG_LOAD_KERNEL ; using BX as a parameter of our function 28 | call print_string ; by specifying the address of a string 29 | 30 | mov bx, KERNEL_OFFSET 31 | mov dh, 30 32 | mov dl, [BOOT_DRIVE] 33 | ; with dx having been loaded with number of sectors to read, we will call disk_load 34 | call disk_load 35 | ret 36 | 37 | [bits 32] 38 | BEGIN_PM: 39 | mov ebx, MSG_PROT_MODE 40 | call print_string_pm 41 | 42 | call KERNEL_OFFSET 43 | 44 | jmp $ 45 | 46 | BOOT_DRIVE: 47 | db 0 48 | 49 | MSG_REAL_MODE: 50 | db " Hello GeekSkool,Started in 16- bit Real Mode ", 0 51 | 52 | MSG_PROT_MODE: 53 | db " Successfully landed in 32- bit Protected Mode ", 0 54 | 55 | MSG_LOAD_KERNEL: 56 | db " Loading kernel into memory. ", 0 57 | 58 | times 510-($-$$) db 0 ; Pad the boot sector out with zeros 59 | ; When compiled , our program must fit into 512 bytes , 60 | ; with the last two bytes being the magic number , 61 | ; so here , tell our assembly compiler to pad out our 62 | ; program with enough zero bytes (db 0) to bring us to the 63 | ; 510 th byte. 64 | dw 0xaa55 ; Last two bytes form the magic number 65 | ; BIOS is informed that we are a boot sector 66 | -------------------------------------------------------------------------------- /src/backupJames/kernelVanilla/boot_sect.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/kernelVanilla/boot_sect.bin -------------------------------------------------------------------------------- /src/backupJames/kernelVanilla/disk_load.asm: -------------------------------------------------------------------------------- 1 | disk_load: 2 | push dx ; Store DX on stack so later we can recall 3 | ; how many sectors were request to be read! 4 | 5 | mov ah, 0x02 ; BIOS read sector function 6 | mov al, dh ; READ dh sectors 7 | mov ch, 0x00 ; select cylinder 0 8 | mov dh, 0x00 ; select head 0 9 | 10 | mov cl, 0x02 ; start reading from 2nd sector (after boot sector) 11 | 12 | int 0x13 ; BIOS interrupt 13 | jc disk_error 14 | 15 | pop dx ; pop dx from the stack 16 | cmp dh, al 17 | jne disk_error ; generate disk error if AL(sectors read) != DH(Expected number of sectors) 18 | 19 | ret 20 | disk_error: ; disk_error label 21 | mov bx, [DISK_ERROR_MSG] ; push the error message 22 | call print_string ; print_string is the helper to print string stored in [BX] 23 | jmp $ ; jump forever 24 | 25 | DISK_ERROR_MSG: 26 | db 'Disk read Error!', 0 27 | -------------------------------------------------------------------------------- /src/backupJames/kernelVanilla/gdt.asm: -------------------------------------------------------------------------------- 1 | ; GDT tabel 2 | gdt_start: 3 | 4 | ;NULL segment 5 | gdt_null: 6 | dd 0x0 7 | dd 0x0 8 | ;define two segments, one for code and another for data 9 | ;there is no memory protection as these segments can overlap 10 | 11 | gdt_code: 12 | ; base =0x0 , limit =0 xfffff , 13 | ; 1st flags : ( present )1 ( privilege )00 ( descriptor type )1 -> 1001 b 14 | ; type flags : ( code )1 ( conforming )0 ( readable )1 ( accessed )0 -> 1010 b 15 | ; 2nd flags : ( granularity )1 (32 - bit default )1 (64 - bit seg )0 ( AVL )0 -> 1100 b 16 | dw 0xffff ; Limit ( bits 0 -15) 17 | dw 0x0 ; Base ( bits 0 -15) 18 | db 0x0 ; Base ( bits 16 -23) 19 | db 10011010b ; 1st flags , type flags 20 | db 11001111b ; 2nd flags , Limit ( bits 16 -19) 21 | db 0x0 ; Base ( bits 24 -31) 22 | 23 | gdt_data: 24 | ; Same as code segment except for the type flags : 25 | ; type flags : ( code )0 ( expand down )0 ( writable )1 ( accessed )0 -> 0010 b 26 | dw 0xffff ; Limit ( bits 0 -15) 27 | dw 0x0 ; Base ( bits 0 -15) 28 | db 0x0 ; Base ( bits 16 -23) 29 | db 10010010b ; 1st flags , type flags 30 | db 11001111b ; 2nd flags , Limit ( bits 16 -19) 31 | db 0x0 ; Base ( bits 24 -31) 32 | 33 | gdt_end: ; The reason for putting a label at the end of the 34 | ; GDT is so we can have the assembler calculate 35 | ; the size of the GDT for the GDT decriptor ( below ) 36 | 37 | gdt_descriptor: 38 | dw gdt_end - gdt_start - 1 ; Size of our GDT , always less one 39 | ; of the true size 40 | dd gdt_start ; Start address of our GDT 41 | 42 | ; Define some handy constants for the GDT segment descriptor offsets , which 43 | ; are what segment registers must contain when in protected mode. For example , 44 | ; when we set DS = 0 x10 in PM , the CPU knows that we mean it to use the 45 | ; segment described at offset 0 x10 ( i.e. 16 bytes ) in our GDT , which in our 46 | ; case is the DATA segment (0 x0 -> NULL ; 0x08 -> CODE ; 0 x10 -> DATA ) 47 | CODE_SEG equ gdt_code - gdt_start 48 | DATA_SEG equ gdt_data - gdt_start 49 | -------------------------------------------------------------------------------- /src/backupJames/kernelVanilla/kernel.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/kernelVanilla/kernel.bin -------------------------------------------------------------------------------- /src/backupJames/kernelVanilla/kernel.c: -------------------------------------------------------------------------------- 1 | /* This will force us to create a kernel entry function instead of jumping to kernel.c:0x00 */ 2 | void dummy_test_entrypoint() 3 | { 4 | 5 | } 6 | 7 | void main() { 8 | char* video_memory = (char*) 0xb8000; 9 | *video_memory = 'X'; 10 | } 11 | -------------------------------------------------------------------------------- /src/backupJames/kernelVanilla/kernel.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/kernelVanilla/kernel.o -------------------------------------------------------------------------------- /src/backupJames/kernelVanilla/kernel_entry.asm: -------------------------------------------------------------------------------- 1 | ;The linker takes object files as inputs, then joins them together, 2 | ;but resolves any labels to their correct addresses. 3 | 4 | [bits 32] 5 | [extern main] ; Define calling point. Must have same name as kernel.c 'main' function 6 | call main ; Calls the C function. The linker will know where it is placed in memory 7 | jmp $ 8 | -------------------------------------------------------------------------------- /src/backupJames/kernelVanilla/kernel_entry.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/kernelVanilla/kernel_entry.o -------------------------------------------------------------------------------- /src/backupJames/kernelVanilla/os-image: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/kernelVanilla/os-image -------------------------------------------------------------------------------- /src/backupJames/kernelVanilla/print_string.asm: -------------------------------------------------------------------------------- 1 | print_string: 2 | mov ah, 0x0e 3 | 4 | loop: 5 | mov al, [bx] ; 6 | cmp al, 0 7 | je out 8 | int 0x10 ; invoking the screen-related Interrupt Service Routine 9 | ;An ISR is simply a sequence of machine instructions, much like our boot sector code, that deals with a 10 | ;specific interrupt (e.g. perhaps to read new data from a disk drive or from a network card). 11 | add bx, 0x01 12 | jmp loop 13 | 14 | out: 15 | mov al, ' ' 16 | int 0x10 17 | ret 18 | -------------------------------------------------------------------------------- /src/backupJames/kernelVanilla/print_string_pm.asm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | 3 | VIDEO_MEMORY equ 0xb8000 4 | WHITE_ON_BLACK equ 0x0f 5 | 6 | ; data in ebx register 7 | print_string_pm: 8 | pusha 9 | mov edx, VIDEO_MEMORY 10 | 11 | print_string_pm_loop: 12 | mov al, [ebx] 13 | mov ah, WHITE_ON_BLACK 14 | 15 | cmp al, 0 16 | je done 17 | 18 | mov [edx], ax 19 | 20 | add edx, 2 21 | add ebx, 1 22 | jmp print_string_pm_loop 23 | 24 | done: 25 | popa 26 | ret 27 | -------------------------------------------------------------------------------- /src/backupJames/kernelVanilla/switch_to_pm.asm: -------------------------------------------------------------------------------- 1 | [bits 16] 2 | 3 | switch_to_pm: 4 | cli ; We must switch off interrupts until we have 5 | ; set -up the protected mode interrupt vector 6 | ; otherwise interrupts will run riot. 7 | 8 | lgdt [gdt_descriptor] 9 | 10 | mov eax, cr0 ; see we are using 32 register (eax) here in 16 bit, we can do that 11 | or eax, 0x1 ; set the first bit of CR0, a control register 12 | mov cr0, eax 13 | 14 | jmp CODE_SEG:init_pm ;Make a far jump ( i.e. to a new segment ) to our 32 - bit 15 | ;code. 16 | 17 | [bits 32] 18 | init_pm: 19 | mov ax, DATA_SEG ; Now we are in protected mode , our old segments are meaningless , 20 | mov ds, ax ; so we point our segment registers to the 21 | mov ss, ax ; data selector we defined in our GDT 22 | mov es, ax 23 | mov fs, ax 24 | mov gs, ax 25 | 26 | mov ebp, 0x90000 ; Updating stack position so that it is right at the top of free space. 27 | mov esp, ebp ; create stack frame(MOV DST, SRC : Stackpointer <----- Basepointer) 28 | 29 | call BEGIN_PM 30 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoDrivers/Makefile: -------------------------------------------------------------------------------- 1 | C_SOURCES = $(wildcard kernel/*.c drivers/*.c) 2 | HEADERS = $(wildcard kernel/*.h drivers/*.h) 3 | # Nice syntax for file extension replacement 4 | OBJ = ${C_SOURCES:.c=.o} 5 | 6 | # Change this if your cross-compiler is somewhere else 7 | CC = gcc -m32 8 | #GDB = /usr/local/i386elfgcc/bin/i386-elf-gdb 9 | # -g: Use debugging symbols in gcc 10 | CFLAGS = -g 11 | 12 | # First rule is run by default 13 | os-image.bin: boot/bootsect.bin kernel.bin 14 | cat $^ > os-image.bin 15 | 16 | # '--oformat binary' deletes all symbols as a collateral, so we don't need 17 | # to 'strip' them manually on this case 18 | kernel.bin: boot/kernel_entry.o ${OBJ} 19 | i386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary 20 | 21 | # Used for debugging purposes 22 | kernel.elf: boot/kernel_entry.o ${OBJ} 23 | i386-elf-ld -o $@ -Ttext 0x1000 $^ 24 | 25 | run: os-image.bin 26 | qemu-system-i386 -fda os-image.bin 27 | 28 | # Open the connection to qemu and load our kernel-object file with symbols 29 | debug: os-image.bin kernel.elf 30 | qemu-system-i386 -s -fda os-image.bin & 31 | # ${GDB} -ex "target remote localhost:1234" -ex "symbol-file kernel.elf" 32 | 33 | # Generic rules for wildcards 34 | # To make an object, always compile from its .c 35 | %.o: %.c ${HEADERS} 36 | ${CC} ${CFLAGS} -ffreestanding -c $< -o $@ 37 | 38 | %.o: %.asm 39 | nasm $< -f elf -o $@ 40 | 41 | %.bin: %.asm 42 | nasm $< -f bin -o $@ 43 | 44 | clean: 45 | rm -rf *.bin *.dis *.o os-image.bin *.elf 46 | rm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o 47 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoDrivers/README.md: -------------------------------------------------------------------------------- 1 | *Concepts you may want to Google beforehand: VGA character cells, screen offset* 2 | 3 | **Goal: Write strings on the screen** 4 | 5 | Finally, we are going to be able to output text on the screen. This lesson contains 6 | a bit more code than usual, so let's go step by step. 7 | 8 | Open `drivers/screen.h` and you'll see that we have defined some constants for the VGA 9 | card driver and three public functions, one to clear the screen and another couple 10 | to write strings, the famously named `kprint` for "kernel print" 11 | 12 | Now open `drivers/screen.c`. It starts with the declaration of private helper functions 13 | that we will use to aid our `kprint` kernel API. 14 | 15 | There are the two I/O port access routines that we learned in the previous lesson, 16 | `get` and `set_cursor_offset()`. 17 | 18 | Then there is the routine that directly manipulates the video memory, `print_char()` 19 | 20 | Finally, there are three small helper functions to transform rows and columns into offsets 21 | and vice versa. 22 | 23 | 24 | kprint_at 25 | --------- 26 | 27 | `kprint_at` may be called with a `-1` value for `col` and `row`, which indicates that 28 | we will print the string at the current cursor position. 29 | 30 | It first sets three variables for the col/row and the offset. Then it iterates through 31 | the `char*` and calls `print_char()` with the current coordinates. 32 | 33 | Note that `print_char` itself returns the offset of the next cursor position, and we reuse 34 | it for the next loop. 35 | 36 | `kprint` is basically a wrapper for `kprint_at` 37 | 38 | 39 | 40 | print_char 41 | ---------- 42 | 43 | Like `kprint_at`, `print_char` allows cols/rows to be `-1`. In that case it retrieves 44 | the cursor position from the hardware, using the `ports.c` routines. 45 | 46 | `print_char` also handles newlines. In that case, we will position the cursor offset 47 | to column 0 of the next row. 48 | 49 | Remember that the VGA cells take two bytes, one for the character itself and another one 50 | for the attribute. 51 | 52 | 53 | kernel.c 54 | -------- 55 | 56 | Our new kernel is finally able to print strings. 57 | 58 | It tests correct character positioning, spanning through multiple lines, line breaks, 59 | and finally it tries to write outside of the screen bounds. What happens then? 60 | 61 | In the next lesson we will learn how to scroll the screen. 62 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoDrivers/boot/32bit_print.asm: -------------------------------------------------------------------------------- 1 | [bits 32] ; using 32-bit protected mode 2 | 3 | ; this is how constants are defined 4 | VIDEO_MEMORY equ 0xb8000 5 | WHITE_OB_BLACK equ 0x0f ; the color byte for each character 6 | 7 | print_string_pm: 8 | pusha 9 | mov edx, VIDEO_MEMORY 10 | 11 | print_string_pm_loop: 12 | mov al, [ebx] ; [ebx] is the address of our character 13 | mov ah, WHITE_OB_BLACK 14 | 15 | cmp al, 0 ; check if end of string 16 | je print_string_pm_done 17 | 18 | mov [edx], ax ; store character + attribute in video memory 19 | add ebx, 1 ; next char 20 | add edx, 2 ; next video memory position 21 | 22 | jmp print_string_pm_loop 23 | 24 | print_string_pm_done: 25 | popa 26 | ret 27 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoDrivers/boot/bootsect.asm: -------------------------------------------------------------------------------- 1 | ; Identical to lesson 13's boot sector, but the %included files have new paths 2 | [org 0x7c00] 3 | KERNEL_OFFSET equ 0x1000 ; The same one we used when linking the kernel 4 | 5 | mov [BOOT_DRIVE], dl ; Remember that the BIOS sets us the boot drive in 'dl' on boot 6 | mov bp, 0x9000 7 | mov sp, bp 8 | 9 | mov bx, MSG_REAL_MODE 10 | call print 11 | call print_nl 12 | 13 | call load_kernel ; read the kernel from disk 14 | call switch_to_pm ; disable interrupts, load GDT, etc. Finally jumps to 'BEGIN_PM' 15 | jmp $ ; Never executed 16 | 17 | %include "boot/print.asm" 18 | %include "boot/print_hex.asm" 19 | %include "boot/disk.asm" 20 | %include "boot/gdt.asm" 21 | %include "boot/32bit_print.asm" 22 | %include "boot/switch_pm.asm" 23 | 24 | [bits 16] 25 | load_kernel: 26 | mov bx, MSG_LOAD_KERNEL 27 | call print 28 | call print_nl 29 | 30 | mov bx, KERNEL_OFFSET ; Read from disk and store in 0x1000 31 | mov dh, 16 ; Our future kernel will be larger, make this big 32 | mov dl, [BOOT_DRIVE] 33 | call disk_load 34 | ret 35 | 36 | [bits 32] 37 | BEGIN_PM: 38 | mov ebx, MSG_PROT_MODE 39 | call print_string_pm 40 | call KERNEL_OFFSET ; Give control to the kernel 41 | jmp $ ; Stay here when the kernel returns control to us (if ever) 42 | 43 | 44 | BOOT_DRIVE db 0 ; It is a good idea to store it in memory because 'dl' may get overwritten 45 | MSG_REAL_MODE db "Started in 16-bit Real Mode", 0 46 | MSG_PROT_MODE db "Landed in 32-bit Protected Mode", 0 47 | MSG_LOAD_KERNEL db "Loading kernel into memory", 0 48 | 49 | ; padding 50 | times 510 - ($-$$) db 0 51 | dw 0xaa55 52 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoDrivers/boot/bootsect.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/kernelVideoDrivers/boot/bootsect.bin -------------------------------------------------------------------------------- /src/backupJames/kernelVideoDrivers/boot/disk.asm: -------------------------------------------------------------------------------- 1 | ; load 'dh' sectors from drive 'dl' into ES:BX 2 | disk_load: 3 | pusha 4 | ; reading from disk requires setting specific values in all registers 5 | ; so we will overwrite our input parameters from 'dx'. Let's save it 6 | ; to the stack for later use. 7 | push dx 8 | 9 | mov ah, 0x02 ; ah <- int 0x13 function. 0x02 = 'read' 10 | mov al, dh ; al <- number of sectors to read (0x01 .. 0x80) 11 | mov cl, 0x02 ; cl <- sector (0x01 .. 0x11) 12 | ; 0x01 is our boot sector, 0x02 is the first 'available' sector 13 | mov ch, 0x00 ; ch <- cylinder (0x0 .. 0x3FF, upper 2 bits in 'cl') 14 | ; dl <- drive number. Our caller sets it as a parameter and gets it from BIOS 15 | ; (0 = floppy, 1 = floppy2, 0x80 = hdd, 0x81 = hdd2) 16 | mov dh, 0x00 ; dh <- head number (0x0 .. 0xF) 17 | 18 | ; [es:bx] <- pointer to buffer where the data will be stored 19 | ; caller sets it up for us, and it is actually the standard location for int 13h 20 | int 0x13 ; BIOS interrupt 21 | jc disk_error ; if error (stored in the carry bit) 22 | 23 | pop dx 24 | cmp al, dh ; BIOS also sets 'al' to the # of sectors read. Compare it. 25 | jne sectors_error 26 | popa 27 | ret 28 | 29 | 30 | disk_error: 31 | mov bx, DISK_ERROR 32 | call print 33 | call print_nl 34 | mov dh, ah ; ah = error code, dl = disk drive that dropped the error 35 | call print_hex ; check out the code at http://stanislavs.org/helppc/int_13-1.html 36 | jmp disk_loop 37 | 38 | sectors_error: 39 | mov bx, SECTORS_ERROR 40 | call print 41 | 42 | disk_loop: 43 | jmp $ 44 | 45 | DISK_ERROR: db "Disk read error", 0 46 | SECTORS_ERROR: db "Incorrect number of sectors read", 0 47 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoDrivers/boot/gdt.asm: -------------------------------------------------------------------------------- 1 | gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps 2 | ; the GDT starts with a null 8-byte 3 | dd 0x0 ; 4 byte 4 | dd 0x0 ; 4 byte 5 | 6 | ; GDT for code segment. base = 0x00000000, length = 0xfffff 7 | ; for flags, refer to os-dev.pdf document, page 36 8 | gdt_code: 9 | dw 0xffff ; segment length, bits 0-15 10 | dw 0x0 ; segment base, bits 0-15 11 | db 0x0 ; segment base, bits 16-23 12 | db 10011010b ; flags (8 bits) 13 | db 11001111b ; flags (4 bits) + segment length, bits 16-19 14 | db 0x0 ; segment base, bits 24-31 15 | 16 | ; GDT for data segment. base and length identical to code segment 17 | ; some flags changed, again, refer to os-dev.pdf 18 | gdt_data: 19 | dw 0xffff 20 | dw 0x0 21 | db 0x0 22 | db 10010010b 23 | db 11001111b 24 | db 0x0 25 | 26 | gdt_end: 27 | 28 | ; GDT descriptor 29 | gdt_descriptor: 30 | dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size 31 | dd gdt_start ; address (32 bit) 32 | 33 | ; define some constants for later use 34 | CODE_SEG equ gdt_code - gdt_start 35 | DATA_SEG equ gdt_data - gdt_start 36 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoDrivers/boot/kernel_entry.asm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | [extern main] ; Define calling point. Must have same name as kernel.c 'main' function 3 | call main ; Calls the C function. The linker will know where it is placed in memory 4 | jmp $ 5 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoDrivers/boot/kernel_entry.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/kernelVideoDrivers/boot/kernel_entry.o -------------------------------------------------------------------------------- /src/backupJames/kernelVideoDrivers/boot/print.asm: -------------------------------------------------------------------------------- 1 | print: 2 | pusha 3 | 4 | ; keep this in mind: 5 | ; while (string[i] != 0) { print string[i]; i++ } 6 | 7 | ; the comparison for string end (null byte) 8 | start: 9 | mov al, [bx] ; 'bx' is the base address for the string 10 | cmp al, 0 11 | je done 12 | 13 | ; the part where we print with the BIOS help 14 | mov ah, 0x0e 15 | int 0x10 ; 'al' already contains the char 16 | 17 | ; increment pointer and do next loop 18 | add bx, 1 19 | jmp start 20 | 21 | done: 22 | popa 23 | ret 24 | 25 | 26 | 27 | print_nl: 28 | pusha 29 | 30 | mov ah, 0x0e 31 | mov al, 0x0a ; newline char 32 | int 0x10 33 | mov al, 0x0d ; carriage return 34 | int 0x10 35 | 36 | popa 37 | ret 38 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoDrivers/boot/print_hex.asm: -------------------------------------------------------------------------------- 1 | ; receiving the data in 'dx' 2 | ; For the examples we'll assume that we're called with dx=0x1234 3 | print_hex: 4 | pusha 5 | 6 | mov cx, 0 ; our index variable 7 | 8 | ; Strategy: get the last char of 'dx', then convert to ASCII 9 | ; Numeric ASCII values: '0' (ASCII 0x30) to '9' (0x39), so just add 0x30 to byte N. 10 | ; For alphabetic characters A-F: 'A' (ASCII 0x41) to 'F' (0x46) we'll add 0x40 11 | ; Then, move the ASCII byte to the correct position on the resulting string 12 | hex_loop: 13 | cmp cx, 4 ; loop 4 times 14 | je end 15 | 16 | ; 1. convert last char of 'dx' to ascii 17 | mov ax, dx ; we will use 'ax' as our working register 18 | and ax, 0x000f ; 0x1234 -> 0x0004 by masking first three to zeros 19 | add al, 0x30 ; add 0x30 to N to convert it to ASCII "N" 20 | cmp al, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F' 21 | jle step2 22 | add al, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7 23 | 24 | step2: 25 | ; 2. get the correct position of the string to place our ASCII char 26 | ; bx <- base address + string length - index of char 27 | mov bx, HEX_OUT + 5 ; base + length 28 | sub bx, cx ; our index variable 29 | mov [bx], al ; copy the ASCII char on 'al' to the position pointed by 'bx' 30 | ror dx, 4 ; 0x1234 -> 0x4123 -> 0x3412 -> 0x2341 -> 0x1234 31 | 32 | ; increment index and loop 33 | add cx, 1 34 | jmp hex_loop 35 | 36 | end: 37 | ; prepare the parameter and call the function 38 | ; remember that print receives parameters in 'bx' 39 | mov bx, HEX_OUT 40 | call print 41 | 42 | popa 43 | ret 44 | 45 | HEX_OUT: 46 | db '0x0000',0 ; reserve memory for our new string 47 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoDrivers/boot/switch_pm.asm: -------------------------------------------------------------------------------- 1 | [bits 16] 2 | switch_to_pm: 3 | cli ; 1. disable interrupts 4 | lgdt [gdt_descriptor] ; 2. load the GDT descriptor 5 | mov eax, cr0 6 | or eax, 0x1 ; 3. set 32-bit mode bit in cr0 7 | mov cr0, eax 8 | jmp CODE_SEG:init_pm ; 4. far jump by using a different segment 9 | 10 | [bits 32] 11 | init_pm: ; we are now using 32-bit instructions 12 | mov ax, DATA_SEG ; 5. update the segment registers 13 | mov ds, ax 14 | mov ss, ax 15 | mov es, ax 16 | mov fs, ax 17 | mov gs, ax 18 | 19 | mov ebp, 0x90000 ; 6. update the stack right at the top of the free space 20 | mov esp, ebp 21 | 22 | call BEGIN_PM ; 7. Call a well-known label with useful code 23 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoDrivers/drivers/ports.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Read a byte from the specified port 3 | */ 4 | unsigned char port_byte_in (unsigned short port) { 5 | unsigned char result; 6 | /* Inline assembler syntax 7 | * !! Notice how the source and destination registers are switched from NASM !! 8 | * 9 | * '"=a" (result)'; set '=' the C variable '(result)' to the value of register e'a'x 10 | * '"d" (port)': map the C variable '(port)' into e'd'x register 11 | * 12 | * Inputs and outputs are separated by colons 13 | */ 14 | __asm__("in %%dx, %%al" : "=a" (result) : "d" (port)); 15 | return result; 16 | } 17 | 18 | void port_byte_out (unsigned short port, unsigned char data) { 19 | /* Notice how here both registers are mapped to C variables and 20 | * nothing is returned, thus, no equals '=' in the asm syntax 21 | * However we see a comma since there are two variables in the input area 22 | * and none in the 'return' area 23 | */ 24 | __asm__("out %%al, %%dx" : : "a" (data), "d" (port)); 25 | } 26 | 27 | unsigned short port_word_in (unsigned short port) { 28 | unsigned short result; 29 | __asm__("in %%dx, %%ax" : "=a" (result) : "d" (port)); 30 | return result; 31 | } 32 | 33 | void port_word_out (unsigned short port, unsigned short data) { 34 | __asm__("out %%ax, %%dx" : : "a" (data), "d" (port)); 35 | } 36 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoDrivers/drivers/ports.h: -------------------------------------------------------------------------------- 1 | unsigned char port_byte_in (unsigned short port); 2 | void port_byte_out (unsigned short port, unsigned char data); 3 | unsigned short port_word_in (unsigned short port); 4 | void port_word_out (unsigned short port, unsigned short data); 5 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoDrivers/drivers/ports.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/kernelVideoDrivers/drivers/ports.o -------------------------------------------------------------------------------- /src/backupJames/kernelVideoDrivers/drivers/screen.c: -------------------------------------------------------------------------------- 1 | #include "screen.h" 2 | #include "ports.h" 3 | 4 | /* Declaration of private functions */ 5 | int get_cursor_offset(); 6 | void set_cursor_offset(int offset); 7 | int print_char(char c, int col, int row, char attr); 8 | int get_offset(int col, int row); 9 | int get_offset_row(int offset); 10 | int get_offset_col(int offset); 11 | 12 | /********************************************************** 13 | * Public Kernel API functions * 14 | **********************************************************/ 15 | 16 | /** 17 | * Print a message on the specified location 18 | * If col, row, are negative, we will use the current offset 19 | */ 20 | void display_at(char *message, int col, int row) { 21 | /* Set cursor if col/row are negative */ 22 | int offset; 23 | if (col >= 0 && row >= 0) 24 | offset = get_offset(col, row); 25 | else { 26 | offset = get_cursor_offset(); 27 | row = get_offset_row(offset); 28 | col = get_offset_col(offset); 29 | } 30 | 31 | /* Loop through message and print it */ 32 | int i = 0; 33 | while (message[i] != 0) { 34 | offset = print_char(message[i++], col, row, WHITE_ON_BLACK); 35 | /* Compute row/col for next iteration */ 36 | row = get_offset_row(offset); 37 | col = get_offset_col(offset); 38 | } 39 | } 40 | 41 | void display(char *message) { 42 | display_at(message, -1, -1); 43 | } 44 | 45 | 46 | /********************************************************** 47 | * Private kernel functions * 48 | **********************************************************/ 49 | 50 | 51 | /** 52 | * Innermost print function for our kernel, directly accesses the video memory 53 | * 54 | * If 'col' and 'row' are negative, we will print at current cursor location 55 | * If 'attr' is zero it will use 'white on black' as default 56 | * Returns the offset of the next character 57 | * Sets the video cursor to the returned offset 58 | */ 59 | int print_char(char c, int col, int row, char attr) { 60 | unsigned char *vidmem = (unsigned char*) VIDEO_ADDRESS; 61 | if (!attr) attr = WHITE_ON_BLACK; 62 | 63 | /* Error control: print a red 'E' if the coords aren't right */ 64 | if (col >= MAX_COLS || row >= MAX_ROWS) { 65 | vidmem[2*(MAX_COLS)*(MAX_ROWS)-2] = 'E'; 66 | vidmem[2*(MAX_COLS)*(MAX_ROWS)-1] = RED_ON_WHITE; 67 | return get_offset(col, row); 68 | } 69 | 70 | int offset; 71 | if (col >= 0 && row >= 0) offset = get_offset(col, row); 72 | else offset = get_cursor_offset(); 73 | 74 | if (c == '\n') { 75 | row = get_offset_row(offset); 76 | offset = get_offset(0, row+1); 77 | } else { 78 | vidmem[offset] = c; 79 | vidmem[offset+1] = attr; 80 | offset += 2; 81 | } 82 | set_cursor_offset(offset); 83 | return offset; 84 | } 85 | 86 | int get_cursor_offset() { 87 | /* Use the VGA ports to get the current cursor position 88 | * 1. Ask for high byte of the cursor offset (data 14) 89 | * 2. Ask for low byte (data 15) 90 | */ 91 | port_byte_out(REG_SCREEN_CTRL, 14); 92 | int offset = port_byte_in(REG_SCREEN_DATA) << 8; /* High byte: << 8 */ 93 | port_byte_out(REG_SCREEN_CTRL, 15); 94 | offset += port_byte_in(REG_SCREEN_DATA); 95 | return offset * 2; /* Position * size of character cell */ 96 | } 97 | 98 | void set_cursor_offset(int offset) { 99 | /* Similar to get_cursor_offset, but instead of reading we write data */ 100 | offset /= 2; 101 | port_byte_out(REG_SCREEN_CTRL, 14); 102 | port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset >> 8)); 103 | port_byte_out(REG_SCREEN_CTRL, 15); 104 | port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset & 0xff)); 105 | } 106 | 107 | void clear_screen() { 108 | int screen_size = MAX_COLS * MAX_ROWS; 109 | int i; 110 | char *screen = (char *)VIDEO_ADDRESS; 111 | 112 | for (i = 0; i < screen_size; i++) { 113 | screen[i*2] = ' '; 114 | screen[i*2+1] = WHITE_ON_BLACK; 115 | } 116 | set_cursor_offset(get_offset(0, 0)); 117 | } 118 | 119 | 120 | int get_offset(int col, int row) { return 2 * (row * MAX_COLS + col); } 121 | int get_offset_row(int offset) { return offset / (2 * MAX_COLS); } 122 | int get_offset_col(int offset) { return (offset - (get_offset_row(offset)*2*MAX_COLS))/2; } 123 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoDrivers/drivers/screen.h: -------------------------------------------------------------------------------- 1 | #define VIDEO_ADDRESS 0xb8000 2 | #define MAX_ROWS 25 3 | #define MAX_COLS 80 4 | #define WHITE_ON_BLACK 0x0f 5 | #define RED_ON_WHITE 0xf4 6 | 7 | /* Screen i/o ports */ 8 | #define REG_SCREEN_CTRL 0x3d4 9 | #define REG_SCREEN_DATA 0x3d5 10 | 11 | /* Public kernel API */ 12 | void clear_screen(); 13 | void display_at(char *message, int col, int row); 14 | void display(char *message); 15 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoDrivers/drivers/screen.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/kernelVideoDrivers/drivers/screen.o -------------------------------------------------------------------------------- /src/backupJames/kernelVideoDrivers/kernel.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/kernelVideoDrivers/kernel.bin -------------------------------------------------------------------------------- /src/backupJames/kernelVideoDrivers/kernel/kernel.c: -------------------------------------------------------------------------------- 1 | #include "../drivers/screen.h" 2 | 3 | void main() { 4 | clear_screen(); 5 | display_at("GeekSkool", 1, 1); 6 | display_at("HasGeek House", 1, 6); 7 | } 8 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoDrivers/kernel/kernel.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/kernelVideoDrivers/kernel/kernel.o -------------------------------------------------------------------------------- /src/backupJames/kernelVideoDrivers/os-image.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/kernelVideoDrivers/os-image.bin -------------------------------------------------------------------------------- /src/backupJames/kernelVideoPorts/Makefile: -------------------------------------------------------------------------------- 1 | C_SOURCES = $(wildcard kernel/*.c drivers/*.c) 2 | HEADERS = $(wildcard kernel/*.h drivers/*.h) 3 | # Nice syntax for file extension replacement 4 | OBJ = ${C_SOURCES:.c=.o} 5 | 6 | # Change this if your cross-compiler is somewhere else 7 | CC = gcc -m32 8 | GDB = /usr/local/i386elfgcc/bin/i386-elf-gdb 9 | # -g: Use debugging symbols in gcc 10 | CFLAGS = -g 11 | 12 | # First rule is run by default 13 | os-image.bin: boot/bootsect.bin kernel.bin 14 | cat $^ > os-image.bin 15 | 16 | # '--oformat binary' deletes all symbols as a collateral, so we don't need 17 | # to 'strip' them manually on this case 18 | kernel.bin: boot/kernel_entry.o ${OBJ} 19 | i386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary 20 | 21 | # Used for debugging purposes 22 | kernel.elf: boot/kernel_entry.o ${OBJ} 23 | i386-elf-ld -o $@ -Ttext 0x1000 $^ 24 | 25 | run: os-image.bin 26 | qemu-system-i386 -fda os-image.bin 27 | 28 | # Open the connection to qemu and load our kernel-object file with symbols 29 | debug: os-image.bin kernel.elf 30 | qemu-system-i386 -s -fda os-image.bin & 31 | ${GDB} -ex "target remote localhost:1234" -ex "symbol-file kernel.elf" 32 | 33 | # Generic rules for wildcards 34 | # To make an object, always compile from its .c 35 | %.o: %.c ${HEADERS} 36 | ${CC} ${CFLAGS} -ffreestanding -c $< -o $@ 37 | 38 | %.o: %.asm 39 | nasm $< -f elf -o $@ 40 | 41 | %.bin: %.asm 42 | nasm $< -f bin -o $@ 43 | 44 | clean: 45 | rm -rf *.bin *.dis *.o os-image.bin *.elf 46 | rm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o 47 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoPorts/boot/32bit_print.asm: -------------------------------------------------------------------------------- 1 | [bits 32] ; using 32-bit protected mode 2 | 3 | ; this is how constants are defined 4 | VIDEO_MEMORY equ 0xb8000 5 | WHITE_OB_BLACK equ 0x0f ; the color byte for each character 6 | 7 | print_string_pm: 8 | pusha 9 | mov edx, VIDEO_MEMORY 10 | 11 | print_string_pm_loop: 12 | mov al, [ebx] ; [ebx] is the address of our character 13 | mov ah, WHITE_OB_BLACK 14 | 15 | cmp al, 0 ; check if end of string 16 | je print_string_pm_done 17 | 18 | mov [edx], ax ; store character + attribute in video memory 19 | add ebx, 1 ; next char 20 | add edx, 2 ; next video memory position 21 | 22 | jmp print_string_pm_loop 23 | 24 | print_string_pm_done: 25 | popa 26 | ret 27 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoPorts/boot/bootsect.asm: -------------------------------------------------------------------------------- 1 | ; Identical to lesson 13's boot sector, but the %included files have new paths 2 | [org 0x7c00] 3 | KERNEL_OFFSET equ 0x1000 ; The same one we used when linking the kernel 4 | 5 | mov [BOOT_DRIVE], dl ; Remember that the BIOS sets us the boot drive in 'dl' on boot 6 | mov bp, 0x9000 7 | mov sp, bp 8 | 9 | mov bx, MSG_REAL_MODE 10 | call print 11 | call print_nl 12 | 13 | call load_kernel ; read the kernel from disk 14 | call switch_to_pm ; disable interrupts, load GDT, etc. Finally jumps to 'BEGIN_PM' 15 | jmp $ ; Never executed 16 | 17 | %include "boot/print.asm" 18 | %include "boot/print_hex.asm" 19 | %include "boot/disk.asm" 20 | %include "boot/gdt.asm" 21 | %include "boot/32bit_print.asm" 22 | %include "boot/switch_pm.asm" 23 | 24 | [bits 16] 25 | load_kernel: 26 | mov bx, MSG_LOAD_KERNEL 27 | call print 28 | call print_nl 29 | 30 | mov bx, KERNEL_OFFSET ; Read from disk and store in 0x1000 31 | mov dh, 16 ; Our future kernel will be larger, make this big 32 | mov dl, [BOOT_DRIVE] 33 | call disk_load 34 | ret 35 | 36 | [bits 32] 37 | BEGIN_PM: 38 | mov ebx, MSG_PROT_MODE 39 | call print_string_pm 40 | call KERNEL_OFFSET ; Give control to the kernel 41 | jmp $ ; Stay here when the kernel returns control to us (if ever) 42 | 43 | 44 | BOOT_DRIVE db 0 ; It is a good idea to store it in memory because 'dl' may get overwritten 45 | MSG_REAL_MODE db "Started in 16-bit Real Mode", 0 46 | MSG_PROT_MODE db "Landed in 32-bit Protected Mode", 0 47 | MSG_LOAD_KERNEL db "Loading kernel into memory", 0 48 | 49 | ; padding 50 | times 510 - ($-$$) db 0 51 | dw 0xaa55 52 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoPorts/boot/bootsect.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/kernelVideoPorts/boot/bootsect.bin -------------------------------------------------------------------------------- /src/backupJames/kernelVideoPorts/boot/disk.asm: -------------------------------------------------------------------------------- 1 | ; load 'dh' sectors from drive 'dl' into ES:BX 2 | disk_load: 3 | pusha 4 | ; reading from disk requires setting specific values in all registers 5 | ; so we will overwrite our input parameters from 'dx'. Let's save it 6 | ; to the stack for later use. 7 | push dx 8 | 9 | mov ah, 0x02 ; ah <- int 0x13 function. 0x02 = 'read' 10 | mov al, dh ; al <- number of sectors to read (0x01 .. 0x80) 11 | mov cl, 0x02 ; cl <- sector (0x01 .. 0x11) 12 | ; 0x01 is our boot sector, 0x02 is the first 'available' sector 13 | mov ch, 0x00 ; ch <- cylinder (0x0 .. 0x3FF, upper 2 bits in 'cl') 14 | ; dl <- drive number. Our caller sets it as a parameter and gets it from BIOS 15 | ; (0 = floppy, 1 = floppy2, 0x80 = hdd, 0x81 = hdd2) 16 | mov dh, 0x00 ; dh <- head number (0x0 .. 0xF) 17 | 18 | ; [es:bx] <- pointer to buffer where the data will be stored 19 | ; caller sets it up for us, and it is actually the standard location for int 13h 20 | int 0x13 ; BIOS interrupt 21 | jc disk_error ; if error (stored in the carry bit) 22 | 23 | pop dx 24 | cmp al, dh ; BIOS also sets 'al' to the # of sectors read. Compare it. 25 | jne sectors_error 26 | popa 27 | ret 28 | 29 | 30 | disk_error: 31 | mov bx, DISK_ERROR 32 | call print 33 | call print_nl 34 | mov dh, ah ; ah = error code, dl = disk drive that dropped the error 35 | call print_hex ; check out the code at http://stanislavs.org/helppc/int_13-1.html 36 | jmp disk_loop 37 | 38 | sectors_error: 39 | mov bx, SECTORS_ERROR 40 | call print 41 | 42 | disk_loop: 43 | jmp $ 44 | 45 | DISK_ERROR: db "Disk read error", 0 46 | SECTORS_ERROR: db "Incorrect number of sectors read", 0 47 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoPorts/boot/gdt.asm: -------------------------------------------------------------------------------- 1 | gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps 2 | ; the GDT starts with a null 8-byte 3 | dd 0x0 ; 4 byte 4 | dd 0x0 ; 4 byte 5 | 6 | ; GDT for code segment. base = 0x00000000, length = 0xfffff 7 | ; for flags, refer to os-dev.pdf document, page 36 8 | gdt_code: 9 | dw 0xffff ; segment length, bits 0-15 10 | dw 0x0 ; segment base, bits 0-15 11 | db 0x0 ; segment base, bits 16-23 12 | db 10011010b ; flags (8 bits) 13 | db 11001111b ; flags (4 bits) + segment length, bits 16-19 14 | db 0x0 ; segment base, bits 24-31 15 | 16 | ; GDT for data segment. base and length identical to code segment 17 | ; some flags changed, again, refer to os-dev.pdf 18 | gdt_data: 19 | dw 0xffff 20 | dw 0x0 21 | db 0x0 22 | db 10010010b 23 | db 11001111b 24 | db 0x0 25 | 26 | gdt_end: 27 | 28 | ; GDT descriptor 29 | gdt_descriptor: 30 | dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size 31 | dd gdt_start ; address (32 bit) 32 | 33 | ; define some constants for later use 34 | CODE_SEG equ gdt_code - gdt_start 35 | DATA_SEG equ gdt_data - gdt_start 36 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoPorts/boot/kernel_entry.asm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | [extern main] ; Define calling point. Must have same name as kernel.c 'main' function 3 | call main ; Calls the C function. The linker will know where it is placed in memory 4 | jmp $ 5 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoPorts/boot/kernel_entry.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/kernelVideoPorts/boot/kernel_entry.o -------------------------------------------------------------------------------- /src/backupJames/kernelVideoPorts/boot/print.asm: -------------------------------------------------------------------------------- 1 | print: 2 | pusha 3 | 4 | ; keep this in mind: 5 | ; while (string[i] != 0) { print string[i]; i++ } 6 | 7 | ; the comparison for string end (null byte) 8 | start: 9 | mov al, [bx] ; 'bx' is the base address for the string 10 | cmp al, 0 11 | je done 12 | 13 | ; the part where we print with the BIOS help 14 | mov ah, 0x0e 15 | int 0x10 ; 'al' already contains the char 16 | 17 | ; increment pointer and do next loop 18 | add bx, 1 19 | jmp start 20 | 21 | done: 22 | popa 23 | ret 24 | 25 | 26 | 27 | print_nl: 28 | pusha 29 | 30 | mov ah, 0x0e 31 | mov al, 0x0a ; newline char 32 | int 0x10 33 | mov al, 0x0d ; carriage return 34 | int 0x10 35 | 36 | popa 37 | ret 38 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoPorts/boot/print_hex.asm: -------------------------------------------------------------------------------- 1 | ; receiving the data in 'dx' 2 | ; For the examples we'll assume that we're called with dx=0x1234 3 | print_hex: 4 | pusha 5 | 6 | mov cx, 0 ; our index variable 7 | 8 | ; Strategy: get the last char of 'dx', then convert to ASCII 9 | ; Numeric ASCII values: '0' (ASCII 0x30) to '9' (0x39), so just add 0x30 to byte N. 10 | ; For alphabetic characters A-F: 'A' (ASCII 0x41) to 'F' (0x46) we'll add 0x40 11 | ; Then, move the ASCII byte to the correct position on the resulting string 12 | hex_loop: 13 | cmp cx, 4 ; loop 4 times 14 | je end 15 | 16 | ; 1. convert last char of 'dx' to ascii 17 | mov ax, dx ; we will use 'ax' as our working register 18 | and ax, 0x000f ; 0x1234 -> 0x0004 by masking first three to zeros 19 | add al, 0x30 ; add 0x30 to N to convert it to ASCII "N" 20 | cmp al, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F' 21 | jle step2 22 | add al, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7 23 | 24 | step2: 25 | ; 2. get the correct position of the string to place our ASCII char 26 | ; bx <- base address + string length - index of char 27 | mov bx, HEX_OUT + 5 ; base + length 28 | sub bx, cx ; our index variable 29 | mov [bx], al ; copy the ASCII char on 'al' to the position pointed by 'bx' 30 | ror dx, 4 ; 0x1234 -> 0x4123 -> 0x3412 -> 0x2341 -> 0x1234 31 | 32 | ; increment index and loop 33 | add cx, 1 34 | jmp hex_loop 35 | 36 | end: 37 | ; prepare the parameter and call the function 38 | ; remember that print receives parameters in 'bx' 39 | mov bx, HEX_OUT 40 | call print 41 | 42 | popa 43 | ret 44 | 45 | HEX_OUT: 46 | db '0x0000',0 ; reserve memory for our new string 47 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoPorts/boot/switch_pm.asm: -------------------------------------------------------------------------------- 1 | [bits 16] 2 | switch_to_pm: 3 | cli ; 1. disable interrupts 4 | lgdt [gdt_descriptor] ; 2. load the GDT descriptor 5 | mov eax, cr0 6 | or eax, 0x1 ; 3. set 32-bit mode bit in cr0 7 | mov cr0, eax 8 | jmp CODE_SEG:init_pm ; 4. far jump by using a different segment 9 | 10 | [bits 32] 11 | init_pm: ; we are now using 32-bit instructions 12 | mov ax, DATA_SEG ; 5. update the segment registers 13 | mov ds, ax 14 | mov ss, ax 15 | mov es, ax 16 | mov fs, ax 17 | mov gs, ax 18 | 19 | mov ebp, 0x90000 ; 6. update the stack right at the top of the free space 20 | mov esp, ebp 21 | 22 | call BEGIN_PM ; 7. Call a well-known label with useful code 23 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoPorts/drivers/ports.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Read a byte from the specified port 3 | */ 4 | unsigned char port_byte_in (unsigned short port) { 5 | unsigned char result; 6 | __asm__("in %%dx, %%al" : "=a" (result) : "d" (port)); 7 | return result; 8 | } 9 | 10 | void port_byte_out (unsigned short port, unsigned char data) { 11 | __asm__("out %%al, %%dx" : : "a" (data), "d" (port)); 12 | } 13 | 14 | unsigned short port_word_in (unsigned short port) { 15 | unsigned short result; 16 | __asm__("in %%dx, %%ax" : "=a" (result) : "d" (port)); 17 | return result; 18 | } 19 | 20 | void port_word_out (unsigned short port, unsigned short data) { 21 | __asm__("out %%ax, %%dx" : : "a" (data), "d" (port)); 22 | } 23 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoPorts/drivers/ports.h: -------------------------------------------------------------------------------- 1 | unsigned char port_byte_in (unsigned short port); 2 | void port_byte_out (unsigned short port, unsigned char data); 3 | unsigned short port_word_in (unsigned short port); 4 | void port_word_out (unsigned short port, unsigned short data); 5 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoPorts/drivers/ports.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/kernelVideoPorts/drivers/ports.o -------------------------------------------------------------------------------- /src/backupJames/kernelVideoPorts/kernel.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/kernelVideoPorts/kernel.bin -------------------------------------------------------------------------------- /src/backupJames/kernelVideoPorts/kernel/kernel.c: -------------------------------------------------------------------------------- 1 | #include "../drivers/ports.h" 2 | 3 | void main() { 4 | /* Screen cursor position: ask VGA control register (0x3d4) for bytes 5 | * 14 = high byte of cursor and 15 = low byte of cursor. */ 6 | port_byte_out(0x3d4, 14); /* Requesting byte 14: high byte of cursor pos */ 7 | /* Data is returned in VGA data register (0x3d5) */ 8 | int position = port_byte_in(0x3d5); 9 | position = position << 8; /* high byte */ 10 | 11 | port_byte_out(0x3d4, 15); /* requesting low byte */ 12 | position += port_byte_in(0x3d5); 13 | 14 | /* VGA 'cells' consist of the character and its control data 15 | * e.g. 'white on black background', 'red text on white bg', etc */ 16 | int offset_from_vga = position * 2; 17 | 18 | /* Now you can examine both variables using gdb, since we still 19 | * don't know how to print strings on screen. Run 'make debug' and 20 | * on the gdb console: 21 | * breakpoint kernel.c:21 22 | * continue 23 | * print position 24 | * print offset_from_vga 25 | */ 26 | 27 | /* Let's write on the current cursor position, we already know how 28 | * to do that */ 29 | char *vga = (char *)0xb8000; 30 | vga[offset_from_vga] = 'X'; 31 | vga[offset_from_vga+1] = 0x0f; /* White text on black background */ 32 | } 33 | -------------------------------------------------------------------------------- /src/backupJames/kernelVideoPorts/kernel/kernel.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/kernelVideoPorts/kernel/kernel.o -------------------------------------------------------------------------------- /src/backupJames/kernelVideoPorts/os-image.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/backupJames/kernelVideoPorts/os-image.bin -------------------------------------------------------------------------------- /src/bochsrc: -------------------------------------------------------------------------------- 1 | floppya : 1_44=os-image , status=inserted 2 | boot : a 3 | -------------------------------------------------------------------------------- /src/boot_sect.asm: -------------------------------------------------------------------------------- 1 | ; boot sector that enters 32 - bit protected mode. 2 | [org 0x7c00] 3 | KERNEL_OFFSET equ 0x1000 4 | 5 | mov [BOOT_DRIVE], dl ; BIOS stores our boot drive in DL , so it 's 6 | ; best to remember this for later. 7 | 8 | mov bp, 0x9000 9 | mov sp, bp 10 | 11 | mov bx, MSG_REAL_MODE 12 | call print_string 13 | 14 | call load_kernel 15 | 16 | call switch_to_pm 17 | jmp $ ; jump forever(jump to address of current instruction) 18 | 19 | %include "print_string.asm" 20 | %include "disk_load.asm" 21 | %include "print_string_pm.asm" 22 | %include "gdt.asm" 23 | %include "switch_to_pm.asm" 24 | 25 | [ bits 16] 26 | load_kernel: 27 | mov bx, MSG_LOAD_KERNEL ; using BX as a parameter of our function 28 | call print_string ; by specifying the address of a string 29 | 30 | mov bx, KERNEL_OFFSET 31 | mov dh, 30 32 | mov dl, [BOOT_DRIVE] 33 | ; with dx having been loaded with number of sectors to read, we will call disk_load 34 | call disk_load 35 | ret 36 | 37 | [bits 32] 38 | BEGIN_PM: 39 | mov ebx, MSG_PROT_MODE 40 | call print_string_pm 41 | 42 | call KERNEL_OFFSET 43 | 44 | jmp $ 45 | 46 | BOOT_DRIVE: 47 | db 0 48 | 49 | MSG_REAL_MODE: 50 | db " Hello GeekSkool,Started in 16- bit Real Mode ", 0 51 | 52 | MSG_PROT_MODE: 53 | db " Successfully landed in 32- bit Protected Mode ", 0 54 | 55 | MSG_LOAD_KERNEL: 56 | db " Loading kernel into memory. ", 0 57 | 58 | times 510-($-$$) db 0 ; Pad the boot sector out with zeros 59 | ; When compiled , our program must fit into 512 bytes , 60 | ; with the last two bytes being the magic number , 61 | ; so here , tell our assembly compiler to pad out our 62 | ; program with enough zero bytes (db 0) to bring us to the 63 | ; 510 th byte. 64 | dw 0xaa55 ; Last two bytes form the magic number 65 | ; BIOS is informed that we are a boot sector 66 | -------------------------------------------------------------------------------- /src/boot_sect.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/boot_sect.bin -------------------------------------------------------------------------------- /src/boot_sector/.disk_load.asm.swp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/boot_sector/.disk_load.asm.swp -------------------------------------------------------------------------------- /src/boot_sector/boot_sect.asm: -------------------------------------------------------------------------------- 1 | ; boot sector that enters 32 - bit protected mode. 2 | [org 0x7c00] 3 | KERNEL_OFFSET equ 0x1000 4 | 5 | mov [BOOT_DRIVE], dl ; BIOS stores our boot drive in DL , so it 's 6 | ; best to remember this for later. 7 | 8 | mov bp, 0x9000 9 | mov sp, bp 10 | 11 | mov bx, MSG_REAL_MODE ;to print a message on screen, store message in BX Register 12 | call print_string 13 | 14 | call load_kernel 15 | 16 | call switch_to_pm 17 | jmp $ ; jump forever(jump to address of current instruction) 18 | 19 | %include "print_string.asm" 20 | %include "disk_load.asm" 21 | %include "print_string_pm.asm" 22 | %include "gdt.asm" 23 | %include "switch_to_pm.asm" 24 | 25 | [ bits 16] 26 | load_kernel: 27 | mov bx, MSG_LOAD_KERNEL ; using BX as a parameter of our function 28 | call print_string ; by specifying the address of a string 29 | 30 | mov bx, KERNEL_OFFSET 31 | mov dh, 30 32 | mov dl, [BOOT_DRIVE] 33 | ; with dx having been loaded with number of sectors to read, we will call disk_load 34 | call disk_load 35 | ret 36 | 37 | [bits 32] 38 | BEGIN_PM: 39 | mov ebx, MSG_PROT_MODE 40 | call print_string_pm 41 | 42 | call KERNEL_OFFSET 43 | 44 | jmp $ 45 | 46 | BOOT_DRIVE: 47 | db 0 48 | 49 | MSG_REAL_MODE: 50 | db " Hello GeekSkool,Started in 16- bit Real Mode ", 0 51 | 52 | MSG_PROT_MODE: 53 | db " Successfully landed in 32- bit Protected Mode ", 0 54 | 55 | MSG_LOAD_KERNEL: 56 | db " Loading kernel into memory. ", 0 57 | 58 | times 510-($-$$) db 0 ; Pad the boot sector out with zeros 59 | ; When compiled , our program must fit into 512 bytes , 60 | ; with the last two bytes being the magic number , 61 | ; so here , tell our assembly compiler to pad out our 62 | ; program with enough zero bytes (db 0) to bring us to the 63 | ; 510 th byte. 64 | dw 0xaa55 ; Last two bytes form the magic number 65 | ; BIOS is informed that we are a boot sector 66 | -------------------------------------------------------------------------------- /src/boot_sector/disk_load.asm: -------------------------------------------------------------------------------- 1 | disk_load: 2 | push dx ; Store DX on stack so later we can recall 3 | ; how many sectors were request to be read! 4 | ;SECTOR 5 | mov ah, 0x02 ; BIOS read sector function 0x02 = READ 6 | mov al, dh ; READ number sectors ; number specified by dh 7 | ;CYLINDER 8 | mov ch, 0x00 ; select cylinder 0 9 | ;HEAD 10 | mov dh, 0x00 ; select head 0 11 | 12 | mov cl, 0x02 ; start reading from 2nd sector (after boot sector) 13 | 14 | int 0x13 ; BIOS interrupt 15 | jc disk_error 16 | 17 | pop dx ; pop dx from the stack 18 | cmp dh, al 19 | jne disk_error ; generate disk error if AL(sectors read) != DH(Expected number of sectors) 20 | 21 | ret 22 | disk_error: ; disk_error label 23 | mov bx, [DISK_ERROR_MSG] ; push the error message 24 | call print_string ; print_string is the helper to print string stored in [BX] 25 | jmp $ ; jump forever 26 | 27 | DISK_ERROR_MSG: 28 | db 'Disk read Error!', 0 29 | -------------------------------------------------------------------------------- /src/boot_sector/gdt.asm: -------------------------------------------------------------------------------- 1 | ; GDT tabel 2 | gdt_start: 3 | 4 | ;NULL segment 5 | gdt_null: 6 | dd 0x0 7 | dd 0x0 8 | ;define two segments, one for code and another for data 9 | ;there is no memory protection as these segments can overlap 10 | 11 | gdt_code: 12 | ; base =0x0 , limit =0 xfffff , 13 | ; 1st flags : ( present )1 ( privilege )00 ( descriptor type )1 -> 1001 b 14 | ; type flags : ( code )1 ( conforming )0 ( readable )1 ( accessed )0 -> 1010 b 15 | ; 2nd flags : ( granularity )1 (32 - bit default )1 (64 - bit seg )0 ( AVL )0 -> 1100 b 16 | dw 0xffff ; Limit ( bits 0 -15) 17 | dw 0x0 ; Base ( bits 0 -15) 18 | db 0x0 ; Base ( bits 16 -23) 19 | db 10011010b ; 1st flags , type flags 20 | db 11001111b ; 2nd flags , Limit ( bits 16 -19) 21 | db 0x0 ; Base ( bits 24 -31) 22 | 23 | gdt_data: 24 | ; Same as code segment except for the type flags : 25 | ; type flags : ( code )0 ( expand down )0 ( writable )1 ( accessed )0 -> 0010 b 26 | dw 0xffff ; Limit ( bits 0 -15) 27 | dw 0x0 ; Base ( bits 0 -15) 28 | db 0x0 ; Base ( bits 16 -23) 29 | db 10010010b ; 1st flags , type flags 30 | db 11001111b ; 2nd flags , Limit ( bits 16 -19) 31 | db 0x0 ; Base ( bits 24 -31) 32 | 33 | gdt_end: ; The reason for putting a label at the end of the 34 | ; GDT is so we can have the assembler calculate 35 | ; the size of the GDT for the GDT decriptor ( below ) 36 | 37 | gdt_descriptor: 38 | dw gdt_end - gdt_start - 1 ; Size of our GDT , always less one 39 | ; of the true size 40 | dd gdt_start ; Start address of our GDT 41 | 42 | ; Define some handy constants for the GDT segment descriptor offsets , which 43 | ; are what segment registers must contain when in protected mode. For example , 44 | ; when we set DS = 0 x10 in PM , the CPU knows that we mean it to use the 45 | ; segment described at offset 0 x10 ( i.e. 16 bytes ) in our GDT , which in our 46 | ; case is the DATA segment (0 x0 -> NULL ; 0x08 -> CODE ; 0 x10 -> DATA ) 47 | CODE_SEG equ gdt_code - gdt_start 48 | DATA_SEG equ gdt_data - gdt_start 49 | -------------------------------------------------------------------------------- /src/boot_sector/print_string.asm: -------------------------------------------------------------------------------- 1 | print_string: 2 | mov ah, 0x0e ;indicating scrolling teletype mode of the OS 3 | 4 | loop: 5 | mov al, [bx] ;Remember [bx] stored the message. 6 | cmp al, 0 7 | je out 8 | int 0x10 ;invoking the screen-related Interrupt Service Routine 9 | ;An ISR is simply a sequence of machine instructions, much like our boot sector code, that deals with a 10 | ;specific interrupt (e.g. perhaps to read new data from a disk drive or from a network card). 11 | add bx, 0x01 12 | jmp loop 13 | 14 | out: 15 | mov al, ' ' 16 | int 0x10 17 | ret 18 | -------------------------------------------------------------------------------- /src/boot_sector/print_string_pm.asm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | 3 | VIDEO_MEMORY equ 0xb8000 4 | WHITE_ON_BLACK equ 0x0f 5 | 6 | ; data in ebx register 7 | print_string_pm: 8 | pusha 9 | mov edx, VIDEO_MEMORY 10 | 11 | print_string_pm_loop: 12 | mov al, [ebx] 13 | mov ah, WHITE_ON_BLACK 14 | 15 | cmp al, 0 16 | je done 17 | 18 | mov [edx], ax 19 | 20 | add edx, 2 21 | add ebx, 1 22 | jmp print_string_pm_loop 23 | 24 | done: 25 | popa 26 | ret 27 | -------------------------------------------------------------------------------- /src/boot_sector/switch_to_pm.asm: -------------------------------------------------------------------------------- 1 | [bits 16] 2 | 3 | switch_to_pm: 4 | cli ; We must switch off interrupts until we have 5 | ; set -up the protected mode interrupt vector 6 | ; otherwise interrupts will run riot. 7 | 8 | lgdt [gdt_descriptor] 9 | 10 | mov eax, cr0 ; see we are using 32 register (eax) here in 16 bit, we can do that 11 | or eax, 0x1 ; set the first bit of CR0, a control register 12 | mov cr0, eax 13 | 14 | jmp CODE_SEG:init_pm ;Make a far jump ( i.e. to a new segment ) to our 32 - bit 15 | ;code. 16 | 17 | [bits 32] 18 | init_pm: 19 | mov ax, DATA_SEG ; Now we are in protected mode , our old segments are meaningless , 20 | mov ds, ax ; so we point our segment registers to the 21 | mov ss, ax ; data selector we defined in our GDT 22 | mov es, ax 23 | mov fs, ax 24 | mov gs, ax 25 | 26 | mov ebp, 0x90000 ; Updating stack position so that it is right at the top of free space. 27 | mov esp, ebp ; create stack frame(MOV DST, SRC : Stackpointer <----- Basepointer) 28 | 29 | call BEGIN_PM 30 | -------------------------------------------------------------------------------- /src/disk_load.asm: -------------------------------------------------------------------------------- 1 | disk_load: 2 | push dx ; Store DX on stack so later we can recall 3 | ; how many sectors were request to be read! 4 | 5 | mov ah, 0x02 ; BIOS read sector function 6 | mov al, dh ; READ dh sectors 7 | mov ch, 0x00 ; select cylinder 0 8 | mov dh, 0x00 ; select head 0 9 | 10 | mov cl, 0x02 ; start reading from 2nd sector (after boot sector) 11 | 12 | int 0x13 ; BIOS interrupt 13 | jc disk_error 14 | 15 | pop dx ; pop dx from the stack 16 | cmp dh, al 17 | jne disk_error ; generate disk error if AL(sectors read) != DH(Expected number of sectors) 18 | 19 | ret 20 | disk_error: ; disk_error label 21 | mov bx, [DISK_ERROR_MSG] ; push the error message 22 | call print_string ; print_string is the helper to print string stored in [BX] 23 | jmp $ ; jump forever 24 | 25 | DISK_ERROR_MSG: 26 | db 'Disk read Error!', 0 27 | -------------------------------------------------------------------------------- /src/gdt.asm: -------------------------------------------------------------------------------- 1 | ; GDT tabel 2 | gdt_start: 3 | 4 | ;NULL segment 5 | gdt_null: 6 | dd 0x0 7 | dd 0x0 8 | 9 | gdt_code: 10 | ; base =0x0 , limit =0 xfffff , 11 | ; 1st flags : ( present )1 ( privilege )00 ( descriptor type )1 -> 1001 b 12 | ; type flags : ( code )1 ( conforming )0 ( readable )1 ( accessed )0 -> 1010 b 13 | ; 2nd flags : ( granularity )1 (32 - bit default )1 (64 - bit seg )0 ( AVL )0 -> 1100 b 14 | dw 0xffff ; Limit ( bits 0 -15) 15 | dw 0x0 ; Base ( bits 0 -15) 16 | db 0x0 ; Base ( bits 16 -23) 17 | db 10011010b ; 1st flags , type flags 18 | db 11001111b ; 2nd flags , Limit ( bits 16 -19) 19 | db 0x0 ; Base ( bits 24 -31) 20 | 21 | gdt_data: 22 | ; Same as code segment except for the type flags : 23 | ; type flags : ( code )0 ( expand down )0 ( writable )1 ( accessed )0 -> 0010 b 24 | dw 0xffff ; Limit ( bits 0 -15) 25 | dw 0x0 ; Base ( bits 0 -15) 26 | db 0x0 ; Base ( bits 16 -23) 27 | db 10010010b ; 1st flags , type flags 28 | db 11001111b ; 2nd flags , Limit ( bits 16 -19) 29 | db 0x0 ; Base ( bits 24 -31) 30 | 31 | gdt_end: ; The reason for putting a label at the end of the 32 | ; GDT is so we can have the assembler calculate 33 | ; the size of the GDT for the GDT decriptor ( below ) 34 | 35 | gdt_descriptor: 36 | dw gdt_end - gdt_start - 1 ; Size of our GDT , always less one 37 | ; of the true size 38 | dd gdt_start ; Start address of our GDT 39 | 40 | ; Define some handy constants for the GDT segment descriptor offsets , which 41 | ; are what segment registers must contain when in protected mode. For example , 42 | ; when we set DS = 0 x10 in PM , the CPU knows that we mean it to use the 43 | ; segment described at offset 0 x10 ( i.e. 16 bytes ) in our GDT , which in our 44 | ; case is the DATA segment (0 x0 -> NULL ; 0x08 -> CODE ; 0 x10 -> DATA ) 45 | CODE_SEG equ gdt_code - gdt_start 46 | DATA_SEG equ gdt_data - gdt_start 47 | -------------------------------------------------------------------------------- /src/kernel/.README.md.swo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernel/.README.md.swo -------------------------------------------------------------------------------- /src/kernel/README.md: -------------------------------------------------------------------------------- 1 | ```sh 2 | $ nasm boot_sect.asm -f bin -o boot_sect.bin 3 | ``` 4 | 5 | ```sh 6 | $ nasm kernel_entry.asm -f elf -o kernel_entry.o 7 | ``` 8 | 9 | ```sh 10 | $ gcc -m32 -ffreestanding -c kernel.c -o kernel.o 11 | ``` 12 | 13 | ```sh 14 | $ i386-elf-ld -o kernel.bin -Ttext 0x0500 kernel_entry.o kernel.o --oformat binary 15 | ``` 16 | 17 | ```sh 18 | $ cat boot_sect.bin kernel.bin > os-image 19 | ``` 20 | -------------------------------------------------------------------------------- /src/kernel/boot_sect.asm: -------------------------------------------------------------------------------- 1 | ; boot sector that enters 32 - bit protected mode. 2 | [org 0x7c00] 3 | KERNEL_OFFSET equ 0x1000 4 | 5 | mov [BOOT_DRIVE], dl ; BIOS stores our boot drive in DL , so it 's 6 | ; best to remember this for later. 7 | 8 | mov bp, 0x9000 9 | mov sp, bp 10 | 11 | mov bx, MSG_REAL_MODE 12 | call print_string 13 | 14 | call load_kernel 15 | 16 | call switch_to_pm 17 | jmp $ ; jump forever(jump to address of current instruction) 18 | 19 | %include "print_string.asm" 20 | %include "disk_load.asm" 21 | %include "print_string_pm.asm" 22 | %include "gdt.asm" 23 | %include "switch_to_pm.asm" 24 | 25 | [ bits 16] 26 | load_kernel: 27 | mov bx, MSG_LOAD_KERNEL ; using BX as a parameter of our function 28 | call print_string ; by specifying the address of a string 29 | 30 | mov bx, KERNEL_OFFSET 31 | mov dh, 30 32 | mov dl, [BOOT_DRIVE] 33 | ; with dx having been loaded with number of sectors to read, we will call disk_load 34 | call disk_load 35 | ret 36 | 37 | [bits 32] 38 | BEGIN_PM: 39 | mov ebx, MSG_PROT_MODE 40 | call print_string_pm 41 | 42 | call KERNEL_OFFSET 43 | 44 | jmp $ 45 | 46 | BOOT_DRIVE: 47 | db 0 48 | 49 | MSG_REAL_MODE: 50 | db " Hello GeekSkool,Started in 16- bit Real Mode ", 0 51 | 52 | MSG_PROT_MODE: 53 | db " Successfully landed in 32- bit Protected Mode ", 0 54 | 55 | MSG_LOAD_KERNEL: 56 | db " Loading kernel into memory. ", 0 57 | 58 | times 510-($-$$) db 0 ; Pad the boot sector out with zeros 59 | ; When compiled , our program must fit into 512 bytes , 60 | ; with the last two bytes being the magic number , 61 | ; so here , tell our assembly compiler to pad out our 62 | ; program with enough zero bytes (db 0) to bring us to the 63 | ; 510 th byte. 64 | dw 0xaa55 ; Last two bytes form the magic number 65 | ; BIOS is informed that we are a boot sector 66 | -------------------------------------------------------------------------------- /src/kernel/boot_sect.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernel/boot_sect.bin -------------------------------------------------------------------------------- /src/kernel/disk_load.asm: -------------------------------------------------------------------------------- 1 | disk_load: 2 | push dx ; Store DX on stack so later we can recall 3 | ; how many sectors were request to be read! 4 | 5 | mov ah, 0x02 ; BIOS read sector function 6 | mov al, dh ; READ dh sectors 7 | mov ch, 0x00 ; select cylinder 0 8 | mov dh, 0x00 ; select head 0 9 | 10 | mov cl, 0x02 ; start reading from 2nd sector (after boot sector) 11 | 12 | int 0x13 ; BIOS interrupt 13 | jc disk_error 14 | 15 | pop dx ; pop dx from the stack 16 | cmp dh, al 17 | jne disk_error ; generate disk error if AL(sectors read) != DH(Expected number of sectors) 18 | 19 | ret 20 | disk_error: ; disk_error label 21 | mov bx, [DISK_ERROR_MSG] ; push the error message 22 | call print_string ; print_string is the helper to print string stored in [BX] 23 | jmp $ ; jump forever 24 | 25 | DISK_ERROR_MSG: 26 | db 'Disk read Error!', 0 27 | -------------------------------------------------------------------------------- /src/kernel/gdt.asm: -------------------------------------------------------------------------------- 1 | ; GDT tabel 2 | gdt_start: 3 | 4 | ;NULL segment 5 | gdt_null: 6 | dd 0x0 7 | dd 0x0 8 | ;define two segments, one for code and another for data 9 | ;there is no memory protection as these segments can overlap 10 | 11 | gdt_code: 12 | ; base =0x0 , limit =0 xfffff , 13 | ; 1st flags : ( present )1 ( privilege )00 ( descriptor type )1 -> 1001 b 14 | ; type flags : ( code )1 ( conforming )0 ( readable )1 ( accessed )0 -> 1010 b 15 | ; 2nd flags : ( granularity )1 (32 - bit default )1 (64 - bit seg )0 ( AVL )0 -> 1100 b 16 | dw 0xffff ; Limit ( bits 0 -15) 17 | dw 0x0 ; Base ( bits 0 -15) 18 | db 0x0 ; Base ( bits 16 -23) 19 | db 10011010b ; 1st flags , type flags 20 | db 11001111b ; 2nd flags , Limit ( bits 16 -19) 21 | db 0x0 ; Base ( bits 24 -31) 22 | 23 | gdt_data: 24 | ; Same as code segment except for the type flags : 25 | ; type flags : ( code )0 ( expand down )0 ( writable )1 ( accessed )0 -> 0010 b 26 | dw 0xffff ; Limit ( bits 0 -15) 27 | dw 0x0 ; Base ( bits 0 -15) 28 | db 0x0 ; Base ( bits 16 -23) 29 | db 10010010b ; 1st flags , type flags 30 | db 11001111b ; 2nd flags , Limit ( bits 16 -19) 31 | db 0x0 ; Base ( bits 24 -31) 32 | 33 | gdt_end: ; The reason for putting a label at the end of the 34 | ; GDT is so we can have the assembler calculate 35 | ; the size of the GDT for the GDT decriptor ( below ) 36 | 37 | gdt_descriptor: 38 | dw gdt_end - gdt_start - 1 ; Size of our GDT , always less one 39 | ; of the true size 40 | dd gdt_start ; Start address of our GDT 41 | 42 | ; Define some handy constants for the GDT segment descriptor offsets , which 43 | ; are what segment registers must contain when in protected mode. For example , 44 | ; when we set DS = 0 x10 in PM , the CPU knows that we mean it to use the 45 | ; segment described at offset 0 x10 ( i.e. 16 bytes ) in our GDT , which in our 46 | ; case is the DATA segment (0 x0 -> NULL ; 0x08 -> CODE ; 0 x10 -> DATA ) 47 | CODE_SEG equ gdt_code - gdt_start 48 | DATA_SEG equ gdt_data - gdt_start 49 | -------------------------------------------------------------------------------- /src/kernel/kernel.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernel/kernel.bin -------------------------------------------------------------------------------- /src/kernel/kernel.c: -------------------------------------------------------------------------------- 1 | /* This will force us to create a kernel entry function instead of jumping to kernel.c:0x00 */ 2 | void dummy_test_entrypoint() 3 | { 4 | 5 | } 6 | 7 | void main() { 8 | char* video_memory = (char*) 0xb8000; 9 | *video_memory = 'X'; 10 | } 11 | -------------------------------------------------------------------------------- /src/kernel/kernel.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernel/kernel.o -------------------------------------------------------------------------------- /src/kernel/kernel_entry.asm: -------------------------------------------------------------------------------- 1 | ;The linker takes object files as inputs, then joins them together, 2 | ;but resolves any labels to their correct addresses. 3 | 4 | [bits 32] 5 | [extern main] ; Define calling point. Must have same name as kernel.c 'main' function 6 | call main ; Calls the C function. The linker will know where it is placed in memory 7 | jmp $ 8 | -------------------------------------------------------------------------------- /src/kernel/kernel_entry.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernel/kernel_entry.o -------------------------------------------------------------------------------- /src/kernel/os-image: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernel/os-image -------------------------------------------------------------------------------- /src/kernel/print_string.asm: -------------------------------------------------------------------------------- 1 | print_string: 2 | mov ah, 0x0e 3 | 4 | loop: 5 | mov al, [bx] ; 6 | cmp al, 0 7 | je out 8 | int 0x10 ; invoking the screen-related Interrupt Service Routine 9 | ;An ISR is simply a sequence of machine instructions, much like our boot sector code, that deals with a 10 | ;specific interrupt (e.g. perhaps to read new data from a disk drive or from a network card). 11 | add bx, 0x01 12 | jmp loop 13 | 14 | out: 15 | mov al, ' ' 16 | int 0x10 17 | ret 18 | -------------------------------------------------------------------------------- /src/kernel/print_string_pm.asm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | 3 | VIDEO_MEMORY equ 0xb8000 4 | WHITE_ON_BLACK equ 0x0f 5 | 6 | ; data in ebx register 7 | print_string_pm: 8 | pusha 9 | mov edx, VIDEO_MEMORY 10 | 11 | print_string_pm_loop: 12 | mov al, [ebx] 13 | mov ah, WHITE_ON_BLACK 14 | 15 | cmp al, 0 16 | je done 17 | 18 | mov [edx], ax 19 | 20 | add edx, 2 21 | add ebx, 1 22 | jmp print_string_pm_loop 23 | 24 | done: 25 | popa 26 | ret 27 | -------------------------------------------------------------------------------- /src/kernel/switch_to_pm.asm: -------------------------------------------------------------------------------- 1 | [bits 16] 2 | 3 | switch_to_pm: 4 | cli ; We must switch off interrupts until we have 5 | ; set -up the protected mode interrupt vector 6 | ; otherwise interrupts will run riot. 7 | 8 | lgdt [gdt_descriptor] 9 | 10 | mov eax, cr0 ; see we are using 32 register (eax) here in 16 bit, we can do that 11 | or eax, 0x1 ; set the first bit of CR0, a control register 12 | mov cr0, eax 13 | 14 | jmp CODE_SEG:init_pm ;Make a far jump ( i.e. to a new segment ) to our 32 - bit 15 | ;code. 16 | 17 | [bits 32] 18 | init_pm: 19 | mov ax, DATA_SEG ; Now we are in protected mode , our old segments are meaningless , 20 | mov ds, ax ; so we point our segment registers to the 21 | mov ss, ax ; data selector we defined in our GDT 22 | mov es, ax 23 | mov fs, ax 24 | mov gs, ax 25 | 26 | mov ebp, 0x90000 ; Updating stack position so that it is right at the top of free space. 27 | mov esp, ebp ; create stack frame(MOV DST, SRC : Stackpointer <----- Basepointer) 28 | 29 | call BEGIN_PM 30 | -------------------------------------------------------------------------------- /src/kernelVanilla/.README.md.swo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernelVanilla/.README.md.swo -------------------------------------------------------------------------------- /src/kernelVanilla/README.md: -------------------------------------------------------------------------------- 1 | ```sh 2 | $ nasm boot_sect.asm -f bin -o boot_sect.bin 3 | ``` 4 | 5 | ```sh 6 | $ nasm kernel_entry.asm -f elf -o kernel_entry.o 7 | ``` 8 | 9 | ```sh 10 | $ gcc -m32 -ffreestanding -c kernel.c -o kernel.o 11 | ``` 12 | 13 | ```sh 14 | $ i386-elf-ld -o kernel.bin -Ttext 0x0500 kernel_entry.o kernel.o --oformat binary 15 | ``` 16 | 17 | ```sh 18 | $ cat boot_sect.bin kernel.bin > os-image 19 | ``` 20 | 21 | ```sh 22 | $ qemu-system-i386 -fda os-image 23 | -------------------------------------------------------------------------------- /src/kernelVanilla/boot_sect.asm: -------------------------------------------------------------------------------- 1 | ; boot sector that enters 32 - bit protected mode. 2 | [org 0x7c00] 3 | KERNEL_OFFSET equ 0x1000 4 | 5 | mov [BOOT_DRIVE], dl ; BIOS stores our boot drive in DL , so it 's 6 | ; best to remember this for later. 7 | 8 | mov bp, 0x9000 9 | mov sp, bp 10 | 11 | mov bx, MSG_REAL_MODE 12 | call print_string 13 | 14 | call load_kernel 15 | 16 | call switch_to_pm 17 | jmp $ ; jump forever(jump to address of current instruction) 18 | 19 | %include "print_string.asm" 20 | %include "disk_load.asm" 21 | %include "print_string_pm.asm" 22 | %include "gdt.asm" 23 | %include "switch_to_pm.asm" 24 | 25 | [ bits 16] 26 | load_kernel: 27 | mov bx, MSG_LOAD_KERNEL ; using BX as a parameter of our function 28 | call print_string ; by specifying the address of a string 29 | 30 | mov bx, KERNEL_OFFSET 31 | mov dh, 30 32 | mov dl, [BOOT_DRIVE] 33 | ; with dx having been loaded with number of sectors to read, we will call disk_load 34 | call disk_load 35 | ret 36 | 37 | [bits 32] 38 | BEGIN_PM: 39 | mov ebx, MSG_PROT_MODE 40 | call print_string_pm 41 | 42 | call KERNEL_OFFSET 43 | 44 | jmp $ 45 | 46 | BOOT_DRIVE: 47 | db 0 48 | 49 | MSG_REAL_MODE: 50 | db " Hello GeekSkool,Started in 16- bit Real Mode ", 0 51 | 52 | MSG_PROT_MODE: 53 | db " Successfully landed in 32- bit Protected Mode ", 0 54 | 55 | MSG_LOAD_KERNEL: 56 | db " Loading kernel into memory. ", 0 57 | 58 | times 510-($-$$) db 0 ; Pad the boot sector out with zeros 59 | ; When compiled , our program must fit into 512 bytes , 60 | ; with the last two bytes being the magic number , 61 | ; so here , tell our assembly compiler to pad out our 62 | ; program with enough zero bytes (db 0) to bring us to the 63 | ; 510 th byte. 64 | dw 0xaa55 ; Last two bytes form the magic number 65 | ; BIOS is informed that we are a boot sector 66 | -------------------------------------------------------------------------------- /src/kernelVanilla/boot_sect.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernelVanilla/boot_sect.bin -------------------------------------------------------------------------------- /src/kernelVanilla/disk_load.asm: -------------------------------------------------------------------------------- 1 | disk_load: 2 | push dx ; Store DX on stack so later we can recall 3 | ; how many sectors were request to be read! 4 | 5 | mov ah, 0x02 ; BIOS read sector function 6 | mov al, dh ; READ dh sectors 7 | mov ch, 0x00 ; select cylinder 0 8 | mov dh, 0x00 ; select head 0 9 | 10 | mov cl, 0x02 ; start reading from 2nd sector (after boot sector) 11 | 12 | int 0x13 ; BIOS interrupt 13 | jc disk_error 14 | 15 | pop dx ; pop dx from the stack 16 | cmp dh, al 17 | jne disk_error ; generate disk error if AL(sectors read) != DH(Expected number of sectors) 18 | 19 | ret 20 | disk_error: ; disk_error label 21 | mov bx, [DISK_ERROR_MSG] ; push the error message 22 | call print_string ; print_string is the helper to print string stored in [BX] 23 | jmp $ ; jump forever 24 | 25 | DISK_ERROR_MSG: 26 | db 'Disk read Error!', 0 27 | -------------------------------------------------------------------------------- /src/kernelVanilla/gdt.asm: -------------------------------------------------------------------------------- 1 | ; GDT tabel 2 | gdt_start: 3 | 4 | ;NULL segment 5 | gdt_null: 6 | dd 0x0 7 | dd 0x0 8 | ;define two segments, one for code and another for data 9 | ;there is no memory protection as these segments can overlap 10 | 11 | gdt_code: 12 | ; base =0x0 , limit =0 xfffff , 13 | ; 1st flags : ( present )1 ( privilege )00 ( descriptor type )1 -> 1001 b 14 | ; type flags : ( code )1 ( conforming )0 ( readable )1 ( accessed )0 -> 1010 b 15 | ; 2nd flags : ( granularity )1 (32 - bit default )1 (64 - bit seg )0 ( AVL )0 -> 1100 b 16 | dw 0xffff ; Limit ( bits 0 -15) 17 | dw 0x0 ; Base ( bits 0 -15) 18 | db 0x0 ; Base ( bits 16 -23) 19 | db 10011010b ; 1st flags , type flags 20 | db 11001111b ; 2nd flags , Limit ( bits 16 -19) 21 | db 0x0 ; Base ( bits 24 -31) 22 | 23 | gdt_data: 24 | ; Same as code segment except for the type flags : 25 | ; type flags : ( code )0 ( expand down )0 ( writable )1 ( accessed )0 -> 0010 b 26 | dw 0xffff ; Limit ( bits 0 -15) 27 | dw 0x0 ; Base ( bits 0 -15) 28 | db 0x0 ; Base ( bits 16 -23) 29 | db 10010010b ; 1st flags , type flags 30 | db 11001111b ; 2nd flags , Limit ( bits 16 -19) 31 | db 0x0 ; Base ( bits 24 -31) 32 | 33 | gdt_end: ; The reason for putting a label at the end of the 34 | ; GDT is so we can have the assembler calculate 35 | ; the size of the GDT for the GDT decriptor ( below ) 36 | 37 | gdt_descriptor: 38 | dw gdt_end - gdt_start - 1 ; Size of our GDT , always less one 39 | ; of the true size 40 | dd gdt_start ; Start address of our GDT 41 | 42 | ; Define some handy constants for the GDT segment descriptor offsets , which 43 | ; are what segment registers must contain when in protected mode. For example , 44 | ; when we set DS = 0 x10 in PM , the CPU knows that we mean it to use the 45 | ; segment described at offset 0 x10 ( i.e. 16 bytes ) in our GDT , which in our 46 | ; case is the DATA segment (0 x0 -> NULL ; 0x08 -> CODE ; 0 x10 -> DATA ) 47 | CODE_SEG equ gdt_code - gdt_start 48 | DATA_SEG equ gdt_data - gdt_start 49 | -------------------------------------------------------------------------------- /src/kernelVanilla/kernel.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernelVanilla/kernel.bin -------------------------------------------------------------------------------- /src/kernelVanilla/kernel.c: -------------------------------------------------------------------------------- 1 | /* This will force us to create a kernel entry function instead of jumping to kernel.c:0x00 */ 2 | void dummy_test_entrypoint() 3 | { 4 | 5 | } 6 | 7 | void main() { 8 | char* video_memory = (char*) 0xb8000; 9 | *video_memory = 'X'; 10 | } 11 | -------------------------------------------------------------------------------- /src/kernelVanilla/kernel.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernelVanilla/kernel.o -------------------------------------------------------------------------------- /src/kernelVanilla/kernel_entry.asm: -------------------------------------------------------------------------------- 1 | ;The linker takes object files as inputs, then joins them together, 2 | ;but resolves any labels to their correct addresses. 3 | 4 | [bits 32] 5 | [extern main] ; Define calling point. Must have same name as kernel.c 'main' function 6 | call main ; Calls the C function. The linker will know where it is placed in memory 7 | jmp $ 8 | -------------------------------------------------------------------------------- /src/kernelVanilla/kernel_entry.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernelVanilla/kernel_entry.o -------------------------------------------------------------------------------- /src/kernelVanilla/os-image: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernelVanilla/os-image -------------------------------------------------------------------------------- /src/kernelVanilla/print_string.asm: -------------------------------------------------------------------------------- 1 | print_string: 2 | mov ah, 0x0e 3 | 4 | loop: 5 | mov al, [bx] ; 6 | cmp al, 0 7 | je out 8 | int 0x10 ; invoking the screen-related Interrupt Service Routine 9 | ;An ISR is simply a sequence of machine instructions, much like our boot sector code, that deals with a 10 | ;specific interrupt (e.g. perhaps to read new data from a disk drive or from a network card). 11 | add bx, 0x01 12 | jmp loop 13 | 14 | out: 15 | mov al, ' ' 16 | int 0x10 17 | ret 18 | -------------------------------------------------------------------------------- /src/kernelVanilla/print_string_pm.asm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | 3 | VIDEO_MEMORY equ 0xb8000 4 | WHITE_ON_BLACK equ 0x0f 5 | 6 | ; data in ebx register 7 | print_string_pm: 8 | pusha 9 | mov edx, VIDEO_MEMORY 10 | 11 | print_string_pm_loop: 12 | mov al, [ebx] 13 | mov ah, WHITE_ON_BLACK 14 | 15 | cmp al, 0 16 | je done 17 | 18 | mov [edx], ax 19 | 20 | add edx, 2 21 | add ebx, 1 22 | jmp print_string_pm_loop 23 | 24 | done: 25 | popa 26 | ret 27 | -------------------------------------------------------------------------------- /src/kernelVanilla/switch_to_pm.asm: -------------------------------------------------------------------------------- 1 | [bits 16] 2 | 3 | switch_to_pm: 4 | cli ; We must switch off interrupts until we have 5 | ; set -up the protected mode interrupt vector 6 | ; otherwise interrupts will run riot. 7 | 8 | lgdt [gdt_descriptor] 9 | 10 | mov eax, cr0 ; see we are using 32 register (eax) here in 16 bit, we can do that 11 | or eax, 0x1 ; set the first bit of CR0, a control register 12 | mov cr0, eax 13 | 14 | jmp CODE_SEG:init_pm ;Make a far jump ( i.e. to a new segment ) to our 32 - bit 15 | ;code. 16 | 17 | [bits 32] 18 | init_pm: 19 | mov ax, DATA_SEG ; Now we are in protected mode , our old segments are meaningless , 20 | mov ds, ax ; so we point our segment registers to the 21 | mov ss, ax ; data selector we defined in our GDT 22 | mov es, ax 23 | mov fs, ax 24 | mov gs, ax 25 | 26 | mov ebp, 0x90000 ; Updating stack position so that it is right at the top of free space. 27 | mov esp, ebp ; create stack frame(MOV DST, SRC : Stackpointer <----- Basepointer) 28 | 29 | call BEGIN_PM 30 | -------------------------------------------------------------------------------- /src/kernelVideoDrivers/Makefile: -------------------------------------------------------------------------------- 1 | C_SOURCES = $(wildcard kernel/*.c drivers/*.c) 2 | HEADERS = $(wildcard kernel/*.h drivers/*.h) 3 | # Nice syntax for file extension replacement 4 | OBJ = ${C_SOURCES:.c=.o} 5 | 6 | # Change this if your cross-compiler is somewhere else 7 | CC = gcc -m32 8 | #GDB = /usr/local/i386elfgcc/bin/i386-elf-gdb 9 | # -g: Use debugging symbols in gcc 10 | CFLAGS = -g 11 | 12 | # First rule is run by default 13 | os-image.bin: boot/bootsect.bin kernel.bin 14 | cat $^ > os-image.bin 15 | 16 | # '--oformat binary' deletes all symbols as a collateral, so we don't need 17 | # to 'strip' them manually on this case 18 | kernel.bin: boot/kernel_entry.o ${OBJ} 19 | i386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary 20 | 21 | # Used for debugging purposes 22 | kernel.elf: boot/kernel_entry.o ${OBJ} 23 | i386-elf-ld -o $@ -Ttext 0x1000 $^ 24 | 25 | run: os-image.bin 26 | qemu-system-i386 -fda os-image.bin 27 | 28 | # Open the connection to qemu and load our kernel-object file with symbols 29 | debug: os-image.bin kernel.elf 30 | qemu-system-i386 -s -fda os-image.bin & 31 | # ${GDB} -ex "target remote localhost:1234" -ex "symbol-file kernel.elf" 32 | 33 | # Generic rules for wildcards 34 | # To make an object, always compile from its .c 35 | %.o: %.c ${HEADERS} 36 | ${CC} ${CFLAGS} -ffreestanding -c $< -o $@ 37 | 38 | %.o: %.asm 39 | nasm $< -f elf -o $@ 40 | 41 | %.bin: %.asm 42 | nasm $< -f bin -o $@ 43 | 44 | clean: 45 | rm -rf *.bin *.dis *.o os-image.bin *.elf 46 | rm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o 47 | -------------------------------------------------------------------------------- /src/kernelVideoDrivers/README.md: -------------------------------------------------------------------------------- 1 | *Concepts you may want to Google beforehand: VGA character cells, screen offset* 2 | 3 | **Goal: Write strings on the screen** 4 | 5 | Finally, we are going to be able to output text on the screen. This lesson contains 6 | a bit more code than usual, so let's go step by step. 7 | 8 | Open `drivers/screen.h` and you'll see that we have defined some constants for the VGA 9 | card driver and three public functions, one to clear the screen and another couple 10 | to write strings, the famously named `kprint` for "kernel print" 11 | 12 | Now open `drivers/screen.c`. It starts with the declaration of private helper functions 13 | that we will use to aid our `kprint` kernel API. 14 | 15 | There are the two I/O port access routines that we learned in the previous lesson, 16 | `get` and `set_cursor_offset()`. 17 | 18 | Then there is the routine that directly manipulates the video memory, `print_char()` 19 | 20 | Finally, there are three small helper functions to transform rows and columns into offsets 21 | and vice versa. 22 | 23 | 24 | kprint_at 25 | --------- 26 | 27 | `kprint_at` may be called with a `-1` value for `col` and `row`, which indicates that 28 | we will print the string at the current cursor position. 29 | 30 | It first sets three variables for the col/row and the offset. Then it iterates through 31 | the `char*` and calls `print_char()` with the current coordinates. 32 | 33 | Note that `print_char` itself returns the offset of the next cursor position, and we reuse 34 | it for the next loop. 35 | 36 | `kprint` is basically a wrapper for `kprint_at` 37 | 38 | 39 | 40 | print_char 41 | ---------- 42 | 43 | Like `kprint_at`, `print_char` allows cols/rows to be `-1`. In that case it retrieves 44 | the cursor position from the hardware, using the `ports.c` routines. 45 | 46 | `print_char` also handles newlines. In that case, we will position the cursor offset 47 | to column 0 of the next row. 48 | 49 | Remember that the VGA cells take two bytes, one for the character itself and another one 50 | for the attribute. 51 | 52 | 53 | kernel.c 54 | -------- 55 | 56 | Our new kernel is finally able to print strings. 57 | 58 | It tests correct character positioning, spanning through multiple lines, line breaks, 59 | and finally it tries to write outside of the screen bounds. What happens then? 60 | 61 | In the next lesson we will learn how to scroll the screen. 62 | -------------------------------------------------------------------------------- /src/kernelVideoDrivers/boot/32bit_print.asm: -------------------------------------------------------------------------------- 1 | [bits 32] ; using 32-bit protected mode 2 | 3 | ; this is how constants are defined 4 | VIDEO_MEMORY equ 0xb8000 5 | WHITE_OB_BLACK equ 0x0f ; the color byte for each character 6 | 7 | print_string_pm: 8 | pusha 9 | mov edx, VIDEO_MEMORY 10 | 11 | print_string_pm_loop: 12 | mov al, [ebx] ; [ebx] is the address of our character 13 | mov ah, WHITE_OB_BLACK 14 | 15 | cmp al, 0 ; check if end of string 16 | je print_string_pm_done 17 | 18 | mov [edx], ax ; store character + attribute in video memory 19 | add ebx, 1 ; next char 20 | add edx, 2 ; next video memory position 21 | 22 | jmp print_string_pm_loop 23 | 24 | print_string_pm_done: 25 | popa 26 | ret 27 | -------------------------------------------------------------------------------- /src/kernelVideoDrivers/boot/bootsect.asm: -------------------------------------------------------------------------------- 1 | ; Identical to lesson 13's boot sector, but the %included files have new paths 2 | [org 0x7c00] 3 | KERNEL_OFFSET equ 0x1000 ; The same one we used when linking the kernel 4 | 5 | mov [BOOT_DRIVE], dl ; Remember that the BIOS sets us the boot drive in 'dl' on boot 6 | mov bp, 0x9000 7 | mov sp, bp 8 | 9 | mov bx, MSG_REAL_MODE 10 | call print 11 | call print_nl 12 | 13 | call load_kernel ; read the kernel from disk 14 | call switch_to_pm ; disable interrupts, load GDT, etc. Finally jumps to 'BEGIN_PM' 15 | jmp $ ; Never executed 16 | 17 | %include "boot/print.asm" 18 | %include "boot/print_hex.asm" 19 | %include "boot/disk.asm" 20 | %include "boot/gdt.asm" 21 | %include "boot/32bit_print.asm" 22 | %include "boot/switch_pm.asm" 23 | 24 | [bits 16] 25 | load_kernel: 26 | mov bx, MSG_LOAD_KERNEL 27 | call print 28 | call print_nl 29 | 30 | mov bx, KERNEL_OFFSET ; Read from disk and store in 0x1000 31 | mov dh, 16 ; Our future kernel will be larger, make this big 32 | mov dl, [BOOT_DRIVE] 33 | call disk_load 34 | ret 35 | 36 | [bits 32] 37 | BEGIN_PM: 38 | mov ebx, MSG_PROT_MODE 39 | call print_string_pm 40 | call KERNEL_OFFSET ; Give control to the kernel 41 | jmp $ ; Stay here when the kernel returns control to us (if ever) 42 | 43 | 44 | BOOT_DRIVE db 0 ; It is a good idea to store it in memory because 'dl' may get overwritten 45 | MSG_REAL_MODE db "Started in 16-bit Real Mode", 0 46 | MSG_PROT_MODE db "Landed in 32-bit Protected Mode", 0 47 | MSG_LOAD_KERNEL db "Loading kernel into memory", 0 48 | 49 | ; padding 50 | times 510 - ($-$$) db 0 51 | dw 0xaa55 52 | -------------------------------------------------------------------------------- /src/kernelVideoDrivers/boot/bootsect.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernelVideoDrivers/boot/bootsect.bin -------------------------------------------------------------------------------- /src/kernelVideoDrivers/boot/disk.asm: -------------------------------------------------------------------------------- 1 | ; load 'dh' sectors from drive 'dl' into ES:BX 2 | disk_load: 3 | pusha 4 | ; reading from disk requires setting specific values in all registers 5 | ; so we will overwrite our input parameters from 'dx'. Let's save it 6 | ; to the stack for later use. 7 | push dx 8 | 9 | mov ah, 0x02 ; ah <- int 0x13 function. 0x02 = 'read' 10 | mov al, dh ; al <- number of sectors to read (0x01 .. 0x80) 11 | mov cl, 0x02 ; cl <- sector (0x01 .. 0x11) 12 | ; 0x01 is our boot sector, 0x02 is the first 'available' sector 13 | mov ch, 0x00 ; ch <- cylinder (0x0 .. 0x3FF, upper 2 bits in 'cl') 14 | ; dl <- drive number. Our caller sets it as a parameter and gets it from BIOS 15 | ; (0 = floppy, 1 = floppy2, 0x80 = hdd, 0x81 = hdd2) 16 | mov dh, 0x00 ; dh <- head number (0x0 .. 0xF) 17 | 18 | ; [es:bx] <- pointer to buffer where the data will be stored 19 | ; caller sets it up for us, and it is actually the standard location for int 13h 20 | int 0x13 ; BIOS interrupt 21 | jc disk_error ; if error (stored in the carry bit) 22 | 23 | pop dx 24 | cmp al, dh ; BIOS also sets 'al' to the # of sectors read. Compare it. 25 | jne sectors_error 26 | popa 27 | ret 28 | 29 | 30 | disk_error: 31 | mov bx, DISK_ERROR 32 | call print 33 | call print_nl 34 | mov dh, ah ; ah = error code, dl = disk drive that dropped the error 35 | call print_hex ; check out the code at http://stanislavs.org/helppc/int_13-1.html 36 | jmp disk_loop 37 | 38 | sectors_error: 39 | mov bx, SECTORS_ERROR 40 | call print 41 | 42 | disk_loop: 43 | jmp $ 44 | 45 | DISK_ERROR: db "Disk read error", 0 46 | SECTORS_ERROR: db "Incorrect number of sectors read", 0 47 | -------------------------------------------------------------------------------- /src/kernelVideoDrivers/boot/gdt.asm: -------------------------------------------------------------------------------- 1 | gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps 2 | ; the GDT starts with a null 8-byte 3 | dd 0x0 ; 4 byte 4 | dd 0x0 ; 4 byte 5 | 6 | ; GDT for code segment. base = 0x00000000, length = 0xfffff 7 | ; for flags, refer to os-dev.pdf document, page 36 8 | gdt_code: 9 | dw 0xffff ; segment length, bits 0-15 10 | dw 0x0 ; segment base, bits 0-15 11 | db 0x0 ; segment base, bits 16-23 12 | db 10011010b ; flags (8 bits) 13 | db 11001111b ; flags (4 bits) + segment length, bits 16-19 14 | db 0x0 ; segment base, bits 24-31 15 | 16 | ; GDT for data segment. base and length identical to code segment 17 | ; some flags changed, again, refer to os-dev.pdf 18 | gdt_data: 19 | dw 0xffff 20 | dw 0x0 21 | db 0x0 22 | db 10010010b 23 | db 11001111b 24 | db 0x0 25 | 26 | gdt_end: 27 | 28 | ; GDT descriptor 29 | gdt_descriptor: 30 | dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size 31 | dd gdt_start ; address (32 bit) 32 | 33 | ; define some constants for later use 34 | CODE_SEG equ gdt_code - gdt_start 35 | DATA_SEG equ gdt_data - gdt_start 36 | -------------------------------------------------------------------------------- /src/kernelVideoDrivers/boot/kernel_entry.asm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | [extern main] ; Define calling point. Must have same name as kernel.c 'main' function 3 | call main ; Calls the C function. The linker will know where it is placed in memory 4 | jmp $ 5 | -------------------------------------------------------------------------------- /src/kernelVideoDrivers/boot/kernel_entry.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernelVideoDrivers/boot/kernel_entry.o -------------------------------------------------------------------------------- /src/kernelVideoDrivers/boot/print.asm: -------------------------------------------------------------------------------- 1 | print: 2 | pusha 3 | 4 | ; keep this in mind: 5 | ; while (string[i] != 0) { print string[i]; i++ } 6 | 7 | ; the comparison for string end (null byte) 8 | start: 9 | mov al, [bx] ; 'bx' is the base address for the string 10 | cmp al, 0 11 | je done 12 | 13 | ; the part where we print with the BIOS help 14 | mov ah, 0x0e 15 | int 0x10 ; 'al' already contains the char 16 | 17 | ; increment pointer and do next loop 18 | add bx, 1 19 | jmp start 20 | 21 | done: 22 | popa 23 | ret 24 | 25 | 26 | 27 | print_nl: 28 | pusha 29 | 30 | mov ah, 0x0e 31 | mov al, 0x0a ; newline char 32 | int 0x10 33 | mov al, 0x0d ; carriage return 34 | int 0x10 35 | 36 | popa 37 | ret 38 | -------------------------------------------------------------------------------- /src/kernelVideoDrivers/boot/print_hex.asm: -------------------------------------------------------------------------------- 1 | ; receiving the data in 'dx' 2 | ; For the examples we'll assume that we're called with dx=0x1234 3 | print_hex: 4 | pusha 5 | 6 | mov cx, 0 ; our index variable 7 | 8 | ; Strategy: get the last char of 'dx', then convert to ASCII 9 | ; Numeric ASCII values: '0' (ASCII 0x30) to '9' (0x39), so just add 0x30 to byte N. 10 | ; For alphabetic characters A-F: 'A' (ASCII 0x41) to 'F' (0x46) we'll add 0x40 11 | ; Then, move the ASCII byte to the correct position on the resulting string 12 | hex_loop: 13 | cmp cx, 4 ; loop 4 times 14 | je end 15 | 16 | ; 1. convert last char of 'dx' to ascii 17 | mov ax, dx ; we will use 'ax' as our working register 18 | and ax, 0x000f ; 0x1234 -> 0x0004 by masking first three to zeros 19 | add al, 0x30 ; add 0x30 to N to convert it to ASCII "N" 20 | cmp al, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F' 21 | jle step2 22 | add al, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7 23 | 24 | step2: 25 | ; 2. get the correct position of the string to place our ASCII char 26 | ; bx <- base address + string length - index of char 27 | mov bx, HEX_OUT + 5 ; base + length 28 | sub bx, cx ; our index variable 29 | mov [bx], al ; copy the ASCII char on 'al' to the position pointed by 'bx' 30 | ror dx, 4 ; 0x1234 -> 0x4123 -> 0x3412 -> 0x2341 -> 0x1234 31 | 32 | ; increment index and loop 33 | add cx, 1 34 | jmp hex_loop 35 | 36 | end: 37 | ; prepare the parameter and call the function 38 | ; remember that print receives parameters in 'bx' 39 | mov bx, HEX_OUT 40 | call print 41 | 42 | popa 43 | ret 44 | 45 | HEX_OUT: 46 | db '0x0000',0 ; reserve memory for our new string 47 | -------------------------------------------------------------------------------- /src/kernelVideoDrivers/boot/switch_pm.asm: -------------------------------------------------------------------------------- 1 | [bits 16] 2 | switch_to_pm: 3 | cli ; 1. disable interrupts 4 | lgdt [gdt_descriptor] ; 2. load the GDT descriptor 5 | mov eax, cr0 6 | or eax, 0x1 ; 3. set 32-bit mode bit in cr0 7 | mov cr0, eax 8 | jmp CODE_SEG:init_pm ; 4. far jump by using a different segment 9 | 10 | [bits 32] 11 | init_pm: ; we are now using 32-bit instructions 12 | mov ax, DATA_SEG ; 5. update the segment registers 13 | mov ds, ax 14 | mov ss, ax 15 | mov es, ax 16 | mov fs, ax 17 | mov gs, ax 18 | 19 | mov ebp, 0x90000 ; 6. update the stack right at the top of the free space 20 | mov esp, ebp 21 | 22 | call BEGIN_PM ; 7. Call a well-known label with useful code 23 | -------------------------------------------------------------------------------- /src/kernelVideoDrivers/drivers/ports.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Read a byte from the specified port 3 | */ 4 | unsigned char port_byte_in (unsigned short port) { 5 | unsigned char result; 6 | /* Inline assembler syntax 7 | * !! Notice how the source and destination registers are switched from NASM !! 8 | * 9 | * '"=a" (result)'; set '=' the C variable '(result)' to the value of register e'a'x 10 | * '"d" (port)': map the C variable '(port)' into e'd'x register 11 | * 12 | * Inputs and outputs are separated by colons 13 | */ 14 | __asm__("in %%dx, %%al" : "=a" (result) : "d" (port)); 15 | return result; 16 | } 17 | 18 | void port_byte_out (unsigned short port, unsigned char data) { 19 | /* Notice how here both registers are mapped to C variables and 20 | * nothing is returned, thus, no equals '=' in the asm syntax 21 | * However we see a comma since there are two variables in the input area 22 | * and none in the 'return' area 23 | */ 24 | __asm__("out %%al, %%dx" : : "a" (data), "d" (port)); 25 | } 26 | 27 | unsigned short port_word_in (unsigned short port) { 28 | unsigned short result; 29 | __asm__("in %%dx, %%ax" : "=a" (result) : "d" (port)); 30 | return result; 31 | } 32 | 33 | void port_word_out (unsigned short port, unsigned short data) { 34 | __asm__("out %%ax, %%dx" : : "a" (data), "d" (port)); 35 | } 36 | -------------------------------------------------------------------------------- /src/kernelVideoDrivers/drivers/ports.h: -------------------------------------------------------------------------------- 1 | unsigned char port_byte_in (unsigned short port); 2 | void port_byte_out (unsigned short port, unsigned char data); 3 | unsigned short port_word_in (unsigned short port); 4 | void port_word_out (unsigned short port, unsigned short data); 5 | -------------------------------------------------------------------------------- /src/kernelVideoDrivers/drivers/ports.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernelVideoDrivers/drivers/ports.o -------------------------------------------------------------------------------- /src/kernelVideoDrivers/drivers/screen.c: -------------------------------------------------------------------------------- 1 | #include "screen.h" 2 | #include "ports.h" 3 | 4 | /* Declaration of private functions */ 5 | int get_cursor_offset(); 6 | void set_cursor_offset(int offset); 7 | int print_char(char c, int col, int row, char attr); 8 | int get_offset(int col, int row); 9 | int get_offset_row(int offset); 10 | int get_offset_col(int offset); 11 | 12 | /********************************************************** 13 | * Public Kernel API functions * 14 | **********************************************************/ 15 | 16 | /** 17 | * Print a message on the specified location 18 | * If col, row, are negative, we will use the current offset 19 | */ 20 | void display_at(char *message, int col, int row) { 21 | /* Set cursor if col/row are negative */ 22 | int offset; 23 | if (col >= 0 && row >= 0) 24 | offset = get_offset(col, row); 25 | else { 26 | offset = get_cursor_offset(); 27 | row = get_offset_row(offset); 28 | col = get_offset_col(offset); 29 | } 30 | 31 | /* Loop through message and print it */ 32 | int i = 0; 33 | while (message[i] != 0) { 34 | offset = print_char(message[i++], col, row, WHITE_ON_BLACK); 35 | /* Compute row/col for next iteration */ 36 | row = get_offset_row(offset); 37 | col = get_offset_col(offset); 38 | } 39 | } 40 | 41 | void display(char *message) { 42 | display_at(message, -1, -1); 43 | } 44 | 45 | 46 | /********************************************************** 47 | * Private kernel functions * 48 | **********************************************************/ 49 | 50 | 51 | /** 52 | * Innermost print function for our kernel, directly accesses the video memory 53 | * 54 | * If 'col' and 'row' are negative, we will print at current cursor location 55 | * If 'attr' is zero it will use 'white on black' as default 56 | * Returns the offset of the next character 57 | * Sets the video cursor to the returned offset 58 | */ 59 | int print_char(char c, int col, int row, char attr) { 60 | unsigned char *vidmem = (unsigned char*) VIDEO_ADDRESS; 61 | if (!attr) attr = WHITE_ON_BLACK; 62 | 63 | /* Error control: print a red 'E' if the coords aren't right */ 64 | if (col >= MAX_COLS || row >= MAX_ROWS) { 65 | vidmem[2*(MAX_COLS)*(MAX_ROWS)-2] = 'E'; 66 | vidmem[2*(MAX_COLS)*(MAX_ROWS)-1] = RED_ON_WHITE; 67 | return get_offset(col, row); 68 | } 69 | 70 | int offset; 71 | if (col >= 0 && row >= 0) offset = get_offset(col, row); 72 | else offset = get_cursor_offset(); 73 | 74 | if (c == '\n') { 75 | row = get_offset_row(offset); 76 | offset = get_offset(0, row+1); 77 | } else { 78 | vidmem[offset] = c; 79 | vidmem[offset+1] = attr; 80 | offset += 2; 81 | } 82 | set_cursor_offset(offset); 83 | return offset; 84 | } 85 | 86 | int get_cursor_offset() { 87 | /* Use the VGA ports to get the current cursor position 88 | * 1. Ask for high byte of the cursor offset (data 14) 89 | * 2. Ask for low byte (data 15) 90 | */ 91 | port_byte_out(REG_SCREEN_CTRL, 14); 92 | int offset = port_byte_in(REG_SCREEN_DATA) << 8; /* High byte: << 8 */ 93 | port_byte_out(REG_SCREEN_CTRL, 15); 94 | offset += port_byte_in(REG_SCREEN_DATA); 95 | return offset * 2; /* Position * size of character cell */ 96 | } 97 | 98 | void set_cursor_offset(int offset) { 99 | /* Similar to get_cursor_offset, but instead of reading we write data */ 100 | offset /= 2; 101 | port_byte_out(REG_SCREEN_CTRL, 14); 102 | port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset >> 8)); 103 | port_byte_out(REG_SCREEN_CTRL, 15); 104 | port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset & 0xff)); 105 | } 106 | 107 | void clear_screen() { 108 | int screen_size = MAX_COLS * MAX_ROWS; 109 | int i; 110 | char *screen = (char *)VIDEO_ADDRESS; 111 | 112 | for (i = 0; i < screen_size; i++) { 113 | screen[i*2] = ' '; 114 | screen[i*2+1] = WHITE_ON_BLACK; 115 | } 116 | set_cursor_offset(get_offset(0, 0)); 117 | } 118 | 119 | 120 | int get_offset(int col, int row) { return 2 * (row * MAX_COLS + col); } 121 | int get_offset_row(int offset) { return offset / (2 * MAX_COLS); } 122 | int get_offset_col(int offset) { return (offset - (get_offset_row(offset)*2*MAX_COLS))/2; } 123 | -------------------------------------------------------------------------------- /src/kernelVideoDrivers/drivers/screen.h: -------------------------------------------------------------------------------- 1 | #define VIDEO_ADDRESS 0xb8000 2 | #define MAX_ROWS 25 3 | #define MAX_COLS 80 4 | #define WHITE_ON_BLACK 0x0f 5 | #define RED_ON_WHITE 0xf4 6 | 7 | /* Screen i/o ports */ 8 | #define REG_SCREEN_CTRL 0x3d4 9 | #define REG_SCREEN_DATA 0x3d5 10 | 11 | /* Public kernel API */ 12 | void clear_screen(); 13 | void display_at(char *message, int col, int row); 14 | void display(char *message); 15 | -------------------------------------------------------------------------------- /src/kernelVideoDrivers/drivers/screen.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernelVideoDrivers/drivers/screen.o -------------------------------------------------------------------------------- /src/kernelVideoDrivers/kernel.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernelVideoDrivers/kernel.bin -------------------------------------------------------------------------------- /src/kernelVideoDrivers/kernel/kernel.c: -------------------------------------------------------------------------------- 1 | #include "../drivers/screen.h" 2 | 3 | void main() { 4 | clear_screen(); 5 | display_at("GeekSkool", 1, 1); 6 | display_at("HasGeek House", 1, 6); 7 | } 8 | -------------------------------------------------------------------------------- /src/kernelVideoDrivers/kernel/kernel.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernelVideoDrivers/kernel/kernel.o -------------------------------------------------------------------------------- /src/kernelVideoDrivers/os-image.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernelVideoDrivers/os-image.bin -------------------------------------------------------------------------------- /src/kernelVideoPorts/Makefile: -------------------------------------------------------------------------------- 1 | C_SOURCES = $(wildcard kernel/*.c drivers/*.c) 2 | HEADERS = $(wildcard kernel/*.h drivers/*.h) 3 | # Nice syntax for file extension replacement 4 | OBJ = ${C_SOURCES:.c=.o} 5 | 6 | # Change this if your cross-compiler is somewhere else 7 | CC = gcc -m32 8 | GDB = /usr/local/i386elfgcc/bin/i386-elf-gdb 9 | # -g: Use debugging symbols in gcc 10 | CFLAGS = -g 11 | 12 | # First rule is run by default 13 | os-image.bin: boot/bootsect.bin kernel.bin 14 | cat $^ > os-image.bin 15 | 16 | # '--oformat binary' deletes all symbols as a collateral, so we don't need 17 | # to 'strip' them manually on this case 18 | kernel.bin: boot/kernel_entry.o ${OBJ} 19 | i386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary 20 | 21 | # Used for debugging purposes 22 | kernel.elf: boot/kernel_entry.o ${OBJ} 23 | i386-elf-ld -o $@ -Ttext 0x1000 $^ 24 | 25 | run: os-image.bin 26 | qemu-system-i386 -fda os-image.bin 27 | 28 | # Open the connection to qemu and load our kernel-object file with symbols 29 | debug: os-image.bin kernel.elf 30 | qemu-system-i386 -s -fda os-image.bin & 31 | ${GDB} -ex "target remote localhost:1234" -ex "symbol-file kernel.elf" 32 | 33 | # Generic rules for wildcards 34 | # To make an object, always compile from its .c 35 | %.o: %.c ${HEADERS} 36 | ${CC} ${CFLAGS} -ffreestanding -c $< -o $@ 37 | 38 | %.o: %.asm 39 | nasm $< -f elf -o $@ 40 | 41 | %.bin: %.asm 42 | nasm $< -f bin -o $@ 43 | 44 | clean: 45 | rm -rf *.bin *.dis *.o os-image.bin *.elf 46 | rm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o 47 | -------------------------------------------------------------------------------- /src/kernelVideoPorts/boot/32bit_print.asm: -------------------------------------------------------------------------------- 1 | [bits 32] ; using 32-bit protected mode 2 | 3 | ; this is how constants are defined 4 | VIDEO_MEMORY equ 0xb8000 5 | WHITE_OB_BLACK equ 0x0f ; the color byte for each character 6 | 7 | print_string_pm: 8 | pusha 9 | mov edx, VIDEO_MEMORY 10 | 11 | print_string_pm_loop: 12 | mov al, [ebx] ; [ebx] is the address of our character 13 | mov ah, WHITE_OB_BLACK 14 | 15 | cmp al, 0 ; check if end of string 16 | je print_string_pm_done 17 | 18 | mov [edx], ax ; store character + attribute in video memory 19 | add ebx, 1 ; next char 20 | add edx, 2 ; next video memory position 21 | 22 | jmp print_string_pm_loop 23 | 24 | print_string_pm_done: 25 | popa 26 | ret 27 | -------------------------------------------------------------------------------- /src/kernelVideoPorts/boot/bootsect.asm: -------------------------------------------------------------------------------- 1 | ; Identical to lesson 13's boot sector, but the %included files have new paths 2 | [org 0x7c00] 3 | KERNEL_OFFSET equ 0x1000 ; The same one we used when linking the kernel 4 | 5 | mov [BOOT_DRIVE], dl ; Remember that the BIOS sets us the boot drive in 'dl' on boot 6 | mov bp, 0x9000 7 | mov sp, bp 8 | 9 | mov bx, MSG_REAL_MODE 10 | call print 11 | call print_nl 12 | 13 | call load_kernel ; read the kernel from disk 14 | call switch_to_pm ; disable interrupts, load GDT, etc. Finally jumps to 'BEGIN_PM' 15 | jmp $ ; Never executed 16 | 17 | %include "boot/print.asm" 18 | %include "boot/print_hex.asm" 19 | %include "boot/disk.asm" 20 | %include "boot/gdt.asm" 21 | %include "boot/32bit_print.asm" 22 | %include "boot/switch_pm.asm" 23 | 24 | [bits 16] 25 | load_kernel: 26 | mov bx, MSG_LOAD_KERNEL 27 | call print 28 | call print_nl 29 | 30 | mov bx, KERNEL_OFFSET ; Read from disk and store in 0x1000 31 | mov dh, 16 ; Our future kernel will be larger, make this big 32 | mov dl, [BOOT_DRIVE] 33 | call disk_load 34 | ret 35 | 36 | [bits 32] 37 | BEGIN_PM: 38 | mov ebx, MSG_PROT_MODE 39 | call print_string_pm 40 | call KERNEL_OFFSET ; Give control to the kernel 41 | jmp $ ; Stay here when the kernel returns control to us (if ever) 42 | 43 | 44 | BOOT_DRIVE db 0 ; It is a good idea to store it in memory because 'dl' may get overwritten 45 | MSG_REAL_MODE db "Started in 16-bit Real Mode", 0 46 | MSG_PROT_MODE db "Landed in 32-bit Protected Mode", 0 47 | MSG_LOAD_KERNEL db "Loading kernel into memory", 0 48 | 49 | ; padding 50 | times 510 - ($-$$) db 0 51 | dw 0xaa55 52 | -------------------------------------------------------------------------------- /src/kernelVideoPorts/boot/bootsect.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernelVideoPorts/boot/bootsect.bin -------------------------------------------------------------------------------- /src/kernelVideoPorts/boot/disk.asm: -------------------------------------------------------------------------------- 1 | ; load 'dh' sectors from drive 'dl' into ES:BX 2 | disk_load: 3 | pusha 4 | ; reading from disk requires setting specific values in all registers 5 | ; so we will overwrite our input parameters from 'dx'. Let's save it 6 | ; to the stack for later use. 7 | push dx 8 | 9 | mov ah, 0x02 ; ah <- int 0x13 function. 0x02 = 'read' 10 | mov al, dh ; al <- number of sectors to read (0x01 .. 0x80) 11 | mov cl, 0x02 ; cl <- sector (0x01 .. 0x11) 12 | ; 0x01 is our boot sector, 0x02 is the first 'available' sector 13 | mov ch, 0x00 ; ch <- cylinder (0x0 .. 0x3FF, upper 2 bits in 'cl') 14 | ; dl <- drive number. Our caller sets it as a parameter and gets it from BIOS 15 | ; (0 = floppy, 1 = floppy2, 0x80 = hdd, 0x81 = hdd2) 16 | mov dh, 0x00 ; dh <- head number (0x0 .. 0xF) 17 | 18 | ; [es:bx] <- pointer to buffer where the data will be stored 19 | ; caller sets it up for us, and it is actually the standard location for int 13h 20 | int 0x13 ; BIOS interrupt 21 | jc disk_error ; if error (stored in the carry bit) 22 | 23 | pop dx 24 | cmp al, dh ; BIOS also sets 'al' to the # of sectors read. Compare it. 25 | jne sectors_error 26 | popa 27 | ret 28 | 29 | 30 | disk_error: 31 | mov bx, DISK_ERROR 32 | call print 33 | call print_nl 34 | mov dh, ah ; ah = error code, dl = disk drive that dropped the error 35 | call print_hex ; check out the code at http://stanislavs.org/helppc/int_13-1.html 36 | jmp disk_loop 37 | 38 | sectors_error: 39 | mov bx, SECTORS_ERROR 40 | call print 41 | 42 | disk_loop: 43 | jmp $ 44 | 45 | DISK_ERROR: db "Disk read error", 0 46 | SECTORS_ERROR: db "Incorrect number of sectors read", 0 47 | -------------------------------------------------------------------------------- /src/kernelVideoPorts/boot/gdt.asm: -------------------------------------------------------------------------------- 1 | gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps 2 | ; the GDT starts with a null 8-byte 3 | dd 0x0 ; 4 byte 4 | dd 0x0 ; 4 byte 5 | 6 | ; GDT for code segment. base = 0x00000000, length = 0xfffff 7 | ; for flags, refer to os-dev.pdf document, page 36 8 | gdt_code: 9 | dw 0xffff ; segment length, bits 0-15 10 | dw 0x0 ; segment base, bits 0-15 11 | db 0x0 ; segment base, bits 16-23 12 | db 10011010b ; flags (8 bits) 13 | db 11001111b ; flags (4 bits) + segment length, bits 16-19 14 | db 0x0 ; segment base, bits 24-31 15 | 16 | ; GDT for data segment. base and length identical to code segment 17 | ; some flags changed, again, refer to os-dev.pdf 18 | gdt_data: 19 | dw 0xffff 20 | dw 0x0 21 | db 0x0 22 | db 10010010b 23 | db 11001111b 24 | db 0x0 25 | 26 | gdt_end: 27 | 28 | ; GDT descriptor 29 | gdt_descriptor: 30 | dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size 31 | dd gdt_start ; address (32 bit) 32 | 33 | ; define some constants for later use 34 | CODE_SEG equ gdt_code - gdt_start 35 | DATA_SEG equ gdt_data - gdt_start 36 | -------------------------------------------------------------------------------- /src/kernelVideoPorts/boot/kernel_entry.asm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | [extern main] ; Define calling point. Must have same name as kernel.c 'main' function 3 | call main ; Calls the C function. The linker will know where it is placed in memory 4 | jmp $ 5 | -------------------------------------------------------------------------------- /src/kernelVideoPorts/boot/kernel_entry.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernelVideoPorts/boot/kernel_entry.o -------------------------------------------------------------------------------- /src/kernelVideoPorts/boot/print.asm: -------------------------------------------------------------------------------- 1 | print: 2 | pusha 3 | 4 | ; keep this in mind: 5 | ; while (string[i] != 0) { print string[i]; i++ } 6 | 7 | ; the comparison for string end (null byte) 8 | start: 9 | mov al, [bx] ; 'bx' is the base address for the string 10 | cmp al, 0 11 | je done 12 | 13 | ; the part where we print with the BIOS help 14 | mov ah, 0x0e 15 | int 0x10 ; 'al' already contains the char 16 | 17 | ; increment pointer and do next loop 18 | add bx, 1 19 | jmp start 20 | 21 | done: 22 | popa 23 | ret 24 | 25 | 26 | 27 | print_nl: 28 | pusha 29 | 30 | mov ah, 0x0e 31 | mov al, 0x0a ; newline char 32 | int 0x10 33 | mov al, 0x0d ; carriage return 34 | int 0x10 35 | 36 | popa 37 | ret 38 | -------------------------------------------------------------------------------- /src/kernelVideoPorts/boot/print_hex.asm: -------------------------------------------------------------------------------- 1 | ; receiving the data in 'dx' 2 | ; For the examples we'll assume that we're called with dx=0x1234 3 | print_hex: 4 | pusha 5 | 6 | mov cx, 0 ; our index variable 7 | 8 | ; Strategy: get the last char of 'dx', then convert to ASCII 9 | ; Numeric ASCII values: '0' (ASCII 0x30) to '9' (0x39), so just add 0x30 to byte N. 10 | ; For alphabetic characters A-F: 'A' (ASCII 0x41) to 'F' (0x46) we'll add 0x40 11 | ; Then, move the ASCII byte to the correct position on the resulting string 12 | hex_loop: 13 | cmp cx, 4 ; loop 4 times 14 | je end 15 | 16 | ; 1. convert last char of 'dx' to ascii 17 | mov ax, dx ; we will use 'ax' as our working register 18 | and ax, 0x000f ; 0x1234 -> 0x0004 by masking first three to zeros 19 | add al, 0x30 ; add 0x30 to N to convert it to ASCII "N" 20 | cmp al, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F' 21 | jle step2 22 | add al, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7 23 | 24 | step2: 25 | ; 2. get the correct position of the string to place our ASCII char 26 | ; bx <- base address + string length - index of char 27 | mov bx, HEX_OUT + 5 ; base + length 28 | sub bx, cx ; our index variable 29 | mov [bx], al ; copy the ASCII char on 'al' to the position pointed by 'bx' 30 | ror dx, 4 ; 0x1234 -> 0x4123 -> 0x3412 -> 0x2341 -> 0x1234 31 | 32 | ; increment index and loop 33 | add cx, 1 34 | jmp hex_loop 35 | 36 | end: 37 | ; prepare the parameter and call the function 38 | ; remember that print receives parameters in 'bx' 39 | mov bx, HEX_OUT 40 | call print 41 | 42 | popa 43 | ret 44 | 45 | HEX_OUT: 46 | db '0x0000',0 ; reserve memory for our new string 47 | -------------------------------------------------------------------------------- /src/kernelVideoPorts/boot/switch_pm.asm: -------------------------------------------------------------------------------- 1 | [bits 16] 2 | switch_to_pm: 3 | cli ; 1. disable interrupts 4 | lgdt [gdt_descriptor] ; 2. load the GDT descriptor 5 | mov eax, cr0 6 | or eax, 0x1 ; 3. set 32-bit mode bit in cr0 7 | mov cr0, eax 8 | jmp CODE_SEG:init_pm ; 4. far jump by using a different segment 9 | 10 | [bits 32] 11 | init_pm: ; we are now using 32-bit instructions 12 | mov ax, DATA_SEG ; 5. update the segment registers 13 | mov ds, ax 14 | mov ss, ax 15 | mov es, ax 16 | mov fs, ax 17 | mov gs, ax 18 | 19 | mov ebp, 0x90000 ; 6. update the stack right at the top of the free space 20 | mov esp, ebp 21 | 22 | call BEGIN_PM ; 7. Call a well-known label with useful code 23 | -------------------------------------------------------------------------------- /src/kernelVideoPorts/drivers/ports.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Read a byte from the specified port 3 | */ 4 | unsigned char port_byte_in (unsigned short port) { 5 | unsigned char result; 6 | __asm__("in %%dx, %%al" : "=a" (result) : "d" (port)); 7 | return result; 8 | } 9 | 10 | void port_byte_out (unsigned short port, unsigned char data) { 11 | __asm__("out %%al, %%dx" : : "a" (data), "d" (port)); 12 | } 13 | 14 | unsigned short port_word_in (unsigned short port) { 15 | unsigned short result; 16 | __asm__("in %%dx, %%ax" : "=a" (result) : "d" (port)); 17 | return result; 18 | } 19 | 20 | void port_word_out (unsigned short port, unsigned short data) { 21 | __asm__("out %%ax, %%dx" : : "a" (data), "d" (port)); 22 | } 23 | -------------------------------------------------------------------------------- /src/kernelVideoPorts/drivers/ports.h: -------------------------------------------------------------------------------- 1 | unsigned char port_byte_in (unsigned short port); 2 | void port_byte_out (unsigned short port, unsigned char data); 3 | unsigned short port_word_in (unsigned short port); 4 | void port_word_out (unsigned short port, unsigned short data); 5 | -------------------------------------------------------------------------------- /src/kernelVideoPorts/drivers/ports.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernelVideoPorts/drivers/ports.o -------------------------------------------------------------------------------- /src/kernelVideoPorts/kernel.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernelVideoPorts/kernel.bin -------------------------------------------------------------------------------- /src/kernelVideoPorts/kernel/kernel.c: -------------------------------------------------------------------------------- 1 | #include "../drivers/ports.h" 2 | 3 | void main() { 4 | /* Screen cursor position: ask VGA control register (0x3d4) for bytes 5 | * 14 = high byte of cursor and 15 = low byte of cursor. */ 6 | port_byte_out(0x3d4, 14); /* Requesting byte 14: high byte of cursor pos */ 7 | /* Data is returned in VGA data register (0x3d5) */ 8 | int position = port_byte_in(0x3d5); 9 | position = position << 8; /* high byte */ 10 | 11 | port_byte_out(0x3d4, 15); /* requesting low byte */ 12 | position += port_byte_in(0x3d5); 13 | 14 | /* VGA 'cells' consist of the character and its control data 15 | * e.g. 'white on black background', 'red text on white bg', etc */ 16 | int offset_from_vga = position * 2; 17 | 18 | /* Now you can examine both variables using gdb, since we still 19 | * don't know how to print strings on screen. Run 'make debug' and 20 | * on the gdb console: 21 | * breakpoint kernel.c:21 22 | * continue 23 | * print position 24 | * print offset_from_vga 25 | */ 26 | 27 | /* Let's write on the current cursor position, we already know how 28 | * to do that */ 29 | char *vga = (char *)0xb8000; 30 | vga[offset_from_vga] = 'X'; 31 | vga[offset_from_vga+1] = 0x0f; /* White text on black background */ 32 | } 33 | -------------------------------------------------------------------------------- /src/kernelVideoPorts/kernel/kernel.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernelVideoPorts/kernel/kernel.o -------------------------------------------------------------------------------- /src/kernelVideoPorts/os-image.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/kernelVideoPorts/os-image.bin -------------------------------------------------------------------------------- /src/print_string.asm: -------------------------------------------------------------------------------- 1 | print_string: 2 | mov ah, 0x0e 3 | 4 | loop: 5 | mov al, [bx] ; 6 | cmp al, 0 7 | je out 8 | int 0x10 ; invoking the screen-related Interrupt Service Routine 9 | ;An ISR is simply a sequence of machine instructions, much like our boot sector code, that deals with a 10 | ;specific interrupt (e.g. perhaps to read new data from a disk drive or from a network card). 11 | add bx, 0x01 12 | jmp loop 13 | 14 | out: 15 | mov al, ' ' 16 | int 0x10 17 | ret 18 | -------------------------------------------------------------------------------- /src/print_string_pm.asm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | 3 | VIDEO_MEMORY equ 0xb8000 4 | WHITE_ON_BLACK equ 0x0f 5 | 6 | ; data in ebx register 7 | print_string_pm: 8 | pusha 9 | mov edx, VIDEO_MEMORY 10 | 11 | print_string_pm_loop: 12 | mov al, [ebx] 13 | mov ah, WHITE_ON_BLACK 14 | 15 | cmp al, 0 16 | je done 17 | 18 | mov [edx], ax 19 | 20 | add edx, 2 21 | add ebx, 1 22 | jmp print_string_pm_loop 23 | 24 | done: 25 | popa 26 | ret 27 | -------------------------------------------------------------------------------- /src/screenDrivers/README.md: -------------------------------------------------------------------------------- 1 | ```sh 2 | $ nasm boot_sect.asm -f bin -o boot_sect.bin 3 | ``` 4 | 5 | ```sh 6 | $ nasm kernel_entry.asm -f elf -o kernel_entry.o 7 | ``` 8 | 9 | ```sh 10 | $ gcc -m32 -ffreestanding -c kernel.c -o kernel.o 11 | ``` 12 | 13 | ```sh 14 | $ i386-elf-ld -o kernel.bin -Ttext 0x0500 kernel_entry.o kernel.o --oformat binary 15 | ``` 16 | 17 | ```sh 18 | $ cat boot_sect.bin kernel.bin > os-image 19 | ``` 20 | -------------------------------------------------------------------------------- /src/screenDrivers/boot_sect.asm: -------------------------------------------------------------------------------- 1 | ; boot sector that enters 32 - bit protected mode. 2 | [org 0x7c00] 3 | KERNEL_OFFSET equ 0x1000 4 | 5 | mov [BOOT_DRIVE], dl ; BIOS stores our boot drive in DL , so it 's 6 | ; best to remember this for later. 7 | 8 | mov bp, 0x9000 9 | mov sp, bp 10 | 11 | mov bx, MSG_REAL_MODE 12 | call print_string 13 | 14 | call load_kernel 15 | 16 | call switch_to_pm 17 | jmp $ ; jump forever(jump to address of current instruction) 18 | 19 | %include "print_string.asm" 20 | %include "disk_load.asm" 21 | %include "print_string_pm.asm" 22 | %include "gdt.asm" 23 | %include "switch_to_pm.asm" 24 | 25 | [ bits 16] 26 | load_kernel: 27 | mov bx, MSG_LOAD_KERNEL ; using BX as a parameter of our function 28 | call print_string ; by specifying the address of a string 29 | 30 | mov bx, KERNEL_OFFSET 31 | mov dh, 30 32 | mov dl, [BOOT_DRIVE] 33 | ; with dx having been loaded with number of sectors to read, we will call disk_load 34 | call disk_load 35 | ret 36 | 37 | [bits 32] 38 | BEGIN_PM: 39 | mov ebx, MSG_PROT_MODE 40 | call print_string_pm 41 | 42 | call KERNEL_OFFSET 43 | 44 | jmp $ 45 | 46 | BOOT_DRIVE: 47 | db 0 48 | 49 | MSG_REAL_MODE: 50 | db " Hello GeekSkool,Started in 16- bit Real Mode ", 0 51 | 52 | MSG_PROT_MODE: 53 | db " Successfully landed in 32- bit Protected Mode ", 0 54 | 55 | MSG_LOAD_KERNEL: 56 | db " Loading kernel into memory. ", 0 57 | 58 | times 510-($-$$) db 0 ; Pad the boot sector out with zeros 59 | ; When compiled , our program must fit into 512 bytes , 60 | ; with the last two bytes being the magic number , 61 | ; so here , tell our assembly compiler to pad out our 62 | ; program with enough zero bytes (db 0) to bring us to the 63 | ; 510 th byte. 64 | dw 0xaa55 ; Last two bytes form the magic number 65 | ; BIOS is informed that we are a boot sector 66 | -------------------------------------------------------------------------------- /src/screenDrivers/boot_sect.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/screenDrivers/boot_sect.bin -------------------------------------------------------------------------------- /src/screenDrivers/disk_load.asm: -------------------------------------------------------------------------------- 1 | disk_load: 2 | push dx ; Store DX on stack so later we can recall 3 | ; how many sectors were request to be read! 4 | 5 | mov ah, 0x02 ; BIOS read sector function 6 | mov al, dh ; READ dh sectors 7 | mov ch, 0x00 ; select cylinder 0 8 | mov dh, 0x00 ; select head 0 9 | 10 | mov cl, 0x02 ; start reading from 2nd sector (after boot sector) 11 | 12 | int 0x13 ; BIOS interrupt 13 | jc disk_error 14 | 15 | pop dx ; pop dx from the stack 16 | cmp dh, al 17 | jne disk_error ; generate disk error if AL(sectors read) != DH(Expected number of sectors) 18 | 19 | ret 20 | disk_error: ; disk_error label 21 | mov bx, [DISK_ERROR_MSG] ; push the error message 22 | call print_string ; print_string is the helper to print string stored in [BX] 23 | jmp $ ; jump forever 24 | 25 | DISK_ERROR_MSG: 26 | db 'Disk read Error!', 0 27 | -------------------------------------------------------------------------------- /src/screenDrivers/gdt.asm: -------------------------------------------------------------------------------- 1 | ; GDT tabel 2 | gdt_start: 3 | 4 | ;NULL segment 5 | gdt_null: 6 | dd 0x0 7 | dd 0x0 8 | ;define two segments, one for code and another for data 9 | ;there is no memory protection as these segments can overlap 10 | 11 | gdt_code: 12 | ; base =0x0 , limit =0 xfffff , 13 | ; 1st flags : ( present )1 ( privilege )00 ( descriptor type )1 -> 1001 b 14 | ; type flags : ( code )1 ( conforming )0 ( readable )1 ( accessed )0 -> 1010 b 15 | ; 2nd flags : ( granularity )1 (32 - bit default )1 (64 - bit seg )0 ( AVL )0 -> 1100 b 16 | dw 0xffff ; Limit ( bits 0 -15) 17 | dw 0x0 ; Base ( bits 0 -15) 18 | db 0x0 ; Base ( bits 16 -23) 19 | db 10011010b ; 1st flags , type flags 20 | db 11001111b ; 2nd flags , Limit ( bits 16 -19) 21 | db 0x0 ; Base ( bits 24 -31) 22 | 23 | gdt_data: 24 | ; Same as code segment except for the type flags : 25 | ; type flags : ( code )0 ( expand down )0 ( writable )1 ( accessed )0 -> 0010 b 26 | dw 0xffff ; Limit ( bits 0 -15) 27 | dw 0x0 ; Base ( bits 0 -15) 28 | db 0x0 ; Base ( bits 16 -23) 29 | db 10010010b ; 1st flags , type flags 30 | db 11001111b ; 2nd flags , Limit ( bits 16 -19) 31 | db 0x0 ; Base ( bits 24 -31) 32 | 33 | gdt_end: ; The reason for putting a label at the end of the 34 | ; GDT is so we can have the assembler calculate 35 | ; the size of the GDT for the GDT decriptor ( below ) 36 | 37 | gdt_descriptor: 38 | dw gdt_end - gdt_start - 1 ; Size of our GDT , always less one 39 | ; of the true size 40 | dd gdt_start ; Start address of our GDT 41 | 42 | ; Define some handy constants for the GDT segment descriptor offsets , which 43 | ; are what segment registers must contain when in protected mode. For example , 44 | ; when we set DS = 0 x10 in PM , the CPU knows that we mean it to use the 45 | ; segment described at offset 0 x10 ( i.e. 16 bytes ) in our GDT , which in our 46 | ; case is the DATA segment (0 x0 -> NULL ; 0x08 -> CODE ; 0 x10 -> DATA ) 47 | CODE_SEG equ gdt_code - gdt_start 48 | DATA_SEG equ gdt_data - gdt_start 49 | -------------------------------------------------------------------------------- /src/screenDrivers/kernel.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/screenDrivers/kernel.bin -------------------------------------------------------------------------------- /src/screenDrivers/kernel.c: -------------------------------------------------------------------------------- 1 | #include "screen.h" 2 | 3 | void main() 4 | { 5 | clear_screen(); 6 | kprint_at("X", 1, 6); 7 | kprint_at("This text spans multiple lines", 75, 10); 8 | kprint_at("There is a line\nbreak", 0, 20); 9 | kprint("There is a line\nbreak"); 10 | kprint_at("What happens when we run out of space?", 45, 24); 11 | } 12 | -------------------------------------------------------------------------------- /src/screenDrivers/kernel.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/screenDrivers/kernel.o -------------------------------------------------------------------------------- /src/screenDrivers/kernel_entry.asm: -------------------------------------------------------------------------------- 1 | ;The linker takes object files as inputs, then joins them together, 2 | ;but resolves any labels to their correct addresses. 3 | 4 | [bits 32] 5 | [extern main] ; Define calling point. Must have same name as kernel.c 'main' function 6 | call main ; Calls the C function. The linker will know where it is placed in memory 7 | jmp $ 8 | -------------------------------------------------------------------------------- /src/screenDrivers/kernel_entry.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/screenDrivers/kernel_entry.o -------------------------------------------------------------------------------- /src/screenDrivers/os-image.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/screenDrivers/os-image.bin -------------------------------------------------------------------------------- /src/screenDrivers/ports.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Read a byte from the specified port 3 | */ 4 | unsigned char port_byte_in (unsigned short port) { 5 | unsigned char result; 6 | /* Inline assembler syntax 7 | * !! Notice how the source and destination registers are switched from NASM !! 8 | * 9 | * '"=a" (result)'; set '=' the C variable '(result)' to the value of register e'a'x 10 | * '"d" (port)': map the C variable '(port)' into e'd'x register 11 | * 12 | * Inputs and outputs are separated by colons 13 | */ 14 | __asm__("in %%dx, %%al" : "=a" (result) : "d" (port)); 15 | return result; 16 | } 17 | 18 | void port_byte_out (unsigned short port, unsigned char data) { 19 | /* Notice how here both registers are mapped to C variables and 20 | * nothing is returned, thus, no equals '=' in the asm syntax 21 | * However we see a comma since there are two variables in the input area 22 | * and none in the 'return' area 23 | */ 24 | __asm__("out %%al, %%dx" : : "a" (data), "d" (port)); 25 | } 26 | 27 | unsigned short port_word_in (unsigned short port) { 28 | unsigned short result; 29 | __asm__("in %%dx, %%ax" : "=a" (result) : "d" (port)); 30 | return result; 31 | } 32 | 33 | void port_word_out (unsigned short port, unsigned short data) { 34 | __asm__("out %%ax, %%dx" : : "a" (data), "d" (port)); 35 | } 36 | -------------------------------------------------------------------------------- /src/screenDrivers/ports.h: -------------------------------------------------------------------------------- 1 | unsigned char port_byte_in (unsigned short port); 2 | void port_byte_out (unsigned short port, unsigned char data); 3 | unsigned short port_word_in (unsigned short port); 4 | void port_word_out (unsigned short port, unsigned short data); 5 | -------------------------------------------------------------------------------- /src/screenDrivers/ports.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/screenDrivers/ports.o -------------------------------------------------------------------------------- /src/screenDrivers/print_string.asm: -------------------------------------------------------------------------------- 1 | print_string: 2 | mov ah, 0x0e 3 | 4 | loop: 5 | mov al, [bx] ; 6 | cmp al, 0 7 | je out 8 | int 0x10 ; invoking the screen-related Interrupt Service Routine 9 | ;An ISR is simply a sequence of machine instructions, much like our boot sector code, that deals with a 10 | ;specific interrupt (e.g. perhaps to read new data from a disk drive or from a network card). 11 | add bx, 0x01 12 | jmp loop 13 | 14 | out: 15 | mov al, ' ' 16 | int 0x10 17 | ret 18 | -------------------------------------------------------------------------------- /src/screenDrivers/print_string_pm.asm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | 3 | VIDEO_MEMORY equ 0xb8000 4 | WHITE_ON_BLACK equ 0x0f 5 | 6 | ; data in ebx register 7 | print_string_pm: 8 | pusha 9 | mov edx, VIDEO_MEMORY 10 | 11 | print_string_pm_loop: 12 | mov al, [ebx] 13 | mov ah, WHITE_ON_BLACK 14 | 15 | cmp al, 0 16 | je done 17 | 18 | mov [edx], ax 19 | 20 | add edx, 2 21 | add ebx, 1 22 | jmp print_string_pm_loop 23 | 24 | done: 25 | popa 26 | ret 27 | -------------------------------------------------------------------------------- /src/screenDrivers/screen.c: -------------------------------------------------------------------------------- 1 | #include "screen.h" 2 | #include "ports.h" 3 | 4 | /* Declaration of private functions */ 5 | int get_cursor_offset(); 6 | void set_cursor_offset(int offset); 7 | int print_char(char c, int col, int row, char attr); 8 | int get_offset(int col, int row); 9 | int get_offset_row(int offset); 10 | int get_offset_col(int offset); 11 | 12 | /********************************************************** 13 | * Public Kernel API functions * 14 | **********************************************************/ 15 | 16 | /** 17 | * Print a message on the specified location 18 | * If col, row, are negative, we will use the current offset 19 | */ 20 | void kprint_at(char *message, int col, int row) { 21 | /* Set cursor if col/row are negative */ 22 | int offset; 23 | if (col >= 0 && row >= 0) 24 | offset = get_offset(col, row); 25 | else { 26 | offset = get_cursor_offset(); 27 | row = get_offset_row(offset); 28 | col = get_offset_col(offset); 29 | } 30 | 31 | /* Loop through message and print it */ 32 | int i = 0; 33 | while (message[i] != 0) { 34 | offset = print_char(message[i++], col, row, WHITE_ON_BLACK); 35 | /* Compute row/col for next iteration */ 36 | row = get_offset_row(offset); 37 | col = get_offset_col(offset); 38 | } 39 | } 40 | 41 | void kprint(char *message) { 42 | kprint_at(message, -1, -1); 43 | } 44 | 45 | 46 | /********************************************************** 47 | * Private kernel functions * 48 | **********************************************************/ 49 | 50 | 51 | /** 52 | * Innermost print function for our kernel, directly accesses the video memory 53 | * 54 | * If 'col' and 'row' are negative, we will print at current cursor location 55 | * If 'attr' is zero it will use 'white on black' as default 56 | * Returns the offset of the next character 57 | * Sets the video cursor to the returned offset 58 | */ 59 | int print_char(char c, int col, int row, char attr) { 60 | unsigned char *vidmem = (unsigned char*) VIDEO_ADDRESS; 61 | if (!attr) attr = WHITE_ON_BLACK; 62 | 63 | /* Error control: print a red 'E' if the coords aren't right */ 64 | if (col >= MAX_COLS || row >= MAX_ROWS) { 65 | vidmem[2*(MAX_COLS)*(MAX_ROWS)-2] = 'E'; 66 | vidmem[2*(MAX_COLS)*(MAX_ROWS)-1] = RED_ON_WHITE; 67 | return get_offset(col, row); 68 | } 69 | 70 | int offset; 71 | if (col >= 0 && row >= 0) offset = get_offset(col, row); 72 | else offset = get_cursor_offset(); 73 | 74 | if (c == '\n') { 75 | row = get_offset_row(offset); 76 | offset = get_offset(0, row+1); 77 | } else { 78 | vidmem[offset] = c; 79 | vidmem[offset+1] = attr; 80 | offset += 2; 81 | } 82 | set_cursor_offset(offset); 83 | return offset; 84 | } 85 | 86 | int get_cursor_offset() { 87 | /* Use the VGA ports to get the current cursor position 88 | * 1. Ask for high byte of the cursor offset (data 14) 89 | * 2. Ask for low byte (data 15) 90 | */ 91 | port_byte_out(REG_SCREEN_CTRL, 14); 92 | int offset = port_byte_in(REG_SCREEN_DATA) << 8; /* High byte: << 8 */ 93 | port_byte_out(REG_SCREEN_CTRL, 15); 94 | offset += port_byte_in(REG_SCREEN_DATA); 95 | return offset * 2; /* Position * size of character cell */ 96 | } 97 | 98 | void set_cursor_offset(int offset) { 99 | /* Similar to get_cursor_offset, but instead of reading we write data */ 100 | offset /= 2; 101 | port_byte_out(REG_SCREEN_CTRL, 14); 102 | port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset >> 8)); 103 | port_byte_out(REG_SCREEN_CTRL, 15); 104 | port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset & 0xff)); 105 | } 106 | 107 | void clear_screen() { 108 | int screen_size = MAX_COLS * MAX_ROWS; 109 | int i; 110 | char *screen = (char* )VIDEO_ADDRESS; 111 | 112 | for (i = 0; i < screen_size; i++) { 113 | screen[i*2] = ' '; 114 | screen[i*2+1] = WHITE_ON_BLACK; 115 | } 116 | set_cursor_offset(get_offset(0, 0)); 117 | } 118 | 119 | 120 | int get_offset(int col, int row) { return 2 * (row * MAX_COLS + col); } 121 | int get_offset_row(int offset) { return offset / (2 * MAX_COLS); } 122 | int get_offset_col(int offset) { return (offset - (get_offset_row(offset)*2*MAX_COLS))/2; } 123 | -------------------------------------------------------------------------------- /src/screenDrivers/screen.h: -------------------------------------------------------------------------------- 1 | #define VIDEO_ADDRESS 0xb8000 2 | #define MAX_ROWS 25 3 | #define MAX_COLS 80 4 | #define WHITE_ON_BLACK 0x0f 5 | #define RED_ON_WHITE 0xf4 6 | 7 | /* Screen i/o ports */ 8 | #define REG_SCREEN_CTRL 0x3d4 9 | #define REG_SCREEN_DATA 0x3d5 10 | 11 | /* Public kernel API */ 12 | void clear_screen(); 13 | void kprint_at(char *message, int col, int row); 14 | void kprint(char *message); 15 | -------------------------------------------------------------------------------- /src/screenDrivers/screen.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/screenDrivers/screen.o -------------------------------------------------------------------------------- /src/screenDrivers/switch_to_pm.asm: -------------------------------------------------------------------------------- 1 | [bits 16] 2 | 3 | switch_to_pm: 4 | cli ; We must switch off interrupts until we have 5 | ; set -up the protected mode interrupt vector 6 | ; otherwise interrupts will run riot. 7 | 8 | lgdt [gdt_descriptor] 9 | 10 | mov eax, cr0 ; see we are using 32 register (eax) here in 16 bit, we can do that 11 | or eax, 0x1 ; set the first bit of CR0, a control register 12 | mov cr0, eax 13 | 14 | jmp CODE_SEG:init_pm ;Make a far jump ( i.e. to a new segment ) to our 32 - bit 15 | ;code. 16 | 17 | [bits 32] 18 | init_pm: 19 | mov ax, DATA_SEG ; Now we are in protected mode , our old segments are meaningless , 20 | mov ds, ax ; so we point our segment registers to the 21 | mov ss, ax ; data selector we defined in our GDT 22 | mov es, ax 23 | mov fs, ax 24 | mov gs, ax 25 | 26 | mov ebp, 0x90000 ; Updating stack position so that it is right at the top of free space. 27 | mov esp, ebp ; create stack frame(MOV DST, SRC : Stackpointer <----- Basepointer) 28 | 29 | call BEGIN_PM 30 | -------------------------------------------------------------------------------- /src/switch_to_pm.asm: -------------------------------------------------------------------------------- 1 | [bits 16] 2 | 3 | switch_to_pm: 4 | cli ; We must switch off interrupts until we have 5 | ; set -up the protected mode interrupt vector 6 | ; otherwise interrupts will run riot. 7 | 8 | lgdt [gdt_descriptor] 9 | 10 | mov eax, cr0 ; see we are using 32 register (eax) here in 16 bit, we can do that 11 | or eax, 0x1 ; set the first bit of CR0, a control register 12 | mov cr0, eax 13 | 14 | jmp CODE_SEG:init_pm ;Make a far jump ( i.e. to a new segment ) to our 32 - bit 15 | ;code. 16 | 17 | [bits 32] 18 | init_pm: 19 | mov ax, DATA_SEG ; Now we are in protected mode , our old segments are meaningless , 20 | mov ds, ax ; so we point our segment registers to the 21 | mov ss, ax ; data selector we defined in our GDT 22 | mov es, ax 23 | mov fs, ax 24 | mov gs, ax 25 | 26 | mov ebp, 0x90000 ; Updating stack position so that it is right at the top of free space. 27 | mov esp, ebp 28 | 29 | call BEGIN_PM 30 | -------------------------------------------------------------------------------- /src/videoPorts/README.md: -------------------------------------------------------------------------------- 1 | ```sh 2 | $ nasm boot_sect.asm -f bin -o boot_sect.bin 3 | ``` 4 | 5 | ```sh 6 | $ nasm kernel_entry.asm -f elf -o kernel_entry.o 7 | ``` 8 | 9 | ```sh 10 | $ gcc -m32 -ffreestanding -c kernel.c -o kernel.o 11 | ``` 12 | 13 | ```sh 14 | $ i386-elf-ld -o kernel.bin -Ttext 0x0500 kernel_entry.o kernel.o --oformat binary 15 | ``` 16 | 17 | ```sh 18 | $ cat boot_sect.bin kernel.bin > os-image 19 | ``` 20 | -------------------------------------------------------------------------------- /src/videoPorts/boot_sect.asm: -------------------------------------------------------------------------------- 1 | ; boot sector that enters 32 - bit protected mode. 2 | [org 0x7c00] 3 | KERNEL_OFFSET equ 0x1000 4 | 5 | mov [BOOT_DRIVE], dl ; BIOS stores our boot drive in DL , so it 's 6 | ; best to remember this for later. 7 | 8 | mov bp, 0x9000 9 | mov sp, bp 10 | 11 | mov bx, MSG_REAL_MODE 12 | call print_string 13 | 14 | call load_kernel 15 | 16 | call switch_to_pm 17 | jmp $ ; jump forever(jump to address of current instruction) 18 | 19 | %include "print_string.asm" 20 | %include "disk_load.asm" 21 | %include "print_string_pm.asm" 22 | %include "gdt.asm" 23 | %include "switch_to_pm.asm" 24 | 25 | [ bits 16] 26 | load_kernel: 27 | mov bx, MSG_LOAD_KERNEL ; using BX as a parameter of our function 28 | call print_string ; by specifying the address of a string 29 | 30 | mov bx, KERNEL_OFFSET 31 | mov dh, 30 32 | mov dl, [BOOT_DRIVE] 33 | ; with dx having been loaded with number of sectors to read, we will call disk_load 34 | call disk_load 35 | ret 36 | 37 | [bits 32] 38 | BEGIN_PM: 39 | mov ebx, MSG_PROT_MODE 40 | call print_string_pm 41 | 42 | call KERNEL_OFFSET 43 | 44 | jmp $ 45 | 46 | BOOT_DRIVE: 47 | db 0 48 | 49 | MSG_REAL_MODE: 50 | db " Hello GeekSkool,Started in 16- bit Real Mode ", 0 51 | 52 | MSG_PROT_MODE: 53 | db " Successfully landed in 32- bit Protected Mode ", 0 54 | 55 | MSG_LOAD_KERNEL: 56 | db " Loading kernel into memory. ", 0 57 | 58 | times 510-($-$$) db 0 ; Pad the boot sector out with zeros 59 | ; When compiled , our program must fit into 512 bytes , 60 | ; with the last two bytes being the magic number , 61 | ; so here , tell our assembly compiler to pad out our 62 | ; program with enough zero bytes (db 0) to bring us to the 63 | ; 510 th byte. 64 | dw 0xaa55 ; Last two bytes form the magic number 65 | ; BIOS is informed that we are a boot sector 66 | -------------------------------------------------------------------------------- /src/videoPorts/boot_sect.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/videoPorts/boot_sect.bin -------------------------------------------------------------------------------- /src/videoPorts/disk_load.asm: -------------------------------------------------------------------------------- 1 | disk_load: 2 | push dx ; Store DX on stack so later we can recall 3 | ; how many sectors were request to be read! 4 | 5 | mov ah, 0x02 ; BIOS read sector function 6 | mov al, dh ; READ dh sectors 7 | mov ch, 0x00 ; select cylinder 0 8 | mov dh, 0x00 ; select head 0 9 | 10 | mov cl, 0x02 ; start reading from 2nd sector (after boot sector) 11 | 12 | int 0x13 ; BIOS interrupt 13 | jc disk_error 14 | 15 | pop dx ; pop dx from the stack 16 | cmp dh, al 17 | jne disk_error ; generate disk error if AL(sectors read) != DH(Expected number of sectors) 18 | 19 | ret 20 | disk_error: ; disk_error label 21 | mov bx, [DISK_ERROR_MSG] ; push the error message 22 | call print_string ; print_string is the helper to print string stored in [BX] 23 | jmp $ ; jump forever 24 | 25 | DISK_ERROR_MSG: 26 | db 'Disk read Error!', 0 27 | -------------------------------------------------------------------------------- /src/videoPorts/gdt.asm: -------------------------------------------------------------------------------- 1 | ; GDT tabel 2 | gdt_start: 3 | 4 | ;NULL segment 5 | gdt_null: 6 | dd 0x0 7 | dd 0x0 8 | ;define two segments, one for code and another for data 9 | ;there is no memory protection as these segments can overlap 10 | 11 | gdt_code: 12 | ; base =0x0 , limit =0 xfffff , 13 | ; 1st flags : ( present )1 ( privilege )00 ( descriptor type )1 -> 1001 b 14 | ; type flags : ( code )1 ( conforming )0 ( readable )1 ( accessed )0 -> 1010 b 15 | ; 2nd flags : ( granularity )1 (32 - bit default )1 (64 - bit seg )0 ( AVL )0 -> 1100 b 16 | dw 0xffff ; Limit ( bits 0 -15) 17 | dw 0x0 ; Base ( bits 0 -15) 18 | db 0x0 ; Base ( bits 16 -23) 19 | db 10011010b ; 1st flags , type flags 20 | db 11001111b ; 2nd flags , Limit ( bits 16 -19) 21 | db 0x0 ; Base ( bits 24 -31) 22 | 23 | gdt_data: 24 | ; Same as code segment except for the type flags : 25 | ; type flags : ( code )0 ( expand down )0 ( writable )1 ( accessed )0 -> 0010 b 26 | dw 0xffff ; Limit ( bits 0 -15) 27 | dw 0x0 ; Base ( bits 0 -15) 28 | db 0x0 ; Base ( bits 16 -23) 29 | db 10010010b ; 1st flags , type flags 30 | db 11001111b ; 2nd flags , Limit ( bits 16 -19) 31 | db 0x0 ; Base ( bits 24 -31) 32 | 33 | gdt_end: ; The reason for putting a label at the end of the 34 | ; GDT is so we can have the assembler calculate 35 | ; the size of the GDT for the GDT decriptor ( below ) 36 | 37 | gdt_descriptor: 38 | dw gdt_end - gdt_start - 1 ; Size of our GDT , always less one 39 | ; of the true size 40 | dd gdt_start ; Start address of our GDT 41 | 42 | ; Define some handy constants for the GDT segment descriptor offsets , which 43 | ; are what segment registers must contain when in protected mode. For example , 44 | ; when we set DS = 0 x10 in PM , the CPU knows that we mean it to use the 45 | ; segment described at offset 0 x10 ( i.e. 16 bytes ) in our GDT , which in our 46 | ; case is the DATA segment (0 x0 -> NULL ; 0x08 -> CODE ; 0 x10 -> DATA ) 47 | CODE_SEG equ gdt_code - gdt_start 48 | DATA_SEG equ gdt_data - gdt_start 49 | -------------------------------------------------------------------------------- /src/videoPorts/kernel.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/videoPorts/kernel.bin -------------------------------------------------------------------------------- /src/videoPorts/kernel.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Read --or-- Write a byte --or-- a word from --or-- to a specified port 3 | */ 4 | 5 | unsigned char port_byte_in (unsigned short port) 6 | { 7 | unsigned char result; 8 | __asm__("in %%dx, %%al" : "=a" (result) : "d" (port)); 9 | //Thanks to GAS, source and destination reigsters are flipped 10 | //AL = A , DX = PORT 11 | return result; 12 | } 13 | 14 | void port_byte_out (unsigned short port, unsigned char data) 15 | { 16 | __asm__("out %%al, %%dx" : : "a" (data), "d" (port)); 17 | } 18 | 19 | unsigned short port_word_in (unsigned short port) 20 | { 21 | unsigned short result; 22 | __asm__("in %%dx, %%ax" : "=a" (result) : "d" (port)); 23 | return result; 24 | } 25 | 26 | void port_word_out (unsigned short port, unsigned short data) 27 | { 28 | __asm__("out %%ax, %%dx" : : "a" (data), "d" (port)); 29 | } 30 | 31 | 32 | void main() 33 | { 34 | /* Screen cursor position: ask VGA control register (0x3d4) for bytes 35 | * 14 = high byte of cursor and 15 = low byte of cursor. */ 36 | port_byte_out(0x3d4, 14); /* Requesting byte 14: high byte of cursor pos */ 37 | /* Data is returned in VGA data register (0x3d5) */ 38 | int position = port_byte_in(0x3d5); 39 | position = position << 8; /* high byte */ 40 | 41 | port_byte_out(0x3d4, 15); /* requesting low byte */ 42 | position += port_byte_in(0x3d5); 43 | 44 | /* VGA 'cells' consist of the character and its control data 45 | * e.g. 'white on black background', 'red text on white bg', etc */ 46 | int offset_from_vga = position * 2; 47 | 48 | char *vga = (char *)0xb8000; 49 | vga[offset_from_vga] = 'H'; 50 | vga[offset_from_vga++] = 0x0f; /* White text on black background */ 51 | 52 | vga[offset_from_vga++] = 'A'; 53 | vga[offset_from_vga++] = 0x0f; /* White text on black background */ 54 | 55 | vga[offset_from_vga++] = 'S'; 56 | vga[offset_from_vga++] = 0x0f; /* White text on black background */ 57 | 58 | vga[offset_from_vga++] = 'G'; 59 | vga[offset_from_vga++] = 0x0f; /* White text on black background */ 60 | 61 | vga[offset_from_vga++] = 'E'; 62 | vga[offset_from_vga++] = 0x0f; /* White text on black background */ 63 | 64 | vga[offset_from_vga++] = 'E'; 65 | vga[offset_from_vga+1] = 0x0f; /* White text on black background */ 66 | 67 | vga[offset_from_vga++] = 'K'; 68 | vga[offset_from_vga+1] = 0x0f; /* White text on black background */ 69 | } 70 | 71 | -------------------------------------------------------------------------------- /src/videoPorts/kernel.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/videoPorts/kernel.o -------------------------------------------------------------------------------- /src/videoPorts/kernel_entry.asm: -------------------------------------------------------------------------------- 1 | ;The linker takes object files as inputs, then joins them together, 2 | ;but resolves any labels to their correct addresses. 3 | 4 | [bits 32] 5 | [extern main] ; Define calling point. Must have same name as kernel.c 'main' function 6 | call main ; Calls the C function. The linker will know where it is placed in memory 7 | jmp $ 8 | -------------------------------------------------------------------------------- /src/videoPorts/kernel_entry.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/videoPorts/kernel_entry.o -------------------------------------------------------------------------------- /src/videoPorts/os-image.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/geekskool/WriteYourOwnOS/dd5856302f73e8b5de19c20ce18e04aa7a970d7b/src/videoPorts/os-image.bin -------------------------------------------------------------------------------- /src/videoPorts/print_string.asm: -------------------------------------------------------------------------------- 1 | print_string: 2 | mov ah, 0x0e 3 | 4 | loop: 5 | mov al, [bx] ; 6 | cmp al, 0 7 | je out 8 | int 0x10 ; invoking the screen-related Interrupt Service Routine 9 | ;An ISR is simply a sequence of machine instructions, much like our boot sector code, that deals with a 10 | ;specific interrupt (e.g. perhaps to read new data from a disk drive or from a network card). 11 | add bx, 0x01 12 | jmp loop 13 | 14 | out: 15 | mov al, ' ' 16 | int 0x10 17 | ret 18 | -------------------------------------------------------------------------------- /src/videoPorts/print_string_pm.asm: -------------------------------------------------------------------------------- 1 | [bits 32] 2 | 3 | VIDEO_MEMORY equ 0xb8000 4 | WHITE_ON_BLACK equ 0x0f 5 | 6 | ; data in ebx register 7 | print_string_pm: 8 | pusha 9 | mov edx, VIDEO_MEMORY 10 | 11 | print_string_pm_loop: 12 | mov al, [ebx] 13 | mov ah, WHITE_ON_BLACK 14 | 15 | cmp al, 0 16 | je done 17 | 18 | mov [edx], ax 19 | 20 | add edx, 2 21 | add ebx, 1 22 | jmp print_string_pm_loop 23 | 24 | done: 25 | popa 26 | ret 27 | -------------------------------------------------------------------------------- /src/videoPorts/switch_to_pm.asm: -------------------------------------------------------------------------------- 1 | [bits 16] 2 | 3 | switch_to_pm: 4 | cli ; We must switch off interrupts until we have 5 | ; set -up the protected mode interrupt vector 6 | ; otherwise interrupts will run riot. 7 | 8 | lgdt [gdt_descriptor] 9 | 10 | mov eax, cr0 ; see we are using 32 register (eax) here in 16 bit, we can do that 11 | or eax, 0x1 ; set the first bit of CR0, a control register 12 | mov cr0, eax 13 | 14 | jmp CODE_SEG:init_pm ;Make a far jump ( i.e. to a new segment ) to our 32 - bit 15 | ;code. 16 | 17 | [bits 32] 18 | init_pm: 19 | mov ax, DATA_SEG ; Now we are in protected mode , our old segments are meaningless , 20 | mov ds, ax ; so we point our segment registers to the 21 | mov ss, ax ; data selector we defined in our GDT 22 | mov es, ax 23 | mov fs, ax 24 | mov gs, ax 25 | 26 | mov ebp, 0x90000 ; Updating stack position so that it is right at the top of free space. 27 | mov esp, ebp ; create stack frame(MOV DST, SRC : Stackpointer <----- Basepointer) 28 | 29 | call BEGIN_PM 30 | --------------------------------------------------------------------------------