Práctica 7: Máquinas Virtuales

Introducción

Una vez construido el ensamblador en la práctica anterior, estamos listos para introducirnos en la virtual machine (VM). La VM nos ayuda traducir un programa de alto nivel ya que antes de que un programa escrito en un lenguaje de alto nivel pueda ejecutarse en una computadora, debe traducirse al lenguaje máquina de la computadora. Esta traducción, conocida como compilación, es un proceso bastante complejo.

Para el proyecto 07 tenemos como objetivo escribir un programa llamado traductor de VM, este traductor deberá ser capaz de traducir los comandos aritméticos y de acceso a la memoria de la VM al lenguaje de máquina hack. Además este traductor debe estar basado en la estructura de datos fundamental, las pilas (stacks).

Por otro lado, para el proyecto 08 nuestro objetivo será ampliar el traductor básico con el flujo del programa (program flow) y la función de llamada de subrutinas (subroutine calling). El resultado es una máquina virtual a gran escala que servirá para los futuros proyectos.

¿Qué hacer para desarrollarlo?

  • El primer paso es conseguir traducir el código de alto nivel en código de máquina virtual por lo que dependemos solo de las especificaciones del lenguaje de alto nivel de origen.

  • El segundo paso es traducir el código de máquina virtual en código ensamblador por lo que acá dependemos solo de las especificaciones del lenguaje de máquina de destino.

  • Finalmente, luego de construir el traductor y de haber traducido el .vm suministrado por nand2tetris obteniendo así el archivo .asm del traductor, comprobaremos si el código generado funciona correctamente, utilizando los archivos .tst y .cmp suministrados para ejecutar el archivo .asm en el emulador de CPU suministrado.

Desarrollo

La imagen de abajo, muestra todas las palabras clave (comandos) que estamos a punto de tratar en el septimo y octavo proyecto.

  • Push command, empujará algunas cosas a la pila, pop moverá algo de la pila a otro lugar. Instrucciones aritméticas como "agregar", "sub"... Son simples porque ya tenemos la ALU. Comparaciones como eq, gt, lt también son simples porque tenemos las instrucciones de salto en nuestro ensamblador.

  • Label, etiqueta la ubicación actual en el código de la función.

  • Goto, efectúa una operación goto incondicional, lo que hace que la ejecución continúe desde la ubicación marcada por la etiqueta. El destino del salto debe estar ubicado en la misma función.

  • If-goto, efectúa una operación goto condicional. Se extrae el valor superior de la pila: si el valor no es cero, la ejecución continúa desde la ubicación marcada por la etiqueta, de lo contrario, la ejecución continúa desde el siguiente comando en el programa. El destino del salto debe estar ubicado en la misma función

  • Function, aquí comienza el código de una función llamada f que tiene n variables locales. Asigna ranuras para guardar LCL, ARG, THIS, THAT (punteros) actuales y también guardar el return_to_address, ademá asigna ranuras para las variables locales y las inicializa todas con el valor de 0.

  • Call f m, indica que la persona que llama ya ha colocado m argumentos en la pila, o sea inserta el "argumentCount" de valores en la pila, y salta a la declaración de la función y comieza a ejecutarse.


Se implementó el traductor de VM utilizando un programa principal (main) y dos módulos: analizador (parser) y escritor de código (code writer).

  • El analizador del módulo (parser), maneja el análisis de un solo archivo .vm y encapsula el acceso al código de entrada. Lee los comandos de la VM, los analiza y brinda un acceso conveniente a sus componentes. Además elimina todos los espacios en blanco y comentarios.

  • El escritor de código (code writer), traduce los comandos de VM en código ensamblador Hack.

  • El programa principal (main), construye un parser para analizar el archivo de entrada de la VM y un code writer para generar un código en el archivo de salida correspondiente. Luego, debe pasar por los comandos de la máquina virtual en el archivo de entrada y generar un código ensamblador para cada uno de ellos. Si el argumento del programa es un nombre de directorio en lugar de un nombre de archivo, el programa principal procesa todos los archivos .vm en este directorio. Al hacerlo, debe usar un analizador separado para manejar cada archivo de entrada y un solo code writer para manejar la salida.

Proyecto 7. VM I: Stack Arithmetic

Main

#include <iostream>

#include <string>

#include <unistd.h>

#include <fstream>

#include "VmTranslator.h"

#include "FileHandler.h"


using namespace std;


int main(int argc, char** argv)

{

if (argc == 1) {

cerr << "usage: vm2hack [-v] fileName.vm|directoryName" << endl;

cerr << " -v verbose" << endl;

return 1;

}


char c;

bool verbose = false;

int inputArg = 1;


while ((c = getopt(argc, argv, "v:")) != -1) {


switch (c) {


case 'v':

verbose = true;

inputArg++;

break;


case '?':

return 1;


}

}


string input(argv[inputArg]);


if (!FileHandler::isFile(input) && !FileHandler::isDir(input)) {

cerr << "error: invalid input" << endl;

return 1;

}


string outputFileName(FileHandler::getOutputFile(input));

ofstream outputFile(outputFileName);


if (!outputFile.good()) {

cerr << "error: unable to open file" << endl;

return 1;

}


VmTranslator vmTranslator(input, outputFile, verbose);

vmTranslator.translate();

outputFile.close();


return 0;

}



Parser

#include "Parser.h"

#include <sstream>

#include <iostream>


Parser::Parser(istream& inputStream)

: inputStream(inputStream), linePos(0)

{

}


string Parser::getCommand()

{

return command;

}


string Parser::getLine()

{

return currentLine;

}


int Parser::getLinePos()

{

return linePos;

}


bool Parser::advance()

{

while (nextValidLine()) {

getTokensFromLine();

return true;

}


return false;

}


VmCommandType Parser::commandType()

{

if (cmdVector.front() == "push") return VmCommandType::C_PUSH;

if (cmdVector.front() == "pop") return VmCommandType::C_POP;


return VmCommandType::C_ARITHMETIC;

}


string Parser::arg1()

{

if (commandType() == VmCommandType::C_ARITHMETIC)

return cmdVector.front();


return cmdVector.at(1);

}


int Parser::arg2()

{

return stoi(cmdVector.at(2));

}


bool Parser::readNextLine()

{

if (getline(inputStream, currentLine)) {

linePos++;

return true;

}


return false;

}


bool Parser::nextValidLine()

{

while (true) {


if (!readNextLine())

return false;


clearCommentsFromLine();

trimLine();


if (!currentLine.empty() && currentLine.size() > 1) // size > 1 for '\n' (empty lines)

return true;

}

}


void Parser::getTokensFromLine()

{

cmdVector.clear();

string::iterator it = currentLine.begin();


while (it != currentLine.end()) {


while (isspace(*it) && it != currentLine.end())

it++;


string token;


while (!isspace(*it) && it != currentLine.end()) {

token.append(1, *it);

it++;

}


if (!token.empty())

cmdVector.push_back(token);

}


command.clear();

vector<string>::iterator vectorIt;


/* reassembles command from the vector entries, so it's free of white spaces */

for (vectorIt = cmdVector.begin(); vectorIt != cmdVector.end(); ++vectorIt)

command += *vectorIt + " ";


command.erase(command.find_last_not_of(' ') + 1); // removes last spaced added in the above for loop

}


void Parser::trimLine()

{

currentLine.erase(0, currentLine.find_first_not_of(' ')); //prefixing spaces

currentLine.erase(0, currentLine.find_first_not_of('\t')); //prefixing tabs

currentLine.erase(currentLine.find_last_not_of(' ') + 1); //surfixing spaces

currentLine.erase(currentLine.find_last_not_of('\t') + 1); //surfixing tabs

}


void Parser::clearCommentsFromLine()

{

if (currentLine.find("//") != string::npos)

currentLine.erase(currentLine.find("//"));

}



CodeWriter

#include "CodeWriter.h"

#include "FileHandler.h"


CodeWriter::CodeWriter(ostream& outputStream, string programName)

: outputStream(outputStream), programName(programName), symbolCounter(0)

{

writeProgramHeader();

}


void CodeWriter::setFileName(string fileName)

{

this->fileName = FileHandler::removePath(fileName);

moduleName = FileHandler::getProgramName(fileName);

symbolCounter = 0;

writeModuleHeader();

}


void CodeWriter::writeArithmetic(string command)

{

if (command == "add") writeArithmeticAdd();

else if (command == "sub") writeArithmeticSub();

else if (command == "neg") writeArithmeticNeg();

else if (command == "eq") writeArithmeticEq();

else if (command == "gt") writeArithmeticGt();

else if (command == "lt") writeArithmeticLt();

else if (command == "and") writeArithmeticAnd();

else if (command == "or") writeArithmeticOr();

else writeArithmeticNot();

}


void CodeWriter::writeArithmeticAdd()

{

write("@SP // add");

write("AM=M-1");

write("D=M");

write("A=A-1");

write("M=D+M");

}


void CodeWriter::writeArithmeticSub()

{

write("@SP // sub");

write("AM=M-1");

write("D=M");

write("A=A-1");

write("M=M-D");

}


void CodeWriter::writeArithmeticNeg()

{

write("@SP // neg");

write("A=M");

write("A=A-1");

write("M=-M");

}


void CodeWriter::writeArithmeticEq()

{

string label("JEQ_" + moduleName + "_" + to_string(symbolCounter));


write("@SP // eq");

write("AM=M-1");

write("D=M");

write("@SP");

write("AM=M-1");

write("D=M-D");

write("@" + label);

write("D;JEQ");

write("D=1");

write("(" + label + ")", false);

write("D=D-1");

write("@SP");

write("A=M");

write("M=D");

write("@SP");

write("M=M+1");


symbolCounter++;

}


void CodeWriter::writeArithmeticGt()

{

string labelTrue("JGT_TRUE_" + moduleName + "_" + to_string(symbolCounter));

string labelFalse("JGT_FALSE_" + moduleName + "_" + to_string(symbolCounter));


write("@SP // gt");

write("AM=M-1");

write("D=M");

write("@SP");

write("AM=M-1");

write("D=M-D");

write("@" + labelTrue);

write("D;JGT");

write("D=0");

write("@" + labelFalse);

write("0;JMP");

write("(" + labelTrue + ")", false);

write("D=-1");

write("(" + labelFalse + ")", false);

write("@SP");

write("A=M");

write("M=D");

write("@SP");

write("M=M+1");


symbolCounter++;

}


void CodeWriter::writeArithmeticLt()

{

string labelTrue("JLT_TRUE_" + moduleName + "_" + to_string(symbolCounter));

string labelFalse("JLT_FALSE_" + moduleName + "_" + to_string(symbolCounter));


write("@SP // lt");

write("AM=M-1");

write("D=M");

write("@SP");

write("AM=M-1");

write("D=M-D");

write("@" + labelTrue);

write("D;JLT");

write("D=0");

write("@" + labelFalse);

write("0;JMP");

write("(" + labelTrue + ")", false);

write("D=-1");

write("(" + labelFalse + ")", false);

write("@SP");

write("A=M");

write("M=D");

write("@SP");

write("M=M+1");


symbolCounter++;

}


