Skip to content

RenanGalvao/Hack-Machine-Assembler

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Hack Machine Assembler

Assembler para o Hack Assembly Language1, feito em C para o projeto #6 do curso Nand2Tetris. O curso Nand2Tetris visa construir um computador de uso geral a partir do portão lógico Nand até o famoso jogo de Tetris.

Linux C

Tabela de Conteúdo

Hack Machine Language

Consiste em duas instruções, especificadas na seguinte imagem: Hack Machine Language Specification

A-Instruction

Modifica o registro A para um valor de 15 bits. A versão binária consiste em dois campos, opcode e o valor não negativo em binário. Por exemplo: @5 ou 0000000000000101 em binário, armazena o valor 5 no registro A.

É usado para três operações:

  • acessar valores constantes2
  • acessar um registo de memória e consequentemente seu valor
  • selecionar um endereço caso o jump ocorra

C-Instruction

Diz o que computar, onde armazenar e o que será feito a seguir. Na versão binária o bit mais à esquerda é o opcode dessa instrução, que é 1. Os próximos dois bits não são utilizados e por convenção são deixados com o valor de 1. Os próximos 7 bits são os de computação. Os próximos 3 bits são de destino e os últimos 3 são para a instrução de jump.

comp

O Hack ALU foi projetado para computar um conjunto fixo de funções dado dois inputs de 16 bits. Apesar de potencialmente poder computar até 128 funções3, apenas 28 funções são listadas na especificação.

Exemplos:

  1. Para computar o valor de D menos 1, olhando a espeficicação, isso pode ser feito utilizando a instrução simbólica D-1, que equivale em binário 1110001110000000.

  2. Para computar o bitwise or entre os valores de D e de M, podemos utilizar a instrução simbólica D|M , que equivale em binário 1111010101000000.

dest

Campo opcional, pode ser utilizado para armazenar o resultado da computação nos 3 registros simultaneamente. O primeiro d-bit diz se deve armazenar o resultado no registro A, o segundo no D e o terceiro no M, que é o registro de memória selecionado naquele momento.

Exemplo: caso se queira incrementar um valor que está na mémoria, é preciso selecionar o registro da memória com que se irá trabalhar e depois realizar as operações necessárias:

    0000000000000111 // @7
    1111110111011000 // DM=M+1

Observe que neste caso, o resultado também é salvo no registro D, que geralmente é utilizado para operações futuras.

jump

A execução das instruções geralmente seguem-se uma após a outra, o campo jump permite que esse fluxo seja alterado, porém antes é necessário dizer qual instrução será executada, caso a condição seja realizada, com um A-instruction. O jump incondicional, realiza-se com 0;JMP, o que em binário equivale a 1110000000000111.

Observação: como uma A-instruction seleciona tanto um endereço na RAM como ROM, e fazemos uso dele na C-instruction, seja para acessar o valor da RAM ou para realizar um jump, como boa prática não se faz referência ao registro M quando se tem uma instrução de jump.

Símbolos

Utilizados para representar endereços na memória RAM ou ROM. São divididos em três categorias:

  • Predefinidos
  • Rótulos
  • Variáveis

Pode ser qualquer qualquer sequência de letras, dígitos, _, ., $ e : que não comece com um dígito.

Predefinidos

  • R0 a R15 RAM[0] até a RAM[15]
  • SP4 RAM[0]
  • LCL4 RAM[1]
  • ARG4 RAM[2]
  • THIS4 RAM[3]
  • THAT4 RAM[4]
  • SCREEN RAM[16384]
  • KBD RAM[24576]

Rótulos

Declarados (LABEL), liga o símbolo LABEL ao endereço da próxima instrução. Instruções jump que fazem uso dos rótulos podem aparecer em qualquer parte do programa, inclusive antes deles serem declarados. Como convenção, os rótulos são escritos em letras maiúsculas. É uma pseudo-instrução já que nenhum código binário é gerado a partir dela.

Variáveis

Qualquer símbolo que não seja predefinido ou que seja um rótulo é tratado como variável. Ela é associada a um endereço de memória RAM decidido pelo assembler, por ordem de aparição começando pelo endereço RAM[16]. Como convenção, as variáveis são escritas em letras minúsculas.

Convenções de Sintaxe e de Formato de Arquivos

Arquivos com código binário

São escritos na Hack Machine Language, amarzenados em texto com a extensão hack, por exemplo Prog.hack. Cada linha do arquivo contém uma instrução, usando uma sequência de 16 dígitos de 0 ou 1. Quando um programa é carregado na memória de instrução, cada instrução é associada ao número respectivo da linha que está no arquivo começando pelo 0.

Arquivos com código assembly

São escritos na simbólica Hack Assembly Language, amarzenados em texto com a extenção asm, por exemplo Prog.asm. O arquivo é composto por instruções A e C, declaração de rótulos e comentários. Os comentários podem ser feitos usando // no início da linha. Espaços em branco e linhas em branco são ignorados. Todos os mnemônicos da linguagem símbolica devem ser escritos em letras maiúsculas.

Exemplo de Programa em Linguagem Simbólica

Ver código
// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/4/Mult.asm

// Multiplies R0 and R1 and stores the result in R2.
// (R0, R1, R2 refer to RAM[0], RAM[1], and RAM[2], respectively.)
// The algorithm is based on repetitive addition.

    // n = R1
    @R1
    D=M
    @n
    M=D

    // v = R0
    @R0
    D=M
    @v
    M=D

    // i = 0
    @i
    M=0

    // R2 = 0 - assert correct value
    @R2
    M=0

(LOOP)
    // if i == n goto END
    @i
    D=M
    @n
    D=D-M
    @END
    D;JEQ

    // R2 = R2 + v
    @R2
    D=M 
    @v
    D=D+M
    @R2
    M=D

    // i++
    @i
    M=M+1

    @LOOP
    0;JMP

(END)
    @END
    0;JMP

Hack Computer

É uma implementação em 16 bits da máquina de Von Neumann projetada para executar programas escritos em Hack Machine Language. Para realizar a tarefa, ela é composta por:

  • CPU
  • ROM 32k
  • RAM 16K + 8K + 2 byte (ou 1 word)
  • SCREEN
  • KEYBOARD
ver imagem

Computador

CPU (Central Processing Unit)

Responsável pela execução das instruções dos programas, cada instrução diz a CPU que computação realizar, que registro acessar e qual instrução será a próxima. É composta por ALU e um conjunto de registros.

A CPU é projetado para executar instruções A e C, sendo A os 16 bits são tratados como binários e armazenados na registro A; sendo C, é tratada como uma "cápsula" de bits de controle que especifica várias microoperações para serem realizadas em várias partes da CPU.

ver imagem

CPU

ALU (Arithmetic Logical Unit)

É o chip que realiza as operações lógicas e matemáticas de baixo nível que são próprias da implementação do Hack Computer. A quantidade de funcionalidades em grande parte é uma decisão de projeto, as que não forem implementadas no hardware são implementadas via software, com o custo de se tornarem mais lentas pois precisam de mais ciclos para serem concluídas.

ver imagem

ALU

Registros

Para que cálculos sejam feitos, é necessário que dados intermediários sejam guardados. Em tese esses dados poderiam ser amarzenados na RAM, o problema é que a RAM é outro chip e isso aumentaria consideravelmente o tempo de execução. Os registros funcionam como uma memória de baixa latência, porém são mais caros por residirem dentro da CPU, por isso costumam suportar pouca informação. No caso dessa CPU há apenas 3 registros: Data, Address e Program Counter.

ver imagem

Registro

Data

Usado para amarzenar valores.

Address

Usado para amarzenar valores ou selecionar um endereço de instrução ou na memória RAM.

Program Counter

Usado para controlar qual instrução será executada.

ver imagem

Registro

ROM

O chip é semelhante ao da RAM, com a exceção de ser uma memóra de leitura somente, é utilizada para carregar os programas que serão executados no Hack Computer. Também de 32K.

ver imagem

ROM

RAM

Utilizada como memória de uso geral, possui 32k endereços de registros de 16 bits5, também funciona como interface entre a CPU e os dispositivos de entrada/saída do computador. Os dois dispositivos, SCREEN e KEYBOARD interagem com o computador a partir de áreas mapeadas nesta memória.

ver imagem

RAM

SCREEN

Usado como dispositivo de saída, a tela é mapeada na RAM nos endereços de 16,384 a 24,575, possui uma resolução de 512x256, cores preto ou branco, totalizando 8k6 bits de informação. É continuamente atualizada para refletir as mudanças que são feitas no seu espaço de memória por uma lógica externa ao computador.

Cada linha da tela começa no canto superior esquerdo e é representado por 32 words consecutivas. Seguindo a convenção, o canto superior esquerdo é considerada a linha 0 e coluna 0. Para encontrar um pixel em específico, na linha l, coluna c, utiliza-se a fórmula c % 16 (do LSB ao MSB)7 da word mapeada na RAM[SCREEN + l*32 + col/16]. O pixel pode ser lido ou escrito, sendo 1 para preto e 0 para branco.

KEYBOARD

Usado como dispositivo de entrada, é mapeado na RAM no endereço 24,576 e reflete a tecla que está sendo pressionada no teclado físico, usado para leitura somente.

Conjunto de teclas Hack

Hack-Character-Set

Uso

  • Tenha o Ceedling instalado.
  • Obtenha uma cópia do repositório git clone https://github.com/RenanGalvao/hack-machine-assembler.git.
  • Entre na pasta e execute o build cd hack-machine-assembler && ceedling release.

Para gerar o código hack, utilize dentro da pasta raiz: ./build/release/assembler <source-file>. Caso não tenha nenhum tipo de erro no código fonte, o arquivo .hack estará na mesma pasta que o código fonte.

Cobertura de Testes

Directory: src/
Date: 2025-03-20 14:10:30
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Exec Total Coverage
Lines: 388 425 91.3%
Functions: 28 28 100.0%
Branches: 125 154 81.2%
File Lines Functions Branches
code.c 97.7 97.7% 42 / 43 100.0% 4 / 4 90.0% 9 / 10
hash-map.c 70.8 70.8% 63 / 89 100.0% 2 / 2 59.1% 26 / 44
parser.c 97.7 97.7% 84 / 86 100.0% 3 / 3 97.2% 35 / 36
tables.c 100.0 100.0% 72 / 72 100.0% 4 / 4 100.0% 2 / 2
utils.c 94.1 94.1% 127 / 135 100.0% 15 / 15 85.5% 53 / 62
Generated by: GCOVR (Version 5.2)

Autores

Licença

MIT

Footnotes

  1. Linguagem simbólica da Hack Machine Language.

  2. Os valores constantes vão de 0 até 32767 (2^15 - 1).

  3. 2^7 = 128.

  4. Usados no código intermediário gerados com o Jack Compiler. 2 3 4 5

  5. Na prática é possível acessar até o endereço <= 0x6000.

  6. Para cada pixel é utilizado apenas um bit e cada um deles possui 2 possibilidades, portanto: (512x256 / 8 ) / 2 = 8192 bytes ou 8kb em binário.

  7. O bit mais significado é o primeiro da esquerda.

About

Assembler para o Hack Assembly Language escrito em C.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published