操作系统入门(三)痛并学习中

类别:编程语言 点击:0 评论:0 推荐:

之前的boot启动镜像文件在需要改变后继文件(kernel.img)时要用partcopy,并且要计算文件的长度,使用起来不方便,现在我们可以引入微软的软盘FAT格式,如果我们使用软盘来作为测试的介质,那么在使用起来会很方便。

;; 文件:boot.asm
;; 作用:从7c00h处启动,显示载入系统信息"Loading System..."
;; 没有文件系统,1.44M 512bits/80sec 软盘启动,
;; 创建日期:2004/01/30 flyback
;; [email protected]
;;      ===================================


 %define loadpoint 9000h    ; 载入点,初始化程序载入到9000h的地方
 %define loadoffset 0000h

 bits 16
 ORG 0x0 ; 启动入口地址
main:
 jmp short start  ; 跳转到开始程序入口
 nop   ;

; 引导区文件系统数据
;----------------------------------------------------------------------------
 brOEM  DB ' -My-0S.' ; 0003h - 引导程序的名字
 brBPS  DW 0x200  ; 000Bh - 每扇区的字节数 512
 brSPC  DB 0x01   ; 000Dh - 每簇扇区数
 brResCount DW 0x0001  ; 000Eh - 保留扇区数
 brFATs  DB 0x02   ; 0010h - FAT 备份数
 brRootEntries DW 0x00e0  ; 0011h - 根目录入口数
 brSectorCount 
    DW 2880  ; 0013h - 磁盘容量扇区数< 32MB
 brMedia  DB 240   ; 0015h - 媒体描述符
 brSPF  DW 9   ; 0016h - 每FAT扇区数
 brSPH  DW 18   ; 0018h - 每磁道扇区数
 brHPC  DW 2   ; 001Ah - 盘面数
 brHidden  DD 0   ; 001Ch - 隐藏扇区数
 brSectors DD 0   ; 0020h - 如果大于32m的扇区总数
    DB 0   ; 0024h - 物理驱动器号
    DB 0   ; 0025h - 系统保留
    DB 29H   ; 0026h - 扩展扇区标记(包含29h)
 brSerialNum     DD 00000006H ; 0027h - 卷ID
 brLabel  DB 'teachosdisk' ; 002Bh - 卷标
 brFSID  DB 'FAT12   '  ; 0036h - 系统保留
;------------------------------------------------------------------------

start:
 cli   ; 关中断,防止意外中断打断程序执行
 mov ax, 0x7c0 ;
 mov ds, ax ; 设置数据段
 mov es, ax ;

 mov ax, 0x0000; 设置堆栈段
 mov ss, ax
 mov sp, 0ffffh ; 堆栈入口

 sti   ; 开中断

 mov si, brOEM  ; system info
 call pntchr
 
 mov si, CRLF
 call pntchr
  
 mov si, loadmsg ; 调用显示载入信息
 call pntchr

 ; 把磁盘目录信息载入到7c00:0200的地方
loadroot:
 mov  cx, 0
 mov  dx, 0 
 mov  ax, 0x0020   ; 32个字节/文件
 mul  WORD [brRootEntries] ; 32*224(文件数) = 7168 (最多所有文件所占的字节数)
 div  WORD [brBPS]   ; 7168/512 = 14 (Root dir) 文件的目录描述字节占用的扇区数14
 xchg  ax, cx    ; CX = 14
 mov  al, [brFATs]   ; 2
 mul  word [brSPF]   ; 9 * 2 = 18
 add  ax, word [brResCount] ; 18 + 1 now AX = 19
 mov  WORD[datasector], ax  ; 目录起始的扇区 19
 add  WORD[datasector], cx  ; 数据区起始扇区 33

 ; 目录放入0x0200内存
 mov bx, 0x0200
 call ReadSec

 ; 比较目录中是否有init.img文件存在
 mov     cx, WORD [brRootEntries] ; CX:224 在目录的所有文件中寻找
 mov     di, 0x0200   ; 从目录入口处开始 0x200
.LOOP:
 push    cx      ; 保护 CX = 224
 mov     cx, kernellen   ; 8个文件名称和3个扩展名称
 mov     si, kernelname   ;
 push    di     
 repe  cmpsb     ; stop compare if cx >0 or the
        ; first unequal is met(zf=1)!
 pop     di
 je    loadfiledec    ;if zf = 1, jmp LOAD_FAT!(Seek OK!)
 pop     cx
 add     di, 0x0020    ; 跳到下一个目录入口
 loop    .LOOP     ; cx 自动减
 jmp     failure

 ; 载入文件描述子节
