Программа:
"Таблица умножения" MASM32
Итак, рассказывать, так подробно. Согласен:
строчками комментария не все можно объяснить, но
пытаться стоит.
; Это рекоментация об использовании набора
инструкций процессора
; Если не учитывать MULTIMEDIA, то 586 от 386 недалеко ушел
.386
; Модель памяти плоская, без
сегментации,
; а вызов подпрограмм стандартный. - А что и нестандартный
бывает?
; Здесь, похоже нужно подробней рассказывать:вызовы
подпрограмм и стек
.model flat,stdcall
; Учет разницы иежду символами
верхнего и нижнего регистра отключаем
option casemap:none
; Объявляем прототип нашей главной
функции, чтобы при ее
; вызове можно было свериться с типами передаваемых ей параметров.
WinMain proto :DWORD,:DWORD,:DWORD,:DWORD
; Даем возможность нашей программе
воспользоваться багажем
; наработок, который нам будет необходим.
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
; Инициализированные данные.
; Переменные, которым мы сразу же присваиваем значения.
; Названия и присваиваемые значения говорят сами за себя
.data
ClassName db "SimpleWinClass",0
AppName db "Таблица умножения",0
ButtonClassName db "button",0
ButtonText db "^ Введи сомножитель и нажми кнопку ",0
EditClassName db "edit",0
smn2 db 1
Des dw 10 ; Константа для получения количества десятков в числе.
mnn dw 0
; Нижеприведенный блок - это заготовка
нашей таблицы в памяти
; В ассемблере, как нигде, все отдано на откуп программисту.
; Можно было этот блок задать пустым и формировать ее динамически.
; Можно было сразу рисовать результат на экране.
; Я решил это сделать так.
blok1 db " x 1 = ",0Dh,0Ah
db " x 2 = ",0Dh,0Ah
db " x 3 = ",0Dh,0Ah
db " x 4 = ",0Dh,0Ah
db " x 5 = ",0Dh,0Ah
db " x 6 = ",0Dh,0Ah
db " x 7 = ",0Dh,0Ah
db " x 8 = ",0Dh,0Ah
db " x 9 = ",0Dh,0Ah
db" x 10 = ",0Dh,0Ah,0
; Неинициализированные
данные
.data?
; Место для введенного сомножителя
smn1 db 1 dup(?)
; Указатель на наше приложение
hInstance HINSTANCE ?
; Командная строка нашей запущеной
программы
CommandLine LPSTR ?
; Указатели на кнопки и поля ввода
вывода
hwndButton HWND ?
hwndEdit HWND ?
hwndVivod HWND ?
; константы
.const
; условные номера присвоенные
элементам нашей программы
ButtonID equ 1
EditID equ 2
VivodID equ 2
IDM_HELLO equ 1
IDM_CLEAR equ 2
IDM_GETTEXT equ 3
IDM_EXIT equ 4
; исполняемый код, начало программы
.code
start:
; Получение указателя на нашу
программу.
; В ассемблере за нас никто это делать не будет
invoke GetModuleHandle, NULL
mov hInstance,eax
; Это просто для демонстрации. Мы ее
параметрами пользоваться не будем
invoke GetCommandLine
; Запустить нашу главную процедуру, а
фактически сформировать окно программы
invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
; А если ей больше
нечего делать, то закончить
работу программы
invoke ExitProcess,eax
; Вот она главная процедура нашего
приложения
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
; Локальные переменные
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
; А теперь заполнение структуру нашего
окна перед регистрацией
mov wc.cbSize,SIZEOF WNDCLASSEX
mov wc.style, CS_HREDRAW or CS_VREDRAW
mov wc.lpfnWndProc, OFFSET WndProc
mov wc.cbClsExtra,NULL
mov wc.cbWndExtra,NULL
push hInst
pop wc.hInstance
mov wc.hbrBackground,COLOR_BTNFACE+1
mov wc.lpszClassName,OFFSET ClassName
; Загрузим иконку нашего приложения,
куда ж без нее
invoke LoadIcon,hInst,500 ; ID - Иконки
mov wc.hIcon, eax
mov wc.hIconSm, eax
; Загрузим курсор: можно какой захотим
invoke LoadCursor,NULL,IDC_ARROW
mov wc.hCursor,eax
; Регистрируем класс окна
invoke RegisterClassEx, addr wc
; Будем думать, что все прошло
нормально, и поэтому создаем окно нашей программы
INVOKE CreateWindowEx,WS_EX_CLIENTEDGE,ADDR ClassName,ADDR AppName,\
WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\
CW_USEDEFAULT,320,400,NULL,NULL,\
hInst,NULL
; Забрасываем указатель на него в
переменную
mov hwnd,eax
; Показываем его
INVOKE ShowWindow, hwnd,SW_SHOWNORMAL
; На всякий случай обновляем
INVOKE UpdateWindow, hwnd
; Организуем цикл приема сообщений
.WHILE TRUE
INVOKE GetMessage, ADDR msg,NULL,0,0
.BREAK .IF (!eax)
INVOKE TranslateMessage, ADDR msg
INVOKE DispatchMessage, ADDR msg
.ENDW
; Прощальный привет
операционной системе в виде указателя на наше окно
mov eax,msg.wParam
; А теперь, просто
вернемся туда, откуда нас вызывли
ret
; Главная процедура закончилась
WinMain endp
; А это процедура, которая
обрабатывает сообщения пришедшие для нашего окна
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
; Это сообщение об окончании работы
программы
.IF uMsg==WM_DESTROY
; Посылаем нашей программе сообщение о
необходимости
; окончания работы
invoke PostQuitMessage,NULL
; Пришло сообщение о создании нашего
окна
.ELSEIF uMsg==WM_CREATE
; Создаем многострочное окно для
вывода
INVOKE CreateWindowEx,WS_EX_CLIENTEDGE,ADDR EditClassName,NULL,\
WS_CHILD or WS_VISIBLE or ES_AUTOHSCROLL or ES_MULTILINE ,\
10,100,280,250,hWnd,VivodID,\
hInstance,NULL
mov hwndVivod,eax
; Создаем однострочное окно для ввода
invoke CreateWindowEx,WS_EX_CLIENTEDGE, ADDR EditClassName,NULL,\
WS_CHILD or WS_VISIBLE or WS_BORDER or ES_LEFT or\
ES_AUTOHSCROLL,\
10,10,50,25,hWnd,EditID,hInstance,NULL
mov hwndEdit,eax
invoke SetFocus, hwndEdit
; Создаем кнопку для выполнения
расчетов и выхода
invoke CreateWindowEx,NULL, ADDR ButtonClassName,ADDR ButtonText,\
WS_CHILD or WS_VISIBLE or BS_DEFPUSHBUTTON,\
10,55,280,25,hWnd,ButtonID,hInstance,NULL
mov hwndButton,eax
; А вот пришло сообщение о какой то
команде
; А она у нас одна выполнить расчет и вывести таблицу
.ELSEIF uMsg==WM_COMMAND
mov eax,wParam
.IF lParam==0
; Обратите внимание, что это реакция
на сообщение о получении текста
; А послало его нам нажатие на нашу кнопку.
.IF ax==IDM_GETTEXT
; Получаем первый сомножитель из поля
ввода
invoke GetWindowText,hwndEdit,ADDR smn1,2
; Мы собираемся воспользоваться
регистром, поэтому сохраним его в стеке
push edi
; Укажем в нем начало нашего блока для
помещения туда нашего сомножителя
mov edi,offset blok1 + 1
; Загоним сомножитель в регистр al
mov al, smn1
; Переместим в блок-заготовку
mov byte ptr [edi], al
; В регистр счетчика цикла поместим 9
- остаток потребности заполнения
mov ecx, 9
; Добавим к EDI 15 - следующую позицию
вывода
addsmn: add edi,15
; Поместим в нужное место тот же
заданный сомножитель из al
mov byte ptr [edi], al
; Будем крутить цикл согласно
заданному значению счетчика
loop addsmn
; Помня, что в al у нас первый
сомножитель в виде символа
; Вычтем из него "0", чтобы получился сомножитель - число
sub al, '0'
; Теперь уже в переменную smn1
поместим его в виде числа
mov smn1, al
; Настроим вывод в нужное место нашего
блока-заготовки
mov edi,offset blok1+11
; Готовим в al значение сомножителя,
хотя в данный момент
; он там и находится, это потребуется нам для цикла
nexchf: mov al,smn1
; Производим перемножение
mul smn2
; Все дальнейшие ухищрения связаны с
организацией вывода
; результата перемножения в соответствующее место блока-заготовки.
; Нюансы связаны с учетом двузначности или однозначности результата,
; а затем отдельного определения цифры десятков и цифры единиц.
mov cx,0
NexDiv: mov dx,0
div Des
push dx
inc cx
cmp ax,0
jne NexDiv
mov mnn, cx
NexSym: pop dx
; Полученная цифра призведения
переводится в символ перед
; выводом
add dl,30h
mov byte ptr [edi], dl
inc edi
loop NexSym
cmp mnn, 1
je onchar
;Если результат односимвольный то
убираем лишнюю позицию
dec edi
onchar: add edi,14
inc smn2
cmp smn2,10
jna nexchf
; Возвращаем из стека использованный
нами регистр
pop edi
; На всякий случай возвращаем второму
сомножителю 1
mov smn2,1
; Блок заготовку помещаем в окно
вывода результата
invoke SetWindowText,hwndVivod,ADDR blok1
; И еще, для демонстрации в окно
вывода сообщений
invoke MessageBox,NULL,ADDR blok1,ADDR AppName,MB_OK
; Очистим на всякий случай
блок-заготовку,
; если потребуется выполнить расчет на другую цифру
push edi
mov ecx,9
mov edi,offset blok1+11
chist: mov byte ptr [edi], ' '
inc edi
mov byte ptr [edi], ' '
dec edi
add edi,15
loop chist
pop edi
.ELSE
; В этой секции кроме сообщения на
получение текста из поля ввода
; Может присутствовать и другое: на выход из программы.
; В этом случае мы должны разрушить наше окно
invoke DestroyWindow,hWnd
.ENDIF
.ELSE
; А в этой секции смотрим: сообщение
от кнопки.
.IF ax==ButtonID
; Сдвинув в EAX биты вправо на 16
позиций, смотрим,
; А не клик ли это нашей кнопки.
shr eax,16
; Если да, то посылаем сообщение нашей
программе,
; чтобы она доставала текст из текстового поля со всеми
; вытекающими из этого последствиями.
.IF ax==BN_CLICKED
invoke SendMessage,hWnd,WM_COMMAND,IDM_GETTEXT,0
.ENDIF
.ENDIF
.ENDIF
.ELSE
; Передача всех "ненаших" сообщений
для обработки функции по умолчанию.
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
; Возврат из процедуры после обработки
сообщения
ret
; Если прием сообщений закончен, то
обнулить EAX и тоже выйти
.ENDIF
xor eax,eax
ret
; Конец оконной процедуры
WndProc endp
; Конец всей программы
end start
; При наличии некоторого количества
специальных для ассемблера моментов
; общяя структура программы для Windows осталась прежней.
Другая информатика. (C)
Публикация на draginf.ru.
При использовании материалов ссылка на сайт обязательна. |