Forum: Mikrocontroller und Digitale Elektronik Z80 Single Board Computer


von Harald N. (haraldn)


Angehängte Dateien:

Lesenswert?

Hallo!

Mein System umfasst die CPU und eine Z80 KIO. Im Moment geht es mir um 
den Interrupt Mode 2 für die serielle Schnittstelle. Im Polling 
funktioniert alles einwandfrei und auch die Echo-Funktion im im2 
funktioniert. Aber wenn ich simpel einen String in den Sendepuffer 
schreibe, wird nur das letzte Zeichen übertragen, aber dafür Anzahl 
Zeichen Mal oft.

Zweites Phänomen. Wenn ich das Umkopieren von Empfangspuffer zu 
Sendepuffer und das Senden auskommentiere, sehe ich den Tx-Puffer 
wachsen und nicht den Rx-Puffer wenn ich auf der Konsole was eingebe.

Vermutlich ist der Fehler etwas sehr simples, aber ich komme nicht 
drauf...

Ich programmiere in C mit sdcc und einem eigenen Startup Skript.

Hat jemand eine Idee was hier die Ursache ist bzw. einen 
Verbesserungsvorschlag für das Handling?

Danke und LG

von Daniel C. (dan1el)


Lesenswert?

Hallo Harald,

kann Dir bei Deinem Problem leider nicht direkt weiterhelfen, vermute 
aber, dass Dein Beitrag im Compiler-Unterforum besser aufgehoben wäre:
https://www.mikrocontroller.net/forum/gcc

Im Betreff würde ich das genaue Problem noch kurz umreißen, damit der 
Leser vorab weiß, worum es genau geht.

VG und viel Erfolg, Z80 Ahoi :)

von Andras H. (kyrk)


Angehängte Dateien:

Lesenswert?

Dein Buffer ist komisch.

buf->num_entries++;
num_enries? Braucht man nicht. Bei 8 bit ist der vielleicht Multi Thread 
Safet. Aber auch nicht da. Habe schon compilers gesehen die val = 1, mit 
clear F und inc F gemacht haben.
Weg damit.


bool circbuff_empty(circbuff_t* buf) {
    return (buf->num_entries == 0);
}
Head ist gleich Tail dann ist der Buffer leer.

unsigned char head, tail,
Das ist nicht gut
mach daraus
unsigned char *head
und
unsigned char *tail

Und hier nimmst du dann
bool circbuff_write(circbuff_t* buf, unsigned char value) {
    if (circbuff_full(buf)) return false;
    *(buf->tail) = value;
    //buf->num_entries++; //Braucht man net
    Das musst du dann umschreiben bisschen. buf->tail = (buf->tail++) % 
CIRCBUFF_SIZE;
    return true;
}

Ach wass, ich lade mein Buffer hoch. Da kannst du dann reinschauen.

von Harald N. (haraldn)


Lesenswert?

Ein Compiler Problem wird es wohl nicht sein.
Und auch danke für die Optimierungsvorschläge, aber die beheben das 
genannte Fehlverhalten auch nicht.

Die Spezialisten haben aber sicher eine Idee. Timing? Mache ich was 
grundlegend falsch?

Beitrag #7678305 wurde vom Autor gelöscht.
von Leo C. (rapid)


Angehängte Dateien:

Lesenswert?

Harald N. schrieb:
> Aber wenn ich simpel einen String in den Sendepuffer
> schreibe, wird nur das letzte Zeichen übertragen, aber dafür Anzahl
> Zeichen Mal oft.

Hallo Harald,
keine Ahnung, ob Dein Problem inzwischen gelöst ist, aber...

circbuff.c
1
    buf->tail = (buf->tail++) % CIRCBUFF_SIZE;
und
1
    buf->head = (buf->head++) % CIRCBUFF_SIZE;

Ausdrücke a la "val = val++" sind grundsätzlich eine schlechte Idee.
Mein Änderungsvorschlag ist im Anhang. Der unschöne cast ist drin, damit 
der Compiler nicht wg Integer Promotion die Modulo-Funktion aufruft. 
Vielleicht hat ja jemand eine bessere Lösung.

von Harald N. (haraldn)


Lesenswert?

Danke klingt plausibel und werde ich so umsetzen.
Mit dem Transmitter Interrupt wird das aber vermutlich nichts zu tun 
haben.
In der Zwischenzeit nutze ich nur noch einen Ringpuffer für den Receiver 
Interrupt und versende die Daten an der SIO klassisch über Polling und 
Datenbyte schreiben. Funktioniert soweit.

Rein akademisch macht es mich halt nicht glücklich...

