Для вычисления контрольного кода чётности будем использовать подсчёт битов в исходных элементах массива. С учётом ёмкости контрольного числа в 32 бита для массивов, размер которых не более 32 байта, используем битовое маскирование результирующего кода, а для массивов большего размера – подсчёт байтов, количество битов в которых чётно.
Блок-схема алгоритма имеет вид:
Из блок-схемы видно, что алгоритм подсчета контрольного кода четности использует два вложенных алгоритма: битового маскирования результата и подсчета количества байтов с четным количеством бит.
Традиционно массивы в графическом виде представляются в виде последовательности элементов, упорядоченных слева направо, а двоичные числа – справа налево:
Такое представление затрудняет понимание общей природы массивов и двоичных чисел. Достаточно упорядочить элементы массива и биты двоичного числа в одном направлении и, возможно, ввести нумерацию элементов массива с 0, чтобы увидеть, что по сути двоичное число – это обычный массив, элементами которого являются биты:
С учётом этого факта несложно построить алгоритм формирования контрольного числа, в котором последовательно будут проверяться элементы массива и в зависимости от результата проверки соответствующие биты контрольного числа будут устанавливаться в 1 или 0. Текстовое описание такого алгоритма:
1. Обнулить контрольное число
2. Установить указатель на последний элемент массива
3. Проверить на четность очередной элемент массива
4. Если чётный, установить флаг переноса в 1. Иначе установить в 0.
5. Выполнить битовый сдвиг контрольного числа с учётом флага переноса на 1 разряд в сторону старших разрядов.
6. Если все элементы массива обработаны, закончить.
7. Иначе перейти к предыдущему элементу массива и продолжить с пункта 3
Графическое представление алгоритма в виде блок-схемы:
Подсчёт количества байтов с чётным количеством бит реализовать достаточно просто:
Обнулить результат
Установить указатель на первый элемент массива
Проверить на чётность очередной элемент массива
Если чётный, добавить к результату 1
Если все элементы массива обработаны, закончить
Иначе перейти к предыдущему элементу массива и продолжить с пункта 3
Графическое представление алгоритма в виде блок-схемы:
Для проверки отдельных байтов массива на чётность удобно использовать команду test, которая вычисляет результат действия побитового логического «И» над приёмником и источником и устанавливает флаги SF, ZF и PF в соответствии с результатом, при этом результат не сохраняется. Если после выполнения команды test флаг PF будет установлен в 1, количество бит в байте чётно.
1. Вывод на экран текстовых сообщений
Наиболее простое решение – использовать для вывода текста функцию 9hDOS (прерывание INT21h), которая получает адрес строки для вывода через регистр dx, причем строка должна завершаться символом $.
2. Преобразование числа к строковому виду
В качестве основы используем реализацию алгоритма преобразования числа
в десятичное представление. [4, 201]
Суть используемого алгоритма заключается в выполнении следующих действий:
Разделить исходное число на 10.
Добавить к остатку ASCII-код нуля.
Записать полученную цифру в стек.
Увеличить счётчик цифр.
Если ещё есть что делить, продолжить с п.1
По количеству цифр: извлечь цифру из стека и дописать в конец результирующей строки.
Дописать в конец результирующей строки символ $.
Базовый алгоритм рассчитан на работу с 16-разрядными числами. Так как в разрабатываемой программе код чётности будет представлен в виде 32-разрядного числа, модифицируем исходный алгоритм для работы с двойными словами. Такая модификация выполняется достаточно просто заменой в исходном коде наименований регистров: ax на eax, bx на ebx, dx на edx.
С учётом того, что разрабатываемая программа является тестовой и не требует специфического управления клавиатурой, ввод с клавиатуры запрограммируем с помощью функций DOS.
Соответственно, алгоритм ввода числа в виде текстового описания будет выглядеть следующим образом:
Подготовить буфер ввода данных
Вызвать функцию DOS 0Ah для ввода с клавиатуры
Проверить количество введенных символов. Если = 0, ошибка
Выполнить проверку ввода десятичного числа
В целях обеспечения возможности повторного использования, а также для более простого понимания алгоритма в нём выделен отдельный подалгоритм проверки ввода десятичного числа.
Алгоритм проверки ввода десятичного числа можно представить в виде текстового описания:
1) Проверить количество символов. Если больше 5, ошибка
2) Обнулить результат
3) Читать символ из буфера ввода
4) Если код символа меньше «0» или больше «9», ошибка
5) Получить промежуточный результат вычитанием кода символа «0»
6) Умножить на 10 результат
7) Если в ходе умножения получено переполнение, ошибка
8) Прибавить к результату промежуточный результат
9) Если обработаны не все символы, перейти к следующему символу. Переход к п.3
10) Если обработаны все символы, вернуть результат
ПРИЛОЖЕНИЕ 1. ЛИСТИНГ ПРОГРАММЫ НА ЯЗЫКЕ ASSEBLER
. modelsmall; модель памяти
. stack 100h; сегмент стека
. data; сегмент данных
MAXDIGITSequ16; максимальное количество символов
; для ввода целого числа
BUFFERSIZEequMAXDIGITS + 2 + 1; объем буфера ввода
bufferdbBUFFERSIZEdup(?) ; буфер ввода
ARRAYSIZEequ4; количество элементов массива / 4
dataArraydb4*ARRAYSIZEdup(?) ; массив данных
changeValueNodb? ; номер элемента массива для изменения
changeValuedb? ; новое значение элемента массива
parityCodedd? ; код четности
; переменные для работы генератора случайных чисел
rand_add69621
rand_mdd7FFFFFFFh
seeddd-1
;
; Сообщения для вывода на экран
;
welcomeMsgdb"Welcome to array parity checker. ", 0Dh, 0Ah
db0Dh, 0Ah
db"This program generates array with random values and calculates its parity code. ", 0Dh, 0Ah
db"Then you can change any array value and program will check parity code. ", 0Dh, 0Ah
db"Enjoy! ", 0Dh, 0Ah
db0Dh, 0Ah
db0Dh, 0Ah, "$"
errorMsgdb"Parity check error. ", 0Dh, 0Ah, "$"
okMsgdb"Parity check ok. ", 0Dh, 0Ah, "$"
dataMsgdb"Array data: $"
codeMsgdb"Parity code: $"
numberMsgdb"Enter array item number to change (0. .15): $"
valueMsgdb"Enter new value (0. .255): $"
uncorrectMsgdb"Incorrect number. $"
writelndb0Dh, 0Ah, "$"
byeMsgdb"Bye! ", 0Dh, 0Ah, "$"
itemSeparatordb" $"
. code; сегмент кода
.386; программа для процессора 80386
; - ----------------------------------------------------------------------------------------
; Главная программа
; - ---------------------------------------------------------------------------------------
start: movax, @data
movds, ax; настройка регистров
moves, ax;
andsp, not 3; выравнивание границы стека
; во избежание ошибки доступа к памяти
movax, 0003h; видеорежим 3, очистка экрана и установка курсора
int10h
movdx, offset welcomeMsg; вывести приветствие
callshowString
callfillArray; заполнить массив случайными данными
movcx, ARRAYSIZE * 4; рассчитать контрольное число
; и записать в parityCode
movsi, offset dataArray
movdi, offset parityCode
callsaveParityCode
callshowArray; вывести значения элементов массива
moveax, parityCode
callshowParityCode
callinputValueNo; запросить у пользователя номер
; элемента массива для изменения
callinputValue; запросить у пользователя новое значение
; элемента массива
callchangeItem; изменить значение элемента массива
callshowArray; вывести значения элементов массива
callgetParityCode; рассчитать контрольное число
callshowParityCode
cmpparityCode, eax
jeparityCode_notChanged
parityCode_Changed:
movdx, offset errorMsg; сообщение об изменении кода четности
callshowString
jmpmain_exit
parityCode_notChanged:
movdx, offset okMsg; сообщение о неизменности кода четности
callshowString
main_exit:
callwriteLine; перевод строки
callwriteLine; перевод строки
movdx, offset byeMsg; до свидания
callshowString
callwriteLine; перевод строки
movax, 4c00h; завершение выполнения программы
int21h
; - ---------------------------------------------------------------------------------------
;
; Заполнить массив случайными данными.
;
; Параметры: нет
;
; Возвращаемое значение: нет
;
; Модификация регистров: нет
;
; Глобальные переменные:
; [ref] dataArray - массив данных для заполнения
; [in] ARRAYSIZE - размер массива
;
; - ---------------------------------------------------------------------------------------
fillArrayproc
pusheax
pushcx
pushedi
movedi, offset dataArray; указатель на массив данных
movcx, ARRAYSIZE; размер массива
fillArray_loop:
callrand; генерировать случайное число в eax
cld; направление записи - вперед
stosd; записать в очередной элемент массива
loopfillArray_loop
popedi
popcx
popeax
ret
fillArrayendp
; - ----------------------------------------------------------------------------------------
; Генерация случайного числа.
;
; Параметры: нет
;
; Возвращаемое значение:
; eax - случайное положительное 32-битное число (от 0 до 2^31 - 2)
;
; Модификация регистров:
; eax
;
; Глобальные переменные:
; [in] rand_a – коэффициент a
; [in] rand_m – коэффициент m
; [ref] seed – последнее случайное число