WinixJ---boot/boot.s文件详解

WinixJ---boot/boot.s文件详解
本篇主要将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  13 10 " Loading loader and kernel  " 13 10
176  MSG1_LEN            equ $  -  MSG1
177  MSG2:                db  13 10 " The loader or kernel is too long  " 13 10
178  MSG2_LEN            equ $  -  MSG2
179  MSG3:                db  13 10 " Entering loader  " 13 10
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的做法,每次只要可以,就加载整个磁道。

你可能感兴趣的:(WinixJ---boot/boot.s文件详解)