view lwdisasm/insn.c @ 448:5cccf90bf838 3.0 tip

Fixed bug with complex external references generating invalid relocations in the object file
author lost@l-w.ca
date Fri, 05 Nov 2010 22:27:00 -0600
parents cba03436c720
children
line wrap: on
line source

/*
insn.c

Copyright © 2010 William Astle

This file is part of LWTOOLS.

LWTOOLS is free software: you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
more details.

You should have received a copy of the GNU General Public License along with
this program. If not, see <http://www.gnu.org/licenses/>.
*/

#include <config.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <lw_alloc.h>
#include <lw_string.h>

#include "lwdisasm.h"

/*
this function creates a linedata_t with the disassembly of the
current location; returns NULL if the current disassembly point
is not a valid instruction or we are at the end of the input
*/

#define fb(d) do { int ____t; ____t = fetch_byte(as); if (____t < 0) return NULL; bytes[len++] = ____t; (d) = ____t; } while (0)
#define fb2(d) do { int ____t; ____t = fetch_byte(as); if (____t < 0) return "?"; bytes[(*len)++] = ____t; (d) = ____t; } while (0)

char *encode_target(disasmstate_t *as, int addr, int section)
{
	symbol_t *s;
	static char buffer[256];
	
	s = find_symbol(as, addr, section);
	if (!s)
	{
		sprintf(buffer, "$%04X", addr);
		return buffer;
	}
	else
	{
		return s -> symbol;
	}
}

char *decode_indexed(disasmstate_t *as, char *bytes, int *len, int *target, int section)
{
	int pb, b1, b2;
	int rn;
	static char *rs[] = { "x", "y", "u", "s", "pcr" };
	static char str[40];
	char *i1 = "", *i2 = "";
	char *tstr;
	*target = -1;
	
	fb2(pb);
	
	rn = (pb >> 5) & 3;
	
	if (pb < 0x80)
	{
		// 5 bit
		b1 = pb & 15;
		if (pb & 16)
			b1 = b1 - 16;
		
		sprintf(str, "%d,%s", b1, rs[rn]);
		return str;
	}
	if ((pb & 0x1F) == 0x1F)
	{
		// extended indirect
		fb2(b1);
		fb2(b2);
		sprintf(str, "[$%04X]", (b1 << 8) | b2);
		return str;
	}
	
	if (pb & 0x10)
	{
		i1 = "[";
		i2 = "]";
	}
	
	switch (pb & 0x0F)
	{
	case 0: // ,r+
		if (*i1 == '[')
		{
			break;
		}
		sprintf(str, "%s,%s+%s", i1, rs[rn], i2);
		return str;
		
	case 1: // ,r++
		sprintf(str, "%s,%s++%s", i1, rs[rn], i2);
		return str;
		
	case 2: // ,-r
		if (*i1 == '[')
		{
			break;
		}
		sprintf(str, "%s,-%s%s", i1, rs[rn], i2);
		return str;
		
	case 3: // ,--r
		sprintf(str, "%s,--%s%s", i1, rs[rn], i2);
		return str;
		
	case 4: // ,r
		sprintf(str, "%s,%s%s", i1, rs[rn], i2);
		return str;
		
	case 5: // B,r
		sprintf(str, "%sb,%s%s", i1, rs[rn], i2);
		return str;
		
	case 6: // A,r
		sprintf(str, "%sa,%s%s", i1, rs[rn], i2);
		return str;
	
	case 8:	// 8 bit,r
		fb2(b1);
		if (b1 > 128)
			b1 -= 256;
		sprintf(str, "%s<$%02X,%s%s", i1, b1, rs[rn], i2);
		return str;
		
	case 9:	// 16 bit,r
		fb2(b1);
		fb2(b2);
		b1 = (b1 << 8) | b2;
		if (b2 > 32768)
			b2 -= 65536;
		sprintf(str, "%s>$%04X,%s%s", i1, b1, rs[rn], i2);
		return str;
		
	case 11: // D,r
		sprintf(str, "%sd,%s%s", i1, rs[rn], i2);
		return str;
		
	case 12: // 8 bit,PCR
		fb2(b1);
		if (b1 > 127)
			b1 -= 256;
		b1 += as -> curoff;
		b1 &= 0xFFFF;
		sprintf(str, "%s<%s,pcr%s", i1, encode_target(as, b1, section), i2);
		*target = b1;
		return str;

	case 13: // 16 bit,PCR
		fb2(b1);
		fb2(b2);
		b1 = (b1 << 8) | b2;
		b1 += as -> curoff;
		b1 &= 0xFFFF;
		sprintf(str, "%s>%s,pcr%s", i1, encode_target(as, b1, section), i2);
		*target = b1;
		return str;
	}
	
	// if we got here, we have an illegal 6809
	if (as -> target == TARGET_6809)
	{
		sprintf(str, "?");
		return str;
	}
	
	// now sort out 6309 operations
	pb &= 0x1F;
	
	switch (pb)
	{
	case 0x07:
	case 0x17:
		// E,r
		sprintf(str, "%se,%s%s", i1, rs[rn], i2);
		return str;

	case 0x0A:
	case 0x1A:
		// F,r
		sprintf(str, "%sf,%s%s", i1, rs[rn], i2);
		return str;

	case 0x0E:
	case 0x1E:
		// W,r
		sprintf(str, "%sw,%s%s", i1, rs[rn], i2);
		return str;
	
	case 0x0F:
	case 0x10:
		// W indexing
		switch (rn)
		{
		case 0:	// ,W
			sprintf(str, "%s,w%s", i1, i2);
			return str;
			
		case 1: // 16 bit,W
			fb2(b1);
			fb2(b2);
			b1 = (b1 << 8) | b2;
			if (b1 > 32768)
				b1 -= 65536;
			sprintf(str, "%s>$%04X,w%s", i1, b1, i2);
			return str;
			
		case 2: // ,W++
			sprintf(str, "%s,w++%s", i1, i2);
			return str;
			
		case 3: // ,--W
			sprintf(str, "%s,--w%s", i1, i2);
			return str;
		}
	}
	
	sprintf(str, "?");
	return str;
}

char *decode_operand(disasmstate_t *as, int addrmode, uint8_t *bytes, int *len, int *target, int section)
{
	int b1, b2, b3, b4;
	static char insnbuffer[256];
	char *t;
	static char *rlist3[] = { "D", "X", "Y", "U", "S", "PC", "W", "V", "A", "B", "CC", "DP", "0", "0", "E", "F" };
	static char *rlist8[] = { "D", "X", "Y", "U", "S", "PC", "?", "?", "A", "B", "CC", "DP", "?", "?", "?", "?" };
	char **rlist = (as -> target == TARGET_6809) ? rlist8 : rlist3;
		
	switch (addrmode)
	{
	case ADDR_IMM8:
		fb2(b1);
		sprintf(insnbuffer, "#$%02X", b1);
		break;
	
	case ADDR_IMM16:
		fb2(b1);
		fb2(b2);
		sprintf(insnbuffer, "#$%04X", (b1 << 8) | b2);
		break;
	
	case ADDR_IMM32:
		fb2(b1);
		fb2(b2);
		fb2(b3);
		fb2(b4);
		sprintf(insnbuffer, "#$%08X", (b1 << 24) | (b2 << 16) | (b3 << 8) | b4);
		break;
	
	case ADDR_DIR:
		fb2(b1);
		sprintf(insnbuffer, "<%s", encode_target(as, (as -> dpval << 8) | b1, section));
		*target = as -> dpval * 256 + b1;
		break;
	
	case ADDR_EXT:
		fb2(b1);
		fb2(b2);
		sprintf(insnbuffer, ">%s", encode_target(as, (b1 << 8) | b2, section));
		*target = (b1 << 8) | b2;
		break;
	
	case ADDR_INH:
		*insnbuffer = 0;
		break;
	
	case ADDR_IMM8DIR:
		fb2(b1);
		fb2(b2);
		sprintf(insnbuffer, "#$%02X;<%s", b1, encode_target(as, (as -> dpval << 8) | b2, section));
		*target = (as -> dpval << 8) | b2;
		break;
	
	case ADDR_IMM8EXT:
		fb2(b1);
		fb2(b2);
		fb2(b3);
		sprintf(insnbuffer, "#$%02X;>%s", b1, encode_target(as, (b2 << 8) | b3, section));
		*target = (b2 << 8) | b3;
		break;
	
	case ADDR_IMM8IND:
		fb2(b1);
		t = decode_indexed(as, bytes, len, target, section);
		sprintf(insnbuffer, "#$%02X;%s", b1, t);
		break;
	
	case ADDR_IND:
		t = decode_indexed(as, bytes, len, target, section);
		sprintf(insnbuffer, "%s", t);
		break;
	
	case ADDR_RTOR:
		fb2(b1);
		b2 = b1 & 0x0F;
		b1 = b1 >> 4;
		sprintf(insnbuffer, "%s,%s", rlist[b1], rlist[b2]);
		break;
	
	case ADDR_BITBIT:
		fb2(b1);
		fb2(b2);
		sprintf(insnbuffer, "%s,%d,%d;<%s", 
			(b1 >> 6 == 0) ? "CC" : ((b1 >> 6 == 1) ? "A" : ((b1 >> 6 == 2) ? "B" : "?")),
		 	(b1 >> 3) & 7, b1 & 7, encode_target(as, (as -> dpval << 8) | b2, section));
		*target = (as -> dpval << 8) | b2;
		break;

	case ADDR_PSHPULS:
		insnbuffer[0] = 0;
		fb2(b1);
		if (b1 & 0x01)
		{
			strcat(insnbuffer, ",cc");
		}
		if (b1 & 0x02)
		{
			strcat(insnbuffer, ",a");
		}
		if (b1 & 0x04)
		{
			strcat(insnbuffer, ",b");
		}
		if (b1 & 0x08)
		{
			strcat(insnbuffer, ",dp");
		}
		if (b1 & 0x10)
		{
			strcat(insnbuffer, "x");
		}
		if (b1 & 0x20)
		{
			strcat(insnbuffer, "y");
		}
		if (b1 & 0x40)
		{
			strcat(insnbuffer, "u");
		}
		if (b1 & 0x80)
		{
			strcat(insnbuffer, "pc");
		}
		return insnbuffer + 1;
		break;
		
	case ADDR_PSHPULU:
		insnbuffer[0] = 0;
		fb2(b1);
		if (b1 & 0x01)
		{
			strcat(insnbuffer, ",cc");
		}
		if (b1 & 0x02)
		{
			strcat(insnbuffer, ",a");
		}
		if (b1 & 0x04)
		{
			strcat(insnbuffer, ",b");
		}
		if (b1 & 0x08)
		{
			strcat(insnbuffer, ",dp");
		}
		if (b1 & 0x10)
		{
			strcat(insnbuffer, "x");
		}
		if (b1 & 0x20)
		{
			strcat(insnbuffer, "y");
		}
		if (b1 & 0x40)
		{
			strcat(insnbuffer, "s");
		}
		if (b1 & 0x80)
		{
			strcat(insnbuffer, "pc");
		}
		return insnbuffer + 1;
	
	case ADDR_REL8:
		fb2(b1);
		if (b1 > 127)
			b1 -= 256;
		b1 += as -> curoff;
		b1 &= 0xFFFF;
		sprintf(insnbuffer, "%s", encode_target(as, b1, section));
		*target = b1;
		break;
		
	case ADDR_REL16:
		fb2(b1);
		if (b1 > 32767)
			b1 -= 65536;
		b1 += as -> curoff;
		b1 &= 0xFFFF;
		sprintf(insnbuffer, "%s", encode_target(as, b1, section));
		*target = b2;
		break;
	
	case ADDR_TFMPP:
		fb2(b1);
		b2 = b1 & 0x0F;
		b1 >>= 4;
		sprintf(insnbuffer, "%s+,%s+", rlist[b1], rlist[b2]);
		break;
		
	case ADDR_TFMMM:
		fb2(b1);
		b2 = b1 & 0x0F;
		b1 >>= 4;
		sprintf(insnbuffer, "%s-,%s-", rlist[b1], rlist[b2]);
		break;
	
	case ADDR_TFMPC:
		fb2(b1);
		b2 = b1 & 0x0F;
		b1 >>= 4;
		sprintf(insnbuffer, "%s+,%s", rlist[b1], rlist[b2]);
		break;
		
	case ADDR_TFMCP:
		fb2(b1);
		b2 = b1 & 0x0F;
		b1 >>= 4;
		sprintf(insnbuffer, "%s,%s+", rlist[b1], rlist[b2]);
		break;
		
	default:
		sprintf(insnbuffer, "????");
		break;
	}
	
	return insnbuffer;
}

