Конкатенація строк Assembler
Конкатенація строк Assembler
ВСТУП Метою курсової роботи було отримання необхідних навичок для програмування на машино-орієнтованій мові Асемблер. У перший частині роботи згідно за варіантом завдання були розроблені граф-схеми алгоритмів, які допомогли наглядно представити задачу, розбити її на декілька менших задач та опанувати логіку завдання, що дало змогу перейти до розробки кода, тобто до другої частини. При розробці кода був використан компілятор MASM32 v8.2. Полний код програми можна побачити у додатку до курсової роботи. Також приведено пояснення коду, якщо його опанування викликало якісь труднощі та примір роботи функції зі скріншотами. Інструкція користувача дає знання як коректно ввести результати та отримати правильний результат. 1 ПРОГРАМА ДЛЯ АРИФМЕТИЧНИХ ОБЧИСЛЕНЬ 1.1 Граф-схема алгоритму обчислювання функції Так як я маю тринадцятий номер за журналом, то моєму варіанту відповідає наступна система: ,если a > b Y =-2,если a = b ,если a < b Згідно цього розробляється ГСА алгоритму, яка буде показувати необхідну реалізацію(Рис 1.1). Рис. 1.1 1.2 Граф-схема алгоритму строкової функції Номер по списку за журналом 13, тобто завдання звучить таким чином. "Ввести 2 строки символов. Произвести их конкатенацию (объединение)." Для цього завдання була разраблона граф-схема, яка представлена на рис 1.2. Рис 1.2 2 . РЕАЛІЗАЦІЯ ПРОГРАМИ 2.1 Опис коду програми На основі алгоритмів, яки були приведені у пункті 1 був розроблен код, який послідовно обробляє дві функції, тобто спочатку математичну, а потім строкову. Арифметична функція називається arithm, а строкова string, але спочатку про код програми. Програма починається з .486 .model flat, stdcall option casemap :none include \masm32\include\windows.inc include \masm32\macros\macros.asm include \masm32\include\masm32.inc include \masm32\include\gdi32.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc include \masm32\include\fpu.inc includelib \masm32\lib\masm32.lib includelib \masm32\lib\gdi32.lib includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib includelib \masm32\lib\fpu.lib Цей код визначає інструкції, які може використовувати компялітор під час створення низькорівневого коду програми. Директива include визначає які бібліотекі необхідно підключити, щоб програма могла користуватися необхідними функціями. На приклад, include \masm32\include\fpu.inc має значення, тому використовуються математичні функції сопроцесора. Докладніший опис бібліотек можна знайти у інструкції компілятора. Наступним йде оголошення сегменту ініціалізованих та не ініціалізованих даних, тобто констант та змінних, які використовують у програмі. .data strNum1 db "Type the the first number: ", 0 strNum2 db "Type the second number: ", 0 strArithm db "The result of fucntion: ", 0 strType1 db "Type the first string: ", 0 strType2 db "Type the second string: ", 0 strResult db "The result of concationation: ", 0 strZeroDiv db "Zero divide is forbidden. Function is undefined ", 13, 10 cons dq -2.0 one dq 1.0 y dq 0 .data? buff db 128 dup(?) str1 db 64 dup(?) str2 db 64 dup(?) a dq ? b dq ? На приклад, strNum1 db "Type the the first number: ", 0 це строка, яка ініціалізована текстом, та має свій розмір. Кожен символ цієї строки має розмір 1 байт. a dq ? Показує також, що у програмі використовується змінна, яка має розмір 4 слова та не визначена, тому що це не біло необхідно. Наступним йде сегмент коду, він починається з мітки start:, у ньому визивається функція математичного обчислювання arithm, print chr$(13, 10) робе перевод строки, а call string визиває строкову функцію, тобто її реалізацію. Функція arithm починається з arithm proc LOCAL hInput :DWORD LOCAL hOutPut :DWORD LOCAL nRead1 :DWORD LOCAL nRead2 :DWORD LOCAL str3[15] :BYTE arithm proc є оголошенням функція, тобто текстом, який каже, зо почалась функція, яка має бути викликаною десь у коді. Наступний текст - оглошення локальних змінних, що є особливістю компілятора. Тільки у MASM 32 можна створити локальні змінні, які розташовуються у стеку. Доступ до цих змінних виконується за допомогою ADDR, тобто макроса, який повертає їх адрес розташування у стеку. Також хочеться особливо відмітити LOCAL hInput :DWORD . Ця змінна потрібна, щоб організувати консольний ввод змінних та строк. Розмір змінної два слова. Змінна LOCAL hOutPut :DWORD також тримає хендл, але для консольного вивода. Змінні LOCAL nRead1 :DWORD LOCAL nRead2 :DWORD мають однакове призначення, тобто воні використовують у однієї і тієїж функції. У них записується результат зчитування строки з консолі, тобто кількість байт, яка була прочитана. Для того щоб реалізувати вивод та ввод у програмі потрібно спочатку отримати хендли для ввода та вивода, тобто проініціалізувати змінні LOCAL hOutPut :DWORD LOCAL hInput :DWORD. Це робиться завдяки invoke GetStdHandle, STD_OUTPUT_HANDLE mov hOutPut, eax invoke GetStdHandle, STD_INPUT_HANDLE mov hInput , eax Функція GetStdHandle , яка отримає аргумент STD_OUTPUT_HANDLE розташовує у регістрі eax хендл для вивода тексти у консоль. mov hOutPut, eax ініціалізує змінну hOutPut. Такая ж операція робиться для hInput але з метою отримати хендл для вивода. invoke szLen, offset strNum1 invoke WriteConsole, hOutPut, offset strNum1, eax, NULL, NULL invoke ReadConsole, hInput , ADDR str1, 10d, ADDR nRead1, NULL Функція szLen отримує як аргумент строку з сегменту даних, щоб знайти її длину, яка розташовуєть у регістрі eax. Функція WriteConsole використовується для вивода на консоль. Нижче приведен її прототип, взяти з MSDN. BOOL WriteConsole( HANDLE hConsoleOutput, // handle to screen buffer CONST VOID *lpBuffer, // write buffer DWORD nNumberOfCharsToWrite, // number of characters to write LPDWORD lpNumberOfCharsWritten, // number of characters written LPVOID lpReserved // reserved); Наступна функція ReadConsole , яка вводе строку str1. Її прототип також приведен нижче. BOOL ReadConsole( HANDLE hConsoleInput, // handle to console input buffer LPVOID lpBuffer, // data buffer DWORD nNumberOfCharsToRead, // number of characters to read LPDWORD lpNumberOfCharsRead, // number of characters read LPVOID lpReserved // reserved); Бачимо, що у цих двух фунціях використовуються змінні, які були пояснені раніше, тобто добре пояснення не потрібне тепер. Треба пояснити код, який використовує бібліотечні функції invoke StrToFloat, ADDR str1, offset a Ця функція має за мету перевод строки у змінну формата qword, яка може потім бути використаною FPU. Перейдемо тепер до основного коду функція, до коду, який обчислює функцію. fld b fld a fcom y fstsw ax sahf jz Divide fxch fcom y fstsw ax sahf jz Divide fld b загружає до стека FPU значення змінної b. fcom y зрівнює цю змінну з нулем, fstsw ax sahf повертає флагі, яки булі загублені під час перевірки. jz Divide робить умовний перехід на метку Divide, якщо операнд буде рівен нулю. Це робиться для того, щоб уникнути ділення на нуль. Далі йде ffree st(1) ffree st(0) fstsw ax sahf fld b fld a fcom fstsw ax sahf je equal ja greater jb lower jmp theend Команда ffree вигружає зі стека операнди, щоб потім загрузити їх знову. Далі йде такий ж логічних русловій, але тепер перехідов більше. je equal якщо операнди рівни між собою, тоді перейти на мітку equal. Також для ja, jb, але вони перевіряються умови більше або менше відповідно. Тепер подрібніше о кожній з меток. equal: fld cons fstp y jmp theend У цій метці до стеку загружається -2, потім ця константа передається до змінної y. По закінченню операції управління переходе до мітки theend. greater: print chr$("A IS GREATER", 13, 10) fxch fdiv fld one fadd fstp y jmp theend У цьому блоці кода виконується ділення b на a. Яке досягається, командами fxch, fdiv. Перша потрібна для зміни міст a и b, друга діле їх друг на друга. У кінці до результату прибавляється константа 1, та усе це грузиться до y. До закінченню управління передається до мітки theend. lower: print chr$("A IS LOWER", 13, 10) fsub st(0), st(1) fld a fxch fdiv st(0), st(1) fstp y jmp theend Мітка отримує управління, якщо a < b. У цій мітці у першу чергу пишеться у консоль, що операнд а таки менший за b. Потім від a віднімається b, загружається знову а, тому ще попереднє значення а находиться вже дальше по стеку. Та ділиться результат віднімання на а. Усе це розтащовуєть у змінній y. theend: invoke FloatToStr, y, ADDR str3 xor eax, eax invoke szLen, offset strArithm invoke WriteConsole, hOutPut, offset strArithm, eax, NULL, NULL xor eax, eax invoke szLen, ADDR str3 mov nRead1, eax invoke WriteConsole, hOutPut, ADDR str3, nRead1, NULL, NULL ret Це остання мітка, є логічним кінцем програми. Треба тільки пояснити invoke FloatToStr, y, ADDR str3 ця функція преобразує число у строку, яка потім буде виведена на консоль. Тепер перейдемо до наступної функції string. Допоміжна части якої, така ж як і у попередньої функції. Особої уваги вимогає invoke strCat, offset buff, offset str1. Ця функція викликає функцію конкатенації, яка буде приведена нижче. strCat proc lpszSource:DWORD, lpszAdd:DWORD push edi invoke szLen, lpszSource mov edi, lpszSource mov ecx, lpszAdd add edi, eax ; set write starting position xor edx, edx ; zero index xor eax, eax ; avoid stall with following AL reads and writes mov [edi-2], byte ptr 20h mov [edi-1], byte ptr 20h @@: mov al, [ecx+edx] ; write append string to end of source mov [edi+edx], al add edx, 1 test al, al ; exit when terminator is written jne @B pop edi mov eax, lpszSource ret strCat endp Ближче познайомимося з кодом цієї функції. У регістр edi загружається ісходна строка, до якої буде конкатинуватися інша строка, яка знаходиться у регістрі ecx. Регістр edi зміщується за розміром ісходної строки, щоб почати писати до неї з кінця. Далі йде цикл, який посимвольно додає до строки байти з іншої строки. Признаком кінця циклу є нулевий термінатор. По виходу з цікла регістр edi востанавлюється . 2.2 Приклад виконання Далі неведено декілька прикладів виконання. Наприклад якщо операнд а буде рівен 0(рис. 2. 1) Рис. 2.1 Також примір, якщо b буде рівно 0.(Рис. 2.2) Рис. 2.2 Тепер приклад, якщо а и б рівні(рис. 2.3). Рис. 2.3 Тепер якщо а більше б(рис. 2.4). Рис. 2.4 Та останній приклад, якщо а менше б(рис. 2.5) Рис. 2.5 ВИСНОВОК Згідно з завданням до курсової роботи, було розроблено три функції на мові Асемблеру, що дозволило зробити їх більш компактними та такими, що є більш ефективними з точки зору не лише кількості операцій, але й за рахунок більшого використання високошвидкісних регістрів замість пам'яті, де це було можливо. СПИСОК ЛІТЕРАТУРИ 1. Абель П."Язык Ассемблер для IBM РС и программирование". 2. Нортон П."Язык Ассемблера для IBM PC". 3. Юров, Хорошенко "Assembler: учебный курс". Додаток .486 .model flat, stdcall option casemap :none include \masm32\include\windows.inc include \masm32\macros\macros.asm include \masm32\include\masm32.inc include \masm32\include\gdi32.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc include \masm32\include\fpu.inc includelib \masm32\lib\masm32.lib includelib \masm32\lib\gdi32.lib includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib includelib \masm32\lib\fpu.lib strCat PROTO lpszSource:DWORD, lpszAdd:DWORD .data strNum1 db "Type the the first number: ", 0 strNum2 db "Type the second number: ", 0 strArithm db "The result of fucntion: ", 0 strType1 db "Type the first string: ", 0 strType2 db "Type the second string: ", 0 strResult db "The result of concationation: ", 0 strZeroDiv db "Zero divide is forbidden. Function is undefined ", 13, 10 cons dq -2.0 one dq 1.0 y dq 0 .data? buff db 128 dup(?) str1 db 64 dup(?) str2 db 64 dup(?) a dq ? b dq ? .code start: call arithm print chr$(13,10) call string invoke Sleep, 2000d exit arithm proc LOCAL hInput :DWORD LOCAL hOutPut :DWORD LOCAL nRead1 :DWORD LOCAL nRead2 :DWORD LOCAL str3[15] :BYTE invoke GetStdHandle, STD_OUTPUT_HANDLE mov hOutPut, eax invoke GetStdHandle, STD_INPUT_HANDLE mov hInput , eax invoke szLen, offset strNum1 invoke WriteConsole, hOutPut, offset strNum1, eax, NULL, NULL invoke ReadConsole, hInput , ADDR str1, 10d, ADDR nRead1, NULL xor eax, eax invoke szLen, offset strNum2 invoke WriteConsole, hOutPut, offset strNum2, eax, NULL, NULL invoke ReadConsole, hInput, ADDR str2, 10d, ADDR nRead2, NULL xor eax, eax invoke StrToFloat, ADDR str1, offset a invoke StrToFloat, ADDR str2, offset b fld b fld a fcom y fstsw ax sahf jz Divide fxch fcom y fstsw ax sahf jz Divide ffree st(1) ffree st(0) fstsw ax sahf fld b fld a fcom fstsw ax sahf je equal ja greater jb lower jmp theend equal: fld cons fstp y jmp theend greater: print chr$("A IS GREATER", 13, 10) fxch fdiv fld one fadd fstp y jmp theend lower: print chr$("A IS LOWER", 13, 10) fsub st(0), st(1) fld a fxch fdiv st(0), st(1) fstp y jmp theend theend: invoke FloatToStr, y, ADDR str3 xor eax, eax invoke szLen, offset strArithm invoke WriteConsole, hOutPut, offset strArithm, eax, NULL, NULL xor eax, eax invoke szLen, ADDR str3 mov nRead1, eax invoke WriteConsole, hOutPut, ADDR str3, nRead1, NULL, NULL ret Divide: invoke szLen, offset strZeroDiv invoke WriteConsole, hOutPut,offset strZeroDiv, eax, NULL, NULL arithm endp string proc LOCAL hInput :DWORD ;handle of console input LOCAL hOutPut :DWORD ;handle of output LOCAL nRead1 :DWORD ;number of bytes read LOCAL nRead2 :DWORD ;the same invoke GetStdHandle, STD_OUTPUT_HANDLE mov hOutPut, eax invoke WriteConsole, hOutPut, offset strType1, 24d, NULL, NULL invoke GetStdHandle, STD_INPUT_HANDLE mov hInput, eax invoke ReadConsole, hInput, ADDR str1, 64d, ADDR nRead1, NULL invoke WriteConsole, hOutPut, offset strType2, 25d, NULL, NULL invoke ReadConsole, hInput, ADDR str2, 64d, ADDR nRead2, NULL invoke strCat, offset buff, offset str1 invoke strCat, offset buff, offset str2 xor eax, eax invoke szLen, offset buff mov nRead2, eax invoke WriteConsole, hOutPut, offset strResult, 31d, NULL, NULL invoke WriteConsole, hOutPut, offset buff, nRead2, NULL, NULL ret string endp strCat proc lpszSource:DWORD, lpszAdd:DWORD push edi invoke szLen, lpszSource mov edi, lpszSource mov ecx, lpszAdd add edi, eax ; set write starting position xor edx, edx ; zero index xor eax, eax ; avoid stall with following AL reads and writes mov [edi-2], byte ptr 20h mov [edi-1], byte ptr 20h @@: mov al, [ecx+edx] ; write append string to end of source mov [edi+edx], al add edx, 1 test al, al ; exit when terminator is written jne @B pop edi mov eax, lpszSource ret strCat endp end start
|