von Rolf M. (rmagnus)


Lesenswert?

Leo C. schrieb:
> Ausdrücke a la "val = val++" sind grundsätzlich eine schlechte Idee.

In C löst das offiziell undefiniertes Verhalten aus. Da kann also 
durchaus Blödsinn dabei rauskommen.

Leo C. schrieb:
1
    buf->tail = (unsigned char)(buf->tail + 1) % CIRCBUFF_SIZE;
Man muss die beiden Operationen nicht zwanghaft in eine Zeile stecken. 
So sieht es übersichtlicher aus:
1
    buf->tail++;
2
    buf->tail %= CIRCBUFF_SIZE;
Man beachte dass die %-Operation je nach Wert von CIRCBUFF_SIZE recht 
"teuer" sein kann.

von Hans-jürgen H. (hjherbert) Benutzerseite


Lesenswert?

buf->tail = (buf->tail++) % CIRCBUFF_SIZE;

bringt doch das gleiche Ergebnis wie:

 buf->tail = (buf->tail) % CIRCBUFF_SIZE;

von Norbert (der_norbert)


Lesenswert?

Hans-jürgen H. schrieb:
> buf->tail = (buf->tail++) % CIRCBUFF_SIZE;
>
> bringt doch das gleiche Ergebnis wie:
>
>  buf->tail = (buf->tail) % CIRCBUFF_SIZE;

Das erste Beispiel ist unzweifelhaft falsch, das zweite ist unsinnig 
(falls sich nicht irgendwer darum kümmert irgendwo zu inkrementieren).

von Falk B. (falk)


Lesenswert?

Rolf M. schrieb:
>     buf->tail++;
>     buf->tail %= CIRCBUFF_SIZE;
> Man beachte dass die %-Operation je nach Wert von CIRCBUFF_SIZE recht
> "teuer" sein kann.

Man kann es aber auch gleich einfacher und schneller schreiben.
1
 
2
   buf->tail++;
3
   if (buf->tail == CIRCBUFF_SIZE) buf->tail = 0;

Das ist immer recht schnell, weil es nur einen einfachen Vergleich 
benötigt.

von Norbert (der_norbert)


Lesenswert?

…oder gleich einen 2^n großen Buffer verwenden…
Aus dem notwendigen INC und AND macht der Compiler einen viertel-Opcode, 
bei geringerer Optimierung vielleicht einen Halben. ;-)

von Harald N. (haraldn)


Lesenswert?

Ja klingt ja alles sehr interessant. Warum der IM2 Transmit nicht 
funktioniert weiß ich noch immer nicht. Der Buffer funktioniert 
jedenfalls. Mittlerweile ist I2C und SPI implementiert.

von Rolf M. (rmagnus)


Lesenswert?

Hans-jürgen H. schrieb:
> buf->tail = (buf->tail++) % CIRCBUFF_SIZE;
>
> bringt doch das gleiche Ergebnis wie:
>
>  buf->tail = (buf->tail) % CIRCBUFF_SIZE;

Das mag bei dir der Fall sein, aber allgemein gilt das nicht. Wie 
gesagt: Das verhalten der ersten Zeile ist undefiniert.

von Norbert (der_norbert)


Lesenswert?

Harald N. schrieb:
> Warum der IM2 Transmit nicht
> funktioniert weiß ich noch immer nicht.

Tja so ist das bei der Fehlersuche. Zunächst bereinigt man alle 
offensichtlichen Fehler und natürlich alles wovor der Compiler warnt.

Dann, erst dann, kümmert man sich um den Rest.

von Harald N. (haraldn)


Lesenswert?

Wer redet von Compiler Warnungen? Der Code hat von Anfang an ohne 
Warnungen kompiliert... Aber für mich ist das Problem vorerst erledigt 
da anders gelöst.

von Harald K. (kirnbichler)


Lesenswert?

Harald N. schrieb:
> Der Code hat von Anfang an ohne
> Warnungen kompiliert.

Mit -Wall?

von Harald N. (haraldn)


Lesenswert?

Danke für die konstruktiven Beiträge. Der Rest darf woanders 
herumlästern. Dieses Forum hat sich offensichtlich nicht verändert in 
den letzten 10 Jahren. Leider sind viele Leute, auf deren Beiträge ich 
gehofft hatte, offensichtlich nicht mehr hier.

von Rolf M. (rmagnus)


Lesenswert?

Falk B. schrieb:
> Man kann es aber auch gleich einfacher und schneller schreiben.
>
>    buf->tail++;
>    if (buf->tail == CIRCBUFF_SIZE) buf->tail = 0;
>
> Das ist immer recht schnell, weil es nur einen einfachen Vergleich
> benötigt.

