/*****************************************************************************/
/*									     */
/*									     */
/*	CP/M emulator version 0.1					     */
/*									     */
/*	written by Michael Bischoff (mbi@mo.math.nat.tu-bs.de)		     */
/*	June-1994							     */
/*									     */
/*	This file is distributed under the GNU COPYRIGHT		     */
/*	see COPYRIGHT.GNU for Copyright details				     */
/*									     */
/*									     */
/*****************************************************************************/
#include "cpmemu.h"

#define X   12      /* offset for memnonic */
static const char *dtab1[] = { "B", "C", "D", "E", "H", "L", "(HL)", "A" };
static const char *dtab2[] = { "BC", "DE", "HL", "SP" };
/*static const char *dtab3[] = { "BC", "DE", "HL", "AF" }; */
static const char *dtab4[] = { "NZ", "Z", "NC", "C", "PO", "PE", "P", "M" };
static const char *dtab5[] = { "ADD A,", "ADC A,", "SUB ", "SBC A,", "AND ", "XOR ", "OR ", "CP " };
static const char *op1tab[] = {
    "NOP",        "LD BC,%04x", "LD (BC),A",   "INC BC", NULL, NULL, NULL, "RLCA", "EX AF,AF'",     "ADD HL,BC", "LD A,(BC)",   "DEC BC", NULL, NULL, NULL, "RRCA",
    "DJNZ %04x",  "LD DE,%04x", "LD (DE),A",   "INC DE", NULL, NULL, NULL, "RLA",  "JR %04x",       "ADD HL,DE", "LD A,(DE)",   "DEC DE", NULL, NULL, NULL, "RRA",
    "JR NZ,%04x", "LD HL,%04x", "LD (%04x),HL","INC HL", NULL, NULL, NULL, "DAA",  "JR Z,%04x",     "ADD HL,HL", "LD HL,(%04x)","DEC HL", NULL, NULL, NULL, "CPL",
    "JR NC,%04x", "LD SP,%04x", "LD (%04x),A", "INC SP", NULL, NULL, NULL, "SCF",  "JR C,%04x",     "ADD HL,SP", "LD A,(%04x)", "DEC SP", NULL, NULL, NULL, "CCF" };
static const char *op2tab[] = {
    NULL, "POP BC", NULL, "JP %04x",      NULL, "PUSH BC", NULL, NULL,   NULL, "RET",     NULL, "???",         NULL, "CALL %04x", NULL, NULL,
    NULL, "POP DE", NULL, "OUT (%03x),A", NULL, "PUSH DE", NULL, NULL,   NULL, "EXX",     NULL, "IN A,(%03x)", NULL, "???",       NULL, NULL,
    NULL, "POP HL", NULL, "EX (SP),HL",   NULL, "PUSH HL", NULL, NULL,   NULL, "JP (HL)", NULL, "EX DE,HL",    NULL, "???",       NULL, NULL,
    NULL, "POP AF", NULL, "DI",           NULL, "PUSH AF", NULL, NULL,   NULL, "LD SP,HL",NULL, "EI",          NULL, "???",       NULL, NULL };
static const char *cbtab1[] = {
    "RLC", "RRC", "RL", "RR", "SLA", "SRA", "? SLIA", "SRL" };
static const char *cbtab2[] = {
    NULL, "BIT", "RES", "SET" };

static int numbytes;

static void byte1(char *s, unsigned o) {
    static char hextab[] = "0123456789abcdef";
    s[3] = hextab[o >> 4];
    s[4] = hextab[o & 15];
    numbytes = 2;
}
static void byte2(char *s, unsigned o) {
    static char hextab[] = "0123456789abcdef";
    s[6] = hextab[o >> 4];
    s[7] = hextab[o & 15];
    numbytes = 3;
}
static void byte3(char *s, unsigned o) {
    static char hextab[] = "0123456789abcdef";
    s[9] = hextab[o >> 4];
    s[10] = hextab[o & 15];
    numbytes = 4;
}
static void word1(char *s, unsigned w) {
    byte1(s, w & 0xff);
    byte2(s, w >> 8);
}
static void word2(char *s, unsigned w) {
    byte2(s, w & 0xff);
    byte3(s, w >> 8);
}

