Toy OS:Part 2: Read App From Hard Disk

从硬盘中读取用户程序到内存中

  1. 加载器和用户程序头之间的约定 或者说 协议 前面一章已经说过
    在这里插入图片描述

  2. 外围设备以及其接口,总线,ICH的作用以及关系
    在这里插入图片描述

  3. I/O端口以及端口访问,硬盘的端口特性
    在这里插入图片描述

  4. 内存空间当前的分布
    在这里插入图片描述

  5. 过程调用

代码实现以及详细注释

;加载器的两个难点:
;难点1 决定加载用户程序到内存中哪个地址上,即需要找到一个合适的空闲内存
;难点2 找到用户程序在硬盘中的位置,起始扇区号和用户程序的大小

;内存空间分部:
;A0000 ~ FFFFF  ROM BIOS
;10000 ~ 9FFFF  AVALIBLE SPACE
;00000 ~ 0FFFF  LOADER AND STACK OF IT   主引导扇区的代码可以放在这里 从0x7c00开始

app_lba_start equ 100                    ;声明 用户程序起始逻辑扇区号

SECTION mbr align=16 vstart=0x7c00       ;引导代码 段内所有元素的汇编地址都从0x7c00开始

    ;设置堆栈段和栈指针  SS=0 SP=0  指向0x7c00开始处
    mov ax,0
    mov ss,ax
    mov sp,ax

    ;计算出用户程序被加载物理地址的段地址  
    mov ax,[cs:phy_base]                 ;被加载的物理地址保存在标号phy_base处的一个双字单元中 所以需要两个16bit的寄存器存储
    mov dx,[cs:phy_base+0x02]            ;注意 是小端模式存储  由于拿到的是物理地址 要转化为段地址才能将数据段指针指向那里 要左移4bit
    mov bx,16                            ;div bx可见是16bit除法  被除数(dx|ax) / 除数(bx) =(ax) ---余数(dx) 
    div bx                               ;这就转化为段地址了
    mov ds,ax                            ;存入ds  数据段指向这个段地址
    mov es,ax                            ;存入es  附加段指向这个段地址

    ;开始读取程序的起始部分
    xor di,di                            ;di清零
    mov si,app_lba_start                 ;程序在硬盘上的起始逻辑扇区号: DI:SI  0:100
    xor bx,bx                            ;开始读取硬盘上的用户程序内容  加载到DS:0x0000处 采用基址寻址 使用bx寄存器提供偏移 bx先设置为0
    call read_hard_disk_0

read_hard_disk_0:                        ;从硬盘读取一个逻辑扇区
                                         ;输入: DI:SI=起始逻辑扇区号        SRC     访问硬盘需要提供扇区起始地址和要访问扇区数目  LBA28编址方式 每个扇区号用28bit数值表示 最多可表示2^28个扇区
                                         ;      DS:BX=目标缓冲区的地址      DST
    push ax                              ;保护现场 将即将会被破坏的寄存器保存到栈中
    push bx
    push cx 
    push dx
                                         ;主磁盘有8个端口0x1f0到0x1f7   分配:f3到f5端口寄存器存放23位的起始扇区号 f6的8bits用于放24~27扇区号以及设置模式和选择盘 f7控制读写
    mov dx,0x1f2
    mov al,1
    out dx,al                            ;要读取的扇区数为1 写入到0x1f2端口寄存器中

    inc dx                               ;指向下一个端口    0x1f3
    mov ax,si                            ;起始逻辑扇区预先保存在di:si里面的  使用多个端口寄存器存放  低地址端口寄存器从si中取  高地址的从di中取
    out dx,al                            ;0x1f3端口寄存器放7~0位 si的低8bit

    inc dx                               ;指向下一个端口    0x1f4
    mov al,ah                            
    out dx,al                            ;0x1f4端口寄存器放15~8 si的高8bit

    inc dx                               ;0x1f5
    mov ax,di
    out dx,al                            ;0x1f5端口寄存器放23~16 di的低8bits

    inc dx                               ;0x1f6  还剩27~24的4bit没有放 要写到0x1f6的低4位中
    mov al,0xe0                          ;设置LBA28模式 并选择主盘 di的高8bits
    or al,ah                             ;di的高8bits在ah中 如果有值 则放到al里 和0xe0一起
    out dx,al                            ;一起写到0x1f6

    inc dx                               ;0x1f7 既是命令端口又是状态端口
    mov al,0x20                          ;读命令
    out dx,al

;等待硬盘Ready
.waits:
    in al,dx                             ;读出0x1f7里的数据-->al
    and al,0x88                          ;0x88=0b10001000 al=al&0x88  保留第7bit和第3bit
    cmp al,0x08                          ;0x08=0b00001000 cmp比较 判断0x1f7的第3bit是否为1  3bit表示硬盘是否已准备好和主机交换数据
    jnz .waits                           ;不忙,且硬盘已准备好数据传输

    mov cx,256                           ;总共要读取的字数
    mov dx,0x1f0                         ;下面还是读取扇区里的内容
;循环读取 从0x1f7端口中读到DS:BX中 BX在30行被清0  BX每次循环+2 跳一个word
.readw:
    in ax,dx
    mov [bx],ax                          ;DS:BX
    add bx,2
    loop .readw

    pop dx                               ;恢复现场
    pop cx
    pop bx
    pop ax

    ret

    phy_base dd 0x10000                  ;用户程序被加载的物理地址 不是一定要在这里

times 510-($-$$) db 0
                 db 0x55,0xaa

整个过程的解析

在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值