Здесь показаны различия между двумя версиями данной страницы.
msx:basic_programming_guide:10 [2020-04-23 14:10] |
msx:basic_programming_guide:10 [2022-09-09 23:28] (текущий) |
||
---|---|---|---|
Строка 1: | Строка 1: | ||
- | [<>] | + | ~~HIDEPAGE:search;sitemap~~ |
- | ~~TOC wide~~ | + | ~~REDIRECT>msx:basic_dialogue_programming_language:010~~ |
- | + | ||
- | ====== Глава X. Управление ресурсами памяти ====== | + | |
- | + | ||
- | <WRAP group 99%> | + | |
- | <WRAP half column> \\ </WRAP> | + | |
- | <WRAP half column><WRAP justify> | + | |
- | Мозг, хорошо устроенный, стоит больше, чем мозг, хорошо наполненный. | + | |
- | <WRAP rightalign> | + | |
- | —//Мишель Монтень// | + | |
- | </WRAP></WRAP> | + | |
- | </WRAP></WRAP> | + | |
- | + | ||
- | ===== X.1. Карта памяти (для компьютеров MSX 1) ===== | + | |
- | + | ||
- | Персональный MSX–компьютер имеет небольшой объем памяти — 96 Кбайт для [[msx:msx_1]] и 242 Кбайта для [[msx:msx_2]]. Поэтому полезной для пользователя оказывается информация о распределении ресурсов памяти и сведения о наличии и | + | |
- | объёме в ней свободных мест в любой момент времени. | + | |
- | + | ||
- | Общий объем памяти у компьютеров серии [[msx:msx_1]] равен 96 Кбайт. Здесь и далее мы будем рассматривать только 64 Кбайта, с которыми обычно и работает основная масса пользователей. | + | |
- | + | ||
- | Взгляните на приведённый ниже [[#pict_1|рис. 1]]… | + | |
- | + | ||
- | Вся память разбита на две основные части: | + | |
- | * ROM ("Read Only Memory" — "Постоянное Запоминающее Устройство") и | + | |
- | * RAM ("Random Access Memory" — "Оперативное Запоминающее Устройство") | + | |
- | + | ||
- | ROM содержит те программы и данные, которые "заложены" в компьютер при изготовлении. Вот почему он всегда выводит определённые сообщения при включении и способен "понимать" программу на языке [[msx:basic:]]. | + | |
- | + | ||
- | В ROM находится //интерпретатор// — программа на машинном языке, которая переводит один за другим операторы языка [[msx:basic:]] в программу на машинном языке, т.е. на //единственном// языке, который понимает компьютер. С помощью этой программы компьютер проверяет синтаксис, выводит при необходимости сообщение об ошибке, переходит к следующему оператору или возвращается в командный режим и так далее. | + | |
- | + | ||
- | Здесь же находятся подпрограммы управления клавиатурой и экраном, которые составляют //экранный редактор// [[msx:basic:]]. | + | |
- | + | ||
- | ROM в основном разделена на две части: | + | |
- | - подпрограммы BIOS ("Basic Input–Output System"); | + | |
- | - другие подпрограммы. Так, например, при включении компьютера наступает небольшая пауза; в этот момент происходят различные инициализации экрана дисплея (установка определённого режима ''SCREEN'', установка ширины экрана ''WIDTH'' и др.). Это происходит оттого,что "зашитые" в ROM подпрограммы инициализации "посылают" определённую информацию в рабочую область RAM, разговор о которой ещё пойдёт впереди. | + | |
- | + | ||
- | Подпрограммы BIOS осуществляют переход к другим подпрограммам. Они напоминают последовательность операторов ''GOSUB'', которую можно увидеть на первом уровне хорошо структурированной программы на [[msx:basic:]]. Подпрограммы BIOS расположены по одним и тем же адресам ROM независимо от версии [[msx:basic:]] и осуществляют переход к другим подпрограммам, положение которых может быть изменено. | + | |
- | + | ||
- | В противоположность ROM, RAM не сохраняет информацию при выключении компьютера. Поговорим теперь о его структуре. | + | |
- | + | ||
- | "Верхушка" памяти (она изображена в //нижней// части таблицы) занята //рабочей областью//, которая состоит из: | + | |
- | * таблицы системных переменных, | + | |
- | * таблицы ловушек ("Hooks Table"). | + | |
- | "Нижняя" область памяти (она изображена в //верхней// части таблицы) занята: | + | |
- | - текстом программы ("Program Instruction Table", PIT); | + | |
- | - таблицей переменных ("Variable Table", VT). VT содержит все переменные, создаваемые в ходе выполнения программы; | + | |
- | - таблицей массивов ("Array Variable Table"). | + | |
- | + | ||
- | Между "верхней" и "нижней" областью памяти располагаются: | + | |
- | * свободная область ("Free Area"); | + | |
- | * //стек// ("Stack Area"); стек содержит всю информацию, необходимую для выполнения программы. Например, именно здесь хранится адрес тех байт PIT, которые содержат сведения о следующем выполняемом операторе Вашей программы; | + | |
- | * //строковая// область ("Character String Area"); по умолчанию для неё отводится 200 байт, однако размеры этой области можно менять оператором ''CLEAR'' (см. [[#X.7. Оператор CLEAR|раздел X.7.]]); | + | |
- | * //блок управления// файлами ("Files Control Block"). | + | |
- | + | ||
- | Если в Вашей программе присутствует оператор ''MAXFILES='' (напомним, что он задаёт максимальное количество одновременно открытых файлов), то для каждого файла автоматически резервируется 267–байтное пространство для | + | |
- | осуществления обмена информацией с файловыми устройствами. | + | |
- | + | ||
- | {{anchor:pict_1}} | + | |
- | <code> | + | |
- | &H0000 ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ | + | |
- | ∗▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧∗ | + | |
- | ∗▧▧▧▧▧▧▧▧▧ ROM (Интерпретатор MSX BASIC) ▧▧▧▧▧▧▧∗ | + | |
- | ∗▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧▧∗ | + | |
- | ─ ─ ─ ─∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗─ ─ ─ ─ | + | |
- | &H8000 │ │ TXTTAB | + | |
- | │ Программа на языке BASIC │ │ | + | |
- | │ ("Program Instruction Table", PIT) │ ▼ | + | |
- | │ │ | + | |
- | │─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │─ ─ ─ ─ | + | |
- | │ │ VARTAB | + | |
- | │ Простые переменные ("Variable Table") │ │ | + | |
- | │ │ ▼ | + | |
- | │─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │─ ─ ─ ─ | + | |
- | │ Массивы ("Array Variable Table") │ ARYTAB | + | |
- | │ │ │ | + | |
- | │─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ │ ▼ | + | |
- | │ │ | + | |
- | │ Свободная область ("Free Area") │ | + | |
- | │ │ | + | |
- | RAM │─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤ | + | |
- | │ │ ▲ | + | |
- | │ Стек ("Stack Area") │ │ | + | |
- | │ │ STKTOP | + | |
- | │─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤─ ─ ─ ─ | + | |
- | │ │ ▲ | + | |
- | │ Строковая область ("Character String Area") │ │ | + | |
- | │ │ FRETOP | + | |
- | │─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤─ ─ ─ ─ | + | |
- | │ │ FILTAB | + | |
- | │ Блок управления файлами ("Files Control Block")│ │ | + | |
- | │ │ ▼ | + | |
- | ─ ─ ─ ─ ┼────────────────────────────────────────────────│─ ─ ─ ─ | + | |
- | &HF380 │ Таблица системных переменных │ HIMEM | + | |
- | │ ("System Variable Table") │ │ | + | |
- | ─ ─ ─ ─ │─ ─ ─ ─ ─ - - - - - - - - - - - - - - - - ─ ─ ─ │ ▼ | + | |
- | &HF3A9 │ │ | + | |
- | │ Таблица ловушек ("Hooks Table") │ | + | |
- | &HFFFF │ │ | + | |
- | ─ ─ ─ ─ └────────────────────────────────────────────────┘ | + | |
- | </code> __//Рис. 1//__ | + | |
- | + | ||
- | Приведённая карта памяти справедлива и для компьютеров серии [[msx:msx_2]]. Но в отличие от компьютеров серии [[msx:msx_1]] с объёмом ROM в 32 Кбайта и RAM в 64 Кбайта, компьютеры серии [[msx:msx_2]] имеют гораздо больший объем памяти (108 Кбайт ROM и 134 Кбайта RAM). Спрашивается, где размещаются эта память? | + | |
- | + | ||
- | <WRAP centeralign>Оказывается, вся память ПЭВМ разбита на блоки объёмом по 64 Кбайта, называемые //слотами// !</WRAP> | + | |
- | + | ||
- | Однако рассмотрение этого вопроса потребует от читателя дополнительных знаний, и поэтому мы рассмотрим его позднее (см. [[#XI.1.9. Слоты видеопамяти|раздел XI.1.9.]]). | + | |
- | + | ||
- | Память разделена на ячейки (//байты//), каждая из которых имеет адрес, закодированный //двумя// байтами: | + | |
- | <code> | + | |
- | Адрес ячейки (байта) | + | |
- | ┌─────────────────┬─────────────────┐ | + | |
- | │ 1 1 0 1 1 0 1 1 │ 1 0 1 1 1 1 0 1 │ | + | |
- | └────────▲────────┴────────▲────────┘ | + | |
- | └── Байты ──┘ | + | |
- | </code> | + | |
- | + | ||
- | Поэтому максимально большой адрес байта равен | + | |
- | 256·&B11111111+&B11111111+1 = 256·&hFF+&HFF+1 = 65535+1 = 65536 , | + | |
- | а следовательно, и обратиться можно не более, чем к 65536 ячейкам памяти (подумайте, почему производится умножение именно на 256?). | + | |
- | + | ||
- | Говорят, что "объём непосредственно адресуемой памяти — 65536 байта". | + | |
- | + | ||
- | ===== X.2. Функция PEEK и оператор POKE ===== | + | |
- | + | ||
- | {{anchor:peek}} | + | |
- | Функция ''PEEK'' позволяет Вам "посмотреть" содержимое любой ячейки памяти в адресном пространстве MSX–компьютера. | + | |
- | Её общий вид: | + | |
- | <WRAP group> | + | |
- | <WRAP half column> | + | |
- | <code> | + | |
- | PEEK (адрес) | + | |
- | </code> | + | |
- | </WRAP> | + | |
- | <WRAP half column> | + | |
- | , | + | |
- | </WRAP> | + | |
- | </WRAP> | + | |
- | где: | + | |
- | * ''PEEK'' ("to peek" — "заглядывать") — служебное слово; | + | |
- | * адрес — арифметическое выражение, значение которого находится в диапазоне от &h0 до &hFFFF. | + | |
- | Функция ''PEEK'' возвращает целое число в интервале от 0 до 255, содержащееся в проверяемой ячейке памяти. | + | |
- | + | ||
- | Например: | + | |
- | - <code> | + | |
- | 10 WIDTH 7:? PEEK(&HF3B0) | + | |
- | run | + | |
- | 7 | + | |
- | Ok | + | |
- | </code> | + | |
- | - <code> | + | |
- | 10 SCREEN 2:PSET(15,18):SCREEN0:PRINT PEEK(&HFCB3);PEEK(&HFCB5) | + | |
- | run | + | |
- | 15 18 | + | |
- | Ok | + | |
- | </code> | + | |
- | В первом примере мы "попросили" компьютер вывести на экран содержимое ячейки с адресом &HF3B0 (в байте по этому адресу хранится значение систем ной переменной — длины дисплейной строки). Во втором примере мы использовали информацию из таблицы адресов системных переменных (см. Приложение 2((Пока не найдено, подробнее [[start#Список отсутствующего материала|здесь]])) ). | + | |
- | + | ||
- | Величину, возвращаемую функцией ''PEEK'', можно интерпретировать как код символа, команду [[msx:basic:]], номер строки, число, "часть" числа, "хранящегося" в нескольких байтах, и т.д. В некоторых случаях правильную интерпретацию можно дать по контексту, однако, если должной уверенности нет, надо //анализировать// не только содержимое одной ячейки, но и содержимое ячеек, находящихся в её "окрестности"! | + | |
- | + | ||
- | {{anchor:e102-01}} __//Пример 1//__. \\ {{.examples:102-01.bas|}} \\ [[+tab|wmsxbpge>102-01.bas]] | + | |
- | <code> | + | |
- | 10 X=&H8000'"Заглянем" в память,начиная с адреса &H8001! | + | |
- | 20 X=X+1:Y=PEEK(X) | + | |
- | 30 IF Y<32 THEN ?"│";:GOTO 20 ELSE ?CHR$(Y);" ";:GOTO20 | + | |
- | </code> | + | |
- | Наличие условия Y<32 в строке 26 связано с тем, что существуют "непечатаемые" символы, имеющие код ASCII, меньший 32. | + | |
- | + | ||
- | {{anchor:poke}} | + | |
- | Поговорим теперь об очень полезном операторе ''POKE''. | + | |
- | Общий вид оператора: | + | |
- | <WRAP group> | + | |
- | <WRAP half column> | + | |
- | <code> | + | |
- | POKE A, D | + | |
- | </code> | + | |
- | </WRAP> | + | |
- | <WRAP half column> | + | |
- | , | + | |
- | </WRAP> | + | |
- | </WRAP> | + | |
- | где: | + | |
- | * POKE ("to poke" — "помещать") — служебное слово; | + | |
- | * A — арифметическое выражение, значение которого находится в диапазоне от &h8000 до &hFFFF; | + | |
- | * D — арифметическое выражение, значение которого принадлежит отрезку [0,255] (поскольку оно должно умещаться в один байт). | + | |
- | + | ||
- | Оператор ''POKE'' вычисляет значения выражений А и D и сохраняет значение D (которое должно помещаться в одном байте!) по адресу А. Обратите внимание на то, что значение А может оказаться //отрицательным//! | + | |
- | + | ||
- | Если значение А не удовлетворяет ограничениям, то компьютер сообщит об ошибке: | + | |
- | <WRAP centeralign>"Overflow" (//"Переполнение"//), а если значение D, то \\ "Illegal function call" \\ (//"Неправильный вызов функции"//).</WRAP> | + | |
- | + | ||
- | Вы можете использовать оператор ''POKE'' для: | + | |
- | * модификации текста Вашей программы; | + | |
- | * изменения значений переменных; | + | |
- | * размещения в RAM программы, написанной на машинном языке (её запись производится //побайтно//). | + | |
- | Более того, этот оператор позволяет Вам экспериментировать с "подвалом" компьютера (рабочей областью). Но делайте так только в том случае, если Вы понимаете, что за этим последует! | + | |
- | + | ||
- | {{anchor:e102-02}} __//Пример 2//__. Сравните результаты работы двух программ: | + | |
- | <WRAP group> | + | |
- | <WRAP half column> | + | |
- | {{.examples:102-021.bas|}} \\ [[+tab|wmsxbpge>102-021.bas]]<code> | + | |
- | 10 SCREEN 1:PRINT"A" | + | |
- | 20 WIDTH 10 | + | |
- | </code> | + | |
- | </WRAP> | + | |
- | <WRAP half column> | + | |
- | {{.examples:102-021.bas|}} \\ [[+tab|wmsxbpge>102-021.bas]]<code> | + | |
- | 10 SCREEN 1:PRINT"A" | + | |
- | 20 POKE &HF3B0,10 | + | |
- | </code> | + | |
- | </WRAP> | + | |
- | </WRAP> | + | |
- | + | ||
- | ===== X.3. Таблица программных команд (PIT) ===== | + | |
- | + | ||
- | Таблица PIT обычно начинается по адресу &H8000. Однако её можно "сдвинуть", изменив значение системной переменной TXTTAB в таблице системных переменных. | + | |
- | + | ||
- | {{anchor:e103-01}} __//Пример 1//__. Для помещения PIT с адреса &HА000,достаточно выполнить следующую программу: \\ {{.examples:103-01.bas|}} \\ [[+tab|wmsxbpge>103-01.bas]] | + | |
- | <code> | + | |
- | NEW | + | |
- | Ok | + | |
- | 5 'Адрес &HА001,находящийся в двух ячейках с номерами, начиная с &hF676 (слове TXTTAB (&HF676)), | + | |
- | 6 'определяет место,с которого начнется текст программы | + | |
- | 10 POKE &HF676,&H01 'Заполнение младшего байта слова TXTTAB | + | |
- | 20 POKE &HF677,&HA0 'Заполнение старшего байта слова TXTTAB | + | |
- | 30 POKE &HA000,0 'Первый байт PIT(&HA000) должен быть нулевым! | + | |
- | 40 NEW 'Стираем данную программу! | + | |
- | </code> | + | |
- | + | ||
- | Напомним Вам, что адрес &HА001 (как и любой другой!) размещается в двух байтах так: | + | |
- | <code> | + | |
- | Адрес ──▶ &HF676 &HF677 ◀── Адрес | + | |
- | младшего байта │ │ старшего байта | + | |
- | ┌─────│─────────│────┐ | + | |
- | Содержимое │ ┌───▼──┐ ┌───▼──┐ │ Содержимое | + | |
- | младшего ◀───── &Н01 │ │ &HА0 ─────▶ старшего | + | |
- | байта │ └──────┘ └──────┘ │ байта | + | |
- | └────────────────────┘ | + | |
- | Слово TXTTAB | + | |
- | </code> | + | |
- | + | ||
- | Очевидно, что в результате этих "манипуляций" размер свободной области уменьшится на &H2000 байт (&HA000-&H8000=&H2000), и область, расположенная между &H8000 и началом PIT, будет защищена от "вторжения" программ на [[msx:basic:]], и следовательно, //"безопасна"// для программ на машинном языке. | + | |
- | + | ||
- | Ясно, что величина PIT зависит от размера текста программы. | + | |
- | + | ||
- | После выполнения данной программы нажмите кнопку сброса <key>RESET</key>. | + | |
- | + | ||
- | А теперь мы расскажем Вам о том, как хранится программа, написанная на языке [[msx:basic:]] в PIT. | + | |
- | + | ||
- | Все строки программы на [[msx:basic:]] начинаются с //двухбайтового// указателя. За этим указателем идут //два// байта, содержащие номер строки. Затем идёт текст строки с последующим нулевым байтом. | + | |
- | + | ||
- | За последней строкой следуют //два// дополнительных нулевых байта адрес которых находится в указателе последней строки программы. | + | |
- | + | ||
- | Цифры и зарезервированные служебные слова записываются во внутреннем коде (один или два байта на слово, цифру) | + | |
- | + | ||
- | Для остального текста используется код ASCII. | + | |
- | + | ||
- | __//Пример 2//__. Введём в память следующую короткую программу: | + | |
- | <code> | + | |
- | 10 B=5 | + | |
- | 20 END | + | |
- | </code> | + | |
- | + | ||
- | Теперь прочитаем, что же реально содержится в PIT, используя в непосредственном режиме команду | + | |
- | <WRAP group> | + | |
- | <WRAP half column> | + | |
- | <code> | + | |
- | PRINT HEX$(PEEK(A)) | + | |
- | </code> | + | |
- | </WRAP> | + | |
- | <WRAP half column> | + | |
- | , | + | |
- | </WRAP> | + | |
- | </WRAP> | + | |
- | где значение переменной А (адреса) изменяется от &H8000 до &H8010. | + | |
- | + | ||
- | Вы обнаружите: | + | |
- | ^ Значение А ^ HEX$(PEEK(A)) ^ Комментарии ^ | + | |
- | | 8000 | 0 |Первый байт PIT всегда нулевой| | + | |
- | | 8001 | 09 |Указатель первой строки "говорит" нам, что указатель следующей строки находится по адресу &Н8009| | + | |
- | | 8002 | 80 |:::| | + | |
- | | 8003 | А |Номер первой строки &H000А = 10| | + | |
- | | 8004 | 0 |:::| | + | |
- | | 8005 | 42 |Шестнадцатеричный код ASCII буквы "B"| | + | |
- | | 8006 | EF |Внутренний код знака равенства| | + | |
- | | 8007 | 16 |Внутренний код цифры 5| | + | |
- | | 8008 | 0 |Конец первой строки| | + | |
- | | 8009 | 0F |Указатель второй строки показывает, что указатель следующей строки находится по адресу &H800F| | + | |
- | | 800A | 80 |:::| | + | |
- | | 800B | 14 |Номер второй строки &H0014 = 20| | + | |
- | | 800C | 00 |:::| | + | |
- | | 800D | 81 |Внутренний код оператора ''END''| | + | |
- | | 800E | 0 |Конец второй строки| | + | |
- | | 800F | 0 |Конец программы| | + | |
- | | 8010 | 0 |:::| | + | |
- | + | ||
- | Теперь, надеемся, Вам стало ясно, как можно изменить программу с помощью оператора ''POKE''. | + | |
- | + | ||
- | Попробуйте выполнить следующее: | + | |
- | <code> | + | |
- | POKE &H8005,&H41 '41 - шестнадцатеричный код ASCII буквы "A" | + | |
- | POKE &H8007,&H17 '17 - внутренний код цифры "6" | + | |
- | </code> | + | |
- | А теперь наберите команду ''LIST'', затем нажмите клавишу <key>'Ввод '⏎</key> и … : | + | |
- | <code> | + | |
- | 10 A=6 | + | |
- | 20 END | + | |
- | </code> | + | |
- | + | ||
- | __//Пример 3//__. | + | |
- | + | ||
- | Теперь Вам ясно, что "инструкции" ''PEEK'' и ''POKE'' таят в себе поистине безграничные возможности. По существу, они позволяют нам распоряжаться памятью компьютера по своему усмотрению. | + | |
- | + | ||
- | Например, они позволяют нам при желании подшутить над компьютером: если известно, где хранится программа, то мы можем сделать так, что после одной из строк программы окажется строка с //меньшим// номером. | + | |
- | + | ||
- | Пусть исходная программа имеет вид: | + | |
- | <code> | + | |
- | 10 PRINT 4 | + | |
- | 20 PRINT 2 | + | |
- | </code> | + | |
- | + | ||
- | Вам, конечно, уже известно, что строки программы на языке [[msx:basic:]] начинаются с двухбайтового указателя, за которым следуют два байта, содержащие номер строки. Поэтому вначале выполним команду: | + | |
- | <code> | + | |
- | PRINT HEX$(PEEK(&H8002));" ";HEX$(PEEK(&H8001)) | + | |
- | 80 9 | + | |
- | Ok | + | |
- | </code> | + | |
- | + | ||
- | Таким образом, указатель следующей (с номером 20) строки располагается в ячейках с адресами &H8009 и &H800A, а следовательно, номер второй строки находится в ячейках с адресами &H800B и &H800C . | + | |
- | Проверим этот факт: | + | |
- | <code> | + | |
- | PRINT HEX$(PEEK(&H800C));" ";HEX$(PEEK(&H800B)) ┌──▶ PRINT &H14 | + | |
- | 0 14 ────────────────────────────────────────────┘ 20 | + | |
- | Ok Ok | + | |
- | + | ||
- | А теперь: | + | |
- | POKE &H800B,1 | + | |
- | Ok | + | |
- | list | + | |
- | 10 PRINT 4 | + | |
- | 1 PRINT 2 | + | |
- | Ok | + | |
- | </code> | + | |
- | + | ||
- | Программа действует, но строку с номером 1 нельзя ни стереть, ни исправить. Вы можете написать ещё одну 1–ю строку и даже новую 20–ю строку! | + | |
- | + | ||
- | {{anchor:e103-04}} __//Пример 4//__. \\ Введём в память короткую программу: | + | |
- | \\ {{.examples:103-04.bas|}} \\ [[+tab|wmsxbpge>103-04.bas]] | + | |
- | <code> | + | |
- | 10 FOR AB=-2.23227 TO 7 STEP 32.671533782376# | + | |
- | </code> | + | |
- | Теперь "просмотрим" содержимое PIT, используя в непосредственном режиме простейшие команды: | + | |
- | <WRAP group> | + | |
- | <WRAP half column> | + | |
- | <code> | + | |
- | A=&H8000: PRINT HEX$(PEEK(A)) | + | |
- | </code> | + | |
- | </WRAP> | + | |
- | <WRAP half column> | + | |
- | , | + | |
- | </WRAP> | + | |
- | </WRAP> | + | |
- | где значение переменной А (адреса) изменяется от &H8000 до &H8020. | + | |
- | + | ||
- | Мы обнаружим массу интересных вещей: | + | |
- | ^ Значение А ^ HEX$(PEEK(A)) ^ Комментарии ^ | + | |
- | | 8000 | 0 |Первый байт PIT всегда нулевой| | + | |
- | | 8001 | 21 |Указатель первой строки "говорит" нам, что указатель следующей строки находится по адресу &Н8021| | + | |
- | | 8002 | 80 |:::| | + | |
- | | 8003 | А |Номер первой строки &H000А = 10| | + | |
- | | 8004 | 0 |:::| | + | |
- | | 8005 | 82 |Код служебного слова ''FOR''| | + | |
- | | 8006 | 20 |Внутренний код символа "пробел"| | + | |
- | | 8007 | 41 |Шестнадцатеричный код ASCII буквы "A"| | + | |
- | | 8008 | 42 |Шестнадцатеричный код ASCII буквы "B"| | + | |
- | | 8009 | EF |Внутренний код символа "="| | + | |
- | | 800A | F2 |Внутренний код символа "-"| | + | |
- | | 800B | 1D |Указатель на тип одинарной точности (знак"!")| | + | |
- | | 800C | 41 |Вторая цифра числа указывает на порядок минимального значения параметра цикла| | + | |
- | | 800D | 22 |1 и 2–я цифры мантиссы минимального значения параметра цикла| | + | |
- | | 800E | 32 |3 и 4–я цифры мантиссы минимального значения параметра цикла| | + | |
- | | 800F | 27 |5 и 6–я цифры мантиссы минимального значения параметра цикла| | + | |
- | | 8010 | 20 |Внутренний код символа "пробел"| | + | |
- | | 8011 | D9 |Код служебного слова ''TO''| | + | |
- | | 8012 | 20 |Внутренний код символа "пробел"| | + | |
- | | 8013 | 18 |Внутренний код символа "7"| | + | |
- | | 8014 | 20 |Внутренний код символа "пробел"| | + | |
- | | 8015 | DC |Код служебного слова ''STEP''| | + | |
- | | 8016 | 20 |Внутренний код символа "пробел"| | + | |
- | | 8017 | 1F |Указатель на тип двойная точность (знак #)| | + | |
- | | 8018 | 42 |Вторая цифра числа указывает на порядок шага| | + | |
- | | 8019 | 32 |1 и 2–я цифры мантиссы шага| | + | |
- | | 801А | 67 |3 и 4–я цифры мантиссы шага| | + | |
- | | 801B | 15 |5 и 6–я цифры мантиссы шага| | + | |
- | | 801C | 33 |7 и 8–я цифры мантиссы шага| | + | |
- | | 801D | 78 |9 и 10–я цифры мантиссы шага| | + | |
- | | 801E | 23 |11 и 12–я цифры мантиссы шага| | + | |
- | | 801F | 76 |13 и 14–я цифры мантиссы шага| | + | |
- | | 8020 | 0 |Конец строки| | + | |
- | | 8021 | 0 |Конец программы| | + | |
- | + | ||
- | Однако не пытайтесь изменять с помощью оператора ''POKE'' длину строки или путать указатели: результат будет //катастрофическим//! | + | |
- | + | ||
- | Если Вы хотите защитить свою программу от "постороннего взгляда" (команды ''LIST''), то примените в непосредственном режиме команду: | + | |
- | <code> | + | |
- | POKE &H8001,1 | + | |
- | Ok | + | |
- | </code> | + | |
- | (разумеется, Ваша программа должна располагаться с адреса &H8000). | + | |
- | + | ||
- | Ну а если Вы нечаянно нажали <key>RESET</key>, — не спешите отчаиваться! Вашу программу ещё можно спасти. Это очень легко сделать, набрав ту же команду | + | |
- | <code> | + | |
- | POKE &H8001,1 | + | |
- | + | ||
- | а затем | + | |
- | auto | + | |
- | + | ||
- | На экране появятся строки: | + | |
- | 10* | + | |
- | 20* | + | |
- | и так далее … | + | |
- | </code> | + | |
- | Строки с "*" — спасённые. Теперь достаточно "скомандовать": ''LIST'' и …, о, чудо! Но это ещё не все! Оказывается, спасены и все строки между теми, номера которых не делятся нацело на 10! | + | |
- | + | ||
- | Если же Вы захотите защитить свою программу от запуска (команды ''RUN''), то примените в непосредственном режиме команду: | + | |
- | <code> | + | |
- | POKE &H8000,1 | + | |
- | Ok | + | |
- | </code> | + | |
- | (разумеется, Ваша программа должна располагаться с адреса &H8000). | + | |
- | + | ||
- | ===== X.4. Таблица переменных (VT) ===== | + | |
- | + | ||
- | Непосредственно следующая за PIT таблица VT начинается с адреса, указанного в слове ''VARTAB'', хранящегося по адресу &HF6C2 в области системных переменных (см. Приложение 2((Пока не найдено, подробнее [[start#Список отсутствующего материала|здесь]]))). Её длина зависит от количества используемых переменных (скалярных и массивов) и их типов. | + | |
- | + | ||
- | Отметим, что переменные и массивы хранятся в порядке их создания. | + | |
- | + | ||
- | Будем говорить, что один программный объект "располагается в памяти //выше// другого", если адрес, с которого он расположен, больше. | + | |
- | + | ||
- | <WRAP centeralign>Массивы хранятся //выше// переменных.</WRAP> | + | |
- | + | ||
- | Это значит, что всякий раз, когда интерпретатор встречает новую скалярную переменную, все массивы сдвигаются //"вверх"//, чтобы высвободить пространство. Это может значительно замедлить выполнение программы! | + | |
- | + | ||
- | <WRAP centeralign>Во избежание этого, описывайте все скалярные переменные и массивы в начале программы оператором ''DIM'' !</WRAP> | + | |
- | + | ||
- | {{anchor:varptr}} | + | |
- | Теперь мы расскажем Вам о важной функции ''VARPTR'', которая указывает адрес расположения данных в оперативной памяти. Её синтаксис: | + | |
- | <WRAP group> | + | |
- | <WRAP half column> | + | |
- | <code> | + | |
- | VARPTR(γ) | + | |
- | </code> | + | |
- | </WRAP> | + | |
- | <WRAP half column> | + | |
- | , | + | |
- | </WRAP> | + | |
- | </WRAP> | + | |
- | где: | + | |
- | * ''VARPTR'' ("VARiable PoinTeR" — "указатель переменной") — служебное слово; | + | |
- | * γ — идентификатор //числовой// переменной. | + | |
- | Функция ''VARPTR'' возвращает //адрес// X байта RAM, начиная с которого располагается значение переменной γ. | + | |
- | + | ||
- | Если переменная не существует, то выдаётся сообщение: <WRAP centeralign>"Illegal function call".</WRAP> | + | |
- | + | ||
- | {{anchor:e104-01}} __//Пример//__. Будьте бдительны! \\ {{.examples:104-01.bas|}} \\ [[+tab|wmsxbpge>104-01.bas]] | + | |
- | <code> | + | |
- | 10 INPUT Z | + | |
- | 20 PRINT VARPTR(Z) | + | |
- | run | + | |
- | ? 0 | + | |
- | -32743 | + | |
- | Ok | + | |
- | + | ||
- | run | + | |
- | ? ◀── Нажата клавиша "RETURN" | + | |
- | Illegal function call in 20 | + | |
- | Ok | + | |
- | </code> | + | |
- | + | ||
- | Функцию ''VARPTR'' часто используют совместно с функцией ''PEEK'' и оператором ''POKE'' соответственно для просмотра или изменения значения переменной. | + | |
- | + | ||
- | ==== X.4.1. Хранение простых переменных ==== | + | |
- | + | ||
- | <WRAP group 99%> | + | |
- | <WRAP half column> \\ </WRAP> | + | |
- | <WRAP half column><WRAP justify> | + | |
- | Ты славно роешь землю, старый крот! \\ | + | |
- | Годишься в рудокопы. | + | |
- | <WRAP rightalign> | + | |
- | —//В.Шекспир. Гамлет// | + | |
- | </WRAP></WRAP> | + | |
- | </WRAP></WRAP> | + | |
- | + | ||
- | Как уже неоднократно упоминалось, //целое// число кодируется в двух байтах. Меньший по адресу байт называется //старшим//, больший по адресу байт — //младшим//. | + | |
- | + | ||
- | Однако, напомним Вам, что <WRAP centeralign>процессор Z80 "хранит" младший байт "перед" старшим.</WRAP> | + | |
- | + | ||
- | Когда //целочисленная// переменная получает значение, процессор записывает в оперативную память следующие //пять// байт информации: | + | |
- | - число 2 ("паспорт" ''VALTYPE''), которое означает, что переменная является целочисленной (значение "паспорта" занимает два байта); | + | |
- | - код ASCII первого символа имени переменной; | + | |
- | - код ASCII второго символа имени (0, если имя состоит из одного символа); | + | |
- | - младший байт значения; | + | |
- | - старший байт значения. | + | |
- | <code> | + | |
- | ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐ | + | |
- | │ 1–й байт │ │ 2–й байт │ │ 3–й байт │ │ 4–й байт │ │ 5–й байт │ | + | |
- | └─────▲─────┘ └─────▲─────┘ └─────▲─────┘ └─────▲─────┘ └─────▲─────┘ | + | |
- | │ │ │ │ │ | + | |
- | │ Код ASCII Код ASCII Младший байт Старший байт | + | |
- | VALTYPE первого символа второго символа значения значения | + | |
- | имени переменной имени переменной | + | |
- | </code> | + | |
- | + | ||
- | Оказывается, что адрес четвёртого байта (младшего байта значения числовой переменной) возвращает как раз переменная ''VARPTR''! Кроме того,напомним, что "содержимое" байта с известным адресом может быть "прочитано"функцией ''PEEK''. | + | |
- | + | ||
- | Перед тем как работать с нижеприведённым примером, во избежание расхождений в результатах не забудьте "почистить" память компьютера оператором ''CLEAR''. | + | |
- | + | ||
- | __//Пример 1//__ | + | |
- | <code> | + | |
- | A%=356:PRINT HEX$(VARPTR(A%)) | + | |
- | 8006 | + | |
- | Ok | + | |
- | </code> | + | |
- | Вы получили шестнадцатеричный адрес младшего байта значения переменной. | + | |
- | + | ||
- | |<code>? РЕЕК(&Н8003)</code>|Этот адрес соответствует байту ''VALTYPE''. Поэтому Вы должны получить 2.| | + | |
- | |<code>? РЕЕК(&Н8004)</code>|Этот адрес соответствует первому символу имени переменной. Вы должны получить число 65, которое является кодом ASCII символа "А".| | + | |
- | |<code>? PEEK(&H8005)</code>|Получили число 0, поскольку имя переменной состоит лишь из одного символа.| | + | |
- | |<code>? РЕЕК(&Н8006)</code>|Этот адрес, возвращённый функцией ''VARPTR'', соответствует младшему байту значения. Должно быть получено число 100.| | + | |
- | |<code>? РЕЕК(&Н8007)</code>|Этот адрес соответствует старшему байту значения. Вы, конечно же, получите число 1.| | + | |
- | Перепишем два последних значения в двоичной системе счисления: | + | |
- | <code> | + | |
- | 100 = 0110 0100₂ | + | |
- | 1 = 0000 0001₂ | + | |
- | </code> | + | |
- | А теперь примем во внимание инверсию порядка байт: | + | |
- | <code> | + | |
- | 0000 0001 0110 0100 ──▶ 356₁₀ | + | |
- | ────▲──── ────▲──── | + | |
- | │ │ | + | |
- | 1 100 | + | |
- | </code> | + | |
- | И перед выполнением следующего примера не забудьте ввести в память компьютера оператор ''CLEAR'' ! | + | |
- | + | ||
- | __//Пример 2//__. | + | |
- | <code> | + | |
- | A%=-356:PRINT HEX$(VARPTR(A%)) | + | |
- | 8006 | + | |
- | Ok | + | |
- | </code> | + | |
- | + | ||
- | Полученный результат — это шестнадцатеричный адрес младшего байта. | + | |
- | |<code>? РЕЕК(&Н8003)</code>|Вы должны получить 2.| | + | |
- | |<code>? РЕЕК(&Н8004)</code>|Этот адрес соответствует первому символу имени переменной. Вы должны получить 65 (код ASCII символа "A").| | + | |
- | |<code>? PEEK(&H8005)</code>|0, т.к. имя переменной состоит лишь из одного символа.| | + | |
- | |<code>? РЕЕК(&Н8006)</code>|Этот адрес, возвращаемый функцией ''VARPTR'', соответствует инвертированному младшему байту значения. Разумеется, Вы получите число 156.| | + | |
- | |<code>? РЕЕК(&Н8007)</code>|Этот адрес соответствует инвертированному старшему байту значения. Вы получите число 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. Ура! | + | |
- | + | ||
- | Приведём схему расположения информации в памяти для простой числовой переменной //одинарной// и //двойной// точности: | + | |
- | <code> | + | |
- | Байт, адрес которого возвращает функция VARPTR() | + | |
- | │ | + | |
- | ┌───┬───┬───┬─▼─┬───┬───┬───┬───┐ | + | |
- | │▧▧▧│▧▧▧│▧▧▧│∗∗∗│███│███│ … │███│ | + | |
- | └───┴───┴───┴───┴───┴───┴───┴───┘ | + | |
- | Адреса──▶ X-3 X-2 X-1 X └───────▲ … ────┘ | + | |
- | ▲ ───▲─── ▲ │ | + | |
- | │ │ │ Значение | + | |
- | │ │ Знак и порядок | + | |
- | │ Идентификатор | + | |
- | Тип переменной | + | |
- | </code> | + | |
- | + | ||
- | Прежде чем проверить работу ниже приведённой командной строки,наберите команду ''CLEAR''. | + | |
- | + | ||
- | __//Пример 3//__. | + | |
- | <code> | + | |
- | AR!=22.3210E+4:PRINT HEX$(VARPTR(AR!)) | + | |
- | 8006 | + | |
- | Ok | + | |
- | </code> | + | |
- | + | ||
- | Если Вы прочитаете адреса с &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 | Бит знака мантиссы| | + | |
- | + | ||
- | Величина порядка задаётся формулой: <WRAP centeralign>Порядок = Двоичное значение 7 младших битов — 64.</WRAP> | + | |
- | + | ||
- | Первый бит байта содержит //знак мантиссы//, причём 0 соответствует знаку "+", а 1 соответствует знаку "-". | + | |
- | Продолжим наши исследования: | + | |
- | |&H8007|34 = &B 0010 0010 = 22 в двоично–десятичной системе| | + | |
- | |&H8008|50 = &B 0011 0010 = 32 в двоично–десятичной системе| | + | |
- | |&H8009|16 = &B 0001 0000 = 10 в двоично–десятичной системе| | + | |
- | + | ||
- | В трёх последних байтах Вы, конечно же, "узнаете" число 225. | + | |
- | + | ||
- | Итак, три последних адреса содержат мантиссу, записанную в двоично–десятичном виде (.223210). | + | |
- | + | ||
- | <WRAP centeralign>Мантисса числа одинарной точности занимает 3 байта, которые позволяют закодировать 6 разрядов в двоично–десятичном виде.</WRAP> | + | |
- | + | ||
- | Перед тем как выполнить предлагаемые в примере действия,наберите и введите в память компьютера оператор ''CLEAR''. | + | |
- | + | ||
- | __//Пример 4//__. | + | |
- | <code> | + | |
- | AR#=-22.321054981117E-4:PRINT HEX$(VARPTR(AR#)) | + | |
- | 8006 | + | |
- | Ok | + | |
- | </code> | + | |
- | + | ||
- | Если Вы прочитаете адреса с &H8006-&H3=&H8003 по &H8006+&H3=&H8009, то обнаружите: | + | |
- | |&H8003|8|''VALTYPE'' переменной //двойной// точности (её значение занимает 8 байт)| | + | |
- | |&H8004|65|Код ASCII символа "А"| | + | |
- | |&H8005|82|Код ASCII символа "R"| | + | |
- | |&H8006|190|<code> | + | |
- | &B 1011 1110 | + | |
- | ▲ | + | |
- | └── Знак мантиссы отрицательный! | + | |
- | </code>| | + | |
- | + | ||
- | Подсчитаем теперь величину порядка: | + | |
- | <code> | + | |
- | print &B0111110-64 | + | |
- | -2 | + | |
- | Ok | + | |
- | </code> | + | |
- | + | ||
- | "Продолжим наши игры!": | + | |
- | |&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. | + | |
- | + | ||
- | <WRAP centeralign>Мантисса числа двойной точности занимает 7 байт, которые позволяют закодировать 14 разрядов в двоично-десятичном виде.</WRAP> | + | |
- | + | ||
- | Подведём //итоги// всему сказанному о хранении числовых переменных. | + | |
- | + | ||
- | ^ Тип ^ Значение \\ VALTYPE ^ Колич. \\ байт ^ Номера \\ байт ^ Значение (система счисления) ^ Примечание ^ | + | |
- | | Целая | 2 | 5 | -3 | ''VALTYPE=2'' (двоичная) | | + | |
- | |:::|:::|:::| -2 | Первый символ имени (код ASCII) | | + | |
- | |:::|:::|:::| -1 | Второй символ имени (код ASCII) | | + | |
- | |:::|:::|:::| 0 | //Значение// (двоичная); \\ для записи отрицательных чисел применяется двоичный дополнительный код; |"Содержимое" этого байта возвращает функция ''VARPTR()''| | + | |
- | |:::|:::|:::| 1 |:::| | + | |
- | | Одинарной точности | 4 | 7 | -3 | VALTYPE=4 (двоичная) | | + | |
- | |:::|:::|:::| -2 | Первый символ имени (код ASCII) | | + | |
- | |:::|:::|:::| -1 | Второй символ имени (код ASCII) | | + | |
- | |:::|:::|:::| 0 | Порядок и знак (знак в первом бите)\\ (двоичная) \\ Величина порядка = двоичное значение семи последних битов — 64 |"Содержимое" этого байта возвращает функция ''VARPTR()''| | + | |
- | |:::|:::|:::| 1÷3 | //Мантисса// (двоично–десятичная) | | + | |
- | | Двойной точности | 8 | 11 | -3 | VALTYPE = 8 (двоичная) | | + | |
- | |:::|:::|:::| -2 | Первый символ имени (код ASCII) | | + | |
- | |:::|:::|:::| -1 | Второй символ имени (код ASCII) | | + | |
- | |:::|:::|:::| 0 | Порядок и знак (знак в первом бите) \\ (двоичная) \\ Величина порядка = двоичное значение семи последних битов — 64 |"Содержимое" этого байта возвращает функция ''VARPTR()''| | + | |
- | |:::|:::|:::| 1÷7 | //Мантисса// (двоично–десятичная) | | + | |
- | + | ||
- | {{anchor:e1041-05}} __//Пример 5//__. Попробуйте самостоятельно в нем разобраться! \\ {{.examples:1041-05.bas|}} \\ [[+tab|wmsxbpge>1041-05.bas]] | + | |
- | <code> | + | |
- | 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 | + | |
- | Введите число? 0 | + | |
- | Попробуем 'собрать' его из памяти | + | |
- | Вот Ваше число:+.00000000000000E-64 | + | |
- | Ok | + | |
- | + | ||
- | run | + | |
- | Введите число? -23545e37 | + | |
- | Попробуем 'собрать' его из памяти | + | |
- | Вот Ваше число:-.23545000000000E+42 | + | |
- | Ok | + | |
- | </code> | + | |
- | + | ||
- | ==== X.4.2. Хранение элементов числовых массивов ==== | + | |
- | + | ||
- | <WRAP group 99%> | + | |
- | <WRAP half column> \\ </WRAP> | + | |
- | <WRAP half column><WRAP justify> | + | |
- | Что имеем — не храним; потерявши — плачем. | + | |
- | <WRAP rightalign> | + | |
- | —//Козьма Прутков// | + | |
- | </WRAP></WRAP> | + | |
- | </WRAP></WRAP> | + | |
- | + | ||
- | Вначале мы расскажем Вам о том, как хранится в памяти //целочисленный// массив. | + | |
- | + | ||
- | Приведём схемы расположения информации в памяти для целочисленных числовых массивов: | + | |
- | * //одномерный// массив.<code> | + | |
- | Байт, адрес которого возвращает функция VARPTR()──┐ | + | |
- | ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─▼─┬───┬───┬───┬─ | + | |
- | │ │ │ │ │ │ &H1 │ │ │███│███│███│███│ … | + | |
- | └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴───┴───┴───┴───┴─ | + | |
- | X-8 X-7 X-6 X-5 X-4 X-3 X-2 X-1 └──▲───┘└──▲───┘ | + | |
- | ▲ ─────▲───── ────▲──── ▲ ────▲──── │ │ | + | |
- | │ │ │ │ │ Значение Значение | + | |
- | │ │ Служебная инф. │ │ нулевого первого | + | |
- | │ Идентификатор Для одномерного │ элемента элемента | + | |
- | Тип массива массива Количество | + | |
- | элементов в массиве | + | |
- | </code> | + | |
- | * //двухмерный// массив.<code> | + | |
- | Байт, адрес которого возвращает функция VARPTR()──┐ | + | |
- | ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─▼─┬───┬─ | + | |
- | │ │ │ │ │ │ &H2 │ │ │ │ │███│███│ … | + | |
- | └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴───┴───┴─ | + | |
- | X-10 X-9 X-8 X-7 X-6 X-5 X-4 X-3 X-2 X-1 └───▲───┘ | + | |
- | ▲ ─────▲───── ─────▲───── ▲ ─────▲───── ─────▲───── │ | + | |
- | │ │ │ │ │ │ Значение | + | |
- | │ │ Служебная инф. │ Количество Количество элемента | + | |
- | │ Идентификатор Для двухмерного столбцов строк (0,0) | + | |
- | Тип массива массива | + | |
- | </code> | + | |
- | * для //элементов// целочисленных числовых массивов.<code> | + | |
- | ┌─── Байт, адрес которого возвращает функция VARPTR() | + | |
- | ┌─▼─┬───┬───┬───┐ | + | |
- | │███│███│ … │███│ | + | |
- | └───┴───┴───┴───┘ | + | |
- | └───────▲───────┘ | + | |
- | │ | + | |
- | Значение | + | |
- | </code> | + | |
- | + | ||
- | Не забыли ли Вы набрать и ввести в память компьютера оператор ''CLEAR''? | + | |
- | + | ||
- | __//Пример//__. | + | |
- | <code> | + | |
- | DIM C5%(2):C5%(0)=32000:C5%(1)=13:C5%(2)=-4 | + | |
- | Ok | + | |
- | print HEX$(VARPTR(C5%(0))-8) | + | |
- | 8003 | + | |
- | Ok | + | |
- | </code> | + | |
- | + | ||
- | |<code>? РЕЕК(&Н8003)</code>|Этот адрес соответствует байту ''VALTYPE''. Поэтому Вы должны получить 2.| | + | |
- | |<code>? РЕЕК(&Н8004)</code>|Этот адрес соответствует первому символу имени массива. \\ Вы получите число 67, которое является кодом ASCII символа "C".| | + | |
- | |<code>? PEEK(&H8005)</code>|Этот адрес соответствует второму символу имени массива Вы получите число 53, которое является кодом ASCII символа "5".| | + | |
- | |<code>? РЕЕК(&Н8006)</code>|9 (служебная информация)| | + | |
- | |<code>? РЕЕК(&Н8007)</code>|0 (служебная информация)| | + | |
- | |<code>? PEEK(&H8008)</code>|Массив C5% — одномерный, поэтому мы получили 1.| | + | |
- | |<code>? PEEK(&H8009)</code>|3 = 00000011₂| | + | |
- | |<code>? PEEK(&H800А)</code>|0 = 00000000₂| | + | |
- | + | ||
- | Теперь можно найти количество элементов в массиве: 00000000 00000011₂ =3 | + | |
- | + | ||
- | |<code>? PEEK(&H800B)</code>|0 = 00000000₂| | + | |
- | |<code>? PEEK(&H800C)</code>|125 = 01111101₂| | + | |
- | + | ||
- | А тогда нулевой элемент массива равен: 01111101 00000000₂ = 32000 | + | |
- | |<code>? PEEK(&H800D)</code>|13 = 00001101₂| | + | |
- | |<code>? PEEK(&H800E)</code>|0| | + | |
- | + | ||
- | Добрались до первого элемента массива: 00000000 00001101₂ = 13 | + | |
- | |<code>? PEEK(&H800F)</code>|252 = 11111100₂| | + | |
- | |<code>? PEEK(&H8010)</code>|255 = 11111111₂| | + | |
- | + | ||
- | Далее 11111111 11111100₂ → 00000000 00000011₂ + 1 → 00000000 00000100₂ = 4 | + | |
- | + | ||
- | Приведём схемы расположения информации в памяти для нецелочисленных числовых массивов: | + | |
- | * //одномерный// массив.<code> | + | |
- | Байт, адрес которого возвращает функция VARPTR() ──┐ | + | |
- | ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬──▼──┬───┬───┬───┬── | + | |
- | │ │ │ │ │ │ &H1 │ │ │ ∗∗∗ │███│ … │███│ … | + | |
- | └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴───┴───┴───┴── | + | |
- | X-8 X-7 X-6 X-5 X-4 X-3 X-2 X-1 X └─────▲─────┘ | + | |
- | ▲ ─────▲───── ────▲──── ▲ ────▲──── ▲ │ | + | |
- | │ │ │ │ │ │ Значение нулевого | + | |
- | │ │ Служебная инф. │ │ Знак и порядок элемента | + | |
- | │ Идентификатор Для одномерного │ нулевого элемента | + | |
- | Тип массива массива │ | + | |
- | Количество элементов в массиве | + | |
- | </code> | + | |
- | * //двухмерный// массив.<code> | + | |
- | Байт, адрес которого возвращает функция VARPTR() ──┐ | + | |
- | ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬──▼──┬─ | + | |
- | │ │ │ │ │ │ &H2 │ │ │ │ │ ∗∗∗ │ … | + | |
- | └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─ | + | |
- | X-10 X-9 X-8 X-7 X-6 X-5 X-4 X-3 X-2 X-1 X | + | |
- | ▲ ─────▲───── ─────▲───── ▲ ─────▲───── ─────▲───── ▲ | + | |
- | │ │ │ │ │ │ │ | + | |
- | │ │ Служебная инф. │ Количество │ Знак и порядок | + | |
- | │ Идентификатор Для двухмерного столбцов │ нулевого элем. | + | |
- | Тип массива массива Количество строк | + | |
- | </code> | + | |
- | * для //элементов// нецелочисленных числовых массивов.<code> | + | |
- | ┌─── Байт, адрес которого возвращает функция VARPTR() | + | |
- | ┌─▼─┬───┬───┬───┬───┐ | + | |
- | │∗∗∗│███│███│ … │███│ | + | |
- | └───┴───┴───┴───┴───┘ | + | |
- | X └───────▲───────┘ | + | |
- | ▲ │ | + | |
- | │ Значение | + | |
- | Знак и порядок | + | |
- | </code> | + | |
- | + | ||
- | Думаем, что теперь Вы в состоянии самостоятельно разобраться с вопросами, касающимися "хранения" в RAM многомерных (двухмерных, трёхмерных и т.д.) вещественных числовых массивов! | + | |
- | + | ||
- | ===== X.5. Стек ===== | + | |
- | + | ||
- | <WRAP group 99%> | + | |
- | <WRAP half column> \\ </WRAP> | + | |
- | <WRAP half column><WRAP justify> | + | |
- | А люди все роптали и роптали, \\ | + | |
- | А люди справедливости хотят: \\ | + | |
- | — Мы в очереди первые стояли, \\ | + | |
- | А те, кто сзади нас,— уже едят. | + | |
- | <WRAP rightalign> | + | |
- | —//В.Высоцкий// | + | |
- | </WRAP></WRAP> | + | |
- | </WRAP></WRAP> | + | |
- | + | ||
- | //Стек// (от англ. "stack" — "стог", "груда") — структура данных или устройство памяти для хранения наращиваемой и сокращаемой последовательности значений, в которой в любой момент доступен только последний член последовательности. Примером //стека// является стопка книг на столе, в которой брать и класть книги можно только сверху ("Математический Энциклопедический Словарь"). | + | |
- | + | ||
- | //Стек// используется как программой на [[msx:basic:]], так и подпрограммами на машинном языке. | + | |
- | + | ||
- | "Вершина" стека указывается в слове ''STKTOP(&HF674)''. Его позиция зависит от размеров строкового пространства и блоков управления файлами, а также от второго аргумента оператора ''CLEAR'' (если этот оператор был выполнен). | + | |
- | + | ||
- | Если Вы хотите получить такие же результаты, как в последующем примере, воспользуйтесь командой ''CLEAR''! | + | |
- | + | ||
- | __//Пример 1//__. | + | |
- | + | ||
- | Рассмотрим структуру расположения информации о цикле ''FOR''. | + | |
- | + | ||
- | * <code> | + | |
- | 10 FOR AB%=2 TO 7 STEP 15 'Оператора NEXT быть не должно! | + | |
- | run | + | |
- | Ok | + | |
- | + | ||
- | PRINT HEX$(PEEK(&HF675))+HEX$(PEEK(&HF674)) | + | |
- | F0A0 | + | |
- | Ok | + | |
- | </code><code> | + | |
- | Адрес значения AB% в VT | + | |
- | Адреса байт │ ┌─ FF◀── для отрицательного шага | + | |
- | │ ┌────┬─────▼─────┬──▼─┬────┬─────┬─────┬─────┬─────┬─────┬─────┬───┬─ | + | |
- | │ │&H82│ &H1B│ &H80│ &H1│&HFF│ … │ &HF │ &H0 │ &H7 │ &H0 │ &HA │&H0│ | + | |
- | │ └────┴─────┴─────┴────┴────┴─────┴─────┴─────┴─────┴─────┴─────┴───┴─ | + | |
- | └─▶ X-27 X-26 X-25 X-24 X-23 X-10 X-9 X-8 X-7 X-6 X-5 | + | |
- | ▲ ────────── ▲ ▲ ─────▲──── ─────▲───── ────▲──── | + | |
- | │ │ │ │ │ │ | + | |
- | │ Знак шага │ Шаг (со знаком) Верхний Номер | + | |
- | Код оператора FOR Тип параметра цикла предел строки | + | |
- | ┌── Адрес данного байта &HF0A0 | + | |
- | ─┬────┬─────┬───┬──▼──┐ | + | |
- | │&H15│ &H80│ … │ &HFF│ | + | |
- | ─┴────┴─────┴───┴─────┘ | + | |
- | X-4 X-3 X | + | |
- | ─────▲───── ▲ | + | |
- | │ └── "Вершина" стека | + | |
- | Адрес конца программной строки | + | |
- | </code> | + | |
- | + | ||
- | Перед выполнением следующего примера наберите команду CLEAR! | + | |
- | + | ||
- | * <code> | + | |
- | 10 FOR AB=2.7 TO 7 STEP-32.671533782376 | + | |
- | run | + | |
- | Ok | + | |
- | + | ||
- | PRINT HEX$(PEEK(&HF675))+HEX$(PEEK(&HF674)) | + | |
- | F0A0 | + | |
- | Ok | + | |
- | </code><code> | + | |
- | Адрес параметра цикла AB в VT, увеличенный на 2 | + | |
- | │ | + | |
- | ┌─────┬────▼──────┬─────┬──────┬──────┬─ | + | |
- | │ &H82│&H25│ &H80 │ &HFF│ &H5 │ &HC2 │ | + | |
- | └─────┴────┴──────┴─────┴──────┴──────┴─ | + | |
- | X-27 X-26 X-25 X-24 X-23 X-22 ◀── Адреса байт | + | |
- | ▲ ────────── ▲ ▲ ▲ | + | |
- | │ │ │ └── Порядок и знак шага | + | |
- | Код оператора FOR │ └───────── Тип параметра цикла | + | |
- | └─────────────── Знак шага | + | |
- | ─┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─ | + | |
- | │ &H32│ &H67│ &H15│ &H33│ &H78│ &H23│ &H76│ &H41│ | + | |
- | ─┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─ | + | |
- | X-21 X-20 X-19 X-18 X-17 X-16 X-15 X-14 | + | |
- | ────────────────────▲─────────────────── ▲ | + | |
- | │ │ | + | |
- | Мантисса шага Порядок и знак верхнего предела | + | |
- | ┬──────┬────┬────┬────┬───┬───┬───┬─────┬───┬─────┬─────┬───┬────┐ | + | |
- | │ &H70 │ &H0│ &H0│ &H0│&H0│&H0│&H0│ &HA │&H0│ &H1F│ &H80│ … │&HFF│ | + | |
- | ┴──────┴────┴────┴────┴───┴───┴───┴─────┴───┴─────┴─────┴───┴────┘ | + | |
- | X-13 X-12 X-11 X-10 X-9 X-8 X-7 X-6 X-5 X-4 X-3 X | + | |
- | ────────────────────▲───────────── ───▲─── ────▲──── | + | |
- | │ │ │ | + | |
- | Мантисса верхнего предела │ Адрес конца программной строки | + | |
- | Номер строки оператора FOR | + | |
- | </code> | + | |
- | Не забудьте о команде ''CLEAR''! | + | |
- | * <code> | + | |
- | 10 FOR AB!=2.7 TO 7 STEP-32.6715 | + | |
- | run | + | |
- | Ok | + | |
- | + | ||
- | PRINT HEX$(PEEK(&HF675))+HEX$(PEEK(&HF674)) | + | |
- | F0A0 | + | |
- | Ok | + | |
- | </code><code> | + | |
- | Адрес параметра цикла AB! в VT, увеличенный на 2 | + | |
- | │ | + | |
- | ┌────┬────▼──────┬─────┬─────┬───┬────┬────┬─────┬─────┬─────┬─────┬─ | + | |
- | │&H82│&H22│ &H80 │ &HFF│ &H1 │ … │&HC2│&H32│ &H67│ &H15│ &H0 │ &H0 │ | + | |
- | └────┴────┴──────┴─────┴─────┴───┴────┴────┴─────┴─────┴─────┴─────┴─ | + | |
- | X-27 X-26 X-25 X-24 X-23 X-14 X-13 X-12 X-11 X-10 X-9 | + | |
- | ▲ ────────── ─▲─ ─▲── ▲ ────────▲─────── ────▲──── | + | |
- | │ │ │ │ │ │ | + | |
- | Код FOR Знак шага │ Знак и порядок шага │ 3÷6–я цифры мантиссы | + | |
- | │ Мантисса шага верхнего | + | |
- | Тип параметра цикла предела | + | |
- | + | ||
- | ─┬─────┬─────┬─────┬─────┬─────┬─────┬───┬─────┐ | + | |
- | │ &H41│ &H70│ &HA │ &H0 │ &H1C│ &H80│ … │ &HFF│ | + | |
- | ─┴─────┴─────┴─────┴─────┴─────┴─────┴───┴─────┘ | + | |
- | X-8 X-7 X-6 X-5 X-4 X-3 X | + | |
- | ▲ ─▲─ ─────▲───── ─────▲───── ▲ | + | |
- | │ │ │ │ └─"Вершина" стека | + | |
- | │ │ │ Адрес конца строки | + | |
- | │ │ Номер строки | + | |
- | │ 1÷2–я цифры мантиссы верхнего предела | + | |
- | Знак и порядок верхнего предела | + | |
- | </code> | + | |
- | Хотите получить те же результаты — пользуйтесь оператором ''CLEAR''! | + | |
- | + | ||
- | * Пример под рубрикой: "Стек в действии!" <code> | + | |
- | 10 FOR AB%=2 TO 7:NEXT ◀── Цикл закрыт! | + | |
- | 20 FOR I%=3 TO 9 ◀── Цикл не закрыт! | + | |
- | run | + | |
- | Ok | + | |
- | + | ||
- | PRINT HEX$(PEEK(&HF675))+HEX$(PEEK(&HF674)) | + | |
- | F0A0 | + | |
- | Ok | + | |
- | </code><code> | + | |
- | Адрес текущего значения I% в VT | + | |
- | Адреса байт │ ┌─ FF◀── для отрицательного шага | + | |
- | │ ┌────┬─────▼─────┬──▼─┬────┬─────┬─────┬─────┬─────┬─────┬─────┬───┬─ | + | |
- | │ │&H82│ &H2C│ &H80│ &H1│&HFF│ … │ &H1 │ &H0 │ &H9 │ &H0 │ &H14│&H0│ | + | |
- | │ └────┴─────┴─────┴────┴────┴─────┴─────┴─────┴─────┴─────┴─────┴───┴─ | + | |
- | └─▶ X-27 X-26 X-25 X-24 X-23 X-10 X-9 X-8 X-7 X-6 X-5 | + | |
- | ▲ ────────── ▲ ▲ ─────▲──── ─────▲───── ────▲──── | + | |
- | │ │ │ │ │ │ | + | |
- | Код оператора FOR Знак шага │ Шаг (со знаком) Верхний Номер | + | |
- | Тип параметра цикла предел строки | + | |
- | ┌── Адрес этого байта: &HF0A0 | + | |
- | ─┬────┬─────┬───┬──▼──┐ | + | |
- | │&H21│ &H80│ … │ &HFF│ | + | |
- | ─┴────┴─────┴───┴─────┘ | + | |
- | X-4 X-3 X | + | |
- | ─────▲───── ▲ | + | |
- | │ └── "Вершина" стека | + | |
- | Адрес конца программной строки | + | |
- | </code> | + | |
- | Отметим, что для версии MSX Disk BASIC с отключённым дисководом B при нулевой длине строковой области максимальное число вложенных циклов равно 576. | + | |
- | + | ||
- | А теперь настала очередь оператора ''GOSUB''… | + | |
- | + | ||
- | Тем не менее, о команде ''CLEAR'' забывать не стоит! | + | |
- | + | ||
- | __//Пример 2//__. | + | |
- | <code> | + | |
- | 10 GOSUB 30:INPUT A | + | |
- | 20 'Просто комментарий! | + | |
- | 30 'Еще один комментарий! | + | |
- | run | + | |
- | Ok | + | |
- | + | ||
- | PRINT HEX$(PEEK(&HF675))+HEX$(PEEK(&HF674)) | + | |
- | F0A0 | + | |
- | Ok | + | |
- | </code><code> | + | |
- | ┌── Адрес этого | + | |
- | ┌────┬────┬────┬────┬────┬────┬────┬────┬────┬──▼─┐ байта: &HF0A0 | + | |
- | │&H8D│ &H0│ &H0│&H0A│ &H0│&h0A│&h80│ &H0│ &H0│&HFF│ | + | |
- | └────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘ | + | |
- | X-9 X-8 X-7 X-6 X-5 X-4 X-3 X-2 X-1 X | + | |
- | ─▲─ ────▲──── ───▲──── ─▲─ | + | |
- | │ │ │ └─ Вершина стека | + | |
- | Код GOSUB │ Адрес следующего оператора | + | |
- | Номер текущей строки | + | |
- | </code> | + | |
- | + | ||
- | {{anchor:e105-03}} __//Пример 3//__. \\ Работу этих двух программ Вы должны проверить на ученическом дисплее. | + | |
- | - {{.examples:105-031.bas|}} \\ [[+tab|wmsxbpge>105-031.bas]]<code> | + | |
- | Ok | + | |
- | 10 GOSUB 30:PRINT 1:END | + | |
- | 20 PRINT 2:END | + | |
- | 30 'POKE (&HF0A0-4),&H10 | + | |
- | 40 RETURN | + | |
- | run | + | |
- | 1 | + | |
- | Ok | + | |
- | </code> | + | |
- | - {{.examples:105-032.bas|}} \\ [[+tab|wmsxbpge>105-032.bas]]<code> | + | |
- | Ok | + | |
- | 10 GOSUB 30:PRINT 1:END | + | |
- | 20 PRINT 2:END | + | |
- | 30 POKE (&HF0A0-4),&H10'Адрес перехода | + | |
- | 40 RETURN 'изменен с адреса &H800A на адрес &H8010 | + | |
- | run | + | |
- | 2 | + | |
- | Ok | + | |
- | </code> | + | |
- | + | ||
- | ===== X.6. Хранение строковых величин ===== | + | |
- | + | ||
- | Функция ''VARPTR'' указывает адрес расположения строковых данных в оперативной памяти. Она имеет следующий синтаксис: | + | |
- | <WRAP group> | + | |
- | <WRAP half column> | + | |
- | <code> | + | |
- | VARPTR(γ) | + | |
- | </code> | + | |
- | </WRAP> | + | |
- | <WRAP half column> | + | |
- | , | + | |
- | </WRAP> | + | |
- | </WRAP> | + | |
- | где: | + | |
- | * ''VARPTR'' ("VARiable PoinTeR" — "указатель переменной") — служебное слово; | + | |
- | * γ — идентификатор //строковой// переменной. | + | |
- | Если переменная не существует, то выдаётся сообщение: <WRAP centeralign>"Illegal function call".</WRAP> | + | |
- | + | ||
- | Функция ''VARPTR'' возвращает число X — //адрес// байта, находящегося на 3 позиции правее той, с которой располагается информация о переменной γ. | + | |
- | + | ||
- | Пусть γ — простая строковая переменная. Изобразим "кусочек" памяти в окрестности байта с адресом X: | + | |
- | + | ||
- | <code> | + | |
- | ┌── Байт, адрес которого возвращает функция | + | |
- | ┌───┬───┬───┬─▼─┬───┬───┐ VARPTR(γ) | + | |
- | │▧▧▧│▧▧▧│▧▧▧│∗∗∗│▧▧▧│▧▧▧│ | + | |
- | └───┴───┴───┴───┴───┴───┘ | + | |
- | Адреса──▶ X-3 X-2 X-1 X X+1 X+2 | + | |
- | ▲ └───▲───┘ ▲ └───▲───┘ | + | |
- | │ │ │ └─ Ссылка на адрес в PIT или на адрес в строковой области | + | |
- | Тип переменной │ └── Байт длины | + | |
- | Идентификатор | + | |
- | </code> | + | |
- | + | ||
- | Напомним Вам, что в программировании //ссылка// — содержимое ячейки памяти, воспринимаемое как адрес некоторой другой ячейки. | + | |
- | + | ||
- | Указатели строковых переменных хранятся в VT. Они занимают 6 байт, причём: | + | |
- | * один байт содержит "паспорт" переменной ''VALTYPE'' (число 3); | + | |
- | * два байта содержат имя строковой переменной; | + | |
- | * один байт содержит длину строки, возвращаемую функцией ''VARPTR(γ)''(таким образом, обе функции ''LEN(A$)'' и ''PEEK(VARPTR(A$))'' возвращают одно и тоже значение). Приведём простой пример: <code> | + | |
- | 10 A$="карандаш":PRINT LEN(A$);PEEK(VARPTR(A$)) | + | |
- | run | + | |
- | 8 8 | + | |
- | Ok | + | |
- | </code> | + | |
- | * следующие два байта указывают адрес первого байта строки. Если символьная переменная создаётся явным присваиванием символьной константы, то указатель задаёт адрес этой константы в PIT. Лишь затем [[msx:basic:]] //может// переслать значение этой символьной переменной в зарезервированное для неё строковое пространство. | + | |
- | + | ||
- | + | ||
- | {{anchor:e106-01}} __//Пример 1//__. "Сборка" значения строковой переменной A$ из памяти. \\ {{.examples:106-01.bas|}} \\ [[+tab|wmsxbpge>106-01.bas]] | + | |
- | <code> | + | |
- | 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 | + | |
- | </code> | + | |
- | + | ||
- | {{anchor:e106-02}} __//Пример 2//__. \\ {{.examples:106-02.bas|}} \\ [[+tab|wmsxbpge>106-02.bas]] | + | |
- | <code> | + | |
- | 10 A$="ABCD" | + | |
- | run | + | |
- | Ok | + | |
- | PRINT HEX$(VARPTR(A$)) | + | |
- | 8014 ◀── Это адрес байта, содержащего длину A$ | + | |
- | Ok | + | |
- | </code> | + | |
- | Затем выполните команду ''PRINT PEEK(AD)'', где значение переменной AD изменяется от &H8011 до &H8016. Вы получите: | + | |
- | |<code>PRINT PEEK(&H8011)</code>|3|''VALTYPE''| | + | |
- | |<code>PRINT PEEK(&H8012)</code>|65| Код ASCII символа "A"| | + | |
- | |<code>PRINT PEEK(&H8013)</code>|0|Второй символ отсутствует| | + | |
- | |<code>PRINT PEEK(&H8014)</code>|4|Длина значения A$| | + | |
- | |<code>PRINT PEEK(&H8015)</code>|9 = 0000 1001₂|Адрес (младший байт)| | + | |
- | |<code>PRINT PEEK(&H8016)</code>|128 = 1000 0000₂|Адрес (старший байт)| | + | |
- | + | ||
- | 1000 0000 0000 1001₂ = &H8009 | + | |
- | + | ||
- | Итак, строка помещается в PIT по адресу &H8009. | + | |
- | + | ||
- | Все остальное очевидно! | + | |
- | <code> | + | |
- | ? PEEK(&H8009) ? PEEK(&H800A) ? PEEK(&H800B) ? PEEK(&H800C) | + | |
- | 65 66 67 68 ◀── Код ASCII "D" | + | |
- | Ok Ok Ok Ok | + | |
- | </code> | + | |
- | + | ||
- | {{anchor:e106-03}} __//Пример 3//__. \\ {{.examples:106-03.bas|}} \\ [[+tab|wmsxbpge>106-03.bas]] | + | |
- | А теперь измените строку 10 и выполните программу. | + | |
- | <code> | + | |
- | 10 A$="ABCD"+"" | + | |
- | run | + | |
- | Ok | + | |
- | </code> | + | |
- | Повторите вышеуказанные шаги. Вы получите: | + | |
- | <code> | + | |
- | PRINT HEX$(VARPTR(A$)) | + | |
- | 8017 | + | |
- | Ok | + | |
- | </code> | + | |
- | |<code>PRINT PEEK(&H8014)</code>|3|''VALTYPE''| | + | |
- | |<code>PRINT PEEK(&H8015)</code>|65|Код ASCII для символа "А"| | + | |
- | |<code>PRINT PEEK(&H8016)</code>|0|В имени нет второго символа| | + | |
- | |<code>PRINT PEEK(&H8017)</code>|4|Длина строки| | + | |
- | |<code>PRINT PEEK(&H8018)</code>|101 = &H65|Новый адрес (млaдший байт)| | + | |
- | |<code>PRINT PEEK(&H8019)</code>|241 = &HF1|Новый адрес (старший байт)| | + | |
- | + | ||
- | Операция, выполненная над строкой, явилась причиной пересылки её по адресу &HF165 (без изменения). Вы можете убедиться в этом, используя "в окрестности" этого адреса функцию ''PEEK''. | + | |
- | <code> | + | |
- | ? PEEK(&HF165) ? PEEK(&HF166) ? PEEK(&HF167) ? PEEK(&HF168) | + | |
- | 65 66 67 68 | + | |
- | Ok Ok Ok Ok | + | |
- | </code> | + | |
- | + | ||
- | Забегая несколько вперёд, отметим, что функция ''%%FRE("")%%'' возвратила бы число 200 в первом случае, однако, во втором случае возвращает число 196. | + | |
- | + | ||
- | Приведём схемы расположения информации в памяти для строковых массивов: | + | |
- | * //одномерный// строковый массив.<code> | + | |
- | VARPTR(B$(0))───┐ ┌── VARPTR(B$(1)) | + | |
- | ┌───┬───┬───┬───┬───┬───┬───┬───┬─▼─┬───┬───┬─▼─┬───┬───┬─── | + | |
- | │ │ │ │ │ │ 1 │ │ │∗∗∗│ │ │∗∗∗│ │ │∗∗∗ | + | |
- | └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴─── | + | |
- | 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 | + | |
- | ▲ ───▲─── ───▲─── ▲ ───▲─── ▲ ─▲───── ─▲─ ───▲─── | + | |
- | │ │ │ │ │ │ │ │ │ | + | |
- | Тип массива │ Служебная │ │ │ │ │ Адрес 1–го элемента | + | |
- | Идентификатор информация│ │ │ │ Длина значения 1–го элемента | + | |
- | │ │ │ Адрес 0–го элемента | + | |
- | Размерность массива │ Длина значения 0–го элемента | + | |
- | Число элементов в массиве | + | |
- | </code> <WRAP> | + | |
- | + | ||
- | {{anchor:e106-04}} __//Пример 4//__. Обязательно разберите пример "с компьютером в руках"! \\ {{.examples:106-04.bas|}} \\ [[+tab|wmsxbpge>106-04.bas]] | + | |
- | <code> | + | |
- | 10 DIM SM$(2):SM$(0)="рог"+"":SM$(1)="Inform"+"":SM$(2)="1989 г."+"" | + | |
- | run | + | |
- | Ok | + | |
- | print HEX$(VARPTR(SM$(0))-8) | + | |
- | 8047 | + | |
- | Ok | + | |
- | </code> | + | |
- | |<code>? РЕЕК(&Н8047)</code>|Этот адрес соответствует байту ''VALTYPE''. Поэтому Вы должны получить 3.| | + | |
- | |<code>? РЕЕК(&Н8048)</code>|Этот адрес соответствует первому символу имени строкового массива. Вы должны получить число 83 — код ASCII символа "S".| | + | |
- | |<code>? PEEK(&H8049)</code>|Этот адрес соответствует второму символу имени строкового массива. Вы должны получить число 77 — код ASCII символа "М".| | + | |
- | |<code>? РЕЕК(&Н804A)</code>|12 Служебная информация| | + | |
- | |<code>? РЕЕК(&Н804B)</code>|0 Служебная информация| | + | |
- | |<code>? PEEK(&H804C)</code>|Массив SM$ одномерный, поэтому мы и получили 1.| | + | |
- | |<code>? PEEK(&H804D)</code>|3 = 00000011₂| | + | |
- | |<code>? PEEK(&H804E)</code>|0 = 00000000₂| | + | |
- | Теперь находим количество элементов в массиве: 00000000 00000011₂ = 3 | + | |
- | ? PEEK(&H800F)◀── 3 Длина значения элемента SM$(0). | + | |
- | Приведём схему расположения информации в памяти для элемента строкового массива: | + | |
- | <code> | + | |
- | ┌───┬───┬───┐ | + | |
- | │∗∗∗│▧▧▧│▧▧▧│ | + | |
- | └───┴───┴───┘ | + | |
- | Адреса ──▶ X X+1 X+2 | + | |
- | ▲ ───▲─── | + | |
- | Байт длины ──┘ └── Ссылка на адрес в строковой области | + | |
- | </code> | + | |
- | Продолжим наш пример: | + | |
- | <code> | + | |
- | PRINT HEX$(VARPTR(SM$(0))) | + | |
- | 804F | + | |
- | Ok | + | |
- | ? PEEK(&h804F) | + | |
- | 3 ◀──── Длина значения 0–го элемента массива | + | |
- | Ok | + | |
- | ? HEX$(PEEK(&h8050)) | + | |
- | 66 ◀──── Младший байт адреса 0–го элемента в строковой области | + | |
- | Ok | + | |
- | ? HEX$(PEEK(&h8051)) | + | |
- | F1 ◀──── Старший байт адреса 0–го элемента в строковой области | + | |
- | Ok | + | |
- | ? CHR$(PEEK(&hF166)) | + | |
- | р ◀─┐ | + | |
- | Ok │ | + | |
- | ? CHR$(PEEK(&hF167)) │ Символы | + | |
- | о ◀─┤ значения | + | |
- | Ok │ 0–го | + | |
- | ? CHR$(PEEK(&hF168)) │ элемента | + | |
- | г ◀─┘ | + | |
- | Ok | + | |
- | </code> | + | |
- | </WRAP> | + | |
- | * //двухмерный// строковый массив. <code> | + | |
- | Значение, возвращаемое функцией VARPTR() ───┐ | + | |
- | ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬─────┬──▼──┐ | + | |
- | │ │ │ │ │ │ &H2 │ │ │ │ │ ∗∗∗ │ | + | |
- | └─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┴─────┘ | + | |
- | X-10 X-9 X-8 X-7 X-6 X-5 X-4 X-3 X-2 X-1 X | + | |
- | ▲ ─────▲───── ─────▲───── ▲ ─────▲───── ─────▲───── ▲ | + | |
- | │ │ │ │ │ │ │ | + | |
- | │ │ Служебная инф. │ │ │ Длина значения | + | |
- | │ Идентификатор Для двухмерного │ │ нулевого элем. | + | |
- | Тип массива массива │ │ | + | |
- | Количество столбцов Количество строк | + | |
- | </code> | + | |
- | + | ||
- | {{anchor:clear}} | + | |
- | ===== X.7. Оператор CLEAR ===== | + | |
- | + | ||
- | <WRAP group 99%> | + | |
- | <WRAP half column> \\ </WRAP> | + | |
- | <WRAP half column><WRAP justify> | + | |
- | Чтобы вычистить одно, приходится выпачкать что–нибудь другое; но можно испачкать всё, что угодно, и ничего при этом не вычистить. | + | |
- | <WRAP rightalign> | + | |
- | —//Принцип Накопления Грязи по Питеру// | + | |
- | </WRAP></WRAP> | + | |
- | </WRAP></WRAP> | + | |
- | + | ||
- | Оператор ''CLEAR'' в общем виде записывается так: | + | |
- | <WRAP group> | + | |
- | <WRAP half column> | + | |
- | <code> | + | |
- | CLEAR [[n][,A]] | + | |
- | </code> | + | |
- | </WRAP> | + | |
- | <WRAP half column> | + | |
- | , | + | |
- | </WRAP> | + | |
- | </WRAP> | + | |
- | где: | + | |
- | * ''CLEAR'' ("очистить") — служебное слово; | + | |
- | * n — арифметическое выражение, целая часть значения которого указывает количество байт, резервируемых под строковое пространство; значение параметра n меняется от нуля до размеров свободного пространства и равно 200 по умолчанию; | + | |
- | * А — арифметическое выражение, целая часть значения которого определяет адрес первого байта участка памяти, расположенного между блоком управления файлами и рабочей областью. Этот участок не будет "обработан" интерпретатором, поэтому он является как бы "резервной" памятью, где Вы можете хранить, например, подпрограммы на машинном языке и другие необходимые Вам данные. | + | |
- | + | ||
- | Другими словами, значение А "переопределяет верхний предел пространства, используемого [[msx:basic:]]". | + | |
- | + | ||
- | Например, команда ''CLEAR 1000, &HF000'' отводит 1000 байт для значений строковых констант в строковой области и RAM с адреса &HF000 для размещения машинной подпрограммы. | + | |
- | + | ||
- | //Максимальным// значением выражения A, конечно же, является адрес, с которого начинается рабочая область (&HF380). | + | |
- | + | ||
- | //Минимальное// значение выражения A может быть определено по формуле: | + | |
- | <WRAP group> | + | |
- | <WRAP half column> | + | |
- | <code> | + | |
- | 267·(MAXFIL+1)+VARTAB+145+n+2 | + | |
- | </code> | + | |
- | </WRAP> | + | |
- | <WRAP half column> | + | |
- | , | + | |
- | </WRAP> | + | |
- | </WRAP> | + | |
- | где: | + | |
- | * ''MAXFIL'' — имя слова, расположенного в рабочей области по адресу &HF85F. Этот адрес занимает 1 байт памяти; | + | |
- | * ''VARTAB'' — имя слова, расположенного в рабочей области по адресу &HF6C2. Адрес занимает 2 байта памяти и отмечает конец PIT; | + | |
- | * n — размер строкового пространства в байтах. | + | |
- | + | ||
- | Заметим, что //минимальная// величина стека равна 145 байтам (это пространство между концом PIT и вершиной стека). | + | |
- | Отметим, что оба аргумента могут быть опущены; в этом случае оператор ''CLEAR'' производит "чистку" значений всех числовых и строковых переменных, элементов массивов и определённых пользователем функций ''DEFFN'', а также "уничтожает" стек. | + | |
- | + | ||
- | Сохранятся только текст программы на [[msx:basic:]] и ранее зарезервированные машинные подпрограммы! | + | |
- | + | ||
- | Приведём два простеньких примера: | + | |
- | * 1) {{anchor:e107-01}} {{.examples:107-01.bas|}} \\ [[+tab|wmsxbpge>107-01.bas]] <code> | + | |
- | 10 DEF FNY(X)=X | + | |
- | 11 CLEAR | + | |
- | 12 PRINT FNY(1) | + | |
- | run | + | |
- | Undefined user function | + | |
- | Ok | + | |
- | </code> | + | |
- | * 2) {{anchor:e107-02}} {{.examples:107-02.bas|}} \\ [[+tab|wmsxbpge>107-02.bas]]<code> | + | |
- | 10 M=9:T$=STRING$(5,"+"):?M;T$ | + | |
- | 20 CLEAR:?M;T$ | + | |
- | run | + | |
- | 9 +++++ | + | |
- | 0 | + | |
- | Ok | + | |
- | </code> | + | |
- | Параметр n в операторе ''CLEAR n'' указывает размер строковой области, зарезервированной для хранения строковых значений, причём: | + | |
- | <WRAP group> | + | |
- | <WRAP half column> | + | |
- | <code> | + | |
- | 0 ≤ n ≤ FRETOP-VARTAB-145-16 | + | |
- | </code> | + | |
- | </WRAP> | + | |
- | <WRAP half column> | + | |
- | , | + | |
- | </WRAP> | + | |
- | </WRAP> | + | |
- | где: | + | |
- | * ''FRETOP'' — имя слова, расположенного в рабочей области по адресу &HF69B. Этот адрес занимает 2 байта памяти; | + | |
- | * ''VARTAB'' — имя слова, расположенного в рабочей области по адресу &HF6C2. Этот адрес занимает 2 байта памяти. | + | |
- | + | ||
- | {{anchor:e107-03}} __//Пример 3//__. Нажмите кнопку <key>RESET</key> Вашего компьютера. \\ {{.examples:107-03.bas|}} \\ [[+tab|wmsxbpge>107-03.bas]] | + | |
- | А теперь: | + | |
- | <code> | + | |
- | 10 PRINT HEX$(PEEK(&HF69C));" ";HEX$(PEEK(&HF69B)) | + | |
- | 11 PRINT HEX$(PEEK(&HF6C3));" ";HEX$(PEEK(&HF6C2)) | + | |
- | 12 PRINT &HDC5F-&H8003-145-16 | + | |
- | DC 5F ◀───── Вы узнали адрес FRETOP ? | + | |
- | 80 3 ◀───── Вы узнали адрес VARTAB ? | + | |
- | 23483 ◀───── 0≤n≤23483 | + | |
- | Ok | + | |
- | </code> | + | |
- | + | ||
- | {{anchor:e107-04}} __//Пример 4//__. \\ {{.examples:107-041.bas|}} \\ [[+tab|wmsxbpge>107-041.bas]] | + | |
- | <code> | + | |
- | 10 CLEAR200:T$=SPACE$(198):B$=STRING$(2,"#"):?B$:X$=STRING$(1,"L"):?X$ | + | |
- | run | + | |
- | ## | + | |
- | Out of string space in 10 | + | |
- | Ok | + | |
- | </code> | + | |
- | Получили сообщение об отсутствии места в строковой области, так как 200 байт, отведённых для неё по умолчанию, оказались уже исчерпанными. | + | |
- | + | ||
- | Однако... | + | |
- | \\ {{.examples:107-042.bas|}} \\ [[+tab|wmsxbpge>107-042.bas]] | + | |
- | <code> | + | |
- | 10 CLEAR 200:T$=SPACE$(198):B$=STRING$(2,"#"):?B$;:CLEAR1:X$=STRING$(1,"L"):? FRE("") | + | |
- | run | + | |
- | ## 0 | + | |
- | Ok | + | |
- | </code> | + | |
- | + | ||
- | ===== X.8. Функция FRE ===== | + | |
- | + | ||
- | {{anchor:fre}} | + | |
- | + | ||
- | <WRAP group 99%> | + | |
- | <WRAP half column> \\ </WRAP> | + | |
- | <WRAP half column><WRAP justify> | + | |
- | Garbage collection ("чистка памяти", "сборка мусора") — действия системы динамического распределения памяти для обнаружения неиспользуемых программой блоков памяти и присоединения их к списку свободной памяти для повторного использования. | + | |
- | <WRAP rightalign> | + | |
- | —//Англо–русский словарь по программированию и информатике// | + | |
- | </WRAP></WRAP> | + | |
- | </WRAP></WRAP> | + | |
- | + | ||
- | Информацию о размере свободной области ("Free Area") в RAM можно получить с помощью функции ''FRE'', обращение к которой имеет вид: | + | |
- | <WRAP group> | + | |
- | <WRAP half column> | + | |
- | <code> | + | |
- | FRE(A) | + | |
- | </code> | + | |
- | </WRAP> | + | |
- | <WRAP half column> | + | |
- | , | + | |
- | </WRAP> | + | |
- | </WRAP> | + | |
- | где: | + | |
- | * ''FRE'' ("FREe" — "свободный") — служебное слово; | + | |
- | * A — арифметическое или строковое выражение, причём для интерпретатора важным является лишь тип выражения, а не его значение. | + | |
- | + | ||
- | На практике применяется следующий синтаксис: | + | |
- | <code> | + | |
- | FRE(0) | + | |
- | </code> | + | |
- | или | + | |
- | <WRAP group> | + | |
- | <WRAP half column> | + | |
- | <code> | + | |
- | FRE("") | + | |
- | </code> | + | |
- | </WRAP> | + | |
- | <WRAP half column> | + | |
- | . | + | |
- | </WRAP> | + | |
- | </WRAP> | + | |
- | + | ||
- | Функция ''FRE(0)'' возвращает количество байт, оставленных для расширения PIT, VT, стека, строковой области и блока управления файлами. | + | |
- | + | ||
- | {{anchor:e108-01}} __//Пример 1//__. \\ {{.examples:108-01.bas|}} \\ [[+tab|wmsxbpge>108-01.bas]] | + | |
- | <code> | + | |
- | 10 ? FRE(0):X=451:? FRE(0):Z#=7.5:? FRE(0) | + | |
- | 20 Y!=555:? FRE(0):W%=111:? FRE(0) | + | |
- | run | + | |
- | 28739 | + | |
- | 28728 ◀── т.к. переменная Х по умолчанию — двойной точности, а, следовательно, занимает в памяти 11 байт; | + | |
- | 28717 ◀── т.к. переменная Z — двойной точности (занимает в памяти также 11 байт); | + | |
- | 28710 ◀── т.к. переменная Y — одинарной точности (занимает в памяти 7 байт); | + | |
- | 28705 ◀── т.к. переменная W — целого типа (занимает в памяти 5 байт). | + | |
- | Ok | + | |
- | </code> | + | |
- | Функция ''FRE(0)'' выдаёт сообщение: <WRAP centeralign>"Out of memory" (//"Не хватает памяти"//)</WRAP> при достижении значения, меньшего 145 байт, минимально допустимого для стека системы [[msx:basic:]]. Посмотрите (предварительно нажав кнопку <key>RESET</key>): | + | |
- | <code> | + | |
- | CLEAR 28868:PRINT FRE(0) | + | |
- | 147 | + | |
- | Ok | + | |
- | </code> | + | |
- | + | ||
- | <code> | + | |
- | CLEAR 28869:PRINT FRE(0) | + | |
- | Out of memory | + | |
- | Ok | + | |
- | </code> | + | |
- | + | ||
- | Заметим,что слово ''VARTAB'' отличается от слова ''TXTTAB'' на 2 байта (при отсутствии программы!), поэтому, добавив эти 2 байта к 145 байтам, необходимым для работы стека, получаем число 147! | + | |
- | + | ||
- | Функция ''%%FRE("")%%'' возвращает количество свободных байт в строковом пространстве. Например: | + | |
- | <code> | + | |
- | print FRE("") | + | |
- | 200 | + | |
- | Ok | + | |
- | </code> | + | |
- | + | ||
- | <code> | + | |
- | X$="2²"+"3²":print FRE("") | + | |
- | 196 | + | |
- | Ok | + | |
- | </code> | + | |
- | + | ||
- | Кроме того, функция ''%%FRE("")%%'' выполняет важное дополнительное действие. | + | |
- | Оно связано с наличием в [[msx:basic:]] строк переменной длины, обработка которых может привести к явлению "фрагментации памяти" (внутри строковой области появляются участки,содержащие неиспользуемую информацию — //"мусор"//). | + | |
- | Поэтому, если в качестве аргумента функции ''FRE'' задано выражение строкового типа, перед вычислением объёма свободной памяти функция выполняет //"сборку мусора"//, т.е. удаление всех неиспользуемых данных и освобождение занимаемых ими областей. | + | |
- | + | ||
- | {{anchor:e108-02}} __//Пример 2//__. Оказывается,что если у Вас в начале программы встречается оператор ''A$="ABCD"+"EF"'', а затем — оператор ''A$="X"+"Y"'', то Вы сразу же создадите 6–байтовое пространство, заполненное "мусором"! \\ | + | |
- | Покажем это: | + | |
- | \\ {{.examples:108-02.bas|}} \\ [[+tab|wmsxbpge>108-02.bas]] \\ FIXME | + | |
- | <code> | + | |
- | print HEX$(PEEK(&HF69C)); HEX$(PEEK(&HF69B)); | + | |
- | F168 ────▲─── | + | |
- | Ok └── Адрес "верхушки" строкового | + | |
- | 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 | + | |
- | └──▲─┘ | + | |
- | Ok └─── "мусор" | + | |
- | + | ||
- | print fre("")'Избавимся от "мусора"! | + | |
- | 198 | + | |
- | Ok | + | |
- | + | ||
- | for t=0 to 7:print chr$(peek(&hF168-t));:next | + | |
- | YXXCBAYX | + | |
- | └──▲─┘ | + | |
- | Ok └─── "мусор" | + | |
- | </code> | + | |
- | + | ||
- | Из примера следует, что строки хранятся в строковом пространстве в том порядке, в каком они были определены. | + | |
- | + | ||
- | Таким образом, функция ''%%FRE("")%%'' изменила положение значения строковой переменной (это и называется //"сборкой мусора"//). | + | |
- | + | ||
- | Если под строки зарезервирован большой объем строкового пространства и определено много символьных переменных, время "сборки мусора" может составить несколько минут. При выполнении этой операции компьютер полностью "застывает". Посмотрите… | + | |
- | + | ||
- | {{anchor:e108-03}} __//Пример 3//__. \\ {{.examples:108-03.bas|}} \\ [[+tab|wmsxbpge>108-03.bas]] | + | |
- | <code> | + | |
- | 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 | + | |
- | ·3500··········3.61638888888 мин | + | |
- | Ok (для MSX 1) | + | |
- | + | ||
- | run | + | |
- | ·3500··········3.3716666666667 мин | + | |
- | Ok (для MSX 2) | + | |
- | </code> | + | |
- | Интересно, что при изменении в строке 10 оператора ''CLEAR 5000'' на оператор ''CLEAR 1600'', результат получается почти тот же (≈3.607 мин. для компьютера [[msx:msx_1]] и ≈3.38 мин. для компьютера [[msx:msx_2]])! | + | |
- | + | ||
- | //Единственный// способ уменьшить время "сборки мусора" — это использовать минимальное количество строк и особенно строковых массивов! | + | |
- | + | ||
- | Следует заметить, что некоторые строки хранятся в тексте самой программы и, таким образом, не занимают места в строковой области. | + | |
- | + | ||
- | {{anchor:e108-04}} __//Пример 4//__. \\ {{.examples:108-04.bas|}} \\ [[+tab|wmsxbpge>108-04.bas]] | + | |
- | <code> | + | |
- | 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 ◀── Произошла "сборка мусора" (свободное место в строковой области | + | |
- | 47 увеличилось, т.к. значение элемента массива E$(1) уже хранится | + | |
- | Ok в тексте программы)… | + | |
- | </code> | + | |
- | Таким образом, строковая область является областью памяти, резервируемой для хранения строковых данных. Если Вы хотите зарезервировать в строковом пространстве место для хранения 10 строк, содержащих каждая максимум 5 символов, то воспользуйтесь, например, оператором цикла: | + | |
- | <code> | + | |
- | FOR I=1 TO 10:A$(I)=SPACE$(5):NEXT | + | |
- | </code> | + | |
- | Во избежание "сборки мусора": | + | |
- | - определяйте все переменные в начале программы; | + | |
- | - <WRAP> используйте строковые функции ''MID$'', ''LEFT$'', ''RIGHT$''. | + | |
- | Перед работой со следующим примером выключите, а затем снова включите Ваш компьютер. | + | |
- | + | ||
- | {{anchor:e108-05}} __//Пример 5//__. \\ {{.examples:108-05.bas|}} \\ [[+tab|wmsxbpge>108-05.bas]] \\ FIXME | + | |
- | <code> | + | |
- | A$="полет" | + | |
- | Ok | + | |
- | for t=0 to 10:print chr$(peek(&HF168-t));:next | + | |
- | телопп██ | + | |
- | Ok └▲─┘ | + | |
- | └─────── "м у с о р" | + | |
- | A$="налет" | + | |
- | Ok | + | |
- | for t=0 to 10:print chr$(peek(&HF168-t));:next | + | |
- | телоптеланн | + | |
- | └─▲─┘ ▲ | + | |
- | └───────└──── "м у с о р" | + | |
- | Ok | + | |
- | print fre("") | + | |
- | 195 | + | |
- | Ok | + | |
- | for t=0 to 10:print chr$(peek(&HF168-t));:next | + | |
- | теланнеланн | + | |
- | └─▲──┘ | + | |
- | └─────── "м у с о р" | + | |
- | Ok | + | |
- | mid$(A$,1,2)="по" | + | |
- | Ok | + | |
- | for t=0 to 10:print chr$(peek(&HF168-t));:next | + | |
- | телоппеланн | + | |
- | └──▲─┘ | + | |
- | └── "м у с о р" (он остался на прежнем месте!) | + | |
- | Ok | + | |
- | </code> | + | |
- | + | ||
- | Отметим, что строковые функции ''MID$'', ''LEFT$'', ''RIGHT$'' не изменяют указатели на значения строковых переменных. Обычный же оператор присваивания, разумеется, указатели изменяет! Покажем это на примере (не забудьте о команде ''CLEAR'' !). | + | |
- | + | ||
- | {{anchor:e108-06}} __//Пример 6//__. \\ {{.examples:108-06.bas|}} \\ [[+tab|wmsxbpge>108-06.bas]] \\ FIXME | + | |
- | <code> | + | |
- | 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 | + | |
- | </code> | + | |
- | а теперь… | + | |
- | <code> | + | |
- | mid$(a$,1,2)="по" | + | |
- | Ok | + | |
- | print hex$(peek(varptr(a$)+2)); hex$(peek(varptr(a$)+1)) | + | |
- | F15F ◀── Обратите внимание, это значение совпадает с предыдущим! | + | |
- | Ok | + | |
- | </code> | + | |
- | Как видим, значение указателя в последнем случае не изменилось! | + | |
- | </WRAP> | + | |
- | - <WRAP>при необходимости используйте оператор ''SWAP A$,В$'' , который не меняет расположение значений переменных, а лишь меняет местами указатели на эти значения. Проиллюстрируем этот факт на примере… | + | |
- | + | ||
- | {{anchor:e108-07}} __//Пример 7//__. \\ {{.examples:108-07.bas|}} \\ [[+tab|wmsxbpge>108-07.bas]] \\ FIXME | + | |
- | <code> | + | |
- | 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 | + | |
- | </code> | + | |
- | + | ||
- | И наконец, функция ''FRE()'' может помочь Вам также в //защите// Вашей программы. Например, в "укромном" месте программы, работающей со строковой информацией, поместите оператор ''%%X$=SPACE$(FRE(""))%%'' — конечно, Вы должны учесть, что целая часть значения аргумента функции ''SPACE$'' должна принадлежать отрезку [0,255]!. Это удержит "любознательных" от модификации значений переменных Вашей программы (разумеется, в данном случае строковых)! | + | |
- | + | ||
- | Посмотрите: | + | |
- | <code> | + | |
- | 10 X$=SPACE$(FRE("")):Y$="2"+Y$ | + | |
- | run | + | |
- | Out of string space in 10 | + | |
- | Ok | + | |
- | </code> | + | |
- | </WRAP> | + | |
- | + | ||
- | ===== X.9. Рабочая область ===== | + | |
- | + | ||
- | В рабочей области содержатся системные подпрограммы, системные переменные и "ловушки", используемые интерпретатором во время выполнения операторов Вашей программы. В рабочей области хранятся данные о позиции курсора, цвете текста, состоянии функциональных клавиш и другая полезная информация, инициализируемая при включении компьютера. | + | |
- | + | ||
- | <WRAP centeralign>Адрес, отмечающий //начало// рабочей области, указан в самой этой области в слове ''HIMEM'', содержимое которого занимает 2 байта, расположенных с адреса &HFC4A .</WRAP> | + | |
- | + | ||
- | Ещё раз напомним Вам, что адреса, занимающие два байта, всегда записываются так: вначале записывается содержимое младшего байта, а затем — содержимое старшего байта! | + | |
- | + | ||
- | Отметим, что значением выражения ''HEX$(PEEK(&HFC4A)+256*PEEK(&HFC4B))'' является //адрес начала рабочей// области. | + | |
- | + | ||
- | Поскольку рабочая область расположена в RAM, её переменные могут изменяться операторами ''POKE''. Но это следует делать только в том случае, если Вы //знаете//, что за этим последует! | + | |
- | + | ||
- | ==== X.9.1. Матрица клавиатуры ==== | + | |
- | + | ||
- | //Матрицей// клавиатуры для MSX–компьютеров назовём таблицу вида: | + | |
- | | | ^ 0–й бит ^ 1–й бит ^ 2–й бит ^ 3–й бит ^ 4–й бит ^ 5–й бит ^ 6–й бит ^ 7–й бит ^ | + | |
- | |:::^Адреса \\ байт^ 254 ^ 253 ^ 251 ^ 247 ^ 239 ^ 223 ^ 191 ^ 127 ^ | + | |
- | ^ 0–я строка ^ FBE5 | 9 | ; | 1 | 2 | 3 | 4 | 5 | 6 | | + | |
- | ^ 1–я строка ^ FBE6 | 7 | 8 | 0 | = | - | H | : | V | | + | |
- | ^ 2–я строка ^ FBE7 | \ | . | B | @ | , | / | F | I | | + | |
- | ^ 3–я строка ^ FBE8 | S | W | U | A | P | R | [ | O | | + | |
- | ^ 4–я строка ^ FBE9 | L | D | X | T | ] | Z | J | K | | + | |
- | ^ 5–я строка ^ FBEA | Y | E | G | M | C | ~ | N | Q | | + | |
- | ^ 6–я строка ^ FBEB | SHIFT | CTRL | GRAPH | CAPS | РУС | F1 | F2 | F3 | | + | |
- | ^ 7–я строка ^ FBEC | F4 | F5 | ESC | TAB | STOP | BS | SELECT | RETURN | | + | |
- | ^ 8–я строка ^ FBED | SPACE | HOME | INS | DEL | ← | ↑ | ↓ | → | | + | |
- | ^ 9–я строка ^ FBEE | RET | + | * | 0 | 1 | 2 | 3 | 4 | | + | |
- | ^ 10–я строка ^ FBEF | 5 | 6 | 7 | 8 | 9 | - | , | . | | + | |
- | + | ||
- | Последние две строки соответствуют цифровой (правой) зоне клавиатуры учительского компьютера серии [[msx:msx_2]]. Более подробная информация по этой теме [[msx:russification:russification#матрица_клавиатуры|здесь]]((Примечание редактора)). | + | |
- | + | ||
- | Ответим теперь на Ваш очевидный вопрос: <WRAP centeralign>Как воспользоваться этой таблицей?</WRAP> | + | |
- | + | ||
- | __//Пример 1//__. Ниже приведены программа, останавливаемая нажатием клавиши <key>GRAPH</key>: | + | |
- | 10 Z=PEEK(&HFBEB):IF Z<>251 THEN 10 | + | |
- | и программа, останавливаемая нажатием клавиш <key>SHIFT+CTRL</key>: | + | |
- | 10 Z=PEEK(&HFBEB):IF Z<>(254 AND 253) THEN 10 | + | |
- | А теперь ответ на Ваш следующий вопрос: <WRAP centeralign>А как получить матрицу клавиатуры ?</WRAP> | + | |
- | + | ||
- | Для "чтения" нажатой клавиши достаточно "прочесть" слово ''NEWKEY'' (11 байт) по адресу &HFBE5 из таблицы системных переменных. | + | |
- | + | ||
- | __//Пример 2//__. Программа "пробегает" все клавиши и возвращает позицию нажатой клавиши (X,Y) матрицы клавиатуры. 11 значений, записанных в слове ''NEWKEY'', соответствуют 11 строкам матрицы клавиатуры. | + | |
- | Если не нажата ни одна клавиша, содержанием каждого из 8 байт, соответствующих строке матрицы является 1. Это фиксируется двоичным числом &B11111111=255. Когда же клавиша нажата, считанное на этой строке значение отличается от 255: бит соответствующей колонки "сбрасывается" в 0. | + | |
- | {{anchor:e1091-01}} {{.examples:1091-01.bas|}} \\ [[+tab|wmsxbpge>1091-01.bas]] | + | |
- | <code> | + | |
- | 10 FOR Y=0 TO 10:Z=PEEK(&HFBE5+Y) | + | |
- | 30 IF Z=255 THEN 80 '──▶ | + | |
- | 40 PRINT"Y=";Y:Z$=RIGHT$("00000000"+BIN$(Z),8) | + | |
- | 60 PRINT"X=";8-INSTR(Z$,"0"):PRINT | + | |
- | 80 NEXT:GOTO 10 '──▶ | + | |
- | </code> | + | |
- | + | ||
- | ==== X.9.2. Динамическая клавиатура [46] ==== | + | |
- | + | ||
- | [[bibliography#b46|[46]]] | + | |
- | + | ||
- | <WRAP group 99%> | + | |
- | <WRAP half column> \\ </WRAP> | + | |
- | <WRAP half column><WRAP justify> | + | |
- | Промедление с лёгким делом превращает его в трудное, промедление же с трудным делом превращает его в невозможное. | + | |
- | <WRAP rightalign> | + | |
- | —//Д.Лоример// | + | |
- | </WRAP></WRAP> | + | |
- | </WRAP></WRAP> | + | |
- | + | ||
- | Исследуем один подход к разработке учебных программ, работающих под управлением интерпретатора [[msx:basic:]]. Существенная особенность этого подхода состоит в том, что программа в процессе выполнения модифицируется | + | |
- | (происходит изменение отдельных строк BASIC–программы или добавление новых строк). Считается, что допущение самомодификации программы во время выполнения является признаком плохого стиля программирования, поэтому начнём с примера, который показывает, что предлагаемый подход является не только оправданным, но и в ряде случаев единственно возможным. | + | |
- | + | ||
- | Пусть необходимо табулировать функцию y=f(x), конкретно x², то есть для каждого значения аргумента вычислить значение функции и результат записать в таблицу. Соответствующая программа выглядит следующим образом: | + | |
- | <code> | + | |
- | 10 'Программа табулирования функции. | + | |
- | 20 DIM X(200),Y(200) | + | |
- | 30 INPUT XN,XK 'Задание границ изменения аргумента функции. | + | |
- | … | + | |
- | 100 GOSUB 1000 'Обращение к подпрограмме табулирования. | + | |
- | … | + | |
- | 999 END | + | |
- | 1000 FOR I=1 TO 200:Y(I)=X(I)*X(I):NEXT I:RETURN | + | |
- | </code> | + | |
- | Части программы между строками 30 и 100, 100 и 999 содержат операторы, обеспечивающие масштабирование, заполнение таблицы, защиту от ошибочных действий пользователя и т.д. Простота программы обусловлена тем, что задача табулирования решается для //фиксированной// функции. Попытаемся теперь разработать программу, которая позволяет табулировать любую функцию одной переменной, аналитическое выражение которой вводится с клавиатуры! | + | |
- | + | ||
- | Для реализации на ПЭВМ "YAMAHA" используем специальный механизм, введенный Дж.Баттерфилдом (J.Batterfield) и названный им принципом //"динамической клавиатуры"//. | + | |
- | + | ||
- | В //командном// (!) режиме информация, набираемая пользователем на клавиатуре, аппаратно записывается в буфер (называемый в дальнейшем //буфером клавиатуры//, БК). БК размещается в рабочей области с адреса &HFBF0 по адрес &HFC17 и занимает 40 байт. При нажатии клавиши <key>'Ввод '⏎</key> содержимое БК считывается интерпретатором и выполняется соответствующая команда. | + | |
- | + | ||
- | Имитация действий пользователя на основе принципа "динамической клавиатуры" осуществляется следующим образом: | + | |
- | - с помощью оператора INPUT вводится текст запроса (в данном случае аналитическое выражение табулируемой функции) в символьную строку F$ (в приведённой ниже программе — строка 10); | + | |
- | - символьная строка дополняется впереди номером, а в конце — кодом команды ''RETURN'' (строки 15 и 1890): \\ ''F$=%%"%%номер строки 1%%"%%+F$+CHR$(13)'' ; | + | |
- | - строка побайтно переписывается в БК, начиная с адреса &HFBF0, при помощи оператора ''POKE'' и функции ''PEEK'' (подпрограмма, начинающаяся со строки 1880); | + | |
- | - строка ''S$=%%"%%goto%%"%%+%%"%%номер строки 2%%"%%+CHR$(13)'', где //номер строк// и 2 — номер строки программы, куда после модификации необходимо передать управление, побайтно переписывается в БК (строка 25); | + | |
- | - выполнение программы прекращается командой ''END'', в результате происходит переход из программного режима в командный. Интерпретатор считывает содержимое БК до первого появления ''CHR$(13)'' и выполняет его как команду, то есть модифицирует строку с номером //номер строки// 1. Далее считывается остаток содержимого БК до второго появления ''CHR$(13)'', и он также выполняется интерпретатором, как команда, после чего происходит переход в программный режим с передачей управления в строку с номером //номер строки 2//. | + | |
- | + | ||
- | Таким образом, указанный алгоритм решает задачу автоматической модификации программы в соответствии с текстом запроса, вводимого пользователем с клавиатуры, и запуска её с указанного номера строки. | + | |
- | + | ||
- | {{anchor:e1092-01}} __//Пример//__. \\ {{.examples:1092-01.bas|}} \\ [[+tab|wmsxbpge>1092-01.bas]] | + | |
- | <code> | + | |
- | 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 '∗∗ Программа табулирования функции Y(x) ∗∗ | + | |
- | 60 INPUT"Введите[A,B] и шаг табулирования H";A,B,H | + | |
- | 65 FOR X=A TO B STEP H:PRINT X;FNY(X):NEXT | + | |
- | 90 END | + | |
- | 1550 '∗∗∗∗∗ Ф о р м и р о в а н и е F$ ∗∗∗∗∗ | + | |
- | 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'──▶ | + | |
- | 1730 '∗∗∗∗∗ В о с с т а н о в л е н и е F$ ∗∗∗∗∗ | + | |
- | 1740 LF=PEEK(&HF600):F$="" | + | |
- | 1750 FOR I=1 TO LF:C=PEEK(&HF601+I):F$=F$+CHR$(C):NEXT | + | |
- | 1780 RETURN'──▶ | + | |
- | 1880 '∗∗∗∗∗ Д и н а м и ч е с к а я к л а в и а т у р а ∗∗∗∗∗ | + | |
- | 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/256)*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$)<L1 THEN AD=AD+LEN(F$) ELSE AD=&HFBF0 | + | |
- | 2030 AD=AD+65536! | + | |
- | 2040 POKE&HF3F9,FIX(AD/256):POKE&HF3F8,AD-FIX(AD/256)*256 | + | |
- | 2050 RETURN'──▶ | + | |
- | 2410 IF LEN(F$)>19 THEN CLS:LOCATE 1,10:PRINT"Эта программа имеет ограничение:":GOTO 2420 ELSE GOTO 2460 | + | |
- | 2420 PRINT:PRINT "Длина формулы не должна превосходить 19 символов!";LEN(F$) | + | |
- | 2440 PRINT:LOCATE 1,23:PRINT"Для продолжения нажмите любую клавишу" | + | |
- | 2450 W$=INKEY$:IF W$="" THEN 2450 ELSE GOTO 10 | + | |
- | 2460 RETURN'──▶ | + | |
- | </code> | + | |
- | + | ||
- | ===== X.10. Порты ввода–вывода ===== | + | |
- | + | ||
- | <WRAP group 99%> | + | |
- | <WRAP half column> \\ </WRAP> | + | |
- | <WRAP half column><WRAP justify> | + | |
- | И я надеюсь, что наши потомки будут благодарны мне не только за то, что я здесь разъяснил, но и за то, что мною было добровольно опущено с целью предоставить им удовольствие самим найти это. | + | |
- | <WRAP rightalign> | + | |
- | —//Pене Декарт. Геометрия// | + | |
- | </WRAP></WRAP> | + | |
- | </WRAP></WRAP> | + | |
- | + | ||
- | //Порт// ввода–вывода — многоразрядный вход или выход компьютера, через который процессор обменивается данными с внешними устройствами (клавиатурой, принтером, дисководом, видеопамятью и видеопроцессором, игровыми манипуляторами). Часто говорят, что порты представляют собой "интерфейсные схемы компьютера". | + | |
- | + | ||
- | Порт ввода–вывода напоминает морской порт, через который ввозят и вывозят товары. В нашем случае через порты вводятся и выводятся данные. Порты принимают данные от периферийных устройств и направляют их в эти устройства. Используя прямой доступ к портам ввода–вывода,Вы более полно используете возможности компьютера. | + | |
- | + | ||
- | Процессор "работает" с портами по адресам, которые не следует путать с адресами ROM или RAM: | + | |
- | - порты с адресами &H00÷&H7F. Вы не можете //изменить// их содержимое (сравните с ROM!); | + | |
- | - порты с адресами &H80÷&HFF. Их содержимое изменять можно (сравните с RAM!); | + | |
- | - порты с адресами &H100÷&HFFFF зарезервированы(пока не используются). | + | |
- | + | ||
- | Некоторые порты, их функции и адреса перечислены ниже: | + | |
- | + | ||
- | ^ Адрес ^ Чтение(Запись) ^ Назначение ^ | + | |
- | | Порты, отвечающие за работу с локальной сетью КУВТ YAMAHA [[msx:msx_1]] ||| | + | |
- | | &h00 | Чтение(Запись) | Посылаемые данные | | + | |
- | | &h01 | Чтение | Статус | | + | |
- | | &H02 | Чтение | Номер компьютера в локальной сети (только для компьютеров [[msx:msx_1]]) | | + | |
- | | Порты, отвечающие за работу с локальной сетью КУВТ YAMAHA [[msx:msx_2]] ||| | + | |
- | | &H09 | | Командный порт (передача или приём) | | + | |
- | | &H0C | | Порт состояния | | + | |
- | | &H0E | | Порт данных | | + | |
- | | — — — | — — — | — — — | | + | |
- | | &H0A | | Используются при инициализации сетевого ОЗУ | | + | |
- | | &H0B | |:::| | + | |
- | | &H0D | |:::| | + | |
- | | //Принтер// ||| | + | |
- | | &H90 | Чтение | Ввод сигнала занятости принтера | | + | |
- | | &H91 | Запись | Код выводимого символа | | + | |
- | | &H92 | Запись | ? | | + | |
- | | //Видеопроцессор// (VDP) ||| | + | |
- | | &H98 | Чтение(Запись) | Обращение к видеопамяти | | + | |
- | | &H99 | Чтение(Запись) | Чтение (запись) в регистр VDP | | + | |
- | | &H9A | Запись | Запись в регистр палитры | | + | |
- | | &H9B | Запись | Косвенная запись в регистры VDP | | + | |
- | | //Звукогенератор// (PSG) | | + | |
- | | &HA0 | Запись | Ввод в порт номера регистра | | + | |
- | | &HA1 | Запись | Ввод в порт информации для установленного регистра | | + | |
- | | &HA2 | Чтение | Последнее число, записанное в PSG | | + | |
- | | Программируемый //периферийный интерфейс// (PPI) ||| | + | |
- | | &HA8 | Чтение(Запись) | Запись (чтение) данных в порт A | | + | |
- | | &HA9 | Чтение(Запись) | Запись (чтение) данных в порт B | | + | |
- | | &HAA | Чтение(Запись) | Запись (чтение) данных в порт C | | + | |
- | | &HAB | Чтение(Запись) | Запись (чтение) режимов PPI | | + | |
- | | Порты, отвечающие за работу с //дисководом// | | + | |
- | | &HB4 | Запись | ? | | + | |
- | | &HB5 | Чтение(Запись) | ? | | + | |
- | | &HFC | Чтение(Запись) | Регистры распределения //слотов// (расширений памяти) для компьютеров серии [[msx:msx_2]] | | + | |
- | | &HFD | Чтение(Запись) |:::| | + | |
- | | &HFE | Чтение(Запись) |:::| | + | |
- | | &HFF | Чтение(Запись) |:::| | + | |
- | + | ||
- | <WRAP centeralign>Для работы с портами ввода–вывода используются: функция ''[[#INP]]'' и операторы ''[[#OUT]]'' и ''[[#WAIT]]''.</WRAP> | + | |
- | + | ||
- | {{anchor:out}} | + | |
- | Формат оператора ''OUT'': | + | |
- | <WRAP group> | + | |
- | <WRAP half column> | + | |
- | <code> | + | |
- | OUT адрес, данное | + | |
- | </code> | + | |
- | </WRAP> | + | |
- | <WRAP half column> | + | |
- | , | + | |
- | </WRAP> | + | |
- | </WRAP> | + | |
- | где: | + | |
- | * ''OUT'' ("OUTput" — "вывод") — служебное слово; | + | |
- | * //адрес// — арифметическое выражение, целая часть значения которого принадлежит отрезку [128,255] (128=&H80, 255=&HFF); | + | |
- | * //данное// — арифметическое выражение, целая часть значения которого принадлежит отрезку [0,255]. | + | |
- | + | ||
- | Оператор OUT "посылает" заданное операндом //данное// значение в порт, номер которого задан значением параметра //адрес//. | + | |
- | + | ||
- | + | ||
- | <WRAP centeralign> | + | |
- | //Внимание!// \\ | + | |
- | На компьютерах серии [[msx:msx_2]] прежде, чем использовать оператор ''OUT'', необходимо в непосредственном режиме выполнить команду ''[[msx:network_basic#CALL NETEND]]'' (отключить Ваш компьютер от локальной сети) | + | |
- | </WRAP> | + | |
- | + | ||
- | {{anchor:inp}} | + | |
- | Опишем синтаксис функции ''INP'': | + | |
- | <WRAP group> | + | |
- | <WRAP half column> | + | |
- | <code> | + | |
- | INP (адрес) | + | |
- | </code> | + | |
- | </WRAP> | + | |
- | <WRAP half column> | + | |
- | , | + | |
- | </WRAP> | + | |
- | </WRAP> | + | |
- | где: | + | |
- | * ''INP'' ("INPut" — "ввод") — служебное слово; | + | |
- | * //адрес// — арифметическое выражение, целая часть значения которого принадлежит отрезку [0,255]. | + | |
- | + | ||
- | Функция ''INP'' возвращает целочисленное значение, "прочитанное" из порта, имеющего указанный адрес. | + | |
- | + | ||
- | Видна ли Вам аналогия между операторами ''POKE'' и ''OUT'' , ''PEEK'' и ''INP'' ?! | + | |
- | + | ||
- | При помощи функции ''INP'' Вы можете использовать в своих расчётах номер Вашего компьютера. Чтобы поместить в переменную А номер компьютера, на котором Вы работаете в локальной сети [[msx:msx_1]], примените оператор: | + | |
- | <WRAP group> | + | |
- | <WRAP half column> | + | |
- | <code> | + | |
- | A=INP(&H02) AND 15 | + | |
- | </code> | + | |
- | </WRAP> | + | |
- | <WRAP half column> | + | |
- | . | + | |
- | </WRAP> | + | |
- | </WRAP> | + | |
- | + | ||
- | Объясним роль логической операции ''AND''. Значением, возвращаемым функцией ''INP(&H02)'', является двоичное число, записанное в одном байте. "Содержимое" четырёх старших битов байта нас не интересует. Заметим, что число 15 = &b00001111. Как Вы уже, наверное, догадались, логическая операция ''AND'' позволяет выделить нужные нам четыре младших бита. | + | |
- | + | ||
- | ==== X.10.1. Программируемый периферийный интерфейс (PPI) ==== | + | |
- | + | ||
- | Теперь мы перейдём к рассказу о работе с портами Программируемого Периферийного Интерфейса (PPI — "Parallel Programming Interface"). Подробное описание [[msx:ppi:ppi|здесь]]. | + | |
- | + | ||
- | Напомним Вам, что //интерфейс// (англ. "interface" — "сопряжение" — способ и средства установления и поддержания информационного обмена между исполнительными устройствами автоматической или человеко–машинной системы. | + | |
- | + | ||
- | В //параллельном// интерфейсе порция двоичной информации, состоящая из n битов, передаётся одновременно по n каналам. | + | |
- | + | ||
- | <WRAP centeralign>Порт А используется для выбора //слотов//, осуществляющих управление расширенной памятью компьютера.</WRAP> | + | |
- | + | ||
- | <WRAP centeralign>Порты PPI B и C применяются для "работы" с матрицей клавиатуры, причём номер строки матрицы клавиатуры "посылается" в порт C, а номер столбца "читается" в порту B.</WRAP> | + | |
- | + | ||
- | {{anchor:e1011-01}} __//Пример 1//__. //Обнаружение// нажатия клавиши <key>GRAPH</key>. \\ {{.examples:1011-01.bas|}} \\ [[+tab|wmsxbpge>1011-01.bas]] | + | |
- | + | ||
- | Отметим, что клавиша <key>GRAPH</key> находится в строке 6 и столбце 2 матрицы клавиатуры (и строки, и столбцы матрицы нумеруются, начиная с 0). Тогда: | + | |
- | - номер строки матрицы клавиатуры "посылаем" в порт C : <code>OUT &HAA,6</code> | + | |
- | - "извлекаем" номер столбца из порта B: <code>X=INP(&HA9)</code> | + | |
- | До нажатия клавиш значением Х является число 255 = &b11111111.В момент нажатия какой–либо клавиши соответствующий бит порта B (в нашем случае второй) на мгновение обнуляется. | + | |
- | + | ||
- | Таким образом, нажатие клавиши <key>GRAPH</key> легко обнаружить, если выделить значение интересующего нас бита командой: | + | |
- | <code> | + | |
- | IF (X AND &b00000100)=0 THEN PRINT"GRAPH" | + | |
- | </code> | + | |
- | Программа, позволяющая обнаружить нажатие клавиши <key>GRAPH</key>, выглядит так: | + | |
- | <code> | + | |
- | 10 OUT &HAA,6:X=INP(&HA9) | + | |
- | 20 IF (X AND 4)=0 THEN PRINT "GRAPH":END | + | |
- | 30 GOTO 10 | + | |
- | </code> | + | |
- | + | ||
- | Надеемся, что Вы обратили внимание на недостаток этой программы: после её запуска неожиданно включается индикатор <key>CAPS</key> (но это не означает,что Вам удалось смоделировать нажатие клавиши <key>CAPS</key>!). | + | |
- | + | ||
- | Разберёмся, почему так происходит. | + | |
- | + | ||
- | Взгляните на приведённую ниже таблицу, в которой описаны назначения битов порта C: | + | |
- | |Биты 0÷3|Строка клавиатуры| | + | |
- | |Бит 4|Если 0, то запускается кассетная лента| | + | |
- | |Бит 5|Сигнал записи на ленту| | + | |
- | |Бит 6|Если 0, то включается индикатор "CAPS"| | + | |
- | |Бит 7|Управление звуковым сигналом| | + | |
- | Все ясно! Индикатор "CAPS" включается потому, что в порт C записывается значение | + | |
- | <code> | + | |
- | 6 = &b00000110, а значит, шестой бит порта C "опрокинулся" в нуль. | + | |
- | ▲ ─────┬──── | + | |
- | └───────────────────────┘ | + | |
- | </code> | + | |
- | + | ||
- | Фактически только четыре //младших// бита порта C определяют номер строки матрицы клавиатуры. Для "маскирования" (игнорирования) значений четырёх старших битов достаточно вместо команды ''OUT &HAA,6'' выполнить команду: | + | |
- | <code> | + | |
- | OUT &HAA,6 OR (INP(&HAA) AND &HF0) | + | |
- | │ │ │ ┌── !!! | + | |
- | ▼ ▼ ▼ ▼ | + | |
- | &B00000110 OR (&B∗1∗∗∗∗∗∗ AND &B11110000) = &B∗1∗∗ 0110 | + | |
- | </code> | + | |
- | (символом "∗" отмечены биты, состояние которых в данном случае роли не играет). | + | |
- | + | ||
- | {{anchor:e1011-02}} __//Пример 2//__. Включение индикатора "CAPS" (если он выключен!) можно осуществить следующей командой: \\ {{.examples:1011-02.bas|}} \\ [[+tab|wmsxbpge>1011-02.bas]] | + | |
- | + | ||
- | <code> | + | |
- | OUT &HAA, INP(&HAA) XOR &B01000000 | + | |
- | │ │ ┌─ !!! | + | |
- | ▼ ▼ ▼ | + | |
- | &B∗1∗∗∗∗∗∗ XOR &B01000000 = &B∗0∗∗∗∗∗∗ | + | |
- | </code> | + | |
- | + | ||
- | {{anchor:e1011-03}} __//Пример 3//__. \\ {{.examples:1011-03.bas|}} \\ [[+tab|wmsxbpge>1011-03.bas]] | + | |
- | <code> | + | |
- | 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 | + | |
- | </code> | + | |
- | В ходе работы программы индикатор "CAPS" сохранит состояние, в котором он находился до пуска программы! | + | |
- | + | ||
- | + | ||
- | {{anchor:e1011-04}} __//Пример 4//__. Получим матрицу клавиатуры при помощи оператора ''OUT''! \\ {{.examples:1011-04.bas|}} \\ [[+tab|wmsxbpge>1011-04.bas]] | + | |
- | <code> | + | |
- | 10 INPUT "Номер строки";N | + | |
- | 20 INPUT "Номер столбца";T | + | |
- | 30 OUT &HAA,INP(&HAA) AND &HF0 OR N | + | |
- | 40 B=((INP(&HA9) AND 2^T)=0) | + | |
- | 50 IF B THEN PRINT "Клавиша нажата":END | + | |
- | 60 GOTO 30 | + | |
- | </code> | + | |
- | + | ||
- | {{anchor:e1011-05}} __//Пример 5//__. Включение и выключение кассетной ленты. Операторы ''MOTOR ON'' и ''MOTOR OFF'' (подробнее о них [[09#motor|здесь]]) могут быть имитированы с помощью команды: \\ {{.examples:1011-05.bas|}} \\ [[+tab|wmsxbpge>1011-05.bas]] | + | |
- | <code> | + | |
- | OUT &HAA, INP(&HAA) XOR &B00010000 | + | |
- | </code> | + | |
- | + | ||
- | ==== X.10.2. Программируемый звуковой генератор (PSG) ==== | + | |
- | + | ||
- | Вначале приведём два примера записи информации в PSG при помощи портов ввода–вывода. | + | |
- | + | ||
- | {{anchor:e1012-01}} __//Пример 1//__. \\ | + | |
- | <WRAP group> | + | |
- | <WRAP half column> | + | |
- | {{.examples:1012-011.bas|}} \\ [[+tab|wmsxbpge>1012-011.bas]] | + | |
- | <code> | + | |
- | 10 SOUND 7,8 'Шум из канала A | + | |
- | 20 SOUND 8,15 'Громкость | + | |
- | 30 SOUND 6,26 'Частота звука | + | |
- | 40 END | + | |
- | </code> | + | |
- | </WRAP> | + | |
- | <WRAP half column> | + | |
- | {{.examples:1012-012.bas|}} \\ [[+tab|wmsxbpge>1012-012.bas]] | + | |
- | <code> | + | |
- | 10 OUT &HA0,7:OUT &HA1,8: A=INP(&HA2) | + | |
- | 20 OUT &HA0,8:OUT &HA1,15:B=INP(&HA2) | + | |
- | 30 OUT &HA0,6:OUT &HA1,26:C=INP(&HA2) | + | |
- | 40 PRINT A;B;C | + | |
- | </code> | + | |
- | </WRAP> | + | |
- | </WRAP> | + | |
- | <code> | + | |
- | run | + | |
- | 8 15 26 | + | |
- | Ok | + | |
- | </code> | + | |
- | + | ||
- | {{anchor:e1012-02}} __//Пример 2//__. Представьте, что Вы находитесь на берегу Чёрного моря в районе Ялты. Закройте глаза и … \\ {{.examples:1012-02.bas|}} \\ [[+tab|wmsxbpge>1012-02.bas]] | + | |
- | <code> | + | |
- | 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 | + | |
- | </code> | + | |
- | + | ||
- | А теперь мы расскажем Вам, как можно "музицировать" при помощи //непосредственной// записи в регистры звукового генератора. Взгляните на следующую небольшую табличку: | + | |
- | + | ||
- | ^ Исполняемая \\ нота ^ Содержимое нулевого \\ регистра ^ Содержимое первого \\ регистра ^ | + | |
- | |<code>PLAY "O4C"</code>| 172 | 1 | | + | |
- | |<code>PLAY "O4C#"</code>| 148 | 1 | | + | |
- | |<code>PLAY "O4D"</code>| 125 | 1 | | + | |
- | |<code>PLAY "O4D#"</code>| 104 | 1 | | + | |
- | |<code>PLAY "O4E"</code>| 83 | 1 | | + | |
- | |<code>PLAY "O4F"</code>| 64 | 1 | | + | |
- | |<code>PLAY "O4F#"</code>| 46 | 1 | | + | |
- | |<code>PLAY "O4G"</code>| 29 | 1 | | + | |
- | |<code>PLAY "O4G#"</code>| 13 | 1 | | + | |
- | |<code>PLAY "O4A"</code>| 254 | 0 | | + | |
- | |<code>PLAY "O4A#"</code>| 240 | 0 | | + | |
- | |<code>PLAY "O4B"</code>| 227 | 0 | | + | |
- | |<code>PLAY "O5C"</code>| 214 | 0 | | + | |
- | + | ||
- | {{anchor:e1012-03}} __//Пример 3//__. Гамма "до–мажор". \\ {{.examples:1012-03.bas|}} \\ [[+tab|wmsxbpge>1012-03.bas]] | + | |
- | <code> | + | |
- | 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 | + | |
- | </code> | + | |
- | + | ||
- | {{anchor:e1012-04}} __//Пример 4//__. "В траве сидел кузнечик!" \\ {{.examples:1012-04.bas|}} \\ [[+tab|wmsxbpge>1012-04.bas]] | + | |
- | <code> | + | |
- | 10 'DEFINTA-Z:BEEP | + | |
- | 20 OUT &HA0,8:OUT&HA1,15:OUT &HA0,1:OUT &HA1,0:OUT &HA0,0:OUT &HA1,254 | + | |
- | 30 RESTORE 240:S=S+1:IF S=1 THEN K=14 ELSE K=12 | + | |
- | 40 FOR I=1 TO K:READ S1,S0,T1 | + | |
- | 50 OUT &HA0,8:OUT &HA1,15 | + | |
- | 60 OUT &HA0,1:OUT &HA1,S1:OUT &HA0,0:OUT &HA1,S0 | + | |
- | 70 TIME=0 | + | |
- | 80 T=TIME:IF T<T1 THEN 80 | + | |
- | 90 SOUND 8,0 'OUT &HA0,8:OUT &HA1,0:FOR J=1 TO 10:NEXT | + | |
- | 100 NEXT | + | |
- | 110 IF S=1 THEN 30 | + | |
- | 120 RESTORE 260:S=0 | + | |
- | 130 S=S+1:IF S=1 THEN K=20 ELSE K=14 | + | |
- | 140 FOR I=1 TO K:READ S1,S0,T1 | + | |
- | 150 OUT &HA0,8:OUT &HA1,15 | + | |
- | 160 OUT &HA0,1:OUT &HA1,S1:OUT &HA0,0:OUT &HA1,S0 | + | |
- | 170 TIME=0 | + | |
- | 180 T=TIME:IFT<T1 THEN 180 | + | |
- | 190 OUT &HA0,0:OUT &HA1,0:OUT &HA0,1:OUT &HA0,0:FOR J=1 TO 10:NEXT:NEXT | + | |
- | 200 IF S=1 THEN RESTORE 270:GOTO 130 | + | |
- | 210 OUT &HA0,8:OUT &HA1,15 | + | |
- | 220 OUT &HA0,1:OUT &HA1,0:OUT &HA0,0:OUT &HA1,254 | + | |
- | 230 RUN | + | |
- | 240 DATA 0,254,15,1,83,15,0,254,15,1,83,15,0,254,15,1,13,15,1,13,30 | + | |
- | 250 DATA 1,13,15,1,83,15,1,13,15,1,83,15,1,13,15,0,254,15,0,254,30 | + | |
- | 260 DATA 0,254,15,0,0,15,0,254,30 | + | |
- | 270 DATA 0,227,15,0,227,7.5,0,227,7.5,0,227,15,0,227,15 | + | |
- | 280 DATA 0,215,15,0,215,7.5,0,215,7.5,0,215,15,0,215,15 | + | |
- | 290 DATA 0,215,15,0,227,15,0,254,15,1,13,15,0,254,15,0,254,30 | + | |
- | 300 DATA 0,254,15 | + | |
- | </code> | + | |
- | + | ||
- | {{anchor:wait}} | + | |
- | ==== X.10.3. Другие порты. Оператор WAIT ==== | + | |
- | + | ||
- | Приведём примеры использования "содержимого" других портов. | + | |
- | + | ||
- | {{anchor:e1013-01}} __//Пример 1//__. Использование портов с адресами &H90 и &H91 для вывода символа на принтер. | + | |
- | + | ||
- | Вначале о "роли" первого бита порта с номером &H90: | + | |
- | <code> | + | |
- | 7–й бит 6–й бит 5–й бит 4–й бит 3–й бит 2–й бит 1–й бит 0–й бит | + | |
- | ┌───────┬────────┬────────┬────────┬───────┬───────┬───────┬───────┐ | + | |
- | │ ∗ │ ∗ │ ∗ │ ∗ │ ∗ │ ∗ │ │ ∗ │ | + | |
- | └───────┴────────┴────────┴────────└───────┴───────┴───▲───┴───────┘ | + | |
- | Принтер в режиме ON LINE (подключен к ПЭВМ): 0 ───────│ | + | |
- | Принтер в режиме OFF LINE (отключен от ПЭВМ): 1 ───────┘ | + | |
- | </code> | + | |
- | + | ||
- | A теперь: включите принтер и вставьте бумагу… | + | |
- | \\ {{.examples:1013-01.bas|}} \\ [[+tab|wmsxbpge>1013-01.bas]] | + | |
- | <code> | + | |
- | 5 CLEAR 300 | + | |
- | 10 INPUT"Слово";A$ | + | |
- | 15 WAIT &H90,2,255 'Вы включите, наконец, принтер или нет? | + | |
- | 20 A$=A$+CHR$(13)+CHR$(10) 'CHR$(13) - код возврата каретки; | + | |
- | 21 ' 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 | + | |
- | </code> | + | |
- | + | ||
- | {{anchor:e1013-02}} __//Пример 2//__. Считывание кода выведенного на экран символа: \\ {{.examples:1013-02.bas|}} \\ [[+tab|wmsxbpge>1013-02.bas]] | + | |
- | <code> | + | |
- | 10 K$=INKEY$:IF K$="" THEN 10 ELSE PRINT K$; | + | |
- | 20 PRINT INP(&H98):GOTO 10 | + | |
- | </code> | + | |
- | + | ||
- | Оператор ''WAIT'' используется сравнительно редко. Его синтаксис: | + | |
- | <WRAP group> | + | |
- | <WRAP half column> | + | |
- | <code> | + | |
- | WAIT P,M[,C] | + | |
- | </code> | + | |
- | </WRAP> | + | |
- | <WRAP half column> | + | |
- | , | + | |
- | </WRAP> | + | |
- | </WRAP> | + | |
- | где: | + | |
- | * ''WAIT'' ("ожидать") — служебное слово; | + | |
- | * P — арифметическое выражение, целая часть значения которого задаёт адрес порта; | + | |
- | * M и C — арифметические выражения, целые части значений которых принадлежат отрезку [0,255]. | + | |
- | + | ||
- | Оператор ''WAIT'' "заставляет" компьютер "ожидать", пока результатом "опроса" порта с указанным адресом не окажется число 0 (данный порт "работает" в режиме //чтения// ). Другими словами, этот оператор является "бесконечным циклом", который "ждёт", пока от порта ввода–вывода не придёт определённый сигнал. | + | |
- | + | ||
- | Вы можете прервать затянувшееся "ожидание", нажав клавиши <key>CTRL+STOP</key> (при этом Вы вернётесь в командный режим). | + | |
- | + | ||
- | Содержимое порта с указанным адресом заносится в некоторый регистр процессора Z–80, который мы назовём X. Далее содержимое регистра X комбинируется со значениями параметров M и С, по формуле: | + | |
- | <code> | + | |
- | X = (X XOR C) AND M | + | |
- | </code> | + | |
- | Если после этого содержимое регистра X окажется равным 0, то происходит "выход из оператора ''WAIT''". В противном случае порт вновь "опрашивается", и процесс повторяется. | + | |
- | + | ||
- | Приведём таблицу–"подсказку": | + | |
- | | X |0 0 0 0 1 1 1 1| | + | |
- | | C |0 0 1 1 0 0 1 1| | + | |
- | | | | | + | |
- | | X XOR C |0 0 1 1 1 1 0 0| | + | |
- | | M |0 1 0 1 0 1 0 1| | + | |
- | | | | | + | |
- | | (X XOR C) AND M |0 0 0 1 0 1 0 0| | + | |
- | Вопрос к читателю: Какой вид будет иметь таблица–"подсказка" при отсутствии параметра C ? | + | |
- | + | ||
- | {{anchor:e1013-03}} __//Пример 3//__. \\ {{.examples:1013-03.bas|}} \\ [[+tab|wmsxbpge>1013-03.bas]] | + | |
- | <code> | + | |
- | 10 WAIT &HAA,64,255 | + | |
- | + | ||
- | (&B∗1∗∗∗∗∗∗ XOR &B11111111) AND &B01000000 = &B00000000 = 0 ! | + | |
- | </code> | + | |
- | Эта программа закончит свою работу, если загорится индикатор "CAPS". | + | |
- | + | ||
- | __//Пример 4//__. | + | |
- | * <code>WAIT &H90,2,255 'Ожидается включение принтера </code> | + | |
- | * <code>WAIT &H90,2,0 'Ожидается отключение принтера</code> | + | |
- | + | ||
- | + | ||
- | __//Дополнение//__. Работа с портом ввода–вывода с адресом &h0C | + | |
- | + | ||
- | Предварительно кратко опишем структуру порта &h0C… | + | |
- | <code> | + | |
- | Старший ┌───┬───┬───┬───┬───┬───┬───┬───┐ Младший | + | |
- | бит │ ∗ │ ∗ │ ∗ │ ∗ │ ∗ │ ∗ │ ∗ │ ∗ │ бит | + | |
- | └─▲─┴─▲─┴─▲─┴─▲─┴─▲─┴─▲─┴─▲─┴─▲─┘ | + | |
- | │ │ │ │ │ │ │ └── Общий бит готовности сети | + | |
- | │ │ Не используются │ (0: сеть готова) | + | |
- | │ │ └────── Бит регистрации данных | + | |
- | │ │ (0: данные поступили) | + | |
- | │ └── Бит направления поступления информации (1: от учителя) | + | |
- | └────── Бит направления поступления информации (1: от ученика) | + | |
- | </code> | + | |
- | + | ||
- | А теперь два примера использования данного порта… | + | |
- | + | ||
- | __//Пример 1//__. //Посылка// байта по сети | + | |
- | <code> | + | |
- | 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 ; | + | |
- | </code> | + | |
- | + | ||
- | __//Пример 2//__. //Приём// байта из сети | + | |
- | <code> | + | |
- | 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 ; | + | |
- | </code> | + | |
- | + | ||
- | ====== Диск с примерами ====== | + | |
- | + | ||
- | {{.examples:examples10.dsk|Загрузить образ диска}} | + | |
- | + | ||
- | [[+tab|wmsxbpged>examples10.dsk|Открыть диск в WebMSX]] | + | |
- | + | ||
- | + | ||
- | ---- | + | |
- | + | ||
- | [<>] | + | |
- | + | ||
- | + | ||
- | {{tag>MSX BASIC Book_msxbpg}} | + |