Вход/Регистрация
Давайте создадим компилятор!
вернуться

Креншоу Джек

Шрифт:

Они вам лгут!!!

Если вы не верите в это, попробуйте разделить любое большое 32-разрядное число (это означает, что оно имеет ненулевые биты в старших 16 разрядах) на целое число 1. Вы гарантированно получите исключение переполнения.

Проблема состоит в том, что эта команда в действительности требует, чтобы получаемое частное вписывалось в 16-разрядный результат. Этого не случится, если делитель достаточно большой. Когда любое число делится на единицу, частное будет конечно тем же самым, что и делимое.

С начала времен (ну во всяком случае компьютерных) архитекторы ЦПУ предусматривали этот маленький подводный камень в схеме деления. Это обеспечивает некоторую симметрию, так как это своего рода инверсия способа каким работает умножение. Но так как единица – это совершенно допустимое (и довольно частое) число для использования в качестве делителя, делению, реализованному аппаратно, требуется некоторая помощь от программистов.

Подразумевает следующее:

Тип частного всегда должен быть того же самого типа, что и делимое. Он независим от делителя.

Несмотря на то, что ЦПУ поддерживает деление длинного слова, аппаратно предоставленной инструкции можно доверить только делимые байт и слово. Для делимых типа длинное слово нам необходима другая библиотечная подпрограмма, которая может возвращать длинный результат.

Это похоже на работу для другой таблицы, для суммирования требуемых действий:

T1

T2 B W L

B Преобразовать D0 в W

Преобразовать D7 в L

DIVS

Result = B Преобразовать D0 в W

Преобразовать D7 в L

DIVS

Result = W Преобразовать D0 в L

JSR DIV32

Result = L

W Преобразовать D7 в L

DIVS

Result = B Преобразовать D7 в L

DIVS

Result = W Преобразовать D0 в L

JSR DIV32

Result = L

L Преобразовать D7 в L

JSR DIV32

Result = B Преобразовать D7 в L

JSR DIV32

Result = W JSR DIV32

Result = L

(Вы можете задаться вопросом, почему необходимо выполнять 32-разрядное деление, когда делимое, скажем, всего лишь байт. Так как число битов в результате может быть только столько, сколько и в делимом, зачем беспокоиться? Причина в том, что если делитель – длинное слово и в нем есть какие-либо установленные старшие разряды, результат деления должен быть равен нулю. Мы не смогли бы получить его, если мы используем только младшее слово делителя)

Следующий код предоставляет корректную функцию для PopDiv:

{–}

{ Generate Code to Divide Stack by the Primary }

function PopDiv(T1, T2: char): char;

begin

Pop(T1);

Convert(T1, 'L', 'D7');

if (T1 = 'L') or (T2 = 'L') then begin

Convert(T2, 'L', 'D0');

GenLongDiv;

PopDiv := 'L';

end

else begin

Convert(T2, 'W', 'D0');

GenDiv;

PopDiv := T1;

end;

end;

{–}

Две подпрограммы генерации кода:

{–}

{ Divide Top of Stack by Primary (Word) }

procedure GenDiv;

begin

EmitLn('DIVS D0,D7');

Move('W', 'D7', 'D0');

end;

{–}

{ Divide Top of Stack by Primary (Long) }

procedure GenLongDiv;

begin

EmitLn('JSR DIV32');

end;

{–}

Обратите внимание, мы предполагаем, что DIV32 оставляет результат (длинное слово) в D0.

ОК, установите новые процедуры деления. Сейчас у вас должна быть возможность генерировать код для любого вида арифметических выражений. Погоняйте ее!

Завершение

Наконец-то, в этой главе мы узнали как работать с переменными (и литералами) различных типов. Как вы можете видеть, это не было слишком сложно. Фактически, в каком-то отношении большая часть кода выглядит даже еще проще, чем это было в более ранних программах. Только операторы умножения и деления требуют небольших размышлений и планирования.

Основная идея, которая облегчила нам жизнь, – идея преобразования процедур типа Expression в функции, возвращающие тип результата. Как только это было сделано, мы смогли сохранить ту же самую общую структуру компилятора.

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

Я так же игнорировал логические операторы And, Or и т.д. Оказывается, их довольно легко обрабатывать. Все логические операторы – побитовые операции, так что они симметричны и, следовательно, работают в том же самом режиме, что и PopAdd. Однако, имеется одно отличие: если необходимо расширить длину слова для логической переменной, расширение должно быть сделано как число без знака. Числа с плавающей точкой, снова, являются простыми для обработки... просто еще несколько процедур, которые будут добавлены в run-time библиотеку или, возможно, инструкции для математического сопроцессора.

  • Читать дальше
  • 1
  • ...
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: