diff --git a/Makefile b/Makefile index 49ab60b..b3c091b 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ # Space or comma separated list of cc65 supported target platforms to build for. # Default: c64 (lowercase!) -TARGETS := +TARGETS := c64 apple2 # Name of the final, single-file executable. # Default: name of the current dir with target name appended @@ -36,6 +36,7 @@ ASFLAGS = # Additional linker flags and options. # Default: none LDFLAGS = +cc65-Chess.apple2: LDFLAGS += --start-addr 0x4000 -Wl -D -Wl __HIMEM__=0xBF00 # Path to the directory containing C and ASM sources. # Default: src diff --git a/apple2/template.dsk b/apple2/template.dsk new file mode 100644 index 0000000..e2354c6 Binary files /dev/null and b/apple2/template.dsk differ diff --git a/build.cmd b/build.cmd new file mode 100644 index 0000000..d0a5e9a --- /dev/null +++ b/build.cmd @@ -0,0 +1,3 @@ +copy apple2\template.dsk cc65-Chess.dsk +java -jar apple2\AppleCommander-win64-1.5.0.jar -p cc65-Chess.dsk chess.system sys < \cc65\target\apple2\util\loader.system +java -jar apple2\AppleCommander-win64-1.5.0.jar -as cc65-Chess.dsk chess bin < cc65-Chess.apple2 diff --git a/src/apple2/charset.bin b/src/apple2/charset.bin new file mode 100644 index 0000000..0ae6e59 Binary files /dev/null and b/src/apple2/charset.bin differ diff --git a/src/apple2/genPieces.cpp b/src/apple2/genPieces.cpp new file mode 100644 index 0000000..cf2b4bc --- /dev/null +++ b/src/apple2/genPieces.cpp @@ -0,0 +1,302 @@ +/* + * genPieces.cpp + * cc65 Chess + * + * Created by Oliver Schmidt, January 2020. + * Pieces designed by Frank Gebhart, 1980s. + * + */ + +#include +#ifdef _WIN32 +#include +#else +#include +#endif + +char pieces[]= +" " +" **** ***** **** " +" * * * * * * " +" * * * * * * " +" * ***** ***** * " +" * * " +" * * " +" * * " +" * ********* * " +" ** ** " +" * * " +" * * " +" * * " +" * * " +" * * " +" * * " +" * * " +" * *********** * " +" * * " +" ******************* " +" ******************* " +" " +" " +" **** ***** **** " +" **** ***** **** " +" **** ***** **** " +" ******************* " +" ******************* " +" ***************** " +" *************** " +" ************* " +" * * " +" *********** " +" *********** " +" *********** " +" *********** " +" *********** " +" ************* " +" *************** " +" *************** " +" * * " +" ******************* " +" ******************* " +" " +" " +" * * " +" * * * * " +" * * * " +" * * " +" * * " +" * * * * " +" * * * " +" * ** * " +" * * * " +" * * " +" * **** * " +" * * * * ** " +" ** * ** * " +" * * * " +" * * * " +" * ** " +" * * " +" * ************ * " +" * * " +" **************** " +" " +" " +" * * " +" * * * * " +" ***** *** " +" ********** " +" ************ " +" **** **** ** " +" ********* ** " +" ********** ** " +" ************ ** " +" **************** " +" **************** " +" **** **** *** " +" ** ***** ** " +" ******* *** " +" ********* ** " +" ************** " +" *************** " +" * * " +" **************** " +" **************** " +" " +" " +" " +" *** " +" * * " +" * * " +" * * * " +" * * * " +" * ******* * " +" * * * " +" * * * " +" * * * " +" * * * " +" * * " +" * * " +" ******* " +" * * " +" * * " +" ***** " +" * * " +" *************** " +" ***************** " +" " +" " +" " +" *** " +" ***** " +" ******* " +" **** **** " +" ***** ***** " +" ** ** " +" ****** ****** " +" ****** ****** " +" ****** ****** " +" ***** ***** " +" *********** " +" ********* " +" * * " +" ******* " +" ******* " +" ***** " +" *********** " +" * * " +" ***************** " +" " +" " +" " +" *** " +" * * " +" *** * * *** " +" * * * * * * " +" * * * * * * " +" * * * * * * " +" * * " +" * * * * * " +" * ** *** ** * " +" * ********* * " +" * ******* * " +" * * " +" * * " +" * *********** * " +" * * " +" * * " +" * * " +" ** ** " +" *********** " +" " +" " +" " +" *** " +" ***** " +" *** ***** *** " +" ***** *** ***** " +" ****** ***** ****** " +" ***** ******* ***** " +" ******************* " +" **** **** **** **** " +" **** ** ** **** " +" **** **** " +" ***** ***** " +" *************** " +" *************** " +" ** ** " +" ************* " +" ************* " +" ************* " +" ************* " +" *********** " +" " +" " +" *** " +" * * " +" *** *** " +" ** ** " +" ** * * ** " +" * * * * * * " +" * ** ** * " +" * * * * " +" * * * * * * " +" * * * * * * " +" * * * * * " +" * * * * * " +" * * * * " +" * * * * " +" * * " +" * ********* * " +" * * " +" * * " +" ** ** " +" *********** " +" " +" " +" *** " +" * * " +" *** *** " +" ** ** " +" ** * * ** " +" **** * * **** " +" ******* ******* " +" ******* ******* " +" *** ** * * ** *** " +" *** **** **** *** " +" *** ***** ***** *** " +" **** ********* **** " +" **** ******* **** " +" ***** ***** ***** " +" *************** " +" ** ** " +" ************* " +" ************* " +" ************* " +" *********** " +" " +" " +" " +" " +" " +" " +" *** " +" * * " +" * * " +" * * " +" * * " +" * * " +" *** " +" * * " +" * * " +" ** ** " +" * * " +" * * " +" ******* " +" * * " +" *********** " +" *********** " +" " +" " +" " +" " +" " +" " +" *** " +" ***** " +" ******* " +" ******* " +" ******* " +" ***** " +" *** " +" ***** " +" ***** " +" ******* " +" ***** " +" ***** " +" ******* " +" * * " +" *********** " +" *********** " +" " +; + +int main(void) +{ + int i; + int f = open("pieces.bin",O_CREAT|O_TRUNC|O_WRONLY); + char c = 0; + + for(i=0; i> 1 | I & $C0 >> 3 + .endrep + +BASEHI: + .repeat $C0, I + .byte >$2000 | I & $07 << 2 | I & $30 >> 4 + .endrep + +_hires_CharSet: +.incbin "charset.bin" + +_hires_Pieces: +.incbin "pieces.bin" + +.code + + +.proc _hires_Init + + bit $C082 ; Switch in ROM + + lda VERSION ; Needs ROM + cmp #$06 ; Apple //e ? + bne :+ + + lda #$15 ; Turn off 80-column firmware + jsr $C300 ; Needs ROM (see $CEF4) + +: bit $C080 ; Back to LC bank 2 for R/O + + bit TXTCLR + bit MIXCLR + bit HIRES + + lda #20 + sta WNDTOP ; Prepare hires_text() + rts + +.endproc + + +.proc _hires_Done + + bit TXTSET + + lda #00 + sta WNDTOP ; Back to full screen text + rts + +.endproc + + +.proc _hires_Text + + tax ; 'flag' + lda MIXCLR,x + rts + +.endproc + + +.data + + +.proc _hires_Draw + + sta src+1 ; 'src' lo + stx src+2 ; 'src' hi + + jsr popax ; 'rop' + stx rop + sta rop+1 + + jsr popa ; 'ysize' + sta ymax+1 + + jsr popa ; 'xsize' + sta xmax+1 + + jsr popa ; 'ypos' + sta ypos+1 + tax + + clc + adc ymax+1 + sta ymax+1 + + jsr popa ; 'xpos' + sta xpos+1 + + clc + adc xmax+1 + sta xmax+1 +yloop: + lda BASELO,x + sta dst+1 + lda BASEHI,x + sta dst+2 + +xpos: ldx #$FF ; Patched +xloop: +src: lda $FFFF,y ; Patched + iny +rop: nop ; Patched + nop ; Patched +dst: sta $FFFF,x ; Patched + inx +xmax: cpx #$FF ; Patched + bne xloop + + inc ypos+1 +ypos: ldx #$FF ; Patched +ymax: cpx #$FF ; Patched + bne yloop + rts + +.endproc + + +.proc _hires_Mask + + stx rop ; 'rop' hi + sta rop+1 ; 'rop' lo + + jsr popa ; 'ysize' + sta ymax+1 + + jsr popa ; 'xsize' + sta xmax+1 + + jsr popa ; 'ypos' + tax + + clc + adc ymax+1 + sta ymax+1 + + jsr popa ; 'xpos' + sta xpos+1 + + clc + adc xmax+1 + sta xmax+1 + +yloop: + lda BASELO,x + sta src+1 + sta dst+1 + lda BASEHI,x + sta src+2 + sta dst+2 + +xpos: ldy #$FF ; Patched +xloop: +src: lda $FFFF,y ; Patched +rop: nop ; Patched + nop ; Patched +dst: sta $FFFF,y ; Patched + iny +xmax: cpy #$FF ; Patched + bne xloop + + inx +ymax: cpx #$FF ; Patched + bne yloop + rts + +.endproc diff --git a/src/apple2/pieces.bin b/src/apple2/pieces.bin new file mode 100644 index 0000000..df0063d Binary files /dev/null and b/src/apple2/pieces.bin differ diff --git a/src/apple2/platA2.c b/src/apple2/platA2.c new file mode 100644 index 0000000..edc1a4a --- /dev/null +++ b/src/apple2/platA2.c @@ -0,0 +1,399 @@ +/* + * platA2.c + * cc65 Chess + * + * Created by Oliver Schmidt, January 2020. + * + */ + +#include +#include +#include +#include "../types.h" +#include "../globals.h" +#include "../undo.h" +#include "../frontend.h" +#include "../plat.h" + +/*-----------------------------------------------------------------------*/ +#define BOARD_PIECE_WIDTH 3 +#define BOARD_PIECE_HEIGHT 22 +#define CHAR_HEIGHT 8 +#define LOG_WINDOW_HEIGHT 23 +#define SCREEN_WIDTH 40 +#define SCREEN_HEIGHT 192 + +#define ROP_CONST(val) 0xA980|(val) +#define ROP_BLACK 0xA980 +#define ROP_WHITE 0xA9FF +#define ROP_XOR(val) 0x4900|(val) +#define ROP_CPY 0x4980 +#define ROP_INV 0x49FF +#define ROP_AND(val) 0x2900|(val) + +char rop_Line[2][7] = {{0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F}, + {0x00, 0x40, 0x60, 0x70, 0x78, 0x7C, 0x7E}}; + +char rop_Color[2][2] = {{0x55, 0x2A}, {0xD5, 0xAA}}; + +extern char hires_CharSet[96][CHAR_HEIGHT]; +extern char hires_Pieces[6][2][BOARD_PIECE_WIDTH*BOARD_PIECE_HEIGHT]; + +void hires_Init(void); +void hires_Done(void); +void hires_Text(char flag); +void hires_Draw(char xpos, char ypos, + char xsize, char ysize, + unsigned rop, char *src); +void hires_Mask(char xpos, char ypos, + char xsize, char ysize, + unsigned rop); + +/*-----------------------------------------------------------------------*/ +void plat_Drawchar(char x, char y, unsigned rop, char c) +{ + hires_Draw(x,y,1,CHAR_HEIGHT,rop,hires_CharSet[c-' ']); +} + +/*-----------------------------------------------------------------------*/ +void plat_Drawstring(char x, char y, unsigned rop, char *s) +{ + while(*s) + plat_Drawchar(x++,y,rop,*s++); +} + +/*-----------------------------------------------------------------------*/ +void plat_Init() +{ + char i; + + // Clear the hires screen + hires_Mask(0,0,SCREEN_WIDTH,SCREEN_HEIGHT,ROP_BLACK); + + // Show the hires screen + hires_Init(); + + // Show credits and wait for key press + plat_Drawstring(2,SCREEN_HEIGHT/2-10-CHAR_HEIGHT/2, + ROP_CPY,gszAbout); + plat_Drawstring(2,SCREEN_HEIGHT/2+10-CHAR_HEIGHT/2, + ROP_CPY,"Apple][ version by O. Schmidt, 2020."); + hires_Draw(SCREEN_WIDTH/2-2,SCREEN_HEIGHT/2-50-BOARD_PIECE_HEIGHT/2, + BOARD_PIECE_WIDTH,BOARD_PIECE_HEIGHT,ROP_CPY, + hires_Pieces[KING-1][SIDE_BLACK]); + hires_Draw(SCREEN_WIDTH/2-2,SCREEN_HEIGHT/2+50-BOARD_PIECE_HEIGHT/2, + BOARD_PIECE_WIDTH,BOARD_PIECE_HEIGHT,ROP_CPY, + hires_Pieces[KING-1][SIDE_WHITE]); + plat_ReadKeys(1); + + // Draw the board border + hires_Mask( 1, 12,1,8*BOARD_PIECE_HEIGHT+2*2,ROP_CONST(rop_Line[1][2])); + hires_Mask(26, 12,1,8*BOARD_PIECE_HEIGHT+2*2,ROP_CONST(rop_Line[0][2])); + hires_Mask( 2, 12,8*BOARD_PIECE_WIDTH,2, ROP_WHITE); + hires_Mask( 2,190,8*BOARD_PIECE_WIDTH,2, ROP_WHITE); + + // Add the A..H and 1..8 tile-keys + for(i=0; i<8; ++i) + { + plat_Drawchar(3+i*BOARD_PIECE_WIDTH,0, ROP_CPY,i+'A'); + plat_Drawchar(0,21+i*BOARD_PIECE_HEIGHT,ROP_CPY,i+'1'); + } + + // Setting this to 0 will not show the "Quit" option in the main menu + gReturnToOS = 1; +} + +/*-----------------------------------------------------------------------*/ +void plat_UpdateScreen() +{ +} + +/*-----------------------------------------------------------------------*/ +char plat_Menu(char **menuItems, char height, char *scroller) +{ + int keyMask; + char i, j; + + clrscr(); + hires_Text(1); + + // Show the title or the scroller if that is more relevant. + cputs(scroller == gszAbout ? menuItems[0] : scroller); + + // Select the first item + i = 1; + do + { + // Show all the menu items + for(j=1; j 2) + i -= 2; + else + i = j-1-(i-1)%2; + break; + + case INPUT_DOWN: + if(i < j-2) + i += 2; + else + i = 1+(i-1)%2; + break; + } + } + keyMask &= (INPUT_SELECT | INPUT_BACKUP); + + } while(keyMask != INPUT_SELECT && keyMask != INPUT_BACKUP); + + hires_Text(0); + + // If backing out of the menu, return 0 + if(keyMask & INPUT_BACKUP) + return 0; + + // Return the selection + return i; +} + +/*-----------------------------------------------------------------------*/ +// Draw the chess board and possibly clear the log section +void plat_DrawBoard(char clearLog) +{ + char i; + + if(clearLog) + hires_Mask(27,0,SCREEN_WIDTH-27,SCREEN_HEIGHT,ROP_BLACK); + + // Redraw all tiles + for(i=0; i<64; ++i) + plat_DrawSquare(i); +} + +/*-----------------------------------------------------------------------*/ +// Draw a tile with background and piece on it for positions 0..63 +void plat_DrawSquare(char position) +{ + unsigned rop; + char inv; + char y = position / 8, x = position & 7; + char piece = gChessBoard[y][x]; + char blackWhite = !((x & 1) ^ (y & 1)); + + if(piece) + { + rop = blackWhite ? ROP_INV : ROP_CPY; + inv = blackWhite ^ (piece & PIECE_WHITE) != 0; + } + else + rop = blackWhite ? ROP_WHITE : ROP_BLACK; + + hires_Draw(2+x*BOARD_PIECE_WIDTH,14+y*BOARD_PIECE_HEIGHT, + BOARD_PIECE_WIDTH,BOARD_PIECE_HEIGHT,rop, + hires_Pieces[(piece&PIECE_DATA)-1][inv]); + + // Show the attack numbers + if(gShowAttackBoard) + { + char val[4]; + + rop = blackWhite ? ROP_INV : ROP_CPY; + sprintf(val,"%02X%d",gChessBoard[y][x],(gChessBoard[y][x]&PIECE_WHITE)>>7); + + plat_Drawchar(2+x*BOARD_PIECE_WIDTH,14+(y+1)*BOARD_PIECE_HEIGHT-CHAR_HEIGHT, + rop,(gpAttackBoard[giAttackBoardOffset[position][0]])+'0'); + plat_Drawchar(2+(x+1)*BOARD_PIECE_WIDTH-1,14+(y+1)*BOARD_PIECE_HEIGHT-CHAR_HEIGHT, + rop,(gpAttackBoard[giAttackBoardOffset[position][1]])+'0'); + plat_Drawstring(2+x*BOARD_PIECE_WIDTH,14+y*BOARD_PIECE_HEIGHT,rop,val); + } +} + +/*-----------------------------------------------------------------------*/ +void plat_ShowSideToGoLabel(char side) +{ + plat_Drawstring(28,SCREEN_HEIGHT-CHAR_HEIGHT, + side?ROP_CPY:ROP_INV,gszSideLabel[side]); +} + +/*-----------------------------------------------------------------------*/ +void plat_Highlight(char position, char color, char cursor) +{ + char y = position / 8, x = position & 7; + + if (cursor && color != HCOLOR_SELECTED) + { + char size = color == HCOLOR_EMPTY ? 6 : color == HCOLOR_INVALID ? 4 : 2; + + hires_Mask(2+x*BOARD_PIECE_WIDTH,14+y*BOARD_PIECE_HEIGHT+size, + 1,BOARD_PIECE_HEIGHT-2*size,ROP_XOR(rop_Line[0][size])); + hires_Mask(2+x*BOARD_PIECE_WIDTH+BOARD_PIECE_WIDTH-1,14+y*BOARD_PIECE_HEIGHT+size, + 1,BOARD_PIECE_HEIGHT-2*size,ROP_XOR(rop_Line[1][size])); + hires_Mask(2+x*BOARD_PIECE_WIDTH,14+y*BOARD_PIECE_HEIGHT, + BOARD_PIECE_WIDTH,size,ROP_XOR(0x7F)); + hires_Mask(2+x*BOARD_PIECE_WIDTH,14+y*BOARD_PIECE_HEIGHT+BOARD_PIECE_HEIGHT-size, + BOARD_PIECE_WIDTH,size,ROP_XOR(0x7F)); + } + else + { + char set = color == HCOLOR_ATTACK; + char val = cursor ^ x * BOARD_PIECE_WIDTH & 1; + + hires_Mask(2+x*BOARD_PIECE_WIDTH,14+y*BOARD_PIECE_HEIGHT, + 1,BOARD_PIECE_HEIGHT,ROP_AND(rop_Color[set][!val])); + hires_Mask(2+x*BOARD_PIECE_WIDTH+1,14+y*BOARD_PIECE_HEIGHT, + 1,BOARD_PIECE_HEIGHT,ROP_AND(rop_Color[set][val])); + hires_Mask(2+x*BOARD_PIECE_WIDTH+2,14+y*BOARD_PIECE_HEIGHT, + 1,BOARD_PIECE_HEIGHT,ROP_AND(rop_Color[set][!val])); + } +} + +/*-----------------------------------------------------------------------*/ +void plat_ShowMessage(char *str, char) +{ + plat_Drawstring(26,0,ROP_CPY,str); +} + +/*-----------------------------------------------------------------------*/ +void plat_ClearMessage() +{ + hires_Mask(26,0,7,CHAR_HEIGHT,ROP_BLACK); +} + +/*-----------------------------------------------------------------------*/ +void plat_AddToLogWin() +{ + char y; + + for(y=0; y<=LOG_WINDOW_HEIGHT; ++y) + { + hires_Mask(SCREEN_WIDTH-6,y*CHAR_HEIGHT, + 6,CHAR_HEIGHT,ROP_BLACK); + + if(undo_FindUndoLine(LOG_WINDOW_HEIGHT-y)) + { + frontend_FormatLogString(); + plat_Drawstring(SCREEN_WIDTH-6,y*CHAR_HEIGHT, + gColor[0]?ROP_CPY:ROP_INV,gLogStrBuffer); + } + } +} + +/*-----------------------------------------------------------------------*/ +void plat_AddToLogWinTop() +{ + plat_AddToLogWin(); +} + +/*-----------------------------------------------------------------------*/ +int plat_ReadKeys(char blocking) +{ + char key = 0; + int keyMask = 0; + + if(blocking || kbhit()) + key = cgetc(); + else + return 0; + + switch(key) + { + case CH_CURS_LEFT: + keyMask |= INPUT_LEFT; + break; + + case CH_CURS_RIGHT: + keyMask |= INPUT_RIGHT; + break; + + case 'O': + case 'o': // Reasonably located on the ][ keyboard + case 0x0B: // No CH_CURS_UP in apple2.h, only in apple2enh.h + keyMask |= INPUT_UP; + break; + + case 'L': + case 'l': // Reasonably located on the ][ keyboard + case 0x0A: // No CH_CURS_DOWN in apple2.h, only in apple2enh.h + keyMask |= INPUT_DOWN; + break; + + case CH_ESC: + keyMask |= INPUT_BACKUP; + break; + + case CH_ENTER: + keyMask |= INPUT_SELECT; + break; + + case 'A': + case 'a': // Show Attackers + keyMask |= INPUT_TOGGLE_A; + break; + + case 'B': + case 'b': // Board attacks - Show all attacks + keyMask |= INPUT_TOGGLE_B; + break; + + case 'D': + case 'd': // Show Defenders + keyMask |= INPUT_TOGGLE_D; + break; + + case 'M': + case 'm': + keyMask |= INPUT_MENU; + break; + + case 'R': + case 'r': + keyMask |= INPUT_REDO; + break; + + case 'U': + case 'u': + keyMask |= INPUT_UNDO; + break; + } + + return keyMask; +} + +/*-----------------------------------------------------------------------*/ +// Only ever gets called if gReturnToOS is true +void plat_Shutdown() +{ + hires_Done(); +}