static const char *bdosfunc[] = {
    /* 00 */ "System Reset", "Console Input", "Console Output",
    /* 03 */ "Aux. Input", "Aux. Output", "List Output",
    /* 06 */ "Direct I/O", "Aux. Input Status", "Aux. Output Status",
    /* 09 */ "Print String", "Read Console Buffer", "Get Console Status",
    /* 12 */ "Return Version Number", "Reset Disk System",
    /* 14 */ "Select Disk", "Open File", "Close File",
    /* 17 */ "Search for First", "Search for Next", "Delete File",
    /* 20 */ "Read Sequential", "Write Sequential", "Make File",
    /* 23 */ "Rename File",    "Return Login Vector", "Return Current Disk",
    /* 26 */ "Set DMA Address", "Get Addr(Alloc)", "Write Protect Disk",
    /* 29 */ "Get R/O-Vector", "Set File Attributes", "Get Addr. of DPB",
    /* 32 */ "Set/Get User Code", "Read Random", "Write Random",
    /* 35 */ "Compute File Size", "Set Random Record", "Reset Drive",
    /* 38 */ "Access Drive", "Free Drive" };
/* extension: 40: change directory */

static char *disassemble(unsigned pc) {
    static char s[60];
    int o, m, r, b, w;

    numbytes = 1;
    if (pc >= BIOS) {
        switch (pc) {
	case 0xff6c:
        case BIOS:      sprintf(s, "BIOS COLDBOOT");
                        break;
	case 0xff6d:
        case BIOS+3:    sprintf(s, "BIOS WARMBOOT");
                        break;
	case 0xff6e:
        case BIOS+6:    sprintf(s, "BIOS Console Status");
                        break;
	case 0xff6f:
        case BIOS+9:    sprintf(s, "BIOS Console Input");
                        break;
	case 0xff70:
        case BIOS+0x0c: sprintf(s, "BIOS Console Output");
                        break;
	case 0xff71:
        case BIOS+0x0f: sprintf(s, "BIOS List Output");
                        break;
	case 0xff72:
        case BIOS+0x12: sprintf(s, "BIOS Punch Output");
                        break;
	case 0xff73:
        case BIOS+0x15: sprintf(s, "BIOS Reader Input");
                        break;
	case 0xff74:
        case BIOS+0x18: sprintf(s, "BIOS Home");
                        break;
	case 0xff75:
        case BIOS+0x1b: sprintf(s, "BIOS Select Disk");
                        break;
	case 0xff76:
        case BIOS+0x1e: sprintf(s, "BIOS Set Track");
                        break;
	case 0xff77:
        case BIOS+0x21: sprintf(s, "BIOS Set Sector");
                        break;
	case 0xff78:
        case BIOS+0x24: sprintf(s, "BIOS Set DMA");
                        break;
	case 0xff79:
        case BIOS+0x27: sprintf(s, "BIOS Read");
                        break;
	case 0xff7a:
        case BIOS+0x2a: sprintf(s, "BIOS Write");
                        break;
	case 0xff7b:
        case BIOS+0x2d: sprintf(s, "BIOS List Status");
                        break;
	case 0xff7c:
        case BIOS+0x30: sprintf(s, "BIOS Sector Translate");
                        break;
        default:        sprintf(s, "BIOS");
        }
        return s;
    } else if ((pc == BDOS || pc == 5) && z80regs.pc == pc) {
	if ((z80regs.bc & 0xff) < sizeof(bdosfunc) / sizeof(bdosfunc[0]))
            sprintf(s, "BDOS %s", bdosfunc[z80regs.bc & 0xff]);
        else
            sprintf(s, "BDOS (0x%02x)", z80regs.bc & 0xff);
        return s;
    }
    o = z80mem[pc];
    sprintf(s, "%02x             ", o);
    m = (o >> 3) & 7;
    r = o & 7;
    switch (o >> 6) {
    case 0: switch(o & 7) {
            case 4: sprintf(s+X, "INC %s", dtab1[m]);
                    break;
            case 5: sprintf(s+X, "DEC %s", dtab1[m]);
                    break;
            case 6: sprintf(s+X, "LD %s,0%02x", dtab1[m], b = z80mem[pc+1]);
                    byte1(s, b);
                    break;
            default:switch (o) {
                    case 0x22: case 0x2a: case 0x32: case 0x3a:
                    case 0x01: case 0x11: case 0x21: case 0x31:
                            w = z80mem[pc+1] + (z80mem[pc+2] << 8);
                            word1(s, w);
                            break;
                    case 0x08: case 0x10: case 0x18: case 0x20: case 0x28: case 0x30: case 0x38:
                            w = ((char *)z80mem)[pc+1];
                            byte1(s, w & 0xff);
                            w += pc + 2;
                            break;
                    }
                    sprintf(s+X, op1tab[o], w);
                    break;
            }
            break;
    case 1: if (o != 0x76)
                sprintf(s+X, "LD %s,%s", dtab1[m], dtab1[r]);
            else
                strcpy(s+X, "HALT");
            break;
    case 2: strcpy(s+X, dtab5[m]);
            strcat(s+X, dtab1[r]);
            break;
    case 3: switch(o & 7) {
            case 0: sprintf(s+X, "RET %s", dtab4[m]);
                    break;
            case 2: w = z80mem[pc+1] + (z80mem[pc+2] << 8);
                    word1(s, w);
                    sprintf(s+X, "JP %s,%04x", dtab4[m], w);
                    break;
            case 4: w = z80mem[pc+1] + (z80mem[pc+2] << 8);
                    word1(s, w);
                    sprintf(s+X, "CALL %s,%04x", dtab4[m], w);
                    break;
            case 6: w = z80mem[pc+1];
                    sprintf(s+X, "%s0%02x", dtab5[m], w);
                    byte1(s, w);
                    break;
            case 7: sprintf(s+X, "RST %03xH", m << 3);
                    break;
            default:switch (o) {
                    case 0xc3: case 0xcd:
                            w = z80mem[pc+1] + (z80mem[pc+2] << 8);
                            word1(s, w);
                            break;
                    case 0xd3: case 0xdb:
                            w = z80mem[pc+1];
                            break;
                    case 0xcb:
                            o = z80mem[pc+1];
                            byte1(s, o);
                            m = (o >> 3) & 7;
                            r = o & 7;
                            if (o & 0xc0)
                                sprintf(s+X, "%s %d,%s", cbtab2[o >> 6], m, dtab1[r]);
                            else
                                sprintf(s+X, "%s %s", cbtab1[m], dtab1[r]);
                            return s;
                    case 0xed:
                            o = z80mem[pc+1];
                            byte1(s, o);
                            switch (o & 0xcf) {
                            case 0x42:  sprintf(s+X, "SBC HL,%s", dtab2[(o & 0x30) >> 4]);
                                        break;
                            case 0x43:  w = z80mem[pc+2] + (z80mem[pc+3] << 8);
                                        word2(s, w);
                                        sprintf(s+X, "LD (%04x),%s", w, dtab2[(o & 0x30) >> 4]);
                                        break;
                            case 0x4a:  sprintf(s+X, "ADC HL,%s", dtab2[(o & 0x30) >> 4]);
                                        break;
                            case 0x4b:  w = z80mem[pc+2] + (z80mem[pc+3] << 8);
                                        word2(s, w);
                                        sprintf(s+X, "LD %s,(%04x)", dtab2[(o & 0x30) >> 4], w);
                                        break;
                            default:    switch (o) {
                                        case 0x44:  sprintf(s+X, "NEG");
                                                    break;
                                        case 0xa0:  sprintf(s+X, "LDI");
                                                    break;
                                        case 0xb0:  sprintf(s+X, "LDIR");
                                                    break;
                                        case 0xb8:  sprintf(s+X, "LDDR");
                                                    break;
                                        default:    sprintf(s+X, "unrec.");
                                        }
                            }
                            return s;
                    }
                    sprintf(s+X, op2tab[o & 63], w);
                    break;
            }
            break;
    }
    return s;
}

