本章目标
2.13 机器指令的编码
Subtract R4, R5 Add R4, R2 Add R5, R3 LOOP1: Move R6, R2 Move R7, R3 LOOP2: MoveByte R8, (R6)+
CompareByte R8, (R7)+
Branch≠0 NOMATCH Compare R5, R7 Branch>0 LOOP2 Move RESULT, R2 Branch DONE NOMATCH: Add R2, #1 Compare R4, R2 Branch≥0 LOOP1 Move RESULT, # –1 DONE: next instruction
R2 指向字符串 T
的形式表示必须由处理器电路执行的操作。就像在2.5 节讨论的那样,为了在处理器中执行,
汇编语言指令必须由汇编程序转换成机器指令,这些机器指令是按紧凑的二进制模式编码的。
现在我们来研究机器指令的几种可能的格式。Add 指令 Add Rdst, Rsrc1, Rsrc2 表示的是操作数都在处理器
Add Rdst, Rsrc, #Value
32 个可用位中,需要 10 位来指定两个寄存器。剩下的 22 位必须给出 OP 码以及立即操作数
BGT R2, R0, LOOP
寄存器R2 和 R0 可以在图 2-32b 中的两个寄存器字段中指定。6 位的 OP 码需要用来标记 BGT
的指令集。附录B 到 E 中我们选择了四种处理器指令集作为例子来进行介绍。
2.14 结束语
这一章从程序员的角度介绍了汇编与机器级的指令和程序的表示与执行。在讨论中强调 了寻址技术和指令序列的基本原则。程序设计示例说明了使用现代计算机指令集实现操作的基 本类型。介绍了常用的寻址方式。对子程序的概念和实现子程序所需要的指令也进行了讨论。
在本章的讨论中,我们还对比了两种不同的机器指令集(RISC 和 CISC)的设计方法。
2.15 问题解析
本节将介绍一些可能要求学生解决的典型问题,并分析说明如何解决这样的问题。
例2.1
问题 :假设有一个由ASCII 编码字符组成的字符串存放在存储器中起始地址为 STRING 的连续单 元中。这个字符串以回车(CR)符结束。写一个 RISC 风格的程序来确定字符串的长度并将其存储在 LENGTH 单元中。
解答 :图2-33 给出了一个可能的程序。字符串中的字符都与 CR(ASCII 码为 0x0D)进行比较,递 增计数器直到到达该字符串的末尾。
Move R2, #STRING
Clear R3
Move R4, #0x0D
LOOP: LoadByte R5, (R2) Branch_if_[R5]= [R4] DONE
Add R2, R2, #1
Add R3, R3, #1
Branch LOOP
DONE: Store R3, LENGTH
R2 指向字符串的开始处 R3 是计数器并被清为 0 回车符的ASCII 码 获取下一个字符 如果字符为CR 则结束 递增字符串指针 递增计数器
如果没有完成,就循环回到前面 将计数值存放在单元LENGTH 中
图2-33 例 2.1 的程序 例2.2
问题:我们想在一个32 位正整数的列表中找出最小的数。在找到最小的数后,把它的值存放在地址 为1000 的字中。下一个字存放列表中的项数 n。接下来的 n 个字保存列表中的数。程序在地址 400 处开 始。写一个RISC 风格的程序来找出最小的数,程序中需包含按指定方式组织程序和数据所需的汇编指 示符。虽然程序必须能够处理不同长度的列表,但是在你的代码中只需包含由七个整数组成的样本数据 列表。
解答:图2-34 中的程序可以完成所需的任务。程序中的注释说明了该任务是如何执行的。
例2.3
问题 :写一个RISC 风格的程序将一个 n 位的十进制整数转换成二进制数。这个十进制数以 n 个 ASCII 编码字符的形式给出,就像在键盘中键入数字的情况一样。存储单元 N 中存放值 n,ASCII 字符串 从DECIMAL 处开始,转换后的数字存放在 BINARY 中。
解答 :考虑一个4 位的十进制数,D=d3d2d1d0 。这个数的值为((d3×10+d2)×10+d1)×10+d0。该 数字的这种表示形式是图2-35 中程序所使用的转换技术的基础。注意,每一个 ASCII 编码字符在它用于 计算之前都被转换为一个二进制编码的十进制(Binary Coded Decimal,BCD)数字。这里假设转换后的 值可以用不超过32 个位来表示。
例2.4
问题 :考虑一个数组A(i, j),其中行索引从 i= 0 到 n-1,而列索引从 j=0 到 m-1。这个数组一行接 一行地存放在计算机存储器中,每行的元素占用 m 个连续的字单元。假设存储器是按字节寻址的且字长 84
85
为32 位。写一个 RISC 风格的子程序,将第 x 列与第 y 列逐个元素地相加,将和元素存放在第 y 列中。
索引 x 和 y 通过寄存器R2 和 R3 传递给子程序。参数 n 和 m 通过寄存器 R4 和 R5 传递给子程序,元素 A(0,0) 的地址通过寄存器 R6 传递。
LIST EQU 1000
ORIGIN 400
Move R2, #LIST
Load R3, 4(R2)
Add R4, R2, #8
Load R5, (R4)
LOOP: Subtract R3, R3, #1 Branch_if_[R3]= 0 DONE
Add R4, R4, #4
Load R6, (R4)
Branch_if_[R5]≤[R6] LOOP
Move R5, R6
Branch LOOP
DONE: Store R5, (R2)
ORIGIN 1000
SMALL: RESERVE 4
N: DATAWORD 7
ENTRIES: DATAWORD 4,5,3,6,1,8,2 END
Move R3, #DECIMAL
Clear R4
LOOP: LoadByte R5, (R3) And R5, R5, #0x0F Add R4, R4, R5 Add R3, R3, #1 Subtract R2, R2, #1 Branch_if_[R2]= 0 DONE Multiply R4, R4, #10 Branch LOOP DONE: Store R4, BINARY
初始化计数器R2 为 n
Load R2, X
Load R3, Y
Load R4, N
Load R5, M
Move R6, #ARRAY
Call SUB
next instruction ::
SUB: Subtract SP, SP, #4 Store R7, (SP) LShiftL R5, R5, #2
Subtract R3, R3, R2 LShiftL R3, R3, #2 LShiftL R2, R2, #2
Add R6, R6, R2
Add R7, R6, R3
LOOP: Load R2, (R6)
Load R3, (R7)
Add R2, R2, R3
Store R2, (R7)
Add R6, R6, R5
Add R7, R7, R5
Subtract R4, R4, #1 Branch_if_[R4]>0 LOOP Load R7, (SP) {if (LIST[k] > LIST[j])
{TEMP = LIST[k];
LIST[k] = LIST[j];
LIST[j] = TEMP;
}
Move R2, #LIST
Move R3, N
Subtract R3, #1
OUTER: Move R4, R3
Subtract R4, #1 MoveByte R5, (R2,R3) INNER: CompareByte (R2,R4), R5
Branch≤0 NEXT MoveByte R6, (R2,R4) MoveByte (R2,R4), R5 MoveByte (R2,R3), R6 MoveByte R5, R6 NEXT: Decrement R4
Branch≥0 INNER Decrement R3 Branch >0 OUTER
加载LIST 到基址寄存器 R2 中 初始化外部循环的索引寄存器R3 为 j=n-1
初始化内部循环的索引寄存器R4 为 k=j-1
加 载LIST(j) 到 R5 中,R5 保 存 着当前子列表中的最大值
如果 LIST(k) ≤ [R5],则不交换 否 则, 将LIST(k) 和 LIST(j) 交 换,
并将新的最大值装入R5 中 寄存器R6 作为 TEMP
递减变址寄存器R4 和 R3,它们也 作为循环计数器,在循环没有完成 时就跳转回前面
图2-38 一个字节排序程序
习题
[ E ] 2.1 在一些存储单元中给出一个二进制模式,是否能说明这个模式表示的是一条机器指令还是一个 数字?
[E] 2.2 考虑一台计算机,它有一个按字节寻址的存储器,该存储器按照大端策略组织为 32 位的字的集 合。一个程序读取在键盘上输入的ASCII 字符并将它们存储到从单元 1000 开始的连续字节单元 中。在输入单词“Computer”后,说明单元 1000 和 1004 中两个存储字的内容。
[E] 2.3 针对小端策略重复习题 2.2 中的问题。
[E] 2.4 在使用以下每种寻址方式去访问一个存储器操作数之前,寄存器 R4 和 R5 包含着十进制数 2000 和3000。在每一种情况中有效地址(EA)是多少?
(a)12(R4)
(b)(R4,R5)
(c)28(R4,R5)
(d)(R4)+
(e)-(R4)
[E] 2.5 写一个 RISC 风格的程序,计算表达式 SUM = 580 + 68 400 + 80 000。
[E] 2.6 写一个 CISC 风格的程序完成习题 2.5 中的任务。
[E] 2.7 写一个 RISC 风格的程序,计算表达式 ANSWER = A×B×C×D。
[E] 2.8 写一个 CISC 风格的程序完成习题 2.7 中的任务。
[M] 2.9 重写图 2-8 中的加法循环,使得列表中的数字可以以相反的顺序被访问 ;也就是说,第一个被访 问的数是列表中的最后一个,最后一个被访问的数在存储单元NUM1 中。尝试实现最有效的确 定循环终止的方法。你的循环是否比图2-8 中的循环执行得更快?
[M] 2.10 将图 2-10 所示的学生成绩列表修改为每个学生包含有 j 项测验分数。假设有 n 个学生。写一个 RISC 风格的程序,计算每项测验的分数和,并将这些和存储在存储器中地址为 SUM、SUM+4、
SUM+8、…的字单元中。由于测验的数量 j 比处理器中的寄存器数量要大,所以图 2-11 所示的 用于3 项测验的程序已不能被使用。应该使用两个嵌套循环。内部循环累加每项特定测验的和,
而外部循环遍历测验的数目 j。假设用来存放和的存储区域最初已被清除为零。
[M] 2.11 写一个 RISC 风格的程序,在包含 n 个 32 位整数的列表中找出负数的个数,并将计数结果存 储在单元NEGNUM 中。值 n 存储在存储单元 N 中,列表中第一个整数存储在单元 NUMBERS
88~ 89
中。程序要包含必要的汇编指示符和一个含有六个数的样本列表,其中有一些数是负数。
[E] 2.12 以下两个语句段都可以将值 300 存储在单元 1000 中,但是却是在不同的时间完成的。
ORIGIN 1000 DATAWORD 300 以及
Move R2, #1000 Move R3, #300 Store R3, (R2)
请解释这种差异。
[E] 2.13 参照图 2-13 的风格为图 2-11 中的程序写一个汇编语言程序。假设采用图 2-10 中的数据布局。
[E] 2.14 写一个 CISC 风格的程序完成例 2.1 中的任务。一条指令最多可以有一个操作数在存储器中。
[M] 2.15 写一个 CISC 风格的程序完成例 2.2 中的任务。一条指令最多可以有一个操作数在存储器中。
[M] 2.16 写一个 CISC 风格的程序完成例 2.3 中的任务。一条指令最多可以有一个操作数在存储器中。
[M] 2.17 写一个 CISC 风格的程序完成例 2.4 中的任务。一条指令最多可以有一个操作数在存储器中。
[M] 2.18 写一个 RISC 风格的程序完成例 2.5 中的任务。
[E] 2.19 在一个程序中寄存器 R5 用来指向一个包含 32 位数的栈的栈顶。使用变址、自动增量和自动减 量寻址方式写一个指令序列,执行以下的各个任务:
(a)弹出栈顶部的两项内容,将它们相加,然后将结果压入栈中。
(b)从栈顶将第五项内容拷贝到寄存器 R3 中。
(c)从栈中删除栈顶部的 10 项内容。
对于每一种情况,假设栈都包含10 个或更多的元素。
[M] 2.20 在图 2-18 的程序中,下列每一条指令执行之后,写出此时处理器栈的内容和栈指针 SP 的内容。
假设在调用程序开始执行之前,[SP]=1000,且在第一级上。
(a)子程序中第二条 Store 指令
(b)子程序中最后一条 Load 指令
(c)调用程序中最后一条 Store 指令 [M] 2.21 一个子程序的返回地址可保存在:
(a)处理器的寄存器中
(b)一个与调用有关的存储单元中,因此当子程序从不同的地方被调用时将使用不同的单元
(c)一个堆栈中
在这些可能性中哪一种支持子程序嵌套?哪一种支持子程序递归(也就是子程序调用它自身)?
[M] 2.22 除处理器栈之外,在一些程序中使用另外一个栈可能会更加方便。通常在存储器中为第二个栈 分配固定大小的空间。在这种情况下,当栈已达到其最大大小时,需要避免再把一个项压入栈 中。同样也要避免从一个空栈中弹出一个项,这可能是由一个编程错误引起的。写两个简短的 RISC 风格的程序,分别叫做 SAFEPUSH 和 SAFEPOP,用于压入和弹出这个栈结构,同时避免 这两个可能出现的错误。假设将被压入或弹出的元素位于寄存器R2 中,寄存器 R5 作为这个用 户栈的栈指针。如果栈中最顶层的元素存放在单元TOP 中,那么这个栈就满了,如果最后弹出 的元素存放在单元BOTTOM 中,那么这个栈就是空的。如果发生错误,程序就应该相应地跳 转到FULLERROR 和 EMPTYERROR 中。假设所有的元素都是一个字的大小,并且栈向较低编 号的地址单元增长。
[M] 2.23 重复习题 2.22 中的问题,写一个 CISC 风格的程序,可以使用自动增量和自动减量寻址方式。
[D] 2.24 另外有一个类似于栈的有用的数据结构称为队列(queue)。将数据存储到队列中和从队列中取 出数据都是基于先进先出(FIFO)方法的。因此,如果我们假设队列在存储器中朝地址增加
[D] 2.24 另外有一个类似于栈的有用的数据结构称为队列(queue)。将数据存储到队列中和从队列中取 出数据都是基于先进先出(FIFO)方法的。因此,如果我们假设队列在存储器中朝地址增加