【第四章复习】突破512字节,实现读扇区函数

来源:互联网 发布:eml文件怎么打开 mac 编辑:程序博客网 时间:2024/06/11 19:22

     经历了保护模式的复习后,我们再来继续我们的操作系统。我们之前只做了一个极其简陋的引导程序,现在来扩展它。

     引导扇区太小,只有512字节,很可能不够用,那么我们把只要的功能交给LOADER来做,引导扇区BOOT的工作就是把LOADER加载进内存,并把控制权交给LOADER。

     那么LOADER怎么放呢?在这里不妨把软盘做成FAT12格式,这样对于操作会方便得多。FAT12是一种使用普遍的文件系统,关于文件系统已经在操作系统教科书上有详述,这里不多说了。

     要想软盘被DOS或者LINUX识别,则在软盘的引导扇区中写入一些数据才行,如下列代码:

Code:
  1. jmp short LABEL_START       ; Start to boot.   
  2.     nop             ; 这个 nop 不可少   
  3.   
  4.     ; 下面是 FAT12 磁盘的头   
  5.     BS_OEMName          db 'MarcusX '       ; OEM String, 必须 8 个字节   
  6.     BPB_BytsPerSec      dw 512              ; 每扇区字节数   
  7.     BPB_SecPerClus      db 1                ; 每簇多少扇区   
  8.     BPB_RsvdSecCnt      dw 1                ; Boot 记录占用多少扇区        
  9.     BPB_NumFATs         db 2                ; 共有多少 FAT 表   
  10.     BPB_RootEntCnt      dw 224              ; 根目录文件数最大值   
  11.     BPB_TotSec16        dw 2880             ; 逻辑扇区总数   
  12.     BPB_Media           db 0xF0             ; 媒体描述符   
  13.     BPB_FATSz16         dw 9                ; 每FAT扇区数   
  14.     BPB_SecPerTrk       dw 18               ; 每磁道扇区数   
  15.     BPB_NumHeads        dw 2                ; 磁头数(面数)   
  16.     BPB_HiddSec         dd 0                ; 隐藏扇区数   
  17.     BPB_TotSec32        dd 0                ; wTotalSectorCount为0时这个值记录扇区数   
  18.     BS_DrvNum           db 0                ; 中断 13 的驱动器号   
  19.     BS_Reserved1        db 0                ; 未使用   
  20.     BS_BootSig          db 29h              ; 扩展引导标记 (29h)   
  21.     BS_VolID            dd 0                ; 卷序列号   
  22.     BS_VolLab           dd 'MarcusOs0.2'    ; 卷标, 必须 11 个字节   
  23.     BS_FileSysType      db 'FAT12   '       ; 文件系统类型, 必须 8个字节        
  24.   
  25. LABEL_START:  

     操作系统就是根据这些信息来进行管理的。这样,我们把编译好的LOADER.BIN文件放到软盘上,引导的时候需要找到这个文件并加载,那么找文件就必须要读软盘上的目录区和FAT,那么现在就来实现这个功能,我们用到BIOS的13h号中断:

     入口参数:ah=02h,al=要读的扇区数,ch=柱面(磁道)号,cl=起始扇区号,dh=磁头号,dl=驱动器号(0表示A盘),es:bx缓冲区地址

     对于1.44M的软盘来说,一共有2880个扇区,所以0到2879是扇区的逻辑号,但是调用这个中断要把逻辑扇区号转换成磁头号(0或1),柱面号(0到79)和相对柱面的扇区号(1到18),公式如下:

     相对扇区号/每磁道扇区号(18)的商Q,余数R,其中:

     柱面号=Q>>1,磁头号=Q&1,相对起始扇区号=R+1

     有了公式,那就很容易的翻译成代码了:

Code:
  1. Read_Sector:   
  2. ;C function proto(REAL MODE,SHORT CALL):   
  3. ;void Read_Sector(byte16 first_sector_index,byte16 read_sector_number);   
  4. ;attention:   
  5. ;before call this function must let es:bx point to the buffer   
  6.     push    bp   
  7.     mov bp,sp   
  8.     push    ax   
  9.     push    bx   
  10.     push    cx   
  11.     push    dx   
  12.        
  13.     push    bx                      ; 暂存缓冲区偏移   
  14.     mov ax,[bp + 6]                 ; 取得要读取的扇区数   
  15.     push    ax                      ; 暂时保存之   
  16.     mov ax,[bp + 4]                 ; 取得要读的逻辑扇区号   
  17.     mov bl,[BPB_SecPerTrk]     
  18.     div bl                          ; 除以每柱面扇区数   
  19.     mov ch,al                                  
  20.     shr ch,1                        ; ch<-柱面号   
  21.     mov dh,al   
  22.     and dh,1                        ; dh<-磁头号   
  23.     mov cl,ah   
  24.     inc cl                          ; cl<-相对扇区号   
  25.     mov dl,0                        ; 读A盘   
  26.     pop ax                          ; al<-要读的扇区数   
  27.     pop bx                          ; 恢复缓冲区偏移   
  28.        
  29. .Go_On_Reading:   
  30.     mov ah,2                        ; ah<-功能号   
  31.     int 13h   
  32.     jc  .Go_On_Reading              ; 如果cf=1,继续读   
  33.        
  34.     pop dx   
  35.     pop cx   
  36.     pop bx   
  37.     pop ax   
  38.        
  39.     pop bp   
  40.     ret  

     为了验证这个函数的正确性,下面来测试一下,准备一个空白的软盘映像,在第1个扇区的第267个字节写入A的ASCII码,也就是逻辑地址30ah,如图所示:

     下面是完整代码,把第3个扇区读进1个512字节的缓冲,然后把A显示出来,另外还加了预处理宏,方便在DOS下调试或直接引导:

