Esse trabalho está sendo implementado como requisito da disciplina Software Básico da Universidade de Brasília (UnB). Ele consiste no processo de tradução (montador + ligador + carregador) do assembly inventado especificado a seguir.
A linguagem de montagem utilizada será a linguagem simbólica hipotética apresentada em sala de aula. Esta linguagem e formada por um conjunto de apenas 14 instruções.
Instruções | ||||
Mneumônico | Operandos | Código | Tamanho | Descrição |
ADD | 1 | 1 | 2 | ACC <- ACC + MEM[OP] |
SUB | 1 | 2 | 2 | ACC <- ACC - MEM[OP] |
MULT | 1 | 3 | 2 | ACC <- ACC * MEM[OP] |
DIV | 1 | 4 | 2 | ACC <- ACC / MEM[OP] |
JMP | 1 | 5 | 2 | PC <- OP |
JMPN | 1 | 6 | 2 | Se ACC < 0, PC <- OP |
JMPP | 1 | 7 | 2 | Se ACC > 0, PC <- OP |
JMPZ | 1 | 8 | 2 | Se ACC = 0, PC <- OP |
COPY | 2 | 9 | 3 | MEM[OP2] <- MEM[OP1] |
LOAD | 1 | 10 | 2 | ACC <- MEM[OP] |
STORE | 1 | 11 | 2 | MEM[OP] <- ACC |
INPUT | 1 | 12 | 2 | MEM[OP] <- STDIN |
OUTPUT | 1 | 13 | 2 | STDOUT <- MEM[OP] |
STOP | 0 | 14 | 1 | Encerrar execução |
Diretivas | ||||
Mneumônico | Operandos | Tamanho | Descrição | |
SECTION | 1 | 0 | Marcar inıcio de seção de codigo (TEXT) ou dados (DATA) | |
SPACE | 0/1 | Variável | Reservar 1 ou mais enderecos de memoria nao-inicializada para armazenamento de uma palavra | |
CONST | 1 | 1 | Reservar memoria para armazenamento de uma constante inteira de 16 bits em base decimal ou hexadecimal | |
EQU | 1 | 0 | Cria um sinônimo textual para um símbolo | |
IF | 1 | 0 | Instrue o montador a incluir a linha seguinte do codigo somente se o valor do operando for 1 | |
BEGIN | 0 | 0 | Início de um módulo | |
END | 0 | 0 | Fim de um módulo | |
EXTERN | 0 | 0 | Indica que o rótulo é definido em um módulo externo | |
PUBLIC | 0 | 0 | Indica que o rótulo é público para outros módulos |
Deve-se implementar um montador do assembly inventado descrito acima (não há tratamento de macros).
Além disso, o mesmo deve receber de 1 a 3 programas de entrada (ex.: ./montador prog1.asm prog2.asm prog3.asm). Assim, se um unico programa foi passado como entrada então o mesmo não deve ter as diretivas BEGIN e END. Já se 2 ou 3 programas foram passados como entrada, as diretivas BEGIN e END são obrigatorias. Checar se as regras acima aplicam-se é o ́unico teste de erro que o montador deve fazer.
Por fim, o arquivo objeto (.o) gerado deve conter o cabeçalho indicado pela letra H (contendo nome, tamanho e informações de realocação do código), a Tabela de Uso indicada pelas letras TU e a Tabela de Definições indicada pelas letras TD.
Recebe como entrada o(s) arquivo(s) objeto(s) gerador pelo montador. Deve-se realizar o processo de ligação dada a ordem dos arquivos passados gerando um único arquivo executável. O arquivo executável não deve ter extensão e deve conter um cabeçalho cobtebdo nome, tamanho e informações de realocação.
Recebe como entrada o arquivo executável gerado pelo ligador e uma sequência de números. Nessa sequência de números, o primeiro representa a quantidade de chunks, os próximos indicam o tamanho em bytes de cada chunk e os últimos os endereços iniciais de cada chunk.
Assim, o carregador deve simular a execução do programa e depois tentar gerar um arquivo de saída de imagem de memória. Para gerar esse arquivo leva-se em conta a sequência de números descrita acima. Cabe ao carregador verificar se um desses chunks é suficiente para suportar o programa inteiro. Se não for, deve-se verificar se ́e possıvel entao dividir o programa em diferentes chunks. Caso, não seja possível, o carregador deve indicar uma mensagem de: “OUT OF MEMORY - YOUR PROGRAM WILL NOT BE LOADED”. Caso contrario, o carregador deve modificar a informação relativa para os enderecos corretos e colocar o codigo final (sem cabecalho) em um arquivo com extensão ".im".
$ make
$ make debug
$ make release
$ make clean
$ make doc
$ make clean
$ ./assembler/montador [<programa>.asm]
$ ./linker/ligador [<programa>.asm]
$ ./loader/carregador <executável> <quantidade de chunks> [<tamanho de cada chunk>] [<endereço inicial de cada chunk>]
Esse projeto depende do Bison e do Flex. O uso de ambos foi autorizado pelo professor da disciplina e ambos geralmente já vem incluídos nos pacotes de utilitários essenciais (build essentials).
Ressalta-se a necessidade do Bison com versão 3.o ou superior e do Flex com versão 2.5 ou superior. Por serem utilitários GNU padrões, se necessário é possível atualizar ambos de forma padrão (com apt-get em distribuições Ubuntu, por exemplo).
Observa-se que os códigos gerados pelo Bison e pelo Flex não são tão legíveis como os códigos feitos a mão. Desta forma, para melhor entendimento dos arquivos scanner e parser recomenda-se analisar os arquivos do diretório generate (são eles que definem as regras para geração dos códigos Flex e Bison).
Esse projeto necessita do compilador GCC na versão 5.x ou maior.
O código será escrito com o padrão "Camel Case". Como padrão, as variáveis, abstrações e nomes no código são em inglês, bem como as mensagens de saída. Já toda a documentação foi feita em português.
A linguagem de programação utilizada é o C++ e o projeto segue diretrizes (Guidelines) para programar nessa linguagem feitas pelos desenvolvedores da Google.
Em nossos estudos para entender o Bison e o Flex, nos deparamos com dois repositórios interessantes (aqui e aqui). Nesse percurso, ambos nos inspiraram e nos forneceram uma base inicial de partida. Portanto, gostaríamos de deixar aqui o nosso agradecimento aos autores desse repositórios!