/***********************************************************************/
/* Emulator belonging to the Chessiverse 1.1                           */
/* This program reads a code image from the state file 'chessi.sav',   */
/* on which the urviving programs are periodically saved. This code    */
/* is then run on a number of randomly generated board position,       */
/* printing a trace of the executed code, including disassembly of all */
/* executed instruction, and register contents specifying the machine  */
/* state.                                                              */
/***********************************************************************/
/* 1.0.1:       OUT instruction in interpreter as wrong if mode != 7   */
#include <stdio.h>

/* Parametric description of tournament */
#define CHAMBERS 1              /* number of different enironments */
#define BOARDS   50
#define PROGRAMS (2*BOARDS)     /* number of programs per chamber  */
#define ROUNDS   100            /* number of games per season      */
#define RECORD   1000           /* save every so many seasons      */
#define BRDSIZE  64             /* size of game board              */
#define TWEIGHT  0              /* weight of CPU time in score     */
#define TLIMIT   1000           /* timeout in nr of instructions   */

/* Description of virtual machines */
#define DATAMEM 0x1000
#define PROGMEM 0x1000
#define FRAME   0x100   /* size of local-variables stack frame */

/* piece encodings in environment */
#define WHITE  8
#define BLACK 16
#define WPAWN  1
#define BPAWN  2
#define KNIGHT 3
#define BISHOP 4
#define ROOK   5
#define QUEEN  6
#define KING   7

/* Breeding parameters */
#define SURVIVORS 25
#define IMPORTS    0
#define PARENTS   (SURVIVORS + IMPORTS)
#define MUTATIONS 10

unsigned short int Prog[PROGMEM];
unsigned char Data[DATAMEM];
unsigned char OutBuf[256];

char Table[BRDSIZE+2];             /* chess boads, incl. stm and cnt30   */

unsigned long long int Seed = 0x55555555;

int Draw(int n)
/* generate random integer from 0 to n-1 */
{
    Seed = Seed * 0x20001083;

    return ((Seed>>48)*n >> 16);
}

char *opcotab[32] = {
"ADD", "INC",
"SUB", "DEC",
"AND", "LSL",
"OR ", "LSR",
"XOR", "ASR",
"MUL", "NEG",
"UML", "COM",
"LD ", "CLR",
"BIT", "TST",
NULL,  "ST ",
NULL,  "IDX",
NULL,  NULL,
NULL,  NULL,
"rmw", "RTS",
"Bcc", "SOB",
"JSR", "JSR"
};

char *conditab[8] = {
"BEQ", "BNE", "BPL", "BMI", "BCC", "BCS", "BVC", "BVS" };

char *modetab[] = {
" %x%x(SP)", "1%x%x(SP)", "2%x%x(SP)", "3%x%x(SP)",
" %x%x", "*%x%x", "*%x%x(SP)", "$%x%x"
};

disas(unsigned short int *Prog, int addr)
/* disassemble one line at addr in given program */
{
    int i, j, k, opc, mode, offs, instr;

    instr = Prog[addr];

    printf("%4x %4x    ", addr, instr);

    if((instr&0xF000) == 0xF000) printf("JSR %3x", instr&0xFFF); else
    if((instr&0xFF00) == 0x9700) printf("INP %2x", instr&0xFF); else
    if((instr&0xFF00) == 0xA700) printf("OUT %2x", instr&0xFF); else
    {
        opc  = instr >> 11;
        mode = instr >> 8 & 7;
        offs = instr & 0xFF;

        if(opcotab[opc] == NULL) printf("NOP"); else
        if(opc == 26) printf("%sA", opcotab[2*mode+1]); else
        if(opc == 27) printf("RTS"); else
        if(opc == 28) printf("%s %3x", conditab[mode], addr + 1 + offs); else
        if(opc == 29) printf("SOB %d, %3x", mode, addr + 1 - offs); else
        {
            printf("%s ", opcotab[opc]);
            if((opc&1) && mode == 7) printf("@");
            else printf(modetab[mode], offs>>4, offs&15);
        }
    }
    printf("\n");
}

int Run(unsigned short int *Code, unsigned char *Port,
                                  int MaxPort, int OPlim, int Ilim)
/* interpret virtual-machine code passed in 'Code' for 'Ilim'      */
/* instructions, or until 'OPlim' outputs are produced.            */
{
    unsigned int SP, PC, IX, IR, ADR, ACCU, CC, VAL, PNR, Opcode;
    int i, j, k, h, Mode, Optr=0, Icnt=0;

    for(i=0; i<DATAMEM; i++) Data[i] = 0; /* clear data memory       */

    PC = 0x200; SP = DATAMEM - FRAME;   /* initialize registers     */
    IX = ADR = PNR = 0;
    printf(" SP   IX PNR ADR   CC ACCU  PC   IR     instruction\n");

    while(1)
    {
        if(Icnt++ > Ilim) return Icnt;  /* Stop when time-out       */
        printf("%3x %4x  %2x %3x %4x %4x ",
                        SP, IX, PNR, ADR, CC&0xFFFF, ACCU&0xFFFF);
        disas(Code, PC);
        PC &= PROGMEM-1;                
        IR = Code[PC++];                /* fetch next instruction   */
        Opcode = IR>>11;

        if(Opcode >= 26)                /* jump, branch or reg-mode */
        {                               /*   have deviating formats */
            if(Opcode >= 30)            /* JSR <address>            */
            {
                SP -= FRAME;            /* reserve new stack frame  */
                if(SP<0x100) return TLIMIT;  /* stack overflow      */
                Data[SP+8] = PC;        /* save return address      */
                Data[SP+9] = PC>>8;
                PC = IR & (PROGMEM-1);  /* load program counter     */
                continue;
            }

            Mode = IR>>8 & 7;

            switch(Opcode)
            {
            case 26:                    /* accumulator unaries      */
                switch(Mode)
                {
                case 0: CC = ++ACCU; break;               /* INC %a */
                case 1: CC = --ACCU; break;               /* DEC %a */
                case 2: CC = ACCU <<= 1; break;           /* LSL %a */
                case 3: CC = ACCU >>= 1; break;           /* LSR %a */
                case 4: CC = ACCU >>= 1; break;           /* ASR %a */
                case 5: CC = ACCU = -ACCU; break;         /* NEG %a */
                case 6: CC = ACCU = ~ACCU; break;         /* COM %a */
                case 7: CC = ACCU = 0; break;             /* CLR %a */
                } continue;
            case 27:                    /* RTS */
                PC = Data[SP+8] + (Data[SP+9]<<8);
                if(SP >= DATAMEM-FRAME) return TLIMIT;  /* stack underflow */
                SP += FRAME; continue;
            case 28:                    /* conditional branches     */
                switch(Mode)
                {       /* Branch forward if condition true */
                case 0: if(CC==0        ) PC += IR & 0xFF; continue; /*BEQ*/
                case 1: if(CC!=0        ) PC += IR & 0xFF; continue; /*BNE*/
                case 2: if((CC&0x80)==0 ) PC += IR & 0xFF; continue; /*BPL*/
                case 3: if((CC&0x80)!=0 ) PC += IR & 0xFF; continue; /*BMI*/
                case 4: if((CC&~0xFF)==0) PC += IR & 0xFF; continue; /*BCC*/
                case 5: if((CC&~0xFF)!=0) PC += IR & 0xFF; continue; /*BCS*/
                case 6: if((CC^CC>>8)&0x80==0) PC += IR & 0xFF; continue;
                case 7: if((CC^CC>>8)&0x80!=0) PC += IR & 0xFF; continue;
                }
            case 29:                    /* SOB ctr, <backward> */
                if(CC=Data[SP+Mode]--) PC -= IR & 0xFF;
                continue;
            }
        }
        if(Opcode>21) continue;

        /* Normal addressing mode, calculate address and fetch data */
        Mode = IR>>8 & 7; 
        h = 0;
        switch(Mode)
        {
        case 1:                         /* adjust offset for access */
        case 2:                         /* of deeper stack frames   */
        case 3:
            h = Mode * FRAME; 
        case 0:                         /* local-frame address      */
        case 6:                         /* add stack pointer        */
            h += SP;
        case 4:                         /* base address + indexing  */
        case 5:
            h += (IX + IR) & 0xFF; IX = 0;
            ADR = h & DATAMEM-1;        /* prevent memory violation */
            if(Mode>4) ADR = Data[ADR]; /* mode 5&6: indirection    */
            VAL = Data[ADR]; break;     /* fetch data               */
        case 7:
            VAL = IR & 0xFF;            /* immediate mode           */
            PNR = (VAL + IX) & 0xFF;    /* Port number              */
            if(IX) VAL = Code[PNR];     /* or constant-table mode   */
        }

        /* perform operation, remembr result in CC for branching    */
        switch(Opcode)
        {
        /* binary operations, performed in accumulator */
        case 0:  CC = ACCU += VAL; continue;         /* ADD <memory> */
        case 2:  CC = ACCU -= VAL; continue;         /* SUB <memory> */
        case 4:  CC = ACCU &= VAL; continue;         /* AND <memory> */
        case 6:  CC = ACCU |= VAL; continue;         /* OR  <memory> */
        case 8:  CC = ACCU ^= VAL; continue;         /* XOR <memory> */
        case 10: CC = ACCU = (char) ACCU * (char) VAL;
                                   continue;         /* MUL <memory> */
        case 12: CC = ACCU *= VAL; continue;         /* UML <memory> */
        case 14: CC = ACCU  = VAL; continue;         /* LD  <memory> */
        case 16: CC = ACCU &= VAL; continue;         /* AND <memory> */
        case 18: if(Mode==7)                         /* INP <port>   */
                 {   CC = ACCU = PNR < MaxPort ? Port[PNR] : Draw(256);
                     IX = 0;
                 } continue;
        case 20: if(Mode==7)
                 {   OutBuf[Optr++] = ACCU;          /* OUT <port>   */
                     if(Optr>=OPlim) return Icnt;   /* enough output */
                 } continue;

        /* unary operators, directly in memory (read-modify-write)   */
        case 1:  Data[ADR] = CC = Data[ADR] + 1; continue; /* INC <memory> */
        case 3:  Data[ADR] = CC = Data[ADR] - 1; continue; /* DEC <memory> */
        case 5:  Data[ADR] = CC = Data[ADR] <<1; continue; /* LSL <memory> */
        case 7:  Data[ADR] = CC = Data[ADR] >>1; continue; /* LSR <memory> */
        case 9:  Data[ADR] = CC = Data[ADR] >>1; continue; /* ASR <memory> */
        case 11: Data[ADR] = CC = -Data[ADR]; continue;    /* NEG <memory> */
        case 13: Data[ADR] = CC = ~Data[ADR]; continue;    /* COM <memory> */
        case 15: Data[ADR] = CC = 0; continue;             /* CLR <memory> */
        case 17:             CC = Data[ADR]; continue;     /* TST <memory> */
        case 19: Data[ADR] = CC = ACCU; continue;          /* ST  <memory> */
        case 21: IX = 0x8000 | Data[ADR]; continue;        /* IDX <memory> */
        }

    }
}


void IniBoard(char *board, int chamber)
/* Initialize new game according to chamber rules */
{
    int i, j;

    for(i=0; i<BRDSIZE+2; i++) board[i] = 0;

    switch(chamber)
    {   /* add rules for other chambers here */
    default:
        /* Position of KQK end-game */
        board[Draw(64)+2] = WHITE + QUEEN;
        i = Draw(64);
        board[i+2] = WHITE + KING;
        j = Draw(63); if(j>=i) j++; /* make sure Kings do not */
        board[j+2] = BLACK + KING;  /* end up on same square  */
        board[0] = Draw(2);         /* randomize side to move */
    }
}


char Dir[] = {0,0,4,22,17,8,13,13};
char Slider[] = {0,0,0,0,1,1,1,0};
char Xvec[] = {0,1,-1,0, 0,1,-1,0, 1,0,-1, 0,0, 1,0,-1, 0,1,-1, 1,-1,0,
               2,-2,1,-1, 2,-2, 1,-1,0};
char Yvec[] = {1,0, 0,0,-1,0, 0,0, 0,1, 0,-1,0, 0,1, 0,-1,1, 1,-1,-1,0,
               1, 1,2, 2,-1,-1,-2,-2,0};

int LegalMove(char *Board, unsigned char *Move)
/* perform move if legal and check for K-capture */
/* Return -1, 0 or 1 for illegal, ongoing or won */
{
    int Color, Piece, PieceType, Victim, Xco, Yco, ToSquare, Ray, i;

    Color = Board[0]*8 + 8;           /* 8 or 16 */

    if(Move[0] >= BRDSIZE || Move[1] >= BRDSIZE)
        return(-1);            /* invalid syntax */

    Piece = Board[Move[0]+2];
    if((Piece&24) != Color)
        return(-1);            /* no piece of us */

    PieceType = Piece & 7;     /* extract type   */
    for(i = Dir[PieceType]; Xvec[i]|Yvec[i]; i++)
    {
        Xco = Move[0]&7;
        Yco = Move[0]&070;
        do{
            Xco += Xvec[i-1];
            Yco += Yvec[i-1];
            if(Xco&~7|Yco&~070) break; /* off board */
            ToSquare = Xco|Yco;
            Victim = Board[ToSquare+2];
            if(Victim&Color) break;    /* capt. own */
            if(Piece <= 2 && (Victim != 0 && Xvec[i-1] == 0 ||
                              Victim == 0 && Xvec[i-1] != 0))
                break; /* Bad Pawn mode */
            if(ToSquare == Move[1])    /* move found */
            {   /* perform move */
                if(PieceType == 1 && Yco == 070 ||
                   PieceType == 2 && Yco == 0)
                    Piece = Color + QUEEN;    /* Promote to Queen */
                Board[ToSquare+2] = Piece;
                Board[1]++;                   /* 30-move counter  */
                if(Victim || PieceType <= 2) Board[1] = 0;
                if((Victim&7) == KING) return 1; /* King captured */
                return 0;    /* signal caller that move was legal */
            }
            Ray = Slider[PieceType];
            if(Xvec[i] == 0 && (PieceType == 1 && Yco == 020 ||
                                PieceType == 2 && Yco == 050) )
                Ray++; /* extend Pawn */
        } while(Victim==0 && Ray);
    }

    return(-1); /* move not found among legal ones */
}

char Image[] = ".?+nbrqk?*?NBRQK.";

void PrintBoard(unsigned char *b)
{
    int i, j, k;

    printf("\n   a b c d e f g h    %s\n", b[0]?"B":" ");
    for(i=7; i>=0; i--)
    {
        printf("%d ", i+1);
        for(j=0; j<8; j++)
            printf(" %c", Image[b[8*i+j+2]&15]);
        printf("  %d\n", i+1);
    }
    printf("   a b c d e f g h    %s\n", b[0]?" ":"W");
}

main()
{
    int i, j, k;
    FILE *f;

    f = fopen("chessi.sav", "rb");
    if(f==NULL) exit(0);
    fread((void *) Prog, 12, 1, f);
    fread((void *) Prog, sizeof (short int), PROGMEM, f);
    fclose(f);

    while(1)
    {
        IniBoard(Table, 0);
        PrintBoard(Table);
        i = Run(Prog, Table, BRDSIZE+2, 2, TLIMIT);
        printf("time=%d, output = %o-%o (%c%c-%c%c)\n",
                i, OutBuf[0], OutBuf[1],
                'a'+(OutBuf[0]&7), '1'+(OutBuf[0]>>3&7),
                'a'+(OutBuf[1]&7), '1'+(OutBuf[1]>>3&7) );
        printf("Result = %d\n", j = LegalMove(Table, OutBuf));
        if(j>=-10) getchar();
    }
}