📄 bootf.asm
字号:
stosd ;{1H}
mov ax, (1024-3)*2
xchg ax, cx
rep stosw
mov ax, 0xD007 ;0FF800000 page tbl pointer
stosd ;{1F}
mov ah, 0xE0 ;Page directory self pointer
stosd ;{1G}
mov al, 0
mov CR3, eax ;Set up page directory
mov eax, CR0 ;Turn on paging and protected mode
or eax, 0x80000001
mov CR0, eax
mov cl, flat_data ;Setup ds and es
push cx ;{5}
pop ds
mov es, cx
jmp dword 8:0xFF800000 ;Go
read_sectors:
; Input:
; EAX = LBN
; DI = sector count
; ES = segment
; Output:
; EBX high half cleared
; DL = drive #
; EDX high half cleared
; ESI = 0
; Clobbered:
; BX, CX, DH
push eax
push di
push es
.10: push eax ;LBN
cdq ;edx = 0
movzx ebx, byte [boot+BB_sec]
div ebx ;EAX=track ;EDX=sector-1
mov cx, dx ;CL=sector-1 ;CH=0
sub bl, dl ;BX = max transfer before end of track
cmp di, bx ;Do we want more than that?
ja .20 ;Yes, do just this much now
mov bx, di ;No, do it all now
.20: mov esi, ebx ;Save count for this transfer.
inc cx ;CL=Sector number
xor dx, dx
mov bl, [boot+BB_head]
div ebx ;EAX=cylinder ;EDX=head
mov dh, dl ;Head
mov dl, [boot] ;Drive
xchg ch, al ;CH=Low 8 bits of cylinder number; AL=0
shr ax, 2 ;AL[6:7]=High two bits of cylinder
or cl, al ;CX = Cylinder and sector
mov ax, si ;Sector count
mov ah, 2 ;Read
xor bx, bx
push ax
int 13h
pop ax
jnc .30
int 13h ;If at second you don't succeed, give up
jc near disk_error
.30: pop eax
add eax, esi ;Advance LBN
push si
shl si, 5
mov bx, es
add bx, si ;Advance segment
mov es, bx
pop si
sub di, si
ja .10
pop es
pop di
pop eax
xor si, si
ret
file_name db 'KERNEL BIN'
gdt start_gdt ;{9}
flat_code desc 0, 0xFFBFF, D_CODE+D_READ+D_BIG+D_BIG_LIM
flat_data desc 0, 0xFFFFF, D_DATA+D_WRITE+D_BIG+D_BIG_LIM
end_gdt
resb 0x1FE+$$-$
db 0x55, 0xAA ;Standard end of boot sector
;_____________________________________________________________________________
;
; Build/Install instructions:
;
; *) NASM -f obj bootf.asm
;
; *) JLOC bootf.lnk bootf.bin
;
; *) Start with a formatted floppy.
;
; *) PARTCOPY bootf.bin 0 3 -f0 0
; This step overwrites the JMP at the start of the floppy with a JMP $+3E
; With many formatters, that matches the JMP that the formatter put there
; and makes no difference. In most other cases it is safe to overwrite
; the JMP. If you want to preserve a different JMP, change the first
; "resb" in bootp.asm to adjust the address of "start".
;
; *) PARTCOPY bootf.bin 3E 1C2 -f0 3E
; This step copies the rest of bootp.bin to the floppy at offset 3E,
; skipping the parameters set up by format, so the diskette will still be
; useable as a DOS diskette.
;
; *) Copy some protected mode image to the file C:\KERNEL.BIN.
;
; *) Boot the floppy.
;_____________________________________________________________________________
;
; I left out most of the error handling (what do you expect for 1B1 bytes).
; For errors which it does detect, it just beeps once and hangs. Errors (and
; unsupported conditions) which it doesn't even try to detect include:
; a) Not running on a 386 or better
; b) Active partition is not a FAT16 partition
; c) Root directory is more than 608Kb long.
; d) TEST.BIN more than 4Mb long
; e) Total RAM less than 1MB plus actual size of TEST.BIN
; Errors which it does check for include:
; a) C-bit set after any int 13h
; b) No active partition
; c) No "TEST.BIN" in the root directory
;
; If this were a partition boot, you probably could leave out the code of
; reading the MBR, and finding and reading partition boot. Then you could
; add a few error checks. As a test boot used from a floppy, it needs to
; do that extra work and you just shouldn't use it if the basic conditions
; aren't valid. As part of a large multi-boot (another example I hope to
; document) it could be bigger than one sector.
;_____________________________________________________________________________
;
; {} Documentation connected to specific source code sections:
;
;{0} I wasn't sure what to assume about the CPU state when the BIOS (or
; earlier boot) transfers control here. Probably interrupts are disabled and
; DS=0; But I didn't assume that. Assuming that could save a couple bytes
; of code.
;
;{1} Memory use:
; {A} The boot itself is at 0:7C00
; {B} The top of the stack is 0:800
; {C} The directory and FAT are both read in and accessed starting at 800:0
; {D} The image is read in, then copied to 1MB (physical)
; {E} The page tables are built at 9C000 physical
; {F} The image is mapped to FFF80000 linear
; {G} The page tables are mapped to FFFC0000 linear
; {H} The first 4Mb is mapped linear = physical
;
;{2} Most of this code runs in "big real mode". That allows the code to make
; int 13h (and other) BIOS calls, but also to access extended memory directly.
;
;{3} I left out most error checks, and just beep once for the errors that are
; checked. That makes this code suitable for learning about pmode booting and
; for test use by pmode kernel developers. To package with an OS as end-user
; boot code, you need to either take out some of this code or load from
; some larger place, and add some real error messages.
;
;{4} One way you might want to reduce the work of the boot is to leave the job
; of enabling A20 to the kernel startup code:
; {A} Don't enable A20 here.
; {B} Load the image at 2Mb instead of at 1Mb. (Only odd Megabytes are
; unusable when A20 is disabled).
; {C} "shr eax,1" to convert 4Mb to 2Mb.
; Note that the current version has a minimum memory requirement of 1Mb plus
; size of the image. This change would require 2Mb plus the size of the image.
;
;{5} The first version of this code changed the value of segment registers
; directly after switching to pmode. It worked on all but one machine that I
; tested it on (a 386). On that machine, on the second switch to protected
; mode, the first segment register loaded got the right selector but a bad
; descriptor. I have read that you need a JMP after to switching to pmode, but
; successfully written MANY programs without that JMP and seen many more
; written by others. I guess there is a case in which a delay is required (the
; usual "flush the prefetch queue" theory doesn't fit the observed facts
; because I fixed the problem by changing "mov ds,cx" to "push cx", "pop ds";
; They each take only two bytes in the prefetch queue.
; Further testing has shown that on a 386 you may need more than a short
; instruction worth of delay after switching back to real mode. Any memory or
; I/O read is enough, so I rearranged the code to have at least one such read
; between a change of CR0 bit 0 and a write to an sreg. The "jmp short $+2"
; that others recommend would work because it forces a true fetch access (on
; a 386 at least). I don't use it because I don't want to waste two bytes in
; boot code.
;
;
;{6} Most of this code doesn't care whether interrupts are enabled or not, so
; I never enable them after the initial cli. BIOS's correctly enable
; interrupts DURING the processing of int 13h, if they need interrupts.
; Unfortunately, some BIOSs also enable interrupts on exit from int 13h.
; Interrupts must be disable for the switch to pmode, so another cli is
; required.
;
;{7} Only the low half of edi is used by read_sectors, so the high half
; doesn't need to be saved and restored across that use.
;
;{8} I call int 8 (IRQ 0) many times. This is done in case the code was
; loaded from floppy. It tricks the BIOS into turning the floppy motor off.
; I don't like to start my pmode tests with the floppy motor on.
;
;{9} The first entry in a GDT is never used by the CPU. A zero selector is
; defined as being safe to put into a segment register without loading a
; descriptor from the GDT (You can't access memory through it). I generally
; use the first 6 bytes of the GDT as a self pointer. Once you are used to
; this convention, it makes code more readable. The macros in gdt.inc set it
; up.
;_____________________________________________________________________________
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -