235 lines
4.9 KiB
NASM
235 lines
4.9 KiB
NASM
USE16
|
|
|
|
stage1:
|
|
; Initialize segment registers
|
|
xor ax, ax ; Set ax to 0
|
|
mov ds, ax
|
|
mov es, ax
|
|
mov ss, ax
|
|
|
|
; Initialize stack pointer
|
|
; (stack grows up)
|
|
mov sp, 0x7C00
|
|
|
|
; Initialize CS
|
|
;
|
|
; `retf` sets both CS and IP to a known-good state.
|
|
; This is necessary because we don't know where the BIOS put us at startup.
|
|
; (could be 0x00:0x7C00, could be 0x7C00:0x00. Not everybody follows spec.)
|
|
push ax ; `ax` is still 0
|
|
push word .set_cs
|
|
retf
|
|
|
|
.set_cs:
|
|
; Save disk number.
|
|
; BIOS sets `dl` to the number of
|
|
; the disk we're booting from.
|
|
mov [disk], dl
|
|
|
|
; Print "Stage 1"
|
|
mov si, stage_msg
|
|
call print
|
|
mov al, '1'
|
|
call print_char
|
|
call print_line
|
|
|
|
; read CHS gemotry, save into [chs]
|
|
; CL (bits 0-5) = maximum sector number
|
|
; CL (bits 6-7) = high bits of max cylinder number
|
|
; CH = low bits of maximum cylinder number
|
|
; DH = maximum head number
|
|
mov ah, 0x08
|
|
mov dl, [disk]
|
|
xor di, di
|
|
int 0x13
|
|
jc error ; carry flag set on error
|
|
mov bl, ch
|
|
mov bh, cl
|
|
shr bh, 6
|
|
mov [chs.c], bx
|
|
shr dx, 8
|
|
inc dx ; returns heads - 1
|
|
mov [chs.h], dx
|
|
and cl, 0x3f
|
|
mov [chs.s], cl
|
|
|
|
; First sector of stage 2
|
|
mov eax, STAGE2_SECTOR
|
|
|
|
; Where to load stage 2
|
|
mov bx, stage2
|
|
|
|
; length of stage2 + stage3
|
|
; (on disk, in sectors)
|
|
mov cx, (stage3.end - stage2) / 512
|
|
mov dx, 0
|
|
|
|
; Consume eax, bx, cx, dx
|
|
; and load code from disk.
|
|
call load
|
|
|
|
jmp stage2.entry
|
|
|
|
; Load sectors from disk to memory.
|
|
; Cannot load more than 1MiB.
|
|
;
|
|
; Input:
|
|
; ax: start sector
|
|
; bx: offset of buffer
|
|
; cx: number of sectors (512 Bytes each)
|
|
; dx: segment of buffer
|
|
;
|
|
; Clobbers ax, bx, cx, dx, si
|
|
load:
|
|
; Every "replace 1" comment means that the `1`
|
|
; on that line could be bigger.
|
|
;
|
|
; See https://stackoverflow.com/questions/58564895/problem-with-bios-int-13h-read-sectors-from-drive
|
|
; We have to load one sector at a time to avoid the 1K boundary error.
|
|
; Would be nice to read more sectors at a time, though, that's faster.
|
|
|
|
cmp cx, 1 ; replace 1
|
|
jbe .good_size
|
|
|
|
pusha
|
|
mov cx, 1 ; replace 1
|
|
call load
|
|
popa
|
|
add eax, 1 ; replace 1
|
|
add dx, 1 * 512 / 16 ; replace 1
|
|
sub cx, 1 ; replace 1
|
|
|
|
jmp load
|
|
.good_size:
|
|
mov [DAPACK.addr], eax
|
|
mov [DAPACK.buf], bx
|
|
mov [DAPACK.count], cx
|
|
mov [DAPACK.seg], dx
|
|
|
|
; Print the data we're reading
|
|
; Prints AAAAAAAA#BBBB CCCC:DDDD, where:
|
|
; - A..A is the lba we're reading (printed in two parts)
|
|
; - BBBB is the number of sectors we're reading
|
|
; - CCCC is the index we're writing to
|
|
; - DDDD is the buffer we're writing to
|
|
mov bx, [DAPACK.addr + 2] ; last two bytes
|
|
call print_hex
|
|
mov bx, [DAPACK.addr] ; first two bytes
|
|
call print_hex
|
|
mov al, '#'
|
|
call print_char
|
|
mov bx, [DAPACK.count]
|
|
call print_hex
|
|
mov al, ' '
|
|
call print_char
|
|
mov bx, [DAPACK.seg]
|
|
call print_hex
|
|
mov al, ':'
|
|
call print_char
|
|
mov bx, [DAPACK.buf]
|
|
call print_hex
|
|
call print_line
|
|
|
|
|
|
; Read from disk.
|
|
; int13h, ah=0x42 does not work on some disks.
|
|
; use int13h, ah=0x02 in thes case.
|
|
cmp byte [chs.s], 0
|
|
jne .chs
|
|
|
|
mov dl, [disk]
|
|
mov si, DAPACK
|
|
mov ah, 0x42
|
|
int 0x13
|
|
jc error ; carry flag set on error
|
|
ret
|
|
|
|
.chs:
|
|
; calculate CHS
|
|
xor edx, edx
|
|
mov eax, [DAPACK.addr]
|
|
div dword [chs.s] ; divide by sectors
|
|
mov ecx, edx ; move sector remainder to ecx
|
|
xor edx, edx
|
|
div dword [chs.h] ; divide by heads
|
|
; eax has cylinders, edx has heads, ecx has sectors
|
|
|
|
; Sector cannot be greater than 63
|
|
inc ecx ; Sector is base 1
|
|
cmp ecx, 63
|
|
ja error_chs
|
|
|
|
; Head cannot be greater than 255
|
|
cmp edx, 255
|
|
ja error_chs
|
|
|
|
; Cylinder cannot be greater than 1023
|
|
cmp eax, 1023
|
|
ja error_chs
|
|
|
|
; Move CHS values to parameters
|
|
mov ch, al
|
|
shl ah, 6
|
|
and cl, 0x3f
|
|
or cl, ah
|
|
shl dx, 8
|
|
|
|
; read from disk using CHS
|
|
mov al, [DAPACK.count]
|
|
mov ah, 0x02 ; disk read (CHS)
|
|
mov bx, [DAPACK.buf]
|
|
mov dl, [disk]
|
|
push es ; save ES
|
|
mov es, [DAPACK.seg]
|
|
int 0x13
|
|
pop es ; restore EC
|
|
jc error ; carry flag set on error
|
|
ret
|
|
|
|
;
|
|
; MARK: errors
|
|
;
|
|
|
|
error_chs:
|
|
mov ah, 0
|
|
|
|
error:
|
|
call print_line
|
|
|
|
mov bh, 0
|
|
mov bl, ah
|
|
call print_hex
|
|
|
|
mov si, stage1_error_msg
|
|
call print
|
|
call print_line
|
|
|
|
; halt after printing error details
|
|
.halt:
|
|
cli
|
|
hlt
|
|
jmp .halt
|
|
|
|
;
|
|
; MARK: data
|
|
;
|
|
|
|
%include "print.asm"
|
|
|
|
stage_msg: db "Stage ",0
|
|
stage1_error_msg: db " ERROR",0
|
|
|
|
disk: db 0
|
|
|
|
chs:
|
|
.c: dd 0
|
|
.h: dd 0
|
|
.s: dd 0
|
|
|
|
DAPACK:
|
|
db 0x10
|
|
db 0
|
|
.count: dw 0 ; int 13 resets this to # of blocks actually read/written
|
|
.buf: dw 0 ; memory buffer destination address (0:7c00)
|
|
.seg: dw 0 ; in memory page zero
|
|
.addr: dq 0 ; put the lba to read in this spot |