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)
Guest book
Компьютерная диагностика двигателя автомобиля (адаптер К-линии)Компьютерная диагностика двигателя автомобиля (адаптер К-линии)
 
 
 
 

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

 

Программирование на низком уровне. Встроенный ассемблер

 
Автор: Владислав Демьянишин
 
Паскаль для новичковСтремление программистов Borland сделать Turbo Pascal универсальным привело к тому, что в Turbo Pascal фактически был интегрирован компилятор языка ассемблера.
  
За те три года, что выходит данный цикл статей, я успел вам прожужжать все уши о том, что Turbo Pascal позволяет создавать высокоэффективные программы, написанные на языке высокого уровня, то есть на Паскале.
  
Язык программирования высокого уровня – язык, представляющий собой словесное описание логических взаимосвязей операций программы, обособляющийся от конкретной реализации кода программы касаемо процессора, оборудования и операционной системы.
  
Язык среднего уровня – язык, представляющий собой последовательность вызовов системных (BIOS и DOS) сервисов дисплея, клавиатуры, мыши, и других устройств компьютера на машинном языке, но при этом ни какой работы со вспомогательными микросхемами устройств напрямую не ведётся.
  
Язык низкого уровня – язык, состоящий из машинных команд, работа которых направлена на непосредственное управление устройствами компьютера через программирование микросхем этих устройств.
  
Чтобы грамотно писать на языке низкого уровня, необходимо знать номера портов устройств компьютера, значения, которые могут быть посланы в них и последовательность выполнения операций с этими портами. Понятно, что в ходе эволюции PC от XT до AT работа некоторых портов могла быть пересмотрена. Чтобы старое программное обеспечение могло успешно работать и на новых машинах, и к тому же чтобы программистам облегчить жизнь в BIOS компьютера были встроены такие сервисы, посредством вызова которых можно было управлять компьютером не вдаваясь в подробности работы непосредственно с портами оборудования. Таким образом, удалось повысить уровень программирования с низкого до среднего.
  
Программирование на низком и среднем уровне позволяет создавать высокопроизводительный код для работы с графикой и памятью.
 

Asm-оператор

 
Встроенный ассемблер Turbo Pascal совместим с компиляторами Borland Turbo Assembler (TASM), Microsoft Assembler (MASM) и Turbo EditAsm, и поддерживает машинные команды процессоров INTEL 8086/8087 и 80286/80287. Компилятор сможет воспринимать ассемблерные инструкции процессора i8087 только при включённой директиве {$N+}, инструкции процессора i80286 только при директиве {$G+}, а инструкции i80287 соответственно при директивах {$N+, G+}. Однако, не поддерживает следующие стандартные ассемблерные директивы EQU, PROC, STRUC, SEGMENT, MACRO поскольку в Turbo Pascal уже имеется возможность объявлять константы, переменные и подпрограммы.
  
Конечно, данный цикл статей посвящён известно чему, поэтому на этом лирическое отступление объявляю завершённым.
  
Итак, операторы ассемблера могут быть использованы только в пределах специального asm-оператора Asm..End. Данный оператор может применяться везде, где допустимо вхождение составного оператора Begin..End. При этом команды ассемблера могут отделяться не только символом ‘;’ (точка с запятой), но и служебным символом перехода на новую строку или комментарием в виде фигурных скобок {..}. Примеры:
 
asm
 mov ax, 1; int 33h
end;
 
или
 
asm
 mov ax, 1 {заносит 1 в регистр AX процессора}
 int 33h
end;
 

Использование регистров

 
В asm-операторе могут быть доступны практически все регистры центрального процессора 8086. Допускается изменение значений регистров AX, BX, CX, DX, DI, SI, ES, Flags. Поскольку для системных нужд Turbo Pascal использует некоторые регистры, то не следует изменять значения регистров DS, SS, SP и BP.
 

Asm-метки

 
Метки, содержащиеся в asm-операторе, должны быть объявлены посредством стандартного описания, как и обычные метки в Паскале. Описание позволит осуществлять переход по такой метке в пределах asm-оператора и за его пределы, например из одного asm-оператора в другой asm-оператор, или из объемлющего блока во внутрь asm-оператора.
  
При этом, если метка используется только внутри asm-оператора и в объемлющем блоке нету перехода по ней во внутрь asm-оператора, то такая метка может начинаться с символа ‘@’ и объявлять её в описании Label нет необходимости. Такие метки можно назвать внутренними, а их имена не должны совпадать в пределах одного asm-оператора, но одноимённые метки могут присутствовать в другом asm-операторе. Пример использования объявляемых меток
 
Label Mouse_seek, Mouse_found, Mouse_not_found;
 
begin
Goto Mouse_seek;
asm
Mouse_seek:
 mov ax, 0
 int 33h
 cmp ax, 0
 jz Mouse_not_found
 jmp Mouse_found
end;
Mouse_not_found:
   writeln('Mouse not found');
   Halt;
 
Mouse_found:
   writeln('Mouse found');
   …
end.
 
и применение внутренних меток
 
function PMode : boolean; assembler;
asm
                 smsw ax
                 test al, 1
                 jnz @Pmode
                 mov al, 0
                 jmp @end
@PMode: mov al, 1
     @end:
end;
 

Asm-префиксы

 
Asm-оператор может содержать префикс Lock, безусловный и условный Rep, сегментные префиксы SEGCS, SEGSS, SEGDS и SEGES. При этом, префикс может быть указан перед ассемблерной инструкцией на одной строке с нею, либо на отдельной строке, распространяя своё действие на последующую ассемблерную инструкцию.
 

Asm-переменные

 
Встроенный ассемблер для объявления данных поддерживает директивы DB, DW, DD. Вопреки документации Turbo Pascal, которая опровергает возможность объявления, таким образом, переменных и утверждает, что переменные следует объявлять при помощи описания var, хочу сказать, что такими директивами можно объявлять именованные переменные. При этом, это будут не совсем переменные, а лишь область памяти, в которой могут храниться данные формата byte, word(integer), longint соответственно. Никаких проверок на счёт выхода значений за допустимые пределы для таких переменных не осуществляется. Таким образом, благодаря следующему коду:
 
asm
                jmp @Start
 
 @byte1: DB 5
@word1: DW 10000
@word2: DW 20000
 
   @Start: mov AL, byte ptr @byte1
                mov BX, word ptr @word1
                mov CL, byte ptr @word2
end;
 
будет отведена (зарезервирована) память в один байт и два слова, помеченная соответственно метками @byte1, @word1 и @word2 с соответствующими предустановленными значениями 5, 10000 и 20000. Очень важно осознавать, что ни в коем случае нельзя допускать прохода выполнения машинного кода через область памяти таких переменных, так как это может привести к непредсказуемым последствиям. Хотя чего тут предсказывать? Тут я и без кофейной гущи и карт tarot ;O) могу предсказать зависание вашего компьютера. Подобные вещи можно осуществлять только в специальных случаях, когда, например, программа должна сама себя модифицировать. Либо когда необходимо вставить код 32-разнядных команд процессора i80386 и выше, который не может быть сформирован компилятором Turbo Pascal.
  
Так как в данном примере ни ту, ни другую цель мы не преследуем, то перед объявлением переменных указываем инструкцию безусловного перехода jmp на метку @Start, то есть, перепрыгиваем через область данных. Далее идут команды mov, первая из которых получает значение переменной @byte1 в младшую половину AL регистра AX, вторая команда обеспечит получение значения переменной @word1 в регистр BX, и третья приведёт к тому, что в младшую половину CL регистра CX будет получен младший байт переменной @word2. Вполне допустимо обращение к любой из таких переменных, как области памяти любого размера, с тем чтобы обработать данные как это необходимо. При таких операциях осторожность должна быть помножена на осторожность.
 
 
 
 
  
Между тем, таким же образом можно размещать в памяти целые списки переменных, указывая любую из трёх директив и перечисляя значения через запятую, например:
 
DB 0, 1111b, 10100000b
DW 1, $10, 50h
DD 2, 5, 1
 

Приоритет зарезервированных слов в asm-операндах

 
Встроенный ассемблер предполагает, что зарезервированы следующие слова: AH, AL, AND, AX, BH, BL, BP, BX, BYTE, CH, CL, CS, CX, DH, DI, DL, DS, DWORD, DX, ES, FAR, HIGH, LOW, MOD, NEAR, NOT, OFFSET, OR, PTR, QWORD, SEG, SHL, SHR, SI, SP, SS, ST, TBYTE, TYPE, WORD, XOR. При формировании инструкций ассемблера следует учесть, что зарезервированные слова всегда имеют приоритет над идентификаторами констант и переменных. Так следующий пример записывает значение 10 не в переменную Bx, а в регистр BX процессора, затем его значение записывается в регистр AX. Чтобы обратиться не к регистру, а к переменной с совпадающим именем следует предварить её имя символом ‘&’ (амперсант, или коммерческое ‘И’), и тогда, инструкция mov &bx,5 запишет значение 5 в переменную Bx.
 
var bx : word;
 
begin
asm
 mov bx, 10
 mov ax, bx
 mov &bx, 5
end;
end.
 

Asm-выражения

 
Ассемблерные инструкции могут содержать выражения либо для вычисления непосредственных значений операнда либо для вычисления адреса, где хранится значение операнда. Первые выражения должны быть константными, то есть такими, чтобы при компиляции можно было вычислить их значение. Чтобы это стало возможным, следует в выражении указывать непосредственные значения и/или константы. В следующем примере в переменную C заносится значение выражения A+B:
 
const
        A = 50;
        B = 100;
var   C : word;
asm
 mov C, A+B
end;
 
В случае попытки использовать в подобном выражении вместо констант переменные, компилятор выдаст сообщение “Error 157: Cannot add or subtract relocatable symbols.” (невозможно сложить или вычесть переменные значения). Всё дело в том, что в ассемблере почти все инструкции работают не более чем с двумя операндами, и если первый операнд – это переменная, то вторым операндом может быть только регистр процессора, непосредственное значение или константа. Если первый операнд – это регистр процессора, то вторым операндом может быть непосредственное значение, константа, другой регистр или этот же, или переменная. Таким образом, невозможно сформировать машинную команду, где могли бы фигурировать сразу две или три переменные. Современные компиляторы ассемблера в результате компиляции программы, конечно, позволяют развернуть подобное выражение в несколько машинных команд, но компилятор Turbo Pascal такими средствами не обладает. Поэтому для переменных пришлось бы ручками писать следующий код:
 
var A, B, C : word;
asm
 mov AX, A
 add AX, B
 mov C, AX
end;
 
Следует отметить, что любое упоминание имени переменной в инструкции ассемблера подразумевает не значение оной как в выражениях Паскаля, а её адрес памяти, откуда и будет взято её значение для выполнения инструкции. Поэтому выражение mov BX, A+2 приведёт не к сложению значения переменной A c двойкой, как предполагается, а к сложению адреса (смещения) переменной A с двойкой, и загрузке значения переменной B в регистр BX, поскольку переменная B расположена в сегменте данных (судя по var-объявлению) сразу за переменной A. Чтобы в регистр BX загрузить значение переменной A, увеличенное на два, необходимо указать следующие инструкции:
 
asm
 mov BX, A
 add BX, 2
end;
 
Вот ещё примеры выражений вычисления адреса для косвенной адресации:
 
asm
    mov ax, [bp – 04]
    mov bx, [bp + di]
    mov dx, [bx + si]
end;
 
где в первом случае от смещения в регистре BP вычитается четыре, по полученному адресу извлекается значение и заносится в регистр AX. Во втором случае к смещению в BP прибавляется смещение в регистре DI, по полученному адресу извлекается значение и заносится в регистр BX. Третий случай действует аналогично применительно к задействованным регистрам.
 

Команды переходов

 
При использовании ассемблерной команды безусловного перехода JMP компилятор самостоятельно оптимизирует код машинной инструкции и в случае, если метка находится внутри текущего сегмента кода, то есть удалена от команды JMP не более, чем на 65535 байт, то формируется команда ближнего (внутрисегментного) перехода JMP NEAR (размер 3 байта). Если метка расположена в другом сегменте кода, то формируется машинная команда дальнего (межсегментного) перехода JMP FAR (размер 5 байт). Имеется возможность явного указания разновидности безусловного перехода при помощи комбинации зарезервированных слов соответственно NEAR PTR и FAR PTR.
 
asm
            jmp far ptr @Metka {дальний переход}
            …
            jmp near ptr @Metka {ближний переход}
            …
            jmp @Metka {ближний переход}
            …
