\/d Г Л А В А X. УПРАВЛЕНИЕ РЕСУРСАМИ ПАМЯТИ MSX-КОМПЬЮТЕРОВ \/d- Мозг, хорошо устроенный, стоит больше, чем мозг, хорошо наполненный. М.Монтень X.1. К а р т а п а м я т и (д л я к о м п ь ю т е р о в MSX-1) Персональный MSX-компьютер имеет небольшой объем памяти - 96 Кбайтов для MSX-1 и 242 Кбайта для MSX-2. Поэтому полезной для пользователя оказы- ваются информация о распределении ресурсов памяти и сведения о наличии и объеме в ней свободных мест в любой момент времени. Общий объем памяти у компьютеров серии MSX-1 равен 96 Кбайтам. Здесь и далее мы будем рассматривать только 64 Кбайта, с которыми обычно и работа- ет основная масса пользователей. Взгляните на приведенный ниже рис.12 ... Вся память разбита на две основные части: XWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V ROM ("Read Only Memory" - "Постоянное Запоминающее Устройство") и V V RAM ("Random Access Memory" - "Оперативное Запоминающее Устройство")V ZWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ ROM содержит те программы и данные, которые "заложены" в компьютер при изготовлении. Вот почему он всегда выводит определенные сообщения при включении и способен "понимать" программу на языке MSX-BASIC. В ROM находится и н т е р п р е т а т о р - программа на машинном язы- ке, которая переводит один за другим операторы языка MSX-BASIC в програм- му на машинном языке, т.е. на е д и н с т в е н н о м языке, который по- нимает компьютер. С помощью этой программы компьютер проверяет синтаксис, выводит при необходимости сообщение об ошибке, переходит к следующему опе- ратору или возвращается в командный режим и так далее. Здесь же находятся подпрограммы управления клавиатурой и экраном, кото- рые составляют э к р а н н ы й р е д а к т о р MSX-BASIC. ROM в основном разделена на две части: 1) подпрограммы BIOS ("Basic Input-Output System"); 2) другие подпрограммы. Так, например, при включении компьютера насту- пает небольшая пауза; в этот момент происходят различные инициализации эк- рана дисплея (установка определенного режима SCREEN, установка ширины эк- рана WIDTH и др.). Это происходит оттого,что "зашитые" в ROM подпрограммы инициализации "посылают" определенную информацию в рабочую область RAM, разговор о которой еще пойдет впереди. Подпрограммы BIOS осуществляют переход к другим подпрограммам. Они на- поминают последовательность операторов GOSUB, которую можно увидеть на первом уровне хорошо структурированной программы на MSX-BASIC. Подпрограм- мы BIOS расположены по одним и тем же адресам ROM независимо от версии MSX-BASIC и осуществляют переход к другим подпрограммам, положение кото- рых может быть изменено. В противоположность ROM RAM не сохраняет информацию при выключении ком- пьютера. Сейчас мы расскажем Вам о структуре RAM. "Верхушка" памяти (она изображена в н и ж н е й части таблицы) за- нята р а б о ч е й о б л а с т ь ю, которая состоит из: ═) таблицы системных переменных, ║) таблицы ловушек ("Hooks Table"). "Нижняя" область памяти (она изображена в в е р х н е й части таблицы) занята: 1) текстом программы ("Program Instruction Table", PIT); 2) таблицей переменных ("Variable Table", VT). VT содержит все перемен- ные, создаваемые в ходе выполнения программы; 3) таблицей массивов ("Array Variable Table"). Между "верхней" и "нижней" областями памяти располагаются: ═) свободная область ("Free Area"); ║) с т е к ("Stack Area"); стек содержит всю информацию, необходимую для выполнения программы. Например, именно здесь хранится адрес тех бай- тов PIT, которые содержат сведения о следующем выполняемом операторе Ва- шей программы; ╖) с т р о к о в а я область ("Character String Area"); по умолчанию для нее отводится 200 байтов, однако размеры этой области можно менять оператором CLEAR (см. раздел X.7.); ╚) б л о к у п р а в л е н и я файлами ("Files Control Block"). Если в Вашей программе присутствует оператор MAXFILES= (напомним, что он задает максимальное количество одновременно открытых файлов), то для каждого файла автоматически резервируется 267-байтное пространство для осуществления обмена информацией с файловыми устройствами. &H0000 OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO O▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄O O▄▄▄▄▄▄▄▄▄ ROM (Интерпретатор MSX-BASIC) ▄▄▄▄▄▄▄O O▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄O W W W WOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOW W W W &H8000 V V TXTTAB V Программа на языке BASIC V V V ("Program Instruction Table", PIT) V █ V V VW W W W W W W W W W W W W W W W W W W W W W W W VW W W W V V VARTAB V Простые переменные ("Variable Table") V V V V █ VW W W W W W W W W W W W W W W W W W W W W W W W VW W W W V Массивы ("Array Variable Table") V ARYTAB V V V VW W W W W W W W W W W W W W W W W W W W W W W W V █ V V V С в о б о д н а я о б л а с т ь ("Free Area")V V V R A M VW W W W W W W W W W W W W W W W W W W W W W W W S V V ▌ V Стек ("Stack Area") V V V V STKTOP VW W W W W W W W W W W W W W W W W W W W W W W W SW W W W V V ▌ V Строковая область ("Character String Area") V V V V FRETOP VW W W W W W W W W W W W W W W W W W W W W W W W SW W W W V V FILTAB V Блок управления файлами ("Files Control Block")V V V V █ W W W W UWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWVW W W W &HF380 V Таблица системных переменных V HIMEM V ("System Variable Table") V V W W W W VW W W W W - - - - - - - - - - - - - - - - W W W V █ &HF3A9 V V V Таблица ловушек ("Hooks Table") V &HFFFF V V W W W W ZWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ Р и с. 12 Приведенная карта памяти справедлива и для компьютеров серии MSX-2. Но в отличие от компьютеров серии MSX-1 с объемом ROM в 32 Кбайта и RAM в 64 Кбайта, компьютеры серии MSX-2 имеют гораздо больший объем памяти (108 Кбайтов ROM и 134 Кбайта RAM). Спрашивается, где размещается эта память? XWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V Оказывается, вся память ПЭВМ разбита на блоки V V объемом по 64 Кбайта, называемые с л о т а м и ! V ZWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ Однако рассмотрение этого вопроса потребует от читателя дополнительных знаний, и поэтому мы рассмотрим его позднее (см. Приложение 1.8.2). Память разделена на ячейки (б а й т ы), каждая из которых имеет адрес, закодированный д в у м я байтами: А д р е с я ч е й к и (байта) XWWWWWWWWWWWWWWWWWRWWWWWWWWWWWWWWWWWY V 1 1 0 1 1 0 1 1 V 1 0 1 1 1 1 0 1 V ZWWWWWWWW▌WWWWWWWWQWWWWWWWW▌WWWWWWWW[ ZWW Б а й т ы WW[ Поэтому максимально большой адрес байта равен 256G&B11111111+&B11111111+1 = 256G&hFF+&HFF+1 = 65535+1 = 65536 , а следовательно, и обратиться можно не более чем к 65536 ячейкам памяти (подумайте, почему производится умножение именно на 256?). Говорят, что "объем непосредственно адресуемой памяти - 65536 байтов". X.2. Ф у н к ц и я PEEK и о п е р а т о р POKE Функция PEEK позволяет Вам "посмотреть" содержимое любой ячейки памяти в адресном пространстве MSX-компьютера. Ее общий вид: XWWWWWWWWWWWWWWWWWWWWWY V PEEK (а д р е с) V , ZWWWWWWWWWWWWWWWWWWWWW[ где: PEEK ("to peek"-"заглядывать") - служебное слово; а д р е с - арифметическое выражение, значение которого находится в диапазоне от &h0 до &hFFFF. Функция PEEK возвращает целое число в интервале от 0 до 255, содержаще- еся в проверяемой ячейке памяти. Например: 1) 10 WIDTH 7:? PEEK(&HF3B0) 2) 10 SCREEN 2:PSET(15,18):SCREEN0:PRINT run PEEK(&HFCB3);PEEK(&HFCB5) 7 run Ok 15 18 Ok В первом примере мы "попросили" компьютер вывести на экран содержимое ячейки с адресом &HF3B0 (в байте по этому адресу хранится значение систем- ной переменной - длины дисплейной строки). Во втором примере мы использо- вали информацию из таблицы адресов системных переменных (см.Приложение 2). Величину, возвращаемую функцией PEEK, можно интерпретировать как код символа, команду MSX-BASIC, номер строки, число, "часть" числа, "храняще- гося" в нескольких байтах, и т.д. В некоторых случаях правильную интерпре- тацию можно дать по контексту, однако, если должной уверенности нет, надо а н а л и з и р о в а т ь не только содержимое одной ячейки, но и содер- жимое ячеек, находящихся в ее "окрестности"! П р и м е р 1. 10 X=&H8000'"Заглянем" в память,начиная с адреса &H8001! WWWWWWWWWWWWW 20 X=X+1:Y=PEEK(X) 30 IF Y<32 THEN ?"V";:GOTO 20 ELSE ?CHR$(Y);" ";:GOTO20 Наличие условия Y<32 в строке 26 связано с тем, что существуют "непеча- таемые" символы, имеющие код ASCII, меньший 32. Поговорим теперь об очень полезном операторе POKE. Общий вид оператора: XWWWWWWWWWWWY V POKE A,D V ZWWWWWWWWWWW[ где: POKE ("to poke"-"помещать") - служебное слово; A - арифметическое выражение, значение которого находится в диапазо- не от &h8000 до &hFFFF; D - арифметическое выражение, значение которого принадлежит отрезку [0,255] (поскольку оно должно умещаться в один байт). Оператор POKE вычисляет значения выражений А и D и сохраняет значение D (которое должно помещаться в одном байте!) по адресу А. Обратите внима- ние на то, что значение А может оказаться о т р и ц а т е л ь н ы м! Если значение А не удовлетворяет ограничениям, то компьютер сообщит об ошибке: "Overflow" ("П е р е п о л н е н и е"), а если значение D, - то "Illegal function call" ("Н е п р а в и л ь н ы й в ы з о в ф у н к ц и и"). Вы можете использовать оператор POKE для: ═) модификации текста Вашей программы; ║) изменения значений переменных; ╖) размещения в RAM программы, написанной на машинном языке (ее запись производится п о б а й т н о). Более того, этот оператор позволяет Вам экспериментировать с "подвалом" компьютера (рабочей областью). Но делайте так только в том случае, если Вы понимаете, что за этим последует! П р и м е р 2. Сравните результаты работы двух программ: WWWWWWWWWWWWW 10 SCREEN 1:PRINT"A" 10 SCREEN 1:PRINT"A" 20 WIDTH 10 20 POKE &HF3B0,10 X.3. Т а б л и ц а п р о г р а м м н ы х к о м а н д (PIT) Таблица PIT обычно начинается по адресу &H8000. Однако ее можно "сдви- нуть", изменив значение системной переменной TXTTAB в таблице системных переменных. П р и м е р 1. Для помещения PIT с адреса &HА000 достаточно выполнить WWWWWWWWWWWWW следующую программу: 5 'Адрес &HА001,находящийся в двух ячейках с номерами,начиная с &hF676 (слове TXTTAB (&HF676)),определяет место,с которого начнется текст про- граммы 10 POKE &HF676,&H01 'Заполнение младшего байта слова TXTTAB 20 POKE &HF677,&HA0 'Заполнение старшего байта слова TXTTAB 30 POKE &HA000,0 'Первый байт PIT(&HA000) должен быть нулевым! 40 NEW 'Стираем данную программу! Напомним Вам, что адрес &HА001 (как и любой другой!) размещается в двух байтах так: Адрес WW▐ &HF676 &HF677 ░WW Адрес младшего байта V V старшего байта XWWWWWVWWWWWWWWWVWWWWY Содержимое V XWWW█WWY XWWW█WWY V Содержимое младшего ░WWWWW &Н01 V V &HА0 WWWWW▐ старшего байта V ZWWWWWW[ ZWWWWWW[ V байта ZWWWWWWWWWWWWWWWWWWWW[ Слово TXTTAB Очевидно, что в результате этих "манипуляций" размер свободной области уменьшится на &H2000 байтов (&HA000-&H8000=&H2000), и область, расположен- ная между &H8000 и началом PIT, будет защищена от "вторжения" программ на MSX-BASIC и, следовательно, "б е з о п а с н а" для программ на машинном языке. Ясно, что величина PIT зависит от размера текста программы. После выполнения данной программы нажмите кнопку сброса "RESET". А теперь мы расскажем Вам о том, как хранится программа, написанная на языке MSX-BASIC в PIT. XWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V Все строки программы на MSX BASIC начинаются с д в у х б а й т - V V н о г о указателя. V V За этим указателем идут д в а байта, содержащие номер строки. V V Затем идет текст строки с последующим нулевым байтом. V V За последней строкой следуют д в а дополнительных нулевых байта, V V адрес которых находится в указателе последней строки программы. V V Цифры и зарезервированные служебные слова записываются во внутрен- V V нем коде (один или два байта на слово, цифру) V V Для остального текста используется код ASCII. V ZWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ П р и м е р 2. Введем в память следующую короткую программу: WWWWWWWWWWWWW 10 B=5 20 END Теперь прочитаем, что же реально содержится в PIT, используя в непо- средственном режиме команду PRINT HEX$(PEEK(A)) , где значение переменной А (адреса) изменяется от &H8000 до &H8010. Вы обнаружите: XWWWWWWWWWWWRWWWWWWWWWWWWWRWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V Значение АVHEX$(PEEK(A))V К о м м е н т а р и и V TWWWWWWWWWWWUWWWWWWWWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V 8000 V 0 V Первый байт PIT всегда нулевой V TWWWWWWWWWWWUWWWWWWWWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS VOO 8001 OOV 09 V Указатель первой строки "говорит" нам, что V VOO 8002 OOV 80 V указатель следующей строки находится по ад- V VO O O O O OV V ресу &Н8009 V TWWWWWWWWWWWUWWWWWWWWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V 8003 V А V Номер первой строки &H000А = 10 V V 8004 V 0 V V TWWWWWWWWWWWUWWWWWWWWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V 8005 V 42 V Шестнадцатеричный код ASCII буквы "B" V V 8006 V EF V Внутренний код знака равенства V V 8007 V 16 V Внутренний код цифры 5 V V 8008 V 0 V Конец первой строки V TWWWWWWWWWWWUWWWWWWWWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS VOO 8009 OOV 0F V Указатель второй строки показывает, что V VOO 800A OOV 80 V указатель следующей строки находится по V VOOOOOOOOOOOV V адресу &H800F V TWWWWWWWWWWWUWWWWWWWWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V 800B V 14 V Номер второй строки &H0014 = 20 V V 800C V 00 V V TWWWWWWWWWWWUWWWWWWWWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V 800D V 81 V Внутренний код оператора END V V 800E V 0 V Конец второй строки V TWWWWWWWWWWWUWWWWWWWWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V 800F V 0 V Конец программы V V 8010 V 0 V V ZWWWWWWWWWWWQWWWWWWWWWWWWWQWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ Теперь, надеемся, Вам стало ясно, как можно изменить программу с помо- щью оператора POKE. Попробуйте выполнить следующее: POKE &H8005,&H41 '41 - шестнадцатеричный код ASCII буквы "A" POKE &H8007,&H17 '17 W внутренний код цифры "6" А теперь наберите команду LIST, затем нажмите клавишу░W[ и ...: 10 A=6 20 END П р и м е р 3. Теперь Вам ясно,что "инструкции" PEEK и POKE таят в се- WWWWWWWWWWWWW бе поистине безграничные возможности. По существу, они позволяют нам распоряжаться памятью компьютера по своему усмотрению. Например, они позволяют нам при желании подшутить над компьютером: ес- ли известно, где хранится программа, то мы можем сделать так, что после одной из строк программы окажется строка с м е н ь ш и м номером. Пусть исходная программа имеет вид: 10 PRINT 4 20 PRINT 2 Вам, конечно, уже известно, что строки программы на языке MSX-BASIC на- чинаются с двухбайтного указателя, за которым следуют два байта, содержа- щие номер строки. Поэтому вначале выполним команду: PRINT HEX$(PEEK(&H8002));" ";HEX$(PEEK(&H8001)) 80 9 Ok Таким образом, указатель следующей (с номером 20) строки располагается в ячейках с адресами &H8009 и &H800A, а, следовательно, номер второй стро- ки находится в ячейках с адресами &H800B и &H800C . Проверим этот факт: PRINT HEX$(PEEK(&H800C));" ";HEX$(PEEK(&H800B)) XWW▐ PRINT &H14 0 14 WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ 20 Ok Ok А теперь: POKE &H800B,1 Ok list 10 PRINT 4 1 PRINT 2 Ok Программа действует, но строку с номером 1 нельзя ни стереть, ни испра- вить. Вы можете написать еще одну 1-ю строку и даже новую 20-ю строку! П р и м е р 4. Введем в память короткую программу: WWWWWWWWWWWWW 10 FOR AB=-2.23227 TO 7 STEP 32.671533782376# Теперь "просмотрим" содержимое PIT, используя в непосредственном режи- ме простейшие команды: A=GGG: PRINT HEX$(PEEK(A)) , где значение переменной А (адреса) изменяется от &H8000 до &H8020. Мы обнаружим массу интересных вещей: XWWWWWWWWWWWRWWWWWWWWWWWWWRWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V Значение АVHEX$(PEEK(A))V К о м м е н т а р и и V TWWWWWWWWWWWUWWWWWWWWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V 8000 V 0 V Первый байт PIT всегда нулевой V TWWWWWWWWWWWUWWWWWWWWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS VOO 8001 OOV 21 V Указатель первой строки "говорит" нам, что V VOO 8002 OOV 80 V указатель следующей строки находится по ад- V VO O O O O OV V ресу &Н8021 V TWWWWWWWWWWWUWWWWWWWWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V 8003 V А V Номер первой строки &H000А = 10 V V 8004 V 0 V V TWWWWWWWWWWWUWWWWWWWWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V 8005 V 82 V Код служебного слова FOR V V 8006 V 20 V Внутренний код символа "пробел" V V 8007 V 41 V Шестнадцатеричный код ASCII буквы "A" V V 8008 V 42 V Шестнадцатеричный код ASCII буквы "B" V V 8009 V EF V Внутренний код символа "=" V V 800A V F2 V Внутренний код символа "-" V V 800B V 1D VУказатель на тип одинарной точности (знак"!")V V 800C V 41 V Вторая цифра числа указывает на порядок V V V V минимального значения параметра цикла V V 800D V 22 V 1 и 2-я цифры мантиссы минимального зна- V V V V чения параметра цикла V V 800E V 32 V 3 и 4-я цифры мантиссы минимального зна- V V V V чения параметра цикла V V 800F V 27 V 5 и 6-я цифры мантиссы минимального зна- V V V V чения параметра цикла V V 8010 V 20 V Внутренний код символа "пробел" V V 8011 V D9 V Код служебного слова TO V V 8012 V 20 V Внутренний код символа "пробел" V V 8013 V 18 V Внутренний код символа "7" V V 8014 V 20 V Внутренний код символа "пробел" V V 8015 V DC V Код служебного слова STEP V V 8016 V 20 V Внутренний код символа "пробел" V V 8017 V 1F V Указатель на тип двойная точность (знак #) V V 8018 V 42 V Вторая цифра числа указывает на порядок шагаV V 8019 V 32 V 1 и 2-я цифры мантиссы шага V V 801А V 67 V 3 и 4-я цифры мантиссы шага V V 801B V 15 V 5 и 6-я цифры мантиссы шага V V 801C V 33 V 7 и 8-я цифры мантиссы шага V V 801D V 78 V 9 и 10-я цифры мантиссы шага V V 801E V 23 V 11 и 12-я цифры мантиссы шага V V 801F V 76 V 13 и 14-я цифры мантиссы шага V V 8020 V 0 V Конец строки V V 8021 V 0 V Конец программы V ZWWWWWWWWWWWQWWWWWWWWWWWWWQWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ Однако не пытайтесь изменять с помощью оператора POKE длину строки или путать указатели: результат будет к а т a с т р о ф и ч е с к и м! Если Вы хотите защитить свою программу от "постороннего взгляда" (ко- манды LIST), то примените в непосредственном режиме команду: POKE &H8001,1 Ok (разумеется, Ваша программа должна располагаться с адреса &H8000). Ну а если Вы нечаянно нажали RESET, - не спешите отчаиваться! Вашу про- грамму еще можно спасти. Это очень легко сделать, набрав ту же команду POKE &H8001,1 а затем auto . На экране появятся строки: 10* 20* и так далее ... Строки с "*" - спасенные. Теперь достаточно "скомандовать": LIST и ..., о, чудо! Но это еще не все! Оказывается, спасены и все строки между теми, номера которых не делятся нацело на 10! Если же Вы захотите защитить свою программу от запуска (команды RUN), то примените в непосредственном режиме команду: POKE &H8000,1 Ok (разумеется, Ваша программа должна располагаться с адреса &H8000).  X.4. Т а б л и ц а п е р е м е н н ы х (VT) Непосредственно следующая за PIT таблица VT начинается с адреса, ука- занного в слове VARTAB, хранящегося по адресу &HF6C2 в области системных переменных (см. Приложение 2). Ее длина зависит от количества используе- мых переменных (скалярных и массивов) и их типов. Отметим, что переменные и массивы хранятся в порядке их создания. Будем говорить, что один программный объект "располагается в памяти в ы ш е другого", если адрес, с которого он расположен, больше. XWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V Массивы хранятся в ы ш е переменных. V ZWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ Это значит, что всякий раз, когда интерпретатор встречает новую скаляр- ную переменную, все массивы сдвигаются "в в е р х", чтобы высвободить про- странство. Это может значительно замедлить выполнение программы! XWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V Во избежание этого, описывайте все скалярные переменные и V V массивы в начале программы оператором DIM ! V ZWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ Теперь мы расскажем Вам о важной функции VARPTR, которая указывает ад- рес расположения данных в оперативной памяти. Ее синтаксис: XWWWWWWWWWWWWWWY V VARPTR(╖) V, ZWWWWWWWWWWWWWW[ где: VARPTR("VARiable PoinTeR"-"указатель переменной") - служебное слово; ╖ - идентификатор ч и с л о в о й переменной. Функция VARPTR возвращает а д р е с X байта RAM, начиная с которого располагается значение переменной ╖. Если переменная не существует, то выдается сообщение: "Illegal function call" . П р и м е р. Будьте бдительны! WWWWWWWWWWW 10 INPUT Z 20 PRINT VARPTR(Z) run run ? 0 ? ░WW Нажата клавиша "RETURN" -32743 Illegal function call in 20 Ok Ok Функцию VARPTR часто используют совместно с функцией PEEK и оператором POKE соответственно для просмотра или изменения значения переменной. X.4.1. Х р а н е н и е п р о с т ы х п е р е м е н н ы х Ты славно роешь землю, старый крот! Годишься в рудокопы. У.Шекспир. Гамлет Как уже неоднократно упоминалось, ц е л о е число кодируется в двух байтах. Меньший по адресу байт называется с т а р ш и м , больший по ад- ресу байт - м л а д ш и м. Однако напомним Вам, что XWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V процессор Z80 "хранит" младший байт "перед" старшим V. ZWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ Когда ц е л о ч и с л е н н а я переменная получает значение, процес- сор записывает в оперативную память следующие п я т ь байтов информации: 1) число 2 ("паспорт" VALTYPE), которое означает, что переменная явля- ется целочисленной (значение "паспорта" занимает два байта); 2) код ASCII первого символа имени переменной; 3) код ASCII второго символа имени (0, если имя состоит из одного сим- вола); 4) младший байт значения; 5) старший байт значения. XWWWWWWWWWWWY XWWWWWWWWWWWY XWWWWWWWWWWWY XWWWWWWWWWWWY XWWWWWWWWWWWY V 1-й байт V V 2-й байт V V 3-й байт V V 4-й байт V V 5-й байт V ZWWWWW▌WWWWW[ ZWWWWW▌WWWWW[ ZWWWWW▌WWWWW[ ZWWWWW▌WWWWW[ ZWWWWW▌WWWWW[ V V V V V V Код ASCII Код ASCII Младший байт Старший байт VALTYPE первого символа второго символа значения значения имени переменной имени переменной Оказывается, что адрес четвертого байта (младшего байта значения число- вой переменной) возвращает как раз переменная VARPTR! Кроме того,напомним, что "содержимое" байта с известным адресом может быть "прочитано"функцией PEEK. Перед тем как работать с нижеприведенным примером, во избежание расхож- дений в результатах не забудьте "почистить" память компьютера оператором CLEAR. П р и м е р 1. A%=356:PRINT HEX$(VARPTR(A%)) WWWWWWWWWWWWW 8006 Ok Вы получили шестнадцатеричный адрес младшего байта значения переменной. XWWWWWWWWWWWWWWWRWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V? РЕЕК(&Н8003) VЭтот адрес соответствует байту VALTYPE. Поэтому Вы долж-V V V ны получить 2. V V? РЕЕК(&Н8004) VЭтот адрес соответствует первому символу имени перемен-V V Vной. Вы должны получить число 65, которое является ко-V V V дом ASCII символа "А". V V? PEEK(&H8005) VПолучили число 0, поскольку имя переменной состоит лишь V V V из одного символа. V V? РЕЕК(&Н8006) VЭтот адрес, возвращенный функцией VARPTR, соответствует V V Vмладшему байту значения. Должно быть получено число 100.V V? РЕЕК(&Н8007) V Этот адрес соответствует старшему байту значения. V V V Вы, конечно же, получите число 1. V ZWWWWWWWWWWWWWWWQWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ Перепишем два последних значения в двоичной системе счисления: 100 = 0110 0100 1=0000 0001 ╫ ╫ А теперь примем во внимание инверсию порядка байтов: 0000 0001 0110 0100 WW▐ 356 WWWW▌WWWW WWWW▌WWWW 10 V V 1 100 И перед выполнением следующего примера не забудьте ввести в память ком- пьютера оператор CLEAR ! П р и м е р 2. A%=-356:PRINT HEX$(VARPTR(A%)) WWWWWWWWWWWWW 8006 Ok Полученный результат - это шестнадцатеричный адрес младшего байта. XWWWWWWWWWWWWWWWRWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V ? РЕЕК(&Н8003)V Вы должны получить 2. V V ? РЕЕК(&Н8004)VЭтот адрес соответствует первому символу имени перемен-V V V ной. Вы должны получить 65 (код ASCII символа "A"). V V ? PEEK(&H8005)V 0, т.к. имя переменной состоит лишь из одного символа.V V ? РЕЕК(&Н8006)VЭтот адрес, возвращаемый функцией VARPTR, соответствует V V V инвертированному младшему байту значения. V V V Разумеется, Вы получите число 156. V V ? РЕЕК(&Н8007)VЭтот адрес соответствует инвертированному старшему бай-V V V ту значения. Вы получите число 254. V ZWWWWWWWWWWWWWWWQWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ Перепишем два последних значения в двоичной системе счисления: 156 = 1001 1100 254=1111 1110 ╫ ╫ Принимая во внимание инверсию порядка байтов, запишем их содержимое: 1111 1110 1001 1100 WWWW▌WWWW WWWW▌WWWW V V 254 156 А теперь учитывая, что двоичное число 1111111010011100 записано в до- полнительном коде, получим ╫ 1111 1110 1001 1100 WW▐ 0000 0001 0110 0011 WW▐ 0000 0001 0110 0011+ ╫ ╫ +1 = 0000 0001 0110 0100 = 101100100 = 356. Ура! ╫ ╫ Приведем схему расположения информации в памяти для простой числовой переменной о д и н а р н о й и д в о й н о й точности: Байт, адрес которого возвращает функция VARPTR() V XWWWRWWWRWWWRW█WRWWWRWWWRWWWRWWWY V▄▄▄V▄▄▄V▄▄▄VOOOV⌡⌡⌡V⌡⌡⌡V...V⌡⌡⌡V ZWWWQWWWQWWWQWWWQWWWQWWWQWWWQWWW[ АдресаWW▐ X-3 X-2 X-1 X ZWWWWWWW▌GGGWWWW[ ▌ WWW▌WWW ▌ V V V V Значение V V Знак и порядок V Идентификатор Тип переменной Прежде чем проверить работу ниже приведенной командной строки,наберите команду CLEAR. П р и м е р 3. AR!=22.3210E+4:PRINT HEX$(VARPTR(AR!)) WWWWWWWWWWWWW 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 содержит порядок, который кодируется следующим образом: XWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWRWWWWWWWWWWY V Содержимое памяти по адресу &H8006 V V TWWWWWWWWWWWWWWWWWWRWWWWWWWWWWWWWWWWWWWWS Порядок V V Двоичное значениеV Десятичное значениеV V TWWWWWWWWWWWWWWWWWWUWWWWWWWWWWWWWWWWWWWWUWWWWWWWWWWS V 01 000000 V 64 V 0 V V 01 000001 V 65 V 1 V V GGG V GGG V GGG V WW▐ 01 000110 V 70 V 6 ░WW V GGG V GGG V GGG V V 01 111111 V 127 V 63 V V 00 111111 V 63 V - 1 V V 00 111110 V 62 V - 2 V V GGG V GGG V GGG V V 00 000001 V 1 V -63 V V 00 000000 V 0 V -64 V ZWWWW▌WWWWWWWWWWWWWQWWWWWWWWWWWWWWWWWWWWQWWWWWWWWWW[ ZWW Бит знака мантиссы Величина порядка задается формулой: XWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V Порядок = Двоичное значение 7 младших битов - 64 V ZWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ Первый бит байта содержит з н а к м а н т и с с ы , причем 0 соот- ветствует знаку "+", а 1 соответствует знаку "-". Продолжим наши исследования: &H8007 34 = &B 0010 0010 = 22 в двоично-десятичной системе &H8008 50 = &B 0011 0010 = 32 в двоично-десятичной системе &H8009 16 = &B 0001 0000 = 10 в двоично-десятичной системе В трех последних байтах Вы, конечно же, "узнаете" число 225. Итак, три последних адреса содержат мантиссу, записанную в двоично-де- сятичном виде (.223210). XWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V Мантисса числа одинарной точности занимает 3 байта, которые V V позволяют закодировать 6 разрядов в двоично-десятичном виде.V ZWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ Перед тем как выполнить предлагаемые в примере действия,наберите и вве- дите в память компьютера оператор CLEAR. П р и м е р 4. AR#=-22.321054981117E-4:PRINT HEX$(VARPTR(AR#)) WWWWWWWWWWWWW 8006 Ok Если Вы прочитаете адреса с &H8006-&H3=&H8003 по &H8006+&H3=&H8009, то обнаружите: &H8003 8 VALTYPE переменной д в о й н о й точности (ее значение занимает 8 байтов) &H8004 65 Код ASCII символа "А" &H8005 82 Код ASCII символа "R" &H8006 190 = &B 1011 1110 ▌ ZWW Знак мантиссы отрицательный! Подсчитаем теперь величину порядка: 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. XWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V Мантисса числа двойной точности занимает 7 байтов, которые V V позволяют закодировать 14 разрядов в двоично-десятичном виде V ZWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ Подведем и т о г и всему сказанному о хранении числовых переменных. XWWWWWWWWWRWWWWWWWRWWWWWWRWWWWWWRWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V Т и п VЗначен.VКолич.VНомераV Значение (система счисления) V V VVALTYPEVбайтовVбайтовV V TWWWWWWWWWUWWWWWWWUWWWWWWUWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS VЦ е л а яV 2 V 5 V -3 V VALTYPE=2 (двоичная) V V V V TWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V V V V -2 V Первый символ имени (код ASCII) V V V V TWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V V V V -1 V Второй символ имени (код ASCII) V V "Содержимое" этого TWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V байта возвращает WWW▐V 0 V З н а ч е н и е (двоичная); V V функция VARPTR() VWWWWWWSдля записи отрицательных чисел применя-V V V V V 1 V ется двоичный дополнительный код; V TWWWWWWWWWUWWWWWWWUWWWWWWUWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS VОдинарнойV 4 V 7 V -3 V VALTYPE=4 (двоичная) V V точностиV V TWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V V V V -2 V Первый символ имени (код ASCII) V V V V TWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V V V V -1 V Второй символ имени (код ASCII) V V "Содержимое" этого TWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V байта возвращает WWW▐V 0 V Порядок и знак (знак в первом бите) V V функция VARPTR() V V (двоичная) V V V V V V Величина порядка = двоичному значению V V V V V V семи последних битов - 64 V V V V TWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V V V V 1╤3 V М а н т и с с а (двоично-десятичная) V TWWWWWWWWWUWWWWWWWUWWWWWWUWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V Двойной V 8 V 11 V -3 V VALTYPE = 8 (двоичная) V V точностиV V TWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V V V V -2 V Первый символ имени (код ASCII) V V V V TWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V V V V -1 V Второй символ имени (код ASCII) V V "Содержимое" этого TWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V байта возвращает WWW▐V 0 V Порядок и знак (знак в первом бите) V V функция VARPTR() V V (двоичная) V V V V V V Величина порядка = двоичному значению V V V V V V семи последних битов - 64 V V V V TWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V V V V 1╤7 V М а н т и с с а (двоично-десятичная) V ZWWWWWWWWWQWWWWWWWQWWWWWWQWWWWWWQWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ П р и м е р 5. Попробуйте самостоятельно в нем разобраться! WWWWWWWWWWWWW 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()WWY XWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRW█WRWWWRWWWRWWWRW V V V V V V &H1 V V V⌡⌡⌡V⌡⌡⌡V⌡⌡⌡V⌡⌡⌡VGGG ZWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWQWWWQWWWQWWWQW X-8 X-7 X-6 X-5 X-4 X-3 X-2 X-1 ZWW▌WWW[ZWW▌WWW[ ▌ WWWWW▌WWWWW WWWW▌WWWW ▌ WWWW▌WWWW V V V V V V V Значение Значение V V Служебная инф. V V нулевого первого V Идентификатор Для одномерного V элемента элемента Тип массива массива Количество элементов в массиве ║) д в у х м е р н ы й массив. Байт, адрес которого возвращает функция VARPTR()WWY XWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRW█WRWWWRW V V V V V V &H2 V V V V V⌡⌡⌡V⌡⌡⌡VGGG ZWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWQWWWQW X-10 X-9 X-8 X-7 X-6 X-5 X-4 X-3 X-2 X-1 ZWWW▌WWW[ ▌ WWWWW▌WWWWW WWWWW▌WWWWW ▌ WWWWW▌WWWWW WWWWW▌WWWWW V V V V V V V Значение V V Служебная инф. V Количество Количество элемента V Идентификатор Для двухмерного столбцов строк (0,0) Тип массива массива ╖) для э л е м е н т о в целочисленных числовых массивов. XWWW Байт, адрес которого возвращает функция VARPTR() XW█WRWWWRWWWRWWWY V⌡⌡⌡V⌡⌡⌡VGGGV⌡⌡⌡V ZWWWQWWWQWWWQWWW[ ZWWWWWWW▌WWWWWWW[ V Значение Не забыли ли Вы набрать и ввести в память компьютера оператор CLEAR? П р и м е р 4. DIM C5%(2):C5%(0)=32000:C5%(1)=13:C5%(2)=-4 WWWWWWWWWWWWW 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 WW▐00000000 00000011 +1 WW▐00000000 00000100 =4 ╫ ╫ ╫ Приведем схемы расположения информации в памяти для нецелочисленных числовых массивов: ═) о д н о м е р н ы й массив. Байт, адрес которого возвращает функция VARPTR() WWY XWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWW█WWRWWWRWWWRWWWRWW V V V V V V &H1 V V V OOO V⌡⌡⌡VGGGV⌡⌡⌡VGGG ZWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWQWWWQWWWQWW X-8 X-7 X-6 X-5 X-4 X-3 X-2 X-1 X ZWWWWW▌WWWWW[ ▌ WWWWW▌WWWWW WWWW▌WWWW ▌ WWWW▌WWWW ▌ V V V V V V V Значение нулевого V V Служебная инф. V V Знак и порядок элемента V Идентификатор Для одномерного V нулевого элемента Тип массива массива V Количество элементов в массиве ║) д в у х м е р н ы й массив. Байт, адрес которого возвращает функция VARPTR() WWY XWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWW█WWRW V V V V V V &H2 V V V V V OOO VGGG ZWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQW X-10 X-9 X-8 X-7 X-6 X-5 X-4 X-3 X-2 X-1 X ▌ WWWWW▌WWWWW WWWWW▌WWWWW ▌ WWWWW▌WWWWW WWWWW▌WWWWW ▌ V V V V V V V V V Служебная инф. V Количество V Знак и порядок V Идентификатор Для двухмерного столбцов V нулевого элем. Тип массива массива Количество строк ╖) для э л е м е н т о в нецелочисленных числовых массивов. XWWW Байт, адрес которого возвращает функция VARPTR() XW█WRWWWRWWWRWWWRWWWY VOOOV⌡⌡⌡V⌡⌡⌡VGGGV⌡⌡⌡V ZWWWQWWWQWWWQWWWQWWW[ X ZWWWWWWW▌WWWWWWW[ ▌ V V Значение Знак и порядок Думаем, что теперь Вы в состоянии самостоятельно разобраться с вопроса- ми, касающимися "хранения" в RAM многомерных (двухмерных, трехмерных и т.д.) вещественных числовых массивов!  X.5. С т е к А люди все роптали и роптали, А люди справедливости хотят: - Мы в очереди первые стояли, А те, кто сзади нас,- уже едят. В.Высоцкий С т е к (от англ. "stack" - "стог", "груда") - структура данных или устройство памяти для хранения наращиваемой и сокращаемой последователь- ности значений, в которой в любой момент доступен только последний член последовательности. Примером с т е к а является стопка книг на столе, в которой брать и класть книги можно только сверху ("Математический Энцик- лопедический Словарь"). С т е к используется как программой на MSX BASIC, так и подпрограмма- ми на машинном языке. "Вершина"стека указывается в слове STKTOP(&HF674). Его позиция зависит от размеров строкового пространства и блоков управления файлами, а также от второго аргумента оператора CLEAR (если этот оператор был выполнен). Если Вы хотите получить такие же результаты, как в последующем приме- ре, воспользуйтесь командой CLEAR! П р и м е р 1. Рассмотрим структуру расположения информации о цик- WWWWWWWWWWWWW ле FOR. ═) 10 FOR AB%=2 TO 7 STEP 15 'Оператора NEXT быть не должно! run Ok PRINT HEX$(PEEK(&HF675))+HEX$(PEEK(&HF674)) F0A0 Ok Адрес значения AB% в VT Адреса байтов V XW FF░WW для отрицательного шага V XWWWWRWWWWW█WWWWWRWW█WRWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWRW V V&H82V &H1BV &H80V &H1V&HFFV GGG V &HF V &H0 V &H7 V &H0 V &HA V&H0V V ZWWWWQWWWWWQWWWWWQWWWWQWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWQW ZW▐ X-27 X-26 X-25 X-24 X-23 X-10 X-9 X-8 X-7 X-6 X-5 ▌ WWWWWWWWWW ▌ ▌ WWWWW▌WWWW WWWWW▌WWWWW WWWW▌WWWW V V V V V V V Знак шага V Шаг (со знаком) Верхний Номер Код оператора FOR Тип параметра цикла предел строки XWW Адрес данного байта &HF0A0 WRWWWWRWWWWWRWWWRWW█WWY V&H15V &H80VGGGV &HFFV WQWWWWQWWWWWQWWWQWWWWW[ X-4 X-3 X WWWWW▌WWWWW ▌ V ZWW "Вершина" стека Адрес конца программной строки Перед выполнением следующего примера наберите команду CLEAR! ║) 10 FOR AB=2.7 TO 7 STEP-32.671533782376 run Ok PRINT HEX$(PEEK(&HF675))+HEX$(PEEK(&HF674)) F0A0 Ok Адрес параметра цикла AB в VT, увеличенный на 2 V XWWWWWRWWWW█WWWWWWRWWWWWRWWWWWWRWWWWWWRW V &H82V&H25V &H80 V &HFFV &H5 V &HC2 V ZWWWWWQWWWWQWWWWWWQWWWWWQWWWWWWQWWWWWWQW X-27 X-26 X-25 X-24 X-23 X-22 ░WW Адреса байтов ▌ WWWWWWWWWW ▌ ▌ ▌ V V V ZWW Порядок и знак шага Код оператора FOR V ZWWWWWWWWW Тип параметра цикла ZWWWWWWWWWWWWWWW Знак шага WRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRW V &H32V &H67V &H15V &H33V &H78V &H23V &H76V &H41V WQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQW X-21 X-20 X-19 X-18 X-17 X-16 X-15 X-14 WWWWWWWWWWWWWWWWWWWW▌WWWWWWWWWWWWWWWWWWW ▌ V V Мантисса шага Порядок и знак верхнего предела RWWWWWWRWWWWRWWWWRWWWWRWWWRWWWRWWWRWWWWWRWWWRWWWWWRWWWWWRWWWRWWWWY V &H70 V &H0V &H0V &H0V&H0V&H0V&H0V &HA V&H0V &H1FV &H80VGGGV&HFFV QWWWWWWQWWWWQWWWWQWWWWQWWWQWWWQWWWQWWWWWQWWWQWWWWWQWWWWWQWWWQWWWW[ X-13 X-12 X-11 X-10 X-9 X-8 X-7 X-6 X-5 X-4 X-3 X WWWWWWWWWWWWWWWWWWWW▌WWWWWWWWWWWWW WWW▌WWW WWWW▌WWWW V V V Мантисса верхнего предела V Адрес конца программной строки Номер строки оператора FOR Не забудьте о команде CLEAR! ╖) 10 FOR AB!=2.7 TO 7 STEP-32.6715 run Ok PRINT HEX$(PEEK(&HF675))+HEX$(PEEK(&HF674)) F0A0 Ok Адрес параметра цикла AB! в VT, увеличенный на 2 V XWWWWRWWWW█WWWWWWRWWWWWRWWWWWRWWWRWWWWRWWWWRWWWWWRWWWWWRWWWWWRWWWWWRW V&H82V&H22V &H80 V &HFFV &H1 VGGGV&HC2V&H32V &H67V &H15V &H0 V &H0 V ZWWWWQWWWWQWWWWWWQWWWWWQWWWWWQWWWQWWWWQWWWWQWWWWWQWWWWWQWWWWWQWWWWWQW X-27 X-26 X-25 X-24 X-23 X-14 X-13 X-12 X-11 X-10 X-9 ▌ WWWWWWWWWW W▌W W▌WW ▌ WWWWWWWW▌WWWWWWW WWWW▌WWWW V V V V V V Код FOR Знак шага V Знак и порядок шага V 3╤6-я цифры мантиссы V Мантисса шага верхнего Тип параметра цикла предела WRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWRWWWWWY V &H41V &H70V &HA V &H0 V &H1CV &H80VGGGV &HFFV WQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWQWWWWW[ X-8 X-7 X-6 X-5 X-4 X-3 X ▌ W▌W WWWWW▌WWWWW WWWWW▌WWWWW ▌ V V V V ZW"Вершина" стека V V V Адрес конца строки V V Номер строки V 1╤2-я цифры мантиссы верхнего предела Знак и порядок верхнего предела Хотите получить те же результаты - пользуйтесь оператором CLEAR! ╚) Пример под рубрикой: "Стек в действии!" 10 FOR AB%=2 TO 7:NEXT ░WW Цикл закрыт! 20 FOR I%=3 TO 9 ░WW Цикл не закрыт! run Ok PRINT HEX$(PEEK(&HF675))+HEX$(PEEK(&HF674)) F0A0 Ok Адрес текущего значения I% в VT Адреса байтов V XW FF░WW для отрицательного шага V XWWWWRWWWWW█WWWWWRWW█WRWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWRW V V&H82V &H2CV &H80V &H1V&HFFV GGG V &H1 V &H0 V &H9 V &H0 V &H14V&H0V V ZWWWWQWWWWWQWWWWWQWWWWQWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWQW ZW▐ X-27 X-26 X-25 X-24 X-23 X-10 X-9 X-8 X-7 X-6 X-5 ▌ WWWWWWWWWW ▌ ▌ WWWWW▌WWWW WWWWW▌WWWWW WWWW▌WWWW V V V V V V Код оператора FOR Знак шага V Шаг (со знаком) Верхний Номер Тип параметра цикла предел строки XWW Адрес этого байта: &HF0A0 WRWWWWRWWWWWRWWWRWW█WWY V&H21V &H80VGGGV &HFFV WQWWWWQWWWWWQWWWQWWWWW[ X-4 X-3 X WWWWW▌WWWWW ▌ V ZWW "Вершина" стека Адрес конца программной строки Отметим, что для версии MSX-Disk BASIC с отключенным дисководом B при нулевой длине строковой области максимальное число вложенных циклов равно 576. А теперь настала очередь оператора GOSUB... Тем не менее о команде CLEAR забывать не стоит! П р и м е р 2. 10 GOSUB 30:INPUT A WWWWWWWWWWWWW 20 'Просто комментарий! 30 'Еще один комментарий! run Ok PRINT HEX$(PEEK(&HF675))+HEX$(PEEK(&HF674)) F0A0 Ok XWW Адрес этого XWWWWRWWWWRWWWWRWWWWRWWWWRWWWWRWWWWRWWWWRWWWWRWW█WY байта: &HF0A0 V&H8DV &H0V &H0V&H0AV &H0V&h0AV&h80V &H0V &H0V&HFFV ZWWWWQWWWWQWWWWQWWWWQWWWWQWWWWQWWWWQWWWWQWWWWQWWWW[ X-9 X-8 X-7 X-6 X-5 X-4 X-3 X-2 X-1 X W▌W WWWW▌WWWW WWW▌WWWW W▌W V V V ZW Вершина стека Код GOSUB V Адрес следующего оператора Номер текущей строки П р и м е р 3. Работу этих двух программ Вы должны проверить на учени- WWWWWWWWWWWWW ческом компьютере. ═) 10 GOSUB 30:PRINT 1:END ║) 10 GOSUB 30:PRINT 1:END 20 PRINT 2:END 20 PRINT 2:END 30 'POKE (&HF0A0-4),&H10 30 POKE (&HF0A0-4),&H10'Адрес перехода 40 RETURN 40 RETURN 'изменен с адреса &H800A на run run 'адрес &H8010 1 2 Ok Ok X.6. Х р а н е н и е с т р о к о в ы х в е л и ч и н Функция VARPTR указывает адрес расположения строковых данных в опера- тивной памяти. Она имеет следующий синтаксис: XWWWWWWWWWWWWWWWWY V VARPTR(╖) V , ZWWWWWWWWWWWWWWWW[ где: VARPTR("VARiable PoinTeR"-"указатель переменной") - служебное слово; ╖ - идентификатор с т р о к о в о й переменной. Если переменная не существует, то выдается сообщение: "Illegal function call" . Функция VARPTR возвращает число X - а д р е с байта, находящегося на 3 позиции правее той, с которой располагается информация о переменной ╖. Пусть ╖ - простая строковая переменная. Изобразим "кусочек" памяти в окрестности байта с адресом X: XWW Байт, адрес которого возвращает функция XWWWRWWWRWWWRW█WRWWWRWWWY VARPTR(╖) V▄▄▄V▄▄▄V▄▄▄VOOOV▄▄▄V▄▄▄V ZWWWQWWWQWWWQWWWQWWWQWWW[ АдресаWW▐ X-3 X-2 X-1 X X+1 X+2 ▌ ZWWW▌WWW[ ▌ ZWWW▌WWW[ V V V ZW Ссылка на адрес в PIT или на адрес Тип переменной V ZWW Байт длины в строковой области Идентификатор Напомним Вам, что в программировании с с ы л к а - содержимое ячейки памяти, воспринимаемое как адрес некоторой другой ячейки. Указатели строковых переменных хранятся в VT. Они занимают 6 байтов, причем: ═) один байт содержит "паспорт" переменной VALTYPE (число 3); ║) два байта содержат имя строковой переменной; ╖) один байт содержит длину строки, возвращаемую функцией VARPTR(╖)(та- ким образом, обе функции LEN(A$) и PEEK(VARPTR(A$)) возвращают одно и то же значение). Приведем простой пример: 10 A$="карандаш":PRINT LEN(A$);PEEK(VARPTR(A$)) run 8 8 Ok ╚) следующие два байта указывают адрес первого байта строки. Если сим- вольная переменная создается явным присваиванием символьной константы, то указатель задает адрес этой константы в PIT.Лишь затем MSX-BASIC м о ж е т переслать значение этой символьной переменной в зарезервированное для нее строковое пространство. П р и м е р 1. "Сборка" значения строковой переменной A$ из памяти. WWWWWWWWWWWWW 5 CLEAR:INPUT A$:A$=A$+"":A=PEEK(VARPTR(A$)):PRINT A 30 B$="&H"+HEX$(PEEK(VARPTR(A$)+2))+HEX$(PEEK(VARPTR(A$)+1)):PRINT B$ 40 B=VAL(B$):BB=PEEK(B) 45 FOR I=0 TO A-1:PRINT CHR$(PEEK(B+I));:NEXT I:END run ? MSX 3 &HF163 MSX Ok П р и м е р 2. 10 A$="ABCD" WWWWWWWWWWWWW run Ok PRINT HEX$(VARPTR(A$)) 8014 ░WW Это адрес байта, содержащего длину A$ Ok Затем выполните команду PRINT PEEK(AD),где значение переменной AD изме- няется от &H8011 до &H8016. Вы получите: PRINT PEEK(&H8011) WW▐ 3 VALTYPE PRINT PEEK(&H8012) WW▐ 65 Код ASCII символа "A" PRINT PEEK(&H8013) WW▐ 0 Второй символ отсутствует PRINT PEEK(&H8014) WW▐ 4 Длина значения A$ PRINT PEEK(&H8015) WW▐ 9 = 0000 1001 WW▐ Адрес (младший байт) ╫ PRINT PEEK(&H8016) WW▐ 128 = 1000 0000 WW▐ Адрес (старший байт) ╫ 1000 0000 0000 1001 = &H8009 ╫ Итак, строка помещается в PIT по адресу &H8009.Все остальное очевидно! ? PEEK(&H8009) ? PEEK(&H800A) ? PEEK(&H800B) ? PEEK(&H800C) 65 66 67 68 ░WW Код ASCII "D" Ok Ok Ok Ok П р и м е р 3. А теперь измените строку 10 и выполните программу. WWWWWWWWWWWWW 10 A$="ABCD"+"" run Ok Повторите вышеуказанные шаги. Вы получите: PRINT HEX$(VARPTR(A$)) 8017 Ok PRINT PEEK(&H8014) WW▐ 3 VALTYPE PRINT PEEK(&H8015) WW▐ 65 Код ASCII для символа "А" PRINT PEEK(&H8016) WW▐ 0 В имени нет второго символа PRINT PEEK(&H8017) WW▐ 4 Длина строки PRINT PEEK(&H8018) WW▐ 101 = &H65 WW▐ Новый адрес (млaдший байт) PRINT PEEK(&H8019) WW▐ 241 = &HF1 WW▐ Новый адрес (старший байт) Операция, выполненная над строкой, явилась причиной пересылки ее по ад- ресу &HF165 (без изменения). Вы можете убедиться в этом, используя "в ок- рестности" этого адреса функцию PEEK. ? PEEK(&HF165) ? PEEK(&HF166) ? PEEK(&HF167) ? PEEK(&HF168) 65 66 67 68 Ok Ok Ok Ok Забегая несколько вперед, отметим, что функция FRE("") возвратила бы число 200 в первом случае, однако во втором случае возвращает число 196. Приведем схемы расположения информации в памяти для строковых массивов: ═) о д н о м е р н ы й строковый массив. VARPTR(B$(0))WWWY XWW VARPTR(B$(1)) XWWWRWWWRWWWRWWWRWWWRWWWRWWWRWWWRW█WRWWWRWWWRW█WRWWWRWWWRWWW V V V V V V 1 V V VOOOV V VOOOV V VOOO ZWWWQWWWQWWWQWWWQWWWQWWWQWWWQWWWQWWWQWWWQWWWQWWWQWWWQWWWQWWW X-8 X-7 X-6 X-5 X-4 X-3 X-2 X-1 X X+1 X+2 X+3 X+4 X+5 ▌ WWW▌WWW WWW▌WWW ▌ WWW▌WWW ▌ W▌WWWWW W▌W WWW▌WWW V V V V V V V V V Тип массива V Служебная V V V V V Адрес 1-го элемента Идентификатор информацияV V V V Длина значения 1-го элемента V V V Адрес 0-го элемента Размерность массива V Длина значения 0-го элемента Число элементов в массиве П р и м е р 4. Обязательно разберите пример "с компьютером в руках"! WWWWWWWWWWWWW 10 DIM SM$(2):SM$(0)="рог"+"":SM$(1)="Inform"+"":SM$(2)="1989 г."+"" run Ok print HEX$(VARPTR(SM$(0))-8) 8047 Ok ? РЕЕК(&Н8047)░WW Этот адрес соответствует байту VALTYPE. Поэтому Вы должны получить 3. ? РЕЕК(&Н8048)░WW Этот адрес соответствует первому символу имени строкового массива. Вы должны получить число 83 - код ASCII символа "S". ? PEEK(&H8049)░WW Этот адрес соответствует второму символу имени строкового массива. Вы должны получить число 77 - код ASCII символа "М". ? РЕЕК(&Н804A)░WW 12 Служебная информация ? РЕЕК(&Н804B)░WW 0 Служебная информация ? PEEK(&H804C)░WW Массив SM$ одномерный, поэтому мы и получили 1. ? PEEK(&H804D)░WW 3 = 00000011 ? PEEK(&H804E)░WW 0 = 00000000 ╫ ╫ Теперь находим количество элементов в массиве: 00000000 00000011 = 3 ? PEEK(&H800F)░WW 3 Длина значения элемента SM$(0). ╫ Приведем схему расположения информации в памяти для элемента строково- го массива: XWWWRWWWRWWWY VOOOV▄▄▄V▄▄▄V ZWWWQWWWQWWW[ Адреса WW▐ X X+1 X+2 ▌ WWW▌WWW Байт длины WW[ ZWW Ссылка на адрес в строковой области Продолжим наш пример: PRINT HEX$(VARPTR(SM$(0))) 804F Ok ? PEEK(&h804F) 3 ░WWWW Длина значения 0-го элемента массива Ok ? HEX$(PEEK(&h8050)) 66 ░WWWW Младший байт адреса 0-го элемента в стро- Ok ковой области ? HEX$(PEEK(&h8051)) F1 ░WWWW Старший байт адреса 0-го элемента в стро- Ok ковой области ? CHR$(PEEK(&hF166)) р ░WY Ok V ? CHR$(PEEK(&hF167)) V Символы о ░WS значения Ok V 0-го ? CHR$(PEEK(&hF168)) V элемента г ░W[ Ok ║) д в у х м е р н ы й строковый массив. Значение, возвращаемое функцией VARPTR() WWWY XWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWW█WWY V V V V V V &H2 V V V V V OOO V ZWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWW[ X-10 X-9 X-8 X-7 X-6 X-5 X-4 X-3 X-2 X-1 X ▌ WWWWW▌WWWWW WWWWW▌WWWWW ▌ WWWWW▌WWWWW WWWWW▌WWWWW ▌ V V V V V V V V V Служебная инф. V V V Длина значения V Идентификатор Для двухмерного V V нулевого элем. Тип массива массива V V Количество столбцов Количество строк X.7. О п е р а т о р CLEAR Чтобы вычистить одно, приходится выпачкать что-нибудь другое; но можно испачкать все, что угодно, и ничего при этом не вычистить. Принцип накопления грязи по Питеру Оператор CLEAR в общем виде записывается так: CLEAR [[n][,A]] , где: CLEAR ("очистить") - служебное слово; n - арифметическое выражение, целая часть значения которого ука- зывает количество байтов, резервируемых под строковое пространство; значе- ние параметра n меняется от нуля до размеров свободного пространства и равно 200 по умолчанию; А - арифметическое выражение, целая часть значения которого опре- деляет адрес первого байта участка памяти, расположенного между блоком уп- равления файлами и рабочей областью. Этот участок не будет "обработан" ин- терпретатором,поэтому он является как бы "резервной" памятью, где Вы може- те хранить, например, подпрограммы на машинном языке и другие необходимые Вам данные. Другими словами, значение А "переопределяет верхний предел пространст- ва, используемого MSX-BASIC". Например, команда CLEAR 1000,&HF000 отводит 1000 байтов для значений строковых констант в строковой области и RAM с адреса &HF000 для размеще- ния машинной подпрограммы. М а к с и м а л ь н ы м значением выражения A , конечно же, является адрес, с которого начинается рабочая область (&HF380). М и н и м а л ь н о е значение выражения A может быть определено по формуле: XWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V 267G(MAXFIL+1)+VARTAB+145+n+2 V , ZWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ где: MAXFIL - имя слова, расположенного в рабочей области по адресу &HF85F. Этот адрес занимает 1 байт памяти; VARTAB - имя слова, расположенного в рабочей области по адресу &HF6C2. Адрес занимает 2 байта памяти и отмечает конец PIT; n - размер строкового пространства в байтах. Заметим, что м и н и м а л ь н а я величина стека равна 145 байтам (это пространство между концом PIT и вершиной стека). Отметим, что оба аргумента могут быть опущены; в этом случае оператор CLEAR производит "чистку" значений всех числовых и строковых переменных, элементов массивов и определенных пользователем функций DEFFN, а также "уничтожает" стек. Сохранятся только текст программы на MSX-BASIC и ранее зарезервирован- ные машинные подпрограммы! Приведем два простеньких примера: 1) 10 DEF FNY(X)=X run WWWW▐ clear WWW▐ print FNY(1) Ok Ok Undefined user function Ok 2) 10 M=9:T$=STRING$(5,"+"):?M;T$ 20 CLEAR:?M;T$ run 9 +++++ 0 Ok Параметр n в операторе CLEAR n указывает размер строковой области, зарезервированной для хранения строковых значений, причем XWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V 0 Ё n Ё FRETOP-VARTAB-145-16 V , ZWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ где: FRETOP - имя слова, расположенного в рабочей области по адресу &HF69B. Этот адрес занимает 2 байта памяти; VARTAB - имя слова, расположенного в рабочей области по адресу &HF6C2. Этот адрес занимает 2 байта памяти. П р и м е р 3. Нажмите кнопку "RESET" Вашего компьютера. WWWWWWWWWWWWW А теперь: print hex$(peek(&HF69C));" ";hex$(peek(&HF69B)) DC 5F ░WWWWW Вы узнали адрес FRETOP ? Ok print hex$(peek(&HF6C3));" ";hex$(peek(&HF6C2)) 80 3 ░WWWWW Вы узнали адрес VARTAB ? Ok print &HDC5F-&H8003-145-16 23483 ░WWWWW 0ЁnЁ23483 Ok П р и м е р 4. 10 CLEAR200:T$=SPACE$(198):B$=STRING$(2,"#"):?B$:X$= WWWWWWWWWWWWW STRING$(1,"L"):?X$ run ## Out of string space in 10 Ok Получили сообщение об отсутствии места в строковой области, так как 200 байтов, отведенных для нее по умолчанию, оказались уже исчерпанными. Однако... 10 CLEAR 200:T$=SPACE$(198):B$=STRING$(2,"#"):?B$;:CLEAR1:X$=STRING$(1," L"):? FRE("") run ## 0 Ok  X.8. Ф у н к ц и я FRE Garbage collection ("чистка памяти","сборка мусора") - действия системы динамического распределения памяти для обнаружения неис- пользуемых программой блоков памяти и присоединения их к списку свободной памяти для повторного использования. Англо-русский словарь по программированию и информатике Информацию о размере свободной области ("Free Area") в RAM можно полу- чить с помощью функции FRE, обращение к которой имеет вид: FRE(A) , где: FRE ("FREe"-"свободный") - служебное слово; A - арифметическое или строковое выражение, причем для интерпретато- ра важным является лишь тип выражения, а не его значение. На практике применяется следующий синтаксис: FRE(0) или FRE("") . Функция FRE(0) возвращает количество байтов, оставленных для расшире- ния PIT, VT, стека, строковой области и блока управления файлами. П р и м е р 1. WWWWWWWWWWWWW 10 ? FRE(0):X=451:? FRE(0):Z#=7.5:? FRE(0) 20 Y!=555:? FRE(0):W%=111:? FRE(0) run 28739 28728 ░WWт.к. переменная Х по умолчанию - двойной точности, а следова- тельно, занимает в памяти 11 байтов; 28717 ░WWт.к. переменная Z - двойной точности (занимает в памяти так- же 11 байтов); 28710 ░WWт.к. переменная Y - одинарной точности (занимает в памяти 7 байтов); 28705 ░WWт.к. переменная W - целого типа (занимает в памяти 5 байтов). Ok Функция FRE(0) выдает сообщение: "Out of memory" ("Н е х в а т а е т п а м я т и") при достижении значения, меньшего 145 байтов, минимально допустимого для стека системы MSX-BASIC. Посмотрите (предварительно нажав кнопку "RESET"): CLEAR 28868:PRINT FRE(0) CLEAR 28869:PRINT FRE(0) 147 Out of memory Ok Ok Заметим,что слово VARTAB отличается от слова TXTTAB на 2 байта (при от- сутствии программы!), поэтому, добавив эти 2 байта к 145 байтам, необходи- мым для работы стека, получаем число 147! Функция FRE("") возвращает количество свободных байтов в строковом про- странстве. Например: print FRE("") X$="2╫"+"3╫":print FRE("") 200 196 Ok Ok Кроме того, функция FRE("") выполняет важное дополнительное действие. Оно связано с наличием в MSX-BASIC строк переменной длины, обработка кото- рых может привести к явлению "фрагментации памяти" (внутри строковой обла- сти появляются участки,содержащие неиспользуемую информацию -"м у с о р"). Поэтому, если в качестве аргумента функции FRE задано выражение строково- го типа, перед вычислением объема свободной памяти функция выполняет "с б о р к у м у с о р а", т.е. удаление всех неиспользуемых данных и освобождение занимаемых ими областей. П р и м е р 2. Оказывается,что если у Вас в начале программы встречает- WWWWWWWWWWWWW ся оператор A$="ABCD"+"EF", а затем оператор A$="X"+"Y", то Вы сразу же создадите 6-байтовое пространство, заполненное "мусором"! Покажем это: print HEX$(PEEK(&HF69C)); HEX$(PEEK(&HF69B)); F168 WWWW▌WWW Ok ZWW Адрес "верхушки" строкового a$="ABCD"+"EF" пространства Ok for t=0 to 5:print chr$(peek(&HF168-t));:next FEDCBA Ok a$="X"+"Y" Ok for t=0 to 7:print chr$(peek(&hF168-t));:next FEDCBAYX ZWW▌W[ Ok ZWWW "м у с о р" print fre("")'Избавимся от "мусора"! 198 Ok for t=0 to 7:print chr$(peek(&hF168-t));:next YXXCBAYX ZWW▌W[ Ok ZWWW "м у с о р" Из примера следует, что строки хранятся в строковом пространстве в том порядке, в каком они были определены. Таким образом, функция FRE("") изменила положение значения строковой переменной (это и называется "с б о р к о й м у с о р а"). Если под строки зарезервирован большой объем строкового пространства и определено много символьных переменных, время "сборки мусора" может соста- вить несколько минут. При выполнении этой операции компьютер полностью "застывает". Посмотрите... П р и м е р 3. WWWWWWWWWWWWW 10 CLEAR 5000 'Объявлен размер строковой области - 5000 байтов 15 DEFINT A-Z:DIM A$(1500):FOR I=1 TO 1500:A$(I)="2"+"":NEXT 30 'Размещение данных в строковой области, "мусора" нет! 40 TIME=0:PRINT FRE(""),TIME/60/60"мин" run run G3500GGGGGGGGGG3.61638888888 мин G3500GGGGGGGGGG3.3716666666667 мин Ok (для MSX-1) Ok (для MSX-2) Интересно, что при изменении в строке 10 оператора CLEAR 5000 на опера- тор CLEAR 1600, результат получается почти тот же (╥3.607 мин. для компью- тера MSX-1 и ╥3.38 мин. для компьютера MSX-2)! Е д и н с т в е н н ы й способ уменьшить время "сборки мусора" - это использовать минимальное количество строк и особенно строковых массивов! Следует заметить, что некоторые строки хранятся в тексте самой програм- мы и, таким образом, не занимают места в строковой области. П р и м е р 4. WWWWWWWWWWWWW 10 ? FRE("");:U$="fywapro":D$="K":? FRE("");:DIM E$(150):? FRE("") 20 FOR K=1 TO 150:E$(K)=CHR$(K):NEXT:? FRE("") 30 E$(1)="APR":? FRE(""):E$(1)=" "+E$(1):? FRE("") run 200 200 200 Далее пауза для "сборки мусора"... 50 51 ░WW Произошла "сборка мусора" (свободное место в строковой облас- 47 ти увеличилось, т.к. значение элемента массива E$(1) уже хра- Ok нится в тексте программы)... Таким образом, строковая область является областью памяти, резервируе- мой для хранения строковых данных. Если Вы хотите зарезервировать в стро- ковом пространстве место для хранения 10 строк, содержащих каждая макси- мум 5 символов, то воспользуйтесь, например, оператором цикла: FOR I=1 TO 10:A$(I)=SPACE$(5):NEXT Во избежание "сборки мусора": ═) определяйте все переменные в начале программы; ║) используйте строковые функции MID$, LEFT$, RIGHT$. Перед работой со следующим примером выключите, а затем снова включите Ваш компьютер. П р и м е р 5. WWWWWWWWWWWWW A$="полет" Ok for t=0 to 10:print chr$(peek(&HF168-t));:next телопп⌡⌡ Ok Z▌W[ ZWWWWWWW "м у с о р" A$="налет" Ok for t=0 to 10:print chr$(peek(&HF168-t));:next телоптеланн ZW▌W[ ▌ ZWWWWWWWZWWWW "м у с о р" Ok print fre("") 195 Ok for t=0 to 10:print chr$(peek(&HF168-t));:next теланнеланн ZW▌WW[ ZWWWWWWW "м у с о р" Ok mid$(A$,1,2)="по" Ok for t=0 to 10:print chr$(peek(&HF168-t));:next телоппеланн ZWW▌W[ ZWW "м у с о р" (он остался на прежнем месте!) Ok Отметим, что строковые функции MID$, LEFT$, RIGHT$ не изменяют указате- ли на значения строковых переменных. Обычный же оператор присваивания, ра- зумеется, указатели изменяет! Покажем это на примере (не забудьте о коман- де CLEAR !). П р и м е р 6. WWWWWWWWWWWWW a$="полет" Ok print hex$(peek(varptr(a$)+2)); hex$(peek(varptr(a$)+1)) F164 Ok a$="налет" Ok print hex$(peek(varptr(a$)+2)); hex$(peek(varptr(a$)+1)) F15F Ok а теперь... mid$(a$,1,2)="по" Ok print hex$(peek(varptr(a$)+2)); hex$(peek(varptr(a$)+1)) F15F ░WW Обратите внимание, это значение совпадает с предыдущим! Ok Как видим, значение указателя в последнем случае не изменилось! ╖) при необходимости используйте оператор SWAP A$,В$ , который не меня- ет расположения значений переменных, а лишь меняет местами указатели на эти значения. Проиллюстрируем этот факт на примере... П р и м е р 7. WWWWWWWWWWWWW clear Ok print HEX$(PEEK(&HF69C)); HEX$(PEEK(&HF69B)) F168 Ok A$="Зачет":B$="Автомат" Оk for t=0 to len(a$)+len(b$):print chr$(peek(&hF168-t));:next течаЗтамотвАА Ok swap A$,B$ Ok for t=0 to len(a$)+len(b$):print chr$(peek(&hF168-t));:next течаЗтамотвАА Ok И наконец, функция FRE() может помочь Вам также в з а щ и т е Вашей программы. Например, в "укромном" месте программы, работающей со строко- вой информацией, поместите оператор X$=SPACE$(FRE(""))- конечно,Вы должны учесть, что целая часть значения аргумента функции SPACE$ должна принадле- жать отрезку [0,255]!. Это удержит "любознательных" от модификации значе- ний переменных Вашей программы (разумеется, в данном случае строковых)! Посмотрите: 10 X$=SPACE$(FRE("")):Y$="2"+Y$ run Out of string space in 10 Ok X.9. Р а б о ч а я о б л а с т ь В рабочей области содержатся системные подпрограммы, системные перемен- ные и "ловушки", используемые интерпретатором во время выполнения операто- ров Вашей программы. В рабочей области хранятся данные о позиции курсора, цвете текста, состоянии функциональных клавиш и другая полезная информа- ция, инициализируемая при включении компьютера. XWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V Адрес, отмечающий н а ч а л о рабочей области, указан в V V самой этой области в слове HIMEM, содержимое которого за- V V нимает 2 байта, расположенных с адреса &HFC4A . V ZWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ Еще раз напомним Вам, что адреса, занимающие два байта, всегда записы- ваются так: вначале записывается содержимое младшего байта, а затем содер- жимое старшего байта! Отметим, что значением выражения HEX$(PEEK(&HFC4A)+256*PEEK(&HFC4B)) является а д р е с н а ч а л а р а б о ч е й области. Поскольку рабочая область расположена в RAM, ее переменные могут изме- няться операторами POKE. Но это следует делать только в том случае, если Вы з н а е т е, что за этим последует! X.9.1. М а т р и ц а к л а в и а т у р ы М а т р и ц е й клавиатуры для MSX-компьютеров назовем таблицу вида: 0-й 1-й 2-й 3-й 4-й 5-й 6-й 7-й бит бит бит бит бит бит бит бит XWWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWRWWWWWWRWWWWWWY VАдресаV &hFEV &hFDV &hFBV &hF7V &hEFV &hDFV &hBF V &h7F V VбайтовV(254)V(253)V(251)V(247)V(239)V(223)V (191)V (127)V TWWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWWUWWWWWWS 0-я строка V FBE5 V 9 V ; V 1 V 2 V 3 V 4 V 5 V 6 V TWWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWWUWWWWWWS 1-я строка V FBE6 V 7 V 8 V 0 V = V - V H V : V V V TWWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWWUWWWWWWS 2-я строка V FBE7 V \ V . V B V @ V , V / V F V I V TWWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWWUWWWWWWS 3-я строка V FBE8 V S V W V U V A V P V R V [ V O V TWWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWWUWWWWWWS 4-я строка V FBE9 V L V D V X V T V ] V Z V J V K V TWWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWWUWWWWWWS 5-я строка V FBEA V Y V E V G V M V C V ~ V N V Q V TWWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWWUWWWWWWS 6-я строка V FBEB VSHIFTV CTRLVGRAPHV CAPSV РУС V F1 V F2 V F3 V TWWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWWUWWWWWWS V V V V V V V V V V 7-я строка V FBEC V F4 V F5 V ESC V TAB V STOPV BS VSELECTVRETURNV V V V V V V V V V V TWWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWWUWWWWWWS V V V V V V V ▌ V V V 8-я строка V FBED VSPACEV HOMEV INS V DEL V░WWWWV V V V V WWW▐ V V V V V V V V V █ V V TWWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWWUWWWWWWS 9-я строка V FBEE V RET V + V * V 0 V 1 V 2 V 3 V 4 V TWWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWUWWWWWWUWWWWWWS 10-я строка V FBEF V 5 V 6 V 7 V 8 V 9 V - V , V . V ZWWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWQWWWWWWQWWWWWW[ Последние две строки соответствуют цифровой (правой) зоне клавиатуры учительского компьютера серии MSX-2. Ответим теперь на Ваш очевидный вопрос: XWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V Как воспользоваться этой таблицей? V ZWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ П р и м е р 1. Ниже приведены программа, останавливаемая нажатием кла- WWWWWWWWWWWWW виши "GRAPH": 10 Z=PEEK(&HFBEB):IF Z<>251 THEN 10 и программа, останавливаемая нажатием клавиш "SHIFT"+"CTRL": 10 Z=PEEK(&HFBEB):IF Z<>(254 AND 253) THEN 10 А теперь ответ на Ваш следующий вопрос: XWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V А как получить матрицу клавиатуры ? V ZWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ Для "чтения" нажатой клавиши достаточно "прочесть"слово NEWKEY (11 бай- тов) по адресу &HFBE5 из таблицы системных переменных. П р и м е р 2. Программа "пробегает" все клавиши и возвращает позицию WWWWWWWWWWWWW нажатой клавиши (X,Y) матрицы клавиатуры. 11 значений, записанных в слове NEWKEY, соответствуют 11 строкам матрицы клавиатуры. Если не нажата ни одна клавиша, содержанием каждого из 8 байтов, соответ- ствующих строке матрицы является 1. Это фиксируется двоичным числом &B11111111=255. Когда же клавиша нажата, считанное на этой строке значе- ние отличается от 255: бит соответствующей колонки "сбрасывается" в 0. 10 FOR Y=0 TO 10:Z=PEEK(&HFBE5+Y) 30 IF Z=255 THEN 80 'WW▐ 40 PRINT"Y=";Y:Z$=RIGHT$("00000000"+BIN$(Z),8) 60 PRINT"X=";8-INSTR(Z$,"0"):PRINT 80 NEXT:GOTO 10 'WW▐ X.9.2. Д и н а м и ч е с к а я к л а в и а т у р а [46] Промедление с легким делом превращает его в трудное, промедление же с трудным делом пре- вращает его в невозможное. Д.Лоример Исследуем один подход к разработке учебных программ, работающих под управлением интерпретатора MSX-BASIC. Существенная особенность этого под- хода состоит в том, что программа в процессе выполнения модифицируется (происходит изменение отдельных строк BASIC-программы или добавление но- вых строк). Считается,что допущение самомодификации программы во время вы- полнения является признаком плохого стиля программирования,поэтому начнем с примера, который показывает, что предлагаемый подход является не только оправданным, но и в ряде случаев единственно возможным. Пусть необходимо табулировать функцию y=f(x), конкретно x╫, то есть для каждого значения аргумента вычислить значение функции и результат за- писать в таблицу. Соответствующая программа выглядит следующим образом: 10 'Программа табулирования функции. 20 DIM X(200),Y(200) 30 INPUT XN,XK 'Задание границ изменения аргумента функции. GGG 100 GOSUB 1000 'Обращение к подпрограмме табулирования. GGG 999 END 1000 FOR I=1 TO 200:Y(I)=X(I)*X(I):NEXT I:RETURN Части программы между строками 30 и 100, 100 и 999 содержат операторы, обеспечивающие масштабирование, заполнение таблицы, защиту от ошибочных действий пользователя и т.д. Простота программы обусловлена тем, что зада- ча табулирования решается для ф и к с и р о в а н н о й функции. Попыта- емся теперь разработать программу, которая позволяет табулировать любую функцию одной переменной, аналитическое выражение которой вводится с кла- виатуры! Для реализации на ПЭВМ"YAMAHA" используем специальный механизм, введен- ный Дж.Баттерфилдом (J.Batterfield) и названный им принципом "д и н а м и- ч е с к о й к л а в и а т у р ы". В к о м а н д н о м (!) режиме информация,набираемая пользователем на клавиатуре, аппаратно записывается в буфер (называемый в дальнейшем б у- ф е р о м к л а в и а т у р ы , БК). БК размещается в рабочей области с адреса &HFBF0 по адрес &HFC17 и занимает 40 байтов. При нажатии клавиши "RETURN" содержимое БК считывается интерпретатором и выполняется соответ- ствующая команда. Имитация действий пользователя на основе принципа "динамической клавиа- туры" осуществляется следующим образом: 1) с помощью оператора INPUT вводится текст запроса (в данном случае - аналитическое выражение табулируемой функции) в символьную строку F$ (в приведенной ниже программе - строка 10); 2) символьная строка дополняется впереди номером, а в конце - кодом ко- манды "RETURN" (строки 15 и 1890): F$="номер строки 1"+F$+CHR$(13) ; 3) строка побайтно переписывается в БК, начиная с aдреса &HFBF0, при помощи оператора POKE и функции PEEK (подпрограмма, начинающаяся со стро- ки 1880); 4) строка S$="goto"+"номер строки 2"+CHR$(13), где н о м е р с т р о- к и 2 - номер строки программы, куда после модификации необходимо пере- дать управление, побайтно переписывается в БК (строка 25); 5) выполнение программы прекращается командой END, в результате проис- ходит переход из программного режима в командный. Интерпретатор считывает содержимое БК до первого появления CHR$(13) и выполняет его как команду, то есть модифицирует строку с номером н о м е р с т р о к и 1. Далее считывается остаток содержимого БК до второго появления CHR$(13), и он также выполняется интерпретатором, как команда, после чего происходит пе- реход в программный режим с передачей управления в строку с номером н о- м е р с т р о к и 2. Таким образом, указанный алгоритм решает задачу автоматической модифи- кации программы в соответствии с текстом запроса, вводимого пользователем с клавиатуры, и запуска ее с указанного номера строки. П р и м е р. WWWWWWWWWWW 1 GOTO 10 2 GOTO 30 10 LINEINPUT"Введите аналитическую запись функции:";F$ 11 INPUT"Укажите номер строки, содержащей оператор описания функции по льзователя DEFFN (51< номер строки <59)";SN:GOSUB 2410 13 GOSUB 1550 'Сохранение F$ 15 F$=STR$(SN)+F$:F$=MID$(F$,2,LEN(F$)-1) 20 GOSUB 1880 25 F$="goto2":GOSUB 1880:END 30 GOSUB 1730 'Восстановление F$ 50 'OO Программа табулирования функции Y(x) OO 60 INPUT"Введите[A,B] и шаг табулирования H";A,B,H 65 FOR X=A TO B STEP H:PRINT X;FNY(X):NEXT 90 END 1550 'OOOOO Ф о р м и р о в а н и е F$ OOOOO 1590 F$="deffny(x)="+F$:POKE &HF600,LEN(F$) 1620 FOR I=1 TO LEN(F$):POKE &HF601+I,ASC(MID$(F$,I,1)):NEXT 1720 RETURN'WW▐ 1730 'OOOOO В о с с т а н о в л е н и е F$ OOOOO 1740 LF=PEEK(&HF600):F$="" 1750 FOR I=1 TO LF:C=PEEK(&HF601+I):F$=F$+CHR$(C):NEXT 1780 RETURN'WW▐ 1880 'OOOOO Д и н а м и ч е с к а я к л а в и а т у р а OOOOO 1890 F$=F$+CHR$(13) 1900 AD=PEEK(&HF3F9)*256+PEEK(&HF3F8)-65536! 1910 L1=&HFC17-AD+1 1920 IF LEN(F$)<=L1 THEN GOTO 1990 1930 L2=LEN(F$)-L1:N=0 1940 FOR I=AD TO &HFC17:N=N+1 1950 POKE I,ASC(MID$(F$,N,1)):NEXT 1960 FOR I=&HFBF0 TO &HFBF0+L2-1:N=N+1 1970 POKE I,ASC(MID$(F$,N,1)):NEXT 1980 AD=&HFBF0+L2+65536!:POKE&HF3F9,FIX(AD/256):POKE&HF3F8,AD-FIX(AD/2 56)*256:GOTO 2050 1990 N=0 2000 FOR I=AD TO AD+LEN(F$)-1:N=N+1 2010 POKE I,ASC(MID$(F$,N,1)):NEXT 2020 IF LEN(F$)19 THEN CLS:LOCATE 1,10:PRINT"Эта программа имеет огра ничение:":GOTO 2420 ELSE GOTO 2460 2420 PRINT:PRINT "Длина формулы не должна превосходить 19 символов!";L EN(F$) 2440 PRINT:LOCATE 1,23:PRINT"Для продолжения нажмите любую клавишу" 2450 W$=INKEY$:IF W$="" THEN 2450 ELSE GOTO 10 2460 RETURN'WW▐  X.10. П о р т ы в в о д а - в ы в о д а И я надеюсь, что наши потомки будут благодарны мне не только за то, что я здесь разъяснил, но и за то, что мною было добровольно опущено с целью предоставить им удовольствие самим найти это. P.Декарт. Геометрия П о р т ввода-вывода - многоразрядный вход или выход компьютера,через который процессор обменивается данными с внешними устройствами (клавиату- рой, принтером, дисководом, видеопамятью и видеопроцессором, игровыми ма- нипуляторами). Часто говорят, что порты представляют собой "интерфейсные схемы компьютера". Порт ввода-вывода напоминает морской порт, через который ввозят и выво- зят товары. В нашем случае через порты вводятся и выводятся данные. Порты принимают данные от периферийных устройств и направляют их в эти устройст- ва. Используя прямой доступ к портам ввода-вывода,Вы более полно использу- ете возможности компьютера. Процессор "работает" с портами по адресам, которые не следует путать с адресами ROM или RAM: 1) порты с адресами &H00╤&H7F. Вы не можете и з м е н и т ь их содер- жимоe (сравните с ROM!); 2) порты с адресами &H80╤&HFF. Их содержимое изменять можно (сравните с RAM!). Некоторые порты, их функции и адреса перечислены ниже: XWWWWWWWWWWWWWWWWRWWWWWWWWWWWWWWWRWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V А д р е с V Чтение(Запись)V Н а з н а ч е н и е V VWWWWWWWWWWWWWWWWQWWWWWWWWWWWWWWWQWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V Порты, отвечающие за работу с локальной сетью КУВТ YAMAHA MSX-1 V TWWWWWWWWWWWWWWWWRWWWWWWWWWWWWWWWRWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V &H00 V Чтение(Запись)V Посылаемые данные V V &H01 V Чтение V Статус V TWWWWWWWWWWWWWWWWUWWWWWWWWWWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V &H02 V Чтение V Номер компьютера в локальной сети V V V V (только для компьютеров MSX-1) V TWWWWWWWWWWWWWWWWQWWWWWWWWWWWWWWWQWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V Порты, отвечающие за работу с локальной сетью КУВТ YAMAHA MSX2 V TWWWWWWWWWWWWWWWWRWWWWWWWWWWWWWWWRWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V &H09 V V Командный порт (передача или прием) V V &H0C V V Порт состояния V V &H0E V V Порт данных V TW W W W W W W W UW W W W W W W WUW W W W W W W W W W W W W W W W W W WS V &H0A V V Используются при V V &H0B V V инициализации V V &H0D V V сетевого ОЗУ V TWWWWWWWWWWWWWWWWQWWWWWWWWWWWWWWWQWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V П р и н т е р V TWWWWWWWWWWWWWWWWRWWWWWWWWWWWWWWWRWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V &H90 V Чтение V Ввод сигнала занятости принтера V V &H91 V Запись V Kод выводимого символа V V &H92 V Запись V ? V TWWWWWWWWWWWWWWWWQWWWWWWWWWWWWWWWQWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V В и д е о п р о ц е с с о р (VDP) V TWWWWWWWWWWWWWWWWRWWWWWWWWWWWWWWWRWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V &H98 V Чтение(Запись)V Обращение к видеопамяти V V &H99 V Чтение(Запись)V Чтение (запись) в регистр VDP V V &H9A V Запись V Запись в регистр палитры V V &H9B V Запись V Косвенная запись в регистры VDP V TWWWWWWWWWWWWWWWWQWWWWWWWWWWWWWWWQWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V З в у к о г е н е р а т о р (PSG) V TWWWWWWWWWWWWWWWWRWWWWWWWWWWWWWWWRWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V &HA0 V Запись V Ввод в порт номера регистра V V &HA1 V Запись V Ввод в порт информации для установ- V V V V ленного регистра V V &HA2 V Чтение V Чтение числа из регистра PSG V TWWWWWWWWWWWWWWWWQWWWWWWWWWWWWWWWQWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V Программируемый п а р а л л е л ь н ы й и н т е р ф е й с (PPI) V TWWWWWWWWWWWWWWWWRWWWWWWWWWWWWWWWRWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V &HA8 V Чтение(Запись)V Запись (чтение) данных в порт A V V &HA9 V Чтение(Запись)V Запись (чтение) данных в порт B V V &HAA V Чтение(Запись)V Запись (чтение) данных в порт C V V &HAB V Чтение(Запись)V Запись (чтение) режимов PPI V TWWWWWWWWWWWWWWWWUWWWWWWWWWWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V &HB0 V V Внешняя память (SONY) (через &HB3) V TWWWWWWWWWWWWWWWWQWWWWWWWWWWWWWWWQWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V Порты, отвечающие за работу с т а й м е р о м V TWWWWWWWWWWWWWWWWRWWWWWWWWWWWWWWWRWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V &HB4 V Запись V Номер столбца ОЗУ таймера V V &HB5 V Чтение(Запись)V Чтение/запись данных в ОЗУ таймера V TWWWWWWWWWWWWWWWWUWWWWWWWWWWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V &HB8 V V Световое перо (Sanyo) (через &HBB) V TWWWWWWWWWWWWWWWWUWWWWWWWWWWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V V V Управление Audio/Video V V V TWWWWWRWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V V VНомерV Назначение бита V V V Vбита V V V &HF7 V Запись TWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V V V 4 V Управление AV (0 - TV) V V V V 5 V Управление Ym (0 - TV) V V V V 6 V Управление Ys (0 - Super) V V V V 7 V Выбор Video (0 - TV) V TWWWWWWWWWWWWWWWWUWWWWWWWWWWWWWWWUWWWWWQWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V &HFC V Чтение(Запись)V Регистры распределения V V &HFD V Чтение(Запись)V с л о т о в V V &HFE V Чтение(Запись)V (расширений памяти) V V &HFF V Чтение(Запись)V для компьютеров серии MSX-2 V ZWWWWWWWWWWWWWWWWQWWWWWWWWWWWWWWWQWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ XWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V Для работы с портами ввода-вывода используются: V V функция INP и операторы OUT и WAIT V ZWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ Формат оператора OUT: XWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V OUT а д р е с, д а н н о е V, ZWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ где: OUT ("OUTput"-"вывод") - служебное слово; а д р е с - арифметическое выражение, целая часть значения которого принадлежит отрезку [128,255] (128=&H80, 255=&HFF); д а н н о е - арифметическое выражение, целая часть значения которо- го принадлежит отрезку [0,255]. Оператор OUT "посылает" заданное операндом д а н н о е значение в порт, номер которого задан значением параметра а д р е с . В н и м а н и е ! На компьютерах серии MSX-2 прежде, чем использовать опе- ратор OUT, необходимо в непосредственном режиме выполнить команду CALL NETEND (т.е. отключить Ваш компьютер от локальной сети). Опишем синтаксис функции INP: XWWWWWWWWWWWWWWWWWWWWWWWWY V INP (а д р е с) V, ZWWWWWWWWWWWWWWWWWWWWWWWW[ где: INP ("INPut"-"ввод") - служебное слово; а д р е с - арифметическое выражение, целая часть значения которого принадлежит отрезку [0,255]. Функция INP возвращает целочисленное значение, "прочитанное" из порта, имеющего указанный адрес. Видна ли Вам аналогия между операторами POKE и OUT , PEEK и INP ?! При помощи функции INP Вы можете использовать в своих расчетах номер Вашего компьютера. Чтобы поместить в переменную А номер компьютера, на ко- тором Вы работаете в локальной сети MSX-1, примените оператор: XWWWWWWWWWWWWWWWWWWWWY V A=INP(&H02) AND 15 V. ZWWWWWWWWWWWWWWWWWWWW[ Объясним роль логической операции AND. Значением, возвращаемым функци- ей INP(&H02), является двоичное число, записанное в одном байте. "Содержи- мое" четырех старших битов байта нас не интересует. Заметим, что число 15 = &b00001111. Как Вы уже, наверное, догадались, логическая операция AND позволяет выделить нужные нам четыре младших бита. X.10.1. П р о г р а м м и р у е м ы й п а р а л л е л ь н ы й и н т е р ф е й с (PPI) Теперь мы перейдем к рассказу о работе с портами Параллельного Програм- мируемого Интерфейса (PPI - "Parallel Programming Interface"). Напомним Вам, что и н т е р ф е й с (англ. "interface"-"сопряжение" - способ и средства установления и поддержания информационного обмена между исполнительными устройствами автоматической или человеко-машинной системы. В п а р а л л е л ь н о м интерфейсе порция двоичной информации, со- стоящая из n битов, передается одновременно по n каналам. XWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY 1. V Порт А используется для выбора с л о т о в, осуществляющих V V управление расширенной памятью компьютера. V ZWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ За подробностями мы отсылаем Вас к Приложению 1 (раздел 1.8.2). XWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V Порты B и C применяются для "работы" с матрицей клавиатуры, V V причем номер строки матрицы клавиатуры "посылается" в порт V V порт C, а номер столбца "читается" в порту B . V ZWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ П р и м е р 1. О б н а р у ж е н и е нажатия клавиши "GRAPH". WWWWWWWWWWWWW Отметим, что клавиша "GRAPH" находится в строке 6 и столбце 2 матрицы клавиатуры (и строки и столбцы матрицы нумеруются, на- чиная с 0). Тогда: 1) номер строки матрицы клавиатуры "посылаем" в порт C : OUT &HAA,6 2) "извлекаем" номер столбца из порта B: X=INP(&HA9) До нажатия клавиш значением Х является число 255 = &b11111111.В момент нажатия какой-либо клавиши соответствующий бит порта B (в нашем случае - второй) на мгновение обнуляется. Таким образом, нажатие клавиши "GRAPH" легко обнаружить, если выделить значение интересующего нас бита командой: IF (X AND &b00000100)=0 THEN PRINT"GRAPH" Программа, позволяющая обнаружить нажатие клавиши "GRAPH",выглядит так: 10 OUT &HAA,6:X=INP(&HA9) 20 IF (X AND 4)=0 THEN PRINT "GRAPH":END 30 GOTO 10 Надеемся, что Вы обратили внимание на недостаток этой программы: после ее запуска неожиданно включается индикатор "CAPS" (но это не означает,что Вам удалось смоделировать нажатие клавиши "CAPS"!). Разберемся, почему так происходит. Взгляните на приведенную ниже таблицу, в которой описаны назначения би- тов порта C: XWWWWWWWWWWRWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V Биты 0╤3 V Строка матрицы клавиатуры V V Бит 4 V Если 0, то запускается магнитная лента V V Бит 5 V Сигнал записи на магнитную ленту V V Бит 6 V Если 0, то включается индикатор "CAPS" V V Бит 7 V Управление звуковым сигналом V ZWWWWWWWWWWQWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ Все ясно! Индикатор "CAPS" включается потому, что в порт C записывает- ся значение 6 = &b00000110, а значит, шестой бит порта C "опрокинулся" в нуль. ▌ WWWWWRWWWW ZWWWWWWWWWWWWWWWWWWWWWWW[ Фактически только четыре м л а д ш и х бита порта C определяют номер строки матрицы клавиатуры. Для "маскирования" (игнорирования) значений че- тырех старших битов достаточно вместо команды OUT &HAA,6 выполнить ко- манду: OUT &HAA,6 OR (INP(&HAA) AND &HF0) V V V XWW !!! █ █ █ █ &B00000110 OR (&BO1OOOOOO AND &B11110000) = &BO1OO 0110 (символом "O" отмечены биты, состояние которых в данном случае роли не иг- рает). П р и м е р 2. Включение индикатора "CAPS" (если он выключен!) можно WWWWWWWWWWWWW осуществить следующей командой: OUT &HAA, INP(&HAA) XOR &B01000000 V V XW !!! █ █ █ &BO1OOOOOO XOR &B01000000 = &BO0OOOOOO П р и м е р 3. WWWWWWWWWWWWW 10 OUT &HAA,6 OR (INP(&HAA) AND &HF0):B=INP(&HA9) 20 IF (B AND 1)=0 THEN PRINT "Нажата клавиша SHIFT" 30 IF (B AND 2)=0 THEN PRINT "Нажата клавиша CTRL" 40 IF (B AND 4)=0 THEN PRINT "Нажата клавиша GRAPH":GOTO 10 ELSE 10 В ходе работы программы индикатор "CAPS" сохранит состояние, в котором он находился до пуска программы! П р и м е р 4. Получим матрицу клавиатуры при помощи оператора OUT! WWWWWWWWWWWWW XWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V 10 INPUT "Номер строки";N V V 20 INPUT "Номер столбца";T V V 30 OUT &HAA,INP(&HAA) AND &HF0 OR N V V 40 B=((INP(&HA9) AND 2^T)=0) V V 50 IF B THEN PRINT "Клавиша нажата":END V V 60 GOTO 30 V ZWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ П р и м е р 5. Включение и выключение кассетной ленты. Операторы MOTOR WWWWWWWWWWWWW ON и MOTOR OFF могут быть имитированы с помощью команды: OUT &HAA, INP(&HAA) XOR &B00010000 X.10.2. П р о г р а м м и р у е м ы й з в у к о в о й г е н е р а т о р (PSG) Вначале приведем два примера записи информации в PSG при помощи портов ввода-вывода. П р и м е р 6. WWWWWWWWWWWWW 10 SOUND 7,8 'Шум из канала A 10 OUT &HA0,7:OUT &HA1,8: A=INP(&HA2) 20 SOUND 8,15 'Громкость 20 OUT &HA0,8:OUT &HA1,15:B=INP(&HA2) 30 SOUND 6,26 'Частота звука 30 OUT &HA0,6:OUT &HA1,26:C=INP(&HA2) 40 END 40 PRINT A;B;C run 8 15 26 Ok П р и м е р 7. Представьте, что Вы находитесь на берегу Черного моря WWWWWWWWWWWWW в районе Ялты. Закройте глаза и ... 10 FOR I=0 TO 13:READ V 20 OUT &HA0,I:OUT &HA1,V 'Имитация действия оператора SOUND I,V 30 NEXT:END 100 DATA 0,0,0,0,0,0,30,&HB7,16,0,0,0,90,14 А теперь мы расскажем Вам, как можно "музицировать" при помощи н е п о- с р е д с т в е н н о й записи в регистры звукового генератора. Взгляни- те на следующую небольшую табличку: XWWWWWWWWWWWWWRWWWWWWWWWWWWWWWWWWWRWWWWWWWWWWWWWWWWWWY V Исполняемая V Содержимое V Содержимое V V нота V нулевого регистра V первого регистра V TWWWWWWWWWWWWWUWWWWWWWWWWWWWWWWWWWUWWWWWWWWWWWWWWWWWWS V PLAY "O4C" V 172 V 1 V V PLAY "O4C#" V 148 V 1 V V PLAY "O4D" V 125 V 1 V V PLAY "O4D#" V 104 V 1 V V PLAY "O4E" V 83 V 1 V V PLAY "O4F V 64 V 1 V V PLAY "O4F#" V 46 V 1 V V PLAY "O4G" V 29 V 1 V V PLAY "O4G#" V 13 V 1 V V PLAY "O4A" V 254 V 0 V V PLAY "O4A#" V 240 V 0 V V PLAY "O4B" V 227 V 0 V V PLAY "O5C" V 214 V 0 V ZWWWWWWWWWWWWWQWWWWWWWWWWWWWWWWWWWQWWWWWWWWWWWWWWWWWW[ Вы можете проверить эту таблицу при помощи следующей программы: 10 PLAY "O4C" 20 OUT &hA0,0: PRINT INP(&hA2) 'Читаем данные 30 OUT &hA0,1: PRINT INP(&hA2) 'из регистров PSG А теперь слушайте ... П р и м е р 8. Гамма "до-мажор". WWWWWWWWWWWWW 10 DATA 1,172,1,125,1,83,1,64,1,29,0,254,0,227,0,214 20 OUT &HA0,8:OUT &HA1,15 'Установим громкость канала A 30 FOR T=1 TO 8:READ I1,I2 40 OUT &HA0,1:OUT &HA1,I1:OUT &HA0,0:OUT &HA1,I2 45 FOR K=1 TO 100:NEXT 'Если Вам захочется "озвучить", например, "выст рел", достаточно убрать из программы эту строку 50 NEXT 60 OUT &HA0,8:OUT &HA1,0 'Сбросим громкость канала A X.10.3. Д р у г и е п о р т ы. О п е р а т о р WAIT Приведем примеры использования "содержимого" других портов. П р и м е р 1. Использование портов с адресами &H90 и &H91 для вывода WWWWWWWWWWWWW символа на принтер. Вначале о "роли" первого бита порта с номером &H90: 7-й бит 6-й бит 5-й бит 4-й бит 3-й бит 2-й бит 1-й бит 0-й бит XWWWWWWWRWWWWWWWWRWWWWWWWWRWWWWWWWWRWWWWWWWRWWWWWWWRWWWWWWWRWWWWWWWY V O V O V O V O V O V O V V O V ZWWWWWWWQWWWWWWWWQWWWWWWWWQWWWWWWWWZWWWWWWWQWWWWWWWQWWW▌WWWQWWWWWWW[ Принтер в режиме ON LINE (подключен к ПЭВМ): 0 WWWWWWWV Принтер в режиме OFF LINE (отключен от ПЭВМ): 1 WWWWWWW[ A теперь: включите принтер и вставьте бумагу... 5 CLEAR 300 10 INPUT"Слово";A$ 15 WAIT &H90,2,255 'Вы включите, наконец, принтер или нет? 20 A$=A$+CHR$(13)+CHR$(10) 'CHR$(13) - код возврата каретки; CHR$(10) - код перевода строки 30 FOR I=1 TO LEN(A$) 35 OUT &H90,0:OUT &H90,137 'Инициализация принтера 40 B=ASC(MID$(A$,I,1)) 50 OUT &H91,B 60 NEXT I П р и м е р 2. Считывание кода выведенного на экран символа: WWWWWWWWWWWWW 10 K$=INKEY$:IF K$="" THEN 10 ELSE PRINT K$; 20 PRINT INP(&H98):GOTO 10 Оператор WAIT используется сравнительно редко. Его синтаксис: XWWWWWWWWWWWWWWWWWWWWWWWWY V WAIT P,M[,C] V, ZWWWWWWWWWWWWWWWWWWWWWWWW[ где: WAIT ("ожидать") - служебное слово; P - арифметическое выражение, целая часть значения которого задает адрес порта; M и C - арифметические выражения, целые части значений которых при- надлежат отрезку [0,255]. Оператор WAIT "заставляет" компьютер "ожидать", пока результатом "опро- са" порта с указанным адресом не окажется число 0 (данный порт "работает" в режиме ч т е н и я ). Другими словами, этот оператор является "беско- нечным циклом", который "ждет", пока от порта ввода-вывода не придет опре- деленный сигнал. Вы можете прервать затянувшееся "ожидание", нажав клавиши "CTRL+STOP" (при этом Вы вернетесь в командный режим). Содержимое порта с указанным адресом заносится в некоторый регистр про- цессора Z-80, который мы назовем X. Далее содержимое регистра X комбиниру- ется со значениями параметров M и С по формуле: X = (X XOR C) AND M Если после этого содержимое регистра X окажется равным 0, то происхо- дит "выход из оператора WAIT". В противном случае порт вновь "опрашивает- ся", и процесс повторяется. Приведем таблицу-"подсказку": XWWWWWWWWWWWWWWWWWRWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V X V 0 0 0 0 1 1 1 1 V V C V 0 0 1 1 0 0 1 1 V TWWWWWWWWWWWWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V X XOR C V 0 0 1 1 1 1 0 0 V V M V 0 1 0 1 0 1 0 1 V TWWWWWWWWWWWWWWWWWUWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V (X XOR C) AND M V 0 0 0 1 0 1 0 0 V ZWWWWWWWWWWWWWWWWWQWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ Вопрос к читателю: Какой вид будет иметь таблица-"подсказка" при отсут- ствии параметра C ? П р и м е р 3. 10 WAIT &HAA,64,255 WWWWWWWWWWWWW (&BO1OOOOOO XOR &B11111111) AND &B01000000 = &B00000000 = 0 ! Эта программа закончит свою работу, если загорится индикатор "CAPS". П р и м е р 4. WWWWWWWWWWWWW ═) WAIT &H90,2,255 'Ожидается в к л ю ч е н и е принтера ║) WAIT &H90,2,0 'Ожидается о т к л ю ч е н и е принтера X.11. Д о п о л н е н и е Р а б о т а с п о р т о м в в о д а - в ы в о д а с а д р е с о м &h0C Предварительно кратко опишем структуру данного порта. Старший XWWWRWWWRWWWRWWWRWWWRWWWRWWWRWWWY Младший бит V O V O V O V O V O V O V O V O V бит ZW▌WQW▌WQW▌WQW▌WQW▌WQW▌WQW▌WQW▌W[ V V V V V V V ZWW Общий бит готовности сети V V Не используются V (0: сеть готова) V V ZWWWWWW Бит регистрации данных V V (0: данные поступили) V ZWW Бит направления поступления информации (1: от учителя) ZWWWWWW Бит направления поступления информации (1: от ученика) А теперь два примера его использования. В н и м а н и е ! Слабонервых просим не смотреть: примеры написаны на Макроассемблере M80! П р и м е р 1. П о с ы л к а байта по сети WWWWWWWWWWWWWW OUT_BYTE:: ; На входе в регистре A - д а н н о е DI ; PUSH DE ; LD D,A ; LD A,005H ; OUT 9,5 - это запись байта в сетевое ОЗУ OUT (009H),A ; OUT_B: IN A,(0CH) ; AND 041H ; CP 040H ; JR NZ,OUT_B ; Если сеть не готова, то ждем LD A,D ; OUT (00EH),A ; POP DE ; EI ; RET ; П р и м е р 2. П р и е м байта из сети WWWWWWWWWWWWWW IN_BYTE:: ; На выходе в регистре A - д а н н о е DI ; LD A,003H ; OUT 9,3 - считывание байта из сетевого ОЗУ OUT (009H),A ; IN_B: IN A,(00CH) ; AND 083H ; CP 080H ; JR NZ,IN_B ; Если сеть не готова, то ждем IN A,(00EH) ; EI ; RET ; К а р т а а д р е с о в п о р т о в в в о д а - в ы в о д а д л я к о м п ь ю т е р о в MSX-1 [30] FFGXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY B4GXWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWY V V V Внешняя память V F8GTWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS B0GTWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V Порты управления Audio/Video V V PPI (8255) V F7GTWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS A8GTWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V V V PSG (AY-3-8910) V F0GTWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS A0GTWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V V V VDP (9918A) V E0GTWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS 98GTWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V ROM для китайских иероглифов V V Принтер V D8GTWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS 90GTWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V Контроллер Floppy Disk V V V D0GTWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS 88GTWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V V V RS-232C V C0GTWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS 80GTWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V Световое перо V V Зарезервированы V B8GTWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS 40GTWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS V V V Не определены V B5GTWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWS 00GZWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[ V Календарь. Часы V B4GZWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW[