[net.sources] An MCS-48 Disassembler

sheu@gitpyr.gatech.EDU (Der-Ru Sheu) (01/20/87)

 Attached is a disassembler for Intel MCS-48 family single component micro-
 processors.  I wrote this a while ago, and hereby put it into the public
 domain. 

 This program would accept an Intel Hex format file, and produce a listing
 type file (with address and opcode) or assembly type file (with -a option).
 To compile it, just use "cc main.c decode.c utility.c" (UCB CC) or
 "cl main.c decode.c utility.c" (MicroSoft C).

- - - - - - - - - - - - - - - -  CUT HERE  - - - - - - - - - - - - - - - -

echo X - main.c
cat << "#### END OF MAIN.C ####" > main.c

#include <stdio.h>

/*	Intel (TM) 8038 family single component u-p machine code disassembler
 *	By: Sheu, Der-Ru; March 12, 1985
 *
 *	Command line options:
 *		-s hex#		sets the starting address (default 0)
 *		-e hex#		sets the ending address (default 2K)
 *		-a		sets output in assembly format (default 0)
 *
 *	Stdin and Stdout are always used as in/output, use redirection for
 *	file i/o.  Input should be in the Intel HEX format.
 *
 */

int pc_start = 0;		/* Command options */
int pc_end = 0x800;
int asm = 0;

main(argc, argv)
int argc;
char *argv[];
{
	int count, New, c, i, pc;
	char addr[6];
	int new_pc, byte1, byte2, two_byte;

/*    -1-        parse command line parameters				*/

	for (i=1; i<argc;)
		switch (argv[i++][1])

		{	case 'a':	
			case 'A':
				asm = 1;
				break;

			case 's':	
			case 'S':
				if (i >= argc) usage();
				pc_start = readhex(argv[i++]);
				break;

			case 'e':
			case 'E':
				if (i >= argc) usage();
				pc_end = readhex(argv[i++]);
				break;

			default: usage();
		}

/*    -2-        now begin disassemblying				*/

	for (pc = 0; pc < pc_start;)
		if (!(get_op(pc, &pc, &byte1))) exit(0);

	if (!(get_op(pc, &pc, &byte1))) exit(0);

	for (; pc <= pc_end;)
		if (!(get_op(pc, &new_pc, &byte2))) exit(0);
		else
		{	if (new_pc == (pc+1))
				two_byte = decode(pc, byte1, byte2, asm);
			else
			{	decode(pc, byte1, 0, asm);
				two_byte = 0;
				printf("\n");
			}
			printf("\n");

			if (two_byte)
			{	if (!(get_op(new_pc, &pc, &byte1))) exit(0); }
			else
			{	pc = new_pc;
				byte1 = byte2;
			}
		}
	printf("\n");
}


readhex(string)
char string[];
{
	int c, i, j;

	for (i=j=0; ; i++)
	{	c = string[i];

		if ((c >= '0') && (c <= '9')) c -= '0';
		else if ((c >= 'A') && (c < 'G')) c -= 'A' + 10;
		else if ((c >= 'a') && (c < 'g')) c -= 'a' + 10;
		else break;

		j <<= 4;
		j += c;
	}

	return(j);
}


usage()
{
	fprintf(stderr, "Usage: dee [-a] [-s starting] [-e ending]\n");
	exit(-1);
}

#### END OF MAIN.C ####
echo X - decode.c
cat << "#### END OF DECODE.C ####" > decode.c

#include <stdio.h>

#include "tables.h"			/* Mnemonis and opcode tables     */

int byte1, byte2, Hnibble, Lnibble, i, c;

/*
 * Routine to decode an instruction "byte1 byte2" with address "pc".
 *      calls Dumpbyte to dump a byte
 *      calls lookup to look up in the opcode table
 */

decode(pc, byte1, byte2, asm)
int pc, byte1, byte2, asm;
{
	if (!asm)
	{
		printf("    ");

		Dumpbyte( pc >> 8 );		/* Dump address */
		Dumpbyte( pc & 0X0FF );
		printf("     ");

		Dumpbyte(byte1);		/* Dump code */
		printf("  ");
		if (_Byte[byte1])
			Dumpbyte(byte2);
		else printf("  ");
	}

	Hnibble = byte1 >> 4;			/* Extract the higher  */
	Lnibble = byte1 & 0X0F;			/* and lower nibbles   */

	printf("           ");
	lookup();				/* and look it up      */

	if (_Byte[byte1])
		if (_Byte[byte1] == 1) Dumpbyte(byte2);
		else
		{	byte2 += ((pc--) & 0XF00);
			Dump(byte2>>8);
			Dumpbyte(byte2 & 0X0FF);
		}
	return(_Byte[byte1]);
}

lookup()
/*  Lookup: look up the opcode table
 *  input - Lnibble and Hnibble
 *  output - mnemonics are printed
 */
{
	if ( (!(Lnibble & 0X08) && (Lnibble & 0X02)) || (Lnibble == 5) )
		printf("%s",Row[trans1[Lnibble]][Hnibble]);	
						/* REGULAR opcodes */

	else if (Lnibble == 4) {		/* CALL or JUMP */
		if (Hnibble & 0X01) printf("CALL ");
		else printf("JMP ");
		Dump( (Hnibble & 0X0F) >> 1 );
	}

	else if ( (byte1 & 0XFE) == 0X0C0 || (byte1 & 0XFE) == 0X0E0 )
		printf("???");			/* unknown opcodes */

	else if ( ((Hnibble != 0) && (Hnibble != 3) && (Hnibble != 8) &&
		(Hnibble != 9) ) || (Lnibble > 11 ) ) {
		i = 0;
		while ( (c = Col[Hnibble][i++]) != '\0') {
			switch (c) {
			case 'r':
				Dump (Lnibble & 0X07);
				break;
			case '@':
				if (Lnibble < 8) printf("@");
				break;
			case 'p':
				Dump(Lnibble & 0X03);
				break;
			default:
				putc( c, stdout );
				break;
			}
		}
	}

	else printf("%s", ir[ trans2[Hnibble]][trans3[Lnibble]]);
}

Dumpbyte( i )
/* Dumpbyte: dump a byte stored in i		*/
int i;
{
	Dump( i >> 4 );
	Dump( i & 0X0F );
}

Dump(nibble)
/* Dump: dump a nibble (4 bits, a hex digit)	*/
int nibble;
{
	if( (nibble >= 0) && (nibble <= 9)) putchar( nibble + '0');
	else putchar(nibble + 'A' -10);
}
#### END OF DECODE.C ####
echo X - utility.c
cat << "#### END OF UTILITY.C ####" > utility.c

/* utility module:
 *
 * GET_OP: get the next opcode in the Intel Hex format.
 * KEEP: keep addresses.
 * SAVE: save the addresses.
 */

#include <stdio.h>


static int check_sum = 0;
static int power_up = 1;
static int byte_max = 0;              /* Force read new line */
static int byte_count = 1;


get_nibble()
{
	int i;

	i = getchar();
	if ((i >= '0') && (i <= '9')) return (i - '0');
	if ((i >= 'A') && (i <= 'F')) return (i - 'A' + 10);
	if ((i >= 'a') && (i <= 'f')) return (i - 'a' + 10);
	printf("\nUnexpected character found in the input file!!!\n");
	return(0);
}

get_byte()
{	int i;

	i = get_nibble() << 4;
	i += get_nibble();
	check_sum += i;
	return(i);
}


get_pc(pc_)
int *pc_;
{	int i;

	if (!power_up)
	{
		i = get_byte();
		if (!check_sum) fprintf(stderr, "Check sum error at PC = $%4x", *pc_);
		check_sum = 0;
	}
	power_up = 0;

	while (1)
	{
		while ( (i = getchar()) != ':')
			if (i == EOF) return(0);
		byte_count = 1;

		byte_max = get_byte();
		*pc_ = get_byte() << 8;
		*pc_ += get_byte();

		if ( !(i = get_byte()) ) return(1);
        if ( i == 1 ) return(0);

		fprintf(stderr, "Unknown record type found at PC = $%4x", *pc_);
	}
}

get_op(pc, pc_, op_)
int *pc_, *op_;
{

	if (byte_count >= byte_max)
	{	if (!get_pc(pc_)) return(0);  }
	else
	{	*pc_ = ++pc;
		byte_count++;
	}

	*op_ = get_byte();
	return(1);
}


static char buff[400][6];
static int ptr = 0;

keep(addr)
char addr[6];
{
	int i;
	for (i=0; i<6; i++) buff[ptr][i] = addr[i];
	ptr++;
}

save(fp)
FILE *fp;
{
	int i;
	for(i = 0; i <= ptr; i++) fprintf( fp, "%s", buff[i] );
}
#### END OF UTILITY.C ####
echo X - tables.h
cat << "#### END OF TABLES.H ####" > tables.h

int _Byte[256]  = {            /* byte table */

/*	0  1  2  3    4  5  6  7    8  9  A  B    C  D  E  F  */

	0, 0, 0, 1,   1, 0, 0, 0,   0, 0, 0, 0,   0, 0, 0, 0,
	0, 0, 2, 1,   1, 0, 2, 0,   0, 0, 0, 0,   0, 0, 0, 0,
	0, 0, 0, 1,   1, 0, 2, 0,   0, 0, 0, 0,   0, 0, 0, 0,
	0, 0, 2, 0,   1, 0, 2, 0,   0, 0, 0, 0,   0, 0, 0, 0,

	0, 0, 0, 1,   1, 0, 2, 0,   0, 0, 0, 0,   0, 0, 0, 0,
	0, 0, 2, 1,   1, 0, 2, 0,   0, 0, 0, 0,   0, 0, 0, 0,
	0, 0, 0, 0,   1, 0, 0, 0,   0, 0, 0, 0,   0, 0, 0, 0,
	0, 0, 2, 0,   1, 0, 2, 0,   0, 0, 0, 0,   0, 0, 0, 0,

	0, 0, 0, 0,   1, 0, 2, 0,   1, 1, 1, 0,   0, 0, 0, 0,
	0, 0, 2, 0,   1, 0, 2, 0,   1, 1, 1, 0,   0, 0, 0, 0,
	0, 0, 0, 0,   1, 0, 0, 0,   0, 0, 0, 0,   0, 0, 0, 0,
	1, 1, 2, 0,   1, 0, 2, 0,   1, 1, 1, 1,   1, 1, 1, 1,

	0, 0, 0, 0,   1, 0, 2, 0,   0, 0, 0, 0,   0, 0, 0, 0,
	0, 0, 2, 1,   1, 0, 0, 0,   0, 0, 0, 0,   0, 0, 0, 0,
	0, 0, 0, 0,   1, 0, 2, 0,   2, 2, 2, 2,   2, 2, 2, 2,
	0, 0, 2, 0,   1, 0, 2, 0,   0, 0, 0, 0,   0, 0, 0, 0   

};

static int trans1[8] = { -1, -1, 0, 1, -1, 2, 3, 4 };  
					/* translation table for *Row */

static char *Row[5][16] = {
{ "???",      "JB0 ",     "???",      "JB1 ",     "MOV A,T",  "JB2 ",     "MOV T,A",  "JB3 ", 
  "???",      "JB4 ",     "???",      "JB5 ",     "???",      "JB6 ",     "???",      "JB7 " },
{ "ADD A,#",  "ADDC A,#", "MOV A,#",  "???",      "ORL A,#",  "ANL A,#",  "???",      "???", 
  "RET",      "RETR",     "MOVP A,@A","** JMPP @A **",  "???","XRL A,#",  "MOVP3 A,@A", "???" },
{ "EN I",     "DIS I",    "EN T",     "DIS T",    "START E",  "START T",  "STOP T",   "ENT0 CLK", 
  "CLR F0",   "CPL F0",   "CLR F1",   "CPL F1",   "SEL RB0",  "SEL RB1",  "** SEL MB0 **", "** SEL MB1 **" },
{ "???",      "JTF ",     "JNT0 ",    "JT0 ",     "JNT1 ",    "JT1 ",     "???",  "JF1 ",     
  "JNI ",     "JNZ ",     "???",      "JF0 ",     "JZ ",      "???",      "JNC ",  "JC " },
{ "DEC A",    "INC A",    "CLR A",    "CPL A",    "SWAP A",   "DA A",     "RRC A", "RR A",
  "???",      "CLR C",    "CPL C",    "???",      "MOV A,PSW","MOV PSW,A","RL A",  "RLC A" }

};

static char *Col[16] = {
  "MOVD A,Pp","INC @Rr",  "XCH A,@Rr", "MOVD Pp,A","ORL A,@Rr", "ANL A, @Rr", "ADD A,@Rr", "ADDC A,@Rr", 
  "ORLD Pp,A","ANLD Pp,A","MOV @Rr, A", "MOV @Rr,#", "DEC Rr", "XRL A,@Rr", "DJNZ Rr,", "MOV A,@Rr" };

static int trans2[10] = { 0, -1, -1, 1, -1, -1, -1, -1, 2, 3 };
static int trans3[12] = { 0,  1, -1, -1, -1, -1, -1, -1, 2, 3, 4, 5 };

static char *ir[4][6] = {
{ "NOP",        "OUTL BUS,A", "INS A,BUS",  "IN A,P1", "IN A,P2", "???" },
{ "XCHD A,@R0", "XCHD A,@R1", "???",      "OUTL P1,A", "OUTL P2,A", "???" },
{ "MOVX A,@R0", "MOVX A,@R1", "ORL BUS,#", "ORL P1,#", "ORL P2,#", "???" },
{ "MOVX @R0,A", "MOVX @R1,A", "ANL BUS,#", "ANL P1,#", "ANL P2,#", "???" }  };
#### END OF TABLES.H ####
echo All done.