ZX-Ревю 1993 №11-12 1992 г.

Читатель - читателю - WHAM! The Music Box. Новые возможности.


ЧИТАТЕЛЬ - ЧИТАТЕЛЮ

Вместо вступления.

Дорогие друзья! Эту объемную статью не хочется предварять утомительным вступлением, но если Вас абсолютно не интересует музыка и, тем более, у Вас нет идей по работа с программой WHAM, мы все же просим Вас внимательно просмотреть статью. Дело в том, что здесь представлена такая гирлянда идей, полезных советов и маленьких хитростей, которая украсит многие программы, даже если они и не имеют отношения к музыке.

"ИНФОРКОМ".

(С) Алексеев А.Г., 1993г.

WHAM! The Music Box. НОВЫЕ ВОЗМОЖНОСТИ

Этот музыкальный редактор наверняка уже знаком читателям "ZX-РЕВЮ". Он помогает не только освоить азы музыкальной грамотности, но и придать особый блеск Вашим собственным программам, тот завершающий штрих, который придает им неповторимый облик.

"WHAM" достаточно широко распространен на рынке программного обеспечения и читатель наверняка имеет ту или иную версию в своем арсенале. Существуют и подробные описания к программе. Более широко развить эту тему вынуждают некоторые тонкости, связанные с ее работой. В частности, неграмотная адаптация под БЕТА-ДИСК многих версий, имеющих хождение на рынке программного обеспечения. В результате этого они неправильно выполняют запись на диск скомпилированного кодового музыкального фрагмента. Предлагаемый материал вносит ясность в этот вопрос. Остановлюсь и на некоторых других особенностях работы этой программы, и главное - довольно существенном ее усовершенствовании.

Надо сказать, что чаще при изложении материала применяется следующий подход: какое-то явление или процедура и как ее можно использовать. Этот материал находится несколько в иной плоскости: конкретная программа и как к ней применить сведения из разных областей, так или иначе имеющие к ней отношение.

Итак, "WHAM". Вот типовой набор файлов, входящих в программу.

"WHAM!" BASIC

"SCREEN$" CODE 16384,6913

"WHAM!1" CODE 65300,235

"WHAM!2" CODE 30000,15000

"WHAM!3" CODE 50000,7000

"M.BOX" BASIC

Первый Бейсик-файл является загрузчиком. Во-первых, он переустанавливает значение RAMTOP=29999. Таким образом, для блоков кодов отводится место с адреса 30000. Во-вторых, именно в этом Бейсик-загрузчике находится команда, переключающая символьный набор на загружаемый утолщенный, стилизованный. Новое значение

системной переменной CHARS задается при помощи POKE:

POKE 23606,43: POKE 23607,217 CHARS=42+256*217=55594

Переключение CHARS происходит в первой же строке Бейсик-загрузчика и является, кроме того, методом защиты. Это приводит к тому, что при нажатии BREAK ничего не видно: сам символьный набор еще не загружен. Это отпугивает начинающих. На самом деле все просто: переключитесь на символьный набор ПЗУ, для этого подайте "вслепую" прямую команду POKE 23606,0: POKE 33607,60 и все встанет на свое место.

Символьный набор расположен с адреса, на 256 большего, чем CHARS: 55594+256=55650. Если у Вас появятся желание заменить его или русифицировать всю программу, то Вы сможете это сделать, расположив новый символьный набор с адреса 55650. При русификации надо также учесть, что значительная доля текстовых сообщений содержится в машинно-кодовой части программы.

Большая часть программы выполняется в машинных кодах, но программа обращается и к Бейсику в определенных случаях. Это загрузка-выгрузка мелодии, компиляция и запись скомпилированного блока кодов. Одной из первоначальных проблем является то, как остановить программу и выйти в Бейсик. Для этого можно в титульном меню нажать "1" или "2" (загрузка или запись). Когда в нижней части экрана программа запросит ввод устройства - это уже работает Бейсик. Нажимайте BREAK для остановки программы. Запустить опять ее можно командой RUN.

Забегая вперед, скажу, что, когда появилась идея дополнить программу некоторыми новыми функциями, встал вопрос, как уместить все эти дополнения в рамках отведенной памяти. Чудес ведь не бывает. Для набивки новых строк необходимо отодвигать RAMTOP в сторону более старших адресов. Поэтому помимо обычных средств по экономии памяти (о которых будет рассказано ниже), пришлось пожертвовать частью буфера для хранения мелодий.

В первоначальном варианте "WHAM" может хранить в памяти 6 мелодий. Новый, усовершенствованный вариант - на одну меньше. Но такая жертва, по-моему, все же оправдана теми дополнительными возможностями, о которых будет рассказано ниже. Поэтому в новом загрузчике в строке 30 устанавливается значение RAMTOP, равное 31999 а не 29999, как в исходном варианте.

Изначальный текст Бейсик-файла "M.BOX" примерно на 80% был переработан. Во избежание неоднозначности, ниже приведен полностью его новый вариант: "m.box". Если Вы захотите воспроизвести программу у себя, внося изменения в старий файл "M.BOX", проверьте, пожалуйста, аккуратно все строки. Обратите внимание на их номера. Во многих случаях это существенно. Поэтому на начальном этапе без необходимости старайтесь ничего не изменять. Когда новая программа у Вас заработает, тогда можете вносить любые дальнейшие изменения.

Изменениям подверглись и кодовые блоки программы для поддержки ее новых возможностей. Подробно все изменения в кодовых блоках приведены в конце статьи. Условимся, что новые файлы, входящие в "WHAM" будут называться: "WHAM+" BASIC "wham $" CODE "Wham 1" CODE "wham 2" CODE "wham 3" CODE "m.box" BASIC

Это нужно для того, чтобы различать новые и старые версии файлов. Теперь некоторые вопросы, связанные с адаптацией под БЕТА-диск.

Универсальный загрузчик.

Адаптация загрузчика под БЕТА-диск тривиальна. Заменяем все LOAD"" на RANDOMIZE USR 15619:REM LOAD "ИМЯ".

Но сейчас я хочу предложить прием, позволяющий реализовать универсальный загрузчик, одинаково работающий и с магнитофоном и с БЕТА-диском. Такой прием можно использовать, в принципе, для любой программы, но для многих - это ненужная избыточность. Однако для таких программ, как "WHAM", которые после адаптации под диск остаются двухрежимными (загрузка и запись сохраняются для ленты и диска) эффектнее выглядит предлагаемый вариант. Он придает большую гибкость программе.

Универсальность основана на том факте, что при использовании БЕТА-диска системная переменная PROG (начало расположения Бейсик-программы) отличается от магнитофонного варианта компьютера на 112 байтов для дополнительных системных переменных. При проверке, если зафиксировано значение PROG=23755, то значит интерфейс Бета-диска отсутствует или неинициализирован. В этом случае программа выполняет загрузку с ленты, Если значение PROG больше, чем 23755, программа переходит на строки загрузки с диска.

Универсальный двухрежимный загрузчик для "WHAM" выглядит так.

10 REM "WHAM!" Universal Tape-Disk loader.

20 BORDER 0: PAPER 0: INK 0: POKE 23624,0: CLEAR 31999

25 IF (PEEK 23635+256*PEEK 23636)=23755 THEN POKE 23739,111:LOAD ""SCREEN$: LOAD ""CODE :

LOAD ""CODE : LOAD ""CODE : GO SUB 100: LOAD "" 30 RANDOMIZE USR 15619: REM : LOAD "wham $"CODE 16384 40 RANDOMIZE USR 15619: REM : LOAD "wham 1"CODE 50 RANDOMIZE USR 15619: REM : LOAD "wham 22"CODE 60 RANDOMIZE USR 15619: REM : LOAD "Wham 3"CODE 70 GO SUB 100

80 RANDOMIZE USR 15619: REM : LOAD "m.box"

100 POKE 23675,88: POKE 23676,255: POKE 23606,42: POKE 23607,217 110 RETURN 9999 SAVE "WHAM+" LINE 10

Подпрограмма GO SUB 100 кроме переключения символьного набора выполняет также переключение символов UDG-графики, восстанавливая исходное значение, которое могло быть изменено дисковым загрузчиком "boot".

Основной файл "m. box".

Как уже говорилось, новый вариант потребовал принятия мер по экономии памяти. Некоторые из них уже реализованы в файле "M.BOX". Это, например, задание вначале программы в 10 строке:

LET O=NOT PI: LET I=SGN PI: LET N7=VAL "7" ...

Буква O похожа на ноль, а буква I - на единицу, так что такая замена практически не сказывается на "читаемости" программы. Кроме того, при помощи переменной N7 задано число 7. Оно часто встречается в программе (например, BORDER N7: PAPER N7:). Замена часто встречающихся чисел переменными позволяет существенно сократить объем программы. Но для БЕТА-диска и всех нововведений этого все же недостаточно. Пришлось бороться буквально за каждый байт. Потребовалось заменить все числа на выражения VAL "число" во всей программе. Только теперь можно быть уверенным, что не произойдет сбоев программы из-за нехватки памяти при работе.

Листинг файла "m.box".

1 REM (C) 1985 MARK TIME LTD (C) 1993 ALANSOFT

2 GO TO VAL "10"

5 GO SUB VAL "90": STOP

6 RANDOMIZE USR VAL "15616"

7 STOP

10 CLS : LET O=NOT PI: LET I=SGN PI: LET N7=VAL "7": BORDER N7: RANDOMIZE USR VAL "50000":

INPUT "": LET T$="" 15 LET STR=VAL "50000": LET LEN=VAL "2000" 20 LET KP=PEEK VAL "23559"-VAL"48" 25 IF KP<O OR KP>N7 THEN RUN

30 PRINT AT VAL "5"+KP,VAL "9" ; OVER I; INVERSE I; " "

35 POKE VAL "23658",VAL "6" 40 IF KP<INT PI THEN GO TO VAL "3000" 45 IF KP=VAL "4" THEN GO TO VAL "8000"

50 CLS : BORDER VAL "5": PRINT AT VAL "8",O;" 1 -Print HELP-PAGE....... 2 -Select MUSIC-KEY

--->...... 3 -Routine RECOMPILER....... 4 -Tune TRANSPONER."

55 RANDOMIZE USR (PEEK VAL "51253"+VAL "56576"): PAUSE 0 60 IF INKEY$="2" THEN GO SUB VAL "600": GO TO VAL "55" 65 IF INKEY$="3" THEN GO TO VAL "9000" 70 IF INKEY$="4" THEN GO TO VAL "5000"

75 IF INKEY$="1" THEN CLS : PRINT : RANDOMIZE USR VAL "55420": PAUSE 0 80 RUN

90 IF PEEK VAL "23635"+VAL "256"*PEEK VAL "23636"=VAL "23755" THEN SAVE "m.box" LINE VAL "2": RETURN

92 RANDOMIZE USR VAL "15619": REM : ERASE "m.box" 94 RANDOMIZE USR VAL "15619": REM : SAVE "m.bOX" LINE 2

99 RETURN

100 IF T$="M" THEN GO SUB VAL "4000": IF KP=I THEN RANDOMIZE USR VAL "54000": GO TO VAL

"140"

105 GO SUB VAL "6000": LET ERR=0 110 IF KP=VAL "2" THEN GO TO VAL "200"

115 IF T$="D" THEN LET ERR=USR VAL "15619": REM : LOAD F$CODE STR,LEN 117 IF ERR<>0 THEN GO TO VAL "300"

120 IF T$="T" THEN CLS : PRINT "START TAPE.....SEARCHING FOR :";F$: IF F$<>" " THEN

LOAD F$CODE STR

130 IF T$="T" AND F$=" " THEN PRINT "Loading any tune - no name given": LOAD ""CODE

STR,LEN

140 IF PEEK STR>VAL "220" THEN POKE VAL "52860",PEEK STR 150 RUN

200 POKE STB,PEEK VAL "52860"

210 IF T$="D" THEN LET ERR=USR VAL "15619": REM : SAVE F$CODE STR,LEN 215 IF ERR<>0 THEN GO TO VAL "300" 220 IF T$="T" THEN SAVE F$CODE STR,LEN

225 IF T$="M" THEN RANDOMIZE USR VAL "54006": FOR A=I TO VAL "10": POKE VAL

"65289"+A+(TN*VAL"10"),CODE F$(A): NEXT A: RUN 250 PRINT #0;"VERIFY ? (Y/N)": BEEP VAL ",1",VAL "20": GO SUB VAL "500": IF NOT F THEN RUN 260 LET ERR=O: PRINT AT VAL "19",O;" Verifying... "

270 IF T$="T" THEN VERIFY F$CODE STR,LEN

280 IF T$="D" THEN LET ERR=USR VAL "15619": REM : VERIFY F$CODE STR,LEN

290 IF ERR=O THEN PRINT #O;TAB VAL "12"; INVERSE I;" O.K. ": BEEP VAL ".1",VAL "20": PAUSE VAL "50": RUN

300 PRINT #0; FLASH I;" ERROR ";ERR;" ": BEEP I,O: PAUSE O: RUN 400 POKE VAL "23658",VAL "8": INPUT I: RETURN

500 POKE VAL "23658",VAL "8": PAUSE O: INPUT ;: IF INKEY$ ="Y" THEN LET F=I: RETURN 510 LET F=0: RETURN

600 IF PEEK VAL "50908"=VAL "159" THEN POKE VAL "50908",VAL "191": POKE VAL "51253",VAL "178": RETURN

610 IF PEEK VAL "50908"=VAL "191" THEN POKE VAL "50908",VAL "159": POKE VAL "51253",VAL

"170": PRINT AT VAL "13",VAL "27";" ";AT VAL "14",VAL "27";" ": RETURN 700 INPUT ;: PRINT #O; FLASH I;" Please, wait... ": RETURN

800 GO SUB VAL "400": PRINT #0;" Select device for ";"LOAD" AND KP=VAL "7";"SAVE" AND

KP=VAL "4";":...... Press: T -TAPE; D -DISK"

805 PAUSE O: INPUT ;: LET T$=INKEY$: IF T$<>"T" AND T$<>"D" THEN GO TO VAL "800" 810 RETURN

3000 PRINT #O;TAB VAL "4"; "SELECT PERIPHERAL DEVICE"

3010 PRINT #0""TAPE MEMORY DISK (SPACE = EXIT) "

3020 PAUSE O: LET T$=INKEY$: IF INKEY$=" " THEN RUN

3030 IF T$<>"M" AND T$<>"T" AND T$<>"D" THEN GO TO VAL "3020"

3040 GO TO VAL "100"

4000 CLS : PRINT .....TUNES STORED IN MEMORY"

4010 LET D=VAL "65300": PRINT ""NO. TITLE:"; 4020 FOR T=I TO VAL "5": PRINT '

4030 FOR D=D TO D+VAL "9": PRINT CHR$ PEEK D;: NEXT D: NEXT T 4040 PRINT #0;"TUNE NUMBER? L"

4050 LET N$=INKEY$: IF N$<"1" OR N$>"5" THEN GO TO VAL "4050" 4060 LET TN=VAL N$: POKE VAL "23681",TN+I: RETURN 5000 CLS : PRINT AT VAL "10", VAL "5"; "Tune Transponierung" 5010 GO SUB VAL "400": PRINT #0; "Press: U -for UP; D -for DOWN" 5020 PAUSE 0: IF INKEY$<>"U" AND INKEY$<>"D" THEN GO TO VAL "5020"

LET R=(INKEY$="U")-(INKEY$="D")

INPUT ;: PRINT #0; " 1 - 1 Channel 2 - 2 Channel 3 - 1 and 2 Channel" PAUSE 0: IF INKEY$<"1" OR INKEY$>"3" THEN GO TO VAL "5050" LET C=VAL INKEY$

INPUT "Tune change (12=octave): ";S LET V=0: GO SUB VAL "700"

IF C<>VAL "2" THEN LET A=VAL "60001": GO SUB VAL "5200" IF C<>I THEN LET A=VAL "61001": GO SUB VAL "5200" IF V=0 THEN LET V=I: GO TO VAL "5110"

INPUT ;: PRINT AT VAL "10",VAL "5"; "Transponierung - O.K.":BEEP VAL ".1",VAL "20": PAUSE 0: RUN

IF PEEK A=VAL "64" OR A=VAL "61000" OR A=VAL "62000" THEN RETURN IF PEEK A>VAL "40" AND PEEK A<VAL "244" THEN LET A=A+I: GO TO VAL "5200" LET T=S*R+PEEK A

IF T>VAL "255" THEN LET T=T-VAL "256" IF T<0 THEN LET T=T+VAL "256"

IF V=0 THEN IF T>VAL "40" AND T<VAL "244" THEN GO TO VAL "5300"

IF V=I THEN POKE A,T

LET A=A+I: GO TO VAL "5200"

INPUT ;: PRINT AT VAL "15",0;" Tune ";("1" AND A<VAL "61000");("2" AND A>VAL

"61000");" Channel is wery ";("high." AND R=I);("low." AND R=-I) PRINT "Transponierung is not available.": BEEP I,0: PAUSE 0: RUN

DIM F$(VAL "10"): INPUT "Press ENTER for CATALOGUE DISK" AND T$="D......FILENAME: ";F$

IF F$=" " AND T$="D" THEN GO TO VAL "6100"

