21:01
Обновить
Немного Ассемблера - Форум
| RSS



[ Новые сообщения · Участники · Правила форума · Поиск · RSS ]
  • Страница 1 из 1
  • 1
Модератор форума: aka_kludge, LeadyTOR  
Немного Ассемблера
aka_kludgeДата: Вторник, 22.04.2008, 23:38 | Сообщение # 1
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Программирование COM портов
 
aka_kludgeДата: Вторник, 22.04.2008, 23:40 | Сообщение # 2
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Порт 3F8h

Этот порт соответствует регистру передавемых данных. Для передачи в порт 3F8h необходимо записать байт передаваемых данных. После приема данных от внешнего устройства они могут быть прочитаны из этого порта. В зависимости от состояния бита управляющего слова, выводимого в управ- ляющий регистр с адресом 3F8h, назначение порта 3F8h изменяться. Если этот бит равен 0, порт используется для записи передаваемых данных.Если же этот бит равен 1, порт используется для вывода значения младшего байта делителя частоты тактового генератора. Изменяя содержимое делителя, можно изменять скорость передачи данных. Старший байт делителя записывается в порт 3F9h. Зависимост скорости передачи данных от значения делителя частоты приведины в таблице 1:

Таблица 1.

Делитель Скорость передчи в бодах. Делитель Скорость передчи в бодах.
1040 110 24 4800
768 150 12 9600
384 300 6 19200
192 600 3 38400
96 1200 2 57600
48 2400 1 115200

 
aka_kludgeДата: Вторник, 22.04.2008, 23:42 | Сообщение # 3
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Порт 3F9h.

Порт используется как регистр управления прерываниями от асинхронного адаптера или (после вывода в порт 3F9h байта с установленым в 1 стар- шим битом) для вывода значения старшего байта делителя частоты такто- вого генератора. В режиме регистра управления прерываниями порт имеет следующий формат.

Таблица 2.
Бит Значение
0 1 - разрешемие прерывания при готовности принимаемых данных.
1 1 - разрешение прерывания после передачи байта (когда выхо - дной выходной буфер передачи пуст.)
2 1 - разрешение прерывания по обнаружении состояния "BREAK" или ошибки.
3 1 - разрешение прерывания по изменению на разъёме RS-232-C.
4-7 Не используются, должны быть равны 0.

 
aka_kludgeДата: Вторник, 22.04.2008, 23:43 | Сообщение # 4
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Порт 3FAh.

Регистр идентификации прерывания. По его содержимому программа может определить причину прерывания. Формат регистра приведён в таблице 3.

Таблица 3.
Бит Значение
0 1 - нет прерываний, ожидающих обслуживания.
1-2 00 - прерывание по линии состояния приёмника, возникает при переполнении приёмника, ошибка чётности или формата данных, или при состоянии "BREAK". Сбрасывается после чтения состо - яния линии и порта 3FDh.

* 01 - данные приняты и доступны для чтения. Сбрасывается после после чтения данных из порта 3F8h.
* 11 - Состояние модема. Устонавливается при изменении состояния входных линий CTS, RI, DCD, DSR.

3-7 Должны быть равны 0.

 
aka_kludgeДата: Вторник, 22.04.2008, 23:43 | Сообщение # 5
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Порт 3FBh.

Управляющий регистр, доступен по записи и чтению. Его формат показан в таблице 4.

Таблица 4.
Бит Значение
0-1 Длинна слова в байтах. 00 - 5 бит.

* 01 - 6 бит.
* 10 - 7 бит.
* 11 - 8 бит.

2 Количество стоповых битов: 0 - 1 бит, 1 - 2 бита.
3-4 Чётность:

* 10 - контроль на чётность неиспользуется;
* 01 - контроль на нечётность.
* 11 - контроль на чётность.

5 Фиксация чётности. При установки этого бита бит чётности всегда принимает зна - чение 0 (если биты 3-4 равны 11) или 1 (если биты 3-4 равны 01)
6 Установка перерыва. Вызывает вывыод строки нулей в качестве сигнала "BREAK" для подключения устройства.
7 1 - порты 3F8h и 3F9h используется для загрузки делителя частоты тактового генератора; 0 - порты используются как обычно.

 
aka_kludgeДата: Вторник, 22.04.2008, 23:43 | Сообщение # 6
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Порт 3FCh.

Регитр управления модемом. Упровляет состоянием выходных линий DTR, RTS, линий, специфических для модемов OUT1 и OUT2, для запуска диагно- стики при входе асинхронного адаптера, замкнутым на его выход. Формат порта приведён в таблице 5.

Таблица 5.
Бит Значение
0 Линия DTR
1 Линия RTS.
2 Линия OUT1 (запасная)
3 Линия OUT2 (запасная)
4 Запуск диагностики при входе асинхронного|
адаптера, замкнутом на его выход.
5-7 Должно быть равно 0

 
aka_kludgeДата: Вторник, 22.04.2008, 23:44 | Сообщение # 7
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Порт 3FDh.
Регистр состоянии линии. Значение зарядов регистра приведены в таблице 6.

Таблица 6.

