АЛМАТИНСКИЙ ИНСТИТУТ ЭНЕРГЕТИКИ И СВЯЗИ

Кафедра инженерной кибернетики

 

 

 

 

 

                                

 

 

 

 

 

 

 

 

 

СИСТЕМНОЕ ПРОГРАММИРОВАНИЕ

Часть 1

Методические указания к выполнению лабораторных работ

 (для студентов специальностей 360140 Автоматизация и информатизация в системах управления, 050702 – Автоматизация и управление)

 

 

 

 

 

 

Алматы 2006


СОСТАВИТЕЛИ: Н.В.Сябина. Системное программирование. Часть 1. Методические указания к выполнению лабораторных работ (для студентов очной формы обучения специальностей 360140 – Автоматизация и информатизация в системах управления, 050702 – Автоматизация и управление). - Алматы: АИЭС, 2006.-  44 с.

 

   

 

Методические указания по дисциплине «Системное программирование» включают 9 лабораторных работ (в двух частях) и разработаны в помощь студентам, изучающим системное программирование, с  целью выработки практических навыков в следующих направлениях:

- применение языка программирования С как инструмента для системного программирования;

- программное управление аппаратными средствами ПЭВМ на низком уровне;

- использование внутренних структур данных операционной системы MS DOS для получения информации и оперативной настройки системы.

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

Методические указания предназначены для студентов специальности 360140 – Автоматизация и информатизация в системах управления. 

  Табл. 5, библиогр. –  10 назв.

 

 

 

 

 

 

    Рецензент: канд. техн. наук, доц. Ибраева Л.К.

 

 

 

 

 

    Печатается по плану издания Алматинского института энергетики

и связи на 2005 г.

 

 

 

 

 

© Алматинский институт энергетики и связи, 2006 г.


Введение

 

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

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

Подразделение программного обеспечения на системное и прикладное является до некоторой степени устаревшим. Современное разделение предусматривает минимум три градации программного обеспечения: системное, промежуточное и  прикладное.

Современная тенденция развития программного обеспечения состоит в снижении объема как системного, так и прикладного программирования. Основная часть работы программистов выполняется в промежуточном программном обеспечении. Промежуточное программное обеспечение (middleware) определяется как совокупность программ, осуществляющих управление вторичными ресурсами (конструируемыми самим программным обеспечением), которые ориентированы на решение определенного класса задач. К такому программному обеспечению относятся менеджеры транзакций, серверы БД, серверы коммуникаций и другие программные серверы.

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

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

Системное программное обеспечение подразделяется на системные управляющие и системные обслуживающие программы.

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

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

Управляющая программа вместе с набором необходимых для эксплуатации системы утилит составляют операционную систему.

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

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

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

В настоящее время одним из наиболее востребованных являются язык программирования высокого уровня С и его модификации (С++, С#). Язык С, получивший свое название от третьей буквы латинского алфавита, был создан в 1972 г. Ден­нисом Ричи (Dennis Ritchie) и Кеном Томпсоном (Ken Thompson). В этом языке сочетаются лучшие свойства языка Ассемблер и язы­ков программирования высокого уровня. По компактности и скорости выполнения программы на языке С приближаются к программам, написанным на языке Ассемблер.

Главным качеством языка C, которое делает его именно языком системного программиста, является то, что «C — это язык относительно «низкого уровня»… Это означает, что C имеет дело с объектами того же вида, что и большинство ЭВМ, а именно: с символами, числами и адресами. Они могут объединяться и пересылаться посредством обычных арифметических и логических операций, осуществляемых реальными ЭВМ» [2]. Система программирования C при представлении данных не вносит никаких дополнительных структур памяти, которые не были бы «видны» программисту. Так, например, внутреннее представление массивов в языке C полностью совпадает с внешним: массив — это только последовательность слотов в памяти. Отсутствие специального дескриптора массива, с одной стороны, делает невозможным контроль выхода индексов за допустимое границы, но, с другой, уменьшает объем памяти программы и увеличивает ее быстродействие за счет отсутствия в памяти дескриптора и команд проверки индекса при каждом обращении к элементу массива. Это общий принцип C-программ: программист имеет максимальные возможности для разработки эффективных программ, но вся ответственность за их правильность ложится на него самого. Поэтому отладка программ на языке C — непростой процесс, C-программы чаще «зависают» и выдают результаты, которые не всегда воспроизводятся и которые труднее объяснить, чем программы на других языках.

Чрезвычайно важным свойством языка C, которого нет в других языках, является адресная арифметика. Над данными типа «указатель» возможны арифметические операции, причем в них могут учитываться размеры тех объектов, которые адресуются указателем. Другое свойство указателей — их явная связь с конструкциями интеграции данных (массивы, структуры, объединения) и возможность подмены операций индексации и квалификации операциями адресной арифметики. За счет указателей программист имеет возможность удобным для себя способом структурировать адресное пространство программы и гибко изменять это структурирование.

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

Еще некоторые средства языка не ориентированы непосредственно на низкоуровневое системное программирование, но могут быть очень полезны при разработке системных программ:

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

- процедурно-ориентированный язык C вместе с тем представляет все кодовые составляющие программы в виде функций. Это дает возможность применять язык C и як инструмент функционально-ориентированного программирования, как, например, язык LISP.

Важное качество языка C — высокая эффективность объектных кодов C-программы как по быстродействию, так и по объему памяти. Хотя это качество обеспечивается не столько свойствами самого языка, сколько свойствами системы программирования, традиция построения систем программирования C именно такова, что они обеспечивают большую эффективность, чем, например, Pascal. Это связано еще и с «родословной» языков. Pascal возник как алгоритмический язык, предназначенный в своей первой версии не для написания программ, а для описания алгоритмов. Отсюда Pascal-трансляторы строились и строятся как синтаксически-ориентированные трансляторы характерно компилирующего типа: транслятор выполняет синтаксический анализ программы в соответствии с формально представленными правилами, а объектные коды формируются в основном в виде обращений к библиотечным процедурам, которые реализуют элементарные функции. Язык C ведет свое происхождение от языка BCPL, который был языком Макроассемблера. Отсюда и объектный код C-программы строится как последовательность машинных команд, которая оптимизируется для каждого конкретного выполнения данной функции.

Предлагаемые методические указания (части 1, 2) включают девять лабораторных работ и разработаны в помощь студентам, изучающим системное программирование, с  целью выработки практических навыков в следующих направлениях:

- применение языка программирования С как инструмента для системного программирования;

- программное управление аппаратными средствами ПЭВМ на низком уровне;

- использование внутренних структур данных операционной системы MS DOS для получения информации и оперативной настройки системы.

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

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

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

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

 

 

1 Лабораторная работа № 1. Работа с символьной информацией

 

Цель работы – получение практических навыков в работе с массивами и указателями языка C, обеспечение функциональной модульности.

Предварительно необходимо изучить следующие темы: «Указатели»,  «Представление строк», «Функции и передача параметров».

1.1 Постановка задачи и варианты заданий

Согласно выбранному варианту (таблица 1.1), создать функцию для обработки символьных строк. За образец следует взять библиотечные функции обработки строк языка C, но применять их в своей функции не разрешается. Следует предусмотреть обработку ошибок в задании параметров и особые случаи. Требуется разработать два варианта заданной функции, используя традиционную обработку массивов и используя адресную арифметику.

Таблица 1.1 – Варианты заданий

Функция

Назначение

1

Copies(s,s1,n)

копирование строки s в строку s1 n раз

2

Words(s)

подсчет слов в строке s

3

Concat(s1,s2)

конкатенация строк s1 и s2

4

Parse(s,t)

разделение строки s на две части: до первого вхождения символа t и после него

5

Center(s1,s2,l)

размещение строки s1 в середине строки s2 длиной l

6

Delete(s,n,l)

удаление из строки s подстроки, начиная с позиции n, длиной l

7

Left(s,l)

выравнивание строки s по левому краю до длины l.

8

Right(s,l)

выравнивание строки s по правому краю до длины l.

9

Insert(s,s1,n)

вставка в строку s подстроки s1, начиная с позиции n

10

Reverse(s)

изменение порядка символов в строке s на обратный

11

Pos(s,s1)

поиск первого вхождения подстроки s1 в строку s

12

LastPos(s,s1)

поиск последнего вхождения подстроки s1 в строку s

13

WordIndex(s,n)

определение позиции начала в строке s слова с номером n

14

WordLength(s,n)

определение длины слова с номером n в строке s

15

SubWord(s,n,m)

выделение из строки s m слов, начиная со слова n

16

WordCmp(s1,s2)

сравнение строк (с игнорированием пробелов)

17

StrSpn(s,s1)

определение длины той части строки s, которая содержит только символы из строки s1

18

Compul(s1,s2)

сравнение строк s1 и s2, игнорируя различия в регистрах

19

Overlay(s,s1,n)

перекрытие части строки s, начиная с позиции n, строкой s1

20

Replace(s,s1,s2)

замена в строке s комбинации символов s1 на s2

21

Compress(s,t)

замена в строке s всех вхождений символа t на одно

22

Word(s)

выделение первого слова из строки s

23

StrSet(s,n,l,t)

установка l символов строки s, начиная с позиции n, в значение t

24

Space(s,l)

доведение строки s до длины l путем вставки пробелов между словами

25

Findwords(s,s1)

поиск вхождения в строку s заданной фразы s1

Примечание - под «словом» везде понимается последовательность символов, которая не содержит пробелов.

1.2 Пример решения задачи

1.2.1 Постановка задачи

Создать функцию substr(s,n,l), предназначенную для выделения из строки s подстроки, начиная с позиции n, длиной l. Предусмотреть обработку ошибок в задании параметров и особые случаи. Разработать два варианта функции,  используя традиционную обработку массивов и используя адресную арифметику.

1.2.2 Метод решения

Строка символов в C представляется в памяти как массив символов, в конце которого находится байт с кодом 0 — признак конца строки. Строку, как и любой другой массив, можно обрабатывать либо традиционным методом как массив с использованием операции индексации, либо через указатели с использованием операций адресной арифметики. При работе со строкой как с массивом нужно иметь в виду, что длина строки заранее неизвестна, поэтому циклы целесообразно организовывать не со счетчиком, а до появления признака конца строки.

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

Определим состав параметров функции

int substr (src, dest, num, len);

где    src — строка, из которой выбираются символы; dest — строка, в которую записываются символы; num — номер первого символа в строке src, с которого начинается подстрока (нумерация символов ведется с 0); len — длина выходной строки.

Возможные возвращаемые значения функции установим: 1 (задание параметров правильное) и 0 (задание неправильное). Эти значения при обращениях к функции можно будет интерпретировать как «истина» или «ложь».

Обозначим через Lsrc длину строки src. Тогда при задании параметров возможны следующие случаи:

а) num+len <= Lsrc — полностью правильное задание;

б) num+len > Lsrc; num < Lsrc — правильное задание, но длина выходной строки будет меньше, чем len;

в) num >= Lsrc — неправильное задание, выходная строка будет пустой;

г) num < 0 или len <= 0 — неправильное задание, выходная строка будет пустой.

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

1.2.3 Описание логической структуры

Программа состоит из одного программного модуля — файл LAB1.C. В состав модуля входят три функции: main, substr_mas и subs_ptr. Общих переменных в программе нет. Макроконстантой N определена максимальная длина строки — 80. Листинг программы приводится в приложении А.

Функция main является главной функцией программы, она предназначена для ввода исходных данных, вызова других функций и вывода результатов. В функции определены переменные: ss и dd — входная и выходная строки соответственно; n — номер символа, с которого должна начинаться выходная строка; l — длина выходной строки.

Функция запрашивает и вводит значение входной строки, номера символа и длины. Далее она вызывает функцию substr_mas, передавая введенные значения как параметры. Если функция substr_mas возвращает 1, на экран выводятся входная и выходная строки, если 0 — выводится сообщение об ошибке и входная строка. Затем входная строка делается пустой и то же самое выполняется для функции substr_ptr.

Функция substr_mas выполняет поставленное задание методом массивов. Ее параметры: src и dest — входная и выходная строки соответственно, представленные в виде массивов неопределенного размера; num и len. Внутренние переменные i и j используются как индексы в массивах.

Функция проверяет значения параметров в соответствии со случаем 4, если условия этого случая обнаружены, в первый элемент массива dest записывается признак конца строки и функция возвращает 0.

Если случай 4 не выявлен, функция просматривает num первых символов входной строки. Если при этом будет найден признак конца строки, это случай 3, при этом в первый элемент массива dest записывается признак конца строки и функция возвращает 0.

Если признак конца в первых num символах не найден, выполняется цикл, в котором индекс входного массива начинает меняться от 1, а индекс выходного — от 0. В каждой итерации этого цикла один элемент входного массива пересылается в выходной. Если пересланный элемент является признаком конца строки (случай 2), то функция немедленно заканчивается, возвращая 1. Если в цикле не встретится конец строки, цикл завершится после len итераций. В этом случае в конец выходной строки записывается признак конца строки и функция возвращает 1.

Функция substr_ptr выполняет поставленное задание методом указателей. Ее параметры: src и dest — входная и выходная строки соответственно, представленные в виде указателей на начала строк; num и len.

Функция проверяет значения параметров в соответствии со случаем 4, если условия этого случая выявлены, по адресу, который задает dest, записывается признак конца строки и функция возвращает 0, эти действия выполняются одним оператором. Если случай 4 не обнаружен, функция пропускает num первых символов входной строки. Это сделано циклом while, условием выхода из которого является уменьшение счетчика num до 0 или появление признака конца входной строки. Порядок операций, которые выполняются в этом цикле:

- выбирается счетчик num;

- счетчик num уменьшается на 1;

- если выбранное значение счетчика 0, цикл завершается; иначе — выбирается символ, на который указывает указатель src;

- указатель src увеличивается на 1;

- если выбранное значение символа 0, то есть признак конца строки, цикл завершается, иначе — повторяется.

После выхода из цикла проверяется значение счетчика num: если оно не 0, это означает, что выход из цикла произошел по признаку конца строки (случай 3), по адресу, который задает dest, записывается признак конца строки и функция возвращает 0.

Если признак конца не найден, выполняется цикл, подобный первому циклу while, но по счетчику len. В каждой итерации этого цикла символ, на который показывает src, переписывается по адресу, задаваемому dest, после чего оба указателя увеличиваются на 1. Цикл закончится, когда будет переписано len символов или встретится признак конца строки. В любом варианте завершения цикла по текущему адресу, который содержится в указателе dest, записывается признак конца строки и функция завершается, возвращая 1.

1.2.4 Данные для тестирования

Тестирование должно обеспечить проверку работоспособности функций для всех вариантов входных данных. Входные данные, на которых проводится тестирование, сводятся в таблицу (таблица 1.2):

Таблица 1.2 – Данные для тестирования

теста

Входные данные

Выходные данные

src

num

len

dest

1

012345

2

2

23

012345

0

1

0

012345

0

6

012345

2

012345

5

3

5

012345

2

6

2345

012345

0

7

012345

3

012345

8

2

Пусто

4

012345

-1

2

Пусто

012345

5

0

Пусто

012345

5

-1

Пусто

 


1.3 Контрольные вопросы

1.3.1 В чем заключается специфика системного программного обеспечения?

1.3.2 Какие особенности языка С позволяют использовать его в качестве инструмента для системного программирования?

1.3.3 Каким образом представляются строки символов в С?

1.3.4 Что представляют собой функции?

1.3.5 С какой целью используются прототипы функций?

1.3.6 В чем разница между локальными и глобальными переменными?

1.3.7 Что такое перегрузка функции?

1.3.8 Что представляют собой указатели?

1.3.9 В чем разница между адресом, хранимым в указателе, и значением, записанным по этому адресу?

1.3.10 В чем различие между ссылкой и указателем?

 

 

2 Лабораторная работа № 2. Представление в памяти массивов и матриц

 

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

 

2.1 Постановка задачи и варианты заданий

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

1.   Все нулевые элементы размещены в левой части матрицы.

2.   Все нулевые элементы размещены в правой части матрицы.

3.   Все нулевые элементы размещены выше главной диагонали.

4.   Все нулевые элементы размещены на местах с нечетными индексами строк и столбцов.

5.   Все нулевые элементы размещены выше главной диагонали в нечетных строках и ниже главной диагонали — в четных.

6.   Все нулевые элементы размещены на главной диагонали, в первых 3 строках выше диагонали и в последних 3 строках ниже диагонали.

7.   Все нулевые элементы размещены в левой и правой четвертях матрицы (главная и побочная диагонали делят матрицу на четверти).

8.   Все нулевые элементы размещены в верхней и нижней четвертях матрицы (главная и побочная диагонали делят матрицу на четверти).

9.   Все нулевые элементы размещены квадратами 2х2 в шахматном порядке.

10.  Все нулевые элементы размещены на главной диагонали и в нижней половине участка ниже диагонали.

11.  Матрица поделена диагоналями на 4 треугольника, элементы левого и правого треугольников нулевые.

12.  Все нулевые элементы размещены в левой и верхней четвертях матрицы (главная и побочная диагонали делят матрицу на четверти).

13.  Матрица поделена диагоналями на 4 треугольника, элементы правого и нижнего треугольников нулевые.

14.  Все нулевые элементы размещены попарно в шахматном порядке (сначала 2 нулевых).

15.  Все нулевые элементы размещены ниже главной диагонали в нечетных строках и выше главной диагонали — в четных.

16.  Все нулевые элементы размещены в столбцах, индексы которых кратны четырем.

17.  Матрица поделена диагоналями на 4 треугольника, элементы верхнего и нижнего треугольников нулевые.

18.  Все нулевые элементы размещены на главной диагонали и в верхней половине участка выше диагонали.

19.  Все нулевые элементы размещены в шахматном порядке, начиная с 1-го элемента 1-й строки.

20.  Все нулевые элементы размещены в строках, индексы которых кратны трем.

21.  Все нулевые элементы размещены на местах с четными индексами строк и столбцов.

22.  Все нулевые элементы размещены в шахматном порядке, начиная со 2-го элемента 1-й строки.

23.  Все элементы нечетных столбцов нулевые.

24.  Все элементы четных строк нулевые

25.  Все нулевые элементы размещены в верхней части матрицы.

 

2.2 Пример решения задачи

2.2.1 Постановка задачи

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

2.2.2 Метод решения

2.2.2.1 Представление в памяти

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

2.2.2.2 Модульная структура программного изделия

Программное изделие должно быть отдельным модулем, файл LAB2.C, в котором должны размещаться как данные (матрица и вспомогательная информация), так и функции, которые обеспечивают доступ. Внешний доступ к программам и данным модуля возможен только через вызов функций чтения и записи элементов матрицы. Доступные извне элементы программного модуля должны быть описаны в отдельном файле LAB2.H, который может включаться в программу пользователя оператором препроцессора:

#include <lab2.h>

Пользователю должен поставляться результат компиляции — файлы LAB2.OBJ и LAB2.H.

Преобразование 2-компонентного адреса элемента матрицы, которую задает пользователь, в 1-компонентную должно выполняться отдельной функцией (так называемой функцией линеаризации), вызов которой возможен только из функций модуля. Возможны три метода преобразования адреса:

а) при создании матрицы для нее создается также и дескриптор D[N] — отдельный массив, каждый элемент которого соответствует одной строке матрицы; дескриптор заполняется значениями, подобранными так, чтобы:

n = D[x] + y,

где x, y — координаты пользователя (строка, столбец), n — линейная координата;

б) линейная координата подсчитывается методом итерации как сумма полезных длин всех строк, предшествующих строке x, и к ней прибавляется смещение y-го полезного элемента относительно начала строки;

в) для преобразования подбирается единое арифметическое выражение, которое реализует функцию: n = f(x,y).

Первый вариант обеспечивает быстрейший доступ к элементу матрицы, ибо требует наименьших расчетов при каждом доступе, но плата за это — дополнительные затраты памяти на дескриптор. Второй вариант — наихудший по всем показателям, ибо каждый доступ требует выполнения оператора цикла, а это и медленно, и занимает память. Третий вариант может быть компромиссом, он не требует дополнительной памяти и работает быстрее, чем второй. Но выражение для линеаризации будет сложнее, чем в первом варианте, следовательно, и вычисляться будет медленнее.

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

2.2.3 Описание логической структуры

В файле LAB2.C описаны следующие статические переменные:

- int NN — размерность матрицы;

- int SIZE — количество ненулевых элементов в матрице;

- int *m_addr — адрес сжатой матрицы в памяти, начальное значение этой переменной — NULL — признак того, что память не выделена;

- int L2_RESULT — общий флаг ошибки (если после выполнения любой функции он равен значению -1, то произошла ошибка).

Переменные SIZE и m_addr описаны вне функций с квалификатором static, это означает, что они доступны для всех функций в этом модуле, но недоступны для внешних модулей. Переменная L2_RESULT также описана вне всех функций, не без явного квалификатора. Эта переменная доступна не только для этого модуля, но и для всех внешних модулей, если она в них будет описана с квалификатором extern. Такое описание имеется в файле LAB2.H.

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

int creat_matr ( int N );

где N — размерность матрицы.

Функция сохраняет значение параметра в собственной статической переменной и подсчитывает необходимый размер памяти для размещения ненулевых элементов матрицы. Для выделения памяти используется библиотечная функция C malloc. Функция возвращает значение -1, если при выделении произошла ошибка, или 0, если выделение прошло нормально. При этом переменной L2_RESULT также присваивается значение 0 или -1.

Функция close_matr предназначена для освобождения памяти при завершении работы с матрицей. Прототип функции

int close_matr ( void );

Функция возвращает значение 0 при успешном освобождении, значение -1 — при попытке освободить невыделенную память.

Если адрес матрицы в памяти имеет значения NULL, это признак того, что память не выделялась, тогда функция возвращает значение -1, иначе — освобождает память при помощи библиотечной функции free и записывает адрес матрицы — NULL. Соответственно функция также устанавливает глобальный признак ошибки — L2_RESULT.

Функция read_matr предназначена для чтения элемента матрицы. Прототип функции

int read_matr(int x, int y);

где x и y — координаты (строка и столбец).

Функция возвращает значение соответствующего элемента матрицы. Если после выполнения функции значение переменной L2_RESULT равно -1, то это указывает на ошибку при обращении.

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

Функция write_matr предназначена для записи элемента в матрицу. Прототип функции

int write_matr(int x, int y, int value);

где x и y — координаты (строка и столбец), value — то значение, которое нужно записать.

Функция возвращает значение параметра value или 0, если была попытка записи в нулевой участок. Если после выполнения функции значение переменной L2_RESULT равно -1, то это указывает на ошибку при обращении. Выполнение функции подобно функции read_matr с тем отличием, что, если координаты указывают на ненулевой участок, то функция записывает value в массив m_addr.

Функция ch_coord предназначена для проверки корректности задания координат. Эта функция описана как static и поэтому может вызываться только из этого же модуля. Прототип функции

static char ch_coord(int x, int y);

где x и y - координаты (строка и столбец).

Функция возвращает значение 0, если координаты верные, значение -1 — если неверные. Соответственно функция также устанавливает значение глобальной переменной L2_RESULT. Выполнение функции собственно состоит из проверки трех условий:

- адрес матрицы не должен быть NULL, то есть матрица должна уже находиться в памяти;

- ни одна из координат не может быть меньше 0;

- ни одна из координат не может быть больше NN.

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

Функция lin предназначена для преобразования двумерных координат в индекс в одномерном массиве. Эта функция описана как static и поэтому может вызываться только из этого же модуля. Прототип функции

static int lin(int x, int y);

где x и y - координаты (строка и столбец).

Функция возвращает координату в массиве m_addr. Выражение, значение которого вычисляет и возвращает функция, подобрано из следующих  соображений. Пусть имеется квадратная матрица порядка 6 и требуется найти линейную координату элемента, обозначенного буквой A с координатами (x,y):

х

х

х

х

х

х

0

х

х

х

х

х

0

0

х

х

А

х

0

0

0

х

х

х

0

0

0

0

х

х

0

0

0

0

0

х

Координату элемента можно определить следующим образом:

n = SIZE - sizeX + offY,

где SIZE - общее количество элементов в матрице, которое рассчитывается по формуле

SIZE = NN * (NN - 1) / 2 + NN;

sizeX - количество ненулевых элементов, которые содержатся в строке x и ниже, причем

sizeX = (NN - x) * (NN - x - 1) / 2 + (NN - x);

offY - смещение нужного элемента от начала строки x, равное

offY = y - x.

 

2.2.4 Программа пользователя

Для проверки функционирования модуля создается программный модуль, который имитирует программу пользователя. Этот модуль обращается к функции creat_matr для создания матрицы нужного размера, заполняет ненулевую ее часть последовательно увеличивающимися числами, используя для этого функцию write_matr, и выводит матрицу на экран, используя для выборки ее элементов функцию read_matr. Далее в диалоговом режиме программа вводит запрос на свои действия и читает/пишет элементы матрицы с заданными координатами, обращаясь к функциям read_matr/ write_matr. Если пользователь захотел закончить работу, программа вызывает функцию close_matr. Листинги программных модулей и изменения, которые следует внести для реализации методов преобразования адреса 1 и 2 приводятся в приложении Б.

 

2.3 Контрольные вопросы

2.3.1 Что представляет собой программный модуль?

2.3.2 Какие переменные являются статическими?

2.3.3 Что представляет собой массив?

2.3.4 Как осуществляется инициализация массива?

2.3.5 Как объявить массив в динамической памяти?

2.3.6 Как удалить массив из динамической памяти?

2.3.7 Что представляет собой разреженный массив?

2.3.8 Можно ли объединять массивы?

2.3.9 Что представляет собой указатель на массив?

2.3.10 Как используются указатели на функции?

 

 

3 Лабораторная работа № 3. Управление клавиатурой

 

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

 


3.1 Постановка задачи и варианты заданий

Согласно выбранному варианту (таблица 3.1), разработать программу обработки прерывания от клавиатуры, которая должна распознавать нажатие «горячей» комбинации клавиш и реагировать на него звуковым сигналом; при первом нажатии «горячей» комбинации переходить в режим блокировки ввода заданной клавиши, при втором — отменять этот режим; системная обработка всех других клавиш нарушаться не должна. Справочная информация по функциям прерывания приведена в приложении В.

Таблица 3.1 – Варианты заданий

«Горячая» комбинация клавиш

Клавиша для блокировки

1

LeftAlt+RightShift+F1

клавиша 1

2

LeftCtrl+RightAlt+F2

клавиша 2

3

LeftAlt+RightAlt+F4

клавиша 5

4

LeftCtrl+RightShift+F5

клавиша А

5

LeftCtrl+RightShift+F6

клавиша 0

6

LeftCtrl+RightAlt+F7

клавиша 8

7

LeftAlt+RightShift+F8

клавиша 4

8

LeftShift+RightShift+F9

клавиша 7

9

LeftCtrl+RightShift+F10

клавиша =

10

LeftCtrl+RightShift+F11

клавиша +

11

LeftCtrl+RightCtrl+F12

клавиша *

12

RightCtrl+LeftShift+F1

клавиша M

13

RightCtrl+LeftShift+F2

клавиша H

14

RightCtrl+LeftCtrl+F3

клавиша D

15

RightCtrl+LeftShift+F4

клавиша K

16

RightCtrl+LeftShift+F5

клавиша Y

17

RightShift+LeftShift+F6

клавиша R

18

RightCtrl+LeftShift+F7

клавиша 6

19

RightCtrl+LeftShift+F8

клавиша 3

20

RightCtrl+LeftShift+F9

клавиша 9

21

RightCtrl+LeftShift+F10

клавиша 8

22

RightCtrl+LeftShift+F11

клавиша +

23

RightCtrl+LeftShift+F12

клавиша -

24

RightShift+LeftShift+F11

клавиша 0

25

LeftCtrl+RightCtrl+ F10

клавиша G

 

3.2 Пример решения задачи

3.2.1 Постановка задачи

