Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
lab_tlpt_12_7.doc
Скачиваний:
2
Добавлен:
01.03.2025
Размер:
494 Кб
Скачать
☆

Лабораторная работа № 9. Генератор кода 5.2.

Генерация кода для простейших арифметических операций, работа с ассемблерным стеком. Генерация загрузки и сохранения переменных. Разбор и генерация оператора печати.

Для реализации арифметических (и не только) операций в языке ассемблер используется работа с регистровой памятью. Так, чтобы поместить значение (числовую константу или переменную) в регистр, используется инструкция mov. Команды для арифметических операций приведены в таблице ниже.

Операция

Инструкция ассемблера

+

add

-

sub

*

mul

/

div

Таким образом, код для выражения a + b будет выглядеть следующим образом:

mov ax, a

mov bx b

add ax, bx

Результат этой последовательности команд будет сохранен в регистре ax. Но в ситуации со сложными выражениями необходимо где-то сохранять промежуточные результаты. Для этих целей мы будем использовать ассемблерный стек: инструкция push - помещение значения регистра на верхушку стека, pop – извлечение из стека в регистр. После каждой атомарной операции мы будем сохранять результат в стек, чтобы не потерять его. Ниже приведен доработанный псевдокод методов синтаксического анализатора: добавленные строки выделены.

разобратьИнструкцию()

{

если( LexicalAnalyzer.текущаяЛексема == Lexems. Идентификатор )

{

Идентификатор Х = NameTable.найтиПоИмени( LexicalAnalyzer.текущееИмя );

если( !Х. эквивалентно( новый Идентификатор() ) )

{

разобратьИнструкциюПрисваивания();

CodeGenerator.добавитьИнструкцию( "pop ax" );

CodeGenerator.добавитьИнструкцию( "mov " + X. Имя + ", ax" );

}

иначе

ошибка();

}

}

tType разобратьСложениеИлиВычитание()

{

tType т;

Lexems оператор;

если( LexicalAnalyzer.текущаяЛексема == Lexems.Плюс

или LexicalAnalyzer.текущаяЛексема == Lexems.Минус )

{

оператор = LexicalAnalyzer.текущаяЛексема;

LexicalAnalyzer.разобратьСледующуюЛексему();

т = разобратьУмножениеИлиДеление();

}

иначе

т = разобратьУмножениеИлиДеление();

если( LexicalAnalyzer.текущаяЛексема == Lexems.Плюс

или LexicalAnalyzer.текущаяЛексема == Lexems.Минус )

{

выполнять

{

оператор = LexicalAnalyzer.текущаяЛексема;

LexicalAnalyzer.разобратьСледующуюЛексему();

т = разобратьУмножениеИлиДеление();

switch( оператор )

{

case Lexems.Плюс:

CodeGenerator.добавитьИнструкцию( "pop bx" );

CodeGenerator.добавитьИнструкцию( "pop ax" );

CodeGenerator.добавитьИнструкцию( "add ax, bx" );

CodeGenerator.добавитьИнструкцию( "push ax" );

break;

case Lexems.Минус:

CodeGenerator.добавитьИнструкцию( "pop bx" );

CodeGenerator.добавитьИнструкцию( "pop ax" );

CodeGenerator.добавитьИнструкцию( "sub ax, bx" );

CodeGenerator.добавитьИнструкцию( "push ax" );

break;

}

}

пока( LexicalAnalyzer.текущаяЛексема == Lexems.Плюс

или LexicalAnalyzer.текущаяЛексема == Lexems.Минус );

}

вернуть т;

}

tType разобратьУмножениеИлиДеление()

{

Lexems оператор;

tType т = разобратьПодвыражение();

если( LexicalAnalyzer.текущаяЛексема == Lexems.Умножить

или LexicalAnalyzer.текущаяЛексема == Lexems.Делить )

{

выполнять

{

оператор = LexicalAnalyzer.текущаяЛексема;

LexicalAnalyzer.разобратьСледующуюЛексему();

т = разобратьПодвыражение();

switch( оператор )

{

case Lexems.Умножить:

CodeGenerator.добавитьИнструкцию( "pop bx" );

CodeGenerator.добавитьИнструкцию( "pop ax" );

CodeGenerator.добавитьИнструкцию( "mul bx" );

CodeGenerator.добавитьИнструкцию( "push ax" );

break;

case Lexems.Делить:

CodeGenerator.добавитьИнструкцию( "pop bx" );

CodeGenerator.добавитьИнструкцию( "pop ax" );

CodeGenerator.добавитьИнструкцию( "cwd" );

CodeGenerator.добавитьИнструкцию( "div bl" );

CodeGenerator.добавитьИнструкцию( "push ax" );

break;

}

}

пока( LexicalAnalyzer.текущаяЛексема == Lexems.Умножить

или LexicalAnalyzer.текущаяЛексема == Lexems.Делить );

}

вернуть т;

}

tType разобратьПодвыражение()

