﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-luqingfei@C++-随笔分类-汇编语言学习</title><link>http://www.cppblog.com/luqingfei/category/14224.html</link><description>Be water, my friends!&lt;br/&gt;世界上只有一种失败，那就是半途而废。</description><language>zh-cn</language><lastBuildDate>Thu, 05 Aug 2010 12:27:34 GMT</lastBuildDate><pubDate>Thu, 05 Aug 2010 12:27:34 GMT</pubDate><ttl>60</ttl><item><title>《汇编语言》--读后感</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/05/122279.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Thu, 05 Aug 2010 02:48:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/05/122279.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/122279.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/05/122279.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/122279.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/122279.html</trackback:ping><description><![CDATA[花了近一个月的时间，终于坚持把王爽老师的《汇编语言》从头到尾看完了一遍。<br>跟着做了笔记（大多时候其实是大段大段的摘录，第一遍看下来，对于我来说，需要消化的东西太多了。）；<br>坚持做了书中的大部分练习和测试，（由于是第一次看汇编，着实吃力）。<br><br>现在虽然已经看完了一遍，感觉书中的知识只大概有了些许概念，在脑子里建立了一个关于汇编的框架。<br><br>根据记忆的规律，我将接下来，经常回顾这本好书，并且在今后的学习中，对照从这本书中所学的知识点加深理解。实践中强化记忆。<br><br>接下来，我计划，看罗云彬大虾的《Windows环境下32位汇编语言程序设计》这本书。<br><br>循序渐进，let's go！<br><br>若哪位朋友有推荐的好书，还请推荐推荐！非常感谢！ 
<img src ="http://www.cppblog.com/luqingfei/aggbug/122279.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-05 10:48 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/05/122279.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编语言--补码</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/05/122271.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Thu, 05 Aug 2010 01:52:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/05/122271.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/122271.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/05/122271.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/122271.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/122271.html</trackback:ping><description><![CDATA[&nbsp;
<p>以8位的数据为例，对于无符号数来说是从00000000b~11111111b到0~255一一对应的。那么我们如何对有符号数进行编码吗？即我们如何用8位数据表示有符号数呢？</p>
<p>&nbsp;</p>
<p>既然表示的数有符号，则必须要能够区分正、负。</p>
<p>&nbsp;</p>
<p>首先，我们可以考虑用8位数据的最高位来表示符号，1表示负，0表示正，而用其他位表示数值，如下：</p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=103>
            <p>00000000b</p>
            </td>
            <td vAlign=top width=103>
            <p>0</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=103>
            <p>00000001b</p>
            </td>
            <td vAlign=top width=103>
            <p>1</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=103>
            <p>00000010b</p>
            </td>
            <td vAlign=top width=103>
            <p>2</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=103>
            <p>01111111b</p>
            </td>
            <td vAlign=top width=103>
            <p>127</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=103>
            <p>10000000b</p>
            </td>
            <td vAlign=top width=103>
            <p>???</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=103>
            <p>10000001b</p>
            </td>
            <td vAlign=top width=103>
            <p>-1</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=103>
            <p>10000010b</p>
            </td>
            <td vAlign=top width=103>
            <p>-2</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=103>
            <p>11111111b</p>
            </td>
            <td vAlign=top width=103>
            <p>-127</p>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p>可见，用上面的表示方式，8位数据可以表示-127~127的254个有符号数。从这里我们看出一些问题（注意问号处），8位数据可以表示255种不同的信息，也就是说应该可以表示255个有符号数，可用上面的方法，只能表示254个有符号数。注意，用上面的方法，00000000b和10000000b都表示0，一个是0，一个是-0，当然不可能有-0。可以看出，这种表示有符号数的方法是有问题的，它并不能正确地表示有符号数。</p>
<p>&nbsp;</p>
<p>我们再考虑用反码来表示，这种思想是，我们先确定用00000000b~01111111b表示0~127，然后再用它们按位取反后的数据表示负数。如下：</p>
<table cellSpacing=0 cellPadding=0 width=360 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=77>
            <p>00000000b</p>
            </td>
            <td vAlign=top width=35>
            <p>0</p>
            </td>
            <td vAlign=top width=116>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=74>
            <p>11111111b</p>
            </td>
            <td vAlign=top width=57>
            <p>???</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=77>
            <p>00000001b</p>
            </td>
            <td vAlign=top width=35>
            <p>1</p>
            </td>
            <td vAlign=top width=116>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=74>
            <p>11111110b</p>
            </td>
            <td vAlign=top width=57>
            <p>-1</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=77>
            <p>00000010b</p>
            </td>
            <td vAlign=top width=35>
            <p>2</p>
            </td>
            <td vAlign=top width=116>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=74>
            <p>11111101b</p>
            </td>
            <td vAlign=top width=57>
            <p>-2</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=77>
            <p>01111111b</p>
            </td>
            <td vAlign=top width=35>
            <p>127</p>
            </td>
            <td vAlign=top width=116>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=74>
            <p>10000000b</p>
            </td>
            <td vAlign=top width=57>
            <p>-127</p>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p>可以看出，用反码表示有符号数存在同样的问题，0出现重码（注意问号处）。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>为了解决这种问题，采用一种称为补码的编码方法。这种思想是：先确定用00000000b~01111111b表示0~127，然后再用它们按位取反加1后的数据表示负数。如下：</p>
<table cellSpacing=0 cellPadding=0 width=465 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=77>
            <p>00000000b</p>
            </td>
            <td vAlign=top width=35>
            <p>0</p>
            </td>
            <td vAlign=top width=116>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=179>
            <p>11111111b + 1 =00000000b</p>
            </td>
            <td vAlign=top width=57>
            <p>0</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=77>
            <p>00000001b</p>
            </td>
            <td vAlign=top width=35>
            <p>1</p>
            </td>
            <td vAlign=top width=116>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=179>
            <p>11111110b + 1 =11111111b</p>
            </td>
            <td vAlign=top width=57>
            <p>-1</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=77>
            <p>00000010b</p>
            </td>
            <td vAlign=top width=35>
            <p>2</p>
            </td>
            <td vAlign=top width=116>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=179>
            <p>11111101b + 1 =11111110b</p>
            </td>
            <td vAlign=top width=57>
            <p>-2</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=77>
            <p>01111111b</p>
            </td>
            <td vAlign=top width=35>
            <p>127</p>
            </td>
            <td vAlign=top width=116>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=179>
            <p>10000000b + 1 =10000001b</p>
            </td>
            <td vAlign=top width=57>
            <p>-127</p>
            </td>
        </tr>
    </tbody>
</table>
<p>观察上面的数据，我们可以发现，<span style="BACKGROUND-COLOR: yellow">补码方案</span>中：</p>
<p style="BACKGROUND-COLOR: yellow">1）最高位为1，表示负数；</p>
<p><span style="BACKGROUND-COLOR: yellow">2）正数的补码取反加1后，为其对应的负数的补码：负数的补码取反加1后，为其绝对值</span>，比如：</p>
<p>1的补码为：00000001b，取反加1后为：11111111b，表示-1；</p>
<p>-1的补码为：11111111b，取反加1后为：00000001，其绝对值为1。</p>
<p>&nbsp;</p>
<p>我们从一个负数的补码不太容易看出它所表示的数据，比如：11010101b表示的数据是多少？</p>
<p>&nbsp;</p>
<p>但是我们利用补码的特性，将11010101b取反加1后为：00101011b，可以知11010101b表示的负数的绝对值为：2BH，则11010101b表示的负数为-2BH。</p>
<p>&nbsp;</p>
<p>那么-20的补码是多少呢？</p>
<p>用补码的特性，-20的绝对值是20，00010100b，将其取反加1后为：11101100b。可知-20H的补码为：1101100b。</p>
<p>&nbsp;</p>
<p>那么10000000b表示多少呢？</p>
<p>10000000b取反加1后为：10000000b，其大小为128，所以10000000b表示-128。</p>
<p>&nbsp;</p>
<p>8位补码所表示的数的范围：-128~127。</p>
<p>&nbsp;</p>
<p>补码为有符号数的运算提供了方便，运算后的结果依旧满足补码规则。</p>
<p>比如：</p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=61>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=71>
            <p>计算</p>
            </td>
            <td vAlign=top width=71>
            <p>补码表示</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=61>
            <p align=right>&nbsp;</p>
            </td>
            <td vAlign=top width=71>
            <p align=right>10</p>
            </td>
            <td vAlign=top width=71>
            <p align=right>00001010b</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=61>
            <p align=right>&nbsp;</p>
            </td>
            <td vAlign=top width=71>
            <p align=right>＋(－20)</p>
            </td>
            <td vAlign=top width=71>
            <p align=right>11101100b</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=61>
            <p align=right>结果</p>
            </td>
            <td vAlign=top width=71>
            <p align=right>－10</p>
            </td>
            <td vAlign=top width=71>
            <p align=right>11110110b</p>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p>&nbsp;</p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=61>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=83>
            <p>计算</p>
            </td>
            <td vAlign=top width=77>
            <p>补码表示</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=61>
            <p align=right>&nbsp;</p>
            </td>
            <td vAlign=top width=83>
            <p align=right>10</p>
            </td>
            <td vAlign=top width=77>
            <p align=right>00001010b</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=61>
            <p align=right>&nbsp;</p>
            </td>
            <td vAlign=top width=83>
            <p align=right>＋(－128)</p>
            </td>
            <td vAlign=top width=77>
            <p align=right>10000000b</p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=61>
            <p align=right>结果</p>
            </td>
            <td vAlign=top width=83>
            <p align=right>－118</p>
            </td>
            <td vAlign=top width=77>
            <p align=right>10001010b</p>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/122271.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-05 09:52 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/05/122271.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编语言--Intel系列微处理器的三种工作模式</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/05/122264.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Thu, 05 Aug 2010 00:50:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/05/122264.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/122264.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/05/122264.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/122264.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/122264.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>微机中常用的</span><span>Intel</span><span>系列微处理器的主要发展过程是：</span><span>8080</span><span>，</span><span>8086/8088</span><span>，</span><span>80186</span><span>，</span><span>80286</span><span>，</span><span>80386</span><span>，</span><span>80486</span><span>，</span><span>Pentium, Pentium II, Pentium III, Pentium 4</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>8086/8088</span><span>是一个重要的阶段，它们略有区别。</span><span>8088</span><span>被</span><span>IBM</span><span>用在了它所生产的第一台微机上，该微机的结构事实上成为以后微机的基本结构。</span></p>
<p>&nbsp;</p>
<p><span>80386</span><span>是第二个重要的型号，随着微机应用及性能的发展，在微机上构造可靠的多任务操作系统的问题日益突出。</span></p>
<p>&nbsp;</p>
<p><span>8086/8088</span><span>不具备实现一个完善的多任务操作系统的功能。为此</span><span>Intel</span><span>开发了</span><span>80286</span><span>，</span><span>80286</span><span>具备了对多任务系统的支持。但对</span><span>8086/8088</span><span>的兼容却做得不好。这妨碍了用户对原</span><span>8086</span><span>机上的程序的使用。</span><span>IBM</span><span>最早基于</span><span>80286</span><span>开发了多任务系统</span><span>OS/2</span><span>，结果犯了一个战略错误。</span></p>
<p>&nbsp;</p>
<p><span>随后</span><span>Intel</span><span>又开发了</span><span>80386</span><span>微处理器，这是一个划时代的新产品。它可以在</span><span>3</span><span>个模式下工作：</span></p>
<p><span>1</span><span>）实模式：工作方式相当于一个</span><span>8086</span><span>。</span></p>
<p><span>2</span><span>）保护模式：提供支持多任务环境的工作方式，建立保护机制。</span></p>
<p><span>3</span><span>）虚拟</span><span>8086</span><span>模式：可从保护模式切换至其中的一种</span><span>8086</span><span>工作方式。这种方式的提供使用户可以方便地在保护模式下运行一个或多个原</span><span>8086</span><span>程序。</span></p>
<p>&nbsp;</p>
<p><span>以后的各代微处理器都提供了上述</span><span>3</span><span>种工作模式。</span></p>
<p>&nbsp;</p>
<p><span>也许你会觉得，这三种模式太抽象了，其实</span><span>CPU</span><span>的这</span><span>3</span><span>种模式只要用过</span><span>PC</span><span>机的人都经历过。任何一台使用</span><span>Intel</span><span>系列</span><span>CPU</span><span>的</span><span>PC</span><span>机只要一开机，</span><span>CPU</span><span>就工作在实模式下。如果你的机器装的是</span><span>DOS</span><span>，那么在</span><span>DOS</span><span>加载后</span><span>CPU</span><span>仍以实模式工作。如果你的机器装的是</span><span>Windows</span><span>，那么</span><span>Windows</span><span>加载后，将由</span><span>Windows</span><span>将</span><span>CPU</span><span>切换到保护模式下工作，因为</span><span>Windows</span><span>是多任务系统，它必须在保护模式下运行。如果你在</span><span>Windows</span><span>中运行一个</span><span>DOS</span><span>下的程序，那么</span><span>Windows</span><span>将</span><span>CPU</span><span>切换到虚拟</span><span>8086</span><span>模式下运行该程序。或者是这样，你点击开始菜单在程序项中进入</span><span>MS-DOS</span><span>方式，这时，</span><span>Windows</span><span>也将</span><span>CPU</span><span>切换到虚拟</span><span>8086</span><span>模式下运行。</span></p>
<p>&nbsp;</p>
<p><span>可以从保护模式直接进入能运行原</span><span>8086</span><span>程序的虚拟</span><span>8086</span><span>模式是很有意义的，这为用户提供了一种机制，可以在现有的多任务系统中方便地运行原</span><span>8086</span><span>系统中的程序。</span></p>
<p>&nbsp;</p>
<p><span>前面讲过，我们在</span><span>8086PC</span><span>机的基础上学习汇编语言，但现在知道，我们实际的编程环境是当前</span><span>CPU</span><span>的实模式，当然，有些程序也可以在虚拟</span><span>8086</span><span>模式下运行。</span></p>
<p>&nbsp;</p>
<p><span>从</span><span>80386</span><span>到当前的</span><span>CPU</span><span>，提供</span><span>8086</span><span>实模式的目的是为了兼容。现今</span><span>CPU</span><span>的真正有效力的工作模式是支持多任务操作系统的保护模式。这也许会引发你的一个疑问：&#8220;为什么我们不在保护模式下学习汇编语言？&#8221;</span></p>
<p>&nbsp;</p>
<p><span>类似问题很多，我们都希望学习更新的东西，但学习的过程是客观的。任何合理的学习过程（尽可能排除走弯路、盲目探索、不成系统）都是一个循序渐进的过程。我们必须先通过一个易于全面把握的事物，来学习和探索一般的规律和方法。信息技术是一个发展非常快、日新月异的技术，新的东西不断出现，使人在学习的时候往往无所适从。在你的身边不断有这样的故事出现：</span><span>COOL</span><span>先生用了三天（或更短）的时间就学会了某某语言，并开始用它编软件。。在这个故事的感召下，一个初学者也去尝试，但完全是另外一种结果。</span><span>COOL</span><span>先生的快速学习只是露出水面的冰山一角，深藏水下的是他的较为系统的相关基础知识和相关的技术。在开始的时候学习保护模式下的编程，是不现实的，保护模式下所涉及的东西对初学者来说太复杂。你必须知道很多知识后，才能开始编写第一个小程序。相比之下</span><span>8086</span><span>就合适很多。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/122264.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-05 08:50 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/05/122264.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编语言--使用BIOS进行键盘输入和磁盘读写</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/04/122220.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Wed, 04 Aug 2010 08:50:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/04/122220.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/122220.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/04/122220.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/122220.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/122220.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>使用</span><span>BIOS</span><span>进行键盘输入和磁盘读写</span></p>
<p>&nbsp;</p>
<p><span>大多数有用的程序都需要处理用户的输入，键盘输入是最基本的输入。程序和数据通常需要长期存储，磁盘是最常用的存储设备。</span></p>
<p><span>BIOS</span><span>为这两种外设的</span><span>I/O</span><span>提供了最基本的中断例程。下面将对它们的应用和相关问题进行学习。</span></p>
<p>&nbsp;</p>
<p><strong><span>int 9</span></strong><strong><span>中断例程对键盘输入的处理</span></strong></p>
<p><span>键盘输入将引发</span><span>9</span><span>号中断，</span><span>BIOS</span><span>提供了</span><span>int 9</span><span>中断例程。</span></p>
<p><span>CPU</span><span>在</span><span>9</span><span>号中断发生后，执行</span><span>int 9</span><span>中断例程，从</span><span>60h</span><span>端口读出扫描码，并将其转化为相应的</span><span>ASCII</span><span>码或状态信息，存储在内存的指定空间（键盘缓冲区或状态字节）中。</span></p>
<p>&nbsp;</p>
<p><span>一般的键盘输入，在</span><span>CPU</span><span>执行完</span><span>int 9</span><span>中断例程后，都放到了键盘缓冲区中。键盘缓冲区有</span><span>16</span><span>个字单元，可以存储</span><span>15</span><span>个按键的扫描码和对应的</span><span>ASCII</span><span>码。</span></p>
<p>&nbsp;</p>
<p><span>下面我们按照键盘缓冲区的逻辑结构，来看一下键盘输入的扫描码和对应的</span><span>ASCII</span><span>码是如何写入键盘缓冲区的。</span></p>
<p>&nbsp;</p>
<p><span>注意：在我们的课程中，仅在逻辑结构的基础上，讨论</span><span>BIOS</span><span>键盘缓冲区的读写问题。其实键盘缓冲区是用<span>环形队列结构</span>管理的内存区，但我们不对队列和环形队列的实现进行讨论，因为那是另一门专业课《数据结构》的内容。</span></p>
<p>&nbsp;</p>
<p><span>下面，我们通过下面几个键：</span></p>
<p><span>A</span><span>、</span><span>B</span><span>、</span><span>C</span><span>、</span><span>D</span><span>、</span><span>E</span><span>、</span><span>shift_A</span><span>、</span><span>A</span></p>
<p><span>的输入过程，简要地看一下</span><span>int 9</span><span>中断例程对键盘输入的处理方法：</span></p>
<p><span>（</span><span>1</span><span>）初始状态下，没有键盘输入，键盘缓冲区空，此时没有任何元素：</span></p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
        </tr>
    </tbody>
</table>
<p><span>（</span><span>2</span><span>）按下</span><span>A</span><span>键，引发键盘中断：</span><span>CPU</span><span>执行</span><span>int 9</span><span>中断例程，从</span><span>60h</span><span>端口读出</span><span>A</span><span>键的通码；然后检测状态字节，看看是否有</span><span>shift</span><span>、</span><span>Ctrl</span><span>等切换键按下；发现没有切换键按下，则将</span><span>A</span><span>键的扫描码</span><span>1eh</span><span>和对应的</span><span>ASCII</span><span>码，即字母&#8220;</span><span>a</span><span>&#8221;的</span><span>ASCII</span><span>码</span><span>61h</span><span>，写入键盘缓冲区。缓冲区的字单元中，高位字节存储扫描码，低位字节存储</span><span>ASCII</span><span>码。此时缓冲区中的内容如下：</span></p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=38>
            <p><span>1E61</span></p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
        </tr>
    </tbody>
</table>
<p><span>（</span><span>3</span><span>）按下</span><span>B</span><span>键，引发键盘中断：</span><span>CPU</span><span>执行</span><span>int 9</span><span>中断例程，从</span><span>60h</span><span>端口读出</span><span>B</span><span>键的通码；然后检测状态字节，看看是否有切换键按下；发现没有切换键按下，将</span><span>B</span><span>键的扫描码</span><span>30h</span><span>和对应的</span><span>ASCII</span><span>码，即字母&#8220;</span><span>b</span><span>&#8221;的</span><span>ASCII</span><span>码</span><span>62h</span><span>，写入键盘缓冲区。此时缓冲区中的内容如下：</span></p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=38>
            <p><span>1E61</span></p>
            </td>
            <td vAlign=top width=38>
            <p><span>3062</span></p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
        </tr>
    </tbody>
</table>
<p><span>（</span><span>4</span><span>）按下</span><span>C</span><span>、</span><span>D</span><span>、</span><span>E</span><span>键后，缓冲区中的内容如下：</span></p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=38>
            <p><span>1E61</span></p>
            </td>
            <td vAlign=top width=38>
            <p><span>2062</span></p>
            </td>
            <td vAlign=top width=38>
            <p><span>2E63</span></p>
            </td>
            <td vAlign=top width=38>
            <p><span>2064</span></p>
            </td>
            <td vAlign=top width=38>
            <p><span>1265</span></p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
        </tr>
    </tbody>
</table>
<p><span>（</span><span>5</span><span>）按下左</span><span>shift</span><span>键，引发键盘中断：</span><span>int 9</span><span>中断例程接收左</span><span>shift</span><span>键的通码，设置</span><span>0040:17</span><span>处的状态字节的第</span><span>1</span><span>位为</span><span>1</span><span>，表示左</span><span>shift</span><span>键按下。</span></p>
<p><span>（</span><span>6</span><span>）按下</span><span>A</span><span>键，引发键盘中断：</span><span>CPU</span><span>执行</span><span>int 9</span><span>中断例程；从</span><span>60h</span><span>端口读出</span><span>A</span><span>键的通码；检测状态字节，看看是否有切换键按下，发现左</span><span>shift</span><span>键被按下，则将</span><span>A</span><span>键的扫描码</span><span>1Eh</span><span>和</span><span>shift_A</span><span>对应的</span><span>ASCCII</span><span>码，即字母&#8220;</span><span>A</span><span>&#8221;的</span><span>ASCII</span><span>码</span><span>41</span><span>，写入键盘缓冲区，此时缓冲区中的内容如下：</span></p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=38>
            <p><span>1E61</span></p>
            </td>
            <td vAlign=top width=38>
            <p><span>2062</span></p>
            </td>
            <td vAlign=top width=38>
            <p><span>2E63</span></p>
            </td>
            <td vAlign=top width=38>
            <p><span>2064</span></p>
            </td>
            <td vAlign=top width=38>
            <p><span>1265</span></p>
            </td>
            <td vAlign=top width=38>
            <p><span>1E41</span></p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
        </tr>
    </tbody>
</table>
<p><span>（</span><span>7</span><span>）松开左</span><span>shift</span><span>键，引发键盘中断：</span><span>int 9</span><span>中断例程接收左</span><span>shift</span><span>键的断码，设置</span><span>0040:17</span><span>处的状态字节的第</span><span>1</span><span>位为</span><span>0</span><span>，表示左</span><span>shift</span><span>键松开。</span></p>
<p><span>（</span><span>8</span><span>）按下</span><span>A</span><span>键，引发键盘中断：</span><span>CPU</span><span>执行</span><span>int 9</span><span>中断例程，从</span><span>60h</span><span>端口读出</span><span>A</span><span>键的通码，然后检测状态字节，看看是否有切换键按下，发现没有切换键按下，则将</span><span>A</span><span>键的扫描码</span><span>1Eh</span><span>和</span><span>A</span><span>对应的</span><span>ASCCII</span><span>码，即字母&#8220;</span><span>a</span><span>&#8221;的</span><span>ASCII</span><span>码</span><span>61h</span><span>，写入键盘缓冲区，此时缓冲区中的内容如下：</span></p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=38>
            <p><span>1E61</span></p>
            </td>
            <td vAlign=top width=38>
            <p><span>2062</span></p>
            </td>
            <td vAlign=top width=38>
            <p><span>2E63</span></p>
            </td>
            <td vAlign=top width=38>
            <p><span>2064</span></p>
            </td>
            <td vAlign=top width=38>
            <p><span>1265</span></p>
            </td>
            <td vAlign=top width=38>
            <p><span>1E41</span></p>
            </td>
            <td vAlign=top width=38>
            <p><span>1E61</span></p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=38>
            <p>&nbsp;</p>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>使用</span><span>int 16h</span></strong><strong><span>中断例程读取键盘缓冲区</span></strong></p>
<p><span>BIOS</span><span>提供了</span><span>int 16h</span><span>中断例程供程序员调用。</span><span>int 16h</span><span>中断例程中包含的一个最重要的功能是从键盘缓冲区中读取一个键盘输入，该功能的编号为</span><span>0</span><span>。</span></p>
<p><span>下面的指令<span>从键盘缓冲区中读取一个键盘输入，并将其从缓冲区中删除</span>：</span></p>
<p><span>mov ah,0</span></p>
<p><span>int 16h</span></p>
<p><span>结果：</span><span>(ah)=</span><span>扫描码，</span><span>(al)=ASCII</span><span>码。</span></p>
<p>&nbsp;</p>
<p><span>int 16h</span><span>中断例程的</span><span>0</span><span>号功能，进行如下的工作：</span></p>
<p><span>1</span><span>）检测键盘缓冲区中是否有数据；</span></p>
<p><span>2</span><span>）没有则继续做第</span><span>1</span><span>步；</span></p>
<p><span>3</span><span>）读取缓冲区第一个字单元中的键盘输入；</span></p>
<p><span>4</span><span>）将读取的扫描码送入</span><span>ah</span><span>，</span><span>ASCII</span><span>码送入</span><span>al</span><span>；</span></p>
<p><span>5</span><span>）将已读取的键盘输入从缓冲区中删除。</span></p>
<p>&nbsp;</p>
<p><span>可见，</span><span>BIOS</span><span>的</span><span>int 9</span><span>中断例程和</span><span>int 16h</span><span>中断例程是一对相互配合的程序，</span><span>int 9</span><span>中断例程向键盘缓冲区中写入，</span><span>int 16h</span><span>中断例程从缓冲区中读出。</span></p>
<p><span>它们写入和读出的时机不同，</span><span>int 9</span><span>中断例程是在有按键按下的时候向键盘缓冲区中写入数据；而</span><span>int 16h</span><span>中断例程是在应用程序对其进行调用的时候，将数据从键盘缓冲区中读出。</span></p>
<p>&nbsp;</p>
<p><span>我们在编写一般的处理键盘输入的程序的时候，可以调用</span><span>int 16h</span><span>从键盘缓冲区中读取键盘的输入。</span></p>
<p>&nbsp;</p>
<p><span>编程，接收用户的键盘输入，输入&#8220;</span><span>r</span><span>&#8221;，将屏幕上的字符设置为红色；输入&#8220;</span><span>g</span><span>&#8221;，将屏幕上的字符设置为绿色；输入&#8220;</span><span>b</span><span>&#8221;，将屏幕上的字符设置为蓝色。</span></p>
<p>&nbsp;</p>
<p><span>程序如下：</span></p>
<p><span>assume cs:code</span></p>
<p><span>code segment</span></p>
<p><span>&nbsp;start:mov ah,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 16h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ah,1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cmp al,'r'</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>je red</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cmp al,'g'</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>je green</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cmp al,'b'</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>je blue</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jmp short sret</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp; </span>red:shl ah,1</span></p>
<p><span>&nbsp;green:shl ah,1</span></p>
<p><span><span>&nbsp;&nbsp; </span>blue:mov bx,0b800h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cx,2000</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>s:add byte ptr es:[bx],11111000b</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>or es:[bx],ah</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add bx,2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>loop s</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp; </span>sret:mov ax,<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="4" UnitName="C">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>字符串的输入</span></strong></p>
<p><span>用户通过键盘输入的通常不仅仅是单个字符而是字符串。</span></p>
<p><span>最基本的字符串输入程序，需要具备下面的功能：</span></p>
<p><span>1</span><span>）在输入的同时需要显示这个字符串；</span></p>
<p><span>2</span><span>）一般在输入回车符后，字符串输入结束；</span></p>
<p><span>3</span><span>）能够删除已经输入的字符。</span></p>
<p>&nbsp;</p>
<p><span>编写一个接收字符串的输入子程序，实现上面三个基本功能。</span></p>
<p><span>子程序的参数如下：</span></p>
<p><span>(dh)</span><span>、</span><span>(dl)=</span><span>字符串在屏幕上显示的行、列位置；</span></p>
<p><span>ds:si</span><span>指向字符串的存储空间，字符串以</span><span>0</span><span>为结尾符。</span></p>
<p>&nbsp;</p>
<p><span>分析：</span></p>
<p><span>（</span><span>1</span><span>）字符的输入和删除。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>每个新输入的字符都存储在前一个输入的字符之后，而删除是从最后面的字符进行的。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>看下面的过程：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>空字符串：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>输入&#8220;</span><span>a</span><span>&#8221;：</span><span>a</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>输入&#8220;</span><span>b</span><span>&#8221;：</span><span>ab</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>输入&#8220;</span><span>c</span><span>&#8221;：</span><span>abc</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>输入&#8220;</span><span>d</span><span>&#8221;：</span><span>abcd</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>删除一个字符：</span><span>abc</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>删除一个字符：</span><span>ab</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>删除一个字符：</span><span>a</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>删除一个字符：</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>可以看出在字符串输入的过程中，字符的输入和输出是按照栈的访问规则进行了的，即后进先出。这样，我们就可以用栈的方式来管理字符串的存储空间，也就是说，字符串的存储空间实际上是一个字符栈。字符栈中的所有字符，从栈底到栈顶，组成一个字符串。</span></p>
<p>&nbsp;</p>
<p><span>（</span><span>2</span><span>）在输入回车符后，字符串输入结束。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>输入回车符后，可以在字符串中加入</span><span>0</span><span>，表示字符串结束。</span></p>
<p>&nbsp;</p>
<p><span>（</span><span>3</span><span>）在输入的同时需要显示这个字符串。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>每次有新的字符输入和删除一个字符的时候，都应该重新显示字符串，即从字符栈的栈底到栈顶，显示所有的字符。</span></p>
<p>&nbsp;</p>
<p><span>（</span><span>4</span><span>）程序的处理过程。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>1</span><span>）调用</span><span>int 16h</span><span>读取键盘输入；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>2</span><span>）如果是字符，进入字符栈，显示字符栈中的所有字符；继续执行</span><span>1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>3</span><span>）如果是退格键，从字符栈中弹出一个字符，显示字符栈中的所有字符；继续执行</span><span>1</span><span>；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>4</span><span>）如果是</span><span>Enter</span><span>键，向字符栈中压入</span><span>0</span><span>，返回。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>从程序的处理过程中可以看出，字符栈的入栈、出栈和显示栈中的内容，是需要在多处使用的功能，应该将它们写为子程序。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>子程序：字符栈的入栈、出栈和显示。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>参数说明：</span><span>(ah)=</span><span>功能号，</span><span>0</span><span>表示入栈，</span><span>1</span><span>表示出栈，</span><span>2</span><span>表示显示；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ds:si</span><span>指向字符栈空间；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>对于</span><span>0</span><span>号功能：</span><span>(al)=</span><span>入栈字符；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>对于</span><span>1</span><span>号功能：</span><span>(al)=</span><span>返回字符；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>对于</span><span>2</span><span>号功能：</span><span>(dh)</span><span>、</span><span>(dl)=</span><span>字符串在屏幕上显示的行、列位置。</span></p>
<p>&nbsp;</p>
<p><span>charstack:<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jmp short charstart</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>table dw charpush,charpop,charshow</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>top&nbsp;dw 0<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>栈顶</span></p>
<p>&nbsp;</p>
<p><span>&nbsp;charstart:<span>&nbsp;&nbsp;&nbsp; </span>push bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push dx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push di</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push es</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cmp ah,2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ja sret</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bl,ah</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bh,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add bx,bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jmp word ptr table[bx]</span></p>
<p>&nbsp;</p>
<p><span>&nbsp;charpush:<span>&nbsp;&nbsp;&nbsp; </span>mov bx,top</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov [si][bx],al</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>inc top</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jmp sret</span></p>
<p>&nbsp;</p>
<p><span>&nbsp;charpop:<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>cmp top,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>je sret</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>dec top</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bx,top</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov al,[si][bx]</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jmp sret</span></p>
<p>&nbsp;</p>
<p><span>&nbsp;charshow:<span>&nbsp;&nbsp; </span>mov bx,0b800b</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov al,160</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ah,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mul dh</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov di,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add dl,dl</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov dh,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add di,dx</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bx,0</span></p>
<p>&nbsp;</p>
<p><span>&nbsp;charshows:<span> </span>cmp bx,top</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jne noempty</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov byte ptr es:[dl],&#8217; &#8216;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jmp sret</span></p>
<p><span>&nbsp;noempty:<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>mov al,[si][bx]</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es:[di],al</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov byte ptr es:[di+2], &#8216; &#8216;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>inc bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add di,2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jmp charshows</span></p>
<p><span>sret:<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop es</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop di</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop dx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p>&nbsp;</p>
<p><span>显示栈中字符的时候，要注意清除屏幕上上一次显示的内容。</span></p>
<p>&nbsp;</p>
<p><span>完整的接收字符串输入的子程序：</span></p>
<p><span>getstr:<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>push ax</span></p>
<p><span>getstrs:<span>&nbsp;&nbsp;&nbsp; </span>mov ah,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 16h</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cmp al,20h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jb nochar<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;ASCII</span><span>码小于</span><span>0</span><span>，说明不是字符</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ah,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>call charstack<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>字符入栈</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ah,2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>call charstack<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>显示栈中的字符</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jmp getstrs</span></p>
<p><span>&nbsp;nochar:cmp ah,0eh<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>退格键的扫描码</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>je backspace</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cmp ah,1ch<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>回车键的扫描码</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>je enter</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jmp getstrs</span></p>
<p><span>backspace:mov ah,1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>call charstack<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>字符出栈</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ah,2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>call charstack<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>显示栈中的字符</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jmp getstrs</span></p>
<p><span>&nbsp;enter:<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>mov al,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ah,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>call charstack<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;0</span><span>入栈</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ah,2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>call charstack <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>显示栈中的字符</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>应用</span><span>int 13h</span></strong><strong><span>中断例程对磁盘进行读写</span></strong></p>
<p><span>BIOS</span><span>提供的访问磁盘的中断例程为</span><span>int 13h</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>入口参数：</span></p>
<p><span>(ah)=int 13h</span><span>的功能号（</span><span>2</span><span>表示读扇区、</span><span>3</span><span>表示写扇区）</span></p>
<p><span>(al)=</span><span>读取的扇区数</span><span>/</span><span>写入的扇区数</span></p>
<p><span>(ch)=</span><span>磁道号</span></p>
<p><span>(cl)=</span><span>扇区号</span></p>
<p><span>(dh)=</span><span>磁头号（对于软盘即面号，因为一个面用一个磁头来读写。）</span></p>
<p><span>(dl)=</span><span>驱动器号</span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>软驱从</span><span>0</span><span>开始，</span><span>0</span><span>：软驱</span><span>A</span><span>，</span><span>1</span><span>：软驱</span><span>B</span><span>；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>硬盘从</span><span>80h</span><span>开始，</span><span>80h</span><span>：硬盘</span><span>C</span><span>，</span><span>81h</span><span>：硬盘</span><span>D</span><span>。</span></p>
<p><span>es:bx</span><span>指向接收从扇区读入数据的内存区</span><span>/</span><span>将写入磁盘的数据</span></p>
<p>&nbsp;</p>
<p><span>返回参数：</span></p>
<p><span>操作成功：</span><span>(ah)=0</span><span>，</span><span>(al)=</span><span>读入的扇区数</span><span>/</span><span>写入的扇区数</span></p>
<p><span>操作失败：</span><span>(ah)=</span><span>出错代码</span></p>
<p>&nbsp;</p>
<p><span>以下指令，读取</span><span>0</span><span>面</span><span>0</span><span>道</span><span>1</span><span>扇区的内容到内存单元</span><span>0:200</span><span>：</span></p>
<p><span>mov ax,0</span></p>
<p><span>mov es,ax</span></p>
<p><span>mov bx,200h</span></p>
<p>&nbsp;</p>
<p><span>mov al,1</span></p>
<p><span>mov ch,0</span></p>
<p><span>mov cl,1</span></p>
<p><span>mov dl,0</span></p>
<p><span>mov dh,0</span></p>
<p><span>mov ah,2</span></p>
<p><span>int 13h</span></p>
<p>&nbsp;</p>
<p><span>以下指令，将</span><span>0:200</span><span>中的内容写入</span><span>0</span><span>面</span><span>0</span><span>道</span><span>1</span><span>扇区：</span></p>
<p><span>mov ax,0</span></p>
<p><span>mov es,ax</span></p>
<p><span>mov bx,200h</span></p>
<p>&nbsp;</p>
<p><span>mov al,1</span></p>
<p><span>mov ch,0</span></p>
<p><span>mov cl,1</span></p>
<p><span>mov dl,0</span></p>
<p><span>mov dh,0</span></p>
<p><span>mov ah,3</span></p>
<p><span>int 13h</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>PC</span></strong><strong><span>机是如何进入操作系统的？</span></strong></p>
<p><span>开机后，</span><span>CPU</span><span>自动进入到</span><span>FFFF:0</span><span>单元处执行，此处有一条转跳指令。</span><span>CPU</span><span>执行该指令后，转去执行</span><span>BIOS</span><span>中的硬件系统检测和初始化程序。</span></p>
<p>&nbsp;</p>
<p><span>初始化程序将建立</span><span>BIOS</span><span>所支持的中断向量，即将</span><span>BIOS</span><span>提供的中断例程的入口地址登记在中断向量表中。</span></p>
<p>&nbsp;</p>
<p><span>硬件系统检测和初始化完成后，调用</span><span>int 19h</span><span>进行操作系统的引导。</span></p>
<p><span>如果设为从软盘启动操作系统，则</span><span>int 19h</span><span>将主要完成以下工作：</span></p>
<p><span>1</span><span>）控制</span><span>0</span><span>号软驱，读取软盘</span><span>0</span><span>道</span><span>0</span><span>面</span><span>1</span><span>扇区的内容到</span><span>0:<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="7" UnitName="C">7c</st1:chmetcnv>00</span><span>。</span></p>
<p><span>2</span><span>）将</span><span>CS:IP</span><span>指向</span><span>0:<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="7" UnitName="C">7c</st1:chmetcnv>00</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>软盘的</span><span>0</span><span>道</span><span>0</span><span>面</span><span>1</span><span>扇区中装有操作系统引导程序。</span><span>int 19h</span><span>将其装到</span><span>0:<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="7" UnitName="C">7c</st1:chmetcnv>00</span><span>处后，设置</span><span>CPU</span><span>从</span><span>0:<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="7" UnitName="C">7c</st1:chmetcnv>00</span><span>开始执行此处的引导程序，操作系统被激活，控制计算机。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/122220.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-04 16:50 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/04/122220.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编语言--直接定址表</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/04/122200.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Wed, 04 Aug 2010 05:56:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/04/122200.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/122200.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/04/122200.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/122200.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/122200.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>直接定址表</span></p>
<p><span>如何有效合理地组织数据，以及相关的编程技术。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>描述了单元长度的标号</span></strong></p>
<p><span>assume cs:code</span></p>
<p><strong><span>code</span></strong><span> segment</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span>a</span></strong>:db 1,2,3,4,5,6,7,8</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span>b</span></strong>:dw 0</span></p>
<p>&nbsp;</p>
<p><span>&nbsp;<strong><span>start</span></strong>:<span>&nbsp;&nbsp; </span>mov si,offset a</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bx, offset b</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cx,8</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span>s</span></strong>:<span>&nbsp;&nbsp; </span>mov al,cs:[si]</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ah,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add cs:[bx],ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>inc si</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>loop s</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="4" UnitName="C">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p><span>程序中，</span><span>code</span><span>、</span><span>a</span><span>、</span><span>b</span><span>、</span><span>start</span><span>、</span><span>s</span><span>都是标号。这些标号仅仅表示了<strong><span>内存单元的地址</span></strong>。</span></p>
<p>&nbsp;</p>
<p><span>我们还可以使用一种标号，这种标号不但表示内存单元的地址，还表示了内存单元的长度，即表示在此标号处的单元，是一个字节单元，还是字单元，还是双字单元。</span></p>
<p><span>比如：</span></p>
<p><span>assume cs:code</span></p>
<p><span>code segment</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span>a</span></strong>&nbsp;db 1,2,3,4,5,6,7,8</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span>b</span></strong>&nbsp;dw 0</span></p>
<p>&nbsp;</p>
<p><span>&nbsp;start:<span>&nbsp;&nbsp;&nbsp; </span>mov si,offset a</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bx, offset b</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cx,8</span></p>
<p><span>&nbsp;<span>&nbsp;&nbsp;&nbsp;</span>s:<span>&nbsp;&nbsp; </span>mov al,cs:[si]</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ah,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add cs:[bx],ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>inc si</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>loop s</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="4" UnitName="C">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p><span>我们在</span><span>code</span><span>段中使用的标号</span><span>a</span><span>、</span><span>b</span><span>后面没有&#8220;</span><span>:</span><span>&#8221;，它们是同时描述内存地址和单元长度的标号。</span></p>
<p><span>标号</span><span>a</span><span>，描述了地址</span><span>code:0</span><span>，和这个地址开始，以后的内存单元都是字节单元；</span></p>
<p><span>而标号</span><span>b</span><span>描述了地址</span><span>code:8</span><span>，和这个地址开始，以后的内存单元都是字单元。</span></p>
<p>&nbsp;</p>
<p><span>因为这种标号包含了对单元长度的描述，所以，在指令中，它可以代表一个段中的内存单元。比如，对于程序中的</span><span>b dw 0</span><span>。</span></p>
<p><span>指令：</span><span>mov ax,b</span></p>
<p><span>相当于：</span><span>mov ax,cs:[8]</span></p>
<p>&nbsp;</p>
<p><span>指令：</span><span>mob b,2</span></p>
<p><span>相当于：</span><span>mov word ptr cs:[8],2</span></p>
<p>&nbsp;</p>
<p><span>指令：</span><span>inc b</span></p>
<p><span>相当于：</span><span>inc word ptr cs:[8]</span></p>
<p>&nbsp;</p>
<p><span>在这些指令中，标号</span><span>b</span><span>代表了一个内存单元，地址为</span><span>code:8</span><span>，长度为</span><span>2</span><span>字节。</span></p>
<p>&nbsp;</p>
<p><span>下面的指令会引起编译错误：</span></p>
<p><span>mov al,b</span></p>
<p><span>因为</span><span>b</span><span>代表的内存单元是字单元，而</span><span>al</span><span>是</span><span>8</span><span>位寄存器。</span></p>
<p>&nbsp;</p>
<p><span>如果我们将程序中的指令：</span><span>add b,ax</span><span>，写为</span><span>add b,al</span><span>，将出现同样的编译错误。</span></p>
<p>&nbsp;</p>
<p><span>对于程序中的</span><span>a db 1,2,3,4,5,6,7,8</span><span>：</span></p>
<p><span>指令：</span><span>mov al,a[si]</span></p>
<p><span>相当于：</span><span>mov al,cs:0[si]<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>这种语法比较像高级语言中的数组语法。</span></p>
<p><span>相当于：</span><span>mov al,cs:[si+0]</span></p>
<p><span>相当于：</span><span>mov al,cs:[si].0</span></p>
<p>&nbsp;</p>
<p><span>指令：</span><span>mov al,a[3]</span></p>
<p><span>相当于：</span><span>mov al,cs:0[3]</span></p>
<p>&nbsp;</p>
<p><span>指令：</span><span>mov al,a[bx+si+3]</span></p>
<p><span>相当于：</span><span>mov al,cs:0[bx+si+3]</span></p>
<p>&nbsp;</p>
<p><span>可见，使用这种包含单元长度的标号，可以使我们以简洁的形式访问内存中的数据。</span></p>
<p>&nbsp;</p>
<p><span>以后，我们将这种标号称为<strong><span>数据标号</span></strong>，它标记了存储数据的单元的地址和长度。它不同于仅仅表示地址的地址标号。</span></p>
<p>&nbsp;</p>
<p><span>检测点</span><span>16.1</span></p>
<p><span>下面的程序将</span><span>code</span><span>段中</span><span>a</span><span>处的</span><span>8</span><span>个数据累加，结果存储到</span><span>b</span><span>处的</span><span>dword</span><span>中，补全程序。</span></p>
<p><span>assume cs:code</span></p>
<p><span>code segment</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>a dw 1,2,3,4,5,6,7,8</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>b dd 0</span></p>
<p><span>&nbsp;start:<span>&nbsp;&nbsp;&nbsp; </span>mov si,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cs,8</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;s:<span> </span>mov ax,<u>&nbsp;a[si]&nbsp;</u></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add <u><span>&nbsp;&nbsp;</span>word ptr b&nbsp;</u>,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>adc <u><span>&nbsp;&nbsp;</span>word ptr b[2]&nbsp;</u>,0<span>&nbsp;&nbsp; </span>;</span><span>双字单元的高字单元用来存放进位数值。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add si,<u> 2 &nbsp;</u><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>;</span><span>内存单元为字节单元。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>loop s </span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="4" UnitName="C">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>在其他段中使用数据标号</span></strong></p>
<p><span>一般来说，我们不在代码段中定义数据，而是将数据定义到其他段中。在其他段中，我们也可以使用数据标号来<span>描述存储数据的单元的地址和长度</span>。</span></p>
<p>&nbsp;</p>
<p><span>注意：在后面加有&#8220;</span><span>:</span><span>&#8221;的地址标号，只能在代码段中使用，不能在其他段中使用。</span></p>
<p>&nbsp;</p>
<p><span>下面的程序将</span><span>data</span><span>段中</span><span>a</span><span>标号处的</span><span>8</span><span>个数据累加，结果存储到</span><span>b</span><span>标号处的字中。</span></p>
<p><span>assume cs:code, <strong><span>ds:data</span></strong></span></p>
<p><span>data segment</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>a db 1,2,3,4,5,6,7,8</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>b dw 0</span></p>
<p><span>data ends</span></p>
<p>&nbsp;</p>
<p><span>code segment</span></p>
<p><span>&nbsp;start:<span>&nbsp;&nbsp;&nbsp; </span><strong><span>mov ax,data</span></strong></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span>mov ds,ax</span></strong></span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov si,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ex,8</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;s:<span> </span>mov al,<strong><span>a[si]</span></strong></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ah,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span>add b,ax</span></strong></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>inc si</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>loop s</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov <st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="4" UnitName="C">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p><span>注意，如果想在代码段中，直接用数据标号访问数据，则需要用伪指令</span><span>assume</span><span>将标号所在的段和一个段寄存器联系起来。否则编译器在编译的时候，无法确定标号的段地</span> <span>在哪一个寄存器中。当然，这种联系是编译器需要的，但绝对不是说，我们因编译器的工作需要，用</span><span>assume</span><span>指令将段寄存器和某个段相联系，段寄存器中就会真的存放该段的地址。我们在程序中还需要使用指令对段寄存器进行设置。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>比如：在上面的程序中，我们要在代码段</span><span>code</span><span>中用</span><span>data</span><span>段中的数据标号</span><span>a</span><span>、</span><span>b</span><span>访问数据，则必须用</span><span>assume</span><span>将一个寄存器和</span><span>data</span><span>段相联。在程序中，我们用</span><span>ds</span><span>寄存器和</span><span>data</span><span>段相联，则编译器对相关指令的编译如下：</span></p>
<p><span>指令：</span><span>mov al,a[si]</span></p>
<p><span>编译为：</span><span>mov al,[si+0]</span></p>
<p>&nbsp;</p>
<p><span>指令：</span><span>add b,ax</span></p>
<p><span>编译为：</span><span>add [8],ax</span></p>
<p>&nbsp;</p>
<p><span>因为这些实际编译出的指令，都默认所访问单元的段地址在</span><span>ds</span><span>中，而实际要访问的段为</span><span>data</span><span>，所以，若要访问正确，在这些指令执行前，</span><span>ds</span><span>中必须为</span><span>data</span><span>段的段地址。则，我们在程序中使用指令：</span></p>
<p><span>mov ax,data</span></p>
<p><span>mov ds,ax</span></p>
<p><span>设置</span><span>ds</span><span>指向</span><span>data</span><span>段。</span></p>
<p>&nbsp;</p>
<p><span>可以将标号当作数据来定义，此时，编译器将标号所表示的地址当作数据的值。比如：</span></p>
<p><span>data segment</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>a db 1,2,3,4,5,6,7,8</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>b dw 0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>c dw a,b</span></p>
<p><span>data ends</span></p>
<p><span>数据标号</span><span>c</span><span>处存储的两个字型数据为标号</span><span>a</span><span>、</span><span>b</span><span>的偏移地址。相当于：</span></p>
<p><span>data segment</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>a db 1,2,3,4,5,6,7,8</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>b dw 0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>c dw offset a, offset b</span></p>
<p><span>data ends</span></p>
<p>&nbsp;</p>
<p><span>再比如：</span></p>
<p><span>data segment</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>a db 1,2,3,4,5,6,7,8</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>b dw 0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>c dd a,b</span></p>
<p><span>data ends</span></p>
<p><span>数据标号</span><span>c</span><span>处存储的两个双字型数据为标号</span><span>a</span><span>的偏移地址和段地址、标号</span><span>b</span><span>的偏移地址和段地址。相当于：</span></p>
<p><span>data segment</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>a db 1,2,3,4,5,6,7,8</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>b dw 0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>c dw offset a, seg a, offset b, seg b</span></p>
<p><span>data ends</span></p>
<p><span>seg</span><span>操作符，功能为取得某一标号的段地址。</span></p>
<p>&nbsp;</p>
<p><span>检测点</span><span>16.2</span></p>
<p><span>下面的程序将</span><span>data</span><span>段中</span><span>a</span><span>处的</span><span>8</span><span>个数据累加，结果存储到</span><span>b</span><span>处的字中。补全程序。</span></p>
<p><span>assume cs:code,es:data</span></p>
<p><span>data segment</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>a db 1,2,3,4,5,6,7,8</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>b dw 0</span></p>
<p><span>data ends</span></p>
<p><span>code segment</span></p>
<p><span>&nbsp;start:<span>&nbsp;&nbsp;&nbsp; </span>mov ax,data</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ds,ax</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov si,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cx,8</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;s:<span> </span>mov al,a[si]</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ah,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add b,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>inc si</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>loop s</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="4" UnitName="C">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>直接定址表</span></strong></p>
<p><span>现在，我们讨论用查表的方法编写相关程序的技巧。</span></p>
<p><span>编写子程序，以十六进制的形式在屏幕中间显示给定的</span><span>byte</span><span>型数据。</span></p>
<p><span>分析：一个字节需要用两个十六进制数码来表示，所以，子程序需要在屏幕上显示两个</span><span>ASCII</span><span>字符。用&#8220;</span><span>0</span><span>&#8221;、&#8220;</span><span>1</span><span>&#8221;、&#8220;</span><span>2</span><span>&#8221;、&#8220;</span><span>3</span><span>&#8221;、&#8220;</span><span>4</span><span>&#8221;、&#8220;</span><span>5</span><span>&#8221;、&#8220;</span><span>6</span><span>&#8221;、&#8220;</span><span>7</span><span>&#8221;、&#8220;</span><span>8</span><span>&#8221;、&#8220;</span><span>9</span><span>&#8221;、&#8220;</span><span>A</span><span>&#8221;、&#8220;</span><span>B</span><span>&#8221;、&#8220;</span><span>C</span><span>&#8221;、&#8220;</span><span>D</span><span>&#8221;、&#8220;</span><span>E</span><span>&#8221;、&#8220;</span><span>F</span><span>&#8221;这</span><span>16</span><span>个字符来显示十六进制数码。</span></p>
<p><span>我们可以将一个</span><span>byte</span><span>的高</span><span>4</span><span>位和低</span><span>4</span><span>位分开，分别用它们的值得到对应的数码字符。比如</span><span>2Bh</span><span>，我们可以得到高</span><span>4</span><span>位的值为</span><span>2</span><span>，低</span><span>4</span><span>位的值为</span><span>11</span><span>，那么我们如何用这两个数值得到对应的数码字符&#8220;</span><span>2</span><span>&#8221;和&#8220;</span><span>B</span><span>&#8221;呢？</span></p>
<p>&nbsp;</p>
<p><span>最简单的办法就是一个一个地比较，如下：</span></p>
<p><span>如果数值为</span><span>0</span><span>，则显示&#8220;</span><span>0</span><span>&#8221;；</span></p>
<p><span>如果数值为</span><span>1</span><span>，则显示&#8220;</span><span>1</span><span>&#8221;；</span></p>
<p><span>&#8230;</span></p>
<p><span>如果数值为</span><span>11</span><span>，则显示&#8220;</span><span>B</span><span>&#8221;；</span></p>
<p><span>&#8230;</span></p>
<p>&nbsp;</p>
<p><span>这样做，程序中要使用多条比较、转移指令。程序将比较长，混乱。</span></p>
<p>&nbsp;</p>
<p><span>显示，我们希望能够在数值</span><span>0~15</span><span>和字符&#8220;</span><span>0</span><span>&#8221;</span><span>~</span><span>&#8220;</span><span>F</span><span>&#8221;之间找到一种映射关系。这样我们用</span><span>0~15</span><span>间的任何数值，都可以通过这种映射关系直接得到&#8220;</span><span>0</span><span>&#8221;</span><span>~</span><span>&#8220;</span><span>F</span><span>&#8221;中对应的字符。</span></p>
<p>&nbsp;</p>
<p><span>数值</span><span>0~9</span><span>和字符&#8220;</span><st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="0" UnitName="&#8221;"><span>0</span><span>&#8221;</span></st1:chmetcnv><span>~</span><span>&#8220;</span><st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="9" UnitName="&#8221;"><span>9</span><span>&#8221;</span></st1:chmetcnv><span>之间的映射关系是很明显的，即：</span></p>
<p><span>数值</span><span>+30h = </span><span>对应字符的</span><span>ASCII</span><span>值。</span></p>
<p>&nbsp;</p>
<p><span>但是，</span><span>10~15</span><span>和&#8220;</span><span>A</span><span>&#8221;</span><span>~</span><span>&#8220;</span><span>F</span><span>&#8221;之间的映射关系是：</span></p>
<p><span>数值</span><span>+37h = </span><span>对应字符的</span><span>ASCII</span><span>值。</span></p>
<p>&nbsp;</p>
<p><span>可见，我们可以利用数值和字符之间的这种原本存在的映射关系，通过高</span><span>4</span><span>位和低</span><span>4</span><span>位值得到对应的字符码。但是由于映射关系的不同，我们在程序中必须进行一些比较，对于大于</span><span>9</span><span>的数值，我们要用不同的计算方法。</span></p>
<p>&nbsp;</p>
<p><span>这样做，虽然使程序得到了简化。但是，如果我们希望用更简捷的算法，就要考虑用同一种映射关系从数值得到字符码。所以，我们就不能利用</span><span>0~9</span><span>和&#8220;</span><span>0</span><span>&#8221;</span><span>~</span><span>&#8220;</span><span>9</span><span>&#8221;之间与</span><span>10~15</span><span>和&#8220;</span><span>A</span><span>&#8221;</span><span>~</span><span>&#8220;</span><span>F</span><span>&#8221;之间原有的映射关系。</span></p>
<p>&nbsp;</p>
<p><span>因为数值</span><span>0~15</span><span>和字符&#8220;</span><span>0</span><span>&#8221;</span><span>~</span><span>&#8220;</span><span>F</span><span>&#8221;之间没有一致的映射关系存在，所以，<span>我们应该在它们之间建立新的映射关系。</span></span></p>
<p>&nbsp;</p>
<p><span>具体的做法是，我们建立一张表，表中依次存储字符&#8220;</span><span>0</span><span>&#8221;</span><span>~</span><span>&#8220;</span><span>F</span><span>&#8221;，我们可以通过数值</span><span>0~15</span><span>直接查找到对应的字符。</span></p>
<p>&nbsp;</p>
<p><span>子程序如下：</span></p>
<p><span>;</span><span>用</span><span>al</span><span>传送要显示的数据</span></p>
<p><span>showtyte:<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jmp short show</span></p>
<p><span>&nbsp;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>table db &#8216;0123456789ABCDEF&#8217;<span>&nbsp;&nbsp;&nbsp; </span>;</span><span>字符表</span></p>
<p><span>&nbsp;show:&nbsp;push bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push es</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ah,al</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>shr ah,1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>shr ah,1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>shr ah,1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>shr ah,1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>右移</span><span>4</span><span>位，</span><span>ah</span><span>中得到高</span><span>4</span><span>位的值。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>and al,00001111b<span>&nbsp;&nbsp;&nbsp; </span>;al</span><span>中为低</span><span>4</span><span>位的值</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bl,ah</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bh,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ah,table[bx]<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>用高</span><span>4</span><span>位的值作为相对于</span><span>table</span><span>的偏移，取得对应的字符。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bx,0b800h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es:[160*12+40*2],ah<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>显示高</span><span>4</span><span>位的十六进制字符码</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bl,al</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bh,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov al,table[bx]<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>用低</span><span>4</span><span>位的值作为相对于</span><span>table</span><span>的偏移，对得对应的字符。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es:[160*12+40*2+2],al&nbsp;;</span><span>显示低</span><span>4</span><span>位的十六进制字符码</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop es</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop bx</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p>&nbsp;</p>
<p><span>可以看出，在子程序中，我们在数值</span><span>0~15</span><span>和字符&#8220;</span><span>0</span><span>&#8221;</span><span>~</span><span>&#8220;</span><span>F</span><span>&#8221;之间建立的映射关系为：</span></p>
<p><span>以数值</span><span>N</span><span>为</span><span>table</span><span>青史的偏移，可以找到对应的字符。</span></p>
<p>&nbsp;</p>
<p><strong><span>利用表，在两个数据集合之间建立一种映射关系，使我们可以用查表现方法根据给出的数据得到其在另一个集合中的对应数据。这样做的目的一般来说有三个：</span></strong></p>
<p><strong><span>1</span></strong><strong><span>）为了算法的清晰和简洁；</span></strong></p>
<p><strong><span>2</span></strong><strong><span>）为了加快运算速度；</span></strong></p>
<p><strong><span>3</span></strong><strong><span>）为了使程序易于扩充。</span></strong></p>
<p>&nbsp;</p>
<p><span>在上面的子程序中，我们更多的是为了算法的清晰和简洁，而采用了查表的方法。</span></p>
<p>&nbsp;</p>
<p><span>编程的时候要注意程序的容错性，即对于错误的输入要有处理能力。</span></p>
<p>&nbsp;</p>
<p><span>上面的例子，我们通给出的数据进行计算或比较而得到结果的问题，转化为用给出的数据作为查表的依据，通过查表得到结果的问题。具体的查表方法，是用查表的依据数据，直接计算出所要查找的元素在表中的位置。像这种可以通过依据数据，直接计算出所要找的元素的位置的表，我们称其为：<strong>直接定址表</strong>。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>程序入口地址的直接定址表</span></strong></p>
<p><span>我们可以在直接定址表中存储子程序的地址，从而方便地实现不同子程序的调用。</span></p>
<p><span>实现一个子程序</span><span>setscreen</span><span>，为显示输出提供如下功能：</span></p>
<p><span>1</span><span>）清屏；</span></p>
<p><span>2</span><span>）设置前景色；</span></p>
<p><span>3</span><span>）设置背景色；</span></p>
<p><span>4</span><span>）向上滚动一行。</span></p>
<p>&nbsp;</p>
<p><span>入口参数说明：</span></p>
<p><span>1</span><span>）用</span><span>ah</span><span>寄存器传递功能号：</span><span>0</span><span>表示清屏，</span><span>1</span><span>表示设置前景色，</span><span>2</span><span>表示设置背景色，</span><span>3</span><span>表示向上滚动一行；</span></p>
<p><span>2</span><span>）对于</span><span>2</span><span>、</span><span>3</span><span>号功能，用</span><span>al</span><span>传送颜色值，</span><span>(al)</span><span>&#8712;</span><span>{0,1,2,3,4,5,6,7}</span></p>
<p>&nbsp;</p>
<p><span>下面，讨论一下各种功能如何实现：</span></p>
<p><span>1</span><span>）清屏：将显存中当前屏幕中的字符设为空格符；</span></p>
<p><span>2</span><span>）设置前景色：设置显存中当前屏幕中处于奇地址的属性字节的第</span><span>0</span><span>、</span><span>1</span><span>、</span><span>2</span><span>位；</span></p>
<p><span>3</span><span>）设置背景色：设置显存中当前屏幕中处于奇地址的属性字节的第</span><span>4</span><span>、</span><span>5</span><span>、</span><span>6</span><span>位；</span></p>
<p><span>4</span><span>）向上滚动一行：依次将第</span><span>n+1</span><span>行的内容复制到第</span><span>n</span><span>行处，最后一行为空。</span></p>
<p>&nbsp;</p>
<p><span>我们将这</span><span>4</span><span>个功能分别写为</span><span>4</span><span>个子程序：</span></p>
<p><span>;</span><span>清屏</span></p>
<p><span>sub1:<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push cx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push es</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bx,0b800h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bx,0</span></p>
<p><span>mov cx,2000</span></p>
<p><span>sub1s:&nbsp;mov byte ptr es:[bx],&#8217; &#8216;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add bx,2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>loop sub1s</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop es</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop cx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p>&nbsp;</p>
<p><span>;</span><span>设置前景色</span></p>
<p><span>sub2:<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push cx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push es</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bx,0b800h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bx,1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cx,2000</span></p>
<p><span>&nbsp;sub2s:&nbsp;and byte ptr es:[bx],11111000b</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>or es:[bx],al</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add bx,2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>loop sub2s</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop es</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop cx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p>&nbsp;</p>
<p><span>;</span><span>设置背景色</span></p>
<p><span>sub3:<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push cx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push es</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cl,4</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>shr al,cl</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bx,0b800h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bx,1</span></p>
<p><span>&nbsp;sub3s:&nbsp;and byte ptr es:[bx],10001111b</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>shl al,1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>shl al,1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>shl al,1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>shl al,1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>or es:[bx],al</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add bx,2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>loop sub3s</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop es</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop cx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p>&nbsp;</p>
<p><span>;</span><span>向上滚动一行</span></p>
<p><span>sub4:<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push cx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push si</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push di</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push es</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push ds</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov si,0b800h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,si</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ds,si</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov si,160<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;ds:si</span><span>指向第</span><span>n+1</span><span>行</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov di,0<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;es:di</span><span>指向第</span><span>n</span><span>行</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cld</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cx,24</span></p>
<p><span>&nbsp;sub4s:&nbsp;push cx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cx,160<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>一行的长度为</span><span>160</span><span>个字节。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>rep movsb<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>一次复制一行</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop cx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>loop sub4s</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cx,80</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov si,0</span></p>
<p><span>&nbsp;sub4s1:mov byte ptr [160*24+si],&#8217; &#8216;<span>&nbsp;&nbsp; </span>;</span><span>最后一行清空</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add si,2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>loop sub4s1</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop ds</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop es</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop di</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop si</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop cx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p>&nbsp;</p>
<p><span>我们可以将这些功能子程序的入口地址存储在一个表中，它们在表中的位置和功能号相对应。对应关系为：功能号</span><span>*2=</span><span>对应的功能子程序在地址表中的偏移。程序如下：</span></p>
<p><span>setscreen:<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jmp short set</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>table: dw sub1,sub2,sub3,sub4</span></p>
<p><span>set: push bx</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cmp ah,3<span>&nbsp;&nbsp;&nbsp; </span>;</span><span>判断功能号是否大于</span><span>3</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ja sret</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bl,ah</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bh,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add bx,bx<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>根据</span><span>ah</span><span>中的功能计算对应子程序在</span><span>table</span><span>表中的偏移</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>call word ptr table[bx]<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>调用对应的功能子程序</span></p>
<p>&nbsp;</p>
<p><span>sret:<span> </span>pop bx</span></p>
<p><span>ret</span></p>
<p>&nbsp;</p>
<p><span>当然，我们也可以将子程序</span><span>setscreen</span><span>如下实现：</span></p>
<p><span>setscreen:<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cmp ah,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>je do1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cmp ah,1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>je do2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cmp ah,2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>je do3</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cmp ah,3</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>je do4</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jmp short sret</span></p>
<p>&nbsp;</p>
<p><span>&nbsp;do1:<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>call sub1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jmp short sret</span></p>
<p><span>&nbsp;do2:<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>call sub2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jmp short sret</span></p>
<p><span>&nbsp;do3:<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>call sub3</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jmp short sret</span></p>
<p><span>&nbsp;do4:<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>call sub4</span></p>
<p><span>&nbsp;sret:<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>显然，用通过比较功能号进行转移的方法，程序结构比较混乱，不得功能的扩充。比如说，在</span><span>setscreen</span><span>中再加入一个功能，则需要修改程序的逻辑，加入新的比较、转移指令。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>用根据功能号查找地址表的方法，程序的结构清晰，便于扩充。如果加入一个新的功能子程序，那么只需要在地址表中加入它的入口地址就可以了。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/122200.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-04 13:56 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/04/122200.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编语言--外中断</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/04/122176.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Wed, 04 Aug 2010 02:38:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/04/122176.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/122176.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/04/122176.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/122176.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/122176.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>外中断</span></p>
<p>&nbsp;</p>
<p><span>以前我们讨论的都是</span><span>CPU</span><span>对指令的执行。</span></p>
<p><span>我们知道，</span><span>CPU</span><span>在计算机系统中，除了能够执行指令，进行运算以外，还应该能够对外部设备进行控制，接收它们的输入，向它们进行输出。也就是说，</span><span>CPU</span><span>除了有运算能力外，还要有</span><span>I/O</span><span>（</span><span>Input/Output</span><span>，输入</span><span>/</span><span>输出）能力。</span></p>
<p><span>比如，我们按下键盘上的确个键，</span><span>CPU</span><span>最终要能够处理这个键。在使用文本编辑器时，按下</span><span>a</span><span>键后，我们可以看到屏幕上出现&#8220;</span><span>a</span><span>&#8221;，是</span><span>CPU</span><span>将从键盘上输入的键所对应的字符送到显示上的。</span></p>
<p>&nbsp;</p>
<p><span>要及时处理外设的输入，显然需要解决两个问题：</span></p>
<p><span>一是外设的输入随时可能发生，</span><span>CPU</span><span>如何得知？</span></p>
<p><span>二是</span><span>CPU</span><span>从何处得到外设的输入？</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>接口芯片和端口</span></strong></p>
<p><span>在前面我们知道，<span>在</span></span><span>PC</span><span>系统的接口卡和主板上，装有各种接口芯片。</span></p>
<p><span>这些外设接口芯片的内部有若干寄存器，</span><span>CPU</span><span>将这些寄存器当作端口来访问。</span></p>
<p>&nbsp;</p>
<p><span>外设的输入不直接送入内存和</span><span>CPU</span><span>，而是送入相关的接口芯片的端口中；</span></p>
<p><span>CPU</span><span>向外设的输出也不是直接送入外设，而是先送入端口中，再由相关的芯片送到外设。</span></p>
<p><span>CPU</span><span>还可以向外设输出控制命令，而这些控制命令也是先送到相关芯片的端口中，然后再由相关的芯片根据命令对外设实施控制。</span></p>
<p>&nbsp;</p>
<p><span>可见，</span><span>CPU</span><span>通过端口和外部设备进行联系</span><span>。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>外中断信息</span></p>
<p><span>现在，我们知道了外设的输入被存放在端口中，可是外设的输入随时都有可能到达，</span><span>CPU</span><span>如何及时地知道，并进行处理呢？更一般地讲，就是外设随时都可能发生需要</span><span>CPU</span><span>及时处理的事件，</span><span>CPU</span><span>如何及时得知并进行处理？</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>CPU</span><span>提供中断机制来满足这种需要。前面讲过，当</span><span>CPU</span><span>的内部有需要处理的事情发生的时候，将产生中断信息，引发中断过程。这种中断信息来自</span><span>CPU</span><span>的内部。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>还有一种中断信息，来自于</span><span>CPU</span><span>外部，当</span><span>CPU</span><span>外部有需要处理的事情发生的时候，比如说，外设的输入到达，相关芯片将向</span><span>CPU</span><span>发出相应的中断信息。</span><span>CPU</span><span>在执行完当前指令后，可以检测到发送过来的中断信息，引发中断过程，处理外设的输入。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>在</span><span>PC</span><span>系统中，外中断源一共有两类：</span></p>
<p><span>1</span><span>、可屏蔽中断</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>可屏蔽中断是</span><span>CPU</span><span>可以不响应的外中断，</span><span>CPU</span><span>是否响应可屏蔽中断，要看标志寄存器的</span><span>IF</span><span>位的设置。当</span><span>CPU</span><span>检测到可屏蔽中断信息时，如果</span><span>IF=1</span><span>，则</span><span>CPU</span><span>在执行完当前指令后响应中断，引发中断过程；如果</span><span>IF=0</span><span>，则不响应可屏蔽中断。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>回忆一下内中断所引发的中断过程：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>1</span><span>）取中断类型码</span><span>n</span><span>；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>2</span><span>）标志寄存器入栈，</span><span>IF=0</span><span>，</span><span>TF=0</span><span>；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>3</span><span>）</span><span>CS</span><span>、</span><span>IP</span><span>入栈；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>4</span><span>）</span><span>(IP)=(n*4)</span><span>，</span><span>(CS)=(n*4+2)</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>由此转去执行中断处理程序。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>可屏蔽中断所引发的中断过程，除在第</span><span>1</span><span>步的实现上有所不同外，基本上和内中断的中断过程相同。因为可屏蔽中断信息来自于</span><span>CPU</span><span>外部，中断类型码是通过数据总路线送入</span><span>CPU</span><span>的；而内中断的中断类型码是在</span><span>CPU</span><span>内部产生的。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>现在，我们可以解释中断过程中将</span><span>IF</span><span>设置为</span><span>0</span><span>的原因了。将</span><span>IF</span><span>置</span><span>0</span><span>的原因就是，在进入中断处理程序后，禁止其他的可屏蔽中断。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>当然，如果在中断处理程序中需要处理可屏蔽中断，可以用指令将</span><span>IF</span><span>置</span><span>1</span><span>。</span><span>8086CPU</span><span>提供的设置</span><span>IF</span><span>的指令如下：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sti</span><span>，用于设置</span><span>IF=1</span><span>；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cli</span><span>，用于设置</span><span>IF=0</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>2</span><span>、不可屏蔽中断</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>不可屏蔽中断是</span><span>CPU</span><span>必须响应的外中断。当</span><span>CPU</span><span>检测到不可屏蔽中断信息时，则在执行完当前指令后，立即响应，引发中断过程。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>对于</span><span>8086CPU</span><span>，不可屏蔽中断的中断类型码固定为</span><span>2</span><span>，所以中断过程中，不需要取中断类型码。则不可屏蔽中断的中断过程为：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（</span><span>1</span><span>）标志寄存器入栈，</span><span>IF=0</span><span>，</span><span>TF=0</span><span>；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（</span><span>2</span><span>）</span><span>CS</span><span>、</span><span>IP</span><span>入栈；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（</span><span>3</span><span>）</span><span>(IP)=(8)</span><span>，</span><span>(CS)=(0AH)</span><span>。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>几乎所有由外设引发的外中断，都是可屏蔽中断。当外设有需要处理的事件（比如说键盘输入）发生时，相关芯片向</span><span>CPU</span><span>发出可屏蔽中断信息。不可屏蔽中断是在系统中有必须处理的情况发生时用来通知</span><span>CPU</span><span>的中断信息。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>PC</span></strong><strong><span>机键盘的处理过程</span></strong></p>
<p><span>下面来看一下键盘输入的处理过程，并以此来体会一下</span><span>PC</span><span>机处理外设输入的基本方法。</span></p>
<p><span>1</span><span>、键盘输入</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>键盘上的每一个键相当于一个开关，键盘中有一个芯片对键盘上的每一个键的开关状态进行扫描。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>按下一个键时，开关接通，该芯片就产生一个扫描码，扫描码说明了按下的键在键盘上的位置。扫描码被送入主板上的相关接口芯片的寄存器中，该寄存器的端口地址为</span><span>60H</span><span>。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>松开按下的键时，也产生一个扫描码，扫描码说明了松开的键在键盘上的位置。松开按键时产生的扫描码也被送入</span><span>60H</span><span>端口中。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>一般将按下一个键时产生的扫描码称为通码，松开一个键产生的扫描码称为断码。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>扫描码长度为一个字节，通码的第</span><span>7</span><span>位为</span><span>0</span><span>，断码的第</span><span>7</span><span>位为</span><span>1</span><span>，即：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>断码</span><span>=</span><span>通码</span><span>+80H</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>比如：</span><span>g</span><span>键的通码为</span><span>22H</span><span>，断码为</span><span>a2H</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>2</span><span>、引发</span><span>9</span><span>号中断</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>键盘的输入到达</span><span>60H</span><span>端口时，相关的芯片就会向</span><span>CPU</span><span>发出中断类型码为</span><span>9</span><span>的可屏蔽中断信息。</span><span>CPU</span><span>检测到该中断信息后，如果</span><span>IF=1</span><span>，则响应中断，引发中断过程，转去执行</span><span>int 9</span><span>中断例程。</span></p>
<p>&nbsp;</p>
<p><span>3</span><span>、执行</span><span>int 9</span><span>中断例程</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>BIOS</span><span>提供了</span><span>int 9</span><span>中断例程，用来进行基本的键盘输入处理，主要的工作如下：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（</span><span>1</span><span>）读出</span><span>60H</span><span>端口中的扫描码；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（</span><span>2</span><span>）如果是<span>字符键</span>的扫描码，将<span>该扫描码和它所对应的字符码（即</span></span><span>ASCII</span><span>码）送入内存中的</span><span>BIOS</span><span>键盘缓冲区</span><span>；如果是控制键（比如</span><span>Ctrl</span><span>）和切换键（比如</span><span>CapsLock</span><span>）的扫描码，则将其转变为<span>状态字节</span>（用二进制位记录控制键和切换键状态的字节）写入内存中<span>存储状态字节的单元</span>。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（</span><span>3</span><span>）对键盘系统进行相关的控制，比如说，向相关芯片发出应答信息。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>BIOS</span></span><span>键盘缓冲区</span><span>是系统启动后，</span><span>BIOS</span><span>用于存放</span><span>int 9</span><span>中断例程所接收的键盘输入的<span>内存区</span>。该内存区可以存储</span><span>15</span><span>个键盘输入</span><span>，因为</span><span>int 9</span><span>中断例程除了接收扫描码外，还要产生和扫描码对应的<span>字符码</span>，所以在</span><span>BIOS</span><span>键盘缓冲区中，<span>一个键盘输入用一个字单元存放</span>，<span>高位字节存放扫描码，低位字节存放字符码</span>。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>0040:17</span><span>单元存储键盘状态字节，该字节记录了控制键和切换键的状态。键盘状态字节各位记录的信息如下：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>0</span><span>：右</span><span>shift</span><span>状态，置</span><span>1</span><span>表示按下右</span><span>shift</span><span>键；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>1</span><span>：左</span><span>shift</span><span>状态，置</span><span>1</span><span>表示按下左</span><span>shift</span><span>键；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>2</span><span>：</span><span>Ctrl</span><span>状态，置</span><span>1</span><span>表示按下</span><span>Ctrl</span><span>键；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>3</span><span>：</span><span>Alt</span><span>状态，置</span><span>1</span><span>表示按下</span><span>Alt</span><span>键；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>4</span><span>：</span><span>ScrolLock</span><span>状态，置</span><span>1</span><span>表示</span><span>Scroll</span><span>指示灯亮；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>5</span><span>：</span><span>NumLock</span><span>状态，置</span><span>1</span><span>表示小键盘输入的是数字；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>6</span><span>：</span><span>CapsLock</span><span>状态，置</span><span>1</span><span>表示输入大写字母；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>7</span><span>：</span><span>Insert</span><span>状态：置</span><span>1</span><span>表示处于删除状态。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>编写</span><span>int 9</span></strong><strong><span>中断例程</span></strong></p>
<p><span>键盘输入的处理过程：</span></p>
<p><span>1</span><span>）键盘产生扫描码；</span></p>
<p><span>2</span><span>）扫描码送入</span><span>60h</span><span>端口；</span></p>
<p><span>3</span><span>）引发</span><span>9</span><span>号中断；</span></p>
<p><span>4</span><span>）</span><span>CPU</span><span>执行</span><span>int 9</span><span>中断例程处理键盘输入。</span></p>
<p>&nbsp;</p>
<p><span>上面的过程中，第（</span><span>1</span><span>）、（</span><span>2</span><span>）、（</span><span>3</span><span>）步都是由硬件系统完成的。我们能够改变的只有</span><span>int 9</span><span>的中断处理程序。</span></p>
<p>&nbsp;</p>
<p><span>编程：在屏蔽中间依次显示&#8220;</span><span>a</span><span>&#8221;</span><span>~</span><span>&#8220;</span><span>z</span><span>&#8221;，并可以让人看清。在显示的过程中，按下</span><span>Esc</span><span>键后，改变显示的颜色。</span></p>
<p><span>assume cs:code</span></p>
<p><span>code segment</span></p>
<p><span>&nbsp;start: &nbsp;mov ax,0b800h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ah,&#8217;a&#8217;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>s:<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es:[160*12+40*2],ah</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>inc ah</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cmp ah,&#8217;z&#8217;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jna s</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p><span>上面的程序的执行过程中，我们无法看清屏幕上的显示，因为一个字母刚显示到屏幕上，因为</span><span>CPU</span><span>执行指令太快了。</span></p>
<p><span>我们应该在每显示一个字母后，延时一段时间，让人看清后，再显示下一个字母。</span></p>
<p><span>那么如何延时呢？我们让</span><span>CPU</span><span>执行一段时间的空循环，因为现在的</span><span>CPU</span><span>的速度都非常快，所以循环的次数一定要大，我们用两个</span><span>16</span><span>位寄存器来存放</span><span>32</span><span>位的循环次数。如下：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov dx,10h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,0</span></p>
<p><span>&nbsp;s:<span> </span>sub ax,1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sbb dx,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cmp ax,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jne s</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cmp dx,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jne s</span></p>
<p>&nbsp;</p>
<p><span>上面的程序，循环</span><span>100000h</span><span>次。我们可以将循环延时的程序段写为一个子程序。</span></p>
<p><span>assume cs:code</span></p>
<p><span>stack segment</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db 128 dup (0)</span></p>
<p><span>stack ends</span></p>
<p>&nbsp;</p>
<p><span>code segment</span></p>
<p><span>&nbsp;start:<span>&nbsp;&nbsp;&nbsp; </span>mov ax,stack</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ss,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov sp,128</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,0b800h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ex,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ah,&#8217;a&#8217;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>s:<span>&nbsp;&nbsp; </span>mov es:[160*12+40*2],ah</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>call delay</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>inc ah</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cmp ah,&#8217;z&#8217;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jna s</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p>&nbsp;</p>
<p><span>&nbsp;delay:<span>&nbsp;&nbsp; </span>push ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push dx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov dx,1000h<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>循环</span><span>10000000</span><span>次，读者可以根据自己机器的速度调整循环次数</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>s1:<span> </span>sub ax,1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sbb dx,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jne sl</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cmp dx,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jne s1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop dx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p><span>上面的程序，显示&#8220;</span><span>a</span><span>&#8221;</span><span>~</span><span>&#8220;</span><span>z</span><span>&#8221;，并可以让人看清，这个任务已经实现。</span></p>
<p><span>那么如何实现，按下</span><span>Esc</span><span>键后，改变显示的颜色呢？</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>键盘输入到达</span><span>60h</span><span>端口后，就会引发</span><span>9</span><span>号中断，</span><span>CPU</span><span>则转去执行</span><span>int 9</span><span>中断例程。我们可以编写</span><span>int 9</span><span>中断例程，功能如下：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（</span><span>1</span><span>）从</span><span>60h</span><span>端口读出键盘的输入；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（</span><span>2</span><span>）调用</span><span>BIOS</span><span>的</span><span>int 9</span><span>中断全程，处理其他硬件细节；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（</span><span>3</span><span>）判断是否为</span><span>Esc</span><span>键的扫描码，如果是，改变显示的颜色后返回；如果不是则直接返回。</span></p>
<p>&nbsp;</p>
<p><span>分析：</span></p>
<p><span>1</span><span>、从端口</span><span>60h</span><span>读出键盘的输入</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>in al,60h</span></p>
<p><span>2</span><span>、调用</span><span>BIOS</span><span>的</span><span>int 9</span><span>中断例程</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>注意，我们写的中断处理程序要成为新的</span><span>int 9</span><span>中断例程，主程序必须要将中断向量表中的</span><span>int 9</span><span>中断例程的入口地址改为我们写的中断处理程序的入口地址。则在新的中断处理程序中调用原来的</span><span>int 9</span><span>中断例程时，中断向量表中的</span><span>int 9</span><span>中断例程的入口地址却不是原来的</span><span>int 9</span><span>中断例程的地址，所以我们不能使用</span><span>int </span><span>指令直接调用。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>要能在我们写的新的中断例程中调用原来的中断例程，就必须在将中断向量表中的中断例程的入口地址改为新地址之前，将原来的入口地址保存起来。这样，在需要调用的时候我们才能找到原来的中断例程的入口。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>有了入口地址后，我们如何进行调用呢？</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>当然不能使用指令</span><span>int 9</span><span>来调用，我们可以用别的指令来对</span><span>int </span><span>指令进行一些模拟，从而实现对中断例程的调用。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int</span><span>指令在执行的时候，</span><span>CPU</span><span>进行下面的工作：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>1</span><span>）取中断类型码</span><span>n</span><span>；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>2</span><span>）标志寄存器入栈；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>3</span><span>）</span><span>IF=0</span><span>，</span><span>TF=0</span><span>；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>4</span><span>）</span><span>CS</span><span>、</span><span>IP</span><span>入栈；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>5</span><span>）</span><span>(IP)=(n*4)</span><span>，</span><span>(CS)=(n*4+2)</span><span>。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>取中断类型码是为了定位中断例程的入口地址，在我们的问题中，中断例程的入口地址已经知道。所以，我们用别的指令模拟</span><span>int</span><span>指令时候，不需要做第</span><span>(1)</span><span>步。在假设要调用的中断例程的入口地址在</span><span>ds:0</span><span>和</span><span>ds:2</span><span>单元中的前提下，我们将</span><span>int</span><span>过程用下面几步模拟：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>1</span><span>）标志寄存器入栈；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>2</span><span>）</span><span>IF=0</span><span>，</span><span>TF=0</span><span>；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>3</span><span>）</span><span>CS</span><span>、</span><span>IP</span><span>入栈；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>4</span><span>）</span><span>(IP)=((ds)*16+0)</span><span>，</span><span>(CS)=((ds)*16+2)</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>所以</span><span>int </span><span>过程的模拟过程变为：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>1</span><span>）标志寄存器入栈；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>2</span><span>）</span><span>IF=0</span><span>，</span><span>TF=0</span><span>；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>3</span><span>）</span><span>call dword ptr ds:[0]</span></p>
<p>&nbsp;</p>
<p><span>对于（</span><span>1</span><span>），可用</span><span>pushf</span><span>实现。</span></p>
<p><span>对于（</span><span>2</span><span>），可用下面的指令实现。</span></p>
<p><span>pushf</span></p>
<p><span>pop ax</span></p>
<p><span>and ah,11111100b<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;IF</span><span>和</span><span>TF</span><span>为标志寄存的第</span><span>9</span><span>位和第</span><span>8</span><span>位</span></p>
<p><span>push ax</span></p>
<p><span>popf</span></p>
<p>&nbsp;</p>
<p><span>则，模拟</span><span>int</span><span>指令的调用功能，调用入口地址在</span><span>ds:0</span><span>、</span><span>ds:2</span><span>中的中断例程的程序为：</span></p>
<p><span>pushf <span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>标志寄存器入栈</span></p>
<p>&nbsp;</p>
<p><span>pushf</span></p>
<p><span>pop ax</span></p>
<p><span>and ah,11111100b</span></p>
<p><span>push ax</span></p>
<p><span>popf<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;IF=0, TF=0</span></p>
<p>&nbsp;</p>
<p><span>call dword ptr ds:[0]<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;CS</span><span>、</span><span>IP</span><span>入栈；</span><span>(IP)=((ds)*16+0), (CS)=((ds)*16+2)</span></p>
<p>&nbsp;</p>
<p><span>3</span><span>、如果是</span><span>Esc</span><span>键扫描码，改变显示的颜色后返回</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>如果改变显示的颜色？</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>显示的位置是屏幕的中间，即第</span><span>12</span><span>行</span><span>40</span><span>列，显存中的偏移地址为：</span><span>160*12+40*2</span><span>。所以字符的</span><span>ASCII</span><span>码要送入</span><span>b800:160*12+40*2</span><span>处。而</span><span>b800:160*12+40*2+1</span><span>处是字符的属性，我们只要改变此处的数据就可以改变在</span><span>b800:160*12+40*2</span><span>处显示的字符的颜色了。</span></p>
<p>&nbsp;</p>
<p><span>该程序的最后一个问题是，要在程序返回前，将中断向量表中的</span><span>int 9</span><span>中断例程的入口地址恢复为原来的地址。否则程序返回后，别的程序将无法使用键盘。</span></p>
<p>&nbsp;</p>
<p><span>完整的程序如下：</span></p>
<p><span>assume cs:code</span></p>
<p>&nbsp;</p>
<p><span>stack segment</span></p>
<p><span>&nbsp;db 128 dup (0)</span></p>
<p><span>stack ends</span></p>
<p>&nbsp;</p>
<p><span>data segment</span></p>
<p><span>&nbsp;dw 0,0</span></p>
<p><span>data ends</span></p>
<p>&nbsp;</p>
<p><span>code segment</span></p>
<p><span>&nbsp;start:mov ax,stack</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ss,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov sp,128</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,data</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ds,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push es:[9*4]</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop ds:[0]</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push es:[9*4+2]</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop ds:[2]<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>将原来的</span><span>int 9</span><span>中断例程的入口地址保存在</span><span>ds:0</span><span>、</span><span>ds:2</span><span>单元中</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov word ptr es:[9*4],offset int9</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es:[9*4+2],cs<span>&nbsp;&nbsp; </span>;</span><span>在中断向量表中设置新的</span><span>int 9</span><span>中断例程的入口地址</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;mov ax,0b800h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ah,'a'</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>s: mov es:[160*12+40*2],ah</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>call delay</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>inc ah</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cmp ah,'z'</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jna s</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push ds:[0]</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop es:[9*4]</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push ds:[2]</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop es:[9*4+2]<span>&nbsp;&nbsp;&nbsp; </span>;</span><span>将中断向量表中</span><span>int 9</span><span>中断例程的入口恢复为原来的地址</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span>&nbsp;delay:push ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push dx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov dx,1000h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>s1:sub ax,1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sbb dx,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cmp ax,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jne s1</span></p>
<p><span>&nbsp;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cmp dx,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jne s1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop dx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;-------</span><span>以下为新的</span><span>int 9</span><span>中断例程</span><span>------------------</span></p>
<p><span>&nbsp;int9: push ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push es</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>in al,60h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pushf</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pushf</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>and bh,11111100b</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>popf</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>call dword ptr ds:[0] ;</span><span>对</span><span>int</span><span>指令进行模拟，调用原来的</span><span>int 9</span><span>中断例程</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cmp al,1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jne int9ret</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,0b800h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>inc byte ptr es:[160*12+40*2+1]<span>&nbsp;&nbsp; </span>;</span><span>将属性值加</span><span>1</span><span>，改变颜色</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span>int9ret:pop es</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>iret</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>检测点</span><span>15.1</span></p>
<p><span>（</span><span>1</span><span>）模拟</span><span>int</span><span>指令调用原</span><span>int9</span><span>中断例程的程序段是可以精简的，因为在进入中断例程后，</span><span>IF</span><span>和</span><span>TF</span><span>都已经置</span><span>0</span><span>，没有必要再进行设置了。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>对于程序段：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pushf</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pushf</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>and ah,11111100b</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>popf</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>call dword ptr ds:[0]</span></p>
<p><span>可以精简为：</span></p>
<p><span>pushf</span></p>
<p><span>call dword ptr ds:[0]</span></p>
<p><span>再条指令。</span></p>
<p>&nbsp;</p>
<p><span>（</span><span>2</span><span>）仔细分析上面程序中的主程序，会发现一个潜在的问题？在主程序中，如果在执行设置</span><span>int9</span><span>中断例程的段地址和偏移地址的指令之间发生了键盘中断，则</span><span>CPU</span><span>将转去一个错误的地址执行，将发生错误。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>排除潜在问题方法：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>在</span><span>pop ds:[2]</span><span>指令后加入一条</span><span>cli</span><span>指令，并在</span><span>mov es:[9*4+2],cs</span><span>指令后加入一条</span><span>sti</span><span>指令即可。意思是在这段期间，让</span><span>IF=0</span><span>，禁止或屏蔽外中断处事程序的执行。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>安装新的</span><span>int9</span></strong><strong><span>中断处理例程</span></strong></p>
<p><span>安装一个新的</span><span>int 9</span><span>中断例程，使得原</span><span>int 9</span><span>中断例程的功能得到扩展。</span></p>
<p><span>任务：安装一个新的</span><span>int 9</span><span>中断例程，功能：在</span><span>DOS</span><span>下，按</span><span>F1</span><span>键后改变当前屏幕的显示颜色，其他的键照常处理。</span></p>
<p><span>分析：</span></p>
<p><span>（</span><span>1</span><span>）改变屏幕的显示颜色</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>改变从</span><span>B8000</span><span>开始的</span><span>4000</span><span>个字节中的所有奇地址单元中的内容，当前屏幕的显示颜色即发生改变。程序如下：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,0b800h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bx,1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cx,2000</span></p>
<p><span>&nbsp;s:<span> </span>nc byte ptr es:[bx]</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add bx,2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>loop s</span></p>
<p>&nbsp;</p>
<p><span>（</span><span>2</span><span>）其他键照常处理</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>可以调用原</span><span>int 9</span><span>中断处理程序，来处理其他的键盘输入。</span></p>
<p>&nbsp;</p>
<p><span>（</span><span>3</span><span>）原</span><span>int 9</span><span>中断例程入口地址的保存</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>因为在编写的新</span><span>int 9</span><span>中断例程中要调用原</span><span>int 9</span><span>中断例程，所以，要保存原</span><span>int 9</span><span>中断例程的入口地址。保存在哪里？显然不能保存在安装程序中，因为安装程序返回后地址将丢失。我们将地址保存在</span><span>0:200</span><span>单元处。</span></p>
<p>&nbsp;</p>
<p><span>（</span><span>4</span><span>）新</span><span>int 9</span><span>中断例程的安装</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>我们可将新的</span><span>int 9</span><span>中断例程安装在</span><span>0:204</span><span>处。</span></p>
<p>&nbsp;</p>
<p><span>完整的程序如下：</span></p>
<p><span>assume cs:code</span></p>
<p>&nbsp;</p>
<p><span>stack segment</span></p>
<p><span>&nbsp;db 128 dup (0)</span></p>
<p><span>stack ends</span></p>
<p>&nbsp;</p>
<p><span>code segment</span></p>
<p><span>&nbsp;start:</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,stack</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ss,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov sp,128</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push cs</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop ds</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov si,offset int9<span>&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>ds:si</span><span>指向源地址</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov di,204h<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>es:di</span><span>指向目的地址</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cx,offset int9end - offset int9<span>&nbsp;&nbsp; </span>;</span><span>设置</span><span>cx</span><span>为传输长度</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cld<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置传输方向为正</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>rep movsb</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push es:[9*4]</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop es:[200h]</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push es:[9*4+2]</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop es:[202h]<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>保存原有的</span><span>int 9</span><span>中断例程入口地址</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cli<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>IF</span><span>为</span><span>0</span><span>，禁止</span><span>CPU</span><span>执行可屏蔽外中断。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov word ptr es:[9*4],204h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov word ptr es:[9*4+2],0<span>&nbsp;&nbsp; </span>;</span><span>设置</span><span>int 9</span><span>中断向量，指向新的中断例程入口地址</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sti<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>恢复设置</span><span>IF</span><span>为</span><span>1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp; </span>int9:push ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push cx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push es</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>in al,60h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pushf</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>call dword ptr cs:[200h]<span>&nbsp;&nbsp;&nbsp; </span>;</span><span>当此中断例程执行时</span><span>(CS)=0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;cmp al,1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;F1</span><span>的扫描码为</span><span>3bh</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;jne int9ret</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,0b800h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bx,1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cx,2000</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>s:mov byte ptr es:[bx],2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;inc byte ptr es:[bx]</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>and bx,2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>loop s</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span>int9ret:pop es</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop cx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop ax</span></p>
<p><span><span>&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>iret</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span>int9end:nop</span></p>
<p>&nbsp;</p>
<p><span>code ends</span></p>
<p>&nbsp;</p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>通过对键盘输入的处理，了解了</span><span>CPU</span><span>对外设输入的通常</span> <span>通常方法，即：</span></p>
<p><span>1</span><span>）外设的输入送入端口；</span></p>
<p><span>2</span><span>）向</span><span>CPU</span><span>发出外中断（可屏蔽中断）信息；</span></p>
<p><span>3</span><span>）</span><span>CPU</span><span>检测到可屏蔽中断信息，如果</span><span>IF=1</span><span>，</span><span>CPU</span><span>在执行完当前指令后响应中断，执行相应的中断例程；</span></p>
<p><span>4</span><span>）可在中断例程中实现对外设输入的处理。</span></p>
<p>&nbsp;</p>
<p><span>端口和中断机制，是</span><span>CPU</span><span>进行</span><span>I/O</span><span>的基础。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>指令系统总结</span></strong></p>
<p><span>8086CPU</span><span>提供以下几大类指令：</span></p>
<p><span>1</span><span>、数据传送指令</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov, push, pop, pushf, popf, xchg</span><span>等。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>实现寄存器和内存、寄存器和寄存器之间的单个数据传送。</span></p>
<p>&nbsp;</p>
<p><span>2</span><span>、自述运算指令</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add, sub, adc, sbb, inc, dec, cmp, imul, idiv, aaa </span><span>等。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>实现寄存器和内存中的数据的算术运算。它们的执行结果影响标志寄存器的：</span><span>sf, zf, of, cf, pf, af</span><span>位。</span></p>
<p>&nbsp;</p>
<p><span>3</span><span>、逻辑指令</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>and, or, not, xor, test, shl, shr, sal, sar, rol, ror, rcl, rcr</span><span>等。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>除了</span><span>not</span><span>指令外，它们的执行结果都影响标志寄存器的相关标志位。</span></p>
<p>&nbsp;</p>
<p><span>4</span><span>、转移指令</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>可以修改</span><span>IP</span><span>，或同时修改</span><span>CS</span><span>和</span><span>IP</span><span>的指令统称为转移指令。分为以下几类：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>1</span><span>）无条件转移指令，比如：</span><span>jmp</span><span>；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>2</span><span>）条件转移指令，比如：</span><span>jcxz, je, jb, ja, jnb, jna</span><span>等；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>3</span><span>）循环指令，比如：</span><span>loop</span><span>；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>4</span><span>）过程，比如：</span><span>call, ret, reft</span><span>；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>5</span><span>）中断，比如：</span><span>int, iret</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>5</span><span>、处理机控制指令</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>这些指令对标志寄存器或其他处理机状态进行设置，比如：</span><span>cld, std, cli, sti, nop, clc, cmc, stc, hlt, wait, csc, lock</span><span>等都是处理机控制指令。</span></p>
<p>&nbsp;</p>
<p><span>6</span><span>、串处理指令</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>这些指令对内存中的批量数据进行处理，比如：</span><span>movsb, movsw, cmps, scas, lods, stos</span><span>等。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>基要使用这些指令方便地进行批量数据的处理，则需要和</span><span>rep, repe, repne</span><span>等前缀指令配合使用。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/122176.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-04 10:38 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/04/122176.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编语言--端口</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/04/122175.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Wed, 04 Aug 2010 02:37:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/04/122175.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/122175.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/04/122175.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/122175.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/122175.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>端口</span></p>
<p>&nbsp;</p>
<p><span>前面讲过，各种存储器都和</span><span>CPU</span><span>的地址线、数据线、控制线相连。</span><span>CPU</span><span>在操控它们的时候，把它们都当作内存来对待，把它们总的看做一个由若干存储单元组成的逻辑存储器，这个逻辑器我们称其为内存地址空间。</span></p>
<p>&nbsp;</p>
<p><span>在</span><span>PC</span><span>机系统中，和</span><span>CPU</span><span>通过总线相连的芯片除各种存储器外，还有以下</span><span>3</span><span>种芯片：</span></p>
<p><span>1</span><span>）各种接口卡（比如：网卡、显卡）上的接口芯片，它们控制接口卡进行工作；</span></p>
<p><span>2</span><span>）主板上的接口芯片，</span><span>CPU</span><span>通过它们对部分外设进行访问；</span></p>
<p><span>3</span><span>）其他芯片，用来存储相关的系统信息，或进行相关的输入输出处理。</span></p>
<p>&nbsp;</p>
<p><span>在这些芯片中，都有一组可以由</span><span>CPU</span><span>读写的寄存器。这些寄存器，它们在物理上可能处于不同的芯片中，但是它们在以下两点上相同：</span></p>
<p><span>1</span><span>）都和</span><span>CPU</span><span>的总线相连，当然这种连接是通过它们所在的芯片进行的；</span></p>
<p><span>2</span><span>）</span><span>CPU</span><span>对它们进行读或写的时候都通过控制线向它们所在的芯片发出端口读写命令。</span></p>
<p>&nbsp;</p>
<p><span>可见，从</span><span>CPU</span><span>的角度，将这些寄存器都当作端口，对它们进行统一编址，从而建立了一个统一的端口地址空间。每一个端口在地址空间中都有一个地址。</span></p>
<p>&nbsp;</p>
<p><span>CPU</span><span>可以直接读写</span><span>3</span><span>个地方的数据：</span></p>
<p><span>1</span><span>）</span><span>CPU</span><span>内部的寄存器；</span></p>
<p><span>2</span><span>）内存单元；</span></p>
<p><span>3</span><span>）端口中。（芯片中的寄存器）</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>端口的读写</span></strong></p>
<p><span>在访问端口的时候，</span><span>CPU</span><span>通过端口地址来定位端口。因为端口所在的芯片和</span><span>CPU</span><span>通过总路线相连，所以，端口地址和内存地址一样，通过地址总路线来传送。在</span><span>PC</span><span>系统中，</span><span>CPU</span><span>最多可以定位</span><span>64K</span><span>个不同的端口。则端口地址的范围为</span><span>0~65535</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>对端口的读写不能用</span><span>mov</span><span>，</span><span>push</span><span>，</span><span>pop</span><span>等内存读写指令。</span></p>
<p><span>端口的读写指令只有两条：</span><span>in</span><span>和</span><span>out</span><span>，分别用于从端口读出数据和往端口写入数据。</span></p>
<p>&nbsp;</p>
<p><span>我们来看一下</span><span>CPU</span><span>执行内存访问指令和端口访问指令时候，总线上的信息：</span></p>
<p><span>1</span><span>）访问内存：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,ds:[8]<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>假设执行前</span><span>(ds)=0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>执行时与总路线相关的操作：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（</span><span>1</span><span>）</span><span>CPU</span><span>通过地址线将地址信息</span><span>8</span><span>发出；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（</span><span>2</span><span>）</span><span>CPU</span><span>通过控制线发出内存读命令，选中存储器芯片，并通知它，将要从中读取数据；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（</span><span>3</span><span>）存储器将</span><span>8</span><span>号单元中的数据通过数据线送入</span><span>CPU</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>2</span><span>）访问端口：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>in al,60h<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>从</span><span>60h</span><span>号端口读入一个字节</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>执行时与总路线相关的操作：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（</span><span>1</span><span>）</span><span>CPU</span><span>通过地址线将地址信息</span><span>60h</span><span>发出；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（</span><span>2</span><span>）</span><span>CPU</span><span>通过控制线发出端口读命令，选中端口所在的芯片，并通知它，将要从中读取数据；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（</span><span>3</span><span>）端口所在的芯片将</span><span>60h</span><span>端口中的数据通过数据线送入</span><span>CPU</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>在</span><span>in</span><span>和</span><span>out</span><span>指令中，只能使用</span><span>ax</span><span>或</span><span>al</span><span>来存放从端口中读入的数据或要发送到端口中的数据。访问</span><span>8</span><span>位端口时用</span><span>al</span><span>，访问</span><span>16</span><span>位端口时用</span><span>ax</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>对</span><span>0~255</span><span>以内的端口进行读写时：</span></p>
<p><span>in al,20h<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>从</span><span>20h</span><span>端口读入一个字节</span></p>
<p><span>out 20h,al<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>往</span><span>20h</span><span>端口写入一个字节</span></p>
<p>&nbsp;</p>
<p><span>对</span><span>256~65535</span><span>的端口进行读写时，端口号放在</span><span>dx</span><span>中：</span></p>
<p><span>mov dx,<st1:chmetcnv w:st="on" UnitName="F" SourceValue="3" HasSpace="False" Negative="False" NumberType="1" TCSC="0">3f</st1:chmetcnv>8h<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>将端口号</span><st1:chmetcnv w:st="on" UnitName="F" SourceValue="3" HasSpace="False" Negative="False" NumberType="1" TCSC="0"><span>3f</span></st1:chmetcnv><span>8h</span><span>送入</span><span>dx</span></p>
<p><span>in al,dx<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>从</span><st1:chmetcnv w:st="on" UnitName="F" SourceValue="3" HasSpace="False" Negative="False" NumberType="1" TCSC="0"><span>3f</span></st1:chmetcnv><span>8h</span><span>端口读入一个字节</span></p>
<p><span>out dx,al<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>向</span><st1:chmetcnv w:st="on" UnitName="F" SourceValue="3" HasSpace="False" Negative="False" NumberType="1" TCSC="0"><span>3f</span></st1:chmetcnv><span>8h</span><span>端口写入一个字节</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>CMOS RAM</span></strong><strong><span>芯片</span></strong></p>
<p><span>我们通过对</span><span>CMOS RAM</span><span>的读写来体会一下对端口的访问。</span></p>
<p><span>PC</span><span>机中，有一个</span><span>CMOS RAM</span><span>芯片，一般简称为</span><span>CMOS</span><span>。此芯片的特征如下：</span></p>
<p><span>1</span><span>）包含一个实时钟和一个有</span><span>128</span><span>个存储单元的</span><span>RAM</span><span>存储器（早期的计算机为</span><span>64</span><span>个字节）。</span></p>
<p><span>2</span><span>）该芯片靠电池供电。所以，关机后其内部的实时钟仍可正常工作，</span><span>RAM</span><span>中的信息不丢失。</span></p>
<p><span>3</span><span>）</span><span>128</span><span>个字节的</span><span>RAM</span><span>中，内部实时钟占用</span><span>0~0dh</span><span>单元来保存时间信息，其余大部分单元用来保存系统配置信息，供系统启动时</span><span>BIOS</span><span>程序读取。</span><span>BIOS</span><span>也提供了相关的程序，使我们可以在开机的时候配置</span><span>CMOS RAM</span><span>中的系统信息。</span></p>
<p><span>4</span><span>）该芯片内部有两个端口，端口地址为</span><span>70h</span><span>和</span><span>71h</span><span>。</span><span>CPU</span><span>通过这两个端口来读写</span><span>CMOS RAM</span><span>。</span></p>
<p><span>5</span><span>）</span><span>70h</span><span>为地址端口，存放要访问的</span><span>CMOS RAM</span><span>单元的地址：</span><span>71h</span><span>为数据端口，存放从选定的</span><span>CMOS RAM</span><span>单元中读取的数据。可见，</span><span>CPU</span><span>对</span><span>CMOS RAM</span><span>的读写分两步进行了，比如：读</span><span>CMOS RAM</span><span>的</span><span>2</span><span>号单元：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（</span><span>1</span><span>）将</span><span>2</span><span>送入端口</span><span>70h</span><span>；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（</span><span>2</span><span>）从</span><span>71h</span><span>读出</span><span>2</span><span>号单元的内容。</span></p>
<p>&nbsp;</p>
<p><span>检测点</span><span>14.1</span></p>
<p><span>1</span><span>）编程：读取</span><span>CMOS RAM</span><span>的</span><span>2</span><span>号单元的内容。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov al,2h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>out 70h,al<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>把</span><span>2</span><span>写入地址端口。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>in 71h,al<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>把数据端口的内容读出放入</span><span>al</span><span>寄存器。</span></p>
<p>&nbsp;</p>
<p><span>2</span><span>）编程：向</span><span>CMOS RAM</span><span>的</span><span>2</span><span>号单元写入</span><span>0</span><span>。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov al,2h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>out 70h,al<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>把</span><span>2</span><span>写入地址端口</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov al,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>out 71h,al<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>把</span><span>0</span><span>写入数据端口</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>shl</span></strong><strong><span>和</span><span>shr</span></strong><strong><span>指令</span></strong></p>
<p><span>shl</span><span>和</span><span>shr</span><span>是逻辑移位指令。</span></p>
<p>&nbsp;</p>
<p><span>shl</span><span>是逻辑左移指令，它的功能为：</span></p>
<p><span>1</span><span>）将一个寄存器或内存单元中的数据向左移位；</span></p>
<p><span>2</span><span>）将最后移出的一位写入</span><span>CF</span><span>中；</span></p>
<p><span>3</span><span>）最低位用</span><span>0</span><span>补充。</span></p>
<p><span>指令：</span></p>
<p><span>mov al,01001000b</span></p>
<p><span>shl al,1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>将</span><span>al</span><span>中的数据左移一位</span></p>
<p><span>执行后</span><span>(al)=10010000b</span><span>，</span><span>CF=0</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>如果移动位数大于</span><span>1</span><span>时，必须将移动位数放在</span><span>cl</span><span>中。</span></p>
<p><span>比如，指令：</span></p>
<p><span>mov al,01010001b</span></p>
<p><span>mov cl,3</span></p>
<p><span>shl al,cl</span></p>
<p><span>执行后</span><span>(al)=10001000b</span><span>，因为最后移出的一位是</span><span>1</span><span>，所以</span><span>CF=1</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>可以看出，将</span><span>X</span><span>逻辑左移一位，相当于执行</span><span>X=X*2</span><span>。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>shr</span><span>是逻辑右移指令，它和</span><span>shl</span><span>所进行的操作刚好相反：</span></p>
<p><span>1</span><span>）将一个寄存器或内存单元中的数据向右移位；</span></p>
<p><span>2</span><span>）将最后移出的一位写入</span><span>CF</span><span>中。</span></p>
<p><span>3</span><span>）最高位用</span><span>0</span><span>补充。</span></p>
<p><span>指令：</span></p>
<p><span>mov al,10000001b</span></p>
<p><span>shr al,1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>将</span><span>al</span><span>中的数据右移一位</span></p>
<p><span>执行后</span><span>(al)=01000000b</span><span>，</span><span>CF=1</span></p>
<p><span>如果移动位数大于</span><span>1</span><span>时，必须将移动位数放在</span><span>cl</span><span>中。</span></p>
<p><span>比如，指令：</span></p>
<p><span>mov al,01010001b</span></p>
<p><span>mov cl,3</span></p>
<p><span>shr al,cl</span></p>
<p><span>执行后</span><span>(al)=00001010b</span><span>，因为最后移出的一位是</span><span>0</span><span>，所以</span><span>CF=0</span><span>。</span></p>
<p><span>可以看出将</span><span>X</span><span>逻辑右移一位，相当于执行</span><span>X=X/2</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>检测点</span><span>14.2</span></p>
<p><span>编程：用加法和移位指令计算</span><span>(ax)=(ax)*10</span></p>
<p><span>提示：</span><span>(ax)*10 = (ax)*2 + (ax)*8</span></p>
<p>&nbsp;</p>
<p><span>assume cs:code</span></p>
<p><span>code segment</span></p>
<p><span>&nbsp;start:<span>&nbsp;&nbsp;&nbsp; </span>mov ax,1000h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bx,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>shl bx,1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;(ax)=(ax)*2</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cl,3</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>shl ax,cl<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;(ax)=(ax)*8</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add ax,bx<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;(ax)=(ax)*10</span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>CMOS RAM</span></strong><strong><span>中存储的时间信息</span></strong></p>
<p><span>在</span><span>CMOS RAM</span><span>中，存放着当前的时间：年、月、日、时、分、秒。这</span><span>6</span><span>个信息的长度都为</span><span>1</span><span>个字节，存放单元为：</span></p>
<p><span>秒：</span><span>0<span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>分：</span><span>2<span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>时：</span><span>4<span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>日：</span><span>7<span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>月：</span><span>8<span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>年：</span><span>9</span></p>
<p><span>这些数据以</span><span>BCD</span><span>码的方式存放。</span></p>
<p><span>BCD</span><span>码是以</span><span>4</span><span>位二进制数表示十进制数码的编码方法，如下表示：</span></p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=53>
            <p><span>十进制</span></p>
            </td>
            <td vAlign=top width=52>
            <p align=right><span>0</span></p>
            </td>
            <td vAlign=top width=52>
            <p align=right><span>1</span></p>
            </td>
            <td vAlign=top width=52>
            <p align=right><span>2</span></p>
            </td>
            <td vAlign=top width=52>
            <p align=right><span>3</span></p>
            </td>
            <td vAlign=top width=52>
            <p align=right><span>4</span></p>
            </td>
            <td vAlign=top width=52>
            <p align=right><span>5</span></p>
            </td>
            <td vAlign=top width=52>
            <p align=right><span>6</span></p>
            </td>
            <td vAlign=top width=52>
            <p align=right><span>7</span></p>
            </td>
            <td vAlign=top width=52>
            <p align=right><span>8</span></p>
            </td>
            <td vAlign=top width=51>
            <p align=right><span>9</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=53>
            <p><span>BCD</span><span>码</span></p>
            </td>
            <td vAlign=top width=52>
            <p align=right><span>0000</span></p>
            </td>
            <td vAlign=top width=52>
            <p align=right><span>0001</span></p>
            </td>
            <td vAlign=top width=52>
            <p align=right><span>0010</span></p>
            </td>
            <td vAlign=top width=52>
            <p align=right><span>0011</span></p>
            </td>
            <td vAlign=top width=52>
            <p align=right><span>0100</span></p>
            </td>
            <td vAlign=top width=52>
            <p align=right><span>0101</span></p>
            </td>
            <td vAlign=top width=52>
            <p align=right><span>0110</span></p>
            </td>
            <td vAlign=top width=52>
            <p align=right><span>0111</span></p>
            </td>
            <td vAlign=top width=52>
            <p align=right><span>1000</span></p>
            </td>
            <td vAlign=top width=51>
            <p align=right><span>1001</span></p>
            </td>
        </tr>
    </tbody>
</table>
<p><span>比如：数值</span><span>26</span><span>，用</span><span>BCD</span><span>码表示为：</span><span>0010 0110 </span><span>（</span><span>2 6</span><span>）</span></p>
<p>&nbsp;</p>
<p><span>可见，一个字节可表示两个</span><span>BCD</span><span>码。则</span><span>CMOS RAM</span><span>存储时间信息的单元中，存储了用两个</span><span>BCD</span><span>码表示的两位十进制数，高</span><span>4</span><span>位的</span><span>BCD</span><span>码表示十位，低</span><span>4</span><span>位的</span><span>BCD</span><span>码表示个位。比如：</span><span>00010100b</span><span>表示</span><span>14</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>编程：在屏幕中间显示当前的月份。</span></p>
<p><span>分析：这个程序主要做两部分工作：</span></p>
<p><span>（</span><span>1</span><span>）从</span><span>CMOS RAM</span><span>的</span><span>8</span><span>号单元读出当前月份的</span><span>BCD</span><span>码：</span><span>&nbsp;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov al,8<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>首先要向地址端口</span><span>70h </span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>out 70h,al<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>写入要访问的单元的地址。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>in al,71h<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>然后，从数据端口</span><span>71h</span><span>中取得指定单元中的数据。</span></p>
<p>&nbsp;</p>
<p><span>（</span><span>2</span><span>）将用</span><span>BCD</span><span>码表示的月份以十进制的形式显示到屏幕上。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>BCD</span><span>码值</span><span>=</span><span>十进制数码值，则</span><span>BCD</span><span>码值</span><span>+30h=</span><span>十进制数对应的</span><span>ASCII</span><span>码。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>从</span><span>CMOS RAM</span><span>的</span><span>8</span><span>号单元读出的一个字节中，包含了两个</span><span>BCD</span><span>码表示的两位十进制数，高</span><span>4</span><span>位的</span><span>BCD</span><span>码表示十位，低</span><span>4</span><span>位的</span><span>BCD</span><span>码表示个位。比如：</span><span>00010100</span><span>表示</span><span>14</span><span>。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>我们需要进行两步工作：</span></p>
<p><span>1</span><span>）将从</span><span>CMOS RAM</span><span>的</span><span>8</span><span>号单元中读出的一个字节，分为两个表示</span><span>BCD</span><span>码值的数据。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ah,al<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;al</span><span>中为从</span><span>CMOS RAM</span><span>的</span><span>8</span><span>号单元读出的数据</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cl,4</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>shr ah,cl<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;ah</span><span>中为月份的十位数码值</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>and al,00001111b<span>&nbsp;&nbsp;&nbsp; </span>;al</span><span>中为月份的个位数码值</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>2</span><span>）显示</span><span>(ah)+30h</span><span>和</span><span>(al)+30h</span><span>对应的</span><span>ASCII</span><span>码字符。</span></p>
<p>&nbsp;</p>
<p><span>完整程序如下：</span></p>
<p><span>assume cs:code</span></p>
<p><span>code segment</span></p>
<p><span>&nbsp;start:</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov al,8</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>out 70h,al</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>in al,71h<span>&nbsp;&nbsp; </span>;</span><span>从</span><span>CMOS RAM</span><span>的</span><span>8</span><span>号单元读出当前月份的</span><span>BCD</span><span>码。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ah,al<span>&nbsp;&nbsp; </span>;ah,al</span><span>都存储着当前月份的</span><span>BCD</span><span>码。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cl,4</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>shr ah,cl<span>&nbsp;&nbsp; </span>;ah</span><span>中内容逻辑右移</span><span>4</span><span>位，则只剩下月份的十位数码。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>and al,00001111b&nbsp;;al</span><span>中内容将只剩下月份的个位数码。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add ah,30h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add al,30h&nbsp;;</span><span>得到十进制数码的</span><span>ASCII</span><span>码值。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bx,0b800h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov byte ptr es:[160*12+40*2],ah&nbsp;;</span><span>显示月份的十位数码</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov byte ptr es:[160*12+40*2+2],al&nbsp;;</span><span>显示月份的个位数码</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/122175.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-04 10:37 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/04/122175.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编语言--int指令</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/04/122174.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Wed, 04 Aug 2010 02:36:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/04/122174.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/122174.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/04/122174.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/122174.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/122174.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>int</span><span>指令</span></p>
<p><span>中断信息可以来自</span><span>CPU</span><span>的内部和外部，当</span><span>CPU</span><span>的内部有需要处理的事情发生的时候，将产生需要马上处理的中断信息，引发中断过程。</span></p>
<p><span>上一章讲解了</span><span>0</span><span>号中断和单步中断，这一章将讲解另一种重要的内中断，由</span><span>int</span><span>指令引发的中断。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>int</span></strong><strong><span>指令</span></strong></p>
<p><span>格式：</span><span>int n</span></p>
<p><span>n</span><span>为中断类型码，它的功能是引发中断过程。</span></p>
<p>&nbsp;</p>
<p><span>CPU</span><span>执行</span><span>int n</span><span>指令，相当于引发一个</span><span>n</span><span>号中断的中断过程，执行过程如下：</span></p>
<p><span>1</span><span>）取中断类型码</span><span>n</span><span>；</span></p>
<p><span>2</span><span>）标志寄存器入栈，</span><span>IF=0</span><span>，</span><span>TF=0</span><span>；</span></p>
<p><span>3</span><span>）</span><span>CS</span><span>、</span><span>IP</span><span>入栈</span></p>
<p><span>4</span><span>）（</span><span>IP</span><span>）</span><span>=</span><span>（</span><span>n*4</span><span>），</span><span>(CS)=(n*4+2)</span></p>
<p><span>从此处转去执行</span><span>n</span><span>号中断的中断处理程序。</span></p>
<p><span>可以在程序中使用</span><span>int</span><span>指令调用任何一个中断的中断处理程序。</span></p>
<p><span>assume cs:code</span></p>
<p><span>code segment</span></p>
<p><span>&nbsp;start:<span>&nbsp;&nbsp;&nbsp; </span>mov ax,0b800h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>move s,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov byte ptr es:[12*160 +40*2],&#8217;!&#8217;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 0</span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p><span>这个程序在</span><span>Windows2000</span><span>中的</span><span>DOS</span><span>方式下执行时，将在屏幕中间显示一个&#8220;！&#8221;，然后显示&#8220;</span><span>Divide overflow</span><span>&#8221;后返回到系统中。&#8220;！&#8221;是我们编程显示的，而&#8220;</span><span>Divide overflow</span><span>&#8221;是哪里来的呢？我们的程序中又没有做除法，不可能产生除法溢出。</span></p>
<p>&nbsp;</p>
<p><span>程序是没有做除法，但是在结尾使用了</span><span>int 0</span><span>指令。</span></p>
<p><span>CPU</span><span>执行</span><span>int 0</span><span>指令时，将引发中断过程，执行</span><span>0</span><span>号中断处理程序，而系统设置的</span><span>0</span><span>号中断处理程序的功能是显示&#8220;</span><span>Divide overflow</span><span>&#8221;，然后返回系统。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>int</span><span>指令的最终功能和</span><span>call</span><span>指令相似，都是调用一段程序。</span></p>
<p>&nbsp;</p>
<p><span>一般情况下，系统将一些具有一定功能的子程序，以中断处理程序的方式提供给应用程序调用。我们在编程的时候，可以用</span><span>int</span><span>指令调用这些子程序。当然，也可以自己编写一些中断处理程序供别人使用。</span></p>
<p><span>中断处理程序可简称为中断例程。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>编写供应用程序调用的中断例程</span></strong></p>
<p>&nbsp;</p>
<p><span>前面已经编写过中断</span><span>0</span><span>的中断例程了，现在我们讨论可以供应用程序调用的中断例程的编写方法。</span></p>
<p><span>1</span><span>，示例一</span></p>
<p><span>编写、安装中断</span><span>7ch</span><span>的中断全程，功能：求一</span><span>word</span><span>型数据的平方。</span></p>
<p><span>参数：</span><span>(ax)=</span><span>要计算的数据。</span></p>
<p><span>返回值：</span><span>dx</span><span>、</span><span>ax</span><span>中存放结果的高</span><span>16</span><span>位和低</span><span>16</span><span>位。</span></p>
<p><span>应用举例：求</span><span>2*3456^2</span></p>
<p><span>assume cs:code</span></p>
<p><span>code segment</span></p>
<p><span>&nbsp;start:<span>&nbsp;&nbsp;&nbsp; </span>mov ax,3456<span>&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;(ax)=3456</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 7ch<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>调用中断</span><span>7ch</span><span>的中断例程，计算</span><span>ax</span><span>中的数据的平方</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add ax,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>adc dx,dx<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;dx:ax</span><span>存放结果，将结果乘以</span><span>2</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p><span>分析，我们要做三部分工作：</span></p>
<p><span>1</span><span>）编写实现求平方功能的程序；</span></p>
<p><span>2</span><span>）安装程序，我们将其安装在</span><span>0:200</span><span>处；</span></p>
<p><span>3</span><span>）设置中断向量表，将程序的入口地址保存在</span><span>7ch</span><span>表项中，使其成为中断</span><span>7ch</span><span>的中断例程。</span></p>
<p>&nbsp;</p>
<p><span>安装程序如下：</span></p>
<p><span>assume cs:code</span></p>
<p><span>code segment</span></p>
<p><span>&nbsp;start:<span>&nbsp;&nbsp;&nbsp; </span>mov ax,cs</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ds,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov si, offset sqr<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>ds:si</span><span>指向源地址</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov di, 200h<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>es:di</span><span>指向目的地址</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cx, offset sqrend &#8211; offset sqr<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>cx</span><span>为传输长度</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cld<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置传输方向为正</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>rep movsb</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov word ptr es:[7ch*4],200h<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置中断向量表</span> <span>偏移地址</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov word ptr es:[7ch*4+2],0<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置中断向量表，段地址</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p>&nbsp;</p>
<p><span>&nbsp;sqr:<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>mul ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>iret</span></p>
<p><span>&nbsp;sqrend:&nbsp;nop</span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p><span>注意，在中断例程</span><span>sqr</span><span>的最后，要使用</span><span>iret</span><span>指令。用汇编语法描述，</span><span>iret</span><span>指令的功能为：</span></p>
<p><span>pop IP</span></p>
<p><span>pop CS</span></p>
<p><span>popf</span></p>
<p>&nbsp;</p>
<p><span>CPU</span><span>执行</span><span>int 7ch</span><span>指令进入中断例程之前，标志寄存器、当前的</span><span>CS</span><span>和</span><span>IP</span><span>被压入栈中，在执行完中断例程后，应该用</span><span>iret</span><span>指令恢复</span><span>int 7ch</span><span>执行前的标志寄存器和</span><span>CS</span><span>、</span><span>IP</span><span>的值，从而接着执行应用程序。</span></p>
<p>&nbsp;</p>
<p><span>int</span><span>指令和</span><span>iret</span><span>指令的配合使用与</span><span>call</span><span>指令和</span><span>ret</span><span>指令的配合使用具有相似的思路。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>2</span><span>，示例二</span></p>
<p><span>编写、安装中断</span><span>7ch</span><span>的中断例程，功能：将一个全是字母，以</span><span>0</span><span>结尾的字符串，转化为大写。</span></p>
<p><span>参数：</span><span>ds:si</span><span>指向字符串的首地址。</span></p>
<p><span>应用举例：将</span><span>data</span><span>段中的字符串转化为大写。</span></p>
<p>&nbsp;</p>
<p><span>assume cs:code</span></p>
<p><span>data segment</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db &#8216;conversation&#8217;,0</span></p>
<p><span>data ends</span></p>
<p>&nbsp;</p>
<p><span>code segment</span></p>
<p><span>&nbsp;start:<span>&nbsp;&nbsp;&nbsp; </span>mov ax,data</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ds,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov si,0</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 7ch</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p><span>安装程序如下：</span></p>
<p><span>assume cs:code</span></p>
<p><span>code segment</span></p>
<p><span>&nbsp;start:<span>&nbsp;&nbsp;&nbsp; </span>mov ax,cs</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ds,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov si,offset capital</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov di,200h</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cx,offset capitalend &#8211; offset capital</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cld</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>rep movsb</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov word ptr es:[7ch*4],200h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov word ptr es:[7ch*4+2],0</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p>&nbsp;</p>
<p><span>&nbsp;capital:<span> </span>push cx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>push si</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>change:<span>&nbsp;&nbsp; </span>mov cl,[si]</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ch,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jcxz ok</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>and byte ptr [si],11011111b</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>inc si</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>jmp short change</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ok:<span>&nbsp;&nbsp; </span>pop si</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>pop cx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>iret</span></p>
<p><span>&nbsp;capitalend:<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>nop</span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p><span>在中断例程</span><span>capital</span><span>中用到了寄存器</span><span>si</span><span>和</span><span>cx</span><span>，编写中断例程和编写子程序的时候具有同样的问题，就是要避免寄存器的冲突。应该注意例程中用到的寄存器的值的保存和恢复。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>对</span><span>int</span></strong><strong><span>、</span><span>iret</span></strong><strong><span>和栈的深入理解</span></strong></p>
<p><span>问题：用</span><span>7ch</span><span>中断例程完成</span><span>loop</span><span>指令的功能。</span></p>
<p><span>loop s</span><span>的执行需要两个信息，循环次数和到</span><span>s</span><span>的位移，所以</span><span>7ch</span><span>中断例程要完成</span><span>loop</span><span>指令的功能，也需要这两个信息作为参数。我们用</span><span>cx</span><span>存放循环次数，用</span><span>bx</span><span>存放位移。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>BIOS</span></strong><strong><span>和</span><span>DOS</span></strong><strong><span>所提供的中断例程</span></strong></p>
<p><span>在系统板的</span><span>ROM</span><span>中存放着一套程序，称为</span><span>BIOS</span><span>（基本输入输出系统），</span><span>BIOS</span><span>中主要包含以下几部分内容：</span></p>
<p><span>1</span><span>）硬件系统的检测和初始化程序；</span></p>
<p><span>2</span><span>）外部中断和内部中断的中断例程；</span></p>
<p><span>3</span><span>）用于对硬件设备进行了</span><span>I/O</span><span>操作的中断例程；</span></p>
<p><span>4</span><span>）其他和硬件系统相关的中断例程。</span></p>
<p>&nbsp;</p>
<p><span>操作系统</span><span>DOS</span><span>也提供了中断例程，从操作系统的角度来看，</span><span>DOS</span><span>的中断例程就是操作系统向程序员提供的编程资源。</span></p>
<p>&nbsp;</p>
<p><span>BIOS</span><span>和</span><span>DOS</span><span>在所提供的中断例程中包含了许多子程序，这些子程序实现了程序员在编程的时候经常需要用到的功能。程序员在编程的时候，可以用</span><span>int</span><span>指令直接调用</span><span>BIOS</span><span>和</span><span>DOS</span><span>提供的中断例程，来完成某些工作。</span></p>
<p>&nbsp;</p>
<p><span>和硬件设备相关的</span><span>DOS</span><span>中断例程中，一般都调用</span> <span>了</span><span>BIOS</span><span>中的中断例程。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>BIOS</span></strong><strong><span>和</span><span>DOS</span></strong><strong><span>中断例程的安装过程</span></strong></p>
<p><span>前面我们都是自己编写中断例程，将它们放到安装程序中，然后运行安装程序，将它们安装到指定的内存区中。此后，别的应用程序才可以调用。</span></p>
<p>&nbsp;</p>
<p><span>而</span><span>BISO</span><span>和</span><span>DOS</span><span>提供的中断例程是如何安装到内存中的呢？</span></p>
<p><span>1</span><span>）开机后，</span><span>CPU</span><span>一加电，初始化</span><span>(CS)=0FFFFH</span><span>，</span><span>(IP)=0</span><span>，自动从</span><span>FFFF:0</span><span>单元开始执行程序。</span><span>FFFF:0</span><span>处有一条转跳指令，</span><span>CPU</span><span>执行该指令后，转去执行</span><span>BIOS</span><span>中的硬件系统检测和初始化程序。</span></p>
<p><span>2</span><span>）初始化程序将建立</span><span>BIOS</span><span>所支持的中断向量，即将</span><span>BIOS</span><span>提供的中断例程的入口地址登记在中断向量表中。注意，对于</span><span>BIOS</span><span>所提供的中断例程，只需要将入口地址登记在中断向量表中即可，因为它们是固化到</span><span>ROM</span><span>中的程序，一直在内存中存在。</span></p>
<p><span>3</span><span>）硬件系统检测和初始化完成后，调用</span><span>int 19h</span><span>进行操作系统的引导。从此将计算机交由操作系统控制。</span></p>
<p><span>4</span><span>）</span><span>DOS</span><span>启动后，除完成其他工作外，还将它所提供的中断例程装入内存，并建立相应的中断向量。</span></p>
<p>&nbsp;</p>
<p><span>检测点</span><span>13.2</span></p>
<p><span>1</span><span>）我们可以编程改变</span><span>FFFF:0</span><span>处的指令，使得</span><span>CPU</span><span>不去执行</span><span>BIOS</span><span>中的硬件系统检测和初始化程序。错，此处内存单元为只读，无法改写。</span></p>
<p><span>2</span><span>）</span><span>int 19h</span><span>中断例程，可以由</span><span>DOS</span><span>提供。错，</span><span>int 19h</span><span>指令是用于引导操作系统的比如</span><span>DOS</span><span>，</span><span>DOS</span><span>没有机会改变它。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>BIOS</span></strong><strong><span>中断例程应用</span></strong></p>
<p><span>int 10h</span><span>中断例程是</span><span>BIOS</span><span>提供的中断例程，其中包含了多个和屏幕输出相关的子程序。</span></p>
<p><span>一般来说，一个供程序员调用的中断例程中往往包括多个子程序，中断例程内部用传递进来的参数来决定执行哪一个子程序。</span><span>BIOS</span><span>和</span><span>DOS</span><span>提供的中断例程，都用</span><span>ah</span><span>来传递内部子程序的编号。</span></p>
<p>&nbsp;</p>
<p><span>我们看一下</span><span>int 10h</span><span>中断例程的设置光标位置功能。</span></p>
<p><span>mov ah,2<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>置光标</span></p>
<p><span>mov bh,0<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>第</span><span>0</span><span>页</span></p>
<p><span>mov dh,5<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>行号</span></p>
<p><span>mov dl,12<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>列号</span></p>
<p><span>int 10h</span></p>
<p>&nbsp;</p>
<p><span>(ah)=2</span><span>表示调用第</span><span>10h</span><span>号中断例程的</span><span>2</span><span>号子程序，功能为设置光标位置，可以提供光标所在的行号</span><span>(80*25</span><span>字符模式中：</span><span>0~24)</span><span>、列号（</span><span>80*25</span><span>字符模式下：</span><span>0~79</span><span>），和页号作为参数。</span></p>
<p><span>(bh)=0</span><span>，</span><span>(dh)=5</span><span>，</span><span>(dl)=12</span><span>，设置光标到第</span><span>0</span><span>页，第</span><span>5</span><span>页，第</span><span>12</span><span>列。</span></p>
<p>&nbsp;</p>
<p><span>bh</span><span>中页号的含义：内存地址空间中，</span><span>B8000h~BFFFFh</span><span>共</span><span>32K</span><span>的空间，为</span><span>80*25</span><span>彩色字符模式的显示缓冲区。一屏的内容在显示缓冲区中共占</span><span>4000</span><span>字节。</span></p>
<p>&nbsp;</p>
<p><span>显示缓冲区分为</span><span>8</span><span>页，每页</span><span>4K</span><span>（≈</span><span>4000</span><span>），显示器可以显示任意一页的内容。一般情况下，显示第</span><span>0</span><span>页的内容。也就是说，通常情况下，</span><span>B8000~B<st1:chmetcnv w:st="on" UnitName="F" SourceValue="8" HasSpace="False" Negative="False" NumberType="1" TCSC="0">8F</st1:chmetcnv><st1:chmetcnv w:st="on" UnitName="F" SourceValue="9" HasSpace="False" Negative="False" NumberType="1" TCSC="0">9F</st1:chmetcnv></span><span>中的</span><span>4000</span><span>个字节的内容将出现在显示器上。</span><span>(2*80*25=4000</span><span>个字节</span><span>)</span></p>
<p>&nbsp;</p>
<p><span>再看一下</span><span>int 10h</span><span>中断例程的在光标位置显示字符功能。</span></p>
<p><span>mov ah,9<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>置光标</span></p>
<p><span>mov al,&#8217;a&#8217;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>字符</span></p>
<p><span>mov bl,7<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>颜色属性</span></p>
<p><span>mov bh,0<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>第</span><span>0</span><span>页</span></p>
<p><span>mov cx,3<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>字符重复个数</span></p>
<p><span>int 10h</span></p>
<p>&nbsp;</p>
<p><span>(ah)=9</span><span>表示调用第</span><span>10h</span><span>号中断例程的</span><span>9</span><span>号子程序，功能为在光标位置显示字符，可以提供要显示的字符、颜色属性、页号、字符重复个数作为参数。</span></p>
<p><span>bh</span><span>中的颜色属性的格式如下：</span></p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=64>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=63>
            <p align=center><strong><span>7</span></strong></p>
            </td>
            <td vAlign=top width=63>
            <p align=center><strong><span>6</span></strong></p>
            </td>
            <td vAlign=top width=63>
            <p align=center><strong><span>5</span></strong></p>
            </td>
            <td vAlign=top width=63>
            <p align=center><strong><span>4</span></strong></p>
            </td>
            <td vAlign=top width=63>
            <p align=center><strong><span>3</span></strong></p>
            </td>
            <td vAlign=top width=63>
            <p align=center><strong><span>2</span></strong></p>
            </td>
            <td vAlign=top width=62>
            <p align=center><strong><span>1</span></strong></p>
            </td>
            <td vAlign=top width=63>
            <p align=center><strong><span>0</span></strong></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=64>
            <p align=center><span>含义</span></p>
            </td>
            <td vAlign=top width=63>
            <p align=center><strong><span>BL</span></strong></p>
            </td>
            <td vAlign=top width=63>
            <p align=center><strong><span>R</span></strong></p>
            </td>
            <td vAlign=top width=63>
            <p align=center><strong><span>G</span></strong></p>
            </td>
            <td vAlign=top width=63>
            <p align=center><strong><span>B</span></strong></p>
            </td>
            <td vAlign=top width=63>
            <p align=center><strong><span>I</span></strong></p>
            </td>
            <td vAlign=top width=63>
            <p align=center><strong><span>R</span></strong></p>
            </td>
            <td vAlign=top width=62>
            <p align=center><strong><span>G</span></strong></p>
            </td>
            <td vAlign=top width=63>
            <p align=center><strong><span>B</span></strong></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=64>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=63>
            <p align=center><span>闪烁</span></p>
            </td>
            <td vAlign=top width=63>
            <p align=center><span>红色</span></p>
            </td>
            <td vAlign=top width=63>
            <p align=center><span>绿色</span></p>
            </td>
            <td vAlign=top width=63>
            <p align=center><span>蓝色</span></p>
            </td>
            <td vAlign=top width=63>
            <p align=center><span>高亮</span></p>
            </td>
            <td vAlign=top width=63>
            <p align=center><span>红色</span></p>
            </td>
            <td vAlign=top width=62>
            <p align=center><span>绿色</span></p>
            </td>
            <td vAlign=top width=63>
            <p align=center><span>蓝色</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=64>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=63>
            <p align=center>&nbsp;</p>
            </td>
            <td vAlign=top width=189 colSpan=3>
            <p align=center><span>背景</span></p>
            </td>
            <td vAlign=top width=63>
            <p align=center>&nbsp;</p>
            </td>
            <td vAlign=top width=189 colSpan=3>
            <p align=center><span>前景</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=64>
            <p align=center><span>例子</span></p>
            </td>
            <td vAlign=top width=63>
            <p align=center><span>1</span></p>
            </td>
            <td vAlign=top width=63>
            <p align=center><span>1</span></p>
            </td>
            <td vAlign=top width=63>
            <p align=center>&nbsp;</p>
            </td>
            <td vAlign=top width=63>
            <p align=center>&nbsp;</p>
            </td>
            <td vAlign=top width=63>
            <p align=center><span>1</span></p>
            </td>
            <td vAlign=top width=63>
            <p align=center>&nbsp;</p>
            </td>
            <td vAlign=top width=62>
            <p align=center><span>1</span></p>
            </td>
            <td vAlign=top width=63>
            <p align=center>&nbsp;</p>
            </td>
        </tr>
    </tbody>
</table>
<p><span>与显存中的属性字节的格式相同。</span></p>
<p>&nbsp;</p>
<p><span>编程：在屏幕的</span><span>5</span><span>行</span><span>12</span><span>列显示</span><span>3</span><span>个红底高亮闪烁绿色的</span><span>&#8217;a&#8217;</span><span>。</span></p>
<p><span>assume cs:code</span></p>
<p><span>code segment</span></p>
<p><span>&nbsp;start:&nbsp;mov ah,2<span>&nbsp;&nbsp;&nbsp; </span>;</span><span>置光标</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bh,0<span>&nbsp;&nbsp;&nbsp; </span>;</span><span>第</span><span>0</span><span>页</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov dh,5<span>&nbsp;&nbsp;&nbsp; </span>;</span><span>行号</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov dl,12<span>&nbsp;&nbsp; </span>;</span><span>列号</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 10h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ah,9<span>&nbsp;&nbsp;&nbsp; </span>;</span><span>置光标</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov al,'a'&nbsp;;</span><span>字符</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bl,11001010b&nbsp;;</span><span>颜色属性，闪烁的效果必须在全屏</span><span>DOS</span><span>方式下才能看到。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bh,0<span>&nbsp;&nbsp;&nbsp; </span>;</span><span>第</span><span>0</span><span>页</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cx,3<span>&nbsp;&nbsp;&nbsp; </span>;</span><span>字符重复个数</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 10h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>DOS</span></strong><strong><span>中断例程应用</span></strong></p>
<p><span>int 21h</span><span>中断例程是</span><span>DOS</span><span>提供的中断例程，其中包含了</span><span>DOS</span><span>提供给程序员在编程时调用的子程序。</span></p>
<p>&nbsp;</p>
<p><span>我们从前一直使用的是</span><span>int 21h</span><span>中断例程的</span><span>4ch</span><span>号功能。即程序返回功能，如下：</span></p>
<p><span>mov ah,4ch<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>程序返回</span></p>
<p><span>mov al,0<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>返回值</span></p>
<p><span>int 21h</span></p>
<p><span>(ah)=4ch</span><span>表示调用第</span><span>21h</span><span>号中断例程的</span><span>4ch</span><span>号子程序，功能为程序返回，可以提供返回值作为参数。</span></p>
<p>&nbsp;</p>
<p><span>我们前面使用这个功能的时候经常写作：</span></p>
<p><span>mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span>int 21h</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>我们看一下</span><span>int 21h</span><span>中断例程的在光标位置显示字符串功能：</span></p>
<p><span>ds:dx </span><span>指向字符串</span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>要显示的字符串需用&#8220;</span><span>$</span><span>&#8221;作为结束符</span></p>
<p><span>mov ah,9<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>功能号</span><span>9</span><span>，表示在光标位置显示字符串</span></p>
<p><span>int 21h</span></p>
<p>&nbsp;</p>
<p><span>(ah)=9</span><span>表示调用第</span><span>21h</span><span>号中断例程的</span><span>9</span><span>号子程序，功能为在光标位置显示字符串，可以提供要显示字符串的地址作为参数。</span></p>
<p>&nbsp;</p>
<p><span>编程：在屏幕的</span><span>5</span><span>行</span><span>12</span><span>列显示字符串&#8220;</span><span>Welcome to masm!</span><span>&#8221;。</span></p>
<p><span>;</span><span>在屏幕的</span><span>5</span><span>行</span><span>12</span><span>列显示字符串&#8220;</span><span>Welcome to masm!</span><span>&#8221;</span></p>
<p><span>assume cs:code</span></p>
<p><span>data segment</span></p>
<p><span>&nbsp;db 'Welcome to masm!','$' ;'$'</span><span>本身不显示，只起到边界的作用。</span></p>
<p><span>data ends</span></p>
<p>&nbsp;</p>
<p><span>code segment</span></p>
<p><span>&nbsp;start:mov ah,2<span>&nbsp;&nbsp;&nbsp; </span>;</span><span>置光标</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bh,0<span>&nbsp;&nbsp;&nbsp; </span>;</span><span>第</span><span>0</span><span>页</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov dh,5<span>&nbsp;&nbsp;&nbsp; </span>;</span><span>行号</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov dl,12<span>&nbsp;&nbsp; </span>;</span><span>列号</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 10h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,data</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ds,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov dx,0<span>&nbsp;&nbsp;&nbsp; </span>;ds:dx</span><span>指向字符串地首地址</span><span>data:0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ah,9</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p><span>&nbsp;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p><span>上述程序在屏幕的</span><span>5</span><span>行</span><span>12</span><span>列显示字符串&#8220;</span><span>Welcome to masm</span><span>&#8221;，直到遇见&#8220;</span><span>$</span><span>&#8221;（&#8220;</span><span>$</span><span>&#8221;本身并不显示，只起到边界的作用）。</span></p>
<p>&nbsp;</p>
<p><span>如果字符串比较长，遇到行尾，程序会自动转到下一行开头处继续显示；如果到了最后一行，还能自动上卷一行。</span></p>
<p>&nbsp;</p>
<p><span>DOS</span><span>为程序员提供了许多可以调用的子程序，都包含在</span><span>int 21h</span><span>中断例程中，我们这里只对原理进行了讲解，对于</span><span>DOS</span><span>提供的所有可调用子程序的情况，读者可以参考相关的书籍。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/122174.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-04 10:36 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/04/122174.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编语言--内中断 </title><link>http://www.cppblog.com/luqingfei/archive/2010/08/04/122172.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Wed, 04 Aug 2010 02:35:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/04/122172.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/122172.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/04/122172.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/122172.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/122172.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>内中断</span></p>
<p><span>任何一个通用的</span><span>CPU</span><span>，比如</span><span>8086</span><span>，都具备一种能力，可以在执行完当前正在执行的指令之后，检测到从</span><span>CPU</span><span>外部发送过来的或内部产生的一种特殊信息，并且可以立即对所接收到信息进行处理。这种特殊的信息，我们可以称其为：中断信息。</span></p>
<p><span>中断的意思是指，</span><span>CPU</span><span>不再接着（刚执行完的指令）向下执行，而是转去处理这个特殊信息。</span></p>
<p>&nbsp;</p>
<p><span>注意，这里所说的中断信息，是为了便于理解而采用的一种逻辑上的说法。它是对几个具有先后顺序的硬件操作所产生的事件的统一描述。</span></p>
<p><span>&#8220;中断信息&#8221;是要求</span><span>CPU</span><span>马上进行某种处理，并向所要进行的该种处理提供了必备的参数的通知信息。</span></p>
<p>&nbsp;</p>
<p><span>中断信息可以来自</span><span>CPU</span><span>的内部和外部。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>内中断的产生</span></strong></p>
<p><span>对于</span><span>8086CPU</span><span>，当</span><span>CPU</span><span>内部有下面的情况发生的时候，将产生相应的中断信息：</span></p>
<p><span><span>1）&nbsp;</span></span><span>除法错误，比如：执行</span><span>div</span><span>指令产生的除法溢出；</span></p>
<p><span><span>2）&nbsp;</span></span><span>单步执行；</span></p>
<p><span><span>3）&nbsp;</span></span><span>执行</span><span>int0</span><span>指令；</span></p>
<p><span><span>4）&nbsp;</span></span><span>执行</span><span>int</span><span>指令。</span></p>
<p>&nbsp;</p>
<p><span>8086CPU</span><span>用称为中断类型码的数据来标识中断信息的来源。中断类型码为一个字节数据，可以表示</span><span>256</span><span>种中断信息的来源。以后，我们将产生中断信息的事件，即中断信息的来源，简称为中断源，上述的</span><span>4</span><span>种中断源，在</span><span>8086CPU</span><span>中的中断类型码如下：</span></p>
<p><span><span>1）&nbsp;</span></span><span>除法错误：</span><span>0</span></p>
<p><span><span>2）&nbsp;</span></span><span>单步执行：</span><span>1</span></p>
<p><span><span>3）&nbsp;</span></span><span>执行</span><span>int0</span><span>指令：</span><span>4</span></p>
<p><span><span>4）&nbsp;</span></span><span>执行</span><span>int</span><span>指令，该指令的格式为</span><span>int n</span><span>，指令中的</span><span>n</span><span>为字节型立即数，是提供给</span><span>CPU</span><span>的中断类型码。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>中断处理程序</span></strong></p>
<p><span>CPU</span><span>收到中断信息后，需要对中断信息进行处理。</span></p>
<p><span>而如何对中断信息进行处理，可以由我们编程决定。</span></p>
<p><span>我们编写的，用来处理中断信息的程序被称为中断处理程序。</span></p>
<p><span>一般来说，需要对不同的中断信息编写不同的处理程序。</span></p>
<p>&nbsp;</p>
<p><span>CPU</span><span>在收到中断信息后，应该转去执行该中断信息的处理程序。</span></p>
<p>&nbsp;</p>
<p><span>若要</span><span>8086CPU</span><span>执行某处的程序，就要将</span><span>CS:IP</span><span>指向它的入口（即程序第一条指令的地址）。</span></p>
<p>&nbsp;</p>
<p><span>可见首要的问题是，</span><span>CPU</span><span>在收到中断信息后，如何根据中断信息确定其处理程序的入口。</span></p>
<p>&nbsp;</p>
<p><span>CPU</span><span>的设计者必须在中断信息和其处理程序的入口地址之间建立某种联系，使得</span><span>CPU</span><span>根据中断信息可以找到要执行的处理程序。</span></p>
<p>&nbsp;</p>
<p><span>中断信息中包含有标识中断源的类型码。根据</span><span>CPU</span><span>的设计，中断类型码的作用就是用来定位中断处理程序。</span></p>
<p><span>若要定位中断处理程序，需要知道它的段地址和偏移地址，而如何根据</span><span>8</span><span>位的中断类型码得到中断处理程序的段地址和偏移地址呢？</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>中断向量表</span></strong></p>
<p><span>CPU</span><span>用</span><span>8</span><span>位的中断类型码通过中断向量表找到相应的中断处理程序的入口地址。</span></p>
<p><span>中断向量表就是中断向量的列表，中断向量，就是中断处理程序的入口地址。</span></p>
<p><span>中断向量表，就是中断处理程序入口地址的列表。</span></p>
<p>&nbsp;</p>
<p><span>中断向量表在内存中保存，其中存放着</span><span>256</span><span>个中断源所对应的中断处理程序的入口。</span></p>
<p>&nbsp;</p>
<p><span>CPU</span><span>只要知道了中断类型码，就可以将中断类型码作为中断向量表的表项号，定位相应的表项，从而得到中断处理程序的入口地址。</span></p>
<p>&nbsp;</p>
<p><span>中断向量表在内存中存放，对于</span><span>8086PC</span><span>机，中断向量表指定放在内存地址</span><span>0</span><span>处。从内存</span><span>0000:0000</span><span>到</span><span>0000:03e8</span><span>的</span><span>1000</span><span>个单元中存放着中断向量表。这是规定。</span></p>
<p>&nbsp;</p>
<p><span>一个表项存放一个中断向量，也就是一个中断处理程序的入口地址，对于</span><span>8086CPU</span><span>，这个入口地址包括段地址和偏移地址，所以一个表项占两个字，四个字节，高地址字存放段地址，低地址字存放偏移地址。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>存储</span><span>N</span><span>号中断源对应的中断处理程序入口的偏移地址的内存单元的地址为：</span><span>N*4</span><span>；</span></p>
<p><span>存储</span><span>N</span><span>号中断源对应的中断处理程序入口的段地址的内存单元的地址为：</span><span>N*4+2</span><span>。</span></p>
<p><span>（</span><span>N</span><span>从</span><span>0</span><span>开始。）</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>中断过程</span></strong></p>
<p><span>可以用中断类型码，在中断向量表中找到中断处理程序的入口。</span></p>
<p><span>找到这个入口地址的最终目的是用它设置</span><span>CS</span><span>和</span><span>IP</span><span>，使</span><span>CPU</span><span>执行中断处理程序。</span></p>
<p>&nbsp;</p>
<p><span>用中断类型码找到中断向量，并用它设置</span><span>CS</span><span>和</span><span>IP</span><span>，这个工作是由</span><span>CPU</span><span>的硬件自动完成的。</span></p>
<p><span>CPU</span><span>硬件完成这个工作的过得被称为中断过程。</span></p>
<p>&nbsp;</p>
<p><span>CPU</span><span>收到中断信息后，要对中断信息进行处理，首先将引发中断过程。硬件在完成中断过程后，</span><span>CS:IP</span><span>将指向中断处理程序的入口，</span><span>CPU</span><span>开始执行中断处理程序。</span></p>
<p>&nbsp;</p>
<p><span>8086CPU</span><span>在收到中断信息后，所引发的中断过程：</span></p>
<p><span>1</span><span>）（从中断信息中）取得中断类型码；</span></p>
<p><span>2</span><span>）标志寄存器的值入栈；（因为在中断过程中要改变标志寄存器的值，所以先将其保存在栈中。）</span></p>
<p><span>3</span><span>）设置标志寄存器的第</span><span>8</span><span>位</span><span>TF</span><span>和第</span><span>9</span><span>位</span><span>IF</span><span>的值为</span><span>0</span><span>；</span></p>
<p><span>4</span><span>）</span><span>CS</span><span>的内容入栈；</span></p>
<p><span>5</span><span>）</span><span>IP</span><span>的内容入栈；</span></p>
<p><span>6</span><span>）从内存地址为中断类型码</span><span>*4</span><span>和中断类型码</span><span>*4+2</span><span>的两个字单元中读取中断处理程序的入口地址设置</span><span>IP</span><span>和</span><span>CS</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>CPU</span><span>在收到中断信息之后，到处理该中断信息，就完成一个由硬件自动执行的中断过程（程序员无法改变这个过程中所要做的工作）。</span></p>
<p>&nbsp;</p>
<p><span>中断过程的主要任务就是用中断类型码在中断向量表中找到中断处理程序的入口地址，设置</span><span>CS</span><span>和</span><span>IP</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>因为中断处理程序执行完成后，</span><span>CPU</span><span>还要回过头来继续执行被中断的程序，所以要在设置</span><span>CS</span><span>、</span><span>IP</span><span>之前，先将它们的值保存起来。</span></p>
<p>&nbsp;</p>
<p><span>因为在执行中断处理程序后，需要恢复在进入中断处理程序之前的</span><span>CPU</span><span>现场（某一时刻，</span><span>CPU</span><span>中各个寄存器的值）。所以应该在修改标记寄存器之前，将它的值入栈保存。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>我们更简洁地描述中断过程，如下：</span></p>
<p><span><span>1）&nbsp;</span></span><span>取得中断类型码</span><span>N</span><span>；</span></p>
<p><span><span>2）&nbsp;</span></span><span>pushf</span></p>
<p><span><span>3）&nbsp;</span></span><span>TF=0</span><span>，</span><span>IF=0</span></p>
<p><span><span>4）&nbsp;</span></span><span>push CS</span></p>
<p><span><span>5）&nbsp;</span></span><span>push IP</span></p>
<p><span><span>6）&nbsp;</span></span><span>(IP)=(N*4), (CS)=(N*4+2)</span></p>
<p><span>在最后一步完成后，</span><span>CPU</span><span>开始执行由程序员编写的中断处理程序。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>中断处理程序</span></strong></p>
<p><span>由于</span><span>CPU</span><span>随时随地可能检测到中断信息，也就是说，</span><span>CPU</span><span>随时随地可能执行中断处理程序，所以中断处理程序必须一直存储在内存某段究竟之中。而中断处理程序的入口地址，即中断向量，处理存储在对应的中断向量表表项中。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>中断处理程序的编写方法和子程序的比较相似，常规步骤如下：</span></p>
<p><span><span>1）&nbsp;</span></span><span>保存用到的寄存器。</span></p>
<p><span><span>2）&nbsp;</span></span><span>处理中断。</span></p>
<p><span><span>3）&nbsp;</span></span><span>恢复用到的寄存器。</span></p>
<p><span><span>4）&nbsp;</span></span><span>用</span><span>iret</span><span>指令返回。</span></p>
<p>&nbsp;</p>
<p><span>iret</span><span>指令的功能用汇编语法描述为：</span></p>
<p><span>pop IP</span></p>
<p><span>pop CS</span></p>
<p><span>popf</span></p>
<p><span>iret</span><span>通常和硬件自动完成的中断过程配合使用。</span></p>
<p><span>iret</span><span>指令执行后，</span><span>CPU</span><span>回到执行中断处理程序前的执行点继续执行程序。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>除法错误中断的处理</span></strong></p>
<p><span>当</span><span>CPU</span><span>执行</span><span>div</span><span>等除法指令的时候，如果发生了除法溢出错误，将产生中断类型码为</span><span>0</span><span>的中断信息，</span><span>CPU</span><span>将检测到这个信息，然后引发中断过程，转去执行</span><span>0</span><span>号中断所对应的中断处理程序。</span></p>
<p><span>mov ax,1000h</span></p>
<p><span>mov bh,1</span></p>
<p><span>div bh</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>编程处理</span><span>0</span></strong><strong><span>号中断</span></strong></p>
<p><span>编程：当发生除法溢出时，在屏幕中间显示&#8220;</span><span>overflow!</span><span>&#8221;，返回</span><span>DOS</span><span>。</span></p>
<p><span>分析：</span></p>
<p><span>（</span><span>1</span><span>）当发生除法溢出的时候，产生</span><span>0</span><span>号中断信息，从而引发中断过程。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>此时，</span><span>CPU</span><span>将进行以下工作：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>1</span><span>）取得中断类型码</span><span>0</span><span>；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>2</span><span>）标志寄存器入栈，</span><span>TF</span><span>、</span><span>IF</span><span>设置为</span><span>0</span><span>；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>3</span><span>）</span><span>CS</span><span>、</span><span>IP</span><span>入栈；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>4</span><span>）</span><span>(IP)=(0*4), (CS)=(0*4+2)</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>（</span><span>2</span><span>）可见，当中断</span><span>0</span><span>发生时，</span><span>CPU</span><span>将转去执行中断处理程序。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>只要按如下步骤编写中断处理程序，当中断</span><span>0</span><span>发生时，即可显示&#8220;</span><span>overflow!</span><span>&#8221;。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>1</span><span>）相关处理。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>2</span><span>）向显示缓冲区送字符串&#8220;</span><span>overflow!</span><span>&#8221;。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>3</span><span>）返回</span><span>DOS</span><span>。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>我们将这段程序称为：</span><span>do0</span><span>。</span></p>
<p><span>（</span><span>3</span><span>）现在的问题是：</span><span>do0</span><span>应存放在内存中。因为除法溢出随时可能发生，</span><span>CPU</span><span>随时都可能将</span><span>CS:IP</span><span>指向</span><span>do0</span><span>的入口，执行程序。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>那么</span><span>do0</span><span>应该放在哪里呢？</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>由于我们是在操作系统之上使用计算机，所有的硬件资源都在操作系统的管理之下，所以我们要想得到一块内存区存放</span><span>do0</span><span>，应该向操作系统申请。</span></p>
<p><span><span>n<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>但在这里出于两个原因我们不想这样做：</span></p>
<p><span><span>n<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>过多地讨论申请内存将偏离问题的主线；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>我们学习汇编的一个重要目的就是要获得对计算机底层的编程体验。所以，在可能的情况下，我们不去理会操作系统，而直接面向硬件资源。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>问题变得简单而直接，我们只需找到一块别的程序不会用到的内存区，将</span><span>do0</span><span>传送到其中即可。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>内存</span><span>0000:0000~0000:03E8</span><span>，大小约</span><span>1KB</span><span>的空间是系统存放中断处理程序入口地址的中断向量表。</span><span>8086</span><span>支持</span><span>256</span><span>个中断，但是，实际上，系统中要处理的中断事件远没有达到</span><span>256</span><span>个。所以在中断向量表中，有许多单元是空的。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>中断向量表是</span><span>PC</span><span>系统中最重要的内存区，只用来存放中断处理程序的入口地址，</span><span>DOS</span><span>系统和其他应用程序都不会随便使用这段空间。我们可以利用中断向量表中的空闲单元来存放我们的程序。一般情况下，从</span><span>0000:0200</span><span>至</span><span>0000:0300</span><span>的</span><span>256</span><span>个字节。</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>结论：我们可以将</span><span>do0</span><span>传送到内存</span><span>0000:0200</span><span>处。</span></p>
<p>&nbsp;</p>
<p><span>（</span><span>4</span><span>）我们将中断处理程序</span><span>do0</span><span>放到</span><span>0000:0200</span><span>后，若要使得除法溢出发生的时候，</span><span>CPU</span><span>转去执行</span><span>do0</span><span>，则必须将</span><span>do0</span><span>的入口地址，即</span><span>0000:0200</span><span>登记在中断向量表的对庆表项中。因为除法溢出对应的中断类型码为</span><span>0</span><span>，它的中断处理程序的入口地址应该从</span><span>0</span><span>&#215;</span><span>4</span><span>字单元开始存放，<span>段地址存放在</span></span><span>0</span><span>&#215;</span><span>4+2</span><span>字单元中，偏移地址存放在</span><span>0</span><span>&#215;</span><span>4</span><span>字单元中</span><span>。也就是说要将</span><span>do0</span><span>的段地址</span><span>0</span><span>存放在</span><span>0000:0002</span><span>字单元中，将偏移地址</span><span>200H</span><span>存放在</span><span>0000:0000</span><span>字单元中。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>总结上面的分析，我们要做以下几件事情：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（</span><span>1</span><span>）编写可以显示&#8220;</span><span>overflow!</span><span>&#8221;的中断处理程序：</span><span>do0</span><span>；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（</span><span>2</span><span>）将</span><span>do0</span><span>送入内存</span><span>0000:0200</span><span>处；</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>（</span><span>3</span><span>）将</span><span>do0</span><span>的入口地址</span><span>0000:0200</span><span>存储在中断向量表</span><span>0</span><span>号表项中。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>程序的框架如下：</span></p>
<p><span>assume cs:code</span></p>
<p><span>code segment</span></p>
<p><span>start: do0</span><span>安装程序</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;</span><span>设置中断向量表</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;int 21h</span></p>
<p>&nbsp;</p>
<p><span>do0:&nbsp;</span><span>显示字符串&#8220;</span><span>overflow!</span><span>&#8221;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;int 21h</span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p><span>上面的程序分为两部分：</span></p>
<p><span>（</span><span>1</span><span>）安装</span><span>do0</span><span>，设置中断向量的程序；</span></p>
<p><span>（</span><span>2</span><span>）</span><span>do0</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>do0</span><span>的代码是不执行的，它只是作为</span><span>do0</span><span>安装程序所要传送的数据。</span></p>
<p><span>首先执行</span><span>do0</span><span>安装程序，将</span><span>do0</span><span>的代码拷贝到内存</span><span>0:200</span><span>处，然后设置中断向量表，将</span><span>do0</span><span>的入口地址，即偏移地址</span><span>200H</span><span>和段地址</span><span>0</span><span>，保存在</span><span>0</span><span>号项中。这两部分工作完成后，程序就返回了。</span></p>
<p><span>程序的目的就是在内存</span><span>0:200</span><span>处安装</span><span>do0</span><span>的代码，将</span><span>0</span><span>号中断处理程序的入口地址设置为</span><span>0:200</span><span>。</span><span>do0</span><span>的代码虽然在程序中，却不在程序执行的时候执行。它是在除法溢出发生的时候才得以执行的中断处理程序。</span></p>
<p>&nbsp;</p>
<p><span>do0</span><span>部分代码的最后两条指令是依照我们的编程要求，用来返回</span><span>DOS</span><span>的。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>现在，我们再反过来从</span><span>CPU</span><span>的角度看一下，什么是中断处理程序？我们来看一下</span><span>do0</span><span>是如何变成</span><span>0</span><span>号中断的中断处理程序的：</span></p>
<p><span>（</span><span>1</span><span>）主程序在执行时，被加载到内存中，此时</span><span>do0</span><span>的代码在程序所在的内存空间中，它只是存放在主程序代码段中的一段要被传送到其他单元中的数据，我们不能说它是</span><span>0</span><span>号中断的中断处理程序；</span></p>
<p><span>（</span><span>2</span><span>）主程序中安装</span><span>do0</span><span>的代码执行完后，</span><span>do0</span><span>的代码被从主程序的代码段中拷贝到</span><span>0:200</span><span>处。此时，我们也不能说它是</span><span>0</span><span>号中断的中断处理程序，它只不过是存放在</span><span>0:200</span><span>处的一些数据；</span></p>
<p><span>（</span><span>3</span><span>）主程序中设置中断向量表的代码执行完后，在</span><span>0</span><span>号表项中填入了</span><span>do0</span><span>的入口地址</span><span>0:200</span><span>，此时</span><span>0:200</span><span>处的信息，即</span><span>do0</span><span>的代码，就变成了</span><span>0</span><span>号中断的中断处理程序。因为当除法溢出（即</span><span>0</span><span>号中断）发生时，</span><span>CPU</span><span>将执行</span><span>0:200</span><span>处的代码。</span></p>
<p>&nbsp;</p>
<p><span>回忆以下：</span></p>
<p><span>如何让一个内存单元成为栈顶？将它的地址放入</span><span>SS</span><span>、</span><span>SP</span><span>中；</span></p>
<p><span>如何让一个内存单元中的信息被</span><span>CPU</span><span>当作指令来执行？将它的地址放入</span><span>CS</span><span>、</span><span>IP</span><span>中；</span></p>
<p><span>如何让一段程序成为</span><span>N</span><span>号中断的中断处理程序？将它的入口地址放入中断向量表的</span><span>N</span><span>号表项中。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>安装</span></strong></p>
<p><span>使用</span><span>movsb</span><span>指令，将</span><span>do0</span><span>的代码送入</span><span>0:200</span><span>处。程序如下：</span></p>
<p><span>assume cs:code</span></p>
<p><span>code segment</span></p>
<p><span>&nbsp;start:<span>&nbsp;&nbsp;&nbsp; </span></span><span>设置</span><span>es:di</span><span>指向目的地址</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>设置</span><span>ds:si</span><span>指向源地址</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>设置</span><span>cx</span><span>为传输长度</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>设置传输方向为正</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>rep movsb</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>设置中断向量表</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>do0:<span> </span></span><span>显示字符串&#8220;</span><span>overflow!</span><span>&#8221;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p><span>用</span><span>rep movsb</span><span>指令的时候要确定的信息如下：</span></p>
<p><span>（</span><span>1</span><span>）传送的原始位置，段地址：</span><span>code</span><span>，偏移地址：</span><span>offset do0</span><span>；</span></p>
<p><span>（</span><span>2</span><span>）传送的目的位置：</span><span>0:200</span><span>；</span></p>
<p><span>（</span><span>3</span><span>）传送的长度：</span><span>do0</span><span>部分代码的长度；</span></p>
<p><span>（</span><span>4</span><span>）传送的方向：正向。</span></p>
<p>&nbsp;</p>
<p><span>更明确的程序如下：</span></p>
<p><span>assume cs:code</span></p>
<p><span>code segment</span></p>
<p><span>&nbsp;start:<span>&nbsp;&nbsp;&nbsp; </span>mov ax,cs</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ds,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov si,offset do0<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>ds:si</span><span>指向源地址</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov di,200h<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>es:di</span><span>指向目的地址</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cx do0</span><span>部分代码的长度</span><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>cx</span><span>为传输长度</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cld<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置传输方向为正</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>rep movsb</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>设置中断向量表</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p>&nbsp;</p>
<p><span>do0:<span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>显示字符串&#8220;</span><span>overflow!</span><span>&#8221;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p><span>如何知道</span><span>do0</span><span>代码的长度？</span></p>
<p><span>可以利用编译器来计算</span><span>do0</span><span>的长度，如下所示：</span></p>
<p><span>assume cs:code</span></p>
<p><span>code segment</span></p>
<p><span>&nbsp;start:<span>&nbsp;&nbsp;&nbsp; </span>mov ax,cs</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ds,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov si,offset do0<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>ds:si</span><span>指向源地址</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov di,200h<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>es:di</span><span>指向目的地址</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cx,<span>offset do0end-offset do0</span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>cx</span><span>为传输长度</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cld<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置传输方向为正</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>rep movsb</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>设置中断向量表</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p>&nbsp;</p>
<p><span>do0</span><span>:<span>&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>显示字符串&#8220;</span><span>overflow!</span><span>&#8221;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p><span>do0end</span><span>: nop</span></p>
<p><span>do0end:</span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span>&#8220;</span><span>-</span><span>&#8221;是编译器识别的运算符号，编译器可以用它来进行了两个常数的减法。</span><span>比如：</span></p>
<p><span>指令：</span><span>mov ax,8-4</span><span>，被编译器处理为指令：</span><span>mov ax,4</span></p>
<p>&nbsp;</p>
<p><span>汇编编译器可以处理表达式，</span><span>比如：</span></p>
<p><span>指令：</span><span>mov ax,(5+3)*5/10</span><span>，被编译器处理为指令：</span><span>mov ax,4</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>do0</span></p>
<p><span>do0</span><span>程序的主要任务是显示字符串，程序如下：</span></p>
<p><span>do0:<span> </span></span><span>设置</span><span>ds:si</span><span>指向字符串</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,0b800h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov di,12*150+36*2<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>es:di</span><span>指向显存空间的中间位置</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cx,9<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>cx</span><span>为字符串长度</span></p>
<p><span>&nbsp;s:<span>&nbsp;&nbsp; </span>mov al,[si]</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es:[di],al</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>inc si</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add di,2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>loop s</span></p>
<p><span>do0end:nop</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p>&nbsp;</p>
<p><span>程序写好了，可要显示的字符串放在哪里呢？</span></p>
<p><span>assume cs:code</span></p>
<p><span>data:segment</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db &#8220;overflow!&#8221;</span></p>
<p><span>data ends</span></p>
<p>&nbsp;</p>
<p><span>code segment</span></p>
<p><span>&nbsp;start:<span>&nbsp;&nbsp;&nbsp; </span>mov ax,cs</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ds,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov si,offset do0<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>ds:si</span><span>指向源地址</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov di,200h<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>es:di</span><span>指向目的地址</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cx, offset do0end-offset do0<span> </span>;</span><span>设置</span><span>cx</span><span>为传输长度</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cld<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置传输方向为正</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>rep movsb</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>设置中断向量表</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov <st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p><span>do0:<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,data</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ds,ax</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov si,0<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>ds:si</span><span>指向字符串</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,0b800h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov di,12*160+36*2<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>es:di</span><span>指向显存空间中的中间位置</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cx,9<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>设置</span><span>cx</span><span>为字符串长度</span></p>
<p><span>&nbsp;s:<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>mov al,[si]</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>move s:[di],al</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>inc<span>&nbsp;&nbsp; </span>si</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add di,2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>loop s</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p><span>&nbsp;do0end:nop</span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p><span>上述程序有一处大错，注意字符串&#8220;</span><span>overflow!</span><span>&#8221;，是在</span><span>data</span><span>段中。程序执行完成后返回，它所占用的内存空间被系统释放，而在其中存放的&#8220;</span><span>overflow!</span><span>&#8221;也将很可能被别的信息覆盖。</span></p>
<p><span>而</span><span>do0</span><span>程序被放到了</span><span>0:200</span><span>处，随时都会因发生了除法溢出而被</span><span>CPU</span><span>执行，很难保证</span><span>do0</span><span>程序从原来程序所处的空间中取得的是要显示的字符串&#8220;</span><span>overflow</span><span>&#8221;。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>因为</span><span>do0</span><span>程序随时可能被执行，而它要用到字符串&#8220;</span><span>overflow</span><span>&#8221;，所以该字符串也应该放在一段不会被覆盖的空间中。正确的程序如下：</span></p>
<p><span>assume cs:code</span></p>
<p><span>code segment</span></p>
<p><span>&nbsp;start:<span>&nbsp;&nbsp;&nbsp; </span>mov ax,cs</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ds,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov si,offset do0<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>ds:si</span><span>指向源地址</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>move s,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov di,200h<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>es:di</span><span>指向目的地址</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cx,offset do0end-offset do0<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>cx</span><span>为传输长度</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>cld<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置传输方向为正</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>rep movsb</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>设置中断向量表</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov <st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p><strong><span>&nbsp;do0:<span>&nbsp;&nbsp;&nbsp; </span>jmp short do0start</span></strong></p>
<p><strong><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db &#8220;overflow!&#8221;</span></strong></p>
<p><span>&nbsp;do0start:<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,cs</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ds,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov si,202h<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>ds:si</span><span>指向字符串</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,0b800h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es,ax</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov di,12*160+36*2<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>es:di</span><span>指向显存空间的中间位置</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov cx,9<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>cx</span><span>为字符串长度</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>s:<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>mov al,[si]</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov es:[di],al</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>inc si</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add di,2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>loop s</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>int 21h</span></p>
<p><span>&nbsp;do0end:nop</span></p>
<p><span>code ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p><span>上面的程序，将字符串&#8220;</span><span>overflow!</span><span>&#8221;放到了</span><span>do0</span><span>程序中，在程序执行时，将标号</span><span>do0</span><span>到标号</span><span>do0end</span><span>之间的内容送到</span><span>0000:0200</span><span>处。</span></p>
<p>&nbsp;</p>
<p><span>注意，因为在</span><span>do0</span><span>程序开始处的&#8220;</span><span>overflow!</span><span>&#8221;不是可以执行的代码，所以在&#8220;</span><span>overflow!</span><span>&#8221;之前加上一条</span><span>jmp</span><span>指令，转移到正式的</span><span>do0</span><span>程序，当除法溢出发生时，</span><span>CPU</span><span>执行</span><span>0:200</span><span>处的</span><span>jmp</span><span>指令，路过后面的字符串，转到正式的</span><span>do0</span><span>程序执行。</span></p>
<p>&nbsp;</p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>do0</span><span>程序执行过程中必须要找到&#8220;</span><span>overflow</span><span>！&#8221;，那么它在哪里呢？首先来看段地址，</span><span>&#8221;overflow!&#8221;</span><span>和</span><span>do0</span><span>的代码处于同一个段中，而除法溢出发生时，</span><span>CS</span><span>中必须存放</span><span>do0</span><span>的段地址，也就是&#8220;</span><span>overflow!</span><span>&#8221;的段地址；再来偏移地址，</span><span>0:200</span><span>处的指令为</span><span>jmp short do0start</span><span>，这条指令占两个字节，所以&#8220;</span><span>overflow!</span><span>&#8221;的偏移地址为</span><span>202h</span><span>。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>设置中断向量</span></strong></p>
<p><span>将</span><span>do0</span><span>的入口地址</span><span>0:200</span><span>，写入中断向量表的</span><span>0</span><span>号表项中，使</span><span>do0</span><span>成为</span><span>0</span><span>号中断的中断处理程序。</span></p>
<p><span>0</span><span>号表项的地址为</span><span>0:0</span><span>，其中</span><span>0:0</span><span>字单元存放偏移地址，</span><span>0:2</span><span>字单元存放段地址。程序如下：</span></p>
<p><span>mov ax,0</span></p>
<p><span>mov es,ax</span></p>
<p><span>mov word ptr es:[0*4],200h</span></p>
<p><span>mov word ptr es:[0*4+2],0</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>单步中断</span></strong></p>
<p><span>CPU</span><span>在执行完一条指令之后，如果检测到标志寄存器的</span><span>TF</span><span>位为</span><span>1</span><span>，则产生单步中断，引发中断过程。单步中断的中断类型码为</span><span>1</span><span>，则它所引发的中断过程如下：</span></p>
<p><span>（</span><span>1</span><span>）取得中断类型码</span><span>1</span><span>；</span></p>
<p><span>（</span><span>2</span><span>）标志寄存器入栈，</span><span>TF</span><span>、</span><span>IF</span><span>设置为</span><span>0</span><span>；</span></p>
<p><span>（</span><span>3</span><span>）</span><span>CS</span><span>、</span><span>IP</span><span>入栈；</span></p>
<p><span>（</span><span>4</span><span>）（</span><span>IP</span><span>）</span><span>=</span><span>（</span><span>1*4</span><span>），（</span><span>CS</span><span>）</span><span>=</span><span>（</span><span>1*4+2</span><span>）。</span></p>
<p>&nbsp;</p>
<p><span>如果</span><span>TF=1</span><span>，则执行一条指令后，</span><span>CPU</span><span>就要转去执行</span><span>1</span><span>号中断处理程序。</span></p>
<p>&nbsp;</p>
<p><span>在进入单步中断处理程序之前，设置</span><span>TF=0</span><span>，从而避免</span><span>CPU</span><span>在执行中断处理程序的时候发生单步中断。这就是为什么在中断过程中有</span><span>TF=0</span><span>这个步骤，再来看一下中断过程：</span></p>
<p>&nbsp;</p>
<p><span>CPU</span><span>为什么要提供这样的功能呢？</span></p>
<p><span>CPU</span><span>提供单步中断功能的原因就是，为单步跟踪程序的执行过程，提供了实现机制。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>响应中断的特殊情况</span></strong></p>
<p><span>一般情况下，</span><span>CPU</span><span>在执行完当前指令后，如果检测到中断信息，就响应中断，引发中断过程。</span></p>
<p><span>但是，在有些情况下，</span><span>CPU</span><span>在执行完当前执行后，即便是发生中断，也不会响应。比如：</span></p>
<p><span>在执行完向</span><span>ss</span><span>寄存器传送数据的指令后，即便是发生中断，</span><span>CPU</span><span>也不会响应。这样做的主要原因是，</span><span>ss:sp</span><span>联合指向栈顶，而<span>对它们的设置应该连续完成</span>。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/122172.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-04 10:35 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/04/122172.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编语言--标志寄存器 </title><link>http://www.cppblog.com/luqingfei/archive/2010/08/04/122171.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Wed, 04 Aug 2010 02:33:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/04/122171.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/122171.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/04/122171.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/122171.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/122171.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>标志寄存器</span></p>
<p><span>CPU</span><span>内部的寄存器中，有一种特殊的寄存器（对于不同的处理机，个数和结构都可能不同）具有三种作用：</span></p>
<p><span><span>1）&nbsp;</span></span><span>用来存储相关指令的某些执行结果；</span></p>
<p><span><span>2）&nbsp;</span></span><span>用来为</span><span>CPU</span><span>执行相关指令提供行为依据；</span></p>
<p><span><span>3）&nbsp;</span></span><span>用来控制</span><span>CPU</span><span>的相关工作方式。</span></p>
<p>&nbsp;</p>
<p><span>这种特殊的寄存器在</span><span>8086CPU</span><span>中，被称为标志寄存器。</span><span>8086CPU</span><span>的标志寄存器有</span><span>16</span><span>位，其中存储的信息通常被称为程序状态字（</span><span>PSW</span><span>）。简称</span><span>flag</span><span>。</span></p>
<p><span>flag</span><span>和其他寄存器不一样，其他寄存器是用来存放数据的，都是整个寄存器具有一个含义。</span></p>
<p><span>而</span><span>flag</span><span>寄存器是按位起作用的，也就是说，它的每一位都有专门的含义，记录特定的信息。</span></p>
<p><span>15<span>&nbsp;&nbsp;&nbsp; </span>14&nbsp;13&nbsp;12<span>&nbsp;&nbsp; </span>11&nbsp;10<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;9<span>&nbsp;&nbsp;&nbsp; </span>8<span>&nbsp;&nbsp; </span>7<span>&nbsp;&nbsp; </span>6<span>&nbsp;&nbsp; </span>5<span>&nbsp;&nbsp; </span>4<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>3<span>&nbsp;&nbsp; </span>2<span>&nbsp;&nbsp; </span>1<span>&nbsp;&nbsp; </span>0</span></p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=30>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=30>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=30>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=30>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=30>
            <p><span>OF</span></p>
            </td>
            <td vAlign=top width=30>
            <p><span>DF</span></p>
            </td>
            <td vAlign=top width=30>
            <p><span>IF</span></p>
            </td>
            <td vAlign=top width=30>
            <p><span>TF</span></p>
            </td>
            <td vAlign=top width=30>
            <p><span>SF</span></p>
            </td>
            <td vAlign=top width=30>
            <p><span>ZF</span></p>
            </td>
            <td vAlign=top width=30>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=30>
            <p><span>AF</span></p>
            </td>
            <td vAlign=top width=30>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=30>
            <p><span>PF</span></p>
            </td>
            <td vAlign=top width=30>
            <p>&nbsp;</p>
            </td>
            <td vAlign=top width=30>
            <p><span>CF</span></p>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p><span>flag</span><span>的</span><span>1</span><span>、</span><span>3</span><span>、</span><span>5</span><span>、</span><span>12</span><span>、</span><span>13</span><span>、</span><span>14</span><span>、</span><span>15</span><span>位在</span><span>8086CPU</span><span>中没有使用，不具有任何含义，而其余位都具有特殊的含义。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>ZF</span></strong><strong><span>标志</span></strong></p>
<p><span>flag</span><span>的第</span><span>6</span><span>位是</span><span>ZF</span><span>，零标志位。它记录相关指令执行后，其结果是否为</span><span>0</span><span>。如果结果为</span><span>0</span><span>，那么</span><span>ZF=1</span><span>，如果结果不为</span><span>0</span><span>，那么</span><span>ZF=0</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>mov ax,1</span></p>
<p><span>sub ax,1</span></p>
<p><span>执行后，结果为</span><span>0</span><span>，则</span><span>ZF=1</span><span>，表示&#8220;结果是</span><span>0</span><span>&#8221;。</span></p>
<p>&nbsp;</p>
<p><span>mov ax,2</span></p>
<p><span>sub ax,1</span></p>
<p><span>执行后，结果不为</span><span>0</span><span>，则</span><span>ZF=0</span><span>，表示&#8220;结果不是</span><span>0</span><span>&#8221;。</span></p>
<p>&nbsp;</p>
<p><span>在计算机中</span><span>0</span><span>表示逻辑假，表示否定，</span><span>1</span><span>表示逻辑真，表示肯定。</span></p>
<p>&nbsp;</p>
<p><span>注意，在</span><span>8086CPU</span><span>的指令集中，有的指令的执行是影响标志寄存器的，比如：</span><span>add</span><span>、</span><span>sub</span><span>、</span><span>mul</span><span>、</span><span>div</span><span>、</span><span>inc</span><span>、</span><span>or</span><span>、</span><span>and</span><span>等，它们大都是运算指令（进行逻辑或自述运算）；有的指令的执行对标志寄存器没有影响，比如：</span><span>mov</span><span>、</span><span>push</span><span>、</span><span>pop</span><span>等，它们大都是传送指令。</span></p>
<p>&nbsp;</p>
<p><span>我们在使用一条指令的时候，要注意这条指令的全部功能，其中包括，执行结果对标记寄存器的哪些标志位造成影响。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>PF</span></strong><strong><span>标志</span></strong></p>
<p><span>flag</span><span>的第</span><span>2</span><span>位是</span><span>PF</span><span>，奇偶标志位。它记录相关指令执行后，其结果的所有二进制位中</span><span>1</span><span>的个数是否为偶数。如果</span><span>1</span><span>的个数为偶数，</span><span>PF=1</span><span>，如果为奇数，那么</span><span>PF=0</span><span>。</span></p>
<p><span>比如：</span></p>
<p><span>mov al,1</span></p>
<p><span>add al,10</span></p>
<p><span>执行后，结果为</span><span>00001011B</span><span>，其中有</span><span>3</span><span>（奇数）个</span><span>1</span><span>，则</span><span>PF=0</span><span>。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>SF</span></strong><strong><span>标志</span></strong></p>
<p><span>flag</span><span>的第</span><span>7</span><span>位是</span><span>SF</span><span>，符号标志位。它记录相关指令执行后，其结果是否为负。如果结果为负，</span><span>SF=1</span><span>，如果非负，</span><span>SF=0</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>我们知道计算机中通常用补码来表示有符号数据。计算机中的一个数据可以看作是有符号数，也可以看成是无符号数。比如：</span></p>
<p><span>00000001B</span><span>，可以看作为无符号数</span><span>1</span><span>，或有符号数</span><span>+1</span><span>；</span></p>
<p><span>10000001B</span><span>，可以看作为无符号数</span><span>129</span><span>，也可以看作有符号数</span><span>-127</span><span>。</span></p>
<p><span>这也就是说，对于同一个二进制数据，计算机可以将它当作无符号数据来运算，也可以当作有符号数据来运算。比如：</span></p>
<p><span>mov al,10000001B</span></p>
<p><span>add al,1</span></p>
<p><span>结果：</span><span>(al)=10000010B</span></p>
<p>&nbsp;</p>
<p><span>我们可以将</span><span>add</span><span>指令进行的运算当作无符号数的运算，那么</span><span>add</span><span>指令相当于计算</span><span>129+1</span><span>，结果为</span><span>130</span><span>（</span><span>10000010B</span><span>）；也可以将</span><span>add</span><span>指令进行的运算当作是有符号数的运算，那么</span><span>add</span><span>指令相当于计算</span><span>-127+1</span><span>，结果为</span><span>-126</span><span>（</span><span>10000010B</span><span>）。</span></p>
<p>&nbsp;</p>
<p><span>不管我们如何看待，</span><span>CPU</span><span>在执行</span><span>add</span><span>等指令的时候，就已经包含了两种含义，也将得到用同一种信息来记录的两种结果。关键在于我们在程序需要哪一种结果。</span></p>
<p>&nbsp;</p>
<p><span>SF</span><span>标志，就是</span><span>CPU</span><span>对有符号数运算结果的一种记录，它记录数据的正负。</span></p>
<p><span>在我们将数据当作有符号数来运算的时候，可以通过它来得知结果的正负。</span></p>
<p><span>如果我们将数据当作无符号数来运算，</span><span>SF</span><span>的值则没有意义，虽然相关的指令影响了它的值。</span></p>
<p>&nbsp;</p>
<p><span>这也就是说，</span><span>CPU</span><span>在执行</span><span>add</span><span>等指令时，是必须要影响到</span><span>SF</span><span>标志位的值的。至于我们需不需要这种影响，那就看我们如何看待指令所进行的运算了。</span></p>
<p>&nbsp;</p>
<p><span>某些指令将影响标志寄存器中的多个标志位，这些被影响的标记位比较全面地记录了指令的执行结果，为相关的处理提供了所需的依据。</span></p>
<p><span>比如指令</span><span>sub al,al</span><span>执行后，</span><span>ZF</span><span>、</span><span>PF</span><span>、</span><span>SF</span><span>等标志位都要受到影响，它们分别为：</span><span>1</span><span>、</span><span>1</span><span>、</span><span>0</span><span>，分别表示结果为零、结果二进制数</span><span>1</span><span>的个数为偶数、结果不为负数。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>CF</span></strong><strong><span>标志</span><span><span>&nbsp;&nbsp; </span>[C,Carry</span></strong><strong><span>进位、</span><span>F,Flag</span></strong><strong><span>标志</span><span>]</span></strong></p>
<p><span>flag</span><span>的第</span><span>0</span><span>位是</span><span>CF</span><span>，进位标志位。一般情况下，在进行了无符号运算的时候，它记录了运算结果的最高有效位向更高位的进位值，或从更高位的借位值。</span></p>
<p>&nbsp;</p>
<p><span>对于倍数为</span><span>N</span><span>的无符号数来说，其对应的二进制信息的最高位，即第</span><span>N-1</span><span>位，就是它的最高有效位，而假想存在的第</span><span>N</span><span>位，就是相对于最高有效位的更高位。</span></p>
<p>&nbsp;</p>
<p><span>当两个数据相加的时候，有可能产生从最高有效位向更高位的进位。</span></p>
<p><span>由于这个进位值有可能无法保存，我们在前面的课程中，就只是简单地说这个进位值丢失了，其实</span><span>CPU</span><span>在运算的时候，并不丢失这个进位值，而是记录在一个特殊的寄存器的某一位上。</span><span>8086CPU</span><span>就用</span><span>flag</span><span>的</span><span>CF</span><span>位来记录这个进位值。</span></p>
<p><span>比如：</span></p>
<p><span>mov al,98H</span></p>
<p><span>add al,al<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>执行后，</span><span>(al) = 30H, CF=1, CF</span><span>记录了从最高有效位向更高位的进位值</span></p>
<p><span>add al,al<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>执行后，</span><span>(al) = 60H, CF=0, CF</span><span>记录了从最高有效位向更高位的进位值</span></p>
<p>&nbsp;</p>
<p><span>而当两个数据做减法的时候，有可能向更高位借位。比如，两个</span><span>8</span><span>位数据：</span><span>97H-98H</span><span>，将产生借位，借位后，相当于计算</span><span>197H-98H</span><span>。而</span><span>flag</span><span>的</span><span>CF</span><span>位也可以用来记录这个借位值。</span></p>
<p><span>比如：</span></p>
<p><span>mov al,97H</span></p>
<p><span>sub al,98H<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>执行后，</span><span>(al) = FFH, CF=1, CF</span><span>记录了向更高位的借位值</span></p>
<p><span>sub al,al<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>执行后，</span><span>(al)=0,CF=0</span><span>，</span><span>CF</span><span>记录了向更高位的借位值</span></p>
<p>&nbsp;</p>
<p><strong><span>OF</span></strong><strong><span>标志</span><span><span>&nbsp;&nbsp; </span>[O,Overflow</span></strong><strong><span>溢出，</span><span>F,Flag</span></strong><strong><span>标志</span><span>]</span></strong></p>
<p><span>溢出：在进行有符号数运算的时候，如结果超过了机器所能表示的范围称为溢出。</span></p>
<p>&nbsp;</p>
<p><span>那么，什么是机器所能表示的范围呢？</span></p>
<p><span>比如说，指令运算的结果用</span><span>8</span><span>位寄存器或内存单元来存放，比如：</span><span>add al,3</span><span>，那么对于</span><span>8</span><span>位的有符号数据，机器所能表示的范围就是</span><span>-128~127</span><span>。同理，对于</span><span>16</span><span>位有符号数，机器所能表示的范围是</span><span>-32768~32767</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>注意，这里所讲的溢出，只是对有符号数运算而言。</span></p>
<p>&nbsp;</p>
<p><span>由于在进行有符号数运算时，可能发生溢出而造成结果的错误，则</span><span>CPU</span><span>需要对指令执行后是否产生溢出进行记录。</span></p>
<p>&nbsp;</p>
<p><span>flag</span><span>的第</span><span>11</span><span>位是</span><span>OF</span><span>，溢出标志位。一般情况下，</span><span>OF</span><span>记录了有符号数运算的结果是否发生了溢出。如果发生溢出，</span><span>OF=1</span><span>，如果没有，</span><span>OF=0</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>一定要注意</span><span>CF</span><span>和</span><span>OF</span><span>的区别：</span><span>CF</span><span>是对无符号数运算有意义的标志位，而</span><span>OF</span><span>是对有符号数运算有意义的标志位。</span></p>
<p><span>比如：</span></p>
<p><span>mov al,98d</span></p>
<p><span>add al,99d</span></p>
<p><span>add</span><span>指令执行后：</span><span>CF=0</span><span>，</span><span>OF=1</span><span>。</span></p>
<p><span>CPU</span><span>在执行</span><span>add</span><span>等指令的时候，就包含了两种含义：无符号数运算和有符号数运算。</span></p>
<p><span>对于无符号数运算，</span><span>CPU</span><span>用</span><span>CF</span><span>位来记录是否产生了进位；</span></p>
<p><span>对于有符号数运算，</span><span>CPU</span><span>用</span><span>OF</span><span>位来记录是否产生了溢出，</span></p>
<p><span>当然，还要用</span><span>SF</span><span>位来记录结果的符号。</span></p>
<p><span>对于无符号数运算，</span><span>98+99</span><span>没有进位，</span><span>CF=0</span><span>；</span></p>
<p><span>对于有符号数运算，</span><span>98+99</span><span>发生溢出，</span><span>OF=1</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>CF</span><span>和</span><span>OF</span><span>所表示的进位和溢出，是分别对无符号数和有符号数运算而言的，它们之间没有任何关系。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>adc</span></strong><strong><span>指令</span></strong></p>
<p><span>adc</span><span>是带进位的加法指令，它利用了</span><span>CF</span><span>位上记录的进位值。</span></p>
<p><span>指令格式：</span><span>adc </span><span>操作对象</span><span>1</span><span>，操作对象</span><span>2</span></p>
<p><span>功能：操作对象</span><span>1=</span><span>操作对象</span><span>1+</span><span>操作对象</span><span>2+CF</span></p>
<p><span>比如指令</span><span>adc ax,bx </span><span>实现的功能是：</span><span>(ax) = (ax) + (bx) +CF</span></p>
<p><span>例：</span></p>
<p><span>mov ax,2</span></p>
<p><span>mov bx,1</span></p>
<p><span>sub bx,ax</span></p>
<p><span>adc ax,1</span></p>
<p><span>执行后，</span><span>(ax) = 4</span><span>。</span><span>adc</span><span>执行时，相当于计算：</span><span>(ax) + 1 + CF=2+1+1=4</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>adc</span><span>指令比</span><span>add</span><span>指令多加了一个</span><span>CF</span><span>位的值。</span></p>
<p>&nbsp;</p>
<p><span>为什么要加上</span><span>CF</span><span>的值呢？</span></p>
<p><span>CPU</span><span>为什么要提供这样一条指令呢？</span></p>
<p><span>CF</span><span>的值的含义，在执行</span><span>adc</span><span>指令的时候加上的</span><span>CF</span><span>的值的含义，由</span><span>adc</span><span>指令前面的指令决定的，也就是说，关键在于所加上的</span><span>CF</span><span>的值是被什么指令设置的。显然，如果</span><span>CF</span><span>的值是被</span><span>sub</span><span>指令设置的，那么它的含义就是借位值；如果是被</span><span>add</span><span>指令设置的，那么它的含义就是进位值。</span></p>
<p>&nbsp;</p>
<p><span>加法可以分两步来进行：（</span><span>1</span><span>）低位相加；（</span><span>2</span><span>）高位相加再加上低位相加产生的进位值。</span></p>
<p><span>下面的指令和</span><span>add ax,bx</span><span>具有相同的结果：</span></p>
<p><span>add al,bl</span></p>
<p><span>adc ah,bh</span></p>
<p>&nbsp;</p>
<p><span>看来</span><span>CPU</span><span>提供的</span><span>adc</span><span>指令的目的，就是来进行加法的第二步运算的。</span></p>
<p><span>adc</span><span>指令和</span><span>add</span><span>指令相配合可以对更大的数据进行加法运算。</span></p>
<p>&nbsp;</p>
<p><span>举例：编程，计算</span><span>1EF000H+201000H</span><span>，结果放在</span><span>ax</span><span>（高</span><span>16</span><span>位）和</span><span>bx</span><span>（低</span><span>16</span><span>位）中。</span></p>
<p><span>因为两个数据的位数都大于</span><span>16</span><span>，用</span><span>add</span><span>指令无法进行计算。我们将计算分两步进行，先将低</span><span>16</span><span>位相加，然后将高</span><span>16</span><span>位和进位值相加。程序如下：</span></p>
<p><span>mov ax,001EH<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>高</span><span>16</span><span>位</span></p>
<p><span>mov bx,<st1:chmetcnv w:st="on" UnitName="F" SourceValue="0" HasSpace="False" Negative="False" NumberType="1" TCSC="0">0f</st1:chmetcnv>000H<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>低</span><span>16</span><span>位</span></p>
<p><span>add bx,1000H<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>低</span><span>16</span><span>位相加</span></p>
<p><span>adc ax,0020H<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>高</span><span>16</span><span>位相加，并加上</span><span>CF</span><span>进位值。</span></p>
<p>&nbsp;</p>
<p><span>adc</span><span>指令执行后，也可能产生进位值，所以也会对</span><span>CF</span><span>位进行设置。</span></p>
<p><span>由于有这样的功能，我们就可以对任意大的数据进行加法运算。</span></p>
<p>&nbsp;</p>
<p><span>inc</span><span>和</span><span>loop</span><span>指令不影响</span><span>CF</span><span>位。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>sbb</span></strong><strong><span>指令</span></strong></p>
<p><span>sbb</span><span>是带借位减法指令，它利用了</span><span>CF</span><span>位上记录的借位值。</span></p>
<p>&nbsp;</p>
<p><span>指令格式：</span><span>sbb </span><span>操作对象</span><span>1</span><span>，操作对象</span><span>2</span></p>
<p><span>功能：操作对象</span><span>1=</span><span>操作对象</span><span>1-</span><span>操作对象</span><span>2-CF</span></p>
<p><span>比如指令</span><span> sbb ax,bx </span><span>实现的功能是：</span><span>(ax) = (ax) &#8211; (bx) &#8211; CF</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>sbb</span><span>指令执行后，将对</span><span>CF</span><span>进行设置。</span></p>
<p><span>利用</span><span>sbb</span><span>指令我们可以对任意大的数据进行减法运算。</span></p>
<p><span>比如，计算</span><span>003E1000H &#8211; 00202000H</span><span>，结果放在</span><span>ax,bx</span><span>中，程序如下：</span></p>
<p><span>mov bx,1000H</span></p>
<p><span>mov ax,003EH</span></p>
<p><span>sub bx,2000H<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;(bx) = (bx)-2000H</span><span>，</span><span>CF</span><span>设置借位数</span><span> 1</span></p>
<p><span>sbb ax,0020H<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;(ax) = (bx) &#8211; 0020H &#8211; CF</span></p>
<p>&nbsp;</p>
<p><span>sbb</span><span>和</span><span>adc</span><span>是基于同样的思想设计的两条指令，在应用思路上和</span><span>adc</span><span>类似。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>cmp</span></strong><strong><span>指令</span></strong></p>
<p><span>cmp</span><span>是比较指令，</span><span>cmp</span><span>的功能相当于减法指令，只是不保存结果。</span></p>
<p><span>cmp</span><span>指令执行后，将对标志寄存器产生影响。其他相关指令通过识别这些被影响的标志寄存器位来得知比较结果。</span></p>
<p>&nbsp;</p>
<p><span>cmp</span><span>指令格式：</span><span>cmp </span><span>操作对象</span><span>1</span><span>，操作对象</span><span>2</span></p>
<p><span>功能：计算操作对象</span><span>1-</span><span>操作对象</span><span>2</span><span>，但并不保存结果，仅仅根据计算结果对标志寄存器进行设置。</span></p>
<p>&nbsp;</p>
<p><span>比如，指令</span><span>cmp ax,ax</span><span>，做</span><span>(ax)-(ax)</span><span>的运算，结果为</span><span>0</span><span>，但并不在</span><span>ax</span><span>中保存，仅影响</span><span>flag</span><span>的相关各位。指令执行后，</span><span>ZF=1</span><span>，</span><span>PF=1</span><span>，</span><span>SF=0</span><span>，</span><span>CF=0</span><span>，</span><span>OF=0</span><span>。</span></p>
<p><span>举例：</span></p>
<p><span>mov ax,8</span></p>
<p><span>mov bx,3</span></p>
<p><span>cmp ax,bx</span></p>
<p><span>执行后：</span><span>(ax)=8</span><span>，</span><span>ZF=0</span><span>，</span><span>PF=1</span><span>，</span><span>SF=0</span><span>，</span><span>CF=0</span><span>，</span><span>OF=0</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>其实，通过</span><span>cmp</span><span>指令执行后，相关标志位的值就可以看出比较的结果。</span></p>
<p><span>cmp ax,bx</span></p>
<p><span>如果</span><span>(ax) = (bx) </span><span>则</span><span>(ax) &#8211; (bx) = 0</span><span>，所以：</span><span>ZF = 1</span><span>；</span></p>
<p><span>如果</span><span>(ax)</span><span>&#8800;</span><span>(bx) </span><span>则</span><span>(ax) &#8211; (bx)</span><span>&#8800;</span><span>0</span><span>，所以：</span><span>ZF = 0</span><span>；</span></p>
<p><span>如果</span><span>(ax)&lt;(bx)&nbsp;</span><span>则</span><span>(ax) &#8211; (bx) </span><span>将产生借位，所以：</span><span>CF=1</span><span>；</span></p>
<p><span>如果</span><span>(ax)</span><span>&#8805;</span><span>(bx) </span><span>则</span><span>(ax) &#8211; (bx) </span><span>将不必借位，所以：</span><span>CF=0</span><span>；</span></p>
<p><span>如果</span><span>(ax)&gt;(bx)&nbsp;</span><span>则</span><span>(ax)-(bx)</span><span>既不必借位，结果又不为</span><span>0</span><span>，所以：</span><span>CF=0 </span><span>并且</span><span> ZF=0</span><span>；</span></p>
<p><span>如果</span><span>(ax)</span><span>&#8804;</span><span>(bx) </span><span>则</span><span>(ax)-(bx)</span><span>既可能借位，结果也可能为</span><span>0</span><span>，所以：</span><span>CF=1 </span><span>或</span><span> ZF=1</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>现在我们可以看出比较指令的设计思路，即：通过做减法运算，影响标志寄存器，标志寄存器的相关位记录了比较的结果。反过来看上面的例子：</span></p>
<p><span>指令</span><span>cmp ax,bx</span><span>的逻辑含义是比较</span><span>ax</span><span>和</span><span>bx</span><span>中的值，如果执行后：</span></p>
<p><span>ZF=1</span><span>，说明</span><span>(ax)=(bx)</span><span>；</span></p>
<p><span>ZF=0</span><span>，说明</span><span>(ax)</span><span>&#8800;</span><span>(bx)</span><span>；</span></p>
<p><span>CF=1</span><span>，说明</span><span>(ax)&lt;(bx)</span><span>；</span></p>
<p><span>CF=0</span><span>，说明</span><span>(ax)</span><span>&#8805;</span><span>(bx)</span><span>；</span></p>
<p><span>CF=0 </span><span>并且</span><span> ZF=0</span><span>，说明</span><span>(ax)&gt;(bx)</span><span>；</span></p>
<p><span>CF=1 </span><span>或</span><span> ZF=1</span><span>，说明</span><span>(ax)</span><span>&#8804;</span><span>(bx)</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>同</span><span>add</span><span>、</span><span>sub</span><span>指令一样，</span><span>CPU</span><span>在执行</span><span>cmp</span><span>指令的时候，也包含两种含义：进行无符号数运算和进行有符号数运算。</span></p>
<p>&nbsp;</p>
<p><span>所以利用</span><span>cmp</span><span>指令可以对无符号数进行比较，也可以对有符号数进行比较。</span></p>
<p><span>上面是用</span><span>cmp</span><span>进行无符号数比较时，相关标志位对比较结果的记录。</span></p>
<p><span>下面来看一下如果用</span><span>cmp</span><span>来进行有符号数比较时，</span><span>CPU</span><span>用哪些标志位对比较结果进行记录。</span></p>
<p><span>cmp ah,bh</span></p>
<p><span>如果</span><span>(ah)=(bh)&nbsp;</span><span>则</span><span>(ah)-(bh)=0</span><span>，所以</span><span>ZF=1</span><span>；</span></p>
<p><span>如果</span><span>(ah)</span><span>&#8800;</span><span>(bh) </span><span>则</span><span>(ah)-(bh)</span><span>&#8800;</span><span>0</span><span>，所以</span><span>ZF=0</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>对于有符号数运算，在</span><span>(ah)&lt;(bh)</span><span>情况下，</span><span>(ah)-(bh)</span><span>显然可能引起</span><span>SF=1</span><span>，即结果为负。</span></p>
<p><span>比如：</span></p>
<p><span>(ah)=1</span><span>，</span><span>(bh)=2</span><span>；则</span><span>(ah) &#8211; (bh)=0FFH</span><span>，</span><span>0FFH</span><span>为</span><span>-1</span><span>的补码，因为结果为负，所以</span><span>SF=1</span><span>。</span></p>
<p><span>(ah)=0FEH</span><span>，</span><span>(bh)=0FFH</span><span>；则</span><span>(ah) &#8211; (bh)= &#8211; 2 &#8211; (&#8211;1)=0FFH</span><span>，因为结果为负，所以</span><span>SF=1</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>(ah)=22H</span><span>，</span><span>(bh)=<st1:chmetcnv w:st="on" UnitName="a" SourceValue="0" HasSpace="False" Negative="False" NumberType="1" TCSC="0">0A</st1:chmetcnv>0H</span><span>；则</span><span>(ah) &#8211; (bh)=34 &#8211; (-96)=82H</span><span>，</span><span>82H</span><span>是</span><span>-126</span><span>的补码，所以</span><span>SF=1</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>两个有符号娄</span><span>A</span><span>和</span><span>B</span><span>相减，得到的是负数，那么可以肯定</span><span>A&lt;B</span><span>，这个思路没有错误，关键在于我们根据什么来断定得到的是一个负数。</span><span>CPU</span><span>将</span><span>cmp</span><span>指令得到的结果记录在</span><span>flag</span><span>的相关标志位中。我们可以根据指令执行后，相关标志位的值来判断比较的结果。单纯地考察</span><span>SF</span><span>的值不可能知道结果的正负。因为</span><span>SF</span><span>记录的只是可以在计算机中存放的相应位数的结果的正负，比如</span><span>add ah,al</span><span>执行后，</span><span>SF</span><span>记录的是</span><span>ah</span><span>中的</span><span>8</span><span>位二进制信息所表示的数据的正负。</span><span>cmp ah,bh</span><span>执行后，</span><span>SF</span><span>记录的是</span><span>(ah)-(bh)</span><span>所得到的</span><span>8</span><span>位二进制信息所表示的数据的正负，虽然这个结果没有在我们能够使用的寄存器或内存单元中保存，但是在指令执行的过程中，它暂存在</span><span>CPU</span><span>内部的暂存器中。</span></p>
<p><span>所得到的相应结果的正负，并不能说明，运算所应该得到的结果的正负。这是因为在运算的过程中可能发生溢出。如果有这样的情况发生，那么，</span><span>SF</span><span>的值就不能说明任何问题。</span></p>
<p><span>比如：</span></p>
<p><span>mov ah,22H</span></p>
<p><span>mov bh,<st1:chmetcnv w:st="on" UnitName="a" SourceValue="0" HasSpace="False" Negative="False" NumberType="1" TCSC="0">0A</st1:chmetcnv>0H</span></p>
<p><span>sub ah,bh</span></p>
<p><span>结果</span><span>SF=1</span><span>，运算实际符号得到的结果是</span><span>(ah)=82H</span><span>，但是在逻辑上，运算所应该得到的结果是：</span><span>34 &#8211; (&#8211; 96)=130</span><span>。就是因为</span><span>130</span><span>这个结果作为一个有符号数超出了</span><span>-128~127</span><span>这个范围，在</span><span>ah</span><span>中不能表示，而</span><span>ah</span><span>中的结果被</span><span>CPU</span><span>当作有符号数解释为</span><span>-126</span><span>。而</span><span>SF</span><span>被用来记录这个实际结果的正负，所以</span><span>SF=1</span><span>。但</span><span>SF=1</span><span>不能说明在逻辑上运算所得的正确结果的正负。</span></p>
<p>&nbsp;</p>
<p><span>但是逻辑上的结果的正负，才是</span><span>cmp</span><span>指令所求的真正结果，因为我们就是要靠它得到两个操作对象的比较信息。所以</span><span>cmp</span><span>指令所作的比较结果，不是仅仅靠</span><span>SF</span><span>就能记录的，因为它只能记录实际结果的正负。</span></p>
<p>&nbsp;</p>
<p><span>两种结果之间的关系，实际结果的正负，和逻辑上真正结果的正负，它们之间有多大的距离呢？</span></p>
<p><span>实际结果的正负，之所以不能说明逻辑上真正结果的正负，关键的原因在于发生了溢出。如果没有溢出发生的话，那么，实际结果的正负和逻辑上真正结果的正负就一致了。</span></p>
<p>&nbsp;</p>
<p><span>所以，我们应该在考察</span><span>SF</span><span>（得知实际结果的正负）的同时考察</span><span>OF</span><span>（得到有没有溢出），就可以得知逻辑上真正结果的正负，同时就可以知道比较的结果。</span></p>
<p>&nbsp;</p>
<p><span>总结，</span><span>cmp ah,bh</span></p>
<p><span><span>1）&nbsp;</span></span><span>如果</span><span>SF=1</span><span>，而</span><span>OF=0</span></p>
<p><span>OF=0</span><span>，说明没有溢出，逻辑上真正结果的正负</span><span>=</span><span>实际结果的正负；</span></p>
<p><span>因</span><span>SF=1</span><span>，实际结果为负，所以逻辑上真正的结果为负，所以</span><span>(ah)&lt;(bh)</span><span>。</span></p>
<p>&nbsp;</p>
<p><span><span>2）&nbsp;</span></span><span>如果</span><span>SF=1</span><span>，而</span><span>OF=1</span></p>
<p><span>OF=1</span><span>，说明有溢出，逻辑上真正结果的正负&#8800;实际结果的正负；</span></p>
<p><span>因</span><span>SF=1</span><span>，实际结果为负。</span></p>
<p><span>实际结果为负，而又有溢出，这说明是由于溢出导致了实际结果为负，简单分析一下，就可以看出，<span>如果因溢出导致了实际结果为负，那么逻辑上真正的结果必须为正。</span></span></p>
<p><span>这样，</span><span>SF=1</span><span>，</span><span>OF=1</span><span>，说明了</span><span>(ah)&gt;(bh)</span><span>。</span></p>
<p>&nbsp;</p>
<p><span><span>3）&nbsp;</span></span><span>如果</span><span>SF=0</span><span>，而</span><span>OF=1</span></p>
<p><span>OF=1</span><span>，说明有溢出，逻辑上真正结果的正负&#8800;实际结果的正负；</span></p>
<p><span>因</span><span>SF=0</span><span>，实际结果非负，而</span><span>OF=1</span><span>说明有溢出，则结果非</span><span>0</span><span>，所以实际结果为正。</span></p>
<p><span>实际结果为正，而又有溢出，这说明是由于溢出导致了实际结果非负，简单分析一下，就可以看出，<span>如果因为溢出导致了实际结果为正，那么逻辑上真正的结果必须为负</span>。</span></p>
<p><span>这样，</span><span>SF=0</span><span>，</span><span>OF=1</span><span>，说明了</span><span>(ah)&lt;(bh)</span><span>。</span></p>
<p>&nbsp;</p>
<p><span><span>4）&nbsp;</span></span><span>如果</span><span>SF=0</span><span>，而</span><span>OF=0</span></p>
<p><span>OF=0</span><span>，说明没有溢出，逻辑上真正结果的正负</span><span>=</span><span>实际结果的正负；</span></p>
<p><span>因</span><span>SF=0</span><span>，实际结果非负，所以逻辑上真正的结果非负，所以</span><span>(ah)</span><span>&#8805;</span><span>(bh)</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>8086CPU</span><span>这种工作机制的设计思想，实际上，对于各种处理机来说是普遍的。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>检测比较结果的条件转移指令</span></strong></p>
<p><span>&#8220;转移&#8221;指的是它能够修改</span><span>IP</span><span>，而&#8220;条件&#8221;指的是它可以根据某种条件，决定是否修改</span><span>IP</span><span>。</span></p>
<p><span>比如：</span><span>jcxz</span><span>就是一个条件转移指令，它可以检测</span><span>cx</span><span>中的数值，如果</span><span>(cx)=0</span><span>，就修改</span><span>IP</span><span>，否则什么也不做。</span></p>
<p><span>所有条件转移指令的转移位移都是</span><span>[-128~127]</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>除了</span><span>jcxz</span><span>之外，</span><span>CPU</span><span>还提供了其他条件转移指令，大多数条件转移指令都检测标志寄存器的相关标志位，根据检测的结果来决定是否修改</span><span>IP</span><span>。</span></p>
<p><span>它们检测的是哪些标志位呢？就是被</span><span>cmp</span><span>指令影响的那些，表示比较结果的标志位。这些条件转移指令通常都和</span><span>cmp</span><span>相配合使用，就好像</span><span>call</span><span>和</span><span>ret</span><span>指令通常相配合使用一样。</span></p>
<p>&nbsp;</p>
<p><span>因为</span><span>cmp</span><span>指令可以同时进行两种比较，无符号数比较和有符号数比较，所以根据</span><span>cmp</span><span>指令的比较结果进行转移的指令也分为两种，即：</span></p>
<p><span>根据无符号数的比较结果进行转移的条件转移指令，它们检测</span><span>ZF</span><span>、</span><span>CF</span><span>的值；</span></p>
<p><span>和根据有符号数的比较结果进行了转移的条件转移指令，它们检测</span><span>SF</span><span>、</span><span>OF</span><span>和</span><span>ZF</span><span>的值。</span></p>
<p>&nbsp;</p>
<p><span>常用的根据无符号数的比较结果进行转移的条件转移指令：</span> </p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=189>
            <p><span>指令</span></p>
            </td>
            <td vAlign=top width=189>
            <p><span>含义</span></p>
            </td>
            <td vAlign=top width=189>
            <p><span>检测的相关标志位</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=189>
            <p><span>je</span></p>
            </td>
            <td vAlign=top width=189>
            <p><span>等于则转移</span></p>
            </td>
            <td vAlign=top width=189>
            <p><span>ZF=1</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=189>
            <p><span>jne</span></p>
            </td>
            <td vAlign=top width=189>
            <p><span>不等于则转移</span></p>
            </td>
            <td vAlign=top width=189>
            <p><span>ZF=0</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=189>
            <p><span>jb</span></p>
            </td>
            <td vAlign=top width=189>
            <p><span>低于则转移</span></p>
            </td>
            <td vAlign=top width=189>
            <p><span>CF=1</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=189>
            <p><span>jnb</span></p>
            </td>
            <td vAlign=top width=189>
            <p><span>不低于则转移</span></p>
            </td>
            <td vAlign=top width=189>
            <p><span>CF=0</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=189>
            <p><span>ja</span></p>
            </td>
            <td vAlign=top width=189>
            <p><span>高于则转移</span></p>
            </td>
            <td vAlign=top width=189>
            <p><span>CF=0 </span><span>且</span><span> ZF=0</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=189>
            <p><span>jna</span></p>
            </td>
            <td vAlign=top width=189>
            <p><span>不高于则转移</span></p>
            </td>
            <td vAlign=top width=189>
            <p><span>CF=1 </span><span>或</span><span> ZF=1</span></p>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p><span>j</span><span>：</span><span><span>&nbsp;&nbsp; </span></span><span>表示</span><span>jump<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>转移</span></p>
<p><span>e</span><span>：</span><span>&nbsp;</span><span>表示</span><span>equal<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>等于</span></p>
<p><span>ne</span><span>：表示</span><span>not equal<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>不等于</span></p>
<p><span>b</span><span>：</span><span>&nbsp;</span><span>表示</span><span>below<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>小于</span></p>
<p><span>nb</span><span>：表示</span><span>not below<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>不小于</span></p>
<p><span>a</span><span>：</span><span>&nbsp;</span><span>表示</span><span>above<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>大于</span></p>
<p><span>na</span><span>：表示</span><span>not above<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>不大于</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>编程实现如下功能：</span></p>
<p><span>如果</span><span>(ah)=(bh)</span><span>则</span><span>(ah)=(ah)+(ah)</span><span>，否则</span><span>(ah)=(ah)+(bh)</span><span>。</span></p>
<p><span>cmp ah,bh</span></p>
<p><span>je s</span></p>
<p><span>add ah,bh</span></p>
<p><span>jmp short ok</span></p>
<p><span>s:add ah,ah</span></p>
<p><span>ok:&#8230;</span></p>
<p>&nbsp;</p>
<p><span>上面的程序执行时，如果</span><span>(ah)=(bh)</span><span>，则</span><span>cmp ah,bh </span><span>使</span><span>ZF=1</span><span>，而</span><span>je</span><span>检测</span><span>ZF</span><span>是否为</span><span>1</span><span>，如果为</span><span>1</span><span>，将转移到标号</span><span>s</span><span>处执行指令</span><span> add ah,ah</span><span>。这也可以说，</span><span>cmp</span><span>比较</span><span>ah</span><span>、</span><span>bh</span><span>后所得到的相等的结果使得</span><span>je</span><span>指令进行转移。从而很好地体现了</span><span>je</span><span>指令的逻辑含义，相等则转移。</span></p>
<p>&nbsp;</p>
<p><span>虽然</span><span>je</span><span>的逻辑含义是&#8220;相等则转移&#8221;，但它进行的操作是，</span><span>ZF=1</span><span>时则转移。</span></p>
<p><span>&#8220;相等则转移&#8221;这种逻辑含义，是通过和</span><span>cmp</span><span>指令配合使用来体现的，因为</span><span>cmp</span><span>指令为&#8220;</span><span>ZF=1</span><span>&#8221;赋予了&#8220;两数相等&#8221;的含义。</span></p>
<p><span>至于究竟在</span><span>je</span><span>之前使不使用</span><span>cmp</span><span>指令，在于我们在安排</span><span>je</span><span>检测的是</span><span>ZF</span><span>位置，不管</span><span>je</span><span>前面是什么指令，只要</span><span>CPU</span><span>执行</span><span>je</span><span>指令时，</span><span>ZF=1</span><span>，那么就会发生转移。</span></p>
<p>&nbsp;</p>
<p><span>如何配合使用这些指令，完全取决于程序作者。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>虽然我们分别讨论了</span><span>cmp</span><span>指令和与其比较结果相关的有条件转移指令，但是它们经常在一起配合使用。所以我们在联合应用它们的时候，不必再考虑</span><span>cmp</span><span>指令对相关标志位的影响和</span><span>je</span><span>等指令对相关标志位的检测。因为相关的标志位，只是为</span><span>cmp</span><span>和</span><span>je</span><span>等指令传递比较结果。我们可以直接考虑</span><span>cmp</span><span>和</span><span>je</span><span>等指令配合使用时，表现出来的逻辑含义。它们在联合使用的时候表现出来的功能有些像高级语言中的</span><span>IF</span><span>语句。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>上面讲解了根据无符号数的比较结果进行转移的条件转移指令。根据有符号数的比较结果进行转移的条件转移指令的工作原理和无符号的相同，只是检测了不同的标志位。</span></p>
<p><span>cmp</span><span>、标志寄存的相关位、条件转移指令三者配合应用，这个原理具有普遍性。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>DF</span><span>标志和串传送指令</span></p>
<p><span>flag</span><span>的第</span><span>10</span><span>位是</span><span>DF</span><span>，</span><span>Direction Flag</span><span>，方向标志位。</span></p>
<p><span>在串处理指令中，控制每操作后</span><span>si</span><span>，</span><span>di</span><span>的增减。</span></p>
<p><span>DF=0</span><span>，每次操作后</span><span>si</span><span>，</span><span>di</span><span>递增；</span></p>
<p><span>DF=1</span><span>，每次操作后</span><span>si</span><span>，</span><span>di</span><span>递减。</span></p>
<p>&nbsp;</p>
<p><span>串传送指令：</span></p>
<p><span>格式：</span><span>movsb</span></p>
<p><span>功能：执行</span><span>movsb </span><span>指令相当于进行下面几步操作：</span></p>
<p><span>1</span><span>）</span><span>((es)*16+(di)) = ((ds)*16+(si))</span></p>
<p><span>2</span><span>）如果</span><span>DF=0</span><span>则：</span><span>(si)=(si)+1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;(di)=(di)+1</span></p>
<p><span><span>&nbsp;&nbsp; </span></span><span>如果</span><span>DF=1</span><span>则：</span><span>(si)=(si)-1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;(di)=(di)-1</span></p>
<p><span>用汇编语法描述</span><span>movsb</span><span>的功能如下：</span></p>
<p><span>mov es:[di],byte ptr ds:[si]<span>&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;8086CPU</span><span>并不支持这样的指令，这里只是个描述。</span></p>
<p><span>如果</span><span>DF=0</span><span>：</span></p>
<p><span>inc si</span></p>
<p><span>inc di</span></p>
<p><span>如果</span><span>DF=1</span><span>：</span></p>
<p><span>dec si</span></p>
<p><span>dec di</span></p>
<p>&nbsp;</p>
<p><span>movsb</span><span>指令的功能是将</span><span>ds:si</span><span>指向的内存单元中的字节送入</span><span>es:di</span><span>中，然后根据标志寄存器</span><span>DF</span><span>位的值，将</span><span>si</span><span>和</span><span>di</span><span>递增或递减。</span></p>
<p><span>也可以传送一个字，指令如下：</span></p>
<p><span>格式：</span><span>movsw</span></p>
<p><span>movsw</span><span>的功能是将</span><span>ds:si</span><span>指向的内存单元中</span><span>word</span><span>送入</span><span>es:di</span><span>中，然后根据标志寄存器</span><span>DF</span><span>位的值，将</span><span>si</span><span>和</span><span>di</span><span>递增</span><span>2</span><span>或递减</span><span>2</span><span>。</span></p>
<p><span>用汇编语法描述</span><span>movsw</span><span>的功能如下：</span></p>
<p><span>mov es:[di], word ptr ds:[si]&nbsp;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;8086CPU</span><span>并不支持这样的指令，这里只是个描述。</span></p>
<p><span>如果</span><span>DF=0</span><span>：</span></p>
<p><span>add si,2</span></p>
<p><span>add di,2</span></p>
<p><span>如果</span><span>DF=1</span><span>：</span></p>
<p><span>sub si,2</span></p>
<p><span>sub di,2</span></p>
<p>&nbsp;</p>
<p><span>movsb</span><span>和</span><span>movsw</span><span>进行的是串传送操作中的一个步骤，一般来说，</span><span>movsb</span><span>和</span><span>movsw</span><span>都和</span><span>rep</span><span>配合使用，格式如下：</span></p>
<p><span>rep movsb</span></p>
<p><span>用汇编语法来描述</span><span>rep movsb</span><span>的功能就是：</span></p>
<p><span>s:movsb</span></p>
<p><span>loop s</span></p>
<p>&nbsp;</p>
<p><span>可见，</span><span>rep</span><span>的作用是根据</span><span>cx</span><span>的值，重复执行后面的串传送指令。</span></p>
<p><span>由于每执行一行</span><span>movsb</span><span>指令，</span><span>si</span><span>和</span><span>di</span><span>都会递增或递减指向后一个单元或前一个单元，则</span><span>rep movsb</span><span>就可以循环实现</span><span>(cx)</span><span>个字符的传送。</span></p>
<p><span>同理，</span><span>movsw</span><span>也有类似功能。</span></p>
<p>&nbsp;</p>
<p><span>由于</span><span>flag</span><span>的</span><span>DF</span><span>位决定着串传送指令执行后，</span><span>si</span><span>和</span><span>di</span><span>改变的方向，所以</span><span>CPU</span><span>应该提供相应的指令来对</span><span>DF</span><span>位进行设置，从而使程序员能够决定传送的方向。</span></p>
<p>&nbsp;</p>
<p><span>8086CPU</span><span>提供下面两条指令对</span><span>DF</span><span>位进行设置：</span></p>
<p><span>cld</span><span>指令：将标志寄存器的</span><span>DF</span><span>位置</span><span>0</span><span>；</span></p>
<p><span>std</span><span>指令：将标志寄存器的</span><span>DF</span><span>位置</span><span>1</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>编程：用串传送指令，将</span><span>data</span><span>段中的第一个字符串复制到它后面的空间中。</span></p>
<p><span>data segment</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db &#8216;Welcome to masm!&#8217;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db 16 dup (0)</span></p>
<p><span>data ends</span></p>
<p><span>分析：使用串传送指令进行数据的传送，需要给它提供一些必要的信息：</span></p>
<p><span><span>1）&nbsp;</span></span><span>传送的原始位置：</span><span>ds:si</span></p>
<p><span><span>2）&nbsp;</span></span><span>传送的目的位置：</span><span>es:di</span></p>
<p><span><span>3）&nbsp;</span></span><span>传送的长度：</span><span>cx</span></p>
<p><span><span>4）&nbsp;</span></span><span>传送的方向：</span><span>DF</span></p>
<p>&nbsp;</p>
<p><span>mov ax,data</span></p>
<p><span>mov ds,ax</span></p>
<p><span>mov si,0<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;ds:si</span><span>指向</span><span>data:0</span></p>
<p><span>mov es,ax</span></p>
<p><span>mov di,16<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;es:di</span><span>指向</span><span>data:16</span></p>
<p><span>mov cx,16<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;(cx)=16, rep</span><span>循环</span><span>16</span><span>次</span></p>
<p><span>cld<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>设置</span><span>DF=0,</span><span>正向传送</span></p>
<p><span>rep movsb</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>pushf</span></strong><strong><span>和</span><span>popf</span></strong></p>
<p><span>pushf</span><span>的功能是将标志寄存器的值压栈，而</span><span>popf</span><span>是从栈中弹出数据，送入标志寄存器中。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>pushf</span><span>和</span><span>popf</span><span>，为直接访问标志寄存器提供了一种方法。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>标志寄存器在</span><span>Debug</span><span>中的表示</span></p>
<p><span>在</span><span>Debug</span><span>中，标志寄存器是按照有意义的各个标志位单独表示的。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/122171.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-04 10:33 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/04/122171.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编语言--call和ret指令 </title><link>http://www.cppblog.com/luqingfei/archive/2010/08/04/122170.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Wed, 04 Aug 2010 02:32:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/04/122170.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/122170.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/04/122170.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/122170.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/122170.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>call</span><span>和</span><span>ret</span><span>指令</span></p>
<p><span>call</span><span>和</span><span>ret</span><span>指令都是转移指令，它们都修改</span><span>IP</span><span>，或同时修改</span><span>CS</span><span>和</span><span>IP</span><span>。</span></p>
<p><span>它们经常被共同用来实现子程序的设计。</span></p>
<p>&nbsp;</p>
<p><strong><span>ret</span></strong><strong><span>和</span><span>retf</span></strong></p>
<p><span>ret</span><span>指令用栈中的数据，修改</span><span>IP</span><span>的内容，从而实现近转移；</span></p>
<p><span>retf</span><span>指令用栈中的数据，修改</span><span>CS</span><span>和</span><span>IP</span><span>的内容，从而实现远转移。</span></p>
<p><span>CPU</span><span>执行</span><span>ret</span><span>指令时，进行下面的两步操作：</span></p>
<p><span>（</span><span>1</span><span>）</span><span>(IP) = ((ss)*16 +(sp))</span></p>
<p><span>（</span><span>2</span><span>）</span><span>(sp) = (sp)+2</span></p>
<p><span>CPU</span><span>执行</span><span>retf</span><span>指令时，进行下面四步操作：</span></p>
<p><span>（</span><span>1</span><span>）</span><span>(IP) = ((ss)*16) + (sp)</span></p>
<p><span>（</span><span>2</span><span>）</span><span>(sp) = (sp) + 2</span></p>
<p><span>（</span><span>3</span><span>）</span><span>(CS) = ((ss)*16) + (sp)</span></p>
<p><span>（</span><span>4</span><span>）</span><span>(sp) = (sp) + 2</span></p>
<span><br clear=all></span>
<p><span>用汇编语法来解释</span><span>ret</span><span>和</span><span>retf</span><span>指令，则：</span></p>
<p><span>CPU</span><span>执行</span><span>ret</span><span>指令时，相当于进行：</span></p>
<p><span>pop IP</span></p>
<p>&nbsp;</p>
<p><span>CPU</span><span>执行</span><span>retf</span><span>指令时，相当于进行：</span></p>
<p><span>pop IP</span></p>
<p><span>pop CS</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>call</span></strong><strong><span>指令</span></strong></p>
<p><span>CPU</span><span>执行</span><span>call</span><span>指令时，进行两步操作：</span></p>
<p><span><span>（1）<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>将当前的</span><span>IP</span><span>或</span><span>CS</span><span>和</span><span>IP</span><span>压入栈中；</span></p>
<p><span><span>（2）<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>转移。</span></p>
<p>&nbsp;</p>
<p><span>call</span><span>指令不能实现短转移，除此之外，</span><span>call</span><span>指令实现转移的方法和</span><span>jmp</span><span>指令的原理相同。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>依据位移进行转移的</span><span>call</span></strong><strong><span>指令</span></strong></p>
<p><span>call </span><span>标号（将当前的</span><span>IP</span><span>压栈后，转到标号处执行指令）</span></p>
<p><span>CPU</span><span>执行此种格式的</span><span>call</span><span>指令时，进行如下的操作：</span></p>
<p><span>（</span><span>1</span><span>）</span><span>(sp) = (sp)-2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp; </span>((ss)*16 +(sp)) = (IP)</span></p>
<p><span>（</span><span>2</span><span>）</span><span>(IP) = (IP)+16</span><span>位位移。</span></p>
<p><span>16</span><span>位位移</span><span>=</span><span>&#8220;标号&#8221;处的地址</span><span>-call</span><span>指令后的第一个字节的地址；</span></p>
<p><span>16</span><span>位位移的范围为</span><span>-32768~32767</span><span>，用补码表示；</span></p>
<p><span>16</span><span>位位移由编译程序在编译时算出。</span></p>
<p>&nbsp;</p>
<p><span>用汇编语法来解释此种格式的</span><span>call</span><span>指令，则：</span></p>
<p><span>CPU</span><span>执行指令&#8220;</span><span>call </span><span>标号&#8221;时，相当于进行：</span></p>
<p><span>push IP</span></p>
<p><span>jmp near ptr </span><span>标号</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>转移的目的地址在指令中的</span><span>call</span></strong><strong><span>指令</span></strong></p>
<p><span>前面讲的</span><span>call</span><span>指令，其对应的机器指令中并没有转移的目的地址，而是相对于当前</span><span>IP</span><span>的转移位移。</span></p>
<p>&nbsp;</p>
<p><span>指令&#8220;</span><span>call far ptr </span><span>标号&#8221;实现的是段间转移。</span></p>
<p><span>CPU</span><span>执行此格式的</span><span>call</span><span>指令时，进行如下的操作：</span></p>
<p><span>（</span><span>1</span><span>）</span><span>(sp)=(sp)-2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;((ss)*16+(sp)) = (CS)</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;(sp)=(sp)-2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>((ss)*16+(sp)) = (IP)</span></p>
<p><span>（</span><span>2</span><span>）</span><span>(CS)=</span><span>标号所在段的段地址</span></p>
<p><span><span>&nbsp;&nbsp; </span><span>&nbsp;&nbsp;</span>(IP)=</span><span>标号在段中的偏移地址</span></p>
<p>&nbsp;</p>
<p><span>用汇编语法来解释此种格式的</span><span>call</span><span>指令，则：</span></p>
<p><span>CPU</span><span>执行指令&#8220;</span><span>call far ptr </span><span>标号&#8221;时，相当于进行：</span></p>
<p><span>push CS</span></p>
<p><span>push IP</span></p>
<p><span>jmp far ptr </span><span>标号</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>转移地址在寄存器中的</span><span>call</span></strong><strong><span>指令</span></strong></p>
<p><span>指令格式：</span><span>call 16</span><span>位寄存器</span></p>
<p><span>功能：</span></p>
<p><span>(sp) = (sp)-2</span></p>
<p><span>((ss)*16+(sp)) = (IP)</span></p>
<p><span>(IP) = (16</span><span>位寄存器</span><span>)</span></p>
<p>&nbsp;</p>
<p><span>用汇编语法来解释此种格式的</span><span>call</span><span>指令，</span><span>CPU</span><span>执行</span><span>call 16</span><span>位</span><span>reg</span><span>时，相当于进行：</span></p>
<p><span>push IP</span></p>
<p><span>jum 16</span><span>位寄存器</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>转移地址在内存中的</span><span>call</span></strong><strong><span>指令</span></strong></p>
<p><span>有两种格式：</span></p>
<p><span><span>1）&nbsp;</span></span><span>call word ptr </span><span>内存单元地址</span></p>
<p><span>相当于：</span></p>
<p><span>push IP</span></p>
<p><span>jum word ptr </span><span>内存单元地址</span></p>
<p><span><span>2）&nbsp;</span></span><span>call dword ptr </span><span>内存单元地址</span></p>
<p><span>相当于：</span></p>
<p><span>push CS</span></p>
<p><span>push IP</span></p>
<p><span>jmp dword ptr </span><span>内存单元地址</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>call</span></strong><strong><span>和</span><span>ret</span></strong><strong><span>的配合使用</span></strong></p>
<p><span>如何将它们配合使用来实现子程序的机制。</span></p>
<p><span>子程序的框架如下：</span></p>
<p><span>标号：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>指令</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p>&nbsp;</p>
<p><span>具有子程序的源程序的框架如下：</span></p>
<p><span>assume cs:code</span></p>
<p><span>code segment</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>main: &#8230;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>主程序</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;&#8230;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;call sub1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>调用子程序</span><span>sub1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;&#8230;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;mov ax,<st1:chmetcnv w:st="on" UnitName="C" SourceValue="4" HasSpace="False" Negative="False" NumberType="1" TCSC="0">4c</st1:chmetcnv>00h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;int 21h</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sub1: &#8230;.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>子程序</span><span>sub1</span><span>开始</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;&#8230;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;call sub2<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>调用子程序</span><span>sub2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;&#8230;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;ret<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>子程序返回</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sub2: &#8230;.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>子程序</span><span>sub2</span><span>开始</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;&#8230;</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;ret<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>子程序返回</span></p>
<p><span>code ends</span></p>
<p><span>end maint</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>mul</span></strong><strong><span>指令</span></strong></p>
<p><span>mul</span><span>是乘法指令。</span></p>
<p><span>使用</span><span>mul</span><span>做乘法的时候：</span></p>
<p><span><span>1）&nbsp;</span></span><span>两个相乘的数：两个相乘的数，要么都是</span><span>8</span><span>位，要么都是</span><span>16</span><span>位。</span></p>
<p><span>如果是</span><span>8</span><span>位，一个默认放在</span><span>AL</span><span>中，别一个放在</span><span>8</span><span>位寄存器或内存单元中；</span></p>
<p><span>如果是</span><span>16</span><span>位，一个默认在</span><span>AX</span><span>中，另一个放在</span><span>16</span><span>位寄存器或内存单元中。</span></p>
<p><span><span>2）&nbsp;</span></span><span>结果：如果是</span><span>8</span><span>位乘法，结果默认放在</span><span>AX</span><span>中；如果是</span><span>16</span><span>位乘法，结果高位默认在</span><span>DX</span><span>中存放，低位在</span><span>AX</span><span>中存放。</span></p>
<p>&nbsp;</p>
<p><span>格式如下：</span></p>
<p><span>mul reg</span></p>
<p><span>mul </span><span>内存单元</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>模块化程序设计</span></strong></p>
<p><span>call</span><span>与</span><span>ret</span><span>指令共同支持了汇编语言编程中的模块化设计。</span></p>
<p><span>在实际编程中，程序的模块化是必不可少的。</span></p>
<p><span>因为实现的问题比较复杂，对现实问题进行分析时，把它转化成为相互联系、不同层次的子问题，是必须的解决方法。</span></p>
<p><span>而</span><span>call</span><span>与</span><span>ret</span><span>指令对这种分析方法提供了程序实现上的支持。</span></p>
<p><span>利用</span><span>call</span><span>和</span><span>ret</span><span>指令，我们可以用简捷的方法，实现多个相互联系、功能独立的子程序来解决一个复杂的问题。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>参数和结果传递的问题</span></strong></p>
<p><span>子程序一般都要根据提供的参数处理一定的事务，处理后，将结果（返回值）提供给调用者。</span></p>
<p><span>其实，我们讨论参数和返回值传递的问题，实际上就是在探讨，应该<span>如何存储子程序需要的参数和产生的返回值</span>。</span></p>
<p>&nbsp;</p>
<p><span>;</span><span>说明：计算</span><span>N</span><span>的</span><span>3</span><span>次方</span></p>
<p><span>;</span><span>参数：</span><span>(bx)=N</span></p>
<p><span>;</span><span>结果：</span><span>(dx:ax)=N^3</span></p>
<p><span>cube:mov ax,bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mul bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mul bx</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ret</span></p>
<p><span>注意，编程时的良好风格，应有有详细的注释。包含对子程序的功能、参数和结果的说明。</span></p>
<p>&nbsp;</p>
<p><span>用寄存器来存储参数和结果是最常使用的方法。对于存放参数的寄存器和存放结果的寄存器，调用者和子程序的读写操作恰恰相反：调用者将参数送入参数寄存器，从结果寄存器中取到返回值；子程序从参数寄存器中取到参数，将返回值送入结果寄存器。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>批量数据的传递</span></strong></p>
<p><span>寄存器的数量终究有限，我们不可能简单地用寄存器来存放多个需要传递的数据。对于返回值，也有同样的问题。</span></p>
<p>&nbsp;</p>
<p><span>在这种时候，我们将批量数据放到内存中，然后将它们所在内存空间的首地址放在寄存器中，传递给需要的子程序。对于具有批量数据的返回结果，也可用同样的方法。</span></p>
<p>&nbsp;</p>
<p><span>除了用寄存器传递参数外，还有一种通用的方法是用栈来传递参数。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>寄存器冲突的问题</span></strong></p>
<p><span>一个一般化的问题，子程序中使用的寄存器，很可能在主程序中也要使用，造成了寄存器使用上的冲突。</span></p>
<p><span>那么我们如何来避免这种冲突呢？粗略地看，可以有两个方案：</span></p>
<p><span><span>1）&nbsp;</span></span><span>在编写调用子程序的程序时，注意看看子程序中有没有用到会产生冲突的寄存器，如果有，调用者使用别的寄存器；</span></p>
<p><span><span>2）&nbsp;</span></span><span>在编写子程序的时候，不要使用会产生冲突的寄存器。</span></p>
<p><span>以上两个方案，不具可行性，第一种给调用子程序的程序的编写造成很大麻烦。第二种不可能实现，子程序无法知道将来的调用情况。</span></p>
<p>&nbsp;</p>
<p><span>我们希望：</span></p>
<p><span><span>1）&nbsp;</span></span><span>编写调用子程序的程序的时候不必关心子程序到底使用了哪些寄存器；</span></p>
<p><span><span>2）&nbsp;</span></span><span>编写子程序的时候不必关心调用者使用了哪些寄存器；</span></p>
<p><span><span>3）&nbsp;</span></span><span>不会发生寄存器冲突。</span></p>
<p>&nbsp;</p>
<p><span>解决这个问题的简捷方法是，在子程序的开始将子程序中所有用到的寄存器中的内容都保存起来，在子程序返回前再恢复。我们可以用栈来保存寄存器中的内容。</span></p>
<p>&nbsp;</p>
<p><span>以后，我们编写子程序的标准框架如下：</span></p>
<p><span>子程序开始：子程序中使用的寄存器入栈</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>子程序内容</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>子程序中使用的寄存器出栈</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>返回（</span><span>ret</span><span>、</span><span>retf</span><span>）</span></p>
<p>&nbsp;</p>
<p><span>要注意寄存器入栈和出栈的顺序。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>实验</span><span>10 </span></strong><strong><span>编写子程序</span></strong></p>
<p><span><span>1、&nbsp;</span></span><span>显示字符串</span></p>
<p><span>问题：显示字符串是现实工作中经常要用到的功能，应该编写一个通用的子程序来实现这个功能。我们应该提供灵活的调用接口，使调用者可以决定显示的位置（行、列）、内容和颜色。</span></p>
<p><span>子程序描述</span></p>
<p><span>名称：</span><span>show_str</span></p>
<p><span>功能：在指定的位置，用指定的颜色，显示一个用</span><span>0</span><span>结束的字符串。</span></p>
<p><span>参数：</span><span>(dh)=</span><span>行号（取值范围</span><span>0~24</span><span>），</span><span>(dl)=</span><span>列号（取值范围</span><span>0~79</span><span>），</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;(cl)=</span><span>颜色，</span><span>ds:si</span><span>指向字符串的首地址</span></p>
<p><span>返回：无</span></p>
<p><span>应用举例：在屏幕的</span><span>8</span><span>行</span><span>3</span><span>列，用绿色显示</span><span>data</span><span>段中的字符串。</span></p>
<p>&nbsp;</p>
<p><span><span>1）&nbsp;</span></span><span>子程序的入口参数是屏幕上的行号和列号，注意在子程序内部要将它们转化为显存中的地址，首先要分析一下屏幕上的行列位置和显存地址的对应关系。</span></p>
<p><span><span>2）&nbsp;</span></span><span>注意保存子程序中用到的相关寄存器。</span></p>
<p><span><span>3）&nbsp;</span></span><span>空上子程序的内部处理和显存的结构密切相关，但是向外提供了与显存结构无关的接口。通过调用这个子程序，进行字符串的显示时可以不必了解显存的结构，为编程提供了方便。在实验中，注意体会这种设计思想。</span></p>
<p>&nbsp;</p>
<p><span><span>2、&nbsp;</span></span><span>解决除法溢出的问题</span></p>
<p><span>问题：</span><span>div</span><span>指令可以做除法。当进行</span><span>8</span><span>位除法的时候，用</span><span>al</span><span>存储结果的商，</span><span>ah</span><span>存储结果的余数；进行</span><span>16</span><span>位除法的时候，用</span><span>ax</span><span>存储结果的商，</span><span>dx</span><span>存储结果的余数。可是，现在有一个问题，如果结果的商大于</span><span>ah</span><span>或</span><span>ax</span><span>所能存储的最大值，那么将如何？</span></p>
<p>&nbsp;</p>
<p><span>当</span><span>CPU</span><span>执行</span><span>div</span><span>等除法指令的时候，如果发生结果数据超出了寄存器所能存储的范围，将引发</span><span>CPU</span><span>的一个内部错误，这个错误被称为：除法溢出。</span></p>
<p>&nbsp;</p>
<p><span>子程序描述</span></p>
<p><span>名称：</span><span>divdw</span></p>
<p><span>功能：进行不会产生溢出的除法运算，被除数为</span><span>dword</span><span>型，除数为</span><span>word</span><span>型，结果为</span><span>dword</span><span>型。</span></p>
<p><span>参数：</span><span>(ax)=dword</span><span>型数据的低</span><span>16</span><span>位</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;(dx)=dword</span><span>型数据的高</span><span>16</span><span>位</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;(cx)=</span><span>除数</span></p>
<p><span>返回：</span><span>(dx)=</span><span>结果的高</span><span>16</span><span>位，</span><span>(ax)=</span><span>结果的低</span><span>16</span><span>位</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;(cx)=</span><span>余数</span></p>
<p><span>应用举例：计算</span><span>1000000/10</span><span>（</span><span>F4240H/0AH</span><span>）</span></p>
<p>&nbsp;</p>
<p><span><span>3、&nbsp;</span></span><span>数值显示</span></p>
<p><span>问题：编程：将</span><span>data</span><span>段中的数据以十进制的形式显示出来。</span></p>
<p><span>数据在内存中都是二进制信息，标记了数值的大小。要把它们显示到屏幕上，成为我们能够读懂的信息，需要进行信息的转化。</span></p>
<p><span>比如，数值</span><span>12666</span><span>，在机器中存储为二进制信息：</span><span>11000101111010B</span><span>（</span><span>317AH</span><span>），计算机可以理解它。而我们要在显示器上读到可以理解的数值</span><span>12666</span><span>，我们看到的应该是一串字符：&#8220;</span><span>12666</span><span>&#8221;，由于显卡遵循的是</span><span>ASCII</span><span>编码，为了让我们能在显示器上看到这串字符，它在机器中应以</span><span>ASCII</span><span>码的形式存储为：</span><span>31H</span><span>、</span><span>32H</span><span>、</span><span>36H</span><span>、</span><span>36H</span><span>、</span><span>36H</span><span>（字符&#8220;</span><span>0</span><span>&#8221;</span><span>~</span><span>&#8220;</span><span>9</span><span>&#8221;对应的</span><span>ASCII</span><span>码为</span><span>30H~39H</span><span>）。</span></p>
<p>&nbsp;</p>
<p><span>通过上面的分析可以看到，在概念世界中，有一个抽象的数据</span><span>12666</span><span>，它表示了一个数值的大小。在现实世界中它可以有多种表示形式，可以在电子机器中以高低电平（二进制）的形式存储，也可以在纸上、黑板上、屏幕上以人类的语言&#8220;</span><span>12666</span><span>&#8221;来书写。现在，我们面临的问题就是，要将同一抽象的数据，从一种表示形式转化为另一种表示形式。</span></p>
<p>&nbsp;</p>
<p><span>要将数据用十进制形式显示到屏幕上，要进行两步工作：</span></p>
<p><span><span>1）&nbsp;</span></span><span>将用二进制信息存储的数据转变为十进制形式的字符串；</span></p>
<p><span><span>2）&nbsp;</span></span><span>显示十进制形式的字符串。</span></p>
<p>&nbsp;</p>
<p><span>子程序描述</span></p>
<p><span>名称：</span><span>dtoc</span></p>
<p><span>功能：将</span><span>word</span><span>型数据转变为表示十进制数的字符串，字符串以</span><span>0</span><span>为结尾符。</span></p>
<p><span>参数：</span><span>(ax)=word</span><span>型数据</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;ds:si</span><span>指向字符串的首地址</span></p>
<p><span>返回：无</span></p>
<p>&nbsp;</p>
<p><span>应用举例：编程，将数据</span><span>12666</span><span>以十进制的形式在屏幕的</span><span>8</span><span>行</span><span>3</span><span>列，用绿色显示出来。</span></p>
<p>&nbsp;</p>
<p><span>分析：要得到字符串&#8220;</span><span>12666</span><span>&#8221;，就是要得到一列表示该字符串的</span><span>ASCII</span><span>码：</span><span>31H</span><span>、</span><span>32H</span><span>、</span><span>36H</span><span>、</span><span>36H</span><span>、</span><span>36H</span><span>。</span></p>
<p><span>十进制数码字符对应的</span><span>ASCII</span><span>码</span><span>=</span><span>十进制数码值</span><span>+30H</span><span>。</span></p>
<p><span>要得到表示十进制数的字符串，先求十进制数每位的值。</span></p>
<p><span>例如，对于</span><span>12666</span><span>，先求得每位的值：</span><span>1</span><span>、</span><span>2</span><span>、</span><span>6</span><span>、</span><span>6</span><span>、</span><span>6</span><span>。再将这些数分别加上</span><span>30H</span><span>，便得到了表示</span><span>12666</span><span>的</span><span>ASCII</span><span>码串，</span><span>31H</span><span>、</span><span>32H</span><span>、</span><span>36H</span><span>、</span><span>36H</span><span>、</span><span>36H</span><span>。</span></p>
<p><span>那么，怎样得到每位的值呢？采用下列方法（除</span><span>10</span><span>取余法）：</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>12666/10=1266&#8230;&#8230;6</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>&nbsp;&nbsp; </span>1266/10=126&#8230;&#8230;..6</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>126/10=12&#8230;&#8230;&#8230;6</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>12/10=1&#8230;&#8230;&#8230;..2</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>1/10=0&#8230;&#8230;&#8230;..1</span></p>
<p><span>可见，用</span><span>10</span><span>除</span><span>12666</span><span>，共除</span><span>5</span><span>次，记下每次的余数，就得到了每位的值。</span></p>
<p><span>综合以上分析，可得出处理过程如下：</span></p>
<p><span>用</span><span>12666</span><span>除以</span><span>10</span><span>，循环</span><span>5</span><span>次，记下每次的余数；将每次的余数分别加</span><span>30H</span><span>，使得到了表示十进制数的</span><span>ASCII</span><span>码串。</span></p>
<p><span>只要是除到商为</span><span>0</span><span>，各位的值就已经全部求出。可以使用</span><span>jcxz</span><span>指令来实现相关的功能。</span></p>
<p>&nbsp;<br></p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/122170.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-04 10:32 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/04/122170.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编语言--转移指令的原理</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/04/122169.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Wed, 04 Aug 2010 02:30:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/04/122169.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/122169.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/04/122169.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/122169.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/122169.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>转移指令的原理</span></p>
<p><span>可以修改</span><span>IP</span><span>，或同时修改</span><span>CS</span><span>和</span><span>IP</span><span>的指令统称为<strong>转移指令</strong>。</span></p>
<p><span>概括以讲，转移指令就是可以控制</span><span>CPU</span><span>执行内存中某处的代码的指令。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>8086CPU</span><span>的转移行为有以下几类：</span></p>
<p><span><span>l<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>只修改</span><span>IP</span><span>时，称为<strong>段内转移</strong>，比如：</span><span>jmp ax</span><span>。</span></p>
<p><span><span>l<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>同时修改</span><span>CS</span><span>和</span><span>IP</span><span>时，称为<strong>段间转移</strong>，比如：</span><span>jmp 1000:0</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>由于转移指令对</span><span>IP</span><span>的修改范围不同，段内转换又分为：短转移和近转移。</span></p>
<p><span><span>l<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>短转移</span><span>IP</span><span>的修改范围为</span><span>-128~127</span><span>。</span></p>
<p><span><span>l<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>近转移</span><span>IP</span><span>的修改范围为</span><span>-32768~32767</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>8086CPU</span><span>的转移指令分为以下几类：</span></p>
<p><span><span>l<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>无条件转移指令（如：</span><span>jmp</span><span>）</span></p>
<p><span><span>l<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>条件转移指令</span></p>
<p><span><span>l<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>循环指令（如：</span><span>loop</span><span>）</span></p>
<p><span><span>l<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>过程</span></p>
<p><span><span>l<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>中断</span></p>
<p>&nbsp;</p>
<p><span>这些转移指令转移的前提条件可能不同，但转移的基本原理是相同的。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>操作符</span><span>offset</span></strong></p>
<p><span>操作符</span><span>offset</span><span>在汇编语言中是由编译器处理的符号，它的功能是取得标号的偏移地址。如：</span></p>
<p><span>assume cs:codesg</span></p>
<p><span>codesg segment</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>start: mov ax, offset start<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>相当于</span><span>mov ax,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>s:mov ax, offset s<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>相当于</span><span>mov ax,3</span></p>
<p><span>codesg ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>jmp</span><span>指令</span></p>
<p><span>jmp</span><span>为无条件转移指令，可以只修改</span><span>IP</span><span>，也可以同时修改</span><span>CS</span><span>和</span><span>IP</span><span>。</span></p>
<p><span>jmp</span><span>指令要给出两种信息：</span></p>
<p><span><span>l<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>转移的目的地址</span></p>
<p><span><span>l<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>转移的距离（段间转移、段内短转移、段人近转移）</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>依据位移进行转移的</span><span>jmp</span></strong><strong><span>指令</span></strong></p>
<p><span>jmp short </span><span>标号（转到标号处执行指令）</span></p>
<p><span>这种格式的</span><span>jmp</span><span>指令实现的是段内短转移，它对</span><span>IP</span><span>的修改范围为</span><span>-128~127</span><span>，也就是说，它向前转移时可以最多越过</span><span>128</span><span>个字节，向后转移可以最多越过</span><span>127</span><span>个字节。</span></p>
<p><span>jmp</span><span>指令中的&#8220;</span><span>short</span><span>&#8221;符号，说明指令进行的是短转移。</span></p>
<p><span>jmp</span><span>指令中的&#8220;标号&#8221;是代码段中的标号，指明了指令要转移的目的地，转移指令结束后，</span><span>CS</span><span>：</span><span>IP</span><span>应该指向标号处的指令。</span></p>
<p><span>比如：</span></p>
<p><span>assume cs:codesg</span></p>
<p><span>codesg segment</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>start: mov ax,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>mov bx,0</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>jmp short s</span></span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>add ax,1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;s: inc ax</span></p>
<p><span>codesg ends</span></p>
<p><span>end start</span></p>
<p>&nbsp;</p>
<p><span>CPU</span><span>在执行</span><span>jmp</span><span>指令的时候并不需要转移的目的地址。</span></p>
<p><span>在&#8220;</span><span>jmp short </span><span>标号&#8221;指令所对应的机器码中，并不包含转移的目的地址，而包含的是<span>转移的位移</span>，这个位移，是编译器根据汇编指令中的&#8220;标号&#8221;计算出来的。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>实际上，指令&#8220;</span><span>jmp short </span><span>标号&#8221;的功能为：</span><span>(IP)=(IP)+8</span><span>位位移。</span></p>
<p><span>(1)8</span><span>位位移</span><span>=</span><span>&#8220;标号&#8221;处的地址</span><span>-jmp</span><span>指令后的第一个字节的地址；</span></p>
<p><span>(2)short</span><span>指明此处的位移为</span><span>8</span><span>位移；</span></p>
<p><span>(3)8</span><span>位位移的范围为</span><span>-128~127</span><span>，用补码表示；</span></p>
<p><span>(4)8</span><span>位位移由编译器在编译时算出。</span></p>
<p>&nbsp;</p>
<p><span>还有一种和指令&#8220;</span><span>jmp short </span><span>标号&#8221;功能相近的指令格式：</span><span>jmp near prt </span><span>标号，它实现的是段内近转移。</span></p>
<p>&nbsp;</p>
<p><span>指令&#8220;</span><span>jmp near ptr </span><span>标号&#8221;的功能为：</span><span>(IP)=(IP)+16</span><span>位位移。</span></p>
<p><span>(1)16</span><span>位位移</span><span>=</span><span>指令&#8220;标号&#8221;处的地址</span><span>-jmp</span><span>指令后的第一个字节的地址；</span></p>
<p><span>(2)near ptr</span><span>指明此处的位移为</span><span>16</span><span>位位移，进行的是段内近转移；</span></p>
<p><span>(3)16</span><span>位位移的范围为</span><span>-32768~32767</span><span>，用补码表示。</span></p>
<p><span>(4)16</span><span>位位移由编译器程序在编译时算出。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>转移的目的地址在指令中的</span><span>jmp</span></strong><strong><span>指令</span></strong></p>
<p><span>前面讲的</span><span>jmp</span><span>指令，其对应的机器指令中并没有转移的目的地址，而是相对于当前</span><span>IP</span><span>的转移地址。</span></p>
<p>&nbsp;</p>
<p><span>指令&#8220;</span><span>jmp far ptr </span><span>标号&#8221;实现的是段间转移，又称为远转移。功能如下：</span></p>
<p><span>(CS)=</span><span>标号所在段的段地址；（</span><span>IP</span><span>）</span><span>=</span><span>标号所在段中的偏移地址。</span></p>
<p><span>far ptr</span><span>指明了指令用标号的段地址和偏移地址修改</span><span>CS</span><span>和</span><span>IP</span><span>。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>转移地址在寄存器中的</span><span>jmp</span></strong><strong><span>指令</span></strong></p>
<p><span>指令格式：</span><span>jmp 16</span><span>位寄存器</span></p>
<p><span>功能：</span><span>(IP)=(16</span><span>位寄存器</span><span>)</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>转移地址在内存中的</span><span>jmp</span></strong><strong><span>指令</span></strong></p>
<p><span>有两种格式：</span></p>
<p><span><span>（1）<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>jmp word ptr </span><span>内存单元地址（段内转移）</span></p>
<p><span>功能：从内存单元地址处开始存放着一个字，是转移的目的偏移地址。</span></p>
<p><span>内存单元寺睛可用寻址方式的任一格式给出。比如：</span></p>
<p><span>mov ax,0123H</span></p>
<p><span>mov ds:[0],ax</span></p>
<p><span>jmp word ptr ds:[0]</span></p>
<p><span>执行后，（</span><span>IP</span><span>）</span><span>=0123H</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>又如：</span></p>
<p><span>mov ax,0123H</span></p>
<p><span>mov [bx],ax</span></p>
<p><span>jmp word ptr [bx]</span></p>
<p><span>执行后，</span><span>(IP)=0123H</span></p>
<p>&nbsp;</p>
<p><span><span>（2）<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>jmp dword ptr </span><span>内存单元地址（段间转移）</span></p>
<p><span>功能：从内存单元地址处开始存放着两个字，高地址处的字是转移的目的段地址，低地址处是转移的目的偏移地址。</span></p>
<p><span>（</span><span>CS</span><span>）</span><span>=</span><span>（内存单元地址</span><span>+2</span><span>）</span></p>
<p><span>（</span><span>IP</span><span>）</span><span>=</span><span>（内存单元地址）</span></p>
<p><span>内存单元地址可用寻址方式的任一格式给出。如：</span></p>
<p><span>mov ax,0123H</span></p>
<p><span>mov ds:[0],ax</span></p>
<p><span>mov word ptr ds:[2],0</span></p>
<p><span>jmp dword ptr ds:[0]</span></p>
<p><span>执行后，（</span><span>CS</span><span>）</span><span>=0</span><span>，（</span><span>IP</span><span>）</span><span>=0123H</span><span>，</span><span>CS</span><span>：</span><span>IP</span><span>指向</span><span>0000:0123</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>又如，</span></p>
<p><span>mov ax,0123H</span></p>
<p><span>mov [bx],ax</span></p>
<p><span>mov word ptr [bx+2],0</span></p>
<p><span>jmp dword ptr [bx]</span></p>
<p><span>执行后，（</span><span>CS</span><span>）</span><span>=0</span><span>，（</span><span>IP</span><span>）</span><span>=0123H</span><span>，</span><span>CS:IP</span><span>指向</span><span>0000:0123</span><span>。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>jcxz</span></strong><strong><span>指令</span></strong></p>
<p><span>jcxz</span><span>指令为有条件转移指令，所有的有条件转移指令都是短转移，在对应的机器码中包含转移的位移，而不是目的地址。对</span><span>IP</span><span>的修改范围都为：</span><span>-128~127</span><span>。</span></p>
<p><span>指令格式：</span><span>jcxz </span><span>标号（如果</span><span>(cx)=0, </span><span>转移到标号处执行。）</span></p>
<p><span>操作：当（</span><span>cx</span><span>）</span><span>=0</span><span>时，（</span><span>IP</span><span>）</span><span>=</span><span>（</span><span>IP</span><span>）</span><span>+8</span><span>位位移；</span></p>
<p><span>8</span><span>位位移</span><span>=</span><span>&#8220;标号&#8221;处的地址</span><span>-jczx</span><span>指令后的第一个字节的地址；</span></p>
<p><span>8</span><span>位位移的范围为</span><span>-128~127</span><span>，用补码表示；</span></p>
<p><span>8</span><span>位位移由编译器在编译时算出。</span></p>
<p><span>当（</span><span>cx</span><span>不等于</span><span>0</span><span>时），什么也不做（程序向下执行）。</span></p>
<p>&nbsp;</p>
<p><span>指令&#8220;</span><span>jcxz </span><span>标号&#8221;的功能相当于：</span></p>
<p><span>if (cx)==0 jmp short </span><span>标号；</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>loop</span></strong><strong><span>指令</span></strong></p>
<p><span>loop</span><span>指令为循环指令，所有的循环指令都是短转移，在对应的机器码中包含转移的位移，而不是目的地址。对</span><span>IP</span><span>的修改范围都为：</span><span>-128~127</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>指令格式：</span><span>loop </span><span>标号（（</span><span>cx</span><span>）</span><span>=</span><span>（</span><span>cx</span><span>）</span><span>-1</span><span>，如果</span><span>(cx)</span><span>&#8800;</span><span>0</span><span>，转移到标号处执行。）</span></p>
<p><span>操作：（</span><span>1</span><span>）</span><span>(cx)=(cx)-1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&nbsp;(2) </span><span>如果</span><span>(cx)</span><span>&#8800;</span><span>0</span><span>，</span><span>(IP)=(IP)+8</span><span>位位移。</span></p>
<p><span>8</span><span>位位移</span><span>=</span><span>&#8220;标号&#8221;处的地址</span><span>-loop</span><span>指令后的第一个字节的地址；</span></p>
<p><span>8</span><span>位位移的范围为</span><span>-128~127</span><span>，用补码表示；</span></p>
<p><span>8</span><span>位位移由编译器在编译时算出。</span></p>
<p><span>如果</span><span>(cx)=0</span><span>，什么也不做（程序向下执行）。</span></p>
<p>&nbsp;</p>
<p><span>指令&#8220;</span><span>loop </span><span>标号&#8221;的功能相当于：</span></p>
<p><span>(cx)--;</span></p>
<p><span>if ( (cx)</span><span>&#8800;</span><span>0) jmp short </span><span>标号；</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>根据位移进行转移的意义</span></strong></p>
<p><span>jmp short </span><span>标号</span></p>
<p><span>jmp near ptr </span><span>标号</span></p>
<p><span>jcxz </span><span>标号</span></p>
<p><span>loop </span><span>标号</span></p>
<p>&nbsp;</p>
<p><span>它们对</span><span>IP</span><span>的修改是根据转移目的地址和转移起始地址之间的位移来进行的。</span></p>
<p><span>在它们对应的机器码中不包含转移的目的地址，而包含的是到目的地址的位移。</span></p>
<p><span>这种设计，方便了程序段在内存中的<strong>浮动装配</strong>。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>编译器对转移位移超界的检测</span></p>
<p><span>根据位移进行转移的指令，它们在转移范围受到转移位移的限制，如果在源程序中出现了转移范围超界的问题，在编译的时候，编译器将报错。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/122169.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-04 10:30 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/04/122169.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编语言--数据处理的两个基本问题</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/04/122168.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Wed, 04 Aug 2010 02:29:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/04/122168.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/122168.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/04/122168.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/122168.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/122168.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>数据处理的两个基本问题</span></p>
<p>&nbsp;</p>
<p><strong><span>汇编语言中数据位置的表达</span></strong></p>
<p><span>在汇编语言中如何表达数据的位置？</span></p>
<p><span>汇编语言中用三个概念来表达数据的位置：</span></p>
<p><span><span>1）&nbsp;</span></span><span>立即数（</span><span>idata</span><span>）</span></p>
<p><span><span>2）&nbsp;</span></span><span>寄存器</span></p>
<p><span><span>3）&nbsp;</span></span><span>内存（段地址</span><span>SA</span><span>和偏移地址</span><span>EA</span><span>）</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>寻址方式</span></strong></p>
<p><span>当数据存放在内存中的时候，我们可以用多种方式来给定这个内存单元的偏移地址，这种定位内存单元的方法一般被称为寻址方式。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>指令要处理的数据有多长？</span></strong></p>
<p><span>8086CPU</span><span>的指令，可以处理两种尺寸的数据，</span><span>byte</span><span>和</span><span>word</span><span>。</span></p>
<p><span>所以在机器指令中要指明指令进行的是字操作还是字节操作。</span></p>
<p><span><span>1）&nbsp;</span></span><span>通过寄存器名指明要处理的数据的尺寸。</span></p>
<p><span><span>2）&nbsp;</span></span><span>在没寄存器名存在的情况下，用操作符</span><span>X ptr</span><span>指明内存单元的长度，</span><span>X</span><span>在汇编指令中可以为</span><span>word</span><span>或</span><span>byte</span><span>。</span></p>
<p><span><span>3）&nbsp;</span></span><span>其他方法，有些指令默认了访问的是字单元还是字节单元，比如，</span><span>push[1000H]</span><span>就不用指明访问的是字单元还是字节单元，因为</span><span>push</span><span>指令只进行字操作。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>寻址方式的综合应用</span></strong></p>
<p><span>8086CPU</span><span>提供的如</span><span>[bx+si+idata]</span><span>的寻址方式为结构化数据的处理提供了方便。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>div</span></strong><strong><span>指令</span></strong></p>
<p><span>div</span><span>是除法指令。</span></p>
<p><span><span>1）&nbsp;</span></span><span>除数：有</span><span>8</span><span>位和</span><span>16</span><span>位两种，在一个寄存器或内存单元中。</span></p>
<p><span><span>2）&nbsp;</span></span><span>被除数：默认放在</span><span>AX</span><span>或</span><span>DX</span><span>和</span><span>AX</span><span>中，如果除数为</span><span>8</span><span>位，被除数则为</span><span>16</span><span>位，默认在</span><span>AX</span><span>中存放；如果除数为</span><span>16</span><span>位，被除数则为</span><span>32</span><span>位，在</span><span>DX</span><span>和</span><span>AX</span><span>中存放，</span><span>DX</span><span>存放高</span><span>16</span><span>位，</span><span>AX</span><span>存放低</span><span>16</span><span>位。</span></p>
<p><span><span>3）&nbsp;</span></span><span>结果：如果除数为</span><span>8</span><span>位，则</span><span>AL</span><span>存储除法操作的商，</span><span>AH</span><span>存储除法操作的余数；如果除数为</span><span>16</span><span>位，同</span><span>AX</span><span>存储除法操作的商，</span><span>DX</span><span>存储除法操作的余数。</span></p>
<p><span>格式如下：</span></p>
<p><span>div reg</span></p>
<p><span>div </span><span>内存单元</span></p>
<p>&nbsp;</p>
<p><span>div byte ptr ds:[0]<span>&nbsp;&nbsp;&nbsp;&nbsp; </span>;</span><span>除数指定为</span><span>8</span><span>位</span> </p>
<p><span>(al) = (ax)/((ds)*16+0)</span><span>的商；</span></p>
<p><span>(ah)=(ax)/((ds)*16+0)</span><span>的余数。</span></p>
<p>&nbsp;</p>
<p><span>div word ptr es:[0]</span></p>
<p><span>(ax) =( (dx)*10000H+(ax))/((ex)*16+0)</span><span>的商；</span></p>
<p><span>(dx)= ( (dx)*10000H+(ax))/((ex)*16+0)</span><span>的余数。</span></p>
<p>&nbsp;</p>
<p><span>div byte ptr [bx+si+8]</span></p>
<p><span>(al)=(ax)/((ds)*16+(bx)+(si)+8)</span><span>的商；</span></p>
<p><span>(ah)= (ax)/((ds)*16+(bx)+(si)+8)</span><span>的余数。</span></p>
<p>&nbsp;</p>
<p><span>div word ptr [bx+si+8]</span></p>
<p><span>(ax)=((dx)*10000H)+(ax))/((ds)*16+(bx)+(si)+8))</span><span>的商；</span></p>
<p><span>(ax)=((dx)*10000H)+(ax))/((ds)*16+(bx)+(si)+8))</span><span>的余数。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>实践：</span><span>100001/100</span></p>
<p><span>被除数</span><span>100001</span><span>大于</span><span>65535</span><span>（</span><span>FFFF</span><span>、十六位），不能用</span><span>ax</span><span>寄存器存放，所以我们只能用</span><span>dx</span><span>和</span><span>ax</span><span>两个寄存器联合存放</span><span>100001</span><span>，也就是说要进行</span><span>16</span><span>位的除法，除数</span><span>100</span><span>小于</span><span>255</span><span>，可以在一个</span><span>8</span><span>位寄存器中存放，但是，因为被除数是</span><span>32</span><span>位的，除数应为</span><span>16</span><span>位，所以要用一个</span><span>16</span><span>位寄存器来存放除数</span><span>100</span><span>。</span></p>
<p><span>100001</span><span>表为十六进制：</span><st1:chmetcnv w:st="on" UnitName="a" SourceValue="186" HasSpace="False" Negative="False" NumberType="1" TCSC="0"><span>186A</span></st1:chmetcnv><span>1H</span><span>。</span></p>
<p><span>mov dx,1</span></p>
<p><span>mov ax,<st1:chmetcnv w:st="on" UnitName="a" SourceValue="86" HasSpace="False" Negative="False" NumberType="1" TCSC="0">86A</st1:chmetcnv>1H</span></p>
<p><span>mov bx,100</span></p>
<p><span>div bx</span></p>
<p>&nbsp;</p>
<p><span>程序执行后，</span><span>(ax)=03E8H</span><span>（即</span><span>1000</span><span>），</span><span>(dx)=1</span><span>（余数为</span><span>1</span><span>）。</span></p>
<p>&nbsp;</p>
<p><span>计算</span><span>1001/100</span></p>
<p><span>被除数</span><span>1001</span><span>可用</span><span>ax</span><span>寄存器存放。除数</span><span>100</span><span>可用</span><span>8</span><span>位寄存器存放。即可进行</span><span>8</span><span>位的除法。</span></p>
<p><span>mov ax,1001</span></p>
<p><span>mov bl,100</span></p>
<p><span>div bl</span></p>
<p><span>程序执行后，</span><span>(al)=0AH</span><span>（即</span><span>10</span><span>），</span><span>(ah)=1</span><span>（余数为</span><span>1</span><span>）。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>伪指令</span><span>dd</span></strong></p>
<p><span>db<span>&nbsp;&nbsp;&nbsp; </span></span><span>定义字节型数据；</span><span>define byte</span><span>；</span><span>&nbsp;</span><span>一个字节表示</span><span>8</span><span>个位；</span></p>
<p><span>dw<span>&nbsp;&nbsp; </span></span><span>定义字型数据；</span><span><span>&nbsp;&nbsp;&nbsp; </span>define word</span><span>；</span><span><span> </span></span><span>一个节表示两个字节；</span></p>
<p><span>dd<span>&nbsp;&nbsp;&nbsp; </span></span><span>定义双字型数据；</span><span>define dword(double word</span><span>，双字</span><span>)</span><span>。两个字。四个字节。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>data segment</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db 1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>dw 1</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>dd 1</span></p>
<p><span>data ends</span></p>
<p>&nbsp;</p>
<p><span>在</span><span>data</span><span>段中定义了三个数据：</span></p>
<p><span>第一个数据为</span><span>01H</span><span>，在</span><span>data:0</span><span>处，占</span><span>1</span><span>个字节；</span></p>
<p><span>第二个数据为</span><span>0001H</span><span>，在</span><span>data:1</span><span>处，占</span><span>1</span><span>个字，两个字节；</span></p>
<p><span>第三个数据为</span><span>00000001H</span><span>，在</span><span>data:3</span><span>处，点</span><span>2</span><span>个字，四个字节。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><strong><span>dup</span></strong></p>
<p><span>dup</span><span>是一个操作符，在汇编语言中同</span><span>db</span><span>、</span><span>dw</span><span>、</span><span>dd</span><span>等一样，也是由编译器识别处理的符号。它是和</span><span>db</span><span>、</span><span>dw</span><span>、</span><span>dd</span><span>等数据定义伪指令配合使用的，用来进行数据的重复。如：</span></p>
<p><span>db 3 dup (0)</span></p>
<p><span>定义了</span><span>3</span><span>个字节，它们的值都是</span><span>0</span><span>，相当于</span><span> db 0,0,0</span></p>
<p><span>db 3 dup (0,1,2)</span></p>
<p><span>定义了</span><span>9</span><span>个字节，它们是</span><span>0</span><span>、</span><span>1</span><span>、</span><span>2</span><span>、</span><span>0</span><span>、</span><span>1</span><span>、</span><span>2</span><span>、</span><span>0</span><span>、</span><span>1</span><span>、</span><span>2</span><span>，相当于</span><span>db 0,1,2, 0,1,2, 0,1,2</span></p>
<p><span>db 3 dup (&#8216;abc&#8217;, &#8216;ABC&#8217;)</span></p>
<p><span>定义了</span><span>18</span><span>个字节，它们是</span><span>&#8217; abcABC abcABC abcABC&#8217;</span><span>，相当于</span><span> db &#8216;abcABC abcABC abcABC&#8217;</span></p>
<p>&nbsp;</p>
<p><span>dup</span><span>的使用格式如下：</span></p>
<p><span>db </span><span>重复的次数</span><span> dup (</span><span>重复的字节型数据</span><span>)</span><span>。</span></p>
<p><span>dw </span><span>重复的次数</span><span> dup (</span><span>重复的字型数据</span><span>)</span><span>。</span></p>
<p><span>dd </span><span>重复的次数</span><span> dup (</span><span>重复的双字数据</span><span>)</span><span>。</span></p>
<p>&nbsp;</p>
<p><span>dup</span><span>是一个十分有用的操作符，比如我们要定义一个容量为</span><span>200</span><span>个字节的栈段。</span></p>
<p><span>stack segment</span></p>
<p><span><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>db 200 dup (0)</span></p>
<p><span>stack ends</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/122168.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-04 10:29 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/04/122168.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编语言--更灵活的定位内存地址的方法</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/04/122166.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Wed, 04 Aug 2010 02:28:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/04/122166.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/122166.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/04/122166.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/122166.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/122166.html</trackback:ping><description><![CDATA[<p>前面学习了用[0]、[bx]在访问内存的指令中，定位内存单元的地址的方法。<br>下面来学习一些更灵活的定位内存地址的方法和相关的编程方法。let's go。<br><br>先来学习一下几个知识点，用于后续讲解实例时用。<br><br>and和or指令<br>1）and指令：逻辑与指令，按位进行与运算。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;通过该指令可将操作对象的相应位设为0，其他位不变。<br><br>2）or指令：逻辑或指令，按位进行或运算。<br>&nbsp;&nbsp;&nbsp;&nbsp; 通过该指令可将操作对象的相应位设为1，其他位不变。<br><br><br>[未完待续...]<br><br><br></p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/122166.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-04 10:28 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/04/122166.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编语言--包含多个段的程序</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/04/122163.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Wed, 04 Aug 2010 02:21:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/04/122163.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/122163.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/04/122163.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/122163.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/122163.html</trackback:ping><description><![CDATA[<p>大多数有用的程序，都要处理数据，使用栈空间，当然也都必须有指令，为了程序设计上的清晰和方便，我们一般定义不同的段来存放它们，将数据、代码、栈放入不同的段中。<br><br>先来体会一下不使用多个段的情况<br><br>在代码段中使用数据<br><br><br>[未完待续...]<br><br><br></p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/122163.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-04 10:21 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/04/122163.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编语言--[bx]和loop指令</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/04/122161.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Wed, 04 Aug 2010 02:09:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/04/122161.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/122161.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/04/122161.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/122161.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/122161.html</trackback:ping><description><![CDATA[1、[bx]和内存单元的描述<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [bx]是什么呢？和[0]有些类似，[0]表示内存单元，它的偏移地址是0。比如下面的指令中：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov ax,[0]<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;将一个内存单元的内容送入16位的寄存器ax，这个内存单元的长度为2字节（字单元），存放一个字，偏移地址为0，段地址在段寄存器ds中。<br><br>&nbsp;&nbsp;&nbsp;&nbsp; mov al,[0]<br>&nbsp;&nbsp;&nbsp;&nbsp; 将一个内存单元的内容送入寄存8位的寄存器al，这个内存单元的长度为1字节（字节单元），存放一个字节，偏移地址为0，段地址在ds中。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们要完整地描述一个内存单元，需要两种信息：（1）内存单元的地址；（2）内存单元的长度（类型）。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们用[0]表示一个内存单元时，0表示单元的偏移地址，段地址默认在ds中，单元的长度（类型）可以由具体指令中的其他操作对象（比如说寄存器）指出。<br><br>&nbsp;&nbsp;&nbsp;&nbsp; [bx]同样也表示一个内存单元，它的偏移地址在bx中，比如下面的指令：<br>&nbsp;&nbsp;&nbsp;&nbsp; mov ax,[bx]<br>&nbsp;&nbsp;&nbsp;&nbsp; 将一个内存单元的内容送入ax，这个内存单元的长度为2字节（字单元），存放一个字，偏移地址在bx中，段地址在ds中。<br><br>&nbsp;&nbsp;&nbsp;&nbsp; mov al,[bx]<br>&nbsp;&nbsp;&nbsp;&nbsp; 将一个内存单元的内容送入al，这个内存单元的长度为1字节（字节单元），存放一个字节，偏移地址在bx中，段地址在ds中。<br><br>2、loop<br>&nbsp;&nbsp;&nbsp;&nbsp; loop顾名思义，是循环的含义，显然这个指令和循环有关。<br><br><br>[未完待续...]<br><br><br><br>&nbsp;&nbsp;&nbsp; 
<img src ="http://www.cppblog.com/luqingfei/aggbug/122161.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-04 10:09 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/04/122161.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编语言--第1个程序</title><link>http://www.cppblog.com/luqingfei/archive/2010/08/04/122151.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Wed, 04 Aug 2010 01:44:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/08/04/122151.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/122151.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/08/04/122151.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/122151.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/122151.html</trackback:ping><description><![CDATA[终于可以编写第1个完整的程序了，以前都是在debug中写一些指令，在debug中执行。<br>现在开始编写完整的汇编语言程序，用编译器将它们编译成为可执行文件，在操作系统中运行。<br><br>为了能够透彻地理解一个完整的程序，我们将经历一个漫长的过程。<br><br><br><strong>一个源程序从写出到执行的过程</strong><br>第一步，编写汇编源程序<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;使用文本编辑器，如editplus,notepad2等<br>第二步，对源程序进行编译连接<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 使用汇编语言编译程序对源程序文件中的源程序进行编译，产生目标文件；再用连接程序对目标文件进行连接，生成可在操作系统中直接运行的可执行文件。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 可执行文件中包含两部分内容：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 1）程序（从源程序中的汇编指令翻译过来的机器码）和数据（源程序中定义的数据）<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2）相关的描述信息（比如，程序有多大、要占用多少内存空间等）<br><br>第三步，执行可执行文件中的程序<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在操作系统中，执行可执行文件中的程序。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 操作系统依照可执行文件中的描述信息，将可执行文件中的机器码和数据加载入内存。并进行相关的初始化（比如设置CS:IP指向第一条要执行的指令），然后由CPU执行程序。<br><br><br>（未完待续....）<br><br><br>
<img src ="http://www.cppblog.com/luqingfei/aggbug/122151.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-08-04 09:44 <a href="http://www.cppblog.com/luqingfei/archive/2010/08/04/122151.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]8086CPU的14个寄存器简介</title><link>http://www.cppblog.com/luqingfei/archive/2010/07/20/120881.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Tue, 20 Jul 2010 07:46:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/07/20/120881.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/120881.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/07/20/120881.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/120881.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/120881.html</trackback:ping><description><![CDATA[AX&nbsp;累加器&nbsp;AL&nbsp;底8位累加器&nbsp;AH&nbsp;高8位累加器&nbsp;EAX&nbsp;32位累加器 <br>BX&nbsp;基址寄存器&nbsp;BL&nbsp;底8位基址寄存器&nbsp;BH&nbsp;高8位基址寄存器&nbsp;EXB&nbsp;32位基址寄存器 <br>CX&nbsp;计数器&nbsp;CL第8位计数器&nbsp;CH&nbsp;高8位计数器&nbsp;ECX&nbsp;32位计数器 <br>DX&nbsp;数据寄存器&nbsp;DL&nbsp;第8位数据寄存器&nbsp;DH&nbsp;高8位数据寄存器&nbsp;EDX&nbsp;32位数据寄存器 <br>CS:&nbsp;代码段寄存器 <br>DS:&nbsp;数据段寄存器 <br>SS:&nbsp;堆栈段寄存器 <br>ES:&nbsp;附加段寄存器 <br>SI:&nbsp;源变址寄存器&nbsp; <br>DI:&nbsp;目的变址寄存器 <br>BP:&nbsp;基址指针寄存器 <br>SP:&nbsp;堆栈指针寄存器 <br>IP:&nbsp;指令指针寄存器 <br>FR:&nbsp;标志指针寄存器</cc><br><br><br>1数据寄存器（或称通用寄存器）&nbsp; <br><br>数据寄存器包括AX，BX，CX，DX四个通用寄存器，他们可以以字16位的形式使用，也可以以字节8位的形式使用。&nbsp; <br><br>以字形式使用时四个通用寄存器称为AX，BX，CX，DX，以字节形式使用，高八位通用寄存器称AH，BH，CH，DH。低八位称AL，BL，CL，DL。&nbsp; <br><br>这四个都是通用寄存器，又可用于专用的目的。&nbsp; <br><br>AX做累加器用（ACCUMALATOR0&nbsp; <br><br>BX在计算存储器地址时，经常用做基地址寄存器，所以又称基址寄存器。（BASE）&nbsp; <br><br>CX（COUNT）可用做通用寄存器。此外，在循环（&nbsp;LOOP）和串处理指令中用做隐含的计数器。&nbsp; <br><br>DX（DATA）在做双字长的运算时，把DX和AX组合在一起存放I/O端口地址。&nbsp; <br><br>2，指针及变址寄存器&nbsp; <br><br>他们包括SP，BP，SI，DI四个16位寄存器。他们可以象数据寄存器一样在运算过程中存放操作数，单他们只能以字16位为单位使用。&nbsp; <br><br>SP（STACK&nbsp;POINTER）堆栈指针寄存器；&nbsp; <br><br>用来指示堆栈的栈顶的偏移地址，与SS堆栈段寄存器形成栈顶存储单元的物理地址。&nbsp; <br><br>BP（BASE&nbsp;POINTER）基址指针寄存器。&nbsp; <br><br>用来指示堆栈中某个数据区的偏移地址-----基地址。&nbsp; <br><br>SI（SOURCE&nbsp;INDEX）源变址寄存器；&nbsp; <br><br>DI（DESTINATION&nbsp;INDEX）目的变址寄存器；&nbsp; <br><br>3，段寄存器&nbsp; <br><br>包括CS，DS，SS，ES四个16位段寄存器&nbsp; <br><br>CS（CODE&nbsp;SEGMENT）代码段寄存器&nbsp; <br><br>SS（STACK&nbsp;SEGMENT）堆栈段寄存器&nbsp; <br><br>DS（DATA&nbsp;SEGMENT）数据段寄存器&nbsp; <br><br>ES（EXTRA&nbsp;SEGMENT）附加段寄存器&nbsp; <br><br>8086/8088采用存储空间的分段技术来解决寻址1M字节的存储空间。这些段寄存器的内容和有效的地址偏移量（称偏移地址）一起可确定内存的存储单元的物理地址。CS控制程序区DS和ES控制数据区，SS控制堆栈区。&nbsp; <br><br>4控制寄存器&nbsp; <br><br>分为两个16位的寄存器IP和PSW。&nbsp; <br><br>IP（INSTRUCTION&nbsp;POINTER）指令指针寄存器；他用来存放代码段中的偏移地址。程序运行中始终指向下一条指令的首地址。计算机就是用IP寄存器来控制指令序列的执行流程的&nbsp; <br><br>PSW（PROGRAM&nbsp;STATUS&nbsp;WORD）程序状态字寄存器或称标志寄存器；&nbsp; <br><br>由状态码标志和控制标志构成，&nbsp; <br><br>OF溢出标志；运算结果超出机器能表示的数值范围称溢出OF=1，否则OF=0；&nbsp; <br><br>SF符号标志；运算结果的符号为负时置1否则置0&nbsp; <br><br>ZF；零标志&nbsp; <br><br>CF进位标志&nbsp; <br><br>AF辅助进位标志&nbsp; <br><br>PF奇偶标志&nbsp; <br><br>DF方向标志&nbsp; <br><br>DF=1每次操作后使SI和DI减量，使串处理指令向低地址方向进行&nbsp; <br><br>IF中断标志&nbsp; <br><br>TF跟踪标志&nbsp; <br><br>控制标志是由系统程序或用户程序根据需要用指令来设置的。</cc><br>
<img src ="http://www.cppblog.com/luqingfei/aggbug/120881.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-07-20 15:46 <a href="http://www.cppblog.com/luqingfei/archive/2010/07/20/120881.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编语言--寄存器（内存访问）</title><link>http://www.cppblog.com/luqingfei/archive/2010/07/18/120697.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Sun, 18 Jul 2010 06:51:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/07/18/120697.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/120697.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/07/18/120697.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/120697.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/120697.html</trackback:ping><description><![CDATA[知识点：内存中字的存储、DS和[address]、字的传送、mov,add,sub指令、数据段、栈、CPU提供的栈机制、栈顶超界的问题、push,pop指令、栈段。<br><br><strong>内存中字的存储<br></strong>CPU中，用16位寄存器来存储一个字。高8位存放高位字节，低8位存放低位字节。<br>在内存中存储时，由于内存单元是字节单元（一个单元存放一个字节），则一个字要用两个地址连接的内存单元来存放，这个字的低位字节放在低地址单元中，高位字节存放在高地址单元中。<br><br>字单元：即存放一个字型数据（16位）的内存单元，由两个地址连续的内存单元组成。高地址内存单元中存放字型数据的高位字节，低地址内存单元中存放字型数据的低位字节。<br><br><span style="BACKGROUND-COLOR: yellow">任何两个地址连续的内存单元，N号单元和N+1号单元，可以将它们看成两个内存单元，也可看成一个地址为N的字单元中的高位字节单元和低位字节单元。</span><br><br><br><strong>DS和[address]</strong><br>CPU要读写一个内存单元的时候，必须先给出这个内存单元的地址，在8086CPU中，内存地址由段地址和偏移地址组成。<br><span style="BACKGROUND-COLOR: yellow">8086CPU中有一个DS寄存器，通常用来存放要访问数据的段地址。</span><br><br>mov指令，可完成三种传送：<br>（1）将数据直接送入寄存器；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov 寄存器名，数据<br>（2）将一个寄存器中的内容送入另一个寄存器；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov 寄存器名，寄存器名<br>（3）将一个内存单元中的内容送入一个寄存器中。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov 寄存器，[内存单元的偏移地址]<br><br>&#8220;[...]&#8221;表示一个内存单元的偏移地址，我们知道，只有偏移地址是不能定位一个内存单元的，那么内存单元的段地址是多少呢？<br>指令执行时，8086CPU自动取ds中的数据为内存单元的段地址。<br><br>所以，我们需要根据情况，改变ds中的数据。<br>比如 mov ds, 1000H<br>但是<span style="BACKGROUND-COLOR: yellow">，8086CPU不支持将数据直接送入段寄存器的操作，ds是一个段寄存器，所以mov ds, 1000H这条指令是非法的</span>。<br><br>那么如何将1000H送入ds呢？<span style="BACKGROUND-COLOR: yellow">只好用一个寄存器来进行中转</span>，即先将1000H送入一个一般的寄存器，如bx，再将bx中的内容送入ds。<br><br><br>怎样将数据从寄存器送入内存单元？<br>从内存单元到寄存器的格式是：mov 寄存器名，内存单元地址<br>从寄存器到内存单元则是：mov 内存单元地址，寄存器名。<br><br>将al中的数据送入内存单元10000H。<br>10000H可表示为1000:0，用ds存放段地址1000H，偏移地址是0，则：mov [0], al可完成从al到10000H的数据传送。<br>mov bx, 1000H<br>mov ds, bx<br>mov [0], al<br><br><br><strong>字的传送<br></strong>mov指令在寄存器和内存之间进行字节型数据的传送。<br>因为8086CPU是16位结构，有16根数据线，所以，可以一次性传送16位的数据，也就是说可以一次性传送一个字。<br><br>我们只要在mov指令中给出16位的寄存器就可以进行16位数据的传送了。<br><br><br><strong>mov, add, sub指令</strong><br>mov指令目前可以有以下几种形式：<br>mov 寄存器，数据&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov ax,8<br>mov 寄存器，寄存器&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov ax,bx<br>mov 寄存器，内存单元&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov ax,[0]<br>mov 内存单元，寄存器&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov [0],ax<br>mov 段寄存器，寄存器&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mov ds,ax<br><br>add和sub指令同mov一样，都有两个操作对象，它们也可以有上面的几种形式。<br>这些形式中有些，两个操作数可以相互交换操作，这些需要用debug中的a命令和t命令多实践实践。<br><br><br><strong>数据段</strong><br>前面讲过，对于8086CPU，在编程时，我们可以根据需要，将一组内在单元定义为一个段。<br>我们可以将一组长度为N（N&#8804;64K）、地址连续、起始地址为16的倍数的内存单元当作专门存储数据的内存空间，从而定义了一个数据段。<br>比如我们用：123B0H～123BAH这段内存空间来存放数据，我们就可以认为，123B0H～123BAH这段内存是一个数据段，它的段地址为123B，长度为10字节。<br><br>如何访问数据段中的数据呢？<br>将一段内存当作数据段，是我们在编程时的一种安排，我们可以在具体操作的时候，用ds存放数据段的段地址，再根据需要，用相关指令访问数据段中的具体单元。<br><br>比如，我们将123B0H～123BAH的内存单元定义为数据段。<br>我们现在要累加这个数据段中的前3个单元中的数据。<br>mov ax, 123BH<br>mov ds, ax&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;将123BH送入ds中，作为数据段的段地址。<br>mov al, 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;用al存放累加结果，先把它清零。<br>add al, [0]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;将数据段第一个单元（偏移地址为0）中的数值加到al中。<br>add al, [1]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;将数据段第一个单元（偏移地址为1）中的数值加到al中。<br>add al, [2]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;将数据段第一个单元（偏移地址为2）中的数值加到al中。<br><br>累加数据段中的前3个字型数据。<br>mov ax, 123BH<br>mov ds, ax&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;将123BH送入ds中，作为数据段的段地址。<br>mov ax, 0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;用ax存放累加结果，先把它清零。<br>add al, [0]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;将数据段第一个字（偏移地址为0）加到ax中。<br>add al, [2]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;将数据段第一个字（偏移地址为2）加到ax中。<br>add al, [4]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;将数据段第一个字（偏移地址为4）加到ax中。<br><br><br><strong>小结</strong><br>（1）字在内存中存储时，要用两个地址连续的内存单元来存放，字的低位字节存放在低地址单元中，高位字节存放在高地址单元中。<br>（2）用mov指令要访问内存单元，可以在mov指令中只给出单元的偏移地址。此时，段地址默认在DS寄存器中。<br>（3）[address]表示一个偏移地址为address的内存单元。<br>（4）在内存和寄存器之间传送字型数据时，高地址单元和高8位寄存器、低地址单元和低8位寄存器相对应。<br>（5）mov, add, sub是具有两个操作对象的指令，jmp是具有一个操作对象的指令。<br>（6）可以根据自己的推测，在Debug中实验指令的新格式。<br><br><br><strong>栈</strong><br>在这里，我们对栈的研究仅限于这个角度：栈是一种具有特殊的访问方式的存储空间。<br>它的特殊性就在于，最后进入这个空间的数据，最先出去。<br><br>栈有两个基本的操作：入栈和出栈。<br>入栈就是将一个新的元素到栈顶，出栈就是从栈顶取出一个元素。<br>栈顶的元素总是最后入栈，需要出栈和时，又最先被从栈中取出。<br>栈的这种操作规则被称为：LIFO（Last In First Out, 后进先出）。<br><br><br>CPU提供的栈机制<br>现今的CPU中都有栈的设计，8086CPU也不例外。<br>8086CPU提供相关的指令来以栈的方式访问内存空间。<br>这意味着，我们在基于8086CPU编程的时候，可以将一段内存当作栈来使用。<br><br>8086CPU提供入栈和出栈指令，最基本的两个是PUSH（入栈）和POP（出栈）。<br>比如：push ax 表示将寄存器ax中的数据据送入栈中，pop ax表示从栈顶取出数据送入ax。<br>8086CPU的入栈和出栈操作都是以字为单位进行的。<br><br>注意，字型数据用两个内存存储单元存放，高地址单元放高8位，低地址单元放低8位。<br><br>8086CPU中，有两个寄存器，段寄存器SS和寄存器SP，栈顶的段地址存放在SS中，偏移地址存放在SP中。<br><br>任意时刻，SS:SP指向栈顶元素。push指令和pop指令执行时，CPU从SS和SP中得到栈顶的地址。<br><br>push ax的执行：<br>1）SP=SP-2, SS:SP指向当前栈顶前面的单元，以当前栈顶前面的单元为新的栈顶；<br>2）将ax中的内容送入SS:SP指向的内存单元处，SS:SP此时指向新栈顶。<br><br>pop ax的执行过程和push ax刚好相反：<br>1）将SS:SP指向的内存单元处的数据送入ax中；<br>2）SP=SP+2,SS:SP指向当前栈顶下面的单元，以当前栈顶下面的单元为新的栈顶。<br><br><strong>栈顶超界的问题</strong><br>8086CPU用SS和SP指示栈顶的地址，并提供push和pop指令实现入栈和出栈。<br><br>但是，SS和SP只是记录了栈顶的地址，依靠SS和SP可以保证在入栈和出栈时找到栈顶，可是，如何能够保证在入栈、出栈时，栈顶不会超出栈空间？<br><br>当栈满的时候再使用push指令入栈，或栈空的时候再使用pop指令出栈，都将发生栈顶超界问题。<br><br>栈顶超界是危险的，因为我们既然将一段空间安排为栈，那么在栈空间之外的空间里很可能存放了具有其他用途的数据、代码等，这些数据、代码可能是我们自己程序的，也可能是别的程序中的（毕竟一个计算机系统中并不是只有我们自己的程序在运行）。但是由于我们在入栈出栈时的不小心，而将这些数据、代码意外地改写，将会引发一连串的错误。<br><br>8086CPU不保证我们对栈的操作不会超界。<br>也就是说，8086CPU只知道栈顶在何处（由SS:SP指示），而不知道读者安排的栈空间有多大。<br><br>我们在编程的时候要自己操心栈顶超界的问题，要根据可能用到的最大栈空间，来安排栈的大小，防止入栈的数据太多而导致的超界；执行出栈操作的时候也要注意，以防栈空的时候继续出栈而导致的超界。<br><br><br><strong>push、pop指令<br></strong>push和pop指令是可以在寄存器和内存（栈空间当然也是内存空间的一部分，它只是一段可以以一种特殊的方式进行访问的内存空间。）之间传送数据的。<br><br>push和pop指令的格式可以是如下形式：<br>push 寄存器&nbsp;&nbsp;&nbsp; ；将一个寄存器中的数据入栈<br>pop 寄存器&nbsp;&nbsp;&nbsp;&nbsp; ；出栈，用一个寄存器接收出栈的数据<br><br>push 段寄存器&nbsp; ；将一个段寄存器中的数据入栈<br>pop 段寄存器 ；出栈，用一个段寄存器接收出栈的数据<br><br>push和pop也可以在内存单元和内存单元之间传送数据<br>push 内存单元 ；将一个内存单元处的字入栈（注意，栈操作都是以字为单位）<br>pop 内存单元&nbsp;&nbsp; ；出栈，用一个内存单元接收出栈的数据<br>指令执行时，CPU要知道内存单元的地址，可以在push、pop指令只给出内存单元的偏移地址，段地址在执行指令时，CPU从ds中取得。<br><br><br><strong>栈的综述<br></strong><br>1）8086CPU提供了栈操作机制，方案如下：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在SS、SP中存放栈顶的段地址和偏移地址；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 提供入栈和出栈指令，它们根据SS:SP指示的地址，按照栈的方式访问内存单元。<br>2）push指令的执行步骤：a、SP=SP-2；b、向SS:SP指向的字单元中送入数据。<br>3）pop指令的执行步骤：a、从SS:SP指向的字单元中读取数据；b、SP=SP+2。<br>4）任意时刻，SS:SP指向栈顶元素。<br>5）8086CPU只记录栈顶，栈空间的大小我们要自己管理。<br>6）用栈来暂存以后需要恢复的寄存器的内容时，寄存器出栈的顺序和入栈的顺序相反。<br>7）push、pop实质上是一种内存传送指令，注意它们的灵活应用。<br><br>栈是一种非常重要的机制，一定要深入理解，灵活掌握。<br><br><br><strong>栈段</strong><br>前面讲过，对于8086CPU，在编程时，我们可以根据需要，将一组内存单元定义为一个段。我们可以将长度为N（N&lt;=64K）的一组地址连接、起始地址为16的倍数的内存单元，当作栈空间来用，从而定义了一个栈段。<br>比如，我们将10010H~1001FH这段长度为16字节的内存空间当作栈来用，以栈的方式进行访问。这段空间就可以称为一个栈段，段地址为1000H，大小为16字节。<br><br>将一段内存当作栈段，仅仅是我们在编程时的一种安排，CPU并不会由于这种安排，就在执行push、pop等栈操作指令时就自动地将我们定义的栈段当作栈空间来访问。<br>如何使得如push、pop等栈操作指令访问我们定义的栈段呢？就是要将SS:SP指向我们定义的栈段。<br><br>任意时刻，SS:SP指向栈顶元素，当栈为空的时候，栈中没有元素，也就不存在栈顶元素，所以SS:SP只能指向栈的最底部单元下面的单元，该单元的地址为栈最底部的字单元的地址+2。<br>如果将10000H～1FFFFH这段空间当作栈段，栈最底部字单元的地址为1000：FFFE，所以栈空时，SP=0000H。<br><br>一个栈段最大可以设为多少？<br>从栈操作指令所完成的功能的角度上来看，push、pop等指令在执行的时候只修改SP，所以栈顶的变化范围是0～FFFFH，人栈空时候的SP=0，一直压栈，直到栈满时SP=0；如果再次压栈，栈顶将循环，覆盖了原来栈中的内容。所以一个栈段的容量最大为64KB。<br><br><br><strong>段的综述</strong><br>我们可以将一段内存定义为一个段，用一个段地址指示段，用偏移地址访问段内的单元。这完全是我们自己的安排。<br>我们可以用一个段丰放数据，将它定义为&#8220;数据段&#8221;；<br>我们可以用一个段存放代码，将它定义为&#8220;代码段&#8221;；<br>我们可以用一个段当作栈，将它定义为&#8220;栈段&#8221;；<br>我们可以这样安排，但若要让CPU按照我们的安排来访问这些段，就要：<br>对于数据段，将它的段地址放在DS中，用mov、add、sub等访问内存单元的指令时，CPU就将我们定义的数据段中的内容当作数据来访问；<br>对于代码段，将它的段地址放在CS中，将段中第一条指令的偏移地址放在IP中，这样CPU就将执行我们定义的代码段中的指令；<br>对于栈段，将它的段地址放在SS中，将栈顶单元的偏移地址放在SP中，这样CPU在需要进行栈操作的时候，比如执行push、pop指令等，就将我们定义的栈段当作栈空间来用。<br><br>可见，不管我们如何安排，CPU将的某段内容当作代码，是为因为CS:IP指向了那里；CPU将某段内存当作栈，是为因SS:SP指向了那里。<br>我们一定要清楚，什么是我们的安排，以及如何让CPU按我们的安排行事。要非常地清楚CPU的工作机理，才能在控制CPU来按照我们的安排运行的时候做到游刃有余。<br>比如我们将10000H～1001FH安排为代码段，并在这里存储如下代码：<br>mov ax,1000H<br>mov ss,ax<br>mov sp,0020H&nbsp;&nbsp;&nbsp;&nbsp; ;初始化栈顶<br>mov ax,cs<br>mov ds,ax&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;设置数据段段地址<br>mov ax,[0]<br>mov ax,[2]<br>mov bx,[4]<br>mov bx,[6]<br>push ax<br>push bx<br>pop bx<br>pop ax<br><br>设置CS=1000H，IP=0，这段代码将得到执行，可以看到，在这段代码中，我们双将10000H～1001FH安排为栈段和数据段，10000H～1001FH这段内存，即是代码段，又是栈段和数据段。<br><br>一段内存，可骒既是代码的存储空间，又是数据的存储空间，还可以是栈空间，也可以什么也不是。<br>关键在于CPU中寄存器的设置，即：CS、IP、SS、SP、DS的指向。<br><br><br>
<img src ="http://www.cppblog.com/luqingfei/aggbug/120697.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-07-18 14:51 <a href="http://www.cppblog.com/luqingfei/archive/2010/07/18/120697.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编语言--寄存器（CPU工作原理）</title><link>http://www.cppblog.com/luqingfei/archive/2010/07/18/120692.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Sun, 18 Jul 2010 06:11:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/07/18/120692.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/120692.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/07/18/120692.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/120692.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/120692.html</trackback:ping><description><![CDATA[<p>知识点：通用寄存器、字在寄存器中的存储、几条汇编指令、物理地址、16位结构的CPU、8086CPU给出物理地址的方法、&#8220;段地址&#215;16 + 偏移地址 = 物理地址&#8221;的本质含义、段的概念、段寄存器、CS和IP、修改CS，IP的指令、代码段、实验1：查看CPU和内存，用机器指令和汇编指令编程。<br><br></p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/120692.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-07-18 14:11 <a href="http://www.cppblog.com/luqingfei/archive/2010/07/18/120692.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编语言--基础知识</title><link>http://www.cppblog.com/luqingfei/archive/2010/07/11/120085.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Sun, 11 Jul 2010 06:50:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/07/11/120085.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/120085.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/07/11/120085.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/120085.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/120085.html</trackback:ping><description><![CDATA[<p>知识点：机器语言、汇编语言的产生、汇编语言的组成、存储器、指令和数据、存储单元、CPU对存储器的读写、地址总线、数据总线、控制总线、内存地址空间（概述）、主板、接口卡、各类存储器芯片、内存地址空间。<br><br><br>汇编语言是直接在硬件之上工作的编程语言，首先要了解硬件系统的结构，才能有效地应用汇编语言对其编程。<br><br><strong>机器语言<br></strong>说到汇编语言的产生，首先要讲一下机器语言。<span style="BACKGROUND-COLOR: yellow">机器语言是机器指令的集合</span>。机器指令展开来讲就是一台机器可以正确执行的命令。<br><span style="BACKGROUND-COLOR: yellow">电子计算机的机器指令是一列二进制数字</span>。<span style="BACKGROUND-COLOR: yellow">计算机将之转变为一列高低电平，以使计算机的电子器件受到驱动，进行运算。</span><br><br>上面所说的计算机指的是可以执行机器指令，进行运算的机器。这是早期计算机的概念。现在，在常用的PC机中，有一个芯片来完成上面所说的计算机的功能，这个芯片就是我们常说的CPU（Central Processing Unit，中央处理器单元），CPU是一种微处理器。<br><br>以后我们提到的计算机是指由CPU和其他受CPU直接或间接控制的芯片、器件、设备组成的计算机系统，比如我们最常见的PC机。<br><br>每一种微处理器，由于硬件设计和内部结构和不同，就需要用不同的电平脉冲来控制，使它工作。所以每一种微处理器都有自己的机器指令集，也就是机器语言。<br><br>早期的程序设计均使用机器语言。程序员们将用0、1数字编成的程序代码打在纸带或卡片上，1打孔，0不打孔，再将程序通过纸带机或卡片机输入计算机，进行运算。<br><br>应用8086CPU完成计算 s = 768 + 12288 - 1280，机器码如下：<br><strike>1011000000000000000000000000011<br>0000010100000000000000000110000<br>0010110100000000000000000000101</strike><br><br>要书写和阅读机器码程序不是一件简单的工作，要记住所有抽象的二进制码。上面只是一个非常简单的小程序，就暴露了机器码的晦涩难懂和不易查错。<br><br><br><strong>汇编语言的产生</strong><br>早期的程序员很快就发现了使用机器语言带来的麻烦，它是如此难于辨别和记忆，给整个产业的发展带来了障碍。于是汇编语言产生了。<br><br>汇编语言的主体是汇编指令。<br>汇编指令和机器指令的差别在于<span style="BACKGROUND-COLOR: yellow">指令的表示方法</span>上。<br><span style="BACKGROUND-COLOR: yellow">汇编指令是机器指令便于记忆的书写格式。<br></span><br>例如：机器指令 1000100111011000表示把寄存器BX的内容送到AX中。汇编指令则写成mov ax, bx，这样的写法与人类语言接近，便于阅读和记忆。<br><br>操作：寄存器BX的内容送到AX中<br>机器指令：1000100111011000<br>汇编指令：mov ax, bx<br><br>（寄存器，简单地讲是CPU中可以存储数据的器件，一个CPU中有多个寄存器。AX是其中一个寄存器的代号，BX是另一个寄存器的代号。）<br><br>此后，程序员们就用汇编指令编写源程序。可是，计算机能读懂的只有机器指令，那么如何让计算机执行程序员用汇编指令编写的程序呢？这时，就需要有一个能够<span style="BACKGROUND-COLOR: yellow">将汇编指令转换成机器指令的翻译程序，这样的程序被称为编译器</span>。程序员用<span style="BACKGROUND-COLOR: yellow">汇编语言写出源程序</span>，再用<span style="BACKGROUND-COLOR: yellow">汇编编译器</span>将其<span style="BACKGROUND-COLOR: yellow">编译</span>为<span style="BACKGROUND-COLOR: yellow">机器码</span>。由计算机最终执行。<br><br><br>用汇编语言编写程序的工作过程：<br>（程序员）汇编指令&nbsp;&nbsp; --&gt;&nbsp; 编译器&nbsp;&nbsp; --&gt;&nbsp; 机器码&nbsp; --&gt;&nbsp; 计算机<br><br><br><strong>汇编语言的组成<br></strong>汇编语言发展至今，由以下3类指令组成。<br>汇编指令：机器码的助记符，有对应的机器码。<br>伪指令：没有对应的机器码，由编译器执行，计算机并不执行。<br>其他符号：如：+、-、*、/等，由编译器识别，没有对应的机器码。<br><br>汇编语言的核心是汇编指令，它决定了汇编语言的特性。<br><br><br><strong>存储器</strong><br>CPU是计算机的核心部件，它控制整个计算机的运作并进行运算。<br><span style="BACKGROUND-COLOR: yellow">要想让一个CPU工作，就必须向它提供指令和数据。<br></span>指令和数据在存储器中存放，也就是平时所说的内存。<br><br>在一台PC机中内在的作用仅次于CPU。离开了内存，性能再好的CPU也无法工作。<br><span style="BACKGROUND-COLOR: yellow">这就像再聪明的大脑，没有了记忆也无法进行思考。</span><br><br>磁盘不同于内存，磁盘上的数据或程序如果不读到内存中，就无法被CPU使用。<br><br>要灵活地利用汇编语言编程，首先要了解CPU是如何从内存中读取信息，以及向内存中写入信息的。<br><br><br><strong>指令和数据</strong><br>指令和数据是应用上的概念。在内存或磁盘上，指令和数据没有任何区别，都是二进制信息。<br>CPU在工作的时候把有的信息看作指令，有的信息看作数据，为同样的信息赋予了不同的意义。<br>就像围棋的棋子，在棋盒里的时候没有任何区别，在对弈的时候就有了不同的意义。<br><br>例如，内存中的二进制信息 1000100111011000，计算机可以把它看作大小为89D8H的数据来处理，也可以将基看作指令mov ax, bx来执行。<br><br>1000100111011000&nbsp;&nbsp;&nbsp;&nbsp; --&gt;&nbsp;&nbsp;&nbsp; 89D8H（数据）<br>1000100111011000&nbsp;&nbsp;&nbsp;&nbsp; --&gt;&nbsp;&nbsp;&nbsp; mov ax, bx（指令）<br><br><br><strong>存储单元<br></strong>存储器被划分成若干个存储单元，每个存储单元从0开始顺序编号，例如一个存储器有128个存储单元，编号从0~127。<br><br>电子计算机的最小信息单位是bit（音译为比特），也就是一个二进制位。<br>8个bit组成一个Byte，也就是通常讲的一个字节。<br>微型机存储器的存储单元可以存储一个字节，即8个二进制位。<br>一个存储器有128个存储单元，它可以存储128个字节。<br><br>微机存储器的容量是以字节为最小单位来计算的。<br>对于拥有128个存储单元的存储器，我们可以说，它的容量是128字节。<br><br>对于大容量的存储器一般还用以下单位计量容量（以下B来代表Byte）：<br>1KB = 1024B<br>1MB = 1024KB<br>1GB = 1024 MB<br>1TB = 1024 GB<br><br>磁盘的容量单位同内存的一样，以上单位是微机中常用的计量单位。<br><br><br><br><strong>CPU对存储器的读写</strong><br>存储器被划分成多个存储单元，存储单元从零开始顺序编号。这些编号可以看作存储单元在存储器中的地址。就像一条街，每个房子都有门牌号码。<br><br>CPU要从内在中读数据，首先要指定存储单元的地址。<br>也就是说它要先确定读取哪一个存储单元的数据。就像在一条街上找人，先要确定他住哪个房子里。<br><br>另外，在一台微机中，不只有存储器这一种器件。CPU在读写数据时还要指明，它要对哪一个器件进行操作，进行哪种操作，是从中读出数据，还是向里面写入数据。<br><br>可见，CPU要想进行数据的读写，必须和外部器件（标准的说法是芯片）进行3类信息的交互：<br>*存储单元的地址（地址信息）<br>*器件的选择，读或写的命令（控制信息）<br>*读或写的数据（数据信息）<br><br>那么CPU是通过什么将地址、数据和控制信息传到存储器芯片中的呢？<br>电子计算机能处理、传输的信息都是电信号，电信号当然要用导线传送。在计算机中专门有连接CPU和其他芯片的导线，通常称为<span style="BACKGROUND-COLOR: yellow">总线</span>。<br>总线从物理上来讲，就是一根根导线的集合。<br>根据传送信息的不同，总线从逻辑上又分为3类，即<span style="BACKGROUND-COLOR: yellow">地址总线、控制总线、和数据总线</span>。<br><br>CPU从3号单元中读取数据的过程如下：<br><br>
<table style="WIDTH: 320px; BORDER-COLLAPSE: collapse" border=1 cellSpacing=0 cellPadding=3>
    <tbody>
        <tr>
            <td>
            <p align=center>CPU</p>
            </td>
            <td></td>
            <td>内存</td>
        </tr>
        <tr>
            <td></td>
            <td>
            <p align=center>地址线</p>
            </td>
            <td>12&nbsp;&nbsp;&nbsp;&nbsp; [0]</td>
        </tr>
        <tr>
            <td></td>
            <td align=middle>3-----&gt; </td>
            <td>3B&nbsp;&nbsp;&nbsp;&nbsp;[1]</td>
        </tr>
        <tr>
            <td></td>
            <td></td>
            <td>9C&nbsp;&nbsp;&nbsp; [2]</td>
        </tr>
        <tr>
            <td></td>
            <td align=middle>数据线 </td>
            <td>08&nbsp;&nbsp;&nbsp;&nbsp; [3]</td>
        </tr>
        <tr>
            <td></td>
            <td align=middle>8&lt;----- </td>
            <td>31&nbsp;&nbsp;&nbsp; &nbsp;[4]</td>
        </tr>
        <tr>
            <td></td>
            <td></td>
            <td>23&nbsp;&nbsp;&nbsp;&nbsp; [5]</td>
        </tr>
        <tr>
            <td></td>
            <td align=middle>控制线 </td>
            <td>15&nbsp;&nbsp;&nbsp;&nbsp; [6]</td>
        </tr>
        <tr>
            <td></td>
            <td align=middle>内存读写命令---&gt; </td>
            <td>13&nbsp;&nbsp;&nbsp; &nbsp;[7]</td>
        </tr>
        <tr>
            <td></td>
            <td></td>
            <td>18&nbsp;&nbsp;&nbsp;&nbsp; [8]</td>
        </tr>
    </tbody>
</table>
<br>cpu从内存中读取数据的过程。<br><br>（1）CPU通过地址线将地址信息3发出。<br>（2）CPU通过控制线发出内在读命令，选中存储器芯片，并通知它，将要从中读取数据。<br>（3）存储器将3号单元中的数据08通过数据线送入CPU。<br><br>写操作与读操作的步骤相似。向3号单元写入数据26：<br>（1）CPU通过地址线将地址信息3发出。<br>（2）CPU通过控制线发出内存写命令，选中存储器芯片，并通知它，要向其中写入数据。<br>（3）CPU通过数据线将数据26送入内存的3号单元中。<br><br>从上面的我们知道CPU是如何进行数据读写的。可是，我们如何命令计算机进行数据的读写呢？<br>要让一个计算机或微处理器工作，应向它输入能够驱动它进行工作的电平信息（机器码）。<br><br>对于8086CPU，下面的机器码能够完成从3号单元读数据：<br>机器码：101000000000001100000000<br>含义：从3号单元读取数据送入寄存器AX<br><br>CPU接收这条机器码后将完成上面所述的读写工作。<br><br>机器码难于记忆，用汇编指令来表示，情况如下：<br>机器码：101000000000001100000000<br>对应的汇编指令：MOV AX, [3]<br>含义：传送3号单元的内容到AX<br><br><br><strong>地址总线</strong><br>CPU是通过地址总线来指定存储器单元的。<br>地址总线上能传送多少个不同的信息，CPU就可以对多少个存储单元进行寻址。<br><br>现假设，一个CPU有10根地址线，让我们来看一下它的寻址情况。<br>在电子计算机中，一根导线可以传送的稳定状态只有两种，高电平或低电平。<br>用二进制表示就是1或0，10根导线可以传送10位二进制数据。<br>而10位二进制数可以表示多个不同的数据呢？<br>2的10次方个，最小数为0，最大数为1023。<br><br>一个CPU有N根地址线，则可以说这个CPU的地址总线的宽度为N。<br>这样的CPU最多可以寻找2的N次方个内存单元。<br><br><br><br><strong>数据总线<br></strong>CPU与内存或其他器件之间的<span style="BACKGROUND-COLOR: yellow">数据传送</span>是通过数据总线来进行的。<br>数据总线的宽度决定了CPU和外界的数据传送速度。<br>8根数据总线一次可传送一个8位二进制数据（即一个字节）。<br>16根数据总线一次可传送2个字节。<br><br><br><strong>控制总线</strong><br>CPU对外部器件的控制是通过总线来进行的。在这里控制总线是个总称，控制总线是一些不同控制线的集合。<br>有多少根控制总线，就意味着这个CPU提供了对外部器件的多少种控制。<br>所以，控制总线的宽度决定了CPU对外部器件的控制能力。<br><br>内存读写命令是由几根控制线综合发出的，其中有一根名为读信号输出控制线负责由CPU向外传送读信号，CPU向该控制线上输出低电平表示将要读取数据；有一根名为写信号输出的控制线则负责传送写信号。<br><br><br><strong>小结<br></strong>1）汇编指令是机器指令的助记符，同机器指令一一对应。<br>2）每一种CPU都有自己的汇编指令集。<br>3）CPU可以直接使用的信息在存储器中存放。<br>4）在存储器中指令和数据没有任何区别，都是二进制信息。<br>5）存储单元从零开始顺序编号。<br>6）一个存储单元可以存储8个bit（用作单位写成"b"），即8位二进制数。<br>7）1B=8b&nbsp;&nbsp;&nbsp;&nbsp; 1KB=1024B&nbsp;&nbsp;&nbsp;&nbsp; 1MB=1024KB&nbsp;&nbsp;&nbsp;&nbsp; 1GB=1024MB<br>8）每一个CPU芯片都有许多管脚，这些管脚和总线相连。也可以说，这些管脚引出总线。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一个CPU可以引出三种总线的宽度标志了这个CPU的不同方面的性能：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;地址总线的宽度决定了CPU的寻址能力；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;数据总线的宽度决定了CPU与其他器件进行数据传送时的一次数据传送数；<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;控制总线的宽度决定了CPU对系统中其他器件的控制能力。<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>习题：<br>（1）1个CPU的寻址能力为8KB，那么它的地址总线的宽度为________。<br>（2）1KB的存储器有________个存储单元？存储单元的编号从__________到__________。<br>（3）1KB的存储器可以存储_________个bit，_________个byte。<br>（4）1GB，1MB，1KB分别是_____________________________byte。<br>（5）8080、8088、80286、80386的地址总线分别为16根、20根、24根、32根，则它们的寻址能力分别为：__________KB、________MB、__________MB、______________GB。<br>（6）8080、8088、8086、80286、80386的数据总线宽度分别为8根、8根、16根、16根、32根。则它们一次可以传送的数据为：_______B、________B、________B、__________B、_______B。<br>（7）从内存中读取1024字节的数据，8086至少要读_______次，80386至少要读__________次。<br>（8）在存储器中，数据和程序以________形式存放。<br><br><br>答案：<br><span style="COLOR: #ffffff">（1）13<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 解析：微型机的存储单元可以存储一个字节，即8个二进制位。8KB即8K字节，即2的13次方个存储单元。一个CPU有N根地址线，则可以说这个CPU的地址总线的宽度为N,这样的CPU最多可以寻找2的N次方个存储单元。<br>（2）1024，0，1023<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 解析：1KB，即1024字节，一个存储单元为一个字节。存储单元从零开始顺序编号。<br>（3）8192，1024<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 解析：1B=8b&nbsp;&nbsp;&nbsp;&nbsp; 1KB=1024B<br>（4）2^30,&nbsp;&nbsp; 2^20, 2^10<br>（5）64， 1，&nbsp;16， 4<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 解析：一个CPU有N根地址线，表示这个CPU地址总线宽度为N，可以寻址2的N次方个存储单元。<br>（6）1，1，2，2，4<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 解析：一根数据线，只能传送一位二进制数（0或1，低电平或高电平）<br>（7）512， 256<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 解析：8086的数据总线宽度为16，一次可以传送2个字节，80386的数据总线宽度为32，一次可以传送4个字节。<br>（8）二进制（0或1）<br></span><br><br><br><strong>内存地址空间（概述）</strong><br>什么是内存地址空间呢？<br>举例来讲，一个CPU的地址线宽度为10，那么可以寻址1024个内存单元（存储单元，一个存储单元在微型机中表示一个字节），这1024个可寻到的内存单元就构成了这个CPU的内存地址空间。<br><br><br><br><strong>主板<br></strong>在每一台PC机中，都有一个主板，主板上有核心器件和一些主要器件，这些器件通过总线（地址总线、数据总线、控制总线）相连。<br>这些器件有：CPU、存储器、外围芯片组、扩展插槽等。<br>扩展插槽上一般插有RAM内存条和各类接口卡。<br><br><strong>接口卡<br></strong>计算机系统中，所有可用程序控制其工作的设备，必须受到CPU的控制。<br>CPU对外部设备都不能直接控制，如显示器、音箱、打印机等。<br>直接控制这些设备进行工作的是插在扩展插槽上的接口卡。<br>扩展插槽通过总线和CPU相连，所以接口卡也通过总线同CPU相连。<br>CPU可以直接控制这些接口卡，从而实现CPU对外设的间接控制。<br>简单地讲，就是<span style="BACKGROUND-COLOR: yellow">CPU通过总线向接口卡发送命令，接口卡根据CPU的命令控制外设进行工作</span>。<br><br><br><strong>各类存储器芯片<br></strong>一台PC机中，装有多个存储器芯片，这些存储器芯片从物理连接上看是独立的、不同的器件。<br>从读写属性上看分为两类：随机存储器（RAM）和只读存储器（ROM）。<br>随机存储器可读可写，但必须带电存储，关机后存储的内容丢失；<br>只读存储只能读取不能写入，关机后其中的内容不丢失。<br>这些存储器从功能和连接上又可分为以下几类：<br><br>随机存储器<br>用于存放供CPU使用的绝大部分程序和数据，主随机存储器一般由两个位置上的RAM组成，装在主板上的RAM和插在扩展插槽上的RAM。<br><br>装有BIOS（Basic Input/Output System，基本输入输出系统）的ROM<br>BIOS是由主板和各类接口卡（如：显卡、网卡等）厂商提供的软件系统，可能通过它利用该硬件设备进行最基本的输入输出。<br>在主板和某些接口卡上插有存储相应BIOS的ROM，例如：主板上的ROM中存储着主板的BIOS（通常称为系统BIOS）；显卡上的ROM中存储显卡的BIOS；如果网卡上装有ROM，那其中就可以存储网卡的BIOS。<br><br>接口卡上的RAM<br>某些接口卡需要对大批量输入、输出数据进行暂时存储，在其上装有RAM。最典型的是显示卡上的RAM，一般称为显存。显示卡随时将显存中的数据向显示器上输出。换句话说，我们将需要显示的内容写入显存，就会出现在显示器上。<br><br><br><strong>内存地址空间<br></strong>上述的各种存储器，它们在物理上是独立的器件，但是它们在以下两点上相同：<br>1）都和CPU的总线相连；<br>2）CPU对它们进行读或写的时候都通过控制线发现内在读写命令。<br><br>也就是说，CPU在操纵和控制它们的时候，把它们都当作内存来对待，把它们总的看作一个由若干存储单元组成的存储器。<br><span style="BACKGROUND-COLOR: yellow">这个逻辑存储器就是我们所说的内在地址空间。</span><br><br><span style="BACKGROUND-COLOR: yellow">所有的物理存储器被看作是一个由若干存储单元组成的逻辑存储器，每个物理存储器在这个逻辑存储器中占有一个地址段，即一段地址空间。<br></span><span style="BACKGROUND-COLOR: yellow">CPU在这段地址空间中读写数据，实际上就是在相对应的物理存储器中读写数据。<br></span><br>内存地址空间的大小受CPU地址总线宽度的限制。<br>8086CPU的地址总线宽度为20，可以传送2^20个不同的地址信息（大小从0至2^20-1）。即可以定位2^20个内在单元，则8086PC的内在地址空间大小为1MB。<br>同理，80386CPU的地址总线宽度为32，则内存地址空间最大为4GB。<br><br>我们在基于一个计算机硬件系统编程的时候，必须得知道这个系统中的内在地址空间分配情况。<br><br>因为当读者想在某类存储器中读写数据的时候，读者必须知道它的第一个单元的地址和最后一个单元的地址，才能保证读写操作是在预期的存储器中进行。<br><br>比如，读者希望向显示器输出一段信息，那么读者必须将这段信息写到显存中，显卡才能将它输出到显示器上。要向显存中写入数据，读者必须知道显存在内存地址空间中的地址。<br><br>不同的计算机系统的内存地址空间的分配情况是不同的。<br><br>内存地址空间<br>最终运行程序的是CPU，我们用汇编编程的时候，必须要从CPU角度考虑问题。<br>对CPU来讲，系统中的所有存储器中的存储单元都处于一个统一的逻辑存储器中，它的容量受CPU寻址能力的限制。<br>这个逻辑存储器即是我们所说的内存地址空间。<br><br><br><br><br><br><br><br><br><br><br><br><br><br></p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/120085.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-07-11 14:50 <a href="http://www.cppblog.com/luqingfei/archive/2010/07/11/120085.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>汇编语言简介</title><link>http://www.cppblog.com/luqingfei/archive/2010/07/11/120083.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Sun, 11 Jul 2010 05:52:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/07/11/120083.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/120083.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/07/11/120083.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/120083.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/120083.html</trackback:ping><description><![CDATA[汇编语言是各种CPU所提供的机器指令的助记符的集合，人们可以用汇编语言直接控制硬件系统进行工作。<br><br>汇编语言是很多相关课程（如：数据结构、操作系统、微机原理等）的重要基础。<br><br>汇编语言是人和计算机沟通的最直接的方式，它描述了机器最终所要执行的指令序列。<br><br>我们想深入研究英国文化，不会英语行吗？<br><br>（当然，并不是非要所有人都去深入研究，毕竟这是相当枯燥和乏味的，必须要具有强大的信仰才行。）<br><br>汇编语言是和具体的微处理器相联系的，每一种微处理器的汇编语言都不一样，我们只能通过一种常用的、结构简洁的微处理器的汇编语言来进行学习，从而达到学习汇编的两个最根本的目的：充分获得底层编程的体验，深刻理解机器运行程序的机理。<br>这两个目的达到了，其他目的也就自然而然地达到了。举例来说，你在学习操作系统等课程时，对许多问题就会有很通透的理解。<br><br>我们的学习不能在一台抽象的计算机上来进行，必须针对一台具体的计算机来完成学习过程。<br><br>为了便于学习的过程容易展开，以8086CPU为中央处理器的PC机来进行学习。<br><br>8086CPU可以满足以下条件：常用而结构简洁，常用保证了可以方便地进行实践，结构简洁则便于进行学习。<br><br>纯粹的8086PC机已经不存在了，对现今的机器来讲是，它已经属于古玩。<br><br>但是，现在的任何一台PC机中的微处理器，只要是和Intel兼容的系列，都可以8086的方式进行工作。<br><br>可以将奔腾系列的微处理器当作一个快速的8086微处理器来用。<br><br><br>
<img src ="http://www.cppblog.com/luqingfei/aggbug/120083.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-07-11 13:52 <a href="http://www.cppblog.com/luqingfei/archive/2010/07/11/120083.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ASMIDE开发环境</title><link>http://www.cppblog.com/luqingfei/archive/2010/07/11/120080.html</link><dc:creator>路青飞</dc:creator><author>路青飞</author><pubDate>Sun, 11 Jul 2010 05:01:00 GMT</pubDate><guid>http://www.cppblog.com/luqingfei/archive/2010/07/11/120080.html</guid><wfw:comment>http://www.cppblog.com/luqingfei/comments/120080.html</wfw:comment><comments>http://www.cppblog.com/luqingfei/archive/2010/07/11/120080.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/luqingfei/comments/commentRss/120080.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/luqingfei/services/trackbacks/120080.html</trackback:ping><description><![CDATA[<p>开发工具基于MASM32 V8<br>编辑工具：EditPlus</p>
<p>直接解压缩到指定目录，比如D盘根目录：<br>d:\masm32\<br>d:\editplus\</p>
<p>可把批处理文件var.bat放置在masm32目录下。</p>
<p>批处理文件Var.bat用于设置临时环境变量，并且调用Makefile，运行编译后的可执行文件。</p>
<p>EditPlus已经配置了ASM代码高亮及模板。</p>
<p>在EditPlus的配置用户工具中，依照如下设置，即可以在Editplus中编译ASM代码文件，并运行编译后的可执行文件。<br>菜单文本：Run Asm<br>命令：d:\masm32\var.bat<br>参数：$(FileNameNoExt)<br>初始目录：$(FileDir)</p>
<p><br>开手你的第一个ASM练习:<br>step1: 打开EditPlus<br>step2: 点击菜单[文件]，[新建 ASM] ，然后EditPlus会自动加载一个hello world的ASM模板<br>step3:如果不想加点什么，可直接保存该代码文件。比如命名为HelloAsm<br>step4：在HelloAsm文件同目录下，建立makefile文件，内容如下：<br>&nbsp;NAME = HelloAsm<br>OBJS = $(NAME).obj</p>
<p>LINK_FLAG = /subsystem:windows<br>ML_FLAG = /c /coff</p>
<p>$(NAME).exe: $(OBJS)<br>&nbsp;Link $(LINK_FLAG) $(OBJS)<br>.asm.obj:<br>&nbsp;ml $(ML_FLAG) $&lt;</p>
<p>clean:<br>&nbsp;del *.obj<br>&nbsp;<br>&nbsp;<br>step5：执行EditPlus菜单[工具]下的RunAsm命令。</p>
<p>即可以编译HelloAsm文件，并且执行编译后的可执行文件。</p>
<p>这只是个简单的粗糙的ASM IDE开发环境。<br>主要用于新手入门。</p>
<p>感谢你的使用。<br></p>
<img src ="http://www.cppblog.com/luqingfei/aggbug/120080.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/luqingfei/" target="_blank">路青飞</a> 2010-07-11 13:01 <a href="http://www.cppblog.com/luqingfei/archive/2010/07/11/120080.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>