随笔-80  评论-24  文章-0  trackbacks-0
本篇主要将boot程序所做的工作做一笔记。
先看代码吧:

  1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2 ; 该文件主要用于加载loader和kernel入内存,其中默认loader和kernel是被压缩成System.Image
  3 ; 文件存放到软盘的第二个扇区起始的地方,loader最大大小不应超过128KB,loader+kernel不应
  4 ; 超过512KB - 32KB = 480KB大小,否则系统将崩溃。
  5 
  6 ; Author :  
  7 ; v 0.01 2011/11/22
  8 ;
  9 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 10 org 0x7c00
 11 jmp start
 12 
 13 ; loader和kernel映像一起被加载到
 14 ; 内存段基址:0x8000 (* 0x10)
 15 ; 即在1MB寻址范围的正中间
 16 ; 将来还会从0xf0000地址起始处放置一些系统参数
 17 ; 因此loader+kernel大小不应超过512KB - 32KB = 480KB,如果
 18 ; 超过这个数字那么地址将回滚,系统崩溃
 19 LOADER_ADDR equ 0x8000
 20 
 21 start:
 22     mov ax, cs
 23     mov ds, ax
 24     mov es, ax
 25     mov ss, ax
 26     mov sp, 0x7b00 ; 栈顶为0x7b00,向低地址延伸;其实boot中我们没有用到栈,因此不需设置堆栈
 27     
 28     ; 清屏
 29     mov ax, 0x0600
 30     mov bx, 0x0700
 31     mov cx, 0
 32     mov dx, 0x184f
 33     int 0x10
 34 
 35     ; 设置光标位置
 36     mov ah, 0x02
 37     mov bh, 0x00
 38     mov dx, 0x0
 39     int 0x10
 40 
 41 get_disk_param:
 42     mov dl, 0x00 ; 读软盘
 43     mov ax, 0x0800 ; 获取磁盘参数
 44     int 0x13
 45     jnc goon_get_disk_param ; 如果读磁盘驱动器参数成功则保存相关参数
 46     mov dx, 0x0000 ; 出错则将磁盘复位然后无限循环读
 47     mov ax, 0x0000
 48     int 0x13
 49     jmp get_disk_param ; 出错则将磁盘复位然后无限循环读
 50 
 51 goon_get_disk_param:
 52     mov bl, cl
 53     and bl, 0x3f
 54     mov byte [SECTOR_PER_TRACK], bl ; SECTOR_PER_TRACK保存每磁道最大扇区数
 55     shr cx, 6
 56     mov word [TRACK_NUM], cx ; TRACK_NUM保存最大磁道号
 57 
 58     ; 获取当前光标位置
 59     mov ah, 0x03
 60     xor bh, bh
 61     int 0x10
 62 
 63     ; 打印一些信息
 64     mov ax, cs
 65     mov es, ax
 66     mov cx, MSG1_LEN
 67     mov bx, 0x0007
 68     mov bp, MSG1 ; 显示字符串"Loading System "
 69     mov ax, 0x1301
 70     int 0x10
 71 
 72 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 73 ; 加载loader和kernel文件,存放位置在0x80000起始的地方
 74 ; loader+kernel大小不应超过480KB,否则由于在实模式下仅有
 75 ; 1MB的寻址能力,loader+kernel的存放空间将超过0x80000~0xf0000的大小
 76 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 77 
 78 load_loader_and_kernel:
 79     mov ax, 0x00
 80     mov byte al, [LOADER_LEN]
 81     add word ax, [KERNEL_LEN]
 82     mov word [CUR_UNREAD_SECTOR], ax ; 初始化当前未加载的扇区数
 83     mov ax, LOADER_ADDR
 84     mov es, ax
 85     mov bx, 0x00 ; es:bx指向loader+kernel映像加载的位置:0x8000 * 0x10 + 0x0
 86 
 87 read_one_track:
 88     mov word ax, [CUR_TRACK]
 89     mov ch, al ; 当前读到的磁道号的低8位
 90     shl ah, 0x06
 91     mov cl, ah
 92     mov byte al, [CUR_READ]
 93     inc al
 94     or cl, al ; cl寄存器高两位为当前读到的磁道号的高两位,低六位为开始扇区号
 95     mov byte dh, [CUR_HEAD] ; 当前的磁头号
 96     mov dl, 0x00 ; 驱动器号为0,代表是软盘
 97     mov ah, 0x02 ; 读扇区
 98     mov byte al, [SECTOR_PER_TRACK]
 99     sub byte al, [CUR_READ] ; 要读取的扇区数量