loadfiledec:
 ; 把启动的镜像文件*.img文件的起始单元保存起来
 mov     si, CRLF
 call    pntchr
 mov     si, loadfat
 call    pntchr
 mov     si, CRLF
 call    pntchr
 mov     dx, WORD [di + 0x001A]    ;the file's first cluster
 mov     WORD [cluster], dx                  ; store the first cluster
 
 ; 计算fat的大小并保存到07c00:0x0200(ds:bx)
 xor     ax, ax
 mov     al, BYTE [brFATs]                ; al:2
 mul     WORD [brSPF]                ; AH:18 = 9*2
 mov     cx, ax        ; CX:18

 ; 读取数据放入到0x7c00:0x0200 ds:bx
 mov     ax, WORD [brResCount]          ; AX:1
 mov     bx, 0x0200                          ;
 call    ReadSec       ; AX:1  CX:18  BX:0200

 ; 读取img 放入 9000:0x0000处
 mov     ax, loadpoint                          ; destination of image CS
 mov     es, ax
 mov     bx, loadoffset                          ; destination for image IP
 push    bx

LOAD_IMAGE:
 mov     ax, WORD [cluster] ; cluster to read
 pop     bx     ; buffer to read into
 call    ClusterLBA   ; convert cluster to LBA
       ; AX:[cluster]  ES:BX[9000:0000](dest)
 xor     cx, cx
 mov     cl, BYTE [brSPC]  ; CL:1
 call    ReadSec    ; AX:LBA  CX:1 BX:0000 ES:0100
 push    bx
 
 ; 计算下一个单元
 mov     ax, WORD [cluster] ; identify current cluster
 mov     cx, ax    ; copy current cluster
 mov     dx, ax    ; copy current cluster
 shr     dx, 0x0001   ; divide by two

 add     cx, dx    ; sum for (3/2)
 mov     bx, 0x200  ; location of FAT in memory
 add     bx, cx                            ; index into FAT
 mov     dx, WORD [bx]             ; read two bytes from FAT
 test    ax, 0x0001
 jnz     .ODD_CLUSTER

.EVEN_CLUSTER:
 and     dx, 0000111111111111b               ; take low twelve bits
 jmp     .DONE

.ODD_CLUSTER:
 shr     dx, 0x0004                          ; take high twelve bits

.DONE:
 mov     WORD [cluster], dx                  ; store new cluster
 cmp     dx, 0x0FF0                          ; test for end of file,>=0x0FF0: end or bad!
 jb      LOAD_IMAGE       ; if dx<0x0ff0 (CF=1), jmp Load_image
    DONE:          ; this Label is not used!
 ; push    WORD loadpoint       ;--->pop CS (second)
 ; push    WORD loadoffset       ;--->pop IP (first)
 ; retf
 mov  si, CRLF
 call  pntchr
 jmp word loadpoint:loadoffset
 
failure:
 mov si, CRLF
 call pntchr
 mov si, loadfail
 call pntchr
 hlt
 jmp $
 

;-------------------- 数据区-------------------------------

loadmsg  db 'Loading System:',0  ; 要显示的字符窜以0结尾
loadfail  db 'Load init Failure!',0  ; 载入失败信息
Sector  db 0x00
Head  db 0x00
Track  db 0x00
datasector dw 0x0000     ; 33 数据区起始扇区号
rootaccess equ 0x0200     ; 存放目录数据的偏移地址
cluster  dw 0x0000
kernelname db "KERNEL  IMG"    ; 11 chars
kernellen  equ $ - kernelname    ; 长度
CRLF   db 13,10,0
Progress  db ".", 0
; msgFailure db "ERROR!", 0x00
; loaddir  db 'Load Dir:', 0 ;字符串,回车,换行,0
loadfat  db 'Load FAT:', 0 ;字符串,回车,换行,0
;readsec  db 'read sec', 13,10,0

; ----------------子程序区-------------------------

;*********************显示字符串**************

pntchr: 
 pusha
.pnt:
 lodsb    ; 从DS:SI装载一个字符到AL
 or al,al    ;
 jz endpntchr   ; 如果 al = 0, 返回 
      ;
 mov ah,0x0E   ;
 mov bx,0x004a  ; 
 int 0x10    ; 调用bios中断显示字符
 jmp .pnt   ;
      ;
endpntchr:    ;
 popa
 ret     ; 返回


;*************************************************************************
; PROCEDURE ReadSec
; 从ax+1的地方把cx个扇区载入到es:bx的内存
; 注意扇区是从2开始,1扇区已经读入了
;*************************************************************************
ReadSec:
.Main:
 ;mov si, readsec
 ;call pntchr
 ;mov di, 0x0005
.secloop:
 push    ax
 push    bx
 push    cx         ;protect ax, bx, cx

 call    LBACHS   ; 调用转换

 mov     ah, 0x02  ; BIOS 读取扇区命令
 mov     al, 0x01  ; 一个扇区
 mov     ch, BYTE [Track] ; track
 mov     cl, BYTE [Sector] ; sector
 mov     dh, BYTE [Head] ; head
 mov     dl, 0   ; 因为是a:所以为0
 int     0x13   ; 调用中断

 jnc     .SUCCESS                            ; test for read error
 
 xor ax, ax
 ;int 0x13
 ;dec di

 pop     cx
 pop     bx
 pop     ax
 jnz .secloop
 jmp failure   ; 错误则显示出错信息并停止
 hlt
 
