The AVR disassembler
Trust is good, checks are better. An old proverb from the country I live in. You need to check in order to
know your current situation. To be able to see if we're heading for the hall of fame, or for hell and
damnation. So before I can start my real project for the AVR (my own kind of assembler) I need a tool to get
from output right back to the input.
So I need a reverse engineering program for Atmel AVR object files. Such a product is generally referred to as
a disassembler. It reads the object file (in Intel Hex) and converts all opcodes (short for Operation Codes,
the raw numbers which are interpreted by the processor) back to human readable (and understandable) mnemonics.
I started the project less than a week ago and by now I have a good working product. I am still beta testing,
but the amount of errors is still very small.
AVRdis the disassembler
Below is the source code for the disassembler. This is a mature beta product, so there might still be some errors and minor inadequacies around. The source is, as usual, in Modula-2. You are free to port this high level language project to a lesser language, if you prefer to use C. But an error is easily made with C and it's lookalikes, especially when nibble-nine codes are to be decoded.
Since the source is in Modula-2 I can suffice to say that it needs little to no explanation. One remark however: I made this program with NEARLY ONLY qualified imports. So I do one IMPORT and next I couple a procedure and a library with the well known dot operator. See how far you get studying the source.
MODULE dis04;
(* This software is released under the rules of the GNU GPL. Please feel free to improve this software,
if needed. You may even port this source to a lesser language like C. This software comes without any
warranty of any kind.
*)
(* version disxx is a disassembler for Atmel AVR processors *)
(* 01 : Try to get the framework running : Sept 20, 2006 *)
(* 02 : Make it adaptable : Oct 12, 2006 *)
(* 03 : Make it recognize bit constant names for ports : Oct 20, 2006 *)
(* 04 : Improved user messages and command line checking
Added the support for the 'COMPILER/END' option
Fixed the sts bug (parameters swapped) : Oct 27, 2006 *)
FROM MemPools IMPORT KillPool, MemPool, NewPool, PoolAllocate;
IMPORT ASCII, Arguments, InOut, NumConv, Strings, SYSTEM, TextIO;
TYPE Identifier = ARRAY [0..63] OF CHAR;
PortPtr = POINTER TO PortNode;
NamePtr = POINTER TO NameNode;
PortNode = RECORD
number : CARDINAL;
Name : Identifier;
next : PortPtr
END;
NameNode = RECORD
RegName : Identifier;
BitNr : CARDINAL;
BitName : Identifier;
next : NamePtr
END;
VAR LstFile, InFile : TextIO.File;
storage : Arguments.ArgTable;
par1,
Source, Target, Dest, path, option : Identifier;
PC, FlashRam, Eeprom, Sram, RAMstart,
line, column : CARDINAL;
DecodeMode : (Jump, ToMemory, FromMemory, None);
DecodeNext, ok, BigFlash : BOOLEAN;
PortNames, BitNames : MemPool;
FirstPort : PortPtr;
FirstName : NamePtr;
PROCEDURE UserMessage (nr : CARDINAL);
BEGIN
CASE nr OF
0 : InOut.WriteCard (line, 5); InOut.Write (':'); InOut.WriteCard (column, 5)
| 1 : InOut.WriteString ("Syntax : AvrDis Infile [-t TargetCPU] [-v]")
| 2 : InOut.WriteString ("AvrDis version 0.4, October 2006")
| 3 : InOut.WriteString ("Could not open "); InOut.WriteString (path)
| 4 : InOut.WriteString ("Error in Configuration file. Aborting.")
| 5 : InOut.WriteString ("Could not open source file. Aborting.")
| 6 : InOut.WriteString ("No source file defined. Aborting.")
| 7 : InOut.WriteString ("Could not create destination file.")
| 8 : InOut.WriteString ("Error in Intel Hex file in line:column"); UserMessage (0)
| 9 : InOut.WriteString ("Error in "); InOut.WriteString (path)
| 10 : InOut.WriteString ("Illegal IO port number; ignoring.")
| 11 : InOut.WriteString ("Please specify a target processor.")
ELSE
InOut.WriteString ("Something weird happened while processing line");
InOut.WriteCard (line, 5);
InOut.WriteString (", column");
InOut.WriteCard (column, 4)
END;
InOut.WriteLn
END UserMessage;
PROCEDURE StorePort (str : Identifier; nr : CARDINAL) : BOOLEAN;
VAR this, prev, new : PortPtr;
result : INTEGER;
BEGIN
this := FirstPort;
prev := NIL;
LOOP
result := Strings.compare (this^.Name, str);
IF result = 0 THEN RETURN FALSE END;
IF result = 1 THEN EXIT END;
prev := this;
this := this^.next
END;
PoolAllocate (PortNames, new, SYSTEM.TSIZE (PortNode));
new^.Name := str;
new^.number := nr;
new^.next := this;
IF prev = NIL THEN
FirstPort := new
ELSE
prev^.next := new
END;
RETURN TRUE
END StorePort;
PROCEDURE FindPort (n : CARDINAL; VAR str : Identifier);
VAR ThisOne : PortPtr;
BEGIN
ThisOne := FirstPort;
LOOP
IF ThisOne^.number = n THEN
str := ThisOne^.Name;
EXIT
ELSE
ThisOne := ThisOne^.next
END;
IF Strings.StrEq (ThisOne^.Name, "|") THEN
str := "Reserved";
EXIT
END;
END
END FindPort;
PROCEDURE StoreName (port : Identifier; bit : CARDINAL; name : Identifier);
VAR this, new, prev : NamePtr;
BEGIN
this := FirstName;
prev := NIL;
LOOP
IF Strings.compare (this^.RegName, port) >= 0 THEN EXIT END;
prev := this;
this := this^.next
END;
PoolAllocate (BitNames, new, SYSTEM.TSIZE (NameNode));
WITH new^ DO
RegName := port;
BitNr := bit;
BitName := name;
next := this
END;
IF prev = NIL THEN
FirstName := new
ELSE
prev^.next := new
END
END StoreName;
PROCEDURE FindBit (reg : Identifier; bit : CARDINAL; VAR str : Identifier);
VAR ThisOne : NamePtr;
result : INTEGER;
BEGIN
ThisOne := FirstName;
LOOP
result := Strings.compare (ThisOne^.RegName, reg);
IF result = 0 THEN
LOOP
IF bit = ThisOne^.BitNr THEN
str := ThisOne^.BitName;
RETURN
ELSE
ThisOne := ThisOne^.next
END;
IF Strings.StrEq (reg, ThisOne^.RegName) = FALSE THEN EXIT END
END
ELSIF result = -1 THEN
ThisOne := ThisOne^.next
ELSE
EXIT
END
END;
str [0] := CHR (bit + ORD ('0'));
str [1] := 0C
END FindBit;
PROCEDURE Configure () : BOOLEAN;
VAR nr : CARDINAL;
PortName, BitName : Identifier;
BEGIN
InOut.WriteString ("Disassembling source "); InOut.WriteString (Source);
InOut.WriteString (" for target processor "); InOut.WriteString (Target);
InOut.WriteLn;
path := "/usr/local/AVR/";
Strings.Append (path, Target);
TextIO.OpenInput (InFile, path);
IF TextIO.Done () = FALSE THEN
UserMessage (3);
InOut.WriteString ("Choosing ATmega8515 instead."); InOut.WriteLn;
InOut.WriteBf;
path := "/usr/local/AVR/ATmega8515";
TextIO.OpenInput (InFile, path);
IF TextIO.Done () = FALSE THEN UserMessage (3); RETURN FALSE END
END;
REPEAT
TextIO.GetString (InFile, option);
IF TextIO.EOF (InFile) = TRUE THEN UserMessage (9); RETURN FALSE END
UNTIL Strings.StrEq (option, 'BEGIN');
LOOP
IF TextIO.EOF (InFile) = TRUE THEN UserMessage (9); RETURN FALSE END;
TextIO.GetString (InFile, option);
IF Strings.StrEq (option, 'END') = TRUE THEN EXIT
ELSIF Strings.StrEq (option, 'FLASH') = TRUE THEN TextIO.GetCard (InFile, FlashRam)
ELSIF Strings.StrEq (option, 'EEPROM') = TRUE THEN TextIO.GetCard (InFile, Eeprom)
ELSIF Strings.StrEq (option, 'SRAM') = TRUE THEN TextIO.GetCard (InFile, Sram)
ELSIF Strings.StrEq (option, 'RAMSTART') = TRUE THEN TextIO.GetCard (InFile, RAMstart)
ELSIF Strings.StrEq (option, 'COMPILER') = TRUE THEN
REPEAT TextIO.GetString (InFile, option) UNTIL Strings.StrEq (option, 'END')
ELSIF Strings.StrEq (option, 'PORTS') = TRUE THEN
LOOP
TextIO.GetString (InFile, option);
IF Strings.StrEq (option, 'END') = TRUE THEN EXIT END;
NumConv.Str2Num (nr, 16, option, ok);
TextIO.GetString (InFile, PortName);
IF NOT ok THEN
UserMessage (10)
ELSE
ok := StorePort (PortName, nr)
END
END
ELSIF Strings.StrEq (option, 'BITS') = TRUE THEN
LOOP
TextIO.GetString (InFile, PortName);
IF Strings.StrEq (PortName, 'END') = TRUE THEN EXIT END;
nr := 7;
LOOP
TextIO.GetString (InFile, BitName);
IF BitName [0] # '-' THEN StoreName (PortName, nr, BitName) END;
IF nr = 0 THEN EXIT ELSE DEC (nr) END
END
END
ELSE
InOut.WriteString ('Invalid parameter in ');
InOut.WriteString (path); InOut.Write ('.');
InOut.WriteLn;
RETURN FALSE
END
END;
TextIO.Close (InFile);
IF FlashRam > 4096 THEN BigFlash := TRUE ELSE BigFlash := FALSE END;
RETURN TRUE
END Configure;
PROCEDURE ReadChar (VAR ch : CHAR);
BEGIN
ch := option [column];
INC (column);
END ReadChar;
PROCEDURE ReadHex (width : CARDINAL) : CARDINAL;
VAR val, n : CARDINAL;
ch : CHAR;
BEGIN
n := 0;
val := 0;
LOOP
val := val * 16;
ReadChar (ch);
IF ch < 'A' THEN
INC (val, ORD (ch) - ORD ('0'))
ELSE
INC (val, ORD (ch) - ORD ('A') + 10)
END;
INC (n);
IF n = width THEN EXIT END
END;
RETURN val
END ReadHex;
PROCEDURE GetRegs (word : CARDINAL; VAR str : Identifier);
VAR reg1, reg2 : CARDINAL;
ok : BOOLEAN;
substr : ARRAY [0..3] OF CHAR;
BEGIN
word := word MOD 1024; (* throw away all rubbish *)
str:= 'R';
reg2 := word MOD 16;
IF 9 IN BITSET (word) THEN INC (reg2, 16) END;
reg1 := (word DIV 16) MOD 32;
NumConv.Num2Str (reg1, 10, substr, ok);
Strings.Append (str, substr);
Strings.Append (str, ', R');
NumConv.Num2Str (reg2, 10, substr, ok);
Strings.Append (str, substr)
END GetRegs;
PROCEDURE DumpToken (word : CARDINAL);
VAR byte1, byte2 : CARDINAL;
char1, char2 : CHAR;
str : ARRAY [0..7] OF CHAR;
BEGIN
IF word = 0A0DH THEN
Strings.Assign (str, ' Cr/Lf')
ELSE
Strings.Assign (str, ' ');
byte1 := word DIV 256;
byte2 := word MOD 256;
IF (byte1 > 31) AND (byte1 < 127) THEN char1 := CHR (byte1) ELSE char1 := '.' END;
IF (byte2 > 31) AND (byte2 < 127) THEN char2 := CHR (byte2) ELSE char2 := '.' END;
str [6] := char2;
str [7] := char1
END;
TextIO.PutString (LstFile, str)
END DumpToken;
PROCEDURE Decode (value : CARDINAL);
VAR mnem, par2, par3 : Identifier;
undef : BOOLEAN;
const, nibble, sub, subb, reg1, reg2 : CARDINAL;
jump : CARDINAL;
BEGIN
TextIO.PutHex (LstFile, PC, 8); TextIO.PutHex (LstFile, value, 8);
DumpToken (value);
undef := FALSE;
TextIO.PutString (LstFile, ' ');
nibble := value DIV 4096;
CASE nibble OF
0 : sub := (value DIV 1024) MOD 4;
CASE sub OF
0 : subb := (value DIV 256) MOD 4;
CASE subb OF
0 : IF value = 0 THEN
mnem := 'nop'
ELSE
mnem := 'Error';
undef := TRUE
END
| 1 : mnem := 'movw ';
reg1 := ((value DIV 16) MOD 16) * 2; reg2 := (value MOD 16) * 2;
| 2 : mnem := 'muls ';
reg1 := ((value DIV 16) MOD 16) + 16; reg2 := (value MOD 16) + 16;
| 3 : const := ((value DIV 8) MOD 2) + ((value DIV 128) MOD 2) * 2;
reg1 := ((value DIV 16) MOD 8) + 16; reg2 := (value MOD 8) + 16;
CASE const OF
0 : mnem := 'mulsu '
| 1 : mnem := 'fmul '
| 2 : mnem := 'fmuls '
| 3 : mnem := 'fmulsu '
END
END;
IF (value > 0) AND (undef = FALSE) THEN
Strings.Append (mnem, 'R');
NumConv.Num2Str (reg1, 10, par1, ok); Strings.Append (par1, ', R');
Strings.Append (mnem, par1);
NumConv.Num2Str (reg2, 10, par1, ok); Strings.Append (mnem, par1)
END
| 1 : mnem := 'cpc '; GetRegs (value, par1);
Strings.Append (mnem, par1)
| 2 : mnem := 'sbc '; GetRegs (value, par1);
Strings.Append (mnem, par1)
| 3 : reg1 := value MOD 16;
IF 9 IN BITSET (value) THEN INC (reg1, 16) END;
reg2 := (value DIV 16) MOD 32;
IF reg1 = reg2 THEN
mnem := 'lsl R';
NumConv.Num2Str (reg1, 10, par1, ok)
ELSE
mnem := 'add '; GetRegs (value, par1)
END;
Strings.Append (mnem, par1)
END
| 1 : sub := (value DIV 1024) MOD 4; (* isolate bits 11 and 12 *)
CASE sub OF
0 : mnem := 'cpse '; GetRegs (value, par1)
| 1 : mnem := 'cp '; GetRegs (value, par1)
| 2 : mnem := 'sub '; GetRegs (value, par1)
| 3 : reg1 := value MOD 16;
IF 9 IN BITSET (value) THEN INC (reg1, 16) END;
reg2 := (value DIV 16) MOD 32;
IF reg1 = reg2 THEN
mnem := 'rol R'; NumConv.Num2Str (reg1, 10, par1, ok)
ELSE
mnem := 'adc '; GetRegs (value, par1)
END;
END;
Strings.Append (mnem, par1)
| 2 : sub := (value DIV 1024) MOD 4; (* isolate bits 11 and 12 *)
CASE sub OF
0 : reg1 := value MOD 16;
IF 9 IN BITSET (value) THEN INC (reg1, 16) END;
reg2 := (value DIV 16) MOD 32;
IF reg1 = reg2 THEN
mnem := 'tst R'; NumConv.Num2Str (reg1, 10, par1, ok)
ELSE
mnem := 'and '; GetRegs (value, par1)
END
| 1 : reg1 := value MOD 16;
IF 9 IN BITSET (value) THEN INC (reg1, 16) END;
reg2 := (value DIV 16) MOD 32;
IF reg1 = reg2 THEN
mnem := 'clr R'; NumConv.Num2Str (reg1, 10, par1, ok)
ELSE
mnem := 'eor '; GetRegs (value, par1)
END
| 2 : mnem := 'or '; GetRegs (value, par1)
| 3 : mnem := 'mov '; GetRegs (value, par1)
END;
Strings.Append (mnem, par1)
| 3 : mnem := 'cpi R';
reg1 := ((value DIV 16) MOD 16) + 16;
const := 16 * ((value DIV 256) MOD 16) + (value MOD 16);
NumConv.Num2Str (reg1, 10, par1, ok);
Strings.Append (mnem, par1);
Strings.Append (mnem, ', ');
NumConv.Num2Str (const, 10, par1, ok);
Strings.Append (mnem, par1)
| 4 : mnem := 'sbci R';
reg1 := ((value DIV 16) MOD 16) + 16;
const := 16 * ((value DIV 256) MOD 16) + (value MOD 16);
NumConv.Num2Str (reg1, 10, par1, ok);
Strings.Append (mnem, par1);
Strings.Append (mnem, ', ');
NumConv.Num2Str (const, 10, par1, ok);
Strings.Append (mnem, par1)
| 5 : mnem := 'subi R';
reg1 := ((value DIV 16) MOD 16) + 16;
const := 16 * ((value DIV 256) MOD 16) + (value MOD 16);
NumConv.Num2Str (reg1, 10, par1, ok);
Strings.Append (mnem, par1);
Strings.Append (mnem, ', ');
NumConv.Num2Str (const, 10, par1, ok);
Strings.Append (mnem, par1)
| 6 : mnem := 'ori R';
reg1 := ((value DIV 16) MOD 16) + 16;
const := 16 * ((value DIV 256) MOD 16) + (value MOD 16);
NumConv.Num2Str (reg1, 10, par1, ok);
Strings.Append (mnem, par1);
Strings.Append (mnem, ', $');
NumConv.Num2Str (const, 16, par1, ok);
IF const < 16 THEN Strings.Append (mnem, '0') END;
Strings.Append (mnem, par1)
| 7 : mnem := 'andi R';
reg1 := ((value DIV 16) MOD 16) + 16;
const := 16 * ((value DIV 256) MOD 16) + (value MOD 16);
NumConv.Num2Str (reg1, 10, par1, ok);
Strings.Append (mnem, par1);
Strings.Append (mnem, ', $');
NumConv.Num2Str (const, 16, par1, ok);
IF const < 16 THEN Strings.Append (mnem, '0') END;
Strings.Append (mnem, par1)
| 9 : sub := (value DIV 512) MOD 8;
CASE sub OF
0 : subb := value MOD 16;
reg1 := (value DIV 16) MOD 32;
NumConv.Num2Str (reg1, 10, par1, ok);
CASE subb OF
0 : mnem := 'lds R';
Strings.Append (mnem, par1);
Strings.Append (mnem, ', ');
DecodeMode := FromMemory;
DecodeNext := FALSE
| 1 : mnem := 'ld R';
Strings.Append (mnem, par1);
Strings.Append (mnem, ', Z+')
| 2 : mnem := 'ld R';
Strings.Append (mnem, par1);
Strings.Append (mnem, ', -Z')
| 4 : mnem := 'lpm R';
Strings.Append (mnem, par1);
Strings.Append (mnem, ', Z')
| 5 : mnem := 'lpm R';
Strings.Append (mnem, par1);
Strings.Append (mnem, ', Z+')
| 6 : mnem := 'elpm R';
Strings.Append (mnem, par1);
Strings.Append (mnem, ', Z+')
| 7 : mnem := 'elpm R';
Strings.Append (mnem, par1);
Strings.Append (mnem, ', Z')
| 9 : mnem := 'ld R';
Strings.Append (mnem, par1);
Strings.Append (mnem, ', Y+')
| 10 : mnem := 'ld R';
Strings.Append (mnem, par1);
Strings.Append (mnem, ', -Y')
| 12 : mnem := 'ld R';
Strings.Append (mnem, par1);
Strings.Append (mnem, ', X')
| 13 : mnem := 'ld R';
Strings.Append (mnem, par1);
Strings.Append (mnem, ', X+')
| 14 : mnem := 'ld R';
Strings.Append (mnem, par1);
Strings.Append (mnem, ', -X')
| 15 : mnem := 'pop R';
Strings.Append (mnem, par1)
ELSE
mnem := 'Error'
END
| 1 : subb := value MOD 16;
reg1 := (value DIV 16) MOD 32;
NumConv.Num2Str (reg1, 10, par1, ok);
CASE subb OF
0 : mnem := 'sts ';
DecodeMode := ToMemory;
DecodeNext := FALSE
| 1 : mnem := 'st Z+, R';
Strings.Append (mnem, par1);
| 2 : mnem := 'st -Z, R';
Strings.Append (mnem, par1);
| 9 : mnem := 'st Y+, R';
Strings.Append (mnem, par1);
| 10 : mnem := 'st -Y, R';
Strings.Append (mnem, par1);
| 12 : mnem := 'st X, R';
Strings.Append (mnem, par1);
| 13 : mnem := 'st X+, R';
Strings.Append (mnem, par1);
| 14 : mnem := 'st -X, R';
Strings.Append (mnem, par1);
| 15 : mnem := 'push R';
Strings.Append (mnem, par1)
ELSE
mnem := 'Error'
END
| 2 : subb := value MOD 16;
reg1 := (value DIV 16) MOD 32;
NumConv.Num2Str (reg1, 10, par1, ok);
CASE subb OF
0 : mnem := 'com R';
Strings.Append (mnem, par1);
| 1 : mnem := 'neg R';
Strings.Append (mnem, par1);
| 2 : mnem := 'swap R';
Strings.Append (mnem, par1);
| 3 : mnem := 'inc R';
Strings.Append (mnem, par1);
| 5 : mnem := 'asr R';
Strings.Append (mnem, par1);
| 6 : mnem := 'lsr R';
Strings.Append (mnem, par1);
| 7 : mnem := 'ror R';
Strings.Append (mnem, par1);
| 8 : const := (value DIV 16) MOD 32;
CASE const OF
0 : mnem := 'sec'
| 1 : mnem := 'sez'
| 2 : mnem := 'sen'
| 3 : mnem := 'sev'
| 4 : mnem := 'ses'
| 5 : mnem := 'seh'
| 6 : mnem := 'set'
| 7 : mnem := 'sei'
| 8 : mnem := 'clc'
| 9 : mnem := 'clz'
| 10 : mnem := 'cln'
| 11 : mnem := 'clv'
| 12 : mnem := 'cls'
| 13 : mnem := 'clh'
| 14 : mnem := 'clt'
| 15 : mnem := 'cli'
| 16 : mnem := 'ret'
| 17 : mnem := 'reti'
| 24 : mnem := 'sleep'
| 25 : mnem := 'break'
| 26 : mnem := 'wdr'
| 28 : mnem := 'lpm R0, Z'
| 29 : mnem := 'elpm R0'
| 30 : mnem := 'spm'
ELSE
mnem := 'Error'
END
| 9 : const := (value DIV 16) MOD 32;
CASE const OF
0 : mnem := 'ijmp'
| 1 : mnem := 'eijmp'
| 16 : mnem := 'icall'
| 17 : mnem := 'eicall'
ELSE
mnem := 'Error'
END
| 10 : mnem := 'dec R';
Strings.Append (mnem, par1);
| 12,
13 : mnem := 'jmp $';
const := ((value DIV 16) MOD 32) + (value MOD 2);
NumConv.Num2Str (const, 16, par1, ok);
Strings.Append (mnem, par1);
DecodeMode := Jump;
DecodeNext := FALSE
| 14,
15 : mnem := 'call $';
const := ((value DIV 16) MOD 32) + (value MOD 2);
NumConv.Num2Str (const, 16, par1, ok);
Strings.Append (mnem, par1);
DecodeMode := Jump;
DecodeNext := FALSE
ELSE
mnem := 'Error'
END
| 3 : reg1 := (((value DIV 16) MOD 4) * 2) + 25;
NumConv.Num2Str (reg1, 10, par1, ok);
const := 16 * ((value DIV 64) MOD 4) + (value MOD 16);
NumConv.Num2Str (const, 10, par2, ok);
IF 8 IN BITSET (value) THEN
mnem := 'sbiw R'
ELSE
mnem := 'adiw R'
END;
Strings.Append (mnem, par1); DEC (par1 [1]);
Strings.Append (mnem, ':'); Strings.Append (mnem, par1);
Strings.Append (mnem, ', '); Strings.Append (mnem, par2)
| 4 : const := value MOD 8;
reg1 := (value DIV 8) MOD 32;
FindPort (reg1, par1); FindBit (par1, const, par2);
IF 8 IN BITSET (value) THEN
mnem := 'sbic '
ELSE
mnem := 'cbi '
END;
Strings.Append (mnem, par1); Strings.Append (mnem, ', ');
Strings.Append (mnem, par2)
| 5 : const := value MOD 8;
reg1 := (value DIV 8) MOD 32;
FindPort (reg1, par1); FindBit (par1, const, par2);
IF 8 IN BITSET (value) THEN
mnem := 'sbis '
ELSE
mnem := 'sbi '
END;
Strings.Append (mnem, par1); Strings.Append (mnem, ', ');
Strings.Append (mnem, par2)
| 6,
7 : mnem := 'mul ';
GetRegs (value, par1);
Strings.Append (mnem, par1)
END;
| 8,
10 : sub := ((value DIV 8) MOD 2) + 2 * ((value DIV 512) MOD 2);
const := (value MOD 8) + 8 * ((value DIV 1024) MOD 4) + 32 * ((value DIV 8192) MOD 2);
reg1 := (value DIV 16) MOD 32;
NumConv.Num2Str (reg1, 10, par1, ok);
NumConv.Num2Str (const, 10, par2, ok);
IF const = 0 THEN
CASE sub OF
0 : mnem := 'ld R';
Strings.Append (mnem, par1); Strings.Append (mnem, ', Z')
| 1 : mnem := 'ld R';
Strings.Append (mnem, par1); Strings.Append (mnem, ', Y')
| 2 : mnem := 'st Z, R'; Strings.Append (mnem, par1)
| 3 : mnem := 'st Y, R'; Strings.Append (mnem, par1)
END
ELSE
CASE sub OF
0 : mnem := 'ldd R';
Strings.Append (mnem, par1);
Strings.Append (mnem, ', Z + ');
Strings.Append (mnem, par2)
| 1 : mnem := 'ldd R';
Strings.Append (mnem, par1);
Strings.Append (mnem, ', Y + ');
Strings.Append (mnem, par2)
| 2 : mnem := 'std Z + ';
Strings.Append (mnem, par2);
Strings.Append (mnem, ', R');
Strings.Append (mnem, par1)
| 3 : mnem := 'std Y + ';
Strings.Append (mnem, par2);
Strings.Append (mnem, ', R');
Strings.Append (mnem, par1)
END
END
| 11 : reg1 := (value DIV 16) MOD 32;
reg2 := 16 * ((value DIV 512) MOD 4) + (value MOD 16);
IF 11 IN BITSET (value) THEN
mnem := 'out ';
FindPort (reg2, par1);
Strings.Append (mnem, par1); Strings.Append (mnem, ', R');
NumConv.Num2Str (reg1, 10, par1, ok);
Strings.Append (mnem, par1)
ELSE
mnem := 'in R';
NumConv.Num2Str (reg1, 10, par1, ok);
Strings.Append (mnem, par1); Strings.Append (mnem, ', ');
FindPort (reg2, par1);
Strings.Append (mnem, par1)
END
| 12 : mnem := 'rjmp $';
const := value MOD 4096;
IF const > 2047 THEN
const := PC + const - 4095
ELSE
const := PC + const + 1
END;
NumConv.Num2Str (const, 16, par1, ok);
Strings.Append (mnem, par1)
| 13 : mnem := 'rcall $';
const := value MOD 4096;
IF const > 2047 THEN
const := PC + const - 4095
ELSE
const := PC + const + 1
END;
NumConv.Num2Str (const, 16, par1, ok);
Strings.Append (mnem, par1)
| 14 : const := (value MOD 16) + 16 * ((value DIV 256) MOD 16);
reg1 := ((value DIV 16) MOD 16) + 16;
IF const = 0FFH THEN
mnem := 'ser R'; NumConv.Num2Str (reg1, 10, par1, ok)
ELSE
mnem := 'ldi R'; NumConv.Num2Str (reg1, 10, par1, ok);
Strings.Append (mnem, par1); Strings.Append (mnem, ', $');
NumConv.Num2Str (const, 16, par1, ok)
END;
Strings.Append (mnem, par1)
| 15 : sub := (value DIV 1024) MOD 4;
subb := value MOD 8;
CASE sub OF
0 : jump := (value DIV 8) MOD 128;
IF jump > 63 THEN
jump := PC + jump - 127
ELSE
jump := PC + jump + 1
END;
CASE subb OF
0 : mnem := 'brcs'
| 1 : mnem := 'breq'
| 2 : mnem := 'brmi'
| 3 : mnem := 'brvs'
| 4 : mnem := 'brlt'
| 5 : mnem := 'brhs'
| 6 : mnem := 'brts'
| 7 : mnem := 'brie'
END;
Strings.Append (mnem, ' $'); NumConv.Num2Str (jump, 16, par1, ok);
Strings.Append (mnem, par1)
| 1 : jump := (value DIV 8) MOD 128;
IF jump > 63 THEN
jump := PC - 127 + jump
ELSE
jump := PC + jump + 1
END;
CASE subb OF
0 : mnem := 'brcc'
| 1 : mnem := 'brne'
| 2 : mnem := 'brpl'
| 3 : mnem := 'brvc'
| 4 : mnem := 'brge'
| 5 : mnem := 'brhc'
| 6 : mnem := 'brtc'
| 7 : mnem := 'brid'
END;
Strings.Append (mnem, ' $'); NumConv.Num2Str (jump, 16, par1, ok);
Strings.Append (mnem, par1)
| 2 : subb := ((value DIV 8) MOD 2) + 2 * ((value DIV 512) MOD 2);
reg1 := (value DIV 16) MOD 32;
NumConv.Num2Str (reg1, 10, par1, ok);
const := value MOD 8;
NumConv.Num2Str (const, 10, par2, ok);
CASE subb OF
0 : mnem := 'bld R'
| 2 : mnem := 'bst R'
ELSE
mnem := 'Error'
END;
IF Strings.StrEq (mnem, 'Error') = FALSE THEN
Strings.Append (mnem, par1); Strings.Append (mnem, ', bit ');
Strings.Append (mnem, par2)
END
| 3 : subb := ((value DIV 8) MOD 2) + 2 * ((value DIV 512) MOD 2);
reg1 := (value DIV 16) MOD 32;
NumConv.Num2Str (reg1, 10, par1, ok);
const := value MOD 8;
NumConv.Num2Str (const, 10, par2, ok);
CASE subb OF
0 : mnem := 'sbrc R'
| 2 : mnem := 'sbrs R'
ELSE
mnem := 'Error'
END;
IF Strings.StrEq (mnem, 'Error') = FALSE THEN
Strings.Append (mnem, par1); Strings.Append (mnem, ', bit ');
Strings.Append (mnem, par2)
END
END
ELSE
mnem := 'Something fishy!'
END;
TextIO.PutString (LstFile, mnem);
IF DecodeNext = TRUE THEN TextIO.PutLn (LstFile) END;
TextIO.PutBf (LstFile)
END Decode;
PROCEDURE ProcessLine;
VAR checksum, words, n, type, code : CARDINAL;
par2 : Identifier;
ch : CHAR;
BEGIN
LOOP
TextIO.GetString (InFile, option);
IF TextIO.EOF (InFile) = TRUE THEN EXIT END;
INC (line);
column := 0;
ReadChar (ch);
IF ch = ':' THEN
words := ReadHex (2) DIV 2;
n := ReadHex (4); PC := n DIV 2;
type := ReadHex (2);
IF type = 1 THEN RETURN END;
IF type = 2 THEN PC := ReadHex (2) + 256 * ReadHex (2) END;
IF type = 0 THEN
FOR n := 1 TO words DO
code := ReadHex (2) + 256 * ReadHex (2);
IF DecodeNext = TRUE THEN
Decode (code)
ELSE
IF DecodeMode = Jump THEN
NumConv.Num2Str (code, 16, par1, ok)
ELSIF (DecodeMode = FromMemory) OR (DecodeMode = ToMemory) THEN
IF DecodeMode = ToMemory THEN par2 := par1 END;
IF code < 32 THEN
TextIO.PutString (LstFile, 'R');
NumConv.Num2Str (code, 10, par1, ok)
ELSIF code < 96 THEN
FindPort (code - 32, par1)
ELSIF code < RAMstart THEN
FindPort (code + 256, par1)
ELSE
TextIO.PutString (LstFile, '[$');
NumConv.Num2Str (code, 16, par1, ok);
Strings.Append (par1, ']')
END
END;
TextIO.PutString (LstFile, par1);
IF DecodeMode = ToMemory THEN
TextIO.PutString (LstFile, ", R"); TextIO.PutString (LstFile, par2)
END;
TextIO.PutLn (LstFile);
TextIO.PutHex (LstFile, PC, 8); TextIO.PutHex (LstFile, code, 8);
DumpToken (code); TextIO.PutLn (LstFile);
DecodeNext := TRUE;
DecodeMode := None
END;
INC (PC)
END
END
ELSE
UserMessage (8)
END
END
END ProcessLine;
PROCEDURE OpenFiles;
BEGIN
TextIO.OpenInput (InFile, Source);
IF NOT TextIO.Done () THEN
UserMessage (5);
HALT
END;
Dest := Source; Strings.Append (Dest, '.lst');
TextIO.OpenOutput (LstFile, Dest);
IF NOT TextIO.Done () THEN
UserMessage (7);
HALT
END;
TextIO.PutString (LstFile, "Disassembly listing");
TextIO.PutLn (LstFile); TextIO.PutLn (LstFile);
TextIO.PutString (LstFile, "Source : "); TextIO.PutString (LstFile, Source);
TextIO.PutString (LstFile, " disassembling for target processor ");
TextIO.PutString (LstFile, Target);
TextIO.PutLn (LstFile); TextIO.PutLn (LstFile);
TextIO.PutString (LstFile, " address opcode ASCII mnemonics");
TextIO.PutLn (LstFile);
TextIO.PutLn (LstFile)
END OpenFiles;
PROCEDURE CloseFiles;
BEGIN
TextIO.Close (InFile);
TextIO.Close (LstFile);
KillPool (PortNames);
KillPool (BitNames)
END CloseFiles;
PROCEDURE Init;
VAR count, i : SHORTCARD;
BEGIN
NewPool (PortNames);
NewPool (BitNames);
PoolAllocate (PortNames, FirstPort, SYSTEM.TSIZE (PortNode));
WITH FirstPort^ DO
Name := "|";
number := 5000;
next := NIL
END;
PoolAllocate (BitNames, FirstName, SYSTEM.TSIZE (NameNode));
WITH FirstName^ DO
RegName := "|";
BitNr := 50000;
BitName := "|";
next := NIL
END;
line := 0; column := 1; PC := 0;
RAMstart := 96;
Strings.EmptyString (Source); Target := "ATmega8515";
Arguments.GetArgs (count, storage);
IF count = 1 THEN
UserMessage (2);
UserMessage (1);
HALT
END;
i := 1;
LOOP
IF i >= count THEN EXIT END;
Strings.Assign (option, storage^ [i]^);
IF Strings.StrEq (option, '-t') THEN
INC (i);
IF i = count THEN
UserMessage (11); InOut.WriteLn;
UserMessage (1); UserMessage (2);
HALT
END;
Strings.Assign (Target, storage^ [i]^)
ELSIF Strings.StrEq (option, '-v') THEN
UserMessage (2)
ELSE
Strings.Assign (Source, option)
END;
INC (i)
END;
IF Source [0] = 0C THEN
UserMessage (6);
HALT
END;
DecodeNext := TRUE
END Init;
BEGIN
Init;
IF Configure () = FALSE THEN
UserMessage (4);
HALT
END;
UserMessage (2);
OpenFiles;
ProcessLine;
CloseFiles
END dis04.
Compiling:
jan@beryllium:~/modula/AVR$ mocka
Mocka 0608m
>> i dis04
>> p dis04
.. Compiling Program Module dis04 I/0013 II/0013
.. Linking dis04
>> q
jan@beryllium:~/modula/AVR$
You can download the executable and the support files in the download section.
You need to be a follower of the Penguin to be able to use AVRdis. If you still prefer to follow the richest man in the world (self acclaimed) you will probably still be using TOS (The Other System) and you have bad luck. Sorry: if you are serious with the AVR you need to be a Follower of the Penguin.
An example
Here follows an example disassembly of a rather big file I found on the internet (source and hex files so I can compare). The complete file is too big to show here but I took out some highlights, showing what avrdis already can do. The ASCII dump makes it easy to spot data area's. An example is in the bottom section of the list. Also pay attention to the smart port name and bit position scanner.
Disassembly listing
Source : Hebb disassembling for target processor ATmega8515
address opcode ASCII mnemonics
0 C00C .. rjmp $D
1 9518 .. reti
2 9518 .. reti
C 9518 .. reti
D 94F8 .. cli
E E6E0 .. ldi R30, $60
F E0F0 .. ldi R31, $0
10 E6C0 .. ldi R28, $60
11 E0D2 .. ldi R29, $2
12 2700 .' clr R16
13 9301 .. st Z+, R16
14 17EC .. cp R30, R28
15 F7E9 .. brne $13
16 17FD .. cp R31, R29
17 F7D9 .. brne $13
18 E50F .. ldi R16, $5F
19 E012 .. ldi R17, $2
1A BF0D .. out SPL, R16
1B BF1E .. out SPH, R17
1C 2700 .' clr R16
1D BB0B .. out PORTA, R16
1E BB0A .. out DDRA, R16
1F BB08 .. out PORTB, R16
20 EF0F .. ser R16
2B E800 .. ldi R16, $80
2C 2E20 . mov R2, R16
2D 2E30 0. mov R3, R16
2E E001 .. ldi R16, $1
2F E010 .. ldi R17, $0
30 D52C ,. rcall $55D
31 2F62 b/ mov R22, R18
32 706F op andi R22, $0F
33 2F72 r/ mov R23, R18
34 9572 r. swap R23
475 9120 . lds R18, [$F3]
476 F3 ..
477 9523 #. inc R18
478 7021 !p andi R18, $01
479 9320 . sts [$F3], R18
47A F3 ..
47B E003 .. ldi R16, $3
47C E010 .. ldi R17, $0
47D D0E6 .. rcall $564
47E CC00 .. rjmp $7F
5F3 B61F .. in R1, SREG
5F4 B19C .. in R25, UDR
5F5 995B [. sbic UCSRA, DOR
5F6 C012 .. rjmp $609
5F7 995C \. sbic UCSRA, FE
5F8 C012 .. rjmp $60B
9E9 A0D Cr/Lf sbc R0, R29
9EA 6E6F on ori R22, $EF
9EB 7420 t andi R18, $40
9EC 6568 he ori R22, $58
9ED 7420 t andi R18, $40
9EE 7265 er andi R22, $25
9EF 696D mi ori R22, $9D
9F0 616E na ori R22, $1E
9F1 206C l and R6, R12
9F2 6E61 an ori R22, $E1
9F3 2064 d and R6, R4
9F4 7270 pr andi R23, $20
9F5 7365 es andi R22, $35
9F6 2073 s and R7, R3
9F7 4E45 EN sbci R20, 229
9F8 4554 TE sbci R21, 84
9F9 2052 R and R5, R2
Manual and requisites
This version of AVRdis (which is named 'dis04' after it has been compiled) is a development version, but it will do the job none the less. If you get the tar.gz file, commence as follows:
AVRdis will now do it's best to take apart the opcodes and make a nice workingfile for you which can be used to reconstruct the lost source file. The '.lst' file contains all data plus some extra, to make life easier on the disassembling human:
Page created on 20 September 2006 and
Page equipped with GoogleBuster technology