Herramientas de usuario

¡Esta es una revisión vieja del documento!


c
//***********************************************************************
//
//  Kim-1 MicroChess (c) 1976-2005 Peter Jennings, www.benlo.com
//  6502 emulation   (c) 2005 Bill Forster
//
//  Runs an emulation of the Kim-1 Microchess on any standard C platform
//
//***********************************************************************
 
// All rights reserved.
 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. The name of the author may not be used to endorse or promote products
//    derived from this software without specific prior written permission.
 
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES// LOSS OF USE,
// DATA, OR PROFITS// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
//**********************************************************************
//*
//*  Part 1
//*  ------
//*  Create virtual 6502 platform using standard C facilities.
//*  Goal is to run Microchess on any platform supporting C.
//*
//*       Part 1 added July 2005 by Bill Forster (www.triplehappy.com)
//**********************************************************************
 
// Standard library include files
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <setjmp.h>
#include <math.h>
 
// Use <setjmp.h> macros and functions to emulate the "jump to reset
//  stack pointer then restart program" behaviour used by microchess
jmp_buf jmp_chess;
#define EXIT        -1
#define RESTART     -2
void RESTART_CHESS( void )  // start CHESS program with reset stack
{
    longjmp( jmp_chess, RESTART );
}
void EXIT_TO_SYSTEM( void ) // return to operating system
{
    longjmp( jmp_chess, EXIT );
}
 
// 6502 emulation memory
typedef unsigned char byte;
byte zeropage[256];
byte stack[256];
byte stack_cy[256];
byte stack_v[256];
 
// 6502 emulation registers
byte reg_a, reg_f, reg_x, reg_y, reg_s, reg_cy, reg_v, temp_cy;
unsigned int temp1, temp2;
 
// Debug stuff
#if 0
    #define DBG , register_dump()
#else
    #define DBG
#endif
void register_dump( void )
{
    printf( "A=%02x X=%02x Y=%02x S=%02x F=%02X CY=%d V=%d\n",
        reg_a, reg_x, reg_y, reg_s, reg_f, reg_cy, reg_v );
}
 
// 6502 emulation macros - register moves
#define T(src,dst)          reg_f = (dst) = (src)    DBG
#define A reg_a
#define S reg_s
#define X reg_x
#define Y reg_y
#define TYA                 T(Y,A)
#define TXS                 T(X,S)
#define TAX                 T(A,X)
#define TAY                 T(A,Y)
#define TSX                 T(S,X)
#define TXA                 T(X,A)
 
// 6502 emulation macros - branches
#define BEQ(label)          if( reg_f == 0 )     goto label
#define BNE(label)          if( reg_f != 0 )     goto label
#define BPL(label)          if( ! (reg_f&0x80) ) goto label
#define BMI(label)          if( reg_f & 0x80 )   goto label
#define BCC(label)          if( !reg_cy )        goto label
#define BCS(label)          if( reg_cy )         goto label
#define BVC(label)          if( !reg_v )         goto label
#define BVS(label)          if( reg_v )          goto label
#define BRA(label) /*extra*/ goto label
 
// 6502 emulation macros - call/return from functions
#define JSR(func)           func()
#define RTS                 return
 
// 6502 emulation macros - jump to functions, note that in
//  assembly language jumping to a function is a more efficient
//  way of calling the function then returning, so we emulate
//  that in the high level language by (actually) calling then
//  returning. There is no JEQ 6502 opcode, but it's useful to
//  us so we have made it up! (like BRA, SEV)
#define JMP(func)           if( 1 )          { func(); return; } \
                            else // else eats ';'
#define JEQ(func) /*extra*/ if( reg_f == 0 ) { func(); return; } \
                            else // else eats ';'
 
// 6502 emulation macros - load registers
//  Addressing conventions;
//   default addressing mode is zero page, else indicate with suffix;
//   i = immediate
//   x = indexed, zero page
//   f = indexed, not zero page (f for "far")
#define ZP(addr8)           (zeropage[ (byte) (addr8) ])
#define ZPX(addr8,idx)      (zeropage[ (byte) ((addr8)+(idx)) ])
#define LDAi(dat8)          reg_f = reg_a = dat8                  DBG
#define LDAx(addr8,idx)     reg_f = reg_a = ZPX(addr8,idx)        DBG
#define LDAf(addr16,idx)    reg_f = reg_a = (addr16)[idx]         DBG
#define LDA(addr8)          reg_f = reg_a = ZP(addr8)             DBG
#define LDXi(dat8)          reg_f = reg_x = dat8                  DBG
#define LDX(addr8)          reg_f = reg_x = ZP(addr8)             DBG
#define LDYi(dat8)          reg_f = reg_y = dat8                  DBG
#define LDY(addr8)          reg_f = reg_y = ZP(addr8)             DBG
#define LDYx(addr8,idx)     reg_f = reg_y = ZPX(addr8,idx)        DBG
 
// 6502 emulation macros - store registers
#define STA(addr8)          ZP(addr8)      = reg_a                DBG
#define STAx(addr8,idx)     ZPX(addr8,idx) = reg_a                DBG
#define STX(addr8)          ZP(addr8)      = reg_x                DBG
#define STY(addr8)          ZP(addr8)      = reg_y                DBG
#define STYx(addr8,idx)     ZPX(addr8,idx) = reg_y                DBG
 
// 6502 emulation macros - set/clear flags
#define CLD            // luckily CPU's BCD flag is cleared then never set
#define CLC                 reg_cy = 0                            DBG
#define SEC                 reg_cy = 1                            DBG
#define CLV                 reg_v  = 0                            DBG
#define SEV /*extra*/       reg_v  = 1  /*avoid problematic V emulation*/ DBG
 
// 6502 emulation macros - accumulator logical operations
#define ANDi(dat8)          reg_f = (reg_a &= dat8)               DBG
#define ORA(addr8)          reg_f = (reg_a |= ZP(addr8))          DBG
 
// 6502 emulation macros - shifts and rotates
#define ASL(addr8)          reg_cy = (ZP(addr8)&0x80) ? 1 : 0,  \
                            ZP(addr8) = ZP(addr8)<<1,           \
                            reg_f = ZP(addr8)                     DBG
#define ROL(addr8)          temp_cy = (ZP(addr8)&0x80) ? 1 : 0, \
                            ZP(addr8) = ZP(addr8)<<1,           \
                            ZP(addr8) |= reg_cy,                \
                            reg_cy = temp_cy,                   \
                            reg_f = ZP(addr8)                     DBG
#define LSR                 reg_cy = reg_a & 0x01,              \
                            reg_a  = reg_a>>1,                  \
                            reg_a  &= 0x7f,                     \
                            reg_f = reg_a                         DBG
 
// 6502 emulation macros - push and pull
#define PHA                 stack[reg_s--]  = reg_a               DBG
#define PLA                 reg_a           = stack[++reg_s]      DBG
#define PHY                 stack[reg_s--]  = reg_y               DBG
#define PLY                 reg_y           = stack[++reg_s]      DBG
#define PHP                 stack   [reg_s] = reg_f,       \
                            stack_cy[reg_s] = reg_cy,      \
                            stack_v [reg_s] = reg_v,       \
                            reg_s--                               DBG
#define PLP                 reg_s++,                       \
                            reg_f  = stack   [reg_s],      \
                            reg_cy = stack_cy[reg_s],      \
                            reg_v  = stack_v [reg_s]              DBG
 
// 6502 emulation macros - compare
#define cmp(reg,dat)        reg_f  = ((reg) - (dat)), \
                            reg_cy = ((reg) >= (dat) ? 1 : 0)  DBG
#define CMPi(dat8)          cmp( reg_a, dat8 )
#define CMP(addr8)          cmp( reg_a, ZP(addr8) )
#define CMPx(addr8,idx)     cmp( reg_a, ZPX(addr8,idx) )
#define CMPf(addr16,idx)    cmp( reg_a, (addr16)[idx] )
#define CPXi(dat8)          cmp( reg_x, dat8 )
#define CPXf(addr16,idx)    cmp( reg_x, (addr16)[idx] )
#define CPYi(dat8)          cmp( reg_y, dat8 )
 
// 6502 emulation macros - increment,decrement
#define DEX                 reg_f = --reg_x                       DBG
#define DEY                 reg_f = --reg_y                       DBG
#define DEC(addr8)          reg_f = --ZP(addr8)                   DBG
#define INX                 reg_f = ++reg_x                       DBG
#define INY                 reg_f = ++reg_y                       DBG
#define INC(addr8)          reg_f = ++ZP(addr8)                   DBG
#define INCx(addr8,idx)     reg_f = ++ZPX(addr8,idx)              DBG
 
// 6502 emulation macros - add
#define adc(dat)            temp1 = reg_a,                   \
                            temp2 = (dat),                   \
                            temp1 += (temp2+(reg_cy?1:0)),   \
                            reg_f = reg_a = (byte)temp1,     \
                            reg_cy = ((temp1&0xff00)?1:0)         DBG
#define ADCi(dat8)          adc( dat8 )
#define ADC(addr8)          adc( ZP(addr8) )
#define ADCx(addr8,idx)     adc( ZPX(addr8,idx) )
#define ADCf(addr16,idx)    adc( (addr16)[idx] )
 
// 6502 emulation macros - subtract
//   (note that both as an input and an output cy flag has opposite
//    sense to that used for adc(), seems unintuitive to me)
#define sbc(dat)            temp1 = reg_a,                   \
                            temp2 = (dat),                   \
                            temp1 -= (temp2+(reg_cy?0:1)),   \
                            reg_f = reg_a = (byte)temp1,     \
                            reg_cy = ((temp1&0xff00)?0:1)         DBG
#define SBC(addr8)          sbc( ZP(addr8) )
#define SBCx(addr8,idx)     sbc( ZPX(addr8,idx) )
 
// Test some of the trickier opcodes (hook this up as needed)
void test_function( void )
{
    byte hi, lo;
                LDAi    (0x33);     // 0x4444 - 0x3333 = 0x1111
                STA     (0);
                STA     (1);
                LDAi    (0x44);
                SEC;
                SBC     (0);
                lo      = reg_a;
                LDAi    (0x44);
                SBC     (1);
                hi      = reg_a;
 
                LDAi    (0x44);     // 0x3333 - 0x4444 = 0xeeef
                STA     (0);
                STA     (1);
                LDAi    (0x33);
                SEC;
                SBC     (0);
                lo      = reg_a;
                LDAi    (0x33);
                SBC     (1);
                hi      = reg_a;
 
                LDAi    (0x33);     // 0x3333 + 0x4444 = 0x7777
                STA     (0);
                STA     (1);
                LDAi    (0x44);
                CLC;
                ADC     (0);
                lo      = reg_a;
                LDAi    (0x44);
                ADC     (1);
                hi      = reg_a;
}
 
 
//**********************************************************************
//*
//*  Part 2
//*  ------
//*  Original microchess program by Peter Jennings, www.benlo.com
//*  In this form, 6502 assembly language has been minimally transformed
//*  to run with the virtual 6502 in C facilities created in part 1.
//*   (New comments by Bill Forster are identified with text (WRF))
//**********************************************************************
 
//
// page zero variables
//
const byte BOARD     = 0x50;
const byte BK        = 0x60;
const byte PIECE     = 0xB0;
const byte SQUARE    = 0xB1;
const byte SP2       = 0xB2;
const byte SP1       = 0xB3;
const byte INCHEK    = 0xB4;
const byte STATE     = 0xB5;
const byte MOVEN     = 0xB6;
const byte REV       = 0xB7;
const byte OMOVE     = 0xDC;
const byte WCAP0     = 0xDD;
const byte COUNT     = 0xDE;
const byte BCAP2     = 0xDE;
const byte WCAP2     = 0xDF;
const byte BCAP1     = 0xE0;
const byte WCAP1     = 0xE1;
const byte BCAP0     = 0xE2;
const byte MOB       = 0xE3;
const byte MAXC      = 0xE4;
const byte CC        = 0xE5;
const byte PCAP      = 0xE6;
const byte BMOB      = 0xE3;
const byte BMAXC     = 0xE4;
const byte BMCC      = 0xE5;     // was BCC (TASS doesn't like it as a label)
const byte BMAXP     = 0xE6;
const byte XMAXC     = 0xE8;
const byte WMOB      = 0xEB;
const byte WMAXC     = 0xEC;
const byte WCC       = 0xED;
const byte WMAXP     = 0xEE;
const byte PMOB      = 0xEF;
const byte PMAXC     = 0xF0;
const byte PCC       = 0xF1;
const byte PCP       = 0xF2;
const byte OLDKY     = 0xF3;
const byte BESTP     = 0xFB;
const byte BESTV     = 0xFA;
const byte BESTM     = 0xF9;
const byte DIS1      = 0xFB;
const byte DIS2      = 0xFA;
const byte DIS3      = 0xF9;
const byte temp      = 0xFC;
 
// (WRF) For C version, data definitions precede code references to data
byte SETW[]   = {       0x03, 0x04, 0x00, 0x07, 0x02, 0x05, 0x01, 0x06,
                        0x10, 0x17, 0x11, 0x16, 0x12, 0x15, 0x14, 0x13,
                        0x73, 0x74, 0x70, 0x77, 0x72, 0x75, 0x71, 0x76,
                        0x60, 0x67, 0x61, 0x66, 0x62, 0x65, 0x64, 0x63
                };
 
byte MOVEX[]  = {       0x00, 0xF0, 0xFF, 0x01, 0x10, 0x11, 0x0F, 0xEF, 0xF1,
                        0xDF, 0xE1, 0xEE, 0xF2, 0x12, 0x0E, 0x1F, 0x21
                };
 
byte POINTS[] = {       0x0B, 0x0A, 0x06, 0x06, 0x04, 0x04, 0x04, 0x04,
                        0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02
                };
 
byte OPNING[] = {       0x99, 0x25, 0x0B, 0x25, 0x01, 0x00, 0x33, 0x25,
                        0x07, 0x36, 0x34, 0x0D, 0x34, 0x34, 0x0E, 0x52,
                        0x25, 0x0D, 0x45, 0x35, 0x04, 0x55, 0x22, 0x06,
                        0x43, 0x33, 0x0F, 0xCC
                };
 
// (WRF) level information:
//       | Level       | addr=02F2 |  addr=018B
//       |             | (level1)  |  (level2)
//       +-------------+-----------+-------------
//       | SUPER BLITZ |    00     |    FF
//       | BLITZ       |    00     |    FB
//       | NORMAL      |    08     |    FB
static byte level1=8;
static byte level2=0xfb;
 
// (WRF) Forward declarations
void CHESS( void );
void JANUS( void );
void INPUT( void );
void DISP( void );
void GNMZ( void );
void GNMX( void );
void GNM( void );
void RUM( void );
void STRV( void );
void SNGMV( void );
void LINE( void );
void REVERSE( void );
void CMOVE( void );
void RESET( void );
void GENRM( void );
void UMOVE( void );
void MOVE( void );
void CKMATE( void );
void GO( void );
void DISMV( void );
void STRATGY( void );
void POUT( void );
void POUT5(void );
void POUT8(void );
void POUT9( void );
void POUT10( void );
void POUT12( void );
void POUT13( void );
void KIN( void );
void syskin( void );
void syschout( void );
void syshexout( void );
void PrintDig( void );
 
// WRF debug stuff
static void show_move_evaluation( int ivalue );
static void show_move_generation( byte src, byte dst );
static int bool_show_move_evaluation;
static int bool_show_move_generation;
 
// Start here; Was  *=$1000   ; load into RAM @ $1000-$15FF
int main( int argc, char* argv[] )
{
                LDAi    (0x00);             // REVERSE TOGGLE
                STA     (REV);
             // JSR     (Init_6551);
                if( EXIT != setjmp(jmp_chess) )
                    CHESS();    // after setjmp() and then any
                                //  subsequent RESTART_CHESS()
                return(0);  // after EXIT_TO_SYSTEM()
}
 