linedata_t *disasm_insn(disasmstate_t *as)
{
	int curbyte;
	int b1;
	linedata_t *l;
	char *opcode;
	int addrmode;
	char insnbuffer[256];
	char *t;
	int addr;
	int len = 0;
	int8_t bytes[10];
	int target = -1;
	
	addr = as -> curoff;
	
	fb(curbyte);
	if (curbyte < 0)
		return NULL;
	
	opcode = as->page0[curbyte].op;
	addrmode = as->page0[curbyte].addrmode;

	if (addrmode == ADDR_PAGE1)
	{
		fb(b1);
		if (b1 < 0)
			return NULL;
		opcode = as->page1[b1].op;
		addrmode = as->page1[b1].addrmode;
		curbyte = (curbyte << 8) | b1;
	}
	else if (addrmode == ADDR_PAGE2)
	{
		fb(b1);
		if (b1 < 0)
			return NULL;
		opcode = as->page2[b1].op;
		addrmode = as->page2[b1].addrmode;
		curbyte = (curbyte << 8) | b1;
	}

	if (opcode == NULL)
		return NULL;

	t = decode_operand(as, addrmode, bytes, &len, &target, 0);

	if (!t)
		return NULL;
			
	// now create the line structure
	l = lw_alloc(sizeof(linedata_t));
	l -> next = NULL;
	l -> prev = NULL;
	l -> address = addr;
	l -> type = type_code;
	l -> length = len;
	l -> bytes = lw_alloc(len);
	memmove(l -> bytes, bytes, len);
	l -> isref = 0;
	l -> symbol = NULL;
	l -> sectionref = 0;
	sprintf(insnbuffer, "%s %s", opcode, t);
	l -> disasm = lw_strdup(insnbuffer);
	l -> target = target;
	return l;
}

void redisasm_insn(disasmstate_t *as, linedata_t *l)
{
	char *opcode;
	int addrmode;
	int len = 0;
	char bytes[15];
	int target = -1;
	char *t;
	char insnbuf[256];
	int ta;
	int ts;
		
	// short circuit if no target in this one	
	if (l -> target == -1)
		return;
	
	opcode = as -> page0[l -> bytes[0]].op;
	addrmode = as -> page0[l -> bytes[0]].addrmode;

	ta = as -> curoff;
	as -> curoff = l -> address + 1;
	
	if (addrmode == ADDR_PAGE1)
	{
		opcode = as -> page1[l -> bytes[1]].op;
		addrmode = as -> page1[l -> bytes[1]].addrmode;
		as -> curoff++;
	}
	else if (addrmode == ADDR_PAGE2)
	{
		opcode = as -> page2[l -> bytes[1]].op;
		addrmode = as -> page2[l -> bytes[1]].addrmode;
		as -> curoff++;
	}
	
	t = decode_operand(as, addrmode, bytes, &len, &target, l -> sectionref);

	// now create the line structure
	lw_free(l -> disasm);
	sprintf(insnbuf, "%s %s", opcode, t);
	l -> disasm = lw_strdup(insnbuf);
}