Práctica 9: Sistema Operativo

Introducción

El front-end del compilador, como bien se dijo anteriormente consta de un analizador de sintaxis, desarrollado en la práctica anterior, y un generador de código, el tema a tratar en la primera parte de la actual practica. Específicamente, en la practica 8 creamos un analizador de sintaxis capaz de "comprender" (analizar) los programas de Jack. Para el proyecto 11 abarcado en la actual practica tenemos como objetivo ampliar el analizador a un compilador a gran escala que convierte cada construcción de alto nivel "entendido" en una serie equivalente de operaciones de VM.

Por otro lado tenemos el proyecto 12 en el cual trataremos la última interfaz importante que falta, el sistema operativo (SO). El sistema operativo está diseñado para "cerrar" las brechas entre el software y el hardware de los computadores, y para hacer que el computador en general sea más accesible tanto para los programadores como para los usuarios por lo que el objetivo principal de este proyecto es implementar el sistema operativo Jack.

Proyecto 11. Compiler II - Code Generation

Desarrollo

Primeramente hay que tratar los temas necesarios para completar el desarrollo del compilador: administrar una tabla de símbolos; representar y generar código para variables, objetos y matrices.

Tabla de símbolos: los programas de alto nivel introducen y manipulan muchos identificadores. Cada vez que el compilador encuentra un identificador, digamos xxx, necesita saber qué significa xxx. ¿Es un nombre de variable, un nombre de clase o un nombre de función? Si es una variable, ¿Qué tipo de variable es: un entero, un valor booleano o quizás un carácter? El compilador debe resolver estas preguntas antes de poder representar la semántica de xxx en el idioma de destino. Además, todas estas preguntas deben responderse cada vez que se encuentre xxx en el código fuente. Claramente, es necesario realizar un seguimiento de todos los identificadores introducidos por el programa y, para cada uno, registrar qué significa el identificador en el programa fuente y en qué construcción se mapea en el idioma de destino. La mayoría de los compiladores mantienen esta información utilizando una abstracción de tabla de símbolos.


Figura 1. Tabla de símbolos.


Manejo de variables: ¿Cómo mapear los diversos tipos de variables declaradas en el programa fuente en la memoria de la plataforma de destino? Primero, los diferentes tipos de variables requieren diferentes tamaños de fragmentos de memoria, por lo que el mapeo no es uno a uno. En segundo lugar, diferentes tipos de variables tienen diferentes ciclos de vida. Por el contrario, cada instancia de objeto de una clase debe tener una copia diferente de todas sus variables de instancia (campos) y, cuando se desecha, la memoria del objeto debe reciclarse. Además, cada vez que se llama a una subrutina, se deben crear nuevas copias de sus variables locales y de argumento, una necesidad que se ve claramente en la recursividad. La buena noticia es que ya hemos manejado todas estas dificultades. La asignación de memoria de las variables se delegó en la máquina virtual de los proyectos 7 y 8. Lo único que se requiere del compilador es mapear las variables que se encuentran en el programa fuente en los segmentos de memoria virtual y expresar los comandos de alto nivel que los manipulan usando comandos de VM.

Manejo de matrices: las matrices casi siempre se almacenan como secuencias de ubicaciones de memoria consecutivas (las matrices multidimensionales se aplanan en unidades unidimensionales). El nombre de la matriz generalmente se trata como un puntero a la dirección base del bloque de RAM asignado para almacenar la matriz en la memoria. La matriz propiamente dicha se crea en la memoria más tarde, siempre y cuando la matriz se construya realmente en tiempo de ejecución. Por lo general, el sistema operativo tiene una función de asignación (tamaño) que sabe cómo encontrar un bloque de memoria disponible de tamaño y devolver su dirección base a la persona que llama. Por lo tanto, al compilar una instrucción de alto nivel como bar=new int [10], el compilador genera código de bajo nivel que efectúa la operación bar=alloc(10). Esto da como resultado la asignación de la dirección base del bloque de memoria de la matriz a la barra, que es exactamente lo que se busca.


Figura 2. Manejo de matrices.


Por lo tanto basándonos en lo anterior y del proyecto 10, el compilador general se puede construir utilizando cinco clases:

  • JackCompiler: esta clase funciona como un controlador de nivel superior que configura e invoca las otras clases.

  • JackTokenizer: tokenizador.

  • SymbolTable: tabla de símbolos.

  • VMWriter: clase para generar código VM.

  • CompilationEngine: esta clase hace la compilación por sí misma. Lee la entrada del JackTokenizer y escribe su salida en un VMWriter.

Figura 3. Estructura del compilador Jack para el proyecto 11

CompilationEngine.py

from SymbolTable import SymbolTable

from VMWriter import VMWriter

from LabelCounter import LabelCounter

from Operator import Operator


class CompilationEngine():

"""

compila un archivo fuente jack de un tokenizador jack en formato xml en archivo_salida

"""

SYMBOL_KINDS = {

'parameter_list': 'argument',

'var_dec': 'local'

}

CLASS_VAR_DEC_TOKENS = [ "static", "field" ]

SUBROUTINE_TOKENS = [ "function", "method", "constructor" ]

STATEMENT_TOKENS = [ 'do', 'let', 'while', 'return', 'if' ]

STARTING_TOKENS = {

'var_dec': ['var'],

'parameter_list': ['('],

'subroutine_body': ['{'],

'expression_list': ['('],

'expression': ['=', '[', '('],

'array': [ '[' ],

'conditional': ['if', 'else']

}

TERMINATING_TOKENS = {

'class': ['}'],

'class_var_dec': [';'],

'subroutine': ['}'],

'parameter_list': [')'],

'expression_list': [')'],

'statements': ['}'],

'do': [';'],

'let': [';'],

'while': ['}'],

'if': ['}'],

'var_dec': [';'],

'return': [';'],

'expression': [';', ')', ']', ','],

'array': [']']

}

OPERATORS = [

'+',

'-',

'*',

'/',

'&',

'|',

'<',

'>',

'='

]

UNARY_OPERATORS = [ '-', '~' ]

TOKENS_THAT_NEED_LABELS = ['if', 'while']


def __init__(self, tokenizer, output_file):

self.tokenizer = tokenizer

self.output_file = output_file

self.class_symbol_table = SymbolTable()

self.subroutine_symbol_table = SymbolTable()

self.vm_writer = VMWriter(output_file)

self.label_counter = LabelCounter(labels=self.TOKENS_THAT_NEED_LABELS)

self.class_name = None


def compile_class(self):

"""

todo lo necesario para compilar una clase, la unidad básica de compilación

"""

# saltar todo hasta el comienzo de la clase

while not self.tokenizer.class_token_reached():

self.tokenizer.advance()

# dado que la unidad de compilación es una clase, tiene sentido almacenar esto como variable de instancia

self.class_name = self.tokenizer.next_token


while self.tokenizer.has_more_tokens:

self.tokenizer.advance()


if self.tokenizer.current_token in self.CLASS_VAR_DEC_TOKENS:

self.compile_class_var_dec()

elif self.tokenizer.current_token in self.SUBROUTINE_TOKENS:

self.compile_subroutine()


def compile_class_var_dec(self):

"""

ejemplo: field int x;

"""

symbol_kind = self.tokenizer.keyword()


# obtener tipo de símbolo

self.tokenizer.advance()

symbol_type = self.tokenizer.keyword()


# get all identifiers

while self._not_terminal_token_for('class_var_dec'):

self.tokenizer.advance()


if self.tokenizer.identifier():

# add symbol to class

symbol_name = self.tokenizer.identifier()

self.class_symbol_table.define(

name=symbol_name,

kind=symbol_kind,

symbol_type=symbol_type

)


def compile_subroutine(self):

"""

ejemplo: methoid void dispose() { ...

"""

# nueva subrutina significa nuevo alcance de subrutina

self.subroutine_symbol_table.reset()


# obtener el nombre de la subrutina

self.tokenizer.advance()

self.tokenizer.advance()

subroutine_name = self.tokenizer.current_token


# lista de parámetros de compilación

self.tokenizer.advance()

self.compile_parameter_list()


# compilar cuerpo

self.tokenizer.advance()

self.compile_subroutine_body(subroutine_name=subroutine_name)


# el resto cuenta desde la subrutina

self.label_counter.reset_counts()


def compile_subroutine_body(self, subroutine_name):

self.tokenizer.advance()

num_locals = 0

while self._starting_token_for('var_dec'):

num_locals += self.compile_var_dec()

self.tokenizer.advance()


self.vm_writer.write_function(

name='{}.{}'.format(self.class_name, subroutine_name),

num_locals=num_locals

)


while self._not_terminal_token_for('subroutine'):

self.compile_statements()


def compile_parameter_list(self):

"""

ejemplo: disponer(int a, int b)

devuelve el número de parámetros encontrados

"""

### symbol table

while self._not_terminal_token_for('parameter_list'):

self.tokenizer.advance()


# symbol table

if self.tokenizer.token_type_of(self.tokenizer.next_token) == "IDENTIFIER":

symbol_kind = self.SYMBOL_KINDS['parameter_list']

symbol_type = self.tokenizer.current_token

symbol_name = self.tokenizer.next_token

self.subroutine_symbol_table.define(

name=symbol_name,

kind=symbol_kind,

symbol_type=symbol_type

)


# 'var' type varName (',' varName)* ';'

def compile_var_dec(self):

"""

ejemplo: var int a;

"""

self.tokenizer.advance()

symbol_type = self.tokenizer.current_token

num_vars = 0


while self._not_terminal_token_for('var_dec'):

self.tokenizer.advance()


if self.tokenizer.identifier():

num_vars += 1

symbol_kind = self.SYMBOL_KINDS['var_dec']

symbol_name = self.tokenizer.identifier()

self.subroutine_symbol_table.define(

name=symbol_name,

kind=symbol_kind,

symbol_type=symbol_type

)

return num_vars


def compile_statements(self):

"""

llamar la declaración correcta

"""

statement_compile_methods = {

'if': self.compile_if,

'do': self.compile_do,

'let': self.compile_let,

'while': self.compile_while,

'return': self.compile_return

}


while self._not_terminal_token_for('subroutine'):

if self.tokenizer.current_token in self.STATEMENT_TOKENS:

statement_type = self.tokenizer.current_token

statement_compile_methods[statement_type]()


self.tokenizer.advance()


def compile_do(self):

"""

ejemplo: do square.dispose();

"""

self.tokenizer.advance()

caller_name = self.tokenizer.current_token

symbol = self._find_symbol_in_symbol_tables(symbol_name=caller_name)

self.tokenizer.advance()

self.tokenizer.advance()

subroutine_name = self.tokenizer.current_token


if symbol: # user defined Method

segment = 'local'

index = symbol['index']

symbol_type = symbol['type']

self.vm_writer.write_push(segment=segment, index=index)

else:

symbol_type = caller_name


subroutine_call_name = symbol_type + '.' + subroutine_name

self.tokenizer.advance()

num_args = self.compile_expression_list()

if symbol:

num_args += 1

self.vm_writer.write_call(name=subroutine_call_name, num_args=num_args)

self.vm_writer.write_pop(segment='temp', index='0')


def compile_let(self):

self.tokenizer.advance()

symbol_name = self.tokenizer.current_token

symbol = self._find_symbol_in_symbol_tables(symbol_name=symbol_name)


array_assignment = self._starting_token_for(keyword_token='array', position='next')

if array_assignment:

self.tokenizer.advance()

self.tokenizer.advance()

self.compile_expression()

self.vm_writer.write_push(segment=symbol['kind'], index=symbol['index'])

self.vm_writer.write_arithmetic(command='+')


while not self.tokenizer.current_token == '=':

self.tokenizer.advance()

while self._not_terminal_token_for('let'):

