Разработка программы обработки информации на языке Си с использованием функций, разработанных на Ассемблере

Тип работы:
Курсовая
Предмет:
Программирование


Узнать стоимость

Детальная информация о работе

Выдержка из работы

ПОЯСНИТЕЛЬНАЯ ЗАПИСКА

к курсовому проекту по теме:

«Разработка программы обработки информации на языке Си с использованием функций, разработанных на Ассемблере»

Введение

Целью выполнения курсового проекта является освоение основ программирования на языках программирования Си и Ассемблер. Язык Си — универсальный язык высокого уровня, является понятным для человека, так как абстрагирован от исполняющих устройств. В курсовом проекте язык Си позволяет осуществлять такие действия как разработка пользовательского интерфейса и работа с файлами без больших затрат средств и времени. Язык Ассемблера необходим там, где требуется непосредственное вмешательство в работу аппаратных средств компьютера на низком уровне обработки информации, поэтому он позволяет получить программу, которая будет максимально использовать возможности процессора и будет компактной и быстрой.

Согласно заданию на курсовое проектирование требуется разработать программу, выполняющую помехоустойчивое кодирование при помощи кода Хемминга (15,11), с графическим пользовательским интерфейсом, работающим под операционную систему Windows. В данном случае для разработки интерфейса и работы с файлами эффективнее использовать язык Си, а обработку информации вести исключительно на Ассемблере.

Помехоустойчивое кодирование используется при передаче и хранении информации для возможности обнаружения и исправления возникающих ошибок. Его суть заключается во внесении избыточности, в результате чего появляются недопустимые комбинации. Получение недопустимой кодовой комбинации говорит о наличии ошибки. По виду недопустимой комбинации можно обнаруживать и исправлять ошибки.

1. Метод обработки информации

В основе кода Хемминга лежит добавление проверочных разрядов в кодовую комбинацию. При искажениях информационные и проверочные разряды используются для определения места ошибки. Код Хэмминга способен исправлять одиночные ошибки.

Код Хэмминга является (n, k) — кодом, где n определяет общее число бит в кодовой комбинации, а k — число информационных бит. Число проверочных бит определяется как r = n — k. Причем значение n удовлетворяет условию n = 2r-1.

Для кода Хэмминга (15,11), имеющего кодовую комбинацию X15X14X13X12X11X10X9X8X7X6X5X4X3X2X1, проверочными разрядами являются X1, X2, X4 и X8. Следует отметить, что в данном случае биты кодовой комбинации нумеруются не с нуля, а с единицы. Проверочные разряды формируются по правилу:

X1 = X3 Е X5 Е X7 ЕX9 Е X11 Е X13 Е X15

X2 = X3 Е X6 Е X7 ЕX10 Е X11 Е X14 Е X15

X4 = X5 Е X6 Е X7 ЕX12 Е X13 Е X14 Е X15

X8 = X9 Е X10 ЕX11 Е X12 Е X13 Е X14 Е X1

где знак Е означает логическую операцию «исключающее или». При проверке кодовой комбинации формируется четырехразрядный синдром S3S2S1S0. Разряды синдрома формируются следующим образом:

S0 = X1 Е X3 Е X5 Е X7 Е X9 Е X11 Е X13 Е X15

S1 = X2 Е X3 Е X6 Е X7 Е X10 Е X11 Е X14 Е X15

S2 = X4 Е X5 Е X6 Е X7 Е X12 Е X13 Е X14 Е X15

S3 = X8 Е X9 Е X10 Е X11Е X12 Е X13 Е X14 Е X15

Если значение синдрома равно нулю, то это означает отсутствие ошибок в кодовой комбинации. В противном случае значение синдрома является номером искаженного бита в кодовой комбинации.

При реализации кодирования на процессоре Intel x86 можно 15-ти битовую комбинацию сохранять в виде 16-ти разрядного слова. При этом один бит не будет использоваться. В качестве информационных комбинаций можно использовать четырехбайтовое слово (32 бита), дополняя его нулевым разрядом для получения трех 11-ти битовых комбинаций.

2. Пользовательский интерфейс

2.1 Общие сведения

Интерфейс взаимодействия с пользователем является важным элементом любой программы. В курсовом проекте осуществлена графическая реализация интерактивного интерфейса. Управление интерактивной программой выполняется с помощью отображаемых на экране элементов управления, к которым относятся различные меню, кнопки, поля ввода, полосы прокрутки и т. д. Вывод элементов управления на экран осуществляется в графическом режиме. Графический пользовательский интерфейс является наиболее удобным и понятным для пользователя.

Согласно заданию на курсовое проектирование была разработана программа, создающая графическое приложение Windows. Программа формирует на экране главное окно, в которое в дальнейшем будет осуществлять весь вывод в графическом виде. Так же программа обрабатывает сообщения, которые посылает ей операционная система. Результат обработки сообщений отображается в главном окне программы.

2.2 Описание алгоритма

программа графический интерфейс кодирование

Алгоритм организован в виде структурных схем, которые состоят из блоков разных типов. Первым шагом определяется операция регистрации класса окна. Далее следует проверка условия об успешном завершении регистрации. Если условие не выполняется, то появляется сообщение об ошибке. В противном случае осуществляется создание окна, его показ и обновление. Далее в алгоритме организован цикл обработки сообщений. Условие выхода из цикла — это поступившее сообщение о закрытии окна. Пока данное сообщение не поступит, проверяется тип любого другого и в зависимости от типа выполняются следующие процедуры:

