В предыдущих примерах рассматривалась программа на языке ассемблера, используемая совместно с интерпретатором Бейсика. Версия языка Бейсик, входящая в поставку 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 1/1/80 04:03:56
Фиг. 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
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. Делается это для того, чтобы редактор связей мог найти подпрограмму и правильно связать ее с программой на Фортране. Для интерпретатора Бейсика такой необходимости не было, поскольку программа на Бейсике не редактировалась совместно с программой на языке ассемблера.