#include <stdio.h>
#include "defs.h"
#include "memory.h"
#include "6502P.h"

#ifdef DEBUG

#define MODE_NULL	0
#define MODE_IMM	1
#define MODE_ZP		2
#define MODE_ZP_X	3
#define MODE_ZP_Y	4
#define MODE_IND_ZP	5
#define MODE_ABS	6
#define MODE_ABS_X	7
#define MODE_ABS_Y	8
#define MODE_IND_ZP_X	9
#define MODE_IND_ZP_Y  10
#define MODE_IND_ABS   11
#define MODE_REL       12
#define MODE_BBS       13
#define MODE_A	       14
#define MODE_IND_ABS_X 15

struct op_info {
    char *name;
    int mode;
    int len;
};

static struct op_info ops[256] = {
/* 00 */
    {"BRK",	MODE_NULL,	1},
    {"ORA",	MODE_IND_ZP_X,	2},
    {"DAT",	MODE_NULL,	2},
    {"DAT",	MODE_NULL,	2},
    {"TSB",	MODE_ZP,	2},
    {"ORA",	MODE_ZP,	2},
    {"ASL",	MODE_ZP,	2},
    {"DAT",	MODE_NULL,	2},
/* 08 */
    {"PHP",	MODE_NULL,	1},
    {"ORA",	MODE_IMM,	2},
    {"ASL",	MODE_A,		1},
    {"DAT",	MODE_NULL,	1},
    {"TSB",	MODE_ABS,	3},
    {"ORA",	MODE_ABS,	3},
    {"ASL",	MODE_ABS,	3},
    {"BBR",	MODE_BBS,	3},
/* 10 */
    {"BPL",	MODE_REL,	2},
    {"ORA",	MODE_IND_ZP_Y,	2},
    {"ORA",	MODE_IND_ZP,	2},
    {"DAT",	MODE_NULL,	2},
    {"TRB",	MODE_ZP,	2},
    {"ORA",	MODE_ZP_X,	2},
    {"ASL",	MODE_ZP_X,	2},
    {"DAT",	MODE_NULL,	2},
/* 18 */
    {"CLC",	MODE_NULL,	1},
    {"ORA",	MODE_ABS_Y,	3},
    {"INA",	MODE_NULL,	1},
    {"DAT",	MODE_NULL,	1},
    {"TRB",	MODE_ABS,	3},
    {"ORA",	MODE_ABS_X,	3},
    {"ASL",	MODE_ABS_X,	3},
    {"BBR",	MODE_BBS,	3},
/* 20 */
    {"JSR",	MODE_ABS,	3},
    {"AND",	MODE_IND_ZP_X,	2},
    {"DAT",	MODE_NULL,	2},
    {"DAT",	MODE_NULL,	2},
    {"BIT",	MODE_ZP,	2},
    {"AND",	MODE_ZP,	2},
    {"ROL",	MODE_ZP,	2},
    {"DAT",	MODE_NULL,	2},
/* 28 */
    {"PLP",	MODE_NULL,	1},
    {"AND",	MODE_IMM,	2},
    {"ROL",	MODE_A,		1},
    {"DAT",	MODE_NULL,	1},
    {"BIT",	MODE_ABS,	3},
    {"AND",	MODE_ABS,	3},
    {"ROL",	MODE_ABS,	3},
    {"BBR",	MODE_BBS,	3},
/* 30 */
    {"BMI",	MODE_REL,	2},
    {"AND",	MODE_IND_ZP_Y,	2},
    {"AND",	MODE_IND_ZP,	2},
    {"DAT",	MODE_NULL,	2},
    {"BIT",	MODE_ZP_X,	2},
    {"AND",	MODE_ZP_X,	2},
    {"ROL",	MODE_ZP_X,	2},
    {"DAT",	MODE_NULL,	2},
/* 38 */
    {"SEC",	MODE_NULL,	1},
    {"AND",	MODE_ABS_Y,	3},
    {"DEA",	MODE_NULL,	1},
    {"DAT",	MODE_NULL,	1},
    {"BIT",	MODE_ABS_X,	3},
    {"AND",	MODE_ABS_X,	3},
    {"ROL",	MODE_ABS_X,	3},
    {"BBR",	MODE_BBS,	3},
/* 40 */
    {"RTI",	MODE_NULL,	1},
    {"EOR",	MODE_IND_ZP_X,	2},
    {"DAT",	MODE_NULL,	2},
    {"DAT",	MODE_NULL,	2},
    {"DAT",	MODE_NULL,	2},
    {"EOR",	MODE_ZP,	2},
    {"LSR",	MODE_ZP,	2},
    {"DAT",	MODE_NULL,	2},
/* 48 */
    {"PHA",	MODE_NULL,	1},
    {"EOR",	MODE_IMM,	2},
    {"LSR",	MODE_A,		1},
    {"DAT",	MODE_NULL,	1},
    {"JMP",	MODE_ABS,	3},
    {"EOR",	MODE_ABS,	3},
    {"LSR",	MODE_ABS,	3},
    {"BBR",	MODE_BBS,	3},
/* 50 */
    {"BVC",	MODE_REL,	2},
    {"EOR",	MODE_IND_ZP_Y,	2},
    {"EOR",	MODE_IND_ZP,	2},
    {"DAT",	MODE_NULL,	2},
    {"DAT",	MODE_NULL,	2},
    {"EOR",	MODE_ZP_X,	2},
    {"LSR",	MODE_ZP_X,	2},
    {"DAT",	MODE_NULL,	2},
/* 58 */
    {"CLI",	MODE_NULL,	1},
    {"EOR",	MODE_ABS_Y,	3},
    {"PHY",	MODE_NULL,	1},
    {"DAT",	MODE_NULL,	1},
    {"DAT",	MODE_NULL,	3},
    {"EOR",	MODE_ABS_X,	3},
    {"LSR",	MODE_ABS_X,	3},
    {"BBR",	MODE_BBS,	3},
/* 60 */
    {"RTS",	MODE_NULL,	1},
    {"ADC",	MODE_IND_ZP_X,	2},
    {"DAT",	MODE_NULL,	2},
    {"DAT",	MODE_NULL,	2},
    {"STZ",	MODE_ZP,	2},
    {"ADC",	MODE_ZP,	2},
    {"ROR",	MODE_ZP,	2},
    {"DAT",	MODE_NULL,	2},
/* 68 */
    {"PLA",	MODE_NULL,	1},
    {"ADC",	MODE_IMM,	2},
    {"ROR",	MODE_A,		1},
    {"DAT",	MODE_NULL,	1},
    {"JMP",	MODE_IND_ABS,	3},
    {"ADC",	MODE_ABS,	3},
    {"ROR",	MODE_ABS,	3},
    {"BBR",	MODE_BBS,	3},
/* 70 */
    {"BVS",	MODE_REL,	2},
    {"ADC",	MODE_IND_ZP_Y,	2},
    {"ADC",	MODE_IND_ZP,	2},
    {"DAT",	MODE_NULL,	2},
    {"STZ",	MODE_ZP_X,	2},
    {"ADC",	MODE_ZP_X,	2},
    {"ROR",	MODE_ZP_X,	2},
    {"DAT",	MODE_NULL,	2},
/* 78 */
    {"SEI",	MODE_NULL,	1},
    {"ADC",	MODE_ABS_Y,	3},
    {"PLY",	MODE_NULL,	1},
    {"DAT",	MODE_NULL,	1},
    {"JMP",	MODE_IND_ABS_X,	3},
    {"ADC",	MODE_ABS_X,	3},
    {"ROR",	MODE_ABS_X,	3},
    {"BBR",	MODE_BBS,	3},
/* 80 */
    {"BRA",	MODE_REL,	2},
    {"STA",	MODE_IND_ZP_X,	2},
    {"DAT",	MODE_NULL,	2},
    {"DAT",	MODE_NULL,	2},
    {"STY",	MODE_ZP,	2},
    {"STA",	MODE_ZP,	2},
    {"STX",	MODE_ZP,	2},
    {"DAT",	MODE_NULL,	2},
/* 88 */
    {"DEY",	MODE_NULL,	1},
    {"BIT",	MODE_IMM,	2},
    {"TXA",	MODE_NULL,	1},
    {"DAT",	MODE_NULL,	1},
    {"STY",	MODE_ABS,	3},
    {"STA",	MODE_ABS,	3},
    {"STX",	MODE_ABS,	3},
    {"BBS",	MODE_BBS,	3},
/* 90 */
    {"BCC",	MODE_REL,	2},
    {"STA",	MODE_IND_ZP_Y,	2},
    {"STA",	MODE_IND_ZP,	2},
    {"DAT",	MODE_NULL,	2},
    {"STY",	MODE_ZP_X,	2},
    {"STA",	MODE_ZP_X,	2},
    {"STX",	MODE_ZP_Y,	2},
    {"DAT",	MODE_NULL,	2},
/* 98 */
    {"TYA",	MODE_NULL,	1},
    {"STA",	MODE_ABS_Y,	3},
    {"TXS",	MODE_NULL,	1},
    {"DAT",	MODE_NULL,	1},
    {"STZ",	MODE_ABS,	3},
    {"STA",	MODE_ABS_X,	3},
    {"STZ",	MODE_ABS_X,	3},
    {"BBS",	MODE_BBS,	3},
/* A0 */
    {"LDY",	MODE_IMM,	2},
    {"LDA",	MODE_IND_ZP_X,	2},
    {"LDX",	MODE_IMM,	2},
    {"DAT",	MODE_NULL,	2},
    {"LDY",	MODE_ZP,	2},
    {"LDA",	MODE_ZP,	2},
    {"LDX",	MODE_ZP,	2},
    {"DAT",	MODE_NULL,	2},
/* A8 */
    {"TAY",	MODE_NULL,	1},
    {"LDA",	MODE_IMM,	2},
    {"TAX",	MODE_NULL,	1},
    {"DAT",	MODE_NULL,	1},
    {"LDY",	MODE_ABS,	3},
    {"LDA",	MODE_ABS,	3},
    {"LDX",	MODE_ABS,	3},
    {"BBS",	MODE_BBS,	3},
/* B0 */
    {"BCS",	MODE_REL,	2},
    {"LDA",	MODE_IND_ZP_Y,	2},
    {"LDA",	MODE_IND_ZP,	2},
    {"DAT",	MODE_NULL,	2},
    {"LDY",	MODE_ZP_X,	2},
    {"LDA",	MODE_ZP_X,	2},
    {"LDX",	MODE_ZP_Y,	2},
    {"DAT",	MODE_NULL,	2},
/* B8 */
    {"CLV",	MODE_NULL,	1},
    {"LDA",	MODE_ABS_Y,	3},
    {"TSX",	MODE_NULL,	1},
    {"DAT",	MODE_NULL,	1},
    {"LDY",	MODE_ABS_X,	3},
    {"LDA",	MODE_ABS_X,	3},
    {"LDX",	MODE_ABS_Y,	3},
    {"BBS",	MODE_BBS,	3},
/* C0 */
    {"CPY",	MODE_IMM,	2},
    {"CMP",	MODE_IND_ZP_X,	2},
    {"DAT",	MODE_NULL,	2},
    {"DAT",	MODE_NULL,	2},
    {"CPY",	MODE_ZP,	2},
    {"CMP",	MODE_ZP,	2},
    {"DEC",	MODE_ZP,	2},
    {"DAT",	MODE_NULL,	2},
/* C8 */
    {"INY",	MODE_NULL,	1},
    {"CMP",	MODE_IMM,	2},
    {"DEX",	MODE_NULL,	1},
    {"DAT",	MODE_NULL,	1},
    {"CPY",	MODE_ABS,	3},
    {"CMP",	MODE_ABS,	3},
    {"DEC",	MODE_ABS,	3},
    {"BBS",	MODE_BBS,	3},
/* D0 */
    {"BNE",	MODE_REL,	2},
    {"CMP",	MODE_IND_ZP_Y,	2},
    {"CMP",	MODE_IND_ZP,	2},
    {"DAT",	MODE_NULL,	2},
    {"UNX",	MODE_IMM,	2},
    {"CMP",	MODE_ZP_X,	2},
    {"DEC",	MODE_ZP_X,	2},
    {"DAT",	MODE_NULL,	2},
/* D8 */
    {"CLD",	MODE_NULL,	1},
    {"CMP",	MODE_ABS_Y,	3},
    {"PHX",	MODE_NULL,	1},
    {"DAT",	MODE_NULL,	1},
    {"DAT",	MODE_NULL,	3},
    {"CMP",	MODE_ABS_X,	3},
    {"DEC",	MODE_ABS_X,	3},
    {"BBS",	MODE_BBS,	3},
/* E0 */
    {"CPX",	MODE_IMM,	2},
    {"SBC",	MODE_IND_ZP_X,	2},
    {"DAT",	MODE_NULL,	2},
    {"DAT",	MODE_NULL,	2},
    {"CPX",	MODE_ZP,	2},
    {"SBC",	MODE_ZP,	2},
    {"INC",	MODE_ZP,	2},
    {"DAT",	MODE_NULL,	2},
/* E8 */
    {"INX",	MODE_NULL,	1},
    {"SBC",	MODE_IMM,	2},
    {"NOP",	MODE_NULL,	1},
    {"DAT",	MODE_NULL,	1},
    {"CPX",	MODE_ABS,	3},
    {"SBC",	MODE_ABS,	3},
    {"INC",	MODE_ABS,	3},
    {"BBS",	MODE_BBS,	3},
/* F0 */
    {"BEQ",	MODE_REL,	2},
    {"SBC",	MODE_IND_ZP_Y,	2},
    {"SBC",	MODE_IND_ZP,	2},
    {"DAT",	MODE_NULL,	2},
    {"DAT",	MODE_NULL,	2},
    {"SBC",	MODE_ZP_X,	2},
    {"INC",	MODE_ZP_X,	2},
    {"DAT",	MODE_NULL,	2},
/* F8 */
    {"SED",	MODE_NULL,	1},
    {"SBC",	MODE_ABS_Y,	3},
    {"PLX",	MODE_NULL,	1},
    {"DAT",	MODE_NULL,	1},
    {"DAT",	MODE_NULL,	3},
    {"SBC",	MODE_ABS_X,	3},
    {"INC",	MODE_ABS_X,	3},
    {"BBS",	MODE_BBS,	3}
};

