Estrutura dos programas ======================= .. _estrutura de programa: 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). .. figure:: figures/section_map_min.png :name: section_map_min :align: center :scale: 12% Organização do programa em três secções .data ..... As variáveis são alojadas na secção **.data**. +----------------------------------------------------------+--------------------------------------------------------+ | .. literalinclude:: code/multiply_add_add/multiply.s | .. literalinclude:: code/multiply_add_add/multiply.s | | :language: c | :language: asm | | :lines: 92-94 | :lines: 90,97-104 | +----------------------------------------------------------+--------------------------------------------------------+ .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**. +----------------------------------------------------------+--------------------------------------------------------+ | .. literalinclude:: code/multiply_add_add/multiply.s | .. literalinclude:: code/multiply_add_add/multiply.s | | :language: c | :language: asm | | :lines: 23-26 | :lines: 29,33-58 | +----------------------------------------------------------+--------------------------------------------------------+ .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 :numref:`codigo_padrao` reserva a área de memória cuja dimensão pode ser ajustada através do símbolo ``STACK_MAX_SIZE`` definido na linha anterior. 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 :numref:`codigo_padrao`. 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. .. literalinclude:: code/multiply_add_add/multiply.s :language: c :caption: Estrutura de código padrão :emphasize-lines: 6-15 :linenos: :name: codigo_padrao :lines: 1-16, 29-33, 88-91, 106-114 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: :download:`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 :download:`multiply.lst`, pode ser visualizado nas linhas 5 a 7 da :numref:`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. .. literalinclude:: code/multiply_add_add/multiply.lst :language: c :caption: Mapa de memória :linenos: :name: multiplylst :lines: 1-8 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. 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. #. .. code-block:: c int x = 2; int y = 3; int z; void main(void) { z = x + y; } #. .. code-block:: c uint8_t m = 20, n = 3; uint16_t p, q; int main() { p = multiply(m, n); q = multiply(4, 7); } #. .. code-block:: c uint16_t q, r, d = 200, e = 10; int main() { q = divide(d, e); r = divide(43, 7); } #. .. code-block:: c 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; } #. .. code-block:: c 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; } #. .. code-block:: c 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; }