Powrót do strony wyboru lekcji

Programowanie okien 4

Operacje matematyczne wykonywane przy pomocy okien



Często w programach zachodzi potrzeba konwersji zmiennych tekstowych na liczby. Dlatego też odpowiednie stosowanie procedur pozwalających na zamianę ciągu znaków na liczbę i na odwrót jest ważnym aspektem w programowaniu w Windows.

Przykład

Tworzymy program sumujący 2 liczby podane w 2 kontrolkach typu EDIT i wyświetlający wynik w 3 kontrolce. Na początku definiujemy standardowo typ instrukcji procesora, określamy model pamięci jako płaski z wywołaniem procedur typu STDCALL. Dołączamy także odpowiednie biblioteki: user32.lib i kernel32.lib:


.586 ;typ instrukcji procesora

.MODEL FLAT,STDCALL

includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib

Następnie definiujemy stałe określające kontrolki wykorzystywane w programie:


IDC_EDT1      EQU   100
IDC_EDT2      EQU   101
IDC_EDT3      EQU   103
IDC_BTN1      EQU   106

Następnie definiujemy wszystkie stałe potrzebne do poprawnego funkcjonowania programu:


WM_SETICON          EQU   80h
WM_COMMAND          EQU   111h
WM_SETTEXT          EQU   0Ch
WM_GETTEXT          EQU   0Dh
WM_CLOSE            EQU   10h
WM_INITDIALOG       EQU   110h

Funkcje Api, z których zamierzamy skorzystać:


EXTERN lstrlenA@4:NEAR

EXTERN SendMessageA@16:NEAR

EXTERN SendDlgItemMessageA@20:NEAR

EXTERN EndDialog@8:NEAR

EXTERN GetModuleHandleA@4:NEAR

EXTERN DialogBoxParamA@20:NEAR

EXTERN ExitProcess@4:NEAR

EXTERN LoadIconA@8:NEAR

Sekcja danych programu wygląda następująco:


.DATA
HINST         DD    ?
BUFF          DB    100  DUP (0)
BUFF2         DB    100  DUP (0)
PA            DB   'IDD_DLG1',   0 ;nazwa dialogu
DLG           DD     0
LICZBA1       DD     0

BUFF to bufor przechowujący cyfry wprowadzonej liczby w postaci kolejnych znaków. BUFF2 to cyfry liczby którą otrzymamy w wyniku. DLG to długość liczby, biorąc pod uwagę ilość znaków. LICZBA1 przechowuje odczytaną liczbę z postaci znakowej. Początek sekcji kodu to standardowe uruchomienie programu korzystającego z okna dialogowego i zdefiniowanie procedury obsługi okna:


.CODE

START:

PUSH  0
CALL  GetModuleHandleA@4
MOV  [HINST],EAX

PUSH  0
PUSH  OFFSET  WNDPROC
PUSH  0
PUSH  OFFSET  PA
PUSH  [HINST]
CALL  DialogBoxParamA@20

CMP  EAX,-1
JNE KOL

KOL:
PUSH  0
CALL  ExitProcess@4

;parametry:
;[EBP +14H] LPARAM
;[EBP +10H] WPARAM
;[EBP +0CH] MES
;[EBP +8] HWND

WNDPROC   PROC


PUSH  EBP
MOV  EBP,ESP
PUSH  EBX
PUSH  ESI
PUSH  EDI

CMP  DWORD  PTR  [EBP + 0CH],WM_CLOSE;komunikat związany ze zniszczeniem okna
JNE  OMIN

PUSH  0
PUSH  DWORD  PTR  [EBP+ 08H]
CALL  EndDialog@8
JMP  FINISH

OMIN:

CMP  DWORD  PTR  [EBP + 0CH],WM_COMMAND;komunikat związany z kontrolkami
JNE  OMINCOMMAND

W sekcji obsługi zdarzeń kontrolek definiujemy reakcje na przycisk wynik, umieszczony w edytorze zasobów:


CMP  WORD  PTR  [EBP + 10H],IDC_BTN1
JNE  OMINCOMMAND

