diff --git a/README.md b/README.md index 729d53dbe342f7196ce24fb3924991e97d4006ca..34ccc49d733c103ae9ebef29cdc2bae5a79f9dfe 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,41 @@ Conditional statements in assembly are implemented using comparison and jump ins #### 2.4 Added Variables In the program, I have added the variables `start_range` and `end_range` to define the range for summing values. -## Worksheet 2 +## Worksheet 2 part 1 The codebase for Worksheet 2 can be found in the `./os_worksheet_2` directory. +### Task 1 +It's not possible to find 0xCAFEBABE in the logQ.txt because it doesn't use hex to store registers. +Instead, you can find EAX=CAFEBABE +### Task 2 +Implement a simple sum_of_three in C using additions. +``` /* The C function */ + int sum_of_three(int arg1, int arg2, int arg3) + { + return arg1 + arg2 + arg3; + } + ``` +in the kmain.c +And add a makefile to support transfer control from loader.asm to C as developed in task 1. + +### Task 3 +#### Framebuffer and Serial Console Driver + +This project implements a simple framebuffer and serial port driver for output in an OS, along with a `printf` function that outputs formatted text to the framebuffer. + +#### Framebuffer Driver + +The framebuffer driver allows writing text to the screen with basic cursor manipulation. The main operations include writing characters to the screen, scrolling the screen when it reaches the bottom, and moving the cursor to the next line when a newline character (`\n`) is encountered. + +#### Key Functions: +- **`write`**: Writes characters from a buffer to the framebuffer. Handles newline characters and automatic scrolling. + +```c +int write(char *buf, unsigned int len); +``` + +## Task 4 +Partially completed + +## Worksheet 2 part 2 +I did the coding, more revise coming up. \ No newline at end of file diff --git a/os_worksheet_2/README.md b/os_worksheet_2/README.md deleted file mode 100644 index 2b2742fd17bb22ac50f93722e32856146d2b045e..0000000000000000000000000000000000000000 --- a/os_worksheet_2/README.md +++ /dev/null @@ -1,4 +0,0 @@ -Worksheet 2 -Task 1 -- It's not possible to find 0xCAFEBABE in the logQ.txt because it doesn't use hex to store registers. -Instead, you can find EAX=CAFEBABE \ No newline at end of file diff --git a/os_worksheet_2/logQ.txt b/os_worksheet_2/logQ.txt index 9bb53152d123bdf6d55f7aec63ec0819d8b840d7..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 Binary files a/os_worksheet_2/logQ.txt and b/os_worksheet_2/logQ.txt differ diff --git a/os_worksheet_2/source/drivers/bochsrc.txt b/os_worksheet_2/source/drivers/bochsrc.txt new file mode 100644 index 0000000000000000000000000000000000000000..307cd3bb5908f0b13fcfbe511dc5f8910a2cb96e --- /dev/null +++ b/os_worksheet_2/source/drivers/bochsrc.txt @@ -0,0 +1 @@ +com1: enabled=1, mode=file, dev=com1.out \ No newline at end of file diff --git a/os_worksheet_2/source/drivers/configuringTheLine.c b/os_worksheet_2/source/drivers/configuringTheLine.c new file mode 100644 index 0000000000000000000000000000000000000000..53cc0481699d435a93cd7f5e05b4bd23a5a5ff6d --- /dev/null +++ b/os_worksheet_2/source/drivers/configuringTheLine.c @@ -0,0 +1,65 @@ +#include "io.h" /* io.h is implemented in the section "Moving the cursor" */ + +/* The I/O ports */ +/* All the I/O ports are calculated relative to the data port. + * This is because all serial ports (COM1, COM2, COM3, COM4) have their ports + * in the same order, but they start at different values. + */ +#define SERIAL_COM1_BASE 0x3F8 /* COM1 base port address */ +#define SERIAL_DATA_PORT(base) (base) /* Data port for serial communication */ +#define SERIAL_FIFO_COMMAND_PORT(base) (base + 2) /* FIFO command port */ +#define SERIAL_LINE_COMMAND_PORT(base) (base + 3) /* Line command port */ +#define SERIAL_MODEM_COMMAND_PORT(base) (base + 4) /* Modem command port */ +#define SERIAL_LINE_STATUS_PORT(base) (base + 5) /* Line status port */ + +/* The I/O port commands */ +/* SERIAL_LINE_ENABLE_DLAB: + * Tells the serial port to expect first the highest 8 bits on the data port, + * then the lowest 8 bits will follow. + */ +#define SERIAL_LINE_ENABLE_DLAB 0x80 /* Command to enable DLAB (Divisor Latch Access Bit) */ + +/** + * serial_configure_baud_rate: + * Sets the speed of the data being sent. The default speed of a serial port is 115200 bits/s. + * The argument is a divisor of that number, hence the resulting speed becomes (115200 / divisor) bits/s. + * + * @param com The COM port to configure (COM1, COM2, etc.) + * @param divisor The divisor value for setting the baud rate + */ +void serial_configure_baud_rate(unsigned short com, unsigned short divisor) +{ + /* Enable DLAB (Divisor Latch Access Bit) */ + outb(SERIAL_LINE_COMMAND_PORT(com), SERIAL_LINE_ENABLE_DLAB); + + /* Send the high byte of the divisor to the data port */ + outb(SERIAL_DATA_PORT(com), (divisor >> 8) & 0x00FF); + + /* Send the low byte of the divisor to the data port */ + outb(SERIAL_DATA_PORT(com), divisor & 0x00FF); +} + +/** + * serial_configure_line: + * Configures the line of the given serial port. The port is set to have a + * data length of 8 bits, no parity bits, one stop bit, and break control + * disabled. + * + * @param com The serial port to configure (e.g., COM1, COM2, etc.) + */ +void serial_configure_line(unsigned short com) +{ + /* Bit: + * | 7 | 6 | 5 4 3 | 2 | 1 0 | + * Content: | d | b | prty | s | dl | + * Value: | 0 | 0 | 0 0 0 | 0 | 1 1 | = 0x03 + * + * Where: + * - d = Data bits (8 bits) + * - b = Break control (disabled) + * - prty = Parity bits (none) + * - s = Stop bits (1 stop bit) + * - dl = Data length (8 bits, value 0x03) + */ + outb(SERIAL_LINE_COMMAND_PORT(com), 0x03); /* Set the line configuration */ +} diff --git a/os_worksheet_2/source/drivers/hardware_interrupt_enabler.h b/os_worksheet_2/source/drivers/hardware_interrupt_enabler.h new file mode 100644 index 0000000000000000000000000000000000000000..5764e2c7b68ba2c781ab4e00905d9e051ef99e6a --- /dev/null +++ b/os_worksheet_2/source/drivers/hardware_interrupt_enabler.h @@ -0,0 +1,7 @@ +#ifndef INCLUDE_HARDWARE_INTERRUPT_ENABLER_H +#define INCLUDE_HARDWARE_INTERRUPT_ENABLER_H + +void enable_hardware_interrupts(); +void disable_hardware_interrupts(); + +#endif /* INCLUDE_HARDWARE_INTERRUPT_ENABLER_H */ diff --git a/os_worksheet_2/source/drivers/hardware_interrupt_enabler.s b/os_worksheet_2/source/drivers/hardware_interrupt_enabler.s new file mode 100644 index 0000000000000000000000000000000000000000..6977e1348a6ae15b72b9892368a93d51c877d617 --- /dev/null +++ b/os_worksheet_2/source/drivers/hardware_interrupt_enabler.s @@ -0,0 +1,11 @@ +global enable_hardware_interrupts + +enable_hardware_interrupts: + sti + ret + +global disable_hardware_interrupts + +disable_hardware_interrupts: + cli + ret diff --git a/os_worksheet_2/source/drivers/interrupt_handlers.h b/os_worksheet_2/source/drivers/interrupt_handlers.h new file mode 100644 index 0000000000000000000000000000000000000000..8535ad248696c09d3ec581a0b4d3e0b8c223304e --- /dev/null +++ b/os_worksheet_2/source/drivers/interrupt_handlers.h @@ -0,0 +1,11 @@ +global load_idt + +; load_idt - Loads the interrupt descriptor table (IDT). +; stack: [esp + 4] the address of the first entry in the IDT +; [esp] the return address + +load_idt: + mov ax, [esp + 4] ; Load the address of the first IDT entry into ax + lidt [eax] ; Load the IDT using the address stored in eax + ret ; Return to the caller + diff --git a/os_worksheet_2/source/drivers/interrupts.c b/os_worksheet_2/source/drivers/interrupts.c new file mode 100644 index 0000000000000000000000000000000000000000..bce2dfaf302d14d99a9222f5d6da0f4f7af22c96 --- /dev/null +++ b/os_worksheet_2/source/drivers/interrupts.c @@ -0,0 +1,52 @@ +#ifndef INCLUDE_INTERRUPTS +#define INCLUDE_INTERRUPTS + +#include "type.h" + +/* Interrupt Descriptor Table (IDT) */ +struct IDT { + u16int size; + u32int address; +} __attribute__((packed)); + +/* IDT Descriptor */ +struct IDTDescriptor { + /* The lowest 32 bits */ + u16int offset_low; // offset bits 0..15 + u16int segment_selector; // a code segment selector in GDT or LDT + + /* The highest 32 bits */ + u8int reserved; // Just 0 + u8int type_and_attr; // type and attributes + u16int offset_high; // offset bits 16..31 +} __attribute__((packed)); + +/* CPU state */ +struct cpu_state { + u32int eax; + u32int ebx; + u32int ecx; + u32int edx; + u32int ebp; + u32int esi; + u32int edi; +} __attribute__((packed)); + +/* Stack state */ +struct stack_state { + u32int error_code; + u32int eip; + u32int cs; + u32int eflags; +} __attribute__((packed)); + +/* Function declarations */ +void interrupt_handler(struct cpu_state cpu, u32int interrupt, struct stack_state stack); +void interrupts_install_idt(); +void load_idt(u32int idt_address); + +// Wrappers around ASM. +void interrupt_handler_33(); +void interrupt_handler_14(); + +#endif /* INCLUDE_INTERRUPTS */ diff --git a/os_worksheet_2/source/drivers/interrupts.h b/os_worksheet_2/source/drivers/interrupts.h new file mode 100644 index 0000000000000000000000000000000000000000..aebab86a513b29a42b336f57924916fd2066d056 --- /dev/null +++ b/os_worksheet_2/source/drivers/interrupts.h @@ -0,0 +1,89 @@ +#include "interrupts.h" +#include "pic.h" +#include "io.h" +#include "frame_buffer.h" +#include "keyboard.h" + +#define INTERRUPTS_DESCRIPTOR_COUNT 256 +#define INTERRUPTS_KEYBOARD 33 +#define INPUT_BUFFER_SIZE 256 + +u8int input_buffer[INPUT_BUFFER_SIZE]; +u8int buffer_index = 0; +struct IDTDescriptor idt_descriptors[INTERRUPTS_DESCRIPTOR_COUNT]; +struct IDT idt; +u32int BUFFER_COUNT; + +void interrupts_init_descriptor(s32int index, u32int address) +{ + idt_descriptors[index].offset_high = (address >> 16) & 0xFFFF; // offset bits 0..15 + idt_descriptors[index].offset_low = (address & 0xFFFF); // offset bits 16..31 + idt_descriptors[index].segment_selector = 0x08; // The second (code) segment selector in GDT: one segment is 64b + idt_descriptors[index].reserved = 0x00; // Reserved + + /* + * Bit layout: + * | 31 5 | 4 3 2 1 0 | + * Content: | offset high | reserved | P | DPL | S | D and GateType | 0 | + * P: If the handler is present in memory or not (1 = present, 0 = not present). Set to 0 for unused interrupts or for Paging. + * DPL: Descriptor Privilege Level, the privilege level the handler can be called from (0, 1, 2, 3). + * S: Storage Segment. Set to 0 for interrupt gates. + * D: Size of gate, (1 = 32 bits, 0 = 16 bits). + */ + idt_descriptors[index].type_and_attr = (0x01 << 7) | (0x00 << 6) | (0x00 << 5) | 0xE; // 32-bit interrupt gate +} + +void interrupts_install_idt() +{ + interrupts_init_descriptor(INTERRUPTS_KEYBOARD, (u32int)interrupt_handler_33); + idt.address = (s32int) &idt_descriptors; + idt.size = sizeof(struct IDTDescriptor) * INTERRUPTS_DESCRIPTOR_COUNT; + load_idt((s32int) &idt); + + // Remap the PICs + pic_remap(PIC_1_OFFSET, PIC_2_OFFSET); + + // Unmask keyboard interrupt (IRQ1) + outb(0x21, inb(0x21) & ~(1 << 1)); +} + +/* Interrupt handlers ********************************************************/ + +void interrupt_handler(__attribute__((unused)) struct cpu_state cpu, u32int interrupt, __attribute__((unused)) struct stack_state stack) +{ + u8int input; + u8int ascii; + + switch (interrupt) { + case INTERRUPTS_KEYBOARD: + while ((inb(0x64) & 1)) { + input = keyboard_read_scan_code(); + // Only process if it's not a break code + if (!(input & 0x80)) { + if (input <= KEYBOARD_MAX_ASCII) { + ascii = keyboard_scan_code_to_ascii(input); + if (ascii != 0) { + // We have detected a backspace + if (ascii == '\b') { + // Remove the last character + } + // We have detected a newline + else if (ascii == '\n') { + // Move our position to a newline + } + // We have detected a regular character + else { + // Add the new character to the display + } + } + } + } + buffer_index = (buffer_index + 1) % INPUT_BUFFER_SIZE; + } + pic_acknowledge(interrupt); + break; + + default: + break; + } +} diff --git a/os_worksheet_2/source/drivers/interupt_asm.s b/os_worksheet_2/source/drivers/interupt_asm.s new file mode 100644 index 0000000000000000000000000000000000000000..ed745bed8dfbea5418af1a6796131c3e070a1076 --- /dev/null +++ b/os_worksheet_2/source/drivers/interupt_asm.s @@ -0,0 +1,48 @@ +; Generic Interrupt Handler + +extern interrupt_handler + +%macro no_error_code_interrupt_handler 1 + global interrupt_handler_%1 +interrupt_handler_%1: + push dword 0 ; push 0 as error code + push dword %1 ; push the interrupt number + jmp common_interrupt_handler ; jump to the common handler +%endmacro + +%macro error_code_interrupt_handler 1 + global interrupt_handler_%1 +interrupt_handler_%1: + push dword %1 ; push the interrupt number + jmp common_interrupt_handler ; jump to the common handler +%endmacro + +common_interrupt_handler: ; the common parts of the generic interrupt handler + ; save the registers + push eax + push ebx + push ecx + push edx + push ebp + push esi + push edi + + ; call the C function + call interrupt_handler + + ; restore the registers + pop edi + pop esi + pop ebp + pop edx + pop ecx + pop ebx + pop eax + + ; restore the esp + add esp, 8 + + ; return to the code that got interrupted + iret + +no_error_code_interrupt_handler 33 ; create handler for interrupt 33 (keyboard) diff --git a/os_worksheet_2/source/drivers/io.h b/os_worksheet_2/source/drivers/io.h new file mode 100644 index 0000000000000000000000000000000000000000..5997e3dbe3e14cf5e3e8917181808fe44bf0acdf --- /dev/null +++ b/os_worksheet_2/source/drivers/io.h @@ -0,0 +1,12 @@ + #ifndef INCLUDE_IO_H + #define INCLUDE_IO_H + /** outb: + * Sends the given data to the given I/O port. Defined in io.s + * + * @param port The I/O port to send the data to + * @param data The data to send to the I/O port + */ + void outb(unsigned short port, unsigned char data); + #endif /* INCLUDE_IO_H */ + +unsigned char inb(unsigned short port); \ No newline at end of file diff --git a/os_worksheet_2/source/drivers/io.s b/os_worksheet_2/source/drivers/io.s new file mode 100644 index 0000000000000000000000000000000000000000..785a349ea530eb3570504b4ab18d39dacb41bcdd --- /dev/null +++ b/os_worksheet_2/source/drivers/io.s @@ -0,0 +1,47 @@ +global outb ; Make the label outb visible outside this file +; outb - send a byte to an I/O port +; Stack: +; [esp + 8] - the data byte +; [esp + 4] - the I/O port +; [esp] - return address + + +global inb + +; inb - Returns a byte from the given I/O port +; Stack: [esp + 4] The address of the I/O port +; [esp] The return address + +outb: + ; Move the data byte into the AL register + mov al, [esp + 8] ; Data byte to AL + ; Move the I/O port address into the DX register + mov dx, [esp + 4] ; I/O port address to DX + ; Send the data byte to the I/O port + out dx, al ; Send AL to the port in DX + ; Return to the calling function + ret + + + +inb: + ; Move the address of the I/O port to the dx register + mov dx, [esp + 4] + + ; Read a byte from the I/O port and store it in the al register + in al, dx + + ; Return to the calling function + ret + +; In file io.h + +/** + * inb: + * Read a byte from an I/O port. + * + * @param port The address of the I/O port + * @return The byte read from the I/O port + */ +unsigned char inb(unsigned short port); + diff --git a/os_worksheet_2/source/drivers/keyboard.c b/os_worksheet_2/source/drivers/keyboard.c new file mode 100644 index 0000000000000000000000000000000000000000..a1db3ba5fbcc298c3bc769f16c9a16decb69db75 --- /dev/null +++ b/os_worksheet_2/source/drivers/keyboard.c @@ -0,0 +1,107 @@ +#include "io.h" +#include "frame_buffer.h" + +#define KEYBOARD_DATA_PORT 0x60 + +/** + * read_scan_code: + * Reads a scan code from the keyboard. + * + * @return The scan code (NOT an ASCII character!) + */ +u8int keyboard_read_scan_code(void) { + return inb(KEYBOARD_DATA_PORT); +} + +/** + * keyboard_scan_code_to_ascii: + * Maps the scan code to an ASCII character for a standard US QWERTY keyboard. + * + * @param scan_code The scan code to convert. + * @return The corresponding ASCII character or 0 if unknown. + */ +u8int keyboard_scan_code_to_ascii(u8int scan_code) { + // Ignore key releases (scan codes with bit 7 set) + if (scan_code & 0x80) { + return 0; + } + + // Scan code to ASCII mapping for standard US QWERTY keyboard + switch(scan_code) { + // Numbers row + case 0x02: return '1'; + case 0x03: return '2'; + case 0x04: return '3'; + case 0x05: return '4'; + case 0x06: return '5'; + case 0x07: return '6'; + case 0x08: return '7'; + case 0x09: return '8'; + case 0x0A: return '9'; + case 0x0B: return '0'; + case 0x0C: return '-'; + case 0x0D: return '='; + case 0x0E: return '\b'; // Backspace + + // Top letter row + case 0x10: return 'q'; + case 0x11: return 'w'; + case 0x12: return 'e'; + case 0x13: return 'r'; + case 0x14: return 't'; + case 0x15: return 'y'; + case 0x16: return 'u'; + case 0x17: return 'i'; + case 0x18: return 'o'; + case 0x19: return 'p'; + case 0x1A: return '['; + case 0x1B: return ']'; + case 0x1C: return '\n'; // Enter + + // Middle letter row + case 0x1E: return 'a'; + case 0x1F: return 's'; + case 0x20: return 'd'; + case 0x21: return 'f'; + case 0x22: return 'g'; + case 0x23: return 'h'; + case 0x24: return 'j'; + case 0x25: return 'k'; + case 0x26: return 'l'; + case 0x27: return ';'; + case 0x28: return '\''; + case 0x29: return '`'; + + // Bottom letter row + case 0x2B: return '\\'; + case 0x2C: return 'z'; + case 0x2D: return 'x'; + case 0x2E: return 'c'; + case 0x2F: return 'v'; + case 0x30: return 'b'; + case 0x31: return 'n'; + case 0x32: return 'm'; + case 0x33: return ','; + case 0x34: return '.'; + case 0x35: return '/'; + case 0x39: return ' '; // Spacebar + + // Numpad + case 0x37: return '*'; // Numpad * + case 0x47: return '7'; + case 0x48: return '8'; + case 0x49: return '9'; + case 0x4A: return '-'; + case 0x4B: return '4'; + case 0x4C: return '5'; + case 0x4D: return '6'; + case 0x4E: return '+'; + case 0x4F: return '1'; + case 0x50: return '2'; + case 0x51: return '3'; + case 0x52: return '0'; + case 0x53: return '.'; + + default: return 0; // Unknown scan code + } +} diff --git a/os_worksheet_2/source/drivers/keyboard.h b/os_worksheet_2/source/drivers/keyboard.h new file mode 100644 index 0000000000000000000000000000000000000000..aebab86a513b29a42b336f57924916fd2066d056 --- /dev/null +++ b/os_worksheet_2/source/drivers/keyboard.h @@ -0,0 +1,89 @@ +#include "interrupts.h" +#include "pic.h" +#include "io.h" +#include "frame_buffer.h" +#include "keyboard.h" + +#define INTERRUPTS_DESCRIPTOR_COUNT 256 +#define INTERRUPTS_KEYBOARD 33 +#define INPUT_BUFFER_SIZE 256 + +u8int input_buffer[INPUT_BUFFER_SIZE]; +u8int buffer_index = 0; +struct IDTDescriptor idt_descriptors[INTERRUPTS_DESCRIPTOR_COUNT]; +struct IDT idt; +u32int BUFFER_COUNT; + +void interrupts_init_descriptor(s32int index, u32int address) +{ + idt_descriptors[index].offset_high = (address >> 16) & 0xFFFF; // offset bits 0..15 + idt_descriptors[index].offset_low = (address & 0xFFFF); // offset bits 16..31 + idt_descriptors[index].segment_selector = 0x08; // The second (code) segment selector in GDT: one segment is 64b + idt_descriptors[index].reserved = 0x00; // Reserved + + /* + * Bit layout: + * | 31 5 | 4 3 2 1 0 | + * Content: | offset high | reserved | P | DPL | S | D and GateType | 0 | + * P: If the handler is present in memory or not (1 = present, 0 = not present). Set to 0 for unused interrupts or for Paging. + * DPL: Descriptor Privilege Level, the privilege level the handler can be called from (0, 1, 2, 3). + * S: Storage Segment. Set to 0 for interrupt gates. + * D: Size of gate, (1 = 32 bits, 0 = 16 bits). + */ + idt_descriptors[index].type_and_attr = (0x01 << 7) | (0x00 << 6) | (0x00 << 5) | 0xE; // 32-bit interrupt gate +} + +void interrupts_install_idt() +{ + interrupts_init_descriptor(INTERRUPTS_KEYBOARD, (u32int)interrupt_handler_33); + idt.address = (s32int) &idt_descriptors; + idt.size = sizeof(struct IDTDescriptor) * INTERRUPTS_DESCRIPTOR_COUNT; + load_idt((s32int) &idt); + + // Remap the PICs + pic_remap(PIC_1_OFFSET, PIC_2_OFFSET); + + // Unmask keyboard interrupt (IRQ1) + outb(0x21, inb(0x21) & ~(1 << 1)); +} + +/* Interrupt handlers ********************************************************/ + +void interrupt_handler(__attribute__((unused)) struct cpu_state cpu, u32int interrupt, __attribute__((unused)) struct stack_state stack) +{ + u8int input; + u8int ascii; + + switch (interrupt) { + case INTERRUPTS_KEYBOARD: + while ((inb(0x64) & 1)) { + input = keyboard_read_scan_code(); + // Only process if it's not a break code + if (!(input & 0x80)) { + if (input <= KEYBOARD_MAX_ASCII) { + ascii = keyboard_scan_code_to_ascii(input); + if (ascii != 0) { + // We have detected a backspace + if (ascii == '\b') { + // Remove the last character + } + // We have detected a newline + else if (ascii == '\n') { + // Move our position to a newline + } + // We have detected a regular character + else { + // Add the new character to the display + } + } + } + } + buffer_index = (buffer_index + 1) % INPUT_BUFFER_SIZE; + } + pic_acknowledge(interrupt); + break; + + default: + break; + } +} diff --git a/os_worksheet_2/source/drivers/moveCursor.c b/os_worksheet_2/source/drivers/moveCursor.c new file mode 100644 index 0000000000000000000000000000000000000000..4d58a26784671b428580a14a1d7214055e730dca --- /dev/null +++ b/os_worksheet_2/source/drivers/moveCursor.c @@ -0,0 +1,27 @@ +#include "io.h" /* io.h is implemented in the section "Moving the cursor" */ + +/* The I/O ports */ +#define FB_COMMAND_PORT 0x3D4 /* Command port for framebuffer */ +#define FB_DATA_PORT 0x3D5 /* Data port for framebuffer */ + +/* The I/O port commands */ +#define FB_HIGH_BYTE_COMMAND 0x0E /* High byte command for framebuffer */ +#define FB_LOW_BYTE_COMMAND 0x0F /* Low byte command for framebuffer */ + +/** + * fb_move_cursor: + * Moves the cursor of the framebuffer to the given position. + * This function uses I/O ports 0x3D4 and 0x3D5 to move the cursor. + * + * @param pos The new position of the cursor. + */ +void fb_move_cursor(unsigned short pos) +{ + /* Move the high byte of the position to the data port */ + outb(FB_COMMAND_PORT, FB_HIGH_BYTE_COMMAND); + outb(FB_DATA_PORT, ((pos >> 8) & 0x00FF)); /* Send the high byte */ + + /* Move the low byte of the position to the data port */ + outb(FB_COMMAND_PORT, FB_LOW_BYTE_COMMAND); + outb(FB_DATA_PORT, pos & 0x00FF); /* Send the low byte */ +} diff --git a/os_worksheet_2/source/drivers/pic.h b/os_worksheet_2/source/drivers/pic.h new file mode 100644 index 0000000000000000000000000000000000000000..b1ceccc0c4f488f0288d7503b49037f0bd325c4a --- /dev/null +++ b/os_worksheet_2/source/drivers/pic.h @@ -0,0 +1,40 @@ +#ifndef INCLUDE_PIC_H +#define INCLUDE_PIC_H + +#include "drivers/type.h" + +/* I/O Ports */ +#define PIC_1 0x20 /* I/O base address for master PIC */ +#define PIC_2 0xA0 /* I/O base address for slave PIC */ + +#define PIC_1_COMMAND PIC_1 +#define PIC_1_DATA (PIC_1 + 1) +#define PIC_2_COMMAND PIC_2 +#define PIC_2_DATA (PIC_2 + 1) + +#define PIC_1_OFFSET 0x20 +#define PIC_2_OFFSET 0x28 +#define PIC_2_END PIC_2_OFFSET + 7 + +#define PIC_1_COMMAND_PORT 0x20 +#define PIC_2_COMMAND_PORT 0xA0 +#define PIC_ACKNOWLEDGE 0x20 + +/* PIC Command Word Definitions */ +#define PIC_ICW1_ICW4 0x01 /* ICW4 (not) needed */ +#define PIC_ICW1_SINGLE 0x02 /* Single (cascade) mode */ +#define PIC_ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */ +#define PIC_ICW1_LEVEL 0x08 /* Level triggered (edge) mode */ +#define PIC_ICW1_INIT 0x10 /* Initialization required! */ + +#define PIC_ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */ +#define PIC_ICW4_AUTO 0x02 /* Auto (normal) EOI */ +#define PIC_ICW4_BUF_SLAVE 0x08 /* Buffered mode (slave) */ +#define PIC_ICW4_BUF_MASTER 0x0C /* Buffered mode (master) */ +#define PIC_ICW4_SFNM 0x10 /* Special fully nested (not) */ + +/* Function Declarations */ +void pic_remap(s32int offset1, s32int offset2); +void pic_acknowledge(u32int interrupt); + +#endif /* INCLUDE_PIC_H */ diff --git a/os_worksheet_2/source/drivers/types.h b/os_worksheet_2/source/drivers/types.h new file mode 100644 index 0000000000000000000000000000000000000000..f71a383c955e0726e8a951b54722c8b2d7003d0c --- /dev/null +++ b/os_worksheet_2/source/drivers/types.h @@ -0,0 +1,16 @@ +#ifndef INCLUDE_TYPES_H +#define INCLUDE_TYPES_H + +typedef unsigned int u32int; +typedef int s32int; +typedef unsigned short u16int; +typedef short s16int; +typedef unsigned char u8int; +typedef char s8int; + +/* Frame buffer colors */ +#define BLACK 0 +#define BLUE 1 +#define LIGHT_GREY 7 + +#endif diff --git a/os_worksheet_2/source/drivers/write.c b/os_worksheet_2/source/drivers/write.c new file mode 100644 index 0000000000000000000000000000000000000000..eb5e51f11f10ba7d0a83595a9f6b68b966f12e23 --- /dev/null +++ b/os_worksheet_2/source/drivers/write.c @@ -0,0 +1,131 @@ +#include "io.h" + +/* Define constants for screen dimensions */ +#define SCREEN_WIDTH 80 +#define SCREEN_HEIGHT 25 +#define FB_BASE_ADDR 0x000B8000 + +/* Function to move the cursor (already provided in previous code) */ +extern void fb_move_cursor(unsigned short pos); + +/* Function to write a character to the framebuffer */ +int write(char *buf, unsigned int len) { + unsigned int i; + static unsigned short cursor_pos = 0; // Keep track of the cursor position + + for (i = 0; i < len; i++) { + char c = buf[i]; + + if (c == '\n') { + /* Handle newline by moving the cursor to the next line */ + cursor_pos = (cursor_pos / SCREEN_WIDTH + 1) * SCREEN_WIDTH; + } else { + /* Write the character at the current cursor position */ + *((volatile unsigned short*) (FB_BASE_ADDR + 2 * cursor_pos)) = (unsigned short) c | 0x0F00; // White foreground, black background + cursor_pos++; + } + + /* If the cursor is at the end of the screen, scroll */ + if (cursor_pos >= SCREEN_WIDTH * SCREEN_HEIGHT) { + /* Scroll the screen up one row */ + for (unsigned int j = 0; j < (SCREEN_HEIGHT - 1) * SCREEN_WIDTH; j++) { + *((volatile unsigned short*) (FB_BASE_ADDR + 2 * j)) = *((volatile unsigned short*) (FB_BASE_ADDR + 2 * (j + SCREEN_WIDTH))); + } + /* Clear the last row */ + for (unsigned int j = (SCREEN_HEIGHT - 1) * SCREEN_WIDTH; j < SCREEN_WIDTH * SCREEN_HEIGHT; j++) { + *((volatile unsigned short*) (FB_BASE_ADDR + 2 * j)) = 0; + } + cursor_pos = (SCREEN_HEIGHT - 1) * SCREEN_WIDTH; /* Move cursor to the last row */ + } + + /* Move the cursor to the new position */ + fb_move_cursor(cursor_pos); + } + + return len; /* Return the number of characters written */ +} + +/* Helper function to convert an integer to a string */ +void itoa(int num, char *str, int base) { + int i = 0; + int isNegative = 0; + + // Handle 0 explicitly, otherwise empty string is printed + if (num == 0) { + str[i++] = '0'; + str[i] = '\0'; + return; + } + + // Handle negative numbers only if base is 10 + if (num < 0 && base == 10) { + isNegative = 1; + num = -num; + } + + // Process individual digits + while (num != 0) { + int rem = num % base; + str[i++] = (rem > 9) ? (rem - 10) + 'a' : rem + '0'; + num = num / base; + } + + // Append negative sign for negative numbers + if (isNegative) + str[i++] = '-'; + + str[i] = '\0'; // Null-terminate string + + // Reverse the string + int start = 0; + int end = i - 1; + while (start < end) { + char temp = str[start]; + str[start] = str[end]; + str[end] = temp; + start++; + end--; + } +} + +/* The printf function */ +int printf(const char *format, ...) { + va_list args; + va_start(args, format); + char buf[1024]; // Buffer to hold the formatted output + int buf_index = 0; + int len = 0; + + while (*format != '\0') { + if (*format == '%') { + format++; // Skip the '%' character + + if (*format == 'd') { // Handle integers + int num = va_arg(args, int); + char str[32]; + itoa(num, str, 10); // Convert integer to string + for (int i = 0; str[i] != '\0'; i++) { + buf[buf_index++] = str[i]; + } + len += buf_index; + } + else if (*format == 's') { // Handle strings + char *str = va_arg(args, char*); + while (*str != '\0') { + buf[buf_index++] = *str++; + } + len += buf_index; + } + // Add other format specifiers like %x for hexadecimal, etc. + } else { + buf[buf_index++] = *format; // Regular character, copy to buffer + } + format++; + } + + // Write the buffered string to the framebuffer + write(buf, buf_index); + va_end(args); + + return len; +} diff --git a/os_worksheet_2/source/drivers/writeText.c b/os_worksheet_2/source/drivers/writeText.c new file mode 100644 index 0000000000000000000000000000000000000000..3ddea754104c142e2fe8fc596deef19595424e49 --- /dev/null +++ b/os_worksheet_2/source/drivers/writeText.c @@ -0,0 +1,20 @@ + /** fb_write_cell: + * Writes a character with the given foreground and background to position i + * in the framebuffer. + * + * @param i The location in the framebuffer + * @param c The character + * @param fg The foreground color + * @param bg The background color + */ + void fb_write_cell(unsigned int i, char c, unsigned char fg, unsigned char bg) +{ + fb[i] = c; + fb[i + 1] = ((fg & 0x0F) << 4) | (bg & 0x0F); +} + +int main() { + #define FB_GREEN 2 + #define FB_DARK_GREY 8 + fb_write_cell(0, ’A’, FB_GREEN, FB_DARK_GREY); +} \ No newline at end of file