http://sulfurzona.com/
News
Service
Magazine
Software (Battle City Game, Wallpaper manager, Superpad, VG-NOW, Puzzle Game, Netler Internet Browser, ..)
Dune Game (Dune III, Dune IV, Cheats, Forum, ..)
Games free
Turbo Pascal (Assembler, Docs, Sources, Debbugers, ..)
Books (Docs for developers)
Forum
Guest book
Компьютерная диагностика двигателя автомобиля (адаптер К-линии)Компьютерная диагностика двигателя автомобиля (адаптер К-линии)
 
 
 
 
 

Паскаль для новичков (часть 41)

 

Системно-зависимые расширения

 
Паскаль для новичковЗа последние несколько лет я успел рассказать практически о всех базовых возможностях языка Паскаль, в частности среды разработки Turbo Pascal 6.0/7.0.
  
Теперь я с полной уверенностью могу сказать, что внимательные читатели моих статей уже выросли из ставших маленькими штанишек новичка, и доросли до ознакомления с теми средствами языка Паскаль, касающимися системно-зависимых возможностей языка, таких как работа с оперативной памятью, аппаратными портами, сервисами прерываний BIOS и MS-DOS, а так же использование драйверов различных устройств (Mouse driver, Himem driver), имитаторов устройств (Disney Sound Source, Univesa). Помимо этого пришла пора научиться создавать самим обработчики аппаратных прерываний, а так же драйверы и резидентные программы.
 

Доступ к памяти

 
Я неоднократно в своих примерах применял служебное слово absolute, позволяющее объявить переменную определённого типа в области памяти, уже отведённой под какую-то другую переменную. Таким образом, описываемая переменная будет совмещена с областью памяти другой переменной, и дополнительная память под неё не будет отводиться. В примере
 
var P : pointer;
      Value : longint absolute P;
      SystemTimer : longint absolute $40:$6C;
 
переменная Value типа Longint совмещается с областью памяти, отведённой под переменную P типа Pointer, что позволит интерпретировать значение указателя P как длинное целое число.
Аналогично переменная SystemTimer совмещается с адресом в памяти $40:$6C, то есть с 4-мя байтами области BIOS, где хранится текущее значение системного таймера, и позволит легко получать его значения из переменной SystemTimer. При указании физического адреса в данной конструкции адрес должен быть указан в виде сегмент:смещение и состоять из двух констант, разделённых двоеточием. Значения таких констант должны быть целого типа и лежать в пределах диапазона типа Word, то есть $0000..$FFFF.
  
Следует заметить, что таким же манером можно совместить переменную типа Word с областью памяти переменной типа Byte, и при этом компилятор никаких проверок на этот счёт делать не будет.
  
Turbo Pascal имеет ещё ряд средств для получения доступа к памяти с определённым физическим адресом. Например, имеется три предопределённых массива с идентификаторами Mem, MemW и MemL, где массив Mem предполагает обращение к памяти как к двухмерному массиву, состоящему из элементов типа Byte, массивы MemW и MemL соответственно к массивам из элементов типа Word и Longint.
Следующий пример демонстрирует применение массивов
 
var j : word;
begin
writeln( ‘SystemTime= ’, MemL[$40:$0C] );
for j:=0 to 19 do writeln( Mem[$0:j] );
end.
 
где первый оператор writeln выдаст на экран текущее значение системного таймера BIOS, область данных которого расположена по адресу $40:$0C, а оператор For-цикла выдаст на экран значения 20 байт, расположенных в самом начале оперативной памяти, а переменная j будет исполнять роль индекса. Подобным образом, можно не только читать из массивов, но и записывать в них необходимые данные, но это следует делать с глубоким знанием дела, так как не в каждую ячейку памяти можно писать всё, что вздумается. Например, можно писать в отведённый буфер памяти или в видеобуфер экрана дисплея. При указании физического адреса памяти для массива, можно использовать константы аналогично команде absolute, или применять стандартные функции Seg и Ofs (см. главу “Ссылочные типы. Динамические переменные. Указатели МК №39/262/ за 29.09.2003), а так же переменные для индексирования элементов массива, главное, чтобы обе части адреса имели значение в пределах, допустимых для типа Word.
  
Средство обращения к оперативной памяти посредством предопределённых массивов позволяет эффективно работать с системной информацией и доступными областями памяти без выделения дополнительной памяти под глобальные или локальные переменные для адресации, например, через ссылочные типы.
 

