Деталі реалізації

Для виконання завдання було розроблено дві динамічні бібліотеки: ArrHandler.dllта ConsoIeIO.dll.Перша містить функції обробки масивів, друга – функції введення/виведення з/на консоль.

Головна програма використовує функції:

– створення масиву;

– додавання нових елементів у масив;

– читання елементів з масиву;

– введення чисел з консолі;

– виведення чисел на консоль.

У свою чергу функції з цих бібліотек використовують Win32 API-функції, такі, як WriteConsole, ReadConsole, GlobalAlloc, GlobalFreeта бібліотечні функції MASM32: atol, Itoa.

 

Лістинг 15.6:

title Рисований О.М. та Назаровець Д.В., НТУ “ХПІ”, КІТ-27А

.386

.model flat,stdcall

option casemap:none

include masm32includewindows.inc

include masm32includekernel32.inc

include arrhandler.inc

includelib masm32libkernel32.lib

includelib arrhandler.lib

includelib ConsoleIO.lib

; прототипи функцій

GetNum proto

PrintNum proto Number :DWORD

WriteLine proto

; константи

L equ 3

M equ 15

N equ 6

.data

count dw 0 ; лічильник циклу

.code

_st:

invoke CreateArr, N ; створення масиву

xor ecx, ecx ; обнулення есх

_input: ; цикл введення

push ecx ; зберегти есх

invoke GetNum ; прочитати номер з консолі

pop ecx ; відновити есх

push ecx ; знов зберегти есх

invoke SetArrValue, cx, ax ; зберегти його в масиві

pop ecx ; відновити есх

inc ecx ; збільшити есх

cmp ecx, N ; порівняти есх

jb _input ; продовжити цикл введення

xor ecx, ecx ; обнулити есх

_print: ; цикл виведення та підрахунку

push ecx ; зберегти есх в стеку

invoke GetArrValue, cx ; узяти дані з масиву

cmp eax, L ; порівняти дані

jb _cont ; пропуск коду

cmp eax, M ; порівняти дані

jae _cont ; пропуск коду

inc count ; збільшити лічильник

_cont:

invoke PrintNum, eax ; виведення числа

pop ecx ; відновити есх

inc ecx ; збільшити есх

cmp ecx, N ; продовжити

jb _print ; цикл

invoke WriteLine ; друк розділювальної лінії

movzx eax, count ; завантажити лічильник в еах

invoke PrintNum, eax ; надрукувати лічильник

invoke Sleep, 3000 ; затримка

invoke ExitProcess, 0 ; вихід

end _st

 

Лістинг 15.7.Файл ArrHandler:

title Рисований О.М. та Назаровець Д.В., НТУ “ХПІ”, КІТ-27А

.386

.model flat,stdcall

option casemap:none

include masm32includewindows.inc

include masm32includekernel32.inc

includelib masm32libkernel32.lib

public CreateArr

public SetArrValue

public GetArrValue

public GetArrSize

public FillArr

public DisposeArr

; константи

ERR_OUT_OF_RANGE equ 0FFFFFFh ; помилка – вихід за межі масиву

ERR_ARR_EXIST equ 0FFFFFEh ; помилка – масив вже існує

ERR_ARR_NO_EXIST equ 0FFFFFDh ; помилка – масив не існує

ERR_NO_MEM equ 0FFFFFCh ; помилка – проблеми з виділенням пам’яті

; прототипи функцій

CreateArr proto :WORD

SetArrValue proto :WORD, :WORD

GetArrValue proto :WORD

GetArrSize proto

FillArr proto :WORD

DisposeArr proto

.data

arrsize dw 0 ; розмір масиву

head dd 0 ; вказівник на масив

hInst dd 0 ; дескриптор модуля

 

.code

; точка входу в бібліотеку

DllEntry proc instance :DWORD, reason :DWORD, unused :DWORD

mov eax, reason ; причина виклику – в еах

cmp eax, DLL_PROCESS_ATTACH ; підключення до процесу?

jnz _m1 ; немає – перехід на м1

push instance ; зберегти

pop hInst ; отриманий дескриптор модуля

jmp _exit ; на вихід

_m1: ; наступна перевірка

cmp eax, DLL_PROCESS_DETACH ; процес знищений?

jnz _exit ; немає - на вихід, решту всіх випадків не розглядаємо

invoke DisposeArr ; так – звільнимо пам’ять в купі

_exit: ; вихід з функції

and eax, 1 ; завжди потрібно повертати 1 (TRUE)

ret ; EIP <- SS:[ESP]; inc ESP

DllEntry endp

; функція створення масиву, єдиний параметр – розмір масиву в байтах

CreateArr proc asize :WORD

push ebx ; зберегти EBX (за угодою Windows,

; функції повинні зберігати значення регістрів ESI, EDI, EBP, EBX)

