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 (;;);
}
