В.В. Ермаков (Магаданская обл.)
МНОГОМЕРНЫЕ МАССИВЫ НА БЕЙСИКЕ
Одним из недостатков вильнюсской версии БЕЙСИКа (24.07.86 г.) является невозможность использования в программах многомерных массивов (Наука и жизнь. - 1989. - № 3; Информатика и образование. - 1989 - № 1). Причина тому - ошибка, внесённая в процессе разработки языка. Суть её в следующем.
Известно, что адрес A i1, i2....in элемента массива, например В (N1, N2, N3,....Nn), можно найти, пользуясь соотношением:
А = i1 + i2N1 + i3N1N2+ .... + inN1N2N3....Nn-1, (1)
где in = 0... Nn-1,
n = 0... 255.
В существующей версии языка реализована формула
A = in + i2N1 + i3N2+... + inNn-1 (2)
Сравнение (1) и (2) показывает, что компьютер верно работает лишь с одно- и двумерными массивами.
Ошибку можно исправить подпрограммой, в которой реализован алгоритм (1):
10 DATA &O13400, &O12403, &O12601, &O5720, &O5303, &O3421, &O10305, &O166405, &O177776, &O5405, &O12602, &O10046, &O10146, &O14001, &O10546, &O4737, &O160442, &O10102, &O12605, &O77507, &O62601, &O12600, &O754, &O137, &O160172 20 FOR I% = &O400 ТО &O460 STEP &O2 30 READ J% 40 S=S+J% 50 POKE I%,J% 60 NEXT 70 IF S><111935 THEN PRINT STRING$(20,7) "Ошибка, проверьте данные в строке 10" 80 PRINT "Подпрограмма загружена" 90 END
Рис. 1
Для этого нужно "заставить" компьютер при вычислении адресов элементов массивов обращаться не к ошибочной подпрограмме, а к исправной, которая после запуска программы рис. 1 будет загружена в ОЗУ с адреса 4008. Сделать это можно, если внести в программу, которая работает с массивами размерностью более 2, небольшое дополнение
Программа
FOR I% = PEEK(&2002) ТО PEEK(&O2004) STEP &O2 IF PEEK (I%) = -8146 THEN POKE I%,&O400 NEXT
Рис. 2
Записать его нужно в текст основной программы до первого обращения к многомерному массиву, лучше всего в начало программы. Это "дополнение" заменит код 1600568 (-814610) в скомпилированной программе (адрес подпрограммы с ошибкой) на 4008 (адрес, куда загружена подпрограмма рис. 1). Ячейки 20028 и 20048 содержат соответственно адреса начала и конца скомпилированной программы. Теперь можно работать с массивами размерностью, как указано в описании языка БЕЙСИК, даже 255, если кому-то удастся описать массив такой размерности оператором DIM.
Для удобства работы машинные коды исправленной подпрограммы можно записать на магнитную ленту командой BSAVE "DIM",&O400,&O460 и загружать её командой BLOAD"DIM каждый раз, когда работаете с многомерными массивами. Однако если позволяет память, программу рис. 1 можно целиком поместить в начало основной программы.
Следует иметь в виду, что в исправленной подпрограмме (см. рис. 1) с целью экономии памяти исключена диагностика ошибок. Поэтому перед внесением дополнения (см. рис. 2) в текст программы, её следует отладить.
Предлагаю читателям короткую игровую программу "Слалом". Цель - управляя "лыжником" (клавиши ←, →) нужно пройти трассу, огибая камни.
5 REM ***SLALOM*** 6 REM БЕЙСИК БК-0010-01 7 REM Автор - В.Ермаков 10 CLS 20 A$=CHR$(182) 30 В$=CHR$(127) 40 С$=STRING$(8, "*") 50 PRINT AT(3,4) "Длина трассы 200 метров" 60 PRINT "уровень 1...5" 70 I$=INKEY$ 80 IF I$="" THEN 70 ELSE KK%=VAL(I$) 90 IF KK%>=1 AND KK%<=5 THEN 130 100 PRINT STRING$(7,7) 110 PRINT "НАЖИМАЙ КЛАВИШИ ОТ '1' ДО '5'" 120 GOTO 60 130 CLS 140 I%=10% 150 J%=0% 160 KK%=200%S*KK% 170 POKE &O177660, &O100 180 IF INP(&O177716,&O100)=&O0 THEN A%=PEEK(&O177662) ELSE 200 190 IF A%=8% THEN I%=I%-1% ELSE IF A%=25% THEN I%=I%+1% 200 IF I%<0% THEN I%=0% 210 IF I%>30% THEN I%=30% 220 IF POINT(I%*8%+4%,104%)=1% THEN 340 230 J%=J%+1% 240 PRINT AT(I%,10) A$ 250 N%=J%\200%+1% 260 FOR I%=1 TO N% 270 PRINT AT(RND(K%)*31,23) В$ 280 NEXT 290 PRINT 300 IF KK%-J%<0% THEN 370 310 FOR K%=1 TO KK%-J% 320 NEXT 330 GOTO 180 340 PRINT STRING$(20,7) 350 PRINT AT(3,10) "ВЫ ПРОЕХАЛИ"; FIX(J%*200/KK%); "M" 360 GOTO 380 370 PRINT AT(2,10) С$"ФИНИШ"C$ 380 POKE &O177660, 0% 390 GOTO 50
Заключение рецензента
Работать с многомерными массивами в вильнюсской версии БЕЙСИКа БК0010.01 в настоящее время действительно невозможно. Практика показывает: "даже трёхмерные массивы оказываются транслятору не по зубам" (Наука и жизнь. - 1989 - №3 (Обзорная статья про бейсик, которую нет смысла распознавать. А в ней ссылка на это.)). Конечно, этот недостаток версии не очень существенный: необходимость работы с многомерными массивами возникает достаточно редко, а организовать использование одномерного массива как многомерного нетрудно.
Можно ввести вспомогательные индексы i1,i2,...in и рассчитывать каждый раз в подпрограмме индекс одномерного массива "i" по формуле I = i1 +i2N1+i3N1N2+ ... + inN1N2...Nn-1, где N1,N2,...Nn-1 - размерности воображаемого n-мерного массива; iК = 0,...,NK-1 .
При этом должно выполняться условие N = N1N2N3...Nn, где N - размерность одномерного массива.
Ясно, что гораздо удобнее пользоваться многомерными массивами в привычной форме. Читатель В.В. Ермаков предложил короткую программу в машинных кодах, позволяющую устранить описанный выше недостаток Бейсика БК0010.01.
Эта подпрограмма в начале сеанса работы загружается в системную область с восьмеричного адреса 400. Затем в начало создаваемой программы на БЕЙСИКе, работающей с многомерными массивами, вставляется обязательный участок текста. Он предназначен для организации подмены адреса подпрограммы, вычисляющей адрес элемента массива по его индексам, во всей откомпилированной программе на адрес введённой подпрограммы. Адреса начала и конца кода программы содержатся в ячейках с восьмеричными адресами 2002 и 2004 соответственно.
Метод подмены очень интересен, но возникает опасность, что в откомпилированной с БЕЙСИКа программе код 160056 (десятичный -8146) является не только адресом подпрограммы, вычисляющей адрес элемента массива. Рассмотрим, какие могут быть варианты.
Во-первых, код -8146 может быть десятичной целочисленной константой -8146%. Этот вариант вполне вероятен, но область данных БЕЙСИК-программы обычно располагается вне диапазона адресов, содержащихся в ячейках 2002, 2004. Вследствие этого случай практически не опасен.
Во-вторых, код -8146 может быть частью текстовой строки с символами 'Ю.' (CHR$(&0340); CHR$(&056)). Такое тоже возможно, но практически безопасно по той же причине.
В-третьих, указанный код может быть машинной командой вычитания: SUB R0,@-(R6). Этот вариант возможен, но достаточно маловероятен из-за редкого способа адресации - косвенный режим автоуменьшения регистра программного стека. Скорее всего такая команда вообще не может быть сгенерирована компилятором БЕЙСИКа, но может быть включена внутрь программного кода программистом. Если же это случится, то программа работать не будет.
Избавиться от указанных опасностей достаточно сложно, так как адрес подпрограммы 160056 встречается в откомпилированном коде в различных вариантах окружения и различить, когда подмена адреса верна, трудно.
В целом программа В.В. Ермакова поучительна и полезна.
"Слалом"
Игра "Слалом" (В.В.Ермаков) впервые напечатана в № 8 за 1990 г. (прим. gid: см.выше, а это - рецензия из какого-то более позднего номера. Помещено сюда, чтобы всё в одном месте было.)
С некоторым опозданием поступила рецензия на эту игру. Откликов от читателей на опубликованную программу было много, поэтому приводим полностью текст рецензии и листинг отредактированной программы.
5 REM ***SLALOM*** 6 REM БЕЙСИК БК0010.01 7 REM Автор - В.Ермаков 10 CLS 20 A$=CHR$(182) 30 В$=CHR$(127) 50 ? AT(3%,4%) "ДЛИНА ТРАССЫ 200 МЕТРОВ" 60 ? AT(3%,5%) "УРОВЕНЬ 1...5" 70 I$=INKEY$ 80 IF I$="" THEN 70 ELSE KK%=VAL(I$) 90 IF KK%>=1% AND KK%<=5% THEN 130 ELSE ? STRING$(7%,7%)"НАЖИМАЙ КЛАВИШИ ОТ '1' ДО '5'" 120 GOTO 60 130 CLS 140 I%=10% 150 J%=0% 160 KK%=200%*KK% 170 POKE &O177660, &O100 180 IF INP(&O177716,&O100)=&O0 THEN A%=PEEK(&O177662) ELSE 220 190 IF A%=8% THEN I%=I%-1% ELSE IF A%=25% THEN I%=I%+1% 200 IF I%<0% THEN I%=0% ELSE IF I%>30% THEN I%=30% 220 IF POINT(I%*8%+4%,104%)=1% THEN 340 230 J%=J%+1% 240 ? AT(I%,10) A$ 260 FOR K%=1 TO J% STEP 200% 270 ? AT(RND(K%)*31%,23%) В$ 280 NEXT K% 290 ? 300 IF KK%-J%<0% THEN 370 310 FOR K%=0% TO KK%-J% 320 NEXT K% 330 GOTO 180 340 ? STRING$(20%,7%) AT(3%,10%) "ВЫ ПРОЕХАЛИ";FIX(J%*200/KK%); "M" 360 GOTO 380 370 ? AT(2%,10%) "******** ФИНИШ ********" 380 POKE &O177660, 0% 390 GOTO 50
Заключение рецензента
Игровая программа, предложенная В.В. Ермаковым, будет интересна многом читателям. К числу её достоинств относятся:
- широкий набор используемых операторов и функций БЕЙСИКа;
- простая логика программы;
- небольшое количество используемых переменных и лаконичность текста;
- достаточно быстрая реакция на действия игрока за счёт использования операций с системными регистрами;
- постепенно изменяемая в процессе одной попытки сложность игры;
- возможность самостоятельного наращивания и совершенствования игры.
Вместе с тем описание алгоритма, которое сделал автор, сверхлаконично. Поэтому поясним работу программы.
В строках 20, 30 формируются переменные, содержащие символы, изображающие "лыжника" (код 182) и препятствие (код №127) соответственно. Подсказка, ввод уровня сложности игры и контроль ошибки ввода обеспечивается в строках 50-120.
Строки 130-160 выполняют очистку экрана и начальную установку координаты "лыжника" I%, пройденного пути J% и длины трассы КК%. Оператор 170 маскирует прерывание от клавиатуры путем установки в единицу шестого бита регистра состояния клавиатуры с адресом 177660.
Строка 180 обеспечивает считывание кода нажатой клавиши из регистра данных клавиатуры (адрес 177662) в переменную А%. Клавиша нажата, если шестой разряд регистра управления системными внешними устройствами (адрес 177716) содержит 0.
Строки 190-210 в зависимости от содержимого переменной А% вычисляют новое значение координаты I% "лыжника", контролируя недопущения выхода за границы экрана. Факт наезда на препятствие устанавливается оператором 220. Пройденный "лыжником" путь увеличивается в строке 230. Строки 250-290 выводят в последнюю строку экрана препятствия, координаты которых определяются с помощью датчика случайных чисел RND. Количество препятствий в строке определяется длиной пройденной дистанции J%. Последний оператор в этой группе строк продвигает содержимое экрана на одну строку вверх.
Оператор 300 проверяет условие окончания трассы. Строки 310-320 создают задержку, зависящую от пройденного "лыжником" пути и длины трассы: чем меньше осталось до финиша - тем меньше задержка и, следовательно, выше скорость спуска.
Строки 340, 350, 370 разделяют два случая завершения спуска: успешного и неуспешного. Разрешение на прерывание от клавиатуры обеспечивается оператором 380.
В программе имеется неточность, внесенная, очевидно, при переписывании текста с экрана. В строке необходима переменная цикла, отличная от I%. Лучше всего I% заменить на К%.
Л.Н. Жариков