Z góry przepraszam za takie wykopaliska, ale trafiłem na ten wątek dopiero teraz, gdy poskładałem sobie dwie płytki v1.4 z układem ATmega48PA@20MHz.
Przejdźmy jednak do rzeczy. Emulacja AY jeśli chodzi o generator dźwiękowy działa. Niestety tryb pracy jako 3 kanały wyjściowe ma pewien mankament. Otóż kanał B jest odtwarzany zdecydowanie ciszej niż A i C. Można to "naprawić" ustawiając tryb pracy jako 2 kanały z programowym mikserem i eliminując standardowy układ miksera na rezystorach. Ewentualnie zmieniając układ by tylko dostosowywał poziom wyjściowy z emulatora (Który jest zdecydowanie większy) do tego co daje oryginalny AY.
Teraz pytanie: Dlaczego tak się dzieje, i dlaczego jeszcze to nie zostało wychwycone i poprawione? Odpowiedź być może jest taka, że w układzie docelowym tego projektu - czyli samodzielnego grajka to nikomu nie przeszkadzało

. Pozostaje tylko kwestia skąd to się bierze.
Takie zachowanie w trybie pracy z 3-ma kanałami wynika IMHO z błędnych założeń układowych i programowych.
Jeśli zajrzymy do kodu źródłowego to będzie można zauważyć, że licznik TIMER1 nie tylko pracuje jako generator dwóch przebiegów PWM - 1-bitowy DAC - dla kanałów A i C, ale jest też podstawą czasu generowanego dźwięku. Dlatego też bitowość tego przetwornika zależy od... częstotliwości pracy kwarcu (załącznik).
Przy konfiguracji licznik TIMER1 jest skracany do wartości wymaganej by dźwięk był poprawny w zakresie jego tonacji.
; Defines the counter's TOP value
; ICR1H = 0
; ICR1L = config
out EEARL, YH ; set EEPROM address 2
sbi EECR, b0
in r18, EEDR ; load byte 2 from EEPROM to r18
#if MCU_TYPE == 0
out ICR1H, C00
out ICR1L, r18 ; set PWM speed from byte 2 of EEPROM (affect AY chip frequency)
#else
sts ICR1H, C00
sts ICR1L, r18 ; set PWM speed from byte 2 of EEPROM (affect AY chip frequency)
#endif
; ICR1L value formula (28000000/109375/2 - 1) where 28000000 = 28MHz - AVR oscillator frequency
; 109375 is for 1.75 MHz version, formula is (PSG frequency / 16) e.g. for 2MHz it is 2000000/16 = 125000
Można to zrobić, bo konstrukcja tego licznika na to pozwala.
Natomiast kanał B jest generowany przez licznik TIMER2, którego bitowość PWM nie jest już zależna od kwarcu. I
zawsze jest 8 bitowa.
Załóżmy, że kanały A i C są 7 bitowe (mniej więcej tyle ma wyjść z równania), stąd wartość 127 wpisana do rejestru daje wypełnienie 100% - najwyższa amplituda na wyjściu analogowym. Ale nie jest tak dla kanału B, bo wpisanie tam wartości 127 daje nam tylko 50% wypełnienia! I w kodzie nie znalazłem na to działań naprawczych, a dostępne są dwa:
Pierwsze, to skrócenie licznika TIMER2 też do wartości zależnej od kwarcu. Teoretycznie da się to zrobić, bo licznik też można sprzętowo skrócić, ale jest pewien haczyk. Wtedy nie możemy dla kanału B skorzystać z wyjścia OCR2A, tylko trzeba by było skorzystać z wyjścia OCR2B, które jest tak niefortunnie umieszczone, że koliduje z jednym z przerwań zewnętrznych, z którego kod korzysta do reakcji na zmiany BDIR/BC1. Zatem ten sposób naprawy odpada już w przedbiegach.
Zostaje rozwiązanie drugie - programowe. Czyli wartość przekazywana do "przetwornika DAC" trzeba po prostu pomnożyć o jakiś faktor.
Najprościej byłoby po prostu wartość pomnożyć przez dwa: 127*2 = 255, i mamy zależność 127 = 100% wypełnienia.
#elif CHANNELS == 3
// three channel version ----------------------------------------
lsl OutB ; napraw głośność B (szybkie, pomnóż OutB przez 2)
#if MCU_TYPE == 0
out OCR2,OutB
#else
sts OCR2A,OutB
#endif
// --------------------------------------------------------------
#endif
Lecz nie mamy pewności co do tego, czy dla danego kwarcu wartość max będzie właśnie <= 127 by się po przemnożeniu zmieściła w 8 bitach. Dlatego można pójść nieco inną drogą - nieco kopiując rozwiązanie kilka linijek wyżej (Mikser programowy dla dwóch kanałów).
#elif CHANNELS == 3
// three channel version ----------------------------------------
mov YL,OutB ; TMP = TMP + (TMP/4 + TMP/8);
lsr OutB ; napraw głośność B (bezpieczne, zwiększ o ok. 65%)
lsr OutB
add YL,OutB
lsr OutB
add OutB,YL
#if MCU_TYPE == 0
out OCR2,OutB
#else
sts OCR2A,OutB
#endif
// --------------------------------------------------------------
#endif
Można jeszcze poeksperymentować z formułą, choć będzie to wpływać na długość kodu, a zatem na szybkość wykonania pętli emulacji.
Jak znajdę chwilę czasu to przetestuje oba rozwiązania.
ps. Jest jeszcze jedna możliwość poprawy tego kodu, choć wymaga daleko idących zmian. Całkowite odcięcie timingów od generowania PWM. Czyli: timingami zajmuje się niewykorzystany licznik TIMER0 pracujący w trybie CTC, czym zachowujemy elastyczność i zależność tonacji od częstotliwości kwarcu. Zaś liczniki TIMER1 i TIMER2 puszczamy na 8 bitów, i wtedy grają równo...