— процедура открытия файла;

— процедура сохранения файла;

— процедура кодирования информации;

— процедура раскодирования информации;

— процедура создания диалогового окна «О программе».

2.3 Описание программы

Основой графического приложения Windows является каркасное приложение, содержащее программный код и состоящее из трех частей: главной функции, цикла обработки сообщений и оконной функции.

Описание главной функции

Разработка графического приложения начинается с главной функции, имеющей имя WinMain (). Она содержит код, осуществляющий инициализацию приложения в среде Windows. Главная функция имеет четыре параметра. Первый параметр является идентификатором данной программы, второй параметр всегда имеет значение NULL, третий параметр содержит командную строку, с помощью которой программа была запущена, а четвертый параметр содержит информацию о том, как будет отображаться окно программы.

Далее создается и инициализируется структура с информацией о классе окна. Для этого в функции WinMain объявляется переменная данного типа и заполнены поля структуры. Следующим шагом выполняется процедура создания окна, которая состоит из регистрации класса окна, создания окна, показа и перерисовки окна.

Регистрация класса окна выполняется функцией RegisterClass (). В качестве параметра функции используется указатель на заполненную структуру класса окна. В случае успешной регистрации функция возвращает ненулевой результат. В противном случае функция возвращает ноль и приложение необходимо завершить.

Создание окна выполняется функцией CreateWindow (), которая возвращает идентификатор открытого окна.

Далее, для показа окна на экране, используется функция ShowWindow (), где первый параметр — идентификатор окна, а второй параметр — способ отображения окна, тот который был передан операционной системой функции WinMain (четвертый параметр).

И, наконец, перерисовка окна осуществлена функцией UpdateWindow (), в которую в качестве параметра передается идентификатор окна.

Следующим шагом создания каркасного приложения будет создание цикла обработки сообщений, являющимся последним действием кода главной функции. Функция GetMessage () выбирает сообщение из очереди сообщений и помещает его в структуру, адрес которой указан в качестве первого параметра. Данная функция возвращает 0, если было выбрано сообщение WM_QUIT и не ноль во всех остальных случаях. Таким образом, цикл обработки сообщений завершается после получения сообщения WM_QUIT. А затем завершается и сама функция WinMain (). Цикл обработки сообщений состоит из вызова функции TranslateMessage, которая преобразует сообщения от клавиатуры в сообщения, содержащие ASCII_коды нажимаемых клавиш и из вызова системной функции DispatchMessage (), которая передает сообщение оконной функции, связанной с данным окном.

После создания цикла обработки сообщений приложение становится пассивным и начинает взаимодействовать с операционной системой посредством сообщений.

Описание оконной функции

Обработку поступающих сообщений осуществляет оконная функция. Прототип оконной функции записан в начале программы, до функции WinMain (), а сам код функции помещен в конец программы. Первый параметр — идентификатор окна, которое обслуживается данной оконной функцией, второй параметр — идентификатор сообщения операционной системы, которое передается на обслуживание, третий и четвертый параметры — дополнительные данные, передаваемые вместе с сообщением. В данном проекте оконная функция получает сообщение, анализирует его и передает управление другим функциям в зависимости от типа полученного сообщения или операционной системе. Реакцией на сообщение WM_DESTROY, передаваемое оконной функции при закрытии окна является посылка сообщения WM_QUIT для завершения приложения. Это сообщение посылается путем вызова функции PostQuitMessage (), параметром которой является код завершения программы. При выборе пунктов меню, отслеживающимся операционной системой, формируются сообщения WM_COMMAND. Оконная функция, получив сообщение такого типа, проверяет его параметр, в зависимости от значения которого, выполняет следующие действия:

— создание диалогового окна «О программе»;

— открытие файла;

— сохранение файла;

— кодирование файла;

— раскодирование файла;

— выход из приложения.

Описание функции вызова диалога открытия файла.

Первым действием осуществляется освобождение места в памяти для структуры с информацией о классе окна и ее заполнение. Элементами структуры являются область памяти под имя файла, максимальная длина имени файла, тип файла и другие параметры. При условии успешного выбора файла для открытия, программа открывает данный файл для чтения. Из файла, который открыли, информация записывается в буфер. Текст из буфера затем помещается в окно для редактирования, которое является дочерним для окна, созданного в главной функции. Последним действием программа закрывает файл.

Описание функции вызова диалога сохранения файла.

Первым действием осуществляется освобождение места в памяти для структуры с информацией о классе окна и ее заполнение. При условии успешного выбора файла для сохранения, программа открывает данный файл для записи. Если файла не существует, то создает его, в противном случае перезаписывает. Из дочернего окна текст записывается в буфер. Информацию из буфера затем помещается в файл. Последним действием программа закрывает файл.

Описание функции обработки сообщений диалогового окна «О программе».

Функция рассматривает два типа сообщений поступающих в созданное ранее окно. Если поступил второй тип сообщения WM_COMMAND, то закрывается окно при нажатии кнопки «ок», в противном случае ничего не происходит.

3. Процедуры работы с файлами

3.1 Анализ метода и описание алгоритма

При выполнении операций над файлами в программе необходимо обеспечить полный контроль над ошибочными ситуациями.

При открытии файла необходимо осуществлять контроль с целью выявления таких ошибочных ситуаций, как попытка открытия несуществующего файла, попытка открытия файла с атрибутом «только для чтения» для проведения операций записи и т. д. В случае возникновения ошибки при открытии файла необходимо завершить программу с выдачей подробного диагностического сообщения, при открытии какого файла какого рода ошибка произошла.

