1. Este site usa cookies. Ao continuar a usar este site está a concordar com o nosso uso de cookies. Saber Mais.

[Tutorial] Programar Assembly (20%)

Discussão em 'Programação' iniciada por Favas, 17 de Dezembro de 2005. (Respostas: 8; Visualizações: 9449)

  1. Favas

    Favas Power Member

    Objectivo:
    O objectivo deste tutorial é dar-te algumas luzes do que é assembly, mostrar-te como funciona a programação nesta linguagem e do quanto é dificil de programar quase ao nivel da maquina.
    Neste tutorial vais construir um programa totalmente em assembly, o programa é simbolico e muito simples, mas devido à dificuldade da linguagem terás que ter muita atenção a cada linha de código e mais que isso, entrar na mecânica do Assembly. (asm)

    Requesitos:
    • Borland Turbo C++ 3.0 que podem sacar daqui: http://www.dei.isep.ipp.pt/~ana/Prog_II/turboc.htm
      Admirado? pois é, vais programar assembly dentro de um compilador C++, eu decidi pelo Turbo C++ 3.0 porque é muito facil de se arranjar, alem disso proporciona um interface gráfico que nos facilita na compilação, linkagem, RUN, etc..
      Não utilizei o Dev C++ (do tutorial anterior) porque utiliza sintaxes muito diferentes do que é realmente assembly.
      Eu quiz usar um compilador C porque, hoje em dia, é o unico sitio onde assembly é realmente util, isto é, só há necessidade de programar assembly dentro de uma linguagens de alto nivel por limitação desta. O assembly é a linguagem mais poderosa de todas, tudo é possivel em assembly, por isso, quando o C, Basic ou Pascal não conseguem fazer, chama-se o todo poderoso e faz-se.
    • Conhecimentos básicos de programação
    • Conhecimentos básicos de DOS
    • Conhecimentos na declaração de variáveis em C

    Programa:
    O programa é muito básico, isto deve-se mais uma vez à dificuldade da linguagem, e à minha perguiça para fazer tutorials :007:
    O programita converterá uma frase até 17 caracteres em caracteres maiusculos. lol > LOL
    Com isto vais ter que aprender a limpar a tela, fazer scanf()'s, percorrer um vector, modificar letras, printf()'s, e getchar(), tudo em 44 linhas de código em assembly.

    Aqui está ele:
    [​IMG]
    Como podes ver, é meramente simbolico mas que já vai dar uma trabalheira.


    Para não haver dúvidas..
    Preparar o C++

    [​IMG]
    [File]->New

    E está tudo pronto.
     
    Última edição: 17 de Dezembro de 2005
  2. Favas

    Favas Power Member

    Começar a programar!

    Vais copiar/colar este codigo na janela no projecto que criaste no Turbo C++.
    (Sei que colar não é possivel em DOS, vai por criar um ficheiro no notepad, cola, salva e renomeia para qqcoisa.cpp, depois então abre no Turbo C++)

    Código:
    main (){
       //0
       char buffer[20];
    
       asm {
          //1
          mov byte[buffer],18
          //2
          lea dx,buffer
          mov ah,0Ah
          int 21h
          //3
          mov al,02h
          int 10h
          //4
          mov bl,byte[buffer]+1
          mov bh,0
          mov di,0
          //5
          }repetir:asm{
          //5.1
          sub byte[buffer]+di+2,32
          cmp di,bx
          inc di
          jb repetir
          //6
          lea di,buffer
          mov byte[di]+bx+2,'$'
          //7
          lea dx,[di]+2
          mov ah,09h
          int 21h
          //8
          mov ah,01h
          int 21h
          //9
          mov ah,4ch
          int 21h
       }
    }
    
    Faz Run (CRTL+F9) para ver se o programa funciona. tks

    Dividi o codigo por 10 passos, que estão como comentários, modo a facilitar a analize.

    Neste programa como seria de esperar, não vais usar nenhuma #include, a declaração de variáveis é em C, não só para facilitar o teu trabalho, mas também porque nada interfere com a mecânica da linguagem. Depois das variáveis declaradas vais fazer "asm{" e agora sim, estás a programar Assembly!

    Ok, antes de começar a analizar o código, tenho que te dar uma pequena seca de teoria que o Assembly exige.

    A linguagem asm é uma linguagem muito básica, tem pouquissimos comandos, não há ciclos, não há funçoes, não há strings, o coitado nem sabe o que são variaveis.. tens apenas operações logicas +,-,*,/, alguns metodos de atribuição e outros de comparação, e é só com isto que podes contar para fazeres o teu programa.
    Tens para te ajudar, os registros do processador, que podem ser vistos como "variaveis".
    Tens 14 registos de 16bits, onde quatro deles são general purpose, AX,BX,CX e DX, que se podem dividir em 8 registos de 8 bits AH,AL,BH,BL,CH,CL,DH,DL.
    Podes guardar valores nestes registos, fazer operaçoes logicas ou comparaçoes da seguinte maneira. (info sobre registos aqui)

    Em assembly ao fazeres "MOV AX,4d" (4=0100) estás copiar o valor 4 para o registo AX, por isso AX é agora igual a 00000000|00000100 binário.
    Se fizermos "INC AX" ele incrementa 1 valor ao AX ficando AX = 5.
    Podemos também sumar "ADD AX,20d" ficando AX = 25.
    Ou então subtrair "SUB AX,7d" ficando AX = 18. (AX = 00000000|00010010)
    Nada mau, já sabes sumar e subtrair!
    Agora vais copiar o valor 10 para o registo AH -> "MOV AH,10d"! AH = 10.
    Sendo AH a parte alta do registo AX, o seu valor que tinhas em AX mudou, ficando 00001010|00010010 binário, o registo AX já nao é igual a 18 como era, agora é igual a 2578 :confused: a nossa sorte é que o valor 18 nao ocupa os 16bits do AX mas apenas 8, por isso o valor 18 ficou intacto na parte baixa do registo AX, isto é AL = 18!
    Resumindo, AH = 10 e AL = 18 (00001010|00010010), por sua vez AX transformou-se num valor que a ti não diz nada AX = 2578...
    Espero nao ter complicado isto tudo..

    OK, tens os registos aos quais podes fazer o que quiser, e onde também podes guardar valores.
    Mas como é que se dá instruções ao computador?
    Atravez de Interruptores! Um interruptor é como a campainha de uma casa, tu tocas à campainha e acontece algo.
    Por exemplo, queres fazer um ClearScreen da consola de DOS, isto em assembly requer um interruptor que é o "INT 10h", a instrução INT é para activar um interruptor, e o 10h é o número do interruptor em hexadecimal.
    Activar o interruptor 10h por si só não faz o ClearScreen, o interruptor 10h faz muita coisa, como por exemplo, Get Pixel, Set Pixel, Set Video Mode, ScrollUp, ScrollDown, todas estas acções expecificam-se dando um valor ao registo AH, para o interruptor 10h saber que queremos um ClearScreen temos que atribuir o valor 02h ao registo AL.
    Por isso:
    Código:
        mov al,02h
        int 10h
    
    isto sim, estas 2 linhas de código, limpam de facto o ecrãn.
    Conclusão, para acções atravez dos interruptores, tens que preparar a informação todinha para que o hardware reaja da forma pretendida.
    Esta informação do que faz o interruptor 10h, do que precisa e como reage, está toda documentada na net, é só procurar. ex. 1 2

    Em Assembly, tudo se resume a mover informação de um sitio para o outro, muitos MOV's, alguns ADD's e SUB's, outros tantos INT's e está o programa feito.
    Informática é isto mesmo, movimentar e processar informação

    (post por acabar)
     
    Última edição: 30 de Maio de 2006
  3. Favas

    Favas Power Member

    0º passo
    Código:
          char buffer[20];
    É simples, com isto estás a reservar um vector com o tamanho 20 (0-19) do tipo char (8bits) na memória ram do computador.
    Este bocado de memória é teu, nenhuma aplicação irá lhe mexer senão a tua.
    E é este vector que irá guardar a palavra do utilizador.

    [​IMG]

    1º passo
    Código:
          mov byte[buffer],18
    este "mov" coloca o valor 18 no primeiro byte (posição 0) do vector buffer.
    ficando assim o teu vector buffer

    [​IMG]

    já explico porque fizeste isto.

    2º passo
    Código:
          lea dx,buffer
          mov ah,0Ah
          int 21h
    
    Vais agora prepara o sistema para receber o input do utilizador, para que tal aconteça tens que recorrer a um interruptor.
    O interruptor que lê do teclado é o 21h "int 21h" e já te expliquei como os interruptor são esquisitos e sensiveis com a informação, segundo pesquisei na net, para que o 21h leia informação do teclado precisa de ter o valor "0Ah" no registro AH, muito bem, fazes "mov ah,0Ah".

    [​IMG]

    Não fica por aqui, o int 21h pede-te também coloques no registro DX o endereço de memória onde ele irá gravar o que for escrito no teclado. Nós mais uma vez fazemos a vontade, e vamos dar o endereço do primeiro byte (posição 0) do vector buffer que reservamos na memoria no registo DX - "lea dx,buffer"
    "LEA" quer dizer "Locate Effective Address" e neste caso o que faz é copiar o endereço de memoria do vector buffer para o registo DX. DX é agora um ponteiro a apontar para o primeiro byte do buffer.

    Este 21h é chato quando se trata a ler do teclado, e pede-te uma 3ª e ultima coisa, que coloques no primeiro byte do vector buffer o limite de caracteres que o user pode escrever. Lá está, foi aquilo que fizemos no passo 1 "mov byte[buffer],18" onde dissemos que o número de caracteres irá ser 18 (incluindo o enter, 17 caracteres práticos).

    ok, os 3 requesitos que ele pediu estão feitos, e eles foram:
    valor 0Ah no AH - "mov ah,0Ah" - Feito
    um endereço de memoria no DX - "lea dx,buffer" - Feito
    limite de caracteres de input no 1º byte do vector - "mov byte[buffer],18" - Feito

    ok, uma vez tudo confirmado! vais activar o interruptor -----> "int 21h"

    info sobre 21h

    3º passo
    Se tudo correu bem e o input foi bem sucedido, deves ter agora o teu vector com a seguinte informação (faz de conta que o user teclou "techzone")

    [​IMG]

    O teu vector está agora preenchido com informação, vais analizar a informação.
    byte 0 - já sabias, é o limite de caracteres da string
    byte 1 - se fores ao site que te dei, vês que o interruptor 21h preenche o segundo byte do vector com o tamanho da string que neste caso é 8 "t-e-c-h-z-o-n-e" (enter não conta)
    byte 2+n - os valores dos proximos bytes sao agora o codigo ascii que o user teclou.

    voltando ao codigo
    Código:
          mov al,02h
          int 10h
    
    este é o tal ClearScreen que te falei usando o interruptor 10h, que para funcionar requer o valor 02h no registo AL - "mov al,02h"
    Activa-se o interruptor - "int 10h" - e voila, tens a tela limpa.

    4º Passo
    Código:
          mov bl,byte[buffer]+1
          mov bh,0
          mov di,0
    
    Como já tens os dados do input do utilizador guardados no teu vector buffer, vais agora ter de processar a informação recebida, já não há mais interruptores agora uff, sendo o objectivo deste programa converter uma string em maiusculas eu decidi fazer do seguinte modo.
    Criar uma rotina tipo FOR que percorra o vector desde o primeiro valor ascii que o user introduziu até ao ultimo, e que subtraia 32 a cada valor.

    O objectivo de subtrair 32 a cada valor ascii do input do user, é porque na tabela ascii de DOS, o caracter minusculo tem o seu correspondente maiusculo 32 valores a baixo.
    Sendo assim: ('a' - 32) = 'A', ('b' - 32) = 'B', ('c' -32) = 'C', etc

    [​IMG]

    e os valores a subtrair são estes

    [​IMG]

    Agora com a estratégia montada, vais começar por guardar o número de caracteres intruduzidos pelo user no registo BL, e isto faz-se da seguinta maneira.
    Já sabias que o int 21h guardava o tamanho da string introduzida no segundo byte (posição 1) do vector buffer não é? então o que tens de fazer é só copiar esse mesmo byte para o registo BL.
    Código:
          mov bl,byte[buffer]+1
    BL é o registo qual queres guardar o tamanho, byte[] serve para informar ao compilador que o valor ao qual o endereço [buffer] aponta é do tipo byte, o +1 é porque o tamanho da string não está guardada no primeiro byte (pos 0) do endereço buffer mas sim no segundo (pos 1).
    Tal como podes ver:
    [​IMG]

    Neste momento BL deve ter agora o valor 8 decimal.
    Mas o objectivo de copiar o tamanho da string para o registo BL, na realidade era fazer com que o registo BX ficasse tambem com o tamanho da string, coisa que não aconteceu, por isso:
    [​IMG]
    OK, BL = tamanho da string = 8, que por consequencia BX também = 8 tao como pretendido.
    Eu tive de fazer isto, porque assembly é muito sensivel a tudo, ele não compara bytes com integers, não efectua operações logicas entre registos diferentes, é por isso que fui obrigado a fazer todas estas mudanças.

    Uma vez que Assembly nao tem rotinas, vou eu ter que criar uma rotina, e para isso preciso de um incrementador. Vou usar o registro DI como incrementador, então vou igualalo a zero.
    Passo seguinte. a Rotina (FOR)

    Boas, desculpem lá o atrazo e nao ter acabado o tutorial ainda.
    Quando comecei o tut tinha todo o tempo do mundo, depois derrepente a vida numa semana muda toda e ficamos sem tempo para nada..
    Prometo acabar o tutorial, assim que puder, talvez para meio ou fim do verão.
    Cumps e desculpa se alguem seguiu este tutorial para aprender asm.
     
    Última edição: 30 de Maio de 2006
  4. possessed

    possessed Full Throttle BOINC Roller

    vou-te interromper aqui um coto :D porque é q não recomendas aí o turbo assembler, acho uma ferramenta mais recomendada para programar asm do que usar o turbo c
     
  5. Favas

    Favas Power Member

    A principal razao é porque muitos de nós está familiarizado com o C, programar no turbo assembler é um bocado chato, tem tudo que ser escrito em assembly, e pouco muda uma vez que são ambus da borland. No Turbo C++ tens (tenho) mais controlo do código, posso fazer um "mov a,ax" depois um "printf("%i",a);" e saber a quantas ando tudo onscren, sem usar watches. Como tambem usar um clrscr(); enquanto nao me apetece pesquisar info de como se faz em asm.
    Acaba por ser uma preferencia pessoal, quem quizeres usar o turbo assembler está à vontada, o código é transportavel, e só muda basicamente a declaração das variaveis e alguma sintaxe.

    Acredita que parei para pensar uns bons minutos sobre qual o compilador iria usar, decidi o Turbo C++, espero não desiludir ngm :(

    btw, já reservei os posts que preciso para o tutorial, nao precisam de ter medo em postar porque não estão a interromper
     
    Última edição: 17 de Dezembro de 2005
  6. Delta

    Delta Suspenso

    desculpa também interromper

    com Pascal também se pode programar/introduzir código em Assembly

    fazia isso para controlar os Interrupts

    isto pode, portanto, ser aplicado ao Pascal

    exactamente assim :

    asm
    Movl $1,%ebx
    Movl $0,%eax
    addl %eax,%ebx
    end;


    ou seja código assembler introduzido no bloco ASM---END;
     
    Última edição: 20 de Dezembro de 2005
  7. greven

    greven Folding Artist

    Excelente tópico, post, tutorial. Assembly em 8086, been there, done that! :D Fiz isso na cadeira de Introdução aos Microprocessadores. Tinhamos uma placa com o 8086, uns leds, um LCD, buzz, etc, etc. Depois no trabalho final tinhamos de fazer um Relógio despertador em que desse para por a hora em que tocava o bezouro. Em alternativa, podiamos fazer um programa para tocar músicas, introduzia-se as notas, uma frequncia uma nota, etc, etc.

    Bom tutorial. :)
     
  8. FR34K

    FR34K Power Member

    hY,

    oh favas, n tens por ai nenhum algoritmo de faça a soma de 2 vectores em assembly ?



    adiciona-me ao msn se puderes para podermos falar !


    hug

    [] cheerz :>
     
  9. olá a todos. Gostaria que alguém me desse umas dicas sobre assembly...Gostaria de obter um programa unicamente de assembly onde podesse editar,compilar,etc programas..ou então um compilador. depois gostaria de saber como faço um cear screen já que as duas linhas
    mov ah,02h
    int 10h
    não funcionaram

    gostaria de saber também como poderia utilizar a função de verificação se uma tecla foi pressionada e continuar a correr outra coisa até que seja pressionada a tecla b ou B

    objectivo: programa para fazer uma contagem crescente iniciada quando premida a tecla a ou A e parar essa contagem com a tecla b ou B

    obrigado pela atenção
     

Partilhar esta Página