X.4. Т а б л и ц а п е р е м е н н ы х (VT) Непосредственно следующая за PIT таблица VT начинается с адреса, ука- занного в слове VARTAB, хранящегося по адресу &HF6C2 в области системных переменных (см. Приложение 2). Ее длина зависит от количества используе- мых переменных (скалярных и массивов) и их типов. Отметим, что переменные и массивы хранятся в порядке их создания. Будем говорить, что один программный объект "располагается в памяти в ы ш е другого", если адрес, с которого он расположен, больше. ┌───────────────────────────────────────────┐ │ Массивы хранятся в ы ш е переменных. │ └───────────────────────────────────────────┘ Это значит, что всякий раз, когда интерпретатор встречает новую скаляр- ную переменную, все массивы сдвигаются "в в е р х", чтобы высвободить про- странство. Это может значительно замедлить выполнение программы! ┌─────────────────────────────────────────────────────────────────────┐ │ Во избежание этого, описывайте все скалярные переменные и │ │ массивы в начале программы оператором DIM ! │ └─────────────────────────────────────────────────────────────────────┘ Теперь мы расскажем Вам о важной функции VARPTR, которая указывает ад- рес расположения данных в оперативной памяти. Ее синтаксис: ┌──────────────┐ │ VARPTR(γ) │, └──────────────┘ где: VARPTR("VARiable PoinTeR"-"указатель переменной") - служебное слово; γ - идентификатор ч и с л о в о й переменной. Функция VARPTR возвращает а д р е с X байта RAM, начиная с которого располагается значение переменной γ. Если переменная не существует, то выдается сообщение: "Illegal function call" . П р и м е р. Будьте бдительны! ─────────── 10 INPUT Z 20 PRINT VARPTR(Z) run run ? 0 ? ◀── Нажата клавиша "RETURN" -32743 Illegal function call in 20 Ok Ok Функцию VARPTR часто используют совместно с функцией PEEK и оператором POKE соответственно для просмотра или изменения значения переменной. X.4.1. Х р а н е н и е п р о с т ы х п е р е м е н н ы х Ты славно роешь землю, старый крот! Годишься в рудокопы. В.Шекспир. Гамлет Как уже неоднократно упоминалось, ц е л о е число кодируется в двух байтах. Меньший по адресу байт называется с т а р ш и м , больший по ад- ресу байт - м л а д ш и м. Однако, напомним Вам, что ┌─────────────────────────────────────────────────────────┐ │ процессор Z80 "хранит" младший байт "перед" старшим │. └─────────────────────────────────────────────────────────┘ Когда ц е л о ч и с л е н н а я переменная получает значение, процес- сор записывает в оперативную память следующие п я т ь байтов информации: 1) число 2 ("паспорт" VALTYPE), которое означает, что переменная явля- ется целочисленной (значение "паспорта" занимает два байта); 2) код ASCII первого символа имени переменной; 3) код ASCII второго символа имени (0, если имя состоит из одного сим- вола); 4) младший байт значения; 5) старший байт значения. ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ │ 1-й байт │ │ 2-й байт │ │ 3-й байт │ │ 4-й байт │ │ 5-й байт │ └─────▲─────┘ └─────▲─────┘ └─────▲─────┘ └─────▲─────┘ └─────▲─────┘ │ │ │ │ │ │ Код ASCII Код ASCII Младший байт Старший байт VALTYPE первого символа второго символа значения значения имени переменной имени переменной Оказывается, что адрес четвертого байта (младшего байта значения число- вой переменной) возвращает как раз переменная VARPTR! Кроме того,напомним, что "содержимое" байта с известным адресом может быть "прочитано"функцией PEEK. Перед тем как работать с нижеприведенным примером, во избежание расхож- дений в результатах не забудьте "почистить" память компьютера оператором CLEAR. П р и м е р 1. A%=356:PRINT HEX$(VARPTR(A%)) ───────────── 8006 Ok Вы получили шестнадцатеричный адрес младшего байта значения переменной. ┌───────────────┬────────────────────────────────────────────────────────┐ │? РЕЕК(&Н8003) │Этот адрес соответствует байту VALTYPE. Поэтому Вы долж-│ │ │ ны получить 2. │ │? РЕЕК(&Н8004) │Этот адрес соответствует первому символу имени перемен-│ │ │ной. Вы должны получить число 65, которое является ко-│ │ │ дом ASCII символа "А". │ │? PEEK(&H8005) │Получили число 0, поскольку имя переменной состоит лишь │ │ │ из одного символа. │ │? РЕЕК(&Н8006) │Этот адрес, возвращенный функцией VARPTR, соответствует │ │ │младшему байту значения. Должно быть получено число 100.│ │? РЕЕК(&Н8007) │ Этот адрес соответствует старшему байту значения. │ │ │ Вы, конечно же, получите число 1. │ └───────────────┴────────────────────────────────────────────────────────┘ Перепишем два последних значения в двоичной системе счисления: 100 = 0110 0100 1=0000 0001 ² ² А теперь примем во внимание инверсию порядка байтов: 0000 0001 0110 0100 ──▶ 356 ────▲──── ────▲──── 10 │ │ 1 100 И перед выполнением следующего примера не забудьте ввести в память ком- пьютера оператор CLEAR ! П р и м е р 2. A%=-356:PRINT HEX$(VARPTR(A%)) ───────────── 8006 Ok Полученный результат - это шестнадцатеричный адрес младшего байта. ┌───────────────┬────────────────────────────────────────────────────────┐ │ ? РЕЕК(&Н8003)│ Вы должны получить 2. │ │ ? РЕЕК(&Н8004)│Этот адрес соответствует первому символу имени перемен-│ │ │ ной. Вы должны получить 65 (код ASCII символа "A"). │ │ ? PEEK(&H8005)│ 0, т.к. имя переменной состоит лишь из одного символа.│ │ ? РЕЕК(&Н8006)│Этот адрес, возвращаемый функцией VARPTR, соответствует │ │ │ инвертированному младшему байту значения. │ │ │ Разумеется, Вы получите число 156. │ │ ? РЕЕК(&Н8007)│Этот адрес соответствует инвертированному старшему бай-│ │ │ ту значения. Вы получите число 254. │ └───────────────┴────────────────────────────────────────────────────────┘ Перепишем два последних значения в двоичной системе счисления: 156 = 1001 1100 254=1111 1110 ² ² Принимая во внимание инверсию порядка байтов, запишем их содержимое: 1111 1110 1001 1100 ────▲──── ────▲──── │ │ 254 156 А теперь учитывая, что двоичное число 1111111010011100 записано в до- полнительном коде, получим ² 1111 1110 1001 1100 ──▶ 0000 0001 0110 0011 ──▶ 0000 0001 0110 0011+ ² ² +1 = 0000 0001 0110 0100 = 101100100 = 356. Ура! ² ² Приведем схему расположения информации в памяти для простой числовой переменной о д и н а р н о й и д в о й н о й точности: Байт, адрес которого возвращает функция VARPTR() │ ┌───┬───┬───┬─▼─┬───┬───┬───┬───┐ │▧▧▧│▧▧▧│▧▧▧│∗∗∗│███│███│...│███│ └───┴───┴───┴───┴───┴───┴───┴───┘ Адреса──▶ X-3 X-2 X-1 X └───────▲···────┘ ▲ ───▲─── ▲ │ │ │ │ Значение │ │ Знак и порядок │ Идентификатор Тип переменной Прежде чем проверить работу ниже приведенной командной строки,наберите команду CLEAR. П р и м е р 3. AR!=22.3210E+4:PRINT HEX$(VARPTR(AR!)) ───────────── 8006 Ok Если Вы прочитаете адреса с &H8006-&H3=&H8003 по &H8006+&H3=&H8009, то обнаружите: &H8003 4 VALTYPE п е р е м е н н о й о д и н а р н о й точ- ности (ее значение занимает 4 байта) &H8004 65 Код ASCII символа "А" &H8005 82 Код ASCII символа "R" &H8006 70 = &B 0100 0110 Адрес &H8006 содержит порядок, который кодируется следующим образом: ┌───────────────────────────────────────┬──────────┐ │ Содержимое памяти по адресу &H8006 │ │ ├──────────────────┬────────────────────┤ Порядок │ │ Двоичное значение│ Десятичное значение│ │ ├──────────────────┼────────────────────┼──────────┤ │ 01 000000 │ 64 │ 0 │ │ 01 000001 │ 65 │ 1 │ │ 01 000010 │ 66 │ 2 │ │ ··· │ ··· │ ··· │ ──▶ 01 000110 │ 70 │ 6 ◀── │ ··· │ ··· │ ··· │ │ 01 111110 │ 126 │ 62 │ │ 01 111111 │ 127 │ 63 │ │ 00 111111 │ 63 │ - 1 │ │ 00 111110 │ 62 │ - 2 │ │ 00 111101 │ 61 │ - 3 │ │ ··· │ ··· │ ··· │ │ 00 000010 │ 2 │ -62 │ │ 00 000001 │ 1 │ -63 │ │ 00 000000 │ 0 │ -64 │ └────▲─────────────┴────────────────────┴──────────┘ └── Бит знака мантиссы Величина порядка задается формулой: ┌───────────────────────────────────────────────────────┐ │ Порядок = Двоичное значение 7 младших битов - 64 │ └───────────────────────────────────────────────────────┘ Первый бит байта содержит з н а к м а н т и с с ы , причем 0 соот- ветствует знаку "+", а 1 соответствует знаку "-". Продолжим наши исследования: &H8007 34 = &B 0010 0010 = 22 в двоично-десятичной системе &H8008 50 = &B 0011 0010 = 32 в двоично-десятичной системе &H8009 16 = &B 0001 0000 = 10 в двоично-десятичной системе В трех последних байтах Вы, конечно же, "узнаете" число 225. Итак, три последних адреса содержат мантиссу, записанную в двоично-де- сятичном виде (.223210). ┌──────────────────────────────────────────────────────────────┐ │ Мантисса числа одинарной точности занимает 3 байта, которые │ │ позволяют закодировать 6 разрядов в двоично-десятичном виде.│ └──────────────────────────────────────────────────────────────┘ Перед тем как выполнить предлагаемые в примере действия,наберите и вве- дите в память компьютера оператор CLEAR. П р и м е р 4. AR#=-22.321054981117E-4:PRINT HEX$(VARPTR(AR#)) ───────────── 8006 Ok Если Вы прочитаете адреса с &H8006-&H3=&H8003 по &H8006+&H3=&H8009, то обнаружите: &H8003 8 VALTYPE переменной д в о й н о й точности (ее значение занимает 8 байтов) &H8004 65 Код ASCII символа "А" &H8005 82 Код ASCII символа "R" &H8006 190 = &B 1011 1110 ▲ └── Знак мантиссы отрицательный! Подсчитаем теперь величину порядка: print &B0111110-64 -2 Ok "Продолжим наши игры!": &H8007 34 = &B 0010 0010 = 22 в двоично-десятичной системе &H8008 50 = &B 0011 0010 = 32 в двоично-десятичной системе &H8009 16 = &B 0001 0000 = 10 в двоично-десятичной системе &H800A 84 = &B 0101 0100 = 54 в двоично-десятичной системе &H800B 152 = &B 1001 1000 = 98 в двоично-десятичной системе &H800C 17 = &B 0001 0001 = 11 в двоично-десятичной системе &H800D 23 = &B 0001 0111 = 17 в двоично-десятичной системе В семи последних байтах "узнается" число 0.22321054981117. ┌─────────────────────────────────────────────────────────────────┐ │ Мантисса числа двойной точности занимает 7 байтов, которые │ │ позволяют закодировать 14 разрядов в двоично-десятичном виде │ └─────────────────────────────────────────────────────────────────┘ Подведем и т о г и всему сказанному о хранении числовых переменных. ┌─────────┬───────┬──────┬──────┬───────────────────────────────────────┐ │ Т и п │Значен.│Колич.│Номера│ Значение (система счисления) │ │ │VALTYPE│байтов│байтов│ │ ├─────────┼───────┼──────┼──────┼───────────────────────────────────────┤ │Ц е л а я│ 2 │ 5 │ -3 │ VALTYPE=2 (двоичная) │ │ │ │ ├──────┼───────────────────────────────────────┤ │ │ │ │ -2 │ Первый символ имени (код ASCII) │ │ │ │ ├──────┼───────────────────────────────────────┤ │ │ │ │ -1 │ Второй символ имени (код ASCII) │ │ "Содержимое" этого ├──────┼───────────────────────────────────────┤ │ байта возвращает ───▶│ 0 │ З н а ч е н и е (двоичная); │ │ функция VARPTR() │──────┤для записи отрицательных чисел применя-│ │ │ │ │ 1 │ ется двоичный дополнительный код; │ ├─────────┼───────┼──────┼──────┼───────────────────────────────────────┤ │Одинарной│ 4 │ 7 │ -3 │ VALTYPE=4 (двоичная) │ │ точности│ │ ├──────┼───────────────────────────────────────┤ │ │ │ │ -2 │ Первый символ имени (код ASCII) │ │ │ │ ├──────┼───────────────────────────────────────┤ │ │ │ │ -1 │ Второй символ имени (код ASCII) │ │ "Содержимое" этого ├──────┼───────────────────────────────────────┤ │ байта возвращает ───▶│ 0 │ Порядок и знак (знак в первом бите) │ │ функция VARPTR() │ │ (двоичная) │ │ │ │ │ │ Величина порядка = двоичное значение │ │ │ │ │ │ семи последних битов - 64 │ │ │ │ ├──────┼───────────────────────────────────────┤ │ │ │ │ 1÷3 │ М а н т и с с а (двоично-десятичная) │ ├─────────┼───────┼──────┼──────┼───────────────────────────────────────┤ │ Двойной │ 8 │ 11 │ -3 │ VALTYPE = 8 (двоичная) │ │ точности│ │ ├──────┼───────────────────────────────────────┤ │ │ │ │ -2 │ Первый символ имени (код ASCII) │ │ │ │ ├──────┼───────────────────────────────────────┤ │ │ │ │ -1 │ Второй символ имени (код ASCII) │ │ "Содержимое" этого ├──────┼───────────────────────────────────────┤ │ байта возвращает ───▶│ 0 │ Порядок и знак (знак в первом бите) │ │ функция VARPTR() │ │ (двоичная) │ │ │ │ │ │ Величина порядка = двоичное значение │ │ │ │ │ │ семи последних битов - 64 │ │ │ │ ├──────┼───────────────────────────────────────┤ │ │ │ │ 1÷7 │ М а н т и с с а (двоично-десятичная) │ └─────────┴───────┴──────┴──────┴───────────────────────────────────────┘ П р и м е р 5. Попробуйте самостоятельно в нем разобраться! ───────────── NEW Ok 10 INPUT"Введите число";A:PRINT"Попробуем 'собрать' его из памяти" 30 B=VARPTR(A):K$=RIGHT$("00000000"+BIN$(PEEK(B)),8) 50 IF MID$(K$,1,1)="1" THEN Z$="-." ELSE Z$="+." 60 FOR T=1 TO 7:Z$=Z$+RIGHT$("00"+HEX$(PEEK(B+T)),2):NEXT 70 Z$=Z$+"E" 80 IF MID$(K$,2,1)="1" THEN Z$=Z$+"+" ELSE Z$=Z$+"-" 90 U=VAL("&b"+MID$(K$,2,7))-64 100 C$=MID$(STR$(U),2):Z$=Z$+RIGHT$("00"+C$,2) 120 PRINT"Вот Ваше число:";Z$ run run Введите число? 0 Введите число? -23545e37 Попробуем 'собрать' его из памяти Попробуем 'собрать' его из памяти Вот Ваше число:+.00000000000000E-64 Вот Ваше число:-.23545000000000E+42 Ok Ok X.4.2. Х р а н е н и е э л е м е н т о в ч и с л о в ы х м а с с и в о в Что имеем - не храним; потерявши - плачем. Козьма Прутков Вначале мы расскажем Вам о том, как хранится в памяти ц е л о ч и с- л е н н ы й массив. Приведем схемы расположения информации в памяти для целочисленных чис- ловых массивов: α) о д н о м е р н ы й массив. Байт, адрес которого возвращает функция VARPTR()──┐ ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─▼─┬───┬───┬───┬─ │ │ │ │ │ │ &H1 │ │ │███│███│███│███│··· └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴───┴───┴───┴───┴─ X-8 X-7 X-6 X-5 X-4 X-3 X-2 X-1 └──▲───┘└──▲───┘ ▲ ─────▲───── ────▲──── ▲ ────▲──── │ │ │ │ │ │ │ Значение Значение │ │ Служебная инф. │ │ нулевого первого │ Идентификатор Для одномерного │ элемента элемента Тип массива массива Количество элементов в массиве β) д в у х м е р н ы й массив. Байт, адрес которого возвращает функция VARPTR()──┐ ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─▼─┬───┬─ │ │ │ │ │ │ &H2 │ │ │ │ │███│███│··· └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴───┴───┴─ X-10 X-9 X-8 X-7 X-6 X-5 X-4 X-3 X-2 X-1 └───▲───┘ ▲ ─────▲───── ─────▲───── ▲ ─────▲───── ─────▲───── │ │ │ │ │ │ │ Значение │ │ Служебная инф. │ Количество Количество элемента │ Идентификатор Для двухмерного столбцов строк (0,0) Тип массива массива γ) для э л е м е н т о в целочисленных числовых массивов. ┌─── Байт, адрес которого возвращает функция VARPTR() ┌─▼─┬───┬───┬───┐ │███│███│···│███│ └───┴───┴───┴───┘ └───────▲───────┘ │ Значение Не забыли ли Вы набрать и ввести в память компьютера оператор CLEAR? П р и м е р 4. DIM C5%(2):C5%(0)=32000:C5%(1)=13:C5%(2)=-4 ───────────── Ok print HEX$(VARPTR(C5%(0))-8) 8003 Ok ? РЕЕК(&Н8003) Этот адрес соответствует байту VALTYPE. Поэтому Вы долж- ны получить 2. ? РЕЕК(&Н8004) Этот адрес соответствует первому символу имени массива. Вы получите число 67, которое является кодом ASCII сим- вола "C". ? PEEK(&H8005) Этот адрес соответствует второму символу имени массива Вы получите число 53, которое является кодом ASCII символа "5". ? РЕЕК(&Н8006) 9 } Служебная информация ? РЕЕК(&Н8007) 0 ? PEEK(&H8008) Массив C5% - одномерный, поэтому мы получили 1. ? PEEK(&H8009) Мы получили 3 = 00000011 ? PEEK(&H800А) 0 = 00000000 ² ² Теперь можно найти количество элементов в массиве:00000000 00000011 =3 ² ? PEEK(&H800B) 0 = 00000000 ? PEEK(&H800C) 125 = 01111101 ² ² А тогда нулевой элемент массива равен: 01111101 00000000 = 32000 ² ? PEEK(&H800D) 13 = 00001101 ? PEEK(&H800E) 0 ² Добрались до первого элемента массива: 00000000 00001101 = 13 ² ? PEEK(&H800F) 252 = 11111100 ? PEEK(&H8010) 255 = 11111111 ² ² Далее 11111111 11111100 ──▶00000000 00000011 +1 ──▶00000000 00000100 =4 ² ² ² Приведем схемы расположения информации в памяти для нецелочисленных числовых массивов: α) о д н о м е р н ы й массив. Байт, адрес которого возвращает функция VARPTR() ──┐ ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬──▼──┬───┬───┬───┬── │ │ │ │ │ │ &H1 │ │ │ ∗∗∗ │███│···│███│··· └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴───┴───┴───┴── X-8 X-7 X-6 X-5 X-4 X-3 X-2 X-1 X └─────▲─────┘ ▲ ─────▲───── ────▲──── ▲ ────▲──── ▲ │ │ │ │ │ │ │ Значение нулевого │ │ Служебная инф. │ │ Знак и порядок элемента │ Идентификатор Для одномерного │ нулевого элемента Тип массива массива │ Количество элементов в массиве β) д в у х м е р н ы й массив. Байт, адрес которого возвращает функция VARPTR() ──┐ ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬──▼──┬─ │ │ │ │ │ │ &H2 │ │ │ │ │ ∗∗∗ │··· └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─ X-10 X-9 X-8 X-7 X-6 X-5 X-4 X-3 X-2 X-1 X ▲ ─────▲───── ─────▲───── ▲ ─────▲───── ─────▲───── ▲ │ │ │ │ │ │ │ │ │ Служебная инф. │ Количество │ Знак и порядок │ Идентификатор Для двухмерного столбцов │ нулевого элем. Тип массива массива Количество строк γ) для э л е м е н т о в нецелочисленных числовых массивов. ┌─── Байт, адрес которого возвращает функция VARPTR() ┌─▼─┬───┬───┬───┬───┐ │∗∗∗│███│███│···│███│ └───┴───┴───┴───┴───┘ X └───────▲───────┘ ▲ │ │ Значение Знак и порядок Думаем, что теперь Вы в состоянии самостоятельно разобраться с вопроса- ми, касающимися "хранения" в RAM многомерных (двухмерных, трехмерных и т.д.) вещественных числовых массивов!