Chapter_10 (1110562), страница 4
Текст из файла (страница 4)
Другими словами, одноимённые сегменты стека тоже склеиваются, это позволяет каждомумодулю увеличивать размер стека на нужное этому модулю число байт. Таким образом, головноймодуль (что вполне естественно) может и не знать, какой дополнительный размер стека необходимдля правильной работы остальных модулей.Для указания наложения одноимённых сегментов одного класса друг на друга при сборке программы из объектных модулей предназначен параметр common директивы segment. В качествепримера использования параметра common рассмотрим другое решение предыдущей задачи суммирования массива, при этом сегменты данных двух наших модулей будут накладываться друг надруга. В то же время кодовые сегменты будут по-прежнему склеиваться.
Итак, новые варианты модулей p1.asm и p2.asm приведены ниже.; p1.asm; Ввод массива, вызов внешней процедурыinclude io.asmStksegment stackdw64 dup (?)StkendsNequ1000Data segment commonAdwN dup (?)Sdw?Diagn db'Переполнение!',13,10,'$'Data endsCode segment publicassume cs:Code,ds:Data,ss:StkStart:movax,Datamovds,axmovcx,Nsubbx,bx; индекс массиваL:inint A[bx];Ввод массива Aaddbx,type A1Заметим, что одноимённые сегменты (как с параметром public, так и без него) могут встречаться ивнутри одного модуля.
В этом случае такие сегменты тоже склеиваются, но эту работу проводит не редакторвнешних связей, а сама программа Ассемблера.10loop Lextrn Sum:far; Внешнее имяcall Sum; Процедура суммированияoutint S; синоним имени Summa в p2.asmnewlinefinishpublic Error; Входная точкаError:leadx,ToutstrfinishCode endsendStart; головной модульComment * модуль p2.asmСуммирование массива, контроль ошибокinclude io.asm не нужен – нет ввода/выводаСтек головного модуля не увеличиваетсяВ конечном end не нужна метка Start*Mequ1000Data segment commonBdwM dup (?)Summa dw?Data endsCode segment publicassume cs:Code,ds:Datapublic Sum; Входная точкаSumproc farpush axpush cxpush bx; сохранение регистровxorax,axmovcx,Mxorbx,bx; индекс 1-го элементаL:addax,B[bx]jnoL1; Обнаружена ошибкаpopbxpopcxpopaxaddSP,4; удаление возвратаextrn Error:nearjmpErrorL1:addbx,type Bloop LmovSumma,axpopbxpopcxpopax; восстановление регистровretCode endsendТеперь сегменты данных с именем Data будут накладываться друг на друга (в головном модуле сегмент данных немного длиннее, так что длина итогового сегмента данных будет равна максимальной длине накладываемых сегментов).
Как видим, почти все имена в модулях теперь являются локальными, однако из-за наложения сегментов данных друг на друга получается, что имя A является синонимом имени B (это имена одной и той же области памяти – начала нашего суммируемого массива). Аналогично имена S и Summa также будут обозначать одну и ту же переменную в сегменте данных.11Можно сказать, что при наложении друг на друга сегментов разных модулей получаются неявные статические связи по данным (очевидно, что накладывать друг на друга сегменты команд почтивсегда бессмысленно).
Вследствие этого можно (как в нашем примере) резко сократить число явныхсвязей по данным (то есть имён входных точек и внешних адресов). Надо, однако, заметить, что такой стиль модульного программирования является весьма опасным: часто достаточно ошибиться врасположении хотя бы одной переменной в накладываемых сегментах, чтобы программа стала работать неправильно. 1 Например, рассмотрите, что будет, если поменять в одном из накладываемыхсегментов местами массив и переменную для хранения суммы этого массива (при компиляции никакой диагностики об этой семантической ошибке при этом, естественно, не будет).Итак, явные статические связи по данным требуют согласования имён и типов входных точек ивнешних адресов в разных модулях, а неявные статические связи (при наложении сегментов данных)требуют согласования взаимного расположения и типов переменных в сегменте (имена теперь являются локальными и о них не надо договариваться).
Оба способа связи модулей по данным имеютсвои достоинства и недостатки, однако, современные способы программирования рекомендуют какможно большее число связей между модулями по данным вообще делать не статическими, адинамическими. Другими словами, модули (кроме, естественно, головного) следует оформлять в виде наборов процедур и функций со стандартными соглашениями о связях. В этом случае, как мы знаем, связь между такими модулями по данным реализуется посредством механизма фактических иформальных параметров, при этом вообще отпадает необходимость "договариваться" между модулями об именах или взаимном расположении параметров в сегментах.Заметим, что во всех предыдущих примерах нам было всё равно, в каких именно конкретныхобластях памяти будут располагаться сегменты нашей программы во время счёта.
Более того, считается хорошим стилем так писать программы, чтобы их сегменты на этапе счёта могли располагатьсяв любых свободных областях оперативной памяти компьютера. Однако очень редко может понадобиться расположить во время счёта определённый сегмент с явно заданного программистом адресаоперативной памяти. Для обеспечения такой возможности на языке Ассемблер служит параметрat <адрес сегмента> директивы segment. Здесь <адрес сегмента> является адресом началасегмента в оперативной памяти, делённым на 16. В качестве примера рассмотрим такое описаниесегмента с именем Interrupt_Vector:Interrupt_Vector Segment at 0Divide_by_Zero dd?Trace_Programdd?Fatal_Interrupt dd?Int_Commanddd?Into_CommandddCode:Error.
. . . . . .Interrupt_Vector endsЭтот сегмент во время счёта программы будет накладываться на начало вектора прерываний, азначения переменных этого сегмента будут обозначать конкретные адреса процедур обработки прерываний. Так заданный сегмент данных может облегчить для программиста написание собственныхпроцедур-обработчиков прерываний.Рассмотрим теперь второй этап работы редактора внешних связей – настройку всех внешнихимён на соответствующие им входные точки в других модулях.
На этом этапе редактор внешнихсвязей начинает просматривать паспорта всех модулей и читать оттуда их внешние имена. Эта работа начинается с головного модуля, для всех его внешних имён ведётся поиск соответствующих имвходных точек в других модулях. Если такой поиск оказывается безуспешным, то редактор внешнихсвязей фиксирует ошибку: "неразрешённое внешнее имя" (термин "неразрешённое" имеет здесь тотже смысл, что и в выражении "неразрешённая, т.е. нерешённая проблема").Для некоторого внешнего имени могут существовать и несколько входных точек в разных модулях. При этом многие редакторы внешних связей такую ситуацию не считают ошибкой, и берут припросмотре паспортов модулей первую встреченную входную точку с таким именем, так что про1Стиль программирования с общими (накладываемыми друг на друга) сегментами данных широко используется, например, в языке Фортран, там такие сегменты называются общими (common) блоками данныхдля нескольких модулей.
При этом часто случаются ошибки, вызванные плохим семантическим согласованиемв расположении переменных в таких общих блоках памяти.12граммисту надо быть осторожным и обеспечить уникальность входных имён у всех модулей. Можносказать, что такие редакторы внешних связей предполагают, что многомодульная программа разрабатывается сравнительно небольшим коллективом программистов, которые всегда смогут договориться между собой об уникальности всех внешних имён модулей (это часть спецификации разрабатываемой программы). 1К большому сожалению, некоторые редакторы внешних связей (в том числе и в нашей системепрограммирования с Ассемблером MASM-4.0) не проверяют соответствие типов у внешнегоимени и входной точки.
Таким образом, например, внешнее имя-переменная размером в слово можетбыть связано с входной точкой – переменной размером в байт или вообще с меткой. При невнимательном программировании это может привести к серьёзным ошибкам, которые будет трудно найтипри отладке программы. Заметим, что в тех системах, которые уделяют большое внимание вопросамнадёжности программирования, такие проверки всегда делаются. Например, редактор внешних связей Турбо-Паскаля считает несоответствие типов внешнего имени в одном модуле и соответствующей входной точки другого модуля фатальной ошибкой.
Скажем здесь, что в системе программирования с языком С, который Вы, скорее всего, будете изучать, также принимаются специальные мерыпо контролю соответствия типов внешних имён и входных точек модулей.Вернёмся к нашему редактору внешних связей. Когда для некоторого внешнего имени найденасоответствующая входная точка, то устанавливается связь: адрес входной точки записывается в соответствующее поле внешнего имени. Например, для командыcall Sum; Формат i32=seg:off = call seg:offна место поля off запишется смещение начала процедуры суммирования в объединённом послесклеивания сегменте кода, а поле seg пока останется незаполненным, его значение (адрес началасегмента кода, делённый на 16) будет известно только после размещения программы в оперативнойпамяти перед началом счёта. Аналогично, в поле с именем N второго операнда командыmov cx,Nзапишется значение 1000:mov cx,1000Итак, если для каждого внешнего имени найдена входная точка в другом объектном модуле, торедактор внешних связей нормально заканчивает свою работу, выдавая в качестве результата загрузочный модуль.