Kwietnia 19, 2024, 05:19:07 *
Witamy, Gość. Zaloguj się lub zarejestruj.

Zaloguj się podając nazwę użytkownika, hasło i długość sesji
Aktualności:
Nowa strona główna  : http://www.emunes.pl
Zapraszamy do testowania !

Chat: http://chat.emunes.pl
Galeria cartów: http://carts.emunes.pl
 
   Strona główna   Pomoc Zaloguj się Rejestracja  

Reklama
Strony: [1]
  Drukuj  
Autor Wątek: Krzysioplayer -odtwarzacz plików NSF z Pegasusa a'la C64 SID,hardware nsf player  (Przeczytany 1965 razy)
0 użytkowników i 1 Gość przegląda ten wątek.
Krzysiobal
Chorąży
***
Offline Offline

Wiadomości: 140



« : Października 31, 2015, 14:22:06 »


Układu SID (6581 stary i 8580 nowy), będącym muzyczną orkiestrą komputera Commodore 64 nie trzeba nikomu przedstawiać – między innymi dzięki jego brzmieniu ta maszyna osiągnęła tak dużą popularność. Warto tylko przypomnieć, że SID ma jedynie trzy kanały, jednak umiejętności programistów potrafiły z niego wycisnąć dźwiękowe cuda. Na dzień dzisiejszy w Internecie krąży kilkadziesiąt plików SID, które zawierają zripowaną muzykę w tym formacie, którą to poprzez emulatory można odtwarzać na PC i delektować się metaliczno-syntezatorowym brzemieniem przemijającej już epoki.

Mało kto natomiast słyszał o formacie NSF (Nintendo Sound Format), będącym odpowiednikiem SID na konsolę NES (Pegasus/Famicom). A szkoda, bo procesor w konsoli NES (RP2a07) czy Pegasusie (UA6527P), będący sklepowym MOS 6502 z dodanym układem do generowania dźwięku wcale nie brzmi gorzej – ma 5 kanałów. I choć kolekcja NSF z gier oraz dem nie jest aż tak bogata jak SID, to wiele muzyczek i tak wpada w ucho już od pierwszego włączenia. I nie mam tu wcale na myśli jedynie popularnych soundtracków z Mario Bros, lecz także soundtracki z gier CodeMasters (autorstwa Allister Brimble’a), czy utwory z serii platformówek Mega Man.

Ponieważ jednak słuchanie ich na PC (emulacja) to nie to samo, co odtwarzanie na prawdziwym sprzęcie, na który zostały stworzone (6502), postanowiłem wykonać sprzętowy odtwarzacz NSF – Krzysioplayer – który pozwoli delektować się nutami odtwarzanymi przez najprawdziwszy procesor 6502 z konsoli Pegasus (układ UA6527P).

Cele:
Celem jest stworzenie urządzenia, które posiada gniazdo na karty pamięci MMC/SD, na które nagram swoje ulubione pliki NFS. Urządzenie ma być oparte o oryginalny procesor 6502 – UA6527P – obudowany w niezbędne układy peryferyjne:
- pamięć ROM z kodem sterującym odtwarzaczem (bootloader) oraz wgraną muzyczką, którą po wybraniu przez użytkownika druga część pamięci ROM jest programowana
- pamięć RAM,
Użytkownik będzie mógł sterować urządzeniem za pomocą przycisków oraz wyświetlacza 24x2(standardowy HD4780). Tu trochę szkoda, że większe wyświetlacze (24x4) są już dużo droższe. Układ generowania i wzmacniania dźwięku będzie jednotranzystorowy – tak samo prymitywny jak w oryginalnym Pegasusie, aby dźwięk brzmiał równie oldschool’owo. Dodatkowo może przydać się regulacja szybkości odtwarzania (niektóre gry pisane były pod PAL – 50HZ, inne NTSC – 60HZ), a poza tym czasami warto posłuchać, jak ulubiony utwór brzmi w innym tempie.
Do tego wszystkiego konieczny jest jakiś układ, który zepnie wszystko w całość, tzn. będzie pełnił funkcje dekodera adresów (do pamięci/LCD/przycisków), ułatwi komunikacje z kartą pamięci SD po protokole SPI – komunikacja jest bit po bicie. Gdyby procesor w każdym cyklu miał wystawiać te bity samodzielnie, to trwałoby to wieczność.