Ja, so in etwa schreibe ich das normalerweise auch. Das hat auch den 
Vorteil, dass es nicht langsamer ist, wenn die Puffergröße keine 
Zweierpotenz ist.

Harald K. schrieb:
> Harald N. schrieb:
>> Der Code hat von Anfang an ohne
>> Warnungen kompiliert.
>
> Mit -Wall?

… und -Wextra -pedantic?

von Leo C. (rapid)


Lesenswert?

Rolf M. schrieb:
> Harald K. schrieb:
>> Harald N. schrieb:
>>> Der Code hat von Anfang an ohne
>>> Warnungen kompiliert.
>>
>> Mit -Wall?
>
> … und -Wextra -pedantic?

Es geht hier um sdcc, und der hat andere Schalter. Die höchste Warnstufe 
ist bei ihm aber (imho) schon default.

Im übrigen hat der TO bei dem geposteten Code schon deshalb keine 
Warnungen bekommen, weil der Compiler mit Error abgebrochen hat.

von Harald N. (haraldn)


Lesenswert?

Ähm der Code wie ich ihn bei Threaderstellung gepostet habe hat ohne 
Fehler und ohne Warnungen kompiliert.

von Leo C. (rapid)


Lesenswert?

Harald N. schrieb:
> Ähm der Code wie ich ihn bei Threaderstellung gepostet habe hat ohne
> Fehler und ohne Warnungen kompiliert.

Frisch ausgepackt:
1
$ sdcc -mz80 -S monitor.c
2
monitor.c:174: error 47: indirections to different types assignment   
3
from type 'const-unsigned-char [16] code'
4
  to type 'const-unsigned-char auto'
5
monitor.c:177: error 22: Array or pointer required for '[]' operation 
6
monitor.c:178: error 22: Array or pointer required for '[]' operation 
7
ernst@waldklause:~/Projekte/HaraldN/src$

Dann lad das Zip von hier nochmal runter und probiers nochmal aus. Ist 
nur eine Kleinigkeit, weshalb ich vorher auch nichts dazu geschrieben 
hatte.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Leo C. schrieb:
> monitor.c:174: error 47: indirections to different types assignment

Die Zeile 174 lautet:
1
const unsigned char text = "Hello Zilog Z80";

Da fehlt definitiv ein Sternchen und kann daher zu einem Überschreiber 
im RAM führen, wenn der Compiler es nicht anmeckert und das durchgehen 
lässt.

Es muss so heißen:
1
const unsigned char * text = "Hello Zilog Z80";

: Bearbeitet durch Moderator
von Harald N. (haraldn)


Lesenswert?

Ok stimmt. Da habe ich beim Zippen wohl eine Zwischenversion erwischt. 
Nur das dieser Fehler halt absolut nichts mit dem Problem oder dem 
Puffer zu tun hat.
1
const unsigned char* text = "Hello Zilog Z80";

Ich hatte den * vergessen um den String als Char Pointer zu deklarieren.

Natürlich hat der Code dann gebaut:
1
C:\Users\NaHa\Downloads\src>sdcc -mz80 -S monitor.c
2
3
C:\Users\NaHa\Downloads\src>

Und das lauffähige bin wurde dann auf den Flash geschrieben. Ich 
behaupte mal, dass ich sonst die Fehlerbeschreibung wie bei 
Threaderöffnung nicht machen könnte.

von Harald N. (haraldn)


Lesenswert?

Zur Erklärung wie es zu dem Fehler kam: Zuerst hatte ich unsigned char 
text[]. Da habe ich allerdings im Assembler Code gesehen, dass das 
höchst ineffizient ist. Hab's zu einem unsigned char* text umgebaut und 
dabei den * vergessen. Beim kompilieren gemerkt, Stern ergänzt und 
Fehler somit behoben.


Warum ist das hier eigentlich so, dass man um Sachen herumeiert, die 
definitiv nichts bzw. nichts mehr mit dem eigentlichen Problem zu tun 
haben. Ergötzt ihr euch wirklich so sehr daran auf anderen Fehlern so 
herumzureiten?

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Harald N. schrieb:
> Warum ist das hier eigentlich so, dass man um Sachen herumeiert, die
> definitiv nichts bzw. nichts mehr mit dem eigentlichen Problem zu tun
> haben.

Es ist nicht einfach, als Leser ein Problem nachzuvollziehen und zu 
helfen, wenn man selber die Hardware nicht zur Hand hat und deshalb die 
Möglichkeit des Debuggens nicht gegegeben ist.

