Henrique M. D. Santos DSI/UM Índice... 1 1. Nível Introdutório... 1 1.1 Expressões simples, transferências com a memória e I/O... 1 1.2 Arrays... 2 1.3 Operações condicionais, de salto e ciclos... 2 1.4 Procedimentos... 4 1. Nível Introdutório 1.1 Expressões simples, transferências com a memória e I/O 1. Implementar em assembly do MIPS um programa que execute o cálculo da expressão: a = (b + c) - (d + e + f). Utilize os registos $s para guardar o valor das variáveis. Complete o programa carregando nas variáveis (registos) valores iniciais à sua escolha e, finalmente, execute o programa no SPIM. Faça executar o programa passo-a-passo, observando cuidadosamente as alterações sucessivas que vão ocorrendo nos registos. O que acontece quando o programa chega ao fim? Comente esse comportamento. Nota: para alterar o valor dos registos utilize, no PCSPim, o menu Simulator -> Set value que lhe dá acesso a uma janela onde pode introduzir o nome de um registo e o valor que lá pretende colocar (em decimal ou hexadecimal); no QtSpim, com o ponteiro sobre o registo em causa e com o botão direito do rato, no menu de contexto seleccione a opção Change Register Contents. TPC1. Implementar em assembly do MIPS um programa que declare dois inteiros (12345 e 0x12345 este último está representado em notação hexadecimal), um byte, com o valor \n (notação simbólica para o caracter newline ASCII 10, ou 0x0A) e uma variável inteira temp. De seguida escreva as instruções necessárias para copiar o conteúdo de cada um dos valores declarados para a variável temp. Faça executar o programa passo-a-passo, observando cuidadosamente as alterações sucessivas que vão ocorrendo na posição de memória associada à variável temp. 2. Complete o programa feito em 1., fazendo aparecer o resultado do cálculo da expressão no monitor consola do SPIM, na respectiva terminologia. Execute o programa passo-a-passo, observando cuidadosamente as alterações sucessivas que vão ocorrendo nos registos. Nota: para escrever no monitor do SPIM utilize o system call apropriado (consulte a tabela fornecida na documentação). TPC2. Altere o programa feito em TPC1. para escrever no monitor do SPIM o valor dos dois inteiros declarados, tendo o cuidado de escrever o caracter \n após cada um dos inteiros. Inclua ainda a leitura de um inteiro do teclado, seguido do respectivo armazenamento na variável temp. Conclua escrevendo no monitor o endereço da primeira variável declarada (estude com cuidado a diferença entre as instruções la e lw). Universidade do Minho, Dpt. Sistemas de Informação, 2011 Página 1 de 5
TPC2a. Implementar em assembly do MIPS um programa que leia do teclado uma temperatura dada em graus celsius, calcule a temperatura equivalente em graus Fahrenheit e que escreva no monitor uma mensagem indicando esse resultado. 1.2 Arrays 3. Implementar em assembly do MIPS um programa que declare um array A de 10 inteiros, com os valores iniciais 10, 20, 30, 40, 50, 60, 70, 80, 90 e 100 e ainda uma variável H com o valor inicial 1000. O programa deverá somar o valor da variável H ao valor armazenado na posição 8 do array, isto é, H + A[8]. O resultado deve ficar num registo $s à escolha e deverá ser mostrado na consola do SPIM. Execute o programa passo-a-passo, observando cuidadosamente as alterações sucessivas que vão ocorrendo nos registos. TPC3. Implementar em assembly do MIPS um programa que declare a string Olá Mundo!, terminada pelo caracter 0 (este formato é exigido pela função do SPIM que escreve strings no monitor). De seguida escreva as instruções necessárias para escrever essa string no monitor. Execute o programa passo-apasso, observando cuidadosamente a forma como a string está armazenada na memória. 4. Modifique o programa construído em 3., aumentando o número de elementos do array A para 15 (acrescentando os valores iniciais 110, 120, 130, 140 e 150) e permitindo que o índice da posição do array a utilizar na soma seja introduzido pelo utilizador (obviamente, com o auxílio de uma system call do SPIM). Execute o programa passo-a-passo, para diferentes valores do índice, e observando cuidadosamente as alterações sucessivas que vão ocorrendo nos registos. Nota: assuma que o utilizador introduz sempre um valor compreendido entre 0 e 14. 5. Implementar em assembly do MIPS um programa que declare um array A de 10 inteiros (com os valores iniciais 0, 1, 2, 3, 4, 5, 6, 7, 8 e 9) e que faça a troca do valor entre as posições k e k+1. O valor k deve ser introduzido pelo utilizador em run time). Execute o programa passo-a-passo, várias vezes e com diferentes valores de k, observando cuidadosamente as alterações sucessivas que vão ocorrendo nos registos. Nota: fica à sua consideração fazer ou não a validação do valor de entrada k. Q: O que aconteceria se o valor k fosse maior que 9? 1.3 Operações condicionais, de salto e ciclos 6. Implementar em assembly do MIPS um programa que compare dois valores contidos nos registos $t0 e $t1 e que mostre a mensagem menor ou igual, se $t0 <= $t1 ou a mensagem maior, se $t0 > $t1. Os valores de $t0 e de $t1 devem ser considerados constantes e carregados nos registos no início do programa. O programa deve terminar, devolvendo o controlo ao SPIM. Execute o programa de forma contínua, eventualmente definindo um ou dois pontos de paragem (break points) intermédios. # Programa equivalente em C: # t0=5; # t1=4; # # if(t0<=t1) # printf("menor ou igual"); # # else # printf("maior"); # # /*resto do programa*/ 7. Implementar em assembly do MIPS, utilizando as instruções slt, beq e j, um programa que execute a seguinte operação, descrita em C : int save[]=0, 0, 0, 0, 0, 10, 20, 30,40, 50; for(i=0;i<10;i++) printf("%d\n",save[i]); Execute o programa e verifique o seu funcionamento. Universidade do Minho, Dpt. Sistemas de Informação, 2011 Página 2 de 5
7a. Implementar em assembly do MIPS um programa que reserve em memória espaço para um vector de 20 elementos (palavras de 32 bits) e que inicialize todos os elementos do vector com o valor da variável val, do tipo float (declare essa variável com o valor inicial 1e -3 ). É aconselhável que declare outras variáveis que poderá necessitar, tais como o índice com que vai precorrer o vector, o deslocamento para aceder a cada posição do vector em memória, o tamanho de cada elemento do vector, o valor máximo do deslocamento dentro do vector, etc. Execute o programa e verifique e verifique o seu funcionamento, observando o conteúdo final da memória. TPC7a. Este exercício é uma variante do exercício 7a., considerando agora uma matriz de duas dimensões (4 linhas e 3 colunas). O preenchimento da matriz deve ser feito recorrendo a um ciclo que faz o preenchimento das colunas, dentro de um outro que percorre as linhas. O programa deve ser executado utilizando breakpoints no início e fim de cada ciclo. Nota: uma das soluções consiste em trabalhar com os índices e efectuar as multiplicações necessárias, em cada passo, para determinar a localização exacta de cada elemento da matriz. Uma segunda solução consiste em determinar inicialmente o tamanho (em bytes) de uma linha e executar apenas uma adição para avançar o ponteiro para a linha seguinte. À partida, esta segunda solução deve ser mais rápida, pois executará menos multiplicações estas são as operações mais caras, em termos do tempo de execução. 7b. Implementar em assembly do MIPS um programa que declare um vector de 15 inteiros (defina livremente os seus valores iniciais) e que permita determinar o valor máximo e mínimo contido nesse vector. Os valores máximo e mínimo devem ser armazenados nas variáveis max e min, devendo ser ainda enviados para o monitor (obviamente, enquadrados numa mensagem adequada). 8. Este exercício destina-se a demonstrar a forma como o Assembler do SPIM calcula o deslocamento a utilizar nos saltos (instruções de branch) e as limitações que estas instruções de salto evidenciam. Carregue para o SPIM o seguinte programa e execute-o passo-a-passo, até que apareça uma mensagem na consola. Procure interpretar essa mensagem de erro. Q: Que alteração deveria ser introduzida para corrigir este programa? TPC8a. Implementar em assembly do MIPS um programa que leia do teclado um valor inteiro, convertao para uma string e escreva no monitor essa string (de certa forma é o que faz a função printf do C quando tem que escrever no monitor um inteiro ) TPC8b. Implementar em assembly do MIPS um programa que leia do teclado um valor inteiro, determine a sua representação em notação hexadecimal (string de símbolos hexadecimais) e escreva no monitor a string com essa representação (mais uma vez, esta é uma das operações que a função printf do C tem que fazer ). 8a. Implementar em assembly do MIPS um programa que permita contar o número de caracteres existente numa string. A string deve ser declarada globalmente ao programa através da directiva.asciiz, a qual permite criar em memória um vector # Programa que ilustra o mecanismo # de saltos (branch) do MIPS.text main: add $t0, $0, $0 beq $t1, $0, label1.text 0x00409000 label1: addi $t0, $t0, 1 int t1 = 0; while (str[t1]!= '\0') t1++; de caracteres, cujo último elemento tem o valor 0 ( \0 ou caracter null). O valor da contagem deve ser enviado para o monitor. Experimente o programa com diferentes strings. Universidade do Minho, Dpt. Sistemas de Informação, 2011 Página 3 de 5
TPC8c. Implementar em assembly do MIPS um programa que permita contar o número de ocorrências de um determinado caracter (char), numa determinada string (str) ambos declarados globalmente ao programa. O resultado da contagem deve ser enviado para o monitor do SPIM. Experimente o programa alterando a string e o caracter a procurar. 8b. Implementar em assembly do MIPS um programa que permita inverter a ordem dos caracteres numa string, utilizando para o efeito a stack. A string deve ser declarada globalmente. De notar que a estrutura que a stack iplementa (LIFO Last In First Out) presta-se naturalmente para o efeito pertendido. 1.4 Procedimentos 9. Este primeiro exercício sobre procedimentos procura apenas mostrar o mecanismo de chamada, a forma como a stack deve ser gerida e o conceito de variável local (ao procedimento). Implemente em assembly do MIPS um procedimento (addnsub)que execute a operação f=(g+h)-(i+j), sendo estas variáveis todas locais ao procedimento e inicializadas com valores arbitrários. Para demonstrar o seu funcionamento, implemente um programa que declare localmente variáveis com o mesmo nome das do procedimento, que lhes atribua valores diferentes do que são atribuídos no procedimento e que imprima no monitor esses valores, antes e depois da chamada do procedimento. O código C equivalente encontra-se na listagem junta. Nota 1: no procedimento as variáveis locais podem ser implementadas em registos, mas os valores desses registos devem ser salvaguardados na stack. Nota 2: Para devolver o valor ao programa principal, no procedimento deve adoptar a convenção definida para o MIPS, isto é, devolver o valor no registo $v0. int addnsub(void) int temp0, temp1, f,g,h,i,j; g=3; h=7; i=1; j=4; temp0=g+h; temp1=i+j; f=temp0-temp1; printf("\nno procedimento:"); return f; void main(void) int f,g,h,i,j,t0; f=10; g=20; h=30; i=40; j=50; printf("\nfora do procedimento:"); t0=addnsub(); printf("\nvalor devolvido:%d",t0); printf("\nfora do procedimento:"); 9a. A série de números de Fibonacci é muito utilizada em 0 sen 0 problemas de análise de tendências a sua origem resulta do estudo que Leonardo Fibonacci fez, por volta do ano 1200, Fn 1 sen 1 relativamente ao crescimento das colónias de coelhos, supondo Fn 1 Fn 2 sen 2 que cada casal gera um casal, ao fim de um mês; os números de Fibonacci indicam o número de pares de coelhos, ao fim de cada mês. A função de Fibonacci é facilmente descrita pela expressão incluída na caixa anexa. Implementar em assembly do MIPS um procedimento que permita calcular o valor da função de Fibonacci para um dado valor de n, valor esse que, de acordo com a convenção assumida para o MIPS, deve ser passado pelo registo $a0. Utilizando a mesma convenção, o procedimento deve devolver o valor calculado no registo $v0. Teste o procedimento com um programa que o chame e que envie para o monitor do SPIM o resultado. Nota: não utilize uma solução recursiva. Universidade do Minho, Dpt. Sistemas de Informação, 2011 Página 4 de 5
TPC9a. Implementar em assembly do MIPS um procedimento que calcule a raiz quadrada inteira de um número inteiro x, passado como parâmetro, executando um dos vários algoritmos disponíveis para o efeito e que é sumariamente descrito pelo código C anexo. As variáveis locais devem ser implementadas na stack e os registos utilizados devem ser salvaguardados. Teste o procedimento com um programa adequado. Se necessitar, encontra uma descrição detalhada deste algoritmo em http://www.pedrofreire.com/crea2_pt.htm int isqrt(x) int r; x = (x + 1) >> 1; for(r = 0; x > r; r++) x = x r; return r; 9b. Implementar em assembly do MIPS um procedimento recursivo que permita calcular o factorial de um número passado como parâmetro. Em anexo encontra a solução equivalente feita em C. O parâmetro de entrada deve ser passado pelo registo $a0 e o resultado deve ser devolvido no int fact (int n) if (n < 1) return (1); else return (n * fact(n - 1)); registo $va (seguindo a convenção para o MIPS). Experimente o procedimento utilizando um programa principal que peça ao utilizador o número relativamente ao qual se deseja calcular o factorial e que envie para o monitor do SPIM o resultado. TPC9b. Modifique o exercício 9a transformando o procedimento iterativo num procedimento recursivo. Experimente a solução com diferentes valores de entrada. TPC9c. O puzzle conhecido por Torres de Hanoi constitui um problema frequentemente utilizado para ilustrar algumas técnicas de programação. Na sua essência, este problema consiste em descobrir uma forma de transportar um número N de discos, de dimensões distintas, da coluna 1 para uma das outras duas colunas. A figura ilustra o tabuleiro deste puzzle, no seu estado inicial. Os discos devem ser transportados um-a-um e, em cada coluna, devem sempre respeitar o empilhamento segundo uma ordem decrescente do tamanho. Esta última restrição não se aplica apenas aos estados inicial e final, mas a todos os possíveis estados intermédios. A solução deste problema existe sob a forma de um algoritmo recursivo, o qual se encontra listado na figura (s representa a coluna de origem, d a coluna destino e spare a coluna extra). 1 2 3 hanoi(n, s, d, spare) if (N == 1) move1(s, d); else hanoi(n-1, s, spare, d); move1(s, d); hanoi(n-1, spare, d, s); Para implementar este algoritmo em assembly do MIPS sugere-se a utilização de arrays para representar as colunas, sendo o conteúdo de cada posição do array o número do disco que lá se encontra armazenado, considerando os discos numerados de 1 até N. A dimensão dos arrays depende do número de discos e sugere-se 10 como limite. Assim, como exemplo, no estado inicial e no caso ilustrado na figura, o array que representa a coluna 1 (array s no algoritmo) poderá conter os valores s[0]=3, s[1]=2 e s[2]=1 (ou, de uma forma visual mais natural, s[7]=1, s[8]=2 e s[9]=3). Para verificar a solução será desejável que a função move1 envie para o monitor do SPIM uma indicação da operação que realizou, indicação essa que poderá ser uma simples mensagem ou uma imagem com caracteres alfanuméricos que ilustre mais fielmente o estado de cada uma das colunas (neste último caso será eventualmente mais útil utilizar os arrays da forma alternativa descrita acima ). Universidade do Minho, Dpt. Sistemas de Informação, 2011 Página 5 de 5