第八章:虚拟机2:程序控制

目标:

扩展vm翻译器,增加语言的程序控制流和函数调用命令。


思路:

本质是将.vm文本按照特定的规则转换为.asm文本,先将两种语言的命令归类:

vm:     算数逻辑      push/pop    跳转(label, goto, if-goto)      函数(function,  return, call)

                                                                 ↓

asm:    A命令         C命令        L命令(label)


在动手之前:

可以使用内置的vm模拟器,观察内存的分配;

也可以将vm先翻译成asm文件,再进行对比观察映射规则,最后自己实现。


这一章没有自己实现下面的代码来自:https://github.com/ThomasCJY/The_Elements_of_Computing_Systems


CodeWriter.py

#!/usr/bin/python
import sys,os
import Parser
CODEFLAG1=0
CODEFLAG2=0
RETURNFLAG=1

def setFileName(filename):
	filetuple=os.path.splitext(filename)
	wfile = open(filetuple[0]+'.asm','w')
	return wfile

def writeArithmatic(wfile,command):
	global CODEFLAG1,CODEFLAG2
	if command == 'add':
		wfile.write('@SP\nM=M-1\nA=M\nD=M\n@SP\nM=M-1\nA=M\nM=D+M\n@SP\nM=M+1\n')
	elif command == 'sub':
		wfile.write('@SP\nM=M-1\nA=M\nD=M\n@SP\nM=M-1\nA=M\nM=M-D\n@SP\nM=M+1\n')
	elif command == 'neg':
		wfile.write('@SP\nM=M-1\nA=M\nM=-M\n@SP\nM=M+1\n')
	elif command == 'and':
		wfile.write('@SP\nM=M-1\nA=M\nD=M\n@SP\nM=M-1\nA=M\nM=D&M\n@SP\nM=M+1\n')
	elif command == 'or':
		wfile.write('@SP\nM=M-1\nA=M\nD=M\n@SP\nM=M-1\nA=M\nM=D|M\n@SP\nM=M+1\n')
	elif command == 'not':
		wfile.write('@SP\nM=M-1\nA=M\nM=!M\n@SP\nM=M+1\n')
	elif command == 'eq':
		wfile.write('@SP\nM=M-1\nA=M\nD=M\n@SP\nM=M-1\nA=M\nD=D-M\n@RET_TRUE'+str(CODEFLAG1)+'\
			\nD;JEQ\nD=0\n@CONTINUE'+str(CODEFLAG2)+'\n0;JMP\n(RET_TRUE'+str(CODEFLAG1)+')\nD=-1\
			\n(CONTINUE'+str(CODEFLAG2)+')\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
		CODEFLAG1+=1
		CODEFLAG2+=1
	elif command == 'gt':
		wfile.write('@SP\nM=M-1\nA=M\nD=M\n@SP\nM=M-1\nA=M\nD=M-D\n@RET_TRUE'+str(CODEFLAG1)+'\
			\nD;JGT\nD=0\n@CONTINUE'+str(CODEFLAG2)+'\n0;JMP\n(RET_TRUE'+str(CODEFLAG1)+')\nD=-1\
			\n(CONTINUE'+str(CODEFLAG2)+')\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
		CODEFLAG1+=1
		CODEFLAG2+=1
	elif command == 'lt':
		wfile.write('@SP\nM=M-1\nA=M\nD=M\n@SP\nM=M-1\nA=M\nD=M-D\n@RET_TRUE'+str(CODEFLAG1)+'\
			\nD;JLT\nD=0\n@CONTINUE'+str(CODEFLAG2)+'\n0;JMP\n(RET_TRUE'+str(CODEFLAG1)+')\nD=-1\
			\n(CONTINUE'+str(CODEFLAG2)+')\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
		CODEFLAG1+=1
		CODEFLAG2+=1


def writePushPop(wfile,command,segment,index,filename):
	if command == 'C_PUSH':
		if segment == 'constant':
			wfile.write('@'+index+'\nD=A\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
		elif segment == 'local':
			wfile.write('@LCL\nD=M\n@'+index+'\nA=A+D\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
		elif segment == 'argument':
			wfile.write('@ARG\nD=M\n@'+index+'\nA=A+D\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
		elif segment == 'this':
			wfile.write('@THIS\nD=M\n@'+index+'\nA=A+D\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
		elif segment == 'that':
			wfile.write('@THAT\nD=M\n@'+index+'\nA=A+D\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
		elif segment == 'pointer':
			if index == '0':
				wfile.write('@3\n')
			elif index == '1':
				wfile.write('@4\n')
			wfile.write('D=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
		elif segment == 'static':
			staticname=filename.strip('.vm')+'.'+index
			wfile.write('@'+staticname+'\nD=A\nA=D\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n')
	elif command =='C_POP': 
		if segment == 'local':
			wfile.write('@SP\nM=M-1\nA=M\nD=M\n@LCL\nA=M\n')
			for i in range (0,int(index)):
				wfile.write('A=A+1\n')
			wfile.write('M=D\n')
		if segment =='argument':
			wfile.write('@SP\nM=M-1\nA=M\nD=M\n@ARG\nA=M\n')
			for i in range (0,int(index)):
				wfile.write('A=A+1\n')
			wfile.write('M=D\n')
		if segment == 'this':
			wfile.write('@SP\nM=M-1\nA=M\nD=M\n@THIS\nA=M\n')
			for i in range (0,int(index)):
				wfile.write('A=A+1\n')
			wfile.write('M=D\n')
		if segment == 'that':
			wfile.write('@SP\nM=M-1\nA=M\nD=M\n@THAT\nA=M\n')
			for i in range (0,int(index)):
				wfile.write('A=A+1\n')
			wfile.write('M=D\n')
		if segment == 'pointer':
			wfile.write('@SP\nM=M-1\nA=M\nD=M\n')
			if index == '0':
				wfile.write('@3\n')
			else:
				wfile.write('@4\n')
			wfile.write('M=D\n')
		if segment == 'static':
			staticname=filename.strip('.vm')+'.'+index
			wfile.write('@SP\nM=M-1\nA=M\nD=M\n@'+staticname+'\nM=D\n')
		if segment == 'temp':
			wfile.write('@SP\nM=M-1\nA=M\nD=M\n@R5\n')
			for i in range (0,int(index)):
				wfile.write('A=A+1\n')
			wfile.write('M=D\n')

def writeLabel(wfile,labelstring):
	wfile.write('('+labelstring+')\n')

def writeGoto(wfile,labelstring):
	wfile.write('@'+labelstring+'\n0;JMP\n')

def writeIf(wfile,labelstring):
	wfile.write('@SP\nM=M-1\nA=M\nD=M\n@'+labelstring+'\nD;JNE\n')

def writeFunction(wfile,functionName,numlocals):
	wfile.write('('+functionName+')\n@LCL\nD=M\n@SP\nM=D\n')
	for i in range(0,int(numlocals)):
		wfile.write('@SP\nA=M\nM=0\nD=A+1\n@SP\nM=D\n')

def writeCall(wfile,functionName,numArgs):
	global RETURNFLAG
	wfile.write('@return_address'+str(RETURNFLAG)+'\nD=A\n@SP\nA=M\nM=D\n@SP\nM=M+1\
		\n@LCL\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n@ARG\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\
		\n@THIS\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n@THAT\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\
		\n@'+numArgs+'\nD=A\n@5\nD=D+A\n@SP\nD=M-D\n@ARG\nM=D\n@SP\nD=M\n@LCL\nM=D\
		\n@'+functionName+'\n0;JMP\n(return_address'+str(RETURNFLAG)+')\n')
	RETURNFLAG+=1

def writeReturn(wfile):
	wfile.write('@LCL\nD=M\n@R13\nM=D\n@5\nD=D-A\nA=D\nD=M\
		\n@R14\nM=D\n@SP\nM=M-1\nA=M\nD=M\n@ARG\nA=M\
		\nM=D\n@ARG\nD=M+1\n@SP\nM=D\n@R13\nD=M\nD=D-1\
		\nA=D\nD=M\n@THAT\nM=D\n@R13\nD=M\nD=D-1\nD=D-1\
		\nA=D\nD=M\n@THIS\nM=D\n@R13\nD=M\nD=D-1\nD=D-1\
		\nD=D-1\nA=D\nD=M\n@ARG\nM=D\n@R13\nD=M\nD=D-1\
		\nD=D-1\nD=D-1\nD=D-1\nA=D\nD=M\n@LCL\nM=D\n@R14\nA=M\n0;JMP\n')


def Close(wfile):
	wfile.close()


Parser.py:

#!/usr/bin/python
Arith=('add','sub','neg','eq','gt','lt','and','or','not')

def hasMoreCommands(line):
	if not line:
		return 0
	else:
		return 1

def advance(rfile):
	line=rfile.readline()
	return line

def commandType(line):
	if line.find('push')>=0:
		return 'C_PUSH'
	elif line.find('pop')>=0:
		return 'C_POP'
	elif line.startswith('label'):
		return 'C_LABEL'
	elif line.startswith('goto'):
		return 'C_GOTO'
	elif line.startswith('if-goto'):
		return 'C_IF'
	elif line.strip() in Arith:
		return 'C_ARITHMATIC'
	elif line.startswith('function'):
		return 'C_FUNCTION'
	elif line.startswith('return'):
		return 'C_RETURN'
	elif line.startswith('call'):
		return 'C_CALL'

def arg1(line):
	if commandType(line) is 'C_ARITHMATIC':
		return line.strip()
	else:
		spline=line.split(' ')
		return spline[1]

def arg2(line):
	if commandType(line) in ('C_POP','C_PUSH','C_FUNCTION','C_CALL'):
		spline=line.split(' ')
		return spline[2]

Vmtranslator.py:

import sys,os
import Parser
import CodeWriter
DELETEFLAG=0

def Main(rfile,wfile,filename):
	line=Parser.advance(rfile)
	flag=Parser.hasMoreCommands(line)
	while flag:
		while line == '\n' or line.startswith('//'):
			line=rfile.readline()
		if '//' in line:
			line=line[:line.find('//')]
		ctype=Parser.commandType(line)
		if ctype == 'C_ARITHMATIC':
			attribute1=Parser.arg1(line).strip()
			CodeWriter.writeArithmatic(wfile,attribute1)
		elif ctype in ('C_PUSH','C_POP'):
			attribute1=Parser.arg1(line).strip()
			attribute2=Parser.arg2(line).strip()
			CodeWriter.writePushPop(wfile,ctype,attribute1,attribute2,filename)
		elif ctype =='C_LABEL':
			attribute1=Parser.arg1(line).strip()
			CodeWriter.writeLabel(wfile,attribute1)
		elif ctype =='C_GOTO':
			attribute1=Parser.arg1(line).strip()
			CodeWriter.writeGoto(wfile,attribute1)
		elif ctype =='C_IF':
			attribute1=Parser.arg1(line).strip()
			CodeWriter.writeIf(wfile,attribute1)	
		elif ctype =='C_FUNCTION':
			attribute1=Parser.arg1(line).strip()
			attribute2=Parser.arg2(line).strip()
			CodeWriter.writeFunction(wfile,attribute1,attribute2)
		elif ctype =='C_RETURN':
			CodeWriter.writeReturn(wfile)
		elif ctype =='C_CALL':
			attribute1=Parser.arg1(line).strip()
			attribute2=Parser.arg2(line).strip()
			CodeWriter.writeCall(wfile,attribute1,attribute2)		
		line=Parser.advance(rfile)
		flag=Parser.hasMoreCommands(line)





filename=sys.argv[1]
#if filename exists in the dir, open the file directly
if os.path.isfile(filename):
	rfile = open(filename,'r')
	wfile = CodeWriter.setFileName(filename)
	wfile.write('@256\nD=A\n@SP\nM=D\n')
	Main(rfile,wfile,filename)
#if filename doesn't exist, find all the .vm files
elif os.path.isfile('Sys.vm'):
	wfile = CodeWriter.setFileName(filename)
	wfile.write('@256\nD=A\n@SP\nM=D\n')
	wfile.write('@return_address0\nD=A\n@SP\nA=M\nM=D\n@SP\nM=M+1\
		\n@LCL\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n@ARG\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\
		\n@THIS\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\n@THAT\nD=M\n@SP\nA=M\nM=D\n@SP\nM=M+1\
		\n@0\nD=A\n@5\nD=D+A\n@SP\nD=M-D\n@ARG\nM=D\n@SP\nD=M\n@LCL\nM=D\
		\n@Sys.init\n0;JMP\n(return_address0)\n')
	#check all the files in the dir
	for i in os.walk(os.getcwd()):
		filelist=i[2]
	for j in range(0,len(filelist)):
		if filelist[j].endswith('.vm'):
			#readfile, copy to the end
			filename=filelist[j]
			rfile = open(filename,'r')
			Main(rfile,wfile,filename)
	DELETEFLAG=1
else:
	print 'Wrong Instruction!'
	exit()

rfile.close()
CodeWriter.Close(wfile)

if DELETEFLAG == 1:
	os.remove(filename)



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值