Add separate bootsector and stage2, add VGA text mode driver

This commit is contained in:
mintsuki 2019-05-30 15:59:25 +02:00
parent 276928841a
commit 3c4c1a878a
10 changed files with 573 additions and 35 deletions

View File

@ -29,12 +29,15 @@ OBJ := $(C_FILES:.c=.o)
all: qloader2.bin
qloader2.bin: $(OBJ)
$(LD) $(LDFLAGS) $(INTERNAL_LDFLAGS) $(OBJ) -o $@
qloader2.bin: bootsect/bootsect.bin $(OBJ)
$(LD) $(LDFLAGS) $(INTERNAL_LDFLAGS) $(OBJ) -o stage2.bin
cat bootsect/bootsect.bin stage2.bin > $@
bootsect/bootsect.bin: bootsect/bootsect.asm
cd bootsect && nasm bootsect.asm -fbin -o bootsect.bin
%.o: %.c
$(CC) $(CFLAGS) $(INTERNAL_CFLAGS) -c $< -o $@
clean:
rm -f $(OBJ)
rm -f $(OBJ) bootsect/bootsect.bin

View File

@ -6,7 +6,7 @@ megs: 512
clock: sync=realtime, time0=local
ata0-master: type=disk, path="qloader2.img", mode=flat
ata0-master: type=disk, path="qloader2.bin", mode=flat
boot: c

67
bootsect/bootsect.asm Normal file
View File

@ -0,0 +1,67 @@
org 0x7C00
bits 16
code_start:
cli
jmp 0x0000:initialise_cs
initialise_cs:
xor ax, ax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov sp, 0x7c00
sti
mov byte [drive_number], dl
mov si, LoadingMsg
call simple_print
; ****************** Load stage 2 ******************
mov si, Stage2Msg
call simple_print
mov ax, 1
mov ebx, 0x7e00
mov cx, 7
call read_sectors
jc err
mov si, DoneMsg
call simple_print
jmp 0x7e00
err:
mov si, ErrMsg
call simple_print
halt:
hlt
jmp halt
;Data
LoadingMsg db 0x0D, 0x0A, '<qLoader 2>', 0x0D, 0x0A, 0x0A, 0x00
Stage2Msg db 'stage1: Loading stage2...', 0x00
ErrMsg db 0x0D, 0x0A, 'Error, system halted.', 0x00
DoneMsg db ' DONE', 0x0D, 0x0A, 0x00
times 0xda-($-$$) db 0
times 6 db 0
;Includes
%include 'simple_print.inc'
%include 'disk.inc'
drive_number db 0
times 0x1b8-($-$$) db 0
times 510-($-$$) db 0
dw 0xaa55

122
bootsect/disk.inc Normal file
View File

@ -0,0 +1,122 @@
read_sector:
; ***********************************************
; Reads a disk sector with an LBA address
; ***********************************************
; IN:
; EAX = LBA sector to load
; DL = Drive number
; ES = Buffer segment
; BX = Buffer offset
; OUT:
; Carry if error
push eax
push ebx
push ecx
push edx
push esi
push edi
push es
pop word [.target_segment]
mov word [.target_offset], bx
mov dword [.lba_address_low], eax
xor esi, esi
mov si, .da_struct
mov ah, 0x42
clc ; Clear carry for int 0x13 because some BIOSes may not clear it on success
int 0x13 ; Call int 0x13
.done:
pop edi
pop esi
pop edx
pop ecx
pop ebx
pop eax
ret ; Exit routine
align 4
.da_struct:
.packet_size db 16
.unused db 0
.count dw 1
.target_offset dw 0
.target_segment dw 0
.lba_address_low dd 0
.lba_address_high dd 0
read_sectors:
; ********************************************
; Reads multiple LBA addressed sectors
; ********************************************
; IN:
; EAX = LBA starting sector
; DL = Drive number
; ES = Buffer segment
; EBX = Buffer offset
; CX = Sectors count
; OUT:
; Carry if error
push eax ; Save GPRs
push ebx
push ecx
push edx
push esi
push edi
.loop:
push es
push ebx
mov bx, 0x7000 ; Load in a temp buffer
mov es, bx
xor bx, bx
call read_sector ; Read sector
pop ebx
pop es
jc .done ; If carry exit with flag
push ds
mov si, 0x7000
mov ds, si
mov edi, ebx
xor esi, esi
push ecx
mov ecx, 512
a32 o32 rep movsb
pop ecx
pop ds
inc eax ; Increment sector
add ebx, 512 ; Add 512 to the buffer
loop .loop ; Loop!
.done:
pop edi
pop esi
pop edx
pop ecx ; Restore GPRs
pop ebx
pop eax
ret ; Exit routine