void CodeWriter::writeArithmeticAnd()

{

write("@SP // and");

write("AM=M-1");

write("D=M");

write("A=A-1");

write("M=D&M");

}


void CodeWriter::writeArithmeticOr()

{

write("@SP // or");

write("AM=M-1");

write("D=M");

write("A=A-1");

write("M=D|M");

}


void CodeWriter::writeArithmeticNot()

{

write("@SP // not");

write("A=M");

write("A=A-1");

write("M=!M");

}


void CodeWriter::writePushPop(VmCommandType cmd, string segment, int index)

{

if (cmd == VmCommandType::C_PUSH)

writePush(segment, index);

else

writePop(segment, index);

}


void CodeWriter::writePush(string segment, int index)

{

string indexStr = to_string(index);

string registerStr = registerName(segment, index);


if (segment == "constant") { // push constant


write("@" + indexStr + " // push " + segment + " " + indexStr);

write("D=A");

write("@SP");

write("A=M");

write("M=D");

write("@SP");

write("M=M+1");


} else if (segment == "static" || segment == "temp" || segment == "pointer") { // push static or temp


write("@" + registerStr + " // push " + segment + " " + indexStr);

write("D=M");

write("@SP");

write("A=M");

write("M=D");

write("@SP");

write("M=M+1");


} else { // all other segments


write("@" + registerStr + " // push " + segment + " " + indexStr);

write("D=M");

write("@" + indexStr);

write("A=D+A");

write("D=M");

write("@SP");

write("A=M");

write("M=D");

write("@SP");

write("M=M+1");


}

}


void CodeWriter::writePop(string segment, int index)

{

string indexStr = to_string(index);

string registerStr = registerName(segment, index);


// pop static, temp or pointer

if (segment == "static" || segment == "temp" || segment == "pointer") {


write("@SP // pop " + segment + " " + indexStr);

write("AM=M-1");

write("D=M");

write("@" + registerStr);

write("M=D");


} else { // all other segments


write("@" + registerStr + " // pop " + segment + " " + indexStr);

write("D=M");

write("@" + indexStr);

write("D=D+A");

write("@R13");

write("M=D");

write("@SP");

write("AM=M-1");

write("D=M");

write("@R13");

write("A=M");

write("M=D");


}

}


void CodeWriter::write(string code, bool ident)

{

if (ident)

outputStream << "\t";

outputStream << code << endl;

}


string CodeWriter::registerName(string segment,int index)

{

if (segment == "local") return "LCL";

if (segment == "argument") return "ARG";

if (segment == "this") return "THIS";

if (segment == "that") return "THAT";

if (segment == "pointer") return "R" + to_string(3 + index);

if (segment == "temp") return "R" + to_string(5 + index);


return moduleName + "." + to_string(index); // else it is static

}


void CodeWriter::writeProgramHeader()

{

outputStream << "// ............................................" << endl;

outputStream << "// Programa: " << programName << ".asm" << endl;

outputStream << "// ............................................" << endl;

}


void CodeWriter::writeModuleHeader()

{

outputStream << endl;

outputStream << "// ............................................" << endl;

outputStream << "// Compilación (traducción): " << moduleName << endl;

outputStream << "// Archivo origen: " << fileName << endl;

outputStream << "// ............................................" << endl;

outputStream << endl;

}



VMTranslator

#include "VmTranslator.h"

#include "VmCommandType.h"

#include "Parser.h"

#include "CodeWriter.h"

#include <fstream>


VmTranslator::VmTranslator(string input, ostream& outputStream, bool verbose)

: input(input), outputStream(outputStream), fileHandler(input),

verbose(verbose),

vmTransTitle("vm2hack"),

vmTransSubtitle("partial vm translator (nand2tetris, chap. 7)"),

vmTransVersion("0.2")

{

}


void VmTranslator::translate()

{

if (verbose) printHeader();


CodeWriter codeWriter(outputStream, fileHandler.getProgramName());

string file;


int count = 0;


while (fileHandler.nextVmFile(file)) {


if (verbose) {

cout << "#" << ++count << " " << file << " -> module: ";

cout << FileHandler::getProgramName(file) << endl;

}


ifstream inputFile(file);

if (!inputFile.good()) {

cerr << "error: unable to open file \"" << file << "\"" << endl;

continue;

}


Parser parser(inputFile);

codeWriter.setFileName(file);


while (parser.advance()) {


switch (parser.commandType()) {


case VmCommandType::C_ARITHMETIC:

codeWriter.writeArithmetic(parser.getCommand());

break;


case VmCommandType::C_PUSH:

case VmCommandType::C_POP:

codeWriter.writePushPop(parser.commandType(), parser.arg1(), parser.arg2());

break;


default:

break;

}


}


}


if (verbose) {

cout << endl;

cout << "Number of files translated: " << count << endl;

cout << "done" << endl << endl;

}

}


void VmTranslator::printHeader()

{

cout << vmTransTitle << " - " << vmTransSubtitle << " v" << vmTransVersion << endl;

cout << "input: " << input;


if (fileHandler.isDir()) {

cout << " (directory with " << fileHandler.getNumVmFiles();

fileHandler.getNumVmFiles() == 1 ? cout << " vm file)" : cout << " vm files)";

}


cout << endl;

cout << "output: " << fileHandler.getOutputFile() << endl << endl;

}



FileHandler

#include "FileHandler.h"

#include <sys/stat.h>

#include <unistd.h>

#include <dirent.h>

#include <iostream>


FileHandler::FileHandler(string input)

: input(input)

{

if (isFile() && isVmFile()) {


path = "";

files.push_back(string(input));


} else if (isDir()) {


if (input.back() == '/')

path = input;

else

path = input + "/";


DIR *dp;

struct dirent *dirp;

if((dp = opendir(input.c_str())) == NULL) {

cerr << "Error opening " << input << endl;

}


while ((dirp = readdir(dp)) != NULL) {

string str(dirp->d_name);

if (str == "." || str == "..")

continue;

if (FileHandler::isVmFile(str))

files.push_back(string(path + str));

}

closedir(dp);

}


it = files.begin();

}


bool FileHandler::isFile()

{

struct stat buf;

stat(input.c_str(), &buf);

return S_ISREG(buf.st_mode);

}


bool FileHandler::isFile(string in) // static

{

struct stat buf;

stat(in.c_str(), &buf);

return S_ISREG(buf.st_mode);

}


bool FileHandler::isDir()

{

struct stat buf;

stat(input.c_str(), &buf);

return S_ISDIR(buf.st_mode);

}


bool FileHandler::isDir(string in) // static

{

struct stat buf;

stat(in.c_str(), &buf);

return S_ISDIR(buf.st_mode);

}


string FileHandler::getOutputFile()

{

string in = input;


if (isDir()) {

return path + getProgramName().append(".asm");

}


string ext = ".asm";

if (in.rfind(".vm") != string::npos)

in.erase(in.rfind(".vm"));

in.append(ext);


return in;

}


string FileHandler::getOutputFile(string in) // static

{

struct stat buf;

stat(in.c_str(), &buf);

if (S_ISDIR(buf.st_mode)) {

string path;

if (in.back() == '/')

path = in;

else

path = in + "/";


return path + FileHandler::getProgramName(in).append(".asm");

}


string ext = ".asm";

if (in.rfind(".vm") != string::npos)

in.erase(in.rfind(".vm"));

in.append(ext);


return in;

}


string FileHandler::removeExtension(string fileName) // static

{

if (fileName.rfind(".") != string::npos)

fileName.erase(fileName.rfind("."));


return fileName;

}


string FileHandler::getProgramName(string fileName) // static

{

fileName = removeExtension(fileName);

return removePath(fileName);

}


string FileHandler::changeExtension(string fileName, string newExtension) // static

{

if (fileName.rfind(".") != string::npos)

fileName.erase(fileName.rfind("."));


fileName.append(newExtension);


return fileName;

}


string FileHandler::getProgramName()

{

string progName;


if (isVmFile()) {

progName = input;

progName.erase(progName.rfind(".vm"));

} else if (input == ".") {

char cwd[256];

getcwd(cwd, sizeof(cwd));

progName = cwd;

} else {

progName = input;

}


if (progName.back() == '/')

progName.erase(progName.rfind("/"));


if (progName.rfind("/") != string::npos)

progName.erase(0, progName.rfind("/") + 1);


return progName;

}


string FileHandler::removePath(string fileName) // static

{

if (fileName.back() == '/')

fileName.erase(fileName.rfind("/"));


if (fileName.rfind("/") != string::npos)

fileName.erase(0, fileName.rfind("/") + 1);


return fileName;

}


int FileHandler::getNumVmFiles()

{

return files.size();

}


bool FileHandler::isVmFile()

{

return input.rfind(".vm") != string::npos ? true : false;

}


bool FileHandler::isVmFile(string in)

{

return in.rfind(".vm") != string::npos ? true : false;

}


bool FileHandler::nextVmFile(string& fileName)

{

if (it == files.end())

return false;


fileName = *it;

++it;


return true;

}



Figura 1. Compilando el traductor

Figura 2. Traduciendo el archivo StackTest.vm

Testeo con programas y resultados

Luego de haber utilizado el traductor de VM para traducir los archivos .vm suministrados y obtener un nuevo archivo de texto que contiene el código ensamblador de hack .asm. Para verificar que el traductor realmente genere los archivos .asm con el código maquina esperado, tendremos que utilizar los archivos .tst y .cmp suministrados para ejecutar el programa .asm en el emulador de CPU suministrado.


SimpleAdd.asm

// ............................................

// Programa: .\ArchivosVM07\StackArithmetic\SimpleAdd\SimpleAdd.asm

// ............................................


// ............................................

// Compilación (traducción): .\ArchivosVM07\StackArithmetic\SimpleAdd\SimpleAdd

// Archivo origen: .\ArchivosVM07\StackArithmetic\SimpleAdd\SimpleAdd.vm

// ............................................


@7 // push constant 7

D=A

@SP

A=M

M=D

@SP

M=M+1

@8 // push constant 8

D=A

@SP

A=M

M=D

@SP

M=M+1

@SP // add

AM=M-1

D=M

A=A-1

M=D+M


Resultados obtenidos

El SimpleAdd empuja dos constantes a la pila y las suma.

StackTest.asm

@17 // push constant 17

D=A

@SP

A=M

M=D

@SP

M=M+1


@17 // push constant 17

D=A

@SP

A=M

M=D

@SP

M=M+1


@SP // eq

AM=M-1

D=M

@SP

AM=M-1

D=M-D

@JGT_TRUE_StackTest_0

D;JEQ

D=0

@JGT_FALSE_StackTest_0

0;JMP

(JGT_TRUE_StackTest_0)

D=-1

(JGT_FALSE_StackTest_0)

@SP

A=M

M=D

@SP

M=M+1


@17 // push constant 17

D=A

@SP

A=M

M=D

@SP

M=M+1


@16 // push constant 16

D=A

@SP

A=M

M=D

@SP

M=M+1


@SP // eq

AM=M-1

D=M

@SP

AM=M-1

D=M-D

