
/************************************/
/* CP1600/CP1610 Disassembler       */
/* Version  0.2 by Frank Palazzolo  */
/*                 palazzol@tir.com */
/*                                  */
/* Modified by J. Zbiciak to work   */
/*   with jzIntv.  This is a        */
/*   quickly-hacked version which   */
/*   also fixes a handful of bugs.  */
/************************************/

#include "config.h"
#include "debug_dasm1600.h"

/* ======================================================================== */
/*  DEBUG_SYMB_FOR_ADDR  -- Returns symbol associated with and address, or  */
/*                          NULL if there is none.  Performs no formatting. */
/*                                                                          */
/*  Prefers symbols that start w/out a '.' if available.                    */
/* ======================================================================== */
const char *debug_symb_for_addr
(
    const uint_32 addr
);

#define R2        "R%c",'0'+i2
#define R3        "R%c",'0'+i3
#define RB        "R%c",'4'+((w2>>8)&3)
#define RR        "R%c",'0'+(i3&3)
#define IM        "#$%04X",(dbd==1?(w2&0xFF)|(w3<<8):w2)

#define ADDR        "%s", symb_for_addr(w2)
#define DA          "%s", symb_for_addr(((w2 & 0x0FC) << 8) + \
                                         (w3 & 0x3FF))
#define DISPMINUS   "%s", symb_for_addr(addr + 1 - w2)
#define DISPPLUS    "%s", symb_for_addr(addr + 2 + w2)

#define OPCODE(Q)      snprintf(display_buffer.opcode_field, \
                         sizeof(display_buffer.opcode_field),"%s",Q)
#define OPERAND1(Q)    snprintf(display_buffer.operand1_field, \
                         sizeof(display_buffer.operand1_field),Q)
#define OPERAND2(Q)    {snprintf(display_buffer.operand2_field, \
                          sizeof(display_buffer.operand2_field),Q); \
                        strcpy(display_buffer.operanddel_field,",");}

#define MAYBE2_1  if(i3&1)OPERAND1("2")
#define MAYBE2_2  if(i3&4)OPERAND2("2")
#define DISP_1     if(sign)OPERAND1(DISPMINUS);else OPERAND1(DISPPLUS)

static const char *const branch_code[16] = {
"B   ","BC  ","BOV ","BPL ","BEQ ","BLT ","BLE ","BUSC",
"B   ","BNC ","BNOV","BMI ","BNEQ","BGE ","BGT ","BESC"
};

char s4a_buf[36];

/*  Symbol/Address Format:
 *
 *  Format Number      Format:
 *        0            SYMBOL ($1234)
 *        1            SYMBOL
 *        2            $1234 (SYMBOL)
 *        3            $1234
 *
 *  In all cases, if no symbol is defined for a given address, the output
 *  will be in format 3.
 */
static int symb_addr_format = 0;

int set_symb_addr_format(int x)
{
    if (x < 0) return 0;
    if (x > 3) return 0;

    if (symb_addr_format == x)
        return 0;

    symb_addr_format = x;

    return 1;
}

static char *symb_for_addr(const uint_32 addr)
{
    const char *const symb = debug_symb_for_addr(addr);
    int fmt = symb_addr_format;
    int len;

    if (!symb)
    {
        fmt = 3;
        len = 0;
    } else
    {
        len = strlen(symb);
    }

    switch (fmt)
    {
        case 0:
            if (len > 27)
                len = 27;

            sprintf(s4a_buf, "%*.*s ($%.4X)", len, len, symb, addr);
            break;

        case 1:
            if (len > 35)
                len = 35;

            sprintf(s4a_buf, "%*.*s", len, len, symb);
            break;

        case 2:
            if (len > 27)
                len = 27;

            sprintf(s4a_buf, "$%.4X (%*.*s)", addr, len, len, symb);
            break;

        case 3:
            sprintf(s4a_buf, "$%.4X", addr);
            break;
    }

    return s4a_buf;
}

struct tag_display_buffer
{
    char label_field[50];
    char address_field[10];
    char word1_field[10];
    char word2_field[10];
    char word3_field[10];
    char opcode_field[10];
    char operand1_field[50];
    char operanddel_field[50];
    char operand2_field[50];
    char comment_field[80];
} display_buffer;

