3. Utilização¶
3.1. Invocação¶
O p16as é invocado na janela de comando segundo a seguinte sintaxe:
p16as [options] <source filename> options: --verbose -h, --help -v, --version -i, --input <source filename> -o, --output <base filename> -s, --section <section name>=<address> -f, --format hexintel | binary | logisim8 | logisim16 -a, --addresses <from>-<to> -l, --interleave
Os erros e avisos são assinalados na janela de comando utilizada na invocação.
O ficheiro com o texto do programa em linguagem assembly – ficheiro fonte – assinalado como <source filename>, tem normalmente a extensão “s”.
A opção --output permite definir o nome base [2] dos ficheiros de saída.
Se esta opção for omitida, os ficheiros produzidos terão
o mesmo nome base do ficheiro fonte
e serão localizados na mesma diretoria.
A opção --section permite definir o endereço de localização das secções.
Em caso de omissão desta opção as secções são localizadas a partir do endereço 0x0000,
pela ordem com que estão escritas no ficheiro fonte.
A opção --format permite definir o formato do ficheiro de saída com o código binário do programa.
A omissão desta opção é equivalente a --format hexintel.
Formatos possíveis para o ficheiro com o código binário:
--format hexintel: ficheiro binário em formato Intel HEX;--format binary: o conteúdo do ficheiro binário é uma imagem exata da memória;--format logisim8: ficheiro binário em formato Logisim, organizado em palavras de 8 bits;--format logisim16: ficheiro binário em formato Logisim, organizado em palavras de 16 bits.
A opção --addresses permite definir a gama de endereços cujo conteúdo é transcrito para o ficheiro binário de saída.
O conteúdo respeitante a endereços fora do intervalo especificado é excluído do ficheiro binário de saída.
A opção --interleave faz com que o código binário do programa seja repartido por dois ficheiros
– um com os bytes respeitantes aos endereços pares
e outro com os bytes respeitantes aos endereços ímpares.
Esta opção é ignorada se também for mencionada a opção --format logisim16.
3.2. Localização das secções¶
A definição da localização em memória de cada secção pode ser explícita ou implícita.
A localização explícita é definida através da opção --section na invocação do p16as.
A localização implícita aplica-se às secções omissas na localização explicita, localizando-as a seguir ao último endereço ocupado pela secção anterior, pela ordem em que estão escritas no ficheiro fonte do programa.
No caso de não ser explicitada a localização da primeira secção definida no programa, esta é localizada no endereço 0x0000.
No caso da secção estar fragmentada, e aplicando-se a localização implícita, a sua localização é definida pela posição do primeiro fragmento.
O início de uma secção é localizado num endereço par.
3.3. Formatos de saída¶
O código binário do programa é guardado em ficheiro num de três formatos: formato Intel HEX [1], formato binário e formato do simulador Logisim.
O conteúdo das secções .stack e .bss não é transposto para os ficheiros de saída.
3.3.1. Formato binário¶
No formato binário o conteúdo do ficheiro produzido é a imagem exata do conteúdo da memória do sistema. O primeiro byte do ficheiro corresponde ao endereço da primeira secção. Ao que se segue o restante conteúdo da primeira secção e das secções seguintes. Se existirem intervalos de espaço de endereçamento entre secções, serão preenchidos com o valor zero.
3.3.2. Formato Logisim¶
O simulador Logisim simula dispositivos de memória RAM ou ROM, cujo conteúdo pode ser carregado a partir de ficheiro.
Na utilização do Logisim na simulação de sistemas baseados no P16 é necessário carregar nesses dispositivos o código binário dos programas, produzido pelo p16as.
No Logisim os dispositivos de memória podem ter palavras com qualquer número de bits. Em ficheiro, o conteúdo da memória é guardado em formato de texto, como uma sequência de valores numéricos representados em base hexadecimal, em que cada valor corresponde a uma posição de memória.
As ocorrências de sucessivos valores iguais são representadas pela sequência N*X. Sendo N o número de vezes que o valor ocorre e X o valor em si.
O p16as gera ficheiros binários em formato Logisim para memórias com palavras de 8 ou 16 bits.
3.4. Exemplo de utilização¶
Considere-se o programa da Listagem 3.1 como conteúdo do ficheiro multiply.s.
Código fonte: multiply.s
1 .section .startup
2 b _start
3 b .
4_start:
5 ldr sp, addressof_stack_top
6 mov r0, pc
7 add lr, r0, #4
8 ldr pc, addressof_main
9 b .
10
11addressof_stack_top:
12 .word stack_top
13addressof_main:
14 .word main
15
16 .text
17 .data
18
19 .stack
20stack:
21 .space 1024
22stack_top:
23
24/*---------------------------------------------------
25uint8_t m = 20, n = 3;
26*/
27 .data
28m:
29 .byte 20
30n:
31 .byte 3
32
33/*---------------------------------------------------
34uint16_t p, q;
35*/
36
37p:
38 .word 0
39q:
40 .word 0
41
42/*---------------------------------------------------
43int main() {
44 p = multiply(m, n);
45 q = multiply(4, 7);
46}
47*/
48 .text
49main:
50 push lr
51 ldr r0, addressof_m
52 ldrb r0, [r0]
53 ldr r1, addressof_n
54 ldrb r1, [r1]
55 bl multiply
56 ldr r1, addressof_p
57 str r0, [r1]
58 mov r0, #4
59 mov r1, #7
60 bl multiply
61 ldr r1, addressof_q
62 str r0, [r1]
63 pop pc
64
65addressof_m:
66 .word m
67addressof_n:
68 .word n
69addressof_p:
70 .word p
71addressof_q:
72 .word q
73
74/*---------------------------------------------------
75<r0> uint16_t multiply(<r0> uint8_t multiplicand, <r1> uint8_t multiplier) {
76 <r2> uint16_t product = 0;
77 while (multiplier > 0) {
78 product += multiplicand;
79 multiplier--;
80 }
81 <r0> return product;
82}
83*/
84multiply:
85 mov r2, #0
86 add r1, r1, #0
87 bzs while_end
88while:
89 add r2, r2, r0
90 sub r1, r1, #1
91 bzc while
92while_end:
93 mov r0, r2
94 mov pc, lr
Invocação
No comando
p16as multiply.s -s .data=0x4000 -s .text=0x1000
a primeira ocorrência da opção -s define o endereços da secção .data em 0x4000
e a segunda ocorrência define o endereço da secção .text em 0x1000.
A omissão de outras opções define que:
os ficheiros de saída terão os mesmos nomes base – multiply.*;
o ficheiro com o código binário terá o formato HEX Intel;
o conteúdo binário não é filtrado por endereço;
não serão gerados dois ficheiros binários com os dados intercalados.
Mensagens de erro e de aviso
Foram introduzidas modificações no ficheiro fonte para exemplificar e emissão de mensagens.
O primeiro caso é um erro de sintaxe – a definição duma mnemónica de instrução inexistente (ld).
multiply.s (51): ld r0, addressof_m
---------------- ^^
ERROR! syntax error
O segundo caso é um erro de domínio – é escrito o número 17 na posição de uma constante cujo domínio vai de 0 a 15.
multiply.s (90): sub r1, r1, 17
---------------- ^^
WARNING! Expression's value = 17 (0x11) not encodable in 4 bit, truncate to 1 (0x1)
Se forem assinaladas apenas mensagens de aviso o processamento prossegue com a
geração dos ficheiros de saída – multiply.lst e multiply.hex.
Note-se que faz parte das boas práticas de programação corrigir o programa
até suprimir a emissão de mensagens de aviso.
Organização
Por uma questão de organização, é conveniente criar especificamente uma diretoria para alojar os
ficheiros relacionados com um dado programa. No exemplo seguinte a diretoria multiply aloja
todos os ficheiros relacionados com este programa: multiply.s, multiply.lst e multiply.hex.
disciplinas
|-- pe
|-- ss
|-- ac
|-- documents
|-- p16_code
|-- divide
|-- multiply
|-- multiply.s
|-- multiply.lst
|-- multiply.hex
Ficheiro lst
O ficheiro de extensão lst contém a tabela de secções,
a tabela de símbolos e a listagem das instruções.
Na tabela de secções listam-se as secções existentes, as gamas de endereços que ocupam e as
respetivas dimensões. A secção .startup é localizada no endereço 0x0000, por localização implícita,
porque está definida em primeiro lugar no ficheiro fonte. As secções .text e .data são localizadas,
respetivamente, nos endereços 0x1000 e 0x4000 por localização explícita.
A secção .stack é localizada no endereço 0x4006, por localização implícita,
porque está definida a seguir a .data que tem a dimensão 6.
Na tabela de símbolos listam-se os símbolos definidos através de label ou através da diretiva .equ.
Por cada símbolo é dada a seguinte informação: identificador, tipo, valor associado e secção a que pertence.
Na listagem das instruções, são apresentados do lado esquerdo, na primeira coluna o número da linha do ficheiro fonte, na segunda coluna os endereços da memória e na terceira e quarta colunas o respetivo conteúdo.
Na arquitetura do P16 as palavras formadas por dois bytes – designadas por word – ocupam duas posições de memória consecutivas, o byte de menor peso toma a posição de endereço menor e o byte de maior peso, a posição de endereço maior – little ended format.
O conteúdo da memória – código das instruções ou valor das variáveis – é escrito na terceira e quarta colunas
como uma sequência de bytes pela ordem dos endereços que ocupam na memória. Por exemplo, na
linha 7, o código máquina da instrução add lr, r0, 4, que ocupa os endereços 0008 e 0009,
e tem o valor 0xA20E, é representado pela sequência de bytes 0E A2. Por exemplo, na linha 29,
a variável m, do tipo .byte, ocupa o endereço 0x0046 e o seu valor é 20 (0x14).
P16 assembler v1.4.99 (Jan 10 2024) multiply.lst Wed Jan 10 14:22:21 2024
Sections
Index Name Address Size
0 .startup 0000 0012 18
1 .text 1000 0034 52
2 .data 4000 0006 6
3 .stack 4006 0400 1024
Symbols
Name Type Value Section
_start LABEL 0004 4 .startup
addressof_m LABEL 101C 4124 .text
addressof_main LABEL 0010 16 .startup
addressof_n LABEL 101E 4126 .text
addressof_p LABEL 1020 4128 .text
addressof_q LABEL 1022 4130 .text
addressof_stack_top LABEL 000E 14 .startup
line#3 LABEL 0002 2 .startup
line#9 LABEL 000C 12 .startup
m LABEL 4000 16384 .data
main LABEL 1000 4096 .text
multiply LABEL 1024 4132 .text
n LABEL 4001 16385 .data
p LABEL 4002 16386 .data
q LABEL 4004 16388 .data
stack LABEL 4006 16390 .stack
stack_top LABEL 4406 17414 .stack
while LABEL 102A 4138 .text
while_end LABEL 1030 4144 .text
Code listing
1 .section .startup
2 0000 01 58 b _start
3 0002 FF 5B b .
4 _start:
5 0004 4D 0C ldr sp, addressof_stack_top
6 0006 80 B7 mov r0, pc
7 0008 0E A2 add lr, r0, #4
8 000A 2F 0C ldr pc, addressof_main
9 000C FF 5B b .
10
11 addressof_stack_top:
12 000E 06 44 .word stack_top
13 addressof_main:
14 0010 00 10 .word main
15
16 .text
17 .data
18
19 .stack
20 stack:
21 4006 00 .space 1024
21 .... ..
21 4405 00
22 stack_top:
23
24 /*---------------------------------------------------
25 uint8_t m = 20, n = 3;
26 */
27 .data
28 m:
29 4000 14 .byte 20
30 n:
31 4001 03 .byte 3
32
33 /*---------------------------------------------------
34 uint16_t p, q;
35 */
36
37 p:
38 4002 00 00 .word 0
39 q:
40 4004 00 00 .word 0
41
42 /*---------------------------------------------------
43 int main() {
44 p = multiply(m, n);
45 q = multiply(4, 7);
46 }
47 */
48 .text
49 main:
50 1000 0E 24 push lr
51 1002 C0 0C ldr r0, addressof_m
52 1004 00 08 ldrb r0, [r0]
53 1006 B1 0C ldr r1, addressof_n
54 1008 11 08 ldrb r1, [r1]
55 100A 0C 5C bl multiply
56 100C 91 0C ldr r1, addressof_p
57 100E 10 20 str r0, [r1]
58 1010 40 60 mov r0, #4
59 1012 71 60 mov r1, #7
60 1014 07 5C bl multiply
61 1016 51 0C ldr r1, addressof_q
62 1018 10 20 str r0, [r1]
63 101A 0F 04 pop pc
64
65 addressof_m:
66 101C 00 40 .word m
67 addressof_n:
68 101E 01 40 .word n
69 addressof_p:
70 1020 02 40 .word p
71 addressof_q:
72 1022 04 40 .word q
73
74 /*---------------------------------------------------
75 <r0> uint16_t multiply(<r0> uint8_t multiplicand, <r1> uint8_t multiplier) {
76 <r2> uint16_t product = 0;
77 while (multiplier > 0) {
78 product += multiplicand;
79 multiplier--;
80 }
81 <r0> return product;
82 }
83 */
84 multiply:
85 1024 02 60 mov r2, #0
86 1026 11 A0 add r1, r1, #0
87 1028 03 40 bzs while_end
88 while:
89 102A 22 80 add r2, r2, r0
90 102C 91 A8 sub r1, r1, #1
91 102E FD 47 bzc while
92 while_end:
93 1030 00 B1 mov r0, r2
94 1032 0F B7 mov pc, lr
94
Ficheiro hex
O ficheiro de extensão hex contém a informação binária do programa em formato Intel HEX.
A informação é composta pelo código binário das instruções ou os valores iniciais das variáveis
e a indicação dos endereços de memória onde serão carregados.
:100000000158FF5B4D0C80B70EA22F0CFF5B06441E
:020010000010DE
:101000000E24C00C0008B10C11080C5C910C1020CF
:1010100040607160075C510C10200F0400400140DB
:1010200002400440026011A00340228091A8FD47C5
:0410300000B10FB745
:06400000140300000000A3
:00000001FF
O seu conteúdo é composto por tramas, formadas por uma marca inicial, a dimensão dos dados, o endereço onde os dados serão carregados, o tipo da trama, os dados contidos na trama e um código para deteção de eventual corrupção dos dados – soma de controlo.
Figura 3.1 Formato de uma trama Intel Hex.¶
A soma de controlo é calculada de modo que a adição, em módulo 0x100, de todos os bytes que formam a trama, some zero.
A trama :00 0000 01 FF tem dimensão zero, invoca virtualmente o endereço zero
e é do tipo 01 – end of file, o que conjuntamente suscita a soma de controlo FF.
Serve para terminar o ficheiro.
Footnotes