dasmfw / the DisASseMbler FrameWork

Copyright (c) 2015-2021 Hermann Seib
Parts Copyright (c) 2000 Arto Salmi
Parts Copyright (c) 2013 Colin Bourassa
Parts Copyright (c) 2014-2015 Rainer Buchty

dasmfw is a disassembler for a variety of processors. At the time of writing, these are:

dasmfw is the successor to f9dasm. It was created when I planned to add an AVR8 disassembler, for the following reasons:

So I did a complete redesign - dasmfw was born. A framework that makes it relatively easy to "plug in" new disassemblers, regardless of the architecture involved.

Hermann Seib, 2021

Syntax

dasmfw [-option]* [filename]*

Options and filenames can be freely mixed; the offset, begin, end, interleave, and bus options always affect the following files. This makes it possible to load multiple interleaved EPROM images, for example.

Options normally take a parameter, unless specified otherwise. The option/parameter pair can be specified in a variety of ways:

If an option expects a numeric parameter, it can be given as a hexadecimal value with a leading 0x. Values without 0x are interpreted to have the base set with the pbase option (see below). Various disassemblers add their own interpretative capabilities to that; for example, a value with a leading $ is interpreted as hexadecimal in 6800-based disassemblers.

Input file name

dasmfw accepts input files in a variety of formats; some of them are disassembler-specific (FLEX binary files are only meaningful for 6800- or 6809-derived disassemblers, for example), some are generally usable: binary files, Intel hex format and Motorola S09 format.

For binary files, an interleave factor (see -interleave option below) can be directly specified by adding ":" plus the factor to the filename.

Disassembler Selection Option

dasm code
This is the one option that must be given: which disassembler should be used? It can be specified in one of the following ways:

General Command Line Options

?, help [option|info]
This option can be used to display help on the available options or info file instructions (more on that later).
out filename
normally, dasmfw streams to standard output; this option forces it to write to the specified file instead.
If you're feeling particularly funny, you can specify -out:console, which has the same effect as not bothering to specify an output file at all.
info filename
filename gives an information file which contains additional hints for the disassembler. See the Info File section below.
Passing the file name help displays help for the information file.

Output Formatting Options

logo on|off
nologo
Normally, dasmfw proudly announces its name, the used disassembler and the loaded files both on the console and in the output file, if used; by specifying -nologo, this can be disabled for cleaner output files.
addr on|off
noaddr
if disabled, suppresses the address field output for clean assembler source files (default is enabled)
hex on|off
nohex
disables or enables hex dump output (default is enabled).
While analyzing a file, the hex dump can be quite helpful; if you want to generate a clean assembler source file, you can disable it.
asc on|off
noasc
can be used to enable or disable output of the ASCII equivalent to code/data.
Default is to output ASCII.
comment on|off
nocomment
can be used to enable or disable the output of comments (which can be provided in an info file, see below).
Normally, comments are enabled. And for a good reason - disabling comments also disables other texts added to the output, like conditional assembly pseudo-ops and the like.
cref on|off
nocref
if set to on, dasmfw adds a cross-reference list to all used labels in the output file.
unused on|off
nounused
if set to on, dasmfw outputs unused labels set in info files, too. Normally, only labels that are referenced somewhere are added to the output.
labellen num
can be used to reserve space in this length for code labels. By default, dasmfw reserves 8 places for labels; that's a fairly standard amount. If you plan on using labels with a length above 7, you might consider reserving more space for a nice output.
1 is minimum, 255 is maximum.
eqlbllen num
can be used to reserve space in this length for EQU labels. By default, dasmfw reserves 8 places for EQU labels; that's a fairly standard amount. If you plan on using EQU labels with a length above 7, you might consider reserving more space for a nice output.
1 is minimum, 255 is maximum.
opcodelen num
can be used to reserve space in this length for the opcode part of a disassembled instruction. By default, dasmfw reserves 8 places for opcodes; that's a fairly standard amount. Depending on your taste, you can reserve more or less.
1 is minimum, 255 is maximum.
copndlen num
can be used to reserve space in this length for the operand part of a disassembled instruction with a line comment. Default is 24; depending on your taste, you can reserve more or less.
1 is minimum, 255 is maximum.
uopndlen num
can be used to reserve space in this length for the operand part of a disassembled instruction without a line comment. Default is 52; depending on your taste, you can reserve more or less.
1 is minimum, 255 is maximum.
dbcount num
can be used to reserve space for hex/ascii dump bytes if the hex or asc options are enabled; this shifts all line comments that many places to the right for a nice layout. Default is 5; depending on your taste, you can reserve more or less.
1 is minimum, 255 is maximum.

Disassembler Options

Most of the following options are universally usable; some, however, are disassembler-specific. These are the universal options:

offset address
When disassembling a binary file, the default load address is 0, since the binary file does not contain any clues; using this option forces dasmfw to load the file at the specified address.
begin address
start disassembly address (address has to be given in hex format)
Default is the first referenced address in the file.
end address
end disassembly address (address has to be given in hex format)
Normally, this is defined either through the file size or its contents, if it has an embedded END addres. This option allows to override the implicit end address.
interleave factor
When disassembling 68000 EPROMs, for example, it is quite likely that there are separate EPROMs for the odd and even addresses; dasmfw allows to load them in an interleaved way. interleave 1 (default) means consecutive addresses; by specifying an interleave factor of 2 or above (dasmfw accepts up to 8), the bytes loaded from the binary are placed that many bytes apart in internal memory.
This can also be specified as part of the input file name (see above).
bus busname
can be used to specify that the following file is to be loaded into a bus-specific area. At the moment, this is only meaningful for the AVR8 disassembler, as AVR8-based microcontrollers are built around a Harvard architecture with separate code, data, I/O, and EEPROM buses.
pbase number
can be used to specify the default base for parsing numbers.
Default is 16.
defdisp bin|char|oct|dec|hex
can be used to specify the default number output format.
Default is hex.
cchar chr
can be used to set up the comment delimiter character
Default is ;
ldchar chr
can be used to set up the label delimiter character. This character is added after labels that get their own line if more than one label is defined for the same address. Requires multilabel on.
Default is :
loadlabel on|off
noloadlabel
flag whether an entry point label (if defined by the input file) is used.
Default is on.
multilabel on|off
nomultilabel
can be used to decide whether multiple labels can be set for a single address.
Default is off.
autolabel on|off
noautolabel
Normally, dasmfw generates label names based on the type (code/data) and the address if no text label is given for the address in an info file. With autolabel on, the last text label plus the current label's offset to that is used to generate the name. This can be convenient if all subroutine entry points have already been determined to automatically create meaningful labels inside the subroutines.
Default is off.
sysvec on|off
nosysvec
If the current processor has a system vector table at a given address and the input file(s) contain the table area, dasmfw can automatically format the system vector table and assign labels to the referenced addresses.
Default is on.

The following options are disassembler-specific; some are only available for specific disassemblers. If in doubt, invoking dasmfw with the command line dasmfw -dasm=code -? can be used to find out whether a specific option is available for the used disassember, and which parameters can be given.