Budowa
Budowa urządzenia oraz przemyślenia zajęła mi około 3 lat – to właśnie wtedy stawiałem pierwsze kroki w rozeznaniu terenu pod jego konstrukcję. Równocześnie też tworzyłem programowalny kartrydż do Pegasus-a oparty o FPGA, więc z racji bliskości obu zagadnień, projekt dobrze się rozwijał. Oprócz procesora potrzebny jest układ pełniący kilka funkcji, tj:
- dekoder adresów do pamięci ROM/RAM/wyświetlacza/bufora podającego naciśnięte klawisze na magistralędanych
- pośrednik w komunikacji między 6502 a kartą pamięci MMC/SD
- zatrzask pamiętający wybrane banki pamięci,
- włączacz/wyłączacz przerwań
Ponieważ 6502 ma tylko 16 bitową magistralę adresową (co pozwala adresować 65536 bajtów pamięci), aby móc dobrać się do większej ilości pamięci (bardziej rozbudowanee utwory), potrzebny jest mechanizm bankowania pamięci, co z kolei wiąże się z koniecznością pamiętania aktualnych banków (NSF przewiduje 8 banków po 4 kB każdy). Już 3 lata temu zdecydowałem się na układ CPLD XC9572XL który pełniłby te wszystkie funkcje. Niestety, wtedy nie umiałem pisać aż tak optymalnego kodu, wobec czego w układzie starczyło jedynie zasobów na zapamiętanie 3 bitów na każdy z banków, co umożliwiało obsługę jedynie plików NSF o rozmiarze 4 kB * 2^3 = 32 kB. Niestety największe (i najciekawsze) pliki NSF są większe i mają rozmiar do 128 kB. Brak ich obsługi zmniejszyłby ciekawość projektu. Zniechęcony takim obrotem spraw, najpierw postanowiłem przenieść się na układ XC95144 (144 makrocele zamiast 72), nawet zrobiłem do niego płytkę ewaluacyjną z uwagi na obudowę SMD i gęsty raster wyprowadzeń. W międzyczasie zacząłem jednak kolejne studia (elektronika) i projekt trafił do szuflady na 3 lata.

Niedawno wygrzebałem projekt z szuflady i postanowiłem go dokończyć (a właściwie napisać wszystko jeszcze raz od zera, dużo optymalniej). W efekcie udało się z układu XC9572 wycisnąć ostatnie soki (wykorzystane wszystkie zasoby w 100% !!!), a układ obsługuję wymarzone do 5 bitów na bank co pozwala na obsługę plików NSF o rozmiarze 4 kB * 2^5 = 128 kB. Główny cel został osiągnięty, jednak był okupiony mnóstwem trudności na każdym kroku.


O formacie NSF
O problemach jednak na koniec, najpierw postaram się przybliżyć czytelnikom format plików NSF.
Pliki NSF to tak naprawdę programy (kod maszynowy asemblera 6502) na procesor 6502, które czytając/pisząc do rejestrów procesora, generują za pomocą układu dźwiękowego wbudowanego w procesor muzykę. Procesor 6502 w konsoli posiada 5 kanałów:
- 2 prostokąty
- 1 trójkąt
- szumy
- PCM, pozwalający na syntezę krótkich efektów specjalnych (modulacja delta), np. imitujących ludzkie głosy, co możemy usłyszeć na początku gry Dizzy Adventurer („Let’s play Dizzy) czy podczas muzyki z gry Skate Or Die:
https://www.youtube.com/watch?v=_l6nrwbaB3g
Rejestry procesora zajmują adresy $4000-$4017.

Przykładowo, „kawałek” wlazł kotek na płotek mógłby wyglądać tak:
Kod:
ca65 V2.13.3 - (C) Copyright 1998-2012 Ullrich von Bassewitz
Main file   : hello.asm
Current file: hello.asm

000000r 1               .SEGMENT "HEADER"
000000r 1  4E 45 53 4D  .byte 'N','E','S','M',$1A                 ;$000    5   STRING  'N','E','S','M',$1A (denotes an NES sound format file)
000004r 1  1A           
000005r 1  01           .byte $1                                  ;$005    1   BYTE    Version number (currently $01)
000006r 1  01           .byte $1                                  ;$006    1   BYTE    Total songs   (1=1 song, 2=2 songs, etc)
000007r 1  01           .byte $1                                  ;$007    1   BYTE    Starting song (1=1st song, 2=2nd song, etc)
000008r 1  rr rr        .word reset                               ;$008    2   WORD    (lo, hi) load address of data ($8000-FFFF)
00000Ar 1  rr rr        .word init                                ;$00A    2   WORD    (lo, hi) init address of data ($8000-FFFF)
00000Cr 1  rr rr        .word play                                ;$00C    2   WORD    (lo, hi) play address of data ($8000-FFFF)
00000Er 1  53 61 6D 70  .byte "Sample name                    ",0 ;$00E    32  STRING  The name of the song, null terminated
000012r 1  6C 65 20 6E 
000016r 1  61 6D 65 20 
00002Er 1  53 61 6D 70  .byte "Sample artist                  ",0 ;$02E    32  STRING  The artist, if known, null terminated
000032r 1  6C 65 20 61 
000036r 1  72 74 69 73 
00004Er 1  53 61 6D 70  .byte "Sample copyright               ",0 ;$04E    32  STRING  The copyright holder, null terminated
000052r 1  6C 65 20 63 
000056r 1  6F 70 79 72 
00006Er 1  1A 41        .word $411A                               ;$06E    2   WORD    (lo, hi) Play speed, in 1/1000000th sec ticks, NTSC (see text)
000070r 1  00 00 00 00  .byte 0, 0, 0, 0, 0, 0, 0, 0              ;$070    8   BYTE    Bankswitch Init Values (see text, and FDS section)
000074r 1  00 00 00 00 
000078r 1  1D 4E        .word $4E1D                               ;$078    2   WORD    (lo, hi) Play speed, in 1/1000000th sec ticks, PAL (see text)
00007Ar 1  00           .byte 0                                   ;$07A    1   BYTE    PAL/NTSC bits
00007Br 1                                                         ;                bit 0: if clear, this is an NTSC tune
00007Br 1                                                         ;                bit 0: if set, this is a PAL tune
00007Br 1                                                         ;                bit 1: if set, this is a dual PAL/NTSC tune
00007Br 1                                                         ;                bits 2-7: not used. they *must* be 0
00007Br 1  00           .byte 0                                   ;$07B    1   BYTE    Extra Sound Chip Support
00007Cr 1                                                         ;                bit 0: if set, this song uses VRC6 audio
00007Cr 1                                                         ;                bit 1: if set, this song uses VRC7 audio
00007Cr 1                                                         ;                bit 2: if set, this song uses FDS audio
00007Cr 1                                                         ;                bit 3: if set, this song uses MMC5 audio
00007Cr 1                                                         ;                bit 4: if set, this song uses Namco 163 audio
00007Cr 1                                                         ;                bit 5: if set, this song uses Sunsoft 5B audio
00007Cr 1                                                         ;                bits 6,7: future expansion: they *must* be 0
00007Cr 1  00 00 00 00  .byte 0, 0, 0, 0                          ;$07C    4   ----    4 extra bytes for expansion (must be $00)
000080r 1                                                         ;$080    nnn ----    The music program/data follows until end of file
000080r 1               
000080r 1               .DEFINE note_c 524
000080r 1               .DEFINE note_d 588
000080r 1               .DEFINE note_e 660
000080r 1               .DEFINE note_f 700
000080r 1               .DEFINE note_g 784
000080r 1               .DEFINE note_a 880
000080r 1               .DEFINE note_h 988
000080r 1               
000080r 1               .MACRO delay_ms ms ;ms=1-2000
000080r 1               .LOCAL ynotzero
000080r 1                ;1773448 = 26601712 / 15 for Dendy/pegasus
000080r 1                ldy #(((((ms) * 1773448 / (5 * 1000)) + 1 + 256 + 65536) >> 0) & $FF)
000080r 1                ldx #(((((ms) * 1773448 / (5 * 1000)) + 1 + 256 + 65536) >> 8) & $FF)
000080r 1                lda #(((((ms) * 1773448 / (5 * 1000)) + 1 + 256 + 65536) >> 16) & $FF)
000080r 1               ynotzero:
000080r 1                dey
000080r 1                bne ynotzero
000080r 1                dex
000080r 1                bne ynotzero
000080r 1                clc
000080r 1                adc #-1
000080r 1                bne ynotzero
000080r 1               .ENDMACRO
000080r 1               
000080r 1               .MACRO simplesnd freq       ;sndfreq: 50-20000
000080r 1                lda #<((1662607 / (16 * freq)) - 1)
000080r 1                   sta $4002
000080r 1                   lda #((>((1662607 / (16 * freq)) - 1)) && %111)
000080r 1                   sta $4003
000080r 1               
000080r 1                   lda #%00001111
000080r 1                   sta $4000
000080r 1                jsr delay_long
000080r 1               
000080r 1                lda #0
000080r 1                sta $4002
000080r 1                sta $4003
000080r 1                jsr delay_short
000080r 1               .ENDMACRO
000080r 1               
000080r 1               
000080r 1               .SEGMENT "CODE"
000000r 1               
000000r 1               reset:
000000r 1  20 rr rr      jsr init
000003r 1  20 rr rr      jsr play
000006r 1               
000006r 1               init:
000006r 1  A0 00            ldy #0
000008r 1               init_apu_loop:
000008r 1  B9 rr rr         lda init_apu_regs,y
00000Br 1  99 00 40         sta $4000, y
00000Er 1  C8               iny
00000Fr 1  C0 18            cpy #$18
000011r 1  D0 F5            bne init_apu_loop
000013r 1  60               rts
000014r 1               
000014r 1               play:
000014r 1  A9 83 8D 02  simplesnd note_g ;wlazl
000018r 1  40 A9 00 8D 
00001Cr 1  03 40 A9 0F 
000031r 1  A9 9C 8D 02  simplesnd note_e ;ko
000035r 1  40 A9 00 8D 
000039r 1  03 40 A9 0F 
00004Er 1  A9 9C 8D 02  simplesnd note_e ;tek
000052r 1  40 A9 00 8D 
000056r 1  03 40 A9 0F 
00006Br 1  A9 93 8D 02  simplesnd note_f ;na
00006Fr 1  40 A9 00 8D 
000073r 1  03 40 A9 0F 
000088r 1  A9 AF 8D 02  simplesnd note_d ;plo
00008Cr 1  40 A9 00 8D 
000090r 1  03 40 A9 0F 
0000A5r 1  A9 AF 8D 02  simplesnd note_d ;tek
0000A9r 1  40 A9 00 8D 
0000ADr 1  03 40 A9 0F 
0000C2r 1  A9 C5 8D 02  simplesnd note_c ;i
0000C6r 1  40 A9 00 8D 
0000CAr 1  03 40 A9 0F 
0000DFr 1  A9 9C 8D 02  simplesnd note_e ;mrus
0000E3r 1  40 A9 00 8D 
0000E7r 1  03 40 A9 0F 
0000FCr 1  A9 83 8D 02  simplesnd note_g ;ga
000100r 1  40 A9 00 8D 
000104r 1  03 40 A9 0F 
000119r 1  60            rts
00011Ar 1               
00011Ar 1               delay_long:
00011Ar 1  A0 A7 A2 A0  delay_ms 300
00011Er 1  A9 02 88 D0 
000122r 1  FD CA D0 FA 
00012Br 1  60            rts
00012Cr 1               delay_short:
00012Cr 1  A0 63 A2 02  delay_ms 1
000130r 1  A9 01 88 D0 
000134r 1  FD CA D0 FA 
00013Dr 1  60            rts
00013Er 1               
00013Er 1               .SEGMENT "DATA"
000000r 1               init_apu_regs:
000000r 1  30 08 00 00      .byte $30,$08,$00,$00
000004r 1  30 08 00 00      .byte $30,$08,$00,$00
000008r 1  80 00 00 00      .byte $80,$00,$00,$00
00000Cr 1  30 00 00 00      .byte $30,$00,$00,$00
000010r 1  00 00 00 00      .byte $00,$00,$00,$00
000014r 1  00 0F 00 40      .byte $00,$0F,$00,$40
000018r 1               
000018r 1               
000018r 1               .SEGMENT "CODE"
00013Er 1               ;----------nmi handler-------------
00013Er 1               nmi:
00013Er 1  40               rti
00013Fr 1               
00013Fr 1               ;----------irq handler-------------
00013Fr 1               irq:
00013Fr 1  40            rti
000140r 1               
000140r 1               
000140r 1               .SEGMENT "VECTORS"
000000r 1  rr rr        .word nmi
000002r 1  rr rr        .word reset
000004r 1  rr rr        .word irq
000006r 1               
000006r 1               
Zapisane

