//***********************************************************************
//
// 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 );
}