Code:
  1. %define _BOOT_DEBUG_    ; 做 Boot Sector 时一定将此行注释掉!将此行打开后用 nasm Boot.asm -o Boot.com 做成一个.COM文件易于调试   
  2.   
  3. %ifdef  _BOOT_DEBUG_   
  4.     org  0100h                  ; 调试状态, 做成 .COM 文件, 可调试   
  5. %else  
  6.     org  07c00h                 ; Boot 状态, Bios 将把 Boot Sector 加载到 0:7C00 处并开始执行   
  7. %endif   
  8.   
  9.     jmp short LABEL_START       ; Start to boot.   
  10.     nop                                         ; 这个 nop 不可少   
  11.   
  12.     ; 下面是 FAT12 磁盘的头   
  13.     BS_OEMName          db 'MarcusX '       ; OEM String, 必须 8 个字节   
  14.     BPB_BytsPerSec      dw 512              ; 每扇区字节数        
  15.     BPB_SecPerClus      db 1                ; 每簇多少扇区   
  16.     BPB_RsvdSecCnt      dw 1                ; Boot 记录占用多少扇区   
  17.     BPB_NumFATs         db 2                ; 共有多少 FAT 表   
  18.     BPB_RootEntCnt      dw 224              ; 根目录文件数最大值   
  19.     BPB_TotSec16        dw 2880             ; 逻辑扇区总数   
  20.     BPB_Media           db 0xF0             ; 媒体描述符   
  21.     BPB_FATSz16         dw 9                ; 每FAT扇区数   
  22.     BPB_SecPerTrk       dw 18               ; 每磁道扇区数   
  23.     BPB_NumHeads        dw 2                ; 磁头数(面数)   
  24.     BPB_HiddSec         dd 0                ; 隐藏扇区数   
  25.     BPB_TotSec32        dd 0                ; wTotalSectorCount为0时这个值记录扇区数   
  26.     BS_DrvNum           db 0                ; 中断 13 的驱动器号   
  27.     BS_Reserved1        db 0                ; 未使用   
  28.     BS_BootSig          db 29h              ; 扩展引导标记 (29h)   
  29.     BS_VolID            dd 0                ; 卷序列号   
  30.     BS_VolLab           dd 'MarcusOs0.2'    ; 卷标, 必须 11 个字节   
  31.     BS_FileSysType      db 'FAT12   '       ; 文件系统类型, 必须 8个字节     
  32.   
  33. LABEL_START:   
  34.     mov ax,cs   
  35.     mov ds,ax   
  36.     mov es,ax   
  37.     mov ss,ax   
  38.     mov sp,7c00h   
  39.        
  40.     mov bx,LABEL_BUFFER   
  41.     push    1   
  42.     push    1   
  43.     call    Read_Sector   
  44.     add sp,4   
  45.        
  46.     mov ax,0b800h   
  47.     mov gs,ax   
  48.     mov ah,0ch   
  49.     mov al,[LABEL_BUFFER + 266]   
  50.     mov [gs:140],ax   
  51.        
  52.     mov ax,4c00h   
  53.     int 21h   
  54.   
  55. LABEL_BUFFER:   
  56.     times 512   db  0   
  57.   
  58. Read_Sector:   
  59. ;C function proto(REAL MODE,SHORT CALL):   
  60. ;void Read_Sector(byte16 first_sector_index,byte16 read_sector_number);   
  61. ;attention:   
  62. ;before call this function must let es:bx point to the buffer   
  63.     push    bp   
  64.     mov bp,sp   
  65.     push    ax   
  66.     push    bx   
  67.     push    cx   
  68.     push    dx   
  69.        
  70.     push    bx                              ; 暂存缓冲区偏移   
  71.     mov ax,[bp + 6]                         ; 取得要读取的扇区数   
  72.     push    ax                              ; 暂时保存之   
  73.     mov ax,[bp + 4]                         ; 取得要读的逻辑扇区号       
  74.     mov bl,[BPB_SecPerTrk]     
  75.     div bl                                  ; 除以每柱面扇区数   
  76.     mov ch,al                                  
  77.     shr ch,1                                ; ch<-柱面号   
  78.     mov dh,al   
  79.     and dh,1                                ; dh<-磁头号   
  80.     mov cl,ah   
  81.     inc cl                                  ; cl<-相对扇区号   
  82.     mov dl,0                                ; 读A盘   
  83.     pop ax                                  ; al<-要读的扇区数   
  84.     pop bx                                  ; 恢复缓冲区偏移   
  85.        
  86. .Go_On_Reading:   
  87.     mov ah,2                                ; ah<-功能号   
  88.     int 13h   
  89.     jc  .Go_On_Reading                      ; 如果cf=1,继续读     
  90.        
  91.     pop dx   
  92.     pop cx   
  93.     pop bx   
  94.     pop ax   
  95.        
  96.     pop bp   
  97.     ret  

     运行结果如图:

原创粉丝点击