self.tokenizer.advance()

self.compile_expression()


if not array_assignment:

# store expression evaluation in symbol location

self.vm_writer.write_pop(segment=symbol['kind'], index=symbol['index'])

else:

self.vm_writer.write_pop(segment='temp', index='0')

self.vm_writer.write_pop(segment='pointer', index='1') # pointer 1 => array

self.vm_writer.write_push(segment='temp', index='0')

self.vm_writer.write_pop(segment='that', index='0')



def compile_while(self):

"""

example: while (x > 0) { ... }

"""

self.vm_writer.write_label(label='WHILE_EXP{}'.format(self.label_counter.get('while')))


self.tokenizer.advance()

self.tokenizer.advance()


self.compile_expression()


self.vm_writer.write_unary(command='~')

self.vm_writer.write_ifgoto(label='WHILE_END{}'.format(self.label_counter.get('while')))


while self._not_terminal_token_for('while'):

self.tokenizer.advance()


if self._statement_token():

self.compile_statements()


self.vm_writer.write_goto(label='WHILE_EXP{}'.format(self.label_counter.get('while')))

self.vm_writer.write_label(label='WHILE_END{}'.format(self.label_counter.get('while')))


self.label_counter.increment('while')


def compile_if(self):

self.tokenizer.advance()

self.tokenizer.advance()

self.compile_expression()

self.vm_writer.write_ifgoto(label='IF_TRUE{}'.format(self.label_counter.get('if')))

self.vm_writer.write_goto(label='IF_FALSE{}'.format(self.label_counter.get('if')))

self.vm_writer.write_label(label='IF_TRUE{}'.format(self.label_counter.get('if')))

self.compile_conditional_body()

if self._starting_token_for(keyword_token='conditional', position='next'):

self.tokenizer.advance()

self.vm_writer.write_goto(label='IF_END{}'.format(self.label_counter.get('if')))

self.vm_writer.write_label(label='IF_FALSE{}'.format(self.label_counter.get('if')))

self.compile_conditional_body()

self.vm_writer.write_label(label='IF_END{}'.format(self.label_counter.get('if')))

else:

self.vm_writer.write_label(label='IF_FALSE{}'.format(self.label_counter.get('if')))


def compile_conditional_body(self):

while self._not_terminal_token_for('if'):

self.tokenizer.advance()


if self._statement_token():

if self.tokenizer.current_token == 'if':

self.label_counter.increment('if')

self.compile_statements()

self.label_counter.decrement('if')

else:

self.compile_statements()


def compile_expression(self):

ops = []


while self._not_terminal_token_for('expression'):

if self._subroutine_call():

self.compile_subroutine_call()

elif self._array_expression():

self.compile_array_expression()

elif self.tokenizer.current_token.isdigit():

self.vm_writer.write_push(segment='constant', index=self.tokenizer.current_token)

elif self.tokenizer.identifier():

self.compile_symbol_push()

elif self.tokenizer.current_token in self.OPERATORS and not self._part_of_expression_list():

ops.insert(0, Operator(token=self.tokenizer.current_token, category='bi'))

elif self.tokenizer.current_token in self.UNARY_OPERATORS:

ops.insert(0, Operator(token=self.tokenizer.current_token, category='unary'))

elif self.tokenizer.string_const():

self.compile_string_const()

elif self.tokenizer.boolean(): # boolean case

self.compile_boolean()

elif self._starting_token_for('expression'): # nested expression

# skip starting (

self.tokenizer.advance()

self.compile_expression()

elif self.tokenizer.null():

self.vm_writer.write_push(segment='constant', index=0)


self.tokenizer.advance()


for op in ops:

self.compile_op(op)


def compile_op(self, op):

if op.unary():

self.vm_writer.write_unary(command=op.token)

elif op.multiplication():

self.vm_writer.write_call(name='Math.multiply', num_args=2)

elif op.division():

self.vm_writer.write_call(name='Math.divide', num_args=2)

else:

self.vm_writer.write_arithmetic(command=op.token)


def compile_boolean(self):

self.vm_writer.write_push(segment='constant', index=0)


if self.tokenizer.boolean() == 'true':

# negate true

self.vm_writer.write_unary(command='~')


def compile_string_const(self):

string_length = len(self.tokenizer.string_const())

self.vm_writer.write_push(segment='constant', index=string_length)

self.vm_writer.write_call(name='String.new', num_args=1)

for char in self.tokenizer.string_const():

if not char == self.tokenizer.STRING_CONST_DELIMITER:

ascii_value_of_char = ord(char)

self.vm_writer.write_push(segment='constant', index=ascii_value_of_char)

self.vm_writer.write_call(name='String.appendChar', num_args=2)


def compile_symbol_push(self):

symbol = self._find_symbol_in_symbol_tables(symbol_name=self.tokenizer.identifier())

segment = symbol['kind']

index = symbol['index']

self.vm_writer.write_push(segment=segment, index=index)


def compile_array_expression(self):

symbol_name = self.tokenizer.current_token

symbol = self._find_symbol_in_symbol_tables(symbol_name=symbol_name)

self.tokenizer.advance()

self.tokenizer.advance()

self.compile_expression()

self.vm_writer.write_push(segment='local', index=symbol['index'])

self.vm_writer.write_arithmetic(command='+')

self.vm_writer.write_pop(segment='pointer', index=1)

self.vm_writer.write_push(segment='that', index=0)


def compile_subroutine_call(self):

subroutine_name = ''


while not self._starting_token_for('expression_list'):

subroutine_name += self.tokenizer.current_token

self.tokenizer.advance()

num_args = self.compile_expression_list()

self.vm_writer.write_call(name=subroutine_name, num_args=num_args)


# (expression (',' expression)* )?

def compile_expression_list(self):

num_args = 0


if self._empty_expression_list():

return num_args


self.tokenizer.advance()


while self._not_terminal_token_for('expression_list'):

num_args += 1

self.compile_expression()

if self._another_expression_coming(): # would be , after compile expression

self.tokenizer.advance()

return num_args


def compile_return(self):

if self._not_terminal_token_for(keyword_token='return', position='next'):

self.compile_expression()

else: # push constant for void

self.vm_writer.write_push(segment='constant', index='0')

self.tokenizer.advance()


self.vm_writer.write_return()


def _not_terminal_token_for(self, keyword_token, position='current'):

if position == 'current':

return not self.tokenizer.current_token in self.TERMINATING_TOKENS[keyword_token]

elif position == 'next':

return not self.tokenizer.next_token in self.TERMINATING_TOKENS[keyword_token]


def _starting_token_for(self, keyword_token, position='current'):

if position == 'current':

return self.tokenizer.current_token in self.STARTING_TOKENS[keyword_token]

elif position == 'next':

return self.tokenizer.next_token in self.STARTING_TOKENS[keyword_token]


def _statement_token(self):

return self.tokenizer.current_token in self.STATEMENT_TOKENS


def _operator_token(self, position='current'):

if position == 'current':

return self.tokenizer.current_token in self.OPERATORS

elif position == 'next':

return self.tokenizer.next_token in self.OPERATORS


def _another_expression_coming(self):

return self.tokenizer.current_token == ","


def _find_symbol_in_symbol_tables(self, symbol_name):

if self.subroutine_symbol_table.find_symbol_by_name(symbol_name):

return self.subroutine_symbol_table.find_symbol_by_name(symbol_name)

elif self.class_symbol_table.find_symbol_by_name(symbol_name):

return self.class_symbol_table.find_symbol_by_name(symbol_name)


def _empty_expression_list(self):

return self._start_of_expression_list() and self._next_ends_expression_list()


def _start_of_expression_list(self):

return self.tokenizer.current_token in self.STARTING_TOKENS['expression_list']


def _next_ends_expression_list(self):

return self.tokenizer.next_token in self.TERMINATING_TOKENS['expression_list']


def _subroutine_call(self):

return self.tokenizer.identifier() and self.tokenizer.next_token == '.'


def _array_expression(self):

return self.tokenizer.identifier() and self._starting_token_for(keyword_token='array', position='next')


def _part_of_expression_list(self):

return self.tokenizer.tokens_found[-3] in [',', '(']

JackCompiler.py

from JackTokenizer import JackTokenizer

from CompilationEngine import CompilationEngine

import sys

import os

import glob


class JackCompiler():

@classmethod

def run(cls, input_file, output_file):

tokenizer = JackTokenizer(input_file)

compiler = CompilationEngine(tokenizer, output_file)

compiler.compile_class()


@classmethod

def output_file_for(cls, input_file, ext_name='.vm'):

file_name = os.path.basename(input_file).split(".")[0]

dir_name = os.path.dirname(input_file).replace('./', '')

try:

os.mkdir("./compiled/{}".format(dir_name))

except OSError:

print("res directory already exists. continuing")


return dir_name + "/" + file_name + ext_name


if __name__ == "__main__" and len(sys.argv) == 2:

arg = sys.argv[1]


if os.path.isfile(arg):

files = [arg]

elif os.path.isdir(arg):

jack_path = os.path.join(arg, "*.jack")

files = glob.glob(jack_path)


try:

os.mkdir("./compiled")

except OSError:

print("output directory already exists. continuing")


for input_file_name in files:

output_file_name = JackCompiler.output_file_for(input_file_name)

output_file = open(output_file_name, 'w')

input_file = open(input_file_name, 'r')

JackCompiler.run(input_file, output_file)


JackTokenizer.py

class JackTokenizer():

KEYWORDS = [

'class',

'constructor',

'function',

'method',

'field',

'static',

'var',

'int',

'char',

'boolean',

'void',

'true',

'false',

'null',

'this',

'let',

'do',

'if',

'else',

'while',

'return'

]

COMMENT_OPERATORS = ["/", "*"]

STRING_CONST_DELIMITER = '"'


def __init__(self, input_file):

self.input_file = input_file

self.tokens_found = []

self.current_token = None

self.next_token = None

self.has_more_tokens = True


def advance(self):

char = self.input_file.read(1)


while char.isspace() or char in self.COMMENT_OPERATORS:

if char.isspace():

char = self.input_file.read(1)

elif char in self.COMMENT_OPERATORS:

last_pos = self.input_file.tell()

rest_of_line = self.input_file.readline()

self.input_file.seek(last_pos)

if not self._is_start_of_comment(char, rest_of_line):

self.input_file.seek(last_pos)

break

self.input_file.readline()

char = self.input_file.read(1)

continue


token = ""


if self._is_string_const_delimeter(char):

token += char

char = self.input_file.read(1)


while not self._is_string_const_delimeter(char):

token += char

char = self.input_file.read(1)


# get last "

token += char

elif char.isalnum():

while self._is_alnum_or_underscore(char):

token += char

last_pos = self.input_file.tell()

char = self.input_file.read(1)


self.input_file.seek(last_pos)

else: # symbol

token = char


if self.current_token:

self.current_token = self.next_token

self.next_token = token

self.tokens_found.append(token)

else: # initial setup

self.current_token = token

self.next_token = token

self.tokens_found.append(token)

# update next token

self.advance()


if not len(self.next_token) > 0:

self.has_more_tokens = False

return False

else:

return True


def part_of_subroutine_call(self):

if len(self.tokens_found) < 3:

return False


index = len(self.tokens_found) - 4

token = self.tokens_found[index]


if token == ".":

return True

else:

return False


def class_token_reached(self):

return self.current_token == 'class'


def token_type_of(self, token):

if token[0] == "\"":

return "STRING_CONST"

elif token in self.KEYWORDS:

return "KEYWORD"

elif token.isnumeric():

return "INT_CONST"

elif token.isalnum():

return "IDENTIFIER"

else:

return "SYMBOL"


def null(self):

if self.current_token == 'null':

return self.current_token


def boolean(self):

if self.current_token in ['true', 'false']:

return self.current_token


def keyword(self):

if self.token_type_of(self.current_token) == "KEYWORD":

return self.current_token


def identifier(self):

if self.token_type_of(self.current_token) == "IDENTIFIER":

return self.current_token


def string_const(self):

if self.token_type_of(self.current_token) == "STRING_CONST":

return self.current_token.replace('"', '')


def _is_alnum_or_underscore(self, char):

return char.isalnum() or char == "_"


def _is_string_const_delimeter(self, char):

return char == "\""


def _is_start_of_comment(self, char, rest_of_line):

single_line_comment = rest_of_line[0] == self.COMMENT_OPERATORS[0]

multi_line_comment = char == self.COMMENT_OPERATORS[0] and rest_of_line[0:2] == "**"

part_of_multi_line_comment = self._part_of_multiline_comment()

return single_line_comment or multi_line_comment or part_of_multi_line_comment


def _part_of_multiline_comment(self):

if not self.tokens_found:

return True

elif self.tokens_found[-1] == ';':

return True

else:

return False


VMWriter.py

class VMWriter():

ARITHMETIC_LOGICAL_OPERATORS = {

'+': 'add',

'-': 'sub',

'=': 'eq',

'>': 'gt',

'<': 'lt',

'&': 'and',

'|': 'or'

}

UNARY_OPERATORS = {

'-': 'neg',

'~': 'not'

}


def __init__(self, output_file):

self.output_file = output_file


def write_push(self, segment, index):

self.output_file.write('push {} {}\n'.format(segment, index))


def write_pop(self, segment, index):

self.output_file.write('pop {} {}\n'.format(segment, index))


def write_arithmetic(self, command):

self.output_file.write(

'{}\n'.format(self.ARITHMETIC_LOGICAL_OPERATORS[command])

)


def write_unary(self, command):

self.output_file.write(

'{}\n'.format(self.UNARY_OPERATORS[command])

)


def write_label(self, label):

self.output_file.write('label {}\n'.format(label))


def write_goto(self, label):

self.output_file.write('goto {}\n'.format(label))


def write_ifgoto(self, label):

self.output_file.write('if-goto {}\n'.format(label))


def write_call(self, name, num_args):

self.output_file.write(

'call {} {}\n'.format(name, num_args)

)


def write_function(self, name, num_locals):

self.output_file.write('function {} {}\n'.format(name, num_locals))


def write_return(self):

self.output_file.write('return\n')


SymbolTable.py

class SymbolTable():

"""

Represents the current symbols for a given class or subroutine for the JackCompiler

"""

def __init__(self):

self.symbols = []


def reset(self):

"""

Starts a new subroutine scope, i.e., resets the subroutine's scope

"""

self.symbols = []


def define(self, name, symbol_type, kind):

"""

defines a new identifier of the given name, type, and kind and assigns it a running index

"""

new_symbol = {

'name': name,

'type': symbol_type,

'kind': kind,

'index': self.var_count(kind)

}

self.symbols.append(new_symbol)


def var_count(self, kind):

"""

returns the number of variables of the given kind already defined in the current scope

"""

return sum(symbol['kind'] == kind for symbol in self.symbols)


def kind_of(self, name):

"""

returns the kind of the named identifer in the current scope

(STATIC, FIELD, ARG, VAR, NONE)

"""

return self.find_symbol_by_name(name).get('kind')


def type_of(self, name):

"""

returns the type of the named identifier in the current scope

"""

return self.find_symbol_by_name(name).get('type')


def index_of(self, name):

"""

returns the index assigned to the named identifier

"""

return self.find_symbol_by_name(name).get('index')


def find_symbol_by_name(self, value):

for symbol in self.symbols:

if symbol['name'] == value:

return symbol


Tests

Para testear el compilador se nos proporcionaron los programas:

  • Seven

  • Conversion to binary

  • Square dance

  • Average

  • Pong

  • Complex arrays

Debemos utilizar nuestro compilador para obtener los respectivos archivos .vm para cada unos de los programas .Jack para posteriormente ejecutarlos con la herramienta VMEmulator suministrada por nand2tetris y verificar si el programa se comporta de forma inesperada o el emulador muestra algún mensaje de error.

Resultados

Seven

Este programa calcula el valor de (3*2)+1 e imprime el resultado en la parte superior izquierda de la pantalla. Como vemos que el resultado es 7 se concluye que el compilador tradujo el programa correctamente.

Resultados obtenidos


main.vm

function Main.main 0

push constant 1

push constant 2

push constant 3

call Math.multiply 2

add

call Output.printInt 1

pop temp 0

push constant 0

return


Conversion to binary

Este programa convierte un número decimal de 16 bits en su representación binaria. Para probarlo debemos

  • Poner (interactivamente) un valor decimal de 16 bits en RAM[8000]

  • Ejecutar el programa durante unos segundos y luego pararlo

  • Y por último verificar (interactivamente) que RAM[8001..8016] contenga los resultados correctos y que ninguno de ellos contenga - 1

Resultados obtenidos

En nuestro caso en el valor de RAM[8000] colocamos el valor de 65, por lo que en los valores RAM[8001...8016] obtuvimos 1000001000000000. Realmente la parte que importa es hasta el 1000001 que es en decimal 65.


main.vm

function Main.main 1

push constant 8001

push constant 16

push constant 1

neg

call Main.fillMemory 3

pop temp 0

push constant 8000

call Memory.peek 1

pop local 0

push local 0

call Main.convert 1

pop temp 0

push constant 0

return

function Main.convert 3

push constant 0

not

pop local 2

label WHILE_EXP0

push local 2

not

if-goto WHILE_END0

push local 1

push constant 1

add

pop local 1

push local 0

call Main.nextMask 1

pop local 0

push local 1

push constant 16

gt

not

if-goto IF_TRUE0

goto IF_FALSE0

label IF_TRUE0

push argument 0

push local 0

and

push constant 0

eq

not

if-goto IF_TRUE1

goto IF_FALSE1

label IF_TRUE1

push constant 8000

push local 1

add

push constant 1

call Memory.poke 2

pop temp 0

goto IF_END1

label IF_FALSE1

push constant 8000

push local 1

add

push constant 0

call Memory.poke 2

pop temp 0

label IF_END1

goto IF_END0

label IF_FALSE0

push constant 0

pop local 2

label IF_END0

goto WHILE_EXP0

label WHILE_END0

push constant 0

return

function Main.nextMask 0

push argument 0

push constant 0

eq

if-goto IF_TRUE0

goto IF_FALSE0

label IF_TRUE0

push constant 1

return

goto IF_END0

label IF_FALSE0

push argument 0

push constant 2

call Math.multiply 2

return

label IF_END0

function Main.fillMemory 0

label WHILE_EXP0

push argument 1

push constant 0

gt

not

if-goto WHILE_END0

push argument 0

push argument 2

call Memory.poke 2

pop temp 0

push argument 1

push constant 1

sub

pop argument 1

push argument 0

push constant 1

add

pop argument 0

goto WHILE_EXP0

label WHILE_END0

push constant 0

return


Square dance

Este programa es un “juego” interactivo que permite mover un cuadrado negro alrededor de la pantalla usando las cuatro teclas de flecha del teclado. Mientras se mueve, el tamaño del cuadrado se puede aumentar y disminuir presionando las teclas "z" y "x", respectivamente. Para probarlo debemos asegurarnos de que funcione de acuerdo con esta descripción al momento de ejecutarlo en el VMEmulator.

Resultados obtenidos

main.vm

function Main.main 1

call SquareGame.new 0

pop local 0

push local 0

call SquareGame.run 1

pop temp 0

push local 0

call SquareGame.dispose 1

pop temp 0

push constant 0

return


Square.vm

function Square.new 0

push constant 3

call Memory.alloc 1

pop pointer 0

push argument 0

pop this 0

push argument 1

pop this 1

push argument 2

pop this 2

push pointer 0

call Square.draw 1

pop temp 0

push pointer 0

return

function Square.dispose 0

push argument 0

pop pointer 0

push pointer 0

call Memory.deAlloc 1

pop temp 0

push constant 0

return

function Square.draw 0

push argument 0

pop pointer 0

push constant 0

not

call Screen.setColor 1

pop temp 0

push this 0

push this 1

push this 0

push this 2

add

push this 1

push this 2

add

call Screen.drawRectangle 4

pop temp 0

push constant 0

return

function Square.erase 0

push argument 0

pop pointer 0

push constant 0

call Screen.setColor 1

pop temp 0

push this 0

push this 1

push this 0

push this 2

add

push this 1

push this 2

add

call Screen.drawRectangle 4

pop temp 0

push constant 0

return

function Square.incSize 0

push argument 0

pop pointer 0

push this 1

push this 2

add

push constant 254

lt

push this 0

push this 2

add

push constant 510

lt

and

if-goto IF_TRUE0

goto IF_FALSE0

label IF_TRUE0

push pointer 0

call Square.erase 1

pop temp 0

push this 2

push constant 2

add

pop this 2

push pointer 0

call Square.draw 1

pop temp 0

label IF_FALSE0

push constant 0

return

function Square.decSize 0

push argument 0

pop pointer 0

push this 2

push constant 2

gt

if-goto IF_TRUE0

goto IF_FALSE0

label IF_TRUE0

push pointer 0

call Square.erase 1

pop temp 0

push this 2

push constant 2

sub

pop this 2

push pointer 0

call Square.draw 1

pop temp 0

label IF_FALSE0

push constant 0

return

function Square.moveUp 0

push argument 0

pop pointer 0

push this 1

push constant 1

gt

if-goto IF_TRUE0

goto IF_FALSE0

label IF_TRUE0

push constant 0

call Screen.setColor 1

pop temp 0

push this 0

push this 1

push this 2

add

push constant 1

sub

push this 0

push this 2

add

push this 1

push this 2

add

call Screen.drawRectangle 4

pop temp 0

push this 1

push constant 2

sub

pop this 1

push constant 0

not

call Screen.setColor 1

pop temp 0

push this 0

push this 1

push this 0

push this 2

add

push this 1

push constant 1

add

call Screen.drawRectangle 4

pop temp 0

label IF_FALSE0

push constant 0

return

function Square.moveDown 0

push argument 0

pop pointer 0

push this 1

push this 2

add

push constant 254

lt

if-goto IF_TRUE0

goto IF_FALSE0

label IF_TRUE0

push constant 0

call Screen.setColor 1

pop temp 0

push this 0

push this 1

push this 0

push this 2

add

push this 1

push constant 1

add

call Screen.drawRectangle 4

pop temp 0

push this 1

push constant 2

add

pop this 1

push constant 0

not

call Screen.setColor 1

pop temp 0

push this 0

push this 1

push this 2

add

push constant 1

sub

push this 0

push this 2

add

push this 1

push this 2

add

call Screen.drawRectangle 4

pop temp 0

label IF_FALSE0

push constant 0

return

function Square.moveLeft 0

push argument 0

pop pointer 0

push this 0

push constant 1

gt

if-goto IF_TRUE0

goto IF_FALSE0

label IF_TRUE0

push constant 0

call Screen.setColor 1

pop temp 0

push this 0

push this 2

add

push constant 1

sub

push this 1

push this 0

push this 2

add

push this 1

push this 2

add

call Screen.drawRectangle 4

pop temp 0

push this 0

push constant 2

sub

pop this 0

push constant 0

not

call Screen.setColor 1

pop temp 0

push this 0

push this 1

push this 0

push constant 1

add

push this 1

push this 2

add

call Screen.drawRectangle 4

pop temp 0

label IF_FALSE0

push constant 0

return

function Square.moveRight 0

push argument 0

pop pointer 0

push this 0

push this 2

add

push constant 510

lt

if-goto IF_TRUE0

goto IF_FALSE0

label IF_TRUE0

push constant 0

call Screen.setColor 1

pop temp 0

push this 0

push this 1

push this 0

push constant 1

add

push this 1

push this 2

add

call Screen.drawRectangle 4

pop temp 0

push this 0

push constant 2

add

pop this 0

push constant 0

not

call Screen.setColor 1

pop temp 0

push this 0

push this 2

add

push constant 1

sub

push this 1

push this 0

push this 2

add

push this 1

push this 2

add

call Screen.drawRectangle 4

pop temp 0

label IF_FALSE0

push constant 0

return


SquareGame.vm

function SquareGame.new 0

push constant 2

call Memory.alloc 1

pop pointer 0

push constant 0

push constant 0

push constant 30

call Square.new 3

pop this 0

push constant 0

pop this 1

push pointer 0

return

function SquareGame.dispose 0

push argument 0

pop pointer 0

push this 0

call Square.dispose 1

pop temp 0

push pointer 0

call Memory.deAlloc 1

pop temp 0

push constant 0

return

function SquareGame.moveSquare 0

push argument 0

pop pointer 0

push this 1

push constant 1

eq

if-goto IF_TRUE0

goto IF_FALSE0

label IF_TRUE0

push this 0

call Square.moveUp 1

pop temp 0

label IF_FALSE0

push this 1

push constant 2

eq

if-goto IF_TRUE1

goto IF_FALSE1

label IF_TRUE1

push this 0

call Square.moveDown 1

pop temp 0

label IF_FALSE1

push this 1

push constant 3

eq

if-goto IF_TRUE2

goto IF_FALSE2

label IF_TRUE2

push this 0

call Square.moveLeft 1

pop temp 0

label IF_FALSE2

push this 1

push constant 4

eq

if-goto IF_TRUE3

goto IF_FALSE3

label IF_TRUE3

push this 0

call Square.moveRight 1

pop temp 0

label IF_FALSE3

push constant 5

call Sys.wait 1

pop temp 0

push constant 0

return

function SquareGame.run 2

push argument 0

pop pointer 0

push constant 0

pop local 1

label WHILE_EXP0

push local 1

not

not

if-goto WHILE_END0

label WHILE_EXP1

push local 0

push constant 0

eq

not

if-goto WHILE_END1

call Keyboard.keyPressed 0

pop local 0

push pointer 0

call SquareGame.moveSquare 1

pop temp 0

goto WHILE_EXP1

label WHILE_END1

push local 0

push constant 81

eq

if-goto IF_TRUE0

goto IF_FALSE0

label IF_TRUE0

push constant 0

not

pop local 1

label IF_FALSE0

push local 0

push constant 90

eq

if-goto IF_TRUE1

goto IF_FALSE1

label IF_TRUE1

push this 0

call Square.decSize 1

pop temp 0

label IF_FALSE1

push local 0

push constant 88

eq

if-goto IF_TRUE2

goto IF_FALSE2

label IF_TRUE2

push this 0

call Square.incSize 1

pop temp 0

label IF_FALSE2

push local 0

push constant 131

eq

if-goto IF_TRUE3

goto IF_FALSE3

label IF_TRUE3

push constant 1

pop this 1

label IF_FALSE3

push local 0

push constant 133

eq

if-goto IF_TRUE4

goto IF_FALSE4

label IF_TRUE4

push constant 2

pop this 1

label IF_FALSE4

push local 0

push constant 130

eq

if-goto IF_TRUE5

goto IF_FALSE5

label IF_TRUE5

push constant 3

pop this 1

label IF_FALSE5

push local 0

push constant 132

eq

if-goto IF_TRUE6

goto IF_FALSE6

label IF_TRUE6

push constant 4

pop this 1

label IF_FALSE6

label WHILE_EXP2

push local 0

push constant 0

eq

not

not

if-goto WHILE_END2

call Keyboard.keyPressed 0

pop local 0

push pointer 0

call SquareGame.moveSquare 1

pop temp 0

goto WHILE_EXP2

label WHILE_END2

goto WHILE_EXP0

label WHILE_END0

push constant 0

return


Average

Este programa calcula el promedio de una secuencia de números enteros proporcionada por el usuario. Para probarlo debemos ejecutar el código traducido en el VMEmulator y seguir las instrucciones que se muestran en la pantalla.

Resultados obtenidos


Main.vm

function Main.main 4

push constant 18

call String.new 1

push constant 72

call String.appendChar 2

push constant 111

call String.appendChar 2

push constant 119

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 109

call String.appendChar 2

push constant 97

call String.appendChar 2

push constant 110

call String.appendChar 2

push constant 121

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 110

call String.appendChar 2

push constant 117

call String.appendChar 2

push constant 109

call String.appendChar 2

push constant 98

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 114

call String.appendChar 2

push constant 115

call String.appendChar 2

push constant 63

call String.appendChar 2

push constant 32

call String.appendChar 2

call Keyboard.readInt 1

pop local 1

push local 1

call Array.new 1

pop local 0

push constant 0

pop local 2

label WHILE_EXP0

push local 2

push local 1

lt

not

if-goto WHILE_END0

push local 2

push local 0

add

push constant 16

call String.new 1

push constant 69

call String.appendChar 2

push constant 110

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 114

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 97

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 110

call String.appendChar 2

push constant 117

call String.appendChar 2

push constant 109

call String.appendChar 2

push constant 98

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 114

call String.appendChar 2

push constant 58

call String.appendChar 2

push constant 32

call String.appendChar 2

call Keyboard.readInt 1

pop temp 0

pop pointer 1

push temp 0

pop that 0

push local 3

push local 2

push local 0

add

pop pointer 1

push that 0

add

pop local 3

push local 2

push constant 1

add

pop local 2

goto WHILE_EXP0

label WHILE_END0

push constant 15

call String.new 1

push constant 84

call String.appendChar 2

push constant 104

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 97

call String.appendChar 2

push constant 118

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 114

call String.appendChar 2

push constant 97

call String.appendChar 2

push constant 103

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 105

call String.appendChar 2

push constant 115

call String.appendChar 2

push constant 32

call String.appendChar 2

call Output.printString 1

pop temp 0

push local 3

push local 1

call Math.divide 2

call Output.printInt 1

pop temp 0

push constant 0

return


Pong

Este programa es un juego. Una pelota se mueve aleatoriamente en la pantalla, rebotando en las “paredes” de la pantalla. El usuario puede mover una pequeña superficie presionando las teclas de flecha izquierda y derecha del teclado. Cada vez que la superficie golpea la pelota, el usuario anota un punto y esta se encoge un poco, para hacer el juego más difícil. Si el usuario falla y la pelota golpea la línea horizontal inferior, el juego termina. Para probarlo debemos ejecute el código traducido en el VMEmulator y jugar un poco.

Resultados obtenidos

Ball.vm

function Ball.new 0

push constant 15

call Memory.alloc 1

pop pointer 0

push argument 0

pop this 0

push argument 1

pop this 1

push argument 2

pop this 10

push argument 3

push constant 6

sub

pop this 11

push argument 4

pop this 12

push argument 5

push constant 6

sub

pop this 13

push constant 0

pop this 14

push pointer 0

call Ball.show 1

pop temp 0

push pointer 0

return

function Ball.dispose 0

push argument 0

pop pointer 0

push pointer 0

call Memory.deAlloc 1

pop temp 0

push constant 0

return

function Ball.show 0

push argument 0

pop pointer 0

push constant 0

not

call Screen.setColor 1

pop temp 0

push pointer 0

call Ball.draw 1

pop temp 0

push constant 0

return

function Ball.hide 0

push argument 0

pop pointer 0

push constant 0

call Screen.setColor 1

pop temp 0

push pointer 0

call Ball.draw 1

pop temp 0

push constant 0

return

function Ball.draw 0

push argument 0

pop pointer 0

push this 0

push this 1

push this 0

push constant 5

add

push this 1

push constant 5

add

call Screen.drawRectangle 4

pop temp 0

push constant 0

return

function Ball.getLeft 0

push argument 0

pop pointer 0

push this 0

return

function Ball.getRight 0

push argument 0

pop pointer 0

push this 0

push constant 5

add

return

function Ball.setDestination 3

push argument 0

pop pointer 0

push argument 1

push this 0

sub

pop this 2

push argument 2

push this 1

sub

pop this 3

push this 2

call Math.abs 1

pop local 0

push this 3

call Math.abs 1

pop local 1

push local 0

push local 1

lt

pop this 7

push this 7

if-goto IF_TRUE0

goto IF_FALSE0

label IF_TRUE0

push local 0

pop local 2

push local 1

pop local 0

push local 2

pop local 1

push this 1

push argument 2

lt

pop this 8

push this 0

push argument 1

lt

pop this 9

goto IF_END0

label IF_FALSE0

push this 0

push argument 1

lt

pop this 8

push this 1

push argument 2

lt

pop this 9

label IF_END0

push constant 2

push local 1

call Math.multiply 2

push local 0

sub

pop this 4

push constant 2

push local 1

call Math.multiply 2

pop this 5

push constant 2

push local 1

push local 0

sub

call Math.multiply 2

pop this 6

push constant 0

return

function Ball.move 0

push argument 0

pop pointer 0

push pointer 0

call Ball.hide 1

pop temp 0

push this 4

push constant 0

lt

if-goto IF_TRUE0

goto IF_FALSE0

label IF_TRUE0

push this 4

push this 5

add

pop this 4

goto IF_END0

label IF_FALSE0

push this 4

push this 6

add

pop this 4

push this 9

if-goto IF_TRUE1

goto IF_FALSE1

label IF_TRUE1

push this 7

if-goto IF_TRUE2

goto IF_FALSE2

label IF_TRUE2

push this 0

push constant 4

add

pop this 0

goto IF_END2

label IF_FALSE2

push this 1

push constant 4

add

pop this 1

label IF_END2

goto IF_END1

label IF_FALSE1

push this 7

if-goto IF_TRUE3

goto IF_FALSE3

label IF_TRUE3

push this 0

push constant 4

sub

pop this 0

goto IF_END3

label IF_FALSE3

push this 1

push constant 4

sub

pop this 1

label IF_END3

label IF_END1

label IF_END0

push this 8

if-goto IF_TRUE4

goto IF_FALSE4

label IF_TRUE4

push this 7

if-goto IF_TRUE5

goto IF_FALSE5

label IF_TRUE5

push this 1

push constant 4

add

pop this 1

goto IF_END5

label IF_FALSE5

push this 0

push constant 4

add

pop this 0

label IF_END5

goto IF_END4

label IF_FALSE4

push this 7

if-goto IF_TRUE6

goto IF_FALSE6

label IF_TRUE6

push this 1

push constant 4

sub

pop this 1

goto IF_END6

label IF_FALSE6

push this 0

push constant 4

sub

pop this 0

label IF_END6

label IF_END4

push this 0

push this 10

gt

not

if-goto IF_TRUE7

goto IF_FALSE7

label IF_TRUE7

push constant 1

pop this 14

push this 10

pop this 0

label IF_FALSE7

push this 0

push this 11

lt

not

if-goto IF_TRUE8

goto IF_FALSE8

label IF_TRUE8

push constant 2

pop this 14

push this 11

pop this 0

label IF_FALSE8

push this 1

push this 12

gt

not

if-goto IF_TRUE9

goto IF_FALSE9

label IF_TRUE9

push constant 3

pop this 14

push this 12

pop this 1

label IF_FALSE9

push this 1

push this 13

lt

not

if-goto IF_TRUE10

goto IF_FALSE10

label IF_TRUE10

push constant 4

pop this 14

push this 13

pop this 1

label IF_FALSE10

push pointer 0

call Ball.show 1

pop temp 0

push this 14

