Assembler-Listing generieren | Assembler-Listing generieren

Die Arduino-IDE und auch die Visual-Studio-Erweiterung Visual Micro bieten die Möglichkeit, den Build-Prozess anzupassen. Dieser wird i.W. durch die Einträge in der Steuer-Datei "platform.txt" des jeweiligen Prozessor-Typs geregelt. Details zum Build-Prozess findet man unter Build Process und die Dokumentation der Inhalte der Steuerdateien unter Arduino IDE 1.5 3rd party Hardware specification.

Leider liefert der Standard-Prozess kein Assembler-Listing. Dies kann man jedoch recht einfach ergänzen.

Die GNU Compiler Collection enthält das Programm objdump (meist mit einem Präfix, z.B. avr-objdump). Mit diesem Programm lässt sich aus der erstellten .elf-Datei (Executable and Linking Format) ein Assembler-Listing generieren. Ein entsprechender Aufruf wäre z.B.:

XXX-objdump -D -S -h program.elf" > program.lss

Die Optionen "-D -S- h" sorgen für ein lesbares Listing. Ob man noch "--demangle" hinzufügt, das aus den C++-Signaturen die originalen Methodennamen generiert, ist Geschmackssache.

Über Einträge in der "platform.txt" lassen sich an vielen Build-Prozess-Schritten zusätzliche Kommandos ausführen (s. Arduino IDE 1.5 3rd party Hardware specification). Die .elf-Datei steht nach dem Linken zur Verfügung. Über die Eintrag "recipe.hooks.linking.postlink.postlink.X.pattern" können Kommandos zur Ausführung nach dem Linken eingefügt werden. Die Parameter {compiler.path}, {build.path} und {build.project_name} erlauben den Aufbau passender Datei-Pfade. Theoretisch könnte man das Kommando zum Erzeugen des Assembler-Listings direkt in die "platform.txt" eintragen. Bedauerlicherweise klappt dabei die Umleitung des Outputs in eine Datei nicht. Deshalb muss ein kleines zwischengeschaltetes Batch-Programm helfen (Generate-lss-File.cmd).

@ECHO OFF
%1 -D -S -h %2 > %3

%1: Vollständiger Pfad zu XXX-objdump ("{compiler.path}XXX-objdump.exe").
%2: Vollständiger Pfad zur .elf-Datei ("{build.path}/{build.project_name}.elf").
%3: Vollständiger Pfad zur Ausgabe-Datei ("{build.path}/{build.project_name}.lss").

"platform.txt" benötigt dann diesen Eintrag (eine Zeile!):

recipe.hooks.linking.postlink.postlink.1.pattern=Generate-lss-File.cmd 
     "{compiler.path}XXX-objdump.exe" "{build.path}/{build.project_name}.elf"  "{build.path}/{build.project_name}.lss"

Wenn "Generate-lss-File.cmd" nicht über die PATH-Angabe gefunden werden kann, muss der vollständige Pfad angegeben werden.

Wenn man mit Visual-Studio (respektive Visual Micro) arbeitet, kann die erzeugte .lss-Datei als "vorhandenes Element" in das Projekt mit einbinden. Die Datei steht dann über den Projekt-Explorer per Klick zur Verfügung.

Eine Variante für den ESP8266 ist in ESP8266 FAQ beschrieben.


Arduino-IDE und Prototypen | Arduino-IDE und Prototypen

Wer mit der Arduino-IDE programmiert, erhält manchmal merkwürdige Fehlermeldungen. Wenn man dann in die angegebene Zeile schaut, scheint alles richtig zu sein. Manchmal können von der IDE selbständig eingefügte Prototypen in die ino-Datei die Ursache sein. Die Probleme treten in jeder ino-Datei auf, nicht nur bei der Hauptdatei (mit setup und loop).

Das folgende gilt sowohl für die Arduino-IDE als auch in ähnlicher Weise für die Visual Studio Extension Visual Micro.