При создании файла также необходимо отслеживать ошибочные ситуации, которые связаны чаще всего с неправильным указанием каталога для открываемого файла (такого каталога не существует) либо попытка открытия файла на устройстве, не поддерживающем операций записи, например CD-ROM или другое устройство, например гибкий диск, с установленной защитой от записи.

Операции открытия существующего файла, создания нового файла следует выделить в отдельные процедуры OPENFILERD () и OPENFILEWR (), в которые дополнительно необходимо добавить обработку ошибочных ситуаций при открытии / создании файла. Алгоритм процедуры OPENFILERD () приведен на рисунке А. 1, Лист 6, алгоритм процедуры OPENFILEWR () — на рисунке А. 1, Лист 7.

Процедура OPENFILERD () открывает файл для двоичного чтения, возвращает номер файла. Если при открытии произошла ошибка, то выводится сообщение об ошибке, и в вызывающую функцию возвращается -1, после чего происходит завершение программы. Процедура OPENFILEWR () открывает файл для двоичной записи.

При записи может возникнуть ситуация отсутствия свободного места на носителе информации. При чтении ошибки могут возникать обычно из-за дефектов поверхности носителей информации. Обычно это характерно для гибких дисков. Кроме того, при работе с сетевыми дисками также могут возникать ошибки доступа к устройству во время операций чтения / записи из-за перегрузок сети или обрывах соединений.

Как уже было отмечено выше, чтение и запись следует вести одновременно с обработкой данных, поэтому ошибки чтения / записи обрабатываются в процедурах на языке Ассемблера, алгоритмы и программирование которых описаны в Разделе 4.

Очень часто возникает ситуация, когда при обработке содержимое файла не может полностью быть помещено в оперативную память компьютера. В этом случае обработку файла ведут частями. В этом случае либо заранее определяется длина обрабатываемого файла, либо обработку ведут до тех пор, пока не будет достигнут конец файла. Обычно эта проверка осуществляется либо с использованием специальных функций, либо по ситуации, когда число реально прочитанных байт из файла меньше заказанных для чтения.

