用nasm语言重新实现linux-0.11 bootsect.s(博古以通今)

;这个程序是改写linux的bootsect.s。我用的nasm的语法格式。昨天刚学,今天学完。所以想马上运用一下。
;我想写一个操作系统,现在觉得最简单的方式莫过于先把前辈的实现的东西重新实现一遍。等到对这个
;问题有更深刻认识的时候,再重新思考,写出有自己特色的系统。
;作者:hk0625
;开始时间: 2010年03月18日星期四 21:00 
;完成时间: 2010年03月19日星期五 20:55(完成)
;最后修改时间: 2010年04月14日星期三 11:08
;地点:北京化工大学郁夫图书馆文法阅览室小圆桌&北化1#宿舍楼426
;(今天是北化图书馆的一个月一次的闭关日,下午没地方去。呵呵,还是在宿舍吧。)
;声明:下面的注释,很多来自赵炯的《linux-0.11完全注释》,我觉得他注释的不错。
;所以大量借用。在这里,说声谢谢!在前辈的基础上,可以做更多有意义的工作。
;下面let's try!

;把程序加载到内存0x07c0:0000处
org 0x7c00

;假设编译后system模块的大小是 0x30000
SYSSIZE equ 0X3000
global begtext, begdata, begbss, endtext, edndata, endbss
section .text
begtext:
;setup模块的大小,4个扇区
SETUPLEN equ 4
;引导条被装入的位置,0x07c0:0000,从这以下都是段地址
BOOTSEG equ 0x07C0
;将bootsec转移到得位置
INITSEG equ 0x9000
;setup将被载入的的内存地址
SETUPSEG equ 0x9020
;SYSTEM模块将被加载的起始地址
SYSSEG equ 0x1000
;SYSTEM模块加载上限
ENDSEG equ SYSSEG + SYSSIZE
;根文件系统设备号
; 0x000 根文件系统设备使用与引导时同样的软驱设备
; 0x301 根文件系统设备在第一个硬盘的第一个分区上

ROOT_DEV equ 0X301 ;修改,2010年04月14日星期三 11:08
;告知链接程序,程序从start标号开始执行   
start:
 mov ax, BOOTSEG
; mov ax, cs
 mov ds, ax
 mov ax, INITSEG
 mov es, ax
 mov cx, 256 ;256个字等于512字节,O(∩_∩)O~
 sub si, si ;源地址  ds:si = 0x07c0:0x0000
 sub di, di ;目的地址 es:di = 0x9000:0x0000
 rep
 movsw
 jmp INITSEG:go-0x7c00

go:
 mov ax, cs
 mov ds, ax
 mov es, ax
 mov ss, ax
 mov sp, 0xff00

 

load_setup:
;下面利用BIOS中断的INT 0X13将setup模块从软盘的第二个扇区
;开始读到0x90200开始处,一共读4个扇区。如果读错,则复位驱动器,
;并不断重试,直到成功。
 mov dx, 0x0000 ; dh = 0 磁头号(head), dl = 0 驱动器号(drive)
 mov cx, 0x0002 ; ch = 0 磁道号(track), cl = 02 扇区号(sector)
 mov bx, 0x0200 ; es:bx = 0x9000:0x0200
 mov ax, 0x0200 + SETUPLEN
 int 0x13
 jnc ok_load_setup
;读取失败,复位软驱。然后重试。
 mov dx, 0x0000
 mov ax, 0x0000
 int 0x13
 jmp load_setup

