本文是《x86汇编语言:从实模式到保护模式》(电子工业出版社)的读书实验笔记。

这篇文章我们先不分析代码,而是说一下在Bochs环境下如何看到实验结果。

  1. 需要的源码文件

    第一个文件是加载程序
         ;代码清单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 ;程序在硬盘上的起始逻辑扇区号 (扇区号为28位,存放在di:si)
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

需要说明的是:

书上假设是从硬盘启动,本文假设从软盘启动

第二个文件是用户程序

         ;代码清单8-2
;文件名:c08.asm
;文件说明:用户程序
;创建日期:2011-5-5 18:17 ;===============================================================================
SECTION header vstart=0 ;定义用户程序头部段
program_length dd program_end ;程序总长度[0x00] ;用户程序入口点
code_entry dw start ;偏移地址[0x04]
dd section.code_1.start ;段地址[0x06] realloc_tbl_len dw (header_end-code_1_segment)/4
;段重定位表项个数[0x0a] ;段重定位表
code_1_segment dd section.code_1.start ;[0x0c]
code_2_segment dd section.code_2.start ;[0x10]
data_1_segment dd section.data_1.start ;[0x14]
data_2_segment dd section.data_2.start ;[0x18]
stack_segment dd section.stack.start ;[0x1c] header_end: ;===============================================================================
SECTION code_1 align=16 vstart=0 ;定义代码段1(16字节对齐)
put_string: ;显示串(0结尾)。
;输入:DS:BX=串地址
mov cl,[bx]
or cl,cl ;cl=0 ?
jz .exit ;是的,返回主程序
call put_char
inc bx ;下一个字符
jmp put_string .exit:
ret ;-------------------------------------------------------------------------------
put_char: ;显示一个字符
;输入:cl=字符ascii
push ax
push bx
push cx
push dx
push ds
push es ;以下取当前光标位置
mov dx,0x3d4
mov al,0x0e
out dx,al
mov dx,0x3d5
in al,dx ;高8位
mov ah,al mov dx,0x3d4
mov al,0x0f
out dx,al
mov dx,0x3d5
in al,dx ;低8位
mov bx,ax ;BX=代表光标位置的16位数 cmp cl,0x0d ;回车符?
jnz .put_0a ;不是。看看是不是换行等字符
mov ax,bx ;此句略显多余,但去掉后还得改书,麻烦
mov bl,80
div bl
mul bl
mov bx,ax
jmp .set_cursor .put_0a:
cmp cl,0x0a ;换行符?
jnz .put_other ;不是,那就正常显示字符
add bx,80
jmp .roll_screen .put_other: ;正常显示字符
mov ax,0xb800
mov es,ax
shl bx,1
mov [es:bx],cl ;以下将光标位置推进一个字符
shr bx,1
add bx,1 .roll_screen:
cmp bx,2000 ;光标超出屏幕?滚屏
jl .set_cursor mov ax,0xb800
mov ds,ax
mov es,ax
cld
mov si,0xa0
mov di,0x00
mov cx,1920
rep movsw
mov bx,3840 ;清除屏幕最底一行
mov cx,80
.cls:
mov word[es:bx],0x0720 ; space
add bx,2
loop .cls mov bx,1920 .set_cursor:
mov dx,0x3d4
mov al,0x0e
out dx,al
mov dx,0x3d5
mov al,bh
out dx,al
mov dx,0x3d4
mov al,0x0f
out dx,al
mov dx,0x3d5
mov al,bl
out dx,al pop es
pop ds
pop dx
pop cx
pop bx
pop ax ret ;---------------------------------- 用户程序入口 --------------------------------------------
start:
;初始执行时,DS和ES指向用户程序头部段
mov ax,[stack_segment] ;设置到用户程序自己的堆栈
mov ss,ax
mov sp,stack_end mov ax,[data_1_segment] ;设置到用户程序自己的数据段
mov ds,ax mov bx,msg0
call put_string ;显示第一段信息 push word [es:code_2_segment]
mov ax,begin
push ax ;可以直接push begin,80386+ retf ;转移到代码段2执行 continue:
mov ax,[es:data_2_segment] ;段寄存器DS切换到数据段2
mov ds,ax mov bx,msg1
call put_string ;显示第二段信息 jmp $ ;===============================================================================
SECTION code_2 align=16 vstart=0 ;定义代码段2(16字节对齐) begin:
push word [es:code_1_segment]
mov ax,continue
push ax ;可以直接push continue,80386+ retf ;转移到代码段1接着执行 ;===============================================================================
SECTION data_1 align=16 vstart=0 msg0 db ' This is NASM - the famous Netwide Assembler. '
db 'Back at SourceForge and in intensive development! '
db 'Get the current versions from http://www.nasm.us/.'
db 0x0d,0x0a,0x0d,0x0a
db ' Example code for calculate 1+2+...+1000:',0x0d,0x0a,0x0d,0x0a
db ' xor dx,dx',0x0d,0x0a
db ' xor ax,ax',0x0d,0x0a
db ' xor cx,cx',0x0d,0x0a
db ' @@:',0x0d,0x0a
db ' inc cx',0x0d,0x0a
db ' add ax,cx',0x0d,0x0a
db ' adc dx,0',0x0d,0x0a
db ' inc cx',0x0d,0x0a
db ' cmp cx,1000',0x0d,0x0a
db ' jle @@',0x0d,0x0a
db ' ... ...(Some other codes)',0x0d,0x0a,0x0d,0x0a
db 0 ;===============================================================================
SECTION data_2 align=16 vstart=0 msg1 db ' The above contents is written by LeeChung. '
db '2011-05-06'
db 0 ;===============================================================================
SECTION stack align=16 vstart=0 resb 256 stack_end: ;===============================================================================
SECTION trail align=16 program_end:

2.利用源码生成.bin文件

nasm -f bin c08_mbr.asm -o c08mbr.bin

nasm -f bin c08.asm -o c08usr.bin

这时候会提示:

c08.asm:203: warning: uninitialized space declared in stack section: zeroing

“这句话的意思是,c08.asm源程序的第203行声明了未初始化的空间。”本实验的栈空间可以不初始化,所以不用管这个警告。

3.把c08mbr.bin写入启动软盘文件

dd if=c08mbr.bin of=a.img

4.制作一个空的硬盘

因为根据源码,我们知道用户程序在硬盘上,所以我们要制作一个硬盘镜像文件——利用工具bximage

在命令行输入 bximage,其他操作如图所示





注意到最下面的提示了吗?

“The following line should appear in your bochsrc:

ata0-master: type=disk, path=”c.img”, mode=flat, cylinders=2, heads=16, spt=63”

由于我们启动Bochs的时候没有用默认的配置文件,所以我们需要在自己的配置文件中添加这一行

ata0-master: type=disk, path="c.img", mode=flat, cylinders=2, heads=16, spt=63

需要注意的是,我们这张硬盘有2016个扇区(每个扇区大小是512字节)

5.把用户程序c08usr.bin写入硬盘镜像

dd if=c08usr.bin of=c.img bs=512 seek=100 conv=notrunk

注意:notrunc的含义是,如果目标文件(这里就是c.img)比来源文件大,不截断目标文件多出来的那部分内容。

比如c.img已经有1024字节了,而c08usr.bin假设只有512字节,如果seek=0,那么不加conv=notrunc,c.img就会变成512字节。

【dd命令说明】

if=输入文件

of=输出文件

ibs = n_bytes 一次读取n_bytes字节,即读入缓冲区的字节数。

obs = n_bytes 一次写入n_bytes字节,即写 入缓冲区的字节数。

bs = n_bytes 同时设置读/写缓冲区的字节数(等于设置ibs和obs)。

count=n_blocks:仅拷贝 n_blocks 个块,块大小等于 ibs 指定的字节数

conv=sync:把每个输入记录的大小都调到ibs的大小(不足的话用Null填充)。

skip=xxx指在备份时对if 后面的部分也就是原文件跳过多少块再开始备份(block size由ibs指定 )

seek=xxx指在备份时对of 后面的部分也就是目标文件跳过多少块再开始写(block size由obs指定 )

6.启动Bochs,查看结果



我们可以看到,应用程序成功地被加载器加载了。

关于本文的源码,我们下次再说明。

Goodbye

