В предыдущих примерах рассматривалась программа на языке
ассемблера, используемая совместно с интерпретатором Бейсика.
Версия языка Бейсик, входящая в поставку IBM PC, является
интерпретируемым языком. Это означает, что программа хранится в
ЭВМ в виде, очень похожем на исходный текст. Интерпретатор не
преобразует операторы языка Бейсик в команды машинного языка.
Интерпретатор Бейсика во время выполнения просматривает каждый
оператор программы и делает все, что необходимо для выполнения
этого оператора.
По-другому работает компилятор. Он преобразует операторы языка
высокого уровня в команды машинного языка. Фирма IBM предлагает
компиляторы для персональной ЭВМ с языков Бейсик, Паскаль, Фортран
и Кобол. Выходом компилятора является программа на машинном языке
(файл *.OBJ), т.е. он во многом аналогичен выходу ассемблера.
Запуск программы, написанной на компилируемом языке высокого уровня
состоит из двух этапов. Сначала программа должна быть
скомпилирована, и должны быть отредактированы связи. Затем она
может быть выполнена. Интерпретируемая программа может выполняться
непосредственно, минуя этап компиляции.
Компилируемые языки на персональной ЭВМ аналогичны языку Бейсик
в том смысле, что не дают возможности делать с техническим
обеспечением все, что вздумается. На самом деле интерпретатор
Бейсика еще позволяет программисту при помощи операторов программы
считывать и записывать информацию с портов ввода-вывода и ячеек
памяти. Другие языки не всегда предоставляют даже эту возможность.
Поэтому применение подпрограмм на языке ассемблера в программе на
Паскале или Фортране может оказаться даже более необходимым.
Возможно, вам придется заняться этим, если вы захотите
воспользоваться всеми возможностями технического обеспечения.
К счастью, включить процедуру на языке ассемблера в программу
на компилируемом языке высокого уровня довольно просто, так как
выходом компилятора является объектный файл, готовый к
редактированию связей. Выход ассемблера - тоже объектный файл.
Следовательно, достаточно лишь связать программу на языке высокого
уровня и программу на языке ассемблера при помощи редактора связей
DOS. Нет необходимости соединять программы в процессе выполнения,
как это делалось для интерпретатора Бейсика.
Построим пример на языке Фортран (Фиг. 10.11). Для языка
Паскаль все очень похоже. Подобный пример приведен в приложении D
справочника к компилятору Фортрана. В примере головная программа,
написанная на Фортране, объединена с программой на языке
ассемблера, которая считывает текущее время, используя программное
прерывание базовой системы ввода-вывода. Подпрограмма на языке
ассемблера обращается к BIOS для определения текущего времени и
возвращает соответствующее значение в программу на Фортране.
Головная программа преобразует кванты таймера, в текущее время,
выраженное в часах, минутах и секундах.
На Фиг. 10.11 представлена головная программа на Фортране.
Эта программа вызывает внешнюю процедуру TIMER, имеющую один
параметр A - четырехбайтовое целое значение. Возрващаемое
процедурой TIMER значение представляет собой текущее время,
выраженное в квантах таймера и отсчитываемое от полуночи.
Программа на Фортране по полученному из процедуры TIMER значению
вычисляет время в часах(HOURS), минутах(MINS), секундах(SECS) и
сотых долях секунды(HSECS). Отметим, насколько проще реализовать
умножение и деление на языке Фортран, чем на языке ассемблера.
Можно убедиться, что выполнение всех подобных операций на Фортране
существенно упрощает программирование. Чрезвычайно удобен и способ
преобразования целых переменных в выдаваемые на печать символы при
помощи операторов Фортрана WRITE и FORMAT. На языке ассемблера для
выполнения тех же самых действий потребовалось бы несколько сот
строк. Вспомним пример для сопроцессора 8087, где программа
преобразовывала число с плавающей точкой в код ASCII. В этой
программе содержалось значительное число команд, и, кроме того,
использовался сопроцессор 8087.
$STORAGE=4
INTEGER A,HOURS,MINS,SECS,HSECS
CALL TIMER(A)
HOURS=A/65543
A=A-HOURS*65543
MINS=A/1092
A=A-MINS*1092
SECS=A/18
HSECS=(100*(A-SECS*18))/18
WRITE(*,10)HOURS,MINS,SECS,HSECS
10 FORMAT(1X,'THE TIME IS: ',I2,':',I2,':',I2,'.',I2)
END
Фиг. 10.11 Программа определения времени дня на Фортране
Microsoft (R) Macro Assembler Version 5.00 4/2/89 16:07:35
Фиг. 10.12 Подпрограмма для программы на ФОРТРАНе Page 1-1
PAGE ,132
TITLE Фиг. 10.12 Подпрограмма для программы на ФОРТРАНе
FRAME STRUC
0000 ???? SAVEBP DW ?
0002 ???????? SAVERET DD ?
0006 ???????? A DD ? ; Указатель на параметр
000A FRAME ENDS
0000 CODE SEGMENT 'CODE'
DGROUP GROUP DATA
ASSUME CS:CODE,DS:DGROUP,ES:DGROUP,SS:DGROUP
0000 TIMER PROC FAR
PUBLIC TIMER ; Указание программе LINK на расположение
; программы TIMER
0000 55 PUSH BP
0001 8B EC MOV BP,SP ; Загрузка адреса стека
0003 B4 00 MOV AH,0
0005 CD 1A INT 1Ah ; Вызов BIOS для получения даты и времени
0007 C4 5E 06 LES BX,[BP].A ; Загрузка адреса поля параметров
000A 26: 89 17 MOV ES:[BX],DX ; Сохранение младшей части времени
000D 26: 89 4F 02 MOV ES:[BX+2],CX ; Сохранение старшей части времени
0011 5D POP BP
0012 CA 0004 RET 4 ; Возврат с удалением параметров из стека
0015 TIMER ENDP
0015 CODE ENDS
END
Фиг. 10.12 Ассемблерная процедура для программы на Фортране
На Фиг. 10.12 представлена подпрограмма на языке ассемблера -
процедура TIMER. В этой несложной программе для считывания
текущего времени и сохранения полученного значения в двойном слове
используется обращение к BIOS. Здесь нам необходимо рассмотреть
способ передачи параметров из программы на Фортране в подпрограмму
на языке ассемблера.
На Фиг.10.13 показано содержимое стека в начальный момент
выполнения подпрограммы на языке ассемблера. Точно так же, как
интерпретатор Бейсика, программа на Фортране помещает адрес
параметра в стек. Однако компиляторы Фортрана и Паскаля передают
указатель длиной в два слова, а не одно только смещение параметра.
Это означает, что программа на языке ассемблера, прежде чем
получить доступ к параметру, должна установить как сегментный
регистр, так и адрес смещения. Если бы параметров было более
одного, то программа на Фортране перед вызовом поместила бы в стек
значения адресов и остальных параметров.
ГДДДДДДДДДДДДґ
SPДДДД>і Смещение і
і возврата і
ГДДДДДДДДДДДДґ
і Сегмент і
і возврата і
ГДДДДДДДДДДДДґ
і Смещение і
і аргумента і
ГДДДДДДДДДДДДґ
і Сегмент і
і аргумента і
ГДДДДДДДДДДДДґ
Фиг. 10.13 Стек для вызова процедуры в Фортране
Подпрограмма TIMER на Фиг. 10.12 адресует стек, помещая в него
регистр BP и устанавливая его на вершину стека.Структура FRAME
помогает идентифицировать разные значения в стеке после того как
программа сохранит в нем значение BP. Команда LES BX,[BP]+A
помещает адрес параметра в пару регистров ES:BX. Используя этот
адрес, программа помещает четырехбайтовое значение текущего времени
в четырехбайтовую целую переменную.
Заметим, что процедура TIMER извлекает адрес параметра из стека
при выполнении команды возврата точно так же, как это делалось в
программах на языке Бейсик. Заметим также, что в этой ассемблерной
программе для идентификации имени TIMER используется оператор
PUBLIC. Делается это для того, чтобы редактор связей мог найти
подпрограмму и правильно связать ее с программой на Фортране. Для
интерпретатора Бейсика такой необходимости не было, поскольку
программа на Бейсике не редактировалась совместно с программой на
языке ассемблера.