Доступ к портам

 
Разработчики языка Turbo Pascal, создавая мощную среду разработки, постарались обеспечить программистов средствами работы с портами ввода-вывода для взаимодействия с внутренними контроллерами гибких и жёстких дисков, прерываний, прямого доступа к памяти (ПДП), таймера, клавиатуры, дисплея, последовательного интерфейса RS-232 и периферийными устройствами. Этими средствами являются предопределённые одномерные массивы Port и PortW, поскольку порты бывают одно- и двухбайтные. Команда Port[W] выполняет обращение к порту с указанным адресом. Значение адреса должно быть в пределах, допустимых для типа Word. Если элемент массива Port стоит в левой части присваивания, то выполняется запись в порт. Если элемент массива указан в качестве операнда в выражении или как параметр подпрограммы, то выполняется чтение значения из порта.
  
Следует помнить, что предопределённые массивы не могут быть указаны в качестве параметров-переменных.
 
LastKey := Port[$60];
 

Прерывания

 
Прерывание, а вернее обработчик прерывания – это ни что иное, как обыкновенная процедура с тем лишь отличием, что она может быть вызвана либо машинной командой без указания адреса (для программных прерываний), но с номером прерывания, либо быть вызвана аппаратно по запросу контроллера прерываний компьютера (для аппаратных прерываний).
  
Прерывания бывают двух видов: внутренние и внешние.
К внутренним прерываниям относятся: прерывание при делении на ноль, прерывание трассировки (по флагу трассировки), которые инициируются самим процессором внутри себя, и программные прерывания.
  
Программные прерывания предназначены для упрощения вызова служебных функций систем MS-DOS и BIOS, так как для вызова их функций программе вовсе не нужно знать точный адрес расположения кода этих функций, а достаточно лишь знать номер сервисного программного прерывания.
  
К внешним прерываниям относятся аппаратные прерывания, которые могут быть маскируемыми (запрещаемыми) или не маскируемыми (незапрещаемыми).
  
Аппаратные (внешние) прерывания предназначены для обслуживания событий, происходящих в работе устройств компьютера, таких как таймер, клавиатура, жёсткий диск, математический процессор, COM-порт и другие. Для более подробного ознакомления смотрите список литературы ниже.
 

Прерывания и системные вызовы

 
Как любая системно зависимая среда разработки, Turbo Pascal взаимодействует с оборудованием через системные вызовы операционной системы MS-DOS, а именно через прерывание $21. Поэтому при каждом обращении, например, к файлу, команды работы с файлами компилируются в системные вызовы соответствующих функций операционной системы. Довольно часто в практике программиста возникает необходимость использовать многочисленные функции MS-DOS и BIOS, аналогов которым в Паскале нет. Именно для таких целей в модуле Dos среды Turbo Pascal предусмотрена стандартная процедура
 
 
 
 
 
Intr( IntNo : byte; var Regs : Registers );
 
позволяющая вызвать программное прерывание с номером IntNo, и передать обработчику вызванного прерывания параметры в виде структуры типа Registers, объявленной в модуле Dos так:
 
type Registers = record
         Case Integer of
           0: ( AX, BX, CX, DX, BP, SI, DI, DS, ES, Flags : word );
           1: ( AL, AH, BL, BH, CL, CH, DL, DH : byte );
        end;
 
Структура позволяет инициализировать поля, ассоциируемые с соответствующими регистрами, при этом регистры AX, BX, CX, DX доступны как целые слова, так и старшими и младшими байтными частями.
  
При выполнении процедуры Intr регистры процессора инициализируются значениями одноимённых полей указанной структуры Regs. После этого процедура вызывает программное прерывание с указанным номером. После выполнения обработчика вызванного прерывания, значения регистров процессора загружаются в одноимённые поля указанной структуры Regs, что позволяет получить результат выполнения вызванного прерывания.
  
Пользоваться данной стандартной процедурой следует в соответствии с руководством по MS-DOS и BIOS. При отсутствии такового можно воспользоваться диалоговой справочной системой Norton Guide. Следующие две процедуры служат для очистки буфера клавиатуры, но выполнены одна на Паскале, а другая во встроенном ассемблере соответственно. Таким образом, у программиста есть выбор инструментария, который придётся ему по душе.
 
procedure ClearKeyboardBuffer; assembler;
asm
 mov ah,0Ch; mov al,0; int 21h
end;
 
или
 
procedure ClearKeyboardBuffer;
var Regs : Registers;
begin
Regs.AH := $0C;
Regs.AL := 0;
Intr( $21, Regs );
end;
 
Вот так просто можно использовать системные сервисы с номерами прерываний $10, $12, $15, $16, $21, $2F, $33,…
  
Поскольку один из часто используемых сервисов – это прерывание MS-DOS под номером $21, то в модуле Dos предусмотрена стандартная процедура
 
MsDos( var Regs : Registers );
 
для вызова этого прерывания. Тогда уже известная вам процедура может выглядеть иначе
 
procedure ClearKeyboardBuffer;
var Regs : Registers;
begin
Regs.AH := $0C;
Regs.AL := 0;
MsDos( Regs );
end;
 
Стандартная процедура MsDos после выполнения прерывания заносит значения регистров процессора в структуру Regs.
 

