PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Funktion lieferte falsches Ergebnis, Compiler-Bug?



Mitch64
26.07.2017, 11:32
Hallo Leute
Ich habe ein Phänomen festgestellt beim Erstellen einer Funktion zur Entprellung einer Taste.





' Lauffähiges Testprogramm für Simulator
' zur Veranschaulichung des Problems

$regfile = "m8def.dat"
$hwstack = 30
$Swstack = 30
$framesize = 35
$Crystal = 800000
$Baud = 19200

$Sim


' Allgemeine Deklarationen
Const True = 1
Const False = 0
Const LowLevel = 0 ' Tastenpegel Low
Const HighLevel = 1 ' Tastenpegel High

Key_Taste Alias PinB.1 ' Abzufragende Taste/Pin
Set Key_Taste ' PullUp an

Declare Function getKeyTaste(Byval Level as Byte) as Byte

Do
' Irgendwo in der Hauptschleife
' Die Abfrage der Taste
If getKeyTaste(LowLevel) = True then ' Taste ist Low-Aktiv
Print "gedrueckt"
Else
Print "nicht gedrückt"
End If
Loop

' Die Function liefert immer True (taste gedrückt)
Function getKeyTaste(Byval Level as Byte) as Byte
getKeyTaste = False ' Rückgabewert zunächst auf falsch (Taste nicht gedrückt)
If Key_Taste = Level.0 then ' Pinpegel = Vorgabepegel?
Waitms 30 ' Entprelldauer abwarten
If Key_Taste = Level.0 then ' Pinpegel noch immer = Vorgabepegel?
getKeyTaste = True ' Rückgabewert True (Taste aktiv gedrückt)
End If
End If
End Function



Die Function liefert immer True zurück, gibt also vor, die Taste sei gedrückt.

Kann mir jemand sagen, ob das ein Bug ist?

Ich verwende Bascom Vollversion 2.0.7.8 (aktuelle, offizielle Version)

Bitte um Infos.

Micha

Searcher
26.07.2017, 12:36
Hallo Mitch,





' Lauffähiges Testprogramm für Simulator
' zur Veranschaulichung des Problems

$regfile = "m8def.dat"
$hwstack = 30
$Swstack = 30
$framesize = 35
$Crystal = 8000000
$Baud = 19200

$Sim


' Allgemeine Deklarationen
Const True = 1
Const False = 0
Const LowLevel = 0 ' Tastenpegel Low
Const HighLevel = 1 ' Tastenpegel High

dim Funktions_wert as byte

Key_Taste Alias PinB.1 ' Abzufragende Taste/Pin
Set Key_Taste ' PullUp an

Declare Function getKeyTaste(Byval Level as Byte) as Byte

Do
' Irgendwo in der Hauptschleife
' Die Abfrage der Taste

Funktions_wert = getKeyTaste(LowLevel)
If Funktions_wert = True then ' Taste ist Low-Aktiv

' If getKeyTaste(LowLevel) = True then ' Taste ist Low-Aktiv

Print "gedrueckt"
Else
Print "nicht gedrückt"
End If
Loop

' Die Function liefert immer True (taste gedrückt)
Function getKeyTaste(Byval Level as Byte) as Byte
getKeyTaste = False ' Rückgabewert zunächst auf falsch (Taste nicht gedrückt)
If Key_Taste = Level.0 then ' Pinpegel = Vorgabepegel?
Waitms 30 ' Entprelldauer abwarten
If Key_Taste = Level.0 then ' Pinpegel noch immer = Vorgabepegel?
getKeyTaste = True ' Rückgabewert True (Taste aktiv gedrückt)
End If
End If
End Function



Kann mir jemand sagen, ob das ein Bug ist?



Keine Ahnung ob man das als Bug bezeichnen kann. In der BASCOM Hilfe zu Function wird drauf hingewiesen:


If you execute other code after you assigned the function result, registers will be trashed. This is no problem if you assigned the function result to a variable. But when you use a function without assigning it to a variable, some temporarily registers are used which might be trashed.

Thus this special attention is only needed when you use the function like :
If Myfunc()=3 then 'myfunc is not assigned to a variable but the result is needed for the test

