/* Generator is (c) James Ponder, 1997-2001 http://www.squish.net/generator/ */
// modified lightly to support MMU functions of Lisa Emulator + MOVEM by Ray Arachelian. :)
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#include "generator.h"
#include "cpu68k.h"

/* forward references */
void diss68k_getoperand(char *text, t_ipc * ipc, t_iib * iib, t_type type);


void printiib(t_iib *iib)
{
 //t_iib *myiib;


 fprintf(buglog,"\n----iib address: %p\n",iib);
 fprintf(buglog,"mask/bits: %02x/%02x\n",iib->mask, iib->bits);
 fprintf(buglog,"mnemonic: %s ",mnemonic_table[iib->mnemonic].name);

 if ( iib->flags.priv)   fprintf(buglog,"privilidged ");
 if ( iib->flags.endblk) fprintf(buglog,"end_block ");
 if ( iib->flags.imm_notzero) fprintf(buglog,"imm_notzero ");
 if ( iib->flags.used) fprintf(buglog,"privileged ");
 fprintf(buglog,"\nused/set: %02x/%02x\n",iib->flags.used,iib->flags.set);
 switch(iib->size)
 {
    case sz_none: fprintf(buglog,"sizeless\n"); break;
    case sz_byte: fprintf(buglog,"byte\n"); break;
    case sz_word: fprintf(buglog,"word\n"); break;
    case sz_long: fprintf(buglog,"long\n"); break;
    default:      fprintf(buglog,"error size!\n"); break;
 }
 fprintf(buglog,"source/dest type: %d/%d\n",(int)iib->stype,(int)iib->dtype);
 fprintf(buglog,"sbitpos/dbitpost: %d/%d\n",(int)iib->sbitpos,(int)iib->dbitpos);
 fprintf(buglog,"immvalue: %d cc:%d function:%d wordlen:%d clocks:%d\n",iib->immvalue, iib->cc, iib->funcnum,iib->wordlen, iib->clocks);
 fprintf(buglog,"-----\n\n"); fflush(buglog);

}




/* functions */

int diss68k_gettext(t_ipc * ipc, char *text)
{
	t_iib *iib;
	char *p, *c;
    char src[128], dst[128];
    char mnemonic[64];

	*text = '\0';

//    DEBUG_LOG(1,"getting iib for opcode:%04x",ipc->opcode);
    iib = cpu68k_iibtable[ipc->opcode];
//    printiib(iib);

    if (iib == NULL) {DEBUG_LOG(1,"Got a null IIB."); return 0;}

    // Added by RA to correct MOVEM direction order   - this worked.
    if (iib->mnemonic!=i_MOVEMMR)
    {  diss68k_getoperand(dst, ipc, iib, tp_dst);
       diss68k_getoperand(src, ipc, iib, tp_src);
    }
    else
    { diss68k_getoperand(src, ipc, iib, tp_dst);
      diss68k_getoperand(dst, ipc, iib, tp_src);     //
    }




	if ((iib->mnemonic == i_Bcc) || (iib->mnemonic == i_BSR) ||
		(iib->mnemonic == i_DBcc)) {
		sprintf(src, "$%08x", ipc->src);
	}


	strcpy(mnemonic, mnemonic_table[iib->mnemonic].name);

	if ((p = strstr(mnemonic, "cc")) != NULL) {
		if (iib->mnemonic == i_Bcc && iib->cc == 0) {
			p[0] = 'R';
			p[1] = 'A';
		} else {
			c = condition_table[iib->cc];
			strcpy(p, c);
		}
	}

	switch (iib->size) {
		case sz_byte:
			strcat(mnemonic, ".B");
			break;
		case sz_word:
			strcat(mnemonic, ".W");
			break;
		case sz_long:
			strcat(mnemonic, ".L");
			break;
		default:
			break;
	}

	sprintf(text, "%-10s %s%s%s", mnemonic, src, dst[0] ? "," : "", dst);


	return 1;
}