Бит Значение
0 Данные получены и готовы для чтения, сбрасываетсь при чтении данных.
1 Ошибка переполнения. Был принят новый байт данных, а предыдущий ещё не был считан программой. Предыдущий байт потерен.
2 Ошибка чётности, сбрасывается после чтения состояния линии.
3 Ошибка синхронизации.
4 Обнаружен запрос на прерывание передачи "BREAK" - длинная строка нулей.
5 Регистр хранения передатчика пуст, в него можно записать новый байт для передачи.
6 Регистр сдвига передатчика пуст. Этот регистр получает данные из регистра хранения и преобразует их в последовательный вид для передачи.
7 Тайм-аут (устройство не связано с компьютером)

 
aka_kludgeДата: Вторник, 22.04.2008, 23:44 | Сообщение # 8
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Порт 3FEh.

Регистр состояния модема. Значения битов указвны в таблице 7.

Таблица 7.
Бит Значение
0 Линия CTS изменила состояние.
1 Линия DSR изменила состояние.
2 Линия IR изменила состояние.
3 Линия DCD изменила состояние.
4 Состояние линии CTS
5 Состояние линии DSR
6 Состояние линии IR.
7 Состояние линии DCD.

 
aka_kludgeДата: Вторник, 22.04.2008, 23:44 | Сообщение # 9
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Приём и передача данных.

Перед записью байта данных в регистр передатчика нужно убедиться, что регистр хранения передатчика свободен, то есть убедиться в том, что передача предыдущего символа завершена. Признаком свободы регистра передатчика является установленный в 1 бит 5 регистра состояния линии с адресом 3FDh.

Аналогично передачи данных перед вводом символа из порта приёмника 3F8h следует убедиться, что бит 0 порта 3FDh установлен в 1, то есть что символ принят из линии и находиться в вуферном регистре приёмника.

 
aka_kludgeДата: Вторник, 22.04.2008, 23:45 | Сообщение # 10
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Программирование на ассемблере под Win32 воспринимается весьма не однозначно. Считается, что написание приложений слишком сложно для применения ассемблера. Собственно обсуждению того, насколько оправдана такая точка зрения, и посвящена данная статья. Она не ставит своей целью обучение программированию под Win32 или обучение ассемблеру, я подразумеваю, что читатели имеют определённые знания в этих областях.

В отличие от программирования под DOS, где программы написанные на языках высокого уровня (ЯВУ) были мало похожи на свои аналоги, написанные на ассемблере, приложения под Win32 имеют гораздо больше общего. В первую очередь, это связано с тем, что обращение к сервису операционной системы в Windows осуществляется посредством вызова функций, а не прерываний, что было характерно для DOS. Здесь нет передачи параметров в регистрах при обращении к сервисным функциям и, соответственно, нет и множества результирующих значений возвращаемых в регистрах общего назначения и регистре флагов. Следовательно проще запомнить и использовать протоколы вызова функций системного сервиса. С другой стороны, в Win32 нельзя непосредственно работать с аппаратным уровнем, чем "грешили" программы для DOS. Вообще написание программ под Win32 стало значительно проще и это обусловлено следующими факторами:

* отсутствие startup кода, характерного для приложений и динамических библиотек написанных под Windows 3.x;
* гибкая система адресации к памяти: возможность обращаться к памяти через любой регистр общего назначения; "отсутствие" сегментных регистров;
* доступность больших объёмов виртуальной памяти;
* развитый сервис операционной системы, обилие функций, облегчающих разработку приложений;
* многообразие и доступность средств создания интерфейса с пользователем (диалоги, меню и т.п.).

Современный ассемблер, к которому относится и TASM 5.0 фирмы Borland International Inc., в свою очередь, развивал средства, которые ранее были характерны только для ЯВУ. К таким средствам можно отнести макроопределение вызова процедур, возможность введения шаблонов процедур (описание прототипов) и даже объектно-ориентированные расширения. Однако, ассемблер сохранил и такой прекрасный инструмент, как макроопределения вводимые пользователем, полноценного аналога которому нет ни в одном ЯВУ.

Все эти факторы позволяют рассматривать ассемблер, как самостоятельный инструмент для написания приложений под платформы Win32 (Windows NT и Windows 95). Как иллюстрацию данного положения, рассмотрим простой пример приложения, работающего с диалоговым окном.

 
aka_kludgeДата: Вторник, 22.04.2008, 23:45 | Сообщение # 11
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Пример 1. Программа работы с диалогом

Файл, содержащий текст приложения, dlg.asm

IDEAL
P586
RADIX 16
MODEL FLAT

%NOINCL
%NOLIST
include "winconst.inc" ; API Win32 consts
include "winptype.inc" ; API Win32 functions prototype
include "winprocs.inc" ; API Win32 function
include "resource.inc" ; resource consts

MAX_USER_NAME = 20
DataSeg
szAppName db 'Demo 1', 0
szHello db 'Hello, '
szUser db MAX_USER_NAME dup (0)

CodeSeg
Start: call GetModuleHandleA, 0
call DialogBoxParamA, eax, IDD_DIALOG, 0, offset DlgProc, 0
cmp eax,IDOK
jne bye
call MessageBoxA, 0, offset szHello, \
offset szAppName, \
MB_OK or MB_ICONINFORMATION
bye: call ExitProcess, 0

