Initial commit

This commit is contained in:
2018-11-11 14:19:19 -06:00
commit af15d43c8a
17 changed files with 1351 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
obj/
iso/
+7
View File
@@ -0,0 +1,7 @@
#ifndef __GDT_H
#define __GDT_H
void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned char access, unsigned char gran);
void gdt_install();
#endif
+7
View File
@@ -0,0 +1,7 @@
#ifndef __IDT_H
#define __IDT_H
void idt_set_gate(unsigned char num, unsigned long base, unsigned short sel, unsigned char flags);
void idt_install();
#endif
+10
View File
@@ -0,0 +1,10 @@
#ifndef __SCRN_H
#define __SCRN_H
extern void cls();
extern void putch(unsigned char c);
extern void puts(unsigned char *str);
extern void settextcolor(unsigned char forecolor, unsigned char backcolor);
extern void init_video();
#endif
+30
View File
@@ -0,0 +1,30 @@
#ifndef __SYSTEM_H
#define __SYSTEM_H
/* MAIN.C */
extern unsigned char *memcpy(unsigned char *dest, const unsigned char *src, int count);
extern unsigned char *memset(unsigned char *dest, unsigned char val, int count);
extern unsigned short *memsetw(unsigned short *dest, unsigned short val, int count);
extern int strlen(const char *str);
extern unsigned char inportb (unsigned short _port);
extern void outportb (unsigned short _port, unsigned char _data);
/* ISRS.C */
void isrs_install();
/* This defines what the stack looks like after an ISR was running */
struct regs
{
unsigned int gs, fs, es, ds; /* pushed the segs last */
unsigned int edi, esi, ebp, esp, ebx, edx, ecx, eax; /* pushed by 'pusha' */
unsigned int int_no, err_code; /* our 'push byte #' and ecodes do this */
unsigned int eip, cs, eflags, useresp, ss; /* pushed by the processor automatically */
};
/* IRQ.C */
void irq_install_handler(int irq, void (*handler)(struct regs *r));
void irq_uninstall_handler(int irq);
void irq_install();
/* TIMER.C */
void timer_handler(struct regs *r);
void timer_install();
/* KB.C */
void kb_install();
#endif
+25
View File
@@ -0,0 +1,25 @@
OUTPUT_FORMAT("binary")
ENTRY(start)
phys = 0x00100000;
SECTIONS
{
.text phys : AT(phys) {
code = .;
*(.text)
*(.rodata)
. = ALIGN(4096);
}
.data : AT(phys + (data - code))
{
data = .;
*(.data)
. = ALIGN(4096);
}
.bss : AT(phys + (bss - code))
{
bss = .;
*(.bss)
. = ALIGN(4096);
}
end = .;
}
+48
View File
@@ -0,0 +1,48 @@
GCCPARAMS = -m32 -fno-pie -nostdlib -fstrength-reduce -fomit-frame-pointer -finline-functions -fno-builtin -nostdinc
ASPARAMS = -f elf32
LDPARAMS = -m elf_i386
#BUILD_NUMBER_FILE=build-number.txt
objects = obj/start.o \
obj/main.o \
obj/scrn.o \
obj/gdt.o \
obj/idt.o \
obj/isrs.o \
obj/irq.o \
obj/timer.o \
obj/kb.o
run: kernel.iso
qemu-system-x86_64 -cdrom kernel.iso &
obj/%.o: src/%.c
mkdir -p $(@D)
gcc $(GCCPARAMS) -c -o $@ $<
obj/%.o: src/%.asm
mkdir -p $(@D)
nasm $(ASPARAMS) -o $@ $<
kernel.bin: linker.ld $(objects)
mkdir obj/bin
ld $(LDPARAMS) -T $< -o obj/bin/$@ $(objects)
#https://www.linuxjournal.com/content/add-auto-incrementing-build-number-your-build-process
#include buildnumber.mak
kernel.iso: kernel.bin
mkdir iso
mkdir iso/boot
mkdir iso/boot/grub
cp obj/bin/kernel.bin iso/boot/kernel.bin
echo 'menuentry SDOS {' > iso/boot/grub/grub.cfg
echo ' multiboot /boot/kernel.bin' >> iso/boot/grub/grub.cfg
echo ' boot' >> iso/boot/grub/grub.cfg
echo '}' >> iso/boot/grub/grub.cfg
grub-mkrescue --output=kernel.iso iso
rm -rf iso
.PHONY: clean
clean:
rm -rf obj kernel.iso
+10
View File
@@ -0,0 +1,10 @@
SDOS
====
SDOS or Slow and Dirty Operating System is a hobby
operating system written purely for the experience.
The name comes from QDOS, or Quick and Dirty Operating
System that ws the core of what would become MS-DOS.
Nothing about this project is fast but the code will
be quite dirty as I learn more about writing an OS.
Therefore, slow and dirty perfectly describes this project.
+76
View File
@@ -0,0 +1,76 @@
#include "../include/system.h"
/* Defines a GDT entry. We say packed, because it prevents the
* compiler from doing things that it thinks is best: Prevent
* compiler "optimization" by packing */
struct gdt_entry
{
unsigned short limit_low;
unsigned short base_low;
unsigned char base_middle;
unsigned char access;
unsigned char granularity;
unsigned char base_high;
} __attribute__((packed));
/* Special pointer which includes the limit: The max bytes
* taken up by the GDT, minus 1. Again, this NEEDS to be packed */
struct gdt_ptr
{
unsigned short limit;
unsigned int base;
} __attribute__((packed));
/* Our GDT, with 3 entries, and finally our special GDT pointer */
struct gdt_entry gdt[3];
struct gdt_ptr gp;
/* This will be a function in start.asm. We use this to properly
* reload the new segment registers */
extern void gdt_flush();
/* Setup a descriptor in the Global Descriptor Table */
void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned char access, unsigned char gran)
{
/* Setup the descriptor base address */
gdt[num].base_low = (base & 0xFFFF);
gdt[num].base_middle = (base >> 16) & 0xFF;
gdt[num].base_high = (base >> 24) & 0xFF;
/* Setup the descriptor limits */
gdt[num].limit_low = (limit & 0xFFFF);
gdt[num].granularity = ((limit >> 16) & 0x0F);
/* Finally, set up the granularity and access flags */
gdt[num].granularity |= (gran & 0xF0);
gdt[num].access = access;
}
/* Should be called by main. This will setup the special GDT
* pointer, set up the first 3 entries in our GDT, and then
* finally call gdt_flush() in our assembler file in order
* to tell the processor where the new GDT is and update the
* new segment registers */
void gdt_install()
{
/* Setup the GDT pointer and limit */
gp.limit = (sizeof(struct gdt_entry) * 3) - 1;
gp.base = &gdt;
/* Our NULL descriptor */
gdt_set_gate(0, 0, 0, 0, 0);
/* The second entry is our Code Segment. The base address
* is 0, the limit is 4GBytes, it uses 4KByte granularity,
* uses 32-bit opcodes, and is a Code Segment descriptor.
* Please check the table above in the tutorial in order
* to see exactly what each value means */
gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);
/* The third entry is our Data Segment. It's EXACTLY the
* same as our code segment, but the descriptor type in
* this entry's access byte says it's a Data Segment */
gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);
/* Flush out the old GDT and install the new changes! */
gdt_flush();
}
+59
View File
@@ -0,0 +1,59 @@
#include "../include/system.h"
/* Defines an IDT entry */
struct idt_entry
{
unsigned short base_lo;
unsigned short sel; /* Our kernel segment goes here! */
unsigned char always0; /* This will ALWAYS be set to 0! */
unsigned char flags; /* Set using the above table! */
unsigned short base_hi;
} __attribute__((packed));
struct idt_ptr
{
unsigned short limit;
unsigned int base;
} __attribute__((packed));
/* Declare an IDT of 256 entries. Although we will only use the
* first 32 entries in this tutorial, the rest exists as a bit
* of a trap. If any undefined IDT entry is hit, it normally
* will cause an "Unhandled Interrupt" exception. Any descriptor
* for which the 'presence' bit is cleared (0) will generate an
* "Unhandled Interrupt" exception */
struct idt_entry idt[256];
struct idt_ptr idtp;
/* This exists in 'start.asm', and is used to load our IDT */
extern void idt_load();
/* Use this function to set an entry in the IDT. Alot simpler
* than twiddling with the GDT ;) */
void idt_set_gate(unsigned char num, unsigned long base, unsigned short sel, unsigned char flags)
{
/* The interrupt routine's base address */
idt[num].base_lo = (base & 0xFFFF);
idt[num].base_hi = (base >> 16) & 0xFFFF;
/* The segment or 'selector' that this IDT entry will use
* is set here, along with any access flags */
idt[num].sel = sel;
idt[num].always0 = 0;
idt[num].flags = flags;
}
/* Installs the IDT */
void idt_install()
{
/* Sets the special IDT pointer up, just like in 'gdt.c' */
idtp.limit = (sizeof (struct idt_entry) * 256) - 1;
idtp.base = &idt;
/* Clear out the entire IDT, initializing it to zeros */
memset(&idt, 0, sizeof(struct idt_entry) * 256);
/* Add any new ISRs to the IDT here using idt_set_gate */
/* Points the processor's internal register to the new IDT */
idt_load();
}
+123
View File
@@ -0,0 +1,123 @@
#include "../include/system.h"
/* These are own ISRs that point to our special IRQ handler
* instead of the regular 'fault_handler' function */
extern void irq0();
extern void irq1();
extern void irq2();
extern void irq3();
extern void irq4();
extern void irq5();
extern void irq6();
extern void irq7();
extern void irq8();
extern void irq9();
extern void irq10();
extern void irq11();
extern void irq12();
extern void irq13();
extern void irq14();
extern void irq15();
/* This array is actually an array of function pointers. We use
* this to handle custom IRQ handlers for a given IRQ */
void *irq_routines[16] =
{
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0
};
/* This installs a custom IRQ handler for the given IRQ */
void irq_install_handler(int irq, void (*handler)(struct regs *r))
{
irq_routines[irq] = handler;
}
/* This clears the handler for a given IRQ */
void irq_uninstall_handler(int irq)
{
irq_routines[irq] = 0;
}
/* Normally, IRQs 0 to 7 are mapped to entries 8 to 15. This
* is a problem in protected mode, because IDT entry 8 is a
* Double Fault! Without remapping, every time IRQ0 fires,
* you get a Double Fault Exception, which is NOT actually
* what's happening. We send commands to the Programmable
* Interrupt Controller (PICs - also called the 8259's) in
* order to make IRQ0 to 15 be remapped to IDT entries 32 to
* 47 */
void irq_remap(void)
{
outportb(0x20, 0x11);
outportb(0xA0, 0x11);
outportb(0x21, 0x20);
outportb(0xA1, 0x28);
outportb(0x21, 0x04);
outportb(0xA1, 0x02);
outportb(0x21, 0x01);
outportb(0xA1, 0x01);
outportb(0x21, 0x0);
outportb(0xA1, 0x0);
}
/* We first remap the interrupt controllers, and then we install
* the appropriate ISRs to the correct entries in the IDT. This
* is just like installing the exception handlers */
void irq_install()
{
irq_remap();
idt_set_gate(32, (unsigned)irq0, 0x08, 0x8E);
idt_set_gate(33, (unsigned)irq1, 0x08, 0x8E);
idt_set_gate(34, (unsigned)irq2, 0x08, 0x8E);
idt_set_gate(35, (unsigned)irq3, 0x08, 0x8E);
idt_set_gate(36, (unsigned)irq4, 0x08, 0x8E);
idt_set_gate(37, (unsigned)irq5, 0x08, 0x8E);
idt_set_gate(38, (unsigned)irq6, 0x08, 0x8E);
idt_set_gate(39, (unsigned)irq7, 0x08, 0x8E);
idt_set_gate(40, (unsigned)irq8, 0x08, 0x8E);
idt_set_gate(41, (unsigned)irq9, 0x08, 0x8E);
idt_set_gate(42, (unsigned)irq10, 0x08, 0x8E);
idt_set_gate(43, (unsigned)irq11, 0x08, 0x8E);
idt_set_gate(44, (unsigned)irq12, 0x08, 0x8E);
idt_set_gate(45, (unsigned)irq13, 0x08, 0x8E);
idt_set_gate(46, (unsigned)irq14, 0x08, 0x8E);
idt_set_gate(47, (unsigned)irq15, 0x08, 0x8E);
}
/* Each of the IRQ ISRs point to this function, rather than
* the 'fault_handler' in 'isrs.c'. The IRQ Controllers need
* to be told when you are done servicing them, so you need
* to send them an "End of Interrupt" command (0x20). There
* are two 8259 chips: The first exists at 0x20, the second
* exists at 0xA0. If the second controller (an IRQ from 8 to
* 15) gets an interrupt, you need to acknowledge the
* interrupt at BOTH controllers, otherwise, you only send
* an EOI command to the first controller. If you don't send
* an EOI, you won't raise any more IRQs */
void irq_handler(struct regs *r)
{
/* This is a blank function pointer */
void (*handler)(struct regs *r);
/* Find out if we have a custom handler to run for this
* IRQ, and then finally, run it */
handler = irq_routines[r->int_no - 32];
if (handler)
{
handler(r);
}
/* If the IDT entry that was invoked was greater than 40
* (meaning IRQ8 - 15), then we need to send an EOI to
* the slave controller */
if (r->int_no >= 40)
{
outportb(0xA0, 0x20);
}
/* In either case, we need to send an EOI to the master
* interrupt controller too */
outportb(0x20, 0x20);
}
+142
View File
@@ -0,0 +1,142 @@
#include "../include/system.h"
/* These are function prototypes for all of the exception
* handlers: The first 32 entries in the IDT are reserved
* by Intel, and are designed to service exceptions! */
extern void isr0();
extern void isr1();
extern void isr2();
extern void isr3();
extern void isr4();
extern void isr5();
extern void isr6();
extern void isr7();
extern void isr8();
extern void isr9();
extern void isr10();
extern void isr11();
extern void isr12();
extern void isr13();
extern void isr14();
extern void isr15();
extern void isr16();
extern void isr17();
extern void isr18();
extern void isr19();
extern void isr20();
extern void isr21();
extern void isr22();
extern void isr23();
extern void isr24();
extern void isr25();
extern void isr26();
extern void isr27();
extern void isr28();
extern void isr29();
extern void isr30();
extern void isr31();
/* This is a very repetitive function... it's not hard, it's
* just annoying. As you can see, we set the first 32 entries
* in the IDT to the first 32 ISRs. We can't use a for loop
* for this, because there is no way to get the function names
* that correspond to that given entry. We set the access
* flags to 0x8E. This means that the entry is present, is
* running in ring 0 (kernel level), and has the lower 5 bits
* set to the required '14', which is represented by 'E' in
* hex. */
void isrs_install()
{
idt_set_gate(0, (unsigned)isr0, 0x08, 0x8E);
idt_set_gate(1, (unsigned)isr1, 0x08, 0x8E);
idt_set_gate(2, (unsigned)isr2, 0x08, 0x8E);
idt_set_gate(3, (unsigned)isr3, 0x08, 0x8E);
idt_set_gate(4, (unsigned)isr4, 0x08, 0x8E);
idt_set_gate(5, (unsigned)isr5, 0x08, 0x8E);
idt_set_gate(6, (unsigned)isr6, 0x08, 0x8E);
idt_set_gate(7, (unsigned)isr7, 0x08, 0x8E);
idt_set_gate(8, (unsigned)isr8, 0x08, 0x8E);
idt_set_gate(9, (unsigned)isr9, 0x08, 0x8E);
idt_set_gate(10, (unsigned)isr10, 0x08, 0x8E);
idt_set_gate(11, (unsigned)isr11, 0x08, 0x8E);
idt_set_gate(12, (unsigned)isr12, 0x08, 0x8E);
idt_set_gate(13, (unsigned)isr13, 0x08, 0x8E);
idt_set_gate(14, (unsigned)isr14, 0x08, 0x8E);
idt_set_gate(15, (unsigned)isr15, 0x08, 0x8E);
idt_set_gate(16, (unsigned)isr16, 0x08, 0x8E);
idt_set_gate(17, (unsigned)isr17, 0x08, 0x8E);
idt_set_gate(18, (unsigned)isr18, 0x08, 0x8E);
idt_set_gate(19, (unsigned)isr19, 0x08, 0x8E);
idt_set_gate(20, (unsigned)isr20, 0x08, 0x8E);
idt_set_gate(21, (unsigned)isr21, 0x08, 0x8E);
idt_set_gate(22, (unsigned)isr22, 0x08, 0x8E);
idt_set_gate(23, (unsigned)isr23, 0x08, 0x8E);
idt_set_gate(24, (unsigned)isr24, 0x08, 0x8E);
idt_set_gate(25, (unsigned)isr25, 0x08, 0x8E);
idt_set_gate(26, (unsigned)isr26, 0x08, 0x8E);
idt_set_gate(27, (unsigned)isr27, 0x08, 0x8E);
idt_set_gate(28, (unsigned)isr28, 0x08, 0x8E);
idt_set_gate(29, (unsigned)isr29, 0x08, 0x8E);
idt_set_gate(30, (unsigned)isr30, 0x08, 0x8E);
idt_set_gate(31, (unsigned)isr31, 0x08, 0x8E);
}
/* This is a simple string array. It contains the message that
* corresponds to each and every exception. We get the correct
* message by accessing like:
* exception_message[interrupt_number] */
unsigned char *exception_messages[] =
{
"Division By Zero",
"Debug",
"Non Maskable Interrupt",
"Breakpoint",
"Into Detected Overflow",
"Out of Bounds",
"Invalid Opcode",
"No Coprocessor",
"Double Fault",
"Coprocessor Segement Overrun",
"Bad TSS",
"Segment Not Present",
"Stack Fault",
"General Protection Fault",
"Page Fault",
"Unkown Interrupt",
"Coprocessor Fault",
"Alignment Check",
"Machine Check",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved"
};
/* All of our Exception handling Interrupt Service Routines will
* point to this function. This will tell us what exception has
* happened! Right now, we simply halt the system by hitting an
* endless loop. All ISRs disable interrupts while they are being
* serviced as a 'locking' mechanism to prevent an IRQ from
* happening and messing up kernel data structures */
void fault_handler(struct regs *r)
{
/* Is this a fault whose number is from 0 to 31? */
if (r->int_no < 32)
{
/* Display the description for the Exception that occurred.
* In this tutorial, we will simply halt the system using an
* infinite loop */
puts(exception_messages[r->int_no]);
puts(" Exception. System Halted!\n");
for (;;);
}
}
+83
View File
@@ -0,0 +1,83 @@
#include "../include/system.h"
//http://www.osdever.net/bkerndev/Docs/keyboard.htm
/* KBDUS means US Keyboard Layout. This is a scancode table
* used to layout a standard US keyboard. I have left some
* comments in to give you an idea of what key is what, even
* though I set it's array index to 0. You can change that to
* whatever you want using a macro, if you wish! */
unsigned char kbdus[128] =
{
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', /* 9 */
'9', '0', '-', '=', '\b', /* Backspace */
'\t', /* Tab */
'q', 'w', 'e', 'r', /* 19 */
't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n', /* Enter key */
0, /* 29 - Control */
'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', /* 39 */
'\'', '`', 0, /* Left shift */
'\\', 'z', 'x', 'c', 'v', 'b', 'n', /* 49 */
'm', ',', '.', '/', 0, /* Right shift */
'*',
0, /* Alt */
' ', /* Space bar */
0, /* Caps lock */
0, /* 59 - F1 key ... > */
0, 0, 0, 0, 0, 0, 0, 0,
0, /* < ... F10 */
0, /* 69 - Num lock*/
0, /* Scroll Lock */
0, /* Home key */
0, /* Up Arrow */
0, /* Page Up */
'-',
0, /* Left Arrow */
0,
0, /* Right Arrow */
'+',
0, /* 79 - End key*/
0, /* Down Arrow */
0, /* Page Down */
0, /* Insert Key */
0, /* Delete Key */
0, 0, 0,
0, /* F11 Key */
0, /* F12 Key */
0, /* All other keys are undefined */
};
/* Handles the keyboard interrupt */
void keyboard_handler(struct regs *r)
{
unsigned char scancode;
/* Read from the keyboard's data buffer */
scancode = inportb(0x60);
/* If the top bit of the byte we read from the keyboard is
* set, that means that a key has just been released */
if (scancode & 0x80)
{
/* You can use this one to see if the user released the
* shift, alt, or control keys... */
}
else
{
/* Here, a key was just pressed. Please note that if you
* hold a key down, you will get repeated key press
* interrupts. */
/* Just to show you how this works, we simply translate
* the keyboard scancode into an ASCII value, and then
* display it to the screen. You can get creative and
* use some flags to see if a shift is pressed and use a
* different layout, or you can add another 128 entries
* to the above layout to correspond to 'shift' being
* held. If shift is held using the larger lookup table,
* you would add 128 to the scancode when you look for it */
putch(kbdus[scancode]);
}
}
void kb_install()
{
irq_install_handler(1, keyboard_handler);
}
+97
View File
@@ -0,0 +1,97 @@
#include "../include/system.h"
#include "../include/gdt.h"
#include "../include/idt.h"
#include "../include/scrn.h"
unsigned char *memcpy(unsigned char *dest, const unsigned char *src, int count)
{
/* Add code here to copy 'count' bytes of data from 'src' to
* 'dest', finally return 'dest' */
const char *sp = (const char *)src;
char *dp = (char *)dest;
for(; count != 0; count--) *dp++ = *sp++;
return dest;
}
unsigned char *memset(unsigned char *dest, unsigned char val, int count)
{
/* Add code here to set 'count' bytes in 'dest' to 'val'.
* Again, return 'dest' */
char *temp = (char *)dest;
for( ; count != 0; count--) *temp++ = val;
return dest;
}
unsigned short *memsetw(unsigned short *dest, unsigned short val, int count)
{
/* Same as above, but this time, we're working with a 16-bit
* 'val' and dest pointer. Your code can be an exact copy of
* the above, provided that your local variables if any, are
* unsigned short */
unsigned short *temp = (unsigned short *)dest;
for( ; count != 0; count--) *temp++ = val;
return dest;
}
int strlen(const char *str)
{
/* This loops through character array 'str', returning how
* many characters it needs to check before it finds a 0.
* In simple words, it returns the length in bytes of a string */
//size_t retval;
unsigned int retval;
for(retval = 0; *str != '\0'; str++) retval++;
return retval;
}
/* We will use this later on for reading from the I/O ports to get data
* from devices such as the keyboard. We are using what is called
* 'inline assembly' in these routines to actually do the work */
unsigned char inportb (unsigned short _port)
{
unsigned char rv;
__asm__ __volatile__ ("inb %1, %0" : "=a" (rv) : "dN" (_port));
return rv;
}
/* We will use this to write to I/O ports to send bytes to devices. This
* will be used in the next tutorial for changing the textmode cursor
* position. Again, we use some inline assembly for the stuff that simply
* cannot be done in C */
void outportb (unsigned short _port, unsigned char _data)
{
__asm__ __volatile__ ("outb %1, %0" : : "dN" (_port), "a" (_data));
}
extern char __BUILD_DATE;
extern char __BUILD_NUMBER;
/* This is a very simple main() function. All it does is sit in an
* infinite loop. This will be like our 'idle' loop */
void main()
{
/* You would add commands after here */
init_video();
puts("Video initialized.\n");
gdt_install();
puts("Global Descriptor Table initialized.\n");
idt_install();
puts("Interrupt Descriptor Table initialized.\n");
isrs_install();
puts("Interrupt Service Routines installed.\n");
irq_install();
__asm__ __volatile__ ("sti");
puts("Interrupt Requests now allowed.\n");
//timer_install();
//puts("Installed Timer.\n");
kb_install();
puts("Keyboard installed.\n");
puts("SDOS version 0.0.0.1 initialized.\n");
//puts(&__BUILD_DATE);
//puts(&__BUILD_NUMBER);
//puts("Divide by zero check:\n");
//puts(5 / 0);
/* ...and leave this loop in. There is an endless loop in
* 'start.asm' also, if you accidentally delete this next line */
for (;;);
}
+158
View File
@@ -0,0 +1,158 @@
#include "../include/system.h"
/* These define our textpointer, our background and foreground
* colors (attributes), and x and y cursor coordinates */
unsigned short *textmemptr;
int attrib = 0x0F;
int csr_x = 0, csr_y = 0;
/* Scrolls the screen */
void scroll(void)
{
unsigned blank, temp;
/* A blank is defined as a space... we need to give it
* backcolor too */
blank = 0x20 | (attrib << 8);
/* Row 25 is the end, this means we need to scroll up */
if(csr_y >= 25)
{
/* Move the current text chunk that makes up the screen
* back in the buffer by a line */
temp = csr_y - 25 + 1;
memcpy (textmemptr, textmemptr + temp * 80, (25 - temp) * 80 * 2);
/* Finally, we set the chunk of memory that occupies
* the last line of text to our 'blank' character */
memsetw (textmemptr + (25 - temp) * 80, blank, 80);
csr_y = 25 - 1;
}
}
/* Updates the hardware cursor: the little blinking line
* on the screen under the last character pressed! */
void move_csr(void)
{
unsigned temp;
/* The equation for finding the index in a linear
* chunk of memory can be represented by:
* Index = [(y * width) + x] */
temp = csr_y * 80 + csr_x;
/* This sends a command to indicies 14 and 15 in the
* CRT Control Register of the VGA controller. These
* are the high and low bytes of the index that show
* where the hardware cursor is to be 'blinking'. To
* learn more, you should look up some VGA specific
* programming documents. A great start to graphics:
* http://www.brackeen.com/home/vga */
outportb(0x3D4, 14);
outportb(0x3D5, temp >> 8);
outportb(0x3D4, 15);
outportb(0x3D5, temp);
}
/* Clears the screen */
void cls()
{
unsigned blank;
int i;
/* Again, we need the 'short' that will be used to
* represent a space with color */
blank = 0x20 | (attrib << 8);
/* Sets the entire screen to spaces in our current
* color */
for(i = 0; i < 25; i++)
memsetw (textmemptr + i * 80, blank, 80);
/* Update out virtual cursor, and then move the
* hardware cursor */
csr_x = 0;
csr_y = 0;
move_csr();
}
/* Puts a single character on the screen */
void putch(unsigned char c)
{
unsigned short *where;
unsigned att = attrib << 8;
/* Handle a backspace, by moving the cursor back one space */
if(c == 0x08)
{
if(csr_x != 0) csr_x--;
}
/* Handles a tab by incrementing the cursor's x, but only
* to a point that will make it divisible by 8 */
else if(c == 0x09)
{
csr_x = (csr_x + 8) & ~(8 - 1);
}
/* Handles a 'Carriage Return', which simply brings the
* cursor back to the margin */
else if(c == '\r')
{
csr_x = 0;
}
/* We handle our newlines the way DOS and the BIOS do: we
* treat it as if a 'CR' was also there, so we bring the
* cursor to the margin and we increment the 'y' value */
else if(c == '\n')
{
csr_x = 0;
csr_y++;
}
/* Any character greater than and including a space, is a
* printable character. The equation for finding the index
* in a linear chunk of memory can be represented by:
* Index = [(y * width) + x] */
else if(c >= ' ')
{
where = textmemptr + (csr_y * 80 + csr_x);
*where = c | att; /* Character AND attributes: color */
csr_x++;
}
/* If the cursor has reached the edge of the screen's width, we
* insert a new line in there */
if(csr_x >= 80)
{
csr_x = 0;
csr_y++;
}
/* Scroll the screen if needed, and finally move the cursor */
scroll();
move_csr();
}
/* Uses the above routine to output a string... */
void puts(unsigned char *text)
{
int i;
for (i = 0; i < strlen(text); i++)
{
putch(text[i]);
}
}
/* Sets the forecolor and backcolor that we will use */
void settextcolor(unsigned char forecolor, unsigned char backcolor)
{
/* Top 4 bytes are the background, bottom 4 bytes
* are the foreground color */
attrib = (backcolor << 4) | (forecolor & 0x0F);
}
/* Sets our text-mode VGA pointer, then clears the screen for us */
void init_video(void)
{
textmemptr = (unsigned short *)0xB8000;
cls();
}
+443
View File
@@ -0,0 +1,443 @@
; This is the kernel's entry point. We could either call main here,
; or we can use this to setup the stack or other nice stuff, like
; perhaps setting up the GDT and segments. Please note that interrupts
; are disabled at this point: More on interrupts later!
[BITS 32]
global start
start:
mov esp, _sys_stack ; This points the stack to our new stack area
jmp stublet
; This part MUST be 4byte aligned, so we solve that issue using 'ALIGN 4'
ALIGN 4
mboot:
; Multiboot macros to make a few lines later more readable
MULTIBOOT_PAGE_ALIGN equ 1<<0
MULTIBOOT_MEMORY_INFO equ 1<<1
MULTIBOOT_AOUT_KLUDGE equ 1<<16
MULTIBOOT_HEADER_MAGIC equ 0x1BADB002
MULTIBOOT_HEADER_FLAGS equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_AOUT_KLUDGE
MULTIBOOT_CHECKSUM equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
EXTERN code, bss, end
; This is the GRUB Multiboot header. A boot signature
dd MULTIBOOT_HEADER_MAGIC
dd MULTIBOOT_HEADER_FLAGS
dd MULTIBOOT_CHECKSUM
; AOUT kludge - must be physical addresses. Make a note of these:
; The linker script fills in the data for these ones!
dd mboot
dd code
dd bss
dd end
dd start
; This is an endless loop here. Make a note of this: Later on, we
; will insert an 'extern _main', followed by 'call _main', right
; before the 'jmp $'.
stublet:
extern main
call main
jmp $
; This will set up our new segment registers. We need to do
; something special in order to set CS. We do what is called a
; far jump. A jump that includes a segment as well as an offset.
; This is declared in C as 'extern void gdt_flush();'
global gdt_flush ; Allows the C code to link to this
extern gp ; Says that 'gp' is in another file
gdt_flush:
lgdt [gp] ; Load the GDT with our '_gp' which is a special pointer
mov ax, 0x10 ; 0x10 is the offset in the GDT to our data segment
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp 0x08:flush2 ; 0x08 is the offset to our code segment: Far jump!
flush2:
ret ; Returns back to the C code!
; Loads the IDT defined in '_idtp' into the processor.
; This is declared in C as 'extern void idt_load();'
global idt_load
extern idtp
idt_load:
lidt [idtp]
ret
global isr0
global isr1
global isr2
global isr3
global isr4
global isr5
global isr6
global isr7
global isr8
global isr9
global isr10
global isr11
global isr12
global isr13
global isr14
global isr15
global isr16
global isr17
global isr18
global isr19
global isr20
global isr21
global isr22
global isr23
global isr24
global isr25
global isr26
global isr27
global isr28
global isr29
global isr30
global isr31
; 0: Divide By Zero Exception
isr0:
cli
push byte 0 ; A normal ISR stub that pops a dummy error code to keep a
push byte 0 ; uniform stack frame
jmp isr_common_stub
; 1: Debug Exception
isr1:
cli
push byte 0
push byte 1
jmp isr_common_stub
; 2: Non Maskable Interrupt Exception
isr2:
cli
push byte 0
push byte 2
jmp isr_common_stub
; 3: Breakpoint Exception
isr3:
cli
push byte 0
push byte 3
jmp isr_common_stub
; 4: Into Detected Overflow Exception
isr4:
cli
push byte 0
push byte 4
jmp isr_common_stub
; 5: Out of Bounds Exception
isr5:
cli
push byte 0
push byte 5
jmp isr_common_stub
; 6: Invalid Opcode Exception
isr6:
cli
push byte 0
push byte 6
jmp isr_common_stub
; 7: No Coprocessor Exception
isr7:
cli
push byte 0
push byte 7
jmp isr_common_stub
; 8: Double Fault Exception (With Error Code!)
isr8:
cli
push byte 8 ; Note that we DON'T push a value on the stack in this one!
; It pushes one already! Use this type of stub for exceptions
; that pop error codes!
jmp isr_common_stub
; 9: Coprocessor Segment Overrun Exception
isr9:
cli
push byte 0
push byte 9
jmp isr_common_stub
; 10: Bad TSS Exception
isr10:
cli
push byte 10
jmp isr_common_stub
; 11: Segement Not Present Exception
isr11:
cli
push byte 11
jmp isr_common_stub
; 12: Stack Fault Exception
isr12:
cli
push byte 12
jmp isr_common_stub
; 13: General Protection Fault Exception
isr13:
cli
push byte 13
jmp isr_common_stub
; 14: Page Fault Exception
isr14:
cli
push byte 14
jmp isr_common_stub
; 15: Unkown Interrupt Exception
isr15:
cli
push byte 0
push byte 15
jmp isr_common_stub
; 16: Coprocessor Fault Exception
isr16:
cli
push byte 0
push byte 16
jmp isr_common_stub
; 17: Aligment Check Exception
isr17:
cli
push byte 0
push byte 17
jmp isr_common_stub
; 18: Machine Check Exception
isr18:
cli
push byte 0
push byte 18
jmp isr_common_stub
; 19 - 31: Reserved Exceptions
isr19:
cli
push byte 0
push byte 19
jmp isr_common_stub
isr20:
cli
push byte 0
push byte 20
jmp isr_common_stub
isr21:
cli
push byte 0
push byte 21
jmp isr_common_stub
isr22:
cli
push byte 0
push byte 22
jmp isr_common_stub
isr23:
cli
push byte 0
push byte 23
jmp isr_common_stub
isr24:
cli
push byte 0
push byte 24
jmp isr_common_stub
isr25:
cli
push byte 0
push byte 25
jmp isr_common_stub
isr26:
cli
push byte 0
push byte 26
jmp isr_common_stub
isr27:
cli
push byte 0
push byte 27
jmp isr_common_stub
isr28:
cli
push byte 0
push byte 28
jmp isr_common_stub
isr29:
cli
push byte 0
push byte 29
jmp isr_common_stub
isr30:
cli
push byte 0
push byte 30
jmp isr_common_stub
isr31:
cli
push byte 0
push byte 31
jmp isr_common_stub
; We call a C function in here. We need to let the assembler know
; that 'fault_handler' exists in another file
extern fault_handler
; This is our common ISR stub. It saves the processor state, sets
; up for kernel mode segments, calls the C-level fault handler,
; and finally restores the stack frame.
isr_common_stub:
pusha
push ds
push es
push fs
push gs
mov ax, 0x10 ; Load the Kernel Data Segment descriptor!
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov eax, esp ; Push us the stack
push eax
mov eax, fault_handler
call eax ; A special call, preserves the 'eip' register
pop eax
pop gs
pop fs
pop es
pop ds
popa
add esp, 8 ; Cleans up the pushed error code and pushed ISR number
iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP!
global irq0
global irq1
global irq2
global irq3
global irq4
global irq5
global irq6
global irq7
global irq8
global irq9
global irq10
global irq11
global irq12
global irq13
global irq14
global irq15
; 32: IRQ0
irq0:
cli
push byte 0 ; Note that these don't push an error code on the stack:
push byte 32 ; We need to push a dummy error code
jmp irq_common_stub
irq1:
cli
push byte 0
push byte 33
jmp irq_common_stub
irq2:
cli
push byte 0
push byte 34
jmp irq_common_stub
irq3:
cli
push byte 0
push byte 35
jmp irq_common_stub
irq4:
cli
push byte 0
push byte 36
jmp irq_common_stub
irq5:
cli
push byte 0
push byte 37
jmp irq_common_stub
irq6:
cli
push byte 0
push byte 38
jmp irq_common_stub
irq7:
cli
push byte 0
push byte 39
jmp irq_common_stub
irq8:
cli
push byte 0
push byte 40
jmp irq_common_stub
irq9:
cli
push byte 0
push byte 41
jmp irq_common_stub
irq10:
cli
push byte 0
push byte 42
jmp irq_common_stub
irq11:
cli
push byte 0
push byte 43
jmp irq_common_stub
irq12:
cli
push byte 0
push byte 44
jmp irq_common_stub
irq13:
cli
push byte 0
push byte 45
jmp irq_common_stub
irq14:
cli
push byte 0
push byte 46
jmp irq_common_stub
; 47: IRQ15
irq15:
cli
push byte 0
push byte 47
jmp irq_common_stub
extern irq_handler
; This is a stub that we have created for IRQ based ISRs. This calls
; 'irq_handler' in our C code. We need to create this in an 'irq.c'
irq_common_stub:
pusha
push ds
push es
push fs
push gs
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov eax, esp
push eax
mov eax, irq_handler
call eax
pop eax
pop gs
pop fs
pop es
pop ds
popa
add esp, 8
iret
; Here is the definition of our BSS section. Right now, we'll use
; it just to store the stack. Remember that a stack actually grows
; downwards, so we declare the size of the data before declaring
; the identifier '_sys_stack'
SECTION .bss
resb 8192 ; This reserves 8KBytes of memory here
_sys_stack:
+31
View File
@@ -0,0 +1,31 @@
#include "../include/system.h"
//http://www.osdever.net/bkerndev/Docs/pit.htm
/* This will keep track of how many ticks that the system
* has been running for */
int timer_ticks = 0;
/* Handles the timer. In this case, it's very simple: We
* increment the 'timer_ticks' variable every time the
* timer fires. By default, the timer fires 18.222 times
* per second. Why 18.222Hz? Some engineer at IBM must've
* been smoking something funky */
void timer_handler(struct regs *r)
{
/* Increment our 'tick count' */
timer_ticks++;
/* Every 18 clocks (approximately 1 second), we will
* display a message on the screen */
if (timer_ticks % 18 == 0)
{
puts("One second has passed\n");
}
}
/* Sets up the system clock by installing the timer handler
* into IRQ0 */
void timer_install()
{
/* Installs 'timer_handler' to IRQ0 */
irq_install_handler(0, timer_handler);
}