X.8. Ф у н к ц и я FRE Garbage collection ("чистка памяти","сборка мусора") - действия системы динамического распределения памяти для обнаружения неис- пользуемых программой блоков памяти и присоединения их к списку свободной памяти для повторного использования. Англо-русский словарь по программированию и информатике Информацию о размере свободной области ("Free Area") в RAM можно полу- чить с помощью функции FRE, обращение к которой имеет вид: FRE(A) где: FRE ("FREe"-"свободный") - служебное слово; A - арифметическое или строковое выражение, причем для интерпретато- ра важным является лишь тип выражения, а не его значение. На практике применяется следующий синтаксис: FRE(0) или FRE("") . Функция FRE(0) возвращает количество байтов, оставленных для расшире- ния PIT, VT, стека, строковой области и блока управления файлами. П р и м е р 1. ───────────── 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 Функция FRE(0) выдает сообщение: "Out of memory" ("Н е х в а т а е т п а м я т и") при достижении значения, меньшего 145 байтов, минимально допустимого для стека системы MSX-BASIC. Посмотрите (предварительно нажав кнопку "RESET"): CLEAR 28868:PRINT FRE(0) CLEAR 28869:PRINT FRE(0) 147 Out of memory Ok Ok Заметим,что слово VARTAB отличается от слова TXTTAB на 2 байта (при от- сутствии программы!), поэтому, добавив эти 2 байта к 145 байтам, необходи- мым для работы стека, получаем число 147! Функция FRE("") возвращает количество свободных байтов в строковом про- странстве. Например: print FRE("") X$="2²"+"3²":print FRE("") 200 196 Ok Ok Кроме того, функция FRE("") выполняет важное дополнительное действие. Оно связано с наличием в MSX-BASIC строк переменной длины, обработка кото- рых может привести к явлению "фрагментации памяти" (внутри строковой обла- сти появляются участки,содержащие неиспользуемую информацию -"м у с о р"). Поэтому, если в качестве аргумента функции FRE задано выражение строково- го типа, перед вычислением объема свободной памяти функция выполняет "с б о р к у м у с о р а", т.е. удаление всех неиспользуемых данных и освобождение занимаемых ими областей. П р и м е р 2. Оказывается,что если у Вас в начале программы встречает- ───────────── ся оператор A$="ABCD"+"EF", а затем-оператор A$="X"+"Y", то Вы сразу же создадите 6-байтовое пространство, заполненное "мусором"! Покажем это: 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 └─── "м у с о р" Из примера следует, что строки хранятся в строковом пространстве в том порядке, в каком они были определены. Таким образом, функция FRE("") изменила положение значения строковой переменной (это и называется "с б о р к о й м у с о р а"). Если под строки зарезервирован большой объем строкового пространства и определено много символьных переменных, время "сборки мусора" может соста- вить несколько минут. При выполнении этой операции компьютер полностью "застывает". Посмотрите... П р и м е р 3. ───────────── 10 CLEAR 5000 'Объявлен размер строковой области - 5000 байтов 15 DEFINT A-Z:DIM A$(1500):FOR I=1 TO 1500:A$(I)="2"+"":NEXT 30 'Размещение данных в строковой области, "мусора" нет! 40 TIME=0:PRINT FRE(""),TIME/60/60"мин" run run ·3500··········3.61638888888 мин ·3500··········3.3716666666667 мин Ok (для MSX-1) Ok (для MSX-2) Интересно, что при изменении в строке 10 оператора CLEAR 5000 на опера- тор CLEAR 1600, результат получается почти тот же (≈3.607 мин. для компью- тера MSX-1 и ≈3.38 мин. для компьютера MSX-2)! Е д и н с т в е н н ы й способ уменьшить время "сборки мусора" - это использовать минимальное количество строк и особенно строковых массивов! Следует заметить, что некоторые строки хранятся в тексте самой програм- мы и, таким образом, не занимают места в строковой области. П р и м е р 4. ───────────── 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 нится в тексте программы)... Таким образом, строковая область является областью памяти, резервируе- мой для хранения строковых данных. Если Вы хотите зарезервировать в стро- ковом пространстве место для хранения 10 строк, содержащих каждая макси- мум 5 символов, то воспользуйтесь, например, оператором цикла: FOR I=1 TO 10:A$(I)=SPACE$(5):NEXT Во избежание "сборки мусора": α) определяйте все переменные в начале программы; β) используйте строковые функции MID$, LEFT$, RIGHT$. Перед работой со следующим примером выключите, а затем снова включите Ваш компьютер. П р и м е р 5. ───────────── 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 Отметим, что строковые функции MID$, LEFT$, RIGHT$ не изменяют указате- ли на значения строковых переменных. Обычный же оператор присваивания, ра- зумеется, указатели изменяет! Покажем это на примере (не забудьте о коман- де CLEAR !). П р и м е р 6. ───────────── a$="полет" Ok print hex$(peek(varptr(a$)+2)); hex$(peek(varptr(a$)+1)) F164 Ok a$="налет" Ok print hex$(peek(varptr(a$)+2)); hex$(peek(varptr(a$)+1)) F15F Ok а теперь... mid$(a$,1,2)="по" Ok print hex$(peek(varptr(a$)+2)); hex$(peek(varptr(a$)+1)) F15F ◀── Обратите внимание, это значение совпадает с предыдущим! Ok Как видим, значение указателя в последнем случае не изменилось! γ) при необходимости используйте оператор SWAP A$,В$ , который не меня- ет расположение значений переменных, а лишь меняет местами указатели на эти значения. Проиллюстрируем этот факт на примере... П р и м е р 7. ───────────── clear Ok print HEX$(PEEK(&HF69C)); HEX$(PEEK(&HF69B)) F168 Ok A$="Зачет":B$="Автомат" Оk for t=0 to len(a$)+len(b$):print chr$(peek(&hF168-t));:next течаЗтамотвАА Ok swap A$,B$ Ok for t=0 to len(a$)+len(b$):print chr$(peek(&hF168-t));:next течаЗтамотвАА Ok И наконец, функция FRE() может помочь Вам также в з а щ и т е Вашей программы. Например, в "укромном" месте программы, работающей со строко- вой информацией, поместите оператор X$=SPACE$(FRE(""))- конечно,Вы должны учесть, что целая часть значения аргумента функции SPACE$ должна принадле- жать отрезку [0,255]!. Это удержит "любознательных" от модификации значе- ний переменных Вашей программы (разумеется, в данном случае строковых)! Посмотрите: 10 X$=SPACE$(FRE("")):Y$="2"+Y$ run Out of string space in 10 Ok X.9. Р а б о ч а я о б л а с т ь В рабочей области содержатся системные подпрограммы, системные перемен- ные и "ловушки", используемые интерпретатором во время выполнения операто- ров Вашей программы. В рабочей области хранятся данные о позиции курсора, цвете текста, состоянии функциональных клавиш и другая полезная информа- ция, инициализируемая при включении компьютера. ┌─────────────────────────────────────────────────────────────────┐ │ Адрес, отмечающий н а ч а л о рабочей области, указан в │ │ самой этой области в слове HIMEM, содержимое которого за- │ │ нимает 2 байта, расположенных с адреса &HFC4A . │ └─────────────────────────────────────────────────────────────────┘ Еще раз напомним Вам, что адреса, занимающие два байта, всегда записы- ваются так: вначале записывается содержимое младшего байта, а затем - со- держимое старшего байта! Отметим, что значением выражения 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-2. Ответим теперь на Ваш очевидный вопрос: ┌───────────────────────────────────────┐ │ Как воспользоваться этой таблицей? │ └───────────────────────────────────────┘ П р и м е р 1. Ниже приведены программа, останавливаемая нажатием кла- ───────────── виши "GRAPH": 10 Z=PEEK(&HFBEB):IF Z<>251 THEN 10 и программа, останавливаемая нажатием клавиш "SHIFT"+"CTRL": 10 Z=PEEK(&HFBEB):IF Z<>(254 AND 253) THEN 10 А теперь ответ на Ваш следующий вопрос: ┌─────────────────────────────────────────┐ │ А как получить матрицу клавиатуры ? │ └─────────────────────────────────────────┘ Для "чтения" нажатой клавиши достаточно "прочесть"слово NEWKEY (11 бай- тов) по адресу &HFBE5 из таблицы системных переменных. П р и м е р 2. Программа "пробегает" все клавиши и возвращает позицию ───────────── нажатой клавиши (X,Y) матрицы клавиатуры. 11 значений, записанных в слове NEWKEY, соответствуют 11 строкам матрицы клавиатуры. Если не нажата ни одна клавиша, содержанием каждого из 8 байтов, соответ- ствующих строке матрицы является 1. Это фиксируется двоичным числом &B11111111=255. Когда же клавиша нажата, считанное на этой строке значе- ние отличается от 255: бит соответствующей колонки "сбрасывается" в 0. 10 FOR Y=0 TO 10:Z=PEEK(&HFBE5+Y) 30 IF Z=255 THEN 80 '──▶ 40 PRINT"Y=";Y:Z$=RIGHT$("00000000"+BIN$(Z),8) 60 PRINT"X=";8-INSTR(Z$,"0"):PRINT 80 NEXT:GOTO 10 '──▶ X.9.2. Д и н а м и ч е с к а я к л а в и а т у р а [46] Промедление с легким делом превращает его в трудное, промедление же с трудным делом пре- вращает его в невозможное. Д.Лоример Исследуем один подход к разработке учебных программ, работающих под управлением интерпретатора MSX-BASIC. Существенная особенность этого под- хода состоит в том, что программа в процессе выполнения модифицируется (происходит изменение отдельных строк BASIC-программы или добавление но- вых строк). Считается,что допущение самомодификации программы во время вы- полнения является признаком плохого стиля программирования,поэтому начнем с примера, который показывает, что предлагаемый подход является не только оправданным, но и в ряде случаев единственно возможным. Пусть необходимо табулировать функцию y=f(x), конкретно x², то есть для каждого значения аргумента вычислить значение функции и результат за- писать в таблицу. Соответствующая программа выглядит следующим образом: 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 Части программы между строками 30 и 100, 100 и 999 содержат операторы, обеспечивающие масштабирование, заполнение таблицы, защиту от ошибочных действий пользователя и т.д. Простота программы обусловлена тем, что зада- ча табулирования решается для ф и к с и р о в а н н о й функции. Попыта- емся теперь разработать программу, которая позволяет табулировать любую функцию одной переменной, аналитическое выражение которой вводится с кла- виатуры! Для реализации на ПЭВМ"YAMAHA" используем специальный механизм, введен- ный Дж.Баттерфилдом (J.Batterfield) и названный им принципом "д и н а м и- ч е с к о й к л а в и а т у р ы". В к о м а н д н о м (!) режиме информация,набираемая пользователем на клавиатуре, аппаратно записывается в буфер (называемый в дальнейшем б у- ф е р о м к л а в и а т у р ы , БК). БК размещается в рабочей области с адреса &HFBF0 по адрес &HFC17 и занимает 40 байтов. При нажатии клавиши "RETURN" содержимое БК считывается интерпретатором и выполняется соответ- ствующая команда. Имитация действий пользователя на основе принципа "динамической клавиа- туры" осуществляется следующим образом: 1) с помощью оператора INPUT вводится текст запроса (в данном случае - аналитическое выражение табулируемой функции) в символьную строку F$ (в приведенной ниже программе - строка 10); 2) символьная строка дополняется впереди номером, а в конце - кодом ко- манды "RETURN" (строки 15 и 1890): F$="номер строки 1"+F$+CHR$(13) ; 3) строка побайтно переписывается в БК, начиная с aдреса &HFBF0, при помощи оператора POKE и функции PEEK (подпрограмма, начинающаяся со стро- ки 1880); 4) строка S$="goto"+"номер строки 2"+CHR$(13), где н о м е р с т р о- к и 2 - номер строки программы, куда после модификации необходимо пере- дать управление, побайтно переписывается в БК (строка 25); 5) выполнение программы прекращается командой END, в результате проис- ходит переход из программного режима в командный. Интерпретатор считывает содержимое БК до первого появления CHR$(13) и выполняет его как команду, то есть модифицирует строку с номером н о м е р с т р о к и 1. Далее считывается остаток содержимого БК до второго появления CHR$(13), и он также выполняется интерпретатором, как команда, после чего происходит пе- реход в программный режим с передачей управления в строку с номером н о- м е р с т р о к и 2. Таким образом, указанный алгоритм решает задачу автоматической модифи- кации программы в соответствии с текстом запроса, вводимого пользователем с клавиатуры, и запуска ее с указанного номера строки. П р и м е р. ─────────── 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/2 56)*256:GOTO 2050 1990 N=0 2000 FOR I=AD TO AD+LEN(F$)-1:N=N+1 2010 POKE I,ASC(MID$(F$,N,1)):NEXT 2020 IF LEN(F$)19 THEN CLS:LOCATE 1,10:PRINT"Эта программа имеет огра ничение:":GOTO 2420 ELSE GOTO 2460 2420 PRINT:PRINT "Длина формулы не должна превосходить 19 символов!";L EN(F$) 2440 PRINT:LOCATE 1,23:PRINT"Для продолжения нажмите любую клавишу" 2450 W$=INKEY$:IF W$="" THEN 2450 ELSE GOTO 10 2460 RETURN'──▶