@Metka:
end;
 
В результате компиляции примера будет сформирована одна команда дальнего перехода и две команды ближнего перехода.
  
Следует отметить, что в случае перехода внутри сегмента, когда цель перехода – это метка, находящаяся в пределах 127 байт от места расположения текущей команды перехода, компилятору уместно было бы сформировать короткую команду перехода JMP SHORT (размер 2 байта). Но он этого не делает и явно указать компилятору на это нет возможности, поэтому в таких случаях всегда формируется команда ближнего перехода.
  
Аналогичным образом компилятор поступает и с командами условных переходов. Так получается, что в результате написания ассемблерных подпрограмм большинство меток находятся в пределах 127 байт и было бы рациональнее процессору переходить по ним выполняя короткую условную (в два байта) команду Jxx SHORT. Но не смотря на это компилятор формирует ближний условный переход Jxx NEAR.
 
asm
    cmp ax,10
    jb @Metka
    …
    cmp bx,50
    ja @Metka
    …
    sub ax,25
    jz @Metka
    …
@Metka:
end;
 
В примере, первая комбинация команд выполнит переход по метке в случае, если значение регистра AX будет меньше десяти. Вторая комбинация отправит процессор по метке, если значение регистра BX окажется больше пятидесяти. Третья комбинация осуществит переход по метке в случае равенства регистра AX двадцати пяти.
  
Применительно к условным переходам возможность явного указания типа перехода, например, Jxx NEAR PTR и Jxx FAR PTR отсутствует.
 

P.S.

 
Я стремился, чтобы после прочтения данной статьи стало ясно, как использовать ассемблер в языке Turbo Pascal. Несмотря на это, статья не рассказывает о самом ассемблере так, как это нужно для полноценной работы. Для этого необходимо рассказать об архитектуре процессоров семейства INTEL 80x86, и о теоретической стороне программирования на ассемблере. Необходимый материал достаточно хорошо освещён в печатной книге [1]. Если не удастся найти последнюю, то её может заменить электронный вариант [4], который есть на выше указанном сайте. В качестве хорошего советчика по работе на среднем и низком уровне могу посоветовать книгу [2]. Хотя печатный вариант читать приятнее, за неимением оного, можно довольствоваться его электронным вариантом, который есть на сайте. В качестве маленького довеска ко всему этому справочник [3].
 
Продолжение следует…
 
© Владислав Демьянишин
 

Литература

1. Б.Э.Смит, М.Т.Джонсон. Архитектура и программирование микропроцессора INTEL 80386 – М.:ТОО”Конкорд”, 1992. – 334 с.
2. Р. Джордейн. Справочник программиста персональных компьютеров типа IBM PC, XT и AT. – М.: Финансы и статистика, 1992. – 543 с.
3. Диалоговая справочная система Norton Guide.
4. Электронный вариант книги “32-х разрядный микропроцессор INTEL 80386”.   
5. Зубков «Assembler. Язык неограниченных возможностей». Ассемблер для DOS, Windows и Unix
 
 
Вы находитесь на официальном сайте Владислава Демьянишина - разработчика игры Dune IV (Dune 4). На нашем сайте Вы можете бесплатно скачать игры Dune IV (Dune 4), Battle City (Танчики с Dendy/Nintendo), читы к играм и многое другое. Также Вы можете скачать бесплатно программы и полезные утилиты. Все программы чистые, т.е. не содержат вирусов и иного вредоносного ПО.
 
Среди доступных программ есть мобильная читалка книг, менеджер переноса файлов с фото- и видеокамер на компьютер, текстовый редактор, WYSIWYG редактор, 3D аниматор, GIF аниматор, AVI аниматор, пакетный конвертор изображений, редактор электрических схем, программа для скриншотов, диспетчер тем рабочего стола и другие.
 
На нашем сайте можно не только бесплатно скачать игры, но и документацию и книги по программированию на MIDLetPascal, Turbo Pascal 6, Turbo Pascal 7, Borland Pascal, по программированию устройств Sound Blaster, Adlib, VESA BIOS, справочник Norton Guide и много другой полезной информации для программистов, включая примеры решения реальных задач по созданию резидентных программ. Предлагаю также посетить Марья искусница - сайт о рукоделии (http://mariya-iskusnica.ru).
 
 

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