ATENÇÃO: O roteiro de aulas práticas está aqui.
referência: Aho & Ullman, 4.2.
add regdest, regop1, regop2 
addi regdest, regop1, const 
permite usar como um dos operandos (operando imediato uma constante de até 16 bits.
 addi regdest, $0, valor 
regdest:
lw regdest, (rege)
 onde rege contém o endereço de memória de onde
deve ser copiada a palavra
regdest:
sw regdest, (rege)
 onde rege contém o endereço de memória para onde
deve ser copiada a palavra
referência: Patterson&Hennessy, 4.3, 4.4
Suponha que as variáveis (int) 
a, b, c, d e e
estão alocadas respectivamente nos registradores
$s0, $s1, $s2, $s3 e $s4.
Como programar estruturas como:
if (a==b) c=d+e; d=a+c;ou
while (a<=b) {
  ...
  a++;
}
d=a+c;
?
Instruções de desvio do assembler servem para "quebrar" a execução sequencial, fazendo com que, sob certas condições, deixe de valer a regra que diz que sempre a próxima instrução a ser executada é a próxima fisicamente na memória.
Existem instruções de desvio  condicional e  incondicional.
No MIPS os desvios condicionais são chamados branch.
Um exemplode branch condicional é a instrução:
bne $s0, $s1, depois
Nesse caso, se o valor de $s0 não for igual ao valor de $s1,
o controle é desviado para o endereço indicado pelo label depois.
Para programar o if acima, poderíamos então escrever:
	bne $s0, $s1, depois
        add $s2, $s3, $s4
depois:	add $s3, $s0, $s2
Existem muitas outras instruções de desvio condicional (ver manual),
como
beq, beqz, bge, bgeu, bgt, etc.
Instruções de desvio incondicional são chamadas de jump
no MIPS.
Um exemplo de desvio incondicional é a instrução:
j depois
que faz com que o controle seja desviado para o endereço indicado pelo label depois, independentemente de qualquer condição.
Para implementar uma estrutura if ... else ... 
é necessário utilizar um desvio condicional e um incondicional!
Tente fazê-lo!
Para programar o while acima, poderíamos escrever:
loop: bgt $s0, $s1, depois # teste no inicio do loop; como fica o do..while? ... addi $s0, $s0, 1 j loop # desvia para teste depois: add $s0, $s1, $s2
A instrução de jump tem várias variantes.
Uma delas é a jr, que desvia para um endereço
armazenado em um registrador.
Outra variante importantíssima 
é a instrução jal, que antes de desviar armazena
o endereço da próxima instrução (sequencial) no registrador
$ra.
Essa instrução é usada para implementar chamadas 
de funções.
Por convenção então, uma função é chamada por jal.
Para retornar dela, pode-se usar
jr $ra
mas para isso é necessário garantir que a função não alterou
o conteúdo de $ra (o que infelizmente vai ocorrer, por exemplo,
se ela chamar outra função), ou então "salvar" o conteúdo de $ra em
outro local e restaurá-lo antes do retorno:
f:
	... 			# tarefas iniciais a serem discutidas depois
	move $s7, $ra
	... 			# corpo da funcao
        move $ra, $s7
        jr $ra
Outra convenção no retorno de funções: os resultados são colocados
em $vo e $v1
referência: Patterson&Hennessy, 3.5
referência: Patterson&Hennessy, 4.2
Lembre-se da instrução jal, usada para chamar
uma função. Essa instrução armazena o endereço de retorno
no registrador $ra.
Assim, o retorno da função pode ser feito com
j $ra
No entanto, se a função por sua vez chama outra função, ie,
usa jal, o conteúdo de $ra
não faz mais sentido na hora do retorno.
É necessário salvar o valor de retorno 
em algum outro lugar.
Para isso (e muitas outras coisas) é usada a pilha de execução, ou pilha de ativação. A pilha é uma área de memória principal usada como pilha, com operações de push (empilha) e pop (desempilha).
Em geral, o hardware dá algum suporte à manutenção dessa
pilha.
No caso da máquina MIPS, um registrador específico,
$sp, é dedicado ao enedereço do topo
da pilha.
Por motivos históricos, a pilha cresce em direção aos endereços
mais baixos de memória.
Ou seja, para alocar espaço para um endereço, devemos subtrair
4 de $sp (lembre-se que um endereço ocupa 4 bytes!)
e para desalocar devemos somar 4 a $sp.
Por exemplo:
-----------------------------------------------
 |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
-----------------------------------------------
                                      ^
                                      |
                                     $sp
empilhar: sub $sp, $sp, 4
          sw $ra, ($sp)  # suponha que o valor de $ra e' a13f45c6
resultado:
-----------------------------------------------
 |  |  |  |  |  |  |  |  |a1|3f|45|c6|  |  |
-----------------------------------------------
                          ^
                          |
                         $sp
desempilhar: lw $ra, ($sp)
             add $sp, $sp, 4
             
resultado:
-----------------------------------------------
 |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
-----------------------------------------------
                                      ^
                                      |
                                     $sp
Voltando ao exemplo da função, uma função como:
int boba1() {
  boba2();
  return 1;
}
pode ser escrita em assembler como:
boba1: 
    #salva valores
    sub $sp, $sp, 4
    sw $ra, ($sp)
    jal boba2
    addi $v0, $0, 1
 
    #restaura valores
    lw $ra, ($sp)
    add $sp, $sp, 4
   
    # retorna
    jr $ra
    
Como exemplo da necessidade de salvar mais valores além de $ra,
imagine que a função boba1 precise utilizar um registrador
para alocar uma variável local:
int boba1() {
  int i;
  for (i=10;i>0;i--)
    boba2();
  return 1;
}
Uma possibilidade é usar um  
Em vez de alocar a pilha posição por posição (subtrair 4 de cada vez),
é comum alocar de uma vez todo o espaço que a função vai precisar:
 referência: Patterson&Hennessy, 3.6 
Todas as CPUs oferecem algumas instruções que permitem manipular
bits individualmente dentro de palavras.
Tipicamente, temos instruções de shift e rotate,
que deslocam os bits para a direita ou esquerda dentro de
uma palavra, e instruções para operações lógicas (and, or, not, ...)
bit a bit.
 
As operações de shift left logical  e shift right logical 
permitem deslocar os bits de uma palavra n bits para a direita
ou esquerda, completando a palavra com zeros.
  
A operação shift right arithmetic  desloca os bits
de uma palavra n bits para a direita replicando à esquerda
o valor anterior do bit 31.
 
Supondo que não haja problema de overflow, o shift de 1 bit para
a esquerda equivale à multiplicação de um número por 2 e o shift aritmético
de 1 bit para a direita equivale à divisão inteira de um número por 2.
 
Isso ocorre porque estamos sempre "subtraindo 1" antes de
fazer a divisão.
 
E por que o shift funciona para multiplicar e dividir?
 
Para números positivos, acontece exatamente a mesma coisa na base 10.
Para negativos é que não é óbvio...
 
Se x é negativo, rep(x)=2k+x:
 
2*rep(x) = 2*(2k+x) = 2k+1+ 2x 
             = 2k+2k+2x 
 
sem overflow, 2x>2k-1, logo
 
 2k+2k+2x > 2k
 
logo haverá um carry para fora, descartando 2k,
e ficaremos com 
 
 2k+2x  = rep(2x)
 
rep(x) div 2 = 
2k-1+x/2
 
mas quando fazemos o shift aritmético em um negativo,
colocamos 1 no bit mais significativo, o que equivale a 
somar 2k-1, logo ficamos com
 
2k+x/2 = rep(x/2)
 referência: Patterson&Hennessy, 4.4 referência: Patterson&Hennessy, 4.8 
Vamos discutir como fica a declaração e utilização de vários
tipos de variáveis.
 
 
em assembler:
 
 
em assembler:
  em assembler:
 
obs: O uso do nome  
 
em assembler:
 
 
em assembler:
 
 
em assembler:
 
 
em assembler:
  em assembler:
 
 
em assembler:
 
 
em assembler:
  em assembler:
 
e se forem 2 arrays?
  em assembler:
 
 
em assembler:
 
 
em assembler:
  em assembler:
  em assembler:
  em assembler:
 
Estruturas introduzem o problema de alinhamento.
Cada campo da estrutura que corresponde a uma palavra deve 
necessariamente começar em um endereço múltiplo de 4.
Assim, se tivermos uma declaração como:
 
onde um campo ocupando dois bytes precede um campo que corresponde
a uma palavra, o compilador terá que deixar dois bytes não utilizado
entre o array de caracteres e o inteiro:
 
Observe que de qualquer forma o início da estrutura tem que
ser alinhado, senão não saberemos quantos bytes devem
ser "pulados" para chegar a um endereço múltiplo de 4.
 
As variáveis declaradas localmente (dentro de funções)
são tipicamente alocadas na pilha de ativação.
  Antes de dar exemplos de variáveis locais
alocadas na pilha, vamos rever o uso da pilha de ativação.
$t?, mas a função boba2
poderia utilizar esse registrador e corromper o valor de i
(sujar o registrador).
Outra possibilidade é usar um $s?, por exemplo, $s0.
Mas a função que chamou boba1 pode estar usando esse
registrador.
Assim, é necessário salvar o valor de  no início de
boba1 e restaurá-lo no final:
boba1: 
	#salva valores
	sub 	$sp, $sp, 4
	sw 	$ra, ($sp)
	sub 	$sp, $sp, 4
	sw 	$s0, ($sp)
	#inicio do for
	add 	$s0, $0, 10
rep:
	blez 	$s0, depois
	jal 	boba2
	sub 	$s0, $s0, 1
	j 	rep
depois:
	addi 	$v0, $0, 1
	#restaura valores
	lw 	$s0, ($sp)
	add 	$sp, $sp, 4
	lw 	$ra, ($sp)
	add 	$sp, $sp, 4
	# retorna
	jr 	$ra
    
boba1: 
	#salva valores
	sub 	$sp, $sp, 8
	sw 	$ra, 4($sp) # armazena em ($sp)+4
	sw 	$s0, ($sp)
	#inicio do for
	add 	$s0, $0, 10
rep:
	blez 	$s0, depois
	jal 	boba2
	sub 	$s0, $s0, 1
	j 	rep
depois:
	addi 	$v0, $0, 1
	#restaura valores
	lw 	$s0, ($sp)
	lw 	$ra, 4($sp)
	add 	$sp, $sp, 8
	# retorna
	jr 	$ra
 
 5/10 e 7/10 
 Operadores Bit a Bit 
 Shift 
	li 	$s0, 22 	# $s0 = 0000 ...  0000 0000 0000 0001 0110
        sll	$s1, $s0, 2	# $s1 = 0000 ...  0000 0000 0000 0101 1000
	srl	$s1, $s0, 2	# $s1 = 0000 ...  0000 0000 0000 0000 0101
	li 	$s0, -6 	# $s0 = 1111 ...  1111 1111 1111 1111 1010
	sra	$s1, $s0, 2	# $s1 = 1111 ...  1111 1111 1111 1111 1110
	li 	$s0, 22 	# $s0 = 0000 ...  0000 0000 0000 0001 0110
	sra	$s1, $s0, 2	# $s1 = 0000 ...  0000 0000 0000 0000 0101
	li 	$s0, 22 	# $s0 = 0000 ...  0000 0000 0000 0001 0110  22
        sll	$s1, $s0, 1	# $s1 = 0000 ...  0000 0000 0000 0010 1100  44
	sra	$s1, $s0, 1	# $s1 = 0000 ...  0000 0000 0000 0000 1011  11  
	li 	$s0, -6 	# $s0 = 1111 ...  1111 1111 1111 1111 1010  -6
        sll	$s1, $s0, 1	# $s1 = 1111 ...  1111 1111 1111 1111 0100  12
	sra	$s1, $s0, 1	# $s1 = 1111 ...  1111 1111 1111 1111 1101  -3  
o arredondamento da divisão inteira é sempre "para baixo",
ou seja, por exemplo, (-5 div 2) = -3:
	li 	$s0, -6 	# $s0 = 1111 ...  1111 1111 1111 1111 1011  -5
	sra	$s1, $s0, 1	# $s1 = 1111 ...  1111 1111 1111 1111 1101  -3  
 14/10 e 19/10 
 Representação Ponto Flutuante 
 21/10 e 27/10 
 Compilação de Mecanismos C 
 Variáveis Globais 
int i; /* declaração sem inicialização */ 
      .data
      .align 2 # declara que deve ser colocado no próximo end. múltiplo de 4
i:    .space 4 # ocupa 4 bytes
int i = 3; /* declaração sem inicialização */ 
      .data
      .align 2 # declara que deve ser colocado no próximo end. múltiplo de 4
i:    .word 3
i += 1;
      la   $t0,i
      lw   $t1,($t0)
      add  $t1,1
      sw   $t1,($t0)
i é uma facilidade do assembler: no
programa executável não existem nomes, apenas endereços!
float f; /* declaração sem inicialização */ 
      # igual à variável inteira!
      .data
      .align 2 # declara que deve ser colocado no próximo end. múltiplo de 4
f:    .space 4 # ocupa 4 bytes
float f = 3.0; /* declaração sem inicialização */ 
      .data
      .align 2 # declara que deve ser colocado no próximo end. múltiplo de 4
f:    .float 3.0
char c; /* declaração sem inicialização */ 
      .data
c:    .space 1 
char c = 'a'; /* declaração sem inicialização */ 
      .data
c:    .byte 97
c += 1;
      la   $t0,i
      lb   $t1,($t0)
      add  $t1,1
      sb   $t1,($t0)
int a[10];  
      .data
      .align 2
a:    .space 40   # cada inteiro ocupa 4 bytes! 
int a[10] = {0,1,2,3,4,5,6,7,8,9} 
      .data
      .align 2
a:    .word 0,1,2,3,4,5,6,7,8,9
int a[10]; int i;
...
a[i]++;
      # calcula o endereco
      la   $t0,a
      la   $t1,i
      lw   $t1,($t1)
      sll  $t1,$t1,2    # multiplica por 4
      add  $t0,$t0,$t1
      # agora acessa a variavel
      lw   $t1,($t0)
      add  $t1,1
      sw   $t1,($t0)
int i, a[10], b[10]; 
...
a[i] = b[i];
      # calcula os enderecos
      la   $t0,a
      la   $t1,b
      la   $t2,i
      lw   $t2,($t2)
      sll  $t2,$t2,2    # multiplica por 4
      add  $t0,$t0,$t2
      add  $t1,$t1,$t2
      # agora acessa as variaveis
      lw   $t2,($t1)
      add  $t2,1
      sw   $t2,($t0)
char ac[10];  
      .data
ac:   .space 10   
int ac[10] = {'0','1','2','3','4','5','6','7','8','9'} 
      .data
ac:   .byte 48,49,50,51,52,53,54,55,56,57
char ac[10]; int i;
...
a[i]++;
      # calcula o endereco
      la   $t0,a
      la   $t1,i
      lw   $t1,($t1)
      add  $t0,$t0,$t1
      # agora acessa a variavel
      lb   $t1,($t0)
      add  $t1,1
      sb   $t1,($t0)
struct {
  int a;
  float b;
  char c[2];
} s;
      .data
      .align 2
s:    .space 10    # reserva tamanho total
s.c[i]++; /* i foi declarada como inteira */
      
      la   $t0,i
      lw   $t1,($t0)
      la   $t1,s
      add  $t1,$t1,$t0
      lw   $t0,8($t1)
      addi $t0,$t0,1
      sw   $t0,8($t1)
struct {
  char c[2];
  int a;
  float b;
} s;
      .data
      .align 2
s:    .space 12    # reserva tamanho total
 26/10 e 4/11 
 Compilação de Mecanismos C 
 Variáveis Locais e Pilha 
Procedimentos Recursivos
int fact (int n)
{
  if (n < 1)
    return 1;
  else
    return (n * fact (n - 1));
  }
fact:
sub   $sp,$sp,8         # aloca registro de ativação na pilha
sw    $ra, 4($sp)       # valor de $ra deve ser preservado
sw    $a0, 0($sp)       # o valor do argumento (n, em $a0) é usado pelo
# procedimento após o retorno da chamada recursiva
slt   $t0,$a0,1
beq   $t0,$zero,else
addi  $v0,$zero,1       # se n < 1, inicia volta da recursão
j     fim               #   com valor de retorno = 1 (em $v0)
else:
addi  $a0,$a0,-1        # n >= 1 : chamada recursiva com argumento n-1
jal   fact
lw    $a0, 0($sp)       # recupera valor original do argumento (n), salvo na pilha
mul   $v0,$v0,$a0       #  valor de retorno = n * fact (n-1)
fim:
lw    $ra, 4($sp)       # restaura o endereço de retorno em $ra
addu  $sp,$sp,8         # elimina o registro de ativação
jr    $ra
void f() {
  double d[4];
  int i;
  int b[20];
  ...
}
O código gerado pelo compilador para esta função deve reservar espaço, na pilha de ativação, para essas variáveis.
f:
      sub    $sp,$sp,120  # 4 para $ra, 80 para b, 4 para i, 32 para d
                          # ($sp) aponta d, 32($sp) aponta i, 36($sp) aponta b
      sw     $ra,116($sp)
      ...
      ...
      lw     $ra,116($sp)
      add    $sp,$sp,120  # desaloca registro de ativação
O comando C
b[i]++
poderia ser traduzido por: 
      # cálculo do endereço de b[i]
      lw     $t0,32($sp)
      sll    $t0,$t0,2
      move   $t1,$sp
      addi   $t1,$t1,36 #inicio de b
      add    $t1,$t1,$t0 #endereco de b[i]
      # atualizacao
      lw     $t0,($t1)
      addi   $t0,$t0,1
      sw     $t0,($t1)
A pilha também deve sempre ficar alinhada. Isso tem que ser levado em conta na hora de calcular o tamanho do registro de ativação. Declarações como:
void f() {
  int i;
  char a,b;
  float f;
  ...
}
poderiam ser traduzidas para:
f:
      sub    $sp,$sp,16  # 4 para $ra, 4 para i, 1 para a, 1 para b, , 4 para f 
                         # ($sp)->i, 4($sp)->a, 5($sp)->b, 8($sp)->f
      sw     $ra,12($sp)
      ...
      ...
      lw     $ra,12($sp)
      add    $sp,$sp,16  # desaloca registro de ativação
referência: Patterson&Hennessy, 3.6, A.6
referências:
referências:
referências:
open: retorno de um descritor de arquivo
read
write
lseek
close
read, write, etc;
stdio)
fopen: retorno de um FILE*
fgetc, fputc
fgets, fputs
fread, fwrite
fflush
fseek
fclose