Source file: /~heha/hs/bl/avr-dasm.zip/main.cpp

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <malloc.h>
#include <windows.h>

typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned __int32 dword;

/************************************************
 * opcode disassembly table			*
 * each entry is a 16-bit mask, a 16-bit value,	*
 * and a tokenized zero-terminated string	*
 ************************************************/

// Bit 7 set indicates a tab (first occurence) or comma (subsequent occurences).
// The table ends with a mask of zero.
// Special processing is done where zero mask-bits correspond with set value bits.
// Processing of table is top-down, so specialized instructions (with more mask bits set)
// have to precede general instructions (less mask bits set but same value)
// Tokens 0x1E, 0x1F, and hence 0x9E, 0x9F indicate 32-bit instruction length.
// Description of tokens:
// \x01 = I/O dataaddr (0..0x1F) in bits [7:3] (sbi,cbi,sbic,cpic)
// \x02 = I/O dataaddr (0..0x3F) in bits [10:9][3:0] (in,out)
// \x08 = K (0..63) in bits [8:7][3:0] (adiw)
// \x09 = K (0..255) in bits [11:8][3:0] (can be data or code offset, hi or lo part)
// \x0A = K (0..7) in bits [2:0]
// \x0B = jump offset (-64..63) in bits [9:3]
// \x0D = q (0..63) in bits [13][11:10][2:0] (ldd)
// \x0E = K (0..15) in bits [7:4] (des (very rare opcode!) only)
// \x10 = register R16..R23 in bits [6:4] (fmul)
// \x11 = register R16..R23 in bits [2:0] (fmul)
// \x12 = register R16..R31 in bits [3:0] (muls)
// \x13 = register R16..R31 in [7:4] (muls, ldi etc.)
// \x14 = register R0..R31 in bits [9][3:0] (many opcodes)
// \x15 = register R0..R31 in bits [8:4] (many opcodes)
// \x17 = register pair W24..W30 in bits [5:4] (adiw, sbiw)
// \x18 = register pair W0..W30 in bits [7:4] (movw)
// \x19 = register pair W0..W30 in bits [3:0] (movw)
// \x1C = codeaddr (-4K..<4K) in bits [11:0] (rcall)
// \x1D = dataaddr (0x40..0xBF) in bits [/8][8][10:9][3:0]
// \x1E = dataaddr (0..<64K) in bits [extraword]
// \x1F = codeaddr (0..<8M) in bits [8:4][0][extraword]
// Bit 7 = Tab (erstes Vorkommen), Komma (weitere Vorkommen)

const char opcodes[]=
 "\xFF\xFF\x00\x00""nop\0"
 "\xFF\xFF\x08\x95""Ret\0"		//		second capital letter = hint: possibly switch to data
 "\xFF\xFF\x09\x95""icall\0"
 "\xFF\xFF\x18\x95""Reti\0"
 "\xFF\xFF\x19\x95""eicall\0"
 "\xFF\xFF\x88\x95""sleep\0"
 "\xFF\xFF\x98\x95""break\0"
 "\xFF\xFF\xA8\x95""wdr\0"
 "\xFF\xFF\xC8\x95""lpm\0"		// lpm		same function as lpm R0,Z (= wasted code space)
 "\xFF\xFF\xD8\x95""elpm\0"
 "\xFF\xFF\xE8\x95""spm\0"
 "\xFF\xFF\xF8\x95""spm\xDA+\0"		// spm Z+

 "\xFF\xFF\x08\x94""sec\0"
 "\xFF\xFF\x18\x94""sez\0"
 "\xFF\xFF\x28\x94""sen\0"
 "\xFF\xFF\x38\x94""sev\0"
 "\xFF\xFF\x48\x94""ses\0"
 "\xFF\xFF\x58\x94""seh\0"
 "\xFF\xFF\x68\x94""set\0"
 "\xFF\xFF\x78\x94""sei\0"
 "\xFF\xFF\x88\x94""clc\0"
 "\xFF\xFF\x98\x94""clz\0"
 "\xFF\xFF\xA8\x94""cln\0"
 "\xFF\xFF\xB8\x94""clv\0"
 "\xFF\xFF\xC8\x94""cls\0"
 "\xFF\xFF\xD8\x94""clh\0"
 "\xFF\xFF\xE8\x94""clt\0"
 "\xFF\xFF\xF8\x94""cli\0"

 "\xFF\xFF\x09\x94""Ijmp\0"
 "\xFF\xFF\x19\x94""Eijmp\0"

 "\x00\xFC\x00\x04""cpc\x95\x94\0"
 "\x00\xFC\x00\x08""sbc\x95\x94\0"
 "\x00\xFC\x1F\x0C""lsl\x95\0"		//		compare bits set for mask bits clear: Cannot match directly, special routine
 "\x00\xFC\x00\x0C""add\x95\x94\0"	// add Rd,Rs	= lsl Rd for d==s
 "\x00\xFC\x00\x10""cPse\x95\x94\0"	// capital letter = possibly skip next instruction
 "\x00\xFC\x00\x14""cp\x95\x94\0"
 "\x00\xFC\x00\x18""sub\x95\x94\0"
 "\x00\xFC\x1F\x1C""rol\x95\0"
 "\x00\xFC\x00\x1C""adc\x95\x94\0"	// adc Rd,Rs	= rol Rd for d==s
 "\x00\xFC\x1F\x20""tst\x95\0"
 "\x00\xFC\x00\x20""and\x95\x94\0"
 "\x00\xFC\x1F\x24""clr\x95\0"
 "\x00\xFC\x00\x24""eor\x95\x94\0"
 "\x00\xFC\x00\x28""or\x95\x94\0"
 "\x00\xFC\x00\x2C""mov\x95\x94\0"	// mov Rd,Rs	wasted code space for Rd==Rs (32 NOP functions)
 "\x00\xFC\x00\x9C""mul\x95\x94\0"	// mul Rd,Rs

 "\x00\xF0\x00\x30""cpi\x93\x89\0"
 "\x00\xF0\x00\x40""sbci\x93\x89\0"
 "\x00\xF0\x00\x50""subi\x93\x89\0"
 "\x00\xF0\x00\x60""ori\x93\x89\0"	// ori rd,K	== sbr
 "\x00\xF0\x00\x70""andi\x93\x89\0"	//		= cbr
 "\x00\xF0\x00\xC0""Rjmp\x9C\0"		// rjmp codelabel
 "\x00\xF0\x00\xD0""rcall\x9C\0"	// rcall codelabel
 "\x00\xF0\x00\xE0""ldi\x93\x89\0"	// ldi rd,K	= ser Rd when K==0xFF

 "\x00\xFF\x00\x96""adiw\x97\x88\0"
 "\x0F\xFE\x05\x94""asr\x95\0"
 "\x07\xFC\x00\xF0""brcs\x8B\0"
 "\x07\xFC\x00\xF4""brcc\x8B\0"
 "\x07\xFC\x01\xF0""breq\x8B\0"
 "\x07\xFC\x01\xF4""brne\x8B\0"
 "\x07\xFC\x02\xF0""brmi\x8B\0"
 "\x07\xFC\x02\xF4""brpl\x8B\0"
 "\x07\xFC\x03\xF0""brvs\x8B\0"
 "\x07\xFC\x03\xF4""brvc\x8B\0"
 "\x07\xFC\x04\xF0""brlt\x8B\0"
 "\x07\xFC\x04\xF4""brge\x8B\0"
 "\x07\xFC\x05\xF0""brhs\x8B\0"
 "\x07\xFC\x05\xF4""brhc\x8B\0"
 "\x07\xFC\x06\xF0""brts\x8B\0"
 "\x07\xFC\x06\xF4""brtc\x8B\0"
 "\x07\xFC\x07\xF0""brie\x8B\0"
 "\x07\xFC\x07\xF4""brid\x8B\0"

 "\x08\xFE\x00\xF8""bld\x95\x8A\0"
 "\x08\xFE\x00\xFA""bst\x95\x8A\0"

 "\x0F\xFE\x06\x90""elpm\x95\xDA\0"	// \xDA = ,Z
 "\x0F\xFE\x07\x90""elpm\x95\xDA+\0"	// \xDA+ = ,Z+
 "\x0F\xFE\x0F\x90""pop\x95\0"		// pop Rd
 "\x0F\xFE\x04\x92""xch\xDA\x95\0"	// xch Z,Rd
 "\x0F\xFE\x05\x92""las\xDA\x95\0"
 "\x0F\xFE\x06\x92""lac\xDA\x95\0"
 "\x0F\xFE\x07\x92""lat\xDA\x95\0"
 "\x0F\xFE\x0F\x92""push\x95\0"		// push Rs
 "\x0F\xFE\x00\x94""com\x95\0"
 "\x0F\xFE\x01\x94""neg\x95\0"		// neg Rd
 "\x0F\xFE\x02\x94""swap\x95\0"
 "\x0F\xFE\x03\x94""inc\x95\0"
 "\x0F\xFE\x06\x94""lsr\x95\0"		// lsr Rd
 "\x0F\xFE\x07\x94""ror\x95\0"
 "\x0F\xFE\x0A\x94""dec\x95\0"

 "\x0F\xFF\x0B\x94""des\x8E\0"
 "\x0E\xFE\x0E\x94""call\x9F\0"
 "\x88\xFF\x08\x03""fmul\x90\x91\0"
 "\x88\xFF\x80\x03""fmuls\x90\x91\0"
 "\x88\xFF\x88\x03""fmulsu\x90\x91\0"
 "\x0E\xFE\x0C\x94""Jmp\x9F\0"
 "\x0F\xFE\x0C\x90""ld\x95\xD8\0"	// ld rd,X	\xD8 = ,X
 "\x0F\xFE\x0D\x90""ld\x95\xD8+\0"	// ld rd,X+
 "\x0F\xFE\x0E\x90""ld\x95\xADX\0"	// ld rd,-X	\xAD = ,-
 "\x0F\xFE\x08\x80""ld\x95\xD9\0"	// ld rd,Y	// same as ldd Rd,Y+0
 "\x0F\xFE\x09\x90""ld\x95\xD9+\0"	// ld rd,Y+
 "\x0F\xFE\x0A\x90""ld\x95\xADY\0"	// ld rd,-Y
 "\x08\xD2\x08\x80""ldd\x95\xD9+\x0D\0"	// ldd rd,Y+q
 "\x0F\xFE\x00\x80""ld\x95\xDA\0"	// ld rd,Z	// same as ldd Rd,Z+0
 "\x0F\xFE\x01\x90""ld\x95\xDA+\0"	// ld rd,Z+
 "\x0F\xFE\x02\x90""ld\x95\xADZ\0"	// ld rd,-Z
 "\x08\xD2\x00\x80""ldd\x95\xDA+\x0D\0"	// ldd rd,Z+q
 "\x0F\xFE\x00\x90""lds\x95\x9E\0"	// lds rd,mem
 "\x00\xF8\x00\xA0""lds\x95\x9D\0"	// lds rd,mem	for tiny4/5/9/10/20
 "\x0F\xFE\x04\x90""lpm\x95\xDA\0"	// lpm Rd,Z
 "\x0F\xFE\x05\x90""lpm\x95\xDA+\0"	// lpm Rd,Z+
 "\x00\xFF\x00\x01""movw\x98\x99\0"	// movw Rd+1:Rd,Rs+1:Rs	wasted code space for Rd==Rs (16 NOP functions)
 "\x00\xFF\x00\x02""muls\x93\x92\0"	// muls Rd,Rs
 "\x88\xFF\x00\x03""mulsu\x90\x91\0"	// mulsu Rd,Rs
 "\x00\xF8\x00\xB0""in\x95\x82\0"
 "\x00\xF8\x00\xB8""out\x82\x95\0"	// out A,Rs
 "\x00\xFF\x00\x98""cbi\x81\x8A\0"
 "\x00\xFF\x00\x9A""sbi\x81\x8A\0"
 "\x00\xFF\x00\x99""sBic\x81\x8A\0"
 "\x00\xFF\x00\x9B""sBis\x81\x8A\0"
 "\x00\xFF\x00\x97""sbiw\x97\x88\0"
 "\x08\xFE\x00\xFC""sBrc\x95\x8A\0"
 "\x08\xFE\x00\xFE""sBrs\x95\x8A\0"
 "\x0F\xFE\x0C\x92""st\xD8\x95\0"	// st X,Rs	\xD8 = \tX
 "\x0F\xFE\x0D\x92""st\xD8+\x95\0"	// st X+,Rs
 "\x0F\xFE\x0E\x92""st\x95\xADX\0"	// st -X,Rs	\xAD = \t-
 "\x0F\xFE\x08\x82""st\xD9\x95\0"	// st Y,Rs	// same as std Y+0,Rs
 "\x0F\xFE\x09\x92""st\xD9+\x95\0"	// st Y+,Rs
 "\x0F\xFE\x0A\x92""st\xADY\x95\0"	// st -Y,Rs
 "\x08\xD2\x08\x82""std\xD9+\x0D\x95\0"	// std Y+q,Rs
 "\x0F\xFE\x00\x82""st\xDA\x95\0"	// st Z,Rs	// same as std Z+0,Rs
 "\x0F\xFE\x01\x92""st\xDA+\x95\0"	// st Z+,Rs
 "\x0F\xFE\x02\x92""st\xADZ\x95\0"	// st -Z,Rs
 "\x08\xD2\x00\x82""std\xDA+\x0D\x95\0"	// std Z+q,Rs
 "\x0F\xFE\x00\x92""sts\x9E\x95\0"	// sts mem,Rs
 "\x00\xF8\x00\xA8""sts\x9D\x95\0"	// sts mem,Rs	for tiny4/5/9/10/20
 "\0"
;

/************************************************
 * Symbol table for I/O and memory space,	*
 * for ATmega32U4				*
 ************************************************/

#pragma warning(disable:4200)	// suppress variable length array warning
struct dsymtable{
 const dsymtable*inherit;
 const char symbols[];	// Array mit variabler Länge
};

// I/O space starts at (i.e. is offset by) 0x20 here.
const dsymtable dsym_avr={
 0,
 "\x5D\x00""SPL\1"	// Stack pointer and Status register (common to all AVR)
 "SPH\1"
 "SREG\0"
 "\0"
};

const dsymtable dsym_mega={
 &dsym_avr,
 "\x4C\x00"	// SPI, MCUCR (common to ATmega)
 "SPCR\1"
 "SPSR\1"
 "SPDR\2"
 "ACSR\4"
 "MCUSR\1"
 "MCUCR\2"
 "SPMCSR\4"
 "RAMPZ\0"
 "\0"
};

const dsymtable dsym_enhmega={
 &dsym_mega,
 "\x3E\x00"	// GPIOR, Eeprom, Timer0 (common for newer ATmega)
 "GPIOR0\1"
 "EECR\1"
 "EEDR\1"
 "EEARL\1"
 "EEARH\1"
 "GTCCR\1"
 "TCCR0A\1"
 "TCCR0B\1"
 "TCNT0\1"
 "OCR0A\1"
 "OCR0B\2"
 "GPIOR1\1"
 "GPIOR2\0"
 "\0"
};

