Informática Numaboa - Tutoriais e Programação

Criando uma mini-calculadora em Assembly para Win32

Qua

14

Jan

2009


20:06

  • Imprimir
(11 votos, média 4.45 de 5) 


Nível Intermediário

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

Mini-calculadora

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 smile

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 smile

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 smile 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 smile

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заказ домаооо полигон плюсподключение ноутбукаказан алюминиевыйникос ресторан харьков лобановский александр