Skip to content

第一章:计算机中的数制与码制 ​

§ 1.1 数制的表示及转换 ​

数制转换我们主要考虑两种情况,十进制与任意进制数的转换,以及2进制,8进制,16进制之间的转换,并且主要考虑整数,对小数而言,了解即可.

首先我们来看十进制数与任意进制数的转换,我们这里只给一个比较通用的算法 十进制转r进制:除以r取余法.

这里给个例子,89转换为二进制:image-20210227150648903

下一个问题就是如何把二进制转换为十进制,我们采用按权展开,即,把r进制转换为十进制,第n位的数字乘以第n位的权(),再是该数的十进制数了

小数部分需要用到乘进制取整法,这个一般不会遇到,看看书了解即可

对于二进制和八进制,十六进制的转换比较基础,这里不多赘述,简而言之就是,三位二进制表示一位八进制,四位二进制表示一位十六进制

§ 1.2 二进制的运算 ​

对于二进制数的运算,分为算数运算(加减乘除)和逻辑运算(与或非)

我相信大多数人对于二进制算数运算都没什么问题,这里给个例子:97+89

image-20210227153525532 那么这个是手动运算,如果我们要在CPU里完成这个操作,应该怎么办呢?

首先认识一个东西,叫寄存器,我们要知道,计算机的CPU要进行运算,一定是数据和数据的运算,那么这些数据来自哪里呢?当然是内存了,但是如果CPU每次运算都要去内存跑一趟,那就很浪费时间了,所以为了节约时间,厂商们在CPU内部这里了一些"小内存".这就是寄存器,寄存器在cpu内部,访问速度是很快的,所以运算的时候我们一般把数据读到寄存器中,8086一共有14个16位的寄存器,其中有4个寄存器分别可以拆成两个8位的寄存器,这是下一章节的内容,这里因为要用到,所以简单提一下

再认识两个汇编指令:

nasm
MOV AL,97;MOV指令,相当于C语言中的赋值语句,把97这个数据传输到AL寄存器,需要注意的是这里的AL是八位寄存器
ADD AL,89;ADD指令,相当于C语言中的+=,将AL的寄存器的值加上89,然后再赋值到AL寄存器

此时上方两条汇编指令运行后,AL寄存器中的值就是97+89的运算结果

简单提一下数制的表示,对于十进制而言,我们可以直接写,也可以在后面加一个D,对于八进制而言,我们在末尾加一个Q表示,对于十六进制,我们在末尾加一个H,对于二进制,我们在末尾加一个B

这里还需要注意,当我们要表示十六进制的时候,如果第一位是字母,例如B0H,这个时候CPU是不认识你这个十个什么东西的,所以为了让CPU把这个识别为一个数据,我们在前面加一个0,也就是0B0H

我们前面所说的都是无符号数,当我们要计算机表示负数的时候,无符号数显然就不能胜任了,这个时候就需要引进有符号数,有符号数,顾名思义,就是有符号的数,有符号数的表示,我们把最高位设置为符号位,最高位是1,表示负数,最高位为0,表示正数

我们再来看这个例子image-20210227153525532

当他是无符号数运算的时候,他一点问题没有,但是如果他是有符号数的运算,两个参与运算的数都是正数,得到的结果却是一个负数,这显然与我们期望的结果不同,这种情况我们叫做溢出,为什么会有溢出?因为要表达的数超过了他能表达的范围了.

这些数进行运算之后,一定会有一些状态,比如,这个数是正数还是负数,这个数是不是0,有没有溢出,这些都是属于运算后的状态,我们程序员就是要根据这些状态来进行判断,从而进行条件分支,那么这些状态存储在哪呢?CPU内部设置了一个保存运算后结果状态的寄存器,我们称他为处理器状态字寄存器,用PSW 表示,也叫做标志寄存器(flags).

image-20210227161643854

PSW是一个十六位的寄存器,但是他只用了九位,其他都是无效位,也就是根本没用的位. 九位有效位在PSW中的位置如图:

image-20210227161804318

例如第六位ZF,ZF状态位保存结果是否为0,如果结果为0,ZF等于1,如果结果不为0,ZF为0,如果我们要判断一个运算的结果是否为0,我们就应该去测试ZF标志位,而不能去像c语言那样直接和0比大小.同理,第十一位为OF,溢出标志位,如果结果溢出,OF就是1,没有溢出OF就是0,这里看一下上面的指令

nasm
MOV AL,97;MOV指令,相当于C语言中的赋值语句,把97这个数据传输到AL寄存器,需要注意的是这里的AL是八位寄存器
ADD AL,89;ADD指令,相当于C语言中的+=,将AL的寄存器的值加上89,然后再赋值到AL寄存器