const dsymtable dsym_mega32u4={
 &dsym_mega,
 "\x23\x00"	// Ports, Interrupt flags
 "PINB\1"
 "DDRB\1"
 "PORTB\1"
 "PINC\1"
 "DDRC\1"
 "PORTC\1"
 "PIND\1"
 "DDRD\1"
 "PORTD\1"
 "PINE\1"
 "DDRE\1"
 "PORTE\1"
 "PINF\1"
 "DDRF\1"
 "PORTF\4"
 "TIFR0\1"
 "TIFR1\2"
 "TIFR3\1"
 "TIFR4\2"
 "PCIFR\1"
 "EIFR\1"
 "EIMSK\0"
 "\x49\x00"	// PLL
 "PLLCSR\0x08"
 "OCDR\1"
 "PLLFRQ\1"
 "SMCR\0"
 "\x60\x00"	// Timer1, Timer3, ADC, OSCCAL, Watchdog
 "WDTCSR\1"
 "CLKPR\3"
 "PRR0\1"
 "PRR1\1"
 "OSCCAL\1"
 "RCCTRL\1"
 "PCICR\1"
 "EICRA\1"
 "EICRB\1"
 "PCMSK0\3"
 "TIMSK0\1"
 "TIMSK1\2"
 "TIMSK3\1"
 "TIMSK4\6"
 "ADCL\1"
 "ADCH\1"
 "ADCSRA\1"
 "ADCSRB\1"
 "ADMUX\1"
 "DIDR2\1"
 "DIDR0\1"
 "DIDR1\1"
 "TCCR1A\1"
 "TCCR1B\1"
 "TCCR1C\2"
 "TCNT1L\1"
 "TCNT1H\1"
 "ICR1L\1"
 "ICR1H\1"
 "OCR1AL\1"
 "OCR1AH\1"
 "OCR1BL\1"
 "OCR1BH\1"
 "OCR1CL\1"
 "OCR1CH\3"
 "TCCR3A\1"
 "TCCR3B\1"
 "TCCR3C\2"
 "TCNT3L\1"
 "TCNT3H\1"
 "ICR3L\1"
 "ICR3H\1"
 "OCR3AL\1"
 "OCR3AH\1"
 "OCR3BL\1"
 "OCR3BH\1"
 "OCR3CL\1"
 "OCR3CH\0"
 "\xB8\x00"	// I²C, Timer4, UART
 "TWBR\1"
 "TWSR\1"
 "TWAR\1"
 "TWDR\1"
 "TWCR\1"
 "TWAMR\1"
 "TCNT4\1"
 "TC4H\1"
 "TCCR4A\1"
 "TCCR4B\1"
 "TCCR4C\1"
 "TCCR4D\1"
 "TCCR4E\1"
 "CLKSEL0\1"
 "CLKSEL1\1"
 "CLKSTA\1"
 "UCSR1A\1"
 "UCSR1B\1"
 "UCSR1C\1"
 "UCSR1D\1"
 "UBRR1L\1"
 "UBRR1H\1"
 "UDR1\1"
 "OCR4A\1"
 "OCR4B\1"
 "OCR4C\1"
 "OCR4D\2"
 "DT4\0"
 "\xD7\x00"	// USB
 "UHWCON\1"
 "USBCON\1"
 "USBSTA\1"
 "USBINT\6"
 "UDCON\1"
 "UDINT\1"
 "UDIEN\1"
 "UDADDR\1"
 "UDFNUML\1"
 "UDFNUMH\1"
 "UDMFN\2"
 "UEINTX\1"
 "UENUM\1"
 "UERST\1"
 "UECONX\1"
 "UECFG0X\1"
 "UECFG1X\1"
 "UESTA0X\1"
 "UESTA1X\1"
 "UEIENX\1"
 "UEDATX\1"
 "UEBCLX\1"
 "UEBCHX\1"
 "UEINT\0"
 "\0"
};