100     int 0x13
101     mov byte cl, [SECTOR_PER_TRACK]
102     sub byte cl, [CUR_READ]
103     cmp al, cl
104     jne die ; 如果实际读出的扇区数比所请求的扇区数少,则报错
105     xor ah, ah
106     sub word [CUR_UNREAD_SECTOR], ax ; 修改当前剩余的未读扇区数
107     jc finish_loading
108     cmp word [CUR_UNREAD_SECTOR], 0x00
109     je finish_loading ; 如果已经读完loader和kernel,则跳出
110     shl ax, 0x09 ; al * 512代表本次读出的字节数
111     add bx, ax
112     jno no_overflow
113     mov cx, es
114     add cx, 0x1000
115     cmp cx, 0xf000
116     ja die ; es已经超过了1MB的界限,说明loader+kernel过大
117     mov es, cx ; 修改es:bx缓冲区地址
118 no_overflow:
119     mov byte [CUR_READ], 0x0
120     cmp byte [CUR_HEAD], 0x0
121     je read_head_1
122     add byte [CUR_TRACK], 0x01
123     mov word ax, [TRACK_NUM]
124     cmp word [CUR_TRACK], ax
125     ja die
126 read_head_1:
127     add byte [CUR_HEAD], 0x01
128     and byte [CUR_HEAD], 0x01 ; 磁头号由0转为1,或者由1转为0
129     jmp read_one_track
130 
131 die:
132     ; 获取当前光标位置
133     mov ah, 0x03
134     xor bh, bh
135     int 0x10
136 
137     ; 打印一些信息
138     mov ax, cs
139     mov es, ax
140     mov cx, MSG2_LEN
141     mov bx, 0x0007
142     mov bp, MSG2 ; 显示字符串"The loader or kernel is too long "
143     mov ax, 0x1301
144     int 0x10
145 
146     xor ax, ax
147     int 0x16
148     int 0x19
149     jmp $
150 
151 finish_loading:
152     ; 关闭软驱马达
153     mov dx, 0x03f2
154     mov al, 0
155     out dx, al
156     nop
157     
158     ; 获取当前光标位置
159     mov ah, 0x03
160     xor bh, bh
161     int 0x10
162 
163     ; 打印一些信息
164     mov ax, cs
165     mov es, ax
166     mov cx, MSG3_LEN
167     mov bx, 0x0007
168     mov bp, MSG3 ; 显示字符串"Success to load the loader and kernel\n\nEntering loader "
169     mov ax, 0x1301
170     int 0x10
171 
172     jmp LOADER_ADDR:0 ; 跳转到loader去执行
173 
174 ; 要显示的字符串
175 MSG1:                db 1310"Loading loader and kernel "1310
176 MSG1_LEN            equ $ - MSG1
177 MSG2:                db 1310"The loader or kernel is too long "1310
178 MSG2_LEN            equ $ - MSG2
179 MSG3:                db 1310"Entering loader "1310
180 MSG3_LEN            equ $ - MSG3
181 
182 CUR_READ:            db 1 ; 当前磁道已经读出的扇区数
183 CUR_HEAD:            db 0 ; 当前的磁头号
184 CUR_TRACK:            dw 0 ; 当前读到的磁道号
185 CUR_UNREAD_SECTOR    dw 0 ; 当前剩余的未读扇区数
186 
187 TRACK_NUM:            dw 0 ; 最大磁道号
188 SECTOR_PER_TRACK:    db 0 ; 每磁道扇区数
189 
190 times 507 - ($ - $$) db 0 ; 填充剩余容量为0
191 
192 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
193 ; 将loader文件的大小和kernel文件的大小都
194 ; 放到boot文件oxaa55前的末尾
195 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
196 
197 ; loader文件所占用的扇区数,loader不会大过
198 2^8 * 512 = 128KB大小,这在短期内应该足够了
199 LOADER_LEN            db 0
200 ; kernel文件所占用的扇区数,kernel不会大过
201 2^16 * 512 = 32MB大小,这在用1.44MB大小的软盘里足够了
202 KERNEL_LEN            dw 0
203 
204 dw 0xaa55

      该文件是由BIOS自动从软盘的第一个扇区加载进内存的0x0:0x7c00处的。因此必须将boot放到软盘的第一个扇区,且要求该文件编译出来必须是512字节,正好占用一个扇区大小。
      boot中指定了loader+kernel被加载的位置:0x8000:0x0,也就是物理地址为0x80000的位置。另外将来loader还要在地址0xf000:0x0处放置一些系统参数,因此loader+kernel文件应该位于0x8000:0x0~0xf000:0x0之间,也就是不大于480KB的大小,超过这个大小系统崩溃,无法启动。
      该文件的核心操作就是从软盘的第二个扇区开始将loader+kernel加载进内存,不过前提是应该已经使用proc_kernel和buildImage程序将boot+loader和kernel压缩成一个System.Image文件,也就是说loader和kernel被一起放到软盘的第二个扇区开始的位置,boot将连续读磁道将loader+kernel加载到从0x8000:0x0起始的内存位置。
      还有一个地方需要注意,boot文件可启动的标志除了大小需要是512B外,最后两个字节也必须是0xaa55。而且我们在紧挨着0xaa55的地方开辟了3个字节的空间用来记录loader的大小和kernel的大小,都是以扇区数计量。其中loader大小占1个字节,因此说loader不得大于2^8 * 512B = 128KB,紧挨着是kernel大小,占用2个字节。这两个值是为了加载loader+kernel做准备的,而且这两个值不是boot或者loader写入的,而是buildImage这个工具直接在获取了loader文件和kernel文件的大小后直接写入的。
      核心的部分是将loader+kernel加载进内存,这个工作具体思想如下:boot先从自己的最后几个字节位置获取loader和kernel占用的扇区数量,然后再利用BIOS中断获取软盘参数,最后加载对应数量的扇区,不过为了加快加载速度,这里学习了linux的做法,每次只要可以,就加载整个磁道。
posted on 2011-11-22 19:31 myjfm 阅读(725) 评论(0)  编辑 收藏 引用 所属分类: 操作系统

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理