Эта статья посвящена тому, как получить доступ к встроенным возможностям БЕЙСИКа из программ в кодах и на языке АССЕМБЛЕРа. Всё сказанное будет относиться к версии БЕЙСИКа "Вильнюс 1986.07.24", поставляемой заводом-изготовителем вместе с БК-0010.01.

Все приведённые здесь тексты программ на языке АССЕМБЛЕРа написаны в формате входного языка для системы "Микро". Однако если эта система у вас отсутствует, то не отчаивайтесь, текст программ без особого труда можно переделать для вашей любимой системы ("Мираж", "Shura", "ОС-0010", "ОТЛ" и т.д.) или просто ввести в кодовом варианте с помощью тестового монитора.

В.В. Авсеев, А.В. Авсеев

ИСПОЛЬЗОВАНИЕ ВОЗМОЖНОСТЕЙ БЕЙСИКА В ПРОГРАММАХ НА ЯЗЫКЕ АССЕМБЛЕРА

Пользователи БК-0010 в модели БК-0010.01 получили новую клавиатуру и "зашитый” в ПЗУ транслятор языка БЕЙСИК, о котором говорилось, что он соответствует популярному стандарту MSX. Кроме того, в руководстве заявлялось о существовании в дополнительном внешнем блоке ПЗУ интерпретатора языка ФОКАЛ. Таким образом, пользователь получал возможность работать сразу с двумя языками высокого уровня на своём компьютере.

Когда первые БК-0010 появились в магазине "Электроника”, авторы этой статьи, не задумываясь, приобрели его (тогда это можно было сделать без всякой очереди). Но вскоре радость приобретения сменилась некоторым разочарованием. Все поиски интерпретатора ФОКАЛа в дополнительном блоке ПЗУ были тщетными, клавиатура оказалась по качеству не на много лучше плёночной, особенно бросался в глаза известный теперь всем пользователям БК-0010.01 "дребезг” клавиатуры. Но больше всего авторов поразило, что обещанных в руководстве демонстрационных программ на БЕЙСИКе не оказалось, вместо них были записаны демонстрационные программы на ФОКАЛе (видимо, на заводе-изготовителе тоже есть шутники). Не с хорошей стороны проявил себя и транслятор БЕЙСИКа. Авторы сразу столкнулись с проблемой нехватки памяти, непонятными зависаниями, замедлением работы с символьными переменными, ненадёжной работой некоторых функций, неправильной реализацией многомерных массивов и т.д.

Правда, потом оказалось, что с "дребезгом” клавиатуры довольно легко можно справиться. Без ФОКАЛа в принципе можно обойтись. Но недостатки БЕЙСИКа исправить без замены ПЗУ никак нельзя. Оставался один выход: переходить к программированию на языке АССЕМБЛЕРа. Но в таком случае возникает много трудностей из-за отсутствия возможностей, предоставляемых пользователю БЕЙСИКом. Конечно, написать подпрограммы, реализующие сложные графические операторы БЕЙСИКа (рисование окружности, закрашивание замкнутой области), можно и на языке АССЕМБЛЕРа. Но всегда ли оправдано изобретать велосипед? Иногда оказывается полезным использовать уже заложенные в ПЗУ БК-0010.01 возможности.

Поиск адресов подпрограмм

Большинство трансляторов БЕЙСИКа является интерпретаторами. Но на ПЭВМ БК-0010.01 реализован компилятор БЕЙСИКа: исходная программа после её запуска командой RUN преобразуется в памяти ЭВМ в прямой шитый код (детальное определение шитого кода см., например: Баранов С.Н., Новдрунев Н.Р., Язык ФОРТ и его реализации. - М: Машиностроение, 1988. - с.43). Прямой шитый код представляет собой последовательность адресов подпрограмм, констант и адресов переменных. Один оператор БЕЙСИКа реализуется с помощью одной или нескольких таких подпрограмм.

Узнать адреса подпрограмм, реализующих тот или иной оператор довольно просто. Авторы уже останавливались на этом вопросе в журнале "Информатика и образование" [1]. В этом же номере приведён почти полный список адресов подпрограмм и операторов БЕЙСИКа.

