第六章:汇编编译器

目标:开发汇编编译器,将Hack汇编语言编写成的程序翻译成Hach硬件平台能够理解的二进制代码。

分析:本章的本质就是文本处理,将给定的.asm文本根据给定的规则映射为.hack二进制文件。

我们先分析.asm文件,一行可以是一下几种情况:

1)指令:又分为A-指令,C-指令。

2)常数和符号:常数还好解决,用户自定义的符号,还得为其分配内存。

3)注释:以"//"开头的被认为是注释,忽略。

4)空行:忽略。

书中为了降低难度先要求实现一个无符号版的,这也是一种很好的思维方法,将复杂的问题,转换为简单的已知的问题。

书中也给出了模块的API,既然是文本处理,语言自然就是python啦。

实现:

Paser模块:

<span style="font-size:12px;">def hasMoreCommand(line):
	if not line:
		return 0
	return 1

def advance(fl):			#return next line
	line = fl.readline()
	return line

def clear(line):
	index = line.find('//')
	if index > 0:	#remove the gloss
		line = line[0:index]
	return line.strip('\n ') 		#remove the blank then return

def commandType(line):
	if line.find('(') > -1:
		return 'L_COMMAND'
	elif line.find('@') > -1:
		return 'A_COMMAND'
	else:
		return 'C_COMMAND'

def symbol(line):
	line = line.strip(' ()@\n')
	return line

def dest(line):
	if line.find(";") > 0:		#deal with the jump command
		return 'null'
	index = line.find('=')
	return line[0:index].strip()

def comp(line):
	index = line.find(';')
	if index > 0:
		return line[0:index].strip()
	idx = line.find('=')
	return line[idx + 1:].strip()</span>

Code模块:

该模块,负责解析C-命令,返回comp,dest,jump域的二进制代码。

<span style="font-size:12px;">import Parser

def dest(line):
	destDict = {'null':'000', 'M':'001', 'D':'010',
				'MD':'011', 'A':'100', 'AM':'101',
				'AD':'110', 'AMD':'111'}
	return destDict[Parser.dest(line)]

def comp(line):
	compDict = {'0':'0101010', '1':'0111111', '-1':'0111010',
				'D':'0001100', 'A':'0110000', '!D':'001101',
				'!A':'0110001', '-D':'0001111', '-A':'0110011',
				'D+1':'0011111', 'A+1':'0110111', 'D-1':'0001110',
				'A-1':'0110010', 'D+A':'0000010', 'D-A':'0010011',
				'A-D':'0000111', 'D&A':'0000000', 'D|A':'0010101',
				'M':'1110000', '!M':'1110001', '-M':'1110011',
				'M+1':'1110111', 'M-1':'1110010', 'D+M':'1000010',
				'D-M':'1010011', 'M-D':'1000111', 'D&M':'1000000',
				'D|M':'1010101'}
	return compDict[Parser.comp(line)]

def jump(line):
	jumpDict = {'null':'000', 'JGT':'001', 'JEQ':'010',
				'JGE':'011', 'JLT':'100', 'JNE':'101',
				'JLE':'110', 'JMP':'111'}
	return jumpDict[Parser.jump(line)]</span>

assembler模块:

该模块实现无符号的汇编编译器,默认输出prog.hack文件。

<span style="font-size:12px;">import sys
import Parser
import Code

fileName = sys.argv[1]
rfile = open(fileName, 'r')
wfile = open('prog.hack', 'w')

line = rfile.readline()
flag = Parser.hasMoreCommand(line)

while flag:
	while line.startswith('\n') or line.startswith('//'):			#skip the gloss line and null string
		line = Parser.advance(rfile)
		flag = Parser.hasMoreCommand(line)

	line = Parser.clear(line)												#remove gloss and '\n'

	cType = Parser.commandType(line)

	if cType == 'L_COMMAND':
		LString = Parser.symbol(line)
		wfile.write('0' + LString + '\n')
	elif cType == 'A_COMMAND':
		AD = Parser.symbol(line)
		AB = bin(int(AD))[2:]
		AString = AB.zfill(15)
		wfile.write('0' + AString + '\n')
	elif cType == 'C_COMMAND':
		destString = Code.dest(line)
		compString = Code.comp(line)
		jumpString = Code.jump(line)
		wfile.write("111" + compString + destString + jumpString + "\n")

	line = Parser.advance(rfile)
	flag = Parser.hasMoreCommand(line)</span>

以下实现有符号版的:

遇到的问题:汇编程序允许在符号被定义之前使用该符号标签。

为了解决这个问题,需要读两遍文本,第一遍遍历时每遇到一条伪指令时,就在符号表上加一条该符号到下一条指令地址的映射,第二次遍历时,每次遇到符号化的A-指令时,

就查找符号表,若不存在,则在符号表中增加该符号到可用的RAM地址。改地址从16开始。

其他都和无符号版的差不多,只是增加了SymbolTable模块。

SymbolTable:

<span style="font-size:12px;">def Constructor():
	symbolDict = {'SP':0, 'LCL':1, 'ARG':2, 'THIS':3,
					'THAT':4, 'R0':0, 'R1':1, 'R2':2,
					'R3':3, 'R4':4, 'R5':5, 'R6':6,
					'R7':7, 'R8':8, 'R9':9, 'R10':10,
					'R11':11, 'R12':12, 'R13':13, 'R14':14,
					'R15':15, 'SCREEN':16384, 'KBD':24576}
	return symbolDict

def addEntry(symbolDict, symbol, address):
	symbolDict[symbol] = address

def contains(symbolDict, symbol):
	return symbolDict.has_key(symbol)

def GetAddress(symbolDict, symbol):
	return symbolDict[symbol]</span>

assembler:

<span style="font-size:12px;">import sys
import Parser
import Code
import SymbolTable

fileName = sys.argv[1]
rfile = open(fileName, 'r')
wfile = open('prog.hack', 'w')

smtbl = SymbolTable.Constructor()

line = rfile.readline()
flag = Parser.hasMoreCommand(line)
counter = 0

while flag:								#first loop complete the symboltable
	while line.startswith('\n') or line.startswith('//'):
		line = Parser.advance(rfile)
		flag = Parser.hasMoreCommand(line)

	line = Parser.clear(line)
	cType = Parser.commandType(line)
	if cType == 'L_COMMAND':
		sym = Parser.symbol(line)
		SymbolTable.addEntry(smtbl, sym, counter)
	else:
		counter += 1
	line = Parser.advance(rfile)			#read next line
	flag = Parser.hasMoreCommand(line)		#qualify this line

rfile.close()

rfile = open(fileName)
line = rfile.readline()
flag = Parser.hasMoreCommand(line)
j = 16								#remember the number of the memory address

while flag:							#second loop
	while line.startswith('\n') or line.startswith('//'):			#skip the gloss line and null string
		line = Parser.advance(rfile)
		flag = Parser.hasMoreCommand(line)

	line = Parser.clear(line)												#remove gloss and '\n'

	cType = Parser.commandType(line)

	if cType == 'L_COMMAND':
			pass
	elif cType == 'A_COMMAND':
		Acmd = Parser.symbol(line)
		if Acmd.isdigit():								#Acmd is digit
			bn = bin(int(Acmd))[2:]
		elif SymbolTable.contains(smtbl, Acmd):			#Acmd is not digit but in Symboltable
			dec = SymbolTable.GetAddress(smtbl, Acmd)
			bn = bin(dec)[2:]
		else:											#Acmd is a symbol that not exist in Symboltable
			bn = bin(j)[2:]
			SymbolTable.addEntry(smtbl, Acmd, j)
			j += 1
		AString = bn.zfill(15)
		wfile.write('0' + AString + '\n')
	elif cType == 'C_COMMAND':
		destString = Code.dest(line)
		compString = Code.comp(line)
		jumpString = Code.jump(line)
		wfile.write("111" + compString + destString + jumpString + "\n")

	line = Parser.advance(rfile)
	flag = Parser.hasMoreCommand(line)</span>

其他两个模块和无符号的一致。

至此我们的汇编编译器就完成啦!

时间: 2024-10-11 21:57:30

第六章:汇编编译器的相关文章

第六章--爆破软件

爆破其实很简单,最起码比你能一下把你家的牙膏给全挤出来要容易多了.你只要先到大街上买几根雷管,然后放到你的显示器上再点着就OK了(不难吧,记的点着后跑远点儿) 爆破的原理我也说过了,相信你很容易就能理解了.我们今天就具体讲一下如何找到那个关键跳转以及如何才能买到即便宜又好用的雷管... 爆 破一个软件一般只需要很少的几个步骤,首先先看一下其有无加壳,有的话是用何工具加的壳,知道了以后用相应的工具将其脱掉或进行手工脱壳,参考以有教程. 接着我们就可以对脱过壳之后的软件来开刀了.你有两种选择,用W3

OpenGL ES着色器语言之语句和结构体(官方文档第六章)内建变量(官方文档第七、八章)

OpenGL ES着色器语言之语句和结构体(官方文档第六章) OpenGL ES着色器语言的程序块基本构成如下: 语句和声明 函数定义 选择(if-else) 迭代(for, while, do-while) 跳跃(discard, return, break, continue) 6.1函数定义   着色器是由一系列全局声明和函数定义组成的.函数声明规范如下: // prototype returnType functionName (type0 arg0, type1 arg1, ...,

(转载)虚幻引擎3--第六章 –函数

第六章 –函数 6.1概述 指南 6.1环境生物, 第一部分:基类声明 指南 6.2 环境生物, 第二部分:类的变量声明 指南 6.3 环境生物,第三部分:渲染及光照组件 指南 6.4 环境生物, 第四部分:碰撞及物理属性 6.2 函数声明 指南 6.5 环境生物, 第五部分: SETRANDDEST() 函数 6.3函数修饰符 Static Native Final Singular NoExport Exec Latent Iterator Simulated Server Client R

c++ primer 5th 笔记:第六章

第六章 笔记 1. 通过调用运算符(call operator)来执行函数.调用运算符的形式是一对圆括号,它作用于一个表达式,该表达式是一个函数或指向函数的指针. 2. 在c++语言中,名字有作用域,对象有生命周期. a. 名字的作用域是程序文本的一部分,名字在其中可见. b. 对象的生命周期是程序执行过程中该对象存在的一段时间. 3. 函数体是一个语句块,块构成一个新的作用域. 4. 形参和函数体内部定义的变量统称为局部变量(local variable). 5. 局部静态对象(local s

20135306 第六章学习总结

第6章存储器层次结构 存储器系统是一个具有不同容量.成本和访问时间的存储设备的层次结构. cPU寄存器保存着最常用的数据. 靠近CPU的小的.快速的高速缓存存储器作为一部分存储在相对慢速的主存储器中数据和指令的缓冲区域. 主存暂时存放存储在容量较大的.慢速磁盘上的数据,而这些磁盘常常又作为存储在通过网络连接的其他机器的磁盘或磁带上的数据的缓冲区域. 6.1 存储技术 6.1.1随机访问存储器 随机访问存储器分为两类-静态和动态的. 静态RAM(SRAM)比动态RAM(DRAM)更快,但也贵很多.

Java核心技术 第六章 接口和内部类

Java核心技术  第六章  接口与内部类 接口: 任何实现Comparable接口的类都需要包含compareTo方法,并且这个方法的参数必须是一个Object对象,返回一个整数数值. 在Java SE 5.0中,Comparable接口已经改进为泛型类型. 接口中所有的方法自动的属于public.因此,在接口中声明方法时,不必提供关键字public. 接口中决不能含有实例域,也不能在接口中实现方法. 要让一个类使用排序服务,必须让它实现compareTo方法,因此必须实现Comparable

[Effective Java]第六章 枚举和注解

第六章      枚举和注解 30.      用enum代替int常量 枚举类型是指由一组固定的常量组成合法值的类型,例如一年中的季节或一副牌中的花色.在没引入枚举时,一般是声明一组int常量,每个类型成员一个常量: public static final int APPLE_FUJI = 0; public static final int APPLE_PIPPIN = 1; public static final int APPLE_GRANNY_SMITH = 2; public sta

Thinking In Java笔记(第六章 访问权限控制)

第六章 访问权限控制 简介 Java提供了访问权限修饰词,供类库开发人员向客户端程序员指明哪些是可用的,哪些是不可用的.访问权限控制的等级,从最大权限到最小权限一次为:public.protected.包(library)访问权限(没有关键词)以及private. 6.1 包(library):库单元 包内含有一组类,它们在单一的名字控件之下被组织在一起.例如,在Java的标准发布中有一个工具库,它被组织在java.util名字空间之下.java.util中有一个叫ArrayList的类,使用A

[CSAPP笔记][第六章存储器层次结构]

第六章 存储器层次结构 在简单模型中,存储器系统是一个线性的字节数组,CPU能够在一个常数访问每个存储器位置. 虽然是一个行之有效的模型,但没有反应现代系统实际工作方式. 实际上,存储器系统(memory system)是一个具有不同容量,成本和访问时间的存储设备的层次结构. CPU寄存器保存着最常用的数据.(0周期) 靠近CPU的小的,快速的高速缓存存储器(cache memory)作为一部分存储在相对慢速的主储存器(main memory,简称主存)中的数据和指令的缓冲区.(1~30周期)