return

function Ball.bounce 5

push argument 0

pop pointer 0

push this 2

push constant 10

call Math.divide 2

pop local 2

push this 3

push constant 10

call Math.divide 2

pop local 3

push argument 1

push constant 0

eq

if-goto IF_TRUE0

goto IF_FALSE0

label IF_TRUE0

push constant 10

pop local 4

goto IF_END0

label IF_FALSE0

push this 2

push constant 0

lt

not

push argument 1

push constant 1

eq

and

push this 2

push constant 0

lt

push argument 1

push constant 1

neg

eq

and

or

if-goto IF_TRUE1

goto IF_FALSE1

label IF_TRUE1

push constant 20

pop local 4

goto IF_END1

label IF_FALSE1

push constant 5

pop local 4

label IF_END1

label IF_END0

push this 14

push constant 1

eq

if-goto IF_TRUE2

goto IF_FALSE2

label IF_TRUE2

push constant 506

pop local 0

push local 3

push constant 50

neg

call Math.multiply 2

push local 2

call Math.divide 2

pop local 1

push this 1

push local 1

push local 4

call Math.multiply 2

add

pop local 1

goto IF_END2

label IF_FALSE2

push this 14

push constant 2

eq

if-goto IF_TRUE3

goto IF_FALSE3

label IF_TRUE3

push constant 0

pop local 0

push local 3

push constant 50

call Math.multiply 2

push local 2

call Math.divide 2

pop local 1

push this 1

push local 1

push local 4

call Math.multiply 2

add

pop local 1

goto IF_END3

label IF_FALSE3

push this 14

push constant 3

eq

if-goto IF_TRUE4

goto IF_FALSE4

label IF_TRUE4

push constant 250

pop local 1

push local 2

push constant 25

neg

call Math.multiply 2

push local 3

call Math.divide 2

pop local 0

push this 0

push local 0

push local 4

call Math.multiply 2

add

pop local 0

goto IF_END4

label IF_FALSE4

push constant 0

pop local 1

push local 2

push constant 25

call Math.multiply 2

push local 3

call Math.divide 2

pop local 0

push this 0

push local 0

push local 4

call Math.multiply 2

add

pop local 0

label IF_END4

label IF_END3

label IF_END2

push pointer 0

push local 0

push local 1

call Ball.setDestination 3

pop temp 0

push constant 0

return


Bat.vm

function Bat.new 0

push constant 5

call Memory.alloc 1

pop pointer 0

push argument 0

pop this 0

push argument 1

pop this 1

push argument 2

pop this 2

push argument 3

pop this 3

push constant 2

pop this 4

push pointer 0

call Bat.show 1

pop temp 0

push pointer 0

return

function Bat.dispose 0

push argument 0

pop pointer 0

push pointer 0

call Memory.deAlloc 1

pop temp 0

push constant 0

return

function Bat.show 0

push argument 0

pop pointer 0

push constant 0

not

call Screen.setColor 1

pop temp 0

push pointer 0

call Bat.draw 1

pop temp 0

push constant 0

return

function Bat.hide 0

push argument 0

pop pointer 0

push constant 0

call Screen.setColor 1

pop temp 0

push pointer 0

call Bat.draw 1

pop temp 0

push constant 0

return

function Bat.draw 0

push argument 0

pop pointer 0

push this 0

push this 1

push this 0

push this 2

add

push this 1

push this 3

add

call Screen.drawRectangle 4

pop temp 0

push constant 0

return

function Bat.setDirection 0

push argument 0

pop pointer 0

push argument 1

pop this 4

push constant 0

return

function Bat.getLeft 0

push argument 0

pop pointer 0

push this 0

return

function Bat.getRight 0

push argument 0

pop pointer 0

push this 0

push this 2

add

return

function Bat.setWidth 0

push argument 0

pop pointer 0

push pointer 0

call Bat.hide 1

pop temp 0

push argument 1

pop this 2

push pointer 0

call Bat.show 1

pop temp 0

push constant 0

return

function Bat.move 0

push argument 0

pop pointer 0

push this 4

push constant 1

eq

if-goto IF_TRUE0

goto IF_FALSE0

label IF_TRUE0

push this 0

push constant 4

sub

pop this 0

push this 0

push constant 0

lt

if-goto IF_TRUE1

goto IF_FALSE1

label IF_TRUE1

push constant 0

pop this 0

label IF_FALSE1

push constant 0

call Screen.setColor 1

pop temp 0

push this 0

push this 2

add

push constant 1

add

push this 1

push this 0

push this 2

add

push constant 4

add

push this 1

push this 3

add

call Screen.drawRectangle 4

pop temp 0

push constant 0

not

call Screen.setColor 1

pop temp 0

push this 0

push this 1

push this 0

push constant 3

add

push this 1

push this 3

add

call Screen.drawRectangle 4

pop temp 0

goto IF_END0

label IF_FALSE0

push this 0

push constant 4

add

pop this 0

push this 0

push this 2

add

push constant 511

gt

if-goto IF_TRUE2

goto IF_FALSE2

label IF_TRUE2

push constant 511

push this 2

sub

pop this 0

label IF_FALSE2

push constant 0

call Screen.setColor 1

pop temp 0

push this 0

push constant 4

sub

push this 1

push this 0

push constant 1

sub

push this 1

push this 3

add

call Screen.drawRectangle 4

pop temp 0

push constant 0

not

call Screen.setColor 1

pop temp 0

push this 0

push this 2

add

push constant 3

sub

push this 1

push this 0

push this 2

add

push this 1

push this 3

add

call Screen.drawRectangle 4

pop temp 0

label IF_END0

push constant 0

return


Main.vm

function Main.main 1

call PongGame.newInstance 0

pop temp 0

call PongGame.getInstance 0

pop local 0

push local 0

call PongGame.run 1

pop temp 0

push local 0

call PongGame.dispose 1

pop temp 0

push constant 0

return


PongGame.vm

function PongGame.new 0

push constant 7

call Memory.alloc 1

pop pointer 0

call Screen.clearScreen 0

pop temp 0

push constant 50

pop this 6

push constant 230

push constant 229

push this 6

push constant 7

call Bat.new 4

pop this 0

push constant 253

push constant 222

push constant 0

push constant 511

push constant 0

push constant 229

call Ball.new 6

pop this 1

push this 1

push constant 400

push constant 0

call Ball.setDestination 3

pop temp 0

push constant 0

push constant 238

push constant 511

push constant 240

call Screen.drawRectangle 4

pop temp 0

push constant 22

push constant 0

call Output.moveCursor 2

pop temp 0

push constant 8

call String.new 1

push constant 83

call String.appendChar 2

push constant 99

call String.appendChar 2

push constant 111

call String.appendChar 2

push constant 114

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 58

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 48

call String.appendChar 2

call Output.printString 1

pop temp 0

push constant 0

pop this 3

push constant 0

pop this 4

push constant 0

pop this 2

push constant 0

pop this 5

push pointer 0

return

function PongGame.dispose 0

push argument 0

pop pointer 0

push this 0

call Bat.dispose 1

pop temp 0

push this 1

call Ball.dispose 1

pop temp 0

push pointer 0

call Memory.deAlloc 1

pop temp 0

push constant 0

return

function PongGame.newInstance 0

call PongGame.new 0

pop static 0

push constant 0

return

function PongGame.getInstance 0

push static 0

return

function PongGame.run 1

push argument 0

pop pointer 0

label WHILE_EXP0

push this 3

not

not

if-goto WHILE_END0

label WHILE_EXP1

push local 0

push constant 0

eq

push this 3

not

and

not

if-goto WHILE_END1

call Keyboard.keyPressed 0

pop local 0

push this 0

call Bat.move 1

pop temp 0

push pointer 0

call PongGame.moveBall 1

pop temp 0

push constant 50

call Sys.wait 1

pop temp 0

goto WHILE_EXP1

label WHILE_END1

push local 0

push constant 130

eq

if-goto IF_TRUE0

goto IF_FALSE0

label IF_TRUE0

push this 0

push constant 1

call Bat.setDirection 2

pop temp 0

goto IF_END0

label IF_FALSE0

push local 0

push constant 132

eq

if-goto IF_TRUE1

goto IF_FALSE1

label IF_TRUE1

push this 0

push constant 2

call Bat.setDirection 2

pop temp 0

goto IF_END1

label IF_FALSE1

push local 0

push constant 140

eq

if-goto IF_TRUE2

goto IF_FALSE2

label IF_TRUE2

push constant 0

not

pop this 3

label IF_FALSE2

label IF_END1

label IF_END0

label WHILE_EXP2

push local 0

push constant 0

eq

not

push this 3

not

and

not

if-goto WHILE_END2

call Keyboard.keyPressed 0

pop local 0

push this 0

call Bat.move 1

pop temp 0

push pointer 0

call PongGame.moveBall 1

pop temp 0

push constant 50

call Sys.wait 1

pop temp 0

goto WHILE_EXP2

label WHILE_END2

goto WHILE_EXP0

label WHILE_END0

push this 3

if-goto IF_TRUE3

goto IF_FALSE3

label IF_TRUE3

push constant 10

push constant 27

call Output.moveCursor 2

pop temp 0

push constant 9

call String.new 1

push constant 71

call String.appendChar 2

push constant 97

call String.appendChar 2

push constant 109

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 79

call String.appendChar 2

push constant 118

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 114

call String.appendChar 2

call Output.printString 1

pop temp 0

label IF_FALSE3

push constant 0

return

function PongGame.moveBall 5

push argument 0

pop pointer 0

push this 1

call Ball.move 1

pop this 2

push this 2

push constant 0

gt

push this 2

push this 5

eq

not

and

if-goto IF_TRUE0

goto IF_FALSE0

label IF_TRUE0

push this 2

pop this 5

push constant 0

pop local 0

push this 0

call Bat.getLeft 1

pop local 1

push this 0

call Bat.getRight 1

pop local 2

push this 1

call Ball.getLeft 1

pop local 3

push this 1

call Ball.getRight 1

pop local 4

push this 2

push constant 4

eq

if-goto IF_TRUE1

goto IF_FALSE1

label IF_TRUE1

push local 1

push local 4

gt

push local 2

push local 3

lt

or

pop this 3

push this 3

not

if-goto IF_TRUE2

goto IF_FALSE2

label IF_TRUE2

push local 4

push local 1

push constant 10

add

lt

if-goto IF_TRUE3

goto IF_FALSE3

label IF_TRUE3

push constant 1

neg

pop local 0

goto IF_END3

label IF_FALSE3

push local 3

push local 2

push constant 10

sub

gt

if-goto IF_TRUE4

goto IF_FALSE4

label IF_TRUE4

push constant 1

pop local 0

label IF_FALSE4

label IF_END3

push this 6

push constant 2

sub

pop this 6

push this 0

push this 6

call Bat.setWidth 2

pop temp 0

push this 4

push constant 1

add

pop this 4

push constant 22

push constant 7

call Output.moveCursor 2

pop temp 0

push this 4

call Output.printInt 1

pop temp 0

label IF_FALSE2

label IF_FALSE1

push this 1

push local 0

call Ball.bounce 2

pop temp 0

label IF_FALSE0

push constant 0

return


Complex arrays

Este programa realiza cinco cálculos complejos utilizando matrices. Para cada cálculo de este tipo, el programa imprime en la pantalla el resultado esperado frente al resultado real (tal como lo realiza el programa compilado). Para probar probarlo debemos utilizar el .vm traducido y ejecutarlo en el VMEmulator y asegurarnos de que los resultados reales sean idénticos a los resultados esperados.

Resultados obtenidos


Main.vm

function Main.main 3

push constant 10

call Array.new 1

pop local 0

push constant 5

call Array.new 1

pop local 1

push constant 1

call Array.new 1

pop local 2

push constant 3

push local 0

add

push constant 2

pop temp 0

pop pointer 1

push temp 0

pop that 0

push constant 4

push local 0

add

push constant 8

pop temp 0

pop pointer 1

push temp 0

pop that 0

push constant 5

push local 0

add

push constant 4

pop temp 0

pop pointer 1

push temp 0

pop that 0

push constant 3

push local 0

add

pop pointer 1

push that 0

push local 1

add

push constant 3

push local 0

add

pop pointer 1

push that 0

push constant 3

add

pop temp 0

pop pointer 1

push temp 0

pop that 0

push constant 3

push local 0

add

pop pointer 1

push that 0

push local 1

add

pop pointer 1

push that 0

push local 0

add

push constant 5

push local 0

add

pop pointer 1

push that 0

push local 0

add

pop pointer 1

push that 0

push constant 7

push constant 3

push local 0

add

pop pointer 1

push that 0

sub

push constant 2

call Main.double 1

sub

push constant 1

add

push local 1

add

pop pointer 1

push that 0

call Math.multiply 2

pop temp 0

pop pointer 1

push temp 0

pop that 0

push constant 0

push local 2

add

push constant 0

pop temp 0

pop pointer 1

push temp 0

pop that 0

push constant 0

push local 2

add

pop pointer 1

push that 0

pop local 2

push constant 43

call String.new 1

push constant 84

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 115

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 49

call String.appendChar 2

push constant 58

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 120

call String.appendChar 2

push constant 112

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 99

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 100

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 114

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 115

call String.appendChar 2

push constant 117

call String.appendChar 2

push constant 108

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 58

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 53

call String.appendChar 2

push constant 59

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 97

call String.appendChar 2

push constant 99

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 117

call String.appendChar 2

push constant 97

call String.appendChar 2

push constant 108

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 114

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 115

call String.appendChar 2

push constant 117

call String.appendChar 2

push constant 108

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 58

call String.appendChar 2

push constant 32

call String.appendChar 2

call Output.printString 1

pop temp 0

push constant 2

push local 1

add

pop pointer 1

push that 0

call Output.printInt 1

pop temp 0

call Output.println 0

pop temp 0

push constant 44

call String.new 1

push constant 84

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 115

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 50

call String.appendChar 2

push constant 58

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 120

call String.appendChar 2

push constant 112

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 99

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 100

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 114

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 115

call String.appendChar 2

push constant 117

call String.appendChar 2

push constant 108

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 58

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 52

call String.appendChar 2

push constant 48

call String.appendChar 2

push constant 59

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 97

call String.appendChar 2

push constant 99

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 117

call String.appendChar 2

push constant 97

call String.appendChar 2

push constant 108

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 114

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 115

call String.appendChar 2

push constant 117

call String.appendChar 2

push constant 108

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 58

call String.appendChar 2

push constant 32

call String.appendChar 2

call Output.printString 1

pop temp 0

push constant 5

push local 0

add

pop pointer 1

push that 0

call Output.printInt 1

pop temp 0

call Output.println 0

pop temp 0

push constant 43

call String.new 1

push constant 84

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 115

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 51

call String.appendChar 2

push constant 58

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 120

call String.appendChar 2

push constant 112

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 99

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 100

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 114

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 115

call String.appendChar 2

push constant 117

call String.appendChar 2

push constant 108

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 58

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 48

call String.appendChar 2

push constant 59

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 97

call String.appendChar 2

push constant 99

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 117

call String.appendChar 2

push constant 97

call String.appendChar 2

push constant 108

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 114

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 115

call String.appendChar 2

push constant 117

call String.appendChar 2

push constant 108

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 58

call String.appendChar 2

push constant 32

call String.appendChar 2

call Output.printString 1

pop temp 0

push local 2

call Output.printInt 1

pop temp 0

call Output.println 0

pop temp 0

push constant 0

pop local 2

push local 2

push constant 0

eq

if-goto IF_TRUE0

goto IF_FALSE0

label IF_TRUE0

push local 0

push constant 10

call Main.fill 2

pop temp 0

push constant 3

push local 0

add

pop pointer 1

push that 0

pop local 2

push constant 1

push local 2

add

push constant 33

pop temp 0

pop pointer 1

push temp 0

pop that 0

push constant 7

push local 0

add

pop pointer 1

push that 0

pop local 2

push constant 1

push local 2

add

push constant 77

pop temp 0

pop pointer 1

push temp 0

pop that 0

push constant 3

push local 0

add

pop pointer 1

push that 0

pop local 1

push constant 1

push local 1

add

push constant 1

push local 1

add

pop pointer 1

push that 0

push constant 1

push local 2

add

pop pointer 1

push that 0

add

pop temp 0

pop pointer 1

push temp 0

pop that 0

label IF_FALSE0

push constant 44

call String.new 1

push constant 84

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 115

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 52

call String.appendChar 2

push constant 58

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 120

call String.appendChar 2

push constant 112

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 99

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 100

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 114

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 115

call String.appendChar 2

push constant 117

call String.appendChar 2

push constant 108

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 58

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 55

call String.appendChar 2

push constant 55

call String.appendChar 2

push constant 59

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 97

call String.appendChar 2

push constant 99

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 117

call String.appendChar 2

push constant 97

call String.appendChar 2

push constant 108

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 114

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 115

call String.appendChar 2

push constant 117

call String.appendChar 2

push constant 108

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 58

call String.appendChar 2

push constant 32

call String.appendChar 2

call Output.printString 1

pop temp 0

push constant 1

push local 2

add

pop pointer 1

push that 0

call Output.printInt 1

pop temp 0

call Output.println 0

pop temp 0

push constant 45

call String.new 1

push constant 84

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 115

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 53

call String.appendChar 2

push constant 58

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 120

call String.appendChar 2

push constant 112

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 99

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 100

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 114

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 115

call String.appendChar 2

push constant 117

call String.appendChar 2

push constant 108

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 58

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 49

call String.appendChar 2

push constant 49

call String.appendChar 2

push constant 48

call String.appendChar 2

push constant 59

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 97

call String.appendChar 2

push constant 99

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 117

call String.appendChar 2

push constant 97

call String.appendChar 2

push constant 108

call String.appendChar 2

push constant 32

call String.appendChar 2

push constant 114

call String.appendChar 2

push constant 101

call String.appendChar 2

push constant 115

call String.appendChar 2

push constant 117

call String.appendChar 2

push constant 108

call String.appendChar 2

push constant 116

call String.appendChar 2

push constant 58

call String.appendChar 2

push constant 32

call String.appendChar 2

call Output.printString 1

pop temp 0

push constant 1

push local 1

add

pop pointer 1

push that 0

call Output.printInt 1

pop temp 0

call Output.println 0

pop temp 0

push constant 0

return

function Main.double 0

push argument 0

push constant 2

call Math.multiply 2

return

function Main.fill 0

label WHILE_EXP0

push argument 1

push constant 0

gt

not

if-goto WHILE_END0

push argument 1

push constant 1

sub

pop argument 1

push argument 1

push argument 0

add

push constant 3

call Array.new 1

pop temp 0

pop pointer 1

push temp 0

pop that 0

goto WHILE_EXP0

label WHILE_END0

push constant 0

return


Proyecto 12. Operating System

Desarrollo

Los sistemas informáticos deben admitir operaciones matemáticas como la suma, la multiplicación y la división. Normalmente, la suma se implementa en hardware como lo vimos cuando trabajamos la ALU. Otras operaciones como la multiplicación y la división pueden ser manejadas por hardware o software es por eso que primeramente debemos ver cómo se pueden implementar de manera eficiente en el software, a nivel del sistema operativo.

Multiplicación: para la multiplicación se considerará el método estándar de multiplicación que nos enseñan. Para calcular 356 por 27, alineamos los dos números uno encima del otro. Luego, multiplicamos cada dígito de 356 por 7. Luego, “desplazamos hacia la izquierda” una posición y multiplicamos cada dígito de 356 por 2. Finalmente, sumamos las columnas y obtenemos el resultado. Todo esto en binario lo podemos ver en la siguiente figura

Figura 4. Multiplicación de dos números de n bits

Figura 5. Algoritmo de multiplicación

División: la forma sencilla de calcular la división de dos números de n bits x/y es restar repetidamente y de x hasta que sea imposible continuar (es decir, hasta que x < y). Pero podemos acelerar este algoritmo, podemos intentar restar grandes porciones de y de x en cada iteración. Por ejemplo, si x = 891 e y = 5, podemos decir de inmediato que podemos deducir cien 5 de x y el resto seguirá siendo mayor que 5, eliminando así 100 iteraciones del enfoque ingenuo. Formalmente, en cada iteración tratamos de restar de x el mayor desplazamiento posible de y, a saber, y * T donde T es la mayor potencia de 10 tal que y * T ≤ x.

Figura 6. Algoritmo de división

Raíz cuadrada: para las raíces cuadradas, bastará un algoritmo simple. La función raíz cuadrada y = √x tiene dos propiedades. Primero, es monótonamente creciente. Segundo, su función inversa, x = y^2 , es algo que ya sabemos calcular (multiplicación). O sea tenemos todo lo que necesitamos para calcular raíces cuadradas mediante lo se llama "búsqueda binaria".

Figura 7. Algoritmo para el cálculo de raíces cuadradas usando búsqueda binaria

Otro aspecto que debemos tener en cuenta para el desarrollo del SO es que, los computadores generalmente están conectadas a una variedad de dispositivos como teclado, pantalla, mouse, discos, etc. Cada uno de estos dispositivos de tiene sus propias idiosincrasias electromecánicas y físicas y, por lo tanto, leer y escribir datos en ellos implica muchos detalles técnicos. Por lo tanto, una función importante del sistema operativo es manejar los diversos dispositivos conectados al computador. Esto se hace encapsulando los detalles de la interfaz del dispositivo y brindando acceso conveniente a su funcionalidad básica, utilizando un conjunto de rutinas de O/S conocidas los controladores (drivers) del dispositivo. Por lo que debemos ver cómo manejaremos los dispositivos más frecuentes como: una pantalla y un teclado.

Se divide el manejo de la pantalla en dos partes lógicamente separadas: el manejo de la salida de gráficos y el manejo de la salida de caracteres.

Salida de gráficos

Dibujo de píxeles: dibujar un píxel se logra escribiendo el valor binario adecuado en la ubicación de RAM que representa el píxel requerido en la memoria. Por lo que hay que formular un algoritmo drawPixel Entonces, ahora que sabemos cómo dibujar un solo píxel, pasemos a describir cómo dibujar líneas y círculos.


Figura 8. Dibujar un píxel

Manejo de salida de caracteres

Salida de caracteres:
para desarrollar la capacidad de escribir texto en una pantalla de mapa de bits, primero tenemos que dividir la pantalla física orientada a píxeles en una pantalla lógica orientada a caracteres adecuada para escribir caracteres completos. Por ejemplo, consideremos una pantalla de 256 filas por 512 columnas. Si asignamos una cuadrícula de 11 * 8 píxeles para dibujar un solo carácter (11 filas, 8 columnas), nuestra pantalla puede mostrar 23 líneas de 64 caracteres cada una (con 3 filas adicionales de píxeles sin usar). Luego, para cada carácter que queremos mostrar en la pantalla, podemos diseñar una fuente atractiva y luego implementar la fuente usando una serie de mapas de bits de caracteres. Por ejemplo, la siguiente figura da un posible mapa de bits para la letra 'A'.

Figura 9. Mapa de bits de la letra “A”.