ok_load_setup:
;取软驱的参数,特别是每道的扇区数量。
;INT 13(直接磁盘服务 Direct Disk Service)
;功能08H
;功能描述:读取驱动器参数
;入口参数:AH=08H
;    DL=驱动器,00H~7FH:软盘;
;         80H~0FFH:硬盘
;出口参数:CF=1——操作失败,AH=状态代码,参见功能号01H中的说明,
;          否则,BL=01H — 360K
;     =02H — 1.2M
;           =03H — 720K
;           =04H — 1.44M
;   CH=柱面数的低8位
;   CL的位7-6=柱面数
;   CL的位5-0=扇区数
;   DH=磁头数
;   DL=驱动器数
;   ES:DI=磁盘驱动器参数表地址

 mov dh, 0x00
 mov ax, 0x0800
 int 0x13
 mov ch, 0x00
 mov [sectors - 0x7c00], cx ;将扇区数保存到sectors单元
 mov ax, INITSEG  ;调用这个BIOS功能会修改es的值
 mov es, ax  ;所以重新设置es的值是必要的。

;显示一些提示信息
 mov ah, 0x03 ;读光标位置
 xor bh, bh
 int 0x10
;如果有谁不了解这些bios功能调用,我想大家推荐几本书吧。
;王爽编著《汇编语言》,适合当做教材来学。
;殷肖川主编《汇编语言程序设计》附录D中断列表,十分详细。 
 mov cx, 24 ;字符串大小为24
 mov bx, 0x0007 ;第0页,属性为7
 mov bp, msg1 - 0x7c00 ;org 0x7c00 让所有的偏移量增加了0x7c00 
 mov ax, 0x1301
 int 0x10
 
;加载系统模块到0x10000
 mov ax, SYSSEG
 mov es, ax
 call read_it ;读磁盘上system模块,es作为输入参数
 call kill_motor ;关闭驱动器马达
 

 
;接下来确定使用的根文件系统设备(简称根设备)。如果已经指定了设备,就直接使用
;给定设备。否则这需要根据BIOS报告的每磁道德扇区数来确定到底使用/dev/SP0(2, 28)
;还是 /dev/at0(2, 8)。
 mov ax, [root_dev - 0x7c00] 
 cmp ax, 0x0000
 jne root_defined
 mov bx, [sectors - 0x7c00]
 
 mov ax, 0x0208
 cmp bx, 15
 je root_defined
 mov ax, 0x021c
 cmp bx, 18
 je root_defined
undef_root:
 jmp undef_root ;死机!
root_defined:
 mov [root_dev - 0x7c00], ax
;到此,所有程序加载完毕,我们跳转到被加载在bootsecxt后面setup程序去。
;不过为了编程方便,现在这里暂停。等到我们调试完这个程序的后,需要调试setup时
;再把这条语句注释起来。
 jmp  SETUPSEG:0
; jmp $ ;这条语句是用来调试用的
 
;下面是两个子程序
sread: dw 1 + SETUPLEN ;当前磁道中已读的扇区数, 其中1 (bootsec模块)
head: dw 0  ;当前磁头号
track: dw 0  ;当前磁道号

read_it:
;测试输入的段值。从磁盘读入的数据必须存放在位于内存地址64KB的边界处,否则进入
;死循环。 清bx寄存器,用于表示当前段内存放数据的开始位置。
 mov ax, es
 test ax, 0x0fff  ;不太理解?呵呵,现在理解了
die: jne die   ;如果es值不是64kb地址边界,则无限循环。如果不太
    ;理解,建议你应该test指令以及64kb的段地址表示
    ;这两个概念想清楚
 xor bx, bx  ;bx为段内偏移,在这里清零。
