[
  {
    "path": ".gitignore",
    "content": "*.bin\n*.o\n*.swp\n*.dis\n*.elf\n*.sym\n.DS_STORE\n"
  },
  {
    "path": "00-environment/README.md",
    "content": "*Concepts you may want to Google beforehand: linux, mac, terminal, compiler, emulator, nasm, qemu*\n\n**Goal: Install the software required to run this tutorial**\n\nI'm working on a Mac, though Linux is better because it will have all the standard tools already\navailable for you.\n\nOn a mac, [install Homebrew](http://brew.sh) and then `brew install qemu nasm`\n\nDon't use the Xcode developer tools `nasm` if you have them installed, they won't work for the most cases. Always use `/usr/local/bin/nasm`\n\nOn some systems qemu is split into multiple binaries. You may want\nto call `qemu-system-x86_64 binfile`\n"
  },
  {
    "path": "01-bootsector-barebones/README.md",
    "content": "*Concepts you may want to Google beforehand: assembler, BIOS*\n\n**Goal: Create a file which the BIOS interprets as a bootable disk**\n\nThis is very exciting, we're going to create our own boot sector!\n\nTheory\n------\n\nWhen the computer boots, the BIOS doesn't know how to load the OS, so it\ndelegates that task to the boot sector. Thus, the boot sector must be\nplaced in a known, standard location. That location is the first sector\nof the disk (cylinder 0, head 0, sector 0) and it takes 512 bytes.\n\nTo make sure that the \"disk is bootable\", the BIOS checks that bytes\n511 and 512 of the alleged boot sector are bytes `0xAA55`.\n\nThis is the simplest boot sector ever:\n\n```\ne9 fd ff 00 00 00 00 00 00 00 00 00 00 00 00 00\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00\n[ 29 more lines with sixteen zero-bytes each ]\n00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa\n```\n\nIt is basically all zeros, ending with the 16-bit value\n`0xAA55` (beware of endianness, x86 is little-endian). \nThe first three bytes perform an infinite jump\n\nSimplest boot sector ever\n-------------------------\n\nYou can either write the above 512 bytes\nwith a binary editor, or just write a very\nsimple assembler code:\n\n```nasm\n; Infinite loop (e9 fd ff)\nloop:\n    jmp loop \n\n; Fill with 510 zeros minus the size of the previous code\ntimes 510-($-$$) db 0\n; Magic number\ndw 0xaa55 \n```\n\nTo compile:\n`nasm -f bin boot_sect_simple.asm -o boot_sect_simple.bin`\n\n> OSX warning: if this drops an error, read chapter 00 again\n\nI know you're anxious to try it out (I am!), so let's do it:\n\n`qemu boot_sect_simple.bin`\n\n> On some systems, you may have to run `qemu-system-x86_64 boot_sect_simple.bin` If this gives an SDL error, try passing the --nographic and/or --curses flag(s).\n\nYou will see a window open which says \"Booting from Hard Disk...\" and\nnothing else. When was the last time you were so excited to see an infinite\nloop? ;-)\n"
  },
  {
    "path": "01-bootsector-barebones/boot_sect_simple.asm",
    "content": "; A simple boot sector program that loops forever\nloop:\n    jmp loop\n\ntimes 510-($-$$) db 0\ndw 0xaa55\n"
  },
  {
    "path": "02-bootsector-print/README.md",
    "content": "*Concepts you may want to Google beforehand: interrupts, CPU\nregisters*\n\n**Goal: Make our previously silent boot sector print some text**\n\nWe will improve a bit on our infinite-loop boot sector and print\nsomething on the screen. We will raise an interrupt for this.\n\nOn this example we are going to write each character of the \"Hello\"\nword into the register `al` (lower part of `ax`), the bytes `0x0e`\ninto `ah` (the higher part of `ax`) and raise interrupt `0x10` which\nis a general interrupt for video services.\n\n`0x0e` on `ah` tells the video interrupt that the actual function\nwe want to run is to 'write the contents of `al` in tty mode'.\n\nWe will set tty mode only once though in the real world we \ncannot be sure that the contents of `ah` are constant. Some other\nprocess may run on the CPU while we are sleeping, not clean\nup properly and leave garbage data on `ah`.\n\nFor this example we don't need to take care of that since we are\nthe only thing running on the CPU.\n\nOur new boot sector looks like this:\n```nasm\nmov ah, 0x0e ; tty mode\nmov al, 'H'\nint 0x10\nmov al, 'e'\nint 0x10\nmov al, 'l'\nint 0x10\nint 0x10 ; 'l' is still on al, remember?\nmov al, 'o'\nint 0x10\n\njmp $ ; jump to current address = infinite loop\n\n; padding and magic number\ntimes 510 - ($-$$) db 0\ndw 0xaa55 \n```\n\nYou can examine the binary data with `xxd file.bin`\n\nAnyway, you know the drill:\n\n`nasm -fbin boot_sect_hello.asm -o boot_sect_hello.bin`\n\n`qemu boot_sect_hello.bin`\n\nYour boot sector will say 'Hello' and hang on an infinite loop\n"
  },
  {
    "path": "02-bootsector-print/boot_sect_hello.asm",
    "content": "mov ah, 0x0e ; tty mode\nmov al, 'H'\nint 0x10\nmov al, 'e'\nint 0x10\nmov al, 'l'\nint 0x10\nint 0x10 ; 'l' is still on al, remember?\nmov al, 'o'\nint 0x10\n\njmp $ ; jump to current address = infinite loop\n\n; padding and magic number\ntimes 510 - ($-$$) db 0\ndw 0xaa55 \n"
  },
  {
    "path": "03-bootsector-memory/README.md",
    "content": "*Concepts you may want to Google beforehand: memory offsets, pointers*\n\n**Goal: Learn how the computer memory is organized**\n\nPlease open page 14 [of this document](\nhttp://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf)<sup>1</sup>\nand look at the figure with the memory layout.\n\nThe only goal of this lesson is to learn where the boot sector is stored\n\nI could just bluntly tell you that the BIOS places it at `0x7C00` and\nget it done with, but an example with wrong solutions will make things clearer.\n\nWe want to print an X on screen. We will try 4 different strategies\nand see which ones work and why.\n\n**Open the file `boot_sect_memory.asm`**\n\nFirst, we will define the X as data, with a label:\n```nasm\nthe_secret:\n    db \"X\"\n```\n\nThen we will try to access `the_secret` in many different ways:\n\n1. `mov al, the_secret`\n2. `mov al, [the_secret]`\n3. `mov al, the_secret + 0x7C00`\n4. `mov al, 2d + 0x7C00`, where `2d` is the actual position of the 'X' byte in the binary\n\nTake a look at the code and read the comments.\n\nCompile and run the code. You should see a string similar to `1[2¢3X4X`, where\nthe bytes following 1 and 2 are just random garbage.\n\nIf you add or remove instructions, remember to compute the new offset of the X\nby counting the bytes, and replace `0x2d` with the new one.\n\nPlease don't continue onto the next section unless you have 100% understood\nthe boot sector offset and memory addressing.\n\n\nThe global offset\n-----------------\n\nNow, since offsetting `0x7c00` everywhere is very inconvenient, assemblers let\nus define a \"global offset\" for every memory location, with the `org` command:\n\n```nasm\n[org 0x7c00]\n```\n\nGo ahead and **open `boot_sect_memory_org.asm`** and you will see the canonical\nway to print data with the boot sector, which is now attempt 2. Compile the code\nand run it, and you will see how the `org` command affects each previous solution.\n\nRead the comments for a full explanation of the changes with and without `org`\n\n-----\n\n[1] This whole tutorial is heavily inspired on that document. Please read the\nroot-level README for more information on that.\n"
  },
  {
    "path": "03-bootsector-memory/boot_sect_memory.asm",
    "content": "mov ah, 0x0e\n\n; attempt 1\n; Fails because it tries to print the memory address (i.e. pointer)\n; not its actual contents\nmov al, \"1\"\nint 0x10\nmov al, the_secret\nint 0x10\n\n; attempt 2\n; It tries to print the memory address of 'the_secret' which is the correct approach.\n; However, BIOS places our bootsector binary at address 0x7c00\n; so we need to add that padding beforehand. We'll do that in attempt 3\nmov al, \"2\"\nint 0x10\nmov al, [the_secret]\nint 0x10\n\n; attempt 3\n; Add the BIOS starting offset 0x7c00 to the memory address of the X\n; and then dereference the contents of that pointer.\n; We need the help of a different register 'bx' because 'mov al, [ax]' is illegal.\n; A register can't be used as source and destination for the same command.\nmov al, \"3\"\nint 0x10\nmov bx, the_secret\nadd bx, 0x7c00\nmov al, [bx]\nint 0x10\n\n; attempt 4\n; We try a shortcut since we know that the X is stored at byte 0x2d in our binary\n; That's smart but ineffective, we don't want to be recounting label offsets\n; every time we change the code\nmov al, \"4\"\nint 0x10\nmov al, [0x7c2d]\nint 0x10\n\n\njmp $ ; infinite loop\n\nthe_secret:\n    ; ASCII code 0x58 ('X') is stored just before the zero-padding.\n    ; On this code that is at byte 0x2d (check it out using 'xxd file.bin')\n    db \"X\"\n\n; zero padding and magic bios number\ntimes 510-($-$$) db 0\ndw 0xaa55\n"
  },
  {
    "path": "03-bootsector-memory/boot_sect_memory_org.asm",
    "content": "[org 0x7c00]\nmov ah, 0x0e\n\n; attempt 1\n; Will fail again regardless of 'org' because we are still addressing the pointer\n; and not the data it points to\nmov al, \"1\"\nint 0x10\nmov al, the_secret\nint 0x10\n\n; attempt 2\n; Having solved the memory offset problem with 'org', this is now the correct answer\nmov al, \"2\"\nint 0x10\nmov al, [the_secret]\nint 0x10\n\n; attempt 3\n; As you expected, we are adding 0x7c00 twice, so this is not going to work\nmov al, \"3\"\nint 0x10\nmov bx, the_secret\nadd bx, 0x7c00\nmov al, [bx]\nint 0x10\n\n; attempt 4\n; This still works because there are no memory references to pointers, so\n; the 'org' mode never applies. Directly addressing memory by counting bytes\n; is always going to work, but it's inconvenient\nmov al, \"4\"\nint 0x10\nmov al, [0x7c2d]\nint 0x10\n\n\njmp $ ; infinite loop\n\nthe_secret:\n    ; ASCII code 0x58 ('X') is stored just before the zero-padding.\n    ; On this code that is at byte 0x2d (check it out using 'xxd file.bin')\n    db \"X\"\n\n; zero padding and magic bios number\ntimes 510-($-$$) db 0\ndw 0xaa55\n"
  },
  {
    "path": "04-bootsector-stack/README.md",
    "content": "*Concepts you may want to Google beforehand: stack*\n\n**Goal: Learn how to use the stack**\n\nThe usage of the stack is important, so we'll write yet another boot sector\nwith an example.\n\nRemember that the `bp` register stores the base address (i.e. bottom) of the stack,\nand `sp` stores the top, and that the stack grows downwards from `bp` (i.e. `sp` gets\ndecremented)\n\nThis lesson is quite straightforward, so jump ahead to the code.\n\nI suggest that you try accessing in-stack memory addresses by yourself, \nat different points in the code, and see what happens.\n"
  },
  {
    "path": "04-bootsector-stack/boot_sect_stack.asm",
    "content": "mov ah, 0x0e ; tty mode\n\nmov bp, 0x8000 ; this is an address far away from 0x7c00 so that we don't get overwritten\nmov sp, bp ; if the stack is empty then sp points to bp\n\npush 'A'\npush 'B'\npush 'C'\n\n; to show how the stack grows downwards\nmov al, [0x7ffe] ; 0x8000 - 2\nint 0x10\n\n; however, don't try to access [0x8000] now, because it won't work\n; you can only access the stack top so, at this point, only 0x7ffe (look above)\nmov al, [0x8000]\nint 0x10\n\n\n; recover our characters using the standard procedure: 'pop'\n; We can only pop full words so we need an auxiliary register to manipulate\n; the lower byte\npop bx\nmov al, bl\nint 0x10 ; prints C\n\npop bx\nmov al, bl\nint 0x10 ; prints B\n\npop bx\nmov al, bl\nint 0x10 ; prints A\n\n; data that has been pop'd from the stack is garbage now\nmov al, [0x8000]\nint 0x10\n\n\njmp $\ntimes 510-($-$$) db 0\ndw 0xaa55\n"
  },
  {
    "path": "05-bootsector-functions-strings/README.md",
    "content": "*Concepts you may want to Google beforehand: control structures,\nfunction calling, strings*\n\n**Goal: Learn how to code basic stuff (loops, functions) with the assembler**\n\nWe are close to our definitive boot sector.\n\nIn lesson 7 we will start reading from the disk, which is the last step before\nloading a kernel. But first, we will write some code with control structures,\nfunction calling, and full strings usage. We really need to be comfortable with\nthose concepts before jumping to the disk and the kernel.\n\n\nStrings\n-------\n\nDefine strings like bytes, but terminate them with a null-byte (yes, like C)\nto be able to determine their end.\n\n```nasm\nmystring:\n    db 'Hello, World', 0\n```\n\nNotice that text surrounded with quotes is converted to ASCII by the assembler,\nwhile that lone zero will be passed as byte `0x00` (null byte)\n\n\nControl structures\n------------------\n\nWe have already used one: `jmp $` for the infinite loop.\n\nAssembler jumps are defined by the *previous* instruction result. For example:\n\n```nasm\ncmp ax, 4      ; if ax = 4\nje ax_is_four  ; do something (by jumping to that label)\njmp else       ; else, do another thing\njmp endif      ; finally, resume the normal flow\n\nax_is_four:\n    .....\n    jmp endif\n\nelse:\n    .....\n    jmp endif  ; not actually necessary but printed here for completeness\n\nendif:\n```\n\nThink in your head in high level, then convert it to assembler in this fashion.\n\nThere are many `jmp` conditions: if equal, if less than, etc. They are pretty \nintuitive but you can always Google them\n\n\nCalling functions\n-----------------\n\nAs you may suppose, calling a function is just a jump to a label.\n\nThe tricky part are the parameters. There are two steps to working with parameters:\n\n1. The programmer knows they share a specific register or memory address\n2. Write a bit more code and make function calls generic and without side effects\n\nStep 1 is easy. Let's just agree that we will use `al` (actually, `ax`) for the parameters.\n\n```nasm\nmov al, 'X'\njmp print\nendprint:\n\n...\n\nprint:\n    mov ah, 0x0e  ; tty code\n    int 0x10      ; I assume that 'al' already has the character\n    jmp endprint  ; this label is also pre-agreed\n```\n\nYou can see that this approach will quickly grow into spaghetti code. The current\n`print` function will only return to `endprint`. What if some other function\nwants to call it? We are killing code reusage.\n\nThe correct solution offers two improvements:\n\n- We will store the return address so that it may vary\n- We will save the current registers to allow subfunctions to modify them\n  without any side effects\n\nTo store the return address, the CPU will help us. Instead of using a couple of\n`jmp` to call subroutines, use `call` and `ret`.\n\nTo save the register data, there is also a special command which uses the stack: `pusha`\nand its brother `popa`, which pushes all registers to the stack automatically and\nrecovers them afterwards.\n\n\nIncluding external files\n------------------------\n\nI assume you are a programmer and don't need to convince you why this is\na good idea.\n\nThe syntax is\n```nasm\n%include \"file.asm\"\n```\n\n\nPrinting hex values\n-------------------\n\nIn the next lesson we will start reading from disk, so we need some way\nto make sure that we are reading the correct data. File `boot_sect_print_hex.asm`\nextends `boot_sect_print.asm` to print hex bytes, not just ASCII chars.\n\n\nCode! \n-----\n\nLet's jump to the code. File `boot_sect_print.asm` is the subroutine which will\nget `%include`d in the main file. It uses a loop to print bytes on screen.\nIt also includes a function to print a newline. The familiar `'\\n'` is\nactually two bytes, the newline char `0x0A` and a carriage return `0x0D`. Please\nexperiment by removing the carriage return char and see its effect.\n\nAs stated above, `boot_sect_print_hex.asm` allows for printing of bytes.\n\nThe main file `boot_sect_main.asm` loads a couple strings and bytes,\ncalls `print` and `print_hex` and hangs. If you understood\nthe previous sections, it's quite straightforward.\n"
  },
  {
    "path": "05-bootsector-functions-strings/boot_sect_main.asm",
    "content": "[org 0x7c00] ; tell the assembler that our offset is bootsector code\n\n; The main routine makes sure the parameters are ready and then calls the function\nmov bx, HELLO\ncall print\n\ncall print_nl\n\nmov bx, GOODBYE\ncall print\n\ncall print_nl\n\nmov dx, 0x12fe\ncall print_hex\n\n; that's it! we can hang now\njmp $\n\n; remember to include subroutines below the hang\n%include \"boot_sect_print.asm\"\n%include \"boot_sect_print_hex.asm\"\n\n\n; data\nHELLO:\n    db 'Hello, World', 0\n\nGOODBYE:\n    db 'Goodbye', 0\n\n; padding and magic number\ntimes 510-($-$$) db 0\ndw 0xaa55\n"
  },
  {
    "path": "05-bootsector-functions-strings/boot_sect_print.asm",
    "content": "print:\n    pusha\n\n; keep this in mind:\n; while (string[i] != 0) { print string[i]; i++ }\n\n; the comparison for string end (null byte)\nstart:\n    mov al, [bx] ; 'bx' is the base address for the string\n    cmp al, 0 \n    je done\n\n    ; the part where we print with the BIOS help\n    mov ah, 0x0e\n    int 0x10 ; 'al' already contains the char\n\n    ; increment pointer and do next loop\n    add bx, 1\n    jmp start\n\ndone:\n    popa\n    ret\n\n\n\nprint_nl:\n    pusha\n    \n    mov ah, 0x0e\n    mov al, 0x0a ; newline char\n    int 0x10\n    mov al, 0x0d ; carriage return\n    int 0x10\n    \n    popa\n    ret\n"
  },
  {
    "path": "05-bootsector-functions-strings/boot_sect_print_hex.asm",
    "content": "; receiving the data in 'dx'\n; For the examples we'll assume that we're called with dx=0x1234\nprint_hex:\n    pusha\n\n    mov cx, 0 ; our index variable\n\n; Strategy: get the last char of 'dx', then convert to ASCII\n; Numeric ASCII values: '0' (ASCII 0x30) to '9' (0x39), so just add 0x30 to byte N.\n; For alphabetic characters A-F: 'A' (ASCII 0x41) to 'F' (0x46) we'll add 0x40\n; Then, move the ASCII byte to the correct position on the resulting string\nhex_loop:\n    cmp cx, 4 ; loop 4 times\n    je end\n    \n    ; 1. convert last char of 'dx' to ascii\n    mov ax, dx ; we will use 'ax' as our working register\n    and ax, 0x000f ; 0x1234 -> 0x0004 by masking first three to zeros\n    add al, 0x30 ; add 0x30 to N to convert it to ASCII \"N\"\n    cmp al, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F'\n    jle step2\n    add al, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7\n\nstep2:\n    ; 2. get the correct position of the string to place our ASCII char\n    ; bx <- base address + string length - index of char\n    mov bx, HEX_OUT + 5 ; base + length\n    sub bx, cx  ; our index variable\n    mov [bx], al ; copy the ASCII char on 'al' to the position pointed by 'bx'\n    ror dx, 4 ; 0x1234 -> 0x4123 -> 0x3412 -> 0x2341 -> 0x1234\n\n    ; increment index and loop\n    add cx, 1\n    jmp hex_loop\n\nend:\n    ; prepare the parameter and call the function\n    ; remember that print receives parameters in 'bx'\n    mov bx, HEX_OUT\n    call print\n\n    popa\n    ret\n\nHEX_OUT:\n    db '0x0000',0 ; reserve memory for our new string\n"
  },
  {
    "path": "06-bootsector-segmentation/README.md",
    "content": "*Concepts you may want to Google beforehand: segmentation*\n\n**Goal: learn how to address memory with 16-bit real mode segmentation**\n\nIf you are comfortable with segmentation, skip this lesson.\n\nWe did segmentation\nwith `[org]` on lesson 3. Segmentation means that you can specify\nan offset to all the data you refer to.\n\nThis is done by using special registers: `cs`, `ds`, `ss` and `es`, for\nCode, Data, Stack and Extra (i.e. user-defined)\n\nBeware: they are *implicitly* used by the CPU, so once you set some\nvalue for, say, `ds`, then all your memory access will be offset by `ds`.\n[Read more here](http://wiki.osdev.org/Segmentation)\n\nFurthermore, to compute the real address we don't just join the two\naddresses, but we *overlap* them: `segment << 4 + address`. For example,\nif `ds` is `0x4d`, then `[0x20]` actually refers to `0x4d0 + 0x20 = 0x4f0`\n\nEnough theory. Have a look at the code and play with it a bit.\n\nHint: We cannot `mov` literals to those registers, we have to\nuse a general purpose register before.\n"
  },
  {
    "path": "06-bootsector-segmentation/boot_sect_segmentation.asm",
    "content": "mov ah, 0x0e ; tty\n\nmov al, [the_secret]\nint 0x10 ; we already saw this doesn't work, right?\n\nmov bx, 0x7c0 ; remember, the segment is automatically <<4 for you\nmov ds, bx\n; WARNING: from now on all memory references will be offset by 'ds' implicitly\nmov al, [the_secret]\nint 0x10\n\nmov al, [es:the_secret]\nint 0x10 ; doesn't look right... isn't 'es' currently 0x000?\n\nmov bx, 0x7c0\nmov es, bx\nmov al, [es:the_secret]\nint 0x10\n\n\njmp $\n\nthe_secret:\n    db \"X\"\n\ntimes 510 - ($-$$) db 0\ndw 0xaa55\n"
  },
  {
    "path": "07-bootsector-disk/README.md",
    "content": "*Concepts you may want to Google beforehand: hard disk, cylinder, head, sector, \ncarry bit*\n\n**Goal: Let the bootsector load data from disk in order to boot the kernel**\n\nOur OS won't fit inside the bootsector 512 bytes, so we need to read data from\na disk in order to run the kernel.\n\nThankfully, we don't have to deal with turning spinning platters on and off,\nwe can just call some BIOS routines, like we did to print characters on the screen.\nTo do so, we set `al` to `0x02` (and other registers with the required cylinder, head\nand sector) and raise `int 0x13`\n\nYou can access [a detailed int 13h guide here](http://stanislavs.org/helppc/int_13-2.html)\n\nOn this lesson we will use for the first time the *carry bit*, which is an extra bit\npresent on each register which stores when an operation has overflowed its current\ncapacity:\n\n```nasm\nmov ax, 0xFFFF\nadd ax, 1 ; ax = 0x0000 and carry = 1\n```\n\nThe carry isn't accessed directly but used as a control structure by other operators,\nlike `jc` (jump if the carry bit is set)\n\nThe BIOS also sets `al` to the number of sectors read, so always compare it\nto the expected number.\n\n\nCode\n----\n\nOpen and examine `boot_sect_disk.asm` for the complete routine that\nreads from disk.\n\n`boot_sect_main.asm` prepares the parameters for disk read and calls `disk_load`.\nNotice how we write some extra data which does not actually belong to the boot\nsector, since it is outside the 512 bits mark.\n\nThe boot sector is actually sector 1 (the first one, sectors start at 1)\nof cylinder 0 of head 0 of hdd 0.\n\nThus, any bytes after byte 512 correspond to sector 2 of cylinder 0 of head 0 of hdd 0\n\nThe main routine will fill it with sample data and then let the bootsector\nread it.\n\n**Note: if you keep getting errors and your code seems fine, make sure that qemu\nis booting from the right drive and set the drive on `dl` accordingly**\n\nThe BIOS sets `dl` to the drive number before calling the bootloader. However,\nI found some problems with qemu when booting from the hdd.\n\nThere are two quick options:\n\n1. Try the flag `-fda` for example, `qemu -fda boot_sect_main.bin` which will set `dl`\nas `0x00`, it seems to work fine then.\n2. Explicitly use the flag `-boot`, e.g. `qemu boot_sect_main.bin -boot c` which \nautomatically sets `dl` as `0x80` and lets the bootloader read data\n\n\n"
  },
  {
    "path": "07-bootsector-disk/boot_sect_disk.asm",
    "content": "; load 'dh' sectors from drive 'dl' into ES:BX\ndisk_load:\n    pusha\n    ; reading from disk requires setting specific values in all registers\n    ; so we will overwrite our input parameters from 'dx'. Let's save it\n    ; to the stack for later use.\n    push dx\n\n    mov ah, 0x02 ; ah <- int 0x13 function. 0x02 = 'read'\n    mov al, dh   ; al <- number of sectors to read (0x01 .. 0x80)\n    mov cl, 0x02 ; cl <- sector (0x01 .. 0x11)\n                 ; 0x01 is our boot sector, 0x02 is the first 'available' sector\n    mov ch, 0x00 ; ch <- cylinder (0x0 .. 0x3FF, upper 2 bits in 'cl')\n    ; dl <- drive number. Our caller sets it as a parameter and gets it from BIOS\n    ; (0 = floppy, 1 = floppy2, 0x80 = hdd, 0x81 = hdd2)\n    mov dh, 0x00 ; dh <- head number (0x0 .. 0xF)\n\n    ; [es:bx] <- pointer to buffer where the data will be stored\n    ; caller sets it up for us, and it is actually the standard location for int 13h\n    int 0x13      ; BIOS interrupt\n    jc disk_error ; if error (stored in the carry bit)\n\n    pop dx\n    cmp al, dh    ; BIOS also sets 'al' to the # of sectors read. Compare it.\n    jne sectors_error\n    popa\n    ret\n\n\ndisk_error:\n    mov bx, DISK_ERROR\n    call print\n    call print_nl\n    mov dh, ah ; ah = error code, dl = disk drive that dropped the error\n    call print_hex ; check out the code at http://stanislavs.org/helppc/int_13-1.html\n    jmp disk_loop\n\nsectors_error:\n    mov bx, SECTORS_ERROR\n    call print\n\ndisk_loop:\n    jmp $\n\nDISK_ERROR: db \"Disk read error\", 0\nSECTORS_ERROR: db \"Incorrect number of sectors read\", 0\n"
  },
  {
    "path": "07-bootsector-disk/boot_sect_main.asm",
    "content": "[org 0x7c00]\n    mov bp, 0x8000 ; set the stack safely away from us\n    mov sp, bp\n\n    mov bx, 0x9000 ; es:bx = 0x0000:0x9000 = 0x09000\n    mov dh, 2 ; read 2 sectors\n    ; the bios sets 'dl' for our boot disk number\n    ; if you have trouble, use the '-fda' flag: 'qemu -fda file.bin'\n    call disk_load\n\n    mov dx, [0x9000] ; retrieve the first loaded word, 0xdada\n    call print_hex\n\n    call print_nl\n\n    mov dx, [0x9000 + 512] ; first word from second loaded sector, 0xface\n    call print_hex\n\n    jmp $\n\n%include \"../05-bootsector-functions-strings/boot_sect_print.asm\"\n%include \"../05-bootsector-functions-strings/boot_sect_print_hex.asm\"\n%include \"boot_sect_disk.asm\"\n\n; Magic number\ntimes 510 - ($-$$) db 0\ndw 0xaa55\n\n; boot sector = sector 1 of cyl 0 of head 0 of hdd 0\n; from now on = sector 2 ...\ntimes 256 dw 0xdada ; sector 2 = 512 bytes\ntimes 256 dw 0xface ; sector 3 = 512 bytes\n"
  },
  {
    "path": "08-32bit-print/32bit-print.asm",
    "content": "[bits 32] ; using 32-bit protected mode\n\n; this is how constants are defined\nVIDEO_MEMORY equ 0xb8000\nWHITE_ON_BLACK equ 0x0f ; the color byte for each character\n\nprint_string_pm:\n    pusha\n    mov edx, VIDEO_MEMORY\n\nprint_string_pm_loop:\n    mov al, [ebx] ; [ebx] is the address of our character\n    mov ah, WHITE_ON_BLACK\n\n    cmp al, 0 ; check if end of string\n    je print_string_pm_done\n\n    mov [edx], ax ; store character + attribute in video memory\n    add ebx, 1 ; next char\n    add edx, 2 ; next video memory position\n\n    jmp print_string_pm_loop\n\nprint_string_pm_done:\n    popa\n    ret\n"
  },
  {
    "path": "08-32bit-print/README.md",
    "content": "*Concepts you may want to Google beforehand: 32-bit protected mode, VGA, video \nmemory*\n\n**Goal: Print on the screen when on 32-bit protected mode**\n\n32-bit mode allows us to use 32 bit registers and memory addressing, \nprotected memory, virtual memory and other advantages, but we will lose\nBIOS interrupts and we'll need to code the GDT (more on this later)\n\nIn this lesson we will write a new print string routine which works in\n32-bit mode, where we don't have BIOS interrupts, by directly manipulating\nthe VGA video memory instead of calling `int 0x10`. The VGA memory starts\nat address `0xb8000` and it has a text mode which is useful to avoid\nmanipulating direct pixels.\n\n\nThe formula for accessing a specific character on the 80x25 grid is:\n\n`0xb8000 + 2 * (row * 80 + col)`\n\nThat is, every character uses 2 bytes (one for the ASCII, another for \ncolor and such), and we see that the structure of the memory concatenates\nrows.\n\nOpen `32bit-print.asm` to see the code. It will always print the string\non the top left of the screen, but soon we'll write higher level routines\nto replace it.\n\nUnfortunately we cannot yet call this routine from the bootloader, because\nwe still don't know how to write the GDT and enter protected mode. Once\nyou have understood the code, jump to the next lesson.\n"
  },
  {
    "path": "09-32bit-gdt/32bit-gdt.asm",
    "content": "gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps\n    ; the GDT starts with a null 8-byte\n    dd 0x0 ; 4 byte\n    dd 0x0 ; 4 byte\n\n; GDT for code segment. base = 0x00000000, length = 0xfffff\n; for flags, refer to os-dev.pdf document, page 36\ngdt_code: \n    dw 0xffff    ; segment length, bits 0-15\n    dw 0x0       ; segment base, bits 0-15\n    db 0x0       ; segment base, bits 16-23\n    db 10011010b ; flags (8 bits)\n    db 11001111b ; flags (4 bits) + segment length, bits 16-19\n    db 0x0       ; segment base, bits 24-31\n\n; GDT for data segment. base and length identical to code segment\n; some flags changed, again, refer to os-dev.pdf\ngdt_data:\n    dw 0xffff\n    dw 0x0\n    db 0x0\n    db 10010010b\n    db 11001111b\n    db 0x0\n\ngdt_end:\n\n; GDT descriptor\ngdt_descriptor:\n    dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size\n    dd gdt_start ; address (32 bit)\n\n; define some constants for later use\nCODE_SEG equ gdt_code - gdt_start\nDATA_SEG equ gdt_data - gdt_start\n"
  },
  {
    "path": "09-32bit-gdt/README.md",
    "content": "*Concepts you may want to Google beforehand: GDT*\n\n**Goal: program the GDT**\n\nRemember segmentation from lesson 6? The offset was left shifted\nto address an extra level of indirection.\n\nIn 32-bit mode, segmentation works differently. Now, the offset becomes an\nindex to a segment descriptor (SD) in the GDT. This descriptor defines\nthe base address (32 bits), the size (20 bits) and some flags, like\nreadonly, permissions, etc. To add confusion, the data structures are split,\nso open the os-dev.pdf file and check out the figure on page 34 or the \nWikipedia page for the GDT.\n\nThe easiest way to program the GDT is to define two segments, one for code\nand another for data. These can overlap which means there is no memory protection,\nbut it's good enough to boot, we'll fix this later with a higher language.\n\nAs a curiosity, the first GDT entry must be `0x00` to make sure that the\nprogrammer didn't make any mistakes managing addresses.\n\nFurthermore, the CPU can't directly load the GDT address, but it requires\na meta structure called the \"GDT descriptor\" with the size (16b) and address\n(32b) of our actual GDT. It is loaded with the `lgdt` operation.\n\nLet's directly jump to the GDT code in assembly. Again, to understand\nall the segment flags, refer to the os-dev.pdf document. The theory for\nthis lesson is quite complex.\n\nIn the next lesson we will make the switch to 32-bit protected mode\nand test our code from these lessons.\n"
  },
  {
    "path": "10-32bit-enter/32bit-main.asm",
    "content": "[org 0x7c00] ; bootloader offset\n    mov bp, 0x9000 ; set the stack\n    mov sp, bp\n\n    mov bx, MSG_REAL_MODE\n    call print ; This will be written after the BIOS messages\n\n    call switch_to_pm\n    jmp $ ; this will actually never be executed\n\n%include \"../05-bootsector-functions-strings/boot_sect_print.asm\"\n%include \"../09-32bit-gdt/32bit-gdt.asm\"\n%include \"../08-32bit-print/32bit-print.asm\"\n%include \"32bit-switch.asm\"\n\n[bits 32]\nBEGIN_PM: ; after the switch we will get here\n    mov ebx, MSG_PROT_MODE\n    call print_string_pm ; Note that this will be written at the top left corner\n    jmp $\n\nMSG_REAL_MODE db \"Started in 16-bit real mode\", 0\nMSG_PROT_MODE db \"Loaded 32-bit protected mode\", 0\n\n; bootsector\ntimes 510-($-$$) db 0\ndw 0xaa55\n"
  },
  {
    "path": "10-32bit-enter/32bit-switch.asm",
    "content": "[bits 16]\nswitch_to_pm:\n    cli ; 1. disable interrupts\n    lgdt [gdt_descriptor] ; 2. load the GDT descriptor\n    mov eax, cr0\n    or eax, 0x1 ; 3. set 32-bit mode bit in cr0\n    mov cr0, eax\n    jmp CODE_SEG:init_pm ; 4. far jump by using a different segment\n\n[bits 32]\ninit_pm: ; we are now using 32-bit instructions\n    mov ax, DATA_SEG ; 5. update the segment registers\n    mov ds, ax\n    mov ss, ax\n    mov es, ax\n    mov fs, ax\n    mov gs, ax\n\n    mov ebp, 0x90000 ; 6. update the stack right at the top of the free space\n    mov esp, ebp\n\n    call BEGIN_PM ; 7. Call a well-known label with useful code\n"
  },
  {
    "path": "10-32bit-enter/README.md",
    "content": "*Concepts you may want to Google beforehand: interrupts, pipelining*\n\n**Goal: Enter 32-bit protected mode and test our code from previous lessons**\n\nTo jump into 32-bit mode:\n\n1. Disable interrupts\n2. Load our GDT\n3. Set a bit on the CPU control register `cr0`\n4. Flush the CPU pipeline by issuing a carefully crafted far jump\n5. Update all the segment registers\n6. Update the stack\n7. Call to a well-known label which contains the first useful code in 32 bits\n\nWe will encapsulate this process on the file `32bit-switch.asm`. Open it\nand take a look at the code.\n\nAfter entering 32-bit mode, we will call `BEGIN_PM` which is the entry point\nfor our actual useful code (e.g. kernel code, etc). You can read the code\nat `32bit-main.asm`. Compile and run this last file and you will see the two \nmessages on the screen.\n\nCongratulations! Our next step will be to write a simple kernel\n"
  },
  {
    "path": "11-kernel-crosscompiler/README.md",
    "content": "*Concepts you may want to Google beforehand: cross-compiler*\n\n**Goal: Create a development environment to build your kernel**\n\nIf you're using a Mac, you will need to do this process right away. Otherwise, it could have waited\nfor a few more lessons. Anyway, you will need a cross-compiler once we jump to developing in a higher\nlanguage, that is, C. [Read why](http://wiki.osdev.org/Why_do_I_need_a_Cross_Compiler%3F)\n\nI'll be adapting the instructions [at the OSDev wiki](http://wiki.osdev.org/GCC_Cross-Compiler). \n\n\nRequired packages\n-----------------\n\nFirst, install the required packages. On linux, use your package distribution. On a Mac, [install brew](http://brew.sh/) if\nyou didn't do it on lesson 00, and get those packages with `brew install`\n\n- gmp\n- mpfr\n- libmpc\n- gcc\n\nYes, we will need `gcc` to build our cross-compiled `gcc`, especially on a Mac where gcc has been deprecated for `clang`\n\nOnce installed, find where your packaged gcc is (remember, not clang) and export it. For example:\n\n```\nexport CC=/usr/local/bin/gcc-4.9\nexport LD=/usr/local/bin/gcc-4.9\n```\n\nWe will need to build binutils and a cross-compiled gcc, and we will put them into `/usr/local/i386elfgcc`, so\nlet's export some paths now. Feel free to change them to your liking.\n\n```\nexport PREFIX=\"/usr/local/i386elfgcc\"\nexport TARGET=i386-elf\nexport PATH=\"$PREFIX/bin:$PATH\"\n```\n\nbinutils\n--------\n\nRemember: always be careful before pasting walls of text from the internet. I recommend copying line by line.\n\n```sh\nmkdir /tmp/src\ncd /tmp/src\ncurl -O http://ftp.gnu.org/gnu/binutils/binutils-2.24.tar.gz # If the link 404's, look for a more recent version\ntar xf binutils-2.24.tar.gz\nmkdir binutils-build\ncd binutils-build\n../binutils-2.24/configure --target=$TARGET --enable-interwork --enable-multilib --disable-nls --disable-werror --prefix=$PREFIX 2>&1 | tee configure.log\nmake all install 2>&1 | tee make.log\n```\n\ngcc\n---\n```sh\ncd /tmp/src\ncurl -O https://ftp.gnu.org/gnu/gcc/gcc-4.9.1/gcc-4.9.1.tar.bz2\ntar xf gcc-4.9.1.tar.bz2\nmkdir gcc-build\ncd gcc-build\n../gcc-4.9.1/configure --target=$TARGET --prefix=\"$PREFIX\" --disable-nls --disable-libssp --enable-languages=c --without-headers\nmake all-gcc \nmake all-target-libgcc \nmake install-gcc \nmake install-target-libgcc \n```\n\nThat's it! You should have all the GNU binutils and the compiler at `/usr/local/i386elfgcc/bin`, prefixed by `i386-elf-` to avoid\ncollisions with your system's compiler and binutils.\n\nYou may want to add the `$PATH` to your `.bashrc`. From now on, on this tutorial, we will explicitly use the prefixes when using\nthe cross-compiled gcc.\n"
  },
  {
    "path": "12-kernel-c/README.md",
    "content": "*Concepts you may want to Google beforehand: C, object code, linker, disassemble*\n\n**Goal: Learn to write the same low-level code as we did with assembler, but in C**\n\n\nCompile\n-------\n\nLet's see how the C compiler compiles our code and compare it to the machine code\ngenerated with the assembler.\n\nWe will start writing a simple program which contains a function, `function.c`.\nOpen the file and examine it.\n\nTo compile system-independent code, we need the flag `-ffreestanding`, so compile\n`function.c` in this fashion:\n\n`i386-elf-gcc -ffreestanding -c function.c -o function.o`\n\nLet's examine the machine code generated by the compiler:\n\n`i386-elf-objdump -d function.o`\n\nNow that is something we recognize, isn't it?\n\n\nLink\n----\n\nFinally, to produce a binary file, we will use the linker. An important part of this\nstep is to learn how high level languages call function labels. Which is the offset\nwhere our function will be placed in memory? We don't actually know. For this\nexample, we'll place the offset at `0x0` and use the `binary` format which\ngenerates machine code without any labels and/or metadata\n\n`i386-elf-ld -o function.bin -Ttext 0x0 --oformat binary function.o`\n\n*Note: a warning may appear when linking, disregard it*\n\nNow examine both \"binary\" files, `function.o` and `function.bin` using `xxd`. You\nwill see that the `.bin` file is machine code, while the `.o` file has a lot\nof debugging information, labels, etc.\n\n\nDecompile\n---------\n\nAs a curiosity, we will examine the machine code.\n\n`ndisasm -b 32 function.bin`\n\n\nMore\n----\n\nI encourage you to write more small programs, which feature:\n\n- Local variables `localvars.c`\n- Function calls `functioncalls.c`\n- Pointers `pointers.c`\n\nThen compile and disassemble them, and examine the resulting machine code. Follow\nthe os-guide.pdf for explanations. Try to answer this question: why does the\ndisassemblement of `pointers.c` not resemble what you would expect? Where is\nthe ASCII `0x48656c6c6f` for \"Hello\"?\n"
  },
  {
    "path": "12-kernel-c/function.c",
    "content": "int my_function() {\n    return 0xbaba;\n}\n"
  },
  {
    "path": "12-kernel-c/functioncalls.c",
    "content": "void caller() {\n    my_func(0xdede);\n}\n\nint my_func(int arg) {\n    return arg;\n}\n"
  },
  {
    "path": "12-kernel-c/localvars.c",
    "content": "int my_function() {\n    int my_var = 0xbaba;\n    return my_var;\n}\n"
  },
  {
    "path": "12-kernel-c/pointers.c",
    "content": "void func() {\n    char* string = \"Hello\";\n}\n"
  },
  {
    "path": "13-kernel-barebones/Makefile",
    "content": "# $@ = target file\n# $< = first dependency\n# $^ = all dependencies\n\n# First rule is the one executed when no parameters are fed to the Makefile\nall: run\n\n# Notice how dependencies are built as needed\nkernel.bin: kernel_entry.o kernel.o\n\ti386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary\n\nkernel_entry.o: kernel_entry.asm\n\tnasm $< -f elf -o $@\n\nkernel.o: kernel.c\n\ti386-elf-gcc -ffreestanding -c $< -o $@\n\n# Rule to disassemble the kernel - may be useful to debug\nkernel.dis: kernel.bin\n\tndisasm -b 32 $< > $@\n\nbootsect.bin: bootsect.asm\n\tnasm $< -f bin -o $@\n\nos-image.bin: bootsect.bin kernel.bin\n\tcat $^ > $@\n\nrun: os-image.bin\n\tqemu-system-i386 -fda $<\n\nclean:\n\trm *.bin *.o *.dis\n"
  },
  {
    "path": "13-kernel-barebones/README.md",
    "content": "*Concepts you may want to Google beforehand: kernel, ELF format, makefile*\n\n**Goal: Create a simple kernel and a bootsector capable of booting it**\n\nThe kernel\n----------\n\nOur C kernel will just print an 'X' on the top left corner of the screen. Go ahead\nand open `kernel.c`.\n\nYou will notice a dummy function that does nothing. That function will force us\nto create a kernel entry routine which does not point to byte 0x0 in our kernel, but\nto an actual label which we know that launches it. In our case, function `main()`.\n\n`i386-elf-gcc -ffreestanding -c kernel.c -o kernel.o`\n\nThat routine is coded on `kernel_entry.asm`. Read it and you will learn how to\nuse `[extern]` declarations in assembly. To compile this file, instead of generating\na binary, we will generate an `elf` format file which will be linked with `kernel.o`\n\n`nasm kernel_entry.asm -f elf -o kernel_entry.o`\n\n\nThe linker\n----------\n\nA linker is a very powerful tool and we only started to benefit from it.\n\nTo link both object files into a single binary kernel and resolve label references,\nrun:\n\n`i386-elf-ld -o kernel.bin -Ttext 0x1000 kernel_entry.o kernel.o --oformat binary`\n\nNotice how our kernel will be placed not at `0x0` in memory, but at `0x1000`. The\nbootsector will need to know this address too.\n\n\nThe bootsector\n--------------\n\nIt is very similar to the one in lesson 10. Open `bootsect.asm` and examine the code.\nActually, if you remove all the lines used to print messages on the screen, it accounts\nto a couple dozen lines.\n\nCompile it with `nasm bootsect.asm -f bin -o bootsect.bin`\n\n\nPutting it all together\n-----------------------\n\nNow what? We have two separate files for the bootsector and the kernel?\n\nCan't we just \"link\" them together into a single file? Yes, we can, and it's easy,\njust concatenate them:\n\n`cat bootsect.bin kernel.bin > os-image.bin`\n\n\nRun!\n----\n\nYou can now run `os-image.bin` with qemu.\n\nRemember that if you find disk load errors you may need to play with the disk numbers\nor qemu parameters (floppy = `0x0`, hdd = `0x80`). I usually use `qemu-system-i386 -fda os-image.bin`\n\nYou will see four messages:\n\n- \"Started in 16-bit Real Mode\"\n- \"Loading kernel into memory\"\n- (Top left) \"Landed in 32-bit Protected Mode\"\n- (Top left, overwriting previous message) \"X\"\n\nCongratulations!\n\n\nMakefile\n--------\n\nAs a last step, we will tidy up the compilation process with a Makefile. Open the `Makefile`\nscript and examine its contents. If you don't know what a Makefile is, now is a good time\nto Google and learn it, as this will save us a lot of time in the future.\n"
  },
  {
    "path": "13-kernel-barebones/bootsect.asm",
    "content": "[org 0x7c00]\nKERNEL_OFFSET equ 0x1000 ; The same one we used when linking the kernel\n\n    mov [BOOT_DRIVE], dl ; Remember that the BIOS sets us the boot drive in 'dl' on boot\n    mov bp, 0x9000\n    mov sp, bp\n\n    mov bx, MSG_REAL_MODE \n    call print\n    call print_nl\n\n    call load_kernel ; read the kernel from disk\n    call switch_to_pm ; disable interrupts, load GDT,  etc. Finally jumps to 'BEGIN_PM'\n    jmp $ ; Never executed\n\n%include \"../05-bootsector-functions-strings/boot_sect_print.asm\"\n%include \"../05-bootsector-functions-strings/boot_sect_print_hex.asm\"\n%include \"../07-bootsector-disk/boot_sect_disk.asm\"\n%include \"../09-32bit-gdt/32bit-gdt.asm\"\n%include \"../08-32bit-print/32bit-print.asm\"\n%include \"../10-32bit-enter/32bit-switch.asm\"\n\n[bits 16]\nload_kernel:\n    mov bx, MSG_LOAD_KERNEL\n    call print\n    call print_nl\n\n    mov bx, KERNEL_OFFSET ; Read from disk and store in 0x1000\n    mov dh, 2\n    mov dl, [BOOT_DRIVE]\n    call disk_load\n    ret\n\n[bits 32]\nBEGIN_PM:\n    mov ebx, MSG_PROT_MODE\n    call print_string_pm\n    call KERNEL_OFFSET ; Give control to the kernel\n    jmp $ ; Stay here when the kernel returns control to us (if ever)\n\n\nBOOT_DRIVE db 0 ; It is a good idea to store it in memory because 'dl' may get overwritten\nMSG_REAL_MODE db \"Started in 16-bit Real Mode\", 0\nMSG_PROT_MODE db \"Landed in 32-bit Protected Mode\", 0\nMSG_LOAD_KERNEL db \"Loading kernel into memory\", 0\n\n; padding\ntimes 510 - ($-$$) db 0\ndw 0xaa55\n"
  },
  {
    "path": "13-kernel-barebones/kernel.c",
    "content": "/* This will force us to create a kernel entry function instead of jumping to kernel.c:0x00 */\nvoid dummy_test_entrypoint() {\n}\n\nvoid main() {\n    char* video_memory = (char*) 0xb8000;\n    *video_memory = 'X';\n}\n"
  },
  {
    "path": "13-kernel-barebones/kernel_entry.asm",
    "content": "[bits 32]\n[extern main] ; Define calling point. Must have same name as kernel.c 'main' function\ncall main ; Calls the C function. The linker will know where it is placed in memory\njmp $\n"
  },
  {
    "path": "14-checkpoint/Makefile",
    "content": "C_SOURCES = $(wildcard kernel/*.c drivers/*.c)\nHEADERS = $(wildcard kernel/*.h drivers/*.h)\n# Nice syntax for file extension replacement\nOBJ = ${C_SOURCES:.c=.o}\n\n# Change this if your cross-compiler is somewhere else\nCC = /usr/local/i386elfgcc/bin/i386-elf-gcc\nGDB = /usr/local/i386elfgcc/bin/i386-elf-gdb\n# -g: Use debugging symbols in gcc\nCFLAGS = -g\n\n# First rule is run by default\nos-image.bin: boot/bootsect.bin kernel.bin\n\tcat $^ > os-image.bin\n\n# '--oformat binary' deletes all symbols as a collateral, so we don't need\n# to 'strip' them manually on this case\nkernel.bin: boot/kernel_entry.o ${OBJ}\n\ti386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary\n\n# Used for debugging purposes\nkernel.elf: boot/kernel_entry.o ${OBJ}\n\ti386-elf-ld -o $@ -Ttext 0x1000 $^ \n\nrun: os-image.bin\n\tqemu-system-i386 -fda os-image.bin\n\n# Open the connection to qemu and load our kernel-object file with symbols\ndebug: os-image.bin kernel.elf\n\tqemu-system-i386 -s -fda os-image.bin &\n\t${GDB} -ex \"target remote localhost:1234\" -ex \"symbol-file kernel.elf\"\n\n# Generic rules for wildcards\n# To make an object, always compile from its .c\n%.o: %.c ${HEADERS}\n\t${CC} ${CFLAGS} -ffreestanding -c $< -o $@\n\n%.o: %.asm\n\tnasm $< -f elf -o $@\n\n%.bin: %.asm\n\tnasm $< -f bin -o $@\n\nclean:\n\trm -rf *.bin *.dis *.o os-image.bin *.elf\n\trm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o\n"
  },
  {
    "path": "14-checkpoint/README.md",
    "content": "*Concepts you may want to Google beforehand: monolithic kernel, microkernel, debugger, gdb*\n\n**Goal: Pause and organize our code a little bit. Then learn how to debug the kernel with gdb**\n\nMaybe you didn't realize it, but you already have your own kernel\nrunning!\n\nHowever, it does very little, just print an 'X'. Now is the time to stop for\na moment and organize the code into folders, create a scalable Makefile for future code,\nand think on a strategy.\n\nTake a look at the new folder structure. Most of the files have been symlinked\nfrom previous lessons, so if we have to change them at some point, it will be\na better idea to remove the symlink and create a new file.\n\nFurthermore, since from now on we will use mostly C to code, we'll take advantage of qemu's\nability to open a connection to gdb. First, let's install a cross-compiled `gdb` since\nOSX uses `lldb` which is not compatible with the ELF file format (neither is the `gdb` available\non Homebrew's repos)\n\n```sh\ncd /tmp/src\ncurl -O http://ftp.rediris.es/mirror/GNU/gdb/gdb-7.8.tar.gz\ntar xf gdb-7.8.tar.gz\nmkdir gdb-build\ncd gdb-build\nexport PREFIX=\"/usr/local/i386elfgcc\"\nexport TARGET=i386-elf\n../gdb-7.8/configure --target=\"$TARGET\" --prefix=\"$PREFIX\" --program-prefix=i386-elf-\nmake\nmake install\n```\n\nCheck out the Makefile target `make debug`. This target uses builds `kernel.elf`, which\nis an object file (not binary) with all the symbols we generated on the kernel, thanks to\nthe `-g` flag on gcc. Please examine it with `xxd` and you'll see some strings. Actually,\nthe correct way to examine the strings in an object file is by `strings kernel.elf`\n\nWe can take advantage of this cool qemu feature. Type `make debug` and, on the gdb shell:\n\n- Set up a breakpoint in `kernel.c:main()`: `b main`\n- Run the OS: `continue`\n- Run two steps into the code: `next` then `next`. You will see that we are just about to set\n  the 'X' on the screen, but it isn't there yet (check out the qemu screen)\n- Let's see what's in the video memory: `print *video_memory`. There is the 'L' from \"Landed in\n  32-bit Protected Mode\"\n- Hmmm, let's make sure that `video_memory` points to the correct address: `print video_memory`\n- `next` to put there our 'X'\n- Let's make sure: `print *video_memory` and look at the qemu screen. It's definitely there.\n\nNow is a good time to read some tutorial on `gdb` and learn super useful things like `info registers`\nwhich will save us a lot of time in the future!\n\n\nYou may notice that, since this is a tutorial, we haven't yet discussed which kind\nof kernel we will write. It will probably be a monolithic one since they are easier\nto design and implement, and after all this is our first OS. Maybe in the future\nwe'll add a lesson \"15-b\" with a microkernel design. Who knows.\n"
  },
  {
    "path": "14-checkpoint/boot/32bit_print.asm",
    "content": "[bits 32] ; using 32-bit protected mode\n\n; this is how constants are defined\nVIDEO_MEMORY equ 0xb8000\nWHITE_OB_BLACK equ 0x0f ; the color byte for each character\n\nprint_string_pm:\n    pusha\n    mov edx, VIDEO_MEMORY\n\nprint_string_pm_loop:\n    mov al, [ebx] ; [ebx] is the address of our character\n    mov ah, WHITE_OB_BLACK\n\n    cmp al, 0 ; check if end of string\n    je print_string_pm_done\n\n    mov [edx], ax ; store character + attribute in video memory\n    add ebx, 1 ; next char\n    add edx, 2 ; next video memory position\n\n    jmp print_string_pm_loop\n\nprint_string_pm_done:\n    popa\n    ret\n"
  },
  {
    "path": "14-checkpoint/boot/bootsect.asm",
    "content": "; Identical to lesson 13's boot sector, but the %included files have new paths\n[org 0x7c00]\nKERNEL_OFFSET equ 0x1000 ; The same one we used when linking the kernel\n\n    mov [BOOT_DRIVE], dl ; Remember that the BIOS sets us the boot drive in 'dl' on boot\n    mov bp, 0x9000\n    mov sp, bp\n\n    mov bx, MSG_REAL_MODE \n    call print\n    call print_nl\n\n    call load_kernel ; read the kernel from disk\n    call switch_to_pm ; disable interrupts, load GDT,  etc. Finally jumps to 'BEGIN_PM'\n    jmp $ ; Never executed\n\n%include \"boot/print.asm\"\n%include \"boot/print_hex.asm\"\n%include \"boot/disk.asm\"\n%include \"boot/gdt.asm\"\n%include \"boot/32bit_print.asm\"\n%include \"boot/switch_pm.asm\"\n\n[bits 16]\nload_kernel:\n    mov bx, MSG_LOAD_KERNEL\n    call print\n    call print_nl\n\n    mov bx, KERNEL_OFFSET ; Read from disk and store in 0x1000\n    mov dh, 16 ; Our future kernel will be larger, make this big\n    mov dl, [BOOT_DRIVE]\n    call disk_load\n    ret\n\n[bits 32]\nBEGIN_PM:\n    mov ebx, MSG_PROT_MODE\n    call print_string_pm\n    call KERNEL_OFFSET ; Give control to the kernel\n    jmp $ ; Stay here when the kernel returns control to us (if ever)\n\n\nBOOT_DRIVE db 0 ; It is a good idea to store it in memory because 'dl' may get overwritten\nMSG_REAL_MODE db \"Started in 16-bit Real Mode\", 0\nMSG_PROT_MODE db \"Landed in 32-bit Protected Mode\", 0\nMSG_LOAD_KERNEL db \"Loading kernel into memory\", 0\n\n; padding\ntimes 510 - ($-$$) db 0\ndw 0xaa55\n"
  },
  {
    "path": "14-checkpoint/boot/disk.asm",
    "content": "; load 'dh' sectors from drive 'dl' into ES:BX\ndisk_load:\n    pusha\n    ; reading from disk requires setting specific values in all registers\n    ; so we will overwrite our input parameters from 'dx'. Let's save it\n    ; to the stack for later use.\n    push dx\n\n    mov ah, 0x02 ; ah <- int 0x13 function. 0x02 = 'read'\n    mov al, dh   ; al <- number of sectors to read (0x01 .. 0x80)\n    mov cl, 0x02 ; cl <- sector (0x01 .. 0x11)\n                 ; 0x01 is our boot sector, 0x02 is the first 'available' sector\n    mov ch, 0x00 ; ch <- cylinder (0x0 .. 0x3FF, upper 2 bits in 'cl')\n    ; dl <- drive number. Our caller sets it as a parameter and gets it from BIOS\n    ; (0 = floppy, 1 = floppy2, 0x80 = hdd, 0x81 = hdd2)\n    mov dh, 0x00 ; dh <- head number (0x0 .. 0xF)\n\n    ; [es:bx] <- pointer to buffer where the data will be stored\n    ; caller sets it up for us, and it is actually the standard location for int 13h\n    int 0x13      ; BIOS interrupt\n    jc disk_error ; if error (stored in the carry bit)\n\n    pop dx\n    cmp al, dh    ; BIOS also sets 'al' to the # of sectors read. Compare it.\n    jne sectors_error\n    popa\n    ret\n\n\ndisk_error:\n    mov bx, DISK_ERROR\n    call print\n    call print_nl\n    mov dh, ah ; ah = error code, dl = disk drive that dropped the error\n    call print_hex ; check out the code at http://stanislavs.org/helppc/int_13-1.html\n    jmp disk_loop\n\nsectors_error:\n    mov bx, SECTORS_ERROR\n    call print\n\ndisk_loop:\n    jmp $\n\nDISK_ERROR: db \"Disk read error\", 0\nSECTORS_ERROR: db \"Incorrect number of sectors read\", 0\n"
  },
  {
    "path": "14-checkpoint/boot/gdt.asm",
    "content": "gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps\n    ; the GDT starts with a null 8-byte\n    dd 0x0 ; 4 byte\n    dd 0x0 ; 4 byte\n\n; GDT for code segment. base = 0x00000000, length = 0xfffff\n; for flags, refer to os-dev.pdf document, page 36\ngdt_code: \n    dw 0xffff    ; segment length, bits 0-15\n    dw 0x0       ; segment base, bits 0-15\n    db 0x0       ; segment base, bits 16-23\n    db 10011010b ; flags (8 bits)\n    db 11001111b ; flags (4 bits) + segment length, bits 16-19\n    db 0x0       ; segment base, bits 24-31\n\n; GDT for data segment. base and length identical to code segment\n; some flags changed, again, refer to os-dev.pdf\ngdt_data:\n    dw 0xffff\n    dw 0x0\n    db 0x0\n    db 10010010b\n    db 11001111b\n    db 0x0\n\ngdt_end:\n\n; GDT descriptor\ngdt_descriptor:\n    dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size\n    dd gdt_start ; address (32 bit)\n\n; define some constants for later use\nCODE_SEG equ gdt_code - gdt_start\nDATA_SEG equ gdt_data - gdt_start\n"
  },
  {
    "path": "14-checkpoint/boot/kernel_entry.asm",
    "content": "[bits 32]\n[extern main] ; Define calling point. Must have same name as kernel.c 'main' function\ncall main ; Calls the C function. The linker will know where it is placed in memory\njmp $\n"
  },
  {
    "path": "14-checkpoint/boot/print.asm",
    "content": "print:\n    pusha\n\n; keep this in mind:\n; while (string[i] != 0) { print string[i]; i++ }\n\n; the comparison for string end (null byte)\nstart:\n    mov al, [bx] ; 'bx' is the base address for the string\n    cmp al, 0 \n    je done\n\n    ; the part where we print with the BIOS help\n    mov ah, 0x0e\n    int 0x10 ; 'al' already contains the char\n\n    ; increment pointer and do next loop\n    add bx, 1\n    jmp start\n\ndone:\n    popa\n    ret\n\n\n\nprint_nl:\n    pusha\n    \n    mov ah, 0x0e\n    mov al, 0x0a ; newline char\n    int 0x10\n    mov al, 0x0d ; carriage return\n    int 0x10\n    \n    popa\n    ret\n"
  },
  {
    "path": "14-checkpoint/boot/print_hex.asm",
    "content": "; receiving the data in 'dx'\n; For the examples we'll assume that we're called with dx=0x1234\nprint_hex:\n    pusha\n\n    mov cx, 0 ; our index variable\n\n; Strategy: get the last char of 'dx', then convert to ASCII\n; Numeric ASCII values: '0' (ASCII 0x30) to '9' (0x39), so just add 0x30 to byte N.\n; For alphabetic characters A-F: 'A' (ASCII 0x41) to 'F' (0x46) we'll add 0x40\n; Then, move the ASCII byte to the correct position on the resulting string\nhex_loop:\n    cmp cx, 4 ; loop 4 times\n    je end\n    \n    ; 1. convert last char of 'dx' to ascii\n    mov ax, dx ; we will use 'ax' as our working register\n    and ax, 0x000f ; 0x1234 -> 0x0004 by masking first three to zeros\n    add al, 0x30 ; add 0x30 to N to convert it to ASCII \"N\"\n    cmp al, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F'\n    jle step2\n    add al, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7\n\nstep2:\n    ; 2. get the correct position of the string to place our ASCII char\n    ; bx <- base address + string length - index of char\n    mov bx, HEX_OUT + 5 ; base + length\n    sub bx, cx  ; our index variable\n    mov [bx], al ; copy the ASCII char on 'al' to the position pointed by 'bx'\n    ror dx, 4 ; 0x1234 -> 0x4123 -> 0x3412 -> 0x2341 -> 0x1234\n\n    ; increment index and loop\n    add cx, 1\n    jmp hex_loop\n\nend:\n    ; prepare the parameter and call the function\n    ; remember that print receives parameters in 'bx'\n    mov bx, HEX_OUT\n    call print\n\n    popa\n    ret\n\nHEX_OUT:\n    db '0x0000',0 ; reserve memory for our new string\n"
  },
  {
    "path": "14-checkpoint/boot/switch_pm.asm",
    "content": "[bits 16]\nswitch_to_pm:\n    cli ; 1. disable interrupts\n    lgdt [gdt_descriptor] ; 2. load the GDT descriptor\n    mov eax, cr0\n    or eax, 0x1 ; 3. set 32-bit mode bit in cr0\n    mov cr0, eax\n    jmp CODE_SEG:init_pm ; 4. far jump by using a different segment\n\n[bits 32]\ninit_pm: ; we are now using 32-bit instructions\n    mov ax, DATA_SEG ; 5. update the segment registers\n    mov ds, ax\n    mov ss, ax\n    mov es, ax\n    mov fs, ax\n    mov gs, ax\n\n    mov ebp, 0x90000 ; 6. update the stack right at the top of the free space\n    mov esp, ebp\n\n    call BEGIN_PM ; 7. Call a well-known label with useful code\n"
  },
  {
    "path": "14-checkpoint/kernel/kernel.c",
    "content": "/* This will force us to create a kernel entry function instead of jumping to kernel.c:0x00 */\nvoid dummy_test_entrypoint() {\n}\n\nvoid main() {\n    char* video_memory = (char*) 0xb8000;\n    *video_memory = 'X';\n}\n"
  },
  {
    "path": "15-video-ports/Makefile",
    "content": "C_SOURCES = $(wildcard kernel/*.c drivers/*.c)\nHEADERS = $(wildcard kernel/*.h drivers/*.h)\n# Nice syntax for file extension replacement\nOBJ = ${C_SOURCES:.c=.o}\n\n# Change this if your cross-compiler is somewhere else\nCC = /usr/local/i386elfgcc/bin/i386-elf-gcc\nGDB = /usr/local/i386elfgcc/bin/i386-elf-gdb\n# -g: Use debugging symbols in gcc\nCFLAGS = -g\n\n# First rule is run by default\nos-image.bin: boot/bootsect.bin kernel.bin\n\tcat $^ > os-image.bin\n\n# '--oformat binary' deletes all symbols as a collateral, so we don't need\n# to 'strip' them manually on this case\nkernel.bin: boot/kernel_entry.o ${OBJ}\n\ti386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary\n\n# Used for debugging purposes\nkernel.elf: boot/kernel_entry.o ${OBJ}\n\ti386-elf-ld -o $@ -Ttext 0x1000 $^ \n\nrun: os-image.bin\n\tqemu-system-i386 -fda os-image.bin\n\n# Open the connection to qemu and load our kernel-object file with symbols\ndebug: os-image.bin kernel.elf\n\tqemu-system-i386 -s -fda os-image.bin &\n\t${GDB} -ex \"target remote localhost:1234\" -ex \"symbol-file kernel.elf\"\n\n# Generic rules for wildcards\n# To make an object, always compile from its .c\n%.o: %.c ${HEADERS}\n\t${CC} ${CFLAGS} -ffreestanding -c $< -o $@\n\n%.o: %.asm\n\tnasm $< -f elf -o $@\n\n%.bin: %.asm\n\tnasm $< -f bin -o $@\n\nclean:\n\trm -rf *.bin *.dis *.o os-image.bin *.elf\n\trm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o\n"
  },
  {
    "path": "15-video-ports/README.md",
    "content": "*Concepts you may want to Google beforehand: I/O ports*\n\n**Goal: Learn how to use the VGA card data ports**\n\nWe will use C to communicate with devices via I/O registers and ports.\n\nOpen `drivers/ports.c` and examine the inline C assembler syntax. It has\nsome differences, like the order of the source and destination operands,\nand the funny syntax to assign variables to operands.\n\nWhen you understand the concepts, open `kernel/kernel.c` for an example\nof use.\n\nIn this example we will examine the I/O ports which map the screen cursor\nposition. Specifically, we will query port `0x3d4` with value `14` to request\nthe cursor position high byte, and the same port with `15` for the low byte.\n\nWhen this port is queried, it saves the result in port `0x3d5`\n\nDon't miss the opportunity to use `gdb` to inspect the value of C variables,\nsince we still can't print them on the screen. To do so, set a breakpoint\nfor a specific line, `breakpoint kernel.c:21` and use the `print` command\nto examine variables. Aren't you glad now that we invested some time in\ncompiling the cross-compiled gdb? ;)\n\nFinally, we will use the queried cursor position to write a character\nat that location.\n"
  },
  {
    "path": "15-video-ports/boot/32bit_print.asm",
    "content": "[bits 32] ; using 32-bit protected mode\n\n; this is how constants are defined\nVIDEO_MEMORY equ 0xb8000\nWHITE_OB_BLACK equ 0x0f ; the color byte for each character\n\nprint_string_pm:\n    pusha\n    mov edx, VIDEO_MEMORY\n\nprint_string_pm_loop:\n    mov al, [ebx] ; [ebx] is the address of our character\n    mov ah, WHITE_OB_BLACK\n\n    cmp al, 0 ; check if end of string\n    je print_string_pm_done\n\n    mov [edx], ax ; store character + attribute in video memory\n    add ebx, 1 ; next char\n    add edx, 2 ; next video memory position\n\n    jmp print_string_pm_loop\n\nprint_string_pm_done:\n    popa\n    ret\n"
  },
  {
    "path": "15-video-ports/boot/bootsect.asm",
    "content": "; Identical to lesson 13's boot sector, but the %included files have new paths\n[org 0x7c00]\nKERNEL_OFFSET equ 0x1000 ; The same one we used when linking the kernel\n\n    mov [BOOT_DRIVE], dl ; Remember that the BIOS sets us the boot drive in 'dl' on boot\n    mov bp, 0x9000\n    mov sp, bp\n\n    mov bx, MSG_REAL_MODE \n    call print\n    call print_nl\n\n    call load_kernel ; read the kernel from disk\n    call switch_to_pm ; disable interrupts, load GDT,  etc. Finally jumps to 'BEGIN_PM'\n    jmp $ ; Never executed\n\n%include \"boot/print.asm\"\n%include \"boot/print_hex.asm\"\n%include \"boot/disk.asm\"\n%include \"boot/gdt.asm\"\n%include \"boot/32bit_print.asm\"\n%include \"boot/switch_pm.asm\"\n\n[bits 16]\nload_kernel:\n    mov bx, MSG_LOAD_KERNEL\n    call print\n    call print_nl\n\n    mov bx, KERNEL_OFFSET ; Read from disk and store in 0x1000\n    mov dh, 16 ; Our future kernel will be larger, make this big\n    mov dl, [BOOT_DRIVE]\n    call disk_load\n    ret\n\n[bits 32]\nBEGIN_PM:\n    mov ebx, MSG_PROT_MODE\n    call print_string_pm\n    call KERNEL_OFFSET ; Give control to the kernel\n    jmp $ ; Stay here when the kernel returns control to us (if ever)\n\n\nBOOT_DRIVE db 0 ; It is a good idea to store it in memory because 'dl' may get overwritten\nMSG_REAL_MODE db \"Started in 16-bit Real Mode\", 0\nMSG_PROT_MODE db \"Landed in 32-bit Protected Mode\", 0\nMSG_LOAD_KERNEL db \"Loading kernel into memory\", 0\n\n; padding\ntimes 510 - ($-$$) db 0\ndw 0xaa55\n"
  },
  {
    "path": "15-video-ports/boot/disk.asm",
    "content": "; load 'dh' sectors from drive 'dl' into ES:BX\ndisk_load:\n    pusha\n    ; reading from disk requires setting specific values in all registers\n    ; so we will overwrite our input parameters from 'dx'. Let's save it\n    ; to the stack for later use.\n    push dx\n\n    mov ah, 0x02 ; ah <- int 0x13 function. 0x02 = 'read'\n    mov al, dh   ; al <- number of sectors to read (0x01 .. 0x80)\n    mov cl, 0x02 ; cl <- sector (0x01 .. 0x11)\n                 ; 0x01 is our boot sector, 0x02 is the first 'available' sector\n    mov ch, 0x00 ; ch <- cylinder (0x0 .. 0x3FF, upper 2 bits in 'cl')\n    ; dl <- drive number. Our caller sets it as a parameter and gets it from BIOS\n    ; (0 = floppy, 1 = floppy2, 0x80 = hdd, 0x81 = hdd2)\n    mov dh, 0x00 ; dh <- head number (0x0 .. 0xF)\n\n    ; [es:bx] <- pointer to buffer where the data will be stored\n    ; caller sets it up for us, and it is actually the standard location for int 13h\n    int 0x13      ; BIOS interrupt\n    jc disk_error ; if error (stored in the carry bit)\n\n    pop dx\n    cmp al, dh    ; BIOS also sets 'al' to the # of sectors read. Compare it.\n    jne sectors_error\n    popa\n    ret\n\n\ndisk_error:\n    mov bx, DISK_ERROR\n    call print\n    call print_nl\n    mov dh, ah ; ah = error code, dl = disk drive that dropped the error\n    call print_hex ; check out the code at http://stanislavs.org/helppc/int_13-1.html\n    jmp disk_loop\n\nsectors_error:\n    mov bx, SECTORS_ERROR\n    call print\n\ndisk_loop:\n    jmp $\n\nDISK_ERROR: db \"Disk read error\", 0\nSECTORS_ERROR: db \"Incorrect number of sectors read\", 0\n"
  },
  {
    "path": "15-video-ports/boot/gdt.asm",
    "content": "gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps\n    ; the GDT starts with a null 8-byte\n    dd 0x0 ; 4 byte\n    dd 0x0 ; 4 byte\n\n; GDT for code segment. base = 0x00000000, length = 0xfffff\n; for flags, refer to os-dev.pdf document, page 36\ngdt_code: \n    dw 0xffff    ; segment length, bits 0-15\n    dw 0x0       ; segment base, bits 0-15\n    db 0x0       ; segment base, bits 16-23\n    db 10011010b ; flags (8 bits)\n    db 11001111b ; flags (4 bits) + segment length, bits 16-19\n    db 0x0       ; segment base, bits 24-31\n\n; GDT for data segment. base and length identical to code segment\n; some flags changed, again, refer to os-dev.pdf\ngdt_data:\n    dw 0xffff\n    dw 0x0\n    db 0x0\n    db 10010010b\n    db 11001111b\n    db 0x0\n\ngdt_end:\n\n; GDT descriptor\ngdt_descriptor:\n    dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size\n    dd gdt_start ; address (32 bit)\n\n; define some constants for later use\nCODE_SEG equ gdt_code - gdt_start\nDATA_SEG equ gdt_data - gdt_start\n"
  },
  {
    "path": "15-video-ports/boot/kernel_entry.asm",
    "content": "[bits 32]\n[extern main] ; Define calling point. Must have same name as kernel.c 'main' function\ncall main ; Calls the C function. The linker will know where it is placed in memory\njmp $\n"
  },
  {
    "path": "15-video-ports/boot/print.asm",
    "content": "print:\n    pusha\n\n; keep this in mind:\n; while (string[i] != 0) { print string[i]; i++ }\n\n; the comparison for string end (null byte)\nstart:\n    mov al, [bx] ; 'bx' is the base address for the string\n    cmp al, 0 \n    je done\n\n    ; the part where we print with the BIOS help\n    mov ah, 0x0e\n    int 0x10 ; 'al' already contains the char\n\n    ; increment pointer and do next loop\n    add bx, 1\n    jmp start\n\ndone:\n    popa\n    ret\n\n\n\nprint_nl:\n    pusha\n    \n    mov ah, 0x0e\n    mov al, 0x0a ; newline char\n    int 0x10\n    mov al, 0x0d ; carriage return\n    int 0x10\n    \n    popa\n    ret\n"
  },
  {
    "path": "15-video-ports/boot/print_hex.asm",
    "content": "; receiving the data in 'dx'\n; For the examples we'll assume that we're called with dx=0x1234\nprint_hex:\n    pusha\n\n    mov cx, 0 ; our index variable\n\n; Strategy: get the last char of 'dx', then convert to ASCII\n; Numeric ASCII values: '0' (ASCII 0x30) to '9' (0x39), so just add 0x30 to byte N.\n; For alphabetic characters A-F: 'A' (ASCII 0x41) to 'F' (0x46) we'll add 0x40\n; Then, move the ASCII byte to the correct position on the resulting string\nhex_loop:\n    cmp cx, 4 ; loop 4 times\n    je end\n    \n    ; 1. convert last char of 'dx' to ascii\n    mov ax, dx ; we will use 'ax' as our working register\n    and ax, 0x000f ; 0x1234 -> 0x0004 by masking first three to zeros\n    add al, 0x30 ; add 0x30 to N to convert it to ASCII \"N\"\n    cmp al, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F'\n    jle step2\n    add al, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7\n\nstep2:\n    ; 2. get the correct position of the string to place our ASCII char\n    ; bx <- base address + string length - index of char\n    mov bx, HEX_OUT + 5 ; base + length\n    sub bx, cx  ; our index variable\n    mov [bx], al ; copy the ASCII char on 'al' to the position pointed by 'bx'\n    ror dx, 4 ; 0x1234 -> 0x4123 -> 0x3412 -> 0x2341 -> 0x1234\n\n    ; increment index and loop\n    add cx, 1\n    jmp hex_loop\n\nend:\n    ; prepare the parameter and call the function\n    ; remember that print receives parameters in 'bx'\n    mov bx, HEX_OUT\n    call print\n\n    popa\n    ret\n\nHEX_OUT:\n    db '0x0000',0 ; reserve memory for our new string\n"
  },
  {
    "path": "15-video-ports/boot/switch_pm.asm",
    "content": "[bits 16]\nswitch_to_pm:\n    cli ; 1. disable interrupts\n    lgdt [gdt_descriptor] ; 2. load the GDT descriptor\n    mov eax, cr0\n    or eax, 0x1 ; 3. set 32-bit mode bit in cr0\n    mov cr0, eax\n    jmp CODE_SEG:init_pm ; 4. far jump by using a different segment\n\n[bits 32]\ninit_pm: ; we are now using 32-bit instructions\n    mov ax, DATA_SEG ; 5. update the segment registers\n    mov ds, ax\n    mov ss, ax\n    mov es, ax\n    mov fs, ax\n    mov gs, ax\n\n    mov ebp, 0x90000 ; 6. update the stack right at the top of the free space\n    mov esp, ebp\n\n    call BEGIN_PM ; 7. Call a well-known label with useful code\n"
  },
  {
    "path": "15-video-ports/drivers/ports.c",
    "content": "/**\n * Read a byte from the specified port\n */\nunsigned char port_byte_in (unsigned short port) {\n    unsigned char result;\n    /* Inline assembler syntax\n     * !! Notice how the source and destination registers are switched from NASM !!\n     *\n     * '\"=a\" (result)'; set '=' the C variable '(result)' to the value of register e'a'x\n     * '\"d\" (port)': map the C variable '(port)' into e'd'x register\n     *\n     * Inputs and outputs are separated by colons\n     */\n    __asm__(\"in %%dx, %%al\" : \"=a\" (result) : \"d\" (port));\n    return result;\n}\n\nvoid port_byte_out (unsigned short port, unsigned char data) {\n    /* Notice how here both registers are mapped to C variables and\n     * nothing is returned, thus, no equals '=' in the asm syntax \n     * However we see a comma since there are two variables in the input area\n     * and none in the 'return' area\n     */\n    __asm__(\"out %%al, %%dx\" : : \"a\" (data), \"d\" (port));\n}\n\nunsigned short port_word_in (unsigned short port) {\n    unsigned short result;\n    __asm__(\"in %%dx, %%ax\" : \"=a\" (result) : \"d\" (port));\n    return result;\n}\n\nvoid port_word_out (unsigned short port, unsigned short data) {\n    __asm__(\"out %%ax, %%dx\" : : \"a\" (data), \"d\" (port));\n}\n"
  },
  {
    "path": "15-video-ports/drivers/ports.h",
    "content": "unsigned char port_byte_in (unsigned short port);\nvoid port_byte_out (unsigned short port, unsigned char data);\nunsigned short port_word_in (unsigned short port);\nvoid port_word_out (unsigned short port, unsigned short data);\n"
  },
  {
    "path": "15-video-ports/kernel/kernel.c",
    "content": "#include \"../drivers/ports.h\"\n\nvoid main() {\n    /* Screen cursor position: ask VGA control register (0x3d4) for bytes\n     * 14 = high byte of cursor and 15 = low byte of cursor. */\n    port_byte_out(0x3d4, 14); /* Requesting byte 14: high byte of cursor pos */\n    /* Data is returned in VGA data register (0x3d5) */\n    int position = port_byte_in(0x3d5);\n    position = position << 8; /* high byte */\n\n    port_byte_out(0x3d4, 15); /* requesting low byte */\n    position += port_byte_in(0x3d5);\n\n    /* VGA 'cells' consist of the character and its control data\n     * e.g. 'white on black background', 'red text on white bg', etc */\n    int offset_from_vga = position * 2;\n\n    /* Now you can examine both variables using gdb, since we still\n     * don't know how to print strings on screen. Run 'make debug' and\n     * on the gdb console:\n     * breakpoint kernel.c:21\n     * continue\n     * print position\n     * print offset_from_vga\n     */\n\n    /* Let's write on the current cursor position, we already know how\n     * to do that */\n    char *vga = 0xb8000;\n    vga[offset_from_vga] = 'X'; \n    vga[offset_from_vga+1] = 0x0f; /* White text on black background */\n}\n"
  },
  {
    "path": "16-video-driver/Makefile",
    "content": "C_SOURCES = $(wildcard kernel/*.c drivers/*.c)\nHEADERS = $(wildcard kernel/*.h drivers/*.h)\n# Nice syntax for file extension replacement\nOBJ = ${C_SOURCES:.c=.o}\n\n# Change this if your cross-compiler is somewhere else\nCC = /usr/local/i386elfgcc/bin/i386-elf-gcc\nGDB = /usr/local/i386elfgcc/bin/i386-elf-gdb\n# -g: Use debugging symbols in gcc\nCFLAGS = -g\n\n# First rule is run by default\nos-image.bin: boot/bootsect.bin kernel.bin\n\tcat $^ > os-image.bin\n\n# '--oformat binary' deletes all symbols as a collateral, so we don't need\n# to 'strip' them manually on this case\nkernel.bin: boot/kernel_entry.o ${OBJ}\n\ti386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary\n\n# Used for debugging purposes\nkernel.elf: boot/kernel_entry.o ${OBJ}\n\ti386-elf-ld -o $@ -Ttext 0x1000 $^ \n\nrun: os-image.bin\n\tqemu-system-i386 -fda os-image.bin\n\n# Open the connection to qemu and load our kernel-object file with symbols\ndebug: os-image.bin kernel.elf\n\tqemu-system-i386 -s -fda os-image.bin &\n\t${GDB} -ex \"target remote localhost:1234\" -ex \"symbol-file kernel.elf\"\n\n# Generic rules for wildcards\n# To make an object, always compile from its .c\n%.o: %.c ${HEADERS}\n\t${CC} ${CFLAGS} -ffreestanding -c $< -o $@\n\n%.o: %.asm\n\tnasm $< -f elf -o $@\n\n%.bin: %.asm\n\tnasm $< -f bin -o $@\n\nclean:\n\trm -rf *.bin *.dis *.o os-image.bin *.elf\n\trm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o\n"
  },
  {
    "path": "16-video-driver/README.md",
    "content": "*Concepts you may want to Google beforehand: VGA character cells, screen offset*\n\n**Goal: Write strings on the screen**\n\nFinally, we are going to be able to output text on the screen. This lesson contains\na bit more code than usual, so let's go step by step.\n\nOpen `drivers/screen.h` and you'll see that we have defined some constants for the VGA\ncard driver and three public functions, one to clear the screen and another couple\nto write strings, the famously named `kprint` for \"kernel print\"\n\nNow open `drivers/screen.c`. It starts with the declaration of private helper functions\nthat we will use to aid our `kprint` kernel API.\n\nThere are the two I/O port access routines that we learned in the previous lesson,\n`get` and `set_cursor_offset()`.\n\nThen there is the routine that directly manipulates the video memory, `print_char()`\n\nFinally, there are three small helper functions to transform rows and columns into offsets\nand vice versa.\n\n\nkprint_at\n---------\n\n`kprint_at` may be called with a `-1` value for `col` and `row`, which indicates that\nwe will print the string at the current cursor position.\n\nIt first sets three variables for the col/row and the offset. Then it iterates through\nthe `char*` and calls `print_char()` with the current coordinates.\n\nNote that `print_char` itself returns the offset of the next cursor position, and we reuse\nit for the next loop.\n\n`kprint` is basically a wrapper for `kprint_at`\n\n\n\nprint_char\n----------\n\nLike `kprint_at`, `print_char` allows cols/rows to be `-1`. In that case it retrieves\nthe cursor position from the hardware, using the `ports.c` routines.\n\n`print_char` also handles newlines. In that case, we will position the cursor offset\nto column 0 of the next row. \n\nRemember that the VGA cells take two bytes, one for the character itself and another one\nfor the attribute.\n\n\nkernel.c\n--------\n\nOur new kernel is finally able to print strings.\n\nIt tests correct character positioning, spanning through multiple lines, line breaks,\nand finally it tries to write outside of the screen bounds. What happens then?\n\nIn the next lesson we will learn how to scroll the screen.\n"
  },
  {
    "path": "16-video-driver/boot/32bit_print.asm",
    "content": "[bits 32] ; using 32-bit protected mode\n\n; this is how constants are defined\nVIDEO_MEMORY equ 0xb8000\nWHITE_OB_BLACK equ 0x0f ; the color byte for each character\n\nprint_string_pm:\n    pusha\n    mov edx, VIDEO_MEMORY\n\nprint_string_pm_loop:\n    mov al, [ebx] ; [ebx] is the address of our character\n    mov ah, WHITE_OB_BLACK\n\n    cmp al, 0 ; check if end of string\n    je print_string_pm_done\n\n    mov [edx], ax ; store character + attribute in video memory\n    add ebx, 1 ; next char\n    add edx, 2 ; next video memory position\n\n    jmp print_string_pm_loop\n\nprint_string_pm_done:\n    popa\n    ret\n"
  },
  {
    "path": "16-video-driver/boot/bootsect.asm",
    "content": "; Identical to lesson 13's boot sector, but the %included files have new paths\n[org 0x7c00]\nKERNEL_OFFSET equ 0x1000 ; The same one we used when linking the kernel\n\n    mov [BOOT_DRIVE], dl ; Remember that the BIOS sets us the boot drive in 'dl' on boot\n    mov bp, 0x9000\n    mov sp, bp\n\n    mov bx, MSG_REAL_MODE \n    call print\n    call print_nl\n\n    call load_kernel ; read the kernel from disk\n    call switch_to_pm ; disable interrupts, load GDT,  etc. Finally jumps to 'BEGIN_PM'\n    jmp $ ; Never executed\n\n%include \"boot/print.asm\"\n%include \"boot/print_hex.asm\"\n%include \"boot/disk.asm\"\n%include \"boot/gdt.asm\"\n%include \"boot/32bit_print.asm\"\n%include \"boot/switch_pm.asm\"\n\n[bits 16]\nload_kernel:\n    mov bx, MSG_LOAD_KERNEL\n    call print\n    call print_nl\n\n    mov bx, KERNEL_OFFSET ; Read from disk and store in 0x1000\n    mov dh, 16 ; Our future kernel will be larger, make this big\n    mov dl, [BOOT_DRIVE]\n    call disk_load\n    ret\n\n[bits 32]\nBEGIN_PM:\n    mov ebx, MSG_PROT_MODE\n    call print_string_pm\n    call KERNEL_OFFSET ; Give control to the kernel\n    jmp $ ; Stay here when the kernel returns control to us (if ever)\n\n\nBOOT_DRIVE db 0 ; It is a good idea to store it in memory because 'dl' may get overwritten\nMSG_REAL_MODE db \"Started in 16-bit Real Mode\", 0\nMSG_PROT_MODE db \"Landed in 32-bit Protected Mode\", 0\nMSG_LOAD_KERNEL db \"Loading kernel into memory\", 0\n\n; padding\ntimes 510 - ($-$$) db 0\ndw 0xaa55\n"
  },
  {
    "path": "16-video-driver/boot/disk.asm",
    "content": "; load 'dh' sectors from drive 'dl' into ES:BX\ndisk_load:\n    pusha\n    ; reading from disk requires setting specific values in all registers\n    ; so we will overwrite our input parameters from 'dx'. Let's save it\n    ; to the stack for later use.\n    push dx\n\n    mov ah, 0x02 ; ah <- int 0x13 function. 0x02 = 'read'\n    mov al, dh   ; al <- number of sectors to read (0x01 .. 0x80)\n    mov cl, 0x02 ; cl <- sector (0x01 .. 0x11)\n                 ; 0x01 is our boot sector, 0x02 is the first 'available' sector\n    mov ch, 0x00 ; ch <- cylinder (0x0 .. 0x3FF, upper 2 bits in 'cl')\n    ; dl <- drive number. Our caller sets it as a parameter and gets it from BIOS\n    ; (0 = floppy, 1 = floppy2, 0x80 = hdd, 0x81 = hdd2)\n    mov dh, 0x00 ; dh <- head number (0x0 .. 0xF)\n\n    ; [es:bx] <- pointer to buffer where the data will be stored\n    ; caller sets it up for us, and it is actually the standard location for int 13h\n    int 0x13      ; BIOS interrupt\n    jc disk_error ; if error (stored in the carry bit)\n\n    pop dx\n    cmp al, dh    ; BIOS also sets 'al' to the # of sectors read. Compare it.\n    jne sectors_error\n    popa\n    ret\n\n\ndisk_error:\n    mov bx, DISK_ERROR\n    call print\n    call print_nl\n    mov dh, ah ; ah = error code, dl = disk drive that dropped the error\n    call print_hex ; check out the code at http://stanislavs.org/helppc/int_13-1.html\n    jmp disk_loop\n\nsectors_error:\n    mov bx, SECTORS_ERROR\n    call print\n\ndisk_loop:\n    jmp $\n\nDISK_ERROR: db \"Disk read error\", 0\nSECTORS_ERROR: db \"Incorrect number of sectors read\", 0\n"
  },
  {
    "path": "16-video-driver/boot/gdt.asm",
    "content": "gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps\n    ; the GDT starts with a null 8-byte\n    dd 0x0 ; 4 byte\n    dd 0x0 ; 4 byte\n\n; GDT for code segment. base = 0x00000000, length = 0xfffff\n; for flags, refer to os-dev.pdf document, page 36\ngdt_code: \n    dw 0xffff    ; segment length, bits 0-15\n    dw 0x0       ; segment base, bits 0-15\n    db 0x0       ; segment base, bits 16-23\n    db 10011010b ; flags (8 bits)\n    db 11001111b ; flags (4 bits) + segment length, bits 16-19\n    db 0x0       ; segment base, bits 24-31\n\n; GDT for data segment. base and length identical to code segment\n; some flags changed, again, refer to os-dev.pdf\ngdt_data:\n    dw 0xffff\n    dw 0x0\n    db 0x0\n    db 10010010b\n    db 11001111b\n    db 0x0\n\ngdt_end:\n\n; GDT descriptor\ngdt_descriptor:\n    dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size\n    dd gdt_start ; address (32 bit)\n\n; define some constants for later use\nCODE_SEG equ gdt_code - gdt_start\nDATA_SEG equ gdt_data - gdt_start\n"
  },
  {
    "path": "16-video-driver/boot/kernel_entry.asm",
    "content": "[bits 32]\n[extern main] ; Define calling point. Must have same name as kernel.c 'main' function\ncall main ; Calls the C function. The linker will know where it is placed in memory\njmp $\n"
  },
  {
    "path": "16-video-driver/boot/print.asm",
    "content": "print:\n    pusha\n\n; keep this in mind:\n; while (string[i] != 0) { print string[i]; i++ }\n\n; the comparison for string end (null byte)\nstart:\n    mov al, [bx] ; 'bx' is the base address for the string\n    cmp al, 0 \n    je done\n\n    ; the part where we print with the BIOS help\n    mov ah, 0x0e\n    int 0x10 ; 'al' already contains the char\n\n    ; increment pointer and do next loop\n    add bx, 1\n    jmp start\n\ndone:\n    popa\n    ret\n\n\n\nprint_nl:\n    pusha\n    \n    mov ah, 0x0e\n    mov al, 0x0a ; newline char\n    int 0x10\n    mov al, 0x0d ; carriage return\n    int 0x10\n    \n    popa\n    ret\n"
  },
  {
    "path": "16-video-driver/boot/print_hex.asm",
    "content": "; receiving the data in 'dx'\n; For the examples we'll assume that we're called with dx=0x1234\nprint_hex:\n    pusha\n\n    mov cx, 0 ; our index variable\n\n; Strategy: get the last char of 'dx', then convert to ASCII\n; Numeric ASCII values: '0' (ASCII 0x30) to '9' (0x39), so just add 0x30 to byte N.\n; For alphabetic characters A-F: 'A' (ASCII 0x41) to 'F' (0x46) we'll add 0x40\n; Then, move the ASCII byte to the correct position on the resulting string\nhex_loop:\n    cmp cx, 4 ; loop 4 times\n    je end\n    \n    ; 1. convert last char of 'dx' to ascii\n    mov ax, dx ; we will use 'ax' as our working register\n    and ax, 0x000f ; 0x1234 -> 0x0004 by masking first three to zeros\n    add al, 0x30 ; add 0x30 to N to convert it to ASCII \"N\"\n    cmp al, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F'\n    jle step2\n    add al, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7\n\nstep2:\n    ; 2. get the correct position of the string to place our ASCII char\n    ; bx <- base address + string length - index of char\n    mov bx, HEX_OUT + 5 ; base + length\n    sub bx, cx  ; our index variable\n    mov [bx], al ; copy the ASCII char on 'al' to the position pointed by 'bx'\n    ror dx, 4 ; 0x1234 -> 0x4123 -> 0x3412 -> 0x2341 -> 0x1234\n\n    ; increment index and loop\n    add cx, 1\n    jmp hex_loop\n\nend:\n    ; prepare the parameter and call the function\n    ; remember that print receives parameters in 'bx'\n    mov bx, HEX_OUT\n    call print\n\n    popa\n    ret\n\nHEX_OUT:\n    db '0x0000',0 ; reserve memory for our new string\n"
  },
  {
    "path": "16-video-driver/boot/switch_pm.asm",
    "content": "[bits 16]\nswitch_to_pm:\n    cli ; 1. disable interrupts\n    lgdt [gdt_descriptor] ; 2. load the GDT descriptor\n    mov eax, cr0\n    or eax, 0x1 ; 3. set 32-bit mode bit in cr0\n    mov cr0, eax\n    jmp CODE_SEG:init_pm ; 4. far jump by using a different segment\n\n[bits 32]\ninit_pm: ; we are now using 32-bit instructions\n    mov ax, DATA_SEG ; 5. update the segment registers\n    mov ds, ax\n    mov ss, ax\n    mov es, ax\n    mov fs, ax\n    mov gs, ax\n\n    mov ebp, 0x90000 ; 6. update the stack right at the top of the free space\n    mov esp, ebp\n\n    call BEGIN_PM ; 7. Call a well-known label with useful code\n"
  },
  {
    "path": "16-video-driver/drivers/ports.c",
    "content": "/**\n * Read a byte from the specified port\n */\nunsigned char port_byte_in (unsigned short port) {\n    unsigned char result;\n    /* Inline assembler syntax\n     * !! Notice how the source and destination registers are switched from NASM !!\n     *\n     * '\"=a\" (result)'; set '=' the C variable '(result)' to the value of register e'a'x\n     * '\"d\" (port)': map the C variable '(port)' into e'd'x register\n     *\n     * Inputs and outputs are separated by colons\n     */\n    __asm__(\"in %%dx, %%al\" : \"=a\" (result) : \"d\" (port));\n    return result;\n}\n\nvoid port_byte_out (unsigned short port, unsigned char data) {\n    /* Notice how here both registers are mapped to C variables and\n     * nothing is returned, thus, no equals '=' in the asm syntax \n     * However we see a comma since there are two variables in the input area\n     * and none in the 'return' area\n     */\n    __asm__(\"out %%al, %%dx\" : : \"a\" (data), \"d\" (port));\n}\n\nunsigned short port_word_in (unsigned short port) {\n    unsigned short result;\n    __asm__(\"in %%dx, %%ax\" : \"=a\" (result) : \"d\" (port));\n    return result;\n}\n\nvoid port_word_out (unsigned short port, unsigned short data) {\n    __asm__(\"out %%ax, %%dx\" : : \"a\" (data), \"d\" (port));\n}\n"
  },
  {
    "path": "16-video-driver/drivers/ports.h",
    "content": "unsigned char port_byte_in (unsigned short port);\nvoid port_byte_out (unsigned short port, unsigned char data);\nunsigned short port_word_in (unsigned short port);\nvoid port_word_out (unsigned short port, unsigned short data);\n"
  },
  {
    "path": "16-video-driver/drivers/screen.c",
    "content": "#include \"screen.h\"\n#include \"ports.h\"\n\n/* Declaration of private functions */\nint get_cursor_offset();\nvoid set_cursor_offset(int offset);\nint print_char(char c, int col, int row, char attr);\nint get_offset(int col, int row);\nint get_offset_row(int offset);\nint get_offset_col(int offset);\n\n/**********************************************************\n * Public Kernel API functions                            *\n **********************************************************/\n\n/**\n * Print a message on the specified location\n * If col, row, are negative, we will use the current offset\n */\nvoid kprint_at(char *message, int col, int row) {\n    /* Set cursor if col/row are negative */\n    int offset;\n    if (col >= 0 && row >= 0)\n        offset = get_offset(col, row);\n    else {\n        offset = get_cursor_offset();\n        row = get_offset_row(offset);\n        col = get_offset_col(offset);\n    }\n\n    /* Loop through message and print it */\n    int i = 0;\n    while (message[i] != 0) {\n        offset = print_char(message[i++], col, row, WHITE_ON_BLACK);\n        /* Compute row/col for next iteration */\n        row = get_offset_row(offset);\n        col = get_offset_col(offset);\n    }\n}\n\nvoid kprint(char *message) {\n    kprint_at(message, -1, -1);\n}\n\n\n/**********************************************************\n * Private kernel functions                               *\n **********************************************************/\n\n\n/**\n * Innermost print function for our kernel, directly accesses the video memory \n *\n * If 'col' and 'row' are negative, we will print at current cursor location\n * If 'attr' is zero it will use 'white on black' as default\n * Returns the offset of the next character\n * Sets the video cursor to the returned offset\n */\nint print_char(char c, int col, int row, char attr) {\n    unsigned char *vidmem = (unsigned char*) VIDEO_ADDRESS;\n    if (!attr) attr = WHITE_ON_BLACK;\n\n    /* Error control: print a red 'E' if the coords aren't right */\n    if (col >= MAX_COLS || row >= MAX_ROWS) {\n        vidmem[2*(MAX_COLS)*(MAX_ROWS)-2] = 'E';\n        vidmem[2*(MAX_COLS)*(MAX_ROWS)-1] = RED_ON_WHITE;\n        return get_offset(col, row);\n    }\n\n    int offset;\n    if (col >= 0 && row >= 0) offset = get_offset(col, row);\n    else offset = get_cursor_offset();\n\n    if (c == '\\n') {\n        row = get_offset_row(offset);\n        offset = get_offset(0, row+1);\n    } else {\n        vidmem[offset] = c;\n        vidmem[offset+1] = attr;\n        offset += 2;\n    }\n    set_cursor_offset(offset);\n    return offset;\n}\n\nint get_cursor_offset() {\n    /* Use the VGA ports to get the current cursor position\n     * 1. Ask for high byte of the cursor offset (data 14)\n     * 2. Ask for low byte (data 15)\n     */\n    port_byte_out(REG_SCREEN_CTRL, 14);\n    int offset = port_byte_in(REG_SCREEN_DATA) << 8; /* High byte: << 8 */\n    port_byte_out(REG_SCREEN_CTRL, 15);\n    offset += port_byte_in(REG_SCREEN_DATA);\n    return offset * 2; /* Position * size of character cell */\n}\n\nvoid set_cursor_offset(int offset) {\n    /* Similar to get_cursor_offset, but instead of reading we write data */\n    offset /= 2;\n    port_byte_out(REG_SCREEN_CTRL, 14);\n    port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset >> 8));\n    port_byte_out(REG_SCREEN_CTRL, 15);\n    port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset & 0xff));\n}\n\nvoid clear_screen() {\n    int screen_size = MAX_COLS * MAX_ROWS;\n    int i;\n    char *screen = VIDEO_ADDRESS;\n\n    for (i = 0; i < screen_size; i++) {\n        screen[i*2] = ' ';\n        screen[i*2+1] = WHITE_ON_BLACK;\n    }\n    set_cursor_offset(get_offset(0, 0));\n}\n\n\nint get_offset(int col, int row) { return 2 * (row * MAX_COLS + col); }\nint get_offset_row(int offset) { return offset / (2 * MAX_COLS); }\nint get_offset_col(int offset) { return (offset - (get_offset_row(offset)*2*MAX_COLS))/2; }\n"
  },
  {
    "path": "16-video-driver/drivers/screen.h",
    "content": "#define VIDEO_ADDRESS 0xb8000\n#define MAX_ROWS 25\n#define MAX_COLS 80\n#define WHITE_ON_BLACK 0x0f\n#define RED_ON_WHITE 0xf4\n\n/* Screen i/o ports */\n#define REG_SCREEN_CTRL 0x3d4\n#define REG_SCREEN_DATA 0x3d5\n\n/* Public kernel API */\nvoid clear_screen();\nvoid kprint_at(char *message, int col, int row);\nvoid kprint(char *message);\n"
  },
  {
    "path": "16-video-driver/kernel/kernel.c",
    "content": "#include \"../drivers/screen.h\"\n\nvoid main() {\n    clear_screen();\n    kprint_at(\"X\", 1, 6);\n    kprint_at(\"This text spans multiple lines\", 75, 10);\n    kprint_at(\"There is a line\\nbreak\", 0, 20);\n    kprint(\"There is a line\\nbreak\");\n    kprint_at(\"What happens when we run out of space?\", 45, 24);\n}\n"
  },
  {
    "path": "17-video-scroll/Makefile",
    "content": "C_SOURCES = $(wildcard kernel/*.c drivers/*.c)\nHEADERS = $(wildcard kernel/*.h drivers/*.h)\n# Nice syntax for file extension replacement\nOBJ = ${C_SOURCES:.c=.o}\n\n# Change this if your cross-compiler is somewhere else\nCC = /usr/local/i386elfgcc/bin/i386-elf-gcc\nGDB = /usr/local/i386elfgcc/bin/i386-elf-gdb\n# -g: Use debugging symbols in gcc\nCFLAGS = -g\n\n# First rule is run by default\nos-image.bin: boot/bootsect.bin kernel.bin\n\tcat $^ > os-image.bin\n\n# '--oformat binary' deletes all symbols as a collateral, so we don't need\n# to 'strip' them manually on this case\nkernel.bin: boot/kernel_entry.o ${OBJ}\n\ti386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary\n\n# Used for debugging purposes\nkernel.elf: boot/kernel_entry.o ${OBJ}\n\ti386-elf-ld -o $@ -Ttext 0x1000 $^ \n\nrun: os-image.bin\n\tqemu-system-i386 -fda os-image.bin\n\n# Open the connection to qemu and load our kernel-object file with symbols\ndebug: os-image.bin kernel.elf\n\tqemu-system-i386 -s -fda os-image.bin &\n\t${GDB} -ex \"target remote localhost:1234\" -ex \"symbol-file kernel.elf\"\n\n# Generic rules for wildcards\n# To make an object, always compile from its .c\n%.o: %.c ${HEADERS}\n\t${CC} ${CFLAGS} -ffreestanding -c $< -o $@\n\n%.o: %.asm\n\tnasm $< -f elf -o $@\n\n%.bin: %.asm\n\tnasm $< -f bin -o $@\n\nclean:\n\trm -rf *.bin *.dis *.o os-image.bin *.elf\n\trm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o\n"
  },
  {
    "path": "17-video-scroll/README.md",
    "content": "*Concepts you may want to Google beforehand: scroll*\n\n**Goal: Scroll the screen when the text reaches the bottom**\n\nFor this short lesson, open `drivers/screen.c` and note that at the\nbottom of `print_char` there is a new section (line 84) which checks\nif the current offset is over the screen size and scrolls the text.\n\nThe actual scrolling is handled by a new function, `memory_copy`. It is\na simpler version of the standard `memcpy` but we named it differently\nto avoid namespace collisions, at least for now. Open `kernel/util.c` to\nsee its implementation.\n\nTo help visualize scrolling, we will also implement a function to\nconvert integers to text, `int_to_ascii`. Again, it is a quick implementation\nof the standard `itoa`. Notice that for integers which have double digits\nor more, they are printed in reverse. This is intended. On future lessons\nwe will extend our helper functions, but that is not the point for now.\n\nFinally, open `kernel/kernel.c`. Initially, each line displays its line\nnumber. You can set a breakpoint on line 14 to confirm this. Then,\nthe following `kprint`s force the kernel to scroll down.\n\nThis lesson ends the coverage for the os-dev.pdf document. From now on, we'll\nfollow [the OSDev wiki](http://wiki.osdev.org/Meaty_Skeleton) and\nother sources and examples. Thanks Prof. Blundell for that great document!\n"
  },
  {
    "path": "17-video-scroll/boot/32bit_print.asm",
    "content": "[bits 32] ; using 32-bit protected mode\n\n; this is how constants are defined\nVIDEO_MEMORY equ 0xb8000\nWHITE_OB_BLACK equ 0x0f ; the color byte for each character\n\nprint_string_pm:\n    pusha\n    mov edx, VIDEO_MEMORY\n\nprint_string_pm_loop:\n    mov al, [ebx] ; [ebx] is the address of our character\n    mov ah, WHITE_OB_BLACK\n\n    cmp al, 0 ; check if end of string\n    je print_string_pm_done\n\n    mov [edx], ax ; store character + attribute in video memory\n    add ebx, 1 ; next char\n    add edx, 2 ; next video memory position\n\n    jmp print_string_pm_loop\n\nprint_string_pm_done:\n    popa\n    ret\n"
  },
  {
    "path": "17-video-scroll/boot/bootsect.asm",
    "content": "; Identical to lesson 13's boot sector, but the %included files have new paths\n[org 0x7c00]\nKERNEL_OFFSET equ 0x1000 ; The same one we used when linking the kernel\n\n    mov [BOOT_DRIVE], dl ; Remember that the BIOS sets us the boot drive in 'dl' on boot\n    mov bp, 0x9000\n    mov sp, bp\n\n    mov bx, MSG_REAL_MODE \n    call print\n    call print_nl\n\n    call load_kernel ; read the kernel from disk\n    call switch_to_pm ; disable interrupts, load GDT,  etc. Finally jumps to 'BEGIN_PM'\n    jmp $ ; Never executed\n\n%include \"boot/print.asm\"\n%include \"boot/print_hex.asm\"\n%include \"boot/disk.asm\"\n%include \"boot/gdt.asm\"\n%include \"boot/32bit_print.asm\"\n%include \"boot/switch_pm.asm\"\n\n[bits 16]\nload_kernel:\n    mov bx, MSG_LOAD_KERNEL\n    call print\n    call print_nl\n\n    mov bx, KERNEL_OFFSET ; Read from disk and store in 0x1000\n    mov dh, 16 ; Our future kernel will be larger, make this big\n    mov dl, [BOOT_DRIVE]\n    call disk_load\n    ret\n\n[bits 32]\nBEGIN_PM:\n    mov ebx, MSG_PROT_MODE\n    call print_string_pm\n    call KERNEL_OFFSET ; Give control to the kernel\n    jmp $ ; Stay here when the kernel returns control to us (if ever)\n\n\nBOOT_DRIVE db 0 ; It is a good idea to store it in memory because 'dl' may get overwritten\nMSG_REAL_MODE db \"Started in 16-bit Real Mode\", 0\nMSG_PROT_MODE db \"Landed in 32-bit Protected Mode\", 0\nMSG_LOAD_KERNEL db \"Loading kernel into memory\", 0\n\n; padding\ntimes 510 - ($-$$) db 0\ndw 0xaa55\n"
  },
  {
    "path": "17-video-scroll/boot/disk.asm",
    "content": "; load 'dh' sectors from drive 'dl' into ES:BX\ndisk_load:\n    pusha\n    ; reading from disk requires setting specific values in all registers\n    ; so we will overwrite our input parameters from 'dx'. Let's save it\n    ; to the stack for later use.\n    push dx\n\n    mov ah, 0x02 ; ah <- int 0x13 function. 0x02 = 'read'\n    mov al, dh   ; al <- number of sectors to read (0x01 .. 0x80)\n    mov cl, 0x02 ; cl <- sector (0x01 .. 0x11)\n                 ; 0x01 is our boot sector, 0x02 is the first 'available' sector\n    mov ch, 0x00 ; ch <- cylinder (0x0 .. 0x3FF, upper 2 bits in 'cl')\n    ; dl <- drive number. Our caller sets it as a parameter and gets it from BIOS\n    ; (0 = floppy, 1 = floppy2, 0x80 = hdd, 0x81 = hdd2)\n    mov dh, 0x00 ; dh <- head number (0x0 .. 0xF)\n\n    ; [es:bx] <- pointer to buffer where the data will be stored\n    ; caller sets it up for us, and it is actually the standard location for int 13h\n    int 0x13      ; BIOS interrupt\n    jc disk_error ; if error (stored in the carry bit)\n\n    pop dx\n    cmp al, dh    ; BIOS also sets 'al' to the # of sectors read. Compare it.\n    jne sectors_error\n    popa\n    ret\n\n\ndisk_error:\n    mov bx, DISK_ERROR\n    call print\n    call print_nl\n    mov dh, ah ; ah = error code, dl = disk drive that dropped the error\n    call print_hex ; check out the code at http://stanislavs.org/helppc/int_13-1.html\n    jmp disk_loop\n\nsectors_error:\n    mov bx, SECTORS_ERROR\n    call print\n\ndisk_loop:\n    jmp $\n\nDISK_ERROR: db \"Disk read error\", 0\nSECTORS_ERROR: db \"Incorrect number of sectors read\", 0\n"
  },
  {
    "path": "17-video-scroll/boot/gdt.asm",
    "content": "gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps\n    ; the GDT starts with a null 8-byte\n    dd 0x0 ; 4 byte\n    dd 0x0 ; 4 byte\n\n; GDT for code segment. base = 0x00000000, length = 0xfffff\n; for flags, refer to os-dev.pdf document, page 36\ngdt_code: \n    dw 0xffff    ; segment length, bits 0-15\n    dw 0x0       ; segment base, bits 0-15\n    db 0x0       ; segment base, bits 16-23\n    db 10011010b ; flags (8 bits)\n    db 11001111b ; flags (4 bits) + segment length, bits 16-19\n    db 0x0       ; segment base, bits 24-31\n\n; GDT for data segment. base and length identical to code segment\n; some flags changed, again, refer to os-dev.pdf\ngdt_data:\n    dw 0xffff\n    dw 0x0\n    db 0x0\n    db 10010010b\n    db 11001111b\n    db 0x0\n\ngdt_end:\n\n; GDT descriptor\ngdt_descriptor:\n    dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size\n    dd gdt_start ; address (32 bit)\n\n; define some constants for later use\nCODE_SEG equ gdt_code - gdt_start\nDATA_SEG equ gdt_data - gdt_start\n"
  },
  {
    "path": "17-video-scroll/boot/kernel_entry.asm",
    "content": "[bits 32]\n[extern main] ; Define calling point. Must have same name as kernel.c 'main' function\ncall main ; Calls the C function. The linker will know where it is placed in memory\njmp $\n"
  },
  {
    "path": "17-video-scroll/boot/print.asm",
    "content": "print:\n    pusha\n\n; keep this in mind:\n; while (string[i] != 0) { print string[i]; i++ }\n\n; the comparison for string end (null byte)\nstart:\n    mov al, [bx] ; 'bx' is the base address for the string\n    cmp al, 0 \n    je done\n\n    ; the part where we print with the BIOS help\n    mov ah, 0x0e\n    int 0x10 ; 'al' already contains the char\n\n    ; increment pointer and do next loop\n    add bx, 1\n    jmp start\n\ndone:\n    popa\n    ret\n\n\n\nprint_nl:\n    pusha\n    \n    mov ah, 0x0e\n    mov al, 0x0a ; newline char\n    int 0x10\n    mov al, 0x0d ; carriage return\n    int 0x10\n    \n    popa\n    ret\n"
  },
  {
    "path": "17-video-scroll/boot/print_hex.asm",
    "content": "; receiving the data in 'dx'\n; For the examples we'll assume that we're called with dx=0x1234\nprint_hex:\n    pusha\n\n    mov cx, 0 ; our index variable\n\n; Strategy: get the last char of 'dx', then convert to ASCII\n; Numeric ASCII values: '0' (ASCII 0x30) to '9' (0x39), so just add 0x30 to byte N.\n; For alphabetic characters A-F: 'A' (ASCII 0x41) to 'F' (0x46) we'll add 0x40\n; Then, move the ASCII byte to the correct position on the resulting string\nhex_loop:\n    cmp cx, 4 ; loop 4 times\n    je end\n    \n    ; 1. convert last char of 'dx' to ascii\n    mov ax, dx ; we will use 'ax' as our working register\n    and ax, 0x000f ; 0x1234 -> 0x0004 by masking first three to zeros\n    add al, 0x30 ; add 0x30 to N to convert it to ASCII \"N\"\n    cmp al, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F'\n    jle step2\n    add al, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7\n\nstep2:\n    ; 2. get the correct position of the string to place our ASCII char\n    ; bx <- base address + string length - index of char\n    mov bx, HEX_OUT + 5 ; base + length\n    sub bx, cx  ; our index variable\n    mov [bx], al ; copy the ASCII char on 'al' to the position pointed by 'bx'\n    ror dx, 4 ; 0x1234 -> 0x4123 -> 0x3412 -> 0x2341 -> 0x1234\n\n    ; increment index and loop\n    add cx, 1\n    jmp hex_loop\n\nend:\n    ; prepare the parameter and call the function\n    ; remember that print receives parameters in 'bx'\n    mov bx, HEX_OUT\n    call print\n\n    popa\n    ret\n\nHEX_OUT:\n    db '0x0000',0 ; reserve memory for our new string\n"
  },
  {
    "path": "17-video-scroll/boot/switch_pm.asm",
    "content": "[bits 16]\nswitch_to_pm:\n    cli ; 1. disable interrupts\n    lgdt [gdt_descriptor] ; 2. load the GDT descriptor\n    mov eax, cr0\n    or eax, 0x1 ; 3. set 32-bit mode bit in cr0\n    mov cr0, eax\n    jmp CODE_SEG:init_pm ; 4. far jump by using a different segment\n\n[bits 32]\ninit_pm: ; we are now using 32-bit instructions\n    mov ax, DATA_SEG ; 5. update the segment registers\n    mov ds, ax\n    mov ss, ax\n    mov es, ax\n    mov fs, ax\n    mov gs, ax\n\n    mov ebp, 0x90000 ; 6. update the stack right at the top of the free space\n    mov esp, ebp\n\n    call BEGIN_PM ; 7. Call a well-known label with useful code\n"
  },
  {
    "path": "17-video-scroll/drivers/ports.c",
    "content": "/**\n * Read a byte from the specified port\n */\nunsigned char port_byte_in (unsigned short port) {\n    unsigned char result;\n    /* Inline assembler syntax\n     * !! Notice how the source and destination registers are switched from NASM !!\n     *\n     * '\"=a\" (result)'; set '=' the C variable '(result)' to the value of register e'a'x\n     * '\"d\" (port)': map the C variable '(port)' into e'd'x register\n     *\n     * Inputs and outputs are separated by colons\n     */\n    __asm__(\"in %%dx, %%al\" : \"=a\" (result) : \"d\" (port));\n    return result;\n}\n\nvoid port_byte_out (unsigned short port, unsigned char data) {\n    /* Notice how here both registers are mapped to C variables and\n     * nothing is returned, thus, no equals '=' in the asm syntax \n     * However we see a comma since there are two variables in the input area\n     * and none in the 'return' area\n     */\n    __asm__(\"out %%al, %%dx\" : : \"a\" (data), \"d\" (port));\n}\n\nunsigned short port_word_in (unsigned short port) {\n    unsigned short result;\n    __asm__(\"in %%dx, %%ax\" : \"=a\" (result) : \"d\" (port));\n    return result;\n}\n\nvoid port_word_out (unsigned short port, unsigned short data) {\n    __asm__(\"out %%ax, %%dx\" : : \"a\" (data), \"d\" (port));\n}\n"
  },
  {
    "path": "17-video-scroll/drivers/ports.h",
    "content": "unsigned char port_byte_in (unsigned short port);\nvoid port_byte_out (unsigned short port, unsigned char data);\nunsigned short port_word_in (unsigned short port);\nvoid port_word_out (unsigned short port, unsigned short data);\n"
  },
  {
    "path": "17-video-scroll/drivers/screen.c",
    "content": "#include \"screen.h\"\n#include \"ports.h\"\n#include \"../kernel/util.h\"\n\n/* Declaration of private functions */\nint get_cursor_offset();\nvoid set_cursor_offset(int offset);\nint print_char(char c, int col, int row, char attr);\nint get_offset(int col, int row);\nint get_offset_row(int offset);\nint get_offset_col(int offset);\n\n/**********************************************************\n * Public Kernel API functions                            *\n **********************************************************/\n\n/**\n * Print a message on the specified location\n * If col, row, are negative, we will use the current offset\n */\nvoid kprint_at(char *message, int col, int row) {\n    /* Set cursor if col/row are negative */\n    int offset;\n    if (col >= 0 && row >= 0)\n        offset = get_offset(col, row);\n    else {\n        offset = get_cursor_offset();\n        row = get_offset_row(offset);\n        col = get_offset_col(offset);\n    }\n\n    /* Loop through message and print it */\n    int i = 0;\n    while (message[i] != 0) {\n        offset = print_char(message[i++], col, row, WHITE_ON_BLACK);\n        /* Compute row/col for next iteration */\n        row = get_offset_row(offset);\n        col = get_offset_col(offset);\n    }\n}\n\nvoid kprint(char *message) {\n    kprint_at(message, -1, -1);\n}\n\n\n/**********************************************************\n * Private kernel functions                               *\n **********************************************************/\n\n\n/**\n * Innermost print function for our kernel, directly accesses the video memory \n *\n * If 'col' and 'row' are negative, we will print at current cursor location\n * If 'attr' is zero it will use 'white on black' as default\n * Returns the offset of the next character\n * Sets the video cursor to the returned offset\n */\nint print_char(char c, int col, int row, char attr) {\n    unsigned char *vidmem = (unsigned char*) VIDEO_ADDRESS;\n    if (!attr) attr = WHITE_ON_BLACK;\n\n    /* Error control: print a red 'E' if the coords aren't right */\n    if (col >= MAX_COLS || row >= MAX_ROWS) {\n        vidmem[2*(MAX_COLS)*(MAX_ROWS)-2] = 'E';\n        vidmem[2*(MAX_COLS)*(MAX_ROWS)-1] = RED_ON_WHITE;\n        return get_offset(col, row);\n    }\n\n    int offset;\n    if (col >= 0 && row >= 0) offset = get_offset(col, row);\n    else offset = get_cursor_offset();\n\n    if (c == '\\n') {\n        row = get_offset_row(offset);\n        offset = get_offset(0, row+1);\n    } else {\n        vidmem[offset] = c;\n        vidmem[offset+1] = attr;\n        offset += 2;\n    }\n\n    /* Check if the offset is over screen size and scroll */\n    if (offset >= MAX_ROWS * MAX_COLS * 2) {\n        int i;\n        for (i = 1; i < MAX_ROWS; i++) \n            memory_copy(get_offset(0, i) + VIDEO_ADDRESS,\n                        get_offset(0, i-1) + VIDEO_ADDRESS,\n                        MAX_COLS * 2);\n\n        /* Blank last line */\n        char *last_line = get_offset(0, MAX_ROWS-1) + VIDEO_ADDRESS;\n        for (i = 0; i < MAX_COLS * 2; i++) last_line[i] = 0;\n\n        offset -= 2 * MAX_COLS;\n    }\n\n    set_cursor_offset(offset);\n    return offset;\n}\n\nint get_cursor_offset() {\n    /* Use the VGA ports to get the current cursor position\n     * 1. Ask for high byte of the cursor offset (data 14)\n     * 2. Ask for low byte (data 15)\n     */\n    port_byte_out(REG_SCREEN_CTRL, 14);\n    int offset = port_byte_in(REG_SCREEN_DATA) << 8; /* High byte: << 8 */\n    port_byte_out(REG_SCREEN_CTRL, 15);\n    offset += port_byte_in(REG_SCREEN_DATA);\n    return offset * 2; /* Position * size of character cell */\n}\n\nvoid set_cursor_offset(int offset) {\n    /* Similar to get_cursor_offset, but instead of reading we write data */\n    offset /= 2;\n    port_byte_out(REG_SCREEN_CTRL, 14);\n    port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset >> 8));\n    port_byte_out(REG_SCREEN_CTRL, 15);\n    port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset & 0xff));\n}\n\nvoid clear_screen() {\n    int screen_size = MAX_COLS * MAX_ROWS;\n    int i;\n    char *screen = VIDEO_ADDRESS;\n\n    for (i = 0; i < screen_size; i++) {\n        screen[i*2] = ' ';\n        screen[i*2+1] = WHITE_ON_BLACK;\n    }\n    set_cursor_offset(get_offset(0, 0));\n}\n\n\nint get_offset(int col, int row) { return 2 * (row * MAX_COLS + col); }\nint get_offset_row(int offset) { return offset / (2 * MAX_COLS); }\nint get_offset_col(int offset) { return (offset - (get_offset_row(offset)*2*MAX_COLS))/2; }\n"
  },
  {
    "path": "17-video-scroll/drivers/screen.h",
    "content": "#define VIDEO_ADDRESS 0xb8000\n#define MAX_ROWS 25\n#define MAX_COLS 80\n#define WHITE_ON_BLACK 0x0f\n#define RED_ON_WHITE 0xf4\n\n/* Screen i/o ports */\n#define REG_SCREEN_CTRL 0x3d4\n#define REG_SCREEN_DATA 0x3d5\n\n/* Public kernel API */\nvoid clear_screen();\nvoid kprint_at(char *message, int col, int row);\nvoid kprint(char *message);\n"
  },
  {
    "path": "17-video-scroll/kernel/kernel.c",
    "content": "#include \"../drivers/screen.h\"\n#include \"util.h\"\n\nvoid main() {\n    clear_screen();\n\n    /* Fill up the screen */\n    int i = 0;\n    for (i = 0; i < 24; i++) {\n        char str[255];\n        int_to_ascii(i, str);\n        kprint_at(str, 0, i);\n    }\n\n    kprint_at(\"This text forces the kernel to scroll. Row 0 will disappear. \", 60, 24);\n    kprint(\"And with this text, the kernel will scroll again, and row 1 will disappear too!\");\n}\n"
  },
  {
    "path": "17-video-scroll/kernel/util.c",
    "content": "void memory_copy(char *source, char *dest, int nbytes) {\n    int i;\n    for (i = 0; i < nbytes; i++) {\n        *(dest + i) = *(source + i);\n    }\n}\n\n/**\n * K&R implementation\n */\nvoid int_to_ascii(int n, char str[]) {\n    int i, sign;\n    if ((sign = n) < 0) n = -n;\n    i = 0;\n    do {\n        str[i++] = n % 10 + '0';\n    } while ((n /= 10) > 0);\n\n    if (sign < 0) str[i++] = '-';\n    str[i] = '\\0';\n\n    /* TODO: implement \"reverse\" */\n}\n"
  },
  {
    "path": "17-video-scroll/kernel/util.h",
    "content": "void memory_copy(char *source, char *dest, int nbytes);\nvoid int_to_ascii(int n, char str[]);\n"
  },
  {
    "path": "18-interrupts/Makefile",
    "content": "C_SOURCES = $(wildcard kernel/*.c drivers/*.c cpu/*.c)\nHEADERS = $(wildcard kernel/*.h drivers/*.h cpu/*.h)\n# Nice syntax for file extension replacement\nOBJ = ${C_SOURCES:.c=.o cpu/interrupt.o} \n\n# Change this if your cross-compiler is somewhere else\nCC = /usr/local/i386elfgcc/bin/i386-elf-gcc\nGDB = /usr/local/i386elfgcc/bin/i386-elf-gdb\n# -g: Use debugging symbols in gcc\nCFLAGS = -g\n\n# First rule is run by default\nos-image.bin: boot/bootsect.bin kernel.bin\n\tcat $^ > os-image.bin\n\n# '--oformat binary' deletes all symbols as a collateral, so we don't need\n# to 'strip' them manually on this case\nkernel.bin: boot/kernel_entry.o ${OBJ}\n\ti386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary\n\n# Used for debugging purposes\nkernel.elf: boot/kernel_entry.o ${OBJ}\n\ti386-elf-ld -o $@ -Ttext 0x1000 $^ \n\nrun: os-image.bin\n\tqemu-system-i386 -fda os-image.bin\n\n# Open the connection to qemu and load our kernel-object file with symbols\ndebug: os-image.bin kernel.elf\n\tqemu-system-i386 -s -fda os-image.bin -d guest_errors,int &\n\t${GDB} -ex \"target remote localhost:1234\" -ex \"symbol-file kernel.elf\"\n\n# Generic rules for wildcards\n# To make an object, always compile from its .c\n%.o: %.c ${HEADERS}\n\t${CC} ${CFLAGS} -ffreestanding -c $< -o $@\n\n%.o: %.asm\n\tnasm $< -f elf -o $@\n\n%.bin: %.asm\n\tnasm $< -f bin -o $@\n\nclean:\n\trm -rf *.bin *.dis *.o os-image.bin *.elf\n\trm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o cpu/*.o\n"
  },
  {
    "path": "18-interrupts/README.md",
    "content": "*Concepts you may want to Google beforehand: C types and structs, include guards, type attributes: packed, extern, volatile, exceptions*\n\n**Goal: Set up the Interrupt Descriptor Table to handle CPU interrupts**\n\nThis lesson and the following ones have been heavily inspired\nby [JamesM's tutorial](https://web.archive.org/web/20160412174753/http://www.jamesmolloy.co.uk/tutorial_html/index.html)\n\nData types\n----------\n\nFirst, we will define some special data types in `cpu/types.h`,\nwhich will help us uncouple data structures for raw bytes from chars and ints.\nIt has been carefully placed on the `cpu/` folder, where we will\nput machine-dependent code from now on. Yes, the boot code\nis specifically x86 and is still on `boot/`, but let's leave\nthat alone for now.\n\nSome of the already existing files have been changed to use\nthe new `u8`, `u16` and `u32` data types.\n\nFrom now on, our C header files will also have include guards.\n\n\nInterrupts\n----------\n\nInterrupts are one of the main things that a kernel needs to \nhandle. We will implement it now, as soon as possible, to be able\nto receive keyboard input in future lessons.\n\nAnother examples of interrupts are: divisions by zero, out of bounds,\ninvalid opcodes, page faults, etc.\n\nInterrupts are handled on a vector, with entries which are\nsimilar to those of the GDT (lesson 9). However, instead of\nprogramming the IDT in assembly, we'll do it in C.\n\n`cpu/idt.h` defines how an idt entry is stored `idt_gate` (there need to be\n256 of them, even if null, or the CPU may panic) and the actual\nidt structure that the BIOS will load, `idt_register` which is \njust a memory address and a size, similar to the GDT register.\n\nFinally, we define a couple variables to access those data structures\nfrom assembler code.\n\n`cpu/idt.c` just fills in every struct with a handler. \nAs you can see, it is a matter\nof setting the struct values and calling the `lidt` assembler command.\n\n\nISRs\n----\n\nThe Interrupt Service Routines run every time the CPU detects an \ninterrupt, which is usually fatal. \n\nWe will write just enough code to handle them, print an error message,\nand halt the CPU.\n\nOn `cpu/isr.h` we define 32 of them, manually. They are declared as\n`extern` because they will be implemented in assembler, in `cpu/interrupt.asm`\n\nBefore jumping to the assembler code, check out `cpu/isr.c`. As you can see,\nwe define a function to install all isrs at once and load the IDT, a list of error\nmessages, and the high level handler, which kprints some information. You\ncan customize `isr_handler` to print/do whatever you want.\n\nNow to the low level which glues every `idt_gate` with its low-level and\nhigh-level handler. Open `cpu/interrupt.asm`. Here we define a common\nlow level ISR code, which basically saves/restores the state and calls\nthe C code, and then the actual ISR assembler functions which are referenced\non `cpu/isr.h`\n\nNote how the `registers_t` struct is a representation of all the registers\nwe pushed in `interrupt.asm`\n\nThat's basically it. Now we need to reference `cpu/interrupt.asm` from our\nMakefile, and make the kernel install the ISRs and launch one of them.\nNotice how the CPU doesn't halt even though it would be good practice\nto do it after some interrupts.\n"
  },
  {
    "path": "18-interrupts/boot/32bit_print.asm",
    "content": "[bits 32] ; using 32-bit protected mode\n\n; this is how constants are defined\nVIDEO_MEMORY equ 0xb8000\nWHITE_OB_BLACK equ 0x0f ; the color byte for each character\n\nprint_string_pm:\n    pusha\n    mov edx, VIDEO_MEMORY\n\nprint_string_pm_loop:\n    mov al, [ebx] ; [ebx] is the address of our character\n    mov ah, WHITE_OB_BLACK\n\n    cmp al, 0 ; check if end of string\n    je print_string_pm_done\n\n    mov [edx], ax ; store character + attribute in video memory\n    add ebx, 1 ; next char\n    add edx, 2 ; next video memory position\n\n    jmp print_string_pm_loop\n\nprint_string_pm_done:\n    popa\n    ret\n"
  },
  {
    "path": "18-interrupts/boot/bootsect.asm",
    "content": "; Identical to lesson 13's boot sector, but the %included files have new paths\n[org 0x7c00]\nKERNEL_OFFSET equ 0x1000 ; The same one we used when linking the kernel\n\n    mov [BOOT_DRIVE], dl ; Remember that the BIOS sets us the boot drive in 'dl' on boot\n    mov bp, 0x9000\n    mov sp, bp\n\n    mov bx, MSG_REAL_MODE \n    call print\n    call print_nl\n\n    call load_kernel ; read the kernel from disk\n    call switch_to_pm ; disable interrupts, load GDT,  etc. Finally jumps to 'BEGIN_PM'\n    jmp $ ; Never executed\n\n%include \"boot/print.asm\"\n%include \"boot/print_hex.asm\"\n%include \"boot/disk.asm\"\n%include \"boot/gdt.asm\"\n%include \"boot/32bit_print.asm\"\n%include \"boot/switch_pm.asm\"\n\n[bits 16]\nload_kernel:\n    mov bx, MSG_LOAD_KERNEL\n    call print\n    call print_nl\n\n    mov bx, KERNEL_OFFSET ; Read from disk and store in 0x1000\n    mov dh, 31 ; Our future kernel will be larger, make this big\n    mov dl, [BOOT_DRIVE]\n    call disk_load\n    ret\n\n[bits 32]\nBEGIN_PM:\n    mov ebx, MSG_PROT_MODE\n    call print_string_pm\n    call KERNEL_OFFSET ; Give control to the kernel\n    jmp $ ; Stay here when the kernel returns control to us (if ever)\n\n\nBOOT_DRIVE db 0 ; It is a good idea to store it in memory because 'dl' may get overwritten\nMSG_REAL_MODE db \"Started in 16-bit Real Mode\", 0\nMSG_PROT_MODE db \"Landed in 32-bit Protected Mode\", 0\nMSG_LOAD_KERNEL db \"Loading kernel into memory\", 0\nMSG_RETURNED_KERNEL db \"Returned from kernel. Error?\", 0\n\n; padding\ntimes 510 - ($-$$) db 0\ndw 0xaa55\n"
  },
  {
    "path": "18-interrupts/boot/disk.asm",
    "content": "; load 'dh' sectors from drive 'dl' into ES:BX\ndisk_load:\n    pusha\n    ; reading from disk requires setting specific values in all registers\n    ; so we will overwrite our input parameters from 'dx'. Let's save it\n    ; to the stack for later use.\n    push dx\n\n    mov ah, 0x02 ; ah <- int 0x13 function. 0x02 = 'read'\n    mov al, dh   ; al <- number of sectors to read (0x01 .. 0x80)\n    mov cl, 0x02 ; cl <- sector (0x01 .. 0x11)\n                 ; 0x01 is our boot sector, 0x02 is the first 'available' sector\n    mov ch, 0x00 ; ch <- cylinder (0x0 .. 0x3FF, upper 2 bits in 'cl')\n    ; dl <- drive number. Our caller sets it as a parameter and gets it from BIOS\n    ; (0 = floppy, 1 = floppy2, 0x80 = hdd, 0x81 = hdd2)\n    mov dh, 0x00 ; dh <- head number (0x0 .. 0xF)\n\n    ; [es:bx] <- pointer to buffer where the data will be stored\n    ; caller sets it up for us, and it is actually the standard location for int 13h\n    int 0x13      ; BIOS interrupt\n    jc disk_error ; if error (stored in the carry bit)\n\n    pop dx\n    cmp al, dh    ; BIOS also sets 'al' to the # of sectors read. Compare it.\n    jne sectors_error\n    popa\n    ret\n\n\ndisk_error:\n    mov bx, DISK_ERROR\n    call print\n    call print_nl\n    mov dh, ah ; ah = error code, dl = disk drive that dropped the error\n    call print_hex ; check out the code at http://stanislavs.org/helppc/int_13-1.html\n    jmp disk_loop\n\nsectors_error:\n    mov bx, SECTORS_ERROR\n    call print\n\ndisk_loop:\n    jmp $\n\nDISK_ERROR: db \"Disk read error\", 0\nSECTORS_ERROR: db \"Incorrect number of sectors read\", 0\n"
  },
  {
    "path": "18-interrupts/boot/gdt.asm",
    "content": "gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps\n    ; the GDT starts with a null 8-byte\n    dd 0x0 ; 4 byte\n    dd 0x0 ; 4 byte\n\n; GDT for code segment. base = 0x00000000, length = 0xfffff\n; for flags, refer to os-dev.pdf document, page 36\ngdt_code: \n    dw 0xffff    ; segment length, bits 0-15\n    dw 0x0       ; segment base, bits 0-15\n    db 0x0       ; segment base, bits 16-23\n    db 10011010b ; flags (8 bits)\n    db 11001111b ; flags (4 bits) + segment length, bits 16-19\n    db 0x0       ; segment base, bits 24-31\n\n; GDT for data segment. base and length identical to code segment\n; some flags changed, again, refer to os-dev.pdf\ngdt_data:\n    dw 0xffff\n    dw 0x0\n    db 0x0\n    db 10010010b\n    db 11001111b\n    db 0x0\n\ngdt_end:\n\n; GDT descriptor\ngdt_descriptor:\n    dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size\n    dd gdt_start ; address (32 bit)\n\n; define some constants for later use\nCODE_SEG equ gdt_code - gdt_start\nDATA_SEG equ gdt_data - gdt_start\n"
  },
  {
    "path": "18-interrupts/boot/kernel_entry.asm",
    "content": "[bits 32]\n[extern main] ; Define calling point. Must have same name as kernel.c 'main' function\ncall main ; Calls the C function. The linker will know where it is placed in memory\njmp $\n"
  },
  {
    "path": "18-interrupts/boot/print.asm",
    "content": "print:\n    pusha\n\n; keep this in mind:\n; while (string[i] != 0) { print string[i]; i++ }\n\n; the comparison for string end (null byte)\nstart:\n    mov al, [bx] ; 'bx' is the base address for the string\n    cmp al, 0 \n    je done\n\n    ; the part where we print with the BIOS help\n    mov ah, 0x0e\n    int 0x10 ; 'al' already contains the char\n\n    ; increment pointer and do next loop\n    add bx, 1\n    jmp start\n\ndone:\n    popa\n    ret\n\n\n\nprint_nl:\n    pusha\n    \n    mov ah, 0x0e\n    mov al, 0x0a ; newline char\n    int 0x10\n    mov al, 0x0d ; carriage return\n    int 0x10\n    \n    popa\n    ret\n"
  },
  {
    "path": "18-interrupts/boot/print_hex.asm",
    "content": "; receiving the data in 'dx'\n; For the examples we'll assume that we're called with dx=0x1234\nprint_hex:\n    pusha\n\n    mov cx, 0 ; our index variable\n\n; Strategy: get the last char of 'dx', then convert to ASCII\n; Numeric ASCII values: '0' (ASCII 0x30) to '9' (0x39), so just add 0x30 to byte N.\n; For alphabetic characters A-F: 'A' (ASCII 0x41) to 'F' (0x46) we'll add 0x40\n; Then, move the ASCII byte to the correct position on the resulting string\nhex_loop:\n    cmp cx, 4 ; loop 4 times\n    je end\n    \n    ; 1. convert last char of 'dx' to ascii\n    mov ax, dx ; we will use 'ax' as our working register\n    and ax, 0x000f ; 0x1234 -> 0x0004 by masking first three to zeros\n    add al, 0x30 ; add 0x30 to N to convert it to ASCII \"N\"\n    cmp al, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F'\n    jle step2\n    add al, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7\n\nstep2:\n    ; 2. get the correct position of the string to place our ASCII char\n    ; bx <- base address + string length - index of char\n    mov bx, HEX_OUT + 5 ; base + length\n    sub bx, cx  ; our index variable\n    mov [bx], al ; copy the ASCII char on 'al' to the position pointed by 'bx'\n    ror dx, 4 ; 0x1234 -> 0x4123 -> 0x3412 -> 0x2341 -> 0x1234\n\n    ; increment index and loop\n    add cx, 1\n    jmp hex_loop\n\nend:\n    ; prepare the parameter and call the function\n    ; remember that print receives parameters in 'bx'\n    mov bx, HEX_OUT\n    call print\n\n    popa\n    ret\n\nHEX_OUT:\n    db '0x0000',0 ; reserve memory for our new string\n"
  },
  {
    "path": "18-interrupts/boot/switch_pm.asm",
    "content": "[bits 16]\nswitch_to_pm:\n    cli ; 1. disable interrupts\n    lgdt [gdt_descriptor] ; 2. load the GDT descriptor\n    mov eax, cr0\n    or eax, 0x1 ; 3. set 32-bit mode bit in cr0\n    mov cr0, eax\n    jmp CODE_SEG:init_pm ; 4. far jump by using a different segment\n\n[bits 32]\ninit_pm: ; we are now using 32-bit instructions\n    mov ax, DATA_SEG ; 5. update the segment registers\n    mov ds, ax\n    mov ss, ax\n    mov es, ax\n    mov fs, ax\n    mov gs, ax\n\n    mov ebp, 0x90000 ; 6. update the stack right at the top of the free space\n    mov esp, ebp\n\n    call BEGIN_PM ; 7. Call a well-known label with useful code\n"
  },
  {
    "path": "18-interrupts/cpu/idt.c",
    "content": "#include \"idt.h\"\n#include \"../kernel/util.h\"\n\nvoid set_idt_gate(int n, u32 handler) {\n    idt[n].low_offset = low_16(handler);\n    idt[n].sel = KERNEL_CS;\n    idt[n].always0 = 0;\n    idt[n].flags = 0x8E; \n    idt[n].high_offset = high_16(handler);\n}\n\nvoid set_idt() {\n    idt_reg.base = (u32) &idt;\n    idt_reg.limit = IDT_ENTRIES * sizeof(idt_gate_t) - 1;\n    /* Don't make the mistake of loading &idt -- always load &idt_reg */\n    __asm__ __volatile__(\"lidtl (%0)\" : : \"r\" (&idt_reg));\n}\n"
  },
  {
    "path": "18-interrupts/cpu/idt.h",
    "content": "#ifndef IDT_H\n#define IDT_H\n\n#include \"types.h\"\n\n/* Segment selectors */\n#define KERNEL_CS 0x08\n\n/* How every interrupt gate (handler) is defined */\ntypedef struct {\n    u16 low_offset; /* Lower 16 bits of handler function address */\n    u16 sel; /* Kernel segment selector */\n    u8 always0;\n    /* First byte\n     * Bit 7: \"Interrupt is present\"\n     * Bits 6-5: Privilege level of caller (0=kernel..3=user)\n     * Bit 4: Set to 0 for interrupt gates\n     * Bits 3-0: bits 1110 = decimal 14 = \"32 bit interrupt gate\" */\n    u8 flags; \n    u16 high_offset; /* Higher 16 bits of handler function address */\n} __attribute__((packed)) idt_gate_t ;\n\n/* A pointer to the array of interrupt handlers.\n * Assembly instruction 'lidt' will read it */\ntypedef struct {\n    u16 limit;\n    u32 base;\n} __attribute__((packed)) idt_register_t;\n\n#define IDT_ENTRIES 256\nidt_gate_t idt[IDT_ENTRIES];\nidt_register_t idt_reg;\n\n\n/* Functions implemented in idt.c */\nvoid set_idt_gate(int n, u32 handler);\nvoid set_idt();\n\n#endif\n"
  },
  {
    "path": "18-interrupts/cpu/interrupt.asm",
    "content": "; Defined in isr.c\n[extern isr_handler]\n\n; Common ISR code\nisr_common_stub:\n    ; 1. Save CPU state\n\tpusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax\n\tmov ax, ds ; Lower 16-bits of eax = ds.\n\tpush eax ; save the data segment descriptor\n\tmov ax, 0x10  ; kernel data segment descriptor\n\tmov ds, ax\n\tmov es, ax\n\tmov fs, ax\n\tmov gs, ax\n\t\n    ; 2. Call C handler\n\tcall isr_handler\n\t\n    ; 3. Restore state\n\tpop eax \n\tmov ds, ax\n\tmov es, ax\n\tmov fs, ax\n\tmov gs, ax\n\tpopa\n\tadd esp, 8 ; Cleans up the pushed error code and pushed ISR number\n\tsti\n\tiret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP\n\t\n; We don't get information about which interrupt was caller\n; when the handler is run, so we will need to have a different handler\n; for every interrupt.\n; Furthermore, some interrupts push an error code onto the stack but others\n; don't, so we will push a dummy error code for those which don't, so that\n; we have a consistent stack for all of them.\n\n; First make the ISRs global\nglobal isr0\nglobal isr1\nglobal isr2\nglobal isr3\nglobal isr4\nglobal isr5\nglobal isr6\nglobal isr7\nglobal isr8\nglobal isr9\nglobal isr10\nglobal isr11\nglobal isr12\nglobal isr13\nglobal isr14\nglobal isr15\nglobal isr16\nglobal isr17\nglobal isr18\nglobal isr19\nglobal isr20\nglobal isr21\nglobal isr22\nglobal isr23\nglobal isr24\nglobal isr25\nglobal isr26\nglobal isr27\nglobal isr28\nglobal isr29\nglobal isr30\nglobal isr31\n\n; 0: Divide By Zero Exception\nisr0:\n    cli\n    push byte 0\n    push byte 0\n    jmp isr_common_stub\n\n; 1: Debug Exception\nisr1:\n    cli\n    push byte 0\n    push byte 1\n    jmp isr_common_stub\n\n; 2: Non Maskable Interrupt Exception\nisr2:\n    cli\n    push byte 0\n    push byte 2\n    jmp isr_common_stub\n\n; 3: Int 3 Exception\nisr3:\n    cli\n    push byte 0\n    push byte 3\n    jmp isr_common_stub\n\n; 4: INTO Exception\nisr4:\n    cli\n    push byte 0\n    push byte 4\n    jmp isr_common_stub\n\n; 5: Out of Bounds Exception\nisr5:\n    cli\n    push byte 0\n    push byte 5\n    jmp isr_common_stub\n\n; 6: Invalid Opcode Exception\nisr6:\n    cli\n    push byte 0\n    push byte 6\n    jmp isr_common_stub\n\n; 7: Coprocessor Not Available Exception\nisr7:\n    cli\n    push byte 0\n    push byte 7\n    jmp isr_common_stub\n\n; 8: Double Fault Exception (With Error Code!)\nisr8:\n    cli\n    push byte 8\n    jmp isr_common_stub\n\n; 9: Coprocessor Segment Overrun Exception\nisr9:\n    cli\n    push byte 0\n    push byte 9\n    jmp isr_common_stub\n\n; 10: Bad TSS Exception (With Error Code!)\nisr10:\n    cli\n    push byte 10\n    jmp isr_common_stub\n\n; 11: Segment Not Present Exception (With Error Code!)\nisr11:\n    cli\n    push byte 11\n    jmp isr_common_stub\n\n; 12: Stack Fault Exception (With Error Code!)\nisr12:\n    cli\n    push byte 12\n    jmp isr_common_stub\n\n; 13: General Protection Fault Exception (With Error Code!)\nisr13:\n    cli\n    push byte 13\n    jmp isr_common_stub\n\n; 14: Page Fault Exception (With Error Code!)\nisr14:\n    cli\n    push byte 14\n    jmp isr_common_stub\n\n; 15: Reserved Exception\nisr15:\n    cli\n    push byte 0\n    push byte 15\n    jmp isr_common_stub\n\n; 16: Floating Point Exception\nisr16:\n    cli\n    push byte 0\n    push byte 16\n    jmp isr_common_stub\n\n; 17: Alignment Check Exception\nisr17:\n    cli\n    push byte 0\n    push byte 17\n    jmp isr_common_stub\n\n; 18: Machine Check Exception\nisr18:\n    cli\n    push byte 0\n    push byte 18\n    jmp isr_common_stub\n\n; 19: Reserved\nisr19:\n    cli\n    push byte 0\n    push byte 19\n    jmp isr_common_stub\n\n; 20: Reserved\nisr20:\n    cli\n    push byte 0\n    push byte 20\n    jmp isr_common_stub\n\n; 21: Reserved\nisr21:\n    cli\n    push byte 0\n    push byte 21\n    jmp isr_common_stub\n\n; 22: Reserved\nisr22:\n    cli\n    push byte 0\n    push byte 22\n    jmp isr_common_stub\n\n; 23: Reserved\nisr23:\n    cli\n    push byte 0\n    push byte 23\n    jmp isr_common_stub\n\n; 24: Reserved\nisr24:\n    cli\n    push byte 0\n    push byte 24\n    jmp isr_common_stub\n\n; 25: Reserved\nisr25:\n    cli\n    push byte 0\n    push byte 25\n    jmp isr_common_stub\n\n; 26: Reserved\nisr26:\n    cli\n    push byte 0\n    push byte 26\n    jmp isr_common_stub\n\n; 27: Reserved\nisr27:\n    cli\n    push byte 0\n    push byte 27\n    jmp isr_common_stub\n\n; 28: Reserved\nisr28:\n    cli\n    push byte 0\n    push byte 28\n    jmp isr_common_stub\n\n; 29: Reserved\nisr29:\n    cli\n    push byte 0\n    push byte 29\n    jmp isr_common_stub\n\n; 30: Reserved\nisr30:\n    cli\n    push byte 0\n    push byte 30\n    jmp isr_common_stub\n\n; 31: Reserved\nisr31:\n    cli\n    push byte 0\n    push byte 31\n    jmp isr_common_stub\n\n\n"
  },
  {
    "path": "18-interrupts/cpu/isr.c",
    "content": "#include \"isr.h\"\n#include \"idt.h\"\n#include \"../drivers/screen.h\"\n#include \"../kernel/util.h\"\n\n/* Can't do this with a loop because we need the address\n * of the function names */\nvoid isr_install() {\n    set_idt_gate(0, (u32)isr0);\n    set_idt_gate(1, (u32)isr1);\n    set_idt_gate(2, (u32)isr2);\n    set_idt_gate(3, (u32)isr3);\n    set_idt_gate(4, (u32)isr4);\n    set_idt_gate(5, (u32)isr5);\n    set_idt_gate(6, (u32)isr6);\n    set_idt_gate(7, (u32)isr7);\n    set_idt_gate(8, (u32)isr8);\n    set_idt_gate(9, (u32)isr9);\n    set_idt_gate(10, (u32)isr10);\n    set_idt_gate(11, (u32)isr11);\n    set_idt_gate(12, (u32)isr12);\n    set_idt_gate(13, (u32)isr13);\n    set_idt_gate(14, (u32)isr14);\n    set_idt_gate(15, (u32)isr15);\n    set_idt_gate(16, (u32)isr16);\n    set_idt_gate(17, (u32)isr17);\n    set_idt_gate(18, (u32)isr18);\n    set_idt_gate(19, (u32)isr19);\n    set_idt_gate(20, (u32)isr20);\n    set_idt_gate(21, (u32)isr21);\n    set_idt_gate(22, (u32)isr22);\n    set_idt_gate(23, (u32)isr23);\n    set_idt_gate(24, (u32)isr24);\n    set_idt_gate(25, (u32)isr25);\n    set_idt_gate(26, (u32)isr26);\n    set_idt_gate(27, (u32)isr27);\n    set_idt_gate(28, (u32)isr28);\n    set_idt_gate(29, (u32)isr29);\n    set_idt_gate(30, (u32)isr30);\n    set_idt_gate(31, (u32)isr31);\n\n    set_idt(); // Load with ASM\n}\n\n/* To print the message which defines every exception */\nchar *exception_messages[] = {\n    \"Division By Zero\",\n    \"Debug\",\n    \"Non Maskable Interrupt\",\n    \"Breakpoint\",\n    \"Into Detected Overflow\",\n    \"Out of Bounds\",\n    \"Invalid Opcode\",\n    \"No Coprocessor\",\n\n    \"Double Fault\",\n    \"Coprocessor Segment Overrun\",\n    \"Bad TSS\",\n    \"Segment Not Present\",\n    \"Stack Fault\",\n    \"General Protection Fault\",\n    \"Page Fault\",\n    \"Unknown Interrupt\",\n\n    \"Coprocessor Fault\",\n    \"Alignment Check\",\n    \"Machine Check\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\"\n};\n\nvoid isr_handler(registers_t r) {\n    kprint(\"received interrupt: \");\n    char s[3];\n    int_to_ascii(r.int_no, s);\n    kprint(s);\n    kprint(\"\\n\");\n    kprint(exception_messages[r.int_no]);\n    kprint(\"\\n\");\n}\n"
  },
  {
    "path": "18-interrupts/cpu/isr.h",
    "content": "#ifndef ISR_H\n#define ISR_H\n\n#include \"types.h\"\n\n/* ISRs reserved for CPU exceptions */\nextern void isr0();\nextern void isr1();\nextern void isr2();\nextern void isr3();\nextern void isr4();\nextern void isr5();\nextern void isr6();\nextern void isr7();\nextern void isr8();\nextern void isr9();\nextern void isr10();\nextern void isr11();\nextern void isr12();\nextern void isr13();\nextern void isr14();\nextern void isr15();\nextern void isr16();\nextern void isr17();\nextern void isr18();\nextern void isr19();\nextern void isr20();\nextern void isr21();\nextern void isr22();\nextern void isr23();\nextern void isr24();\nextern void isr25();\nextern void isr26();\nextern void isr27();\nextern void isr28();\nextern void isr29();\nextern void isr30();\nextern void isr31();\n\n/* Struct which aggregates many registers */\ntypedef struct {\n   u32 ds; /* Data segment selector */\n   u32 edi, esi, ebp, esp, ebx, edx, ecx, eax; /* Pushed by pusha. */\n   u32 int_no, err_code; /* Interrupt number and error code (if applicable) */\n   u32 eip, cs, eflags, useresp, ss; /* Pushed by the processor automatically */\n} registers_t;\n\nvoid isr_install();\nvoid isr_handler(registers_t r);\n\n#endif\n"
  },
  {
    "path": "18-interrupts/cpu/types.h",
    "content": "#ifndef TYPES_H\n#define TYPES_H\n\n/* Instead of using 'chars' to allocate non-character bytes,\n * we will use these new type with no semantic meaning */\ntypedef unsigned int   u32;\ntypedef          int   s32;\ntypedef unsigned short u16;\ntypedef          short s16;\ntypedef unsigned char  u8;\ntypedef          char  s8;\n\n#define low_16(address) (u16)((address) & 0xFFFF)\n#define high_16(address) (u16)(((address) >> 16) & 0xFFFF)\n\n#endif\n"
  },
  {
    "path": "18-interrupts/drivers/ports.c",
    "content": "#include \"ports.h\"\n\n/**\n * Read a byte from the specified port\n */\nu8 port_byte_in (u16 port) {\n    u8 result;\n    /* Inline assembler syntax\n     * !! Notice how the source and destination registers are switched from NASM !!\n     *\n     * '\"=a\" (result)'; set '=' the C variable '(result)' to the value of register e'a'x\n     * '\"d\" (port)': map the C variable '(port)' into e'd'x register\n     *\n     * Inputs and outputs are separated by colons\n     */\n    __asm__(\"in %%dx, %%al\" : \"=a\" (result) : \"d\" (port));\n    return result;\n}\n\nvoid port_byte_out (u16 port, u8 data) {\n    /* Notice how here both registers are mapped to C variables and\n     * nothing is returned, thus, no equals '=' in the asm syntax \n     * However we see a comma since there are two variables in the input area\n     * and none in the 'return' area\n     */\n    __asm__ __volatile__(\"out %%al, %%dx\" : : \"a\" (data), \"d\" (port));\n}\n\nu16 port_word_in (u16 port) {\n    u16 result;\n    __asm__(\"in %%dx, %%ax\" : \"=a\" (result) : \"d\" (port));\n    return result;\n}\n\nvoid port_word_out (u16 port, u16 data) {\n    __asm__ __volatile__(\"out %%ax, %%dx\" : : \"a\" (data), \"d\" (port));\n}\n"
  },
  {
    "path": "18-interrupts/drivers/ports.h",
    "content": "#ifndef PORTS_H\n#define PORTS_H\n\n#include \"../cpu/types.h\"\n\nunsigned char port_byte_in (u16 port);\nvoid port_byte_out (u16 port, u8 data);\nunsigned short port_word_in (u16 port);\nvoid port_word_out (u16 port, u16 data);\n\n#endif\n"
  },
  {
    "path": "18-interrupts/drivers/screen.c",
    "content": "#include \"screen.h\"\n#include \"ports.h\"\n#include \"../kernel/util.h\"\n\n/* Declaration of private functions */\nint get_cursor_offset();\nvoid set_cursor_offset(int offset);\nint print_char(char c, int col, int row, char attr);\nint get_offset(int col, int row);\nint get_offset_row(int offset);\nint get_offset_col(int offset);\n\n/**********************************************************\n * Public Kernel API functions                            *\n **********************************************************/\n\n/**\n * Print a message on the specified location\n * If col, row, are negative, we will use the current offset\n */\nvoid kprint_at(char *message, int col, int row) {\n    /* Set cursor if col/row are negative */\n    int offset;\n    if (col >= 0 && row >= 0)\n        offset = get_offset(col, row);\n    else {\n        offset = get_cursor_offset();\n        row = get_offset_row(offset);\n        col = get_offset_col(offset);\n    }\n\n    /* Loop through message and print it */\n    int i = 0;\n    while (message[i] != 0) {\n        offset = print_char(message[i++], col, row, WHITE_ON_BLACK);\n        /* Compute row/col for next iteration */\n        row = get_offset_row(offset);\n        col = get_offset_col(offset);\n    }\n}\n\nvoid kprint(char *message) {\n    kprint_at(message, -1, -1);\n}\n\n\n/**********************************************************\n * Private kernel functions                               *\n **********************************************************/\n\n\n/**\n * Innermost print function for our kernel, directly accesses the video memory \n *\n * If 'col' and 'row' are negative, we will print at current cursor location\n * If 'attr' is zero it will use 'white on black' as default\n * Returns the offset of the next character\n * Sets the video cursor to the returned offset\n */\nint print_char(char c, int col, int row, char attr) {\n    unsigned char *vidmem = (unsigned char*) VIDEO_ADDRESS;\n    if (!attr) attr = WHITE_ON_BLACK;\n\n    /* Error control: print a red 'E' if the coords aren't right */\n    if (col >= MAX_COLS || row >= MAX_ROWS) {\n        vidmem[2*(MAX_COLS)*(MAX_ROWS)-2] = 'E';\n        vidmem[2*(MAX_COLS)*(MAX_ROWS)-1] = RED_ON_WHITE;\n        return get_offset(col, row);\n    }\n\n    int offset;\n    if (col >= 0 && row >= 0) offset = get_offset(col, row);\n    else offset = get_cursor_offset();\n\n    if (c == '\\n') {\n        row = get_offset_row(offset);\n        offset = get_offset(0, row+1);\n    } else {\n        vidmem[offset] = c;\n        vidmem[offset+1] = attr;\n        offset += 2;\n    }\n\n    /* Check if the offset is over screen size and scroll */\n    if (offset >= MAX_ROWS * MAX_COLS * 2) {\n        int i;\n        for (i = 1; i < MAX_ROWS; i++) \n            memory_copy(get_offset(0, i) + VIDEO_ADDRESS,\n                        get_offset(0, i-1) + VIDEO_ADDRESS,\n                        MAX_COLS * 2);\n\n        /* Blank last line */\n        char *last_line = get_offset(0, MAX_ROWS-1) + VIDEO_ADDRESS;\n        for (i = 0; i < MAX_COLS * 2; i++) last_line[i] = 0;\n\n        offset -= 2 * MAX_COLS;\n    }\n\n    set_cursor_offset(offset);\n    return offset;\n}\n\nint get_cursor_offset() {\n    /* Use the VGA ports to get the current cursor position\n     * 1. Ask for high byte of the cursor offset (data 14)\n     * 2. Ask for low byte (data 15)\n     */\n    port_byte_out(REG_SCREEN_CTRL, 14);\n    int offset = port_byte_in(REG_SCREEN_DATA) << 8; /* High byte: << 8 */\n    port_byte_out(REG_SCREEN_CTRL, 15);\n    offset += port_byte_in(REG_SCREEN_DATA);\n    return offset * 2; /* Position * size of character cell */\n}\n\nvoid set_cursor_offset(int offset) {\n    /* Similar to get_cursor_offset, but instead of reading we write data */\n    offset /= 2;\n    port_byte_out(REG_SCREEN_CTRL, 14);\n    port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset >> 8));\n    port_byte_out(REG_SCREEN_CTRL, 15);\n    port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset & 0xff));\n}\n\nvoid clear_screen() {\n    int screen_size = MAX_COLS * MAX_ROWS;\n    int i;\n    char *screen = VIDEO_ADDRESS;\n\n    for (i = 0; i < screen_size; i++) {\n        screen[i*2] = ' ';\n        screen[i*2+1] = WHITE_ON_BLACK;\n    }\n    set_cursor_offset(get_offset(0, 0));\n}\n\n\nint get_offset(int col, int row) { return 2 * (row * MAX_COLS + col); }\nint get_offset_row(int offset) { return offset / (2 * MAX_COLS); }\nint get_offset_col(int offset) { return (offset - (get_offset_row(offset)*2*MAX_COLS))/2; }\n"
  },
  {
    "path": "18-interrupts/drivers/screen.h",
    "content": "#ifndef SCREEN_H\n#define SCREEN_H\n\n#define VIDEO_ADDRESS 0xb8000\n#define MAX_ROWS 25\n#define MAX_COLS 80\n#define WHITE_ON_BLACK 0x0f\n#define RED_ON_WHITE 0xf4\n\n/* Screen i/o ports */\n#define REG_SCREEN_CTRL 0x3d4\n#define REG_SCREEN_DATA 0x3d5\n\n/* Public kernel API */\nvoid clear_screen();\nvoid kprint_at(char *message, int col, int row);\nvoid kprint(char *message);\n\n#endif\n"
  },
  {
    "path": "18-interrupts/kernel/kernel.c",
    "content": "#include \"../drivers/screen.h\"\n#include \"util.h\"\n#include \"../cpu/isr.h\"\n#include \"../cpu/idt.h\"\n\nvoid main() {\n    isr_install();\n    /* Test the interrupts */\n    __asm__ __volatile__(\"int $2\");\n    __asm__ __volatile__(\"int $3\");\n}\n"
  },
  {
    "path": "18-interrupts/kernel/util.c",
    "content": "#include \"util.h\"\n\nvoid memory_copy(char *source, char *dest, int nbytes) {\n    int i;\n    for (i = 0; i < nbytes; i++) {\n        *(dest + i) = *(source + i);\n    }\n}\n\nvoid memory_set(u8 *dest, u8 val, u32 len) {\n    u8 *temp = (u8 *)dest;\n    for ( ; len != 0; len--) *temp++ = val;\n}\n\n/**\n * K&R implementation\n */\nvoid int_to_ascii(int n, char str[]) {\n    int i, sign;\n    if ((sign = n) < 0) n = -n;\n    i = 0;\n    do {\n        str[i++] = n % 10 + '0';\n    } while ((n /= 10) > 0);\n\n    if (sign < 0) str[i++] = '-';\n    str[i] = '\\0';\n\n    /* TODO: implement \"reverse\" */\n}\n"
  },
  {
    "path": "18-interrupts/kernel/util.h",
    "content": "#ifndef UTIL_H\n#define UTIL_H\n\n#include \"../cpu/types.h\"\n\nvoid memory_copy(char *source, char *dest, int nbytes);\nvoid memory_set(u8 *dest, u8 val, u32 len);\nvoid int_to_ascii(int n, char str[]);\n\n#endif\n"
  },
  {
    "path": "19-interrupts-irqs/Makefile",
    "content": "C_SOURCES = $(wildcard kernel/*.c drivers/*.c cpu/*.c)\nHEADERS = $(wildcard kernel/*.h drivers/*.h cpu/*.h)\n# Nice syntax for file extension replacement\nOBJ = ${C_SOURCES:.c=.o cpu/interrupt.o} \n\n# Change this if your cross-compiler is somewhere else\nCC = /usr/local/i386elfgcc/bin/i386-elf-gcc\nGDB = /usr/local/i386elfgcc/bin/i386-elf-gdb\n# -g: Use debugging symbols in gcc\nCFLAGS = -g\n\n# First rule is run by default\nos-image.bin: boot/bootsect.bin kernel.bin\n\tcat $^ > os-image.bin\n\n# '--oformat binary' deletes all symbols as a collateral, so we don't need\n# to 'strip' them manually on this case\nkernel.bin: boot/kernel_entry.o ${OBJ}\n\ti386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary\n\n# Used for debugging purposes\nkernel.elf: boot/kernel_entry.o ${OBJ}\n\ti386-elf-ld -o $@ -Ttext 0x1000 $^ \n\nrun: os-image.bin\n\tqemu-system-i386 -fda os-image.bin\n\n# Open the connection to qemu and load our kernel-object file with symbols\ndebug: os-image.bin kernel.elf\n\tqemu-system-i386 -s -fda os-image.bin -d guest_errors,int &\n\t${GDB} -ex \"target remote localhost:1234\" -ex \"symbol-file kernel.elf\"\n\n# Generic rules for wildcards\n# To make an object, always compile from its .c\n%.o: %.c ${HEADERS}\n\t${CC} ${CFLAGS} -ffreestanding -c $< -o $@\n\n%.o: %.asm\n\tnasm $< -f elf -o $@\n\n%.bin: %.asm\n\tnasm $< -f bin -o $@\n\nclean:\n\trm -rf *.bin *.dis *.o os-image.bin *.elf\n\trm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o cpu/*.o\n"
  },
  {
    "path": "19-interrupts-irqs/README.md",
    "content": "*Concepts you may want to Google beforehand: IRQs, PIC, polling*\n\n**Goal: Finish the interrupts implementation and CPU timer**\n\nWhen the CPU boots, the PIC maps IRQs 0-7 to INT 0x8-0xF\nand IRQs 8-15 to INT 0x70-0x77. This conflicts with the ISRs\nwe programmed last lesson. Since we programmed ISRs 0-31, \nit is standard to remap the IRQs to ISRs 32-47.\n\nThe PICs are communicated with via I/O ports (see lesson 15).\nThe Master PIC has command 0x20 and data 0x21, while the slave has\ncommand 0xA0 and data 0xA1.\n\nThe code for remapping the PICs is weird and includes\nsome masks, so check \n[this article](http://www.osdev.org/wiki/PIC) if you're curious.\nOtherwise, just look at `cpu/isr.c`, new code after we set the IDT\ngates for the ISRs. After that, we add the IDT gates for IRQs.\n\nNow we jump to assembler, at `interrupt.asm`. The first task is to\nadd global definitions for the IRQ symbols we just used in the C code. \nLook at the end of the `global` statements.\n\nThen, add the IRQ handlers. Same `interrupt.asm`, at the bottom. Notice\nhow they jump to a new common stub: `irq_common_stub` (next step)\n\nWe then create this `irq_common_stub` which is very similar to the ISR one.\nIt is located at the top of `interrupt.asm`, and it also defines\na new `[extern irq_handler]`\n\nNow back to C code, to write the `irq_handler()` in `isr.c`. It sends some\nEOIs to the PICs and calls the appropriate handler, which is stored in an array\nnamed `interrupt_handlers` and defined at the top of the file. The new structs\nare defined in `isr.h`. We will also use a simple function to register \nthe interrupt handlers.\n\nThat was a lot of work, but now we can define our first IRQ handler!\n\nThere are no changes in `kernel.c`, so there is nothing new to run and see.\nPlease move on to the next lesson to check those shiny new IRQs.\n"
  },
  {
    "path": "19-interrupts-irqs/cpu/idt.c",
    "content": "#include \"idt.h\"\n#include \"../kernel/util.h\"\n\nvoid set_idt_gate(int n, u32 handler) {\n    idt[n].low_offset = low_16(handler);\n    idt[n].sel = KERNEL_CS;\n    idt[n].always0 = 0;\n    idt[n].flags = 0x8E; \n    idt[n].high_offset = high_16(handler);\n}\n\nvoid set_idt() {\n    idt_reg.base = (u32) &idt;\n    idt_reg.limit = IDT_ENTRIES * sizeof(idt_gate_t) - 1;\n    /* Don't make the mistake of loading &idt -- always load &idt_reg */\n    __asm__ __volatile__(\"lidtl (%0)\" : : \"r\" (&idt_reg));\n}\n"
  },
  {
    "path": "19-interrupts-irqs/cpu/idt.h",
    "content": "#ifndef IDT_H\n#define IDT_H\n\n#include \"types.h\"\n\n/* Segment selectors */\n#define KERNEL_CS 0x08\n\n/* How every interrupt gate (handler) is defined */\ntypedef struct {\n    u16 low_offset; /* Lower 16 bits of handler function address */\n    u16 sel; /* Kernel segment selector */\n    u8 always0;\n    /* First byte\n     * Bit 7: \"Interrupt is present\"\n     * Bits 6-5: Privilege level of caller (0=kernel..3=user)\n     * Bit 4: Set to 0 for interrupt gates\n     * Bits 3-0: bits 1110 = decimal 14 = \"32 bit interrupt gate\" */\n    u8 flags; \n    u16 high_offset; /* Higher 16 bits of handler function address */\n} __attribute__((packed)) idt_gate_t ;\n\n/* A pointer to the array of interrupt handlers.\n * Assembly instruction 'lidt' will read it */\ntypedef struct {\n    u16 limit;\n    u32 base;\n} __attribute__((packed)) idt_register_t;\n\n#define IDT_ENTRIES 256\nidt_gate_t idt[IDT_ENTRIES];\nidt_register_t idt_reg;\n\n\n/* Functions implemented in idt.c */\nvoid set_idt_gate(int n, u32 handler);\nvoid set_idt();\n\n#endif\n"
  },
  {
    "path": "19-interrupts-irqs/cpu/interrupt.asm",
    "content": "; Defined in isr.c\n[extern isr_handler]\n[extern irq_handler]\n\n; Common ISR code\nisr_common_stub:\n    ; 1. Save CPU state\n\tpusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax\n\tmov ax, ds ; Lower 16-bits of eax = ds.\n\tpush eax ; save the data segment descriptor\n\tmov ax, 0x10  ; kernel data segment descriptor\n\tmov ds, ax\n\tmov es, ax\n\tmov fs, ax\n\tmov gs, ax\n\t\n    ; 2. Call C handler\n\tcall isr_handler\n\t\n    ; 3. Restore state\n\tpop eax \n\tmov ds, ax\n\tmov es, ax\n\tmov fs, ax\n\tmov gs, ax\n\tpopa\n\tadd esp, 8 ; Cleans up the pushed error code and pushed ISR number\n\tsti\n\tiret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP\n\n; Common IRQ code. Identical to ISR code except for the 'call' \n; and the 'pop ebx'\nirq_common_stub:\n    pusha \n    mov ax, ds\n    push eax\n    mov ax, 0x10\n    mov ds, ax\n    mov es, ax\n    mov fs, ax\n    mov gs, ax\n    call irq_handler ; Different than the ISR code\n    pop ebx  ; Different than the ISR code\n    mov ds, bx\n    mov es, bx\n    mov fs, bx\n    mov gs, bx\n    popa\n    add esp, 8\n    sti\n    iret \n\t\n; We don't get information about which interrupt was caller\n; when the handler is run, so we will need to have a different handler\n; for every interrupt.\n; Furthermore, some interrupts push an error code onto the stack but others\n; don't, so we will push a dummy error code for those which don't, so that\n; we have a consistent stack for all of them.\n\n; First make the ISRs global\nglobal isr0\nglobal isr1\nglobal isr2\nglobal isr3\nglobal isr4\nglobal isr5\nglobal isr6\nglobal isr7\nglobal isr8\nglobal isr9\nglobal isr10\nglobal isr11\nglobal isr12\nglobal isr13\nglobal isr14\nglobal isr15\nglobal isr16\nglobal isr17\nglobal isr18\nglobal isr19\nglobal isr20\nglobal isr21\nglobal isr22\nglobal isr23\nglobal isr24\nglobal isr25\nglobal isr26\nglobal isr27\nglobal isr28\nglobal isr29\nglobal isr30\nglobal isr31\n; IRQs\nglobal irq0\nglobal irq1\nglobal irq2\nglobal irq3\nglobal irq4\nglobal irq5\nglobal irq6\nglobal irq7\nglobal irq8\nglobal irq9\nglobal irq10\nglobal irq11\nglobal irq12\nglobal irq13\nglobal irq14\nglobal irq15\n\n; 0: Divide By Zero Exception\nisr0:\n    cli\n    push byte 0\n    push byte 0\n    jmp isr_common_stub\n\n; 1: Debug Exception\nisr1:\n    cli\n    push byte 0\n    push byte 1\n    jmp isr_common_stub\n\n; 2: Non Maskable Interrupt Exception\nisr2:\n    cli\n    push byte 0\n    push byte 2\n    jmp isr_common_stub\n\n; 3: Int 3 Exception\nisr3:\n    cli\n    push byte 0\n    push byte 3\n    jmp isr_common_stub\n\n; 4: INTO Exception\nisr4:\n    cli\n    push byte 0\n    push byte 4\n    jmp isr_common_stub\n\n; 5: Out of Bounds Exception\nisr5:\n    cli\n    push byte 0\n    push byte 5\n    jmp isr_common_stub\n\n; 6: Invalid Opcode Exception\nisr6:\n    cli\n    push byte 0\n    push byte 6\n    jmp isr_common_stub\n\n; 7: Coprocessor Not Available Exception\nisr7:\n    cli\n    push byte 0\n    push byte 7\n    jmp isr_common_stub\n\n; 8: Double Fault Exception (With Error Code!)\nisr8:\n    cli\n    push byte 8\n    jmp isr_common_stub\n\n; 9: Coprocessor Segment Overrun Exception\nisr9:\n    cli\n    push byte 0\n    push byte 9\n    jmp isr_common_stub\n\n; 10: Bad TSS Exception (With Error Code!)\nisr10:\n    cli\n    push byte 10\n    jmp isr_common_stub\n\n; 11: Segment Not Present Exception (With Error Code!)\nisr11:\n    cli\n    push byte 11\n    jmp isr_common_stub\n\n; 12: Stack Fault Exception (With Error Code!)\nisr12:\n    cli\n    push byte 12\n    jmp isr_common_stub\n\n; 13: General Protection Fault Exception (With Error Code!)\nisr13:\n    cli\n    push byte 13\n    jmp isr_common_stub\n\n; 14: Page Fault Exception (With Error Code!)\nisr14:\n    cli\n    push byte 14\n    jmp isr_common_stub\n\n; 15: Reserved Exception\nisr15:\n    cli\n    push byte 0\n    push byte 15\n    jmp isr_common_stub\n\n; 16: Floating Point Exception\nisr16:\n    cli\n    push byte 0\n    push byte 16\n    jmp isr_common_stub\n\n; 17: Alignment Check Exception\nisr17:\n    cli\n    push byte 0\n    push byte 17\n    jmp isr_common_stub\n\n; 18: Machine Check Exception\nisr18:\n    cli\n    push byte 0\n    push byte 18\n    jmp isr_common_stub\n\n; 19: Reserved\nisr19:\n    cli\n    push byte 0\n    push byte 19\n    jmp isr_common_stub\n\n; 20: Reserved\nisr20:\n    cli\n    push byte 0\n    push byte 20\n    jmp isr_common_stub\n\n; 21: Reserved\nisr21:\n    cli\n    push byte 0\n    push byte 21\n    jmp isr_common_stub\n\n; 22: Reserved\nisr22:\n    cli\n    push byte 0\n    push byte 22\n    jmp isr_common_stub\n\n; 23: Reserved\nisr23:\n    cli\n    push byte 0\n    push byte 23\n    jmp isr_common_stub\n\n; 24: Reserved\nisr24:\n    cli\n    push byte 0\n    push byte 24\n    jmp isr_common_stub\n\n; 25: Reserved\nisr25:\n    cli\n    push byte 0\n    push byte 25\n    jmp isr_common_stub\n\n; 26: Reserved\nisr26:\n    cli\n    push byte 0\n    push byte 26\n    jmp isr_common_stub\n\n; 27: Reserved\nisr27:\n    cli\n    push byte 0\n    push byte 27\n    jmp isr_common_stub\n\n; 28: Reserved\nisr28:\n    cli\n    push byte 0\n    push byte 28\n    jmp isr_common_stub\n\n; 29: Reserved\nisr29:\n    cli\n    push byte 0\n    push byte 29\n    jmp isr_common_stub\n\n; 30: Reserved\nisr30:\n    cli\n    push byte 0\n    push byte 30\n    jmp isr_common_stub\n\n; 31: Reserved\nisr31:\n    cli\n    push byte 0\n    push byte 31\n    jmp isr_common_stub\n\n; IRQ handlers\nirq0:\n\tcli\n\tpush byte 0\n\tpush byte 32\n\tjmp irq_common_stub\n\nirq1:\n\tcli\n\tpush byte 1\n\tpush byte 33\n\tjmp irq_common_stub\n\nirq2:\n\tcli\n\tpush byte 2\n\tpush byte 34\n\tjmp irq_common_stub\n\nirq3:\n\tcli\n\tpush byte 3\n\tpush byte 35\n\tjmp irq_common_stub\n\nirq4:\n\tcli\n\tpush byte 4\n\tpush byte 36\n\tjmp irq_common_stub\n\nirq5:\n\tcli\n\tpush byte 5\n\tpush byte 37\n\tjmp irq_common_stub\n\nirq6:\n\tcli\n\tpush byte 6\n\tpush byte 38\n\tjmp irq_common_stub\n\nirq7:\n\tcli\n\tpush byte 7\n\tpush byte 39\n\tjmp irq_common_stub\n\nirq8:\n\tcli\n\tpush byte 8\n\tpush byte 40\n\tjmp irq_common_stub\n\nirq9:\n\tcli\n\tpush byte 9\n\tpush byte 41\n\tjmp irq_common_stub\n\nirq10:\n\tcli\n\tpush byte 10\n\tpush byte 42\n\tjmp irq_common_stub\n\nirq11:\n\tcli\n\tpush byte 11\n\tpush byte 43\n\tjmp irq_common_stub\n\nirq12:\n\tcli\n\tpush byte 12\n\tpush byte 44\n\tjmp irq_common_stub\n\nirq13:\n\tcli\n\tpush byte 13\n\tpush byte 45\n\tjmp irq_common_stub\n\nirq14:\n\tcli\n\tpush byte 14\n\tpush byte 46\n\tjmp irq_common_stub\n\nirq15:\n\tcli\n\tpush byte 15\n\tpush byte 47\n\tjmp irq_common_stub\n\n"
  },
  {
    "path": "19-interrupts-irqs/cpu/isr.c",
    "content": "#include \"isr.h\"\n#include \"idt.h\"\n#include \"../drivers/screen.h\"\n#include \"../kernel/util.h\"\n#include \"../drivers/ports.h\"\n\nisr_t interrupt_handlers[256];\n\n/* Can't do this with a loop because we need the address\n * of the function names */\nvoid isr_install() {\n    set_idt_gate(0, (u32)isr0);\n    set_idt_gate(1, (u32)isr1);\n    set_idt_gate(2, (u32)isr2);\n    set_idt_gate(3, (u32)isr3);\n    set_idt_gate(4, (u32)isr4);\n    set_idt_gate(5, (u32)isr5);\n    set_idt_gate(6, (u32)isr6);\n    set_idt_gate(7, (u32)isr7);\n    set_idt_gate(8, (u32)isr8);\n    set_idt_gate(9, (u32)isr9);\n    set_idt_gate(10, (u32)isr10);\n    set_idt_gate(11, (u32)isr11);\n    set_idt_gate(12, (u32)isr12);\n    set_idt_gate(13, (u32)isr13);\n    set_idt_gate(14, (u32)isr14);\n    set_idt_gate(15, (u32)isr15);\n    set_idt_gate(16, (u32)isr16);\n    set_idt_gate(17, (u32)isr17);\n    set_idt_gate(18, (u32)isr18);\n    set_idt_gate(19, (u32)isr19);\n    set_idt_gate(20, (u32)isr20);\n    set_idt_gate(21, (u32)isr21);\n    set_idt_gate(22, (u32)isr22);\n    set_idt_gate(23, (u32)isr23);\n    set_idt_gate(24, (u32)isr24);\n    set_idt_gate(25, (u32)isr25);\n    set_idt_gate(26, (u32)isr26);\n    set_idt_gate(27, (u32)isr27);\n    set_idt_gate(28, (u32)isr28);\n    set_idt_gate(29, (u32)isr29);\n    set_idt_gate(30, (u32)isr30);\n    set_idt_gate(31, (u32)isr31);\n\n    // Remap the PIC\n    port_byte_out(0x20, 0x11);\n    port_byte_out(0xA0, 0x11);\n    port_byte_out(0x21, 0x20);\n    port_byte_out(0xA1, 0x28);\n    port_byte_out(0x21, 0x04);\n    port_byte_out(0xA1, 0x02);\n    port_byte_out(0x21, 0x01);\n    port_byte_out(0xA1, 0x01);\n    port_byte_out(0x21, 0x0);\n    port_byte_out(0xA1, 0x0); \n\n    // Install the IRQs\n    set_idt_gate(32, (u32)irq0);\n    set_idt_gate(33, (u32)irq1);\n    set_idt_gate(34, (u32)irq2);\n    set_idt_gate(35, (u32)irq3);\n    set_idt_gate(36, (u32)irq4);\n    set_idt_gate(37, (u32)irq5);\n    set_idt_gate(38, (u32)irq6);\n    set_idt_gate(39, (u32)irq7);\n    set_idt_gate(40, (u32)irq8);\n    set_idt_gate(41, (u32)irq9);\n    set_idt_gate(42, (u32)irq10);\n    set_idt_gate(43, (u32)irq11);\n    set_idt_gate(44, (u32)irq12);\n    set_idt_gate(45, (u32)irq13);\n    set_idt_gate(46, (u32)irq14);\n    set_idt_gate(47, (u32)irq15);\n\n    set_idt(); // Load with ASM\n}\n\n/* To print the message which defines every exception */\nchar *exception_messages[] = {\n    \"Division By Zero\",\n    \"Debug\",\n    \"Non Maskable Interrupt\",\n    \"Breakpoint\",\n    \"Into Detected Overflow\",\n    \"Out of Bounds\",\n    \"Invalid Opcode\",\n    \"No Coprocessor\",\n\n    \"Double Fault\",\n    \"Coprocessor Segment Overrun\",\n    \"Bad TSS\",\n    \"Segment Not Present\",\n    \"Stack Fault\",\n    \"General Protection Fault\",\n    \"Page Fault\",\n    \"Unknown Interrupt\",\n\n    \"Coprocessor Fault\",\n    \"Alignment Check\",\n    \"Machine Check\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\"\n};\n\nvoid isr_handler(registers_t r) {\n    kprint(\"received interrupt: \");\n    char s[3];\n    int_to_ascii(r.int_no, s);\n    kprint(s);\n    kprint(\"\\n\");\n    kprint(exception_messages[r.int_no]);\n    kprint(\"\\n\");\n}\n\nvoid register_interrupt_handler(u8 n, isr_t handler) {\n    interrupt_handlers[n] = handler;\n}\n\nvoid irq_handler(registers_t r) {\n    /* After every interrupt we need to send an EOI to the PICs\n     * or they will not send another interrupt again */\n    if (r.int_no >= 40) port_byte_out(0xA0, 0x20); /* slave */\n    port_byte_out(0x20, 0x20); /* master */\n\n    /* Handle the interrupt in a more modular way */\n    if (interrupt_handlers[r.int_no] != 0) {\n        isr_t handler = interrupt_handlers[r.int_no];\n        handler(r);\n    }\n}\n"
  },
  {
    "path": "19-interrupts-irqs/cpu/isr.h",
    "content": "#ifndef ISR_H\n#define ISR_H\n\n#include \"types.h\"\n\n/* ISRs reserved for CPU exceptions */\nextern void isr0();\nextern void isr1();\nextern void isr2();\nextern void isr3();\nextern void isr4();\nextern void isr5();\nextern void isr6();\nextern void isr7();\nextern void isr8();\nextern void isr9();\nextern void isr10();\nextern void isr11();\nextern void isr12();\nextern void isr13();\nextern void isr14();\nextern void isr15();\nextern void isr16();\nextern void isr17();\nextern void isr18();\nextern void isr19();\nextern void isr20();\nextern void isr21();\nextern void isr22();\nextern void isr23();\nextern void isr24();\nextern void isr25();\nextern void isr26();\nextern void isr27();\nextern void isr28();\nextern void isr29();\nextern void isr30();\nextern void isr31();\n/* IRQ definitions */\nextern void irq0();\nextern void irq1();\nextern void irq2();\nextern void irq3();\nextern void irq4();\nextern void irq5();\nextern void irq6();\nextern void irq7();\nextern void irq8();\nextern void irq9();\nextern void irq10();\nextern void irq11();\nextern void irq12();\nextern void irq13();\nextern void irq14();\nextern void irq15();\n\n#define IRQ0 32\n#define IRQ1 33\n#define IRQ2 34\n#define IRQ3 35\n#define IRQ4 36\n#define IRQ5 37\n#define IRQ6 38\n#define IRQ7 39\n#define IRQ8 40\n#define IRQ9 41\n#define IRQ10 42\n#define IRQ11 43\n#define IRQ12 44\n#define IRQ13 45\n#define IRQ14 46\n#define IRQ15 47\n\n/* Struct which aggregates many registers */\ntypedef struct {\n   u32 ds; /* Data segment selector */\n   u32 edi, esi, ebp, esp, ebx, edx, ecx, eax; /* Pushed by pusha. */\n   u32 int_no, err_code; /* Interrupt number and error code (if applicable) */\n   u32 eip, cs, eflags, useresp, ss; /* Pushed by the processor automatically */\n} registers_t;\n\nvoid isr_install();\nvoid isr_handler(registers_t r);\n\ntypedef void (*isr_t)(registers_t);\nvoid register_interrupt_handler(u8 n, isr_t handler);\n\n#endif\n"
  },
  {
    "path": "19-interrupts-irqs/cpu/types.h",
    "content": "#ifndef TYPES_H\n#define TYPES_H\n\n/* Instead of using 'chars' to allocate non-character bytes,\n * we will use these new type with no semantic meaning */\ntypedef unsigned int   u32;\ntypedef          int   s32;\ntypedef unsigned short u16;\ntypedef          short s16;\ntypedef unsigned char  u8;\ntypedef          char  s8;\n\n#define low_16(address) (u16)((address) & 0xFFFF)\n#define high_16(address) (u16)(((address) >> 16) & 0xFFFF)\n\n#endif\n"
  },
  {
    "path": "19-interrupts-irqs/kernel/kernel.c",
    "content": "#include \"../drivers/screen.h\"\n#include \"util.h\"\n#include \"../cpu/isr.h\"\n#include \"../cpu/idt.h\"\n\nvoid main() {\n    isr_install();\n    /* Test the interrupts */\n    __asm__ __volatile__(\"int $2\");\n    __asm__ __volatile__(\"int $3\");\n}\n"
  },
  {
    "path": "19-interrupts-irqs/kernel/util.c",
    "content": "#include \"util.h\"\n\nvoid memory_copy(char *source, char *dest, int nbytes) {\n    int i;\n    for (i = 0; i < nbytes; i++) {\n        *(dest + i) = *(source + i);\n    }\n}\n\nvoid memory_set(u8 *dest, u8 val, u32 len) {\n    u8 *temp = (u8 *)dest;\n    for ( ; len != 0; len--) *temp++ = val;\n}\n\n/**\n * K&R implementation\n */\nvoid int_to_ascii(int n, char str[]) {\n    int i, sign;\n    if ((sign = n) < 0) n = -n;\n    i = 0;\n    do {\n        str[i++] = n % 10 + '0';\n    } while ((n /= 10) > 0);\n\n    if (sign < 0) str[i++] = '-';\n    str[i] = '\\0';\n\n    /* TODO: implement \"reverse\" */\n}\n"
  },
  {
    "path": "19-interrupts-irqs/kernel/util.h",
    "content": "#ifndef UTIL_H\n#define UTIL_H\n\n#include \"../cpu/types.h\"\n\nvoid memory_copy(char *source, char *dest, int nbytes);\nvoid memory_set(u8 *dest, u8 val, u32 len);\nvoid int_to_ascii(int n, char str[]);\n\n#endif\n"
  },
  {
    "path": "20-interrupts-timer/Makefile",
    "content": "C_SOURCES = $(wildcard kernel/*.c drivers/*.c cpu/*.c libc/*.c)\nHEADERS = $(wildcard kernel/*.h drivers/*.h cpu/*.h libc/*.h)\n# Nice syntax for file extension replacement\nOBJ = ${C_SOURCES:.c=.o cpu/interrupt.o} \n\n# Change this if your cross-compiler is somewhere else\nCC = /usr/local/i386elfgcc/bin/i386-elf-gcc\nGDB = /usr/local/i386elfgcc/bin/i386-elf-gdb\n# -g: Use debugging symbols in gcc\nCFLAGS = -g \n\n# First rule is run by default\nos-image.bin: boot/bootsect.bin kernel.bin\n\tcat $^ > os-image.bin\n\n# '--oformat binary' deletes all symbols as a collateral, so we don't need\n# to 'strip' them manually on this case\nkernel.bin: boot/kernel_entry.o ${OBJ}\n\ti386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary\n\n# Used for debugging purposes\nkernel.elf: boot/kernel_entry.o ${OBJ}\n\ti386-elf-ld -o $@ -Ttext 0x1000 $^ \n\nrun: os-image.bin\n\tqemu-system-i386 -fda os-image.bin\n\n# Open the connection to qemu and load our kernel-object file with symbols\ndebug: os-image.bin kernel.elf\n\tqemu-system-i386 -s -fda os-image.bin -d guest_errors,int &\n\t${GDB} -ex \"target remote localhost:1234\" -ex \"symbol-file kernel.elf\"\n\n# Generic rules for wildcards\n# To make an object, always compile from its .c\n%.o: %.c ${HEADERS}\n\t${CC} ${CFLAGS} -ffreestanding -c $< -o $@\n\n%.o: %.asm\n\tnasm $< -f elf -o $@\n\n%.bin: %.asm\n\tnasm $< -f bin -o $@\n\nclean:\n\trm -rf *.bin *.dis *.o os-image.bin *.elf\n\trm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o cpu/*.o\n"
  },
  {
    "path": "20-interrupts-timer/README.md",
    "content": "*Concepts you may want to Google beforehand: CPU timer, keyboard interrupts, scancode*\n\n**Goal: Implement our first IRQ handlers: the CPU timer and the keyboard**\n\nEverything is now ready to test our hardware interrupts.\n\nTimer\n-----\n\nThe timer is easy to configure. First we'll declare an `init_timer()` on `cpu/timer.h` and\nimplement it on `cpu/timer.c`. It is just a matter of computing the clock frequency and\nsending the bytes to the appropriate ports.\n\nWe will now fix `kernel/utils.c int_to_ascii()` to print the numbers in the correct order.\nFor that, we need to implement `reverse()` and `strlen()`.\n\nFinally, go back to the `kernel/kernel.c` and do two things. Enable interrupts again\n(very important!) and then initialize the timer interrupt.\n\nGo `make run` and you'll see the clock ticking!\n\n\nKeyboard\n--------\n\nThe keyboard is even easier, with a drawback. The PIC does not send us the ASCII code\nfor the pressed key, but the scancode for the key-down and the key-up events, so we\nwill need to translate those.\n\nCheck out `drivers/keyboard.c` where there are two functions: the callback and\nthe initialization which configures the interrupt callback. A new `keyboard.h` was\ncreated with the definitions.\n\n`keyboard.c` also has a long table to translate scancodes to ASCII keys. For the time\nbeing, we will only implement a simple subset of the US keyboard. You can read\nmore [about scancodes here](http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html)\n\nI don't know about you, but I'm thrilled! We are very close to building a simple shell.\nIn the next chapter, we will expand a little bit on keyboard input\n"
  },
  {
    "path": "20-interrupts-timer/cpu/timer.c",
    "content": "#include \"timer.h\"\n#include \"../drivers/screen.h\"\n#include \"../kernel/util.h\"\n#include \"isr.h\"\n\nu32 tick = 0;\n\nstatic void timer_callback(registers_t regs) {\n    tick++;\n    kprint(\"Tick: \");\n    \n    char tick_ascii[256];\n    int_to_ascii(tick, tick_ascii);\n    kprint(tick_ascii);\n    kprint(\"\\n\");\n}\n\nvoid init_timer(u32 freq) {\n    /* Install the function we just wrote */\n    register_interrupt_handler(IRQ0, timer_callback);\n\n    /* Get the PIT value: hardware clock at 1193180 Hz */\n    u32 divisor = 1193180 / freq;\n    u8 low  = (u8)(divisor & 0xFF);\n    u8 high = (u8)( (divisor >> 8) & 0xFF);\n    /* Send the command */\n    port_byte_out(0x43, 0x36); /* Command port */\n    port_byte_out(0x40, low);\n    port_byte_out(0x40, high);\n}\n\n"
  },
  {
    "path": "20-interrupts-timer/cpu/timer.h",
    "content": "#ifndef TIMER_H\n#define TIMER_H\n\n#include \"../kernel/util.h\"\n\nvoid init_timer(u32 freq);\n\n#endif\n"
  },
  {
    "path": "20-interrupts-timer/drivers/keyboard.c",
    "content": "#include \"keyboard.h\"\n#include \"ports.h\"\n#include \"../cpu/isr.h\"\n#include \"screen.h\"\n\nstatic void keyboard_callback(registers_t regs) {\n    /* The PIC leaves us the scancode in port 0x60 */\n    u8 scancode = port_byte_in(0x60);\n    char *sc_ascii;\n    int_to_ascii(scancode, sc_ascii);\n    kprint(\"Keyboard scancode: \");\n    kprint(sc_ascii);\n    kprint(\", \");\n    print_letter(scancode);\n    kprint(\"\\n\");\n}\n\nvoid init_keyboard() {\n   register_interrupt_handler(IRQ1, keyboard_callback); \n}\n\nvoid print_letter(u8 scancode) {\n    switch (scancode) {\n        case 0x0:\n            kprint(\"ERROR\");\n            break;\n        case 0x1:\n            kprint(\"ESC\");\n            break;\n        case 0x2:\n            kprint(\"1\");\n            break;\n        case 0x3:\n            kprint(\"2\");\n            break;\n        case 0x4:\n            kprint(\"3\");\n            break;\n        case 0x5:\n            kprint(\"4\");\n            break;\n        case 0x6:\n            kprint(\"5\");\n            break;\n        case 0x7:\n            kprint(\"6\");\n            break;\n        case 0x8:\n            kprint(\"7\");\n            break;\n        case 0x9:\n            kprint(\"8\");\n            break;\n        case 0x0A:\n            kprint(\"9\");\n            break;\n        case 0x0B:\n            kprint(\"0\");\n            break;\n        case 0x0C:\n            kprint(\"-\");\n            break;\n        case 0x0D:\n            kprint(\"+\");\n            break;\n        case 0x0E:\n            kprint(\"Backspace\");\n            break;\n        case 0x0F:\n            kprint(\"Tab\");\n            break;\n        case 0x10:\n            kprint(\"Q\");\n            break;\n        case 0x11:\n            kprint(\"W\");\n            break;\n        case 0x12:\n            kprint(\"E\");\n            break;\n        case 0x13:\n            kprint(\"R\");\n            break;\n        case 0x14:\n            kprint(\"T\");\n            break;\n        case 0x15:\n            kprint(\"Y\");\n            break;\n        case 0x16:\n            kprint(\"U\");\n            break;\n        case 0x17:\n            kprint(\"I\");\n            break;\n        case 0x18:\n            kprint(\"O\");\n            break;\n        case 0x19:\n            kprint(\"P\");\n            break;\n\t\tcase 0x1A:\n\t\t\tkprint(\"[\");\n\t\t\tbreak;\n\t\tcase 0x1B:\n\t\t\tkprint(\"]\");\n\t\t\tbreak;\n\t\tcase 0x1C:\n\t\t\tkprint(\"ENTER\");\n\t\t\tbreak;\n\t\tcase 0x1D:\n\t\t\tkprint(\"LCtrl\");\n\t\t\tbreak;\n\t\tcase 0x1E:\n\t\t\tkprint(\"A\");\n\t\t\tbreak;\n\t\tcase 0x1F:\n\t\t\tkprint(\"S\");\n\t\t\tbreak;\n        case 0x20:\n            kprint(\"D\");\n            break;\n        case 0x21:\n            kprint(\"F\");\n            break;\n        case 0x22:\n            kprint(\"G\");\n            break;\n        case 0x23:\n            kprint(\"H\");\n            break;\n        case 0x24:\n            kprint(\"J\");\n            break;\n        case 0x25:\n            kprint(\"K\");\n            break;\n        case 0x26:\n            kprint(\"L\");\n            break;\n        case 0x27:\n            kprint(\";\");\n            break;\n        case 0x28:\n            kprint(\"'\");\n            break;\n        case 0x29:\n            kprint(\"`\");\n            break;\n\t\tcase 0x2A:\n\t\t\tkprint(\"LShift\");\n\t\t\tbreak;\n\t\tcase 0x2B:\n\t\t\tkprint(\"\\\\\");\n\t\t\tbreak;\n\t\tcase 0x2C:\n\t\t\tkprint(\"Z\");\n\t\t\tbreak;\n\t\tcase 0x2D:\n\t\t\tkprint(\"X\");\n\t\t\tbreak;\n\t\tcase 0x2E:\n\t\t\tkprint(\"C\");\n\t\t\tbreak;\n\t\tcase 0x2F:\n\t\t\tkprint(\"V\");\n\t\t\tbreak;\n        case 0x30:\n            kprint(\"B\");\n            break;\n        case 0x31:\n            kprint(\"N\");\n            break;\n        case 0x32:\n            kprint(\"M\");\n            break;\n        case 0x33:\n            kprint(\",\");\n            break;\n        case 0x34:\n            kprint(\".\");\n            break;\n        case 0x35:\n            kprint(\"/\");\n            break;\n        case 0x36:\n            kprint(\"Rshift\");\n            break;\n        case 0x37:\n            kprint(\"Keypad *\");\n            break;\n        case 0x38:\n            kprint(\"LAlt\");\n            break;\n        case 0x39:\n            kprint(\"Spc\");\n            break;\n        default:\n            /* 'keuyp' event corresponds to the 'keydown' + 0x80 \n             * it may still be a scancode we haven't implemented yet, or\n             * maybe a control/escape sequence */\n            if (scancode <= 0x7f) {\n                kprint(\"Unknown key down\");\n            } else if (scancode <= 0x39 + 0x80) {\n                kprint(\"key up \");\n                print_letter(scancode - 0x80);\n            } else kprint(\"Unknown key up\");\n            break;\n    }\n}\n\n"
  },
  {
    "path": "20-interrupts-timer/drivers/keyboard.h",
    "content": "#include \"../cpu/types.h\"\n\nvoid init_keyboard();\n"
  },
  {
    "path": "20-interrupts-timer/drivers/ports.c",
    "content": "#include \"ports.h\"\n\n/**\n * Read a byte from the specified port\n */\nu8 port_byte_in (u16 port) {\n    u8 result;\n    /* Inline assembler syntax\n     * !! Notice how the source and destination registers are switched from NASM !!\n     *\n     * '\"=a\" (result)'; set '=' the C variable '(result)' to the value of register e'a'x\n     * '\"d\" (port)': map the C variable '(port)' into e'd'x register\n     *\n     * Inputs and outputs are separated by colons\n     */\n    __asm__(\"in %%dx, %%al\" : \"=a\" (result) : \"d\" (port));\n    return result;\n}\n\nvoid port_byte_out (u16 port, u8 data) {\n    /* Notice how here both registers are mapped to C variables and\n     * nothing is returned, thus, no equals '=' in the asm syntax \n     * However we see a comma since there are two variables in the input area\n     * and none in the 'return' area\n     */\n    __asm__ __volatile__(\"out %%al, %%dx\" : : \"a\" (data), \"d\" (port));\n}\n\nu16 port_word_in (u16 port) {\n    u16 result;\n    __asm__(\"in %%dx, %%ax\" : \"=a\" (result) : \"d\" (port));\n    return result;\n}\n\nvoid port_word_out (u16 port, u16 data) {\n    __asm__ __volatile__(\"out %%ax, %%dx\" : : \"a\" (data), \"d\" (port));\n}\n"
  },
  {
    "path": "20-interrupts-timer/drivers/ports.h",
    "content": "#ifndef PORTS_H\n#define PORTS_H\n\n#include \"../cpu/types.h\"\n\nunsigned char port_byte_in (u16 port);\nvoid port_byte_out (u16 port, u8 data);\nunsigned short port_word_in (u16 port);\nvoid port_word_out (u16 port, u16 data);\n\n#endif\n"
  },
  {
    "path": "20-interrupts-timer/drivers/screen.c",
    "content": "#include \"screen.h\"\n#include \"../drivers/ports.h\"\n\n/* Declaration of private functions */\nint get_cursor_offset();\nvoid set_cursor_offset(int offset);\nint print_char(char c, int col, int row, char attr);\nint get_offset(int col, int row);\nint get_offset_row(int offset);\nint get_offset_col(int offset);\n\n/**********************************************************\n * Public Kernel API functions                            *\n **********************************************************/\n\n/**\n * Print a message on the specified location\n * If col, row, are negative, we will use the current offset\n */\nvoid kprint_at(char *message, int col, int row) {\n    /* Set cursor if col/row are negative */\n    int offset;\n    if (col >= 0 && row >= 0)\n        offset = get_offset(col, row);\n    else {\n        offset = get_cursor_offset();\n        row = get_offset_row(offset);\n        col = get_offset_col(offset);\n    }\n\n    /* Loop through message and print it */\n    int i = 0;\n    while (message[i] != 0) {\n        offset = print_char(message[i++], col, row, WHITE_ON_BLACK);\n        /* Compute row/col for next iteration */\n        row = get_offset_row(offset);\n        col = get_offset_col(offset);\n    }\n}\n\nvoid kprint(char *message) {\n    kprint_at(message, -1, -1);\n}\n\n\n/**********************************************************\n * Private kernel functions                               *\n **********************************************************/\n\n\n/**\n * Innermost print function for our kernel, directly accesses the video memory \n *\n * If 'col' and 'row' are negative, we will print at current cursor location\n * If 'attr' is zero it will use 'white on black' as default\n * Returns the offset of the next character\n * Sets the video cursor to the returned offset\n */\nint print_char(char c, int col, int row, char attr) {\n    unsigned char *vidmem = (unsigned char*) VIDEO_ADDRESS;\n    if (!attr) attr = WHITE_ON_BLACK;\n\n    /* Error control: print a red 'E' if the coords aren't right */\n    if (col >= MAX_COLS || row >= MAX_ROWS) {\n        vidmem[2*(MAX_COLS)*(MAX_ROWS)-2] = 'E';\n        vidmem[2*(MAX_COLS)*(MAX_ROWS)-1] = RED_ON_WHITE;\n        return get_offset(col, row);\n    }\n\n    int offset;\n    if (col >= 0 && row >= 0) offset = get_offset(col, row);\n    else offset = get_cursor_offset();\n\n    if (c == '\\n') {\n        row = get_offset_row(offset);\n        offset = get_offset(0, row+1);\n    } else {\n        vidmem[offset] = c;\n        vidmem[offset+1] = attr;\n        offset += 2;\n    }\n\n    /* Check if the offset is over screen size and scroll */\n    if (offset >= MAX_ROWS * MAX_COLS * 2) {\n        int i;\n        for (i = 1; i < MAX_ROWS; i++) \n            memory_copy(get_offset(0, i) + VIDEO_ADDRESS,\n                        get_offset(0, i-1) + VIDEO_ADDRESS,\n                        MAX_COLS * 2);\n\n        /* Blank last line */\n        char *last_line = get_offset(0, MAX_ROWS-1) + VIDEO_ADDRESS;\n        for (i = 0; i < MAX_COLS * 2; i++) last_line[i] = 0;\n\n        offset -= 2 * MAX_COLS;\n    }\n\n    set_cursor_offset(offset);\n    return offset;\n}\n\nint get_cursor_offset() {\n    /* Use the VGA ports to get the current cursor position\n     * 1. Ask for high byte of the cursor offset (data 14)\n     * 2. Ask for low byte (data 15)\n     */\n    port_byte_out(REG_SCREEN_CTRL, 14);\n    int offset = port_byte_in(REG_SCREEN_DATA) << 8; /* High byte: << 8 */\n    port_byte_out(REG_SCREEN_CTRL, 15);\n    offset += port_byte_in(REG_SCREEN_DATA);\n    return offset * 2; /* Position * size of character cell */\n}\n\nvoid set_cursor_offset(int offset) {\n    /* Similar to get_cursor_offset, but instead of reading we write data */\n    offset /= 2;\n    port_byte_out(REG_SCREEN_CTRL, 14);\n    port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset >> 8));\n    port_byte_out(REG_SCREEN_CTRL, 15);\n    port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset & 0xff));\n}\n\nvoid clear_screen() {\n    int screen_size = MAX_COLS * MAX_ROWS;\n    int i;\n    char *screen = VIDEO_ADDRESS;\n\n    for (i = 0; i < screen_size; i++) {\n        screen[i*2] = ' ';\n        screen[i*2+1] = WHITE_ON_BLACK;\n    }\n    set_cursor_offset(get_offset(0, 0));\n}\n\n\nint get_offset(int col, int row) { return 2 * (row * MAX_COLS + col); }\nint get_offset_row(int offset) { return offset / (2 * MAX_COLS); }\nint get_offset_col(int offset) { return (offset - (get_offset_row(offset)*2*MAX_COLS))/2; }\n"
  },
  {
    "path": "20-interrupts-timer/drivers/screen.h",
    "content": "#ifndef SCREEN_H\n#define SCREEN_H\n\n#define VIDEO_ADDRESS 0xb8000\n#define MAX_ROWS 25\n#define MAX_COLS 80\n#define WHITE_ON_BLACK 0x0f\n#define RED_ON_WHITE 0xf4\n\n/* Screen i/o ports */\n#define REG_SCREEN_CTRL 0x3d4\n#define REG_SCREEN_DATA 0x3d5\n\n/* Public kernel API */\nvoid clear_screen();\nvoid kprint_at(char *message, int col, int row);\nvoid kprint(char *message);\n\n#endif\n"
  },
  {
    "path": "20-interrupts-timer/kernel/kernel.c",
    "content": "#include \"../cpu/isr.h\"\n#include \"../cpu/timer.h\"\n#include \"../drivers/keyboard.h\"\n\nvoid main() {\n    isr_install();\n\n    asm volatile(\"sti\");\n    init_timer(50);\n    /* Comment out the timer IRQ handler to read\n     * the keyboard IRQs easier */\n    init_keyboard();\n}\n"
  },
  {
    "path": "20-interrupts-timer/kernel/util.c",
    "content": "#include \"util.h\"\n\nvoid memory_copy(char *source, char *dest, int nbytes) {\n    int i;\n    for (i = 0; i < nbytes; i++) {\n        *(dest + i) = *(source + i);\n    }\n}\n\nvoid memory_set(u8 *dest, u8 val, u32 len) {\n    u8 *temp = (u8 *)dest;\n    for ( ; len != 0; len--) *temp++ = val;\n}\n\n/**\n * K&R implementation\n */\nvoid int_to_ascii(int n, char str[]) {\n    int i, sign;\n    if ((sign = n) < 0) n = -n;\n    i = 0;\n    do {\n        str[i++] = n % 10 + '0';\n    } while ((n /= 10) > 0);\n\n    if (sign < 0) str[i++] = '-';\n    str[i] = '\\0';\n\n    reverse(str);\n}\n\n/* K&R */\nvoid reverse(char s[]) {\n    int c, i, j;\n    for (i = 0, j = strlen(s)-1; i < j; i++, j--) {\n        c = s[i];\n        s[i] = s[j];\n        s[j] = c;\n    }\n}\n\n/* K&R */\nint strlen(char s[]) {\n    int i = 0;\n    while (s[i] != '\\0') ++i;\n    return i;\n}\n"
  },
  {
    "path": "20-interrupts-timer/kernel/util.h",
    "content": "#ifndef UTIL_H\n#define UTIL_H\n\n#include \"../cpu/types.h\"\n\nvoid memory_copy(char *source, char *dest, int nbytes);\nvoid memory_set(u8 *dest, u8 val, u32 len);\nvoid int_to_ascii(int n, char str[]);\nvoid reverse(char s[]);\nint strlen(char s[]);\n\n#endif\n"
  },
  {
    "path": "21-shell/Makefile",
    "content": "C_SOURCES = $(wildcard kernel/*.c drivers/*.c cpu/*.c libc/*.c)\nHEADERS = $(wildcard kernel/*.h drivers/*.h cpu/*.h libc/*.h)\n# Nice syntax for file extension replacement\nOBJ = ${C_SOURCES:.c=.o cpu/interrupt.o} \n\n# Change this if your cross-compiler is somewhere else\nCC = /usr/local/i386elfgcc/bin/i386-elf-gcc\nGDB = /usr/local/i386elfgcc/bin/i386-elf-gdb\n# -g: Use debugging symbols in gcc\nCFLAGS = -g -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs \\\n\t\t -Wall -Wextra -Werror\n\n# First rule is run by default\nos-image.bin: boot/bootsect.bin kernel.bin\n\tcat $^ > os-image.bin\n\n# '--oformat binary' deletes all symbols as a collateral, so we don't need\n# to 'strip' them manually on this case\nkernel.bin: boot/kernel_entry.o ${OBJ}\n\ti386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary\n\n# Used for debugging purposes\nkernel.elf: boot/kernel_entry.o ${OBJ}\n\ti386-elf-ld -o $@ -Ttext 0x1000 $^ \n\nrun: os-image.bin\n\tqemu-system-i386 -fda os-image.bin\n\n# Open the connection to qemu and load our kernel-object file with symbols\ndebug: os-image.bin kernel.elf\n\tqemu-system-i386 -s -fda os-image.bin -d guest_errors,int &\n\t${GDB} -ex \"target remote localhost:1234\" -ex \"symbol-file kernel.elf\"\n\n# Generic rules for wildcards\n# To make an object, always compile from its .c\n%.o: %.c ${HEADERS}\n\t${CC} ${CFLAGS} -ffreestanding -c $< -o $@\n\n%.o: %.asm\n\tnasm $< -f elf -o $@\n\n%.bin: %.asm\n\tnasm $< -f bin -o $@\n\nclean:\n\trm -rf *.bin *.dis *.o os-image.bin *.elf\n\trm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o cpu/*.o libc/*.o\n"
  },
  {
    "path": "21-shell/README.md",
    "content": "\n**Goal: Clean the code a bit and parse user input**\n\nIn this lesson we will do two things. First, we will clean up the code a bit, so it is ready \nfor further lessons. During the previous ones I tried to put things in the most predictable places,\nbut it is also a good exercise to know when the code base is growing and adapt it to current\nand further needs.\n\n\nCode cleaning\n-------------\n\nFirst of all, we will quickly start to need more utility functions\nfor handling strings and so on. In a regular OS, this is called the C library,\nor libc for short.\n\nRight now we have a `utils.c` which we will split into `mem.c` and `string.c`, with their respective headers.\n\nSecond, we will create a new function `irq_install()` so that the kernel\nonly needs to perform one call to initialize all the IRQs. That function\nis akin to `isr_install()` and placed on the same `irq.c`.\nWhile we're here, we will disable the `kprint()` on `timer_callback()`\nto avoid filling the screen with junk, now that we know that it works\nproperly.\n\nThere is not a clear distinction between `cpu/` and `drivers/`.\nKeep in mind that I'm\ncreating this tutorial while following many others, and each of them\nhas a distinct folder structure. The only change we will do for now is to\nmove `drivers/ports.*` into `cpu/` since it is clearly cpu-dependent code.\n`boot/` is also CPU-dependent code, but we will not mess with it until\nwe implement the boot sequence for a different machine.\n\nThere are more switches for the `CFLAGS` on the `Makefile`, since we will now\nstart creating higher-level functions for our C library and we don't want\nthe compiler to include any external code if we make a mistake with a declaration.\nWe also added some flags to turn warnings into errors, since an apparently minor mistake\nconverting pointers can blow up later on. This also forced us to modify some misc pointer\ndeclarations in our code.\n\nFinally, we'll add a macro to avoid warning-errors on unused parameters on `libc/function.h`\n\nKeyboard characters\n-------------------\n\nHow to access the typed characters, then?\n\n- When a key is pressed, the callback gets the ASCII code via a new\narrays which are defined at the beginning of `keyboard.c`\n- The callback then appends that character to a buffer, `key_buffer`\n- It is also printed on the screen\n- When the OS wants to read user input, it calls `libc/io.c:readline()`\n\n`keyboard.c` also parses backspace, by removing the last element\nof the key buffer, and deleting it from the screen, by calling \n`screen.c:kprint_backspace()`. For this we needed to modify a bit\n`print_char()` to not advance the offset when printing a backspace\n\n\nResponding to user input\n------------------------\n\nThe keyboard callback checks for a newline, and then calls the kernel,\ntelling it that the user has input something. Out final libc function\nis `strcmp()`, which compares two strings and returns 0 if they\nare equal. If the user inputs \"END\", we halt the CPU.\n\nThis is the most basic shell ever, but you should be proud, because\nwe implemented it from scratch. Do you realize how cool this is?\n\nIf you want to, expand `kernel.c` to parse more stuff. In the future,\nwhen we have a filesystem, we will allow the user to run some basic commands.\n"
  },
  {
    "path": "21-shell/cpu/idt.c",
    "content": "#include \"idt.h\"\n\nvoid set_idt_gate(int n, u32 handler) {\n    idt[n].low_offset = low_16(handler);\n    idt[n].sel = KERNEL_CS;\n    idt[n].always0 = 0;\n    idt[n].flags = 0x8E; \n    idt[n].high_offset = high_16(handler);\n}\n\nvoid set_idt() {\n    idt_reg.base = (u32) &idt;\n    idt_reg.limit = IDT_ENTRIES * sizeof(idt_gate_t) - 1;\n    /* Don't make the mistake of loading &idt -- always load &idt_reg */\n    __asm__ __volatile__(\"lidtl (%0)\" : : \"r\" (&idt_reg));\n}\n"
  },
  {
    "path": "21-shell/cpu/idt.h",
    "content": "#ifndef IDT_H\n#define IDT_H\n\n#include \"types.h\"\n\n/* Segment selectors */\n#define KERNEL_CS 0x08\n\n/* How every interrupt gate (handler) is defined */\ntypedef struct {\n    u16 low_offset; /* Lower 16 bits of handler function address */\n    u16 sel; /* Kernel segment selector */\n    u8 always0;\n    /* First byte\n     * Bit 7: \"Interrupt is present\"\n     * Bits 6-5: Privilege level of caller (0=kernel..3=user)\n     * Bit 4: Set to 0 for interrupt gates\n     * Bits 3-0: bits 1110 = decimal 14 = \"32 bit interrupt gate\" */\n    u8 flags; \n    u16 high_offset; /* Higher 16 bits of handler function address */\n} __attribute__((packed)) idt_gate_t ;\n\n/* A pointer to the array of interrupt handlers.\n * Assembly instruction 'lidt' will read it */\ntypedef struct {\n    u16 limit;\n    u32 base;\n} __attribute__((packed)) idt_register_t;\n\n#define IDT_ENTRIES 256\nidt_gate_t idt[IDT_ENTRIES];\nidt_register_t idt_reg;\n\n\n/* Functions implemented in idt.c */\nvoid set_idt_gate(int n, u32 handler);\nvoid set_idt();\n\n#endif\n"
  },
  {
    "path": "21-shell/cpu/interrupt.asm",
    "content": "; Defined in isr.c\n[extern isr_handler]\n[extern irq_handler]\n\n; Common ISR code\nisr_common_stub:\n    ; 1. Save CPU state\n\tpusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax\n\tmov ax, ds ; Lower 16-bits of eax = ds.\n\tpush eax ; save the data segment descriptor\n\tmov ax, 0x10  ; kernel data segment descriptor\n\tmov ds, ax\n\tmov es, ax\n\tmov fs, ax\n\tmov gs, ax\n\t\n    ; 2. Call C handler\n\tcall isr_handler\n\t\n    ; 3. Restore state\n\tpop eax \n\tmov ds, ax\n\tmov es, ax\n\tmov fs, ax\n\tmov gs, ax\n\tpopa\n\tadd esp, 8 ; Cleans up the pushed error code and pushed ISR number\n\tsti\n\tiret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP\n\n; Common IRQ code. Identical to ISR code except for the 'call' \n; and the 'pop ebx'\nirq_common_stub:\n    pusha \n    mov ax, ds\n    push eax\n    mov ax, 0x10\n    mov ds, ax\n    mov es, ax\n    mov fs, ax\n    mov gs, ax\n    call irq_handler ; Different than the ISR code\n    pop ebx  ; Different than the ISR code\n    mov ds, bx\n    mov es, bx\n    mov fs, bx\n    mov gs, bx\n    popa\n    add esp, 8\n    sti\n    iret \n\t\n; We don't get information about which interrupt was caller\n; when the handler is run, so we will need to have a different handler\n; for every interrupt.\n; Furthermore, some interrupts push an error code onto the stack but others\n; don't, so we will push a dummy error code for those which don't, so that\n; we have a consistent stack for all of them.\n\n; First make the ISRs global\nglobal isr0\nglobal isr1\nglobal isr2\nglobal isr3\nglobal isr4\nglobal isr5\nglobal isr6\nglobal isr7\nglobal isr8\nglobal isr9\nglobal isr10\nglobal isr11\nglobal isr12\nglobal isr13\nglobal isr14\nglobal isr15\nglobal isr16\nglobal isr17\nglobal isr18\nglobal isr19\nglobal isr20\nglobal isr21\nglobal isr22\nglobal isr23\nglobal isr24\nglobal isr25\nglobal isr26\nglobal isr27\nglobal isr28\nglobal isr29\nglobal isr30\nglobal isr31\n; IRQs\nglobal irq0\nglobal irq1\nglobal irq2\nglobal irq3\nglobal irq4\nglobal irq5\nglobal irq6\nglobal irq7\nglobal irq8\nglobal irq9\nglobal irq10\nglobal irq11\nglobal irq12\nglobal irq13\nglobal irq14\nglobal irq15\n\n; 0: Divide By Zero Exception\nisr0:\n    cli\n    push byte 0\n    push byte 0\n    jmp isr_common_stub\n\n; 1: Debug Exception\nisr1:\n    cli\n    push byte 0\n    push byte 1\n    jmp isr_common_stub\n\n; 2: Non Maskable Interrupt Exception\nisr2:\n    cli\n    push byte 0\n    push byte 2\n    jmp isr_common_stub\n\n; 3: Int 3 Exception\nisr3:\n    cli\n    push byte 0\n    push byte 3\n    jmp isr_common_stub\n\n; 4: INTO Exception\nisr4:\n    cli\n    push byte 0\n    push byte 4\n    jmp isr_common_stub\n\n; 5: Out of Bounds Exception\nisr5:\n    cli\n    push byte 0\n    push byte 5\n    jmp isr_common_stub\n\n; 6: Invalid Opcode Exception\nisr6:\n    cli\n    push byte 0\n    push byte 6\n    jmp isr_common_stub\n\n; 7: Coprocessor Not Available Exception\nisr7:\n    cli\n    push byte 0\n    push byte 7\n    jmp isr_common_stub\n\n; 8: Double Fault Exception (With Error Code!)\nisr8:\n    cli\n    push byte 8\n    jmp isr_common_stub\n\n; 9: Coprocessor Segment Overrun Exception\nisr9:\n    cli\n    push byte 0\n    push byte 9\n    jmp isr_common_stub\n\n; 10: Bad TSS Exception (With Error Code!)\nisr10:\n    cli\n    push byte 10\n    jmp isr_common_stub\n\n; 11: Segment Not Present Exception (With Error Code!)\nisr11:\n    cli\n    push byte 11\n    jmp isr_common_stub\n\n; 12: Stack Fault Exception (With Error Code!)\nisr12:\n    cli\n    push byte 12\n    jmp isr_common_stub\n\n; 13: General Protection Fault Exception (With Error Code!)\nisr13:\n    cli\n    push byte 13\n    jmp isr_common_stub\n\n; 14: Page Fault Exception (With Error Code!)\nisr14:\n    cli\n    push byte 14\n    jmp isr_common_stub\n\n; 15: Reserved Exception\nisr15:\n    cli\n    push byte 0\n    push byte 15\n    jmp isr_common_stub\n\n; 16: Floating Point Exception\nisr16:\n    cli\n    push byte 0\n    push byte 16\n    jmp isr_common_stub\n\n; 17: Alignment Check Exception\nisr17:\n    cli\n    push byte 0\n    push byte 17\n    jmp isr_common_stub\n\n; 18: Machine Check Exception\nisr18:\n    cli\n    push byte 0\n    push byte 18\n    jmp isr_common_stub\n\n; 19: Reserved\nisr19:\n    cli\n    push byte 0\n    push byte 19\n    jmp isr_common_stub\n\n; 20: Reserved\nisr20:\n    cli\n    push byte 0\n    push byte 20\n    jmp isr_common_stub\n\n; 21: Reserved\nisr21:\n    cli\n    push byte 0\n    push byte 21\n    jmp isr_common_stub\n\n; 22: Reserved\nisr22:\n    cli\n    push byte 0\n    push byte 22\n    jmp isr_common_stub\n\n; 23: Reserved\nisr23:\n    cli\n    push byte 0\n    push byte 23\n    jmp isr_common_stub\n\n; 24: Reserved\nisr24:\n    cli\n    push byte 0\n    push byte 24\n    jmp isr_common_stub\n\n; 25: Reserved\nisr25:\n    cli\n    push byte 0\n    push byte 25\n    jmp isr_common_stub\n\n; 26: Reserved\nisr26:\n    cli\n    push byte 0\n    push byte 26\n    jmp isr_common_stub\n\n; 27: Reserved\nisr27:\n    cli\n    push byte 0\n    push byte 27\n    jmp isr_common_stub\n\n; 28: Reserved\nisr28:\n    cli\n    push byte 0\n    push byte 28\n    jmp isr_common_stub\n\n; 29: Reserved\nisr29:\n    cli\n    push byte 0\n    push byte 29\n    jmp isr_common_stub\n\n; 30: Reserved\nisr30:\n    cli\n    push byte 0\n    push byte 30\n    jmp isr_common_stub\n\n; 31: Reserved\nisr31:\n    cli\n    push byte 0\n    push byte 31\n    jmp isr_common_stub\n\n; IRQ handlers\nirq0:\n\tcli\n\tpush byte 0\n\tpush byte 32\n\tjmp irq_common_stub\n\nirq1:\n\tcli\n\tpush byte 1\n\tpush byte 33\n\tjmp irq_common_stub\n\nirq2:\n\tcli\n\tpush byte 2\n\tpush byte 34\n\tjmp irq_common_stub\n\nirq3:\n\tcli\n\tpush byte 3\n\tpush byte 35\n\tjmp irq_common_stub\n\nirq4:\n\tcli\n\tpush byte 4\n\tpush byte 36\n\tjmp irq_common_stub\n\nirq5:\n\tcli\n\tpush byte 5\n\tpush byte 37\n\tjmp irq_common_stub\n\nirq6:\n\tcli\n\tpush byte 6\n\tpush byte 38\n\tjmp irq_common_stub\n\nirq7:\n\tcli\n\tpush byte 7\n\tpush byte 39\n\tjmp irq_common_stub\n\nirq8:\n\tcli\n\tpush byte 8\n\tpush byte 40\n\tjmp irq_common_stub\n\nirq9:\n\tcli\n\tpush byte 9\n\tpush byte 41\n\tjmp irq_common_stub\n\nirq10:\n\tcli\n\tpush byte 10\n\tpush byte 42\n\tjmp irq_common_stub\n\nirq11:\n\tcli\n\tpush byte 11\n\tpush byte 43\n\tjmp irq_common_stub\n\nirq12:\n\tcli\n\tpush byte 12\n\tpush byte 44\n\tjmp irq_common_stub\n\nirq13:\n\tcli\n\tpush byte 13\n\tpush byte 45\n\tjmp irq_common_stub\n\nirq14:\n\tcli\n\tpush byte 14\n\tpush byte 46\n\tjmp irq_common_stub\n\nirq15:\n\tcli\n\tpush byte 15\n\tpush byte 47\n\tjmp irq_common_stub\n\n"
  },
  {
    "path": "21-shell/cpu/isr.c",
    "content": "#include \"isr.h\"\n#include \"idt.h\"\n#include \"../drivers/screen.h\"\n#include \"../drivers/keyboard.h\"\n#include \"../libc/string.h\"\n#include \"timer.h\"\n#include \"ports.h\"\n\nisr_t interrupt_handlers[256];\n\n/* Can't do this with a loop because we need the address\n * of the function names */\nvoid isr_install() {\n    set_idt_gate(0, (u32)isr0);\n    set_idt_gate(1, (u32)isr1);\n    set_idt_gate(2, (u32)isr2);\n    set_idt_gate(3, (u32)isr3);\n    set_idt_gate(4, (u32)isr4);\n    set_idt_gate(5, (u32)isr5);\n    set_idt_gate(6, (u32)isr6);\n    set_idt_gate(7, (u32)isr7);\n    set_idt_gate(8, (u32)isr8);\n    set_idt_gate(9, (u32)isr9);\n    set_idt_gate(10, (u32)isr10);\n    set_idt_gate(11, (u32)isr11);\n    set_idt_gate(12, (u32)isr12);\n    set_idt_gate(13, (u32)isr13);\n    set_idt_gate(14, (u32)isr14);\n    set_idt_gate(15, (u32)isr15);\n    set_idt_gate(16, (u32)isr16);\n    set_idt_gate(17, (u32)isr17);\n    set_idt_gate(18, (u32)isr18);\n    set_idt_gate(19, (u32)isr19);\n    set_idt_gate(20, (u32)isr20);\n    set_idt_gate(21, (u32)isr21);\n    set_idt_gate(22, (u32)isr22);\n    set_idt_gate(23, (u32)isr23);\n    set_idt_gate(24, (u32)isr24);\n    set_idt_gate(25, (u32)isr25);\n    set_idt_gate(26, (u32)isr26);\n    set_idt_gate(27, (u32)isr27);\n    set_idt_gate(28, (u32)isr28);\n    set_idt_gate(29, (u32)isr29);\n    set_idt_gate(30, (u32)isr30);\n    set_idt_gate(31, (u32)isr31);\n\n    // Remap the PIC\n    port_byte_out(0x20, 0x11);\n    port_byte_out(0xA0, 0x11);\n    port_byte_out(0x21, 0x20);\n    port_byte_out(0xA1, 0x28);\n    port_byte_out(0x21, 0x04);\n    port_byte_out(0xA1, 0x02);\n    port_byte_out(0x21, 0x01);\n    port_byte_out(0xA1, 0x01);\n    port_byte_out(0x21, 0x0);\n    port_byte_out(0xA1, 0x0); \n\n    // Install the IRQs\n    set_idt_gate(32, (u32)irq0);\n    set_idt_gate(33, (u32)irq1);\n    set_idt_gate(34, (u32)irq2);\n    set_idt_gate(35, (u32)irq3);\n    set_idt_gate(36, (u32)irq4);\n    set_idt_gate(37, (u32)irq5);\n    set_idt_gate(38, (u32)irq6);\n    set_idt_gate(39, (u32)irq7);\n    set_idt_gate(40, (u32)irq8);\n    set_idt_gate(41, (u32)irq9);\n    set_idt_gate(42, (u32)irq10);\n    set_idt_gate(43, (u32)irq11);\n    set_idt_gate(44, (u32)irq12);\n    set_idt_gate(45, (u32)irq13);\n    set_idt_gate(46, (u32)irq14);\n    set_idt_gate(47, (u32)irq15);\n\n    set_idt(); // Load with ASM\n}\n\n/* To print the message which defines every exception */\nchar *exception_messages[] = {\n    \"Division By Zero\",\n    \"Debug\",\n    \"Non Maskable Interrupt\",\n    \"Breakpoint\",\n    \"Into Detected Overflow\",\n    \"Out of Bounds\",\n    \"Invalid Opcode\",\n    \"No Coprocessor\",\n\n    \"Double Fault\",\n    \"Coprocessor Segment Overrun\",\n    \"Bad TSS\",\n    \"Segment Not Present\",\n    \"Stack Fault\",\n    \"General Protection Fault\",\n    \"Page Fault\",\n    \"Unknown Interrupt\",\n\n    \"Coprocessor Fault\",\n    \"Alignment Check\",\n    \"Machine Check\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\"\n};\n\nvoid isr_handler(registers_t r) {\n    kprint(\"received interrupt: \");\n    char s[3];\n    int_to_ascii(r.int_no, s);\n    kprint(s);\n    kprint(\"\\n\");\n    kprint(exception_messages[r.int_no]);\n    kprint(\"\\n\");\n}\n\nvoid register_interrupt_handler(u8 n, isr_t handler) {\n    interrupt_handlers[n] = handler;\n}\n\nvoid irq_handler(registers_t r) {\n    /* After every interrupt we need to send an EOI to the PICs\n     * or they will not send another interrupt again */\n    if (r.int_no >= 40) port_byte_out(0xA0, 0x20); /* slave */\n    port_byte_out(0x20, 0x20); /* master */\n\n    /* Handle the interrupt in a more modular way */\n    if (interrupt_handlers[r.int_no] != 0) {\n        isr_t handler = interrupt_handlers[r.int_no];\n        handler(r);\n    }\n}\n\nvoid irq_install() {\n    /* Enable interruptions */\n    asm volatile(\"sti\");\n    /* IRQ0: timer */\n    init_timer(50);\n    /* IRQ1: keyboard */\n    init_keyboard();\n}\n"
  },
  {
    "path": "21-shell/cpu/isr.h",
    "content": "#ifndef ISR_H\n#define ISR_H\n\n#include \"types.h\"\n\n/* ISRs reserved for CPU exceptions */\nextern void isr0();\nextern void isr1();\nextern void isr2();\nextern void isr3();\nextern void isr4();\nextern void isr5();\nextern void isr6();\nextern void isr7();\nextern void isr8();\nextern void isr9();\nextern void isr10();\nextern void isr11();\nextern void isr12();\nextern void isr13();\nextern void isr14();\nextern void isr15();\nextern void isr16();\nextern void isr17();\nextern void isr18();\nextern void isr19();\nextern void isr20();\nextern void isr21();\nextern void isr22();\nextern void isr23();\nextern void isr24();\nextern void isr25();\nextern void isr26();\nextern void isr27();\nextern void isr28();\nextern void isr29();\nextern void isr30();\nextern void isr31();\n/* IRQ definitions */\nextern void irq0();\nextern void irq1();\nextern void irq2();\nextern void irq3();\nextern void irq4();\nextern void irq5();\nextern void irq6();\nextern void irq7();\nextern void irq8();\nextern void irq9();\nextern void irq10();\nextern void irq11();\nextern void irq12();\nextern void irq13();\nextern void irq14();\nextern void irq15();\n\n#define IRQ0 32\n#define IRQ1 33\n#define IRQ2 34\n#define IRQ3 35\n#define IRQ4 36\n#define IRQ5 37\n#define IRQ6 38\n#define IRQ7 39\n#define IRQ8 40\n#define IRQ9 41\n#define IRQ10 42\n#define IRQ11 43\n#define IRQ12 44\n#define IRQ13 45\n#define IRQ14 46\n#define IRQ15 47\n\n/* Struct which aggregates many registers */\ntypedef struct {\n   u32 ds; /* Data segment selector */\n   u32 edi, esi, ebp, esp, ebx, edx, ecx, eax; /* Pushed by pusha. */\n   u32 int_no, err_code; /* Interrupt number and error code (if applicable) */\n   u32 eip, cs, eflags, useresp, ss; /* Pushed by the processor automatically */\n} registers_t;\n\nvoid isr_install();\nvoid isr_handler(registers_t r);\nvoid irq_install();\n\ntypedef void (*isr_t)(registers_t);\nvoid register_interrupt_handler(u8 n, isr_t handler);\n\n#endif\n"
  },
  {
    "path": "21-shell/cpu/timer.c",
    "content": "#include \"timer.h\"\n#include \"isr.h\"\n#include \"ports.h\"\n#include \"../libc/function.h\"\n\nu32 tick = 0;\n\nstatic void timer_callback(registers_t regs) {\n    tick++;\n    UNUSED(regs);\n}\n\nvoid init_timer(u32 freq) {\n    /* Install the function we just wrote */\n    register_interrupt_handler(IRQ0, timer_callback);\n\n    /* Get the PIT value: hardware clock at 1193180 Hz */\n    u32 divisor = 1193180 / freq;\n    u8 low  = (u8)(divisor & 0xFF);\n    u8 high = (u8)( (divisor >> 8) & 0xFF);\n    /* Send the command */\n    port_byte_out(0x43, 0x36); /* Command port */\n    port_byte_out(0x40, low);\n    port_byte_out(0x40, high);\n}\n\n"
  },
  {
    "path": "21-shell/cpu/timer.h",
    "content": "#ifndef TIMER_H\n#define TIMER_H\n\n#include \"types.h\"\n\nvoid init_timer(u32 freq);\n\n#endif\n"
  },
  {
    "path": "21-shell/cpu/types.h",
    "content": "#ifndef TYPES_H\n#define TYPES_H\n\n/* Instead of using 'chars' to allocate non-character bytes,\n * we will use these new type with no semantic meaning */\ntypedef unsigned int   u32;\ntypedef          int   s32;\ntypedef unsigned short u16;\ntypedef          short s16;\ntypedef unsigned char  u8;\ntypedef          char  s8;\n\n#define low_16(address) (u16)((address) & 0xFFFF)\n#define high_16(address) (u16)(((address) >> 16) & 0xFFFF)\n\n#endif\n"
  },
  {
    "path": "21-shell/drivers/keyboard.c",
    "content": "#include \"keyboard.h\"\n#include \"../cpu/ports.h\"\n#include \"../cpu/isr.h\"\n#include \"screen.h\"\n#include \"../libc/string.h\"\n#include \"../libc/function.h\"\n#include \"../kernel/kernel.h\"\n\n#define BACKSPACE 0x0E\n#define ENTER 0x1C\n\nstatic char key_buffer[256];\n\n#define SC_MAX 57\nconst char *sc_name[] = { \"ERROR\", \"Esc\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \n    \"7\", \"8\", \"9\", \"0\", \"-\", \"=\", \"Backspace\", \"Tab\", \"Q\", \"W\", \"E\", \n        \"R\", \"T\", \"Y\", \"U\", \"I\", \"O\", \"P\", \"[\", \"]\", \"Enter\", \"Lctrl\", \n        \"A\", \"S\", \"D\", \"F\", \"G\", \"H\", \"J\", \"K\", \"L\", \";\", \"'\", \"`\", \n        \"LShift\", \"\\\\\", \"Z\", \"X\", \"C\", \"V\", \"B\", \"N\", \"M\", \",\", \".\", \n        \"/\", \"RShift\", \"Keypad *\", \"LAlt\", \"Spacebar\"};\nconst char sc_ascii[] = { '?', '?', '1', '2', '3', '4', '5', '6',     \n    '7', '8', '9', '0', '-', '=', '?', '?', 'Q', 'W', 'E', 'R', 'T', 'Y', \n        'U', 'I', 'O', 'P', '[', ']', '?', '?', 'A', 'S', 'D', 'F', 'G', \n        'H', 'J', 'K', 'L', ';', '\\'', '`', '?', '\\\\', 'Z', 'X', 'C', 'V', \n        'B', 'N', 'M', ',', '.', '/', '?', '?', '?', ' '};\n\nstatic void keyboard_callback(registers_t regs) {\n    /* The PIC leaves us the scancode in port 0x60 */\n    u8 scancode = port_byte_in(0x60);\n    \n    if (scancode > SC_MAX) return;\n    if (scancode == BACKSPACE) {\n        backspace(key_buffer);\n        kprint_backspace();\n    } else if (scancode == ENTER) {\n        kprint(\"\\n\");\n        user_input(key_buffer); /* kernel-controlled function */\n        key_buffer[0] = '\\0';\n    } else {\n        char letter = sc_ascii[(int)scancode];\n        /* Remember that kprint only accepts char[] */\n        char str[2] = {letter, '\\0'};\n        append(key_buffer, letter);\n        kprint(str);\n    }\n    UNUSED(regs);\n}\n\nvoid init_keyboard() {\n   register_interrupt_handler(IRQ1, keyboard_callback); \n}\n"
  },
  {
    "path": "21-shell/drivers/keyboard.h",
    "content": "#include \"../cpu/types.h\"\n\nvoid init_keyboard();\n"
  },
  {
    "path": "21-shell/drivers/screen.c",
    "content": "#include \"screen.h\"\n#include \"../cpu/ports.h\"\n#include \"../libc/mem.h\"\n\n/* Declaration of private functions */\nint get_cursor_offset();\nvoid set_cursor_offset(int offset);\nint print_char(char c, int col, int row, char attr);\nint get_offset(int col, int row);\nint get_offset_row(int offset);\nint get_offset_col(int offset);\n\n/**********************************************************\n * Public Kernel API functions                            *\n **********************************************************/\n\n/**\n * Print a message on the specified location\n * If col, row, are negative, we will use the current offset\n */\nvoid kprint_at(char *message, int col, int row) {\n    /* Set cursor if col/row are negative */\n    int offset;\n    if (col >= 0 && row >= 0)\n        offset = get_offset(col, row);\n    else {\n        offset = get_cursor_offset();\n        row = get_offset_row(offset);\n        col = get_offset_col(offset);\n    }\n\n    /* Loop through message and print it */\n    int i = 0;\n    while (message[i] != 0) {\n        offset = print_char(message[i++], col, row, WHITE_ON_BLACK);\n        /* Compute row/col for next iteration */\n        row = get_offset_row(offset);\n        col = get_offset_col(offset);\n    }\n}\n\nvoid kprint(char *message) {\n    kprint_at(message, -1, -1);\n}\n\nvoid kprint_backspace() {\n    int offset = get_cursor_offset()-2;\n    int row = get_offset_row(offset);\n    int col = get_offset_col(offset);\n    print_char(0x08, col, row, WHITE_ON_BLACK);\n}\n\n\n/**********************************************************\n * Private kernel functions                               *\n **********************************************************/\n\n\n/**\n * Innermost print function for our kernel, directly accesses the video memory \n *\n * If 'col' and 'row' are negative, we will print at current cursor location\n * If 'attr' is zero it will use 'white on black' as default\n * Returns the offset of the next character\n * Sets the video cursor to the returned offset\n */\nint print_char(char c, int col, int row, char attr) {\n    u8 *vidmem = (u8*) VIDEO_ADDRESS;\n    if (!attr) attr = WHITE_ON_BLACK;\n\n    /* Error control: print a red 'E' if the coords aren't right */\n    if (col >= MAX_COLS || row >= MAX_ROWS) {\n        vidmem[2*(MAX_COLS)*(MAX_ROWS)-2] = 'E';\n        vidmem[2*(MAX_COLS)*(MAX_ROWS)-1] = RED_ON_WHITE;\n        return get_offset(col, row);\n    }\n\n    int offset;\n    if (col >= 0 && row >= 0) offset = get_offset(col, row);\n    else offset = get_cursor_offset();\n\n    if (c == '\\n') {\n        row = get_offset_row(offset);\n        offset = get_offset(0, row+1);\n    } else if (c == 0x08) { /* Backspace */\n        vidmem[offset] = ' ';\n        vidmem[offset+1] = attr;\n    } else {\n        vidmem[offset] = c;\n        vidmem[offset+1] = attr;\n        offset += 2;\n    }\n\n    /* Check if the offset is over screen size and scroll */\n    if (offset >= MAX_ROWS * MAX_COLS * 2) {\n        int i;\n        for (i = 1; i < MAX_ROWS; i++) \n            memory_copy((u8*)(get_offset(0, i) + VIDEO_ADDRESS),\n                        (u8*)(get_offset(0, i-1) + VIDEO_ADDRESS),\n                        MAX_COLS * 2);\n\n        /* Blank last line */\n        char *last_line = (char*) (get_offset(0, MAX_ROWS-1) + (u8*) VIDEO_ADDRESS);\n        for (i = 0; i < MAX_COLS * 2; i++) last_line[i] = 0;\n\n        offset -= 2 * MAX_COLS;\n    }\n\n    set_cursor_offset(offset);\n    return offset;\n}\n\nint get_cursor_offset() {\n    /* Use the VGA ports to get the current cursor position\n     * 1. Ask for high byte of the cursor offset (data 14)\n     * 2. Ask for low byte (data 15)\n     */\n    port_byte_out(REG_SCREEN_CTRL, 14);\n    int offset = port_byte_in(REG_SCREEN_DATA) << 8; /* High byte: << 8 */\n    port_byte_out(REG_SCREEN_CTRL, 15);\n    offset += port_byte_in(REG_SCREEN_DATA);\n    return offset * 2; /* Position * size of character cell */\n}\n\nvoid set_cursor_offset(int offset) {\n    /* Similar to get_cursor_offset, but instead of reading we write data */\n    offset /= 2;\n    port_byte_out(REG_SCREEN_CTRL, 14);\n    port_byte_out(REG_SCREEN_DATA, (u8)(offset >> 8));\n    port_byte_out(REG_SCREEN_CTRL, 15);\n    port_byte_out(REG_SCREEN_DATA, (u8)(offset & 0xff));\n}\n\nvoid clear_screen() {\n    int screen_size = MAX_COLS * MAX_ROWS;\n    int i;\n    u8 *screen = (u8*) VIDEO_ADDRESS;\n\n    for (i = 0; i < screen_size; i++) {\n        screen[i*2] = ' ';\n        screen[i*2+1] = WHITE_ON_BLACK;\n    }\n    set_cursor_offset(get_offset(0, 0));\n}\n\n\nint get_offset(int col, int row) { return 2 * (row * MAX_COLS + col); }\nint get_offset_row(int offset) { return offset / (2 * MAX_COLS); }\nint get_offset_col(int offset) { return (offset - (get_offset_row(offset)*2*MAX_COLS))/2; }\n"
  },
  {
    "path": "21-shell/drivers/screen.h",
    "content": "#ifndef SCREEN_H\n#define SCREEN_H\n\n#include \"../cpu/types.h\"\n\n#define VIDEO_ADDRESS 0xb8000\n#define MAX_ROWS 25\n#define MAX_COLS 80\n#define WHITE_ON_BLACK 0x0f\n#define RED_ON_WHITE 0xf4\n\n/* Screen i/o ports */\n#define REG_SCREEN_CTRL 0x3d4\n#define REG_SCREEN_DATA 0x3d5\n\n/* Public kernel API */\nvoid clear_screen();\nvoid kprint_at(char *message, int col, int row);\nvoid kprint(char *message);\nvoid kprint_backspace();\n\n#endif\n"
  },
  {
    "path": "21-shell/kernel/kernel.c",
    "content": "#include \"../cpu/isr.h\"\n#include \"../drivers/screen.h\"\n#include \"kernel.h\"\n#include \"../libc/string.h\"\n\nvoid main() {\n    isr_install();\n    irq_install();\n\n    kprint(\"Type something, it will go through the kernel\\n\"\n        \"Type END to halt the CPU\\n> \");\n}\n\nvoid user_input(char *input) {\n    if (strcmp(input, \"END\") == 0) {\n        kprint(\"Stopping the CPU. Bye!\\n\");\n        asm volatile(\"hlt\");\n    }\n    kprint(\"You said: \");\n    kprint(input);\n    kprint(\"\\n> \");\n}\n"
  },
  {
    "path": "21-shell/kernel/kernel.h",
    "content": "#ifndef KERNEL_H\n#define KERNEL_H\n\nvoid user_input(char *input);\n\n#endif\n"
  },
  {
    "path": "21-shell/libc/function.h",
    "content": "#ifndef FUNCTION_H\n#define FUNCTION_H\n\n/* Sometimes we want to keep parameters to a function for later use\n * and this is a solution to avoid the 'unused parameter' compiler warning */\n#define UNUSED(x) (void)(x)\n\n#endif\n"
  },
  {
    "path": "21-shell/libc/mem.c",
    "content": "#include \"mem.h\"\n\nvoid memory_copy(u8 *source, u8 *dest, int nbytes) {\n    int i;\n    for (i = 0; i < nbytes; i++) {\n        *(dest + i) = *(source + i);\n    }\n}\n\nvoid memory_set(u8 *dest, u8 val, u32 len) {\n    u8 *temp = (u8 *)dest;\n    for ( ; len != 0; len--) *temp++ = val;\n}\n"
  },
  {
    "path": "21-shell/libc/mem.h",
    "content": "#ifndef MEM_H\n#define MEM_H\n\n#include \"../cpu/types.h\"\n\nvoid memory_copy(u8 *source, u8 *dest, int nbytes);\nvoid memory_set(u8 *dest, u8 val, u32 len);\n\n#endif\n"
  },
  {
    "path": "21-shell/libc/string.c",
    "content": "#include \"string.h\"\n\n/**\n * K&R implementation\n */\nvoid int_to_ascii(int n, char str[]) {\n    int i, sign;\n    if ((sign = n) < 0) n = -n;\n    i = 0;\n    do {\n        str[i++] = n % 10 + '0';\n    } while ((n /= 10) > 0);\n\n    if (sign < 0) str[i++] = '-';\n    str[i] = '\\0';\n\n    reverse(str);\n}\n\n/* K&R */\nvoid reverse(char s[]) {\n    int c, i, j;\n    for (i = 0, j = strlen(s)-1; i < j; i++, j--) {\n        c = s[i];\n        s[i] = s[j];\n        s[j] = c;\n    }\n}\n\n/* K&R */\nint strlen(char s[]) {\n    int i = 0;\n    while (s[i] != '\\0') ++i;\n    return i;\n}\n\nvoid append(char s[], char n) {\n    int len = strlen(s);\n    s[len] = n;\n    s[len+1] = '\\0';\n}\n\nvoid backspace(char s[]) {\n    int len = strlen(s);\n    s[len-1] = '\\0';\n}\n\n/* K&R \n * Returns <0 if s1<s2, 0 if s1==s2, >0 if s1>s2 */\nint strcmp(char s1[], char s2[]) {\n    int i;\n    for (i = 0; s1[i] == s2[i]; i++) {\n        if (s1[i] == '\\0') return 0;\n    }\n    return s1[i] - s2[i];\n}\n"
  },
  {
    "path": "21-shell/libc/string.h",
    "content": "#ifndef STRINGS_H\n#define STRINGS_H\n\nvoid int_to_ascii(int n, char str[]);\nvoid reverse(char s[]);\nint strlen(char s[]);\nvoid backspace(char s[]);\nvoid append(char s[], char n);\nint strcmp(char s1[], char s2[]);\n\n#endif\n"
  },
  {
    "path": "22-malloc/Makefile",
    "content": "C_SOURCES = $(wildcard kernel/*.c drivers/*.c cpu/*.c libc/*.c)\nHEADERS = $(wildcard kernel/*.h drivers/*.h cpu/*.h libc/*.h)\n# Nice syntax for file extension replacement\nOBJ = ${C_SOURCES:.c=.o cpu/interrupt.o} \n\n# Change this if your cross-compiler is somewhere else\nCC = /usr/local/i386elfgcc/bin/i386-elf-gcc\nGDB = /usr/local/i386elfgcc/bin/i386-elf-gdb\n# -g: Use debugging symbols in gcc\nCFLAGS = -g -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector -nostartfiles -nodefaultlibs \\\n\t\t -Wall -Wextra -Werror\n\n# First rule is run by default\nos-image.bin: boot/bootsect.bin kernel.bin\n\tcat $^ > os-image.bin\n\n# '--oformat binary' deletes all symbols as a collateral, so we don't need\n# to 'strip' them manually on this case\nkernel.bin: boot/kernel_entry.o ${OBJ}\n\ti386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary\n\n# Used for debugging purposes\nkernel.elf: boot/kernel_entry.o ${OBJ}\n\ti386-elf-ld -o $@ -Ttext 0x1000 $^ \n\nrun: os-image.bin\n\tqemu-system-i386 -fda os-image.bin\n\n# Open the connection to qemu and load our kernel-object file with symbols\ndebug: os-image.bin kernel.elf\n\tqemu-system-i386 -s -fda os-image.bin -d guest_errors,int &\n\t${GDB} -ex \"target remote localhost:1234\" -ex \"symbol-file kernel.elf\"\n\n# Generic rules for wildcards\n# To make an object, always compile from its .c\n%.o: %.c ${HEADERS}\n\t${CC} ${CFLAGS} -ffreestanding -c $< -o $@\n\n%.o: %.asm\n\tnasm $< -f elf -o $@\n\n%.bin: %.asm\n\tnasm $< -f bin -o $@\n\nclean:\n\trm -rf *.bin *.dis *.o os-image.bin *.elf\n\trm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o cpu/*.o libc/*.o\n"
  },
  {
    "path": "22-malloc/README.md",
    "content": "*Concepts you may want to Google beforehand: malloc*\n\n**Goal: Implement a memory allocator**\n\nWe will add a kernel memory allocator to `libc/mem.c`. It is \nimplemented as a simple pointer to free memory, which keeps\ngrowing.\n\nThe `kmalloc()` function can be used to request an aligned page,\nand it will also return the real, physical address, for later use.\n\nWe'll change the `kernel.c` leaving all the \"shell\" code there,\nLet's just try out the new `kmalloc()`, and check out that\nour first page starts at 0x10000 (as hardcoded on `mem.c`) and\nsubsequent `kmalloc()`'s produce a new address which is\naligned 4096 bytes or 0x1000 from the previous one.\n\nNote that we added a new `strings.c:hex_to_ascii()` for\nnicer printing of hex numbers.\n\nAnother cosmetic modification is to rename `types.c` to \n`type.c` for language consistency.\n\nThe rest of the files are unchanged from last lesson.\n"
  },
  {
    "path": "22-malloc/cpu/idt.c",
    "content": "#include \"idt.h\"\n\nvoid set_idt_gate(int n, u32 handler) {\n    idt[n].low_offset = low_16(handler);\n    idt[n].sel = KERNEL_CS;\n    idt[n].always0 = 0;\n    idt[n].flags = 0x8E; \n    idt[n].high_offset = high_16(handler);\n}\n\nvoid set_idt() {\n    idt_reg.base = (u32) &idt;\n    idt_reg.limit = IDT_ENTRIES * sizeof(idt_gate_t) - 1;\n    /* Don't make the mistake of loading &idt -- always load &idt_reg */\n    __asm__ __volatile__(\"lidtl (%0)\" : : \"r\" (&idt_reg));\n}\n"
  },
  {
    "path": "22-malloc/cpu/idt.h",
    "content": "#ifndef IDT_H\n#define IDT_H\n\n#include \"type.h\"\n\n/* Segment selectors */\n#define KERNEL_CS 0x08\n\n/* How every interrupt gate (handler) is defined */\ntypedef struct {\n    u16 low_offset; /* Lower 16 bits of handler function address */\n    u16 sel; /* Kernel segment selector */\n    u8 always0;\n    /* First byte\n     * Bit 7: \"Interrupt is present\"\n     * Bits 6-5: Privilege level of caller (0=kernel..3=user)\n     * Bit 4: Set to 0 for interrupt gates\n     * Bits 3-0: bits 1110 = decimal 14 = \"32 bit interrupt gate\" */\n    u8 flags; \n    u16 high_offset; /* Higher 16 bits of handler function address */\n} __attribute__((packed)) idt_gate_t ;\n\n/* A pointer to the array of interrupt handlers.\n * Assembly instruction 'lidt' will read it */\ntypedef struct {\n    u16 limit;\n    u32 base;\n} __attribute__((packed)) idt_register_t;\n\n#define IDT_ENTRIES 256\nidt_gate_t idt[IDT_ENTRIES];\nidt_register_t idt_reg;\n\n\n/* Functions implemented in idt.c */\nvoid set_idt_gate(int n, u32 handler);\nvoid set_idt();\n\n#endif\n"
  },
  {
    "path": "22-malloc/cpu/interrupt.asm",
    "content": "; Defined in isr.c\n[extern isr_handler]\n[extern irq_handler]\n\n; Common ISR code\nisr_common_stub:\n    ; 1. Save CPU state\n\tpusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax\n\tmov ax, ds ; Lower 16-bits of eax = ds.\n\tpush eax ; save the data segment descriptor\n\tmov ax, 0x10  ; kernel data segment descriptor\n\tmov ds, ax\n\tmov es, ax\n\tmov fs, ax\n\tmov gs, ax\n\t\n    ; 2. Call C handler\n\tcall isr_handler\n\t\n    ; 3. Restore state\n\tpop eax \n\tmov ds, ax\n\tmov es, ax\n\tmov fs, ax\n\tmov gs, ax\n\tpopa\n\tadd esp, 8 ; Cleans up the pushed error code and pushed ISR number\n\tsti\n\tiret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP\n\n; Common IRQ code. Identical to ISR code except for the 'call' \n; and the 'pop ebx'\nirq_common_stub:\n    pusha \n    mov ax, ds\n    push eax\n    mov ax, 0x10\n    mov ds, ax\n    mov es, ax\n    mov fs, ax\n    mov gs, ax\n    call irq_handler ; Different than the ISR code\n    pop ebx  ; Different than the ISR code\n    mov ds, bx\n    mov es, bx\n    mov fs, bx\n    mov gs, bx\n    popa\n    add esp, 8\n    sti\n    iret \n\t\n; We don't get information about which interrupt was caller\n; when the handler is run, so we will need to have a different handler\n; for every interrupt.\n; Furthermore, some interrupts push an error code onto the stack but others\n; don't, so we will push a dummy error code for those which don't, so that\n; we have a consistent stack for all of them.\n\n; First make the ISRs global\nglobal isr0\nglobal isr1\nglobal isr2\nglobal isr3\nglobal isr4\nglobal isr5\nglobal isr6\nglobal isr7\nglobal isr8\nglobal isr9\nglobal isr10\nglobal isr11\nglobal isr12\nglobal isr13\nglobal isr14\nglobal isr15\nglobal isr16\nglobal isr17\nglobal isr18\nglobal isr19\nglobal isr20\nglobal isr21\nglobal isr22\nglobal isr23\nglobal isr24\nglobal isr25\nglobal isr26\nglobal isr27\nglobal isr28\nglobal isr29\nglobal isr30\nglobal isr31\n; IRQs\nglobal irq0\nglobal irq1\nglobal irq2\nglobal irq3\nglobal irq4\nglobal irq5\nglobal irq6\nglobal irq7\nglobal irq8\nglobal irq9\nglobal irq10\nglobal irq11\nglobal irq12\nglobal irq13\nglobal irq14\nglobal irq15\n\n; 0: Divide By Zero Exception\nisr0:\n    cli\n    push byte 0\n    push byte 0\n    jmp isr_common_stub\n\n; 1: Debug Exception\nisr1:\n    cli\n    push byte 0\n    push byte 1\n    jmp isr_common_stub\n\n; 2: Non Maskable Interrupt Exception\nisr2:\n    cli\n    push byte 0\n    push byte 2\n    jmp isr_common_stub\n\n; 3: Int 3 Exception\nisr3:\n    cli\n    push byte 0\n    push byte 3\n    jmp isr_common_stub\n\n; 4: INTO Exception\nisr4:\n    cli\n    push byte 0\n    push byte 4\n    jmp isr_common_stub\n\n; 5: Out of Bounds Exception\nisr5:\n    cli\n    push byte 0\n    push byte 5\n    jmp isr_common_stub\n\n; 6: Invalid Opcode Exception\nisr6:\n    cli\n    push byte 0\n    push byte 6\n    jmp isr_common_stub\n\n; 7: Coprocessor Not Available Exception\nisr7:\n    cli\n    push byte 0\n    push byte 7\n    jmp isr_common_stub\n\n; 8: Double Fault Exception (With Error Code!)\nisr8:\n    cli\n    push byte 8\n    jmp isr_common_stub\n\n; 9: Coprocessor Segment Overrun Exception\nisr9:\n    cli\n    push byte 0\n    push byte 9\n    jmp isr_common_stub\n\n; 10: Bad TSS Exception (With Error Code!)\nisr10:\n    cli\n    push byte 10\n    jmp isr_common_stub\n\n; 11: Segment Not Present Exception (With Error Code!)\nisr11:\n    cli\n    push byte 11\n    jmp isr_common_stub\n\n; 12: Stack Fault Exception (With Error Code!)\nisr12:\n    cli\n    push byte 12\n    jmp isr_common_stub\n\n; 13: General Protection Fault Exception (With Error Code!)\nisr13:\n    cli\n    push byte 13\n    jmp isr_common_stub\n\n; 14: Page Fault Exception (With Error Code!)\nisr14:\n    cli\n    push byte 14\n    jmp isr_common_stub\n\n; 15: Reserved Exception\nisr15:\n    cli\n    push byte 0\n    push byte 15\n    jmp isr_common_stub\n\n; 16: Floating Point Exception\nisr16:\n    cli\n    push byte 0\n    push byte 16\n    jmp isr_common_stub\n\n; 17: Alignment Check Exception\nisr17:\n    cli\n    push byte 0\n    push byte 17\n    jmp isr_common_stub\n\n; 18: Machine Check Exception\nisr18:\n    cli\n    push byte 0\n    push byte 18\n    jmp isr_common_stub\n\n; 19: Reserved\nisr19:\n    cli\n    push byte 0\n    push byte 19\n    jmp isr_common_stub\n\n; 20: Reserved\nisr20:\n    cli\n    push byte 0\n    push byte 20\n    jmp isr_common_stub\n\n; 21: Reserved\nisr21:\n    cli\n    push byte 0\n    push byte 21\n    jmp isr_common_stub\n\n; 22: Reserved\nisr22:\n    cli\n    push byte 0\n    push byte 22\n    jmp isr_common_stub\n\n; 23: Reserved\nisr23:\n    cli\n    push byte 0\n    push byte 23\n    jmp isr_common_stub\n\n; 24: Reserved\nisr24:\n    cli\n    push byte 0\n    push byte 24\n    jmp isr_common_stub\n\n; 25: Reserved\nisr25:\n    cli\n    push byte 0\n    push byte 25\n    jmp isr_common_stub\n\n; 26: Reserved\nisr26:\n    cli\n    push byte 0\n    push byte 26\n    jmp isr_common_stub\n\n; 27: Reserved\nisr27:\n    cli\n    push byte 0\n    push byte 27\n    jmp isr_common_stub\n\n; 28: Reserved\nisr28:\n    cli\n    push byte 0\n    push byte 28\n    jmp isr_common_stub\n\n; 29: Reserved\nisr29:\n    cli\n    push byte 0\n    push byte 29\n    jmp isr_common_stub\n\n; 30: Reserved\nisr30:\n    cli\n    push byte 0\n    push byte 30\n    jmp isr_common_stub\n\n; 31: Reserved\nisr31:\n    cli\n    push byte 0\n    push byte 31\n    jmp isr_common_stub\n\n; IRQ handlers\nirq0:\n\tcli\n\tpush byte 0\n\tpush byte 32\n\tjmp irq_common_stub\n\nirq1:\n\tcli\n\tpush byte 1\n\tpush byte 33\n\tjmp irq_common_stub\n\nirq2:\n\tcli\n\tpush byte 2\n\tpush byte 34\n\tjmp irq_common_stub\n\nirq3:\n\tcli\n\tpush byte 3\n\tpush byte 35\n\tjmp irq_common_stub\n\nirq4:\n\tcli\n\tpush byte 4\n\tpush byte 36\n\tjmp irq_common_stub\n\nirq5:\n\tcli\n\tpush byte 5\n\tpush byte 37\n\tjmp irq_common_stub\n\nirq6:\n\tcli\n\tpush byte 6\n\tpush byte 38\n\tjmp irq_common_stub\n\nirq7:\n\tcli\n\tpush byte 7\n\tpush byte 39\n\tjmp irq_common_stub\n\nirq8:\n\tcli\n\tpush byte 8\n\tpush byte 40\n\tjmp irq_common_stub\n\nirq9:\n\tcli\n\tpush byte 9\n\tpush byte 41\n\tjmp irq_common_stub\n\nirq10:\n\tcli\n\tpush byte 10\n\tpush byte 42\n\tjmp irq_common_stub\n\nirq11:\n\tcli\n\tpush byte 11\n\tpush byte 43\n\tjmp irq_common_stub\n\nirq12:\n\tcli\n\tpush byte 12\n\tpush byte 44\n\tjmp irq_common_stub\n\nirq13:\n\tcli\n\tpush byte 13\n\tpush byte 45\n\tjmp irq_common_stub\n\nirq14:\n\tcli\n\tpush byte 14\n\tpush byte 46\n\tjmp irq_common_stub\n\nirq15:\n\tcli\n\tpush byte 15\n\tpush byte 47\n\tjmp irq_common_stub\n\n"
  },
  {
    "path": "22-malloc/cpu/isr.c",
    "content": "#include \"isr.h\"\n#include \"idt.h\"\n#include \"../drivers/screen.h\"\n#include \"../drivers/keyboard.h\"\n#include \"../libc/string.h\"\n#include \"timer.h\"\n#include \"ports.h\"\n\nisr_t interrupt_handlers[256];\n\n/* Can't do this with a loop because we need the address\n * of the function names */\nvoid isr_install() {\n    set_idt_gate(0, (u32)isr0);\n    set_idt_gate(1, (u32)isr1);\n    set_idt_gate(2, (u32)isr2);\n    set_idt_gate(3, (u32)isr3);\n    set_idt_gate(4, (u32)isr4);\n    set_idt_gate(5, (u32)isr5);\n    set_idt_gate(6, (u32)isr6);\n    set_idt_gate(7, (u32)isr7);\n    set_idt_gate(8, (u32)isr8);\n    set_idt_gate(9, (u32)isr9);\n    set_idt_gate(10, (u32)isr10);\n    set_idt_gate(11, (u32)isr11);\n    set_idt_gate(12, (u32)isr12);\n    set_idt_gate(13, (u32)isr13);\n    set_idt_gate(14, (u32)isr14);\n    set_idt_gate(15, (u32)isr15);\n    set_idt_gate(16, (u32)isr16);\n    set_idt_gate(17, (u32)isr17);\n    set_idt_gate(18, (u32)isr18);\n    set_idt_gate(19, (u32)isr19);\n    set_idt_gate(20, (u32)isr20);\n    set_idt_gate(21, (u32)isr21);\n    set_idt_gate(22, (u32)isr22);\n    set_idt_gate(23, (u32)isr23);\n    set_idt_gate(24, (u32)isr24);\n    set_idt_gate(25, (u32)isr25);\n    set_idt_gate(26, (u32)isr26);\n    set_idt_gate(27, (u32)isr27);\n    set_idt_gate(28, (u32)isr28);\n    set_idt_gate(29, (u32)isr29);\n    set_idt_gate(30, (u32)isr30);\n    set_idt_gate(31, (u32)isr31);\n\n    // Remap the PIC\n    port_byte_out(0x20, 0x11);\n    port_byte_out(0xA0, 0x11);\n    port_byte_out(0x21, 0x20);\n    port_byte_out(0xA1, 0x28);\n    port_byte_out(0x21, 0x04);\n    port_byte_out(0xA1, 0x02);\n    port_byte_out(0x21, 0x01);\n    port_byte_out(0xA1, 0x01);\n    port_byte_out(0x21, 0x0);\n    port_byte_out(0xA1, 0x0); \n\n    // Install the IRQs\n    set_idt_gate(32, (u32)irq0);\n    set_idt_gate(33, (u32)irq1);\n    set_idt_gate(34, (u32)irq2);\n    set_idt_gate(35, (u32)irq3);\n    set_idt_gate(36, (u32)irq4);\n    set_idt_gate(37, (u32)irq5);\n    set_idt_gate(38, (u32)irq6);\n    set_idt_gate(39, (u32)irq7);\n    set_idt_gate(40, (u32)irq8);\n    set_idt_gate(41, (u32)irq9);\n    set_idt_gate(42, (u32)irq10);\n    set_idt_gate(43, (u32)irq11);\n    set_idt_gate(44, (u32)irq12);\n    set_idt_gate(45, (u32)irq13);\n    set_idt_gate(46, (u32)irq14);\n    set_idt_gate(47, (u32)irq15);\n\n    set_idt(); // Load with ASM\n}\n\n/* To print the message which defines every exception */\nchar *exception_messages[] = {\n    \"Division By Zero\",\n    \"Debug\",\n    \"Non Maskable Interrupt\",\n    \"Breakpoint\",\n    \"Into Detected Overflow\",\n    \"Out of Bounds\",\n    \"Invalid Opcode\",\n    \"No Coprocessor\",\n\n    \"Double Fault\",\n    \"Coprocessor Segment Overrun\",\n    \"Bad TSS\",\n    \"Segment Not Present\",\n    \"Stack Fault\",\n    \"General Protection Fault\",\n    \"Page Fault\",\n    \"Unknown Interrupt\",\n\n    \"Coprocessor Fault\",\n    \"Alignment Check\",\n    \"Machine Check\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\"\n};\n\nvoid isr_handler(registers_t r) {\n    kprint(\"received interrupt: \");\n    char s[3];\n    int_to_ascii(r.int_no, s);\n    kprint(s);\n    kprint(\"\\n\");\n    kprint(exception_messages[r.int_no]);\n    kprint(\"\\n\");\n}\n\nvoid register_interrupt_handler(u8 n, isr_t handler) {\n    interrupt_handlers[n] = handler;\n}\n\nvoid irq_handler(registers_t r) {\n    /* After every interrupt we need to send an EOI to the PICs\n     * or they will not send another interrupt again */\n    if (r.int_no >= 40) port_byte_out(0xA0, 0x20); /* slave */\n    port_byte_out(0x20, 0x20); /* master */\n\n    /* Handle the interrupt in a more modular way */\n    if (interrupt_handlers[r.int_no] != 0) {\n        isr_t handler = interrupt_handlers[r.int_no];\n        handler(r);\n    }\n}\n\nvoid irq_install() {\n    /* Enable interruptions */\n    asm volatile(\"sti\");\n    /* IRQ0: timer */\n    init_timer(50);\n    /* IRQ1: keyboard */\n    init_keyboard();\n}\n"
  },
  {
    "path": "22-malloc/cpu/isr.h",
    "content": "#ifndef ISR_H\n#define ISR_H\n\n#include \"type.h\"\n\n/* ISRs reserved for CPU exceptions */\nextern void isr0();\nextern void isr1();\nextern void isr2();\nextern void isr3();\nextern void isr4();\nextern void isr5();\nextern void isr6();\nextern void isr7();\nextern void isr8();\nextern void isr9();\nextern void isr10();\nextern void isr11();\nextern void isr12();\nextern void isr13();\nextern void isr14();\nextern void isr15();\nextern void isr16();\nextern void isr17();\nextern void isr18();\nextern void isr19();\nextern void isr20();\nextern void isr21();\nextern void isr22();\nextern void isr23();\nextern void isr24();\nextern void isr25();\nextern void isr26();\nextern void isr27();\nextern void isr28();\nextern void isr29();\nextern void isr30();\nextern void isr31();\n/* IRQ definitions */\nextern void irq0();\nextern void irq1();\nextern void irq2();\nextern void irq3();\nextern void irq4();\nextern void irq5();\nextern void irq6();\nextern void irq7();\nextern void irq8();\nextern void irq9();\nextern void irq10();\nextern void irq11();\nextern void irq12();\nextern void irq13();\nextern void irq14();\nextern void irq15();\n\n#define IRQ0 32\n#define IRQ1 33\n#define IRQ2 34\n#define IRQ3 35\n#define IRQ4 36\n#define IRQ5 37\n#define IRQ6 38\n#define IRQ7 39\n#define IRQ8 40\n#define IRQ9 41\n#define IRQ10 42\n#define IRQ11 43\n#define IRQ12 44\n#define IRQ13 45\n#define IRQ14 46\n#define IRQ15 47\n\n/* Struct which aggregates many registers */\ntypedef struct {\n   u32 ds; /* Data segment selector */\n   u32 edi, esi, ebp, esp, ebx, edx, ecx, eax; /* Pushed by pusha. */\n   u32 int_no, err_code; /* Interrupt number and error code (if applicable) */\n   u32 eip, cs, eflags, useresp, ss; /* Pushed by the processor automatically */\n} registers_t;\n\nvoid isr_install();\nvoid isr_handler(registers_t r);\nvoid irq_install();\n\ntypedef void (*isr_t)(registers_t);\nvoid register_interrupt_handler(u8 n, isr_t handler);\n\n#endif\n"
  },
  {
    "path": "22-malloc/cpu/ports.c",
    "content": "#include \"ports.h\"\n\n/**\n * Read a byte from the specified port\n */\nu8 port_byte_in (u16 port) {\n    u8 result;\n    /* Inline assembler syntax\n     * !! Notice how the source and destination registers are switched from NASM !!\n     *\n     * '\"=a\" (result)'; set '=' the C variable '(result)' to the value of register e'a'x\n     * '\"d\" (port)': map the C variable '(port)' into e'd'x register\n     *\n     * Inputs and outputs are separated by colons\n     */\n    __asm__(\"in %%dx, %%al\" : \"=a\" (result) : \"d\" (port));\n    return result;\n}\n\nvoid port_byte_out (u16 port, u8 data) {\n    /* Notice how here both registers are mapped to C variables and\n     * nothing is returned, thus, no equals '=' in the asm syntax \n     * However we see a comma since there are two variables in the input area\n     * and none in the 'return' area\n     */\n    __asm__ __volatile__(\"out %%al, %%dx\" : : \"a\" (data), \"d\" (port));\n}\n\nu16 port_word_in (u16 port) {\n    u16 result;\n    __asm__(\"in %%dx, %%ax\" : \"=a\" (result) : \"d\" (port));\n    return result;\n}\n\nvoid port_word_out (u16 port, u16 data) {\n    __asm__ __volatile__(\"out %%ax, %%dx\" : : \"a\" (data), \"d\" (port));\n}\n"
  },
  {
    "path": "22-malloc/cpu/ports.h",
    "content": "#ifndef PORTS_H\n#define PORTS_H\n\n#include \"../cpu/type.h\"\n\nunsigned char port_byte_in (u16 port);\nvoid port_byte_out (u16 port, u8 data);\nunsigned short port_word_in (u16 port);\nvoid port_word_out (u16 port, u16 data);\n\n#endif\n"
  },
  {
    "path": "22-malloc/cpu/timer.c",
    "content": "#include \"timer.h\"\n#include \"isr.h\"\n#include \"ports.h\"\n#include \"../libc/function.h\"\n\nu32 tick = 0;\n\nstatic void timer_callback(registers_t regs) {\n    tick++;\n    UNUSED(regs);\n}\n\nvoid init_timer(u32 freq) {\n    /* Install the function we just wrote */\n    register_interrupt_handler(IRQ0, timer_callback);\n\n    /* Get the PIT value: hardware clock at 1193180 Hz */\n    u32 divisor = 1193180 / freq;\n    u8 low  = (u8)(divisor & 0xFF);\n    u8 high = (u8)( (divisor >> 8) & 0xFF);\n    /* Send the command */\n    port_byte_out(0x43, 0x36); /* Command port */\n    port_byte_out(0x40, low);\n    port_byte_out(0x40, high);\n}\n\n"
  },
  {
    "path": "22-malloc/cpu/timer.h",
    "content": "#ifndef TIMER_H\n#define TIMER_H\n\n#include \"type.h\"\n\nvoid init_timer(u32 freq);\n\n#endif\n"
  },
  {
    "path": "22-malloc/cpu/type.h",
    "content": "#ifndef TYPE_H\n#define TYPE_H\n\n/* Instead of using 'chars' to allocate non-character bytes,\n * we will use these new type with no semantic meaning */\ntypedef unsigned int   u32;\ntypedef          int   s32;\ntypedef unsigned short u16;\ntypedef          short s16;\ntypedef unsigned char  u8;\ntypedef          char  s8;\n\n#define low_16(address) (u16)((address) & 0xFFFF)\n#define high_16(address) (u16)(((address) >> 16) & 0xFFFF)\n\n#endif\n"
  },
  {
    "path": "22-malloc/drivers/keyboard.c",
    "content": "#include \"keyboard.h\"\n#include \"../cpu/ports.h\"\n#include \"../cpu/isr.h\"\n#include \"screen.h\"\n#include \"../libc/string.h\"\n#include \"../libc/function.h\"\n#include \"../kernel/kernel.h\"\n\n#define BACKSPACE 0x0E\n#define ENTER 0x1C\n\nstatic char key_buffer[256];\n\n#define SC_MAX 57\nconst char *sc_name[] = { \"ERROR\", \"Esc\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \n    \"7\", \"8\", \"9\", \"0\", \"-\", \"=\", \"Backspace\", \"Tab\", \"Q\", \"W\", \"E\", \n        \"R\", \"T\", \"Y\", \"U\", \"I\", \"O\", \"P\", \"[\", \"]\", \"Enter\", \"Lctrl\", \n        \"A\", \"S\", \"D\", \"F\", \"G\", \"H\", \"J\", \"K\", \"L\", \";\", \"'\", \"`\", \n        \"LShift\", \"\\\\\", \"Z\", \"X\", \"C\", \"V\", \"B\", \"N\", \"M\", \",\", \".\", \n        \"/\", \"RShift\", \"Keypad *\", \"LAlt\", \"Spacebar\"};\nconst char sc_ascii[] = { '?', '?', '1', '2', '3', '4', '5', '6',     \n    '7', '8', '9', '0', '-', '=', '?', '?', 'Q', 'W', 'E', 'R', 'T', 'Y', \n        'U', 'I', 'O', 'P', '[', ']', '?', '?', 'A', 'S', 'D', 'F', 'G', \n        'H', 'J', 'K', 'L', ';', '\\'', '`', '?', '\\\\', 'Z', 'X', 'C', 'V', \n        'B', 'N', 'M', ',', '.', '/', '?', '?', '?', ' '};\n\nstatic void keyboard_callback(registers_t regs) {\n    /* The PIC leaves us the scancode in port 0x60 */\n    u8 scancode = port_byte_in(0x60);\n    \n    if (scancode > SC_MAX) return;\n    if (scancode == BACKSPACE) {\n        backspace(key_buffer);\n        kprint_backspace();\n    } else if (scancode == ENTER) {\n        kprint(\"\\n\");\n        user_input(key_buffer); /* kernel-controlled function */\n        key_buffer[0] = '\\0';\n    } else {\n        char letter = sc_ascii[(int)scancode];\n        /* Remember that kprint only accepts char[] */\n        char str[2] = {letter, '\\0'};\n        append(key_buffer, letter);\n        kprint(str);\n    }\n    UNUSED(regs);\n}\n\nvoid init_keyboard() {\n   register_interrupt_handler(IRQ1, keyboard_callback); \n}\n"
  },
  {
    "path": "22-malloc/drivers/keyboard.h",
    "content": "#include \"../cpu/type.h\"\n\nvoid init_keyboard();\n"
  },
  {
    "path": "22-malloc/drivers/screen.c",
    "content": "#include \"screen.h\"\n#include \"../cpu/ports.h\"\n#include \"../libc/mem.h\"\n\n/* Declaration of private functions */\nint get_cursor_offset();\nvoid set_cursor_offset(int offset);\nint print_char(char c, int col, int row, char attr);\nint get_offset(int col, int row);\nint get_offset_row(int offset);\nint get_offset_col(int offset);\n\n/**********************************************************\n * Public Kernel API functions                            *\n **********************************************************/\n\n/**\n * Print a message on the specified location\n * If col, row, are negative, we will use the current offset\n */\nvoid kprint_at(char *message, int col, int row) {\n    /* Set cursor if col/row are negative */\n    int offset;\n    if (col >= 0 && row >= 0)\n        offset = get_offset(col, row);\n    else {\n        offset = get_cursor_offset();\n        row = get_offset_row(offset);\n        col = get_offset_col(offset);\n    }\n\n    /* Loop through message and print it */\n    int i = 0;\n    while (message[i] != 0) {\n        offset = print_char(message[i++], col, row, WHITE_ON_BLACK);\n        /* Compute row/col for next iteration */\n        row = get_offset_row(offset);\n        col = get_offset_col(offset);\n    }\n}\n\nvoid kprint(char *message) {\n    kprint_at(message, -1, -1);\n}\n\nvoid kprint_backspace() {\n    int offset = get_cursor_offset()-2;\n    int row = get_offset_row(offset);\n    int col = get_offset_col(offset);\n    print_char(0x08, col, row, WHITE_ON_BLACK);\n}\n\n\n/**********************************************************\n * Private kernel functions                               *\n **********************************************************/\n\n\n/**\n * Innermost print function for our kernel, directly accesses the video memory \n *\n * If 'col' and 'row' are negative, we will print at current cursor location\n * If 'attr' is zero it will use 'white on black' as default\n * Returns the offset of the next character\n * Sets the video cursor to the returned offset\n */\nint print_char(char c, int col, int row, char attr) {\n    u8 *vidmem = (u8*) VIDEO_ADDRESS;\n    if (!attr) attr = WHITE_ON_BLACK;\n\n    /* Error control: print a red 'E' if the coords aren't right */\n    if (col >= MAX_COLS || row >= MAX_ROWS) {\n        vidmem[2*(MAX_COLS)*(MAX_ROWS)-2] = 'E';\n        vidmem[2*(MAX_COLS)*(MAX_ROWS)-1] = RED_ON_WHITE;\n        return get_offset(col, row);\n    }\n\n    int offset;\n    if (col >= 0 && row >= 0) offset = get_offset(col, row);\n    else offset = get_cursor_offset();\n\n    if (c == '\\n') {\n        row = get_offset_row(offset);\n        offset = get_offset(0, row+1);\n    } else if (c == 0x08) { /* Backspace */\n        vidmem[offset] = ' ';\n        vidmem[offset+1] = attr;\n    } else {\n        vidmem[offset] = c;\n        vidmem[offset+1] = attr;\n        offset += 2;\n    }\n\n    /* Check if the offset is over screen size and scroll */\n    if (offset >= MAX_ROWS * MAX_COLS * 2) {\n        int i;\n        for (i = 1; i < MAX_ROWS; i++) \n            memory_copy((u8*)(get_offset(0, i) + VIDEO_ADDRESS),\n                        (u8*)(get_offset(0, i-1) + VIDEO_ADDRESS),\n                        MAX_COLS * 2);\n\n        /* Blank last line */\n        char *last_line = (char*) (get_offset(0, MAX_ROWS-1) + (u8*) VIDEO_ADDRESS);\n        for (i = 0; i < MAX_COLS * 2; i++) last_line[i] = 0;\n\n        offset -= 2 * MAX_COLS;\n    }\n\n    set_cursor_offset(offset);\n    return offset;\n}\n\nint get_cursor_offset() {\n    /* Use the VGA ports to get the current cursor position\n     * 1. Ask for high byte of the cursor offset (data 14)\n     * 2. Ask for low byte (data 15)\n     */\n    port_byte_out(REG_SCREEN_CTRL, 14);\n    int offset = port_byte_in(REG_SCREEN_DATA) << 8; /* High byte: << 8 */\n    port_byte_out(REG_SCREEN_CTRL, 15);\n    offset += port_byte_in(REG_SCREEN_DATA);\n    return offset * 2; /* Position * size of character cell */\n}\n\nvoid set_cursor_offset(int offset) {\n    /* Similar to get_cursor_offset, but instead of reading we write data */\n    offset /= 2;\n    port_byte_out(REG_SCREEN_CTRL, 14);\n    port_byte_out(REG_SCREEN_DATA, (u8)(offset >> 8));\n    port_byte_out(REG_SCREEN_CTRL, 15);\n    port_byte_out(REG_SCREEN_DATA, (u8)(offset & 0xff));\n}\n\nvoid clear_screen() {\n    int screen_size = MAX_COLS * MAX_ROWS;\n    int i;\n    u8 *screen = (u8*) VIDEO_ADDRESS;\n\n    for (i = 0; i < screen_size; i++) {\n        screen[i*2] = ' ';\n        screen[i*2+1] = WHITE_ON_BLACK;\n    }\n    set_cursor_offset(get_offset(0, 0));\n}\n\n\nint get_offset(int col, int row) { return 2 * (row * MAX_COLS + col); }\nint get_offset_row(int offset) { return offset / (2 * MAX_COLS); }\nint get_offset_col(int offset) { return (offset - (get_offset_row(offset)*2*MAX_COLS))/2; }\n"
  },
  {
    "path": "22-malloc/drivers/screen.h",
    "content": "#ifndef SCREEN_H\n#define SCREEN_H\n\n#include \"../cpu/type.h\"\n\n#define VIDEO_ADDRESS 0xb8000\n#define MAX_ROWS 25\n#define MAX_COLS 80\n#define WHITE_ON_BLACK 0x0f\n#define RED_ON_WHITE 0xf4\n\n/* Screen i/o ports */\n#define REG_SCREEN_CTRL 0x3d4\n#define REG_SCREEN_DATA 0x3d5\n\n/* Public kernel API */\nvoid clear_screen();\nvoid kprint_at(char *message, int col, int row);\nvoid kprint(char *message);\nvoid kprint_backspace();\n\n#endif\n"
  },
  {
    "path": "22-malloc/kernel/kernel.c",
    "content": "#include \"../cpu/isr.h\"\n#include \"../drivers/screen.h\"\n#include \"kernel.h\"\n#include \"../libc/string.h\"\n#include \"../libc/mem.h\"\n\nvoid main() {\n    isr_install();\n    irq_install();\n\n    kprint(\"Type something, it will go through the kernel\\n\"\n        \"Type END to halt the CPU or PAGE to request a kmalloc()\\n> \");\n}\n\nvoid user_input(char *input) {\n    if (strcmp(input, \"END\") == 0) {\n        kprint(\"Stopping the CPU. Bye!\\n\");\n        asm volatile(\"hlt\");\n    } else if (strcmp(input, \"PAGE\") == 0) {\n        /* Lesson 22: Code to test kmalloc, the rest is unchanged */\n        u32 phys_addr;\n        u32 page = kmalloc(1000, 1, &phys_addr);\n        char page_str[16] = \"\";\n        hex_to_ascii(page, page_str);\n        char phys_str[16] = \"\";\n        hex_to_ascii(phys_addr, phys_str);\n        kprint(\"Page: \");\n        kprint(page_str);\n        kprint(\", physical address: \");\n        kprint(phys_str);\n        kprint(\"\\n\");\n    }\n    kprint(\"You said: \");\n    kprint(input);\n    kprint(\"\\n> \");\n}\n"
  },
  {
    "path": "22-malloc/kernel/kernel.h",
    "content": "#ifndef KERNEL_H\n#define KERNEL_H\n\nvoid user_input(char *input);\n\n#endif\n"
  },
  {
    "path": "22-malloc/libc/function.h",
    "content": "#ifndef FUNCTION_H\n#define FUNCTION_H\n\n/* Sometimes we want to keep parameters to a function for later use\n * and this is a solution to avoid the 'unused parameter' compiler warning */\n#define UNUSED(x) (void)(x)\n\n#endif\n"
  },
  {
    "path": "22-malloc/libc/mem.c",
    "content": "#include \"mem.h\"\n\nvoid memory_copy(u8 *source, u8 *dest, int nbytes) {\n    int i;\n    for (i = 0; i < nbytes; i++) {\n        *(dest + i) = *(source + i);\n    }\n}\n\nvoid memory_set(u8 *dest, u8 val, u32 len) {\n    u8 *temp = (u8 *)dest;\n    for ( ; len != 0; len--) *temp++ = val;\n}\n\n/* This should be computed at link time, but a hardcoded\n * value is fine for now. Remember that our kernel starts\n * at 0x1000 as defined on the Makefile */\nu32 free_mem_addr = 0x10000;\n/* Implementation is just a pointer to some free memory which\n * keeps growing */\nu32 kmalloc(u32 size, int align, u32 *phys_addr) {\n    /* Pages are aligned to 4K, or 0x1000 */\n    if (align == 1 && (free_mem_addr & 0xFFFFF000)) {\n        free_mem_addr &= 0xFFFFF000;\n        free_mem_addr += 0x1000;\n    }\n    /* Save also the physical address */\n    if (phys_addr) *phys_addr = free_mem_addr;\n\n    u32 ret = free_mem_addr;\n    free_mem_addr += size; /* Remember to increment the pointer */\n    return ret;\n}\n"
  },
  {
    "path": "22-malloc/libc/mem.h",
    "content": "#ifndef MEM_H\n#define MEM_H\n\n#include \"../cpu/type.h\"\n\nvoid memory_copy(u8 *source, u8 *dest, int nbytes);\nvoid memory_set(u8 *dest, u8 val, u32 len);\n\n/* At this stage there is no 'free' implemented. */\nu32 kmalloc(u32 size, int align, u32 *phys_addr);\n\n#endif\n"
  },
  {
    "path": "22-malloc/libc/string.c",
    "content": "#include \"string.h\"\n#include \"../cpu/type.h\"\n\n/**\n * K&R implementation\n */\nvoid int_to_ascii(int n, char str[]) {\n    int i, sign;\n    if ((sign = n) < 0) n = -n;\n    i = 0;\n    do {\n        str[i++] = n % 10 + '0';\n    } while ((n /= 10) > 0);\n\n    if (sign < 0) str[i++] = '-';\n    str[i] = '\\0';\n\n    reverse(str);\n}\n\nvoid hex_to_ascii(int n, char str[]) {\n    append(str, '0');\n    append(str, 'x');\n    char zeros = 0;\n\n    s32 tmp;\n    int i;\n    for (i = 28; i > 0; i -= 4) {\n        tmp = (n >> i) & 0xF;\n        if (tmp == 0 && zeros == 0) continue;\n        zeros = 1;\n        if (tmp > 0xA) append(str, tmp - 0xA + 'a');\n        else append(str, tmp + '0');\n    }\n\n    tmp = n & 0xF;\n    if (tmp >= 0xA) append(str, tmp - 0xA + 'a');\n    else append(str, tmp + '0');\n}\n\n/* K&R */\nvoid reverse(char s[]) {\n    int c, i, j;\n    for (i = 0, j = strlen(s)-1; i < j; i++, j--) {\n        c = s[i];\n        s[i] = s[j];\n        s[j] = c;\n    }\n}\n\n/* K&R */\nint strlen(char s[]) {\n    int i = 0;\n    while (s[i] != '\\0') ++i;\n    return i;\n}\n\nvoid append(char s[], char n) {\n    int len = strlen(s);\n    s[len] = n;\n    s[len+1] = '\\0';\n}\n\nvoid backspace(char s[]) {\n    int len = strlen(s);\n    s[len-1] = '\\0';\n}\n\n/* K&R \n * Returns <0 if s1<s2, 0 if s1==s2, >0 if s1>s2 */\nint strcmp(char s1[], char s2[]) {\n    int i;\n    for (i = 0; s1[i] == s2[i]; i++) {\n        if (s1[i] == '\\0') return 0;\n    }\n    return s1[i] - s2[i];\n}\n"
  },
  {
    "path": "22-malloc/libc/string.h",
    "content": "#ifndef STRINGS_H\n#define STRINGS_H\n\nvoid int_to_ascii(int n, char str[]);\nvoid hex_to_ascii(int n, char str[]);\nvoid reverse(char s[]);\nint strlen(char s[]);\nvoid backspace(char s[]);\nvoid append(char s[], char n);\nint strcmp(char s1[], char s2[]);\n\n#endif\n"
  },
  {
    "path": "23-fixes/Makefile",
    "content": "C_SOURCES = $(wildcard kernel/*.c drivers/*.c cpu/*.c libc/*.c)\nHEADERS = $(wildcard kernel/*.h drivers/*.h cpu/*.h libc/*.h)\n# Nice syntax for file extension replacement\nOBJ = ${C_SOURCES:.c=.o cpu/interrupt.o} \n\n# Change this if your cross-compiler is somewhere else\nCC = /usr/local/i386elfgcc/bin/i386-elf-gcc\nGDB = /usr/local/i386elfgcc/bin/i386-elf-gdb\n# -g: Use debugging symbols in gcc\nCFLAGS = -g -ffreestanding -Wall -Wextra -fno-exceptions -m32\n\n# First rule is run by default\nos-image.bin: boot/bootsect.bin kernel.bin\n\tcat $^ > os-image.bin\n\n# '--oformat binary' deletes all symbols as a collateral, so we don't need\n# to 'strip' them manually on this case\nkernel.bin: boot/kernel_entry.o ${OBJ}\n\ti386-elf-ld -o $@ -Ttext 0x1000 $^ --oformat binary\n\n# Used for debugging purposes\nkernel.elf: boot/kernel_entry.o ${OBJ}\n\ti386-elf-ld -o $@ -Ttext 0x1000 $^ \n\nrun: os-image.bin\n\tqemu-system-i386 -fda os-image.bin\n\n# Open the connection to qemu and load our kernel-object file with symbols\ndebug: os-image.bin kernel.elf\n\tqemu-system-i386 -s -fda os-image.bin -d guest_errors,int &\n\t${GDB} -ex \"target remote localhost:1234\" -ex \"symbol-file kernel.elf\"\n\n# Generic rules for wildcards\n# To make an object, always compile from its .c\n%.o: %.c ${HEADERS}\n\t${CC} ${CFLAGS} -c $< -o $@\n\n%.o: %.asm\n\tnasm $< -f elf -o $@\n\n%.bin: %.asm\n\tnasm $< -f bin -o $@\n\nclean:\n\trm -rf *.bin *.dis *.o os-image.bin *.elf\n\trm -rf kernel/*.o boot/*.bin drivers/*.o boot/*.o cpu/*.o libc/*.o\n"
  },
  {
    "path": "23-fixes/README.md",
    "content": "*Concepts you may want to Google beforehand: freestanding, uint32_t, size_t*\n\n**Goal: Fix miscellaneous issues with our code**\n\nThe OSDev wiki has a section [which describes some issues with\nJamesM's tutorial](http://wiki.osdev.org/James_Molloy%27s_Tutorial_Known_Bugs).\nSince we followed his tutorial for lessons 18-22 (interrupts through malloc), we'll\nneed to make sure we fix any of the issues before moving on.\n\n1. Wrong CFLAGS\n---------------\n\nWe add  `-ffreestanding` when compiling `.o` files, which includes `kernel_entry.o` and thus\n`kernel.bin` and `os-image.bin`.\n\nBefore, we disabled libgcc (not libc) through the use of `-nostdlib` and we didn't re-enable\nit for linking. Since this is tricky, we'll delete `-nostdlib`\n\n`-nostdinc` was also passed to gcc, but we will need it for step 3, so let's delete it.\n\n\n2. kernel.c `main()` function\n-----------------------------\n\nModify `kernel/kernel.c` and change `main()` to `kernel_main()` since gcc recognizes \"main\" as \na special keyword and we don't want to mess with that.\n\nChange `boot/kernel_entry.asm` to point to the new name accordingly.\n\nTo fix the `i386-elf-ld: warning: cannot find entry symbol _start; defaulting to 0000000000001000`\nwarning message, add a `global _start;` and define the `_start:` label in `boot/kernel_entry.asm`.\n\n\n3. Reinvented datatypes\n-----------------------\n\nIt looks like it was a bad idea to define non-standard data types like `u32` and such, since\nC99 introduces standard fixed-width data types like `uint32_t`\n\nWe need to include `<stdint.h>` which works even in `-ffreestanding` (but requires stdlibs)\nand use those data types instead of our own, then delete them on `type.h`\n\nWe also delete the underscores around `__asm__` and `__volatile__` since they aren't needed.\n\n\n4. Improperly aligned `kmalloc`\n-------------------------------\n\nFirst, since `kmalloc` uses a size parameter, we'll use the correct data type `size_t` instead\nof `u32int_t`. `size_t` should be used for all parameters which \"count\" stuff and cannot be\nnegative. Include `<stddef.h>`. \n\nWe will fix our `kmalloc` in the future, making it a proper memory manager and aligning data types.\nFor now, it will always return a new page-aligned memory block.\n\n\n5. Missing functions\n--------------------\n\nWe will implement the missing `mem*` functions in following lessons\n\n\n6. Interrupt handlers\n---------------------\n`cli` is redundant, since we already established on the IDT entries if interrupts\nare enabled within a handler using the `idt_gate_t` flags.\n\n`sti` is also redundant, as `iret` loads the eflags value from the stack, which contains a \nbit telling whether interrupts are on or off.\nIn other words the interrupt handler automatically restores interrupts whether or not \ninterrupts were enabled before this interrupt\n\nOn `cpu/isr.h`, `struct registers_t` has a couple issues. \nFirst, the alleged `esp` is renamed to `useless`.\nThe value is useless because it has to do with the current stack context, not what was interrupted.\nThen, we rename `useresp` to `esp`\n\nWe add `cld` just before `call isr_handler` on `cpu/interrupt.asm` as suggested\nby the osdev wiki.\n\nThere is a final, important issue with `cpu/interrupt.asm`. The common stubs create an instance\nof `struct registers` on the stack and then call the C handler. But that breaks the ABI, since\nthe stack belongs to the called function and they may change them as they please. It is needed\nto pass the struct as a pointer.\n\nTo achieve this, edit `cpu/isr.h` and `cpu/isr.c` and change `registers_t r` into `registers_t *t`,\nthen, instead of accessing the fields of the struct via `.`, access the fields of the pointer via `->`.\nFinally, in `cpu/interrupt.asm`, and add a `push esp` before calling both `isr_handler` and\n`irq_handler` -- remember to also `pop eax` to clear the pointer afterwards.\n\nBoth current callbacks, the timer and the keyboard, also need to be changed to use a pointer to\n`registers_t`.\n"
  },
  {
    "path": "23-fixes/boot/32bit_print.asm",
    "content": "[bits 32] ; using 32-bit protected mode\n\n; this is how constants are defined\nVIDEO_MEMORY equ 0xb8000\nWHITE_OB_BLACK equ 0x0f ; the color byte for each character\n\nprint_string_pm:\n    pusha\n    mov edx, VIDEO_MEMORY\n\nprint_string_pm_loop:\n    mov al, [ebx] ; [ebx] is the address of our character\n    mov ah, WHITE_OB_BLACK\n\n    cmp al, 0 ; check if end of string\n    je print_string_pm_done\n\n    mov [edx], ax ; store character + attribute in video memory\n    add ebx, 1 ; next char\n    add edx, 2 ; next video memory position\n\n    jmp print_string_pm_loop\n\nprint_string_pm_done:\n    popa\n    ret\n"
  },
  {
    "path": "23-fixes/boot/bootsect.asm",
    "content": "; Identical to lesson 13's boot sector, but the %included files have new paths\n[org 0x7c00]\nKERNEL_OFFSET equ 0x1000 ; The same one we used when linking the kernel\n\n    mov [BOOT_DRIVE], dl ; Remember that the BIOS sets us the boot drive in 'dl' on boot\n    mov bp, 0x9000\n    mov sp, bp\n\n    mov bx, MSG_REAL_MODE \n    call print\n    call print_nl\n\n    call load_kernel ; read the kernel from disk\n    call switch_to_pm ; disable interrupts, load GDT,  etc. Finally jumps to 'BEGIN_PM'\n    jmp $ ; Never executed\n\n%include \"boot/print.asm\"\n%include \"boot/print_hex.asm\"\n%include \"boot/disk.asm\"\n%include \"boot/gdt.asm\"\n%include \"boot/32bit_print.asm\"\n%include \"boot/switch_pm.asm\"\n\n[bits 16]\nload_kernel:\n    mov bx, MSG_LOAD_KERNEL\n    call print\n    call print_nl\n\n    mov bx, KERNEL_OFFSET ; Read from disk and store in 0x1000\n    mov dh, 31 ; Our future kernel will be larger, make this big\n    mov dl, [BOOT_DRIVE]\n    call disk_load\n    ret\n\n[bits 32]\nBEGIN_PM:\n    mov ebx, MSG_PROT_MODE\n    call print_string_pm\n    call KERNEL_OFFSET ; Give control to the kernel\n    jmp $ ; Stay here when the kernel returns control to us (if ever)\n\n\nBOOT_DRIVE db 0 ; It is a good idea to store it in memory because 'dl' may get overwritten\nMSG_REAL_MODE db \"Started in 16-bit Real Mode\", 0\nMSG_PROT_MODE db \"Landed in 32-bit Protected Mode\", 0\nMSG_LOAD_KERNEL db \"Loading kernel into memory\", 0\nMSG_RETURNED_KERNEL db \"Returned from kernel. Error?\", 0\n\n; padding\ntimes 510 - ($-$$) db 0\ndw 0xaa55\n"
  },
  {
    "path": "23-fixes/boot/disk.asm",
    "content": "; load 'dh' sectors from drive 'dl' into ES:BX\ndisk_load:\n    pusha\n    ; reading from disk requires setting specific values in all registers\n    ; so we will overwrite our input parameters from 'dx'. Let's save it\n    ; to the stack for later use.\n    push dx\n\n    mov ah, 0x02 ; ah <- int 0x13 function. 0x02 = 'read'\n    mov al, dh   ; al <- number of sectors to read (0x01 .. 0x80)\n    mov cl, 0x02 ; cl <- sector (0x01 .. 0x11)\n                 ; 0x01 is our boot sector, 0x02 is the first 'available' sector\n    mov ch, 0x00 ; ch <- cylinder (0x0 .. 0x3FF, upper 2 bits in 'cl')\n    ; dl <- drive number. Our caller sets it as a parameter and gets it from BIOS\n    ; (0 = floppy, 1 = floppy2, 0x80 = hdd, 0x81 = hdd2)\n    mov dh, 0x00 ; dh <- head number (0x0 .. 0xF)\n\n    ; [es:bx] <- pointer to buffer where the data will be stored\n    ; caller sets it up for us, and it is actually the standard location for int 13h\n    int 0x13      ; BIOS interrupt\n    jc disk_error ; if error (stored in the carry bit)\n\n    pop dx\n    cmp al, dh    ; BIOS also sets 'al' to the # of sectors read. Compare it.\n    jne sectors_error\n    popa\n    ret\n\n\ndisk_error:\n    mov bx, DISK_ERROR\n    call print\n    call print_nl\n    mov dh, ah ; ah = error code, dl = disk drive that dropped the error\n    call print_hex ; check out the code at http://stanislavs.org/helppc/int_13-1.html\n    jmp disk_loop\n\nsectors_error:\n    mov bx, SECTORS_ERROR\n    call print\n\ndisk_loop:\n    jmp $\n\nDISK_ERROR: db \"Disk read error\", 0\nSECTORS_ERROR: db \"Incorrect number of sectors read\", 0\n"
  },
  {
    "path": "23-fixes/boot/gdt.asm",
    "content": "gdt_start: ; don't remove the labels, they're needed to compute sizes and jumps\n    ; the GDT starts with a null 8-byte\n    dd 0x0 ; 4 byte\n    dd 0x0 ; 4 byte\n\n; GDT for code segment. base = 0x00000000, length = 0xfffff\n; for flags, refer to os-dev.pdf document, page 36\ngdt_code: \n    dw 0xffff    ; segment length, bits 0-15\n    dw 0x0       ; segment base, bits 0-15\n    db 0x0       ; segment base, bits 16-23\n    db 10011010b ; flags (8 bits)\n    db 11001111b ; flags (4 bits) + segment length, bits 16-19\n    db 0x0       ; segment base, bits 24-31\n\n; GDT for data segment. base and length identical to code segment\n; some flags changed, again, refer to os-dev.pdf\ngdt_data:\n    dw 0xffff\n    dw 0x0\n    db 0x0\n    db 10010010b\n    db 11001111b\n    db 0x0\n\ngdt_end:\n\n; GDT descriptor\ngdt_descriptor:\n    dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size\n    dd gdt_start ; address (32 bit)\n\n; define some constants for later use\nCODE_SEG equ gdt_code - gdt_start\nDATA_SEG equ gdt_data - gdt_start\n"
  },
  {
    "path": "23-fixes/boot/kernel_entry.asm",
    "content": "global _start;\n[bits 32]\n\n_start:\n    [extern kernel_main] ; Define calling point. Must have same name as kernel.c 'main' function\n    call kernel_main ; Calls the C function. The linker will know where it is placed in memory\n    jmp $\n"
  },
  {
    "path": "23-fixes/boot/print.asm",
    "content": "print:\n    pusha\n\n; keep this in mind:\n; while (string[i] != 0) { print string[i]; i++ }\n\n; the comparison for string end (null byte)\nstart:\n    mov al, [bx] ; 'bx' is the base address for the string\n    cmp al, 0 \n    je done\n\n    ; the part where we print with the BIOS help\n    mov ah, 0x0e\n    int 0x10 ; 'al' already contains the char\n\n    ; increment pointer and do next loop\n    add bx, 1\n    jmp start\n\ndone:\n    popa\n    ret\n\n\n\nprint_nl:\n    pusha\n    \n    mov ah, 0x0e\n    mov al, 0x0a ; newline char\n    int 0x10\n    mov al, 0x0d ; carriage return\n    int 0x10\n    \n    popa\n    ret\n"
  },
  {
    "path": "23-fixes/boot/print_hex.asm",
    "content": "; receiving the data in 'dx'\n; For the examples we'll assume that we're called with dx=0x1234\nprint_hex:\n    pusha\n\n    mov cx, 0 ; our index variable\n\n; Strategy: get the last char of 'dx', then convert to ASCII\n; Numeric ASCII values: '0' (ASCII 0x30) to '9' (0x39), so just add 0x30 to byte N.\n; For alphabetic characters A-F: 'A' (ASCII 0x41) to 'F' (0x46) we'll add 0x40\n; Then, move the ASCII byte to the correct position on the resulting string\nhex_loop:\n    cmp cx, 4 ; loop 4 times\n    je end\n    \n    ; 1. convert last char of 'dx' to ascii\n    mov ax, dx ; we will use 'ax' as our working register\n    and ax, 0x000f ; 0x1234 -> 0x0004 by masking first three to zeros\n    add al, 0x30 ; add 0x30 to N to convert it to ASCII \"N\"\n    cmp al, 0x39 ; if > 9, add extra 8 to represent 'A' to 'F'\n    jle step2\n    add al, 7 ; 'A' is ASCII 65 instead of 58, so 65-58=7\n\nstep2:\n    ; 2. get the correct position of the string to place our ASCII char\n    ; bx <- base address + string length - index of char\n    mov bx, HEX_OUT + 5 ; base + length\n    sub bx, cx  ; our index variable\n    mov [bx], al ; copy the ASCII char on 'al' to the position pointed by 'bx'\n    ror dx, 4 ; 0x1234 -> 0x4123 -> 0x3412 -> 0x2341 -> 0x1234\n\n    ; increment index and loop\n    add cx, 1\n    jmp hex_loop\n\nend:\n    ; prepare the parameter and call the function\n    ; remember that print receives parameters in 'bx'\n    mov bx, HEX_OUT\n    call print\n\n    popa\n    ret\n\nHEX_OUT:\n    db '0x0000',0 ; reserve memory for our new string\n"
  },
  {
    "path": "23-fixes/boot/switch_pm.asm",
    "content": "[bits 16]\nswitch_to_pm:\n    cli ; 1. disable interrupts\n    lgdt [gdt_descriptor] ; 2. load the GDT descriptor\n    mov eax, cr0\n    or eax, 0x1 ; 3. set 32-bit mode bit in cr0\n    mov cr0, eax\n    jmp CODE_SEG:init_pm ; 4. far jump by using a different segment\n\n[bits 32]\ninit_pm: ; we are now using 32-bit instructions\n    mov ax, DATA_SEG ; 5. update the segment registers\n    mov ds, ax\n    mov ss, ax\n    mov es, ax\n    mov fs, ax\n    mov gs, ax\n\n    mov ebp, 0x90000 ; 6. update the stack right at the top of the free space\n    mov esp, ebp\n\n    call BEGIN_PM ; 7. Call a well-known label with useful code\n"
  },
  {
    "path": "23-fixes/cpu/idt.c",
    "content": "#include \"idt.h\"\n#include \"type.h\"\n\nvoid set_idt_gate(int n, uint32_t handler) {\n    idt[n].low_offset = low_16(handler);\n    idt[n].sel = KERNEL_CS;\n    idt[n].always0 = 0;\n    idt[n].flags = 0x8E; \n    idt[n].high_offset = high_16(handler);\n}\n\nvoid set_idt() {\n    idt_reg.base = (uint32_t) &idt;\n    idt_reg.limit = IDT_ENTRIES * sizeof(idt_gate_t) - 1;\n    /* Don't make the mistake of loading &idt -- always load &idt_reg */\n    asm volatile(\"lidtl (%0)\" : : \"r\" (&idt_reg));\n}\n"
  },
  {
    "path": "23-fixes/cpu/idt.h",
    "content": "#ifndef IDT_H\n#define IDT_H\n\n#include <stdint.h>\n\n/* Segment selectors */\n#define KERNEL_CS 0x08\n\n/* How every interrupt gate (handler) is defined */\ntypedef struct {\n    uint16_t low_offset; /* Lower 16 bits of handler function address */\n    uint16_t sel; /* Kernel segment selector */\n    uint8_t always0;\n    /* First byte\n     * Bit 7: \"Interrupt is present\"\n     * Bits 6-5: Privilege level of caller (0=kernel..3=user)\n     * Bit 4: Set to 0 for interrupt gates\n     * Bits 3-0: bits 1110 = decimal 14 = \"32 bit interrupt gate\" */\n    uint8_t flags; \n    uint16_t high_offset; /* Higher 16 bits of handler function address */\n} __attribute__((packed)) idt_gate_t ;\n\n/* A pointer to the array of interrupt handlers.\n * Assembly instruction 'lidt' will read it */\ntypedef struct {\n    uint16_t limit;\n    uint32_t base;\n} __attribute__((packed)) idt_register_t;\n\n#define IDT_ENTRIES 256\nidt_gate_t idt[IDT_ENTRIES];\nidt_register_t idt_reg;\n\n\n/* Functions implemented in idt.c */\nvoid set_idt_gate(int n, uint32_t handler);\nvoid set_idt();\n\n#endif\n"
  },
  {
    "path": "23-fixes/cpu/interrupt.asm",
    "content": "; Defined in isr.c\n[extern isr_handler]\n[extern irq_handler]\n\n; Common ISR code\nisr_common_stub:\n    ; 1. Save CPU state\n\tpusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax\n\tmov ax, ds ; Lower 16-bits of eax = ds.\n\tpush eax ; save the data segment descriptor\n\tmov ax, 0x10  ; kernel data segment descriptor\n\tmov ds, ax\n\tmov es, ax\n\tmov fs, ax\n\tmov gs, ax\n\tpush esp ; registers_t *r\n    ; 2. Call C handler\n    cld ; C code following the sysV ABI requires DF to be clear on function entry\n\tcall isr_handler\n\t\n    ; 3. Restore state\n\tpop eax \n    pop eax\n\tmov ds, ax\n\tmov es, ax\n\tmov fs, ax\n\tmov gs, ax\n\tpopa\n\tadd esp, 8 ; Cleans up the pushed error code and pushed ISR number\n\tiret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP\n\n; Common IRQ code. Identical to ISR code except for the 'call' \n; and the 'pop ebx'\nirq_common_stub:\n    pusha \n    mov ax, ds\n    push eax\n    mov ax, 0x10\n    mov ds, ax\n    mov es, ax\n    mov fs, ax\n    mov gs, ax\n    push esp\n    cld\n    call irq_handler ; Different than the ISR code\n    pop ebx  ; Different than the ISR code\n    pop ebx\n    mov ds, bx\n    mov es, bx\n    mov fs, bx\n    mov gs, bx\n    popa\n    add esp, 8\n    iret \n\t\n; We don't get information about which interrupt was caller\n; when the handler is run, so we will need to have a different handler\n; for every interrupt.\n; Furthermore, some interrupts push an error code onto the stack but others\n; don't, so we will push a dummy error code for those which don't, so that\n; we have a consistent stack for all of them.\n\n; First make the ISRs global\nglobal isr0\nglobal isr1\nglobal isr2\nglobal isr3\nglobal isr4\nglobal isr5\nglobal isr6\nglobal isr7\nglobal isr8\nglobal isr9\nglobal isr10\nglobal isr11\nglobal isr12\nglobal isr13\nglobal isr14\nglobal isr15\nglobal isr16\nglobal isr17\nglobal isr18\nglobal isr19\nglobal isr20\nglobal isr21\nglobal isr22\nglobal isr23\nglobal isr24\nglobal isr25\nglobal isr26\nglobal isr27\nglobal isr28\nglobal isr29\nglobal isr30\nglobal isr31\n; IRQs\nglobal irq0\nglobal irq1\nglobal irq2\nglobal irq3\nglobal irq4\nglobal irq5\nglobal irq6\nglobal irq7\nglobal irq8\nglobal irq9\nglobal irq10\nglobal irq11\nglobal irq12\nglobal irq13\nglobal irq14\nglobal irq15\n\n; 0: Divide By Zero Exception\nisr0:\n    push byte 0\n    push byte 0\n    jmp isr_common_stub\n\n; 1: Debug Exception\nisr1:\n    push byte 0\n    push byte 1\n    jmp isr_common_stub\n\n; 2: Non Maskable Interrupt Exception\nisr2:\n    push byte 0\n    push byte 2\n    jmp isr_common_stub\n\n; 3: Int 3 Exception\nisr3:\n    push byte 0\n    push byte 3\n    jmp isr_common_stub\n\n; 4: INTO Exception\nisr4:\n    push byte 0\n    push byte 4\n    jmp isr_common_stub\n\n; 5: Out of Bounds Exception\nisr5:\n    push byte 0\n    push byte 5\n    jmp isr_common_stub\n\n; 6: Invalid Opcode Exception\nisr6:\n    push byte 0\n    push byte 6\n    jmp isr_common_stub\n\n; 7: Coprocessor Not Available Exception\nisr7:\n    push byte 0\n    push byte 7\n    jmp isr_common_stub\n\n; 8: Double Fault Exception (With Error Code!)\nisr8:\n    push byte 8\n    jmp isr_common_stub\n\n; 9: Coprocessor Segment Overrun Exception\nisr9:\n    push byte 0\n    push byte 9\n    jmp isr_common_stub\n\n; 10: Bad TSS Exception (With Error Code!)\nisr10:\n    push byte 10\n    jmp isr_common_stub\n\n; 11: Segment Not Present Exception (With Error Code!)\nisr11:\n    push byte 11\n    jmp isr_common_stub\n\n; 12: Stack Fault Exception (With Error Code!)\nisr12:\n    push byte 12\n    jmp isr_common_stub\n\n; 13: General Protection Fault Exception (With Error Code!)\nisr13:\n    push byte 13\n    jmp isr_common_stub\n\n; 14: Page Fault Exception (With Error Code!)\nisr14:\n    push byte 14\n    jmp isr_common_stub\n\n; 15: Reserved Exception\nisr15:\n    push byte 0\n    push byte 15\n    jmp isr_common_stub\n\n; 16: Floating Point Exception\nisr16:\n    push byte 0\n    push byte 16\n    jmp isr_common_stub\n\n; 17: Alignment Check Exception\nisr17:\n    push byte 0\n    push byte 17\n    jmp isr_common_stub\n\n; 18: Machine Check Exception\nisr18:\n    push byte 0\n    push byte 18\n    jmp isr_common_stub\n\n; 19: Reserved\nisr19:\n    push byte 0\n    push byte 19\n    jmp isr_common_stub\n\n; 20: Reserved\nisr20:\n    push byte 0\n    push byte 20\n    jmp isr_common_stub\n\n; 21: Reserved\nisr21:\n    push byte 0\n    push byte 21\n    jmp isr_common_stub\n\n; 22: Reserved\nisr22:\n    push byte 0\n    push byte 22\n    jmp isr_common_stub\n\n; 23: Reserved\nisr23:\n    push byte 0\n    push byte 23\n    jmp isr_common_stub\n\n; 24: Reserved\nisr24:\n    push byte 0\n    push byte 24\n    jmp isr_common_stub\n\n; 25: Reserved\nisr25:\n    push byte 0\n    push byte 25\n    jmp isr_common_stub\n\n; 26: Reserved\nisr26:\n    push byte 0\n    push byte 26\n    jmp isr_common_stub\n\n; 27: Reserved\nisr27:\n    push byte 0\n    push byte 27\n    jmp isr_common_stub\n\n; 28: Reserved\nisr28:\n    push byte 0\n    push byte 28\n    jmp isr_common_stub\n\n; 29: Reserved\nisr29:\n    push byte 0\n    push byte 29\n    jmp isr_common_stub\n\n; 30: Reserved\nisr30:\n    push byte 0\n    push byte 30\n    jmp isr_common_stub\n\n; 31: Reserved\nisr31:\n    push byte 0\n    push byte 31\n    jmp isr_common_stub\n\n; IRQ handlers\nirq0:\n\tpush byte 0\n\tpush byte 32\n\tjmp irq_common_stub\n\nirq1:\n\tpush byte 1\n\tpush byte 33\n\tjmp irq_common_stub\n\nirq2:\n\tpush byte 2\n\tpush byte 34\n\tjmp irq_common_stub\n\nirq3:\n\tpush byte 3\n\tpush byte 35\n\tjmp irq_common_stub\n\nirq4:\n\tpush byte 4\n\tpush byte 36\n\tjmp irq_common_stub\n\nirq5:\n\tpush byte 5\n\tpush byte 37\n\tjmp irq_common_stub\n\nirq6:\n\tpush byte 6\n\tpush byte 38\n\tjmp irq_common_stub\n\nirq7:\n\tpush byte 7\n\tpush byte 39\n\tjmp irq_common_stub\n\nirq8:\n\tpush byte 8\n\tpush byte 40\n\tjmp irq_common_stub\n\nirq9:\n\tpush byte 9\n\tpush byte 41\n\tjmp irq_common_stub\n\nirq10:\n\tpush byte 10\n\tpush byte 42\n\tjmp irq_common_stub\n\nirq11:\n\tpush byte 11\n\tpush byte 43\n\tjmp irq_common_stub\n\nirq12:\n\tpush byte 12\n\tpush byte 44\n\tjmp irq_common_stub\n\nirq13:\n\tpush byte 13\n\tpush byte 45\n\tjmp irq_common_stub\n\nirq14:\n\tpush byte 14\n\tpush byte 46\n\tjmp irq_common_stub\n\nirq15:\n\tpush byte 15\n\tpush byte 47\n\tjmp irq_common_stub\n\n"
  },
  {
    "path": "23-fixes/cpu/isr.c",
    "content": "#include \"isr.h\"\n#include \"idt.h\"\n#include \"../drivers/screen.h\"\n#include \"../drivers/keyboard.h\"\n#include \"../libc/string.h\"\n#include \"timer.h\"\n#include \"ports.h\"\n\nisr_t interrupt_handlers[256];\n\n/* Can't do this with a loop because we need the address\n * of the function names */\nvoid isr_install() {\n    set_idt_gate(0, (uint32_t)isr0);\n    set_idt_gate(1, (uint32_t)isr1);\n    set_idt_gate(2, (uint32_t)isr2);\n    set_idt_gate(3, (uint32_t)isr3);\n    set_idt_gate(4, (uint32_t)isr4);\n    set_idt_gate(5, (uint32_t)isr5);\n    set_idt_gate(6, (uint32_t)isr6);\n    set_idt_gate(7, (uint32_t)isr7);\n    set_idt_gate(8, (uint32_t)isr8);\n    set_idt_gate(9, (uint32_t)isr9);\n    set_idt_gate(10, (uint32_t)isr10);\n    set_idt_gate(11, (uint32_t)isr11);\n    set_idt_gate(12, (uint32_t)isr12);\n    set_idt_gate(13, (uint32_t)isr13);\n    set_idt_gate(14, (uint32_t)isr14);\n    set_idt_gate(15, (uint32_t)isr15);\n    set_idt_gate(16, (uint32_t)isr16);\n    set_idt_gate(17, (uint32_t)isr17);\n    set_idt_gate(18, (uint32_t)isr18);\n    set_idt_gate(19, (uint32_t)isr19);\n    set_idt_gate(20, (uint32_t)isr20);\n    set_idt_gate(21, (uint32_t)isr21);\n    set_idt_gate(22, (uint32_t)isr22);\n    set_idt_gate(23, (uint32_t)isr23);\n    set_idt_gate(24, (uint32_t)isr24);\n    set_idt_gate(25, (uint32_t)isr25);\n    set_idt_gate(26, (uint32_t)isr26);\n    set_idt_gate(27, (uint32_t)isr27);\n    set_idt_gate(28, (uint32_t)isr28);\n    set_idt_gate(29, (uint32_t)isr29);\n    set_idt_gate(30, (uint32_t)isr30);\n    set_idt_gate(31, (uint32_t)isr31);\n\n    // Remap the PIC\n    port_byte_out(0x20, 0x11);\n    port_byte_out(0xA0, 0x11);\n    port_byte_out(0x21, 0x20);\n    port_byte_out(0xA1, 0x28);\n    port_byte_out(0x21, 0x04);\n    port_byte_out(0xA1, 0x02);\n    port_byte_out(0x21, 0x01);\n    port_byte_out(0xA1, 0x01);\n    port_byte_out(0x21, 0x0);\n    port_byte_out(0xA1, 0x0); \n\n    // Install the IRQs\n    set_idt_gate(32, (uint32_t)irq0);\n    set_idt_gate(33, (uint32_t)irq1);\n    set_idt_gate(34, (uint32_t)irq2);\n    set_idt_gate(35, (uint32_t)irq3);\n    set_idt_gate(36, (uint32_t)irq4);\n    set_idt_gate(37, (uint32_t)irq5);\n    set_idt_gate(38, (uint32_t)irq6);\n    set_idt_gate(39, (uint32_t)irq7);\n    set_idt_gate(40, (uint32_t)irq8);\n    set_idt_gate(41, (uint32_t)irq9);\n    set_idt_gate(42, (uint32_t)irq10);\n    set_idt_gate(43, (uint32_t)irq11);\n    set_idt_gate(44, (uint32_t)irq12);\n    set_idt_gate(45, (uint32_t)irq13);\n    set_idt_gate(46, (uint32_t)irq14);\n    set_idt_gate(47, (uint32_t)irq15);\n\n    set_idt(); // Load with ASM\n}\n\n/* To print the message which defines every exception */\nchar *exception_messages[] = {\n    \"Division By Zero\",\n    \"Debug\",\n    \"Non Maskable Interrupt\",\n    \"Breakpoint\",\n    \"Into Detected Overflow\",\n    \"Out of Bounds\",\n    \"Invalid Opcode\",\n    \"No Coprocessor\",\n\n    \"Double Fault\",\n    \"Coprocessor Segment Overrun\",\n    \"Bad TSS\",\n    \"Segment Not Present\",\n    \"Stack Fault\",\n    \"General Protection Fault\",\n    \"Page Fault\",\n    \"Unknown Interrupt\",\n\n    \"Coprocessor Fault\",\n    \"Alignment Check\",\n    \"Machine Check\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\"\n};\n\nvoid isr_handler(registers_t *r) {\n    kprint(\"received interrupt: \");\n    char s[3];\n    int_to_ascii(r->int_no, s);\n    kprint(s);\n    kprint(\"\\n\");\n    kprint(exception_messages[r->int_no]);\n    kprint(\"\\n\");\n}\n\nvoid register_interrupt_handler(uint8_t n, isr_t handler) {\n    interrupt_handlers[n] = handler;\n}\n\nvoid irq_handler(registers_t *r) {\n    /* After every interrupt we need to send an EOI to the PICs\n     * or they will not send another interrupt again */\n    if (r->int_no >= 40) port_byte_out(0xA0, 0x20); /* slave */\n    port_byte_out(0x20, 0x20); /* master */\n\n    /* Handle the interrupt in a more modular way */\n    if (interrupt_handlers[r->int_no] != 0) {\n        isr_t handler = interrupt_handlers[r->int_no];\n        handler(r);\n    }\n}\n\nvoid irq_install() {\n    /* Enable interruptions */\n    asm volatile(\"sti\");\n    /* IRQ0: timer */\n    init_timer(50);\n    /* IRQ1: keyboard */\n    init_keyboard();\n}\n"
  },
  {
    "path": "23-fixes/cpu/isr.h",
    "content": "#ifndef ISR_H\n#define ISR_H\n\n#include <stdint.h>\n\n/* ISRs reserved for CPU exceptions */\nextern void isr0();\nextern void isr1();\nextern void isr2();\nextern void isr3();\nextern void isr4();\nextern void isr5();\nextern void isr6();\nextern void isr7();\nextern void isr8();\nextern void isr9();\nextern void isr10();\nextern void isr11();\nextern void isr12();\nextern void isr13();\nextern void isr14();\nextern void isr15();\nextern void isr16();\nextern void isr17();\nextern void isr18();\nextern void isr19();\nextern void isr20();\nextern void isr21();\nextern void isr22();\nextern void isr23();\nextern void isr24();\nextern void isr25();\nextern void isr26();\nextern void isr27();\nextern void isr28();\nextern void isr29();\nextern void isr30();\nextern void isr31();\n/* IRQ definitions */\nextern void irq0();\nextern void irq1();\nextern void irq2();\nextern void irq3();\nextern void irq4();\nextern void irq5();\nextern void irq6();\nextern void irq7();\nextern void irq8();\nextern void irq9();\nextern void irq10();\nextern void irq11();\nextern void irq12();\nextern void irq13();\nextern void irq14();\nextern void irq15();\n\n#define IRQ0 32\n#define IRQ1 33\n#define IRQ2 34\n#define IRQ3 35\n#define IRQ4 36\n#define IRQ5 37\n#define IRQ6 38\n#define IRQ7 39\n#define IRQ8 40\n#define IRQ9 41\n#define IRQ10 42\n#define IRQ11 43\n#define IRQ12 44\n#define IRQ13 45\n#define IRQ14 46\n#define IRQ15 47\n\n/* Struct which aggregates many registers.\n * It matches exactly the pushes on interrupt.asm. From the bottom:\n * - Pushed by the processor automatically\n * - `push byte`s on the isr-specific code: error code, then int number\n * - All the registers by pusha\n * - `push eax` whose lower 16-bits contain DS\n */\ntypedef struct {\n   uint32_t ds; /* Data segment selector */\n   uint32_t edi, esi, ebp, useless, ebx, edx, ecx, eax; /* Pushed by pusha. */\n   uint32_t int_no, err_code; /* Interrupt number and error code (if applicable) */\n   uint32_t eip, cs, eflags, esp, ss; /* Pushed by the processor automatically */\n} registers_t;\n\nvoid isr_install();\nvoid isr_handler(registers_t *r);\nvoid irq_install();\n\ntypedef void (*isr_t)(registers_t*);\nvoid register_interrupt_handler(uint8_t n, isr_t handler);\n\n#endif\n"
  },
  {
    "path": "23-fixes/cpu/ports.c",
    "content": "#include \"ports.h\"\n\n/**\n * Read a byte from the specified port\n */\nuint8_t port_byte_in (uint16_t port) {\n    uint8_t result;\n    /* Inline assembler syntax\n     * !! Notice how the source and destination registers are switched from NASM !!\n     *\n     * '\"=a\" (result)'; set '=' the C variable '(result)' to the value of register e'a'x\n     * '\"d\" (port)': map the C variable '(port)' into e'd'x register\n     *\n     * Inputs and outputs are separated by colons\n     */\n    asm(\"in %%dx, %%al\" : \"=a\" (result) : \"d\" (port));\n    return result;\n}\n\nvoid port_byte_out (uint16_t port, uint8_t data) {\n    /* Notice how here both registers are mapped to C variables and\n     * nothing is returned, thus, no equals '=' in the asm syntax \n     * However we see a comma since there are two variables in the input area\n     * and none in the 'return' area\n     */\n    asm volatile(\"out %%al, %%dx\" : : \"a\" (data), \"d\" (port));\n}\n\nuint16_t port_word_in (uint16_t port) {\n    uint16_t result;\n    asm(\"in %%dx, %%ax\" : \"=a\" (result) : \"d\" (port));\n    return result;\n}\n\nvoid port_word_out (uint16_t port, uint16_t data) {\n    asm volatile(\"out %%ax, %%dx\" : : \"a\" (data), \"d\" (port));\n}\n"
  },
  {
    "path": "23-fixes/cpu/ports.h",
    "content": "#ifndef PORTS_H\n#define PORTS_H\n\n#include <stdint.h>\n\nunsigned char port_byte_in (uint16_t port);\nvoid port_byte_out (uint16_t port, uint8_t data);\nunsigned short port_word_in (uint16_t port);\nvoid port_word_out (uint16_t port, uint16_t data);\n\n#endif\n"
  },
  {
    "path": "23-fixes/cpu/timer.c",
    "content": "#include \"timer.h\"\n#include \"isr.h\"\n#include \"ports.h\"\n#include \"../libc/function.h\"\n\nuint32_t tick = 0;\n\nstatic void timer_callback(registers_t *regs) {\n    tick++;\n    UNUSED(regs);\n}\n\nvoid init_timer(uint32_t freq) {\n    /* Install the function we just wrote */\n    register_interrupt_handler(IRQ0, timer_callback);\n\n    /* Get the PIT value: hardware clock at 1193180 Hz */\n    uint32_t divisor = 1193180 / freq;\n    uint8_t low  = (uint8_t)(divisor & 0xFF);\n    uint8_t high = (uint8_t)( (divisor >> 8) & 0xFF);\n    /* Send the command */\n    port_byte_out(0x43, 0x36); /* Command port */\n    port_byte_out(0x40, low);\n    port_byte_out(0x40, high);\n}\n\n"
  },
  {
    "path": "23-fixes/cpu/timer.h",
    "content": "#ifndef TIMER_H\n#define TIMER_H\n\n#include <stdint.h>\n\nvoid init_timer(uint32_t freq);\n\n#endif\n"
  },
  {
    "path": "23-fixes/cpu/type.h",
    "content": "#ifndef TYPE_H\n#define TYPE_H\n\n#include <stdint.h>\n\n#define low_16(address) (uint16_t)((address) & 0xFFFF)\n#define high_16(address) (uint16_t)(((address) >> 16) & 0xFFFF)\n\n#endif\n"
  },
  {
    "path": "23-fixes/drivers/keyboard.c",
    "content": "#include \"keyboard.h\"\n#include \"../cpu/ports.h\"\n#include \"../cpu/isr.h\"\n#include \"screen.h\"\n#include \"../libc/string.h\"\n#include \"../libc/function.h\"\n#include \"../kernel/kernel.h\"\n#include <stdint.h>\n\n#define BACKSPACE 0x0E\n#define ENTER 0x1C\n\nstatic char key_buffer[256];\n\n#define SC_MAX 57\nconst char *sc_name[] = { \"ERROR\", \"Esc\", \"1\", \"2\", \"3\", \"4\", \"5\", \"6\", \n    \"7\", \"8\", \"9\", \"0\", \"-\", \"=\", \"Backspace\", \"Tab\", \"Q\", \"W\", \"E\", \n        \"R\", \"T\", \"Y\", \"U\", \"I\", \"O\", \"P\", \"[\", \"]\", \"Enter\", \"Lctrl\", \n        \"A\", \"S\", \"D\", \"F\", \"G\", \"H\", \"J\", \"K\", \"L\", \";\", \"'\", \"`\", \n        \"LShift\", \"\\\\\", \"Z\", \"X\", \"C\", \"V\", \"B\", \"N\", \"M\", \",\", \".\", \n        \"/\", \"RShift\", \"Keypad *\", \"LAlt\", \"Spacebar\"};\nconst char sc_ascii[] = { '?', '?', '1', '2', '3', '4', '5', '6',     \n    '7', '8', '9', '0', '-', '=', '?', '?', 'Q', 'W', 'E', 'R', 'T', 'Y', \n        'U', 'I', 'O', 'P', '[', ']', '?', '?', 'A', 'S', 'D', 'F', 'G', \n        'H', 'J', 'K', 'L', ';', '\\'', '`', '?', '\\\\', 'Z', 'X', 'C', 'V', \n        'B', 'N', 'M', ',', '.', '/', '?', '?', '?', ' '};\n\nstatic void keyboard_callback(registers_t *regs) {\n    /* The PIC leaves us the scancode in port 0x60 */\n    uint8_t scancode = port_byte_in(0x60);\n    \n    if (scancode > SC_MAX) return;\n    if (scancode == BACKSPACE) {\n        backspace(key_buffer);\n        kprint_backspace();\n    } else if (scancode == ENTER) {\n        kprint(\"\\n\");\n        user_input(key_buffer); /* kernel-controlled function */\n        key_buffer[0] = '\\0';\n    } else {\n        char letter = sc_ascii[(int)scancode];\n        /* Remember that kprint only accepts char[] */\n        char str[2] = {letter, '\\0'};\n        append(key_buffer, letter);\n        kprint(str);\n    }\n    UNUSED(regs);\n}\n\nvoid init_keyboard() {\n   register_interrupt_handler(IRQ1, keyboard_callback); \n}\n"
  },
  {
    "path": "23-fixes/drivers/keyboard.h",
    "content": "void init_keyboard();\n"
  },
  {
    "path": "23-fixes/drivers/screen.c",
    "content": "#include \"screen.h\"\n#include \"../cpu/ports.h\"\n#include \"../libc/mem.h\"\n#include <stdint.h>\n\n/* Declaration of private functions */\nint get_cursor_offset();\nvoid set_cursor_offset(int offset);\nint print_char(char c, int col, int row, char attr);\nint get_offset(int col, int row);\nint get_offset_row(int offset);\nint get_offset_col(int offset);\n\n/**********************************************************\n * Public Kernel API functions                            *\n **********************************************************/\n\n/**\n * Print a message on the specified location\n * If col, row, are negative, we will use the current offset\n */\nvoid kprint_at(char *message, int col, int row) {\n    /* Set cursor if col/row are negative */\n    int offset;\n    if (col >= 0 && row >= 0)\n        offset = get_offset(col, row);\n    else {\n        offset = get_cursor_offset();\n        row = get_offset_row(offset);\n        col = get_offset_col(offset);\n    }\n\n    /* Loop through message and print it */\n    int i = 0;\n    while (message[i] != 0) {\n        offset = print_char(message[i++], col, row, WHITE_ON_BLACK);\n        /* Compute row/col for next iteration */\n        row = get_offset_row(offset);\n        col = get_offset_col(offset);\n    }\n}\n\nvoid kprint(char *message) {\n    kprint_at(message, -1, -1);\n}\n\nvoid kprint_backspace() {\n    int offset = get_cursor_offset()-2;\n    int row = get_offset_row(offset);\n    int col = get_offset_col(offset);\n    print_char(0x08, col, row, WHITE_ON_BLACK);\n}\n\n\n/**********************************************************\n * Private kernel functions                               *\n **********************************************************/\n\n\n/**\n * Innermost print function for our kernel, directly accesses the video memory \n *\n * If 'col' and 'row' are negative, we will print at current cursor location\n * If 'attr' is zero it will use 'white on black' as default\n * Returns the offset of the next character\n * Sets the video cursor to the returned offset\n */\nint print_char(char c, int col, int row, char attr) {\n    uint8_t *vidmem = (uint8_t*) VIDEO_ADDRESS;\n    if (!attr) attr = WHITE_ON_BLACK;\n\n    /* Error control: print a red 'E' if the coords aren't right */\n    if (col >= MAX_COLS || row >= MAX_ROWS) {\n        vidmem[2*(MAX_COLS)*(MAX_ROWS)-2] = 'E';\n        vidmem[2*(MAX_COLS)*(MAX_ROWS)-1] = RED_ON_WHITE;\n        return get_offset(col, row);\n    }\n\n    int offset;\n    if (col >= 0 && row >= 0) offset = get_offset(col, row);\n    else offset = get_cursor_offset();\n\n    if (c == '\\n') {\n        row = get_offset_row(offset);\n        offset = get_offset(0, row+1);\n    } else if (c == 0x08) { /* Backspace */\n        vidmem[offset] = ' ';\n        vidmem[offset+1] = attr;\n    } else {\n        vidmem[offset] = c;\n        vidmem[offset+1] = attr;\n        offset += 2;\n    }\n\n    /* Check if the offset is over screen size and scroll */\n    if (offset >= MAX_ROWS * MAX_COLS * 2) {\n        int i;\n        for (i = 1; i < MAX_ROWS; i++) \n            memory_copy((uint8_t*)(get_offset(0, i) + VIDEO_ADDRESS),\n                        (uint8_t*)(get_offset(0, i-1) + VIDEO_ADDRESS),\n                        MAX_COLS * 2);\n\n        /* Blank last line */\n        char *last_line = (char*) (get_offset(0, MAX_ROWS-1) + (uint8_t*) VIDEO_ADDRESS);\n        for (i = 0; i < MAX_COLS * 2; i++) last_line[i] = 0;\n\n        offset -= 2 * MAX_COLS;\n    }\n\n    set_cursor_offset(offset);\n    return offset;\n}\n\nint get_cursor_offset() {\n    /* Use the VGA ports to get the current cursor position\n     * 1. Ask for high byte of the cursor offset (data 14)\n     * 2. Ask for low byte (data 15)\n     */\n    port_byte_out(REG_SCREEN_CTRL, 14);\n    int offset = port_byte_in(REG_SCREEN_DATA) << 8; /* High byte: << 8 */\n    port_byte_out(REG_SCREEN_CTRL, 15);\n    offset += port_byte_in(REG_SCREEN_DATA);\n    return offset * 2; /* Position * size of character cell */\n}\n\nvoid set_cursor_offset(int offset) {\n    /* Similar to get_cursor_offset, but instead of reading we write data */\n    offset /= 2;\n    port_byte_out(REG_SCREEN_CTRL, 14);\n    port_byte_out(REG_SCREEN_DATA, (uint8_t)(offset >> 8));\n    port_byte_out(REG_SCREEN_CTRL, 15);\n    port_byte_out(REG_SCREEN_DATA, (uint8_t)(offset & 0xff));\n}\n\nvoid clear_screen() {\n    int screen_size = MAX_COLS * MAX_ROWS;\n    int i;\n    uint8_t *screen = (uint8_t*) VIDEO_ADDRESS;\n\n    for (i = 0; i < screen_size; i++) {\n        screen[i*2] = ' ';\n        screen[i*2+1] = WHITE_ON_BLACK;\n    }\n    set_cursor_offset(get_offset(0, 0));\n}\n\n\nint get_offset(int col, int row) { return 2 * (row * MAX_COLS + col); }\nint get_offset_row(int offset) { return offset / (2 * MAX_COLS); }\nint get_offset_col(int offset) { return (offset - (get_offset_row(offset)*2*MAX_COLS))/2; }\n"
  },
  {
    "path": "23-fixes/drivers/screen.h",
    "content": "#ifndef SCREEN_H\n#define SCREEN_H\n\n#define VIDEO_ADDRESS 0xb8000\n#define MAX_ROWS 25\n#define MAX_COLS 80\n#define WHITE_ON_BLACK 0x0f\n#define RED_ON_WHITE 0xf4\n\n/* Screen i/o ports */\n#define REG_SCREEN_CTRL 0x3d4\n#define REG_SCREEN_DATA 0x3d5\n\n/* Public kernel API */\nvoid clear_screen();\nvoid kprint_at(char *message, int col, int row);\nvoid kprint(char *message);\nvoid kprint_backspace();\n\n#endif\n"
  },
  {
    "path": "23-fixes/kernel/kernel.c",
    "content": "#include \"../cpu/isr.h\"\n#include \"../drivers/screen.h\"\n#include \"kernel.h\"\n#include \"../libc/string.h\"\n#include \"../libc/mem.h\"\n#include <stdint.h>\n\nvoid kernel_main() {\n    isr_install();\n    irq_install();\n\n    asm(\"int $2\");\n    asm(\"int $3\");\n\n    kprint(\"Type something, it will go through the kernel\\n\"\n        \"Type END to halt the CPU or PAGE to request a kmalloc()\\n> \");\n}\n\nvoid user_input(char *input) {\n    if (strcmp(input, \"END\") == 0) {\n        kprint(\"Stopping the CPU. Bye!\\n\");\n        asm volatile(\"hlt\");\n    } else if (strcmp(input, \"PAGE\") == 0) {\n        /* Lesson 22: Code to test kmalloc, the rest is unchanged */\n        uint32_t phys_addr;\n        uint32_t page = kmalloc(1000, 1, &phys_addr);\n        char page_str[16] = \"\";\n        hex_to_ascii(page, page_str);\n        char phys_str[16] = \"\";\n        hex_to_ascii(phys_addr, phys_str);\n        kprint(\"Page: \");\n        kprint(page_str);\n        kprint(\", physical address: \");\n        kprint(phys_str);\n        kprint(\"\\n\");\n    }\n    kprint(\"You said: \");\n    kprint(input);\n    kprint(\"\\n> \");\n}\n"
  },
  {
    "path": "23-fixes/kernel/kernel.h",
    "content": "#ifndef KERNEL_H\n#define KERNEL_H\n\nvoid user_input(char *input);\n\n#endif\n"
  },
  {
    "path": "23-fixes/libc/function.h",
    "content": "#ifndef FUNCTION_H\n#define FUNCTION_H\n\n/* Sometimes we want to keep parameters to a function for later use\n * and this is a solution to avoid the 'unused parameter' compiler warning */\n#define UNUSED(x) (void)(x)\n\n#endif\n"
  },
  {
    "path": "23-fixes/libc/mem.c",
    "content": "#include \"mem.h\"\n\nvoid memory_copy(uint8_t *source, uint8_t *dest, int nbytes) {\n    int i;\n    for (i = 0; i < nbytes; i++) {\n        *(dest + i) = *(source + i);\n    }\n}\n\nvoid memory_set(uint8_t *dest, uint8_t val, uint32_t len) {\n    uint8_t *temp = (uint8_t *)dest;\n    for ( ; len != 0; len--) *temp++ = val;\n}\n\n/* This should be computed at link time, but a hardcoded\n * value is fine for now. Remember that our kernel starts\n * at 0x1000 as defined on the Makefile */\nuint32_t free_mem_addr = 0x10000;\n/* Implementation is just a pointer to some free memory which\n * keeps growing */\nuint32_t kmalloc(size_t size, int align, uint32_t *phys_addr) {\n    /* Pages are aligned to 4K, or 0x1000 */\n    if (align == 1 && (free_mem_addr & 0xFFFFF000)) {\n        free_mem_addr &= 0xFFFFF000;\n        free_mem_addr += 0x1000;\n    }\n    /* Save also the physical address */\n    if (phys_addr) *phys_addr = free_mem_addr;\n\n    uint32_t ret = free_mem_addr;\n    free_mem_addr += size; /* Remember to increment the pointer */\n    return ret;\n}\n"
  },
  {
    "path": "23-fixes/libc/mem.h",
    "content": "#ifndef MEM_H\n#define MEM_H\n\n#include <stdint.h>\n#include <stddef.h>\n\nvoid memory_copy(uint8_t *source, uint8_t *dest, int nbytes);\nvoid memory_set(uint8_t *dest, uint8_t val, uint32_t len);\n\n/* At this stage there is no 'free' implemented. */\nuint32_t kmalloc(size_t size, int align, uint32_t *phys_addr);\n\n#endif\n"
  },
  {
    "path": "23-fixes/libc/string.c",
    "content": "#include \"string.h\"\n#include <stdint.h>\n\n/**\n * K&R implementation\n */\nvoid int_to_ascii(int n, char str[]) {\n    int i, sign;\n    if ((sign = n) < 0) n = -n;\n    i = 0;\n    do {\n        str[i++] = n % 10 + '0';\n    } while ((n /= 10) > 0);\n\n    if (sign < 0) str[i++] = '-';\n    str[i] = '\\0';\n\n    reverse(str);\n}\n\nvoid hex_to_ascii(int n, char str[]) {\n    append(str, '0');\n    append(str, 'x');\n    char zeros = 0;\n\n    int32_t tmp;\n    int i;\n    for (i = 28; i > 0; i -= 4) {\n        tmp = (n >> i) & 0xF;\n        if (tmp == 0 && zeros == 0) continue;\n        zeros = 1;\n        if (tmp > 0xA) append(str, tmp - 0xA + 'a');\n        else append(str, tmp + '0');\n    }\n\n    tmp = n & 0xF;\n    if (tmp >= 0xA) append(str, tmp - 0xA + 'a');\n    else append(str, tmp + '0');\n}\n\n/* K&R */\nvoid reverse(char s[]) {\n    int c, i, j;\n    for (i = 0, j = strlen(s)-1; i < j; i++, j--) {\n        c = s[i];\n        s[i] = s[j];\n        s[j] = c;\n    }\n}\n\n/* K&R */\nint strlen(char s[]) {\n    int i = 0;\n    while (s[i] != '\\0') ++i;\n    return i;\n}\n\nvoid append(char s[], char n) {\n    int len = strlen(s);\n    s[len] = n;\n    s[len+1] = '\\0';\n}\n\nvoid backspace(char s[]) {\n    int len = strlen(s);\n    s[len-1] = '\\0';\n}\n\n/* K&R \n * Returns <0 if s1<s2, 0 if s1==s2, >0 if s1>s2 */\nint strcmp(char s1[], char s2[]) {\n    int i;\n    for (i = 0; s1[i] == s2[i]; i++) {\n        if (s1[i] == '\\0') return 0;\n    }\n    return s1[i] - s2[i];\n}\n"
  },
  {
    "path": "23-fixes/libc/string.h",
    "content": "#ifndef STRINGS_H\n#define STRINGS_H\n\nvoid int_to_ascii(int n, char str[]);\nvoid hex_to_ascii(int n, char str[]);\nvoid reverse(char s[]);\nint strlen(char s[]);\nvoid backspace(char s[]);\nvoid append(char s[], char n);\nint strcmp(char s1[], char s2[]);\n\n#endif\n"
  },
  {
    "path": "24-el-capitan/README.md",
    "content": "**Goal: Update our build system to El Capitan**\n\nIf you were following this guide from the beginning and upgraded to El Capitan only\nto find that Makefiles don't compile anymore, follow these instructions to upgrade\nyour cross-compiler.\n\nOtherwise, move on to the next lesson\n\nUpgrading the cross-compiler\n----------------------------\n\nWe will follow the same instructions as in lesson 11, more or less.\n\nFirst, run `brew upgrade` and you will get your gcc upgraded to version 5.0 (at the time this guide was written)\n\nThen run `xcode-select --install` to update OSX commandline tools\n\nOnce installed, find where your packaged gcc is (remember, not clang) and export it. For example:\n\n```\nexport CC=/usr/local/bin/gcc-5\nexport LD=/usr/local/bin/gcc-5\n```\n\nWe will need to recompile binutils and our cross-compiled gcc. Export the targets and prefix:\n\n```\nexport PREFIX=\"/usr/local/i386elfgcc\"\nexport TARGET=i386-elf\nexport PATH=\"$PREFIX/bin:$PATH\"\n```\n\nbinutils\n--------\n\nRemember: always be careful before pasting walls of text from the internet. I recommend copying line by line.\n\n```sh\nmkdir /tmp/src\ncd /tmp/src\ncurl -O http://ftp.gnu.org/gnu/binutils/binutils-2.24.tar.gz # If the link 404's, look for a more recent version\ntar xf binutils-2.24.tar.gz\nmkdir binutils-build\ncd binutils-build\n../binutils-2.24/configure --target=$TARGET --enable-interwork --enable-multilib --disable-nls --disable-werror --prefix=$PREFIX 2>&1 | tee configure.log\nmake all install 2>&1 | tee make.log\n```\n\n\ngcc\n---\n```sh\ncd /tmp/src\ncurl -O http://mirror.bbln.org/gcc/releases/gcc-4.9.1/gcc-4.9.1.tar.bz2\ntar xf gcc-4.9.1.tar.bz2\nmkdir gcc-build\ncd gcc-build\n../gcc-4.9.1/configure --target=$TARGET --prefix=\"$PREFIX\" --disable-nls --disable-libssp --enable-languages=c --without-headers\nmake all-gcc \nmake all-target-libgcc \nmake install-gcc \nmake install-target-libgcc \n```\n\n\nNow try to type `make` on this lesson's folder and check that everything compiles smoothly\n"
  },
  {
    "path": "LICENSE",
    "content": "BSD 3-Clause License\n\nCopyright (c) 2018, Carlos Fenollosa\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\n* Neither the name of the copyright holder nor the names of its\n  contributors may be used to endorse or promote products derived from\n  this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "os-tutorial\n===========\n\n_⚠️ Hey! This is an old, abandoned project, with both technical and design issues [listed here](https://github.com/cfenollosa/os-tutorial/issues/269). Please have fun with this tutorial but do look for more modern and authoritative sources if you want to learn about OS design. ⚠️_\n\nHow to create an OS from scratch!\n\nI have always wanted to learn how to make an OS from scratch. In college I was taught\nhow to implement advanced features (pagination, semaphores, memory management, etc)\nbut:\n\n- I never got to start from my own boot sector\n- College is hard so I don't remember most of it.\n- I'm fed up with people who think that reading an already existing kernel, even if small, is \na good idea to learn operating systems.\n\nInspired by [this document](https://web.archive.org/web/20211008041419/http://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf)\nand the [OSDev wiki](http://wiki.osdev.org/), I'll try to make short step-by-step READMEs and\ncode samples for anybody to follow. Honestly, this tutorial is basically the first document but\nsplit into smaller pieces and without the theory.\n\nUpdated: more sources: [the little book about OS development](https://littleosbook.github.io),\n[JamesM's kernel development tutorials](https://web.archive.org/web/20160412174753/http://www.jamesmolloy.co.uk/tutorial_html/index.html)\n\n\nFeatures\n--------\n\n- This course is a code tutorial aimed at people who are comfortable with low level computing. For example,\nprogrammers who have curiosity on how an OS works but don't have the time or willpower to start reading the Linux kernel\ntop to bottom.\n- There is little theory. Yes, this is a feature. Google is your theory lecturer. Once you pass college, \nexcessive theory is worse than no theory because it makes things seem more difficult than they really are.\n- The lessons are tiny and may take 5-15 minutes to complete. Trust me and trust yourself. You can do it!\n\n\nHow to use this tutorial\n------------------------\n\n1. Start with the first folder and go down in order. They build on previous code, so if \nyou jump right to folder 05 and don't know why there is a `mov ah, 0x0e`, it's because you missed lecture 02.\nReally, just go in order. You can always skip stuff you already know.\n\n2. Open the README and read the first line, which details the concepts you should be familiar with\nbefore reading the code. Google concepts you are not familiar with. The second line states the goals for each lesson. \nRead them, because they explain why we do what we do. The \"why\" is as important as the \"how\".\n \n3. Read the rest of the README. It is **very concise**.\n\n4. (Optional) Try to write the code files by yourself after reading the README.\n\n5. Look at the code examples. They are extremely well commented.\n\n6. (Optional) Experiment with them and try to break things. The only way to make sure you understood something is\ntrying to break it or replicate it with different commands.\n\n\nTL;DR: First read the README on each folder, then the code files. If you're brave, try to code them yourself.\n\n\nStrategy\n--------\n\nWe will want to do many things with our OS:\n\n- Boot from scratch, without GRUB - DONE!\n- Enter 32-bit mode - DONE\n- Jump from Assembly to C - DONE!\n- Interrupt handling - DONE!\n- Screen output and keyboard input - DONE!\n- A tiny, basic `libc` which grows to suit our needs - DONE!\n- Memory management\n- Write a filesystem to store files\n- Create a very simple shell\n- User mode\n- Maybe we will write a simple text editor\n- Multiple processes and scheduling\n\nProbably we will go through them in that order, however it's soon to tell.\n\nIf we feel brave enough:\n\n- A BASIC interpreter, like in the 70s!\n- A GUI\n- Networking\n\n\n\nContributing\n------------\n\nThis is a personal learning project, and even though it hasn't been updated for a long time, I still have hopes to get into it at some point.\n\nI'm thankful to all those who have pointed out bugs and submitted pull requests. I will need some time to review everything and I cannot guarantee that at this moment.\n\nPlease feel free to fork this repo. If many of you are interested in continuing the project, let me know and I'll link the \"main fork\" from here.\n"
  }
]