C tut weh!

Oct 10, 2019 53 Replies

Am 10.10.2019 um 12:42 schrieb Wolfgang Allinger:

[...]

Und nun zum Vergleich:

double det = p*p/4.0 - q; if (det >= 0.0) { double sol1 = -p/2 - sqrt(det); double sol2 = -p/2 + sqrt(det); printf("solutions = %f, %f\n", sol1, sol2); } else { printf("no solution\n"); }

Was braucht man im richten Leben öfter, das Lösen von Rechen- oder Logikaufgaben jedweder Art oder den Sprung an den Resetvektor? Wieviele Befehle sind eine Rechenaufgabe in Z80-Assembler? Wie lesbar ist die Rechenaufgabe in Forth? Wieviel Transistoren braucht die Rechenaufgabe als HW-Schaltung?

Stefan

Mal abgesehen davon: Was sollte beim Z80 nicht richtig sein?

MfG hjs

Am 10.10.19 um 12:42 schrieb Wolfgang Allinger:

RST 0 ist ja wohl nur 1 Byte und nicht 2 Befehle.

Selbst JP 0000 ist nur 1 Befehl.

/Gerhard

Ja, aber drei Bytes. Das geht an die Substanz. Wenn ich wissen will, ob das Programm von einem normal denkenden Menschen geschrieben ist, dann durchsuche ich den Hexcode nach '3e 00' und 'fe 00'.

MfG hjs

Hmmm ich verstehe deine Frage nicht ganz, ich glaube wir reden aneinander vorbei.

Ich nehme an, es ist Konsens, dass es gut und sogar notwendig ist, in Programmen Sprünge auslösen zu können. Bei einer Hochsprache kann man sich vielleicht darüber streiten, ob das Abstraktionsniveau so ein Hardwaredetail exponieren sollte oder nicht. C macht das, andere Programmiersprachen nicht. Beides ist okay.

Ich glaube niemand hat bestritten, dass es völlig normal ist, dass man in Z80-Assembler (oder jedem beliebigen nicht-esoterischen ASM-Dialekt) einen absoluten Sprung auslösen kann.

Gruß, Johannes

Ich hatte den Wert der Konstante 0 kritiklos vom OP übernommen, in der Annahme, daß da implementations-definiertes Verhalten in einer freestanding_implementation wirkt.

An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.67)

Undefiniertes Verhalten erlaubt sogar ein Verhalten, das so erscheint, als sei es definiertes Verhalten. Der Standard hat es halt nicht definiert - aber die Implementation.

Eben.

Das kann so nicht gesagt werden! Der WERT, den sizeof liefert, ist natürlich implementations-definiert. Aber alles Sonstige nicht.

Bei VLA wird zur Laufzeit berechnet. Der letzte Satz stimmt.

Der Operand zu goto muß ein konstanter Label-Name sein.

Die 0 ist hier aber ein Spezialfall, da (void*)0 eben die null pointer contant ist. §6.3.2.3.3:

"An integer constant expression with the value 0, or such an expression cast to type void *, is called a null pointer constant."

Naja, der C-Standard differenziert schon genau zwischen undefined behavior und implementation-defined behavior. Und einen NULL-pointer zu dereferenzieren ist ersteres.

Aber klar, letztlich wird es zu Code compiliert und der wird in aller Regel konsistent irgendwas tun. Nur was, das ist halt nicht definiert und der C-Standard erlaubt prinzipiell alles: Abort z.B..

Hmja, da war ich nicht klar genug in meiner Aussage. Was ich meinte war §6.5.3.4.4:

"The value of the result is implementation-defined, and its type (an unsigned integer type) is size_t".

Für dein vorliegendes Beispiel ist das aber eh zweitranging, weil §6.5.3.4.2 ja schon sagt

"If the type of the operand is a variable length array type, the operand is evaluated; otherwise, the operand is not evaluated and the result is an integer constant."

Viele Grüße, Johannes

Ja, ich hatte gedacht, dass es besondesrs böse sein könnte, ausgerechnet eine Sprung nach 0x0000 auszuführen.

MfG hjs

Ich mag es, wenn ich sowas machen kann. Auch wenn das ein Beispiel dafür ist, wie man es nicht macht. Denn 10 Jahre später wird das Programm auf einen andern Controller portiert, dessen Reset-Adresse nicht 0 ist...

