硬盘和显卡的访问与控制(二)——《x86汇编语言:从实模式到保护模式》读书笔记02

上一篇博文我们讲了如何看到实验结果,这篇博文我们着重分析源代码。
书中作者为了说明原理,约定了一种比较简单地用户程序头部格式,示意图如下(我参考原书图8-15绘制的,左边的数字表示偏移地址):

用户程序头部结构示意图


所以,如果用户程序要利用本章的源码c08_mbr.asm生成的加载器来加载的话,就应该遵循这种头部格式。

下面我们讲解源码c08_mbr.asm(粘贴的源代码不一定和配书的代码完全一样,因为有些地方我加了注释)

;代码清单8-1 ;文件名:c08_mbr.asm ;文件说明:硬盘主引导扇区代码(加载程序) ;创建日期:2011-5-5 18:17 app_lba_start equ 100 ;声明常数(用户程序起始逻辑扇区号) ;常数的声明不会占用汇编地址 SECTION mbr align=16 vstart=0x7c00 ;设置堆栈段和栈指针 mov ax,0 mov ss,ax mov sp,ax mov ax,[cs:phy_base] ;计算用于加载用户程序的逻辑段地址 mov dx,[cs:phy_base+0x02] mov bx,16 div bx mov ds,ax ;令DS和ES指向该段以进行操作 mov es,ax ;以下读取程序的起始部分 xor di,di mov si,app_lba_start ;程序在硬盘上的起始逻辑扇区号 xor bx,bx ;加载到DS:0x0000处 call read_hard_disk_0 ;以下判断整个程序有多大 mov dx,[2] ;曾经把dx写成了ds,花了二十分钟排错 mov ax,[0] mov bx,512 ;512字节每扇区 div bx cmp dx,0 jnz @1 ;未除尽,因此结果比实际扇区数少1 dec ax ;已经读了一个扇区,扇区总数减1 @1: cmp ax,0 ;考虑实际长度小于等于512个字节的情况 jz direct ;读取剩余的扇区 push ds ;以下要用到并改变DS寄存器 mov cx,ax ;循环次数(剩余扇区数) @2: mov ax,ds add ax,0x20 ;得到下一个以512字节为边界的段地址 mov ds,ax xor bx,bx ;每次读时,偏移地址始终为0x0000 inc si ;下一个逻辑扇区 call read_hard_disk_0 loop @2 ;循环读,直到读完整个功能程序 pop ds ;恢复数据段基址到用户程序头部段 ;计算入口点代码段基址 direct: mov dx,[0x08] mov ax,[0x06] call calc_segment_base mov [0x06],ax ;回填修正后的入口点代码段基址 ;开始处理段重定位表 mov cx,[0x0a] ;需要重定位的项目数量 mov bx,0x0c ;重定位表首地址 realloc: mov dx,[bx+0x02] ;32位地址的高16位 mov ax,[bx] call calc_segment_base mov [bx],ax ;回填段的基址 add bx,4 ;下一个重定位项(每项占4个字节) loop realloc jmp far [0x04] ;转移到用户程序 ;------------------------------------------------------------------------------- read_hard_disk_0: ;从硬盘读取一个逻辑扇区 ;输入:DI:SI=起始逻辑扇区号 ; DS:BX=目标缓冲区地址 push ax push bx push cx push dx mov dx,0x1f2 mov al,1 out dx,al ;读取的扇区数 inc dx ;0x1f3 mov ax,si out dx,al ;LBA地址7~0 inc dx ;0x1f4 mov al,ah out dx,al ;LBA地址15~8 inc dx ;0x1f5 mov ax,di out dx,al ;LBA地址23~16 inc dx ;0x1f6 mov al,0xe0 ;LBA28模式,主盘 or al,ah ;LBA地址27~24 out dx,al inc dx ;0x1f7 mov al,0x20 ;读命令 out dx,al .waits: in al,dx and al,0x88 cmp al,0x08 jnz .waits ;不忙,且硬盘已准备好数据传输 mov cx,256 ;总共要读取的字数 mov dx,0x1f0 .readw: in ax,dx mov [bx],ax add bx,2 loop .readw pop dx pop cx pop bx pop ax ret ;------------------------------------------------------------------------------- calc_segment_base: ;计算16位段地址 ;输入:DX:AX=32位物理地址 ;返回:AX=16位段基地址 push dx add ax,[cs:phy_base] adc dx,[cs:phy_base+0x02] shr ax,4 ror dx,4 and dx,0xf000 or ax,dx pop dx ret ;------------------------------------------------------------------------------- phy_base dd 0x10000 ;用户程序被加载的物理起始地址 times 510-($-$$) db 0 db 0x55,0xaa

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwsszg.html