/*
 * arch/s390/lib/idis.c
 *
 * S390 version
 *   Copyright (C) 2001 IBM Deutschland Entwicklung GmbH,
 *                      IBM Corporation
 *
 * Derived from arch/ia64/lib/dis.c
 *   Copyright 1999 Silicon Graphics, Inc. All rights reserved.
 */

#include "lcrash.h"
#include <stdarg.h>
#include "dis-asm.h"

static disassemble_info disinfo;

int i_fprintf(FILE *, const char *, char *);

static int
dis_fprintf(PTR ofp, const char *fmt, ...)
{
        char buffer[256];
        va_list ap;

        va_start(ap, fmt);
        vsprintf(buffer, fmt, ap);
        va_end(ap);

        i_fprintf((FILE *)ofp, "%s", buffer);
        return(0);
}

static int
getidmem(bfd_vma addr, bfd_byte *buf, unsigned int length, 
	 struct disassemble_info *dip)
{
	/* Fill the provided buffer with bytes from
	 * memory, starting at address 'addr' for 'length bytes.
	 */
	GET_BLOCK(addr, length, buf);
	if (KL_ERROR) {
		return(1);
	}
	return(0);
}

static void
dis_printintaddr(bfd_vma addr, struct disassemble_info *dip, int flag)
{
	int offset = 0;
	syment_t *sp;

	if ((sp = kl_lkup_symaddr(addr))) {
		offset = addr - sp->s_addr;
	}

	/* Print out address
	 */
	dip->fprintf_func(dip->stream, "0x%llx", (uint64_t)addr);

	/* Print out symbol name 
	 */
	if (sp) {
		if (offset) {
			dip->fprintf_func(dip->stream, " <%s+%d>", 
				sp->s_name, offset);
		} else {
			dip->fprintf_func(dip->stream, " <%s>", sp->s_name);
		}
	} 

	/* Line things up properly for current function
	 */
	if (flag) {
		if (offset == 0) {
			dip->fprintf_func(dip->stream, ":       ");
		} else if (offset < 10) {
			dip->fprintf_func(dip->stream, ":     ");
		} else if (offset < 100) {
			dip->fprintf_func(dip->stream, ":    ");
		} else if (offset < 1000) {
			dip->fprintf_func(dip->stream, ":   ");
		} else if (offset < 10000) {
			dip->fprintf_func(dip->stream, ":  ");
		} else {
			dip->fprintf_func(dip->stream, ": ");
		}
	}
}

static void
dis_printaddr(bfd_vma addr, struct disassemble_info *dip)
{
	dis_printintaddr(addr, dip, 0);
}

static int
dis_getsym(bfd_vma addr, struct disassemble_info *dip)
{
        return 0;
}

void
set_dis_ofp(FILE *ofp) {
	/* Because the disassemble makes use of callbacks to display
	 * the disassembly output, we need to have a way to make sure
	 * the output is going where we want it to go. Since the disinfo
	 * struct is hidden from view, we have to have a function to do 
	 * this for us.
	 */
	if (ofp) {
		disinfo.stream = ofp;	
	}
}

int
do_dis(kaddr_t value, int lines, FILE *ofp)
{
	bfd_vma pc;
	int i;

	set_dis_ofp(ofp);

        pc = value;
	for (i = 0; i < lines; i++) {
		dis_printintaddr(pc, &disinfo, 1);
		pc += print_insn_s390(pc, &disinfo);
		disinfo.fprintf_func(ofp, "\n");
	}
	return(pc);
}

int
get_opcode(kaddr_t pc, int *opcode)
{
	int size;

	*opcode = 0;
	return (size);
}

