Hallo zusammen,
ich versuche zu verstehen, wie ein Programm in Assemblersprache und das
Hexfile, welches in den Mikrocontroller übertragen wird, zusammenhängen.
Im Dokument „AVR Instruction Set Manual“, welches Atmel bzw. Microchip
zum Download bereitstellt, ist der AVR Befehlssatz vollständig
beschrieben, auch mit den zu jedem Befehl gehörigen Opcode. Ich erwarte
natürlich, dass die Assemblerbefehle nach der Kompilierung als Opcode im
Code des .Hex Files auftauchen. Und da finde ich ihn nicht...
Ich nutze AtmelStudio und einen Atmega32A, an welchen 8 LEDs an Port D
angeschlossen sind. Der Code funktioniert und das Bitmuster 0xCC
(0b11001100) wird über die LEDs angezeigt.
Hier der Assemblercode:
1
.INCLUDE "m32Adef.inc"
2
.CSEG
3
rjmp start
4
.ORG 0x13
5
6
start:
7
ldi r16, 0xFF
8
out DDRD, r16
9
10
mainloop:
11
12
ldi r16, 0xCC
13
out PORTD, r16
14
rjmp mainloop
15
16
.EXIT
Und hier mein Hexfile, nachdem ich es in *.txt umbenannt und im
Texteditor geöffnet habe:
:020000020000FC
:0200000012C02C
:0A0026000FEF01BB0CEC02BBFDCF95
:00000001FF
Die AVR Instruction Set Manual liefert für den Befehl "ldi" folgenden
Opcode:
16-Bit Opcode: "1110 KKKK dddd KKKK"
Ich erwarte also die Code-Sequenz "EC0C". E für 1110. C für das erste
Nibble der Konstante CC, 0 für das Register r16, und C für das zweite
Nibble der Konstanten CC.
Wenn ich den Befehl
1
ldi r16, 0xCC
ändere in
1
ldi r16, 0xFF
so ändert sich nach erneuter Kompilierung mein Opcode zu:
:020000020000FC
:0200000012C02C
:0A0026000FEF01BB0FEF02BBFDCF8F
:00000001FF
Die letzten beiden Hexadezimalzahlen in Zeile drei ändern sich von 95 zu
8F. Kann mir das jemand erklären?
Mit Dank und Gruß,
Janik
> :0A0026000FEF01BB-0FEF-02BBFDCF8F
Du solltest Dich noch mit dem Intel-Hex-Format und dem Thema Byteorder
befassen.
Ich habe mal mit - die Befehle eingerahmt.
Das letzte Byte ist die Prüfsumme der Zeile.
Gruß aus Berlin
Michael
Trotzdem hätte ich die Codesequenz EC0C erwartet. Wieso taucht die nicht
auf? ich würde gerne die Opcodes neben die entsprechenden
Assemblerbefehle schreiben, um die zuzuordnen. Das gelingt mir aber
nicht. Kann mir jemand helfen. Wenn ich einen schlüssigen opcode finde,
kann ich den Rest sicher selbst rausarbeiten.
Janik schrieb:> Trotzdem hätte ich die Codesequenz EC0C erwartet. Wieso taucht die nicht> auf?Janik schrieb:> :0A0026000FEF01BB0CEC02BBFDCF95
^^^^
Ist doch alles da: BB 0CEC 02
Janik schrieb:> Wieso wird denn aus EC0C -> 0CEC?
Siehe da:
Michael U. schrieb:> Du solltest Dich noch mit dem Intel-Hex-Format und dem Thema Byteorder> befassen.
Ausserdem solltest du bedenken, das du aus dem Opcode nicht
zwangslaeufig auf den verwendeten ASM-Befehl schliessen kannst. Da
fallen einige raus.
Siehe hier:
Beitrag "AVR: Werden gleiche Opcodes unterschieden?"
Hi,
habe noch eine Frage zum Programm.
Also Pin Change A Interrupt Einsprungadresse wird da genommen:
.ORG 0x13 oder?
Aber es folgt darauf kein Sprungbefehl auf eine Interrupt Service
Routine.
Wo gibt es dazu Infos, damit ich noch etwas dazulerne.
Also, wenn es erlaubt ist, "meine" Version:
.nolist
.INCLUDE "m32Adef.inc"
.list
;
.CSEG
.org 0x0000
rjmp start
.ORG 0x0013
rjmp ISR
;
start:
ser r16
out DDRD, r16
;
mainloop:
ldi r16, 0xCC
out PORTD, r16
rjmp mainloop
;
ISR:
nop
reti
;ciao gustav
Wenn man das Listfile zuschaltet erhaelt man ASM Code neben der Hex
Codierung in der anderen Kolonne. Das Intel Hex file muss man nicht
anschauen. Allenfalls kann man sich ueberlegen, erst aus Intel Hex
Binaer zu machen.
Hi,
und noch das zugehörige Hexfile:
:020000020000FC
:0200000013C02B
:1000260005C00FEF01BB0CEC02BBFDCF000018951D
:00000001FF
Und zurück mit Disassemble-Feature (Bild)
ciao
gustav
Karl B. schrieb:> habe noch eine Frage zum Programm.> Also Pin Change A Interrupt Einsprungadresse wird da genommen:> .ORG 0x13 oder?
Oder.
> Wo gibt es dazu Infos, damit ich noch etwas dazulerne.
Im Datenblatt gibt es eine Tabelle, die die Interrupt-Vektoren und die
dazugehörigen Adressen im Flash nennt. Aber da die Vektoren allen an
geraden Adressen liegen, kann 0x0013 nicht richtig sein.
> Also, wenn es erlaubt ist, "meine" Version:>> .nolist> .INCLUDE "m32Adef.inc"> .list> ;> .CSEG> .org 0x0000> rjmp start> .ORG 0x0013> rjmp ISR> ;> start:
...
Gräßlich.
Dein Startcode landet so mitten in der Vektortabelle. Und etliche
Vektoren sind schlicht leer, weiß der Teufel was dann passiert, wenn so
ein Interrupt mal ausgelöst wird.
Korrekt macht man das so, daß man immer die komplette Vektortabelle
ausfüllt. Und erst dahinter kommt der eigentliche Programmcode. Ob man
die ungenutzten Interrupt-Vektoren auf ein einsames RETI zeigen läßt
oder auf eine Routine, die eine Fehler-LED einschaltet und dann in eine
Endlosschleife geht, ist eine andere Frage. Zum Experimentieren eher
letzteres.
Axel S. schrieb:> Gräßlich.
Hi,
Genau, ist ja auch nicht meine Programm.
Die Frage zielte genau darauf ab, wieso eine Interrupteinsprungadresse
angegeben wird, ohne sich weiter darum zu kümmern.
Also, persönlich mache das seit einiger Zeit immer so, wie im
angehängten
File gezeigt, auch, wenn nur ein einziger Interrupt verwendet wird. Ist
zwar "aufgeblasen", man erlebt dann aber auch keine Überraschungen.
(Nur beim Target-Wechsel muss man wieder anpassen.)
ciao
gustav
Karl B. schrieb:> Also, persönlich mache das seit einiger Zeit immer so, wie im> angehängten> File gezeigt
Du kannst dir die ganzen 'reti' sparen, es reicht ein einziges:
1
;Interrupt Service Routinen:
2
;
3
INT0adr: ;Extern. Interrupt Request 0
4
INT1adr: ;Extern. Interrupt Request 1
5
INT2adr: ;Extern. Interrupt Request 2
6
INT3adr: ;Extern. Interrupt Request 3
7
INT4adr: ;Extern. Interrupt Request 4
8
INT5adr: ;Extern. Interrupt Request 5
9
INT6adr: ;Extern. Interrupt Request 6
10
INT7adr: ;Extern. Interrupt Request 7
11
PCI0adr: ;Pin Change Interrupt Request 0
12
PCI1adr: ;Pin Change Interrupt Request 1
13
USB_GEN: ;USB General Interrupt Request
14
USB_COM: ;USB Endpoint/Pipe Interr. Com. Request
15
WDTadre: ;Watchdog Timeout Interrupt
16
ICP1adr: ;Timer/Counter2 Capture Event
17
OC1Aadr: ;Timer/Counter2 compare match A
18
OC1Badr: ;Timer/Counter2 compare match B
19
OC1Cadr: ;Timer/Counter2 compare match C
20
OVF1adr: ;Timer/Counter1 overflow
21
OC0Aadr: ;Timer/Counter0 compare match A
22
OC0Badr: ;Timer/Counter0 compare match B
23
OVF0adr: ;Timer/Counter0 overflow
24
SPIadre: ;SPI Serial Transfer Complete
25
URXC1ad: ;USART1 RX Complete
26
UDRE1ad: ;USART1 Data Register Empty
27
UTXC1ad: ;USART1 TX Complete
28
ACIadre: ;Analog Comparator
29
ERDYadr: ;EEPROM Ready
30
SPMRadr: ;Store Program Memory Read
31
: if desired, this would be the place for a 'Bad Interrupt' Routine
32
;
33
reti
So zeigen alle Routinen erstmal auf einen Stub. Die benutzten Interrupts
dann hier rausnehmen und behandeln.
Erst einmal "Danke" an @M..
Matthias S. schrieb:> So zeigen alle Routinen erstmal auf einen Stub. Die benutzten Interrupts> dann hier rausnehmen und behandeln.
Ja, wenn man von vorne herein nicht weiß, wie das Prog später aussehen
soll, lieber 100 x abschreiben, sonst komm ich immer durcheinander.
Ist zwar unelegant, habe aber schon viele Fehler deswegen vermieden, die
ich mir vorher nicht erklären konnte.
Hi, noch einmal zurück zum Prog. des TO
org. 0x13
Das stört mich irgendwie.
Darum nochmal eine Nachfrage.
Wie ich drauf komme ?:
Es gibt auch "ungeradzahlige" Ints.
z.B. .equ URXCaddr = 0x0007 ; UART, Rx Complete beim Attiny 2313
der verwendete 32-er hat die nicht. Das "falsche" def.inc file
zugrundegelegt.sorry...
ciao
gustav
>Du kannst dir die ganzen 'reti' sparen, es reicht ein einziges:
dann hast du ca. 20 Labels/Namen auf EINE Adresse.
Du kannst 100 Labels benutzen aber ohne nop o.ä. bleiben die Adressen
gleich.
besser:
- bei kleineren µC (jmp==rjmp)=1W je Interrupt bsp. nop/rjmp/reti
- bei größeren '(mehr Flash) (jmp!=rjmp) 2W je Interrupt bsp.
nop+nop/jmp /reti+nop oder nop+reti
vereinfacht: 13 ist möglich sofern der µC bescheiden unter 4kb(?)
bleibt, ansonsten sind ungerade Zahlen von der Ziehung der
Interupttadressen ausgeschlossen.
Dirk schrieb:> dann hast du ca. 20 Labels/Namen auf EINE Adresse.
Ja, und? Das ist doch völlig egal. Es wird so genau eine Anweisung
benötigt, statt für jede Routine eine eigene.
Es geht hier ja nur um die unbenutzten IRQ Vektoren.
Dirk schrieb:> - bei kleineren µC (jmp==rjmp)=1W je Interrupt bsp. nop/rjmp/reti
Auch das ist völlig unnötig, was soll das für Vorteile haben?
Den entsprechenden Vektor nimmst du aus dem Stub raus und schreibst den
Interrupt. Die Call Tabelle ruft ja diesen Vektor schon auf.
Matthias S. schrieb:> Ja, und? Das ist doch völlig egal. Es wird so genau eine Anweisung> benötigt, statt für jede Routine eine eigene. Es geht hier ja nur um die> unbenutzten IRQ Vektoren.
Ähm - nö.
Du hast da was Grundlegendes übersehen. Die Adressen der
Interrupteinsprünge sind sozusagen im Controller hartcodiert. Deine
Labels interessieren den Controller einen Dreck. Die stehen in dem Fall
alle an der gleichen Stelle, und zwar ganz vorn.
Entweder mit .org vor jedem Label die Adresse setzen, oder mit einem
reti nach jedem Label die Adresse für das nächste inkrementieren. Oder
wenigstens das einzige reti per .org ganz ans Ende der Tabelle, dann
passiert Das was Du eigentlich erreichen willst. Aber dann kannst Du die
Label auch komplett weglassen, den wie gesagt die sieht der Controller
eh nicht. Da kannste auch Pippilangstrumpf reinschreiben.
Karl B. schrieb:> (Nur beim Target-Wechsel muss man wieder anpassen.)
Das ist der Nachteil dieser Variante. Auf einem anderen Prozessor kommt
Blödsinn raus, wenn die Vektortabelle anders ist.
Bei den Angaben beim .ORG kann man statt Magic Numbers auch die
vordefinierten Namen für die Einträge der Vektortabelle nutzen. Wenn man
dann den Prozessor wechselt, sind die Interrupts dann trotzdem richtig
sortiert, bzw. bei denen, die es dort nicht gibt, kommt ein Fehler, weil
der Name nicht aufgelöst werden kann.
Karl B. schrieb:> Wie ich drauf komme ?:> Es gibt auch "ungeradzahlige" Ints.> z.B. .equ URXCaddr = 0x0007 ; UART, Rx Complete beim Attiny 2313>> der verwendete 32-er hat die nicht. Das "falsche" def.inc file> zugrundegelegt.sorry...
Wenn du schon weißt, dass die Namen definiert sind, warum um alles in
der Welt nutzt du die dann nicht, sondern schreibst stattdessen 0x13
hin?
Dirk schrieb:> vereinfacht: 13 ist möglich sofern der µC bescheiden unter 4kb(?)
4k Words, also 8kB.
> bleibt, ansonsten sind ungerade Zahlen von der Ziehung der> Interupttadressen ausgeschlossen.
Karl schrieb:> Du hast da was Grundlegendes übersehen. Die Adressen der> Interrupteinsprünge sind sozusagen im Controller hartcodiert. Deine> Labels interessieren den Controller einen Dreck. Die stehen in dem Fall> alle an der gleichen Stelle, und zwar ganz vorn.
Ja und Nein. Ja, die Labels zeigen alle auf eine Adresse. Aber Nein,
weder stehen sie ganz vorn noch sind sie dem Controller egal. Du bist
gedanklich immer noch beim AVR hängen geblieben. Matthias hingegen ist
bei einem Controller, in dem die Vektortabelle eine wirkliche Tabelle
ist.
Der AVRspringt bei einen Interrupt an die entsprechende Position im
Flash, weswegen dort ein gültiger Befehl stehen muß (typischerweise ein
RJMP). Andere µC haben in der Vektortabelle lediglich Adressen stehen
und der Controller macht beim Interrupt einen indirekten Sprung: er
fischt die Adresse aus der Tabelle und springt dann dahin.
Karl schrieb:> Entweder mit .org vor jedem Label die Adresse setzen, oder mit einem> reti nach jedem Label die Adresse für das nächste inkrementieren.
Nein. Ein ORG hat innerhalb der Tabelle nichts verloren. Da gehört nur
ein einziges ORG an den Anfang der Tabelle. Spezifisch für den AVR
würde die Tabelle so aussehen:
1
.org 0
2
RJMP RESET
3
RJMP INT0
4
RJMP INT1
5
...
(komplette Tabelle gefüllt mit RJMPs zu Labels mit sprechenden Namen)
Weiter hinten im Code werden alle vorerst ungenutzten Interrupts vor
einen einzelnen RETI aufgereiht:
Axel S. schrieb:> Karl schrieb:>> Du hast da was Grundlegendes übersehen. Die Adressen der>> Interrupteinsprünge sind sozusagen im Controller hartcodiert. Deine>> Labels interessieren den Controller einen Dreck. Die stehen in dem Fall>> alle an der gleichen Stelle, und zwar ganz vorn.>> Ja und Nein.
Wieso Nein? Axel S. hat vollkommen Recht. Der Codeauszug von Matthias
S. ist vollkommener Blödsinn.
Axel S. schrieb:> Andere µC haben in der Vektortabelle lediglich Adressen stehen> und der Controller macht beim Interrupt einen indirekten Sprung:
Auch dann wäre die Variante von Matthias S. nicht lauffähig!
Axel S. schrieb:> Nein. Ein ORG hat innerhalb der Tabelle nichts verloren. Da gehört nur> ein einziges ORG an den Anfang der Tabelle.
Das ist nun wieder absoluter Blödsinn!
Wenn ich nur die letzten drei INT-Vektoren der Tabelle brauche, müsste
ich ja die Lücke vom RESET-Vektor bis zu meinen verwendeten Vektoren
irgendwie ausfüllen. Das kann ich mit dem von Matthias falsch
angedeutetem RETI oder mit der ORG-Anweisung lösen.
Das geht sowohl bei festen INT-Einsprungstellen (AVR, 8051, Z8...) und
auch bei vektorisierten Interrupts (wie z.B. beim Z80)!
Route 6. schrieb:> Der Codeauszug von Matthias S. ist vollkommener Blödsinn.
Ich glaube, Du hast den Codeauszug von Matthias S. einfach
fehlinterpretiert. Das soll keine Interrupt-Tabelle sein, sondern der
Code, der aus der Interrupt-Tabelle per RJMP angesprungen wird.
Route 6. schrieb:> Axel S. schrieb:>> Karl schrieb:>>> Du hast da was Grundlegendes übersehen. Die Adressen der>>> Interrupteinsprünge sind sozusagen im Controller hartcodiert. Deine>>> Labels interessieren den Controller einen Dreck. Die stehen in dem Fall>>> alle an der gleichen Stelle, und zwar ganz vorn.>>>> Ja und Nein.>> Wieso Nein? Axel S. hat vollkommen Recht.
Du bist durcheinander. Erst kritisierst du meinen Post, um mir im
nächsten Satz zu bescheinigen, ich hätte "vollkommen Recht".
> Der Codeauszug von Matthias S. ist vollkommener Blödsinn.
Du hast ihn wohl nur nicht verstanden.
>> Andere µC haben in der Vektortabelle lediglich Adressen stehen>> und der Controller macht beim Interrupt einen indirekten Sprung:>> Auch dann wäre die Variante von Matthias S. nicht lauffähig!
Wieder: du hast es nur nicht verstanden. Ich hatte übrigens auch nicht
genau genug geschaut. Matthias hat ja gar keine Tabelle gezeigt. Nur
die Implementierung der Dummy-ISR.
>> Ein ORG hat innerhalb der Tabelle nichts verloren. Da gehört nur>> ein einziges ORG an den Anfang der Tabelle.>> Das ist nun wieder absoluter Blödsinn!
Manieren!
> Wenn ich nur die letzten drei INT-Vektoren der Tabelle brauche, müsste> ich ja die Lücke vom RESET-Vektor bis zu meinen verwendeten Vektoren> irgendwie ausfüllen.
Die Vektortabelle hat keine Lücken. Das sind alles gültige Vektoren,
auch dann wenn du sie nicht nutzt. Und es ist zwar vielleicht nicht
absolut zwingend, trotzdem aber höchst empfehlenswert, auch die
ungenutzten Vektoren auf etwas zeigen zu lassen, das etwas
wohldefiniertes tut. Im Zweifel einfach ein IRET.
Da andererseits ein aktivierter Interrupt ohne die zugehörige ISR ein
klarer Fehler ist, ist es zumindest in der Entwicklungsphase der
Software besser, wenn man diesen Fehler klar diagnostizierbar macht.
Matthias S. schrieb:> Es geht hier ja nur um die unbenutzten IRQ Vektoren.
da gibt es wohl einige Missverständnisse um welche IRQ 'Vektoren' es
geht.
Ich hab deinen relativen Bezugspunkt (Listing mit von Karl B
programmierten 'Vektoren' /jmp's) erst übersehen und die Aussage
>Die benutzten Interrupts dann hier rausnehmen und behandeln
kann sehr interessant werden, wenn 'unbenutzte' Interrupts
'normalerweise' (u.a. meine Gewohnheit) an Ort und Stelle (Addr
0..max_vector_size) mit reti behandelt werden und während Karl B. erst
mal alle IRQ 'raus'jmp und 'draußen' jeden IRQ individuell mit einem
eigenen reti abschließt, aber innerhalb von draußen jeweils zwischen
label:
reti
eine vor Ort Behandlung programmieren könnte.
Es gibt da interessante Widersprüche, dass du glaubst Karl B wolle
überhaupt reti sparen und ich vermutlich dachte dann kann man sich den
jmp zum reti gleich ganz sparen (wäre eine Möglichkeit, aber ich hab nur
den Bezug zum Listing nicht gesehen, aber nach dem Besuch des Listings
vermutlich die Intention von Karl B. fehlerfreier gelesen)
(erstes) kurz: ca. zwei Programmierstile verursachen unterschiedliche
IRQ-Vektoren die leichte Kompatibiltätsprobleme in einem Forum haben.
Programmierer sich häufig Menschen und da können sich Stile
unterscheiden und wenn man etwas lernen will dann ist auch ganz
interessant sich mehrere(!)Varianten anzuschauen (und auch Programmierer
sprachliche Effekte dabei zu beachten):
Ein Interuptfester Programmierer, der an kontrollierte Interupt-Ursachen
glaubt und sich dafür nicht dafür interessiert was der Teufel weiß wenn
"so ein Interrupt [zufällig(?)] mal ausgelöst wird":
1
.org 0
2
jmp start
3
.org INT_VECTORS_SIZE; kann bei Glaube an vorher korrekte Reihenfolge entfallen
4
start:
5
;mit einem rausgeholten Interupt:
6
.org 0
7
jmp start
8
.org INT0addr
9
jmp prg_int0; Programmierer erwartet ex_int0 eingeschaltet zu haben und holt den passenden Interupt raus
10
start:
(Vorteil: sehr viel gespart, weder zu viel Speicher noch zu viel Text)
Programmierer der erst alle Interupts rausholt (Karl B)
1
org 0x0000
2
rjmp RESET ;"RESET vector"
3
.org 0x0002
4
rjmp INT0adr ;Extern. Interrupt Request
5
...
6
.org 0x0038
7
rjmp SPMRadr ;Store Program Memory Read
8
;
9
;Interrupt Service Routinen:
10
INT0adr: ;Extern. Interrupt Request 0
11
behandlungsplatz 1
12
reti
13
INT1adr: ;Extern. Interrupt Request 1
14
reti
15
...
16
RESET:
(Vorteil: kann von Karl B. und anderen besser gelesen werden)
Programmierer der befürchtet Karl B wollte gar nicht so viele reti
investieren
1
.org 0x0000
2
rjmp RESET ;"RESET vector"
3
.org 0x0002
4
rjmp INT0adr ;Extern. Interrupt Request
5
...
6
.org 0x0038
7
rjmp SPMRadr ;Store Program Memory Read
8
;
9
;Interrupt Service Routinen:
10
INT0adr:
11
INT1adr:
12
...
13
reti
14
raus_geholt:
15
behandlung
16
reti
17
RESET:
18
...
(Vorteil: relativ zu Karl B. viele reti gespart, aber eins mehr als
weiter oben verwendet)
Nach Tabellennorm programmierter Programmierer/Ausfüller, der
nebenberuflich über man aufklärt ("Korrekt macht man das so, daß man
immer die komplette Vektortabelle *ausfüllt*")
1
org 0
2
rjmp RESET
3
reti ;hier könnte ein anderer korrekter Vektor stehen
4
reti ;hier könnte ein anderer korrekter Vektor stehen
5
reti ;hier könnte ein anderer korrekter Vektor stehen
6
...
7
reti ;hier könnte ein anderer korrekter Vektor stehen
8
reti ;hier könnte ein anderer korrekter Vektor stehen
9
Reset:
(Vorteil: muss nicht denken und kann die gesparte Leistung zur
Aufklärung von Abweichlern nutzen)
oder ein Programmierer der wirklich etwas knapp bei Speicher ist und
ernsthaft reti 'sparen' will/muss (und kein Interesse an Teufel & Co
hat)
1
.org 0
2
anweisung 1; geht üblicherweise eh bei 0 los
3
anweisung 2;
4
...
5
jmp end_r_vec
6
.org 13
7
jmp int13; der berühmte int 13 wird rausgeholt
8
end_r_vec:..;weiter im Programm
(Vorteil: der Sparwille ist leicht ablesbar und spart u.U. 20 Byte)
Neben der hoffentlich geklärten Frage warum int 13 bei größeren µC
tabuisiert ist(weil nur gerade Adressen 32-bit-jmp sicher adressieren
können)ist bei deinem Vorschlag eine gewisse Ähnlichkeit mit einem
traditionellen NOP-Slide aus der PC-Heap-Sprayer-Szene zu erkennen (der
Einsprung soll viele Möglichkeiten haben), praktisch ein NOP-Slide ohne
nop. Nichts dramatisches, aber irgendwie anders als anders gewohnt.
Da ich gestern das Listing als Bezug übersehen hab und deinen Vorschlag
als Programmanfang gelesen hab, aber aktuell noch die Vorstellung
interessant finde wenn dein Programm (ohne Karlsteil) von einem µC
ausgeführt würde (aus geplantem Kommentar):
Der Programmablauf vom µC ausgeführt (an Adresse 0:reti):
- der µC erhält Strom
- Anweisung 'zurück' aus Interupt(reti)
- im Stack-Speicher könnte nach längerer Stromlosigkeit $ff $ff
eventuell sicher sein
==>'Rück'sprung an Adresse $ffff
sicherlich auch interessant, wenn ein µC nach längerem ausschalten
wahrscheinlicher einen sichereren Anfang am Ende des Programmspeichers
vollzieht, aber mit Programmierung im traditionellen Sinne hätte das
wenig zu tun.
Wahrscheinlich werden einige Programierstile bestimmte Fehler
wahrscheinlicher beeinflussen, aber bevor ein Programmierer/Mensch den
Teufel zur Erklärung benötigt kann ein offenerer Umgang mit Sprache auch
Spass machen:
Nicht nur rausgeholte Interrupts, sondern auch der Vektorraum der in der
Speichersparvariante mit ungewöhnlichen Aufgaben belegt wurde können
missverstanden werden, aber funktionieren auf dem µC (häufig).
Disclaimer: die Moralprogrammierer-Version die über verlorene Seelen
programmiert ("Ein ORG hat innerhalb der Tabelle nichts verloren. Da
gehört nur ein einziges ORG an den Anfang der Tabelle.")habe ich
vergessen, aber der gehörige Prediger sich sicher nichts dabei gedacht
haben.
Dirk schrieb:> Ein Interuptfester Programmierer, der an kontrollierte Interupt-Ursachen> glaubt und sich dafür nicht dafür interessiert was der Teufel weiß wenn> "so ein Interrupt [zufällig(?)] mal ausgelöst wird"
Du hast meinen Punkt nicht mal annähernd verstanden. Es geht nicht
darum, ob ein Programmierer "interruptfest" ist. Es geht darum, daß
Programmierer Menschen sind und (deswegen?) Fehler machen.
Zum Beispiel könnte ein Programmierer zwar einen Interrupt aktivieren,
dann aber vergessen, die ISR auch zu programmieren. Oder er benutzt den
falschen Namen für die ISR (das kann in C recht leicht passieren). Oder
er verschusselt den Namen eines Bits oder Registers und aktiviert so
einen ganz anderen Interrupt als vorgesehen. Oder oder oder.
Und dann geht es darum, den Schaden zu begrenzen. Und den Fehler (der ja
nicht offensichtlich sein muß) erkennbar zu machen. Der avr-gcc fügt
z.B. für ungenutzte Vektoren einen Sprung zum Reset-Vektor ein. In so
einem Fall würde der unbeabsichtigt ausgelöste Interrupt zum Neustart
des Programms führen. Und ich erinnere mich an diverse Threads in diesem
Forum, wo genau das passiert ist und der Anwender hanebüchene Theorien
darüber aufstellte, was denn am Compiler oder am µC kaputt wäre, weil
das Programm scheinbar "nichts tut".
In Produktionscode würde man ungenutzte Interrupts vermutlich am besten
mit einem RETI terminieren. Zur Entwicklungszeit ist es besser, dafür
eine Diagnosemöglichkeit vorzusehen. Sei es eine LED, die im Fehlerfall
eingeschaltet wird. Oder eine Fehlermeldung auf einem Display oder über
den UART ausgegeben. Was halt da ist und paßt.
Aber einfach nur zu sagen "das passiert mir nicht und deswegen brauche
ich mir auch keine Gedanken darüber zu machen, was passieren könnte" ist
einfach nur dämlich.
Ach, eins noch: deine Sätze sind zu lang. Dein Post ist sehr anstrengend
zu lesen.
Axel S. schrieb:> Ich hatte übrigens auch nicht> genau genug geschaut. Matthias hat ja gar keine Tabelle gezeigt. Nur> die Implementierung der Dummy-ISR.
Siehste!
Axel S. schrieb:>>> Ein ORG hat innerhalb der Tabelle nichts verloren. Da gehört nur>>> ein einziges ORG an den Anfang der Tabelle.>>>> Das ist nun wieder absoluter Blödsinn!>> Manieren!
Nein. Dogmen sind falsch!
Klar, ich verstehe deine Meinung. Es führt zu vielen
Fehlermöglichkeiten. Man sollte diese Form vermeiden. Aber "...nichts
verloren" ohne Erklärung ist Dogmatismus.
Axel S. schrieb:> Und es ist zwar vielleicht nicht> absolut zwingend, trotzdem aber höchst empfehlenswert, auch die> ungenutzten Vektoren auf etwas zeigen zu lassen, das etwas> wohldefiniertes tut. Im Zweifel einfach ein IRET.
Siehste...
Axel S. schrieb:> Da andererseits ein aktivierter Interrupt ohne die zugehörige ISR ein> klarer Fehler ist, ist es zumindest in der Entwicklungsphase der> Software besser, wenn man diesen Fehler klar diagnostizierbar macht.
Richtig - aber nicht zwingend notwendig.
Axel S. schrieb:> Du hast meinen Punkt nicht mal annähernd verstanden.
das heißt du kannst nicht eigenständig antworten und musst wie ein Idiot
glauben ich hätte "deinen Punkt" nicht verstanden.
Ich habe nicht deinen Punkt erwähnt, sondern deinen Text (praktisch das
was du 'programmiert' hast)
Falls du eigenständig meinen Text lesen könntest
>>Wahrscheinlich werden einige Programierstile bestimmte Fehler wahrscheinlicher
beeinflussen
und nicht von einem Abweichungsinterrupt zu deiner unsortierten Ausgabe
gezwungen wärst, dann könntest du dich vorher in einen
Konzentrationsmodus schalten und
>Zum Beispiel könnte ein Programmierer zwar einen Interrupt aktivieren,
den Namen des einen Interrupt nennen und die konkreten
unterschiedlichen Fehlerwirkungen vergleichen. Deine Massenausgabe an
unbestimmten Spekulationen ohne konkrete Inhalte dürfte deiner Teufel
Nutzung sehr ähnlich sein.
> Aber einfach nur zu sagen "das passiert mir nicht und deswegen brauche> ich mir auch keine Gedanken darüber zu machen, was passieren könnte" ist >
einfach nur dämlich.
Wenn du festen Speicher verwenden würdest, dann müsstet du nicht
dämlich Zitate einfach fühlen, sondern könntest eigenständig antworten
> Ach, eins noch: deine Sätze sind zu lang. Dein Post ist sehr anstrengend> zu lesen.
du bist sicherlich überfordert längere Sätze zu lesen. Versuch doch
einfach den Satz/die Sätze die dich belasten zu zitieren, damit der/die
Fehler den du meinst auch nachlesbar sind.
Programmierer sind Menschen und machen Fehler, deswegen sollten bspw. zu
lange Sätze offen beim Satz genannt werden, damit die fehlerhafte Länge
korrigiert werden kann.
Ein IRET allein ist aber nur die absolute Notbremse denn wenn das
zugehörige I-Flag nicht gelöscht wird dann haut der INT permanent wieder
rein und erzeugt ordentliche CPU Last.
Hi,
noch ein "Bockmist"-Beispiel.
Die .equ Anweisung ist zweifelsfrei per copy&paste vom def.inc-File
blindfischmäßig reingesetzt worden.
Aber, was mich damals etwas verwunderte, sogar das "Renamen" der
Interrupts wird akzeptiert.
Das entscheidendes Kriterium ist offensichtlich die korrekte vorgegebene
Reihenfolge.
(Und dementsprechend natürlich auch die Angabe der ISRs.)
Aus diesem Fallbeispiel hatte ich endgültig meine Lehren gezogen und
mache es jetzt wie oben schon gezeigt. Das ist zwar speicherfressend und
"redundant", aber für Korrekturen irgendwie bequemer. Steht das Programm
endgültig, kann es an die Feinstrukturierung gehen.
ciao
gustav
Hi
>Aus diesem Fallbeispiel hatte ich endgültig meine Lehren gezogen und>mache es jetzt wie oben schon gezeigt.
Das ist aber auch Blödsinn, denn der Flash des Controllers ist größer
als der mit rjmp erreichbare Speicherraum ist.
Bei mir geht das einfacher. Ich habe mir ein Delphi-Programm
geschrieben, das aus den XML-Dateien des 4er Studios Code generiert. Der
Anhang hat nur ein paar Sekunden gedauert.
MfG Spess
spess53 schrieb:> denn der Flash des Controllers ist größer> als der mit rjmp erreichbare Speicherraum ist.
Hi,
yep, klaro, aber:
"...JMP - Jump
Jump to an address within the entire 4M (words) program memory. See also
RJMP.
This instruction is not available in all devices. Refer to the device
specific instruction set summary...."
Targetspezifisch. Wurde oben - glaube ich - schon erwähnt.
Karl B. schrieb:> Nur beim Target-Wechsel muss man wieder anpassen.
Das "extended" Prinzip haben beide "Varianten" aber gemeinsam:
Auflisten der Int Adr und
Auflisten der ISRs in der richtigen Reihenfolge mit Abschluss (jede für
sich ein RETI spendiert. Und kein General RETI für alle.)
(Die .org's sind überflüssig (bis auf den Nuller vielleicht, das ist ja
kein "echter" Interruptvektor))
Gefällt mir. Mach ich demnächst so.
Die Sache mit der Sprungweite des rjmp-Befehls kann bei den "kleineren"
AVRs schon Probs. bereiten. Erst recht bei den Megas etc. Habe letztens
Routinen vom Ende näher in die Mitte des Progs. holen müssen, sonst
liefen die nicht.
ciao
gustav
Hi
>... und Auflisten der ISRs in der richtigen Reihenfolge mit Abschluss (jede >für
sich ein RETI spendiert.
???
Für die ISRs muss keinerle Reihenfolge eingehalten werde. Die kann man
hinschreiben wo man will. Bei AVRs mit <=4kWord Flash kann man mit rjmp
jede Stelle des Flashs erreichen. Und die AVRs mit >4kWord haben pro
IV-Tabelleneintrg Platz für jmp. Damit lassen sich 4 MWord adressieren.
Also verstehe ich deine Probleme nicht wirklich.
MfG Spess
Karl B. schrieb:> Auflisten der ISRs in der richtigen Reihenfolge mit Abschluss (jede für> sich ein RETI spendiert. Und kein General RETI für alle.)> (Die .org's sind überflüssig (bis auf den Nuller vielleicht, das ist ja> kein "echter" Interruptvektor)
Im Gegenteil sind die.orgs sehr nützlich, wenn man sie mit reti oder
rjmp kombiniert. Also:
.org int1addr
reti
.org int2addr
rjmp isr_int2
.org int3addr
reti
...
Dann passiert nämlich Folgendes: Das reti oder rjmp inkrementiert den
Adresscounter, das .org setzt den nächsten Befehl genau auf diese
Addresse.
Kopiert man ein Programm für eine anderen Typ, oder baut man ein rjmp
ein ohne das reti zu löschen, oder verwechselt man Interruptaddresse,
dann meckert der Compiler, weil sich Addressen überschneiden. Löscht man
aus Versehen ein reti, ohne ein rjmp einzufügen, stimmen nachfolgende
Addressen trotzdem erstmal.
Das ist sehr sicher.
Andererseits, ich hab in 15+ Jahren Assembler auf dem Avr schon viel
Mist gebaut. Auch Interrupts aktiviert und dann andere genutzt, zum
Beispiel beim Txd Interrupt, UDR empty oder Data send. Aber rjmp ohne
zugehörige Routine geht gar nicht, weil dann der Compiler wegen
fehlendem Label meckert.
Karl schrieb:> Du hast da was Grundlegendes übersehen. Die Adressen der> Interrupteinsprünge sind sozusagen im Controller hartcodiert. Deine> Labels interessieren den Controller einen Dreck. Die stehen in dem Fall> alle an der gleichen Stelle, und zwar ganz vorn.
Es ist doch klar, das die Vektortabelle drin bleibt. Es geht nur um die
Labels, die aus dieser angesprungen werden. Ich hatte die Vektortabelle
in meinem Codefetzen nur nicht nochmal reingeschrieben und dachte, das
wäre durch die Benutzung deiner Labelnamen klar geworden. Und diese
Labels können alle auf das gleiche 'reti' zeigen - oder man behandelt
hier auf Wunsch den 'Bad Interrupt'.
Matthias S. schrieb:> Es ist doch klar, das die Vektortabelle drin bleibt. Es geht nur um die> Labels, die aus dieser angesprungen werden. Ich hatte die Vektortabelle> in meinem Codefetzen nur nicht nochmal reingeschrieben.
Ich hatte das sofort verstanden. Aber Du musst immer in Erwägung ziehen,
dass es hier Leute gibt, die Deine Beiträge nur flüchtig lesen -
entweder, weil sie generell immer nur querlesen oder weil sie einfach
nicht genügend Zeit zum Lesen (und Verstehen) aufbringen wollen.
Dann kommt so etwas raus:
Route 6. schrieb:> Der Codeauszug von Matthias S. ist vollkommener Blödsinn.Karl schrieb:> Du hast da was Grundlegendes übersehen. Die Adressen der> Interrupteinsprünge sind sozusagen im Controller hartcodiert.
Ich hatte das aber hier schon klargestellt:
Frank M. schrieb:> Du hast den Codeauszug von Matthias S. einfach fehlinterpretiert. Das> soll keine Interrupt-Tabelle sein, sondern der Code, der aus der> Interrupt-Tabelle per RJMP angesprungen wird.
Fazit:
Schreib's beim nächsten Mal etwas vollständiger, spart eine Menge
unnötiger Antworten ;-)
Naja, irgendwie ist die Idee so auch eher sinnlos, finde ich.
Matthias S. schrieb:> Du kannst dir die ganzen 'reti' sparen, es reicht ein einziges:
Dafür braucht man dann eine Reihe rjmp, die alle zu dem gemeinsamen reti
springen. Gespart hat man damit also eigentlich nichts.
Karl schrieb:> Im Gegenteil sind die.orgs sehr nützlich, wenn man sie mit reti oder> rjmp kombiniert. Also:>> .org int1addr> reti
Hi,
also doch wieder so, wie ich es im Beispiel am Anfang gemacht habe.
https://www.mikrocontroller.net/attachment/357533/Interruptvektoren_.asm
Zusatz laut Empfehlung von @spess53:
Wenn es nicht hinhaut, dann eben mit dem jmp-Befehl -> targetspezifisch.
spess53 schrieb:> Für die ISRs muss keinerle Reihenfolge eingehalten werde. Die kann man> hinschreiben, wo man will.
Die Reihenfolge bei mehreren ISRs "sollte" der Übersichtlichkeit halber
der vorher oben angegebenen Int Vektoren- (jetzt mache ich nicht den
Fehler und sage "Tabelle") Anordnung entsprechen.
Das war etwas unsauber formuliert von mir.
Sicherlich gibt es Situationen in der Strukturierung des Listings,
wo es sinnvoller ist, die funktionell zusammengehörigen Befehle
hintereinander zu schreiben (mit Kommentar). Jetzt sklavisch dem einmal
in Stein gemeißelten Schema zu gehorchen, ist natürlich wahlfrei.
Die Diskussion ist jetzt aber in eine andere Richtung abgedriftet:
Die Frage des TO war, wieso man im Hex-File nicht den OP-Code 1:1
wiederfindet.
Stichwort "Big Endian" etc.
Janik schrieb:> Ok, danke, ich les mich mal rein ^^ Und melde mich wieder ;)spess53 schrieb:> Also verstehe ich deine Probleme nicht wirklich.
Der Stein des Anstoßes war die ominöse Angabe von
.org 0x13 ganz oben
Diese Zeile wurde von mir als Angabe eines Interrups angesehen, den es
laut Kommentar hier auch garnicht gibt. Bis jetzt habe ich die Bedeutung
dieser Angabe immer noch nicht verstanden. Wäre für Aufklärung sehr
dankbar.
ciao
gustav
Rolf M. schrieb:> Naja, irgendwie ist die Idee so auch eher sinnlos, finde ich.>> Matthias S. schrieb:>> Du kannst dir die ganzen 'reti' sparen, es reicht ein einziges:>> Dafür braucht man dann eine Reihe rjmp, die alle zu dem gemeinsamen reti> springen. Gespart hat man damit also eigentlich nichts.
Sparen ist hier auch fehl am Platz. Die Vektortabelle ist ohnehin da, an
der kann man nichts einsparen [1]. Und ob da nun ein RJMP oder RETI an
der entsprechenden Stelle steht, macht für die Codegröße keinen
Unterschied.
Die einzige Stelle, wo man ein bißchen sparen kann, ist wenn man alle
ungenutzten Interrupts auf das gleiche RETI umleitet. Das macht zwar das
Kraut nicht fett, der Vorteil wird aber sofort sichtbar, wenn man statt
RETI eine echte BAD-ISR Behandlung machen will.
Direkt RETI in die Vektortabelle zu schreiben, halte ich für unschön.
Tatsächlich hadere ich schon mit der Entscheidung von AVR, die Tabelle
überhaupt mit Code zu füllen, statt mit reinen Adressen. Aber gut, das
ist jetzt im Silizium und muß so hingenommen werden.
Was aus meiner Sicht für JMP/RJMP spricht, ist daß AVR das so vorgesehen
hat und die Länge der einzelnen Einträge genau darauf abgestimmt ist. Es
ist nur ein glücklicher Zufall, daß der Opcode für RETI genauso lang ist
wie der für den Sprung.
[1] Im Prinzip kann man schon etwas sparen. Wenn man z.B. keinen
einzigen Interrupt benutzt, kann man das Programm direkt an 0x0000
beginnen lassen. Oder wenn man nur ein paar Interrupts von Anfang der
Tabelle braucht, kann man den Rest der Tabelle für Code nutzen. Ich
halte das aber für üblen Pfusch.
Axel S. schrieb:> Sparen ist hier auch fehl am Platz. Die Vektortabelle ist ohnehin da, an> der kann man nichts einsparen
Doch, natürlich. Grundsätzlich kann man alle ungenutzten Vektoren
natürlich als ganz normalen Flashspace verwenden. Wenn man für ein
konkretes Projekt gerade knapp beim Flash ist, ist das manchmal
vielleicht ganz allgemein nützlich, weil es den Wechsel zum
nächstgrößeren Controller spart (inbesondere, wenn es keinen
nächstgrößeren Verwandten gibt).
Attraktiver wird die Sache aber noch, wenn man Anwendungen mit extrem
häufig aufgerufenen ISRs hat. Dann ermöglicht das nämlich u.U. eine "in
place"-ISR. Spart mindestens zwei Takte pro Aufruf durch den
eingesparten rjmp.
Wenn der Nutzcode dieser ISR z.B. nur 8 Takte braucht, dann ist die
Ersparnis durch so eine "in place"-ISR enorm, satte 20%. Das hat
durchaus das Potential, der Diskriminator zwischen "geht" und "geht
nicht" für eine Anwendung zu sein...
> Direkt RETI in die Vektortabelle zu schreiben, halte ich für unschön.
Warum?
> Tatsächlich hadere ich schon mit der Entscheidung von AVR, die Tabelle> überhaupt mit Code zu füllen, statt mit reinen Adressen.
Weil du das Potential nicht wirklich begreifst. Einem Asm-Programmierer
(also einem richtigen, nicht einem Anfänger wie dem TO) ist es hingegen
sofort klar.
> [1] Im Prinzip kann man schon etwas sparen. Wenn man z.B. keinen> einzigen Interrupt benutzt, kann man das Programm direkt an 0x0000> beginnen lassen. Oder wenn man nur ein paar Interrupts von Anfang der> Tabelle braucht, kann man den Rest der Tabelle für Code nutzen. Ich> halte das aber für üblen Pfusch.
Weil du halt nur in den engen Kategorien deiner Fähigkeiten und der
durch Hochsprachenkonventionen geprägten Denkmuster denken kannst. Du
hast deinen Intellekt freiwillig selbst kastriert. Das einzige, was dich
trösten kann: das hast du mit schätzungsweise 99,8 aller (noch) aktiven
Programmierer gemeinsam..
Die anderen, die noch selber wussten was sie tun und keine
Compiler-Nanny benötigten, sind überwiegend im Ruhestand oder sogar
schon tot...
Frank M. schrieb:> Ich hatte das sofort verstanden. Aber Du musst immer in Erwägung ziehen,
Ich würde eher in Erwägung ziehen, dass ein derart mißverständlicher
Codeschnipsel - denn im Begleittext steht noch sowas wie "braucht man
nicht" - unter Garantie wieder im Forum aufschlägt: Hilfe! Ich hab das
genauso gemacht wies da steht, aber mein Interrupt geht nicht.
Axel S. schrieb:> Direkt RETI in die Vektortabelle zu schreiben, halte ich für unschön.
Tja, das sieht der Hersteller der Avrs anders, und schlägt genau das
vor. Aber das ist ja nur der Hersteller, was weiss der schon...
Hallo!
Man muss den Hex Code in Bin code umschreiben dann findet man in den
Opcodes in der Liste für die AVR's.
das wird hier gut erklärt.
(http://www.rowalt.de/mc/index.htm - AVR Buch Vorschau
Buch 2.Ausgabe kann online gelesen werden Seite 12 und 13 wird das
Problem sehr gut erklärt