Приведённая далее программа на БЕЙСИКе выдаёт на экран шитый код, соответствующий вашим операторам, записанным в первых строках этой программы. Все числа выводятся в восьмеричной системе исчисления. Программа использует ячейку &O2002, в которой хранится адрес последнего байта исходного текста программы на БЕЙСИКе. Шитый код будет начинаться со следующего большего чётного адреса. Предполагается, что среди ваших операторов, записанных в первых строках программы, отсутствует CLS, так как именно до этого оператора будет выводиться шитый код. Подпрограмма, реализующая оператор CLS, находится по адресу &О156776.

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
140 ? OCT$(PEEK(N%))
150 N% = N% + 2%
160 GOTO 130
170 END

В приведённой ниже таблице каждому оператору языка, записанному в общей форме, сопоставляется соответствующий ему шитый код - содержимое последовательно расположенных в ОЗУ микро-ЭВМ 16-разрядных слов. В шитом коде, кроме адресов подпрограмм, могут располагаться адреса переменных и другие данные. Типы параметров, приводимых в таблице, соответствуют стандарту Бейсик-MSX и обозначаются: # - вещественное двойной точности; ! - вещественное одинарной точности; % - целое число. Параметры, помещённые в первой части таблицы в скобках, означают, что их необходимо переслать на стек в порядке перечисления. На вершину стека указывает регистр R6(SP) процессора микро-ЭВМ. Переслать параметры на стек можно при помощи вспомогательных подпрограмм БЕЙСИКа, также перечисленных в таблице. В приведённой таблице в отличие от таблицы в журнале "Информатика и образование" занесены лишь наиболее употребительные операторы и системные адреса:

Операторы

PRINT А%

(A%) 157070

PRINT A!

(A!) 157014

PRINT A#

