Informática Numaboa - Tutoriais e Programação
Criando uma mini-calculadora em Assembly para Win32
- Detalhes
-
Categoria: Assembly Numaboa (antigo oiciliS)
-
Atualização: Quinta, 15 Janeiro 2009 17:45
-
Autor: Felipe Costa
-
Acessos: 21433
Bem, pessoal, este é o meu primeiro tutorial sobre alguma coisa, então, se for mal escrito ou mal explicado, já peço desculpas. Há um tempo atrás já tinha lido sobre assembly, mas não consegui aprender nada, aí dei uma lida em um tutorial da Vó Vicki, sobre como criar janelas em ASM e desenvolvi uma MINI-Calculadora em Assembly para Win32. Pode ter alguns erros ou falta programar algumas coisas, mas funciona e serve muito pra você aprimorar seus conhecimentos e muito mais.
Conhecimento preciso
Você precisa saber pelo menos o básico sobre como criar janelas em ASM, registrá-las e mostrá-las na tela. Se você tiver alguma dúvida sobre isto, veja o tutorial sobre como criar sua primeira janela. Clique no link abaixo:
Link para o tutorial sobre Janelas
Pronto! Você precisa deste tutorial para saber o básico sobre Janelas.
O programa que foi usado para fazer o desenvolvimento do aplicativo foi o Masm32, então acredito que você vai precisar dele também ou algum outro de sua preferência. Ah, e com certeza você também precisa de conhecimento em Assembly, não muito, mas você precisa.
Começando
Eu sempre quis fazer primeiros algumas coisas para depois aprender como elas funcionam, mas não façam isso. Se você não souber nada ou pouca coisa sobre como criar janelas, volte um pouco e leia o tutorial indicado.
Bem, então vamos ver o que vai ser feito para ficarmos logo por dentro de tudo!
Essa aí seria a nossa calculadora, mas é claro que, depois de terminada, você pode implementar algumas coisas a mais como um menu com uma About Box, mas isto será feito em outra ocasião por você mesmo. Vamos primeiro tentar desenvolver esta calculadora básica.
Primeiro começamos com o cabeçalho do programa e, nesta calculadora, você não precisa mais do que instruções do processador .386. Então você já sabe o que fazer, né?
Aqui começa o código começa:
.386
.MODEL Flat, StdCall
Option casemap:none
Logo no começo iremos precisar apenas das bibliotecas user32.lib e Kernel32.lib. Então, continuando com o código:
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
Agora vamos inserir o protótipo Gerenciador_Janela:
Gerenciador_Janela proto :DWORD, :DWORD, :DWORD, :DWORD
Você pode chamar esse protótipo da forma que quiser, desde que você saiba que ele servirá de modelo para o procedimento que criaremos mais à frente para gerenciar a janela
Como sabemos, a seguir vêm as variáveis inicializadas. No momento precisamos apenas das principais para criarmos a janela onde ficarão os botões. Indicamos o título da janela e o nome da classe da janela:
DATA
Titulo_Janela db "Calculadora em ASM", 0
Classe_Janela db "Form1",0
A seguir vem o Handle de novas janelas e a Linha de Comando da janela (a linha de comando é opcional, mas, por via das dúvidas, vamos colocá–la), e depois os Handles dos botões e do edit:
.DATA?
Handle_Janela DWORD ?
LinhaComando DWORD ?
Agora vem o código propriamente dito:
.CODE
Inicio:
Invoke GetModuleHandle, NULL
Mov Handle_Janela, eax
Invoke GetCommandLine
Mov LinhaComando, eax
Invoke Gerenciador_Janela, Handle_Janela, NULL, LinhaComando, SW_SHOWDEFAULT
Invoke ExitProcess, 0
Programa começado, agora precisaremos escrever o procedimento que gerenciará nossa janela, criando-a e registrando-a.
Gerenciador_Janela proc hInstance :DWORD, hInstAntiga :DWORD, LnComando :DWORD, Tipo_Janela: DWORD
Nomearei a classe da minha janela de "wnd", abreviação de Window.
LOCAL wnd: WNDCLASSEX
LOCAL Janela: HWND
LOCAL Mensagem: MSG
;;Criando a janela e registrando
mov wnd.cbSize, SIZEOF WNDCLASSEX
mov wnd.style, CS_HREDRAW or CS_VREDRAW
mov wnd.lpfnWndProc, offset GerenteMensagem
mov wnd.cbClsExtra, NULL
mov wnd.cbWndExtra, NULL
push Handle_Janela
pop wnd.hInstance
invoke LoadIcon, NULL, IDI_WINLOGO
mov wnd.hIcon, eax
mov wnd.hIconSm, eax
invoke LoadCursor, NULL, IDC_ARROW
mov wnd.hCursor, eax
mov wnd.hbrBackground, COLOR_BTNFACE+1
mov wnd.lpszMenuName, NULL
mov wnd.lpszClassName, OFFSET Classe_Janela
invoke RegisterClassEx, ADDR wnd
Bem, no tutorial de janelas você aprendeu a chamar a API do Windows (CreateWindowEx). Espero que você tenha entendido ela bem, por que nós a usaremos para criar os botões e os edits.
invoke CreateWindowEx, NULL, ADDR Classe_Janela, ADDR
Titulo_Janela,WS_OVERLAPPEDWINDOW, 433, 302, 230,190, NULL, NULL, hInstance, NULL
Você talvez deva ter achado aqueles números na chamada estranhos, mas, para refrescar a memória, eles significam:
- 433: Distância do começo de sua janela, que é o seu lado esquerdo, até o início esquerdo da tela do seu computador.
- 302: Distância do topo de sua janela até o topo da tela do seu computador.
- 230: Dimensão da largura da sua janela.
- 190: Dimensão da Altura da sua janela.
Você pode defini-los como quiser.
mov Janela, eax
invoke ShowWindow, Janela, SW_SHOWNORMAL
invoke UpdateWindow, Janela
.WHILE TRUE
invoke GetMessage, ADDR Mensagem, NULL, 0,0
.BREAK .IF (eax < 1)
invoke TranslateMessage, ADDR Mensagem
invoke DispatchMessage, ADDR Mensagem
.ENDW
mov eax, Mensagem.wParam
ret
Gerenciador_Janela endp
Agora que já temos o nosso procedimento de criar a janela feito iremos para onde trataremos as mensagens, onde saberemos qual foi a mensagem recebida e o que será feito. É lá onde o negócio legal vai começar!
Continuando com o código:
GerenteMensagem proc hWnd: DWORD, uMsg: UINT, wParam: WPARAM, lParam: LPARAM
Primeiro verificamos se a mensagem que foi recebida é WM_DESTROY. Se for, feche o programa, caso contrário prossiga com as nossas instruções.
.If uMsg == WM_DESTROY
Invoke PostQuitMessage, NULL
Bem, aí sabemos que o programa recebeu a mensagem para ser fechado e depois disto nada mais pode ser feito.
Agora chegou a hora em que temos que criar o Edit Principal da Calculadora, onde ficarão os números, e depois os botões. Para criá-los, verificamos se a mensagem é igual a WM_CREATE; se for, podemos criar os edits e os botões.
.ELSEIF uMsg == WM_CREATE
Bem, pessoal, como meu irmão sempre me disse, tudo no Windows é uma janela. Pode não ser tudo, mas pelo menos quase tudo é: botões, edits, ListBox, etc, são todos janelas, porém são diferentes da nossa janela "mãe". São janelas filhas e, se são filhas, com certeza precisam de uma janela mãe. Então, já que agora sabemos que são janelas filhas e você já tem a mãe delas, então é só chamar a API CreateWindowEx pra criar novas janelas. Mas como sabemos, uma janela precisa basicamente de uma classe, de um título e de um Handle. Então sabemos agora que precisamos criar variáveis novas para o nosso programa. Para criar o Edit, não precisamos necessariamente de um título - então crie somente o nome da classe do Edit e uma variável não inicializada para ficar com o Handle do Edit.
Voltando ao início do código você teria:
.DATA
Titulo_Janela db "Calculadora em ASM", 0
Classe_Janela db "Form1",0
Edit_Classe db "Edit", 0
.DATA ?
Handle_Janela DWORD ?
LinhaComando DWORD ?
EditHandle DWORD ?
Pronto. Agora podemos criar nosso novo Edit! É só chamar a API CreateWindowEx
Invoke CreateWindowEx, NULL, ADDR Edit_Classe, NULL, WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT or ES_AUTOHSCROLL, 8, 8, 193, 21, hWnd, NULL, Handle_Janela, NULL
Mov EditHandle, eax
.ELSE
Invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
.ENDIF
GerenteMensagem endp
End Inicio
A partir de agora temos o nosso edit criado. Se você quiser executá-lo neste ponto, se o seu código estiver correto você já terá um edit na tela.
Criando os botões
Agora iremos aprender ou, se já sabemos, iremos criar os botões na nossa tela. Os botões irão de 0 até 9 e ainda teremos os botões de Soma, Subtração, Multiplicação e Divisão. Um ainda não implementado por mim, mas que pode ser implementado por vocês, é o da vírgula ou ponto como queira. Teremos ainda o CE, e por fim o Botão C.
Se já sabemos quais botões temos que criar, então vamos criar a classe de todos os botões. Voltando novamente ao início:
.DATA
...
; Logo após as variáveis que você já criou, crie uma agora que conterá a classe do botão
Botao_Classe db "Button", 0
; Classe criada, mas nós queremos mostrar o titulo do botão nele para sabermos quem é o
; Botão 1, o 2 , 3 e etc... sendo assim, temos que criar o titulo desse botão.
Botao1_Titulo db "1",0
.DATA?
...
; Logo após todas as variáveis que você já criou dentro dessa seção, você terá que criar outra.
; Sabemos que um botão é uma janela, então significa que ele terá que ter seu próprio Handle.
; Então temos que criar um novo Handle para o botão que vai ter o número 1.
Botao1_Handle DWORD ?
Agora podemos tranquilamente criar o nosso primeiro botão, que será o botão que conterá o número 1. Teremos que ir agora pra o procedimento GerenteMensagem e, logo abaixo de onde criamos o nosso edit, vamos criar o resto dos botões.
Invoke CreateWindowEx, NULL, ADDR Edit_Classe, NULL, WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT or ES_AUTOHSCROLL, 8, 8, 193, 21, hWnd, NULL, Handle_Janela, NULL
Mov EditHandle, eax
Logo após essa parte do código chamaremos novamente a função CreateWindowEx para criar o primeiro botão, mas, antes disso, um problema que talvez seja achado por muitos "difícil" de resolver é onde ficará a posição dos botões na tela definida através de valores. Com o tempo, porém, se você pegar o jeito, isso deixa de ser um "problema" e sem contar que você pode usar suas técnicas Mas não se preocupe, no tutorial os botões já virão todos com suas posições definidas na tela, e lembrando que você pode alterar a posição deles na hora que quiser. Então vem o código do botão.
Invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR Botao1_Titulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 8, 32, 33, 25, hWnd, NULL, Handle_Janela, NULL
Mov Botao1_Handle, eax
Se você adicionar o código e executar o programa, terá um botão na tela. Criar os outros botões não é diferente. Você só tem que se lembrar de criar uma nova variável em .DATA com o título do botão e uma nova em .DATA? que salvará o Handle do novo botão. Podemos fazer aquele velho CTRL + C e CTRL + V, sem esquecer de alterar algumas coisas.
OBS: A variável Botão_Classe pode ser usada para criar o resto de todos os outros botões. Então ela não muda, por que todos pertencem à mesma classe - a classe não precisa ser recriada. Ah, se você quiser pegar mais familiaridade com a API CreateWindowEx, então crie todos os botões digitando os códigos, sem copiar e colar.
Segue abaixo o código para criar o resto dos botões com as devidas posições. Primeiro adicionar os títulos de todos os botões e os Handles para os mesmos na seção .DATA e .DATA?
.DATA
...
Botao2_Titulo db "2", 0
Botao3_Titulo db "3", 0
Botao4_Titulo db "4", 0
Botao5_Titulo db "5", 0
Botao6_Titulo db "6", 0
Botao7_Titulo db "7", 0
Botao8_Titulo db "8", 0
Botao9_Titulo db "9", 0
Botao0_Titulo db "0", 0
BotaoVirgulaTitulo db ",", 0
BotaoIgualTitulo db "=", 0
BotaoMaisTitulo db "+", 0
BotaoMenosTitulo db "-", 0
BotaoMultiplicarTitulo db "*", 0
BotaoDividirTitulo db "/", 0
BotaoCETitulo db "CE", 0
BotaoCTitulo db "C", 0
.DATA?
...
Botao2Handle DWORD ?
Botao3Handle DWORD ?
Botao4Handle DWORD ?
Botao5Handle DWORD ?
Botao6Handle DWORD ?
Botao7Handle DWORD ?
Botao8Handle DWORD ?
Botao9Handle DWORD ?
Botao0Handle DWORD ?
BotaoVirgulaHandle DWORD ?
BotaoMaisHandle DWORD ?
BotaoMenosHandle DWORD ?
BotaoMultiplicarHandle DWORD ?
BotaoDividirHandle DWORD ?
BotaoIgualHandle DWORD ?
BotaoCEHandle DWORD ?
BotaoCHandle DWORD ?
Logo após o código do botão 1 você pode implementar o código de acordo com meu...
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR Botao2_Titulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 48, 32, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov Botao2Handle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR Botao3_Titulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 88, 32, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov Botao3Handle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR Botao4_Titulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 8, 64, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov Botao4Handle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR Botao5_Titulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 48, 64, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov Botao5Handle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR Botao6_Titulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 88, 64, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov Botao6Handle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR Botao7_Titulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 8, 96, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov Botao7Handle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR Botao8_Titulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 48, 96, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov Botao8Handle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR Botao9_Titulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 88, 96, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov Botao9Handle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR Botao0_Titulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 8, 128, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov Botao0Handle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR Botao0_Titulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 8, 128, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov Botao0Handle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR BotaoVirgulaTitulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 48, 128, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov BotaoVirgulaHandle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR BotaoIgualTitulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 88, 128, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov BotaoIgualHandle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR BotaoMaisTitulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 128, 32, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov BotaoMaisHandle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR BotaoMenosTitulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 128, 64, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov BotaoMenosHandle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR BotaoMultiplicarTitulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 128, 96, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov BotaoMultiplicarHandle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR BotaoDividirTitulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 128, 128, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov BotaoDividirHandle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR BotaoCETitulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 168, 32, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov BotaoCEHandle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR BotaoCTitulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 168, 64, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov BotaoCHandle, eax
Pronto. Com todas essas adições nós temos o design final da nossa mini_calc.
Dando funcionalidade aos botões
Agora vem a parte onde você tem que programar o que o botão vai fazer ao ser clicado pelo usuário. É só verificar se a mensagem agora é um WM_COMMAND. Se for, teremos que fazer outra verificação, teremos que pegar o valor retornado na mensagem no parâmetro do procedimento que nós chamamos anteriormente de lParam. Certo? Vamos ver como fazer isso na prática.
Logo abaixo do código do último botão (o botão "C"), vamos ter que implementar mais código, agora pra dizermos o que os botões irão fazer.
...
.ELSEIF uMsg == WM_COMMAND
;;Movendo o valor de lParam para edx
Mov edx, lParam
;;Comparando edx com o Handle do Botão 1
.if edx == Botao1_Handle
Bem, nesse momento você deve ter pensado que agora era só colocar uma chamada para SetWindowText passando o valor do título para o botão. Se você pensou assim, você não errou. Porém há algo mais a se pensar: se o usuário clicar no botão 1, logicamente o texto do nosso edit seria 1, certo? Mas se o usuário clicasse no 2, então o texto do edit seria 2 e não 12. É ai onde entra a função lstrcat.
Breve explicação sobre a função lstrcat:
lstrcat recebe duas strings terminadas em 0 e retorna um ponteiro que nos indica onde está a junção das duas strings que foram passadas, e esse ponteiro é retornado no registrador eax.
Já que sabemos isso, vamos programar; só precisamos da lógica. Primeiro pegaremos o texto que está no Edit. Então vamos lá! Vamos criar uma variável para guardar o texto do edit lá em data?.
.DATA?
...
Edit_Texto db 100 dup (?)
Indo agora para o código do Botao1, chamaremos a função GetWindowText para pegar o texto do Edit:
...
Invoke GetWindowText, Edit_Handle, ADDR Edit_Texto, 100
E agora chamamos lstrcat passando as duas strings.
Invoke lstrcat, ADDR Edit_Texto, ADDR Botao1_Titulo
E você terá que guardar o valor do ponteiro retornado em alguma variável. Então declare uma em Data? do tipo DWORD.
.DATA
...
Endereco_String DWORD ?
Agora que já temos a variável é só mover o valor para dentro dela.
Mov Endereço_String, eax
Agora, como você tinha pensado (ou não), chamaremos a API SetWindowText.
Invoke SetWindowText, Edit_Handle, [Endereco_String]
.endif
Pronto, terminamos o código do botao1. Pense e faça a mesma coisa para os outros botões, menos virgula, adição, subtração e tal, tal, tal...
Aqui vai o código completo de mais dois botões. Faça o mesmo para os botões restantes:
...
.if edx == Botao2Handle
Invoke GetWindowText, EditHandle, ADDR Edit_Texto, 100
Invoke lstrcat, ADDR Edit_Texto, ADDR Botao2_Titulo
Mov Endereco_String, eax
Invoke SetWindowText, EditHandle, [Endereco_String]
.endif
.if edx == Botao3Handle
Invoke GetWindowText, EditHandle, ADDR Edit_Texto, 100
Invoke lstrcat, ADDR Edit_Texto, ADDR Botao3_Titulo
Mov Endereco_String, eax
Invoke SetWindowText, EditHandle, [Endereco_String]
.endif
Tendo todos os botões codificados, agora só precisamos configurar os botões das operações. Iremos começar pelo botão de Adição, os outros três são semelhantes mudando poucas funções.
.if edx == BotaoMaisHandle
Agora vamos ter que fazer algo bem legal - conversões. Você não pode somar duas strings, então você tem que converte-la num valor inteiro e salvá-lo em algum lugar. Então vamos pensar um pouco: quando o usuário digitar alguns números e depois clicar em mais, você terá que salvar o valor digitado e já convertido e limpar o edit para a entrada do outro valor. Isto nos diz que precisaremos de 3 variáveis, uma para salvar o primeiro valor, outra para o segundo valor e a terceira para salvar qual operação está sendo selecionada. Então vamos criá-las:
.DATA?
....
Valor1 DWORD ?
Valor2 DWORD ?
Resultado DWORD ? ;; aqui é uma variável que vai ser usada mais a frente para salvar o
;;resultado da adição, subtração, Multiplicação ou Divisão.
Operacao DWORD ?
Continuando com o botão de adição...
Mov Operacao, 01h ;; Nesse momento definimos que a operação atual é adição representada
;;pelo número 1
Invoke GetWindowText, EditHandle, ADDR Edit_Texto, 100
Voltamos a usar nossa variável Edit_Texto em outro lugar, mas não se preocupe, trabalhar com ela aqui não vai atrapalhar o funcionamento dos outros botões. Agora chegamos num ponto onde chamaremos uma função de conversão, porque teremos que converter os dados de string para dword. Esta função se encontra na biblioteca chamada masm32.lib e é só você adicionar lá no começo no cabeçalho "include \masm32\include\masm32.inc" e "includelib \masm32\lib\masm32.lib".
Agora podemos chamar as funções "atodw" e "dwtoa", abreviações de "AsciiToDword" e "DwordtoAscii". Para chamar a função "atodw" nós passamos como parâmetro a string a ser convertida e o valor é retornado em eax.
Invoke atodw, ADDR Edit_Texto
Mov Valor1, eax ;; Movendo o valor convertido para a variável Valor1
Invoke SetWindowText, EditHandle, NULL ;; Deixando o Edit vazio para a entrada do
;; segundo valor
.endif
Este é o código para o botão da soma. Para o botão da subtração a operação será mudada de 01h para 02h, e quando o clique for no botão multiplicar, 03h e é claro, quando o botão clicado for dividir 04h. Então programe
O código dos outros botões estarão no fim do tutorial quando for mostrado o código completo. Estamos chegando no fim deste tutorial onde programaremos o código do botão Igual. É onde chamaremos os quatro procedimentos de adição, subtração, multiplicação e divisão. Teremos o seguinte código:
invoke GetWindowText, EditHandle, ADDR Edit_Texto, 100
invoke atodw, ADDR Edit_Texto
mov Valor2, eax ;; O valor2 foi pego, então já podemos fazer a operação
.if Operacao == 01h
invoke Soma, Valor1, Valor2
.elseif Operacao == 02h
invoke Subtracao, Valor1, Valor2
.elseif Operacao == 03h
invoke Multiplicacao, Valor1, Valor2
.elseif Operacao == 04h
Na divisão temos que verificar se o valor digitado pelo usuário foi zero ("0"). Se sim, mostraremos uma mensagem dizendo que é impossível dividir por zero. Então crie duas variáveis, uma para ser o texto da mensagem e a outra o caption. O código segue abaixo.
.if Valor2 == 0
invoke MessageBox, hWnd, ADDR Texto_Msg, ADDR Titulo_Msg, MB_OK + MB_ICONEXCLAMATION
invoke SetWindowText, EditHandle, NULL
.elseif
invoke Divisao, Valor1, Valor2
.endif
invoke Divisao, Valor1, Valor2
.endif
Não execute o programa agora, pois ele daria erros dizendo que os procedimentos não existem. Então vamos criá-los. Primeiros temos que add os prototypes no início do programa.
...
Gerenciador_Janela proto :DWORD, :DWORD, :DWORD, :DWORD
Soma proto :DWORD, :DWORD, :DWORD, :DWORD
Subtracao proto :DWORD, :DWORD, :DWORD, :DWORD
Multiplicacao proto :DWORD, :DWORD, :DWORD, :DWORD
Divisao proto :DWORD, :DWORD, :DWORD, :DWORD
Nesse momento você pode terminar o procedimento GerenteMensagem. Nada mais será posto lá. Então, primeiro iremos criar o procedimento de soma, o procedimento abaixo pode ser digitado antes de end inicio e fora de qualquer "proc". Considerando que já temos os dois valores predefinidos, o que precisaremos fazer? Somente adicionar um valor a outro. Procurei preservar os valores de eax e ebx, então os "pushs" e "pops" serão usados:
Soma proc Val1: DWORD, Val2: DWORD
push eax
push ebx
xor eax, eax
xor ebx, ebx ;; Zerando os valores dos dois registradores para receberem os valores Val1 e Val2
mov eax, Val1
mov ebx, Val2 ;; Agora fazendo a soma entre os dois
add eax, ebx ;; Movendo o resultado para a variável que já criamos
mov Resultado, eax ;; Agora como sabemos não podemos mostrar uma variável DWORD como texto ela tem
;; que ser convertida então usaremos de dword para string, na chamada passamos como
;;parâmetro o valor a ser convertido e a string que irá receber o valor.
invoke dwtoa, Resultado, ADDR Edit_Texto
invoke SetWindowText, EditHandle, ADDR Edit_Texto
xor eax, eax
mov Valor1, eax
mov Valor2, eax
mov Resultado, eax
pop ebx
pop eax
ret
Soma endp
Se você entendeu esse procedimento, os outros não mudam grande coisa. O da subtração (Subtracao proc Val1: DWORD, Val2: DWORD), ao invés de add eax,ebx vai usar sub eax,ebx; o da multipilicação (Multiplicacao proc Val1: DWORD, Val2: DWORD) vai usar mul ebx e o da divisão (Divisao proc Val1: DWORD, Val2: DWORD) vai usar div ebx.
Comentários
Tutorial grande, né? Eu achei, mas não sei se você percebeu, ainda ficou faltando o código de dois botões, o CE e o C. Então os deixo como atividade para vocês fazerem. Dou uma dica, um deles apaga somente o Valor2 e o outro zera tudo. Então até o próximo tutorial. Espero que vocês tenham aprendido alguma coisa galera, blz? Segue abaixo o código fonte completo da Calculadora.
Código fonte completo
.386
.MODEL Flat, StdCall
Option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\masm32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\masm32.lib
Gerenciador_Janela proto :DWORD, :DWORD, :DWORD, :DWORD
Soma proto :DWORD, :DWORD
Subtracao proto :DWORD, :DWORD
Multiplicacao proto :DWORD, :DWORD
Divisao proto :DWORD, :DWORD
.DATA
Titulo_Janela db "Calculadora em ASM", 0
Classe_Janela db "Form1",0
Edit_Classe db "Edit", 0
Botao_Classe db "Button", 0
Botao1_Titulo db "1",0
Botao2_Titulo db "2", 0
Botao3_Titulo db "3", 0
Botao4_Titulo db "4", 0
Botao5_Titulo db "5", 0
Botao6_Titulo db "6", 0
Botao7_Titulo db "7", 0
Botao8_Titulo db "8", 0
Botao9_Titulo db "9", 0
Botao0_Titulo db "0", 0
BotaoVirgulaTitulo db ",", 0
BotaoIgualTitulo db "=", 0
BotaoMaisTitulo db "+", 0
BotaoMenosTitulo db "-", 0
BotaoMultiplicarTitulo db "*", 0
BotaoDividirTitulo db "/", 0
BotaoCETitulo db "CE", 0
BotaoCTitulo db "C", 0
Texto_Msg db "É impossível dividir por zero!", 0
Titulo_Msg db "Aviso!", 0
.DATA?
Handle_Janela DWORD ?
LinhaComando DWORD ?
EditHandle DWORD ?
Botao1_Handle DWORD ?
Botao2Handle DWORD ?
Botao3Handle DWORD ?
Botao4Handle DWORD ?
Botao5Handle DWORD ?
Botao6Handle DWORD ?
Botao7Handle DWORD ?
Botao8Handle DWORD ?
Botao9Handle DWORD ?
Botao0Handle DWORD ?
BotaoVirgulaHandle DWORD ?
BotaoMaisHandle DWORD ?
BotaoMenosHandle DWORD ?
BotaoMultiplicarHandle DWORD ?
BotaoDividirHandle DWORD ?
BotaoIgualHandle DWORD ?
BotaoCEHandle DWORD ?
BotaoCHandle DWORD ?
Edit_Texto db 100 dup (?)
Endereco_String DWORD ?
Operacao DWORD ?
Valor1 DWORD ?
Valor2 DWORD ?
Resultado DWORD ?
.CODE
Inicio:
Invoke GetModuleHandle, NULL
Mov Handle_Janela, eax
Invoke GetCommandLine
Mov LinhaComando, eax
invoke Gerenciador_Janela, Handle_Janela, NULL, LinhaComando, SW_SHOWDEFAULT
Invoke ExitProcess, 0
Gerenciador_Janela proc hInstance:DWORD, hInstAntiga:DWORD, LnComando:DWORD, Tipo_Janela:DWORD
LOCAL wnd: WNDCLASSEX
LOCAL Janela: HWND
LOCAL Mensagem: MSG
mov wnd.cbSize, SIZEOF WNDCLASSEX
mov wnd.style, CS_HREDRAW or CS_VREDRAW
mov wnd.lpfnWndProc, offset GerenteMensagem
mov wnd.cbClsExtra, NULL
mov wnd.cbWndExtra, NULL
push hInstance
pop wnd.hInstance
invoke LoadIcon, NULL, IDI_WINLOGO
mov wnd.hIcon, eax
mov wnd.hIconSm, eax
invoke LoadCursor, NULL, IDC_ARROW
mov wnd.hCursor, eax
mov wnd.hbrBackground, COLOR_BTNFACE+1
mov wnd.lpszMenuName, NULL
mov wnd.lpszClassName, OFFSET Classe_Janela
invoke RegisterClassEx, ADDR wnd
invoke CreateWindowEx, NULL, ADDR Classe_Janela, ADDR Titulo_Janela,WS_OVERLAPPEDWINDOW, 433, 302, 230,190, NULL, NULL, hInstance, NULL
mov Janela, eax
invoke ShowWindow, Janela, SW_SHOWNORMAL
invoke UpdateWindow, Janela
.WHILE TRUE
invoke GetMessage, ADDR Mensagem, NULL, 0,0
.BREAK .IF (eax < 1)
invoke TranslateMessage, ADDR Mensagem
invoke DispatchMessage, ADDR Mensagem
.ENDW
mov eax, Mensagem.wParam
ret
Gerenciador_Janela endp
GerenteMensagem proc hWnd: DWORD, uMsg: UINT, wParam: WPARAM, lParam: LPARAM
.IF uMsg==WM_DESTROY
Invoke PostQuitMessage, NULL
.ELSEIF uMsg == WM_CREATE
invoke CreateWindowEx, NULL, ADDR Edit_Classe, NULL, WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT or ES_AUTOHSCROLL, 8, 8, 193, 21, hWnd, NULL, Handle_Janela, NULL
mov EditHandle, eax
Invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR Botao1_Titulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 8, 32, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov Botao1_Handle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR Botao2_Titulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 48, 32, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov Botao2Handle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR Botao3_Titulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 88, 32, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov Botao3Handle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR Botao4_Titulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 8, 64, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov Botao4Handle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR Botao5_Titulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 48, 64, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov Botao5Handle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR Botao6_Titulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 88, 64, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov Botao6Handle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR Botao7_Titulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 8, 96, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov Botao7Handle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR Botao8_Titulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 48, 96, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov Botao8Handle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR Botao9_Titulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 88, 96, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov Botao9Handle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR Botao0_Titulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 8, 128, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov Botao0Handle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR BotaoVirgulaTitulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 48, 128, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov BotaoVirgulaHandle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR BotaoIgualTitulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 88, 128, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov BotaoIgualHandle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR BotaoMaisTitulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 128, 32, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov BotaoMaisHandle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR BotaoMenosTitulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 128, 64, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov BotaoMenosHandle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR BotaoMultiplicarTitulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 128, 96, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov BotaoMultiplicarHandle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR BotaoDividirTitulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 128, 128, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov BotaoDividirHandle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR BotaoCETitulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 168, 32, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov BotaoCEHandle, eax
invoke CreateWindowEx, NULL, ADDR Botao_Classe, ADDR BotaoCTitulo, WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON, 168, 64, 33, 25, hWnd, NULL, Handle_Janela, NULL
mov BotaoCHandle, eax
.ELSEIF uMsg==WM_COMMAND
mov edx, lParam
.if edx == Botao1_Handle
Invoke GetWindowText, EditHandle, ADDR Edit_Texto, 100
Invoke lstrcat, ADDR Edit_Texto, ADDR Botao1_Titulo
Mov Endereco_String, eax
Invoke SetWindowText, EditHandle, [Endereco_String]
.endif
.if edx == Botao2Handle
Invoke GetWindowText, EditHandle, ADDR Edit_Texto, 100
Invoke lstrcat, ADDR Edit_Texto, ADDR Botao2_Titulo
Mov Endereco_String, eax
Invoke SetWindowText, EditHandle, [Endereco_String]
.endif
.if edx == Botao3Handle
Invoke GetWindowText, EditHandle, ADDR Edit_Texto, 100
Invoke lstrcat, ADDR Edit_Texto, ADDR Botao3_Titulo
Mov Endereco_String, eax
Invoke SetWindowText, EditHandle, [Endereco_String]
.endif
.if edx == Botao4Handle
Invoke GetWindowText, EditHandle, ADDR Edit_Texto, 100
Invoke lstrcat, ADDR Edit_Texto, ADDR Botao4_Titulo
Mov Endereco_String, eax
Invoke SetWindowText, EditHandle, [Endereco_String]
.endif
.if edx == Botao5Handle
Invoke GetWindowText, EditHandle, ADDR Edit_Texto, 100
Invoke lstrcat, ADDR Edit_Texto, ADDR Botao5_Titulo
Mov Endereco_String, eax
Invoke SetWindowText, EditHandle, [Endereco_String]
.endif
.if edx == Botao6Handle
Invoke GetWindowText, EditHandle, ADDR Edit_Texto, 100
Invoke lstrcat, ADDR Edit_Texto, ADDR Botao6_Titulo
Mov Endereco_String, eax
Invoke SetWindowText, EditHandle, [Endereco_String]
.endif
.if edx == Botao7Handle
Invoke GetWindowText, EditHandle, ADDR Edit_Texto, 100
Invoke lstrcat, ADDR Edit_Texto, ADDR Botao7_Titulo
Mov Endereco_String, eax
Invoke SetWindowText, EditHandle, [Endereco_String]
.endif
.if edx == Botao8Handle
Invoke GetWindowText, EditHandle, ADDR Edit_Texto, 100
Invoke lstrcat, ADDR Edit_Texto, ADDR Botao8_Titulo
Mov Endereco_String, eax
Invoke SetWindowText, EditHandle, [Endereco_String]
.endif
.if edx == Botao9Handle
Invoke GetWindowText, EditHandle, ADDR Edit_Texto, 100
Invoke lstrcat, ADDR Edit_Texto, ADDR Botao9_Titulo
Mov Endereco_String, eax
Invoke SetWindowText, EditHandle, [Endereco_String]
.endif
.if edx == Botao0Handle
Invoke GetWindowText, EditHandle, ADDR Edit_Texto, 100
Invoke lstrcat, ADDR Edit_Texto, ADDR Botao0_Titulo
Mov Endereco_String, eax
Invoke SetWindowText, EditHandle, [Endereco_String]
.endif
.if edx == BotaoMaisHandle
Mov Operacao, 01h
Invoke GetWindowText, EditHandle, ADDR Edit_Texto, 100
Invoke atodw, ADDR Edit_Texto
Mov Valor1, eax
Invoke SetWindowText, EditHandle, NULL
.endif
.if edx == BotaoMenosHandle
Mov Operacao, 02h
Invoke GetWindowText, EditHandle, ADDR Edit_Texto, 100
Invoke atodw, ADDR Edit_Texto
Mov Valor1, eax
Invoke SetWindowText, EditHandle, NULL
.endif
.if edx == BotaoMultiplicarHandle
Mov Operacao, 03h
Invoke GetWindowText, EditHandle, ADDR Edit_Texto, 100
Invoke atodw, ADDR Edit_Texto
Mov Valor1, eax
Invoke SetWindowText, EditHandle, NULL
.endif
.if edx == BotaoDividirHandle
Mov Operacao, 04h
Invoke GetWindowText, EditHandle, ADDR Edit_Texto, 100
Invoke atodw, ADDR Edit_Texto
Mov Valor1, eax ;;Movendo o valor convertido para a variável Valor1
Invoke SetWindowText, EditHandle, NULL
.endif
.if edx == BotaoIgualHandle
invoke GetWindowText, EditHandle, ADDR Edit_Texto, 100
invoke atodw, ADDR Edit_Texto
mov Valor2, eax
.if Operacao == 01h
Invoke Soma, Valor1, Valor2
.elseif Operacao == 02h
Invoke Subtracao, Valor1, Valor2
.elseif Operacao == 03h
Invoke Multiplicacao, Valor1, Valor2
.elseif Operacao == 04h
.if Valor2 == 0
invoke MessageBox, hWnd, ADDR Texto_Msg, ADDR Titulo_Msg, MB_OK + MB_ICONEXCLAMATION
invoke SetWindowText, EditHandle, NULL
.elseif
Invoke Divisao, Valor1, Valor2
.endif
.endif
.endif
.ELSE
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
.ENDIF
xor eax, eax
ret
GerenteMensagem endp
Soma proc Val1: DWORD, Val2: DWORD
Push eax
Push ebx
Xor eax, eax
Xor ebx, ebx
Mov eax, Val1
Mov ebx, Val2
Add eax, ebx
Mov Resultado, eax
Invoke dwtoa, Resultado, ADDR Edit_Texto
Invoke SetWindowText, EditHandle, ADDR Edit_Texto
Xor eax, eax
Mov Valor1, eax
Mov Valor2, eax
Mov Resultado, eax
Pop ebx
Pop eax
ret
Soma endp
Subtracao proc Val1: DWORD, Val2: DWORD
Push eax
Push ebx
Xor eax, eax
Xor ebx, ebx
;;Zerando os valores dos dois registradores para receberem os valores Val1 e Val2
Mov eax, Val1
Mov ebx, Val2
;; Agora fazendo a soma entre os dois
SUB eax, ebx
Mov Resultado, eax
Invoke dwtoa, Resultado, ADDR Edit_Texto
Invoke SetWindowText, EditHandle, ADDR Edit_Texto
Xor eax, eax
Mov Valor1, eax
Mov Valor2, eax
Mov Resultado, eax
Pop ebx
Pop eax
ret
Subtracao endp
Multiplicacao proc Val1: DWORD, Val2: DWORD
Push eax
Push ebx
Xor eax, eax
Xor ebx, ebx
Mov eax, Val1
Mov ebx, Val2
MUL ebx
Mov Resultado, eax
Invoke dwtoa, Resultado, ADDR Edit_Texto
Invoke SetWindowText, EditHandle, ADDR Edit_Texto
Xor eax, eax
Mov Valor1, eax
Mov Valor2, eax
Mov Resultado, eax
Pop ebx
Pop eax
ret
Multiplicacao endp
Divisao proc Val1: DWORD, Val2: DWORD
push eax
push ebx
xor eax, eax
xor ebx, ebx
mov eax, Val1
mov ebx, Val2
Div ebx
mov Resultado, eax
invoke dwtoa, Resultado, ADDR Edit_Texto
invoke SetWindowText, EditHandle, ADDR Edit_Texto
xor eax, eax
mov Valor1, eax
mov Valor2, eax
mov Resultado, eax
pop ebx
pop eax
ret
Divisao endp
end Inicio
Contato com o autor
O endereço de e-mail address está sendo protegido de spambots. Você precisa ativar o JavaScript enabled para vê-lo.
O endereço de e-mail address está sendo protegido de spambots. Você precisa ativar o JavaScript enabled para vê-lo.
mfxbroker.comзаказ домаооо полигон плюсподключение ноутбукаказан алюминиевыйникос ресторан харьков лобановский александр