@JGT_TRUE_StackTest_1

D;JEQ

D=0

@JGT_FALSE_StackTest_1

0;JMP

(JGT_TRUE_StackTest_1)

D=-1

(JGT_FALSE_StackTest_1)

@SP

A=M

M=D

@SP

M=M+1


@16 // push constant 16

D=A

@SP

A=M

M=D

@SP

M=M+1


@17 // push constant 17

D=A

@SP

A=M

M=D

@SP

M=M+1


@SP // eq

AM=M-1

D=M

@SP

AM=M-1

D=M-D

@JGT_TRUE_StackTest_2

D;JEQ

D=0

@JGT_FALSE_StackTest_2

0;JMP

(JGT_TRUE_StackTest_2)

D=-1

(JGT_FALSE_StackTest_2)

@SP

A=M

M=D

@SP

M=M+1


@892 // push constant 892

D=A

@SP

A=M

M=D

@SP

M=M+1


@891 // push constant 891

D=A

@SP

A=M

M=D

@SP

M=M+1


@SP // lt

AM=M-1

D=M

@SP

AM=M-1

D=M-D

@JLT_TRUE_StackTest_3

D;JLT

D=0

@JLT_FALSE_StackTest_3

0;JMP

(JLT_TRUE_StackTest_3)

D=-1

(JLT_FALSE_StackTest_3)

@SP

A=M

M=D

@SP

M=M+1


@891 // push constant 891

D=A

@SP

A=M

M=D

@SP

M=M+1


@892 // push constant 892

D=A

@SP

A=M

M=D

@SP

M=M+1


@SP // lt

AM=M-1

D=M

@SP

AM=M-1

D=M-D

@JLT_TRUE_StackTest_4

D;JLT

D=0

@JLT_FALSE_StackTest_4

0;JMP

(JLT_TRUE_StackTest_4)

D=-1

(JLT_FALSE_StackTest_4)

@SP

A=M

M=D

@SP

M=M+1


@891 // push constant 891

D=A

@SP

A=M

M=D

@SP

M=M+1


@891 // push constant 891

D=A

@SP

A=M

M=D

@SP

M=M+1


@SP // lt

AM=M-1

D=M

@SP

AM=M-1

D=M-D

@JLT_TRUE_StackTest_5

D;JLT

D=0

@JLT_FALSE_StackTest_5

0;JMP

(JLT_TRUE_StackTest_5)

D=-1

(JLT_FALSE_StackTest_5)

@SP

A=M

M=D

@SP

M=M+1


@32767 // push constant 32767

D=A

@SP

A=M

M=D

@SP

M=M+1


@32766 // push constant 32766

D=A

@SP

A=M

M=D

@SP

M=M+1


@SP // gt

AM=M-1

D=M

@SP

AM=M-1

D=M-D

@JGT_TRUE_StackTest_6

D;JGT

D=0

@JGT_FALSE_StackTest_6

0;JMP

(JGT_TRUE_StackTest_6)

D=-1

(JGT_FALSE_StackTest_6)

@SP

A=M

M=D

@SP

M=M+1


@32766 // push constant 32766

D=A

@SP

A=M

M=D

@SP

M=M+1


@32767 // push constant 32767

D=A

@SP

A=M

M=D

@SP

M=M+1


@SP // gt

AM=M-1

D=M

@SP

AM=M-1

D=M-D

@JGT_TRUE_StackTest_7

D;JGT

D=0

@JGT_FALSE_StackTest_7

0;JMP

(JGT_TRUE_StackTest_7)

D=-1

(JGT_FALSE_StackTest_7)

@SP

A=M

M=D

@SP

M=M+1


@32766 // push constant 32766

D=A

@SP

A=M

M=D

@SP

M=M+1


@32766 // push constant 32766

D=A

@SP

A=M

M=D

@SP

M=M+1


@SP // gt

AM=M-1

D=M

@SP

AM=M-1

D=M-D

@JGT_TRUE_StackTest_8

D;JGT

D=0

@JGT_FALSE_StackTest_8

0;JMP

(JGT_TRUE_StackTest_8)

D=-1

(JGT_FALSE_StackTest_8)

@SP

A=M

M=D

@SP

M=M+1


@57 // push constant 57

D=A

@SP

A=M

M=D

@SP

M=M+1


@31 // push constant 31

D=A

@SP

A=M

M=D

@SP

M=M+1


@53 // push constant 53

D=A

@SP

A=M

M=D

@SP

M=M+1


@SP // add

AM=M-1

D=M

A=A-1

M=D+M


@112 // push constant 112

D=A

@SP

A=M

M=D

@SP

M=M+1


@SP // sub

AM=M-1

D=M

A=A-1

M=M-D


@SP // neg

A=M

A=A-1

M=-M


@SP // and

AM=M-1

D=M

A=A-1

M=D&M


@82 // push constant 82

D=A

@SP

A=M

M=D

@SP

M=M+1


@SP // or

AM=M-1

D=M

A=A-1

M=D|M


@SP // not

A=M

A=A-1

M=!M


(END)

@END

0;JMP



Resultados obtenidos

El StackTest ejecuta una secuencia de operaciones aritméticas y lógicas en la pila.

BasicTest.asm


// ............................................

// Programa: .\ArchivosVM07\MemoryAccess\BasicTest\BasicTest.asm

// ............................................


// ............................................

// Compilación (traducción): .\ArchivosVM07\MemoryAccess\BasicTest\BasicTest

// Archivo origen: .\ArchivosVM07\MemoryAccess\BasicTest\BasicTest.vm

// ............................................


@10 // push constant 10

D=A

@SP

A=M

M=D

@SP

M=M+1

@LCL // pop local 0

D=M

@0

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@21 // push constant 21

D=A

@SP

A=M

M=D

@SP

M=M+1

@22 // push constant 22

D=A

@SP

A=M

M=D

@SP

M=M+1

@ARG // pop argument 2

D=M

@2

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@ARG // pop argument 1

D=M

@1

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@36 // push constant 36

D=A

@SP

A=M

M=D

@SP

M=M+1

@THIS // pop this 6

D=M

@6

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@42 // push constant 42

D=A

@SP

A=M

M=D

@SP

M=M+1

@45 // push constant 45

D=A

@SP

A=M

M=D

@SP

M=M+1

@THAT // pop that 5

D=M

@5

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@THAT // pop that 2

D=M

@2

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@510 // push constant 510

D=A

@SP

A=M

M=D

@SP

M=M+1

@SP // pop temp 6

AM=M-1

D=M

@R11

M=D

@LCL // push local 0

D=M

@0

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@THAT // push that 5

D=M

@5

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP // add

AM=M-1

D=M

A=A-1

M=D+M

@ARG // push argument 1

D=M

@1

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP // sub

AM=M-1

D=M

A=A-1

M=M-D

@THIS // push this 6

D=M

@6

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@THIS // push this 6

D=M

@6

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP // add

AM=M-1

D=M

A=A-1

M=D+M

@SP // sub

AM=M-1

D=M

A=A-1

M=M-D

@R11 // push temp 6

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP // add

AM=M-1

D=M

A=A-1

M=D+M


Resultados obtenidos

El BasicTest ejecuta operaciones push/pop utilizando los segmentos de memoria virtual constante, local, argumento, esto, aquello y temporal.

PointerTest.asm

// ............................................

// Programa: .\ArchivosVM07\MemoryAccess\PointerTest\PointerTest.asm

// ............................................


// ............................................

// Compilación (traducción): .\ArchivosVM07\MemoryAccess\PointerTest\PointerTest

// Archivo origen: .\ArchivosVM07\MemoryAccess\PointerTest\PointerTest.vm

// ............................................


@3030 // push constant 3030

D=A

@SP

A=M

M=D

@SP

M=M+1

@SP // pop pointer 0

AM=M-1

D=M

@R3

M=D

@3040 // push constant 3040

D=A

@SP

A=M

M=D

@SP

M=M+1

@SP // pop pointer 1

AM=M-1

D=M

@R4

M=D

@32 // push constant 32

D=A

@SP

A=M

M=D

@SP

M=M+1

@THIS // pop this 2

D=M

@2

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@46 // push constant 46

D=A

@SP

A=M

M=D

@SP

M=M+1

@THAT // pop that 6

D=M

@6

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@R3 // push pointer 0

D=M

@SP

A=M

M=D

@SP

M=M+1

@R4 // push pointer 1

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP // add

AM=M-1

D=M

A=A-1

M=D+M

@THIS // push this 2

D=M

@2

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP // sub

AM=M-1

D=M

A=A-1

M=M-D

@THAT // push that 6

D=M

@6

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP // add

AM=M-1

D=M

A=A-1

M=D+M



Resultados obtenidos

El PointerTest ejecuta operaciones push/pop utilizando el puntero de segmentos de memoria virtual, esto y aquello.

StaticTest.asm


// ............................................

// Programa: .\ArchivosVM07\MemoryAccess\StaticTest\StaticTest.asm

// ............................................


// ............................................

// Compilación (traducción): .\ArchivosVM07\MemoryAccess\StaticTest\StaticTest

// Archivo origen: .\ArchivosVM07\MemoryAccess\StaticTest\StaticTest.vm

// ............................................


// push constant 111

@111

D=A

@SP

A=M

M=D

@SP

M=M+1

// push constant 333

@333

D=A

@SP

A=M

M=D

@SP

M=M+1

// push constant 888

@888

D=A

@SP

A=M

M=D

@SP

M=M+1

// pop static 8

@StaticTest8

D=A

@R13

M=D

@SP

M=M-1

A=M

D=M

@R13

A=M

M=D

// pop static 3

@StaticTest3

D=A

@R13

M=D

@SP

M=M-1

A=M

D=M

@R13

A=M

M=D

// pop static 1

@StaticTest1

D=A

@R13

M=D

@SP

M=M-1

A=M

D=M

@R13

A=M

M=D

// push static 3

@StaticTest3

D=M

@SP

A=M

M=D

@SP

M=M+1

// push static 1

@StaticTest1

D=M

@SP

A=M

M=D

@SP

M=M+1

// sub

@SP

M=M-1

A=M

D=M

@SP

M=M-1

@SP

A=M

M=M-D

@SP

M=M+1

// push static 8

@StaticTest8

D=M

@SP

A=M

M=D

@SP

M=M+1

// add

@SP

M=M-1

A=M

D=M

@SP

M=M-1

@SP

A=M

M=D+M

@SP

M=M+1


Resultados obtenidos

El StaticTest ejecuta operaciones push/pop utilizando el segmento de memoria virtual estático.

Proyecto 8. VM II: Program Control

Main

#include <iostream>

#include <string>

#include <unistd.h>

#include <fstream>

#include "VmTranslator.h"

#include "FileHandler.h"


using namespace std;


int main(int argc, char** argv)

