Особенности транслятора с языка Бейсик для БК-0010.01
Большинство трансляторов с Бейсика являются интерпретаторами. Но на БК-0010.01 реализован компилятор Бейсика: исходная программа после её запуска командой RUN преобразуется в памяти ЭВМ в прямой шитый код[1]. Он представляет собой последовательность адресов подпрограмм, реализующих соответствующие операторы Бейсика, и исходных данных: констант, адресов переменных и т.д. Подпрограммы «зашиты» в ПЗУ и являются частью компилятора, Каждый оператор программы на Бейсике реализуется с помощью одной или нескольких таких подпрограмм. Зная адреса этих подпрограмм и способ передачи им параметров, можно существенно облегчить написание программ в машинных кодах или в таких системах программирования, как МИКРО-10, МИКРО-11, так как при этом открывается сравнительно простой доступ ко многим средствам языка высокого уровня.
Остановимся на методике нахождения адресов подпрограмм для версии Бейсика «Вильнюс 1986.07.24», поставляемой заводом-изготовителем вместе с БК-0010.01.
В слове по адресу 2002 хранится адрес последнего байта текста введённой программы на Бейсике. Соответствующий ей шитый код транслятор начинает размещать со следующего слова ОЗУ (ближайший больший чётный адрес). В этом легко убедиться, если ввести программу, запустить её командой RUN, а затем, остановив программу, подключить к микро-ЭВМ блок МСТД, войти в тестовый монитор и просмотреть содержимое памяти, руководствуясь числом, хранящимся в ячейке 2002. Но такой способ, конечно, не слишком удобен, если требуется определить шитый код, соответствующий различным командам, операторам и операциям, так как в программе большого объёма разобраться достаточно сложно, а повторять такую процедуру для каждого оператора слишком утомительно. Удобнее для этих целей написать сервисную программу, позволяющую просматривать содержимое памяти как в числовом, так и в символьном виде, а также выполнять ряд вспомогательных функций. Представление операторов и команд языка в шитом коде было определено с помощью следующей программы:
10 REM ПРОГРАММА ЗАПУСКАЕТСЯ 20 REM КОМАНДОЙ "RUN 100" 30 REM ЗДЕСЬ ДОЛЖНЫ НАХОДИТЬСЯ 40 REM ВАШИ ОПЕРАТОРЫ 50 .. . 100 CLS 110 N%=PEEK(&O2002) 120 IF N% MOD 2%=1% THEN N%=N%+1% 130 IF PEEK(N%)=&O156776 THEN STOP
В её первых строках размещаются операторы, шитый код которых требуется определить. После запуска программа выводит на экран в столбик в восьмеричном виде соответствующий им шитый код. Вывод кодов производится до первого встреченного оператора CLS (адрес подпрограммы в ПЗУ равен 156776).
В приводимой ниже таблице каждому оператору языка, записанному в общей форме, сопоставлен соответствующий ему шитый код - содержимое последовательно расположенных в ОЗУ микро-ЭВМ 16-разрядных слов. В шитом коде кроме адресов подпрограмм могут располагаться константы, адреса переменных и другие данные.
Типы параметров, приводимых в таблице, соответствуют стандарту языка Бейсик-MSX и обозначаются: % - целое число; ! - дробное число одинарной точности; # - дробное число двойной точности. Параметру, помещённые в правой части таблицы в скобках, означают, что их значения необходимо переслать в стек в порядке их перечисления. На вершину стека указывает регистр R6 процессора микро-ЭВМ. Переслать параметры в стек можно при помощи вспомогательных подпрограмм Бейсика, также перечисленных в таблице. Восьмеричные числа в правой части таблицы - это адреса подпрограмм, вызов которых приводит к выполнению данного оператора. Некоторые неудобства при таком подходе к составлению программ доставляет способ вызова подпрограммы: для этого требуется записать в регистр R4 адрес слова в ОЗУ, где расположен адрес подпрограммы, а затем выполнить команду JMP @(R4)+. Такова особенность компилятора - передача управления от одной подпрограммы к другой осуществляется через регистр R4 по команде JMP @(R4)+, содержащейся в конце каждой подпрограммы. В связи с этим желательно весь список адресов подпрограмм записывать в ОЗУ подряд, тогда автоматически после выполнения одной подпрограммы будет вызываться на исполнение следующая.
140 ? OCT$(PEEK(N%)) 150 N%=N%+2% 160 GOTO 130 170 END
В качестве параметров, передаваемых подпрограммам, могут использоваться как константы, так и содержимое некоторых переменных, значение которых можно переслать в стек с помощью соответствующих вспомогательных подпрограмм.
В конце таблицы приведены адреса некоторых системных ячеек Бейсика, знание которых может оказаться полезным. Известны также и адреса подпрограмм, реализующих команды Бейсика, однако механизм передачи им параметров авторами пока ещё не установлен.
Если теперь в памяти ЭВМ последовательно разместить адреса необходимых подпрограмм и исходные данные для нескольких операторов Бейсика, то, в принципе, можно получить некую исполняемую программу. По сути дела, этим проделывается работа, аналогичная той, которую выполняет компилятор Бейсика после команды RUN.
Иллюстрацией может служить программа, очищающая экран, рисующая в его левом верхнем углу квадрат и закрашивающая его текущим цветом. Она написана на ассемблере МИКРО-10 и выглядит так:
MOV #PRG,R4 JMP @(R4)+ PRG: .#156776.#156160.#0.#156160.#0 .#156160.#62.#156160.#62 .#125454.#156160.#24.#156160 .#24.#125376.#125404.#125642 .#155560.#PRG END
Запуск этой программы на исполнение производится в режиме 32 символа в строке. Соответствующая ей программа на Бейсике выглядит так:
10 CLS 20 LINE(0%,0%)-(&O62,&O62),В 30 PAINT(&O24,&O24) 40 GOTO 10
Необходимо помнить, что параметр целого типа, передаваемый подпрограммам Бейсика, занимает в ОЗУ 2 байта, дробный параметр одинарной точности - 4 байта, двойной точности - 8 байт. Ввод строки с клавиатуры оператором INPUT производится в буфер длиной 256 байт, расположенный с адреса 2422(8).
|
Операторы |
---|---|
KEY А%,В$ |
(A%, адрес B$, длина B$) 124570 |
PRINT А% |
(A%) 157070, 156770 |
PRINT А! |
(A!) 157014, 156770 |
PRINT A# |
(A#) 157002, 156770 |
PRINT A$ |
(адрес A$, длина A$) 156460, 156770 |
INPUT X1,...,Xn |
160634, 157150, адрес следующей команды, список адресов X1,,,,,Xn |
OPEN A$ FOR OUTPUT |
(адрес A$, длина A$) 162336 |
OPEN A$ FOR INPUT |
(адрес A$, длина A$) 163360 |
CLOSE |
162656 |
POKE A%,B% |
(A%, B%) 160536 |
OUT A%,B%,C% |
(A%, B%, C%) 160542 |
CLS |
156776 |
COLOR A%,B% |
(A%, B%) 125172, 125242 |
LOCATE A%,B%,C% |
(A%, B%) 124512, (C%), 124324 |
PSET(A%,B%),C% |
(A%, B%, C%) 125242, 125410, 125220 |
LINE(A%,B%)-(C%,D%),E% |
(A%, B%, C%, D%, E%) 125242, 125426, 125220 |
LINE(A%,B%)-(C%,D%),E%,B |
(A%, B%, C%, D%, E%) 125242, 125454, 125220 |
CIRCLE(A%,B%),C%,D% |
(A%, B%, C%, D%) 125334, 126724, 126742, 127122, 127324 |
PAINT(A%,B%),C%,D% |
(A%, B%, C%) 125334, (D%), 125334, 125642 |
DRAW A$ |
(адрес A$, длина A$) 131412 |
BEEP |
156762 |
TRON |
154626 |
TROFF |
154674 |
ON A% GOTO m1,...,mn |
(A%) 155564, n, адреса m1,...,mn |
STOP |
154754 |
END |
154722 |
GOTO N |
155560, адрес N |
GOSUB N |
155526, адрес N |
RETURN |
155722 |
FOR I%=A% TO B% STEP C% |
(A%) 156350, адрес I%, (B%, C%), 156426, адрес следующего оператора, адрес I% |
FOR I#=A# TO B# STEP C# |
(А#) 156334, адрес I#, (В#, С#), 156404, адрес следующего оператора, адрес I# |
NEXT I% |
156466, адрес I%, 156572 |
NEXT I# |
156466, адрес I#, 156636, 155176, 155466 |
IF...THEN...ELSE |
сравнение, 155404, адрес ветви ELSE, ветвь THEN, 155560, адрес оператора, следующего за ELSE, ветвь ELSE |
Отношения и логические операции
А%<В% |
(А%, |
В%) |
155172, |
155452 |
А#<B# |
(А#, |
В#) |
177034, |
155452 |
А%>В% |
(А%, |
В%) |
155172, |
155456 |
A#>B# |
(А#, |
В#) |
177034, |
155456 |
А%>=В% |
(А%, |
В%) |
155172, |
155446 |
А#>=В# |
(А#, |
В#) |
177034, |
155446 |
А%<=В% |
(А%, |
В%) |
155172, |
155442 |
А#<=В# |
(А#, |
В#) |
177034, |
155442 |
А%<>В% |
(А%, |
В%) |
155172, |
155436 |
А#<>В# |
(А#, |
В#) |
177034, |
155436 |
NOT А% |
(А%) |
|
162306 |
|
А% AND В% |
(А%, |
В%) |
162316 |
|
А% OR В% |
(А%, |
В%) |
162312 |
|
А% XOR В% |
(А%, |
В%) |
162324 |
|
А% EQV В% |
(А%, |
В%) |
162340 |
|
А% IMP В% |
(А%, |
В%) |
162344 |
|
А%=В% |
(А%, |
В%) |
155172, |
155430 |
А#=В# |
(А#, |
В#) |
177034, |
155430 |
Функции
SQR(X#) |
(X#) 171350 |
CDBL(A%) |
(A%) 166646 |
SIN(X#) |
(X#) 173614 |
PEEK(A%) |
(A%) 160556 |
COS(X#) |
(X#) 173566 |
INF(X %,Y%) |
(X%, Y%) 160564 |
TAN(X#) |
(X#) 174306 |
ASC(A$) |
(адрес A$, длина A$) 161534 |
ATN(X#) |
(X#) 174434 |
CHR$(A%) |
(A%) 160510 |
EXP(X#) |
(X#) 171762 |
LEN(A$) |
(адрес A$, длина A$) 161530 |
PI |
167102 |
MID$(A$,B%,C%) |
(адрес A$, длина A$, B%, C%) 161340 |
LOG(X#) |
(X#) 173052 |
STRING(X%,A$) |
(X%) 160634, (адрес A$, длина A$),161404 |
ABS(X#) |
(X#) 166566 |
VAL(A$) |
(адрес A$, длина A$) 161550 |
ABS(X%) |
(X%) 166556 |
INKEY$ |
124132 |
FIX(X#) |
(X#) 176212 |
STR$(A%) |
(A%) 161610 |
INT(X#) |
(X#) 176340 |
OCT$(A%) |
(A%) 161624 |
SGN(X%) |
(X%) 166572 |
HEX$(A%) |
(A%) 161734 |
SGN(X!) |
(X!) 166624 |
BIN$(A%) |
(A%) 161672 |
SGN(X#) |
(X#) 166614 |
CSRLIN(A%) |
(A%) 124560 |
RND(X#) |
(X#) 175176 |
POS(A%) |
(A%) 124544 |
FRE(X%) |
(X%) 160600 |
LPOS(A%) |
(A%) 157054 |
FRE(A$) |
(адрес A$, длина A$) 160620 |
EOF |
163040 |
CINT(A#) |
(A#) 160760 |
AT(A%,B%) |
(A%, B%) 124512 |
CSNG(A#) |
(A#) 162020 |
TAB(A%) |
(A%) 124420 |
CDBL(A!) |
(A!) 166742 |
POINT(A%,B%) |
(A%, B%) 125602 |
Адреса вспомогательных подпрограмм
156160,А% |
- запись слова А% в стек |
156156,А! |
- запись двух слов в стек |
156152,A# |
- запись четырёх слов в стек |
156232,А% |
- пересылка слова no адресу А% в стек |
156214,А% |
- пересылка двух слов по адресу А% в стек |
156170,А% |
- пересылка четырёх слов по адресу А% в стек |
156350,А% |
- запись слова из стека по адресу А% |
156330,А% |
- запись двух слов из стека по адресу А% |
156334,А% |
- запись четырёх слов из стека по адресу А% |
124164 |
- вызов вспомогательного ПЗУ (CALL) |
156770 |
- вывод мода БК |
124324 |
- включение курсора |
166562 |
- изменение знака целого числа в стеке |
156750 |
- изменение знака вещественного числа в стеке |
125242 |
- установка цвета (значение цвета находится в стеке) |
Некоторые системные адреса Бейсика
2006 |
- количество зарезервированных Байтов (CLEAR) |
2002 |
- содержит адрес конца программы |
2050 |
- флаг трассировки (1 - включена, 0 - выключена) |
37400 |
- буфер ввода-вывода с магнитофона |
120300 |
- адрес аварийного входа в Бейсик |
121052 |
- вызов строкового редактора текстов Бейсика |
146404 |
- адрес подпрограммы обработки прерывания по прерыванию TRAP |
120234 |
- адрес подпрограммы обработки прерывания по клавише СТОП |
120540 |
- адрес подпрограммы обработки прерывания по вектору 10 |
Команды Бейсика
136650 |
CLOAD |
136104 |
LOAD |
124302 |
MONIT |
136420 |
BLOAD |
137256 |
SAVE |
134626 |
LLIST |
136306 |
FIND |
134600 |
NEW |
141034 |
RENUM |
137124 |
CSAVE |
140376 |
AUTO |
140514 |
DELETE |
136562 |
BSAVE |
134634 |
LIST |
136066 |
CONT |
|
|
|
|
135242 |
RUN |
Арифметические операции
А%+В% |
(A%, |
B%) |
162102 |
A#/B# |
(A#, |
B#) |
170776 |
A#+B# |
(A#, |
B#) |
167144 |
A%\B% |
(A%, |
B%) |
162052 |
А%-В% |
(A%, |
B%) |
162112 |
A%*B% |
(A%, |
B%) |
162116 |
A#-B# |
(A#, |
B#) |
167124 |
A#*B# |
(A#, |
B#) |
170210 |
В. АВСЕЕВ, А. АВСЕЕВ
[1] Детальное определение шитого кода см., например: Баранов С.Н., Ноздрунов Н.Р. Язык Форт и его реализации. М.: Машиностроение, 1988. С. 43.
Увеличение тактовой частоты
Худшим из языков программирования для БК считается Фокал (в силу своей уникальной медлительности). Тем не менее, существует возможность повысить быстродействие компьютера при работе с этим и некоторыми другими языками. Для этого необходимо увеличить тактовую частоту микропроцессора. Как показал опыт, удвоение тактовой частоты совершенно не сказывается на надёжности работы БК, а программы выполняются почти в два раза быстрее.
Фрагмент схемы доработанного компьютера приведён на рисунке. Соединения, которые необходимо разорвать, перечёркнуты, а новые элементы изображены жирной линией. Микросхемы показаны в упрощённом виде. Переключатели SA1 и SA2 - микротумблеры; их можно установить на заднюю панель БК. Переключателем SA1 изменяют частоту, a SA2 переводит процессор в состояние прямого доступа к памяти (ПДП).
Последовательность действий при переходе с одной скорости на другую такова: сначала приостанавливаем выполнение текущей программы, переведя переключатель SA2 в нижнее по схеме положение, затем с помощью тумблера SA1 изменяем тактовую частоту и возвращаем переключатель SA2 в исходное положение. Переключать скорости при активно работающем микропроцессоре нельзя - произойдёт сбой. Кстати, переключатель SA2 реализует дополнительное удобство: возможность временного приостанова выполнения любой программы (аналогично действию клавиши ШАГ в Фокале).
Следует учесть, что успешно работать с магнитофоном БК может только при нормальной тактовой частоте (3 МГц).
Д. АНТОНОВ
Спрайты для БК
Для программного вывода на экран небольших изображений в стандарте Бейсика MSX имеется оператор SPRITE$, который позволяет задать до 256 картинок (спрайтов) размером 8x8 или 64 спрайта 16x16. С его помощью можно создавать и плавно перемещать спрайты по экрану. Безусловно, такой оператор очень полезен, но в БК его нет. Что делать?
Ниже приведено несколько программ для БК, позволяющих получить на экране спрайты размером 8x10 или 16x20 точек. Координаты вывода и размеры спрайтов не меняются при переключении размеров курсора (32/64).
При использовании программ надо будет в начале основной программы поместить оператор PRINT CHR$(140); CHR$(140), да и в самой программе нельзя «сдвигать» экран вверх; самой верхней точке экрана с координатами (0, 0) должен постоянно соответствовать адрес &O42000.
Можно использовать программы, написанные на Бейсике, - их легче ввести в основную программу. Можно использовать программы в кодах - они значительно быстрее выводят спрайт на экран.
Недостаток моих программ - крупный шаг координат спрайтов по горизонтали (он равен широкому курсору). Желающие могут доработать их - сделать шаг координат равным одной точке.
Вариант на Бейсике. Приведённые ниже программы выводят на экран спрайт в соответствии со следующими параметрами: N - номер спрайта (0-...);
X - координата Х (0-31);
Y - координата Y (0-229).
Программы используют файл, содержащий коды изображений спрайтов. Он записывается в память с адреса:
&O27774 - если перед кодами изображений спрайтов идут 4 байта, содержащие данные о ширине и высоте столбца спрайтов (результат работы редактора GRED0);
&O30000 - если коды начинаются сразу.
10 'Программа SPRITE 8x10 20 NN=N*&O24 30 ХХ=Х+Х 40 YY=Y*&O100 50 XY=XX+YY+&O42000 60 FOR I=&O30000+NN TO &O30023+NN STEP &O2 70 POKE XY,PEEK(I) 80 XY=XY+&O100 90 NEXT I
10 'Программа SPRITE 16x20 20 NN=N*&O120 30 ХХ=Х+Х 40 YY=Y*&O100 50 XY=XX+YY+&O42000 60 FOR I=&O30000+NN TO &O30116+NN STEP &O4 70 POKE XY,PEEK(I) 80 POKE XY+2,PEEK(I+2) 90 XY=XY+&O100 100 NEXT I
Вариант в кодах. Следующие программы выводят на экран спрайт, параметры которого заданы в следующих ячейках:
&О37770 содержит в себе номер спрайта (0-...);
&O37772 содержит в себе координату X (0-31);
&O37774 содержит в себе координату Y (0-229).
Файл спрайтов записывается с адреса:
&О30174 - если коды изображения идут не сразу;
&O30200 - если коды изображения идут сразу.
10 'Программа SPRITE 8x10 20 DATA 5572,16376,4865,5568,20,2563,2562,3009 30 DATA 777,1539,2817,26050,1,24579,32322,3010 40 DATA 769,2819,4293,5572,16380,4865,5568,64 50 DATA 2563,2562,3009,777,1539,2817,26050,1 60 DATA 24579,32322,3010,769,2819,5568,16378,4612 70 DATA 3268,24772,26052,17408,5571,12416,24899,4290 80 DATA 26050,19,5324,26052,64,8386,2043,2560 90 DATA 2561,2562,2563,2564,2565,135,0,0 100 DATA -1,0,-1,0 120 FOR A=&O30000 TO &O30200 STEP &O2 130 READ К 140 POKE A,К 150 NEXT A 160 END
10 'программа SPRITE 16x20 20 DATA 5572,16376,4865,5568,80,2563,2562,3009 30 DATA 777,1539,2817,26050,1,24579,32322,3010 40 DATA 769,2819,4293,5572,16380,4865,5568,64 50 DATA 2563,2562,3009,777,1539,2817,26050,1 60 DATA 24579,32322,3010,769,2819,5568,16378,4612 70 DATA 3268,24772,26052,17400,5571,12416,24899,4290 80 DATA 26050,78,5332,5324,26052,62,8386,2042 90 DATA 2561,2562,2563,2564,2565,135,0,0 100 DATA -1,0,-1,0 120 FOR A=&O30000 TO &O30200 STEP &O2 130 READ К 140 POKE A,К 150 NEXT A 160 END
Будьте осторожны - использовать сразу оба типа программ нельзя, так как возникнет путаница из-за использования одинаковых адресов для записи файла спрайтов.
10 * Пример использования 20 ' подпрограммы 25 ' SPRITE 16x20 30 ' 40 DEF USR=&O30000 50 INPUT "Номер";N 60 PRINT CHR$(140);CHR$(140) 70 POKE &O37770,N 80 FOR Y=0 TO 200 STEP 20 90 FOR X=0 TO 30 STEP 2 100 POKE &O37772,X 110 POKE &O37774,Y 120 A=USR(A) 130 NEXT X 140 NEXT Y 150 GOTO 50
Подготовка спрайтов. Изображение спрайтов подготавливается с помощью графического редактора типа GRED0. Рисовать спрайты надо один под другим - столбиком, тогда при записи на магнитофон будут идти последовательно сперва 4 байта, несущие информацию о высоте и ширине изображения (столбца), потом байты первого спрайта, затем байты второго спрайта и т.д.
При записи имени файла спрайтов можно использовать только 6 символов, после чего надо добавить расширение «.BIN». Это даёт возможность считать файл через команду BLOAD.
Пример:
- запись: ИМЯ.BIN
- чтение: BLOAD «ИМЯ», &О30174
Общий вид загрузки готовой программы:
- загрузка основной программы: CLOAD «ИМЯ 1»
- загрузка подпрограммы вывода: BLOAD «ИМЯ 2», &O30000
- загрузка файла спрайтов: BLOAD «ИМЯЗ», &О30174
- запуск: RUN
Е. УМНИКОВ
Строки вместо матриц
Про цвет на экране БК подробно написано в «ИНФО» № 2 за 1989 г. Задавать матрицы псевдоцветов, как предлагает С. Зильберштейн, заманчиво и просто; сколько цветов - столько и матриц... Но такой способ не очень экономичен, особенно если вспомнить про куцее ОЗУ. Лучше задавать не всю матрицу, а одну строку. Чередуя строки, получим различные комбинации. Для иллюстрации этой идеи привожу программу КАЛЕЙДОСКОП. Она рисует многоцветные калейдоскопические узоры, но, в отличие от известной программы В. Кумандина ЦМУ, цветов на экране в несколько раз больше.
10 'Программа КАЛЕЙДОСКОП 20 DEF FNA(А,В)=INT(RND(1)*А)+В 30 ? CHR$(148)CHR$(158)CHR$(140)CHR$(140)CHR$(146) 40 DATA &H1111,&H2222,&H3333,&H5555,&H6666,&H7777,&HAAAA,&HBBBB,&HFFFF,0 'маски псевдоцветов 50 CR=FNA(10,1) 60 RESTORE 70 FOR I=1 TO CR 'выбор цвета 80 READ C% 90 NEXT 100 NY=64*FNA(200,1) 'выбор 110 NX=2*FNA(28,1) 'размера 120 LY=64*FNA(53.2) 'прямоугольника 130 LX=2*FNA(5,1) 140 AN=16384+NY+NX 'вычисление 150 AK=32704-NY+NX 'адресов 160 AM=AN-2*NX+62 'ОЗУ 170 AL=AK-2*NX+62 'экрана 180 FOR I=0 TO LY STEP 128 190 FOR J=0 TO LX STEP 2 200 POKE AN+I+J,C% 'запись 210 POKE AM+I-J,C% 'маски 220 POKE AK-I+J,C% 'в экранную 230 POKE AL-I-J,C% 'область 240 NEXT J,I 'через строку 250 GOTO 50
Как видно из текста программы, десять масок в случайной последовательности записываются в ОЗУ экрана (адреса от &O40000 до &O77776). Каждая маска, заданная в виде шестнадцатеричной константы, заполняет прямоугольную область со случайно выбранными размерами. Запись делается через строку. Кроме того, строится ещё три одинаковых прямоугольника, чтобы получить центральную симметрию, т.е. калейдоскопический узор. Постепенное наложение прямоугольников даст различные сочетания масок (вспомните пропущенные строки и посчитайте, сколько разных комбинаций возможно при десяти масках). Визуально многие псевдоцвета почти не отличаются, поэтому реально на экране отобразится «всего лишь» несколько десятков оттенков. Небольшая модификация программы позволит рисовать не только прямоугольники, но и треугольники, диагональные полосы и другие фигуры.
Что же такое маска? И почему масок только десять? Как они выбраны?
Действительно, масок может быть намного больше, и задавать их можно по-разному. Давайте вспомним, как в БК организовано видео-ОЗУ. В цветном режиме экран представляет собой матрицу 256X256 точек. Каждой точке соответствуют два бита ОЗУ. Байт «отвечает» за четыре точки, слово - за восемь. В два бита можно поместить только четыре двоичных числа. Отсюда и четыре цвета. Их маски: 00 - чёрный, 01 - синий, 10 - зелёный, 11 - красный. Маской, точнее - трафаретом, называется образец, по которому заполняются биты ОЗУ. Маски задают для битов, байтов или слов. Процессор БК имеет 16-разрядную шину, и оператор РОКЕ обращается к машинному слову, т.е. только к чётным адресам. Теперь ясно, что запись двоичного числа (маски) &В1111111111111111 по адресу &O40000 отобразится в виде красной горизонтальной полоски длиной в восемь точек в левом верхнем углу экрана. В строке размещается 32 слова или 64 байта, поэтому адреса позиций в соседних по вертикали телевизионных строках отличаются на 64 (в операторах программы 100, 120 использован именно этот множитель). Двоичные маски для слова наглядны, но слишком длинны. Можно перевести их в десятичные числа; станет коротко, но непонятно. Удобнее всего использовать шестнадцатеричные константы. Что означает, к примеру, маска &Н7777? Каждая цифра соответствует четырём битам, или двум точкам. Вспомнив двоичное представление семёрки (7=0111), легко видеть, что эта маска означает чередование синих и красных точек. Все остальные маски выбраны либо как одинаковые точки (4 цвета), либо как последовательное чередование двух цветов (например, синий - зелёный, чёрный - красный и т.д.). Всего сочетаний по два из четырёх возможно шесть, отсюда - десять масок. Остаётся объяснить, что делает оператор в строке 30. Он очищает служебную строку и экран, а также устанавливает видео-ОЗУ в начальное положение (т.е. сбрасывает рулонный сдвиг). Как получается симметричный узор, вы, очевидно, уже поняли (см. операторы 140-170).
С. КОМАРОВ
Хитрости Бейсика БК
Недостатки компилятора Бейсика для БК-0010.01 хорошо известны. Наибольшее их число связано с операциями с символьными переменными и символьными функциями. Вот характерный пример.
10 А$="ДА" 20 В$="МА" 30 С$=А$+В$ 40 PRINT С$ 50 GOTO 10
Компьютер при такой программе должен выдавать слово ДАМА, пока не нажата клавиша STOP, но, выведя в первый раз слово ДАМА, БК будет затем упорно выдавать слово МАМА.
Чтобы «обойти» эту особенность интерпретатора, можно определить операцию сложения символьных переменных через функцию пользователя:
8 DEF FNA$(A$,B$)=A$+B$ 40 C$=FNA$(A$,B$)
Теперь программа будет работать нормально. Подобный приём помогает почти всегда, хотя в практике автора был случай, когда пришлось определить через функцию пользователя даже операцию присвоения значения символьной переменной:
10 DEF FNE$(A$)=A$ 100 C$=FNE$(B$)
Обратимся теперь к функции STR$, которая ошибочно добавляет к соответствующему символьному представлению числа по пробелу слева и справа, вследствие чего перестают верно работать функции LEN, VAL. Рецепт здесь такой же - определить функцию пользователя
10 DEF FNSR$(N)=MID$(STR$(N),2,LEN(STR$(N))-2)
которую и использовать в дальнейшем вместо функции STR$. Возможно, это не лучший выход, но работать можно.
Несколько слов о процедуре ввода-вывода числовых данных по каналу работы с магнитофоном. Если магнитофон не снабжён дистанционным управлением или же этот канал используется для обмена данными между двумя БК, то возникает неприятная проблема: если файл, открываемый оператором OPEN «имя» FOR INPUT/OUTPUT, состоит из нескольких блоков (один блок равен 256 байтам), то типичной является ситуация, когда временной интервал между блоками при выводе оказывается меньше, чем это необходимо при вводе. В результате при попытке считать файл данных с магнитофона компьютер, приняв первый блок, «не успевает» к началу второго и зависает. В подобной ситуации для повторного запуска после команды STOP обязательно надо выполнить команду SAVE и только потом RUN. Для предотвращения же такой ситуации следует увеличить интервал между блоками при выводе. Сделать это можно следующим образом (выводится массив X(N)):
100 OPEN "DATA" FOR OUTPUT 110 FOR I=0 TO N 120 A=1.1^10% 'ЗАДЕРЖКА 130 PRINT# X(I) 140 NEXT 150 CLOSE
Оператор в строке 120 обеспечивает необходимое увеличение интервала между блоками, конкретное же значение показателя степени в нем зависит от ряда факторов, в частности при выводе целых чисел оно обычно может быть уменьшено до 2-4. При отладке программы всегда можно подобрать значение показателя степени таким, чтобы интервалы между блоками при выводе и вводе были примерно равны. Однако если последний блок файла при выводе оказывается существенно меньше 256 байтов, то временной интервал между предпоследним и последним блоками заметно сокращается, и при вводе компьютер опять может зависнуть. Выход - добавить при выводе ещё один, лишний блок. Для этого в приведённый выше фрагмент программы между строками 140 и 150 следует включить, например, следующие строки:
142 А%=1% 144 FOR I=0 ТО 62 146 PRINT# А% 148 NEXT
Разумеется, при вводе все дополнительно введённые операторы не нужны, поэтому соответствующий фрагмент программы должен выглядеть так:
100 OPEN "DATA" FOR INPUT 110 FOR I=0 TO N 120 INPUT# X(I) 130 NEXT 140 CLOSE
Использование этого приёма позволяет обеспечить надёжный обмен данными с магнитофоном или с другим компьютером БК.
А. БОЧАРОВ
Вмешательство в работу программы
Часто, особенно в играх, нужно, чтобы программа получала информацию с клавиатуры, не останавливаясь. Например, в игре типа «Клад» необходимо управлять действиями человека, в то же время должны происходить полёт пули, зарастание стенки или бег хранителей кладов.
Мне известно три способа реализации этого на БК-0010.
Первый способ. Позволяет после нажатия клавиши выполнять кодируемое ею действие, пока не будет нажата другая клавиша (рис. 1).
Если этот способ используется в игре типа «Клад», то «выполнить действие, кодируемое нажатой клавишей», - передвинуть, например, человечка вверх по лестнице, если была нажата клавиша «1»; «выполнить прочие действия» - нарисовать следующую стадию полёта пути, передвинуть хранителей кладов, зарастить стену и т.п. Пример программы, использующей такой способ, - «LODE RUNNER» Маркова.
Второй способ. Позволяет делать действие, кодируемое клавишей, только один раз после её нажатия. Для повторного исполнения этого действия необходимо снова нажать эту клавишу (рис. 2).
Если этот способ используется в игре типа «Клад», то, в отличие от первого способа, при нажатии, например, клавиши «<-» человек не будет бежать влево, пока не будет нажата другая клавиша, а сдвинется влево лишь на одну позицию. Чтобы заставить его двигаться влево непрерывно, надо всё время нажимать-отпускать данную клавишу. В играх типа «Клад» - это, конечно, не лучший вариант, но в других случаях может оказаться удобным.
Такой способ используется в программе «Диверсант» Кумандина.
Третий способ. Позволяет выполнять действие, пока нажата кодирующая его клавиша (рис. 3).
Он используется во многих динамических играх: «Клад», «LAND» и др. По такому же принципу построен механизм действия клавиши «повтор».
В. БУЛИТКО VIII класс г. Одесса
Советы и наблюдения
Запятая либо точка с запятой необходимы при выводе вещественных чисел, не содержащих признака #. Не обойтись без точки с запятой и в самом конце списка выводимых на печать выражений, когда нужно заблокировать перевод строки, а без запятой - когда выдача должна происходить зонами. В других же случаях разделителями могут служить признаки вида чисел #, !, %, $, префиксы-признаки системы счисления &В, &О, &Н, пары скобок в функциях печати АТ(...), ТАБ(...) и символьных функций CHR$(...), MID$(...), пары кавычек с пробелами или без между ними.
Всё это позволяет в соответствующих ситуациях опускать точку с запятой в качестве разделителя и так экономить место в ОЗУ.
Пример.
10 D=111 20 E!=222! 30 C%=333% 40 T$="444" 50 ? D:E!C%T$CHR$(76%)&B101 60 ? D#TAB(3%)MID$(T$,2%)" "HEX$(255%)
На монитор будет выдано:
111 222 333 444L 5 111 44 FF
В. ЯКОВЛЕВ
Хочу предложить исправления к программе А. Чистякова «Голос-2» (ИНФО. 1988. № 4).
- По адресу 1124 необходима не команда ROL R4 (код 6104), а команда ROR R4 (код 6004).
- Дополнительно вместо 40 по адресу 1134 предлагаю поставить 140, чтобы при воспроизведении работал динамик компьютера.
После этого голос БК неузнаваемо изменился: серьёзные искажения исчезли, стало просто различать все слова не только при скоростях 1 - 6. В качестве указателя скорости можно нажимать даже буквы и специальные знаки. В этом случае длительность записываемой речи может достигать минуты и более.
Для устранения шума между словами можно обнулить эти участки памяти хотя бы с помощью директив отладки.
А. ЖАРИКОВ
При перенумерации программ в тексте могут оставаться пробелы; например, GOTO 1000, если 1000 превратится в 1, будет выглядеть так: GOTO 1. Если памяти «в обрез», пробелы нужно убрать.
Я заметил в интерпретаторе ошибку. Проиллюстрировать её можно следующим примером (далее ответы интерпретатора выделены крупным шрифтом).
А$="1" Ok А$=А$+"2" Ok ? А$ 12 Ok A$=A$+"3" Ok ? A$ 223 Ok
А правильный ответ - 123.
Консультант редакции И.Г. Панченков советует: чтобы избавиться от этого эффекта, следует каждое присваивание начинать с пустой строки: A$=""+A$+"2"
Если в ключ записать слово, начинающееся с русских букв, то в верхней строке экрана (в подсказке) отобразится первая латинская буква (или цифра), встретившаяся в тексте ключа.
Кстати, чтобы обновить строку с первыми буквами ключей, надо одновременно нажать АР2+СУ+Ч, затем клавишу ввода. Если эта клавиша не будет нажата, то, когда вы дойдёте до середины строки, набирая текст, БК зависнет.
И. КОНИВЕЦ
Длительное прижатие магнитной ленты к тонвалу при выключенном двигателе магнитофона может вызвать её порчу в месте прижатия и даже отслоение магнитного слоя от основы (особенно склонны к этому импортные МЛ).
Ю. А. ЗАЛЬЦМАН
Часть владельцев БК испытывают затруднения при вводе-выводе на МЛ программ, написанных в кодах БК. Приводим подробные описания этих операций. Обычным шрифтом набраны команды пользователя, жирным - ответы БК, мелким - комментарии для читателей.
Считывание, первый способ.
<ЛАТ>Р М<ВВОД> ? М<ВВОД> ИМЯ? <название программы><ВВОД> /названная программа загружается/ ? /загрузка уже произошла/ S<ВВОД> /или S1000<ВВОД>/
Считывание, второй способ.
<ЛАТ>P Т<ВВОД> + <РУС><TC><ВВОД> $ МЧ<ВВОД> Нажмите клавишу магнитофона "ПУСК". Адрес= 0<ВВОД> Имя= <название программы><ВВОД> /названная программа загружается/ $ /загрузка уже произошла/ 1000<ЛАТ>G<ВВОД> /БК выполняет заданную программу/
Для правильного проведения записи нужно знать начальный адрес загрузки программы и её длину. Эти параметры записаны в ячейках 346 и 350, узнать их можно командой
$<РУС>346А4Л<ВВОД>
Процедура записи.
<ЛАТ>Р Т<ВВОД> + <РУС><TC><ВВОД> $ МЗ<ВВОД> Нажмите клавиши магнитофона "ПУСК" и "ЗАПИСЬ" Адрес= <адрес><ВВОД> Длина= <длина><ВВОД> Имя= <название программы><ВВОД>
Хотел бы переписываться с поклонниками Бейсика-MSX, XFOCALa и Мини-Фонда. Имею небольшую библиотечку системных и игровых программ.
А. МУМИНОВ.
703007, Самарканд, ул. Октябрьская, 53, кв. 27.
Л. БЕЛОВА, Ю. БЕЛОВ
Ярославский государственный университет
Изображение поверхностей
С необходимостью изображения поверхностей приходится сталкиваться в аналитической геометрии при изучении поверхностей второго порядка и в анализе при изучении функций двух переменных. В школьных факультативах по информатике тоже не раз приходится встречаться с задачей построения на экране или бумаге красивого купола, или гамака, или волн моря. Мы обратились к литературе по машинной графике [1, 2] и на основе имеющихся алгоритмов разработали, используя Бейсик, простые программы построения поверхностей, точнее - графиков функций двух переменных. При этом удалось даже улучшить контроль видимости путём простейшей модификации алгоритма, описанного в [1] и [2]. Мы предлагаем читателям краткое описание основных процедур программы построения графика функции двух переменных. Программа реализована на БК-0010.01 и ДВК-3; графические возможности этих машин не позволяют создавать полутоновые изображения, поэтому поверхность приходится изображать каркасным (сеточным) методом с помощью семейств кривых, являющихся сечениями поверхности графика функции плоскостями х=const или y=const. На рис. 1 - 4 приведены результаты работы нашей программы для различных семейств сечений и функции FNS(X, Y)=A*X*X+B*Y*Y, где А, В - запрашиваемые параметры.
Рис. 1
Рис. 2
Рис. 3
Рис. 4
Итак, нам надо как-то спроектировать на экран график функции Z=f(X, Y), т.е. множество точек трёхмерного пространства, координаты которых имеют вид (X, Y, f(X, Y)). Считаем при этом, что |X|≤K и |Y|≤K, т.е. точка (X, Y) пробегает на плоскости X0Y квадрат размером 2*К × 2*К. Как отмечалось выше, при сеточном методе на экран отображаются не все точки поверхности, а лишь те, которые образуют семейство сечений с некоторым равномерным шагом. При этом возникает два основных вопроса: как спроектировать на экран точку трёхмерного пространства, имеющую известные мировые координаты (X, Y, Z), и как обеспечить контроль видимости, т.е. уничтожение тех частей линий, которые закрыты другими фрагментами поверхности? Для решения первого вопроса надо прежде всего условиться, как располагать изображения трёх пространственных координатных осей на экране. На уроках черчения в школе употребляется изометрическая проекция (рис. 5), а при изучении стереометрии оси располагаются чаще так, как на рис. 6 (диаметрическая проекция или близкая к ней).
В программе реализован второй вариант; при этом начало координат располагается в центре экрана, ось 0Y идёт вправо, 0Z - вверх, 0X - к наблюдателю, а на экране изображается идущей влево и вниз. Предполагается, что наблюдатель расположен выше плоскости X0Y. Напомним, что начало двухмерных дисплейных координат в БК-0010 находится в левом верхнем углу экрана, ось XD горизонтальна, YD направлена сверху вниз. Экранные единицы длины очень малы, поэтому в программе используется масштаб М, определяющий, сколько экранных единиц содержится в одном шаге по оси 0Y или 0Z. Так как XD изменяется от 0 до 255, a YD - от 0 до 240, то точка (0, 0, 0) будет иметь экранные координаты (128, 120). Изображение точки (0, 1, 0) с учётом масштаба будет иметь экранные координаты XD=128+М*1, YD=120. Изображение точки (0, 0, Z) будет иметь координаты XD=128, YD=120-M*Z, так как оси 0Z и YD направлены противоположно. Экранные координаты изображения точки (X, 0, 0) будут, очевидно, таковы: XD=128-M*k*X, YD=120+M*k*X, где 0<k<1 можно подбирать, добиваясь лучшей реалистичности изображений. Можно, например, положить k=0.5. В общем случае получим, что образ точки (X, Y, Z) на экране будет иметь координаты
XD=128+M*Y-0.5*M*X (1) YD=120-M*Z+0.5*M*Х
Рис. 5
Конечно, имеется общая математическая теория проектирования пространства на плоскость, но наша задача - разобраться только в одном конкретном виде проектирования. Отметим, что для ДВК-3 формулы (1) немного изменятся в связи с тем, что там пределы XD и YD - 400×280 и начало экранных координат в левом нижнем углу. Для этой ПЭВМ верны следующие формулы:
XD=200+M*Y-0.5*M*X (2) YD=140+M*Z-0.5*M*X
Теперь, считая, что f(X, Y) определена на квадрате от -K до +K по X и от -K до +K по Y, можно организовать построение семейств сечений. Сечение строится поточечно, каждая точка проектируется на экран с помощью формул (1) (или (2)). Вот соответствующий фрагмент программы.
Рис. 6
280 REM ПОСТРОЕНИЕ СЕЧЕНИЙ, ПАРАЛЛЕЛЬНЫХ 0Y С.280-360 290 FOR Х=K ТО -K STEP -2*K/LX 300 FOR Y=-К ТО К STEP 1/М 310 Z=FNS(X,Y) ’ВЫЧИСЛЕНИЕ ЗНАЧЕНИЯ F(X,Y) 320 GOSUB 510 ’ПОЛУЧЕНИЕ ЭКРАННЫХ КООРДИНАТ (XD,YD) 330 GOSUB 610 'ВЫЧИСЛЕНИЕ ПРИЗНАКА ВИДИМОСТИ V 340 IF V=1 THEN PSET(XD,YD) ’ПОСТРОЕНИЕ ТЕКУЩЕЙ ТОЧКИ, ЕСЛИ ОНА ВИДИМА 350 NEXT Y 360 NEXT X
Здесь LX - количество сечений, параллельных оси 0Y; во внутреннем цикле шаг 1/М соответствует единичному шагу по экрану. Переход на 510 соответствует вычислениям по формулам (1) для текущих значений (X, Y, Z), вычисление признака видимости в точке 610 будет разобрано ниже. Отметим, что совершенно аналогично проводится построение семейства сечений, параллельных 0X, только снаружи будет цикл по Y, а внутри - по X.
Попробуем теперь разобрать вопрос о контроле видимости, который решается независимо для семейств сечений, параллельных 0X и параллельных 0Y. Отметим, что сечения, параллельные 0Y, строятся по убыванию X, т.е. по удалению от наблюдателя («вглубь» экрана). Поэтому очередное строящееся сечение может быть заслонено (экранировано) только той частью поверхности, которая расположена ближе к наблюдателю и уже простроена. Аналогичное соображение имеется и для сечений, параллельных 0X. Это позволяет вести контроль видимости, пользуясь принципом «текущего экранирования» («текущего экрана»), т.е. очередную точку (XD, YD) текущего сечения отображать на экран только тогда, когда она не попадает в промежуток между уже построенными сечениями. Для отслеживания этого факта заводятся два массива МАХ(255) и MIN(255), в которых хранятся верхняя и нижняя границы «текущего экрана». Перед рисованием сечений в массив МАХ заносятся нули, а в массив MIN - единицы, что означает пустоту «текущего экрана». В тех точках XD, где рисунок уже появился, имеем, конечно, неравенство MAX(XD)≥MIN(XD), а разность MAX(XD)-MIN(XD) равна толщине текущего рисунка вдоль вертикали XD. После проверки очередной точки «текущий экран» может, если надо, расшириться, включая её в себя (рис. 7). Вот соответствующий фрагмент программы.
610 REM ВЫЧИСЛЕНИЕ ПРИЗНАКА ВИДИМОСТИ ТЕКУЩЕЙ ТОЧКИ (XD,YD) 620 V=0 630 IF MIN(XD)<=YD AND YD<=MAX(XD) THEN 670 640 V=1 650 IF YD>MAX(XD) THEN MAX(XD)=YD 660 IF YD<MIN(XD) THEN MIN(XD)=YD 670 RETURN
Рис. 7
Это основные моменты программы. Перед началом построения графика программа запрашивает К, М, параметры А, В и требуемое количество сечений LX и LY, параллельных 0Y и 0X. Затем экран дисплея очищается, инициируется массивы МАХ и MIN и запускается цикл построения семейства сечений, параллельных 0Y, если LX>0. Перед построением второго семейства снова инициируются МАХ и MIN, если LY>0, строится второе семейство.
Теперь об упомянутом ранее замечании по улучшению алгоритма контроля видимости. Отметим, что описанный выше алгоритм контроля «лишь приближённо отслеживает реальный контур спроектированного к данному моменту участка поверхности» (1), поэтому возможен неполный контроль видимости на краях рисунка. Для уменьшения дефектов в [1] рекомендуется увеличивать качество сечений. Однако даже для указанной довольно простой функции FNS одно сечение на БК-0010.01 строится около 7 с. Для тригонометрических функций расход времени ещё возрастает. Легко понять, что время построения графика фактически пропорционально LX+LY, так что вряд ли имеет смысл задавать LX и LY больше, скажем, 20. Невелика и разрешающая способность дисплея.
Поэтому для улучшения контроля видимости при построении сечений, параллельных 0X (в этом семействе искажения больше), можно МАХ и MIN после обычной инициализации «подправить» - задать им значения, равные граничному сечению графика функций, параллельному оси 0Y, т.е. выполнить следующие команды:
381 X=K 382 FOR Y=-К TO К STEP 1/M 383 Z=FNS(X,Y) 384 GOSUB 510 385 MAX(XD)=YD 386 MIN(XD)=YD 387 NEXT Y
Аналогично можно улучшить контроль в семействе сечений, параллельных 0Y, присвоив МАХ и MIN значения соответствующего граничного сечения, параллельного 0X. С дополнительным контролем время выполнения будет пропорционально LX+LY+2. Один и тот же график с дополнительным контролем и без него приведён на рис. 1, 2.
Описанная программа реализована на Бейсике БК-0010.01, на «зеленоградском» варианте Бейсика для ДВК-3, на Паскале в системе РАФОС с графическим пакетом MED для ДВК-3.
Кроме того, программа на Паскале для ДВК-3 компонуется с разработанным одним из авторов интерпретатором арифметических выражений, что позволяет в режиме диалога строить графики любых комбинаций элементарных функций, допускаемых, например, синтаксисом Паскаля, даже с некоторыми расширениями.
Она может использоваться для изучения поверхностей второго порядка в курсе аналитической геометрии и линейной алгебры, так как фактически реализует классический метод изучения вида поверхностей - метод координатных сечений. Программа полезна и в практических занятиях по математическому анализу при изучении темы «Функции нескольких переменных», так как позволяет наглядно показать различные типы особенностей их графиков, зависимость вида функции от выбираемых параметров и другие детали поведения функции.
На занятиях можно требовать при заданных параметрах и выражении функции «угадать» заранее (до включения программы) график или, наоборот, по предъявленному на экране графику определить функцию.
Адрес для справок: 150000, Ярославль, ул. Кирова, 8/10, факультет ИВТ Ярославского государственного университета, кафедра системного программирования.
Литература
- Баяковский Ю.М., Галактионов В.А., Михайлова Т.Н. Графор. Графическое расширение Фортрана. М.: Наука, 1985.
- Котов Ю.В. Как рисует машина. М.: Наука, 1988.