Chapter_09 (1110561), страница 5
Текст из файла (страница 5)
Заметим, что это неизменило величины нашего элемента, так как он беззнаковый (0..15).Наш простой пример показывает, что при работе с упакованными данными скорость доступа кним уменьшается в несколько раз, и программист должен решить, стоит ли это достигнутой намиэкономии памяти (в нашем примере мы сэкономили 5000 байт оперативной памяти). Обычно этотипичная ситуация в программировании: выигрывая в скорости обработки данных, мы проигрываемв объёме памяти для хранения этих данных, и наоборот. Иногда, по аналогии с физикой, это называют "законом рычага", который гласит, что, выигрывая в силе, мы проигрываем в длине перемещенияконца рычага, и наоборот.
Заметим также, что самый большой выигрыш по памяти получается приобработке упакованных массивов логических величин, например:var Z: packed array[1..100000] of boolean;В качестве ещё одного примера использования логических команд рассмотрим реализацию наАссемблере некоторых операций языка Паскаль над множествами. Пусть на Паскале есть описания двух символьных множеств X и Y, а также символьной переменной Sym:Var X,Y: set of char; Sym: char;15Сначала напишем на Ассемблере фрагмент программы для реализации операции объединениядвух множеств:X := X + Y;Каждое такое множество будем хранить в памяти в виде 256 последовательных битов, т.е. 32байт или 16 слов.
Бит, расположенный в позиции i принимает значение 1, если символ с кодом (номером) i присутствует во множестве, иначе этот бит имеет значение 0 (заметим, что язык ТурбоПаскаль реализует такое же представление для переменных типа set of char ). Множества X и Yможно так описать на Ассемблере:Data Segment. . .Xdw16 dup (?)Ydw16 dup (?). . .Data endsТогда операцию объединения двух множеств можно реализовать, например, таким фрагментомна Ассемблере:L:movxormovoraddloopcx,16bx,bxax,Y[bx]X[bx],axbx,2LОбратите внимание на такое обстоятельство.
Несмотря на то, что множества X и Y из последнегопримера являются упакованными структурами данных (упакованными логическими векторами из256 элементов), при работе с ними происходит не замедление, а увеличение (в нашем случае в 16раз) скорости работы, по сравнению с неупакованными данными.
Как мы понимает, такое увеличение скорости происходит из-за параллельной обработки битов своих операндов логической командой and.В заключение рассмотрим условный оператор, включающий знакомую нам по языку Паскальоперацию отношения in (символ Sym входит во множество X):if Sym in X then goto L;На Ассемблере этот оператор можно, например, реализовать с использованием логических команд в виде такого фрагмента программы:XSym. . .db32 dup (?); 32*8=256 битовdb?. .
.moval,Symmovcl,3shral,clxorbx,bxmovbl,al; Индекс нужного байта в Xmoval,X[bx]; Байт с битом символа Symmovcl,Symandcl,111b; Позиция символа Sym в байтеshlal,cl; наш бит в al – крайний слеваshlal,1; Нужный бит в CFjcLРазберём, как работает эта программа. Сначала, используя команду логического сдвига на 3(это, как мы знаем, аналогично делению беззнакового числа на 8), на регистр bx заносим индекс того байта в массиве X, в котором находится бит, соответствующий символу Sym во множестве X.
За-16тем этот байт выбирается на регистр al, а на регистр cl помещаем три последних бита номера символа Sym в алфавите – это и будет номер нужного нам бита в выбранном байте (биты в байте приэтом нумеруются, как нам и нужно, слева направо, начиная с нуля). После этого первой командойсдвига перемещаем нужный нам бит в крайнюю левую позицию в байте, а следующей командойсдвига – во флаг переноса.
Теперь осталось только сделать условный переход jc по значению этогофлага.На этом мы закончим рассмотрение логических команд.9.4. Упакованные битовые поляОдним из применений логических команд и команд сдвигов, как мы выяснили, является работа счастями байтов и слов (т.е. составляющими их битами и наборами битов).
Для автоматизации работыс такими данными Ассемблер предоставляет в распоряжение программиста определённые языковыесредства. К таким средствам, в частности, относятся так называемые упакованные битовые поля(record). На примере упакованных битовых полей мы познакомимся со способами описания типов в языке Ассемблера.До сих пор для работы с переменными в Ассемблере мы использовали только предложения резервирования памяти, которые являются аналогами описания переменных в языках высокого уровня.Теперь познакомимся с Ассемблерными аналогами описания типов данных.
Использование типовданных в Ассемблере, как и во всяком алгоритмическом языке, повышает надёжность программирования и делает программу лучше для понимания и модификации.Битовым полем в Ассемблере называется последовательный набор битов в байте или слове, причём каждому такому битовому полю пользователь присваивает имя. В одном байте или слове можноопределить несколько битовых полей, если их общая длина не превышает, соответственно, размерабайта или слова. Так организованные данные и называются упакованными битовыми полями(имеется в виду, что эти поля плотно упакованы, т.е. примыкают друг к другу, внутри байта или машинного слова).В Ассемблере для упакованных битовых полей предусмотрено описание типа.
В качестве примера рассмотрим описание трёх битовых полей, упакованных в одно машинное слово. Присвоимэтому типу данных имя date, что будет отражать суть трёх входящих в этот тип битовых полей,предназначенных для хранения некоторой даты (числа, месяца и двух последних цифр года):date record day:5,month:4,year:7=4В этом описании типа указано, что переменная с именем типа date для своего хранения потребует одномашинное слово (два байта), в котором будут содержаться битовые поля с именами day (длиной 5 бит),month (4 бита) и year (7 битов). Заметим, что некоторым аналогом этого описания в языке Паскаль будет,например, такое описание типа (при условии, что Паскаль-машина "прислушается" к нашей рекомендации использовать именно упакованную структуру данных):type date = packed recordday: 0..31; month: 0..15; year: 0..127end;Упакованные битовые поля располагаются в машинном слове в порядке их описания, но выравниваются по правому краю машинного слова или байта, если сумма длин битовых полей меньшедлины слова (или байта).
Ниже показан вид переменной типа date с указанием номеров битов машинного слова, занимаемыми битовыми полями:1511day107 6month0YearПодчеркнём, что имя date является именем типа, и показанное выше предложение Ассемблера не резервирует в памяти никакой области данных (никакой переменной), то есть, является с точкизрения языка Ассемблер директивой.
Само же резервирование областей памяти для хранения переменных типа date производится в некотором сегменте по предложениям резервирования памяти.Ниже показаны примеры таких предложений резервирования памяти для хранения переменных типаdate, в комментариях показаны начальные значения полей.17dt1date<,8>;day0month8year4dt2date<21,4,96>;21496dt3date<>;004dt4date<13,5,?>;1350Обратите внимание, что переменные типа упакованных битовых полей всегда порождаются сначальными значениями, которые либо задаются программистом в параметрах предложения резервирования памяти, либо берутся из описания данного типа, в остальных случаях они по умолчаниюсчитаются равными нулю.
В нашем примере описания типа 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Такая спецификация сделана исключительно для учебных целей, она проста для понимания и использования, но допускает хранение некоторых дат в двух форматах.