Если в командной строке не задан 5_й параметр — опция `/rw' (перезапись выходного файла, см. рисунок А. 1, Лист 2), то прежде всего необходимо проверить, не существует ли выходной файл. Для этого следует просто попытаться открыть файл в режиме «только для чтения», и если файл существует, выдать пользователю предупреждение и завершить работу программы.

После завершения операций чтения / записи файл обязательно необходимо закрыть, чтобы сохранить содержимое файла. Также, если при открытии одного из файлов произошла ошибка, то перед выходом из программы необходимо закрыть все открытые файлы.

3.2 Программирование

Для создания / открытия файла следует использовать библиотечные функции небуферизированного ввода / вывода. Это исходит из того, что функции данного вида возвращают номер обработчика, или дескриптор, файла, который затем необходимо передавать в качестве параметра при вызове функции на Ассемблере, т.к. ассемблерные функции чтения / записи работают только с номерами файлов.

Операции открытия файла для чтения и обработка ошибок при открытии производятся в процедуре OpenFileRD () (см. прил. Б). В качестве параметра в функцию передается указатель на строковую константу, содержащую путь и имя открываемого файла (файл, открываемый для чтения, задается в командной строке 3_м параметром, т. е. на него указывает аргумент argv[2])

Для открытия файла использована функция open (). Данная функция определена в заголовочном файле < sysstat. h>. Формат функции следующий:

int open (const char *path, int access [, unsigned mode]);

Чтобы открыть файл, указанный в path, только для двоичного чтения необходимо в access указать O_BINARY|O_RDONLY, а в mode — S_IREAD. Эти константы определены в заголовочном файле < fcntl. h>.

При успешном завершении функция возвращает дескриптор файла (неотрицательное число), в случае ошибки возвращается -1.

В случае ошибки необходимо определить характер ошибки. Для этой цели следует использовать библиотечную функцию perror (). Данная функция определена в файле < stdio. h>. Формат функции следующий:

void perror (const char *s);

Функция печатает сообщение о системной ошибке последней библиотечной функции. Сначала выводится аргумент s, двоеточие и сообщение об ошибке. В качестве аргумента s лучше всего использовать имя файла.

Если произошла ошибка, т. е. функция open () возвратила -1, то в главную функцию необходимо также возвратить -1, после чего программа завершит свою работу. В случае успешного выполнения функции необходимо возвратить номер файла и перейти к следующим операциям.

Операции открытия файла для записи и обработка ошибок при открытии производятся в процедуре OpenFileWR () (см. прил. Б). В качестве параметра в функцию передается указатель на строковую константу, содержащую путь и имя открываемого файла (на выходной файл будет указывать argv[3]).

Чтобы открыть файл, указанный в path, только для двоичной записи необходимо в access указать O_CREAT|O_TRUNC|O_BINARY|O_WRONLY, а в mode — S_IWRITE. Если файл уже существовал, его длина устанавливается в 0, иначе создается новый файл

В случае ошибки также необходимо вызвать функцию обработки ошибок perror (), возвратить -1 и завершить программу.

Для открытия файла только для чтения в функции open () необходимо в access указать O_RDONLY, а в mode — S_IREAD. Если функция возвратила не -1 (файл существует), необходимо выдать предупреждение пользователю и завершить программу.

Для закрытия файла используется функция close (). Данная функция требует только один параметр — номер файла, который необходимо закрыть.

4. Процедуры обработки информации

4.1 Процедура сжатия данных

Анализ метода и описание алгоритма

Для работы с последовательностями повторяющихся и неповторяющихся байт необходимо использовать две переменные: переменная DIFBYTES необходима для подсчета количества неповторяющихся байт в последовательности, SIMBYTES — для подсчета числа повторяющихся байт в последовательности. Также необходимо организовать временный буфер TEMPBUF размером 128−130 байт для хранения последовательности неповторяющихся байт и буферы PREDBYTE и NEXTBYTE размером в один байт для хранения предыдущего и следующего байтов соответственно.

Работа алгоритма основана на сравнении предыдущего байта со следующим.

В более понятном виде алгоритм можно описать следующим образом:

Шаг 1: прочитать один байт из входного файла в буфер PREDBYTE;

Шаг 2: Проверить индикатор конца входного файла. Если файл прочитан, перейти на Шаг 8. Прочитать один байт в буфер NETXBYTE, сравнить содержимое буфера PREDBYTE с содержимым буфера NEXTBYTE. Если байты совпадают, перейти на Шаг 3, иначе — на Шаг 4;

Шаг 3 — «Обработка повторяющихся байт»:

увеличить SIMBYTES, проверить DIFBYTES на ноль. Если DIFBYTES не ноль (закончилась последовательность неповторяющихся байт, началась новая последовательность повторяющихся байт), перейти на Шаг 6. Проверить SIMBYTES на 128. Если равенство, перейти на Шаг 5. Перейти на Шаг 7;

Шаг 4 — «Обработка неповторяющихся байт»:

проверить SIMBYTES на ноль. Если SIMBYTES не ноль (закончилась последовательность повторяющихся байт, началась новая последовательность неповторяющихся байт), перейти на Шаг 5. Записать байт из PREDBYTE во временный буфер TEMPBUF, увеличить DIFBYTES. Проверить DIFBYTES на 128. Если равенство, перейти на Шаг 6. Перейти на Шаг 7;

Шаг 5 — «Запись повторяющихся байт»:

записать служебный байт N (N=1_SIMBYTES), записать байт из PREDBYTE, обнулить SIMBYTES. Перейти на Шаг 7;

Шаг 6 — «Запись неповторяющихся байт»:

записать служебный байт N (N=DIFBYTES1), записать последовательность байт из буфера TEMPBUF (длина последовательности равна DIFBYTES). Очистить буфер TEMPBUF, обнулить DIFBYTES. Перейти на Шаг 7;

Шаг 7: байт из NEXTBYTE переместить в PREDBYTE. Перейти на Шаг 2;

Шаг 8: Проверить SIMBYTES на ноль. Если неравенство, перейти на Шаг 5. Проверить DIFBYTES на ноль. Если неравенство, записать байт из PREDBYTE в буфер TEMPBUF, перейти на Шаг 6. Завершить процедуру.

Шаг 8 необходимо выполнить для того, чтобы по достижении конца входного файла дописать оставшиеся байты, т.к. в противном случае они будут потеряны. Только после этого можно завершить процедуру.

Все операции чтения / записи необходимо вести с обработкой ошибок, в случае ошибки необходимо завершить процедуру с возвратом в вызывающую программу кода ошибки.

Программирование

Для того чтобы функция RLE, написанная на Ассемблере, была доступна в модуле на Си, данную функцию в модуле на Ассемблере необходимо объявить как общедоступную с помощью псевдооператора PUBLIC. Имя функции в ассемблерном модуле необходимо начинать с символа подчеркивания «_». Функцию RLE в модуле на Ассемблере необходимо объявить как дальнюю с помощью атрибута дистанции far, чтобы использовать LARGE модель памяти.

В сегменте данных необходимо выделить память для следующих идентификаторов:

— TEMPBUF — временный буфер для хранения неповторяющихся байт (размер — 130 байт);

— PREDBYTE — предыдущий байт (1 байт);

— NEXTBYTE — следующий байт (1 байт);

— HANDLE_IN — для хранения номера входного файла (размерность — слово);

— HANDLE_OUT — номер выходного файла (слово);

— SIMBYTES — счетчик повторяющихся байт (1 байт);

— DIFBYTES — счетчик неповторяющихся байт (1 байт).

В начале вызываемой функции необходимо выполнить «пролог». Пролог включает в себя сохранение регистра BP в стеке, загрузка в BP указателя на вершину стека, сохранение регистров DS, SI, DI. Также следует выделить стековый кадр для одной локальной переменной размером в одно слово, для чего SP нужно уменьшить на 2. В качестве локальной переменной будет использоваться смещение TEMPBUF. Локальная переменная будет находиться по адресу [BP2].

Для получения доступа к параметрам, передаваемым из вызывающего модуля на Си в функцию на Ассемблере, необходимо обращаться к стеку. В функцию на Ассемблере необходимо передать два параметра: номер входного и номер выходного файлов. Оба параметра имеют тип int, размерность которого равна слову. Поэтому в стеке 1_й параметр будет находиться по адресу [BP+6], а 2_й — по адресу [BP+8]. Данные параметры необходимо загрузить в переменные HANDLE_IN и HANDLE_OUT для дальнейшего использования (см. прил. Г).

Для чтения информации из файла необходимо использовать функцию MS-DOS 3Fh. При этом в функцию передается файловый номер, адрес буфера, куда следует поместить считываемые байты, и количество считываемых байтов. При успешном завершении в регистре AX возвращается количество считанных из файла байтов. Оно может оказаться меньше, чем требовалось.

Формат вызова данной функции следующий:

Входные параметры:

AH — номер функции (3Fh);

BX — номер файла, откуда необходимо считать данные;

DS: DX — адрес массива, куда необходимо прочитать данные;

CX — количество считываемых байт.

Возвращаемое значение:

если CF = 0, то AX содержит количество байтов, прочитанных из файла;

если CF = 1, то AX содержит код ошибки [1].

Так, чтобы считать из входного файла HANDLE_IN один байт в буфер PREDBYTE, необходимо вызвать функцию в следующем виде:

mov AH, 3FH

mov BX, HANDLE_IN

lea DX, PREDBYTE

mov CX, 1

int 21h

jc to_exit_return

Для проверки флага CF необходимо использовать команду условного перехода jc адрес_перехода. Адрес перехода удобнее задавать в виде метки. Если CF=1, т. е. произошла ошибка, то производится переход на метку to_exit_return и выход из процедуры с возвратом в вызывающий модуль кода ошибки.

Для записи информации в файл необходимо использовать функцию MS-DOS 40h. При этом в функцию необходимо передать файловый номер, адрес буфера, откуда следует записать байты, и количество записываемых байтов. При успешном завершении в регистре AX возвращается количество записанных в файл байтов. Оно может оказаться меньше, чем требовалось.

Формат вызова данной функции следующий:

Входные параметры:

AH — номер функции (40h);

BX — номер файла, куда необходимо записать данные;

DS: DX — адрес массива, где содержатся записываемые данные;

CX — количество записываемых байт.

Возвращаемое значение:

если CF = 0, то AX содержит количество байтов, записанных в файл;

если CF = 1, то AX содержит код ошибки [1].

Так, чтобы записать в выходной файл HANDLE_OUT количество байт, содержащееся в буфере DIFBYTES, из буфера TEMPBUF, необходимо вызвать функцию в следующем виде:

mov AH, 40H

mov BX, HANDLE_OUT

xor CH, CH

mov CL, byte ptr [DIFBYTES]

lea DX, TEMPBUF

int 21h

jc exit_return

Для сравнения предыдущего байта со следующим необходимо предварительно в аккумулятор AL загрузить байт из PREDBYTE:

mov AL, byte ptr [PREDBYTE]

и содержимое аккумулятора сравнить с байтом в NEXTBYTE:

cmp AL, byte ptr [NEXTBYTE]

Для сравнения двух операндов используется команду сравнения cmp операнд1 операнд2. Команда cmp вычитает из 1_го операнда 2_й операнд, но при этом содержимое 1_го операнда не изменяется. В зависимости от результата вычитания устанавливаются флаги. Если операнды равны, то флаг ZF = 1. В этом случае по команде условного перехода jz можно перейти на соответствующую метку.

При обработке последовательностей неповторяющихся байт требуется сохранить байты во временном буфере TEMPBUF. Для этого следует воспользоваться командой работы со строками STOS адрес_приемника. Данная команда содержимое аккумулятора (регистр AX/AL) пересылает в приемник. При этом содержимое DI увеличивается на размер элемента (в данном случае на один байт). Адрес приемника должен быть помещен в пару регистров ES: DI:

mov DI, [BP2]; загрузка локальной переменной (смещение TEMPBUF) в DI

STOS TEMPBUF

mov [BP2], DI; сохранение смещения

Выполнение главного цикла чтения, обработки и записи происходит при условии, что не возникли ошибки чтения / записи и функции чтения / записи возвращают в AX количество считанных / записанных байтов. Если при чтении из входного файла одного байта в AX помещается 0, то этот факт говорит о том, что был достигнут конец входного файла. В этом случае необходимо завершить обработку информации и выйти из процедуры с возвратом в вызывающую функцию нуля.

Если же при записи в выходной файл нескольких байт функция записи поместила в AX значение, меньшее требуемого, при этом флаг CF не установлен, то это также свидетельствует об ошибке записи в выходной файл, возможно, по причине нехватки свободного места на диске. В этом случае также необходимо завершить выполнение функции и возвратить -1.

При возврате из функции необходимо выполнить «эпилог» — действия, обратные прологу.

Для возврата в вызывающую функцию значения типа int необходимо использовать регистр AX.

В случае успешного завершения процедуры (достигнут конец файла, функция чтения поместила в AX ноль), перед выходом нет необходимости помещать в регистр AX ноль. В случае ошибки записи в выходной файл (флаг CF = 0, функция записи поместила в AX ноль), необходимо перед выходом из процедуры в AX поместить -1. В случае системной ошибки (флаг CF = 1, AX содержит код ошибки) также нет необходимости изменять содержимое AX, и в вызывающую функцию возвращается код ошибки.

Если вызываемая функция в модуль на Си возвратила положительное число (код ошибки), то для обработки ошибок следует использовать библиотечную функцию strerror (). В качестве параметра в функцию необходимо передать код ошибки. Функция возвращает указатель на строку, содержащую сообщение об ошибке.

4.2 Процедура восстановления данных

Анализ метода и описание алгоритма

Работа процедуры начинается с чтения первого байта из входного файла в PREDBYTE. Данный байт должен является служебным байтом. Затем необходимо определить, в каком диапазоне лежит байт из буфера PREDBYTE. Если байт находится в диапазоне от -127 до -1 (последовательность повторяющихся байт), то необходимо прочитать следующий байт в NEXTBYTE и в выходной файл записать байт из NEXTBYTE N раз, где N находится как 1_PREDBYTE. Затем нужно перейти к чтению следующего байта.

В противном случае байт находится в диапазоне от 0 до 127 (неповторяющиеся байты). В этом случае необходимо прочитать следующую последовательность байт в буфер TEMPBUF, длина последовательности находится как N=PREDBYTE+1, и только после этого записать эту же последовательность из буфера в выходной файл. Перейти к чтению следующего байта.

При чтении / записи необходимо следить за ошибками. В случае ошибки возвращается код ошибки, в случае успешного завершения возвращается 0.

Цикл чтения производится до признака конца входного файла.

Программирование

В данной процедуре были применены функции чтения / записи MS-DOS и основные команды, использованные при программировании функции RLE (см. п. 4.1. 2).

5. Отладка и проверка работоспособности программы

5.1 Подготовка и отладка исполняемой программы

Для подготовки исполняемой программы с помощью интегрированной среды программирования Borland C++ 3.1 необходимо:

a) в главном модуле на языке Си объявить функцию, разработанную на языке Ассемблера, как внешнюю с помощью квалификатора extern. Так как функция на Ассемблере имеет дальний вызов, то в модуле на Си функция также должна быть объявлена как дальняя с помощью атрибута дистанции far;

b) отдельно откомпилировать файл, содержащий функцию на Ассемблере. В результате будет получен объектный файл;

c) в интегрированной среде создать проект, в который подключить главный модуль на Си и полученный объектный файл функции на Ассемблере;

d) скомпоновать модули.

Если при компиляции исходных файлов и компоновке объектных модулей не возникло ошибок, то в результате будет получен файл исполняемой программы.

Для отладки программы следует использовать отладчик Turbo Debugger 3. 1, встроенный в среду Borland C++ 3.1. С помощью данного отладчика можно выполнить программу в пошаговом режиме, проследить за переменными, просмотреть содержимое регистров микропроцессора, выполнить трассирующее вхождение в функцию на Ассемблере. Для того чтобы в отладчике был доступен текст модуля на Ассемблере, при компиляции исходного файла необходимо включать отладочную информацию в объектный файл (опция /zi).

5.2 Проверка работоспособности программы

Для проверки работоспособности программы был использован файл test. dat, содержащий последовательности повторяющихся и неповторяющихся байт. Размер файла 1 955 743 байт. В результате работы программы файл test. dat был сжат в файл ts. dat размером 1 036 924 байт (процент сжатия — 47%). В результате восстановления файла ts. dat был получен файл test2. dat размером 1 955 743 байт. Т. е. файл был восстановлен до своего первоначального размера. Этот факт свидетельствует о корректной работе программы.

При попытке произвести запись на гибкий диск с недостаточным свободным местом было выдано соответствующее сообщение.

Что касается операций с файлами, то были предотвращены попытки перезаписи существующего файла без задания опции /rw, открытия для чтения несуществующего файла, использования одного файла как для записи, так и для чтения, задания в качестве одного из файлов имени файла-программы. При этом были выданы соответствующие предупреждения. Это также говорит о правильной работе программы.

6. Руководство пользователя

6.1 Системные требования

Нормальное функционирование данной программы требует наличия следующих системных ресурсов:

— компьютер, совместимый с IBM PC/XT;

— процессор i8086 и выше;

— операционная система MS DOS 6. 22 и выше, Windows 3. х/9x/NT;

6.2 Описание работы с программой

Во-первых, перед запуском программы необходимо ознакомиться с параметрами запуска. Для этого в командной строке необходимо ввести исключительно имя программы без каких-либо параметров. При этом на экран будет выведена подробная информация о параметрах запуска программы.

Для сжатия файла в командной строке после имени программы необходимо указать опцию `/c', затем имя файла, который требуется сжать и, далее, имя файла, в который будут записаны результаты сжатия. Все параметры необходимо отделять друг от друга при вводе пробелами. Файл, используемый для записи результатов сжатия, не должен существовать в указанном каталоге (или в текущем, если каталог не указывается). Если файл существует, то необходимо последним параметром ввести опцию `/rw'.

