Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • dh2-trung/operatingsystem
  • tl22-nguyen/operatingsystem
  • operatingsystem24-25/operatingsystem
3 results
Select Git revision
Show changes
Commits on Source (5)
Showing
with 412 additions and 5 deletions
# Learn Week 3 # Operating System Worksheet Submission
## Log file
## App/State Repository-Machine ## Description
Srd-shm This document contains the worksheet submissions and related lesson files for *Dao Hieu Trung*, a student from *UWE Bristol - Phenikaa Campus*.
## Unlock File Single - **Student Username**: DH2-TRUNG
\ No newline at end of file - **Student ID**: 23085493
## Worksheet 1
### Overview
The source code for Worksheet 1 is located in the `./src` directory.
To compile the programs, use the provided `Makefile` by running:
```bash
make
```
Once the programs are compiled, you can execute them one by one. For example, to run **Task 1**, use:
```bash
./task1
```
### Task 1
In this task, the memory layout of the program is explained. The sections are as follows:
- **`section .data`**: Used to store initialized variables.
- **`section .bss`**: Used to store uninitialized variables.
- **`section .text`**: This section contains the actual machine code (instructions) of the program, and in this case, the instructions are sourced from `driver.o`.
The program begins with `asm_main`, a global function that is typically called from C using `_start`. Below is an example of how `printf` is used to print a message:
```assembly
push msg
call printf
add esp, 4 ; Clean up the stack (removes 4 bytes)
```
In this example, `msg` is pushed onto the stack (which is 4 bytes). After calling `printf`, the stack is cleaned up by adjusting the stack pointer to release the 4 bytes.
### Task 2
#### 2.1 Equivalent C Code Comments
The assembly code in this worksheet mimics certain functions that would typically be found in C programming. To clarify the relationship between assembly and C, I have added comments to point out the equivalent C functions. For example:
- **Print the prompt**:
```assembly
push prompt
call printf
add esp, 4 ; Clean up the stack
```
Equivalent in C:
```c
printf(prompt);
```
- **Read the input**:
```assembly
push name ; Address of the buffer to store the input
push format ; Format string for scanf
call scanf
add esp, 8 ; Clean up the stack (2 arguments)
```
Equivalent in C:
```c
scanf(format, name);
```
#### 2.2 Loops
In assembly, loops are implemented with a mechanism similar to that of C. Loops in assembly typically involve setting up a counter, checking a condition, and then jumping to a specific part of the code based on that condition.
#### 2.3 Conditional Statements
Conditional statements in assembly are implemented using comparison and jump instructions, like `cmp` and `jne`. This can be directly mapped to if-else statements in C.
#### 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 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
OBJECTS = loader.o kmain.o
CC = gcc
CFLAGS = -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector \
-nostartfiles -nodefaultlibs -Wall -Wextra -Werror -c
LDFLAGS = -T link.ld -melf_i386
AS = nasm
ASFLAGS = -f elf
all: kernel.elf
kernel.elf: $(OBJECTS)
ld $(LDFLAGS) $(OBJECTS) -o kernel.elf
os.iso: kernel.elf
cp kernel.elf iso/boot/kernel.elf
genisoimage -R -b boot/grub/stage2_eltorito -no-emul-boot \
-boot-load-size 4 -A os -input-charset utf8 -quiet \
-boot-info-table -o os.iso iso
run: os.iso
bochs -f bochsrc.txt -q
%.o: %.c
$(CC) $(CFLAGS) $< -o $@
%.o: %.s
$(AS) $(ASFLAGS) $< -o $@
clean:
rm -rf *.o kernel.elf os.iso
File added
default=0
timeout=0
title os
kernel /boot/kernel.elf
\ No newline at end of file
File added
File added
File added
File added
ENTRY(loader) /* The name of the entry label */
SECTIONS {
. = 0x00100000; /* The code should be loaded at 1MB */
.text ALIGN(0x1000): /* Align at 4KB */
{
*(.text) /* All text sections from all files */
}
.rodata ALIGN(0x1000): /* Align at 4KB */
{
*(.rodata*) /* All read-only data sections from all files */
}
.data ALIGN(0x1000): /* Align at 4KB */
{
*(.data) /* All data sections from all files */
}
.bss ALIGN(0x1000): /* Align at 4KB */
{
*(COMMON) /* All COMMON sections from all files */
*(.bss) /* All bss sections from all files */
}
}
\ No newline at end of file
File added
File added
com1: enabled=1, mode=file, dev=com1.out
\ No newline at end of file
#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 */
}
#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 */
global enable_hardware_interrupts
enable_hardware_interrupts:
sti
ret
global disable_hardware_interrupts
disable_hardware_interrupts:
cli
ret
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
#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 */
#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;
}
}