Немига/Звук

Материал из Emuverse
Этот документ создан для Emuverse и распространяется на условиях лицензии CC-BY-SA-3.0.

Вызов SOUND

Системная программа SOUND делает следующее:

  1. Смотрит откуда она вызвана, пропускает одно слово
  2. Следующее слово трактуется как адрес начала мелодии
  3. Дальше запускает мелодию на исполнение -- см. Кодирование мелодии ниже

Поэтому, вызов SOUND выглядит примерно так:

        jsr pc,.sound
        br 1$
        .word melody
    1$: ............
        ............
        ............
melody: .byte  .........  ; мелодия
        .byte  0          ; индикатор конца мелодии

Кодирование мелодии

  1. Первым должен быть байт октавы и громкости — Двузначное восьмеричное число (см. описание регистра 170030)
  2. Далее следуют два байта :
    • Байт с номером ноты (бит 7=1, то есть коды от 200 до 214)
    • Байт с длительностью (в 1/50 долях секунды)
  3. Далее повторяются два байта, задающие ноту и ее длительность, а при необходимости, перед ними может стоять байт октавы и громкости.
  4. Мелодия заканчивается байтом, содержащим нули в разрядах 0..4 и 7. Комбинация разрядов 5 и 6 определяют способ завершения мелодии:
    • 000 — никаких действий не производится
    • 040 — прерывание по вектору 110
    • 100 — зацикливание мелодии
    • 140 — очистка байта, следующего после индикатора конца мелодии

Управляющие регистры таймера и звука

    RgStSnd    -   170020  -  регистр состояния
    RgF        -   170022  -  регистр частоты -- счётчик 1
    RgLength   -   170024  -  регистр длительности -- счётчик 2
    RgOn       -   170026  -  включение звука (обращение)
    RgOct      -   170030  -  регистр октавы и громкости
    RgOff      -   170032  -  выключение звука (обращение)
  170020  Регистр состояния программируемого таймера
 ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
 │15│14 13 12│11 10  9│ 8  7  6│ 5  4  3│ 2  1  0│
 └──┴──┴──┴──┴──┴──┴─┬┴─┬┴─┬┴─┬┴─┬┴─┬┴─┬┴─┬┴─┬┴─┬┘
  Фикс прерывания 2 ─┘  │  │  │  │  │  │  │  │  └─ ??
     Фикс прерывания 1 ─┘  │  │  │  │  │  │  └ Режим работы 1-го счётчика
          Блокировка ЗПР2 ─┘  │  │  │  └──┴─ Режим пуска 1-го счётчика
             Блокировка ЗПР1 ─┘  │  │  0  1  блокировка счётчика
                                 │  │  1  0  запуск по CO1
                                 │  │  1  1  запуск по C1
                                 └──┴─ Режим пуска 2-го счётчика
                                 0  1  блокировка счётчика
                                 1  0  запуск по CO2
                                 1  1  запуск по C2
  170030  Регистр октавы и громкости
 ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
 │15│14 13 12│11 10  9│ 8  7  6│ 5  4  3│ 2  1  0│
 └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴─┬┴─┬┴─┬┴─┬┴─┬┘
                                    │  │  └──┴──┴─ Октава 1..7
                                    └──┴─ Громкость 0..3

Работа со звуком в ПЗУ Немига 3.03

Точка входа подпрограммы SOUND в начале ПЗУ:

160126: JMP	@177754			; SOUND

Звук нажатия клавиши:

160360: TSTB	@#177761		; Флаг блокировки вызова SOUND
160364: BNE	160402			; <> 0 => не вызывать повторно
160366: CALL	@#160126		; SOUND
160372: BR	160402
160374: DW	160376			; Адрес мелодии
160376: DB	011, 214, 001, 000	; Мелодия -- звук длиной 1/50 сек

Обработка продолжения SOUND в обработчике прерывания HALT:

160602: BIT	#010000, @#170006	; Сигнал Н3?
160610: BEQ	161060			; Нет => Завершаем обработку прерывания HALT
; Обработка сигнала Н3
160612: MOV	R4, -(SP)		; Сохраняем R4
160614: TSTB	@#177761		; Флаг блокировки SOUND
160620: BPL	160746			; bit7=0 - блокировано => переходим
160622: MOV	@#177752, R4		; Берём адрес продолжения мелодии
160626: INCB	@#177760		; Увеличиваем счётчик??
160632: CMPB	@#177760, 177777(R4)	; Сравниваем с длительностью ноты
160640: BLO	160746			; пока не кончилась => переходим
160642: TSTB	(R4)			; 200 ?
160644: BMI	160742			; да => переходим
160646: BITB	#000037, (R4)		; Есть ещё нота мелодии?
160652: BNE	160736			; да => переходим
160654: BICB	#000200, @#177761	; Снимаем флаг блокировки SOUND
; Обработка флагов завершения мелодии
160662: BITB	#000040, (R4)		; 040 и 140 ?
160666: BNE	160712			; да => переходим
160670: TSTB	(R4)			; 000 ? (никаких действий по окончании мелодии)
160672: BEQ	160746			; да => переходим
160674: MOV    	@#177750, @#160710	; Копируем адрес начала мелодии??
160702: CALL   	@#160126		; SOUND
160706: BR     	160746
160710: DW	??
; Окончание мелодии
160712: BITB   	#000100, (R4)+
160716: BNE    	160732
160720: MOV    	(SP)+, R4
160722: CLRB   	@#170006
; Переход на прерывание по вектору 110
160726: MOV    	@#000110, PC
; Очистка байта после мелодии
160732: CLRB   	(R4)
160734: BR     	160746
;
160736: MOVB   	(R4)+, @#170030		; Октава и громкость
160742: CALL   	@#162560		; Обработка продолжения мелодии

Подпрограмма SOUND:

162474: MOV	R4, -(SP)		; Сохраняем R4
162476: BICB	#000200, @#177761	; Предотвращаем повторный вызов SOUND: bit7=0
162504: CLR	@#170024		; Сброс таймер 2 (длительности)
162510: MOV	000002(SP), R4		; Откуда вызвали SOUND
162514: MOV	000002(R4), R4		; Получаем адрес мелодии 
162520: TSTB	(R4)
162522: BEQ	162554
162524: MOV	R4, @#177750		; Запоминаем адрес начала мелодии
162530: MOV	#001516, @#170020	; Пишем в регистр состояния таймера
162536: MOVB	(R4)+, @#170030		; Октава и громкость
162542: CALL	@#162560		; Обработка продолжения мелодии
162546: BISB	#000200, @#177761	; Возвращаем флаг блокировки SOUND: bit7=1
162554: MOV	(SP)+, R4		; Восстанавливаем R4
162556: RETURN	
;
162560: MOV	R3, -(SP)		; Сохраняем R3
162562: MOVB	(R4)+, R3
162564: BIC	#177760, R3		; Оставляем нижние 4 бита -- 00..17
162570: ASL	R3			; и умножаем на 2
162572: MOV	162624(R3), @#170022	; Выбираем по R3 слово и пишем в первый счётчик
162600: MOVB	(R4)+, @#170024		; Задаём длительность
162604: CLRB	@#177760		; Очищаем счётчик??
162610: MOV	R4, @#177752		; Запоминаем адрес продолжения мелодии
162614: TST	@#170026		; Устанавливаем триггер таймера
162620: MOV	(SP)+, R3		; Восстанавливаем R3
162622: RETURN
; Значения задержки первого таймера для нот
162624: DW	000000, 003570, 003414, 003246, 003110, 002754, 002630, 002510
162644: DW	002374, 002264, 002160, 002061, 001764, 000000, 000000, 000000

Переменные:

177750: DW      ; Адрес начала мелодии
177752: DW      ; Текущий адрес мелодии
177760: DB      ; Счётчик
177761: DB      ; Флаги

Работа этого кода предполагает, что каждые 1/50 секунды будет генерироваться прерывание HALT с выставленным сигналом Н3. Также это используется для организации мигания курсора.

Работа со звуком в ПЗУ Немига 4.06

Обработка продолжения SOUND в обработчике прерывания HALT:

160610: BIT	#010000, @#170006	; Сигнал Н3?
160616: BEQ	161074			; Нет => Завершаем обработку прерывания HALT
; Обработка сигнала Н3
160620: MOV	R4, -(SP)		; Сохраняем R4
160622: TST	@#177760		; Флаг блокировки SOUND
160626: BPL	160750			; bit7=0 => переходим
160630: MOV	@#177752, R4		; Берём адрес продолжения мелодии
160634: INCB	@#177760		; Увеличиваем счётчик длительности ноты
160640: CMPB	@#177760, 177777(R4)	; Сравниваем с длительностью ноты
160646: BLO	160750			; пока не кончилась => переходим
160650: TSTB	(R4)			; 200 ?
160652: BMI	160744			; да => переходим
160654: BITB	#000037, (R4)		; Есть ещё нота мелодии?
160660: BNE	160740			; да => переходим
160662: BIC	#100000, @#177760	; Снимаем флаг блокировки SOUND
; Обработка флагов завершения мелодии
160670: BITB	#000040, (R4)		; 040 и 140 ?
160674: BNE	160712			; да => переходим
160676: TSTB	(R4)			; 000 ? (никаких действий по окончании мелодии)
160700: BEQ	160750			; да => переходим
160702: CALL   	@#160126		; SOUND
160706: BR     	160750
160710: DW	000000
; Окончание мелодии
160712: BITB   	#000100, (R4)+
160716: BNE    	160734
160720: MOV    	(SP)+, R4
; Переход на прерывание по вектору 110
160722: MOV    	@#000112, -(SP)
160726: MOV    	@#000110, -(SP)
160732: BR     	161074
; Очистка байта после мелодии
160734: CLRB   	(R4)
160736: BR     	160750
;
160740: MOVB   	(R4)+, @#170030		; Октава и громкость
160744: CALL   	@#162520		; Обработка продолжения мелодии

Подпрограмма SOUND:

162426: MOV	R4, -(SP)		; Сохраняем R4
162430: BICB	#000200, @#177761
162436: CLR	@#170024		; Сброс таймер 2 (длительности)
162442: MOV	000002(SP), R4		; Откуда вызвали SOUND
162446: MOV	000002(R4), R4		; Получаем адрес мелодии 
162452: BNE	162460
162454: MOV    	@#177750, R4
162460: TSTB	(R4)
162462: BEQ	162514
162464: MOV	R4, @#177750
162470: MOV	#001516, @#170020	; Пишем в регистр состояния таймера
162476: MOVB	(R4)+, @#170030		; Октава и громкость
162502: CALL	@#162520		; Обработка продолжения мелодии
162506: BISB	#000200, @#177761	; Возвращаем флаг блокировки SOUND: bit7=1
162514: MOV	(SP)+, R4		; Восстанавливаем R4
162516: RETURN	
; Обработка продолжения мелодии
162520: MOV	R3, -(SP)		; Сохраняем R3
162522: MOVB	(R4)+, R3
162524: BIC	#177760, R3		; Оставляем нижние 4 бита -- 00..17
162530: ASL	R3			; и умножаем на 2
162532: MOV	162564(R3), @#170022	; Выбираем по R3 слово и пишем в первый счётчик
162540: MOVB	(R4)+, @#170024		; Задаём длительность
162544: CLRB	@#177760		; Очищаем счётчик длительности ноты
162550: MOV	R4, @#177752		; Запоминаем адрес продолжения мелодии
162554: TST	@#170026		; Устанавливаем триггер таймера
162560: MOV	(SP)+, R3		; Восстанавливаем R3
162562: RETURN	
; Значения задержки первого таймера для нот
162564: DW	000000, 002253, 002150, 002050, 001755, 001664, 001577, 001515
162604: DW	001435, 001361, 001306, 001237, 001171, 000000, 000000, 000000

Звук в Бейсик Вильнюс

После ввода оператора PLAY "AB", Бейсик выполняет какие-то действия, и затем ожидает в следующем цикле:

065170: CMP	R2, @#001456
065174: BNE	065270
065176: ADD	#000005, R2
065202: CMP	R2, @#177752
065206: BLOS	065262
065210: SUB	#000005, R2
065214: CMP	R2, @#177752
065220: BLOS	065162
065162: CALL	065372
065372: BICB	#000177, @#177761
065400: BEQ	065426
065402: CMP	@#177752, #001624
065410: BLO	065426
065412: CMP	@#177752, #001663
065420: BHI	065426
065422: ADD	#000002, (SP)
065426: RETURN	
065170: CMP	R2, @#001456