Exemplo 3 - Outra forma de trabalhar com a memória

 

Existe uma outra maneira de se carregar dados em memória sem a utilização de instruções store. Ela é muito útil principalmente para a definição de valores ditos constantes (na verdade eles podem ser alterados futuramente já que temos acesso a sua posição de memória) e em inicialização de dados.

Para se armazenar os dados em memória, basta colocar a diretiva .data, que indica ao SPIM que os dados que a seguem devem ser carregados no segmento de dados.

.data # indica ao SPIM que as próximas linhas são dados
const1: .byte 1 # const1 declarado como byte com valor 1
const2: .word 4 # const2 declarado como word com valor 4
array1: .byte 9, 21, 16, 18, 38 # array1 declarado com 5 elementos (bytes)
tam1 : .byte 5 # tam1 (tamanho do array1 em bytes)
array2: .word 206, 1543, 348, 709, 7000, 994 # array2 declarado com 6 elementos (words)
tam2 : .byte 24 # tam2 (tamanho do array2 em bytes)

Realizaremos algumas operações simples com estes dados para ver como é feito o acesso a eles.

.text
.globl main
main:
lb $s0,const1 # $s0 recebe o valor de const1 (1)
lw $s1,const2 # $s1 recebe o valor de const2 (4)

Observe que o label do dado declarado funciona como um ponteiro de memória. Assim, para acessá-lo basta fazer a instrução de load, passando-o como parâmetro. Com os dois arrays vamos fazer algo para acessar todos os seus elementos, como por exemplo somar todos colocando o resultado em um registrador.

add $s2,$zero,$zero # Zera o registrador $s2
add $t0,$zero,$zero # Zera o registrador $t0
lb $t1,tam1 # $t1 recebe o tamanho do array1 (5bytes)
soma1:
lb $t2,array1($t0) # $t2 recebe o valor da posicao apontada por array1 + $t0
add $s2,$s2,$t2 # $s2 é somado com o valor carregado do array1
add $t0,$t0,$s0 # $t0 irá apontar para a próxima posição (atual +1)
bne $t0,$t1,soma1 # Repete até chegar ao final do array1

Para o segundo array basta trocar a instrução de carga de load byte para load word:

add $s3,$zero,$zero # Zera o registrador $s3
add $t0,$zero,$zero # Zera o registrador $t0
lb $t1,tam2 # $t1 recebe o tamanho do array2 (24bytes)
soma2:
lw $t2,array2($t0) # $t2 recebe o valor da posicao apontada por array2 + $t0
add $s3,$s3,$t2 # $s3 é somado com o valor carregado do array2
add $t0,$t0,$s1 # $t0 irá apontar para a próxima posição do array (atual +4)
bne $t0,$t1,soma2 # Repete até chegar ao final do array2

Salve o arquivo com o nome de "exercicio3.s". Abra a Janela do segmento de dados. Observe que os dados definidos já estão carregados em memória. É interessante observar como esses dados são armazenados:

A primeira posição armazenada (10011000h) foi ocupada apenas por um byte (valor 1). O próximo valor era uma word (de valor 4). Em seguida vem o primeiro array (5 elementos - bytes). Veja que o SPIM opera a memória em little-endian, ou seja, a posição de memória vai sendo ocupada de trás para frente. O último valor do array1 é armazenado em outra posição de memória e após ele há um byte contendo o tamanho do array. Segue-se com o array2, mas desta vez utilizando toda a posição de memória (word) para cada elemento.

Execute o código. Na tela de registradores você vai ver os valores carregados em $s0, $s1, $s2, e $s3 - 1, 4, 88 (soma dos valores do array1) e 10800 (soma dos valores do array2).

Observação: Os dados carregados em memória através da diretiva .data são armazenados a partir da posição 10011000h. Tome cuidado quando estiver usando este tipo de carga em memória para não alcançar esta posição pelo programa, ou seja, não use valores maiores que $gp + 36864. Se necessitar de mais memória verifique até que posição os dados estáticos foram carregados e utilize um valor maior que esse.

Observação: Existem outros tipos além de .word e .byte. Entre eles podemos citar o tipo .double para armazenar valores de ponto flutuante com precisão dupla, o .float para precisão simples, o .half para armazenar meia-palavra (16bits) e outro que vamos fazer muito uso principalmente quando chegarmos à escrita de dados no console do SPIM: o .asciiz que armazena strings.

Curiosidade: O processador MIPS pode operar tanto em big-endian como em little-endian. O SPIM por sua vez, utiliza o padrão da máquina que o simulador está sendo executado. Assim, o SPIM é little-endian no Intel 80x86 e big-endian no Macintosh.