10. Estrutura dos programas¶
Para assegurar a adequada versatilidade na utilização do espaço de endereçamento, de acordo com as caraterísticas dos dispositivos de memória que o populam e as necessidades da aplicação, um programa é organizado por zonas de memória.
- Destacam-se três zonas:
zona para código binário das instruções;
zona para variáveis;
e zona para stack.
Estas zonas de memória são designadas na literatura inglesa por segment ou section. Aqui, vais ser utilizado o termo “secção”.
- Para concretização deste modelo, definem-se três secções:
.text – para o código binário das instruções e dados constantes;
.data – para as variáveis que são modificadas durante a execução do programa;
.stack – para suporte da execução estruturada com base em rotinas (funções).
Figura 10.1 Organização do programa em três secções¶
10.1. .data¶
As variáveis são alojadas na secção .data.
/*
uint8_t m = 20, n = 3;
|
m:
.byte 20
n:
.byte 3
p:
.word 0
q:
|
10.2. .text¶
O código binário das instruções do programa, assim com dados auxiliares ao código, são alojados na secção .text.
int main() {
q = multiply(4, 7);
p = multiply(m, n);
}
|
main:
push lr
mov r0, #4
mov r1, #7
bl multiply
ldr r1, q_addr
str r0, [r1]
ldr r0, m_addr
ldrb r0, [r0]
ldr r1, n_addr
ldrb r1, [r1]
bl multiply
ldr r1, p_addr
str r0, [r1]
pop pc
m_addr:
.word m
n_addr:
.word n
p_addr:
.word p
q_addr:
|
10.3. .stack¶
A secção .stack é uma zona de memória para salvaguarda de dados temporários, necessários à execução do programa. O conteúdo inicial desta zona de memória é indiferente.
A diretiva .space na linha 34 da Listagem 10.1
reserva a área de memória cuja dimensão pode ser ajustada através do
símbolo STACK_MAX_SIZE definido na linha anterior.
10.4. Ambiente de execução¶
Os programas aqui apresentados seguem o modelo de execução da linguagem C. A norma define dois modelos de execução: Hosted environment e Freestanding environment. O primeiro para sistemas com sistema operativo e o segundo para outros casos.
No caso da programação para o P16 é usado o modelo Freestanding environment.
Em ambos os modelos, a execução do programa começa com a chamada de uma função principal e termina com o retorno dessa função. Para chamar e receber o retorno da função principal, é criado um código de preparação que além destas ações, realiza as necessárias inicializações.
No modelo Hosted a função principal deve ter uma das seguintes assinaturas:
int main(void);
int main(int argc, char *argv[]);
No modelo Freestanding o nome e tipo da função principal não é definido pela norma, ficando ao critério do implementador. Nestes exemplos de programas vai ser usada:
void main(void);
Após a ação reset, o P16 passa a executar código a partir do endereço 0x0000. Neste endereço inicia-se o código de preparação – linha 6.
A preparação consiste em inicializar o registo SP
com o endereço de topo da secção .stack –
linhas 10, 14 e 15 da Listagem 10.1.
Segue-se a chamada da função main na linha 11.
Após o retorno do programa, a única ação a tomar é garantir que o processamento não se descontrola.
Para isso é executada a instrução b .,
que significa saltar para o endereço atual o que resulta na execução repetida desta instrução
– linha 12.
1;-------------------------------------------------------------------------------
2; Secção .text são alojadas as instruções
3
4 .text
5
6 b start
7 b .
8
9start:
10 ldr sp, stack_top_addr
11 bl main
12 b .
13
14stack_top_addr:
15 .word stack_top
16
17main:
18
19 ; Código da função main
20 ; e das restantes funções do programa
21
22;-------------------------------------------------------------------------------
23; Secção .data onde são alojadas as variáveis
24
25 .data
26
27;-------------------------------------------------------------------------------
28; Secção .stack onde é reservada memória para Stack
29
30 .stack
31
32 .equ STACK_MAX_SIZE, 1024
33 .space STACK_MAX_SIZE * 2
34stack_top:
10.5. Localização¶
A localização e a dimensão das secções no espaço de endereçamento designa-se por mapa de memória. As secções são localizadas pela ordem com que são escritas no ficheiro fonte do programa. O endereço da secção seguinte é o endereço par imediatamente disponível depois do fim da secção anterior. A dimensão de cada secção depende do número de instruções e do número de variáveis e respetivos tipos, definidos no seu interior.
O código completo do programa utilizado como exemplo nesta secção pode ser descarregado daqui:
multiply.s.
O comando
$ p16as multiply.s
gera os ficheiros multiply.hex e multiply.lst.
O mapa de memória do programa, que faz parte do ficheiro
multiply.lst,
pode ser visualizado nas linhas 5 a 7 da
multiplylst.
A secção .text começa no endereço 0x0000 por ser a que aparece em primeiro lugar no ficheiro fonte do programa.
O endereço da secção .data é posterior à secção .text porque aparece em segundo lugar. O seu endereço inicial é 0x0044 porque é essa a dimensão da secção anterior.
As instruções b program e b . que aparem no início da secção .text
são localizadas nos endereços 0x0000 e 0x0002, respetivamente.
Estes endereços são fixados pela arquitetura P16, como os pontos de entrada em execução
dos acontecimentos reset e interrupção, respetivamente.
Razão pela qual o código de preparação não é localizado exatamente no endereço 0x0000.
A instrução b . no endereço 0x0002 é apenas figurativa.
Este programa não está capacitado para realizar processamento de interrupções.
10.5.1. Exercícios¶
Este conjunto de exercícios destina-se a exercitar a programação, em linguagem assembly do P16.
O objetivo de cada exercício é traduzir para assembly, o código de todo o programa definido em linguagem C, incluindo a definição das variáveis.
int x = 2; int y = 3; int z; void main(void) { z = x + y; }
uint8_t m = 20, n = 3; uint16_t p, q; int main() { p = multiply(m, n); q = multiply(4, 7); }
uint16_t q, r, d = 200, e = 10; int main() { q = divide(d, e); r = divide(43, 7); }
uint16_t a = 1000; uint8_t log2a; uint8_t log10a; int main() { log2a = logarithm(a, 10); log10a = logarithm(a, 2); } uint8_t logarithm(uint16_t value, uint8_t base) { uint8_t log = 0; while (value >= base) { ++log; value = divide( value, base ); } return log; }
uint8_t exp = 3, base = 4 uint16_t power1, power2; int main() { power1 = power(base, exp); power2 = power(2, 7); } uint16_t power(uint8_t b, uint8_t e) { uint16_t result = 1; while (e > 0) { if (result > 0xff) return UINT16_MAX; result = multiply(result, b); e--; } return result; }
uint16_t a = 333; uint8_t prime1; uint8_t prime2; int main() { prime1 = is_prime(a); prime2 = is_prime(77); } bool is_prime(uint16_t n) { for (uint16_t_t i = 2; i < n; ++i) if (n % i == 0) return false; return true; }