22
bootsect/simple_print.inc Normal file
View File

@ -0,0 +1,22 @@
simple_print:
; **************************************
; Prints a string using the BIOS
; **************************************
; IN:
; SI = points to a 0x00 terminated string
push ax ; Save registers
push si
mov ah, 0x0E ; int 0x10, function 0x0E (print character)
.loop:
lodsb ; Load character from string
test al, al ; Is is the 0x00 terminator?
jz .done ; If it is, exit routine
int 0x10 ; Call BIOS
jmp .loop ; Repeat!
.done:
pop si ; Restore registers
pop ax
ret ; Exit routine

279
drivers/vga_textmode.c Normal file
View File

@ -0,0 +1,279 @@
#include <stdint.h>
#include <stddef.h>
#include <lib/cio.h>
#include <drivers/vga_textmode.h>
#define VIDEO_BOTTOM ((VD_ROWS * VD_COLS) - 1)
#define VD_COLS (80 * 2)
#define VD_ROWS 25
static void escape_parse(char c);
static void text_putchar(char c);
static char *video_mem = (char *)0xb8000;
static size_t cursor_offset = 0;
static int cursor_status = 1;
static uint8_t text_palette = 0x07;
static uint8_t cursor_palette = 0x70;
static int escape = 0;
static int esc_value0 = 0;
static int esc_value1 = 0;
static int *esc_value = &esc_value0;
static int esc_default0 = 1;
static int esc_default1 = 1;
static int *esc_default = &esc_default0;
static void clear_cursor(void) {
video_mem[cursor_offset + 1] = text_palette;
return;
}
static void draw_cursor(void) {
if (cursor_status) {
video_mem[cursor_offset + 1] = cursor_palette;
}
return;
}
static void scroll(void) {
// move the text up by one row
for (size_t i = 0; i <= VIDEO_BOTTOM - VD_COLS; i++)
video_mem[i] = video_mem[i + VD_COLS];
// clear the last line of the screen
for (size_t i = VIDEO_BOTTOM; i > VIDEO_BOTTOM - VD_COLS; i -= 2) {
video_mem[i] = text_palette;
video_mem[i - 1] = ' ';
}
return;
}
static void text_clear(void) {
clear_cursor();
for (size_t i = 0; i < VIDEO_BOTTOM; i += 2) {
video_mem[i] = ' ';
video_mem[i + 1] = text_palette;
}
cursor_offset = 0;
draw_cursor();
return;
}
static void text_clear_no_move(void) {
clear_cursor();
for (size_t i = 0; i < VIDEO_BOTTOM; i += 2) {
video_mem[i] = ' ';
video_mem[i + 1] = text_palette;
}
draw_cursor();
return;
}
void init_vga_textmode(void) {
port_out_b(0x3d4, 0x0a);
port_out_b(0x3d5, 0x20);
text_clear();
return;
}
static void text_enable_cursor(void) {
cursor_status = 1;
draw_cursor();
return;
}
static void text_disable_cursor(void) {
cursor_status = 0;
clear_cursor();
return;
}
static void text_set_cursor_palette(uint8_t c) {
cursor_palette = c;
draw_cursor();
return;
}
static uint8_t text_get_cursor_palette(void) {
return cursor_palette;
}
static void text_set_text_palette(uint8_t c) {
text_palette = c;
return;
}
static uint8_t text_get_text_palette(void) {
return text_palette;
}
static int text_get_cursor_pos_x(void) {
return (cursor_offset % VD_COLS) / 2;
}
static int text_get_cursor_pos_y(void) {
return cursor_offset / VD_COLS;
}
static void text_set_cursor_pos(int x, int y) {
clear_cursor();
cursor_offset = y * VD_COLS + x * 2;
draw_cursor();
return;
}
void text_write(const char *buf, size_t count) {
for (size_t i = 0; i < count; i++)
text_putchar(buf[i]);
}
static void text_putchar(char c) {
if (escape) {
escape_parse(c);
return;
}
switch (c) {
case 0x00:
break;
case 0x1B:
escape = 1;
return;
case 0x0A:
if (text_get_cursor_pos_y() == (VD_ROWS - 1)) {
clear_cursor();
scroll();
text_set_cursor_pos(0, (VD_ROWS - 1));
} else {
text_set_cursor_pos(0, (text_get_cursor_pos_y() + 1));
}
break;
case 0x08:
if (cursor_offset) {
clear_cursor();
cursor_offset -= 2;
video_mem[cursor_offset] = ' ';
draw_cursor();
}
break;
default:
clear_cursor();
video_mem[cursor_offset] = c;
if (cursor_offset >= (VIDEO_BOTTOM - 1)) {
scroll();
cursor_offset = VIDEO_BOTTOM - (VD_COLS - 1);
} else
cursor_offset += 2;
draw_cursor();
}
return;
}
static uint8_t ansi_colours[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
static void sgr(void) {
if (esc_value0 >= 30 && esc_value0 <= 37) {
uint8_t pal = text_get_text_palette();
pal = (pal & 0xf0) + ansi_colours[esc_value0 - 30];
text_set_text_palette(pal);
return;
}
if (esc_value0 >= 40 && esc_value0 <= 47) {
uint8_t pal = text_get_text_palette();
pal = (pal & 0x0f) + ansi_colours[esc_value0 - 40] * 0x10;
text_set_text_palette(pal);
return;
}
return;
}
static void escape_parse(char c) {
if (c >= '0' && c <= '9') {
*esc_value *= 10;
*esc_value += c - '0';
*esc_default = 0;
return;
}
switch (c) {
case '[':
return;
case ';':
esc_value = &esc_value1;
esc_default = &esc_default1;
return;
case 'A':
if (esc_default0)
esc_value0 = 1;
if (esc_value0 > text_get_cursor_pos_y())
esc_value0 = text_get_cursor_pos_y();
text_set_cursor_pos(text_get_cursor_pos_x(),
text_get_cursor_pos_y() - esc_value0);
break;
case 'B':
if (esc_default0)
esc_value0 = 1;
if ((text_get_cursor_pos_y() + esc_value0) > (VD_ROWS - 1))
esc_value0 = (VD_ROWS - 1) - text_get_cursor_pos_y();
text_set_cursor_pos(text_get_cursor_pos_x(),
text_get_cursor_pos_y() + esc_value0);
break;
case 'C':
if (esc_default0)
esc_value0 = 1;
if ((text_get_cursor_pos_x() + esc_value0) > (VD_COLS / 2 - 1))
esc_value0 = (VD_COLS / 2 - 1) - text_get_cursor_pos_x();
text_set_cursor_pos(text_get_cursor_pos_x() + esc_value0,
text_get_cursor_pos_y());
break;
case 'D':
if (esc_default0)
esc_value0 = 1;
if (esc_value0 > text_get_cursor_pos_x())
esc_value0 = text_get_cursor_pos_x();
text_set_cursor_pos(text_get_cursor_pos_x() - esc_value0,
text_get_cursor_pos_y());
break;
case 'H':
esc_value0--;
esc_value1--;
if (esc_default0)
esc_value0 = 0;
if (esc_default1)
esc_value1 = 0;
if (esc_value1 >= (VD_COLS / 2))
esc_value1 = (VD_COLS / 2) - 1;
if (esc_value0 >= VD_ROWS)
esc_value0 = VD_ROWS - 1;
text_set_cursor_pos(esc_value1, esc_value0);
break;
case 'm':
sgr();
break;
case 'J':
switch (esc_value0) {
case 2:
text_clear_no_move();
break;
default:
break;
}
break;
default:
escape = 0;
text_putchar('?');
break;
}
esc_value = &esc_value0;
esc_value0 = 0;
esc_value1 = 0;
esc_default = &esc_default0;
esc_default0 = 1;
esc_default1 = 1;
escape = 0;
return;
}

9
drivers/vga_textmode.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef __VGA_TEXTMODE_H__
#define __VGA_TEXTMODE_H__
#include <stddef.h>
void init_vga_textmode(void);
void text_write(const char *, size_t);
#endif

59
lib/cio.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef __CIO_H__
#define __CIO_H__
#include <stdint.h>
#define port_out_b(port, value) ({ \
asm volatile ( "out dx, al" \
: \
: "a" (value), "d" (port) \
: ); \
})
#define port_out_w(port, value) ({ \
asm volatile ( "out dx, ax" \
: \
: "a" (value), "d" (port) \
: ); \
})
#define port_out_d(port, value) ({ \
asm volatile ( "out dx, eax" \
: \
: "a" (value), "d" (port) \
: ); \
})
#define port_in_b(port) ({ \
uint8_t value; \
asm volatile ( "in al, dx" \
: "=a" (value) \
: "d" (port) \
: ); \
value; \
})
#define port_in_w(port) ({ \
uint16_t value; \
asm volatile ( "in ax, dx" \
: "=a" (value) \
: "d" (port) \
: ); \
value; \
})
#define port_in_d(port) ({ \
uint32_t value; \
asm volatile ( "in eax, dx" \
: "=a" (value) \
: "d" (port) \
: ); \
value; \
})
#define io_wait() ({ port_out_b(0x80, 0x00); })
#define disable_interrupts() ({ asm volatile ("cli"); })
#define enable_interrupts() ({ asm volatile ("sti"); })
#endif

