FORUM
Русификация резидентной процедурой
Тема русификации это, пожалуй, вечная тема для "Спектрума". Пока что не существует способ (и будет ли когда-нибудь существовать), который бы был достаточно универсален и хорош для всех. Многие программисты ломают голову над решением этой проблемы. В этот раз интересное письмо пришло к нам от Александра Еременко из Свердловска. Он прислал программу в машинных кодах, позволяющую переключать символьный набор одновременным нажатием двух клавиш (SYMBOL SHIFT)+(ENTER). При первом нажатии включается загруженный в ОЗУ символьный набор, а при повторном нажатии - опять символьный набор из ПЗУ. Самое главное, что такое переключение можно сделать в любой момент как пишет Александр, неважно, где Вы работаете, в режиме редактирования Бейсик-строк или в программе, все равно можно производить переключение шрифта.
Нам понравилась идея уважаемого корреспондента и мы даем ее всем читателям, снабдив собственными комментариями и постаравшись немного ее развить.
Вот эта программа:
CD7C |
3ECB |
LD |
A,#CB |
CD7E |
2120CD |
LD |
HL, #CD20 |
CD81 |
36CD |
LD |
(HL),#CD |
CD83 |
2B |
DEC |
HL |
CD84 |
BC |
CP |
H |
CD85 |
20FA |
JR |
NZ,#CD81 |
CD87 |
3ECC |
LD |
A,#CC |
CD89 |
ED47 |
LD |
I,A |
CD8B |
F3 |
DI |
|
CD8C |
ED5E |
IM |
2 |
CD8E |
FB |
EI |
|
CD8F |
C9 |
RET |
|
CD90 |
C5 |
PUSH |
BC |
CD91 |
D5 |
PUSH |
DE |
CD92 |
E5 |
PUSH |
HL |
CD93 |
F5 |
PUSH |
AF |
CD94 |
3E7F |
LD |
A,#7F |
CD96 |
DBFE |
IN |
A,(#FE) |
CD98 |
FEFD |
CP |
#FD |
CD9A |
202A |
JR |
NZ,#CDC6 |
CD9C |
3EBF |
LD |
A,#BF |
CD9E |
DBFE |
IN |
A,(#FE) |
CDA0 |
FEFE |
CP |
#FE |
CDA2 |
2022 |
JR |
NZ,#CDC6 |
CDA4 |
3A10CD |
LD |
A,(#CD10) |
CDA7 |
3C |
INC |
A |
CDA8 |
3210CD |
LD |
(#CD10),A |
CDAB |
CB47 |
BIT |
0,A |
CDAD |
2805 |
JR |
Z,#CDB4 |
CDAF |
01003C |
LD |
BC,#3C00 |
CDB2 |
1803 |
JR |
#CDB7 |
CDB4 |
01D0CC |
LD |
BC,#CCD0 |
CDB7 |
21365C |
LD |
HL, #5C36 |
CDBA |
71 |
LD |
(HL),C |
CDBB |
23 |
INC |
HL |
CDBC |
70 |
LD |
(HL),B |
CDBD |
219701 |
LD |
HL,#0197 |
CDCO |
116400 |
LD |
DE, #0064 |
CDC3 |
CDB503 |
CALL |
#03B5 |
CDC6 |
F1 |
POP |
AF |
CDC7 |
E1 |
POP |
HL |
CDC8 D1
CDC9 C1
CDCA C33800
CDCD C39OCD
CDD0 ......
POP DE POP BC JP #0038 JP #CD90 символьный набор
Как видим, принцип действия этой программы основан на использовании прерываний второго рода IM 2. Подробнее о прерываниях Вы можете прочитать в нашей книге, посвященной программированию в машинных кодах. Там мы рассказывали, что на использовании прерывания первого рода основан основной режим работы "Спектрума". В этом режиме (если он разрешен командой EI) всегда в ответ на прерывание, поступающее по шине INT (а это происходит 50 раз в секунду), выполняется обращение по адресу 0038H. Процедура, расположенная с этого адреса, обеспечивает сканирование клавиатуры в поисках нажатой клавиши.
Теперь о прерывании второго рода. В компьютерах иных систем, собранных на базе процессора Z-80, для программиста существует возможность самому задать адрес, по которому будет происходить обработка этого прерывания. Он определяется следующим образом. Старший байт поступает из регистра I процессора (он так и называется "вектор прерываний"), а младший байт должно выдавать периферийное устройство, от которого получено прерывание. В паре ячеек, адрес которых определен таким образом, хранится адрес процедуры, обслуживающей прерывание. Программ, обслуживающих такое прерывание может быть 128, так как старший байт определен регистром I, а младший байт -это 256 ячеек, в которых можно записать 128 двухбайтных адресов. В "Спектруме" режим прерываний второго рода не используется для взаимодействия с периферийными устройствами, поэтому появляется возможность использовать этот режим в своих целях.
Теперь конкретно о предложенной программе. Вначале выполняется инициализирующая процедура. Она запускается с адреса загрузки, то есть CD7CH (52604). При этом происходит следующее. Область, начиная с СС00Н, и кончая адресом CD20H заполняется кодом CD. Затем в регистре I процессора задается старший байт адреса ячейки памяти, в которой хранится адрес обслуживающей процедуры CC. Далее происходит включение режима прерываний второго рода и выполняется возврат в вызывающую программу.
Что же происходит при получении сигнала на прерывание, (который, как мы помним, приходит 50 раз в секунду) Несмотря на то что неопределен младший байт ячейки с адресом обслуживающей программы, обратившись по любому адресу со старшим байтом CC (то есть начиная СС00Н и кончая CCFFH), получим один и тот же адрес обслуживавшей программы CDCDH, так как вся эта область заполнена кодом CD.
По адресу CDCDH (см. листинг программы) видим безусловный переход на адрес СD90Н. Это и есть начало программы, обслуживающей полученное прерывание. Перед началом выполнения каких либо действий, на стеке запоминается содержимое всех регистров процессора, которые могут быть изменены при работе обслуживающей процедуры. Затем выполняется сканирование двух полурядов клавиатуры (о том, как производится опрос клавиш, Вы также можете прочитать в нашей книге, посвященной программированию в машинных кодах). Вначале проверяется нажатие клавиши SYMBOL SHIFT и, если она не нажата, то сразу переход на завершение процедуры обработки прерывания. Если SYMBOL SHIFT нажата, то проверяется факт нажатия ENTER. Если нет, то также переход на завершение процедуры, а если и эта клавиша нажата, то происходит дальнейшая работа.
Проверяется содержимое ячейки, определяющей включение одного или другого символьного набора это ячейка CD10H. От значения младшего бита этой ячейки (четное число или нечетное) зависит выбор одного из двух символьных наборов, при этом происходит увеличение на единицу содержимого этой ячейки (то есть подготовка к включению в следующий раз противоположного символьного набора). Соответствующее значение системной переменной CHARS пересылается при помощи регистра ВС, в область системных переменных, в ячейку 5С36Н. Далее происходит вызов подпрограммы BEEPER из
ПЗУ (параметры: тон и длительность звукового сигнала задаются в регистрах HL и DE). Звуковой сигнал свидетельствует о произошедшем переключении символьного набора.
Далее выполняется финишная процедура: восстановление со стека прежних значений регистров процессора и безусловный, переход на адрес 0038H. Как это должно быть в случае обработки прерываний первого рода, с этого места работа компьютера совпадает с традиционной.
При реализации этого метода следует помнить, что символьный набор располагается сразу же за самой программой, с адреса CDD0H (52688). При этом системная переменная CHARS равна CCD0H. Но Вы можете расположить символьной набор там, где это Вам удобно, изменив значение CHARS в ячейках CDB5H и CDB6H. Кроме того, оба переключаемых символьных набора могут находиться в ОЗУ, и Вы можете поочередно переводить на любой из них. Для этого значение системной переменной chars для второго символьного набора должно быть задано в ячейках CDB0H и CDB1H. Следует также помнить, что для работы программы необходима область кодов с адреса СС00Н (52224), то есть перед стартом инициализирующей процедуры (RANDOMIZE U5R 52604) надо зарезервировать это место, изменив RAMTOP командой CLEAR 52223.
Следует также учитывать тот факт, что раз уж мы работаем с прерываниями второго рода, призванными обслуживать периферийные устройства, то работа с последними, например с дисководом, после инициализации блока кодов будет невозможна. Для работы с периферией необходимо восстановить традиционный порядок работы. Об этом еще будет разговор ниже.
Вообще нам очень понравилась идея, заложенная в этой программе и тот изящный способ, при помощи которого эта идея реализуется. Для того, чтобы поглубже "прочувствовать" действие этой программы, наберите простую демонстрационную программку на Бейсике:
1 GO TO 100
2 CLEAR 52223: LOAD "rus"CODE 52604: LOAD "cbr" CODE 52688
3 RANDOMIZE USR 52604
4 RUN
100 PRINT "Press <S.S.>+<ENTER> for control"": PAUSE 0
200 FOR a=32 TO 127; PRINT CHR$ a;: PAUSE 1: NEXT a: GO TO 200
После набора запустите её с первой строки командой RUN и внимательно понаблюдайте за реакцией компьютера. Хотя ничего неожиданного сейчас произойти не должно (компьютер не подпрыгнет, он по-прежнему останется на своем месте), но все-таки остановимся на происходящем подробнее. Сразу после старта на экране появится надпись: "нажмите <S.S.>+<ENTER> для управления" и программа остановится на PAUSE 0, ожидая нажатия клавиши. Попробуйте нажать указанную комбинацию клавиш. Пауза прервется (SYMBOL SHIFT здесь ни при чем, сработало нажатие ENTER) и начнется распечатка символьного набора из ПЗУ. Когда заполнится весь экран, появится привычный запрос: "scroll?". Попробуйте опять нажать SYMBOL SHIFT + ENTER. Вывод символов на экран будет продолжен.
Теперь остановите программу нажатием BREAK и запустите ее со второй строки (с которой должен происходить автостарт) RUN 2. При этом загрузите (заготовленные за ранее) исследуемый блок кодов "rus" и любой имеющийся у Вас символьный набор "chr" (лучше "утолщенный" или стилизованный, так будет нагляднее). После загрузки произойдет инициализация программы в кодах, после чего Бейсик программа запустится с начала, как это делали Вы командой RUN. Сравните реакцию компьютера. После появления надписи в верхней строке, нажмите SYMBOL SHIFT + ENTER. Вы услышите короткий звуковой сигнал, свидетельствующий о нажатии на клавиши, но больше ничего на экране не изменилось: пауза не прервалась, как раньше. То есть, указанная комбинация клавиш теперь не влияет никаким образом на работу Бейсик-программы. Теперь она воспринимается именно как SYMBOL SHIFT + ENTER, а не как просто ENTER. Прервите паузу нажатием на любую клавишу, и Вы увидите, что на экран выводится новый символьный набор, то есть произошло переключение. Пока экран заполняется, можете попробовать несколько раз нажать SYMBOL SHIFT + ENTER и убедиться в том, что происходит переключение символьных наборов. Когда экран будет заполнен и появится запрос "scroll?", опять нажмите комбинацию клавиш. Кроме звукового сигнала, свидетельствующего о переключении символьных наборов, ничего не происходит. Для продолжения работы надо нажать, например, просто ENTER. Можно, таким образом, сделать вывод: приведенный блок кодов не оказывает никакого вмешательства в работу интерпретатора Бейсика, он только исправно выполняет то, что ему предписано - переключает символьные наборы.
Продолжаем наше исследование. Вызовите на редактирование 100 строку Бейсик-программы. Попробуем в кавычках напечатать текст, состоящий из символов разных символьных наборов. Для этого, находясь в середине текста, нажмите SYMBOL SHIFT + ENTER. Звуковой сигнал - переключение произошло, но на экране ничего не меняется до того момента, пока Вы не нажмете какую-нибудь клавишу. А происходит следующее. Весь текст редактируемой строки перерисовывается заново, причем символами нового символьного набора, отсюда второй вывод: нельзя при помощи этого блока кодов решить задачу совмещения символов разный символьных наборов внутри одного оператора PRINT, действительно, чудес ведь не бывает. Кодовый блок только переключает символьный набор, а текст, подлежащий выводу на экран, никак не изменяется, то есть каким было кодовое выражение строки текста, таким оно и осталось, таким и выводится на экран, только в новом символьном наборе. То же относится и к оператору INPUT. Нельзя при вводе текстовых выражений совместить русские и английские буквы.
И еще один вывод. Переключение символьного набора таким способом происходит только "вручную", то есть, если по алгоритму выполнения программы должно произойти переключение символьного набора, то оно должно быть выполнено, как обычно, изменением CHARS при помощи РОКЕ. Так что же, новый изящный метод переключения шрифтов не вносит ничего нового по существу проблемы? Видимо, такое заключение преждевременно. Существуют ситуации, где изложенный способ сможет оказать действительно неоценимую пользу. Например, программа "МОЗАИКА", которая опубликована в этом выпуске ZX-РЕВЮ на с. 1. Там переключение из текстового в графический режим происходит включением регистра CAPS LOCK, из-за чего существует ограниченное число псевдографических символов, только по числу малых букв латинского символьного набора - 26. Если переключать символьные наборы новым способом, то во сколько раз можно расширить графические возможности, а следовательно и привлекательность этой программы! Ведь все до одного 96 символов нового символьного набора можно отдать графическим образам, да и захватить еще часть первого символьного набора, как это сделано в "МОЗАИКЕ".
Другой пример. Вы набираете программу, в которой заведомо будет только русский текст (какой-нибудь тест или обучающая программа). И естественно, хотите для придания программе большей привлекательности, использовать русские заглавные и строчные буквы, используя загружаемый только русский символьный набор. При этом, если Вы набиваете текст программы, используя стандартный символьный набор ПЗУ, то легко можете допустить ошибки при наборе русского текста в операторах PRINT, так как набор ведется практически "вслепую". Если же Вы переключитесь на русский символьный набор, то сама Бейсик-программа, токены, имена переменных - все это станет "нечитаемым". Применение изложенного в этой статье приема позволит вам при наборе и отладке программы без труда переключаться с одного на другой символьные наборы в зависимости от того, что Вы в данный момент предпочитаете видеть.
Мы уверены, что если начать развивать эту тему, то появятся новые оригинальные программы с использованием этого метода. Определенные проблемы для начинающих может вызвать тот факт, что блок кодов нельзя переместить в другое место памяти, но при желании можно это сделать. Мы попробовали поэкспериментировать и у нас получился несколько измененный и немного усовершенствованный вариант этой программы. Вот то,
что у нас получилось.
FBF4 |
3EFB |
LD |
A, #FB |
FBF6 |
ED47 |
LD |
I, A |
FBF8 |
ED5E |
IM |
2 |
FBFA |
C9 |
RET |
|
FBFB |
00 |
NOP |
|
FBFC |
ED56 |
IM |
1 |
FBFE |
C9 |
RET |
|
FBFF |
01FC |
DEFB |
#FTO1 |
FC01 |
C5 |
PUSH |
BC |
FC02 |
D5 |
PUSH |
DE |
FC03 |
E5 |
PUSH |
HL |
FC04 |
F5 |
PUSH |
AF |
FC05 |
3E7F |
LD |
A, #7F |
FC07 |
DBFE |
IN |
A, (#FE) |
FC09 |
FEFD |
CP |
#FD |
FC0B |
202D |
JR |
NZ,#FC3A |
FC0D |
3EBF |
LD |
A, #BF |
FC0F |
DBFE |
IN |
A, (#FE) |
FC11 |
FEFE |
CP |
#FE |
FC13 |
2025 |
JR |
NZ,#FC3A |
FC15 |
2132FC |
LD |
HL,#FC32 |
FC18 |
CB7E |
BIT |
7,(HL) |
FC1A |
2807 |
JR |
Z, #FC23 |
FC1C |
11003C |
LD |
DE, #3C00 |
FC1F |
CBBE |
RES |
7,(HL) |
FC21 |
1805 |
JR |
#FC28 |
FC23 |
1158FB |
LD |
DE,#FB58 |
FC26 |
CBFE |
SET |
7,(HL) |
FC28 |
21365C |
LD |
HL,#5C36 |
FC2B |
73 |
LD |
(HL),E |
FC2C |
23 |
INC |
HL |
FC2D |
72 |
LD |
(HL),D |
FC2E |
210002 |
LD |
HL,#0200 |
FC31 |
117F00 |
LD |
DE, #007F |
FC34 |
CDB503 |
CALL |
#03B5 |
FC37 |
00 |
NOP |
|
FC38 |
00 |
NOP |
|
FC39 |
00 |
NOP |
|
FC3A |
F1 |
POP |
AF |
FC3B |
E1 |
POP |
HL |
FC3C |
D1 |
POP |
DE |
FC3D |
C1 |
POP |
BC |
FC3E |
C33800 |
JP |
#0036 |
Прежде чем начать пояснения, рассмотрим подробно карту памяти, тех областей, которые имеют значение для работы программы.
RAMTOP 64499
Программа в машинных 645 0 0
кодах (новый вариант) FBF4H
77 байт
Свободное место; для ра- 64577 боты программы не исполь FC41H зуется 23 байта
Символьный набор 64600
(любой по вкусу) FC58H
7 68 байт
UDG-графика, стандартно 653 68
расположенная здесь FF58H
168 байт
P_RAMT
Эта область памяти должна быть зарезервирована при старте Бейсик-программы, путем переустановки RAMTOP командой CLEAR 64499. Файл, содержащий блок кодов, начинается с адреса 64500 и включает в себя, в зависимости от конкретных задач, либо только программу в кодах (до адреса 64577), либо программу и символьный набор (заодно с 23-мя неиспользуемыми байтами), либо еще и блок UDG-графики (то есть по адрес 65535 включительно). Область памяти с адреса 64577 (23 байта) в работе программы не участвует, Вы можете использовать ее по своему усмотрению.
Инициализация выполняется командой RANDOMIZE USR 64500. С этого адреса (FBF4H) начинается инициализируемая процедура. Кстати сказать, нет необходимости заполнять почти 300 байт кодом для задания адреса обслуживавшей процедуры. Дело в том, что при существующей архитектуре "Спектрума" младший байт всегда будет равен FF. То есть, если в регистре I процессора задать, например FB, то при прерывании второго рода переход произойдет всегда на программу, адрес которой хранится в FBFFH. В этой паре ячеек (FBFF - младший байт, в FC00 - старший байт) находится адрес процедуры, обслуживающей прерывание второго рода. Читаем этот адрес (см. листинг программы): FC01.
Начало процедуры, расположенной по этому адресу точно такое же, как и в неполном варианте. Так же опрашивается нажатие интересующих клавишей, а дальше начинается отличие. Системной ячейкой, определяющей, какой символьный набор должен включиться, выбрана ячейка FC32H. Точнее, определяющим является 7 бит числа, находящегося там, (получается либо 7F, либо FF). В этой ячейке содержится параметр, задающий время звукового сигнала для подпрограммы BEEPER, вызываемой из ПЗУ. Таким образом, переключение на символьный набор из ОЗУ сопровождается длинным звуком, а обратное переключение на символьный набор из ОЗУ - коротким.
После вызова подпрограммы BEEPER, зарезервировано 3 свободных ячейки памяти. Здесь, при необходимости, можно организовать вызов каких-то дополнительных сервисных процедур, если это потребуется в будущем (вызов какой-нибудь подпрограммы: CALL ... или переход - JP ... - все это занимает 3 байта) далее - финишные операции и переход на адрес 0033H.
Кроме указанных отличий, новый вариант программы предусматривает возможность отключения режима прерываний второго рода, это необходимо делать прежде, чем, например, обращаться к дисководу. Иначе произойдет сбой, зависание или сброс компьютера. "Де-инициализация" или восстановление традиционного режима работы выполняется командой RANDOMIZE USR 64508. С этого адреса (FBFCH) расположена восстанавливавшая процедура. Здесь включается режим прерываний первого рода, восстанавливая исходный режим работы "Спектрума".
В заключение хочется поблагодарить Александра Еременко за присланную программу. Надеемся, что она подтолкнет и других читателей к решению насущных проблем оригинальными, нетрадиционными методами.