{

bool verbose = false;

bool bootstrap = true;

bool comments = true;

bool optimise = true;


string input("OS_0/");


if (!FileHandler::isFile(input) && !FileHandler::isDir(input)) {

cerr << "error: invalid input \"" << input << "\"" << endl;

return 1;

}


string outputFileName(FileHandler::getOutputFile(input));

ofstream outputFile(outputFileName);


if (!outputFile.good()) {

cerr << "error: unable to open file \"" << outputFileName << "\"" << endl;

return 1;

}


VmTranslator vmTranslator(input, outputFile, verbose, bootstrap, comments, optimise);

vmTranslator.translate();

outputFile.close();


return 0;

}


Parser

#include "Parser.h"

#include <sstream>

#include <iostream>


Parser::Parser(istream& inputStream)

: inputStream(inputStream),

linePos(0)

{

}


string Parser::getCommand()

{

return command;

}


string Parser::getLine()

{

return currentLine;

}


int Parser::getLinePos()

{

return linePos;

}


bool Parser::advance()

{

while (nextValidLine()) {

getTokensFromLine();

return true;

}


return false;

}


VmCommandType Parser::commandType()

{

return commandType(cmdVector.front());

}


VmCommandType Parser::commandType(string cmd)

{

if (cmd == "push") return VmCommandType::C_PUSH;

if (cmd == "pop") return VmCommandType::C_POP;

if (cmd == "label") return VmCommandType::C_LABEL;

if (cmd == "goto") return VmCommandType::C_GOTO;

if (cmd == "if-goto") return VmCommandType::C_IF;

if (cmd == "function") return VmCommandType::C_FUNCTION;

if (cmd == "call") return VmCommandType::C_CALL;

if (cmd == "return") return VmCommandType::C_RETURN;


return VmCommandType::C_ARITHMETIC;

}


VmCommandOptType Parser::commandOptType()

{

switch (commandType()) {


case VmCommandType::C_ARITHMETIC:

if (cmdVector.front() == "add") return VmCommandOptType::C_ARITHMETIC_ADD;

if (cmdVector.front() == "sub") return VmCommandOptType::C_ARITHMETIC_SUB;

if (cmdVector.front() == "neg") return VmCommandOptType::C_ARITHMETIC_NEG;

if (cmdVector.front() == "eq") return VmCommandOptType::C_ARITHMETIC_EQ;

if (cmdVector.front() == "gt") return VmCommandOptType::C_ARITHMETIC_GT;

if (cmdVector.front() == "lt") return VmCommandOptType::C_ARITHMETIC_LT;

if (cmdVector.front() == "and") return VmCommandOptType::C_ARITHMETIC_AND;

if (cmdVector.front() == "or") return VmCommandOptType::C_ARITHMETIC_OR;

if (cmdVector.front() == "not") return VmCommandOptType::C_ARITHMETIC_NOT;

break;


case VmCommandType::C_PUSH:

if (cmdVector.at(1) == "argument") return VmCommandOptType::C_PUSH_ARGUMENT;

if (cmdVector.at(1) == "local") return VmCommandOptType::C_PUSH_LOCAL;

if (cmdVector.at(1) == "static") return VmCommandOptType::C_PUSH_STATIC;

if (cmdVector.at(1) == "constant") return VmCommandOptType::C_PUSH_CONSTANT;

if (cmdVector.at(1) == "this") return VmCommandOptType::C_PUSH_THIS;

if (cmdVector.at(1) == "that") return VmCommandOptType::C_PUSH_THAT;

if (cmdVector.at(1) == "pointer") return VmCommandOptType::C_PUSH_POINTER;

if (cmdVector.at(1) == "temp") return VmCommandOptType::C_PUSH_TEMP;


case VmCommandType::C_POP:

if (cmdVector.at(1) == "argument") return VmCommandOptType::C_POP_ARGUMENT;

if (cmdVector.at(1) == "local") return VmCommandOptType::C_POP_LOCAL;

if (cmdVector.at(1) == "static") return VmCommandOptType::C_POP_STATIC;

if (cmdVector.at(1) == "constant") return VmCommandOptType::C_POP_CONSTANT;

if (cmdVector.at(1) == "this") return VmCommandOptType::C_POP_THIS;

if (cmdVector.at(1) == "that") return VmCommandOptType::C_POP_THAT;

if (cmdVector.at(1) == "pointer") return VmCommandOptType::C_POP_POINTER;

if (cmdVector.at(1) == "temp") return VmCommandOptType::C_POP_TEMP;


case VmCommandType::C_LABEL:

return VmCommandOptType::C_LABEL;


case VmCommandType::C_GOTO:

return VmCommandOptType::C_GOTO;


case VmCommandType::C_IF:

return VmCommandOptType::C_IF;


case VmCommandType::C_FUNCTION:

return VmCommandOptType::C_FUNCTION;


case VmCommandType::C_CALL:

return VmCommandOptType::C_CALL;


default:

break;

}


return VmCommandOptType::C_RETURN;

}


string Parser::arg1()

{

if (commandType() == VmCommandType::C_ARITHMETIC)

return cmdVector.front();


return cmdVector.at(1);

}


int Parser::arg2()

{

return stoi(cmdVector.at(2));

}


void Parser::reset()

{

inputStream.clear();

inputStream.seekg(0);

linePos = 0;

}


bool Parser::readNextLine()

{

if (getline(inputStream, currentLine)) {

linePos++;

return true;

}


return false;

}


bool Parser::nextValidLine()

{

while (true) {


if (!readNextLine())

return false;


clearCommentsFromLine();

trimLine();


if (!currentLine.empty() && currentLine.size() > 1) // size > 1 for '\n' (empty lines)

return true;

}

}


void Parser::getTokensFromLine()

{

cmdVector.clear();

string::iterator it = currentLine.begin();


while (it != currentLine.end()) {


while (isspace(*it) && it != currentLine.end())

it++;


string token;


while (!isspace(*it) && it != currentLine.end()) {

token.append(1, *it);

it++;

}


if (!token.empty())

cmdVector.push_back(token);

}


command.clear();

vector<string>::iterator vectorIt;


/* reassembles command from the vector entries, so it's free of white spaces */

for (vectorIt = cmdVector.begin(); vectorIt != cmdVector.end(); ++vectorIt)

command += *vectorIt + " ";


command.erase(command.find_last_not_of(' ') + 1); // removes last spaced added in the above for loop

}


void Parser::trimLine()

{

currentLine.erase(0, currentLine.find_first_not_of(' ')); //prefixing spaces

currentLine.erase(0, currentLine.find_first_not_of('\t')); //prefixing tabs

currentLine.erase(currentLine.find_last_not_of(' ') + 1); //surfixing spaces

currentLine.erase(currentLine.find_last_not_of('\t') + 1); //surfixing tabs

}


void Parser::clearCommentsFromLine()

{

if (currentLine.find("//") != string::npos)

currentLine.erase(currentLine.find("//"));

}



CodeWriter

#include "CodeWriter.h"

#include "FileHandler.h"


CodeWriter::CodeWriter(ostream& outputStream, string programName, bool writeComments)

: outputStream(outputStream),

programName(programName),

writeComments(writeComments),

callGlobalID(0),

eqModuleID(0),

gtModuleID(0),

ltModuleID(0)

{

if (writeComments)

writeProgramHeader();

}


void CodeWriter::setFileName(string fileName)

{

this->fileName = FileHandler::removePath(fileName);

moduleName = FileHandler::getProgramName(fileName); // removes path and extension of file

// reset module IDs since a new file/module is up for translation

eqModuleID = 0;

gtModuleID = 0;

ltModuleID = 0;


if (writeComments)

writeModuleHeader();

}


void CodeWriter::writeArithmetic(string command)

{

if (command == "add") writeArithmeticAdd();

else if (command == "sub") writeArithmeticSub();

else if (command == "neg") writeArithmeticNeg();

else if (command == "eq") writeArithmeticEq();

else if (command == "gt") writeArithmeticGt();

else if (command == "lt") writeArithmeticLt();

else if (command == "and") writeArithmeticAnd();

else if (command == "or") writeArithmeticOr();

else writeArithmeticNot();

}


void CodeWriter::writeArithmeticAdd()

{

string com("// add"); // "com" for comment


write("@SP", com);

write("AM=M-1");

write("D=M");

write("A=A-1");

write("M=D+M");

}


void CodeWriter::writeArithmeticSub()

{

string com("// sub");


write("@SP", com);

write("AM=M-1");

write("D=M");

write("A=A-1");

write("M=M-D");

}


void CodeWriter::writeArithmeticNeg()

{

string com("// neg");


write("@SP", com);

write("A=M");

write("A=A-1");

write("M=-M");

}


void CodeWriter::writeArithmeticEq()

{

string com("// eq");

string label("JEQ_" + moduleName + "_" + to_string(eqModuleID));

eqModuleID++;


write("@SP", com);

write("AM=M-1");

write("D=M");

write("@SP");

write("AM=M-1");

write("D=M-D");

write("@" + label);

write("D;JEQ");

write("D=1");

write("(" + label + ")", false);

write("D=D-1");

write("@SP");

write("A=M");

write("M=D");

write("@SP");

write("M=M+1");

}


void CodeWriter::writeArithmeticGt()

{

string com("// gt");

string labelTrue("JGT_TRUE_" + moduleName + "_" + to_string(gtModuleID));

string labelFalse("JGT_FALSE_" + moduleName + "_" + to_string(gtModuleID));

gtModuleID++;


write("@SP", com);

write("AM=M-1");

write("D=M");

write("@SP");

write("AM=M-1");

write("D=M-D");

write("@" + labelTrue);

write("D;JGT");

write("D=0");

write("@" + labelFalse);

write("0;JMP");

write("(" + labelTrue + ")", false);

write("D=-1");

write("(" + labelFalse + ")", false);

write("@SP");

write("A=M");

write("M=D");

write("@SP");

write("M=M+1");

}


void CodeWriter::writeArithmeticLt()

{

string com("// lt");

string labelTrue("JLT_TRUE_" + moduleName + "_" + to_string(ltModuleID));

string labelFalse("JLT_FALSE_" + moduleName + "_" + to_string(ltModuleID));

ltModuleID++;


write("@SP", com);

write("AM=M-1");

write("D=M");

write("@SP");

write("AM=M-1");

write("D=M-D");

write("@" + labelTrue);

write("D;JLT");

write("D=0");

write("@" + labelFalse);

write("0;JMP");

write("(" + labelTrue + ")", false);

write("D=-1");

write("(" + labelFalse + ")", false);

write("@SP");

write("A=M");

write("M=D");

write("@SP");

write("M=M+1");

}


void CodeWriter::writeArithmeticAnd()

{

string com("// and");


write("@SP", com);

write("AM=M-1");

write("D=M");

write("A=A-1");

write("M=D&M");

}


void CodeWriter::writeArithmeticOr()

{

string com("// or");


write("@SP", com);

write("AM=M-1");

write("D=M");

write("A=A-1");

write("M=D|M");

}


void CodeWriter::writeArithmeticNot()

{

string com("// not");


write("@SP", com);

write("A=M");

write("A=A-1");

write("M=!M");

}


void CodeWriter::writePushPop(VmCommandType cmd, string segment, int index)

{

if (cmd == VmCommandType::C_PUSH)

writePush(segment, index);

else

writePop(segment, index);

}


void CodeWriter::writePush(string segment, int index)

