initial commit
authorJohn Tsiombikas <nuclear@member.fsf.org>
Wed, 16 Nov 2022 00:18:35 +0000 (02:18 +0200)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Wed, 16 Nov 2022 00:18:35 +0000 (02:18 +0200)
.gdbinit [new file with mode: 0644]
.gitignore [new file with mode: 0644]
Makefile [new file with mode: 0644]
boot.ld [new file with mode: 0644]
boot/boot.asm [new file with mode: 0644]
boot/boot2.asm [new file with mode: 0644]
boot/main.c [new file with mode: 0644]

diff --git a/.gdbinit b/.gdbinit
new file mode 100644 (file)
index 0000000..f0a6182
--- /dev/null
+++ b/.gdbinit
@@ -0,0 +1,4 @@
+target remote localhost:1234
+disp/i $pc
+set disassembly-flavor intel
+set architecture i8086
diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..3576cff
--- /dev/null
@@ -0,0 +1,7 @@
+*.o
+*.d
+*.swp
+*.img
+*.map
+dis
+*.log
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..0bac632
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,29 @@
+bin = boot.img
+
+CC = ia16-elf-gcc
+LD = ia16-elf-ld
+
+CFLAGS = -march=i8088 -mtune=i8088
+
+$(bin): boot/boot.o boot/boot2.o boot/main.o
+       $(LD) -T boot.ld -Map boot.map -o $@ $^
+
+%.o: %.asm
+       nasm -o $@ -f elf $<
+
+.PHONY: clean
+clean:
+       rm -f boot/*.o $(bin)
+
+
+.PHONY: run
+run: $(bin)
+       qemu-system-i386 -fda $(bin) -serial file:serial.log
+
+.PHONY: debug
+debug: $(bin)
+       qemu-system-i386 -fda $(bin) -serial file:serial.log -s -S
+
+.PHONY: disasm
+disasm: $(bin)
+       ndisasm -o 0x7c00 $< >dis
diff --git a/boot.ld b/boot.ld
new file mode 100644 (file)
index 0000000..b607e11
--- /dev/null
+++ b/boot.ld
@@ -0,0 +1,29 @@
+OUTPUT_FORMAT(binary)
+ENTRY(_start)
+
+SECTIONS {
+       . = 0x7c00;
+       .bootsect : {
+               * (.bootsect);
+       }
+
+       . = 0x7e00;
+       _stage2_start = .;
+       .text : {
+               * (.startup);
+               * (.text*);
+       }
+       .data : {
+               * (.rodata*);
+               * (.data*);
+       }
+       .bss ALIGN(4) (NOLOAD): {
+               _bss_start = .;
+               * (.bss*);
+               * (COMMON);
+               . = ALIGN(4);
+       }
+       _bss_size = SIZEOF(.bss);
+       . = ALIGN(4);
+       _stage2_end = .;
+};
diff --git a/boot/boot.asm b/boot/boot.asm
new file mode 100644 (file)
index 0000000..5b82c2d
--- /dev/null
@@ -0,0 +1,250 @@
+; first stage boot loader
+       cpu 8086
+       bits 16
+       section .bootsect
+
+extern _stage2_start
+extern _stage2_end
+
+stacktop       equ 7b00h
+boot_driveno   equ 7b00h
+num_read_tries equ 7b06h       ; 2 bytes
+sect_pending   equ 7b08h       ; 2 bytes
+sect_per_track equ 7b0ah       ; 2 bytes
+cur_head       equ 7b0ch       ; 2 bytes - current head
+start_sect     equ 7b0eh       ; 2 bytes - start sector in track
+destptr                equ 7b10h       ; 2 bytes - destination pointer
+num_heads      equ 7b12h       ; 2 bytes - number of heads
+cur_cyl                equ 7b14h       ; 2 bytes - current cylinder
+
+%macro floppy_motor_off 0
+       pushf
+       and dl, 80h
+       jnz %%end       ; skip if high bit is set (i.e. it's not a floppy)
+       mov dx, 3f2h
+       in al, dx
+       and al, 0fh
+       out dx, al
+%%end: popf
+%endmacro
+
+
+bios_param_block:
+       jmp start       ; 2 bytes
+       nop             ; 1 byte
+       ; start of BPB at offset 3
+       db "BSPL 0.1"   ; 03h: OEM ident, 8 bytes
+       dw 512          ; 0bh: bytes per sector
+       db 1            ; 0dh: sectors per cluster
+       dw 1            ; 0eh: reserved sectors (including boot record)
+       db 2            ; 10h: number of FATs
+       dw 224          ; 11h: number of dir entries
+       dw 2880         ; 13h: number of sectors in volume
+       db 0fh          ; 15h: media descriptor type (f = 3.5" HD floppy)
+       dw 9            ; 16h: number of sectors per FAT
+       dw 18           ; 18h: number of sectors per track
+       dw 2            ; 1ah: number of heads
+       dd 0            ; 1ch: number of hidden sectors
+       dd 0            ; 20h: high bits of sector count
+       db 0            ; 24h: drive number
+       db 0            ; 25h: winnt flags
+       db 28h          ; 26h: signature(?)
+       dd 0            ; 27h: volume serial number
+       db "80 SIX BOOT"; 2bh: volume label, 11 bytes
+       db "FAT12   "   ; 36h: filesystem id, 8 bytes
+
+start:
+       cld
+       xor ax, ax
+       mov ds, ax
+       mov es, ax
+       mov ss, ax
+       mov gs, ax
+       mov fs, ax
+       jmp 00:.setcs
+.setcs:
+       mov sp, stacktop
+       mov [boot_driveno], dl
+
+       ; query sectors per track
+       mov ah, 8       ; get drive parameters call, dl already has the drive
+       xor di, di
+       int 13h
+       jc .queryfail
+       and cx, 3fh
+       mov [sect_per_track], cx
+       mov cl, 8
+       shr dx, cl
+       inc dx
+       mov [num_heads], dx
+       jmp .querydone
+.queryfail:
+       ; in case of failure, try 18 sectors per track, 2 heads
+       mov word [sect_per_track], 18
+       mov word [num_heads], 2
+.querydone:
+
+; load the rest of the code at 7e00h
+       mov ax, _stage2_end
+       sub ax, _stage2_start
+       add ax, 511
+       mov cl, 9
+       shr ax, cl
+       inc ax
+       mov [sect_pending], ax
+       mov ax, _stage2_start
+       shr ax, 1
+       shr ax, 1
+       shr ax, 1
+       shr ax, 1
+       mov es, ax      ; destination segment 7e0h to allow loading up to 64k
+       mov word [destptr], 0
+       mov word [start_sect], 1        ; start from sector 1 to skip boot sector
+       mov word [cur_cyl], 0
+       mov word [cur_head], 0
+
+.rdloop:
+       mov cx, [start_sect]
+       mov ax, [sect_pending]
+       sub ax, cx              ; num_sect = pending - start_sect
+       cmp ax, [sect_per_track]
+       jbe .noadj
+       mov ax, [sect_per_track]        ; read max sect_per_track at a time
+       sub ax, cx
+.noadj: test ax, ax
+       jz .done
+       push ax         ; save how many sectors we're reading
+
+       push ax
+       push bx
+       push cx
+       push dx
+       push si
+       call print_hex_word
+       mov ax, str_rdtrack2
+       call printstr
+       mov ax, [cur_cyl]
+       call print_hex_byte
+       mov ax, str_rdtrack3
+       call printstr
+       mov ax, [cur_head]
+       call print_hex_byte
+       mov ax, str_rdtrack3
+       call printstr
+       mov ax, [start_sect]
+       call print_hex_byte
+       mov ax, str_newline
+       call printstr
+       pop si
+       pop dx
+       pop cx
+       pop bx
+       pop ax
+
+       mov ch, [cur_cyl]
+       mov dh, [cur_head]
+       mov bx, [destptr]
+
+       inc cl                  ; sector numbers start from 1
+       mov ah, 2               ; read sectors is call 2
+       mov dl, [boot_driveno]
+       int 13h
+       jc .fail
+       ; track read sucessfully
+       mov word [start_sect], 0        ; all subsequent tracks are whole
+       mov ax, [cur_head]
+       inc ax
+       cmp ax, [num_heads]
+       jnz .skip_cyl_adv
+       xor ax, ax
+       inc byte [cur_cyl]
+.skip_cyl_adv:
+       mov [cur_head], ax
+
+       pop ax                  ; num_sect
+       sub [sect_pending], ax
+       push cx
+       mov cl, 9
+       shl ax, cl              ; convert to bytes
+       pop cx
+       add [destptr], ax
+       jnz .rdloop
+
+       ; loaded sucessfully, reset es back to 0 and jump
+.done: xor ax, ax
+       mov es, ax
+       jmp _stage2_start
+
+.fail: add sp, 2       ; clear num_sect off the stack
+       dec word [num_read_tries]
+       jz .abort
+
+       ; reset controller and retry
+       xor ax, ax
+       mov dl, [boot_driveno]
+       int 13h
+       jmp .rdloop
+
+       ; failed to load second sector
+.abort:        xor ax, ax
+       mov es, ax
+       floppy_motor_off        ; turn off floppy motor (if dl is < 80h)
+       mov ax, str_load_fail
+       call printstr
+.hang: hlt
+       jmp .hang
+
+       ; expects string ptr in ax
+printstr:
+       mov si, ax
+.loop: mov al, [si]
+       inc si
+       test al, al
+       jz .done
+       mov ah, 0eh
+       mov bx, 7
+       int 10h
+       jmp .loop
+.done: ret
+
+print_hex_word:
+       mov cx, 4       ; 4 digits to print
+       jmp print_n_hex_digits
+
+print_hex_byte:
+       mov cx, 2       ; 2 digits to print
+       mov ah, al      ; move them in place
+
+print_n_hex_digits:
+       rol ax, 1
+       rol ax, 1
+       rol ax, 1
+       rol ax, 1
+       mov dx, ax      ; save ax, print_hex_digit destroys it
+       call print_hex_digit
+       mov ax, dx
+       dec cx
+       jnz print_n_hex_digits
+       ret
+
+print_hex_digit:
+       mov bl, al
+       and bx, 0fh
+       mov al, [bx + .hexdig_tab]
+       mov ah, 0eh
+       mov bx, 7
+       int 10h
+       ret
+
+.hexdig_tab:
+       db "0123456789abcdef"
+
+str_rdtrack2   db " from ",0
+str_rdtrack3   db "/",0
+str_load_fail  db "Failed to load 2nd stage!",0
+str_newline    db 13,10,0
+
+       times 510-($-$$) db 0
+bootsig dw 0xaa55
+
+; vi:set ts=8 sts=8 sw=8 ft=nasm:
diff --git a/boot/boot2.asm b/boot/boot2.asm
new file mode 100644 (file)
index 0000000..6837579
--- /dev/null
@@ -0,0 +1,18 @@
+; second stage boot loader C startup
+       cpu 8086
+       bits 16
+       section .startup
+
+extern bootmain
+
+global _start
+_start:
+       ; TODO floppy off if necessary
+       ; TODO zero .bss
+       call bootmain
+
+hang:  hlt
+       jmp hang
+
+
+; vi:set ts=8 sts=8 sw=8 ft=nasm:
diff --git a/boot/main.c b/boot/main.c
new file mode 100644 (file)
index 0000000..7110581
--- /dev/null
@@ -0,0 +1,13 @@
+void bootmain(void)
+{
+       int i, j;
+       unsigned short __far *vmem = (void __far*)0xb8000000ul;
+       unsigned short c;
+
+       for(i=0; i<25; i++) {
+               c = ((i & 0xf) << 8) | '@';
+               for(j=0; j<80; j++) {
+                       *vmem++ = c;
+               }
+       }
+}