Лекция: Объектный код компилятора C0
Объектный код, т. е. результат трансляции С0-программы, представляет собой программу на языке ассемблера IBM PC, состоящую из сегмента кода (команд), сегмента данных и сегмента стека. Сегмент данных содержит области для хранения значений глобальных переменных С0-программы, создаваемые псевдокомандами вида:
_имя DW ?
Локальные переменные и параметры каждой функции С0-программы хранятся в сегменте стека в виде кадра, структура которого показана на рис. 1. Адрес кадра находится в регистре ВР. На рис. 2 показан кадр стека функции kod.
В отличие от глобальных переменных, адреса которых в ассемблерной программе задаются их именами, адреса локальных переменных задаются с помощью смещений относительно регистра ВР в виде:
смещeние [BP].
В этом случае в объектной программе «Коды символов» адресом переменной с является ее имя, а адрес переменной (параметра) x запишется как 6[ВР].
Сегмент команд состоит из процедур на языке ассемблера. Каждая процедура соответствует одной функции С0-программы и имеет такое же имя (перед ним вставляется подчеркивание). Процедуры размещены в таком же порядке, как функции С0-программы.
Локальная переменная k
-4 .. .
-2 Локальная переменная 1
BP Адрес предыдущего кадра -2 Значение y
2 Адрес возврата BP 0 Адрес предыд. кадра
4 Параметр n 2 Адрес возврата
.. . 4 Значение baza
Параметр 1 6 Значение x
Рис. 1. Кадр стека Рис. 2. Кадр стека
функции kod
Пример 2. Структура объектной программы для примера 1:
ASSUME CS:KOM_,SS:STEK_,DS:DAN_
; Сегмент стека
STEK_ SEGMENT STACK
DW 10000 DUP (?); Область памяти для стека
DNOST_ DW ?
STEK_ ENDS
; Сегмент команд
KOM_ SEGMENT
_kod PROC ;
.. .; Объектный код функции kod
_kod ENDP ;
_main PROC ;
.. .; Объектный код функции main
_main ENDP ;
INCLUDE std.asm; Процедуры библиотечных функций
KOM_ ENDS
; Сегмент данных
DAN_ SEGMENT
_c DW?; Область для значения глобальной переменной c
DAN_ ENDS
END _main; Адрес точки входа в программу
; Компилятоp С0 от 10/04/08;
; колич. ошибок 0
Структура обычной процедуры (не main):
_имя PROC
; Пролог процедуры (создание кадра стека)
PUSH BP; Сохранение регистра BP
MOV BP,SP; Адрес кадра стека
[ SUB SP,2*кол.лок.переменных; Создание лок-х пер-х]
; Объектный код операторов функции
.. .
; Объектный код оператора return; обычной функции (не main)
[ ADD SP,2*кол.лок.перем-х; Удаление локальных пер-х ]
POP BP; Восстановление регистра BP
RET 2*кол.параметров; Удаление параметров и возврат
_имя ENDP
Команда PUSH сохраняет значение регистра BP в стеке, MOV пересылает адрес кадра стека в регистр BP, SUB выделяет в кадре стека место для локальных переменных.
Команда ADD удаляет из стека локальные переменные, РОР восстанавливает в регистре BP адрес кадра вызывающей функции, RET удаляет из стека параметры функции и выполняет возврат.
Для функции main вместо команды RET используются команды:
MOV AH,4CH
INT 21H
Они обеспечивает выход в операционную систему с помощью функции 4СН прерывания номер 21H (типа 21Н).
Процедура main имеет такой же пролог, как и обычная процедура, но предварительно должна инициализировать регистры сегментов и указатель стека.
Структура процедуры main:
_main PROC FAR
MOV AX,DAN_; Адрес сегмента данных--> регистр DS
MOV DS,AX ;
MOV AX,STEK_; Адрес сегмента стека --> регистр SS
MOV SS,AX ;
LEA SP,DNOST_; Адрес дна стека
; Пролог процедуры (создание кадра стека)
.. .
; Объектный код операторов функции main
.. .
; Объектный код оператора return; функции main
[ ADD SP,2*кол.лок.перем-х; Удаление локальных пер-х ]
POP BP; Восстановление регистра BP
MOV AH,4CH; Выход в MS DOS
INT 21H ;
_main ENDP
Обозначим объектный код некоторой конструкции С0-программы как
объект_код (конструкция).
Ниже в обобщенном виде приведена форма объектного кода разных видов операторов языка С0, которые записаны перед своим объектным кодом в виде комментария языка ассемблера (как в выходном файле компилятора С0).
; выражение;
объект_код (выражение)
; { оператор_1; оператор_2;… оператор_n; }
объект_код (оператор_1)
объект_код (оператор_2)
.. .
объект_код (оператор_n)
; return выражение;
объект_код (выражение)
ADD SP,2*кол.лок.переменных
POP BP
RET 2*кол.параметров
; if (выражение) оператор
объект_код (выражение)
РОР AX; значение выражения
TEST AX,AX; проверка значения
JNZ CC_i; истина (не нуль)
JMP CC_i+1; обход оператора
СС_i:
объект_код (оператор)
СС_i+1:
; while (выражение) оператор
СС_i:
объект_код (выражение)
РОР AX; значение выражения
TEST AX,AX; проверка значения
JNZ CC_i+1; истина
JMP CC_i+2; выход из цикла
CC_i+1:
объект_код (оператор); тело цикла
JMP CС_i; переход на начало цикла
CC_i+2:
Для организации ветвлений и циклов компилятор вставляет в объектную программу метки вида СС_1, СС_2и т. д., которые здесь обозначены как СС_i. Чтобы вставляемые транслятором метки не могли совпасть с именами функций или глобальных переменных С0-программы, транслятор добавляет к меткам символ подчеркивания “_”, который в языке ассемблера используется наравне с буквами. Этот прием называется декорированием имен. Имена функций и глобальных переменных, в свою очередь, приходится декорировать, чтобы они не совпали с именами сегментов, регистров или команд языка ассемблера.
В объектном коде операторов if и while пару соседних команд перехода нельзя заменить одной командой с противоположным условием, так как в IBM PC условный переход возможен лишь на расстояние не более 128 байтов. Если объект_код (оператор) занимает более 128 байт, возникнет ошибка. В отличие от условного перехода, безусловный переход возможен на любое расстояние.
Объектный код выражения состоит из групп команд, реализующих операции выражения в порядке их выполнения. Результат каждой операции (в том числе значение функции) и значение выражения помещаются в регистр AX.
Если выражение заключено в скобки, например, условие в операторах if и while или выражение-параметр в вызове функции, его значение затем помещается в стек.
Выражение из одного операнда (без операций) – переменная или константа, переводится в команду вида:
MOV AX, константа
илиMOV AX, адрес-переменной
Если операндами являются константы (числа), то операция выполняется транслятором, заменяется результатом и не появляется в объектном коде, например, объектный код выражения 8 / 2 + X совпадает с объектным кодом выражения 4 + X.
Объектный код операции состоит из следующих шагов.
1. Загрузка 2-го операнда в регистр BX (для присваивания и изменения знака вместо BX используется AX).
2. Загрузка 1-го операнда в регистр AX. (кроме присваивания и изменения знака).
3. Выполнение операции над AX и BX с записью результата в AX (остаток от деления получается в DX). Функции возвращают значение в регистре AX.
4. Если операция не является последней в выражении, то ее результат помещается в стек.
Указанный порядок загрузки операндов важен, если оба операнда являются результатами других операций и поэтому выбираются из стека (2-й операнд попадает туда позже, чем 1-й операнд, и поэтому должен выбираться в первую очередь).
Операция, операнды которой являются константами, не компилируется, а интерпретируется, т. е. выполняется транслятором, например, операторы
X=Y+2*5; и X=Y+10; имеют одинаковый объектный код.
Объектный код 3-го шага в зависимости от операции имеет следующий вид.
Присваивание = :
MOV адрес-переменной,AX
Операции сравнения ( == != < > <= >= ):
CMP AX,BX
MOV AX,1
усл-переход CC_i
SUB AX,AX
CC_i:
Команда условного перехода вставляется в зависимости от использованной операции сравнения.
Операция: == != < > <= >=
усл-переход: JE JNE JL JG JLE JGE
Арифметические операции:
+: ADD AX,BX; сложение
— : SUB AX,BX; вычитание
— : NEG AX; изменение знака
*: IMUL BX; умножение
/ или % :
CWD; преобразование делимого в 4 байта
IDIV BX; деление
Для операции %, если она последняя в выражении, добавляется команда:
MOV AX,DX
Вызов функции имя-функции (выражение_1,…, выражение_n) :
объект_код (выражение_1); параметр_1 --> стек
.. .
объект_код (выражение_n); параметр_n --> стек
CALL имя-функции
Для ассемблирования необходимо наличие библиотеки стандартных функций — файла std.asm, который присоединяется командой INCLUDE к транслируемой программе на этапе ее ассемблирования (приложение 1). Эта команда вставляется компилятором С0 в конце сегмента кода.
Для наглядности результата трансляции компилятор C0 переносит строки исходной программы в получаемую из нее ассемблерную программу в виде строк комментария, начинающихся символом ";". За каждой такой строкой размещается объектный код, т.е. команды, полученные в результате ее трансляции (точнее — после ее ввода).
Сообщения об ошибках в исходной программе также вставляются компилятором С0 в объектный код в виде строк комментария, содержащего номер (тип) ошибки и символ "^", указывающий на текущую позицию предшествующей исходной строки в момент обнаружения ошибки. В конце объектной программы вставляется итоговое сообщение о количестве обнаруженных ошибок, дублируемое на экране.
Программа 2. Ниже приведена объектная программа для С0-программы «Коды символов» (выведена в две колонки). Два последних оператора этой программы не приведены, т. к. практически совпадают с предыдущими двумя операторами и имеют аналогичный объектный код.
ASSUME CS:KOM_,SS:STEK_,DS:DAN_ STEK_ SEGMENT STACK DW 100 DUP (?) DNOST_ DW? STEK_ ENDS ;kod (x,baza) KOM_ SEGMENT ;{ int y;; if ((y = x/baza)!= 0) _kod PROC PUSH BP MOV BP,SP SUB SP,2 MOV BX,4[BP] MOV AX,6[BP] CWD IDIV BX MOV -2[BP],AX PUSH AX MOV BX,0 POP AX CMP AX,BX MOV AX,1 JNE CC_1 SUB AX,AX CC_1:; kod (y, baza); TEST AX,AX JNZ CC_3 JMP CC_2 CC_3: MOV AX,-2[BP] PUSH AX MOV AX,4[BP] PUSH AX CALL _kod; putchar (x%baza + 48); CC_2: MOV BX,4[BP] MOV AX,6[BP] CWD IDIV BX PUSH DX MOV BX,48 POP AX ADD AX,BX PUSH AX ;} CALL _putchar ADD SP,2 POP BP RET 4 ;int c; ;main () _kod ENDP | _main PROC FAR MOV AX,DAN_ MOV DS,AX MOV AX,STEK_ MOV SS,AX ;{ c=0; LEA SP,DNOST_ PUSH BP MOV BP,SP MOV AX,0; while (c != 13) MOV _c,AX CC_4: MOV BX,13 MOV AX,_c CMP AX,BX MOV AX,1 JNE CC_6 SUB AX,AX CC_6:; { c = getchar (); TEST AX,AX JNZ CC_7 JMP CC_5 CC_7: CALL _getchar; putchar (61); MOV _c,AX MOV AX,61 PUSH AX; kod (c, 10); CALL _putchar MOV AX,_c PUSH AX MOV AX,10 PUSH AX; } CALL _kod JMP CC_4 ;} CC_5: POP BP MOV AH,4CH INT 21H; _main ENDP INCLUDE std.asm KOM_ ENDS DAN_ SEGMENT _c DW? DAN_ ENDS END _main; Компилятоp С0 от 10/04/08:; колич. ошибок 0 |