rp_read:
;判断是否已经读入全部数据。比较当前所读入段是否就是系统数据末端所处的段
;(#ENDSEG),如果不是就跳转至下面ok1_read标号处继续读数据。否则退出子程序返回。
 mov ax, es
 cmp ax, ENDSEG    ;判断数据是否加完?
 jb ok1_read
 ret
ok1_read:
;计算和验证当前磁道需要读取的扇区数,放在ax寄存器中。
;根据当前磁道还未读取的扇区数以及段内数据字节开始偏移位置,计算机如果全部读取
;这些未读扇区,所读总字节数是否会超过64kb段长度的限制。若会超过,则根据此次
;最多能读入的字节数(64kb - 段内偏移位置),反算出此次需要读入的扇区数。
 mov ax, [sectors - 0x7c00]   ;每磁道德扇区数,前面获得 
 sub ax, [sread - 0x7c00]     ;减去当前磁道已读扇区数
 mov cx, ax  ; cx = ax = 当前磁道未读扇区数。
 shl cx, 9  ; cx = cx * 512 字节。 2^9 = 512 O(∩_∩)O~
 add cx, bx  ; cx = cx + 段前偏移量值(bx) 
 
 
 jnc ok2_read ;段偏移量没有超过64kb
 je  ok2_read ;有点疑惑?;段偏移量刚好等于64kb
 xor ax, ax ; ax = 0 =64kb, 呵呵,能理解吧。
 sub ax, bx ; ax = 64kb - 段内偏移量
 shr ax, 9 ;可以读入的扇区数
ok2_read:
 call read_track
 mov cx, ax
 add ax, [sread - 0x7c00]
 cmp ax, [sectors - 0x7c00]
 jne ok3_read

 mov ax, 1
 sub ax, [head - 0x7c00]
 jne ok4_read
 inc  word [track - 0x7c00]
ok4_read:
 mov [head - 0x7c00], ax
 xor ax, ax
ok3_read:
 mov [sread - 0x7c00], ax
 shl cx, 9
 add bx, cx
 jnc rp_read

 mov ax, es
 add ax, 0x1000
 mov es, ax
 xor bx, bx
 jmp rp_read
;读当前磁道上指定开始扇区和需要读取扇区数的数据到es:bx开始处。
read_track:
 pusha
 pusha
 push ax
 push bx
 mov ax, 0xe2e ;loading ... message 2e=.
 mov bx, 7
 int 0x10
 pop bx
 pop ax
 popa


 push ax
 push bx
 push cx
 push dx
 mov dx, [track - 0x7c00] ;获取当前磁道号
 mov cx, [sread - 0x7c00] ;获取已读扇区号
 inc cx    ;扇区号加 1
 mov ch, dl   ;将磁道号送往ch,至此ch保存磁道号,而cl保存扇区号
 mov dx, [head - 0x7c00]  ;这两句的作用将磁头号(即面号)送往dh
 mov dh, dl
 mov dl, 0 ;原来是mov dh, 0 。错了,呵呵
   ;这个错在后面在加载system文件时出错。将驱动器号设置为0
 and dx, 0x0100
 mov ah, 2
 int 0x13
 jc bad_rt
 pop dx
 pop cx
 pop bx
 pop ax
 popa
 ret

;执行驱动器复位操作(磁盘中断功能号0), 在跳转到read_track处重试。
bad_rt: mov ax, 0
 mov dx, 0
 int 0x13
 pop dx
 pop cx
 pop bx
 pop ax
 popa
 jmp read_track
;这个子程序用于关闭软驱的马达,这样我们进入内核后它处于已知状态,
;以后也就无须担心它了。
kill_motor:
 push dx
 mov dx, 0x3f2 ;软驱控制卡的驱动端口,只写。
 mov al, 0 ;A软驱动器,关闭FDC,禁止DMA和中断请求,关闭马达。
 outb  ;将al中的内容输出到dx指定的端口去。
 pop dx
 ret

sectors: dw 0  ;存放当前驱动软盘的扇区数

msg1: db 13, 10      ;回车, 换行
 db 'Loading system ...';一共有二十四个ascii
 db 13, 10, 13, 10      ; 2 + 18 + 4 = 24

times 508  -  ($ - $$) db 0

root_dev dw ROOT_DEV ;这里存放根文件系统所在的设备号
 dw 0xaa55
endtext:
enddata:
section .bss
begbss:
endbss:

;

;注:这是我重写整个linux-0.11代码。现在代码已经有1万多行了吧。我会慢慢公布。

;由于考虑重写,为了便于与原来的代码比较。所以我没有对这个代码结构进行精简。

你可能感兴趣的:(用nasm语言重新实现linux-0.11 bootsect.s(博古以通今))