static int get_num_words(int op, int dbd)
{
    if (op == 0x4)
        return 3;

    if ((op & 0x3c0) == 0x200)
        return 2;

    if ((op & 0x3f8) == 0x240)
        return 2;

    if ((op & 0x3f8) == 0x278)
        return 2;

    if ((op & 0x238) == 0x200)
        return 2;

    if ((op & 0x238) == 0x238)
        return 2 + (dbd == 1);

    return 1;
}


int dasm1600(char *outbuf, int addr, int dbd, int w1, int w2, int w3)
{
    int i1, i2, i3, sign;
    int s;

    unsigned short labeladdr;

    display_buffer.label_field[0] = 0;
    display_buffer.address_field[0] = 0;
    display_buffer.word1_field[0] = 0;
    display_buffer.word2_field[0] = 0;
    display_buffer.word3_field[0] = 0;
    display_buffer.opcode_field[0] = 0;
    display_buffer.operand1_field[0] = 0;
    display_buffer.operanddel_field[0] = 0;
    display_buffer.operand2_field[0] = 0;
    display_buffer.comment_field[0] = 0;

    snprintf(display_buffer.address_field,5,"%04X",addr);

    s = get_num_words(w1,dbd);

    switch (s)
    {
        case 1:
        {
            snprintf(display_buffer.word1_field,5,"%04X",w1);
            break;
        }
        case 2:
        {
            snprintf(display_buffer.word1_field,5,"%04X",w1);
            snprintf(display_buffer.word2_field,5,"%04X",w2);
            break;
        }
        case 3:
        {
            snprintf(display_buffer.word1_field,5,"%04X",w1);
            snprintf(display_buffer.word2_field,5,"%04X",w2);
            snprintf(display_buffer.word3_field,5,"%04X",w3);
            break;
        }
    }

    i1 = (w1 & 0x03c0)>>6;
    i2 = (w1 & 0x0038)>>3;
    i3 = w1 & 0x0007;

    switch(i1)
    {
    case 0:
    {
        switch (i2)
        {
        case 0:
        {
            switch (i3)
            {
            case 0:
            {
                OPCODE("HLT");
                break;
            }
            case 1:
            {
                OPCODE("SDBD");
                break;
            }
            case 2:
            {
                OPCODE("EIS");
                break;
            }
            case 3:
            {
                OPCODE("DIS");
                break;
            }
            case 4:
            {
                if ((w2 & 0x300) == 0x300)
                {
                if ((w2 & 3)==0)
                {
                    OPCODE("J");
                    OPERAND1(DA);
                    labeladdr = ((w2&0xfc)<<8)+(w3&0x3ff);
                }
                else if ((w2 & 3)==1)
                {
                    OPCODE("JE");
                    OPERAND1(DA);
                    labeladdr = ((w2&0xfc)<<8)+(w3&0x3ff);
                }
                else if ((w2 & 3)==2)
                {
                    OPCODE("JD");
                    OPERAND1(DA);
                    labeladdr = ((w2&0xfc)<<8)+(w3&0x3ff);
                }
                else
                {
                    OPCODE("Ji");
                    OPERAND1(DA);
                    labeladdr = ((w2&0xfc)<<8)+(w3&0x3ff);
                }
                }
                else
                {
                if ((w2 & 3)==0)
                {
                    OPCODE("JSR");
                    OPERAND1(RB);
                    OPERAND2(DA);
                    labeladdr = ((w2&0xfc)<<8)+(w3&0x3ff);

                }
                else if ((w2 & 3)==1)
                {
                    OPCODE("JSRE");
                    OPERAND1(RB);
                    OPERAND2(DA);
                    labeladdr = ((w2&0xfc)<<8)+(w3&0x3ff);
                }
                else if ((w2 & 3)==2)
                {
                    OPCODE("JSRD");
                    OPERAND1(RB);
                    OPERAND2(DA);
                    labeladdr = ((w2&0xfc)<<8)+(w3&0x3ff);
                }
                else
                {
                    OPCODE("JSRi");
                    OPERAND1(RB);
                    OPERAND2(DA);
                    labeladdr = ((w2&0xfc)<<8)+(w3&0x3ff);
                }
                }

                break;
            }
            case 5:
            {
                OPCODE("TCI");
                break;
            }
            case 6:
            {
                OPCODE("CLRC");
                break;
            }
            case 7:
            {
                OPCODE("SETC");
                break;
            }
            }
            break;
        }
        case 1:
        {
            OPCODE("INCR");
            OPERAND1(R3);
            break;
        }
        case 2:
        {
            OPCODE("DECR");
            OPERAND1(R3);
            break;
        }
        case 3:
        {
            OPCODE("COMR");
            OPERAND1(R3);
            break;
        }
        case 4:
        {
            OPCODE("NEGR");
            OPERAND1(R3);
            break;
        }
        case 5:
        {
            OPCODE("ADCR");
            OPERAND1(R3);
            break;
        }
        case 6:
        {
            if (i3 < 4)
            {
            OPCODE("GSWD");
            OPERAND1(RR);
            }
            else if (i3 < 6)
            {
            OPCODE("NOP");
            MAYBE2_1;
            }
            else
            {
            OPCODE("SIN");
            MAYBE2_1;
            }
            break;
        }
        case 7:
        {
            OPCODE("RSWD");
            OPERAND1(R3);
            break;
        }
        }
        break;
    }

    case 1:
    {
        switch (i2)
        {
        case 0:
        {
            OPCODE("SWAP");
            OPERAND1(RR);
            MAYBE2_2;
            break;
        }
        case 1:
        {
            OPCODE("SLL");
            OPERAND1(RR);
            MAYBE2_2;
            break;
        }
        case 2:
        {
            OPCODE("RLC");
            OPERAND1(RR);
            MAYBE2_2;
            break;
        }
        case 3:
        {
            OPCODE("SLLC");
            OPERAND1(RR);
            MAYBE2_2;
            break;
        }
        case 4:
        {
            OPCODE("SLR");
            OPERAND1(RR);
            MAYBE2_2;
            break;
        }
        case 5:
        {
            OPCODE("SAR");
            OPERAND1(RR);
            MAYBE2_2;
            break;
        }
        case 6:
        {
            OPCODE("RRC");
            OPERAND1(RR);
            MAYBE2_2;
            break;
        }
        case 7:
        {
            OPCODE("SARC");
            OPERAND1(RR);
            MAYBE2_2;
            break;
        }
        }
        break;
    }
    case 2:
    {
        if (((w1 & 0x38)>>3) == (w1 & 0x7))
        {
        OPCODE("TSTR");
        OPERAND1(R2);
        }
        else
        if ((w1 & 0x7) == 7)
        {
        OPCODE("JR");
        OPERAND1(R2);
        }
        else
        {
        OPCODE("MOVR");
        OPERAND1(R2);
        OPERAND2(R3);
        }
        break;
    }
    case 3:
    {
        OPCODE("ADDR");
        OPERAND1(R2);
        OPERAND2(R3);
        break;
    }
    case 4:
    {
        OPCODE("SUBR");
        OPERAND1(R2);
        OPERAND2(R3);
        break;
    }
    case 5:
    {
        OPCODE("CMPR");
        OPERAND1(R2);
        OPERAND2(R3);
        break;
    }
    case 6:
    {
        OPCODE("ANDR");
        OPERAND1(R2);
        OPERAND2(R3);
        break;
    }
    case 7:
    {
        if (((w1 & 0x38)>>3) == (w1 & 0x7))
        {
        OPCODE("CLRR");
        OPERAND1(R3);
        }
        else
        {
        OPCODE("XORR");
        OPERAND1(R2);
        OPERAND2(R3);
        }
        break;
    }
    case 8:
    {
        sign = (i2 & 4)>>2;
        if ((w1 & 0x1f) == 0x00)
        {
        OPCODE("B");
        DISP_1;
        if(sign)
            labeladdr = addr+1-w2;
        else
            labeladdr = addr+2+w2;
        }
        else if ((w1 & 0x1f) == 0x8)
        {
        OPCODE("NOPP");
        }
        else if ((w1 & 0x10) == 0x00)
        {
        OPCODE(branch_code[w1&0xf]);
        DISP_1;
        if(sign)
            labeladdr = addr+1-w2;
        else
            labeladdr = addr+2+w2;
        }
        else
        {
        OPCODE("BEXT");
        DISP_1;
        if(sign)
            labeladdr = addr+1-w2;
        else
            labeladdr = addr+2+w2;
        OPERAND2("E");
        }
        break;
    }
    case 9:
    {
        switch(i2)
        {
        case 0:
        {
            OPCODE("MVO");
            OPERAND1(R3);
            OPERAND2(ADDR);
            break;
        }
        case 6:
        {
            OPCODE("PSHR");
            OPERAND1(R3);
            break;
        }
        case 7:
        {
            OPCODE("MVOI");
            OPERAND1(R3);
            OPERAND2(IM);
            break;
        }
        default:
        {
            OPCODE("MVO@");
            OPERAND1(R3);
            OPERAND2(R2);
            break;
        }
        }
        break;
    }
    case 0xa:
    {
        switch(i2)
        {
        case 0:
        {
            OPCODE("MVI");
            OPERAND1(ADDR);
            OPERAND2(R3);
            break;
        }
        case 6:
        {
            OPCODE("PULR");
            OPERAND1(R3);
            break;
        }
        case 7:
        {
            OPCODE("MVII");
            OPERAND1(IM);
            OPERAND2(R3);
            break;
        }
        default:
        {
            OPCODE("MVI@");
            OPERAND1(R2);
            OPERAND2(R3);
            break;
        }
        }
        break;
    }
    case 0xb:
    {
        switch(i2)
        {
        case 0:
        {
            OPCODE("ADD");
            OPERAND1(ADDR);
            OPERAND2(R3);
            break;
        }
        case 7:
        {
            OPCODE("ADDI");
            OPERAND1(IM);
            OPERAND2(R3);
            break;
        }
        default:
        {
            OPCODE("ADD@");
            OPERAND1(R2);
            OPERAND2(R3);
            break;
        }
        }
        break;
    }
    case 0xc:
    {
        switch(i2)
        {
        case 0:
        {
            OPCODE("SUB");
            OPERAND1(ADDR);
            OPERAND2(R3);
            break;
        }
        case 7:
        {
            OPCODE("SUBI");
            OPERAND1(IM);
            OPERAND2(R3);
            break;
        }
        default:
        {
            OPCODE("SUB@");
            OPERAND1(R2);
            OPERAND2(R3);
            break;
        }
        }
        break;
    }
    case 0xd:
    {
        switch(i2)
        {
        case 0:
        {
            OPCODE("CMP");
            OPERAND1(ADDR);
            OPERAND2(R3);
            break;
        }
        case 7:
        {
            OPCODE("CMPI");
            OPERAND1(IM);
            OPERAND2(R3);
            break;
        }
        default:
        {
            OPCODE("CMP@");
            OPERAND1(R2);
            OPERAND2(R3);
            break;
        }
        }
        break;
    }
    case 0xe:
    {
        switch(i2)
        {
        case 0:
        {
            OPCODE("AND");
            OPERAND1(ADDR);
            OPERAND2(R3);
            break;
        }
        case 7:
        {
            OPCODE("ANDI");
            OPERAND1(IM);
            OPERAND2(R3);
            break;
        }
        default:
        {
            OPCODE("AND@");
            OPERAND1(R2);
            OPERAND2(R3);
            break;
        }
        }
        break;
    }
    case 0xf:
    {
        switch(i2)
        {
        case 0:
        {
            OPCODE("XOR");
            OPERAND1(ADDR);
            OPERAND2(R3);
            break;
        }
        case 7:
        {
            OPCODE("XORI");
            OPERAND1(IM);
            OPERAND2(R3);
            break;
        }
        default:
        {
            OPCODE("XOR@");
            OPERAND1(R2);
            OPERAND2(R3);
            break;
        }
        }
        break;
    }
    }

    snprintf(outbuf,1024,
            "%-10s $%4s  %4s %4s %4s         %-4s %s%1s%s    %s",
            display_buffer.label_field,
            display_buffer.address_field,
            display_buffer.word1_field,
            display_buffer.word2_field,
            display_buffer.word3_field,
            display_buffer.opcode_field,
            display_buffer.operand1_field,
            display_buffer.operanddel_field,
            display_buffer.operand2_field,
            display_buffer.comment_field);

    (void)labeladdr;
    return s;
}