Например, требуется сжать файл test. dat, находящийся в текущем каталоге, в файл ts. dat. В командной строке это будет выглядеть так:

rle /c test. dat ts. dat

Файл ts. dat будет создан в текущем каталоге.

Если программа выдала сообщение, что файл ts. dat существует, то если данный файл не содержит важной информации, его можно перезаписать с помощью опции `/rw':

rle /c test. dat ts. dat /rw

Для восстановления файла необходимо использовать опцию `/d':

rle /d ts. dat test2. dat

При этом важно точно знать, что при сжатии исходного файла ts. dat был использован метод RLE. В противном случае результаты работы программы могут оказаться непредсказуемыми.

Заключение

В процессе работы над проектом была разработана программа, выполняющая помехоустойчивое кодирование с помощью кода Хемминга. Были получены практические навыки программирования в интегрированной среде VISUAL STUDIO VERSION 6.0.

Программа была тестирована и отлажена, ее работоспособность была проверена на практике.

В результате работы над проектом были приобретены навыки программирования на языках Си и Ассемблера.

Список используемых источников

Лупанов М.Ю., Грунтович М. М., Фролов В. Н. Средства и методы программирования. Использование языка Ассемблера. Методические указания к лабораторным работам. — Пенза: Пензенский государственный университет, 2000. — 64 с.

