From c90c6dc69202b70d3a3085fbf8bf1c66a4e487a3 Mon Sep 17 00:00:00 2001 From: Tiberiu Chibici Date: Tue, 14 Sep 2021 18:29:04 +0300 Subject: [PATCH] CTAOS v1 --- .gitignore | 2 +- compile.bat | 22 +++ console.o | Bin 0 -> 2605 bytes gdt.c | 78 ++++++++ idt.c | 62 +++++++ include/console.h | 152 ++++++++++++++++ include/system.h | 104 +++++++++++ init.c | 37 ++++ irq.c | 118 ++++++++++++ isrs.c | 163 +++++++++++++++++ kernel.bin | Bin 0 -> 12288 bytes keyus.c | 209 +++++++++++++++++++++ link.ld | 25 +++ loader.asm | 449 ++++++++++++++++++++++++++++++++++++++++++++++ loader.o | Bin 0 -> 2226 bytes main.c | 65 +++++++ main.o | Bin 0 -> 12679 bytes timer.c | 52 ++++++ 18 files changed, 1537 insertions(+), 1 deletion(-) create mode 100644 compile.bat create mode 100644 console.o create mode 100644 gdt.c create mode 100644 idt.c create mode 100644 include/console.h create mode 100644 include/system.h create mode 100644 init.c create mode 100644 irq.c create mode 100644 isrs.c create mode 100644 kernel.bin create mode 100644 keyus.c create mode 100644 link.ld create mode 100644 loader.asm create mode 100644 loader.o create mode 100644 main.c create mode 100644 main.o create mode 100644 timer.c diff --git a/.gitignore b/.gitignore index fb98d2c..c13f0bc 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ # Compiled Object files *.slo *.lo -*.o +#*.o *.obj # Precompiled Headers diff --git a/compile.bat b/compile.bat new file mode 100644 index 0000000..8b34431 --- /dev/null +++ b/compile.bat @@ -0,0 +1,22 @@ +@echo off +rem The name of the loader assembly file (without extension, must be .asm): +set loader_name=loader + +rem NASM and DJGPP executable paths: +set nasm_path=C:\nasm +set djgpp_path=C:\DJGPP\bin + +@echo on +rem Compile loader +%nasm_path%\nasm.exe -f aout -o %loader_name%.o %loader_name%.asm +rem Compile main Kernel +%djgpp_path%\gcc.exe -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions -nostdinc -fno-builtin -I./include -c -o main.o main.c +rem OTHER GCC/NASM SOURCES GO HERE + +@echo on +%djgpp_path%\ld -T link.ld -o kernel.bin %loader_name%.o main.o +@echo . +@echo Done! + +@pause +copy C:\CTAOS\KERNEL.BIN C:\SHARE diff --git a/console.o b/console.o new file mode 100644 index 0000000000000000000000000000000000000000..33bd1c53303d04a1a4420d1ca286137933a1653b GIT binary patch literal 2605 zcmaJ@UuauZ96o7o)2>T*GmKDKHv4c3c4C~fldW{n*~yBOlBMk`w%esmZrao&x8&Z? z7PPi>#WY#L2lYjeZO)g8L_tQaP|~J2tcWrgolingbaL@gI z-|z4Fos)agW|IgV=N*L9F(lc%rZ#Fa%%|V%gnS4%$4$u-z_ob1!9KGO@B58rD82oL zaX%ZXo514@=+USa6RF+cE);VD@=-=e=$s4e)Q|g`_Q|hj>k3e`){%)qp`1)?CvUqT zUoNM!PoRf@awj^{M-^wfS8=6H#XbG^xN9CXNjV=#I$cj6`XljI@>sZ6&iu2iq$ zoc?sYn%N{hFjz_4>nZ6Ghw67QeQ3txqXu_-a_LS*nxk<9hGed}1Uz|*F1~{+-vKeT zT-%dNp#mCp;$L~hk$jMbY?4RH&r^`=_!u)uu|$ivq$&o^QI(uwO`Fb+@1M6pW{XHy zQ`INZ5yDZ@v$J_>sf-J9hG`EizP!pC4%1@mDWc87Qqrsf+ZC3CJJx?IXI75OnXhKu zF264yST7Wq)|Q=F0_*4d!u%F*N*~c+DVA#6ihrK&W&L$Ivt;L+oDai&YPqH-S1N)< zYi37b>R!#bn`>&|Ib_R+Sfiv|S2s>XL~&!sYOve2sWwHK`9{thrc1WnNgtqZG}4ol z#^nsFW>(spypEE#trXkQdBoOpNBG505GSI$uruzaszgQJ zj}m@4&NIjvmdMWLG~17)WN@%?BKUUQKwcI(+4-Dp(-Nn%W_iAVF5vtkm4p-rK}$*$LXy36gxAMH|SxT`C{@WdtdABk{7bRf{CWB z+K(!VFaItgw4|gxQ_Jqt*~Aa5F`~&YDmD1od04wT~QY@2anD@HTk&qYCmcp6z%X?5pKu=yA-|41I^W$l)Z@ZFi7^K=kN1$TNa# z7hDGr_kA9S`^JEHj!}W8h20rp_Xf}|7WpiYhoKLIb`FR~SpecWz82b_fp~p)fp~pY z_{MRMT|hjt0>mTl73hFKeL%eCFc9}05!@?+dsA>11oxTXt^n~It3W*O_rh*X*xeR( ze+s*GVON0<75A+c=rJH(Ujq=&(I~XdK)mKw!LJSPTqhmpvMe7qIm&vfFlUxLe*E7Z*)K^ASSr z=qH5^&d^hAWuCM!YE+8`hR4W3usjZB49z4bw=yFyl^DZ}N3f6qDLa+qVQ5xEf$R6cY}78veQ+zT9_P|*DTEEVBQyuI-D@+Y1)lVFU45} z(`#YYz`Q4z-yx~Sm|<#BH5?0@MD4^MrxW3zru+M%L=7b3ar~W8qk2$NN5VmKa0@r6 zg$D-VhCZ|HAu9dn#qHs2=yAl?Of{&5`VtXS4d@X)ZV(~&_XS=Ui0g@1u*8M*xVFLZ g80siXi)iW?VJlUKbwds5_@^brA`+xwwP7Op5AW>+cmMzZ literal 0 HcmV?d00001 diff --git a/gdt.c b/gdt.c new file mode 100644 index 0000000..12638ec --- /dev/null +++ b/gdt.c @@ -0,0 +1,78 @@ +#include + +/* 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(); +} + \ No newline at end of file diff --git a/idt.c b/idt.c new file mode 100644 index 0000000..e719dec --- /dev/null +++ b/idt.c @@ -0,0 +1,62 @@ +#include + +/* Defines an IDT entry */ +struct idt_entry +{ + unsigned short base_lo; + unsigned short sel; + unsigned char always0; + unsigned char flags; + 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(); +} diff --git a/include/console.h b/include/console.h new file mode 100644 index 0000000..4630fe8 --- /dev/null +++ b/include/console.h @@ -0,0 +1,152 @@ +#include + +#ifndef __CONSOLE_H +#define __CONSOLE_H +#define _ATTRIB 0x0F + +byte default_background, default_foreground; + + +// Change cursor position +void text_mode_cursor(int x, int y) +{ + unsigned temp = y*current_mode_width + x; + + outportb (0x3D4, 14); + outportb (0x3D5, temp >> 8); + outportb (0x3D4, 15); + outportb (0x3D5, temp); +} + +// Set the default colors; max is 0x0F +void set_default_colors(byte back, byte fore) +{ + if (back < 0x10) default_background = back; + if (fore < 0x10) default_foreground = fore; +} + + +// Clear screen, and set font to default font +void clrscr() +{ + byte font = default_foreground | (default_background<<4); + int i = 0; + for (i = 0; i < current_mode_width*current_mode_height; i++) + { TextVideoRam[2*i] = 0; + TextVideoRam[2*i+1] = font; + } + cursor_x = 0; cursor_y = 0; +} + +void scroll(int n) +{ + memcpy(TextVideoRam, + TextVideoRam+(current_mode_width*n*2), + 2*current_mode_width*(current_mode_height - n)); + + byte blank = default_foreground | (default_background<<4); + + int i; + for (i = current_mode_width*(current_mode_height-n); + i < current_mode_width*current_mode_height; i++){ + TextVideoRam[2*i] = 0; + TextVideoRam[2*i+1] = blank; + } +} + + +void _endl() +{ + cursor_x = 0; + if (++cursor_y >=25) { + cursor_y = 24; scroll(1); + } +} + + +// Put character on screen in specified position; can use different font colors +void putc_pos_font(int x, int y, char c, byte back, byte fore) +{ + TextVideoRam[2*(y*current_mode_width+x)] = c; + TextVideoRam[2*(y*current_mode_width+x)+1] = fore|(back<<4); +} +// Put character on screen in specified position; use default font colors +void putc_pos(int x, int y, char c) +{ + TextVideoRam[2*(y*current_mode_width+x)] = c; +} +// Put character on screen in the current cursor position; different font colors +void putc_font(char c, byte back, byte fore) +{ + if (cursor_x > current_mode_width) _endl(); + + if (c == '\n') {_endl(); return;}; + TextVideoRam[2*(cursor_y*current_mode_width+cursor_x)] = c; + TextVideoRam[2*(cursor_y*current_mode_width+cursor_x)+1] = fore|(back<<4); + cursor_x++; +} +// Put character on screen in the current cursor position; default font colors +void putc(char c) +{ + if (cursor_x > current_mode_width) _endl(); + + if (c == '\n') {_endl(); return;}; + TextVideoRam[2*(cursor_y*current_mode_width+cursor_x)] = c; + cursor_x++; +} + +// Put string on screen in specified position; can use different font colors +void puts_pos_font(int x, int y, char *str, byte back, byte fore) +{ + int i; + for (i = 0; str[i] != 0; i++) + putc_pos_font(x+i, y, str[i], back, fore); + +} + +// Put string on screen in specified position; use default font colors +void puts_pos(int x, int y, char *str) +{ + int i; + for (i = 0; str[i] != 0; i++) + putc_pos(x+i, y, str[i]); +} +void puts(char *str) +{ + int i; + for (i = 0; str[i] != 0; i++) + putc(str[i]); +} + +void puts_font(char *str, byte back, byte fore) +{ + int i; + for (i = 0; str[i] != 0; i++) + putc_font(str[i], back, fore); +} + +void put_hex(int x, int y, unsigned int alpha) +{ + char hex[] = "0123456789ABCDEF"; + char nr[9]; + int i; + for (i = 7; i >= 0; i--) { + nr[i] = hex[alpha%16]; + alpha /= 16; + } + nr[8] = 0; + puts_pos(x, y, nr); +} + +void put_bin (int x, int y, byte xz) +{ + int i; + char arr[9] = {0,0,0,0,0,0,0,0,0}; + for(i=7; i>=0; i--) { + arr[i] = (xz%2) + '0'; xz/=2; + } + puts_pos (x, y, arr); +} + + +#endif \ No newline at end of file diff --git a/include/system.h b/include/system.h new file mode 100644 index 0000000..87af581 --- /dev/null +++ b/include/system.h @@ -0,0 +1,104 @@ +#ifndef __SYSTEM_H +#define __SYSTEM_H + +// Data type declarations +typedef unsigned char byte; +typedef unsigned short word; +typedef unsigned int dword; + +/* This defines what the stack looks like after an ISR was running */ +typedef struct +{ + 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 */ +} regs; + + +byte *TextVideoRam; +int cursor_x, cursor_y; +int current_mode_width; +int current_mode_height; + +// System functions declaration +void system_init(); +void *memcpy(void *dest, const void *src, int count); +void *memset(void *dest, char val, int count); +unsigned short *memsetw(unsigned short *dest, unsigned short val, int count); +int strlen (const char *str); +byte inportb (word _port); +byte inb (word _port); +void outportb (word _port, byte _data); +void outb (word _port, byte _data) ; + +// GDT, IDT, ISRs, IRQ functions declarations +void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned char access, unsigned char gran); +void gdt_install(); + +void idt_set_gate(unsigned char num, unsigned long base, unsigned short sel, unsigned char flags); +void idt_install(); + +void isrs_install(); + +void irq_install_handler(int irq, void (*handler)(regs *r)); +void irq_uninstall_handler(int irq); +void irq_install(); + + +// Initialize system + + +void *memcpy(void *dest, const void *src, int 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, int count) +{ + char *temp = (char *)dest; + for( ; count != 0; count--) *temp++ = val; + return dest; +} + +unsigned short *memsetw(unsigned short *dest, unsigned short val, int count) +{ + unsigned short *temp = (unsigned short *)dest; + for( ; count != 0; count--) *temp++ = val; + return dest; +} + + +// strlen -- Get lenght of str +int strlen (const char *str) +{ + int i; + for (i = 0; str[i]!=0; i++) {} + return i; +} + + +byte inportb (word _port) { + byte rv; + __asm__ __volatile__ ("inb %1, %0" : "=a" (rv) : "dN" (_port)); + return rv; +} +byte inb (word _port) { + byte rv; + __asm__ __volatile__ ("inb %1, %0" : "=a" (rv) : "dN" (_port)); + return rv; +} + + +void outportb (word _port, byte _data) { + __asm__ __volatile__ ("outb %1, %0" : : "dN" (_port), "a" (_data)); +} +void outb (word _port, byte _data) { + __asm__ __volatile__ ("outb %1, %0" : : "dN" (_port), "a" (_data)); +} + + +#endif \ No newline at end of file diff --git a/init.c b/init.c new file mode 100644 index 0000000..9dcd6ef --- /dev/null +++ b/init.c @@ -0,0 +1,37 @@ +#include + + +void system_init() +{ + // Detect if color/monochrome screen + char c = (*(volatile unsigned short*)0x410)&0x30; + if (c==0x30) TextVideoRam = (byte *)0xb0000; + else TextVideoRam = (byte *)0xb8000; + + // Reset cursor, use 80x25 text video mode + current_mode_width = 80; + current_mode_height = 25; + cursor_x = cursor_y = 0; + + // Install GDT, IDT, ISRs and IRQs; Enable interrupts + gdt_install(); + idt_install(); + isrs_install(); + irq_install(); + __asm__ __volatile__ ("sti"); + + // Install PIT timer + timer_ticks = 0; + irq_install_handler(0, timer_handler); + timer_phase (100); + + // Install keyboard + kb_modifier_status = 0; + kb_lights_status = 0xFF; kb_update_LED(); + kb_lights_status = 0; kb_update_LED(); + irq_install_handler(1, kb_handler); + + // mouse_driver(); + + +} \ No newline at end of file diff --git a/irq.c b/irq.c new file mode 100644 index 0000000..4c1e861 --- /dev/null +++ b/irq.c @@ -0,0 +1,118 @@ +#include + +/* 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)(regs *r)) +{ + irq_routines[irq] = handler; +} + +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 (regs *r) +{ + /* This is a blank function pointer */ + void (*handler)(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(0x0A, 0x20); + + /* In either case, we need to send an EOI to the master + * interrupt controller too */ + outportb(0x20, 0x20); +} diff --git a/isrs.c b/isrs.c new file mode 100644 index 0000000..cf975eb --- /dev/null +++ b/isrs.c @@ -0,0 +1,163 @@ +#include +#include + +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); +} + + char *exception_messages[] = { + "Division By Zero Exception", + "Debug Exception", + "Non Maskable Interrupt Exception", + "Breakpoint Exception", + "Into Detected Overflow Exception", + "Out of Bounds Exception", + "Invalid Opcode Exception", + "No Coprocessor", + "Double Fault Exception", + "Coprocessor Segment Overrun Exception", + "Bad TSS Exception", + "Segment Not Present Exception", + "Stack Fault Exception", + "General Protection Fault Exception", + "Page Fault Exception", + "Unknown Interrupt Exception", + "Coprocessor Fault Exception", + "Alignment Check Exception", + "Machine Check Exception", + "Reserved Exception", + "Reserved Exception", + "Reserved Exception", + "Reserved Exception", + "Reserved Exception", + "Reserved Exception", + "Reserved Exception", + "Reserved Exception", + "Reserved Exception", + "Reserved Exception", + "Reserved Exception", + "Reserved Exception" +}; + + +/* 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(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.*/ + + // Put on the BSOD screen + set_default_colors (0x01, 0x0F); clrscr(); + puts (" Blue Screen Of Death\n"); + int i; for (i = 79; i>=0; i--) putc('='); + puts_pos_font (15, 2, "A fatal error has occured, CTA OS has been halted.", 0x01, 0x0C); + puts_pos_font (10, 4, "gs", 0x01, 0x0B); put_hex(15, 4, r->gs); + puts_pos_font (10, 5, "fs", 0x01, 0x0B); put_hex(15, 5, r->fs); + puts_pos_font (10, 6, "es", 0x01, 0x0B); put_hex(15, 6, r->es); + puts_pos_font (10, 7, "ds", 0x01, 0x0B); put_hex(15, 7, r->ds); + + puts_pos_font (40, 4, "edi", 0x01, 0x0B); put_hex(45, 4, r->edi); + puts_pos_font (40, 5, "esi", 0x01, 0x0B); put_hex(45, 5, r->esi); + puts_pos_font (40, 6, "ebp", 0x01, 0x0B); put_hex(45, 6, r->ebp); + puts_pos_font (40, 7, "esp", 0x01, 0x0B); put_hex(45, 7, r->esp); + + puts_pos_font (10, 9, "eax", 0x01, 0x0B); put_hex(15, 9, r->eax); + puts_pos_font (10, 10, "ebx", 0x01, 0x0B); put_hex(15, 10, r->ebx); + puts_pos_font (40, 9, "ecx", 0x01, 0x0B); put_hex(45, 9, r->ecx); + puts_pos_font (40, 10, "edx", 0x01, 0x0B); put_hex(45, 10, r->edx); + + puts_pos_font (10, 12, "int_no", 0x01, 0x0B); put_hex(17, 12, r->int_no); + puts_pos_font (10, 14, "Error code:", 0x01, 0x0B); put_hex(24, 14, r->err_code); + puts_pos_font (10, 15, "Error msg: ", 0x01, 0x0B); puts_pos(24, 15, exception_messages[r->int_no]); + + puts_pos_font (10, 17, "eip", 0x01, 0x0B); put_hex(17, 17, r->eip); + puts_pos_font (10, 18, "cs", 0x01, 0x0B); put_hex(17, 18, r->cs); + puts_pos_font (10, 19, "eflags", 0x01, 0x0B); put_hex(17, 19, r->eflags); + puts_pos_font (10, 20, "useresp", 0x01, 0x0B); put_hex(17, 20, r->useresp); + puts_pos_font (10, 21, "ss", 0x01, 0x0B); put_hex(17, 21, r->ss); + + puts_pos_font (29, 24, "!!! System Halted !!!", 0x01, 0x0C); + for (;;); + } +} diff --git a/kernel.bin b/kernel.bin new file mode 100644 index 0000000000000000000000000000000000000000..cb04e41ef38e9bee9548efd8929e9ac22b77c5f1 GIT binary patch literal 12288 zcmeHMe{@q-o_|S`Hnb@(g_IU4@QMLjpji3g0tPK@MZ^k$6j6&xXp^R)BsF;f{Dsxf zF=@AK$8l$8XV+c8v%9W)Ts%8xIH)sDLy$5$J63lc(RD`rx%W{7Ms}u1mF(wx-+LiZ z&dk}fyZ^Q4+`RAo+|T{~y7#_!@9j?nmrYP+34$;%V0_{EX?cP{7+bgg6RThYhEj{t zp~~#HWrCoLrPFEAP%8Otg72_R*n48{;NFva+1}w(8^E^+DbX142Mh<{AWn zB!ePpl2MtWNqLG@lT31KZ}$j{ApEQ z;?H8`U-)yP@*IB(0a4kfNjByCnpC3vsU}TQ9@nJF$}UZ^D?v>vRoXOZigLFml_@Sw zDpyu%(p1H%NmnTgHEEhsrAZaa3{9G@*fnW}Qm9EY6$rwX8TsNq@!9OeCQiBzKUicF z(!+O(|0wP+nCW@!s0GwFC`VEzmdZ>lvof(ysV!p5Mzh8c?CvIyr8& z)i3*1^mi@yrif$dYjW6nH#@97KbMO+Ctd-pg?J7runmBJd#kife%#Gp!UmbqJka<{>T2e?i!s;s}mf@}aq)P2s{jPZMwe{k`f9to~Hn^DgQab%2 zb?z-504MpOsj#>U0{2Vpo;X(ho5XqX;Ctf1BeA2_WvNHz_nW5mm6Fb5CZnKA@g}38 zAvS32D|G=E4|2=P-h1>N4C`eV_Yb47?iu=xT`1peS6wcL{eXe3=4mvgHg1CVoen$3 zvm6sa>TPm367PrySMXrY_AMdHNOMKpv|BUOZ<-r_FZq#a!fRTxnpYZH?!E6G8fS~f z$;05NiD+_I<5pJ+W}x0C9>Rna4?XNC-%pvmfse&*Gg}3By2lEm16R6MRaMQA@6=|G+4yrrv}!Qh5b^x+zVMnk3m# zfhorm)SJI&P`;i|vBy57xtVz5v5)xk?M)lSR|Z{a8Cg7$*XVG#*biZ)q1QAccGSLS zF!}+{R3__41eRz8S2%+!r0$#gP4isr`AkPEI{qi`NHfjSn+NlDBC-l(tG5A@;iw6m z(=C)|P;Btjj-f2))H#}yzto(3=Z;OhvmXZ3⪼e&sXfUgkh{5YIKOT*DYLx=j6pp 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 */ + 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 */ +}; + +unsigned char kbdus_shift[128] = +{ + 0, 27, '!', '@', '#', '$', '%', '^', '&', '*', /* 9 */ + '(', ')', '_', '+', '\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 */ +}; + + + +/* kb_modifier_status: + BIT | Description + ----+----------------------------------- + 0 | Left Shift + 1 | Right Shift + 2 | Left Alt + 3 | Right Alt + 4 | Left CTRL + 5 | Right CTRL + 6 | Fake Shift + 7 | Special */ +unsigned char kb_modifier_status; + +/* kb_lights_status + BIT | Description + ----+----------------------------------- + 0 | SCROLLOCK + 1 | NUMLOCK + 2 | CAPSLOCK */ +unsigned char kb_lights_status; + +void kb_update_LED() +{ + while ((inportb (0x64)&2)!=0); + outportb (0x60, 0xED); + + while ((inportb (0x64)&2)!=0); + outportb (0x60, kb_lights_status); +} + +/* kb_toggle_LED + Toggle NUM/CAPS/SCROLL LOCK LEDs. 0 to ignore. If all 0, reset; */ +void kb_toggle_LED(int NUM, int CAPS, int SCROLL) +{ + if (NUM) kb_lights_status ^= 1<<1; + if (CAPS) kb_lights_status ^= 1<<2; + if (SCROLL) kb_lights_status ^= 1; + if ((!NUM) && (!CAPS) && (!SCROLL)) kb_lights_status = 0; + kb_update_LED(); +} + +byte prev, scancode; +void kb_handler(regs *r) { + scancode = inportb(0x60); + + puts_pos(0, 0, "Key Pressed ");put_hex(30, 0, scancode); + + switch (scancode) { + // Special + case 0x0E: kb_modifier_status |= 1<<7; + break; + + // Left Shift or fake shift make + case 0x2A: if (kb_modifier_status && 128) kb_modifier_status |= 1<<6; + else kb_modifier_status |= 1; + break; + + // Left Shift or fake shift release + case 0xAA: if (kb_modifier_status && 128) kb_modifier_status &= (1<<6) ^0xFF; + else kb_modifier_status &= 1 ^0xFF; + break; + + // Right Shift make + case 0x36: kb_modifier_status |= 1<<1; + break; + + // Right shift release + case 0xB6: kb_modifier_status &= (1<<1) ^0xFF; + break; + + // Left Alt or Right Alt make + case 0x38: if (kb_modifier_status && 128) kb_modifier_status |= 1<<3; + else kb_modifier_status |= 1<<2; + break; + + // Left Alt or Right Alt release + case 0xB8: if (kb_modifier_status && 128) kb_modifier_status &= (1<<3) ^0xFF; + else kb_modifier_status &= (1<<2) ^0xFF; + break; + + // Left Ctrl or Right Ctrl make + case 0x1D: if (kb_modifier_status && 128) kb_modifier_status |= 1<<5; + else kb_modifier_status |= 1<<4; + break; + + // Left Ctrl or Right Ctrl release + case 0x9D: if (kb_modifier_status && 128) kb_modifier_status &= (1<<5) ^0xFF; + else kb_modifier_status &= (1<<5) ^0xFF; + break; + + // Caps, Num and Scroll (in this order) + case 0x3A: kb_toggle_LED (0,1,0); break; + case 0x45: kb_toggle_LED (1,0,0); break; + case 0x46: kb_toggle_LED (0,0,1); break; + + default: if (scancode & 0x80) break; + if (scancode != prev) kb_modifier_status &= (1<<7) ^0xFF; + if ((kb_modifier_status && 1) || (kb_modifier_status && 2)) + putc_pos(50, 0, kbdus_shift[scancode]); + else putc_pos (50, 0, kbdus[scancode]); + break; + } + + outportb(0x20, 0x20); +} + + +char kb_getch() +{ + regs *r = 0; + do { + prev = scancode; kb_handler(r); } + while (prev == scancode || prev+0x80 == scancode); + + + if ((kb_modifier_status && 1) || (kb_modifier_status && 2)) + return kbdus_shift[scancode]; + + return kbdus[scancode]; +} + +void kb_get_status() +{ + byte a = inportb(0x64); + puts_pos(0, 8, "KeyBrd Status: "); + put_bin(0, 9, a); +} \ No newline at end of file diff --git a/link.ld b/link.ld new file mode 100644 index 0000000..1699422 --- /dev/null +++ b/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 = .; +} \ No newline at end of file diff --git a/loader.asm b/loader.asm new file mode 100644 index 0000000..c5488fc --- /dev/null +++ b/loader.asm @@ -0,0 +1,449 @@ +[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 $ + + +; !!! GDT !!! +; 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! + + +; !!! IDT !!! +; Loads the IDT defined in '_idtp' +global _idt_load +extern _idtp +_idt_load: + lidt [_idtp] + ret + +; !!! ISRs !!! +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 + +_isr0: + cli + push byte 0; A normal ISR stub that pops a dummy error code to keep a + ; uniform stack frame + push byte 0 + jmp isr_common_stub +_isr1: + cli + push byte 0 + push byte 1 + jmp isr_common_stub +_isr2: + cli + push byte 0 + push byte 2 + jmp isr_common_stub +_isr3: + cli + push byte 0 + push byte 3 + jmp isr_common_stub +_isr4: + cli + push byte 0 + push byte 4 + jmp isr_common_stub +_isr5: + cli + push byte 0 + push byte 5 + jmp isr_common_stub +_isr6: + cli + push byte 0 + push byte 6 + jmp isr_common_stub +_isr7: + cli + push byte 0 + push byte 7 + jmp isr_common_stub +_isr8: + cli + push byte 8 + jmp isr_common_stub +_isr9: + cli + push byte 0 + push byte 9 + jmp isr_common_stub +_isr10: + cli + push byte 10 + jmp isr_common_stub +_isr11: + cli + push byte 11 + jmp isr_common_stub +_isr12: + cli + push byte 12 + jmp isr_common_stub +_isr13: + cli + push byte 13 + jmp isr_common_stub +_isr14: + cli + push byte 14 + jmp isr_common_stub +_isr15: + cli + push byte 0 + push byte 15 + jmp isr_common_stub +_isr16: + cli + push byte 0 + push byte 16 + jmp isr_common_stub +_isr17: + cli + push byte 0 + push byte 17 + jmp isr_common_stub +_isr18: + cli + push byte 0 + push byte 18 + jmp isr_common_stub +_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 + +extern _fault_handler + +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! + + +; !!! IRQ !!! +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 + push byte 32; Note that these don't push an error code on the stack: + ; We need to push a dummy error code + jmp irq_common_stub + +; 33: IRQ1 +_irq1: + cli + push byte 0 + push byte 33 + jmp irq_common_stub + +; 34: IRQ2 +_irq2: + cli + push byte 0 + push byte 34 + jmp irq_common_stub + +; 35: IRQ3 +_irq3: + cli + push byte 0 + push byte 35 + jmp irq_common_stub + +; 36: IRQ4 +_irq4: + cli + push byte 0 + push byte 36 + jmp irq_common_stub + +; 37: IRQ5 +_irq5: + cli + push byte 0 + push byte 37 + jmp irq_common_stub + +; 38: IRQ6 +_irq6: + cli + push byte 0 + push byte 38 + jmp irq_common_stub + +; 39: IRQ7 +_irq7: + cli + push byte 0 + push byte 39 + jmp irq_common_stub + +; 40: IRQ8 +_irq8: + cli + push byte 0 + push byte 40 + jmp irq_common_stub +; 41: IRQ9 +_irq9: + cli + push byte 0 + push byte 41 + jmp irq_common_stub + +; 42: IRQ10 +_irq10: + cli + push byte 0 + push byte 42 + jmp irq_common_stub + +; 43: IRQ11 +_irq11: + cli + push byte 0 + push byte 43 + jmp irq_common_stub + +; 44: IRQ12 +_irq12: + cli + push byte 0 + push byte 44 + jmp irq_common_stub + +; 45: IRQ13 +_irq13: + cli + push byte 0 + push byte 45 + jmp irq_common_stub + +; 46: IRQ14 +_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 + + + +SECTION .bss + resb 8192 ; This reserves 8KBytes of memory here +_sys_stack: \ No newline at end of file diff --git a/loader.o b/loader.o new file mode 100644 index 0000000000000000000000000000000000000000..273055864fdbfc3020c9fe3d5030b89eb1f801b3 GIT binary patch literal 2226 zcmb7_U2GIp6vyvwKeoWur9x@3&<_OUtIA9%+9=VrY?n=UyQS?C1a;VUx6r!Xb!N5+ z#0R5BgGSaq7=7@C`d~z3Lf}E-SB%Ju#*}DCG@`)(k!zwp7^B#S^?&X?PR97;r04v9 zbMBn~oin?$4Fz)Icu?r0Q;4w|jfb>|500-BLL$W6T=4WeZ8ahw7SnsLg?ZgCHx{bZ zs$4{h1e$4K^4xM!`MUCPF5xC^d-lk-%D7Jpb3Um=|_ecRZKN9 z%v39bOm*^4G*d^t{DmnbuPJJfm$|Y;eyM1w{7mUG`H9l7d{1ekJfrj$`Ib^K9g!!Q zmdjU|n&gX2x5}rPR>-4FD`k~8l$pcI+a+s-A4lu2j_c7fjO{O-viD`{&XId*) zGPTR4OdS#+4z<@uPDbAJKiKE544c#z$Q<3>lNA{^t8*sirRzrOTUToj1$v z-Yo0ASvGjHZ1iTi!<%K3H_K*kmMz{aTfJGfd9!qTv)t*;a@YT!We%S{zB{6}6=~{XpKZla% zHpEv5+G{7`dj!?#-MfyU`Y7U8=7Z4Rne)&^<`Oi32SV2$Lxh;00XH(A0Iy(v6}*~x z9^AqFHh2T`S@2fo55Xq$1#lnp7vKk&FM;FC-+_miuY)tpG{?itH^CFkHPC5hx?h{Q z1$vaZ3;HB8-Pd!>z0ecP_d{Q1PC#E{9)-Tn{0Q_E^I_;|X4>;z<|m*ZF+U5vfS}Li zCFmutzYqPEnbv<`UV#3{{0;PH=Bv=(n5lM?`44E7Ie;%n9R>DU2BjcwhBh&GKwFtN zL+PBZ>49!!-UaPujzf1a?}grrpji$=2NCq^L;p7RfhS72ycl;~kuTGRd*TgX#2`H5`q{tY|V3AMzDb zu~d5GAuBO7koHxh>CBiln9S^s$9&a2vFPsDh?R=Q6aD`!4mCs*n`#oPP|9MuDc9MqvY|4qxlWJDJ5=sfd8f*Et9*~jeJUH= zth)`}Zs>kP8yMQb&=!XFFtmxGT} ziPFqWsc6wdz@0iaEjl*s;MlZ*W0SvQlf7e;yJM5NW0SXIleJ@$GrG80cdIE;)ctBo Uob?J^`0riaV=ng3PCO?52JEfXCIA2c literal 0 HcmV?d00001 diff --git a/main.c b/main.c new file mode 100644 index 0000000..84c99d2 --- /dev/null +++ b/main.c @@ -0,0 +1,65 @@ +#include +#include +#include "gdt.c" +#include "idt.c" +#include "isrs.c" +#include "irq.c" +#include "timer.c" +#include "keyus.c" +#include "init.c" + + +/*void put_line_scroll(int line, char c) +{ + int i = 0; + while (TextVideoRam[2*(80*line+i)] != 0 && i<=80) ++i; + if (i>78) { + memcpy(TextVideoRam + (2*80*line), TextVideoRam + (2*80*line) + 2, (i+1)*2); + } + putc_pos(i, line, c); +}*/ + + +int main() +{ + system_init(); + set_default_colors (0x07, 0x04); + clrscr(); + + int i; + for (i=0;i<80;i++) putc_pos_font (i, 0, ' ', 0x02, 0x0F); + puts_pos_font (60, 0, "Uptime:", 0x02, 0x0E); cursor_y++; + + // Do other stuff + puts ("Testing puts...\nAnd this shoudl be new line\n"); + puts_font ("And this should be colored in another color ", 0x02, 0x0F); + puts ("<- colored font should be right about heree"); + + puts_pos (0, 23, "This text should be starting in position (0, 23)"); + puts_pos_font (0, 23, "THIS", 0x00, 0x09); + + puts_pos (5, 20, "<- The cursor should be blinking right here"); + putc_pos_font (3, 20, 0, 0x04, 0x0F); + text_mode_cursor(3, 20); + + + while (1) { + kb_get_status(); + + put_bin(60, 15, kb_modifier_status); + puts_pos_font (60, 16, "LShift", 0x07, 0x08 - (kb_modifier_status&1)*0x06); + puts_pos_font (60, 17, "RShift", 0x07, 0x08 - (kb_modifier_status&2)*0x06); + puts_pos_font (60, 18, "LAlt", 0x07, 0x08 - (kb_modifier_status&4)*0x06); + puts_pos_font (60, 19, "RAlt", 0x07, 0x08 - (kb_modifier_status&8)*0x06); + puts_pos_font (60, 20, "LCtrl", 0x07, 0x08 - (kb_modifier_status&16)*0x06); + puts_pos_font (60, 21, "RCtrl", 0x07, 0x08 - (kb_modifier_status&32)*0x06); + puts_pos_font (60, 22, "FakeShift", 0x07, 0x08 - (kb_modifier_status&64)*0x06); + puts_pos_font (60, 23, "Special", 0x07, 0x08 - (kb_modifier_status&128)*0x06); + + putc (kb_getch()); + } + + // do nothing + for(;;); + return 0; +} \ No newline at end of file diff --git a/main.o b/main.o new file mode 100644 index 0000000000000000000000000000000000000000..5f8a70a7c92fd8760df3ce0d3a328e44d42cbbee GIT binary patch literal 12679 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 literal 0 HcmV?d00001 diff --git a/timer.c b/timer.c new file mode 100644 index 0000000..3e09c41 --- /dev/null +++ b/timer.c @@ -0,0 +1,52 @@ +#include +int timer_ticks = 0; +int timer_hz; + +void timer_phase(int hz) +{ + int divisor = 1193180/hz; // Calculate the divisor + outportb(0x43, 0x36); // Set our command byte 0x36 + outportb(0x40, divisor&0xFF); // Set low byte + outportb(0x40, divisor>>8); // Set high byte + timer_hz = hz; +} + +void timer_clock (int x, int y, int secs) +{ + int s, m, h; + char arr[9] = {0,0,0,0,0,0,0,0,0}; + + s = secs%60; + m = (secs/60)%60; + h = secs/3600; + + arr[0] = (h/10)%10 + '0'; if (arr[0]=='0') arr[0] = ' '; + arr[1] = h%10 + '0'; + + arr[3] = (m/10) + '0'; + arr[4] = m%10 + '0'; + + arr[6] = s/10 + '0'; + arr[7] = s%10 + '0'; + + if (secs%2 == 0) { + arr[2] = ' '; + arr[5] = ' '; + } + else { + arr[2] = ':'; + arr[5] = ':'; + } + + puts_pos(x, y, arr); +} + + +void timer_handler(regs *r) +{ + timer_ticks++; + + if (timer_ticks % timer_hz) { + timer_clock (70, 0, timer_ticks / timer_hz); + } +} \ No newline at end of file