boot.s
boot.s is loaded at 0x7c00 by the bios-startup routines, and moves itself
out of the way to address 0x90000, and jumps there.
It then loads the system at 0x10000, using BIOS interrupts. Thereafter
it disables all interrupts, moves the system down to 0x0000, changes
to protected mode, and calls the start of system. System then must
RE-initialize the protected mode in it's own tables, and enable
interrupts as needed.
NOTE! currently system is at most 8*65536 bytes long. This should be no
problem, even in the future. I want to keep it simple. This 512 kB
kernel size should be enough - in fact more would mean we'd have to move
not just these start-up routines, but also do something about the cache-
memory (block IO devices). The area left over in the lower 640 kB is meant
for these. No other memory is assumed to be "physical", ie all memory
over 1Mb is demand-paging. All addresses under 1Mb are guaranteed to match
their physical addresses.
NOTE1 abouve is no longer valid in it's entirety. cache-memory is allocated
above the 1Mb mark as well as below. Otherwise it is mainly correct.
NOTE 2! The boot disk type must be set at compile-time, by setting
the following equ. Having the boot-up procedure hunt for the right
disk type is severe brain-damage.
The loader has been made as simple as possible (had to, to get it
in 512 bytes with the code to move to protected mode), and continuos
read errors will result in a unbreakable loop. Reboot by hand. It
loads pretty fast by getting whole sectors at a time whenever possible.
1.44Mb disks: sectors = 18
1.2Mb disks:
sectors = 15
720kB disks:
sectors = 9
.globl begtext, begdata, begbss, endtext, enddata, endbss
.text
begtext:
.data
begdata:
.bss
begbss:
.text
BOOTSEG = 0x07c0
IN99vSEG = 0x9000
SYSSEG = 0x1000 system loaded at 0x10000 (65536).
ENDSEG = SYSSEG + SYSSIZE SYSSIZE在Makefile中定義的 ^_^
entry start
start:
mov ax,#BOOTSEG 現在應仍處在REAL MODE下.
mov ds,ax 移動自身從BOOTSEG:0000到IN99vSEG:0000
mov ax,#IN99vSEG 共512字節.
mov es,ax 那麼BOOT.S處在0x90000-0x90200.
mov cx,#256
sub si,si
sub di,di
rep
movw
jmpi go,IN99vSEG
go: mov ax,cs
mov ds,ax 將DS,ES,SS均設為0x9000,所有數據都以
mov es,ax 0x9000為段偏移.
mov ss,ax 堆棧偏移0x9000
mov sp,#0x400 棧頂指針0x9000:0x0400,堆棧空間512bytes??
mov ah,#0x03 read cursor pos
xor bh,bh
int 0x10
mov cx,#24
mov bx,#0x0007 page 0, attribute 7 (normal)
mov bp,#msg1 顯示Loading System ...
mov ax,#0x1301 write string, move cursor
int 0x10
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 讀內核到0x10000
call kill_motor 殺了軟驅!? ^_^
if the read went well we get current cursor position ans save it for
posterity.
mov ah,#0x03 read cursor pos
xor bh,bh
int 0x10 save it in known place, con_init fetches
mov [510],dx it from 0x90510.
now we want to move to protected mode ...
cli no interrupts allowed !
first we move the system to it's rightful place
mov ax,#0x0000
cld 'direction'=0, movs moves forward
do_move:
mov es,ax destination segment
add ax,#0x1000
cmp ax,#0x9000
jz end_move
mov ds,ax source segment
sub di,di 置零,地址為0x1000:0000
sub si,si 置零,地址為0x9000:0000
mov cx,#0x8000 cx的作用是計數器
rep
movsw
j do_move 將位於低端0x1000:0000的內核移到內存
高端0x9000:0000,覆蓋了boot.S !?
then we load the segment descriptors
end_move:
mov ax,cs right, forgot this at first. didn't work :-)
mov ds,ax
lidt idt_48 idt_48和gdt_48都是一個3個Word長的數據結構
lgdt gdt_48 第一個字說明(Global Interrupt) Descriptor
Table有多長,因為每個Table是四個字長,所以
可以得出整個DescriptorTable的entries.(見下)
後兩個字指出DT的具體位置.
idt_48是0,0,0;應表示沒有中斷描述符entries.
gdt_48有256個入口,第一個是個空入口,然後
定義了一個code段和一個data段.基址都是
0x00000000, !?那裡是什麼東西???
*** 0x00000000 != 0x0000:0000 ***
that was painless, now we enable A20
call empty_8042
mov al,#0xD1 command write
out #0x64,al
call empty_8042
mov al,#0xDF A20 on
out #0x60,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.
初始化中斷處理器8259i
初始化順序為: 1. 向主8259A寫ICW1, 0x20
2. 向第二塊8259A寫ICW1, 0xA0
3. 向主8259A寫ICW2, 0x21
4. 向第二塊8259A寫ICW2, 0xA1
5. 如果ICW1指示有級聯中斷處理器,則初始化Master&Slave
(在下例中只有IR2有級聯8259A), 0x21, 0xA1
6. 向兩塊8259寫ICW4,指定工作模式.
輸入了適當的初始化命令之後, 8259已經准備好接收中斷請求.
現在向他輸入工作
命令字以規定其工作方式. 8259A共有三個工作命令字,但下例中只用過OCW1.
OCW1將所有的中斷都屏蔽掉, OCW2&OCW3也就沒什麼意義了.
** ICW stands for Initialization Command Word;
OCW for Operation Command Word.
1. mov al,#0x11
out #0x20,al
.word 0x00eb,0x00eb jmp $+2, jmp $+2
2. out #0xA0,al and to 8259A-2
.word 0x00eb,0x00eb
3. mov al,#0x20 向主8259A寫入ICW2.
out #0x21,al 硬件中斷入口地址0x20, 並由ICW1
得知中斷向量長度 = 8 bytes.
.word 0x00eb,0x00eb
4. mov al,#0x28 start of hardware int's 2 (0x28)
out #0xA1,al 第二塊8259A的中斷入口是0x28.
.word 0x00eb,0x00eb
5. mov al,#0x04 8259-1 is master
out #0x21,al Interrupt Request 2有級聯處理.
.word 0x00eb,0x00eb
mov al,#0x02 8259-2 is slave
out #0xA1,al 於上面對應,告訴大家我就是IR2對應
級聯處理器.
out #0xA1,al 於上面對應,告訴大家我就是IR2對應
級聯處理器.