{

string indexStr = to_string(index);

string registerStr = registerName(segment, index);

string com("// push " + segment + " " + indexStr);


if (segment == "constant") { // push constant


write("@" + indexStr, com);

write("D=A");

write("@SP");

write("A=M");

write("M=D");

write("@SP");

write("M=M+1");


} else if (segment == "static" || segment == "temp" || segment == "pointer") { // push static, temp or pointer


write("@" + registerStr, com);

write("D=M");

write("@SP");

write("A=M");

write("M=D");

write("@SP");

write("M=M+1");


} else { // all other segments


write("@" + registerStr, com);

write("D=M");

write("@" + indexStr);

write("A=D+A");

write("D=M");

write("@SP");

write("A=M");

write("M=D");

write("@SP");

write("M=M+1");


}

}


void CodeWriter::writePop(string segment, int index)

{

string indexStr = to_string(index);

string registerStr = registerName(segment, index);

string com("// pop " + segment + " " + indexStr);


// pop static, temp or pointer

if (segment == "static" || segment == "temp" || segment == "pointer") {


write("@SP", com);

write("AM=M-1");

write("D=M");

write("@" + registerStr);

write("M=D");


} else { // all other segments


write("@" + registerStr, com);

write("D=M");

write("@" + indexStr);

write("D=D+A");

write("@R13");

write("M=D");

write("@SP");

write("AM=M-1");

write("D=M");

write("@R13");

write("A=M");

write("M=D");


}

}


void CodeWriter::writeInit()

{

/*

SP=256

call Sys.init

*/


string com("// bootstrap code");


// this label is for a guard loop in case Sys.init returns (it is not supposed to)

string bootstrapLabel("BOOTSTRAP_" + programName + "$GUARD_LOOP");


write("@256", com);

write("D=A");

write("@SP");

write("M=D");

writeCall("Sys.init", 0);

write("(" + bootstrapLabel + ")", false);

write("@" + bootstrapLabel);

write("0;JMP");

}


void CodeWriter::writeLabel(string label)

{

string com("// label " + label);


write("(" + labelSymbol(label) + ")", com ,false);

}


void CodeWriter::writeGoto(string label)

{

string com("// goto " + label);


write("@" + labelSymbol(label), com);

write("0;JMP");

}


void CodeWriter::writeIf(string label)

{

string com("// if-goto " + label);


write("@SP", com);

write("AM=M-1");

write("D=M");

write("@" + labelSymbol(label));

write("D;JNE");

}


void CodeWriter::writeCall(string functionName, int numArgs)

{

/*

push return-address // (using the label declared below)

push LCL // save LCL of the calling function

push ARG // save ARG of the calling function

push THIS // save THIS of the calling function

push THAT // save THAT of the calling function

ARG = SP-n-5 // reposition ARG (n = number of args)

LCL = SP // reposiiton LCL

goto f // transfer control

(return-address) // declare a label for the return-address

*/


string com("// call " + functionName + " " + to_string(numArgs));

string returnSymbol(functionName + "_RETURN_" + to_string(callGlobalID));

callGlobalID++;


write("@" + returnSymbol, com); // push return-addr

write("D=A");

write("@SP");

write("A=M");

write("M=D");

write("@SP");

write("M=M+1");


write("@LCL"); // push LCL

write("D=M");

write("@SP");

write("A=M");

write("M=D");

write("@SP");

write("M=M+1");


write("@ARG"); // push ARG

write("D=M");

write("@SP");

write("A=M");

write("M=D");

write("@SP");

write("M=M+1");


write("@THIS"); // push THIS

write("D=M");

write("@SP");

write("A=M");

write("M=D");

write("@SP");

write("M=M+1");


write("@THAT"); // push THAT

write("D=M");

write("@SP");

write("A=M");

write("M=D");

write("@SP");

write("M=M+1");


write("@" + to_string(numArgs)); // ARG = SP-n-5

write("D=A");

write("@5");

write("D=D+A");

write("@SP");

write("D=M-D");

write("@ARG");

write("M=D");


write("@SP"); // LCL = SP

write("D=M");

write("@LCL");

write("M=D");


write("@" + functionName); // goto f

write("0;JMP");


write("(" + returnSymbol + ")", false); // (return-address)

}


void CodeWriter::writeReturn()

{

/*

FRAME = LCL // FRAME is a temporary var

RET = *(FRAME-5) // put the return-address in a temporary var

*ARG = pop() // reposition the return value for the caller

SP = ARG + 1 // restore SP of the caller

THAT = *(FRAME - 1) // restore THAT of the caller

THIS = *(FRAME - 2) // restore THIS of the caller

ARG = *(FRAME - 3) // restore ARG of the caller

LCL = *(FRAME - 4) // restore LCL of the caller

goto RET // goto return-address (in the caller's code)

*/


string com("// return");


write("@LCL", com); // FRAME = LCL

write("D=M");

write("@R13"); // R13 -> FRAME

write("M=D");


write("@5"); // RET = *(FRAME-5)

write("A=D-A");

write("D=M");

write("@R14"); // R14 -> RET

write("M=D");


write("@SP"); // *ARG = pop()

write("AM=M-1");

write("D=M");

write("@ARG");

write("A=M");

write("M=D");


write("D=A"); // SP = ARG+1

write("@SP");

write("M=D+1");


write("@R13"); // THAT = *(FRAME-1)

write("AM=M-1");

write("D=M");

write("@THAT");

write("M=D");


write("@R13"); // THIS = *(FRAME-2)

write("AM=M-1");

write("D=M");

write("@THIS");

write("M=D");


write("@R13"); // ARG = *(FRAME-3)

write("AM=M-1");

write("D=M");

write("@ARG");

write("M=D");


write("@R13"); // LCL = *(FRAME-4)

write("AM=M-1");

write("D=M");

write("@LCL");

write("M=D");


write("@R14"); // goto RET

write("A=M");

write("0;JMP");

}


void CodeWriter::writeFunction(string functionName, int numLocals)

{

/*

(f) // declare a label for the function entry

repeat k times: // k = number of local vars

PUSH 0

*/


this->functionName = functionName;

string loopLabel(functionName + "_INIT_LOCALS_LOOP");

string loopEndLabel(functionName + "_INIT_LOCALS_END");

string com("// function " + functionName + " " + to_string(numLocals));


write("(" + functionName + ")", com, false);

write("@" + to_string(numLocals));

write("D=A");

write("@R13"); // temp

write("M=D");

write("(" + loopLabel + ")", false);

write("@" + loopEndLabel);

write("D;JEQ");

write("@0");

write("D=A");

write("@SP");

write("A=M");

write("M=D");

write("@SP");

write("M=M+1");

write("@R13");

write("MD=M-1");

write("@" + loopLabel);

write("0;JMP");

write("(" + loopEndLabel + ")", false);

}


void CodeWriter::writeProgramHeader()

{

outputStream << "// ............................................" << endl;

outputStream << "// program: " << programName << ".asm" << endl;

outputStream << "// ............................................" << endl;

outputStream << endl;

}


void CodeWriter::writeModuleHeader()

{

outputStream << endl;

outputStream << "// ............................................" << endl;

outputStream << "// module: " << moduleName << endl;

outputStream << "// from file: " << fileName << endl;

outputStream << "// ............................................" << endl;

outputStream << endl;

}


void CodeWriter::write(string code, bool ident)

{

if (ident)

outputStream << "\t";


outputStream << code << endl;

}


void CodeWriter::write(string code, string comment, bool ident)

{

if (ident)

outputStream << "\t";


outputStream << code;


if (writeComments)

outputStream << "\t\t" << comment;


outputStream << endl;

}


string CodeWriter::labelSymbol(string label)

{

if (functionName.empty())

// there should not be any label/goto statement before the 1st function, but this is just in case...

return programName + ".asm$" + label;

else

return functionName + "$" + label;

}


string CodeWriter::registerName(string segment,int index)

{

if (segment == "local") return "LCL";

if (segment == "argument") return "ARG";

if (segment == "this") return "THIS";

if (segment == "that") return "THAT";

if (segment == "pointer") return "R" + to_string(3 + index);

if (segment == "temp") return "R" + to_string(5 + index);


return moduleName + "." + to_string(index); // else it is static

}


VMTranslator

#include "VmTranslator.h"

#include "VmCommandType.h"

#include "Parser.h"

#include "CodeWriter.h"

#include "CodeWriterOptimised.h"

#include <fstream>

#include <list>


VmTranslator::VmTranslator(string input, ostream& outputStream, bool verbose, bool bootstrap, bool comments, bool optimise)

: input(input),

outputStream(outputStream),

fileHandler(input),

verbose(verbose),

bootstrap(bootstrap),

comments(comments),

optimise(optimise),

vmTransTitle("vm2hack"),

vmTransSubtitle("vm translator (nand2tetris, chap. 8)"),

vmTransVersion("0.1")

{

}


void VmTranslator::translate()

{

if (verbose)

printHeader();


CodeWriter* codeWriter;


if (!optimise)

codeWriter = new CodeWriter(outputStream, fileHandler.getProgramName(), comments);

else

codeWriter = new CodeWriterOptimised(outputStream, fileHandler.getProgramName(), comments);


if (bootstrap) {

codeWriter->writeInit();


if (verbose)

cout << "bootstrap code written" << endl;

}


string file;

int fileCount = 0;


while (fileHandler.nextVmFile(file)) {


if (verbose) {

cout << "processing file " << FileHandler::removePath(file) << " -> module: ";

cout << FileHandler::getProgramName(file) << endl;

}


ifstream inputFile(file);

if (!inputFile.good()) {

cerr << "error: unable to open file \"" << file << "\"" << endl;

continue;

}


Parser parser(inputFile);

codeWriter->setFileName(file);


if (!optimise)

translateNonOptimised(parser, codeWriter);

else

translateOptimised(parser, (CodeWriterOptimised*) codeWriter);


fileCount++;


}


delete codeWriter;


if (verbose) {

cout << endl;

cout << "Number of files translated: " << fileCount << endl;

cout << "done" << endl << endl;

}

}


void VmTranslator::translateNonOptimised(Parser& parser, CodeWriter* codeWriter)

{

while (parser.advance()) {


switch (parser.commandType()) {


case VmCommandType::C_ARITHMETIC:

codeWriter->writeArithmetic(parser.getCommand());

break;


case VmCommandType::C_PUSH:

case VmCommandType::C_POP:

codeWriter->writePushPop(parser.commandType(), parser.arg1(), parser.arg2());

break;


case VmCommandType::C_LABEL:

codeWriter->writeLabel(parser.arg1());

break;


case VmCommandType::C_GOTO:

codeWriter->writeGoto(parser.arg1());

break;


case VmCommandType::C_IF:

codeWriter->writeIf(parser.arg1());

break;


case VmCommandType::C_FUNCTION:

if (verbose)

cout << " ~ function " << parser.arg1() << " (allocates " << parser.arg2() << " local vars)" << endl;


codeWriter->writeFunction(parser.arg1(), parser.arg2());

break;


case VmCommandType::C_CALL:

codeWriter->writeCall(parser.arg1(), parser.arg2());

break;


case VmCommandType::C_RETURN:

codeWriter->writeReturn();

break;


}

}

}