When you use :
myvar=Myfunc()
Then you will not trash the registers. So in such a case there is no problem to run code after the function assignment.


Mit Hinzufügen der grünen Zeilen und Entfernen der roten Zeilen funktionierts im Simulator. Bei $crystal fehlt glaub ich noch 'ne Null bei Dir?

Gruß
Searcher

Mitch64
26.07.2017, 15:36
Danke für den Hinweis!
Das wusste ich nicht.

Aber dennoch ist das wenn man es genau nimmt ein Compiler-Fehler.
Denn der müsste selbstständig für die Rückgabevariable Speicher reservieren.
Auf dem Softstack 2 Adressen und im im Framesize die Variablenwerte.
Und wenn man des Soft-Stack anschaut, reserviert er auch Platz für 2 Adressen.
Die eine Adresse zeigt auf den Parameter. Die andre Adresse zeigt auch irgendwo hin,
konnte aber nicht herausfinden auf was.

Wenn man es weiß, kann man den Fehler umgehen, hatte mich aber dumm gesucht,
bis ich merkte, dass die Funktion den Rückgabewert überschreibt.

Vermutlich ist das auch das selbe Problem, wenn man z.B. eine Funktion so schreibt.



Function Sub DurchsucheArray(byval Anzahl as Byte, byval SuchString as String) as Byte
local i as Byte
For i=0 to Anzahl-1
If myArray(i)=SuchString then
DurchsucheArray=i
Exit Function
End If
Next
End Function


Hierbei hatte ich den Effekt, dass es einige male funktionierte und plötzlich läuft die Schleife über Anzahl hinaus.
Eben noch so ein Bug in Bascom.

Eigentlich Schade, dass die Programmierer die Software und die Bascom-Hilfe nicht richtig pflegt.

Aber nochmals danke für die Info.

Micha

- - - Aktualisiert - - -

Nachtrag

Wenn der Funktion (aus 1. Beitrag) keine Variable zur Aufnahme des Rückgabewerts zur Verfügung (var=Funktionsaufruf) gestellt wird,
zeigt die 2. Adresse des Software-Stack auf den Wert $0010. Dies entspricht dem Register 16.

Ein Test im Simulator zeigt, dass der Wert aus Register r16 zurück gegeben wird.
Auf dem Framesize wurde nur 1 Wert abgelegt, der Bytewert des Übergabeparameters.

Fazit: Der Compiler hat seine Macken und ist irgendwie nicht ausgereift. Gäbe es so Macken in GCC, würden die Programmierer auf die Barrikaden gehen.

Schade, Schade.

Searcher
26.07.2017, 18:21
Fazit: Der Compiler hat seine Macken und ist irgendwie nicht ausgereift. Gäbe es so Macken in GCC, würden die Programmierer auf die Barrikaden gehen.


Ja, meine ich auch. Ich hatte auch schon manchmal an "unerklärbaren" Fehlern rumgesucht und zum Teil auch an den mcs support gemeldet. Es wurde Behebung in SW oder ein Hinweis in Doku in Aussicht gestellt oder auch als gewolltes Verhalten bezeichnet. Ich nutze nur die Demo Version; die reicht mir noch aus.

Das hier der Rückgabewert überschrieben wird, gefällt mir auch nicht. 'ne Möglichkeit wäre, das auch an den support zu melden. Wird vielleicht keine Änderung verursachen, da ja schon "in der Doku erwähnt ist" aber je mehr sich beschweren, desto größer wird die Wahrscheinlichkeit, daß in Version 3 :-) was daran getan wird und man eine Funktion auch direkt in einer If Abfrage verwenden kann, was eine tolle Verbesserung wäre.

Gruß
Searcher

Ceos
27.07.2017, 06:13
wenn man aber mal ehrlich ran geht, ist der aufruf einer methode im conditional statement aber auch ganz böse "bad practice"
selbst der c-compiler standard definiert nicht in welcher reihenfolge methoden in einem statement effektiv ausgeführt werden, von daher sollte die rückgabe einer funktion immer in eine variable geleitet werden, welche dann verarbeitet wird.

