PUCRS - INSTITUTO DE INFORMÁTICA
Arquitetura de Computadores I
Prof. Eduardo Augusto Bezerra
Data: 30/09/97
Simulador parcial para o MIPS
Implementar um simulador para a arquitetura MIPS. O simulador deverá
executar programas compostos por no mínimo as instruções
sublinhadas:
- Lógicas e aritméticas: add, addi, addu,
addiu, and, andi, div, divu, mult, multu, nor, or, ori, sll, sllv, sra,
srav, srl, srlv, sub, subu, xor, xori
- Comparação: slt, sltu, slti, sltiu
- Controle de fluxo: bczt, bczf, beq, bgez, bgezal,
bgtz, blez, bgezal, bltzal, bltz, bne, j, jal, jalr,
jr
- Transferência de dados: lui, lb, lbu, lh, lhu,
lw, lwcz, lwl, lwr, sb, sh, sw, swcz, swl, swr, mfhi, mflo,
mthi, mvtl, mfcz, mtcz
Adicionalmente, o tradutor de linguagem assembly para linguagem de máquina
deverá gerar o código equivalente das seguintes pseudo-instruções:
abs, neg, not, rem, li, seq, sne, blt, move, la.
O simulador deverá realizar as seguintes etapas:
- Leitura do programa fonte em assembly a partir de um arquivo texto;
- Tradução do programa em assembly para um código
equivalente em linguagem de máquina, resolvendo o problema dos enderecos
(labels das instruções e variáveis);
- Durante a tradução, verificar a existência de erros
(ex. instruções inválidas);
- Carga na memória do programa em linguagem de máquina;
- Execução do programa em linguagem de máquina,
mostrando os efeitos no hardware (registradores/memória). A execução
deverá ser nos modos direto e passo-a-passo.
Interface com o usuário:
- Janela contendo as posições da memória (com scroll).
Colocar o endereço da posição e o conteúdo;
- Apresentação de todos os registradores ($0..$31, PC,
LO, HI e flag de overflow localizado no co-processador 0);
- Janela com o código fonte em assembly (com scroll);
- Janela para informações, como por exemplo, ocorrência
de erros durante a tradução;
- Janela console, onde o usuário poderá visualizar os dados
gerados pelas chamadas de sistema (instrução syscal), e fornecer
dados via teclado;
- Facilidade para entrada dos seguintes comandos:
- carga do arquivo (fornecer o nome do arquivo e endereço de carga);
- início da execução (passo-a-passo ou direta);
- chaveamento entre binário, decimal e hexadecimal (modo de visualização
dos registradores/memória);
- reset - utilizado para limpar a memória/registradores.
Observações:
- As constantes podem aparecer nas instruções nos formatos:
decimal, hexadecimal (0x...), e binário (0b...);
- Utilizar uma memória com 1250 posições
(32 bits cada posição == 5000 bytes). Lembrar que algumas
instruções do MIPS endereçam a memória byte-a-byte
(ex. LW - realiza leitura de dados de 32 bits, endereçando a memória
byte-a-byte). Assim, implementar uma memória byte endereçavel;
- Utilizar o caracter # como indicador de comentário, assim tudo
o que estiver a direita do # pode ser desconsiderado;
- Utilizar dois pontos (:) após um determinado símbolo
para indicar que se trata de um label;
- O tradutor deverá considerar que o programador assembly pode
ter utilizado espaços ou caracteres de tabulação antes
do início dos comandos em cada linha do programa;
- Utilizar os registradores conforme convencionado nas aulas durante
o semestre, ou seja:
- $0 - manter a constante zero (0)
- $1 - auxiliar na execução de instruções;
- $2, $3 - contêm valores de retorno de subrotinas;
- $4 .. $7 - para passagem dos quatro primeiros parâmetros para
uma subrotina;
- $28 - ponteiro para a área de dados. Sempre conterá o
valor 2000 (em decimal), que é o endereço do primeiro byte
do segmento de dados.
- $29 - sempre conterá um ponteiro para o topo da pilha;
- $30 - sempre apontará para o quinto parâmetro (na pilha)
de uma subrotina. Os demais parâmetros estarão nos endereços
acima do quinto;
- $31 - utilizado para armazenar o endereço de retorno de subrotina.
- A chamada de sistema (instrução syscall) deverá
fornecer os seguintes serviços:
- impressão de inteiro (print_int)
- código da chamada: 1;
- argumentos: $4 <- valor inteiro a ser impresso
- impressão de string (print_string)
- código da chamada: 4;
- argumentos: $4 <- endereço da string a ser impressa
- leitura de inteiro (read_int)
- código da chamada: 5;
- argumentos: nenhum;
- resultado após execução: $2 contém valor
inteiro fornecido pelo usuário
- leitura de string (read_string)
- código da chamada: 8;
- argumentos: $4 <- endereço de memória que receberá
a string, e $5 recebe o tamanho da string a ser lida;
- resultado após execução: a posição
de memória apontada por $4 conterá a string fornecida pelo
usuário.
- Convenção a ser utilizada para tratamento de exceções
(por parte do sistema):
- Realizar uma simulação bastante simplificada do sistema
de tratamento de exceções do MIPS;
- Caso alguma operação aritmética resulte em overflow:
- rmazenar o endereço da próxima instrução
a ser executada em uma variável auxiliar;
- desviar para uma rotina do sistema de tratamento de exceções
que deverá simplesmente escrever uma mensagem de erro na tela informando
o endereço da instrução que causou a exceção;
- continuar a execução normal do programa.
- Caso o programa realize um desvio para uma instrução
inválida (inexistente), proceder de forma semelhante ao reallizado
no caso do overflow, porém exibida a mensagem de erro o programa
deverá ser encerrado.
- Convenção a ser utilizada para tratamento de sub-rotinas
(por parte do programador):
- Chamada de subrotinas:
- Passagem de parâmetros:
- Os primeiros 4 parâmetros são passados em $4 .. $7. Os
restantes são colocados na pilha (ver figura abaixo)
- Fazer $30 apontar para o quinto parâmetro (se houver).
- Registradores temporários ($4 .. $7, $8 .. $15, $24 e $25):
- Salvar esses registradores na pilha, pois a subrotina acionada utiliza
esses registradores sem salvá-los.
- Se a rotina que executou a subrotina precisar de algum deles, deverá
salvá-lo antes de acionar a subrotina.
- Execução de jal, que salva o endereço de
retorno em $31.
- Execução de subrotinas:
- Alocação de memória (na pilha, decrementando $29),
e empilhamento de:
- Registradores $16 .. $23 (poderão ser alterados na subrotina)
- Registradores $30 e $31
Obs. $31 só precisará ser salvo se a partir da subrotina
acionada houver execução de outra subrotina.
- Retorno de subrotinas:
- Se a rotina é uma função que retorna um valor,
colocá-lo em $2 e $3;
- Restaurar os registradores que estavam na pilha ($16 .. $23, $30 e
$31);
- Retornar ao programa chamador com jr $31.
Entrega do trabalho: documentação e apresentação:
- O simulador deverá ser de fácil utilização.
Caso necessário, preparar um manual do usuário (cópia
em papel ou on-line);
- Deverá ser entregue documentação contendo bugs
conhecidos, ou seja, os problemas existentes no simulador que ficaram sem
solução;
- Entregar os códigos fonte e executável em um disquete
informando qual compilador/versão foi utilizado;
- Poderá ser solicitada uma demonstração do simulador
e explicações sobre o código implementado.
- O trabalho é individual. Trabalhos iguais receberão
grau ZERO;
- , ou
seja, o módulo responsável pela tradução do
programa em assembly para linguagem de máquina. Esse módulo
deverá utilizar como entrada um arquivo texto contendo um fonte
em assembly e deverá fornecer como saída um arquivo texto
contendo o programa em linguagem de máquina (cada linha deverá
possuir uma única instrução - 32 bits - em binário
e hexadecimal);
- . Nessa
data deverá ser entregue o trabalho completo, composto pelo tradutor
e simulador.
- Os primeiros 15 minutos de cada aula até a data da entrega serão
reservados para esclarecimento de dúvidas sobre o trabalho.
Dicas:
- Implementar uma instrução (ou conjunto pequeno de instruções
semelhantes) por vez. Por exemplo, implementar rotina de tradução
da instrução ADD. Assim que estiver funcionando, implementar
a rotina de tradução da instrução SUB.
- NUNCA IMPLEMENTE O PROGRAMA COMPLETO PARA DEPOIS TESTAR. A medida que
forem sendo desenvolvidos pequenos módulos, realizar baterias de
testes;
- Utilizar o simulador SPIM como referência. Verificar o que o
SPIM faz durante a execução de um programa e comparar com
o simulador em construção.
- Exemplos de programas em assembly: exemplo1.s,
exemplo2.s, exemplo3.s