9_Дополнительные воможности ассемблера (975806), страница 6
Текст из файла (страница 6)
В нашем примере описания типа date мы указали, что битовое поле сименем year должно иметь начальное значение по умолчанию 4, если при порождении такой переменной этому полю явно не будет задано другое значение. В директивах резервирования памятиначальные значения битовых полей перечисляются через запятые в угловых скобках. Заметьте также, что символ ? здесь при резервировании памяти задаёт не неопределённое, как в других случаях,а нулевое начальное значение соответствующего упакованного битового поля.Языковые средства, которые Ассемблер предоставляет для работы с упакованными битовымиполями, разберём на следующем примере. Напишем фрагмент программы, в котором берётся значение переменной с именем X типа date, и хранящаяся в этой переменной дата выводится на экран впривычном виде, например, как день/месяц/год. Так как для значения года хранятся только двепоследние десятичные цифры, то здесь у нас тоже возникает своя "проблема 2000 года".
Мы сделаемследующую спецификацию нашей задачи: если значение года больше 50, то будем считать дату принадлежащей прошлому веку, иначе – текущему веку.1 Ниже приведён фрагмент программы, решающий эту задачу.movax,Xmovcl,day; cl=11 (№ позиции поля day)shrax,cl; ax:=Только поле деньoutword axoutch '/'movax,Xandax,mask month; mask month=0000011110000000bmovcl,month; cl=7 (№ позиции поля month)shrax,cl; ax:=Только поле месяцoutword axoutch '/'movax,Xandax,mask year; mask year=0000000001111111bmovbx,2000; нынешний векcmpax,50jbeLmovbx,1900; прошлый векaddax,bx; ax:=Год из 4-х цифрoutword axnewlineL:Прокомментируем этот фрагмент программы. Как мы знаем, почти всем именам, которые программист записывает в предложениях Ассемблера, приписываются определённые числовые значения, которые Ассемблер и подставляет на место этого имени в программу.
Так, значением имёнпеременных и имён меток являются их адреса – смещения относительно начала сегмента,2 значением1Такая спецификация сделана исключительно для учебных целей, она проста для понимания и использования, но допускает хранение некоторых дат в двух форматах. Например, даты, записанные как 01/01/110 и01/01/10 будет соответствовать одной "настоящей" дате 01/01/2010. Это происходит из-за того, что извсего возможного диапазона хранимых в таком виде 128 лет (0–127) мы использовали только диапазон в 100лет.2Как исключение метки, указанные как операнды в командах относительного перехода, как мы ужезнаем, имеют значения констант, задающих знаковое расстояние до точки перехода в байтах.18имён целочисленных констант, определяемых директивами эквивалентности – сами эти константы,значениями имён сегментов являются адреса начал этих сегментов в оперативной памяти, делённыена 16 и т.д.
Отметим, что некоторое имена могут вообще не иметь числовых значений, например,это, например, вот такое имя S, заданное директивойS equ [bp+6]Имя упакованного битового поля тоже имеет значение – это количество разрядов, на которое надо сдвинуть это поле, чтобы оно попало в самую правую позицию той области памяти (байта илислова) в которой хранится данное поле.
Например, имя month имеет значение 7. Таким образом,имена упакованных битовых полей в Ассемблере предназначены в программе для задания числасдвигов слова или байта, что мы и использовали в нашем примере.Другое языковое средство, предоставляемое Ассемблером для работы с упакованными битовыми полями – это одноместный оператор, который задаётся служебным именем mask. Операндомэтого оператора должно являться имя упакованного битового поля, а значением данного операторабудет байт или слово, содержащее биты '1' только в позициях, занимаемых указанным полем, в остальных позициях будут находиться нулевые биты. Как видим, оператор mask удобно использоватьдля задания констант (масок) в командах логического умножения для выделения нужного битовогополя, это и было продемонстрировано в нашем примере.И, наконец, рассмотрим ещё один оператор Ассемблера, предназначенный для работы с именами упакованных битовых полей.
Значением оператораwidth <имя битового поля>является ширина этого поля, т.е. количество составляющих его бит. Например, для нашего описаниятипа date выражение width month равно 4. Заметим, что если применить этот оператор к именитипа, задающего упакованные битовые поля, то значением оператора width будет сумма длин всехполей в этом типе.
Например, width date равно 16. Оператор width достаточно редко встречается в практике программирования, и мы не будем приводить примеры его использования.Итак, из рассмотренного выше примера видно, что упакованные битовые поля, как и другие упакованные структуры данных, которые мы рассматривали ранее, позволяют экономить место в памяти для размещения данных, но требуют значительно больше команд для манипулирования такимиупакованными данными.
На этом закончим наше краткое знакомство с упакованными битовыми полями в языке Ассемблера.9.5. Структуры на АссемблереДругим примером описания типов в языке Ассемблера являются структуры. В языке Паскальаналогом структур Ассемблера являются записи, которые позволяют описать совокупность именованных переменных (вообще говоря, разных типов) как новый самостоятельный тип данных. Описания типа структуры задаются в языке Ассемблера в виде<имя типа структуры> strucПредложения резервированияпамяти под поля структуры<имя типа структуры> endsВ качестве примера рассмотрим описание простой структуры, предназначенной для храненияинформации о студенте: текстовой строки, в которой будет храниться фамилия студента (не более 20символов), а также трёх полей для хранения даты рождения (дня, месяца и года).Stud strucFiodb"********************"; 20 '*'db'$'Daydb?Month db?Year dw1986Stud endsВ нашей структуре описано 5 полей. Первое поле с именем Fio длиной 20 байт предназначенодля хранения фамилии студента, у этого поля есть значение по умолчанию – это строка из 20 символов '*'.
Обратите внимание на следующую тонкость: это предложение нельзя записать в виде19Fio db20 dup ('*'); 20 '*'так как в этом случае будет определено 20 полей длиной по одному байту каждое. Соответственно,если в первом случае имя Fio является именем всей строки символов (массива коротких целыхчисел), то во втором – будет только именем первого символа в массиве из 20 символов.Второе поле нашей структуры длиной в один байт не имеет имени, но имеет значение по умолчанию '$'. Как видим, мы подготавливаем удобный способ вывода фамилии студента с использованием уже знакомой нам макрокоманды outstr.
Три последних поля нашей структуры определяют переменные для хранения числовых значений дня, месяца и года рождения студента, причём угода рождения мы предусмотрели значение по умолчанию 1986.Каждое имя поля имеет целочисленное значение, которое равно смещению в байтах начала этогополя от начала структуры. Так, например, имя поля Month в описанной выше структуре Stud имеетзначение 22.После описания структуры можно резервировать в некотором сегменте переменные описанноготипа с помощью предложений резервирования памяти, например:St1StudSt2StudGrup Stud<'Иванов И.И.',,21,4><'Петров П.П.',,31,5,1985>30 dup (<>); Студенческая группаСтроки, задающие начальные значения фамилий, дополняются пробелами справа, если их длина менее длины поля (в нашем примере менее 20 символов). Обратите внимание, что мы выделилидвумя запятыми подряд второе безымянное поля, которому при размещении переменной в памяти,таким образом, не присваивается нового значения, и это поле сохраняет начальное значение поумолчанию '$', определённое при описании структуры Stud.
Для студента с фамилией Ивановмы не задали начального значения поля, в котором хранится год рождения, таким образом, это полебудет иметь начальное значение 1986, заданное в описании типа Stud.К сожалению, поля структуры могут принадлежать только к стандартным типам Ассемблера(db, dw, dd и т.д.).
Нельзя, например, задавать в виде полей структуры определённые пользователемупакованные битовые поля и другие структуры.В качестве небольшого примера рассмотрим фрагмент программы на Ассемблере, в котором выводится информация о студенте, хранящаяся в переменной с именем Z типа Stud.movdx,offset Z.Fiooutstrnewlinexorax,axmoval,Z.Dayoutword axoutch '/'moval,Z.Monthoutword axoutch '/'outword Z.YearnewlineВ этом примере мы встретились с новым двуместным оператором языка Ассемблер, которыйобозначается символом '.'. Этот оператор выполняется Ассемблером так же, как и двуместныйоператор '+', однако тип (размер) этого операнда задаётся тем именем, которое располагается послеточки. Таким образом, командаoutword Z.Yearэквивалента командеoutword word ptr Z+YearЭто показывает, как различаются правила вычисления типа выражения, в которое входит полеструктуры.