Экономия памяти - проблема, с которой редко стакиваются начинающие программисты, но по мере того, как начинает расти их уровень подготовки, начинают расти и размеры программ и ужесточаются требования по экономному расходованию байтов.
48 килобайт - много это или мало? Для начинающих, конечно очень много, но посмотрите фирменные программы, выполненные лучшими программистами мира. Это сотни экранов графики, десятки страниц текста. Если бы только знать, какими трудами это дается!
Мы начнем с малого. У английских программистов есть хорошая поговорка -"Позаботься о своих байтах, а килобайты о себе сами позаботятся."
Итак, из 48 K "Спектрума" около 7K уходят на память экрана, а есть еще область системных переменных, область машинного и программного стеков, а также стека калькулятора, область графики пользователя UDG. Одним словом реально для написания программы и размещения данных Вы располагаете объемом чуть больше 40 K.
Это по-прежнему немало, но представьте себе, что Вы пишете базу данных или текстовой редактор, в которых Вы отводите как можно больше памяти под хранение информации, скажем 30K, тогда на Вашу программу остаются крохи. То же самое, если Вы пишете игру и хотите иметь в ней скажем 20 игровых экранов (как в программе MANIC MINER). Даже если Вы скомпрессируете каждый экран до 1.5K, все равно графика займет порядка 30K.
И это еще не все. Дело в том, что при программировании на БЕЙСИКе информация хранится как бы дважды. Во-первых, текст вашей программы находится в области, которая начинается с адреса, на который указывает системная переменная PROG (23635,23636). Обычно, если никакая периферия не подключена, то эта область начинается с адреса 23755. Во-вторых, значения всех переменных и, соответственно и массивов, которые Вы используете в своей программе, хранятся в области переменных, которая начинается с адреса, на который указывает системная переменная VARS (23627,33628). Адрес, на который указывает VARS - величина переменная, в отличие от PROG и зависит от размера текста программы. Это естественно, ведь область переменных расположена после программной области и чем длиннее вторая, тем выше адрес, с которого начинается первая.
Как определить адрес начала области, зная содержимое системной переменной Вы наверное знаете:
LET ad=PEEK 23627 +256*PEEK 33628
Экономия памяти должна проводиться грамотно. При этом не должна нарушаться удобочитаемость программ. Те приемы, которые мы здесь дадим, помогут Вам прояснить и некоторые туманные моменты, ставящие в тупик начинающих, когда они прорабатывают листинги БЕЙСИК-программ, написанных другими авторами. Наиболее частый вопрос, который мы встречаем из этой области:
"Скажите, почему в БЕЙСИК-загрузчиках программ иногда встречается запись типа LET a=VAL"15", ведь гораздо проще и короче написать LET a=15?"
С этого мы и начнем.
Числа
Числа являются самыми большими расточителями памяти в "Спектруме". Если Вы думаете, что число .134 занимает три байта, а число .13 - два байта, то Вы глубоко заблуждаетесь. Они хранятся в памяти в пятибайтной (интегральной) форме, но и это еще не все затраты. Давайте посмотрим как число .134 записано в программной строке. Напишите:
999 LET a=.134:LET p=PEEK 23635 + 256*PEEK 23636: FOR f=p TO p+17: PRINT f,PEEK f : NEXT f
Теперь дайте команду RUN и Вы увидите на экране две колонки цифр. Левая показывает адреса, в которых хранится первый оператор строки 10 LET a=.134:, а правая
показывает код символа, содержащегося в каждом адресе.
Адрес Код Значение
23755 3 Номер строки:
23756 231 3+231*356=999
23757 78 Длина строки:
23758 0 78+0*256-78
23759 211 LET
23760 97 а
23761 61 =
23762 46 . (точка)
23763 49 1
23764 51 2
23765 52 3
23766 14 Код, означающий что за ним следует число, записанное в
интегральной форме.
23767 126
23768 9 Это и есть число 0.134 в 5-ти байтной (интегральной) форме.
23769 55
23770 75
23771 199
23772 58 : (двоеточие)
Из всего этого вытекает, что числа расходуют на 6 байтов больше, чем это кажется на первый взгляд.
Несколько приемов
Может быть обойтись без чисел, чтобы не тратить на них память? Для ряда чисел это сделать можно путем применения вместо них функций. Например, вместо нуля можно использовать NOT PI. Вместо семи байтов здесь расходуется всего 2. PI - это число "пи", которое уже хранится в ПЗУ компьютера, а функция NОТ дает 0 для любого аргумента, отличного от нуля.
Вместо 1 применяют функцию SGN PI, поскольку число "пи" всегда положительное, эта функция всегда дает 1. Экономия здесь также 2 байта вместо 7.
С той же эффективностью применяют INT PI вместо числа 3.
Другие числа получают путем комбинирования приемов, причем здесь есть множество возможных путей. Например, если Вами уже введена переменная L, равная 1 путем 10 LET L=SGN PI, то дальше Вы можете это использовать например так:
20 LET z=L+L 30 LET f = z + z
Эта запись "съедает" 22 байта. Ее традиционной альтернативой, расходующей 30 байтов является:
20 LET z=2 30 LET f=4
Другой способ сокращения расхода памяти для произвольных чисел - применение функции VAL, что позволяет экономить три байта на каждом ее применении. Эта функция заменяет строковую переменную ее значением. Поскольку в памяти компьютера под строковую переменную отводится только один байт на символ, а на число - 5 байтов, то это и является основным источником экономии.
Посмотрите сами как в памяти хранятся строки: 10 LET z= 2 и 10 LET z=VAL"2"
Другая "маленькая хитрость" связана с использованием функции CODE для получения целых чисел от 32 до 164. Функция CODE возвращает код символа, стоящего после нее. Так, LET s= CODE "1" сделает s равным сорока девяти, поскольку код единицы в системе ASCII равен 49. Возникающую при этом экономию можете теперь подсчитать сами.
Вы наверное знаете, что после 164-го кода в Синклер-компьютерах расположены уже не символы, а токены ключевых слов, по токену на каждый код. Прелесть в том, что даже такое длинное слово как RANDOMIZE занимает только один байт и выражается одним кодом - 249. Теперь было бы желательно использовать возможности функции CODE, чтобы получать числа от 165 до 255. Действительно, запись типа LET s=CODE "RANDOMIZE" присвоит переменной s значение 249. Вот только вся беда, что сделать такую запись Вам не удастся.
Попробуйте, и у Вас ничего не выйдет, потому что когда Вы наберете LET s = " , у Вас на экране будет курсор L или C, а Вам надо курсор K, чтобы ввести ключевое слово RANDOMIZE. Набирать по буквам его нельзя, ведь это же токен. Здесь Вам поможет еще одна "маленькая "хитрость", связанная с использованием оператора THEN, поскольку после него появляется необходимый Вам курсор K. Делайте так: наберите LET s = CODE "THEN RANDOMIZE",
a теперь сдвиньте курсор влево и с помощью DELETE удалите THEN.