RETURN

RANDOMIZE USR VAL "15619": REM : CAT GO TO VAL "6000"

LET CHN1=VAL "57266": IF WHIND=VAL "3000" THEN LET CHN1=VAL "57464" LET CHN2=CHN1+LEN1+I

LET HL=VAL "60001": LET DE=CHN1: LET BC=LEN1: GO SUB VAL "7500": LET HL=VAL "61001":

LET DE=CHN2: LET BC=LEN2: GO SUB VAL "7500" LET TLENG=CHN2+LEN2-VAL "57000" LET CHN1=CHN1-VAL "57000"+ASEM LET CHN2 = CHN2-VAL "57000"+ASEM PRINT AT VAL "10",VAL "6";"CODE LENGTH : ";TLENG

POKE VAL "57030",INT (CHN1/VAL "256"): POKE VAL "57029",CHN1-VAL "256"*PEEK VAL "57030"

POKE VAL "57002",INT ((CHN1-I)/VAL "256"): POKE VAL "57001",CHN1-I-VAL "256"*PEEK VAL "57002"

POKE VAL "57034",INT (CHN2/VAL "256"): POKE VAL "57033",CHN2-VAL "256"*PEEK VAL "57034" POKE VAL "57008", INT ((CHN2-I)/VAL "256"): POKE VAL "57007",CHN2-I-VAL "256"*PEEK VAL

"57008" RETURN

LET Z=VAL "256": LET H=INT(HL/Z): LET L=HL-H*Z: LET D=INT(DE/Z): LET E=DE-D*Z: LET B=INT(BC/Z): LET C=BC-B*Z: RESTORE VAL "7700": FOR Z=VAL "16416" TO VAL "16428": READ S: POKE Z,S: NEXT Z: RANDOMIZE USR VAL "16416":RETURN DATA VAL "33",L,H,VAL "17",E,D,I,C,B,VAL "237",VAL "176",VAL "201",PI PRINT #0;"RETURN OPTION 1,2 OR 3"

PRINT AT VAL "12",0;"1,KEYPRESS.....2,ALWAYS.....3,TUNE END"

IF INKEY$="1" THEN RETURN

IF INKEY$="2" THEN PRINT AT VAL "4",VAL "20";"ALWAYS ": POKE VAL "57020",VAL "62": RETURN

IF INKEY$="3" THEN POKE VAL "57063",VAL "225": POKE VAL "57064",VAL "225": POKE VAL "57065",VAL "251": POKE VAL "57066",VAL "201": PRINT AT VAL "4",VAL "20";"TUNE END": LET A$=" N/A ": PRINT AT VAL "7",VAL "20";A$;AT VAL "9",VAL "20";A$: RETURN GO TO VAL "7702"

PAPER N7: INK I: BORDER VAL "5": CLS : PRINT INK N7;AT I,0;" "; INK I:

RANDOMIZE USR VAL "55640" POKE VAL "23681",I: LET LEN1=USR VAL "54032"-VAL "60000" PRINT AT VAL "6",VAL "20";LEN1

IF LEN1=VAL "999" THEN PRINT AT VAL "6",VAL "20";"NOT PRESENT" POKE VAL "23681",VAL "2": LET LEN2=USR VAL "54032"-VAL "61000" PRINT AT VAL "8",VAL "20";LEN2

IF LEN2=VAL "999" THEN PRINT AT VAL "8",VAL "20";"NOT PRESENT" LET WHIND=USR VAL "54056"

5030 5040 5050 5060 5070 5100 5110 5120 5130 5140

5200 5210 5220 5230 5240 5250 5260 5270 5300

5310 6000 6010 6020 6100 6110 7000 7010 7020

7030

7031

7032 7040 7050

7055

7060 7065

7080 7500

7700

7701

7702

7703

7704

7706

7707 8000

8110

8115

8116 8120

8125

8126 8130

8133 IF VHIND=VAL "3000" THEN PRINT AT VAL "5",VAL "20";"*PRESENT*"

8140 IF LEN1=VAL "999" OR LEN2=VAL "999" THEN GO TO VAL "8800"

8141 LET SM=LEN1-I: IF LEN2<LEN1 THEN LET SM=LEN2-I

8142 IF (LEN1-I)/SM<>INT ((LEN1-I)/SM) OR (LEN2-I)/SM<>INT ((LEN2-I)/SM) THEN GO TO VAL

"8600"

8150 INPUT "ASSEMBLY ADDRESS: ";ASEM

8160 IF ASEM<VAL "16384" THEN GO TO VAL "8150"

8161 LET TLENG=LEN1+LEN2+VAL "267"+(VAL "198"*(WHIND=VAL "3000")): IF ASEM+TLENG>VAL "65536

THEN PRINT #0;"Error :-no room for tune.": BEEP I,0: LET ASEM=VAL "85536"-TLENG: PRINT AT VAL "21",0;" MAX. ADDRESS: ";ASEM: PAUSE 0: GO TO VAL "8150"

8162 PRINT AT VAL "3",VAL "20";ASEM

8164 POKE VAL "55703",INT (ASEM/VAL "256");POKE VAL "55702",ASEM-VAL "256"*PEEK VAL "55703"

RANDOMIZE USR VAL "55700"

8165 IF LEN1 = LEN2 THEN GO SUB VAL "7700": INPUT "":PRINT AT VAL "12",0"': GO TO VAL "8170"

8166 PRINT #0;"CHANGE RETURN OPTION TO ALWAYS ?": GO SUB VAL "500": IF F THEN POKE VAL

"57020",VAL "62": PRINT AT VAL "4",VAL "20";"ALWAYS " 8170 GO SUB VAL "700": GO SUB VAL "7000": INPUT :

8176 INK 0: PRINT AT VAL "12",0;"ROUTINE COMPILATION COMPLETED OK",,

8177 PRINT '"ADJUSTMENT POKES :-",

8178 PRINT "REPLAY SPEED :";ASEM+VAL "35";",(230 TO 255).....BORDER COLOUR:";JASEM+VAL

"26";",(0 TO 7)"

8179 PRINT "TO RUN - RANDOMIZE USR ";ASEM

8180 POKE VAL "57035",PEEK VAL "52860"-VAL "4": POKE VAL "57026",PEEK VAL "52851"

8205 POKE VAL "56814",INT (ASEM/VAL "256"): POKE VAL "56813",ASEM-VAL "256"*PEEK VAL "56814

8207 POKE VAL "56812",INT (TLENG/VAL "256"): POKE VAL "55811",TLENG-VAL "256"*PEEK VAL

"56812"

8208 POKE VAL "56718",PEEK VAL "56811": POKE VAL "56719",PEEK VAL "56812"

8210 GO SUB VAL "6000": PRINT AT VAL "21",0,,: PRINT AT VAL "2",VAL "8"; INK I;"TUNE NAME : ";F$

8220 FOR A=I TO VAL "10": POKE VAL "56800"+A,CODE (F$(A)): NEXT A 8230 GO SUB VAL "800"

8240 IF T$="T" THEN PRINT #0;"Start tape, then press any key.":PAUSE 0: BORDER N7: INPUT ;:

RANDOMIZE USR VAL "58700": GO TO VAL "8300" 8250 LET ERR=USR VAL "15619": REM : SAVE F$CODE 57000,TLENG 8260 IF ERR<>0 THEN GO TO VAL "300" 8270 RANDOMIZE USR VAL "56728"

8300 LET STR=VAL "57000": LET LEN=TLENG: GO TO VAL "250"

8600 PRINT AT VAL "12",VAL "8";"WARNING.....End marker mismatch.......may cause distorted

tune.......Continue ? (y/n)"

8610 GO SUB VAL "500": IF F THEN GO TO VAL "8145" 8630 RUN

8810 PRINT AT VAL "12",0;"Tune compilation abandoned."

8811 PRINT '"No end markers defined."

8813 PRINT "Use the w key to place the end marker for the current channel." 8820 PAUSE 0: RUN

9000 CLS : PRINT AT VAL "10", VAL "6"; "Routine Recompiling": GO SUB VAL "800" 9010 IF T$="D" THEN GO SUB VAL "6090": LET ERR=USR VAL "15619":REM : LOAD F$ CODE 57000 9020 IF T$="D" THEN LET STR=PEEK VAL "23782"+VAL "256"*PEEK VAL"23783": IF ERR<> THEN GO TO VAL "300"

9030 IF T$="T" THEN LOAD ""CODE VAL "57000": LET STR=PEEK (PEEK 23649+256*PEEK

23650+30)+256*PEEK (PEEK 23649+256*PEEK 23650+31) 9040 PRINT AT 0,0; PAPER VAL "7"; INK VAL "7";" ": GO SUB VAL "700"

9050 RESTORE VAL "9000": FOR A=VAL "16384" TO VAL "16397": READ B: POKE A,B: NEXT A:

RANDOMIZE USR VAL "16384" 9060 DATA VAL "33",VAL "96",VAL "234",VAL "54",VAL "41",VAL "17",VAL "97",VAL "234",SGN

PI,VAL "207",VAL "7",VAL "237",VAL "176",VAL "201" 9070 IF PEEK VAL "57035">VAL "250" THEN POKE VAL "60000",VAL "255" 9080 IF PEEK VAL "57035"<=250 THEN POKE VAL "60000",PEEK VAL "57035"+VAL "4" 9090 POKE VAL "52860",PEEK VAL "60000"

9100 LET A1=VAL "57000"-STR+(PEEK VAL "57001"+VAL "256"*PEEK VAL "57002")+I: LET A2=VAL "60001"

9110 GO SUB VAL "9200": LET M1=A2

9120 LET A1=A1+VAL "2": LET A2=VAL "61001"

9130 GO SUB VAL "9200": LET M2=A

9140 CLS : PRINT AT VAL "10",VAL "7": "Recompiling - O.K.": BEEP VAL ".1",VAL "20"

9150 GO SUB VAL "400": PRINT #0;"Delete markers""Tune end""? (Y/N)": GO SUB VAL "500" 9160 IF F THEN POKE M1,VAL "41": POKE M2,VAL "41" 9170 RUN

9200 POKE A2,PEEK A1

9210 IF PEEK A1<>VAL "64" THEN LET M=A1+I: LET A2=A2+I: GO TO VAL "9200" 9220 RETURN

После загрузки основного Бейсик файла "m.box", происходит старт программы с начальной строки. Несколько начальных строк выполняют вспомогательные функции. RUN5 5 - самозапись файла на ленту или диск (в зависимости от его наличия проверка в строке 90 по PROG выполняется аналогично тому, как это сделано в универсальном загрузчике). Переключение символьного набора - GO SUB 8. Обратное переключение его GO SUB 9.

Фактическое начало программы - строка 10. Титульная заставка прорисовывается при помощи подпрограммы в машинных кодах, расположенной по адресу 50000. Последняя устроена так, что при первом старте после загрузки сначала воспроизводится демонстрационная мелодия, прекращающаяся после нажатия на любую клавишу. Эта мелодия представляет собой скомпилированный фрагмент, расположенный и стартуемый с адреса 42500. При последующих запусках программы командой RUN, мелодия при прорисовке заставки не воспроизводится.

Когда подпрограмма в кодах RANDOMIZE USR 50000 возвращается в Бейсик, в ячейке 23559 сохраняется код нажатой клавиши. Строка 20 возвращает номер нажатой цифровой клавиши в переменную KP и дальнейшее распределение происходит в зависимости от ее значения. Так, строка 40 переводит на подпрограмму записи-считывания, строка 45 - на компиляцию. В том случае, если были нажаты клавиши "5" - "6", возврата в Бейсик вообще не происходит. А вот если нажата клавиша "7", то вместо "HELP PAGE" вызывается дополнительное системное меню: "SYST. MENU" - (соответствующее изменение текста титульного меню произведено в кодовом блоке). В "SYST.MENU" заложены все дополнительные возможности, но об этом чуть позже.

А сейчас более подробно об адаптации файла "m.box" под БЕТА-ДИСК. Изменения будут касаться процедур загрузки и выгрузки. Это пункты "1" и "2" в титульном меню программы. Эти процедуры имеют много общих строк, так что технология адаптации упрощается. В исходном варианте "WHAM" после нажатия клавишей "1" или "2" строка 40 переводит на блок строк с 3000, выполняющий задание устройства ввода-вывода. Внизу экрана появляется новое меню, предлагающее выбрать устройство:

ТАРЕ, MEMORY, DRIVE (SPACE=EXIT)

Так как микродрайвы в нашей стране не слишком распространены, удобно было DRIVE заменить на DISK с изменением всех Бейсик-строк, относящихся к микродрайву. Заменена прежде всего строка, выводящая это меню - 3010.

Строка 3040 завершает выбор устройства и переводит на строку 100. Здесь выполняются дальнейшие действия, связанные с загрузкой-сохранением.

Строка 100 - выполнение подпрограммы записи-считывания мелодии при работе с буфером памяти: GO SUB 4000.

Подпрограмма GO SUB 6000 (строка 105) - это ввод имени файла. Удобно будет при выборе диска предложить пользователю вывести его каталог. Поэтому подпрограмма устроена так, что в случае дисковода, если не вводя имени нажать ENTER, будет выдан каталог диска. Такое решение удобнее, чем дополнительное меню, запрашивающее вывод каталога, так как освобождает оператора от лишних действий. Это, казалось бы, несущественная мелочь, но как раз из таких мелочей складывается впечатление о той или иной программе. Приятно иметь дело с программой, где все продумано до мелочей. Я не пытаюсь представить эту программу как эталон, усовершенствованию нет предела, но Вы можете в своих разработках использовать те моменты и приемы, которые уже хорошо зарекомендовали себя.

В строке 105 кроме ввода имени, задается нулевое начальное значение параметра ERR - кода ошибки TR-DOS. Далее, в строке 10 происходит разделение на процедуры

записи и загрузки. Если запись, то переход на строку 200. Если загрузка, то - на строку 115.

Как Вы знаете, в случае ошибки загрузки с магнитофона, программа остановится с сообщением "Таре loadind error". В случае ошибки при загрузке с диска, мы ничего не заметим. Поэтому необходимо предусмотреть специальные меры по перехвату ошибок TR-DOS. Для этого код ошибки возвращается в переменную ERR для контроля выполнения команды.

Строка 300 должна сигнализировать о невыполненной команде TR-DOS. Здесь применяется упрощенный прием, когда не расшифровывается подробно причина неудачи, а просто выводится код ошибки. Как правило, бывает достаточно даже просто одного сигнала об ошибке, чтобы вспомнить, что, например, файл с таким именем уже есть на дискете. Но лучше, все же, вывести код ошибки ERR. Это не отнимет много памяти, как подробные комментарии на все случаи жизни. Для дальнейшей оценки кода ошибки можно воспользоваться руководством к TR-DOS, но на практике, как правило, в этом не бывает необходимости.

После завершения записи программа предлагает выполнить верификацию. Если в ответ на запрос ввести любую клавишу, кроме "?", то от нее можно отказаться. Верификация реализуется в строках с 250.

Строки загрузки с магнитной ленты и записи на ленту остались без изменения.

Изменения, представленные выше, предназначены для загрузки и выгрузки на диск исходных текстов мелодий. Рассмотрим теперь проблемы, возникающие при компиляции мелодии и записи готового блока кодов, но сначала немного о компиляции. Программа устроена так, что можно скомпилировать музыкальный фрагмент в произвольно заданный адрес. Причем, наблюдательный пользователь наверняка заметил, что выгрузка кодового блока на магнитную ленту происходит необычно на слух: нет привычной паузы между заголовком и самим блоком кодов. Это наводит на мысль, что выгрузка выполняется нестандартной процедурой. Для чего это понадобилось? Выгрузка скомпилированного блока кодов происходит всё время из одного и того же места памяти: из адреса 57000. Хитрость заключается в том, что заголовок файла подменяется другим, в который заносится адрес, заданный Вами при компиляции, а сам массив кодов выгружаются всегда из 57000 с уже изменённой адресацией под заданный Вами адрес. Запись скомпилированного музыкального фрагмента выполняется в строке 8210 при помощи процедуры в кодах 56700. Вот эта программа.

56700: LD IX,56800 . LD DE,00017

. SUB A

. SCF

CALL 1222 . NOP

. LD IX,57000

LD DE,Длина . LD A,255

. SCF

CALL 1222 . EI

56727: RET

Как видим, запись производится в два этапа. Сначала записывается блок кодов, имитирующий заголовок кодового файла. Этот блок расположен с адреса 56800 и имеет стандартную для заголовка длину 17 байтов. При компиляции туда заносятся заданный стартовый адрес и получившаяся длина кодового блока. Затем выполняется вторая часть программы - запись самого блока кодов, как уже говорилось, из адреса 57000 (длина тоже заносится при компиляции).

Самый простой приём при адаптации под диск может быть следующий. Запись выполнить при помощи Бейсика, обычной командой TR-DOS, из адреса 57000. Это выполняют строки 8230 ... 8260.

Естественно, в этом случае в заголовках блоков, скомпилированных в разные адреса, будет всегда стартовый адрес 57000. Загружать записанный таким образом скомпилированный блок надо, обязательно указывая адрес загрузки, тот который Вы задали при компиляции. Поэтому, если Вы завтра забудете адрес, для которого сегодня скомпилировали мелодию, то, скорее всего, Вы уже никогда не сумеете воспользоваться этим блоком кодов.

Поэтому, для устранения этого недостатка, процедура записи дополняется небольшой программой в машинных кодах, которая, будучи выполненной сразу же после записи файла, "подменяет" заголовок на диске на новый, с заданным Вами стартовым адресом.

Расположена эта программа следом за процедурой записи, с адреса 56728 (#DD96):

#DD98

0E0A

LD

C, #0A

#DD9A

CD133D

CALL

#3D13

#DD9D

79

LD

A, C

#DD9E

2AEDDD

LD

HL,(#DDED)

#DDA1

22E65C

LD

(#5CE6),HL

#DDA4

0E09

LD

C, #09

#DDA6

CD133D

CALL

#3D13

#DDA9

C9

RET

Для того, чтобы подробнее разобраться, как она действует, откройте ZX-РЕВЮ № 1-2 за этот год на стр. 25 и вспомните, как выполняются программы TR-DOS из машинного кода: в регистр C заносится код команды и вызывается подпрограмма 15636 (3D13H).

Программа работает так. Вначале в регистр C заносится код 10, что соответствует процедуре поиска файла в каталоге диска. Параметры для поиска - спецификация файла, то есть его имя и тип. Эти данные уже заданы - они остались в системных ячейках после записи файла. После завершения поиска, результат будет в регистре C. Следующей будет выполнена процедура записи 16 байтов заголовка с изменённым параметром START на место старого. В регистре A задаётся номер заголовка (из регистра С), согласно требованиям к последующей команде при C=9 - записи заголовка файла. Теперь надо то значение адреса, которое было задано при компиляции, занести (в двухбайтном виде) в область спецификации файла в таблице системных переменных TR-DOS, а именно, в ячейки 23782, 23783 (5СЕ6Н, 5СЕ7Н). Для этого вспомним, что стартовый адрес уже занесён при компиляции в 17-байтный заголовок магнитофонного файла. Два его байта находятся по адресу 56813 (DDEDH). Поэтому берём его оттуда и при помощи регистра HL переписываем в системные переменные TR-DOS. Далее осталось только выполнять подпрограмму 15635 (3D13H) с параметром C=9. Теперь заголовок переписан с тем значением адреса, которое было задано при компиляции и проблем с адресом запуска больше не будет. Практически, при записи такой процедурой, процесс записи внешне никак не отличается от традиционного.

Рассмотренная процедура выполняется в строке 8270. Строка 8300 задаёт параметры: стартовый адрес и длину для верификации. Последняя осуществляется блоком строк, начиная с 250.

Надо сказать, что показанный приём может применяться и в других Ваших программах. Блок кодов, переписывающий заголовок, может использоваться, например, при желании наоборот, засекретить истинное значение стартового адреса или с иными целями. Сама записывающая процедура может работать в любом месте памяти.

Как "бороться" с COPY.

От некоторых пользователей, в основном, начинающих, можно услышать претензии по поводу того, что после нажатия "7" в титульном меню, нормально выводится на экран страничка - подсказка ("HELP - PAGE"), но после этого "программа зависает". HELP - PAGE выводится при помощи блока в кодах, значит в нём причина "зависания"? Тогда устранить её будет сложно! Но на самом деле всё очень просто.

"Зависание" происходит на команде COPY (расположенной в старом варианте "WHAM" в строке 50 Бейсик - программы) на тех компьютерах, где имеется интерфейс для принтера LPRINT - 3 с собственным теневым ПЗУ. Оно активизируется, когда компьютер пытается выполнить команду COPY. Но он не может её выполнить, так как предварительно должен быть задан режим для получения графической копии экрана командами:

LPRINT CHR$ 0: PRINT CHR$ 1

LPRINT CHR$ 0: PRINT CHR$ 6

Выходов из этого положения два: сложный и простой. Сначала - сложный (но он пока не реализован в Бейсик - файле "m. box"). Для этого надо организовать команду, инициализирующую интерфейс принтера в одной из начальных строк, чтобы она выполнялась при старте программы. Причём команда эта по возможности должна сама определять, инициализирован интерфейс принтера или нет. Вот так это можно выполнить для LPRINT-3:

IF PEEK VAL "23296"<>VAL "95" THEN LPRINT: LPRINT CHR$ NOT PI; CHR$ VAL "5"

GO TO VAL "10"

Контроль (упрощённый) того, что интерфейс инициализирован, выполняется по наличию в буфере принтера (с адреса 23296) драйвера печати, перебрасываемого туда из теневого ПЗУ при инициализации интерфейса LPRINT-3.

Но надо сказать, что начальная инициализация поможет правильно распечатать HELP - PAGE на принтере, но не поможет "бороться с COPY', если у Вас выключен или отсоединён принтер. Ну действительно, зачем всё время выполнять графическую копию экрана, может Вы хотите просто посмотреть страничку - подсказку и ничего больше. Можно, конечно, организовать запрос, например, "НАЖМИТЕ [P] ДЛЯ ПЕЧАТИ НА ПРИНТЕРЕ". Но гораздо изящнее всё это выглядит, когда выполняется автоматически. Помните о мелочах!

Попробуйте так:

75 IF INKEY$="1" THEN CLS: PRINT: RANDOMIZE USR VAL "55420"

76 IF kp=N7 THEN BORDER N7: PAPER N7: INK 0: CLS: PRINT: RANDOMIZE USR VAL "55420"

77 IF IN 123<>255 THEN COPY

78 PAUSE 0: RUN

Команда RANDOMIZE USR 55420 в строке 75 (в старом файле "M. BOX" это происходит в строке 50) выводит на экране HELP - PAGE, после чего происходит контроль, включён ли принтер и готов ли он к печати. Если готовность принтера есть, тогда IN 123 даст значение 127 (выключится 7 бит) и будет выполнена распечатка графической копии экрана. Таким образом, если Ваш принтер выключен, то строка 77 никак не будет влиять на работу программы, но как только Вам понадобится "твёрдая копия", достаточно будет всего лишь включить принтер.

Но даже такой "мудрый" подход к команде COPY проблему решает только отчасти, так как невозможно предусмотреть заранее все многообразие интерфейсов принтеров, имеющих хождение в стране. Не у всех же LPRINT-3. Да и вряд ли имеет смысл тратить много сил и компьютерной памяти для решения проблемы в рамках этой программы. Ну в самом деле? Это же не текстовый редактор, где печать является определяющим параметром. Поэтому, предложу теперь второй выход - простой, но подходящий на все случаи жизни: вообще исключить команду COPY из программы. Она (программа) не сильно пострадает от этого. Кстати, COPY встречается в программе ещё в одном месте: в старом варианте "WHAM" это строка 8210. Там по замыслу авторов должны выдаваться на печать параметры скомпилированного музыкального фрагмента. COPY надо удалить и в этой строке, ограничившись выводом данных только на экран.

Компиляция.

Теперь поговорим подробнее о самой компиляции. В принципе, при компиляции возможно задание любого адреса. Но определённые ограничения вносит строка 8160. В старом варианте "WHAM" она запрещает задавать адрес меньше 32768. но если Вы захотите скомпилировать мелодию, скажем, для размещения её в нулевой Бейсик-строке или в дисплейном файле, то Вы не сможете это сделать. Для чего авторами программы внесено такое ограничение, наверное, уже никогда не узнать. Но Вы можете устранить этот дефект простой заменой числа в строке 8160.

Кроме того, наверное ограничение накладывает строка 8161 старого "M. BOX". Если мелодия расположена близко к концу памяти (65535), то заданный адрес может оказаться недоступен, хотя при полученной реальной длине Вы можете рассчитать, что скомпилированный блок кодов поместился бы до конца памяти. Для устранения этого надо также изменить числовые параметры в строке 8161 (см. листинг "m. box").

Теперь будет возможно разместить мелодию в любом месте ОЗУ, и только адреса ПЗУ для компиляции будут недоступны. Причём, если задать заведомо большой адрес, например 65500, то программа после вывода сообщения о недопустимой длине, сама рассчитает и предложит Вам такое предельное значение адреса, которое позволит Вам расположить скомпилированный блок вплотную к концу ОЗУ (это опять, казалось бы, мелочь, но как приятно работать с такой дружелюбной программой!).

Ещё один момент, связанный с компиляцией, на который хочется обратить внимание пользователя. В строке 7500 старого варианта "WHAM" есть такой фрагмент:

... FOR Z=VAL "23296" TO VAL "23308": READ S: POKE Z, S: NEXT Z: RANDOMIZE USR VAL "23296" ...

Здесь создаётся вспомогательный кодовый блок, необходимый для компиляции.

С одной стороны мы говорили о корректной работе интерфейса принтера, а с другой -в драйвер печати, находящийся в буфере принтера, вносятся изменения, разрушающие его. Если Вы решите убрать команду COPY из программы и отказаться от вывода на печать, то не произойдёт ничего плохого. Но если Вы всё же сохраните печать (как это было показано выше для интерфейса LPRINT-3), то приведённый фрагмент будет систематически нарушать процесс печати. Вспомним, что печать (COPY) используется при компиляции, как раз после того, как содержимое буфера принтера испорчено приведённым выше фрагментом.

Хотя в предлагаемом варианте "WHAM" команда COPY исключена, всё же зарезервирована возможность для дальнейшего усовершенствования, пока не реализованного - распечатки полного текста на принтере. Поэтому для вспомогательного кодового фрагмента найдено другое место. Так как его работа временная, то есть возможность расположить его в экранной памяти, прямо в дисплейном файле, на том месте, где нет никакой информации. А для того, чтобы не "портить" изображение, надо предварительно "замаскировать" его, напечатав на этом месте пробелы с одинаковым белым цветом INK и PAPER, включив эту печать, например, в строку 8000. Здесь печатается 13 пробелов для "маскировки" блока кодов, который будет создан на этом месте строкой 7500. В последней - соответствующим образом изменены значения адресов.

Теперь вспомогательный блок кодов создаётся прямо на экране, но для пользователя это происходит совершенно незаметно.

Другим недостатком процесса компиляции является такой момент. После завершения её, на экран выводятся комментарии по поводу ячеек, в которых можно задать темп мелодии и цвет бордюра. При запуске скомпилированного блока кодов Вы увидите, что и темп и цвет бордюра не совпадает с тем, что были заданы Вами в редакторе перед компиляцией. Поэтому придётся ещё неоднократно вносить изменения в кодовый блок, добиваясь требуемого темпа воспроизведения.

Этот недостаток можно легко устранить, если процесс компиляции дополнить специальной Бейсик - строкой. Параметры, темп и цвет бордюра хранятся в памяти: темп - в ячейке 52860, а цвет бордюра - в 52851

и достаточно всего лишь перенести их в скомпилированный блок кодов. Но при этом надо учесть один момент. Перед занесением параметра, определяющего темп, надо внести определённые коррективы из-за разницы во времени работы воспроизводящих процедур. Экспериментальным путём подобрана величина смещения - заносится значение на 4 меньше, чем в ячейке 52850 (строка 8080).

Теперь после завершения компиляции Вам не потребуется вносить изменения - блок сразу же готов к работе.

Кодировка мелодии.

Исходный (нескомпилированный) текст мелодии, которая в данный момент редактируется, располагается с адреса 60000. Это - рабочая область. Общая длина массива - 2000 байтов. При сохранении мелодии в памяти этот массив перебрасывается в буфер, находящийся в старом "WHAM" с адреса 30000 длиной 12 Кб, а в новом - с 32000, длиной 10 Кб. Рабочая область с 60000 поделена на две равные части по 1Кб. С адреса 60000 расположены коды первого канала, с адреса 61000 - второго канала. Изначально область, где расположен текст мелодии, заполнена кодом 41. Этот код для текста мелодии означает отсутствие ноты или "пауза" - то есть то же, что "пробел" для обычного текста.

В ячейке 60000 находится код, определяющий скорость воспроизведения мелодии. Коды вводимых нот будут располагаться начиная с адреса 60001. Для второго канала - с 61001.

Для обозначения нот используются коды (в порядке повышения тона) с 244 по 255 и далее с 0 по 40. Самая низкая нота - "до" 1-ой октавы имеет код 244. Следующая за ней "до -диез" 1-ой октавы - код 245 и т. д. Код 255 соответствует ноте "си" 1-ой октавы. Следующая нота - "до" 2-ой октавы имеет код 0. А самая высокая нота - "ми" 5 октавы (клавиша SYMB. SH. в режиме 4 октавы) имеет код 40.

Коды с 42 по 239 для нот не используется, они соответствуют тем звуковым эффектам, которые можно реализовать в программе: имитируют звуки барабана, ударника, и т.д. Вставляя шумовые эффекты в мелодию, надо учитывать, что при этом в память заносятся два числа: в области памяти для 1 и для 2 каналов. Пара этих чисел определяет звучание эффекта. Этим объясняется их многообразие. Кстати, здесь кроется причина того, что поставив музыкальный эффект в каком-то месте, пользователь с удивлением обнаруживает, что удалить его он не может. Для нот удаление выполняется клавишей ENTER (при этом в память заносится код 41). Для звуковых эффектов это не проходит, так как эффект определяется не одним числом, как нота, а двумя. Поэтому для того, чтобы удалить эффект, надо нажать ENTER в одном канале, затем вернуться на 1 шаг назад (клавиша "0"), переключить канал клавишей "Т" и еще раз нажать ENTER, только тогда звуковой эффект будет стерт.

При завершении мелодии, перед тем, как ее скомпилировать, необходимо "зациклить" ее, установив в обоих каналах маркер "конец мелодии". Кстати, он может быть различным для каждого канала, например, 1-ый канал является солирующим и имеет максимальную длину, а 2-ой канал, сопровождая мелодию, все время повторяет одну и ту же короткую последовательность нот, с периодом в несколько тактов.

"Концу мелодии" соответствует код 64. Он ставится клавишей "W". Не все знают о том, что маркер "конец мелодии" легко можно удалить. Это выполняется так. Надо подойти к концу и остановиться на самом последнем такте мелодии, затем нажать ENTER. При этом в память будет занесена "пауза" на то место, где был маркер "конец мелодии". Код 64 просто заменится кодом 41. Это выполняется в каждом канале отдельно.

Теперь мы подошли к наиболее интересной части, о которой говорилось выше.

Дополнительные функции "WHAM".

Они реализованы через дополнительное системное меню "SYST.MENU", вход в которое происходит теперь из титульного меню при нажатии клавиши "7" (в старом "WHAM" - это соответствовало "HELP PAGE"). При нажатии "7" программа попадает на строку 50. Вид системного меню представлен на рис 1.

При нажатии "1" выводится страничка-подсказка, аналогично тому, как это выполнялось в старом "WHAM". При нажатии "2" происходит смена нотного ключа на нижнем нотном стане. Этот момент требует комментария.

Нотный стан.

Динамический диапазон "WHAM" охватывает четыре полных октавы с первой по четвертую (и частично захватывает пятую). Эти октавы относятся к скрипичным, басовые октавы в "WHAM" не используются.

Тем не менее, верхний нотный стан - верхние пять линеек - предназначен для обозначения нот в скрипичном ключе, а нижний - в басовом. Поэтому Вы можете заметить, что, например, нота "соль" 3-ей октавы, находящаяся на предпоследней линии верхнего нотного стана, не соответствует таким образом ноте "соль", для 1 октавы, находящейся на последней линейке на низшем нотном стане. Но учитывая то, что нижний нотный стан представлен в басовом ключе, все выглядит верно.

Это может оказаться неудобным если, например, надо набрать с нот мелодию, написанную так, что и верхний и нижний нотные станы находятся в скрипичном ключе. Поэтому добавлена новая функция - переключение нотного ключа на нижнем нотном стане. Теперь при переходе к редактированию мелодии (например, "6" в титульном меню), на экран выводится изображение нотных ключей. Нижний нотный стан формируется теперь тоже в соответствии с нотным ключом: в зависимости от последнего, с разницей в одну линейку. Это поясняет рисунок 2.

Переключение выполняется при помощи подпрограммы GO SUB 600. Формирование изображения нотных ключей производится подпрограммой в машинных кодах, выполняемой в строке 55.

В соответствии с типом нижнего нотного ключа, вход в подпрограмму печати ключей должен происходить с разных точек входа: для басового ключа - 56746 (DDAAH), для скрипичного - 56754 (DDB2H).

Для изображения ключей применен специальный символьный набор, а точнее, 23 дополнительных символа, соответствующие кодам с 33 по 55 включительно (символы с "!" по "7"). Их образы расположены на свободном месте с адреса 56816 по 56999 включительно. Изображение скрипичного ключа, например, складывается из 6 строк по 2 знакоместа. "Текст", изображающий нотные ключи, состоит из трех фрагментов, расположенных по адресам 65184 (FEA0H), 65218 (FEC2H) и 65264 (FEF0H), то есть по адрес 65299 и таким образом в новом варианте включен в файл "Wham 1"CODE.

Подпрограмма печати нотных ключей переключает CHARS на этот новый символьный набор, производит печать нотных ключей, выводя на экран строку текста, "вырисовывающую" нотный ключ, а затем возвращает прежнее значение CHARS.

Эта процедура расположена с адреса 56746 (DDAAH), следом за приведенной выше процедурой записи на диск заголовка файла.

DDAA

11A0FE

LD

DE,#FEA0

DDAD

012200

LD

BC,#0022

DDB0

1806

JR

#DDB8

DDB2

11C2FE

LD

DE,$FEC2

DDB5

012E00

LD

BC,#002E

DDB8

2A365C

LD

HL,(#5C36)

DDBB

E5

PUSH

HL

DDBC

21E8DC

LD

HL,#DCE8

DDBF

22365C

LD

(#5C36),HL

DDC2

CD3C20

CALL

#203C

DDC5

11F0FE

LD

DE,#FEF0

DDC8

012400

LD

BC,#0024

DDCB

CD3C20

CALL

#203C

DDCE

E1

POP

HL

DDCF

22365C

LD

(#5C36),HL

DDD2

C9

RET

Подпрограмма на Бейсике GO SUB 600 в зависимости от типа ключа вносит изменения в формирователь нижнего нотного стана: для скрипичного ключа нотные линейки должны быть сдвинуты на 1 линию вниз по сравнению с басовым. Кодовый блок программы корректируется при помощи РОКЕ.

Рекомпиляции.

Это очень мощная новая возможность программы "WHAM". Но сначала некоторые дополнительные сведения о компиляции.

При компиляции мелодии происходит следующее. Сначала подготавливается воспроизводящая процедура. Надо сказать, что ее длина непостоянна и зависит от исходного текста мелодии, наличия в ней шумовых эффектов. Звучание нот в обоих каналах определяется "нотной" процедурой, а шумового эффекта - "шумовой" процедурой. Если шумовые эффекты отсутствуют, длина воспроизводящей только "нотной" процедуры будет равна 267 байт. Если в мелодии используется хоть один шумовой эффект, то длина воспроизводящего блока увеличивается на 198 байт - столько занимает "шумовая" процедура.

Затем к воспроизводящей процедуре пристыковывается нотная последовательность -берется фактическая длина исходного текста мелодии, отмеченная в рабочей области маркером "конец мелодии" для каждого голоса, формируется скомпилированный блок, как Вы уже знаете, с адреса 57000. В полученном блоке кодов изменяется адресация машиннокодовых команд переходов с абсолютной адресацией. Так выполняется "привязка" к тому адресу, который Вы задали при компиляции. Получается фрагмент кодов, состоящий из двух частей: "воспроизводящая процедура" и "нотная последовательность". Первая же команда воспроизводящей процедуры имеет вид: LD HL,ADDR

где ADDR - адрес, с которого начинается в памяти нотная последовательность.

Исходя из этих данных, сделана подпрограмма, которая выполняет операцию, обратную компиляции, то есть выделяет из скомпилированного блока кодов исходный текст мелодии. Это очень часто нужно на практике. Например, Вы озвучили свою программу мелодией собственного сочинения, А потом захотели внести какие-то изменения в эту мелодию. Если Вы не сохранили исходный текст мелодии, то для выполнения этой задачи Вам пришлось бы набирать всю мелодию заново. Наличие подпрограммы рекомпиляции позволит Вам не сохранять все исходные тексты мелодий, а в любой момент получить из скомпилированного блока кодов полноценный исходный текст, доступный для дальнейшего

редактирования.

Вызов подпрограммы рекомпиляции происходит при нажатии "3" в системном меню. При этом программа переходит на строку 9000. После выбора устройства (магнитофон или дисковод) происходит загрузка скомпилированного блока кодов. Она выполняется в адрес 57000, то есть туда, где как раз готовился к выгрузке скомпилированный блок.

Теперь необходимо определить стартовый адрес STR, для которого блок был скомпилирован. В случае диска, эти данные можно взять из системных переменных TR-DOS, где они остались после загрузки (строка 9020). В случае загрузки с магнитофона, стартовый адрес можно прочитать в заголовке загружаемого файла. Он (заголовок) считывается во временную рабочую область памяти. Сразу же после завершения загрузки, пока временная рабочая область еще не уничтожена, его можно прочитать при помощи РЕЕК. Это выполняет строка 9030. В этой строке не используется прием по сокращению памяти типа VAL "число", так как рассчет значения числа приводит к изменениям во временной рабочей области и преждевременному разрушению информации о считанном заголовке.

Строки 9040... 9050 при помощи вспомогательного блока кодов, формируемого в дисплейном файле, производят очистку области памяти с 60000 длиной 2000 - где располагается текущая мелодия. Сюда будет выполняться рекомпиляция. Строки 9070-9090 выполняют восстановление темпа воспроизведения. Правда, надо опять, как при компиляции, учитывать разницу во время работы машиннокодовых процедур, воспроизводящих мелодию.

Строка 9100 рассчитывает адрес, с которого располагается нотная последовательность. Коды, расположенные с этого адреса, надо переписать в рабочую область с адреса 60001. Это выполняется при помощи подпрограммы GO SUB 9200. Если встречается код 64, соответствующий маркеру "конец мелодии", подпрограмма 9200 заканчивает работу. Адрес расположения маркера "конец мелодии" запоминается в переменной M1 (строка 9110). Строки 9120-9130 выполняют аналогичные действия для второго канала. Адрес маркера конца - в переменной M2.

После звукового сигнала, свидетельствующего о завершении рекомпиляции (строка 9140), в строке 9150 следует запрос: надо ли уничтожить маркеры "конец мелодии". Это удобно, если Вы собираетесь продолжить мелодию, сделать ее длиннее, чем она была. В этом случае нажмите клавишу "Y", иначе - любую другую (если Вам надо всего лишь скорректировать некоторые ноты, то маркеры удалять незачем).

На этом подпрограмма рекомпиляции заканчивает свою работу и происходит возврат к титульному меню. Теперь восстановленный исходный текст мелодии находится в рабочей области. Его можно обрабатывать дальше, как обычно: редактировать, прослушать, сохранить и т.д.

Транспонирование мелодии.

В эту подпрограмму можно попасть при нажатии "4" в системном меню. Это очень удобный режим, если надо изменить тональность или перезадать мелодию для другой октавы.

Программа транспонирования расположена со строки 5000. Прежде всего необходимо ответить на запрос, в какую сторону требуется изменить тональность: вниз или вверх. Это реализуется в строках 5010...5030.

Следующий запрос (строки 5040-5060) - для какого канала выполнить транспонирование: 1 - только для первого; 2 - только для второго; 3 - для обоих каналов.

В строке 5070 идет последний запрос: на какую величину произвести смещение. Минимальная величина - 1 тон. Если надо изменить тональность на 1 октаву, то величина смещения должна быть равна 12.

Работа начинается в строке 5100 и выполняется в два этапа: вначале - проверка, затем - собственно замена кодов. В строке 5100 обнуляется флаг режима - переменная V. Если V=0, то проверка, если V=1, то замена кодов. При V=0 происходит предварительный просмотр всего текста мелодии от начала до конца - возможно ли вообще осуществить транспонирование - не выйдет ли результирующая мелодия за пределы динамического диапазона "WHAM". Это выполняется подпрограммой GO SUB 5200 для двух каналов -соответственно в областях памяти с адреса 60001 и с 61001 в зависимости от заданных каналов (строки 5110-5180). Если в результате проверки выясняется, что должен быть получен тон, недоступный для "WHAM", слишком высокий или слишком низкий, то процесс проверки прерывается с выдачей сообщения о невозможности транспонирования. Это выполняется переходом на строку 5300. После чего - возврат к титульному меню. А если проверка доведена до конца, то в строке 5130 устанавливается флаг V=1, что означает "транспонирование возможно". Можно переходить к замене кодов. Это выполняется путем возврата на строку 5110. Здесь процесс вызова подпрограммы GO SUB 5200 повторяется вторым циклом, но уже со значением флага V=1. В этом случае строка 5260 производит замену кодов. Выход из подпрограммы 5200 по достижении маркера "конец мелодии" или, при его отсутствии, по достижении последнего адреса рабочей области.

Строка 5140 подводит итог транспонирования, после чего - возврат в титульное меню. Дальше - обычная работа с полученной мелодией.

Надо сказать, что работа подпрограмм рекомпиляции и транспонирования, организованная в Бейсике, не отличается быстродействием, так что, выполняя их, наберитесь терпения. "Неторопливость" подпрограмм усугубляется внесением элементов экономии памяти: VAL "число" и заменой чисел переменными. Но учитывая, что этими возможностями не каждую минуту придется пользоваться, с этим можно смириться. Лучше было бы, конечно, построить такие процедуры в машинных кодах, тем более, что это не так уж сложно и вполне доступно начинающим. Попробуйте, если хотите, заменить Бейсик машинным кодом. Я же - ограничусь идеей.

Сокращение программы.

Как известно, после загрузки программы мы имеем в памяти шесть демонстрационных мелодий. Кроме того, непосредственно в памяти может находиться седьмая. Это удобно, когда Вы в первый раз загружаете программу - можно послушать их, посмотреть, как задаются ноты и эффекты. Но когда Вы освоитесь и будете заниматься самостоятельным набором мелодий для оформления своих собственных программ, демонстрационные мелодии не понадобятся. Вы все равно будете удалять их, записывая в память на их место свои заготовки. Каждая мелодия занимает в памяти по 2 Кб. Исключив их из загрузки, можно сократить программу на 12 Кб. А если еще "выбросить" цветную заставку (около 7 Кб) то получится ощутимая экономия.

Владельцы магнитофонных версий, представьте, во что выльется сокращение времени при каждой загрузке! Здесь речь идет не об урезании программы вообще, а о "персональной копии" - специальной версии, предназначенной для быстрой загрузки. И не об удалении демонстрационных мелодий вообще, их надо сохранить, но отдельно, на ленте (или дискете), чтобы всегда можно было при желании загрузить опять. Для этого надо каждую мелодию сначала загрузить из буферной памяти в рабочую область редактора, а затем сохранить на ленте (или диске). После того, как демонстрационные мелодии надежно сохранены, можно приступить к модификации кодового блока "WHAM!2" CODE. Начиная с адреса 30000, длиной по 2 Кб, здесь расположено 6 мелодий. Итого - по адрес 41999 включительно. Можно сделать так: LOAD "WHAM!2"CODE 30000,15000 SAVE "wham 2"CODE 42000,3000

Осталось только дополнить загрузчик небольшой подпрограммой, которая будет инициализировать область с 32000 (в старом варианте это надо было делать с 30000), заполняя ее кодом 41 -"пауза". Вот эта подпрограммка: LD HL,32000 LD (HL),041 LD DE,32001 LD BC,09999 LDIR RET

Для ее формирования и запуска универсальный Бейсик-загрузчик "WHAM", приведенный в начале статьи, надо дополнить следующими строками:

102 FOR a=20480 TO 20493: READ b: POKE a,b: NEXT a 104 RANDOMIZE USR 20480

106 DATA 33,0,125,54,41,17,1,125,1,15,39,237,176,201

Кроме того, надо очистить (а точнее, заполнить пробелами или, например, словом: "EMPTY") ту область, где хранятся названия демонстрационных мелодий. Это область с адреса 65300, по 10 байтов на каждое название. Эти исправления надо сделать в файле "WHAM!3"CODE.

Для устранения загрузки картинки, из загрузчика можно удалять строку 30 и из строки 25 исключить:

... LOAD ""SCREEN $: ...

и перекомпоновать заново файлы для магнитофонного варианта.

Теперь, когда описание программы и всех ее новых функций завершено, переходим к тем изменениям, которые надо выполнить в кодовых блоках.

Изменение кодовых блоков.

Ниже приводится программа на Бейсике, выполняющая все необходимые изменения в кодовых блоках "WHAM!1"CODE и "WHAM!2"CODE. По завершении она выдает новые файлы "wham 1"CODE и "wham 3"CODE.

Программа предназначена для работы с магнитофоном. Владельцы "Спектрумов" с БЕТА-диском, наверняка сами сумеют внести необходимые коррективы.

10 CLEAR 31999: POKE 23675,88: POKE 23676,255 20 LOAD "WHAM!1"CODE 30 LOAD "WHAM!3"CODE

40 POKE 50908,159: POKE 50909,79: POKE 50921,223: POKE 50922,71 50 POKE 51248,0: POKE 51253,170: POKE 51254,221 60 FOR A=51374 TO 51406: READ B: POKE A,B: NEXT A

70 DATA 022,017,001,127,049,057,056,053,032,077,065,082,075,032,084,073,077,069,043,127,049,

057,057,051,032,065,076,065,078,083,079,070,064 80 FOR A=51495 TO 51527: READ B: POKE A,B: NEXT A

90 DATA 083,089,083,084,046,077,069,078,085,019,000,022,015,007,032,080,082,069,083,083,032, 067,079,078,084,082,079,076,032,075,069,089,032 FOR A=56728 TO 56786: READ B: POKE A,B: NEXT A

DATA 014,010,205,019,061,121,042,237,221,034,230,092,014,009,205,019,061,201,017,160,

254,001,034,000,024,006,017,194,254,001,046,000 DATA 042,054,092,229,033,232,220,034,054,092,205,060,032,017,240,254,001,036,000,205,060

,032,225,034,054,092,201 FOR A=56816 TO 56999: READ B: POKE A,B: NEXT A

DATA 000,000,000,000,000,000,000,255,016,040,044,076,076,076,076,255,128,128,128,128,

128,128,128,255,092,088,088,120,112,112,096,255 DATA 128,129,131,131,135,134,142,255,224,192,192,064,064,064,064,255,156,153,187,187,

183,183,182,255,224,248,252,252,078,070,070,255 DATA 182,179,147,153,136,132,131,255,070,070,068,068,072,080,224,255,128,128,128,134,

143,143,142,135,064,054,064,064,064,064,128,000 DATA 000,000,000,006,015,015,014,007,128,128,128,128,128,128,128,128,135,140,136,144,

144,158,159,255,128,192,224,096,118,118,112,255 DATA 159,142,128,128,128,128,128,255,112,112,112,112,102,102,096,255,128,128,128,129,

129,131,130,255,192,192,128,128,000,000,000,255 DATA 132,136,144,160,128,128,128,255,000,060,064,124,006,056,060,000,000,126,002,004, 008,016,016,000 FOR A=USR "A" TO USR "B"+7: READ B: POKE A,B: NEXT A DATA 008,120,248,248,248,112,000,000,000,000,000,009,014,008,008,008 FOR A=USR "E" TO USR "F"+7: READ B: POKE A,B: NEXT A DATA 248,112,000,000,000,000,000,000,009,014,008,008,008,120,248,248 FOR A=65184 TO 65299: READ B: POKE A,B: NEXT A

DATA 016,000,017,007,022,008,027,035,033,033,022,009,027,047,048,033,022,010,027,049,

050,033,022,011,027,051,052,033,022,012,027,053 DATA 033,033,016,000,017,007,022,008,027,046,128,128,022,009,027,035,034,033,022,010, 027,035,036,033,022,011,027,037,038,033,022,012

100 110

120

130 140

150

160

170

180

190

200 210 220 230 240 250

270 DATA 027,039,040,033,022,013,027,041,042,033,022,014,027,045,044,128,022,002,027,033,

034,033,022,003,027,035,036,033,022,004,027,037 280 DATA 038,033,022,005,027,039,040,033,022,006,027,041,042,033,022,007,027,043,044,128 290 FOR N=1 TO 6: RESTORE 300: FOR M=1 TO 10: READ B: POKE (65300+10*(N-1)+M-1),B: NEXT M: NEXT N

300 DATA 045,045,069,077,080,084,089,045,045,032 400 SAVE"wham 1"CODE 65184,352 410 SAVE"wham 3"CODE 50000,7000

Цифры в строках DATA напечатаны одно под другим только для упрощения "читаемости" программы. Вы можете набирать их так, как Вам удобно (например, вместо "002" разумеется, можно набрать "2").

На этом мы заканчиваем изложение некоторых вопросов, связанных с музыкальным редактором "WHAM", но ни в коем случае не заканчиваем музыкальную тематику. Наоборот, планируем всячески ее развивать в дальнейшем. В первом номере "ZX-РЕВЮ" будущего года мы продолжим рассказ о музыкальных редакторах. Будет рассмотрен другой вариант редактора "WHAM" - рассчитанный на работу с трехканальным музыкальным сопроцессором AY-8910 (8912). А затем, в дальнейшем, программы "SOUND-TRACER" и "SONGCOMPILER", также рассчитанные на музыкальный сопроцессор. С их помощью можно создавать звуковое сопровождение такого высокого качества и широкой палитры звучания, что оно становится практически соизмеримо с музыкой, которую мы слышим по радио.




СОДЕРЖАНИЕ:


  Оставте Ваш отзыв:

  НИК/ИМЯ
  ПОЧТА (шифруется)
  КОД



Темы: Игры, Программное обеспечение, Пресса, Аппаратное обеспечение, Сеть, Демосцена, Люди, Программирование

Похожие статьи:
Psycho Нidden Birthday - DJ Psych.
With Demo - Захват 0 demo.
Введение - я исключаю из газеты рубрику Ассемблер, т.к. я в нем еще туповат.
Сюрприз - The Colour of Magic: находка сезона.
Железо - еще раз о Turbo, Mega и Multi.

В этот день...   29 марта