📄 boot12.asm
字号:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; BING first-stage loader
; Copyright (C) 2000, Chris Giese <geezer@execpc.com>
; http://www.execpc.com/~geezer/os
;
; This code goes into sector 0 of the floppy, so it must fit into a single
; disk sector. After being loaded by the BIOS to address 0000:7C00, we:
; 1. search the root directory for the file named at 'second_stage:'.
; 2. load that file into memory at address ADR_SECOND
; 3. jump to the loaded file
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This value must be a multiple of 16. Chose the location of the second
; stage carefully. This first stage code will cheerfully overwrite
; itself while loading the second stage
ADR_SECOND equ 00500h
%define ADR_START 7C00h
ADR_STACK equ (ADR_START - 32)
ADR_DIRBUF equ (ADR_START - 512 * 2)
ADR_FATBUF equ (ADR_DIRBUF - 512 * 2)
ORG ADR_START
start:
; define memory used for scratchpad variables. These are initialized
; at run-time, so there's no need to waste 1st stage memory on them.
; Put them just below 'start'
; (1 byte) drive we booted from; 0=A, 80h=C
boot_drive EQU (start - 1)
; (2 bytes) sector where root directory starts (FAT 12 or FAT 16)
root_start EQU (boot_drive - 2)
; (2 bytes) sector where the actual disk data starts
data_start EQU (root_start - 2)
; (2 bytes) number of 16-byte paragraphs per sector
para_per_sector EQU (data_start - 2)
; (2 bytes) number of 16-byte paragraphs per cluster
para_per_cluster EQU (para_per_sector - 2)
jmp short over ; skip over BPB
nop
; BIOS Parameter Block (BPB)
oem_id: ; 03h not used by this code
db "GEEZER", 0, 0
bytes_per_sector: ; 0Bh
dw 512
sectors_per_cluster: ; 0Dh
db 1
fat_start:
num_reserved_sectors: ; 0Eh
dw 1
num_fats: ; 10h
db 2
num_root_dir_ents: ; 11h
dw 224
total_sectors: ; 13h not used by this code
dw 2880 ; 2880 for 1.44 meg, 3360 for 1.68 meg
media_id: ; 15h not used by this code
db 0F0h
sectors_per_fat: ; 16h
dw 9
sectors_per_track: ; 18h
dw 18 ; 18 for 1.44 meg, 21 for 1.68 meg
heads: ; 1Ah
dw 2
; hidden_sectors and total_sectors_large are not used by this code,
; but are needed for proper operation of DOS 7 FORMAT /Q
hidden_sectors: ; 1Ch
dd 0
total_sectors_large: ; 20h
dd 0
over:
; evidently, some buggy BIOSes load the boot code to 07C0:0000,
; so fix that now
jmp 0:over2
over2:
xor ax,ax ; zero DS and SS
mov ds,ax
mov ss,ax
mov bp,over ; for compact relative addressing
mov sp,ADR_STACK
cld ; all string operations go up
; save [boot_drive] from DL. BIOS sets DL=0 if
; booting from drive A, DL=80h if booting from drive C
mov [bp - (over - boot_drive)],dl
; compute 1st sector of root directory
mov al,[num_fats] ; number of FATs
cbw ; "mov ah,0"
mul word [bp - (over - sectors_per_fat)] ; multiply by sectors/FAT
add ax,[bp - (over - fat_start)] ; plus reserved sectors
mov [root_start],ax
; compute 1st sector of disk data area
mov bx,ax
mov ax,[num_root_dir_ents] ; entries in root dir
mov dx,32 ; * bytes/entry
mul dx ; == bytes in root dir
div word [bp - (over - bytes_per_sector)] ; / bytes per sector
add ax,bx ; = sectors
mov [data_start],ax
; compute number of 16-byte paragraphs per disk sector
mov dx,[bp - (over - bytes_per_sector)]
mov cl,4
shr dx,cl
mov [bp - (over - para_per_sector)],dx
; compute number of 16-byte paragraphs per FAT cluster
mov al,[sectors_per_cluster]
cbw
mul dx ; DX still=paragraphs/sector
mov dx,ax
mov [bp - (over - para_per_cluster)],dx
; OK, everything is set up for 'find_file', 'walk_fat', and 'read_sectors'
; Find the file named at 'second_stage:' in the root directory
mov si,second_stage
call find_file
jc err ; disk error
jne err ; 2nd stage file not found
; found 2nd stage, load it. Adding DS to the segment value does nothing
; for a bootloader, but it lets this code work as a .COM file as well.
mov cx,ds
add cx,(ADR_SECOND >> 4)
load_2nd:
mov es,cx
; convert cluster BX to sector value in AX, and get next cluster in BX
call walk_fat
jc err
; read an entire cluster
mov di,[bp - (over - sectors_per_cluster)]
and di,00FFh
call read_sectors
jc err
add cx,[bp - (over - para_per_cluster)] ; advance mem ptr 1 cluster
cmp bx,0F00h ; EOF cluter value for FAT12
jb load_2nd
; put stack at top of tiny segment
xor ax,ax
mov sp,ax
%if 0
; jump to second stage that is ORGed 0
mov ax,(ADR_SECOND >> 4)
mov ds,ax
mov es,ax
mov ss,ax
jmp (ADR_SECOND >> 4):0
%else
; jump to second stage that is ORGed 100h (.COM file)
mov ax,((ADR_SECOND - 100h) >> 4)
mov ds,ax
mov es,ax
mov ss,ax
jmp ((ADR_SECOND - 100h) >> 4):100h
%endif
err:
; not enough room for an error message, so just beep
mov ax,0E07h ; *** BEEEP ***
int 10h
; await key pressed
mov ah,0
int 16h
; re-start the boot process
int 19h
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: find_file
; action: searches for file in root dir of FAT12 floppy
; in: 11-char all-caps filename at SI, [root_start] set
; out (disk error): CY=1
; out (file not found): CY=0, ZF=0
; out (found it): CY=0, ZF=1, BX=starting cluster of file
; modifies: BX
; minimum CPU: 8088
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
find_file:
push es
push di
push cx
push ax
; 1st sector in root directory
mov ax,[root_start]
mov bx,ds ; where to load it
add bx,(ADR_DIRBUF >> 4)
mov es,bx
find_file_1:
call read_sector ; one sector at a time
jc find_file_5 ; disk read error
xor di,di
find_file_2:
test [es:di],byte 0FFh ; 0 byte = end of root dir
je find_file_3
mov cx,11
push si
push di ; compare filename to dirent
rep cmpsb
pop di
pop si
je find_file_4
; if filename comparision failed, advance 32 bytes to next dir entry
add di,byte 32
cmp di,[bp - (over -bytes_per_sector)]
jb find_file_2
inc ax
cmp ax,[bp - (over - data_start)]
jb find_file_1 ; go to next sector of root dir
find_file_3:
; did not find the file: return with CY=0, ZF=0
or al,1
find_file_4:
; found the file
mov bx,[es:di + 26] ; get starting cluster from dir entry
find_file_5:
pop ax
pop cx
pop di
pop es
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: walk_fat
; action: converts cluster value to sector,
; and gets next cluster of file from FAT
; in: BX=cluster
; out (disk error): CY=1
; out (success): CY=0, AX=first sector of cluster, BX=next cluster
; modifies: AX, BX
; minimum CPU: 8088
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
walk_fat:
push es
push di
push dx
push cx
mov ax,bx
; cluster 2 is the first data cluster
dec ax
dec ax
; convert from clusters to sectors
mov dh,0
mov dl,[bp - (over - sectors_per_cluster)]
mul dx
add ax,[bp - (over - data_start)]
; save sector in AX
push ax
mov ax,ds
add ax,(ADR_FATBUF >> 4)
mov es,ax
; FAT12 entries are 12 bits, bytes are 8 bits. Ratio is 3 / 2,
; so multiply cluster by 3, and divide by 2 later.
mov ax,bx
shl ax,1 ; entry * 2
add ax,bx ; + entry = entry * 3
mov bx,ax
; BX:0 =use high or low 12 bits of 16-bit value read from FAT
; BX:9...BX:1 =byte offset into FAT sector (9 assumes 512-byte sectors)
; BX:?...BX:10 =which sector of FAT to load
;
; figure out which FAT sectors to load
shr ax,1
xor dx,dx
div word [bp - (over - bytes_per_sector)]
add ax,[bp - (over - fat_start)]
; leave the remainder (byte offset) in DX
; check the FAT buffer to see if this sector is already loaded
cmp ax,[bp - (over - curr_sector)]
je walk_fat_1
mov [curr_sector],ax
; read the target FAT sector plus the sector after it
; (in case the 12-bit FAT entry straddles the two sectors)
mov di,2
call read_sectors
jc walk_fat_4
walk_fat_1:
; point to correct entry in loaded FAT
mov di,dx
; get 16 bits from FAT
mov ax,[es:di]
; look at BX:0 to see if we want the high 12 bits or the low 12 bits
shr bx,1
jc walk_fat_2
and ax,0FFFh ; CY=1: use low 12 bits
jmp short walk_fat_3
walk_fat_2:
mov cl,4
shr ax,cl ; CY=0: use high 12 bits
walk_fat_3:
mov bx,ax
; clear CY bit to signal success
clc
walk_fat_4:
pop ax
pop cx
pop dx
pop di
pop es
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: read_cluster
; read_sector
; read_sectors
; action: reads one or more disk sectors into memory
; in: ES:0=address of memory where sectors should be read
; AX=first sector to read
; DI=number of sectors to read
; (not used for read_cluster or read_sector)
; out (disk error): CY=1
; out (success): CY=0
; modifies: read_cluster and read_sector modify DI
; minimum CPU: 8088
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
read_cluster:
mov di,[bp - (over - sectors_per_cluster)]
and di,00FFh
jmp short read_sectors
read_sector:
mov di,1
read_sectors:
; read DI sectors, starting with sector AX into RAM at ES:0
push es
push di
push si
push dx
push cx
push bx
push ax
mov si,ax ; SI=sector
xor bx,bx ; ES:BX -> buffer
read_sectors_1:
mov ax,si
xor dx,dx ; DX:AX=sector
div word [bp - (over - sectors_per_track)]
mov cx,dx
inc cx ; CL=sector
xor dx,dx
div word [bp - (over - heads)]
mov dh,dl ; DH=head
mov ch,al ; CH=cylinder 7:0
mov dl,[bp - (over - boot_drive)]
shl cl,1
shl cl,1 ; trying to use only
shr ah,1 ; 8088 asm here...
rcr cl,1
shr ah,1
rcr cl,1 ; CL7:6=cylinder 9:8, CL5:0=sector
; read one sector
mov ax,0201h
int 13h
jnc read_sectors_3
; disk error; recalibrate/reset drive. This _must_ be done immediately after
; the failed call to INT 13h AH=02, while the floppy is still spinning.
mov ah,0
int 13h
jc read_sectors_4
; try the read again. If it fails a second time, give up.
mov ax,0201h
int 13h
jc read_sectors_4
read_sectors_3:
; advance memory pointer
mov cx,es
add cx,[bp - (over - para_per_sector)]
mov es,cx
; next sector
inc si
dec di
jne read_sectors_1
; clear CY bit to signal success
clc
read_sectors_4:
pop ax
pop bx
pop cx
pop dx
pop si
pop di
pop es
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 1st-stage data
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
curr_sector: ; which sector is in the FAT buffer
dw -1
; name of the second stage bootloader. 11 characters exactly,
; upper case only, filename on the left, extension on the right,
; space padding in the middle
second_stage:
db "LOADER BIN"
; pad with NOPs to offset 510
times (510 + $$ - $) nop
; 2-byte magic bootsector signature
db 55h, 0AAh
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 2nd-stage code
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; second:
; ADR_SECOND EQU ((second - start) + ADR_START)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -