logo资料库

x86汇编语言-从实模式到保护模式(文字版).pdf

第1页 / 共388页
第2页 / 共388页
第3页 / 共388页
第4页 / 共388页
第5页 / 共388页
第6页 / 共388页
第7页 / 共388页
第8页 / 共388页
资料共388页,剩余部分请下载后查看
x86 汇编语言 从实模式到保护模式 李 忠 王晓波 余 洁 著 Publishing House of Electronics Industry BEIJING
x86 汇编语言:从实模式到保护模式 内 容 简 介 每一种处理器都有它自己的机器指令集,而汇编语言的发明则是为了方便这些机器指令的记忆和书写。 尽管汇编语言已经较少用于大型软件程序的开发,但从学习者的角度来看,要想真正理解计算机的工作原理, 掌握它内部的运行机制,学习汇编语言是必不可少的。 这套图书分为两册,采用开源的 NASM 汇编语言编译器和 VirtualBox 虚拟机软件,以个人计算机广泛采 用的 Intel 处理器为基础,详细讲解了 Intel 处理器的指令系统和工作模式,以大量的代码演示了 16/32/64 位软件的开发方法。上册集中介绍处理器的 16 位实模式和 32 位保护模式,以及基本的指令系统;下册侧重于 介绍 64 位工作模式、多处理器管理、高速缓存控制、温度和电源管理、高级可编程中断控制器、多媒体支持 等。 这是一本有趣的书,它没有把篇幅花在计算一些枯燥的数学题上。相反,它教你如何直接控制硬件,在 不借助于 BIOS、DOS、Windows、Linux 或者任何其他软件支持的情况下来显示字符、读取硬盘数据、控制其 他硬件等。本书可作为大专院校相关专业学生和计算机编程爱好者的教程。 未经许可,不得以任何方式复制或抄袭本书之部分或全部内容。 版权所有,侵权必究。 图书在版编目(CIP)数据 /主编. —北京: 电子工业出版社,2012.9 ISBN 978-7-121-0-0 Ⅰ. ①汇… Ⅱ. ①… Ⅲ. ① Ⅳ. ① 中国版本图书馆 CIP 数据核字(2012)第 号 责任编辑:董亚峰 印 刷: 订: 装 出版发行:电子工业出版社 北京市海淀区万寿路 173 信箱 邮编 100036 开 本:787×1 092 1/16 印张: 字数: 千字 印 次:2012 年 9 月第 1 次印刷 定 价:00.00 元 凡所购买电子工业出版社图书有缺损问题,请向购买书店调换。若书店售缺,请与本社发行部联系,联系 及邮购电话:(010)88254888。 质量投诉请发邮件至 zlts@phei.com.cn,盗版侵权举报请发邮件至 dbqq@phei.com.cn。 服务热线:(010)88258888。 4
第 1 章 十六进制计数法 前 言 尽管汇编语言也是一种计算机语言,但却是与众不同的,与它的同类们格格不入。一方面, 处理器的工作是执行指令,用它所做的一切都是执行指令并获得结果;另一方面,汇编语言为 每一种指令提供了简单好记、易于书写的符号化表示形式。 一直以来,人们对于汇编语言的认识和评价可以分为两种,一种是觉得它非常简单,另一 种是觉得它学习起来非常困难。 你认为我会赞同哪一种?说汇编语言难学,这没有道理。学习任何一门计算机语言,都需 要一些数制和数制转换的知识,也需要大体上懂得计算机是怎么运作的。在这个前提下,汇编 语言是最贴近硬件实体的,也是最自然和最朴素的。最朴素的东西反而最难掌握,这实在说不 过去。因此,原因很可能出在我们的教科书上,那些一上来就搞一大堆寻址方式的书,往往以 最快的速度打败了本来激情高昂的初学者。 但是,说汇编语言好学,也同样有些荒谬。据我的观察,很多人掌握了若干计算机指令, 会编写一个从键盘输入数据,然后进行加减乘除或者归类排序的程序后,就认为自己掌握了汇 编语言。还有,直到现在,我还经常在网上看到学生们使用 DOS 中断编写程序,他们讨论的 也大多是实模式,而非 32 位或者 64 位保护模式。他们知道如何编译源程序,也知道在命令行 输入文件名,程序就能运行了,使用一个中断,就能显示字符。至于这期间发生了什么,程序 是如何加载到内存中的,又是怎么重定位的,似乎从来不关汇编语言的事。这样做的结果,就 是让人以为汇编语言不过如此,没有大用,而且非常枯燥。 很难说我已经掌握了汇编语言的要义。但至少我知道,尽管汇编语言不适合用来编写大型 程序,但它对于理解计算机原理很有帮助,特别是处理器的工作原理和运行机制。就算是为了 这个目的,也应该让汇编语言回归它的本位,那就是访问和控制硬件(包括处理器),而不仅 仅是编写程序,输入几个数字,找出正数有几个、负数有几个,大于 30 的有几个。 事实上,汇编语言对学习和理解高级语言,比如 C 语言,也有极大的帮助。老教授琢磨了 好几天,终于想到一个好的比喻来帮助学生理解什么是指针,实际上,这对于懂得汇编语言的 学生来说,根本就不算个事儿,并因此能够使老教授省下时间来喝茶。 对于一个国家来说,不能没有人来研究基础学科,尽管它们不能直接产生效益;而对于一 个人来说,也不能没有常识。尽管常识不能直接挣钱吃饭,但它影响谈吐,影响你的判断力和 决断力,决定着你接受新事物和新知识的程度。相应地,汇编语言就是计算机语言里的常识和 基础。 这是继《穿越计算机的迷雾》之后,我写的第二本书。这本书与上本书有两点不同,第一, 上一本花了 4 年才完成,而这本只用了一年,速度之快,令我自己咂舌;第二,上本书属于科 普性质,漫谈计算机原理,这本书就相对专业了。那些还想把我的书当小说看的人,这回要失 望了。 很多人可能会问我,为什么要写这样一本书。我只能说,我第一次学汇编的经历实在是太 深刻了。我第一次学汇编语言是在 1993 年,手中的教材不能说不好,但学习起来实在很吃力。 要知道,在那个年代,没有网络,要买到好书,还得到大武汉。就这样,我抱着两本书,反反 复复地看,直到半年之后才懂得汇编语言是个什么东西。后来,虽然有心写一本汇编语言的书, 一本不一样的汇编语言书,但始终没有时间和精力。 时间过得真快,转眼 20 年过去了。猛回头,我发现同学们依然在走我的老路,他们所用 5
x86 汇编语言:从实模式到保护模式 的教材,都还是我那个年代的,至少区别不大,都还在讲 8086 处理器的实模式。保护模式是 从哪个处理器开始引入的?当然是 80286。它是哪个年代的产品?1982 年!可是,直到现在, 市面上也找不到太多能够把保护模式讲得比较清楚的图书。 也许我应该做点什么。不,事实上,我已经做了,那就是你手中的这本图书。王晓波和湖 北经济学院的余洁共同参与了本书的创作。 在计划写这本书的时候,我就给自己画了几条线。首先不能走老路,一上来就讲指令、寻 址方式,采用任务驱动方式来写,每一章都要做点事情,最好是比较有趣,足够引起读者的事 情。在解决问题的过程中,引入一个个的指令,并进行讲解。一句话,我希望是润物细无声式 的。 其次,汇编语言和硬件并举,完全抛弃 BIOS 中断和 DOS 中断,直接访问硬件,发挥汇 编语言的长处。这样,读者才会深刻体会到汇编的妙处。 这套图书主要讲述 16 位实模式、32 位保护模式和 Intel-64 架构。引入虚拟 8086 模式是为 了兼容传统的 8086 程序,现在看来已经完全过时,不再进行讲述。至于增强的 32 位模式 IA-32e, 读者可以在读完这本书之后自学,也予以省略。 书中配套的程序清单和源代码以及可能用到的程序软件,感兴趣的读者可到电子工业出版 社华信教育资源网下载(待定)。 本书原来有 18 章,后来,考虑到实模式的内容过多,而去掉了一章。这一章的标题是《聆 听数字的声音》,讲述如何通过直接访问和控制 Sound Blaster 16 声卡来播放声音,对此感兴趣 的朋友可以在配书光盘中找到它。 特别感谢长春电视台的王志强台长和台长助理周武军,上本书《穿越计算机的迷雾》出版 后,台长王志强亲自过问出版情况,并给予我特别的奖励,希望大家同样能从这本书中读到他 们对我的关怀和鼓励;同时也要感谢我的母亲、我爱人和我的女儿,她们是我的精神支柱。好 友王南洋、桑国伟、刘维钊、蒋胜友、邱海龙、万利等负责了本书的一部分校对工作;好友周 卫平帮我验证配书代码是否能够在他的机器上正常工作;如果想调试本书中的程序,可以使用 bochs 软件,它的视频教程是由王南洋制作的,在这里向他们表示感谢。在阅读本书的过程中, 如果有任何问题,可以按以下电子邮件地址给我写信:leechung@126.com;或者进入我的博客 参与讨论。博客地址是 http://blog.163.com/leechung@126 6
第 1 章 十六进制计数法 目 录 第 1 部分 预备知识 第 1 章 十六进制计数法 ··························································································· 3 1.1 二进制计数法回顾 ·················································································· 3 1.1.1 关于二进制计数法 ··········································································· 3 1.1.2 二进制到十进制的转换 ····································································· 3 1.1.3 十进制到二进制的转换 ····································································· 4 1.2 十六进制计数法 ····················································································· 4 1.2.1 十六进制计数法的原理 ····································································· 4 1.2.2 十六进制到十进制的转换 ·································································· 5 1.2.3 十进制到十六进制的转换 ·································································· 6 1.3 为什么需要十六进制 ··············································································· 6 本章习题 ···································································································· 7 第 2 章 处理器、内存和指令 ···················································································· 8 2.1 最早的处理器 ························································································ 8 2.2 寄存器和算术逻辑部件 ············································································ 8 2.3 内存储器···························································································· 10 2.4 指令和指令集 ······················································································ 11 2.5 古老的 Intel 8086 处理器 ········································································ 13 2.5.1 8086 的通用寄存器 ········································································· 13 2.5.2 程序的重定位难题 ········································································· 14 2.5.3 内存分段机制 ··············································································· 17 2.5.4 8086 的内存分段机制 ······································································ 18 本章习题 ·································································································· 21 第 3 章 汇编语言和汇编软件 ···················································································22 3.1 汇编语言简介 ······················································································ 22 3.2 NASM 编译器 ····················································································· 24 3.2.1 从网上下载 NASM 安装程序 ···························································· 24 3.2.2 安装 NASM 编译器 ········································································ 25 3.2.3 下载配书源码和工具 ······································································ 26 3.2.4 用 Nasmide 体验代码的书写和编译过程 ·············································· 28 3.2.5 用 HexView 观察编译后的机器代码 ···················································· 29 本章习题 ·································································································· 30 7
x86 汇编语言:从实模式到保护模式 第 4 章 虚拟机的安装和使用 ········································································· 31 4.1 计算机的启动过程 ················································································ 31 4.1.1 如何将编译好的程序提交给处理器 ···················································· 31 4.1.2 计算机的加电和复位 ······································································ 31 4.1.3 基本输入输出系统 ········································································· 32 4.1.4 硬盘及其工作原理 ········································································· 33 4.1.5 一切从主引导扇区开始 ··································································· 35 4.2 创建和使用虚拟机 ················································································ 35 4.2.1 别害怕,虚拟机是软件 ··································································· 35 4.2.2 下载 Oracle VM VirtualBox ······························································· 36 4.2.3 安装 Oracle VM VirtualBox ······························································· 36 4.2.4 创建一台虚拟 PC ··········································································· 37 4.2.5 虚拟硬盘简介 ··············································································· 42 4.2.6 练习使用 FixVhdWr 工具向虚拟硬盘写数据 ········································· 43 第 2 部分 16 位处理器下的实模式 第 5 章 编写主引导扇区代码 ···················································································49 5.1 欢迎来到主引导扇区 ············································································· 49 5.2 注释 ································································································· 49 5.3 在屏幕上显示文字 ················································································ 50 5.3.1 显卡和显存 ·················································································· 50 5.3.2 初始化段寄存器 ············································································ 52 5.3.3 显存的访问和 ASCII 代码 ································································ 53 5.3.4 显示字符 ····················································································· 55 5.4 显示标号的汇编地址 ············································································· 56 5.4.1 标号 ··························································································· 56 5.4.2 如何显示十进制数字 ······································································ 60 5.4.3 在程序中声明并初始化数据 ····························································· 61 5.4.4 分解数的各个数位 ········································································· 61 5.4.5 显示分解出来的各个数位 ································································ 65 5.5 使程序进入无限循环状态 ······································································· 66 5.6 完成并编译主引导扇区代码 ···································································· 67 5.6.1 主引导扇区有效标志 ······································································ 67 5.6.2 代码的保存和编译 ········································································· 68 5.7 加载和运行主引导扇区代码 ···································································· 68 5.7.1 把编译后的指令写入主引导扇区 ······················································· 68 5.7.2 启动虚拟机观察运行结果 ································································ 70 5.7.3 程序的调试 ·················································································· 70 8
第 1 章 十六进制计数法 本章习题 ·································································································· 71 第 6 章 相同的功能,不同的代码 ············································································· 72 6.1 代码清单 6-1 ······················································································· 72 6.2 跳过非指令的数据区 ············································································· 72 6.3 在数据声明中使用字面值 ······································································· 72 6.4 段地址的初始化 ··················································································· 73 6.5 段之间的批量数据传送 ·········································································· 74 6.6 使用循环分解数位 ················································································ 75 6.7 计算机中的负数 ··················································································· 76 6.7.1 无符号数和有符号数 ······································································ 76 6.7.2 处理器视角中的数据类型 ································································ 80 6.8 数位的显示 ························································································· 82 6.9 其他标志位和条件转移指令 ···································································· 83 6.9.1 奇偶标志位 PF ·············································································· 83 6.9.2 进位标志 CF ················································································· 83 6.9.3 溢出标志 OF ················································································· 84 6.9.4 现有指令对标志位的影响 ································································ 84 6.9.5 条件转移指令 ··············································································· 85 6.10 NASM 编译器的$和$$标记 ··································································· 87 6.11 观察运行结果 ···················································································· 87 本章习题 ·································································································· 88 第 7 章 比高斯更快的计算 ·······················································································89 7.1 从 1 加到 100 的故事 ············································································· 89 7.2 代码清单 7-1 ······················································································· 89 7.3 显示字符串 ························································································· 89 7.4 计算 1 到 100 的累加和 ·········································································· 90 7.5 累加和各个数位的分解与显示 ································································· 90 7.5.1 堆栈和堆栈段的初始化 ··································································· 90 7.5.2 分解各个数位并压栈 ······································································ 92 7.5.3 出栈并显示各个数位 ······································································ 94 7.5.4 进一步认识堆栈 ············································································ 95 7.6 程序的编译和运行 ················································································ 96 7.7 8086 处理器的寻址方式 ········································································· 96 7.7.1 寄存器寻址 ·················································································· 96 7.7.2 立即寻址 ····················································································· 97 7.7.3 内存寻址 ····················································································· 97 本章习题 ································································································· 101 9
分享到:
收藏