[CA80][SDCC][Z80] Interfejs RS232 do komputerka CA80 (tymczasowy a'la blog)

Kącik dla elektroniki retro - układy, urządzenia, podzespoły, literatura itp.
Awatar użytkownika
tasza
Expert
Expert
Posty: 952
Rejestracja: czwartek 12 sty 2017, 10:24
Kontaktowanie:

[CA80][SDCC][Z80] Interfejs RS232 do komputerka CA80 (tymczasowy a'la blog)

Postautor: tasza » niedziela 26 maja 2019, 17:08

♬ ☘ Moja muzyka do kodowania ♬ ♬ ♬ ☘
♫ ♩ ♪ Walk In Darkness ⚡ ☘ ⚡ Time To Rise ♪ ♩ ♫
https://youtu.be/hG8Ged5Hb5c


Zakładam, że większość owej pisaniny po pewnych reedycjach i uporządkowaniu wyląduje na bienata, na podstronach właściwych CA80, ale póki co tu chciałabym opisywać to, co spontanicznie się dzieje.

Temat interfejsu szeregowego do komputerka CA80 nie jest nowy, choćby w Radioelektronik 1/94 opisywano całkiem sympatyczną konstrukcję takiej przystawki :arrow: http://bienata.waw.pl/ca804.php Między innymi ten właśnie artykuł za lat mych szczenięcych skutecznie konkurował z koniecznością uczenia się do matury, no cóż - definicja słowa pokusa jest jak widać wielowymiarowa. I nawet coś tam powstało i to dokładnie na bazie kostki 8251A, pamiątkowa fotografia poniżej.

00_zamiast_do_matury.jpg


Sprawa RS232 do CA80 nabrała ponownie rumieńców po skleceniu `Ładowarki do pamięci` no i chyba też po rozpoznaniu pakietu SDCC, narzędzia deweloperskie są, można się zmierzyć z tematem raz jeszcze, a cel ćwiczenia zdefiniowałam następująco: ma powstać przystawka/rozszerzenie do starego CA80 umożliwiające komunikację szeregową na dwóch niezależnych kanałach z typowymi (dla mojego retro) parametrami transmisji. Całość utrzymana w klimatach antycznych, nowobogackie wstawki ograniczone do minimum. Oprócz podstawowej komunikacji via RxD/TxD ma być dostępne proste I/O na okazję eksperymentów z RS485, do przełączania kierunków. Dlaczego akurat do starego CA80? Na trzech laminatach?
No właśnie ze względu na otwartość tej konstrukcji (wyprowadzona szyna systemowa), tego brakuje nowej wersji CA. No i z chwilą sklonowania pcb MIK290 nowy CA80 stracił dla mnie cały urok i smaczek kolekcjonerskiej niszy. Docelowo przystawka ma stać się integralną częścią systemu, podobnie jak płytka MIK89 z kostkami 8255 i Z80-CTC.

Wybór UART... no cóż, tu łatwo nie miałam. Kostkę Intel 8251 obćwiczyłam swego czasu dokładnie, była dodatkowo w użyciu w komputerku dino-85, tak więc nuda to nieco. Dodatkowo, jej integracja z systemem przerwań maskowalnych oznaczałaby konieczność rezygnacji z trybu IM2 ( a to mnie aktualnie intryguje ) lub zagonienie do współpracy kostki Z80-CTC (jako odbiorcy/generatora przerwań), tak czy inaczej fotomontaż. No i dwa kanały to dwa takie układy.
Podobnie wychodzi z 8250 czy 16550, całość zoo w trakcie przeglądu w formie wystawki na fotografii.

01_uarty.jpg


Niby implementacja tych kostek jest prosta, ale to jednak układy z poza rodziny Zilog, pełna integracja wymagałaby kilku dodatkowych zabiegów sprzętowych. I tym prostym sposobem stanęło na tym, że w interfejsie będzie pracował dedykowany układ Zilog, wybór w tym zakresie mamy w sumie niewielki - SIO lub DART. Układów DART mam tylko dwa, więc instynkt samozachowawczy odradził pakowanie się w użycie kostek, których nagły zgon (np. z nieprzemyślanej mej głupoty) skutecznie rozłoży projekt. Układów SIO znalazło się kilka, niemniej jednak w większości posiadały one dość istotną wadę - wygląd. A raczej jego brak. Podpisane MME wyglądają tak, jakby zrobiono je pilnikiem w stodole, to już nasze układy CEMI czy rosyjskie klony wyglądały o niebo lepiej.

99_dygresja_IMG_5721.JPG


Na szczęście znalazł się egzemplarz SIO, który nadaje się do ludzi - tak więc reszta wywodów będzie skoncentrowana na Z80-SIO/0, choć to nie rasowy Zilog ale włoski SGS Microelettronica (potem SGS-THOMSON, finalnie STMicroelectronics).

02_Z80-SIO-0-pokazowy.jpg


W trakcie prac kartkowane namiętnie są dwie pozycje - Zilogowy manual od gawła i po naszemu, kniżka bodajże z bazarku.

03_knizki_IMG_5726.JPG


Testowo roboczy schemat ideowy prezentuje się skromnie, ale to piaskownica to dalszych zabaw:

04_sch_26maj.png


Kostka Z80-SIO posadzona na płytce stykowej nie wymaga póki co dodatkowych elementów, sygnał zegarowy CLK taktujący interfejs od strony procesora pochodzi z CA80, taktowanie rejestrów przesuwnych transmisji szeregowej (RxTxC, itp) zrobiłam sobie z Analog Discovery 2, podobnie jak podgląd tego, co układ nadaje po zaprogramowaniu testowym kodem.

05_sio-test.jpg


No i póki co jest całkiem nieźle, kostka jest rozmowna i AD2 całkiem ładnie pokazuje to, co jest wysyłane przez dwa niezależnie pobudzane kanały:

06_waves.png


Fragment "programu" głównego:

sio_demo_1.c pisze:

Kod: Zaznacz cały

  const char *pMsg[2] = {
      "Tosia",
      "tasza"
    };   
    unsigned char n;   
    sioInit();           
    while (1) {
        for (n = 0; n < strlen( pMsg[0] ); n++ ) {
            putChar( 0, pMsg[0][n] );           
        }
        for (n = 0; n < strlen( pMsg[1] ); n++ ) {
            putChar( 1, pMsg[1][n] );           
        }       
        delay( 10 );                       
    }


A to szkic funkcji putChar() w wersji blokującej, z kontrolą wysłania znaczka:

sio.s pisze:

Kod: Zaznacz cały

SIO_BASE    .equ    0xE8       
SIO_A_DAT   .equ    SIO_BASE+0
SIO_B_DAT   .equ    SIO_BASE+1
SIO_A_CMD   .equ    SIO_BASE+2
SIO_B_CMD   .equ    SIO_BASE+3
        ;
_putChar:   
        ld IY,#2
        add IY,SP
        ld A,0(IY)      ; numer kanału (0,1)
        add A,#SIO_A_CMD  ; SIO_A_CMD+0 lub SIO_A_CMD+1 czyli SIO_B_CMD
        ld C,A          ; będzie od teraz numerem portu       
        ; czekaj aż skończy poprzedni transfer
putChar_wait:       
        ld A,#0
        out(C),A            ; wybierz RR0 wskazanego kanału
        in  A,(C)           ; daj RR0
        bit 2,A             ; czy Transfer Buffer Empty? (D2==1)
        jr Z,putChar_wait   ; to czekaj dalej
        ; przerób _CMD na _DAT czyli C := C-2
        dec C
        dec C               ; w C jest adres portu danych :)               
        ld IY,#2
        add IY,SP
        ld A,1(IY)          ; weź znaczek
        out (C),A           ; i wyślij
        ret


I tu tak ogólnie mam teraz dylemat - jak zaprojektować wygodne API do tej kostki. Widzę trzy konkurencyjne warianty: osobne zestawy nisko i wysokopoziomowych funkcji dla każdego z kanałów (np. putCharA, putCharB), można też (jak w przykładzie powyżej) podawać numer kanału jako parametr wywołania. Można w końcu poprzestać na uniwersalnym putChar() a nazwijmy to kontekst pracy (kanał - A lub B) wskazywać dodatkową funkcją, dziwne to ale gdzieś w tyle głowy mi kołacze. Póki co jest jak widać funkcja z parametrem.
Rozkład rejestrów Z80-SIO jest jak w definicjach .equ powyżej, przy pomocy nieskomplikowanych zabiegów można wskazać zarówno rejestr poleceń jak i danych wybranego kanału przez proste operacje arytmetyczne, co powyższy kod skrzętnie wykorzystuje i funkcja całkiem ładnie działa.


#slowanawiatr
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
___________________________________________ ____ ___ __ _ _ _ _
J​eżeli dadzą ci papier w linie, pisz w poprzek. Juan Ramón Jiménez

Awatar użytkownika
gaweł
Expert
Expert
Posty: 767
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: [CA80][SDCC][Z80] Interfejs RS232 do komputerka CA80 (tymczasowy a'la blog)

Postautor: gaweł » niedziela 26 maja 2019, 20:18

tasza pisze:Testowo roboczy schemat ideowy prezentuje się skromnie, ale to piaskownica to dalszych zabaw:

No fajnie. To mi wywołało pewne wspomnienie dawnej zabawy z układem SIO. Ten układ ma synchroniczny tryb transmisji (DART tego nie ma). To daje bardzo ciekawe możliwości → komunikacja via SDLC.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
tasza
Expert
Expert
Posty: 952
Rejestracja: czwartek 12 sty 2017, 10:24
Kontaktowanie:

Re: [CA80][SDCC][Z80] Interfejs RS232 do komputerka CA80 (tymczasowy a'la blog)

Postautor: tasza » niedziela 26 maja 2019, 20:38

♬ ☘ Moja muzyka do kodowania ♬ ♬ ♬ ☘
♫ ♩ ♪ Walk In Darkness ⚡ ☘ ⚡ A Way to the Stars ♪ ♩ ♫
https://youtu.be/8N_HQVYV6tg


Drobny progress, choć pozajęciowo poobijana nieco i do dziergania już siły nie mam, strzepiki zatem jeno.
Funkcja putStr() wysyłająca w kanał zadany jako pierwszy argument napis (NULL string) o wskazanym jako drugi adresie.
sio.s pisze:

Kod: Zaznacz cały

_putStr:
        ; translacja numer kanału->adres SIO
        ld IY,#2
        add IY,SP
        ld A,0(IY)     
        add A,#SIO_A_CMD
        ld C,A          ; C - cmd wybranego kanału, data jest C-2
        ; adres napisu do wysłania
        ld L,1(IY)      ;low
        ld H,2(IY)      ;high
putStr_wait:       
        ld A,#0
        out(C),A            ; wybierz RR0 wskazanego kanału
        in  A,(C)           ; daj RR0
        bit 2,A             ; czy Transfer Buffer Empty? (D2==1)
        jr Z,putStr_wait
        dec C
        dec C               ; C wskazuje na DAT
        ld A,(HL)           ; weź znaczek z napisu
        cp #0               ; czy NULL?
        ret Z               ; tak, spadaj!
        out (C),A           ; wyślij znaczek
        inc C
        inc C               ; C wskazuje na CMD
        inc HL              ; prt++
        jr putStr_wait      ; i tak dalej

Oczywiście wykorzystanie nowej zabaweczki w praktyce:
sio_demo_1.c pisze:

Kod: Zaznacz cały

void main( void ) {
    const char *pMsg = {
      "my way pole dance, spin around or die\n"
    };   
    sioInit();           
    while (1) {     
        putStr( 0, (unsigned short)pMsg );           
        delay( 10 );                       
    }   
}

Zamiana numer kanału (0/1) na fizyczne adresy rejestrów SIO - jak poprzednio, choć tu więcej uwagi potrzeba, ponieważ całość wykonuje się w pętli, aż wskazywana via HL wartość w pamięci będzie 0x00 (NULL). Funkcja działa całkiem fajnie, co dokumentuje zrzutka ekranu poniżej. Tylko dla jednego kanału, ponieważ drugi kabelek USB/TTL gdzieś zapodziałam, no zdarza się.
00_putStr_test_mypole.png


gaweł pisze:To daje bardzo ciekawe możliwości → komunikacja via SDLC

Czytałam, pamiętam, w sposobnej chwili rozeznam co i jak.

#slowanawiatr
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
___________________________________________ ____ ___ __ _ _ _ _
J​eżeli dadzą ci papier w linie, pisz w poprzek. Juan Ramón Jiménez

Awatar użytkownika
gaweł
Expert
Expert
Posty: 767
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: [CA80][SDCC][Z80] Interfejs RS232 do komputerka CA80 (tymczasowy a'la blog)

Postautor: gaweł » niedziela 26 maja 2019, 21:03

tasza pisze:
gaweł pisze:To daje bardzo ciekawe możliwości → komunikacja via SDLC

Czytałam, pamiętam, w sposobnej chwili rozeznam co i jak.

Warto poznać tą historię, to samoadresująca się komunikacja pakietowa. Niewielki krok i... można zbudować maleńką sieć lokalną.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
gaweł
Expert
Expert
Posty: 767
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: [CA80][SDCC][Z80] Interfejs RS232 do komputerka CA80 (tymczasowy a'la blog)

Postautor: gaweł » poniedziałek 27 maja 2019, 09:43

tasza pisze:Drobny progress, choć pozajęciowo poobijana nieco i do dziergania już siły nie mam, strzepiki zatem jeno.

Na to da się coś zaradzić, wystarczy dać znać, czyli poprosić.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

tapy
Posty: 7
Rejestracja: niedziela 14 kwie 2019, 17:09

Re: [CA80][SDCC][Z80] Interfejs RS232 do komputerka CA80 (tymczasowy a'la blog)

Postautor: tapy » poniedziałek 27 maja 2019, 20:01

SIO genialny układ, gdyby tylko miał w sobie choć niewielkie FIFO... Koniec z marzeniami, podpowiem pewien trik dla budujących systemy Z80 chcących uruchamiać CP/M przy wykorzystaniu tego układu, a którzy borykają się z przełącznikiem odłączającym ROM po załadowaniu programu ładującego system operacyjny. Warto wykorzystać, rzadko używany w transmisji szeregowej z potwierdzeniem sprzętowym (większość zastosowań używa RTS/CTS), wyjście DTR jako pinu sterującego. Jego stan po RESET jest zawsze określony.

Awatar użytkownika
tasza
Expert
Expert
Posty: 952
Rejestracja: czwartek 12 sty 2017, 10:24
Kontaktowanie:

Re: [CA80][SDCC][Z80] Interfejs RS232 do komputerka CA80 (tymczasowy a'la blog)

Postautor: tasza » poniedziałek 27 maja 2019, 22:26

gaweł pisze:Na to da się coś zaradzić, wystarczy dać znać, czyli poprosić.

Ach, no ja dziękuje dobra duszo, ale na trening za mnie nie pójdziesz. A jak się zapomni ochraniaczy to potem tylko Altacet, taki lajf, iguana sama się nie zrobi.

♬ ☘ Moja muzyka do kodowania ♬ ♬ ♬ ☘
♫ ♩ ♪ Silent Force ⚡ ☘ ⚡ Goodbye My Ghost ♪ ♩ ♫
https://youtu.be/8VtULoiyhWw


Kolejny przyrost wieczorny, deczko do przodu, choć jednak szału nie ma.

Najpierw zakomunikuję prawdę objawioną, że Z80-SIO jakby znacznie lepiej nadaje te swoje znaczki, gdy mu się poda na wejścia taktujące TxC przebieg prostokątny, taaaa. Dłuższą chwilę i na granicy paniki sprawdzałam tego druciaka mojego pokracznego z SIO, bo gdy po południu wróciłam do dłubania to on strzelił focha i nie chciał już gadać z maniakalnym uporem wysyłając BREAK. Tylko że w programie WaveForms sierota jedna nie wcisnęłam guzika `play` na generatorze. Brawo ja. Ale to w sumie zadecydowało, że czas skończyć dziadowanie i dorobić lokalny generatorek, a zatem schemat w wersji poniedziałkowej:

00_sch27maj_gen.png


I oczywiście realizacja obok SIO - kwarc 3.072MHz i 7400 produkuje impulsy, 7490 daje mi finalnie na wyjściu Qd sygnał taktujący o częstotliwości 307.2kHz. To podzielone wewnętrznymi dzielnikami SIO ( /16, /32, /64) da mi odpowiednio 19200, 9600 i 4800 baud.

01_gen1.jpg


Funkcyjka inicjująca SIO doczekała się wersji sparametryzowanej i już bardziej nadaje się w ludzi, a zatem:

sio.s pisze:

Kod: Zaznacz cały

_sioInit:       
        ; sioInit (channel, bauds)
        ld IY,#2
        add IY,SP
        ld A,0(IY)      ; numer kanału (0,1)
        add A,#SIO_A_CMD  ; SIO_A_CMD+0 lub SIO_A_CMD+1 czyli SIO_B_CMD
        ld C,A          ; będzie od teraz numerem portu       
        ;
        ld A,#0
        out (C),A           ; WR0
        ;
        ld A,#1
        out (C),A           ; ustaw WR1
        ld A,#0
        out (C),A           ; WR1 := 0
        ;
        ld  A,#4
        out (C),A           ; ustaw WR4       
        ld A,1(IY)          ; weź baudy (bity B7,D6)
        or A,#0x04          ; nałóż 1 stop, no parity
        out (C),A           ;
        ;
        ld  A,#3
        out (C),A           ; ustaw na WR3
        ld  A,#0xC1         ; 8 bit, Rx enable
        out (C),A
        ;
        ld  A,#5
        out (C),A           ; ustaw WR5
        ld  A,#0xEA         ; 8bit, Tx enable
        out (C),A       
        ret


sio.h pisze:

Kod: Zaznacz cały

#define B19200  0x40    // x16
#define B9600   0x80    // x32
#define B4800   0xC0    // x64
extern void sioInit(unsigned char /*nChannel*/, unsigned char /*bauds*/);


To oczywiście stan na tu i teraz sioInit() będzie dalej ćwiczona.

I dla relaksu test, czy to w ogóle działa - nadawanie napisów na dwa kanały z różnymi szybkościami, zrzutka statyczna:

02_zrzut24.png


I programik, jako uzupełnienie teledysku:

sio_demo_1.c pisze:

Kod: Zaznacz cały

const char *pGoodbyeMyGhost[] = {
"I stand at the edge of myself and my health\n",
"The gravity of the situation shows itself\n",
"I can never stay focused the voice deep inside\n",
"It's screaming to me\n",
"I have not failed to run and hide\n",
"Goodbye to you, goodbye my ghost\n",
"Goodbye to you, goodbye my ghost\n",
"Have you every felt crazy\n",
"Like someone you didn't know?\n",
"Now that you're questioning yourself down to the bones\n",
"Overwhelmed by the life we lead and demons we have shown\n",
"Goodbye to you, goodbye my ghost\n",
"Goodbye to you, goodbye my ghost\n",
"Goodbye to you, goodbye my ghost\n", 
"As I sit and stare, it's a statement of my mind\n",
"Release me, as I close my eyes\n",
"I'm drifting through the time\n",
"I can see you breath as I can feel your death\n",
"The memory of you will fade away\n",
"I hope forever...\n",
"     \n",
"     \n",
};

void main( void ) {
    unsigned char m = sizeof( pGoodbyeMyGhost )/sizeof( pGoodbyeMyGhost[0] );
    unsigned char n = 0;
    sioInit( 1, B4800 );
    sioInit( 0, B19200 );               
    while (1) {     
        putStr( n % 2, (unsigned short)pGoodbyeMyGhost[ n ] );                   
        n++;       
        if ( n == m ) {
            n = 0;
        }
        delay( 40 );                       
    }   
}


Bardzie na żywo mam tak:

https://youtu.be/SXwsx9VM5AA

Tematy otwarte na okazję generatorka taktującego transmisje szeregową - czy warto robić go z jeszcze mniejszymi szybkościami? Dostawienie 7493 (podział /16) pozwoli mi zejść na 1200/600/300 baud, z podziałem /8 będzie to 2400/1200/600 - akurat aby pogadać z Commodore C64 lub paroma staroświeckimi sterownikami, co je mam w pracy. Kolejna kwestia to: czy bawić się w elektronicznie sterowaną konfigurację (multiplekser, rejestr|port konfiguracyjny) czy wykorzystać zworki, w końcu - jak często będzie potrzeba korzystania z takich egzotycznych dziś szybkości. Przez chwilę myślałam, aby zagonić do tego wszystkiego Z80-CTC ale...jakoś szkoda mi go marnować do takich spraw, choć ludzie na CTC właśnie takie rozwiązania opierają, tu się muszę zastanowić, bo sygnałów /CS też za wiele luzem nie mam.

#slowanawiatr
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
___________________________________________ ____ ___ __ _ _ _ _
J​eżeli dadzą ci papier w linie, pisz w poprzek. Juan Ramón Jiménez

tapy
Posty: 7
Rejestracja: niedziela 14 kwie 2019, 17:09

Re: [CA80][SDCC][Z80] Interfejs RS232 do komputerka CA80 (tymczasowy a'la blog)

Postautor: tapy » poniedziałek 27 maja 2019, 23:03

Pomysł na generatorek? ATTiny 8 pin :D

PS. Podciągnij SYNC do plusa, bo IM2 to różne cuda się zdarzają. Ja z reguły robię pętle DTR-DCD i CTS-DTR, mniej "druta", ale to Twoje jest też spoko.

Awatar użytkownika
tasza
Expert
Expert
Posty: 952
Rejestracja: czwartek 12 sty 2017, 10:24
Kontaktowanie:

Re: [CA80][SDCC][Z80] Interfejs RS232 do komputerka CA80 (tymczasowy a'la blog)

Postautor: tasza » środa 29 maja 2019, 20:25

tapy pisze:Pomysł na generatorek? ATTiny 8 pin

Dziękuje, ale nie przewiduje tego typu dodatków, jak pisałam wcześniej - ma być klimatycznie.

♬ ☘ Moja muzyka do kodowania ♬ ♬ ♬ ☘
♫ ♩ ♪ Katra ⚡ ☘ ⚡ Delirium ♪ ♩ ♫
https://youtu.be/GHxoiubfSMU


Popołudniowe drobiazgi, pierwsze próby odbioru w sposób blokujący. Na początek jednak drobna modyfikacja układu, taktowanie kanału B jednak zrobiłam przez /16 czyli doszła kostka 7493, daje mi to minimalną szybkość transmisji na poziomie 300 baud. I ma to w sumie sporą zaletę - przy takich szybkościach widać doskonale ile czasu procesora idzie w gwizdek podczas oczekiwania na skompletowanie znaczka w buforze lub jego wysłanie, mam dość wdzięczne poletko doświadczalne.

Schemat na dziś:

00_srodowo29maj_sch.png


Oraz realizacja na płytce stykowej:

01_19200.JPG


Funkcyjka getChar(), oczywiście z jednym parametrem - numerem kanału, wygląda mniej więcej tak:

sio.s pisze:

Kod: Zaznacz cały

_getChar:   
        ld IY,#2
        add IY,SP
        ld A,0(IY)      ; numer kanału (0,1)
        add A,#SIO_A_CMD
        ld C,A         
        ;
getChar_wait:       
        ld A,#0
        out (C),A   ; RR0
        in  A,(C)   ; status
        and A,#1    ; sprawdź D0 Receive Character Available
        jr Z, getChar_wait
        dec C
        dec C
        in  A,(C)   ; weź znaczek z RxD
        ld L,A      ; epilog
        ret


Jak widać, czarów tam nie ma, sprawdzane jest czy w rejestrze jest kompletny znak do odebrania, jak nie - jest sprawdzane w kółko.
Gdy znaczek się ujawni - jest wybierany z rejestru danych i zwracany do warstwy wyżej via rejestr L, proste.

Funkcja główna programu testowego wygląda następująco:

sio_demo_2.c pisze:

Kod: Zaznacz cały

void main( void ) {
    unsigned char rx;
    sioInit( 0, B19200 );
    sioInit( 1, B4800 ); // 300!               
    while (1) {     
        rx = getChar( 1 );
        sysLBYTE( PWYS(2,6), rx );
        putChar ( 1, rx );
    }   
}


Zauważmy, że konfiguracja kanału 1(B) jest na stałą dla 4800 baud, z okazji dzielnika wynikowa szybkość wyniesie 300 baud. W sumie wypadałoby wymyślić jakąś mechanikę na #definę, która zapewni mi operowanie czytelną symboliką zależnie od obecności lub nie dodatkowego zamulającego dzielnika, pomysł w sumie już mam, ale to za chwilę. A sam program to chyba widać co robi - czeka na znaczek i puszcza go nazat do PC, w międzyczasie pokazując wartość jego kodu ASCII w hex na wyświetlacz CA80. Na żywo tak:


https://youtu.be/G3Nn4Vg8G5I


#slowanawiatr
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
___________________________________________ ____ ___ __ _ _ _ _
J​eżeli dadzą ci papier w linie, pisz w poprzek. Juan Ramón Jiménez

Awatar użytkownika
gaweł
Expert
Expert
Posty: 767
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: [CA80][SDCC][Z80] Interfejs RS232 do komputerka CA80 (tymczasowy a'la blog)

Postautor: gaweł » czwartek 30 maja 2019, 02:07

Sorry, może nie do końca w temacie, ale w stosie CD'ów namierzyłem swoje przemyślenia (datowane na 1994 rok) dotyczące Z80, które były protoplastą koncepcji opisanej TUTAJ. Z grubsza wygląda to tak:

Kod: Zaznacz cały

;-----------------------------------------------------------------------------
;       CALL frame handling
;
;               PUSH    ....    ; call param 1
;               PUSH    ....    ; call param 2
;                .
;                .
;                .
;               PUSH    ....    ; call param n
;               CALL    ....
;               zdjac ze stosu paramery
;
;-----------------------------------------------------------------------------
;       example :
;         var
;           var1        : BYTE ;
;           var2        : BYTE ;
;           var3        : BYTE ;
;           var4        : BYTE ;
;           var5        : BYTE ;
;           var6        : BYTE ;
;
;               CALL    Enter           ; start frame
;               LD      BC , 6          ; local var size
;               CALL    OpenLV          ; open local var
;               LD      L , ( IX )      ; --+
;                                       ;   + last call param
;               LD      H , ( IX + 1 )  ; --+
;               LD      ( IX - 14 ) , A ; var6 := ...
;               LD      ( IX - 13 ) , A ; var5 := ...
;               LD      ( IX - 12 ) , A ; var4 := ...
;               LD      ( IX - 11 ) , A ; var3 := ...
;               LD      ( IX - 10 ) , A ; var2 := ...
;               LD      ( IX - 9 ) , A  ; var1 := ...
;               CALL    CloseLV         ; close local var
;               CALL    Leave           ; stop frame
;               RET                     ;
;-----------------------------------------------------------------------------
;-----------------------------------------------------------------------------
;       CALL frame
;               ramka wywolania zachowuje : BC, HL, IX
;                                                 +-----------------+ lower
;                                                 | Enter caller PC |
;                                                 +-----------------+
;                                                 | PROC caller PC  |
;                                                 +-----------------+
;                                                 | call param. 2   |
;                                                 +-----------------+
;                                                 | call param. 1   |
;                                                 +-----------------+ upper
Enter           :
                EX      ( SP ) , HL             ;
                PUSH    IX                      ;
                PUSH    BC                      ;
                PUSH    HL                      ;
                XOR     A                       ;
                LD      H , A                   ;
                LD      L , A                   ;
                ADD     HL , SP                 ;
                LD      BC , 10                 ;
                ADD     HL , BC                 ;
                PUSH    HL                      ;
                POP     IX                      ;
;                                                 +-----------------+ lower
;                                                 | Enter caller PC |
;                                                 +-----------------+
;                                                 |        BC       | <-- IX-8
;                                                 +-----------------+
;                                                 |        IX       | <-- IX-6
;                                                 +-----------------+
;                                                 |        HL       | <-- IX-4
;                                                 +-----------------+
;                                                 | PROC caller PC  | <-- IX-2
;                                                 +-----------------+
;                                                 | call param. 2   | <-- IX
;                                                 +-----------------+
;                                                 | call param. 1   | <-- IX+2
;                                                 +-----------------+ upper
                RET                             ;
;-----------------------------------------------------------------------------
;       RETURN frame
Leave           :
;                                                 +-----------------+ lower
;                                                 | Leave caller PC |
;                                                 +-----------------+
;                                                 |        BC       | <-- IX-8
;                                                 +-----------------+
;                                                 |        IX       | <-- IX-6
;                                                 +-----------------+
;                                                 |        HL       | <-- IX-4
;                                                 +-----------------+
;                                                 | PROC caller PC  | <-- IX-2
;                                                 +-----------------+
;                                                 | call param. 2   | <-- IX
;                                                 +-----------------+
;                                                 | call param. 1   | <-- IX+2
;                                                 +-----------------+ upper
                POP     HL
                POP     BC
                POP     IX
                EX      (SP),HL
;                                                 +-----------------+ lower
;                                                 | Leave caller PC |
;                                                 +-----------------+
;                                                 | PROC caller PC  |
;                                                 +-----------------+
;                                                 | call param. 2   |
;                                                 +-----------------+
;                                                 | call param. 1   |
;                                                 +-----------------+ upper
                RET
;-----------------------------------------------------------------------------
OpenLV          :
;                                                 +-----------------+ lower
;                                                 | OpenLV caller PC|
;                                                 +-----------------+
;                                                 |        BC       | <-- IX-8
;                                                 +-----------------+
;                                                 |        IX       | <-- IX-6
;                                                 +-----------------+
;                                                 |        HL       | <-- IX-4
;                                                 +-----------------+
;                                                 | PROC caller PC  | <-- IX-2
;                                                 +-----------------+
;                                                 | call param. 2   | <-- IX
;                                                 +-----------------+
;                                                 | call param. 1   | <-- IX+2
;                                                 +-----------------+ upper
                XOR     A                       ;
                PUSH    IX                      ;
                POP     HL                      ;
                SBC     HL , BC                 ;
                LD      BC , 8                  ;
                SBC     HL , BC                 ;
                POP     BC                      ;
                LD      SP , HL                 ;
                PUSH    BC                      ;
;                                                 +-----------------+ lower
;                                                 | OpenLV caller PC|
;                                                 +-----------------+
;                                                            .
;                                                            .
;                                                            .
;                                                 +-----------------+
;                                                 |    local var 3  | <-- IX-14
;                                                 +-----------------+
;                                                 |    local var 2  | <-- IX-12
;                                                 +-----------------+
;                                                 |    local var 1  | <-- IX-10
;                                                 +-----------------+
;                                                 |        BC       | <-- IX-8
;                                                 +-----------------+
;                                                 |        IX       | <-- IX-6
;                                                 +-----------------+
;                                                 |        HL       | <-- IX-4
;                                                 +-----------------+
;                                                 | PROC caller PC  | <-- IX-2
;                                                 +-----------------+
;                                                 | call param. 2   | <-- IX
;                                                 +-----------------+
;                                                 | call param. 1   | <-- IX+2
;                                                 +-----------------+ upper
                RET                             ;
;-----------------------------------------------------------------------------
CloseLV         :
;                                                +------------------+ lower
;                                                | CloseLV caller PC|
;                                                +------------------+
;                                                           .
;                                                           .
;                                                           .
;                                                +------------------+
;                                                |    local var 3   | <-- IX-14
;                                                +------------------+
;                                                |    local var 2   | <-- IX-12
;                                                +------------------+
;                                                |    local var 1   | <-- IX-10
;                                                +------------------+
;                                                |        BC        | <-- IX-8
;                                                +------------------+
;                                                |        IX        | <-- IX-6
;                                                +------------------+
;                                                |        HL        | <-- IX-4
;                                                +------------------+
;                                                | PROC caller PC   | <-- IX-2
;                                                +------------------+
;                                                | call param. 2    | <-- IX
;                                                +------------------+
;                                                | call param. 1    | <-- IX+2
;                                                +------------------+ upper
                XOR     A                       ;
                PUSH    IX                      ;
                POP     HL                      ;
                LD      BC , 8                  ;
                SBC     HL , BC                 ;
                POP     BC                      ;
                LD      SP , HL                 ;
                PUSH    BC                      ;
;                                                +------------------+ lower
;                                                | CloseLV caller PC|
;                                                +------------------+
;                                                |        BC        | <-- IX-8
;                                                +------------------+
;                                                |        IX        | <-- IX-6
;                                                +------------------+
;                                                |        HL        | <-- IX-4
;                                                +------------------+
;                                                | PROC caller PC   | <-- IX-2
;                                                +------------------+
;                                                | call param. 2    | <-- IX
;                                                +------------------+
;                                                | call param. 1    | <-- IX+2
;                                                +------------------+ upper
                RET                             ;
;-----------------------------------------------------------------------------
czyli (IX+<coś>) to parametry wywołania, (IX-<coś>) to zmienne lokalne. Ja widziałem w twoim kodzie tego typu użycia. Te moje poszerzają funkcjonalność.
Więcej jest tu:
Z80.ZIP
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
tasza
Expert
Expert
Posty: 952
Rejestracja: czwartek 12 sty 2017, 10:24
Kontaktowanie:

Re: [CA80][SDCC][Z80] Interfejs RS232 do komputerka CA80 (tymczasowy a'la blog)

Postautor: tasza » niedziela 02 cze 2019, 21:40

gaweł pisze:Sorry, może nie do końca w temacie...

Ależ to jak najbardziej w temacie, bo to cały czas krąży wokół Z80. Tylko że w przypadku SDCC interface assembler-C został już zdefiniowany i tu pola na własną pomysłowość (jak opisywana alokacja zmiennych lokalnych) za bardzo nie ma. No ale jak ktoś od zera taki rozwiązanie projektuje i nic go nie wiąże (nie ogranicza) to tylko korzystać, dziękuje zatem.

♬ ☘ Moja muzyka do kodowania ♬ ♬ ♬ ☘
♫ ♩ ♪ Cold Insight ⚡ ☘ ⚡ Even Dies a Sun ♪ ♩ ♫
https://youtu.be/u5T1jQ-Yucs


Naturalnym etapem ewolucji całej tej zabawki jest wplecenie układu Z80-SIO w system przerwań procesora Z80 pracujący w trybie IM2 - wektoryzowane przerwania ze sprzętowo ustalonymi priorytetami. Najpierw drobiazg natury sprzętowej i tu pozwolę sobie nieco pogderać. Wiele uwagi poświęcono w tomikach MIK łańcuchowemu połączeniu urządzeń zgłaszających przerwania, sporo było przykładów stabilizacji daisy-chain i interakcji wyprowadzeń IEI-IEO kolejnych kostek systemu. No i co zastajemy na płytce MIK89 z układem Z80-CTC? Nóżkę 11 układu CTC czyli IEO wiszącą w powietrzu, podłączoną zupełnie do niczego. Wejście IEI kostki CTC ma trwale wysoki stan logiczny, układ uzyskuje zatem najwyższy możliwy (po NMI) priorytet w systemie, no ale co z kolejnymi układami? CTC,SIO? Dziwne to jest dla mnie, tym bardziej, że pin 38 systemowego złącza jest sobie luzem i aż się prosi, aby tam ten sygnał doprowadzić celem dalszego wykorzystania. No i tak właśnie zrobiłam, oczywiście kawałek drutu okazał się koniecznością i odrobinę psuje wystawkę. Szczęściem jest pomiędzy płytkami i w sumie to go nie widać.

IMG_5732.JPG


Po tej drobnej poprawce kostka Z80-SIO wpasowana jest w łańcuch urządzeń, priorytet ma niższy niż liczniki układu CTC, ale do celów zabawowych wystarczy całkowicie.

Oprogramowanie też uległo pewnej ewolucji, przede wszystkim rozrosła się tabela wektorów przerwań. O ile wykorzystanie Z80-CTC wymuszało przytulenie tabeli wektorów do adresu modulo 8, to dołożenie SIO (o ośmiu wektorach /2 kanały x 4 zdarzenia/) narzuca konieczność wyrównania tabelki do 32 bajtów. Od początku licząc, struktura będzie miała 4 wektory dla CTC, potem nieco wypełniających trocin, potem od 16-tego bajtu licząc - osiem wektorów dla zdarzeń od kanałów A i B. W praktyce wygląda to następująco:

sio.s pisze:

Kod: Zaznacz cały

        ;
        .globl _irqHandlersTable                       
        .area   _DATA   
        .bndry 32     ; co 32 bajty
_irqHandlersTable:
        .ds 2        ; CTC, kanal 0
        .ds 2        ; CTC, kanal 1
        .ds 2        ; CTC, kanal 2
        .ds 2        ; CTC, kanal 3       
        ;---------
        .ds 8        ; wata-wypychacz
        ;---------       
        .ds 2        ; SIO, Ch B Transmit Buffer Empty
        .ds 2        ; SIO, Ch B External/Status Change
        .ds 2        ; SIO, Ch B Receive Character Available
        .ds 2        ; SIO, Ch B Special Receive Condition
        ;---------       
        .ds 2        ; SIO, Ch A Transmit Buffer Empty
        .ds 2        ; SIO, Ch A External/Status Change
        .ds 2        ; SIO, Ch A Receive Character Available
        .ds 2        ; SIO, Ch A Special Receive Condition
        ;               


Zauważmy, że tak przygotowana tabela zapewnia zgodną koegzystencję handlerów przerwań i CTC i SIO, zainicjowanie tabeli i wskazanie procedurek obsługi wygląda mniej więcej tak:

sio_demo_irq1.c pisze:

Kod: Zaznacz cały

   irqHandlersTable[ IRQ_CTC_CH0 ] = (unsigned short)&OnCounterZero;
    irqHandlersTable[ IRQ_CTC_CH1 ] = (unsigned short)&OnCounterOne;
    irqHandlersTable[ 2 ] = 0x0000;
    irqHandlersTable[ 3 ] = 0x0000;                   
    irqHandlersTable[ IRQ_SIO_CHB_TxEmpty ] = 0x0000;
    irqHandlersTable[ IRQ_SIO_CHB_ExtChange ] = 0x0000;
    irqHandlersTable[ IRQ_SIO_CHB_RxAvailable ] = (unsigned short)&OnReceiveB;
    irqHandlersTable[ IRQ_SIO_CHB_SpecialCond ] = 0x0000;           
    irqHandlersTable[ IRQ_SIO_CHA_TxEmpty ] = 0x0000;
    irqHandlersTable[ IRQ_SIO_CHA_ExtChange ] = 0x0000;
    irqHandlersTable[ IRQ_SIO_CHA_RxAvailable ] = 0x0000;
    irqHandlersTable[ IRQ_SIO_CHA_SpecialCond ] = 0x0000;     


Inicjacja wektorów CTC była omawiana, nie będę się powtarzać, słowo natomiast o wskazaniu układowi Z80-SIO wymienionych powyżej procedurek:

sio_demo_irq1.c pisze:

Kod: Zaznacz cały

#define SIOIRQOFFSET(loAddr)   LOBYTE(loAddr)|0x10
sioSetIrqHandlersTable( SIOIRQOFFSET( irqHandlersTable ) );


Ciało procedurki-instalatora machnęłam w assemblerze:
sio.s pisze:

Kod: Zaznacz cały

_sioSetIrqHandlersTable:
        ld  A,#2
        out (SIO_B_CMD),A   ; ustaw WR2 kan. B
        ld IY,#2
        add IY,SP
        ld A,0(IY)          ; dolny bajt adresu tab.irq
        out (SIO_B_CMD),A   ; nowy wektor
        ; enable Rx,Tx int.
        ld A,#1             ; WR1
        out (SIO_B_CMD),A
        ld A,#0x1C         
        out (SIO_B_CMD),A   
        ret;



Tu jako ciekawostkę wspomnę, że Z80-SIO jest skonfigurowany tak, że modyfikuje zapisaną wartość pierwotnego wektora (adresu w tabeli) i samodzielnie wskazuje odpowiedni do zdarzenia handler, to konsekwencja ustawienia bitu D2 (Status Affects Vector) w rejestrze WR1.

Program demonstracyjny posiada trzy procedury obsługi przerwań - dwa liczydełka na bazie Z80-CTC, które napędza działający jakkolwiek Analog Discovery 2 oraz procedurkę reagująca na odebranie znaczka w kanale B kostki Z80-SIO. Liczniki są proste:

sio_demo_irq1.c pisze:

Kod: Zaznacz cały

unsigned short volatile nCntr0 = 0;
unsigned short volatile nCntr1 = 0;
void OnCounterZero(void) __interrupt {
    nCntr0++;
    EI();
}
void OnCounterOne(void) __interrupt {
    nCntr1++;
    EI();
}


Odbieranie znaczków w przerwaniach w sumie też jest całkiem przyjemne, udało mi się sklecić całość w C, niezbyt to urodziwe, ale na początek wystarczy:

sio_demo_irq1.c pisze:

Kod: Zaznacz cały

volatile unsigned char rxBuffer[64] = "";
volatile unsigned char rxIndex = 0;
volatile unsigned char rxReady = 0;
void OnReceiveB(void) __interrupt {
    unsigned char newChar = getChar( 1 );
    if ( newChar == 0x0D ) {
        rxReady = 1;
        rxIndex = 0;
    }
    else {
        rxBuffer[ rxIndex ] = newChar;
        rxIndex++;
        if ( rxIndex == sizeof(rxBuffer) ) {
            rxIndex = 0;   
        }
    }
    EI();   
}


Jak widać procedurka chomikuje przychodzące znaki w buforze, gdy uzbiera pod korek - zaczyna od początku. Gdy nadeślemy znak końca linii - bufor jest przekazywany do analizy w pętli głównej - decyduje o tym flaga rxReady. A program główny taki:

sio_demo_irq1.c pisze:

Kod: Zaznacz cały

 IM2();
    EI();
    putStr( 1, "\n\nno to lecimy\n\n" );                           
    while (1) {   
        sysLADR( PWYS(4,4), nCntr1 ); // po lewo
        sysLADR( PWYS(4,0), nCntr0 ); // prawo
        if ( rxReady == 1 ) {
            putStr( 1, "\necho:" );
            putStr( 1, rxBuffer );
            putStr( 1, "\n" );
            if ( strcmp( rxBuffer, "zero" ) == 0 ) {
                nCntr0 = 0;       
                putStr( 1, "licznik zerowy skasowany\n" );               
            } else if ( strcmp( rxBuffer, "jeden" ) == 0 ) {
                nCntr1 = 0;       
                putStr( 1, "licznik pierwszy skasowany\n" );                               
            } else {
                putStr( 1, "nie wiem...\n" );                                               
            }
            memset( rxBuffer, 0x00, sizeof(rxBuffer) );
            rxReady = 0;           
        }
    }   


Przez znakomitą większość czasu wyświetlany jest stan liczników od CTC, gdy w buforze znajdzie się napis do sprawdzenia porównywany jest z dwoma wzorcami - zależnie od wartości kasowany jest pierwszy albo drugi licznik. Jak program nie wie co zrobić - właśnie to pisze na terminalu, następnie wraca do pokazywania liczydeł. Na żywo zabawka działa tak:

https://youtu.be/iQti9V4gDYU

No i to w zasadzie koniec przygody z Z80-SIO, przynajmniej w zakresie zapoznania z kostką i melanżu z CA80. Dalej czeka mnie może niezbyt twórcze, ale jednak konieczne do wykonania rzemiosło - przygotowanie małej pcb tak aby zgrabnie wpasować układ w trójpłytkową konstrukcję starego CA80.

#slowanawiatr
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
___________________________________________ ____ ___ __ _ _ _ _
J​eżeli dadzą ci papier w linie, pisz w poprzek. Juan Ramón Jiménez

Awatar użytkownika
tasza
Expert
Expert
Posty: 952
Rejestracja: czwartek 12 sty 2017, 10:24
Kontaktowanie:

Re: [CA80][SDCC][Z80] Interfejs RS232 do komputerka CA80 (tymczasowy a'la blog)

Postautor: tasza » piątek 07 cze 2019, 18:27

♬ ☘ Moja muzyka do kodowania ♬ ♬ ♬ ☘
♫ ♩ ♪ Elysion ⚡ ☘ ⚡ Never Forever ♪ ♩ ♫
https://youtu.be/xOk4ueekOkQ


No i nie ma się co zarzekać jak żaba błota, że niby koniec i że nigdy, bo pomysły się lęgną niczym muszki owocówki, stąd i krótki, ale zawsze - odcinek kolejny o przerwaniach nadawczych z Z80-SIO.

nadawanie znaczków w przerwaniach

Czarów tu nie ma, a przynajmniej gdy posiadamy w miarę oprogramowaną infrastrukturę do obsługi przerwań w trybie IM2. Dwa słowa jak to właściwie działa, a mianowicie: po odpowiednim skonfigurowaniu kostka Z80-SIO jest w stanie zgłosić wektoryzowane przerwanie maskowalne gdy zakończy wysyłanie znaczka z bufora wyjściowego. W obsłudze tego zdarzenia można na przykład ustawić wskaźnik na kolejny znaczek i podać go do UART - to zapewni nam wysyłkę danych w tle, oczywiście należy pilnować momentu, że znaczki się skończyły i należy ten przerwaniowo-nadawczy samograj wyłączyć. No i teraz konkrety - najpierw pomocnicza funkcyjka do włączania/wyłączania przerwań od Rx i Tx kostki Z80-SIO dla wybranego kanału:

sio.s pisze:

Kod: Zaznacz cały

_sioIrqEnable:
        ; 1100 1110
        ld IY,#2
        add IY,SP
        ld A,0(IY)          ; numer kanału (0,1)
        add A,#SIO_A_CMD   
        ld C,A         
        ;
        ld A,#1             ; WR1
        out (C),A
        ld A,1(IY)          ; maska na Tx / Rx     
        or A,#0x18
        out (C),A       
        ret


Funkcja współpracuje z dwoma stałymi określającymi selektywnie jakie przerwania mają być obsługiwane:

sio.h pisze:

Kod: Zaznacz cały

#define IRQ_RX      0x04
#define IRQ_TX      0x02
#define IRQ_NONE    0x00


Oczywiście pojawia się nowy element w tabeli handlerów przerwań:

sio_demo_irq2.c pisze:

Kod: Zaznacz cały

irqHandlersTable[ IRQ_SIO_CHB_TxEmpty ] = (unsigned short)&OnSendNextChar;


A tak prezentuje się właściwa procedura obsługi przerwania nadawczego:

sio_demo_irq2.c pisze:

Kod: Zaznacz cały

char **pTxDLines = NULL;
volatile unsigned char nLineId = 0;
volatile unsigned char nCharId = 0;

void OnSendNextChar( void ) __interrupt {   
    if ( pTxDLines != NULL ) {
        nCharId++;
        if ( *(*(pTxDLines+nLineId)+nCharId) == 0 ) {
            nCharId = 0;
            nLineId++;
            if ( *(pTxDLines+nLineId) == NULL ) {
                pTxDLines = NULL;
                sioIrqEnable( 1, IRQ_RX );                   
                EI();
                return;
            }           
        }   
        putChar( 1, *(*(pTxDLines+nLineId)+nCharId) );                   
    }
    EI();       
}


Jak widać, handler bazuje na ustawionym gdzieś na zewnątrz globalnym adresie na tabelę napisów pTxDLines, do indeksacji elementu tabeli (napisu) i znaczka w tejże - są dwa pomocnicze indeksy, resztę załatwia stosunkowo prosta arytmetyka wskaźników. Ważne jest to, że po wysłaniu ostatniego znaku z ostatniego napisu w tabeli należy samograja wyłączyć - zabronić przerwań od TxD, bo to nam zablokuje możliwość wysyłania znaków w klasyczny sposób, z oczekiwaniem.

Program główny to w większości (w sensie objętości) słowa 'Never Forever' kapeli Elysion:

sio_demo_irq2.c pisze:

Kod: Zaznacz cały

const char *pNeverForever_Elysion_lyrics[] = {
    "I'm standing alone in the rain\n",
    "Facing the fear within\n",
    "Inside nothing but pain\n",
    // . . .
    NULL
};


Równie ważna jak nuty jest funkcja inicjująca wysyłanie bufora tekstowego:

sio_demo_irq2.c pisze:

Kod: Zaznacz cały

void sendTextBuffer( char **pStrings ) {
    pTxDLines = pStrings;
    nLineId = 0;
    nCharId = 0;   
    sioIrqEnable( 1, IRQ_RX + IRQ_TX );   
    putChar( 1, *(*pTxDLines) );
}


Tu ustawiany jest globalny wskaźnik na dane oraz zerowane są liczniki linii i znaków, w końcu odblokowywane są przerwania od UART i wysyłany jest pierwszy, inicjalny znaczek. Tak na upartego można by zastąpić wywołanie putChar() zwykłym przypisaniem do SIO_B_DAT, podobnie można zrobić w handlerze OnSendNextChar(). No ale skoro już są gotowe funkcje, to nie róbmy wioski i ich używajmy, bez chodzenia na skróty.

W funkcji głównej main() mamy pierwszy start nadawania funkcją sendTextBuffer(), potem jego ponawianie po zakończeniu poprzedniego cyklu na podstawie wyzerowanego wskaźnika na dane. W tak zwanym międzyczasie na wyświetlaczu CA80 pokazywany jest adres wskazanego bufora danych do wysyłki (pola 7-4) oraz aktualne numery linii (pola 3-2) i znaczka w bieżącej linii (pola 1-0), kod takowy:

sio_demo_irq2.c pisze:

Kod: Zaznacz cały

    IM2();
    EI();
    sendTextBuffer( pNeverForever_Elysion_lyrics );       
    while (1) {   
        sysLADR( PWYS(4,4), (unsigned short)pTxDLines );
        sysLBYTE( PWYS(2,2), nLineId );
        sysLBYTE( PWYS(2,0), nCharId );
        if ( pTxDLines == NULL ) {
            delay( 20 );
            sendTextBuffer( pNeverForever_Elysion_lyrics );           
        }
    }   


Na upartego, zamiast delay(),mogłabym na terminalu wypisać jakiś komunikat, że ponawiam nadawanie. Klasyczna, blokująca pustStr() zadziała tu znakomicie i bez efektów ubocznych, ponieważ z chwilą nadania ostatniego znaczka wyłączam generowanie przerwań od TxD...

Zabawka korespondująca z teledyskiem ze wstępu na żywo pracuje sobie tak:

https://youtu.be/XK_1sV6qJIs


#slowanawiatr
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
___________________________________________ ____ ___ __ _ _ _ _
J​eżeli dadzą ci papier w linie, pisz w poprzek. Juan Ramón Jiménez

Awatar użytkownika
tasza
Expert
Expert
Posty: 952
Rejestracja: czwartek 12 sty 2017, 10:24
Kontaktowanie:

Re: [CA80][SDCC][Z80] Interfejs RS232 do komputerka CA80 (tymczasowy a'la blog)

Postautor: tasza » wtorek 11 cze 2019, 17:45

♬ ☘ Moja muzyka do kodowania ♬ ♬ ♬ ☘
♫ ♩ ♪ Dawn Of Solace ⚡ ☘ ⚡ Dead Air ♪ ♩ ♫
https://youtu.be/JO1k4ZJjgFY


Od zarania dziejów kodującą część ludzkości ciekawiło niezmiernie, co też pracowicie wydziergany program tak naprawdę wyrabia, którym if-em poszedł i dlaczego. I tak pewnie wymyślono logowanie do pliku. W systemie CA80 póki co pamięci masowej nie mam, ale skoro w Z80-SIO są dwa kanały szeregowe to można sobie wyobrazić oddelegowanie jednego do roli 'debug logu' i przy jego pomocy przesyłać informacje diagnostyczne na terminal. Uzyskany tak podział zadań jest dość klarowny: kanał A zajmie się komunikacją biznesową, czyli przesyłaniem tekstów piosenek `Dawn of Solace` z prędkością 300 baud, kanał B dla odmiany, pracujący na 38.4kbaud będzie monitorował system, bazgrając po drugim terminalu.

Schemat uległ drobnym przeprowadzkom, głównie w kontekście zasilania kanałów Z80-SIO sygnałem zegarowym. Miałam szczere chęci podnieść szybkość logu do 76800 bodów (i jest to w tym układzie sprzętowo osiągalne), ale niestety moja przelotka USB/RS232-TTL z Biedronki odmówiła współpracy i pozostało 38.4kbd.

00_schemat_debug_zrzut27.png


Odrobina niby teorii, a mianowicie: co do zasady każdy pomiar wpływa na obiekt pomiaru. To wynika z zasady zachowania energii i można dowolnie długo o tym filozofować. Dołączenie kodu logującego też jest swego rodzaju pomiarem, jego dołączenie zmienia nam środowisko pracy programu oraz jest on wykonywany kosztem pierwotnego programu, konsumując cenny czas procesora i zasoby systemu (pamięć, sieć, dysk, łącza komunikacyjne), wpływ zatem jest.

Pomysł na makro logujące zaczerpnęłam z tejże stronki, całkiem fajnie opisana technika logowania z C, proszę: :arrow: define-macro-for-debug-printing-in-c W kolejnym demku widzimy mocną parafrazę tego opracowania, a mianowicie moje szeregowe makro logujące:

sio_demo_irq3_debug.c pisze:

Kod: Zaznacz cały

#define DEBUG 1

#ifdef DEBUG
    unsigned char szDebugTxtBuff[ 64 ];
    #define debugLog(fmt, ...)\
        do {\
            if ( DEBUG == 1 ) {\
                USER8255_PA = 1; \
                sprintf( szDebugTxtBuff, "[%s][%d]",__FILE__,__LINE__); \
                putStr (1, szDebugTxtBuff );\
                sprintf( szDebugTxtBuff, fmt, __VA_ARGS__);\
                putStr (1, szDebugTxtBuff );\
                putStr (1, "\n" );\
                USER8255_PA = 0; \
            }\
        } while (0)
#else
    #define debugLog(fmt, ...) do {} while (0)
#endif


I tu oczywiście komentarz - przy braku definicji DEBUG - nic się nie dzieje, no może poza pustymi do-while, które i tak kompilator wytnie w ramach optymalizacji i detekcji martwego kodu. Dla wskazanego DEBUG zaczynają się sztuki.
Po pierwsze - makro o zmiennej ilości argumentów ściśle łączy się z biblioteczną funkcją sprintf(), jest intuicyjnie jasne, że ten kod będzie ciężki, zarówno w sensie objętości jak i czasu wykonania. No i proszę - budowanie programu testowego z DEBUG i bez - widać sporą różnicę w rozmiarze kompilatu.

01_kompilacja_zrzut28.png


Przy okazji, trzy teksty do muzyki plus kod sprintf() zbliżył się do granicy moich 8kb pamięci RAM od adresu 0x8000, aby nie przesadzić napisałam sobie drobny wyświetlacz objętości wynikowej, jest wpleciony w wywołanie make:

showSize.sh pisze:

Kod: Zaznacz cały

#!/bin/bash
str=`grep '_CODE ' *.map | grep 'REL,CON' | awk '{ print $5 }'`
bytes=${str/./}
left=$((8192-bytes))
echo ">> (!!!) Final size is $bytes bytes, $left bytes left"


Można też zauważyć, że wejście w kod debugowy ustawia PA.0 na 1, wyjście tę jedynkę gasi, podobnie zmodyfikowałam dla testów assemblerową funkcję putStr(), tam dla odmiany intro-outro sygnalizuje dostawiony bit PA.1, to na okazję obserwacji czasu wykonania na oscyloskopie.

sio.s pisze:

Kod: Zaznacz cały

_putStr:
        ld A,#3
        out (0xE0),A
        ; translacja numer kanału->adres SIO
        ld IY,#2
        add IY,SP
        ld A,0(IY)     
        add A,#SIO_A_CMD
        ld C,A          ; C - cmd wybranego kanału, data jest C-2
        ; adres napisu do wysłania
        ld L,1(IY)      ;low
        ld H,2(IY)      ;high
putStr_wait:       
        ld A,#0
        out(C),A            ; wybierz RR0 wskazanego kanału
        in  A,(C)           ; daj RR0
        bit 2,A             ; czy Transfer Buffer Empty? (D2==1)
        jr Z,putStr_wait
        dec C
        dec C               ; C wskazuje na DAT
        ld A,(HL)           ; weź znaczek z napisu
        cp #0               ; czy NULL?
        jp Z,putStr_exit               ; tak, spadaj!
        out (C),A           ; wyślij znaczek
        inc C
        inc C               ; C wskazuje na CMD
        inc HL              ; prt++
        jr putStr_wait      ; i tak dalej
putStr_exit        :
        ld A,#1
        out (0xE0),A
        ret


Funkcja debugLog() została wpleciona w aplikację w że tak ujmę - co ciekawszych miejscach, jest także wywoływana z wnętrza handlera przerwania od pierwszego kanału SIO, przykłady:

sio_demo_irq3_debug.c pisze:

Kod: Zaznacz cały

void OnSendNextCharA( void ) __interrupt {   
    if ( pTxDLines != NULL ) {
        nCharId++;
        if ( *(*(pTxDLines+nLineId)+nCharId) == 0 ) {
            nCharId = 0;
            nLineId++;
            debugLog( "current line of %04X lyrics: %d", pTxDLines, nLineId );                       
            if ( *(pTxDLines+nLineId) == NULL ) {
                debugLog( "line of lyrics - last one, reset %s", "" );                                       
                pTxDLines = NULL;
                sioIrqEnable( 0, IRQ_NONE );                   
                EI();
                return;
            }           
        }   
        putChar( 0, *(*(pTxDLines+nLineId)+nCharId) );                   
    }
    EI();       
}


sio_demo_irq3_debug.c pisze:

Kod: Zaznacz cały

    IM2();
    EI();   
    debugLog( "Z80 in IM2 mode %s", "" );                   
    lyricsIdx = 0;
    sendTextBuffer( allSongs[ lyricsIdx ] );               
    while (1) {   
        if ( pTxDLines == NULL ) {
            lyricsIdx++;
            if ( allSongs[ lyricsIdx ] == NULL ) {
                debugLog( "song repeat 'cause of %02X", lyricsIdx );
                lyricsIdx = 0;   
            }                                   
            debugLog( "selecting song: %04X", allSongs[ lyricsIdx ] );                           
            sendTextBuffer( allSongs[ lyricsIdx ] );           
        }
        sysLADR( PWYS(4,4), (unsigned short)pTxDLines );
        sysLBYTE( PWYS(2,2), nLineId );
        sysLBYTE( PWYS(2,0), nCharId );
    } }


No i zabawka w działaniu, najpierw zrzutki z oscyloskopu:

02_wave.png


Widać boleśnie prawdziwie ile czasu zajęło wywołanie funkcyjki debugLog(), to się liczy w wielokrotnościach 50ms, czyli jest dość nieciekawie. Powstało pytanie = ile z tego czasu jest konsumowane na przygotowanie danych do wysłania - funkcja formatująca sprintf() a ile zajmuje fizyczna komunikacja. I stąd drugi zestaw oscylogramów, dolny przebieg obrazuje tylko i wyłącznie wysyłkę z szybkością 38.4kbd

03_wave.png


I z zabawy tej wniosek prosty - takie naiwne w sumie podejście do logowania po RS232 nie ma racji bytu, szczególnie z wykorzystaniem bibliotecznych funkcji typu sprintf() etc. Wypadałoby spreparować własny zestaw diagnostycznych procedurek do wysyłania napisu, bajtu, słowa, etc, no i oczywiście przejść na najwyższą z możliwych w danych warunkach prędkość transmisji. Myślę, że wtedy udałoby się uzyskać akceptowalne czasy wykonania.

Na koniec tradycyjnie filmik, zwróćmy też uwagę na...czas kompilacji programu, widać delikatne zamulenie w make, to włączony DEBUG i dociągane są funkcje biblioteczne.

https://youtu.be/_OeMUZ0AvBk


#slowanawiatr
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
___________________________________________ ____ ___ __ _ _ _ _
J​eżeli dadzą ci papier w linie, pisz w poprzek. Juan Ramón Jiménez

Awatar użytkownika
gaweł
Expert
Expert
Posty: 767
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: [CA80][SDCC][Z80] Interfejs RS232 do komputerka CA80 (tymczasowy a'la blog)

Postautor: gaweł » środa 12 cze 2019, 00:52

tasza pisze:

Kod: Zaznacz cały

#define DEBUG 1

#ifdef DEBUG
    unsigned char szDebugTxtBuff[ 64 ];
    #define debugLog(fmt, ...)\
        do {\
            if ( DEBUG == 1 ) {\
                USER8255_PA = 1; \
                sprintf( szDebugTxtBuff, "[%s][%d]",__FILE__,__LINE__); \
                putStr (1, szDebugTxtBuff );\
                sprintf( szDebugTxtBuff, fmt, __VA_ARGS__);\
                putStr (1, szDebugTxtBuff );\
                putStr (1, "\n" );\
                USER8255_PA = 0; \
            }\
        } while (0)
#else
    #define debugLog(fmt, ...) do {} while (0)
#endif
Przeczytałem z uwagą. No nigdy nie wątpiłem w twoją inteligencję w rozwiązywaniu złożonych problemów w sofcie. Nie spotkałem się z zaklęciem __VA_ARGS__ i nie wiem co ono znaczy :o . fmt → widzę i rozumiem sens, a te drugie? To pozostałe parametry?

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
gaweł
Expert
Expert
Posty: 767
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: [CA80][SDCC][Z80] Interfejs RS232 do komputerka CA80 (tymczasowy a'la blog)

Postautor: gaweł » środa 12 cze 2019, 04:17

tasza pisze:...lęgną niczym muszki owocówki,...
W ramach żartu i na luzie: muszka owocówka znosi swoja jajeczka w oku, specyfikacje częstotliwości dla DDS: 112Hz przez 4 minuty, 3 minuty przerwy, 120 Hz przez 4 minuty, 3 minuty przerwy, 332Hz przez 4 minuty, 3 minuty przerwy i 753 Hz przez 4 minuty. Rozwijające się larwy umierają.


tasza pisze:

Kod: Zaznacz cały

#define DEBUG 1

#ifdef DEBUG
    unsigned char szDebugTxtBuff[ 64 ];
    #define debugLog(fmt, ...)\
        do {\
            if ( DEBUG == 1 ) {\
                USER8255_PA = 1; \
                sprintf( szDebugTxtBuff, "[%s][%d]",__FILE__,__LINE__); \
                putStr (1, szDebugTxtBuff );\
                sprintf( szDebugTxtBuff, fmt, __VA_ARGS__);\
                putStr (1, szDebugTxtBuff );\
                putStr (1, "\n" );\
                USER8255_PA = 0; \
            }\
        } while (0)
#else
    #define debugLog(fmt, ...) do {} while (0)
#endif
Przeczytałem z uwagą. No nigdy nie wątpiłem w twoją inteligencję w rozwiązywaniu złożonych problemów w sofcie. Nie spotkałem się z zaklęciem __VA_ARGS__ i nie wiem co ono znaczy :o . fmt → widzę i rozumiem sens, a te drugie? To pozostałe parametry?[/quote]

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse

Awatar użytkownika
tasza
Expert
Expert
Posty: 952
Rejestracja: czwartek 12 sty 2017, 10:24
Kontaktowanie:

Re: [CA80][SDCC][Z80] Interfejs RS232 do komputerka CA80 (tymczasowy a'la blog)

Postautor: tasza » środa 12 cze 2019, 08:35

Te ... i __VA_ARGS__ to elementy `variadic macro` tak to nazywają, makro o zmiennej ilości argumentów. Po polsku ochrzczone jako makrodefinicja wariadyczna. Zagadnienie jest dobrze opisane, taka garstka linków:
https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html
https://codecraft.co/2014/11/25/variadic-macros-tricks/
https://jacquesmattheij.com/c-preprocessor-hell/
W sumie to powinnam tam zaznaczyć, że taki kod jest słabo czytelny i łatwo o pomyłkę przy modyfikacjach, czyli jest po prostu niebezpieczny już na poziomie źródła. No ale tak było mi łatwiej rozwiązać problem. Teraz dłubię posta o Kibanie, tam będzie po bożemu, funkcją nie makrem i z użyciem va_list.
___________________________________________ ____ ___ __ _ _ _ _
J​eżeli dadzą ci papier w linie, pisz w poprzek. Juan Ramón Jiménez

Awatar użytkownika
gaweł
Expert
Expert
Posty: 767
Rejestracja: wtorek 24 sty 2017, 22:05
Lokalizacja: Białystok

Re: [CA80][SDCC][Z80] Interfejs RS232 do komputerka CA80 (tymczasowy a'la blog)

Postautor: gaweł » środa 12 cze 2019, 19:22

tasza pisze:W sumie to powinnam tam zaznaczyć, że taki kod jest słabo czytelny i łatwo o pomyłkę przy modyfikacjach, czyli jest po prostu niebezpieczny już na poziomie źródła.

No ktoś musi pilnować twoich 4 liter, jak skręcasz w maliny :D :D :D

Prawdziwe słowa nie są przyjemne. Przyjemne słowa nie są prawdziwe.
Lao Tse


Wróć do „Retro”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 4 gości