public stdcall DlgProc
proc DlgProc stdcall
arg @@hDlg :dword, @@iMsg :dword, @@wPar :dw ord, @@lPar :dword
mov eax,[@@iMsg]
cmp eax,WM_INITDIALOG
je @@init
cmp eax,WM_COMMAND
jne @@ret_false

mov eax,[@@wPar]
cmp eax,IDCANCEL
je @@cancel
cmp eax,IDOK
jne @@ret_false

call GetDlgItemTextA, @@hDlg, IDR_NAME, \
offset szUser, MAX_USER_NAME
mov eax,IDOK
@@cancel: call EndDialog, @@hDlg, eax

@@ret_false: xor eax,eax
ret

@@init: call GetDlgItem, @@hDlg, IDR_NAME
call SetFocus, eax
jmp @@ret_false
endp DlgProc
end Start

Файл ресурсов dlg.rc

#include "resource.h"
IDD_DIALOG DIALOGEX 0, 0, 187, 95
STYLE DS_MODALFRAME | DS_3DLOOK | WS_POPUP | WS_CAPTION | WS_SYSMENU
EXSTYLE WS_EX_CLIENTEDGE
CAPTION "Dialog"
FONT 8, "MS Sans Serif"
BEGIN
DEFPUSHBUTTON "OK",IDOK,134,76,50,14
PUSHBUTTON "Cancel",IDCANCEL,73,76,50,14
LTEXT "Type your name",IDC_STATIC,4,36,52,8
EDITTEXT IDR_NAME,72,32,112,14,ES_AUTOHSCROLL
END

Остальные файлы из данного примера, приведены в приложении 1.

 
aka_kludgeДата: Вторник, 22.04.2008, 23:45 | Сообщение # 12
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Краткие комментарии к программе

Сразу после метки Start, программа обращается к функции API Win32 GetModuleHandle для получения handle данного модуля (данный параметр чаще именуют как handle of instance). Получив handle, мы вызываем диалог, созданный либо вручную, либо с помощью какой-либо программы построителя ресурсов. Далее программа проверяет результат работы диалогового окна. Если пользователь вышел из диалога посредством нажатия клавиши OK, то приложение запускает MessageBox с текстом приветствия.

Диалоговая процедура обрабатывает следующие сообщения. При инициализации диалога (WM_INITDIALOG) она просит Windows установить фокус на поле ввода имени пользователя. Сообщение WM_COMMAND обрабатывается в таком порядке: делается проверка на код нажатия клавиши. Если была нажата клавиша OK, то пользовательский ввод копируется в переменную szValue, если же была нажата клавиша Cancel, то копирования не производится. Но и в том и другом случае вызывается функция окончания диалога: EndDialog. Остальные сообщения в группе WM_COMMAND просто игнорируются, предоставляя Windows действовать по умолчанию.

Вы можете сравнить приведённую программу с аналогичной программой, написанной на ЯВУ, разница в написании будет незначительна. Очевидно те, кто писал приложения на ассемблере под Windows 3.x, отметят тот факт, что исчезла необходимость в сложном и громоздком startup коде. Теперь приложение выглядит более просто и естественно.

 
aka_kludgeДата: Вторник, 22.04.2008, 23:45 | Сообщение # 13
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Пример 2. Динамическая библиотека

Написание динамических библиотек под Win32 также значительно упростилось, по сравнению с тем, как это делалось под Windows 3.x. Исчезла необходимость вставлять startup код, а использование четырёх событий инициализации/деинициализации на уровне процессов и потоков, кажется логичным.

Рассмотрим простой пример динамической библиотеки, в которой всего одна функция, преобразования целого числа в строку в шестнадцатеричной системе счисления.

Файл mylib.asm

Ideal
P586
Radix 16
Model flat
DLL_PROCESS_ATTACH

extrn GetVersion: proc

DataSeg
hInst dd 0
OSVer dw 0

CodeSeg
proc libEntry stdcall
arg @@hInst :dword, @@rsn :dword, @@rsrv :dword
cmp [@@rsn],DLL_PROCESS_ATTACH
jne @@1
call GetVersion
mov [OSVer],ax
mov eax,[@@hInst]
mov [hInst],eax
@@1: mov eax,1
ret
endP libEntry

public stdcall Hex2Str
proc Hex2Str stdcall
arg @@num :dword, @@str :dword
uses ebx
mov eax,[@@num]
mov ebx,[@@str]
mov ecx,7
@@1: mov edx,eax
shr eax,4
and edx,0F
cmp edx,0A
jae @@2
add edx,'0'
jmp @@3
@@2: add edx,'A' - 0A
@@3: mov [byte ebx + ecx],dl
dec ecx
jns @@1
mov [byte ebx + 8],0
ret
endp Hex2Str

end libEntry

Остальные файлы, которые необходимы для данного примера, можно найти в приложении 2.

 
aka_kludgeДата: Вторник, 22.04.2008, 23:46 | Сообщение # 14
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Краткие комментарии к динамической библиотеке

Процедура libEntry является точкой входа в динамическую библиотеку, её не надо объявлять как экспортируемую, загрузчик сам определяет её местонахождение. LibEntry может вызываться в четырёх случаях:

* при проецировании библиотеки в адресное пространство процесса (DLL_PROCESS_ATTACH);
* при первом вызове библиотеки из потока (DLL_THREAD_ATTACH), например, с помощью функции LoadLibrary;
* при выгрузке библиотеки потоком (DLL_THREAD_DETACH);
* при выгрузке библиотеки из адресного пространства процесса (DLL_PROCESS_DETACH).

В нашем примере обрабатывается только первое из событий DLL_PROCESS_ATTACH. При обработке данного события библиотека запрашивает версию OS сохраняет её, а также свой handle of instance.

Библиотека содержит только одну экспортируемую функцию, которая собственно не требует пояснений. Вы, пожалуй, можете обратить внимание на то, как производится запись преобразованных значений. Интересна система адресации посредством двух регистров общего назначения: ebx + ecx, она позволяет нам использовать регистр ecx одновременно и как счётчик и как составную часть адреса.

 
aka_kludgeДата: Вторник, 22.04.2008, 23:47 | Сообщение # 15
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Пример 3. Оконное приложение

Файл dmenu.asm
Ideal
P586
Radix 16
Model flat

struc WndClassEx
cbSize dd 0
style dd 0
lpfnWndProc dd 0
cbClsExtra dd 0
cbWndEx tra dd 0
hInstance dd 0
hIcon dd 0
hCursor dd 0
hbrBackground dd 0
lpszMenuName dd 0
lpszClassName dd 0
hIconSm dd 0
ends W ndClassEx

struc Point
left dd 0
top dd 0
right dd 0
bottom dd 0
ends Point

struc msgStruc
hwnd dd 0
message dd 0
w Param dd 0
lParam dd 0
time dd 0
pt Point <>
ends msgStruc

MyMenu = 0065
ID_OPEN = 9C41
ID_SAVE = 9C42
ID_EXIT = 9C43

CS_VREDRAW = 0001
CS_HREDRAW = 0002
IDI_APPLICATION = 7F00
IDC_ARROW = 7F00
COLOR_WINDOW = 5
WS_EX_WINDOWEDGE = 00000100
WS_EX_CLIENTEDGE = 00000200
WS_EX_OVERLAPPEDWINDOW = WS_EX_WINDOWEDGE OR WS_EX_CLIENTEDGE
WS_OVERLAPPED = 00000000
WS_CAPTION = 00C00000
WS_SYSMENU = 00080000
WS_THICKFRAME = 00040000
WS_MINIMIZEBOX = 00020000
WS_MAXIMIZEBOX = 00010000
WS_OVERLAPPEDWINDOW = WS_OVERLAPPED OR \
WS_CAPTION OR \
WS_SYSMENU OR \
WS_THICKFRAME OR \
WS_MINIMIZEBOX OR \
WS_MAXIMIZEBOX
CW_USEDEFAULT = 80000000
SW_SHOW = 5
WM_COMMAND = 0111
WM_DESTROY = 0002
WM_CLOSE = 0010
MB_OK = 0

PROCTYPE ptGetModuleHandle stdcall \
lpModuleName :dword

PROCTYPE ptLoadIcon stdcall \
hInstance :dword, \
lpIconName : dword

PROCTYPE ptLoadCursor stdcall \
hInstance :dword, \
lpCursorName :dword

PROCTYPE ptLoadMenu stdcall \
hInstance :d word, \
lpMenuName :dword

PROCTYPE ptRegisterClassEx stdcall \
lpwcx :dword

PROCTYPE ptCreateWindowEx stdcall \
dwExStyle :dword, \
lpClassName :dword, \
lpWindowName :dword, \
dwStyle :dword, \
x :dword, \
y :dword, \
nWidth :dword, \
nHeight :dword, \
hWndParent :dword, \
hMenu :dword, \
hInstance :dword, \
lpParam :dword

PROCTYPE ptShowWindow stdcall \
hWnd :dword, \
nCmdShow :dword

PROCTYPE ptUpda teWindow stdcall \
hWnd :dword

PROCTYPE ptGetMessage stdcall \
pMsg :dword, \
hWnd :dword, \
wMsgFilterMin :dword, \
wMsgFilterMax :dword

PROCTYPE ptTranslateMessage stdcall \
lpMsg :dword

PROCTYPE ptDispatchMessage stdcall \
pmsg :dwor d

PROCTYPE ptSetMenu stdcall \
hWnd :dword, \
hMenu :dword

PROCTYPE ptPostQuitMessage stdcall \
nExitCode :dword

PRO CTYPE ptDefWindowProc stdcall \
hWnd :dword, \
Msg :dword, \
wParam :dword, \
lParam :dword

PROCTYPE ptSendMessage s tdcall \
hWnd :dword, \
Msg :dword, \
wParam :dword, \
lParam :dword

PROCTYPE ptMessageBox stdcall \
hWnd :dwor d, \
lpText :dword, \
lpCaption :dword, \
uType :dword

