Hallo,
ich habe mich hier angemeldet, da ich einige Threads gesehen aus dem
Forum gesehen habe und mir gedacht habe, dass man mir hier bestimmt
helfen kann.
Es geht um folgendes..
Ich habe ein Steuerteil disassembled und eine Sub-routine geschrieben,
die durch 2 Taster, zwischen 3 Codeblöcken hin und her schalten soll.
Soweit funktioniert auch alles, das Problem an der Sache ist blos, dass
"db3" nicht funktioniert, sondern lediglich zwischen "db1" und "db2"
geschaltet wird.
hier erstmal der Code um den es geht:
1
$MOD167 ; Define C167 mode
2
$SEGMENTED ; Segemented memory mode
3
$CASE ; Symbols case sensitive
4
$include (reg167.inc)
5
NAME MINIMON
6
ASSUME DPP3:system
7
StackData0 SECTION DATA SYSSTACK ; Data Section to reserve
8
; Stack-Memory
9
DSB 20H ; 32 Byte
10
StackData0 ENDS ; End of Dummy-Section
11
DriverCode0 SECTION CODE PUBLIC 'CDRIVER'
12
DriverProc PROC FAR
13
14
movb rl1,0xc361 ;adresse taster1
15
jnb r1.0, needle
16
movb rl1,0xc367 ;adresse taster2
17
jnb r1.0, xyz
18
movb rl1,#1
19
movb 0xcb40,rl1 ;taster2 debounce
20
jmpr cc_uc, needle
21
22
xyz:
23
movb rl1,0xcb40
24
jmpr cc_z, needle
25
movb rl1,#0
26
movb 0xcb40,rl1
27
movb rl1,0xcb43 ;delay counter
28
jmpr cc_nz, db1
29
movb rl1,#0x7f ;initialize delay counter so that r4 isnt updated for atleast ~ 40ms * 0x7F= 5080ms ~ 5sec
30
movb 0xcb43,rl1
31
jmpr cc_uc, needle
32
33
db1:
34
cmp dpp0,#0x34 ;wenn dpp0 == 0x34 gehe zu "db2"
35
jmpr cc_eq, db2
36
mov dpp0,#0x34 ;setzt dpp0 auf 0x34
37
mov dpp1,#0x35 ;setzt dpp1 auf 0x35
38
mov dpp2,#0x36 ;setzt dpp2 auf 0x36
39
jmpr cc_uc,needle
40
41
db2:
42
cmp dpp0,#0x38 ;wenn dpp0 == 0x34 gehe zu "db3"
43
jmpr cc_eq, db3
44
mov dpp0,#0x38 ;setzt dpp0 auf 0x38
45
mov dpp1,#0x39 ;setzt dpp1 auf 0x39
46
mov dpp2,#0x3A ;setzt dpp2 auf 0x3A
47
jmpr cc_uc,needle
48
49
db3:
50
mov dpp0,#0x3c ;setzt dpp0 auf 0x3c
51
mov dpp1,#0x3d ;setzt dpp1 auf 0x3d
52
mov dpp2,#0x3e ;setzt dpp2 auf 0x3e
53
54
needle:
55
mov rl1,0xcb43
56
jmpr cc_z,end1
57
subb rl1,#1
58
mov 0xcb43,rl1
59
cmp dpp0,#0x34 ;wenn dpp0 == 0x34
60
jmpr cc_ne, n1 ;gehe zu n1, sonst
61
mov r4,#0x1F40 ;setze U-angezeigt auf 4*2000
62
jmpr cc_uc, end1
63
64
n1:
65
cmp dpp0,#0x38 ;wenn dpp0 == 0x38
66
jmpr cc_ne, n2 ;gehe zu n2, sonst
67
mov r4,#0x2EE0 ;setze U-angezeigt auf 4*3000
68
jmpr cc_uc, end1
69
70
n2:
71
mov r4,#0x3E80 ;setze U-angezeigt auf 4*4000
72
73
end1:
74
add r0,#4
75
mov r9,r4 ;r9=U-gemessen r4=U-angezeigt
76
rets
77
78
79
DriverProc ENDP
80
DriverCode0 ENDS
81
END
Programmablauf ist folgender:
- Taster 1 wird gedrückt und gehalten.
- Taster 2 wird getastet und dadurch wird geschaltet.
- wenn geschaltet wird, werden 3 Werte geändert. dpp0, dpp1 und dpp2.
- am ende wird geprüft welcher datenblock aktiv ist und das wird dann in
"needle" ausgegeben
Da ich selbst mit ASM leider nicht so extrem bewandert bin, versuche ich
mal hier mein glück, auch für evtl spätere dinge, die noch in den code
einfliesen sollen, aber das erst, sobald die Hauptfunktion 100% passt.
Ich bevorzuge "learning-by-doing", das grundgerüst vom Code habe ich
gefunden und dann an meine Umgebung angepasst, deswegen habe ich als
kommentare mal angefügt, wie ich den Code verstehe.
Hoffentlich bin ich im richtigen Unterforum und es erklärt sich jemand
bereit, mir ein wenig unter die Arme zu greifen. :)
Ad 1:
needle:
mov rl1,0xcb43
jmpr cc_z,end1
Sicher, dass die Sprungbedingung stimmt? Wo wird das Zero-Flag gesetzt?
Ad 2: Kommentare wie der folgende sind fast immer nutzlos, weil sie nur
das wiederholen, was man auch so, d. h. links im Befehl, sieht. Und
manchmal sind sie auch falsch und führen in die Irre, wie du hier
siehst:
cmp dpp0,#0x38 ;wenn dpp0 == 0x34 gehe zu "db3"
Ad 3:
Gib den Variablen, den Sprungmarken und den Konstanten sprechende Namen.
Statt
"movb rl1,0xc361 ;adresse taster1"
besser
"movb rl1, adrT1"
Ad 4:
Bei Konstanten macht man das mit den sprechenden Namen so, dass vorne im
Programm irgendwo eine Liste steht:
adrT1 EQU 0xc361
adrT2 EQU ....
....
Chris schrieb:> jmpr cc_uc, needle
Um evtl. die Lesbarkeit (etwas) zu erhöhen:
- darf man beim unbedingten Sprung das cc_uc weglassen
- erkennt der Assembler automatisch, ob er ein relativer oder absoluter
Sprung nötigt ist, einfach JMP <target> hinschreiben
Chris schrieb:> eine Sub-routine geschrieben,> die durch 2 Taster, zwischen 3 Codeblöcken hin und her schalten soll
Kannst Du das evtl. mal in Tabellenform aufschreiben?
Die Umschaltung erfolgt durch Anpassung der drei DPP-Register?
Erstmal danke für Antworten euch beiden.
zu Rolf:
Zero-Flag... Da muss ich mich jetzt erstmal informieren was das ist. :)
Das die Kommentare hinter den Befehlen das ausdrücken, bzw halt auch
nicht, liegt daran, dass ich die kommentare dahinter geschrieben habe,
wie ich die Befehle verstehe.
Wie gesagt, ich habe von ASM keinerlei Ahnung und versuche mir das
gerade für diese bestimmte anwendung anzueignen.
Wenn ich die Adressen usw vorher definiere, dürfte der Compiler das ja
verstehen und alles einkürzen, sodass die ausgabe am ende nicht immens
größer wird.
Ich habe nämlich nur einen bestimmten bereich an bytes im Steuergerät
frei für diese sache.
Zu Rick:
Die Umschaltung erfolgt über die DPP Register, richtig.
---------------DPP1----DPP2---DPP3
Codeblock 1 = 0x34 0x35 0x36
Codeblock 2 = 0x38 0x39 0x3A
Codeblock 3 = 0x3C 0x3D 0x3D
(ich hoffe die formatierung wird übernommen)
An für sich funktioniert die Routine so auch.
Das einzige Problem bleibt lediglich immer der letzte Codeblock.
Dieser wird quasi einfach nicht beachtet und es schaltet immer zwischen
Codeblock 1 und Codeblock 2.
EDIT:
Um es etwas besser zu verstehen,..
Die Kupplung (Taster 1) wird getreten, wenn dann, während die Kupplung
getreten wird, der zweite taster gedrückt wird, wird der Codeblock
gewechselt.
Zur zeit ist es so, dass man dafür 5 sekunden zeit hat. (delaycounter)
später würde ich es bevorzugen, wenn das Programm immer eine sekunde
länger läuft, wenn man den codeblock gewechselt hat. aber das ist nur
für die bedienerfreundlichkeit wichtig. Erstmal muss die grundfunktion
gegeben sein.
So, kurzer Zwischenstand..
ich habe mich nun ein wenig in dieser Sache mit dem Branching usw
belesen.
und siehe da, der Code funktioniert.
Ich kann mir vorstellen, dass man diesen besser schreiben könnte, aber
nun habe ich erst einmal ein grundgerüst mit dem ich arbeiten kann.
Nun werde ich versuchen, die Laufzeit des Programms so anzupassen, dass
das Programm nicht nach 5 Sekunden, nach der ersten eingabe, beendet
wird, sondern immer 1 sekunde weiter läuft, nach jeder eingabe.
1
$MOD167 ; Define C167 mode
2
$SEGMENTED ; Segemented memory mode
3
$CASE ; Symbols case sensitive
4
$include (reg167.inc)
5
NAME MINIMON
6
ASSUME DPP3:system
7
StackData0 SECTION DATA SYSSTACK ; Data Section to reserve
8
; Stack-Memory
9
DSB 20H ; 32 Byte
10
StackData0 ENDS ; End of Dummy-Section
11
DriverCode0 SECTION CODE PUBLIC 'CDRIVER'
12
DriverProc PROC FAR
13
14
movb rl1,0xc361 ;b_kuppl
15
jnb r1.0, needle
16
movb rl1,0xc367 ;cc_cancel toggle button
17
jnb r1.0, xyz
18
movb rl1,#1
19
movb 0xcb40,rl1 ;cc_off debounce
20
jmpr cc_uc, needle
21
22
xyz:
23
movb rl1,0xcb40
24
jmpr cc_z, needle
25
movb rl1,#0
26
movb 0xcb40,rl1
27
movb rl1,0xcb43 ;delay counter
28
jmpr cc_nz, db1
29
movb rl1,#0x7f ; initialize delay counter so that r4 isnt updated by the ECU for atleast ~ 40ms * 0x7F= 5080ms ~ 5sec
Ich würde versuchen die Probleme voneinander zu trennen.
Die ganze Sache sieht aus, als wenn man sie gut auf eine Finite State
Machine (FSM) abbilden kann. In den jeweiligen States wird dann ein
passender Timer gestartet und bei Ablauf entsprechend der State
gewechselt.
Die Tastenentprellung und -abfrage wird sinnvollerweise separat
betrachtet.
Das Umschalten der Kennlinienfelder ist ja offensichtlich schon gelöst.
Chris schrieb:> zu Rolf:>> Zero-Flag... Da muss ich mich jetzt erstmal informieren was das ist. :)
Wieso? Du selbst hast "jmpr cc_z,end1" geschrieben. Ich kenne zwar
diesen speziellen Assembler nicht, aber das kann doch nur "Springe nach
'end1', falls das Zero-Flag den Wert 'logisch 1' hat" bedeuten. Das ist
das 'z' in dem Befehl. Also weißt du es doch?
Aber: 'needle:' und somit auch der obige Sprungbefehl kann von mehreren
Stellen aus angesprungen. Da ist für den Leser kaum noch zu erkennen,
wann das Zero-Flag gesetzt ist und wann nicht. Ein Paradebeispiel für
eine unübersichtliche Konstruktion, die garantiert irgendwann zu schwer
zu findenden Fehlern führt.
Zu demselben Thema:
xyz:
movb rl1,0xcb40
jmpr cc_z, needle
movb rl1,#0
movb 0xcb40,rl1
movb rl1,0xcb43 ;delay counter
jmpr cc_nz, db1
Da wird der zweite Sprung in jedem Fall ausgeführt: Die Bedingung
'nonzero' ist ja immer erfüllt. Das Programm tut also nicht das, was du
denkst.
> Wenn ich die Adressen usw vorher definiere, dürfte der Compiler das ja> verstehen und alles einkürzen, sodass die ausgabe am ende nicht immens> größer wird.
Richtig, es sollte keinerlei Änderung der Länge geben.
Guten Morgen,
die Sprünge sind beabsichtigt so.
beim ersten durchlauf des Programmes, wird geprüft ob die 2 Taster
gedrückt werden und dann wird erstmal kein Kennlinienfeld umgeschaltet,
deswegen der direkte Sprung zu "needle". Somit wird beim ersten mal
drücken der aktuell aktive Codeblock auf dem Drehzahlmesser angezeigt
und der Timer aktiviert.
Beim zweiten mal drücken, wird dann der Codeblock umgeschaltet.
Soweit funktioniert die ganze Sache auch konstant.
Habe es jetzt ein paar Tage testen können und es gab keinerlei ausfälle.
Da ich zur Zeit die Werkstatt voll habe, kann ich aber erstmal nicht
weiter machen mit einer besseren Version des Programms.
Chris schrieb:> die Sprünge sind beabsichtigt so.
Ein bedingter Sprungbefehl, bei dem einer der beiden Wege niemals
ausgeführt werden kann, ist unsinnig. Sieh dir mal das von mir zitierte
Codestück genau an.
> Soweit funktioniert die ganze Sache auch konstant.
Der Spruch, den ich vor Jahrzehnten als Anfänger selbst gebracht habe,
wenn mir gesagt wurde, so und so dürfe man das und das nicht machen: "Es
funktioniert doch, also ist es nicht falsch!" Nach einem Jahr wusste ich
es besser.
Der Sprung wird ja aber ausgeführt.
Sobald der Timer angelaufen ist, wird er nicht mehr ausgeführt.
Rolf schrieb:> Der Spruch, den ich vor Jahrzehnten als Anfänger selbst gebracht habe,> wenn mir gesagt wurde, so und so dürfe man das und das nicht machen: "Es> funktioniert doch, also ist es nicht falsch!" Nach einem Jahr wusste ich> es besser.
Nein nein, so war das nicht gemeint. Ich hatte ja schon geschrieben,
sobald ich wieder mehr Zeit habe, setze ich mich dran, den Code
"korrekt" zu machen.
denn auch für einen selbst ist es besser, wenn die Adressen usw mit
Namen da stehen.
Auch für die Übertragbarkeit des Codes ist es besser, wenn man die Namen
setzt.
So verhindert man, dass man bei Anpassungen Adressen vergisst zum
Beispiel.
Mir ging es mit der Aussage nur darum, dass ich erst einmal testen
musste, ob das system mit dem Umschalten der Codeblöcke in der Praxis
funktioniert.
Quasi eine Beta Version.
Chris schrieb:> Der Sprung wird ja aber ausgeführt.> Sobald der Timer angelaufen ist, wird er nicht mehr ausgeführt.
Einer von uns beiden kann den Code nicht lesen. :-(
Rolf schrieb:> Chris schrieb:>> Der Sprung wird ja aber ausgeführt.>> Sobald der Timer angelaufen ist, wird er nicht mehr ausgeführt.>> Einer von uns beiden kann den Code nicht lesen. :-(
Das werde dann definitiv ich sein.
Da ich von ASM nicht viel Ahnung habe.
Ich kann nur nach dem gehen, was ich sehe.
Und zur Zeit ist es halt so, dass "r1l, #0x7f" ausgeführt wird.
Daraus schlussfolgere ich, dass der Sprung davor mindestens einmal nicht
ausgeführt wird.
Denn wenn ich #0x7f ändere auf #0xfa bleibt das Programm keine 5
Sekunden aktiv sondern 10.
Wie ich schon sagte, ich versuche mir die ganze Thematik mit dem
Schreiben von eigenen Programmen für das Steuergerät gerade
beizubringen.
Und ich bin über jeden Tipp dankbar.
Wie zum Beispiel deinem Tipp mit dem Namen verteilen.
Das macht die ganze Sache wirklich viel übersichtlicher.
Wenn dieser Sprung theoretisch nicht funktionieren dürfte, weiß ich
nicht woran das liegt, dass er dennoch funktioniert.
Dieser Teil wird aber vermutlich eh bald Geschichte sein, da ja der
Timer zukünftig nicht am Anfang gestartet werden soll, sondern bei jedem
Tastendruck neu gesetzt werden soll.
Chris schrieb:> Hoffentlich bin ich im richtigen Unterforum
Nicht ganz, aber hier verschwindet das Thema auch nicht so schnell in
der Versenkung, wie weiter oben.
Du kannst dein Programm auch mit Pfeilen, bzw. in einem Flussdiagramm
ansehen.
Es kann auch helfen, sich das Programm als Ablauf einer
Dominosteinkettenreaktion vorzustellen.
Wenn man vorbildlichen Programmcode hat, dann kann man auch da
nachsehen, z.B. wie Sprünge effektiv organisiert werden und mit etwas
Glück Techniken übernehmen.
Die Sache mit den Flussidagrammen hat recht gut geholfen, als ich die
File vom Steuergerät disassembled habe.
Dadurch habe ich zum Beispiel wie die Sache mit den Checksummen bei dem
Steuergerät funktioniert und konnte die Checksummen Abfrage dadurch
deaktivieren, damit mein Programm überhaupt läuft.
Nun sitze ich allerdings da, und zerbreche mir den Kopf, wie ich das
realisieren könnte, dass der Timer bei jedem Tastendruck verlängert
wird, ohne den restlichen Ablauf des Steuergerätes zu stören.
Chris schrieb:> Nun sitze ich allerdings da, und zerbreche mir den Kopf, wie ich das> realisieren könnte, dass der Timer bei jedem Tastendruck verlängert> wird, ohne den restlichen Ablauf des Steuergerätes zu stören.
Üblicherweise hat man auf embedded-Systemen sowas wie eine Hauptschleife
(beim Arduino heißt die loop()). Diese wird ggf. von Interrupts
unterbrochen.
Ganz zeitkritische Dinge, die schnell erledigt werden müssen, werden
direkt im Interrupt versorgt. Alles andere (wie z.B. ein Tastendruck)
läuft über Flags.
Du hast also eine Zählvariable, die bei Tastendruck (wieder) aufgeladen
wird. Ein Tick-Timer setzt das tick-Flag und in der Hauptschleife wird
bei gesetzten tick-Flag eine Funktion aufgerufen, die sich um die
Zählvariable kümmert:
Solange die Variable größer als Null ist, wird sie dekrementiert.
Wenn die Null erreicht wird, rufst Du die gewünschte Funktion auf.
Die Zählvariable bleibt solange auf Null, bis wieder jemand kommt und
die Taste drückt.
P.S.: Für Flags hat die C166/C167-Familie sogar einen eigenen
bitaddressierbaren Speicherbereich im internen RAM, der z.B. mit
BSET/BCLR/BCMP genutzt werden kann.