void CHESS( void )
{
CHESS_BEGIN:                                //
                CLD;                        // INITIALIZE
                LDXi    (0xFF);             // TWO STACKS
                TXS;
                LDXi    (0xC8);
                STX     (SP2);
//
//       ROUTINES TO LIGHT LED
//       DISPLAY AND GET KEY
//       FROM KEYBOARD
//
/*OUT:*/        JSR     (POUT);             // DISPLAY AND
                JSR     (KIN);              // GET INPUT   *** my routine waits for a keypress
//              CMP     (OLDKY);            // KEY IN ACC  *** no need to debounce
//              BEQ     (OUT);              // (DEBOUNCE)
//              STA     (OLDKY);
//
                CMPi    (0x43);             // [C]
                BNE     (NOSET);            // SET UP
                LDXi    (0x1F);             // BOARD
WHSET:          LDAf    (SETW,X);           // FROM
                STAx    (BOARD,X);          // SETW
                DEX;
                BPL     (WHSET);
                LDXi    (0x1B);             // *ADDED
                STX     (OMOVE);            // INITS TO 0xFF
                LDAi    (0xCC);             // Display CCC
                BNE     (CLDSP);
//
NOSET:          CMPi    (0x45);             // [E]
                BNE     (NOREV);            // REVERSE
                JSR     (REVERSE);          // BOARD IS
                SEC;
                LDAi    (0x01);
                SBC     (REV);
                STA     (REV);              // TOGGLE REV FLAG
                LDAi    (0xEE);             // IS
                BNE     (CLDSP);
//
NOREV:          CMPi    (0x40);             // [P]
                BNE     (NOGO);             // PLAY CHESS
                JSR     (GO);
CLDSP:          STA     (DIS1);             // DISPLAY
                STA     (DIS2);             // ACROSS
                STA     (DIS3);             // DISPLAY
                BNE     (CHESS_BEGIN);
//
NOGO:           CMPi    (0x0D);             // [Enter]
                BNE     (NOMV);             // MOVE MAN
                JSR     (MOVE);             // AS ENTERED
                JMP     (DISP);             //
NOMV:           CMPi    (0x41);             // [Q] ***Added to allow game exit***
                BEQ     (DONE);             // quit the game, exit back to system.
                JMP     (INPUT);            //
DONE:           JMP     (EXIT_TO_SYSTEM);   // JMP (0xFF00); // *** MUST set this to YOUR OS starting address
}
 
//
//       THE ROUTINE JANUS DIRECTS THE
//       ANALYSIS BY DETERMINING WHAT
//       SHOULD OCCUR AFTER EACH MOVE
//       GENERATED BY GNM
//
//
//
void JANUS( void )
{               LDX     (STATE);
                BMI     (NOCOUNT);
//
//       THIS ROUTINE COUNTS OCCURRENCES
//       IT DEPENDS UPON STATE TO INDEX
//       THE CORRECT COUNTERS
//
/*COUNTS:*/     LDA     (PIECE);
                BEQ     (OVER);             // IF STATE=8
                CPXi    (0x08);             // DO NOT COUNT
                BNE     (OVER);             // BLK MAX CAP
                CMP     (BMAXP);            // MOVES FOR
                BEQ     (XRT);              // WHITE
//
OVER:           INCx    (MOB,X);            // MOBILITY
                CMPi    (0x01);             //  + QUEEN
                BNE     (NOQ);              // FOR TWO
                INCx    (MOB,X);
//
NOQ:            BVC     (NOCAP);
                LDYi    (0x0F);             // CALCULATE
                LDA     (SQUARE);           // POINTS
ELOOP:          CMPx    (BK,Y);             // CAPTURED
                BEQ     (FOUN);             // BY THIS
                DEY;                        // MOVE
                BPL     (ELOOP);
FOUN:           LDAf    (POINTS,Y);
                CMPx    (MAXC,X);
                BCC     (LESS);             // SAVE IF
                STYx    (PCAP,X);           // BEST THIS
                STAx    (MAXC,X);           // STATE
//
LESS:           CLC;
                PHP;                        // ADD TO
                ADCx    (CC,X);             // CAPTURE
                STAx    (CC,X);             // COUNTS
                PLP;
//
NOCAP:          CPXi    (0x04);
                BEQ     (ON4);
                BMI     (TREE);             //(=00 ONLY)
XRT:            RTS;
 
//
//      GENERATE FURTHER MOVES FOR COUNT
//      AND ANALYSIS
//
ON4:            LDA     (XMAXC);            // SAVE ACTUAL
                STA     (WCAP0);            // CAPTURE
                LDAi    (0x00);             // STATE=0
                STA     (STATE);
                JSR     (MOVE);             // GENERATE
                JSR     (REVERSE);          // IMMEDIATE
                JSR     (GNMZ);             // REPLY MOVES
                JSR     (REVERSE);
//
                LDAi    (0x08);             // STATE=8
                STA     (STATE);            // GENERATE
                JSR     (GNM);              // CONTINUATION
                JSR     (UMOVE);            // MOVES
//
                JMP     (STRATGY);          //
NOCOUNT:        CPXi    (0xF9);
                BNE     (TREE);
//
//      DETERMINE IF THE KING CAN BE
//      TAKEN, USED BY CHKCHK
//
                LDA     (BK);               // IS KING
                CMP     (SQUARE);           // IN CHECK?
                BNE     (RETJ);             // SET INCHEK=0
                LDAi    (0x00);             // IF IT IS
                STA     (INCHEK);
RETJ:           RTS;
 
//
//      IF A PIECE HAS BEEN CAPTURED BY
//      A TRIAL MOVE, GENERATE REPLIES &
//      EVALUATE THE EXCHANGE GAIN/LOSS
//
TREE:           BVC     (RETJ);             // NO CAP
                LDYi    (0x07);             // (PIECES)
                LDA     (SQUARE);
LOOPX:          CMPx    (BK,Y);
                BEQ     (FOUNX);
                DEY;
                BEQ     (RETJ);             // (KING)
                BPL     (LOOPX);            // SAVE
FOUNX:          LDAf    (POINTS,Y);         // BEST CAP
                CMPx    (BCAP0,X);          // AT THIS
                BCC     (NOMAX);            // LEVEL
                STAx    (BCAP0,X);
NOMAX:          DEC     (STATE);
                LDAf    (&level2,0);        // IF STATE=FB  (WRF, was LDAi (0xFB);)
                CMP     (STATE);            // TIME TO TURN
                BEQ     (UPTREE);           // AROUND
                JSR     (GENRM);            // GENERATE FURTHER
UPTREE:         INC     (STATE);            // CAPTURES
                RTS;
}
 
 
//
//      THE PLAYER'S MOVE IS INPUT
//
void INPUT( void )
{
                CMPi    (0x08);             // NOT A LEGAL
                BCS     (ERROR);            // SQUARE #
                JSR     (DISMV);
                JMP     (DISP);             // fall through
ERROR:          JMP     (RESTART_CHESS);
}
 
void DISP( void )
{
                LDXi    (0x1F);
SEARCH:         LDAx    (BOARD,X);
                CMP     (DIS2);
                BEQ     (HERE);             // DISPLAY
                DEX;                        // PIECE AT
                BPL     (SEARCH);           // FROM
HERE:           STX     (DIS1);             // SQUARE
                STX     (PIECE);
                JMP     (RESTART_CHESS);
}
 
//
//      GENERATE ALL MOVES FOR ONE
//      SIDE, CALL JANUS AFTER EACH
//      ONE FOR NEXT STEP
//
//
void GNMZ( void )
{
                LDXi    (0x10);             // CLEAR
                JMP     (GNMX);             // fall through
}
 
void GNMX( void )
{
                LDAi    (0x00);             // COUNTERS
CLEAR:          STAx    (COUNT,X);
                DEX;
                BPL     (CLEAR);
                JMP     (GNM);              // fall though
}
 
void GNM( void )
{
                LDAi    (0x10);             // SET UP
                STA     (PIECE);            // PIECE
NEWP:           DEC     (PIECE);            // NEW PIECE
                BPL     (NEX);              // ALL DONE?
                RTS;                        //    -YES
//
NEX:            JSR     (RESET);            // READY
                LDY     (PIECE);            // GET PIECE
                LDXi    (0x08);
                STX     (MOVEN);            // COMMON START
                CPYi    (0x08);             // WHAT IS IT?
                BPL     (PAWN);             // PAWN
                CPYi    (0x06);
                BPL     (KNIGHT);           // KNIGHT
                CPYi    (0x04);
                BPL     (BISHOP);           // BISHOP
                CPYi    (0x01);
                BEQ     (QUEEN);            // QUEEN
                BPL     (ROOK);             // ROOK
//
KING:           JSR     (SNGMV);            // MUST BE KING!
                BNE     (KING);             // MOVES
                BEQ     (NEWP);             // 8 TO 1
QUEEN:          JSR     (LINE);
                BNE     (QUEEN);            // MOVES
                BEQ     (NEWP);             // 8 TO 1
//
ROOK:           LDXi    (0x04);
                STX     (MOVEN);            // MOVES
AGNR:           JSR     (LINE);             // 4 TO 1
                BNE     (AGNR);
                BEQ     (NEWP);
//
BISHOP:         JSR     (LINE);
                LDA     (MOVEN);            // MOVES
                CMPi    (0x04);             // 8 TO 5
                BNE     (BISHOP);
                BEQ     (NEWP);
//
KNIGHT:         LDXi    (0x10);
                STX     (MOVEN);            // MOVES
AGNN:           JSR     (SNGMV);            // 16 TO 9
                LDA     (MOVEN);
                CMPi    (0x08);
                BNE     (AGNN);
                BEQ     (NEWP);
//
PAWN:           LDXi    (0x06);
                STX     (MOVEN);
P1:             JSR     (CMOVE);            // RIGHT CAP?
                BVC     (P2);
                BMI     (P2);
                JSR     (JANUS);            // YES
P2:             JSR     (RESET);
                DEC     (MOVEN);            // LEFT CAP?
                LDA     (MOVEN);
                CMPi    (0x05);
                BEQ     (P1);
P3:             JSR     (CMOVE);            // AHEAD
                BVS     (NEWP);             // ILLEGAL
                BMI     (NEWP);
                JSR     (JANUS);
                LDA     (SQUARE);           // GETS TO
                ANDi    (0xF0);             // 3RD RANK?
                CMPi    (0x20);
                BEQ     (P3);               // DO DOUBLE
                BRA     (NEWP);             // JMP (NEWP);
}
 
//
//      CALCULATE SINGLE STEP MOVES
//      FOR K,N
//
void SNGMV( void )
{
                JSR     (CMOVE);            // CALC MOVE
                BMI     (ILL1);             // -IF LEGAL
                JSR     (JANUS);            // -EVALUATE
ILL1:           JSR     (RESET);
                DEC     (MOVEN);
                RTS;
}
 
//
//     CALCULATE ALL MOVES DOWN A
//     STRAIGHT LINE FOR Q,B,R
//
void LINE( void )
{
LINE:           JSR     (CMOVE);            // CALC MOVE
                BCC     (OVL);              // NO CHK
                BVC     (LINE);             // NOCAP
OVL:            BMI     (ILL);              // RETURN
                PHP;
                JSR     (JANUS);            // EVALUATE POSN
                PLP;
                BVC     (LINE);             // NOT A CAP
ILL:            JSR     (RESET);            // LINE STOPPED
                DEC     (MOVEN);            // NEXT DIR
                RTS;
}
 
//
//      EXCHANGE SIDES FOR REPLY
//      ANALYSIS
//
void REVERSE( void )
{
                LDXi    (0x0F);
ETC:            SEC;
                LDYx    (BK,X);             // SUBTRACT
                LDAi    (0x77);             // POSITION
                SBCx    (BOARD,X);          // FROM 77
                STAx    (BK,X);
                STYx    (BOARD,X);          // AND
                SEC;
                LDAi    (0x77);             // EXCHANGE
                SBCx    (BOARD,X);          // PIECES
                STAx    (BOARD,X);
                DEX;
                BPL     (ETC);
                RTS;
}
//
//        CMOVE CALCULATES THE TO SQUARE
//        USING SQUARE AND THE MOVE
//       TABLE  FLAGS SET AS FOLLOWS:
//       N - ILLEGAL MOVE
//       V - CAPTURE (LEGAL UNLESS IN CH)
//       C - ILLEGAL BECAUSE OF CHECK
//       [MY THANKS TO JIM BUTTERFIELD
//        WHO WROTE THIS MORE EFFICIENT
//        VERSION OF CMOVE]
//
void CMOVE( void )
{
    byte src;
                LDA     (SQUARE);           // GET SQUARE
                src     = reg_a;
                LDX     (MOVEN);            // MOVE POINTER
                CLC;
                ADCf    (MOVEX,X);          // MOVE LIST
                STA     (SQUARE);           // NEW POS'N
                ANDi    (0x88);
                BNE     (ILLEGAL);          // OFF BOARD
                LDA     (SQUARE);
                if( bool_show_move_generation )
                    show_move_generation( src, reg_a );
 
//
                LDXi    (0x20);
LOOP:           DEX;                        // IS TO
                BMI     (NO);               // SQUARE
                CMPx    (BOARD,X);          // OCCUPIED?
                BNE     (LOOP);
//
                CPXi    (0x10);             // BY SELF?
                BMI     (ILLEGAL);
//
             // LDAi    (0x7F);             // MUST BE CAP!
             // ADCi    (0x01);             // SET V FLAG
                SEV;    LDAi(0x80);         // Avoid problematic V emulation
                BVS     (SPX);              // (JMP)
//
NO:             CLV;                        // NO CAPTURE
//
SPX:            LDA     (STATE);            // SHOULD WE
                BMI     (RETL);             // DO THE
                CMPf    (&level1,0);        // CHECK CHECK? (WRF: was CMPi (0x08);)
                BPL     (RETL);
//
//        CHKCHK REVERSES SIDES
//       AND LOOKS FOR A KING
//       CAPTURE TO INDICATE
//       ILLEGAL MOVE BECAUSE OF
//       CHECK  SINCE THIS IS
//       TIME CONSUMING, IT IS NOT
//       ALWAYS DONE
//
/*CHKCHK:*/     PHA;                        // STATE
                PHP;
                LDAi    (0xF9);
                STA     (STATE);            // GENERATE
                STA     (INCHEK);           // ALL REPLY
                JSR     (MOVE);             // MOVES TO
                JSR     (REVERSE);          // SEE IF KING
                JSR     (GNM);              // IS IN
                JSR     (RUM);              // CHECK
                PLP;
                PLA;
                STA     (STATE);
                LDA     (INCHEK);
                BMI     (RETL);             // NO - SAFE
                SEC;                        // YES - IN CHK
                LDAi    (0xFF);
                RTS;
//
RETL:           CLC;                        // LEGAL
                LDAi    (0x00);             // RETURN
                RTS;
//
ILLEGAL:        LDAi    (0xFF);
                CLC;                        // ILLEGAL
                CLV;                        // RETURN
                RTS;
}
 
//
//       REPLACE PIECE ON CORRECT SQUARE
//
void RESET( void )
{
                LDX     (PIECE);            // GET LOGAT
                LDAx    (BOARD,X);          // FOR PIECE
                STA     (SQUARE);           // FROM BOARD
                RTS;
}
 
 
//
//
//
void GENRM( void )
{
                JSR     (MOVE);             // MAKE MOVE
/*GENR2:*/      JSR     (REVERSE);          // REVERSE BOARD
                JSR     (GNM);              // GENERATE MOVES
                JMP     (RUM);              // fall through
}
 
void RUM( void )
{
                JSR     (REVERSE);          // REVERSE BACK
                JMP     (UMOVE);            // fall through
}
 
//
//       ROUTINE TO UNMAKE A MOVE MADE BY
//         MOVE
//
void UMOVE( void )
{
                TSX;                        // UNMAKE MOVE
                STX     (SP1);
                LDX     (SP2);              // EXCHANGE
                TXS;                        // STACKS
                PLA;                        // MOVEN
                STA     (MOVEN);
                PLA;                        // CAPTURED
                STA     (PIECE);            // PIECE
                TAX;
                PLA;                        // FROM SQUARE
                STAx    (BOARD,X);
                PLA;                        // PIECE
                TAX;
                PLA;                        // TO SOUARE
                STA     (SQUARE);
                STAx    (BOARD,X);
                JMP     (STRV);
}
 
//
//       THIS ROUTINE MOVES PIECE
//       TO SQUARE, PARAMETERS
//       ARE SAVED IN A STACK TO UNMAKE
//       THE MOVE LATER
//
void MOVE( void )
{               TSX;
                STX     (SP1);              // SWITCH
                LDX     (SP2);              // STACKS
                TXS;
                LDA     (SQUARE);
                PHA;                        // TO SQUARE
                TAY;
                LDXi    (0x1F);
CHECK:          CMPx    (BOARD,X);          // CHECK FOR
                BEQ     (TAKE);             // CAPTURE
                DEX;
                BPL     (CHECK);
TAKE:           LDAi    (0xCC);
                STAx    (BOARD,X);
                TXA;                        // CAPTURED
                PHA;                        // PIECE
                LDX     (PIECE);
                LDAx    (BOARD,X);
                STYx    (BOARD,X);          // FROM
                PHA;                        // SQUARE
                TXA;
                PHA;                        // PIECE
                LDA     (MOVEN);
                PHA;                        // MOVEN
                JMP     (STRV);             // fall through
}
 
// (WRF) Fortunately when we swap stacks we jump here and swap back before
//  returning. So we aren't swapping stacks to do threading (if we were we
//  would need to enhance 6502 stack emulation to incorporate our
//  subroutine mechanism, instead we simply use the native C stack for
//  subroutine return addresses).
void STRV( void )
{
                TSX;
                STX     (SP2);              // SWITCH
                LDX     (SP1);              // STACKS
                TXS;                        // BACK
                RTS;
}
 
//
//       CONTINUATION OF SUB STRATGY
//       -CHECKS FOR CHECK OR CHECKMATE
//       AND ASSIGNS VALUE TO MOVE
//
void CKMATE( void )
{
                LDX     (BMAXC);            // CAN BLK CAP
                CPXf    (POINTS,0);         // MY KING?
                BNE     (NOCHEK);
                LDAi    (0x00);             // GULP!
                BEQ     (RETV);             // DUMB MOVE!
//
NOCHEK:         LDX     (BMOB);             // IS BLACK
                BNE     (RETV);             // UNABLE TO
                LDX     (WMAXP);            // MOVE AND
                BNE     (RETV);             // KING IN CH?
                LDAi    (0xFF);             // YES! MATE
//
RETV:           LDXi    (0x04);             // RESTORE
                STX     (STATE);            // STATE=4
//
//       THE VALUE OF THE MOVE (IN ACCU)
//       IS COMPARED TO THE BEST MOVE AND
//       REPLACES IT IF IT IS BETTER
//
                if( bool_show_move_evaluation )
                    show_move_evaluation( reg_a );
/*PUSH:*/       CMP     (BESTV);            // IS THIS BEST
                BCC     (RETP);             // MOVE SO FAR?
                BEQ     (RETP);
                if( bool_show_move_evaluation )
                    printf( "NEW BEST MOVE\n" );
                STA     (BESTV);            // YES!
                LDA     (PIECE);            // SAVE IT
                STA     (BESTP);
                LDA     (SQUARE);
                STA     (BESTM);            // FLASH DISPLAY
RETP:           LDAi    ('.');              // print ... instead of flashing disp
                JMP     (syschout);         // print . and return
}
 
//
//       MAIN PROGRAM TO PLAY CHESS
//       PLAY FROM OPENING OR THINK
//
void GO( void )
{
                LDX     (OMOVE);            // OPENING?
                BMI     (NOOPEN);           // -NO   *ADD CHANGE FROM BPL
                LDA     (DIS3);             // -YES WAS
                CMPf    (OPNING,X);         // OPPONENT'S
                BNE     (END);              // MOVE OK?
                DEX;
                LDAf    (OPNING,X);         // GET NEXT
                STA     (DIS1);             // CANNED
                DEX;                        // OPENING MOVE
                LDAf    (OPNING,X);
                STA     (DIS3);             // DISPLAY IT
                DEX;
                STX     (OMOVE);            // MOVE IT
                BNE     (MV2);              // (JMP)
//
END:            LDAi    (0xFF);             // *ADD - STOP CANNED MOVES
                STA     (OMOVE);            // FLAG OPENING
NOOPEN:         LDXi    (0x0C);             // FINISHED
                STX     (STATE);            // STATE=C
                STX     (BESTV);            // CLEAR BESTV
                LDXi    (0x14);             // GENERATE P
                JSR     (GNMX);             // MOVES
//
                LDXi    (0x04);             // STATE=4
                STX     (STATE);            // GENERATE AND
                JSR     (GNMZ);             // TEST AVAILABLE
//                                             MOVES
//
                LDX     (BESTV);            // GET BEST MOVE
                CPXi    (0x0F);             // IF NONE
                BCC     (MATE);             // OH OH!
//
MV2:            LDX     (BESTP);            // MOVE
                LDAx    (BOARD,X);          // THE
                STA     (BESTV);            // BEST
                STX     (PIECE);            // MOVE
                LDA     (BESTM);
                STA     (SQUARE);           // AND DISPLAY
                JSR     (MOVE);             // IT
                JMP     (RESTART_CHESS);
//
MATE:           LDAi    (0xFF);             // RESIGN
                RTS;                        // OR STALEMATE
}
 
//
//       SUBROUTINE TO ENTER THE
//       PLAYER'S MOVE
//
void DISMV( void )
{
                LDXi    (0x04);             // ROTATE
DROL:           ASL     (DIS3);             // KEY
                ROL     (DIS2);             // INTO
                DEX;                        // DISPLAY
                BNE     (DROL);             //
                ORA     (DIS3);
                STA     (DIS3);
                STA     (SQUARE);
                RTS;
}
 
//
//       THE FOLLOWING SUBROUTINE ASSIGNS
//       A VALUE TO THE MOVE UNDER
//       CONSIDERATION AND RETURNS IT IN
//       THE ACCUMULATOR
//
void STRATGY( void )
{
                CLC;
                LDAi    (0x80);
                ADC     (WMOB);             // PARAMETERS
                ADC     (WMAXC);            // WITH WEIGHT
                ADC     (WCC);              // OF O.25
                ADC     (WCAP1);
                ADC     (WCAP2);
                SEC;
                SBC     (PMAXC);
                SBC     (PCC);
                SBC     (BCAP0);
                SBC     (BCAP1);
                SBC     (BCAP2);
                SBC     (PMOB);
                SBC     (BMOB);
                BCS     (POS);              // UNDERFLOW
                LDAi    (0x00);             // PREVENTION
POS:            LSR;
                CLC;                        // **************
                ADCi    (0x40);
                ADC     (WMAXC);            // PARAMETERS
                ADC     (WCC);              // WITH WEIGHT
                SEC;                        // OF 0.5
                SBC     (BMAXC);
                LSR;                        // **************
                CLC;
                ADCi    (0x90);
                ADC     (WCAP0);            // PARAMETERS
                ADC     (WCAP0);            // WITH WEIGHT
                ADC     (WCAP0);            // OF 1.0
                ADC     (WCAP0);
                ADC     (WCAP1);
                SEC;                        // [UNDER OR OVER-
                SBC     (BMAXC);            // FLOW MAY OCCUR
                SBC     (BMAXC);            // FROM THIS
                SBC     (BMCC);             // SECTION]
                SBC     (BMCC);
                SBC     (BCAP1);
                LDX     (SQUARE);           // ***************
                CPXi    (0x33);
                BEQ     (POSN);             // POSITION
                CPXi    (0x34);             // BONUS FOR
                BEQ     (POSN);             // MOVE TO
                CPXi    (0x22);             // CENTRE
                BEQ     (POSN);             // OR
                CPXi    (0x25);             // OUT OF
                BEQ     (POSN);             // BACK RANK
                LDX     (PIECE);
                BEQ     (NOPOSN);
                LDYx    (BOARD,X);
                CPYi    (0x10);
                BPL     (NOPOSN);
POSN:           CLC;
                ADCi    (0x02);
NOPOSN:         JMP     (CKMATE);           // CONTINUE
}
 
//**********************************************************************
//*
//*  Part 3
//*  ------
//*  Text based interface over RS-232 port added later.
//*
//*       Part 3 added August 2002 by Daryl Richtor
//**********************************************************************
 
//-----------------------------------------------------------------
// The following routines were added to allow text-based board
// display over a standard RS-232 port.
//
 
// (WRF) For C version, data definitions precede code references to data
char banner[] = "MicroChess (c) 1976-2005 Peter Jennings, www.benlo.com\r\n";
char cpl[]    = "WWWWWWWWWWWWWWWWBBBBBBBBBBBBBBBBWWWWWWWWWWWWWWWW";
char cph[]    = "KQRRBBNNPPPPPPPPKQRRBBNNPPPPPPPP";
 
void POUT( void )
{               JSR     (POUT9);            // print CRLF
                JSR     (POUT13);           // print copyright
                JSR     (POUT10);           // print column labels
                LDYi    (0x00);             // init board location
                JSR     (POUT5);            // print board horz edge
POUT1:          LDAi    ('|');              // print vert edge
                JSR     (syschout);         // PRINT ONE ASCII CHR - SPACE
                LDXi    (0x1F);
POUT2:          TYA;                        // scan the pieces for a location match
                CMPx    (BOARD,X);          // match found?
                BEQ     (POUT4);            // yes, print the piece's color and type
                DEX;                        // no
                BPL     (POUT2);            // if not the last piece, try again
                TYA;                        // empty square
                ANDi    (0x01);             // odd or even column?
                STA     (temp);             // save it
                TYA;                        // is the row odd or even
                LSR;                        // shift column right 4 spaces
                LSR;                        //
                LSR;                        //
                LSR;                        //
                ANDi    (0x01);             // strip LSB
                CLC;                        //
                ADC     (temp);             // combine row & col to determine square color
                ANDi    (0x01);             // is board square white or blk?
                BEQ     (POUT25);           // white, print space
                LDAi    ('#');              // black, print # 
                BRA     (POUT99);           // DB      0x2c             // used to skip over LDA #0x20
POUT25:         LDAi    (' ');              // ASCII space
POUT99:         JSR     (syschout);         // PRINT ONE ASCII CHR - SPACE
                JSR     (syschout);         // PRINT ONE ASCII CHR - SPACE
POUT3:          INY;                        //
                TYA;                        // get row number
                ANDi    (0x08);             // have we completed the row?
                BEQ     (POUT1);            // no, do next column
                LDAi    ('|');              // yes, put the right edge on
                JSR     (syschout);         // PRINT ONE ASCII CHR - |
                JSR     (POUT12);           // print row number
                JSR     (POUT9);            // print CRLF
                JSR     (POUT5);            // print bottom edge of board
                CLC;                        //
                TYA;                        //
                ADCi    (0x08);             // point y to beginning of next row
                TAY;                        //
                CPYi    (0x80);             // was that the last row?
                JEQ     (POUT8);            // (BEQ) yes, print the LED values
                BNE     (POUT1);            // no, do new row
 
POUT4:          LDA     (REV);              // print piece's color & type
                BEQ     (POUT41);           //
                LDAf    (cpl+16,X);         //
                BNE     (POUT42);           //
POUT41:         LDAf    (cpl,X);            //
POUT42:         JSR     (syschout);         //
                LDAf    (cph,X);            //
                JSR     (syschout);         //
                BNE     (POUT3);            // branch always
}
 