// Disassembler komplett
class dasm{
friend class dline;
 void outdatalabel(word address,bool ioflag=false) const;
 void outcodelabel(dword address) const;	// for jmp/call/rjmp/rcall, even address!
 void outreg(byte address) const;	// address = 0..31
 void out2reg(byte address) const;	// address must be even: 0..30
 void outlohi(byte address,byte hiflag) const;	// guess lo/hi code/data label (mostly: data)
 const char*finddsym(word address) const;	// returned string is terminated with character code 0..\x1F!!
 static const char*finddsym(word address,const dsymtable*dsyms);
 static int symlen(const char*sym);		// length of string in bytes, without terminating byte
public:
 static const char*search(word opcode);
 dword address,flashsize;
 const byte*data;
 int datalen;
 const dsymtable*dsyms;	// device-specific symbols in data space
 FILE*out;
 dasm() {out=stdout; address=0;flashsize=8192;}
 void disassemble();
 void trace();		// TODO: build csymtable = call/jump labels and find code vs. data areas
};

// Disassembler für 1 Zeile
class dline{
 const dasm*parent;
 const word*p;
 bool indent,endcode;
public:
 dword a,e;	// Byteadresse
 dline(const dasm*_parent):parent(_parent) {start();}
 void start();
 void disassemble();	// one line, advancing p, a, sometimes indent and endcode.
};

