发信人: wenbobo()
整理人: wenbobo(2003-01-09 10:08:30), 站内信件
|
糟糕的代码,来自osd项目,这是wenbobo见过的最糟糕的臃肿的汇编代码,呵呵,不知道作者怎么想的
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; First-stage bootloader for FAT12 (DOS/Win) floppy
; Chris Giese <[email protected]>, http://www.execpc.com/~geezer
;
; This code is public domain (no copyright).
; You can do whatever you want with it.
;
; This code updated, fixed, and released on Nov 26, 2002
;
; BUILD:
; nasm -f bin -o fat12.bin fat12.asm
;
; DOS INSTALL (do NOT use RAWRITE):
; partcopy fat12.bin 0 3 -f0
; partcopy fat12.bin 24 1DC -f0 24
;
; UNIX INSTALL:
; dd bs=1 if=fat12.bin skip=0 count=3 of=/dev/fd0
; dd bs=1 if=fat12.bin skip=36 count=476 of=/dev/fd0 seek=36
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; SS_NAME must be in FAT format: all caps, 11 chars total, 8-char
; filename, 3-char extension, both padded on the right with spaces
%define SS_NAME "LOAD BIN" ; 2nd stage file name
SS_ADR EQU 10000h ; where to load 2nd stage
SS_ORG EQU 100h ; 2nd stage ORG value
; "No user-serviceable parts beyond this point" :)
; Actually, you can modify it if you want.
; first-stage address offset linear
%define FS_ORG 100h ; 100h 7C00h
; 512-byte stack. Put this ABOVE the code in memory, so this code
; can also be built as a .COM file (for testing purposes only!)
ADR_STACK equ (FS_ORG + 400h) ; 500h 8000h
; one-sector directory buffer. I assume FAT sectors are no larger than 4K
ADR_DIRBUF equ ADR_STACK ; 500h 8000h
; two-sector FAT buffer -- two sectors because FAT12
; entries are 12 bits and may straddle a sector boundary
ADR_FATBUF equ (ADR_DIRBUF + 1000h) ; 1500h 9000h
; start of unused memory: 3500h B000h
; use byte-offset addressing from BP for smaller code
%define VAR(x) ((x) - start) + bp
; bootsector loaded at physical address 07C00h, but address 100h
; will work if we load IP and segment registers properly.
; This is also the ORG address of a DOS .COM file
ORG FS_ORG
start:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; VARIABLES
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; (1 byte) drive we booted from; 0=A, 80h=C
boot_drive EQU (start - 1)
; (2 bytes) number of 16-byte paragraphs per sector
para_per_sector EQU (boot_drive - 2)
; (2 bytes) number of 32-byte FAT directory entries per sector
dir_ents_per_sector EQU (para_per_sector - 2)
; (2 bytes) sector where the actual disk data starts
; This is relative to partition start, so we need only 16 bits
data_start EQU (dir_ents_per_sector - 2)
; (2 bytes) number of 16-byte paragraphs per cluster
para_per_cluster EQU (data_start - 2)
jmp short skip_bpb
nop
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; MINIMAL BIOS PARAMETER BLOCK (BPB)
;
; 'Minimal' means just enough of a BPB so DOS/Windows:
; - recognizes this disk as a FAT12 floppy,
; - doesn't complain when you try to access the disk,
; - doesn't insist on a full format if you say "FORMAT /Q A:"
;
; Installation will use the BPB already present on your floppy disk.
; The values shown here work only with 1.44 meg disks (CHS=80:2:18)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
oem_id: ; offset 03h (03) - not used by this code
db "GEEZER", 0, 0
bytes_per_sector: ; offset 0Bh (11)
dw 512
sectors_per_cluster: ; offset 0Dh (13)
db 1
fat_start:
num_reserved_sectors: ; offset 0Eh (14)
dw 1
num_fats: ; offset 10h (16)
db 2
num_root_dir_ents: ; offset 11h (17)
dw 224
total_sectors: ; offset 13h (19) - not used by this code
dw 18 * 2 * 80
media_id: ; offset 15h (21) - not used by this code
db 0F0h
sectors_per_fat: ; offset 16h (22)
dw 9
sectors_per_track: ; offset 18h (24)
dw 18
heads: ; offset 1Ah (26)
dw 2
hidden_sectors: ; offset 1Ch (28)
dd 0
total_sectors_large: ; offset 20h (32) - not used by this code
dd 0
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; CODE
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
skip_bpb:
; put CPU into a known state
%ifdef DOS
mov dl,0 ; A: drive
%else
jmp ((7C00h - FS_ORG) / 16):fix_cs
fix_cs:
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,ADR_STACK
%endif
mov bp,start
mov [VAR(boot_drive)],dl
cld
; calculate some values that we need:
; 16-byte paragraphs per sector
mov ax,[VAR(bytes_per_sector)]
mov cl,4
shr ax,cl
mov [VAR(para_per_sector)],ax
; 16-byte paragraphics per cluster
xor dh,dh
mov dl,[VAR(sectors_per_cluster)]
mul dx
mov [VAR(para_per_cluster)],ax
; 32-byte FAT directory entries per sector
mov ax,[VAR(bytes_per_sector)]
mov bx,32 ; bytes/dirent
xor dx,dx
div bx
mov [VAR(dir_ents_per_sector)],ax
; number of sectors used for root directory (store in CX)
mov ax,[VAR(num_root_dir_ents)]
mul bx
div word [VAR(bytes_per_sector)]
mov cx,ax
; first sector of root directory
xor ah,ah
mov al,[VAR(num_fats)]
mul word [VAR(sectors_per_fat)]
add ax,[VAR(num_reserved_sectors)]
; first sector of disk data area:
mov si,ax
add si,cx
mov [VAR(data_start)],si
; scan root directory for file. We don't bother to check for deleted
; entries or 'virgin' entries (first byte = 0) that mark end of directory
mov bx,ADR_DIRBUF
next_sect:
push cx
mov cx,1
call read_sectors_chs
pop cx
jc disk_error
mov si,bx
push cx
mov cx,[VAR(dir_ents_per_sector)]
next_ent:
mov di,ss_name
push si
push cx
mov cx,11 ; 8.3 FAT filename
rep cmpsb
pop cx
pop si
je found_it
add si,32 ; bytes/dirent
loop next_ent
pop cx
add ax,byte 1 ; next sector
adc dx,byte 0
loop next_sect
mov al,'F' ; file not found; display blinking 'F'
; 'hide' the next 2-byte instruction by converting it to CMP AX,NNNN
; I learned this trick from Microsoft's Color Computer BASIC :)
db 3Dh
disk_error:
mov al,'R' ; disk read error; display blinking 'R'
error:
mov ah,9Fh ; blinking blue-on-white attribute
mov bx,0B800h ; xxx - 0B800h assumes color emulation...
mov es,bx ; ...should still be able to hear the beep
mov [es:0],ax
mov ax,0E07h ; *** BEEEP ***
int 10h
exit:
%ifdef DOS
mov ax,4C01h
int 21h
%else
mov ah,0 ; await key pressed
int 16h
int 19h ; re-start the boot process
%endif
found_it:
; leave the old CX value on the stack to save a byte or two
; Get conventional memory size (Kbytes) in AX
int 12h
; subtract load address
%ifdef DOS
mov dx,ds
add dx,((SS_ADR - 7C00h) / 16)
mov cl,6
shr dx,cl
sub ax,dx
%else
sub ax,(SS_ADR / 1024)
%endif
; convert from K to bytes
mov dx,1024
mul dx
; 32-bit file size (4 bytes) is at [si + 28]
; If second stage file is too big...
sub ax,[si + 28]
sbb dx,[si + 30]
; ...display a blinking 'M'
mov al,'M'
jc error
; get starting cluster of file
mov si,[si + 26]
; set load address DI:BX
xor bx,bx
%ifdef DOS
mov di,ds
add di,(SS_ADR / 16)
%else
mov di,(SS_ADR / 16)
%endif
xor ch,ch
mov cl,[VAR(sectors_per_cluster)]
next_cluster:
; convert 16-bit cluster value (in SI) to 32-bit LBA sector value (in DX:AX)
; and get next cluster in SI
call walk_fat
jc disk_error
; xxx - this will always load an entire cluster (e.g. 64 sectors),
; even if the file is shorter than this
mov es,di
call read_sectors_chs
jc disk_error
add di,[VAR(para_per_cluster)]
; 0FF6h: reserved 0FF7h: bad cluster
; 0FF8h-0FFEh: reserved 0FFFh: end of cluster chain
cmp si,0FF6h
jb next_cluster
; turn off floppy motor
mov dx,3F2h
mov al,0
out dx,al
; jump to second stage loaded at SS_ADR and ORGed to address SS_ORG
%ifdef DOS
; build or copy a PSP for the second stage? nah, too much work...
mov ax,ds
add ax,((SS_ADR - SS_ORG) / 16)
%else
mov ax,((SS_ADR - SS_ORG) / 16)
%endif
mov ds,ax
mov es,ax
; we leave SS:SP as they were
; Here is the actual far 'jump' to the second stage (RETF, actually)
push ax
mov bx,SS_ORG
push bx
retf
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: read_sectors_chs
; action: reads one or more disk sectors using INT 13h AH=02h
; in: DX:AX=LBA number of sector to read (relative to
; start of partition), CX=sector count, ES:BX -> buffer
; out (disk error): CY=1
; out (success): CY=0
; modifies: (nothing)
; minimum CPU: 8088
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
read_sectors_chs:
push es
push di
push dx
push cx
push ax
; DX:AX==LBA sector number
; add partition start (= number of hidden sectors)
add ax,[VAR(hidden_sectors + 0)]
adc dx,[VAR(hidden_sectors + 2)]
inc cx
jmp short rsc_3
rsc_1:
push dx
push cx
push ax
; DX:AX=LBA sector number
; divide by number of sectors per track to get sector number
; Use 32:16 DIV instead of 64:32 DIV for 8088 compatability
; Use two-step 32:16 divide to avoid overflow
mov cx,ax
mov ax,dx
xor dx,dx
div word [VAR(sectors_per_track)]
xchg cx,ax
div word [VAR(sectors_per_track)]
xchg cx,dx
; DX:AX=quotient, CX=remainder=sector (S) - 1
; divide quotient by number of heads
mov di,ax
mov ax,dx
xor dx,dx
div word [VAR(heads)]
xchg di,ax
div word [VAR(heads)]
xchg di,dx
; DX:AX=quotient=cylinder (C), DI=remainder=head (H)
; error if cylinder >=1024
or dx,dx ; DX != 0; so cyl >= 65536
stc
jne rsc_2
cmp ah,4 ; AH >= 4; so cyl >= 1024
cmc
jb rsc_2
; move variables into registers for INT 13h AH=02h
mov dx,di
mov dh,dl ; DH=head
inc cx ; CL5:0=sector
mov ch,al ; CH=cylinder 7:0
shl cl,1
shl cl,1
shr ah,1
rcr cl,1
shr ah,1
rcr cl,1 ; CL7:6=cylinder 9:8
mov dl,[VAR(boot_drive)] ; DL=drive
; we call INT 13h AH=02h once for each sector. Multi-sector reads
; may fail if we cross a track or 64K boundary
mov ax,0201h ; AH=02h, AL=num_sectors
int 13h
jnc rsc_2
; reset drive
xor ax,ax
int 13h
jc rsc_2
; try read again
mov ax,0201h
int 13h
rsc_2:
pop ax
pop cx
pop dx
jc rsc_4
; increment segment part of address and LBA sector number, and loop
mov di,es
add di,[VAR(para_per_sector)]
mov es,di
inc ax
jne rsc_3
inc dx
rsc_3:
loop rsc_1
rsc_4:
pop ax
pop cx
pop dx
pop di
pop es
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name: walk_fat
; action: converts cluster number to sector number and
; finds next cluster in chain
; in: SI = cluster
; out (disk error): CY=1
; out (success): CY=0, SI = next cluster, DX:AX = sector number
; modifies: AX, DX, SI
; minimum CPU: 8088
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
walk_fat:
push es
push di
push cx
push bx
; cluster 2 is the first data cluster
lea ax,[si - 2]
; convert from clusters to sectors
mov dh,0
mov dl,[VAR(sectors_per_cluster)]
mul dx
add ax,[VAR(data_start)]
adc dx,byte 0
; DX:AX is return value: save it
push dx
push ax
; prepare to load FAT
%ifdef DOS
mov ax,ds
add ax,(ADR_FATBUF / 16)
%else
mov ax,(ADR_FATBUF / 16)
%endif
mov es,ax
xor bx,bx
; FAT12 entries are 12 bits, bytes are 8 bits. Ratio is 3 / 2,
; so multiply cluster by 3 now, and divide by 2 later.
xor dx,dx
mov ax,si
shl ax,1
rcl dx,1
add ax,si
adc dx,byte 0
; DX:AX b0 =use high or low 12 bits of 16-bit value read from FAT
; DX:AX b9:1 =byte offset into FAT sector (9 bits assumes 512-byte sectors)
; DX:AX b?:10 =which sector of FAT to load
mov di,ax
shr dx,1
rcr ax,1
div word [VAR(bytes_per_sector)]
; remainder is byte offset into FAT sector: put it in SI
mov si,dx
; quotient in AX is FAT sector: add FAT starting sector
add ax,[VAR(fat_start)]
; check the FAT buffer to see if this sector is already loaded
; (simple disk cache; speeds things up a little --
; actually, it speeds things up a lot)
cmp ax,[curr_sector]
je wf_1
mov [curr_sector],ax
; read the target FAT sector. FAT12 entries may straddle a sector
; boundary, so read 2 sectors.
xor dx,dx
mov cx,2
call read_sectors_chs
jc wf_4
wf_1:
; get 16 bits from FAT
mov ax,[es:bx + si]
; look at CX:0 to see if we want the high 12 bits or the low 12 bits
shr di,1
jc wf_2
and ax,0FFFh ; CY=1: use low 12 bits
jmp short wf_3
wf_2:
mov cl,4
shr ax,cl ; CY=0: use high 12 bits
wf_3:
mov si,ax
; clear CY bit to signal success
xor dx,dx
wf_4:
pop ax
pop dx
pop bx
pop cx
pop di
pop es
ret
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; DATA
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; which sector is in the FAT buffer
; this is relative to partition start, so we need only 16 bits
curr_sector:
dw -1
ss_name:
db SS_NAME
; pad with NOPs to offset 510
times (510 + $$ - $) nop
; 2-byte magic bootsector signature
db 55h, 0AAh
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
优秀的范例,FreeDos的代码,十几年来变动不大:
;
; File:
; boot.asm
; Description:
; DOS-C boot
;
; Copyright (c) 1997;
; Svante Frey
; All Rights Reserved
;
; This file is part of DOS-C.
;
; DOS-C is free software; you can redistribute it and/or
; modify it under the terms of the GNU General Public License
; as published by the Free Software Foundation; either version
; 2, or (at your option) any later version.
;
; DOS-C is distributed in the hope that it will be useful, but
; WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
; the GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public
; License along with DOS-C; see the file COPYING. If not,
; write to the Free Software Foundation, 675 Mass Ave,
; Cambridge, MA 02139, USA.
;
;
; +--------+
; | |
; | |
; |--------| 4000:0000
; | |
; | FAT |
; | |
; |--------| 2000:0000
; |BOOT SEC|
; |RELOCATE|
; |--------| 1FE0:0000
; | |
; | |
; | |
; | |
; |--------|
; |BOOT SEC|
; |ORIGIN | 07C0:0000
; |--------|
; | |
; | |
; | |
; |--------|
; |KERNEL |
; |LOADED |
; |--------| 0060:0000
; | |
; +--------+
;%define ISFAT12 1
;%define ISFAT16 1
;%define CALCPARAMS 1
;%define MULTI_SEC_READ 1
segment .text
%define BASE 0x7c00
org BASE
Entry: jmp short real_start
nop
; bp is initialized to 7c00h
%define bsOemName bp+0x03 ; OEM label
%define bsBytesPerSec bp+0x0b ; bytes/sector
%define bsSecPerClust bp+0x0d ; sectors/allocation unit
%define bsResSectors bp+0x0e ; # reserved sectors
%define bsFATs bp+0x10 ; # of fats
%define bsRootDirEnts bp+0x11 ; # of root dir entries
%define bsSectors bp+0x13 ; # sectors total in image
%define bsMedia bp+0x15 ; media descrip: fd=2side9sec, etc...
%define sectPerFat bp+0x16 ; # sectors in a fat
%define sectPerTrack bp+0x18 ; # sectors/track
%define nHeads bp+0x1a ; # heads
%define nHidden bp+0x1c ; # hidden sectors
%define nSectorHuge bp+0x20 ; # sectors if > 65536
%define drive bp+0x24 ; drive number
%define extBoot bp+0x26 ; extended boot signature
%define volid bp+0x27
%define vollabel bp+0x2b
%define filesys bp+0x36
%define LOADSEG 0x0060
%define FATBUF 0x2000 ; offset of temporary buffer for FAT
; chain
; Some extra variables
;%define StoreSI bp+3h ;temp store
; To save space, functions that are just called once are
; implemented as macros instead. Four bytes are saved by
; avoiding the call / ret instructions.
; GETDRIVEPARMS: Calculate start of some disk areas.
;
%macro GETDRIVEPARMS 0
mov si, word [nHidden]
mov di, word [nHidden+2]
add si, word [bsResSectors]
adc di, byte 0 ; DI:SI = first FAT sector
mov word [fat_start], si
mov word [fat_start+2], di
mov al, [bsFATs]
xor ah, ah
mul word [sectPerFat] ; DX:AX = total number of FAT sectors
add si, ax
adc di, dx ; DI:SI = first root directory sector
mov word [root_dir_start], si
mov word [root_dir_start+2], di
; Calculate how many sectors the root directory occupies.
mov bx, [bsBytesPerSec]
mov cl, 5 ; divide BX by 32
shr bx, cl ; BX = directory entries per sector
mov ax, [bsRootDirEnts]
xor dx, dx
div bx
mov word [RootDirSecs], ax ; AX = sectors per root directory
add si, ax
adc di, byte 0 ; DI:SI = first data sector
mov [data_start], si
mov [data_start+2], di
%endmacro
;-----------------------------------------------------------------------
times 0x3E-$+$$ db 0
%define tempbuf bp+0x3E
dw LOADSEG
%ifdef CALCPARAMS
%define RootDirSecs bp+0x27 ; # of sectors root dir uses
%define fat_start bp+0x29 ; first FAT sector
%define root_dir_start bp+0x2D ; first root directory sector
%define data_start bp+0x31 ; first data sector
%else
%define RootDirSecs bp+0x40 ; # of sectors root dir uses
dw 0
%define fat_start bp+0x42 ; first FAT sector
dd 0
%define root_dir_start bp+0x46 ; first root directory sector
dd 0
%define data_start bp+0x4A ; first data sector
dd 0
%endif
;-----------------------------------------------------------------------
; ENTRY
;-----------------------------------------------------------------------
real_start: cli
cld
xor ax, ax
mov ss, ax ; initialize stack
mov ds, ax
mov bp, 0x7c00
lea sp, [bp-0x20]
sti
cmp byte [drive], 0xff ; BIOS bug ??
jne dont_use_dl
mov [drive], dl ; BIOS passes drive number in DL
; a reset should not be needed here
dont_use_dl:
; int 0x13 ; reset drive
; int 0x12 ; get memory available in AX
; mov ax, 0x01e0
; mov cl, 6 ; move boot sector to higher memory
; shl ax, cl
; sub ax, 0x07e0
mov ax, 0x1FE0
mov es, ax
mov si, bp
mov di, bp
mov cx, 0x0100
rep movsw
jmp word 0x1FE0:cont
cont: mov ds, ax
mov ss, ax
call print
db "FreeDOS",0
%ifdef CALCPARAMS
GETDRIVEPARMS
%endif
; FINDFILE: Searches for the file in the root directory.
;
; Returns:
; AX = first cluster of file
; First, read the whole root directory
; into the temporary buffer.
mov ax, word [root_dir_start]
mov dx, word [root_dir_start+2]
mov di, word [RootDirSecs]
xor bx, bx
mov word [tempbuf], LOADSEG
mov es, [tempbuf]
call readDisk
jc jmp_boot_error
xor di, di
; Search for KERNEL.SYS file name, and find start cluster.
next_entry: mov cx, 11
mov si, filename
push di
repe cmpsb
pop di
mov ax, [es:di+0x1A]; get cluster number from directory entry
je ffDone
add di, byte 0x20 ; go to next directory entry
cmp byte [es:di], 0 ; if the first byte of the name is 0,
jnz next_entry ; there is no more files in the directory
jc boot_error ; fail if not found
ffDone:
push ax ; store first cluster number
call print
db " FAT",0
; GETFATCHAIN:
;
; Reads the FAT chain and stores it in a temporary buffer in the first
; 64 kb. The FAT chain is stored an array of 16-bit cluster numbers,
; ending with 0.
;
; The file must fit in conventional memory, so it can't be larger than
; 640 kb. The sector size must be at least 512 bytes, so the FAT chain
; can't be larger than around 3 kb.
;
; Call with: AX = first cluster in chain
; Load the complete FAT into memory. The FAT can't be larger
; than 128 kb, so it should fit in the temporary buffer.
mov es, [tempbuf]
xor bx, bx
mov di, [sectPerFat]
mov ax, word [fat_start]
mov dx, word [fat_start+2]
call readDisk
pop ax ; restore first cluster number
jmp_boot_error: jc boot_error
; Set ES:DI to the temporary storage for the FAT chain.
push ds
push es
pop ds
pop es
mov di, FATBUF
next_clust: stosw ; store cluster number
mov si, ax ; SI = cluster number
%ifdef ISFAT12
; This is a FAT-12 disk.
fat_12: add si, si ; multiply cluster number by 3...
add si, ax
shr si, 1 ; ...and divide by 2
lodsw
; If the cluster number was even, the cluster value is now in
; bits 0-11 of AX. If the cluster number was odd, the cluster
; value is in bits 4-15, and must be shifted right 4 bits. If
; the number was odd, CF was set in the last shift instruction.
jnc fat_even
mov cl, 4
shr ax, cl ; shift the cluster number
fat_even: and ah, 0x0f ; mask off the highest 4 bits
cmp ax, 0x0ff8 ; check for EOF
jb next_clust ; continue if not EOF
%endif
%ifdef ISFAT16
; This is a FAT-16 disk. The maximal size of a 16-bit FAT
; is 128 kb, so it may not fit within a single 64 kb segment.
fat_16: mov dx, [tempbuf]
add si, si ; multiply cluster number by two
jnc first_half ; if overflow...
add dh, 0x10 ; ...add 64 kb to segment value
first_half: mov ds, dx ; DS:SI = pointer to next cluster
lodsw ; AX = next cluster
cmp ax, 0xfff8 ; >= FFF8 = 16-bit EOF
jb next_clust ; continue if not EOF
%endif
finished: ; Mark end of FAT chain with 0, so we have a single
; EOF marker for both FAT-12 and FAT-16 systems.
xor ax, ax
stosw
push cs
pop ds
call print
db " Kernel",0 ; "KERNEL"
; loadFile: Loads the file into memory, one cluster at a time.
mov es, [tempbuf] ; set ES:BX to load address
xor bx, bx
mov si, FATBUF ; set DS:SI to the FAT chain
cluster_next: lodsw ; AX = next cluster to read
or ax, ax ; if EOF...
je boot_success ; ...boot was successful
dec ax ; cluster numbers start with 2
dec ax
mov di, word [bsSecPerClust]
and di, 0xff ; DI = sectors per cluster
mul di
add ax, [data_start]
adc dx, [data_start+2] ; DX:AX = first sector to read
call readDisk
jnc cluster_next
boot_error: call print
db 13,10,"BOOT err!",0
xor ah,ah
int 0x16 ; wait for a key
int 0x19 ; reboot the machine
boot_success: call print
db " GO! ",0
mov bl, [drive]
jmp word LOADSEG:0
; prints text after call to this function.
print_1char:
xor bx, bx ; video page 0
mov ah, 0x0E ; else print it
int 0x10 ; via TTY mode
print: pop si ; this is the first character
print1: lodsb ; get token
push si ; stack up potential return address
cmp al, 0 ; end of string?
jne print_1char ; until done
ret ; and jump to it
; readDisk: Reads a number of sectors into memory.
;
; Call with: DX:AX = 32-bit DOS sector number
; DI = number of sectors to read
; ES:BX = destination buffer
; ES must be 64k aligned (1000h, 2000h etc).
;
; Returns: CF set on error
; ES:BX points one byte after the last byte read.
readDisk: push si
read_next: push dx
push ax
;******************** LBA_READ *******************************
; check for LBA support
push bx
mov ah,041h ;
mov bx,055aah ;
mov dl, [drive]
test dl,dl ; don't use LBA addressing on A:
jz read_normal_BIOS ; might be a (buggy)
; CDROM-BOOT floppy emulation
int 0x13
jc read_normal_BIOS
sub bx,0aa55h
jne read_normal_BIOS
shr cx,1 ; CX must have 1 bit set
jnc read_normal_BIOS
; OK, drive seems to support LBA addressing
lea si,[LBA_DISK_PARAMETER_BLOCK]
; setup LBA disk block
mov [si+12],bx
mov [si+14],bx
pop bx
pop ax
pop dx
push dx
push ax
mov [si+ 8],ax
mov [si+10],dx
mov [si+4],bx
mov [si+6],es
mov ah,042h
jmp short do_int13_read
LBA_DISK_PARAMETER_BLOCK:
db 10h ; constant size of block
db 0
dw 1 ; 1 sector read
; and overflow into code !!!
read_normal_BIOS:
pop bx
pop ax
pop dx
push dx
push ax
;******************** END OF LBA_READ ************************
;
; translate sector number to BIOS parameters
;
;
; abs = sector offset in track
; + head * sectPerTrack offset in cylinder
; + track * sectPerTrack * nHeads offset in platter
;
xchg ax, cx
mov al, [sectPerTrack]
mul byte [nHeads]
xchg ax, cx
; cx = nHeads * sectPerTrack <= 255*63
; dx:ax = abs
div cx
; ax = track, dx = sector + head * sectPertrack
xchg ax, dx
; dx = track, ax = sector + head * sectPertrack
div byte [sectPerTrack]
; dx = track, al = head, ah = sector
mov cx, dx
; cx = track, al = head, ah = sector
; the following manipulations are necessary in order to
; properly place parameters into registers.
; ch = cylinder number low 8 bits
; cl = 7-6: cylinder high two bits
; 5-0: sector
mov dh, al ; save head into dh for bios
xchg ch, cl ; set cyl no low 8 bits
ror cl, 1 ; move track high bits into
ror cl, 1 ; bits 7-6 (assumes top = 0)
mov al, byte [sectPerTrack]
sub al, ah ; al has # of sectors left
inc ah ; sector offset from 1
or cl, ah ; merge sector into cylinder
%ifdef MULTI_SEC_READ
; Calculate how many sectors can be transfered in this read
; due to dma boundary conditions.
push dx
mov si, di ; temp register save
; this computes remaining bytes because of modulo 65536
; nature of dma boundary condition
mov ax, bx ; get offset pointer
neg ax ; and convert to bytes
jz ax_min_1 ; started at seg:0, skip ahead
xor dx, dx ; convert to sectors
div word [bsBytesPerSec]
cmp ax, di ; check remainder vs. asked
jb ax_min_1 ; less, skip ahead
mov si, ax ; transfer only what we can
ax_min_1: pop dx
; Check that request sectors do not exceed track boundary
mov si, [sectPerTrack]
inc si
mov ax, cx ; get the sector/cyl byte
and ax, 0x3f ; and mask out sector
sub si, ax ; si has how many we can read
mov ax, di
cmp si, di ; see if asked <= available
jge ax_min_2
mov ax, si ; get what can be xfered
ax_min_2: push ax
mov ah, 2
mov dl, [drive]
int 0x13
pop ax
%else
mov ax, 0x0201
do_int13_read:
mov dl, [drive]
int 0x13
%endif
read_finished:
jnc read_ok ; jump if no error
xor ah, ah ; else, reset floppy
int 0x13
pop ax
pop dx ; and...
read_next_chained:
jmp short read_next ; read the same sector again
read_ok:
%ifdef MULTI_SEC_READ
mul word [bsBytesPerSec] ; add number of bytes read to BX
add bx, ax
%else
add bx, word [bsBytesPerSec]
%endif
jnc no_incr_es ; if overflow...
mov ax, es
add ah, 0x10 ; ...add 1000h to ES
mov es, ax
no_incr_es: pop ax
pop dx ; DX:AX = last sector number
%ifdef MULTI_SEC_READ
add ax, si
adc dx, byte 0 ; DX:AX = next sector to read
sub di,si ; if there is anything left to read,
jg read_next ; continue
%else
add ax, byte 1
adc dx, byte 0 ; DX:AX = next sector to read
dec di ; if there is anything left to read,
jnz read_next_chained ; continue
%endif
clc
pop si
ret
filename db "KERNEL SYS"
times 0x01fe-$+$$ db 0
sign dw 0xAA55
---- 掬水月在手
弄花香满身 |
|