static void print_mode(word PC, byte op) {
    printf("%s ", ops[op].name);

    switch (ops[op].mode) {
    case MODE_NULL:
	putchar('\n');
	break;
    case MODE_IMM:
	printf("#&%02X\n", readb(PC+1));
	break;
    case MODE_ZP:
	printf("&%02X\n", readb(PC+1));
	break;
    case MODE_ZP_X:
	printf("&%02X,X\n", readb(PC+1));
	break;
    case MODE_ZP_Y:
	printf("&%02X,Y\n", readb(PC+1));
	break;
    case MODE_IND_ZP:
	printf("(&%02X)\n", readb(PC+1));
	break;
    case MODE_ABS:
	printf("&%04X\n", readw(PC+1));
	break;
    case MODE_ABS_X:
	printf("&%04X,X\n", readw(PC+1));
	break;
    case MODE_ABS_Y:
	printf("&%04X,Y\n", readw(PC+1));
	break;
    case MODE_IND_ZP_X:
	printf("(&%02X,X)\n", readb(PC+1));
	break;
    case MODE_IND_ZP_Y:
	printf("(&%02X),Y\n", readb(PC+1));
	break;
    case MODE_IND_ABS:
	printf("(&%04X)\n", readw(PC+1));
	break;
    case MODE_REL:
	printf("&%04X\n", PC + 2 + (signed char)readb(PC+1));
	break;
    case MODE_BBS:
	printf("%d &%02X &%04X\n", op & 0xf, readb(PC+1),
	       PC + 3 + (signed char)readb(PC+2));
	break;
    case MODE_A:
	printf("A\n");
	break;
    case MODE_IND_ABS_X:
	printf("(&%04X,X)\n", readw(PC+1));
	break;
    }
}

static void print_mem(word PC, byte op) {
    switch(ops[op].len) {
    case 1:
	printf("%04x: %02x      ", PC, op);
	break;
    case 2:
	printf("%04x: %02x %02x   ", PC, op, readbq(PC+1));
	break;
    case 3:
	printf("%04x: %02x %02x %02x", PC, op, readbq(PC+1), readbq(PC+2));
	break;
    }
}

void disassemble(word PC, byte A, byte X, byte Y, byte S, byte P) {
    byte op = readbq(PC);

    print_mem(PC, op);

    printf("  A=%02x, X=%02x, Y=%02x, S=%02x, P=%02x ; ",
	   A, X, Y, S, P);

    print_mode(PC, op);
}

void disregion(int start, int end) {
    word PC = start;
    byte op;
    
    while (PC < end) {
	op = readbq(PC);
	print_mem(PC, op);
	printf("  ");
	print_mode(PC, op);
	PC += ops[op].len;
	fflush(stdout);
    }
}

#endif /* DEBUG */