硬盘和显卡的访问与控制(一)——《x86汇编语言:从实模式到保护模式》读书笔记01的更多相关文章

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

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

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

    上一篇博文我们用了很大的篇幅说了加载器,这一篇我们该说说用户程序了. 先看作者的源码吧. ;代码清单8-2 ;文件名:c08.asm ;文件说明:用户程序 ;创建日期:2011-5-5 18:17 ; ...

  3. ASM:《X86汇编语言-从实模式到保护模式》第8章:实模式下硬盘的访问,程序重定位和加载

        第八章是一个非常重要的章节,讲述的是实模式下对硬件的访问(这一节主要讲的是硬盘),还有用户程序重定位的问题.现在整理出来刚好能和保护模式下的用户程序定位作一个对比. ★PART1:用户程序的重 ...

  4. ASM:《X86汇编语言-从实模式到保护模式》1-4章:处理器,内存和硬盘基础

    其实很久之前就学完了实模式了,但是一直没有总结,感觉现在直接在书上做笔记的弊端就是有些知识点不能很很深刻地记下来(毕竟手写最明显的优点就是能深刻地记住知识,但是就是用太多的时间罢了).一下内容都是一些 ...

  5. ASM:《X86汇编语言-从实模式到保护模式》越计卷:实模式下对DMA和Sound Blaster声卡的控制

    说实话越计卷作者用了16页(我还是删过的),来讲怎么控制声卡,其实真正归纳起来就那么几点. ★PART1:直接存储访问 1. 总线控制设备(bus master) 在硬件技术不发达的早期,处理器是最重 ...

  6. x86保护模式-六 控制转移

    控制转移可以分为两大类  :同一任务内的控制转移    和   任务间的控制转移(任务切换) 同一个任务内的控制转移可以分为段内转移 .特权级不变的段间转移和特权级改变的段间转移 段内转移与实模式相同 ...

  7. 强化学习读书笔记 - 10 - on-policy控制的近似方法

    强化学习读书笔记 - 10 - on-policy控制的近似方法 学习笔记: Reinforcement Learning: An Introduction, Richard S. Sutton an ...

  8. 论Java访问权限控制的重要性

    人在什么面前最容易失去抵抗力? 美色,算是一个,比如说西施的贡献薄就是忍辱负重.以身报国.助越灭吴:金钱,算是另外一个,我们古人常说“钱乃身外之物,生不带来死不带去”,但我们又都知道“有钱能使鬼推磨” ...

  9. MongoDB 安全和访问权限控制

    MongoDB的访问控制能够有效保证数据库的安全,访问控制是指绑定Application监听的IP地址,设置监听端口,使用账户和密码登录 一,访问控制的参数 1,绑定IP地址 mongod 参数:-- ...

随机推荐

  1. Windows 安装 mysql-5.7.12-winx64(CommunityServer) 备忘

    下载后文件名 mysql-5.7.12-winx64.msi 以为还是会和以往一样,一路next傻瓜式安装并完成设置,然后启动运行,但实际情况不是,感觉就是一解压过程,然后点击The MySQL Co ...

  2. mvvm模式下在WPF项目中动态加载项目的程序集和类

    在mvvm模式的wpf项目中有个需求需要去加载解决方案的程序集,并且根据程序集去动态加载当前程序集的类,做成下拉框形式. 效果: //全局定义 private ComboBox abList= nul ...

  3. dubbo异步调用原理 (1)

    此文已由作者赵计刚授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 一.使用方式 服务提供方不变,调用方代码如下: 1     <dubbo:reference id=& ...

  4. Linux计划任务 定时任务 Crond 配置详解 crond计划任务调试 sh -x 详解 JAVA脚本环境变量定义

    一.Crond 是什么?(概述) crontab 是一款linux系统中的定时任务软件用于实现无人值守或后台定期执行及循环执行任务的脚本程序,在企业中使用的非常广泛.     现在开始学习linux计 ...

  5. 用python面向对象的方法实现欧拉算法和龙格库塔算法

    #!/bin/python3 # -*-coding:utf-8 -*- import math import numpy as np #定义一个欧拉算法的类,从而实现不同步长的引用 class Eu ...

  6. EA888发动机系列

    EA888发动机是一个发动机系列,有很多型号,比如从二代机开始,二代机叫做Evolution 2,简称EVO2,分别有CEA:1.8T 横置:CCU:1.8T 纵置:CGM:2.0T横置,CRH:2. ...

  7. Python十大应用领域与就业方向

    参考链接:https://baijiahao.baidu.com/s?id=1604847283884842928&wfr=spider&for=pc 正文: 近些年,编程语言Pyth ...

  8. 统计Metric

    package com.example.mail; import org.apache.storm.Config; import org.apache.storm.LocalCluster; impo ...

  9. PHP HTTP的PUT,DELETE的使用

    GET操作是安全的.所谓安全是指不管进行多少次操作,资源的状态都不会改变.比如我用GET浏览文章,不管 浏览多少次,那篇文章还在那,没有变化.当然,你可能说每浏览一次文章,文章的浏览数就加一,这不也改 ...

  10. 15.Servlet程序结构与部署

    1.JavaEE应用程序结构 组成:Servlet  JSP  工具类  第三方jar包,HTML页面(图片.Flash) 部署结构: JavaEE应用根目录下的资源都是允许客户端访问的(WEB-IN ...