Tutorial de Assembly
Tutorial de Assembly
Tutorial de Assembly
Aprenda Assembly (e no assembler) em 1 hora (pelo menos o tempo que leva para ler este tutorial, ou 15 min. Com leitura dinmica).
Introduo: Assembly uma linguagem que d medo em muita gente, pois as instrues so completamente estranhas e diferentes de uma linguagem de alto nvel, e mais ainda de uma linguagem visual. Nela voc tem poucas funes bsicas: mover, somar, subtrair, chamar interrupes etc. Neste tutorial, eu estou assumindo que voc j saiba cdigos binrios e um pouco de eletrnica digital; eu irei abordar temas como programao VGA e um pouco de interface com as portas seriais e paralelas. Como este o primeiro tutorial que estou escrevendo, ser um texto para completos iniciantes no assunto, e depois, quem sabe eu escreva textos mais avanados. Mande-me e-mails com sugestes do que eu devo abordar nos prximos textos. Vamos ao que interessa: Em primeiro lugar, eu gostaria de esclarecer algumas coisas: Assembler o compilador, montador, aquele que traduz para o cdigo de mquina o seu cdigo-fonte; Assembly a linguagem de programao que todos vocs querem estudar. Assembly uma linguagem muito eficiente, veloz e que gera executveis menores que as outras linguagens, pois nela voc fala diretamente com a mquina, as instrues usadas so as do processador, por isso sempre importante conseguir o manual do processador, ou algum texto com todas as instrues e comandos dele, isso voc pode encontrar geralmente no site do fabricante. Comparando com as linguagens de alto nvel, no assembly voc ter que digitar muito mais para resolver certos problemas e no ter a portabilidade que certas linguagens oferecem. Para resolver este ltimo problema, sugiro sempre ter em mos o manual de todos os processadores com o qual voc quer que seu programa rode, assim como o dos Sistemas Operacionais, para converte-los. Mas, com tanta dificuldade, porque aprender assembly ento? Simples, as empresas de Hardware preferem profissionais que saibam assembly, pois eles tero uma viso melhor da mquina em si e de como program-la; algumas empresas de Software, como as de jogos, levam isso como bnus, pois algumas solues de problemas s podem ser feitos no assembly, e estes programadores tero uma maior lgica computacional.
Chega de papo furado: Que compilador eu uso? Neste tutorial eu irei utilizar o TASM, pois mais conhecido e mais usado, mas sugiro que aprenda tambm a usar o NASM (no o MASM!!!) pois de graa e tambm muito bom. Afinal, como funciona o Assembly? O Assembly simplesmente se comunica diretamente com a memria e as portas do computador usando registradores. Registradores so nada mais que flip-flops usados para armazenar os dados, e pass-los memria, basicamente os registradores so: AX accumulator (acumulador): utilizado em operaes aritmticas e de I/O (entrada e sada). BX base (base): utilizado como base ou ponteiro.
CX counter (contador): utilizado como contador, e em loops de repetio. DX displacement (repositor): igual ao BX, utilizado para propsitos gerais Estes segmentos podem ser usados sem restries pelo programador, so os chamados de propsitos gerais, eles tem 16 bits que podem ser acessados tambm em duas partes de 8 bits, a parte alta (H) e a baixa (L). Ex.: AH e AL. partir dos 386 estes segmentos passaram a com EAX, EBX, ECX, EDX (o E de Extended, estendido). Existem tambm os registradores de segmentos, eles so: CS code segment (segmento de cdigo): ele contm o endereo do cdigo que est atualmente sendo executado (no mexa nele). DS data segment (segmento de data): contm o endereo dos dados do programa (no mexa nele). ES extra segment (segmento extra): este segmento aponta para onde voc quer que ele aponte, use-o como voc quiser. SS stack segment (segmento de pilha, e no o exrcito alemo): nele est o endereo da pilha, que contm valores retornados de instrues. Destes segmentos, voc s poder modificar realmente o ES, pois ele no aponta para nada importante para a execuo do programa. Existe tambm o FS, que parecido com o ES, e dizem ser mais rpido. Agora eu vos apresento os registradores de ponteiros: SI e DI (source index e destiny index): eles so usados em comandos que movem blocos de bytes de um lugar(SI) para outro(DI), so a fonte e o destino. BP base pointer (ponteiro base): usado geralmente em conjunto com SS, ele nos servir para, por exemplo, passar argumentos para uma funo de um modo bem rpido (e complicado), quando fizer operaes com pilhas, use-o, pois dificilmente voc far besteira com ele. SP stack pointer (ponteiro da pilha): Ele aponta para o topo da pilha, no mexa com ele, no ser que queira que o seu programa trave. Mas me diga....o que so pilhas? Considere pilhas do jeito que o prprio nome diz, pilha!!! K ? Imagine uma pilha de livros, voc comeando a empilha-los, primeiro voc pe o livro de Eletrnica Digital no cho, depois voc pe o livro de Algoritmos em cima dele, e ento pe o de clculo, e em seguida o de Eletromagnetismo, no dia seguinte o professor de Algoritmos pede para trazer o livro na prxima aula, bem, voc ter que tirar os livros de Eletromagnetismo e de Clculo para poder peg-lo.....em Assembly a pilha funciona desse jeito tambm, ou seja, o primeiro que entra o ltimo que sai (neste caso vale aquele ditado: Os ltimos sero os primeiros). Os comandos para pr e tirar um registrador da pilha so respectivamente: PUSH reg POP reg Ex.:1) PUSH AX ;salva o valor de AX ...cdigos, e mais cdigos.... POP AX ;retorna o valor de AX ser de 32 bits, sendo acessados
2) PUSH AX PUSH BX ...mais cdigos... POP BX ;primeiro retorna o valor de BX e ento... POP AX ;...o de AX
Segmentos de Memria, que diabo isso? A memria do computador divida em segmentos para facilitar (?) o seu acesso, isso vem dos XTs que tinham pouca memria, tinham na verdade uma vaga lembrana.... A memria foi divida em segmentos e os segmentos em offsets. 4BC2 : 378F seg. off. Os registradores usados para representar os segmentos e offsets foram ditos acima. Ex.: DS:DI. Voc entender isso melhor ao longo do curso. Pare com essa coisa chata, vamos partir logo para a parte legal (?), os comandos. Bom, vamos comear com alguns comandos bsicos, e ao longo deste texto eu irei mostrando novos comandos.
MOV dest, origem Move um valor para um registrador. Pode-se fazer os seguintes tipos de MOVs: registrador registrador registrador memria registrador registrador de segmentos valor registrador memria registrador
ADD dest, origem Soma a origem com o destino e pe o resultado no destino. Segue a mesma regra do MOV, citado acima. ADC dest, origem Mesma coisa que ADD, s que com "vai um", modificando o "Carry Flag". INT interrupo Chama uma interrupo de propsitos gerais. JUMP label Pula a execuo do programa para a posio do cdigo que se encontra label. Label: pode ser qualquer palavra, desde que no seja reservada pelo compilador (algum comando, etc.) seguido ":". Os nmeros em assembly seguem as seguintes notaes: decimal: apenas escreva o nmero naturalmente; binrio, octal, hexadecimal: ponha uma letra b, o, h no final do nmero. OBS.: no hexadecimal, se o nmero comear com A, B, C, D, E ou F, ponha um zero na frente, assim A000h vira 0A000h. Bom, vamos ao cdigo... Como primeiro programa, vamos fazer o tradicional programa de iniciao em todas as linguagens.....o famoso: HELLO WORLD!!!! Digite o texto abaixo em um editor (e no processador) de texto, como o EDIT ou o Bloco de Notas, e rode a seguinte linha de programa: TASM <nome-do-arquivo>, depois digite TLINK <nome-do-arquivo>. E execute-o.
Hellow.asm .MODEL SMALL ;modelo de memria .STACK 200h ;aloca espao na pilha .DATA ;aqui comeam os dados e declarao de ;variveis HelloWorld DB "Hello World$" ;string sempre termina com $ .CODE ;aqui comea o cdigo do programa START: ;e aqui a funo principal
MOV DX, OFFSET HelloWorld ;movendo o OFFSET de HelloWorld MOV AX, SEG HelloWorld ;movendo o segmento de HelloWorld MOV DS, AX ;e pondo ele no DS MOV AH, 9h ;nmero da funo do MS-DOS INT 21h ;chamada de funo MOV AX, 4c00h ;funo de sada do programa INT 21h END START ;fim do programa
Explicao: .MODEL declara o modelo de memria usado no programa, ele pode ser: TINY: cdigo e dados cabem em 64kb de memria. SMALL: cdigo e dados so menores que 64kb, mas esto em segmentos diferentes. MEDIUM: cdigo maior que 64kb mas os dados so menores. COMPACT: cdigo menor que 64kb e os dados so maiores. LARGE: cdigos e dados so maiores que 64kb, mas arrays no so. HUGE: tudo pode ser maior que 64kb. .STACK aloca espao na pilha. .DATA indica o comeo do bloco de dados. .CODE indica o comeo do bloco de cdigo. START e END START indica onde comea e termina o cdigo principal. Interrupo 21h: so as mais usadas, pois so as chamadas de funes do MS-DOS. Funo 9h do INT 21h: esta funo escreve na tela um string apontada por DS:DX, perceba que a string sempre termina com $, e veja tambm como foi passado os valores do segmento e offset da string, e por ltimo perceba que no podemos passar diretamente o segmento para DS, como foi explicado acima. OBS.: Podemos mover o segmento DATA da seguinte forma: MOV AX, @DATA. Funo 4c00h do INT 21h: esta funo retorna para o MS-DOS. Repare que as funes dos INTs so escritas no registrador AX. Na declarao da String, voc reparou naquela palavra DB? Pois ela significa que estamos declarando um varivel BYTE (8 bits), apesar do tamanho dela ser 1 BYTE, no caso das strings, ela amplia seu valor at o cdigo $ subdividindo a string.
O que so flags Flags so quase como registradores, mas que servem para indicar certos acontecimentos, na verdade ele um registrador de 8 bits onde cada bit indica uma coisa.
SF Signal Flag: 0 positivo, 1 negativo; ZF Zero Flag: 0 no zero, 1 zero; AF Auxiliary Flag: carry (vai um) no fim do reg. ? 0 no 1 sim; PF Parity Flag: 0 mpar, 1 par; CF Carry Flag: vai um; OF Overflow Flag: ocorreu overflow; DF Direction Flag: direo nos comandos de bloco, 0 normal, 1 reverso; IF Interrupt Flag: 0 no executa interrupes, 1 executa interrupes; TF Trap Flag: usado para debulhao (debug) do programa. Como e para que eu uso isso? Flags so importantes para verificar erros nos dados, estouro de pilha, e comandos parecidos com "se..ento". Eles so usados com os comandos CLx e STx, onde x a inicial do flag que voc usar. Ou com comandos derivados do JUMP: Jx ou JNx , onde x a inicial do flag (Ex.: JC, pula se o carry flag est ativado). So usados depois de operaes como ADD e MOV.
JUMPS condicionais: Substituem o "se ento" de outras linguagens. So usados geralmente aps o comando CMP. CMP reg1, reg2 Compara dois registradores, em relao ao seus valores. Os Jumps condicionais so os seguintes: Jumps levando em conta o sinal: JG pula se reg1 for maior que reg2;
JGE pula se reg1 for maior ou igual reg2; JNG pula se reg1 no for maior reg2; JL pula se reg1 for menor que reg2; JLE pula se reg1 for menor que reg2; JNL pula se reg1 for menor ou igual reg2; JE pula se reg1 for igual reg2; JNE pula se reg1 no for igual reg2; OBS.: quando os registradores forem iguais o ZF ser ativado, portanto dizer JE e JZ a mesma coisa. Jumps condicionais sem levar o sinal em considerao: JA pula se reg1 for maior que reg2; JAE pula se reg1 for maior ou igual reg2; JNA pula se reg1 no for maior reg2; JB pula se reg1 for menor que reg2; JBE pula se reg1 for menor que reg2; JNB pula se reg1 for menor ou igual reg2; JE pula se reg1 for igual reg2; JNE pula se reg1 no for igual reg2;
Vamos agora exemplificar tudo isso com um programa que faz uma pergunta e pega a reposta do teclado. Pergunte.asm .MODEL SMALL .STACK 200h .DATA Pergunta DB "Voce gosta de estudar Eletromagnetismo?$" Sim DB "Isso e bom!$" Nao DB "Deveria...$" .CODE START: MOV AX, @DATA
MOV DS, AX ;primeiro faamos com que DS aponte para os dados DeNovo: MOV DX, OFFSET Pergunta MOV AH, 9h INT 21h MOV AH, 0h ;funo 0 do INT 16h: pega um caractere do teclado INT 16h ;e coloca em AX. CMP AX, "S" ;v se sim foi pressionado JNE MauAluno ; se no for, vai para o label "MauAluno" MOV AH, 9h MOV DX, OFFSET Sim INT 21h MOV AX, 4c00h INT 21h MauAluno: MOV AH, 9h MOV DX, OFFSET Nao INT 21h JUMP DeNovo ;repete ate dizer sim END START
Espero que tenha entendido este exemplo, pois a coisa vai comear a pegar agora que vamos falar de...... PROGRAMAO DA PLACA VGA
MODO GRFICO: 320x200 Primeiramente iremos trabalhar com o modo grfico 320x200, e possivelmente eu escreverei tutoriais sobre o famoso modo X e, quem sabe, sobre programao SVGA e VESA. Como eu mudo do modo de texto para o modo VGA? Simples, usa-se interrupes. A interrupo de funes VGA a 10h.
Para passar para modo 320x200: INT 10h Funo 00h mudar modo de vdeo 13h modo 320x200 00h modo texto Ex.: MOV AH, 00h MOV AL, 13h INT 10h ;muda para 320x200 MOV AL, 00h INT 10h ;muda para modo texto Podemos simplesmente escrever: MOV AX, 13h INT 10h J que MOV AX, 13h vai mover 00 para AH e 13h para AL.
Legal, agora eu estou em 320x200, mas o que eu fao com isso? Calma! Um passo de cada vez...vamos aprender a plotar um pixel. Lembre-se que o modo VGA permite utilizar 256 cores de uma vez (0 255), ou seja, utilizaremos apenas 8bits de um registrador para colocar a cor. Poderamos simplesmente utilizar-se de funes do INT 10h para isso, mas isso deixaria o programa realmente muito lento se tivssemos que pintar a tela inteira (precisaramos repetir 64000 vezes). Existe um jeito mais fcil de faze-lo: acessando a memria de vdeo diretamente. O segmento de memria do VGA est no endereo 0A000h, mas e da? E da que assim ns podemos escrever um pixel na tela apenas usando comando MOV, isso torna o programa muito mais rpido, no final deste texto eu colocarei todas as instrues do assembler com seus respectivos ticks (cada perodo do clock). Para plotar um pixel faremos assim: MOV AX, 0A000h ; o segmento do VGA vai para um registrador MOV ES, AX ; de segmento (ES) MOV BX, 32160 ; plota na posio (159,99) MOV DI, BX
MOV AL, 54 ;cor a ser plotada MOV ES:[DI], AL ;[ ] significa que estamos movendo para o ;local de memria indicado por DI, e no ;para o registrador DI. Mas como eu sei que 32160 a posio (159,99)? A memria VGA tratada linearmente (veja a figura abaixo), portanto o offset 0 representa (0,0) o 1 (1,0) o 319 (319,0) e o 320 (0,1), lembrando que se comea a contar do 0,0. Como ento calcular onde plotar? Basta usar esta simples frmula: x + (320 * y), fcil no?
Ento faamos a nossa funo PlotPixel: PlotPixel PROC ARG x:WORD, y:WORD, cor:BYTE MOV AX, 0A000h MOV ES, AX MOV BX, [x] MOV DI, BX MOV AX, [y] MUL 320 ADD DI, AX MOV AL, [cor] MOV ES:[DI], AL RET PlotPixel ENDP Basicamente, isso, como PlotPixel um procedimento e no a funo principal ela definida por: <nome-da-funo> PROC
ARG argumento1:tipo, argumento2:tipo, ... ... RET <nome-da-funo> ENDP ARG usado para declarar os argumentos, RET diz para voltar funo principal, tipo pode ser DBYTE, DWORD, DDOUBLE (8 bits, 16 bits, 32 bits).
Antes de prosseguir vamos dar uma palavra sobre otimizao. Bem essa funo bastante rpida, e se voc tem um computador acima de um 486 voc vai achar isso realmente muito rpido, mas quando voc est trabalhando com animaes, ou atualizao de tela constante, isso se tornar excessivamente lento, ento o que devemos fazer? Em primeiro lugar, vamos lembrar que o processador trata tudo como nmero binrio, ou seja 0s e 1s, nesse caso o processo de multiplicao se torna realmente lento, mas existe duas funes que podem nos ajudar no processo de multiplicao e diviso, os chamados shifts. Os shifts simplesmente arrastam os bits de um registrador para direita ou para a esquerda. Esse processo realmente rpido para o computador, uma vez que ele apenas move os bits. Mas mover os bits de um nmero binrio para a esquerda a mesma coisa que multiplicar por uma potncia de dois, e para a direita dividir por um potncia de dois: SHL reg, num Move os bits para a esquerda num casas. SHR reg, num Move os bits para a direita num casas. Ex.: SHR AX, 1 (move os bits uma casa direita = dividir por 2) SHL AX, 3 (move os bits 3 casas esquerda = multiplicar por 23 = 8) Voc ir notar que 320 no uma potncia de 2, certo, mas 256 e 64 so. Sim, e da? E da que 256 + 64 = 320....ou seja, 256 * y + 64 * y = 320 * y. Agora o cdigo ficar assim: PlotPixel PROC ARG x:WORD, y:WORD, cor:BYTE MOV AX, 0A000h MOV ES, AX MOV BX, [y] MOV DI, BX SHL BX, 8 SHL DI, 6
ADD DI, BX MOV DX, [x] ADD DI, DX MOV AL, [cor] MOV ES:[DI], AL RET PlotPixel ENDP Assim ele j est bom o suficiente, existe outros mtodos para deix-lo ainda mais rpido, mas isso eu deixarei para vocs, por enquanto... Retas Horizontais, Verticais e Diagonais Para desenhar um reta horizontal bem simples, basta repetirmos um plot varias vezes sempre indo para a prxima coluna at o final da linha. PlotHorizontal PROC ARG x1:WORD, x2:WORD, y:WORD, cor:BYTE MOV AX, 0A000h MOV ES, AX MOV AX, [x1] MOV CX, [x2] CMP AX, CX JNA Desenha MOV BX, CX MOV CX, AX MOV AX, BX Desenha: SUB CX, AX MOV BX, [y] MOV DI, BX SHL BX, 8 SHL DI, 6 ADD DI, BX
ADD DI, AX MOV AX, [cor] REP STOSB RET PlotHorizontal ENDP Oba! Mais duas funes novas serem consideradas... REP funo Repete a funo o nmero de vezes indicado no registrador CX, no caso da nossa funo ele vai repetir a diferena x2-x1, que o comprimento da linha. STOSB Copia o contedo de AL para o segmento de memria apontado por ES:DI. STOSW Copia o contedo de AX para o segmento de memria apontado por ES:DI MOVSB Move um byte do contedo apontado por DS:SI para o contedo apontado em ES:DI. MOVSW Move uma word do contedo apontado por DS:SI para o contedo apontado em ES:DI.
Para desenhar um linha vertical, fcil, mas tem-se que considerar certas coisas, tente fazer este por si s e tambm uma funo geral para as linhas. Eu deixarei as respostas com os devidos comentrios nas mesmas pginas que se encontra este tutorial. A resposta no to diferente do procedimento descrito acima, apenas lembre-se de que a cada STOSB o registrador DI acrescido de mais 1. Eu tambm deixarei um pequeno programa de demonstrao, que mostra todas estas funes em uso. Esta parte de VGA ir se encerrar aqui, existem bons materiais espalhados pela net sobre o assunto, eu tambm escreverei um tutorial mais especfico sobre o assunto. Vamos agora com a comunicao serial e paralela. O processador se comunica com os diversos perifricos atravs de portas de comunicao, um PC tem 65535 portas, comunicar-se com estas portas uma tarefa simples, o difcil saber o que faz cada uma dessas portas. Os comandos para entrada e sada de comunicao so: IN AL, porta ou IN AL, DX ou IN AX, DX Este comando obtm dados da porta e coloca-os em AL ou AX, dependendo se voc quer bytes ou words, geralmente usa-se o registrador DX para dizer qual o no da porta, embora possa-se dizer diretamente, se o no da porta for at 255.
OUT DX, AL ou OUT DX, AX Este comando envia dados para a porta especificada por DX. Esta a base do interfaceamento, dependendo do que voc quer fazer, ou do que a mquina que voc estar ligando ao computador foi feita, voc usar esses comandos do jeito mais apropriado. As portas seriais so: Porta: Endereo: COM1 3F8h COM2 3E8h COM3 2F8h COM4 2E8h
A porta paralela geralmente 378h (LPT1). Apndice I: Instrues Assembly Aqui eu colocarei os comandos mais importantes da linguagem Assembly com uma breve explicao e quantos ticks (ciclos de mquina) ele leva (para o 486). ADC dest, fonte Realiza uma soma com carry (vai um). ADD dest, fonte Realiza uma soma. AND dest, fonte Realiza um "e" lgico. CALL function/macro Chama um procedimento ou uma macro. CLx, onde x o flag Limpa o flag correspondente (zera). CMC Muda o valor do carry flag. Se for 1 vira 0, se for 0 vira 1. CMP dest, fonte Compara os dois valores mudando os flags correspondentes. CMPSx, onde x B (byte), W (word), ou D (double). Compara o byte/word/double no endereo DS:[SI] com o ES:[DI]. DEC dest Decrementa de um o registrador. DIV num
Calcula o quociente (sem sinal) da diviso de dest por fonte. Se for uma diviso de bytes, o dividendo estar em AX e o quociente e resto em AH e AL respectivamente. Se for uma diviso de words, o dividendo estar em DX:AX e o quociente e o resto em AX e DX. IDIV num Calcula a diviso com sinal. IMUL num Calcula a multiplicao com sinal. IN AL, porta/DX Pega dados de alguma porta. INC reg Incrementa em um o registrador. INT interrupt Chama uma interrupo. JUMP label Pula para o label correspondente no cdigo fonte. Jx label, onde x alguma condio descrita anteriormente no tutorial. Pula para o label correspondente se a condio for satisfeita. LAHF Pe os valores dos bits mais baixos dos flags em AH. LEA dest, fonte Calcula o offset e pe no registrador. LODSx, onde x B, W ou D. Pe o valor de DS:[SI] em Al, AX ou DX:AX, dependendo da escolha. LOOP/LOOPE/LOOPNE label Repete o cdigo compreendido entre o label e a instruo o nmero de vezes armazenado em CX. LOOPE e LOOPNE so loops condicionais, eles repetem verificando tambm se o zero flag est setado ou zerado. MOV dest, fonte Move o valor de fonte para dest. MOVSx, onde x B, W, D. Move o byte, word ou double de DS:[SI] para ES:[DI].
MUL num Multiplica AX ou AL por num e o resultado armazenado em DX:AX ou AX. NEG dest Nega dest (0 dest). NOP No faz nada (no operation sem operao). (o Windows provavelmente tem muitas desta instruo em seu cdigo...J ). NOT dest Faz um "no" lgico. OR dest, fonte Faz um "ou" lgico. OUT porta/DX, AX/AL Pe um dado na porta especificada. POP reg Pe o valor no topo da pilha em reg. PUSH reg Pe o valor de reg no topo da pilha. POPF/PUSHF Pe o valor da pilha no flag / Pe o valor do flag na pilha. REP instruo Repete a instruo CX vezes. RET Retorna de uma sub-rotina. SHR / SHL dest, num Move os bits de dest para direita / esquerda num posies. STx, onde x um flag. Seta (pe o valor 1) no flag. STOSx, onde x B, W, D. Pe o valor de AL, AX ou DX:AX em ES:[DI].
SUB dest, fonte Subtrai fonte de dest e pe o valor em dest. TEST dest, fonte Faz um "e" lgico, s que no pe o resultado no registrador <dest>, ele apenas muda os flags. XCHG dest, fonte Troca os valores entre os registradores. XOR dest, fonte Faz um "ou exclusivo" entre os registradores.