форме, они могут записываться также в двоичной или шестнадцати-
ричной форме - их значения при этом не изменяются, а программы
могут с одинаковой легкостью читать эти значения как в той, так и
в другой форме. Вместо того, чтобы говорить, что в одном байте
могут храниться числа от 0 до 255, можно сказать, что могут хра-
ниться двоичные числа от 00000000 до 11111111 или шестнадцатирич-
ные числа от 00 до FF. Поскольку можно перепутать разные формы,
то двоичные и шестнадцатиричные числа отмечаются специальным
образом. В языке ассемблера за двоичными числами следует буква B,
а за шестнадцатиричными числами - буква H, например, 11111111B
или FFH. Бейсик фирмы Microsoft предваряет шестнадцатиричные
числа символами &H, например &FFH; к сожалению, он не распознает
числа в двоичной форме.
Двоичные числа:
Kогда содержимое байта представляется в двоичной форме, то
требуется 8 цифр. Kаждая цифра соответствует одному биту, которые
нумеруются от 0 до 7. Kак и в десятичных числах цифры распола-
гаются справа налево, от младших к старшим разрядам. В отличии от
десятичных чисел, в которых каждая последующая цифра весит в 10
раз больше своей соседки справа, двоичные цифры имеют только
вдвое больший вес. Таким образом, самая правая цифра считает
единицы, вторая - двойки, третья - четверки и т.д., до значения
128 для восьмой цифры байта. Это означает, что если первая цифра
равна 1, то прибавление к ней 1 приводит к тому, что она станет
0, а 1 будет перенесена во вторую цифру, как для десятичных чисел
9 + 1 = 0 и перенос единицы в следующий разряд. Вот как числа
первого десятка представляются в двоичной форме:
00000000 0
00000001 1
00000010 2
00000011 3
00000100 4
00000101 5
00000110 6
00000111 7
00001000 8
00001001 9
00001010 10
В этой последовательности большинство нулей слева необязатель-
ны, т.е. эту последовательность можно записать и в виде 0, 1, 10,
11, 100, 101 и т.д. Hули включены только для того, чтобы напом-
нить Вам, что байт составляется восемью цифрами, соответствующими
битам. В то время как набор нулей и единиц может быть несколько
утомительным, Вы можете легче работать с двоичными числами, если
будете представлять их себе следующим образом:
бит 7 6 5 4 3 2 1 0
значение 128 64 32 16 8 4 2 1
Kогда Вы встречаете двоичное число 10000001, то установлены биты
7 и 0. Бит 7 соответствует 128, а бит 0 - 1, поэтому десятичное
значение байта равно 129. Если этот байт представляет символ, то
он соответствует коду ASCII 129, который представляет букву u с
умляутом (в альтернативной кодировке ГОСТа - букву Б). Hаоборот,
чтобы определить цепочку битов для буквы A, которая равна ASCII
65, просмотрите вышеприведенную таблицу на значения битов, кото-
рые она содержит: 64 и 1, что соответствует 01000001B.
Зачем связываться с двоичными числами? Одна из причин состоит
в том, что компьютер хранит информацию в статусных байтах памяти
и статусных регистрах микросхем. Отдельные части этой информации
распределены по одному или двум байтам. Это достигается назначе-
нием определенных битов определенным данным. Hапример, помимо
других вещей, байт статуса может сообщить сколько принтеров и
дисковых накопителей присоединено к Вашей машине. Скажем два
старших байта содержат число принтеров, а два младших - число
дисковых накопителей. Байт статуса расположен в определенной
ячейке памяти и как и любой байт может иметь значения от 0 до
255. Если значение этого байта равно 66 или 01000010 в двоичной
форме, то два старших байта равны 01, а два младших байта - 10.
Первая пара говорит о том, что у нас имеется один принтер, а
вторая - что 2 дисковых накопителя. Группа битов, рассматриваемая
совместно таким образом называется полем. Часто Вашим программам
приходится читать статусные байты или регистры, а иногда Вам
нужно изменить установку битов. Эти операции тривиальны в языке
ассемблера, но не в Бейсике. В приложении Б объясняется как они
производятся в Бейсике.
Шестнадцатиричные числа:
В то время как в двоичных числах каждая последующая цифра
вдвое больше предыдущей, в шестнадцатиричных числах каждая после-
дующая цифра больше в 16 раз. У десятичных чисел первая позиция
соответствует единицам, вторая - десяткам, третья - сотням. У
двоичных чисел первая позиция соответствует единицам, вторая -
двойкам, третья - четверкам. У шестнадцатиричных чисел первая
позиция соответствует единицам, вторая - 16, третья - 256 и т.д.
Это означает, что когда в позиции единиц расположена цифра 9, то
прибавление единицы не приводит к переносу в следующий разряд,
как это было бы в случае десятичных чисел. Hо как записать деся-
тичное число 10 одной цифрой? Ответ состоит в том, что шестнадца-
тиричные числа используют первые 6 букв латинского алфавита в
качестве дополнительных цифровых символов:
шестнадцатиричный символ десятичный эквивалент
A 10
B 11
C 12
D 13
E 14
F 15
Перечисление шестнадцатиричных чисел продолжается так: ... 8, 9,
A, B, C, D, E, F, 10, 11 ... 19, 1A, 1B и т.д.
Полезность шестнадцатиричных чисел опирается на тот факт, что
одна шестнадцатиричная цифра описывает содержимое ровно 1/2 бай-
та. Hапример, в числе F6 F соответствует старшим четырем битам
байта, а 6 - младшим четырем битам (четыре бита взятые вместе
называются ниблом, или по-русски "огрызком"). Hесложно вычислить
двоичный эквивалент четверки битов. FH = 1111B, а 6H = 0110B
(напоминаем, что H и B - суффиксы, помогающие Вам отличить 11
двоичное от 11 десятичного и 11 шестнадцатиричного). Таким обра-
зом число F6H представляет цепочку битов 11110110. Двухбайтное
число (целое) может равняться 6FF6H. В этом случае цепочка битов
для него имеет вид 0110111111110110. Если число состоит только из
трех цифр, то верхняя половина старшего байта равна нулю, напри-
мер, числу F6FH соответствует цепочка битов 0000111101101111.
Шестнадцатиричные числа намного легче читать, чем двоичные.
После небольшой практики оказывается, что работать с ними намного
удобнее, чем с десятичными.
Адреса памяти и портов:
Теперь, когда Вы разобрались с шестнадцатиричными числами,
можно разбираться в системе, которой пользуется процессор при
адресации памяти. Во-первых, важно отметить, что имеется два типа
адресов: адреса памяти и адреса портов. Hомера адресов используе-
мые теми и другими совершенно не связаны; засылка значения в
ячейку памяти с алресом 2000 не имеет ничего общего с засылкой
значения в порт с адресом 2000. Доступ к портам осуществляется с
помощью инструкций INP и OUT в Бейсике и IN в OUT языке ассембле-
ра. Доступ к адресам памяти осуществляется в Бейсике инструкциями
PEEK и POKE, а в языке ассемблера инструкцией MOV. Имеется 65K
доступных адресов портов и 1024K доступных адресов памяти.
Поскольку процессор использует 16-битные регистры, то он нам-
ного быстрее вычисляет адреса памяти если они не превосходят по
длине 16 битов. Однако максимальное число, которое может содер-
жаться в 16 битах равно 65535. Мы будем представлять его как
четырехзначное шестнадцатиричное число FFFFH. Требуется еще 4
добавочных бита, чтобы представить такое большое число как мил-
лион (FFFFH), которому равен размер адресного пространства IBM PC
(AT может иметь доступ к еще большей памяти, используя виртуаль-
ную адресацию, не рассматриваемую здесь).
Процессор решает проблему адресации более чем 64K с помощью
16-битного указателя за счет разбиения памяти на сегменты. Сег-
ментом является любая непрерывная область памяти размером 64K;
при этом 16-битный указатель может указывать на любой байт внутри
него. Процессор хранит положение начала сегмента в мегабайтном
адресном простанстве и рассматривает 16-битные адреса, как смеще-
ния относительно этой точки. Hо как определить эту точку? Ответ
состоит в том, что второе двухбайтное значение используется для
отметки начала сегмента и это значение умножается на 16 ( = 4
битам) перед его использованием. Таким образом, если это сегмент-
ное значение равно 2, то, умножив его на 16, получим 32, и адреса
будут затем вычисляться как смещение относительно 32-го байта в
памяти. Если адрес в сегменте равен 7, то суммируя 32 и 7 полу-
чаем, что нам нужно обратиться к 39-му байту памяти, а не к 7-му.
Относительный адрес (или смещение) этого байта равен 7, а абсо-
лютный адрес - 39.
В Бейсике Вы можете установить сегментный адрес с помощью
оператора DEF SEG. Если Вы напишете оператор DEF SEG = 2, то
установите начало сегмента, к которому Вы будете обращаться на
32-й байт, как в предыдущем примере. Затем Вы можете использовать
операторы PEEK и POKE для чтения и записи отдельных байтов памя-
ти. Hапример, PEEK(7) прочитает седьмой байт с начала сегмента,
т.е. 39-й байт памяти.
Во многих местах этой книги мы ссылаемся на абсолютные адреса
памяти. Это необходимо, поскольку операционная система хранит
важную информацию в определенных местах. Абсолютные алреса приво-
дятся в виде 0000:0000, где первые 4 шестнадцатиричные цифры
указывают адрес сегмента, а вторые - относительный адрес (смеще-
ние). Вспоминая предыдущий пример, мы можем адресовать 39-й байт
памяти записав 0002:0007. Отметим, что тот же самый адрес может
быть записан в другом виде, если изменить значение сегментного
регистра, например, 0001:0017. Этот адрес можно представить также
в виде одного 5-значного шестнадцатиричного числа. Hапример,
видеобуфер начинается с адреса B000:0000, который можно записать
как B0000H. Отметим, что суффикс H опускается в специальной ад-
ресной нотации.
И последнее замечание относительно использования памяти. Kогда
число занимает два или более байтов, то младший байт этого числа
хранится в ячейке с меньшим адресом. Если целое число A48BH хра-
нится, начиная с ячейки 1000:0007, то ячейка 0007 содержит 8B, а
0008 - A4. Подобным образом, если вещественное число хранится в
памяти как F58CA98DH, то 8D будет храниться в ячейке с самым
младшим адресом, а F5 - с самым старшим.
Приложение Б. Битовые операции в Бейсике.
В Бейсике нельзя использовать числа в двоичной форме. Он расс-
матривает цепочку битов 11000000 как 11 миллионов, а не как 192.
Однако манипуляции с цепочками битов часто необходимы при прог-
раммировании, поскольку требуется читать и изменять содержимое
статусных байтов и статусных регистров.
В большинстве случаев к цепочкам битов применяются две логи-
ческие операции. Это операции ИЛИ (OR) и И (AND) и обе они дос-
тупны в Бейсике. Используемые по отдельности или в комбинации
они позволяют программе читать и устанавливать индивидуальные
биты байта. Обе эти операции бинарные, т.е. они применяются к
паре значений, давая в качестве результата третье, в точности как
обычные арифметические операции: Z = X OR Y. При использовании со
значениями байтной длины эти операции выполняются 8 раз, по разу
для каждого бита. ИЛИ проверяет бит 0 двух байтов и если этот бит
установлен хотя бы в одном байте, то бит 0 будет установлен и в
результирующем байте. Этот процесс выполняется и для остальных
семи пар битов. Операция И устанавливает бит результата только в
том случае, если оба бита были установлены, в противном случае