Forum: Fahrzeugelektronik ASM Codehilfe / c167


von Chris (07mtrclt)


Lesenswert?

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. :)

von Rolf (rolf22)


Lesenswert?

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  ....

....

: Bearbeitet durch User
von Rick (rick)


Lesenswert?

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?

von Chris (07mtrclt)


Lesenswert?

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.

: Bearbeitet durch User
von Chris (07mtrclt)


Lesenswert?

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
30
movb 0xcb43,rl1
31
jmpr cc_uc, needle
32
33
db1:
34
cmp     dpp0, #0x34 
35
jmpr    cc_eq, db2
36
cmp     dpp0, #0x38 
37
jmpr    cc_eq, db3
38
cmp     dpp0, #0x3c 
39
jmpr    cc_eq, db4
40
41
db2:
42
mov    dpp0,#0x38
43
mov   dpp1,#0x39
44
mov   dpp2,#0x3A
45
jmpr    cc_uc, needle
46
47
db3:
48
mov     dpp0, #0x3c 
49
mov     dpp1, #0x3d
50
mov     dpp2, #0x3e
51
jmpr    cc_uc, needle
52
53
db4:
54
mov     dpp0, #0x34 
55
mov     dpp1, #0x35
56
mov     dpp2, #0x36
57
jmpr    cc_uc, needle
58
59
60
needle:
61
mov rl1, 0xcb43
62
jmpr cc_z,end1
63
subb rl1,#1
64
mov 0xcb43,rl1
65
cmp dpp0,#0x34
66
jmpr cc_eq, n1
67
cmp dpp0,#0x38
68
jmpr cc_eq, n2
69
mov r4,#0x3E80
70
jmpr cc_uc, end1
71
72
n1:
73
mov r4,#0x1f40
74
jmpr cc_uc, end1
75
76
n2:
77
mov r4,#0x2ee0
78
79
end1:
80
add r0,#4
81
mov r9,r4
82
rets
83
84
85
DriverProc  ENDP
86
DriverCode0  ENDS
87
    END

von Rick (rick)


Lesenswert?

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.

von Chris (07mtrclt)


Lesenswert?

Viele Begriffe, mit denen ich mich jetzt mal befassen werde.

Vielen Dank für die Tipps.

von Rolf (rolf22)


Lesenswert?

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.

von Chris (07mtrclt)


Lesenswert?

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.

von Rolf (rolf22)


Lesenswert?

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.

von Chris (07mtrclt)


Lesenswert?

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.

von Rolf (rolf22)


Lesenswert?

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.  :-(

von Chris (07mtrclt)


Lesenswert?

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.

von Rbx (rcx)


Lesenswert?

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.

von Chris (07mtrclt)


Lesenswert?

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.

von Rick (rick)


Lesenswert?

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.

: Bearbeitet durch User
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.