void VmTranslator::translateOptimised(Parser& parser, CodeWriterOptimised* codeWriterOpt)

{

static bool globalFunctions = true;


if (globalFunctions) {

codeWriterOpt->writeGlobalFunctions();

globalFunctions = false;

}


vmCmdList.clear();


while (parser.advance())

vmCmdList.push_back(parser.commandOptType());


parser.reset();


while (parser.advance()) {


if (translateOptimisedSequence(parser, codeWriterOpt))

continue;


switch (parser.commandType()) {


case VmCommandType::C_ARITHMETIC:

codeWriterOpt->writeArithmetic(parser.getCommand());

break;


case VmCommandType::C_PUSH:

case VmCommandType::C_POP:

codeWriterOpt->writePushPop(parser.commandType(), parser.arg1(), parser.arg2());

break;


case VmCommandType::C_LABEL:

codeWriterOpt->writeLabel(parser.arg1());

break;


case VmCommandType::C_GOTO:

codeWriterOpt->writeGoto(parser.arg1());

break;


case VmCommandType::C_IF:

codeWriterOpt->writeIf(parser.arg1());

break;


case VmCommandType::C_FUNCTION:

if (verbose)

cout << " ~ function " << parser.arg1() << " (allocates " << parser.arg2() << " local vars)" << endl;


codeWriterOpt->writeFunction(parser.arg1(), parser.arg2());

break;


case VmCommandType::C_CALL:

codeWriterOpt->writeCall(parser.arg1(), parser.arg2());

break;


case VmCommandType::C_RETURN:

codeWriterOpt->writeReturn();

break;


}


if (!vmCmdList.empty())

vmCmdList.erase(vmCmdList.begin());


}

}


bool VmTranslator::translateOptimisedSequence(Parser& parser, CodeWriterOptimised* codeWriterOpt)

{

if (vmCmdList.size() >= 3) {

if (vmCmdList.at(0) == VmCommandOptType::C_PUSH_CONSTANT &&

vmCmdList.at(1) == VmCommandOptType::C_PUSH_LOCAL &&

vmCmdList.at(2) == VmCommandOptType::C_ARITHMETIC_ADD)

{

int constant = parser.arg2();

parser.advance();

int index = parser.arg2();


codeWriterOpt->writeSeqPushConstantPushTempAdd(constant, index);

vmCmdList.erase(vmCmdList.begin(), vmCmdList.begin() + 3);

parser.advance();

return true;

}

}


if (vmCmdList.size() >= 2) {


if (vmCmdList.at(0) == VmCommandOptType::C_PUSH_CONSTANT &&

vmCmdList.at(1) == VmCommandOptType::C_ARITHMETIC_ADD)

{

codeWriterOpt->writeSeqPushConstantAdd(parser.arg2());

vmCmdList.erase(vmCmdList.begin(), vmCmdList.begin() + 2);

parser.advance();

return true;

}


if (vmCmdList.at(0) == VmCommandOptType::C_PUSH_CONSTANT &&

vmCmdList.at(1) == VmCommandOptType::C_ARITHMETIC_SUB)

{

codeWriterOpt->writeSeqPushConstantSub(parser.arg2());

vmCmdList.erase(vmCmdList.begin(), vmCmdList.begin() + 2);

parser.advance();

return true;

}


if (vmCmdList.at(0) == VmCommandOptType::C_PUSH_CONSTANT &&

vmCmdList.at(1) == VmCommandOptType::C_ARITHMETIC_NEG)

{

codeWriterOpt->writeSeqPushConstantNeg(parser.arg2());

vmCmdList.erase(vmCmdList.begin(), vmCmdList.begin() + 2);

parser.advance();

return true;

}


if ((vmCmdList.at(0) == VmCommandOptType::C_PUSH_ARGUMENT ||

vmCmdList.at(0) == VmCommandOptType::C_PUSH_LOCAL ||

vmCmdList.at(0) == VmCommandOptType::C_PUSH_THIS ||

vmCmdList.at(0) == VmCommandOptType::C_PUSH_THAT) &&

vmCmdList.at(1) == VmCommandOptType::C_ARITHMETIC_ADD)

{

codeWriterOpt->writeSeqPushArgLocalThisThatAdd(parser.arg1(), parser.arg2());

vmCmdList.erase(vmCmdList.begin(), vmCmdList.begin() + 2);

parser.advance();

return true;

}


if ((vmCmdList.at(0) == VmCommandOptType::C_PUSH_ARGUMENT ||

vmCmdList.at(0) == VmCommandOptType::C_PUSH_LOCAL ||

vmCmdList.at(0) == VmCommandOptType::C_PUSH_THIS ||

vmCmdList.at(0) == VmCommandOptType::C_PUSH_THAT) &&

vmCmdList.at(1) == VmCommandOptType::C_ARITHMETIC_SUB)

{

codeWriterOpt->writeSeqPushArgLocalThisThatSub(parser.arg1(), parser.arg2());

vmCmdList.erase(vmCmdList.begin(), vmCmdList.begin() + 2);

parser.advance();

return true;

}


if (vmCmdList.at(0) == VmCommandOptType::C_PUSH_TEMP &&

vmCmdList.at(1) == VmCommandOptType::C_POP_THAT)

{

int tempIndex = parser.arg2();

parser.advance();

int thatIndex = parser.arg2();


codeWriterOpt->writeSeqPushTempPopThat(tempIndex, thatIndex);

vmCmdList.erase(vmCmdList.begin(), vmCmdList.begin() + 2);

return true;

}


if (vmCmdList.at(0) == VmCommandOptType::C_PUSH_ARGUMENT &&

vmCmdList.at(1) == VmCommandOptType::C_POP_POINTER)

{

int argIndex = parser.arg2();

parser.advance();

int pointerIndex = parser.arg2();


cout << "HERE" << endl;


codeWriterOpt->writeSeqPushArgumentPopPointer(argIndex, pointerIndex);

vmCmdList.erase(vmCmdList.begin(), vmCmdList.begin() + 2);

return true;

}


if (vmCmdList.at(0) == VmCommandOptType::C_PUSH_STATIC &&

vmCmdList.at(1) == VmCommandOptType::C_ARITHMETIC_ADD)

{

codeWriterOpt->writeSeqPushStaticAdd(parser.arg2());

vmCmdList.erase(vmCmdList.begin(), vmCmdList.begin() + 2);

parser.advance();

return true;

}


}


return false;

}




























/*

void VmTranslator::translateOptimised(Parser& parser, CodeWriterOptimised* codeWriterOpt)

{

static bool globalFunctions = true;


if (globalFunctions)

codeWriterOpt->writeGlobalFunctions();


while (!vmCommands.empty() || parser.advance()) {


switch (parser.commandType()) {


case VmCommandType::C_ARITHMETIC:

codeWriterOpt->writeArithmetic(parser.getCommand());

break;


case VmCommandType::C_PUSH:

case VmCommandType::C_POP:

codeWriterOpt->writePushPop(parser.commandType(), parser.arg1(), parser.arg2());

break;


case VmCommandType::C_LABEL:

codeWriterOpt->writeLabel(parser.arg1());

break;


case VmCommandType::C_GOTO:

codeWriterOpt->writeGoto(parser.arg1());

break;


case VmCommandType::C_IF:

codeWriterOpt->writeIf(parser.arg1());

break;


case VmCommandType::C_FUNCTION:

if (verbose)

cout << " ~ function " << parser.arg1() << " (allocates " << parser.arg2() << " local vars)" << endl;


codeWriterOpt->writeFunction(parser.arg1(), parser.arg2());

break;


case VmCommandType::C_CALL:

codeWriterOpt->writeCall(parser.arg1(), parser.arg2());

break;


case VmCommandType::C_RETURN:

codeWriterOpt->writeReturn();

break;


}

}

}

*/


void VmTranslator::printHeader()

{

cout << vmTransTitle << " - " << vmTransSubtitle << " v" << vmTransVersion << endl;

cout << "input " << input;


if (fileHandler.isDir()) {

cout << " (directory with " << fileHandler.getNumVmFiles();

fileHandler.getNumVmFiles() == 1 ? cout << " vm file)" : cout << " vm files)";

}


cout << endl;

cout << "output to file " << fileHandler.getOutputFile() << endl << endl;

}



FileHandler

#include "FileHandler.h"

#include <sys/stat.h>

#include <unistd.h>

#include <dirent.h>

#include <iostream>


FileHandler::FileHandler(string input)

: input(input)

{

if (isFile() && isVmFile()) {


path = "";

files.push_back(string(input));


} else if (isDir()) {


if (input.back() == '/')

path = input;

else

path = input + "/";


DIR *dp;

struct dirent *dirp;

if((dp = opendir(input.c_str())) == NULL) {

cerr << "Error opening " << input << endl;

}


while ((dirp = readdir(dp)) != NULL) {

string str(dirp->d_name);

if (str == "." || str == "..")

continue;

if (FileHandler::isVmFile(str))

files.push_back(string(path + str));

}

closedir(dp);

}


it = files.begin();

}


bool FileHandler::isFile()

{

struct stat buf;

stat(input.c_str(), &buf);

return S_ISREG(buf.st_mode);

}


bool FileHandler::isDir()

{

struct stat buf;

stat(input.c_str(), &buf);

return S_ISDIR(buf.st_mode);

}


string FileHandler::getOutputFile()

{

string in = input;


if (isDir()) {

return path + getProgramName().append(".asm");

}


string ext = ".asm";

if (in.rfind(".vm") != string::npos)

in.erase(in.rfind(".vm"));

in.append(ext);


return in;

}


string FileHandler::getProgramName()

{

string progName;


if (isVmFile()) {

progName = input;

progName.erase(progName.rfind(".vm"));

} else if (input == ".") {

char cwd[256];

getcwd(cwd, sizeof(cwd));

progName = cwd;

} else {

progName = input;

}


if (progName.back() == '/')

progName.erase(progName.rfind("/"));


if (progName.rfind("/") != string::npos)

progName.erase(0, progName.rfind("/") + 1);


return progName;

}


int FileHandler::getNumVmFiles()

{

return files.size();

}


bool FileHandler::isVmFile()

{

return input.rfind(".vm") != string::npos ? true : false;

}


bool FileHandler::nextVmFile(string& fileName)

{

if (it == files.end())

return false;


fileName = *it;

++it;


return true;

}


bool FileHandler::isVmFile(string in) // static

{

return in.rfind(".vm") != string::npos ? true : false;

}


bool FileHandler::isFile(string in) // static

{

struct stat buf;

stat(in.c_str(), &buf);

return S_ISREG(buf.st_mode);

}


bool FileHandler::isDir(string in) // static

{

struct stat buf;

stat(in.c_str(), &buf);

return S_ISDIR(buf.st_mode);

}


string FileHandler::getOutputFile(string in) // static