Разработать программу обработки прерывания от клавиатуры, которая должна распознавать нажатие комбинации клавиш LeftCtrl+RightShift+F3 и реагировать на него звуковым сигналом; при первом нажатии этой комбинации переходить в режим блокировки ввода заданной клавиши 3, при втором — отменять этот режим; системная обработка всех других клавиш нарушаться не должна.

3.2.2 Метод решения

3.2.2.1 Структура программы

Программа состоит из основной программы и трех функций:

- void *readvect(int in) — функция читает вектор прерывания с номером in и возвращает его значение;

- void writevect (int in, void *h) — функция устанавливает новый вектор прерывания in на новый обработчик этого прерывания по адресу h;

- void interrupt new9() — процедура нового обработчика прерывания 9h.

3.2.2.2 Описание переменных

Глобальные переменные программы: old9 — адрес старого обработчика прерывания 9h; F3_code — скан-код клавиши «F3», которая входит в комбинацию «горячих» клавиш; key3_code — скан-код клавиши «3», которая будет блокироваться/разблокироваться при каждом нажатии «горячей» комбинации клавиш; f — флаг, который при каждом нажатии «горячей» комбинации клавиш переключается из состояния 0 в 1 или из 1 в 0 (состояние 1 означает, что клавиша «3» заблокирована); rr и sr — переменные, которые используются для задания значений регистров общего назначения и сегментных регистров соответственно при вызове прерывания.

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

Переменные процедуры обработки прерывания 9h:

- c — переменная, которая используется для подтверждения приема из клавиатуры, в случае, если была нажата клавиша «3», а флаг f показывал, что эта клавиша заблокирована;

- x, y — переменные, которые используются для сохранения координат курсора на экране в момент вызова процедуры обработки прерывания;

- byte17 — байт флага состояния клавиатуры в области данных BIOS по адресу 0040:0017;

- byte18 — байт флага состояния клавиатуры в области данных BIOS по адресу 0040:0018;

- mask — маска, которая используется для определения нажатия клавиши левый Shift (в этом случае бит 1 в byte17 установлен в 1);

- mask17 — маска, которая используется для определения нажатия клавиши Сtrl (в этом случае бит 2 в byte17 установлен в 1);

- mask18 — маска, которая используется для определения нажатия клавиши левый Сtrl (в этом случае бит 0 в byte18 установлен в 1);

3.2.2.3  Порты

Порт представляет собой устройство, которое соединяет процессор с внешним миром. Через порт процессор получает сигналы с устройств ввода и посылает сигналы на устройство вывода. Теоретически процессор может управлять до 65 536 портами, начиная с нулевого порта. Для управления вводом-выводом непосредственно на уровне порта используются команды IN и OUT:

- Команда IN передает данные из входного порта в регистр AL (байт) или в регистр АХ (слово). Формат команды:

IN регистр,порт

- Команда OUT передает данные в порт из регистра AL (байт) или из регистра АХ (слово). Формат команды:

OUT порт,регистр

Номер порта можно указывать статически или динамически.

Статическое указание порта возможно при непосредственном использовании значения от 0 до 255:

Ввод: IN AL.порт# ;           Ввод одного байта

Вывод: OUT порт#,АХ ;    Вывод одного слова

Динамическое указание порта устанавливается в регистре DX от 0 до 65535. Этот метод удобен для последовательной обработки нескольких портов. Значение в регистре DX в этом случае увеличивается в цикле на 1. Пример ввода байта из порта 60Н:

MOV DX,60H ;                Порт 60Н (клавиатура)

IN AL,DX ;                           Ввод байта

Список некоторых портов (номера в шестнадцатеричном представлении):

21

Регистры маски прерываний.

40...42

Таймер/счетчик.

60

Ввод с клавиатуры.

61

Звуковой порт (биты 0 и 1).

201

Управление играми.

3B0...3BF

Монохромный дисплей и параллельный адаптер печати.

3D0...3DF

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

3F0...3F7

Дисковый контроллер

В случае если, например, программа запрашивает ввод с клавиатуры, то она выдает команду прерывания INT 16H. В этом случае система устанавливает связь с BIOS, которая с помощью команды IN вводит байт с порта 60Н. На практике рекомендуется пользоваться прерываниями DOS и BIOS. Однако можно также успешно обойтись без BIOS при работе с портами 21, 40...42, 60 и 201.

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

Главная программа выполняет следующие действия:

а) запоминает адрес старого обработчика прерывания 9h, вызывая функцию readvect(in) с параметром in=9;

б) записывает в таблицу векторов прерываний адрес нового обработчика прерывания с помощью функции writevect();

в) вводом строки символов дает возможность проверить работу программы и ее реакцию на нажатие «горячей» комбинации клавиш и блокирование/разблокирование ввода клавиши «3»;

г) в конце работы восстанавливает в таблице векторов прерываний адрес старого обработчика.

Для решения задачи процедура обработки прерывания от клавиатуры new9() должна действовать по алгоритму:

а) прочитать флаги состояния клавиатуры (статус клавиш-переключателей), которые находятся в области данных BIOS (два байта по адресам 0040:0017 и 0040:0018);

б) выделить бит 1 во флаге по адресу 0040:0017 (если он равен 1, то нажата клавиша левый Shift);

в) выделить бит 2 в этом же флаге (если он равен 1, то нажат левый или правый Ctrl);

г) выделить бит 0 в флаге состояния клавиатуры по адресу 0040:0018 (если он равен 1, то нажата клавиша левый Ctrl);

д) из порта 60h прочитать скан-код нажатой клавиши;

е) если нажата комбинация клавиш левый Shift, правый Ctrl (нажата клавиша Ctrl, но это не правый Ctrl) и клавиша F3, то выполнить п.7. Иначе — перейти к п.8;

ж) сигнализировать о нажатии «горячей» комбинации клавиш звуковым сигналом; переключить значение флага блокирования ввода клавиши «3» на обратное и вызвать старый обработчик прерывания от клавиатуры;

з) прочитав байт из порта 60h, определить, нажата ли клавиша «3», и если, кроме этого, еще и флаг блокирования указывает на то, что она заблокирована (f=1), то выполнить п.п. 9 и 10, иначе — вызвать старый обработчик прерывания;

и) послать подтверждение приема в клавиатуру. Для этого в порт 61h на короткое время выставить «1» по шине старшего разряда;

к) сбросить контроллер прерываний, посылая код 20h в порт 20h.

Функция readvect() читает вектор заданного прерывания. Для чтения вектора используется функция 35h DOS (прерывания 21h):

Вход: AH = 35h;

AL = номер вектора прерывания.

Выход: ES:BX = адрес программы обработки прерывания.

Функция writevect() устанавливает новый вектор прерывания на заданный адрес. Для записи вектора используется функция 25h DOS:

Вход: AL = номер вектора прерывания;

DS:BX = 4-байтный адрес нового обработчика прерывания.

3.2.4 Программа и результаты ее работы

Текст программы приведен в приложении Г. Во время работы при первом нажатии комбинации клавиш LeftCtrl+RightShift+F3 программа переходит в режим блокирования ввода клавиши 3, при втором — отменяет этот режим.

 

3.3 Контрольные вопросы

3.3.1 Что такое прерывание?

3.3.2 Каким образом осуществляется обработка прерываний?

3.3.3 Какие действия выполняет команда прерывания INT?

3.3.4 Каким образом осуществляется установка курсора и очистка экрана?

3.3.5 Какие клавиши относятся к функциональным и что представляют собой их скэн-коды?

3.3.6 Что представляет собой расширенный ASCII код?

3.3.7 Что такое порт?

3.3.8 Что представляют собой флаги?

3.3.9 Как статически указать порт?

3.3.10 Чем отличается динамическое указание порта от статического?

 

 

4 Лабораторная работа № 4. Управление таймером

 

Цель работы – изучение функций системного таймера и закрепление практических навыков работы с ним.

 

4.1 Постановка задачи и варианты заданий

Построить модель аналого-цифрового преобразователя (АЦП), которая работает в реальном времени. Процесс, который дискретизуется, моделируется программой (программным блоком), который выполняет циклическое вычисление функции y=F(x), где x — номер итерации. Преобразователь моделируется программой, которая выполняет с заданной частотой (в реальном времени) прерывание процесса, считывание и запоминание текущего значения функции. Запомнить не меньше 80 значений функции. Обеспечить наглядное представление результатов работы «АЦП». Частоту дискретизации выдерживать с точностью до 1 Гц.

Таблица 4.1 – Варианты заданий

№№

Функция

Диапазон, R

Частота, Гц

1

y=40*(sin(x/5)+cos(x/4))+R+120

0 — 20

30,2

2

y=15*(sin(x/8)+cos(x/2))+R+100

0 — 12

26,4

3

y=4*(sin(x)+cos(x/3))+R+50

0 — 10

31,2

4

y=20*(tg(x/5)+ctg(x/8))+R+90

0 — 20

36,4

5

y=10*(tg(x/5)+ctg(x/4))+R+110

0 — 15

40,5

6

y=50*(sin(x/15)+cos(3x/4))+R+80

0 — 10

34,3

7

y=90*(sin(x/5)+cos(x/4))+R+20

0 — 30

50,0

8

y=40*(sin(2x)+cos(x/4))+R+120

0 — 20

30,2

9

y=50*(sin(x/9)+cos(x/6))+R+150

0 — 25

36,9

10

y=30*(cos(x/5)+sin(x/4))+R+85

0 — 15

33,5

 

Продолжение таблицы 4.1

11

y=20*(tg(x/8)+ctg(x/6))+R+130

0 — 15

41,5

12