Dicho todo lo anterior podemos decir que el desarrollo del el sistema operativo Jack se divide en ocho clases:

  • Matemáticas: proporcionará operaciones matemáticas básicas

  • String: implementará el tipo String y las operaciones relacionadas con cadenas

  • Array: implementará el tipo Array y las operaciones relacionadas con los arrays

  • Salida: manejará la salida de texto a la pantalla

  • Pantalla: manejará la salida gráfica a la pantalla

  • Teclado: manejará la entrada del usuario desde el teclado

  • Memoria: manejará las operaciones de memoria

  • Sys: proporcionará algunos servicios relacionados con la ejecución.

Las funciones que tiene cada clase podremos verlas en su respectivo código.

Math.jack

class Math {

static Array twoToThe;

function void init() {

let twoToThe = Array.new(16);

let twoToThe[0] = 1;

let twoToThe[1] = 2;

let twoToThe[2] = 4;

let twoToThe[3] = 8;

let twoToThe[4] = 16;

let twoToThe[5] = 32;

let twoToThe[6] = 64;

let twoToThe[7] = 128;

let twoToThe[8] = 256;

let twoToThe[9] = 512;

let twoToThe[10] = 1024;

let twoToThe[11] = 2048;

let twoToThe[12] = 4096;

let twoToThe[13] = 8192;

let twoToThe[14] = 16384;

let twoToThe[15] = 16384 + 16384;

return;

}

function int two_to_the(int power) {

return twoToThe[power];

}

function boolean bit(int x, int j) {

return ~((x & twoToThe[j]) = 0);

}

function int abs(int x) {

if (x < 0) {

let x = -x;

}

return x;

}


function int multiply(int x, int y) {

var int j;

var int sum;

var int shiftedX;

let j = 0;

let sum = 0;

let shiftedX = x;

while (j < 16) { //each number is 16 bits

if (Math.bit(y, j) = true) {

let sum = sum + shiftedX;

}

let shiftedX = shiftedX + shiftedX;

let j = j + 1;

}

return sum;

}


function int divide(int x, int y) {

var int q;

var int result;

var int negX;

var int negY;

let negX = x < 0;

let negY = y < 0;

let x = Math.abs(x);

let y = Math.abs(y);


if(y > x) {

return 0;

}

let q = Math.divide(x, y + y);

if(x - (Math.multiply(q + q, y)) < y) {

let result = q + q;

}

else {

let result = q + q + 1;

}

if(negX = negY) {

return result;

}

else {

return -result;

}

}



function int sqrt(int x) {

var int result;

var int j;

var int checked;

var int squared;

let result = 0;

let j = 7;

while(~(j < 0)) {

let checked = result + twoToThe[j];

let squared = Math.multiply(checked, checked);

if(~(squared > x) & (squared > 0)) {

let result = checked;

}

let j = j - 1;

}

return result;

}


function int max(int a, int b) {

if (a > b) {

return a;

}

else {

return b;

}

}


function int min(int a, int b) {

if (a < b) {

return a;

}

else {

return b;

}

}

}

String.jack

class String {

field Array str;

field int length;

field int max;

constructor String new(int maxLength) {

if( maxLength = 0 ) {

let maxLength = 1;

}

let str = Array.new(maxLength);

let length = 0;

let max = maxLength;

return this;

}


method void dispose() {

do Array.dispose(str);

return;

}


method int length() {

return length;

}


method char charAt(int j) {

return str[j];

}


method void setCharAt(int j, char c) {

let str[j] = c;

return;

}


method String appendChar(char c) {

if (length < max) {

let str[length] = c;

let length = length + 1;

}

return this;

}


method void eraseLastChar() {

if (length > 0) {

let length = length - 1;

}

return;

}


method int intValue() {

var int i;

var int sum;

var boolean neg;

let sum = 0;

if ((length > 0) & (str[0] = 45)) { //'-' sign in ascii

let neg = true;

let i = 1;

}

else {

let neg = false;

let i = 0;

}

while (i < length) {

if (str[i] > 47 & str[i] < 58) { //the char is a digit between 0 to 9 in ascii

let sum = (sum * 10) + (str[i] - 48);

}

let i = i + 1;

}

if (neg) {

return -sum;

}

else {

return sum;

}

}


method void setInt(int number) {

let length = 0;

if (number < 0) {

let number = -number;

do appendChar(45); //add leading '-'

}

do recSetInt(number);

return;

}

method void recSetInt(int number) {

var int mod;

var int div;


let div = number / 10;

let mod = number - (div * 10);

if (number < 10)

{

do appendChar(mod + 48);

}

else{

do recSetInt(div);

do appendChar(mod + 48);

}

return;

}


function char newLine() {

return 128;

}


function char backSpace() {

return 129;

}


function char doubleQuote() {

return 34;

}

}

Array.jack

class Array {


function Array new(int size) {

return Memory.alloc(size);

}


method void dispose() {

do Memory.deAlloc(this);

return;

}

}

Output.jack

class Output {


static Array charMaps;

static Array lineLength;

static Array screen;

static Array align;

static int row, col;

static boolean whitePixel, blackPixel;

function void init() {

let row = 0;

let col = 0;

let screen = 16384;

let whitePixel = 0;

let blackPixel = 1;

let align = Array.new(2);

let align[0] = 255;

let align[1] = -1 & 255;

do Output.initMap();

do Output.initLineLength();

return;

}


function void initLineLength() {

let lineLength = Array.new(12);

let lineLength[0] = 0;

let lineLength[1] = 0;

let lineLength[2] = 0;

let lineLength[3] = 0;

let lineLength[4] = 0;

let lineLength[5] = 0;

let lineLength[6] = 0;

let lineLength[7] = 0;

let lineLength[8] = 0;

let lineLength[9] = 0;

let lineLength[10] = 0;

let lineLength[11] = 0;

return;

}

function void initMap() {

var int i;

let charMaps = Array.new(127);

do Output.create(0,63,63,63,63,63,63,63,63,63,0,0);


do Output.create(32,0,0,0,0,0,0,0,0,0,0,0); //

do Output.create(33,12,30,30,30,12,12,0,12,12,0,0); // !

do Output.create(34,54,54,20,0,0,0,0,0,0,0,0); // "

do Output.create(35,0,18,18,63,18,18,63,18,18,0,0); // #

do Output.create(36,12,30,51,3,30,48,51,30,12,12,0); // $

do Output.create(37,0,0,35,51,24,12,6,51,49,0,0); // %

do Output.create(38,12,30,30,12,54,27,27,27,54,0,0); // &

do Output.create(39,12,12,6,0,0,0,0,0,0,0,0); // '

do Output.create(40,24,12,6,6,6,6,6,12,24,0,0); // (

do Output.create(41,6,12,24,24,24,24,24,12,6,0,0); // )

do Output.create(42,0,0,0,51,30,63,30,51,0,0,0); // *

do Output.create(43,0,0,0,12,12,63,12,12,0,0,0); // +

do Output.create(44,0,0,0,0,0,0,0,12,12,6,0); // ,

do Output.create(45,0,0,0,0,0,63,0,0,0,0,0); // -

do Output.create(46,0,0,0,0,0,0,0,12,12,0,0); // .

do Output.create(47,0,0,32,48,24,12,6,3,1,0,0); // /

do Output.create(48,12,30,51,51,51,51,51,30,12,0,0); // 0

do Output.create(49,12,14,15,12,12,12,12,12,63,0,0); // 1

do Output.create(50,30,51,48,24,12,6,3,51,63,0,0); // 2

do Output.create(51,30,51,48,48,28,48,48,51,30,0,0); // 3

do Output.create(52,16,24,28,26,25,63,24,24,60,0,0); // 4

do Output.create(53,63,3,3,31,48,48,48,51,30,0,0); // 5

do Output.create(54,28,6,3,3,31,51,51,51,30,0,0); // 6

do Output.create(55,63,49,48,48,24,12,12,12,12,0,0); // 7

do Output.create(56,30,51,51,51,30,51,51,51,30,0,0); // 8

do Output.create(57,30,51,51,51,62,48,48,24,14,0,0); // 9

do Output.create(58,0,0,12,12,0,0,12,12,0,0,0); // :

do Output.create(59,0,0,12,12,0,0,12,12,6,0,0); // ;

do Output.create(60,0,0,24,12,6,3,6,12,24,0,0); // <

do Output.create(61,0,0,0,63,0,0,63,0,0,0,0); // =

do Output.create(62,0,0,3,6,12,24,12,6,3,0,0); // >

do Output.create(64,30,51,51,59,59,59,27,3,30,0,0); // @

do Output.create(63,30,51,51,24,12,12,0,12,12,0,0); // ?


do Output.create(65,12,30,51,51,63,51,51,51,51,0,0); // A

do Output.create(66,31,51,51,51,31,51,51,51,31,0,0); // B

do Output.create(67,28,54,35,3,3,3,35,54,28,0,0); // C

do Output.create(68,15,27,51,51,51,51,51,27,15,0,0); // D

do Output.create(69,63,51,35,11,15,11,35,51,63,0,0); // E

do Output.create(70,63,51,35,11,15,11,3,3,3,0,0); // F

do Output.create(71,28,54,35,3,59,51,51,54,44,0,0); // G

do Output.create(72,51,51,51,51,63,51,51,51,51,0,0); // H

do Output.create(73,30,12,12,12,12,12,12,12,30,0,0); // I

do Output.create(74,60,24,24,24,24,24,27,27,14,0,0); // J

do Output.create(75,51,51,51,27,15,27,51,51,51,0,0); // K

do Output.create(76,3,3,3,3,3,3,35,51,63,0,0); // L

do Output.create(77,33,51,63,63,51,51,51,51,51,0,0); // M

do Output.create(78,51,51,55,55,63,59,59,51,51,0,0); // N

do Output.create(79,30,51,51,51,51,51,51,51,30,0,0); // O

do Output.create(80,31,51,51,51,31,3,3,3,3,0,0); // P

do Output.create(81,30,51,51,51,51,51,63,59,30,48,0);// Q

do Output.create(82,31,51,51,51,31,27,51,51,51,0,0); // R

do Output.create(83,30,51,51,6,28,48,51,51,30,0,0); // S

do Output.create(84,63,63,45,12,12,12,12,12,30,0,0); // T

do Output.create(85,51,51,51,51,51,51,51,51,30,0,0); // U

do Output.create(86,51,51,51,51,51,30,30,12,12,0,0); // V

do Output.create(87,51,51,51,51,51,63,63,63,18,0,0); // W

do Output.create(88,51,51,30,30,12,30,30,51,51,0,0); // X

do Output.create(89,51,51,51,51,30,12,12,12,30,0,0); // Y

do Output.create(90,63,51,49,24,12,6,35,51,63,0,0); // Z


do Output.create(91,30,6,6,6,6,6,6,6,30,0,0); // [

do Output.create(92,0,0,1,3,6,12,24,48,32,0,0); // \

do Output.create(93,30,24,24,24,24,24,24,24,30,0,0); // ]

do Output.create(94,8,28,54,0,0,0,0,0,0,0,0); // ^

do Output.create(95,0,0,0,0,0,0,0,0,0,63,0); // _

do Output.create(96,6,12,24,0,0,0,0,0,0,0,0); // `


do Output.create(97,0,0,0,14,24,30,27,27,54,0,0); // a

do Output.create(98,3,3,3,15,27,51,51,51,30,0,0); // b

do Output.create(99,0,0,0,30,51,3,3,51,30,0,0); // c

do Output.create(100,48,48,48,60,54,51,51,51,30,0,0); // d

do Output.create(101,0,0,0,30,51,63,3,51,30,0,0); // e

do Output.create(102,28,54,38,6,15,6,6,6,15,0,0); // f

do Output.create(103,0,0,30,51,51,51,62,48,51,30,0); // g

do Output.create(104,3,3,3,27,55,51,51,51,51,0,0); // h

do Output.create(105,12,12,0,14,12,12,12,12,30,0,0); // i

do Output.create(106,48,48,0,56,48,48,48,48,51,30,0); // j

do Output.create(107,3,3,3,51,27,15,15,27,51,0,0); // k

do Output.create(108,14,12,12,12,12,12,12,12,30,0,0); // l

do Output.create(109,0,0,0,29,63,43,43,43,43,0,0); // m

do Output.create(110,0,0,0,29,51,51,51,51,51,0,0); // n

do Output.create(111,0,0,0,30,51,51,51,51,30,0,0); // o

do Output.create(112,0,0,0,30,51,51,51,31,3,3,0); // p

do Output.create(113,0,0,0,30,51,51,51,62,48,48,0); // q

do Output.create(114,0,0,0,29,55,51,3,3,7,0,0); // r

do Output.create(115,0,0,0,30,51,6,24,51,30,0,0); // s

do Output.create(116,4,6,6,15,6,6,6,54,28,0,0); // t

do Output.create(117,0,0,0,27,27,27,27,27,54,0,0); // u

do Output.create(118,0,0,0,51,51,51,51,30,12,0,0); // v

do Output.create(119,0,0,0,51,51,51,63,63,18,0,0); // w

do Output.create(120,0,0,0,51,30,12,12,30,51,0,0); // x

do Output.create(121,0,0,0,51,51,51,62,48,24,15,0); // y

do Output.create(122,0,0,0,63,27,12,6,51,63,0,0); // z

do Output.create(123,56,12,12,12,7,12,12,12,56,0,0); // {

do Output.create(124,12,12,12,12,12,12,12,12,12,0,0); // |

do Output.create(125,7,12,12,12,56,12,12,12,7,0,0); // }

do Output.create(126,38,45,25,0,0,0,0,0,0,0,0); // ~


return;

}


function void create(int index, int a, int b, int c, int d, int e,

int f, int g, int h, int i, int j, int k) {

var Array map;


let map = Array.new(11);

let charMaps[index] = map;


let map[0] = a;

let map[1] = b;

let map[2] = c;

let map[3] = d;

let map[4] = e;

let map[5] = f;

let map[6] = g;

let map[7] = h;

let map[8] = i;

let map[9] = j;

let map[10] = k;


return;

}

function Array getMap(char c) {

if ((c < 32) | (c > 126)) {

let c = 0;

}

return charMaps[c];

}


function void moveCursor(int i, int j) {

if (i > -1 & i < 23) {

let row = i;

}

else {

let row = 0;

}

if (j > -1 & j < 64) {

let col = j;

}

else {

let col = 0;

}

return;

}


function void printChar(char c) {

var Array map;

var int address;

var int pixel;

var int left;

var int i;

let map = Output.getMap(c);

let address = (row * 32 * 11) + (col / (16 / 8));

let left = col & 1;

let i = 0;

while (i < 11) {

let pixel = map[i];

if (left = 1) {

let pixel = pixel * 256;

}

let screen[address] = screen[address] & align[left] | pixel;

let address = address + 32;

let i = i + 1;

}

if (col = 63) {

if (row = 22) {

do Output.moveCursor(0,0);

}

else {

do Output.println();

}

}

else {

let col = col + 1;

}

return;

}


function void printString(String s) {

var int i;

let i = 0;

while (i < s.length()) {

do Output.printChar(s.charAt(i));

let i = i + 1;

}

return;

}


function void printInt(int i) {

var String s;

let s = String.new(16);

do s.setInt(i);

do Output.printString(s);

do s.dispose();

return;

}


function void println() {

if (row < 22) {

let lineLength[row] = col;

let row = row + 1;

}

else {

let row = 0;

}

let col = 0;

return;

}


function void backSpace() {

if (col = 0) {

if (row > 0) {

let row = row - 1;

let col = lineLength[row];

}

return;

}

if (col > 0) {

let col = col - 1;

}

do Output.printChar(" ");

let col = col - 1;

return;

}

}

