Управление качеством (шпаргалка)
ВСТУП
Метою
курсової роботи було отримання
необхідних навичок для програмування на машино-орієнтованій мові Асемблер. У
перший частині роботи згідно за варіантом завдання були розроблені граф-схеми
алгоритмів, які допомогли наглядно представити задачу, розбити її на декілька
менших задач та опанувати логіку завдання, що дало змогу перейти до розробки
кода, тобто до другої частини. При розробці кода був використан компілятор 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
Функція
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
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
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)
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