(A#) 157022

INPUT X1,X2,...,Xn

160634, 157150, адрес следующей команды, список адресов Х1,...,Хn

CLS

156776

CIRCLE(A%,B%),C%,D%

(А%,В%,С%,D%) 125334, 126724,126742, 127122,127324

PAINT(A%,B%), C%, D%

(А%,В%,С%) 125334, (D%), 125334, 125642

DRAW A$

(адрес А$, длина А$) 131412

Отношения и логические операции

А%<В%

(А%, В%)

155172,

155452

А#<В#

(А#, В#)

177034,

155452

А%>В%

(А%, В%)

155172,

155456

А#>В#

(А#, В#)

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

SIN(X#)

(X#)

173614

COS(X#)

(X#)

173566

TAN(X#)

(X#)

174306

ATN(X#)

(X#)

174434

EXP(X#)

(X#)

171762

PI

 

167102

LOG(X#)

(X#)

173052

ABS(X#)

(X#)

166566

FIX(X#)

(X#)

176212

INT(X#)

(X#)

176340

SGN(X%)

(X%)

166572

SGN(X!)

(X!)

166624

SGN(X#)

(X#)

166614

RND(X#)

(x#)

175176

CINT(X#)

(X#)

160760

CSHG(X#)

(x#)

162020

CDBL(A!)

(A!)

166742

CDBL(A%)

(A%)

166646

VAL(A$)

(адрес A$, длина A$)

161550

STR$(A%)

(A%)

161610 (JSR PC,140536)

STR$(A#)

(A#)

161602 (JSR PC, 164710)

STR$(A!)

(A!)

161574 (JSR PC, 164664)

OCT$(A%)

(A%)

161624

BIN$(A%)

(A%)

161672

HEX$(A%)

(A%)

161734

Адреса вспомогательных подпрограмм

156160,

A%

- запись слова A% на стек

156156,

A!

- запись двух слов на стек

156152,

A#

- запись четырёх слов на стек

156232,

А%

- пересылка слова по адресу А% на стек

156214,

А%

- пересылка двух слов по адресу А% на стек

156170,

А%

- пересылка четырёх слов по адресу А% на стек

156350,

А%

- запись слова со стека по адресу А%

156330,

А%

- запись двух слов со стека по адресу А%

156334,

А%

- запись четырёх слов со стека по адресу А%

166562

 

- изменение знака целого числа на стеке

156750

 

- изменение знака вещественного числа на стеке

Некоторые системные адреса БЕЙСИКа

2002

- ячейка, содержащая адрес конца текста программы

2004

- ячейка, содержащая адрес конца шитого кода программы

37400

- адрес буфера ввода-вывода с магнитофона

2422

- адрес буфера ввода с клавиатуры

3022

- адрес буфера преобразования в строку и вывода чисел на экран

120300

- адрес перезапуска БЕЙСИКа из монитора с сохранением программы

120234

- адрес подпрограммы обработки прерывания по нажатию клавиши "СТОП"

121052

- адрес вызова строкового редактора текстов Бейсика

Арифметические операции

А% + В%

(А%, В%)

162102

A# + B#

(А#, В#)

167144

А% - В%

(А%, В%)

162112

A# - B#

(А#, В#)

167124

А# / В#

(А#, В#)

170776

А% \ В%

(А%, В%)

162052

А% * В%

(А%, В%)

162116

A# * B#

(А#, В#)

170210

Заметим, что в таблице буквами А - D обозначены константы, при этом константа А% занимает одно слово (два байта), А! - два слова, A# - четыре слова.

Некоторые неудобства при использовании операторов БЕЙСИКа из программ в кодах или на языке АССЕМБЛЕРа доставляет способ их вызова: для этого требуется записать в регистр R4 адрес слова ОЗУ, где расположен адрес подпрограммы, а затем выполнить команду JMP @(R4)+. Такова особенность компилятора БЕЙСИКа - передача управления от одной подпрограммы к другой осуществляется через регистр R4 по команде JMP @(R4)+, содержащейся в конце каждой подпрограммы. В связи с этим желательно весь список адресов подпрограмм (в вашей программе) записывать в ОЗУ подряд, тогда автоматически после одной подпрограммы будет вызвана на исполнение следующая. Многие подпрограммы БЕЙСИКа используют служебную область памяти и прерывание TRAP. Основная служебная область БЕЙСИКа начинается с адреса &O2000. Адреса &O2422 - &O3022 занимает буфер строкового редактора (его длина 256 байт), в самом конце этого буфера располагается маленькая область, используемая оператором PRINT для преобразования чисел в символьное представление. Начальный адрес этой области - &O3002. При использовании операторов ввода/вывода на внешнее устройство (магнитофон, локальная сеть) будет задействован буфер с начальным адресом &O37400. При использовании операторов БЕЙСИКа не желательно изменять содержимое памяти по адресам &O200 - &O3052, так как эти ячейки используются подпрограммами БЕЙСИКа для своих нужд. Область памяти &O1000 - &O2000 БЕЙСИК в принципе не использует, но во время его работы эта область задействована под системный стек. Если переместить начало стека на его "обычное'' место (адреса &O736 - &O1000), то областью памяти &O1000 - &O2000 можно пользоваться без опасений. При использовании операторов ввода/вывода нужно обязательно обнулить ячейку по адресу &O2416, а перед вызовом арифметических операций также нужно обнулить ячейку &O2314.

Ниже приводится пример использования подпрограмм БЕЙСИКа.

Адрес

Код команды

Команда АССЕМБЛЕРа

1000:

12737,146404,34

     MOV #146404,@#34

1006:

105737,40

     TSTB @#40

1012:

1003

     BNE 1

1014:

12700,233

     MOV #233,R0

1020:

104016

     ЕМТ 16

1022:

12704,1034

1:   MOV #PRG,R4

1026:

134

     JMP @(R4)+

1030:

104006

EXT: ЕМТ 6

1032:

0

     HALT

1034:

156156,175,170,156160

PRG: .#156156 .#175 .#170 .#156160

1044:

24,125376,126724

     .#24 .#125376 .#126724

1052:

126742,127122,127334

     .#126742 .#127122 .#127334

1060:

156156,175,170,125376

     .#156156 .#175 .#170 .#125376

1072:

125404,125642,1030

     .#125404 .#125642 .#EXT

 

 

     .END

TRAP - запросы БЕЙСИКа

В БЕЙСИКе, как и в ФОКАЛе, TRAP-запросы выполняют служебную роль. При этом запросы с номерами 0 - &O77 отведены для прерывания работы по ошибочной ситуации во время выполнения программ на БЕЙСИКе, а запросы с номерами больше &O77 вызывают внутренние служебные подпрограммы БЕЙСИКа. Во время выполнения подпрограмм БЕЙСИКа могут возникнуть ошибочные ситуации: деление на нуль, переполнение разрядной сетки, недопустимое использование функции и т.д. Конечно, это приведёт к выполнению TRAP-запроса с номером, равным номеру соответствующей ошибки в БЕЙСИКе. Если вы не напишете свой вариант "ловушки" для TRAP-запросов и не установите её начальный адрес в ячейку &O34, использование подпрограмм БЕЙСИКа может привести к непредсказуемым последствиям. Поэтому мы приводим здесь свой вариант текста "ловушки" TRAP- запросов:

      MOV R0,-(SP)
      MOV R2,-(SP)
      MOV 4(SP),R0
      MOV 177776(R0),R2
      BIC #177440,R2
      CMP R2,#100
      BLO 1
      JMP @#146424
1:    JMP ERR ; переход по ошибке
      ...
      ...
ERR:

После метки ERR: должна располагаться программа, реагирующая на возникновение ошибки. При этом в R2 будет храниться номер ошибки (список ошибок и их номера можно посмотреть в руководстве по БЕЙСИКу). В начале вашей программы, использующей подпрограммы БЕЙСИКа, запишите в ячейку &O34 адрес этой "ловушки".

Оригинальная идея использовать TRAP-прерывания по ошибке 5 принадлежит И. Николаеву (см. журнал "Информатика и образование" [2]). В приведённой там программе применяется перехват прерывания TRAP 5, возникающего при попытке выполнить команду LOAD в момент, когда в памяти уже есть текст некоторой программы. Вообще, использование TRAP-запросов для своих целей при вызове подпрограмм БЕЙСИКа может в некоторых случаях привести к зависаниям. Задействование же TRAP-запросов при работе компилятора вообще невозможно

Арифметика чисел с плавающей запятой

БЕЙСИК-БК представляет более широкие возможности для работы с вещественными числами по сравнению с ФОКАЛом. Но механизм доступа к подпрограммам, реализующим плавающую арифметику, более неудобен. Он возможен либо командой JSR PC,<N>, где <N> - адрес подпрограммы, или командой JMP @(R4)+. Прерывание по резервной команде процессора в БЕЙСИКе вообще не задействовано (в отличие от ФОКАЛа), и реакцией на неё будет сообщение об ошибке с номером 52. Но использование встроенных возможностей арифметики БЕЙСИКа экономит память и время для написания новых подпрограмм. Примером использования плавающей арифметики может служить следующая программа. Она использует встроенные подпрограммы БЕЙСИКа: сложение, вычитание, деление и умножение чисел, оператор PRINT A# и функцию VAL(A$).

        MOV     #TRAP,@#34  ; УСТАНОВКА АДРЕСА TRAP-ЛОВУШКИ
        MOV     #1000,SP    ; УСТАНОВКА УКАЗАТЕЛЯ СТЕКА
        CLR     @#2416      ; ВЫВОД НА ЭКРАН
        CLR     @#2134
        TSTB    @#40        ; УСТАНОВКА ЦВЕТНОГО РЕЖИМА
        BNE     1
        MOV     #233,R0
        EMT     16
1:      MOV     #ST2,R1     ; ВЫДАЕМ НАЧАЛЬНУЮ ЗАСТАВКУ
        JSR     PC,OUS
CIC:    MOV     #M1,EXT     ; ЗАПОМИНАЕМ АДРЕС ВЫХОДА 
M3:     MOV     #ST3,R1     ; ВЫВОДИМ ОПЕРАНД
        JSR     PC,OUS
        JSR     PC,@#121052
        MOV     #VAL,R4
        JMP     @(R4)+
Ml:     MOV     #M2,EXT     ; ЗАПОМИНАЕМ АДРЕС ВЫХОДА
M2:     MOV     #ST4,R1
        JSR     PC,OUS
        EMT     6           ; ВВОД ОПЕРАЦИИ
        EMT     16
        MOV     #ST5,R1
        JSR     PC,OUS
        CMPB    R0,'+'
        BNE     1
        MOV     #PR1,R4     ; СЛОЖЕНИЕ ЧИСЕЛ
        JMP     @(R4)+
1:      CMPB    R0,'-'
        BNE     2
        MOV     #PR2,R4     ; ВЫЧИТАНИЕ ЧИСЕЛ
        JMP     @(R4)+
2:      CMPB    R0,'/'
        BNE     3
        MOV     #PR3,R4     ; ДЕЛЕНИЕ ЧИСЕЛ
3:      JMP     @(R4)+
        CMPB    R0,'.'
        BNE     4
        MOV     #PR4,R4     ; УМНОЖЕНИЕ ЧИСЕЛ
4:      JMP     @(R4)+
        TRAP    0
TRP:    MOV     #ST1,R1    ; БЛОК TRAP-ЛОВУШКИ
        JSR     PC,OUS
        MOV     #1000,SP
        JMP     CIC
OUS:    CLR     R2          ; ПОДПРОГРАММА ВЫВОДА 
        EMT     20          ; СТРОКИ НА ЭКРАН
        RTS     PC
STI:    .B:12 .A:ОШИБКА ! ПОВТОРИТЕ ВВОД
        .B:12 .E
ST2:    .B:14 .A:НЕБОЛЬШОЙ КАЛЬКУЛЯТОР
        .B:12 .E
ST3:    .B:12 .A:ВВЕДИТЕ ОПЕРАНД:
        .E
ST4:    .B:12 .A:ОПЕРАЦИЯ (-, + ,*,/):
        .E
ST5:    .B:12 .A:РЕЗУЛЬТАТ =
        .E
VAL:    .#156156 .#2422 .#400 .#161550
EXT:    .Е
PR1:    .#167144 .#157022 .#СIС
PR2:    .#167124 .#157022 .#СIС
PR3:    .#170776 .#157022 .#СIС
PR4:    .#170210 .#157022 .#СIС
        END

12737

4214

34

12706

1000

5037

2416

5037

2134

105737

40

1003

12700

233

104016

12701

4276

4767

166

12767

4076

344

12701

4326

4767

150

4737

121052

12704

4410

134

12767

4106

314

763

12701

4350

4767

116

104006

104016

12701

4374

4767

102

120027

53

1003

12704

4422

134

120027

55

1003

12701

4430

134

120027

57

1003

12704

4436

134

120027

52

1003

12704

4444

134

10400

12701

4242

4767

10

12706

1000

167

177612

5002

104020

207

47412

164773

45742

20101

20041

47760

52102

50117

52351

20105

41102

162117

5056

0

44014

161105

166117

175770

165117

45440

166101

45770

166365

52361

50117

12

41012

42502

164744

42524

47440

42760

40520

162110

72

47412

42760

40520

164743

24361

26053

26055

26052

24457

72

50012

175105

166365

52370

52101

75

156156

2422

400

161550

0

167144

157022

4046

167124

157022

4046

170776

157022

4046

170210

157022

4046

 

 

Обратите внимание, что программа "калькулятор" использует для ввода строк с клавиатуры не стандартную функцию ЕМТ 10, а встроенный редактор строк БЕЙСИКа. В отличие от ЕМТ 10 этот редактор допускает довольно мощные возможности редактирования. Вызывается этот редактор командой JSR РС,@#121052 Текст вводится в буфер с начальным адресом &O2422, длина буфера - 256 байт Заметим, что формат представления чисел с плавающей запятой в БЕЙСИКе отличен от формата, применяемого в ФОКАЛе БК. В вещественных числах одинарной точности в первом слове с 7-го по 14-й бит занимает порядок числа, старшие разряды мантиссы занимают с 0-го по 6-й бит этого же слова. Младшие разряды мантиссы располагаются в следующем слове. В вещественных числах двойной точности точность увеличивается за счёт удлинения мантиссы. Надо заметить, что в БЕЙСИКе нет, например, арифметических операций над вещественными числами одинарной точности (точнее, нет таких подпрограмм) Перед выполнением арифметических операций над такими числами БЕЙСИК производит преобразование типа: CDBL(A!)

К вопросу об экономии памяти

Память БК стала объектом злых шуток и поговорок. Пожалуй, каждый пользователь БК сталкивается с проблемой экономии памяти. БЕЙСИК-БК является на редкость неэкономным языком. Мало того, что операторы в БЕЙСИКе довольно длинные, он их при этом никак не кодирует для хранения в памяти (подавляющее большинство трансляторов БЕЙСИКа кодируют операторы в специальный внутренний экономный код). Во время же компиляции память "поедается" под размещение шитого кода, который, кстати сказать, не совсем рационально реализован. Многие пользователи БК считают, что секрет экономии памяти заключён в уменьшении длины исходного текста программы. Но это не всегда так. Простейшим примером является цикл БЕЙСИКа:

1 FOR I = 1 ТО 100
2 NEXT

Можно записать этот пример и по-другому:

1 FOR I% = 1% ТО 100%
2 NEXT I%

Первая программа по тексту короче второй. Однако если мы посмотрим после команды RUN объём свободной памяти (в первом и во втором случае), то окажется, что второй вариант занимает в памяти на 22 байта меньше. Причина этого в том, что в первой программе переменная I будет по умолчанию принята как вещественная двойной точности, а во второй мы принудительно указали, что переменная 1 является целой (экономия 6 байт). Это же относится и к константам 1 и 100 (ещё 12 байт) Интересно отметить, что NEXT занимает в шитом коде места больше, чем NEXT I%. Здесь можно сэкономить на длине текста программы, но больше потерять за счёт увеличения длины шитого кода. Поэтому никогда не используйте NEXT без явного указания имени переменной. Если возможно, то чаще используйте целые переменные и константы. Если вы используете целые или вещественные одинарной точности константы и переменные, то не забывайте явно указывать их тип. Но всё-таки обычно сокращение длины текста программы сопровождается и уменьшением занимаемой памяти ПЭВМ.

Вот пример простой задержки в программе:

100 А$ = INKEY$
110 IF А$ = "" THEN 100

Этот пример можно сделать и таким:

100 IFINKEY$=""TH100

Во втором случае мы сэкономим 37 байт и за счёт уменьшения длины текста, и за счёт сокращения шитого кода.

Авторы хотели бы предупредить, что при отладке программы "Калькулятор" в системе "Микро" перед перезапуском нужно перезагрузить её с МЛ, так как используемые в программе подпрограммы БЕЙСИКа размещают свои переменные в области памяти &O2000 - &О3022 и портят при этом "Микро". Не пытайтесь также запускать приведённые в статье программы при подключённом внешнем блоке ПЗУ с тестовым монитором (и, может быть, ФОКАЛом), ведь при этом ими будут перекрыты области памяти, занимаемые подпрограммами БЕЙСИКа.

В заключение авторы хотели бы выразить благодарность студенту МАИ Александру Сергееву. Именно его письмо (Вычислительная техника и её применение. - 1989. - № 9. - С.46-47) убедило нас, что данная статья нужна пользователям БК-0010.01.

Литература

  1. Авсеев В.В., Авсеев А.В. Особенности транслятора с языка Бейсик для БК-0010.01 // Информатика и образование. - 1990. - № 2. - С.42-46.
  2. Николаев И. Нуждается в доработке // Информатика и образование. - 1989. - № 1. - С.101.
  3. Осетинский Л.Г., Осетинский М.Г., Писаревский А.Н. ФОКАЛ для микро- и миникомпьютеров. - М.: Машиностроение, 1988.
  4. Ларкин М. Экономия памяти БК-0010.01 // Информатика и образование. - 1990. - № 3. - С.44-49.

Performed by © gid, 2012-2024.