PROCTYPE ptExitProcess stdcall \
exitCode :dword

 
aka_kludgeДата: Вторник, 22.04.2008, 23:47 | Сообщение # 16
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
extrn GetModuleHandleA :ptGetModuleHandle
extrn LoadIconA :ptLoadIcon
extrn LoadCursorA :ptLoadCursor
extrn RegisterClassExA :ptReg isterClassEx
extrn LoadMenuA :ptLoadMenu
extrn CreateWindowExA :ptCreateWindowEx
extrn ShowWindow :ptShowWindow
extrn UpdateWindo w :ptUpdateWindow
extrn GetMessageA :ptGetMessage
extrn TranslateMessage :ptTranslateMessage
extrn DispatchMessageA :ptDispatchMessa ge
extrn SetMenu :ptSetMenu
extrn PostQuitMessage :ptPostQuitMessage
extrn DefWindowProcA :ptDefWindowProc
extrn SendMessageA : ptSendMessage
extrn MessageBoxA :ptMessageBox
extrn ExitProcess :ptExitProcess

UDataSeg
hInst dd ?
hWnd dd ?

IFNDEF VER1
h Menu dd ?
ENDIF

DataSeg
msg msgStruc <>
classTitle db 'Menu demo', 0
wndTitle db 'Demo program', 0
msg_open_txt db 'You selected open', 0
msg_open_tlt db 'Open box', 0
msg_save_txt db 'You selected save', 0
msg_save_tlt db 'Save box', 0

CodeSeg
Start: call GetModuleHandleA, 0 ; не обязательно, но желательно
mov [hInst],eax

sub esp,SIZE WndClassEx ; отведём место в стеке под структуру

mov [(WndClassEx esp).cbSize],SIZE WndClassEx
mov [(WndClassEx esp).style],CS_HREDRAW or CS_VREDRAW
mov [(WndClassEx esp).lpfnWndProc],offset WndProc
mov [(WndClassEx esp).cbWndExtra],0
mov [(WndClassEx esp).cbClsExtra],0
mov [(WndClassEx esp).hInstance],eax
call LoadIconA, 0, IDI_APPLICATION
mov [(WndClassEx esp).hIcon],eax
call LoadCursorA, 0, IDC_ARROW
mov [(WndClassEx esp).hCursor],eax
mov [(WndClassEx esp).hbrBackground],COLOR_WINDOW
IFDEF VER1
mov [(WndClassEx esp).lpszMenuName],MyMenu
ELSE
mov [(WndClassEx esp).lpszMenuName],0
ENDIF
mov [(WndClassEx esp).lpszClassName],offset classTitle
mov [(WndClassEx esp).hIconSm],0
call RegisterClassExA, esp ; зарегистрируем класс окна

add esp,SIZE WndClassEx ; восстановим стек
; и создадим окно
IFNDEF VER2
call CreateWindowExA, WS_EX_OVERLAPPEDWINDOW, \ extended window style
offset classTitle, \ pointer to registered class name
offset wndTitle,\ pointer to window name
WS_OVERLAPPEDWINDOW, \ window style
CW_USEDEFAULT, \ horizontal position of window
CW_USEDEFAULT, \ vertical position of window
CW_USEDEFAULT, \ window width
CW_USEDEFAULT, \ window height
0, \ handle to parent or owner window
0, \ handle to menu, or child-window identifier
[hInst], \ handle to application instance
0 ; pointer to window-creation data
ELSE
call LoadMenu, hInst, MyMenu
mov [hMenu],eax
call CreateWindowExA, WS_EX_OVERLAPPEDWINDOW, \ extended window style
offset classTitle, \ pointer to registered class name
offset wndTitle, \ pointer to window name
WS_OVERLAPPEDWINDOW, \ window style
CW_USEDEFAULT, \ horizontal position of window
CW_USEDEFAULT, \ vertical position of window
CW_USEDEFAULT, \ window width
CW_USEDEFAULT, \ window height
0, \ handle to parent or owner window
eax, \ handle to menu, or child-window identifier
[hInst], \ handle to application instance
0 ; pointer to window-creation data
ENDIF
mov [hWnd],eax
call ShowWindow, eax, SW_SHOW ; show window
call UpdateWindow, [hWnd] ; redraw window

IFDEF VER3
call LoadMenuA, [hInst], MyMenu
mov [hMenu],eax
call SetMenu, [hWnd], eax
ENDIF

msg_loop:
call GetMessageA, offset msg, 0, 0, 0
or ax,ax
jz exit
call TranslateMessage, offset msg
call DispatchMessageA, offset msg
jmp msg_loop
exit: call ExitProcess, 0

 
aka_kludgeДата: Вторник, 22.04.2008, 23:47 | Сообщение # 17
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
public stdcall WndProc
proc WndProc stdcall
arg @@hwnd: dword, @@msg: dword, @@wPar: dword, @@lPar: dword
mov eax,[@@msg]
cmp eax,WM_COMMAND
je @@command
cmp eax,WM_DESTROY
jne @@default
call PostQuitMessage, 0
xor eax,eax
jmp @@ret
@@default:
c all DefWindowProcA, [@@hwnd], [@@msg], [@@wPar], [@@lPar]
@@ret: ret
@@command:
mov eax,[@@wPar]
cmp eax,ID_OPEN
je @@open
cmp eax,ID_SAVE
je @@save
call SendMessageA, [@@hwnd], WM_CLOSE, 0, 0
xor eax,eax
jmp @@ret
@@open: mov eax, offset msg_open_txt
mov edx, offset msg_open_tlt
jmp @@mess
@@save: mov eax, offset msg_save_txt
mov edx, offset msg_save_tlt
@@mess: call MessageBoxA, 0, eax, edx, MB_OK
xor eax,eax
jmp @@ret
endp WndProc
end Start
 
