Programação ao nível da máquina Instruções de controlo Variáveis Vectores Instruções de controlo Saltos (jumps): alteram o valor do EIP - Instruction Pointer (ou Program Counter) Incondicionais Exemplo: jmp endereço eip endereço a próxima instrução executada é a que se encontra no endereço indicado Condicionais Primeiro testam códigos de condição (flags). Exemplos: jz endereço só salta se a flag de Zero estiver a 1 jnz endereço só salta se a flag de Zero estiver a 0 Outras instruções testam outras flags ou conjuntos de flags. Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 167 Saltos condicionais (com uma flag) Os que testam directamente uma flag Salta se: Carry Overflow Signal Zero CF=1 CF=0 OF=1 OF=0 SF=1 SF=0 ZF=1 ZF=0 JC – Jump if Carry JNC – Jump if Not Carry JO – Jump if Overflow JNO – Jump if Not Overflow JS – Jump if Sign JNS – Jump if Not Sign JZ – Jump if Zero JNZ – Jump if Not Zero Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 168 Saltos condicionais (inteiros s/sinal) As relações entre inteiros sem sinal, são dadas, após cmp (ou sub) pelas flags: após sub X,Y ou cmp X,Y se X<Y CF=1 (borrow) se X=Y ZF=1 se X>Y CF=0 e ZF=0 Saltos condicionados por estas flags: JB – Jump if Below (=JC) JE – Jump if Equal (=JZ) JA – Jump if Above (CF=0 e ZF=0) JBE – Jump if Below or Equal (CF=1 ou ZF=1) JAE – Jump if Above or Equal (=JNC) Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 169 Saltos condicionais (aliases) Mais mnemónicas para as mesmas instruções: JNBE = JA JNB = JAE = JNC JNA = JBE JNAE = JB = JC JNE = JNZ Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 170 Comparar números com sinal Sem sinal 1111 1111 = 25510 0000 0000 = 010 Com sinal 1111 1111 = -110 0000 0000 = 010 Qual é maior? 255 > 0 Qual é maior? -1 < 0 Flag a testar? Carry (CF), usando sub/cmp Flag a testar? necessitamos de testar flags diferentes Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 171 Exemplos de comparações Usando cmp/sub para X>Y: X Y X-Y OF SF 0111 (7) 0001 (1) 0110 (6) 0 0 0001 (1) 1110 (-2) 0011 (3) 0 0 0001 (1) 1001 (-7) 1000 (-8?) 1 1 1111 (-1) 1001 (-7) 0110 (6) 0 0 Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 172 Exemplos de comparações Usando cmp/sub para X<Y: X Y X-Y OF SF 0001 (1) 0111 (7) 1010 (-6) 0 1 1000 (-8) 0001 (1) 0111 (3?) 1 0 1001 (-7) 0001 (1) 1000 (-8) 0 1 1001 (-7) 1111 (-1) 1010 (-6) 0 1 Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 173 Conclusão Comparação usando cmp/sub entre números com sinal: Quando X-Y>=0 (SF=0) então X>=Y, excepto se houver Overflow (OF=1) Após sub X,Y ou cmp X,Y Se X > Y Greater than: Se X >= Y Greater or Equal: Se X < Y Less than: Se X <= Y Less or Equal: SF = OF and ZF = 0 SF = OF SF ≠ OF and ZF = 0 SF ≠ OF or ZF = 1 Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 174 Saltos condicionais (inteiros c/sinal) Saltos condicionais para as relações entre inteiros com sinal: JG – Jump if greater than (=JNLE) JGE – Jump if greater or equal (=JNL) JL – Jump if less than (=JNGE) JLE – Jump if less or equal (=JNG) Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 175 Comparando números (sub/cmp) Sem sinal 1111 1111 = 25510 0000 0000 = 010 Com sinal 1111 1111 = -110 0000 0000 = 010 Qual é maior? 255 > 0 Qual é maior? -1 < 0 Necessitamos de instruções que comparem flags diferentes. Exemplos: Flag a testar: Carry (CF) ja /jb / etc Flags a testar: Sign (SF) e Overflow (OF) jg / jl / etc Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 176 Etiquetas (labels) Em vez de usar endereços, no assembly etiqueta-se as posições de memória Exemplo: ciclo: add eax, ebx jmp ciclo ciclo representa o endereço de memória onde fica a instrução ‘add eax, ebx’ Usa-te também para etiquetar posições de memória que contém dados: variáveis Exemplo, carregar para eax o conteúdo de Var mov eax, [Var] Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 177 Exemplo: if Como executar código diferente dependendo de uma condição? condição estado das flags salto condicional (jxx) if (condicao) codigo_do_then; else codigo_do_else; ; código da condição de modo ; a que altere as flags ; (exemplo cmp eax, 5) jxx bloco_else ; codigo_do_then jmp fim_if bloco_else: ; codigo_do_else fim_if: Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 178 Exemplo: if Exemplo if (x < 5) codigo_do_then; else codigo_do_else; mov eax, [x] cmp eax, 5 jge bloco_else ; codigo_do_then (eax < 5) jmp fim_if bloco_else: ; codigo_do_else (eax ≥ 5) fim_if: Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 179 Exemplo: while Ciclo com teste "à cabeça" while (condicao) codigo_do_ciclo; inicio_while: ; código da condição de modo ; a que altere as flags ; (exemplo cmp ax, 5) jxx fim_while ; codigo_do_ciclo jmp inicio_while fim_while: Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 180 Exemplo: while Exemplo while (x < 5) codigo_do_ciclo; inicio_while: mov eax, [x] cmp eax, 5 jge fim_while ; codigo_do_ciclo jmp inicio_while fim_while: Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 181 Exemplo: do-while Ciclo com teste "no fim" do codigo_do_ciclo; while (condicao); inicio_do: ; codigo_do_ciclo ; código da condição de modo ; a que altere as flags ; (exemplo cmp ax, 5) jxx inicio_do Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 182 Exemplo: do-while Exemplo do codigo_do_ciclo; while (x < 5); inicio_do: ; codigo_do_ciclo mov eax, [x] cmp eax, 5 jl inicio_do Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 183 Exemplo: for Ciclo com base num contador for ( i=n; i>0; i-- ) codigo_do_ciclo; mov ecx, [n] inicio_for: cmp ecx, 0 jle fim_for ; codigo_do_ciclo dec ecx jmp inicio_for fim_for: Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 184 LOOP Salto com base num contador, para ciclos que executam pelo menos uma vez Equivale a ‘dec cx’ seguido de ‘jnz’ Usa implicitamente o registo CX (16bits) for ( i=n; i>0; i-- ) codigo_do_ciclo; mov cx, [n] inicio_for: ; codigo_do_ciclo loop inicio_for Nota: tem de n > 0! Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 185 Declarar variáveis Declarar dados inicializados DB – 8 bits DW – 16 bits DD – 32 bits DQ – constante virgula flutuante em 64 bits DT – constante virgula flutuante em 80 bits Exemplo: Var: dd 20 ; um inteiro a 32 bits com o valor 20 Reservar espaço para dados não inicializados RESB, RESW, RESD, RESQ e REST Exemplo : Var: resd 1 ; espaço para 1 inteiro a 32 bits Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 186 Declarar vectores Um vector é um conjunto contíguo de dados do mesmo tipo Exemplos: vector1: dw 1,2,3,4,5 vector2: resb 20 ; um vector de 5 elementos de ; 16 bits ; reservar um vector de 20 bytes Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 187 Exemplo: somar elementos de um vector Dados: vector: dd 1, 2, 3, 4, 5, 6, 7 len: dw 7 Código da soma l: mov eax, 0 mov ebx, vector mov cx, [len] add eax, [ebx] add ebx, 4 loop l … Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 188 Endereço de um elemento de um vector Consideremos o seguinte vector: vector: dd 1, 2, 3, 4, 5, 6, 7 Calcular o endereço do valor 7, considerando que ecx contém o numero de elementos do vector mov mov sub mul add ebx, vector eax, ecx eax, 1 dword 4 ebx, eax O IA-32 tem modos de endereçamento que facilitam um pouco a programação Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 189 Modos de endereçamento no IA-32 Forma geral para endereçamento indirecto no IA-32: [Rb+Ri*S+D] D: Constante “deslocamento” Rb: Registo Base: Qualquer dos 8 registos inteiros Ri: Registo índice: Qualquer, excepto esp Também é pouco provável que seja ebp S: Escala Casos especiais [D] [Rb] [Rb+Ri] [Rb+Ri+D] [Rb+Ri*S] Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 190 Endereçamento baseado O endereço efectivo é obtido somando o conteúdo de um registo (registo base ou índice) a uma constante exemplo: mov eax, [ebx+8] load-baseado eax ebx Registo especificado na instrução (ebx) Memória 8 + Endereço efectivo Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 191 Endereçamento baseado Conveniente para endereçar estruturas struct my_struct { // estrutura em C int a; // ocupa 4 bytes double b; // ocupa 8 bytes char c; // ocupa 1 byte } s1; +13 c, ocupa 1 byte, Distância do início:12 +12 b, ocupa 8 bytes distância do início: 4 Se registo base com endereço de s1: [RegBase + 0] : campo a da estrutura [RegBase + 4] : campo b da estrutura [RegBase + 12]: campo c da estrutura +4 a, ocupa 4 bytes distância do início:0 Endereço Inicial da variável s1 Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 192 Endereçamento indexado O endereço efectivo é obtido somando o conteúdo de dois registos (registos base e índice). Exemplo: mov [ebx+ecx], 5 store-im-ind ebx ecx 5 Memória Registo 1 (ebx) + Registo 2 (ecx) Endereço efectivo Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 193 Endereçamento indexado com escala O endereço efectivo é obtido somando o conteúdo de dois registos (registos base e índice). Exemplo: mov [ebx+ecx*4], 5 store-im-scl-ind ebx ecx 4 5 Memória Registo 2 (ecx) x + Endereço efectivo Registo 1 (ebx) Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 194 Endereçamento indexado com escala Conveniente para endereçar vectores int arr[100]; … Sabendo o endereço de arr: Um registo pode funcionar como índice [arr+ regInd] onde regInd toma os valores 0, 1, 2 … Exemplo: percorrer o vector: mov ebx, 0 Ciclo: mov eax, [arr+ebx*4] [arr+ebx] inc addebx ebx, 4 cmp ebx, 100 400 jne Ciclo arr[3], +12 distância do início:12 arr[2], +8 distância do início:8 +4 arr[1], distância do início:4 arr[0], ocupa 4bytes distância do início:0 Endereço Inicial do vector arr Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 195 Aceder a vectores: os registos ESI e EDI Registos de uso geral, normalmente utilizados para percorrer vectores em memória ESI - Índice de origem (Source Index) EDI - Índice de destino (Destination Index) 32 bits 16 bits edi di esi si Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 196 Uso do registo ESI no exemplo anterior Conveniente para endereçar vectores int arr[100]; … Sabendo o endereço de arr: Um registo pode funcionar como índice [arr+ regInd] onde regInd toma os valores 0, 1, 2 … Exemplo: percorrer o vector: mov esi, 0 Ciclo: mov eax, [arr+esi*4] inc esi cmp esi, 100 jne Ciclo arr[3], +12 distância do início:12 arr[2], +8 distância do início:8 +4 arr[1], distância do início:4 arr[0], ocupa 4bytes distância do início:0 Endereço Inicial do vector arr Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 197 Endereçamento baseado+indexado O endereço efectivo é obtido somando o conteúdo de dois registos (registos base e índice) mais uma constante (deslocamento) Existe ainda a variante com escala Exemplo: mov [ebx+esi+10], 5 store-im-bas-ind ebx esi 10 Registo 1 (ebx) + + Memória 5 Endereço efectivo Registo 2 (esi) Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 198 Endereçamento baseado+indexado Conveniente para endereçar um vector de estruturas struct my_struct { ... // struct com 16 bytes } arr[100]; arr[99] Suponhamos um registo base (reg1) com o endereço inicial do vector arr Para aceder ao elemento i carregase outro registo (reg2) com i +48 arr[2] +32 arr[1] +16 [reg1 + reg2*16 + 0] : endereço de arr[i].a arr[0] [reg1 + reg2*16 + 4] : endereço de arr[i].b [reg1 + reg2*16 + 12]: endereço de arr[i].c Endereço Inicial do vector arr Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 199 Exemplos de cálculo do endereço efectivo edx 0xf000 ecx 0x100 End. assembly [edx+0x8] Cálculo 0xf000 + 0x8 [edx+ecx] 0xf000 + 0x100 0xf100 [edx+ecx*4] 0xf000 + 4*0x100 0xf400 [edx*2+0x80] 2*0xf000 + 0x80 0x1e080 Arquitectura de Computadores (2008/2009): Programação ao nível da máquina Endereço 0xf008 200 Modos de endereçamento no NASM O NASM permite uma sintaxe mais abrangente e portanto podemos usar posições de memória em vez de registos e realizar operações algébricas que não as definidas no IA-32 Exemplos: mov eax, [vector+ecx] eax Mem[vector+ecx] mov eax, [vector+(ecx-1)*2] eax Mem[vector+(ecx-1)*2] O NASM converte estas notações para as disponíveis no IA-32 Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 201 A instrução LEA Podemos usar a instrução lea para calcular um endereço efectivo, por exemplo o endereço de um elemento de um vector: vector: dd 1, 2, 3, 4, 5, 6, 7 Consideremos que: ebx contém o endereço de vector ecx contém o numero de elementos do vector dec ecx lea edx, [ebx+ecx*4] Podemos ainda usar a notação mais flexível do NASM lea edx, [vector+(ecx-1)*4] Arquitectura de Computadores (2008/2009): Programação ao nível da máquina 202