Es ist weithin bekannt, dass C-Programme sehr leicht dazu neigen, dass 
beispielsweise ein Überschreiber im RAM an Codestelle "A" zu einem 
Problem an einer ganz anderen Codestelle "B" führen kann. 
Augenscheinlich haben für den Programmierer die Stellen A und Stellen B 
nicht miteinander zu tun. Da kann ich Dir tausende von Beispielen 
nennen.

Von daher gibt es bei C-Programmen keine "Sachen", die "definitv nichts 
mit dem eigentlichen Problem zu tun haben". Das ist ein Trugschluss, den 
man niemals außer acht lassen darf. Sonst hat man bei der Fehlersuche 
schon verloren.

> Ergötzt ihr euch wirklich so sehr daran auf anderen Fehlern so
> herumzureiten?

Deine falsche Annahme ("herumeiern") oben führt zwangsweise zu einer 
falschen Folgerung ("ergötzen") unten. Genau wie ein Fehler im 
C-Programm "oben" zu einem Problem weiter "unten" führen kann. Witzige 
Analogie, aber hier zutreffend. Mit "Ergötzen" hat das nichts zu tun, da 
spielt Dir Deine Phantasie offenbar einen Streich.

Ich stelle aber an dieser Stelle freiwillig meine Hilfe ein. Dafür 
gescholten zu werden, einen Fehler anzumerken, ist schon ein starkes 
Stück.

: Bearbeitet durch Moderator
von Harald N. (haraldn)


Lesenswert?

Akzeptiert. Trotz allem. Lassen wir diese Diskussion.

Ich versuche es jetzt noch einmal mit ganz konkreten Fragen.

Vorrausschicken möchte ich, dass die Hardware funktioniert. Im Polling 
Modus funktioniert alles einwandfrei. Und auch jetzt, wo Rx über 
Interrupts und Tx über Polling läuft, funktioniert das Programm 
einwandfrei. Hatte sogar mcurses (hier aus dem Forum) und damit auch das 
Beispiel Hexeditor zum Laufen gebracht.

Hat von euch noch nie jemand eine Z80 SIO im IM2 zum Senden benutzt?

Wie kann es sein, dass ein IM2 Rx und IM2 Tx funktioniert solange ich 
nur ein Echo mache, aber nicht, wenn ich einen String im Programm in den 
Tx-Puffer schreibe?

Muss man ein besonderes Timing beachten?

Zwar keine Frage, aber ein Nachtrag: Ich habe zuletzt auch wirklich alle 
Stellen, wo mit dem Puffer handiert wird, mit DI -- EI umschlossen.

von Harald N. (haraldn)


Lesenswert?

Frank M. schrieb:
>
> Ich stelle aber an dieser Stelle freiwillig meine Hilfe ein. Dafür
> gescholten zu werden, einen Fehler anzumerken, ist schon ein starkes
> Stück.

Das ging ja nicht gegen dich persönlich. Aber wie gesagt, das Forum hat 
sich nicht verändert.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Harald N. schrieb:
> Das ging ja nicht gegen dich persönlich.

Naja, die Anmerkungen der anderen waren durchaus näher am Thema dran, 
nämlich an der Behandlung des Fifos. Da ging es bestimmt nicht um 
Stellen, die nichts mit dem Thema zu tun haben. Ein Increment auf der 
rechten Seite und Zuweisung an dieselbe Variable führt unweigerlich zu 
UB und geht daher in den seltensten Fällen gut.

Harald N. schrieb:
> Hatte sogar mcurses (hier aus dem Forum) und damit auch das Beispiel
> Hexeditor zum Laufen gebracht.

Nur zur Info: Ich bin nicht nur der Autor von MCURSES hier aus dem 
Forum und des dazu passenden Hexeditors, sondern auch von STECCY, 
des ZX-Spectrum-Emulators, der natürlich auch den Z80 emuliert. Ich weiß 
daher im allgemeinen, wovon ich spreche ;-)

von Leo C. (rapid)


Lesenswert?

Harald N. schrieb:
> Hat von euch noch nie jemand eine Z80 SIO im IM2 zum Senden benutzt?

Doch, aber das ist fast 40 Jahre her. Damals gings allerdings um 
HDLC-Protokolle...

Bei den Retrobasteleien der letzten Jahre habe ich tatsächlich nichts 
mit Z80-SIO gefunden. Trotzdem wird sich Dein Problem wohl lösen lassen. 
Ich kann am Wochenende nochmal reinschauen, wenn ich wieder zu Hause 
bin.

von Rick (rick)


Lesenswert?

Harald N. schrieb:
> Mein System umfasst die CPU und eine Z80 KIO.