cmp head, 0 ; перевірка існування масиву

jnz _break1 ; якщо масив вже є, то нічого створювати не потрібно

movzx ebx, asize ; завантажити в ebx розмір

shl ebx, 1 ;

invoke GlobalAlloc, GMEM_FIXED, ebx ; виділення пам’яті з купи

or eax, eax ; якщо не вдалося

jz _break2 ; то на вихід

mov head, eax ; зберегти вказівник на масив

mov arrsize, bx ; зберегти розмір масиву

pop ebx ; відновити ebx

ret ; вихід, в eax – вказівник на масив

_break1: ; аварійний вихід

mov eax, ERR_ARR_EXIST ;

pop ebx ; відновити eax

ret ; вийти

_break2:

mov eax, ERR_NO_MEM

pop ebx

ret

CreateArr endp

; встановлення байта номеру idx в значення val

SetArrValue proc idx :WORD, val :WORD

mov eax, head ; завантажити адресу масиву в eax

or eax, eax ; масив існує?

jz _break1 ; немає – на вихід

movzx ebx, arrsize ; завантажити довжину в ebx

movzx ecx, idx ; завантажити індекс в есх

cmp ebx, ecx ; індекс допустимий?

jb _break2 ; немає – на вихід

shl ecx, 1

add eax, ecx ; отримати вказівник на елемент

mov bx, val ; занести значення в bx

mov word ptr [eax], bx ; зберегти значення в масиві

and eax, 1 ; встановити 1 в eax (TRUE)

ret ; вийти

_break1: ; аварійний вихід №1

mov eax, ERR_ARR_NO_EXIST ; код помилки – масив не існує

ret ; вийти

_break2: ; аварійний вихід №2

mov eax, ERR_OUT_OF_RANGE ; код помилки – вихід за межі масиву

ret ; вийти

SetArrValue endp

; набути значення елемента масиву з індексом idx

GetArrValue proc idx :WORD

push ebx ; зберегти ebx

mov eax, head ; завантажити вказівник на масив

or eax, eax ; масив існує?

jz _break1 ; немає – на вихід

movzx ebx, arrsize ; занести в ebx розмір масиву

movzx ecx, idx ; занести в есх індекс елемента

cmp ebx, ecx ; індекс укладається в рамки масиву?

jb _break2 ; немає – на вихід

shl ecx, 1 ;

add eax, ecx ; обчислити вказівник на елемент

movzx eax, word ptr [eax] ; занести елемент еах

pop ebx ; відновити ebx

ret ; вихід

_break1: ; аварійний вихід №1

mov eax, ERR_ARR_NO_EXIST ; код помилки – масив не існує

pop ebx ; відновити ebx

ret ;

_break2: ; аварійний вихід №2

mov eax, ERR_OUT_OF_RANGE ; код помилки – вихід за межі масиву

pop ebx ; відновити ebx

ret ; вийти

GetArrValue endp

; повертає розмір масиву в байтах. Якщо масив не існує, поверне 0

GetArrSize proc

movzx eax, arrsize ; занести розмір в еах

ret ; повернутися

GetArrSize endp

; заповнення масиву значенням val

FillArr proc val :WORD

mov eax, head ; занести вказівник на масив еах

or eax, eax ; масив існує?

jz _break ; немає – на вихід

push ebx ; зберегти

push ecx ; регістри в стеку

mov bx, val ; завантажити значення

movzx ecx, arrsize ; завантажити розмір масиву

_cycle: ; цикл заповнення

mov word ptr [eax], bx ; заповнити поточний елемент

inc eax ; перейти на наступний елемент

loop _cycle ; продовжити цикл

pop ecx ; відновити

pop ebx ; регістри із стека

and eax, 1 ; встановити 1 в eax (TRUE)

ret ; вихід

_break: ; аварійний вихід

mov eax, ERR_ARR_NO_EXIST ; код помилки – масив не існує

ret ; вихід

FillArr endp

DisposeArr proc ; видалення масиву

mov eax, head ; завантажити адреси початку масиву

or eax, eax ; масив існує?

jz _break ; немає – на вихід

invoke GlobalFree, eax ; так – видаляємо його

ret ; вихід

_break:

mov eax, ERR_ARR_NO_EXIST ; код помилки – масив не існує

ret ; вихід

DisposeArr endp

end DllEntry

Лістинг 15.8.Файл ConsoleO:

title Рисований О.М. та Назаровець Д.В., НТУ “ХПІ”, КІТ-27А

.386

.model flat,stdcall

option casemap:none

include masm32includewindows.inc

include masm32includekernel32.inc

include masm32includemasm32.inc

includelib masm32libkernel32.lib

includelib masm32libmasm32.lib

includelib masm32libuser32.lib