Л.Н. ЖАРИКОВ (г. Ленинград)
О СТАТЬЕ «БОЛЬШИЕ ПРОГРАММЫ НА БК0010» (№3, 1989)
С трудом самостоятельно разобрался в работе опубликованной программы и устранил неточности. Прочитав в № 7 серии ВТ письмо П.И. Павлова и представив, сколько усилий потребуется любителям для запуска данной программы, хочу предложить несколько модифицированный вариант этой интересной программы с более подробным, чем у авторов статьи, описанием её работы. Эта модификация позволяет ускорить выполнение программы на 20% и снять некоторые ограничения, присущие оригиналу.
Предлагаю опубликовать модифицированную программу с целью обучения.
По примеру авторов программы COM.ASC программа COMM.ASC (см. таблицу) написана на БЕЙСИКе. Она загружается в память компьютера и запускается как любая программа на БЕЙСИКе БК-0010.01. При своей работе программа COMM.ASC создаёт программу в машинных кодах, начиная с адреса 37400 (здесь и далее все адреса представлены в восьмеричной системе счисления), и передаёт ей управление. В результате на экране пропадает курсор, и пользователь с клавиатуры может ввести произвольную последовательность команд БЕЙСИК-системы, разделяя их клавишей ввода (ВК).
Например,
NEW /ВК/ CLEAR 200,&O20000 /ВК/ LOAD «PROG1»/BK/ RUN /ВК/ NEW /ВК/ LOAD «PROG2» /ВК/ RUN /ВК/
Запуск введённой последовательности на исполнение осуществляется нажатием клавиши «:».
В примере выполняются последовательно две программы «PROG1» и «PROG2». Оператор CLEAR организует область памяти для возможной передачи данных из «PROG1» в «PROG2».
По окончании выполнения последовательности команд останется загруженной в память Бейсик-программа «PROG2» То есть точно так же, как если бы вы набирали и выполняли эти команды последовательно с клавиатуры в непосредственном режиме Бейсик-системы. Кроме того, останется резидентно в памяти и программа в кодах, сформированная программой COMM.ASC, если в «PROG1» или в «PROG2» не переопределялись функции пользователя USR0. Необходимо заметить, что команды NEW и CLEAR не переопределяют функции USR пользователя.
Этим последним обстоятельством можно воспользоваться для повторного запуска программы в кодах, оставшейся резидентно, командой A=USR(A), введённой в непосредственном режиме Бейсик-системы.
Полезно сохранить сформированную программой COMM.ASC программу в кодах на магнитной ленте командой BSAVE «СОМК», &О37400, &O37562. Записанная таким образом программа COMK.BIN в машинном коде занимает на МЛ существенно меньше места, чем COMM.ASC, следовательно, загружается быстрее и не требует компиляции.
Загрузить в память БК и запустить программу COMK.BIN можно, набрав команду BLOAD «СОМК», R. Кроме того, при запуске программы СОМК таким образом не требуется начальная команда NEW в серии команд Бейсик-системы для освобождения памяти под первую загружаемую программу.
Недостатком программы COM.ASC является то обстоятельство, что при наличии в загружаемых Бейсик-программах операторов INPUT и функций INKEY$ требующих ввода символов с клавиатуры, все необходимые программам данные нужно заносить в буфер, а это не всегда целесообразно.
Программа COMM.ASC в отличие от программы COM.ASC позволяет свободно использовать INPUT и INKEY$. Это достигается введением в загружаемые программы на БЕЙСИКе четырёх дополнительных строк:
- в самом начале программы, использующей клавиатуру, нужно вставлять
DEF USR1=&O37554 DEF USR2=&O37440 A=USR2(A);
- в конце программы добавляется строка
A=USR2(A).
Эти вставки восстанавливают и заменяют соответственно адрес стандартной программы обработки программного прерывания ЕМТ, хранящийся в ячейке с адресом 30, на адрес 37450 программы выбора символа из буфера, заполненного с клавиатуры пользователем.
Теперь коротко об отличиях программы СОММ.ASC от программы COM.ASC.
Для ускорения передачи символа из буфера Бейсик-системе восстановление адреса стандартной программы обработки прерывания ЕМТ переставлено в другой участок программы
С целью универсализации программы выброшены строки 54,55 программы COM.ASC, которые должны по замыслу авторов заносить в буфер программы самой первой команду NEW/ВК/, а не являются лишними, как считает тов. Павлов П.И. из г. Москвы. Такой подход при отладке приводил сразу к стиранию из памяти оригинала Бейсик-программы COM.ASC независимо от желания пользователя (а способ запуска резидентной программы, сохранившейся в памяти, авторы не указали). Об этой команде-невидимке NEW в статье не сказано ни слова, а расшифровка строк 54-55, опубликованных с ошибками в адресах, не даётся с лёта, особенно если работаешь в БЕЙСИКе, а не на АССЕМБЛЕРе. Кроме того, наличие скрытой от пользователя команды не является примером хорошего стиля программирования и занимает память.
Буфер программы в кодах подвинут вплотную к самой программе.
Рассмотрим теперь структуру программы COMK.BIN в кодах, которая будет сформирована программой СОММ. ASC
В программе СОМК работают четыре подпрограммы:
- подпрограмма заполнения буфера с клавиатуры;
- подпрограмма обработки программного прерывания ЕМТ 6 для выдачи символа из буфера в регистр R0;
- подпрограмма подмены в ячейке 30 адреса стандартной программы обработки прерывания ЕМТ;
- подпрограмма восстановления в ячейке 30 стандартного адреса подпрограммы обработки прерывания ЕМТ
Графически структура программы COMK.BIN выглядит так:
Адрес начала |
Наименование подпрограммы |
Условное наименование |
---|---|---|
37400: |
Заполнение буфера программы с клавиатуры, пока не будет нажата клавиша «:» Подмена адреса подпрограммы обработки прерывания EMT |
Заполнение |
37440: |
Подмена |
|
37450: |
Обработка программного прерывания ЕМТ 6 для выдачи символа из буфера в регистр R0 |
Обработка |
37554: |
Восстановление в ячейке 30 стандартного адреса подпрограммы обработки прерывания ЕМТ |
Восстановление |
Рассмотрим работу программы COMM.ASC и создаваемой ею программы COMK.BIN.
Операторы 1-58 создают программу в кодах COMK.BIN, начиная с адреса 37400. Оператор 59 определяет функцию пользователя USR0. Оператор 60 передаёт управление этой функции, т.е. программе в кодах СОМК.
Машинные команды, сформированные в строках 1-6, вызывают начальное заполнение адреса текущего свободного байта в буфере в ячейку с адресом 37564 и адреса текущего байта в буфере в ячейку с адресом 37566.
Команда, сформированная в строке 7, вызывает программное прерывание ЕМТ 6, по которому система ожидает ввода символа с клавиатуры. Этот символ заносится в регистр R0.
Следующая команда ЕМТ 16 вызывает прерывание для выдачи символа из регистра R0 на экран.
Строки 9-10 вызывают передачу символа из регистра R0 в текущий байт буфера, адрес которого находится в ячейке с адресом 37564.
Команда в строках 11-12 увеличивает на 1 содержимое ячейки с адресом 37564.
Команда в строках 13-14 сравнивает код только что переданного символа с кодом «:».
Команда в строке 15 в случае равенства переданного в буфер символа «:», т.е. когда пользователь ввёл уже в буфер то, что хотел, передаёт управление на команду, сформированную в строке 17. Иначе отработает команда из строки 16. Управление будет вследствие этого передано вновь на команду из строки 7, т.е. на ввод очередного символа с клавиатуры в регистр R0, а затем на передачу его в буфер программы. Заполнение буфера будет закончено только тогда, когда пользователь нажмёт клавишу «:».
В этом случае команда, сформированная в строке 17, подменяет адрес в ячейке 30 (адрес, сначала содержащийся в ней, равен 100112) на адрес 37450, который указывает на подпрограмму «Обработка».
Следующая команда, сформированная в строке 20, возвращает управление Бейсик-системе. Бейсик-система переходит в непосредственный режим выполнения, т.е. в режим ожидания ввода от клавиатуры по команде программного прерывания ЕМТ 6. Но адрес программы обработки прерывания, извлекаемый командой ЕМТ 6 из ячейки 30, уже был подменен нашей программой в кодах на адрес 37450, поэтому осуществляется переход не по обычному адресу 100112, а по указанному.
Программа COMK.BIN начиная с команды в строках 21-22, выполняет следующие действия:
- запоминает адрес команды, с которой необходимо продолжить выполнение после завершения нашей программы, в ячейке с адресом 37570;
- уменьшает этот адрес на 2 (строки 23-26), т.е. определяет адрес команды, которая вызвала программное прерывание ЕМТ (не обязательно ЕМТ 6);
- вставляет эту команду в нашу программу на место команды в строке 37.
Это делается на тот случай, если программное прерывание, вызвавшее переход на программу «Обработка», окажется не прерыванием ЕМТ 6, а каким-нибудь другим ЕМТ J. В этом случае переход на программу «Обработка», которая не может выполнить этих функций, является ошибкой. Поэтому нужно, временно восстановив обычный адрес программы обработки прерывания ЕМТ, ещё раз выполнить эту же команду ЕМТ J;
- сравнивает команду, которая вызвала прерывание ЕМТ, с командой ЕМТ 6 (код 104006) в строках 30-32;
- в случае команды ЕМТ 6 осуществляется переход на команду, сформированную в строках 42-43, являющуюся частью программы «Обработка»;
- в случае команды, отличной от ЕМТ 6, выполняется переход на команду в строках 34- 36, засылающую обычный адрес программы обработки прерывания ЕМТ в ячейку 30;
- на месте команды HALT, сформированной в строке 37, записана некоторая команда ЕМТ J, которая занесена туда в процессе работы программы, поэтому она выполняется повторно;
- после выполнения команды ЕМТ J выполняется замена обычного адреса подпрограммы обработки прерывания ЕМТ на адрес 37450 (строки 38-40) и осуществляется возврат из прерывания (строка 41);
- строки 42-54 формируют программу «Обработка», которая выбирает из буфера очередной символ и передаёт в регистр R0;
- команда в строках 42-43 пересылает байт, находящийся по адресу, который находится в ячейке 37566, в регистр R0;
- строки 44-45 увеличивают адрес очередного байта на 1;
- команда в строках 46-48 проверяет адрес последнего байта буфера и адрес текущего байта буфера на совпадение;
- если буфер не исчерпан, т.е. указанные адреса не равны, то осуществляется выход из прерывания (строка 50), иначе восстанавливается обычный адрес подпрограммы обработки прерывания ЕМТ в ячейке 30 и программа завершает работу (строки 51-54);
- строки 55-58 формируют подпрограмму «Восстановление», которая нужна для включения обычного режима обработки прерывания ЕМТ в конце Бейсик-программ, использующих ввод с клавиатуры;
- строки 17-20 формируют подпрограмму «Подмена», служащую для выключения обычного режима обработки программного прерывания ЕМТ.
Анализ программы показывает, что её можно несколько сократить, если выделить подпрограммы. Например, вместо команды в строках 51-53 можно вставить вызов подпрограммы по адресу 37554, но при этом выполнение программы несколько замедлится.
Всем предложенная программа хороша. Одно плохо: отсутствие у большинства читателей магнитофонов с электронным управлением включения / выключения двигателя. Хотелось бы, чтобы читатели, имеющие доработанные магнитофоны, поделились своими находками. Интересно также было бы узнать, какие альтернативные варианты расширения оперативной памяти БК существуют у читателей, которые владеют ими, можно ли доработать БК для подключения к нему накопителя на гибких магнитных дисках?
Адрес |
Машин-ный код |
Ассемблер |
Комментарии |
Бейсик СОММ. ASC |
|
---|---|---|---|---|---|
1 |
2 |
3 |
4 |
5 |
|
37400 |
012737 |
MOV #A+6, @#A |
Засылка адреса начала буфера в ячейку, предназначенную для хранения адреса первого свободного байта в буфере |
1. |
POKE &O37400,&O12737 |
37402 |
037572 |
2. |
POKE &O37402,&O37572 |
||
37404 |
037564 |
3. |
POKE &O37404,&O37564 |
||
37406 |
012737 |
MOV #A+6, @#A+2 |
Засылка адреса начала буфера в ячейку, предназначенную для хранения адреса текущего байта в буфере |
4. |
POKE &O37406,&O12737 |
37410 |
037572 |
5. |
POKE &O37410,&O37572 |
||
37412 |
037566 |
6. |
POKE &O37412,&O37566 |
||
37414 |
104006 |
M1: EMT 6 |
Ввод символа с клавиатуры в регистр R0 |
7. |
POKE &O37414,&O104006 |
37416 |
104016 |
EMT 16 |
Вывод символа из регистра R0 в текущую позицию экрана |
8. |
POKE &O37416,&O104016 |
37420 |
110077 |
MOVB R0,@A |
Пересылка символа из регистра R0 в текущий байт буфера |
9. |
POKE &O37420,&O110077 |
37422 |
000140 |
10. |
POKE &O37422,&O140 |
||
37424 |
005237 |
INC @#A |
Увеличение на 1 адреса первого свободного байта в буфере |
11. |
POKE &O37424,&O5237 |
37426 |
037564 |
12. |
POKE &O37426,&O37564 |
||
37430 |
020027 |
CMP R0,#72 |
Сравнение содержимого |
13. |
POKE &O37430,&O20027 |
37432 |
000072 |
R0 с кодом символа «:» |
14. |
POKE &O37432,&O72 |
|
37434 |
001401 |
BEQ M2 |
Переход к концу подпрограммы в случае равенства |
15. |
POKE &O37434,&O1401 |
37436 |
000766 |
BR M1 |
Переход на ввод очередного символа с клавиатуры |
16. |
POKE &O37436,&O766 |
37440 |
012737 |
M2: MOV #M3,@#30 |
Засылка адреса подпрограммы «Обработка» в ячейку с адресом 30. Подмена адреса подпрограммы обработки прерывания ЕМТ |
17. |
POKE &O37440,&O12737 |
37442 |
037450 |
18. |
POKE &O37442,&O37450 |
||
37444 |
000030 |
19. |
POKE &O37444,&O30 |
||
37446 |
000207 |
RTS PC |
Возврат из подпрограммы |
20. |
POKE &O37446,&O207 |
37450 |
011637 |
M3: MOV (SP),@#A+4 |
Запоминание адреса команды, которую нужно выполнить при возврате |
21. |
POKE &O37450,&O11637 |
37452 |
037570 |
22. |
POKE &O37452,&O37570 |
||
37454 |
005337 |
DEC @#A+4 |
из прерывания Уменьшение предыдущего адреса на 2, чтобы определить адрес команды, которая вызвала программное прерывание ЕМТ |
23. |
POKE &O37454,&O5337 |
37456 |
037570 |
24. |
POKE &O37456,&O37570 |
||
37460 |
005337 |
DEC @#A+4 |
25. |
POKE &O37460,&O5337 |
|
37462 |
037570 |
26. |
POKE &O37462,&O37570 |
||
|
|
P=100112 |
Начальное присвоение адреса |
|
|
37464 |
017737 |
MOV @A+4,@#M4 |
Запись команды, которая вызвала прерывание, в программу на тот случай, если эта команда не ЕМТ 6 |
27. |
POKE &O37464,&O17737 |
37466 |
000100 |
28. |
POKE &O37466,&O100 |
||
37470 |
037510 |
29. |
POKE &O37470,&O37510 |
||
37472 |
022737 |
CMP #104006,@#M4 |
Сравнение кода команды, которая вызвала прерывание, с кодом команды ЕМТ 6 |
30. |
POKE &O37472,&O22737 |
37474 |
104006 |
31. |
POKE &O37474,&O104006 |
||
37476 |
037510 |
32. |
POKE &O37476,&O37510 |
||
37500 |
001410 |
BEQ M5 |
Переход на часть подпрограммы, передающей символ из буфера в регистр R0, в случае равенства |
33. |
POKE &O37500,&O1410 |
37502 |
012737 |
MOV #P,@#30 |
Засылка обычного адреса подпрограммы обработки прерывания ЕМТ в ячейку с адресом 30 |
34. |
POKE &O37502,&O12737 |
37504 |
100112 |
35. |
POKE &O37504,&O100112 |
||
37506 |
000030 |
36. |
POKE &O37506,&O30 |
||
37510 |
000000 |
M4: HALT |
На место этой команды записывается команда, вызвавшая прерывание |
37. |
POKE &O37510,&O0 |
37512 |
012737 |
MOV #M3,@#30 |
Повторная подмена адреса подпрограммы обработки прерывания ЕМТ |
38. |
POKE &O37512,&O12737 |
37514 |
037450 |
39. |
POKE &O37514,&O37450 |
||
37516 |
000030 |
40. |
POKE &O37516,&O30 |
||
37520 |
000002 |
RTI |
Возврат из прерывания |
41. |
POKE &O37520,&O2 |
37522 |
117700 |
M5: MOVB @A+2,R0 |
Пересылка байта из буфера в регистр R0 |
42. |
POKE &O37522,&O117700 |
37524 |
000054 |
43. |
POKE &O37524,&O54 |
||
37526 |
005237 |
INC@#A+2 |
Увеличение адреса текущего байта в буфере |
44. |
POKE &O37526,&O5237 |
37530 |
037566 |
45. |
POKE &O37530,&O37566 |
||
37532 |
023737 |
CMP @#A,@#A+2 |
Сравнение адреса последнего байта буфера и текущего адреса в буфере (исчерпание буфера) |
46. |
POKE &O37532,&O23737 |
37534 |
037564 |
47. |
POKE &O37534,&O37564 |
||
37536 |
037566 |
48. |
POKE &O37536,&O37566 |
||
37540 |
001401 |
BEQ M6 |
Если буфер исчерпан, то переход на конец подпрограммы |
49. |
POKE &O37540,&O1401 |
37542 |
000002 |
RTI |
Возврат из прерывания |
50. |
POKE &O37542,&O2 |
37544 |
012737 |
M6: MOV #P,@#30 |
Восстановление обычного адреса подпрограммы обработки программного прерывания ЕМТ в ячейке по адресу 30 |
51. |
POKE &O37544,&O12737 |
37546 |
100112 |
52. |
POKE &O37546,&O100112 |
||
37550 |
000030 |
53. |
POKE &O37550,&O30 |
||
37552 |
000000 |
HALT |
Останов программы |
54. |
POKE &O37552,&O0 |
37554 |
012737 |
MOV #P,@#30 |
Восстановление обычного адреса подпрограммы обработки программного прерывания ЕМТ в ячейке по адресу 30 |
55. |
POKE &O37554,&O12737 |
37556 |
100112 |
56. |
POKE &O37556,&O100112 |
||
37560 |
000030 |
57. |
POKE &O37560,&O30 |
||
37562 |
000207 |
RTS PC |
Возврат из подпрограммы |
58. |
POKE &O37562,&O207 |
|
|
A: HALT |
Начало рабочей области |
|
|
|
|
END |
Конец на ассемблере |
59. |
DEF USR=&O34700 |
|
|
|
Определение функции пользователя |
60. |
A=USR(A) |
|
|
|
Вызов функции пользователя |
|
|