void disassem(unsigned *loc, int num) {
    while (num--) {
	printf("%04x %s\n", *loc, disassemble(*loc));
	*loc = (*loc + numbytes) & 0xffff;
    }
}

static char *flags(unsigned f) {
    static char s[10];
    const char *fl = "SZ0H0VNC";
    int i;
    strcpy(s, "        ");
    for (i = 0; i < 8; ++i) {
        if (f & 0x80)
            s[i] = fl[i];
        f <<= 1;
    }
    return s;
}

void dispregs(unsigned pc) {
    printf("%s A=%02x BC=%04x DE=%04x HL=%04x SP=%04x PC=%04x  %s\n",
	   flags(z80regs.af >> 8), z80regs.af & 0xff,
	   z80regs.bc, z80regs.de, z80regs.hl, z80regs.sp,
	   pc, disassemble(pc));
    fflush(stdout);
}

void dispregs2(void) {
    printf("%s a=%02x bc=%04x de=%04x hl=%04x IX=%04x IY=%04x  I=%02x R=%02x\n",
	   flags(z80regs.af2 >> 8), z80regs.af2 & 0xff,
	   z80regs.bc2, z80regs.de2, z80regs.hl2, z80regs.ix, z80regs.iy, z80regs.ir >> 8, z80regs.ir & 0xff);
    fflush(stdout);
}

void memdump(unsigned addr, int lines) {
    int i, j;
    for (i = 0; i < lines; ++i) {
	printf("%04x ", addr);
	for (j = 0; j < 16; ++j) {
	    if (j == 8)
		printf(" -");
	    printf(" %02x", z80mem[addr+j]);
	}
	printf("  ");
	for (j = 0; j < 16; ++j) {
	    if (j == 8)
		printf(" ");
	    printf("%c", z80mem[addr+j] < ' ' ? '.' : z80mem[addr+j]);
	}
	printf("\n");
	addr += 16;
    }
}