void dline::start() {
 p=(word*)parent->data;
 e=(a=parent->address)+parent->datalen;
 indent=endcode=false;	// start with code, not data
};

const char*dasm::search(word opcode) {
 union{
  const char*pc;
  const word*pw;
 }p={opcodes};
 while (*p.pw) {
  if ((opcode&p.pw[0])==(p.pw[1]&p.pw[0])) switch (~p.pw[0]&p.pw[1]) {
   case 0x1F:	// magic code for Rd (R0..R31) == Rs
    if ((opcode>>4&0x1F) == (opcode&0x0F|opcode>>5&0x10)) return p.pc+4; break;
   default: return p.pc+4;
  }
  p.pc+=4+strlen(p.pc+4)+1;
 }
 return p.pc;
}

void dasm::outdatalabel(word address,bool ioflag) const{
 const char*sym=finddsym(ioflag?address+0x20:address);
 if (sym) fwrite(sym,1,symlen(sym),out);
 else fprintf(out,"0x%03X",address);		// NO! wrap-around here
}
void dasm::outcodelabel(dword address) const{
 address&=flashsize-1;			// Resolve wrap-around
 fprintf(out,"0x%04X",address);
}
void dasm::outreg(byte address) const{
 assert(address<32);
 if (address>=26) fprintf(out,"%c%c",(address>>1)-13+'X',address&1?'H':'L');
 else fprintf(out,"r%u",address);
}
void dasm::out2reg(byte address) const{
 assert(!(address&1));
 outreg(address+1); putc(':',out); outreg(address);
}
void dasm::outlohi(byte address,byte hiflag) const{
 fprintf(out,"0x%02X",address);
}
const char*dasm::finddsym(word address) const{
 return finddsym(address,dsyms);
}
const char*dasm::finddsym(word address,const dsymtable*dsyms) {
 if (!dsyms) return 0;
 union{
  const char*pc;
  const word*pw;
 }p={dsyms->symbols};
 do{
  word a=*p.pw++;	// Nimm erste Wortadresse
  char add;
  do{
   if (a==address) return p.pc;
   p.pc+=symlen(p.pc);	// zeigt auf Terminator (0..'\x1F')
   a+=add=*p.pc++;	// stets 0..31
	// Aufeinanderfolgende Symbole sind mit numerischem Terminator gekennzeichnet.
	// Größere Lücken sind mit '\0' gekennzeichnet, dann kommt erneut eine Wortadresse.
  }while(add);
 }while(*p.pw);		// letzte Adresse ist 0
 return finddsym(address,dsyms->inherit);	// Rekursion bis Vererbungsliste abgearbeitet
}
int dasm::symlen(const char*sym) {
 for(int i=0;;i++) if ((byte)sym[i]<32) return i;
}

void dasm::disassemble(){
 dline line(this);	// create data object changing line-by-line
 while (line.a<line.e) line.disassemble();
}

static void underline(FILE*out,bool on) {
 fprintf(out,"\33[%dm",on?4:0);	// ab Windows 10
/* 
 HANDLE h=GetStdHandle(STD_OUTPUT_HANDLE);
 CONSOLE_SCREEN_BUFFER_INFO csbi;
 GetConsoleScreenBufferInfo(h,&csbi);
 if (on) csbi.wAttributes|=COMMON_LVB_UNDERSCORE;
 else csbi.wAttributes&=~COMMON_LVB_UNDERSCORE;	// geht nicht!
 SetConsoleTextAttribute(h,csbi.wAttributes);
*/
}