View File

@ -3,19 +3,18 @@ ENTRY(main)
SECTIONS
{
. = 0x7c00;
. = 0x7e00;
.text : {
bootsect_begin = .;
KEEP(*(.early_boot*))
KEEP(*(.entry*))
KEEP(*(.text*))
}
.data : {
KEEP(*(.data*))
KEEP(*(.bss*))
. += 510 - (. - bootsect_begin);
SHORT(0xaa55)
. += 3584 - (. - bootsect_begin);
}
}

30
main.c
View File

@ -1,37 +1,15 @@
asm (
".section .early_boot\n\t"
"cli\n\t"
"jmp 0x0:1f\n\t"
"1:\n\t"
"xor ax, ax\n\t"
"mov ds, ax\n\t"
"mov es, ax\n\t"
"mov fs, ax\n\t"
"mov gs, ax\n\t"
"mov ss, ax\n\t"
"mov sp, 0x7c00\n\t"
".section .entry\n\t"
"xor dh, dh\n\t"
"push edx\n\t"
"call main\n\t"
);
void bios_print(const char *str) {
asm (
"1:\n\t"
"lodsb\n\t"
"test al, al\n\t"
"jz 2f\n\t"
"int 0x10\n\t"
"jmp 1b\n\t"
"2:\n\t"
:
: "a"(0x0e00), "S"(str)
: "cc", "memory"
);
}
#include <drivers/vga_textmode.h>
void main(int boot_drive) {
// TODO
bios_print("hello world from qloader2");
init_vga_textmode();
text_write("hello world", 11);
for (;;);
}