Compare commits
No commits in common. "9711ebb62c1c389635f10218bbb614338a0b0dc2" and "842ae8b49d29cda3deec3fec56cbfcfc92004f13" have entirely different histories.
9711ebb62c
...
842ae8b49d
14
Makefile
14
Makefile
@ -1,11 +1,11 @@
|
|||||||
ASM=nasm
|
ASM=nasm
|
||||||
BIN=bin
|
BIN=bin
|
||||||
CC=i686-elf-gcc
|
CFLAGS=-ffreestanding -fno-pie -m32
|
||||||
CFLAGS=-ffreestanding -fno-pie -ansi
|
CC=gcc
|
||||||
LD=i686-elf-ld
|
LD=ld
|
||||||
LDFLAGS=-Ttext 0x1000 --oformat binary
|
LDFLAGS=-melf_i386 -Ttext 0x1000 --oformat binary
|
||||||
|
|
||||||
OBJ=kernel/kernel_entry.o kernel/kernel.o kernel/ports.o kernel/util.o drivers/screen.o
|
OBJ=kernel/kernel_entry.o kernel/kernel.o
|
||||||
|
|
||||||
all: os-image
|
all: os-image
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ os-image: boot/boot_sect.bin kernel/kernel.bin
|
|||||||
kernel/kernel.bin: $(OBJ)
|
kernel/kernel.bin: $(OBJ)
|
||||||
$(LD) $(LDFLAGS) $^ -o $@
|
$(LD) $(LDFLAGS) $^ -o $@
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c ${HEADERS}
|
||||||
$(CC) $(CFLAGS) -c $< -o $@
|
$(CC) $(CFLAGS) -c $< -o $@
|
||||||
|
|
||||||
%.o: %.asm
|
%.o: %.asm
|
||||||
@ -29,5 +29,5 @@ kernel/kernel.bin: $(OBJ)
|
|||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf boot/*.bin kernel/*.bin
|
rm -rf boot/*.bin boot/*.o
|
||||||
rm -rf kernel/*.o boot/*.o drivers/*.o
|
rm -rf kernel/*.o boot/*.o drivers/*.o
|
||||||
|
@ -4,14 +4,7 @@ This is code that I've been writing for a basic kernel from the [Writing a Simpl
|
|||||||
|
|
||||||
Compiling
|
Compiling
|
||||||
---------
|
---------
|
||||||
I cross-compile from `x86_64` to `i686` on my machine. You will require `i686-elf-gcc` and `nasm` (as well as all the rest of the GNU binutils for `i686`) and you can run the final image using [QEMU](https://www.qemu.org/). To compile just run `make`.
|
I cross-compile from `x86_64` to `x86` (64 to 32-bit) on my machine, but all the code is plain `x86`. You will require `gcc` and `nasm` (as well as the normal GNU core utils) and you can run the final image using [QEMU](https://www.qemu.org/). To compile just run `make`.
|
||||||
|
|
||||||
Code Structure
|
|
||||||
--------------
|
|
||||||
- `boot/`: code necessary for the boot process (i.e. loading the kernel and switching to 32-bit protected mode).
|
|
||||||
- `kernel/`: code related to the kernel itself.
|
|
||||||
- `drivers/`: interface code that simplifies interaction with hardware devices.
|
|
||||||
- `bin/`: binary directory where final `os-image` is stored.
|
|
||||||
|
|
||||||
License
|
License
|
||||||
-------
|
-------
|
||||||
|
@ -10,7 +10,7 @@ KERNEL_OFFSET: equ 0x1000 ; kernel memory offset
|
|||||||
call load_kernel ; load our kernel from memory
|
call load_kernel ; load our kernel from memory
|
||||||
call switch_to_pm ; switch to 32-bit protected mode
|
call switch_to_pm ; switch to 32-bit protected mode
|
||||||
|
|
||||||
;jmp $
|
jmp $
|
||||||
|
|
||||||
%include "print.asm"
|
%include "print.asm"
|
||||||
%include "print32.asm"
|
%include "print32.asm"
|
||||||
@ -25,7 +25,7 @@ load_kernel:
|
|||||||
call print_string
|
call print_string
|
||||||
|
|
||||||
mov bx, KERNEL_OFFSET ; address to load the kernel to
|
mov bx, KERNEL_OFFSET ; address to load the kernel to
|
||||||
mov dh, 3 ; number of sectors to load
|
mov dh, 1 ; number of sectors to load
|
||||||
mov dl, [BOOT_DRIVE] ; drive to load from
|
mov dl, [BOOT_DRIVE] ; drive to load from
|
||||||
call disk_load
|
call disk_load
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ BEGIN_PM:
|
|||||||
|
|
||||||
call KERNEL_OFFSET
|
call KERNEL_OFFSET
|
||||||
|
|
||||||
;jmp $
|
jmp $
|
||||||
|
|
||||||
;;;
|
;;;
|
||||||
; Data
|
; Data
|
||||||
|
131
drivers/screen.c
131
drivers/screen.c
@ -1,131 +0,0 @@
|
|||||||
#include "screen.h"
|
|
||||||
|
|
||||||
#include "../kernel/ports.h"
|
|
||||||
#include "../kernel/util.h"
|
|
||||||
|
|
||||||
int get_screen_offset(int col, int row) {
|
|
||||||
return (row * MAX_COLS + col) * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
int get_cursor() {
|
|
||||||
/*
|
|
||||||
* screens use their control register as an index to
|
|
||||||
* select their internal registers of which:
|
|
||||||
* - 14: high byte of the cursor's offset
|
|
||||||
* - 15: low byte of the cursor's offset
|
|
||||||
*/
|
|
||||||
port_byte_out(REG_SCREEN_CTRL, 14);
|
|
||||||
/* get the high byte and shift it a byte */
|
|
||||||
int offset = port_byte_in(REG_SCREEN_DATA) << 8;
|
|
||||||
port_byte_out(REG_SCREEN_CTRL, 15);
|
|
||||||
/* now add the low byte with the free space from before */
|
|
||||||
offset += port_byte_in(REG_SCREEN_DATA);
|
|
||||||
|
|
||||||
/* remember: each cell is two bytes, not one */
|
|
||||||
return offset * 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_cursor(int offset) {
|
|
||||||
/* convert from cell offset to char offset */
|
|
||||||
offset /= 2;
|
|
||||||
|
|
||||||
port_byte_out(REG_SCREEN_CTRL, 14);
|
|
||||||
port_byte_out(REG_SCREEN_DATA, (unsigned char)(offset >> 8));
|
|
||||||
port_byte_out(REG_SCREEN_CTRL, 15);
|
|
||||||
port_byte_out(REG_SCREEN_DATA, (unsigned char)offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
int handle_scrolling(int cursor_offset) {
|
|
||||||
/* if within the screen then don't modify */
|
|
||||||
if(cursor_offset < MAX_ROWS * MAX_COLS * 2)
|
|
||||||
return cursor_offset;
|
|
||||||
|
|
||||||
/* translate rows back by one */
|
|
||||||
unsigned int i;
|
|
||||||
for(i = 1; i < MAX_ROWS; ++i)
|
|
||||||
{
|
|
||||||
memory_copy((char*)get_screen_offset(0, i) + VIDEO_ADDRESS,
|
|
||||||
(char*)get_screen_offset(0, i - 1) + VIDEO_ADDRESS,
|
|
||||||
MAX_COLS * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set last line to blank */
|
|
||||||
char *last_line = (char*)get_screen_offset(0, MAX_ROWS - 1) + VIDEO_ADDRESS;
|
|
||||||
for(i = 0; i < MAX_COLS * 2; ++i)
|
|
||||||
last_line[i] = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* move the cursor offset to the last row instead of
|
|
||||||
* off the screen
|
|
||||||
*/
|
|
||||||
cursor_offset -= MAX_COLS * 2;
|
|
||||||
|
|
||||||
/* updated cursor position */
|
|
||||||
return cursor_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_char(char c, int col, int row, char attr_byte) {
|
|
||||||
unsigned char *vidmem = (unsigned char*) VIDEO_ADDRESS;
|
|
||||||
|
|
||||||
if(!attr_byte)
|
|
||||||
attr_byte = WHITE_ON_BLACK;
|
|
||||||
|
|
||||||
/* get the offset of the screen location specified */
|
|
||||||
int offset;
|
|
||||||
/*
|
|
||||||
* if row & col are non-negative then they can be used
|
|
||||||
* as an offset
|
|
||||||
*/
|
|
||||||
if(col >= 0 && row >= 0)
|
|
||||||
offset = get_screen_offset(col, row);
|
|
||||||
/* otherwise use the cursor's current location */
|
|
||||||
else
|
|
||||||
offset = get_cursor();
|
|
||||||
|
|
||||||
/* handle a newline */
|
|
||||||
if(c == '\n')
|
|
||||||
{
|
|
||||||
int rows = offset / (2 * MAX_COLS);
|
|
||||||
offset = get_screen_offset(79,rows);
|
|
||||||
}
|
|
||||||
/* else set the character */
|
|
||||||
else
|
|
||||||
{
|
|
||||||
vidmem[offset] = c;
|
|
||||||
vidmem[offset + 1] = attr_byte;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* update the offset to the next cell */
|
|
||||||
offset += 2;
|
|
||||||
/* set scrolling adjustment */
|
|
||||||
offset = handle_scrolling(offset);
|
|
||||||
/* update the cursor position */
|
|
||||||
set_cursor(offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
void print_at(const char *msg, int col, int row) {
|
|
||||||
if(col >= 0 && row >= 0)
|
|
||||||
set_cursor(get_screen_offset(col, row));
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for(i = 0; msg[i] != 0; ++i)
|
|
||||||
print_char(msg[i], col, row, WHITE_ON_BLACK);
|
|
||||||
}
|
|
||||||
|
|
||||||
void print(const char *msg) {
|
|
||||||
print_at(msg, -1, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear_screen() {
|
|
||||||
int row, col;
|
|
||||||
|
|
||||||
for(row = 0; row < MAX_ROWS; ++row)
|
|
||||||
{
|
|
||||||
for(col = 0; col < MAX_COLS; ++col)
|
|
||||||
{
|
|
||||||
print_char(' ', col, row, WHITE_ON_BLACK);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set_cursor(get_screen_offset(0, 0));
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#define VIDEO_ADDRESS 0xb8000
|
|
||||||
#define MAX_ROWS 25
|
|
||||||
#define MAX_COLS 80
|
|
||||||
|
|
||||||
#define WHITE_ON_BLACK 0x0f
|
|
||||||
|
|
||||||
#define REG_SCREEN_CTRL 0x3d4
|
|
||||||
#define REG_SCREEN_DATA 0x3d5
|
|
||||||
|
|
||||||
void print_char(char c, int col, int row, char attr_byte);
|
|
||||||
void print_at(const char *msg, int col, int row);
|
|
||||||
void print(const char *msg);
|
|
||||||
void clear_screen();
|
|
@ -1,6 +1,10 @@
|
|||||||
#include "../drivers/screen.h"
|
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
clear_screen();
|
/*
|
||||||
print("Kernel initialized successfully!\n");
|
* point to first text cell of the video memory
|
||||||
|
*/
|
||||||
|
char *video_memory = (char*) 0xb8000;
|
||||||
|
/*
|
||||||
|
* store the character 'X' there
|
||||||
|
*/
|
||||||
|
*video_memory = 'X';
|
||||||
}
|
}
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
unsigned char port_byte_in(unsigned short port) {
|
|
||||||
unsigned char res;
|
|
||||||
/*
|
|
||||||
* `"=a" (res)' means: put result of `al' into `res' variable
|
|
||||||
* `"d" (port)' means: load `port' into `dx'.
|
|
||||||
*/
|
|
||||||
__asm__("in %%dx, %%al" : "=a" (res) : "d" (port));
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void port_byte_out(unsigned short port, unsigned char data) {
|
|
||||||
__asm__("out %%al, %%dx" : : "a" (data), "d" (port));
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned short port_word_in(unsigned short port) {
|
|
||||||
unsigned short res;
|
|
||||||
__asm__("in %%dx, %%ax" : "=a" (res) : "d" (port));
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void port_word_out(unsigned short port, unsigned short data) {
|
|
||||||
__asm__("out %%ax, %%dx" : : "a" (data), "d" (port));
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
/*
|
|
||||||
* return a byte from a port.
|
|
||||||
*/
|
|
||||||
unsigned char port_byte_in(unsigned short port);
|
|
||||||
/*
|
|
||||||
* write a byte of data to a port.
|
|
||||||
*/
|
|
||||||
void port_byte_out(unsigned short port, unsigned char data);
|
|
||||||
/*
|
|
||||||
* return a word of data from a port.
|
|
||||||
*/
|
|
||||||
unsigned short port_word_in(unsigned short port);
|
|
||||||
/*
|
|
||||||
* write a word of data to a port.
|
|
||||||
*/
|
|
||||||
void port_word_out(unsigned short port, unsigned short data);
|
|
@ -1,7 +0,0 @@
|
|||||||
void memory_copy(char *src, char *dest, unsigned int n) {
|
|
||||||
unsigned int i;
|
|
||||||
for(i = 0; i < n; ++i)
|
|
||||||
{
|
|
||||||
dest[i] = src[i];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
void memory_copy(char *src, char *dest, unsigned int n);
|
|
Loading…
Reference in New Issue
Block a user