aka_kludgeДата: Вторник, 22.04.2008, 23:50 | Сообщение # 18
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Комментарии к программе

Здесь мне хотелось в первую очередь продемонстрировать использование прототипов функций API Win32. Конечно их (а также описание констант и структур из API Win32) следует вынести в отдельные подключаемые файлы, поскольку, скорее всего Вы будете использовать их и в других программах. Описание прототипов функций обеспечивает строгий контроль со стороны компилятора за количеством и типом параметров, передаваемых в функции. Это существенно облегчает жизнь программисту, позволяя избежать ошибок времени исполнения, тем более, что число параметров в некоторых функциях API Win32 весьма значительно.

Существо данной программы заключается в демонстрации вариантов работы с оконным меню. Программу можно откомпилировать в трёх вариантах (версиях), указывая компилятору ключи VER2 или VER3 (по умолчанию используется ключ VER1). В первом варианте программы меню определяется на уровне класса окна и все окна данного класса будут иметь аналогичное меню. Во втором варианте, меню определяется при создании окна, как параметр функции CreateWindowEx. Класс окна не имеет меню и в данном случае, каждое окно этого класса может иметь своё собственное меню. Наконец, в третьем варианте, меню загружается после создания окна. Данный вариант показывает, как можно связать меню с уже созданным окном.

Директивы условной компиляции позволяют включить все варианты в текст одной и той же программы. Подобная техника удобна не только для демонстрации, но и для отладки. Например, когда Вам требуется включить в программу новый фрагмент кода, то Вы можете применить данную технику, дабы не потерять функционирующий модуль. Ну, и конечно, применение директив условной компиляции - наиболее удобное средство тестирования различных решений (алгоритмов) на одном модуле.

Представляет определённый интерес использование стековых фреймов и заполнение структур в стеке посредством регистра указателя стека (esp). Именно это продемонстрировано при заполнении структуры WndClassEx. Выделение места в стеке (фрейма) делается простым перемещением esp:

sub esp,SIZE WndClassEx

 
aka_kludgeДата: Вторник, 22.04.2008, 23:50 | Сообщение # 19
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Теперь мы можем обращаться к выделенной памяти используя всё тот же регистр указатель стека. При создании 16-битных приложений такой возможностью мы не обладали. Данный приём можно использовать внутри любой процедуры или даже произвольном месте программы. Накладные расходы на подобное выделение памяти минимальны, однако, следует учитывать, что размер стека ограничен и размещать большие объёмы данных в стеке вряд ли целесообразно. Для этих целей лучше использовать "кучи" (heap) или виртуальную память (virtual memory).

Остальная часть программы достаточно тривиальна и не требует каких-либо пояснений. Возможно более интересным покажется тема использования макроопределений.

 
aka_kludgeДата: Вторник, 22.04.2008, 23:50 | Сообщение # 20
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Макроопределения

Мне достаточно редко приходилось серьёзно заниматься разработкой макроопределений при программировании под DOS. В Win32 ситуация принципиально иная. Здесь грамотно написанные макроопределения способны не только облегчить чтение и восприятие программ, но и реально облегчить жизнь программистов. Дело в том, что в Win32 фрагменты кода часто повторяются, имея при этом не принципиальные отличия. Наиболее показательна, в этом смысле, оконная и/или диалоговая процедура. И в том и другом случае мы определяем вид сообщения и передаём управление тому участку кода, который отвечает за обработку полученного сообщения. Если в программе активно используются диалоговые окна, то аналогичные фрагменты кода сильно перегрузят программу, сделав её малопригодной для восприятия. Применение макроопределений в таких ситуациях более чем оправдано. В качестве основы для макроопределения, занимающегося диспетчеризацией поступающих сообщений на обработчиков, может послужить следующее описание.

Пример макроопределений

macro MessageVector message1, message2:REST
IFNB
dd message1
dd offset @@&message1
@@VecCount = @@VecCount + 1
MessageVector message2
ENDIF
endm MessageVector

macro WndMessages VecName, message1, message2:REST
@@VecCount = 0
DataSeg
label @@&VecName dword
MessageVector message1, message2
@@&VecName&Cnt = @@VecCount
CodeSeg
mov ecx,@@&VecName&Cnt
mov eax,[@@msg]
@@&VecName&_1: dec ecx
js @@default
cmp eax,[dword ecx * 8 + offset @@&VecName]
jne @@&VecName&_1
jmp [dword ecx + offset @@&VecName + 4]

@@default: call DefWindowProcA, [@@hWnd], [@@msg], [@@wPar], [@@lPar]
@@ret: ret
@@ret_false: xor eax,eax
jmp @@ret
@@ret_true: mov eax,-1
dec eax
jmp @@ret
endm WndMessage

 
aka_kludgeДата: Вторник, 22.04.2008, 23:50 | Сообщение # 21
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Комментарии к макроопределениям

При написании процедуры окна Вы можете использовать макроопределение WndMessages, указав в списке параметров те сообщения, обработку которых намерены осуществить. Тогда процедура окна примет вид:

proc WndProc stdcall
arg @@hWnd: dword, @@msg: dword, @@wPar: dword, @@lPar: dword
WndMessages WndVector, WM_CREATE, WM_SIZE, WM_PAINT, WM_CLOSE, WM_DESTROY

@@WM_CREATE:
; здесь обрабатываем сообщение WM_CREATE
@@WM_SIZE:
; здесь обрабатываем сообщение WM_SIZE
@@WM_PAINT:
; здесь обрабатываем сообщение WM_PAINT
@@WM_CLOSE:
; здесь обрабатываем сообщение WM_CLOSE
@@WM_DESTROY:
; здесь обрабатываем сообщение WM_DESTROY

endp WndProc

Обработку каждого сообщения можно завершить тремя способами:

* вернуть значение TRUE, для этого необходимо использовать переход на метку @@ret_true;
* вернуть значение FALSE, для этого необходимо использовать переход на метку @@ret_false;
* перейти на обработку по умолчанию, для этого необходимо сделать переход на метку @@default.

Отметьте, что все перечисленные метки определены в макро WndMessages и Вам не следует определять их заново в теле процедуры.

Теперь давайте разберёмся, что происходит при вызове макроопределения WndMessages. Вначале производится обнуление счётчика параметров самого макроопределения (число этих параметров может быть произвольным). Теперь в сегменте данных создадим метку с тем именем, которое передано в макроопределение в качестве первого параметра. Имя метки формируется путём конкатенации символов @@ и названия вектора. Достигается это за счёт использования оператора &. Например, если передать имя TestLabel, то название метки примет вид: @@TestLabel. Сразу за объявлением метки вызывается другое макроопределение MessageVector, в которое передаются все остальные параметры, которые должны быть ничем иным, как списком сообщений, подлежащих обработке в процедуре окна. Структура макроопределения MessageVector проста и бесхитростна. Она извлекает первый параметр и в ячейку памяти формата dword заносит код сообщения. В следующую ячейку памяти формата dword записывается адрес метки обработчика, имя которой формируется по описанному выше правилу. Счётчик сообщений увеличивается на единицу. Далее следует рекурсивный вызов с передачей ещё не зарегистрированных сообщений, и так продолжается до тех пор, пока список сообщений не будет исчерпан.

Сейчас в макроопределении WndMessage можно начинать обработку. Теперь существо обработки, скорее всего, будет понятно без дополнительных пояснений.

Обработка сообщений в Windows не является линейной, а, как правило, представляет собой иерархию. Например, сообщение WM_COMMAND может заключать в себе множество сообщений поступающих от меню и/или других управляющих элементов. Следовательно, данную методику можно с успехом применить и для других уровней каскада и даже несколько упростить её. Действительно, не в наших силах исправить код сообщений, поступающих в процедуру окна или диалога, но выбор последовательности констант, назначаемых пунктам меню или управляющим элементам (controls) остаётся за нами. В этом случае нет нужды в дополнительном поле, которое сохраняет код сообщения. Тогда каждый элемент вектора будет содержать только адрес обработчика, а найти нужный элемент весьма просто. Из полученной константы, пришедшей в сообщении, вычитается идентификатор первого пункта меню или первого управляющего элемента, это и будет номер нужного элемента вектора. Остаётся только сделать переход на обработчик.

Вообще тема макроопределений весьма поучительна и обширна. Мне редко доводится видеть грамотное использование макросов и это досадно, поскольку с их помощью можно сделать работу в ассемблере значительно проще и приятнее.

 
aka_kludgeДата: Вторник, 22.04.2008, 23:51 | Сообщение # 22
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Резюме

Для того, чтобы писать полноценные приложения под Win32 требуется не так много:

* собственно компилятор и компоновщик (я использую связку TASM32 и TLINK32 из пакета TASM 5.0). Перед использованием рекомендую "наложить" patch, на данный пакет. Patch можно взять на site http://www.borland.com/ или на нашем ftp сервере ftp.uralmet.ru.
* редактор и компилятор ресурсов (я использую Developer Studio и brcc32.exe);
* выполнить перетрансляцию header файлов с описаниями процедур, структур и констант API Win32 из нотации принятой в языке Си, в нотацию выбранного режима ассемблера: Ideal или MASM.

В результате у Вас появится возможность писать лёгкие и изящные приложения под Win32, с помощью которых Вы сможете создавать и визуальные формы, и работать с базами данных, и обслуживать коммуникации, и работать multimedia инструментами. Как и при написании программ под DOS, у Вас сохраняется возможность наиболее полного использования ресурсов процессора, но при этом сложность написания приложений значительно снижается за счёт более мощного сервиса операционной системы, использования более удобной системы адресации и весьма простого оформления программ.

 
aka_kludgeДата: Вторник, 22.04.2008, 23:51 | Сообщение # 23
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Приложение 1. Файлы, необходимые для первого примера

Файл констант ресурсов resource.inc

IDD_DIALOG = 65 ; 101
IDR_NAME = 3E8 ; 1000
IDC_STATIC = -1

Файл заголовков resource.h

#define IDD_DIALOG 101
#define IDR_NAME 1000
#define IDC_STATIC

Файл определений dlg.def

NAME TEST
DESCRIPTION 'Demo dialog'
EXETYPE WINDOWS
EXPORTS DlgProc @1

