Nand2Tetris Chapter7

2022. 10. 21. 23:10Computer Science/CPU

Nand2Tetris :  https://www.coursera.org/learn/build-a-computer

 

Build a Modern Computer from First Principles: From Nand to Tetris (Project-Centered Course)

히브리대학교에서 제공합니다. What you’ll achieve: In this project-centered course* you will build a modern computer system, from the ground up. We’ll divide ... 무료로 등록하십시오.

www.coursera.org


Chapter 7

Virtual Machine 1: Stack Arithmetic

Chapter 7에서는 VM-on-Hack translator를 제작한다(산술 연산 부분만). 나는 Python을 이용하여 제작했다


fileName = 'StaticTest'
inputFileName = fileName + '.vm'
outputFileName = fileName + '.asm'

COMMANDTYPE = {
    'C_ARITHMETIC':1,
    'C_PUSH':2,
    'C_POP':3,
    'C_LABEL':4,
    'C_GOTO':5,
    'C_IT':6,
    'C_FUNCTION':7,
    'C_RETURN':8,
    'C_CALL':9
}

ARITHMETIC_LOGICAL_CAMMANDS = ['add','sub','neg','eq','gt','lt','and','or','not']

# parses each VM command into its lexical elements
class Parser :
    def __init__(self) :
        self.line = ''      # 현재의 line
        self.type = 0       # line의 종류 (in COMMANDTYPE)
        self.arg1 = ''      # 파라미터1 (연산자의 유형 / 메모리 종류)
        self.arg2 = 0       # 파라미터2 (메모리 위치)
        self.f = open(inputFileName, 'r')

    def __del__(self) :
        self.f.close()

    def readOneLine(self) :
        tmpLine = self.f.readline()
        if not tmpLine :
            return -1

        tmpLine = tmpLine.split('/', maxsplit=2)[0]    # 주석제거
        tmpLine = tmpLine.strip()                      # 앞 뒤 공백 제거
        if len(tmpLine) == 0 :                         # 빈 문자열 제거
            return self.readOneLine()
            
        print(tmpLine)                                  # line 출력
        self.line = tmpLine                         
        return 0             

    def parser(self) :
        if self.readOneLine() == -1:
            return -1

        lineDetail = self.line.split(' ', maxsplit=3)
        if lineDetail[0] in ARITHMETIC_LOGICAL_CAMMANDS :
            self.type = COMMANDTYPE['C_ARITHMETIC']
            self.arg1 = lineDetail[0]
        elif lineDetail[0] == 'push' :
            self.type = COMMANDTYPE['C_PUSH']
            self.arg1 = lineDetail[1]
            self.arg2 = lineDetail[2]
        elif lineDetail[0] == 'pop' :
            self.type = COMMANDTYPE['C_POP']
            self.arg1 = lineDetail[1]
            self.arg2 = lineDetail[2]

        

# writes the assembly code that implements the parsed command 
class CodeWriter :
    def __init__(self) :
        self.BASEADDTABLE = {
            'local':'LCL',
            'argument':'ARG',
            'this':'THIS',
            'that':'THAT'
        }
        self.setBranchIndex = 0
        self.f = open(outputFileName, 'w')

    def __del__(self) :
        self.f.close()

    def writeArithmetic(self, command) :

        hackCode = f"// {command}\n"

        if command == 'neg' or command == 'not':
            hackCode += f'@SP\n'
            hackCode += f'A=M-1\n'
            if command == 'neg' :
                hackCode += f'M=-M\n'
            elif command == 'not' :
                hackCode += f'M=!M\n'

        else :
            hackCode += f'@SP\n'    
            hackCode += f'A=M-1\n'  
            hackCode += f'D=M\n'      
            hackCode += f'A=A-1\n' 

            if command == 'add' :   
                hackCode += f'M=D+M\n'
            elif command == 'sub' :
                hackCode += f'M=M-D\n'
            elif command == 'and' :
                hackCode += f'M=D&M\n'
            elif command == 'or' :
                hackCode += f'M=D|M\n'
            else :
                hackCode += f'D=M-D\n'
                hackCode += f'@SETTRUE{self.setBranchIndex}\n'
                if command == 'eq' :
                    hackCode += f'D;JEQ\n'
                elif command == 'gt' :
                    hackCode += f'D;JGT\n'
                elif command == 'lt' :
                    hackCode += f'D;JLT\n'
                hackCode += f'@SP\n'
                hackCode += f'A=M-1\n'
                hackCode += f'A=A-1\n'
                hackCode += f'M=0\n'
                hackCode += f'@END{self.setBranchIndex}\n'
                hackCode += f'0;JMP\n'
                hackCode += f'(SETTRUE{self.setBranchIndex})\n'
                hackCode += f'@SP\n'
                hackCode += f'A=M-1\n'
                hackCode += f'A=A-1\n'
                hackCode += f'M=-1\n'
                hackCode += f'@END{self.setBranchIndex}\n'
                hackCode += f'0;JMP\n'
                hackCode += f'(END{self.setBranchIndex})\n'
                self.setBranchIndex += 1

            hackCode += f'@SP\n'
            hackCode += f'M=M-1\n'

        self.f.write(hackCode)
    
    def writePushPop(self, commandType, segment, index) :

        hackCode = f"// {('push' if commandType == 2 else 'pop')} {segment} {index}\n"

        if commandType == COMMANDTYPE['C_PUSH'] :
            hackCode += f'// get {segment} {index}\n'
            if segment == 'constant' :                  # constant
                hackCode += f'@{str(index)}\n'
                hackCode += f'D=A\n'
            elif segment in self.BASEADDTABLE.keys() :  # local, argument, this, that
                hackCode += f'@{index}\n'
                hackCode += f'D=A\n' 
                hackCode += f'@{self.BASEADDTABLE[segment]}\n'
                hackCode += f'A=D+M\n'
                hackCode += f'D=M\n'
            elif segment == 'temp' :                    # temp
                hackCode += f'@{index}\n'
                hackCode += f'D=A\n' 
                hackCode += f'@5\n'
                hackCode += f'A=D+A\n'
                hackCode += f'D=M\n'
            elif segment == 'pointer' :                 # pointer
                hackCode += f'@{index}\n'
                hackCode += f'D=A\n' 
                hackCode += f'@3\n'
                hackCode += f'A=D+A\n'
                hackCode += f'D=M\n'        
            elif segment == 'static' :
                hackCode += f'@{fileName}.{index}\n'
                hackCode += f'D=M\n'

            hackCode += '// push to stack\n'
            hackCode += '@SP\n'
            hackCode += 'A=M\n'
            hackCode += 'M=D\n'
            hackCode += '@SP\n'
            hackCode += 'M=M+1\n'

        elif commandType == COMMANDTYPE['C_POP'] :
            hackCode += '// pop from stack\n'
            hackCode += '@SP\n'
            hackCode += 'M=M-1\n'
            hackCode += '@SP\n'
            hackCode += 'A=M\n'
            hackCode += 'D=M\n'
            hackCode += f'@popTmp\n'    # popTmp1: 스택의 값 저장
            hackCode += f'M=D\n'

            hackCode += f'// put {segment} {index}\n'
            if segment in self.BASEADDTABLE.keys() :    # local, argument, this, that
                hackCode += f'@{index}\n'
                hackCode += f'D=A\n' 
                hackCode += f'@{self.BASEADDTABLE[segment]}\n'
                hackCode += f'D=D+M\n'
            elif segment == 'temp' :                    # temp
                hackCode += f'@{index}\n'
                hackCode += f'D=A\n' 
                hackCode += f'@5\n'
                hackCode += f'D=D+A\n'
            elif segment == 'pointer' :                 # pointer
                hackCode += f'@{index}\n'
                hackCode += f'D=A\n' 
                hackCode += f'@3\n'
                hackCode += f'D=D+A\n'
            elif segment == 'static' :                  # static
                hackCode += f'@{fileName}.{index}\n'
                hackCode += f'D=A\n' 

            hackCode += f'@popTmp2\n'   # popTmp2: 저장 목표 장소의 주소 저장
            hackCode += f'M=D\n'

            hackCode += f'@popTmp\n'
            hackCode += f'D=M\n'

            hackCode += f'@popTmp2\n'
            hackCode += f'A=M\n'
            hackCode += f'M=D\n'


        self.f.write(hackCode)

# drives the process
def main() :
    parser = Parser()
    codeWriter = CodeWriter()
    while True :
        if parser.parser() == -1:
            break
        if parser.type == COMMANDTYPE['C_ARITHMETIC'] :
            codeWriter.writeArithmetic(parser.arg1)
        elif parser.type == COMMANDTYPE['C_POP'] \
            or parser.type == COMMANDTYPE['C_PUSH'] :
            codeWriter.writePushPop(parser.type, parser.arg1, parser.arg2)
        
        print(f'종류: {parser.type}, 파라미터 {parser.arg1} {parser.arg2}')
            
if __name__=="__main__":
    main()

'Computer Science > CPU' 카테고리의 다른 글

Nand2Tetris Chapter9  (0) 2022.10.26
Nand2Tetris Chapter8  (0) 2022.10.21
Nand2Tetris Chapter6  (0) 2022.10.10
Nand2Tetris Chapter5  (0) 2022.09.22
Nand2Tetris Chapter4  (0) 2022.09.20