Назад На главную Вперед

         Программа: "Таблица умножения" 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. При использовании материалов ссылка на сайт обязательна.