Файл компиляции makefile

# Make file for Demo dialog
# make -B

NAME = dlg
OBJS = $(NAME).obj
DEF = $(NAME).def
RES = $(NAME).res

TASMOPT=/m3 /mx /z /q /DWINVER=0400 /D_WIN32_WINNT=0400

!if $d(DEBUG)
TASMDEBUG=/zi
LINKDEBUG=/v
!else
TASMDEBUG=/l
LINKDEBUG=
!endif

!if $d(MAKEDIR)
IMPORT=$(MAKEDIR)\..\lib\import32
!else
IMPORT=import32
!endif

$(NAME).EXE: $(OBJS) $(DEF) $(RES)
tlink32 /Tpe /aa /c $(LINKDEBUG) $(OBJS),$(NAME),, $(IMPORT), $(DEF), $(RES)

.asm.obj:
tasm32 $(TASMDEBUG) $(TASMOPT) $&.asm

$(RES): $(NAME).RC
BRCC32 -32 $(NAME).RC

 
aka_kludgeДата: Вторник, 22.04.2008, 23:51 | Сообщение # 24
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Приложение 2. Файлы, необходимые для второго примера

Файл описания mylib.def

LIBRARY MYLIB
DESCRIPTION 'DLL EXAMPLE, 1997'
EXPORTS Hex2Str @1

Файл компиляции makefile

# Make file for Demo DLL# make -B# make -B -DDEBUG for debug information

NAME = mylib
OBJS = $(NAME).obj
DEF = $(NAME).def
RES = $(NAME).res

TASMOPT=/m3 /mx /z /q /DWINVER=0400 /D_WIN32_WINNT=0400

!if $d(DEBUG)
TASMDEBUG=/zi
LINKDEBUG=/v
!else
TASMDEBUG=/l
LINKDEBUG=
!endif

!if $d(MAKEDIR)
IMPORT=$(MAKEDIR)\..\lib\import32
!else
IMPORT=import32
!endif

$(NAME).EXE: $(OBJS) $(DEF)
tlink32 /Tpd /aa /c $(LINKDEBUG) $(OBJS),$(NAME),, $(IMPORT), $(DEF)

.asm.obj:
tasm32 $(TASMDEBUG) $(TASMOPT) $&.asm

$(RES): $(NAME).RC
BRCC32 -32 $(NAME).RC

 
aka_kludgeДата: Вторник, 22.04.2008, 23:51 | Сообщение # 25
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
Приложение 3. Файлы, необходимые для третьего примера

Файл описания dmenu.def

NAME TEST
DESCRIPTION 'Demo menu'
EXETYPE WINDOWS
EXPORTS WndProc @1

Файл ресурсов dmenu.rc

#include "resource.h
"MyMenu MENU DISCARDABLE
BEGIN POPUP "Files"
BEGIN
MENUITEM "Open", ID_OPEN
MENUITEM "Save", ID_SAVE
MENUITEM SEPARATOR
MENUITEM "Exit", ID_EXIT
END
MENUITEM "Other", 65535
END

Файл заголовков resource.h

#define MyMenu 101
#define ID_OPEN 40001
#define ID_SAVE 40002
#define ID_EXIT 40003

Файл компиляции makefile

# Make file for Turbo Assembler Demo menu
# make -B
# make -B -DDEBUG -DVERN for debug information and version
NAME = dmenu
OBJS = $(NAME).obj
DEF = $(NAME).def
RES = $(NAME).res
!if $d(DEBUG)
TASMDEBUG=/zi
LINKDEBUG=/v
!else
TASMDEBUG=/l
LINKDEBUG=
!endif

!if $d(VER2)
TASMVER=/dVER2
!elseif $d(VER3)
TASMVER=/dVER3
!else
TASMVER=/dVER1
!endif

!if $d(MAKEDIR)
IMPORT=$(MAKEDIR)\..\lib\import32
!else
IMPORT=import32
!endif

$(NAME).EXE: $(OBJS) $(DEF) $(RES)
tlink32 /Tpe /aa /c $(LINKDEBUG) $(OBJS),$(NAME),, $(IMPORT), $(DEF), $(RES)

.asm.obj:
tasm32 $(TASMDEBUG) $(TASMVER) /m /mx /z /zd $&.asm

$(RES): $(NAME).RC
BRCC32 -32 $(NAME).RC

 
aka_kludgeДата: Среда, 27.05.2009, 08:31 | Сообщение # 26
Admin
Группа: Администраторы
Сообщений: 1058
Награды: 2
Репутация: 25
Статус: Offline
cool
 
  • Страница 1 из 1
  • 1
Поиск:

Профиль
ИнформацияУправление
Сегодня: 21, 21.12.2024, 21:01
Вы используете: " v "
ВаШ внешний IP: "3.147.49.154"
У вас новых личных сообщений · Мой профиль | Выход




    Главная      
...
На службе : дней

21:01
Обновить


Пользователи
aka_kludge
qwerty
LeadyTOR
aka_Atlantis
AdHErENt
mAss
Sissutr
hiss
DrBio
tHick

Поиск


Copyright tHR - TeAM 2024 г. admin: aka_kludge (ICQ:334449009) Moderator's: LeadyTOR, ... Яндекс.Метрика