ASM Codehilfe / c167

OP #7674220
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. :)

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

....

#7676939
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?

OP #7676956
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.

OP #7678133
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
#7678304
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.

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

OP #7681181
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.

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

OP #7681244
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.

OP #7681851
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.

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

OP #7687319
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.

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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren