Dwoma procedurami w tym rozdziale są procedura BEEPER, która faktycznie
steruje głośnikiem, oraz procedura polecenia BEEP.
Głośnik jest włączany przez ustawienie na 0 linii D4 podczas instrukcji
OUT odnoszącej
się do portu '254'. Gdy w podobnej sytuacji linia D4 zostaje ustawiona w
stan wysoki, to głośnik jest wyłączany. Stąd 'pisk' może być wytworzony
przez regularne zmienianie poziomu linii D4.
Rozważmy teraz dźwięk 'środkowe C', który posiada częstotliwość 261.63
Hz. Aby otrzymać ten dźwięk, głośnik musi być naprzemiennie włączany i
wyłączany co 1/523,26 część sekundy. W SPECTRUM zegar
systemowy jest ustawiony do pracy przy 3,5 MHz i dźwięk 'środkowe C' będzie
wymagał instrukcji OUT wykonywanej z najwyższą dokładnością co 6.689 taktów
zegarowych. Ta ostatnia wartość, zmniejszona nieco ze względu na czas
niezbędny do wykonania różnych rozkazów, reprezentuje 'długość pętli
czasowej' w procedurze BEEPER.
PROCEDURA 'BEEPER'
Wejście do tej procedury następuje z parą rejestrów DE zawierającą
wartość 'f*t', gdzie dźwięk o danej częstotliwości 'f' ma posiadać czas
trwania 't' sekund, i z parą rejestrów HL zawierającą wartość równą liczbie
taktów zegara w 'pętli czasu' podzielonej przez '4'.
Np. Dla nuty 'środkowe C' mającej być odtwarzaną przez jedną sekundę
DE
zawiera +0105 (INT(261.3 * 1)) a HL zawiera
+066A
(otrzymane z 6,689/4 - 30.125).
03B5 BEEPER DI Wyłącz przerwania na okres trwania 'pisku'.
LD A,L Chwilowo zachowaj L.
SRL L Każde '1' w rejestrze L ma odpowiadać
SRL L '4' taktom zegara, lecz wykonaj
INT (L/4) i zamiast tego zliczaj '16' taktów.
CPL Wróć do pierwotnej wartości
AND +03 w L i sprawdź, ile zostało stracone
LD C,A przez obliczenie INT (L/4).
LD B,+00
LD IX,+03D1 Adres bazowy pętli czasowej.
ADD IX,BC Zmień długość pętli czasowej.
Użyj wcześniejszego punktu
dla każdego '1' utraconego przez wyznaczenie INT (L/4).
LD A,(BORDCR) Pobierz kolor bieżącego brzegu
AND +38 i przenieś go do bitów
RRCA 2, 1 1 0 rejestru A.
RRCA
RRCA
OR +08 Upewnij się, że wyjście MIC jest wyłączone.
Teraz wejdź to pętli drugiej generacji. Wykonywane jest 'DE' obiegów, tj.
obiegów dla każdego okresu dźwięku.
Para rejestrów HL zawiera 'długość pętli czasowej' przy 16 taktach zegara
używanych dla każdego '1' w rejestrze L i 1.024 taktach zegara dla
każdego '1' w rejestrze H..
03D1 BE-IX+3 NOP Dodaj '4' takty zegara dla każdego
03D2 BE-IX+2 NOP wcześniejszego punktu wejścia,
03D3 BE-IX+1 NOP który jest używany.
03D4 BE-IX+0 INC B Wartości w rejestrach B i C
INC C będą pochodziły z rejestrów H i L
- patrz niżej.
03D6 BE-H&L-LP DEC C 'Pętla czasowa'.
JR NZ,03D6,BE-H&L-LP 'BC' * '4' cykle zegara.
LD C,+3F (Lecz zauważ, że w punkcie połowy okresu
DEC B C będzie równe 'L+1'.)
JP NZ,03D6,BE-H&L-LP
Teraz głośnik jest naprzemian włączany i wyłączany.
XOR +10 Zmień na przeciwny bit 4.
OUT (+FE),A Wykonaj operację OUT;
pozostawiając brzeg bez zmiany koloru.
LD B,H Resetuj rejestr B.
LD C,A Zachowaj rejestr A.
BIT 4,A Skocz, jeśli jest to punkt połowy okresu.
JR NZ,03F2,BE-AGAIN
Po pełnym cyklu para rejestrów DE jest testowana.
LD A,D Skocz naprzód, jeśli ostatni
OR E pełny okres został
JR Z,03D6,BE-END już wykonany.
LD A,C Pobierz zachowaną wartość.
LD C,L Resetuj rejestr C.
DEC DE Zmniejsz licznik obiegów.
JP (IX) Skocz wstecz do wymaganego
punktu startowego pętli.
Ustawiane są parametry dla drugiej połowy cyklu.
03F2 BE-AGAIN LD C,L Resetuj rejestr C.
INC C Dodaj '16' cykli, ponieważ ta ścieżka
jest krótsza.
JP (IX) Skocz do tyłu.
Po zakończeniu generacji dźwięku muszą zostać włączone przerwania.
03F6 BE-END EI Włącz przerwania.
RET Powróć ostatecznie.
PROCEDURA ROZKAZU 'BEEP'
Wejście do tej procedury następuje z dwoma liczbami na stosie
kalkulatora. Liczba na szczycie stosu przedstawia 'wysokość' nuty P, a
liczba leżąca pod spodem przedstawia jej 'długość' t.
03F8 BEEP RST 0028,FP-CALC Użyty zostaje kalkulator zmiennoprzecinkowy
do manipulacji tymi wartościami - t i P.
DEFB +31,duplicate t,P,P
DEFB +27,int t,P,i (gdzie i = INT P)
DEFB +C0,st-mem-0 t,P,i (mem-0 zawiera i)
DEFB +03,subtract t,P (gdzie p jest częścią ułamkową P)
DEFB +34,stk-data Umieść na stosie liczbę 'K'.
DEFB +EC,exponent+7C 0.0577622606 (co jest
DEFB +6C,+98,+1F,+F5 nieco poniżej 12*(2^0.5)-1)
DEFB +04,multiply t,p*K
DEFB +A1,stk-one t,p*K,1
DEFB +0F,addition t,p*K+1
DEFB +38,end-calc
Teraz wykonaj kilka testów na i, części całkowitej z 'wysokości' nuty.
LD HL,+5C92 Adres 'mem-0-1st' (MEMBOT).
LD A,(HL) Pobierz wykładnik i.
AND A Zgłoś błąd B, jeśli i nie jest
JR NZ,046C,REPORT-B w krótkiej postaci całkowitej.
INC HL Skopiuj bit znaku do
LD C,(HL) rejestru C.
INC HL Skopiuj młodszy bajt do
LD B,(HL) rejestru B i do
LD A,B rejestru A.
RLA Znów zgłoś błąd B, jeśli nie
SBC A,A spełnia warunku:
CP C -128<=i<=+127
JR NZ,046C,REPORT-B
INC HL
CP (HL)
JR NZ,046C,REPORT-B
LD A,B Pobierz młodszy bajt i
przetestuj go dalej.
ADD A,+3C
JP P,0425,BE-i-OK Zaakceptuj -60<=i<=67.
JP PO,046C,REPORT-B Odrzuć -128 do -61.
Uwaga: Zakres od +70 do +127 zostanie odrzucony w dalszej części
procedury.
Teraz dla każdej 'wysokości' można znaleźć prawidłową częstotliwość.
0425 BE-i-OK LD B,+FA Rozpocznij '6' oktaw poniżej środkowego C.
0427 BE-OCTAVE INC B Cyklicznie redukuj i w celu znalezienia
SUB +0C właściwej oktawy.
JR NC,0427,BE-OCTAVE
ADD A,+0C Anuluj ostatnie odejmowanie.
PUSH BC Zachowaj numer oktawy.
LD HL,+046E Adres bazowy 'tablicy półtonów'.
CALL 3406,LOC-MEM Rozpatrz tę tablicę i przekaż
CALL 33B4,STACK-NUM 'A-tą' wartość na stos kalkulatora (nazwijmy ją C).
Teraz pod uwagę można wziąć część ułamkową 'wysokości'.
RST 0028,FP-CALC t, p*K+1, C
DEFB +04,multiply t, C*(p*K+1)
DEFB +38,end-calc
Ostateczna częstotliwość f jest znajdowana przez modyfikację 'ostatniej
wartości' zgodnie z numerem oktawy.
POP AF Pobierz numer oktawy.
ADD A,(HL) Pomnóż 'ostatnią wartość' przez
LD (HL),A '2 do potęgi numeru oktawy.
RST 0028,FP-CALC t, f
DEFB +C0,st-mem-0 Chwilowo częstotliwość jest odkładana na bok
DEFB +02,delete do mem-0.
Teraz uwaga jest kierowana na 'długość'.
DEFB +31,duplicate t, t
DEFB +38,end-calc
CALL 1E94,FIND-INT1 Wartość 'INT t' musi być
CP +0B w zakresie od +00 do +0A.
JR NC,046C,REPORT-B
Liczba pełnych okresów w 'pisku' jest dana przez 'f*t', więc teraz ta
wartość zostaje obliczona
RST 0028,FP-CALC t
DEFB +E0,get-mem-0 t, f
DEFB +04,multiply f*t
Wynik jest pozostawiany na stosie kalkulatora, natomiast obliczona
zostaje wymagana długość 'pętli czasowej' dla 'pisku';
DEFB +E0,get-mem-0 f*t, f
DEFB +34,stk-data Wartość '3.5 * 10^6/8'
DEFB +80,four bytes jest tworzona na szczycie
DEFB +43,exponent +93 stosu kalkulatora.
DEFB +55,+9F,+80,(+00) f*t, f, 437,500 (dziesiętnie)
DEFB +01,exchange f*t, 437,500, f
DEFB +05,division f*t, 437,500/f
DEFB +34,stk-data
DEFB +35,exponent +85
DEFB +71,(+00,+00,+00) f*t, 437,500/f, 30.125 (dziesiętnie)
DEFB +03,subtract f*t, 437,500/f - 30.125
DEFB +38,end-calc
Uwaga: Wartość '437,500/f' daje długość 'połowy okresu' nuty skróconej o
30.125' dla '120.5' taktów, w czasie których dźwięk jest tworzony oraz są
obsługiwane liczniki.
Wartości te mogą teraz zostać przeniesione do odpowiednich rejestrów.
CALL 1E99,FIND-INT2 Wartość 'pętli czasowej' zostaje
PUSH BC umieszczona w parze rejestrów BC i zachowana.
Uwaga: Jeśli wartość dla pętli czasowej jest zbyt duża, to powstanie błąd
(powrót poprzez ERROR-1); w ten sposób unika się
'wysokości' od '+70 do +127'.
CALL 1E99,FIND-INT2 Wartość 'f*t' trafia do pary rejestrów BC.
POP HL Przenieś ją do pary rejestrów HL.
LD D,B Przenieś wartość 'f*t'
LD E,C do pary rejestrów DE.
Jednakże przed wykonaniem 'pisku' sprawdź wartość 'f*t'.
LD A,D Wróć, jeśli 'f*t' dało
OR E wymagany wynik
RET Z 'brak taktów'.
DEC DE Zmniejsz liczbę taktów
JP 03B5,BEEPER i skocz do procedury BEEPER
(wykonując co najmniej jeden obieg).
Raport B - liczba całkowita poza zakresem
046C REPORT-B RST 0008,ERROR-1 Wywołaj procedurę obsługi
DEFB +0A błędów.
TABLICA 'PÓŁTONÓW'
Ta tablica przechowuje częstotliwości dwunastu półtonów w oktawie.
częstotliwość w Hz. nuta
046E DEFB +89,+02,+D0,+12,+86 261.63 C
DEFB +89,+0A,+97,+60,+75 277.18 C#
DEFB +89,+12,+D5,+17,+1F 293.66 D
DEFB +89,+1B,+90,+41,+02 311.12 D#
DEFB +89,+24,+D0,+53,+CA 329.63 E
DEFB +89,+2E,+9D,+36,+B1 349.23 F
DEFB +89,+38,+FF,+49,+3E 369.99 F#
DEFB +89,+43,+FF,+6A,+73 392 G
DEFB +89,+4F,+A7,+00,+54 415.30 G#
DEFB +89,+5C,+00,+00,+00 440 A
DEFB +89,+69,+14,+F6,+24 466.16 A#
DEFB +89,+76,+F1,+10,+05 493.88 H
PROCEDURA 'NAZWA PROGRAMU' Z ZX81
Poniższa procedura odnosi się do ZX81 i nie została usunięta, gdy program
został przepisany dla SPECTRUM.
04AA DEFB +CD,+FB,+24,+3A
DEFB +3B,+5C,+87,+FA
DEFB +8A,+1C,+E1,+D0
DEFB +E5,+CD,+F1,+2B
DEFB +62,+6B,+0D,+F8
DEFB +09,+CB,+FE,+C9