基本单元:
在MMIX中,基本构成单元为一个字节(共6位),由此一个字节能够表征的十进制数范围为\([0,63],\; \dagger \text{二进制为}[000000,111111]\)。在这样的情况下,想要表征超过63的数值,则需要至少两个字节的组合!计算机中的字由五个字节和一个符号位组成,其中符号位仅存在两种可能的值:正号+或者符号-。
寄存器:
寄存器是对存储器传输过来的数据进行处理的场所,包括常规的算术运算。在MMIX中,共存在”四种-九个“寄存器:
- A类寄存器主要用于算术运算,基本单元是符号位+五个字节
- X类寄存器主要与A类寄存器搭档使用,用于乘或者除算术操作,基本单元是符号位+五个字节
- I类寄存器主要用于计算以及参考变量的地址,基本单元是符号位+两个字节
- J类寄存器主要用于记载“跳转”命令的地址,基本单元是两个字节
除却寄存器,MMIX中还包括了:
“溢出”设定器
- “溢出”控制器,其值仅两种情况:开或者关
- “对比”指示器,其值为大于、小于或者等于三种情况
- 存储器,共有4000个字的储量,每个字涉及一个符号位+五个字节位
- 输入-输出设备
可以参考下图:

字的区域:
在MMIX中,一个字包括:符号位+五个字节位。实际运用中,大多数的指令仅涉及部分字节位的运用,因此需要对某个特定的指令在五个字节中的占位情况进行说明,如\((L:R)\),其中L表征左侧部分的序列号,R表征右侧部分的序列号。为了更简洁的进行表征,直接取\(8L+R\)的数值结果进行表示。
对于一个特定的命令而言,通常五个字节:a) 最低位存储的是命令的操作代码,如“8”代码表征“LDA”,即加载A类寄存器;b) 倒数第二位存储的是区域位,如“11”代码表征“1:3”的字区域占位;c) 倒数第三位存储的是地址的变更指令,如“4代码表征加载寄存器\(I_4\)用于变更地址;d) 其余为存储地址的代码
| 0 | 1 | 2 | 3 | 4 | 5 |
| ± | 地址占据两个字节 | 4 | 11 | 8 | |
指令的规则:
- ”加载“指令:”加载“指令的动作是将内容从存储器中取出后放置到寄存器中,其基本结构形式为:LDA 地址代码 区域代码。意思是将地址代码对应的全体内容中的特定区域部分取出来放置到A类寄存器中,内容在A类寄存器中的摆放原则为:优先占据低位,再依次占据高位。其他的X类寄存器与I类寄存器的基本加载原理类似。
- ”储存“指令:”储存“指令的动作是将寄存器中的内容取出后放置再存储器去,其基本结构形式为:STA 地址代码 区域代码。意思是将A类寄存器的全体内容中指定区域部分取出,放置在地址代码指定的存储器中去,内容在存储器中的摆放原则是:区域计数从右侧部分以1为基准开始往左侧移动R位,再往右侧以1位基准移动L位,在存储器中对应区域摆放,其他内容保持不变。
- ”算术“指令:MMIX中涉及到的”算术“指令主要为:加、减、乘、除这四个方面,其主要作用为将存储器中的内容与寄存器中的内容进行算术操作后再赋予寄存器。可以简单的将加、减归为一类,乘、除归为一类\(\dagger \text{分类标准为X类寄存器的运用}\)。
- ”值变更“指令:”值变更“指令的动作是对寄存器中的内容进行变更的操作,包括”录入“、“递增”、“递减”等,其中“录入”指令在本质上与“加载”指令一致,均是从存储器中取值并赋予给寄存器的操作。
- “对比”指令:“对比”指令是相对于寄存器中的内容与存储器中某个位置中的内容两者而言。
- “跳转”指令:正常来讲,MMIX中的指令的执行是按顺序逐个执行的,但在实际操作中,一个初始指令执行完毕后,会存在的一种需求是需要先去执行在其他位置的指令,再接着执行初始指令后的下一个指令。
- “其他”指令:除却上述指令,MMIX中还存在其他指令,如:“移位”指令、”移址“指令、”暂停“指令、”类型转换“指令等
习题集:
00) 如果MMIX是三进制计算器,一个字节能够表征的数共有多少种?
因为MMIX的一个字节能够容纳最大的十进制数为63,其转换为三进制如下
$$
\begin{align}
63 &= 2\cdot 3^3 + 1\cdot 3^2 + 0\cdot 3^1 + 0\cdot 3^0 \\
&= \lvert 2100 \rvert_3
\end{align}
$$
所有共需要4个位来容纳
01) 如果数值99999999需要在MMIX中进行表征,则需要多少个字节?
MMIX的一个字节能够表征的最大十进制数为99,而99999999数值共涉及8位,所以对于十进制型的计算机而言,4个字节便足够;然而,当需要二进制型的计算机时,4个字节所能表达的最大十进制数值为16777215,小于99999999,所以还需要额外的一个字节,即共5个字节
02) MMIX的基本结构形式为一个符号位+五个字节位,其中的地址区间、标定区间、场区间、操作符区间所占据的字节位分为是多少?
地址区间:0:2
标定区间:3:3
场区间:4:4
操作符区间:5:5
03) 在”加载“指令中,存在如下的命令结构形式:LDA -2000,4,为什么第二列中表征地址的代码能够用负数?
因为加载存储器中的特定位置的内容之前,首先对于特定位置的判定方式是I类寄存器的内容与地址M的和,如此一来,I类寄存器(4)的内容若是不小于2000,则上述命令即是有效的!
04) 在MMIX中,表征命令的方式有两种,如下:
- 字符表征:\(OP\; ADDRESS,I(F)\)
- 数值表征:\(|\dots|\dots|\dots|\dots|\dots|\dots|,\dagger\text{一个符号位+五个字节位}\)
由此,字\(|-|\overbrace{|\quad 80\quad|}^{two \;bytes}|3|5|4|\)对应的字符表征是什么?
MMIX中,最后一个字节位由具体操作的标识代号占据,倒数第二个字节位由区域代号占据,倒数第三个字节位由I类寄存器代号占据,前两个字节位为存储器中对应的地址。
基于上述基础知识,可知\(|-|\; 80\;||3|5|4|\)对应的字符表征为\(DIV \quad -80,\;3(0:5)\)
05) 假设存储器中地址3000存放的内容为\(|+|5|1|\;200\;|15|\),那么在此基础上,执行下述命令后的结果为何?
- LDAN 3000: 该命令正常将地址3000中的内容加载到A类寄存器,并对符号位进行取反处理,结果为\(|-|5|1|\;200\;|15|\)
- LD2N 3000(3:4): 该命令涉及到\(I_2\)类寄存器,并仅加载3、4字节位的内容,结果为\(|-|0|0|0|\;200\;|\)
- LDX 3000(1:3): 因为3、4字节位是合成的一个变量,这样加载内容时便会存在不确定情况,结果为\(|+|0|0|5|1|?|\)
- LD6 3000:因为是I类寄存器,有效位为最后两位字节,但是该命令默认加载全部字节位和符号位,由此会造成不不确定的结果,即\(|+|?|?|?|?|?|\)
- LDXN 3000(0:0):结果为\(|-|0|0|0|0|0|\)
06) ”算术“指令中的DIV在下面几类情况下会存在”溢出“问题:
- 被除数V为0
- 商的大小超出5个字节能够容纳的范畴
那么对于涉及到的任意DIV操作,其不会发生”溢出“问题的边界条件为何?用\(X\; mod \; Y,\quad \lfloor X/Y \rfloor\)进行表征
已经清楚,发生”溢出“问题的边界条件为\(\lvert rA \rvert \ge \lvert V \rvert\),如此不发生”溢出“问题的边界条件即为\(\lvert rA \rvert \lt \lvert V \rvert\):
$$
\lvert rA \rvert \lt \lvert V \rvert \\
\Downarrow \\
\frac{\lvert rA \rvert}{\lvert V \rvert} \in [0,1) \\
\Downarrow \\
\lfloor \frac{\lvert rA \rvert}{\lvert V \rvert}
\rfloor + \frac{\lvert rA \rvert \;mod\;\lvert V \rvert}{\lvert V \rvert} \in [0,1)
$$
07) ”相除“指令DIV的某个运用如下:

现在追问一个问题:倘若rX before为\(|-|\;1234\;|0|3|1|\),其他的寄存器与存储器的值保持不变,求rA after与rX after?
因为DIV操作对rA寄存器与rX寄存器的影响在于:如果rA与V的符号一致,则rA after的符号为+,否则为-;rX after的符号与rA before的符号保持一致,由此rA after与rX after的结果为:
- rA after:\(|+|0|\;617\;|0|1|\)
- rX after:\(|-|0|0|0|1|1|\)
08) 罗列出所有能够触发“溢出”问题的命令操作?
ADD、DIV、INCA、INCX、NUM、JOV、JNOV
09) 罗列出所有能够触发“比较”指令的操作?
- CMPA、CMPX、CMPi
- JL、JE、JG、JGE、JLE
- JAN、JAZ、JAP、JANN、JANZ、JANP
- JXN、JXZ、JXP、JXNN、JXNZ、JXNP、JiN、JiZ、JiP、JiNN、JiNZ、JiNP
10) 罗列出所有能够影响寄存器rI1设定的MMIX操作?
LD1、LD1N、ENT1、ENN1、INC1、DEC1、MOVE
11) 找到一个命令,使得可实现如下命令:将rI3的内容乘以2后,将所得的结果再次存入rI3?
INC3 0,CONTENTS(rI3)
12) 假设在存储器地址1000处内包含的命令为‘JOV 1001’,该命令会在“溢出”开启的情况下关闭“溢出”,下一条被执行的命令的地址在1001。如果该命令更改为‘JNOV 1001’,结果会有何不同?更进一步的,如果该命令更改为‘JOV 1000’或者‘JNOV 1000’?
- ‘JNOV 1001’:该命令会在“溢出”未开启的情况下进行跳转,跳转的位置为下一个地址;若“溢出”开启,则关闭“溢出”并不会发生任何动作。
- ‘JOV 1000’:当“溢出”开启时,该命令关闭“溢出”并继续执行当前命令,而此时“溢出”已处于关闭状态,因此不会再由任何动作;当“溢出”命令关闭时,该命令不发生任何动作。
- ‘JNOV 1000’:当“溢出“关闭时,该命令开启“溢出”并继续执行当前命令,而此时“溢出”已处于开启状态,该命令会关闭”溢出“,因此会进行不断循环开启并关闭”溢出“键;当“溢出”命令开启时,该命令同样会不断进行循环
13) 对于每个MMIX操作,请考虑是否存在一种用于设定地址符(±AA)、地址变更标定符(I)、区域划分符(F)的指令,使得该指令的结果等效于NOP(可能命令执行所需的时间有所不同!),假设目前寄存器或者存储器的内容未知。比如:当地址符以及地址变更标定符为零时,INCA即是一个NOP;又比如:JMP永远无法成为一个NOP,因为该命令的执行总是会影响到rJ!
- 当地址符以及地址变更符为零时,LDA\LDX M(a) \(\dagger \; a \ge 1\) ;
- 当地址符及地址变更符为零时,LDi M(a) \(\dagger \; a \ge 1\) ;
- 当地址符的内容与寄存器的内容一致且地址变更符为零时,STA\STX M(a) a ≥ 1;
- 当地址符的内容与寄存器I的内容一致且地址变更符为零时,STi M(a) a ≥ 1;
- 当地址符的内容与寄存器J的内容一致且地址变更符为零时,STJ M(a) a ≥ 1;
- 当地址符的内容为+0且地址变更符为零时,STZ M(a)
- 当地址符及地址变更符为零时,ADD SUB
- 当地址符的内容与寄存器的内容一致且地址变更符为零时,ENTA\ENTX M(a) a ≥ 1;
- 当地址符的内容与寄存器的内容一致且地址变更符为零时,ENTi M(a) a ≥ 1;
- 当地址符及地址变更符为零时,INCX INCi DECA DECX DECi
- 当寄存器的内容不满足JA\JX\Ji后面紧跟的判定符时
- 当地址符及地址变更符为零时, SLC SRC
- 区域符为零、地址标记符为零时 MOVE
- HLT
14) 对于:“typewriter”或者“paper-tape”、“card-reader”或者“card-punch”、“line-printer”三种输入-输出设备而言,每个block中,能够携带的字母数字字符的数量为多少?
因为上述三种设备的输入或者输出机制是基于字符码,每个字节(byte)能够表征一个字符,即每个字(word)能够传递五个字符,这样三种设备能够携带的字符数取决于其block中字的数量
- “typewriter”或者“paper-tape”设备每个block能够携带14个字,即14×5=70个字符
- “card-reader”或者“card-punch”设备每个block能够携带16个字,即16×5=80个字符
- “line-printer”设备每个block能够携带24个字,即24×5=120个字符
15) 书写一个程序,其作用是将存储器中地址在0000到0099之间的内容设定为0,并且需要满足:a) 程序尽可能短;b) 程序执行起来尽可能快
对于这样一个问题,对其的解构分为两步:a) 初始化为零;b) 将初始化的值赋予各个地址中,则程序如下:
STZ 0000 — 初始化地址0000为零
INC1 1 — 初始化寄存器rI1
MOVE 0000(99) — 连续地址区间进行赋值
除了上述的程序,还可以直接将命令STZ 命令执行100次
对于第一种方法,其所需时间为(单位为unit):2+1+99×1.2
对于第二种方法,其所需时间为(单位为unit):1×100
显然第一种方法更为简洁,但第二种方法更快
16) 现在同样需要写一则程序,程序的作用是将存储器中地址在0000到N之间的内容赋值为0,其中N是寄存器rI2的内容。现需要满足:a) 程序尽可能简洁;b) 程序执行尽可能快;c) 程序从地址3000开始(整个程序的放置地);d) 程序对于\(0\le N \le 2999\)均能起作用
方法一:
(3000)STZ 0,2 --- 将寄存器rI2中的内容添加至0后得到的地址的内容赋值为零
(3001)DEC2 1 --- 寄存器rI2中的内容更新(以减1的形式展开)
(3002)J2NN 3000 --- 根据条件跳转回开始地
(3003)备注:该程序的缺陷之一是需要一个地址接着一个地址地展开赋值操作,相对来说比较费时,现在抛给我们自己的问题是能够一次性进行多次赋值操作?
方法二:一次性执行多个赋值操作
(3000)STZ 0 --- 将地址0000的内容进行初始化
(3001)INC1 1 --- 将地址0001的内容进行初始化
(3002)MOVE 0(N) --- 这里可以一次性将所涉及到的地址内容进行赋值操作
-------------------------------------------------
-思考01) 怎么对寄存器rI2中的变量N进行控制?
-思考02) 为了得到更为快速的程序,总是想将MOVE一次能够触发的赋值数最大化,即63(字节可能最大的情况)
-思考03) 需要展开寄存器rI2中的内容与63展开比较
-------------------------------------------------
(3003)DEC2 63
(3004)J2P 3005
(3005)MOVE 0(63) 注:可能会涉及到重复赋值,只是对结果无影响!
(3006)JMP 3003
-------------------------------------------------
-思考04) 当寄存器rI2中的内容不足63时怎么办?
-思考05) 按照第一种方法进行挨个执行?
(3007)STZ 0,1
(3008)DEC2 1
(3009)INC1 1
(3009)J2NN 3007
-------------------------------------------------
-思考06) Knuth提供的解答方案如下(当寄存器rI2中的内容不足63时)
(3007)INC2 63
(3008)ST2 3009(4:4) 注:将寄存器rI2中不足63的数放置在3009操作中的F域中!我得承认,Knuth你确实是个天才!
(3009)MOVE 0
17) 在下述名为“第一”的程序执行后,寄存器、存储器、以及溢出、比较指示器的值为何?
STZ 1 ---将地址0001的内容全部设定为零
ENNX 1 ---将地址0001的内容赋值给寄存器rX,符号取负
STX 1(0:1) --- 将寄存器rX的符号位以及地址位的首位赋值给地址0001
SLAX 1 ---将寄存器rAX(组合起来)的内容向左移位1
ENNA 1 ---将地址0001中的内容赋值给寄存器rA,符号取符
INCX 1 ---将寄存器rX的内容进行加1处理
ENT1 1 ---将0001的内容赋值给寄存器rI1
SRC 1 ---将寄存器rAX(组合起来)的内容右旋转移位1
ADD 1 ---将地址0001的内容与寄存器rA相加后赋给寄存器rA
DEC1 -1 --将寄存器rI1的内容进行减-1处理
STZ 1 ---将地址0001的内容全部设定为零
CMPA 1 ---将地址0001的内容与寄存器rA的内容进行比较
MOVE -1,1(1) --将地址为rI1-1的内容赋值到地址为rI1的内容中去
NUM 1 ---将寄存器rA以及rX里面的字符代码转化为十进制数存储在rA中
CHAR 1 ---将rA中的十进制数转化为字符代码依次存储在寄存器rA与rX中
HLT 1 ---机器停止运行
首先,对上述程序的每一行进行拆解,理解其大致意思,将其主要作用标注出来
其次,将上述程序的每一行执行后的结果表征出来,如下:
STZ 1 --- CONTENTS(0001) = +00000
ENNX 1 --- rX = -00001
STX 1(0:1) --- CONTENTS(0001) = -00000
SLAX 1 ---rA:待定 rX = -00010
ENNA 1 ---rA = -00001
INCX 1 ---rX = -00011
ENT1 1 ---rI1 = +00001
SRC 1 ---rA = -10000 rX = -00001
ADD 1 ---rA = -10000
DEC1 -1 --rI1 = +00002
STZ 1 ---CONTENTS(0001) = +00000
CMPA 1 ---EQUAL(对比指示器)
MOVE -1,1(1) --rI1:-+00003 CONTENTS(0001):+00000 CONTENTS(0002):+00000 CONTENTS(0003):+00000
NUM 1 ---rA = -10000 rX = -00 00 01 00 00
CHAR 1 ---rA = - 3030303030 rX = -31 30 30 30 30
HLT 1 ---机器停止运行
18) 在不考虑指令HLT的运行时间情况下,计算17)问题中的程序运行所需要的时间
记计算机的单位时间为\(\mu_0\),根据文章中的知识点,将各类命令的执行时间分类如下:
| 序号 | 命令 | 时间 |
| 1 | ADD、SUB、LOAD类、STORE类、SHIFT类、CMP类 | \(2\mu_0\) |
| 2 | MOVE | \(1.2\mu_0\) |
| 3 | MUL、NUM、CHAR | \(10\mu_0\) |
| 4 | DIV | \(12\mu_0\) |
| 5 | 所有其他命令 | \(1\mu_0\) |
如此,上述流程中每一条指令的运行时间梳理如下:
STZ 1 两个单位时间
ENNX 1 一个单位时间
STX 1(0:1) 两个单位时间
SLAX 1 两个单位时间
ENNA 1 一个单位时间
INCX 1 一个单位时间
ENT1 1 一个单位时间
SRC 1 两个单位时间
ADD 1 两个单位时间
DEC1 -1 一个单位时间
STZ 1 两个单位时间
CMPA 1 两个单位时间
MOVE -1,1(1) 1+2个单位时间
NUM 1 10个单位时间
CHAR 1 10个单位时间
从上至下,将各个命令所用时间进行加和,总共所需时间为42个单位时间,即\(42\mu_0\)
19) 写一个程序,该程序能够实现的功能如下:将MMIX的4000个地址(0~3999)的内容设定为‘HLT’指令,然后停止运行程序
根据题目中的指令结构形式,任意一个指令均可以表征为如:
| 0 | 1 | 2 | 3 | 4 | 5 |
| ± | A | A | I | F | C |
其中’HLT’指令的F项为2,C项为5,那么‘HLT’的各字节的内容如下:
| 0 | 1 | 2 | 3 | 4 | 5 |
| + | 0 | 0 | 0 | 2 | 5 |
所以为实现题干中的目标,基本的思路如下:
- 寻找一个临时的寄存器作为储存器用
- 将该寄存器的内容初始化为0
- 对寄存器的内容操作使其内容达到指令‘HLT’的内容
- 将该寄存器的内容赋值给0~3999地址
- 最后执行‘HLT’命令
初步尝试程序书写如下:
STZ 0 --- 将地址0的内容初始化为0
LDA 0 --- 将地址0的内容赋值送给临时寄存器rA中
LD1 0 --- 将地址0的内容赋值送给指标寄存器rI1
INCA 25 --- 将临时寄存器rA的内容加和至25
INC1 1 --- 将标定寄存器rI1的内容加和至1
STA 0 --- 将寄存器rA的内容赋值给地址0→指令‘HLT’
MOVE 0(3999) --- 对地址0~3999进行循环赋值操作
上述程序似乎理论上可行,但实际几个关键的问题等待确定:
- 因字节(6个bit)本身的容量限制,3999无法一次性装填进F字节位
- 执行‘MOVE’指令时,标定寄存器rI1会不断叠加,同样因为rI1寄存器的容量限制,无法填充超过两个字节的容量
- 上述指令是否是最简状态,是否存在优化空间?
由此,对上述程序,尤其是指令’MOVE 0(3999)’进行改写,改写的内容如下:
MOVE 0(63) --- 将地址0~63赋值,rI1更新为64
MOVE 63(63) --- 将地址63~126赋值,rI1更新为127
MOVE 126(63) --- 将地址126~189赋值,rI1更新为190
、、、、、、、、、、
思考:
如果按照上述不断执行’MOVE’指令,那么初步估计要将0~3999全部赋值完需要执行将近63次
因此,对上述多个’MOVE’指令再次进行改写,改写后如下:
ENT2 3999
JMP 3004
3003 MOVE 0(63)
DEC2 63
J2P 3003 --- 循环执行'MOVE'指令
INC2 63
ST2 3008(4:4)
3008 MOVE 0
综上所有分析,全部程序罗列如下:
3000 ENTA 2
3001 STA 0(4:4)
3002 INCA 3
3003 STA 0(5:5)
3004 ENT1 1
3005 ENT2 3999
3006 MOVE 0(63)
3007 DEC2 63
3008 J2P 3006
3009 INC2 63
3010 ST2 3011(4:4)
3011 MOVE 0
3012 HLT 0
或者更进一步的,将程序优化如下:
3000 ENT1 0
3001 MOVE 3004 → 等效于将字节表征进行复制操作
3002 MOVE 0(43) → 3999 = 43*93
3003 JMP 3002
3004 HLT 0
20) 请对以下的两个问题进行解答:a) 跳转类寄存器rJ的值可能为0吗? b) 写一个程序,已知标定寄存器rI4的内容为N,将寄存器rJ的内容设为与rI4一致,即N,其中\(0 \lt N \le 3000\)。书写的程序应该满足两个边界条件:1、程序开始的地址为3000;2、程序执行完成后,所有的存储器中的内容均应保持不变
a) 根据书中的基本定义:J类寄存器中总是储存着紧接着跳转寄存器的指令对应的地址,因此J类寄存器所能存储的最小的地址为1,无法达到0
b) 在书写程序前,首先梳理下基本思路:
- 如何解决不可调和的矛盾:程序开始地址为3000,rJ的内容必然只能在3000以后,而题干中要求内容范围在3000以内?
- 如果中途需要变更存储器中的内容,变更后怎么进行还原?
初步书写程序如下:
3000 DEC4 1 --- 标定寄存器rI4的内容不断进行更新
3001 J4P 3000 --- 不断执行跳转命令,共执行N次
3002 STJ