Mitch64
27.07.2017, 09:45
Im Grunde geht es ja nur darum, dass der Compiler selbstständig einen Speicherbereich für den Rückgabewert einer Funktion reserviert.
(Das könnte der Compiler ohne weiteres tun, würde auch nichts an der Komatibilität beim Compilieren ändern.)
Wenn man dann den Rückgabewert setzt, egal an welcher Stelle in der Function, sollte dieser reservierte Speicher den neuen Wert erhalten.
Beim Verlassen wird dann dieser Wert an den Aufrufer zurück gegeben.
In C wird der Rückgabewert auch zwischengespeichert, bevor er letztendlich zurückgegeben wird.

Ich finde es halt nicht schön, einen Rückgabewert in r16 zwischenzuspeichern. Auf dem Frame wäre genug Platz.

Irendwie erinnert mich das ganze an die Straßen in Deutschland (nicht Ostdeutschland).
Hier sind die Straßen in einem rel. schlechten Zustand, so wie der Compiler auch (lange nichts geändert).
Anstatt die Straßen zu reparieren (den Compiler verbessern), werden 30ger-Schilder aufgestellt.
Das entspricht dem Hinweis in der Hilfe, dass man den Rückgabe-Wert erst möglichst
unmittelbar vor dem Verlassen der Function setzen soll. (in C muss man das nicht!).

Übrigens ist das ja nicht die einzig Macke, die der Compiler hat, lediglich ein Beispiel.
Wollte ich alle mir bekannten Macken von Bascom an MCS melden, wäre ich nur noch
mit dem Mailprogramm beschäftigt.

Aber egal, meine Frage ist quasi beantwortet.

Micha.

Ceos
27.07.2017, 09:58
schadet ja trotzdem nicht den strassenbelag zu tauschen (bessere programmier hygiene) als nur einen flicken drauf zu hauen (fehler beim compiler anmeckern) :P

jede münze hat zwei seiten und cih wollte es halt nur mal erwähnen

Klebwax
27.07.2017, 12:54
In C wird der Rückgabewert auch zwischengespeichert, bevor er letztendlich zurückgegeben wird.

Eigentlich nicht. Da schreibt man:

return value;

Und wenn man nur

return;

schreibt, wird gar nichts zurückgegeben. Und wenn man den Returnwert nicht gleich beim Funktionsaufruf in eine Variable packt, ist er weg.

var = function();


Ich finde es halt nicht schön, einen Rückgabewert in r16 zwischenzuspeichern. Auf dem Frame wäre genug Platz.


Wenn man in einer Hochsprache programmiert, darf es einen nicht interessieren wie oder wo der Compiler oder Interpreter Variable abspeichert. Ob der Prozessor überhaupt Register oder einen Stack hat, muß dem Programmierer in der Hochsprache vollkommen egal sein. Es geht ihn nichts an, es sei denn er programmiert einen Compiler. Das einzige. was zählt ist, daß sich Compiler oder Interpreter an die Sprachdefinition hält. Das ist der Sinn einer Hochsprache.

Das Problem bei BASIC ist, daß in der ürsprünglichen Sprachdefinition lokale Variable und Funktionen mit Returnwerten nicht vorkommen. Sie sind bei den jeweiligen BASIC-Versionen nachträglich angestrickt. Das ist mal besser, mal aber auch schlechter gelungen. Nicht umsonst wird BASIC als Programmiersprache auch nicht so recht ernst genommen.

MfG Klebwax

HaWe
27.07.2017, 13:14
wenn man aber mal ehrlich ran geht, ist der aufruf einer methode im conditional statement aber auch ganz böse "bad practice"
selbst der c-compiler standard definiert nicht in welcher reihenfolge methoden in einem statement effektiv ausgeführt werden, von daher sollte die rückgabe einer funktion immer in eine variable geleitet werden, welche dann verarbeitet wird.

ich dachte eigentlich schon, dass eine Reihenfolge durch die Syntaxregeln in C definiert ist:

von links nach rechts, und dann wie lt. dieser Liste: http://en.cppreference.com/w/c/language/operator_precedence

Und es mag anders, mit Zwischenspeichern, auch eher meinem Schönheitsempfinden zu entsprechen, trotzdem ist gerade in C die Ineinanderverschachtelung von Ausdrücken und Funktionswerten gang und gäbe:



if (wiringPiSetupGpio() ) {
fprintf (stdout, "oops: %s\n", strerror (errno)) ;
return 1 ;
}
else fprintf (stdout, "\nwiringPi init: OK, by BCM numbering\n") ;

oder gar:


if(!a->K|(a->X&M)!=M|a->D<=d)
{a->K=Z;a->V=m;a->D=d;A->K=0;
a->X=X|8*(m>q)|S*(m<l);a->Y=Y;
}


...und zugegeben, mit Einschränkungen:

The C language standard doesn't specify operator precedence. It specifies the language grammar, and the precedence table is derived from it to simplify understanding. There is a part of the grammar that cannot be represented by a precedence table: an assignment-expression is not allowed as the right hand operand of a conditional operator

Auf der anderen Seite ist aber auch klar, dass es für C grundsätzlich schon weltweite Standards gibt (u.a. ANSI oder ISO für EU), in denen das haarkllein festgeschrieben ist, nicht aber in dieser Form für Basic, wie du ja schon erwähnt hast

Das Problem bei BASIC ist, daß in der ürsprünglichen Sprachdefinition lokale Variable und Funktionen mit Returnwerten nicht vorkommen. Sie sind bei den jeweiligen BASIC-Versionen nachträglich angestrickt. Das ist mal besser, mal aber auch schlechter gelungen. Nicht umsonst wird BASIC als Programmiersprache auch nicht so recht ernst genommen.
.

Ceos
27.07.2017, 14:21
die reihenfolge beim ausführen ist dennoch nicht gewährleistet weil nicht definiert

es ist nur vorgeschrieben in welcher reihenfolge ausgewertet werden muss

bei einem aufruf in einem statement muss der compiler jedoch erst das ergebnis des aufrufs ermitteln und die reihenfolge ist zumindest hier nicht explizit vorgeschrieben soweit ich informiert bin oder hast du etwas dass gegensätzliches behauptet?

HaWe
27.07.2017, 17:52
die reihenfolge beim ausführen ist dennoch nicht gewährleistet weil nicht definiert

es ist nur vorgeschrieben in welcher reihenfolge ausgewertet werden muss

bei einem aufruf in einem statement muss der compiler jedoch erst das ergebnis des aufrufs ermitteln und die reihenfolge ist zumindest hier nicht explizit vorgeschrieben soweit ich informiert bin oder hast du etwas dass gegensätzliches behauptet?

nein, die Reihenfolge der Auswertung beim Ausführen ist nicht vorgeschrieben, nur was ausgewertet wird, ist ntl von Syntax-Regeln abhängig (z.B. Klammern vor Punktrechnung vor Strichrechnung). Von daher sind boolsche Funktionsaufruf-Auswertungen erlaubt und völlig korrekt, auch wenn sie nicht in Variablen zwischengespeichert werden.

(editiert, präzisiert)

Peter(TOO)
27.07.2017, 23:50
Hallo HaWe,


if ( a && b && c) ....


Wenn a == FALSE ist, werden b und c gar nicht mehr ausgewertet, den egal welchen Wert b und c haben, kann das Resultat nie TRUE werden.
Interessant wird es, wenn b und c Funktionsaufrufe sind, welche dann nicht ausgeführt werden.

Ein BASIC-Interpreter muss zwangsläufig alle Ausdrücke auswerten, weil er etwas doof ist.
Ein BASIC-Compiler kann das handhaben wie in C.

MfG Peter(TOO)

HaWe
28.07.2017, 08:15
Hallo HaWe,


if ( a && b && c) ....


Wenn a == FALSE ist, werden b und c gar nicht mehr ausgewertet, den egal welchen Wert b und c haben, kann das Resultat nie TRUE werden.
Interessant wird es, wenn b und c Funktionsaufrufe sind, welche dann nicht ausgeführt werden.

Ein BASIC-Interpreter muss zwangsläufig alle Ausdrücke auswerten, weil er etwas doof ist.
Ein BASIC-Compiler kann das handhaben wie in C.

MfG Peter(TOO)

ja, das ist mir bekannt, und das habe ich auch gar nicht bestritten.
Es ging nur speziell um Ceos' Hinweis in Bezug auf die Evaluierung von Funktionsrückgabewerten in Ausdrücken, auch ohne Zwischenspeicherung.