void dline::disassemble() {  
 word opcode=*p++,op2;
 const char*s=parent->search(opcode);
 fprintf(parent->out,"%04X %04X",a,opcode);
 a+=2;
 if (strchr(s,0x9E) || strchr(s,0x9F)) {	// 4-Byte-Opcodes?
  op2=*p++;
  fprintf(parent->out," %04X",op2);
  a+=2;
 }
  // TODO: 2. opcode ausspucken wenn entsprechendes Token im String enthalten ist
 char tab='\t';
 putc(tab,parent->out);
 if (indent) putc(' ',parent->out);
 indent=false;
 endcode=false;
 if (*s) for(int charpos=0;;charpos++){
 char c=*s++;
  if (!c) break;
  if (c&0x80) {
   putc(tab,parent->out);
   c&=0x7F;
   tab=',';
  }
  if (tab=='\t' && 'A'<=c && c<='Z') {	// detect uppercase characters in mnemonic part
   c|=0x20;
   switch (charpos) {
    case 0: endcode=true; break;// may switch to data
    case 1: indent=true; break;	// indent _next_ line
   }
  }
  if (endcode) underline(parent->out,true);  
  switch (c) {
   case 0x01: parent->outdatalabel((opcode>>3&0x1F),true); break;
   case 0x02: parent->outdatalabel((opcode>>5&0x30|opcode&0x0F),true); break;
   case 0x08: fprintf(parent->out,"%u",opcode>>4&0x30|opcode&0x0F); break;	// 0..63
   case 0x09: parent->outlohi(opcode>>4&0xF0|opcode&0x0F,opcode&0x10); break;	// 0..255
   case 0x0A: fprintf(parent->out,"%u",opcode&0x07); break;	// 0..7
   case 0x0B: parent->outcodelabel(a+signed char(opcode>>2&0xFE)); break;		// brcc -128 .. +126
   case 0x0D: fprintf(parent->out,"%u",opcode>>8&0x20|opcode>>7&0x18|opcode&0x07); break;	// displacement q = 0..63
   case 0x10: parent->outreg(16+(opcode>>4&7)); break;
   case 0x11: parent->outreg(16+(opcode&7)); break;
   case 0x12: parent->outreg(16+(opcode&0x0F)); break;
   case 0x13: parent->outreg(16+(opcode>>4&0x0F)); break;
   case 0x14: parent->outreg(opcode>>5&0x10|opcode&0xF); break;
   case 0x15: parent->outreg(opcode>>4&0x1F); break;
   case 0x17: parent->out2reg(24+(opcode>>3&6)); break;
   case 0x18: parent->out2reg(opcode>>3&0x1E); break;
   case 0x19: parent->out2reg(opcode<<1&0x1E); break;
   case 0x1C: parent->outcodelabel(a+(signed short(opcode<<4&0xFFF0)>>3)); break;	// rjmp/rcall -4K .. +4K-2
   case 0x1D: parent->outdatalabel(~opcode>>1&0x80|opcode>>2&0x40|opcode>>5&0x30|opcode&0x0F); break;
   case 0x1E: parent->outdatalabel(op2); break;
   case 0x1F: parent->outcodelabel(opcode<<14&0x7C0000|opcode<<17&0x20000|op2<<1); break;
   default: if (c<32) __debugbreak();	// unused token: error in disassembler table or processing
   putc(c,parent->out);
  }
 }else
  fprintf(parent->out,".word\t0x%04X",opcode);
 if (endcode) underline(parent->out,false);
 putc('\n',parent->out);
}

int main(int argc, char**argv) {
 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
 DWORD mode;
 GetConsoleMode(handle,&mode);
 mode |= 4 /*ENABLE_VIRTUAL_TERMINAL_PROCESSING*/;	// ab Windows 10
 SetConsoleMode(handle,mode);
 class dasm dasm;
 dasm.data=(const byte*)dasm.search;
 dasm.datalen=32;
 FILE*f=fopen("c:/Users/heha/avr/convac/Convac.bin","rb");
 if (f) {
  fseek(f,0,SEEK_END);
  dasm.datalen=ftell(f);
  fseek(f,0,SEEK_SET);
  dasm.data=(byte*)malloc(dasm.datalen);
  fread((void*)dasm.data,1,dasm.datalen,f);
  fclose(f);
  dasm.flashsize=0x8000;
  dasm.dsyms=&dsym_mega32u4;
 }
 dasm.disassemble();
 return 0;
}
Detected encoding: ANSI (CP1252)4
Wrong umlauts? - Assume file is ANSI (CP1252) encoded