├── Makefile ├── bootlib ├── boot.ld ├── pit.s ├── boot.s ├── interrupts.s └── vga.s ├── LICENSE ├── example.s └── README /Makefile: -------------------------------------------------------------------------------- 1 | 2 | boot.elf: $(wildcard bootlib/*.s *.s) 3 | $(CC) -g -nostdlib -m32 -Wl,-Tbootlib/boot.ld -o $@ $^ 4 | 5 | .PHONY: test 6 | test: boot.elf 7 | qemu-system-x86_64 -kernel $< 8 | 9 | .PHONY: clean 10 | clean: 11 | rm -f boot.elf 12 | 13 | -------------------------------------------------------------------------------- /bootlib/boot.ld: -------------------------------------------------------------------------------- 1 | ENTRY (boot) 2 | 3 | SECTIONS{ 4 | . = 0x00100000; 5 | 6 | .text : { 7 | *(.text) 8 | } 9 | 10 | .rodata ALIGN (0x1000) : { 11 | *(.rodata) 12 | } 13 | 14 | .data ALIGN (0x1000) : { 15 | *(.data) 16 | } 17 | 18 | .bss : { 19 | bss_start = ABSOLUTE(.); 20 | *(.bss) 21 | bss_size = ABSOLUTE(.) - bss_start; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /bootlib/pit.s: -------------------------------------------------------------------------------- 1 | 2 | .global set_timer_frequency, set_timer_rate 3 | 4 | .text 5 | 6 | # Set the frequency of the timer that fires IRQ0. 7 | # Note: See http://wiki.osdev.org/PIT 8 | set_timer_frequency: # Parameters: frequency in Hz 9 | movl $0, %edx 10 | movl $1193046, %eax 11 | divl 4(%esp) 12 | pushl %eax 13 | call set_timer_rate 14 | addl $4, %esp 15 | ret 16 | 17 | # Set the rate of the timer that fires IRQ0. 18 | # Note: See http://wiki.osdev.org/PIT 19 | set_timer_rate: # Parameters: timer_rate 20 | movb $0x34, %al # channel 0, rate generator mode -> ... 21 | outb %al, $0x43 # ... PIT Mode/Command port 22 | movl 4(%esp), %eax 23 | outb %al, $0x40 # low byte -> PIT channel 0 data port 24 | movb %ah, %al # high byte -> ... 25 | outb %al, $0x40 # ... PIT channel 0 data port 26 | ret 27 | 28 | 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, 2012 - Mara Bos, Maarten de Vries 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /example.s: -------------------------------------------------------------------------------- 1 | # A simple example that uses bootlib. 2 | 3 | .global main 4 | 5 | .text 6 | main: 7 | # Set the timer frequency to 1000Hz 8 | pushl $1000 9 | call set_timer_frequency 10 | addl $4, %esp 11 | 12 | # Register the handle for the timer IRQ (IRQ0) and enable it. 13 | pushl $irq0 14 | pushl $0 15 | call set_irq_handler 16 | call enable_irq 17 | addl $8, %esp 18 | 19 | # Set up VGA stuff 20 | call color_text_mode 21 | call hide_cursor 22 | 23 | # Clear the screen 24 | movb $' ', %al 25 | movb $0x4E, %ah 26 | movl $25*80, %ecx 27 | movl $vga_memory, %edi 28 | cld 29 | rep stosw 30 | 31 | # Write some text 32 | movb $'T', vga_memory + 160*11+60 33 | movb $'i', vga_memory + 160*11+62 34 | movb $'m', vga_memory + 160*11+64 35 | movb $'e', vga_memory + 160*11+66 36 | movb $':', vga_memory + 160*11+68 37 | 38 | movb $'m', vga_memory + 160*12+84 39 | movb $'s', vga_memory + 160*12+86 40 | 41 | # Continiously show the time 42 | loop: 43 | movl time, %eax 44 | movl $vga_memory + 160*12+80, %edi 45 | movl $10, %ebx 46 | print_loop: 47 | movl $0, %edx 48 | divl %ebx 49 | addb $0x30, %dl 50 | movb %dl, (%edi) 51 | subl $2, %edi 52 | test %eax, %eax 53 | jnz print_loop 54 | jmp loop 55 | 56 | # Timer IRQ handler 57 | irq0: 58 | # increment the time and go on. 59 | incl time 60 | jmp end_of_irq0 61 | 62 | .data 63 | time: .long 0 64 | 65 | -------------------------------------------------------------------------------- /bootlib/boot.s: -------------------------------------------------------------------------------- 1 | .global boot, halt 2 | 3 | .bss 4 | 5 | .skip 0x0004000 # 16 KiB stack 6 | stack: 7 | 8 | .text 9 | 10 | .align 4 11 | multiboot_header: # this header contains some magic values to let the bootloader (grub/lilo/whatever) find our entrypoint 12 | .long 0x1BADB002 13 | .long 0x00000000 14 | .long 0xE4524FFE 15 | jmp boot 16 | 17 | 18 | # global descriptor table, see http://wiki.osdev.org/GDT 19 | gdt: 20 | .quad 0x0000000000000000 # 0 - unused 'null' section 21 | .quad 0x00CF9A000000FFFF # 8 - code section (0x00000000 - 0xFFFFFFFF, executable) 22 | .quad 0x00CF92000000FFFF # 16 - data section (0x00000000 - 0xFFFFFFFF, readable, writable) 23 | gdt_ptr: # pointer to the GDT 24 | .short (gdt_ptr - gdt) - 1 # the size of the GDT, minus one 25 | .long gdt # the location of the GDT 26 | 27 | init_gdt: 28 | lgdt gdt_ptr 29 | 30 | movw $16, %ax # load the right section (16 for the data section) ... 31 | movw %ax, %ds # ... into DS, 32 | movw %ax, %es # ... ES, 33 | movw %ax, %fs # ... FS, 34 | movw %ax, %gs # ... GS, 35 | movw %ax, %ss # ... and SS. (these are all sections except CS, the code section) 36 | 37 | ljmp $8, $init_gdt_end # load the right section (8 for the code section) into CS 38 | 39 | init_gdt_end: 40 | ret 41 | 42 | 43 | boot: 44 | 45 | # Set up our environment, gdt, idt, etc. 46 | 47 | cli # turn off interrupts, we don't want to be interrupted while setting up our environment 48 | 49 | movb $0, %al # \ 50 | movl $bss_start, %edi # | 51 | movl $bss_size, %ecx # | Clear the .BSS section. 52 | cld # | 53 | rep stosb # / 54 | 55 | movl $stack, %esp # set up the stack 56 | 57 | pushl $0 # \ clear ... 58 | popf # / ... all flags 59 | 60 | call init_gdt # load the GDT 61 | call init_idt # load the IDT 62 | call init_pic # initialize the PICs 63 | 64 | sti # end of critical section, so interrupts can be enabled again 65 | 66 | # Everything is set up, now execute the user application 67 | call main 68 | 69 | halt: 70 | cli # turn off interrupts, we don't want to be awaken from death 71 | hlt # halt the CPU 72 | jmp halt # die, again, if the CPU got out of the halt state (for example, by a NMI) 73 | 74 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Hai, 2 | 3 | The directory you just opened contains our 'bootlib' and a minimal 4 | example that uses it. 'bootlib' is a tiny library written in x86 5 | assembly which should provide you just enough functionality to let 6 | you make your own bootable program without too much trouble. 7 | 8 | If you're using some form of Linux (x86 or x86_64), you should 9 | be able to use the example makefile: 10 | This makefile lets you use 'make' to compile your bootable image, 11 | and 'make test' to run it in the Qemu emulator (if installed). 12 | 13 | This file contains some hints on how to use things such as VGA 14 | output and keyboard input. You'll probably need a lot more 15 | information than what's given here, but this should give you a 16 | good start. 17 | 18 | Have fun! 19 | 20 | Maarten de Vries and Mara Bos 21 | 22 | 23 | [Booting your image] 24 | 25 | The file that comes out of the compiler should be a Multiboot ELF 26 | image, which can be loaded by a bootloader such as GRUB. 27 | 28 | If you want to run your program natively on your computer from your 29 | harddrive, you should configure your bootloader to add an option 30 | for your boot image. For example, if you're using Ubuntu with GRUB2: 31 | Put the image in a convenient place (such as /boot/) and add the 32 | following to /etc/grub.d/40_custom: (and run: sudo update-grub2) 33 | 34 | submenu "Custom boot images" { 35 | menuentry "My Awesome Program" { 36 | multiboot /boot/myawesomeprogram 37 | } 38 | } 39 | 40 | It is also fun to try to get your program running without a 41 | bootloader. Reading your a file from a harddrive with a file system 42 | can get pretty tough in assembly, but reading your image from a 43 | fixed position on a floppy should be pretty easy. 44 | 45 | 46 | [VGA output] 47 | 48 | Code is included that will set all VGA registers to get the graphics 49 | card into 16 color 25*80 text mode. If you want a different mode, 50 | you'll have to find out the values for all those registers yourself. 51 | 52 | In text mode, each block on the screen (of 8 by 16 pixels) will match 53 | two bytes in the the VGA memory (which starts at 0xB8000, also 54 | available as the symbol 'vga_memory'): one for the character itself 55 | (in ASCII), and one for the colour (four bits background, four bits 56 | foreground). 57 | 58 | It is possible to overwrite the default font, so you can display other 59 | kind of (8*16 pixel tiled) graphics. Code for this is not included. 60 | 61 | 62 | [Keyboard input] 63 | 64 | Any input from the keyboard fires IRQ1 (if enabled). After the IRQ 65 | fired, the keyboard controller might not be ready to give you the 66 | scancode yet. You'll have to wait for the 'ready bit' to turn on: 67 | Read a byte from 0x64 (inb $0x64, %al), and repeat 68 | until the least signifigant bit is one. 69 | Then, you can read the scancode (byte) from 0x60 (inb $0x60, %al). 70 | 71 | For a overview of scancodes, see 72 | http://www.win.tue.nl/~aeb/linux/kbd/scancodes-1.html 73 | 74 | 75 | [License] 76 | 77 | See the 'LICENSE' file. 78 | -------------------------------------------------------------------------------- /bootlib/interrupts.s: -------------------------------------------------------------------------------- 1 | # [Minimalistic interrupt (IDT, PIC, IRQ) library] 2 | 3 | .global init_idt, init_pic 4 | .global set_interrupt_handler, clear_interrupt_handler, set_irq_handler, clear_irq_handler 5 | .global enable_irq, disable_irq 6 | .global end_of_irq0, end_of_irq1, end_of_irq2, end_of_irq3, end_of_irq4, end_of_irq5, end_of_irq6, end_of_irq7 7 | .global end_of_irq8, end_of_irq9, end_of_irqA, end_of_irqB, end_of_irqC, end_of_irqD, end_of_irqE, end_of_irqF 8 | 9 | .bss 10 | 11 | idt: 12 | .skip 0x800 # 256 entries * 8 bytes per entry 13 | 14 | .text 15 | 16 | idt_ptr: # pointer to the IDT 17 | .short 0x800 - 1 # the size of the IDT, minus one 18 | .long idt # the location of the IDT 19 | 20 | # Initialize the Interrupt Descriptor Table. 21 | # Note: see http://wiki.osdev.org/IDT 22 | init_idt: 23 | lidt idt_ptr 24 | ret 25 | 26 | # Initialize the Programmable Interrupt Controllers. 27 | # IRQs are mapped to interrupts 0x20..0x2F. 28 | # Note: See http://wiki.osdev.org/PIC 29 | init_pic: 30 | movb $0x11, %al # init command, ICW4 present 31 | outb %al, $0x20 # PIC1 command port 32 | outb %al, $0xA0 # PIC2 command port 33 | 34 | movb $0x20, %al # interrupt offset 35 | outb %al, $0x21 # PIC1 data port 36 | movb $0x28, %al # interrupt offset 37 | outb %al, $0xA1 # PIC2 data port 38 | 39 | movb $0x04, %al # IR4 is connected to a slave (PIC2) 40 | outb %al, $0x21 # PIC1 data port 41 | movb $0x02, %al # slave ID 2 42 | outb %al, $0xA1 # PIC2 data port 43 | 44 | movb $0x01, %al # 8086/88 mode 45 | outb %al, $0x21 # PIC1 data port 46 | movb $0x01, %al # 8086/88 mode 47 | outb %al, $0xA1 # PIC2 data port 48 | 49 | movb $0xFF, %al # disable all IRQs 50 | outb %al, $0x21 # PIC1 data port 51 | movb $0xFF, %al # disable all IRQs 52 | outb %al, $0xA1 # PIC2 data port 53 | 54 | pushl $spurious_interrupt_handler 55 | pushl $7 56 | call set_irq_handler 57 | addl $4, %esp 58 | pushl $15 59 | call set_irq_handler 60 | addl $8, %esp 61 | 62 | ret 63 | 64 | spurious_interrupt_handler: 65 | iret 66 | 67 | 68 | # Create/update an entry in the IDT for an interrupt specified by its index. 69 | # Note that an interrupt handler must end with iret, and shoud leave all registers in tact. 70 | set_interrupt_handler: # Parameters: interrupt_index, handler_address 71 | cli # disable interrupts before modifying the IDT 72 | movl 4(%esp), %ecx # the interrupt index 73 | movw 8(%esp), %ax # \ store the low word of the handler address ... 74 | movw %ax, 0+idt(,%ecx,8) # / ... in the right place of the IDT entry 75 | movw 10(%esp), %ax # \ store the high word of the handler address ... 76 | movw %ax, 6+idt(,%ecx,8) # / ... in the right place of the IDT entry 77 | movw %cs, %ax # \ store the code segment selector ... 78 | movw %ax, 2+idt(,%ecx,8) # / ... in the right place of the IDT entry 79 | movw $0x8E00, 4+idt(,%ecx,8) # store the attributes (0x8E00: 32-bit interrupt gate) in the IDT entry 80 | sti # we're done, enable interrupts again 81 | ret 82 | 83 | 84 | # Remove an entry in the IDT for an interrupt specified by its index. 85 | clear_interrupt_handler: # Parameters: interrupt_index 86 | movl 4(%esp), %ecx # the interrupt index 87 | movw $0, 4+idt(,%ecx,8) # store the attributes (0: not present) in the IDT entry 88 | ret 89 | 90 | 91 | # Create/update an entry in the IDT for an irq specified by its index. 92 | # Note that a irq handler must acknowledge the IRQ after processing it. 93 | # This can be done by jumping to end_of_irqN, see below. 94 | set_irq_handler: # Parameters: irq_index, handler_address 95 | movl 4(%esp), %ecx # the IRQ index 96 | movl 8(%esp), %esi # the handler address 97 | addl $0x20, %ecx # IRQs are mapped to interrupts 0x20..0x2F 98 | pushl %esi # / -> the handler address 99 | pushl %ecx # | -> the interrupt index 100 | call set_interrupt_handler # \ set the interrupt handler 101 | addl $8, %esp # (cleanup the stack) 102 | ret 103 | 104 | 105 | # Remove an entry in the IDT for an irq specified by its index. 106 | clear_irq_handler: # Parametrs: irq_index 107 | movl 4(%esp), %ecx # the IRQ index 108 | addl $0x20, %ecx # IRQs are mapped to interrupts 0x20..0x2F 109 | pushl %ecx # / -> the interrupt index 110 | call clear_interrupt_handler # \ clear the interrupt handler 111 | addl $4, %esp # (cleanup the stack) 112 | ret 113 | 114 | 115 | # Disable a specific irq. 116 | # Note: See http://wiki.osdev.org/PIC 117 | disable_irq: # Parameters: irq_index 118 | movw $0x21, %dx # PIC1 data port 119 | movb 4(%esp), %al # IRQ index 120 | shlb $4, %al # \ if the IRQ index ... 121 | andb $0x80, %al # | ... is higher than 8 ... 122 | addb %al, %dl # / ... use 0xA1 (PIC2) instead of 0x21 (PIC1) 123 | movb 4(%esp), %cl # \ IRQ index ... 124 | andb $0b0111, %cl # / ... modulo 8 125 | movb $1, %bl # \ create the right mask for this IRQ index 126 | shlb %cl, %bl # / (set turn that bit, clear the rest) 127 | inb %dx, %al # \ set the selected bit ... 128 | orb %bl, %al # | ... in the data register ... 129 | outb %al, %dx # / ... of the right PIC 130 | ret 131 | 132 | 133 | # Enable a specific irq. 134 | # Note: See http://wiki.osdev.org/PIC 135 | enable_irq: # Parameters: irq_index 136 | movw $0x21, %dx # PIC1 data port 137 | movb 4(%esp), %al # IRQ index 138 | shlb $4, %al # \ if the IRQ index ... 139 | andb $0x80, %al # | ... is higher than 8 ... 140 | addb %al, %dl # / ... use 0xA1 (PIC2) instead of 0x21 (PIC1) 141 | movb 4(%esp), %cl # \ IRQ index ... 142 | andb $0b0111, %cl # / ... modulo 8 143 | movb $1, %bl # \ create the right mask for this IRQ index 144 | shlb %cl, %bl # / (set turn that bit, clear the rest) 145 | inb %dx, %al # \ clear the selected bit ... 146 | notb %bl # | ... in the ... 147 | andb %bl, %al # | ... data register ... 148 | outb %al, %dx # / ... of the right PIC 149 | ret 150 | 151 | 152 | # Acknowledge a specific irq. 153 | # Jump to one of these instead of returning at the end of your irq handler. 154 | # Note: See http://wiki.osdev.org/PIC 155 | end_of_irq0: 156 | end_of_irq1: 157 | end_of_irq2: 158 | end_of_irq3: 159 | end_of_irq4: 160 | end_of_irq5: 161 | end_of_irq6: 162 | end_of_irq7: 163 | # IRQs 0..7 are controlled by PIC1 164 | pushl %eax 165 | movb $0x20, %al # end of interrupt command 166 | outb %al, $0x20 # PIC1 command port 167 | popl %eax 168 | iret 169 | end_of_irq8: 170 | end_of_irq9: 171 | end_of_irqA: 172 | end_of_irqB: 173 | end_of_irqC: 174 | end_of_irqD: 175 | end_of_irqE: 176 | end_of_irqF: 177 | # IRQs 8..F are controlled by PIC2 via PIC1 178 | pushl %eax 179 | movb $0x20, %al # end of interrupt command 180 | outb %al, $0xA0 # PIC2 command port 181 | outb %al, $0x20 # PIC1 command port 182 | popl %eax 183 | iret 184 | 185 | -------------------------------------------------------------------------------- /bootlib/vga.s: -------------------------------------------------------------------------------- 1 | # [Minimalistic VGA text mode driver] 2 | 3 | # See 4 | # - http://wiki.osdev.org/VGA_Hardware 5 | # - http://corbatech.sourceforge.net/appleII/SecondSightVGARegisters.pdf 6 | 7 | 8 | .global vga_memory, color_text_mode, hide_cursor, vsync 9 | 10 | .equ vga_memory, 0xB8000 11 | 12 | .text 13 | 14 | # Sets a lot of VGA registers to get to 16 color 80*25 text mode 15 | color_text_mode: 16 | 17 | # Set I/OAS to 1 in Miscellaneous Output Register 18 | movw $0x3CC, %dx # read Miscellaneous Output Register ... 19 | inb %dx, %al # ... into AL 20 | andb $0b11110011, %al # select clock 00 (25 MHz) 21 | orb $0b00000011, %al # set Ram Enable and I/OAS 22 | movw $0x3C2, %dx # write the new value back 23 | outb %al, %dx # " 24 | 25 | # CRT Vertical Retrace End 26 | movb $0x11, %al # Select CRT register 27 | movw $0x3D4, %dx # " 28 | outb %al, %dx # " 29 | inb %dx, %al 30 | andb $0b01110000, %al # unset bit 7 and 0 to 3 31 | outb %al, %dx # write the new value 32 | 33 | # CRT Mode Control Register 34 | movb $0x17, %al # Select CRT register 35 | movw $0x3D4, %dx # " 36 | outb %al, %dx # " 37 | movw $0x3D5, %dx # read current value ... 38 | inb %dx, %al # ... into AL 39 | andb $0b01111111, %al # unset Sync Enable bit 40 | outb %al, %dx # write the new value back 41 | 42 | # CRT Horizontal Total Register 43 | movb $0x00, %al # Select CRT register 44 | movw $0x3D4, %dx # " 45 | outb %al, %dx # " 46 | movb $77, %al # horizontal total 47 | outb %al, %dx # write the new value 48 | 49 | # CRT End Horizontal Display 50 | movb $0x01, %al # Select CRT register 51 | movw $0x3D4, %dx # " 52 | outb %al, %dx # " 53 | movb $80, %al # last character clock to output? 54 | outb %al, %dx # write the new value 55 | 56 | # CRT Start Horizontal Blanking 57 | movb $0x02, %al # Select CRT register 58 | movw $0x3D4, %dx # " 59 | outb %al, %dx # " 60 | movb $80, %al # last character clock to output? 61 | outb %al, %dx # write the new value 62 | 63 | # CRT End Horizontal Blanking 64 | movb $0x03, %al # Select CRT register 65 | movw $0x3D4, %dx # " 66 | outb %al, %dx # " 67 | movb $0b10000001, %al # first character clock to output? 68 | outb %al, %dx # write the new value 69 | 70 | # CRT Start Horizontal Retrace 71 | movb $0x04, %al # Select CRT register 72 | movw $0x3D4, %dx # " 73 | outb %al, %dx # " 74 | movb $81, %al # first character clock to not output? 75 | outb %al, %dx # write the new value 76 | 77 | # CRT End Horizontal Retrace 78 | movb $0x05, %al # Select CRT register 79 | movw $0x3D4, %dx # " 80 | outb %al, %dx # " 81 | movb $0b00000000, %al # retrace all the way, right? 82 | outb %al, %dx # write the new value 83 | 84 | # CRT Vertical Total 85 | movb $0x06, %al # Select CRT register 86 | movw $0x3D4, %dx # " 87 | outb %al, %dx # " 88 | movb $0b10010001, %al # last scanline, lower 8 bits 89 | outb %al, %dx # write the new value 90 | 91 | # CRT Overflow 92 | movb $0x07, %al # Select CRT register 93 | movw $0x3D4, %dx # " 94 | outb %al, %dx # " 95 | movb $0b00001111, %al # overflows 96 | outb %al, %dx # write the new value 97 | 98 | # CRT Preset Row Scan Register 99 | movb $0x08, %al # Select CRT register 100 | movw $0x3D4, %dx # " 101 | outb %al, %dx # " 102 | movb $0, %al # no panning and no preset row 103 | outb %al, %dx # write the new value 104 | 105 | # CRT Maximum Scan Line 106 | movb $0x09, %al # Select CRT register 107 | movw $0x3D4, %dx # " 108 | outb %al, %dx # " 109 | movb $15, %al # maximum scan line, all the rest 0 110 | outb %al, %dx # write the new value 111 | 112 | # CRT Cursor Start 113 | movb $0x0A, %al # Select CRT register 114 | movw $0x3D4, %dx # " 115 | outb %al, %dx # " 116 | movb $0b00100000, %al # no cursor >:[ 117 | outb %al, %dx # write the new value 118 | 119 | # CRT Vertical Retrace Start 120 | movb $0x10, %al # Select CRT register 121 | movw $0x3D4, %dx # " 122 | outb %al, %dx # " 123 | movb $0b10010010, %al # lower 8 bits 124 | outb %al, %dx # write the new value 125 | 126 | # CRT Vertical Display End 127 | movb $0x12, %al # Select CRT register 128 | movw $0x3D4, %dx # " 129 | outb %al, %dx # " 130 | movb $0b10010001, %al # lower 8 bits 131 | outb %al, %dx # write the new value 132 | 133 | # CRT Start Vertical Blanking 134 | movb $0x15, %al # Select CRT register 135 | movw $0x3D4, %dx # " 136 | outb %al, %dx # " 137 | inb %dx, %al 138 | andb $0b10010001, %al # lower 8 bits 139 | outb %al, %dx # write the new value 140 | 141 | # CRT End Vertical Blanking 142 | movb $0x15, %al # Select CRT register 143 | movw $0x3D4, %dx # " 144 | outb %al, %dx # " 145 | movb $1, %al # first scanline not to blank 146 | outb %al, %dx # write the new value 147 | 148 | # select text mode 149 | movw $0x3DA, %dx 150 | inb %dx, %al 151 | 152 | movb $0x30, %al 153 | movw $0x3C0, %dx 154 | outb %al, %dx 155 | 156 | movb $0x00, %al 157 | outb %al, %dx 158 | 159 | # set sequencer register index to 0x01: Clocking Mode Register 160 | movb $0x01, %al 161 | movw $0x3C4, %dx 162 | outb %al, %dx 163 | # read current Clocking Mode Register 164 | movw $0x3C5, %dx 165 | inb %dx, %al 166 | # set 9/8 Dot Mode bit to 1 (8 pixels wide characters) 167 | orb $0b00000001, %al 168 | outb %al, %dx 169 | 170 | 171 | # set sequencer register index to 0x03: Character Map Select Register 172 | movb $0x03, %al # sequencer register index 173 | movw $0x3C4, %dx # sequencer address register port 174 | outb %al, %dx # output it 175 | # set font A and B to 000 (plane 2 0x0000 - 0x1FFF) 176 | movb $0, %al # set font bits to 0 177 | movw $0x3C5, %dx # sequencer data register port 178 | outb %al, %dx # output it 179 | 180 | # Sequencer Memory Mode Register 181 | movb $0x04, %al # sequencer register index 182 | movw $0x3C4, %dx # sequencer address register port 183 | outb %al, %dx # output it 184 | movb $0b0000, %al # 185 | movw $0x3C5, %dx # sequencer data register port 186 | outb %al, %dx # output it 187 | 188 | 189 | # Set/Reset Register 190 | movb $0x00, %al # select correct graph register 191 | movw $0x3CE, %dx 192 | outb %al, %dx 193 | movb $0xFF, %al # enable all bits 194 | movw $0x3CF, %dx 195 | outb %al, %dx 196 | 197 | # Enable Set/Reset Register 198 | movb $0x01, %al # select correct graph register 199 | movw $0x3CE, %dx 200 | outb %al, %dx 201 | movb $0x00, %al # disable all bits 202 | movw $0x3CF, %dx 203 | outb %al, %dx 204 | 205 | # Data Rotate Register 206 | movb $0x03, %al # select correct graph register 207 | movw $0x3CE, %dx 208 | outb %al, %dx 209 | movb $0x00, %al # disable all bits (don't touch mah data) 210 | movw $0x3CF, %dx 211 | outb %al, %dx 212 | 213 | # Graphics Mode Register 214 | movb $0x05, %al # select correct graph register 215 | movw $0x3CE, %dx 216 | outb %al, %dx 217 | movb $0x10, %al 218 | movw $0x3CF, %dx 219 | outb %al, %dx 220 | 221 | # Miscellaneous Graphics Register 222 | movb $0x06, %al # select correct graph register 223 | movw $0x3CE, %dx 224 | outb %al, %dx 225 | movb $0b1110, %al 226 | movw $0x3CF, %dx 227 | outb %al, %dx 228 | 229 | # Bit Mask Register 230 | movb $0x08, %al # select correct graph register 231 | movw $0x3CE, %dx 232 | outb %al, %dx 233 | movb $0xFF, %al # set all bits to 1 234 | movw $0x3CF, %dx 235 | outb %al, %dx 236 | 237 | # CRT Mode Control Register 238 | movb $0x17, %al # Select CRT register 239 | movw $0x3D4, %dx # " 240 | outb %al, %dx # " 241 | movw $0x3D5, %dx # read current value ... 242 | inb %dx, %al # ... into AL 243 | orb $0b10000000, %al # set Sync Enable bit 244 | outb %al, %dx # write the new value back 245 | 246 | ret 247 | 248 | 249 | # Hide the text mode cursor 250 | hide_cursor: 251 | movb $0x0A, %al 252 | movw $0x3D4, %dx 253 | outb %al, %dx 254 | 255 | movb $0x10, %al 256 | movw $0x3D5, %dx 257 | outb %al, %dx 258 | 259 | ret 260 | 261 | # Wait for vertical retrace to end 262 | vsync: 263 | movw $0x3DA, %dx # move port address of VGA status register to DX 264 | vsync_1: 265 | inb %dx, %al # read the current VGA status register 266 | test $0b00001000, %al # check bit 3 ... 267 | jnz vsync_1 # ... and loop until it's 0 268 | # Wait for vertical retrace to start 269 | vsync_2: 270 | inb %dx, %al # read the current VGA status register 271 | test $0b00001000, %al # check bit 3 ... 272 | jz vsync_2 # ... and loop until it's 1 273 | ret 274 | 275 | --------------------------------------------------------------------------------