Schauen wir uns einmal das folgende kleine Programm an:

01: void setup() { 
02:   foo();
03: }
04: 
05: void loop() { }
06: 
07: void foo(){ }

Wenn man dieses Programm außerhalb der IDE übersetzen würde, würde der Compiler in Zeile 2 einen Fehler melden. Funktion foo ist an dieser Stelle unbekannt. Da der Arduino auch für Anfänger geeignet sein soll, hilft die IDE hier ein wenig aus. Sie fügt selbständig passende Prototypen ein. Die IDE generiert aus der ino-Datei eine cpp-Datei, die in etwa so aussieht:

#include <arduino.h>
#line 1 "C:\\Users\\Ulli\\Documents\\Arduino\\ArduinoPrototypes\\ArduinoPrototypes.ino"

void foo();

#line 1 "C:\\Users\\Ulli\\Documents\\Arduino\\ArduinoPrototypes\\ArduinoPrototypes.ino"
void setup() { 
  foo();
}

void loop() { }

void foo(){ }

Als erstes wird ein include-Statement für "ardoino.h" eingefügt. Etwas darunter wird ein Prototyp für foo eingeschoben. Die Anweisungen, die mit #line beginnen, sollen dafür sorgen, dass Fehlermeldungen des Compiler den Dateinamen und die Zeilennummern der ursprünglichen ino-Datei erhalten.

Das hinzufügen der Prototypen erfolgt nur in der ino-Datei. Normale cpp-Dateien bleiben unangetastet. Fügt man eine cpp-Datei (ExternFuncs.cpp) folgenden Inhalts in das Projekt ein:

01: void dummy() {
02:   bar();
03: }
04: 
05: void bar() {}

meckert der Compiler:

ExternFuncs.cpp: In function void dummy()

Error compiling project sources
ExternFuncs.cpp: 2:7: error: 'bar' was not declared in this scope
Build failed for project 'ArduinoPrototypes'

Eine andere Konfiguration, die mächtig verwirrend scheinen kann:

class Foo{};
void bar(Foo f) {}

ergibt die Fehlermeldung:

error: 'Foo' was not declared in this scope

Schaut man sich die generierte cpp-Datei an, die dem Compiler vorgelegt wird, wird alles klar:

void bar(Foo f);

#line 1 "C:\\Users\\Ulli\\Documents\\Arduino\\ArduinoPrototypes\\ArduinoPrototypes.ino"
class Foo {};
void bar(Foo f) {}

Die Fehlermeldung bezieht sich auf eingefügten den Prototyp für bar. Je nach dem, was vor oder nach dem Code steht, ist die Zeilenangabe falsch.

Auch gilt wieder: Es wird nur die ino-Datei analysiert und modifiziert. Schreibt den obigen Code in eine Header-Datei und bindet diese in die ino-Datei ein, wird das Programm einwandfrei kompiliert. Auch in einer cpp-Datei wird kein Fehler gemeldet.

In der ino-Datei müssen entsprechende Prototypen eingefügt werden. So geht's:

class Foo;
void bar(Foo f);

class Foo {};
void bar(Foo f) {}

Noch ein wenig undurchsichtiger wird es, wenn man mit Templates arbeitet:

template <typename T> T foo(T a, T b) { return a + b; }

führt zu

error: 'T' does not name a type

mit falscher Zeilenangabe.

Schaut man wieder auf die generierte cpp-Datei, findet man:

T foo(T a, T b);

template <typename T> T foo(T a, T b) { return a + b; }

Kein Wunder, dass das nicht funktioniert.

Auch hier ist es am einfachsten, die Funktionsdefinition über eine Header-Datei einzubinden.

Alternativ kann man einen Funktionsprototyp explizit angeben:

template <typename T> T foo(T a, T b);
template <typename T> T foo(T a, T b) { return a + b; }