Переопределение прерываний

 
Задачи, которые возникают перед программистом, бывают довольно разнообразные. Иногда приходится разрабатывать собственные сервисы, которые потом можно использовать посредством вызова определённого прерывания, выделенного этому сервису. Бывает, что возникает необходимость перехвата (переопределения) некоторых аппаратных или программных прерываний.
  
Именно для таких целей разработчики среды Turbo Pascal ввели возможность компиляции процедур специального вида. Такая процедура в заголовке должна иметь стандартный перечень параметров:
 
procedure IntProc( Flags, CS, IP, AX, BX, CX, DX, SI, DI, DS, ES, BP : word ); interrupt;
begin
end;
 
Допускается, при необходимости, указывать лишь некоторые параметры из данного списка, но при этом следует сохранять строгую последовательность, начиная с конца списка. То есть, если необходимо объявить процедуру обработчика прерывания и при этом параметрами должны служить лишь регистры процессора AX, DI и ES, то заголовок процедуры может быть таким:
 
procedure ClibBoardService( RegAX, UnuseBX, UnuseCX, UnuseDX, UnuseSI, RegDI, UnuseDS, RegES, UnuseBP : word ); interrupt;
 
из чего следует, что начало списка можно опустить за ненадобностью, а те параметры, которые обработчику не нужны, следует перечислить, как положено. При этом все параметры могут иметь произвольные имена, главное, чтобы их порядок был строго соблюдён.
  
Такой механизм задания параметров позволяет обработчику получать данные из вызывающей программы, выполнить некоторые действия по их обработке и результат вернуть в эти же параметры, как предусмотрено интерфейсом разрабатываемого сервиса. Внутри блока процедуры значения этих параметров можно изменять так, как если бы это были локальные переменные. По сути - это и есть локальные переменные, в которые, грубо говоря, перед началом выполнения блока процедуры обработчика прерывания, заносятся значения соответствующих регистров процессора.
  
На самом деле при выполнении аппаратного или программного вызова обработчика прерывания процессор в стек заносит регистр флагов Flags, регистровую пару CS:IP, содержащую адрес машинной команды, следующей за командой вызова. После чего пара регистров CS:IP загружается значением адреса процедуры обработчика прерывания, и управление передаётся блоку процедуры. Неявно блок процедуры сохраняет все регистры процессора в стек. Именно эта область стека и интерпретируется как местоположение выше описанных локальных переменных.
  
Далее выделяется область стека под локальные переменные, объявленные в var-объявлении блока процедуры, при условии, что такое объявление имеет место быть. Следует понимать, что стек, который будет использован в процедуре обработчика – это ни что иное, как стек прерванной программы, и ещё не известно, достаточен ли он по объёму для нужд процедуры обработчика. Поэтому в таком щекотливом деле с локальными данными не разгонишься, и следует быть экономным или вообще, размещать данные в сегменте данных.
  
Затем выполняется инициализация регистра сегмента данных DS значением истинного сегмента данных программы, которой принадлежит процедура обработки прерывания. Это позволяет совершенно спокойно манипулировать данными в собственном сегменте данных, не разрушая данные в сегменте данных прерванной программы.
  
После всех этих приготовлений выполняется явная часть блока процедуры.
 
Продолжение следует…
 
© Владислав Демьянишин
 

Литература

1. Р. Джордейн. Справочник программиста персональных компьютеров типа IBM PC, XT и AT. – М.: Финансы и статистика, 1992. – 543 с.
2. Диалоговая справочная система Norton Guide.
3. Пpогpаммно-технические сpедства пеpсональных ЭВМ семейства IBM PC (описание портов)
4. Дмитрий Меламуд "TSR и нерезидентные обработчики прерываний" + Шеховцов Александр "Уменьшение размера резидентных программ, написанных на Turbo-Pascal 6.0" + Демьянишин Владислав "Примеры резидентных программ на Turbo Pascal".
 
На нашем сайте можно не только бесплатно скачать игры, но и документацию и книги по программированию на MIDLetPascal, Turbo Pascal 6, Turbo Pascal 7, Borland Pascal, по программированию устройств Sound Blaster, Adlib, VESA BIOS, справочник Norton Guide и много другой полезной информации для программистов, включая примеры решения реальных задач по созданию резидентных программ. Предлагаю также посетить Марья искусница - сайт о рукоделии (http://mariya-iskusnica.ru).
 

Журнал > Программирование > Паскаль для новичков (Turbo Pascal, Assembler) > Паскаль для новичков (часть 41): Системно-зависимые расширения
 
 
 
9
 
ВКонтакте
Facebook
 
 
 
На главную страницу На предыдущую страницу На начало страницы
 
 
Украина онлайн Рейтинг@Mail.ru Рейтинг Сайтов YandeG