PUSH  OFFSET  BUFF
PUSH  100
PUSH  WM_GETTEXT
PUSH  IDC_EDT1
PUSH  DWORD  PTR  [EBP + 08H]
CALL  SendDlgItemMessageA@20

PUSH  OFFSET  BUFF
CALL  lstrlenA@4

MOV  DLG,EAX

XOR  EAX,EAX

CALL  KONWERSJA

Funkcją SetDlgItemMessageA@12 pobieramy tekst z pierwszej kontrolki. Za pomocą funkcji lstrlenA@4 odczytujemy ilość znaków wprowadzonej liczby. Następnie ilość tą kopiujemy do zmiennej DLG, czyścimy rejestr EAX i wywołujemy procedurę KONWERSJA, która konwertuje podaną przez nas liczbę z postaci znakowej i dodaje do zmiennej LICZBA1 . Procedura KONWERSJA wygląda następująco:


KONWERSJA PROC

PUSHAD;wrzucamy wszystkie rejestry 32-bitowe na stos

MOV  ECX,DLG   ;ilość cyfr kopiujemy do ECX

MOV  EBX,10; rejestr potrzebny do zamiany liczby na postać dziesiętną

MOV  ESI,ECX

MOV  EDI,0; rejestr informujący o liczbie mnożeń przez 10
CMP  ESI,0; czy mamy jakieś cyfry jak nie to koniec

JE   NIEOMIJAJ

DEC  ESI

PETLADOD:

MOV  EAX,DWORD  PTR  [BUFF + ESI] ; cyfra pobierana z bufora
AND  EAX,000000FFh; zakładamy maskę na 8 najmłodszych bitów
SUB  EAX,30h; z postaci znakowej przechodzimy do konkretnej cyfry odejmując 30 h

PUSH  ECX ; pętla zagnieżdżona -ECX wędruje na stos

CMP  EDI,0 ; jeżeli to pierwsza cyfra liczby to nie mnożymy przez 10
JE   NIEMNOZ

MOV  ECX,EDI

; mnożymy odpowiednią ilość razy 10
PETLAMNOZ:
MUL   EBX
LOOP   PETLAMNOZ

NIEMNOZ:

INC  EDI

POP  ECX

ADD  LICZBA1,EAX; otrzymaną liczbę dodajemy do LICZBA1
JC   OMINKONWERSJE

XOR  EAX,EAX

DEC  ESI
LOOP   PETLADOD

OMINKONWERSJE:

NIEOMIJAJ:

POPAD;ściągamy wszystkie rejestry 32-bitowe ze stosu

RET

KONWERSJA ENDP

Następnie czyścimy bufor na drugą liczbę funkcją CZYSC :


CALL  CZYSC

Definicja funkcji CZYSC wygląda następująco:


CZYSC PROC

PUSHAD;wrzucamy wszystkie rejestry 32-bitowe na stos

MOV  ECX,100; nasz bufor zawiera 100 elementów, więc tyle trzeba wyczyścić w pętli

MOV  DLG,0   ;ilość cyfr będzie na początku 0

MOV  [BUFF],0    ;0 do pierwszej cyfry bufora na liczbę
MOV  [BUFF2],0   ;0 do pierwszej cyfry bufora wynikowego

PETLA:

MOV  [BUFF + ECX],0
MOV  [BUFF2 + ECX],0

LOOP   PETLA

POPAD;ściągamy wszystkie rejestry 32-bitowe ze stosu

RET

CZYSC ENDP

Po wyczyszczeniu bufora można sczytać kolejną liczbę:


PUSH  OFFSET  BUFF
PUSH  100
PUSH  WM_GETTEXT
PUSH  IDC_EDT2
PUSH  DWORD  PTR  [EBP + 08H]
CALL  SendDlgItemMessageA@20

PUSH  OFFSET  BUFF
CALL  lstrlenA@4

MOV  DLG,EAX

XOR  EAX,EAX

CALL  KONWERSJA

CALL  CZYSC

Następnie otrzymaną liczbę wynikową znajdująca się w zmiennej LICZBA1 kopiujemy do rejestru EAX i dekonwertujmy na postać znakową funkcją DEKONWERSJA, tak aby można było ją wyświetlić w 3 kontrolce typu EDIT:


MOV  EAX,LICZBA1

CALL  DEKONWERSJA

Kod funkcji DEKONWERSJA wygląda następująco:


DEKONWERSJA PROC

PUSHAD;wrzucamy wszystkie rejestry 32-bitowe na stos

XOR  ESI,ESI
XOR  EBX,EBX
XOR  EDX,EDX
MOV  EBX,10;tym razem będziemy dzielić przez 10 aby otrzymać odpowiednie cyfry

DZIEL:

DIV   EBX
PUSH  EDX;resztę wrzucamy na stos jest to odpowiednia cyfra liczby
XOR   EDX,EDX
INC   ESI
CMP   EAX,0;jeżeli EAX nie zawiera zera tzn. że należy dalej dzielić przez 10

JNE   DZIEL

MOV  ECX,ESI
XOR  ESI,ESI

DOBUFORA:

POP   EDX;ściągamy resztę w postaci cyfr do bufora
ADD   EDX,30h; przechodzimy do postaci znakowej poprzez dodanie 30h

MOV  DWORD  PTR  [BUFF2 + ESI],EDX; cyfra ładowana do bufora wynikowego
INC   ESI
LOOP  DOBUFORA

POPAD;ściągamy wszystkie rejestry 32-bitowe ze stosu

RET

DEKONWERSJA ENDP

Po dekonwersji możemy ostatecznie wyświetlić nasz wynik oraz wyczyścić zmienną LICZBA1 i bufory dla nowych wartości:


PUSH  OFFSET  BUFF2
PUSH  100
PUSH  WM_SETTEXT
PUSH  IDC_EDT3
PUSH  DWORD  PTR  [EBP + 08H]
CALL  SendDlgItemMessageA@20

CALL  CZYSC

MOV  LICZBA1, 0

OMINCOMMAND:

FINISH:

MOV  EAX,0

POP  EDI
POP  ESI
POP  EBX
POP  EBP
RET
WNDPROC  ENDP

Przykładowe wywołanie programu na Rys.1:


Nie można wyświetlić obrazu
Rys.1. Interfejs programu .


Całość programu przedstawiona jest na poniższym listingu:


.586 ;typ instrukcji procesora

.MODEL FLAT,STDCALL

includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib


IDC_EDT1      EQU   101
IDC_EDT2      EQU   102
IDC_EDT3      EQU   103
IDC_BTN1      EQU   106

WM_SETICON          EQU   80h
WM_COMMAND          EQU   111h
WM_SETTEXT          EQU   0Ch
WM_GETTEXT          EQU   0Dh
WM_CLOSE            EQU   10h
WM_INITDIALOG       EQU   110h

EXTERN lstrlenA@4:NEAR

EXTERN SendMessageA@16:NEAR

EXTERN SendDlgItemMessageA@20:NEAR

EXTERN EndDialog@8:NEAR

EXTERN GetModuleHandleA@4:NEAR

EXTERN DialogBoxParamA@20:NEAR

EXTERN ExitProcess@4:NEAR

EXTERN LoadIconA@8:NEAR

.DATA
HINST         DD    ?
BUFF          DB    100  DUP (0)
BUFF2         DB    100  DUP (0)
PA            DB   'IDD_DLG1',   0 ;nazwa dialogu
DLG           DD     0
LICZBA1       DD     0

.CODE

START:

PUSH  0
CALL  GetModuleHandleA@4
MOV  [HINST],EAX

PUSH  0
PUSH  OFFSET  WNDPROC
PUSH  0
PUSH  OFFSET  PA
PUSH  [HINST]
CALL  DialogBoxParamA@20

CMP  EAX,-1
JNE KOL

KOL:
PUSH  0
CALL  ExitProcess@4

;parametry:
;[EBP +14H] LPARAM
;[EBP +10H] WPARAM
;[EBP +0CH] MES
;[EBP +8] HWND

WNDPROC   PROC


PUSH  EBP
MOV  EBP,ESP
PUSH  EBX
PUSH  ESI
PUSH  EDI

CMP  DWORD  PTR  [EBP + 0CH],WM_CLOSE;komunikat związany ze zniszczeniem okna
JNE  OMIN