{

struct stat buf;

stat(in.c_str(), &buf);

if (S_ISDIR(buf.st_mode)) {

string path;

if (in.back() == '/')

path = in;

else

path = in + "/";


return path + FileHandler::getProgramName(in).append(".asm");

}


string ext = ".asm";

if (in.rfind(".vm") != string::npos)

in.erase(in.rfind(".vm"));

in.append(ext);


return in;

}


string FileHandler::getProgramName(string fileName) // static

{

fileName = removeExtension(fileName);

return removePath(fileName);

}


string FileHandler::removeExtension(string fileName) // static

{

if (fileName.rfind(".") != string::npos)

fileName.erase(fileName.rfind("."));


return fileName;

}


string FileHandler::removePath(string fileName) // static

{

if (fileName.back() == '/')

fileName.erase(fileName.rfind("/"));


if (fileName.rfind("/") != string::npos)

fileName.erase(0, fileName.rfind("/") + 1);


return fileName;

}


string FileHandler::changeExtension(string fileName, string newExtension) // static

{

if (fileName.rfind(".") != string::npos)

fileName.erase(fileName.rfind("."));


fileName.append(newExtension);


return fileName;

}


Testeo con programas y resultados

Luego de haber utilizado el traductor de VM para traducir los archivos .vm suministrados y obtener un nuevo archivo de texto que contiene el código ensamblador de hack .asm. Para verificar que el traductor realmente genere los archivos .asm con el código maquina esperado, tendremos que utilizar los archivos .tst y .cmp suministrados para ejecutar el programa .asm en el emulador de CPU suministrado.


BasicLoop.asm

@0

D=A

@SP

A=M

M=D

@SP

M=M+1

@LCL

D=M

@0

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

(LOOP_START)

@ARG

D=M

@0

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@LCL

D=M

@0

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

A=A-1

M=M+D

@LCL

D=M

@0

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@ARG

D=M

@0

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@1

D=A

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

A=A-1

M=M-D

@ARG

D=M

@0

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@ARG

D=M

@0

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

A=A-1

@LOOP_START

D;JNE

@LCL

D=M

@0

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1


Resultados obtenidos

BasicLoop calcula la suma 1+2+...+ n y coloca el resultado en la pila. Este programa prueba la implementación de los comandos de bifurcación del lenguaje VM goto e if-goto.

FibonacciSeries.asm

@ARG

D=M

@1

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@THAT

D=A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@0

D=A

@SP

A=M

M=D

@SP

M=M+1

@THAT

D=M

@0

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@1

D=A

@SP

A=M

M=D

@SP

M=M+1

@THAT

D=M

@1

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@ARG

D=M

@0

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@2

D=A

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

A=A-1

M=M-D

@ARG

D=M

@0

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

(MAIN_LOOP_START)

@ARG

D=M

@0

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

A=A-1

@COMPUTE_ELEMENT

D;JNE

@END_PROGRAM

0;JMP

(COMPUTE_ELEMENT)

@THAT

D=M

@0

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@THAT

D=M

@1

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

A=A-1

M=M+D

@THAT

D=M

@2

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@THAT

D=M

@SP

A=M

M=D

@SP

M=M+1

@1

D=A

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

A=A-1

M=M+D

@THAT

D=A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@ARG

D=M

@0

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@1

D=A

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

A=A-1

M=M-D

@ARG

D=M

@0

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@MAIN_LOOP_START

0;JMP

(END_PROGRAM)


Resultados obtenidos

FibonacciSeries calcula y almacena en memoria los primeros n elementos de la serie de Fibonacci. Este programa típico de manipulación de matrices proporciona una prueba más desafiante de los comandos de bifurcación de la máquina virtual.

FibonacciElement.asm

@256

D=A

@SP

M=D

@RETURN_LABEL0

D=A

@SP

A=M

M=D

@SP

M=M+1

@LCL

D=M

@SP

A=M

M=D

@SP

M=M+1

@ARG

D=M

@SP

A=M

M=D

@SP

M=M+1

@THIS

D=M

@SP

A=M

M=D

@SP

M=M+1

@THAT

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP

D=M

@5

D=D-A

@0

D=D-A

@ARG

M=D

@SP

D=M

@LCL

M=D

@Sys.init

0;JMP

(RETURN_LABEL0)

(Main.fibonacci)

@ARG

D=M

@0

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@2

D=A

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

A=A-1

D=M-D

@FALSE0

D;JGE

@SP

A=M-1

M=-1

@CONTINUE0

0;JMP

(FALSE0)

@SP

A=M-1

M=0

(CONTINUE0)

@SP

AM=M-1

D=M

A=A-1

@IF_TRUE

D;JNE

@IF_FALSE

0;JMP

(IF_TRUE)

@ARG

D=M

@0

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@LCL

D=M

@FRAME

M=D

@5

A=D-A

D=M

@RET

M=D

@ARG

D=M

@0

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@ARG

D=M

@SP

M=D+1

@FRAME

D=M-1

AM=D

D=M

@THAT

M=D

@FRAME

D=M-1

AM=D

D=M

@THIS

M=D

@FRAME

D=M-1

AM=D

D=M

@ARG

M=D

@FRAME

D=M-1

AM=D

D=M

@LCL

M=D

@RET

A=M

0;JMP

(IF_FALSE)

@ARG

D=M

@0

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@2

D=A

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

A=A-1

M=M-D

@RETURN_LABEL1

D=A

@SP

A=M

M=D

@SP

M=M+1

@LCL

D=M

@SP

A=M

M=D

@SP

M=M+1

@ARG

D=M

@SP

A=M

M=D

@SP

M=M+1

@THIS

D=M

@SP

A=M

M=D

@SP

M=M+1

@THAT

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP

D=M

@5

D=D-A

@1

D=D-A

@ARG

M=D

@SP

D=M

@LCL

M=D

@Main.fibonacci

0;JMP

(RETURN_LABEL1)

@ARG

D=M

@0

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@1

D=A

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

A=A-1

M=M-D

@RETURN_LABEL2

D=A

@SP

A=M

M=D

@SP

M=M+1

@LCL

D=M

@SP

A=M

M=D

@SP

M=M+1

@ARG

D=M

@SP

A=M

M=D

@SP

M=M+1

@THIS

D=M

@SP

A=M

M=D

@SP

M=M+1

@THAT

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP

D=M

@5

D=D-A

@1

D=D-A

@ARG

M=D

@SP

D=M

@LCL

M=D

@Main.fibonacci

0;JMP

(RETURN_LABEL2)

@SP

AM=M-1

D=M

A=A-1

M=M+D

@LCL

D=M

@FRAME

M=D

@5

A=D-A

D=M

@RET

M=D

@ARG

D=M

@0

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@ARG

D=M

@SP

M=D+1

@FRAME

D=M-1

AM=D

D=M

@THAT

M=D

@FRAME

D=M-1

AM=D

D=M

@THIS

M=D

@FRAME

D=M-1

AM=D

D=M

@ARG

M=D

@FRAME

D=M-1

AM=D

D=M

@LCL

M=D

@RET

A=M

0;JMP

(Sys.init)

@4

D=A

@SP

A=M

M=D

@SP

M=M+1

@RETURN_LABEL3

D=A

@SP

A=M

M=D

@SP

M=M+1

@LCL

D=M

@SP

A=M

M=D

@SP

M=M+1

@ARG

D=M

@SP

A=M

M=D

@SP

M=M+1

@THIS

D=M

@SP

A=M

M=D

@SP

M=M+1

@THAT

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP

D=M

@5

D=D-A

@1

D=D-A

@ARG

M=D

@SP

D=M

@LCL

M=D

@Main.fibonacci

0;JMP

(RETURN_LABEL3)

(WHILE)

@WHILE

0;JMP



Resultados obtenidos

FibonacciElement prueba el manejo de los comandos de llamada de funciones de la máquina virtual, la sección de arranque y la mayoría de los otros comandos de la máquina virtual.

NestedCall.asm

@256

D=A

@SP

M=D

@RET_ADDRESS.1

D=A

@SP

A=M

M=D

@SP

M=M+1

@LCL

D=M

@SP

A=M

M=D

@SP

M=M+1

@ARG

D=M

@SP

A=M

M=D

@SP

M=M+1

@THIS

D=M

@SP

A=M

M=D

@SP

M=M+1

@THAT

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP

D=M

@0

D=D-A

@5

D=D-A

@ARG

M=D

@SP

D=M

@LCL

M=D

@Sys.init

0;JMP

(RET_ADDRESS.1)

(Sys.init)

@0

D=A

(LOOP.ADD_LOCALS.1)

@NO_LOCALS.1

D;JEQ

@SP

A=M

M=0

@SP

M=M+1

D=D-1

@LOOP.ADD_LOCALS.1

D;JNE

(NO_LOCALS.1)

@4000

D=A

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

@R13

M=D

@3

D=A

@0

D=D+A

@R14

M=D

@R13

D=M

@R14

A=M

M=D

@5000

D=A

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

@R13

M=D

@3

D=A

@1

D=D+A

@R14

M=D

@R13

D=M

@R14

A=M

M=D

@RET_ADDRESS.2

D=A

@SP

A=M

M=D

@SP

M=M+1

@LCL

D=M

@SP

A=M

M=D

@SP

M=M+1

@ARG

D=M

@SP

A=M

M=D

@SP

M=M+1

@THIS

D=M

@SP

A=M

M=D

@SP

M=M+1

@THAT

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP

D=M

@0

D=D-A

@5

D=D-A

@ARG

M=D

@SP

D=M

@LCL

M=D

@Sys.main

0;JMP

(RET_ADDRESS.2)

@SP

AM=M-1

D=M

@R13

M=D

@5

D=A

@1

D=D+A

@R14

M=D

@R13

D=M

@R14

A=M

M=D

(LOOP)

@LOOP

0;JMP

(Sys.main)

@5

D=A

(LOOP.ADD_LOCALS.2)

@NO_LOCALS.2

D;JEQ

@SP

A=M

M=0

@SP

M=M+1

D=D-1

@LOOP.ADD_LOCALS.2

D;JNE

(NO_LOCALS.2)

@4001

D=A

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

@R13

M=D

@3

D=A

@0

D=D+A

@R14

M=D

@R13

D=M

@R14

A=M

M=D

@5001

D=A

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

@R13

M=D

@3

D=A

@1

D=D+A

@R14

M=D

@R13

D=M

@R14

A=M

M=D

@200

D=A

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

@R13

M=D

@1

D=M

@1

D=D+A

@R14

M=D

@R13

D=M

@R14

A=M

M=D

@40

D=A

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

@R13

M=D

@1

D=M

@2

D=D+A

@R14

M=D

@R13

D=M

@R14

A=M

M=D

@6

D=A

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

@R13

M=D

@1

D=M

@3

D=D+A

@R14

M=D

@R13

D=M

@R14

A=M

M=D

@123

D=A

@SP

A=M

M=D

@SP

M=M+1

@RET_ADDRESS.3

D=A

@SP

A=M

M=D

@SP

M=M+1

@LCL

D=M

@SP

A=M

M=D

@SP

M=M+1

@ARG

D=M

@SP

A=M

M=D

@SP

M=M+1

@THIS

D=M

@SP

A=M