{

Идентификатор х;

tType т = tType.None;

если( LexicalAnalyzer.текущаяЛексема == Lexems. Идентификатор )

{

х = NameTable.найтиПоИмени( LexicalAnalyzer.текущееИмя );

если( !Х. эквивалентно( новый Идентификатор() ) и х.категория == tCat.Var )

{

CodeGenerator.добавитьИнструкцию( "mov ax, " + LexicalAnalyzer.текущееИмя);

CodeGenerator.добавитьИнструкцию( "push ax" );

LexicalAnalyzer. разобратьСледующуюЛексему ();

вернуть х.тип;

}

иначе

ошибка();

}

иначе если( LexicalAnalyzer.текущаяЛексема == Lexems.Число )

{

CodeGenerator.добавитьИнструкцию( "mov ax, " + LexicalAnalyzer.текущееЧисло );

CodeGenerator.добавитьИнструкцию( "push ax" );

LexicalAnalyzer.разобратьСледующуюЛексему();

вернуть tType.Int;

}

иначе если( LexicalAnalyzer.текущаяЛексема == Lexems.ЛеваяСкобка )

{

LexicalAnalyzer.разобратьСледующуюЛексему();

т = разобратьВыражение();

проверитьЛексему( Lexems.ПраваяСкобка );

}

иначе

ошибка();

вернуть т;

}

После доработки текущих методов наш транслятор будет генерировать ассемблерный код, однако, проверить работоспособность этого кода можно проверить разве что отладчиком, ведь в синтаксисе нашего языка до сих пор нет оператора вывода на печать. Закрепим за оператором вывода на печать ключевое слово print. В синтаксическом анализаторе необходимо реализовать метод разбора этого оператора.

разобратьИнструкциюВыводаНаПечать()

{

проверитьЛексему( Lexems.ВыводНаПечать );

если( LexicalAnalyzer.текущаяЛексема == Lexems.Идентификатор )

{

Идентификатор X = NameTable.НайтиПоИмени( LexicalAnalyzer.текущееИмя );

CodeGenerator.добавитьИнструкцию( "push ax" );

CodeGenerator.добавитьИнструкцию( "mov ax, " + LexicalAnalyzer.текущееИмя );

CodeGenerator.добавитьИнструкцию( "CALL PRINT" );

CodeGenerator.добавитьИнструкцию( "pop ax" );

LexicalAnalyzer.разобратьСледующуюЛексему();

}

иначе

ошибка();

}

Внутри этого метода мы сохраняем содержимое регистра аккумулятора в стек, вызываем процедуру вывода на печать, о которой речь пойдет далее, и восстанавливаем содержимое аккумулятора. Далее дополним метод разобратьИнструкцию().

разобратьИнструкцию()

{

если( LexicalAnalyzer.текущаяЛексема == Lexems. Идентификатор )

{

Идентификатор Х = NameTable.найтиПоИмени( LexicalAnalyzer.текущееИмя );

если( !Х. эквивалентно( новый Идентификатор() ) )

разобратьИнструкциюПрисваивания();

иначе

ошибка();

}

иначе если( LexicalAnalyzer.текущаяЛексема == Lexems.ВыводНаПечать )

разобратьИнструкциюВыводаНаПечать();

}

В генераторе кода реализуем объявление процедуры вывода на печать, а в методе компилировать() синтаксического анализатора вызовем реализованный метод.

декларироватьПроцедуруВыводаНаПечать()

{

добавитьИнструкцию( "PRINT PROC NEAR" );

добавитьИнструкцию( "MOV CX, 10" );

добавитьИнструкцию( "MOV DI, BUFEND - PRINT_BUF" );

добавитьИнструкцию( "PRINT_LOOP:" );

добавитьИнструкцию( "MOV DX, 0" );

добавитьИнструкцию( "DIV CX" );

добавитьИнструкцию( "ADD DL, '0'" );

добавитьИнструкцию( "MOV [PRINT_BUF + DI - 1], DL" );

добавитьИнструкцию( "DEC DI" );

добавитьИнструкцию( "CMP AL, 0" );

добавитьИнструкцию( "JNE PRINT_LOOP" );

добавитьИнструкцию( "LEA DX, PRINT_BUF" );

добавитьИнструкцию( "ADD DX, DI" );

добавитьИнструкцию( "MOV AH, 09H" );

добавитьИнструкцию( "INT 21H" );

добавитьИнструкцию( "RET" );

добавитьИнструкцию( "PRINT ENDP" );

}

компилировать()

{

………………………………………………………………………………………………………

CodeGenerator.декларироватьЗавершениеОсновнойПроцедуры();

CodeGenerator. декларироватьПроцедуруВыводаНаПечать();

CodeGenerator.декларироватьЗавершениеКода();

}

Для тестирования работы транслятора можно использовать любой код на исходном языке, например, такой:

int a,b,c

begin

a=7

b=4

a=a*a-(7*b)

c=(a-b)*a+b

print c

end

После компиляции, линкования и запуска сгенерированного ассемблерного кода программа должна вывести на печать правильный результат – 361.

Примеры кода:

Файлы CodeGenerator.cs, SyntaxAnalyzer.cs в проекте.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]