PUSH  0
PUSH  DWORD  PTR  [EBP+ 08H]
CALL  EndDialog@8
JMP  FINISH

OMIN:

CMP  DWORD  PTR  [EBP + 0CH],WM_COMMAND;komunikat związany z kontrolkami
JNE  OMINCOMMAND

CMP  WORD  PTR  [EBP + 10H],IDC_BTN1
JNE  OMINCOMMAND

PUSH  OFFSET  BUFF
PUSH  100
PUSH  WM_GETTEXT
PUSH  IDC_EDT1
PUSH  DWORD  PTR  [EBP + 08H]
CALL  SendDlgItemMessageA@20

PUSH  OFFSET  BUFF
CALL  lstrlenA@4

MOV  DLG,EAX

XOR  EAX,EAX

CALL  KONWERSJA

CALL  CZYSC

PUSH  OFFSET  BUFF
PUSH  100
PUSH  WM_GETTEXT
PUSH  IDC_EDT2
PUSH  DWORD  PTR  [EBP + 08H]
CALL  SendDlgItemMessageA@20

PUSH  OFFSET  BUFF
CALL  lstrlenA@4

MOV  DLG,EAX

XOR  EAX,EAX

CALL  KONWERSJA

CALL  CZYSC

MOV  EAX,LICZBA1

CALL  DEKONWERSJA

PUSH  OFFSET  BUFF2
PUSH  100
PUSH  WM_SETTEXT
PUSH  IDC_EDT3
PUSH  DWORD  PTR  [EBP + 08H]
CALL  SendDlgItemMessageA@20

CALL  CZYSC

MOV  LICZBA1, 0

OMINCOMMAND:

FINISH:

MOV  EAX,0

POP  EDI
POP  ESI
POP  EBX
POP  EBP
RET
WNDPROC  ENDP





CZYSC PROC

PUSHAD;wrzucamy wszystkie rejestry 32-bitowe na stos

MOV  ECX,100; nasz bufor zawiera 100 elementów, więc tyle trzeba wyczyścić w pętli

MOV  DLG,0   ;ilość cyfr będzie na początku 0

MOV  [BUFF],0    ;0 do pierwszej cyfry bufora na liczbę
MOV  [BUFF2],0   ;0 do pierwszej cyfry bufora wynikowego

PETLA:

MOV  [BUFF + ECX],0
MOV  [BUFF2 + ECX],0

LOOP   PETLA

POPAD;ściągamy wszystkie rejestry 32-bitowe ze stosu

RET

CZYSC ENDP





KONWERSJA PROC

PUSHAD;wrzucamy wszystkie rejestry 32-bitowe na stos

MOV  ECX,DLG   ;ilość cyfr kopiujemy do ECX

MOV  EBX,10; rejestr potrzebny do zamiany liczby na postać dziesiętną

MOV  ESI,ECX

MOV  EDI,0; rejestr informujący o liczbie mnożeń przez 10
CMP  ESI,0; czy mamy jakieś cyfry jak nie to koniec

JE   NIEOMIJAJ

DEC  ESI

PETLADOD:

MOV  EAX,DWORD  PTR  [BUFF + ESI]; cyfra pobierana z bufora
AND  EAX,000000FFh; zakładamy maskę na 8 najmłodszych bitów
SUB  EAX,30h; z postaci znakowej przechodzimy do konkretnej cyfry odejmując 30 h

PUSH  ECX; pętla zagnieżdżona -ECX wędruje na stos

CMP  EDI,0; jeżeli to pierwsza cyfra liczby to nie mnożymy przez 10
JE   NIEMNOZ

MOV  ECX,EDI

; mnożymy odpowiednią ilość razy 10
PETLAMNOZ:
MUL   EBX
LOOP   PETLAMNOZ

NIEMNOZ:

INC  EDI

POP  ECX

ADD  LICZBA1,EAX; otrzymaną liczbę dodajemy do LICZBA1
JC   OMINKONWERSJE

XOR  EAX,EAX

DEC  ESI
LOOP   PETLADOD

OMINKONWERSJE:

NIEOMIJAJ:

POPAD;ściągamy wszystkie rejestry 32-bitowe ze stosu