Harald N. schrieb:
> Hat von euch noch nie jemand eine Z80 SIO im IM2 zum Senden benutzt?

Die KIO ist ja nun offensichtlich etwas mehr als nur eine SIO:
https://www.zilog.com/docs/z80/ps0118.pdf
Die KIO gab es vor 40 Jahren noch nicht.

Funktioniert denn dein System mit anderen Interruptquellen?

Harald N. schrieb:
> Aber wenn ich simpel einen String in den Sendepuffer
> schreibe, wird nur das letzte Zeichen übertragen, aber dafür Anzahl
> Zeichen Mal oft.
Dann funktioniert offenbar der Zeichenzähler, aber der Lesezeiger steht 
immer auf dem letzten Zeichen.

Ich würde zum Debuggen die funktionierende Polling-Funktion nehmen und 
in einem Debugspeicher den Zustand vom Lesezeiger ablegen.
Den Debugspeicher könnte man sich dann per 
Tastendruck/Kommando/zeitgesteuert ausgeben lassen um zu schauen, was 
mit dem Lesezeiger so angestellt wird.

von Leo C. (rapid)


Angehängte Dateien:

Lesenswert?

Hallo,
es hat leider etwas länger gedauert.
Statt mit Z80 KIO (habe ich nicht), habe ich das Ganze mit Toshiba 
TMPZ84C015 getestet. Ein Chip, der neben einer Z80 CPU noch CTC, PIO, 
SIO und etwas Kleinkram enthält. Den gleichen Quarz habe ich auch nicht, 
und meiner (18,432MHz) lässt sich mit dem CTC nicht auf die üblichen 
Bitraten teilen. Also läuft die CTC bei mir im Counter-Mode mit extra 
Takt. F_CPU, BAUD und KIO oder nicht, können/sollten im Makefile 
eingestellt werden.

Das wichtigste bei der SIO im interrupt-Modus: Der Tx-(Transmit Buffer 
Empty) Interrupt kommt (nur), wenn das Senderegister leer wird, nicht 
wenn es leer ist.
Wenn der Interrupt kommt, und der circbuf/FIFO auch leer ist, muß man 
den Interrupt mit dem "Reset TxINT Pending" Kommando quittieren. Den 
Zustand muß man sich merken, und das nächste Zeichen direkt an die SIO 
schicken, statt in den FIFO zu schreiben.

Im angeängten Zip-File ist noch eine Version mit meinen eigenen 
FIFO-Routinen. Tatsächlich hatte ich damit zuerst getestet. Die Routinen 
kommen ohne Zähler aus, und haben deshalb keine Variable, die von beiden 
Seiten geschrieben wird. Das reduziert mögliche race conditions.

Das Programm muß mit einem halbwegs aktuellen sdcc übersetzt werden, da 
es sich an einigen Stellen auf Parameterübergabe in Registern verlässt.

von Harald N. (haraldn)


Lesenswert?

Hallo Leo!

Vielen Dank für deinen Input und dass du deine Zeit geopfert hast. 
Alleine deine Erklärung macht Sinn und deren Nichtbeachtung führt zu dem 
Fehlerbild, dass ich beschrieben habe.
Mein Code ist natürlich schon lange nicht mehr der den ich hier gepostet 
habe, aber ich werde trotzdem meine Routinen anpassen und es versuchen.

von Harald N. (haraldn)


Lesenswert?

Edit:

Ich habe den Code gerade in meinen aktuellen Code eingeflechtet. Da ich 
mittlerweile bereits die getchar und putchar Routinen implementiert 
hatte, damit ich printf aus der Standardbibliothek benutzen kann, 
erweiterte ich mein Programm nur um die (in der Zwischenzeit 
auskommentierte) Tx-ISR-Routine und habe die putchar nach deinem Vorbild 
geändert.

Und siehe da es funkktionert. Das Prinzip des Tx-Interrupts hatte ich 
schon verstanden, aber offensichtlich komplett falsch umgesetzt. Vielen 
Dank nochmal Leo!!!

von Manfred P. (pruckelfred)


Angehängte Dateien:

Lesenswert?

Und nun hat sich's aus-ge-zettet, gerade die Tage gelesen.

von Klaus F. (klaus27f)


Lesenswert?

Manfred P. schrieb:
> Und nun hat sich's aus-ge-zettet, gerade die Tage gelesen.

Hallo Manfred,

auch schon aufgewacht???

Und wie immer: Auch dein angefügter Beitrag ist inhaltlich falsch, aber 
da kann du nix dafür.

Beitrag "Re: Das Ende des Z80"

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.