:: commit 3c4c1a878a09c05c65c6184631ed98386ee93eb8

mintsuki <mintsuki@protonmail.com> — 2019-05-30 13:59

parents: 276928841a

Add separate bootsector and stage2, add VGA text mode driver

diff --git a/Makefile b/Makefile
index b4fe92e0..d7ad1d6c 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/bochsrc b/bochsrc
index 990bafdd..9201dec8 100644
--- a/bochsrc
+++ b/bochsrc
@@ -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
 
diff --git a/bootsect/bootsect.asm b/bootsect/bootsect.asm
new file mode 100644
index 00000000..08d6e97b
--- /dev/null
+++ b/bootsect/bootsect.asm
@@ -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
diff --git a/bootsect/disk.inc b/bootsect/disk.inc
new file mode 100644
index 00000000..e685eba8
--- /dev/null
+++ b/bootsect/disk.inc
@@ -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
diff --git a/bootsect/simple_print.inc b/bootsect/simple_print.inc
new file mode 100644
index 00000000..add926af
--- /dev/null
+++ b/bootsect/simple_print.inc
@@ -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
diff --git a/drivers/vga_textmode.c b/drivers/vga_textmode.c
new file mode 100644
index 00000000..e0bcc861
--- /dev/null
+++ b/drivers/vga_textmode.c
@@ -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;
+}
diff --git a/drivers/vga_textmode.h b/drivers/vga_textmode.h
new file mode 100644
index 00000000..c9dc43f3
--- /dev/null
+++ b/drivers/vga_textmode.h
@@ -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
diff --git a/lib/cio.h b/lib/cio.h
new file mode 100644
index 00000000..5c23e5d4
--- /dev/null
+++ b/lib/cio.h
@@ -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
diff --git a/linker.ld b/linker.ld
index 31218ae9..10e9a0f1 100644
--- a/linker.ld
+++ b/linker.ld
@@ -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);
     }
 
 }
diff --git a/main.c b/main.c
index 374c915b..e28aa021 100644
--- a/main.c
+++ b/main.c
@@ -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 (;;);
 }
tab: 248 wrap: offon