RET

KONWERSJA ENDP





DEKONWERSJA PROC

PUSHAD;wrzucamy wszystkie rejestry 32-bitowe na stos

XOR  ESI,ESI
XOR  EBX,EBX
XOR  EDX,EDX
MOV  EBX,10;tym razem będziemy dzielić przez 10 aby otrzymać odpowiednie cyfry

DZIEL:

DIV   EBX
PUSH  EDX;resztę wrzucamy na stos jest to odpowiednia cyfra liczby
XOR   EDX,EDX
INC   ESI
CMP   EAX,0;jeżeli EAX nie zawiera zera tzn. że należy dalej dzielić przez 10

JNE   DZIEL

MOV  ECX,ESI
XOR  ESI,ESI

DOBUFORA:

POP   EDX;ściągamy resztę w postaci cyfr do bufora
ADD   EDX,30h; przechodzimy do postaci znakowej poprzez dodanie 30h

MOV  DWORD  PTR  [BUFF2 + ESI],EDX; cyfra ładowana do bufora wynikowego
INC   ESI
LOOP  DOBUFORA

POPAD;ściągamy wszystkie rejestry 32-bitowe ze stosu

RET

DEKONWERSJA ENDP

END Start

Pobierz kod

Plik zasobów programu wygląda następująco:


#include "..\include\resource.h"

#define IDC_EDT1 101
#define IDC_EDT2 102
#define IDC_EDT3 103
#define IDC_STC1 104
#define IDC_STC2 105
#define IDC_BTN1 106

IDD_DLG1 DIALOGEX 10,10,249,166
CAPTION "Dodawanie"
FONT 8,"MS Sans Serif", 0, 0, 0
STYLE  WS_VISIBLE|WS_OVERLAPPEDWINDOW

BEGIN
CONTROL "",IDC_EDT1,"Edit",WS_CHILD|WS_VISIBLE|WS_TABSTOP,30,51,54,15,WS_EX_CLIENTEDGE
CONTROL "",IDC_EDT2,"Edit",WS_CHILD|WS_VISIBLE|WS_TABSTOP,108,51,54,15,WS_EX_CLIENTEDGE
CONTROL "",IDC_EDT3,"Edit",WS_CHILD|WS_VISIBLE|WS_TABSTOP,183,51,54,15,WS_EX_CLIENTEDGE

CONTROL "=",IDC_STC1,"Static",WS_CHILD|WS_VISIBLE,168,54,12,9

CONTROL "+",IDC_STC2,"Static",WS_CHILD|WS_VISIBLE,93,54,12,9

CONTROL "Wynik",IDC_BTN1,"Button",WS_CHILD|WS_VISIBLE|WS_TABSTOP,183,69,54,15
END

Pobierz kod

Ćwiczenie 6

Należy zaprogramować kalkulator, o wyglądzie przedstawionym na rysunku poniżej - Rys.2. Kalkulator ma realizować podstawowe działania arytmetyczne: dodawanie, odejmowanie, mnożenie i dzielenie na liczbach dziesiętnych, naturalnych mieszczących się na 32-bitach. W przypadku liczby większej niż 32 bitowa, bądź mniejszej od zera kalkulator ma wyświetlić w polu edycyjnym informacje o zbyt dużej bądź zbyt małej podanej/otrzymanej liczbie. Kalkulator ma mieć możliwość realizacji sekwencji kilku działań pod rząd np. wciśnięcie sekwencji klawiszy 2, +, 5, +, 6 ,*, 7,= ma dać w wyniku liczbę 49. Pole edycyjne powinno być wypełniane od prawej strony tak jak przedstawione jest to na rysunku poniżej i zabezpieczone przed wpisywaniem przez użytkownika czegokolwiek. Pole służy jedynie w celu wyświetlania odpowiednich operacji związanych z przyciskami kalkulatora. Po wciśnięciu przycisku C "pamięć" kalkulatora jest kasowana. Należy także zabezpieczyć kalkulator przed dzieleniem przez 0.

Pobierz szablon

Nie można wyświetlić obrazu
Rys.2. Interfejs kalkulatora.

<Poprzednia lekcjaQuiz>