Screen.jack

class Screen {


static boolean whitePixel;

static boolean blackPixel;

static boolean color;

static Array screen;

function void init() {

let screen = 16384;

let whitePixel = false;

let blackPixel = true;

let color = blackPixel;

return;

}


function void clearScreen() {

var int i;

let i = 0;

while (i < 8192) {

let screen[i] = whitePixel;

}

return;

}


function void setColor(boolean b) {

let color = b;

return;

}


function void drawPixel(int x, int y) {

var int address;

var int mask;

let address = (y * 32) + (x / 16);

let mask = Math.two_to_the(x & 15);

if (color) {

let screen[address] = screen[address] | mask;

}

else {

let screen[address] = screen[address] & ~mask;

}

return;

}


function void drawLine(int x1, int y1, int x2, int y2) {

var int dx, dy;

var int startX, startY;

var int i, j;

var int proportion;

let dx = x2 - x1;

let dy = y2 - y1;

let startX = Math.min(x1, x2);

let startY = Math.min(y1, y2);

// draw diagonal lines

if (((dx < 0) & (dy > 0)) | ((dx > 0) & (dy < 0))) {

if (dy < 0) {

do Screen.drawDiagonalLine(x1, y1, dx, dy);

} else {

do Screen.drawDiagonalLine(x2, y2, -dx, -dy);

}

return;

} else {

let dx = Math.abs(dx);

let dy = Math.abs(dy);

}

// When dx = 0 or dy = 0, use special functions

if (dy = 0) {

do Screen.drawVerticalLine(startX, startY, dx);

return;

}

if (dx = 0) {

do Screen.drawHorizontalLine(startX, startY, dy);

return;

}

let i = 0;

let j = 0;

let proportion = 0;

while (~(i > dx) & ~(j > dy)) {

do Screen.drawPixel(startX + i, startY + j);

if (proportion < 0) {

let i = i + 1;

let proportion = proportion + dy;

}

else {

let j = j + 1;

let proportion = proportion - dx;

}

}

return;

}


function void drawHorizontalLine(int x, int y, int dy) {

var int i;

let i = 0;

while (~(i > dy)) {

do Screen.drawPixel(x, y + i);

let i = i + 1;

}

return;

}

function void drawVerticalLine(int x, int y, int dx) {

var int i;

let i = 0;

while (~(i > dx)) {

do Screen.drawPixel(x + i, y);

let i = i + 1;

}

return;

}

function void drawDiagonalLine(int x, int y, int dx, int dy) {

var int proportion, i, j;

let proportion = 0;

let i = 0;

let j = 0;

while ((~(i > dx)) & (~(j < dy))) {

do Screen.drawPixel(x + i, y + j);

if (proportion < 0) {

let j = j - 1;

let proportion = proportion + dx;

} else {

let i = i + 1;

let proportion = proportion + dy;

}

}

return;

}

function void drawRectangle(int x1, int y1, int x2, int y2) {

var int dx, dy;

var int startX, startY;

var int i;

let dx = Math.abs(x2 - x1);

let dy = Math.abs(y2 - y1);

let startX = Math.min(x1, x2);

let startY = Math.min(y1, y2);

let i = 0;

while (i < dy) {

do Screen.drawVerticalLine(startX, startY + i, dx);

let i = i + 1;

}

return;

}


function void drawCircle(int cx, int cy, int r) {

var int dy;

var int sqrtDist;

let dy = -r;

while (~(dy > r)) {

let sqrtDist = Math.sqrt((r * r) - (dy * dy));

do Screen.drawVerticalLine(cx - sqrtDist, cy + dy, 2 * sqrtDist);

let dy = dy + 1;

}

return;

}

}

Keyboard.jack

class Keyboard {


static Array keyboard;

function void init() {

let keyboard = 24576;

return;

}


function char keyPressed() {

return keyboard[0];

}


function char readChar() {

var int c;

while (Keyboard.keyPressed() = 0) {}

let c = keyboard[0];

while (Keyboard.keyPressed() = c) {}

do Output.printChar(c);

return c;

}


function String readLine(String message) {

var String s;

var int c;

do Output.printString(message);

let s = String.new(100); //arbitrary length

let c = Keyboard.readChar();

while (~(c = String.newLine())) {

if (c = String.backSpace()) {

do s.eraseLastChar();

}

else {

do s.appendChar(c);

}

let c = Keyboard.readChar();

}

return s;

}


function int readInt(String message) {

var String s;

let s = Keyboard.readLine(message);

return s.intValue();

}

}

Memory.jack

class Memory {

static Array memory;

static int base, max;

static int length;

static int nextNode;

static int endOfList;

function void init() {

let memory = 0;

let base = 2048;

let max = 16384;

let length = base;

let nextNode = base + 1;

let endOfList = -1;

let memory[length] = max - base;

let memory[nextNode] = endOfList;

return;

}


function int peek(int address) {

return memory[address];

}


function void poke(int address, int value) {

let memory[address] = value;

return;

}


function int alloc(int size) {

var int currNode;

var int prevNode;

var int next;

var int returnAddress;

let prevNode = length;

let currNode = length;

let next = nextNode;

let returnAddress = -1;

while (~(memory[currNode] > size) & ~(memory[next] = endOfList)) {

let prevNode = currNode;

let currNode = memory[next];

let next = memory[next + 1];

}

if (~(next = endOfList)) { //found free block in the middle of the list

if (currNode < (size + 3)) {

let memory[prevNode + 1] = memory[currNode + 1];

let memory[currNode] = size + 1;

let returnAddress = currNode + 1;

}

else {

let memory[currNode] = memory[currNode] - size - 1;

let returnAddress = currNode + memory[currNode];

let memory[returnAddress - 1] = size + 1;

}

}

return returnAddress;

}


function void deAlloc(int object) {

let memory[object] = memory[object - 1];

let memory[object + 1] = memory[length + 1];

let memory[length + 1] = object;

return;

}

}


Sys.jack

class Sys {


function void init() {

do Memory.init();

do Math.init();

do Screen.init();

do Output.init();

do Keyboard.init();

do Main.main();

do Sys.halt();

return;

}


function void halt() {

while (true) {}

return;

}


function void wait(int duration) {

var int i;

var int j;

if (duration < 0) {

do Sys.error(1);

}

let i = 0;

while (i < duration) {

let j = 0;

while (j < 200) {

let j = j + 1;

}

let i = i + 1;

}

return;

}


function void error(int errorCode) {

do Output.printString("ERR");

do Output.printInt(errorCode);

do Sys.halt();

return;

}

}

Tests

Para cada clase del sistema operativo, se nos proporciona un archivo de clase xxx.jack con todas las subrutinas requeridas y con las clases main.jack junto con los scripts de prueba que siguen unas pautas que nos ayudan a verificar si los resultados son los esperados por medio de la herramienta VMEmulator por lo que hay que compilar cada clase y sus respectivos archivos de prueba .jack

Resultados obtenidos

Memory.jack

Como vemos en la figura, la comparación con el archivo de comparación finaliza correctamente.

Array.jack

Como vemos en la figura, la comparación con el archivo de comparación finaliza correctamente.

Math.jack

Como vemos en la figura, la comparación con el archivo de comparación finaliza correctamente.

Los siguientes programas de prueba restantes no incluyen scripts de prueba. Se deben probar directamente en el VMEmulato. Para comprobar que obtenemos los resultados esperados el proyecto nos proporciona imágenes sobre los resultados que debemos obtener‎

String.jack

Resultados esperados

Resultados obtenidos

Output.jack

Resultados esperados

Resultados obtenidos

Screen.jack

Resultados esperados

Resultados obtenidos

Keyboard.jack

Resultados esperados

Resultados obtenidos

Sys.jack

Resultados obtenidos

Una vez haber testeado con éxito cada clase del sistema operativo Jack de forma independiente, pasaremos a probar toda la implementación del sistema operativo utilizando el juego Pong, suministrado en el proyecto 11.