void diss68k_getoperand(char *text, t_ipc * ipc, t_iib * iib, t_type type)
{
	int bitpos;
	uint32 val;

	if (type == tp_src) {
		bitpos = iib->sbitpos;
		val = ipc->src;
	} else {
		bitpos = iib->dbitpos;
		val = ipc->dst;
	}

	switch (type == tp_src ? iib->stype : iib->dtype) {
		case dt_Dreg:
			sprintf(text, "D%d", (ipc->opcode >> bitpos) & 7);
			break;
		case dt_Areg:
			sprintf(text, "A%d", (ipc->opcode >> bitpos) & 7);
			break;
		case dt_Aind:
			sprintf(text, "(A%d)", (ipc->opcode >> bitpos) & 7);
			break;
		case dt_Ainc:
			sprintf(text, "(A%d)+", (ipc->opcode >> bitpos) & 7);
			break;
		case dt_Adec:
			sprintf(text, "-(A%d)", (ipc->opcode >> bitpos) & 7);
			break;
		case dt_Adis:
			sprintf(text, "$%04x(A%d)", (uint16)val, (ipc->opcode >> bitpos) & 7);
			break;
		case dt_Aidx:
            //sprintf(text, "AIDX:%d(A%d,%c%d.%c)",
            sprintf(text, "{AIDX:%08x}=(%d+A%d+%c%d.%c)",val,
                         (sint8)(val & 0xff),  // RA - signed decimal since it's signed
                         (ipc->opcode >> bitpos) & 7,
                         ((val & 0x80000000) ? 'A':'D'),  // RA 2004.04.27 Address vs Data register
                         ((val>>(12+16)) & 7),              //               Reg #
                         ((val & 0x8000000) ? 'L':'W')   //               word VS Long
                         );                            //
			break;
		case dt_AbsW:
			sprintf(text, "$%08x", val);
			break;
		case dt_AbsL:
			sprintf(text, "$%08x", val);
			break;
		case dt_Pdis:
            //sprintf(text, "%d(pc)",
            sprintf(text, "{PDIS:%08x}=(PC%c#$%04x)",val,
                     ((val-pc24) & 0x80000000 ?'-':'+'),
                     ((val-pc24) & 0x80000000 ? (pc24-val):(val-pc24)) & 0xffff);   // RA 2004.11.13 +2 as per Macintosh ResEdit
			break;

		case dt_Pidx:
            //sprintf(text, "PIDX:%d(pc, %c%d.%c)",
            sprintf(text, "{PIDX:%08x}=(%d+PC+%c%d.%c)",val,
                         (sint8)(val-pc24),                // RA 2004.04.27 - decimal because it's signed
                         ((val & 0x80000000) ? 'A':'D'),   // RA 2004.04.27 Address vs Data register
                         ((val>>(12+16)) & 7),                  //               Reg #
                         ((val & 0x8000000) ? 'L':'W')     //               word VS Long
                    );                                     // might want to change %02x to signed decimal
			break;

		case dt_ImmB:
			sprintf(text, "#$%02x", val);
			break;

		case dt_ImmW:
            // Added by RAY for output of LINK #$ (needs to print negative integers instead of #$FFxx
            if (iib->mnemonic==i_LINK) {int32 ival=(int16)(val); sprintf(text,"#%d",ival); break;}

            // Added by RA for output of MOVEM opcode disassembly
            if (iib->mnemonic!=i_MOVEMRM && iib->mnemonic!=i_MOVEMMR) {sprintf(text, "#$%04x", val); break;}
            {
                                  //    15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
                                  //     0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15
                char *REGMASK=         "D0/D1/D2/D3/D4/D5/D6/D7:A0/A1/A2/A3/A4/A5/A6/A7/";
                int16 i,k=0;
                uint8 mode=(ipc->opcode>>3) & 7;

                // remove next line after you debug this:
                sprintf(text, "{0x%04x}: ", val); k=strlen(text);

                if (mode==4)
                {
                    // remove next line after debug
                    strcat(text,"REV:");
                    // select pre-decrement register order instead
                    for (i=15; i>-1; i--)             // there are 16 registers to deal with. A0-A7,D0-D7
                    {
                      if (val & (1<<i))
                      {       text[k++]=REGMASK[(15-i)*3]; text[k++]=REGMASK[(15-i)*3+1];  // bit is set, print register associated with it

                              if (val & (1<<(i-1)) && i!=7)  // is the next bit selected too?
                              {     text[k++]='-';   // yes, and this is going to be a range.

                                    while (val & (1<<i) && i>-1)  // skip over bits that are not in the range.
                                          {i--; if (i==7) break;}   // but stop on border b/w A/D regs

                                    i++;  // step back one since we passed the last 1 bit (or on border)

                                    // print the end register's name
                                    text[k++]=REGMASK[(15-i)*3]; text[k++]=REGMASK[(15-i)*3+1];
                              }
                              text[k++]='/';  // print a slash and leave it for the next one.
                      }
                    }
                }
                else
                {
                    // remove next line after debug
                    strcat(text,"FWD:");

                    for (i=0; i<16; i++)             // there are 16 registers to deal with. A0-A7,D0-D7
                    {
                      if (val & (1<<i))
                      {       text[k++]=REGMASK[i*3]; text[k++]=REGMASK[i*3+1];  // bit is set, print register associated with it

                              if (val & (1<<(i+1)) && i!=8)  // is the next bit selected too?
                              {     text[k++]='-';   // yes, and this is going to be a range.

                                    while (val & (1<<i))  // skip over bits that are not in the range.
                                          {i++; if (i==8) break;}   // but stop on border b/w A/D regs

                                    i--;  // step back one since we passed the last 1 bit (or on border)

                                    // print the end register's name
                                    text[k++]=REGMASK[i*3]; text[k++]=REGMASK[i*3+1];
                              }
                              text[k++]='/';  // print a slash and leave it for the next one.
                      }
                    }
                }
            text[k-1]=0;                  // terminate
            }
            break;

		case dt_ImmL:
			sprintf(text, "#$%08x", val);
			break;
		case dt_ImmS:
			sprintf(text, "#%d", iib->immvalue);
			break;
		case dt_Imm3:
			sprintf(text, "#%d", (ipc->opcode >> bitpos) & 7);
			break;
		case dt_Imm4:
			sprintf(text, "#%d", (ipc->opcode >> bitpos) & 15);
			break;
		case dt_Imm8:
			sprintf(text, "#%d", (ipc->opcode >> bitpos) & 255);
			break;
		case dt_Imm8s:
			sprintf(text, "#%d", (sint32)val);
			break;


        default:
            *text=0;
			break;
	}
}