Life is brutal, full of zasadzkas and sometimes kopas w dupas.
Krzysiobal
Chorąży
***
Offline Offline

Wiadomości: 140



« Odpowiedz #1 : Października 31, 2015, 14:22:18 »

Niestety, gdyby piosenka była jednym programem, który dostaje kontrolę sterowania na początku i już jej nie oddaje, emulacja byłaby utrudniona (a tworzenie sprzętowych odtwarzaczy – niemożliwe). Na szczęście plik NSF składa się z funkcji init, którą należy wywołać przed uruchomieniem utworu (jako parametr podajemy, który numer piosenki z pliku ma być odtwarzany – w NSF może być zakodowanych do 255 utworów) oraz funkcji play, którą należy wywoływać co 50 Hz. Dlaczego tak? Bo w konsoli procesor dostaje przerwanie co 50Hz od procesora graficznego, więc jest to najlepszy sposób na odmierzanie czasu.

Projekt przestrzeni adresowej
Trudność projektu polegała nie tylko na zgraniu ze sobą dużej liczby podzespołów, czy też implementacji systemu plików FAT16/FAT32 od zera samodzielnie. Pierwsze trudności zaczęły się już na początku, gdy trzeba było zaplanować sobie, jak przestrzeń adresowa procesora oraz pamięć ROM/RAM będzie zorganizowana. Odtwarzacz NSF jest niejako mini-komputerem, który działa pod kontrolą systemu operacyjnego (firmware) napisanego przeze mnie, a system ten oddaje co pewien czas sterowanie programowi (utworowi NSF). System i utwór NSF powinny mieć oddzielne miejsca w pamięci RAM dla zmiennych/stosu, oddzielne też obszary w pamięci ROM gdzie będzie wgrany ich kod. Ponieważ pliki NSF już istnieją i to ja musze się do nich dopasować, najlepszym rozwiązaniem jest to, aby pamięć RAM także była przełączana (aby pod tymi samymi adresami zarówno odtwarzacz NSF jak i utwór muzyczny miały swoje zmienne i ze sobą nie kolidowały).

Projekt mapy pamięci przedstawiłem poniżej:


Widzimy więc, że pamięć RAM jest przełączana (odtwarzacz oraz muzyczka NSF mają oddzielne:
4096 ($0000-$0FFF) + 8192 ($6000-$7FFF) = 12288 bajtów pamięci
oraz wspólne 4096 ($1000-$1FFF) bajtów pamięci.
Ten wspólny obszar pamięci jest bardzo ważny, gdyż pozwala na przekazywanie danych pomiędzy odtwarzaczem a utworem (oba programy widzą te same dane w tym obszarze) ale też uruchamianie funkcji – wystarczy kod funkcji przekopiować do tego adresu i go stamtąd uruchomić.

Problemy:
Assembler – ogólnie sporo kodu do napisania w Assemblerze, który zajmie się obsługą karty SD (niskopoziomowe operacje) ale także obsługa FAT16 i FAT32, kasowanie/programowanie pamięci 29f040, obsługa klawiszy i LCD. Asembler 6502 jest bardzo przyjemny (gdybym kogoś miał uczyć asemblera to wybrałbym właśnie tą platformę). Jednak, gdy program się rozrasta w asemblerze, to ciężko panować nad całością. Ponadto z uwagi na to, że odtwarzacz musi kasować/programować pamięć oraz pogodzić wywoływanie swojego kodu z kodem odtwarzanej muzyki i bankowaniem pamięci, trzeba w całej przestrzeni adresowej było stworzyć taki obszar pamięci RAM, który nie koliduje z żadnym obszarem w odtwarzaczu ani piosence ($1000-$1FFF) i do tego kodu kopiować a następnie wywoływać kod. Takie niskopoziomowe tricki powodują, że jedynie asembler daje radę. Ponadto często potrzeba pisać optymalny kod, aby np. kopiowanie z karty SF do 29F040 trwało możliwie jak najkrócej.
Ponadto assembler 6502 jest dość ubogi – brak mnożenia i dzielenia. Takie operacje jak konwersja na BCD czy operacje na liczbach 16 i 32 bitowych (dodawanie, odejmowanie) trzeba implementować samemu.

XC9572 - Ciągła walka ze zmieszczeniem się w układzie CPLD XC9572. Układ XC9572 zawiera jedynie 72 makrocele. Jedna makrocela to obszar, który może realizować funkcję logiczną obsługującą jedno wejście, wyjście lub pamiętająca jeden bit pamięci. Jest to bardzo niewiele, a przecież oprócz obsługi baków (5 * 8 = 40 makrocel) musi jeszcze starczyć na dekodery adresów, obsługę SPI do karty SD. Od CPU do CPLD poprowadziłem tylko niezbędne sygnały (kilka linii adresowych, całą magistrale danych, linie sterujące).
Ponadto 3 lata temu pisałem kod na XC9572XL, a teraz przesiadłem się na XC9572, bo mam tego więcej, i wymaga jedynie zasilania 5V core+io, zamiast 3.3V jak w XC9572XL. Oba układy mają po 72 makrocele, jednak XC9572 ma mniej połączeń w środku, przez co kod który wejdzie na XC9572XL może się nie zmieścić na XC9572 i sam tego doświadczyłem i musiałem optymalizować.



Generator 50HZ na NE555.
Istota formatu NSF polega na wywoływaniu co 50 Hz procedury play. Zatem procesor 6502 musi co ten okres dostawać przerwanie. Fajnie, jakby dało się tą częstotliwość delikatnie regulować, aby korygować tempo odtwarzanej muzyki. Potrzebowałem niedrogiego i prostego generatora sygnału prostokątnego o regulowanej częstotliwości w zakresu ok 40-70 Hz w celu zmiany nastawy szybkości odgrywanego utworu. Gdybym dysponował potężniejszym układem CPLD/FPGA, odpowiedni generator sygnału można byłoby zsyntetyzować w układzie CPLD/FPGA poprzez podział sygnału zegarowego wejściowego (1.77MHz). Potrzebny byłby jednak dzielnik zliczający do n = 1.77 MHz / 40 Hz = 44000 czyli musiałby się składać z licznika 16 bitowego. Jeśli miałby być programowany (ustawienie podziału za pomoca software-u), to drugi 16 bitowy licznik byłby konieczny do zapamiętania wartości porównywanej z licznikiem.
Dysponując skromnym 72 makrocelowym układem nie mogłem pozwolić sobie na taką rozrzutność i zastosowałem zewnętrzny generator oparty o NE555 (tryb astabilny). Dodatkowo NE555 posiada wejście resetu, którym możemy wyłączać generacje (procesor może więc włączać i wyłączać przerwanie co 50 Hz – bardzo przydatne)
Jakież było moje zdziwienie, gdy układ, pomimo dobranych parametrów wg. obliczeń (wzory i programy ogólnodostępne w internecie) generował sygnał przeszło 2 razy większej częstotliwości niż zakładana. Długo zastanawiałem się co może być nie tak, aż w końcu okazało się, że zastosowane w układzie kondensatory ceramiczne 100 nF, mają tak naprawdę realną (zmierzoną) pojemność ok. 62 nF. Najdziwniejsze jest to, że zmierzyłem 10 kondensatorów 100 nF i wszystkie mają pojemność w okolicach 62 nF. Ciekawe czy producent w specyfikacji faktycznie zabezpieczył się, podając 50% rozrzut!

Wykonywanie kodu podczas kasowania/programowania pamięci
Urządzenie odczytuje z karty SD wybrany plik z muzyką, nagrywa go do pamięci flash 29f040 (uprzednio kasując ją w obszarze przeznaczonym na nagrywanie) i następnie wykonuje nagrany program z muzyką. Ponieważ urządzenie musi wykonywać swój kod z pamięci 29f040, jednocześnie ją kasując, to fragmenty kodu realizujące programowanie tej pamięci są kopiowane do pamięci ram, a stamtąd są dopiero wykonywane.

Połączenie pamięci 29f040 także do zapisu
W pamięci RAM (62256 oraz inne) linia !WE ma priorytet nad linią !OE, tzn. jeśli obie linie są aktywne, to pamięć przestawia się w tryb zapisu. Jest to bardzo duże ułatwienie, gdyż procesor 6502 ma tylko jedną linię informującą o rodzaju wykonywanej operacji (R/!W). Jeśli jednak linię !OE ściągniemy na stałe do GND, a R/!W podłączymy do !WE, a CS! do dekodera to będzie to działć jak należy, gdyż:
CPU wykonuje zapis do RAMU (R/!W = 0) -> !CS = 0, !OE = 0, !WE = 0 -> ZAPIS
CPU wykonuje oczyt z RAMU  (R/!W = 1) -> !CS = 0, !OE = 0, !WE = 1 -> ODCZYT
CPU wykonuje operacje spoza ramu -> !CS = 1 -> Pamięc ODŁĄCZONA od magistrali

Niestety w pamięci Flash (29f040) jest inaczej: jesli !WE = 0 i !OE = 0 to pamięć nie reaguje na komendy zapisu! Tylko jedna z linii !WE/!OE może być na raz aktywna. Dlatego podłączyłem !CS do GND, !OE do !ROMSEL z dekodera, a !WE do R/!W.  Ponadto dekoder na wyjściu daje !ROMSEL=0 tylko gdy CPU chce coś odczytać z obszaru RAMU, a gdy chce zapisać, to daje !ROMSEL=1. Dzięki temu:
CPU wykonuje zapis do ROMU (R/!W = 0) -> !CS = 0, !OE = 1, !WE = 0 -> ZAPIS
CPU wykonuje oczyt z ROMU  (R/!W = 1) -> !CS = 0, !OE = 0, !WE = 1 -> ODCZYT
CPU wykonuje odczyt spoza ROMU (R/!W = 1) -> !CS = 0, !OE=1, !WE = 1 -> pamięć odłączona od magistrali
CPU wykonuje zapis spoza ROMU   (R/!W = 0) -> !CS = 0, !OE=1, !WE = 1 -> ZAPIS (!!!)

Niepokoić może fakt, że gdy CPU chce cos zapisać do obszaru spoza ROMU to pamięć ROM będzie to odbierać jako instrukcje zapisu.
Na szczęście instrukcje zapisu (programowanie/kasowanie) składają się z kilku komend, w których na przemian na linii adresowej pojawiają się kombinacje 010101 oraz 101010, dzięki czemu zapis nawet przypadkowych danych jest bardzo mało prawdopodobny, aby aktywował faktyczny zapis.

Obsługa karty pamięci MMC/SD
Zaimplementowałem od zera samodzielnie komunikację z kartą pamięci MMC/SD po protokole SPI oraz obsługę FAT16/FAT32 wraz z długimi nazwami plików LFN (!!!). Układ CPLD pośredniczy w komunikacji 6502 z MMC/SD gdyż w protokole SPI dane przesyłane są bit po bicie i gdyby sam 6502 miał wystawiać jeden bit w każdym cyklu rozkazowym, trwałoby to wieki.


Procesor chcąc wysłać bajt (8 bitów) do karty SD/MMC, wykonuje cykl zapisu pod adres $3002 i CPLD najbliższe 16 taktów zegara zajmuje się wysłaniem tych danych.
Gdy procesor chce odczytać bajt z karty SD/MMC, wykonuje cykl zapisu pod adres $3003 (MMC_READ_STROBE) a następnie za 16 taktów zegara może odczytać odebrany z karty SD/MMC bajt pod adresem $3002. Zarówno zapis jak i odczyt trwają te kilkanaście taktów, gdyż karty SD/MMC są dość wolnymi urządzeniami (i tak specyfikacja podaje maksymalną częstotliwość na linii SCLK 400 kHz a u mnie jest to CLK/2 = 1.71 MHz / 2 = 855 kHz). Wszystko jednak działa bez zarzutu.

Ponieważ jednak cykl zapisu trwa kilkanaście taktów, CPLD musi sobie zapamiętać (zatrzasnąć) wszystkie bity odebrane od , które później w kolejnych cyklach będzie wysyłał, gdyż w następnym cyklu CPU wystawi już inne dane na magistrale adresową. Normalnie obsługa MMC zajmowałaby około 3 (8 stanów) + 8 (8 bitów) = 11 makrocel, jednak wtedy miałem problem ze zmieszczeniem się w układzie więc CPLD zamiast zapamiętywać 8 bitów, zapamiętuje 7 (pierwszy bit jest od razu wysyłany do karty)

Kłopoty podczas uruchomienia
Układ, jak na każdego elektronika przystało nie działał od pierwszego włączenia, a potem działał bardzo niestabilnie (przytknięcie palca do pamięci FLASH powodowało restart). Myślałem, że albo mam jakieś mikropęknięcie na ścieżkach, albo podstawka nie styka, albo zwarcie, ale nic z tych rzeczy. Potem dopiero mnie olśniło, że do CPLD co prawda podłączone są wszystkie górne linie adresowe od pamięci, ale w kodzie ustawienie stanu na najwyszej z tych linii pominąłem, gdyż nie potrzebuje pełnych 512 kB pamięci, wystarczy 256 kB, a to dodatkowa makrocela sterująca wyjściem zaoszczędzona. Na szczęście nie trzeba było przecinać ścieżki i podłączać  wejścia do masy – można ustawić w opcjach programowania, aby niewykorzystane linie od CPLD były automatycznie podciągnięte do VCC lub ściągnięte do MASY.

Ciekawostki:
Utwory NSF nie mają końca!
Ponieważ pliki NSF są programami a nie sekwencyjnym zapisem dźwięków (nut), nie ma możliwości stwierdzenia, ile dany utwór trwa (z reguły utwory są zapętlone) – problem STOPu. Jedyną możliwością jest wykrywanie dłuższych chwil ciszy.

Czas programowania
Cały cykl od momentu wybrania utworu NSF o rozmiarze 128 kB do chwili, gdy jego odtwarzanie zostanie rozpoczęte (kasowanie pamięci ROM + odczyt z karty SD/MMC + nagranie na flash 29f040) trwa 15 sekund. Myślę, że to dobry wynik, chociaż jeszcze nie starałem się tego specjalnie optymalizować (nie wiem co jest wąskim gardłem – czy odczyt z karty pamięci czy oczekiwanie aż bajt się zaprogramuje czy operacje procesora na licznikach).

Grzanie się układu CPLD XC9572
Układ CPLD XC9572 dość mocno się grzeje. Myślałem, że jest jakieś zwarcie, ale przeglądając internet okazuje się, że nie tylko ja miałem taki problem (a problem wystepował nawet na płytce ewaluacyjnej gdy  do układu nie były podłączone żadne inne elementy).
Jak się potem okazało, producent podaje wzór na prąd pobierany przez układ:
Icc [mA] = MCHP (1.7) + MCLP (0.9) + MC (0.006 mA/MHz) * f,
gdzie:
MCHP = Macrocells in high-performance mode
MCLP = Macrocells in low-power mode
MC = Total number of macrocells used
f = Clock frequency (MHz)
U mnie będzie to Icc = 1.7 * 72 + 0 * 0.9 + 72 * (0.006 mA/MHz) * 1.7 MHz = 139.4  mA + 0.7344 mA = 140 mA
Przy zasilaniu 5V dostajemy  pokażne P = 5 V * 140 mA = 700 mW, stąd problem grzania rozwiązany. Gdyby wykorzystać XC9572XL, który zasilany jest z 3.3V oraz wzór na prąd jest trochę inny (mniejszy), otrzymalibyśmy mniejsze grzanie.

Przerwania raz jeszcze
I tak był problem ze zmieszczeniem się w CPLD XC9572. Wcześniej CPU mógł dać znać CPLD, czy ten ma podawać czy nie sygnał przerwania (50 HZ) od NE555. Jednak było to marnotrawstwo makrocel – jedna makrocela na wejście od NE555, druga na wyjście oraz trzecia na pamiętanie, czy sygnał ma być podawany czy nie. Na szczeście 6502 w Pegasusie/NESie posiada wbudowany 3 bitowy zatrzask (bity D2-D0 zapisane pod $4016 pojawiają się na pinach 39-37 procesora), dzięki czemu sam CPU może bez pomocy CPLD włączać/wyłączać CPLD. Gdybym chciał jeszcze dodać coś do CPLD (a miejsca już brak), mogę w podobny sposób realizować przełączanie pamięci ROM/RAM z obszaru przeznaczonego dla odtwarzacza na obszar przeznaczony dla utworu (obecne rejestry $3004 i $3005)

Obsługa klawiszy
Tak naprawdę sygnał włączający bufor podający stan przycisków na magistralę wcale nie pochodzi od CPLD. 6502 w Pegasusie/NESie posiada specjalny rejestr $4016, którego odczyt powoduję podanie na pinie 35 stanu niskiego do sterowania buforem, a dane z bufora przekazane na magistrale są następnie odczytywane przez 6502 i przekazywane jako odczytane dane z adresu $4016. Dzięki temu mamy za darmo (bez angażowania CPLD) obsługe 8 przycisków. Można podłączyć dodatkowe 8 przycisków przez bufor w analogiczny sposób pod drugi rejestr $4017 (pin 35 od CPU).

Wyświetlacz LCD
Podłączenie wyświetlacza HD44780 do procesora 6502 jest bardzo proste – sam procesor może bezpośrednio czytac/pisać dane z wyświetlacze. Potrzebny jest jedynie sygnał z dekodera adresów, który w przeciwieństwie niż jak w pamięciach, ma mieć stan WYSOKI, jeśli wyświetlacz ma być aktywny. Czytanie z wyświetlacza jest przydatne, gdy chcemy odczytać BUSY FLAG (aby czekać aż poprzednia operacja została zakończona)

Regulacja szybkości odtwarzania piosenek
Podłączając potencjometr zamiast rezystora, określającego częstotliwość generacji NE555 w trybie astabilnym uzyskałem przestrajany generator w zakresie 40-60Hz, dzięki czemu można sobie ustawić szybkość odtwarzania, jaka nam pasuje.

Co zostało i można jeszcze zrobić?
Regulacja glośności
W zasadzie pominąłem ten etap bo i tak dźwiek generowany przez odtwarzacz podłączam do wzmacniacza głośnikowego. Gdyby jednak chcieć podłączyć słuchawki, można pomyśleć o jakimś regulatorze głośności, innym niż potencjometr.

Aktualizacja oprogramowania (firmware) z karty MMC/SD
Obecnie aby zaktualizować program sterujący odtwarzaczem, należy  wyjąć zeń pamięć 29F040, włożyć do programatora, nagrać nonowy program, wyjąć, włożyć z powrotem do urządzenia. Lepiej, gdyby urządzenie potrafiło tuż po uruchomieniu odczytać z karty pamięci obraz oprogramowania, wgrać go do pamięci 29f040 i uruchomić. 3 lata temu zrobiłem coś takiego więc to jest jak najbardziej do wykonania.

Lista ulubionych utworów
Fajnie, gdyby można było dodawać swoje ulubione utwory do playlisty. Playlista musiałaby być zapisywana na karcie pamięci SD/MMC lub we Flashu, gdyż urządzenie nie posiada pamięci RAM podtrzymywanej bateryjnie.

Tytułem zakończenie
Po wielu tygodniach prac, nieprzespanych nocy powstało wymarzone urządzenie zgodne z założonym planem – około 2000 linii kodu w Asemblerze + trochę linii kodu w VHDLu.
Jest to chyba jedyny sprzętowy odtwarzacz NSF na świecie, nie licząc projektu Kevina Hortona sprzed kilkunastu lat:
http://www.kevtris.org/Projects/hardnes/index.html

Zapraszam do dyskusji, oglądania zdjęć i filmów z działania.





https://youtu.be/xJwnrvtit8A
Zapisane

Life is brutal, full of zasadzkas and sometimes kopas w dupas.
dic-sc7
Kapitan
****
Offline Offline

Wiadomości: 345


« Odpowiedz #2 : Listopada 07, 2015, 20:33:46 »

Krzysiu moje najszczersze gratulacje, to wspaniały i okazały projekt. Widać bardzo wyraźnie, ile pracy i czasu trzeba było w niego włożyć. Samo wykonanie bardzo mi pasuje, to mój ulubiony styl jeśli chodzi o konstrukcje.
Kevin Horton zrobił coś podobnego, ale w formie przystawki do CopyNes. Tamten projekt pozostał na etapie wczesnego prototypu, właściwie jako ciekawostka, a opcja w CN niebyła rozwijana.
Szkoda tylko, że zdecydowałeś się zbudować ten odtwarzacz jako urządzenie niezależne. Czyli nadal najbardziej interesującym, innowacyjnym  rozwiązaniem pozostaje TNS i jego kolejne wersje. Kilka lat temu próbowałem nawet pozyskać jeden egzemplarz, ale wówczas autor zdecydowanie odmówił.

http://www2s.biglobe.ne.jp/~tns/nr26740601.html
   
Zapisane
Strony: [1]
  Drukuj  
 
Skocz do:  

Działa na MySQL Działa na PHP Powered by SMF 1.1.11 | SMF © 2006-2008, Simple Machines LLC Prawidłowy XHTML 1.0! Prawidłowy CSS!
Strona wygenerowana w 0.044 sekund z 18 zapytaniami.