博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
计算机系统要素 C4
阅读量:6888 次
发布时间:2019-06-27

本文共 2644 字,大约阅读时间需要 8 分钟。

hot3.png

存储

D、A、M

Hack 机器包含两个16位寄存器——D 和 A,分别是 Data 和 Address 的缩写。他们本质上是一样的,用途的不同是分配出来的。

如当访问 M 这个符号的时候,总是以 A 寄存器的值作为地址去操作内存。即 M 解释为 Memory[A]

又如 goto 指令,跳转到的位置也是指令存储器的 A 地址,即 goto 跳到 InstructionMemory[A]

最后,A 寄存器也可以像 D 寄存器一样用于存储数据值,而到底它存的是什么含义,其实取决于上下文的解释。

指令

从寄存器 A 的用法上可以看出,Hack 程序的很大一部分指令应该都是在操作地址。写一个地址,操作一下,写一个地址,操作一下这样。因此他的指令也分为两类:

A 指令

A 指令的第一位是 0,后面跟 15 位值。表示将 A 寄存器设为这个值。

它的意义首先是允许了输入常数(15位),这是在将 A 寄存器的值做数字处理的时候;其次为操作内存、跳转指令 提供地址。

因此也可以推导 Hack 机器的内存地址空间是 15 位的。

C 指令

C 指令第一位是 1,之后两位没有使用。后面的 13 位被分为三个域:7 位的计算域(Comp)、3 位的目标域(Dest)、3 位的跳转域(Jump)。

为什么空出两位没有使用,而目标域和跳转域都是 3 位呢?

目标域是因为 C 指令操作的存储对象只有三个:A 、D 、M。每个对象都需要表达 “存入” 或 “不存入” 两种选择,因此需要三个位的域宽。

因为计数器的存在,程序的默认执行流程是顺序执行的,即执行完 ROM[N] 后执行 ROM[N+1]。跳转域需要表达下一步指令是否需要跳转的问题,且跳转的本质是实现流程控制,故该域还需要能够对计算域输出的值做出反应。计算域的输出值范围有 16 位宽,判断条件则是一个确定的数,比如 10086。那么输出值和判断条件之间的关系就应该定义三种:小于、等于和大于。这三种关系的输出值之间互相独立,故每种都需要表达 “跳” 或 “不跳” 两种选择,因此需要三个位的域宽。

另外两点:1. 至于说输出值和判断条件之间的关系为什么定义三种,是出于效率考虑的,两个数字之间的关系也可以表达为 两种:等于或不等于。但这样在做大于或小于判断的时候就会非常麻烦。 2. 因为指令已经没有空间存放判断条件的比较数,因此跳转域实际使用的条件值是 0,即你需要先减一次比较数。

计算域有 7 位,用于控制 ALU。记得 A 寄存器有两种意义吗,它既可以解释为值,也可以解释为地址。这个解释操作其实就是用计算域的第一位控制的,通常管他叫 a-bits,为 0 时表示输入值,为 1 时表示输入 M[A] 的值。剩余 6 为称为 c-bits ,用于控制 ALU 函数的选择。具体对应需要参考表 4-3。但如果你看的是中文版,注意该表右半部助记符有个错误,应该是 (当a=1)。

说到勘误,4.1.1 的寄存器段还有一个错误,寄存器的宽度与内存相等,都是 16 位。因此那里应该是 “只存储 1 个值”,而不是书中的 “只存储 1 位”.以及本章错误真鸡儿多,感觉跟前三章不是同一个人翻的。

汇编

本章和附录都没有一个很完善的汇编参考或教程,因此在做题的时候虽然思路是有的但不知道应该怎么写,或者不应该怎么写。于是把章内的 sum(1..100) 程序抄了一遍,并且用编译器和CPU模拟器尝试跑了一下。

原始的 asm 是这样的:(OSC 的 markdown 渲染有点傻逼)

[@i](https://my.oschina.net/izhuchao)		// i refers to some mem. location    M=1    [@sum](https://my.oschina.net/Asum)    M=0(LOOP)    [@i](https://my.oschina.net/izhuchao)    D=M    [@100](https://my.oschina.net/laoka)    D=D-A    [@END](https://my.oschina.net/u/567204)    D;JGT		// if (i-100)>0 goto END    @i    D=M    @sum    M=D+M    @i    M=M+1    @LOOP    0;JMP		// goto LOOP(END)    @END    0;JMP		// infinit loop

我疑惑的点在于,这些 isum 啊的变量到底是怎么分配内存的?还有 (LOOP) 这种标记算什么?为什么有缩进,他们有具体的含义吗?当把它编译之后,得到的文件是这样的:

0   @161   M=12   @173   M=04   @165   D=M6   @1007   D=D-A8   @189   D;JGT10  @1611  D=M12  @1713  M=D+M14  @1615  M=M+116  @417  0;JMP18  @1819  0;JMP

程序自动为 i 分配了 16(10000),而 sum 分配了 17(10001)。之后不论我怎么修改程序,发现总是按顺序从 16 开始赋值,即使你的程序写成有冲突的样子,比如:

@aM=1@16M=2

它依然会编译成:

0   @161   M=02   @163   M=1

这个故事告诉我们,不要随便硬编码内存地址,尽量都通过符号来分配。

后记:眼瞎如我,其实有一段(4.2.4)讲解符号的。

套路

把题做完后总结了一些套路:

  1. 所有变量存在内存里,使用符号指定
  2. 只有一个A寄存器,它不能既用来存地址,又用来存值。这意味着:当使用跳转命令时,A 的值只能是跳转的目标地址,因此表达式只能是 D 或常数。即当使用 C 指令时,总是要先把值存入 D 或使用常数。
  3. 流程控制全靠跳转,跳转全靠比较。看到有流控的需求的时候,第一反应把程序分几段。
  4. 因为 hack 机器只有一个 D 和 一个 A 寄存器,写汇编就像玩汉诺塔,总想着怎么倒数据
  5. 将 M 的值赋给 A 要使用 C 指令而不是 A 指令,即 A=M, 而不是 @A

转载于:https://my.oschina.net/lionets/blog/2906856

你可能感兴趣的文章
centos7安装使用samba服务器免密码登录简单配置
查看>>
mysql中if-elesif-endif使用
查看>>
drbd状态信息详细说明
查看>>
apache详解
查看>>
PHP技巧分享:7个非常适合初学者使用的实用PHP方法
查看>>
深入浅出处理器(中)_中断
查看>>
闲聊 -软路由的安装
查看>>
ubuntu安装最新docker
查看>>
quartz任务调度整合springMVC学习一
查看>>
Keepalived双主模型实现nginx负载均衡
查看>>
虚拟用户邮件传输
查看>>
查看oracle字符集 版本
查看>>
Linux常用命令——ln
查看>>
记下每一个心灵的瞬间
查看>>
TeamViewer安装企业版以后无法安装个人版的解决办法?
查看>>
决心书
查看>>
python入门:字典
查看>>
面向对象的程序设计-原型模式
查看>>
HTML5+NodeJs实现WebSocket即时通讯
查看>>
ConcurrentDictionary线程不安全么
查看>>