Arquitectura de Computadores
Curso: LMCC
Exame 2ª Chamada
99/02/13
Duração: 2h

 

Componente teórica ; Componente prática


Componente Prática

I

  1. Represente o seguinte número negativo usando a norma IEEE754 para números com vírgula flutuante, precisão simples: -17,125.
    Apresente o resultado final em binário e em hexadecimal.
  2. Represente o valor simétrico do valor indicado na alínea anterior.
    Apresente o resultado final em binário e em hexadecimal.

Sugestões para resolução:

  1. Clique aqui para obter informação àcerca da representação de reais em vírgula flutuante
    Clique aqui para obter informação àcerca na Norma IEEE754

    Formato para representação em Precisão Simples:
  2. [_|_._._._._._._._|_._._._._._._._._._._._._._._._._._._._._._._]
     S       E                            F  

    S = sinal - 1 bit
    E = expoente - 8 bits - representado em excesso +127
    F = fracção - 23 bits - valor absoluto, considerando a existência do bit escondido

    com, Valor = (-1)S * (1.F) * 2E-127

    17,125 = 17 + 1/8
    = 24 + 20 + 2-3
    = 10001,0012
    = 1,00010012 * 24
    -17,125 = (-1) * 1,0001001 * 24

    O número já se encontra na forma pretendida (-1)S * 1.F * 2E-127 com:

    S = 1
    F = 00010010000000000000…
    E = 4 (Como o expoente E é representado em excesso 127, o valor a colocar é 4+127 = 131 = 1000 00112

    [1|10000011|00010010000000000000000]
     S    E             F

    Agrupando os dígitos binários da direita para a esquerda em grupos de 4 facilmente se obtém a representação do mesmo número na base hexadecimal.

    Em binário: 1100 0001 1000 1001 0000 0000 0000 00002

    Em hexadecimal: 0xC1890000

  3. Valor simétrico de -17,125 = +17,125

    i) Se é muito trabalhador mas não gosta de pensar, refaça todos os cálculos anteriores para o número 17,125.

    ii) Se é pouco trabalhador mas mais esperto, troque o bit mais à esquerda passando de 1 a 0. Porquê?

    Em binário: 0100 0001 1000 1001 0000 0000 0000 00002

    Em hexadecimal: 0x41890000

     


II

Considere o seguinte programa escrito em assembly do MIPS

    li   $t1, 0x20080120
    lw   $t2, 4($t1)
    subi $t3, $t2, 1

Sabendo que:

Registo $t1 corresponde a $9

Nas instruções lw e sw o campo que contém o endereço é o Rs

A palavra de 4 bytes, colocada em memória no endereço 0x20080124 tem o valor inicial de 1000.

  1. Apresente o código máquina do MIPS que corresponde a este programa, em binário e hexadecimal. Apresente uma instrução por linha. Na representação em binário identifique o tipo de instrução e cada um dos campos que a constituem.
  2. Qual o valor dos registos $t1, $t2 e $t3 no fim da execução do programa?

 

Sugestões para resolução:

a)

li   $t1, 0x20080120
     => lui $t1, 0x2008
     => ori $t1, $t1, 0x0120
- Pseudo instrução. Substituir por instruções nativas MIPS. Há várias alternativas. Apresente outras.
lw   $t2, 4 ($t1)
- Instrução nativa MIPS.
subi $t3, $t2, 1
     => addi $t3, $t2, -1
- Pseudo instrução. Substituir por instruções nativas MIPS. Há mais alternativas? Quais?

O código anterior em que foram substituidas as pseudo-instruções por instruções nativas com resultado equivalente:

  lui  $t1, 0x2008       (I) [0x0f | 0 | 9 | 0x2008]
  ori  $t1, $t1, 0x0120  (I) [0x0d | 9 | 9 | 0x0120]
  lw   $t2, 4($t1)       (I) [0x23 | 9 | 10 | 4] 
  addi $t3, $t2, -1      (I) [0x08 | 10 | 11 | -1] 

NOTA: Nºs negativos (no campo Imm16) são representados em complemento para 2.

Tendo em conta a dimensão dos campos no formato (I) [opcode (6) | Rs (5) | Rt (5) | Imm (16) ] vem em binário:

   001111 00000 01001 0010 0000 0000 1000 = 0x3C092008
   001101 00000 01001 0000 0001 0010 0000 = 0x34090120
   100011 01001 01010 0000 0000 0000 0100 = 0x8D2A0004
   001000 01010 01011 1111 1111 1111 1111 = 0x214BFFFF

