На рис. 21.7 приведена исходная BASIC-программа и ассемблерная
подпрограмма. Обратите внимание на следующие особенности BASIC-программы:
оператор 10 очищает 32К байт памяти; операторы 20, 30, 40 и 50 временно
содержат комментарии.
Позже мы вставим BASIC-операторы для связи с ассемблерным модулем.
BASIC-программу можно сразу проверить. Введите команду BASIC и затем
наберите все пронумерованные операторы так, как они показаны в примере.
Для выполнения программы нажмите F2. Не забудьте сохранить текст программы
с помощью команды
SAVE "B:BASTEST.BAS"
Обратите внимание на следующие особенности ассемблерной подпрограммы:
- отсутствует определение стека, так как его обеспечивает BASIC;
программа не предусмотрена для отдельного выполнения и не может быть
выполнена;
- подпрограмма сохраняет в стеке содержимое регистра BP и
записывает значение регистра SP в BP;
- подпрограмма выполняет очистку экрана, хотя она может быть
изменена для выполнения других операций, таких как прокрутка экрана
вверх или вниз или установка курсора.
Все что осталось - это связать эти программы вместе. Следующие
действия предполагают, что системная дискета (DOS) находится на дисководе
A, а рабочие программы - на дисководе B:
1. Наберите ассемблерную подпрограмму, сохраните ее под именем
B:LINKBAS.ASM и оттранслируйте ее.
2. Используя компоновщик LINK, сгенерируйте объектный модуль, который
будет загружаться в старшие адреса памяти:
LINK B:LINKBAS,B:LINKBAS/HIGH,CON;
3. С помощью отладчика DEBUG загрузите BASIC - компилятор: DEBUG
BASIC.COM.
4. По команде отладчика R выведите на экран содержимое регистров.
Запишите значения в регистрах SS, CS и IP.
5. Теперь установите имя и загрузите скомпонованный ассемблерный модуль
следующими командами:
N B:LINKBAS.EXE
L
6. По команде R выведите на экран содержимое регистров и запишите
значения в CX, CS и IP.
7. Замените содержимое регистров SS, CS и IP значениями из шага 4. (Для
этого служат команды R SS, R CS и R IP).
8. Введите команду отладчика G (go) для передачи управления в BASIC. На
экране должен появиться запрос из BASIC-программы.
9. Для того, чтобы сохранить ассемблерный модуль, введите следующие
команды (без номеров операторов):
DEF SEG = &Hxxxx (значение в CS из шага 6)
BSAVE "B:CLRSCRN.MOD",0,&Hxx (значение в CX из шага 6)
Первая команда обеспечивает адрес загрузки модуля в память для
выполнения. Вторая команда идентифицирует имя модуля, относительную
точку входа и размер модуля. По второй команде система запишет модуль
на дисковод B.
10. Теперь необходимо модифицировать BASIC-программу для компоновки.
Можно загрузить ее сразу, находясь в отладчике, но вместо этого
наберите команду SYSTEM для выхода из BASIC и, затем, введите Q для
выхода из отладчика DEBUG. На экране должно появиться приглашение DOS
11. Введите команду BASIC, загрузите BASIC-программу и выведите ее на
экран:
BASIC
LOAD "B:BASTEST.BAS"
LIST
12. Измените операторы 20, 30, 40 и 50 следующим образом:
20 BLOAD "B:CLRSCRN.MOD"
30 DEF SEG = &Hxxxx (значение в CS из шага 6)
40 CLRSCRN = 0 (точка входа в подпрограмму)
50 CALL CLRSCRN (вызов подпрограммы)
13. Просмотрите, выполните и сохраните измененную BASIC-программу.
Если BASIC-программа и ассемблерные команды были введены правильно, а
также правильно установлены шестнадцатеричные значения из регистров, то
связанная программа должна сразу очистить экран и выдать запрос на ввод
времени и расценки. На рис.21.8 приведен протокол всех шагов - но
некоторые значения могут отличаться в зависимости от версии операционной
системы и размера памяти.
Приведенный пример выбран намеренно простым только для демонстрации
компоновки. Можно использовать более сложную технологию, используя
передачу параметров из BASIC-программы в ассемблерную подпрограмму с
помощью оператора
CALL подпрограмма (параметр-1,параметр-2,...)
Ассемблерная подпрограмма может получить доступ к этим параметрам,
используя регистр BP в виде [BP], как это делалось ранее на рис.21.3. В
этом случае необходимо определить операнд в команде RET, соответствующий
длине адресов параметров в стеке. Например, если оператор CALL передает
три параметра то возврат должен быть закодирован в виде RET 6.
__________________________________________________________________________
D>LINK
IBM Personal Computer Linker
Version 2.30 (C) Copyright IBM Corp. 1981, 1985
Object Modules [.OBJ]: LINKBAS
Run File [LINKBAS.EXE]: LINKBAS/HIGH
List File [NUL.MAP]: CON
Libraries [.LIB]:
Warning: no stack segment
Start Stop Length Name Class
00000H 00011H 00012H CODESG CODE
D>DEBUG BASIC.COM
-R
AX=0000 BX=0000 CX=0012 DX=0000 SP=FFFF BP=0000 SI=0000 DI=0000
DS=1410 ES=1410 SS=1410 CS=1410 IP=0100 NV UP EI PL NZ NA PO NC
1410:0100 E9E03E JMP 3FE3
-N D:LINKBAS.EXE
-L
-R
AX=FFA3 BX=0000 CX=0012 DX=0000 SP=0000 BP=0000 SI=0000 DI=0000
DS=1410 ES=1410 SS=9FE0 CS=9FE0 IP=0000 NV UP EI PL NZ NA PO NC
9FE0:0000 55
-R SS
SS 9FE0
:1410
-R CS
CS 9FE0
:1410
-R IP
IP 0000
:0100
-G
Ok
DEF SEG = &H9EF0
Ok
BSAVE "D:CLRSCREEN.MOD",0,&H12
Ok
SYSTEM
Program terminated normally
-Q
D>BASIC
IBM Personal Computer Basic
Ver4sion D3.10 Copyright IBM Corp. 1981, 1985
61310 Bytes free
Ok
LOAD"D:BASTEST.BAS
Ok
20 BLOAD "D:CLRSCREEN.MOD"
30 DEF SEG = &H9FE0
40 CLRSCRN = 0
50 CALL CLRSCRN
LIST
10 CLEAR ,32768!
20 BLOAD "D:CLRSCRN.MOD"
30 DEF SEG = &H9FE0
40 CLRSCRN = 0
50 CALL CLRSCRN
60 FOR N = 1 TO 5
70 INPUT "HOURS"; H
80 INPUT "rATE"; R
90 W = H * R
100 PRINT "WAGE = " W
110 NEXT N
120 END
Ok
__________________________________________________________________________
Рис.21.8. Этапы связи BASIC и ассемблера.
КОМПОНОВКА ПРОГРАММ НА ЯЗЫКЕ PASCAL И АССЕМБЛЕРЕ
________________________________________________________________
__________________________________________________________________________
program pascall ( input, output );
procedure move_cursor( const row: integer;
const col: integer ); extern;
var
temp_row: integer;
temp_col: integer;
begin
write( 'Enter cursor row: ' );
readln( temp_row );
write( 'Enter cursor column:' );
readln( temp_col );
move_cursor( temprow, temp_col );
write( 'New cursor location' );
end.
_____________________________________________________________________
TITLE MOVCUR Подпрограмма на ассемблере,
; вызываемая из программы на Паскале
PUBLIC MOVE_CURSOR
;----------------------------------------------------------
; MOVE_CURSOR: Устанавливает курсор
; по переданным параметрам
; Параметры: const row Строка и столбец
; const col для установки курсора
; Возвращаемое значение: Отсутствует
;----------------------------------------------------------
CODESEG SEGMENT PARA PUBLIC 'CODE'
MOVE_CURSOR PROC FAR
ASSUME CS:CODESEG
ROWWPAR EQU 8 ;Параметр "строка"
COLPAR EQU 6 ;Параметр "столбец"
PUSH BP ;Сохранить регистр BP
MOV BP,SP ;Установить BP на параметры
MOV SI,[BP+ROWPAR] ;SI указывает на строку
MOV DH,[SI] ;Поместить столбец в DL
MOV AH,02 ;Функция установки курсора
SUB BH,BH ;Страница #0
INT 10H
POP BP ;Вернуться
RET 4 ; в вызывающую программу
MOVE_CURSOR ENDP
CODESEG ENDS
END
__________________________________________________________________________
Рис.21.9. Компановка PASCAL-ассемблер.
В данном разделе показано, как можно установить связь между
программами на языке PASCAL фирм IBM и MicroSoft с программами на
ассемблере. На рис.21.9 приведен пример связи простой PASCAL-программы с
ассемблерной подпрограммой. PASCAL-программа скомпилирована для получения
OBJ-модуля, а ассемблерная программа оттранслирована также для получения
OBJ-модуля. Программа LINK затем компонует вместе эти два OBJ-модуля в
один выполнимый EXE-модуль.
В PASCAL-программе определены две переменные: temp_row и temp_col,
которые содержат введенные с клавиатуры значения строки и колонки
соответственно. Программа передает адреса переменных temp_row и temp_col в
виде параметров в ассемблерную подпрограмму для установки курсора по этим
координатам. PASCAL-программа определяет также имя ассемблерной
подпрограммы в операторе procedure как move_cursor и определяет два
параметра, как extern (внешние). Оператор в PASCAL-программе, который
вызывает ассемблерную программу по имени и передает параметры, имеет
следующий вид:
move_cursor (temp_row, temp_col);
Через стек передаются следующие величины: указатель блока вызывающей
программы, указатель на сегмент возврата, смещение возврата и адреса двух
передаваемых параметров. Ниже показаны смещения для каждого элемента в
стеке:
00 Указатель блока вызывающей программы
02 Указатель сегмента возврата
04 Указатель смещения возврата
06 Адрес второго параметра
08 Адрес первого параметра
Так как ассемблерная подпрограмма будет использовать регистр BP, то
его необходимо сохранить в стеке для последующего восстановления при
возврате в вызывающую PASCAL-программу. Заметьте, что этот шаг в
вызываемой подпрограмме аналогичен предыдущему примеру на рис.21.6.
Регистр SP обычно адресует элементы стека. Но так как этот регистр
нельзя использовать в качестве индексного регистра, то после сохранения
старого значения регистра BP необходимо переслать адрес из регистра SP в
BP. Этот шаг дает возможность использовать регистр BP в качестве
индексного регистра для доступа к элементам в стеке.
Следующий шаг - получить доступ к адресам двух параметров в стеке.
Первый переданный параметр (адрес строки) находится в стеке по смещению
08, и может быть адресован по BP+08. Второй переданный параметр (адрес
столбца) находится в стеке по смещению 06 и может быть адресован по BP+06.
Два адреса из стека должны быть переданы в один из индексных
регистров BX, DI или SI. В данном примере адрес строки пересылается из
[BP+08] в регистр SI, а затем содержимое из [SI] (значение строки)
пересылается в регистр DH.
Значение столбца пересылается аналогичным способом в регистр DL.
Затем подпрограмма использует значения строки и столбца в регистре DX при
вызове BIOS для установки курсора. При выходе подпрограмма восстанавливает
регистр BP. Команда RET имеет операнд, значение которого в два раза больше
числа параметров, в данном случае 2х2, или 4. Параметры автоматически
выводятся из стека и управление переходит в вызывающую программу.
Если в подпрограмме предстоит изменить сегментный регистр то
необходимо сохранить его значение командой PUSH на входе и восстановить