Natürlich kann man sich in C leicht in den Fuß schiessen. In Java nicht ganz so einfach, aber da ist dann gleich das ganze Bein weg ;-)

...

Was? Sprung nach NULL, Reset-Simulation, auf welcher Plattform?

Da ist der Prozessor ja bekannt ;-)

Gruß, Falk

Ich kenne Implementationen, die viele Fälle, für die der Standard UB nennt, definieren, also das UB des Standards entfernen.

Der Standard verlangt von Implementationen fast nur, daß ein 'strikt konformes' Programm entsprechend funktionieren muß. Darüber hinaus darf eine Implementation beliebig viel definieren. Die Implementation muß ihre Erweiterungen in einem Dokument beschreiben.

Beispielsweise darf der Ausdruck ((void*)0) jeden beliebigen Wert haben (im Adressenkontext). Der REALE Nullpointer darf folglich z.B. den Wert 0xCB haben. Je nach Typ dürfen es verschiedene Werte sein.

THX für die rege Beteiligung :)

ja es ist ein Sprung an die Adresse 0. Ist bei vielen CPUs der RESET Vektor. (8080 ... Z80... etc.)

Also in FORTH:

0 EXECUTE

In Z80: LD HL,0 JP (HL)

Ja ich weiss, dass auch ein JP 0 geht, entspricht eben einem GOTO 0, bzw. RST 0 geht auch. Aber die machen nicht exact das gleiche, wie der C- Konstrukt.

k.a. ob C GOTO hat, vermutlich nicht, denn obiger C-Konstrukt stammt aus einem Programm, was unter bestimmten Umständen einen RESET auslösen soll. (nein ich habs nicht gefunden, wurde mir vor Äonen angetragen. War aber keine DummenFa)

BTW Dumm: unser (vor)lauter Dummbatz Johannes Bauer auf dem Mt. Stupid(1) rumkrakelend, sollte sich noch hinter die Ohren schreiben, dass man in jedem uC nach dem Reset alle HW Register auf den gleichen Stand wie nach einem echten RESET setzt. Besonders auch die undefinierten Registerzustände (auch das gibt es!) in einen ordentlichen Zustand bringt und erst dann mit seiner Applikation anfängt und danach alles auf den Zustand setzt, wie es die Applikation erfordert. Das sorgt dafür, dass alle externen Signale immer in der gleichen Reihenfolge hochkommen und keine lustige Spikes irgendeinen externen Baustein durcheinanderbringen.

Das erspart mächtig Ärger, ist zwar etwas Arbeit, aber das lohnt sich.

BTW2 taugen in fast allen uC die HW Reset nichts, daher hab ich IMMER einen externen RST Baustein in meinen Entwicklungen eingebaut, nachdem ich es auf die harte Tour lernen musste. IIRC wars ein 80531 und auch der ADuC812 hatte einen grauenhaften HW RST. Schande über deren Entwickler.

BTW3 der eine oder andere uC macht auch beim runtertorkeln der Spannung noch lustige Dinge. 8031 schreiben dabei noch gerne Murks in ein externes Flash, meist an die Adresse 0 bzw. Block 0. Auch das verhindert eine ext. RST Baustein, der die RST Leitung auf GND zieht.

Ich weiss nicht, ob die internen RST besser geworden sind, da ich das nicht mehr ausprobueren wollte. s.o. IMMER ext. RST Baustein.

Ich weiss nicht mehr, welchen Baustein ich genommen habe. Habe alle AKten vor der Auswanderung verkauft.

Ich hab keine Lust, auf meiner uralt Plattensammlung noch was zu suchen, ob da noch Schaltpläne und Stücklisten zu finden sind.

IIRC war das irgend ein 3pin SMD aus der Schweiz. Funzte auch einwandfrei bei Schleichspannungsbetrieb :)

(1) Gruss von Dunning-Kruger

Saludos (an alle Vernünftigen, Rest sh. sig) Wolfgang

Falk Willberg schrieb:

Das Problem stellt sich so meist nicht. Wenn man für einen konkreten Controller programmiert, dann gibt es notwendigerweise Controller-spezififischen Code, der so nicht auf anderen Controllern laufen wird und den man immer anpassen muß. Die Lösung ist, diesen Code möglichst abgeschottet in separaten Übersetzungseinheiten / Librarys zu verpacken und ein sauberes Interface zu definieren. Im spezifischen Code darf dann auch nicht portabel programmiert werden.

- Heinz

Musst du dich denn wirklich jedes Mal zum Brot machen indem du deutlich machst, dass deine Mikrocontroller-Kenntnisse auf dem Stand von Anno

1980 stehen geblieben sind?

Auf deinen Spielzeugcontrollerchen und trivialen Demoapplikationen mag das so gehen. Die verzeihen dir ja sogar deinen grotesken Codepfusch und die Raceconditions. Meistens geht das schon gut, da ist noch nie was passiert, gell.

Controller von heute haben buchstäblich *tausende* von Registern, bei denen man sich darauf verlassen können muss, dass sie mit einem Reset ordentlich initialisiert werden, sonst ist die Hardware Murks. Ganz abgesehen davon, dass es Register gibt, die sich nur in eine Richtung initialisieren lassen (z.B. MPU). Da ist es schlicht nicht möglich, die zu verbiegen, nachdem sie einmal gesetzt wurden. Vermutlich hast du mit solcher Peripherie aber noch nie gearbeitet, das würde zumindest deine Ahnungslosigkeit erklären.

Lass es doch einfach sein, Wolfgang. Es ist einfach nur peinlich was du vom Stapel lässt und wirklich gute 40 Jahre von der heutigen Realität entfernt. Deine Tipps von gestern sind heute nur noch für die Tonne.

Immer gern, Dein Johannes

Am 10.10.2019 um 19:28 schrieb Hans-Juergen Schneider:

Um es genau solchen Reverse Engineering Leuten schwer zu machen, sollte jeder halbwegs clevere Programmierer mit wechselnden Sprungtechniken arbeiten und ausnutzen, was die Trickkiste so hergibt. :-D

W.

"Stefan Reuther" snipped-for-privacy@arcor.de schrieb im Newsbeitrag news: snipped-for-privacy@stefan.msgid.phost.de...

Was ist das für eine bescheuerte Frage ?

Wenn eine Programmiersprache etwas nicht bietet was man braucht, und man es auch nicht nachrüsten kann, dann ist sie für manche Jobs einfach unbrauchbar.

Wer lässt sich schon auf halb-durchdachtes ein mit dem er eines Tages in einer Sackgasse steht aus der es kein heraus gibt ?

Da nimmt man doch lieber gleich eine vernünftige Basis die von vielen anderen erprobt wurde, sich also i der Praxis bewiesen hat.

Am 11.10.2019 um 11:34 schrieb MaWin:

Faustkeile!

[...]

Auch bei aktuellen uC funktioniert nicht immer alles makellos.

Bei uns wurde z.B. ein zusätzlicher externer WatchDog eingeführt. Der interne war eben nicht ganz einwandfrei. RST wird von einem BrownOut-IC betätigt.

Hardware kann natürlich immer Errata haben, aber ein nicht richtig funktionierender WDT ist schon *richtig* übel. Das ist ja die *eine* Komponente, die alles noch irgendwie retten soll, wenn in dem Rest Murks passiert. Was war das denn für ein SoC?

Wenn ich mich nicht darauf verlassen kann, was mir im Datenblatt garantiert wird (z.B. eben definierte Werte der Peripherie-Register und dessen Zustand) dann habe ich da vermutlich noch ganz andere Probleme.

Es ist trotzdem schlicht auf einem modernen System unrealistisch, lediglich in Software einen Reset ausführen zu wollen: Zum einen, dass man möglicherweise gar nicht die Privilegien dazu hat und an den Reset-Vektor springen kann, wie man will und mag -- nur halt wegen fehlender Berechtigungen das System nicht resetten darf. Zum anderen, weil da im Hintergrund, parallel, dann z.B. noch irgendwo ein DMA wild rumwurstelt oder der Interrupt-Controller "undefiniertet" konfiguriert ist.

Das macht echt einfach keinen Sinn und ist genau der Grund, wieso man üblicherweise eben einen echten (Software) Reset auslösen kann.

Gruß, Johannes

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required