Выбрать главу

2.2.5 Множественные проходы

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

Изменение значений меток может быть причиной того, что некоторые инструкции перекодируются с другими длинами и это снова повлечет изменение меток. И так как метки и константы ещё могут использоваться внутри выражений, которые влияют на поведение директив управления, весь блок инструкций в новый проход может ассемблироваться абсолютно по-другому. Поэтому ассемблер делает проходы снова и снова, каждый раз пытаясь создать лучшие прогнозы, чтобы приблизиться к финальному решению, когда все значения спрогнозированы правильно. Для прогнозов используются разные методы, которые выбираются с тем, чтобы найти с как можно меньшим количеством проходов решение наименьшей возможной длины для большинства программ.

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

Если в коде встречается директива «display», фактически отображаются только сообщения, созданные в последний совершённый проход. В случае, если ассемблер остановился из-за ошибки, эти сообщения могут отражать спрогнозированные значения, которые еще не разрешены правильно.

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

if ~ defined alpha

 alpha:

end if

Если оператор «defined» выдает значение истина, если выражение, следующее за ним, в этом месте может быть вычислено, что в данном случае означает, что метка «alpha» где-то определена. Но блок выше определяет эту метку только, если значение, данное оператором «defined» ложь, что ведет к противоречию и делает невозможным разрешить такой код. Если, обрабатывая директиву «if» ассемблер должен прогнозировать, будет ли где-нибудь определена метка «alpha» (этого делать не приходится только если метка уже определена раньше), то какой бы ни был прогноз, всегда происходит противоположное. Поэтому ассемблирование остановится, если только метка «alpha» не определена где-то в коде перед вышеуказанным блоком — в этом случае, как уже было отмечено прогнозирование не требуется и блок просто будет пропущен.

Предыдущий пример может быть создан как попытка определить метку, только если этого все ещё не сделано. Эти строк неправильны, поскольку оператор «defined» проверяет определена ли метка где-либо вообще, и это включает определение внутри этого условного блока. Однако есть способ обойти эту проблему:

if ~ defined alpha | defined @f

 alpha:

 @@:

end if

«@f» это всегда та же метка, что ближайший следующий за ним символ «@@», поэтому предыдущий пример значит то же, как если бы вместо анонимной метки было определено любое уникальное имя. Если метка «alpha» ещё не определена, ассемблер спрогнозирует значение «defined alpha» как ложь, это будет однако значить, что будут определены обе метки. Но на следующем проходе ассемблер спрогнозирует, что определены обе метки, что заставит определить их вновь — так прогноз будет совпадать с результатом и процесс ассемблиования придет к правильному решению. Анонимная метка выступает здесь как маркер того, что метка «alpha» определена в этом месте.

Из этого примера вы можете заключить, что прогноз для оператора «defined» очень прямолинейный — метка прогнозируется как определенная только если она была определена в предыдущий проход (а если она была определена в текущий проход, прогноз не требуется). То же самое относится к оператору «used». Однако прогнозы для значений меток не так просты и вам никогда не следует полагать, что ассемблер работает таким способом.