Skip to content

Commit

Permalink
Add 2 chapters
Browse files Browse the repository at this point in the history
  • Loading branch information
optixx committed Jan 25, 2014
1 parent 1b54504 commit 43895a5
Show file tree
Hide file tree
Showing 17 changed files with 1,483 additions and 0 deletions.
15 changes: 15 additions & 0 deletions 05_idt/Makefile
@@ -0,0 +1,15 @@
OBJECTS = scrn.o gdt.o idt.o main.o start.o

all: kernel.bin

.c.o:
gcc -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions -nostdinc -fno-builtin -c $< -o $@

.s.o:
nasm -f aout -o $@ $<

kernel.bin: $(OBJECTS)
ld -T link.ld -o kernel.bin $(OBJECTS)

clean:
rm -f *.o kernel.bin
78 changes: 78 additions & 0 deletions 05_idt/gdt.c
@@ -0,0 +1,78 @@
#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();
}
57 changes: 57 additions & 0 deletions 05_idt/idt.c
@@ -0,0 +1,57 @@
#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)
{
idt[num].base_lo = (base & 0xFFFF);
idt[num].base_hi = (base >> 24) & 0xFF;
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();
}
25 changes: 25 additions & 0 deletions 05_idt/link.ld
@@ -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 = .;
}
58 changes: 58 additions & 0 deletions 05_idt/main.c
@@ -0,0 +1,58 @@
#include "system.h"

void *memcpy(void *dest, const void *src, size_t count)
{
const char *sp = (const char *)src;
char *dp = (char *)dest;
for(; count != 0; count--) *dp++ = *sp++;
return dest;
}

void *memset(void *dest, char val, size_t count)
{
char *temp = (char *)dest;
for( ; count != 0; count--) *temp++ = val;
return dest;
}

unsigned short *memsetw(unsigned short *dest, unsigned short val, size_t count)
{
unsigned short *temp = (unsigned short *)dest;
for( ; count != 0; count--) *temp++ = val;
return dest;
}

size_t strlen(const char *str)
{
size_t 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));
}

int main()
{
gdt_install();
idt_install();
init_video();
puts("Hello World!");
for (;;);
}
158 changes: 158 additions & 0 deletions 05_idt/scrn.c
@@ -0,0 +1,158 @@
#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();
}

0 comments on commit 43895a5

Please sign in to comment.