/* V810 Emulator
 *
 * Copyright (C) 2006 David Tucker
 *
 * This program 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 2 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

//////////////////////////////////////////////////////////
// CPU Debug routines

//what do we realy need?
//#include <stdio.h>
//#include <stdlib.h> 
//#include <string.h>
//#include <ctype.h>

#include "vb_io.h"
#include "v810_opt.h"
#include "v810_cpu.h"
#include "v810_cpuD.h"
#include "vb_vbt.h"
#include "vb_vbtD.h"


////////////////////////////////////////////////////////////
// Defines

//Structure to store an element in our linked list
// used to dynamicaly dissasemble a rom
typedef struct dasms {
	int            offset;
	uint32 		   PC;
	uint32           jump;
      struct dasms * nextElement;
} dasmS;

typedef struct {
   int addr_mode;               // Addressing mode
   char * opname;               // Optcode name (string)
} operation;

static operation optable[80] = {
  { AM_I,       "mov  " },           // 0x00
  { AM_I,       "add  " },           // 0x01
  { AM_I,       "sub  " },           // 0x02
  { AM_I,       "cmp  " },           // 0x03
  { AM_I,       "shl  " },           // 0x04
  { AM_I,       "shr  " },           // 0x05
  { AM_I,       "jmp  " },           // 0x06
  { AM_I,       "sar  " },           // 0x07
  { AM_I,       "mul  " },           // 0x08
  { AM_I,       "div  " },           // 0x09
  { AM_I,       "mulu " },           // 0x0A
  { AM_I,       "divu " },           // 0x0B
  { AM_I,       "or   " },           // 0x0C
  { AM_I,       "and  " },           // 0x0D
  { AM_I,       "xor  " },           // 0x0E
  { AM_I,       "not  " },           // 0x0F

  { AM_II,      "mov  " },           // 0x10  // Imediate
  { AM_II,      "add  " },           // 0x11
  { AM_II,      "setf " },           // 0x12
  { AM_II,      "cmp  " },           // 0x13
  { AM_II,      "shl  " },           // 0x14
  { AM_II,      "shr  " },           // 0x15
  { AM_UDEF,    "???  " },           // 0x16  // Unknown
  { AM_II,      "sar  " },           // 0x17
  { AM_II,      "trap " },           // 0x18

  { AM_IX,      "reti " },           // 0x19  //BRKRETI
  { AM_IX,      "halt " },           // 0x1A  //STBY

  {AM_UDEF,     "???  " },           // 0x1B  // Unknown
  { AM_II,      "ldsr " },           // 0x1C
  { AM_II,      "stsr " },           // 0x1D
  { AM_UDEF,    "???  " },           // 0x1E  // Unknown
  {AM_BSTR,     "BSTR " },           // 0x1F  // Special Bit String Instructions

  {AM_UDEF,     "???  " },           // 0x20  // Unknown   // This is a fudg on our part
  {AM_UDEF,     "???  " },           // 0x21  // Unknown   // We have 6 and 7 bit instructions
  {AM_UDEF,     "???  " },           // 0x22  // Unknown   // this is filld in by the Conditional Branch Instructions
  {AM_UDEF,     "???  " },           // 0x23  // Unknown
  {AM_UDEF,     "???  " },           // 0x24  // Unknown
  {AM_UDEF,     "???  " },           // 0x25  // Unknown
  {AM_UDEF,     "???  " },           // 0x26  // Unknown
  {AM_UDEF,     "???  " },           // 0x27  // Unknown

  { AM_V,       "movea" },           // 0x28
  { AM_V,       "addi " },           // 0x29
  { AM_IV,      "jr   " },           // 0x2A
  { AM_IV,      "jal  " },           // 0x2B
  { AM_V,       "ori  " },           // 0x2C
  { AM_V,       "andi " },           // 0x2D
  { AM_V,       "xori " },           // 0x2E
  { AM_V,       "movhi" },           // 0x2F

  { AM_VIa,     "ld.b " },           // 0x30
  { AM_VIa,     "ld.h " },           // 0x31
  {AM_UDEF,     "???  " },           // 0x32  // Unknown
  { AM_VIa,     "ld.w " },           // 0x33
  { AM_VIb,     "st.b " },           // 0x34
  { AM_VIb,     "st.h " },           // 0x35
  {AM_UDEF,     "???  " },           // 0x36  // Unknown
  { AM_VIb,     "st.w " },           // 0x37
  { AM_VIa,     "in.b " },           // 0x38
  { AM_VIa,     "in.h " },           // 0x39
  { AM_VIa,     "caxi " },           // 0x3A
  { AM_VIa,     "in.w " },           // 0x3B
  { AM_VIb,     "out.b" },           // 0x3C
  { AM_VIb,     "out.h" },           // 0x3D
  { AM_FPP,     "FPP  " },           // 0x3E  //Floating Point Instruction, Special Case
  { AM_VIb,     "out.w" },           // 0x3F

  { AM_III,     "bv   " },           // 0x40
  { AM_III,     "bl   " },           // 0x41  //BC  0x41
  { AM_III,     "be   " },           // 0x42  //BZ  0x42
  { AM_III,     "bnh  " },           // 0x43
  { AM_III,     "bn   " },           // 0x44
  { AM_III,     "br   " },           // 0x45
  { AM_III,     "blt  " },           // 0x46
  { AM_III,     "ble  " },           // 0x47
  { AM_III,     "bnv  " },           // 0x48
  { AM_III,     "bnl  " },           // 0x49 //BNC 0x49
  { AM_III,     "bne  " },           // 0x4A //BNZ 0x4A
  { AM_III,     "bh   " },           // 0x4B
  { AM_III,     "bp   " },           // 0x4C
  { AM_III,     "nop  " },           // 0x4D
  { AM_III,     "bge  " },           // 0x4E
  { AM_III,     "bgt  " }            // 0x4F
};
// All instructions greater than 0x50 are undefined (this should not be posible of cource)


//Structure for holding the SubOpcodes, Same as above, without the InsType.
typedef struct {
   char * opname;               // Optcode name (string)
} suboperation;


//  Bit String Subopcodes
static suboperation bssuboptable[16] = {
  { "SCH0BSU" },           // 0x00
  { "SCH0BSD" },           // 0x01
  { "SCH1BSU" },           // 0x02
  { "SCH1BSD" },           // 0x03
  { "BError4" },           // 0x04  // Unknown
  { "BError5" },           // 0x05  // Unknown
  { "BError6" },           // 0x06  // Unknown
  { "BError7" },           // 0x07  // Unknown
  { "ORBSU  " },           // 0x08
  { "ANDBSU " },           // 0x09
  { "XORBSU " },           // 0x0A
  { "MOVBSU " },           // 0x0B
  { "ORNBSU " },           // 0x0C
  { "ANDNBSU" },           // 0x0D
  { "XORNBSU" },           // 0x0E
  { "NOTBSU " }            // 0x0F
};

//  Floating Point Subopcodes
static suboperation fpsuboptable[16] = {
  { "cmpf.s " },           // 0x00
  { "FError1" },           // 0x01  // Unknown
  { "cvt.ws " },           // 0x02
  { "cvt.sw " },           // 0x03
  { "addf.s " },           // 0x04
  { "subf.s " },           // 0x05
  { "mulf.s " },           // 0x06
  { "divf.s " },           // 0x07
  { "XB" },                // 0x08  // undocumented
  { "XH" },                // 0x09  // undocumented //VFishing???
  { "REV" },               // 0x0A  // undocumented
  { "trnc.sw" },           // 0x0B
  { "MPYHW" },             // 0x0C  // undocumented
  { "FErrorD" },           // 0x0D  // Unknown
  { "FErrorE" },           // 0x0E  // Unknown
  { "FErrorF" }            // 0x0F  // Unknown
};


////////////////////////////////////////////////////////////
// Globals
//global linked list
dasmS* dasmHead = NULL;

//Add an element into our linked list
void v810_addDasm(uint32 tPC) {
	dasmS* tPtr = dasmHead;
	dasmS* tData;

	//Special case, header
	if(tPtr == NULL) {
		//Fill in data...
		tData = (dasmS *)malloc(sizeof(dasmS));
		tData->PC = tPC;
		tData->nextElement = NULL;

		dasmHead = tData;
		dasmHead->nextElement = NULL;
		return;
	//Special Case, header node...
	} else if(dasmHead->PC >= tPC) {
		// Dont insert if there the same...
		if(dasmHead->PC != tPC) {
			//Fill in data...
			tData = (dasmS *)malloc(sizeof(dasmS));
			tData->PC = tPC;
			tData->nextElement = dasmHead;
			dasmHead = tData;
		}
	} else {
		// Iterate up to our node...
		while((tPtr->nextElement != NULL)&&(tPtr->nextElement->PC < tPC)) {
			tPtr = tPtr->nextElement;
		}
		// Only add if not in already
		if((tPtr->PC != tPC)&&((tPtr->nextElement == NULL)||(tPtr->nextElement->PC != tPC))) {
			//Fill in data...
			tData = (dasmS *)malloc(sizeof(dasmS));
			tData->PC = tPC;
			tData->nextElement = tPtr->nextElement;
			tPtr->nextElement = tData;
		}
	}
}

//write out our linked list to a file
void v810_writeDasm() {
	dasmS* tPtr = dasmHead;

	while(tPtr != NULL) {
		v810_dis(tPtr->PC,1,ferr);
		tPtr = tPtr->nextElement;
	}
}

//clear our linked list of data
void v810_clearDasm() {
	dasmS* tPtr = dasmHead;

	while(dasmHead != NULL) {
		tPtr = dasmHead;
		dasmHead = dasmHead->nextElement;
		free(tPtr);
	}
}

long v810_dis(long tPC, int num, FILE* fout) {
    int lowB, highB, lowB2, highB2;             // up to 4 bytes for instruction (either 16 or 32 bits)
    int opcode, arg1, arg2, arg3;
    int i = 0;

    //if (tPC == -1) // | tPC < V810_ROM1.lowaddr
    //      tPC = PC;
    for(i = 0; i< num; i++) {
        lowB   = mem_rbyte(tPC);
        highB  = mem_rbyte(tPC+1);
        lowB2  = mem_rbyte(tPC+2);
        highB2 = mem_rbyte(tPC+3);
    
        opcode = highB >> 2;
        if((highB & 0xE0) == 0x80)        // Special opcode format for          
            opcode = (highB >> 1);            // type III instructions.
    
        if((opcode > 0x4F) | (opcode < 0)) {
            //Error Invalid opcode!
            fprintf(fout,"\n0x%08lx\t\t0x%2x 0x%2x  ;Invalid Opcode", tPC, lowB, highB);
            tPC += 2;                                               
        }
        
        switch(optable[opcode].addr_mode) {
        case AM_I:       // Do the same Ither way =)
            arg1 = (lowB >> 5) + ((highB & 0x3) << 3);
            arg2 = (lowB & 0x1F);
            if (opcode == JMP) {
                fprintf(fout,"\n0x%08lx\t%s\t\t[$%d]", tPC, optable[opcode].opname, arg2);
            } else {
                fprintf(fout,"\n0x%08lx\t%s\t\t$%d, $%d", tPC, optable[opcode].opname, arg2, arg1);
            }
            tPC += 2;   // 16 bit instruction
            break;
        case AM_II:
            arg1 = (lowB >> 5) + ((highB & 0x3) << 3);
            arg2 = (lowB & 0x1F);
			if(opcode == LDSR) {
				fprintf(fout,"\n0x%08lx\t%s\t\t $%d, $s%d", tPC, optable[opcode].opname, arg1, arg2);
			} else if(opcode == STSR) {
				fprintf(fout,"\n0x%08lx\t%s\t\t $s%d, $%d", tPC, optable[opcode].opname, arg2, arg1);
			} else {
				fprintf(fout,"\n0x%08lx\t%s\t\t%ld, $%d", tPC, optable[opcode].opname, sign_5(arg2), arg1);
			}
            tPC += 2;   // 16 bit instruction
            break;
        case AM_III:
            arg1 = ((highB & 0x1) << 8) + (lowB & 0xFE);
            fprintf(fout,"\n0x%08lx\t%s\t\t%ld", tPC, optable[opcode].opname, sign_9(arg1));
            tPC += 2;   // 16 bit instruction
            break;
        case AM_IV:
            arg1 = ((highB & 0x3) << 24) + (lowB << 16) + (highB2 << 8) + lowB2;
            fprintf(fout,"\n0x%08lx\t%s\t\t%ld", tPC, optable[opcode].opname, sign_26(arg1));
            tPC += 4;                                               // 32 bit instruction
            break;
        case AM_V:       
            arg1 = (lowB >> 5) + ((highB & 0x3) << 3);
            arg2 = (lowB & 0x1F);
            arg3 = (highB2 << 8) + lowB2;
            fprintf(fout,"\n0x%08lx\t%s\t\t0x%x, $%d, $%d", tPC, optable[opcode].opname, arg3, arg2, arg1 );
            tPC += 4;   // 32 bit instruction
            break;
        case AM_VIa:  // Mode6 form1
            arg1 = (lowB >> 5) + ((highB & 0x3) << 3);
            arg2 = (lowB & 0x1F);
            arg3 = (highB2 << 8) + lowB2;
            fprintf(fout,"\n0x%08lx\t%s\t\t%ld[$%d], $%d", tPC, optable[opcode].opname, sign_16(arg3), arg2, arg1);
            tPC += 4;   // 32 bit instruction
            break;
        case AM_VIb:  // Mode6 form2
            arg1 = (lowB >> 5) + ((highB & 0x3) << 3);
            arg2 = (lowB & 0x1F);
            arg3 = (highB2 << 8) + lowB2;                              //  whats the order??? 2,3,1 or 1,3,2
            fprintf(fout,"\n0x%08lx\t%s\t\t$%d, %ld[$%d]", tPC, optable[opcode].opname, arg1, sign_16(arg3), arg2);
            tPC += 4;   // 32 bit instruction
            break;
        case AM_VII:   // Unhandled
            fprintf(fout,"\n0x%08lx\t\t0x%2x 0x%2x 0x%2x 0x%2x", tPC, lowB, highB, lowB2, highB2);
            tPC +=4;        // 32 bit instruction
            break;
        case AM_VIII:  // Unhandled
            fprintf(fout,"\n0x%08lx\t\t0x%2x 0x%2x 0x%2x 0x%2x", tPC, lowB, highB, lowB2, highB2);
            tPC += 4;   // 32 bit instruction
            break;
        case AM_IX:
            arg1 = (lowB & 0x1); // Mode ID, Ignore for now
            fprintf(fout,"\n0x%08lx\t%s", tPC, optable[opcode].opname);   
            tPC += 2;   // 16 bit instruction
            break;
        case AM_BSTR:  // Bit String Subopcodes
            arg1 = (lowB >> 5) + ((highB & 0x3) << 3);
            arg2 = (lowB & 0x1F);
            if(arg2 > 15) {
                fprintf(fout,"\n0x%08lx\tBError", tPC);
            } else {
                fprintf(fout,"\n0x%08lx\t%s, $%d", tPC, bssuboptable[arg2].opname,arg1);
            }
            tPC += 2;   // 16 bit instruction
            break;
        case AM_FPP:   // Floating Point Subcode
            arg1 = (lowB >> 5) + ((highB & 0x3) << 3);
            arg2 = (lowB & 0x1F);
            arg3 = (highB2 >> 2);
            if(arg3 > 15) {
                fprintf(fout,"\n0x%08lx\tFError", tPC);
            } else {
                fprintf(fout,"\n0x%08lx\t%s  $%d, $%d", tPC, fpsuboptable[arg3].opname, arg1, arg2);
            }
            tPC += 4;   // 32 bit instruction
            break;
        case AM_UDEF:  // Invalid opcode.
        default:       // Invalid opcode.
            fprintf(fout,"\n0x%08lx\t\t0x%2x 0x%2x  ;Invalid Opcode", tPC, lowB, highB);
            tPC += 2;                                               
        }
    }
    return(tPC);
}

void v810_preg() {
    int i = 0;
    //int j = 0;
    for(i = 0; i < 32; i+=4) {
        //for(j = 0; j < 4; j++) {
        printf("\n$%02d %08lx, $%02d %08lx, $%02d %08lx, $%02d %08lx",i, P_REG[i],i+1,P_REG[i+1],i+2,P_REG[i+2],i+3,P_REG[i+3]);
    } 
    printf("\n$PSW %08lx",S_REG[PSW]);
}

void v810_dump(long tPC, int num) {
    uint8 t1, t2, t3, t4;
    int i = 0;
    int j = 0;
    
    //if (tPC == -1) // | tPC < V810_ROM1.lowaddr
    //      tPC = PC;
    
    for(i = 0; i< num; i++) {
        printf("\n%08lx",tPC);
        for (j = 0; j < 16; j+=8) {
            t1 = mem_rhword(tPC+j);
            t2 = mem_rhword(tPC+j+2);
            t3 = mem_rhword(tPC+j+4);
            t4 = mem_rhword(tPC+j+6);
            printf("  %04x %04x %04x %04x", t1, t2, t3, t4);
        }
        tPC +=16;
    }
}