conv on|off
noconv
Quite some assemblers know "convenience mnemonics"; these are evaluated to a combination of "real" processor instructions. dasmfw can be instructed to detect these instruction combinations and to convert them back to the original convenience mnemonics. Not all possible convenience mnemonics are supported; some have a different implementation in various assemblers, so re-assembling dasmfw's output might lead to a binary that's no longer identical to the original. These convenience mnemonics are not used.
Default is on.
showzero on|off
noshowzero
For 6800-based disassemblers (including the Hitachi range)
Normally, indexed-mode operands with value 0 are not output (like, for example, in "LDA ,X"). Using this option forces their output (like, in the given example example, "LDA $00,X").
Default is off.
closecc on|off
noclosecc
In many assemblers, a closing delimiter for a single character is not necessary; dasmfw normally uses the most appropriate way of handling character constants. This setting can be used if the default does not provide the required output.
Default is processor-dependent.
fcc on|off
nofcc
Many assemblers provide more than one way to fill an area with single byte characters; examples for 6800-based processors mostly provide FCB ("fill constant byte") and FCC ("fill constant character") mnemonics for that, where FCB can be used to define (a range of) single bytes and FCC can be also used to define strings. Where possible, dasmfw tries to represent arrays of printable characters as strings (i.e., with fcc on, which is more space-efficient and readable. This setting can be used to turn it off for the complete disassembly. For individual areas, use of the const statement in an info file (see below) may be the better option.
Default is normally on.
dplabel on|off
nodplabel
Various processors know the concept of a direct page which allows for shorter instructions; if the referenced address is in the direct page area, it can be given as a single byte. dasmfw can be instructed to interpret single-byte data as direct-page addresses by default; using a const info file statement can be used for specific addresses where this is definitely wrong.
Since this is a bit hairy, default is off.
forceaddr on|off
If the targeted processor (and assembler, if available) can handle forced direct / extended addressing, dasmfw tries its best to determine whether adding the required < and > marks is necessary; in very rare occasions, this may fail. If you build the "RB" variant, it isn't done by default; normally, it is. This option can be used to change the behavior.
undef on|off
noundef
Some processors (currently 6501 and 6502-based) support additional, but pretty well understood officially undefined opcodes. These can be made available; however, since using these instructions can lead to quite problematic program behavior, it's normally turned off.
flex on|off
noflex
For 6809-based disassemblers only.
If the file in question is known to be a binary for the FLEX9 operating system, dasmfw can automatically supply labels for many of the known available FLEX9 entry points.
Default is off.
os9 on|off
noos9
For 6809-based disassemblers only.
If the file in question is known to be a binary for the OS9 operating system, dasmfw can automatically patch SWI2 instructions to the corresponding OS9 system call.
Default is off, unless an OS9 module has been loaded.
os9c on|off
noos9c
For 6809-based disassemblers only.
If the file in question is known to be a binary for the OS9 operating system, dasmfw can automatically add header and checksum comments to the module.
Default is off.
os9m on|off
noos9c
For 6809-based disassemblers only.
If the file in question is known to be a binary for the OS9 operating system, dasmfw can automatically add reformatting instructions to the generated assembler source for the OS9 assembler that replace the header and the checksum with the appropriate macros.
Default is off.
asm assembler
For some processors (currently, 68000-based only), dasmfw can generate output that's best suited for a specific assembler. Normally, a "works on most" route is taken.
Default depends on the disassembler.
codebits num
AVR8 only
Here, the number of bits usaed for code addresses can be given in range 8..32.
Default is 22.
highcode addr
AVR8 only
Here, the highest code address can be given in range 512..4194303 (0x3fffff)
Default is 0x3fffff.
highdata addr
AVR8 only
Here, the highest data address can be given in range 128..65535 (0xffff)
Default is 0xffff.
higheeprom addr
AVR8 only
Here, the highest EEPROM address can be given in range 128..65535 (0xffff)
Default is 0xffff.
dbalign on|off
AVR8 only
With this option, dasmfw tries its best to align .db to word boundaries.
Default is on.
avr-gcc on|off
AVR8 only
With this option, dasmfw creates avr-gcc compatible output in .sx format.
Default is off.

Info File

Using the -info filename option, you can give dasmfw additional information about the layout of the processed file.

Normally, dasmfw will try to interpret everything as code; it doesn't try to find out which areas contain code and which areas contain data, or the format of the data. Using an info file, you can give it detailed instructions how to process the file, add comments, insert additional stuff, and so on.

The info file is a simple text file, which contains one instruction per line.
The instructions are case-insensitive.
Addresses need to be given in hexadecimal notation. Anything following an asterisk (*) is interpreted as a comment.

dasmfw looks for default information files in the following positions:

  1. in a subdirectory .dasmfw of the current user's home directory
  2. in the current directory

In both cases, the current disassembler's name + .nfo is used; normally, that would be dasmfw.nfo, but if you renamed or symlinked it to dasm6809, for example, it would be dasm6809.nfo.

This can be used to set up global or project-specific options.

Info File Instructions

Each instruction line has the following syntax:

inst [bus tgtbus] [range] [value] [* comment]

inst is one of the instructions described below.
tgtbus is one of the busses defined for the current disassembler; for the AVR8, this can be code, data, io, or eeprom; it can also be specified numerically as 0..3. For all others, only the default code (or 0) is defined. This defines the target bus for the instruction, if it should differ from the currently selected bus (see the bus instruction below). Not all disassemblers need the target bus definition (in fact, currently the AVR8 disassembler is the only one that does use it).
range consists of up to 3 parts: from, to, step, in the format

from[-to[/step]]

where not all instructions need each part. The step size, if needed and not given, defaults to 1.

Note: wherever text requires leading blanks or tabs, it isn't possible to simply put them into the instruction, as dasmfw would discard them. For these cases, simply prepend a '\' (backslash) to the text, like, for example, in
insert 0 \        OPT H63
so that dasmfw knows where text really starts. Despite the similarity, dasmfw doesn't know the range of "C" escape characters; a '\' simply means "Ignore this backslash, but make sure the next character is part of the text". If you need to have a * as part of the text, it is mandatory to write it as \*; dasmfw would otherwise assume the line ends at the *, which starts the comment part.

Global Info File Instructions

bus infobus
infobus defines the bus to be used for all following instructions.
Only meaningful for the AVR8 disassembler at the moment, where it be code, data, io, or eeprom; it can also be specified numerically as 0..3. All others deal with a plain, simple von Neumann architecture with exactly one bus, which doesn't necessitate separate sections in the loaded binary.
include filename
includes the given info file.
file filename [baseaddr]
This instructs dasmfw to load the given file at the given address.
Can be used instead of the command line parameter for the file name; this can, for example, be useful if you want to generate a listing for a bunch of small EPROMs that cover a continuous memory area.
option option [value]
option is one of the options listed above, just without the leading hyphen (-).
code addr[-addr[/step]]
defines the given address (range) as containing code.
A step size can be given, but... honestly, I can't dream up a situation where that might make sense. That goes for a lot of the instructions below, too :-)
data addr[-addr[/step]]
defines the given address (range) as containing data instead of code.
dasmfw will try to decipher ASCII strings in the area and display them in the best possible format.
bin[ary] addr[-addr[/step]]
char[acter] addr[-addr[/step]]
oct[al] addr[-addr[/step]]
dec[imal] addr[-addr[/step]]
hex[adecimal] addr[-addr[/step]]
sedecimal addr[-addr[/step]] (for purists ;-)
defines the output format used for the given data range.
This can also be used for constants in the code area; if, for example, dasmfw outputs the following line of code:
        LDA     #$D6                     *C115: 86 D6
and you know pretty well that this is a binary bit mask, you can force it to display the data in a nicer format by giving the instruction bin c116 (note that the address of the constant byte is given, not the address of the instruction!). This results in the modified output
        LDA     #%11010110               *C115: 86 D6
which may be easier to read (depending on your mental approach to assembler programming :-).
Note that char and bin can not be used for (d)word areas (see below).
signed addr[-addr[/step]]
unsigned addr[-addr[/step]]
float addr[-addr[/step]]
double addr[-addr[/step]]
tenbytes addr[-addr[/step]]
defines the type of a given memory location. signed and unsigned define integral types, float, double, and tenbytes define floating-point values of sizes 4, 8, or 10, respectively (always signed).
byte addr[-addr[/step]]
defines that the area consists of bytes (i.e., 1-byte entities).
word addr[-addr[/step]]
defines that the area consists of words (i.e., 2-byte entities, byte order defined by the processor architecture) instead of single bytes.
dword addr[-addr[/step]]
defines that the area consists of double words (i.e., 4-byte entities, byte order defined by the processor architecture) instead of single bytes.
Only useful for processors that can handle this.
const addr[-addr[/step]]
defines the data in the given range as constants.
Normally, if dasmfw can interpret the data as addresses, it will; and if there's a label defined for this address, it will display the label instead of the original value.
For a range of characters, const is used to instruct dasmfw to treat them as separate entities and display their hex value instead of displaying them as characters or parts of a string.
break addr[-addr[/step]]
Normally, when disassembling data areas, dasmfw puts as much data into a line as possible (governed by the copndlen and uopndlen options), provided they have the same type and there's no label that needs to be used. Since this doesn't necessarily reflect the data organization (for example, an array might consist of triplets), break can be used to insert a line break before the given address (range).
unbreak addr[-addr[/step]]
Removes breaks from the given address (range).
unused addr[-addr[/step]]
defines the given address range as unused.
This can be useful if an area in the loaded file is known to be empty; there's no need to put it into the generated assembler source.
force[used] addr[-addr[/step]]
defines the given address range as used.
rmb addr[-addr[/step]]
bss addr[-addr[/step]]
defines the given address range as reserved, but not initialized to defined values.
forceaddr addr[-addr[/step]]
If the targeted processor (and assembler, if available) can handle forced direct / extended addressing, dasmfw tries its best to determine whether adding the required < and > marks is necessary; in very rare occasions, this may fail (or, if you build the "RB" variant, it isn't done anyway). In such a case, this instruction can be used to force the mark.
unforceaddr addr[-addr[/step]]
Removes the forceaddr (see above) flag for the given range.
label addr[-addr] name
sets a label at the given address.
Note that dasmfw doesn't restrict the length of the label, nor does it enforce a given range of characters (except for * and zero bytes - these terminate the name). This may conflict with the assembler of your choice, so choose the labels with caution.
If a range is given, dasmfw automatically appends "+1","+2",… for the successive positions.
used[label] addr [name]
forces the given address used.
Normally, dasmfw would only emit a label definition in the form of an EQU statement if the label is really used in the code.
unlabel addr[-addr]
removes defined labels in the given address range.
This is mainly useful if you use a set of info files (see the include instruction below) and want to remove label definitions from an earlier info file.
deflabel addr [name]
can be used to create definition labels. deflabel uses the value at the referenced address and creates a definition label for it, which is then used instead of the value. For example (6800 instruction set), the code 86 24 at address $008d would be disassembled to LDA #24; by adding the instruction deflabel 008e NiceVal (note that the deflabel has to be set for the data byte, not for the start of the instruction!), the instruction would be disassembled as LDA #NiceVal and a NiceVal EQU $24 statement would be added at the top.
A little bit of caution is necessary here; if the same deflabel is used for different values, the last one wins and is used for the EQU statement.
insert [after] [addr[-addr[/step]]] text
This instruction adds the given text to the text lines which are displayed before a given (range of) address(es).
In contrast to a comment, there's no comment character written before the text, which allows to insert any assembler statement or pseudo-op.
Normally, the line is inserted before the disassembled line(s); if after is specified, it is inserted after the disassembled line(s).
comment [after] [addr[-addr[/step]]] text
appends a comment to the lines displayed before a given address (range).
Normally, the comment is inserted before the disassembled line(s); if after is specified, it is inserted after the disassembled line(s).
lcomment addr[-addr[/step]] text
appends a line comment to the lines displayed before a given address (range).
A line comment is displayed as a comment to the right of the instruction.
If more than one line comment is given, they are displayed on separate lines, but all to the right of the instruction.
prepend [after] [addr[-addr[/step]]] text
This instruction prepends the given text to the text lines which are displayed before (or after) a given (range of) address(es).
This is mainly useful if you use a set of info files (see the include instruction below) and want to add additional text from a later info file before text lines from an earlier info file.
prepcomm [after] [addr[-addr[/step]]] text
Same as prepend (see above), but it prepends a comment instead of a normal text line.
preplcom[ment] addr[-addr[/step]] text
prepends a line comment to the lines displayed before a given address (range).
uncomment [after] addr[-addr[/step]]
removes insert and comment lines from the given address range.
This is mainly useful if you use a set of info files (see the include instruction below) and want to remove comments from an earlier info file so that you can replace them.
unlcomment addr[-addr[/step]]
removes line comments from the given address range.
This is mainly useful if you use a set of info files (see the include instruction below) and want to remove line comments from an earlier info file.
rel[ative] addr[-addr] baseaddr
This can be used to make instructions with indexed addressing easier to read.
unrel[ative] addr[-addr[/step]]
cancels the effect of relative instructions.
remap addr[-addr[/step]] offset
This is a tricky instruction that only makes sense in very special situations; imagine, for example, that you already have an elaborate info file for a specific EPROM - and then you get another EPROM that contains nearly the same stuff, just with one or two instructions added or missing.
You could, of course, adapt all instructions in your info file to the new address layout.
Would you? I wouldn't.
In this case, it's easier to prepend a remap instruction that tells dasmfw to "shift" the following addresses in the info file some bytes. Remaps are cumulative; that is, if you define a remapped region and then define another remap inside that region, the second remap is added to the first one for the given range.
unremap addr[-addr[/step]]
This one removes all remaps for the given range. In contrast to remap, it is not cumulative; all remaps for the given region are removed.
phase addr[-addr[/step]] [+|-]phase
Sometimes, an EPROM can contain data that are mapped to a different location; in this case, all jump targets etc. inside that area are incorrect, i.e., "out of phase". The phase instruction tells dasmfw to disassemble that area as if it was starting at the address given in phase.
Using this instruction embeds PHASE/DEPHASE pseudo-ops in the generated source code, so an Assembler that can process these pseudo-ops is required (the mighty AS can do it, and my A09 can do it since V1.30, too).
This deals with one part of the problem - data inside phased areas are disassembled correctly. But to reference them correctly from outside the phased area requires additional, relative phase statements.
Relative phase statements are made by adding a + or - before phase (which in this case is a relative value).
Attention: a relative phase statement can only be set inside a phased area; so, if relative phasing is required, all areas in the disassembled module that use it have to be put into a phased area, even the "un-phased" ones, like:
phase 8000-ffff 8000
and relative phases have to be defined after the phase area definition.
If your Assembler of choice can't handle phasing, all you can do is to cut the binary in slices, use the -offset command line option to disassemble them, and merge the generated assembler source files by hand.
cvec[tor] addr[-addr[/step]]
defines a code vector area (a table of code addresses).
Works like word addr[-addr], but also defines code labels for the addresses if necessary
dvec[tor] addr[-addr[/step]]
defines a data vector area (a table of data addresses).
Works like word addr[-addr], but also defines data labels for the addresses if necessary
patch addr[-addr[/step]] hex [hex]*
Can be used to patch bytes in a loaded file. Using this feature, modified versions of a file can be created automatically.
A09 can be used to emit patch instructions instead of listings to make it easier to develop modified versions of a program.
patchw addr[-addr[/step]] word [word]*
Can be used to patch words (i.e., 2-byte entities in the byte order defined by the processor architecture) in a loaded file.
patchd addr[-addr[/step]] dword [dword]*
Can be used to patch double words (i.e., 4-byte entities in the byte order defined by the processor architecture) in a loaded file.
Patching in unused areas causes them to become used.
end
Terminates processing of this info file at this line.

Disassembler-specific Info File Instructions

Some of the disassemblers add their own instructions.
setdp [addr[-addr[/step]]] dp-content
6809 and derived only
uses the specified direct page (if the specified processor can do that - 6800 and its derivates implicitly uses Direct Page 0).
To allow usage with programs that repeatedly change the direct page, an address range can be given.
If only the start address is given, the range is assumed to start there and go up to the end of the address range.
If no address is given, the global direct page is changed.
Any value between 00 and FF sets the direct page (0 is the default); values outside this range disable direct page processing by forcing dasmfw to emit a
        SETDP
line which 6809 assemblers normally interpret as "don't use direct page addressing".
Only the last global setdp statement is used.
unsetdp [addr[-addr[/step]]]
6809 and derived only
removes setdp effects from the given range or, if no range is given, sets the global direct page back to the default.

The Atmel AVR8 codeset and the underlying philosophy has some peculiarities. One of these is that addresses of data have to be loaded into registers in two steps: one for the high byte of the address, one for the low byte. The various high and low statements described below reflect that fact.

reglabel addr Rxx [text]
AVR8 only
Due to the abundance of registers, AVR8 code tends to keep many local variables in registers. reglabel allows to set a label to be used instead of the register name starting at the given address.
unreglabel addr Rxx
AVR8 only
Cancels the previous reglabel definition for the passed register.
high [bus tgtbus] addr[-addr] reladdr
low [bus tgtbus] addr[-addr] reladdr
AVR8 only
These statements innstruct dasmfw to treat the value in the instruction as the low or high part of the address given in reladdr. As an example, if you come across a code passage like
        ldi     R26,0x00                ; 000634: A0 E0          '..'
        ldi     R27,0x01                ; 000636: B1 E0
and you know that R26:27 is a pointer to a data item, it's easy to determine that this sets up the pointer to 0x0100. In this case, adding the statements
low  bus data 0634 0100
high bus data 0636 0100
would lead to the following, much more readable output:
        ldi     R26,LOW(MD0100)
        ldi     R27,HIGH(MD0100)
mhigh [bus tgtbus] addr[-addr] reladdr
mlow [bus tgtbus] addr[-addr] reladdr
AVR8 only
avr-gcc has a tendency to produce code like the following:
        subi    R30,0xae                ; 001764: EE 5A
        sbci    R31,0xfe                ; 001766: FE 4F
That means: it subtracts the negated address parts from the registers. Why ... I don't know. Someone decided that optimizing code this way was a good idea. Now, this would require the use of a calculator (or mental artistry for hex wizards) to find out what address is hidden behind the constant 0xfeae that's subtracted from the pointer. dasmfw makes that a bit easier; adding the statements
mlow  bus data 1764 feae
mhigh bus data 1766 feae
leads to the much more pleasing output
        subi    R30,LOW(-MD0152)
        sbci    R31,HIGH(-MD0152)
high2 [bus tgtbus] addr[-addr] reladdr
low2 [bus tgtbus] addr[-addr] reladdr
AVR8 only
Works like low/high, but for pointers into the code area - the AVR8 instruction set addresses the code area in words, so pointers have to be set to half the real value. So, if you come across something like that:
        ldi     R16,0xaf                ; 0006D0: 0F EA
        ldi     R17,0x03                ; 0006D2: 13 E0
and you know that R16:17 is used as a pointer into the code area, adding the statements
low2  06d0 03af
high2 06d2 03af
would lead to the output
        ldi     R16,LOW(Z00075E>>1)
        ldi     R17,HIGH(Z00075E>>1)
which shows the real address pointed to.