这个指令运算后作为有符号数而言显然是溢出了,所以OF就自动变成1了,还有个SF标志位,结果为负数的话SF就是1,结果是正数SF就是0.总而言之,我们知道了,在条件分支结构的程序设计里,不能像C语言那样去用关系运算符比较,而是根据PSW保存的结果状态来判断,但实际上不是所有运算都会改变psw,这里我们只能知道,ADD运算是一定可以改变结果状态的

前面通过二进制加法运算引出了很多知识,接下来说一下减法运算,减法运算和加法运算类似:

例:image-20210227162857153 用汇编语言实现这个功能:

nasm
MOV AL,97
SUB AL,89;SUB指令相当于C语言中的-=操作,该指令把AL的值减去89并把结果赋值给AL

那么这里给出一个问题,CPU执行完这个指令后,ZF SF OF等于啥 这里就不说了,读者可以自己思考,只给一个答案:ZF = 0 SF = 0 OF = 0

这里补充一下溢出的判断原则,先说无符号数,无符号数的最高位如果最高位有进位或者借位,结果一定溢出 有符号数就稍微麻烦一点,我们把符号位称为最高位,符号位后面的那个叫做次高位,如果最高位和次高位一个进位(借位)另一个没有进位(借位),那么结果就一定溢出,要想要结果不溢出,最高位和次高位要么同时进位(借位),要么同时不进位(借位)

乘除法不用掌握,就不说了

除了算数元素,我们还有逻辑运算 逻辑运算有与或非三种

与运算对应的汇编操作码为AND,那么这个操作数对应C语言的代码为&= 举个例子

nasm
MOV AL,97
AND AL,89

这里执行的操作就是把97放到AL寄存器后,让AL寄存器的值(97)和89进行按位与运算,然后把结果放回AL寄存器

注意,运算指令ADD,SUB这些,影响了所有状态标志位,但是这个逻辑运算的与或非,只会影响一部分状态标志位,至于哪一部分,以后会讲到

那么与运算有什么用途呢?我们一般用与运算来屏蔽某些位,也就是把某些位清零,就可以用与运算(与0为0,与1不变)

*eg:*将AX寄存器的低4位清零,其余位不变;

nasm
AND AX,0FFF0H;这个程序应该不难

或运算对应的操作码为OR,对应的C语言运算符为|=,使用方法和与运算类似,不多赘述,主要说一下或运算的运用

image-20210227170344015

或运算的运用场合:要把某些为变成1,其余位不变(或1为1,或0不变)

*eg:*将BX寄存器的低4位置1,其余位不变;

nasm
OR BX,000FH;这个程序也不难,很好理解了

剩下一个异或运算,这个运算对应的机器码为XOR,对应C语言运算符为^=,按位异或后再赋值到左边

image-20210227170818897

异或运算的应用场合,要把某些操作数取反,剩余位不变,就要用异或运算(异或1取反,异或0不变)

*eg:*把AL寄存器的低4位保持不变,高4位取反;

nasm
XOR AL,0F0H

非运算,这个在C语言中似乎没有对应的运算符,但也很好理解,非运算操作码为NOT 简而言之就是取反后再放回去,NOT只有一个操作数,比如NOT AL就是把AL里面的东西按位取反后放回AL

特别强调:逻辑非运算不影响标志位,与运算或运算异或运算都会影响PSW中的部分标志位

§ 1.3 有符号数的表示 ​

这里简单的运算就说的差不多了,那么读者应该还记得我们前面说过的溢出吧,我们说过,当数超过了其能表示的最大范围,就会溢出,并且还说了溢出的判断方法,还说了无符号数和有符号数的表示范围是不同的,那么这里就来说一下有符号数和无符号数的表示范围 对于n位无符号数而言,表示范围为0~ 特殊的,8位无符号数的范围为0~255,16位无符号数的表示范围为0~65535

前面我们已经简单说过了有符号数在计算机中表示方法,这里再系统说一下,对于一个8位有符号数,他有~八位,对于~位我们用来表示数值,而位我们用来表示符号位,正数用0表示,负数用1表示,同理,如果是16位的数据,那么就是位表示数的数值,剩下的表示数的符号,这种表示方法表示的带符号数,我们叫做机器数表示法,但实际上,计算机的内部并不是这么简单地直接把数放进去,而是进行了某种运算,这里我们就来说说计算机表示带符号数采用的码制

原码:[X]~原~表示X的原码,用原码表示带符号数,其实就是机器数,例:= 00000011B=03H; = 10000011B = 83H 但是用原码表示有一个缺陷,如果我们要表示[0]~原~ = 00000000B,表示= 10000000B,同样一个数,表示却不一样,所以计算机内部一定不能用源码表示,这里还要了解一下源码的表示范围,对于n位源码的表示范围是

由于这种错误,就引出了补码 的定义:如果X大于0, 当X小于0, 例:= 03H

这里我们有简便算法,由原码求补码,符号位不变,剩余位按位取反,末位加一

在计算机中,数据都是补码表示

例如

nasm
MOV AL,-3;这里的-3会由汇编程序自动翻译为补码,所以这个不需要程序员操心

§ 1.4 有符号数的运算 ​

有符号数的运算主要聚焦在补码的运算

首先说补码的表示范围,n位补码的表示范围为~

补码的运算:

判断有符号数是否溢出我们前面已经简单说过了,也就是用最高位的进位状态去异或次高位的进位状态,如果状态为0,表示没有溢出,如果为1,表示溢出

例如:+在八位机里一定溢出,因为他们做补码运算一定会得到一个负数,我们用程序实现

nasm
MOV AL,53
ADD AL,91

当执行完这段程序的时候,结合我们已经接触过的知识点,我们就知道,psw中的OF标志位一定为1,SF一定为1,CF为0,ZF为0,PF = 1,AF = 1

pf为奇偶标志位,如果结果第八位1的个数为偶数,则PF为1,PF标志位存在的意义在于串行通信里对数据进行奇偶校验,AF标志位为辅助进位标志位,如果D~3~到D~4~位有进位,AF就为1,至此,psw中的6个状态标志就说完了,剩下的3个为控制标志,以后来说

从公式③不难看出,我们要计算两个补码相减,就要知道一个补码的负数的补码,你会发现这样很麻烦,因为我们需要把他转换成源码后取反再转换成补码,这里有一个简便算法,的基础上,包括符号位,各位取反,末位加一,得到的就是,我们把这个过程称为变补 之所以要把减法转换成加法,因为减法器的逻辑比加法器难,设置ALU电路的时候就可以不用考虑减法器,可以简化电路缩小体积

§ 1.5 BCD码 ​

bcd码是一种二进制对十进制的编码,本质上是用四位二进制数表示一位十进制数,但是四位二进制数有16种状态,一位十进制数只有10种状态,所以我们规定四位二进制的前十个状态,也就是0000B~1001B来分别表示十进制的0~9,注意BCD码是按位进行编码的,那么存储器内部是按照字节组织的,一个BCD码是四位,一个字节是八位,我们可以用一个字节的高四位表示一个bcd码,低四位再表示一个BCD码,这样一个字节可以表示两个BCD码,我们称这种一个字节表示两个BCD码的编码为压缩BCD码,还有一种叫做分离BCD码,分离BCD码也就是用一个字节的低四位表示一位BCD码,高四位不用他,一般把他置0

BCD码也是用来做运算的,因为计算机内部都是二进制数,有时候需要我们进行十进制数运算,就要用BCD编码,但这样编码后通过加法器得到的值肯定是有问题的,所以运算后需要用进行BCD调整,对于压缩BCD数和分离BCD数有不同的调整方式,可以去查看参考教材的p53,实际上不用掌握

§ 1.6 ASCII码 ​

回顾一下,计算机中的数都是用二进制数表示的,计算机中的带符号数都是用补码表示的 计算机中的字符都是用ASCII码表示的 在本课程中,在汇编语言程序设计中会用到ASCII码

我们需要掌握的ASCII码有 'A' 65 'a' 97 '0' 48 ' ' 32

键盘上输入的都是字符,如何把字符'0'~'9'转换为数字0~9呢,我们可以减去30H或者去与0FH

大写字符+36D就可以转换成小写字符,小写字符-36D就可以转换成大写字符,还可以采用逻辑运算 image-20210228163826271 这个如何转换呢,其实可以用逻辑运算很简单的完成这个指令 先了解大写字母的asc码规律,都是:0100xxxxB~0101xxxxB 再看小写字母的规律:0110xxxxB~0111xxxxB 你会发现大写字母和小写字母的区别在于D~5~位,小写字母D~5~位为1,大写字母D~5~位为0 所以把小写转换成大写的标准答案为:AND AL,0DFH由于asc码最高位一定是0,所以等效于AND AL,5FH 把大写转换成小写的标准答案为:OR AL,20H

以笔记为镜,可以知得失