Инструменты пользователя

Инструменты сайта


msx:basic_programming_guide:10

Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

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}}+
msx/basic_programming_guide/10.1587640225.txt.gz · Последние изменения: 2020-04-23 14:10 (внешнее изменение)