void POUT5( void )
{
                TXA;                        // print "-----...-----<crlf>"
                PHA;
                LDXi    (0x19);
                LDAi    ('-');
POUT6:          JSR     (syschout);         // PRINT ONE ASCII CHR - "-"
                DEX;
                BNE     (POUT6);
                PLA;
                TAX;
                JSR     (POUT9);
                RTS;
}
 
void POUT8( void )
{
                JSR     (POUT10);           //
                LDA     (0xFB);
                JSR     (syshexout);        // PRINT 1 BYTE AS 2 HEX CHRS
                LDAi    (0x20);
                JSR     (syschout);         // PRINT ONE ASCII CHR - SPACE
                LDA     (0xFA);
                JSR     (syshexout);        // PRINT 1 BYTE AS 2 HEX CHRS
                LDAi    (0x20);
                JSR     (syschout);         // PRINT ONE ASCII CHR - SPACE
                LDA     (0xF9);
                JSR     (syshexout);        // PRINT 1 BYTE AS 2 HEX CHRS
                JMP     (POUT9);            // fall through
}
 
void POUT9( void )
{
                LDAi    (0x0D);
                JSR     (syschout);         // PRINT ONE ASCII CHR - CR
                LDAi    (0x0A);
                JSR     (syschout);         // PRINT ONE ASCII CHR - LF
                RTS;
}
 
void POUT10( void )
{
                LDXi    (0x00);             // print the column labels
POUT11:         LDAi    (0x20);             // 00 01 02 03 ... 07 <CRLF>
                JSR     (syschout);
                TXA;
                JSR     (syshexout);
                INX;
                CPXi    (0x08);
                BNE     (POUT11);
                JEQ     (POUT9);
                JMP     (POUT12);           // fall through
}
 
void POUT12( void )
{
                TYA;
                ANDi    (0x70);
                JSR     (syshexout);
                RTS;
}
 
void POUT13( void )
{
                LDXi    (0x00);             // Print the copyright banner
POUT14:         LDAf    (banner,X);
                BEQ     (POUT15);
                JSR     (syschout);
                INX;
                BNE     (POUT14);
POUT15:         RTS;
}
 
void KIN( void )
{
                LDAi    ('?');
                JSR     (syschout);         // PRINT ONE ASCII CHR - ?
                JSR     (syskin);           // GET A KEYSTROKE FROM SYSTEM
                ANDi    (0x4F);             // MASK 0-7, AND ALPHA'S
                RTS;
}
 
//
// 6551 I/O Support Routines
//
//
/*
Init_6551      lda   #0x1F                  // 19.2K/8/1
               sta   ACIActl                // control reg
               lda   #0x0B                  // N parity/echo off/rx int off/ dtr active low
               sta   ACIAcmd                // command reg
               rts                          // done
*/
 
// (WRF) See enhanced syskin() routine in Part 4
// input chr from ACIA1 (waiting)
//
/*
syskin         lda   ACIASta                // Serial port status
               and   #0x08                  // is recvr full
               beq   syskin                 // no character to get
               Lda   ACIAdat                // get chr
               RTS                          //
*/
 
// (WRF) See enhanced syschout() routine in Part 4
// output to OutPut Port
//
/*
syschout       PHA                          // save registers
ACIA_Out1      lda   ACIASta                // serial port status
               and   #0x10                  // is tx buffer empty
               beq   ACIA_Out1              // no
               PLA                          // get chr
               sta   ACIAdat                // put character to Port
               RTS                          // done
*/
 
// Print as hex digits
void syshexout( void )
{
               PHA;                         //  prints AA hex digits
               LSR;                         //  MOVE UPPER NIBBLE TO LOWER
               LSR;                         //
               LSR;                         //
               LSR;                         //
               JSR   (PrintDig);            //
               PLA;                         //
               JMP   (PrintDig);            // fall through
}
 
void PrintDig( void )
{
               char Hexdigdata[] = "0123456789ABCDEF";
               ANDi  (0x0F);                //  prints A hex nibble (low 4 bits)
               PHY;                         //
               TAY;                         //
               LDAf  (Hexdigdata,Y);        //
               PLY;                         //
               JMP   (syschout);            //
}
 
//**********************************************************************
//*
//*  Part 4
//*  ------
//*  Enhance text interface by creating 'smart' syskin(), syschout()
//*  routines.
//*
//*       Part 4 added July 2005 by Bill Forster (www.triplehappy.com)
//**********************************************************************
 
// Misc prototypes
static char algebraic_file( byte square );
static char algebraic_rank( byte square );
static char octal_file( char file );
static char octal_rank( char rank );
 
// Here are the commands available in the enhanced interface
static char help[] =
    "Los comandos son;\n"
    " w      ;empezar jugando blancas contra microchess jugando negras\n"
    " b      ;empezar jugando negras contra microchess jugando blancas\n"
    " nnnn   ;(eg 6343 = P-K4) mover pieza usando coordenadas numéricas\n"
    " anan   ;(eg e7e5 = negro P-K4, e2e4 = blanco P-K4) mover con notación \n"
    "        ; algebráica \n"
    " oo     ;enroque a rey\n"
    " ooo    ;enroque a reina\n"
    " f      ;jugar movimiento\n"
    " p      ;forzar movimiento a la CPU\n"
    " a      ;conmutar autojugado (éste inserta comandos \'p\' y \'f\' )\n"
    " c      ;limpiar tablero\n"
    " e      ;alternar (invertir) el lado del tablero\n"
    " ln     ;ajustar nivel, n=1 (flojo), 2 (medio), 3 (fuerte)\n"
    " hh     ;editor de piezas, ver lugar de la pieza, ej 01, reina de la CPU\n"
    " hh=xx  ;editor de piezas, colocar pieza, ej 01=64 o 01=e2\n"
    " hh=    ;editor, borrar pieza, ej 01=, borrar reina de la CPU\n"
    " m      ;depuración, alterna info sobre generar un movimiento\n"
    " v      ;depuración, alterna info sobre evaluar un movimiento\n"
    "        ;Note que la depuración es muy locuaz y lo mejor \n"
    "        ; es redirigirla a un fichero (esp. generando movimientos)\n"
    " q      ;salir\n"
    "?";
 
// Alternatively, allow a primitive interface for DOS/Windows systems
//  that closely simulates the RS-232 interface originally envisaged
//  for part 3
// #define PRIMITIVE_INTERFACE  // normally commented out
#ifdef PRIMITIVE_INTERFACE
#include <conio.h>      // these routines emulate dumb terminal in/out
#endif
 
// Forward declaration of smart in/out alternatives
void smart_out( char c );
char smart_in( void );
 
// Character in
void syskin( void )
{
    #ifdef PRIMITIVE_INTERFACE
    reg_a = (byte)getch();
    #else
    reg_a = (byte)smart_in();
    #endif
}
 
// Character out
void syschout( void )
{
    #ifdef PRIMITIVE_INTERFACE
    putch( (int)reg_a );
    #else
    smart_out( (char)reg_a );
    #endif
}
 
// Smart character out, supplements enhanced interface of smart_in()
static int discard = 1194;  // Discard until initial CLEAR and EXCHANGE
                            //  commands are complete
    // The discard mechanism optionally discards characters - this is
    //  useful because the smart_in() routine works by converting higher
    //  level commands into a a series of primitive commands. The discard
    //  mechanism allows us to skip over (discard) the board displays
    //  generated for all of the intermediate primitive commands.
    //  The values assigned to discard are in each case determined by
    //  simple trial and error
void smart_out( char c )
{
    static char first='?';  // replace first '?' with message below
    if( discard )
        discard--;
    else
    {
        if( first && c==first )
        {
            printf( " (escriba ? para ayuda)\n?" );
            first = '\0';
        }
        else
        {
            if( c != '\r' )//printf converts "\n" to "\r\n" so don't need "\r"
                printf( "%c", c );
        }
    }
}
 
// Smart character in, provides a help screen + algebraic notation interface
//  + position editor + diagnostics commands etc.
char smart_in( void )
{
    static char error[] = "Movimiento o comando no válido, escribe ? para ayuda\n?";
    static char buf[20] = " CE";  // start with a CLEAR then EXCHANGE command
    static int offset=1;
    static int bool_auto=1;
    char color, file, rank, file2, rank2, ch='\0';
    int i, len, bool_okay;
    byte piece, square;
    char *s;
 
    // Get orientation; is human player white or black ?
    byte bool_white = ZP(REV);
 
    // Emit buffered commands until '\0'
    if( offset )
        ch = buf[offset++];
 
    // Loop until command ready
    while( ch == '\0' )
    {
 
        // Reset grooming machinery
        offset  = 0;
        discard = 2;    // remove initial "\r\n"
 
        // Reset flag indicating entry of a legal command handled internally
        //  (i.e. within this function, without passing characters to
        //  underlying microchess implementation)
        bool_okay = 0;
 
        // Get edited command line
        if( NULL == fgets(buf,sizeof(buf)-1,stdin) )
            EXIT_TO_SYSTEM();
        else
        {
 
            // Convert to lower case, zap '\n'
            for( s=buf; *s; s++ )
            {
                if( isascii(*s) && isupper(*s) )
                    *s = tolower(*s);
                else if( *s == '\n' )
                    *s = '\0';
            }
 
            // Trim string from end
            s = strchr(buf,'\0') - 1;
            while( s>=buf && (*s==' '||*s=='\t') )
                *s-- = '\0';
 
            // Trim string from start
            s = buf;
            while( *s==' ' || *s=='\t' )
                s++;
            len = strlen(s);
            for( i=0; i<len+1; i++ )  // +1 gets '\0' at end
                buf[i] = *s++;  // if no leading space this does
                                //  nothing, but no harm either
 
            // Convert an algebraic move eg "e2e4" into an octal microchess
            //  move
            if( len == 4 &&
                'a'<=buf[0] && buf[0]<='h' &&
                '1'<=buf[1] && buf[1]<='8' &&
                'a'<=buf[2] && buf[2]<='h' &&
                '1'<=buf[3] && buf[3]<='8'
              )
            {
                file  = octal_file(buf[0]);
                rank  = octal_rank(buf[1]);
                file2 = octal_file(buf[2]);
                rank2 = octal_rank(buf[3]);
                buf[0] = rank;     // specify move microchess grid style
                buf[1] = file;     //
                buf[2] = rank2;    //
                buf[3] = file2;    //
            }
 
            // Is it a microchess octal numeric move eg "6364" ?
            if( len == 4 &&
                '0'<=buf[0] && buf[0]<='7' &&
                '0'<=buf[1] && buf[1]<='7' &&
                '0'<=buf[2] && buf[2]<='7' &&
                '0'<=buf[3] && buf[3]<='7'
              )
            {
                offset = 1;         // emit from here next
                if( bool_auto )
                {
                    buf[4] = '\r';   // play move
                    buf[5] = 'p';    // get response
                    buf[6] = '\0';   // done
                    discard = 2386;  // skip over intermediate board displays
                }
                else
                {
                    buf[4] = '\0';   // done
                    discard = 1790;  // skip over intermediate board displays
                }
            }
 
            // Is it a level command ?
            else if( len==2 &&
                (buf[0]=='l' && '1'<=buf[1] && buf[1]<='3' )
              )
            {
                bool_okay = 1;
                switch( buf[1] )
                {
                    case '1':   level1 = 0;
                                level2 = 0xff;
                                printf( "Level 1, super blitz\n" );
                                break;  // (on 6502: 3 seconds per move)
                    case '2':   level1 = 0;
                                level2 = 0xfb;
                                printf( "Level 2, blitz\n" );
                                break;  // (on 6502: 10 seconds per move)
                    case '3':   level1 = 8;
                                level2 = 0xfb;
                                printf( "Level 3, normal\n" );
                                break;  // (on 6502: 100 seconds per move)
                }
            }
 
            // Is it a single letter command ?
            else if( len == 1 )
            {
                switch( buf[0] )
                {
 
                    // Send single letter commands to underlying microchess
                    //  (step 3) interface
                    case 'c':   ch = 'C';   break;
                    case 'e':   ch = 'E';   break;
                    case 'p':   ch = 'P';   discard=0;  // no initial "\r\n"
                                            break;
                    case 'q':   ch = 'Q';   break;
                    case 'f':   ch = '\r';  break;
 
                    // Toggle various features
                    case 'a':
                    {
                        bool_okay = 1;
                        bool_auto = !bool_auto;
                        printf( "Auto play now %s\n",
                                            bool_auto ? "enabled"
                                                      : "disabled" );
                        break;
                    }
                    case 'm':
                    {
                        bool_okay = 1;
                        bool_show_move_generation = !bool_show_move_generation;
                        printf( "Show move generation now %s\n",
                                 bool_show_move_generation ? "enabled"
                                                           : "disabled" );
                        break;
                    }
                    case 'v':
                    {
                        bool_okay = 1;
                        bool_show_move_evaluation = !bool_show_move_evaluation;
                        printf( "Show move evaluation now %s\n",
                                 bool_show_move_evaluation ? "enabled"
                                                           : "disabled" );
                        break;
                    }
 
                    // Start a white game by emitting "clear" and "reverse"
                    //  commands. Make sure we set to "black" orientation
                    //  before clear command else we get a nasty mirror
                    //  image chess board
                    case 'w':
                    {
                        strcpy( buf, bool_white?"ece":"ce" );
                        discard = bool_white?1194:598;
                        offset = 1; // emit from here next
                        break;
                    }
 
                    // Start a black game by emitting ["reverse"], "clear",
                    //  and "play" commands. Make sure we set to "black"
                    //  orientation before clear command else we get a
                    //  nasty mirror image chess board
                    case 'b':
                    {
                        strcpy( buf, bool_white?"ecp":"cp" );
                        discard = bool_white?1194:598;
                        offset = 1; // emit from here next
                        break;
                    }
                }
            }
 
            // Algebraic castling - emit as two half moves
            else if( 0==strcmp(buf,"oo") || 0==strcmp(buf,"ooo") )
            {
                if( bool_auto )
                {
                    if( 0 == strcmp(buf,"oo") )
                        strcpy( buf, bool_white ? "7476\r7775\rp"
                                                : "7371\r7072\rp" );
                    else
                        strcpy( buf, bool_white ? "7472\r7073\rp"
                                                : "7375\r7774\rp" );
                    offset = 1;
                    discard = 5422; // skip intermediate boards
                }
                else
                {
                    printf( "Castling only available in auto play mode"
                            " (use \'a\' command)\n" );
                    bool_okay = 1;
                }
            }
 
            // Piece editor ?
            else if( len>=2 && '0'<=buf[0] && buf[0]<='1' &&
                               isascii(buf[1]) && isxdigit(buf[1]) )
            {
                bool_okay = 0; // assume syntax is bad unless proven otherwise
                if( len == 2 )
                    bool_okay = 1;   // view
                else if( len==3 && buf[2]=='=' )
                    bool_okay = 1;   // delete
                else if( len==5 && buf[2]=='=' &&
                        ( '0'<=buf[3] && buf[3]<='7' &&
                          '0'<=buf[4] && buf[4]<='7' )
                  )
                {
                    bool_okay = 1;   // octal edit
                    file = buf[4];
                    rank = buf[3];
                }
                else if( len==5 && buf[2]=='=' &&
                        ( 'a'<=buf[3] && buf[3]<='h' &&
                          '1'<=buf[4] && buf[4]<='8' )
                  )
                {
                    bool_okay = 1;   // algebraic edit
                    file = octal_file(buf[3]);
                    rank = octal_rank(buf[4]);
                }
 
                // If piece editor command with correct syntax
                if( bool_okay )
                {
 
                    // First two characters are hex 00-1f indicating one of
                    //  the 32 pieces
                    piece = buf[1]>='a' ? buf[1]-'a'+10 : buf[1]-'0';
                    if( buf[0] == '1' )
                        piece += 16;
                    square = ZP(BOARD+piece); // square our piece is occupying
 
                    // If edit place our piece on specified square, after
                    //  making sure no other piece is on that square
                    if( len == 5 )  // edit ?
                    {
                        square = (rank-'0')*16 + (file-'0');
                        for( i=0; i<32; i++ )
                        {
                            if( ZP(BOARD+i) == square ) //delete other piece?
                                ZP(BOARD+i)= 0xcc; // microchess convention
                        }
                        ZP(BOARD+piece) = square;
                    }
 
                    // If delete assign special illegal square value to piece
                    else if( len == 3 )
                        ZP(BOARD+piece) = 0xcc; // microchess convention
 
                    // Report on the color and type of piece ...
                    if( piece < 16 )
                        color = bool_white?'B':'W';
                    else
                        color = bool_white?'W':'B';
                    printf( "Piece %c%c is %s %c%c ", buf[0], buf[1],
                                        (piece&0x0f) < 2 ? "the" : "a",
                                        color,
                                        "KQRRBBNNPPPPPPPP"[piece&0x0f] );
 
                    // ... and the square it (now) occupies
                    if( square & 0x88 )
                        printf( "and is not on the board\n" );
                    else
                    {
                        printf( "%son square %02x",
                                           len==3?"previously ":"",
                                           square );
                        printf( " (algebraic %c%c)", algebraic_file(square),
                                                     algebraic_rank(square) );
                        if( len == 3 )
                            printf( " now deleted" );
                        printf("\n");
                    }
                    POUT();
                }
            }
 
            // Emit the first of a buffered series of commands ?
            if( offset )
                ch = buf[0];
 
            // If still no command available, illegal or unknown command
            if( ch == '\0' )
            {
                if( len==0 || bool_okay ) // if bool_okay internal command 
                    printf( "?" );
                else if( buf[0] == '?' )
                    printf( help );
                else
                    printf( error );
            }
        }
    }
    return( ch );
}
 
 
// Show internally generated move
void show_move_generation( byte src, byte dst )
{
    static byte lookup[64] =
    {
        0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,
        0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,
        0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,
        0x30,0x31,0x32,0x33,0x34,0x35,0x36,0x37,
        0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,
        0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x57,
        0x60,0x61,0x62,0x63,0x64,0x65,0x66,0x67,
        0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77
    };
    static char spaces[]=
        "                                                           ";
    int i, indent;
    char ch;
    byte square, piece;
 
    // Indent according to state
    printf( "\n" );
    if( ZP(STATE) >= 0xf5 )
        indent = (ZP(STATE)-0xf5)*4;
    else
        indent = ZP(STATE);
    printf( strchr(spaces,'\0') - indent );
 
    // Print two characters for each square
    for( i=0; i<64; i++ )
    {
        square = lookup[i];
        ch = ' ';  // empty by default
        for( piece=0; piece<32; piece++ ) // unless we find a piece
        {
            if( ZP(BOARD+piece) == square )
            {
                ch = "KQRRBBNNPPPPPPPPkqrrbbnnpppppppp"[piece];
                break;
            }
        }
        printf( "%c", ch );
        if( square == src )
            printf( "*" );  // highlight src square like this
        else if( square == dst )
            printf( "@" );  // highlight dst square like this
        else
            printf( " " );  // normally no hightlight
 
        // Next row
        if( (i&7) == 7 )
        {
            printf( "\n" );
            printf( strchr(spaces,'\0') - indent );
        }
    }
 
    // Also show the most important debug variable information
    printf( "state=%02x ", ZP(STATE) );
}
 
 
// Show numeric move evaluation
static void show_move_evaluation( int ivalue )
{
 
    // Compare ivalue calculated by microchess with independently calculated
    //  float value, then float value scaled into same range as ivalue
    double value;
    int svalue;
 
    // Counters
    byte wcap0 = ZP(WCAP0);
    byte wcap1 = ZP(WCAP1);
    byte wmaxc = ZP(WMAXC);
    byte wcc   = ZP(WCC  );
    byte wmob  = ZP(WMOB );
    byte wcap2 = ZP(WCAP2);
    byte bmaxc = ZP(BMAXC);
    byte bcc   = ZP(BMCC );
    byte bcap1 = ZP(BCAP1);
    byte pmaxc = ZP(PMAXC);
    byte pcc   = ZP(PCC  );
    byte pmob  = ZP(PMOB );
    byte bcap0 = ZP(BCAP0);
    byte bcap2 = ZP(BCAP2);
    byte bmob  = ZP(BMOB );
 
    // Show move
    printf( "\nEvaluating move %c-%c%c\n",
                             "KQRRBBNNpppppppp"[ZP(PIECE)&0x0f],
                             algebraic_file(ZP(SQUARE)),
                             algebraic_rank(ZP(SQUARE)) );
 
    // Calculate weighted sum
    value =   4.00 * (wcap0)
            + 1.25 * (wcap1)
            + 0.75 * (wmaxc + wcc)
            + 0.25 * (wmob + wcap2)
            - 2.50 * (bmaxc)
            - 2.00 * (bcc)
            - 1.25 * (bcap1)
            - 0.25 * (pmaxc + pcc + pmob + bcap0 + bcap2 + bmob);
    printf( "(+4)    WCAP0=%u\n", wcap0 );
    printf( "(+1.25) WCAP1=%u\n", wcap1 );
    printf( "(+0.75) WMAXC=%u WCC=%u\n", wmaxc, wcc );
    printf( "(+0.25) WMOB =%u WCAP2=%u\n", wmob, wcap2  );
    printf( "(-2.50) BMAXC=%u\n", bmaxc );
    printf( "(-2.00) BCC  =%u\n", bcc   );
    printf( "(-1.25) BCAP1=%u\n", bcap1 );
    printf( "(-0.25) PMAXC=%u PCC=%u PMOB=%u BCAP0=%u BCAP2=%u BMOB=%u\n",
                     pmaxc, pcc, pmob, bcap0, bcap2, bmob  );
    printf( "Weighted sum        = %f\n", value  );
 
    // Calculate scaled weighted sum, corresponds to single byte value used
    //  internally by microchess
    svalue = (int)floor(208.0 + value);  // 208 = 0x90+0x40 from STRATGY();
    printf( "Scaled weighted sum = %d\n", svalue );
 
    // Comment on correspondence (or otherwise) of two values
    printf( "Move value = %d"  , ivalue );
    if( ivalue == 0 )
        printf( " (minimum, I'm in check?)\n", ivalue );
    else if( ivalue == 255 )
        printf( " (maximum, I'm delivering mate?)\n", ivalue );
    else if( ivalue == svalue )
        printf( " (=scaled weighted sum)\n" );
    else if( ivalue == svalue+2 )
        printf( " (=scaled weighted sum plus 2 bonus points)\n" );
    else
        printf( " (unexpected value, suspect overflow or underflow)\n" );
    printf( "best so far = %u\n", ZP(BESTV) );
}
 
 
// Get algebraic file 'a'-'h' from octal square
static char algebraic_file( byte square )
{
    char file = square & 0x0f;
    byte bool_white = ZP(REV);
    if( bool_white )
        file = 'a' +  file;     // eg 0->'a', 7->'h'
    else //if( black )
        file = 'a' + (7-file);  // eg 7->'a', 0->'h'
    return( file );
}
 
 
// Get algebraic rank '1'-'8' from octal square
static char algebraic_rank( byte square )
{
    char rank = (square>>4) & 0x0f;
    byte bool_white = ZP(REV);
    if( bool_white )
        rank = '1' + (7-rank);  // eg 7->'1', 0->'8'
    else //if( black )
        rank = '1' + rank;      // eg 0->'1', 7->'8'
    return( rank );
}
 
 
// Get microchess file '0'-'7' from algebraic file 'a'-'h'
static char octal_file( char file )
{
    byte bool_white = ZP(REV);
    if( bool_white )
        file = '0' + (file-'a');  // eg 'a'->'0', 'h'->'7'
    else //if( black )
        file = '7' - (file-'a');  // eg 'a'->'7', 'h'->'0'
    return( file );
}
 
 
// Get microchess rank '0'-'7' from algebraic rank '1'-'8'
static char octal_rank( char rank )
{
    byte bool_white = ZP(REV);
    if( bool_white )
        rank = '7' - (rank-'1');  // eg '1'->'7', '8'->'0'
    else //if( black )
        rank = '0' + (rank-'1');  // eg '1'->'0', '8'->'7'
    return( rank );
}

Este sitio web utiliza cookies para guardar datos esenciales de su actividad, como su autenticación. Al entrar acepta el uso de cookies.

Más información