M=D

@SP

M=M+1

@THAT

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP

D=M

@1

D=D-A

@5

D=D-A

@ARG

M=D

@SP

D=M

@LCL

M=D

@Sys.add12

0;JMP

(RET_ADDRESS.3)

@SP

AM=M-1

D=M

@R13

M=D

@5

D=A

@0

D=D+A

@R14

M=D

@R13

D=M

@R14

A=M

M=D

@1

D=M

@0

D=D+A

A=D

D=M

@SP

A=M

M=D

@SP

M=M+1

@1

D=M

@1

D=D+A

A=D

D=M

@SP

A=M

M=D

@SP

M=M+1

@1

D=M

@2

D=D+A

A=D

D=M

@SP

A=M

M=D

@SP

M=M+1

@1

D=M

@3

D=D+A

A=D

D=M

@SP

A=M

M=D

@SP

M=M+1

@1

D=M

@4

D=D+A

A=D

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

@SP

AM=M-1

M=M+D

@SP

M=M+1

@SP

AM=M-1

D=M

@SP

AM=M-1

M=M+D

@SP

M=M+1

@SP

AM=M-1

D=M

@SP

AM=M-1

M=M+D

@SP

M=M+1

@SP

AM=M-1

D=M

@SP

AM=M-1

M=M+D

@SP

M=M+1

@LCL

D=M

@R13

M=D

@5

D=A

@R13

A=M-D

D=M

@R14

M=D

@SP

AM=M-1

D=M

@ARG

A=M

M=D

@ARG

D=M+1

@SP

M=D

@1

D=A

@R13

A=M-D

D=M

@THAT

M=D

@2

D=A

@R13

A=M-D

D=M

@THIS

M=D

@3

D=A

@R13

A=M-D

D=M

@ARG

M=D

@4

D=A

@R13

A=M-D

D=M

@LCL

M=D

@R14

A=M

0;JMP

(Sys.add12)

@0

D=A

(LOOP.ADD_LOCALS.3)

@NO_LOCALS.3

D;JEQ

@SP

A=M

M=0

@SP

M=M+1

D=D-1

@LOOP.ADD_LOCALS.3

D;JNE

(NO_LOCALS.3)

@4002

D=A

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

@R13

M=D

@3

D=A

@0

D=D+A

@R14

M=D

@R13

D=M

@R14

A=M

M=D

@5002

D=A

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

@R13

M=D

@3

D=A

@1

D=D+A

@R14

M=D

@R13

D=M

@R14

A=M

M=D

@2

D=M

@0

D=D+A

A=D

D=M

@SP

A=M

M=D

@SP

M=M+1

@12

D=A

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

@SP

AM=M-1

M=M+D

@SP

M=M+1

@LCL

D=M

@R13

M=D

@5

D=A

@R13

A=M-D

D=M

@R14

M=D

@SP

AM=M-1

D=M

@ARG

A=M

M=D

@ARG

D=M+1

@SP

M=D

@1

D=A

@R13

A=M-D

D=M

@THAT

M=D

@2

D=A

@R13

A=M-D

D=M

@THIS

M=D

@3

D=A

@R13

A=M-D

D=M

@ARG

M=D

@4

D=A

@R13

A=M-D

D=M

@LCL

M=D

@R14

A=M

0;JMP

Resultados obtenidos

NestedCall prueba varios requisitos del protocolo de llamada de función.

SimpleFunction.asm

(SimpleFunction.test)

@2

D=A

(LOOP.ADD_LOCALS.1)

@SP

A=M

M=0

@SP

M=M+1

D=D-1

@LOOP.ADD_LOCALS.1

D;JNE

@1

D=M

@0

D=D+A

A=D

D=M

@SP

A=M

M=D

@SP

M=M+1

@1

D=M

@1

D=D+A

A=D

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

@SP

AM=M-1

M=M+D

@SP

M=M+1

@SP

AM=M-1

M=!M

@SP

M=M+1

@2

D=M

@0

D=D+A

A=D

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

@SP

AM=M-1

M=M+D

@SP

M=M+1

@2

D=M

@1

D=D+A

A=D

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

@SP

AM=M-1

M=M-D

@SP

M=M+1

@LCL

D=M

@R5

M=D

@5

D=A

@R5

A=M-D

D=M

@R6

M=D

@SP

AM=M-1

D=M

@ARG

A=M

M=D

@ARG

D=M+1

@SP

M=D

@1

D=A

@R5

A=M-D

D=M

@THAT

M=D

@2

D=A

@R5

A=M-D

D=M

@THIS

M=D

@3

D=A

@R5

A=M-D

D=M

@ARG

M=D

@4

D=A

@R5

A=M-D

D=M

@LCL

M=D

@R6

A=M

0;JMP

Resultados obtenidos

SimpleFunction realiza un cálculo simple y devuelve el resultado. Este programa proporciona una prueba básica de la implementación de la función y el retorno de los comandos de VM.

StaticsTest.asm

@256

D=A

@SP

M=D

@RETURN_LABEL0

D=A

@SP

A=M

M=D

@SP

M=M+1

@LCL

D=M

@SP

A=M

M=D

@SP

M=M+1

@ARG

D=M

@SP

A=M

M=D

@SP

M=M+1

@THIS

D=M

@SP

A=M

M=D

@SP

M=M+1

@THAT

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP

D=M

@5

D=D-A

@0

D=D-A

@ARG

M=D

@SP

D=M

@LCL

M=D

@Sys.init

0;JMP

(RETURN_LABEL0)

(Class1.set)

@ARG

D=M

@0

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@Class1.vm0

D=A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@ARG

D=M

@1

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@Class1.vm1

D=A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@0

D=A

@SP

A=M

M=D

@SP

M=M+1

@LCL

D=M

@R11

M=D

@5

A=D-A

D=M

@R12

M=D

@ARG

D=M

@0

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@ARG

D=M

@SP

M=D+1

@R11

D=M-1

AM=D

D=M

@THAT

M=D

@R11

D=M-1

AM=D

D=M

@THIS

M=D

@R11

D=M-1

AM=D

D=M

@ARG

M=D

@R11

D=M-1

AM=D

D=M

@LCL

M=D

@R12

A=M

0;JMP

(Class1.get)

@Class1.vm0

D=M

@SP

A=M

M=D

@SP

M=M+1

@Class1.vm1

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

A=A-1

M=M-D

@LCL

D=M

@R11

M=D

@5

A=D-A

D=M

@R12

M=D

@ARG

D=M

@0

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@ARG

D=M

@SP

M=D+1

@R11

D=M-1

AM=D

D=M

@THAT

M=D

@R11

D=M-1

AM=D

D=M

@THIS

M=D

@R11

D=M-1

AM=D

D=M

@ARG

M=D

@R11

D=M-1

AM=D

D=M

@LCL

M=D

@R12

A=M

0;JMP

(Class2.set)

@ARG

D=M

@0

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@Class2.vm0

D=A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@ARG

D=M

@1

A=D+A

D=M

@SP

A=M

M=D

@SP

M=M+1

@Class2.vm1

D=A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@0

D=A

@SP

A=M

M=D

@SP

M=M+1

@LCL

D=M

@R11

M=D

@5

A=D-A

D=M

@R12

M=D

@ARG

D=M

@0

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@ARG

D=M

@SP

M=D+1

@R11

D=M-1

AM=D

D=M

@THAT

M=D

@R11

D=M-1

AM=D

D=M

@THIS

M=D

@R11

D=M-1

AM=D

D=M

@ARG

M=D

@R11

D=M-1

AM=D

D=M

@LCL

M=D

@R12

A=M

0;JMP

(Class2.get)

@Class2.vm0

D=M

@SP

A=M

M=D

@SP

M=M+1

@Class2.vm1

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP

AM=M-1

D=M

A=A-1

M=M-D

@LCL

D=M

@R11

M=D

@5

A=D-A

D=M

@R12

M=D

@ARG

D=M

@0

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@ARG

D=M

@SP

M=D+1

@R11

D=M-1

AM=D

D=M

@THAT

M=D

@R11

D=M-1

AM=D

D=M

@THIS

M=D

@R11

D=M-1

AM=D

D=M

@ARG

M=D

@R11

D=M-1

AM=D

D=M

@LCL

M=D

@R12

A=M

0;JMP

(Sys.init)

@6

D=A

@SP

A=M

M=D

@SP

M=M+1

@8

D=A

@SP

A=M

M=D

@SP

M=M+1

@RETURN_LABEL1

D=A

@SP

A=M

M=D

@SP

M=M+1

@LCL

D=M

@SP

A=M

M=D

@SP

M=M+1

@ARG

D=M

@SP

A=M

M=D

@SP

M=M+1

@THIS

D=M

@SP

A=M

M=D

@SP

M=M+1

@THAT

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP

D=M

@5

D=D-A

@2

D=D-A

@ARG

M=D

@SP

D=M

@LCL

M=D

@Class1.set

0;JMP

(RETURN_LABEL1)

@R5

D=M

@5

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@23

D=A

@SP

A=M

M=D

@SP

M=M+1

@15

D=A

@SP

A=M

M=D

@SP

M=M+1

@RETURN_LABEL2

D=A

@SP

A=M

M=D

@SP

M=M+1

@LCL

D=M

@SP

A=M

M=D

@SP

M=M+1

@ARG

D=M

@SP

A=M

M=D

@SP

M=M+1

@THIS

D=M

@SP

A=M

M=D

@SP

M=M+1

@THAT

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP

D=M

@5

D=D-A

@2

D=D-A

@ARG

M=D

@SP

D=M

@LCL

M=D

@Class2.set

0;JMP

(RETURN_LABEL2)

@R5

D=M

@5

D=D+A

@R13

M=D

@SP

AM=M-1

D=M

@R13

A=M

M=D

@RETURN_LABEL3

D=A

@SP

A=M

M=D

@SP

M=M+1

@LCL

D=M

@SP

A=M

M=D

@SP

M=M+1

@ARG

D=M

@SP

A=M

M=D

@SP

M=M+1

@THIS

D=M

@SP

A=M

M=D

@SP

M=M+1

@THAT

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP

D=M

@5

D=D-A

@0

D=D-A

@ARG

M=D

@SP

D=M

@LCL

M=D

@Class1.get

0;JMP

(RETURN_LABEL3)

@RETURN_LABEL4

D=A

@SP

A=M

M=D

@SP

M=M+1

@LCL

D=M

@SP

A=M

M=D

@SP

M=M+1

@ARG

D=M

@SP

A=M

M=D

@SP

M=M+1

@THIS

D=M

@SP

A=M

M=D

@SP

M=M+1

@THAT

D=M

@SP

A=M

M=D

@SP

M=M+1

@SP

D=M

@5

D=D-A

@0

D=D-A

@ARG

M=D

@SP

D=M

@LCL

M=D

@Class2.get

0;JMP

(RETURN_LABEL4)

(WHILE)

@WHILE

0;JMP


Resultados obtenidos

StaticsTest contiene funciones de VM diseñadas para establecer y obtener varios valores estáticos, esto se hace para probar el manejo del segmento de memoria estática.