Hallo,
dies soll der neue Update-Log Thread werden, da ich das User Interface
rausgeworfen habe und es somit nicht mehr viel mit einem Debugger zu tun
hat.
Übrig geblieben ist nun die Engine für Simulation und Analyse von AVR
Binaries. Und um die soll es nun hier gehen. Das ganze habe ich libvmcu
(Library for Virtual Microcontroller) genannt.
Features:
--------------
- statische Analyse (SFRs, ISRs, mögliche Labels, Cycle Analyse, ...)
- Echtzeit Simulation inklusive Peripherie und Backstepping
- Java Binding
Usecases:
--------------
libvmcu kann verwendet werden um
- Regression Tests für AVR Projekte zu erstellen
- eigene Tools (Simulatoren oder kleine Utilities) zu erstellen
- detaillierte Informationen aus dem Binary zu erhalten
Beispiel:
--------------
Dieses Beispiel stellt einen möglichen Regressiontest für ein ATmega328P
Programm dar.
Das zu testende Programm: Eine angeschlossene LED an PB5. Die LED soll
im Takt von einer Sekunde getoggled werden. Dazu wird der Timer0 im
Normal Mode verwendet und nach einer gewissen Anzahl an Overflows wird
die LED getoggled. Der Timer wurde mit berechneten Werten konfiguriert,
somit ist zu erwarten, dass dieser auch im richtigen Takt toggled.
Dies soll nun dennoch verifiziert werden, dabei ist eine Abweichung von
maximal +-0.05 Sekunden erlaubt. Der Test ist also erfolgreich, wenn die
Zeit zwischen dem LED toggling zwischen 0.95 und 1.05 Sekunden liegt.
1 | #define TESTFILE "../../driver/led/led.hex"
| 2 |
| 3 | #define PORTB 0x0025
| 4 | #define PB5 0x05
| 5 |
| 6 | #define bit(v, b) ((v & (1 << b)) >> b)
| 7 |
| 8 | int main(const int argc, const char **argv) {
| 9 |
| 10 | uint8_t led;
| 11 |
| 12 | /* ignoring checks for this example */
| 13 | vmcu_report_t *report = vmcu_analyze_ihex(TESTFILE);
| 14 | vmcu_system_t *sys = vmcu_system_ctor(report);
| 15 |
| 16 | do {
| 17 |
| 18 | vmcu_system_step(sys);
| 19 | led = vmcu_system_read_data(sys, PORTB);
| 20 |
| 21 | } while(bit(led, PB5) == 0x00);
| 22 |
| 23 | const double f = 16000000U;
| 24 | const double c = sys->cycles;
| 25 | const double time = (c / f);
| 26 |
| 27 | assert((0.95 <= time) && (time <= 1.05));
| 28 | printf("Time between LED toggle: %lf [s]\n", time);
| 29 |
| 30 | vmcu_report_dtor(report);
| 31 | vmcu_system_dtor(sys);
| 32 |
| 33 | return EXIT_SUCCESS;
| 34 | }
|
1 | Test Resultat: Time between LED toggle: 1.000021 [s]
|
Dies ist natürlich nur ein vereinfachtes Beispiel und soll lediglich das
Prinzip dahinter verdeutlichen.
Link: https://github.com/Milo-D/libvmcu-Virtual-MCU-Library
Update (v.0.7.3)
-> libvmcu bietet nun xrefs an (cross references). Der Analyzer kann nun
Referenzen setzten, sodass man diese dann später einsehen kann um
beispielsweise herauszufinden, welche Instruktion ein potentielles Label
referenziert.
-> VCD Trace Tool (driver) wurde hinzugefügt. Dieser driver wird wie
folgt aufgerufen: ./vcdump <file.hex> <cycles> <dataspace address>
Dabei wird die Datei <file.hex> solange simuliert bis <cycles> erreicht
wird. Währenddessen wird der Wert der <dataspace address> aufgezeichnet
(es können mehrere Addressen angegeben werden) und als .vcd exportiert.
Diese kann dann mit bspw. gtkwave interpretiert werden (siehe Screenshot
im Anhang).
-> ein kleiner Bugfix, welches die 'or' Instruktion betrifft.
xref Beispiel:
----------------
1 | int main(void) {
| 2 |
| 3 | /* ignoring checks for this example */
| 4 | vmcu_report_t *report = vmcu_analyze_ihex("file.hex");
| 5 |
| 6 | for(int32_t i = 0; i < report->nlabels; i++) {
| 7 |
| 8 | vmcu_label_t *lx = &report->labels[i];
| 9 | printf("0x%04x\tL%d\n\n", lx->addr, lx->id);
| 10 |
| 11 | for(int32_t j = 0; j < lx->nxrefs; j++) {
| 12 |
| 13 | vmcu_xref_t *x = &lx->xrefs[j];
| 14 |
| 15 | printf(" xref from 0x%04x ", x->p->addr);
| 16 | printf("(%s)\n", x->p->mnem);
| 17 | }
| 18 |
| 19 | printf("\n");
| 20 | }
| 21 |
| 22 | vmcu_report_dtor(report);
| 23 | return EXIT_SUCCESS;
| 24 | }
|
1 | Output:
| 2 |
| 3 | 0x0000 L0
| 4 |
| 5 | xref from 0x0051 (jmp +0 ; PC <- 0x0)
| 6 |
| 7 | 0x0034 L1
| 8 |
| 9 | xref from 0x0000 (jmp +52 ; PC <- 0x34)
| 10 |
| 11 | 0x0040 L2
| 12 |
| 13 | xref from 0x0044 (brne -5 ; (Z = 0): PC <- PC - 0x5 + 1)
| 14 |
| 15 | 0x0042 L3
| 16 |
| 17 | xref from 0x003f (rjmp +2 ; PC <- PC + 0x2 + 1)
| 18 |
| 19 | 0x0049 L4
| 20 |
| 21 | xref from 0x004c (brne -4 ; (Z = 0): PC <- PC - 0x4 + 1)
| 22 |
| 23 | 0x004a L5
| 24 |
| 25 | xref from 0x0048 (rjmp +1 ; PC <- PC + 0x1 + 1)
| 26 |
| 27 | 0x0051 L6
| 28 |
| 29 | xref from 0x0002 (jmp +81 ; PC <- 0x51)
| 30 | xref from 0x0004 (jmp +81 ; PC <- 0x51)
| 31 | xref from 0x0006 (jmp +81 ; PC <- 0x51)
| 32 | xref from 0x0008 (jmp +81 ; PC <- 0x51)
| 33 | xref from 0x000a (jmp +81 ; PC <- 0x51)
| 34 | xref from 0x000c (jmp +81 ; PC <- 0x51)
| 35 | xref from 0x000e (jmp +81 ; PC <- 0x51)
| 36 | xref from 0x0010 (jmp +81 ; PC <- 0x51)
| 37 | xref from 0x0012 (jmp +81 ; PC <- 0x51)
| 38 | xref from 0x0014 (jmp +81 ; PC <- 0x51)
| 39 | xref from 0x0016 (jmp +81 ; PC <- 0x51)
| 40 | xref from 0x0018 (jmp +81 ; PC <- 0x51)
| 41 | xref from 0x001a (jmp +81 ; PC <- 0x51)
| 42 | xref from 0x001c (jmp +81 ; PC <- 0x51)
| 43 | xref from 0x001e (jmp +81 ; PC <- 0x51)
| 44 | xref from 0x0020 (jmp +81 ; PC <- 0x51)
| 45 | xref from 0x0022 (jmp +81 ; PC <- 0x51)
| 46 | xref from 0x0024 (jmp +81 ; PC <- 0x51)
| 47 | xref from 0x0026 (jmp +81 ; PC <- 0x51)
| 48 | xref from 0x0028 (jmp +81 ; PC <- 0x51)
| 49 | xref from 0x002a (jmp +81 ; PC <- 0x51)
| 50 | xref from 0x002e (jmp +81 ; PC <- 0x51)
| 51 | xref from 0x0030 (jmp +81 ; PC <- 0x51)
| 52 | xref from 0x0032 (jmp +81 ; PC <- 0x51)
|
Ich suche zurzeit noch nach Leuten, die gerne was zum Projekt beitragen
würden.
Bei folgenden Teilen des Projekts bräuchte ich noch Unterstützung:
Bindings
------------------
Ein Python Binding ist zurzeit in Planung. Ein Python Binding würde es
ermöglichen, sehr schnell kleine Skripte zu schreiben, die mit der AVR
binary interagieren.
Das Binding müsste dann auch maintained werden, da sich die libvmcu
library in C auch verändern kann.
Prinzipiell bildet ein Binding ein Subsystem der Repository für welches
man dann selbst verantwortlich wäre.
Sollte mal das Binding eine längere Zeit nicht gebaut werden können, ist
dies zwar Schade aber kein großes Problem. Das Binding wird dann einfach
archiviert bis sich wieder einer bereit erklärt die Arbeit am Binding
wieder aufzunehmen.
Bindings für andere Sprachen sind zwar nicht geplant, jedoch hätte ich
nichts dagegen, solange diese auch gepflegt werden. Zurzeit wird das
Java Binding von pointbazaar maintained.
Bindings generell sind, dadurch dass sich das Library Interface
verändern kann und man somit das Binding adjustieren muss, ein etwas
höherer Aufwand.
Driver
--------------
Driver dagegen sind weniger aufwendig. Wenn man nur wenig Zeit hat aber
dennoch etwas beitragen möchte, sind Driver genau richtig.
Ein Driver ist eine Art Hilfsprogramm (utility) oder Beispielprogramm,
das gewisse Funktionen erfüllt indem es die libvmcu library benutzt.
Ein Beispiel für einen Driver wäre der "findisr" driver, welcher
mithilfe von libvmcu die ISRs in einer AVR binary ausfindig macht.
Bei den Drivers gibt es keine genauen Vorgaben. Man kann schreiben wozu
man Lust hat, solange es Funktionen aus der libvmcu verwendet und
kombiniert um neue Funktionen zu kreieren. Dabei müssen die Funktionen
keineswegs kompliziert sein.
Die Driver werden von mir im Nachhinein noch maintained, sodass sie auch
Veränderungen an der Library überstehen und nicht kaputt gehen.
Tests
------------
Weitere Tests werden ebenfalls benötigt. Generell werden so viele Tests
benötigt, dass ich alleine da nicht mehr hinterher kommen werde.
Würde mich freuen falls jemand Interesse daran hätte.
Update (v.0.7.4)
-> Die vmcu_sfr_t Struktur wurde dem Analyzer Report hinzugefügt. Diese
beinhaltet eine SFR ID und eine Liste an cross references (xrefs). Die
ID gibt an um welches konkrete SFR es sich handelt und die xrefs sind
eine Referenz (bzw. Zeiger) auf die Stelle im Disassembly an dem dieses
SFR angesprochen wird.
-> Es existiert nun ein neues enum (SFREGISTER). Die bereits oben
erwähnte SFR ID ist von genau diesem Typ. Dies ermöglicht es sehr
einfach SFRs zu filtern. Ein Beispiel wäre VMCU_EECR oder VMCU_RESERVED.
-> Weitere Verbesserungen am SFR Analyzer.
-> Kleinere Änderungen an der Namenskonvention von vmcu_report_t
membern. Listen erhalten den Namen der Structure im Singular. Die Listen
Counter erhalten den Namen der Structure im Singular inklusive des
Prefix n_
-> Bugfix im occurence driver
-> COM(A/B)x bits überschreiben nun auch die normale Portfunktion
xref (SFR) Beispiel
-----------------------
1 | #define FILE "avr_filesystem/avr_filesystem.hex"
| 2 |
| 3 | int main(const int argc, const char **argv) {
| 4 |
| 5 | /* ignoring checks for this example */
| 6 | vmcu_report_t *report = vmcu_analyze_ihex(FILE);
| 7 |
| 8 | for(int32_t i = 0; i < report->n_sfr; i++) {
| 9 |
| 10 | vmcu_sfr_t *sfr = &report->sfr[i];
| 11 | printf("SFR ID: %d\n", sfr->id);
| 12 |
| 13 | for(int32_t j = 0; j < sfr->n_xref; j++) {
| 14 |
| 15 | vmcu_xref_t *x = &sfr->xref[j];
| 16 |
| 17 | printf(" xref from 0x%04x ", x->p->addr);
| 18 | printf("(%s)\n", x->p->mnem);
| 19 | }
| 20 |
| 21 | printf("\n");
| 22 | }
| 23 |
| 24 | vmcu_report_dtor(report);
| 25 | return EXIT_SUCCESS;
| 26 | }
|
1 | Output:
| 2 |
| 3 | SFR ID: 0
| 4 | xref from 0x0112 (out 0x01, r16 ; IO[addr] <- R16)
| 5 | xref from 0x011c (out 0x00, r16 ; IO[addr] <- R16)
| 6 |
| 7 | SFR ID: 33
| 8 | xref from 0x011f (in r16, 0x31 ; R16 <- IO[addr])
| 9 |
| 10 | SFR ID: 38
| 11 | xref from 0x0152 (out 0x3d, r28 ; IO[addr] <- R28)
| 12 | xref from 0x016f (in r28, 0x3d ; R28 <- IO[addr])
| 13 | xref from 0x018d (in r28, 0x3d ; R28 <- IO[addr])
| 14 | xref from 0x01a0 (in r28, 0x3d ; R28 <- IO[addr])
| 15 | xref from 0x01c5 (in r28, 0x3d ; R28 <- IO[addr])
| 16 | xref from 0x01df (in r28, 0x3d ; R28 <- IO[addr])
| 17 | xref from 0x01fc (in r28, 0x3d ; R28 <- IO[addr])
| 18 | xref from 0x020c (in r28, 0x3d ; R28 <- IO[addr])
| 19 | xref from 0x021c (in r28, 0x3d ; R28 <- IO[addr])
| 20 | xref from 0x0223 (out 0x3d, r28 ; IO[addr] <- R28)
| 21 | xref from 0x0264 (out 0x3d, r28 ; IO[addr] <- R28)
| 22 | xref from 0x026d (in r28, 0x3d ; R28 <- IO[addr])
| 23 | xref from 0x0296 (in r28, 0x3d ; R28 <- IO[addr])
| 24 | xref from 0x029e (out 0x3d, r28 ; IO[addr] <- R28)
| 25 | xref from 0x02e1 (out 0x3d, r28 ; IO[addr] <- R28)
| 26 | xref from 0x02e8 (in r28, 0x3d ; R28 <- IO[addr])
| 27 | xref from 0x0357 (in r28, 0x3d ; R28 <- IO[addr])
| 28 | xref from 0x03f4 (out 0x3d, r28 ; IO[addr] <- R28)
| 29 | xref from 0x03fd (in r28, 0x3d ; R28 <- IO[addr])
| 30 | xref from 0x04c2 (out 0x3d, r28 ; IO[addr] <- R28)
| 31 | xref from 0x04d0 (in r28, 0x3d ; R28 <- IO[addr])
| 32 | xref from 0x04d8 (out 0x3d, r28 ; IO[addr] <- R28)
| 33 | xref from 0x0a35 (out 0x3d, r28 ; IO[addr] <- R28)
| 34 | xref from 0x0a44 (in r28, 0x3d ; R28 <- IO[addr])
| 35 | xref from 0x0b65 (in r28, 0x3d ; R28 <- IO[addr])
| 36 | xref from 0x0b96 (in r28, 0x3d ; R28 <- IO[addr])
| 37 | xref from 0x0b9d (out 0x3d, r28 ; IO[addr] <- R28)
| 38 | xref from 0x0c34 (out 0x3d, r28 ; IO[addr] <- R28)
| 39 | xref from 0x0c3a (in r28, 0x3d ; R28 <- IO[addr])
| 40 | xref from 0x0c41 (out 0x3d, r28 ; IO[addr] <- R28)
| 41 | xref from 0x0ca1 (out 0x3d, r28 ; IO[addr] <- R28)
| 42 | xref from 0x0caf (in r28, 0x3d ; R28 <- IO[addr])
| 43 | xref from 0x0cb6 (out 0x3d, r28 ; IO[addr] <- R28)
| 44 | xref from 0x0d24 (out 0x3d, r28 ; IO[addr] <- R28)
| 45 | xref from 0x0d33 (in r28, 0x3d ; R28 <- IO[addr])
| 46 | xref from 0x0d8e (in r28, 0x3d ; R28 <- IO[addr])
| 47 | xref from 0x0d95 (out 0x3d, r28 ; IO[addr] <- R28)
| 48 | xref from 0x12a4 (in r28, 0x3d ; R28 <- IO[addr])
| 49 | xref from 0x12ac (out 0x3d, r28 ; IO[addr] <- R28)
| 50 | xref from 0x12c6 (out 0x3d, r28 ; IO[addr] <- R28)
| 51 |
| 52 | SFR ID: 39
| 53 | xref from 0x0151 (out 0x3e, r29 ; IO[addr] <- R29)
| 54 | xref from 0x0170 (in r29, 0x3e ; R29 <- IO[addr])
| 55 | xref from 0x018e (in r29, 0x3e ; R29 <- IO[addr])
| 56 | xref from 0x01a1 (in r29, 0x3e ; R29 <- IO[addr])
| 57 | xref from 0x01c6 (in r29, 0x3e ; R29 <- IO[addr])
| 58 | xref from 0x01e0 (in r29, 0x3e ; R29 <- IO[addr])
| 59 | xref from 0x01fd (in r29, 0x3e ; R29 <- IO[addr])
| 60 | xref from 0x020d (in r29, 0x3e ; R29 <- IO[addr])
| 61 | xref from 0x021d (in r29, 0x3e ; R29 <- IO[addr])
| 62 | xref from 0x0221 (out 0x3e, r29 ; IO[addr] <- R29)
| 63 | xref from 0x0262 (out 0x3e, r29 ; IO[addr] <- R29)
| 64 | xref from 0x026e (in r29, 0x3e ; R29 <- IO[addr])
| 65 | xref from 0x0297 (in r29, 0x3e ; R29 <- IO[addr])
| 66 | xref from 0x029c (out 0x3e, r29 ; IO[addr] <- R29)
| 67 | xref from 0x02df (out 0x3e, r29 ; IO[addr] <- R29)
| 68 | xref from 0x02e9 (in r29, 0x3e ; R29 <- IO[addr])
| 69 | xref from 0x0358 (in r29, 0x3e ; R29 <- IO[addr])
| 70 | xref from 0x03f2 (out 0x3e, r29 ; IO[addr] <- R29)
| 71 | xref from 0x03fe (in r29, 0x3e ; R29 <- IO[addr])
| 72 | xref from 0x04c0 (out 0x3e, r29 ; IO[addr] <- R29)
| 73 | xref from 0x04d1 (in r29, 0x3e ; R29 <- IO[addr])
| 74 | xref from 0x04d6 (out 0x3e, r29 ; IO[addr] <- R29)
| 75 | xref from 0x0a33 (out 0x3e, r29 ; IO[addr] <- R29)
| 76 | xref from 0x0a45 (in r29, 0x3e ; R29 <- IO[addr])
| 77 | xref from 0x0b66 (in r29, 0x3e ; R29 <- IO[addr])
| 78 | xref from 0x0b97 (in r29, 0x3e ; R29 <- IO[addr])
| 79 | xref from 0x0b9b (out 0x3e, r29 ; IO[addr] <- R29)
| 80 | xref from 0x0c32 (out 0x3e, r29 ; IO[addr] <- R29)
| 81 | xref from 0x0c3b (in r29, 0x3e ; R29 <- IO[addr])
| 82 | xref from 0x0c3f (out 0x3e, r29 ; IO[addr] <- R29)
| 83 | xref from 0x0c9f (out 0x3e, r29 ; IO[addr] <- R29)
| 84 | xref from 0x0cb0 (in r29, 0x3e ; R29 <- IO[addr])
| 85 | xref from 0x0cb4 (out 0x3e, r29 ; IO[addr] <- R29)
| 86 | xref from 0x0d22 (out 0x3e, r29 ; IO[addr] <- R29)
| 87 | xref from 0x0d34 (in r29, 0x3e ; R29 <- IO[addr])
| 88 | xref from 0x0d8f (in r29, 0x3e ; R29 <- IO[addr])
| 89 | xref from 0x0d93 (out 0x3e, r29 ; IO[addr] <- R29)
| 90 | xref from 0x12a5 (in r29, 0x3e ; R29 <- IO[addr])
| 91 | xref from 0x12aa (out 0x3e, r29 ; IO[addr] <- R29)
| 92 | xref from 0x12c4 (out 0x3e, r29 ; IO[addr] <- R29)
| 93 |
| 94 | SFR ID: 40
| 95 | xref from 0x014e (out 0x3f, r1 ; IO[addr] <- R1)
| 96 | xref from 0x021f (in r0, 0x3f ; R0 <- IO[addr])
| 97 | xref from 0x0222 (out 0x3f, r0 ; IO[addr] <- R0)
| 98 | xref from 0x0260 (in r0, 0x3f ; R0 <- IO[addr])
| 99 | xref from 0x0263 (out 0x3f, r0 ; IO[addr] <- R0)
| 100 | xref from 0x029a (in r0, 0x3f ; R0 <- IO[addr])
| 101 | xref from 0x029d (out 0x3f, r0 ; IO[addr] <- R0)
| 102 | xref from 0x02dd (in r0, 0x3f ; R0 <- IO[addr])
| 103 | xref from 0x02e0 (out 0x3f, r0 ; IO[addr] <- R0)
| 104 | xref from 0x03f0 (in r0, 0x3f ; R0 <- IO[addr])
| 105 | xref from 0x03f3 (out 0x3f, r0 ; IO[addr] <- R0)
| 106 | xref from 0x04be (in r0, 0x3f ; R0 <- IO[addr])
| 107 | xref from 0x04c1 (out 0x3f, r0 ; IO[addr] <- R0)
| 108 | xref from 0x04d4 (in r0, 0x3f ; R0 <- IO[addr])
| 109 | xref from 0x04d7 (out 0x3f, r0 ; IO[addr] <- R0)
| 110 | xref from 0x0a31 (in r0, 0x3f ; R0 <- IO[addr])
| 111 | xref from 0x0a34 (out 0x3f, r0 ; IO[addr] <- R0)
| 112 | xref from 0x0b99 (in r0, 0x3f ; R0 <- IO[addr])
| 113 | xref from 0x0b9c (out 0x3f, r0 ; IO[addr] <- R0)
| 114 | xref from 0x0c30 (in r0, 0x3f ; R0 <- IO[addr])
| 115 | xref from 0x0c33 (out 0x3f, r0 ; IO[addr] <- R0)
| 116 | xref from 0x0c3d (in r0, 0x3f ; R0 <- IO[addr])
| 117 | xref from 0x0c40 (out 0x3f, r0 ; IO[addr] <- R0)
| 118 | xref from 0x0c9d (in r0, 0x3f ; R0 <- IO[addr])
| 119 | xref from 0x0ca0 (out 0x3f, r0 ; IO[addr] <- R0)
| 120 | xref from 0x0cb2 (in r0, 0x3f ; R0 <- IO[addr])
| 121 | xref from 0x0cb5 (out 0x3f, r0 ; IO[addr] <- R0)
| 122 | xref from 0x0d20 (in r0, 0x3f ; R0 <- IO[addr])
| 123 | xref from 0x0d23 (out 0x3f, r0 ; IO[addr] <- R0)
| 124 | xref from 0x0d91 (in r0, 0x3f ; R0 <- IO[addr])
| 125 | xref from 0x0d94 (out 0x3f, r0 ; IO[addr] <- R0)
| 126 | xref from 0x12a8 (in r0, 0x3f ; R0 <- IO[addr])
| 127 | xref from 0x12ab (out 0x3f, r0 ; IO[addr] <- R0)
| 128 | xref from 0x12c2 (in r0, 0x3f ; R0 <- IO[addr])
| 129 | xref from 0x12c5 (out 0x3f, r0 ; IO[addr] <- R0)
|
Update (v.0.8.0)
-> Devices und Device Model hinzugefügt.
Ein Device Model ist eine Abstraktion über verschiedene AVRs. Es
beinhaltet Mikrocontroller spezifische Daten, wie beispielsweise
Speichersektionen und Speicherlayouts.
Jedes implementierte device besitzt einen Device-Loader, welches das
Device Model mit relevanten Daten bezüglich des AVR Typen füttert. Das
Model kann dem Analyzer übergeben werden. Dieser analysiert dann das
Programm im Bezug auf das übergebene Model.
Dieses neue Feature vereinfacht es neue AVR Typen in die statische
Analyse zu integrieren.
Schritte zum hinzufügen eines neuen AVRs:
1.) erstelle das neue device arch/device/new_avr.c
2.) device-loader Funktion load_device_new_avr in
arch/device/new_avr.c
3.) befülle das device model mit den Daten zum neuen AVR
4.) verbinde die device-loader Funktion im Konstruktor des device
models
Damit wurde nun der AVR new_avr der stattischen Analyse hinzugefügt.
Bei einigen AVRs müsste man sicherlich noch das device model ein wenig
adjustieren, da dieser noch nicht alle Typen berücksichtigt.
-> vmcu_ prefix fehlte bei einigen Funktionen noch. Dies wurde hiermit
ergänzt. Damit dürften nun keinen Nameclashes mehr auftauchen (es sei
denn natürlich man verwendet absichtlich denselben prefix).
-> weitere tests für den Decoder (com -> sbic)
-> einige Bugfixes
Beispiel Device Models
-------------------------
1 | int main(const int argc, const char **argv) {
| 2 |
| 3 | /* ignoring checks for this example */
| 4 |
| 5 | vmcu_model_t *m328p = vmcu_model_ctor(VMCU_M328P); // ATmega328P
| 6 | vmcu_report_t *report = vmcu_analyze_ihex(argv[1], m328p);
| 7 |
| 8 | for(int32_t i = 0; i < report->progsize; i++) {
| 9 |
| 10 | vmcu_plain_t *p = &report->disassembly[i];
| 11 |
| 12 | if(p->src.type == VMCU_REGISTER && p->src.value == 16) // filter src = r16
| 13 | printf("0x%04x\t%s\n", p->addr, p->mnem);
| 14 | }
| 15 |
| 16 | vcmu_report_dtor(report);
| 17 | vmcu_model_dtor(m328p);
| 18 |
| 19 | return EXIT_SUCCESS;
| 20 | }
|
Update (v.0.8.1)
-> Hinzugefügt: Gruppen für Instruktionen. Diese richten sich nach dem
offiziellen Instruction Set Manual. Bis auf, dass die "cp" (compare)
Instruktionen in die VMCU_GROUP_MATH_LOGIC Gruppe aufgenommen wurden, da
diese nun wirklich nichts in der Flow Gruppe zu suchen haben. Wer auch
immer auf die Idee gekommen ist.
1.) arithmetic/logic instructions (VMCU_GROUP_MATH_LOGIC)
2.) data transfer instructions (VMCU_GROUP_TRANSFER)
3.) MCU control instructions (VMCU_GROUP_SYS_CTRL)
4.) flow control instructions (VMCU_GROUP_FLOW)
5.) bit and bit-test instructions (VMCU_GROUP_BIT)
-> vmcu_plain_t nach vmcu_instr_t umbenannt
Beispiel (Filtern von FLOW Instruktionen in file.hex)
-------------------------------------------------------
1 | int main(const int argc, const char **argv) {
| 2 |
| 3 | /* ignoring checks for this example */
| 4 | vmcu_model_t *m328p = vmcu_model_ctor(VMCU_M328P);
| 5 | vmcu_report_t *report = vmcu_report_ctor("file.hex", m328p);
| 6 |
| 7 | for(int32_t i = 0; i < report->progsize; i++) {
| 8 |
| 9 | vmcu_instr_t *instr = &report->disassembly[i];
| 10 |
| 11 | if(instr->group != VMCU_GROUP_FLOW)
| 12 | continue;
| 13 |
| 14 | printf("0x%04x\t", instr->addr);
| 15 | printf("%s\n", instr->mnem);
| 16 | }
| 17 |
| 18 | vmcu_report_dtor(report);
| 19 | vmcu_model_dtor(m328p);
| 20 |
| 21 | return EXIT_SUCCESS;
| 22 | }
|
Output
-------- 1 | 0x0000 jmp +52 ; PC <- 0x34
| 2 | 0x0002 jmp +81 ; PC <- 0x51
| 3 | 0x0004 jmp +81 ; PC <- 0x51
| 4 | 0x0006 jmp +81 ; PC <- 0x51
| 5 | 0x0008 jmp +81 ; PC <- 0x51
| 6 | 0x000a jmp +81 ; PC <- 0x51
| 7 | 0x000c jmp +81 ; PC <- 0x51
| 8 | 0x000e jmp +81 ; PC <- 0x51
| 9 | 0x0010 jmp +81 ; PC <- 0x51
| 10 | 0x0012 jmp +81 ; PC <- 0x51
| 11 | 0x0014 jmp +81 ; PC <- 0x51
| 12 | 0x0016 jmp +81 ; PC <- 0x51
| 13 | 0x0018 jmp +81 ; PC <- 0x51
| 14 | 0x001a jmp +81 ; PC <- 0x51
| 15 | 0x001c jmp +81 ; PC <- 0x51
| 16 | 0x001e jmp +81 ; PC <- 0x51
| 17 | 0x0020 jmp +81 ; PC <- 0x51
| 18 | 0x0022 jmp +81 ; PC <- 0x51
| 19 | 0x0024 jmp +81 ; PC <- 0x51
| 20 | 0x0026 jmp +81 ; PC <- 0x51
| 21 | 0x0028 jmp +81 ; PC <- 0x51
| 22 | 0x002a jmp +81 ; PC <- 0x51
| 23 | 0x002c jmp +83 ; PC <- 0x53
| 24 | 0x002e jmp +81 ; PC <- 0x51
| 25 | 0x0030 jmp +81 ; PC <- 0x51
| 26 | 0x0032 jmp +81 ; PC <- 0x51
| 27 | 0x003f rjmp +2 ; PC <- PC + 0x2 + 1
| 28 | 0x0044 brne -5 ; (Z = 0): PC <- PC - 0x5 + 1
| 29 | 0x0048 rjmp +1 ; PC <- PC + 0x1 + 1
| 30 | 0x004c brne -4 ; (Z = 0): PC <- PC - 0x4 + 1
| 31 | 0x004d call +130 ; PC <- 0x82
| 32 | 0x004f jmp +134 ; PC <- 0x86
| 33 | 0x0051 jmp +0 ; PC <- 0x0
| 34 | 0x0071 cpse r24, r1 ; (R24 = R1): PC <- skip
| 35 | 0x0072 rjmp +9 ; PC <- PC + 0x9 + 1
| 36 | 0x007b reti ; PC <- DATA[SP]
| 37 | 0x0081 rjmp -14 ; PC <- PC - 0xe + 1
| 38 | 0x0085 rjmp -1 ; PC <- PC - 0x1 + 1
| 39 | 0x0087 rjmp -1 ; PC <- PC - 0x1 + 1
|
Update (v.0.8.2)
-> Interrupt Vektor Analyse wurde hinzugefügt. Es werden nun Vektoren
erkannt und im Report zur Verfügung gestellt.
Bedingungen für einen Vektor:
[1] Die Instruktion muss im (default) Vectortable Segment liegen (nur
default, da BOOTRESET von Parametern abhängt).
[2] Die Instruktion muss entweder ein jmp oder ein rjmp sein
[3] Die Instruktion darf nicht zwischen zwei Vektoren liegen, falls ein
Vektor 32-bit breit ist.
[4] Es muss mindestens einmal ein "sei" im disassembly vorkommen, da man
sonst davon ausgehen muss, dass Interrupts gesperrt bleiben.
Eine zukünftige extra Bedingung wird sich die verwendeten SFRs anschauen
um festzustellen, ob für den potentiellen Vektor die erforderlichen
Konfigurationen vorgenommen werden.
Beispiel: Ausgeben von Interrupt Vektoren und deren ISR
------------------------------------------------------------
1 | int main(const int argc, const char **argv) {
| 2 |
| 3 | /* ignoring checks for this example */
| 4 | vmcu_model_t *m328p = vmcu_model_ctor(VMCU_M328P);
| 5 | vmcu_report_t *report = vmcu_analyze_ihex("file.hex", m328p);
| 6 |
| 7 | for(int32_t i = 0; i < report->n_vector; i++) {
| 8 |
| 9 | vmcu_vector_t *vect = &report->vector[i];
| 10 | vmcu_instr_t *isr = vect->xto->i;
| 11 |
| 12 | printf("Vector ID %d @ 0x%04x\n", vect->id, vect->addr);
| 13 | printf(" interrupt service routine at 0x%04x", isr->addr);
| 14 | printf("\n\n");
| 15 | }
| 16 |
| 17 | vmcu_report_dtor(report);
| 18 | vmcu_model_dtor(m328p);
| 19 |
| 20 | return EXIT_SUCCESS;
| 21 | }
|
Output (Ausschnitt)
-----------------------
1 | Vector ID 16 @ 0x0020
| 2 | interrupt service routine at 0x03f5
| 3 |
| 4 | Vector ID 17 @ 0x0022
| 5 | interrupt service routine at 0x008a
| 6 |
| 7 | Vector ID 18 @ 0x0024
| 8 | interrupt service routine at 0x03c3
| 9 |
| 10 | Vector ID 19 @ 0x0026
| 11 | interrupt service routine at 0x039d
|
Update v.0.8.6
-> Submodul für String Analyse wurde hinzugefügt. Die im binary
vorhandenen Strings werden in die report structure abgelegt. Zurzeit nur
ASCII encoding, jedoch eventuell bald auch UTF16.
-> Dabei besteht die string structure (vmcu_string_t) aus einer
Stringadresse (Startadresse im FLASH), einer Stringlänge und einem
Zeichenbuffer der den String selbst repräsentiert. Eine Folge an bytes
wird als String erkannt, wenn sie mindestens 2 Zeichen lang ist (+ null
byte).
-> Der Nutzer muss non printable characters mittels einer kleinen
Hilfsfunktion selber handlen.
-> Ein kleiner Fehler im intel hex reader wurde behoben.
ASCII strings aus einer binary lesen
------------------------------------
1 | int main(const int argc, const char **argv) {
| 2 |
| 3 | /* ignoring checks for this example */
| 4 | vmcu_model_t *m328p = vmcu_model_ctor(VMCU_DEVICE_M328P);
| 5 | vmcu_report_t *report = vmcu_analyze_ihex("file.hex", m328p);
| 6 |
| 7 | for(int32_t i = 0; i < report->n_string; i++) {
| 8 |
| 9 | vmcu_string_t *str = &report->string[i];
| 10 |
| 11 | printf("Found string \"%s", str->bytes);
| 12 | printf("\" l = %d", str->length);
| 13 | printf(" @ 0x%04x\n", str->addr);
| 14 | }
| 15 |
| 16 | printf("\nTotal strings found: %d\n", report->n_string);
| 17 |
| 18 | vmcu_report_dtor(report);
| 19 | vmcu_model_dtor(m328p);
| 20 |
| 21 | return EXIT_SUCCESS;
| 22 | }
|
1 | Found string "Welcome " l = 8 @ 0x092e
| 2 | Found string "[1] Login\n" l = 11 @ 0x0933
| 3 | Found string "[2] Memory management\n" l = 23 @ 0x0939
| 4 | Found string "Please authenticate yourself with your hardware token\n" l = 55 @ 0x0946
| 5 | Found string "Please insert token. (%d characters)\n" l = 38 @ 0x0962
| 6 | Found string "Token can only contain the characters [A-Z/a-z/0-9]\n" l = 53 @ 0x0975
| 7 |
| 8 | Total strings found: 6
|
v.0.8.8
-> vmcu_mnemonic_t structure zu vmcu_instr_t hinzugefügt. Diese Struktur
löst den "char *mnem" member ab. vmcu_mnemonic_t enthält 4 strings:
- base (z.B. "ldi", "movw", etc.)
- src (z.B. "r29", "0xff", etc.)
- dest ("r29", "0xff", etc.)
- commment (z.B. "; r21 <- 0xab", "IF <- 0", etc.)
-> disasm driver hinzugefügt. Dies ist ein kleiner disassembler mit
Syntax Highlighting, der die Vorteile der obigen Änderungen nutzt.
-> Außerdem wurde der interne disassembler überarbeitet. Diese
Überarbeitung war eine notwendige Anpassung an die obigen Änderungen.
-> Bestehende driver und die README wurden angepasst.
Dieses Update macht die Ausgabe vom disassembly viel flexibler.
Beispielsweise kann nun zwischen AT&T und Intel Syntax gewechselt werden
oder ganz bestimmte Teile können sehr einfach mit Syntax Highlighting
hervorgehoben werden.
Der Nachteil ist der, dass nun eine zusätzliche printer Fuktion
geschrieben werden muss um das disassembly auszugeben.
Diese Funktion könnte so aussehen (intel syntax)
----------------------------- 1 | /* this snippet can be used to assemble and print an instruction */
| 2 |
| 3 | void print_instruction(vmcu_instr_t *instr) {
| 4 |
| 5 | vmcu_mnemonic_t *mnem = &instr->mnem;
| 6 |
| 7 | printf("%s ", mnem->base);
| 8 | printf("%s", mnem->dest);
| 9 |
| 10 | if(instr->dest.type != VMCU_OP_NONE)
| 11 | printf(", ");
| 12 |
| 13 | printf("%s ", mnem->src);
| 14 | printf("%s\n", mnem->comment);
| 15 | }
|
Dies ist das neue Layout von vmcu_instr_t
----------------------------- 1 | typedef struct vmcu_instr {
| 2 |
| 3 | struct { ///< instruction core
| 4 |
| 5 | VMCU_IKEY key; ///< instruction key (instruction identifier)
| 6 | VMCU_GROUP group; ///< instruction group
| 7 | };
| 8 |
| 9 | int opcode; ///< 16-bit or 32-bit opcode
| 10 | int addr;
| 11 |
| 12 | bool exec; ///< instruction executable ?
| 13 | bool dword; ///< 32-bit instruction ?
| 14 |
| 15 | vmcu_operand_t src; ///< source operand (right operand)
| 16 | vmcu_operand_t dest; ///< destination operand (left operand)
| 17 |
| 18 | vmcu_mnemonic_t mnem; ///< disassembled mnemonic of instruction
| 19 |
| 20 | } vmcu_instr_t;
|
Und das ist das Layout von vmcu_mnemonic_t
----------------------------- 1 | typedef struct vmcu_mnemonic {
| 2 |
| 3 | char base [7]; ///< mnemonic base string (ldi, sts, etc.)
| 4 |
| 5 | char src [9]; ///< source operand string (r29, 0xff9a, etc.)
| 6 | char dest [9]; ///< destination operand string (r29, 0xff9a, etc.)
| 7 |
| 8 | char comment[40]; ///< comment string (todo: make comments optional)
| 9 |
| 10 | } vmcu_mnemonic_t;
|
Wie man sehen kann, nimmt der Kommentar den meisten Speicher innerhalb
der Struktur ein. Um Speicher zu sparen könnte man mit bedingter
Kompilierung anfangen um einzelne Komponenten ein- bzw. auszuschalten.
Somit wären Kommentare optional und würden nur bei tatsächlichem Bedarf
Speicher verbrauchen.
Feedback/Verbesserungsvorschläge sind natürlich gern gesehen :)
v.0.8.9 - Überarbeitung von Operanden und Operandentypen
-> VMCU_OP wurde überarbeitet und in VMCU_OPTYPE umgenannt. VMCU_OPTYPE
wurde nun nach Spezifikation
(https://en.wikipedia.org/wiki/Atmel_AVR_instruction_set) erstellt (mit
einigen wenigen Änderungen).
Die Operandentypen sehen nun wie folgt aus:
- VMCU_OPTYPE_NONE = -1 (no operand, therefore no type)
- VMCU_OPTYPE_R, (register operand)
- VMCU_OPTYPE_RP, (registerpair operand)
- VMCU_OPTYPE_X, (x pointer operand)
- VMCU_OPTYPE_Y, (y pointer operand)
- VMCU_OPTYPE_Z, (z pointer operand)
- VMCU_OPTYPE_B, (bit number 0-7)
- VMCU_OPTYPE_K4, (4-bit immediate)
- VMCU_OPTYPE_K6, (6-bit immediate)
- VMCU_OPTYPE_K8, (8-bit immediate)
- VMCU_OPTYPE_IO5, (5-bit I/O address)
- VMCU_OPTYPE_IO6, (6-bit I/O address)
- VMCU_OPTYPE_D7, (7-bit data address)
- VMCU_OPTYPE_D16, (16-bit data address)
- VMCU_OPTYPE_P22, (22-bit program address)
- VMCU_OPTYPE_S7, (7-bit signed displacement, program memory)
- VMCU_OPTYPE_S12 (12-bit signed displacement, program memory)
-> VMCU_REGISTER enumeration für die GPRs (r0-r31) hinzugefügt.
-> vmcu_registerpair_t structure hinzugefügt. Diese Structure
repräsentiert ein gewöhnliches Registerpaar der Form Rh:Rl. Die Struktur
besteht aus 2 VMCU_REGISTER Variablen.
-> vmcu_operand_t wurde ebenfalls überarbeitet. Es enthält immernoch ein
Feld für den Operandentypen (VMCU_OPTYPE) und neuerdings eine C-Union um
die verschiedenen Datentypen darzustellen.
Die Union enthält folgende Datentypen:
- uint8_t k for type = K4, K6, K8
- uint8_t b for type = B
- uint8_t io for type = IO5, IO6
- uint16_t d for type = D7, D16
- uint32_t p for type = P22
- int16_t s for type = S7, S12
-> endloop driver (driver/endloop/) verbessert. Der endloop driver kann
nun zwischen bedingten und unbedingten Endlosschleifen unterscheiden und
ermittelt diese mittels statischer Analyse.
- Driver Code wurden an v.0.8.9 angepasst
vmcu_operand_t nach Überarbeitung
------------------------ 1 | typedef struct vmcu_operand {
| 2 |
| 3 | union {
| 4 |
| 5 | uint8_t k;
| 6 | uint8_t b;
| 7 | uint8_t io;
| 8 |
| 9 | uint16_t d;
| 10 | uint32_t p;
| 11 | int16_t s;
| 12 |
| 13 | VMCU_REGISTER r;
| 14 | vmcu_registerpair_t rp;
| 15 | };
| 16 |
| 17 | VMCU_OPTYPE type;
| 18 |
| 19 | } vmcu_operand_t;
|
VMCU_OP nach Überarbeitung
------------------------ 1 | typedef enum {
| 2 |
| 3 | VMCU_OPTYPE_NONE = -1,
| 4 |
| 5 | VMCU_OPTYPE_R,
| 6 | VMCU_OPTYPE_RP,
| 7 | VMCU_OPTYPE_X,
| 8 | VMCU_OPTYPE_Y,
| 9 | VMCU_OPTYPE_Z,
| 10 | VMCU_OPTYPE_B,
| 11 | VMCU_OPTYPE_K4,
| 12 | VMCU_OPTYPE_K6,
| 13 | VMCU_OPTYPE_K8,
| 14 | VMCU_OPTYPE_IO5,
| 15 | VMCU_OPTYPE_IO6,
| 16 | VMCU_OPTYPE_D7,
| 17 | VMCU_OPTYPE_D16,
| 18 | VMCU_OPTYPE_P22,
| 19 | VMCU_OPTYPE_S7,
| 20 | VMCU_OPTYPE_S12
| 21 |
| 22 | } VMCU_OPTYPE;
|
VMCU_REGISTER enumeration
------------------------ 1 | typedef enum {
| 2 |
| 3 | VMCU_REGISTER_NONE = -1,
| 4 |
| 5 | VMCU_REGISTER_R0,
| 6 | VMCU_REGISTER_R1,
| 7 | VMCU_REGISTER_R2,
| 8 | VMCU_REGISTER_R3,
| 9 | VMCU_REGISTER_R4,
| 10 | VMCU_REGISTER_R5,
| 11 | VMCU_REGISTER_R6,
| 12 | VMCU_REGISTER_R7,
| 13 | VMCU_REGISTER_R8,
| 14 | VMCU_REGISTER_R9,
| 15 | VMCU_REGISTER_R10,
| 16 | VMCU_REGISTER_R11,
| 17 | VMCU_REGISTER_R12,
| 18 | VMCU_REGISTER_R13,
| 19 | VMCU_REGISTER_R14,
| 20 | VMCU_REGISTER_R15,
| 21 | VMCU_REGISTER_R16,
| 22 | VMCU_REGISTER_R17,
| 23 | VMCU_REGISTER_R18,
| 24 | VMCU_REGISTER_R19,
| 25 | VMCU_REGISTER_R20,
| 26 | VMCU_REGISTER_R21,
| 27 | VMCU_REGISTER_R22,
| 28 | VMCU_REGISTER_R23,
| 29 | VMCU_REGISTER_R24,
| 30 | VMCU_REGISTER_R25,
| 31 | VMCU_REGISTER_R26,
| 32 | VMCU_REGISTER_R27,
| 33 | VMCU_REGISTER_R28,
| 34 | VMCU_REGISTER_R29,
| 35 | VMCU_REGISTER_R30,
| 36 | VMCU_REGISTER_R31
| 37 |
| 38 | } VMCU_REGISTER;
|
vmcu_registerpair_t structure
------------------------ 1 | typedef struct vmcu_registerpair {
| 2 |
| 3 | VMCU_REGISTER low;
| 4 | VMCU_REGISTER high;
| 5 |
| 6 | } vmcu_registerpair_t;
|
v.0.8.10
- Added: vmcu_access_t
- vmcu_access_t holds bitfields (representing booleans)
- following bitfields are available
- registers (true if instr reads/writes gpr)
- flash (true if instr reads/writes flash)
- stack (true if instr reads/writes stack)
- io (true if instr reads/writes io segment)
- ds (true if instr reads/writes data segment)
- sp (true if instr reads/writes stack pointer)
- pc (true if instr reads/writes program counter)
- c_flag (true if instr reads/writes carry flag)
- z_flag (true if instr reads/writes zero flag)
- n_flag (true if instr reads/writes negative flag)
- v_flag (true if instr reads/writes overflow flag)
- s_flag (true if instr reads/writes sign flag)
- h_flag (true if instr reads/writes halfcarry flag)
- t_flag (true if instr reads/writes t flag)
- i_flag (true if instr reads/writes interrupt flag)
- NOTE: Although (stack, io, registers ⊂ ds), (stack = true) or (io =
true)
or (registers = true) does not imply (ds = true).
- Added: annotator stage in pipeline
- this stage annotates instructions by adding additional information
about
the instruction itself, like groups and explicit/implicit read/write
access.
- Added: rwaccess driver (driver/rwaccess/)
- Adjusted drivers, etc.
Example
------------ 1 | /* A possible implementation of print_instruction can be found above */
| 2 |
| 3 | int main(const int argc, const char **argv) {
| 4 |
| 5 | /* ignoring checks for this example */
| 6 | vmcu_model_t *m328p = vmcu_model_ctor(VMCU_DEVICE_M328P);
| 7 | vmcu_report_t *report = vmcu_analyze_ihex("file.hex", m328p);
| 8 |
| 9 | for(int32_t i = 0; i < report->progsize; i++) {
| 10 |
| 11 | vmcu_instr_t *instr = &report->disassembly[i];
| 12 |
| 13 | if(instr->writes.c_flag == true)
| 14 | print_instruction(instr);
| 15 |
| 16 | if(instr->reads.c_flag == true)
| 17 | print_instruction(instr);
| 18 | }
| 19 |
| 20 | vmcu_report_dtor(report);
| 21 | vmcu_model_dtor(m328p);
| 22 |
| 23 | return EXIT_SUCCESS;
| 24 | }
|
Possible Output
------------------- 1 | subi r18, 0x00 ; r18 <- r18 - 0x00
| 2 | adiw r29:r28, 0x1a ; r29:r28 <- r29:r28 + 0x1a
| 3 | sbci r23, 0xff ; r23 <- r23 - 0xff - CF
| 4 | cpc r19, r17 ; r19 - r17 - CF
|
v.0.8.11 - Finally added the control flow graph (cfg)
libvmcu has now control flow graphs
- added: CFG - control flow graph
- vmcu_cfg_t is the controlflow graph of libvmcu. It holds,
- vmcu_cfg_t node*, a dynamic array of vmcu_node_t structures
- node[0] is the entry node
- a node consists of
- vmcu_xref_t xto, a cross-reference to the corresponding
instruction (xref-to)
- vmcu_cfg_node_t *t, a node pointer for the true branch
- vmcu_cfg_node_t *f, a node pointer for the false branch
- int32_t used, a simple node counter, should be equal to
report.progsize
- added: CFG example driver (driver/cfg/)
- using CMake now
- added syntax highlight for stepper driver
A controlflow graph will allow us to analyze and model functions, loops,
cycles, ...
Printing control flow of a binary
------------------------------------ 1 | /* A possible implementation of print_instruction can be found in repository */
| 2 |
| 3 | int main(const int argc, const char **argv) {
| 4 |
| 5 | /* ignoring checks for this example */
| 6 | vmcu_model_t *m328p = vmcu_model_ctor(VMCU_DEVICE_M328P);
| 7 | vmcu_report_t *report = vmcu_analyze_ihex("file.hex", m328p);
| 8 |
| 9 | for(int32_t i = 0; i < report->cfg->used; i++) {
| 10 |
| 11 | vmcu_cfg_node_t *node = &report->cfg->node[i];
| 12 | print_instruction(node->xto.i);
| 13 |
| 14 | if(node->t != NULL) {
| 15 |
| 16 | printf("true -> ");
| 17 | print_instruction(node->t->xto.i);
| 18 | }
| 19 |
| 20 | if(node->f != NULL) {
| 21 |
| 22 | printf("false -> ");
| 23 | print_instruction(node->f->xto.i);
| 24 | }
| 25 |
| 26 | printf("\n");
| 27 | }
| 28 |
| 29 | vmcu_report_dtor(report);
| 30 | vmcu_model_dtor(m328p);
| 31 |
| 32 | return EXIT_SUCCESS;
| 33 | }
|
Example Output
-------------------- 1 | 0x0000 .... f1f3 breq -2 ; (ZF == 1): PC <- PC + -2 + 1
| 2 | true -> 0x3fff .... 839a sbi 0x10, 3 ; IO[0x10, 3] <- 1
| 3 | false -> 0x0001 .... 0fef ldi r16, 0xff ; r16 <- 0xff
| 4 | --------------------------------------------------------------------------------
| 5 | 0x0001 .... 0fef ldi r16, 0xff ; r16 <- 0xff
| 6 | true -> 0x0002 .... 5817 cp r21, r24 ; r21 - r24
| 7 | --------------------------------------------------------------------------------
| 8 | 0x0002 .... 5817 cp r21, r24 ; r21 - r24
| 9 | true -> 0x0003 .... 19f4 brne 3 ; (ZF == 0): PC <- PC + 3 + 1
| 10 | --------------------------------------------------------------------------------
| 11 | 0x0003 .... 19f4 brne 3 ; (ZF == 0): PC <- PC + 3 + 1
| 12 | true -> 0x0007 .... 0127 eor r16, r17 ; r16 <- r16 ^ r17
| 13 | false -> 0x0004 .... a895 wdr ; watchdog reset
| 14 | --------------------------------------------------------------------------------
| 15 | 0x0004 .... a895 wdr ; watchdog reset
| 16 | true -> 0x0005 .... 8895 sleep ; circuit sleep
| 17 | --------------------------------------------------------------------------------
| 18 | 0x0005 .... 8895 sleep ; circuit sleep
| 19 | true -> 0x0006 .... 0000 nop ; no operation
| 20 | --------------------------------------------------------------------------------
| 21 | 0x0006 .... 0000 nop ; no operation
| 22 | true -> 0x0007 .... 0127 eor r16, r17 ; r16 <- r16 ^ r17
| 23 | --------------------------------------------------------------------------------
| 24 | 0x0007 .... 0127 eor r16, r17 ; r16 <- r16 ^ r17
| 25 | --------------------------------------------------------------------------------
| 26 | 0x3fff .... 839a sbi 0x10, 3 ; IO[0x10, 3] <- 1
| 27 | true -> 0x0000 .... f1f3 breq -2 ; (ZF == 1): PC <- PC + -2 + 1
| 28 | --------------------------------------------------------------------------------
|
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|