Приложение 1

Текст программы RLE. C

#include < stdio. h>

#include < conio. h>

#include < process. h>

#include < sys/stat. h>

#include < fcntl. h>

#include < errno. h>

#include < string. h>

#include < io. h>

extern far int RLE (int, int);

extern far int DERLE (int, int);

/*****************************************

int RLE (int handle_infile, int handle_outfile);

Функция сжатия данных;

Параметры:

handle_infile — дескриптор входного файла;

handle_outfile — дескриптор выходного файла;

Возвращаемое значение:

0 — если не возникло ошибок;

-1 — если возникла ошибка при записи в выходной файл;

'+'-ное значение — другая ошибка.

int DERLE (int handle_infile, int handle_outfile);

Функция восстановления данных.

Те же параметры, что и у функции сжатия.

*****************************************/

int OpenFileRD (const char *);

int OpenFileWR (const char *);

char *TOUPPER (char *);

int STRCMP (char *, char *);

void HELP (void)

{

printf («nПрограмма выполняет сжатие и восстановление данных методом RLE (Run Length Encoding). «);

printf («nФормат запуска программы: «);

printf («n [диск: \путь] RLE опции имя_файла1 имя_файла2 [/rw]»);

printf («nОбязательные опции: «);

printf («n/c — процедура сжатия; «);

printf («n/d — процедура восстановления; «);

printf («nимя_файла1 — путь и имя входного файла (открывается для чтения); «);

printf («nимя_файла2 — путь и имя выходного файла (создается для записи); «);

printf («nНеобязательные опции: «);

printf («n/rw — если файл2 существует, он будет перезаписан. «);

return;

}

void main (int argc, char *argv[])

{

int in, out, s;

if (argc==1) {

clrscr ();

HELP ();

return;

}

if (argc==4 || argc==5) {

if ((STRCMP (argv[1], «/c»)≠0) & & (STRCMP (argv[1], «/d»)≠0)) {

printf («n % s — нет такой опции», argv[1]);

HELP ();

return;

}

if (STRCMP (argv[2], argv[3])==0) {

printf («nДля записи нужно использовать другой файл»);

HELP ();

return;

}

if ((STRCMP (argv[0], argv[2])==0)|| (STRCMP (argv[0], argv[3])==0)) {

printf («n % s — имя программы», argv[0]);

exit (1);

}

if (argv[4]==NULL) {

if ((s=open (argv[3], O_RDONLY, S_IREAD))≠-1) {

printf («nФайл % s существует. «, argv[3]);

close (s);

HELP ();

return;

}

}

else if (STRCMP (argv[4], «/rw»)≠0) {

printf («n % s — нет такой опции», argv[4]);

HELP ();

return;

}

switch (argv[1] [1]) {

int ind;

long unsigned bytes_rd, bytes_wr;

case 'c': case 'C':

if ((in=OpenFileRD (argv[2]))==-1)

return;

printf («nФайл % s открыт!», argv[2]);

if ((out=OpenFileWR (argv[3]))==-1) {

close (in);

return;

}

printf («nФайл % s открыт!», argv[3]);

ind=RLE (in, out);

if (ind==-1) {

printf («nОшибка записи в файл: %s! Возможно, недостаточно места на диске. «, argv[3]);

close (in);

close (out);

exit (1);

}

else if (ind> 0) {

printf («nОШИБКА: %s», strerror (ind));

close (out);

close (in);

exit (1);

}

bytes_rd=tell (in);

bytes_wr=tell (out);

close (out);

close (in);

printf («nПрочитано % lu байт, записано % lu байт», bytes_rd, bytes_wr);

printf («nПроцент сжатия: %2. 0f%», (100−100*((double) bytes_wr/(double) bytes_rd)));

printf («nПроцедура сжатия завершена!!!»);

break;

case 'd': case’D':

if ((in=OpenFileRD (argv[2]))==-1)

return;

printf («nФайл % s открыт!», argv[2]);

if ((out=OpenFileWR (argv[3]))==-1)

close (in);

return;

printf («nФайл % s открыт!», argv[3]);

ind=DERLE (in, out);

if (ind==-1) {

printf («nОшибка записи в файл: %s! Возможно, недостаточно места на диске. «, argv[3]);

close (in);

close (out);

exit (1);

}

else if (ind> 0) {

printf («nОШИБКА: %s», strerror (ind));

close (out);

close (in);

exit (1);

}

bytes_rd=tell (in);

bytes_wr=tell (out);

close (out);

close (in);

printf («nПрочитано % lu байт, записано % lu байт», bytes_rd, bytes_wr);

printf («nПроцент восстановления: %2. 0f%», (100−100*((double) bytes_rd/(double) bytes_wr)));

printf («nПроцедура восстановления завершена!!!»);

break;

return;

else {

printf («nНеверный формат запуска программы»);

HELP ();

return;

}

}

int OpenFileWR (const char *filename) {

int s;

if ((s=open (filename, O_CREAT|O_TRUNC|O_BINARY|O_WRONLY, S_IWRITE))==-1) {

puts (««);

perror (filename);

return -1;

}

return s;

}

int OpenFileRD (const char *filename)

{

int s;

if ((s=open (filename, O_BINARY|O_RDONLY, S_IREAD))==-1) {

puts (««);

perror (filename);

return -1;

}

return s;

}

int STRCMP (char *s, char *t)

{

return strcmp (TOUPPER (s), TOUPPER (t));

}

char *TOUPPER (char *str)

{

int i;

for (i=0; str[i]≠''; i++) {

if (str[i]> ='a' & & str[i]< ='z')

str[i]-=32;

else if (str[i]> ='а' & & str[i]< ='п')

str[i]-=32;

else if (str[i]> ='р' & & str[i]< ='я')

str[i]-=80;

else if (str[i]=='ё')

str[i]='Ё';

}

return str;

}

Приложение 2

Текст функции RLE

PUBLIC _RLE,_DERLE

DATA_SEG segment para public 'data'

TEMPBUF db 130 dup (?); временный буфер для хранения неповторяющихся байт

PREDBYTE db ?; предыдущий байт

NEXTBYTE db ?; следующий байт

HANDLE_IN dw ?; номер входного файла

HANDLE_OUT dw ?; номер выходного файла

SIMBYTES db 0; счетчик повторяющихся байт

DIFBYTES db 0; счетчик неповторяющихся байт

DATA_SEG ends

CODE_SEG segment para public 'code'

ASSUME CS: CODE_SEG, DS: DATA_SEG, ES: DATA_SEG

; ***************FUNCTION «RLE"**********************

_RLE proc far

PUSH BP

MOV BP, SP

SUB SP, 2

PUSH SI DI DS

mov AX, SEG DATA_SEG

mov DS, AX

mov ES, AX

LEA BX, TEMPBUF

mov [BP2], BX

mov AX, [BP+6]; 1_й параметр

mov HANDLE_IN, AX

xor AX, AX

mov AX, [BP+8]; 2_й параметр

mov HANDLE_OUT, AX

mov AH, 3FH

mov BX, HANDLE_IN

lea DX, PREDBYTE

mov CX, 1

int 21h

jc to_exit_return

xor AX, AX

xor DX, DX

xor BX, BX

xor CX, CX

CLD

main_cycl:

mov AH, 3FH

mov BX, HANDLE_IN

lea DX, NEXTBYTE

mov CX, 1

int 21h

jc to_exit_return

cmp AX, 0

jz to_all_bytes?; проверить перед выходом

mov AL, byte ptr [PREDBYTE]

cmp AL, byte ptr [NEXTBYTE]

jz get_sim_char

jnz get_dif_char

get_dif_char:

cmp byte ptr [SIMBYTES], 0

jnz put_sim_char

mov DI, [BP2]

STOS TEMPBUF

mov [BP2], DI

inc [DIFBYTES]

cmp byte ptr [DIFBYTES], 128D

jz put_dif_char

jmp next_char

get_sim_char:

inc [SIMBYTES]

cmp byte ptr [DIFBYTES], 0

jnz put_dif_char

cmp byte ptr [SIMBYTES], 128D

jz put_sim_char_not_inc

jmp next_char

; ********Промежуточные метки************

to_exit_return:

jmp exit_return

to_all_bytes?:

jmp all_bytes?

; **************************************

put_sim_char:

inc [SIMBYTES]

put_sim_char_not_inc:

xor CH, CH

xor DH, DH

mov CL, 1D

mov DL, byte ptr [SIMBYTES]

sub CL, DL

mov byte ptr [SIMBYTES], CL

mov AH, 40H

mov BX, HANDLE_OUT

lea DX, SIMBYTES

mov CX, 1

int 21h

jc to_exit_return; см. exit_return

cmp AX, 0

jz to_return_error_wr

mov AH, 40H

mov BX, HANDLE_OUT

lea DX, PREDBYTE

mov CX, 1

int 21h

jc to_exit_return

cmp AX, 0

jz to_return_error_wr

mov [SIMBYTES], 0

xor DX, DX

xor CX, CX

jmp next_char

; **************************************

to_return_error_wr:

jmp return_error_wr

; **************************************

put_dif_char:

xor CH, CH

xor DH, DH

mov CL, byte ptr [DIFBYTES]

PUSH CX sub CL, 1D

mov byte ptr [DIFBYTES], CL

mov AH, 40H

mov BX, HANDLE_OUT

lea DX, DIFBYTES

mov CX, 1

int 21h

jc exit_return

cmp AX, 0

jz return_error_wr

POP CX

mov AH, 40H

mov BX, HANDLE_OUT

lea DX, TEMPBUF

int 21h

jc exit_return

cmp AX, CX

jb return_error_wr; если <

LEA BX, TEMPBUF

mov [BP2], BX

mov [DIFBYTES], 0

xor CX, CX

xor DX, DX

jmp next_char

; **************************************

yet_dif_char:

mov AL, byte ptr [PREDBYTE]

mov DI, [BP2]

STOS TEMPBUF

mov [BP2], DI

inc [DIFBYTES]

jmp put_dif_char

yet_sim_char:

jmp put_sim_char

; **************************************

next_char:

mov AL, byte ptr [NEXTBYTE]

mov byte ptr [PREDBYTE], AL

jmp main_cycl

all_bytes?:

cmp byte ptr [DIFBYTES], 0

jnz yet_dif_char

cmp byte ptr [SIMBYTES], 0

jnz yet_sim_char

jmp exit_return

return_error_wr:

mov AX,-1D

exit_return:

pop DS DI SI

ADD SP, 2

pop BP

ret

_RLE endp

ПоказатьСвернуть
Заполнить форму текущей работой