b) Para cada instrução vá anotando os valores de cada um dos registos $t1, $t2 e $t3.

 


III

Considere o seguinte programa escrito em linguagem C. Codifique este mesmo programa em assembly do MIPS respeitando a convenção do MIPS para a utilização de registos. (Este exercício é parecido, mas não é o mesmo que saiu no exame)

struct alunos_t 
{   char nome [ 10 ];
    int idade;
} turma[3] = { { "João", 22 }, { "Anabela", 21 }, { "Manuel", 22 } };


void Calcula_Idade( struct alunos_t turma [], int dim )
{
    int idade, i;

    idade = turma[0].idade;
    for ( i = 1; i < dim; i ++ )
        if ( turma[i].idade < idade ) idade = turma[i].idade;
    return idade; 
}


void main()
{
    int junior = Calcula_Idade( turma, 3 );
}

Análise do enunciado:

Depois da interpretação do enunciado em cima poderá verificar que o objectivo deste exercício é procurar a idade do aluno mais novo de uma qualquer turma (talvez para uma aposta, quem sabe...).

A turma é representada por um array de alunos e cada aluno representado por um record constituído pelo seu nome e idade. O algoritmo base assenta na função Calcula_Idade que vai empreender o "trabalho sujo" de vasculhar o enorme array (de 3 elementos) que é apresentado.

A resolução deste exercício apresenta diversos sub-problemas, como a declaração dos dados, a implementação da função Calcula_Idade e da função main, podendo cada um deles ser ainda mais dividido, como veremos adiante nos comentários do programa.

Sugestões para resolução:

        .DATA     # declaração dos dados

Os dados são declarados como uma sequência de dados simples (.ASCIIZ corresponde a declarar uma string terminada por zero, ver anexo C), já que o assembly do MIPS não suporta a declaração de estruturas de dados. Todos os elementos do array precisam, no entanto, de ter o mesmo tamanho. Assim, como o nome do aluno varia em tamanho, foi necessário introduzir um factor de alinhamento ao tamanho de record pretendido (com .SPACE).

turma:  .asciiz "João"     #turma[0]
        .space 5
        .word 22
        .asciiz "Anabela"  #turma[1]
        .space 2
        .word 21
        .asciiz "Manuel"   #turma[2]
        .space 3
        .word 22


        .TEXT

calcula_idade:                # int calcula_idade ( alunos_t turma[] => $a0,
                              #                     int dim => $a1 )
                              # salvaguarda de registos
                              #   neste caso não são utilizados registos
                              #   que seja necessário salvaguardar
                              # atribuição de variáveis locais
                              #   int idade => $t0 
                              #   int i => $s1

        lw   $t0, 10($a0)     # idade = turma[0].idade
        li   $s1, 1           # i = 1
ciclo:                        # for (i=1; i<dim; i++)
        bge  $s1, $a1, fim_ciclo

        addi $a0, $a0, 14     # Note-se que aqui se preferiu considerar que
                              # o valor do apontador para o array é 
                              # incrementado a cada iteração do ciclo, 
                              # aproveitanto o facto de i ser sempre
                              # incrementado uma unidade (a que corresponde
                              # avançar registo com 14 bytes).
                              # Uma alternativa, menos eficiente mas mais
                              # genérica, seria recalcular a posição do
                              # elemento actual em cada iteração.
if: 
                              # registo temporário
        lw   $t2, 10($a0)     # turma[n].idade = $t2
        bge  $t2, $t0, fim_if # if ( turma[i].idade < idade )
        move $t0, $t2         # idade = turma[i].idade;
fim_if:
        b    ciclo
fim_ciclo:
        move $v0, $t0         # return idade;
                              # restaurar os registos
        jr   $ra


main:                         # void main()
        addi $sp, $sp, -8     # salvaguardar os registos:
        sw   $s0, 0($sp)      # Embora a função main seja iniciada pelo
                              # sistema, não deixa de ser uma função como
                              # outra qualquer, sendo, assim, necessário
                              # salvaguardar os registo seguros que utiliza.
        sw   $ra, 4($sp)      # Como não é um procedimento folha, i.e.,
                              # invoca outras
                              # funções, é necessário salvaguardar o registo
                              # de retorno.
                              # int junior = $s0  (variáveis locais)
        la   $a0, turma       # definir os parâmetros da função
        li   $a1, 3
        jal  calcula_idade    # invocar a função
        move $s0, $v0         # guardar resultados
        addi $sp, $sp, 8      # restaurar os registos
        lw   $s0, 0($sp)
        lw   $ra, 4($sp)
        jr   $ra