ВСТУП
Метоюкурсової роботи було отриманнянеобхідних навичок для програмування на машино-орієнтованій мові Асемблер. Уперший частині роботи згідно за варіантом завдання були розроблені граф-схемиалгоритмів, які допомогли наглядно представити задачу, розбити її на декількаменших задач та опанувати логіку завдання, що дало змогу перейти до розробкикода, тобто до другої частини. При розробці кода був використан компілятор MASM32v8.2.Полний код програми можна побачити у додатку до курсовоїроботи. Також приведено пояснення коду, якщо його опанування викликало якісьтруднощі та примір роботи функції зі скріншотами. Інструкція користувача даєзнання як коректно ввести результати та отримати правильний результат.
1ПРОГРАМА ДЛЯ АРИФМЕТИЧНИХ ОБЧИСЛЕНЬ
1.1 Граф-схема алгоритму обчислюванняфункції
Такяк я маю тринадцятий номер за журналом, то моєму варіанту відповідає наступна система:
/>/>, еслиa >b
Y =–2, еслиa =b
/>, еслиa
Згідноцього розробляється ГСА алгоритму, яка буде показуватинеобхідну реалізацію(Рис 1.1).
/>
Рис.1.1
1.2Граф-схема алгоритму строкової функції
Номерпо списку за журналом 13, тобто завдання звучить таким чином.
«Ввести2 строки символов. Произвести их конкатенацию (объединение).»
Дляцього завдання була разраблона граф-схема, яка представлена на рис 1.2.
/>
Рис1.2
2 .РЕАЛІЗАЦІЯПРОГРАМИ
2.1 Опис коду програми
Наоснові алгоритмів, яки були приведені у пункті 1 був розроблен код, якийпослідовно обробляє дві функції, тобто спочатку математичну, а потім строкову. Арифметичнафункція називається arithm,астрокова string, але спочаткупро код програми. Програма починається з
.486
.modelflat, stdcall
optioncasemap :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
strNum1db «Type the the first number: », 0
strNum2db «Type the second number: », 0
strArithmdb «The result of fucntion: », 0
strType1db «Type the first string: », 0
strType2db «Type the second string: », 0
strResultdb «The result of concationation: », 0
strZeroDivdb «Zero divide is forbidden. Function is undefined », 13, 10
consdq -2.0
onedq 1.0
ydq 0
.data?
buffdb 128 dup(?)
str1db 64 dup(?)
str2db 64 dup(?)
adq ?
bdq ?
Наприклад, strNum1 db «Type the the firstnumber: », 0 це строка, яка ініціалізованатекстом, та має свій розмір. Кожен символ цієї строки має розмір 1 байт. adq? Показує також, що у програмі використовуєтьсязмінна, яка має розмір 4 слова та не визначена, тому що це не біло необхідно.Наступним йде сегмент коду, він починається з мітки start:,у ньому визивається функція математичного обчислювання arithm,print chr$(13,10) робе перевод строки, а callstring визиває строковуфункцію, тобто її реалізацію. Функція arithmпочинається з
arithmproc
LOCALhInput :DWORD
LOCALhOutPut :DWORD
LOCALnRead1 :DWORD
LOCALnRead2 :DWORD
LOCALstr3[15] :BYTE
arithmproc є оголошенням функція, тобто текстом, який каже, зопочалась функція, яка має бути викликаною десь у коді. Наступний текст –оглошення локальних змінних, що є особливістю компілятора. Тільки у MASM32 можна створити локальні змінні, які розташовуються у стеку. Доступ до цихзмінних виконується за допомогою ADDR,тобто макроса, який повертає їх адрес розташування у стеку. Також хочетьсяособливо відмітити LOCAL hInput :DWORD.Ця змінна потрібна, щоб організувати консольний ввод змінних та строк. Розмірзмінної два слова. Змінна LOCAL hOutPut :DWORD такожтримає хендл, але для консольного вивода. Змінні LOCALnRead1 :DWORD LOCAL nRead2 :DWORD мають однаковепризначення, тобто воні використовують у однієї і тієїж функції. У нихзаписується результат зчитування строки з консолі, тобто кількість байт, якабула прочитана.
Длятого щоб реалізувати вивод та ввод у програмі потрібно спочатку отримати хендлидля ввода та вивода, тобто проініціалізувати змінніLOCAL hOutPut :DWORD LOCAL hInput :DWORD. Церобиться завдяки
invokeGetStdHandle, STD_OUTPUT_HANDLE
movhOutPut, eax
invokeGetStdHandle, STD_INPUT_HANDLE
movhInput, eax
ФункціяGetStdHandle,яка отримає аргумент STD_OUTPUT_HANDLE розташовуєу регістрі eax хендлдля вивода тексти у консоль. mov hOutPut, eax ініціалізуєзмінну hOutPut. Такаяж операція робиться для hInput алез метою отримати хендл для вивода.
invokeszLen, offset strNum1
invokeWriteConsole, hOutPut, offset strNum1, eax, NULL, NULL
invokeReadConsole, 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);
Бачимо,що у цих двух фунціях використовуються змінні, які були пояснені раніше, тобтодобре пояснення не потрібне тепер. Треба пояснити код, який використовуєбібліотечні функції
invokeStrToFloat, ADDR str1, offset a
Цяфункція має за мету перевод строки у змінну формата qword,якаможе потім бути використаною FPU.Перейдемотепер до основного коду функція, до коду, який обчислює функцію.
fldb
flda
fcomy
fstswax
sahf
jzDivide
fxch
fcomy
fstswax
sahfjz Divide
fldb загружає до стека FPUзначення змінної b.fcomy зрівнює цю змінну з нулем, fstswax sahf повертає флагі, яки булі загублені підчас перевірки. jz Divide робитьумовний перехід на метку Divide,якщо операнд буде рівен нулю. Це робиться для того, щоб уникнути ділення нануль. Далі йде
ffreest(1)
ffreest(0)
fstswax
sahf
fldb
flda
fcom
fstswax
sahf
jeequal
jagreater
jblower
jmptheend
Командаffreeвигружаєзі стека операнди, щоб потім загрузити їх знову. Далі йде такий ж логічнихрусловій, але тепер перехідов більше. je equal якщооперанди рівни між собою, тоді перейти на мітку equal.Такождля ja, jb,алевони перевіряються умови більше або менше відповідно. Тепер подрібніше о кожнійз меток.
equal:
fldcons
fstpy
jmptheend
Уцій метці до стеку загружається -2, потім ця константа передається до змінної y.По закінченню операції управління переходе до мітки theend.
greater:
printchr$(«A IS GREATER», 13, 10)
fxch
fdiv
fldone
fadd
fstpy
jmptheend
Уцьому блоці кода виконується ділення 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
theend:
invokeFloatToStr, y, ADDR str3
xoreax, eax
invokeszLen, offset strArithm
invokeWriteConsole, hOutPut, offset strArithm, eax, NULL, NULL
xoreax, eax
invokeszLen, ADDR str3
movnRead1, eax
invokeWriteConsole, hOutPut, ADDR str3, nRead1, NULL, NULL
ret
Цеостання мітка, є логічним кінцем програми. Треба тільки пояснити invokeFloatToStr, y, ADDR str3 ця функція преобразуєчисло у строку, яка потім буде виведена на консоль.
Теперперейдемо до наступної функції string.Допоміжначасти якої, така ж як і у попередньої функції. Особої уваги вимогає invokestrCat, offset buff, offset str1. Ця функція викликаєфункцію конкатенації, яка буде приведена нижче.
strCatproc lpszSource:DWORD, lpszAdd:DWORD
pushedi
invokeszLen, lpszSource
movedi, lpszSource
movecx, lpszAdd
addedi, eax; set write starting position
xoredx, edx; zero index
xoreax, eax; avoid stall with following AL reads and writes
mov[edi-2], byte ptr 20h
mov[edi-1], byte ptr 20h
@@:
moval, [ecx+edx]; write append string to end of source
mov[edi+edx], al
addedx, 1
testal, al; exit when terminator is written
jne@B
popedi
moveax, lpszSource
ret
strCatendp
Ближчепознайомимося з кодом цієї функції. У регістр 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
.modelflat, stdcall
optioncasemap :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
strCatPROTO lpszSource:DWORD, lpszAdd:DWORD
.data
strNum1db «Type the the first number: », 0
strNum2db «Type the second number: », 0
strArithmdb «The result of fucntion: », 0
strType1db «Type the first string: », 0
strType2db «Type the second string: », 0
strResultdb «The result of concationation: », 0
strZeroDivdb «Zero divide is forbidden. Function is undefined », 13, 10
consdq -2.0
onedq 1.0
ydq 0
.data?
buffdb 128 dup(?)
str1db 64 dup(?)
str2db 64 dup(?)
adq ?
bdq ?
.code
start:
callarithm
printchr$(13,10)
callstring
invokeSleep, 2000d
exit
arithmproc
LOCALhInput :DWORD
LOCALhOutPut :DWORD
LOCALnRead1 :DWORD
LOCALnRead2 :DWORD
LOCALstr3[15] :BYTE
invokeGetStdHandle, STD_OUTPUT_HANDLE
movhOutPut, eax
invokeGetStdHandle, STD_INPUT_HANDLE
movhInput, eax
invokeszLen, offset strNum1
invokeWriteConsole, hOutPut, offset strNum1, eax, NULL, NULL
invokeReadConsole, hInput, ADDR str1, 10d, ADDR nRead1, NULL
xoreax, eax
invokeszLen, offset strNum2
invokeWriteConsole, hOutPut, offset strNum2, eax, NULL, NULL
invokeReadConsole, hInput, ADDR str2, 10d, ADDR nRead2, NULL
xoreax, eax
invokeStrToFloat, ADDR str1, offset a
invokeStrToFloat, ADDR str2, offset b
fldb
flda
fcomy
fstswax
sahf
jzDivide
fxch
fcomy
fstswax
sahf
jzDivide
ffreest(1)
ffreest(0)
fstswax
sahf
fldb
flda
fcom
fstswax
sahf
jeequal
jagreater
jblower
jmptheend
equal:
fldcons
fstpy
jmptheend
greater:
printchr$(«A IS GREATER», 13, 10)
fxch
fdiv
fldone
fadd
fstpy
jmptheend
lower:
printchr$(«A IS LOWER», 13, 10)
fsubst(0), st(1)
flda
fxch
fdivst(0), st(1)
fstpy
jmptheend
theend:
invokeFloatToStr, y, ADDR str3
xoreax, eax
invokeszLen, offset strArithm
invokeWriteConsole, hOutPut, offset strArithm, eax, NULL, NULL
xoreax, eax
invokeszLen, ADDR str3
movnRead1, eax
invokeWriteConsole, hOutPut, ADDR str3, nRead1, NULL, NULL
ret
Divide:
invokeszLen, offset strZeroDiv
invokeWriteConsole, hOutPut,offset strZeroDiv, eax, NULL, NULL
arithmendp
stringproc
LOCALhInput :DWORD ;handle of console input
LOCALhOutPut :DWORD ;handle of output
LOCALnRead1 :DWORD ;number of bytes read
LOCALnRead2 :DWORD ;the same
invokeGetStdHandle, STD_OUTPUT_HANDLE
movhOutPut, eax
invokeWriteConsole, hOutPut, offset strType1, 24d, NULL, NULL
invokeGetStdHandle, STD_INPUT_HANDLE
movhInput, eax
invokeReadConsole, hInput, ADDR str1, 64d, ADDR nRead1, NULL
invokeWriteConsole, hOutPut, offset strType2, 25d, NULL, NULL
invokeReadConsole, hInput, ADDR str2, 64d, ADDR nRead2, NULL
invokestrCat, offset buff, offset str1
invokestrCat, offset buff, offset str2
xoreax, eax
invokeszLen, offset buff
movnRead2, eax
invokeWriteConsole, hOutPut, offset strResult, 31d, NULL, NULL
invokeWriteConsole, hOutPut, offset buff, nRead2, NULL, NULL
ret
stringendp
strCatproc lpszSource:DWORD, lpszAdd:DWORD
pushedi
invokeszLen, lpszSource
movedi, lpszSource
movecx, lpszAdd
addedi, eax; set write starting position
xoredx, edx; zero index
xoreax, eax; avoid stall with following AL reads and writes
mov[edi-2], byte ptr 20h
mov[edi-1], byte ptr 20h
@@:
moval, [ecx+edx]; write append string to end of source
mov[edi+edx], al
addedx, 1
testal, al; exit when terminator is written
jne@B
popedi
moveax, lpszSource
ret
strCatendp
endstart