由于代码是从Linux源代码翻译过来的,且赵炯博士已经注释的十分详细,这里就不再多说了。代码为汇编语言,编译环境为TASM+TLINK。
1。bootsect.asm,这个文件是从bootsect.s翻译过来的,功能相同,我在文件末尾增加了用来调试的error例程。内容为:
code segment
;define the const here
SYSSIZE = 3000h
SETUPLEN = 4 ; nr of setup-sectors
BOOTSEG = 07c0h ; original address of boot-sector
INITSEG = 9000h ; we move boot here - out of the way
SETUPSEG = 9020h ; setup starts here
SYSSEG = 1000h ; system loaded at 0x10000 (65536).
ENDSEG = SYSSEG + SYSSIZE ; where to stop loading
assume cs:code,ds:code,es:code
start:
mov ax,BOOTSEG
mov ds,ax
mov ax,INITSEG
mov es,ax
mov cx,256
sub si,si
sub di,di
rep movsw
;jump to 0x90000
mov bx,8ff0h
mov ds,bx
mov bx,00h
mov word ptr ds:[bx],offset go
mov word ptr ds:[bx]+2,9000h
mov bx,00h
jmp dword ptr [bx]
go LABEL FAR
mov ax,INITSEG
mov ds,ax
mov es,ax
; put stack at 0x9ff00.
mov ss,ax
mov sp,0FF00h ; arbitrary value >>512
; load the setup-sectors directly after the bootblock.
; Note that 'es' is already set up.
load_setup:
;copy sector 2,3,4,5 code to 0x90200,seg=0x9020
mov dx,0000h ; drive 0, head 0
mov cx,0002h ; sector 2, track 0
mov bx,0200h ; address = 512, in INITSEG
mov ax,0200h+SETUPLEN ; service 2, nr of sectors
int 13h ; read it
jnc ok_load_setup ; ok - continue
mov dx,0000h
mov ax,0000h ; reset the diskette
int 13h
jmp load_setup
ok_load_setup:
; Get disk drive parameters, specifically nr of sectors/track
mov dl,00h
mov ax,0800h ; AH=8 is get drive parameters
int 13h
mov ch,00h
mov sectors,cx
mov ax,INITSEG
mov es,ax
; Print some inane message
mov ah,03h ; read cursor pos
xor bh,bh
int 10h
mov cx,24
mov bx,0007h ; page 0, attribute 7 (normal)
mov bp,offset msg1
mov ax,1301h ; write string, move cursor
int 10h
; ok, we've written the message, now
; we want to load the system (at 0x10000)
mov ax,SYSSEG
mov es,ax ; segment of 0x010000
call read_it
call kill_motor
; After that we check which root-device to use. If the device is
; defined (;= 0), nothing is done and the given device is used.
; Otherwise, either /dev/PS0 (2,28) or /dev/at0 (2,8), depending
; on the number of sectors that the BIOS reports currently.
mov ax,root_dev
cmp ax,00h
jne root_defined
mov bx,sectors
mov ax,0208h ; /dev/ps0 - 1.2Mb
cmp bx,15
je root_defined
mov ax,021ch ; /dev/PS0 - 1.44Mb
cmp bx,18
je root_defined
undef_root: ;will loop for ever
jmp undef_root
root_defined:
mov root_dev,ax
; after that (everyting loaded), we jump to
; the setup-routine loaded directly after
; the bootblock:
;jump to 0x90200
mov bx,8ff0h
mov ds,bx
mov bx,00h
mov word ptr ds:[bx],0000h
mov word ptr ds:[bx]+2,9020h
mov bx,00h
jmp dword ptr [bx]
; This routine loads the system at address 0x10000, making sure
; no 64kB boundaries are crossed. We try to load it as fast as
; possible, loading whole tracks whenever we can.
;
; in: es - starting address segment (normally 0x1000)
;
sread dw 1+SETUPLEN ; sectors read of current track
head dw 0 ; current head
track dw 0 ; current track
read_it:
mov ax,es
test ax,0fffh
die: jne die ; es must be at 64kB boundary
xor bx,bx ; bx is starting address within segment
mov ax,INITSEG
mov ds,ax
rp_read:
mov ax,es
cmp ax,ENDSEG ; have we loaded all yet?
jb ok1_read
ret
ok1_read:
mov ax,INITSEG
mov ds,ax
mov ax,sectors
sub ax,sread
mov cx,ax
shl cx,09h
add cx,bx
jnc ok2_read
je ok2_read
xor ax,ax
sub ax,bx
shr ax,09h
ok2_read:
call read_track
mov cx,ax
add ax,sread
cmp ax,sectors
jne ok3_read
mov ax,01h
sub ax,head
jne ok4_read
inc track
ok4_read:
mov head,ax
xor ax,ax
ok3_read:
mov sread,ax
shl cx,09h
add bx,cx
jnc rp_read
mov ax,es
add ax,1000h
mov es,ax
xor bx,bx
jmp rp_read
read_track:
push ax
push bx
push cx
push dx
mov dx,track
mov cx,sread
inc cx
mov ch,dl
mov dx,head
mov dh,dl
mov dl,00h
and dx,0100h
mov ah,02h
int 13h
jc bad_rt
pop dx
pop cx
pop bx
pop ax
ret
bad_rt:
mov ax,00h
mov dx,00h
int 13h
pop dx
pop cx
pop bx
pop ax
jmp read_track
;/*
; * This procedure turns off the floppy drive motor, so
; * that we enter the kernel in a known state, and
; * don't have to worry about it later.
; */
kill_motor:
push dx
mov dx,03f2h
mov al,0h
out dx,al
pop dx
ret
;read fail and show error,我自己加的,可以在需要的地方调用以显示信息
error:
mov ax,0b800h
mov es,ax
xor di,di
mov ax,INITSEG
mov ds,ax
mov si,offset strError
mov cx,20
repShow:
lodsb
stosb
mov al,87h
stosb
dec cx
cmp cx,0
jne repShow
ret
;define the vars here
sectors dw 00h
strError db 'ERROR***********ERROR***********'
msg1 db 13,10,'Loading system ...',13,10,13,10
; ROOT_DEV: 0x000 - same type of floppy as boot.
; 0x301 - first partition on first drive etc
ROOT_DEV dw 0
;ROOT_DEV = 0306h
code ends
end start
2。setup.asm,是从setup.s翻译过来的。但这里为16位段的386指令,且我增加了可以输出字符的代码段和数据段。内容为:
P386
jump macro selector,offset
db 0eah ;
dw offset ;
dw selector ;
endm
code segment use16
;define the const
INITSEG = 9000h ; we move boot here - out of the way
SYSSEG = 1000h ; system loaded at 0x10000 (65536).
SETUPSEG = 9020h ; this is the current segment
assume cs:code,ds:code,es:code
start:
mov ax,INITSEG ; this is done in bootsect already, but...
mov ds,ax
mov ah,03h ; read cursor pos
xor bh,bh
int 10h ; save it in known place, con_init fetches
mov ds:[0],dx ; it from 0x90000.
; Get memory size (extended mem, kB)
mov ah,88h
int 15h
mov ds:[2],ax
; Get video-card data:
mov ah,0fh
int 10h
mov ds:[4],bx ; bh = display page
mov ds:[6],ax ; al = video mode, ah = window width
; check for EGA/VGA and some config parameters
mov ah,12h
mov bl,10h
int 10h
mov ds:[8],ax
mov ds:[10],bx
mov ds:[12],cx
; Get hd0 data
mov ax,0000h
mov ds,ax
mov si,0104h ;4*41h
mov ax,INITSEG
mov es,ax
mov di,0080h
mov cx,10h
rep movsb
; Get hd1 data
mov ax,0000h
mov ds,ax
mov si,0118 ;4*46h
mov ax,INITSEG
mov es,ax
mov di,0090h
mov cx,10h
rep movsb
; Check that there IS a hd1 :-)
mov ax,01500h
mov dl,81h
int 13h
jc no_disk1
cmp ah,03h
je is_disk1
no_disk1:
mov ax,INITSEG
mov es,ax
mov di,0090h
mov cx,10h
mov ax,00h
rep stosb
is_disk1:
; now we want to move to protected mode ...
cli ; no interrupts allowed ;
; first we move the system to it's rightful place
mov ax,0000h
cld ; 'direction'=0, movs moves forward
do_move:
mov es,ax ; destination segment
add ax,1000h
cmp ax,9000h
jz end_move
mov ds,ax ; source segment
xor di,di
xor si,si
mov cx,8000h
rep movsw
jmp do_move
; then we load the segment descriptors
end_move:
mov ax,SETUPSEG ; right, forgot this at first. didn't work :-)
mov ds,ax
lidt qword ptr idt_48 ; load idt with 0,0 ;can't run in via cpu ,so mark off
lgdt qword ptr gdt_48 ; load gdt with whatever appropriate
; that was painless, now we enable A20
call empty_8042
mov al,0D1h ; command write
out 64h,al
call empty_8042
mov al,0DFh ; A20 on
out 60h,al
call empty_8042
; well, that went ok, I hope. Now we have to reprogram the interrupts :-(
; we put them right after the intel-reserved hardware interrupts, at
; int 0x20-0x2F. There they won't mess up anything. Sadly IBM really
; messed this up with the original PC, and they haven't been able to
; rectify it afterwards. Thus the bios puts interrupts at 0x08-0x0f,
; which is used for the internal hardware interrupts as well. We just
; have to reprogram the 8259's, and it isn't fun.
mov al,11h ; initialization sequence
out 20h,al ; send it to 8259A-1
dw 00ebh,00ebh ; jmp $+2, jmp $+2
out 0A0h,al ; and to 8259A-2
dw 00ebh,00ebh
mov al,20h ; start of hardware int's (0x20)
out 21h,al
dw 00ebh,00ebh
mov al,28h ; start of hardware int's 2 (0x28)
out 0A1h,al
dw 00ebh,00ebh
mov al,04h ; 8259-1 is master
out 21h,al
dw 00ebh,00ebh
mov al,02h ; 8259-2 is slave
out 0A1h,al
dw 00ebh,00ebh
mov al,01h ; 8086 mode for both
out 21h,al
dw 00ebh,00ebh
out 0A1h,al
dw 00ebh,00ebh
mov al,0FFh ; mask off all interrupts for now
out 21h,al
dw 00ebh,00ebh
out 0A1h,al
; well, that certainly wasn't fun :-(. Hopefully it works, and we don't
; need no steenking BIOS anyway (except for the initial loading :-).
; The BIOS-routine wants lots of unnecessary data, and it's less
; "interesting" anyway. This is how REAL programmers do it.
;
; Well, now's the time to actually move into protected mode. To make
; things as simple as possible, we do no register set-up or anything,
; we let the gnu-compiled 32-bit programs do that. We just jump to
; absolute address 0x00000, in 32-bit protected mode.
mov eax,00000001h
mov cr0,eax
mov ax,0001h ; protected mode (PE) bit
lmsw ax ; This is it;
;jmp offset 0 of segment 8 (cs)
;mov bx,0018h ;jump segment select
;mov ds,bx
;mov bx,00h
;mov word ptr ds:[bx],5000h ;because jump to 00h,then jump to 5000h in sec3 has error,so jump to 5000h dirctly here
;mov word ptr ds:[bx]+2,0008h
;mov bx,00h
;jmp dword ptr [ebx]
jump <0008h>,<0000h> ;now,sec3 use32,all is ok
; This routine checks that the keyboard command queue is empty
; No timeout is used - if this hangs there is something wrong with
; the machine, and we probably couldn't proceed anyway.
empty_8042:
dw 00ebh,00ebh
in al,64h ; 8042 status port
test al,02h ; is input buffer full?
jnz empty_8042 ; yes - loop
ret
;display,can run in protect mode
showMsg:
mov bx,0020h
mov es,bx
xor di,di
mov bx,0010h
mov ds,bx
mov si,offset strShow
mov cx,30
repeat:
lodsb
stosb
mov al,87h
stosb
dec cx
cmp cx,0
jne repeat
;define the vars
strShow db 'SECTOR2***********SECTOR2***********SECTOR2'
idt_48 dw 0,0,0
gdt dw 0,0,0,0 ; dummy
gdt1 dw 7FFFh,0000h,9A00h,00C0h ; granularity=4096, 386
gdt2 dw 7FFFh,0000h,9200h,00C0h ; 128MB
gdt3 dw 07FFh,0fff0h,9208h,00C0h ; for jmp use,base=8fff0h near the 90000h
gdt4 dw 07FFh,08000h,920Bh,00C0h ; for show Message use,base=b8000h,4MB size
; gdt limit=2048, 256 GDT entries
gdt_48 dw 800h,0200h+gdt,09h ; gdt base = 0X9xxxx
code ends
end start
3。setup.asm,是从setup.s翻译过来的。不过,这里有些改动,我把中断0-F分别用自己的函数实现了,并且加入了两个任务,他们通过TSS相互切换。这里的程序已经是32位的了,且声明支持386指令。内容为:
P386
jump macro selector,offset ;for 32 bit jmp
db 0eah ;
dw offset ;
dw 00h
dw selector ;
endm
call32 macro selector,offset ;for 32 bit call
db 09ah
dw offset
dw 00h
dw selector
endm
code segment use32
assume cs:code,ds:code,es:code
public showMsg
public strShow
pg_dir:
start:
mov ax,10h
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ss,ax ;setup ss,sp by nomad
mov esp,0ffffffh ;16MB upper
call setup_idt
call setup_gdt ; load gdt with whatever appropriate
mov ax,10h ; reload all the segment registers
mov ds,ax ; after changing gdt. CS was already
mov es,ax ; reloaded in 'setup_gdt'
mov fs,ax
mov gs,ax
xor eax,eax
A20:
inc eax ; check that A20 really IS enabled
mov ds:[000000h],eax ; loop forever if it isn't
cmp ds:[100000h],eax
je A20
;chach 80287
mov eax,cr0 ; check math chip
and eax,80000011h ; Save PG,PE,ET
; "orl x10020,%eax" here for 486 might be good
or eax,02h ; set MP
mov cr0,eax
call check_x87
jmp after_page_tables
;check the 80287/80387*******************
check_x87:
fninit
fstsw ax
cmp al,0
je x87 ;no coprocessor: have to set bits
mov eax,cr0
xor eax,06h ;reset MP, set EM
mov cr0,eax
ret
align 2
x87:
db 0DBh,0E4h ;fsetpm for 287, ignored by 387
ret
;setup the interrupt**********************
setup_idt:
lea edx,ignore_int
mov eax,00080000h
mov ax,dx ;selector = 0x0008 = cs
mov dx,8E00h ;interrupt gate - dpl=0, present
lea edi,idt
mov ecx,256
rp_sidt:
mov [edi],eax
mov 4[edi],edx
add edi,8
dec ecx
jne rp_sidt
;lidt qword ptr idt_48
;ret
;add 0-9 interrupt for test
add_int0:
lea edx,int_0
mov eax,00080000h
mov ax,dx ;selector = 0x0008 = cs
mov dx,8E00h ;interrupt gate - dpl=0, present
lea edi,idt
add edi,8*0
mov [edi],eax
mov 4[edi],edx
add_int1:
lea edx,int_1
mov eax,00080000h
mov ax,dx ;selector = 0x0008 = cs
mov dx,8E00h ;interrupt gate - dpl=0, present
lea edi,idt
add edi,8*1
mov [edi],eax
mov 4[edi],edx