Initial commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
obj/
|
||||
iso/
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 = .;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
@@ -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 (;;);
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user