/* 6809dasm.c - a 6809 opcode disassembler */
/* Version 1.4 1-MAR-95 */
/* Copyright © 1995 Sean Riddle */
/* thanks to Franklin Bowen for bug fixes, ideas */
/* Freely distributable on any medium given all copyrights are retained */
/* by the author and no charge greater than $7.00 is made for obtaining */
/* this software */
/* Please send all bug reports, update ideas and data files to: */
/* sriddle@ionet.net */
/* latest version at: */
/* Please don't hurl on my URL! */
/* the data files must be kept compatible across systems! */
/* usage: 6809disasm [] */
/* output to stdout, so use redirection */
/* The optional data file contains 7 types of lines: */
/* o Remarks - these are lines beginning with a semi-colon (;) */
/* they are completely ignored. */
/* o 1 ORG line - gives the origin of the code; this is the starting */
/* address to be used for the disassembly. */
/* o COMMENT lines - used to add comments to the end of lines of the */
/* disassembly. */
/* o COMMENTLINE lines - provide full-line comments to be included before */
/* a given address in the disassembly. */
/* o DATA lines - mark sections as data. These sections will not be */
/* disassembled, but dumped as hex data instead. */
/* o ASCII lines - mark sections as text. These sections will not be */
/* disassembled, but printed as text instead. */
/* o WTEXT lines - interprets section as text encoded as in Joust, */
/* Bubbles, Sinistar (0x0=0,...,0xa=space,0xb=A,...,0x24=Z,...,0x32=:*/
/* See sample data files (*.dat) for examples. */
/* current limitations: */
/* o number of LABEL, DATA/ASCII, COMMENT and COMMENTLINE lines determined */
/* at compile-time - see MAXLABEL, MAXDATA, MAXCOMMENT and MAXCOMMLINE */
/* o all DATA/ASCII lines in data file must be sorted in ascending */
/* address order */
/* o ditto for COMMENT and COMMENTLINE lines */
/* o if a DATA/ASCII area is preceded by what 6809dasm thinks is code */
/* that continues into the DATA/ASCII area, the data will not be marked */
/* as such, and an error will be printed. If this is the case, mark the */
/* line before the data as data also. */
/* to do: */
/* o sort comment, ascii, data lines */
/* o look at JMP and JSR addresses- set to code unless overridden */
/* in data file */
/* o perhaps a 'scan' that creates a first-guess .dat file? */
/* generate dummy labels, mark code, find ASCII, etc. */
/* compiled on Amiga SAS/C 6.51 and Sun 10 Unix cc */
#include
#include
#include
#include
/* a few of my datatypes (from Amiga) */
#define TRUE (1==1)
#define FALSE (!TRUE)
typedef short BOOL; /* boolean quantity */
typedef unsigned char UBYTE; /* unsigned 8-bit quantity */
/* array sizes for labels, DATA/ASCII definitions and COMMENT/COMMENTLINE */
#define MAXLABEL 999
#define MAXDATA 999 /* both DATA and ASCII lines */
#define MAXCOMMENT 999
#define MAXCOMMLINE 999
/* output listing spacing */
#define TABOPNAME 19
#define TABOPERAND 25
#define TABCOMM 40
#define OPNAMETAB (TABOPNAME-1)
#define OPERANDTAB (TABOPERAND-1)
#define COMMTAB (TABCOMM-1)
typedef struct { /* opcode structure */
UBYTE opcode; /* 8-bit opcode value */
UBYTE numoperands;
char name[6]; /* opcode name */
UBYTE mode; /* addressing mode */
UBYTE numcycles; /* number of cycles - not used */
} opcodeinfo;
/* 6809 ADDRESSING MODES */
#define INH 0
#define DIR 1
#define IND 2
#define REL 3
#define EXT 4
#define IMM 5
#define LREL 6
#define PG2 7 /* PAGE SWITCHES - Page 2 */
#define PG3 8 /* Page 3 */
/* number of opcodes in each page */
#define NUMPG1OPS 223
#define NUMPG2OPS 38
#define NUMPG3OPS 9
int numops[3]={
NUMPG1OPS,NUMPG2OPS,NUMPG3OPS,
};
char modenames[9][14]={
"inherent",
"direct",
"indexed",
"relative",
"extended",
"immediate",
"long relative",
"page 2",
"page 3",
};
opcodeinfo pg1opcodes[NUMPG1OPS]={ /* page 1 ops */
0,1,"NEG",DIR,6,
3,1,"COM",DIR,6,
4,1,"LSR",DIR,6,
6,1,"ROR",DIR,6,
7,1,"ASR",DIR,6,
8,1,"ASL",DIR,6,
9,1,"ROL",DIR,6,
10,1,"DEC",DIR,6,
12,1,"INC",DIR,6,
13,1,"TST",DIR,6,
14,1,"JMP",DIR,3,
15,1,"CLR",DIR,6,
16,1,"page2",PG2,0,
17,1,"page3",PG3,0,
18,0,"NOP",INH,2,
19,0,"SYNC",INH,4,
22,2,"LBRA",LREL,5,
23,2,"LBSR",LREL,9,
25,0,"DAA",INH,2,
26,1,"ORCC",IMM,3,
28,1,"ANDCC",IMM,3,
29,0,"SEX",INH,2,
30,1,"EXG",IMM,8,
31,1,"TFR",IMM,6,
32,1,"BRA",REL,3,
33,1,"BRN",REL,3,
34,1,"BHI",REL,3,
35,1,"BLS",REL,3,
36,1,"BCC",REL,3,
37,1,"BCS",REL,3,
38,1,"BNE",REL,3,
39,1,"BEQ",REL,3,
40,1,"BVC",REL,3,
41,1,"BVS",REL,3,
42,1,"BPL",REL,3,
43,1,"BMI",REL,3,
44,1,"BGE",REL,3,
45,1,"BLT",REL,3,
46,1,"BGT",REL,3,
47,1,"BLE",REL,3,
48,1,"LEAX",IND,2,
49,1,"LEAY",IND,2,
50,1,"LEAS",IND,2,
51,1,"LEAU",IND,2,
52,1,"PSHS",INH,5,
53,1,"PULS",INH,5,
54,1,"PSHU",INH,5,
55,1,"PULU",INH,5,
57,0,"RTS",INH,5,
58,0,"ABX",INH,3,
59,0,"RTI",INH,6,
60,1,"CWAI",IMM,20,
61,0,"MUL",INH,11,
63,0,"SWI",INH,19,
64,0,"NEGA",INH,2,
67,0,"COMA",INH,2,
68,0,"LSRA",INH,2,
70,0,"RORA",INH,2,
71,0,"ASRA",INH,2,
72,0,"ASLA",INH,2,
73,0,"ROLA",INH,2,
74,0,"DECA",INH,2,
76,0,"INCA",INH,2,
77,0,"TSTA",INH,2,
79,0,"CLRA",INH,2,
80,0,"NEGB",INH,2,
83,0,"COMB",INH,2,
84,0,"LSRB",INH,2,
86,0,"RORB",INH,2,
87,0,"ASRB",INH,2,
88,0,"ASLB",INH,2,
89,0,"ROLB",INH,2,
90,0,"DECB",INH,2,
92,0,"INCB",INH,2,
93,0,"TSTB",INH,2,
95,0,"CLRB",INH,2,
96,1,"NEG",IND,6,
99,1,"COM",IND,6,
100,1,"LSR",IND,6,
102,1,"ROR",IND,6,
103,1,"ASR",IND,6,
104,1,"ASL",IND,6,
105,1,"ROL",IND,6,
106,1,"DEC",IND,6,
108,1,"INC",IND,6,
109,1,"TST",IND,6,
110,1,"JMP",IND,3,
111,1,"CLR",IND,6,
112,2,"NEG",EXT,7,
115,2,"COM",EXT,7,
116,2,"LSR",EXT,7,
118,2,"ROR",EXT,7,
119,2,"ASR",EXT,7,
120,2,"ASL",EXT,7,
121,2,"ROL",EXT,7,
122,2,"DEC",EXT,7,
124,2,"INC",EXT,7,
125,2,"TST",EXT,7,
126,2,"JMP",EXT,4,
127,2,"CLR",EXT,7,
128,1,"SUBA",IMM,2,
129,1,"CMPA",IMM,2,
130,1,"SBCA",IMM,2,
131,2,"SUBD",IMM,4,
132,1,"ANDA",IMM,2,
133,1,"BITA",IMM,2,
134,1,"LDA",IMM,2,
136,1,"EORA",IMM,2,
137,1,"ADCA",IMM,2,
138,1,"ORA",IMM,2,
139,1,"ADDA",IMM,2,
140,2,"CMPX",IMM,4,
141,1,"BSR",REL,7,
142,2,"LDX",IMM,3,
144,1,"SUBA",DIR,4,
145,1,"CMPA",DIR,4,
146,1,"SBCA",DIR,4,
147,1,"SUBD",DIR,6,
148,1,"ANDA",DIR,4,
149,1,"BITA",DIR,4,
150,1,"LDA",DIR,4,
151,1,"STA",DIR,4,
152,1,"EORA",DIR,4,
153,1,"ADCA",DIR,4,
154,1,"ORA",DIR,4,
155,1,"ADDA",DIR,4,
156,1,"CPX",DIR,6,
157,1,"JSR",DIR,7,
158,1,"LDX",DIR,5,
159,1,"STX",DIR,5,
160,1,"SUBA",IND,4,
161,1,"CMPA",IND,4,
162,1,"SBCA",IND,4,
163,1,"SUBD",IND,6,
164,1,"ANDA",IND,4,
165,1,"BITA",IND,4,
166,1,"LDA",IND,4,
167,1,"STA",IND,4,
168,1,"EORA",IND,4,
169,1,"ADCA",IND,4,
170,1,"ORA",IND,4,
171,1,"ADDA",IND,4,
172,1,"CPX",IND,6,
173,1,"JSR",IND,7,
174,1,"LDX",IND,5,
175,1,"STX",IND,5,
176,2,"SUBA",EXT,5,
177,2,"CMPA",EXT,5,
178,2,"SBCA",EXT,5,
179,2,"SUBD",EXT,7,
180,2,"ANDA",EXT,5,
181,2,"BITA",EXT,5,
182,2,"LDA",EXT,5,
183,2,"STA",EXT,5,
184,2,"EORA",EXT,5,
185,2,"ADCA",EXT,5,
186,2,"ORA",EXT,5,
187,2,"ADDA",EXT,5,
188,2,"CPX",EXT,7,
189,2,"JSR",EXT,8,
190,2,"LDX",EXT,6,
191,2,"STX",EXT,6,
192,1,"SUBB",IMM,2,
193,1,"CMPB",IMM,2,
194,1,"SBCB",IMM,2,
195,2,"ADDD",IMM,4,
196,1,"ANDB",IMM,2,
197,1,"BITB",IMM,2,
198,1,"LDB",IMM,2,
200,1,"EORB",IMM,2,
201,1,"ADCB",IMM,2,
202,1,"ORB",IMM,2,
203,1,"ADDB",IMM,2,
204,2,"LDD",IMM,3,
206,2,"LDU",IMM,3,
208,1,"SUBB",DIR,4,
209,1,"CMPB",DIR,4,
210,1,"SBCB",DIR,4,
211,1,"ADDD",DIR,6,
212,1,"ANDB",DIR,4,
213,1,"BITB",DIR,4,
214,1,"LDB",DIR,4,
215,1,"STB",DIR,4,
216,1,"EORB",DIR,4,
217,1,"ADCB",DIR,4,
218,1,"ORB",DIR,4,
219,1,"ADDB",DIR,4,
220,1,"LDD",DIR,5,
221,1,"STD",DIR,5,
222,1,"LDU",DIR,5,
223,1,"STU",DIR,5,
224,1,"SUBB",IND,4,
225,1,"CMPB",IND,4,
226,1,"SBCB",IND,4,
227,1,"ADDD",IND,6,
228,1,"ANDB",IND,4,
229,1,"BITB",IND,4,
230,1,"LDB",IND,4,
231,1,"STB",IND,4,
232,1,"EORB",IND,4,
233,1,"ADCB",IND,4,
234,1,"ORB",IND,4,
235,1,"ADDB",IND,4,
236,1,"LDD",IND,5,
237,1,"STD",IND,5,
238,1,"LDU",IND,5,
239,1,"STU",IND,5,
240,2,"SUBB",EXT,5,
241,2,"CMPB",EXT,5,
242,2,"SBCB",EXT,5,
243,2,"ADDD",EXT,7,
244,2,"ANDB",EXT,5,
245,2,"BITB",EXT,5,
246,2,"LDB",EXT,5,
247,2,"STB",EXT,5,
248,2,"EORB",EXT,5,
249,2,"ADCB",EXT,5,
250,2,"ORB",EXT,5,
251,2,"ADDB",EXT,5,
252,2,"LDD",EXT,6,
253,2,"STD",EXT,6,
254,2,"LDU",EXT,6,
255,2,"STU",EXT,6,
};
opcodeinfo pg2opcodes[NUMPG2OPS]={ /* page 2 ops 10xx*/
33,3,"LBRN",LREL,5,
34,3,"LBHI",LREL,5,
35,3,"LBLS",LREL,5,
36,3,"LBCC",LREL,5,
37,3,"LBCS",LREL,5,
38,3,"LBNE",LREL,5,
39,3,"LBEQ",LREL,5,
40,3,"LBVC",LREL,5,
41,3,"LBVS",LREL,5,
42,3,"LBPL",LREL,5,
43,3,"LBMI",LREL,5,
44,3,"LBGE",LREL,5,
45,3,"LBLT",LREL,5,
46,3,"LBGT",LREL,5,
47,3,"LBLE",LREL,5,
63,2,"SWI2",INH,20,
131,3,"CMPD",IMM,5,
140,3,"CMPY",IMM,5,
142,3,"LDY",IMM,4,
147,2,"CMPD",DIR,7,
156,2,"CMPY",DIR,7,
158,2,"LDY",DIR,6,
159,2,"STY",DIR,6,
163,2,"CMPD",IND,7,
172,2,"CMPY",IND,7,
174,2,"LDY",IND,6,
175,2,"STY",IND,6,
179,3,"CMPD",EXT,8,
188,3,"CMPY",EXT,8,
190,3,"LDY",EXT,7,
191,3,"STY",EXT,7,
206,3,"LDS",IMM,4,
222,2,"LDS",DIR,6,
223,2,"STS",DIR,6,
238,2,"LDS",IND,6,
239,2,"STS",IND,6,
254,3,"LDS",EXT,7,
255,3,"STS",EXT,7,
};
opcodeinfo pg3opcodes[NUMPG3OPS]={ /* page 3 ops 11xx */
63,1,"SWI3",INH,20,
131,3,"CMPU",IMM,5,
140,3,"CMPS",IMM,5,
147,2,"CMPU",DIR,7,
156,2,"CMPS",DIR,7,
163,2,"CMPU",IND,7,
172,2,"CMPS",IND,7,
179,3,"CMPU",EXT,8,
188,3,"CMPS",EXT,8,
};
opcodeinfo *pgpointers[3]={
pg1opcodes,pg2opcodes,pg3opcodes,
};
int count; /* current program counter for disasm */
/* getbyte() - get a byte from a file, and increment the byte counter */
int getbyte(FILE *fp) {
int c;
count++;
c=getc(fp);
return(c);
}
#ifdef NOCONST /* TFB had to undefine const */
#define const
#endif
const char *regs[5]={"X","Y","U","S","PC"};
const char *teregs[16]={"D","X","Y","U","S","PC","inv","inv","A","B","CC",
"DP","inv","inv","inv","inv"};
BOOL PC=FALSE; /* to see if a PUL instr is pulling PC */
#define LABELSIZE 40
/* label structure */
struct lastruct {
unsigned short lab; /* label address */
char label[LABELSIZE]; /* label text */
} *labarray=NULL;
int numlab=0; /* number of labels defined */
#ifndef AMIGA
/* hmmm, these aren't ANSI */
/* stricmp() - compare two strings, case insensitive */
int stricmp(const char *s1, const char *s2) {
for(;toupper(*s1)==toupper(*s2);++s1,++s2)
if(*s1=='\0')
return(0);
return((toupper(*(unsigned char *)s1)>5)&0x3;
pb2=pb&0x8f;
if((pb2==0x88)||(pb2==0x8c)) { /* 8-bit offset */
offset=getbyte(fp);
sprintf(out2,"%02hX ",offset);
strcat(str,out2);
if(offset>127) /* convert to signed */
offset=offset-256;
if(pb==0x8c)
reg=4;
for(sp=strlen(str);sp=0)
sprintf(out2,"$%02X,%s",offset,regs[reg]);
else
sprintf(out2,"-$%02X,%s",-offset,regs[reg]);
strcat(str,out2);
if(pb==0x8c) {
sprintf(out2," ; ($%04X)",offset+count);
strcat(str,out2);
}
} else if((pb2==0x89)||(pb2==0x8d)||(pb2==0x8f)) { /* 16-bit */
offset=(getbyte(fp)<<8);
sprintf(out2,"%02X ",offset>>8);
strcat(str,out2);
offset+=getbyte(fp);
sprintf(out2,"%02X ",offset&0xff);
strcat(str,out2);
if((pb!=0x8f)&&(offset>32767))
offset=offset-65536;
offset&=0xffff;
if(pb==0x8d)
reg=4;
for(sp=strlen(str);sp=0)
sprintf(out2,"$%04X,%s",offset,regs[reg]);
else
sprintf(out2,"-$%04X,%s",offset,regs[reg]);
strcat(str,out2);
if(pb==0x8d) {
sprintf(out2," ; ($%04X)",offset+count);
strcat(str,out2);
}
} else if(pb&0x80) {
for(sp=strlen(str);sp15)
offset=offset-32;
for(sp=strlen(str);sp>4)&0xf],
teregs[operandarray[0]&0xf]);
strcat(str,out2);
} else if((opcode==0x34)||(opcode==0x36)) { /* PUSH */
comma=FALSE;
if(operandarray[0]&0x80) {
strcat(str,"PC");
comma=TRUE;
PC=TRUE;
}
if(operandarray[0]&0x40) {
if(comma)
strcat(str,",");
if((opcode==0x34)||(opcode==0x35))
strcat(str,"U");
else
strcat(str,"S");
comma=TRUE;
}
if(operandarray[0]&0x20) {
if(comma)
strcat(str,",");
strcat(str,"Y");
comma=TRUE;
}
if(operandarray[0]&0x10) {
if(comma)
strcat(str,",");
strcat(str,"X");
comma=TRUE;
}
if(operandarray[0]&0x8) {
if(comma)
strcat(str,",");
strcat(str,"DP");
comma=TRUE;
}
if(operandarray[0]&0x4) {
if(comma)
strcat(str,",");
strcat(str,"B");
comma=TRUE;
}
if(operandarray[0]&0x2) {
if(comma)
strcat(str,",");
strcat(str,"A");
comma=TRUE;
}
if(operandarray[0]&0x1) {
if(comma)
strcat(str,",");
strcat(str,"CC");
}
} else if((opcode==0x35)||(opcode==0x37)) { /* PULL */
comma=FALSE;
if(operandarray[0]&0x1) {
strcat(str,"CC");
comma=TRUE;
}
if(operandarray[0]&0x2) {
if(comma)
strcat(str,",");
strcat(str,"A");
comma=TRUE;
}
if(operandarray[0]&0x4) {
if(comma)
strcat(str,",");
strcat(str,"B");
comma=TRUE;
}
if(operandarray[0]&0x8) {
if(comma)
strcat(str,",");
strcat(str,"DP");
comma=TRUE;
}
if(operandarray[0]&0x10) {
if(comma)
strcat(str,",");
strcat(str,"X");
comma=TRUE;
}
if(operandarray[0]&0x20) {
if(comma)
strcat(str,",");
strcat(str,"Y");
comma=TRUE;
}
if(operandarray[0]&0x40) {
if(comma)
strcat(str,",");
if((opcode==0x34)||(opcode==0x35))
strcat(str,"U");
else
strcat(str,"S");
comma=TRUE;
}
if(operandarray[0]&0x80) {
if(comma)
strcat(str,",");
strcat(str,"PC");
strcat(str," ;(PUL? PC=RTS)");
PC=TRUE;
}
} else {
if(numoperands==2) {
strcat(str,checklabs((operandarray[0]<<8)+operandarray[1],
FALSE,TRUE));
} else {
if(printdollar)
strcat(str,"$");
for(i=0;i=*numlab)
(*numlab)++;
} else
printf("Too many labels\n");
} else if(!strnicmp(line,"DATA ",5)) {
if(*numdatacommarray[*curcomm].comline+numoperands)&&
(*curcomm=commarray[*curcomm].comline)||
((line==commarray[*curcomm].comline)&&(numoperands==1))||
(((line-1)==commarray[*curcomm].comline)&&(numoperands==0))) {
if(*curcomm>=numcomm)
break;
if(first_comment!=TRUE) {
printf("%s\n",out);
out[0]='\0';
}
for(sp=strlen(out);spcommlinearray[*curcommline].comline)&&
(*curcommline127) {
data-=128;
newl=TRUE;
} else
newl=FALSE;
if(data<10)
data+=48;
else if(data==10)
data=32;
else if(data<37)
data+=54;
else if(data<51)
data=(int)wchars[data-37];
else
data=39;
return((char)data);
}
BOOL diddata=FALSE;
int dumpdata(int opcode,int *curdata,int numdata,FILE *fp,
int *curcomm,int numcomm,int *curcommline,int numcommline) {
int pnum,tnum;
int numoperands;
int k;
int numchars=0;
numoperands=2+dataarray[*curdata].end-count;
pnum=dataarray[*curdata].per_line; /* print up to pnum bytes data */
if(dataarray[*curdata].type==DATA) {
sprintf(out2,"%02X ",(UBYTE)opcode);
strcat(out,out2);
if((pnum<1)||(pnum>24))
pnum=16; /* no more than 24 bytes hex data */
} else {
if(dataarray[*curdata].type==ASCII)
out2[0]=opcode;
else
out2[0]=wtext(opcode);
out2[1]='\0';
strcat(out,out2);
if((pnum<1)||(pnum>70))
pnum=32; /* no more than 70 bytes ASCII data */
}
newl=FALSE;
tnum=(pnum>numoperands)?numoperands-2:pnum-1;
for(k=0;k=pnum)||newl) {
if((tnum)||(pnum==1)) {
docomment(count-tnum,curcomm,numcomm,tnum);
docommline(count-tnum,curcommline,numcommline);
}
printf("%s\n",out);
sprintf(out,"%04X: ",count);
numchars=0;
newl=FALSE;
}
if(dataarray[*curdata].type==DATA)
sprintf(out2,"%02X ",getbyte(fp)); /* hex */
else {
if(dataarray[*curdata].type==ASCII)
out2[0]=getbyte(fp); /* ASCII */
else
out2[0]=wtext(getbyte(fp)); /* text */
out2[1]='\0';
}
strcat(out,out2);
}
if(*curdata1) {
if(!stricmp(argv[1],"list")) { /* show all instructions */
for(i=0;i2)
readdatafile(argv[2],&org,&numlab,&numdata,&numcomm,&numcommline);
if(org>-1) /* int PC to ORG val or 0 */
count=org;
else
count=0;
for(k=0;kdataarray[curdata].end)&&
(curdata=dataarray[curdata].start)&& /* data? */
((count-1)<=dataarray[curdata].end)) {
numoperands=dumpdata(opcode,&curdata,numdata,fp,
&curcomm,numcomm,&curcommline,numcommline);
i=numops[0]+1; /* skip decoding as an opcode */
} else { /* not data - search for opcode */
sprintf(out2,"%02X ",(UBYTE)opcode);
strcat(out,out2);
for(i=0;(i=PG2) { /* page switch */
opcode=getbyte(fp);
sprintf(out2,"%02X ",(UBYTE)opcode);
strcat(out,out2);
page=pg1opcodes[i].mode-PG2+1; /* get page # */
for(k=0;(kname,"BRA"))|| /* extra space - branch */
(!stricmp(op->name,"LBRA"))||
(!stricmp(op->name,"RTS"))||
(!stricmp(op->name,"JMP"))||
(!stricmp(op->name,"RTI"))||
(!strnicmp(op->name,"PUL",3)&&PC)|| /* PUL? PC=RTS */
(!stricmp(op->name,"WAI"))) {
if(strlen(out)&&(out[strlen(out)-1]!='\n'))
strcat(out,"\n");
}
}
if(diddata) {
if(strlen(out)&&(out[strlen(out)-1]!='\n'))
strcat(out,"\n");
}
printf("%s\n",out);
PC=FALSE;
diddata=FALSE;
}
fclose(fp);
} else
fprintf(stderr,"Can't open file `%s'\n",argv[1]);
}
} else
printf("Usage: `%s []'\n",argv[0]);
freearrays();
}