.SUCCESS
 mov    si, Progress
 call      pntchr
 pop     cx
 pop     bx
 pop     ax

 add bx, WORD [brBPS] ; 读取下一个扇区的内容
 inc ax     
 loop .Main   ; cx -= 1, if cx != 0, jmp .main
 ret

;*************************************************************************
; PROCEDURE LBACHS
; 转换逻辑块访问为读取磁盘所使用的磁道,盘面,扇区
; 相对扇区 = (逻辑扇区 / 每磁道扇区数) + 1
; 相对盘面   = (逻辑扇区 / 每磁道扇区数) MOD 盘面数
; 相对磁道  = 逻辑扇区 / (每磁道扇区数 * 盘面数)
;*************************************************************************
LBACHS:
 xor     dx, dx    ; dx = 0
 div     WORD [brSPH]  ; div m16: ax/18 -> 商:ax 余数:dx
 inc     dl     ;
 mov     BYTE [Sector], dl  ;sector No relative to the Track
 xor     dx, dx    ; dx = 0
 div     WORD [brHPC]  ; ax/2 -> 商:ax 余数:dx
 mov     BYTE [Head], dl  ;
 mov     BYTE [Track], al  ;
 ret

;*************************************************************************
; PROCEDURE ClusterLBA
; 转换单元访问到直接扇区访问
; LBA = (cluster - 2) * sectors per cluster
;*************************************************************************
ClusterLBA:
sub     ax, 0x0002                          ; zero base cluster number
xor     cx, cx
mov     cl, BYTE [brSPC]        ; convert byte to word
mul     cx
add     ax, WORD [datasector]               ; base data sector
ret

; -----------------------------------------------------------------
times 510 - ($ - $$) db 0   ; 保证boot区有512个字节
 dw 0AA55h    ; boot区标记

上面是改进后的boot.asm编译后用partcopy 放入软盘开始的512字节

这时,我们只要把我们的其它程序编译成独立的镜像文件命名为KERNEL.IMG拷贝到软盘(软盘文件)就可以了

;-------------------------------------------------
;; 文件:显示初始化信息
;; KERNEL.ASM
;; 作用:显示初始化信息
;; [email protected]
;;-------------------------------------------------
start:
 mov ax, cs
 mov ds, ax
 mov es, ax

 lea si, [prompt] ; press a key to clear screen 
 call pntchr

 ; 等待按下键盘
 ;mov ah, 0x10
 ;int 0x16

 ; cls
 mov ah, 0x06
 mov al, 0x00
 mov bh,0x07
 mov cx, 0x0
 mov dx,0x184f
 int 0x10

 lea si, [start2msg]
 call pntchr
 ; 显示图形功能
 ; 保存原来的显示方式
 mov ah, 0x0f
 int 0x10
 mov [gmode], ax

 ; 设置图形模式640X480
 mov ah,0x0
 mov al, 0x12
 int 0x10

 ; 设置调色板
 mov ah, 0x0b
 mov bh, 0x0
 mov bl, 0x11
 int 0x10

 ; 显示字符串
 lea bp, [start2msg] ; es:bp
 mov ah, 0x13
 mov al, 0x1
 mov bh, 0
 mov bl, 0x71
 mov dx, 0x00
 mov cx, msgsize
 int 0x10

 ; 划线
 mov bx, 0x0
 mov cx, 64
 mov dx,70
.pix:
 mov ah,0x0c
 mov al, bl
 int 0x10
 inc cx
 cmp cx,576
 jne .pix
 mov cx,64
 inc bl
 inc dx
 cmp dx, 280
 jne .pix

 ; 等待键盘
 mov ah, 0x10
 int 0x16

 ; 恢复原来的图形
 mov ax, [gmode]
 mov ah, 0x0
 int 0x10

 mov si, retmode
 call pntchr

 jmp $
 nop

;----------------显示字符串----------------------
pntchr: 
 lodsb    ; 从DS:SI装载一个字符到AL
 or al,al    ;
 jz endpntchr   ; 如果 al = 0, 返回 
      ;
 mov ah,0x0E   ;
 mov bx,0x0007  ; 
 int 0x10    ; 调用bios中断显示字符
 jmp pntchr   ;
      ;
endpntchr:    ;
 ret     ; 返回

start2msg db 'Display 640X480 Video mode:',13,10,0
msgsize equ ($-start2msg)
prompt db 'Press any key...',13,10,0
prosize equ ($ - prompt)
retmode db 'Return to Text Mode.', 13,10,0
gmode dw 0

把上面文件编译成KERNEL.IMG(大写)拷贝到做好的软盘里,然后用它启动。

本文地址:http://com.8s8s.com/it/it21734.htm