Elektroda.pl
Elektroda.pl
X

Wyszukiwarki naszych partnerów

Wyszukaj w ofercie 200 tys. produktów TME
Kategoria: Kamery IP / Alarmy / Automatyka Bram
Montersi
Proszę, dodaj wyjątek elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

Krzysioplayer -odtwarzacz plików NSF z Pegasusa a'la C64 SID,hardware nsf player

phanick 31 Paź 2015 13:52 6297 6
  • Krzysioplayer -odtwarzacz plików NSF z Pegasusa a'la C64 SID,hardware nsf player
    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 owa maszyna osiągnęła tak dużą popularność. Warto tylko przypomnieć, że SID ma jedynie trzy kanały, ale umiejętności programistów potrafiły z niego wycisnąć dźwiękowe cuda. Na dzień dzisiejszy w Internecie krąży kilkadziesiąt tysięcy plików SID (High Voltage SID Collection). Zawierają one zgraną muzykę z gier, ale też własne aranżacje (np. piosenka` Mydełko Fa` czy `I Like Chopin`). Pliki te poprzez emulatory można odtwarzać na PC i delektować się metaliczno-syntezatorowym brzmieniem 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 utwory z gier CodeMasters (autorstwa Allister Brimble’a), czy też 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 NSF. 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 rozbudowane 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 bardziej optymalnie). 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.
    Krzysioplayer -odtwarzacz plików NSF z Pegasusa a'la C64 SID,hardware nsf player

    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.
    Krzysioplayer -odtwarzacz plików NSF z Pegasusa a'la C64 SID,hardware nsf player

    Przykładowo, „kawałek” wlazł kotek na płotek mógłby wyglądać tak:

    Code:

    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               


    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:
    Krzysioplayer -odtwarzacz plików NSF z Pegasusa a'la C64 SID,hardware nsf player

    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ć.
    Krzysioplayer -odtwarzacz plików NSF z Pegasusa a'la C64 SID,hardware nsf player


    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 pomocą 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.
    Krzysioplayer -odtwarzacz plików NSF z Pegasusa a'la C64 SID,hardware nsf player

    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 CPU, 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.

    Pisanie optymalnego kodu na CPLD XC9572
    VHDL daje nam wyrazić to samo na wiele sposobów. Efekt działania może być identyczny, ale jeden kod może zajmować więcej zasobów niż drugi, np.

    Code:

    cpu_d <=  mmc_byte_to_recv                  when cpu_m2 = '1' and cpu_r_nw = '1' and cpu_a(15 downto 12) = "0011" and cpu_a(2 downto 0) = "010"  --read from $3002
             else mmc_rdy & "0000000"          when cpu_m2 = '1' and cpu_a(15 downto 12) = "0011" and cpu_a(2 downto 0) = "011" and cpu_r_nw = '1'
             else (others => 'Z');


    zajmuje więcej zasobów (1 makrocele więcej) niż

    Code:

    with cpu_m2 & cpu_r_nw = '1' & cpu_a(15 downto 12) & cpu_a(2 downto 0) select
       cpu_d <= mmc_byte_to_recv    when "1" & "1" & "0011" & "010",
                mmc_rdy & "0000000" when "1" & "1" & "0011" & "011",
              (others => 'Z')     when others


    Gdy już nie możemy nic więcej poprawić w naszym kodzie, aby zajmował jeszcze mniej makrocel lub wewnętrznych połączeń, należy pobawić się parametrami w środowisku programistyczym. I tak, w opcjach Fit, warto ustawić Implementation Template na Optimize Balance. Przy ustawionej opcji Optimize Density, projekt się nie mieścił!
    Krzysioplayer -odtwarzacz plików NSF z Pegasusa a'la C64 SID,hardware nsf player

    Problemy z programowaniem XC9572
    Często napotkałem bardzo dziwne problemy podczas programowania układu – a to układ raz na kilka razy nie chciał się programować, a to po zaprogramowaniu działał źle (np. podczas transmisji do karty SD/MMC pojawiały się nagle inne bajty niż wynikałoby to z programu). Nawet kasowanie i potem blank-check układu nie działał!
    Należy w programie do nagrywania Impact wybrać nasz układ i zaznaczyć (domyślnie odznaczoną) opcję Override Write Protect
    Krzysioplayer -odtwarzacz plików NSF z Pegasusa a'la C64 SID,hardware nsf player

    Nie działanie niektórych piosenek
    Doszedłem do momentu że większość piosenek działała, ale bywały też takie które nie chciały się odtwarzać. Długo zastanawiałem się, o co chodzi. Pierwszy problem polegał na nieprawidłowym odczytaniu z nagłówka pliku NSF ilości bajtów offsetu które należy dodać przed wgraniem pliku do pamięci ROM (trochę nie rozumiałem zapisu w specyfikacji plików NSF).

    Drugim problemem było to, że na sztywno ustawiałem banki przed uruchomieniem funkcji init i play na wartości z nagłówka pliku NSF. Jeśli funkcja init/play zmieniła banki, to przy następnym wywołaniu znowu były ustawiane wartości startowe. Aż dziwne że z tego powodu nie działały tylko dwa pliki, a w jednym i tak połowa utworów działała! Należało więc po zakończeniu funkcji init/play odczytać ustawione numery banków, a przed następnym wywołaniem – przywrócić je. Niestety dodanie odczytu tych banków w CPLD przekroczyłoby już zasoby. I tu kolejny trick – w pamięci RAM 62256 został fragment niewykorzystanego obszaru. Wystarczy, że przy zapisie przez CPU do adresu $5ff7-$5fff CPLD aktywuje pamięć i numery banków oprócz zapamiętania w CPLD zostaną także zapisane w pamięci RAM. Jeśli dodatkowo CPLD aktywuje pamięć także gdy CPU będzie chciał czytać z tego adresu, to przeczytamy właśnie zapamiętane numery. Tak naprawdę aktywowana jest pamięć RAM przy całym zakresie $5000-$6000 gdyż CPLD dekoduje adresy nie wszystkimi liniami adresowymi. Bardzo sprytne rozwiązanie, a przy okazji zyskujemy kolejny (prawie 8 kB) obszar pamięci RAM pod $5000-$5ff6 który jest wspólny dla odtwarzacza oraz utworu.

    Co zostało i można jeszcze zrobić?
    Regulacja głoś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.

    Krzysioplayer -odtwarzacz plików NSF z Pegasusa a'la C64 SID,hardware nsf player

    Krzysioplayer -odtwarzacz plików NSF z Pegasusa a'la C64 SID,hardware nsf player Krzysioplayer -odtwarzacz plików NSF z Pegasusa a'la C64 SID,hardware nsf player Krzysioplayer -odtwarzacz plików NSF z Pegasusa a'la C64 SID,hardware nsf player Krzysioplayer -odtwarzacz plików NSF z Pegasusa a'la C64 SID,hardware nsf player


    Link


    Fajne!
  • #2 31 Paź 2015 15:41
    horik
    Poziom 13  

    Toż to niemal doktorat! Rozpracowałeś to wszystko w stopniu budzącym mój niekłamany podziw. Widać, że swobodnie operujesz komponentami "cyfrowymi", co dla mnie, "analoga" jest wystarczającym powodem, by otworzyć usta z uznania :)

    A ten klimat... dawno już nie słyszałem tych dźwięków. No i pierwszy raz spotykam się z takim rozwiązaniem, jak muzyka zapisana w postaci programu.

  • #3 31 Paź 2015 16:02
    trol.six
    Poziom 30  

    phanick napisał:
    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

    Hm... nie znam specyfikacji wszystkich kart, ale SD potrzebuje wolniejszego zegara tylko podczas inicjacji. Można potem dać zegar z większą częstotliwością.

  • #4 31 Paź 2015 20:53
    phanick
    Poziom 28  

    Jeszcze z ciekawostek mogę powiedzieć, że cały czas układ nie działał do końca prawidłowo, tzn.tuż po uruchomieniu miał problemy z wykrywaniem karty MMC/SD, dopiero restart powodował poprawne działanie. Próbowałem podłączyć analizator stanów logicznych aby zobaczyć, w czym tkwi problem, ale jak to na złość, z podłączonym analizatorem zawsze działało dobrze. Przed chwilą sprawdziłem i trzymająć palec przy liniach od karty także działało dobrze. Dopiero doznałem olśnienia, że linia MISO to przecież otwarty kolektor, a ja zapomniałem podciągnąć jej do VCC. Po podciągnięciu działa bezbłędnie.

  • #5 01 Lis 2015 05:52
    jackfinch
    Poziom 13  

    Witam
    Opis urządzenia które wykonałeś jest na naprawdę wysokim poziomie. Czyta się go bardzo przyjemnie i jest to opis bardzo logiczny i opisujący szczegółowo poszczególne bloki odtwarzacza. Po takim opisie ciężko o coś pytać, bo wszystko jest dokładnie wyjaśnione. Nie jest to na pewno urządzenie, które każdy by chciał wykonać, ale czytając o nim można wiele rzeczy się nauczyć. Chciałbym tylko zapytać jakiego używasz analizatora stanów logicznych.
    Pozdrawiam

  • #6 01 Lis 2015 22:09
    phanick
    Poziom 28  

    Poprawiłem trochę błędów w opisie, m. in. schemat i mapę pamięci.
    Dodałem też opis pisania optymalnego kodu w VHDLu oraz opis ostatnio napotkanych błędów w odtwarzaczu i rozwiązanie.

    Co do analizatora to zwykły 8 kanałowy SALEAE, sprawdza się dobrze, nie licząc chwytaków pomiarowych, które odpadają od przytwierdzonej nogi scalaka.

  • #7 03 Lis 2015 00:57
    phanick
    Poziom 28  

    Po znalezieniu kolejnych błędów czas na coś nowego - a czasami są one tak ciekawe, że znajduje je tylko przez przypadek, jak np. to, że sprawdzając czy liczba 32 bitowa jest równa zero sprawdzałem tylko jej najmłodszy bajt - chyba z lenistwa. Tą liczbą był numer klastra pliku w FAT32 - wszystko działało dobrze, ale przy jednej piosence się sypało, a wyszło tylko dlatego, że akurat plik miał numer klastra 256, czyli 0h 0h 1h 0h.

    Tym czymś nowym jest to, co tygryski lubią najbardziej, czyli optymalizacja. Powalczymy z szybkością odczytu z karty MMC/SD i programowaniem pamięci. O ile wgrywanie średnich rozmiarów muzyczek (kilka kB) trwa ok. 2-3 sekund, to na największe (128 kB) musimy już poczekać ok. 15-20 sekund.
    Dane z karty są odczytywane w porcjach po 512 bajtów (taki sposób wymusza protokół komunikacji z kartą MMC/SD), więc po odczytaniu 512 bajtów do pamięci RAM, procesor programuję pamięć ROM tymi danymi, po czym odczytuje następne 512 bajtów, itd..
    Dla uproszczenia stworzyłem partycję o klastrach rozmiaru 8 kB, a test przeprowadziłem na pliku o rozmiarze 6272 bajtów, czyli zajmującego jeden klaster (13 sektorów) - nie ma konieczności odczytywania z tablicy alokacji plików następnego klastra, co generowałoby dodatkowe odczyty z karty sd i utrudniało analizę.

    Po wybraniu pliku z muzyką, od momentu skasowania pamięci ROM (ok. 1 sekundy) mija następna sekunda, którą zajmuje nagranie pamięci ROM odpowiednimi danymi, po czym z głośników rozbrzmiewa muzyka. Czas spojrzeć pod analizatorem, co się dzieje na drutach.
    MOSI, SCLK, MISO – sygnały do karty MMC/SD, M2 – zegar procesora 6502 (1.71 MHz).

    Tu widzimy, że cała operacja programowania trwała ok 0.7 sekundy.
    Krzysioplayer -odtwarzacz plików NSF z Pegasusa a'la C64 SID,hardware nsf player

    Czas w jednym sektorze pliku, gdy linia SCLK zmienia stan to czas odczytu z karty sd, a czas gdy nie zmienia – to czas programowania pamięci. Przy okazji widać że podczas odczytu ostatniego sektora na końcu było już sporo zer – sektor nie był do końca wypełniony danymi – linia MISO przez większość czasu trwania odczytu ostatniego sektora jest na masie.

    Przyjrzyjmy się jednak bliżej, co się dzieje podczas odczytu i programowania sektorów. Tu już doskonale widać, żez 60 ms na czas poświęcony na każdy z sektorów tylko ok 12 ms przypada na odczyt z karty SD, a aż 48 ms na programowanie tymi danymi pamięci ROM!
    Krzysioplayer -odtwarzacz plików NSF z Pegasusa a'la C64 SID,hardware nsf player


    Zobaczmy najpierw co dzieje się w czasie odczytu danych z karty MMC/SD:
    Krzysioplayer -odtwarzacz plików NSF z Pegasusa a'la C64 SID,hardware nsf player

    Zgodnie z oczekiwaniem najpierw wysyłany jest rozkaz odczytu sektora (ułamek sekundy), potem karta przetwarza polecenie przez 0.6ms, a potem przez 11 ms odczytywane jest z niej 512 bajtów)

    Przyjrzyjmy się teraz, co dzieje się, gdy procesor odczytuje już te 512 bajtów z karty SD „ciurkem”:
    Krzysioplayer -odtwarzacz plików NSF z Pegasusa a'la C64 SID,hardware nsf player

    Gdy CPLD pobierze z karty 8 bitów, to potem karta przez średnio drugie tyle czasu jest bezczynna - procesor czeka aż cpld odczyta te 8 bajtów z danymi, a potem sam zapisuje je do pamięci ram. Tu można by trochę przyspieszyć.


    A co dzieje się w czasie, gdy procesor programuje bajt po bajcie pamięć ROM? Tutaj warto dodać do analizatora lnię okręślającą rodzaj operacji wykonywanej przez procesor: odczyt/zapis (r/!w). Należy znaleźć odpowiedni moment na przebiegu od początku do końca zapisu takiego bajtu. Można to poznać po tym, gdy linia r/!w jest w stanie niskim przez dwa nastepujące po sobie takty zegara – wtedy procesor wywołuje funkcje zapisu bajtu do pamięci ROM – i odkłada na stos dwa bajty – adres powrotu – stąd te cykle zapisu.
    Czas programowania jednego bajtu trwa 98 us. Mnożąc to przez ilość bajtów dostajemy 98us * 512bajtów = 50ms co by się zgadzało.

    Krzysioplayer -odtwarzacz plików NSF z Pegasusa a'la C64 SID,hardware nsf player

    Dokonałem w międzyczasie drobnych usprawnień – usunąłem zbędne skoki, sprawdzenia, zapisy do pamięci. Po tych optymalizacjach osiągnąłem poprawę o 30% - teraz czas zapisu trwa tylko 64us.
    Krzysioplayer -odtwarzacz plików NSF z Pegasusa a'la C64 SID,hardware nsf player

    Na zakończenie gdy pamięć zostanie zaprogramowana i piosenka włączona, warto z ciekawości przyjrzeć się co dzieje się na magistrali.

    Krzysioplayer -odtwarzacz plików NSF z Pegasusa a'la C64 SID,hardware nsf player

    Ten okres gdy procesor coś intensywnie zapisuje do pamięci, a potem długo nic nie zapisuje powtarza się co 20 ms - bo właśnie co 50 Hz (20 ms) procesor dostaje przerwanie i wywołuję funkcję play, a potem przez resztę czasu nic już nei zapisuje, tylko odczytuje stan przycisków, itp. Wywołanie funkcji play trwa ok. 1.12ms więc (przynajmniej dla tej piosenki) procesor przez większość czasu nudzi się.

 
Promocja -20%
Zamknij 
Wyszukaj w ofercie 200 tys. produktów TME
tme