y=10*(tg(x/3)+ctg(x/7))+R+60

0 — 20

26,8

13

y=15*(tg(x/2+ctg(x/3))+R+70

0 — 30

39,0

14

y=25*(tg(x/3)+ctg(x/2))+R+110

0 — 20

38,1

15

y=80*(cos(x/4)+sin(x/8))+R+140

0 — 10

35,0

16

y=60*(cos(x/2)+sin(x/4))+R+100

0 — 10

40,3

17

y=30*(cos(x)+sin(x/2))+R+70

0 — 30

50,0

18

y=50*(cos(x/3)+sin(x))+R+90

0 — 15

45,2

19

y=60*(cos()+sin(x/2))+R+140

0 — 20

33,4

20

y=40*(sh(x/4)+ch(x/8))+R+100

0 — 10

35,0

21

y=60*(sh(x/2)+ch(x/4))+R+80

0 — 20

40,6

22

y=30*(sh(x/9)+ch(x/3))+R+90

0 — 10

46,8

23

y=20*(sh(x/5)+ch(x/10))+R+130

0 — 10

48,0

24

y=50*(sh(x/10)+ch(x/6))+R+140

0 — 20

38,6

25

y=40*(sh(x/12)+ch(x/6))+R+90

0 — 10

36,8

 

4.2 Пример решения задачи

4.2.1 Постановка задачи

Построить модель аналого-цифрового преобразователя (АЦП) по следующим данным: функция — y=50*(sin(x/10)+cos(x/8))+R+150; R изменяется в диапазоне 0 — 10; частота — 36.4 Гц.

4.2.2 Метод решения

4.2.2.1 Структура программы

Программа состоит из основной программы и трех функций:

- void *readvect(int in) — функция читает вектор прерывания с номером in и возвращает его значение;

- void writevect (int in, void *h) — функция устанавливает новый вектор прерывания in на новый обработчик этого прерывания по адресу h;

- void interrupt newtime() — процедура нового обработчика прерывания таймера.

4.2.2.2 Описание переменных

В программе применяются константы:

- TIMEINT=8 — номер прерывания таймера;

- NN=100 — максимальное число показаний АЦП.

Переменные, глобальные для всей программы:

- y — массив показаний АЦП;

- ny — текущий индекс в массиве показаний;

- yc — текущее значение функции;

- kf — счетчик вызовов oldtime (oldtime вызывается каждые второй раз);

- rr и sr — переменные, которые используются для задания значений регистров общего назначения и сегментных регистров соответственно при вызове прерывания.

Переменные главной программы:

- oldtic — старый коэффициент деления;

- newtic — новый коэффициент деления (применяется для увеличения частоты вызова прерываний таймера);

- x — аргумент заданной функции F(x);

- dd — тип графического адаптера;

- m — режим графики;

- errorcode — код результата инициализации графики.

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

Программу можно назвать моделью процесса АЦП. Главная программа постоянно вычисляет значения заданной функции F(x) при переменном аргументе, что имитирует непрерывный сигнал, а обработчик прерывания 8 имитирует преобразователь с постоянным шагом дискретизации по времени. Перед началом работы канал 0 таймера программируется на частоту в 2 раза большую обычной (записью в порт 43h управляющего байта 00110110b=36h, а потом посылкой в порт 40h нового значения коэффициента деления), таким образом, “частота дискретизации” составляет около 36.4 Гц. При поступлении следующего прерывания запоминается текущее значение функции F(x). Старый обработчик прерывания oldtime вызывается не при каждом прерывании, а лишь один раз из двух (переменная kf — счетчик по модулю 2). Когда oldtime не вызывается, обработчик сам сбрасывает контроллер прерываний посылкой значения 20h в порт 20h. После набора 100 «показаний АЦП» восстанавливается старый вектор обработчика таймера, а результат аналого-цифрового преобразования выводится на терминал в графическом режиме в виде решетчатой функции.

Функция readvect() читает вектор заданного прерывания. Для чтения вектора применяется функция 35h DOS (прерывания 21h):

Вход: AH = 35h;

AL = номер вектора прерывания.

Выход: ES:BX = адрес программы обработки прерывания.

Функция writevect() устанавливает новый вектор прерывания по заданному адресу. Для записи вектора применяется функция 25h DOS:

Вход: AH = 25h;

AL = номер вектора прерывания;

DS:BX = 4-байтный адрес нового обработчика прерывания.

4.2.4 Программа и результаты ее работы

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

4.3 Контрольные вопросы

4.3.1 Что собой представляет системный таймер?

4.3.2 Назовите основные функции системного таймера.

4.3.3 Что понимается под аналого-цифровым преобразованием?

4.3.4 Что представляет собой модель аналого-цифрового преобразователя?

4.3.5 Какие средства используются для моделирования АЦП?

4.3.6 Какова структура разработанной программы?

4.3.7 Опишите алгоритм программы.

4.3.8 Как можно интерпретировать результаты работы программы?

4.3.9 Каким образом осуществляется инициализация графического режима?

4.3.10 Как отслеживаются возможные ошибки?

 

 

5 Лабораторная работа № 5. Управление видеоадаптером

 

Цель работы – изучение особенностей функционирования видеосистемы в текстовом режиме и получение практических навыков работы с видеомонитором в этом режиме.

 

5.1 Постановка задачи и варианты заданий

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

Таблица 5.1 – Варианты заданий

Видеоэффект

1

«Теннисный мячик» — шарик, который летает по экрану и отражается от рамок и границ экрана.

2

«Сухой лист» — «опадание» букв с экрана случайным образом.

3

«Жук-пожиратель» — фигурка, которая перемещается по экрану по случайной траектории и «съедает» буквы.

4

«Удав» — то же, что и «жук», но к тому же он увеличивается в размерах по мере «поедания» букв.

5

«Спираль» - кривая, которая увеличивается из центра экрана, изменяя цвет.

6

«Бегущая строка» - задаваемый текст, перемещающийся по случайной траектории на экране.

7

«Вечный двигатель» - кривая, которая формируется на экране и изменяет свою форму с течением времени.

8

«Геометрический вальс» - кривая, которая формируется на экране и перемещается по случайной траектории.

9

«Метаморфозы» - куб, перемещаясь по экрану, изменяет свою форму.

Продолжение таблицы 5.1

10

«Летающие кубы» - 3 куба разного цвета, которые летают по случайной траектории на экране.

11

«Вращение» - задаваемый текст вращается вокруг своей оси, изменяя цвет.

12

«Вселенная» - звезды мерцают на небе, создавая эффект перемещения.

13

«Падающая звезда» - по звездному небу перемещается, «падая» звезда.

14

«Соты» - экран условно делится на квадраты, которые последовательно заполняются шестиугольниками.

15

«Вечный двигатель» - окружность, которая формируется на экране и изменяет свою форму с течением времени.

16

«Геометрический вальс» - окружность, которая формируется на экране и перемещается по случайной траектории.

17

«Метаморфозы» - шар, перемещаясь по экрану, изменяет свою форму.

18

«Летающие шары» - 4 шара разного цвета, которые летают по экрану.

19

Экран случайным образом постепенно заполняется кругами разного цвета до нажатия клавиши.

20

«Исчезновение» - задаваемый текст, при нажатии клавиши начинает исчезать с экрана. Размер текста должен быть подобран так, чтобы он занимал по возможности весь экран.

21

Экран постепенно заполняется случайным образом буквами, при нажатии клавиши буквы изменяют цвет.

22

«Жук-пожиратель-1» — фигурка, которая перемещается по экрану по случайной траектории и «съедает» только гласные буквы.

23

«Удав-1» — то же, что и «жук-1», но к тому же он увеличивается в размерах по мере «поедания» букв.

24

«Теннисный мячик» — шарик, который летает по экрану и отражается от верхней и нижней границ экрана, изменяя цвет.

25

«Метеорит» - по звездному небу летит метеорит.

   

5.2 Пример решения задачи

5.2.1 Постановка задачи

Применяя прямую запись в видеопамять, получить на экране следующий  динамический видеоэффект:

- весь экран (80х25 символов) условно делится на прямоугольники размером (10х5 символов), текущий прямоугольник инвертирует экран под собой;

- управление положением текущего прямоугольника на экране осуществляется с помощью клавиш управления курсором, а при нажатии клавиши “пробел” текущий прямоугольник обменивается содержимым с левым верхним прямоугольником; при нажатии клавиши Enter содержимое прямоугольников экрана перемешивается случайным образом между собой до нажатия любой клавиши.

Восстановить начальный экран или выйти из программы можно с помощью клавиши Esc.

5.2.2 Метод решения

5.2.2.1 Структура программы

Программа состоит из основной функции main() и семи вспомогательных функций

- byte GetSym(x1,y1) — функция читает символ с заданной позиции экрана дисплея;

- byte GetAtr(x1,y1) — функция читает атрибут символа с заданной позиции экрана дисплея;

- void PutSym(x1,y1,sym) — функция выводит на экран дисплея символ в заданную позицию (x1,y1);

- void PutAtr(x1,y1,atr) — функция меняет на экране дисплея атрибут символа в заданной позиции (x1,y1);

- void Invert(x1,y1) — функция инвертирует участок на экране размером (10х5), координаты (x1,y1) задают один из участков на экране;

- void Change(x,y) — функция обменивает содержимое текущего участка с содержимым левого верхнего участка на экране. Координаты (x,y) задают положение текущего участка;

- void RandText(void) — функция перетасовывает все участки на экране псевдослучайным образом.

5.2.2.2 Описание переменных

Переменные, глобальные для всей программы: xk — координата X текущего участка; yk — координата Y текущего участка; координаты участка задаются в пределах: X — [0..7], Y — [0..4] .

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

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

Функции GetSym(x1,y1), GetAtr(x1,y1) читают непосредственно из видеопамяти дисплея символ и атрибут соответственно.

Функции PutSym(x1,y1,sym), PutAtr(x1,y1,atr) выводят непосредственно в видеопамять дисплея символ и атрибут соответственно.

Во всех этих четырех функциях координаты задаются в квадрате 79х24 символов (нумерация начинается с нуля).

Функция Invert(x1,y1) использует функции GetAtr и PutAtr для инверсии прямоугольника экрана размером 10х5 по маске 0x7f, при этом независимо выполняется инверсия фона и цвета символа.

Функция Change(x,y) обменивает содержимое текущего участка с содержимым левого верхнего участка путем последовательного побайтного обмена атрибутов и символов. Она использует функции GetSym, GetAtr, PutSym, PutAtr.

Функция RandText(void) псевдослучайным образом перетасовывает все участки на экране, при этом она в цикле увеличивает на единицу локальные в данной функции координаты текущего участка xk, yk и обращается к функции Change. Таким образом, достигается эффект перемешивания. Функция работает до нажатия любой клавиши.

5.2.4 Программа и результаты ее работы

Текст программы приведен в приложении Е. Результаты работы программы выводятся на экран терминала и меняются интерактивно.

 

5.3 Контрольные вопросы

5.3.1 Перечислите возможные режимы функционирования видеоадаптера.

5.3.2 Какие функции обеспечивает графический режим?

5.3.3 Для каких целей предназначен текстовый режим?

5.3.4 Какие функции обеспечивает текстовый режим?

5.3.5 Какие цвета являются основными?

5.3.6 Что понимается под уровнем яркости?

5.3.7 Что представляет собой байт-атрибут?

5.3.8 Каким способом можно инвертировать экран?

5.3.9 Какие функции языка обеспечивают случайное перемешивание (перемещение)?

5.3.10 Как практически можно осуществить чтение/запись атрибутов в видеопамять?


Приложение A

Листинг программы и комментарии к лабораторной работе № 1

 

#include <stdio.h>

#include <conio.h>

#define N 80

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

int substr_mas(char src[N], char dest[N], int num, int len)

{

int i, j;

/* проверка случая 4*/

if((num<0)||(len<=0))

{

dest[0]=0; return 0;

}

/* выход на символ num*/

for (i=0; i<=num; i++)

/* проверка случая 3*/

if(src[i]=='\0')

{

dest[0]=0; return 0;

}

/* перезапись символов*/

for (i--, j=0; j<len; j++, i++)

{

dest[j]=src[i];

/* проверка случая 2*/

if (dest[j]=='\0') return 1;

}

/* запись признака конца в выходную строку*/

dest[j]='\0';

return 1;

}

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

/* функция выделения подстроки*/

/* адресная арифметика*/

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

int substr_ptr(char *src, char *dest, int num, int len)

{

/* проверка случая 4*/

if((num<0)||(len<=0)) return dest[0]=0;

/* выход на символ num или на конец строки*/

while (num-- && *src++);

/* проверка случая 3*/

if(!num) return dest[0]=0;

/* перезапись символов*/

while(len-- && *src) *dest++=*src++;

/* запись признака конца в выходную строку*/

*dest=0;

return 1;

}

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

main()

{

char ss[N], dd[N];

int n,l;

clrscr();

printf("Enter your symbols:\n");

gets(ss);

printf("begin=");

scanf("%d",&n);

printf("length=");

scanf("%d",&l);

printf("Arrays:\n");

if(substr_mas(ss,dd,n,l)) printf(">>%s<<\n>>%s<<\n",ss,dd);

else printf("Error! >>%s<<\n",dd);

printf("Address arithmetic:\n");

if(substr_ptr(ss,dd,n,l)) printf(">>%s<<\n>>%s<<\n",ss,dd);

else printf("Error! >>%s<<\n",dd);

getch();

}


Приложение Б

Листинг программы и комментарии к лабораторной работе № 2

 

/************************* файл LAB2.H ************************/

/* Описание функций и внешних переменных файла LAB2.C*/

extern int L2_RESULT;

/* Глобальная переменная – флаг ошибки*/

/* Выделение памяти под матрицу*/

int creat_matr (int N);

/* Чтение элемента матрицы по заданным координатам*/

int read_matr(int x, int y);

/* Запись элемента в матрицу по заданным координатам*/

int write_matr(int x, int y, int value);

/* Уничтожение матрицы*/

int close_matr(void);

/*********************** конец файла LAB2.H *******************/

 

/************************** файл LAB2.С **********************/

/* В файле определены функции и переменные для обработки матрицы,

    заполненной нулями ниже главной диагонали*/

#include <alloc.h>

static int NN;                        /* размерность матрицы*/

static int SIZE;                     /* размер памяти*/

static int *m_addr=NULL;  /* адрес сжатой матрицы*/

static int lin(int,int);             /* описание функции линеаризации*/

static char ch_coord(int, int);   /* описание функции проверки*/

int L2_RESULT;                  /* внешняя переменная – флаг ошибки*/

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

/* Выделение памяти под сжатую матрицу*/

int creat_matr(int N)

/* N – размер матрицы*/

{

NN=N;

SIZE=N*(N-1)/2+N;

if((m_addr=(int *)malloc(SIZE*sizeof(int))) == NULL)

return L2_RESULT=-1;

else

return L2_RESULT=0;

/* Возвращает 0, если выделение памяти прошло успешно, иначе -1 */

}

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

/* Уничтожение матрицы (освобождение памяти*/

int close_matr(void)

{ if (m_addr!=NULL)

{

free(m_addr);

m_addr=NULL;

return L2_RESULT=0;

/* Возвращает 0, если освобождение памяти прошло успешно, иначе -1*/

}

else return L2_RESULT=-1;

}

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

/* Чтение элемента матрицы по заданным координатам*/

int read_matr(int x, int y)

/* x, y – координаты (строка, столбец)*/

{

if(ch_coord(x,y)) return 0;

/* Если координаты попадают в нулевой участок, возвращается 0,

иначе - применяется функция линеаризации */

return(x>y) ? 0 : m_addr[lin(x,y)];

/* Проверка успешности чтения по переменной L2_RESULT:

0 – без ошибок, -1 – ошибка  */

}

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

/* Запись элемента матрицы по заданным координатам*/

int write_matr(int x,int y,int value)

/* x, y – координаты, value – записываемое значение*/

{

if(ch_coord(x,y)) return 0;

/* Если координаты попадают в нулевой участок, записи нет,

 иначе - применяется функция линеаризации*/

if(x>y) return 0;

else return m_addr[lin(x,y)]=value;

/* Проверка успешности записи по переменной L2_RESULT */

}

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

/*Преобразование 2-мерных координат в линейные (вариант 3)*/

static int lin(int x,int y)

{

int n;

n=NN-x;

return SIZE-n*(n-1)/2-n+y-x;

}

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

/* Проверка корректности обращения*/

static char ch_coord(int x,int y)

{

if((m_addr==NULL) || (x>SIZE) || (y>SIZE) || (x<0) || (y<0))

/* Если матрица не размещена в памяти, или заданные координаты

выходят за пределы матрицы */

return L2_RESULT=-1;

return L2_RESULT=0;

}

/********************** конец файла LAB2.С *********************/

 

/************************* файл MAIN2.С ***********************/

/*Программа пользователя*/

#include “ lab2.h”

#include <conio.h>

#include <stdio.h>

#include <stdlib.h>

main()

{int R,i,j,m;   /*размерность, номера строки и столбца, значения элемента*/

int op;               /* операция*/

clrscr();

printf("Введите размерность матрицы >");

scanf("%d",R);

/* Создание матрицы*/

if (creat_matr(R))

{ printf("Ошибка создания матрицы \n");

exit(0);}

/* Заполнение матрицы*/

for(m=j=0; j<R; j++)

for(i=0; i<R; i++)

write_matr(i,j,++m);

while(1)

/* Вывод матрицы на экран*/

{ clrscr();

for(j=0; j<R; j++)

{ for(i=0; i<R; i++)

printf("%3d",read_matr(i,j));

printf("\n");}

printf("0 – выход \n1 – чтение \n2 - запись\n>");

scanf("%d",&op);

switch (op)

{ case 0:

if(close_matr()) printf("Ошибка при уничтожении\n");

else printf(“Матрица уничтожена \n”);

exit (0);

case 1: case 2:

printf("Введите номер строки>");

scanf("%d",&j);

printf("Введите номер столбца>");

scanf("%d",&i);

if (op==2)

{printf("Введите значение элемента>");

scanf("%d",&m);

write_matr(j,i,m);

if(L2_RESULT<0) printf("Ошибка записи\n");}

else

{ m=read_matr(j,i);

if(L2_RESULT<0) printf("Ошибка записи\n");

else printf("Считано: %d\n",m);}

printf("Нажмите клавишу\n");

getch();

break;

}

}

}

/********************** конец файла MAIN2.С ********************/

 

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

- к общим статическим переменным добавляется переменная

static int *D;   /* адрес дескриптора*/

- в функцию create_matr добавляется блок

{int i, s;

D=(int *)malloc(N*sizeof(int));

for (D[0]=0, s=NN-1, i=1; i<NN; i++)

D[i]=D[i-1]+s--;}

- функция lin изменяется на функцию вида

static int lin(int x, int y)

{return D[x]+y;}

 

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

static int lin(int x, int y)

{int s;

for (s=j=0; j<x; j++)

s+=NN-j;

return s+y-x;}


Приложение В

Функции прерывания DOS INT 21H

 

Некоторые базовые функции для прерывания DOS INT 21H. Код функции устанавливается в регистре AH:

00

Завершение программы (аналогично INT 20H).

01

Ввод символа с клавиатуры с эхом на экран.

02

Вывод символа на экран.

03

Ввод символа из асинхронного коммуникационного канала.

04

Вывод символа на асинхронный коммуникационный канал.

05

Вывод символа на печать (гл.19).

06

Прямой ввод с клавиатуры и вывод на экран.

07

Ввод с клавиатуры без эха и без проверки Ctrl/Break.

08

Ввод с клавиатуры без эха с проверкой Ctrl/Break.

09

Вывод строки символов на экран.

0A

Ввод с клавиатуры с буферизацией.

0B

Проверка наличия ввода с клавиатуры.

0C

Очистка буфера ввода с клавиатуры и запрос на ввод.

0D

Сброс диска.

Установка текущего дисковода.

0F

Открытие файла через FCB.

10

Закрытие файла через FCB.

11

Начальный поиск файла по шаблону.

12

Поиск следующего файла по шаблону.

13

Удаление файла с диска.

14

Последовательное чтение файла.

15

Последовательная запись файла.

16

Создание файла.

17

Переименование файла.

18

Внутренняя операция DOS.

19

Определение текущего дисковода.

Установка области передачи данных (DTA).

Получение таблицы FAT для текущего дисковода.

Получение FAT для любого дисковода.

21

Чтение с диска с прямым доступом.

22

Запись на диск с прямым доступом.

23

Определение размера файла.

24

Установка номера записи для прямого доступа.

25

Установка вектора прерывания.

26

Создание программного сегмента.

27

Чтение блока записей с прямым доступом.

28

Запись блока с прямым доступом.

29

Преобразование имени файла во внутренние параметры.

Получение даты (CX-год,DН-месяц,DL-день).

Установка даты.

Получение времени (CH-час,CL-мин,DН-с,DL-1/100с).

2D

Установка времени.

Установка/отмена верификации записи на диск.

2F

Получение адреса DTA в регистровой паре ES:BX.

30

Получение номера версии DOS в регистре АХ.

31

Завершение программы, после чего она остается резидентной в памяти.

33

Проверка Ctrl/Break.

35

Получение вектора прерывания (адреса подпрограммы).

36

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

38

Получение государственно зависимых форматов.

39

Создание подкаталога (команда MKDIR).

3А

Удаление подкаталога (команда RMDIR).

Установка текущего каталога (команда CHDIR).

3C

Создание файла без использования FCB.

3D

Открытие файла без использования FCB.

3E

Закрытие файла без использования FCB.

3F

Чтение из файла или ввод с устройства.

40

Запись в файл или вывод на устройство.

41

Удаление файла из каталога.

42

Установка позиции для последовательного доступа.

43

Изменение атрибутов файла.

44

Управление вводом-выводом для различных устройств.

45

Дублирование файлового номера.

46

«Склеивание» дублированных файловых номеров.

47

Получение текущего каталога.

48

Выделение памяти из свободного пространства.

49

Освобождение выделенной памяти.

Изменение длины блока выделенной памяти.

Загрузка/выполнение программы (подпроцесса).

Завершение подпроцесса с возвратом управления.

4D

Получение кода завершения подпроцесса.

Начальный поиск файла по шаблону.

4F

Поиск следующего файла по шаблону.

54

Получение состояния верификации.

56

Переименование файла.

57

Получение/установка даты и времени изменения файла.

59

Получение расширенного кода ошибки.

Создание временного файла.

Создание нового файла.

Блокирование/разблокирование доступа к файлу.

62

Получение адреса префикса программного сегмента (PSP).

 


Приложение Г

Листинг программы и комментарии к лабораторной работе № 3

 

/*****************Управление клавиатурой******************/

/*Подключение стандартных заголовков*/

#include <dos.h>

#include <stdio.h>

#include <conio.h>

void interrupt (*old9)();               /*старый обработчик прерывания 9h*/

void interrupt new9();              /*новый обработчик прерывания 9h*/

void *readvect (int in);             /*чтение вектора*/

void writevect (int in,void *h);  /*запись вектора*/

unsigned char F3_code=61;        /*скан-код F3*/

unsigned char key3_code=4;       /*скан-код 3*/

char f=0;                                 /*флаг*/

union REGS rr;

struct SREGS sr;

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

void main()

{char string[80];                         /*буфер для ввода текста*/

textbackground(0);

clrscr();

textattr(0x0a);

cprintf("-----------------");

cprintf("Лабораторная работа №3");

cprintf("-----------------");

cprintf("-----------------");

cprintf("Управление клавиатурой");

cprintf("-----------------");

old9=readvect(9);

writevect(9,new9);

textattr(0x0c);

cprintf("\n\n\r\"горячая\"комбинация: ");

textattr(0x0a);

cprintf("Left Shift, Right Ctrl, F3\n\r");

textattr(0x0b);

cprintf("Клавиша, которая блокируется: ");

textattr(0x0f);

cprintf("3");

textattr(0x07);

cprintf("\r\nВведите строку символов>");

scanf("%s",string);

writevect(9,old9);}

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

/*чтение вектора*/

void *readvect(int in)

{rr.h.ah=0x35;

rr.h.al=in;

intdosx(&rr,&rr,&sr);

return(MK_FP(sr.es,rr.x.bx));}

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

/*запись вектора*/

void writevect(int in,void *h)

{rr.h.ah=0x25;

rr.h.al=in;

sr.ds=FP_SEG(h);

rr.x.dx=FP_OFF(h);

intdosx(&rr,&rr,&sr);}

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

/*новый обработчик 9-го прерывания*/

void interrupt new9()

{unsigned char c,x,y;

unsigned char byte17,byte18;

unsigned char mask=0x02;

unsigned char mask17=0x04;

unsigned char mask18=0x01;

byte17=peekb(0x40,0x17);

byte18=peekb(0x40,0x18);

if((inportb(0x60)==F3_code)&&(byte17&mask)&&(byte17&mask17)&&

(!(byte18&mask18)))

{cputs("\7");

x=wherex();

y=wherey();

gotoxy(55,3);

textattr(0x1e);

if(f==0)  {f=1; cprintf("Клавиша \"3\" заблокирована ");}

else { f=0;

cprintf("Клавиша \"3\" разблокирована ");}

gotoxy(x,y);

textattr(0x07);

(*old9)();}

if((f==1)&&(inportb(0x60)==key3_code))

{c=inportb(0x61);

outportb(0x61,c|0x80);

outportb(0x61,c);

outportb(0x20,0x20);}

else (*old9)();

}


Приложение Д

Листинг программы и комментарии к лабораторной работе № 4

 

/*****************Управление таймером******************/

#include <dos.h>

#include <math.h>

#include <stdlib.h>

#include <graphics.h>

#include <time.h>

#include <conio.h>

#include <stdio.h>

#define TIMEINT 8                 /*прерывание таймера*/

#define NN 100                        /* максимальное количество показаний*/

void interrupt (*oldtime)();       /* старый обработчик прерываний таймера*/

void interrupt newtime();          /* новый обработчик прерываний таймера*/

static int y[NN];                        /* накопитель показаний*/

static int ny;                             /* индекс в массиве у*/

static int yc;                              /* текущее значение*/

static int kf;                              /* счетчик вызовов oldtime*/

union REGS rr;                        /* запись нового вектора*/

struct SREGS sr;

void *readvect(int in);              /* получение старого вектора*/

void writevect(int in, void *h);  /* запись нового вектора*/

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

void main()

{unsigned oldtic=65535u;       /* старый коэффициент деления*/

unsigned newtic=32768u;       /* новый коэффициент деления*/

int dd,m,errorcode;              /*графич. драйвер, графич. режим, код ошибки*/

double x;                              /* аргумент функций sin и cos*/

textbackground(0);

clrscr(); textattr(0x0a);

cprintf("Лабораторная работа №4");

cprintf("\n Управление таймером");

textattr(0x8e); gotoxy(35,12); cprintf("Please wait");

/* программирование канала 0*/

outportb(0x43,0x36);                       /* управляющий байт*/

outportb(0x40,newtic&0x00ff);        /* младший байт счетчика*/

outportb(0x40,newtic>>8);         /* старший байт счетчика*/

ny=-1;                                         /* признак того, что АЦП еще не началось*/

kf=15;

oldtime=readvect(TIMEINT);      /* подключение к вектору*/

writevect(TIMEINT,newtime);      /* запуск непрерывного процесса*/

randomize();                               

for(x=ny=0; ny<NN; x+=1)

yc=(int)(50*(sin(x/10)+cos(x/8))+random(11)+150);  

writevect(TIMEINT,oldtime);       /* восстановление вектора*/

/* восстановление канала 0*/

outportb(0x43,0x36);                   /* управляющий байт*/

outportb(0x40,oldtic&0x00ff);      /* младший байт счетчика*/

outportb(0x40,oldtic>>8);           /* старший байт счетчика*/

/* вывод записанных в память результатов*/

dd=3;                                              /* EGA, 16 цветов*/

m=1;                                           /* режим 640*350*/

initgraph(&dd,&m,"");

errorcode=graphresult();             /* проверка результата инициализации*/

if(errorcode!=grOk)                     /* ошибка графического режима*/

{printf("Graphics error: %s\n", grapherrormsg(errorcode));

printf("Press any key to halt:");

getch();

exit(1);}                                       /* аварийное завершение*/

setcolor(10); settextstyle(0,0,2);

outtextxy(15,10,"Результат аналого-цифрового преобразования:");

setcolor(9); rectangle(15,40,624,330); setcolor(11);

for(ny=0; ny<NN; ny++)

{circle(22+ny*6,330-y[ny]*1,2);

line(22+ny*6,330,22+ny*6,330-y[ny]*1);}

setcolor(12); settextstyle(0,0,1);

outtextxy(260,340,"Нажмите любую клавишу...");

getch(); closegraph();}

void interrupt newtime()           /* новый обработчик прерываний таймера*/

{if(--kf<0)

{ (*oldtime)();                          /* вызов oldtime во второй раз*/

kf=1;}

else outportb(0x20,0x20);            /* иначе – сброс контроллера*/

if((ny>=0)                                   /* если АЦП началось */

&&(ny<NN))                               /* и NN показаний еще не набрано*/

y[ny++]=yc; }                         /* запоминание очередного показания*/

/* получение старого вектора*/

void *readvect(int in)

{rr.h.ah=0x35; rr.h.al=in;

intdosx(&rr,&rr,&sr);

return(MK_FP(sr.es,rr.x.bx));}

void writevect(int in,void *h)    /* запись нового вектора*/

{rr.h.ah=0x25;

rr.h.al=in;

sr.ds=FP_SEG(h);

rr.x.dx=FP_OFF(h);

intdosx(&rr,&rr,&sr);}

Приложение Е

Листинг программы и комментарии к лабораторной работе № 5

 

/*****************Управление видеоадаптером********************/

#include <dos.h>

#include <stdio.h>

#include <stdlib.h>

#include <conio.h>

#include <time.h>

/*****************Константы***********************************/

#define VSEG 0xb800              /* сегментный адрес видеопамяти*/

#define byte unsigned char

#define word unsigned int

#define Esc 27

#define Spase 32

#define Enter 13

#define Up 0x48

#define Down 0x50

#define Left 0x4b

#define Right 0x4d

#define Home 0x47

int xk,yk;

/* Чтение символа из видеопамяти*/

byte GetSym(x1,y1)

int x1,y1;

{

return(peekb(VSEG,y1*160+x1*2));

}

/* Чтение атрибута из видеопамяти*/

byte GetAtr(x1,y1)

int x1,y1;

{

return(peekb(VSEG,y1*160+x1*2+1));

}

/* Запись символа в видеопамять*/

void PutSym(x1,y1,sym)

int x1,y1;

byte sym;

{

pokeb(VSEG,y1*160+x1*2,sym);

}

/* Запись атрибута в видеопамять*/

void PutAtr(x1,y1,atr)

int x1,y1;

byte atr;

{

pokeb(VSEG,y1*160+x1*2+1,atr);

}

/* Инверсия квадрата на экране*/

void Invert(x1,y1)

int x1,y1;

{

byte b;

int i,j;

for(j=0;j<5;j++)

for(i=0;i<10;i++)

{b=GetAtr(x1*10+i,y1*5+j);

PutAtr(x1*10+i,y1*5+j,(b^0x7f));}

}

/* Замена текущего квадрата на левый верхний*/

void Change(x,y)

byte x,y;

{

int i,j;

byte ba,bs;

if((x!=0)||(y!=0))

for(j=0;j<5;j++)

for(i=0;i<10;i++)

{bs=GetSym(x*10+i,y*5+j);

ba=GetAtr(x*10+i,y*5+j);

PutSym(x*10+i,y*5+j,GetSym(i,j));

PutAtr(x*10+i,y*5+j,GetAtr(i,j));

PutSym(i,j,bs);

PutAtr(i,j,ba);

}}

/* Перемешивание квадратов до нажатия клавиши*/

void RandText(void)

{

Invert(xk,yk);

xk=5;

yk=1;

while(!kbhit())

{Change(xk,yk);

xk++;

if(xk>7) xk=0;

yk++;

if(yk>4) yk=0;}

Invert(xk,yk);}

/* начало основной программы*/

void main(int argn)

{

int i;

xk=0;

yk=0;

if(argn>1){}

else                /* Если параметров нет, вывод инструкции*/

{textattr(10);

clrscr();

cprintf("--------------");

cprintf("   Лабораторная работа № 5    ");

cprintf("--------------");

cprintf("--------------");

cprintf("  Управление видеоадаптером      ");

cprintf("--------------");

textattr(15);

gotoxy(23,4);

cprintf("Демонстрация работы с видеопамятью.");

textattr(12);

gotoxy(30,6);

cprintf("<<МОЗАИКА>>");

textattr(14);

gotoxy(30,8);

cprintf("Клавиши управления:");

gotoxy(7,10);

cprintf("<Left, Right, Up, Down > - ");

cprintf("управление выделенным квадратом.");

gotoxy(7,11);

cprintf("<Spaсe Bar> - обмен содержимым между выделенным ");

gotoxy(7,12);

cprintf("     квадратом и левым верхним квадратом.");

gotoxy(7,13);

cprintf("<Enter> - перемешивание квадратов до нажатия любой клавиши.");

gotoxy(7,14);

cprintf("<Esc> - выход.");

textattr(11);

gotoxy(28,16);

cprintf("ЗАДАЧА ИГРЫ :");

gotoxy(14,17);

cprintf("Собрать при помощи клавиш управления начальный экран.");

textattr(12);

gotoxy(27,19);

cprintf("Ж е л а е м    у с п е х а !!!");

textattr(7);

gotoxy(1,21);

cprintf("Примечание - при запуске с параметром <->");

gotoxy(13,22);

cprintf("начальным экраном для игры является текущий.");}

Invert(xk,yk);

for(i=0;i==0;)

switch (getch())

/* Обработка начальных клавиш*/

{case Esc: i++; break;

case Enter: RandText(); break;

case Spase: Invert(xk,yk);

Change(xk,yk);

Invert(xk,yk);

break;

case 0:

switch (getch())

{case Left:Invert(xk,yk);

     xk--;

     if(xk<0) xk=7;

     Invert(xk,yk);

     break;

case Right:Invert(xk,yk);

     xk++;

     if(xk>7) xk=0;

     Invert(xk,yk);

     break;

case Up:Invert(xk,yk);

     yk--;

     if(yk<0) yk=4;

     Invert(xk,yk);

     break;

case Down:Invert(xk,yk);

     yk++;

     if(yk>4) yk=0;

     Invert(xk,yk);

     break;}}

Invert(xk,yk);

}

 


Библиография

 

1. Гордеев А.В., Молчанов А.Ю. Системное программное обеспечение. – СПб.: Питер, 2002.

2  . Фельдман С.К. Системное программирование на персональном компьютере. - М.: ЗАО «Новый издательский дом», 2004.

3. Архангельский А.Я. C++ Builder 6. Справочное пособие. Книга 1. Язык С++. – М.: Бином-Пресс, 2002.

4. Архангельский А.Я. C++ Builder 6. Справочное пособие. Книга 2. Классы и компоненты. – М.: Бином-Пресс, 2002.

5. Ашарина И.В. Основы программирования на языках С и С++. - М.: Горячая линия - Телеком, 2002.

6. Иртегов Д.В. Введение в операционные системы. – СПб.: БХВ-Петербург, 2002.

7. Таненбаум Э. Современные операционные системы. -  СПб.: Питер, 2002.

8. Гордеев В.А. Операционные системы. – СПб.: Питер, 2004.

9. Пирогов В.Ю. ASSEMBLER. Учебный курс. - М.: Издательство Нолидж, 2001.

10. Зубков С. В. Assembler для DOS, Windows и UNIX.  - М.: ДМК Пресс; СПб.: Питер, 2004.

 

 

 

Содержание

 

Введение................................................................................................. 3

1 Лабораторная работа №1. Работа с символьной информацией........ 6

2 Лабораторная работа №2. Представление в памяти массивов и

матриц................................................................................................... 11

3 Лабораторная работа №3. Управление клавиатурой.......................... 16

4 Лабораторная работа №4. Управление таймером.............................. 21

5 Лабораторная работа №5. Управление видеоадаптером................... 24

Приложение А....................................................................................... 28

Приложение Б........................................................................................ 30

Приложение В....................................................................................... 34

Приложение Г........................................................................................ 36

Приложение Д....................................................................................... 38

Приложение Е........................................................................................ 40

Библиография........................................................................................ 44

 

 

 

 

 

Сводный план 2005 г., поз. 20

 

 

Наталья Валерьевна Сябина

 

 

 

 

 

 

 

 

 

 

 

СИСТЕМНОЕ ПРОГРАММИРОВАНИЕ

Часть 1

Методические указания к выполнению лабораторных работ

 (для студентов очной формы обучения специальностей 360140Автоматизация и информатизация в системах управления, 050702 – Автоматизация и управление)

 

 

 

 

 

 

 

Редактор             Сыздыкова Ж.М.

 

 

 

 

 

 

Подписано в печать __. __. __.                                     Формат 60х84  1/ 16

Тираж  75 экз.                                                      Бумага типографская №1

Объем ___ уч.-изд. л.                                              Заказ _____. Цена      тг.

 

 

 

 

Копировально-множительное бюро

 Алматинского института энергетики и связи

050013, Алматы, Байтурсынова, 126