/** DANGER ** When we call this the *addr param must come from fetchaddr(addr) else no MMU translation! **/
// actually *addr appears to no longer be used, so there.
//int diss68k_getdumpline(uint32 addr68k, uint8 *addr, char *dumpline)
int diss68k_getdumpline(uint32 addr68k, char *dumpline)
{
	t_ipc ipc;

    //t_iib *iibp = cpu68k_iibtable[LOCENDIAN16(*(uint16 *)addr)];
    abort_opcode=2;
    t_iib *iibp = cpu68k_iibtable[fetchword(addr68k)];
    if (abort_opcode==1) {DEBUG_LOG(0,"got_abort_opcode!"); return 0;} else abort_opcode=0;

    int words, i;
	char dissline[64], *p;

	if (addr68k < 256) {
        //sprintf(dissline, "dc.l $%08x", LOCENDIAN32(*(uint32 *)addr));
        abort_opcode=2;
        sprintf(dissline, "dc.l $%08x", fetchlong(addr68k));
        if (abort_opcode==1) {DEBUG_LOG(0,"got_abort_opcode!"); return 0;} else abort_opcode=0;

         abort_opcode=0;

		words = 2;
	} else {

       #if DEBUG
       if (!iibp) ALERT_LOG(0,"about to pass NULL IIB");
       #endif
        cpu68k_ipc(addr68k, iibp, &ipc); if (abort_opcode==1) {DEBUG_LOG(0,"got_abort_opcode!"); return 0;} else abort_opcode=0;
		if (!diss68k_gettext(&ipc, dissline))
			strcpy(dissline, "Illegal Instruction");
		words = ipc.wordlen;
	}

	p = dumpline;
    //p += sprintf(p, "%6x : %04x ", addr68k, (addr[0] << 8) + addr[1]);
    abort_opcode=2;
    p += sprintf(p, "%6x : %04x ", addr68k, (fetchbyte(addr68k) << 8) + fetchbyte(addr68k+1));
    if (abort_opcode==1) {DEBUG_LOG(0,"got_abort_opcode!"); return 0;} else abort_opcode=0;

	for (i = 1; i < words; i++) {
        //p += sprintf(p, "%04x ", (addr[i * 2] << 8) + addr[i * 2 + 1]);
        abort_opcode=2;
        p += sprintf(p, "%04x ", fetchword(addr68k+(i * 2)));
        if (abort_opcode==1) {DEBUG_LOG(0,"got_abort_opcode!"); return 0;} else abort_opcode=0;
	}
	for (i = 29 - strlen(dumpline); i > 0; i--) {
		*p++ = ' ';
	}
	p += sprintf(p, ": ");
    /*-----------------5/7/2003 6:49PM------------------
   for (i = 0; i < words; i++) {
    if (isalnum(addr[i * 2])) {
      *p++ = addr[i * 2];
    } else
      *p++ = '.';
    if (isalnum(addr[i * 2 + 1])) {
      *p++ = addr[i * 2 + 1];
    } else
    * --------------------------------------------------*/

    for (i = 0; i < words; i++) {
        abort_opcode=2;
        if (isalnum(fetchbyte(addr68k+(i * 2)))) {
            if (abort_opcode==1) {DEBUG_LOG(0,"got_abort_opcode!"); return 0;} else abort_opcode=0;
            abort_opcode=2;
            *p++ = fetchbyte(addr68k+i * 2);
            if (abort_opcode==1) {DEBUG_LOG(0,"got_abort_opcode!"); return 0;} else abort_opcode=0;
		} else
			*p++ = '.';
        abort_opcode=2;
        if (isalnum(fetchbyte(addr68k+(i * 2 + 1)))) {
            if (abort_opcode==1) {DEBUG_LOG(0,"got_abort_opcode!"); return 0;} else abort_opcode=0;
            abort_opcode=2;
            *p++ = fetchbyte(addr68k+(i * 2 + 1));
            if (abort_opcode==1) {DEBUG_LOG(0,"got_abort_opcode!"); return 0;} else abort_opcode=0;
		} else
			*p++ = '.';
	}
	*p = '\0';
	for (i = 39 - strlen(dumpline); i > 0; i--) {
		*p++ = ' ';
	}
	if (iibp) {
		sprintf(p, " : %4d : %s\n", iibp->funcnum, dissline);
	} else {
		sprintf(p, " :      : %s\n", dissline);
	}

	return words;
}