void
dis_init(FILE *ofp)
{
	disinfo.fprintf_func     	= dis_fprintf;
	disinfo.stream       		= ofp;
	disinfo.application_data 	= NULL;
	disinfo.flavour          	= bfd_target_elf_flavour;
        disinfo.arch			= bfd_arch_s390;
#if defined(_ARCH_S390X)
 #if defined(bfd_mach_s390_esame)
	disinfo.mach			= bfd_mach_s390_esame;
 #elif defined(bfd_mach_s390_64)
	disinfo.mach			= bfd_mach_s390_64;
 #else
        #error "Invalid binutils version"
 #endif
#else
 #if defined(bfd_mach_s390_esa) || defined(bfd_mach_s390_31)
	disinfo.mach                    = 0 /* bfd_mach_s390_esa */;
 #else
	#error "Invalid binutils version"
 #endif
#endif
        disinfo.endian                  = BFD_ENDIAN_BIG;
	disinfo.flags        		= 0;
	disinfo.private_data         	= NULL;
	disinfo.read_memory_func 	= getidmem;
	disinfo.print_address_func 	= dis_printaddr;
	disinfo.symbol_at_address_func 	= dis_getsym;
	disinfo.buffer       		= NULL;
	disinfo.buffer_vma       	= 0;
	disinfo.buffer_length    	= 0;
	disinfo.bytes_per_line   	= 0;
	disinfo.bytes_per_chunk  	= 0;
        disinfo.display_endian          = BFD_ENDIAN_BIG;
	disinfo.insn_info_valid  	= 0;
	disinfo.branch_delay_insns 	= 0;
	disinfo.data_size            	= 0;
	disinfo.insn_type            	= 0;
	disinfo.target           	= 0;
	disinfo.target2          	= 0;
}



/* 
 * print_instr()
 */
int                     
print_instr(kaddr_t pc, FILE *ofp, int flag)
{               
	return(do_dis(pc, 1, ofp) - pc);
}

/*
 * list_instructions()
 */
void
list_instructions(FILE *ofp)
{
	fprintf(ofp, "This option not implemented for s390\n");
}

/*  #define SLOT(pc) (((pc & 0xf) < 6) ? 0 : (((pc & 0xf) < 0xc) ? 1 : 2)) */
#define SLOT(pc) 1

/*
 * start_instr()
 */
kaddr_t
start_instr(kaddr_t pc, int before)
{
	while (before--) {
		if (SLOT(pc)) {
			pc -= 6;
		} else {
			pc -= 4;
		}
	}
	return(pc);
}

/*              
 * print_instr_stream()
 */
kaddr_t
print_instr_stream(kaddr_t v, int bcount, int acount, int flags, FILE *ofp)
{
	int count;
	kaddr_t pc = v;

	/* Make sure that output goes to the right place
	 */ 
	set_dis_ofp(ofp);

	/* Make sure that pc is aligned properly
	 */
/*  	if (SLOT(pc) == 1) { */
/*  		pc = ((pc >> 4) << 4) | 0x6; */
/*  	} else if (SLOT(pc) == 2) { */
/*  		pc = ((pc >> 4) << 4) | 0xc; */
/*  	} */
	count = acount + 1;
	if (bcount) {
		pc = start_instr(pc, bcount);
		count += bcount;	
	}
	pc = do_dis(pc, count, ofp);
	return(pc);
}

/*
 * dump_instr() -- architecture specific instruction dump routine
 */
void
dump_instr(kaddr_t addr, uint64_t count, int flags, FILE *ofp)
{
	fprintf(ofp, "This option not implemented for s390x\n");
/*  	unsigned char template; */
/*  	uint64_t t0, t1, slot0, slot1, slot2; */
/*  	kaddr_t bundlep; */
/*  	bfd_byte bundle[16]; */
	
/*  	bundlep = (addr & 0xfffffffffffffff0); */
/*  	GET_BLOCK(bundlep, sizeof(bundle), bundle); */
/*  	t0 = bfd_getl64(bundle); */
/*  	t1 = bfd_getl64(bundle + 8); */
/*  	template = (t0 >> 1) & 0xf; */
/*  	slot0 = (t0 >>  5) & 0x1ffffffffffLL; */
/*  	slot1 = ((t0 >> 46) & 0x3ffff) | ((t1 & 0x7fffff) << 18); */
/*  	slot2 = (t1 >> 23) & 0x1ffffffffffLL; */

/*  	fprintf(ofp, "\nbundlep  : 0x%"FMTPTR"x\n", bundlep); */
/*  	fprintf(ofp, "template : %02x\n", template); */
/*  	fprintf(ofp, "   slot0 : %011"FMTPTR"x\n", slot0); */
/*  	fprintf(ofp, "   slot1 : %011"FMTPTR"x\n", slot1); */
/*  	fprintf(ofp, "   slot2 : %011"FMTPTR"x\n", slot2); */
}
